Software Defined Radio - bagaimana cara kerjanya? Bagian 4

Hai, Habr.

Bagian ketiga menjelaskan cara mengakses penerima SDR menggunakan Python. Sekarang kita akan berkenalan dengan program Radio GNU - sistem yang memungkinkan Anda membuat konfigurasi perangkat radio yang cukup kompleks tanpa menulis satu baris kode pun.



Sebagai contoh, perhatikan masalah penerimaan paralel beberapa stasiun FM pada satu penerima. Kami akan menggunakan RTL SDR V3 yang sama dengan penerima.

Berlanjut di bawah potongan.

Instalasi


Untuk memulai, GNU Radio perlu diinstal, kit distribusi untuk Windows dapat diunduh di sini . Sistem ini adalah lintas platform, ada juga versi untuk Linux dan OSX (tampaknya GNU Radio berhasil diluncurkan pada Raspberry Pi, tetapi saya tidak dapat memberikan jaminan 100%).

Sebenarnya, GNU Radio adalah keseluruhan kerangka kerja untuk pemrosesan sinyal digital, di mana program ini "dirakit" dari modul yang terpisah. Ada sejumlah besar blok siap pakai, jika mau, Anda juga dapat membuat blok Anda sendiri. Modul-modul itu sendiri ditulis dalam C ++, dan Python berinteraksi satu sama lain. Mereka yang ingin dapat melihat API secara lebih rinci , tetapi dalam praktiknya ini kemungkinan besar tidak berguna - semua tindakan dapat dilakukan secara visual dalam program Pendamping Radio GNU.

Sistem difokuskan pada pemrosesan aliran data, sehingga setiap blok biasanya memiliki input dan output. Selanjutnya, menghubungkan blok di editor, kami mendapatkan sistem yang sudah jadi. Antarmuka GNU Radio itu sendiri cukup sederhana, kesulitannya adalah dalam memahami apa yang dilakukan blok. Seperti disebutkan sebelumnya, pekerjaan tingkat rendah dengan SDR memiliki ambang input tinggi dan membutuhkan pengetahuan tentang DSP dan matematika. Tetapi kami akan mempertimbangkan tugas sederhana yang tidak membutuhkan pengetahuan khusus. Jadi mari kita mulai.

Memulai


Kami meluncurkan GNU Radio Companion, membuat proyek baru, pilih jenis proyek WX GUI, tambahkan ke layar dan hubungkan dua blok, seperti yang ditunjukkan pada tangkapan layar.



Kami melihat dua jenis blok - Sumber (sumber) dan Sink (keluaran, "tiriskan"). RTL-SDR adalah penerima kami, FFT GUI adalah penganalisa spektrum virtual.

Variabel Tingkat Sampel diatur ke 2048000, ini adalah tingkat sampel penerima kami. Frekuensi RTL-SDR default adalah 100 MHz.

Kami memulai proyek - semuanya berfungsi, kami melihat berbagai stasiun FM. Program pertama untuk GNU Radio sudah siap!



Jika kita melihat log, kita akan melihat garis seperti itu.

Menghasilkan: 'D: \\ MyProjects \\ GNURadio \\ top_block.py'
Menjalankan: C: \ Python27 \ python.exe -u D: \ MyProjects \ GNURadio \ top_block.py

Ya, kita dapat melihat file top_block.py yang dihasilkan GNU Radio Companion untuk kita. True Jedi dapat menulis secara langsung dengan Python, tetapi kode yang diperlukan, seperti yang kita lihat, cukup besar. Kami membuatnya dalam 1 menit.

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() 


Namun, jika kita menghapus inisialisasi yang rumit, kita akan melihat bahwa tidak ada begitu banyak baris kode.
 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() 

Jadi pada dasarnya, itu bisa ditulis secara manual. Tetapi masih lebih cepat dengan mouse. Meskipun kemampuan untuk mengubah kode kadang-kadang bisa berguna jika Anda ingin menambahkan beberapa logika non-standar.

Terima Radio FM


Sekarang cobalah untuk mengambil salah satu stasiun. Seperti yang terlihat dari tangkapan layar, frekuensi pusat penerima adalah 100 MHz dan bandwidth sekitar 2 MHz. Dalam spektrum kita melihat dua stasiun, masing-masing pada 100,1 MHz dan 100,7 MHz.

Langkah pertama adalah mentransfer spektrum stasiun ke pusat, sekarang 100 KHz ke kanan. Untuk melakukan ini, kita mengingat rumus sekolah untuk mengalikan cosinus - akibatnya akan ada dua frekuensi, jumlah dan perbedaan - stasiun yang diinginkan akan pindah ke pusat, yang adalah apa yang kita butuhkan (dan kita menyaring kelebihannya).

Kami membuat dua variabel untuk menyimpan freq_center = 100000000 dan freq_1 = 100100000 frekuensi, kami juga menambahkan generator sinyal dengan frekuensi freq_center - freq_1.



Karena Karena sistem ini didasarkan pada Python, kita dapat menggunakan ekspresi di bidang input parameter, yang cukup nyaman.

Diagram akan terlihat seperti ini:



