blob: 407e72453f6b7948d5f24c9f5bf5c84499c389e9 [file] [log] [blame]
Piotr Krysik902f4eb2017-09-19 08:04:33 +02001#!/usr/bin/env python2
2# -*- coding: utf-8 -*-
3
4# GR-GSM based transceiver
5# Follow graph implementation
6#
Vadim Yanitskiy97dc84e2018-08-10 05:29:23 +07007# (C) 2016-2018 by Vadim Yanitskiy <axilirator@gmail.com>
Piotr Krysikd2f162f2017-11-30 12:50:00 +01008# (C) 2017 by Piotr Krysik <ptrkrysik@gmail.com>
Piotr Krysik902f4eb2017-09-19 08:04:33 +02009#
10# All Rights Reserved
11#
12# This program is free software; you can redistribute it and/or modify
13# it under the terms of the GNU General Public License as published by
14# the Free Software Foundation; either version 2 of the License, or
15# (at your option) any later version.
16#
17# This program is distributed in the hope that it will be useful,
18# but WITHOUT ANY WARRANTY; without even the implied warranty of
19# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
20# GNU General Public License for more details.
21#
22# You should have received a copy of the GNU General Public License along
23# with this program; if not, write to the Free Software Foundation, Inc.,
24# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
25
26import pmt
27import time
28import grgsm
Piotr Krysik902f4eb2017-09-19 08:04:33 +020029
30from math import pi
31
Vadim Yanitskiy97dc84e2018-08-10 05:29:23 +070032from gnuradio import eng_notation
Vadim Yanitskiy89aa4692017-11-14 00:15:20 +070033from gnuradio import digital
Piotr Krysik902f4eb2017-09-19 08:04:33 +020034from gnuradio import blocks
Vadim Yanitskiy89aa4692017-11-14 00:15:20 +070035from gnuradio import uhd
Piotr Krysik902f4eb2017-09-19 08:04:33 +020036from gnuradio import gr
37
Vadim Yanitskiyd222ee52017-12-03 23:49:09 +070038from gnuradio import filter
39from gnuradio.filter import firdes
40
Vadim Yanitskiy89aa4692017-11-14 00:15:20 +070041
42# HACK: should be implemented in C++!
Vadim Yanitskiyd222ee52017-12-03 23:49:09 +070043class dict_toggle_sign(gr.basic_block):
44 def __init__(self): # only default arguments here
45 gr.basic_block.__init__(self,
46 name='Change sign of elts in dict',
47 in_sig=[],
48 out_sig=[]
49 )
50 self.message_port_register_in(pmt.intern("dict_in"))
51 self.message_port_register_out(pmt.intern("dict_out"))
52 self.set_msg_handler(pmt.intern("dict_in"), self.change_sign)
53
54 def change_sign(self, msg):
55 if pmt.is_dict(msg):
56 d = pmt.to_python(msg)
57 for key, value in d.items():
58 d[key] *= -1
59 self.message_port_pub(pmt.intern("dict_out"), pmt.to_pmt(d))
60
Vadim Yanitskiy873e44e2017-10-17 06:52:01 +070061class radio_if(gr.top_block):
Piotr Krysik1cc264f2018-09-06 19:57:57 +020062 # PHY specific variables
63 freq_offset_hz = None
64 rx_freq = 935e6
65 tx_freq = 890e6
66 osr = 4
Piotr Krysik902f4eb2017-09-19 08:04:33 +020067
Piotr Krysik1cc264f2018-09-06 19:57:57 +020068 # Application state flags
69 trx_started = False
Vadim Yanitskiy89aa4692017-11-14 00:15:20 +070070
Piotr Krysik1cc264f2018-09-06 19:57:57 +020071 # GSM timings (in microseconds [uS])
72 # One timeslot duration is 576.9 μs = 15/26 ms,
73 # or 156.25 symbol periods (a symbol period is 48/13 μs)
74 GSM_SYM_PERIOD_uS = 48.0 / 13.0
75 GSM_TS_PERIOD_uS = GSM_SYM_PERIOD_uS * 156.25
76 GSM_UL_DL_SHIFT_uS = -(GSM_TS_PERIOD_uS * 3)
Vadim Yanitskiy9dded9b2017-12-05 01:00:51 +070077
Piotr Krysik1cc264f2018-09-06 19:57:57 +020078 # FIXME: shall be measured (automatically?) for
79 # particular device and particular clock rate.
80 # The current value is measured for USRP B2X0 at 26e6.
81 delay_correction = (285.616 + 2 * GSM_SYM_PERIOD_uS) * 1e-6
Piotr Krysik902f4eb2017-09-19 08:04:33 +020082
Piotr Krysik1cc264f2018-09-06 19:57:57 +020083 def __init__(self, phy_args, phy_sample_rate,
84 phy_rx_gain, phy_tx_gain, phy_ppm,
85 phy_rx_antenna, phy_tx_antenna,
86 trx_bind_addr, trx_remote_addr,
87 trx_base_port):
Piotr Krysik902f4eb2017-09-19 08:04:33 +020088
Piotr Krysik1cc264f2018-09-06 19:57:57 +020089 print("[i] Init Radio interface (L:%s:%u <-> R:%s:%u)"
90 % (trx_bind_addr, trx_base_port + 2,
91 trx_remote_addr, trx_base_port + 102))
Piotr Krysik902f4eb2017-09-19 08:04:33 +020092
Piotr Krysik1cc264f2018-09-06 19:57:57 +020093 # PHY specific variables
94 self.sample_rate = phy_sample_rate
95 self.rx_gain = phy_rx_gain
96 self.tx_gain = phy_tx_gain
97 self.ppm = phy_ppm
Piotr Krysik902f4eb2017-09-19 08:04:33 +020098
Piotr Krysik1cc264f2018-09-06 19:57:57 +020099 gr.top_block.__init__(self, "GR-GSM TRX")
Piotr Krysik902f4eb2017-09-19 08:04:33 +0200100
Piotr Krysik1cc264f2018-09-06 19:57:57 +0200101 # TRX Burst Interface
102 self.trx_burst_if = grgsm.trx_burst_if(
103 trx_bind_addr, trx_remote_addr,
104 str(trx_base_port))
Piotr Krysik902f4eb2017-09-19 08:04:33 +0200105
Piotr Krysik1cc264f2018-09-06 19:57:57 +0200106 # RX path definition
107 self.phy_src = uhd.usrp_source(phy_args,
108 uhd.stream_args(cpu_format="fc32",
109 channels=range(1)))
Vadim Yanitskiy89aa4692017-11-14 00:15:20 +0700110
Piotr Krysik1cc264f2018-09-06 19:57:57 +0200111 self.phy_src.set_clock_rate(26e6, uhd.ALL_MBOARDS)
112 self.phy_src.set_center_freq(self.rx_freq, 0)
113 self.phy_src.set_antenna(phy_rx_antenna, 0)
114 self.phy_src.set_samp_rate(phy_sample_rate)
115 self.phy_src.set_bandwidth(650e3, 0)
116 self.phy_src.set_gain(phy_rx_gain)
Piotr Krysik902f4eb2017-09-19 08:04:33 +0200117
Piotr Krysik1cc264f2018-09-06 19:57:57 +0200118 self.msg_to_tag_src = grgsm.msg_to_tag()
Piotr Krysik902f4eb2017-09-19 08:04:33 +0200119
Piotr Krysik1cc264f2018-09-06 19:57:57 +0200120 self.rotator_src = grgsm.controlled_rotator_cc(
121 self.calc_phase_inc(self.rx_freq))
Piotr Krysik902f4eb2017-09-19 08:04:33 +0200122
Piotr Krysik1cc264f2018-09-06 19:57:57 +0200123 self.lpf = filter.fir_filter_ccf(1, firdes.low_pass(
124 1, phy_sample_rate, 125e3, 5e3, firdes.WIN_HAMMING, 6.76))
Vadim Yanitskiyd222ee52017-12-03 23:49:09 +0700125
Piotr Krysik1cc264f2018-09-06 19:57:57 +0200126 self.gsm_receiver = grgsm.receiver(self.osr, ([0]), ([]))
Piotr Krysik902f4eb2017-09-19 08:04:33 +0200127
Piotr Krysik1cc264f2018-09-06 19:57:57 +0200128 self.ts_filter = grgsm.burst_timeslot_filter(0)
129 self.ts_filter.set_policy(grgsm.FILTER_POLICY_DROP_ALL)
Vadim Yanitskiy962e2d82017-10-17 09:24:55 +0700130
Piotr Krysik1cc264f2018-09-06 19:57:57 +0200131 # Connections
132 self.connect(
133 (self.phy_src, 0),
134 (self.msg_to_tag_src, 0))
Piotr Krysik902f4eb2017-09-19 08:04:33 +0200135
Piotr Krysik1cc264f2018-09-06 19:57:57 +0200136 self.connect(
137 (self.msg_to_tag_src, 0),
138 (self.rotator_src, 0))
Vadim Yanitskiyd222ee52017-12-03 23:49:09 +0700139
Piotr Krysik1cc264f2018-09-06 19:57:57 +0200140 self.connect(
141 (self.rotator_src, 0),
142 (self.lpf, 0))
Vadim Yanitskiyd222ee52017-12-03 23:49:09 +0700143
Piotr Krysik1cc264f2018-09-06 19:57:57 +0200144 self.connect(
145 (self.lpf, 0),
146 (self.gsm_receiver, 0))
Vadim Yanitskiy89aa4692017-11-14 00:15:20 +0700147
Piotr Krysik1cc264f2018-09-06 19:57:57 +0200148 self.msg_connect(
149 (self.gsm_receiver, 'C0'),
150 (self.ts_filter, 'in'))
Vadim Yanitskiy89aa4692017-11-14 00:15:20 +0700151
Piotr Krysik1cc264f2018-09-06 19:57:57 +0200152 self.msg_connect(
153 (self.ts_filter, 'out'),
154 (self.trx_burst_if, 'bursts'))
Vadim Yanitskiy89aa4692017-11-14 00:15:20 +0700155
Vadim Yanitskiy962e2d82017-10-17 09:24:55 +0700156
Piotr Krysik1cc264f2018-09-06 19:57:57 +0200157 # TX Path Definition
158 self.phy_sink = uhd.usrp_sink(phy_args,
159 uhd.stream_args(cpu_format="fc32",
160 channels=range(1)), "packet_len")
Piotr Krysik902f4eb2017-09-19 08:04:33 +0200161
Piotr Krysik1cc264f2018-09-06 19:57:57 +0200162 self.phy_sink.set_clock_rate(26e6, uhd.ALL_MBOARDS)
163 self.phy_sink.set_antenna(phy_tx_antenna, 0)
164 self.phy_sink.set_samp_rate(phy_sample_rate)
165 self.phy_sink.set_center_freq(self.tx_freq, 0)
166 self.phy_sink.set_gain(self.tx_gain)
Vadim Yanitskiy89aa4692017-11-14 00:15:20 +0700167
Piotr Krysik1cc264f2018-09-06 19:57:57 +0200168 self.tx_time_setter = grgsm.txtime_setter(
169 0xffffffff, 0, 0, 0, 0, 0,
170 self.delay_correction + self.GSM_UL_DL_SHIFT_uS * 1e-6)
Vadim Yanitskiy89aa4692017-11-14 00:15:20 +0700171
Piotr Krysik1cc264f2018-09-06 19:57:57 +0200172 self.tx_burst_proc = grgsm.preprocess_tx_burst()
Vadim Yanitskiy89aa4692017-11-14 00:15:20 +0700173
Piotr Krysik1cc264f2018-09-06 19:57:57 +0200174 self.pdu_to_tagged_stream = blocks.pdu_to_tagged_stream(
175 blocks.byte_t, 'packet_len')
Vadim Yanitskiy89aa4692017-11-14 00:15:20 +0700176
Piotr Krysik1cc264f2018-09-06 19:57:57 +0200177 self.gmsk_mod = grgsm.gsm_gmsk_mod(
178 BT = 0.3, pulse_duration = 4, sps = self.osr)
Vadim Yanitskiy89aa4692017-11-14 00:15:20 +0700179
Piotr Krysik1cc264f2018-09-06 19:57:57 +0200180 self.burst_shaper = digital.burst_shaper_cc(
181 (firdes.window(firdes.WIN_HANN, 16, 0)),
182 0, 20, False, "packet_len")
Vadim Yanitskiy89aa4692017-11-14 00:15:20 +0700183
Piotr Krysik1cc264f2018-09-06 19:57:57 +0200184 self.msg_to_tag_sink = grgsm.msg_to_tag()
Vadim Yanitskiyd222ee52017-12-03 23:49:09 +0700185
Piotr Krysik1cc264f2018-09-06 19:57:57 +0200186 self.rotator_sink = grgsm.controlled_rotator_cc(
187 -self.calc_phase_inc(self.tx_freq))
Vadim Yanitskiyd222ee52017-12-03 23:49:09 +0700188
Piotr Krysik1cc264f2018-09-06 19:57:57 +0200189 # Connections
190 self.msg_connect(
191 (self.trx_burst_if, 'bursts'),
192 (self.tx_time_setter, 'bursts_in'))
Vadim Yanitskiy89aa4692017-11-14 00:15:20 +0700193
Piotr Krysik1cc264f2018-09-06 19:57:57 +0200194 self.msg_connect(
195 (self.tx_time_setter, 'bursts_out'),
196 (self.tx_burst_proc, 'bursts_in'))
Vadim Yanitskiy89aa4692017-11-14 00:15:20 +0700197
Piotr Krysik1cc264f2018-09-06 19:57:57 +0200198 self.msg_connect(
199 (self.tx_burst_proc, 'bursts_out'),
200 (self.pdu_to_tagged_stream, 'pdus'))
Vadim Yanitskiy89aa4692017-11-14 00:15:20 +0700201
Piotr Krysik1cc264f2018-09-06 19:57:57 +0200202 self.connect(
203 (self.pdu_to_tagged_stream, 0),
204 (self.gmsk_mod, 0))
Vadim Yanitskiy89aa4692017-11-14 00:15:20 +0700205
Piotr Krysik1cc264f2018-09-06 19:57:57 +0200206 self.connect(
207 (self.gmsk_mod, 0),
208 (self.burst_shaper, 0))
Vadim Yanitskiy89aa4692017-11-14 00:15:20 +0700209
Piotr Krysik1cc264f2018-09-06 19:57:57 +0200210 self.connect(
211 (self.burst_shaper, 0),
212 (self.msg_to_tag_sink, 0))
Vadim Yanitskiyd222ee52017-12-03 23:49:09 +0700213
Piotr Krysik1cc264f2018-09-06 19:57:57 +0200214 self.connect(
215 (self.msg_to_tag_sink, 0),
216 (self.rotator_sink, 0))
Vadim Yanitskiyd222ee52017-12-03 23:49:09 +0700217
Piotr Krysik1cc264f2018-09-06 19:57:57 +0200218 self.connect(
219 (self.rotator_sink, 0),
220 (self.phy_sink, 0))
Vadim Yanitskiy89aa4692017-11-14 00:15:20 +0700221
222
Piotr Krysik1cc264f2018-09-06 19:57:57 +0200223 # RX & TX synchronization
224 self.bt_filter = grgsm.burst_type_filter([3])
225 self.burst_to_fn_time = grgsm.burst_to_fn_time()
Vadim Yanitskiy89aa4692017-11-14 00:15:20 +0700226
Piotr Krysik1cc264f2018-09-06 19:57:57 +0200227 # Connections
228 self.msg_connect(
229 (self.gsm_receiver, 'C0'),
230 (self.bt_filter, 'bursts_in'))
Vadim Yanitskiy89aa4692017-11-14 00:15:20 +0700231
Piotr Krysik1cc264f2018-09-06 19:57:57 +0200232 self.msg_connect(
233 (self.bt_filter, 'bursts_out'),
234 (self.burst_to_fn_time, 'bursts_in'))
Vadim Yanitskiy89aa4692017-11-14 00:15:20 +0700235
Piotr Krysik1cc264f2018-09-06 19:57:57 +0200236 self.msg_connect(
237 (self.burst_to_fn_time, 'fn_time_out'),
238 (self.tx_time_setter, 'fn_time'))
Piotr Krysik902f4eb2017-09-19 08:04:33 +0200239
Vadim Yanitskiyd222ee52017-12-03 23:49:09 +0700240
Piotr Krysik1cc264f2018-09-06 19:57:57 +0200241 # AFC (Automatic Frequency Correction)
242 self.gsm_clck_ctrl = grgsm.clock_offset_control(
243 self.rx_freq, phy_sample_rate, osr = self.osr)
Vadim Yanitskiyd222ee52017-12-03 23:49:09 +0700244
Piotr Krysik1cc264f2018-09-06 19:57:57 +0200245 self.dict_toggle_sign = dict_toggle_sign()
Vadim Yanitskiyd222ee52017-12-03 23:49:09 +0700246
Piotr Krysik1cc264f2018-09-06 19:57:57 +0200247 # Connections
248 self.msg_connect(
249 (self.gsm_receiver, 'measurements'),
250 (self.gsm_clck_ctrl, 'measurements'))
Vadim Yanitskiyd222ee52017-12-03 23:49:09 +0700251
Piotr Krysik1cc264f2018-09-06 19:57:57 +0200252 self.msg_connect(
253 (self.gsm_clck_ctrl, 'ctrl'),
254 (self.msg_to_tag_src, 'msg'))
Vadim Yanitskiyd222ee52017-12-03 23:49:09 +0700255
Piotr Krysik1cc264f2018-09-06 19:57:57 +0200256 self.msg_connect(
257 (self.gsm_clck_ctrl, 'ctrl'),
258 (self.dict_toggle_sign, 'dict_in'))
Vadim Yanitskiyd222ee52017-12-03 23:49:09 +0700259
Piotr Krysik1cc264f2018-09-06 19:57:57 +0200260 self.msg_connect(
261 (self.dict_toggle_sign, 'dict_out'),
262 (self.msg_to_tag_sink, 'msg'))
Piotr Krysik902f4eb2017-09-19 08:04:33 +0200263
Vadim Yanitskiy0017a352018-03-03 23:37:30 +0700264
Piotr Krysik1cc264f2018-09-06 19:57:57 +0200265 # Some UHD devices (such as UmTRX) do start the clock
266 # not from 0, so it's required to reset it manually.
267 # Resetting UHD source will also affect the sink.
268 self.phy_src.set_time_now(uhd.time_spec(0.0))
Vadim Yanitskiy0017a352018-03-03 23:37:30 +0700269
Piotr Krysik1cc264f2018-09-06 19:57:57 +0200270 def shutdown(self):
271 print("[i] Shutdown Radio interface")
272 self.stop()
273 self.wait()
Piotr Krysik902f4eb2017-09-19 08:04:33 +0200274
Piotr Krysik1cc264f2018-09-06 19:57:57 +0200275 def calc_phase_inc(self, fc):
276 return self.ppm / 1.0e6 * 2 * pi * fc / self.sample_rate
Vadim Yanitskiyd222ee52017-12-03 23:49:09 +0700277
Piotr Krysik1cc264f2018-09-06 19:57:57 +0200278 def set_rx_freq(self, fc):
279 if self.freq_offset_hz is not None:
280 fc += self.freq_offset_hz
281 print("[#] Shifting RX freq. to %s (offset is %s)"
282 % (eng_notation.num_to_str(fc),
283 eng_notation.num_to_str(self.freq_offset_hz)))
284 self.phy_src.set_center_freq(fc, 0)
285 self.rotator_src.set_phase_inc(self.calc_phase_inc(fc))
286 self.rx_freq = fc
Vadim Yanitskiy89aa4692017-11-14 00:15:20 +0700287
Piotr Krysik1cc264f2018-09-06 19:57:57 +0200288 def set_tx_freq(self, fc):
289 if self.freq_offset_hz is not None:
290 fc += self.freq_offset_hz
291 print("[#] Shifting TX freq. to %s (offset is %s)"
292 % (eng_notation.num_to_str(fc),
293 eng_notation.num_to_str(self.freq_offset_hz)))
294 self.phy_sink.set_center_freq(fc, 0)
295 self.rotator_sink.set_phase_inc(-self.calc_phase_inc(fc))
296 self.tx_freq = fc
Piotr Krysik902f4eb2017-09-19 08:04:33 +0200297
Piotr Krysik1cc264f2018-09-06 19:57:57 +0200298 def set_rx_gain(self, gain):
299 self.phy_src.set_gain(gain, 0)
300 self.rx_gain = gain
Vadim Yanitskiy01c6afd2017-10-19 01:14:24 +0700301
Piotr Krysik1cc264f2018-09-06 19:57:57 +0200302 def set_tx_gain(self, gain):
303 self.phy_sink.set_gain(gain, 0)
304 self.tx_gain = gain
Vadim Yanitskiy34266e72017-12-05 01:01:43 +0700305
Piotr Krysik1cc264f2018-09-06 19:57:57 +0200306 def set_ta(self, ta):
307 print("[i] Setting TA value %d" % ta)
308 advance_time_sec = ta * self.GSM_SYM_PERIOD_uS * 1e-6
309 self.tx_time_setter.set_timing_advance(advance_time_sec)