blob: 389e2a00d853c74c8a9f71a0f4c29cf1e28a359d [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 Krysik82638ab2017-07-23 19:14:13 +020028# Generated: Sun Jul 23 19:07:48 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 Krysik85ec6ae2017-08-26 12:24:34 +020061 def __init__(self, args="", fc=941.8e6, gain=30, osr=4, ppm=0, samp_rate=2000000.052982, shiftoff=400e3, collector="localhost", collectorport="4729"):
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 Krysik85ec6ae2017-08-26 12:24:34 +020088 self.fc = fc
Piotr Krysik35582082015-09-02 21:49:12 +020089 self.gain = gain
Piotr Krysik82638ab2017-07-23 19:14:13 +020090 self.osr = osr
Piotr Krysik35582082015-09-02 21:49:12 +020091 self.ppm = ppm
92 self.samp_rate = samp_rate
93 self.shiftoff = shiftoff
Piotr Krysik85ec6ae2017-08-26 12:24:34 +020094 self.collector = collector
95 self.collectorport = collectorport
Piotr Krysik35582082015-09-02 21:49:12 +020096
97 ##################################################
98 # Variables
99 ##################################################
100 self.ppm_slider = ppm_slider = ppm
101 self.g_slider = g_slider = gain
102 self.fc_slider = fc_slider = fc
103
104 ##################################################
105 # Blocks
106 ##################################################
Piotr Krysik93ba6bc2017-01-23 21:33:09 +0100107 self._ppm_slider_range = Range(-150, 150, 0.1, ppm, 100)
Piotr Krysik35582082015-09-02 21:49:12 +0200108 self._ppm_slider_win = RangeWidget(self._ppm_slider_range, self.set_ppm_slider, "PPM Offset", "counter", float)
109 self.top_layout.addWidget(self._ppm_slider_win)
110 self._g_slider_range = Range(0, 50, 0.5, gain, 100)
111 self._g_slider_win = RangeWidget(self._g_slider_range, self.set_g_slider, "Gain", "counter", float)
112 self.top_layout.addWidget(self._g_slider_win)
Piotr Krysik93ba6bc2017-01-23 21:33:09 +0100113 self._fc_slider_range = Range(800e6, 1990e6, 2e5, fc, 100)
Piotr Krysik35582082015-09-02 21:49:12 +0200114 self._fc_slider_win = RangeWidget(self._fc_slider_range, self.set_fc_slider, "Frequency", "counter_slider", float)
115 self.top_layout.addWidget(self._fc_slider_win)
Piotr Krysik7185b662016-02-14 20:24:54 +0100116 self.rtlsdr_source_0 = osmosdr.source( args="numchan=" + str(1) + " " + args )
Piotr Krysik35582082015-09-02 21:49:12 +0200117 self.rtlsdr_source_0.set_sample_rate(samp_rate)
118 self.rtlsdr_source_0.set_center_freq(fc_slider-shiftoff, 0)
119 self.rtlsdr_source_0.set_freq_corr(ppm_slider, 0)
120 self.rtlsdr_source_0.set_dc_offset_mode(2, 0)
121 self.rtlsdr_source_0.set_iq_balance_mode(2, 0)
122 self.rtlsdr_source_0.set_gain_mode(False, 0)
123 self.rtlsdr_source_0.set_gain(g_slider, 0)
124 self.rtlsdr_source_0.set_if_gain(20, 0)
125 self.rtlsdr_source_0.set_bb_gain(20, 0)
126 self.rtlsdr_source_0.set_antenna("", 0)
127 self.rtlsdr_source_0.set_bandwidth(250e3+abs(shiftoff), 0)
128
129 self.qtgui_freq_sink_x_0 = qtgui.freq_sink_c(
130 1024, #size
131 firdes.WIN_BLACKMAN_hARRIS, #wintype
132 fc_slider, #fc
133 samp_rate, #bw
134 "", #name
135 1 #number of inputs
136 )
137 self.qtgui_freq_sink_x_0.set_update_time(0.10)
138 self.qtgui_freq_sink_x_0.set_y_axis(-140, 10)
Piotr Krysik6577ec22016-07-15 13:21:09 +0200139 self.qtgui_freq_sink_x_0.set_trigger_mode(qtgui.TRIG_MODE_FREE, 0.0, 0, "")
140 self.qtgui_freq_sink_x_0.enable_autoscale(False)
141 self.qtgui_freq_sink_x_0.enable_grid(False)
142 self.qtgui_freq_sink_x_0.set_fft_average(1.0)
143 self.qtgui_freq_sink_x_0.enable_control_panel(False)
144
145 if not True:
146 self.qtgui_freq_sink_x_0.disable_legend()
147
148 if "complex" == "float" or "complex" == "msg_float":
149 self.qtgui_freq_sink_x_0.set_plot_pos_half(not True)
150
151 labels = ["", "", "", "", "",
152 "", "", "", "", ""]
153 widths = [1, 1, 1, 1, 1,
154 1, 1, 1, 1, 1]
155 colors = ["blue", "red", "green", "black", "cyan",
156 "magenta", "yellow", "dark red", "dark green", "dark blue"]
157 alphas = [1.0, 1.0, 1.0, 1.0, 1.0,
158 1.0, 1.0, 1.0, 1.0, 1.0]
159 for i in xrange(1):
160 if len(labels[i]) == 0:
161 self.qtgui_freq_sink_x_0.set_line_label(i, "Data {0}".format(i))
162 else:
163 self.qtgui_freq_sink_x_0.set_line_label(i, labels[i])
164 self.qtgui_freq_sink_x_0.set_line_width(i, widths[i])
165 self.qtgui_freq_sink_x_0.set_line_color(i, colors[i])
166 self.qtgui_freq_sink_x_0.set_line_alpha(i, alphas[i])
Piotr Krysik35582082015-09-02 21:49:12 +0200167
168 self._qtgui_freq_sink_x_0_win = sip.wrapinstance(self.qtgui_freq_sink_x_0.pyqwidget(), Qt.QWidget)
169 self.top_layout.addWidget(self._qtgui_freq_sink_x_0_win)
Piotr Krysik6577ec22016-07-15 13:21:09 +0200170 self.gsm_sdcch8_demapper_0 = grgsm.gsm_sdcch8_demapper(
171 timeslot_nr=1,
172 )
173 self.gsm_receiver_0 = grgsm.receiver(4, ([0]), ([]), False)
174 self.gsm_message_printer_1 = grgsm.message_printer(pmt.intern(""), False,
175 False, False)
Piotr Krysik35582082015-09-02 21:49:12 +0200176 self.gsm_input_0 = grgsm.gsm_input(
Piotr Krysik93ba6bc2017-01-23 21:33:09 +0100177 ppm=ppm-int(ppm),
Piotr Krysik35582082015-09-02 21:49:12 +0200178 osr=4,
179 fc=fc,
180 samp_rate_in=samp_rate,
181 )
182 self.gsm_decryption_0 = grgsm.decryption(([]), 1)
183 self.gsm_control_channels_decoder_0_0 = grgsm.control_channels_decoder()
184 self.gsm_control_channels_decoder_0 = grgsm.control_channels_decoder()
Piotr Krysikfe538eb2016-07-18 18:14:49 +0200185 self.gsm_clock_offset_control_0 = grgsm.clock_offset_control(fc-shiftoff, samp_rate, osr)
Piotr Krysik82638ab2017-07-23 19:14:13 +0200186 self.gsm_bcch_ccch_sdcch4_demapper_0 = grgsm.gsm_bcch_ccch_sdcch4_demapper(
Piotr Krysik6577ec22016-07-15 13:21:09 +0200187 timeslot_nr=0,
188 )
189 self.blocks_socket_pdu_0_0 = blocks.socket_pdu("UDP_SERVER", "127.0.0.1", "4729", 10000, False)
Piotr Krysik85ec6ae2017-08-26 12:24:34 +0200190 self.blocks_socket_pdu_0 = blocks.socket_pdu("UDP_CLIENT", collector, collectorport, 1500, False)
Piotr Krysik35582082015-09-02 21:49:12 +0200191 self.blocks_rotator_cc_0 = blocks.rotator_cc(-2*pi*shiftoff/samp_rate)
192
193 ##################################################
194 # Connections
195 ##################################################
Piotr Krysik82638ab2017-07-23 19:14:13 +0200196 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 +0200197 self.msg_connect((self.gsm_clock_offset_control_0, 'ctrl'), (self.gsm_input_0, 'ctrl_in'))
Piotr Krysik35582082015-09-02 21:49:12 +0200198 self.msg_connect((self.gsm_control_channels_decoder_0, 'msgs'), (self.blocks_socket_pdu_0, 'pdus'))
199 self.msg_connect((self.gsm_control_channels_decoder_0, 'msgs'), (self.gsm_message_printer_1, 'msgs'))
200 self.msg_connect((self.gsm_control_channels_decoder_0_0, 'msgs'), (self.blocks_socket_pdu_0, 'pdus'))
201 self.msg_connect((self.gsm_control_channels_decoder_0_0, 'msgs'), (self.gsm_message_printer_1, 'msgs'))
202 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 Krysik85ec6ae2017-08-26 12:24:34 +0200224 def get_fc(self):
225 return self.fc
226
227 def set_fc(self, fc):
228 self.fc = fc
229 self.set_fc_slider(self.fc)
230 self.gsm_input_0.set_fc(self.fc)
231
Piotr Krysik35582082015-09-02 21:49:12 +0200232 def get_gain(self):
233 return self.gain
234
235 def set_gain(self, gain):
236 self.gain = gain
237 self.set_g_slider(self.gain)
238
Piotr Krysik82638ab2017-07-23 19:14:13 +0200239 def get_osr(self):
240 return self.osr
241
242 def set_osr(self, osr):
243 self.osr = osr
244
Piotr Krysik35582082015-09-02 21:49:12 +0200245 def get_ppm(self):
246 return self.ppm
247
248 def set_ppm(self, ppm):
249 self.ppm = ppm
250 self.set_ppm_slider(self.ppm)
Piotr Krysik93ba6bc2017-01-23 21:33:09 +0100251 self.gsm_input_0.set_ppm(self.ppm-int(self.ppm))
Piotr Krysik35582082015-09-02 21:49:12 +0200252
253 def get_samp_rate(self):
254 return self.samp_rate
255
256 def set_samp_rate(self, samp_rate):
257 self.samp_rate = samp_rate
Piotr Krysik82638ab2017-07-23 19:14:13 +0200258 self.blocks_rotator_cc_0.set_phase_inc(-2*pi*self.shiftoff/self.samp_rate)
Piotr Krysikfe538eb2016-07-18 18:14:49 +0200259 self.gsm_input_0.set_samp_rate_in(self.samp_rate)
Piotr Krysik35582082015-09-02 21:49:12 +0200260 self.qtgui_freq_sink_x_0.set_frequency_range(self.fc_slider, self.samp_rate)
261 self.rtlsdr_source_0.set_sample_rate(self.samp_rate)
262
263 def get_shiftoff(self):
264 return self.shiftoff
265
266 def set_shiftoff(self, shiftoff):
267 self.shiftoff = shiftoff
Piotr Krysik82638ab2017-07-23 19:14:13 +0200268 self.blocks_rotator_cc_0.set_phase_inc(-2*pi*self.shiftoff/self.samp_rate)
Piotr Krysik35582082015-09-02 21:49:12 +0200269 self.rtlsdr_source_0.set_center_freq(self.fc_slider-self.shiftoff, 0)
270 self.rtlsdr_source_0.set_bandwidth(250e3+abs(self.shiftoff), 0)
Piotr Krysikfe538eb2016-07-18 18:14:49 +0200271
Piotr Krysik85ec6ae2017-08-26 12:24:34 +0200272 def get_collector(self):
273 return self.collector
Piotr Krysikfe538eb2016-07-18 18:14:49 +0200274
Piotr Krysik85ec6ae2017-08-26 12:24:34 +0200275 def set_collector(self, collector):
276 self.collector = collector
277
278 def get_collectorport(self):
279 return self.collectorport
280
281 def set_collectorport(self, collectorport):
282 self.collectorport = collectorport
Piotr Krysik35582082015-09-02 21:49:12 +0200283
284 def get_ppm_slider(self):
285 return self.ppm_slider
286
287 def set_ppm_slider(self, ppm_slider):
288 self.ppm_slider = ppm_slider
289 self.rtlsdr_source_0.set_freq_corr(self.ppm_slider, 0)
290
291 def get_g_slider(self):
292 return self.g_slider
293
294 def set_g_slider(self, g_slider):
295 self.g_slider = g_slider
296 self.rtlsdr_source_0.set_gain(self.g_slider, 0)
297
298 def get_fc_slider(self):
299 return self.fc_slider
300
301 def set_fc_slider(self, fc_slider):
302 self.fc_slider = fc_slider
303 self.qtgui_freq_sink_x_0.set_frequency_range(self.fc_slider, self.samp_rate)
304 self.rtlsdr_source_0.set_center_freq(self.fc_slider-self.shiftoff, 0)
305
306
Piotr Krysik6577ec22016-07-15 13:21:09 +0200307def argument_parser():
Piotr Krysik35582082015-09-02 21:49:12 +0200308 parser = OptionParser(option_class=eng_option, usage="%prog: [options]")
Piotr Krysik6577ec22016-07-15 13:21:09 +0200309 parser.add_option(
310 "", "--args", dest="args", type="string", default="",
311 help="Set Device Arguments [default=%default]")
312 parser.add_option(
Piotr Krysik85ec6ae2017-08-26 12:24:34 +0200313 "-f", "--fc", dest="fc", type="eng_float", default=eng_notation.num_to_str(941.8e6),
314 help="Set GSM channel's central frequency [default=%default]")
315 parser.add_option(
Piotr Krysik6577ec22016-07-15 13:21:09 +0200316 "-g", "--gain", dest="gain", type="eng_float", default=eng_notation.num_to_str(30),
Piotr Krysik35582082015-09-02 21:49:12 +0200317 help="Set gain [default=%default]")
Piotr Krysik6577ec22016-07-15 13:21:09 +0200318 parser.add_option(
Piotr Krysik82638ab2017-07-23 19:14:13 +0200319 "", "--osr", dest="osr", type="intx", default=4,
320 help="Set OverSampling Ratio [default=%default]")
321 parser.add_option(
Piotr Krysik93ba6bc2017-01-23 21:33:09 +0100322 "-p", "--ppm", dest="ppm", type="eng_float", default=eng_notation.num_to_str(0),
Piotr Krysik35582082015-09-02 21:49:12 +0200323 help="Set ppm [default=%default]")
Piotr Krysik6577ec22016-07-15 13:21:09 +0200324 parser.add_option(
325 "-s", "--samp-rate", dest="samp_rate", type="eng_float", default=eng_notation.num_to_str(2000000.052982),
Piotr Krysik35582082015-09-02 21:49:12 +0200326 help="Set samp_rate [default=%default]")
Piotr Krysik6577ec22016-07-15 13:21:09 +0200327 parser.add_option(
328 "-o", "--shiftoff", dest="shiftoff", type="eng_float", default=eng_notation.num_to_str(400e3),
Piotr Krysik82638ab2017-07-23 19:14:13 +0200329 help="Set Frequency Shiftoff [default=%default]")
Piotr Krysikfe538eb2016-07-18 18:14:49 +0200330 parser.add_option(
Piotr Krysik85ec6ae2017-08-26 12:24:34 +0200331 "", "--collector", dest="collector", type="string", default="localhost",
332 help="Set IP or DNS name of collector point [default=%default]")
333 parser.add_option(
334 "", "--collectorport", dest="collectorport", type="string", default="4729",
335 help="Set UDP port number of collector [default=%default]")
Piotr Krysik6577ec22016-07-15 13:21:09 +0200336 return parser
337
338
339def main(top_block_cls=grgsm_livemon, options=None):
340 if options is None:
341 options, _ = argument_parser().parse_args()
342
343 from distutils.version import StrictVersion
344 if StrictVersion(Qt.qVersion()) >= StrictVersion("4.5.0"):
345 style = gr.prefs().get_string('qtgui', 'style', 'raster')
346 Qt.QApplication.setGraphicsSystem(style)
Piotr Krysik35582082015-09-02 21:49:12 +0200347 qapp = Qt.QApplication(sys.argv)
Piotr Krysik6577ec22016-07-15 13:21:09 +0200348
Piotr Krysik85ec6ae2017-08-26 12:24:34 +0200349 tb = top_block_cls(args=options.args, fc=options.fc, gain=options.gain, osr=options.osr, ppm=options.ppm, samp_rate=options.samp_rate, shiftoff=options.shiftoff, collector=options.collector, collectorport=options.collectorport)
Piotr Krysik35582082015-09-02 21:49:12 +0200350 tb.start()
351 tb.show()
352
353 def quitting():
354 tb.stop()
355 tb.wait()
356 qapp.connect(qapp, Qt.SIGNAL("aboutToQuit()"), quitting)
357 qapp.exec_()
Piotr Krysik6577ec22016-07-15 13:21:09 +0200358
359
360if __name__ == '__main__':
361 main()