This week only. Pastebin PRO Accounts Christmas Special! Don't miss out!Want more features on Pastebin? Sign Up, it's FREE!
Guest

smartnet2logging_rtl2.py

By: a guest on Jun 10th, 2012  |  syntax: None  |  size: 10.31 KB  |  views: 109  |  expires: Never
download  |  raw  |  embed  |  report abuse  |  print
Text below is selected. Please press Ctrl+C to copy to your clipboard. (⌘+C on Mac)
  1. root@bt:~/gr-smartnet/src/python# cat smartnet2logging_rtl2.py
  2. #!/usr/bin/env python
  3. """
  4.         This program decodes the Motorola SmartNet II trunking protocol from the control channel
  5.         Tune it to the control channel center freq, and it'll spit out the decoded packets.
  6.         In what format? Who knows.
  7.  
  8.         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.
  9. """
  10.  
  11. from gnuradio import gr, gru, blks2, optfir, digital
  12. from grc_gnuradio import blks2 as grc_blks2
  13. from gnuradio import audio
  14. from gnuradio import eng_notation
  15. #from gnuradio import uhd
  16. from fsk_demod import fsk_demod
  17. from logging_receiver import logging_receiver
  18. from optparse import OptionParser
  19. from gnuradio.eng_option import eng_option
  20. from gnuradio import smartnet
  21. #from gnuradio.wxgui import slider
  22. #from gnuradio.wxgui import stdgui2, fftsink2, form
  23. import osmosdr
  24. from gnuradio import digital
  25.  
  26. #from pkt import *
  27. import time
  28. import gnuradio.gr.gr_threading as _threading
  29. import csv
  30. import os
  31.  
  32. class top_block_runner(_threading.Thread):
  33.     def __init__(self, tb):
  34.         _threading.Thread.__init__(self)
  35.         self.setDaemon(1)
  36.         self.tb = tb
  37.         self.done = False
  38.         self.start()
  39.  
  40.     def run(self):
  41.         self.tb.run()
  42.         self.done = True
  43.  
  44. class my_top_block(gr.top_block):
  45.         def __init__(self, options, queue):
  46.                 gr.top_block.__init__(self)
  47.  
  48.                 if options.centerfreq is 0:
  49.                         print "Center Frequency not defined, auto-calculating..."
  50.                         self.centerfreq = ((options.rate / 2) + (options.freq - options.rate)) + .100
  51.                 else:
  52.                         self.centerfreq = options.centerfreq   
  53.        
  54.                 print "Setting Center Frequency to: %fMHz" % self.centerfreq
  55.  
  56.                 self.rate = options.rate * 1000000
  57.                 print "Using Rate: %f (%iM)" % (options.rate, self.rate)
  58.  
  59.  
  60.                 self.rtl = osmosdr.source_c( args="nchan=" + str(1) + " " + ""  )
  61.                 self.rtl.set_sample_rate(self.rate)
  62.                 self.rtl.set_center_freq(options.centerfreq, 0)
  63.                 self.rtl.set_freq_corr(options.ppm, 0)
  64.                 self.rtl.set_gain_mode(1, 0)
  65.  
  66.                 print "Tuning to: %fMHz" % (self.centerfreq - options.error)
  67.                 if not(self.tune(options.centerfreq - options.error)):
  68.                         print "Failed to set initial frequency"
  69.  
  70.                 if options.gain is None: #set to halfway
  71. #                       g = self.u.get_gain_range()
  72. #                       options.gain = (g.start()+g.stop()) / 2.0
  73.                         options.gain = 10
  74.                         # TODO FIX^
  75.  
  76.                 print "Setting gain to %i" % options.gain
  77.                 self.rtl.set_gain(options.gain, 0)
  78.  
  79.                 print "Samples per second is %fM" % self.rate
  80.  
  81.                 self._syms_per_sec = 3600;
  82.  
  83.                 options.audiorate = 11025
  84.                 options.rate = self.rate
  85.  
  86.  
  87.                 options.samples_per_second = self.rate #yeah i know it's on the list
  88.                 options.syms_per_sec = self._syms_per_sec
  89.                 options.gain_mu = 0.01
  90.                 options.mu=0.5
  91.                 options.omega_relative_limit = 0.3
  92.                 options.syms_per_sec = self._syms_per_sec
  93.                 options.offset = options.freq - self.centerfreq
  94.                 print "Control channel offset: %f" % options.offset
  95.  
  96.                 self.demod = fsk_demod(options)
  97.                 self.start_correlator = digital.correlate_access_code_bb("10101100",0) #should mark start of packet
  98.                 self.smartnet_sync = smartnet.sync()
  99.                 self.smartnet_deinterleave = smartnet.deinterleave()
  100.                 self.smartnet_parity = smartnet.parity()
  101.                 self.smartnet_crc = smartnet.crc()
  102.                 self.smartnet_packetize = smartnet.packetize()
  103.                 self.parse = smartnet.parse(queue) #packet-based. this simply posts lightly-formatted messages to the queue.
  104.  
  105.                 self.connect(self.rtl, self.demod)
  106.  
  107.                 self.connect(self.demod, self.start_correlator, self.smartnet_sync, self.smartnet_deinterleave, self.smartnet_parity, self.smartnet_crc, self.smartnet_packetize, self.parse)
  108.  
  109.                
  110.         def tune(self, freq):
  111.                 result = self.rtl.set_center_freq(freq)
  112.                 return True
  113.  
  114. def getfreq(chanlist, cmd):
  115.         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.
  116. #               if cmd < 0x2d0:        
  117. # Changed for rebanding
  118.                 if cmd < 0x1b8:
  119.                         freq = float(cmd * 0.025 + 851.0125)
  120.                 elif cmd < 0x230:
  121.                         freq = float(cmd * 0.025 + 851.0125 - 10.9875)
  122.                 else:
  123.                         freq = None
  124.         else: #program your channel listings, get the right freqs.
  125.                 if chanlist.get(str(cmd), None) is not None:
  126.                         freq = float(chanlist[str(cmd)])
  127.                 else:
  128.                         freq = None
  129.  
  130.         return freq
  131.  
  132. def parsefreq(s, chanlist):
  133.         retfreq = None
  134.         [address, groupflag, command] = s.split(",")
  135.         command = int(command)
  136.         address = int(address) & 0xFFF0
  137.         groupflag = bool(groupflag)
  138.  
  139.         if chanlist is None:
  140.                 if command < 0x2d0:
  141.                         retfreq = getfreq(chanlist, command)
  142.  
  143.         else:
  144.                 if chanlist.get(str(command), None) is not None: #if it falls into the channel somewhere
  145.                         retfreq = getfreq(chanlist, command)
  146.         return [retfreq, address] # mask so the squelch opens up on the entire group
  147.  
  148.  
  149.  
  150. def main():
  151.         # Create Options Parser:
  152.         parser = OptionParser (option_class=eng_option, conflict_handler="resolve")
  153.         expert_grp = parser.add_option_group("Expert")
  154.  
  155.         parser.add_option("-f", "--freq", type="eng_float", default=866.9625,
  156.                                                 help="set control channel frequency to MHz [default=%default]", metavar="FREQ")
  157.         parser.add_option("-c", "--centerfreq", type="eng_float", default=0,
  158.                                                 help="set center receive frequency to MHz [default=%default]. Set to center of 800MHz band for best results")
  159.         parser.add_option("-g", "--gain", type="int", default=None,
  160.                                                 help="set RF gain", metavar="dB")
  161.         parser.add_option("-r", "--rate", type="eng_float", default=2.800000,
  162.                                                 help="set sample rate [default=%default]")
  163. #       parser.add_option("-b", "--bandwidth", type="eng_float", default=3e6,
  164. #                                               help="set bandwidth of DBS RX frond end [default=%default]")
  165.         parser.add_option("-C", "--chanlistfile", type="string", default=None,
  166.                                                 help="read in list of Motorola channel frequencies (improves accuracy of frequency decoding) [default=%default]")
  167.         parser.add_option("-E", "--error", type="eng_float", default=0,
  168.                                                 help="enter an offset error to compensate for USRP clock inaccuracy")
  169.         parser.add_option("-m", "--monitor", type="int", default=None,
  170.                                                 help="monitor a specific talkgroup")
  171.         parser.add_option("-v", "--volume", type="eng_float", default=3.0,
  172.                                                 help="set volume gain for audio output [default=%default]")
  173.         parser.add_option("-s", "--squelch", type="eng_float", default=28,
  174.                                                 help="set audio squelch level (default=%default, play with it)")
  175.         parser.add_option("-D", "--directory", type="string", default="./log",
  176.                                                 help="choose a directory in which to save log data [default=%default]")
  177. #       parser.add_option("-a", "--addr", type="string", default="",
  178. #                                               help="address options to pass to UHD")
  179. #       parser.add_option("-s", "--subdev", type="string",
  180. #                                               help="UHD subdev spec", default=None)
  181. #       parser.add_option("-A", "--antenna", type="string", default=None,
  182. #                                       help="select Rx Antenna where appropriate")
  183. # FOR RTL
  184.         parser.add_option("-p", "--ppm", type="eng_float", default=0,
  185.                                                 help="set RTL PPM frequency adjustment [default=%default]")
  186.  
  187.         #receive_path.add_options(parser, expert_grp)
  188.  
  189.         (options, args) = parser.parse_args ()
  190.  
  191.         if len(args) != 0:
  192.                 parser.print_help(sys.stderr)
  193.                 sys.exit(1)
  194.  
  195.  
  196.         if options.chanlistfile is not None:
  197.                 clreader=csv.DictReader(open(options.chanlistfile), quotechar='"')
  198.                 chanlist={"0": 0}
  199.                 for record in clreader:
  200.                         chanlist[record['channel']] = record['frequency']
  201.         else:
  202.                 chanlist = None
  203.  
  204.         # build the graph
  205.         queue = gr.msg_queue()
  206.         tb = my_top_block(options, queue)
  207.  
  208.         runner = top_block_runner(tb)
  209.  
  210.         updaterate = 10 #main loop rate in Hz
  211.         audiologgers = [] #this is the list of active audio sinks.
  212.         rxfound = False #a flag to indicate whether or not an audio sink was found with the correct talkgroup ID; see below
  213.  
  214.  
  215.         try:
  216.                 while 1:
  217.                         if not queue.empty_p():
  218.                                 msg = queue.delete_head() # Blocking read
  219.                                 sentence = msg.to_string()
  220.                                
  221.                                 [newfreq, newaddr] = parsefreq(sentence, chanlist)
  222.  
  223.                                 monaddr = newaddr & 0xFFF0 #last 8 bits are status flags for a given talkgroup
  224.  
  225.                                 if newfreq is not None and int(newfreq*1e6) == int(options.freq):
  226.                                         newfreq = None #don't log the audio from the trunk itself
  227.  
  228.                                 #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
  229.                                 rxfound = False
  230.  
  231.                                 for rx in audiologgers:
  232.  
  233.                                         #print "Logger info: %i @ %f idle for %fs" % (rx.talkgroup, rx.getfreq(options.centerfreq), rx.timeout()) #TODO: debug
  234.  
  235.                                         #first look through the list to find out if there is a receiver assigned to this talkgroup
  236.                                         if rx.talkgroup == monaddr: #here we've got one
  237.                                                 if newfreq != rx.getfreq(options.centerfreq) and newfreq is not None: #we're on a new channel, though
  238.                                                         rx.tuneoffset(newfreq, options.centerfreq)
  239.                                                
  240.                                                 rx.unmute() #this should be unnecessary but it does update the timestamp
  241.                                                 rxfound = True
  242.                                                 #print "New transmission on TG %i, updating timestamp" % rx.talkgroup
  243.  
  244.                                         else:
  245.                                                 if rx.getfreq(options.centerfreq) == newfreq: #a different talkgroup, but a new assignment on that freq! time to mute.
  246.                                                         rx.mute()
  247.  
  248.                                 if rxfound is False and newfreq is not None: #no existing receiver for this talkgroup. time to create one.
  249.                                         #lock the flowgraph
  250.                                         tb.lock()
  251.                                         audiologgers.append( logging_receiver(newaddr, options) ) #create it
  252.                                         audiologgers[-1].tuneoffset(newfreq, options.centerfreq) #tune it
  253.                                         tb.connect(tb.rtl, audiologgers[-1]) #connect to the flowgraph
  254.                                         tb.unlock()
  255.                                         audiologgers[-1].unmute() #unmute it
  256.  
  257.                                 if newfreq is not None:
  258.                                         print "TG %i @ %f, %i active loggers" % (newaddr, newfreq, len(audiologgers))
  259.  
  260.  
  261.                         else:
  262.                                 time.sleep(1.0/updaterate)
  263.  
  264.                         for rx in audiologgers:
  265.                                 if rx.timeout() >= 5.0: #if this receiver has been muted more than 3 seconds
  266.                                         rx.close() #close the .wav file that the logger has been writing to
  267.                                         tb.lock()
  268.                                         tb.disconnect(rx)
  269.                                         tb.unlock()
  270.                                         audiologgers.remove(rx) #delete the audio logger object from the list
  271.  
  272.         except KeyboardInterrupt:
  273.                 #perform cleanup: time to get out of Dodge
  274.                 for rx in audiologgers: #you probably don't need to lock, disconnect, unlock, remove. but you might as well.
  275.                         rx.close()
  276.                         #tb.lock()
  277.                         #tb.disconnect(rx)
  278.                         #tb.unlock()
  279.                         audiologgers.remove(rx)
  280.  
  281.                 tb.stop()
  282.  
  283.                 runner = None
  284.  
  285. if __name__ == '__main__':
  286.         main()
  287.  
  288.  
  289. root@bt:~/gr-smartnet/src/python#
clone this paste RAW Paste Data