Stereo FM transmitter using gnuradio
(3 intermediate revisions by one user not shown) |
Latest revision as of 10:08, 15 April 2013
Stereo FM transmitter using gnuradio.
Tools needed:
- gnuradio 3.2
- grc (gnuradio companion)
- mpg123
- USRP
In this example we will show how to make a Stereo FM software transmitter. Input is from an mp3 stream for an Internet radio.
Look at GRC schema or use directly python code below.
Input file is a unix FIFO file that means that we need to feed it externally from the shell using these commands:
Creation of the FIFO file (once):
mkfifo stream_32k.fifo
Decoding of an mp3 Internet radio station stream, conversion and output of raw linear samples to the FIFO:
mpg123 -r32000 -s http://maxxima.mine.nu:8000 >stream_32k.fifo
"-r32000" perform sample rate conversion to 32kHz.
Note: This transmitter could be used without USRP by feeding the multiplex stream to to a high samplerate soundcard connected to a hardware FM modulator. To be tested...
[edit] GRC Schema
ERROR: An error exists in this program, preemphasis block must be added on audio and not the whole signal, it is advised to use the other FM RDS Stereo transmitter using gnuradio (error on this one will be corrected in the future, orginal source has been lost)
[edit] Python code
#!/usr/bin/env python ################################################## # Gnuradio Python Flow Graph # Title: FM Stereo Transmitter # Author: Mathias Coinchon # Generated: Sat Jan 2 19:06:07 2010 ################################################## from gnuradio import blks2 from gnuradio import gr from gnuradio.eng_option import eng_option from gnuradio.gr import firdes from gnuradio.wxgui import fftsink2 from gnuradio.wxgui import forms from grc_gnuradio import usrp as grc_usrp from grc_gnuradio import wxgui as grc_wxgui from optparse import OptionParser import wx class FM_stereo_tx(grc_wxgui.top_block_gui): def __init__(self): grc_wxgui.top_block_gui.__init__(self, title="FM Stereo Transmitter") ################################################## # Variables ################################################## self.st_gain = st_gain = 10 self.samp_rate = samp_rate = 32000 self.pilot_gain = pilot_gain = 80e-3 self.mpx_rate = mpx_rate = 160000 self.Mono_gain = Mono_gain = 0.3 self.FM_freq = FM_freq = 107900000 ################################################## # Controls ################################################## _st_gain_sizer = wx.BoxSizer(wx.VERTICAL) self._st_gain_text_box = forms.text_box( parent=self.GetWin(), sizer=_st_gain_sizer, value=self.st_gain, callback=self.set_st_gain, label="Stereo gain", converter=forms.float_converter(), proportion=0, ) self._st_gain_slider = forms.slider( parent=self.GetWin(), sizer=_st_gain_sizer, value=self.st_gain, callback=self.set_st_gain, minimum=0, maximum=100, num_steps=100, style=wx.SL_HORIZONTAL, cast=float, proportion=1, ) self.Add(_st_gain_sizer) _pilot_gain_sizer = wx.BoxSizer(wx.VERTICAL) self._pilot_gain_text_box = forms.text_box( parent=self.GetWin(), sizer=_pilot_gain_sizer, value=self.pilot_gain, callback=self.set_pilot_gain, label='pilot_gain', converter=forms.float_converter(), proportion=0, ) self._pilot_gain_slider = forms.slider( parent=self.GetWin(), sizer=_pilot_gain_sizer, value=self.pilot_gain, callback=self.set_pilot_gain, minimum=0, maximum=1, num_steps=100, style=wx.SL_HORIZONTAL, cast=float, proportion=1, ) self.Add(_pilot_gain_sizer) _Mono_gain_sizer = wx.BoxSizer(wx.VERTICAL) self._Mono_gain_text_box = forms.text_box( parent=self.GetWin(), sizer=_Mono_gain_sizer, value=self.Mono_gain, callback=self.set_Mono_gain, label="Mono signal gain", converter=forms.float_converter(), proportion=0, ) self._Mono_gain_slider = forms.slider( parent=self.GetWin(), sizer=_Mono_gain_sizer, value=self.Mono_gain, callback=self.set_Mono_gain, minimum=0, maximum=1, num_steps=100, style=wx.SL_HORIZONTAL, cast=float, proportion=1, ) self.Add(_Mono_gain_sizer) _FM_freq_sizer = wx.BoxSizer(wx.VERTICAL) self._FM_freq_text_box = forms.text_box( parent=self.GetWin(), sizer=_FM_freq_sizer, value=self.FM_freq, callback=self.set_FM_freq, label="FM Frequency", converter=forms.float_converter(), proportion=0, ) self._FM_freq_slider = forms.slider( parent=self.GetWin(), sizer=_FM_freq_sizer, value=self.FM_freq, callback=self.set_FM_freq, minimum=87500000, maximum=108000000, num_steps=205, style=wx.SL_HORIZONTAL, cast=float, proportion=1, ) self.Add(_FM_freq_sizer) ################################################## # Blocks ################################################## self.Audio_A_resampler = blks2.rational_resampler_fff( interpolation=5, decimation=1, taps=None, fractional_bw=None, ) self.Audio_B_resampler = blks2.rational_resampler_fff( interpolation=5, decimation=1, taps=None, fractional_bw=None, ) self.MPX_upsampler = blks2.rational_resampler_fff( interpolation=4, decimation=1, taps=None, fractional_bw=None, ) self.Pilot = gr.sig_source_f(mpx_rate, gr.GR_SIN_WAVE, 19000, pilot_gain, 0) self.band_pass_filter_0 = gr.fir_filter_fff(1, firdes.band_pass( st_gain, mpx_rate, 23e3, 53e3, 2e3, firdes.WIN_HAMMING, 6.76)) self.blks2_fm_preemph_0 = blks2.fm_preemph(fs=mpx_rate, tau=50e-6) self.gr_add_xx_0 = gr.add_vff(1) self.gr_add_xx_1 = gr.add_vff(1) self.gr_file_source_0 = gr.file_source(gr.sizeof_short*2, "/home/mc/FM_demo/stream_32k.fifo", True) self.gr_frequency_modulator_fc_0 = gr.frequency_modulator_fc(0.98) self.gr_multiply_const_vxx_0 = gr.multiply_const_vcc((32768, )) self.gr_multiply_const_vxx_1 = gr.multiply_const_vff((0.00003, )) self.gr_multiply_const_vxx_3 = gr.multiply_const_vff((0.00003, )) self.gr_multiply_xx_1 = gr.multiply_vff(1) self.gr_short_to_float_0 = gr.short_to_float() self.gr_short_to_float_1 = gr.short_to_float() self.gr_sub_xx_0 = gr.sub_ff(1) self.gr_vector_to_streams_0 = gr.vector_to_streams(gr.sizeof_short*1, 2) self.low_pass_filter_0 = gr.fir_filter_fff(1, firdes.low_pass( Mono_gain, mpx_rate, 15e3, 2e3, firdes.WIN_HAMMING, 6.76)) self.st_38k_carrier = gr.sig_source_f(mpx_rate, gr.GR_SIN_WAVE, 38000, 30e-3, 0) self.usrp_simple_sink_x_0 = grc_usrp.simple_sink_c(which=0, side="B") self.usrp_simple_sink_x_0.set_interp_rate(200) self.usrp_simple_sink_x_0.set_frequency(-FM_freq, verbose=True) self.usrp_simple_sink_x_0.set_gain(0) self.usrp_simple_sink_x_0.set_enable(True) self.wxgui_fftsink2_0 = fftsink2.fft_sink_f( self.GetWin(), baseband_freq=0, y_per_div=10, y_divs=10, ref_level=50, sample_rate=mpx_rate, fft_size=1024, fft_rate=30, average=False, avg_alpha=None, title="Baseband spectrum", peak_hold=False, ) self.Add(self.wxgui_fftsink2_0.win) ################################################## # Connections ################################################## self.connect((self.Audio_A_resampler, 0), (self.gr_add_xx_1, 0)) self.connect((self.Audio_B_resampler, 0), (self.gr_add_xx_1, 1)) self.connect((self.Audio_A_resampler, 0), (self.gr_sub_xx_0, 0)) self.connect((self.gr_multiply_xx_1, 0), (self.band_pass_filter_0, 0)) self.connect((self.st_38k_carrier, 0), (self.gr_multiply_xx_1, 1)) self.connect((self.low_pass_filter_0, 0), (self.gr_add_xx_0, 0)) self.connect((self.gr_add_xx_1, 0), (self.low_pass_filter_0, 0)) self.connect((self.gr_frequency_modulator_fc_0, 0), (self.gr_multiply_const_vxx_0, 0)) self.connect((self.MPX_upsampler, 0), (self.gr_frequency_modulator_fc_0, 0)) self.connect((self.blks2_fm_preemph_0, 0), (self.MPX_upsampler, 0)) self.connect((self.gr_add_xx_0, 0), (self.blks2_fm_preemph_0, 0)) self.connect((self.Pilot, 0), (self.gr_add_xx_0, 2)) self.connect((self.band_pass_filter_0, 0), (self.gr_add_xx_0, 1)) self.connect((self.gr_add_xx_0, 0), (self.wxgui_fftsink2_0, 0)) self.connect((self.gr_sub_xx_0, 0), (self.gr_multiply_xx_1, 0)) self.connect((self.Audio_B_resampler, 0), (self.gr_sub_xx_0, 1)) self.connect((self.gr_multiply_const_vxx_0, 0), (self.usrp_simple_sink_x_0, 0)) self.connect((self.gr_file_source_0, 0), (self.gr_vector_to_streams_0, 0)) self.connect((self.gr_short_to_float_1, 0), (self.gr_multiply_const_vxx_1, 0)) self.connect((self.gr_multiply_const_vxx_1, 0), (self.Audio_A_resampler, 0)) self.connect((self.gr_short_to_float_0, 0), (self.gr_multiply_const_vxx_3, 0)) self.connect((self.gr_multiply_const_vxx_3, 0), (self.Audio_B_resampler, 0)) self.connect((self.gr_vector_to_streams_0, 1), (self.gr_short_to_float_1, 0)) self.connect((self.gr_vector_to_streams_0, 0), (self.gr_short_to_float_0, 0)) def set_st_gain(self, st_gain): self.st_gain = st_gain self.band_pass_filter_0.set_taps(firdes.band_pass(self.st_gain, self.mpx_rate, 23e3, 53e3, 2e3, firdes.WIN_HAMMING, 6.76)) self._st_gain_slider.set_value(self.st_gain) self._st_gain_text_box.set_value(self.st_gain) def set_samp_rate(self, samp_rate): self.samp_rate = samp_rate def set_pilot_gain(self, pilot_gain): self.pilot_gain = pilot_gain self.Pilot.set_amplitude(self.pilot_gain) self._pilot_gain_slider.set_value(self.pilot_gain) self._pilot_gain_text_box.set_value(self.pilot_gain) def set_mpx_rate(self, mpx_rate): self.mpx_rate = mpx_rate self.band_pass_filter_0.set_taps(firdes.band_pass(self.st_gain, self.mpx_rate, 23e3, 53e3, 2e3, firdes.WIN_HAMMING, 6.76)) self.low_pass_filter_0.set_taps(firdes.low_pass(self.Mono_gain, self.mpx_rate, 15e3, 2e3, firdes.WIN_HAMMING, 6.76)) self.Pilot.set_sampling_freq(self.mpx_rate) self.st_38k_carrier.set_sampling_freq(self.mpx_rate) self.wxgui_fftsink2_0.set_sample_rate(self.mpx_rate) def set_Mono_gain(self, Mono_gain): self.Mono_gain = Mono_gain self.low_pass_filter_0.set_taps(firdes.low_pass(self.Mono_gain, self.mpx_rate, 15e3, 2e3, firdes.WIN_HAMMING, 6.76)) self._Mono_gain_slider.set_value(self.Mono_gain) self._Mono_gain_text_box.set_value(self.Mono_gain) def set_FM_freq(self, FM_freq): self.FM_freq = FM_freq self.usrp_simple_sink_x_0.set_frequency(-self.FM_freq) self._FM_freq_slider.set_value(self.FM_freq) self._FM_freq_text_box.set_value(self.FM_freq) if __name__ == '__main__': parser = OptionParser(option_class=eng_option, usage="%prog: [options]") (options, args) = parser.parse_args() tb = FM_stereo_tx() tb.Run(True)