diff options
Diffstat (limited to 'gsm-tvoid/src/lib/gsm_burst.cc')
-rwxr-xr-x | gsm-tvoid/src/lib/gsm_burst.cc | 766 |
1 files changed, 766 insertions, 0 deletions
diff --git a/gsm-tvoid/src/lib/gsm_burst.cc b/gsm-tvoid/src/lib/gsm_burst.cc new file mode 100755 index 0000000..c52ff38 --- /dev/null +++ b/gsm-tvoid/src/lib/gsm_burst.cc @@ -0,0 +1,766 @@ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include "gsm_burst.h" +#include <gr_math.h> +#include <stdio.h> +#include <math.h> +#include <memory.h> +#include <assert.h> +#include "system.h" +#include "gsmstack.h" + + +gsm_burst::gsm_burst (gr_feval_ll *t) : + p_tuner(t), + d_clock_options(DEFAULT_CLK_OPTS), + d_print_options(0), + d_test_options(0), + d_hop_good_arfcn(1), + d_hop_bad_arfcn(2), + d_equalizer_type(EQ_FIXED_DFE) +{ + +// fprintf(stderr,"gsm_burst: enter constructor (t=%8.8x)\n",(unsigned int)t); + +// M_PI = M_PI; //4.0 * atan(1.0); + + full_reset(); + + //encode sync bits + float tsync[N_SYNC_BITS]; + + for (int i=0; i < N_SYNC_BITS; i++) { + tsync[i] = 2.0*SYNC_BITS[i] - 1.0; + } + + diff_encode(tsync,corr_sync,N_SYNC_BITS); + +/* + fprintf(stderr," Sync: "); + print_bits(tsync,N_SYNC_BITS); + fprintf(stderr,"\n"); + + fprintf(stderr,"DSync: "); + print_bits(corr_sync,N_SYNC_BITS); + fprintf(stderr,"\n\n"); +*/ + + for (int i=0; i < 10; i++) { + for (int j=0; j < N_TRAIN_BITS; j++) { + tsync[j] = 2.0*train_seq[i][j] - 1.0; + } + diff_encode(tsync,corr_train_seq[i],N_TRAIN_BITS); + +/* + fprintf(stderr,"TSC%d: ",i); + print_bits(corr_train_seq[i],N_TRAIN_BITS); + fprintf(stderr,"\n"); +*/ + } + + /* Initialize GSM Stack */ + GS_new(&d_gs_ctx); +} + +gsm_burst::~gsm_burst () +{ +} + +void gsm_burst::sync_reset(void) +{ + d_sync_state = WAIT_FCCH; + d_last_good = 0; + d_last_sch = 0; + d_burst_count = 0; +} + +//TODO: check this for thread safeness +void gsm_burst::full_reset(void) +{ + sync_reset(); + + d_sync_loss_count=0; + d_fcch_count=0; + d_part_sch_count=0; + d_sch_count=0; + d_normal_count=0; + d_dummy_count=0; + d_unknown_count=0; + d_total_count=0; + + d_freq_offset=0.0; + d_freq_off_sum=0.0; + d_freq_off_weight=0; + + d_ts=0; + + d_bbuf_pos=0; + d_burst_start=MAX_CORR_DIST; + d_sample_count=0; + d_last_burst_s_count=0; + d_corr_pattern=0; + d_corr_pat_size=0; + d_corr_max=0.0; + d_corr_maxpos=0; + d_corr_center=0; + d_last_sync_state=WAIT_FCCH; + +} + +float gsm_burst::mean_freq_offset(void) +{ + if (d_freq_off_weight) + return d_freq_off_sum / d_freq_off_weight; + else + return 0.0; +} + +void gsm_burst::diff_encode(const float *in,float *out,int length,float lastbit) { + + for (int i=0; i < length; i++) { + out[i] = in[i] * lastbit; + lastbit=in[i]; + + } +} + +void gsm_burst::diff_decode(const float *in,float *out,int length,float lastbit) { + + for (int i=0; i < length; i++) { + out[i] = in[i] * lastbit; + lastbit = out [i]; + } +} + +void gsm_burst::diff_decode_burst(void) { + char lastbit = 0; + + //slice + for (int i = 0; i < USEFUL_BITS; i++) { + d_decoded_burst[i] = d_burst_buffer[d_burst_start + i] > 0 ? 0 : 1; + } + + //diff decode + for (int i=0; i < USEFUL_BITS; i++) { + d_decoded_burst[i] ^= lastbit; + lastbit = d_decoded_burst[i]; + } + +} + +void gsm_burst::print_hex(const unsigned char *data,int length) +{ + unsigned char tbyte; + int i,bitpos=0; + + assert(data); + assert(length >= 0); + + + + while (bitpos < length) { + tbyte = 0; + for (i=0; (i < 8) && (bitpos < length); i++) { + tbyte <<= 1; + tbyte |= data[bitpos++]; + } + if (i<8) + tbyte <<= 8 - i; + + fprintf(stdout,"%2.2X ",tbyte); + } +} + +void gsm_burst::print_bits(const float *data,int length) +{ + assert(data); + assert(length >= 0); + + for (int i=0; i < length; i++) + data[i] < 0 ? fprintf(stderr,"+") : fprintf(stderr,"."); + +} + +#if 0 +void gsm_burst::soft2hardbit(char *dst, const float *data, int len) +{ + for (int i=0; i < len; i++) + { + if (data[i] < 0) + dst[i] = 0; + else + dst[i] = 1; + } +} +#endif + +void gsm_burst::print_burst(void) +{ + int bursts_since_sch; + + int print = 0; + + //fprintf(stderr,"p=%8.8X ", d_print_options); + + if ( PRINT_GSM_DECODE & d_print_options ) { + + /* + * Pass information to GSM stack. GSM stack will try to extract + * information (fn, layer 2 messages, ...) + */ + diff_decode_burst(); + GS_process(&d_gs_ctx, d_ts, d_burst_type, d_decoded_burst); + } + + if ( PRINT_EVERYTHING == d_print_options ) + print = 1; + else if ( (!d_ts) && (d_print_options & PRINT_TS0) ) + print = 1; + else if ( (DUMMY == d_burst_type) && (d_print_options & PRINT_DUMMY) ) + print = 1; + else if ( (NORMAL == d_burst_type) && (d_print_options & PRINT_NORMAL) ) + print = 1; + else if ( (SCH == d_burst_type) && (d_print_options & PRINT_SCH) ) + print = 1; + else if ( (FCCH == d_burst_type) && (d_print_options & PRINT_FCCH) ) + print = 1; + else if ( (UNKNOWN == d_burst_type) && (d_print_options & PRINT_UNKNOWN) ) + print = 1; + + if ( print && (d_print_options & PRINT_BITS) ) { + if (d_print_options & PRINT_ALL_BITS) + { + print_bits(d_burst_buffer,BBUF_SIZE); + } else { + /* 142 useful bits: 2*58 + 26 training */ + print_bits(d_burst_buffer + d_burst_start,USEFUL_BITS); + } + + fprintf(stderr," "); + } + + if (print) { + + fprintf(stderr,"%d/%d/%+d/%lu/%lu ", + d_sync_state, + d_ts, + d_burst_start - MAX_CORR_DIST, + d_sample_count, + d_sample_count - d_last_burst_s_count); + + switch (d_burst_type) { + case FCCH: + fprintf(stderr,"[FCCH] foff:%g cnt:%lu",d_freq_offset,d_fcch_count); + break; + case PARTIAL_SCH: + bursts_since_sch = d_burst_count - d_last_sch; + + fprintf(stderr,"[P-SCH] cor:%.2f last:%d cnt: %lu", + d_corr_max,bursts_since_sch,d_sch_count); + break; + case SCH: + bursts_since_sch = d_burst_count - d_last_sch; + + fprintf(stderr,"[SCH] cor:%.2f last:%d cnt: %lu", + d_corr_max,bursts_since_sch,d_sch_count); + break; + case DUMMY: + fprintf(stderr,"[DUMMY] cor:%.2f",d_corr_max); + break; + case ACCESS: + fprintf(stderr,"[ACCESS]"); //We don't detect this yet + break; + case NORMAL: + fprintf(stderr,"[NORM] clr:%d cor:%.2f",d_color_code,d_corr_max); + break; + case UNKNOWN: + fprintf(stderr,"[?]"); + break; + default: + fprintf(stderr,"[oops! default]"); + break; + } + + fprintf(stderr,"\n"); + + + //print the correlation pattern for visual inspection + if ( (UNKNOWN != d_burst_type) && + (d_sync_state > WAIT_SCH_ALIGN) && + (d_print_options & PRINT_CORR_BITS) ) + { + + int pat_indent; + + if (d_print_options & PRINT_ALL_BITS) + pat_indent = d_corr_center + d_corr_maxpos; + else + pat_indent = d_corr_center - MAX_CORR_DIST; //useful bits will already be offset + + for (int i = 0; i < pat_indent; i++) + fprintf(stderr," "); + + fprintf(stderr," "); //extra space for skipped bit + print_bits(d_corr_pattern+1,d_corr_pat_size-1); //skip first bit (diff encoding) + + fprintf(stderr,"\t\toffset:%d, max: %.2f \n",d_corr_maxpos,d_corr_max); + } + + } + + //Print Burst data in hex + if ( d_print_options & PRINT_HEX ) { + fprintf(stdout,"%d,%d,",d_ts,d_burst_type); + diff_decode_burst(); + print_hex(d_decoded_burst,USEFUL_BITS); + fprintf(stdout,"\n"); + } + + //Print State related messages + if ( d_print_options & PRINT_STATE ) { + if ( (SYNCHRONIZED == d_sync_state) && (SYNCHRONIZED != d_last_sync_state) ) { + fprintf(stderr,"====== SYNC GAINED (FOff: %g Corr: %.2f, Color: %d ) ======\n",d_freq_offset,d_corr_max,d_color_code); + } + else if ( (SYNCHRONIZED != d_sync_state) && (SYNCHRONIZED == d_last_sync_state) ) { + fprintf(stderr,"====== SYNC LOST (%ld) ======\n",d_sync_loss_count); + } + } + +} + +void gsm_burst::shift_burst(int shift_bits) +{ + //fprintf(stderr,"sft:%d\n",shift_bits); + + assert(shift_bits >= 0); + assert(shift_bits < BBUF_SIZE ); + + float *p_src = d_burst_buffer + shift_bits; + float *p_dst = d_burst_buffer; + int num = BBUF_SIZE - shift_bits; + + memmove(p_dst,p_src,num * sizeof(float)); //need memmove because of overlap + + //adjust the buffer positions + d_bbuf_pos -= shift_bits; + + assert(d_bbuf_pos >= 0); +} + + +//Calculate frequency offset of an FCCH burst from the mean phase difference +//FCCH should be a constant frequency and equivalently a constant phase +//increment (pi/2) per sample. Calculate the frequency offset by the difference +//of the mean phase from pi/2. +void gsm_burst::calc_freq_offset(void) +{ + const int padding = 20; + int start = d_burst_start + padding; + int end = d_burst_start + USEFUL_BITS - padding; + + float sum = 0.0; + for (int j = start; j <= end; j++) { + sum += d_burst_buffer[j]; + } + float mean = sum / ((float)USEFUL_BITS - (2.0 * (float)padding) ); + + float p_off = mean - (M_PI / 2); + d_freq_offset = p_off * 1625000.0 / (12.0 * M_PI); + + + //maintain a 100 weight mean + if (d_freq_off_weight < 100) + d_freq_off_weight++; + else + d_freq_off_sum *= 99.0/100.0; + + d_freq_off_sum += d_freq_offset; +} + +// This will look for a series of positive phase differences comprising +// a FCCH burst. When we find one, we calculate the frequency offset and +// adjust the burst timing so that it will be at least coarsely aligned +// for SCH detection. +// +// TODO: Adjust start pos on long hits +// very large hit counts may indicate an unmodulated carrier. +BURST_TYPE gsm_burst::get_fcch_burst(void) +{ + int hit_count = 0; + int miss_count = 0; + int start_pos = -1; + + for (int i=0; i < BBUF_SIZE; i++) { + if (d_burst_buffer[i] > 0) { + if ( ! hit_count++ ) + start_pos = i; + } + else { + if (hit_count >= FCCH_HITS_NEEDED) { + break; + } + else if ( ++miss_count > FCCH_MAX_MISSES ) { + start_pos = -1; + hit_count = miss_count = 0; + } + } + } + + //Do we have a match? + if ( start_pos >= 0 ) { + //Is it within range? (we know it's long enough then too) + if ( start_pos < 2*MAX_CORR_DIST ) { + d_burst_start = start_pos; + d_bbuf_pos = 0; //load buffer from start + return FCCH; + + } + else { + //TODO: don't shift a tiny amount + shift_burst(start_pos - MAX_CORR_DIST); + } + } + else { + //Didn't find anything + d_burst_start = MAX_CORR_DIST; + d_bbuf_pos = 0; //load buffer from start + } + + return UNKNOWN; +} + + +void gsm_burst::equalize(void) +{ + float last = 0.0; + + switch ( d_equalizer_type ) { + case EQ_FIXED_LINEAR: + //TODO: should filter w/ inverse freq response + //this is just for giggles + for (int i = 1; i < BBUF_SIZE - 1; i++) { + d_burst_buffer[i] = - 0.4 * d_burst_buffer[i-1] + 1.1 * d_burst_buffer[i] - 0.4 * d_burst_buffer[i+1]; + } + break; + case EQ_FIXED_DFE: + //TODO: allow coefficients to be options? + for (int i = 0; i < BBUF_SIZE; i++) { + d_burst_buffer[i] -= 0.4 * last; + d_burst_buffer[i] > 0.0 ? last = M_PI/2 : last = -M_PI/2; + } + break; + default: + fprintf(stderr,"!EQ"); + case EQ_NONE: + break; + } +} + +//TODO: optimize by working incrementally out from center and returning when a provided threshold is reached +float gsm_burst::correlate_pattern(const float *pattern,const int pat_size,const int center,const int distance) +{ + float corr; + + //need to save these for later printing, etc + //TODO: not much need for function params when we have the member vars + d_corr_pattern = pattern; + d_corr_pat_size = pat_size; + d_corr_max = 0.0; + d_corr_maxpos = 0; + d_corr_center = center; + + for (int j=-distance;j<=distance;j++) { + corr = 0.0; + for (int i = 1; i < pat_size; i++) { //Start a 1 to skip first bit due to diff encoding + //d_corr[j+distance] += d_burst_buffer[center+i+j] * pattern[i]; + //corr += SIGNUM(d_burst_buffer[center+i+j]) * pattern[i]; //binary corr/sliced + corr += d_burst_buffer[center+i+j] * pattern[i]; + } + corr /= pat_size - 1; //normalize, -1 for skipped first bit + if (corr > d_corr_max) { + d_corr_max = corr; + d_corr_maxpos = j; + } + } + + return d_corr_max; +} + +BURST_TYPE gsm_burst::get_sch_burst(void) +{ + BURST_TYPE type = UNKNOWN; + int tpos = 0; //default d_bbuf_pos + + equalize(); + +// if (!d_ts) { // wait for TS0 + + //correlate over a range to detect and align on the sync pattern + correlate_pattern(corr_sync,N_SYNC_BITS,MAX_CORR_DIST+SYNC_POS,20); + + if (d_corr_max > SCH_CORR_THRESHOLD) { + d_burst_start += d_corr_maxpos; + + //It's possible that we will corelate far enough out that some burst data will be lost. + // In this case we should be in aligned state, and wait until next SCH to decode it + if (d_burst_start < 0) { + //We've missed the beginning of the data, wait for the next SCH + //TODO: verify timing in this case + type = PARTIAL_SCH; + } else if (d_burst_start > 2 * MAX_CORR_DIST) { + //The rest of our data is still coming, get it... + shift_burst(d_burst_start - MAX_CORR_DIST); + d_burst_start = MAX_CORR_DIST; + tpos = d_bbuf_pos; + } else { + type = SCH; + } + } + else { + d_burst_start = MAX_CORR_DIST; + } + +// } else { +// d_burst_start = MAX_CORR_DIST; +// } + + d_bbuf_pos = tpos; + + return type; +} + +BURST_TYPE gsm_burst::get_norm_burst(void) +{ + int eq = 0; + BURST_TYPE type = UNKNOWN; + + + if (!d_ts) { + // Don't equalize before checking FCCH + if ( FCCH_CORR_THRESHOLD < correlate_pattern(corr_train_seq[TS_FCCH],N_TRAIN_BITS,MAX_CORR_DIST+TRAIN_POS,0) ) { + type = FCCH; + d_burst_start = MAX_CORR_DIST; + d_corr_maxpos = 0; //we don't want to affect timing + + } + else { + equalize(); + eq=1; + + //TODO: check CTS & COMPACT SYNC + if (SCH_CORR_THRESHOLD < correlate_pattern(corr_sync,N_SYNC_BITS,MAX_CORR_DIST+SYNC_POS,MAX_CORR_DIST) ) + type = SCH; + } + } + + if (UNKNOWN == type) { //no matches yet + if (!eq) equalize(); + + //Match dummy sequence + if ( NORM_CORR_THRESHOLD < correlate_pattern(corr_train_seq[TS_DUMMY],N_TRAIN_BITS,MAX_CORR_DIST+TRAIN_POS,MAX_CORR_DIST) ) { + type = DUMMY; + + } + else { + //Match normal training sequences + //TODO: start with current color code + for (int i=0; i < 8; i++) { + if ( NORM_CORR_THRESHOLD < correlate_pattern(corr_train_seq[i],N_TRAIN_BITS,MAX_CORR_DIST+TRAIN_POS,MAX_CORR_DIST) ) { + type = NORMAL; + d_color_code = i; + break; + } + } + } + } + + if ( UNKNOWN == type ) { + d_burst_start = MAX_CORR_DIST; + + } else { + d_burst_start += d_corr_maxpos; + } + + return type; +} + + +int gsm_burst::get_burst(void) +{ + //TODO: should we output data while looking for FCCH? Maybe an option. + int got_burst=1; //except for the WAIT_FCCH case we always have output + d_burst_type = UNKNOWN; //default + + //begin with the assumption the the burst will be in the correct position + d_burst_start = MAX_CORR_DIST; + + //process the burst + switch (d_sync_state) { + case WAIT_FCCH: + d_ts = 0; + + if ( FCCH == ( d_burst_type = get_fcch_burst()) ) { + d_sync_state = WAIT_SCH_ALIGN; + d_bbuf_pos = 0; //load buffer from start + + } + else { + got_burst = 0; + } + + break; + + case WAIT_SCH_ALIGN: + d_burst_type = get_sch_burst(); + + switch ( d_burst_type ) { + case PARTIAL_SCH: + d_sync_state = WAIT_SCH; + break; + //case SCH: + //let the burst type switch handle this so it knows if new or old sync + // d_sync_state = SYNCHRONIZED; + break; + default: + break; + } + + break; + + case WAIT_SCH: //TODO: check this case + case SYNCHRONIZED: + d_burst_type = get_norm_burst(); + d_bbuf_pos = 0; //load buffer from start + + break; + } + + //Update stats + switch (d_burst_type) { + case FCCH: + if (SYNCHRONIZED == d_sync_state) + d_burst_count++; + else + d_burst_count = 0; + + d_fcch_count++; + calc_freq_offset(); + d_ts = 0; + break; + case PARTIAL_SCH: + d_burst_count++; + d_part_sch_count++; + d_last_sch = d_burst_count; + d_ts = 0; //TODO: check this + break; + case SCH: + //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); + + } + d_burst_count++; + d_sch_count++; + d_last_sch = d_burst_count; + d_sync_state = SYNCHRONIZED; //handle WAIT_SCH + d_ts = 0; + break; + case NORMAL: + d_burst_count++; + d_normal_count++; + break; + case DUMMY: + d_burst_count++; + d_dummy_count++; + break; + default: + case UNKNOWN: + if (SYNCHRONIZED == d_sync_state) { + d_burst_count++; + d_unknown_count++; + } + break; + } + + if (UNKNOWN != d_burst_type) { + d_last_good = d_burst_count; + } + + //Check for loss of sync + int bursts_since_good = d_burst_count - d_last_good; + if (bursts_since_good > MAX_SYNC_WAIT) { + d_sync_loss_count++; + sync_reset(); + } + + if (got_burst) { + d_total_count++; + + //print info + print_burst(); + + ///////////////////// + //start tune testing +#ifdef TEST_HOP_SPEED + static int good_count = -1; //-1: wait sch, >=0: got sch, counting + static int wait_count = 0; + + if (OPT_TEST_HOP_SPEED & d_test_options ) { + //have we started counting? + if ( good_count >= 0 ) { + + if (UNKNOWN == d_burst_type) { + if (good_count >= 0) { + fprintf(stdout,"good_count: %d\n",good_count); + + if (p_tuner) { + next_arfcn = d_hop_good_arfcn; + p_tuner->calleval(BURST_CB_TUNE); + } + } + good_count = -1; // start again at resync + + } else { + //count good bursts + good_count++; + } + + } else { + //haven't started counting + // get some good syncs before trying again + if ((SCH == d_burst_type) && (++wait_count > 5)) { + //fprintf(stdout,"restarting good_count\n"); + good_count = wait_count = 0; + //tune away + if (p_tuner) { + next_arfcn = d_hop_bad_arfcn; + p_tuner->calleval(BURST_CB_TUNE); + } + } + } + } +#endif + //end tune testing + ///////////////////// + + + //Adjust the buffer write position to align on MAX_CORR_DIST + if ( d_clock_options & CLK_CORR_TRACK ) + d_bbuf_pos += MAX_CORR_DIST - d_burst_start; + } + + d_last_sync_state = d_sync_state; + + d_ts = (++d_ts)%8; //next TS + + return got_burst; +} + |