Software Defined Radio - how does it work? Part 9

    Hi, Habr.

    In the previous part , decoding of RDS signals for FM radio stations was considered, and the idea of ​​the next article arose by itself - you need to create your own FM transmitter.

    There will be two options - simple, and more complex, with RDS.



    As in previous cases, we will do all this in GNU Radio without writing a single line of code. For those who are interested, continued under the cut.

    So let's get started. Of course, for the tests we need an SDR with the ability to transfer (HackRF, USRP, LimeSDR).

    Fm transmitter


    The simple transmitter scheme is done in almost two clicks, and I hope it does not present any difficulties in understanding. I use a WAV-file as a source, although you can use another source if you wish, for example, the input of a sound card.



    Actually, there are two key blocks in this scheme - a WBFM transmitter and a Resampler, which converts the sample rate of the stream. All values ​​of the sampling frequency must correspond to each other, otherwise there will be a skip of samples, which will be heard as clicks by ear. The Multiply Const block is used to adjust the input level. It is also important not to confuse, in GNU Radio there are two different blocks for FM - WBFM and NBFM. We need exactly the first, Wide Band FM. NFM modulation is used for portable radios.

    In general, everything is simple, we start, it works. There are no UI blocks in the circuit, so you can use it also from the command line (more in the 4th part ).

    By the way, if you look at the spectrum, you can make sure that a simple mono signal is transmitted, without any channels, pilot tones and other things.



    It's time to move on to a more complex option.

    Transmitter with RDS


    The transmitter circuit with RDS will, of course, be more complicated. Its original was taken from gr-rds examples with minor changes (the original version for Windows did not work, the “correct” version at the bottom of the article), we will consider what components it consists of.



    (original full resolution ) The

    upper third of the circuit is RDS transmission. At the input there is an RDS encoder, which, based on the available data (station name, country code, etc.), generates RDS packets. Then the bitstream is differential encodedwhose idea is to remove repeating sequences of the form 001000001. Then the signal is processed, and it modulates the “carrier” at 57KHz. The central part is the creation of sound. Here the channels L + R and LR are formed. Bottom as a sound source is a .wav file. Finally, the Add block adds all this together, and a 19KHz pilot tone and a second 38KHz tone are added to the signal. All this total signal is sent to the frequency modulator, all this is sent via the SDR to the air.

    In theory, this scheme is not 100% complete, for example, there is no predistortion block, the phases of the tones at 19, 38 and 57KHz are not synchronized. On the other hand, to understand how sound is broadcasted in general, such a scheme is quite enough; those who wish can finalize it on their own.

    When starting, a window opens, some of the parameters can be changed.



    There was a desire to test RDS with a real receiver, but it turned out that FM radio is already atavism, and I don’t have it at home. Even in the smartphone of the latest model, it is also missing. So I had to use RTL-SDR V3 and GQRX as a control receiver, the result is on the KDPV.

    Of course, before testing it is necessary to choose a free frequency so that there are no stations on it, and it is advisable not to exceed the allowed power. For those who want to get the maximum range, you can buy an amplifier , it is advisable to make the antenna at the right frequency so that there is at least a 1/4 dipole wavelength.

    Conclusion


    As you can see, in principle, there is nothing super complicated in transmitting FM radio, everything is completely feasible in GNU Radio (although it would not be easy to repeat this without examples, given that the system is not documented at all). Well now, at least the readers have a working example.

    The source codes of the blocks under the spoiler.

    fm_tx.grc
    Tue Jun 18 20:27:26 2019optionsauthorwindow_sizecategory[GRC Hier Blocks]commentdescription_enabledTrue_coordinate(16, 20)_rotation0generate_optionswx_guihier_block_src_path.:idtop_blockmax_nouts0qt_qss_themerealtime_schedulingrun_command{python} -u {filename}run_optionspromptrunTruethread_safe_setterstitleanalog_wfm_txaudio_rate80000aliascommentaffinity_enabledTrue_coordinate(592, 76)_rotation0idanalog_wfm_tx_0max_dev75e3maxoutbuf0minoutbuf0fh-1.0quad_rate320000tau75e-6blocks_multiply_const_vxxaliascommentconst0.45affinity_enabledTrue_coordinate(248, 108)_rotation0idblocks_multiply_const_vxx_0typefloatmaxoutbuf0minoutbuf0vlen1blocks_wavfile_sourcealiascommentaffinity_enabledTruefileD:\MyProjects\GNURadio\sound.wav_coordinate(24, 100)_rotation0idblocks_wavfile_source_0maxoutbuf0minoutbuf0nchan1repeatTruerational_resampler_xxxaliascommentaffinitydecim3_enabledTruefbw0_coordinate(408, 84)_rotation0idrational_resampler_xxx_0interp5maxoutbuf0minoutbuf0tapstypefffuhd_usrp_sinkaliasant0bw00center_freq095.6e6norm_gain0Falsegain030ant10bw100center_freq100norm_gain10Falsegain100ant11bw110center_freq110norm_gain11Falsegain110ant12bw120center_freq120norm_gain12Falsegain120ant13bw130center_freq130norm_gain13Falsegain130ant14bw140center_freq140norm_gain14Falsegain140ant15bw150center_freq150norm_gain15Falsegain150ant16bw160center_freq160norm_gain16Falsegain160ant17bw170center_freq170norm_gain17Falsegain170ant18bw180center_freq180norm_gain18Falsegain180ant19bw190center_freq190norm_gain19Falsegain190ant1bw10center_freq10norm_gain1Falsegain10ant20bw200center_freq200norm_gain20Falsegain200ant21bw210center_freq210norm_gain21Falsegain210ant22bw220center_freq220norm_gain22Falsegain220ant23bw230center_freq230norm_gain23Falsegain230ant24bw240center_freq240norm_gain24Falsegain240ant25bw250center_freq250norm_gain25Falsegain250ant26bw260center_freq260norm_gain26Falsegain260ant27bw270center_freq270norm_gain27Falsegain270ant28bw280center_freq280norm_gain28Falsegain280ant29bw290center_freq290norm_gain29Falsegain290ant2bw20center_freq20norm_gain2Falsegain20ant30bw300center_freq300norm_gain30Falsegain300ant31bw310center_freq310norm_gain31Falsegain310ant3bw30center_freq30norm_gain3Falsegain30ant4bw40center_freq40norm_gain4Falsegain40ant5bw50center_freq50norm_gain5Falsegain50ant6bw60center_freq60norm_gain6Falsegain60ant7bw70center_freq70norm_gain7Falsegain70ant8bw80center_freq80norm_gain8Falsegain80ant9bw90center_freq90norm_gain9Falsegain90clock_rate0.0commentaffinitydev_addr""dev_args""_enabledTrue_coordinate(848, 68)_rotation0iduhd_usrp_sink_0typefc32clock_source0sd_spec0time_source0clock_source1sd_spec1time_source1clock_source2sd_spec2time_source2clock_source3sd_spec3time_source3clock_source4sd_spec4time_source4clock_source5sd_spec5time_source5clock_source6sd_spec6time_source6clock_source7sd_spec7time_source7nchan1num_mboards1samp_rate320000hide_cmd_portFalsehide_lo_controlsTruestream_argsstream_chans[]synclen_tag_nameotwanalog_wfm_tx_0uhd_usrp_sink_000blocks_multiply_const_vxx_0rational_resampler_xxx_000blocks_wavfile_source_0blocks_multiply_const_vxx_000rational_resampler_xxx_0analog_wfm_tx_000

    fm_tx_rds.grc
    Thu Aug 28 08:28:15 2014optionsauthorwindow_size1600, 2048categoryRDScommentdescription_enabledTrue_coordinate(104, 4)_rotation0generate_optionswx_guihier_block_src_path.:idrds_txmax_nouts0qt_qss_themerealtime_schedulingrun_command{python} -u {filename}run_optionspromptrunTruethread_safe_setterstitlevariablecomment_enabledTrue_coordinate(352, 4)_rotation0iddata_ratevalue380000variablecomment_enabledTrue_coordinate(440, 4)_rotation0idfm_max_devvalue80e3variablecomment_enabledTrue_coordinate(264, 4)_rotation0idfreqvalue95.6e6variable_slidercommentconververfloat_convertervalue.75_enabledTrue_coordinate(544, 4)_rotation0grid_posidinput_gainlabelmax10min0notebooknum_steps100stylewx.SL_HORIZONTALvariable_slidercommentconververfloat_convertervalue.09_enabledTrue_coordinate(928, 4)_rotation0grid_posidpilot_gainlabelmax3min0notebooknum_steps100stylewx.SL_HORIZONTALvariable_slidercommentconververfloat_convertervalue.05_enabledTrue_coordinate(800, 4)_rotation0grid_posidrds_gainlabelmax3min0notebooknum_steps100stylewx.SL_HORIZONTALvariable_slidercommentconververfloat_convertervalue.45_enabledTrue_coordinate(672, 4)_rotation0grid_posidstereo_gainlabelmax3min0notebooknum_steps100stylewx.SL_HORIZONTALblocks_multiply_const_vxxaliascommentconstinput_gainaffinity_enabledTrue_coordinate(392, 764)_rotation0idblocks_multiply_const_vxx_0typefloatmaxoutbuf0minoutbuf0vlen1blocks_multiply_const_vxxaliascommentconstrds_gainaffinity_enabled1_coordinate(948, 432)_rotation270idblocks_multiply_const_vxx_0_0typefloatmaxoutbuf0minoutbuf0vlen1blocks_multiply_const_vxxaliascommentconstpilot_gainaffinity_enabledTrue_coordinate(200, 300)_rotation0idblocks_multiply_const_vxx_0_0_1typefloatmaxoutbuf0minoutbuf0vlen1blocks_multiply_const_vxxaliascommentconstinput_gainaffinity_enabledTrue_coordinate(400, 812)_rotation0idblocks_multiply_const_vxx_0_1typefloatmaxoutbuf0minoutbuf0vlen1blocks_repeataliascommentaffinity_enabled1_coordinate(1080, 172)_rotation0idblocks_repeat_0interp160maxoutbuf0minoutbuf0typefloatvlen1blocks_socket_pdualiascommentaffinity_enabled1_coordinate(24, 148)_rotation0hostidblocks_socket_pdu_0mtu10000maxoutbuf0minoutbuf0port52001tcp_no_delayFalsetype"TCP_SERVER"blocks_throttlealiascommentaffinity_enabledTrue_coordinate(232, 764)_rotation0idblocks_throttle_1ignoretagTruemaxoutbuf0minoutbuf0samples_per_second48000typefloatvlen1blocks_throttlealiascommentaffinity_enabledTrue_coordinate(240, 812)_rotation0idblocks_throttle_2ignoretagTruemaxoutbuf0minoutbuf0samples_per_second48000typefloatvlen1blocks_wavfile_sourcealiascommentaffinity_enabled1fileD:\MyProjects\GNURadio\sound.wav_coordinate(16, 768)_rotation0idblocks_wavfile_source_0maxoutbuf0minoutbuf0nchan2repeatTrueblocks_add_xxaliascommentaffinity_enabledTrue_coordinate(472, 552)_rotation0idgr_add_xx_0typefloatmaxoutbuf0minoutbuf0num_inputs2vlen1blocks_add_xxaliascommentaffinity_enabledTrue_coordinate(840, 640)_rotation270idgr_add_xx_1typefloatmaxoutbuf0minoutbuf0num_inputs4vlen1blocks_char_to_floataliascommentaffinity_enabled1_coordinate(944, 172)_rotation0idgr_char_to_float_0maxoutbuf0minoutbuf0scale1vlen1digital_diff_encoder_bbaliascommentaffinity_enabled1_coordinate(400, 172)_rotation0idgr_diff_encoder_bb_0maxoutbuf0minoutbuf0modulus2analog_frequency_modulator_fcaliascommentaffinity_enabledTrue_coordinate(944, 764)_rotation0idgr_frequency_modulator_fc_0maxoutbuf0minoutbuf0sensitivity2*math.pi*fm_max_dev/data_ratedigital_map_bbaliascommentaffinity_enabled1_coordinate(824, 172)_rotation0idgr_map_bb_0map[-1,1]maxoutbuf0minoutbuf0digital_map_bbaliascommentaffinity_enabled1_coordinate(576, 172)_rotation0idgr_map_bb_1map[1,2]maxoutbuf0minoutbuf0blocks_multiply_xxaliascommentaffinity_enabled1_coordinate(1432, 232)_rotation0idgr_multiply_xx_0typefloatmaxoutbuf0minoutbuf0num_inputs2vlen1blocks_multiply_xxaliascommentaffinity_enabledTrue_coordinate(768, 360)_rotation0idgr_multiply_xx_1typefloatmaxoutbuf0minoutbuf0num_inputs2vlen1gr_rds_encoderaf189.8e6aliascommentaffinity_enabled1_coordinate(160, 96)_rotation0idgr_rds_encoder_0msTruemaxoutbuf0minoutbuf0pi_country_code13pi_coverage_area0pi_reference_number147psSDRRADIOpty_locale0pty0radiotextHello HABR!taFalsetpTrueanalog_sig_source_xamp1aliascommentaffinity_enabledTruefreq38e3_coordinate(24, 372)_rotation0idgr_sig_source_x_0maxoutbuf0minoutbuf0offset0typefloatsamp_ratedata_ratewaveformanalog.GR_SIN_WAVEanalog_sig_source_xamp1aliascommentaffinity_enabled1freq57e3_coordinate(1264, 100)_rotation0idgr_sig_source_x_0_0maxoutbuf0minoutbuf0offset0typefloatsamp_ratedata_ratewaveformanalog.GR_SIN_WAVEanalog_sig_source_xamp1aliascommentaffinity_enabledTruefreq19e3_coordinate(24, 268)_rotation0idgr_sig_source_x_0_1maxoutbuf0minoutbuf0offset0typefloatsamp_ratedata_ratewaveformanalog.GR_SIN_WAVEblocks_sub_xxaliascommentaffinity_enabledTrue_coordinate(448, 456)_rotation0idgr_sub_xx_0typefloatmaxoutbuf0minoutbuf0num_inputs2vlen1blocks_unpack_k_bits_bbaliascommentaffinity_enabled1_coordinate(680, 172)_rotation0idgr_unpack_k_bits_bb_0k2maxoutbuf0minoutbuf0importaliascomment_enabledTrue_coordinate(8, 4)_rotation0idimport_0importimport mathlow_pass_filterbeta6.76aliascommentaffinitycutoff_freq2.5e3decim1_enabled1typeinterp_fir_filter_fff_coordinate(1248, 212)_rotation0gain1idlow_pass_filter_0interp1maxoutbuf0minoutbuf0samp_ratedata_ratewidth.5e3winfirdes.WIN_HAMMINGlow_pass_filterbeta6.76aliascommentaffinitycutoff_freq15e3decim1_enabledTruetypeinterp_fir_filter_fff_coordinate(584, 532)_rotation0gain1idlow_pass_filter_0_0interp1maxoutbuf0minoutbuf0samp_ratedata_ratewidth2e3winfirdes.WIN_HAMMINGlow_pass_filterbeta6.76aliascommentaffinitycutoff_freq15e3decim1_enabledTruetypeinterp_fir_filter_fff_coordinate(584, 396)_rotation0gain1idlow_pass_filter_0_0_0interp1maxoutbuf0minoutbuf0samp_ratedata_ratewidth2e3winfirdes.WIN_HAMMINGrational_resampler_xxxaliascommentaffinitydecim6_enabledTruefbw0_coordinate(168, 444)_rotation0idrational_resampler_xxx_0interp48maxoutbuf0minoutbuf0tapstypefffrational_resampler_xxxaliascommentaffinitydecim6_enabledTruefbw0_coordinate(160, 540)_rotation0idrational_resampler_xxx_0_0interp48maxoutbuf0minoutbuf0tapstypefffrational_resampler_xxxaliascommentaffinitydecim38_enabled1fbw0_coordinate(1104, 740)_rotation0idrational_resampler_xxx_1interp50maxoutbuf0minoutbuf0tapstypecccuhd_usrp_sinkaliasant0TX/RXbw00center_freq0freqnorm_gain0Falsegain050ant10bw100center_freq100norm_gain10Falsegain100ant11bw110center_freq110norm_gain11Falsegain110ant12bw120center_freq120norm_gain12Falsegain120ant13bw130center_freq130norm_gain13Falsegain130ant14bw140center_freq140norm_gain14Falsegain140ant15bw150center_freq150norm_gain15Falsegain150ant16bw160center_freq160norm_gain16Falsegain160ant17bw170center_freq170norm_gain17Falsegain170ant18bw180center_freq180norm_gain18Falsegain180ant19bw190center_freq190norm_gain19Falsegain190ant1bw10center_freq10norm_gain1Falsegain10ant20bw200center_freq200norm_gain20Falsegain200ant21bw210center_freq210norm_gain21Falsegain210ant22bw220center_freq220norm_gain22Falsegain220ant23bw230center_freq230norm_gain23Falsegain230ant24bw240center_freq240norm_gain24Falsegain240ant25bw250center_freq250norm_gain25Falsegain250ant26bw260center_freq260norm_gain26Falsegain260ant27bw270center_freq270norm_gain27Falsegain270ant28bw280center_freq280norm_gain28Falsegain280ant29bw290center_freq290norm_gain29Falsegain290ant2bw20center_freq20norm_gain2Falsegain20ant30bw300center_freq300norm_gain30Falsegain300ant31bw310center_freq310norm_gain31Falsegain310ant3bw30center_freq30norm_gain3Falsegain30ant4bw40center_freq40norm_gain4Falsegain40ant5bw50center_freq50norm_gain5Falsegain50ant6bw60center_freq60norm_gain6Falsegain60ant7bw70center_freq70norm_gain7Falsegain70ant8bw80center_freq80norm_gain8Falsegain80ant9bw90center_freq90norm_gain9Falsegain90clock_rate0.0commentaffinitydev_addrdev_args""_enabled1_coordinate(1296, 724)_rotation0iduhd_usrp_sinktypefc32clock_source0sd_spec0time_source0clock_source1sd_spec1time_source1clock_source2sd_spec2time_source2clock_source3sd_spec3time_source3clock_source4sd_spec4time_source4clock_source5sd_spec5time_source5clock_source6sd_spec6time_source6clock_source7sd_spec7time_source7nchan1num_mboards1samp_rate500000hide_cmd_portFalsehide_lo_controlsTruestream_argsstream_chans[]synclen_tag_nameotwwxgui_fftsink2avg_alpha0averageFalsebaseband_freq0aliascommentaffinity_enabled1fft_size1024freqvarNone_coordinate(1104, 520)_rotation0grid_posidwxgui_fftsink2_0notebookpeak_holdFalseref_level0ref_scale2.0fft_rate30samp_ratedata_ratetitleFFT Plottypefloatwin_sizewinNoney_divs10y_per_div20blocks_multiply_const_vxx_0rational_resampler_xxx_000blocks_multiply_const_vxx_0_0gr_add_xx_100blocks_multiply_const_vxx_0_0_1gr_add_xx_101blocks_multiply_const_vxx_0_1rational_resampler_xxx_0_000blocks_repeat_0low_pass_filter_000blocks_socket_pdu_0gr_rds_encoder_0pdusrds inblocks_throttle_1blocks_multiply_const_vxx_000blocks_throttle_2blocks_multiply_const_vxx_0_100blocks_wavfile_source_0blocks_throttle_100blocks_wavfile_source_0blocks_throttle_210gr_add_xx_0low_pass_filter_0_000gr_add_xx_1gr_frequency_modulator_fc_000gr_add_xx_1wxgui_fftsink2_000gr_char_to_float_0blocks_repeat_000gr_diff_encoder_bb_0gr_map_bb_100gr_frequency_modulator_fc_0rational_resampler_xxx_100gr_map_bb_0gr_char_to_float_000gr_map_bb_1gr_unpack_k_bits_bb_000gr_multiply_xx_0blocks_multiply_const_vxx_0_000gr_multiply_xx_1gr_add_xx_102gr_rds_encoder_0gr_diff_encoder_bb_000gr_sig_source_x_0gr_multiply_xx_100gr_sig_source_x_0_0gr_multiply_xx_000gr_sig_source_x_0_1blocks_multiply_const_vxx_0_0_100gr_sub_xx_0low_pass_filter_0_0_000gr_unpack_k_bits_bb_0gr_map_bb_000low_pass_filter_0gr_multiply_xx_001low_pass_filter_0_0gr_add_xx_103low_pass_filter_0_0_0gr_multiply_xx_101rational_resampler_xxx_0gr_add_xx_000rational_resampler_xxx_0gr_sub_xx_000rational_resampler_xxx_0_0gr_add_xx_001rational_resampler_xxx_0_0gr_sub_xx_001rational_resampler_xxx_1uhd_usrp_sink00

    And as usual, all successful experiments. And do not interfere with other radio stations.

    Also popular now: