Software Defined Radio - how does it work? Part 8

    Hi Habr.

    In a previous article about Software Defined Radio, I was asked how to decode RDS using GNU Radio. The RDS decoder is not so easy to create from scratch, but fortunately for us, it is already built into GNU Radio, so it's easy to see how it works without writing a single line of code, just the RTL-SDR receiver.



    How it works, continued under the cut.

    I will not repeat about GNU Radio and its installation, it has already been described previously . In short, a distribution for Windows can be found here . Let's move on to RDS.

    If someone doesn’t know, RDS(Radio Data System) is a protocol used in conventional FM broadcasting to transmit additional digital information (time, exchange rates, weather, etc.). The transmission is at a speed of 1187.5bps at a frequency of 57KHz (3rd harmonic of the pilot tone), phase minimization using BPSK phase encoding (Binary Phase Shift Keying) is used. The bit-level RDS has already been considered on the hub before , well, we will move on to GNU Radio.

    The decoder for GNU Radio was made about 10 years ago , but it’s useless to watch that version, the names of many blocks have changed, and the examples given there no longer work. Updated fork can be taken from here .

    By the way, it did not work to collect gr-rds from the source for Windows - cmake gives errors for the absence of Boost, although it is installed. It’s incomprehensible, by the way, that over the years of existence cmake and boost, cmake have not been taught how to find paths in Windows - like finding a folder on a disk is not rocket science at all (if anyone knows the solution, write in the comments, although judging by Stack Overflow, the problem has existed for years and all do not care). But as it turned out, we don’t need it - the RDS decoder has already been added to GNU Radio, so from the project on github we only need examples that are in the apps folder .

    The block diagram enclosed in the examples is quite monstrous, and besides, errors are generated when it is opened (blocks are marked in red).



    In real life, however, everything is simpler - 2/3 of the circuit is a stereo player for FM, where L + R and LR channels are extracted from the original signal, processed and fed to the sound card. This is not relevant for us now, so these blocks can be removed (all the more so because for some reason it didn’t work out of the box, but it was too lazy to understand). Errors arise due to the Grid Position parameter, which, apparently, is not supported by the Windows version, but it can be removed without problems, it does not affect the functionality.

    After removing "all that is superfluous", the working RDS decoder circuit looks like this:



    Let's see what is there.

    The initial signal comes from RTL-SDR Source, the frequency is set by the freq parameter, which is of the WX GUI Slider type (yes, you can create your own UI in GNU Radio, and there are basic controls). To avoid a peak at zero frequency in the center, the freq_offset parameter is used, the Frequency Xlating block shifts the frequency by this value. The WBFM Receive block, as the name implies, performs FM demodulation, then the frequency is shifted again to highlight 57KHz RDS itself. The Root Cosined Filter block selects a narrow frequency, but the MPSK decoder with parameter 2 performs BPSK decoding (the RDS itself is transmitted using phase modulation with two states). RDS uses differential encoding, so the corresponding differential decoder is called, and finally, the finished binary stream is fed to the RDS Decoder block (its source can be viewed on github ). After the decoder, an equally important part is the RDS parser - there are quite a lot of packet types in RDS, and the parser does all the work to decrypt them.

    Actually that's all. The results of the decoder on the KDPV and screenshots.







    If someone needs to use the program in no-UI mode, you can use the FR Tap block, a more detailed description here , I personally have not tried it. If the lower bit-level RDS is interesting, I considered it earlier , for general interest it can also be useful.

    As usual, all successful experiments.

    The source GRC file running under Windows is under a spoiler (the frequency of the radio station will only have to be changed).

    rds_rx.grc
    Thu Aug 28 08:24:49 2014optionsauthorwindow_size1600, 1600categoryCustomcommentdescription_enabledTrue_coordinate(14, 9)_rotation0generate_optionswx_guihier_block_src_path.:idrds_rxmax_nouts0qt_qss_themerealtime_schedulingrun_command{python} -u {filename}run_optionspromptrunTruethread_safe_setterstitleStereo FM receiver and RDS Decodervariablecomment_enabledTrue_coordinate(8, 156)_rotation0idaudio_decimvalue5variablecomment_enabledTrue_coordinate(112, 156)_rotation0idaudio_decim_ratevaluebaseband_rate/audio_decimvariablecomment_enabledTrue_coordinate(112, 92)_rotation0idbaseband_ratevaluesamp_rate/bb_decimvariablecomment_enabledTrue_coordinate(240, 156)_rotation0idbb_decimvalue4variable_slidercommentconververfloat_convertervalue100.7e6_enabledTrue_coordinate(448, 4)_rotation0grid_posidfreqlabelFreqmax107.9e6min88.1e6notebooknum_steps99stylewx.SL_HORIZONTALvariablecomment_enabledTrue_coordinate(448, 132)_rotation0idfreq_offsetvalue250000variablecomment_enabledTrue_coordinate(224, 92)_rotation0idfreq_tunevaluefreq - freq_offsetvariable_slidercommentconververfloat_convertervalue20_enabledTrue_coordinate(336, 4)_rotation0grid_posidgainlabelRF Gainmax49.6min0notebooknum_steps124stylewx.SL_HORIZONTALvariablecomment_enabledTrue_coordinate(8, 92)_rotation0idsamp_ratevalue1000000variablecomment_enabledTrue_coordinate(336, 132)_rotation0idxlate_bandwidthvalue100000analog_wfm_rcvaudio_decimationbb_decimaliascommentaffinity_enabledTrue_coordinate(576, 356)_rotation0idanalog_wfm_rcv_0maxoutbuf0minoutbuf0quad_ratesamp_rateblocks_complex_to_realaliascommentaffinity_enabledTrue_coordinate(792, 632)_rotation0idblocks_complex_to_real_0maxoutbuf0minoutbuf0vlen1blocks_keep_one_in_naliascommentaffinity_enabledTrue_coordinate(280, 788)_rotation0idblocks_keep_one_in_n_0maxoutbuf0minoutbuf0n2typebytevlen1digital_binary_slicer_fbaliascommentaffinity_enabledTrue_coordinate(112, 792)_rotation0iddigital_binary_slicer_fb_0maxoutbuf0minoutbuf0digital_diff_decoder_bbaliascommentaffinity_enabledTrue_coordinate(424, 788)_rotation0iddigital_diff_decoder_bb_0maxoutbuf0minoutbuf0modulus2digital_mpsk_receiver_ccaliascommentaffinity_enabledTrue_coordinate(528, 488)_rotation0gain_mu0.05gain_omega0.001iddigital_mpsk_receiver_cc_0w1*cmath.pi/100.0M2fmax0.06maxoutbuf0fmin-0.06minoutbuf0mu0.5omega_relative_limit0.005omegasamp_rate/bb_decim/audio_decim/ 2375.0theta0freq_xlating_fir_filter_xxxaliascenter_freqfreq_offsetcommentaffinitydecim1_enabledTrue_coordinate(279, 296)_rotation0idfreq_xlating_fir_filter_xxx_0maxoutbuf0minoutbuf0samp_ratesamp_ratetapsfirdes.low_pass(1, samp_rate, xlate_bandwidth, 100000)typecccfreq_xlating_fir_filter_xxxaliascenter_freq57e3commentaffinitydecimaudio_decim_enabledTrue_coordinate(72, 532)_rotation0idfreq_xlating_fir_filter_xxx_1maxoutbuf0minoutbuf0samp_ratebaseband_ratetapsfirdes.low_pass(2500.0,baseband_rate,2.4e3,2e3,firdes.WIN_HAMMING)typefccgr_rds_decoderaliascommentaffinitydebugFalse_enabledTrue_coordinate(632, 780)_rotation0idgr_rds_decoder_0logFalsemaxoutbuf0minoutbuf0gr_rds_panelaliascommentaffinity_enabledTruefreqfreq_coordinate(984, 792)_rotation0grid_posidgr_rds_panel_0notebookgr_rds_parseraliascommentaffinitydebugFalse_enabledTrue_coordinate(800, 772)_rotation0idgr_rds_parser_0logTruemaxoutbuf0minoutbuf0pty_locale0reset0importaliascomment_enabledTrue_coordinate(576, 4)_rotation0idimport_0importimport mathnotebookaliascomment_enabledTrue_coordinate(184, 6)_rotation0grid_posidnblabels['BB', 'Demod', 'L+R', 'Pilot', 'DSBSC', 'RDS', 'L-R', 'RDS constellation','Waterfall']notebookstylewx.NB_TOProot_raised_cosine_filteralpha1aliascommentaffinitydecim1_enabledTruetypefir_filter_ccf_coordinate(304, 516)_rotation0gain1idroot_raised_cosine_filter_0interp1maxoutbuf0minoutbuf0ntaps100samp_ratesamp_rate/bb_decim/audio_decimsym_rate2375rtlsdr_sourcealiasant0bb_gain020bw00dc_offset_mode00corr00freq0freq_tunegain_mode0Falseif_gain020iq_balance_mode00gain0gainant10bb_gain1020bw100dc_offset_mode100corr100freq10100e6gain_mode10Falseif_gain1020iq_balance_mode100gain1010ant11bb_gain1120bw110dc_offset_mode110corr110freq11100e6gain_mode11Falseif_gain1120iq_balance_mode110gain1110ant12bb_gain1220bw120dc_offset_mode120corr120freq12100e6gain_mode12Falseif_gain1220iq_balance_mode120gain1210ant13bb_gain1320bw130dc_offset_mode130corr130freq13100e6gain_mode13Falseif_gain1320iq_balance_mode130gain1310ant14bb_gain1420bw140dc_offset_mode140corr140freq14100e6gain_mode14Falseif_gain1420iq_balance_mode140gain1410ant15bb_gain1520bw150dc_offset_mode150corr150freq15100e6gain_mode15Falseif_gain1520iq_balance_mode150gain1510ant16bb_gain1620bw160dc_offset_mode160corr160freq16100e6gain_mode16Falseif_gain1620iq_balance_mode160gain1610ant17bb_gain1720bw170dc_offset_mode170corr170freq17100e6gain_mode17Falseif_gain1720iq_balance_mode170gain1710ant18bb_gain1820bw180dc_offset_mode180corr180freq18100e6gain_mode18Falseif_gain1820iq_balance_mode180gain1810ant19bb_gain1920bw190dc_offset_mode190corr190freq19100e6gain_mode19Falseif_gain1920iq_balance_mode190gain1910ant1bb_gain120bw10dc_offset_mode10corr10freq1100e6gain_mode1Falseif_gain120iq_balance_mode10gain110ant20bb_gain2020bw200dc_offset_mode200corr200freq20100e6gain_mode20Falseif_gain2020iq_balance_mode200gain2010ant21bb_gain2120bw210dc_offset_mode210corr210freq21100e6gain_mode21Falseif_gain2120iq_balance_mode210gain2110ant22bb_gain2220bw220dc_offset_mode220corr220freq22100e6gain_mode22Falseif_gain2220iq_balance_mode220gain2210ant23bb_gain2320bw230dc_offset_mode230corr230freq23100e6gain_mode23Falseif_gain2320iq_balance_mode230gain2310ant24bb_gain2420bw240dc_offset_mode240corr240freq24100e6gain_mode24Falseif_gain2420iq_balance_mode240gain2410ant25bb_gain2520bw250dc_offset_mode250corr250freq25100e6gain_mode25Falseif_gain2520iq_balance_mode250gain2510ant26bb_gain2620bw260dc_offset_mode260corr260freq26100e6gain_mode26Falseif_gain2620iq_balance_mode260gain2610ant27bb_gain2720bw270dc_offset_mode270corr270freq27100e6gain_mode27Falseif_gain2720iq_balance_mode270gain2710ant28bb_gain2820bw280dc_offset_mode280corr280freq28100e6gain_mode28Falseif_gain2820iq_balance_mode280gain2810ant29bb_gain2920bw290dc_offset_mode290corr290freq29100e6gain_mode29Falseif_gain2920iq_balance_mode290gain2910ant2bb_gain220bw20dc_offset_mode20corr20freq2100e6gain_mode2Falseif_gain220iq_balance_mode20gain210ant30bb_gain3020bw300dc_offset_mode300corr300freq30100e6gain_mode30Falseif_gain3020iq_balance_mode300gain3010ant31bb_gain3120bw310dc_offset_mode310corr310freq31100e6gain_mode31Falseif_gain3120iq_balance_mode310gain3110ant3bb_gain320bw30dc_offset_mode30corr30freq3100e6gain_mode3Falseif_gain320iq_balance_mode30gain310ant4bb_gain420bw40dc_offset_mode40corr40freq4100e6gain_mode4Falseif_gain420iq_balance_mode40gain410ant5bb_gain520bw50dc_offset_mode50corr50freq5100e6gain_mode5Falseif_gain520iq_balance_mode50gain510ant6bb_gain620bw60dc_offset_mode60corr60freq6100e6gain_mode6Falseif_gain620iq_balance_mode60gain610ant7bb_gain720bw70dc_offset_mode70corr70freq7100e6gain_mode7Falseif_gain720iq_balance_mode70gain710ant8bb_gain820bw80dc_offset_mode80corr80freq8100e6gain_mode8Falseif_gain820iq_balance_mode80gain810ant9bb_gain920bw90dc_offset_mode90corr90freq9100e6gain_mode9Falseif_gain920iq_balance_mode90gain910commentaffinityargs_enabled1_coordinate(24, 272)_rotation0idrtlsdr_source_0maxoutbuf0clock_source0time_source0clock_source1time_source1clock_source2time_source2clock_source3time_source3clock_source4time_source4clock_source5time_source5clock_source6time_source6clock_source7time_source7minoutbuf0nchan1num_mboards1typefc32sample_ratesamp_ratesyncwxgui_fftsink2avg_alpha0.8averageTruebaseband_freq0aliascommentaffinity_enabledTruefft_size1024freqvarNone_coordinate(1056, 56)_rotation0grid_posidwxgui_fftsink2_0notebooknb, 0peak_holdFalseref_level-30ref_scale2.0fft_rate15samp_ratesamp_ratetitleBasebandtypecomplexwin_sizewinNoney_divs10y_per_div10wxgui_fftsink2avg_alpha0.8averageTruebaseband_freq0aliascommentaffinity_enabledTruefft_size1024freqvarNone_coordinate(1056, 280)_rotation0grid_posidwxgui_fftsink2_0_0notebooknb, 1peak_holdFalseref_level0ref_scale2.0fft_rate15samp_ratebaseband_ratetitleFM Demodtypefloatwin_sizewinNoney_divs10y_per_div10wxgui_scopesink2ac_coupleFalsealiascommentaffinity_enabledTrue_coordinate(1056, 500)_rotation0grid_posidwxgui_scopesink2_1notebooknb,7num_inputs1samp_rate2375t_scale0titleScope Plottrig_modewxgui.TRIG_MODE_AUTOtypecomplexv_offset0v_scale0.4win_sizexy_modeTruey_axis_labelCountsanalog_wfm_rcv_0freq_xlating_fir_filter_xxx_100analog_wfm_rcv_0wxgui_fftsink2_0_000blocks_complex_to_real_0digital_binary_slicer_fb_000blocks_keep_one_in_n_0digital_diff_decoder_bb_000digital_binary_slicer_fb_0blocks_keep_one_in_n_000digital_diff_decoder_bb_0gr_rds_decoder_000digital_mpsk_receiver_cc_0blocks_complex_to_real_000digital_mpsk_receiver_cc_0wxgui_scopesink2_100freq_xlating_fir_filter_xxx_0analog_wfm_rcv_000freq_xlating_fir_filter_xxx_0wxgui_fftsink2_000freq_xlating_fir_filter_xxx_1root_raised_cosine_filter_000gr_rds_decoder_0gr_rds_parser_0outingr_rds_parser_0gr_rds_panel_0outinroot_raised_cosine_filter_0digital_mpsk_receiver_cc_000rtlsdr_source_0freq_xlating_fir_filter_xxx_000


    Also popular now: