blob: e1c273ddfa79aaaa81449a649cdcb1768af5d3c2 [file] [log] [blame]
Piotr Krysik35582082015-09-02 21:49:12 +02001#!/usr/bin/env python2
Piotr Krysik6577ec22016-07-15 13:21:09 +02002# -*- coding: utf-8 -*-
Piotr Krysikea34c012016-10-02 18:53:43 +02003# @file
Piotr Krysika6268a52017-08-23 16:02:19 +02004# @author (C) 2014-2016 by Piotr Krysik <ptrkrysik@gmail.com>
Piotr Krysikea34c012016-10-02 18:53:43 +02005# @section LICENSE
6#
7# Gr-gsm is free software; you can redistribute it and/or modify
8# it under the terms of the GNU General Public License as published by
9# the Free Software Foundation; either version 3, or (at your option)
10# any later version.
11#
12# Gr-gsm is distributed in the hope that it will be useful,
13# but WITHOUT ANY WARRANTY; without even the implied warranty of
14# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15# GNU General Public License for more details.
16#
17# You should have received a copy of the GNU General Public License
18# along with gr-gsm; see the file COPYING. If not, write to
19# the Free Software Foundation, Inc., 51 Franklin Street,
20# Boston, MA 02110-1301, USA.
21#
22#
Piotr Krysik35582082015-09-02 21:49:12 +020023##################################################
24# GNU Radio Python Flow Graph
Piotr Krysik332e0b52016-02-13 18:37:32 +010025# Title: Gr-gsm Livemon
Piotr Krysik6577ec22016-07-15 13:21:09 +020026# Author: Piotr Krysik
27# Description: Interactive monitor of a single C0 channel with analysis performed by Wireshark (command to run wireshark: sudo wireshark -k -f udp -Y gsmtap -i lo)
Piotr Krysik688c81e2017-08-26 13:32:51 +020028# Generated: Sat Aug 26 13:26:35 2017
Piotr Krysik35582082015-09-02 21:49:12 +020029##################################################
30
31if __name__ == '__main__':
32 import ctypes
33 import sys
34 if sys.platform.startswith('linux'):
35 try:
36 x11 = ctypes.cdll.LoadLibrary('libX11.so')
37 x11.XInitThreads()
38 except:
39 print "Warning: failed to XInitThreads()"
40
41from PyQt4 import Qt
42from gnuradio import blocks
43from gnuradio import eng_notation
44from gnuradio import gr
45from gnuradio import qtgui
46from gnuradio.eng_option import eng_option
47from gnuradio.filter import firdes
48from gnuradio.qtgui import Range, RangeWidget
49from math import pi
50from optparse import OptionParser
51import grgsm
52import osmosdr
53import pmt
54import sip
55import sys
56import time
57
58
Piotr Krysik332e0b52016-02-13 18:37:32 +010059class grgsm_livemon(gr.top_block, Qt.QWidget):
Piotr Krysik35582082015-09-02 21:49:12 +020060
Piotr Krysik688c81e2017-08-26 13:32:51 +020061 def __init__(self, args="", collector="localhost", collectorport="4729", fc=941.8e6, gain=30, osr=4, ppm=0, samp_rate=2000000.052982, serverport="4729", shiftoff=400e3):
Piotr Krysik332e0b52016-02-13 18:37:32 +010062 gr.top_block.__init__(self, "Gr-gsm Livemon")
Piotr Krysik35582082015-09-02 21:49:12 +020063 Qt.QWidget.__init__(self)
Piotr Krysik332e0b52016-02-13 18:37:32 +010064 self.setWindowTitle("Gr-gsm Livemon")
Piotr Krysik35582082015-09-02 21:49:12 +020065 try:
Piotr Krysik6577ec22016-07-15 13:21:09 +020066 self.setWindowIcon(Qt.QIcon.fromTheme('gnuradio-grc'))
Piotr Krysik35582082015-09-02 21:49:12 +020067 except:
Piotr Krysik6577ec22016-07-15 13:21:09 +020068 pass
Piotr Krysik35582082015-09-02 21:49:12 +020069 self.top_scroll_layout = Qt.QVBoxLayout()
70 self.setLayout(self.top_scroll_layout)
71 self.top_scroll = Qt.QScrollArea()
72 self.top_scroll.setFrameStyle(Qt.QFrame.NoFrame)
73 self.top_scroll_layout.addWidget(self.top_scroll)
74 self.top_scroll.setWidgetResizable(True)
75 self.top_widget = Qt.QWidget()
76 self.top_scroll.setWidget(self.top_widget)
77 self.top_layout = Qt.QVBoxLayout(self.top_widget)
78 self.top_grid_layout = Qt.QGridLayout()
79 self.top_layout.addLayout(self.top_grid_layout)
80
Piotr Krysik332e0b52016-02-13 18:37:32 +010081 self.settings = Qt.QSettings("GNU Radio", "grgsm_livemon")
Piotr Krysik35582082015-09-02 21:49:12 +020082 self.restoreGeometry(self.settings.value("geometry").toByteArray())
83
84 ##################################################
85 # Parameters
86 ##################################################
Piotr Krysik6577ec22016-07-15 13:21:09 +020087 self.args = args
Piotr Krysik688c81e2017-08-26 13:32:51 +020088 self.collector = collector
89 self.collectorport = collectorport
Piotr Krysik85ec6ae2017-08-26 12:24:34 +020090 self.fc = fc
Piotr Krysik35582082015-09-02 21:49:12 +020091 self.gain = gain
Piotr Krysik82638ab2017-07-23 19:14:13 +020092 self.osr = osr
Piotr Krysik35582082015-09-02 21:49:12 +020093 self.ppm = ppm
94 self.samp_rate = samp_rate
Piotr Krysik50c3e362017-08-26 12:59:15 +020095 self.serverport = serverport
Piotr Krysik688c81e2017-08-26 13:32:51 +020096 self.shiftoff = shiftoff
Piotr Krysik35582082015-09-02 21:49:12 +020097
98 ##################################################
99 # Variables
100 ##################################################
101 self.ppm_slider = ppm_slider = ppm
102 self.g_slider = g_slider = gain
103 self.fc_slider = fc_slider = fc
104
105 ##################################################
106 # Blocks
107 ##################################################
Piotr Krysik93ba6bc2017-01-23 21:33:09 +0100108 self._ppm_slider_range = Range(-150, 150, 0.1, ppm, 100)
Piotr Krysik35582082015-09-02 21:49:12 +0200109 self._ppm_slider_win = RangeWidget(self._ppm_slider_range, self.set_ppm_slider, "PPM Offset", "counter", float)
110 self.top_layout.addWidget(self._ppm_slider_win)
111 self._g_slider_range = Range(0, 50, 0.5, gain, 100)
112 self._g_slider_win = RangeWidget(self._g_slider_range, self.set_g_slider, "Gain", "counter", float)
113 self.top_layout.addWidget(self._g_slider_win)
Piotr Krysik93ba6bc2017-01-23 21:33:09 +0100114 self._fc_slider_range = Range(800e6, 1990e6, 2e5, fc, 100)
Piotr Krysik35582082015-09-02 21:49:12 +0200115 self._fc_slider_win = RangeWidget(self._fc_slider_range, self.set_fc_slider, "Frequency", "counter_slider", float)
116 self.top_layout.addWidget(self._fc_slider_win)
Piotr Krysik7185b662016-02-14 20:24:54 +0100117 self.rtlsdr_source_0 = osmosdr.source( args="numchan=" + str(1) + " " + args )
Piotr Krysik35582082015-09-02 21:49:12 +0200118 self.rtlsdr_source_0.set_sample_rate(samp_rate)
119 self.rtlsdr_source_0.set_center_freq(fc_slider-shiftoff, 0)
120 self.rtlsdr_source_0.set_freq_corr(ppm_slider, 0)
121 self.rtlsdr_source_0.set_dc_offset_mode(2, 0)
122 self.rtlsdr_source_0.set_iq_balance_mode(2, 0)
123 self.rtlsdr_source_0.set_gain_mode(False, 0)
124 self.rtlsdr_source_0.set_gain(g_slider, 0)
125 self.rtlsdr_source_0.set_if_gain(20, 0)
126 self.rtlsdr_source_0.set_bb_gain(20, 0)
127 self.rtlsdr_source_0.set_antenna("", 0)
128 self.rtlsdr_source_0.set_bandwidth(250e3+abs(shiftoff), 0)
129
130 self.qtgui_freq_sink_x_0 = qtgui.freq_sink_c(
131 1024, #size
132 firdes.WIN_BLACKMAN_hARRIS, #wintype
133 fc_slider, #fc
134 samp_rate, #bw
135 "", #name
136 1 #number of inputs
137 )
138 self.qtgui_freq_sink_x_0.set_update_time(0.10)
139 self.qtgui_freq_sink_x_0.set_y_axis(-140, 10)
Piotr Krysik6577ec22016-07-15 13:21:09 +0200140 self.qtgui_freq_sink_x_0.set_trigger_mode(qtgui.TRIG_MODE_FREE, 0.0, 0, "")
141 self.qtgui_freq_sink_x_0.enable_autoscale(False)
142 self.qtgui_freq_sink_x_0.enable_grid(False)
143 self.qtgui_freq_sink_x_0.set_fft_average(1.0)
144 self.qtgui_freq_sink_x_0.enable_control_panel(False)
145
146 if not True:
147 self.qtgui_freq_sink_x_0.disable_legend()
148
149 if "complex" == "float" or "complex" == "msg_float":
150 self.qtgui_freq_sink_x_0.set_plot_pos_half(not True)
151
152 labels = ["", "", "", "", "",
153 "", "", "", "", ""]
154 widths = [1, 1, 1, 1, 1,
155 1, 1, 1, 1, 1]
156 colors = ["blue", "red", "green", "black", "cyan",
157 "magenta", "yellow", "dark red", "dark green", "dark blue"]
158 alphas = [1.0, 1.0, 1.0, 1.0, 1.0,
159 1.0, 1.0, 1.0, 1.0, 1.0]
160 for i in xrange(1):
161 if len(labels[i]) == 0:
162 self.qtgui_freq_sink_x_0.set_line_label(i, "Data {0}".format(i))
163 else:
164 self.qtgui_freq_sink_x_0.set_line_label(i, labels[i])
165 self.qtgui_freq_sink_x_0.set_line_width(i, widths[i])
166 self.qtgui_freq_sink_x_0.set_line_color(i, colors[i])
167 self.qtgui_freq_sink_x_0.set_line_alpha(i, alphas[i])
Piotr Krysik35582082015-09-02 21:49:12 +0200168
169 self._qtgui_freq_sink_x_0_win = sip.wrapinstance(self.qtgui_freq_sink_x_0.pyqwidget(), Qt.QWidget)
170 self.top_layout.addWidget(self._qtgui_freq_sink_x_0_win)
Piotr Krysik6577ec22016-07-15 13:21:09 +0200171 self.gsm_sdcch8_demapper_0 = grgsm.gsm_sdcch8_demapper(
172 timeslot_nr=1,
173 )
174 self.gsm_receiver_0 = grgsm.receiver(4, ([0]), ([]), False)
175 self.gsm_message_printer_1 = grgsm.message_printer(pmt.intern(""), False,
176 False, False)
Piotr Krysik35582082015-09-02 21:49:12 +0200177 self.gsm_input_0 = grgsm.gsm_input(
Piotr Krysik93ba6bc2017-01-23 21:33:09 +0100178 ppm=ppm-int(ppm),
Piotr Krysik35582082015-09-02 21:49:12 +0200179 osr=4,
180 fc=fc,
181 samp_rate_in=samp_rate,
182 )
183 self.gsm_decryption_0 = grgsm.decryption(([]), 1)
184 self.gsm_control_channels_decoder_0_0 = grgsm.control_channels_decoder()
185 self.gsm_control_channels_decoder_0 = grgsm.control_channels_decoder()
Piotr Krysikfe538eb2016-07-18 18:14:49 +0200186 self.gsm_clock_offset_control_0 = grgsm.clock_offset_control(fc-shiftoff, samp_rate, osr)
Piotr Krysik82638ab2017-07-23 19:14:13 +0200187 self.gsm_bcch_ccch_sdcch4_demapper_0 = grgsm.gsm_bcch_ccch_sdcch4_demapper(
Piotr Krysik6577ec22016-07-15 13:21:09 +0200188 timeslot_nr=0,
189 )
Piotr Krysik688c81e2017-08-26 13:32:51 +0200190 self.blocks_socket_pdu_0_1 = blocks.socket_pdu("UDP_CLIENT", collector, collectorport, 1500, False)
Piotr Krysik50c3e362017-08-26 12:59:15 +0200191 self.blocks_socket_pdu_0_0 = blocks.socket_pdu("UDP_SERVER", "127.0.0.1", serverport, 10000, False)
Piotr Krysik35582082015-09-02 21:49:12 +0200192 self.blocks_rotator_cc_0 = blocks.rotator_cc(-2*pi*shiftoff/samp_rate)
193
194 ##################################################
195 # Connections
196 ##################################################
Piotr Krysik50c3e362017-08-26 12:59:15 +0200197 self.msg_connect((self.blocks_socket_pdu_0_0, 'pdus'), (self.gsm_message_printer_1, 'msgs'))
Piotr Krysik82638ab2017-07-23 19:14:13 +0200198 self.msg_connect((self.gsm_bcch_ccch_sdcch4_demapper_0, 'bursts'), (self.gsm_control_channels_decoder_0, 'bursts'))
Piotr Krysik6577ec22016-07-15 13:21:09 +0200199 self.msg_connect((self.gsm_clock_offset_control_0, 'ctrl'), (self.gsm_input_0, 'ctrl_in'))
Piotr Krysik688c81e2017-08-26 13:32:51 +0200200 self.msg_connect((self.gsm_control_channels_decoder_0, 'msgs'), (self.blocks_socket_pdu_0_1, 'pdus'))
201 self.msg_connect((self.gsm_control_channels_decoder_0_0, 'msgs'), (self.blocks_socket_pdu_0_1, 'pdus'))
Piotr Krysik35582082015-09-02 21:49:12 +0200202 self.msg_connect((self.gsm_decryption_0, 'bursts'), (self.gsm_control_channels_decoder_0_0, 'bursts'))
Piotr Krysik82638ab2017-07-23 19:14:13 +0200203 self.msg_connect((self.gsm_receiver_0, 'C0'), (self.gsm_bcch_ccch_sdcch4_demapper_0, 'bursts'))
Piotr Krysik35582082015-09-02 21:49:12 +0200204 self.msg_connect((self.gsm_receiver_0, 'measurements'), (self.gsm_clock_offset_control_0, 'measurements'))
205 self.msg_connect((self.gsm_receiver_0, 'C0'), (self.gsm_sdcch8_demapper_0, 'bursts'))
206 self.msg_connect((self.gsm_sdcch8_demapper_0, 'bursts'), (self.gsm_decryption_0, 'bursts'))
207 self.connect((self.blocks_rotator_cc_0, 0), (self.gsm_input_0, 0))
208 self.connect((self.blocks_rotator_cc_0, 0), (self.qtgui_freq_sink_x_0, 0))
209 self.connect((self.gsm_input_0, 0), (self.gsm_receiver_0, 0))
210 self.connect((self.rtlsdr_source_0, 0), (self.blocks_rotator_cc_0, 0))
211
212 def closeEvent(self, event):
Piotr Krysik332e0b52016-02-13 18:37:32 +0100213 self.settings = Qt.QSettings("GNU Radio", "grgsm_livemon")
Piotr Krysik35582082015-09-02 21:49:12 +0200214 self.settings.setValue("geometry", self.saveGeometry())
215 event.accept()
216
Piotr Krysik6577ec22016-07-15 13:21:09 +0200217
218 def get_args(self):
219 return self.args
220
221 def set_args(self, args):
222 self.args = args
223
Piotr Krysik688c81e2017-08-26 13:32:51 +0200224 def get_collector(self):
225 return self.collector
226
227 def set_collector(self, collector):
228 self.collector = collector
229
230 def get_collectorport(self):
231 return self.collectorport
232
233 def set_collectorport(self, collectorport):
234 self.collectorport = collectorport
235
Piotr Krysik85ec6ae2017-08-26 12:24:34 +0200236 def get_fc(self):
237 return self.fc
238
239 def set_fc(self, fc):
240 self.fc = fc
241 self.set_fc_slider(self.fc)
242 self.gsm_input_0.set_fc(self.fc)
243
Piotr Krysik35582082015-09-02 21:49:12 +0200244 def get_gain(self):
245 return self.gain
246
247 def set_gain(self, gain):
248 self.gain = gain
249 self.set_g_slider(self.gain)
250
Piotr Krysik82638ab2017-07-23 19:14:13 +0200251 def get_osr(self):
252 return self.osr
253
254 def set_osr(self, osr):
255 self.osr = osr
256
Piotr Krysik35582082015-09-02 21:49:12 +0200257 def get_ppm(self):
258 return self.ppm
259
260 def set_ppm(self, ppm):
261 self.ppm = ppm
262 self.set_ppm_slider(self.ppm)
Piotr Krysik93ba6bc2017-01-23 21:33:09 +0100263 self.gsm_input_0.set_ppm(self.ppm-int(self.ppm))
Piotr Krysik35582082015-09-02 21:49:12 +0200264
265 def get_samp_rate(self):
266 return self.samp_rate
267
268 def set_samp_rate(self, samp_rate):
269 self.samp_rate = samp_rate
Piotr Krysik82638ab2017-07-23 19:14:13 +0200270 self.blocks_rotator_cc_0.set_phase_inc(-2*pi*self.shiftoff/self.samp_rate)
Piotr Krysikfe538eb2016-07-18 18:14:49 +0200271 self.gsm_input_0.set_samp_rate_in(self.samp_rate)
Piotr Krysik35582082015-09-02 21:49:12 +0200272 self.qtgui_freq_sink_x_0.set_frequency_range(self.fc_slider, self.samp_rate)
273 self.rtlsdr_source_0.set_sample_rate(self.samp_rate)
274
Piotr Krysik688c81e2017-08-26 13:32:51 +0200275 def get_serverport(self):
276 return self.serverport
277
278 def set_serverport(self, serverport):
279 self.serverport = serverport
280
Piotr Krysik35582082015-09-02 21:49:12 +0200281 def get_shiftoff(self):
282 return self.shiftoff
283
284 def set_shiftoff(self, shiftoff):
285 self.shiftoff = shiftoff
Piotr Krysik82638ab2017-07-23 19:14:13 +0200286 self.blocks_rotator_cc_0.set_phase_inc(-2*pi*self.shiftoff/self.samp_rate)
Piotr Krysik35582082015-09-02 21:49:12 +0200287 self.rtlsdr_source_0.set_center_freq(self.fc_slider-self.shiftoff, 0)
288 self.rtlsdr_source_0.set_bandwidth(250e3+abs(self.shiftoff), 0)
Piotr Krysikfe538eb2016-07-18 18:14:49 +0200289
Piotr Krysik35582082015-09-02 21:49:12 +0200290 def get_ppm_slider(self):
291 return self.ppm_slider
292
293 def set_ppm_slider(self, ppm_slider):
294 self.ppm_slider = ppm_slider
295 self.rtlsdr_source_0.set_freq_corr(self.ppm_slider, 0)
296
297 def get_g_slider(self):
298 return self.g_slider
299
300 def set_g_slider(self, g_slider):
301 self.g_slider = g_slider
302 self.rtlsdr_source_0.set_gain(self.g_slider, 0)
303
304 def get_fc_slider(self):
305 return self.fc_slider
306
307 def set_fc_slider(self, fc_slider):
308 self.fc_slider = fc_slider
309 self.qtgui_freq_sink_x_0.set_frequency_range(self.fc_slider, self.samp_rate)
310 self.rtlsdr_source_0.set_center_freq(self.fc_slider-self.shiftoff, 0)
311
312
Piotr Krysik6577ec22016-07-15 13:21:09 +0200313def argument_parser():
Piotr Krysik35582082015-09-02 21:49:12 +0200314 parser = OptionParser(option_class=eng_option, usage="%prog: [options]")
Piotr Krysik6577ec22016-07-15 13:21:09 +0200315 parser.add_option(
316 "", "--args", dest="args", type="string", default="",
317 help="Set Device Arguments [default=%default]")
318 parser.add_option(
Piotr Krysik688c81e2017-08-26 13:32:51 +0200319 "", "--collector", dest="collector", type="string", default="localhost",
320 help="Set IP or DNS name of collector point [default=%default]")
321 parser.add_option(
322 "", "--collectorport", dest="collectorport", type="string", default="4729",
323 help="Set UDP port number of collector [default=%default]")
324 parser.add_option(
Piotr Krysik85ec6ae2017-08-26 12:24:34 +0200325 "-f", "--fc", dest="fc", type="eng_float", default=eng_notation.num_to_str(941.8e6),
326 help="Set GSM channel's central frequency [default=%default]")
327 parser.add_option(
Piotr Krysik6577ec22016-07-15 13:21:09 +0200328 "-g", "--gain", dest="gain", type="eng_float", default=eng_notation.num_to_str(30),
Piotr Krysik35582082015-09-02 21:49:12 +0200329 help="Set gain [default=%default]")
Piotr Krysik6577ec22016-07-15 13:21:09 +0200330 parser.add_option(
Piotr Krysik82638ab2017-07-23 19:14:13 +0200331 "", "--osr", dest="osr", type="intx", default=4,
332 help="Set OverSampling Ratio [default=%default]")
333 parser.add_option(
Piotr Krysik93ba6bc2017-01-23 21:33:09 +0100334 "-p", "--ppm", dest="ppm", type="eng_float", default=eng_notation.num_to_str(0),
Piotr Krysik35582082015-09-02 21:49:12 +0200335 help="Set ppm [default=%default]")
Piotr Krysik6577ec22016-07-15 13:21:09 +0200336 parser.add_option(
337 "-s", "--samp-rate", dest="samp_rate", type="eng_float", default=eng_notation.num_to_str(2000000.052982),
Piotr Krysik35582082015-09-02 21:49:12 +0200338 help="Set samp_rate [default=%default]")
Piotr Krysik6577ec22016-07-15 13:21:09 +0200339 parser.add_option(
Piotr Krysik50c3e362017-08-26 12:59:15 +0200340 "", "--serverport", dest="serverport", type="string", default="4729",
341 help="Set UDP server listening port [default=%default]")
Piotr Krysik688c81e2017-08-26 13:32:51 +0200342 parser.add_option(
343 "-o", "--shiftoff", dest="shiftoff", type="eng_float", default=eng_notation.num_to_str(400e3),
344 help="Set Frequency Shiftoff [default=%default]")
Piotr Krysik6577ec22016-07-15 13:21:09 +0200345 return parser
346
347
348def main(top_block_cls=grgsm_livemon, options=None):
349 if options is None:
350 options, _ = argument_parser().parse_args()
351
352 from distutils.version import StrictVersion
353 if StrictVersion(Qt.qVersion()) >= StrictVersion("4.5.0"):
354 style = gr.prefs().get_string('qtgui', 'style', 'raster')
355 Qt.QApplication.setGraphicsSystem(style)
Piotr Krysik35582082015-09-02 21:49:12 +0200356 qapp = Qt.QApplication(sys.argv)
Piotr Krysik6577ec22016-07-15 13:21:09 +0200357
Piotr Krysik688c81e2017-08-26 13:32:51 +0200358 tb = top_block_cls(args=options.args, collector=options.collector, collectorport=options.collectorport, fc=options.fc, gain=options.gain, osr=options.osr, ppm=options.ppm, samp_rate=options.samp_rate, serverport=options.serverport, shiftoff=options.shiftoff)
Piotr Krysik35582082015-09-02 21:49:12 +0200359 tb.start()
360 tb.show()
361
362 def quitting():
363 tb.stop()
364 tb.wait()
365 qapp.connect(qapp, Qt.SIGNAL("aboutToQuit()"), quitting)
366 qapp.exec_()
Piotr Krysik6577ec22016-07-15 13:21:09 +0200367
368
369if __name__ == '__main__':
370 main()