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

183 lines
6.4 KiB
Python
Executable File

#!/usr/bin/env python
#
# Copyright 2009 KA1RBI
# 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 blks2
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 op25
except Exception:
import 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
"""
class p25_mod_bf(gr.hier_block2):
def __init__(self, output_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
symbol_rate = 4800 # P25 baseband symbol rate
lcm = gru.lcm(symbol_rate, output_rate)
self._interp_factor = int(lcm // symbol_rate)
self._decimation = int(lcm // output_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
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)
# generate output at appropriate rate
self.decimator = blks2.rational_resampler_fff(1, self._decimation)
self.connect(self, self.C2S, self.rrc_filter, self.shaping_filter, self.decimator, self)
def forecast(noutput, ninput_reqd):
ninput_reqd[0] = noutput * 26.7
"""
Simple tx-from-pcap-file utility.
"""
class usrp_c4fm_tx_block(gr.top_block):
def __init__(self, subdev_spec, freq, subdev_gain, filename, delay):
gr.top_block.__init__ (self)
# data sink and sink rate
u = usrp.sink_c()
# important vars (to be calculated from USRP when available
dac_rate = u.dac_rate()
usrp_rate = 320e3
usrp_interp = int(dac_rate // usrp_rate)
channel_rate = 32e3
interp_factor = int(usrp_rate // channel_rate)
# open the pcap source
pcap = op25.pcap_source_b(filename, delay)
# pcap = gr.glfsr_source_b(16)
# convert octets into dibits
bits_per_symbol = 2
unpack = gr.packed_to_unpacked_bb(bits_per_symbol, gr.GR_MSB_FIRST)
# modulator
c4fm = p25_mod_bf(output_rate=channel_rate)
# setup low pass filter + interpolator
low_pass = 2.88e3
interp_taps = gr.firdes.low_pass(1.0, channel_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
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(),)
u.set_interp_rate(usrp_interp)
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, unpack, 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]")
(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)
rx.run()
except KeyboardInterrupt:
rx.db.set_enable(False)