diff --git a/python/usrp_c4fm_tx_nogui.py b/python/usrp_c4fm_tx_nogui.py index 2f77c1d..3f07450 100755 --- a/python/usrp_c4fm_tx_nogui.py +++ b/python/usrp_c4fm_tx_nogui.py @@ -27,7 +27,7 @@ import wx from gnuradio import audio from gnuradio import eng_notation from gnuradio import gr -from gnuradio import p25_mod_bf +from gnuradio import gru from gnuradio import usrp from gnuradio.eng_option import eng_option @@ -39,6 +39,60 @@ except Exception: import fsk4, op25 +""" +Hierarchical block for RRC-filtered P25 FM modulation. + +The input is a dibit (P25 symbol) stream (char, not packed) and the +output is the float "C4FM" signal at baseband, suitable for +application to an FM modulator stage + +Input is at the base symbol rate (4800), output sample rate is +typically either 32000 (USRP TX chain) or 48000 (sound card) + +@param output_sample_rate: output sample rate +@type output_sample_rate: integer +@param excess_bw: Root-raised cosine filter excess bandwidth +@type excess_bw: float +@param reverse: reverse polarity flag +@type reverse: bool +@param verbose: Print information about modulator? +@type verbose: bool +@param debug: Print modulation data to files? +@type debug: bool +""" +class p25_mod_bf(gr.hier_block2): + + def __init__(self, output_sample_rate): + + gr.hier_block2.__init__(self, "p25_c4fm_mod_bf", + gr.io_signature(1, 1, gr.sizeof_char), # Input signature + gr.io_signature(1, 1, gr.sizeof_float)) # Output signature + + input_sample_rate = 4800 # P25 baseband symbol rate + lcm = gru.lcm(input_sample_rate, output_sample_rate) + self._interp_factor = int(lcm // input_sample_rate) + self._decimation = int(lcm // output_sample_rate) + self._excess_bw =0.2 + + mod_map = [1.0/3.0, 1.0, -(1.0/3.0), -1.0] + self.C2S = gr.chunks_to_symbols_bf(mod_map) + + ntaps = 11 * self._interp_factor + rrc_taps = gr.firdes.root_raised_cosine( + self._interp_factor, # gain (since we're interpolating by sps) + lcm, # sampling rate + input_sample_rate, # symbol rate + self._excess_bw, # excess bandwidth (roll-off factor) + ntaps) + + self.rrc_filter = gr.interp_fir_filter_fff(self._interp_factor, rrc_taps) + + # FM pre-emphasis filter + shaping_coeffs = [-0.018, 0.0347, 0.0164, -0.0064, -0.0344, -0.0522, -0.0398, 0.0099, 0.0798, 0.1311, 0.121, 0.0322, -0.113, -0.2499, -0.3007, -0.2137, -0.0043, 0.2825, 0.514, 0.604, 0.514, 0.2825, -0.0043, -0.2137, -0.3007, -0.2499, -0.113, 0.0322, 0.121, 0.1311, 0.0798, 0.0099, -0.0398, -0.0522, -0.0344, -0.0064, 0.0164, 0.0347, -0.018] + self.shaping_filter = gr.fir_filter_fff(1, shaping_coeffs) + + self.connect(self, self.C2S, self.rrc_filter, self.shaping_filter, self) + """ Simple tx-from-pcap-file utility. """ @@ -57,48 +111,41 @@ class usrp_c4fm_tx_block(gr.top_block): # open the pcap source pcap = op25.pcap_source_b(filename, delay, repeat) - # ToDo: consider octet -> symbol unpacking in flow graph - c4fm = p25_mod_bf.p25_mod(output_sample_rate=channel_rate, log=False, verbose=False) - self.connect(pcap, c4fm) + # modulator + c4fm = p25_mod_bf(output_sample_rate=channel_rate) # setup low pass filter + interpolator - interp_factor = self.usrp_rate / self.channel_rate + interp_factor = usrp_rate / channel_rate low_pass = 2.88e3 - interp_taps = gr.firdes.low_pass(1.0, self.usrp_rate, low_pass, low_pass * 0.1, gr.firdes.WIN_HANN) + interp_taps = gr.firdes.low_pass(1.0, usrp_rate, low_pass, low_pass * 0.1, gr.firdes.WIN_HANN) interpolator = gr.interp_fir_filter_fff (int(interp_factor), interp_taps) - self.connect(c4fm, interpolator) # frequency modulator max_dev = 12.5e3 - k = 2 * math.pi * max_dev / self.usrp_rate + k = 2 * math.pi * max_dev / usrp_rate adjustment = 1.5 # adjust for proper c4fm deviation level fm = gr.frequency_modulator_fc(k * adjustment) - self.connect(interpolator, fm) - # amplify signal + # signal gain gain = gr.multiply_const_cc(4000) - self.connect(fm, gain) - # # configure USRP + # configure USRP u = usrp.sink_c() if subdev_spec is None: subdev_spec = usrp.pick_tx_subdevice(u) u.set_mux(usrp.determine_tx_mux_value(u, subdev_spec)) - subdev = usrp.selected_subdev(u, subdev_spec) - print "Using TX d'board %s" % (subdev.side_and_name(),) + self.db = usrp.selected_subdev(u, subdev_spec) + print "Using TX d'board %s" % (self.db.side_and_name(),) if gain is None: - g = subdev.gain_range() + g = self.db.gain_range() gain = float(g[0] + g[1]) / 2 - subdev.set_gain(self.subdev.gain_range()[0]) - u.tune(subdev.which(), subdev, freq) - self.connect(gain, u) - subdev.set_enable(True) - -# sink = gr.null_sink(gr.sizeof_float) -# self.connect(gain, sink) + self.db.set_gain(self.db.gain_range()[0]) + u.tune(self.db.which(), self.db, freq) + self.db.set_enable(True) + self.connect(pcap,c4fm, interpolator, fm, gain, u) # inject frames # @@ -106,7 +153,7 @@ if __name__ == '__main__': from optparse import OptionParser parser = OptionParser (option_class=eng_option) - parser.add_option("-T", "--tx-subdev-spec", type="subdev", default=None, + parser.add_option("-T", "--subdev-spec", type="subdev", default=None, help="select USRP Tx side A or B") parser.add_option("-f", "--freq", type="eng_float", default=None, help="set Tx frequency to FREQ [required]") @@ -127,4 +174,4 @@ if __name__ == '__main__': rx = usrp_c4fm_tx_block(options.subdev_spec, options.freq, options.gain, options.pcap_file, options.delay, options.repeat) rx.run() except KeyboardInterrupt: - rx.subdev.set_enable(False) + rx.db.set_enable(False)