git-svn-id: http://op25.osmocom.org/svn/trunk@327 65a5c917-d112-43f1-993d-58c26a4786be
This commit is contained in:
max 2013-09-28 00:06:50 +00:00
parent 54f1a19a17
commit ef927d63ec
2 changed files with 327 additions and 6 deletions

View File

@ -35,6 +35,11 @@ import math
import numpy
import time
import re
try:
import Hamlib
except:
pass
try:
import Numeric
except:
@ -49,6 +54,8 @@ from optparse import OptionParser
import gnuradio.wxgui.plot as plot
import trunking
speeds = [300, 600, 900, 1200, 1440, 1800, 1920, 2400, 2880, 3200, 3600, 3840, 4000, 4800, 6000, 6400, 7200, 8000, 9600, 14400, 19200]
WIRESHARK_PORT = 23456
@ -76,9 +83,11 @@ class p25_rx_block (stdgui2.std_top_block):
parser.add_option("-C", "--costas-alpha", type="eng_float", default=0.125, help="value of alpha for Costas loop", metavar="Hz")
parser.add_option("-f", "--frequency", type="eng_float", default=0.0, help="USRP center frequency", metavar="Hz")
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("-s", "--seek", type="int", default=0, help="ifile seek in K")
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", "--trunk-cc-freq", type="eng_float", default=None, help="trunk control channel frequency", metavar="Hz")
parser.add_option("-v", "--verbosity", type="int", default=10, help="message debug level")
parser.add_option("-V", "--vocoder", action="store_true", default=False, help="voice codec")
parser.add_option("-o", "--offset", type="eng_float", default=0.0, help="tuning offset frequency [to circumvent DC offset]", metavar="Hz")
@ -89,6 +98,8 @@ class p25_rx_block (stdgui2.std_top_block):
parser.add_option("-R", "--rx-subdev-spec", type="subdev", default=(0, 0), help="select USRP Rx side A or B (default=A)")
parser.add_option("-g", "--gain", type="eng_float", default=None, help="set USRP gain in dB (default is midpoint) or set audio gain")
parser.add_option("-G", "--gain-mu", type="eng_float", default=0.025, help="gardner gain")
parser.add_option("-N", "--gains", type="string", default=None, help="gain settings")
parser.add_option("-O", "--audio-output", type="string", default="plughw:0,0", help="audio output device name")
(options, args) = parser.parse_args()
if len(args) != 0:
parser.print_help()
@ -107,7 +118,16 @@ class p25_rx_block (stdgui2.std_top_block):
print "osmosdr source_c creation failure"
ignore = True
print 'gain names ', self.src.get_gain_names()
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)
if options.audio:
self.channel_rate = 48000
@ -140,6 +160,24 @@ class p25_rx_block (stdgui2.std_top_block):
self.current_speed = i
self.default_speed_idx = i
if options.hamlib_model:
self.hamlib_attach(options.hamlib_model)
class _trunked_states(object):
ACQ = 0
CC = 1
TO_VC = 2
VC = 3
TO_CC = 4
self.trunked_states = _trunked_states
self.trunked_state = self.trunked_states.ACQ
if options.trunk_cc_freq:
if options.hamlib_model:
self.hamlib.set_freq(int(options.trunk_cc_freq))
# acquire trunk CC based on list of primary and alternates
self.trunked_state = self.trunked_states.CC
# initialize the UI
#
self.__init_gui(frame, panel, vbox)
@ -200,7 +238,8 @@ class p25_rx_block (stdgui2.std_top_block):
self.buffer = gr.copy(gr.sizeof_float)
msgq = gr.msg_queue(2)
msgq = gr.msg_queue(100)
self.du_watcher = du_queue_watcher(msgq, self.process_data_unit)
udp_port = 0
if self.options.wireshark:
udp_port = WIRESHARK_PORT
@ -208,12 +247,14 @@ class p25_rx_block (stdgui2.std_top_block):
self.sink_sf = gr.file_sink(gr.sizeof_char, self.options.raw_symbols)
do_imbe = 1
do_output = 1
self.sink_s = repeater.p25_frame_assembler(self.options.wireshark_host, udp_port, self.options.verbosity, do_imbe, do_output, 0, msgq)
do_msgq = 1
self.sink_s = repeater.p25_frame_assembler(self.options.wireshark_host, udp_port, self.options.verbosity, do_imbe, do_output, do_msgq, msgq)
self.trunk_ctl = trunking.trunked_system(frequency_set = self.change_freq)
if self.options.vocoder:
self.sink_imbe = repeater.vocoder(0, 0, 0, '', 0, 0)
self.audio_s2f = gr.short_to_float()
self.audio_scaler = gr.multiply_const_ff(1 / 32768.0)
self.audio_output = audio.sink(8000, "pulse", True)
self.audio_output = audio.sink(8000, self.options.audio_output, True)
self.connect(self.sink_imbe, self.audio_s2f, self.audio_scaler, self.audio_output)
else:
self.sink_imbe = gr.null_sink(gr.sizeof_char)
@ -233,7 +274,7 @@ class p25_rx_block (stdgui2.std_top_block):
self.fac_state = False
self.fsk4_demod_connected = False
self.psk_demod_connected = False
self.fsk4_demod_mode = True
self.fsk4_demod_mode = False
self.corr_i_chan = False
if self.baseband_input:
@ -320,7 +361,7 @@ class p25_rx_block (stdgui2.std_top_block):
self.__connect([[source, (self.mixer, 0)],
[self.lo, (self.mixer, 1)]])
self.set_connection(fft=1)
self.connect_fsk4_demod()
self.connect_demods()
# Connect up the flow graph
#
@ -589,6 +630,49 @@ class p25_rx_block (stdgui2.std_top_block):
vbox.Add(hbox, 0, 0)
def change_freq(self, freq):
if self.trunked_state == self.trunked_states.CC:
print "%d: change frequency to: %f" % (self.trunked_state, float(freq) / 1000000.0)
self.trunked_state = self.trunked_states.TO_VC
if self.options.hamlib_model:
self.hamlib.set_freq(freq)
else:
self.set_freq(freq)
def hamlib_attach(self, model):
Hamlib.rig_set_debug (Hamlib.RIG_DEBUG_NONE) # RIG_DEBUG_TRACE
self.hamlib = Hamlib.Rig (model)
self.hamlib.set_conf ("serial_speed","9600")
self.hamlib.set_conf ("retry","5")
self.hamlib.open ()
def process_data_unit(self, msg):
type = msg.type()
#print "type %d at %f state %d" %(type, time.time(), self.trunked_state)
s = msg.to_string()
t = 0
for c in s:
t = (t << 8) + ord(c)
nac = t & 0xffff
t = t >> 16
if type == 7:
self.trunk_ctl.decode_tsbk(t)
if self.trunked_state == self.trunked_states.TO_CC:
self.trunked_state = self.trunked_states.CC
#print self.trunk_ctl.to_string()
else:
if self.trunked_state == self.trunked_states.TO_VC:
self.trunked_state = self.trunked_states.VC
if self.trunked_state == self.trunked_states.VC and (type == 3 or type == 15):
self.trunked_state = self.trunked_states.TO_CC
print "%d: change frequency to: %f" % (self.trunked_state, float(self.options.trunk_cc_freq) / 1000000.0)
if self.options.hamlib_model:
self.hamlib.set_freq(int(self.options.trunk_cc_freq))
else:
self.set_freq(int(self.options.trunk_cc_freq))
def set_gain(self, gain):
if self.baseband_input:
f = 1.0
@ -1242,6 +1326,23 @@ class wizard_details_page(wx.wizard.WizardPageSimple):
ToDo = True
# data unit receive queue
#
class du_queue_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()
self.callback(msg)
# Frequency tracker
#
class demod_watcher(threading.Thread):

