البرامج المعرفة للراديو - كيف يعمل؟ الجزء 4

مرحبا يا هبر.

وصف الجزء الثالث كيفية الوصول إلى مستقبل حقوق السحب الخاصة باستخدام بيثون. الآن سنتعرف على برنامج راديو جنو - نظام يسمح لك بإنشاء تكوين معقد إلى حد ما لجهاز راديو دون كتابة سطر واحد من الكود.



كمثال ، خذ بعين الاعتبار مشكلة الاستقبال الموازي لعدة محطات FM على مستقبل واحد. سوف نستخدم نفس RTL SDR V3 مثل المتلقي.

تابع تحت القطع.

تركيب


للبدء ، يجب تثبيت راديو جنو ، يمكن تنزيل مجموعة التوزيع لنظام Windows هنا . هذا النظام متعدد المنصات ، وهناك أيضًا إصدارات لنظامي التشغيل Linux و OSX (يبدو أن راديو GNU تم إطلاقه بنجاح على Raspberry Pi ، لكن لا يمكنني تقديم ضمان بنسبة 100 ٪).

في الواقع ، يعتبر راديو جنو إطارًا كاملًا لمعالجة الإشارات الرقمية ، حيث يتم "تجميع" البرنامج من وحدات منفصلة. هناك عدد كبير من الكتل الجاهزة ، يمكنك أيضًا إنشاء الكتل الخاصة بك إذا كنت ترغب في ذلك. الوحدات النمطية مكتوبة بلغة C ++ ، وتتفاعل بيثون مع بعضها البعض. يمكن لأولئك الذين يرغبون في الاطلاع على واجهة برمجة التطبيقات بمزيد من التفصيل ، ولكن من الناحية العملية ، من غير المرجح أن يكون هذا مفيدًا - يمكن تنفيذ جميع الإجراءات بشكل مرئي في برنامج جنو راديو رفيق.

يركز النظام على معالجة تدفقات البيانات ، بحيث يكون لكل كتلة عادةً مدخلات ومخرجات. بعد ذلك ، عند توصيل الكتل في المحرر ، نحصل على نظام جاهز. واجهة GNU Radio نفسها بسيطة للغاية ، وتكمن الصعوبة في فهم ما تقوم به الكتلة. كما ذكرنا سابقًا ، فإن العمل على مستوى منخفض مع SDR له عتبة إدخال عالية ويتطلب بعض المعرفة بـ DSP والرياضيات. لكننا سننظر في مهمة بسيطة لا تتطلب معرفة خاصة. لذلك دعونا نبدأ.

الابتداء


نبدأ تشغيل GNU Radio Companion وإنشاء مشروع جديد وتحديد نوع المشروع WX GUI وإضافته إلى الشاشة وتوصيل الكتلين كما هو موضح في لقطة الشاشة.



نرى نوعين من القطع - المصدر (المصدر) والمغسلة (الإخراج ، "التصريف"). RTL-SDR هو جهاز الاستقبال لدينا ، FFT GUI هو محلل طيف افتراضي.

تم تعيين متغير معدل العينة على 2048000 ، وهذا هو معدل عينة من المتلقي لدينا. تردد RTL-SDR الافتراضي هو 100 ميجاهرتز.

نبدأ المشروع - كل شيء يعمل ، نرى مجموعة من محطات FM. البرنامج الأول لراديو جنو جاهز!



إذا نظرنا إلى السجل ، سنرى مثل هذه الخطوط.

إنشاء: 'D: \\ MyProjects \\ GNURadio \\ top_block.py'
التنفيذ: C: \ Python27 \ python.exe -u D: \ MyProjects \ GNURadio \ top_block.py

نعم ، يمكننا أن نرى ملف top_block.py الذي أنشأه GNU Radio Companion لنا. يمكن لـ True Jedi الكتابة مباشرة في Python ، لكن الكود المطلوب ، كما نرى ، كبير جدًا. لقد أنشأناها في دقيقة واحدة.

