From 26645da7613da2c50675f2059c70811354a334a3 Mon Sep 17 00:00:00 2001 From: tvoid Date: Fri, 11 Apr 2008 08:23:48 -0600 Subject: -modified offset tuning to call only on good SCH -added burst_ff_single (from testing, unbuilt) -add complex capture in burst_sink_c -gsm_scan.py: added options for realtime, fusb settings - add auto offset adjustment for file input --- gsm-tvoid/src/lib/gsm_burst.cc | 28 +++---- gsm-tvoid/src/lib/gsm_burst.h | 27 +++---- gsm-tvoid/src/lib/gsm_burst_ff.cc | 2 +- gsm-tvoid/src/lib/gsm_burst_ff_single.cc | 118 +++++++++++++++++++++++++++++ gsm-tvoid/src/lib/gsm_burst_ff_single.h | 31 ++++++++ gsm-tvoid/src/lib/gsm_burst_sink_c.cc | 31 ++++++-- gsm-tvoid/src/lib/gsm_burst_sink_c.h | 4 + gsm-tvoid/src/python/gsm_scan.py | 125 +++++++++++++++++++------------ 8 files changed, 285 insertions(+), 81 deletions(-) create mode 100755 gsm-tvoid/src/lib/gsm_burst_ff_single.cc create mode 100755 gsm-tvoid/src/lib/gsm_burst_ff_single.h (limited to 'gsm-tvoid') diff --git a/gsm-tvoid/src/lib/gsm_burst.cc b/gsm-tvoid/src/lib/gsm_burst.cc index 3bef146..eb44bb4 100755 --- a/gsm-tvoid/src/lib/gsm_burst.cc +++ b/gsm-tvoid/src/lib/gsm_burst.cc @@ -621,8 +621,9 @@ int gsm_burst::get_burst(void) case PARTIAL_SCH: d_sync_state = WAIT_SCH; break; - case SCH: - d_sync_state = SYNCHRONIZED; + //case SCH: + //let the burst type switch handle this so it knows if new or old sync + // d_sync_state = SYNCHRONIZED; break; default: break; @@ -648,17 +649,6 @@ int gsm_burst::get_burst(void) d_fcch_count++; calc_freq_offset(); - -#ifndef TEST_TUNE_TIMING - if (p_tuner) { - if (SYNCHRONIZED == d_sync_state) - p_tuner->calleval(BURST_CB_ADJ_OFFSET); - else - p_tuner->calleval(BURST_CB_SYNC_OFFSET); - - } -#endif - d_ts = 0; break; case PARTIAL_SCH: @@ -668,6 +658,18 @@ int gsm_burst::get_burst(void) d_ts = 0; //TODO: check this break; case SCH: +#ifndef TEST_TUNE_TIMING + //TODO: it would be better to adjust tuning on first FCCH (for better SCH detection), + // but tuning can run away with false FCCHs + // Some logic to retune back to original offset on false FCCH might work + if (p_tuner) { + if (SYNCHRONIZED == d_sync_state) + p_tuner->calleval(BURST_CB_ADJ_OFFSET); + else + p_tuner->calleval(BURST_CB_SYNC_OFFSET); + + } +#endif d_burst_count++; d_sch_count++; d_last_sch = d_burst_count; diff --git a/gsm-tvoid/src/lib/gsm_burst.h b/gsm-tvoid/src/lib/gsm_burst.h index d6ab381..2e482d0 100755 --- a/gsm-tvoid/src/lib/gsm_burst.h +++ b/gsm-tvoid/src/lib/gsm_burst.h @@ -134,24 +134,25 @@ protected: BURST_TYPE get_sch_burst(void); BURST_TYPE get_norm_burst(void); - void shift_burst(int); - void calc_freq_offset(void); - void equalize(void); - float correlate_pattern(const float *,const int,const int,const int); - void diff_decode_burst(void); + virtual void shift_burst(int); + void calc_freq_offset(void); + virtual void equalize(void); + float correlate_pattern(const float *,const int,const int,const int); + void diff_decode_burst(void); - void sync_reset(void); + void sync_reset(void); - void print_bits(const float *data,int length); - void print_hex(const unsigned char *data,int length); - void soft2hardbit(char *dst, const float *data, int len); - void print_burst(void); + void print_bits(const float *data,int length); + void print_hex(const unsigned char *data,int length); - void diff_encode(const float *in,float *out,int length,float lastbit = 1.0); - void diff_decode(const float *in,float *out,int length,float lastbit = 1.0); + void soft2hardbit(char *dst, const float *data, int len); //need this? + void print_burst(void); + + void diff_encode(const float *in,float *out,int length,float lastbit = 1.0); + void diff_decode(const float *in,float *out,int length,float lastbit = 1.0); public: - ~gsm_burst (); + virtual ~gsm_burst (); ////// General Stats //TODO: Maybe there should be a burst_stats class? diff --git a/gsm-tvoid/src/lib/gsm_burst_ff.cc b/gsm-tvoid/src/lib/gsm_burst_ff.cc index 89b513f..dd449f1 100755 --- a/gsm-tvoid/src/lib/gsm_burst_ff.cc +++ b/gsm-tvoid/src/lib/gsm_burst_ff.cc @@ -38,7 +38,7 @@ void gsm_burst_ff::forecast (int noutput_items, gr_vector_int &ninput_items_requ { unsigned ninputs = ninput_items_required.size (); for (unsigned i = 0; i < ninputs; i++) - ninput_items_required[i] = noutput_items * BBUF_SIZE + history(); + ninput_items_required[i] = noutput_items * BBUF_SIZE; } diff --git a/gsm-tvoid/src/lib/gsm_burst_ff_single.cc b/gsm-tvoid/src/lib/gsm_burst_ff_single.cc new file mode 100755 index 0000000..ff6c354 --- /dev/null +++ b/gsm-tvoid/src/lib/gsm_burst_ff_single.cc @@ -0,0 +1,118 @@ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include +#include +#include +#include +#include + +gsm_burst_ff_sptr gsm_make_burst_ff (gr_feval_ll *t) +{ + return gsm_burst_ff_sptr (new gsm_burst_ff(t)); +} + +static const int MIN_IN = 1; // minimum number of input streams +static const int MAX_IN = 1; // maximum number of input streams +static const int MIN_OUT = 0; // minimum number of output streams +static const int MAX_OUT = 1; // maximum number of output streams + +gsm_burst_ff::gsm_burst_ff (gr_feval_ll *t) : + gsm_burst(t), + gr_block( "burst_ff", + gr_make_io_signature (MIN_IN, MAX_IN, sizeof (float)), +// gr_make_io_signature (MIN_OUT, MAX_OUT, USEFUL_BITS * sizeof (float))) + gr_make_io_signature (0, 0, 0)) +// gr_make_io_signature (MIN_OUT, MAX_OUT, sizeof (float))) +{ + + set_history(1); + +} + +gsm_burst_ff::~gsm_burst_ff () +{ +} + +/* +void gsm_burst_ff::forecast (int noutput_items, gr_vector_int &ninput_items_required) +{ + unsigned ninputs = ninput_items_required.size (); + for (unsigned i = 0; i < ninputs; i++) + ninput_items_required[i] = noutput_items * BBUF_SIZE + history(); +} +*/ + +int gsm_burst_ff::general_work (int noutput_items, + gr_vector_int &ninput_items, + gr_vector_const_void_star &input_items, + gr_vector_void_star &output_items) +{ + const float *in = (const float *) input_items[0]; + float *out = (float *) output_items[0]; + + int ii=0; + //int rval = 0; //default to no output + int rval = noutput_items; //default to no output + + //int do_output = output_items.size() > 0 ? 1 : 0; + int do_output = 0; + + int n_input = ninput_items[0]; +// fprintf(stderr,"out=%8.8x/#i=%d/#o=%d",(unsigned)out,n_input,noutput_items); +// fprintf(stderr,"#i=%d/#o=%d",n_input,noutput_items); + +// while (( rval < noutput_items) && ( ii < n_input ) ) { + while ( ii < n_input ) { + + assert(d_bbuf_pos <= BBUF_SIZE ); + + if (d_bbuf_pos >= 0) //could have been offset negative. TODO: perhaps better just to add some slack to the buffer + d_burst_buffer[d_bbuf_pos] = in[ii]; + + d_bbuf_pos++; + + if ( d_bbuf_pos >= BBUF_SIZE ) { + + if (get_burst()) { + //found a burst, send to output + if (do_output) { + //ensure that output data is in range + int b = d_burst_start; + if (b < 0) + b = 0; + else if (b >= 2 * MAX_CORR_DIST) + b = 2 * MAX_CORR_DIST - 1; + + memcpy(out+rval*USEFUL_BITS, d_burst_buffer + b, USEFUL_BITS*sizeof(float)); + } + //rval++; + //rval += USEFUL_BITS*sizeof(float); + + switch ( d_clock_options & QB_MASK ) { + case QB_QUARTER: //Can't do this in the FF version + case QB_FULL04: //extra bit on timeslot 0 & 4 + if (!(d_ts%4)) + d_bbuf_pos--; + break; + case QB_NONE: //don't adjust for quarter bits at all + default: + break; + } + + d_last_burst_s_count = d_sample_count; + + } + } + d_sample_count++; + ii++; + } + +// fprintf(stderr,"/ii=%d/rval=%d\n",ii,rval); + + consume_each (ii); + + return rval; +} diff --git a/gsm-tvoid/src/lib/gsm_burst_ff_single.h b/gsm-tvoid/src/lib/gsm_burst_ff_single.h new file mode 100755 index 0000000..553fe65 --- /dev/null +++ b/gsm-tvoid/src/lib/gsm_burst_ff_single.h @@ -0,0 +1,31 @@ +#ifndef INCLUDED_GSM_BURST_FF_H +#define INCLUDED_GSM_BURST_FF_H + +#include +#include + +class gsm_burst_ff; + +typedef boost::shared_ptr gsm_burst_ff_sptr; + +gsm_burst_ff_sptr gsm_make_burst_ff(gr_feval_ll *); + +class gsm_burst_ff : public gr_block, public gsm_burst +{ +private: + + friend gsm_burst_ff_sptr gsm_make_burst_ff(gr_feval_ll *); + gsm_burst_ff(gr_feval_ll *t); + +public: + ~gsm_burst_ff (); + +// void forecast (int noutput_items, gr_vector_int &ninput_items_required); + + int general_work ( int noutput_items, + gr_vector_int &ninput_items, + gr_vector_const_void_star &input_items, + gr_vector_void_star &output_items); +}; + +#endif /* INCLUDED_GSM_BURST_FF_H */ diff --git a/gsm-tvoid/src/lib/gsm_burst_sink_c.cc b/gsm-tvoid/src/lib/gsm_burst_sink_c.cc index 103c09f..58787bb 100755 --- a/gsm-tvoid/src/lib/gsm_burst_sink_c.cc +++ b/gsm-tvoid/src/lib/gsm_burst_sink_c.cc @@ -49,7 +49,26 @@ gsm_burst_sink_c::~gsm_burst_sink_c () delete d_interp; } +void gsm_burst_sink_c::shift_burst(int shift_bits) +{ + //fprintf(stderr,"sft:%d\n",shift_bits); + + assert(shift_bits >= 0); + assert(shift_bits < BBUF_SIZE ); + + gr_complex *p_src = d_complex_burst + shift_bits; + gr_complex *p_dst = d_complex_burst; + int num = BBUF_SIZE - shift_bits; + + memmove(p_dst,p_src,num * sizeof(gr_complex)); //need memmove because of overlap + //DON'T adjust the buffer positions, the superclass method will do that... + //d_bbuf_pos -= shift_bits; + //call the parent method to shift the float version + gsm_burst::shift_burst(shift_bits); +} + +//TODO: put everything but GR stuff in a common complex type class (share w/ gsm_burst_cf) int gsm_burst_sink_c::work(int noutput_items, gr_vector_const_void_star &input_items, gr_vector_void_star &output_items) @@ -61,15 +80,9 @@ int gsm_burst_sink_c::work(int noutput_items, assert( d_ii >= 0 ); -// while (( rval < noutput_items) && ( ii < ni ) ) { while ( d_ii < noutput_items ) { -// while ( oo++ < noutput_items ) { //clock symbols - //TODO: this is very basic and can be improved. Need tracking... - //TODO: use burst_start offsets as timing feedback - //TODO: save complex samples for Viterbi EQ - - //from m&m + gr_complex sample = d_interp->interpolate (&in[d_ii], d_mu); gr_complex conjprod = sample * conj(d_last_sample); @@ -80,8 +93,10 @@ int gsm_burst_sink_c::work(int noutput_items, #if 1 assert(d_bbuf_pos <= BBUF_SIZE ); - if (d_bbuf_pos >= 0) //could be negative offset from burst alignment. TODO: perhaps better just to add some padding to the buffer + if (d_bbuf_pos >= 0) { //could be negative offset from burst alignment. TODO: perhaps better just to add some padding to the buffer d_burst_buffer[d_bbuf_pos] = diff_angle; + d_complex_burst[d_bbuf_pos] = sample; + } d_bbuf_pos++; diff --git a/gsm-tvoid/src/lib/gsm_burst_sink_c.h b/gsm-tvoid/src/lib/gsm_burst_sink_c.h index 5918dc6..1197a2c 100755 --- a/gsm-tvoid/src/lib/gsm_burst_sink_c.h +++ b/gsm-tvoid/src/lib/gsm_burst_sink_c.h @@ -30,6 +30,10 @@ private: int d_ii; //save index between work calls for interp advance gri_mmse_fir_interpolator_cc *d_interp; //sub-sample interpolator from GR + + gr_complex d_complex_burst[BBUF_SIZE]; + + void shift_burst(int shift_bits); public: ~gsm_burst_sink_c (); diff --git a/gsm-tvoid/src/python/gsm_scan.py b/gsm-tvoid/src/python/gsm_scan.py index 2dcf538..b98bf43 100755 --- a/gsm-tvoid/src/python/gsm_scan.py +++ b/gsm-tvoid/src/python/gsm_scan.py @@ -1,13 +1,15 @@ #!/usr/bin/env python # TODO: -# * Adjust offset by PPM -# * Auto-tune offset (add option to enable) # * Add status info to window (frequency, offset, etc) # * Put direct frequency tuning back # * Add rate-limited file reads (throttle?) # * Make console only version # * Reset burst_stats on retune # * Add better option checking +# * Wideband (multi-channel) processing (usrp and/or file input) +# * Automatic beacon scan (quick scan RSSI, then check for BCCH) +# * AGC +# * Refactor, this is too messy import sys @@ -31,40 +33,50 @@ class burst_callback(gr.feval_ll): def __init__(self, fg): gr.feval_ll.__init__(self) self.fg = fg - self.offset_mean_num = 30 #number of FCCH offsets to average + self.offset_mean_num = 10 #number of FCCH offsets to average self.offset_vals = [] def eval(self, x): + #print "burst_callback: eval(",x,")\n"; try: #TODO: rework so this will work on file input if gsm.BURST_CB_SYNC_OFFSET == x: - last_offset = self.fg.burst.last_freq_offset() - self.fg.offset -= last_offset - print "burst_callback: SYNC_OFFSET:", last_offset, " ARFCN: ", self.fg.arfcn, "\n"; - self.fg.set_channel(self.fg.arfcn) + #print "burst_callback: SYNC_OFFSET\n"; + if self.fg.options.tuning.count("o"): + last_offset = self.fg.burst.last_freq_offset() + self.fg.offset -= last_offset + #print "burst_callback: SYNC_OFFSET:", last_offset, " ARFCN: ", self.fg.channel, "\n"; + self.fg.set_channel(self.fg.channel) elif gsm.BURST_CB_ADJ_OFFSET == x: last_offset = self.fg.burst.last_freq_offset() - #print "burst_callback: ADJ_OFFSET:", last_offset, " ARFCN: ", self.fg.arfcn, "\n"; self.offset_vals.append(last_offset) - - if len(self.offset_vals) >= self.offset_mean_num: + count = len(self.offset_vals) + #print "burst_callback: ADJ_OFFSET:", last_offset, ", count=",count,"\n"; + + if count >= self.offset_mean_num: sum = 0.0 while len(self.offset_vals): sum += self.offset_vals.pop(0) - - mean_offset = sum / self.offset_mean_num - self.fg.offset -= mean_offset - + + self.fg.mean_offset = sum / self.offset_mean_num + + #print "burst_callback: mean offset:", self.fg.mean_offset, "\n"; + #retune if greater than 100 Hz - if mean_offset > 100.0: - print "burst_callback: mean offset adjust:", mean_offset, "\n"; - self.fg.set_channel(self.fg.arfcn) - - elif gsm.BURST_CB_TUNE == x: - print "burst_callback: BURST_CB_TUNE: ARFCN: ", self.fg.burst.next_arfcn, "\n"; - self.fg.set_channel(self.fg.burst.next_arfcn) + if abs(self.fg.mean_offset) > 100.0: + #print "burst_callback: mean offset adjust:", self.fg.mean_offset, "\n"; + if self.fg.options.tuning.count("o"): + #print "burst_callback: tuning.\n"; + self.fg.offset -= self.fg.mean_offset + self.fg.set_channel(self.fg.channel) + + elif gsm.BURST_CB_TUNE == x and self.fg.options.tuning.count("h"): + #print "burst_callback: BURST_CB_TUNE: ARFCN: ", self.fg.burst.next_arfcn, "\n"; + if self.fg.options.tuning.count("h"): + #print "burst_callback: tuning.\n"; + self.fg.set_channel(self.fg.burst.next_arfcn) return 0 @@ -153,6 +165,11 @@ class app_flow_graph(stdgui.gui_flow_graph): help="Type of timing techniques to use. [default=%default] \n" + "(n)one, (c)orrelation track, (q)uarter bit, (f)ull04 ") + #tuning options + parser.add_option("-T", "--tuning", type="string", default="oh", + help="Type of tuning to perform. [default=%default] \n" + + "(n)one, (o)ffset adjustment, (h)opping ") + #file options parser.add_option("-I", "--inputfile", type="string", default=None, help="Select a capture file to read") @@ -164,10 +181,17 @@ class app_flow_graph(stdgui.gui_flow_graph): #usrp options parser.add_option("-R", "--rx-subdev-spec", type="subdev", default=None, help="Select USRP Rx side A or B (default=first one with a daughterboard)") + parser.add_option("--fusb-block-size", type="int", default=0, + help="Set USRP blocksize") + parser.add_option("--fusb-nblocks", type="int", default=0, + help="Set USRP block buffers") + parser.add_option("--realtime",action="store_true", dest="realtime", + help="Use realtime scheduling.") + #FIXME: gain not working? parser.add_option("-g", "--gain", type="eng_float", default=None, help="Set gain in dB (default is midpoint)") - parser.add_option("-c", "--channel", type="int", default=None, + parser.add_option("-c", "--channel", type="int", default=1, help="Tune to GSM ARFCN. Overrides --freq") parser.add_option("-r", "--region", type="string", default="u", help="Frequency bands to use for channels. (u)s or (e)urope [default=%default]") @@ -183,6 +207,8 @@ class app_flow_graph(stdgui.gui_flow_graph): self.region = options.region self.channel = options.channel self.offset = options.offset + + self.mean_offset = 0.0 #this is relative averaged freq offset self.print_status = options.print_console.count('s') @@ -213,25 +239,27 @@ class app_flow_graph(stdgui.gui_flow_graph): gsm_symb_rate = 1625000.0 / 6.0 sps = input_rate/gsm_symb_rate - # Attempt to enable realtime scheduling - r = gr.enable_realtime_scheduling() - if r == gr.RT_OK: - realtime = True - print "Realtime scheduling ENABLED" - else: - realtime = False - print "Realtime scheduling FAILED" - -# if options.fusb_block_size == 0 and options.fusb_nblocks == 0: - if realtime: # be more aggressive -# options.fusb_block_size = gr.prefs().get_long('fusb', 'rt_block_size', 1024) - options.fusb_block_size = gr.prefs().get_long('fusb', 'rt_block_size', 512) - options.fusb_nblocks = gr.prefs().get_long('fusb', 'rt_nblocks', 16) + if options.realtime: + r = gr.enable_realtime_scheduling() + if r == gr.RT_OK: + realtime = True + print "Realtime scheduling ENABLED" + else: + realtime = False + print "Realtime scheduling FAILED" + + #set resonable defaults if no user prefs set + if options.realtime: # be more aggressive + if options.fusb_block_size == 0: + options.fusb_block_size = gr.prefs().get_long('fusb', 'rt_block_size', 1024) + if options.fusb_nblocks == 0: + options.fusb_nblocks = gr.prefs().get_long('fusb', 'rt_nblocks', 16) else: -# options.fusb_block_size = gr.prefs().get_long('fusb', 'block_size', 4096) - options.fusb_block_size = gr.prefs().get_long('fusb', 'block_size', 1024) - options.fusb_nblocks = gr.prefs().get_long('fusb', 'nblocks', 32) + if options.fusb_block_size == 0: + options.fusb_block_size = gr.prefs().get_long('fusb', 'block_size', 4096) + if options.fusb_nblocks == 0: + options.fusb_nblocks = gr.prefs().get_long('fusb', 'nblocks', 16) print "fusb_block_size =", options.fusb_block_size print "fusb_nblocks =", options.fusb_nblocks @@ -274,6 +302,7 @@ class app_flow_graph(stdgui.gui_flow_graph): else: offset = 0.0 + #TODO: try a differnet filter for latency (and CPU) filter_taps = gr.firdes.low_pass(1.0, input_rate, filter_cutoff, filter_t_width, gr.firdes.WIN_HAMMING) self.filter = gr.freq_xlating_fir_filter_ccf(1, filter_taps, offset, input_rate) @@ -487,9 +516,15 @@ class app_flow_graph(stdgui.gui_flow_graph): def set_freq(self, freq): - + #TODO: for wideband processing, determine if the desired freq is within our current sample range. + # If so, use the frequency translator to tune. Tune the USRP otherwise. + # Maybe have a flag to force tuning the USRP? + if not self.using_usrp: - return False + #if reading from file just adjust for offset in the freq translator + print "Setting filter center freq to offset: ", self.offset, "\n" + self.filter.set_center_freq(self.offset) + return True freq = freq - self.offset @@ -511,10 +546,7 @@ class app_flow_graph(stdgui.gui_flow_graph): def set_channel(self, chan): - self.arfcn = chan - - if not self.using_usrp: - return False + self.chan = chan freq = get_freq_from_arfcn(chan,self.region) @@ -531,7 +563,7 @@ class app_flow_graph(stdgui.gui_flow_graph): print "======== STATS =========" print 'freq_offset: ',self.offset - print 'mean_offset: ',self.burst.mean_freq_offset() + print 'mean_offset: ',self.mean_offset print 'sync_loss_count:',self.burst.d_sync_loss_count print 'total_bursts: ',n_total print 'fcch_count: ',self.burst.d_fcch_count @@ -553,6 +585,7 @@ class app_flow_graph(stdgui.gui_flow_graph): self.print_stats() def on_idle(self, event): + #We can't update this while in the tune functions since they can be invoked by callbaks and the GUI croaks... self._set_status_msg(self.status_msg) #print "Idle.\n"; -- cgit v1.2.3