blob: 1ded3370079b69a5bebd3c356408965398450ba7 [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>
8#
9# All Rights Reserved
10#
11# This program is free software; you can redistribute it and/or modify
12# it under the terms of the GNU General Public License as published by
13# the Free Software Foundation; either version 2 of the License, or
14# (at your option) any later version.
15#
16# This program is distributed in the hope that it will be useful,
17# but WITHOUT ANY WARRANTY; without even the implied warranty of
18# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
19# GNU General Public License for more details.
20#
21# You should have received a copy of the GNU General Public License along
22# with this program; if not, write to the Free Software Foundation, Inc.,
23# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
24
25import pmt
26import time
27import grgsm
28import osmosdr
29
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 Yanitskiy89aa4692017-11-14 00:15:20 +070042class burst_to_fn_time(gr.basic_block):
43 def __init__(self): # only default arguments here
44 gr.basic_block.__init__(
45 self,
46 name='Burst to fn_time',
47 in_sig=[],
48 out_sig=[]
49 )
50 self.message_port_register_in(pmt.intern("bursts_in"))
51 self.message_port_register_out(pmt.intern("fn_time_out"))
52 self.set_msg_handler(pmt.intern("bursts_in"), self.convert)
53
54 def convert(self, msg):
55 fn_time = pmt.dict_ref(pmt.car(msg),pmt.intern("fn_time"),pmt.PMT_NIL)
56 fn_time_msg = pmt.dict_add(pmt.make_dict(), pmt.intern("fn_time"), fn_time)
57 if pmt.to_python(fn_time) is not None:
58 self.message_port_pub(pmt.intern("fn_time_out"), fn_time_msg)
59
Vadim Yanitskiyd222ee52017-12-03 23:49:09 +070060class dict_toggle_sign(gr.basic_block):
61 def __init__(self): # only default arguments here
62 gr.basic_block.__init__(self,
63 name='Change sign of elts in dict',
64 in_sig=[],
65 out_sig=[]
66 )
67 self.message_port_register_in(pmt.intern("dict_in"))
68 self.message_port_register_out(pmt.intern("dict_out"))
69 self.set_msg_handler(pmt.intern("dict_in"), self.change_sign)
70
71 def change_sign(self, msg):
72 if pmt.is_dict(msg):
73 d = pmt.to_python(msg)
74 for key, value in d.items():
75 d[key] *= -1
76 self.message_port_pub(pmt.intern("dict_out"), pmt.to_pmt(d))
77
Vadim Yanitskiy873e44e2017-10-17 06:52:01 +070078class radio_if(gr.top_block):
Piotr Krysik902f4eb2017-09-19 08:04:33 +020079 # PHY specific variables
Vadim Yanitskiy89aa4692017-11-14 00:15:20 +070080 rx_freq = 935e6
81 tx_freq = 890e6
Vadim Yanitskiyd222ee52017-12-03 23:49:09 +070082 osr = 4
Piotr Krysik902f4eb2017-09-19 08:04:33 +020083
84 # Application state flags
85 trx_started = False
Vadim Yanitskiy89aa4692017-11-14 00:15:20 +070086
87 # GSM timings
88 delay_correction = 285.616e-6
89 ul_dl_shift = -(6.0/1625000*(156.25)*3)
Piotr Krysik902f4eb2017-09-19 08:04:33 +020090
Vadim Yanitskiy790b6f02017-10-17 11:47:36 +070091 def __init__(self, phy_args, phy_sample_rate,
92 phy_rx_gain, phy_tx_gain, phy_ppm,
93 phy_rx_antenna, phy_tx_antenna,
Vadim Yanitskiy5d68aa52017-10-17 11:14:48 +070094 trx_remote_addr, trx_base_port):
Piotr Krysik902f4eb2017-09-19 08:04:33 +020095
Vadim Yanitskiy790b6f02017-10-17 11:47:36 +070096 print("[i] Init Radio interface")
Piotr Krysik902f4eb2017-09-19 08:04:33 +020097
98 # PHY specific variables
Vadim Yanitskiyd222ee52017-12-03 23:49:09 +070099 self.sample_rate = phy_sample_rate
Vadim Yanitskiy790b6f02017-10-17 11:47:36 +0700100 self.rx_gain = phy_rx_gain
101 self.tx_gain = phy_tx_gain
Vadim Yanitskiyd222ee52017-12-03 23:49:09 +0700102 self.ppm = phy_ppm
Piotr Krysik902f4eb2017-09-19 08:04:33 +0200103
104 gr.top_block.__init__(self, "GR-GSM TRX")
Piotr Krysik902f4eb2017-09-19 08:04:33 +0200105
Vadim Yanitskiy89aa4692017-11-14 00:15:20 +0700106 # TRX Burst Interface
107 self.trx_burst_if = grgsm.trx_burst_if(
108 trx_remote_addr, str(trx_base_port))
Piotr Krysik902f4eb2017-09-19 08:04:33 +0200109
Piotr Krysik902f4eb2017-09-19 08:04:33 +0200110
Vadim Yanitskiy89aa4692017-11-14 00:15:20 +0700111 # RX path definition
112 self.phy_src = uhd.usrp_source(phy_args,
113 uhd.stream_args(cpu_format="fc32",
114 channels=range(1)))
115
Vadim Yanitskiy63703bb2017-12-03 23:40:21 +0700116 self.phy_src.set_clock_rate(26e6, uhd.ALL_MBOARDS)
Vadim Yanitskiy89aa4692017-11-14 00:15:20 +0700117 self.phy_src.set_center_freq(self.rx_freq, 0)
118 self.phy_src.set_antenna(phy_rx_antenna, 0)
119 self.phy_src.set_samp_rate(phy_sample_rate)
120 self.phy_src.set_bandwidth(650e3, 0)
121 self.phy_src.set_gain(phy_rx_gain)
Piotr Krysik902f4eb2017-09-19 08:04:33 +0200122
Vadim Yanitskiyd222ee52017-12-03 23:49:09 +0700123 self.msg_to_tag_src = grgsm.msg_to_tag()
Piotr Krysik902f4eb2017-09-19 08:04:33 +0200124
Vadim Yanitskiyd222ee52017-12-03 23:49:09 +0700125 self.rotator_src = grgsm.controlled_rotator_cc(
126 self.calc_phase_inc(self.rx_freq))
Piotr Krysik902f4eb2017-09-19 08:04:33 +0200127
Vadim Yanitskiyd222ee52017-12-03 23:49:09 +0700128 self.lpf = filter.fir_filter_ccf(1, firdes.low_pass(
129 1, phy_sample_rate, 125e3, 5e3, firdes.WIN_HAMMING, 6.76))
130
131 self.gsm_receiver = grgsm.receiver(self.osr, ([0]), ([]))
Piotr Krysik902f4eb2017-09-19 08:04:33 +0200132
Vadim Yanitskiy89aa4692017-11-14 00:15:20 +0700133 self.ts_filter = grgsm.burst_timeslot_filter(0)
134 self.ts_filter.set_policy(grgsm.FILTER_POLICY_DROP_ALL)
Vadim Yanitskiy962e2d82017-10-17 09:24:55 +0700135
Piotr Krysik902f4eb2017-09-19 08:04:33 +0200136 # Connections
Vadim Yanitskiy89aa4692017-11-14 00:15:20 +0700137 self.connect(
138 (self.phy_src, 0),
Vadim Yanitskiyd222ee52017-12-03 23:49:09 +0700139 (self.msg_to_tag_src, 0))
Piotr Krysik902f4eb2017-09-19 08:04:33 +0200140
Vadim Yanitskiy89aa4692017-11-14 00:15:20 +0700141 self.connect(
Vadim Yanitskiyd222ee52017-12-03 23:49:09 +0700142 (self.msg_to_tag_src, 0),
143 (self.rotator_src, 0))
144
145 self.connect(
146 (self.rotator_src, 0),
147 (self.lpf, 0))
148
149 self.connect(
150 (self.lpf, 0),
Vadim Yanitskiy89aa4692017-11-14 00:15:20 +0700151 (self.gsm_receiver, 0))
152
153 self.msg_connect(
154 (self.gsm_receiver, 'C0'),
155 (self.ts_filter, 'in'))
156
157 self.msg_connect(
158 (self.ts_filter, 'out'),
159 (self.trx_burst_if, 'bursts'))
160
Vadim Yanitskiy962e2d82017-10-17 09:24:55 +0700161
Vadim Yanitskiy89aa4692017-11-14 00:15:20 +0700162 # TX Path Definition
163 self.phy_sink = uhd.usrp_sink(phy_args,
164 uhd.stream_args(cpu_format="fc32",
165 channels=range(1)), "packet_len")
Piotr Krysik902f4eb2017-09-19 08:04:33 +0200166
Vadim Yanitskiy63703bb2017-12-03 23:40:21 +0700167 self.phy_sink.set_clock_rate(26e6, uhd.ALL_MBOARDS)
Vadim Yanitskiy89aa4692017-11-14 00:15:20 +0700168 self.phy_sink.set_antenna(phy_tx_antenna, 0)
169 self.phy_sink.set_samp_rate(phy_sample_rate)
170 self.phy_sink.set_center_freq(self.tx_freq, 0)
171 self.phy_sink.set_gain(self.tx_gain)
172
173 self.tx_time_setter = grgsm.txtime_setter(
174 0xffffffff, 0, 0, 0, 0, 0,
175 self.delay_correction + self.ul_dl_shift)
176
177 self.tx_burst_proc = grgsm.preprocess_tx_burst()
178
179 self.pdu_to_tagged_stream = blocks.pdu_to_tagged_stream(
180 blocks.byte_t, 'packet_len')
181
182 self.gmsk_mod = grgsm.gsm_gmsk_mod(
Vadim Yanitskiyd222ee52017-12-03 23:49:09 +0700183 BT = 0.3, pulse_duration = 4, sps = self.osr)
Vadim Yanitskiy89aa4692017-11-14 00:15:20 +0700184
185 self.burst_shaper = digital.burst_shaper_cc(
186 (firdes.window(firdes.WIN_HANN, 16, 0)),
187 0, 20, False, "packet_len")
188
Vadim Yanitskiyd222ee52017-12-03 23:49:09 +0700189 self.msg_to_tag_sink = grgsm.msg_to_tag()
190
191 self.rotator_sink = grgsm.controlled_rotator_cc(
192 -self.calc_phase_inc(self.tx_freq))
193
Vadim Yanitskiy89aa4692017-11-14 00:15:20 +0700194 # Connections
195 self.msg_connect(
196 (self.trx_burst_if, 'bursts'),
197 (self.tx_time_setter, 'bursts_in'))
198
199 self.msg_connect(
200 (self.tx_time_setter, 'bursts_out'),
201 (self.tx_burst_proc, 'bursts_in'))
202
203 self.msg_connect(
204 (self.tx_burst_proc, 'bursts_out'),
205 (self.pdu_to_tagged_stream, 'pdus'))
206
207 self.connect(
208 (self.pdu_to_tagged_stream, 0),
209 (self.gmsk_mod, 0))
210
211 self.connect(
212 (self.gmsk_mod, 0),
213 (self.burst_shaper, 0))
214
215 self.connect(
216 (self.burst_shaper, 0),
Vadim Yanitskiyd222ee52017-12-03 23:49:09 +0700217 (self.msg_to_tag_sink, 0))
218
219 self.connect(
220 (self.msg_to_tag_sink, 0),
221 (self.rotator_sink, 0))
222
223 self.connect(
224 (self.rotator_sink, 0),
Vadim Yanitskiy89aa4692017-11-14 00:15:20 +0700225 (self.phy_sink, 0))
226
227
228 # RX & TX synchronization
Vadim Yanitskiy4650fad2017-12-01 05:08:35 +0700229 self.bt_filter = grgsm.burst_type_filter([3])
Vadim Yanitskiy89aa4692017-11-14 00:15:20 +0700230 self.burst_to_fn_time = burst_to_fn_time()
231
232 # Connections
233 self.msg_connect(
234 (self.gsm_receiver, 'C0'),
235 (self.bt_filter, 'bursts_in'))
236
237 self.msg_connect(
238 (self.bt_filter, 'bursts_out'),
239 (self.burst_to_fn_time, 'bursts_in'))
240
241 self.msg_connect(
242 (self.burst_to_fn_time, 'fn_time_out'),
243 (self.tx_time_setter, 'fn_time'))
Piotr Krysik902f4eb2017-09-19 08:04:33 +0200244
Vadim Yanitskiyd222ee52017-12-03 23:49:09 +0700245
246 # AFC (Automatic Frequency Correction)
247 self.gsm_clck_ctrl = grgsm.clock_offset_control(
248 self.rx_freq, phy_sample_rate, osr = self.osr)
249
250 self.dict_toggle_sign = dict_toggle_sign()
251
252 # Connections
253 self.msg_connect(
254 (self.gsm_receiver, 'measurements'),
255 (self.gsm_clck_ctrl, 'measurements'))
256
257 self.msg_connect(
258 (self.gsm_clck_ctrl, 'ctrl'),
259 (self.msg_to_tag_src, 'msg'))
260
261 self.msg_connect(
262 (self.gsm_clck_ctrl, 'ctrl'),
263 (self.dict_toggle_sign, 'dict_in'))
264
265 self.msg_connect(
266 (self.dict_toggle_sign, 'dict_out'),
267 (self.msg_to_tag_sink, 'msg'))
268
Piotr Krysik902f4eb2017-09-19 08:04:33 +0200269 def shutdown(self):
270 print("[i] Shutdown Radio interface")
271 self.stop()
272 self.wait()
273
Vadim Yanitskiyd222ee52017-12-03 23:49:09 +0700274 def calc_phase_inc(self, fc):
275 return self.ppm / 1.0e6 * 2 * pi * fc / self.sample_rate
276
Vadim Yanitskiy89aa4692017-11-14 00:15:20 +0700277 def set_rx_freq(self, fc):
278 self.phy_src.set_center_freq(fc, 0)
Vadim Yanitskiyd222ee52017-12-03 23:49:09 +0700279 self.rotator_src.set_phase_inc(self.calc_phase_inc(fc))
Vadim Yanitskiy89aa4692017-11-14 00:15:20 +0700280 self.rx_freq = fc
281
282 def set_tx_freq(self, fc):
283 self.phy_sink.set_center_freq(fc, 0)
Vadim Yanitskiyd222ee52017-12-03 23:49:09 +0700284 self.rotator_sink.set_phase_inc(-self.calc_phase_inc(fc))
Vadim Yanitskiy89aa4692017-11-14 00:15:20 +0700285 self.tx_freq = fc
Piotr Krysik902f4eb2017-09-19 08:04:33 +0200286
Vadim Yanitskiy01c6afd2017-10-19 01:14:24 +0700287 def set_rx_gain(self, gain):
Vadim Yanitskiy89aa4692017-11-14 00:15:20 +0700288 self.phy_src.set_gain(gain, 0)
Vadim Yanitskiy01c6afd2017-10-19 01:14:24 +0700289 self.rx_gain = gain
290
291 def set_tx_gain(self, gain):
Vadim Yanitskiy89aa4692017-11-14 00:15:20 +0700292 self.phy_sink.set_gain(gain, 0)
Vadim Yanitskiy01c6afd2017-10-19 01:14:24 +0700293 self.tx_gain = gain