top_blocks.py
#!/usr/bin/env python2 # -*- coding: utf-8 -*- ################################################## # GNU Radio Python Flow Graph # Title: Top Block # Generated: Wed May 22 22:05:14 2019 ################################################## if __name__ == '__main__': import ctypes import sys if sys.platform.startswith('linux'): try: x11 = ctypes.cdll.LoadLibrary('libX11.so') x11.XInitThreads() except: print "Warning: failed to XInitThreads()" from gnuradio import eng_notation from gnuradio import gr from gnuradio import wxgui from gnuradio.eng_option import eng_option from gnuradio.fft import window from gnuradio.filter import firdes from gnuradio.wxgui import fftsink2 from grc_gnuradio import wxgui as grc_wxgui from optparse import OptionParser import osmosdr import time import wx class top_block(grc_wxgui.top_block_gui): def __init__(self): grc_wxgui.top_block_gui.__init__(self, title="Top Block") ################################################## # Variables ################################################## self.samp_rate = samp_rate = 2048000 ################################################## # Blocks ################################################## self.wxgui_fftsink2_0 = fftsink2.fft_sink_c( self.GetWin(), baseband_freq=0, y_per_div=10, y_divs=10, ref_level=0, ref_scale=2.0, sample_rate=samp_rate, fft_size=1024, fft_rate=15, average=False, avg_alpha=None, title='FFT Plot', peak_hold=False, ) self.Add(self.wxgui_fftsink2_0.win) self.rtlsdr_source_0 = osmosdr.source( args="numchan=" + str(1) + " " + '' ) self.rtlsdr_source_0.set_sample_rate(samp_rate) self.rtlsdr_source_0.set_center_freq(100e6, 0) self.rtlsdr_source_0.set_freq_corr(0, 0) self.rtlsdr_source_0.set_dc_offset_mode(0, 0) self.rtlsdr_source_0.set_iq_balance_mode(0, 0) self.rtlsdr_source_0.set_gain_mode(False, 0) self.rtlsdr_source_0.set_gain(10, 0) self.rtlsdr_source_0.set_if_gain(20, 0) self.rtlsdr_source_0.set_bb_gain(20, 0) self.rtlsdr_source_0.set_antenna('', 0) self.rtlsdr_source_0.set_bandwidth(0, 0) ################################################## # Connections ################################################## self.connect((self.rtlsdr_source_0, 0), (self.wxgui_fftsink2_0, 0)) def get_samp_rate(self): return self.samp_rate def set_samp_rate(self, samp_rate): self.samp_rate = samp_rate self.wxgui_fftsink2_0.set_sample_rate(self.samp_rate) self.rtlsdr_source_0.set_sample_rate(self.samp_rate) def main(top_block_cls=top_block, options=None): tb = top_block_cls() tb.Start(True) tb.Wait() if __name__ == '__main__': main() 


ومع ذلك ، إذا أزلنا التهيئة المرهقة ، فسنرى أنه لا يوجد الكثير من أسطر التعليمات البرمجية الرئيسية.
 from gnuradio import gr from gnuradio.wxgui import fftsink2 import osmosdr class top_block(grc_wxgui.top_block_gui): def __init__(self): grc_wxgui.top_block_gui.__init__(self, title="Top Block") self.samp_rate = samp_rate = 2048000 self.wxgui_fftsink2_0 = fftsink2.fft_sink_c(...) self.Add(self.wxgui_fftsink2_0.win) self.rtlsdr_source_0 = osmosdr.source(args="numchan=" + str(1) + " " + '' ) self.connect((self.rtlsdr_source_0, 0), (self.wxgui_fftsink2_0, 0)) def main(top_block_cls=top_block, options=None): tb = top_block_cls() tb.Start(True) tb.Wait() 

لذلك أساسا ، يمكن كتابتها يدويا. لكنه لا يزال أسرع باستخدام الماوس. على الرغم من أن القدرة على تغيير الرمز يمكن أن تكون مفيدة في بعض الأحيان إذا كنت ترغب في إضافة بعض المنطق غير القياسي.

تلقي راديو FM


الآن حاول أن تأخذ إحدى المحطات. كما يتضح من اللقطات ، فإن التردد المركزي للمستقبل هو 100 ميجاهرتز وعرض النطاق الترددي حوالي 2 ميغاهرتز. في الطيف نرى محطتين ، في 100.1 ميغاهيرتز و 100.7 ميغاهيرتز ، على التوالي.

الخطوة الأولى هي نقل طيف المحطة إلى المركز ، والآن هو 100 كيلو هرتز إلى اليمين. للقيام بذلك ، نتذكر الصيغة المدرسية لضرب جيب التمام - ونتيجة لذلك سيكون هناك ترددان ، المجموع والفرق - ستنتقل المحطة المطلوبة إلى المركز ، وهو ما نحتاج إليه (ونحن نقوم بتصفية الفائض).

نقوم بإنشاء متغيرين لتخزين freq_center = 100000000 و freq_1 = 100100000 ترددات ، كما نضيف مولد إشارة مع تردد freq_center - freq_1.



