p2tdma tg logging, fdma trunk cc hunting, blacklist/whitelist files, bug fixes

This commit is contained in:
Max 2014-12-19 19:16:15 -05:00
parent 2b3eda4b05
commit e319bdcd9f
5 changed files with 265 additions and 128 deletions

View File

@ -35,24 +35,25 @@ import op25_repeater
# default values (used in __init__ and add_options)
_def_debug = 0
_def_do_ambe = False
_def_num_ambe = False
_def_do_imbe = True
_def_wireshark_host = ''
_def_udp_port = 0
_def_dest = 'wav'
_def_audio_rate = 8000
_def_audio_output = 'plughw:0,0'
_def_max_tdma_timeslots = 2
# /////////////////////////////////////////////////////////////////////////////
# decoder
# /////////////////////////////////////////////////////////////////////////////
class p25_decoder_c(gr.hier_block2):
class p25_decoder_sink_b(gr.hier_block2):
def __init__(self,
dest = _def_dest,
do_imbe = _def_do_imbe,
do_ambe = _def_do_ambe,
num_ambe = _def_num_ambe,
wireshark_host = _def_wireshark_host,
udp_port = _def_udp_port,
do_msgq = False,
@ -70,6 +71,9 @@ class p25_decoder_c(gr.hier_block2):
gr.io_signature(1, 1, gr.sizeof_char), # Input signature
gr.io_signature(0, 0, 0)) # Output signature
assert 0 <= num_ambe <= _def_max_tdma_timeslots
assert not (num_ambe > 1 and dest != 'wav')
self.debug = debug
self.dest = dest
do_output = 1
@ -77,30 +81,52 @@ class p25_decoder_c(gr.hier_block2):
if msgq is None:
msgq = gr.msg_queue(1)
self.p25decoder = op25_repeater.p25_frame_assembler(wireshark_host, udp_port, debug, do_imbe, do_output, do_msgq, msgq, do_audio_output, do_ambe)
if dest == 'wav':
filename = 'default-%f.wav' % (time.time())
n_channels = 1
sample_rate = 8000
bits_per_sample = 16
self.audio_sink = blocks.wavfile_sink(filename, n_channels, sample_rate, bits_per_sample)
elif dest == 'audio':
self.audio_sink = audio.sink(_def_audio_rate, audio_output, True)
self.p25_decoders = []
self.audio_s2f = []
self.scaler = []
self.audio_sink = []
self.xorhash = []
num_decoders = 1
if num_ambe > 1:
num_decoders += num_ambe - 1
for slot in xrange(num_decoders):
self.p25_decoders.append(op25_repeater.p25_frame_assembler(wireshark_host, udp_port, debug, do_imbe, do_output, do_msgq, msgq, do_audio_output, True))
self.p25_decoders[slot].set_slotid(slot)
self.audio_s2f = blocks.short_to_float() # another ridiculous conversion
self.scaler = blocks.multiply_const_ff(1 / 32768.0)
self.audio_s2f.append(blocks.short_to_float()) # another ridiculous conversion
self.scaler.append(blocks.multiply_const_ff(1 / 32768.0))
self.xorhash.append('')
self.connect(self, self.p25decoder, self.audio_s2f, self.scaler, self.audio_sink)
if dest == 'wav':
filename = 'default-%f-%d.wav' % (time.time(), slot)
n_channels = 1
sample_rate = 8000
bits_per_sample = 16
self.audio_sink.append(blocks.wavfile_sink(filename, n_channels, sample_rate, bits_per_sample))
elif dest == 'audio':
self.audio_sink.append(audio.sink(_def_audio_rate, audio_output, True))
def close_file(self):
self.connect(self, self.p25_decoders[slot], self.audio_s2f[slot], self.scaler[slot], self.audio_sink[slot])
def close_file(self, index=0):
if self.dest != 'wav':
return
self.audio_sink.close()
self.audio_sink[index].close()
def set_output(self, slot, filename):
def set_slotid(self, slot, index=0):
self.p25_decoders[index].set_slotid(slot)
def set_output(self, filename, index=0):
if self.dest != 'wav':
return
self.audio_sink.open(filename)
print 'set_output', slot, filename
self.audio_sink[index].open(filename)
def set_xormask(self, xormask, xorhash, index=0):
if self.xorhash[index] == xorhash:
return
self.xorhash[index] = xorhash
self.p25_decoders[index].set_xormask(xormask)
def set_scaler_k(self, k, index=0):
self.scaler[index].set_k(k)