View File

@ -0,0 +1,220 @@
def crc16(dat,len): # slow version
poly = (1<<12) + (1<<5) + (1<<0)
crc = 0
for i in range(len):
bits = (dat >> (((len-1)-i)*8)) & 0xff
for j in range(8):
bit = (bits >> (7-j)) & 1
crc = ((crc << 1) | bit) & 0x1ffff
if crc & 0x10000:
crc = (crc & 0xffff) ^ poly
crc = crc ^ 0xffff
return crc
class trunked_system (object):
def __init__(self, debug=0, frequency_set=None):
self.debug = debug
self.frequency_set = frequency_set
self.freq_table = {}
self.stats = {}
self.stats['tsbks'] = 0
self.stats['crc'] = 0
self.tsbk_cache = {}
self.secondary = {}
self.adjacent = {}
self.rfss_syid = 0
self.rfss_rfid = 0
self.rfss_stid = 0
self.rfss_chan = 0
self.rfss_txchan = 0
self.ns_syid = 0
self.ns_wacn = 0
self.ns_chan = 0
def to_string(self):
s = []
for table in self.freq_table:
a = self.freq_table[table]['frequency'] / 1000000.0
b = self.freq_table[table]['step'] / 1000000.0
c = self.freq_table[table]['offset'] / 1000000.0
s.append('tbl-id: %x frequency: %f step %f offset %f' % ( table, a,b,c))
#self.freq_table[table]['frequency'] / 1000000.0, self.freq_table[table]['step'] / 1000000.0, self.freq_table[table]['offset']) / 1000000.0)
s.append('stats: tsbks %d crc %d' % (self.stats['tsbks'], self.stats['crc']))
s.append('secondary control channel(s): %s' % ','.join(['%f' % (float(k) / 1000000.0) for k in self.secondary.keys()]))
for f in self.adjacent:
s.append('adjacent %f: %s' % (float(f) / 1000000.0, self.adjacent[f]))
s.append('rf: sysid %x rfid %x stid %x frequency %f uplink %f' % ( self.rfss_syid, self.rfss_rfid, self.rfss_stid, float(self.rfss_chan) / 1000000.0, float(self.rfss_txchan) / 1000000.0))
s.append('net: sysid %x wacn %x frequency %f' % ( self.ns_syid, self.ns_wacn, float(self.ns_chan) / 1000000.0))
return '\n'.join(s)
# return frequency in Hz
def channel_id_to_frequency(self, id):
table = (id >> 12) & 0xf
channel = id & 0xfff
if table not in self.freq_table:
return None
return self.freq_table[table]['frequency'] + self.freq_table[table]['step'] * channel
def channel_id_to_string(self, id):
table = (id >> 12) & 0xf
channel = id & 0xfff
if table not in self.freq_table:
return "%x-%d" % (table, channel)
return "%f" % (self.freq_table[table]['frequency'] + self.freq_table[table]['step'] * channel) / 1000000.0
def decode_mbt_data(self, opcode, header, mbt_data):
# print "decode_mbt_data: %x %x" %(opcode, mbt_data)
if opcode == 0x0: # grp voice channel grant
ch1 = (mbt_data >> 32) & 0xffff
ch2 = (mbt_data >> 16) & 0xffff
ga = (mbt_data ) & 0xffff
if self.debug > 10:
print "mbt00 voice grant ch1 %x ch2 %x addr 0x%x" %(ch1, ch2, ga)
elif opcode == 0x3c: # adjacent status
rfid = (header >> 24) & 0xff
stid = (header >> 16) & 0xff
ch1 = (mbt_data >> 48) & 0xffff
ch2 = (mbt_data >> 32) & 0xffff
if self.debug > 10:
print "mbt3c adjacent rfid %x stid %x ch1 %x ch2 %x" %(rfid, stid, ch1, ch2)
elif opcode == 0x3b: # network status
wacn = (mbt_data >> 44) & 0xfffff
ch1 = (mbt_data >> 24) & 0xffff
ch2 = (mbt_data >> 8) & 0xffff
if self.debug > 10:
print "mbt3b net stat wacn %x ch1 %x ch2 %x" %(wacn, ch1, ch2)
elif opcode == 0x3a: # rfss status
rfid = (mbt_data >> 56) & 0xff
stid = (mbt_data >> 48) & 0xff
ch1 = (mbt_data >> 32) & 0xffff
ch2 = (mbt_data >> 16) & 0xffff
if self.debug > 10:
print "mbt3a rfss stat rfid %x stid %x ch1 %x ch2 %x" %(rfid, stid, ch1, ch2)
#else:
# print "mbt other %x" % opcode
def decode_tsbk(self, tsbk):
self.stats['tsbks'] += 1
# if crc16(tsbk, 12) != 0:
# self.stats['crc'] += 1
# return # crc check failed
tsbk = tsbk << 16 # for missing crc
opcode = (tsbk >> 88) & 0x3f
if self.debug > 10:
print "TSBK: 0x%02x 0x%024x" % (opcode, tsbk)
if opcode == 0x02: # group voice chan grant update
ch1 = (tsbk >> 64) & 0xffff
ga1 = (tsbk >> 48) & 0xffff
ch2 = (tsbk >> 32) & 0xffff
ga2 = (tsbk >> 16) & 0xffff
if self.frequency_set:
frequency = self.channel_id_to_frequency(ch1)
if frequency:
self.frequency_set(frequency)
if self.debug > 10:
print "tsbk02 grant update: chan %s %d %s %d" %(self.channel_id_to_string(ch1), ga1, self.channel_id_to_string(ch2), ga2)
elif opcode == 0x16: # sndcp data ch
ch1 = (tsbk >> 48) & 0xffff
ch2 = (tsbk >> 32) & 0xffff
if self.debug > 10:
print "tsbk16 sndcp data ch: chan %x %x" %(ch1, ch2)
elif opcode == 0x34: # iden_up vhf uhf
iden = (tsbk >> 76) & 0xf
bwvu = (tsbk >> 72) & 0xf
toff0 = (tsbk >> 58) & 0x3fff
spac = (tsbk >> 48) & 0x3ff
freq = (tsbk >> 16) & 0xffffffff
toff_sign = (toff0 >> 13) & 1
toff = toff0 & 0x1fff
if toff_sign == 0:
toff = 0 - toff
txt = ["mob Tx-", "mob Tx+"]
self.freq_table[iden] = {}
self.freq_table[iden]['offset'] = toff * spac * 125
self.freq_table[iden]['step'] = spac * 125
self.freq_table[iden]['frequency'] = freq * 5
if self.debug > 10:
print "tsbk34 iden vhf/uhf id %d toff %f spac %f freq %f [%s]" % (iden, toff * spac * 0.125 * 1e-3, spac * 0.125, freq * 0.000005, txt[toff_sign])
elif opcode == 0x3d: # iden_up
iden = (tsbk >> 76) & 0xf
bw = (tsbk >> 67) & 0x1ff
toff0 = (tsbk >> 58) & 0x1ff
spac = (tsbk >> 48) & 0x3ff
freq = (tsbk >> 16) & 0xffffffff
toff_sign = (toff0 >> 8) & 1
toff = toff0 & 0xff
if toff_sign == 0:
toff = 0 - toff
txt = ["mob xmit < recv", "mob xmit > recv"]
self.freq_table[iden] = {}
self.freq_table[iden]['offset'] = toff * 250000
self.freq_table[iden]['step'] = spac * 125
self.freq_table[iden]['frequency'] = freq * 5
if self.debug > 10:
print "tsbk3d iden id %d toff %f spac %f freq %f" % (iden, toff * 0.25, spac * 0.125, freq * 0.000005)
elif opcode == 0x3a: # rfss status
syid = (tsbk >> 56) & 0xfff
rfid = (tsbk >> 48) & 0xff
stid = (tsbk >> 40) & 0xff
chan = (tsbk >> 24) & 0xffff
f1 = self.channel_id_to_frequency(chan)
if f1:
self.rfss_syid = syid
self.rfss_rfid = rfid
self.rfss_stid = stid
self.rfss_chan = f1
self.rfss_txchan = f1 + self.freq_table[chan >> 12]['offset']
if self.debug > 10:
print "tsbk3a rfss status: syid: %x rfid %x stid %d ch1 %x(%s)" %(syid, rfid, stid, chan, self.channel_id_to_string(chan))
elif opcode == 0x39: # secondary cc
rfid = (tsbk >> 72) & 0xff
stid = (tsbk >> 64) & 0xff
ch1 = (tsbk >> 48) & 0xffff
ch2 = (tsbk >> 24) & 0xffff
f1 = self.channel_id_to_frequency(ch1)
f2 = self.channel_id_to_frequency(ch2)
if f1 and f2:
self.secondary[ f1 ] = 1
self.secondary[ f2 ] = 1
if self.debug > 10:
print "tsbk39 secondary cc: rfid %x stid %d ch1 %x(%s) ch2 %x(%s)" %(rfid, stid, ch1, self.channel_id_to_string(ch1), ch2, self.channel_id_to_string(ch2))
elif opcode == 0x3b: # network status
wacn = (tsbk >> 52) & 0xfffff
syid = (tsbk >> 40) & 0xfff
ch1 = (tsbk >> 24) & 0xffff
f1 = self.channel_id_to_frequency(ch1)
if f1:
self.ns_syid = syid
self.ns_wacn = wacn
self.ns_chan = f1
if self.debug > 10:
print "tsbk3b net stat: wacn %x syid %x ch1 %x(%s)" %(wacn, syid, ch1, self.channel_id_to_string(ch1))
elif opcode == 0x3c: # adjacent status
rfid = (tsbk >> 48) & 0xff
stid = (tsbk >> 40) & 0xff
ch1 = (tsbk >> 24) & 0xffff
f1 = self.channel_id_to_frequency(ch1)
if f1:
self.adjacent[f1] = 'rfid: %x stid:%x' % (rfid, stid)
if self.debug > 10:
print "tsbk3c adjacent: rfid %x stid %d ch1 %x(%s)" %(rfid, stid, ch1, self.channel_id_to_string(ch1))
#else:
# print "tsbk other %x" % opcode
def main():
q = 0x3a000012ae01013348704a54
rc = crc16(q,12)
print "should be zero: %x" % rc
q = 0x3a001012ae01013348704a54
rc = crc16(q,12)
print "should be nonzero: %x" % rc
t = trunked_system(debug=255)
q = 0x3a000012ae0101334870
t.decode_tsbk(q)
if __name__ == '__main__':
main()