لأن نظرًا لأن النظام يعتمد على Python ، يمكننا استخدام التعبيرات في حقول إدخال المعلمات ، وهي ملائمة تمامًا.

يجب أن يبدو الرسم التخطيطي كما يلي:



أنت الآن بحاجة إلى إضافة عدة كتل مرة واحدة - قم بتقليل تردد الساعة لإشارة الدخل (هو 2048 كيلو هرتز) ، وقم بترشيح الإشارة وتطبيقها على وحدة فك ترميز FM ، ثم قم بتقليل تردد الساعة مرة أخرى إلى 48 كيلو هرتز.

تظهر النتيجة في الصورة:



نحن ننظر بعناية. نقسم سرعة الساعة 2048 كيلو هرتز على 4 مرات مع كتلة Rational Resampler (نحصل على 512 كيلو هرتز) ، ثم بعد مرشح Low Pass هناك وحدة فك ترميز WBFM مع عشرية عشرية (نحصل على 51.2 كيلو هرتز). من حيث المبدأ ، يمكن بالفعل تغذية هذه الإشارة إلى بطاقة الصوت ، ولكن الملعب سيكون مختلفًا قليلاً. مرة أخرى ، قمنا بتغيير تردد الساعة إلى 48/51 ، ونتيجة لذلك ، سيكون تردد الساعة 48.2 كيلو هرتز ، ويمكن بالفعل إهمال الفرق.

النقطة المهمة الثانية هي نوع المدخلات. يتم تلقي إشارة معدل الذكاء المعقدة (المدخلات / المخرجات باللون الأزرق) من جهاز الاستقبال ، والإشارة الحقيقية هي الإخراج من وحدة فك ترميز FM - المدخلات والمخرجات باللون الأصفر. إذا اختلط ، لن ينجح شيء. كان هناك المزيد بالفعل على حبري ، يكفي أن نفهم المبدأ العام.

بشكل عام ، تشغيل ، تأكد من أن كل شيء يعمل. يمكنك تشغيل البرنامج والاستماع إلى الراديو. سنذهب أبعد من ذلك - لا يزال لدينا راديو محدد من قبل البرنامج - سنضيف الاستقبال المتزامن للمحطة الثانية.

استقبال متعدد القنوات


تتم إضافة جهاز الاستقبال الثاني عن طريق طريقة البرمجة المفضلة لديك - Ctrl + C / Ctrl + V. أضف المتغير freq_2 ، انسخ الكتل وربطها بنفس الطريقة.



والنتيجة هي سريالية للغاية - يمكنك الاستماع إلى محطتي وزير الخارجية في وقت واحد. باستخدام نفس الطريقة (Ctrl + V) ، يمكنك إضافة محطة ثالثة.

سجل


الاستماع إلى محطتين بطريقة أصلية ، ولكن في الممارسة العملية ليست مفيدة للغاية. سنفعل شيئًا أكثر أهمية ، على سبيل المثال ، إضافة تسجيل صوتي لفصل الملفات. قد يكون هذا مناسبًا تمامًا - يمكن تسجيل عدة قنوات في وقت واحد من جهاز استقبال فعلي واحد.

أضف مكون Sink File إلى كل مخرجات ، كما هو موضح في لقطة الشاشة.



إصدار Windows لسبب ما يتطلب مسارات الملفات المطلقة ، وإلا فإن التسجيل لا يعمل. نبدأ ، نحن مقتنعون بأن كل شيء طبيعي. حجم الملفات المحفوظة كبير جدًا ، لأن التنسيق الافتراضي هو تعويم. الدخول بتنسيق int سيترك القراء كواجب منزلي.

يمكن فتح الملفات الناتجة في Cool Edit وتأكد من تسجيل الصوت بشكل طبيعي.





بالطبع ، يمكن زيادة عدد القنوات المسجلة ، فهو محدود فقط من خلال عرض النطاق الترددي المتلقي وقوة الكمبيوتر. بالإضافة إلى File Sink ، يمكن أيضًا استخدام UDP Sink ، لذلك يمكن استخدام البرنامج للبث عبر الشبكة.

تشغيل من سطر الأوامر


وآخر واحد. إذا كنت تستخدم البرنامج بشكل مستقل ، على سبيل المثال ، للتسجيل متعدد القنوات ، فإن واجهة المستخدم ، من حيث المبدأ ، ليست ضرورية. في الجزء العلوي الأيسر من الخيارات ، قم بتغيير المعلمة Run Options إلى No UI. قم بتشغيل البرنامج مرة أخرى ، تأكد من أن كل شيء يعمل. الآن نقوم بحفظ الملف الذي تم إنشاؤه top_block.py - يمكننا تشغيله فقط من سطر الأوامر ، على سبيل المثال من ملف الخفافيش أو من وحدة التحكم.



