Major simplification and some argument changes in grgsm_capture

grmgs_capture tried to do too many things for a simple recorder.
It was simplified by removing the receiver and ability to
save data to bursts files.

All other stuff that is not necessary for recording signal to disk was
also removed:
-setters/getters,
-storing of parameters that never will be changed.

The 'fc' parameter name was changed to 'freq' to follow GNU Radio
guidelines.

The 'shiftoff' parameter was removed.

'bb_gain' and 'if_gain' parameters were added.

Variables specific to some of SDR's like:
-gains at different stages,
-bandwidth (not all devices can set it),
-antennas (some devices have just one, some not),
were moved to separate options group.

What is left to be exposed is:
-dc_offset_mode,
-iq_balance_mode,
-gain_mode.

Change-Id: I092a43eaddb09a99c6cc05fde13f0ae94d9e0251
This commit is contained in:
Piotr Krysik 2018-09-20 14:49:27 +02:00
parent 57431ed50c
commit 81ba56f59d
1 changed files with 117 additions and 171 deletions

View File

@ -2,6 +2,7 @@
# -*- coding: utf-8 -*-
# @file
# @author (C) 2015 by Roman Khassraf <rkhassraf@gmail.com>
# (C) 2019 by Piotr Krysik <ptrkrysik@gmail.com>
# @section LICENSE
#
# Gr-gsm is free software; you can redistribute it and/or modify
@ -26,217 +27,162 @@ 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
from optparse import OptionParser, OptionGroup
import osmosdr
import pmt
import time
import grgsm
import signal
import sys
class grgsm_capture(gr.top_block):
def __init__(self, fc, gain, samp_rate, ppm, arfcn, cfile=None, burst_file=None, verbose=False, rec_length=None, args=""):
def __init__(self,
freq,
gain=30,
samp_rate=1e6,
rec_length=float('Inf'),
freq_corr=0,
output_filename=None,
bandwidth=0,
bb_gain=20,
if_gain=20,
antenna="",
device_args=""):
gr.top_block.__init__(self, "Gr-gsm Capture")
##################################################
# 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
self.verbose = verbose
self.shiftoff = shiftoff = 400e3
self.rec_length = rec_length
##################################################
# Processing Blocks
# Setting up RF source
##################################################
self.rtlsdr_source = osmosdr.source( args="numchan=" + str(1) + " " + args )
self.rtlsdr_source.set_sample_rate(samp_rate)
self.rtlsdr_source.set_center_freq(fc - shiftoff, 0)
self.rtlsdr_source.set_freq_corr(ppm, 0)
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)
self.rtlsdr_source.set_gain(gain, 0)
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)
if self.rec_length is not None:
self.blocks_head_0 = blocks.head(gr.sizeof_gr_complex, int(samp_rate*rec_length))
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,
)
self.gsm_clock_offset_control = grgsm.clock_offset_control(fc-shiftoff, samp_rate, osr=4)
self.sdr_source = \
osmosdr.source(args="numchan=1" + device_args )
self.sdr_source.set_sample_rate(samp_rate)
self.sdr_source.set_center_freq(freq, 0)
self.sdr_source.set_freq_corr(freq_corr, 0)
self.sdr_source.set_dc_offset_mode(2, 0)
self.sdr_source.set_iq_balance_mode(2, 0)
self.sdr_source.set_gain_mode(True, 0)
self.sdr_source.set_gain(gain, 0)
self.sdr_source.set_if_gain(if_gain, 0)
self.sdr_source.set_bb_gain(bb_gain, 0)
self.sdr_source.set_antenna("", 0)
if bandwidth != 0:
self.sdr_source.set_bandwidth(bandwidth, 0)
if self.burst_file:
self.gsm_burst_file_sink = grgsm.burst_file_sink(self.burst_file)
##################################################
# The rest of processing blocks
##################################################
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 rec_length != float('Inf'):
self.head = \
blocks.head(gr.sizeof_gr_complex, int(samp_rate*rec_length))
if self.verbose:
self.gsm_bursts_printer_0 = grgsm.bursts_printer(pmt.intern(""),
False, False, False, False)
self.file_sink = blocks.file_sink(gr.sizeof_gr_complex*1, \
output_filename, False)
self.file_sink.set_unbuffered(False)
##################################################
# Connections
##################################################
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))
if rec_length != float('Inf'): #if recording length is not infinite
#connect head block after the source
self.connect((self.sdr_source, 0), (self.head, 0))
self.connect((self.head, 0), (self.file_sink, 0))
else:
self.connect((self.rtlsdr_source, 0), (self.blocks_rotator, 0))
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))
self.msg_connect(self.gsm_clock_offset_control, "ctrl", self.gsm_input, "ctrl_in")
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)
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])
new_freq = grgsm.arfcn.arfcn2downlink(self.arfcn)
self.set_fc(new_freq)
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)
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))
self.connect((self.sdr_source, 0), (self.file_sink, 0))
if __name__ == '__main__':
parser = OptionParser(option_class=eng_option, usage="%prog [options]",
parser = OptionParser(option_class=eng_option, usage="%prog [options] output_filename",
description="RTL-SDR capturing app of gr-gsm.")
parser.add_option("-f", "--fc", dest="fc", type="eng_float",
parser.add_option("-f", "--freq", dest="freq", type="eng_float",
help="Set frequency [default=%default]")
parser.add_option("-a", "--arfcn", dest="arfcn", type="intx",
help="Set ARFCN instead of frequency (for PCS1900 add 0x8000 (2**15) to the ARFCN number)")
parser.add_option("-g", "--gain", dest="gain", type="eng_float",
parser.add_option("-a", "--arfcn", dest="arfcn", type="intx",
help="Set ARFCN instead of frequency (for PCS1900 add"
"0x8000 (2**15) to the ARFCN number)")
parser.add_option("-g", "--gain", dest="gain", type="eng_float",
default=eng_notation.num_to_str(30),
help="Set gain [default=%default]")
parser.add_option("-s", "--samp-rate", dest="samp_rate", type="eng_float",
parser.add_option("-s", "--samp-rate", dest="samp_rate", type="eng_float",
default=eng_notation.num_to_str(1000000),
help="Set samp_rate [default=%default]")
parser.add_option("-p", "--ppm", dest="ppm", type="intx", default=0,
help="Set ppm [default=%default]")
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")
parser.add_option("", "--args", dest="args", type="string", default="",
help="Set device arguments [default=%default]")
parser.add_option("-v", "--verbose", action="store_true",
help="If set, the captured bursts are printed to stdout")
parser.add_option("-T", "--rec-length", dest="rec_length", type="float",
default='Inf', help="Set length of recording in seconds "
"[default=infinity]")
parser.add_option("-T", "--rec-length", dest="rec_length", type="eng_float",
help="Set length of recording in seconds [default=%default]")
parser.add_option("-p", "--freq-corr", dest="freq_corr", type="eng_float",
default="0", help="Set frequency correction in"
" ppm [default=%default]")
osmogroup = OptionGroup(parser, 'Additional osmosdr source options',
"Options specific to a subset of SDR receivers "
"supported by osmosdr source.")
osmogroup.add_option("-w", "--bandwidth", dest="bandwidth", type="eng_float",
default="0", help="Set bandwidth [default=samp_rate]")
osmogroup.add_option("", "--bb-gain", dest="bb_gain", type="eng_float",
default=eng_notation.num_to_str(20),
help="Set baseband gain [default=%default]")
osmogroup.add_option("", "--if-gain", dest="if_gain", type="eng_float",
default=eng_notation.num_to_str(20),
help="Set intermediate freque gain [default=%default]")
osmogroup.add_option("", "--ant", dest="antenna", type="string",
default="", help="Set antenna "
"[default=%default]")
osmogroup.add_option("", "--args", dest="device_args", type="string",
default="", help="Set device arguments "
"[default=%default]")
parser.add_option_group(osmogroup)
(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")
if not args:
parser.error("Please provide an output file name to save the captured data\n")
output_filename = args[0]
if (options.freq is None and options.arfcn is None) or \
(options.freq 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
freq = 0
if options.arfcn:
if not grgsm.arfcn.is_valid_arfcn(options.arfcn):
parser.error("ARFCN is not valid\n")
else:
arfcn = options.arfcn
fc = grgsm.arfcn.arfcn2downlink(arfcn)
elif options.fc:
fc = options.fc
arfcn = grgsm.arfcn.downlink2arfcn(options.fc)
tb = grgsm_capture(fc=fc, gain=options.gain, samp_rate=options.samp_rate,
ppm=options.ppm, arfcn=arfcn, cfile=options.cfile,
burst_file=options.burst_file, verbose=options.verbose,
rec_length=options.rec_length, args=options.args)
freq = grgsm.arfcn.arfcn2downlink(options.arfcn)
elif options.freq:
freq = options.freq
tb = grgsm_capture(freq=freq,
gain=options.gain,
freq_corr=options.freq_corr,
samp_rate=options.samp_rate,
output_filename=output_filename,
rec_length=options.rec_length,
bandwidth=options.bandwidth,
bb_gain=options.bb_gain,
if_gain=options.if_gain,
antenna=options.antenna,
device_args=options.device_args)
def signal_handler(signal, frame):
tb.stop()
tb.wait()
tb.wait()
sys.exit(0)
signal.signal(signal.SIGINT, signal_handler)
tb.start()