Sekarang Anda perlu menambahkan beberapa blok sekaligus - mengurangi frekuensi clock dari sinyal input (2048KHz), menyaring sinyal, menerapkannya ke decoder FM, kemudian mengurangi frekuensi clock lagi menjadi 48KHz.

Hasilnya ditunjukkan pada gambar:



Kami mempertimbangkan dengan cermat. Kami membagi kecepatan clock 2048KHz sebanyak 4 kali dengan blok Rational Resampler (kami mendapatkan 512KHz), kemudian setelah filter Low Pass ada decoder WBFM dengan decimation 10 (kita mendapatkan 51.2KHz). Pada prinsipnya, sinyal ini sudah dapat diumpankan ke kartu suara, tetapi nada akan sedikit berbeda. Sekali lagi, kami mengubah frekuensi jam menjadi 48/51, sebagai akibatnya, frekuensi clock akan menjadi 48,2 KHz, perbedaannya sudah dapat diabaikan.

Poin penting kedua adalah jenis input. Sinyal IQ kompleks (input / output berwarna biru) diterima dari receiver, sinyal nyata adalah output dari dekoder FM - input dan output berwarna kuning. Jika campur aduk, tidak ada yang akan berhasil. Lebih banyak tentang Habré , sudah cukup bagi kita untuk memahami prinsip umum.

Secara umum, jalankan, pastikan semuanya bekerja. Anda dapat menjalankan program dan mendengarkan radio. Kami akan melangkah lebih jauh - kami masih memiliki radio yang Ditetapkan Perangkat Lunak - kami akan menambahkan penerimaan simultan stasiun kedua.

Penerimaan multichannel


Penerima kedua ditambahkan oleh metode pemrograman favorit Anda - Ctrl + C / Ctrl + V. Tambahkan variabel freq_2, salin blok dan hubungkan dengan cara yang sama.



Hasilnya cukup nyata - Anda dapat mendengarkan dua stasiun FM secara bersamaan. Dengan menggunakan metode yang sama (Ctrl + V), Anda dapat menambahkan stasiun ketiga.

Rekam


Mendengarkan dua stasiun dengan cara yang orisinal, tetapi dalam praktiknya tidak terlalu berguna. Kami akan melakukan sesuatu yang lebih penting, misalnya, menambahkan rekaman suara ke file terpisah. Ini bisa sangat nyaman - beberapa saluran dapat direkam secara bersamaan dari satu penerima fisik.

Tambahkan komponen File Sink ke setiap output, seperti yang ditunjukkan pada tangkapan layar.



Versi Windows karena alasan tertentu memerlukan jalur file absolut, jika tidak rekaman tidak akan berfungsi. Kami mulai, kami yakin bahwa semuanya normal. Ukuran file yang disimpan cukup besar, karena format default adalah float. Entri dalam format int akan meninggalkan pembaca sebagai pekerjaan rumah.

File yang dihasilkan dapat dibuka di Cool Edit dan pastikan bahwa suara direkam secara normal.





Tentu saja, jumlah saluran yang direkam dapat ditingkatkan, hanya dibatasi oleh bandwidth penerima dan daya komputer. Selain File Sink, UDP Sink juga dapat digunakan, sehingga program dapat digunakan untuk disiarkan melalui jaringan.

Jalankan dari baris perintah


Dan yang terakhir. Jika Anda menggunakan program secara mandiri, misalnya, untuk perekaman multi-saluran, maka UI, pada prinsipnya, tidak diperlukan. Di blok kiri atas Opsi, ubah parameter Opsi Jalankan ke Tidak Ada UI. Jalankan kembali program tersebut, pastikan semuanya bekerja. Sekarang kita menyimpan file yang dihasilkan top_block.py - kita bisa menjalankannya dari baris perintah, misalnya dari file bat atau dari konsol.



Jika ada yang tertarik, file yang dihasilkan disimpan di bawah spoiler.
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() 


Juga nyaman bahwa sistemnya adalah lintas platform, dan program yang dihasilkan dapat berjalan di Linux, Windows dan OSX.

Kesimpulan


Kita dapat mengatakan bahwa GNU Radio adalah sistem yang agak rumit, bukan dalam hal menggambar blok tentu saja, tetapi dalam hal memahami bagaimana semuanya bekerja. Tetapi untuk melakukan beberapa hal sederhana cukup layak dan menarik. GNU Radio juga mudah digunakan sebagai "laboratorium virtual" untuk pelatihan - Anda dapat menghubungkan osiloskop virtual atau penganalisa spektrum ke bagian mana pun dari sirkuit dan melihat bagaimana sinyal terlihat.

Jika tidak ada keinginan individu, topik penerimaan SDR mungkin dapat ditutup - semua poin utama telah dipertimbangkan, dan jumlah tampilan dari bagian pertama hingga ketiga turun hampir secara eksponensial (meskipun Anda masih dapat menulis tentang transfer, tetapi membutuhkan lebih mahal " perangkat keras ”untuk pengujian daripada RTL SDR). Meskipun demikian, saya berharap bahwa beberapa pemahaman tentang bagaimana ini bekerja tetap dengan pembaca. Nah, semua percobaan berhasil.

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


All Articles