From 89aa469cf395a24e960a0a9fac3decc7e78f1be2 Mon Sep 17 00:00:00 2001 From: Vadim Yanitskiy Date: Tue, 14 Nov 2017 00:15:20 +0700 Subject: [PATCH] python/trx: WIP: implement transmission chain --- python/trx/ctrl_if_bb.py | 18 ++-- python/trx/radio_if.py | 217 ++++++++++++++++++++++++++++++--------- 2 files changed, 174 insertions(+), 61 deletions(-) diff --git a/python/trx/ctrl_if_bb.py b/python/trx/ctrl_if_bb.py index 99c0451..26ae49a 100644 --- a/python/trx/ctrl_if_bb.py +++ b/python/trx/ctrl_if_bb.py @@ -49,11 +49,6 @@ class ctrl_if_bb(ctrl_if): print("[!] Transceiver already started") return -1 - # Ensure transceiver is ready to start - if not self.tb.check_available(): - print("[!] Transceiver isn't ready to start") - return -1 - print("[i] Starting transceiver...") self.tb.trx_started = True self.tb.start() @@ -97,14 +92,17 @@ class ctrl_if_bb(ctrl_if): # TODO: check freq range freq = int(request[1]) * 1000 - self.tb.set_fc(freq) + self.tb.set_rx_freq(freq) return 0 elif self.verify_cmd(request, "TXTUNE", 1): print("[i] Recv TXTUNE cmd") - # TODO: is not implemented yet + # TODO: check freq range + freq = int(request[1]) * 1000 + self.tb.set_tx_freq(freq) + return 0 # Timeslot management @@ -124,12 +122,12 @@ class ctrl_if_bb(ctrl_if): if config == 0: # Value 0 means 'drop all' - self.tb.gsm_ts_filter.set_policy( + self.tb.ts_filter.set_policy( grgsm.FILTER_POLICY_DROP_ALL) else: - self.tb.gsm_ts_filter.set_policy( + self.tb.ts_filter.set_policy( grgsm.FILTER_POLICY_DEFAULT) - self.tb.gsm_ts_filter.set_tn(tn) + self.tb.ts_filter.set_tn(tn) return 0 diff --git a/python/trx/radio_if.py b/python/trx/radio_if.py index d8c0444..689f074 100644 --- a/python/trx/radio_if.py +++ b/python/trx/radio_if.py @@ -29,17 +29,66 @@ import osmosdr from math import pi +from gnuradio.filter import firdes +from gnuradio import digital from gnuradio import blocks +from gnuradio import uhd from gnuradio import gr + +# HACK: should be implemented in C++! +import numpy as np + +class burst_type_filter(gr.sync_block): + def __init__(self, burst_types=[]): + gr.sync_block.__init__( + self, + name='Burst type filter', + in_sig=[], + out_sig=[] + ) + self.burst_types = burst_types + self.message_port_register_in(pmt.intern("bursts_in")) + self.message_port_register_out(pmt.intern("bursts_out")) + self.set_msg_handler(pmt.intern("bursts_in"), self.filter) + + def filter(self, msg): + burst_with_header = pmt.to_python(pmt.cdr(msg)) + burst = burst_with_header[-148:] + header = np.ndarray.tolist(burst_with_header[0:-148]) + burst_type = header[12] + if burst_type in self.burst_types: + self.message_port_pub(pmt.intern("bursts_out"), msg) + +class burst_to_fn_time(gr.basic_block): + def __init__(self): # only default arguments here + gr.basic_block.__init__( + self, + name='Burst to fn_time', + in_sig=[], + out_sig=[] + ) + self.message_port_register_in(pmt.intern("bursts_in")) + self.message_port_register_out(pmt.intern("fn_time_out")) + self.set_msg_handler(pmt.intern("bursts_in"), self.convert) + + def convert(self, msg): + fn_time = pmt.dict_ref(pmt.car(msg),pmt.intern("fn_time"),pmt.PMT_NIL) + fn_time_msg = pmt.dict_add(pmt.make_dict(), pmt.intern("fn_time"), fn_time) + if pmt.to_python(fn_time) is not None: + self.message_port_pub(pmt.intern("fn_time_out"), fn_time_msg) + class radio_if(gr.top_block): # PHY specific variables - shiftoff = 400e3 - fc = 941.6e6 # TODO: set ARFCN to 0? + rx_freq = 935e6 + tx_freq = 890e6 # Application state flags trx_started = False - fc_set = False + + # GSM timings + delay_correction = 285.616e-6 + ul_dl_shift = -(6.0/1625000*(156.25)*3) def __init__(self, phy_args, phy_sample_rate, phy_rx_gain, phy_tx_gain, phy_ppm, @@ -53,83 +102,149 @@ class radio_if(gr.top_block): self.tx_gain = phy_tx_gain gr.top_block.__init__(self, "GR-GSM TRX") - shift_fc = self.fc - self.shiftoff - ################################################## - # PHY Definition - ################################################## - self.phy = osmosdr.source(args = "numchan=%d %s" % (1, phy_args)) + # TRX Burst Interface + self.trx_burst_if = grgsm.trx_burst_if( + trx_remote_addr, str(trx_base_port)) - self.phy.set_bandwidth(250e3 + abs(self.shiftoff), 0) - self.phy.set_center_freq(shift_fc, 0) - self.phy.set_sample_rate(phy_sample_rate) - self.phy.set_freq_corr(phy_ppm, 0) - self.phy.set_iq_balance_mode(2, 0) - self.phy.set_dc_offset_mode(2, 0) - self.phy.set_gain_mode(False, 0) - self.phy.set_gain(self.rx_gain, 0) - self.phy.set_if_gain(20, 0) - self.phy.set_bb_gain(20, 0) - self.phy.set_antenna(phy_rx_antenna, 0) - ################################################## - # GR-GSM Magic - ################################################## - self.blocks_rotator = blocks.rotator_cc( - -2 * pi * self.shiftoff / phy_sample_rate) + # RX path definition + self.phy_src = uhd.usrp_source(phy_args, + uhd.stream_args(cpu_format="fc32", + channels=range(1))) + + self.phy_src.set_center_freq(self.rx_freq, 0) + self.phy_src.set_antenna(phy_rx_antenna, 0) + self.phy_src.set_samp_rate(phy_sample_rate) + self.phy_src.set_bandwidth(650e3, 0) + self.phy_src.set_gain(phy_rx_gain) self.gsm_input = grgsm.gsm_input( - ppm = phy_ppm, osr = 4, fc = self.fc, - samp_rate_in = phy_sample_rate) + ppm = phy_ppm - int(phy_ppm), osr = 4, + fc = self.rx_freq, samp_rate_in = phy_sample_rate) self.gsm_receiver = grgsm.receiver(4, ([0]), ([])) self.gsm_clck_ctrl = grgsm.clock_offset_control( - shift_fc, phy_sample_rate, osr = 4) + self.rx_freq, phy_sample_rate, osr = 4) - self.gsm_ts_filter = grgsm.burst_timeslot_filter(0) - self.gsm_ts_filter.set_policy(grgsm.FILTER_POLICY_DROP_ALL) + self.ts_filter = grgsm.burst_timeslot_filter(0) + self.ts_filter.set_policy(grgsm.FILTER_POLICY_DROP_ALL) - self.gsm_trx_burst_if = grgsm.trx_burst_if( - trx_remote_addr, str(trx_base_port)) - - ################################################## # Connections - ################################################## - self.connect((self.phy, 0), (self.blocks_rotator, 0)) - self.connect((self.blocks_rotator, 0), (self.gsm_input, 0)) - self.connect((self.gsm_input, 0), (self.gsm_receiver, 0)) + self.connect( + (self.phy_src, 0), + (self.gsm_input, 0)) - self.msg_connect((self.gsm_receiver, 'measurements'), + self.connect( + (self.gsm_input, 0), + (self.gsm_receiver, 0)) + + self.msg_connect( + (self.gsm_receiver, 'C0'), + (self.ts_filter, 'in')) + + self.msg_connect( + (self.ts_filter, 'out'), + (self.trx_burst_if, 'bursts')) + + self.msg_connect( + (self.gsm_receiver, 'measurements'), (self.gsm_clck_ctrl, 'measurements')) - self.msg_connect((self.gsm_clck_ctrl, 'ctrl'), + self.msg_connect( + (self.gsm_clck_ctrl, 'ctrl'), (self.gsm_input, 'ctrl_in')) - self.msg_connect((self.gsm_receiver, 'C0'), - (self.gsm_ts_filter, 'in')) - self.msg_connect((self.gsm_ts_filter, 'out'), - (self.gsm_trx_burst_if, 'bursts')) + # TX Path Definition + self.phy_sink = uhd.usrp_sink(phy_args, + uhd.stream_args(cpu_format="fc32", + channels=range(1)), "packet_len") - def check_available(self): - return self.fc_set + self.phy_sink.set_antenna(phy_tx_antenna, 0) + self.phy_sink.set_samp_rate(phy_sample_rate) + self.phy_sink.set_center_freq(self.tx_freq, 0) + self.phy_sink.set_gain(self.tx_gain) + + self.tx_time_setter = grgsm.txtime_setter( + 0xffffffff, 0, 0, 0, 0, 0, + self.delay_correction + self.ul_dl_shift) + + self.tx_burst_proc = grgsm.preprocess_tx_burst() + + self.pdu_to_tagged_stream = blocks.pdu_to_tagged_stream( + blocks.byte_t, 'packet_len') + + self.gmsk_mod = grgsm.gsm_gmsk_mod( + BT = 4, pulse_duration = 4, sps = 4) + + self.burst_shaper = digital.burst_shaper_cc( + (firdes.window(firdes.WIN_HANN, 16, 0)), + 0, 20, False, "packet_len") + + # Connections + self.msg_connect( + (self.trx_burst_if, 'bursts'), + (self.tx_time_setter, 'bursts_in')) + + self.msg_connect( + (self.tx_time_setter, 'bursts_out'), + (self.tx_burst_proc, 'bursts_in')) + + self.msg_connect( + (self.tx_burst_proc, 'bursts_out'), + (self.pdu_to_tagged_stream, 'pdus')) + + self.connect( + (self.pdu_to_tagged_stream, 0), + (self.gmsk_mod, 0)) + + self.connect( + (self.gmsk_mod, 0), + (self.burst_shaper, 0)) + + self.connect( + (self.burst_shaper, 0), + (self.phy_sink, 0)) + + + # RX & TX synchronization + self.bt_filter = burst_type_filter([3]) + self.burst_to_fn_time = burst_to_fn_time() + + # Connections + self.msg_connect( + (self.gsm_receiver, 'C0'), + (self.bt_filter, 'bursts_in')) + + self.msg_connect( + (self.bt_filter, 'bursts_out'), + (self.burst_to_fn_time, 'bursts_in')) + + self.msg_connect( + (self.burst_to_fn_time, 'fn_time_out'), + (self.tx_time_setter, 'fn_time')) def shutdown(self): print("[i] Shutdown Radio interface") self.stop() self.wait() - def set_fc(self, fc): - self.phy.set_center_freq(fc - self.shiftoff, 0) + def set_rx_freq(self, fc): + self.phy_src.set_center_freq(fc, 0) + self.gsm_clck_ctrl.set_fc(fc) self.gsm_input.set_fc(fc) - self.fc_set = True - self.fc = fc + self.rx_freq = fc + + def set_tx_freq(self, fc): + self.phy_sink.set_center_freq(fc, 0) + self.tx_freq = fc def set_rx_gain(self, gain): - self.phy.set_gain(gain, 0) + self.phy_src.set_gain(gain, 0) self.rx_gain = gain def set_tx_gain(self, gain): - # TODO: TX chain not implemented yet + self.phy_sink.set_gain(gain, 0) self.tx_gain = gain