إذا كان أي شخص مهتمًا ، فسيتم حفظ الملف الذي تم إنشاؤه تحت المفسد.
recorder.py
 #!/usr/bin/env python2 # -*- coding: utf-8 -*- ################################################## # GNU Radio Python Flow Graph # Title: Top Block # Generated: Fri May 24 21:47:03 2019 ################################################## from gnuradio import analog from gnuradio import audio from gnuradio import blocks from gnuradio import eng_notation from gnuradio import filter from gnuradio import gr from gnuradio.eng_option import eng_option from gnuradio.filter import firdes from optparse import OptionParser import osmosdr import time class top_block(gr.top_block): def __init__(self): gr.top_block.__init__(self, "Top Block") ################################################## # Variables ################################################## self.samp_rate = samp_rate = 2048000 self.freq_center = freq_center = 100000000 self.freq_2 = freq_2 = 100700000 self.freq_1 = freq_1 = 100100000 ################################################## # Blocks ################################################## self.rtlsdr_source_0 = osmosdr.source( args="numchan=" + str(1) + " " + '' ) self.rtlsdr_source_0.set_sample_rate(samp_rate) self.rtlsdr_source_0.set_center_freq(freq_center, 0) self.rtlsdr_source_0.set_freq_corr(0, 0) self.rtlsdr_source_0.set_dc_offset_mode(0, 0) self.rtlsdr_source_0.set_iq_balance_mode(0, 0) self.rtlsdr_source_0.set_gain_mode(False, 0) self.rtlsdr_source_0.set_gain(10, 0) self.rtlsdr_source_0.set_if_gain(20, 0) self.rtlsdr_source_0.set_bb_gain(20, 0) self.rtlsdr_source_0.set_antenna('', 0) self.rtlsdr_source_0.set_bandwidth(0, 0) self.rational_resampler_xxx_1_0 = filter.rational_resampler_fff( interpolation=48, decimation=51, taps=None, fractional_bw=None, ) self.rational_resampler_xxx_1 = filter.rational_resampler_fff( interpolation=48, decimation=51, taps=None, fractional_bw=None, ) self.rational_resampler_xxx_0_0 = filter.rational_resampler_ccc( interpolation=1, decimation=4, taps=None, fractional_bw=None, ) self.rational_resampler_xxx_0 = filter.rational_resampler_ccc( interpolation=1, decimation=4, taps=None, fractional_bw=None, ) self.low_pass_filter_0_0 = filter.fir_filter_ccf(1, firdes.low_pass( 1, samp_rate/4, 100000, 500000, firdes.WIN_HAMMING, 6.76)) self.low_pass_filter_0 = filter.fir_filter_ccf(1, firdes.low_pass( 1, samp_rate/4, 100000, 500000, firdes.WIN_HAMMING, 6.76)) self.blocks_multiply_xx_0_0 = blocks.multiply_vcc(1) self.blocks_multiply_xx_0 = blocks.multiply_vcc(1) self.blocks_file_sink_0_0 = blocks.file_sink(gr.sizeof_float*1, 'D:\\Temp\\1\\audio2.snd', False) self.blocks_file_sink_0_0.set_unbuffered(False) self.blocks_file_sink_0 = blocks.file_sink(gr.sizeof_float*1, 'D:\\Temp\\1\\audio1.snd', False) self.blocks_file_sink_0.set_unbuffered(False) self.audio_sink_0 = audio.sink(48000, '', True) self.analog_wfm_rcv_0_0 = analog.wfm_rcv( quad_rate=samp_rate/4, audio_decimation=10, ) self.analog_wfm_rcv_0 = analog.wfm_rcv( quad_rate=samp_rate/4, audio_decimation=10, ) self.analog_sig_source_x_0_0 = analog.sig_source_c(samp_rate, analog.GR_COS_WAVE, freq_center - freq_2, 1, 0) self.analog_sig_source_x_0 = analog.sig_source_c(samp_rate, analog.GR_COS_WAVE, freq_center - freq_1, 1, 0) ################################################## # Connections ################################################## self.connect((self.analog_sig_source_x_0, 0), (self.blocks_multiply_xx_0, 1)) self.connect((self.analog_sig_source_x_0_0, 0), (self.blocks_multiply_xx_0_0, 1)) self.connect((self.analog_wfm_rcv_0, 0), (self.rational_resampler_xxx_1, 0)) self.connect((self.analog_wfm_rcv_0_0, 0), (self.rational_resampler_xxx_1_0, 0)) self.connect((self.blocks_multiply_xx_0, 0), (self.rational_resampler_xxx_0, 0)) self.connect((self.blocks_multiply_xx_0_0, 0), (self.rational_resampler_xxx_0_0, 0)) self.connect((self.low_pass_filter_0, 0), (self.analog_wfm_rcv_0, 0)) self.connect((self.low_pass_filter_0_0, 0), (self.analog_wfm_rcv_0_0, 0)) self.connect((self.rational_resampler_xxx_0, 0), (self.low_pass_filter_0, 0)) self.connect((self.rational_resampler_xxx_0_0, 0), (self.low_pass_filter_0_0, 0)) self.connect((self.rational_resampler_xxx_1, 0), (self.audio_sink_0, 0)) self.connect((self.rational_resampler_xxx_1, 0), (self.blocks_file_sink_0, 0)) self.connect((self.rational_resampler_xxx_1_0, 0), (self.audio_sink_0, 1)) self.connect((self.rational_resampler_xxx_1_0, 0), (self.blocks_file_sink_0_0, 0)) self.connect((self.rtlsdr_source_0, 0), (self.blocks_multiply_xx_0, 0)) self.connect((self.rtlsdr_source_0, 0), (self.blocks_multiply_xx_0_0, 0)) def get_samp_rate(self): return self.samp_rate def set_samp_rate(self, samp_rate): self.samp_rate = samp_rate self.rtlsdr_source_0.set_sample_rate(self.samp_rate) self.low_pass_filter_0_0.set_taps(firdes.low_pass(1, self.samp_rate/4, 100000, 500000, firdes.WIN_HAMMING, 6.76)) self.low_pass_filter_0.set_taps(firdes.low_pass(1, self.samp_rate/4, 100000, 500000, firdes.WIN_HAMMING, 6.76)) self.analog_sig_source_x_0_0.set_sampling_freq(self.samp_rate) self.analog_sig_source_x_0.set_sampling_freq(self.samp_rate) def get_freq_center(self): return self.freq_center def set_freq_center(self, freq_center): self.freq_center = freq_center self.rtlsdr_source_0.set_center_freq(self.freq_center, 0) self.analog_sig_source_x_0_0.set_frequency(self.freq_center - self.freq_2) self.analog_sig_source_x_0.set_frequency(self.freq_center - self.freq_1) def get_freq_2(self): return self.freq_2 def set_freq_2(self, freq_2): self.freq_2 = freq_2 self.analog_sig_source_x_0_0.set_frequency(self.freq_center - self.freq_2) def get_freq_1(self): return self.freq_1 def set_freq_1(self, freq_1): self.freq_1 = freq_1 self.analog_sig_source_x_0.set_frequency(self.freq_center - self.freq_1) def main(top_block_cls=top_block, options=None): tb = top_block_cls() tb.start() try: raw_input('Press Enter to quit: ') except EOFError: pass tb.stop() tb.wait() if __name__ == '__main__': main() 


