From 4650fad7cb3acf103775a1f14beb131650b09ebf Mon Sep 17 00:00:00 2001 From: Vadim Yanitskiy Date: Fri, 1 Dec 2017 05:08:35 +0700 Subject: trx/radio_if.py: use native burst_type_filter block --- python/trx/radio_if.py | 25 +------------------------ 1 file changed, 1 insertion(+), 24 deletions(-) (limited to 'python') diff --git a/python/trx/radio_if.py b/python/trx/radio_if.py index 689f074..9c26ec6 100644 --- a/python/trx/radio_if.py +++ b/python/trx/radio_if.py @@ -37,29 +37,6 @@ 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__( @@ -210,7 +187,7 @@ class radio_if(gr.top_block): # RX & TX synchronization - self.bt_filter = burst_type_filter([3]) + self.bt_filter = grgsm.burst_type_filter([3]) self.burst_to_fn_time = burst_to_fn_time() # Connections -- cgit v1.2.3 From 63703bb1ff93d024a175983e57d1045dba997b3b Mon Sep 17 00:00:00 2001 From: Vadim Yanitskiy Date: Sun, 3 Dec 2017 23:40:21 +0700 Subject: trx/radio_if.py: explicilty set device clock rate The device timings are depend on the current clock rate, so let's explicilty set 26e6 as it's value, which is usable for USRP B2X0. --- python/trx/radio_if.py | 2 ++ 1 file changed, 2 insertions(+) (limited to 'python') diff --git a/python/trx/radio_if.py b/python/trx/radio_if.py index 9c26ec6..dbccae7 100644 --- a/python/trx/radio_if.py +++ b/python/trx/radio_if.py @@ -90,6 +90,7 @@ class radio_if(gr.top_block): uhd.stream_args(cpu_format="fc32", channels=range(1))) + self.phy_src.set_clock_rate(26e6, uhd.ALL_MBOARDS) 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) @@ -139,6 +140,7 @@ class radio_if(gr.top_block): uhd.stream_args(cpu_format="fc32", channels=range(1)), "packet_len") + self.phy_sink.set_clock_rate(26e6, uhd.ALL_MBOARDS) 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) -- cgit v1.2.3 From d222ee58bbc260ea70cc995c70d797768f00f895 Mon Sep 17 00:00:00 2001 From: Vadim Yanitskiy Date: Sun, 3 Dec 2017 23:49:09 +0700 Subject: trx/radio_if.py: implement AFC for both RX and TX paths AFC (Automatic Frequency Control) was previously only utilized in the receive path of the radio interface. Now we also need to keep the transmitter frequency as accurate as possible. --- python/trx/radio_if.py | 104 +++++++++++++++++++++++++++++++++++++++---------- 1 file changed, 84 insertions(+), 20 deletions(-) (limited to 'python') diff --git a/python/trx/radio_if.py b/python/trx/radio_if.py index dbccae7..1ded337 100644 --- a/python/trx/radio_if.py +++ b/python/trx/radio_if.py @@ -29,12 +29,14 @@ 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 +from gnuradio import filter +from gnuradio.filter import firdes + # HACK: should be implemented in C++! class burst_to_fn_time(gr.basic_block): @@ -55,10 +57,29 @@ class burst_to_fn_time(gr.basic_block): if pmt.to_python(fn_time) is not None: self.message_port_pub(pmt.intern("fn_time_out"), fn_time_msg) +class dict_toggle_sign(gr.basic_block): + def __init__(self): # only default arguments here + gr.basic_block.__init__(self, + name='Change sign of elts in dict', + in_sig=[], + out_sig=[] + ) + self.message_port_register_in(pmt.intern("dict_in")) + self.message_port_register_out(pmt.intern("dict_out")) + self.set_msg_handler(pmt.intern("dict_in"), self.change_sign) + + def change_sign(self, msg): + if pmt.is_dict(msg): + d = pmt.to_python(msg) + for key, value in d.items(): + d[key] *= -1 + self.message_port_pub(pmt.intern("dict_out"), pmt.to_pmt(d)) + class radio_if(gr.top_block): # PHY specific variables rx_freq = 935e6 tx_freq = 890e6 + osr = 4 # Application state flags trx_started = False @@ -75,8 +96,10 @@ class radio_if(gr.top_block): print("[i] Init Radio interface") # PHY specific variables + self.sample_rate = phy_sample_rate self.rx_gain = phy_rx_gain self.tx_gain = phy_tx_gain + self.ppm = phy_ppm gr.top_block.__init__(self, "GR-GSM TRX") @@ -97,14 +120,15 @@ class radio_if(gr.top_block): self.phy_src.set_bandwidth(650e3, 0) self.phy_src.set_gain(phy_rx_gain) - self.gsm_input = grgsm.gsm_input( - ppm = phy_ppm - int(phy_ppm), osr = 4, - fc = self.rx_freq, samp_rate_in = phy_sample_rate) + self.msg_to_tag_src = grgsm.msg_to_tag() - self.gsm_receiver = grgsm.receiver(4, ([0]), ([])) + self.rotator_src = grgsm.controlled_rotator_cc( + self.calc_phase_inc(self.rx_freq)) - self.gsm_clck_ctrl = grgsm.clock_offset_control( - self.rx_freq, phy_sample_rate, osr = 4) + self.lpf = filter.fir_filter_ccf(1, firdes.low_pass( + 1, phy_sample_rate, 125e3, 5e3, firdes.WIN_HAMMING, 6.76)) + + self.gsm_receiver = grgsm.receiver(self.osr, ([0]), ([])) self.ts_filter = grgsm.burst_timeslot_filter(0) self.ts_filter.set_policy(grgsm.FILTER_POLICY_DROP_ALL) @@ -112,10 +136,18 @@ class radio_if(gr.top_block): # Connections self.connect( (self.phy_src, 0), - (self.gsm_input, 0)) + (self.msg_to_tag_src, 0)) self.connect( - (self.gsm_input, 0), + (self.msg_to_tag_src, 0), + (self.rotator_src, 0)) + + self.connect( + (self.rotator_src, 0), + (self.lpf, 0)) + + self.connect( + (self.lpf, 0), (self.gsm_receiver, 0)) self.msg_connect( @@ -126,14 +158,6 @@ class radio_if(gr.top_block): (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.gsm_input, 'ctrl_in')) - # TX Path Definition self.phy_sink = uhd.usrp_sink(phy_args, @@ -156,12 +180,17 @@ class radio_if(gr.top_block): blocks.byte_t, 'packet_len') self.gmsk_mod = grgsm.gsm_gmsk_mod( - BT = 4, pulse_duration = 4, sps = 4) + BT = 0.3, pulse_duration = 4, sps = self.osr) self.burst_shaper = digital.burst_shaper_cc( (firdes.window(firdes.WIN_HANN, 16, 0)), 0, 20, False, "packet_len") + self.msg_to_tag_sink = grgsm.msg_to_tag() + + self.rotator_sink = grgsm.controlled_rotator_cc( + -self.calc_phase_inc(self.tx_freq)) + # Connections self.msg_connect( (self.trx_burst_if, 'bursts'), @@ -185,6 +214,14 @@ class radio_if(gr.top_block): self.connect( (self.burst_shaper, 0), + (self.msg_to_tag_sink, 0)) + + self.connect( + (self.msg_to_tag_sink, 0), + (self.rotator_sink, 0)) + + self.connect( + (self.rotator_sink, 0), (self.phy_sink, 0)) @@ -205,19 +242,46 @@ class radio_if(gr.top_block): (self.burst_to_fn_time, 'fn_time_out'), (self.tx_time_setter, 'fn_time')) + + # AFC (Automatic Frequency Correction) + self.gsm_clck_ctrl = grgsm.clock_offset_control( + self.rx_freq, phy_sample_rate, osr = self.osr) + + self.dict_toggle_sign = dict_toggle_sign() + + # Connections + self.msg_connect( + (self.gsm_receiver, 'measurements'), + (self.gsm_clck_ctrl, 'measurements')) + + self.msg_connect( + (self.gsm_clck_ctrl, 'ctrl'), + (self.msg_to_tag_src, 'msg')) + + self.msg_connect( + (self.gsm_clck_ctrl, 'ctrl'), + (self.dict_toggle_sign, 'dict_in')) + + self.msg_connect( + (self.dict_toggle_sign, 'dict_out'), + (self.msg_to_tag_sink, 'msg')) + def shutdown(self): print("[i] Shutdown Radio interface") self.stop() self.wait() + def calc_phase_inc(self, fc): + return self.ppm / 1.0e6 * 2 * pi * fc / self.sample_rate + 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.rotator_src.set_phase_inc(self.calc_phase_inc(fc)) self.rx_freq = fc def set_tx_freq(self, fc): self.phy_sink.set_center_freq(fc, 0) + self.rotator_sink.set_phase_inc(-self.calc_phase_inc(fc)) self.tx_freq = fc def set_rx_gain(self, gain): -- cgit v1.2.3 From 0aafe2856de2286e5b88e43f81785a1aa5284f3f Mon Sep 17 00:00:00 2001 From: Vadim Yanitskiy Date: Fri, 1 Dec 2017 19:24:14 +0700 Subject: Implement the 'burst_to_fn_time' block in C++ --- python/__init__.py | 1 - python/misc_utils/CMakeLists.txt | 1 - python/misc_utils/burst_to_fn_time.py | 28 ---------------------------- 3 files changed, 30 deletions(-) delete mode 100644 python/misc_utils/burst_to_fn_time.py (limited to 'python') diff --git a/python/__init__.py b/python/__init__.py index b3c9768..ad551ac 100644 --- a/python/__init__.py +++ b/python/__init__.py @@ -58,7 +58,6 @@ from gsm_sdcch8_demapper import gsm_sdcch8_demapper from gsm_gmsk_mod import gsm_gmsk_mod from fn_time import * from txtime_bursts_tagger import * -from burst_to_fn_time import * import arfcn diff --git a/python/misc_utils/CMakeLists.txt b/python/misc_utils/CMakeLists.txt index 8c7c175..ec732a4 100644 --- a/python/misc_utils/CMakeLists.txt +++ b/python/misc_utils/CMakeLists.txt @@ -23,6 +23,5 @@ GR_PYTHON_INSTALL( clock_offset_corrector_tagged.py hier_block.py fn_time.py - burst_to_fn_time.py DESTINATION ${GR_PYTHON_DIR}/grgsm ) diff --git a/python/misc_utils/burst_to_fn_time.py b/python/misc_utils/burst_to_fn_time.py deleted file mode 100644 index e31cf92..0000000 --- a/python/misc_utils/burst_to_fn_time.py +++ /dev/null @@ -1,28 +0,0 @@ -""" -Embedded Python Blocks: - -Each this file is saved, GRC will instantiate the first class it finds to get -ports and parameters of your block. The arguments to __init__ will be the -parameters. All of them are required to have default values! -""" -import numpy as np -from gnuradio import gr -import pmt - -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) -- cgit v1.2.3 From 5cba704319a7f31a64ff1600af39c701061a9b2a Mon Sep 17 00:00:00 2001 From: Vadim Yanitskiy Date: Mon, 4 Dec 2017 01:51:00 +0700 Subject: trx/radio_if.py: use native burst_to_fn_time block --- python/trx/radio_if.py | 20 +------------------- 1 file changed, 1 insertion(+), 19 deletions(-) (limited to 'python') diff --git a/python/trx/radio_if.py b/python/trx/radio_if.py index 1ded337..f84a710 100644 --- a/python/trx/radio_if.py +++ b/python/trx/radio_if.py @@ -39,24 +39,6 @@ from gnuradio.filter import firdes # HACK: should be implemented in C++! -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 dict_toggle_sign(gr.basic_block): def __init__(self): # only default arguments here gr.basic_block.__init__(self, @@ -227,7 +209,7 @@ class radio_if(gr.top_block): # RX & TX synchronization self.bt_filter = grgsm.burst_type_filter([3]) - self.burst_to_fn_time = burst_to_fn_time() + self.burst_to_fn_time = grgsm.burst_to_fn_time() # Connections self.msg_connect( -- cgit v1.2.3 From 9dded9b9f6e3a8a0412c2ab8f5b3e49b376d1b04 Mon Sep 17 00:00:00 2001 From: Vadim Yanitskiy Date: Tue, 5 Dec 2017 01:00:51 +0700 Subject: trx/radio_if.py: clean up and explain GSM timings --- python/trx/radio_if.py | 16 ++++++++++++---- 1 file changed, 12 insertions(+), 4 deletions(-) (limited to 'python') diff --git a/python/trx/radio_if.py b/python/trx/radio_if.py index f84a710..e0678ab 100644 --- a/python/trx/radio_if.py +++ b/python/trx/radio_if.py @@ -66,9 +66,17 @@ class radio_if(gr.top_block): # Application state flags trx_started = False - # GSM timings - delay_correction = 285.616e-6 - ul_dl_shift = -(6.0/1625000*(156.25)*3) + # GSM timings (in microseconds [uS]) + # One timeslot duration is 576.9 μs = 15/26 ms, + # or 156.25 symbol periods (a symbol period is 48/13 μs) + GSM_SYM_PERIOD_uS = 48.0 / 13.0 + GSM_TS_PERIOD_uS = GSM_SYM_PERIOD_uS * 156.25 + GSM_UL_DL_SHIFT_uS = -(GSM_TS_PERIOD_uS * 3) + + # FIXME: shall be measured (automatically?) for + # particular device and particular clock rate. + # The current value is measured for USRP B2X0 at 26e6. + delay_correction = (285.616 + 2 * GSM_SYM_PERIOD_uS) * 1e-6 def __init__(self, phy_args, phy_sample_rate, phy_rx_gain, phy_tx_gain, phy_ppm, @@ -154,7 +162,7 @@ class radio_if(gr.top_block): self.tx_time_setter = grgsm.txtime_setter( 0xffffffff, 0, 0, 0, 0, 0, - self.delay_correction + self.ul_dl_shift) + self.delay_correction + self.GSM_UL_DL_SHIFT_uS * 1e-6) self.tx_burst_proc = grgsm.preprocess_tx_burst() -- cgit v1.2.3 From 34266e785dc7c203c978dc5c6910dd2db4e6b3b8 Mon Sep 17 00:00:00 2001 From: Vadim Yanitskiy Date: Tue, 5 Dec 2017 01:01:43 +0700 Subject: trx/radio_if.py: handle Timing Advance from CTRL --- python/trx/ctrl_if_bb.py | 14 ++++++++++++++ python/trx/radio_if.py | 5 +++++ 2 files changed, 19 insertions(+) (limited to 'python') diff --git a/python/trx/ctrl_if_bb.py b/python/trx/ctrl_if_bb.py index 26ae49a..5874e59 100644 --- a/python/trx/ctrl_if_bb.py +++ b/python/trx/ctrl_if_bb.py @@ -62,6 +62,7 @@ class ctrl_if_bb(ctrl_if): if self.tb.trx_started: print("[i] Stopping transceiver...") self.tb.trx_started = False + self.tb.set_ta(0) self.tb.stop() self.tb.wait() @@ -144,6 +145,19 @@ class ctrl_if_bb(ctrl_if): return (0, [meas_dbm]) + # Timing Advance control + elif self.verify_cmd(request, "SETTA", 1): + print("[i] Recv SETTA cmd") + + # Check TA range + ta = int(request[1]) + if ta < 0 or ta > 63: + print("[!] TA value must be in range: 0..63") + return -1 + + self.tb.set_ta(ta) + return 0 + # Misc elif self.verify_cmd(request, "ECHO", 0): print("[i] Recv ECHO cmd") diff --git a/python/trx/radio_if.py b/python/trx/radio_if.py index e0678ab..82491a8 100644 --- a/python/trx/radio_if.py +++ b/python/trx/radio_if.py @@ -281,3 +281,8 @@ class radio_if(gr.top_block): def set_tx_gain(self, gain): self.phy_sink.set_gain(gain, 0) self.tx_gain = gain + + def set_ta(self, ta): + print("[i] Setting TA value %d" % ta) + advance_time_sec = ta * self.GSM_SYM_PERIOD_uS * 1e-6 + self.tx_time_setter.set_timing_advance(advance_time_sec) -- cgit v1.2.3