2015-08-22 17:52:41 +00:00
#!/usr/bin/env python
# -*- coding: utf-8 -*-
# @file
# @author Roman Khassraf <rkhassraf@gmail.com>
# @section LICENSE
#
# Gr-gsm is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation; either version 3, or (at your option)
# any later version.
#
# Gr-gsm is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with gr-gsm; see the file COPYING. If not, write to
# the Free Software Foundation, Inc., 51 Franklin Street,
# Boston, MA 02110-1301, USA.
#
#
from gnuradio import blocks
from gnuradio import eng_notation
from gnuradio import gr
from gnuradio . eng_option import eng_option
from gnuradio . filter import firdes
from math import pi
from optparse import OptionParser
import grgsm
import osmosdr
import pmt
2015-08-24 18:00:48 +00:00
import signal
import sys
2015-08-22 17:52:41 +00:00
2016-02-13 06:31:51 +00:00
class grgsm_capture ( gr . top_block ) :
2015-08-22 17:52:41 +00:00
2016-02-14 19:24:54 +00:00
def __init__ ( self , fc , gain , samp_rate , ppm , arfcn , cfile = None , burst_file = None , band = None , verbose = False , rec_length = None , args = " " ) :
2015-08-22 17:52:41 +00:00
2016-02-13 06:31:51 +00:00
gr . top_block . __init__ ( self , " Gr-gsm Capture " )
2015-08-24 18:00:48 +00:00
2015-08-22 17:52:41 +00:00
##################################################
# Parameters
##################################################
self . fc = fc
self . gain = gain
self . samp_rate = samp_rate
self . ppm = ppm
self . arfcn = arfcn
self . cfile = cfile
self . burst_file = burst_file
2015-08-24 18:11:10 +00:00
self . band = band
2015-08-22 17:52:41 +00:00
self . verbose = verbose
self . shiftoff = shiftoff = 400e3
2015-09-20 11:53:06 +00:00
self . rec_length = rec_length
2015-08-24 18:00:48 +00:00
2015-08-22 17:52:41 +00:00
##################################################
# Processing Blocks
##################################################
2016-03-18 10:39:09 +00:00
self . rtlsdr_source = osmosdr . source ( args = " numchan= " + str ( 1 ) + " " + args )
2015-08-22 17:52:41 +00:00
self . rtlsdr_source . set_sample_rate ( samp_rate )
2015-08-24 18:00:48 +00:00
self . rtlsdr_source . set_center_freq ( fc - shiftoff , 0 )
self . rtlsdr_source . set_freq_corr ( ppm , 0 )
2015-08-22 17:52:41 +00:00
self . rtlsdr_source . set_dc_offset_mode ( 2 , 0 )
self . rtlsdr_source . set_iq_balance_mode ( 2 , 0 )
self . rtlsdr_source . set_gain_mode ( True , 0 )
2015-08-24 18:00:48 +00:00
self . rtlsdr_source . set_gain ( gain , 0 )
2015-08-22 17:52:41 +00:00
self . rtlsdr_source . set_if_gain ( 20 , 0 )
self . rtlsdr_source . set_bb_gain ( 20 , 0 )
self . rtlsdr_source . set_antenna ( " " , 0 )
self . rtlsdr_source . set_bandwidth ( 250e3 + abs ( shiftoff ) , 0 )
self . blocks_rotator = blocks . rotator_cc ( - 2 * pi * shiftoff / samp_rate )
2015-09-20 11:53:06 +00:00
if self . rec_length is not None :
self . blocks_head_0 = blocks . head ( gr . sizeof_gr_complex , int ( samp_rate * rec_length ) )
2015-08-22 17:52:41 +00:00
if self . verbose or self . burst_file :
self . gsm_receiver = grgsm . receiver ( 4 , ( [ self . arfcn ] ) , ( [ ] ) )
self . gsm_input = grgsm . gsm_input (
ppm = 0 ,
osr = 4 ,
fc = fc ,
samp_rate_in = samp_rate ,
)
2016-10-01 11:00:43 +00:00
self . gsm_clock_offset_control = grgsm . clock_offset_control ( fc - shiftoff , samp_rate , osr = 4 )
2015-08-22 17:52:41 +00:00
if self . burst_file :
self . gsm_burst_file_sink = grgsm . burst_file_sink ( self . burst_file )
if self . cfile :
self . blocks_file_sink = blocks . file_sink ( gr . sizeof_gr_complex * 1 , self . cfile , False )
self . blocks_file_sink . set_unbuffered ( False )
if self . verbose :
self . gsm_bursts_printer_0 = grgsm . bursts_printer ( pmt . intern ( " " ) ,
False , False , False , False )
##################################################
# Connections
##################################################
2015-09-20 11:53:06 +00:00
if self . rec_length is not None : #if recording length is defined connect head block after the source
self . connect ( ( self . rtlsdr_source , 0 ) , ( self . blocks_head_0 , 0 ) )
self . connect ( ( self . blocks_head_0 , 0 ) , ( self . blocks_rotator , 0 ) )
else :
self . connect ( ( self . rtlsdr_source , 0 ) , ( self . blocks_rotator , 0 ) )
2015-08-22 17:52:41 +00:00
if self . cfile :
self . connect ( ( self . blocks_rotator , 0 ) , ( self . blocks_file_sink , 0 ) )
if self . verbose or self . burst_file :
self . connect ( ( self . gsm_input , 0 ) , ( self . gsm_receiver , 0 ) )
self . connect ( ( self . blocks_rotator , 0 ) , ( self . gsm_input , 0 ) )
2016-10-01 11:13:05 +00:00
self . msg_connect ( self . gsm_clock_offset_control , " ctrl " , self . gsm_input , " ctrl_in " )
2015-08-22 17:52:41 +00:00
self . msg_connect ( self . gsm_receiver , " measurements " , self . gsm_clock_offset_control , " measurements " )
if self . burst_file :
self . msg_connect ( self . gsm_receiver , " C0 " , self . gsm_burst_file_sink , " in " )
if self . verbose :
self . msg_connect ( self . gsm_receiver , " C0 " , self . gsm_bursts_printer_0 , " bursts " )
def get_fc ( self ) :
return self . fc
def set_fc ( self , fc ) :
self . fc = fc
if self . verbose or self . burst_file :
self . gsm_input . set_fc ( self . fc )
2015-08-24 18:11:10 +00:00
def get_arfcn ( self ) :
return self . arfcn
def set_arfcn ( self , arfcn ) :
self . arfcn = arfcn
if self . verbose or self . burst_file :
self . gsm_receiver . set_cell_allocation ( [ self . arfcn ] )
if options . band :
new_freq = grgsm . arfcn . arfcn2downlink ( self . arfcn , self . band )
else :
for band in grgsm . arfcn . get_bands ( ) :
if grgsm . arfcn . is_valid_arfcn ( arfcn , band ) :
new_freq = grgsm . arfcn . arfcn2downlink ( arfcn , band )
break
self . set_fc ( new_freq )
2015-08-22 17:52:41 +00:00
def get_gain ( self ) :
return self . gain
def set_gain ( self , gain ) :
self . gain = gain
def get_samp_rate ( self ) :
return self . samp_rate
def set_samp_rate ( self , samp_rate ) :
self . samp_rate = samp_rate
self . rtlsdr_source . set_sample_rate ( self . samp_rate )
if self . verbose or self . burst_file :
self . gsm_input . set_samp_rate_in ( self . samp_rate )
def get_ppm ( self ) :
return self . ppm
def set_ppm ( self , ppm ) :
self . ppm = ppm
self . set_ppm_slider ( self . ppm )
2015-09-20 11:53:06 +00:00
def get_rec_length ( self ) :
return self . rec_length
def set_rec_length ( self , rec_length ) :
self . rec_length = rec_length
self . blocks_head_0 . set_length ( int ( self . samp_rate * self . rec_length ) )
2015-08-22 17:52:41 +00:00
if __name__ == ' __main__ ' :
parser = OptionParser ( option_class = eng_option , usage = " % prog [options] " ,
description = " RTL-SDR capturing app of gr-gsm. " )
parser . add_option ( " -f " , " --fc " , dest = " fc " , type = " eng_float " ,
help = " Set frequency [default= %d efault] " )
parser . add_option ( " -a " , " --arfcn " , dest = " arfcn " , type = " intx " ,
help = " Set ARFCN instead of frequency. In some cases you may have to provide the GSM band also " )
parser . add_option ( " -g " , " --gain " , dest = " gain " , type = " eng_float " ,
default = eng_notation . num_to_str ( 30 ) ,
help = " Set gain [default= %d efault] " )
parser . add_option ( " -s " , " --samp-rate " , dest = " samp_rate " , type = " eng_float " ,
2016-07-21 14:06:03 +00:00
default = eng_notation . num_to_str ( 1000000 ) ,
2015-08-22 17:52:41 +00:00
help = " Set samp_rate [default= %d efault] " )
parser . add_option ( " -p " , " --ppm " , dest = " ppm " , type = " intx " , default = 0 ,
help = " Set ppm [default= %d efault] " )
parser . add_option ( " -b " , " --burst-file " , dest = " burst_file " ,
help = " File where the captured bursts are saved " )
parser . add_option ( " -c " , " --cfile " , dest = " cfile " ,
help = " File where the captured data are saved " )
bands_list = " , " . join ( grgsm . arfcn . get_bands ( ) )
parser . add_option ( " --band " , dest = " band " ,
help = " Specify the GSM band for the frequency. \n Available bands are: " + bands_list + " . \n If no band is specified, it will be determined automatically, defaulting to 0. " )
2015-08-24 18:00:48 +00:00
2016-02-14 19:24:54 +00:00
parser . add_option ( " " , " --args " , dest = " args " , type = " string " , default = " " ,
help = " Set device arguments [default= %d efault] " )
2015-08-24 18:00:48 +00:00
parser . add_option ( " -v " , " --verbose " , action = " store_true " ,
help = " If set, the captured bursts are printed to stdout " )
2015-09-20 11:53:06 +00:00
parser . add_option ( " -T " , " --rec-length " , dest = " rec_length " , type = " eng_float " ,
help = " Set length of recording in seconds [default= %d efault] " )
2015-08-22 17:52:41 +00:00
( options , args ) = parser . parse_args ( )
if options . cfile is None and options . burst_file is None :
parser . error ( " Please provide a cfile or a burst file (or both) to save the captured data \n " )
if ( options . fc is None and options . arfcn is None ) or ( options . fc is not None and options . arfcn is not None ) :
parser . error ( " You have to provide either a frequency or an ARFCN (but not both). \n " )
arfcn = 0
fc = 939.4e6
if options . arfcn :
if options . band :
if options . band not in grgsm . arfcn . get_bands ( ) :
parser . error ( " Invalid GSM band \n " )
elif not grgsm . arfcn . is_valid_arfcn ( options . arfcn , options . band ) :
parser . error ( " ARFCN is not valid in the specified band \n " )
else :
arfcn = options . arfcn
fc = grgsm . arfcn . arfcn2downlink ( arfcn , options . band )
else :
arfcn = options . arfcn
for band in grgsm . arfcn . get_bands ( ) :
if grgsm . arfcn . is_valid_arfcn ( arfcn , band ) :
fc = grgsm . arfcn . arfcn2downlink ( arfcn , band )
break
elif options . fc :
fc = options . fc
if options . band :
if options . band not in grgsm . arfcn . get_bands ( ) :
parser . error ( " Invalid GSM band \n " )
elif not grgsm . arfcn . is_valid_downlink ( options . fc , options . band ) :
parser . error ( " Frequency is not valid in the specified band \n " )
else :
arfcn = grgsm . arfcn . downlink2arfcn ( options . fc , options . band )
else :
for band in grgsm . arfcn . get_bands ( ) :
if grgsm . arfcn . is_valid_downlink ( options . fc , band ) :
arfcn = grgsm . arfcn . downlink2arfcn ( options . fc , band )
break
2016-02-13 06:31:51 +00:00
tb = grgsm_capture ( fc = fc , gain = options . gain , samp_rate = options . samp_rate ,
2015-08-24 18:00:48 +00:00
ppm = options . ppm , arfcn = arfcn , cfile = options . cfile ,
2015-09-20 11:53:06 +00:00
burst_file = options . burst_file , band = options . band , verbose = options . verbose ,
2016-03-18 10:39:09 +00:00
rec_length = options . rec_length , args = options . args )
2015-08-24 18:00:48 +00:00
def signal_handler ( signal , frame ) :
2015-08-22 17:52:41 +00:00
tb . stop ( )
2015-08-24 18:00:48 +00:00
tb . wait ( )
sys . exit ( 0 )
signal . signal ( signal . SIGINT , signal_handler )
2015-08-22 17:52:41 +00:00
2015-08-24 18:00:48 +00:00
tb . start ( )
tb . wait ( )