من المريح أيضًا أن يكون النظام متعدد المنصات ، ويمكن تشغيل البرنامج الناتج على Linux و Windows و OSX.

استنتاج


يمكننا أن نقول إن راديو جنو هو نظام معقد إلى حد ما ، ليس من ناحية رسم الكتل بالطبع ، ولكن من حيث فهم كيف يعمل كل شيء. لكن القيام ببعض الأشياء البسيطة أمر ممكن وممتع للغاية. يتم استخدام راديو جنو أيضًا بشكل مريح باعتباره "مختبرًا افتراضيًا" للتدريب - يمكنك توصيل مرسمة الذبذبات الافتراضية أو محلل الطيف بأي جزء من الدائرة ومعرفة كيف تبدو الإشارة.

إذا لم تكن هناك أي رغبات فردية ، فمن المحتمل أن يتم إغلاق موضوع استقبال حقوق السحب الخاصة - فقد تم بالفعل النظر في جميع النقاط الرئيسية ، وينخفض ​​عدد مرات المشاهدة من الجزء الأول إلى الثالث بشكل كبير (على الرغم من أنه لا يزال بإمكانك الكتابة عن النقل ، لكنه يتطلب تكلفة أعلى " الأجهزة "للاختبارات من RTL SDR). ومع ذلك ، آمل أن يظل بعض القراء يفهمون كيف يعمل هذا. حسنا ، كل التجارب الناجحة.

Source: https://habr.com/ru/post/ar453038/


All Articles