From 08f95b2ac7f377f98f83761b52027b88824d7a64 Mon Sep 17 00:00:00 2001 From: Harald Welte Date: Mon, 13 Oct 2008 09:02:02 +0200 Subject: initial import of gssm-v0.1.1a --- gssm/src/lib/gssm_sink.cc | 764 ++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 764 insertions(+) create mode 100644 gssm/src/lib/gssm_sink.cc (limited to 'gssm/src/lib/gssm_sink.cc') diff --git a/gssm/src/lib/gssm_sink.cc b/gssm/src/lib/gssm_sink.cc new file mode 100644 index 0000000..307e3c8 --- /dev/null +++ b/gssm/src/lib/gssm_sink.cc @@ -0,0 +1,764 @@ +/* + * $Id: gssm_sink.cc,v 1.2 2007-07-07 16:31:42 jl Exp $ + */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include +#include + +#include +#include +#include + +#include "gssm_sink.h" +#include "gsm_constants.h" +#include "burst_types.h" +#include "bursts.h" +#include "sch.h" +#include "cch.h" +#include "gssm_state.h" +#include "tun.h" +#include "rr_decode.h" +#include "display.h" +#include "buffer.h" + + +gssm_sink_sptr gssm_make_sink(double sps) { + + return gssm_sink_sptr(new gssm_sink(sps)); +} + + +static const char *chan_name = "gsm"; // GSM TUN interface name + + +gssm_sink::gssm_sink(double sps) : + gr_sync_block("gssm_sink", + gr_make_io_signature(1, 1, sizeof(gr_complex)), + gr_make_io_signature(0, 0, 0)) { + + if((d_tunfd = mktun(chan_name, d_ether_addr)) == -1) { + fprintf(stderr, "warning: was not able to open TUN device, " + "disabling Wireshark interface\n"); + // throw std::runtime_error("cannot open TUN device"); + } + + // allocate memory for physical channel to logical channel mapping + if(!(d_phy_buf = (unsigned char *)malloc(8 * 51 * eBLOCK_SIZE))) { + perror("malloc"); + close(d_tunfd); + throw std::runtime_error("cannot allocate buffer memory"); + } + + // allocate memory to specify which physical data is present + if(!(d_phy_ind = (int *)malloc(8 * 51 * sizeof(int)))) { + perror("malloc"); + close(d_tunfd); + throw std::runtime_error("cannot allocate index memory"); + } + memset(d_phy_ind, 0, 8 * 51 * sizeof(int)); + + // buffers to hold quadrature demod and clock recovery output + d_buf_qd = gr_make_buffer(BUFFER_QD_SIZE, sizeof(float)); + d_qd_reader = gr_buffer_add_reader(d_buf_qd, 0); + d_buf_mm = gr_make_buffer(BUFFER_MM_SIZE, sizeof(float)); + d_mm_reader = gr_buffer_add_reader(d_buf_mm, 0); + + // indicate to clock recovery that we are not sync'ed + d_bitno = -1; + + // set samples per second and symbol + d_sps = sps; + d_samples_per_symbol = d_sps / GSM_RATE; + + // initial program state + d_state = state_fc; + + // initial base station synchronization + d_tn = -1; + d_fn = -1; + d_bsic = -1; + + // stats + d_search_fc_count = d_found_fc_count = d_valid_s = d_invalid_s = + d_invalid_s_1 = d_valid_bcch = d_invalid_bcch = d_valid_ia = + d_invalid_ia = d_valid_sdcch4 = d_invalid_sdcch4 = + d_valid_sacchc4 = d_invalid_sacchc4 = d_valid_sdcch8 = + d_invalid_sdcch8 = d_valid_sacchc8 = d_invalid_sacchc8 = 0; + + // interpolator + d_interp = new gri_mmse_fir_interpolator(); + + // M&M clock recovery + reset_clock(); + + // set quad demod constants + d_qd_last = 0; + d_qd_gain = M_PI_2l / d_samples_per_symbol; // rate at quad demod + + // we always want multiples of the burst length + set_output_multiple( + (int)ceil((BURST_LENGTH + 1) * d_samples_per_symbol) + + d_interp->ntaps() + 10); +} + + +gssm_sink::~gssm_sink() { + + // close TUN interface + close(d_tunfd); + + // free phy memory + free(d_phy_buf); + free(d_phy_ind); +} + + +int gssm_sink::check_logical_channel(int b1, int b2, int b3, int b4) { + + int r = 0; + unsigned int data_len; + unsigned char *data; + + if(d_phy_ind[b1] && d_phy_ind[b2] && d_phy_ind[b3] && + d_phy_ind[b4]) { + // channel data present + data = decode_cch( + d_phy_buf + b1 * eBLOCK_SIZE, + d_phy_buf + b2 * eBLOCK_SIZE, + d_phy_buf + b3 * eBLOCK_SIZE, + d_phy_buf + b4 * eBLOCK_SIZE, + &data_len); + if(data) { + write_interface(d_tunfd, data + 1, data_len - 1, + d_ether_addr); + + // since we decoded this data, clear present flag + d_phy_ind[b1] = d_phy_ind[b2] = d_phy_ind[b3] = + d_phy_ind[b4] = 0; + + free(data); + + return 0; + } + return -1; + } + + return -2; // channel data not present +} + + +void gssm_sink::check_logical_channels() { + + int o, o_s, ts, rl, fn_s; + + // do we have complete data for various logical channels? + + // BCCH Norm + rl = 51; + fn_s = 2; + for(ts = 0; ts < 8; ts += 2) { + o = ts * rl + fn_s; + if(check_logical_channel(o, o + 1, o + 2, o + 3) == -1) + fprintf(stderr, "error: BCCH Norm (%d, %d)\n", ts, + fn_s); + } + + // BCCH Ext + rl = 51; + fn_s = 6; + for(ts = 0; ts < 8; ts += 2) { + o = ts * rl + fn_s; + if(check_logical_channel(o, o + 1, o + 2, o + 3) == -1) + fprintf(stderr, "error: BCCH Ext (%d, %d)\n", ts, + fn_s); + } + + // PCH, AGCH, SDCCH, SACCHC4 + rl = 51; + for(ts = 0; ts < 8; ts += 2) { + o_s = ts * rl; + + fn_s = 6; + o = o_s + fn_s; + if(check_logical_channel(o, o + 1, o + 2, o + 3) == -1) + fprintf(stderr, "error: PCH, AGCH (%d, %d)\n", ts, + fn_s); + + fn_s = 12; + o = o_s + fn_s; + if(check_logical_channel(o, o + 1, o + 2, o + 3) == -1) + fprintf(stderr, "error: PCH, AGCH (%d, %d)\n", ts, + fn_s); + + fn_s = 16; + o = o_s + fn_s; + if(check_logical_channel(o, o + 1, o + 2, o + 3) == -1) + fprintf(stderr, "error: PCH, AGCH (%d, %d)\n", ts, + fn_s); + + fn_s = 22; + o = o_s + fn_s; + if(check_logical_channel(o, o + 1, o + 2, o + 3) == -1) + fprintf(stderr, "error: PCH, AGCH (%d, %d)\n", ts, + fn_s); + + fn_s = 26; + o = o_s + fn_s; + if(check_logical_channel(o, o + 1, o + 2, o + 3) == -1) + fprintf(stderr, "error: PCH, AGCH (%d, %d)\n", ts, + fn_s); + + fn_s = 32; + o = o_s + fn_s; + if(check_logical_channel(o, o + 1, o + 2, o + 3) == -1) + fprintf(stderr, "error: PCH, AGCH (%d, %d)\n", ts, + fn_s); + + fn_s = 36; + o = o_s + fn_s; + if(check_logical_channel(o, o + 1, o + 2, o + 3) == -1) + fprintf(stderr, "error: PCH, AGCH (%d, %d)\n", ts, + fn_s); + + fn_s = 42; + o = o_s + fn_s; + if(check_logical_channel(o, o + 1, o + 2, o + 3) == -1) + fprintf(stderr, "error: PCH, AGCH (%d, %d)\n", ts, + fn_s); + + fn_s = 46; + o = o_s + fn_s; + if(check_logical_channel(o, o + 1, o + 2, o + 3) == -1) + fprintf(stderr, "error: PCH, AGCH (%d, %d)\n", ts, + fn_s); + } + + // SDCCH8 + rl = 51; + for(ts = 0; ts < 8; ts++) { + o_s = ts * rl; + for(fn_s = 0; fn_s < 32; fn_s += 4) { + o = o_s + fn_s; + if(check_logical_channel(o, o + 1, o + 2, o + 3) == -1) + fprintf(stderr, "error: SDCCH8 (%d, %d)\n", + ts, fn_s); + } + } + + // SACCHC8 + // XXX ignoring subchannel numbers + rl = 51; + for(ts = 0; ts < 8; ts++) { + o_s = ts * rl; + for(fn_s = 32; fn_s < 48; fn_s += 4) { + o = o_s + fn_s; + if(check_logical_channel(o, o + 1, o + 2, o + 3) == -1) + fprintf(stderr, "error: SACCH8 (%d, %d)\n", + ts, fn_s); + } + } + +} + + +void gssm_sink::next_timeslot() { + + d_tn++; + if(d_tn >= 8) { + d_tn %= 8; + d_fn = (d_fn + 1) % MAX_FN; + d_fnm51 = d_fn % 51; + d_fnm102 = d_fn % 102; + } +} + + +static void differential_decode(const unsigned char *in, unsigned char *out) { + + int i; + unsigned char is = 1; + + for(i = 0; i < BURST_LENGTH; i++) + is = out[i] = !in[i] ^ is; +} + + +static void differential_decode(const float *in, unsigned char *out) { + + int i; + unsigned char is = 1; + + for(i = 0; i < BURST_LENGTH; i++) + is = out[i] = (in[i] < 0) ^ is; +} + + +static const unsigned int MAX_INVALID_S = 10; +//static const unsigned int MAX_INVALID_S = 1; + +int gssm_sink::check_num_invalid_s() { + + if(d_invalid_s - d_invalid_s_1 >= MAX_INVALID_S) { + d_invalid_s_1 = d_invalid_s; + memset(d_phy_ind, 0, 8 * 51 * sizeof(int)); + + return 1; + } + return 0; +} + + +int gssm_sink::handle_sch(const unsigned char *buf, int *fn, int *bsic) { + + int ret; + + if(ret = !decode_sch(buf, fn, bsic)) { + d_valid_s++; + d_invalid_s_1 = d_invalid_s; + } else { + d_invalid_s++; + } + return ret; +} + + +/* + * search_s + * + * Searches for the synchronization packet. We assume that we have + * just seen the frequency correction packet. Hence: + * + * 1. We first enter with tn = 1. + * 2. The sync burst is the next burst in this channel. + */ +int gssm_sink::search_state_s(const float *in, int nitems) { + + int i, imax, wl; + unsigned char buf[BURST_LENGTH]; + float fbuf[BURST_LENGTH]; + + // assume that we are at the start of a burst + + imax = nitems - (int)ceil((BURST_LENGTH + 1) * d_samples_per_symbol) - + d_interp->ntaps() - (int)d_mu; + + for(i = 0; i < imax;) { + wl = BURST_LENGTH; + i += mm_demod(in + i, nitems - i, fbuf, wl); + if(!d_tn) { + differential_decode(fbuf, buf); + if(handle_sch(buf, &d_fn, &d_bsic)) { + + d_fnm51 = d_fn % 51; + d_fnm102 = d_fn % 102; + + next_timeslot(); + + d_state = state_data; + + return i; + } else if(check_num_invalid_s()) { + reset_state(); + return 0; + } + } + next_timeslot(); + } + return i; +} + + +static int is_sch(int tn, int fnm51) { + + return ((!tn) && ((fnm51 == 1) || (fnm51 == 11) || (fnm51 == 21) || + (fnm51 == 31) || (fnm51 == 41))); +} + + +void gssm_sink::search_sch(const unsigned char *buf) { + + int fn, bsic; + + if(is_sch(d_tn, d_fnm51)) { + if(handle_sch(buf, &fn, &bsic)) { + if(d_fn != fn) { + fprintf(stderr, "error: lost sync " + "(%d != %d [%d])\n", fn, d_fn, fn - d_fn); + d_fn = fn; + d_fnm51 = fn % 51; + d_fnm102 = fn % 102; + } + if(d_bsic != bsic) { + fprintf(stderr, "warning: bsic changed " + "(%d -> %d)\n", d_bsic, bsic); + d_bsic = bsic; + } + + } else { + if(check_num_invalid_s()) { + reset_state(); + } + } + } +} + + +int gssm_sink::search_state_data(const float *in, int nitems) { + + int i, imax, wl; + unsigned int offset, offset_i; + unsigned char buf[BURST_LENGTH]; + float fbuf[BURST_LENGTH]; + + imax = nitems - (int)ceil((BURST_LENGTH + 1) * d_samples_per_symbol) - + d_interp->ntaps() - (int)d_mu; + + for(i = 0; i < imax;) { + wl = BURST_LENGTH; + i += mm_demod(in + i, nitems - i, fbuf, wl); + + differential_decode(fbuf, buf); + + /* + for(int ii = 0; ii < BURST_LENGTH; ii++) + printf("%f\n", fbuf[ii]); + */ + + /* + if(is_sch(d_tn, d_fnm51)) + printf("S"); + else + printf(" "); + printf("%2d:%d: ", d_fnm51, d_tn); + for(int ii = 0; ii < BURST_LENGTH; ii++) + printf("%d", buf[ii]); + printf("\n"); + */ + + /* + if((buf[0] == 1) || (buf[1] == 1) || (buf[2] == 1)) + printf("%2d:%d: %d%d%d:%d%d%d %f %f %f : %f %f %f\n", + d_fnm51, d_tn, buf[0], buf[1], buf[2], buf[145], + buf[146], buf[147], fbuf[0], fbuf[1], fbuf[2], + fbuf[145], fbuf[146], fbuf[147]); + */ + + search_sch(buf); + if(d_state != state_data) + return 0; + + if(!is_dummy_burst(buf)) { + offset_i = (d_tn * 51 + (d_fn % 51)); + offset = offset_i * eBLOCK_SIZE; + memcpy(d_phy_buf + offset, buf + N_EDATA_OS_1, + N_EDATA_LEN_1); + memcpy(d_phy_buf + offset + N_EDATA_LEN_1, + buf + N_EDATA_OS_2, N_EDATA_LEN_2); + d_phy_ind[offset_i] = 1; + } + + // check_logical_channels(); + + if((!d_tn) && (!d_fnm51)) { + check_logical_channels(); + memset(d_phy_ind, 0, 8 * 51 * sizeof(int)); + } + + next_timeslot(); + } + return i; +} + + +static int slice(float f) { + + return (f >= 0); +} + + +int gssm_sink::mm_demod(const float *in, int nitems, float *out, int &nitems_out) { + int i, o; + float mm_val, v, f; + + f = floor(d_mu); + i = (int)f; + d_mu -= f; + + for(o = 0; (i < nitems - d_interp->ntaps()) && (o < nitems_out); o++) { + /* + * Produce output sample interpolated by d_mu where d_mu + * represents the normalized distance between the first and + * second sample of the given sequence. + * + * For example, d_mu = 0.5 interpolates a sample halfway + * in-between the current sample and the next. + */ + v = d_interp->interpolate(&in[i], d_mu); + + // adjust how fast and in which direction we modify omega + mm_val = slice(d_last_sample) * v - slice(v) * d_last_sample; + + // write output + // out[o] = (d_last_sample = v) >= 0; // hard decision + out[o] = d_last_sample = v; // soft decision + + // adjust the sample time for the next symbol + d_omega = d_omega + d_gain_omega * mm_val; + + // don't allow it to exceed extrema + if(d_omega > d_max_omega) + d_omega = d_max_omega; + else if(d_omega < d_min_omega) + d_omega = d_min_omega; + + // advance to next symbol + d_mu += d_omega + d_gain_mu * mm_val; + + // if we're sync'ed + if(d_bitno > -1) { + d_bitno++; + + // skip the quarter-bit at the end of a burst + if(d_bitno >= BURST_LENGTH) { + d_mu += d_omega / 4.0l; + d_bitno = 0; + } + } + + // d_mu is now the number of samples to the next symbol + f = floor(d_mu); + i += (int)f; // integer advance + d_mu -= f; // fractional advance + } + + // we may have advanced past the buffer + i -= (int)f; + d_mu += f; + + nitems_out = o; + + return i; // returns number consumed +} + + +int gssm_sink::quad_demod(const gr_complex *in, int nitems, float *out, int &nitems_out) { + + int r, M; + gr_complex product; + + M = std::min(nitems, nitems_out); + for(r = 0; r < M; r++) { + product = in[r] * conj(d_qd_last); + d_qd_last = in[r]; + out[r] = d_qd_gain * gr_fast_atan2f(imag(product), real(product)); + } + + nitems_out = r; + return r; // returns number consumed +} + + +int gssm_sink::process_input(const gr_complex *in, const int nitems) { + + float *w; + const float *r; + int wl, rl, rrl, ret; + + w = (float *)d_buf_qd->write_pointer(); + wl = d_buf_qd->space_available(); + ret = quad_demod(in, nitems, w, wl); + d_buf_qd->update_write_pointer(wl); + + r = (const float *)d_qd_reader->read_pointer(); + rl = d_qd_reader->items_available(); + w = (float *)d_buf_mm->write_pointer(); + wl = d_buf_mm->space_available(); + rrl = mm_demod(r, rl, w, wl); + d_qd_reader->update_read_pointer(rrl); + d_buf_mm->update_write_pointer(wl); + + return ret; +} + + +void gssm_sink::flush_buffers() { + + d_qd_reader->update_read_pointer(d_qd_reader->items_available()); + d_mm_reader->update_read_pointer(d_mm_reader->items_available()); +} + + +void gssm_sink::save_clock() { + + d_mu_bak = d_mu; + d_omega_bak = d_omega; + d_last_sample_bak = d_last_sample; +} + + +void gssm_sink::restore_clock() { + + d_mu = d_mu_bak; + d_omega = d_omega_bak; + d_last_sample = d_last_sample_bak; +} + + +void gssm_sink::reset_state() { + + d_state = state_fc; + d_bitno = -1; + flush_buffers(); + d_tn = -1; + d_fn = -1; + d_fnm51 = -1; + d_fnm102 = -1; + d_bsic = -1; + + reset_clock(); +} + + +void gssm_sink::reset_clock() { + + // M&M clock recovery + d_mu = 0.0; + d_gain_mu = 0.01; + d_omega = d_samples_per_symbol; + //d_omega_relative_limit = 0.01; + d_omega_relative_limit = 0.3; + d_max_omega = d_omega * (1.0 + d_omega_relative_limit); + d_min_omega = d_omega * (1.0 - d_omega_relative_limit); + d_gain_omega = 0.25 * d_gain_mu * d_gain_mu; +} + + +/* + * search_fc + * + * Searches for the frequency correction burst. + */ +int gssm_sink::search_state_fc(const float *in, int nitems) { + + int i, j, slen = sizeof(fc_fb_de) / sizeof(*fc_fb_de), + imax, c, nbits; + float w[BURST_LENGTH]; + double f; + const unsigned char *s = fc_fb_de; + + f = floor(d_mu); + i = (int)f; + d_mu -= f; + + imax = nitems - (int)ceil((BURST_LENGTH + 1) * d_samples_per_symbol) - + d_interp->ntaps() - i; + + while(i < imax) { + + // update stats + d_search_fc_count++; + + // save current clock parameters + save_clock(); + + // calculate burst + nbits = BURST_LENGTH; + c = mm_demod(in + i, nitems - i, w, nbits); + + // compare fixed and tail bits + for(j = 0; j < std::min(slen, nbits); j++) + if(slice(w[j]) != s[j]) + break; + + // was the fc burst detected? + if(j == slen) { + + // update stats + d_found_fc_count++; + + d_mu += d_omega / 4.0l; // advance quarter-bit + d_bitno = 0; // burst sync'ed now + d_tn = 0; // fc always in timeslot 0 + next_timeslot(); + d_state = state_s; + + return i + c; + } + + // restore clock to start of attempt + restore_clock(); + + // skip 1 bit and start again + nbits = 1; + i += mm_demod(in + i, nitems - i, w, nbits); + } + + return i; +} + + +void gssm_sink::stats() { + + printf("search fc:\t%d\n", d_search_fc_count); + printf("found fc:\t%d\n", d_found_fc_count); + printf("valid s:\t%d\n", d_valid_s); + printf("invalid s:\t%d\n", d_invalid_s); +} + + +int gssm_sink::process_qd(const gr_complex *in, int nitems) { + + float *w; + int wl, ret; + + w = (float *)d_buf_qd->write_pointer(); + wl = d_buf_qd->space_available(); + ret = quad_demod(in, nitems, w, wl); + d_buf_qd->update_write_pointer(wl); + + return ret; +} + + +int gssm_sink::work(int nitems, gr_vector_const_void_star &input_items, + gr_vector_void_star &) { + + const gr_complex *in = (gr_complex *)input_items[0]; + const float *r; + int rl, ret, i, imax, n; + + ret = process_qd(in, nitems); + + r = (const float *)d_qd_reader->read_pointer(); + rl = d_qd_reader->items_available(); + + imax = rl - + (int)(ceil((BURST_LENGTH + 1) * d_samples_per_symbol + d_mu) + + d_interp->ntaps()); + + for(i = 0; i < imax;) { + switch(d_state) { + case state_fc: + n = search_state_fc(r + i, rl - i); + break; + case state_s: + n = search_state_s(r + i, rl - i); + break; + case state_data: + n = search_state_data(r + i, rl - i); + break; + default: + fprintf(stderr, "error: bad state\n"); + reset_state(); + return 0; + } + i += n; + } + d_qd_reader->update_read_pointer(i); + + return ret; +} -- cgit v1.2.3