Roman Khassraf | 996103f | 2015-09-27 11:22:35 +0200 | [diff] [blame] | 1 | #!/usr/bin/env python |
| 2 | # -*- coding: utf-8 -*- |
| 3 | # @file |
Piotr Krysik | a6268a5 | 2017-08-23 16:02:19 +0200 | [diff] [blame] | 4 | # @author (C) 2015 by Piotr Krysik <ptrkrysik@gmail.com> |
| 5 | # @author (C) 2015 by Roman Khassraf <rkhassraf@gmail.com> |
Roman Khassraf | 996103f | 2015-09-27 11:22:35 +0200 | [diff] [blame] | 6 | # @section LICENSE |
| 7 | # |
| 8 | # Gr-gsm is free software; you can redistribute it and/or modify |
| 9 | # it under the terms of the GNU General Public License as published by |
| 10 | # the Free Software Foundation; either version 3, or (at your option) |
| 11 | # any later version. |
| 12 | # |
| 13 | # Gr-gsm is distributed in the hope that it will be useful, |
| 14 | # but WITHOUT ANY WARRANTY; without even the implied warranty of |
| 15 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
| 16 | # GNU General Public License for more details. |
| 17 | # |
| 18 | # You should have received a copy of the GNU General Public License |
| 19 | # along with gr-gsm; see the file COPYING. If not, write to |
| 20 | # the Free Software Foundation, Inc., 51 Franklin Street, |
| 21 | # Boston, MA 02110-1301, USA. |
| 22 | # |
| 23 | # |
| 24 | from gnuradio import blocks |
| 25 | from gnuradio import gr |
| 26 | from gnuradio import eng_notation |
| 27 | from gnuradio.eng_option import eng_option |
| 28 | from gnuradio.filter import firdes |
| 29 | from gnuradio.filter import pfb |
| 30 | from math import pi |
| 31 | from optparse import OptionParser |
| 32 | |
| 33 | import grgsm |
| 34 | import numpy |
| 35 | import os |
| 36 | import osmosdr |
| 37 | import pmt |
| 38 | import time |
| 39 | |
| 40 | |
Roman Khassraf | 98af2c9 | 2016-09-23 07:26:13 +0200 | [diff] [blame] | 41 | # from wideband_receiver import * |
Roman Khassraf | 996103f | 2015-09-27 11:22:35 +0200 | [diff] [blame] | 42 | |
Piotr Krysik | 8715da0 | 2016-01-06 22:21:09 +0100 | [diff] [blame] | 43 | class receiver_with_decoder(grgsm.hier_block): |
Roman Khassraf | 996103f | 2015-09-27 11:22:35 +0200 | [diff] [blame] | 44 | def __init__(self, OSR=4, chan_num=0, fc=939.4e6, ppm=0, samp_rate=0.2e6): |
Piotr Krysik | 8715da0 | 2016-01-06 22:21:09 +0100 | [diff] [blame] | 45 | grgsm.hier_block.__init__( |
Roman Khassraf | 996103f | 2015-09-27 11:22:35 +0200 | [diff] [blame] | 46 | self, "Receiver With Decoder", |
Roman Khassraf | 98af2c9 | 2016-09-23 07:26:13 +0200 | [diff] [blame] | 47 | gr.io_signature(1, 1, gr.sizeof_gr_complex * 1), |
Roman Khassraf | 996103f | 2015-09-27 11:22:35 +0200 | [diff] [blame] | 48 | gr.io_signature(0, 0, 0), |
| 49 | ) |
Piotr Krysik | 8715da0 | 2016-01-06 22:21:09 +0100 | [diff] [blame] | 50 | self.message_port_register_hier_out("bursts") |
| 51 | self.message_port_register_hier_out("msgs") |
Roman Khassraf | 996103f | 2015-09-27 11:22:35 +0200 | [diff] [blame] | 52 | |
| 53 | ################################################## |
| 54 | # Parameters |
| 55 | ################################################## |
| 56 | self.OSR = OSR |
| 57 | self.chan_num = chan_num |
| 58 | self.fc = fc |
| 59 | self.ppm = ppm |
| 60 | self.samp_rate = samp_rate |
| 61 | |
| 62 | ################################################## |
| 63 | # Variables |
| 64 | ################################################## |
Roman Khassraf | 98af2c9 | 2016-09-23 07:26:13 +0200 | [diff] [blame] | 65 | self.samp_rate_out = samp_rate_out = 1625000.0 / 6.0 * OSR |
Roman Khassraf | 996103f | 2015-09-27 11:22:35 +0200 | [diff] [blame] | 66 | |
| 67 | ################################################## |
| 68 | # Blocks |
| 69 | ################################################## |
| 70 | self.gsm_receiver_0 = grgsm.receiver(OSR, ([chan_num]), ([])) |
| 71 | self.gsm_input_0 = grgsm.gsm_input( |
| 72 | ppm=ppm, |
| 73 | osr=OSR, |
| 74 | fc=fc, |
| 75 | samp_rate_in=samp_rate, |
| 76 | ) |
| 77 | self.gsm_control_channels_decoder_0 = grgsm.control_channels_decoder() |
Piotr Krysik | fe538eb | 2016-07-18 18:14:49 +0200 | [diff] [blame] | 78 | self.gsm_clock_offset_control_0 = grgsm.clock_offset_control(fc, samp_rate, osr=4) |
Piotr Krysik | 773a194 | 2016-05-20 12:45:54 +0200 | [diff] [blame] | 79 | self.gsm_bcch_ccch_demapper_0 = grgsm.gsm_bcch_ccch_demapper(0) |
Roman Khassraf | 996103f | 2015-09-27 11:22:35 +0200 | [diff] [blame] | 80 | |
| 81 | ################################################## |
| 82 | # Connections |
| 83 | ################################################## |
| 84 | self.msg_connect(self.gsm_bcch_ccch_demapper_0, 'bursts', self, 'bursts') |
| 85 | self.msg_connect(self.gsm_bcch_ccch_demapper_0, 'bursts', self.gsm_control_channels_decoder_0, 'bursts') |
Piotr Krysik | 6577ec2 | 2016-07-15 13:21:09 +0200 | [diff] [blame] | 86 | self.msg_connect(self.gsm_clock_offset_control_0, 'ctrl', self.gsm_input_0, 'ctrl_in') |
Roman Khassraf | 996103f | 2015-09-27 11:22:35 +0200 | [diff] [blame] | 87 | self.msg_connect(self.gsm_control_channels_decoder_0, 'msgs', self, 'msgs') |
| 88 | self.msg_connect(self.gsm_receiver_0, 'C0', self.gsm_bcch_ccch_demapper_0, 'bursts') |
| 89 | self.msg_connect(self.gsm_receiver_0, 'measurements', self.gsm_clock_offset_control_0, 'measurements') |
Roman Khassraf | 98af2c9 | 2016-09-23 07:26:13 +0200 | [diff] [blame] | 90 | self.connect((self.gsm_input_0, 0), (self.gsm_receiver_0, 0)) |
| 91 | self.connect((self, 0), (self.gsm_input_0, 0)) |
Roman Khassraf | 996103f | 2015-09-27 11:22:35 +0200 | [diff] [blame] | 92 | |
| 93 | def get_OSR(self): |
| 94 | return self.OSR |
| 95 | |
| 96 | def set_OSR(self, OSR): |
| 97 | self.OSR = OSR |
Roman Khassraf | 98af2c9 | 2016-09-23 07:26:13 +0200 | [diff] [blame] | 98 | self.set_samp_rate_out(1625000.0 / 6.0 * self.OSR) |
Roman Khassraf | 996103f | 2015-09-27 11:22:35 +0200 | [diff] [blame] | 99 | self.gsm_input_0.set_osr(self.OSR) |
| 100 | |
| 101 | def get_chan_num(self): |
| 102 | return self.chan_num |
| 103 | |
| 104 | def set_chan_num(self, chan_num): |
| 105 | self.chan_num = chan_num |
| 106 | |
| 107 | def get_fc(self): |
| 108 | return self.fc |
| 109 | |
| 110 | def set_fc(self, fc): |
| 111 | self.fc = fc |
| 112 | self.gsm_input_0.set_fc(self.fc) |
| 113 | |
| 114 | def get_ppm(self): |
| 115 | return self.ppm |
| 116 | |
| 117 | def set_ppm(self, ppm): |
| 118 | self.ppm = ppm |
| 119 | self.gsm_input_0.set_ppm(self.ppm) |
| 120 | |
| 121 | def get_samp_rate(self): |
| 122 | return self.samp_rate |
| 123 | |
| 124 | def set_samp_rate(self, samp_rate): |
| 125 | self.samp_rate = samp_rate |
| 126 | self.gsm_input_0.set_samp_rate_in(self.samp_rate) |
| 127 | |
| 128 | def get_samp_rate_out(self): |
| 129 | return self.samp_rate_out |
| 130 | |
| 131 | def set_samp_rate_out(self, samp_rate_out): |
| 132 | self.samp_rate_out = samp_rate_out |
| 133 | |
| 134 | |
Piotr Krysik | 8715da0 | 2016-01-06 22:21:09 +0100 | [diff] [blame] | 135 | class wideband_receiver(grgsm.hier_block): |
Roman Khassraf | 996103f | 2015-09-27 11:22:35 +0200 | [diff] [blame] | 136 | def __init__(self, OSR=4, fc=939.4e6, samp_rate=0.4e6): |
Piotr Krysik | 8715da0 | 2016-01-06 22:21:09 +0100 | [diff] [blame] | 137 | grgsm.hier_block.__init__( |
Roman Khassraf | 996103f | 2015-09-27 11:22:35 +0200 | [diff] [blame] | 138 | self, "Wideband receiver", |
Roman Khassraf | 98af2c9 | 2016-09-23 07:26:13 +0200 | [diff] [blame] | 139 | gr.io_signature(1, 1, gr.sizeof_gr_complex * 1), |
Roman Khassraf | 996103f | 2015-09-27 11:22:35 +0200 | [diff] [blame] | 140 | gr.io_signature(0, 0, 0), |
| 141 | ) |
Piotr Krysik | 8715da0 | 2016-01-06 22:21:09 +0100 | [diff] [blame] | 142 | self.message_port_register_hier_out("bursts") |
| 143 | self.message_port_register_hier_out("msgs") |
Roman Khassraf | 996103f | 2015-09-27 11:22:35 +0200 | [diff] [blame] | 144 | self.__init(OSR, fc, samp_rate) |
Roman Khassraf | 98af2c9 | 2016-09-23 07:26:13 +0200 | [diff] [blame] | 145 | |
Roman Khassraf | 996103f | 2015-09-27 11:22:35 +0200 | [diff] [blame] | 146 | def __init(self, OSR=4, fc=939.4e6, samp_rate=0.4e6): |
| 147 | ################################################## |
| 148 | # Parameters |
| 149 | ################################################## |
| 150 | self.OSR = OSR |
| 151 | self.fc = fc |
| 152 | self.samp_rate = samp_rate |
Roman Khassraf | 98af2c9 | 2016-09-23 07:26:13 +0200 | [diff] [blame] | 153 | self.channels_num = int(samp_rate / 0.2e6) |
Roman Khassraf | 996103f | 2015-09-27 11:22:35 +0200 | [diff] [blame] | 154 | self.OSR_PFB = 2 |
Roman Khassraf | 98af2c9 | 2016-09-23 07:26:13 +0200 | [diff] [blame] | 155 | |
Roman Khassraf | 996103f | 2015-09-27 11:22:35 +0200 | [diff] [blame] | 156 | ################################################## |
| 157 | # Blocks |
| 158 | ################################################## |
| 159 | self.pfb_channelizer_ccf_0 = pfb.channelizer_ccf( |
| 160 | self.channels_num, |
| 161 | (), |
| 162 | self.OSR_PFB, |
| 163 | 100) |
| 164 | self.pfb_channelizer_ccf_0.set_channel_map(([])) |
| 165 | self.create_receivers() |
| 166 | |
| 167 | ################################################## |
| 168 | # Connections |
| 169 | ################################################## |
| 170 | self.connect((self, 0), (self.pfb_channelizer_ccf_0, 0)) |
Roman Khassraf | 98af2c9 | 2016-09-23 07:26:13 +0200 | [diff] [blame] | 171 | for chan in xrange(0, self.channels_num): |
Roman Khassraf | 996103f | 2015-09-27 11:22:35 +0200 | [diff] [blame] | 172 | self.connect((self.pfb_channelizer_ccf_0, chan), (self.receivers_with_decoders[chan], 0)) |
| 173 | self.msg_connect(self.receivers_with_decoders[chan], 'bursts', self, 'bursts') |
| 174 | self.msg_connect(self.receivers_with_decoders[chan], 'msgs', self, 'msgs') |
| 175 | |
| 176 | def create_receivers(self): |
| 177 | self.receivers_with_decoders = {} |
Roman Khassraf | 98af2c9 | 2016-09-23 07:26:13 +0200 | [diff] [blame] | 178 | for chan in xrange(0, self.channels_num): |
| 179 | self.receivers_with_decoders[chan] = receiver_with_decoder(fc=self.fc, OSR=self.OSR, chan_num=chan, |
| 180 | samp_rate=self.OSR_PFB * 0.2e6) |
Roman Khassraf | 996103f | 2015-09-27 11:22:35 +0200 | [diff] [blame] | 181 | |
| 182 | def get_OSR(self): |
| 183 | return self.OSR |
| 184 | |
| 185 | def set_OSR(self, OSR): |
| 186 | self.OSR = OSR |
| 187 | self.create_receivers() |
| 188 | |
| 189 | def get_fc(self): |
| 190 | return self.fc |
| 191 | |
| 192 | def set_fc(self, fc): |
| 193 | self.fc = fc |
| 194 | self.create_receivers() |
| 195 | |
| 196 | def get_samp_rate(self): |
| 197 | return self.samp_rate |
| 198 | |
| 199 | |
Roman Khassraf | e2fbb87 | 2015-09-29 19:40:04 +0200 | [diff] [blame] | 200 | class wideband_scanner(gr.top_block): |
Piotr Krysik | 881b6c3 | 2017-03-30 10:53:25 +0200 | [diff] [blame] | 201 | def __init__(self, rec_len=3, sample_rate=2e6, carrier_frequency=939e6, gain=24, ppm=0, args=""): |
Roman Khassraf | 996103f | 2015-09-27 11:22:35 +0200 | [diff] [blame] | 202 | gr.top_block.__init__(self, "Wideband Scanner") |
Roman Khassraf | 98af2c9 | 2016-09-23 07:26:13 +0200 | [diff] [blame] | 203 | |
Roman Khassraf | 996103f | 2015-09-27 11:22:35 +0200 | [diff] [blame] | 204 | self.rec_len = rec_len |
| 205 | self.sample_rate = sample_rate |
| 206 | self.carrier_frequency = carrier_frequency |
| 207 | self.ppm = ppm |
Roman Khassraf | 98af2c9 | 2016-09-23 07:26:13 +0200 | [diff] [blame] | 208 | |
Roman Khassraf | 996103f | 2015-09-27 11:22:35 +0200 | [diff] [blame] | 209 | # if no file name is given process data from rtl_sdr source |
Roman Khassraf | 98af2c9 | 2016-09-23 07:26:13 +0200 | [diff] [blame] | 210 | print "Args=", args |
| 211 | self.rtlsdr_source = osmosdr.source(args="numchan=" + str(1) + " " + args) |
Piotr Krysik | 4a3a52c | 2018-06-02 12:27:58 +0200 | [diff] [blame] | 212 | #self.rtlsdr_source.set_min_output_buffer(int(sample_rate*rec_len)) #this line causes segfaults on HackRF |
Roman Khassraf | 996103f | 2015-09-27 11:22:35 +0200 | [diff] [blame] | 213 | self.rtlsdr_source.set_sample_rate(sample_rate) |
Roman Khassraf | 98af2c9 | 2016-09-23 07:26:13 +0200 | [diff] [blame] | 214 | |
Roman Khassraf | 996103f | 2015-09-27 11:22:35 +0200 | [diff] [blame] | 215 | # capture half of GSM channel lower than channel center (-0.1MHz) |
| 216 | # this is needed when even number of channels is captured in order to process full captured bandwidth |
Roman Khassraf | 98af2c9 | 2016-09-23 07:26:13 +0200 | [diff] [blame] | 217 | |
Roman Khassraf | 996103f | 2015-09-27 11:22:35 +0200 | [diff] [blame] | 218 | self.rtlsdr_source.set_center_freq(carrier_frequency - 0.1e6, 0) |
Roman Khassraf | 98af2c9 | 2016-09-23 07:26:13 +0200 | [diff] [blame] | 219 | |
Roman Khassraf | 996103f | 2015-09-27 11:22:35 +0200 | [diff] [blame] | 220 | # correction of central frequency |
Roman Khassraf | 98af2c9 | 2016-09-23 07:26:13 +0200 | [diff] [blame] | 221 | # if the receiver has large frequency offset |
Roman Khassraf | 996103f | 2015-09-27 11:22:35 +0200 | [diff] [blame] | 222 | # the value of this variable should be set close to that offset in ppm |
Roman Khassraf | a5f5a31 | 2016-11-20 22:11:46 +0100 | [diff] [blame] | 223 | self.rtlsdr_source.set_freq_corr(ppm, 0) |
Roman Khassraf | 98af2c9 | 2016-09-23 07:26:13 +0200 | [diff] [blame] | 224 | |
Roman Khassraf | 996103f | 2015-09-27 11:22:35 +0200 | [diff] [blame] | 225 | self.rtlsdr_source.set_dc_offset_mode(2, 0) |
| 226 | self.rtlsdr_source.set_iq_balance_mode(0, 0) |
| 227 | self.rtlsdr_source.set_gain_mode(True, 0) |
| 228 | self.rtlsdr_source.set_bandwidth(sample_rate, 0) |
Piotr Krysik | 881b6c3 | 2017-03-30 10:53:25 +0200 | [diff] [blame] | 229 | self.rtlsdr_source.set_gain(gain, 0) |
| 230 | self.rtlsdr_source.set_if_gain(32, 0) |
| 231 | self.rtlsdr_source.set_bb_gain(30, 0) |
Roman Khassraf | 996103f | 2015-09-27 11:22:35 +0200 | [diff] [blame] | 232 | self.head = blocks.head(gr.sizeof_gr_complex * 1, int(rec_len * sample_rate)) |
Roman Khassraf | 98af2c9 | 2016-09-23 07:26:13 +0200 | [diff] [blame] | 233 | |
Roman Khassraf | 996103f | 2015-09-27 11:22:35 +0200 | [diff] [blame] | 234 | # shift again by -0.1MHz in order to align channel center in 0Hz |
Roman Khassraf | a5f5a31 | 2016-11-20 22:11:46 +0100 | [diff] [blame] | 235 | self.blocks_rotator_cc = blocks.rotator_cc(-2 * pi * 0.1e6 / sample_rate) |
Roman Khassraf | 98af2c9 | 2016-09-23 07:26:13 +0200 | [diff] [blame] | 236 | |
Roman Khassraf | 996103f | 2015-09-27 11:22:35 +0200 | [diff] [blame] | 237 | self.wideband_receiver = wideband_receiver(OSR=4, fc=carrier_frequency, samp_rate=sample_rate) |
| 238 | self.gsm_extract_system_info = grgsm.extract_system_info() |
Roman Khassraf | 98af2c9 | 2016-09-23 07:26:13 +0200 | [diff] [blame] | 239 | |
Roman Khassraf | 996103f | 2015-09-27 11:22:35 +0200 | [diff] [blame] | 240 | self.connect((self.rtlsdr_source, 0), (self.head, 0)) |
| 241 | self.connect((self.head, 0), (self.blocks_rotator_cc, 0)) |
Roman Khassraf | 98af2c9 | 2016-09-23 07:26:13 +0200 | [diff] [blame] | 242 | self.connect((self.blocks_rotator_cc, 0), (self.wideband_receiver, 0)) |
Roman Khassraf | 996103f | 2015-09-27 11:22:35 +0200 | [diff] [blame] | 243 | self.msg_connect(self.wideband_receiver, 'msgs', self.gsm_extract_system_info, 'msgs') |
Roman Khassraf | 98af2c9 | 2016-09-23 07:26:13 +0200 | [diff] [blame] | 244 | |
Roman Khassraf | 996103f | 2015-09-27 11:22:35 +0200 | [diff] [blame] | 245 | def set_carrier_frequency(self, carrier_frequency): |
| 246 | self.carrier_frequency = carrier_frequency |
| 247 | self.rtlsdr_source.set_center_freq(carrier_frequency - 0.1e6, 0) |
| 248 | |
Roman Khassraf | 98af2c9 | 2016-09-23 07:26:13 +0200 | [diff] [blame] | 249 | |
Roman Khassraf | e2fbb87 | 2015-09-29 19:40:04 +0200 | [diff] [blame] | 250 | class channel_info(object): |
Roman Khassraf | 996103f | 2015-09-27 11:22:35 +0200 | [diff] [blame] | 251 | def __init__(self, arfcn, freq, cid, lac, mcc, mnc, ccch_conf, power, neighbours, cell_arfcns): |
| 252 | self.arfcn = arfcn |
| 253 | self.freq = freq |
| 254 | self.cid = cid |
| 255 | self.lac = lac |
| 256 | self.mcc = mcc |
| 257 | self.mnc = mnc |
| 258 | self.ccch_conf = ccch_conf |
| 259 | self.power = power |
| 260 | self.neighbours = neighbours |
| 261 | self.cell_arfcns = cell_arfcns |
Roman Khassraf | 98af2c9 | 2016-09-23 07:26:13 +0200 | [diff] [blame] | 262 | |
Roman Khassraf | 996103f | 2015-09-27 11:22:35 +0200 | [diff] [blame] | 263 | def get_verbose_info(self): |
| 264 | i = " |---- Configuration: %s\n" % self.get_ccch_conf() |
| 265 | i += " |---- Cell ARFCNs: " + ", ".join(map(str, self.cell_arfcns)) + "\n" |
| 266 | i += " |---- Neighbour Cells: " + ", ".join(map(str, self.neighbours)) + "\n" |
| 267 | return i |
Roman Khassraf | 98af2c9 | 2016-09-23 07:26:13 +0200 | [diff] [blame] | 268 | |
Roman Khassraf | 996103f | 2015-09-27 11:22:35 +0200 | [diff] [blame] | 269 | def get_ccch_conf(self): |
| 270 | if self.ccch_conf == 0: |
| 271 | return "1 CCCH, not combined" |
| 272 | elif self.ccch_conf == 1: |
| 273 | return "1 CCCH, combined" |
| 274 | elif self.ccch_conf == 2: |
| 275 | return "2 CCCH, not combined" |
| 276 | elif self.ccch_conf == 4: |
| 277 | return "3 CCCH, not combined" |
| 278 | elif self.ccch_conf == 6: |
| 279 | return "4 CCCH, not combined" |
| 280 | else: |
| 281 | return "Unknown" |
Roman Khassraf | 98af2c9 | 2016-09-23 07:26:13 +0200 | [diff] [blame] | 282 | |
Roman Khassraf | 996103f | 2015-09-27 11:22:35 +0200 | [diff] [blame] | 283 | def getKey(self): |
| 284 | return self.arfcn |
Roman Khassraf | 98af2c9 | 2016-09-23 07:26:13 +0200 | [diff] [blame] | 285 | |
Roman Khassraf | 996103f | 2015-09-27 11:22:35 +0200 | [diff] [blame] | 286 | def __cmp__(self, other): |
| 287 | if hasattr(other, 'getKey'): |
| 288 | return self.getKey().__cmp__(other.getKey()) |
Roman Khassraf | 98af2c9 | 2016-09-23 07:26:13 +0200 | [diff] [blame] | 289 | |
Roman Khassraf | 996103f | 2015-09-27 11:22:35 +0200 | [diff] [blame] | 290 | def __repr__(self): |
Petter Reinholdtsen | 1eeb1b4 | 2017-08-27 05:39:11 +0000 | [diff] [blame] | 291 | return "%s(%s, %s, %s, %s, %s, %s, %s, %s, %s, %s)" % ( |
Petter Reinholdtsen | 284282e | 2017-08-24 11:33:03 +0200 | [diff] [blame] | 292 | self.__class__, self.arfcn, self.freq, self.cid, self.lac, |
| 293 | self.mcc, self.mnc, self.ccch_conf, self.power, |
| 294 | self.neighbours, self.cell_arfcns) |
| 295 | |
| 296 | def __str__(self): |
Roman Khassraf | 98af2c9 | 2016-09-23 07:26:13 +0200 | [diff] [blame] | 297 | return "ARFCN: %4u, Freq: %6.1fM, CID: %5u, LAC: %5u, MCC: %3u, MNC: %3u, Pwr: %3i" % ( |
| 298 | self.arfcn, self.freq / 1e6, self.cid, self.lac, self.mcc, self.mnc, self.power) |
| 299 | |
Vasil Velichkov | 1e04c1f | 2018-09-04 23:27:04 +0300 | [diff] [blame] | 300 | def do_scan(samp_rate, band, speed, ppm, gain, args, prn = None, debug = False): |
Petter Reinholdtsen | 6a7dc44 | 2017-08-24 10:46:43 +0200 | [diff] [blame] | 301 | signallist = [] |
| 302 | channels_num = int(samp_rate / 0.2e6) |
| 303 | for arfcn_range in grgsm.arfcn.get_arfcn_ranges(band): |
Roman Khassraf | 98af2c9 | 2016-09-23 07:26:13 +0200 | [diff] [blame] | 304 | first_arfcn = arfcn_range[0] |
| 305 | last_arfcn = arfcn_range[1] |
| 306 | last_center_arfcn = last_arfcn - int((channels_num / 2) - 1) |
| 307 | |
Piotr Krysik | 3dfa11b | 2017-09-06 17:48:38 +0200 | [diff] [blame] | 308 | current_freq = grgsm.arfcn.arfcn2downlink(first_arfcn + int(channels_num / 2) - 1) |
| 309 | last_freq = grgsm.arfcn.arfcn2downlink(last_center_arfcn) |
Roman Khassraf | 98af2c9 | 2016-09-23 07:26:13 +0200 | [diff] [blame] | 310 | stop_freq = last_freq + 0.2e6 * channels_num |
| 311 | |
| 312 | while current_freq < stop_freq: |
| 313 | |
Vasil Velichkov | 1e04c1f | 2018-09-04 23:27:04 +0300 | [diff] [blame] | 314 | if not debug: |
| 315 | # silence rtl_sdr output: |
| 316 | # open 2 fds |
| 317 | null_fds = [os.open(os.devnull, os.O_RDWR) for x in xrange(2)] |
| 318 | # save the current file descriptors to a tuple |
| 319 | save = os.dup(1), os.dup(2) |
| 320 | # put /dev/null fds on 1 and 2 |
| 321 | os.dup2(null_fds[0], 1) |
| 322 | os.dup2(null_fds[1], 2) |
Roman Khassraf | 98af2c9 | 2016-09-23 07:26:13 +0200 | [diff] [blame] | 323 | |
| 324 | # instantiate scanner and processor |
Petter Reinholdtsen | 6a7dc44 | 2017-08-24 10:46:43 +0200 | [diff] [blame] | 325 | scanner = wideband_scanner(rec_len=6 - speed, |
| 326 | sample_rate=samp_rate, |
Roman Khassraf | 98af2c9 | 2016-09-23 07:26:13 +0200 | [diff] [blame] | 327 | carrier_frequency=current_freq, |
Petter Reinholdtsen | 6a7dc44 | 2017-08-24 10:46:43 +0200 | [diff] [blame] | 328 | ppm=ppm, gain=gain, args=args) |
Roman Khassraf | 98af2c9 | 2016-09-23 07:26:13 +0200 | [diff] [blame] | 329 | |
| 330 | # start recording |
| 331 | scanner.start() |
| 332 | scanner.wait() |
| 333 | scanner.stop() |
| 334 | |
| 335 | freq_offsets = numpy.fft.ifftshift( |
| 336 | numpy.array(range(int(-numpy.floor(channels_num / 2)), int(numpy.floor((channels_num + 1) / 2)))) * 2e5) |
| 337 | detected_c0_channels = scanner.gsm_extract_system_info.get_chans() |
| 338 | |
Roman Khassraf | 996103f | 2015-09-27 11:22:35 +0200 | [diff] [blame] | 339 | found_list = [] |
Roman Khassraf | 996103f | 2015-09-27 11:22:35 +0200 | [diff] [blame] | 340 | |
Roman Khassraf | 98af2c9 | 2016-09-23 07:26:13 +0200 | [diff] [blame] | 341 | if detected_c0_channels: |
| 342 | chans = numpy.array(scanner.gsm_extract_system_info.get_chans()) |
| 343 | found_freqs = current_freq + freq_offsets[(chans)] |
| 344 | |
| 345 | cell_ids = numpy.array(scanner.gsm_extract_system_info.get_cell_id()) |
| 346 | lacs = numpy.array(scanner.gsm_extract_system_info.get_lac()) |
| 347 | mccs = numpy.array(scanner.gsm_extract_system_info.get_mcc()) |
| 348 | mncs = numpy.array(scanner.gsm_extract_system_info.get_mnc()) |
| 349 | ccch_confs = numpy.array(scanner.gsm_extract_system_info.get_ccch_conf()) |
| 350 | powers = numpy.array(scanner.gsm_extract_system_info.get_pwrs()) |
| 351 | |
| 352 | for i in range(0, len(chans)): |
| 353 | cell_arfcn_list = scanner.gsm_extract_system_info.get_cell_arfcns(chans[i]) |
| 354 | neighbour_list = scanner.gsm_extract_system_info.get_neighbours(chans[i]) |
| 355 | |
Piotr Krysik | 3dfa11b | 2017-09-06 17:48:38 +0200 | [diff] [blame] | 356 | info = channel_info(grgsm.arfcn.downlink2arfcn(found_freqs[i]), found_freqs[i], |
Roman Khassraf | 98af2c9 | 2016-09-23 07:26:13 +0200 | [diff] [blame] | 357 | cell_ids[i], lacs[i], mccs[i], mncs[i], ccch_confs[i], powers[i], |
| 358 | neighbour_list, cell_arfcn_list) |
| 359 | found_list.append(info) |
| 360 | |
| 361 | scanner = None |
| 362 | |
Vasil Velichkov | 1e04c1f | 2018-09-04 23:27:04 +0300 | [diff] [blame] | 363 | |
| 364 | if not debug: |
| 365 | # restore file descriptors so we can print the results |
| 366 | os.dup2(save[0], 1) |
| 367 | os.dup2(save[1], 2) |
| 368 | # close the temporary fds |
| 369 | os.close(null_fds[0]) |
| 370 | os.close(null_fds[1]) |
Petter Reinholdtsen | 6a7dc44 | 2017-08-24 10:46:43 +0200 | [diff] [blame] | 371 | if prn: |
| 372 | prn(found_list) |
| 373 | signallist.extend(found_list) |
Roman Khassraf | 98af2c9 | 2016-09-23 07:26:13 +0200 | [diff] [blame] | 374 | |
| 375 | current_freq += channels_num * 0.2e6 |
Petter Reinholdtsen | 6a7dc44 | 2017-08-24 10:46:43 +0200 | [diff] [blame] | 376 | return signallist |
| 377 | |
| 378 | def argument_parser(): |
| 379 | parser = OptionParser(option_class=eng_option, usage="%prog: [options]") |
| 380 | bands_list = ", ".join(grgsm.arfcn.get_bands()) |
Piotr Krysik | 3dfa11b | 2017-09-06 17:48:38 +0200 | [diff] [blame] | 381 | parser.add_option("-b", "--band", dest="band", default="GSM900", |
Petter Reinholdtsen | 6a7dc44 | 2017-08-24 10:46:43 +0200 | [diff] [blame] | 382 | help="Specify the GSM band for the frequency.\nAvailable bands are: " + bands_list) |
| 383 | parser.add_option("-s", "--samp-rate", dest="samp_rate", type="float", default=2e6, |
| 384 | help="Set sample rate [default=%default] - allowed values even_number*0.2e6") |
| 385 | parser.add_option("-p", "--ppm", dest="ppm", type="intx", default=0, |
| 386 | help="Set frequency correction in ppm [default=%default]") |
| 387 | parser.add_option("-g", "--gain", dest="gain", type="eng_float", default=24.0, |
| 388 | help="Set gain [default=%default]") |
| 389 | parser.add_option("", "--args", dest="args", type="string", default="", |
| 390 | help="Set device arguments [default=%default]") |
| 391 | parser.add_option("--speed", dest="speed", type="intx", default=4, |
| 392 | help="Scan speed [default=%default]. Value range 0-5.") |
| 393 | parser.add_option("-v", "--verbose", action="store_true", |
| 394 | help="If set, verbose information output is printed: ccch configuration, cell ARFCN's, neighbour ARFCN's") |
Vasil Velichkov | 1e04c1f | 2018-09-04 23:27:04 +0300 | [diff] [blame] | 395 | parser.add_option("-d", "--debug", action="store_true", |
| 396 | help="Print additional debug messages") |
Petter Reinholdtsen | 6a7dc44 | 2017-08-24 10:46:43 +0200 | [diff] [blame] | 397 | |
| 398 | """ |
| 399 | Dont forget: sudo sysctl kernel.shmmni=32000 |
| 400 | """ |
| 401 | return parser |
| 402 | |
| 403 | def main(options = None): |
| 404 | if options is None: |
| 405 | (options, args) = argument_parser().parse_args() |
| 406 | |
| 407 | if options.band not in grgsm.arfcn.get_bands(): |
| 408 | parser.error("Invalid GSM band\n") |
| 409 | |
| 410 | if options.speed < 0 or options.speed > 5: |
| 411 | parser.error("Invalid scan speed.\n") |
| 412 | |
| 413 | if (options.samp_rate / 0.2e6) % 2 != 0: |
| 414 | parser.error("Invalid sample rate. Sample rate must be an even numer * 0.2e6") |
| 415 | |
| 416 | def printfunc(found_list): |
| 417 | for info in sorted(found_list): |
| 418 | print info |
| 419 | if options.verbose: |
| 420 | print info.get_verbose_info() |
| 421 | print "" |
| 422 | do_scan(options.samp_rate, options.band, options.speed, |
Vasil Velichkov | 1e04c1f | 2018-09-04 23:27:04 +0300 | [diff] [blame] | 423 | options.ppm, options.gain, options.args, prn = printfunc, debug = options.debug) |
Petter Reinholdtsen | 6a7dc44 | 2017-08-24 10:46:43 +0200 | [diff] [blame] | 424 | |
| 425 | if __name__ == '__main__': |
| 426 | main() |