#!/usr/bin/env python import os import sys import math import fcd import time from gnuradio import gr, gru, audio, eng_notation, blks2, optfir, op25, repeater from gnuradio.eng_option import eng_option from optparse import OptionParser from gnuradio import smartnet import threading import struct import pickle TUNE_OFFSET = 15000 # Decoder watcher # class decode_watcher(threading.Thread): def __init__(self, msgq, **kwds): threading.Thread.__init__ (self, **kwds) self.setDaemon(1) self.msgq = msgq self.keep_running = True self.start() def run(self): while(self.keep_running): msg = self.msgq.delete_head() pickled_dict = msg.to_string() attrs = pickle.loads(pickled_dict) print attrs #self.traffic_pane.update(attrs) # Frequency tracker # class demod_watcher(threading.Thread): def __init__(self, msgq, callback, **kwds): threading.Thread.__init__ (self, **kwds) self.setDaemon(1) self.msgq = msgq self.callback = callback self.keep_running = True self.start() def run(self): while(self.keep_running): msg = self.msgq.delete_head() frequency_correction = msg.arg1() #print "Correcting "+str(frequency_correction) self.callback(frequency_correction) class voice_block(gr.top_block): def __init__(self, options=None, thismain = False, fcd_dev = None): self.filestoclose = [] gr.top_block.__init__(self) parser = OptionParser(option_class=eng_option) parser.add_option("-S", "--stretch", type="int", default=0, help="flex amt") parser.add_option("-y", "--symbol-rate", type="int", default=4800, help="input symbol rate") #parser.add_option("-v", "--verbose", action="store_true", default=False, help="dump demodulation data") parser.add_option("-v", "--verbose", action="store_true", default=True, help="dump demodulation data") parser.add_option("-r", "--sample-rate", type="int", default=96000) parser.add_option("-z", "--sample-size", type="int", default=8, help="sample size of input (2 for short or 4 for float, 8 for complex)") parser.add_option("-I", "--input-file", type="string", default="") parser.add_option("-i", "--interp", action="store_true", default=False) parser.add_option("-o", "--output-file", type="string", default="") parser.add_option("-s", "--save-file", type="string", default="") parser.add_option("-m", "--multiply", type="float", default=1.0) parser.add_option("-f", "--freq", type="int", default=867712500) if options is not None: (options, args) = parser.parse_args(options) else: (options, args) = parser.parse_args() sample_rate = options.sample_rate symbol_rate = options.symbol_rate self.quiet = True self.endpoint = None self.rawpoint = None self.outfile = None self.current_shortname = None self.current_userid = None self.current_freq = options.freq self.recordmetadata = None if options.input_file: IN = gr.file_source(options.sample_size, options.input_file) print "Input from file "+options.input_file else: if fcd_dev: IN = fcd_dev else: IN = fcd.source_c("hw:1") IN.set_freq_corr(-12) IN.set_dc_corr(0.0013,0.0007) #GAIN=30 #IN.set_lna_gain(GAIN) #print "Setting lna gain to "+str(GAIN) self.fcd = IN #self.rawpoint = IN self.adjust_freq() if options.save_file: SAVE = gr.file_sink(options.sample_size, options.save_file) self.connect(IN, SAVE) symbol_decim = 1 self.symbol_deviation = 600.0 trans_width = 12.5e3 / 2; trans_centre = trans_width + (trans_width / 2) samples_per_symbol = sample_rate // symbol_rate symbol_coeffs = (1.0/samples_per_symbol,)*samples_per_symbol SYMBOL_FILTER = gr.fir_filter_fff (symbol_decim, symbol_coeffs) AMP = gr.multiply_const_ff( 0.5 ) msgq = gr.msg_queue(2) FSK4 = op25.fsk4_demod_ff(msgq, sample_rate, symbol_rate) levels = levels = [-2.0, 0.0, 2.0, 4.0] #self.rawpoint = AMP SLICER = repeater.fsk4_slicer_fb(levels) framer_msgq = gr.msg_queue(2) self.DECODE = repeater.p25_frame_assembler('127.0.0.1', # udp hostname 23456, # udp port no. 0, #debug True, # do_imbe True, # do_output True, # do_msgq framer_msgq) self.rawpoint = self.DECODE IMBE = repeater.vocoder(False, # 0=Decode,True=Encode True, # Verbose flag options.stretch, # flex amount "", # udp ip address 0, # udp port True) # dump raw u vectors self.demod_watcher = demod_watcher(msgq, self.adjust_channel_offset) if options.sample_size==2: stof = gr.short_to_float() self.connect(IN, stof) nextconnect = stof elif options.sample_size==8: # Data has not been demodulated yet # Define frequency offset to translate #offset = 12500 self.offset = -(12500 + TUNE_OFFSET) xlate_filter_taps = gr.firdes.low_pass(1, sample_rate, trans_centre, trans_width, gr.firdes.WIN_HANN) self.XLAT_FILTER = gr.freq_xlating_fir_filter_ccf(1, xlate_filter_taps, self.offset, sample_rate) fm_demod_gain = sample_rate / (2.0 * math.pi * self.symbol_deviation) NBFM = gr.quadrature_demod_cf(fm_demod_gain) # Let's setup this part of the chain self.connect(IN, self.XLAT_FILTER, NBFM) nextconnect = NBFM else: # straight up demodulated data nextconnect = IN stream_to_vector = gr.stream_to_vector(1, 32) self.voice_queue = gr.msg_queue() self.msg_sink = gr.message_sink( 32, self.voice_queue, True ) self.connect(nextconnect, SYMBOL_FILTER, AMP, FSK4, SLICER, self.DECODE, IMBE) self.connect(self.DECODE, stream_to_vector, self.msg_sink) connectlist = [ IMBE, gr.short_to_float(), gr.multiply_const_ff(1.0 / 32767.0) ] if options.interp: interp_taps = gr.firdes.low_pass(1.0, 48000, 4000, 4000 * 0.1, gr.firdes.WIN_HANN) INTERP = gr.interp_fir_filter_fff(48000 // 8000, interp_taps) connectlist.append(INTERP) self.wavrate = 48000 amplify = 20 else: self.wavrate = 8000 amplify = 5 AMP2 = gr.multiply_const_ff( amplify ); connectlist.append( AMP2 ) if options.output_file: print "outputting to file "+options.output_file wavsink = gr.wavfile_sink(options.output_file, 1, self.wavrate, 16) connectlist.append(wavsink) else: if thismain: connectlist.append(audio.sink(self.wavrate, "hw:0,0", True)) else: # We are being used by another script, wait to be tuned self.endpoint = connectlist[-1] self.nullsink = gr.null_sink(4) connectlist.append(self.nullsink) self.connect( *connectlist ) def adjust_channel_offset(self, delta_hz): max_delta_hz = 12000.0 delta_hz *= self.symbol_deviation delta_hz = max(delta_hz, -max_delta_hz) delta_hz = min(delta_hz, max_delta_hz) self.XLAT_FILTER.set_center_freq(self.offset - delta_hz) def adjust_freq(self, freq = None): if freq: self.current_freq = freq self.fcd.set_freq(int(self.current_freq*1000000) - TUNE_OFFSET) print "Changing freq to "+str(self.current_freq) def cleanup_files_to_close(self, secs): while len(self.filestoclose) > 0 and (secs - self.filestoclose[-1]['time']) >= 1.0: fobject = self.filestoclose.pop() fobject['blockhandle'].close() print "Really closing a file now" # outdat = "" # for line in fdata: # tline = line.strip().replace(' ','') # if len(tline) == 24: # outdat += struct.pack('III', int(tline[0:8], 16), int(tline[8:16], 16), int(tline[16:24], 16)) # Compress every 8 hex chars to 4 binary ones (24 to 12) # else: # print "FAIL",len(tline),tline # if not outdat: # print "EMPTY OUTDAT",fobject['rawfilename'] # # obj = { "data": outdat, "datasize": len(outdat) } # callback = fobject['recordmetadata']['done_callback'] # del fobject['recordmetadata']['done_callback'] # obj.update(fobject['recordmetadata']) # callback( obj ) def tune(self, freq, secs, shortname, userid, recordmetadata): if self.quiet and not freq: return if freq and freq != self.current_freq: self.adjust_freq(freq) # Okay, we should return if the user and channel has not changed, no use making a new file if shortname == self.current_shortname and userid == self.current_userid: return self.lock() if self.quiet: self.disconnect(self.endpoint, self.nullsink) else: self.disconnect(self.endpoint, self.outfile) print "Closing" self.filestoclose.insert(0, {"blockhandle": self.outfile, "time": secs} ) self.outfile = None if freq: print "Opening",recordmetadata['filename'] self.outfile = gr.wavfile_sink(recordmetadata['filename'], 1, self.wavrate, 16) self.connect(self.endpoint, self.outfile) self.quiet = False self.current_shortname = shortname self.current_userid = userid self.recordmetadata = recordmetadata else: self.connect(self.endpoint, self.nullsink) self.quiet = True self.current_shortname = None self.current_userid = None self.recordmetadata = None self.unlock() if __name__ == "__main__": try: voice_block(thismain = True).run() except KeyboardInterrupt: tb.stop()