blob: d2afcf607ca84ee4164423c0183d2ec45d7f6754 [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#
7# (C) 2016-2017 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 Yanitskiy89aa4692017-11-14 00:15:20 +070032from gnuradio import digital
Piotr Krysik902f4eb2017-09-19 08:04:33 +020033from gnuradio import blocks
Vadim Yanitskiy89aa4692017-11-14 00:15:20 +070034from gnuradio import uhd
Piotr Krysik902f4eb2017-09-19 08:04:33 +020035from gnuradio import gr
36
Vadim Yanitskiyd222ee52017-12-03 23:49:09 +070037from gnuradio import filter
38from gnuradio.filter import firdes
39
Vadim Yanitskiy89aa4692017-11-14 00:15:20 +070040
41# HACK: should be implemented in C++!
Vadim Yanitskiyd222ee52017-12-03 23:49:09 +070042class dict_toggle_sign(gr.basic_block):
43 def __init__(self): # only default arguments here
44 gr.basic_block.__init__(self,
45 name='Change sign of elts in dict',
46 in_sig=[],
47 out_sig=[]
48 )
49 self.message_port_register_in(pmt.intern("dict_in"))
50 self.message_port_register_out(pmt.intern("dict_out"))
51 self.set_msg_handler(pmt.intern("dict_in"), self.change_sign)
52
53 def change_sign(self, msg):
54 if pmt.is_dict(msg):
55 d = pmt.to_python(msg)
56 for key, value in d.items():
57 d[key] *= -1
58 self.message_port_pub(pmt.intern("dict_out"), pmt.to_pmt(d))
59
Vadim Yanitskiy873e44e2017-10-17 06:52:01 +070060class radio_if(gr.top_block):
Piotr Krysik902f4eb2017-09-19 08:04:33 +020061 # PHY specific variables
Vadim Yanitskiy89aa4692017-11-14 00:15:20 +070062 rx_freq = 935e6
63 tx_freq = 890e6
Vadim Yanitskiyd222ee52017-12-03 23:49:09 +070064 osr = 4
Piotr Krysik902f4eb2017-09-19 08:04:33 +020065
66 # Application state flags
67 trx_started = False
Vadim Yanitskiy89aa4692017-11-14 00:15:20 +070068
Vadim Yanitskiy9dded9b2017-12-05 01:00:51 +070069 # GSM timings (in microseconds [uS])
70 # One timeslot duration is 576.9 μs = 15/26 ms,
71 # or 156.25 symbol periods (a symbol period is 48/13 μs)
72 GSM_SYM_PERIOD_uS = 48.0 / 13.0
73 GSM_TS_PERIOD_uS = GSM_SYM_PERIOD_uS * 156.25
74 GSM_UL_DL_SHIFT_uS = -(GSM_TS_PERIOD_uS * 3)
75
76 # FIXME: shall be measured (automatically?) for
77 # particular device and particular clock rate.
78 # The current value is measured for USRP B2X0 at 26e6.
79 delay_correction = (285.616 + 2 * GSM_SYM_PERIOD_uS) * 1e-6
Piotr Krysik902f4eb2017-09-19 08:04:33 +020080
Vadim Yanitskiy790b6f02017-10-17 11:47:36 +070081 def __init__(self, phy_args, phy_sample_rate,
82 phy_rx_gain, phy_tx_gain, phy_ppm,
83 phy_rx_antenna, phy_tx_antenna,
Vadim Yanitskiy473b35b2018-08-10 00:20:03 +070084 trx_bind_addr, trx_remote_addr,
85 trx_base_port):
Piotr Krysik902f4eb2017-09-19 08:04:33 +020086
Vadim Yanitskiy2adbee42018-08-10 00:51:36 +070087 print("[i] Init Radio interface (L:%s:%u <-> R:%s:%u)"
88 % (trx_bind_addr, trx_base_port + 2,
89 trx_remote_addr, trx_base_port + 102))
Piotr Krysik902f4eb2017-09-19 08:04:33 +020090
91 # PHY specific variables
Vadim Yanitskiyd222ee52017-12-03 23:49:09 +070092 self.sample_rate = phy_sample_rate
Vadim Yanitskiy790b6f02017-10-17 11:47:36 +070093 self.rx_gain = phy_rx_gain
94 self.tx_gain = phy_tx_gain
Vadim Yanitskiyd222ee52017-12-03 23:49:09 +070095 self.ppm = phy_ppm
Piotr Krysik902f4eb2017-09-19 08:04:33 +020096
97 gr.top_block.__init__(self, "GR-GSM TRX")
Piotr Krysik902f4eb2017-09-19 08:04:33 +020098
Vadim Yanitskiy89aa4692017-11-14 00:15:20 +070099 # TRX Burst Interface
100 self.trx_burst_if = grgsm.trx_burst_if(
Vadim Yanitskiy5394c602018-08-10 00:01:26 +0700101 trx_bind_addr, trx_remote_addr,
102 str(trx_base_port))
Piotr Krysik902f4eb2017-09-19 08:04:33 +0200103
Vadim Yanitskiy89aa4692017-11-14 00:15:20 +0700104 # RX path definition
105 self.phy_src = uhd.usrp_source(phy_args,
106 uhd.stream_args(cpu_format="fc32",
107 channels=range(1)))
108
Vadim Yanitskiy63703bb2017-12-03 23:40:21 +0700109 self.phy_src.set_clock_rate(26e6, uhd.ALL_MBOARDS)
Vadim Yanitskiy89aa4692017-11-14 00:15:20 +0700110 self.phy_src.set_center_freq(self.rx_freq, 0)
111 self.phy_src.set_antenna(phy_rx_antenna, 0)
112 self.phy_src.set_samp_rate(phy_sample_rate)
113 self.phy_src.set_bandwidth(650e3, 0)
114 self.phy_src.set_gain(phy_rx_gain)
Piotr Krysik902f4eb2017-09-19 08:04:33 +0200115
Vadim Yanitskiyd222ee52017-12-03 23:49:09 +0700116 self.msg_to_tag_src = grgsm.msg_to_tag()
Piotr Krysik902f4eb2017-09-19 08:04:33 +0200117
Vadim Yanitskiyd222ee52017-12-03 23:49:09 +0700118 self.rotator_src = grgsm.controlled_rotator_cc(
119 self.calc_phase_inc(self.rx_freq))
Piotr Krysik902f4eb2017-09-19 08:04:33 +0200120
Vadim Yanitskiyd222ee52017-12-03 23:49:09 +0700121 self.lpf = filter.fir_filter_ccf(1, firdes.low_pass(
122 1, phy_sample_rate, 125e3, 5e3, firdes.WIN_HAMMING, 6.76))
123
124 self.gsm_receiver = grgsm.receiver(self.osr, ([0]), ([]))
Piotr Krysik902f4eb2017-09-19 08:04:33 +0200125
Vadim Yanitskiy89aa4692017-11-14 00:15:20 +0700126 self.ts_filter = grgsm.burst_timeslot_filter(0)
127 self.ts_filter.set_policy(grgsm.FILTER_POLICY_DROP_ALL)
Vadim Yanitskiy962e2d82017-10-17 09:24:55 +0700128
Piotr Krysik902f4eb2017-09-19 08:04:33 +0200129 # Connections
Vadim Yanitskiy89aa4692017-11-14 00:15:20 +0700130 self.connect(
131 (self.phy_src, 0),
Vadim Yanitskiyd222ee52017-12-03 23:49:09 +0700132 (self.msg_to_tag_src, 0))
Piotr Krysik902f4eb2017-09-19 08:04:33 +0200133
Vadim Yanitskiy89aa4692017-11-14 00:15:20 +0700134 self.connect(
Vadim Yanitskiyd222ee52017-12-03 23:49:09 +0700135 (self.msg_to_tag_src, 0),
136 (self.rotator_src, 0))
137
138 self.connect(
139 (self.rotator_src, 0),
140 (self.lpf, 0))
141
142 self.connect(
143 (self.lpf, 0),
Vadim Yanitskiy89aa4692017-11-14 00:15:20 +0700144 (self.gsm_receiver, 0))
145
146 self.msg_connect(
147 (self.gsm_receiver, 'C0'),
148 (self.ts_filter, 'in'))
149
150 self.msg_connect(
151 (self.ts_filter, 'out'),
152 (self.trx_burst_if, 'bursts'))
153
Vadim Yanitskiy962e2d82017-10-17 09:24:55 +0700154
Vadim Yanitskiy89aa4692017-11-14 00:15:20 +0700155 # TX Path Definition
156 self.phy_sink = uhd.usrp_sink(phy_args,
157 uhd.stream_args(cpu_format="fc32",
158 channels=range(1)), "packet_len")
Piotr Krysik902f4eb2017-09-19 08:04:33 +0200159
Vadim Yanitskiy63703bb2017-12-03 23:40:21 +0700160 self.phy_sink.set_clock_rate(26e6, uhd.ALL_MBOARDS)
Vadim Yanitskiy89aa4692017-11-14 00:15:20 +0700161 self.phy_sink.set_antenna(phy_tx_antenna, 0)
162 self.phy_sink.set_samp_rate(phy_sample_rate)
163 self.phy_sink.set_center_freq(self.tx_freq, 0)
164 self.phy_sink.set_gain(self.tx_gain)
165
166 self.tx_time_setter = grgsm.txtime_setter(
167 0xffffffff, 0, 0, 0, 0, 0,
Vadim Yanitskiy9dded9b2017-12-05 01:00:51 +0700168 self.delay_correction + self.GSM_UL_DL_SHIFT_uS * 1e-6)
Vadim Yanitskiy89aa4692017-11-14 00:15:20 +0700169
170 self.tx_burst_proc = grgsm.preprocess_tx_burst()
171
172 self.pdu_to_tagged_stream = blocks.pdu_to_tagged_stream(
173 blocks.byte_t, 'packet_len')
174
175 self.gmsk_mod = grgsm.gsm_gmsk_mod(
Vadim Yanitskiyd222ee52017-12-03 23:49:09 +0700176 BT = 0.3, pulse_duration = 4, sps = self.osr)
Vadim Yanitskiy89aa4692017-11-14 00:15:20 +0700177
178 self.burst_shaper = digital.burst_shaper_cc(
179 (firdes.window(firdes.WIN_HANN, 16, 0)),
180 0, 20, False, "packet_len")
181
Vadim Yanitskiyd222ee52017-12-03 23:49:09 +0700182 self.msg_to_tag_sink = grgsm.msg_to_tag()
183
184 self.rotator_sink = grgsm.controlled_rotator_cc(
185 -self.calc_phase_inc(self.tx_freq))
186
Vadim Yanitskiy89aa4692017-11-14 00:15:20 +0700187 # Connections
188 self.msg_connect(
189 (self.trx_burst_if, 'bursts'),
190 (self.tx_time_setter, 'bursts_in'))
191
192 self.msg_connect(
193 (self.tx_time_setter, 'bursts_out'),
194 (self.tx_burst_proc, 'bursts_in'))
195
196 self.msg_connect(
197 (self.tx_burst_proc, 'bursts_out'),
198 (self.pdu_to_tagged_stream, 'pdus'))
199
200 self.connect(
201 (self.pdu_to_tagged_stream, 0),
202 (self.gmsk_mod, 0))
203
204 self.connect(
205 (self.gmsk_mod, 0),
206 (self.burst_shaper, 0))
207
208 self.connect(
209 (self.burst_shaper, 0),
Vadim Yanitskiyd222ee52017-12-03 23:49:09 +0700210 (self.msg_to_tag_sink, 0))
211
212 self.connect(
213 (self.msg_to_tag_sink, 0),
214 (self.rotator_sink, 0))
215
216 self.connect(
217 (self.rotator_sink, 0),
Vadim Yanitskiy89aa4692017-11-14 00:15:20 +0700218 (self.phy_sink, 0))
219
220
221 # RX & TX synchronization
Vadim Yanitskiy4650fad2017-12-01 05:08:35 +0700222 self.bt_filter = grgsm.burst_type_filter([3])
Vadim Yanitskiy5cba7042017-12-04 01:51:00 +0700223 self.burst_to_fn_time = grgsm.burst_to_fn_time()
Vadim Yanitskiy89aa4692017-11-14 00:15:20 +0700224
225 # Connections
226 self.msg_connect(
227 (self.gsm_receiver, 'C0'),
228 (self.bt_filter, 'bursts_in'))
229
230 self.msg_connect(
231 (self.bt_filter, 'bursts_out'),
232 (self.burst_to_fn_time, 'bursts_in'))
233
234 self.msg_connect(
235 (self.burst_to_fn_time, 'fn_time_out'),
236 (self.tx_time_setter, 'fn_time'))
Piotr Krysik902f4eb2017-09-19 08:04:33 +0200237
Vadim Yanitskiyd222ee52017-12-03 23:49:09 +0700238
239 # AFC (Automatic Frequency Correction)
240 self.gsm_clck_ctrl = grgsm.clock_offset_control(
241 self.rx_freq, phy_sample_rate, osr = self.osr)
242
243 self.dict_toggle_sign = dict_toggle_sign()
244
245 # Connections
246 self.msg_connect(
247 (self.gsm_receiver, 'measurements'),
248 (self.gsm_clck_ctrl, 'measurements'))
249
250 self.msg_connect(
251 (self.gsm_clck_ctrl, 'ctrl'),
252 (self.msg_to_tag_src, 'msg'))
253
254 self.msg_connect(
255 (self.gsm_clck_ctrl, 'ctrl'),
256 (self.dict_toggle_sign, 'dict_in'))
257
258 self.msg_connect(
259 (self.dict_toggle_sign, 'dict_out'),
260 (self.msg_to_tag_sink, 'msg'))
Piotr Krysik902f4eb2017-09-19 08:04:33 +0200261
Vadim Yanitskiy0017a352018-03-03 23:37:30 +0700262
263 # Some UHD devices (such as UmTRX) do start the clock
264 # not from 0, so it's required to reset it manually.
265 # Resetting UHD source will also affect the sink.
266 self.phy_src.set_time_now(uhd.time_spec(0.0))
267
Piotr Krysik902f4eb2017-09-19 08:04:33 +0200268 def shutdown(self):
269 print("[i] Shutdown Radio interface")
270 self.stop()
271 self.wait()
272
Vadim Yanitskiyd222ee52017-12-03 23:49:09 +0700273 def calc_phase_inc(self, fc):
274 return self.ppm / 1.0e6 * 2 * pi * fc / self.sample_rate
275
Vadim Yanitskiy89aa4692017-11-14 00:15:20 +0700276 def set_rx_freq(self, fc):
277 self.phy_src.set_center_freq(fc, 0)
Vadim Yanitskiyd222ee52017-12-03 23:49:09 +0700278 self.rotator_src.set_phase_inc(self.calc_phase_inc(fc))
Vadim Yanitskiy89aa4692017-11-14 00:15:20 +0700279 self.rx_freq = fc
280
281 def set_tx_freq(self, fc):
282 self.phy_sink.set_center_freq(fc, 0)
Vadim Yanitskiyd222ee52017-12-03 23:49:09 +0700283 self.rotator_sink.set_phase_inc(-self.calc_phase_inc(fc))
Vadim Yanitskiy89aa4692017-11-14 00:15:20 +0700284 self.tx_freq = fc
Piotr Krysik902f4eb2017-09-19 08:04:33 +0200285
Vadim Yanitskiy01c6afd2017-10-19 01:14:24 +0700286 def set_rx_gain(self, gain):
Vadim Yanitskiy89aa4692017-11-14 00:15:20 +0700287 self.phy_src.set_gain(gain, 0)
Vadim Yanitskiy01c6afd2017-10-19 01:14:24 +0700288 self.rx_gain = gain
289
290 def set_tx_gain(self, gain):
Vadim Yanitskiy89aa4692017-11-14 00:15:20 +0700291 self.phy_sink.set_gain(gain, 0)
Vadim Yanitskiy01c6afd2017-10-19 01:14:24 +0700292 self.tx_gain = gain
Vadim Yanitskiy34266e72017-12-05 01:01:43 +0700293
294 def set_ta(self, ta):
295 print("[i] Setting TA value %d" % ta)
296 advance_time_sec = ta * self.GSM_SYM_PERIOD_uS * 1e-6
297 self.tx_time_setter.set_timing_advance(advance_time_sec)