Software Defined Radio - bagaimana cara kerjanya? Bagian 3

Hai, Habr.

Bagian kedua meneliti aspek praktis menggunakan SDR. Pada bagian ini, kita akan melihat cara menerima data satelit cuaca NOAA menggunakan Python dan penerima RTL-SDR yang murah ($ 30). Kode yang dipertimbangkan akan bekerja di mana saja - pada Windows, OSX, Linux, dan bahkan pada Raspberry Pi.



Siapa peduli, terus di bawah luka.

SoapySDR


Ada cukup banyak produsen berbagai perangkat SDR, dan akan sangat merepotkan untuk mendukung masing-masing secara terpisah, dan itu mahal dalam hal pembelian perangkat keras untuk pengujian. Pada prinsipnya, untuk akses terpadu, ada dua perpustakaan yang pada dasarnya menjadi standar. Yang pertama adalah antarmuka ExtIO DLL yang agak lama, yang mungkin tidak kurang dari 10 tahun, yang kedua adalah perpustakaan SoapySDR yang lebih modern, yang akan kita periksa.

SoapySDR adalah seperangkat pustaka lintas-platform yang ditulis dalam C ++ yang menyediakan akses terpadu ke perangkat SDR, baik penerima maupun transceiver. Jika pabrikan membuat antarmuka seperti itu, maka perangkatnya akan secara otomatis bekerja dengan sejumlah besar program populer (GQRX, GNU Radio, CubicSDR, dll.). Hampir semua produsen yang memadai, kecuali beberapa, (mengambil kesempatan ini, saya kirim salam saya ke EE) memiliki dukungan SoapySDR, daftar perangkat yang didukung dapat ditemukan di halaman proyek . Seperti yang Anda lihat, itu cukup besar dan termasuk HackRF, USRP, SDRPlay, LimeSDR, RTL-SDR, Red Pitaya dan banyak lainnya.

Perpustakaan SoapySDR adalah lintas-platform, mis. kode yang ditulis untuknya akan bekerja di bawah Windows, OSX, Linux, dan bahkan pada Raspberry Pi. Untuk Windows, pustaka yang diperlukan adalah bagian dari paket PothosSDR , untuk platform lain, SoapySDR harus dikompilasi secara independen. Penting untuk mengkompilasi dua bagian - perpustakaan itu sendiri , dan "plug-in" untuk penerima yang diinginkan, dalam kasus kami itu akan SoapyRTLSDR (di bawah Windows, perpustakaan juga dapat dirakit dari sumber, untuk ini Anda memerlukan Visual Studio, Cmake, dan SWIG). Sekarang semuanya sudah siap, dan Anda dapat menulis kode.

Kami mengimpor perpustakaan dan mendapatkan daftar penerima:

from __future__ import print_function import SoapySDR # Enumerate devices print("SDR devices:") for d in SoapySDR.Device.enumerate(''): print(d) print() 

Kami menghubungkan receiver, menjalankan kode dan melihat daftar perangkat, di antaranya adalah rtlsdr kami.



Sisa perangkat adalah kartu suara, seperti yang kita ingat, secara historis SDR pertama bekerja dengan tepat melalui input linear PC, dan perpustakaan juga mendukungnya. Kami mendapatkan informasi tentang perangkat - jumlah saluran yang tersedia, rentang frekuensi, dll .:

 soapy_device = "rtlsdr" device = SoapySDR.Device(dict(driver = soapy_device)) channels = list(range(device.getNumChannels(SoapySDR.SOAPY_SDR_RX))) print("Channels:", channels) ch = channels[0] sample_rates = device.listSampleRates(SoapySDR.SOAPY_SDR_RX, ch) print("Sample rates:\n", sample_rates) bandwidths = list(map(lambda r: int(r.maximum()), device.getBandwidthRange(SoapySDR.SOAPY_SDR_RX, ch))) print("Bandwidths:\n", bandwidths) print("Gain controls:") for gain in device.listGains(SoapySDR.SOAPY_SDR_RX, ch): print(" %s: %s" % (gain, device.getGainRange(SoapySDR.SOAPY_SDR_RX, ch, gain))) frequencies = device.listFrequencies(SoapySDR.SOAPY_SDR_RX, ch) print("Frequencies names:", frequencies) frequency_name = frequencies[0] print("Frequency channel name:", frequency_name) print("Frequency range:", device.getFrequencyRange(SoapySDR.SOAPY_SDR_RX, ch, frequency_name)[0]) 

Kami memulai program dan melihat informasi tentang penerima:



Kita melihat bahwa penerima memiliki satu saluran input dengan nama "RF", frekuensi sampling yang mungkin [250000.0, 1024000.0, 1536000.0, 1792000.0, 1920000.0, 2048000.0, 2160000.0, 2560000.0, 2880000.0, 3200000.0] dan rentang frekuensi 24 MHz-1.7 GHz.

Life hack - data yang sama juga dapat diperoleh dari baris perintah dengan mengetikkan perintah SoapySDRUtil --probe = "driver = rtlsdr" .

Mengetahui hal ini, kita dapat merekam aliran data dalam WAV. Seperti disebutkan di bagian sebelumnya, data dari SDR diwakili oleh aliran sinyal yang disebut I dan Q, yang merupakan sampel dari ADC, secara kasar mereka dapat direpresentasikan sebagai data RAW dari kamera. Siapa pun yang tertarik pada detail lebih lanjut dapat membaca, misalnya, di sini . Cukup bagi kami untuk mengetahui bahwa kami dapat menulis data ini, dan program SDR lainnya kemudian dapat bekerja dengannya.

Catatan itu sendiri cukup sederhana - fungsi readStream mengisi buffer jika ada data, jika tidak ada data, maka -1 akan dikembalikan. Di bawah ini adalah kode rekaman 10 sampel (bagian yang tidak penting dari kode dihilangkan).

 device.setFrequency(SoapySDR.SOAPY_SDR_RX, channel, "RF", frequency) device.setGain(SoapySDR.SOAPY_SDR_RX, channel, "TUNER", gain) device.setGainMode(SoapySDR.SOAPY_SDR_RX, channel, False) device.setSampleRate(SoapySDR.SOAPY_SDR_RX, channel, sample_rate) # Number of blocks to save block, max_blocks = 0, 10 block_size = device.getStreamMTU(stream) print("Block size:", block_size) buffer_format = np.int8 buffer_size = 2*block_size # I+Q buffer = np.empty(buffer_size, buffer_format) while True: d_info = device.readStream(stream, [buffer], buffer_size) if d_info.ret > 0: wav.write(buffer[0:2*d_info.ret]) print("Bytes saved:", 2*d_info.ret) block += 1 if block > max_blocks: break 

Hasilnya dalam tangkapan layar:



Seperti yang Anda lihat, kami menerima blok data dari perangkat, ukuran satu blok adalah 131072 byte, yang pada tingkat sampling 250.000 memberi kita durasi sekitar setengah detik. Secara umum, mereka yang sebelumnya bekerja dengan kartu suara di Windows akan menemukan banyak kesamaan.

Untuk pengujian, tulis file dan periksa apakah semuanya baik-baik saja - dapat diputar dalam SDR #. Ada satu trik lagi - agar SDR # dapat menampilkan frekuensi stasiun dengan benar, nama file harus ditulis dalam format yang kompatibel dengan HDSDR, dalam bentuk “HDSDR_20190518_115500Z_101000kHz_RF.wav” (seperti yang Anda duga, tanggal dan waktu dalam GMT di awal, kemudian frekuensi di kiloher). . Ini mudah ditulis dengan Python:

 frequency = 101000000 file_name = "HDSDR_%s_%dkHz_RF.wav" % (datetime.datetime.utcnow().strftime("%Y%m%d_%H%M%SZ"), frequency/1000) 

Pertama, periksa band FM. Semuanya baik-baik saja, stasiun terlihat, musik diputar, RDS berfungsi.



Anda dapat mulai merekam NOAA.

Asupan NOAA


Jadi, kami memiliki penerima dan program rekaman. Kami akan tertarik dengan satelit cuaca NOAA 15, NOAA 18 dan NOAA 19 yang mentransmisikan gambar-gambar permukaan bumi pada frekuensi 137.620, 137.9125 dan 137.100 MHz. Kesulitan utama di sini adalah bahwa Anda perlu "menangkap" saat ketika satelit terbang di atas kami. Anda dapat mengetahui waktu penerbangan online di https://www.n2yo.com/passes/?s=25338 , https://www.n2yo.com/passes/?s=28654 dan https://www.n2yo.com / berlalu /? s = 33591 masing-masing.



