This repository has been archived on 2022-02-17. You can view files and clone it, but cannot push or open issues or pull requests.
op25-legacy/python/usrp_c4fm_tx_nogui.py

178 lines
6.5 KiB
Python
Executable File

#!/usr/bin/env python
#
# Copyright 2011 Steve Glass.
#
# This file is part of GNU Radio and part of OP25
#
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation; either version 3, or (at your option)
# any later version.
#
# It is distributed in the hope that it will be useful, but WITHOUT
# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
# or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public
# License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this; see the file COPYING. If not, write to
# the Free Software Foundation, Inc., 51 Franklin Street,
# Boston, MA 02110-1301, USA.
#
import math
import sys
import wx
from gnuradio import audio
from gnuradio import eng_notation
from gnuradio import gr
from gnuradio import gru
from gnuradio import usrp
from gnuradio.eng_option import eng_option
# Python is putting the packages in some strange places
# This is a workaround until we figure out WTF is going on
try:
from gnuradio import fsk4, op25
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.
"""
class usrp_c4fm_tx_block(gr.top_block):
def __init__(self, subdev_spec, freq, subdev_gain, filename, delay, repeat):
gr.top_block.__init__ (self)
# important vars (to be calculated from USRP when available
dac_rate = 128e6
usrp_interp = 400
usrp_rate = dac_rate / usrp_interp # 320KS/s
sw_interp = 10
channel_rate = usrp_rate / sw_interp # 32 kS/s
# open the pcap source
pcap = op25.pcap_source_b(filename, delay, repeat)
# modulator
c4fm = p25_mod_bf(output_sample_rate=channel_rate)
# setup low pass filter + interpolator
interp_factor = usrp_rate / channel_rate
low_pass = 2.88e3
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)
# frequency modulator
max_dev = 12.5e3
k = 2 * math.pi * max_dev / usrp_rate
adjustment = 1.5 # adjust for proper c4fm deviation level
fm = gr.frequency_modulator_fc(k * adjustment)
# signal gain
gain = gr.multiply_const_cc(4000)
# 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))
self.db = usrp.selected_subdev(u, subdev_spec)
print "Using TX d'board %s" % (self.db.side_and_name(),)
if gain is None:
g = self.db.gain_range()
gain = float(g[0] + g[1]) / 2
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
#
if __name__ == '__main__':
from optparse import OptionParser
parser = OptionParser (option_class=eng_option)
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]")
parser.add_option("-g", "--gain", type="eng_float", default=None,
help="set device gain")
parser.add_option("-p", "--pcap-file", default="",
help="packet capture file [required]")
parser.add_option("-d", "--delay", type="int", default=0,
help="delay before TX of first symbol [default=%default]")
parser.add_option("-r","--repeat", action="store_true", default=False,
help="continuously replay input file [default=%default]")
(options, args) = parser.parse_args ()
if options.freq is None:
parser.print_help()
sys.exit(1)
try:
rx = usrp_c4fm_tx_block(options.subdev_spec, options.freq, options.gain, options.pcap_file, options.delay, options.repeat)
rx.run()
except KeyboardInterrupt:
rx.db.set_enable(False)