View File

@ -112,6 +112,7 @@ class p25_demod_fb(p25_demod_base):
p25_demod_base.__init__(self, if_rate=input_rate, symbol_rate=symbol_rate)
self.input_rate = input_rate
self.float_sink = None
self.connect(self, self.baseband_amp, self.symbol_filter, self.fsk4_demod, self.slicer, self)

View File

@ -137,40 +137,41 @@ class p25_rx_block (stdgui2.std_top_block):
self.channel_rate = 0
self.baseband_input = False
self.rtl_found = False
self.channel_rate = options.sample_rate
# check if osmocom is accessible
try:
self.src = None
import osmosdr
self.src = osmosdr.source(options.args)
self.channel_rate = options.sample_rate
except Exception:
print "osmosdr source_c creation failure"
ignore = True
self.src = None
if not options.input:
# check if osmocom is accessible
try:
import osmosdr
self.src = osmosdr.source(options.args)
except Exception:
print "osmosdr source_c creation failure"
ignore = True
if "rtl" in options.args.lower():
#print "'rtl' has been found in options.args (%s)" % (options.args)
self.rtl_found = True
if "rtl" in options.args.lower():
#print "'rtl' has been found in options.args (%s)" % (options.args)
self.rtl_found = True
gain_names = self.src.get_gain_names()
for name in gain_names:
range = self.src.get_gain_range(name)
print "gain: name: %s range: start %d stop %d step %d" % (name, range[0].start(), range[0].stop(), range[0].step())
if options.gains:
for tuple in options.gains.split(","):
name, gain = tuple.split(":")
gain = int(gain)
print "setting gain %s to %d" % (name, gain)
self.src.set_gain(gain, name)
gain_names = self.src.get_gain_names()
for name in gain_names:
range = self.src.get_gain_range(name)
print "gain: name: %s range: start %d stop %d step %d" % (name, range[0].start(), range[0].stop(), range[0].step())
if options.gains:
for tuple in options.gains.split(","):
name, gain = tuple.split(":")
gain = int(gain)
print "setting gain %s to %d" % (name, gain)
self.src.set_gain(gain, name)
rates = self.src.get_sample_rates()
try:
print 'supported sample rates %d-%d step %d' % (rates.start(), rates.stop(), rates.step())
except:
pass # ignore
rates = self.src.get_sample_rates()
try:
print 'supported sample rates %d-%d step %d' % (rates.start(), rates.stop(), rates.step())
except:
pass # ignore
if options.freq_corr:
self.src.set_freq_corr(options.freq_corr)
if options.freq_corr:
self.src.set_freq_corr(options.freq_corr)
if options.audio:
self.channel_rate = 48000
@ -248,9 +249,6 @@ class p25_rx_block (stdgui2.std_top_block):
udp_port = 0
if self.options.wireshark:
udp_port = WIRESHARK_PORT
if self.options.raw_symbols:
self.sink_sf = blocks.file_sink(gr.sizeof_char, self.options.raw_symbols)
self.connect(self.slicer, self.sink_sf)
self.tdma_state = False
self.xor_cache = {}
@ -288,18 +286,28 @@ class p25_rx_block (stdgui2.std_top_block):
if self.options.wireshark:
udp_port = WIRESHARK_PORT
self.decoder = p25_decoder.p25_decoder_c(dest='audio', do_imbe=True, do_ambe=self.options.phase2_tdma, wireshark_host=self.options.wireshark_host, udp_port=udp_port, do_msgq = True, msgq=self.rx_q, audio_output=self.options.audio_output, debug=self.options.verbosity)
num_ambe = 0
if self.options.phase2_tdma:
num_ambe = 1
self.decoder = p25_decoder.p25_decoder_sink_b(dest='audio', do_imbe=True, num_ambe=num_ambe, wireshark_host=self.options.wireshark_host, udp_port=udp_port, do_msgq = True, msgq=self.rx_q, audio_output=self.options.audio_output, debug=self.options.verbosity)
# connect it all up
self.connect(source, self.demod, self.decoder)
if self.options.raw_symbols:
self.sink_sf = blocks.file_sink(gr.sizeof_char, self.options.raw_symbols)
self.connect(self.demod, self.sink_sf)
logfile_workers = []
if self.options.phase2_tdma:
num_ambe = 2
if self.options.logfile_workers:
for i in xrange(self.options.logfile_workers):
demod = p25_demodulator.p25_demod_cb(input_rate=capture_rate,
demod_type='cqpsk', ### FIXME
offset=self.options.offset)
decoder = p25_decoder.p25_decoder_c(debug = self.options.verbosity, do_imbe = self.options.vocoder, do_ambe=self.options.phase2_tdma)
decoder = p25_decoder.p25_decoder_sink_b(debug = self.options.verbosity, do_imbe = self.options.vocoder, num_ambe=num_ambe)
logfile_workers.append({'demod': demod, 'decoder': decoder, 'active': False})
self.connect(source, demod, decoder)
@ -667,18 +675,15 @@ class p25_rx_block (stdgui2.std_top_block):
return # already in desired state
self.tdma_state = set_tdma
if set_tdma:
self.p25decoder.set_slotid(params['tdma'])
self.decoder.set_slotid(params['tdma'])
hash = '%x%x%x' % (params['nac'], params['sysid'], params['wacn'])
if hash not in self.xor_cache:
sreg = lfsr.p25p2_lfsr(params['nac'], params['sysid'], params['wacn'])
self.xor_cache[hash] = ''
for c in sreg.xorsyms:
self.xor_cache[hash] += chr(c)
self.p25decoder.set_xormask(self.xor_cache[hash])
self.xor_cache[hash] = lfsr.p25p2_lfsr(params['nac'], params['sysid'], params['wacn']).xor_chars
self.decoder.set_xormask(self.xor_cache[hash], hash)
sps = self.basic_rate / 6000
else:
sps = self.basic_rate / 4800
self.clock.set_omega(float(sps))
self.demod.clock.set_omega(float(sps))
def change_freq(self, params):
freq = params['freq']
@ -753,7 +758,7 @@ class p25_rx_block (stdgui2.std_top_block):
def set_audio_scaler(self, vol):
#print 'audio scaler: %f' % ((1 / 32768.0) * (vol * 0.1))
self.decoder.scaler.set_k((1 / 32768.0) * (vol * 0.1))
self.decoder.set_scaler_k((1 / 32768.0) * (vol * 0.1))
def set_rtl_ppm(self, ppm):
self.src.set_freq_corr(ppm)
@ -774,6 +779,8 @@ class p25_rx_block (stdgui2.std_top_block):
the result of that operation and our target_frequency to
determine the value for the digital down converter.
"""
if not self.src:
return False
tune_freq = target_freq + self.options.calibration + self.options.offset
r = self.src.set_center_freq(tune_freq)

View File

@ -28,7 +28,8 @@ class p25p2_lfsr(object):
self.xorsyms = [0] * (len(xorbits)/2)
for i in xrange(len(self.xorsyms)):
self.xorsyms[i] = (xorbits[i*2] << 1) + xorbits[i*2+1]
self.xor_chars = ''.join([chr(c) for c in self.xorsyms])
def asm_reg(self,s1,s2,s3,s4,s5,s6):
s1 = s1 & 0xfL
s2 = s2 & 0x1fL

View File

@ -21,7 +21,10 @@
# FIXME: hideously mixes indentation, some is tabs and some is spaces
#
import sys
import time
sys.path.append('tdma')
import lfsr
def crc16(dat,len): # slow version
poly = (1<<12) + (1<<5) + (1<<0)
@ -66,8 +69,16 @@ class trunked_system (object):
self.tgid_map = {}
self.offset = 0
self.sysname = 0
self.trunk_cc = 0
self.cc_list = []
self.cc_list_index = 0
self.CC_HUNT_TIME = 5.0
self.center_frequency = 0
self.last_tsbk = 0
self.cc_timeouts = 0
self.talkgroups = {}
if config:
self.blacklist = config['blacklist']
self.whitelist = config['whitelist']
@ -75,7 +86,9 @@ class trunked_system (object):
self.offset = config['offset']
self.sysname = config['sysname']
self.trunk_cc = config['cclist'][0] # TODO: scan thru list
self.cc_list = config['cclist']
self.center_frequency = config['center_frequency']
self.modulation = config['modulation']
def to_string(self):
s = []
@ -86,7 +99,8 @@ class trunked_system (object):
s.append('')
t = time.time()
for f in self.voice_frequencies:
s.append('voice frequency %f tgid %d %4.1fs ago count %d' % (f / 1000000.0, self.voice_frequencies[f]['tgid'], t - self.voice_frequencies[f]['time'], self.voice_frequencies[f]['counter']))
tgs = '%s %s' % (self.voice_frequencies[f]['tgid'][0], self.voice_frequencies[f]['tgid'][1])
s.append('voice frequency %f tgid(s) %s %4.1fs ago count %d' % (f / 1000000.0, tgs, t - self.voice_frequencies[f]['time'], self.voice_frequencies[f]['counter']))
s.append('')
for table in self.freq_table:
a = self.freq_table[table]['frequency'] / 1000000.0
@ -130,42 +144,47 @@ class trunked_system (object):
return "Talkgroup ID %d [0x%x]" % (tgid, tgid)
return self.tgid_map[tgid]
def update_talkgroup(self, frequency, tgid, tdma_slot):
if tgid not in self.talkgroups:
self.talkgroups[tgid] = {'counter':0}
self.talkgroups[tgid]['time'] = time.time()
self.talkgroups[tgid]['frequency'] = frequency
self.talkgroups[tgid]['tdma_slot'] = tdma_slot
def update_voice_frequency(self, frequency, tgid=None, tdma_slot=None):
if frequency is None:
if not frequency: # e.g., channel identifier not yet known
return
self.update_talkgroup(frequency, tgid, tdma_slot)
if frequency not in self.voice_frequencies:
self.voice_frequencies[frequency] = {'counter':0}
self.voice_frequencies[frequency]['tgid'] = tgid
if tdma_slot is None:
tdma_slot = 0
if 'tgid' not in self.voice_frequencies[frequency]:
self.voice_frequencies[frequency]['tgid'] = [None, None]
self.voice_frequencies[frequency]['tgid'][tdma_slot] = tgid
self.voice_frequencies[frequency]['counter'] += 1
self.voice_frequencies[frequency]['time'] = time.time()
self.voice_frequencies[frequency]['slot'] = tdma_slot
def get_updated_talkgroup_frequencies(self, start_time):
updated_talkgroup_frequencies = []
for frequency in self.voice_frequencies:
if self.voice_frequencies[frequency]['time'] < start_time:
def get_updated_talkgroups(self, start_time):
return [tgid for tgid in self.talkgroups if (
self.talkgroups[tgid]['time'] >= start_time and
tgid not in self.blacklist and
not (self.whitelist and tgid not in self.whitelist))]
def find_talkgroup(self, start_time, tgid=None):
if tgid is not None and tgid in self.talkgroups and self.talkgroups[tgid]['time'] >= start_time:
return self.talkgroups[tgid]['frequency'], tgid, self.talkgroups[tgid]['tdma_slot']
for active_tgid in self.talkgroups:
if self.talkgroups[active_tgid]['time'] < start_time:
continue
active_tgid = self.voice_frequencies[frequency]['tgid']
if active_tgid in self.blacklist:
continue
if self.whitelist and active_tgid not in self.whitelist:
continue
updated_talkgroup_frequencies.append(frequency)
return updated_talkgroup_frequencies
def find_voice_frequency(self, start_time, tgid=None):
for frequency in self.voice_frequencies:
if self.voice_frequencies[frequency]['time'] < start_time:
if self.talkgroups[active_tgid]['tdma_slot'] is not None and (self.ns_syid < 0 or self.ns_wacn < 0):
continue
active_tgid = self.voice_frequencies[frequency]['tgid']
if active_tgid in self.blacklist:
continue
if self.whitelist and active_tgid not in self.whitelist:
continue
if self.voice_frequencies[frequency]['slot'] is not None and (self.ns_syid < 0 or self.ns_wacn < 0):
continue
if tgid is None or tgid == active_tgid:
return frequency, active_tgid, self.voice_frequencies[frequency]['slot']
if tgid is None:
return self.talkgroups[active_tgid]['frequency'], active_tgid, self.talkgroups[active_tgid]['tdma_slot']
return None, None, None
def add_blacklist(self, tgid):
@ -174,6 +193,8 @@ class trunked_system (object):
self.blacklist[tgid] = 1
def decode_mbt_data(self, opcode, header, mbt_data):
self.cc_timeouts = 0
self.last_tsbk = time.time()
if self.debug > 10:
print "decode_mbt_data: %x %x" %(opcode, mbt_data)
if opcode == 0x0: # grp voice channel grant
@ -227,6 +248,7 @@ class trunked_system (object):
# print "mbt other %x" % opcode
def decode_tsbk(self, tsbk):
self.cc_timeouts = 0
self.stats['tsbks'] += 1
updated = 0
# if crc16(tsbk, 12) != 0:
@ -387,6 +409,20 @@ class trunked_system (object):
# print "tsbk other %x" % opcode
return updated
def hunt_cc(self, curr_time):
if self.cc_timeouts < 6:
return
self.cc_timeouts = 0
self.cc_list_index += 1
if self.cc_list_index >= len(self.cc_list):
self.cc_list_index = 0
self.trunk_cc = self.cc_list[self.cc_list_index]
print '%f set trunk_cc to %s' % (curr_time, self.trunk_cc)
def get_int_dict(s):
if s[0].isdigit():
return dict.fromkeys([int(d) for d in s.split(',')])
return dict.fromkeys([int(d) for d in open(s).readlines()])
class rx_ctl (object):
def __init__(self, debug=0, frequency_set=None, conf_file=None, logfile_workers=None):
@ -413,6 +449,11 @@ class rx_ctl (object):
self.P2_GRACE_TIME = 1.0 # TODO: make more configurable
self.logfile_workers = logfile_workers
self.active_talkgroups = {}
self.working_frequencies = {}
self.xor_cache = {}
self.last_garbage_collect = 0
if self.logfile_workers:
self.input_rate = self.logfile_workers[0]['demod'].input_rate
if conf_file:
if conf_file.endswith('.tsv'):
@ -424,6 +465,11 @@ class rx_ctl (object):
self.current_state = self.states.CC
tsys = self.trunked_systems[self.current_nac]
if self.logfile_workers and tsys.modulation == 'c4fm':
for worker in self.logfile_workers:
worker['demod'].connect_chain('fsk4')
self.set_frequency({
'freq': tsys.trunk_cc,
'tgid': None,
@ -502,10 +548,9 @@ class rx_ctl (object):
self.configs[nac]['modulation'] = configs[nac]['modulation']
else:
self.configs[nac]['modulation'] = 'cqpsk'
if 'whitelist' in configs[nac]:
self.configs[nac]['whitelist'] = dict.fromkeys([int(d) for d in configs[nac]['whitelist'].split(',')])
if 'blacklist' in configs[nac]:
self.configs[nac]['blacklist'] = dict.fromkeys([int(d) for d in configs[nac]['blacklist'].split(',')])
for k in ['whitelist', 'blacklist']:
if k in configs[nac]:
self.configs[nac][k] = get_int_dict(configs[nac][k])
if 'tgid_tags_file' in configs[nac]:
import csv
with open(configs[nac]['tgid_tags_file'], 'rb') as csvfile:
@ -545,6 +590,8 @@ class rx_ctl (object):
elif type == -1: # timeout
print "process_data_unit timeout"
self.update_state('timeout', curr_time)
if self.logfile_workers:
self.logging_scheduler(curr_time)
return
elif type < 0:
print 'unknown message type %d' % (type)
@ -587,7 +634,7 @@ class rx_ctl (object):
return
if self.logfile_workers:
self.update_logging_state(curr_time)
self.logging_scheduler(curr_time)
return
if updated:
@ -602,51 +649,102 @@ class rx_ctl (object):
return worker
return None
def update_logging_state(self, curr_time):
def free_frequency(self, frequency, curr_time):
assert not self.working_frequencies[frequency]['tgids']
self.working_frequencies[frequency]['worker']['demod'].set_relative_frequency(0)
self.working_frequencies[frequency]['worker']['active'] = False
self.working_frequencies.pop(frequency)
print '%f release worker frequency %d' % (curr_time, frequency)
def free_talkgroup(self, frequency, tgid, curr_time):
decoder = self.working_frequencies[frequency]['worker']['decoder']
tdma_slot = self.working_frequencies[frequency]['tgids'][tgid]['tdma_slot']
index = tdma_slot
if tdma_slot is None:
index = 0
filename = 'idle-channel-%d-%d-%f.wav' % (frequency, index, curr_time)
decoder.set_output(filename, index=index)
self.working_frequencies[frequency]['tgids'].pop(tgid)
print '%f release tgid %d frequency %d' % (curr_time, tgid, frequency)
def logging_scheduler(self, curr_time):
tsys = self.trunked_systems[self.current_nac]
freqs = tsys.get_updated_talkgroup_frequencies(curr_time)
for frequency in freqs:
tgid = tsys.voice_frequencies[frequency]['tgid']
relative_freq = tsys.center_frequency - frequency
if tgid in self.active_talkgroups:
self.active_talkgroups[tgid]['updated'] = curr_time
self.active_talkgroups[tgid]['worker']['demod'].set_relative_frequency(relative_freq)
for tgid in tsys.get_updated_talkgroups(curr_time):
frequency = tsys.talkgroups[tgid]['frequency']
tdma_slot = tsys.talkgroups[tgid]['tdma_slot']
# see if this tgid active on any other freq(s)
other_freqs = [f for f in self.working_frequencies if f != frequency and tgid in self.working_frequencies[f]['tgids']]
if other_freqs:
print '%f tgid %d slot %s frequency %d found on other frequencies %s' % (curr_time, tgid, tdma_slot, frequency, ','.join(['%s' % f for f in other_freqs]))
for f in other_freqs:
self.free_talkgroup(f, tgid, curr_time)
if not self.working_frequencies[f]['tgids']:
self.free_frequency(f, curr_time)
diff = abs(tsys.center_frequency - frequency)
if diff > self.input_rate/2:
#print '%f request for frequency %d tgid %d failed, offset %d exceeds maximum %d' % (curr_time, frequency, tgid, diff, self.input_rate/2)
continue
worker = self.find_available_worker()
if worker is None:
print '*** error, no free demodulators, freq %d tgid %d' % (frequency, tgid)
update = True
if frequency in self.working_frequencies:
tgids = self.working_frequencies[frequency]['tgids']
if tgid in tgids:
if tgids[tgid]['tdma_slot'] == tdma_slot:
update = False
else:
print '%f slot switch %s was %s tgid %d frequency %d' % (curr_time, tdma_slot, tgids[tgid]['tdma_slot'], tgid, frequency)
worker = self.working_frequencies[frequency]['worker']
else:
#active_tdma_slots = [tgids[tg]['tdma_slot'] for tg in tgids]
print '%f new tgid %d slot %s arriving on already active frequency %d' % (curr_time, tgid, tdma_slot, frequency)
worker = self.working_frequencies[frequency]['worker']
else:
worker = self.find_available_worker()
if worker is None:
print '*** error, no free demodulators, freq %d tgid %d' % (frequency, tgid)
continue
self.working_frequencies[frequency] = {'tgids' : {}, 'worker': worker}
worker['demod'].set_relative_frequency(tsys.center_frequency - frequency)
print '%f starting worker frequency %d tg %d slot %s' % (curr_time, frequency, tgid, tdma_slot)
self.working_frequencies[frequency]['tgids'][tgid] = {'updated': curr_time, 'tdma_slot': tdma_slot}
if not update:
continue
self.active_talkgroups[tgid] = {}
self.active_talkgroups[tgid]['updated'] = curr_time
self.active_talkgroups[tgid]['worker'] = worker
filename = 'tgid-%d-%f.wav' % (tgid, curr_time)
print '%f update frequency %d tg %d slot %s file %s' % (curr_time, frequency, tgid, tdma_slot, filename)
# set demod speed, decoder slot, output file name
demod = worker['demod']
decoder = worker['decoder']
demod.set_relative_frequency(relative_freq)
symbol_rate = 4800
if tsys.voice_frequencies[frequency]['slot'] is not None:
if tdma_slot is None:
index = 0
else:
index = tdma_slot
symbol_rate = 6000
demod.set_tdma_slotid(tsys.voice_frequencies[frequency]['slot'])
demod.set_tdma_parameters(self.current_nac, tsys.ns_syid, tsys.ns_wacn)
xorhash = '%x%x%x' % (self.current_nac, tsys.ns_syid, tsys.ns_wacn)
if xorhash not in self.xor_cache:
self.xor_cache[xorhash] = lfsr.p25p2_lfsr(self.current_nac, tsys.ns_syid, tsys.ns_wacn).xor_chars
decoder.set_xormask(self.xor_cache[xorhash], xorhash, index=index)
demod.set_omega(symbol_rate)
decoder.set_output(filename, index=index)
filename = 'tgid-%d-%f.wav' % (tgid, curr_time)
decoder.set_output(tsys.voice_frequencies[frequency]['slot'], filename)
# garbage collection
if self.last_garbage_collect + 1 > curr_time:
return
self.last_garbage_collect = curr_time
# look for inactive talkgroups
timeout_groups = []
for tgid in self.active_talkgroups:
if self.active_talkgroups[tgid]['updated'] + self.TGID_HOLD_TIME < curr_time:
timeout_groups.append(tgid)
for tgid in timeout_groups:
print '%f release %d' % (time.time(), tgid)
self.active_talkgroups[tgid]['worker']['decoder'].close_file()
self.active_talkgroups[tgid]['worker']['active'] = False
self.active_talkgroups.pop(tgid)
gc_frequencies = []
gc_tgids = []
for frequency in self.working_frequencies:
tgids = self.working_frequencies[frequency]['tgids']
inactive_tgids = [[frequency, tgid] for tgid in tgids if tgids[tgid]['updated'] + self.TGID_HOLD_TIME < curr_time]
if len(inactive_tgids) == len(tgids):
gc_frequencies += [frequency]
gc_tgids += inactive_tgids
for frequency, tgid in gc_tgids: # expire talkgroups
self.free_talkgroup(frequency, tgid, curr_time)
for frequency in gc_frequencies: # expire working frequencies
self.free_frequency(frequency, curr_time)
def update_state(self, command, curr_time):
if not self.configs:
@ -662,7 +760,9 @@ class rx_ctl (object):
new_slot = None
if command == 'timeout' or command == 'duid15':
if self.current_state != self.states.CC and curr_time - self.last_tdma_vf > self.P2_GRACE_TIME:
if self.current_state == self.states.CC:
tsys.cc_timeouts += 1
elif self.current_state != self.states.CC and curr_time - self.last_tdma_vf > self.P2_GRACE_TIME:
new_state = self.states.CC
new_frequency = tsys.trunk_cc
elif command == 'update':
@ -670,7 +770,7 @@ class rx_ctl (object):
desired_tgid = None
if self.tgid_hold_until > curr_time:
desired_tgid = self.tgid_hold
new_frequency, new_tgid, tdma_slot = tsys.find_voice_frequency(curr_time, tgid=desired_tgid)
new_frequency, new_tgid, tdma_slot = tsys.find_talkgroup(curr_time, tgid=desired_tgid)
if new_frequency:
new_state = self.states.TO_VC
self.current_tgid = new_tgid
@ -714,6 +814,8 @@ class rx_ctl (object):
print 'update_state: unknown command: %s\n' % command
assert 0 == 1
tsys.hunt_cc(curr_time)
if self.wait_until <= curr_time and self.tgid_hold_until <= curr_time and new_state is None:
self.wait_until = curr_time + self.TSYS_HOLD_TIME
new_nac = self.find_next_tsys()