Agar tidak duduk di depan komputer, tambahkan program menunggu waktu yang tepat. Ini juga akan memungkinkan Anda untuk menjalankan program di Raspberry Pi, tanpa layar dan keyboard.

 import datetime def wait_for_start(dt): # Wait for the start while True: now = datetime.datetime.now() diff = int((dt - now).total_seconds()) print("{:02d}:{:02d}:{:02d}: Recording will be started after {}m {:02d}s...".format(now.hour, now.minute, now.second, int(diff / 60), diff % 60)) time.sleep(5) if diff <= 1: break wait_for_start(datetime.datetime(2019, 5, 18, 21, 49, 0)) 


Ngomong-ngomong, untuk menjalankan skrip pada Raspberry Pi dan membiarkannya tetap bekerja setelah menutup konsol, Anda harus memasukkan perintah "nohup python recorder.py &".

Semuanya siap, jalankan skrip dan dapat melakukan hal-hal lain, rekaman berlangsung sekitar 20 menit. Secara paralel, pertanyaan mungkin muncul - apakah mungkin untuk melihat bagian dari satelit dengan mata telanjang? Menurut tabel tersebut, kecerahan maksimumnya sekitar 5,5m besarnya , batas mata manusia dalam kondisi ideal adalah 6m. Yaitu di langit yang sangat gelap, jauh di luar kota, bagian dari satelit NOAA secara teoritis dapat diperhatikan, di kota itu, tentu saja, tidak ada kesempatan (seperti yang mereka tulis di Habré , generasi orang yang belum pernah melihat Bimasakti dalam kehidupan mereka telah tumbuh).

Hasil skrip adalah file wav yang direkam, spektrumnya ditunjukkan pada tangkapan layar.



Kami melihat sinyal yang sepenuhnya dapat dibedakan, meskipun tentu saja dengan antena khusus untuk menerima NOAA kualitasnya akan jauh lebih baik. Format sinyal disebut APT ( Automatic Picture Transmission ), dari situ Anda bisa mendapatkan gambar permukaan bumi, jika ada yang tertarik, Anda dapat secara terpisah mempertimbangkan penguraiannya. Tetapi ada, tentu saja, program yang sudah jadi, Anda dapat mendekode sinyal tersebut menggunakan WxToImg atau MultiPSK.

Sangat menarik untuk melihat pergeseran Doppler dalam spektrum, yang terjadi karena satelit terbang melewati kita. Mungkin, tidak sulit untuk menghitung kecepatannya, mengetahui waktu dan frekuensi bergeser di hertz.

Tentu saja, program ini dapat digunakan tidak hanya untuk merekam NOAA, bandwidth dan frekuensi apa pun dapat ditentukan dalam pengaturan. Bagi mereka yang ingin bereksperimen dengan SoapySDR sendiri, kode program seluruhnya berada di bawah spoiler.

