diff options
Diffstat (limited to 'gssm/src/lib')
-rw-r--r-- | gssm/src/lib/Makefile.am | 44 | ||||
-rw-r--r-- | gssm/src/lib/buffer.h | 4 | ||||
-rw-r--r-- | gssm/src/lib/burst_types.h | 217 | ||||
-rw-r--r-- | gssm/src/lib/bursts.cc | 153 | ||||
-rw-r--r-- | gssm/src/lib/bursts.h | 10 | ||||
-rw-r--r-- | gssm/src/lib/cch.cc | 460 | ||||
-rw-r--r-- | gssm/src/lib/cch.h | 42 | ||||
-rw-r--r-- | gssm/src/lib/display.cc | 46 | ||||
-rw-r--r-- | gssm/src/lib/display.h | 4 | ||||
-rw-r--r-- | gssm/src/lib/gsm_constants.h | 76 | ||||
-rw-r--r-- | gssm/src/lib/gssm.i | 28 | ||||
-rw-r--r-- | gssm/src/lib/gssm_sink.cc | 765 | ||||
-rw-r--r-- | gssm/src/lib/gssm_sink.h | 125 | ||||
-rw-r--r-- | gssm/src/lib/gssm_state.h | 9 | ||||
-rw-r--r-- | gssm/src/lib/rr_decode.cc | 236 | ||||
-rw-r--r-- | gssm/src/lib/rr_decode.h | 4 | ||||
-rw-r--r-- | gssm/src/lib/rrm.h | 22 | ||||
-rw-r--r-- | gssm/src/lib/sch.cc | 320 | ||||
-rw-r--r-- | gssm/src/lib/sch.h | 3 | ||||
-rw-r--r-- | gssm/src/lib/tun.cc | 125 | ||||
-rw-r--r-- | gssm/src/lib/tun.h | 4 |
21 files changed, 2697 insertions, 0 deletions
diff --git a/gssm/src/lib/Makefile.am b/gssm/src/lib/Makefile.am new file mode 100644 index 0000000..be3775c --- /dev/null +++ b/gssm/src/lib/Makefile.am @@ -0,0 +1,44 @@ +# $Id: Makefile.am,v 1.1.1.1 2007-06-01 04:26:57 jl Exp $ + +include $(top_srcdir)/Makefile.common + +ourpythondir = $(pythondir) +ourlibdir = $(pyexecdir) + +INCLUDES = $(STD_DEFINES_AND_INCLUDES) $(PYTHON_CPPFLAGS) + +SWIGCPPPYTHONARGS = -c++ -python $(PYTHON_CPPFLAGS) -I$(swigincludedir) -I$(grincludedir) + +LOCAL_IFILES = gssm.i +NON_LOCAL_IFILES = $(GNURADIO_CORE_INCLUDEDIR)/swig/gnuradio.i +ALL_IFILES = $(LOCAL_IFILES) $(NON_LOCAL_IFILES) + +BUILT_SOURCES = gssm_glue.cc gssm.py + +ourpython_PYTHON = gssm.py + +ourlib_LTLIBRARIES = _gssm.la + +# source files that go into the shared library +_gssm_la_SOURCES = \ + gssm_glue.cc \ + gssm_sink.cc \ + bursts.cc \ + sch.cc \ + cch.cc \ + rr_decode.cc \ + tun.cc \ + display.cc + +_gssm_la_LDFLAGS = -module -avoid-version + +_gssm_la_LIBADD = $(PYTHON_LDFLAGS) -lstdc++ + +gssm_glue.cc gssm.py: gssm.i $(ALL_IFILES) + $(SWIG) $(SWIGCPPPYTHONARGS) -module gssm -o gssm_glue.cc $< + +grinclude_HEADERS = gssm_sink.h + +swiginclude_HEADERS = $(LOCAL_IFILES) + +MOSTLYCLEANFILES = $(BUILT_SOURCES) *.pyc diff --git a/gssm/src/lib/buffer.h b/gssm/src/lib/buffer.h new file mode 100644 index 0000000..038cba6 --- /dev/null +++ b/gssm/src/lib/buffer.h @@ -0,0 +1,4 @@ +#pragma once + +static const int BUFFER_QD_SIZE = 16384; +static const int BUFFER_MM_SIZE = 16384; diff --git a/gssm/src/lib/burst_types.h b/gssm/src/lib/burst_types.h new file mode 100644 index 0000000..fc8fa4a --- /dev/null +++ b/gssm/src/lib/burst_types.h @@ -0,0 +1,217 @@ +// $Id: burst_types.h,v 1.2 2007-07-07 16:31:42 jl Exp $ + +#pragma once + +#include "gsm_constants.h" + +static const int TB_LEN = 3; +static const int TB_OS1 = 0; +static const int TB_OS2 = 145; +static const unsigned char tail_bits[TB_LEN] = {0, 0, 0}; + +/* + * The normal burst is used to carry information on traffic and control + * channels. + */ +static const int N_TSC_NUM = 8; // number of training sequence codes +static const int N_TSC_CODE_LEN = 26; // length of tsc +static const int N_TSC_OS = 61; // tsc offset +static const int N_EDATA_LEN_1 = 58; // length of first data section +static const int N_EDATA_OS_1 = 3; // offset of first data section +static const int N_EDATA_LEN_2 = 58; // length of second data section +static const int N_EDATA_OS_2 = 87; // offset of second data section +static const unsigned char n_tsc[N_TSC_NUM][N_TSC_CODE_LEN] = { +/* 0 */ { + 0, 0, 1, 0, 0, 1, 0, 1, 1, 1, 0, 0, 0, + 0, 1, 0, 0, 0, 1, 0, 0, 1, 0, 1, 1, 1 + }, +/* 1 */ { + 0, 0, 1, 0, 1, 1, 0, 1, 1, 1, 0, 1, 1, + 1, 1, 0, 0, 0, 1, 0, 0, 1, 0, 1, 1, 1 + }, +/* 2 */ { + 0, 1, 0, 0, 0, 0, 1, 1, 1, 0, 1, 1, 1, + 0, 1, 0, 0, 1, 0, 0, 0, 0, 1, 1, 1, 0 + }, +/* 3 */ { + 0, 1, 0, 0, 0, 1, 1, 1, 1, 0, 1, 1, 0, + 1, 0, 0, 0, 1, 0, 0, 0, 1, 1, 1, 1, 0 + }, +/* 4 */ { + 0, 0, 0, 1, 1, 0, 1, 0, 1, 1, 1, 0, 0, + 1, 0, 0, 0, 0, 0, 1, 1, 0, 1, 0, 1, 1 + }, +/* 5 */ { + 0, 1, 0, 0, 1, 1, 1, 0, 1, 0, 1, 1, 0, + 0, 0, 0, 0, 1, 0, 0, 1, 1, 1, 0, 1, 0 + }, +/* 6 */ { + 1, 0, 1, 0, 0, 1, 1, 1, 1, 1, 0, 1, 1, + 0, 0, 0, 1, 0, 1, 0, 0, 1, 1, 1, 1, 1 + }, +/* 7 */ { + 1, 1, 1, 0, 1, 1, 1, 1, 0, 0, 0, 1, 0, + 0, 1, 0, 1, 1, 1, 0, 1, 1, 1, 1, 0, 0 + } +}; + + +/* + * The frequency correction burst is used for frequency synchronization + * of the mobile. This is broadcast in TS0 together with the SCH and + * BCCH. + * + * Modulating the bits below causes a spike at 62.5kHz above (below for + * COMPACT) the center frequency. One can use this spike with a narrow + * band filter to accurately determine the center of the channel. + */ +static const int FC_CODE_LEN = 142; +static const int FC_OS = 3; +static const unsigned char fc_fb[FC_CODE_LEN] = { + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 +}; + +static const unsigned char fc_fb_de[TB_LEN + FC_CODE_LEN + TB_LEN] = { + 0, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1 +}; + +static const unsigned char fc_compact_fb[FC_CODE_LEN] = { + 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, + 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, + 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, + 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, + 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, + 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, + 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, + 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, + 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0 +}; + + +/* + * The synchronization burst is used for time synchronization of the + * mobile. The bits given below were chosen for their correlation + * properties. The synchronization channel (SCH) contains a long + * training sequence (given below) and carries the TDMA frame number and + * base station identity code. It is broadcast in TS0 in the frame + * following the frequency correction burst. + */ +static const int SB_CODE_LEN = 64; +static const int SB_ETS_OS = 42; +static const int SB_EDATA_LEN_1 = 39; +static const int SB_EDATA_OS_1 = 3; +static const int SB_EDATA_LEN_2 = 39; +static const int SB_EDATA_OS_2 = 106; +static const unsigned char sb_etsc[SB_CODE_LEN] = { + 1, 0, 1, 1, 1, 0, 0, 1, 0, 1, 1, 0, 0, 0, 1, 0, + 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, + 0, 0, 1, 0, 1, 1, 0, 1, 0, 1, 0, 0, 0, 1, 0, 1, + 0, 1, 1, 1, 0, 1, 1, 0, 0, 0, 0, 1, 1, 0, 1, 1 +}; + +static const unsigned char sb_cts_etsc[SB_CODE_LEN] = { + 1, 1, 1, 0, 1, 1, 1, 0, 0, 1, 1, 0, 1, 0, 1, 1, + 0, 0, 1, 0, 1, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 0, + 1, 1, 1, 1, 0, 1, 0, 0, 0, 1, 1, 1, 1, 1, 1, 0, + 1, 1, 0, 0, 1, 0, 1, 1, 0, 0, 0, 1, 0, 1, 0, 1 +}; + +static const unsigned char sb_compact_etsc[SB_CODE_LEN] = { + 1, 1, 1, 0, 1, 1, 0, 0, 0, 0, 1, 1, 0, 1, 1, 1, + 0, 1, 0, 1, 0, 0, 0, 1, 0, 1, 0, 1, 1, 0, 1, 0, + 0, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, + 0, 0, 1, 0, 0, 0, 1, 1, 0, 1, 0, 0, 1, 1, 1, 0 +}; + + +/* + * A base tranceiver station must transmit a burst in every timeslot of + * every TDMA frame in channel C0. The dummy burst will be transmitted + * on all timeslots of all TDMA frames for which no other channel + * requires a burst to be transmitted. + */ +static const int D_CODE_LEN = 142; +static const int D_MB_OS = 3; +static const unsigned char d_mb[D_CODE_LEN] = { + 1, 1, 1, 1, 1, 0, 1, 1, 0, 1, 1, 1, 0, 1, 1, 0, + 0, 0, 0, 0, 1, 0, 1, 0, 0, 1, 0, 0, 1, 1, 1, 0, + 0, 0, 0, 0, 1, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 0, + 0, 0, 0, 1, 1, 1, 1, 1, 0, 0, 0, 1, 1, 1, 0, 0, + 0, 1, 0, 1, 1, 1, 0, 0, 0, 1, 0, 1, 1, 1, 0, 0, + 0, 1, 0, 1, 0, 1, 1, 1, 0, 1, 0, 0, 1, 0, 1, 0, + 0, 0, 1, 1, 0, 0, 1, 1, 0, 0, 1, 1, 1, 0, 0, 1, + 1, 1, 1, 0, 1, 0, 0, 1, 1, 1, 1, 1, 0, 0, 0, 1, + 0, 0, 1, 0, 1, 1, 1, 1, 1, 0, 1, 0, 1, 0 +}; + + +/* + * The access burst is used for random access from a mobile. + */ +static const int AB_ETB_CODE_LEN = 8; +static const int AB_ETB_OS = 0; +static const unsigned char ab_etb[AB_ETB_CODE_LEN] = { + 0, 0, 1, 1, 1, 0, 1, 0 +}; + +static const int AB_SSB_CODE_LEN = 41; +static const int AB_SSB_OS = 8; +static const unsigned char ab_ssb[AB_SSB_CODE_LEN] = { + 0, 1, 0, 0, 1, 0, 1, 1, 0, 1, 1, 1, 1, 1, 1, 1, + 1, 0, 0, 1, 1, 0, 0, 1, 1, 0, 1, 0, 1, 0, 1, 0, + 0, 0, 1, 1, 1, 1, 0, 0, 0 +}; + +static const unsigned char ab_ts1_ssb[AB_SSB_CODE_LEN] = { + 0, 1, 0, 1, 0, 1, 0, 0, 1, 1, 1, 1, 1, 0, 0, 0, + 1, 0, 0, 0, 0, 1, 1, 0, 0, 0, 1, 0, 1, 1, 1, 1, + 0, 0, 1, 0, 0, 1, 1, 0, 1 +}; + +static const unsigned char ab_ts2_ssb[AB_SSB_CODE_LEN] = { + 1, 1, 1, 0, 1, 1, 1, 1, 0, 0, 1, 0, 0, 1, 1, 1, + 0, 1, 0, 1, 0, 1, 1, 0, 0, 0, 0, 0, 1, 1, 0, 1, + 1, 0, 1, 1, 1, 0, 1, 1, 1 +}; + + +typedef enum { + burst_n_0, + burst_n_1, + burst_n_2, + burst_n_3, + burst_n_4, + burst_n_5, + burst_n_6, + burst_n_7, + burst_fc, + burst_fc_c, + burst_s, + burst_s_cts, + burst_s_c, + burst_d, + burst_a, + burst_a_ts1, + burst_a_ts2, + burst_not_a_burst +} burst_t; + +static const int N_BURST_TYPES = ((int)(burst_not_a_burst) + 1); diff --git a/gssm/src/lib/bursts.cc b/gssm/src/lib/bursts.cc new file mode 100644 index 0000000..cd35382 --- /dev/null +++ b/gssm/src/lib/bursts.cc @@ -0,0 +1,153 @@ +// $Id: bursts.cc,v 1.1.1.1 2007-06-01 04:26:57 jl Exp $ + +#include <stdio.h> +#include <stdlib.h> +#include <unistd.h> +#include <string.h> +#include "burst_types.h" + +void display_burst_type(burst_t t) { + + switch(t) { + case burst_n_0: + case burst_n_1: + case burst_n_2: + case burst_n_3: + case burst_n_4: + case burst_n_5: + case burst_n_6: + case burst_n_7: + printf("normal burst %d", t); + break; + case burst_fc: + printf("frequency correction burst"); + break; + case burst_fc_c: + printf("frequency correction burst (COMPACT)"); + break; + case burst_s: + printf("synchronization burst"); + break; + case burst_s_cts: + printf("synchronization burst (CTS)"); + break; + case burst_s_c: + printf("synchronization burst (COMPACT)"); + break; + case burst_d: + printf("dummy burst"); + break; + case burst_a: + printf("access burst"); + break; + case burst_a_ts1: + printf("access burst (TS1)"); + break; + case burst_a_ts2: + printf("access burst (TS2)"); + break; + default: + printf("unknown burst type"); + break; + } +} + + +static int burst_diff(const unsigned char *b1, const unsigned char *b2, + unsigned int l) { + + int d = 0; + unsigned int i; + + for(i = 0; i < l; i++) + d += (b1[i] ^ b2[i]); + return d; +} + + +int search_fc(unsigned char *buf) { + + return !(burst_diff(buf + TB_OS1, tail_bits, TB_LEN) + + burst_diff(buf + TB_OS2, tail_bits, TB_LEN) + + burst_diff(buf + FC_OS, fc_fb, FC_CODE_LEN)); +} + + +int is_dummy_burst(const unsigned char *buf) { + + int i; + + for(i = 0; i < D_CODE_LEN; i++) + if(buf[i + D_MB_OS] != d_mb[i]) + return 0; + return 1; +} + + +burst_t search_burst(unsigned char *buf, int max_burst_errors, int *rmin_o) { + + int i, d_burst[N_BURST_TYPES], t_d, d_min, rmin; + burst_t bt_min; + + for(i = 0; i < N_BURST_TYPES; i++) + d_burst[i] = max_burst_errors + 1; + + // access burst (uplink only) + t_d = burst_diff(buf + AB_ETB_OS, ab_etb, AB_ETB_CODE_LEN); + d_burst[(int)burst_a] = + t_d + burst_diff(buf + AB_SSB_OS, ab_ssb, AB_SSB_CODE_LEN); + + d_burst[(int)burst_a_ts1] = + t_d + burst_diff(buf + AB_SSB_OS, ab_ts1_ssb, AB_SSB_CODE_LEN); + + d_burst[(int)burst_a_ts2] = + t_d + burst_diff(buf + AB_SSB_OS, ab_ts2_ssb, AB_SSB_CODE_LEN); + + // check tail bits + t_d = + burst_diff(buf + TB_OS1, tail_bits, TB_LEN) + + burst_diff(buf + TB_OS2, tail_bits, TB_LEN); + + // normal bursts + for(i = 0; i < N_TSC_NUM; i++) + d_burst[(int)burst_n_0 + i] = + t_d + burst_diff(buf + N_TSC_OS, n_tsc[i], N_TSC_CODE_LEN); + + // frequency correction + d_burst[(int)burst_fc] = + t_d + burst_diff(buf + FC_OS, fc_fb, FC_CODE_LEN); + + d_burst[(int)burst_fc_c] = + t_d + burst_diff(buf + FC_OS, fc_compact_fb, FC_CODE_LEN); + + // synchronization burst + d_burst[(int)burst_s] = + t_d + burst_diff(buf + SB_ETS_OS, sb_etsc, SB_CODE_LEN); + + d_burst[(int)burst_s_cts] = + t_d + burst_diff(buf + SB_ETS_OS, sb_cts_etsc, SB_CODE_LEN); + + d_burst[(int)burst_s_c] = + t_d + burst_diff(buf + SB_ETS_OS, sb_compact_etsc, SB_CODE_LEN); + + // dummy + d_burst[(int)burst_d] = + t_d + burst_diff(buf + D_MB_OS, d_mb, D_CODE_LEN); + + d_burst[(int)burst_not_a_burst] = max_burst_errors; + + rmin = BURST_LENGTH; + d_min = max_burst_errors + 1; + bt_min = burst_not_a_burst; + for(i = 0; i < N_BURST_TYPES; i++) { + if(d_burst[i] < d_min) { + bt_min = (burst_t)i; + d_min = d_burst[i]; + } + if((d_burst[i] < rmin) && ((burst_t)i != burst_not_a_burst)) + rmin = d_burst[i]; + } + if(rmin_o) + *rmin_o = rmin; + return bt_min; +} diff --git a/gssm/src/lib/bursts.h b/gssm/src/lib/bursts.h new file mode 100644 index 0000000..c8a5096 --- /dev/null +++ b/gssm/src/lib/bursts.h @@ -0,0 +1,10 @@ +// $Id: bursts.h,v 1.1.1.1 2007-06-01 04:26:57 jl Exp $ + +#pragma once + +#include "burst_types.h" + +void display_burst_type(burst_t); +burst_t search_burst(unsigned char *, int, int *); +int search_fc(unsigned char *); +int is_dummy_burst(const unsigned char *); diff --git a/gssm/src/lib/cch.cc b/gssm/src/lib/cch.cc new file mode 100644 index 0000000..5659440 --- /dev/null +++ b/gssm/src/lib/cch.cc @@ -0,0 +1,460 @@ +// $Id: cch.cc,v 1.1.1.1 2007-06-01 04:26:57 jl Exp $ + +#include <stdio.h> +#include <stdlib.h> +#include <unistd.h> +#include <string.h> +#include <ctype.h> + +#include <exception> +#include <stdexcept> + +#include "burst_types.h" +#include "cch.h" + +/* + * GSM SACCH -- Slow Associated Control Channel + * + * These messages are encoded exactly the same as on the BCCH. + * (Broadcast Control Channel.) + * + * Input: 184 bits + * + * 1. Add parity and flushing bits. + * 2. Convolutional encode. + * 3. Interleave. + * 4. Map on bursts. + */ + + +/* + * Parity (FIRE) for the GSM SACCH channel. + * + * g(x) = (x^23 + 1)(x^17 + x^3 + 1) + * = x^40 + x^26 + x^23 + x^17 + x^3 + 1 + */ + +static const unsigned char parity_polynomial[PARITY_SIZE + 1] = { + 1, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 1, 0, + 0, 1, 0, 0, 0, 0, 0, 1, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 1, 0, 0, + 1 +}; + +// remainder after dividing data polynomial by g(x) +static const unsigned char parity_remainder[PARITY_SIZE] = { + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1 +}; + + +/* +static void parity_encode(unsigned char *d, unsigned char *p) { + + int i; + unsigned char buf[DATA_BLOCK_SIZE + PARITY_SIZE], *q; + + memcpy(buf, d, DATA_BLOCK_SIZE); + memset(buf + DATA_BLOCK_SIZE, 0, PARITY_SIZE); + + for(q = buf; q < buf + DATA_BLOCK_SIZE; q++) + if(*q) + for(i = 0; i < PARITY_SIZE + 1; i++) + q[i] ^= parity_polynomial[i]; + for(i = 0; i < PARITY_SIZE; i++) + p[i] = !buf[DATA_BLOCK_SIZE + i]; +} + */ + + +static int parity_check(unsigned char *d) { + + unsigned int i; + unsigned char buf[DATA_BLOCK_SIZE + PARITY_SIZE], *q; + + memcpy(buf, d, DATA_BLOCK_SIZE + PARITY_SIZE); + + for(q = buf; q < buf + DATA_BLOCK_SIZE; q++) + if(*q) + for(i = 0; i < PARITY_SIZE + 1; i++) + q[i] ^= parity_polynomial[i]; + return memcmp(buf + DATA_BLOCK_SIZE, parity_remainder, PARITY_SIZE); +} + + +/* + * Convolutional encoding and Viterbi decoding for the GSM SACCH channel. + */ + +/* + * Convolutional encoding: + * + * G_0 = 1 + x^3 + x^4 + * G_1 = 1 + x + x^3 + x^4 + * + * i.e., + * + * c_{2k} = u_k + u_{k - 3} + u_{k - 4} + * c_{2k + 1} = u_k + u_{k - 1} + u_{k - 3} + u_{k - 4} + */ +#define K 5 +#define MAX_ERROR (2 * CONV_INPUT_SIZE + 1) + + +/* + * Given the current state and input bit, what are the output bits? + * + * encode[current_state][input_bit] + */ +static const unsigned int encode[1 << (K - 1)][2] = { + {0, 3}, {3, 0}, {3, 0}, {0, 3}, + {0, 3}, {3, 0}, {3, 0}, {0, 3}, + {1, 2}, {2, 1}, {2, 1}, {1, 2}, + {1, 2}, {2, 1}, {2, 1}, {1, 2} +}; + + +/* + * Given the current state and input bit, what is the next state? + * + * next_state[current_state][input_bit] + */ +static const unsigned int next_state[1 << (K - 1)][2] = { + {0, 8}, {0, 8}, {1, 9}, {1, 9}, + {2, 10}, {2, 10}, {3, 11}, {3, 11}, + {4, 12}, {4, 12}, {5, 13}, {5, 13}, + {6, 14}, {6, 14}, {7, 15}, {7, 15} +}; + + +/* + * Given the previous state and the current state, what input bit caused + * the transition? If it is impossible to transition between the two + * states, the value is 2. + * + * prev_next_state[previous_state][current_state] + */ +static const unsigned int prev_next_state[1 << (K - 1)][1 << (K - 1)] = { + { 0, 2, 2, 2, 2, 2, 2, 2, 1, 2, 2, 2, 2, 2, 2, 2}, + { 0, 2, 2, 2, 2, 2, 2, 2, 1, 2, 2, 2, 2, 2, 2, 2}, + { 2, 0, 2, 2, 2, 2, 2, 2, 2, 1, 2, 2, 2, 2, 2, 2}, + { 2, 0, 2, 2, 2, 2, 2, 2, 2, 1, 2, 2, 2, 2, 2, 2}, + { 2, 2, 0, 2, 2, 2, 2, 2, 2, 2, 1, 2, 2, 2, 2, 2}, + { 2, 2, 0, 2, 2, 2, 2, 2, 2, 2, 1, 2, 2, 2, 2, 2}, + { 2, 2, 2, 0, 2, 2, 2, 2, 2, 2, 2, 1, 2, 2, 2, 2}, + { 2, 2, 2, 0, 2, 2, 2, 2, 2, 2, 2, 1, 2, 2, 2, 2}, + { 2, 2, 2, 2, 0, 2, 2, 2, 2, 2, 2, 2, 1, 2, 2, 2}, + { 2, 2, 2, 2, 0, 2, 2, 2, 2, 2, 2, 2, 1, 2, 2, 2}, + { 2, 2, 2, 2, 2, 0, 2, 2, 2, 2, 2, 2, 2, 1, 2, 2}, + { 2, 2, 2, 2, 2, 0, 2, 2, 2, 2, 2, 2, 2, 1, 2, 2}, + { 2, 2, 2, 2, 2, 2, 0, 2, 2, 2, 2, 2, 2, 2, 1, 2}, + { 2, 2, 2, 2, 2, 2, 0, 2, 2, 2, 2, 2, 2, 2, 1, 2}, + { 2, 2, 2, 2, 2, 2, 2, 0, 2, 2, 2, 2, 2, 2, 2, 1}, + { 2, 2, 2, 2, 2, 2, 2, 0, 2, 2, 2, 2, 2, 2, 2, 1} +}; + + +static inline unsigned int hamming_distance2(unsigned int w) { + + return (w & 1) + !!(w & 2); +} + + +/* +static void conv_encode(unsigned char *data, unsigned char *output) { + + unsigned int i, state = 0, o; + + // encode data + for(i = 0; i < CONV_INPUT_SIZE; i++) { + o = encode[state][data[i]]; + state = next_state[state][data[i]]; + *output++ = !!(o & 2); + *output++ = o & 1; + } +} + */ + + +static int conv_decode(unsigned char *output, unsigned char *data) { + + int i, t; + unsigned int rdata, state, nstate, b, o, distance, accumulated_error, + min_state, min_error, cur_state; + + unsigned int ae[1 << (K - 1)]; + unsigned int nae[1 << (K - 1)]; // next accumulated error + unsigned int state_history[1 << (K - 1)][CONV_INPUT_SIZE + 1]; + + // initialize accumulated error, assume starting state is 0 + for(i = 0; i < (1 << (K - 1)); i++) + ae[i] = nae[i] = MAX_ERROR; + ae[0] = 0; + + // build trellis + for(t = 0; t < CONV_INPUT_SIZE; t++) { + + // get received data symbol + rdata = (data[2 * t] << 1) | data[2 * t + 1]; + + // for each state + for(state = 0; state < (1 << (K - 1)); state++) { + + // make sure this state is possible + if(ae[state] >= MAX_ERROR) + continue; + + // find all states we lead to + for(b = 0; b < 2; b++) { + + // get next state given input bit b + nstate = next_state[state][b]; + + // find output for this transition + o = encode[state][b]; + + // calculate distance from received data + distance = hamming_distance2(rdata ^ o); + + // choose surviving path + accumulated_error = ae[state] + distance; + if(accumulated_error < nae[nstate]) { + + // save error for surviving state + nae[nstate] = accumulated_error; + + // update state history + state_history[nstate][t + 1] = state; + } + } + } + + // get accumulated error ready for next time slice + for(i = 0; i < (1 << (K - 1)); i++) { + ae[i] = nae[i]; + nae[i] = MAX_ERROR; + } + } + + // the final state is the state with the fewest errors + min_state = (unsigned int)-1; + min_error = MAX_ERROR; + for(i = 0; i < (1 << (K - 1)); i++) { + if(ae[i] < min_error) { + min_state = i; + min_error = ae[i]; + } + } + + // trace the path + cur_state = min_state; + for(t = CONV_INPUT_SIZE; t >= 1; t--) { + min_state = cur_state; + cur_state = state_history[cur_state][t]; // get previous + output[t - 1] = prev_next_state[cur_state][min_state]; + } + + // return the number of errors detected (hard-decision) + return min_error; +} + + +/* + * GSM SACCH interleaving and burst mapping + * + * Interleaving: + * + * Given 456 coded input bits, form 4 blocks of 114 bits: + * + * i(B, j) = c(n, k) k = 0, ..., 455 + * n = 0, ..., N, N + 1, ... + * B = B_0 + 4n + (k mod 4) + * j = 2(49k mod 57) + ((k mod 8) div 4) + * + * Mapping on Burst: + * + * e(B, j) = i(B, j) + * e(B, 59 + j) = i(B, 57 + j) j = 0, ..., 56 + * e(B, 57) = h_l(B) + * e(B, 58) = h_n(B) + * + * Where h_l(B) and h_n(B) are bits in burst B indicating flags. + */ + +/* +static void interleave(unsigned char *data, unsigned char *iBLOCK) { + + int j, k, B; + + // for each bit in input data + for(k = 0; k < CONV_SIZE; k++) { + B = k % 4; + j = 2 * ((49 * k) % 57) + ((k % 8) / 4); + iBLOCK[B * iBLOCK_SIZE + j] = data[k]; + } +} + */ + + +static void decode_interleave(unsigned char *data, unsigned char *iBLOCK) { + + int j, k, B; + + for(k = 0; k < CONV_SIZE; k++) { + B = k % 4; + j = 2 * ((49 * k) % 57) + ((k % 8) / 4); + data[k] = iBLOCK[B * iBLOCK_SIZE + j]; + } +} + + +/* +static void burstmap(unsigned char *iBLOCK, unsigned char *eBLOCK, + unsigned char hl, unsigned char hn) { + + int j; + + for(j = 0; j < 57; j++) { + eBLOCK[j] = iBLOCK[j]; + eBLOCK[j + 59] = iBLOCK[j + 57]; + } + eBLOCK[57] = hl; + eBLOCK[58] = hn; +} + */ + + +static void decode_burstmap(unsigned char *iBLOCK, unsigned char *eBLOCK, + unsigned char *hl, unsigned char *hn) { + + int j; + + for(j = 0; j < 57; j++) { + iBLOCK[j] = eBLOCK[j]; + iBLOCK[j + 57] = eBLOCK[j + 59]; + } + *hl = eBLOCK[57]; + *hn = eBLOCK[58]; +} + + +/* + * Transmitted bits are sent least-significant first. + */ +static int compress_bits(unsigned char *dbuf, unsigned int dbuf_len, + unsigned char *sbuf, unsigned int sbuf_len) { + + unsigned int i, j, c, pos = 0; + + if(dbuf_len < ((sbuf_len + 7) >> 3)) + return -1; + + for(i = 0; i < sbuf_len; i += 8) { + for(j = 0, c = 0; (j < 8) && (i + j < sbuf_len); j++) + c |= (!!sbuf[i + j]) << j; + dbuf[pos++] = c & 0xff; + } + return pos; +} + + +int get_ns_l3_len(unsigned char *data, unsigned int datalen) { + + if((data[0] & 3) != 1) { + fprintf(stderr, "error: get_ns_l3_len: pseudo-length reserved " + "bits bad (%2.2x)\n", data[0] & 3); + return -1; + } + return (data[0] >> 2); +} + + +static unsigned char *decode_sacch(unsigned char *e0, unsigned char *e1, + unsigned char *e2, unsigned char *e3, unsigned int *datalen) { + + int errors, len, data_size = (DATA_BLOCK_SIZE + 7) >> 3; + unsigned char conv_data[CONV_SIZE], iBLOCK[BLOCKS][iBLOCK_SIZE], + hl, hn, decoded_data[PARITY_OUTPUT_SIZE], *data; + + if(!(data = (unsigned char *)malloc(data_size))) { + throw std::runtime_error("error: decode_cch: malloc"); + } + + if(datalen) + *datalen = 0; + + // unmap the bursts + decode_burstmap(iBLOCK[0], e0, &hl, &hn); // XXX ignore stealing bits + decode_burstmap(iBLOCK[1], e1, &hl, &hn); + decode_burstmap(iBLOCK[2], e2, &hl, &hn); + decode_burstmap(iBLOCK[3], e3, &hl, &hn); + + // remove interleave + decode_interleave(conv_data, (unsigned char *)iBLOCK); + + // Viterbi decode + errors = conv_decode(decoded_data, conv_data); + + // check parity + if(errors || parity_check(decoded_data)) { + // fprintf(stderr, "error: sacch: parity error (%d)\n", errors); + return 0; + } + if((len = compress_bits(data, data_size, decoded_data, + DATA_BLOCK_SIZE)) < 0) { + fprintf(stderr, "error: compress_bits\n"); + return 0; + } + if((unsigned int)len < data_size) { + fprintf(stderr, "error: buf too small (%d < %d)\n", + sizeof(data), len); + return 0; + } + + if(datalen) + *datalen = (unsigned int)len; + return data; +} + + +/* + * decode_cch + * + * Decode a "common" control channel. Most control channels use + * the same burst, interleave, Viterbi and parity configuration. + * The documentation for the control channels defines SACCH first + * and then just keeps referring to that. + * + * The current (investigated) list is as follows: + * + * BCCH Norm + * BCCH Ext + * PCH + * AGCH + * CBCH (SDCCH/4) + * CBCH (SDCCH/8) + * SDCCH/4 + * SACCH/C4 + * SDCCH/8 + * SACCH/C8 + * + * We provide two functions, one for where all four bursts are + * contiguous, and one where they aren't. + */ +unsigned char *decode_cch(unsigned char *e0, unsigned char *e1, + unsigned char *e2, unsigned char *e3, unsigned int *datalen) { + + return decode_sacch(e0, e1, e2, e3, datalen); +} + + +unsigned char *decode_cch(unsigned char *e, unsigned int *datalen) { + + return decode_sacch(e, e + eBLOCK_SIZE, e + 2 * eBLOCK_SIZE, + e + 3 * eBLOCK_SIZE, datalen); +} diff --git a/gssm/src/lib/cch.h b/gssm/src/lib/cch.h new file mode 100644 index 0000000..bb1d629 --- /dev/null +++ b/gssm/src/lib/cch.h @@ -0,0 +1,42 @@ +// $Id: cch.h,v 1.1.1.1 2007-06-01 04:26:57 jl Exp $ + +/* + * decode_cch + * + * Decode a "common" control channel. Most control channels use + * the same burst, interleave, Viterbi and parity configuration. + * The documentation for the control channels defines SACCH first + * and then just keeps referring to that. + * + * The current (investigated) list is as follows: + * + * BCCH Norm + * BCCH Ext + * PCH + * AGCH + * CBCH (SDCCH/4) + * CBCH (SDCCH/8) + * SDCCH/4 + * SACCH/C4 + * SDCCH/8 + * SACCH/C8 + * + * We provide two functions, one for where all four bursts are + * contiguous, and one where they aren't. + */ + +#define DATA_BLOCK_SIZE 184 +#define PARITY_SIZE 40 +#define FLUSH_BITS_SIZE 4 +#define PARITY_OUTPUT_SIZE (DATA_BLOCK_SIZE + PARITY_SIZE + FLUSH_BITS_SIZE) + +#define CONV_INPUT_SIZE PARITY_OUTPUT_SIZE +#define CONV_SIZE (2 * CONV_INPUT_SIZE) + +#define BLOCKS 4 +#define iBLOCK_SIZE (CONV_SIZE / BLOCKS) +#define eBLOCK_SIZE (iBLOCK_SIZE + 2) + +unsigned char *decode_cch(unsigned char *, unsigned char *, unsigned char *, + unsigned char *, unsigned int *); +unsigned char *decode_cch(unsigned char *, unsigned int *); diff --git a/gssm/src/lib/display.cc b/gssm/src/lib/display.cc new file mode 100644 index 0000000..086b48a --- /dev/null +++ b/gssm/src/lib/display.cc @@ -0,0 +1,46 @@ +// $Id: display.cc,v 1.1.1.1 2007-06-01 04:26:57 jl Exp $ + +#include <stdio.h> +#include <stdlib.h> + + +void dump_raw(unsigned char *buf, unsigned int len) { + + unsigned int i; + + for(i = 0; i < len; i++) { + printf("%2.2x", buf[i]); + if(!((i + 1) % 4)) + printf(" "); + } + printf("\n"); +} + + +void display_raw(unsigned char *buf, unsigned int len) { + + unsigned int i, j, c; + unsigned long long v; + + for(i = 0; i < len; i += 64) { + v = 0; + for(j = 0; (j < 64) && (i + j < len); j++) { + printf("%d", buf[i + j]); + if(!((j + 1) % 4)) + printf(" "); + v = (v << 1) | buf[i + j]; + } + for(; j < 64; j++) { + printf(" "); + if(!((j + 1) % 4)) + printf(" "); + v <<= 1; + } + printf("\t"); + for(j = 0; j < 8; j++) { + c = (v >> (8 * (7 - j))) & 0xff; + printf("%2.2x ", c); + } + printf("\n"); + } +} diff --git a/gssm/src/lib/display.h b/gssm/src/lib/display.h new file mode 100644 index 0000000..cacd931 --- /dev/null +++ b/gssm/src/lib/display.h @@ -0,0 +1,4 @@ +// $Id: display.h,v 1.1.1.1 2007-06-01 04:26:57 jl Exp $ + +void dump_raw(unsigned char *, unsigned int); +void display_raw(unsigned char *, unsigned int); diff --git a/gssm/src/lib/gsm_constants.h b/gssm/src/lib/gsm_constants.h new file mode 100644 index 0000000..6f4f07e --- /dev/null +++ b/gssm/src/lib/gsm_constants.h @@ -0,0 +1,76 @@ +// $Id: gsm_constants.h,v 1.1.1.1 2007-06-01 04:26:57 jl Exp $ + +#pragma once + +static const double QN_TIME = 12.0l / 13.0l / 1000000.0l; +static const double BN_TIME = 4.0l * QN_TIME; +static const double TN_TIME = 156.25l * BN_TIME; +static const double FN_TIME = 8.0l * TN_TIME; + +static const double GSM_RATE = 812500.0l / 3.0l; +static const int MAX_FN = 26 * 51 * 2048; + +static const int BURST_LENGTH = 156; + + +/* + * frequency correction: + * tn = 0 fnm51 = 0 + * + * sync: + * tn = 0 fnm51 = 1 + * tn = 0 fnm51 = 11 + * tn = 0 fnm51 = 21 + * tn = 0 fnm51 = 31 + * tn = 0 fnm51 = 41 + * + * bcch: + * tn = 0 fnm51 = 2, 3, 4, 5 + * tn = 2 fnm51 = 2, 3, 4, 5 + * tn = 4 fnm51 = 2, 3, 4, 5 + * tn = 6 fnm51 = 2, 3, 4, 5 + * + * bcch_ext: + * tn = 0 fnm51 = 6, 7, 8, 9 + * tn = 2 fnm51 = 6, 7, 8, 9 + * tn = 4 fnm51 = 6, 7, 8, 9 + * tn = 6 fnm51 = 6, 7, 8, 9 + * + * pch and agch: + * tn = 0 fnm51 = 6, 7, 8, 9 + * tn = 0 fnm51 = 12, 13, 14, 15 + * tn = 0 fnm51 = 16, 17, 18, 19 + * tn = 0 fnm51 = 22, 23, 24, 25 + * tn = 0 fnm51 = 26, 27, 28, 29 + * tn = 0 fnm51 = 32, 33, 34, 35 + * tn = 0 fnm51 = 36, 37, 38, 39 + * tn = 0 fnm51 = 42, 43, 44, 45 + * tn = 0 fnm51 = 46, 47, 48, 49 + * tn = 2 fnm51 = 6, 7, 8, 9 + * tn = 2 fnm51 = 12, 13, 14, 15 + * tn = 2 fnm51 = 16, 17, 18, 19 + * tn = 2 fnm51 = 22, 23, 24, 25 + * tn = 2 fnm51 = 26, 27, 28, 29 + * tn = 2 fnm51 = 32, 33, 34, 35 + * tn = 2 fnm51 = 36, 37, 38, 39 + * tn = 2 fnm51 = 42, 43, 44, 45 + * tn = 2 fnm51 = 46, 47, 48, 49 + * tn = 4 fnm51 = 6, 7, 8, 9 + * tn = 4 fnm51 = 12, 13, 14, 15 + * tn = 4 fnm51 = 16, 17, 18, 19 + * tn = 4 fnm51 = 22, 23, 24, 25 + * tn = 4 fnm51 = 26, 27, 28, 29 + * tn = 4 fnm51 = 32, 33, 34, 35 + * tn = 4 fnm51 = 36, 37, 38, 39 + * tn = 4 fnm51 = 42, 43, 44, 45 + * tn = 4 fnm51 = 46, 47, 48, 49 + * tn = 6 fnm51 = 6, 7, 8, 9 + * tn = 6 fnm51 = 12, 13, 14, 15 + * tn = 6 fnm51 = 16, 17, 18, 19 + * tn = 6 fnm51 = 22, 23, 24, 25 + * tn = 6 fnm51 = 26, 27, 28, 29 + * tn = 6 fnm51 = 32, 33, 34, 35 + * tn = 6 fnm51 = 36, 37, 38, 39 + * tn = 6 fnm51 = 42, 43, 44, 45 + * tn = 6 fnm51 = 46, 47, 48, 49 + */ diff --git a/gssm/src/lib/gssm.i b/gssm/src/lib/gssm.i new file mode 100644 index 0000000..fbac240 --- /dev/null +++ b/gssm/src/lib/gssm.i @@ -0,0 +1,28 @@ +// $Id: gssm.i,v 1.2 2007-07-07 16:31:42 jl Exp $ + +%include "exception.i" +%import "gnuradio.i" + +%{ +#include "gnuradio_swig_bug_workaround.h" +#include "gssm_sink.h" +#include <stdexcept> +%} + + +GR_SWIG_BLOCK_MAGIC(gssm, sink); +gssm_sink_sptr gssm_make_sink(double); + +class gssm_sink : public gr_sync_block { + +public: + int d_search_fc_count; + int d_found_fc_count; + int d_valid_s; + int d_invalid_s; + + void stats(); + +private: + gssm_sink(double); +}; diff --git a/gssm/src/lib/gssm_sink.cc b/gssm/src/lib/gssm_sink.cc new file mode 100644 index 0000000..62c6f33 --- /dev/null +++ b/gssm/src/lib/gssm_sink.cc @@ -0,0 +1,765 @@ +/* + * $Id: gssm_sink.cc,v 1.2 2007-07-07 16:31:42 jl Exp $ + */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include <exception> +#include <stdexcept> +#include <string.h> + +#include <gr_io_signature.h> +#include <gr_buffer.h> +#include <gr_math.h> + +#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; +} diff --git a/gssm/src/lib/gssm_sink.h b/gssm/src/lib/gssm_sink.h new file mode 100644 index 0000000..3e8a57f --- /dev/null +++ b/gssm/src/lib/gssm_sink.h @@ -0,0 +1,125 @@ +// $Id: gssm_sink.h,v 1.2 2007-07-07 16:31:42 jl Exp $ + +#pragma once + +#include <linux/if_ether.h> +#include <gr_sync_block.h> +#include <gri_mmse_fir_interpolator.h> +#include "gssm_state.h" + +class gssm_sink; +typedef boost::shared_ptr<gssm_sink> gssm_sink_sptr; +gssm_sink_sptr gssm_make_sink(double); + +class gssm_sink : public gr_sync_block { + +public: + ~gssm_sink(void); + + int work(int nitems, gr_vector_const_void_star &input_items, + gr_vector_void_star &output_items); + +private: + // sample speeds + double d_sps; // samples per second + double d_samples_per_symbol; + + // M&M clock recovery + gri_mmse_fir_interpolator *d_interp; + double d_mu; + double d_gain_mu; + double d_omega; + double d_gain_omega; + double d_omega_relative_limit; + double d_max_omega; + double d_min_omega; + float d_last_sample; + + double d_mu_bak; + double d_omega_bak; + double d_last_sample_bak; + + int d_bitno; + + // buffers + gr_buffer_sptr d_buf_qd; + gr_buffer_reader_sptr d_qd_reader; + gr_buffer_sptr d_buf_mm; + gr_buffer_reader_sptr d_mm_reader; + + // quad demod + gr_complex d_qd_last; + double d_qd_gain; + + // GSM BTS timing + int d_tn; // time slot + int d_fn; // frame number + int d_fnm51; // frame number mod 51 + int d_fnm102; // frame number mod 102 + int d_bsic; // current bsic + + // program state + gssm_state_t d_state; + + // buffer to hold physical data + unsigned char * d_phy_buf; + int * d_phy_ind; + + // Wireshark interface + int d_tunfd; // TUN fd + unsigned char d_ether_addr[ETH_ALEN]; + + /*******************************************************************/ + + friend gssm_sink_sptr gssm_make_sink(double); + gssm_sink(double); + + int search_state_fc(const float *, int); + int search_state_s(const float *, int); + int search_state_data(const float *, int); + + void search_sch(const unsigned char *); + int handle_sch(const unsigned char *, int *, int *); + + void next_timeslot(void); + + int check_logical_channel(int, int, int, int); + void check_logical_channels(void); + + int mm_demod(const float *, int, float *, int &); + int quad_demod(const gr_complex *, int, float *, int &); + int process_input(const gr_complex *, int); + int process_qd(const gr_complex *, int); + + void save_clock(); + void restore_clock(); + void reset_clock(); + void flush_buffers(); + void reset_state(); + + + /*******************************************************************/ + // Debug + + int check_num_invalid_s(); + +public: + int 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; + void stats(); +}; diff --git a/gssm/src/lib/gssm_state.h b/gssm/src/lib/gssm_state.h new file mode 100644 index 0000000..44400a2 --- /dev/null +++ b/gssm/src/lib/gssm_state.h @@ -0,0 +1,9 @@ +// $Id: gssm_state.h,v 1.1.1.1 2007-06-01 04:26:57 jl Exp $ + +#pragma once + +typedef enum { + state_fc, + state_s, + state_data +} gssm_state_t; diff --git a/gssm/src/lib/rr_decode.cc b/gssm/src/lib/rr_decode.cc new file mode 100644 index 0000000..312c883 --- /dev/null +++ b/gssm/src/lib/rr_decode.cc @@ -0,0 +1,236 @@ +// $Id: rr_decode.cc,v 1.1.1.1 2007-06-01 04:26:57 jl Exp $ + +#include <stdio.h> +#include <stdlib.h> +#include <unistd.h> + +static char *pd_string(unsigned char pd) { + + switch(pd) { + case 0: + return "group call control"; + case 1: + return "broadcast call control"; + case 2: + return "reserved (PDSS1 in earlier phases)"; + case 3: + return "call control; call related SS messages"; + case 4: + return "GPRS Transparent Transport Protocol (GTTP)"; + case 5: + return "mobility management messages"; + case 6: + return "radio resources management messages"; + case 8: + return "GPRS mobility management messages"; + case 9: + return "SMS messages"; + case 10: + return "GPRS session management messages"; + case 11: + return "non-call related SS messages"; + case 12: + return "location services"; + case 14: + return "reserved for extension of the PD"; + case 15: + return "reserved for tests procedures"; + default: + return "unknown PD"; + } +} + + +static char *message_type_rrm_string(unsigned char mt) { + + switch(mt) { + case 0x3c: + return "channel establishment message: RR " + "initialization request"; + case 0x3b: + return "channel establishment message: aditional " + "assignment"; + case 0x3f: + return "channel establishment message: immediate " + "assignment"; + case 0x39: + return "channel establishment message: immediate " + "assignment extended"; + case 0x3a: + return "channel establishment message: immediate " + "assignment reject"; + + case 0x35: + return "ciphering message: ciphering mode command"; + case 0x32: + return "ciphering message: ciphering mode complete"; + + case 0x30: + return "configuration change message: configuration " + "change command"; + case 0x31: + return "configuration change message: configuration " + "change acknowledgement"; + case 0x33: + return "configuration change message: configuration " + "change reject"; + + case 0x2e: + return "handover message: assignment command"; + case 0x29: + return "handover message: assignment complete"; + case 0x2f: + return "handover message: assignment failure"; + case 0x2b: + return "handover message: handover command"; + case 0x2c: + return "handover message: handover complete"; + case 0x28: + return "handover message: handover failure"; + case 0x2d: + return "handover message: physical information"; + + case 0x08: + return "RR-cell change order"; + + case 0x23: + return "PDCH assignment command"; + + case 0x0d: + return "channel release message: channel release"; + case 0x0a: + return "channel release message: partial release"; + case 0x0f: + return "channel release message: partial release " + "complete"; + + case 0x21: + return "paging and notification message: paging " + "request type 1"; + case 0x22: + return "paging and notification message: paging " + "request type 2"; + case 0x24: + return "paging and notification message: paging " + "request type 3"; + case 0x27: + return "paging and notification message: paging " + "response"; + case 0x20: + return "paging and notification message: " + "notification / NCH"; + case 0x25: + return "paging and notification message: " + "notification / FACCH"; + case 0x26: + return "paging and notification message: " + "notification response"; + + case 0x0b: + return "reserved"; + + case 0x18: + return "system information message: type 8"; + case 0x19: + return "system information message: type 1"; + case 0x1a: + return "system information message: type 2"; + case 0x1b: + return "system information message: type 3"; + case 0x1c: + return "system information message: type 4"; + case 0x1d: + return "system information message: type 5"; + case 0x1e: + return "system information message: type 6"; + case 0x1f: + return "system information message: type 7"; + case 0x02: + return "system information message: type 2bis"; + case 0x03: + return "system information message: type 2ter"; + case 0x05: + return "system information message: type 5bis"; + case 0x06: + return "system information message: type 5ter"; + case 0x04: + return "system information message: type 9"; + case 0x00: + return "system information message: type 13"; + case 0x3d: + return "system information message: type 16"; + case 0x3e: + return "system information message: type 17"; + + case 0x10: + return "miscellaneous message: channel mode modify"; + case 0x12: + return "miscellaneous message: RR status"; + case 0x17: + return "miscellaneous message: channel mode modify " + "acknowledge"; + case 0x14: + return "miscellaneous message: frequency redefinition"; + case 0x15: + return "miscellaneous message: measurement report"; + case 0x16: + return "miscellaneous message: classmark change"; + case 0x13: + return "miscellaneous message: classmark enquiry"; + case 0x36: + return "miscellaneous message: extended measurement " + "report"; + case 0x37: + return "miscellaneous message: extended measurement " + "order"; + case 0x34: + return "miscellaneous message: GPRS suspension request"; + + case 0x09: + return "VGCS uplink control message: uplink grant"; + case 0x0e: + return "VGCS uplink control message: uplink release"; + case 0x0c: + return "VGCS uplink control message: uplink free"; + case 0x2a: + return "VGCS uplink control message: uplink busy"; + case 0x11: + return "VGCS uplink control message: talker indication"; + + case 0x38: + return "application message: application information"; + + default: + return "unknown radio resource management message " + "type"; + } +} + + +void display_l3(unsigned char *buf, unsigned int buflen) { + + printf("(%d) PD: %s: (%2.2x) %s\n", buflen, pd_string(buf[0] & 0xf), + buf[1], message_type_rrm_string(buf[1])); +} + + +void display_ns_l3(unsigned char *data, unsigned int datalen) { + + int len; + + // bit 1 == 1, bit 2 == 0 + if((data[0] & 3) != 1) { + fprintf(stderr, "error: display_ns_l3: pseudo-length reserved " + "bits bad (%2.2x)\n", data[0] & 3); + return; + } + len = data[0] >> 2; + if(datalen < len) { + fprintf(stderr, "error: display_ns_l3: bad data length " + "(%d < %d)\n", datalen, len); + return; + } + + printf("L3 length: %d\n", len); + display_l3(data + 1, datalen - 1); +} diff --git a/gssm/src/lib/rr_decode.h b/gssm/src/lib/rr_decode.h new file mode 100644 index 0000000..42d6266 --- /dev/null +++ b/gssm/src/lib/rr_decode.h @@ -0,0 +1,4 @@ +// $Id: rr_decode.h,v 1.1.1.1 2007-06-01 04:26:57 jl Exp $ + +void display_l3(unsigned char *, unsigned int); +void display_ns_l3(unsigned char *, unsigned int); diff --git a/gssm/src/lib/rrm.h b/gssm/src/lib/rrm.h new file mode 100644 index 0000000..52be6a5 --- /dev/null +++ b/gssm/src/lib/rrm.h @@ -0,0 +1,22 @@ +// $Id: rrm.h,v 1.1.1.1 2007-06-01 04:26:57 jl Exp $ + +#pragma once + +typedef struct { + unsigned char b0:1, + b1:1, + len:6; +} l2_pseudo_length_s; + +typedef union { + unsigned char v; + l2_pseudo_length_s b; +} l2_pseudo_length_t; + + +typedef struct { + unsigned char pd:4, + si:4; + unsigned char mt; + unsigned char ie[0]; +} l3_h_t; diff --git a/gssm/src/lib/sch.cc b/gssm/src/lib/sch.cc new file mode 100644 index 0000000..c336dc1 --- /dev/null +++ b/gssm/src/lib/sch.cc @@ -0,0 +1,320 @@ +// $Id: sch.cc,v 1.1.1.1 2007-06-01 04:26:57 jl Exp $ + +#include <stdio.h> +#include <stdlib.h> +#include <unistd.h> +#include <string.h> +#include "burst_types.h" + +/* + * Synchronization channel. + * + * Timeslot Repeat length Frame Number (mod repeat length) + * 0 51 1, 11, 21, 31, 41 + */ + +/* + * Parity (FIRE) for the GSM SCH. + * + * g(x) = x^10 + x^8 + x^6 + x^5 + x^4 + x^2 + 1 + */ +#define DATA_BLOCK_SIZE 25 +#define PARITY_SIZE 10 +#define TAIL_BITS_SIZE 4 +#define PARITY_OUTPUT_SIZE (DATA_BLOCK_SIZE + PARITY_SIZE + TAIL_BITS_SIZE) + +static const unsigned char parity_polynomial[PARITY_SIZE + 1] = { + 1, 0, 1, 0, 1, 1, 1, 0, 1, 0, 1 +}; + +static const unsigned char parity_remainder[PARITY_SIZE] = { + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1 +}; + + +static void parity_encode(unsigned char *d, unsigned char *p) { + + unsigned int i; + unsigned char buf[DATA_BLOCK_SIZE + PARITY_SIZE], *q; + + memcpy(buf, d, DATA_BLOCK_SIZE); + memset(buf + DATA_BLOCK_SIZE, 0, PARITY_SIZE); + + for(q = buf; q < buf + DATA_BLOCK_SIZE; q++) + if(*q) + for(i = 0; i < PARITY_SIZE + 1; i++) + q[i] ^= parity_polynomial[i]; + for(i = 0; i < PARITY_SIZE; i++) + p[i] = !buf[DATA_BLOCK_SIZE + i]; +} + + +static int parity_check(unsigned char *d) { + + unsigned int i; + unsigned char buf[DATA_BLOCK_SIZE + PARITY_SIZE], *q; + + memcpy(buf, d, DATA_BLOCK_SIZE + PARITY_SIZE); + + for(q = buf; q < buf + DATA_BLOCK_SIZE; q++) + if(*q) + for(i = 0; i < PARITY_SIZE + 1; i++) + q[i] ^= parity_polynomial[i]; + return memcmp(buf + DATA_BLOCK_SIZE, parity_remainder, PARITY_SIZE); +} + + +/* + * Convolutional encoding and Viterbi decoding for the GSM SCH. + * (Equivalent to the GSM SACCH.) + * + * G_0 = 1 + x^3 + x^4 + * G_1 = 1 + x + x^3 + x^4 + * + * i.e., + * + * c_{2k} = u_k + u_{k - 3} + u_{k - 4} + * c_{2k + 1} = u_k + u_{k - 1} + u_{k - 3} + u_{k - 4} + */ +#define CONV_INPUT_SIZE PARITY_OUTPUT_SIZE +#define CONV_SIZE (2 * CONV_INPUT_SIZE) +#define K 5 +#define MAX_ERROR (2 * CONV_INPUT_SIZE + 1) + + +/* + * Given the current state and input bit, what are the output bits? + * + * encode[current_state][input_bit] + */ +static const unsigned int encode[1 << (K - 1)][2] = { + {0, 3}, {3, 0}, {3, 0}, {0, 3}, + {0, 3}, {3, 0}, {3, 0}, {0, 3}, + {1, 2}, {2, 1}, {2, 1}, {1, 2}, + {1, 2}, {2, 1}, {2, 1}, {1, 2} +}; + + +/* + * Given the current state and input bit, what is the next state? + * + * next_state[current_state][input_bit] + */ +static const unsigned int next_state[1 << (K - 1)][2] = { + {0, 8}, {0, 8}, {1, 9}, {1, 9}, + {2, 10}, {2, 10}, {3, 11}, {3, 11}, + {4, 12}, {4, 12}, {5, 13}, {5, 13}, + {6, 14}, {6, 14}, {7, 15}, {7, 15} +}; + + +/* + * Given the previous state and the current state, what input bit caused + * the transition? If it is impossible to transition between the two + * states, the value is 2. + * + * prev_next_state[previous_state][current_state] + */ +static const unsigned int prev_next_state[1 << (K - 1)][1 << (K - 1)] = { + { 0, 2, 2, 2, 2, 2, 2, 2, 1, 2, 2, 2, 2, 2, 2, 2}, + { 0, 2, 2, 2, 2, 2, 2, 2, 1, 2, 2, 2, 2, 2, 2, 2}, + { 2, 0, 2, 2, 2, 2, 2, 2, 2, 1, 2, 2, 2, 2, 2, 2}, + { 2, 0, 2, 2, 2, 2, 2, 2, 2, 1, 2, 2, 2, 2, 2, 2}, + { 2, 2, 0, 2, 2, 2, 2, 2, 2, 2, 1, 2, 2, 2, 2, 2}, + { 2, 2, 0, 2, 2, 2, 2, 2, 2, 2, 1, 2, 2, 2, 2, 2}, + { 2, 2, 2, 0, 2, 2, 2, 2, 2, 2, 2, 1, 2, 2, 2, 2}, + { 2, 2, 2, 0, 2, 2, 2, 2, 2, 2, 2, 1, 2, 2, 2, 2}, + { 2, 2, 2, 2, 0, 2, 2, 2, 2, 2, 2, 2, 1, 2, 2, 2}, + { 2, 2, 2, 2, 0, 2, 2, 2, 2, 2, 2, 2, 1, 2, 2, 2}, + { 2, 2, 2, 2, 2, 0, 2, 2, 2, 2, 2, 2, 2, 1, 2, 2}, + { 2, 2, 2, 2, 2, 0, 2, 2, 2, 2, 2, 2, 2, 1, 2, 2}, + { 2, 2, 2, 2, 2, 2, 0, 2, 2, 2, 2, 2, 2, 2, 1, 2}, + { 2, 2, 2, 2, 2, 2, 0, 2, 2, 2, 2, 2, 2, 2, 1, 2}, + { 2, 2, 2, 2, 2, 2, 2, 0, 2, 2, 2, 2, 2, 2, 2, 1}, + { 2, 2, 2, 2, 2, 2, 2, 0, 2, 2, 2, 2, 2, 2, 2, 1} +}; + + +static inline unsigned int hamming_distance2(unsigned int w) { + + return (w & 1) + !!(w & 2); +} + + +static void conv_encode(unsigned char *data, unsigned char *output) { + + unsigned int i, state = 0, o; + + // encode data + for(i = 0; i < CONV_INPUT_SIZE; i++) { + o = encode[state][data[i]]; + state = next_state[state][data[i]]; + *output++ = !!(o & 2); + *output++ = o & 1; + } +} + + +static int conv_decode(unsigned char *data, unsigned char *output) { + + int i, t; + unsigned int rdata, state, nstate, b, o, distance, accumulated_error, + min_state, min_error, cur_state; + + unsigned int ae[1 << (K - 1)]; + unsigned int nae[1 << (K - 1)]; // next accumulated error + unsigned int state_history[1 << (K - 1)][CONV_INPUT_SIZE + 1]; + + // initialize accumulated error, assume starting state is 0 + for(i = 0; i < (1 << (K - 1)); i++) + ae[i] = nae[i] = MAX_ERROR; + ae[0] = 0; + + // build trellis + for(t = 0; t < CONV_INPUT_SIZE; t++) { + + // get received data symbol + rdata = (data[2 * t] << 1) | data[2 * t + 1]; + + // for each state + for(state = 0; state < (1 << (K - 1)); state++) { + + // make sure this state is possible + if(ae[state] >= MAX_ERROR) + continue; + + // find all states we lead to + for(b = 0; b < 2; b++) { + + // get next state given input bit b + nstate = next_state[state][b]; + + // find output for this transition + o = encode[state][b]; + + // calculate distance from received data + distance = hamming_distance2(rdata ^ o); + + // choose surviving path + accumulated_error = ae[state] + distance; + if(accumulated_error < nae[nstate]) { + + // save error for surviving state + nae[nstate] = accumulated_error; + + // update state history + state_history[nstate][t + 1] = state; + } + } + } + + // get accumulated error ready for next time slice + for(i = 0; i < (1 << (K - 1)); i++) { + ae[i] = nae[i]; + nae[i] = MAX_ERROR; + } + } + + // the final state is the state with the fewest errors + min_state = (unsigned int)-1; + min_error = MAX_ERROR; + for(i = 0; i < (1 << (K - 1)); i++) { + if(ae[i] < min_error) { + min_state = i; + min_error = ae[i]; + } + } + + // trace the path + cur_state = min_state; + for(t = CONV_INPUT_SIZE; t >= 1; t--) { + min_state = cur_state; + cur_state = state_history[cur_state][t]; // get previous + output[t - 1] = prev_next_state[cur_state][min_state]; + } + + // return the number of errors detected (hard-decision) + return min_error; +} + + +int decode_sch(const unsigned char *buf, int *fn_o, int *bsic_o) { + + int errors, bsic, t1, t2, t3p, t3, fn, tt; + unsigned char data[CONV_SIZE], decoded_data[PARITY_OUTPUT_SIZE]; + + // extract encoded data from synchronization burst + memcpy(data, buf + SB_EDATA_OS_1, SB_EDATA_LEN_1); + memcpy(data + SB_EDATA_LEN_1, buf + SB_EDATA_OS_2, SB_EDATA_LEN_2); + + // Viterbi decode + if(errors = conv_decode(data, decoded_data)) { + // fprintf(stderr, "error: sch: conv_decode (%d)\n", errors); + return errors; + } + + // check parity + if(parity_check(decoded_data)) { + // fprintf(stderr, "error: sch: parity failed\n"); + return 1; + } + + // Synchronization channel information, 44.018 page 171. (V7.2.0) + bsic = + (decoded_data[ 7] << 5) | + (decoded_data[ 6] << 4) | + (decoded_data[ 5] << 3) | + (decoded_data[ 4] << 2) | + (decoded_data[ 3] << 1) | + (decoded_data[ 2] << 0); + t1 = + (decoded_data[ 1] << 10) | + (decoded_data[ 0] << 9) | + (decoded_data[15] << 8) | + (decoded_data[14] << 7) | + (decoded_data[13] << 6) | + (decoded_data[12] << 5) | + (decoded_data[11] << 4) | + (decoded_data[10] << 3) | + (decoded_data[ 9] << 2) | + (decoded_data[ 8] << 1) | + (decoded_data[23] << 0); + t2 = + (decoded_data[22] << 4) | + (decoded_data[21] << 3) | + (decoded_data[20] << 2) | + (decoded_data[19] << 1) | + (decoded_data[18] << 0); + t3p = + (decoded_data[17] << 2) | + (decoded_data[16] << 1) | + (decoded_data[24] << 0); + + t3 = 10 * t3p + 1; + + // modulo arithmetic + tt = t3; + while(tt < t2) + tt += 26; + tt = (tt - t2) % 26; + fn = (51 * 26 * t1) + (51 * tt) + t3; + + /* + * BSIC: Base Station Identification Code + * BCC: Base station Color Code + * NCC: Network Color Code + * + * FN: Frame Number + */ + /* + printf("bsic: %x (bcc: %u; ncc: %u)\tFN: %u\n", bsic, bsic & 7, + (bsic >> 3) & 7, fn); + */ + + if(fn_o) + *fn_o = fn; + if(bsic_o) + *bsic_o = bsic; + + return 0; +} diff --git a/gssm/src/lib/sch.h b/gssm/src/lib/sch.h new file mode 100644 index 0000000..5f8b217 --- /dev/null +++ b/gssm/src/lib/sch.h @@ -0,0 +1,3 @@ +// $Id: sch.h,v 1.1.1.1 2007-06-01 04:26:57 jl Exp $ + +int decode_sch(const unsigned char *, int *, int *); diff --git a/gssm/src/lib/tun.cc b/gssm/src/lib/tun.cc new file mode 100644 index 0000000..2abda90 --- /dev/null +++ b/gssm/src/lib/tun.cc @@ -0,0 +1,125 @@ +// $Id: tun.cc,v 1.2 2007-07-07 16:31:42 jl Exp $ + +#include <stdio.h> +#include <stdlib.h> +#include <unistd.h> +#include <string.h> +#include <libgen.h> +#include <fcntl.h> +#include <sys/ioctl.h> +#include <sys/socket.h> +#include <linux/if_tun.h> +#include <linux/if.h> +#include <linux/if_ether.h> +#include <arpa/inet.h> + +int mktun(const char *chan_name, unsigned char *ether_addr) { + + struct ifreq ifr; + // struct ifreq ifw; + char if_name[IFNAMSIZ]; + int fd, one = 1; + // int sd; + + // construct TUN interface + if((fd = open("/dev/net/tun", O_RDWR)) == -1) { + perror("open"); + return -1; + } + memset(&ifr, 0, sizeof(ifr)); + ifr.ifr_flags = IFF_TAP | IFF_NO_PI; + snprintf(ifr.ifr_name, IFNAMSIZ, "%s", chan_name); + if(ioctl(fd, TUNSETIFF, (void *)&ifr) == -1) { + perror("TUNSETIFF"); + close(fd); + return -1; + } + + // save actual name + memcpy(if_name, ifr.ifr_name, IFNAMSIZ); + + // get ether addr + memset(&ifr, 0, sizeof(ifr)); + memcpy(ifr.ifr_name, if_name, IFNAMSIZ); + if(ioctl(fd, SIOCGIFHWADDR, (void *)&ifr) == -1) { + perror("SIOCGIFHWADDR"); + close(fd); + return -1; + } + memcpy(ether_addr, ifr.ifr_hwaddr.sa_data, ETH_ALEN); + + // set persistent + if(ioctl(fd, TUNSETPERSIST, (void *)&one) == -1) { + perror("TUNSETPERSIST"); + close(fd); + return -1; + } + + // set interface up + /* XXX must be root + if((sd = socket(AF_INET, SOCK_DGRAM, 0)) == -1) { + perror("socket"); + close(fd); + return -1; + } + + // get current flags + memset(&ifr, 0, sizeof(ifr)); + strncpy(ifr.ifr_name, if_name, IFNAMSIZ - 1); + if(ioctl(sd, SIOCGIFFLAGS, &ifr) == -1) { + perror("SIOCGIFFLAGS"); + close(sd); + close(fd); + return -1; + } + + // set up + memset(&ifw, 0, sizeof(ifw)); + strncpy(ifw.ifr_name, if_name, IFNAMSIZ - 1); + ifw.ifr_flags = ifr.ifr_flags | IFF_UP | IFF_RUNNING; + if(ioctl(sd, SIOCSIFFLAGS, &ifw) == -1) { + perror("SIOCSIFFLAGS"); + close(sd); + close(fd); + return -1; + } + close(sd); + */ + + return fd; +} + + +static inline int min(int a, int b) { + + return (a < b)? a : b; +} + + +static const unsigned int DEFAULT_MTU = 1500; +static const unsigned short ether_type = 0xfed5; // current dtap ethertype + +int write_interface(int fd, unsigned char *data, unsigned int data_len, + unsigned char *ether_addr) { + + unsigned char frame[DEFAULT_MTU]; // XXX buffer overflow? + struct ethhdr eh; + + if(fd < 0) + return data_len; + + memcpy(eh.h_dest, ether_addr, ETH_ALEN); + memcpy(eh.h_source, ether_addr, ETH_ALEN); + eh.h_proto = htons(ether_type); + + memcpy(frame, &eh, sizeof(eh)); + memcpy(frame + sizeof(eh), data, + min(data_len, sizeof(frame) - sizeof(eh))); + + if(write(fd, frame, sizeof(eh) + data_len) == -1) { + perror("write"); + return -1; + } + + return data_len; +} diff --git a/gssm/src/lib/tun.h b/gssm/src/lib/tun.h new file mode 100644 index 0000000..a7868c4 --- /dev/null +++ b/gssm/src/lib/tun.h @@ -0,0 +1,4 @@ +// $Id: tun.h,v 1.1.1.1 2007-06-01 04:26:57 jl Exp $ + +int mktun(const char *, unsigned char *); +int write_interface(int, unsigned char *, unsigned int, unsigned char *); |