summaryrefslogtreecommitdiff
path: root/gssm/src
diff options
context:
space:
mode:
Diffstat (limited to 'gssm/src')
-rw-r--r--gssm/src/Makefile.am4
-rw-r--r--gssm/src/lib/Makefile.am44
-rw-r--r--gssm/src/lib/buffer.h4
-rw-r--r--gssm/src/lib/burst_types.h217
-rw-r--r--gssm/src/lib/bursts.cc153
-rw-r--r--gssm/src/lib/bursts.h10
-rw-r--r--gssm/src/lib/cch.cc460
-rw-r--r--gssm/src/lib/cch.h42
-rw-r--r--gssm/src/lib/display.cc46
-rw-r--r--gssm/src/lib/display.h4
-rw-r--r--gssm/src/lib/gsm_constants.h76
-rw-r--r--gssm/src/lib/gssm.i28
-rw-r--r--gssm/src/lib/gssm_sink.cc764
-rw-r--r--gssm/src/lib/gssm_sink.h125
-rw-r--r--gssm/src/lib/gssm_state.h9
-rw-r--r--gssm/src/lib/rr_decode.cc236
-rw-r--r--gssm/src/lib/rr_decode.h4
-rw-r--r--gssm/src/lib/rrm.h22
-rw-r--r--gssm/src/lib/sch.cc320
-rw-r--r--gssm/src/lib/sch.h3
-rw-r--r--gssm/src/lib/tun.cc125
-rw-r--r--gssm/src/lib/tun.h4
-rw-r--r--gssm/src/mktun/Makefile.am7
-rw-r--r--gssm/src/mktun/mktun.c125
-rwxr-xr-xgssm/src/python/file_gssm.py45
-rwxr-xr-xgssm/src/python/file_stats.py46
-rwxr-xr-xgssm/src/python/gssm_stats.py85
-rwxr-xr-xgssm/src/python/gssm_usrp.py63
28 files changed, 3071 insertions, 0 deletions
diff --git a/gssm/src/Makefile.am b/gssm/src/Makefile.am
new file mode 100644
index 0000000..afa6595
--- /dev/null
+++ b/gssm/src/Makefile.am
@@ -0,0 +1,4 @@
+include $(top_srcdir)/Makefile.common
+
+SUBDIRS = lib mktun
+DIST_SUBDIRS = lib mktun
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..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 <exception>
+#include <stdexcept>
+
+#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 *);
diff --git a/gssm/src/mktun/Makefile.am b/gssm/src/mktun/Makefile.am
new file mode 100644
index 0000000..95dc50a
--- /dev/null
+++ b/gssm/src/mktun/Makefile.am
@@ -0,0 +1,7 @@
+# $Id: Makefile.am,v 1.3 2007-06-01 05:35:59 jl Exp $
+
+#include $(top_srcdir)/Makefile.common
+
+bin_PROGRAMS = mktun
+mktun_SOURCES = mktun.c
+
diff --git a/gssm/src/mktun/mktun.c b/gssm/src/mktun/mktun.c
new file mode 100644
index 0000000..e22d496
--- /dev/null
+++ b/gssm/src/mktun/mktun.c
@@ -0,0 +1,125 @@
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <string.h>
+#include <libgen.h>
+#include <getopt.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>
+
+
+static struct option long_options[] = {
+ { "delete", no_argument, 0, 'd'},
+ { "help", no_argument, 0, 'h'},
+ { 0, 0, 0, 0}
+};
+
+
+void usage(char *prog) {
+
+ fprintf(stderr, "note: you must be root (except perhaps to delete "
+ "the interface)\n");
+ fprintf(stderr, "usage: %s [--help | -h] | [--delete | -d] "
+ "<interface_name>\n", basename(prog));
+ exit(-1);
+}
+
+
+int main(int argc, char **argv) {
+
+ struct ifreq ifr, ifw;
+ char if_name[IFNAMSIZ], *chan_name = "gsm";
+ int option_index = 0, c, delete_if = 0, persist = 1, fd, sd;
+
+ while((c = getopt_long(argc, argv, "dh?", long_options, &option_index))
+ != -1) {
+ switch(c) {
+ case 'd':
+ delete_if = 1;
+ break;
+ case 'h':
+ case '?':
+ default:
+ usage(argv[0]);
+ break;
+ }
+ }
+
+ if(optind >= argc)
+ usage(argv[0]);
+
+ chan_name = argv[optind];
+
+ if(!delete_if && getuid() && geteuid())
+ usage(argv[0]);
+
+ // 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);
+
+ if(delete_if)
+ persist = 0;
+
+ if(ioctl(fd, TUNSETPERSIST, (void *)persist) == -1) {
+ perror("TUNSETPERSIST");
+ close(fd);
+ return -1;
+ }
+
+ if(delete_if) {
+ close(fd);
+ return 0;
+ }
+
+ // set interface up
+ 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);
+ close(fd);
+
+ return 0;
+}
diff --git a/gssm/src/python/file_gssm.py b/gssm/src/python/file_gssm.py
new file mode 100755
index 0000000..47013f3
--- /dev/null
+++ b/gssm/src/python/file_gssm.py
@@ -0,0 +1,45 @@
+#!/usr/bin/env python
+
+# $Id: file_gssm.py,v 1.3 2007-07-07 16:31:44 jl Exp $
+
+from gnuradio import gr, usrp, db_dbs_rx, blks
+from gnuradio.blksimpl import gmsk
+import gssm
+import sys
+
+#sps = 1000e3
+
+usrp_rate = 64e6
+decim_rate = 112
+sps = usrp_rate / decim_rate
+
+gsm_rate = 1625000.0 / 6.0
+
+#xcf = 150e3
+#xtw = 50e3
+#xm = -31127.933289
+
+file_name = "signal.data"
+
+
+class gssm_graph(gr.flow_graph):
+ def __init__(self, fname):
+ gr.flow_graph.__init__(self)
+
+ src = gr.file_source(gr.sizeof_gr_complex, fname)
+ gs = gssm.sink()
+ self.connect(src, gs)
+
+def main():
+ fname = file_name
+ if len(sys.argv) == 2:
+ fname = sys.argv[1]
+ try:
+ gg = gssm_graph(fname)
+ gg.run()
+
+ except KeyboardInterrupt:
+ pass
+
+if __name__ == '__main__':
+ main()
diff --git a/gssm/src/python/file_stats.py b/gssm/src/python/file_stats.py
new file mode 100755
index 0000000..cf8a109
--- /dev/null
+++ b/gssm/src/python/file_stats.py
@@ -0,0 +1,46 @@
+#!/usr/bin/env python
+
+# $Id: file_stats.py,v 1.1 2007-07-07 16:26:33 jl Exp $
+
+from gnuradio import gr, usrp, db_dbs_rx, blks
+from gnuradio.blksimpl import gmsk
+import gssm
+import sys
+
+#sps = 1000e3
+
+usrp_rate = 64e6
+decim_rate = 112
+sps = usrp_rate / decim_rate
+
+gsm_rate = 1625000.0 / 6.0
+
+#xcf = 150e3
+#xtw = 50e3
+#xm = -31127.933289
+
+file_name = "signal.data"
+
+
+class gssm_graph(gr.flow_graph):
+ def __init__(self, fname):
+ gr.flow_graph.__init__(self)
+
+ src = gr.file_source(gr.sizeof_gr_complex, fname)
+ self.gs = gs = gssm.sink(sps)
+ self.connect(src, gs)
+
+def main():
+ fname = file_name
+ if len(sys.argv) == 2:
+ fname = sys.argv[1]
+ try:
+ gg = gssm_graph(fname)
+ gg.run()
+ gg.gs.stats()
+
+ except KeyboardInterrupt:
+ pass
+
+if __name__ == '__main__':
+ main()
diff --git a/gssm/src/python/gssm_stats.py b/gssm/src/python/gssm_stats.py
new file mode 100755
index 0000000..43677d9
--- /dev/null
+++ b/gssm/src/python/gssm_stats.py
@@ -0,0 +1,85 @@
+#!/usr/bin/env python
+
+# $Id: gssm_stats.py,v 1.2 2007-07-07 16:31:44 jl Exp $
+
+from gnuradio import gr, usrp, db_dbs_rx, blks
+from gnuradio.blksimpl import gmsk
+import usrp_dbid
+import gssm
+import sys
+import time
+import thread
+
+# constant
+gsm_rate = 1625000.0 / 6.0
+
+# script constant
+decim = 112
+gain = 32
+
+# bts channel
+c0 = 874e6
+
+# experimental constant
+default_usrp_offset = 4e3
+
+# filter constants
+xcf = 150e3
+xtw = 50e3
+
+def display_stats(gs):
+ while 1:
+ print "%d:\t%d:%d" % \
+ (gs.d_found_fc_count, gs.d_valid_s, gs.d_invalid_s)
+ time.sleep(1)
+
+
+class gssm_flow_graph(gr.flow_graph):
+ def __init__(self, usrp_offset):
+ gr.flow_graph.__init__(self)
+
+ print "decim = %d, gain = %d, offset = %.2f" % \
+ (decim, gain, usrp_offset)
+ print "filter center %.2f, filter width %.2f" % \
+ (xcf, xtw)
+
+ u = usrp.source_c(decim_rate = decim)
+ s = usrp.pick_subdev(u, (usrp_dbid.DBS_RX,))
+ u.set_mux(usrp.determine_rx_mux_value(u, s))
+ subdev = usrp.selected_subdev(u, s)
+
+ if subdev.dbid() != usrp_dbid.DBS_RX:
+ raise Exception('dbs daughterboard not detected!')
+
+ subdev.set_gain(gain)
+
+ sps = u.adc_freq() / u.decim_rate()
+ if sps < 2 * gsm_rate:
+ raise Exception('sample rate too low')
+
+ u.tune(0, subdev, c0 + usrp_offset)
+
+ xt = gr.firdes.low_pass(1.0, sps, xcf, xtw,
+ gr.firdes.WIN_HAMMING)
+ xf = gr.fir_filter_ccf(1, xt)
+
+ self.gs = gs = gssm.sink()
+
+ self.connect(u, xf, gs)
+
+def main():
+ if len(sys.argv) == 2:
+ uo = float(sys.argv[1])
+ else:
+ uo = default_usrp_offset
+ g = gssm_flow_graph(uo)
+ # thread.start_new_thread(display_stats, (g.gs,))
+ # g.run()
+ g.start()
+ time.sleep(10.0)
+ g.stop()
+ g.gs.stats()
+
+if __name__ == '__main__':
+ main()
+
diff --git a/gssm/src/python/gssm_usrp.py b/gssm/src/python/gssm_usrp.py
new file mode 100755
index 0000000..6fe84c5
--- /dev/null
+++ b/gssm/src/python/gssm_usrp.py
@@ -0,0 +1,63 @@
+#!/usr/bin/env python
+
+# $Id: gssm_usrp.py,v 1.2 2007-07-07 16:31:44 jl Exp $
+
+from gnuradio import gr, usrp, db_dbs_rx, blks
+from gnuradio.blksimpl import gmsk
+from usrpm import usrp_dbid
+import gssm
+import sys
+
+# constant
+gsm_rate = 1625000.0 / 6.0
+
+# script constant
+decim = 112
+gain = 70
+
+# bts channel
+c0 = 875.4e6
+
+# experimental constant
+default_usrp_offset = 12e3
+
+class gssm_flow_graph(gr.flow_graph):
+ def __init__(self, usrp_offset):
+ gr.flow_graph.__init__(self)
+
+ u = usrp.source_c(decim_rate = decim)
+ s = usrp.pick_subdev(u, (usrp_dbid.DBS_RX,))
+ u.set_mux(usrp.determine_rx_mux_value(u, s))
+ subdev = usrp.selected_subdev(u, s)
+
+ if subdev.dbid() != usrp_dbid.DBS_RX:
+ raise Exception('dbs daughterboard not detected!')
+
+ subdev.set_gain(gain)
+
+ sps = u.adc_freq() / u.decim_rate()
+ if sps < 2 * gsm_rate:
+ raise Exception('sample rate too low')
+
+ u.tune(0, subdev, c0 + usrp_offset)
+
+ xcf = 150e3
+ xtw = 50e3
+ xt = gr.firdes.low_pass(1.0, sps, xcf, xtw,
+ gr.firdes.WIN_HAMMING)
+ xf = gr.fir_filter_ccf(1, xt)
+
+ g = gssm.sink()
+
+ self.connect(u, xf, g)
+
+def main():
+ if len(sys.argv) == 2:
+ uo = float(sys.argv[1])
+ else:
+ uo = default_usrp_offset
+ g = gssm_flow_graph(uo)
+ g.run()
+
+if __name__ == '__main__':
+ main()
personal git repositories of Harald Welte. Your mileage may vary