Kode sumber
 from __future__ import print_function import SoapySDR import numpy as np import struct import sys import time import datetime def wait_for_start(dt): # Wait for the start while True: now = datetime.datetime.now() diff = int((dt - now).total_seconds()) print("{:02d}:{:02d}:{:02d}: Recording will be started after {}m {:02d}s...".format(now.hour, now.minute, now.second, int(diff / 60), diff % 60)) time.sleep(5) if diff <= 1: break def sdr_enumerate(): # Enumerate SDR devices print("SDR devices:") for d in SoapySDR.Device.enumerate(''): print(d) print() def sdr_init(): soapy_device = "rtlsdr" device = SoapySDR.Device({"driver": soapy_device}) channels = list(range(device.getNumChannels(SoapySDR.SOAPY_SDR_RX))) print("Channels:", channels) ch = channels[0] sample_rates = device.listSampleRates(SoapySDR.SOAPY_SDR_RX, ch) print("Sample rates:\n", sample_rates) print("Gain controls:") for gain in device.listGains(SoapySDR.SOAPY_SDR_RX, ch): print(" %s: %s" % (gain, device.getGainRange(SoapySDR.SOAPY_SDR_RX, ch, gain))) frequencies = device.listFrequencies(SoapySDR.SOAPY_SDR_RX, ch) print("Frequencies names:", frequencies) frequency_name = frequencies[0] print("Frequency channel name:", frequency_name) print("Frequency range:", device.getFrequencyRange(SoapySDR.SOAPY_SDR_RX, ch, frequency_name)[0]) print() return device def sdr_record(device, frequency, sample_rate, gain, blocks_count): print("Frequency:", frequency) print("Sample rate:", sample_rate) print("Gain:", gain) channel = 0 # Always for RTL-SDR device.setFrequency(SoapySDR.SOAPY_SDR_RX, channel, "RF", frequency) device.setGain(SoapySDR.SOAPY_SDR_RX, channel, "TUNER", gain) device.setGainMode(SoapySDR.SOAPY_SDR_RX, channel, False) device.setSampleRate(SoapySDR.SOAPY_SDR_RX, channel, sample_rate) data_format = SoapySDR.SOAPY_SDR_CS8 # if 'rtlsdr' in soapy_device or 'hackrf' in soapy_device else SoapySDR.SOAPY_SDR_CS16 stream = device.setupStream(SoapySDR.SOAPY_SDR_RX, data_format, [channel], {}) device.activateStream(stream) block_size = device.getStreamMTU(stream) print("Block size:", block_size) print("Data format:", data_format) print() # IQ: 2 digits ver variable buffer_format = np.int8 buffer_size = 2 * block_size # I + Q samples buffer = np.empty(buffer_size, buffer_format) # Number of blocks to save block, max_blocks = 0, blocks_count # Save to file file_name = "HDSDR_%s_%dkHz_RF.wav" % (datetime.datetime.utcnow().strftime("%Y%m%d_%H%M%SZ"), frequency/1000) print("Saving file:", file_name) with open(file_name, "wb") as wav: # Wav data info bits_per_sample = 16 channels_num, samples_num = 2, int(max_blocks * block_size) subchunk_size = 16 # always 16 for PCM subchunk2_size = int(samples_num * channels_num * bits_per_sample / 8) block_alignment = int(channels_num * bits_per_sample / 8) # Write RIFF header wav.write('RIFF'.encode('utf-8')) wav.write(struct.pack('<i', 4 + (8 + subchunk_size) + (8 + subchunk2_size))) # Size of the overall file wav.write('WAVE'.encode('utf-8')) # Write fmt subchunk wav.write('fmt '.encode('utf-8')) # chunk type wav.write(struct.pack('<i', subchunk_size)) # subchunk data size (16 for PCM) wav.write(struct.pack('<h', 1)) # compression type 1 - PCM wav.write(struct.pack('<h', channels_num)) # channels wav.write(struct.pack('<i', int(sample_rate))) # sample rate wav.write(struct.pack('<i', int(sample_rate * bits_per_sample * channels_num/ 8))) # byte rate wav.write(struct.pack('<h', block_alignment)) # block alignment wav.write(struct.pack('<h', bits_per_sample)) # sample depth # Write data subchunk wav.write('data'.encode('utf-8')) wav.write(struct.pack('<i', subchunk2_size)) while True: d_info = device.readStream(stream, [buffer], buffer_size) if d_info.ret > 0: data = buffer[0:2*d_info.ret] fileData = data if data_format == SoapySDR.SOAPY_SDR_CS8: fileData = data.astype('int16') wav.write(fileData) print("Block %d saved: %d bytes" % (block, 2*d_info.ret)) block += 1 if block > max_blocks: break device.deactivateStream(stream) device.closeStream(stream) if __name__ == "__main__": print("App started") # Forecast for active NOAA satellites # NOAA 15: 137.620, https://www.n2yo.com/passes/?s=25338 # NOAA 18: 137.9125, https://www.n2yo.com/passes/?s=28654 # NOAA 19: 137.100, https://www.n2yo.com/passes/?s=33591 # Wait for the start: 18-May 21:49 21:49: wait_for_start(datetime.datetime(2019, 5, 18, 21, 49, 0)) device = sdr_init() t_start = time.time() sdr_record(device, frequency=137912500, sample_rate=250000, gain=35, blocks_count=2100) print("Recording complete, time = %ds" % int(time.time() - t_start)) print() 


SoapySDR plus adalah program yang sama dengan perubahan minimal akan bekerja dengan penerima lain, misalnya dengan SDRPlay atau HackRF. Nah, tentang cross-platform juga sudah disebutkan.

Jika pembaca masih tertarik pada topik penerimaan radio, Anda dapat mempertimbangkan contoh penggunaan SDR dengan GNU Radio dengan membuat beberapa penerima virtual berdasarkan pada satu perangkat keras.

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


All Articles