parallel talkgroup logging support
This commit is contained in:
parent
d6807f5575
commit
dd5c8b6d7e
|
@ -58,6 +58,9 @@ import gnuradio.wxgui.plot as plot
|
||||||
|
|
||||||
import trunking
|
import trunking
|
||||||
|
|
||||||
|
import p25_demodulator
|
||||||
|
import p25_decoder
|
||||||
|
|
||||||
sys.path.append('tdma')
|
sys.path.append('tdma')
|
||||||
import lfsr
|
import lfsr
|
||||||
|
|
||||||
|
@ -107,6 +110,7 @@ class p25_rx_block (stdgui2.std_top_block):
|
||||||
parser.add_option("-F", "--ifile", type="string", default=None, help="read input from complex capture file")
|
parser.add_option("-F", "--ifile", type="string", default=None, help="read input from complex capture file")
|
||||||
parser.add_option("-H", "--hamlib-model", type="int", default=None, help="specify model for hamlib")
|
parser.add_option("-H", "--hamlib-model", type="int", default=None, help="specify model for hamlib")
|
||||||
parser.add_option("-s", "--seek", type="int", default=0, help="ifile seek in K")
|
parser.add_option("-s", "--seek", type="int", default=0, help="ifile seek in K")
|
||||||
|
parser.add_option("-L", "--logfile-workers", type="int", default=None, help="number of demodulators to instantiate")
|
||||||
parser.add_option("-S", "--sample-rate", type="int", default=320e3, help="source samp rate")
|
parser.add_option("-S", "--sample-rate", type="int", default=320e3, help="source samp rate")
|
||||||
parser.add_option("-t", "--tone-detect", action="store_true", default=False, help="use experimental tone detect algorithm")
|
parser.add_option("-t", "--tone-detect", action="store_true", default=False, help="use experimental tone detect algorithm")
|
||||||
parser.add_option("-T", "--trunk-conf-file", type="string", default=None, help="trunking config file name")
|
parser.add_option("-T", "--trunk-conf-file", type="string", default=None, help="trunking config file name")
|
||||||
|
@ -258,8 +262,6 @@ class p25_rx_block (stdgui2.std_top_block):
|
||||||
|
|
||||||
self.rx_q = gr.msg_queue(100)
|
self.rx_q = gr.msg_queue(100)
|
||||||
msg_EVT_DATA_EVENT(self.frame, self.msg_data)
|
msg_EVT_DATA_EVENT(self.frame, self.msg_data)
|
||||||
self.trunk_rx = trunking.rx_ctl(frequency_set = self.change_freq, debug = self.options.verbosity, conf_file = self.options.trunk_conf_file)
|
|
||||||
self.du_watcher = du_queue_watcher(self.rx_q, self.trunk_rx.process_qmsg)
|
|
||||||
udp_port = 0
|
udp_port = 0
|
||||||
if self.options.wireshark:
|
if self.options.wireshark:
|
||||||
udp_port = WIRESHARK_PORT
|
udp_port = WIRESHARK_PORT
|
||||||
|
@ -368,7 +370,7 @@ class p25_rx_block (stdgui2.std_top_block):
|
||||||
|
|
||||||
alpha = self.options.costas_alpha
|
alpha = self.options.costas_alpha
|
||||||
beta = 0.125 * alpha * alpha
|
beta = 0.125 * alpha * alpha
|
||||||
fmax = 1200 # Hz
|
fmax = 2400 # Hz
|
||||||
fmax = 2*pi * fmax / self.basic_rate
|
fmax = 2*pi * fmax / self.basic_rate
|
||||||
|
|
||||||
self.clock = op25_repeater.gardner_costas_cc(omega, gain_mu, gain_omega, alpha, beta, fmax, -fmax)
|
self.clock = op25_repeater.gardner_costas_cc(omega, gain_mu, gain_omega, alpha, beta, fmax, -fmax)
|
||||||
|
@ -391,6 +393,18 @@ class p25_rx_block (stdgui2.std_top_block):
|
||||||
self.set_connection(fft=1)
|
self.set_connection(fft=1)
|
||||||
self.connect_demods()
|
self.connect_demods()
|
||||||
|
|
||||||
|
logfile_workers = []
|
||||||
|
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', 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)
|
||||||
|
logfile_workers.append({'demod': demod, 'decoder': decoder, 'active': False})
|
||||||
|
self.connect(source, demod, decoder)
|
||||||
|
|
||||||
|
self.trunk_rx = trunking.rx_ctl(frequency_set = self.change_freq, debug = self.options.verbosity, conf_file = self.options.trunk_conf_file, logfile_workers=logfile_workers)
|
||||||
|
|
||||||
|
self.du_watcher = du_queue_watcher(self.rx_q, self.trunk_rx.process_qmsg)
|
||||||
|
|
||||||
# Connect up the flow graph
|
# Connect up the flow graph
|
||||||
#
|
#
|
||||||
def __connect(self, cnxns):
|
def __connect(self, cnxns):
|
||||||
|
@ -432,6 +446,10 @@ class p25_rx_block (stdgui2.std_top_block):
|
||||||
nac = params['nac']
|
nac = params['nac']
|
||||||
system = params['system']
|
system = params['system']
|
||||||
self.myform['system'].set_value('%X:%s' % (nac, system))
|
self.myform['system'].set_value('%X:%s' % (nac, system))
|
||||||
|
if 'tdma' in params and params['tdma'] is not None:
|
||||||
|
self.myform['tdma'].set_value('TDMA Slot %d' % (params['tdma']))
|
||||||
|
else:
|
||||||
|
self.myform['tdma'].set_value('')
|
||||||
|
|
||||||
def set_speed(self, new_speed):
|
def set_speed(self, new_speed):
|
||||||
# assumes that lock is held, or that we are in init
|
# assumes that lock is held, or that we are in init
|
||||||
|
@ -661,7 +679,14 @@ class p25_rx_block (stdgui2.std_top_block):
|
||||||
parent=self.panel, sizer=vbox_form, label="Talkgroup", weight=0)
|
parent=self.panel, sizer=vbox_form, label="Talkgroup", weight=0)
|
||||||
myform['talkgroup'].set_value("........................................")
|
myform['talkgroup'].set_value("........................................")
|
||||||
|
|
||||||
if not self.baseband_input:
|
if self.baseband_input:
|
||||||
|
min_gain = 0
|
||||||
|
max_gain = 200
|
||||||
|
initial_val = 50
|
||||||
|
else:
|
||||||
|
min_gain = 0
|
||||||
|
max_gain = 25
|
||||||
|
initial_val = 10
|
||||||
if self.options.trunk_conf_file:
|
if self.options.trunk_conf_file:
|
||||||
myform['freq'] = form.static_text_field(
|
myform['freq'] = form.static_text_field(
|
||||||
parent=self.panel, sizer=vbox_form, label="Frequency", weight=0)
|
parent=self.panel, sizer=vbox_form, label="Frequency", weight=0)
|
||||||
|
@ -671,14 +696,9 @@ class p25_rx_block (stdgui2.std_top_block):
|
||||||
parent=self.panel, sizer=vbox_form, label="Frequency", weight=0,
|
parent=self.panel, sizer=vbox_form, label="Frequency", weight=0,
|
||||||
callback=myform.check_input_and_call(_form_set_freq, self._set_status_msg))
|
callback=myform.check_input_and_call(_form_set_freq, self._set_status_msg))
|
||||||
myform['freq'].set_value(self.options.frequency)
|
myform['freq'].set_value(self.options.frequency)
|
||||||
if self.baseband_input:
|
myform['tdma'] = form.static_text_field(
|
||||||
min_gain = 0
|
parent=self.panel, sizer=vbox_form, label=None, weight=0)
|
||||||
max_gain = 200
|
myform['tdma'].set_value("")
|
||||||
initial_val = 50
|
|
||||||
else:
|
|
||||||
min_gain = 0
|
|
||||||
max_gain = 25
|
|
||||||
initial_val = 10
|
|
||||||
|
|
||||||
hbox.Add(vbox_form, 0, 0)
|
hbox.Add(vbox_form, 0, 0)
|
||||||
|
|
||||||
|
@ -749,7 +769,7 @@ class p25_rx_block (stdgui2.std_top_block):
|
||||||
self.hamlib.set_freq(freq)
|
self.hamlib.set_freq(freq)
|
||||||
elif params['center_frequency']:
|
elif params['center_frequency']:
|
||||||
relative_freq = center_freq - freq
|
relative_freq = center_freq - freq
|
||||||
if relative_freq + self.options.offset > self.channel_rate / 2:
|
if abs(relative_freq + self.options.offset) > self.channel_rate / 2:
|
||||||
print '***unable to tune Local Oscillator to offset %d Hz' % (relative_freq + self.options.offset)
|
print '***unable to tune Local Oscillator to offset %d Hz' % (relative_freq + self.options.offset)
|
||||||
print '***limit is one half of sample-rate %d = %d' % (self.channel_rate, self.channel_rate / 2)
|
print '***limit is one half of sample-rate %d = %d' % (self.channel_rate, self.channel_rate / 2)
|
||||||
print '***request for frequency %d rejected' % freq
|
print '***request for frequency %d rejected' % freq
|
||||||
|
@ -1126,10 +1146,10 @@ class p25_rx_block (stdgui2.std_top_block):
|
||||||
"source-dev": "AUDIO",
|
"source-dev": "AUDIO",
|
||||||
"source-decim": 1 }
|
"source-decim": 1 }
|
||||||
self.audio_source = audio.source(capture_rate, audio_input_filename)
|
self.audio_source = audio.source(capture_rate, audio_input_filename)
|
||||||
self.audio_cvt = gr.float_to_complex()
|
self.audio_cvt = blocks.float_to_complex()
|
||||||
self.connect((self.audio_source, 0), (self.audio_cvt, 0))
|
self.connect((self.audio_source, 0), (self.audio_cvt, 0))
|
||||||
self.connect((self.audio_source, 1), (self.audio_cvt, 1))
|
self.connect((self.audio_source, 1), (self.audio_cvt, 1))
|
||||||
self.source = gr.multiply_const_cc(gain)
|
self.source = blocks.multiply_const_cc(gain)
|
||||||
self.connect(self.audio_cvt, self.source)
|
self.connect(self.audio_cvt, self.source)
|
||||||
self.__set_rx_from_audio(capture_rate)
|
self.__set_rx_from_audio(capture_rate)
|
||||||
self._set_titlebar("Capturing")
|
self._set_titlebar("Capturing")
|
||||||
|
|
|
@ -140,6 +140,19 @@ class trunked_system (object):
|
||||||
self.voice_frequencies[frequency]['time'] = time.time()
|
self.voice_frequencies[frequency]['time'] = time.time()
|
||||||
self.voice_frequencies[frequency]['slot'] = tdma_slot
|
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:
|
||||||
|
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):
|
def find_voice_frequency(self, start_time, tgid=None):
|
||||||
for frequency in self.voice_frequencies:
|
for frequency in self.voice_frequencies:
|
||||||
if self.voice_frequencies[frequency]['time'] < start_time:
|
if self.voice_frequencies[frequency]['time'] < start_time:
|
||||||
|
@ -376,7 +389,7 @@ class trunked_system (object):
|
||||||
|
|
||||||
|
|
||||||
class rx_ctl (object):
|
class rx_ctl (object):
|
||||||
def __init__(self, debug=0, frequency_set=None, conf_file=None):
|
def __init__(self, debug=0, frequency_set=None, conf_file=None, logfile_workers=None):
|
||||||
class _states(object):
|
class _states(object):
|
||||||
ACQ = 0
|
ACQ = 0
|
||||||
CC = 1
|
CC = 1
|
||||||
|
@ -398,6 +411,8 @@ class rx_ctl (object):
|
||||||
self.configs = {}
|
self.configs = {}
|
||||||
self.last_tdma_vf = 0
|
self.last_tdma_vf = 0
|
||||||
self.P2_GRACE_TIME = 1.0 # TODO: make more configurable
|
self.P2_GRACE_TIME = 1.0 # TODO: make more configurable
|
||||||
|
self.logfile_workers = logfile_workers
|
||||||
|
self.active_talkgroups = {}
|
||||||
|
|
||||||
if conf_file:
|
if conf_file:
|
||||||
if conf_file.endswith('.tsv'):
|
if conf_file.endswith('.tsv'):
|
||||||
|
@ -408,6 +423,19 @@ class rx_ctl (object):
|
||||||
self.current_nac = self.nacs[0]
|
self.current_nac = self.nacs[0]
|
||||||
self.current_state = self.states.CC
|
self.current_state = self.states.CC
|
||||||
|
|
||||||
|
tsys = self.trunked_systems[self.current_nac]
|
||||||
|
self.set_frequency({
|
||||||
|
'freq': tsys.trunk_cc,
|
||||||
|
'tgid': None,
|
||||||
|
'offset': tsys.offset,
|
||||||
|
'tag': "",
|
||||||
|
'nac': self.current_nac,
|
||||||
|
'system': tsys.sysname,
|
||||||
|
'center_frequency': tsys.center_frequency,
|
||||||
|
'tdma': None,
|
||||||
|
'wacn': None,
|
||||||
|
'sysid': None})
|
||||||
|
|
||||||
def set_frequency(self, params):
|
def set_frequency(self, params):
|
||||||
frequency = params['freq']
|
frequency = params['freq']
|
||||||
if frequency and self.frequency_set:
|
if frequency and self.frequency_set:
|
||||||
|
@ -558,11 +586,68 @@ class rx_ctl (object):
|
||||||
if nac != self.current_nac:
|
if nac != self.current_nac:
|
||||||
return
|
return
|
||||||
|
|
||||||
|
if self.logfile_workers:
|
||||||
|
self.update_logging_state(curr_time)
|
||||||
|
return
|
||||||
|
|
||||||
if updated:
|
if updated:
|
||||||
self.update_state('update', curr_time)
|
self.update_state('update', curr_time)
|
||||||
else:
|
else:
|
||||||
self.update_state('duid%d' % type, curr_time)
|
self.update_state('duid%d' % type, curr_time)
|
||||||
|
|
||||||
|
def find_available_worker(self):
|
||||||
|
for worker in self.logfile_workers:
|
||||||
|
if not worker['active']:
|
||||||
|
worker['active'] = True
|
||||||
|
return worker
|
||||||
|
return None
|
||||||
|
|
||||||
|
def update_logging_state(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)
|
||||||
|
continue
|
||||||
|
|
||||||
|
worker = self.find_available_worker()
|
||||||
|
if worker is None:
|
||||||
|
print '*** error, no free demodulators, freq %d tgid %d' % (frequency, tgid)
|
||||||
|
continue
|
||||||
|
|
||||||
|
self.active_talkgroups[tgid] = {}
|
||||||
|
self.active_talkgroups[tgid]['updated'] = curr_time
|
||||||
|
self.active_talkgroups[tgid]['worker'] = worker
|
||||||
|
demod = worker['demod']
|
||||||
|
decoder = worker['decoder']
|
||||||
|
demod.set_relative_frequency(relative_freq)
|
||||||
|
symbol_rate = 4800
|
||||||
|
|
||||||
|
if tsys.voice_frequencies[frequency]['slot'] is not None:
|
||||||
|
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)
|
||||||
|
|
||||||
|
demod.set_omega(symbol_rate)
|
||||||
|
|
||||||
|
filename = 'tgid-%d-%f.wav' % (tgid, curr_time)
|
||||||
|
decoder.set_output(tsys.voice_frequencies[frequency]['slot'], filename)
|
||||||
|
|
||||||
|
# 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)
|
||||||
|
|
||||||
def update_state(self, command, curr_time):
|
def update_state(self, command, curr_time):
|
||||||
if not self.configs:
|
if not self.configs:
|
||||||
return # run in "manual mode" if no conf
|
return # run in "manual mode" if no conf
|
||||||
|
|
Loading…
Reference in New Issue