Advertisement
Not a member of Pastebin yet?
Sign Up,
it unlocks many cool features!
- root@bt:~/gr-smartnet/src/python# cat smartnet2logging_rtl2.py
- #!/usr/bin/env python
- """
- This program decodes the Motorola SmartNet II trunking protocol from the control channel
- Tune it to the control channel center freq, and it'll spit out the decoded packets.
- In what format? Who knows.
- This program does not include audio output support. It logs channels to disk by talkgroup name. If you don't specify what talkgroups to log, it logs EVERYTHING.
- """
- from gnuradio import gr, gru, blks2, optfir, digital
- from grc_gnuradio import blks2 as grc_blks2
- from gnuradio import audio
- from gnuradio import eng_notation
- #from gnuradio import uhd
- from fsk_demod import fsk_demod
- from logging_receiver import logging_receiver
- from optparse import OptionParser
- from gnuradio.eng_option import eng_option
- from gnuradio import smartnet
- #from gnuradio.wxgui import slider
- #from gnuradio.wxgui import stdgui2, fftsink2, form
- import osmosdr
- from gnuradio import digital
- #from pkt import *
- import time
- import gnuradio.gr.gr_threading as _threading
- import csv
- import os
- class top_block_runner(_threading.Thread):
- def __init__(self, tb):
- _threading.Thread.__init__(self)
- self.setDaemon(1)
- self.tb = tb
- self.done = False
- self.start()
- def run(self):
- self.tb.run()
- self.done = True
- class my_top_block(gr.top_block):
- def __init__(self, options, queue):
- gr.top_block.__init__(self)
- if options.centerfreq is 0:
- print "Center Frequency not defined, auto-calculating..."
- self.centerfreq = ((options.rate / 2) + (options.freq - options.rate)) + .100
- else:
- self.centerfreq = options.centerfreq
- print "Setting Center Frequency to: %fMHz" % self.centerfreq
- self.rate = options.rate * 1000000
- print "Using Rate: %f (%iM)" % (options.rate, self.rate)
- self.rtl = osmosdr.source_c( args="nchan=" + str(1) + " " + "" )
- self.rtl.set_sample_rate(self.rate)
- self.rtl.set_center_freq(options.centerfreq, 0)
- self.rtl.set_freq_corr(options.ppm, 0)
- self.rtl.set_gain_mode(1, 0)
- print "Tuning to: %fMHz" % (self.centerfreq - options.error)
- if not(self.tune(options.centerfreq - options.error)):
- print "Failed to set initial frequency"
- if options.gain is None: #set to halfway
- # g = self.u.get_gain_range()
- # options.gain = (g.start()+g.stop()) / 2.0
- options.gain = 10
- # TODO FIX^
- print "Setting gain to %i" % options.gain
- self.rtl.set_gain(options.gain, 0)
- print "Samples per second is %fM" % self.rate
- self._syms_per_sec = 3600;
- options.audiorate = 11025
- options.rate = self.rate
- options.samples_per_second = self.rate #yeah i know it's on the list
- options.syms_per_sec = self._syms_per_sec
- options.gain_mu = 0.01
- options.mu=0.5
- options.omega_relative_limit = 0.3
- options.syms_per_sec = self._syms_per_sec
- options.offset = options.freq - self.centerfreq
- print "Control channel offset: %f" % options.offset
- self.demod = fsk_demod(options)
- self.start_correlator = digital.correlate_access_code_bb("10101100",0) #should mark start of packet
- self.smartnet_sync = smartnet.sync()
- self.smartnet_deinterleave = smartnet.deinterleave()
- self.smartnet_parity = smartnet.parity()
- self.smartnet_crc = smartnet.crc()
- self.smartnet_packetize = smartnet.packetize()
- self.parse = smartnet.parse(queue) #packet-based. this simply posts lightly-formatted messages to the queue.
- self.connect(self.rtl, self.demod)
- self.connect(self.demod, self.start_correlator, self.smartnet_sync, self.smartnet_deinterleave, self.smartnet_parity, self.smartnet_crc, self.smartnet_packetize, self.parse)
- def tune(self, freq):
- result = self.rtl.set_center_freq(freq)
- return True
- def getfreq(chanlist, cmd):
- if chanlist is None: #if no chanlist file, make a guess. there are four extant bandplan schemes, and i believe this one is the most common.
- # if cmd < 0x2d0:
- # Changed for rebanding
- if cmd < 0x1b8:
- freq = float(cmd * 0.025 + 851.0125)
- elif cmd < 0x230:
- freq = float(cmd * 0.025 + 851.0125 - 10.9875)
- else:
- freq = None
- else: #program your channel listings, get the right freqs.
- if chanlist.get(str(cmd), None) is not None:
- freq = float(chanlist[str(cmd)])
- else:
- freq = None
- return freq
- def parsefreq(s, chanlist):
- retfreq = None
- [address, groupflag, command] = s.split(",")
- command = int(command)
- address = int(address) & 0xFFF0
- groupflag = bool(groupflag)
- if chanlist is None:
- if command < 0x2d0:
- retfreq = getfreq(chanlist, command)
- else:
- if chanlist.get(str(command), None) is not None: #if it falls into the channel somewhere
- retfreq = getfreq(chanlist, command)
- return [retfreq, address] # mask so the squelch opens up on the entire group
- def main():
- # Create Options Parser:
- parser = OptionParser (option_class=eng_option, conflict_handler="resolve")
- expert_grp = parser.add_option_group("Expert")
- parser.add_option("-f", "--freq", type="eng_float", default=866.9625,
- help="set control channel frequency to MHz [default=%default]", metavar="FREQ")
- parser.add_option("-c", "--centerfreq", type="eng_float", default=0,
- help="set center receive frequency to MHz [default=%default]. Set to center of 800MHz band for best results")
- parser.add_option("-g", "--gain", type="int", default=None,
- help="set RF gain", metavar="dB")
- parser.add_option("-r", "--rate", type="eng_float", default=2.800000,
- help="set sample rate [default=%default]")
- # parser.add_option("-b", "--bandwidth", type="eng_float", default=3e6,
- # help="set bandwidth of DBS RX frond end [default=%default]")
- parser.add_option("-C", "--chanlistfile", type="string", default=None,
- help="read in list of Motorola channel frequencies (improves accuracy of frequency decoding) [default=%default]")
- parser.add_option("-E", "--error", type="eng_float", default=0,
- help="enter an offset error to compensate for USRP clock inaccuracy")
- parser.add_option("-m", "--monitor", type="int", default=None,
- help="monitor a specific talkgroup")
- parser.add_option("-v", "--volume", type="eng_float", default=3.0,
- help="set volume gain for audio output [default=%default]")
- parser.add_option("-s", "--squelch", type="eng_float", default=28,
- help="set audio squelch level (default=%default, play with it)")
- parser.add_option("-D", "--directory", type="string", default="./log",
- help="choose a directory in which to save log data [default=%default]")
- # parser.add_option("-a", "--addr", type="string", default="",
- # help="address options to pass to UHD")
- # parser.add_option("-s", "--subdev", type="string",
- # help="UHD subdev spec", default=None)
- # parser.add_option("-A", "--antenna", type="string", default=None,
- # help="select Rx Antenna where appropriate")
- # FOR RTL
- parser.add_option("-p", "--ppm", type="eng_float", default=0,
- help="set RTL PPM frequency adjustment [default=%default]")
- #receive_path.add_options(parser, expert_grp)
- (options, args) = parser.parse_args ()
- if len(args) != 0:
- parser.print_help(sys.stderr)
- sys.exit(1)
- if options.chanlistfile is not None:
- clreader=csv.DictReader(open(options.chanlistfile), quotechar='"')
- chanlist={"0": 0}
- for record in clreader:
- chanlist[record['channel']] = record['frequency']
- else:
- chanlist = None
- # build the graph
- queue = gr.msg_queue()
- tb = my_top_block(options, queue)
- runner = top_block_runner(tb)
- updaterate = 10 #main loop rate in Hz
- audiologgers = [] #this is the list of active audio sinks.
- rxfound = False #a flag to indicate whether or not an audio sink was found with the correct talkgroup ID; see below
- try:
- while 1:
- if not queue.empty_p():
- msg = queue.delete_head() # Blocking read
- sentence = msg.to_string()
- [newfreq, newaddr] = parsefreq(sentence, chanlist)
- monaddr = newaddr & 0xFFF0 #last 8 bits are status flags for a given talkgroup
- if newfreq is not None and int(newfreq*1e6) == int(options.freq):
- newfreq = None #don't log the audio from the trunk itself
- #we have a new frequency assignment. look through the list of audio logger objects and find if any of them have been allocated to it
- rxfound = False
- for rx in audiologgers:
- #print "Logger info: %i @ %f idle for %fs" % (rx.talkgroup, rx.getfreq(options.centerfreq), rx.timeout()) #TODO: debug
- #first look through the list to find out if there is a receiver assigned to this talkgroup
- if rx.talkgroup == monaddr: #here we've got one
- if newfreq != rx.getfreq(options.centerfreq) and newfreq is not None: #we're on a new channel, though
- rx.tuneoffset(newfreq, options.centerfreq)
- rx.unmute() #this should be unnecessary but it does update the timestamp
- rxfound = True
- #print "New transmission on TG %i, updating timestamp" % rx.talkgroup
- else:
- if rx.getfreq(options.centerfreq) == newfreq: #a different talkgroup, but a new assignment on that freq! time to mute.
- rx.mute()
- if rxfound is False and newfreq is not None: #no existing receiver for this talkgroup. time to create one.
- #lock the flowgraph
- tb.lock()
- audiologgers.append( logging_receiver(newaddr, options) ) #create it
- audiologgers[-1].tuneoffset(newfreq, options.centerfreq) #tune it
- tb.connect(tb.rtl, audiologgers[-1]) #connect to the flowgraph
- tb.unlock()
- audiologgers[-1].unmute() #unmute it
- if newfreq is not None:
- print "TG %i @ %f, %i active loggers" % (newaddr, newfreq, len(audiologgers))
- else:
- time.sleep(1.0/updaterate)
- for rx in audiologgers:
- if rx.timeout() >= 5.0: #if this receiver has been muted more than 3 seconds
- rx.close() #close the .wav file that the logger has been writing to
- tb.lock()
- tb.disconnect(rx)
- tb.unlock()
- audiologgers.remove(rx) #delete the audio logger object from the list
- except KeyboardInterrupt:
- #perform cleanup: time to get out of Dodge
- for rx in audiologgers: #you probably don't need to lock, disconnect, unlock, remove. but you might as well.
- rx.close()
- #tb.lock()
- #tb.disconnect(rx)
- #tb.unlock()
- audiologgers.remove(rx)
- tb.stop()
- runner = None
- if __name__ == '__main__':
- main()
- root@bt:~/gr-smartnet/src/python#
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement