summaryrefslogtreecommitdiff
path: root/gsm-receiver/src
diff options
context:
space:
mode:
authorPiotr Krysik <perper@o2.pl>2009-06-30 23:03:33 +0200
committerPiotr Krysik <perper@o2.pl>2009-06-30 23:03:33 +0200
commit8d2bc49fb9e0c9a5fbd75aa3cad207608e72bf99 (patch)
treea32fdcd2e8e2b16cc725045d049ed94b3f675956 /gsm-receiver/src
parent8f97a59b21fd8d3ecd111ee770932d852e625d52 (diff)
moved gsm-receiver into directory - preparation to move to airprobe
Diffstat (limited to 'gsm-receiver/src')
-rw-r--r--gsm-receiver/src/Makefile.am23
-rw-r--r--gsm-receiver/src/lib/Assert.h67
-rw-r--r--gsm-receiver/src/lib/Makefile.am109
-rw-r--r--gsm-receiver/src/lib/decoder/AUTHORS1
-rw-r--r--gsm-receiver/src/lib/decoder/Makefile.am56
-rw-r--r--gsm-receiver/src/lib/decoder/a5-1-2.h453
-rw-r--r--gsm-receiver/src/lib/decoder/burst_types.h214
-rw-r--r--gsm-receiver/src/lib/decoder/cch.c482
-rw-r--r--gsm-receiver/src/lib/decoder/cch.h56
-rw-r--r--gsm-receiver/src/lib/decoder/fire_crc.c179
-rw-r--r--gsm-receiver/src/lib/decoder/fire_crc.h47
-rw-r--r--gsm-receiver/src/lib/decoder/gsmstack.c206
-rw-r--r--gsm-receiver/src/lib/decoder/gsmstack.h43
-rw-r--r--gsm-receiver/src/lib/decoder/gsmtap.h41
-rw-r--r--gsm-receiver/src/lib/decoder/interleave.c47
-rw-r--r--gsm-receiver/src/lib/decoder/interleave.h19
-rw-r--r--gsm-receiver/src/lib/decoder/openbtsstuff/AUTHORS173
-rw-r--r--gsm-receiver/src/lib/decoder/openbtsstuff/Assert.h49
-rw-r--r--gsm-receiver/src/lib/decoder/openbtsstuff/BitVector.cpp513
-rw-r--r--gsm-receiver/src/lib/decoder/openbtsstuff/BitVector.h427
-rw-r--r--gsm-receiver/src/lib/decoder/openbtsstuff/GSM610Tables.cpp492
-rw-r--r--gsm-receiver/src/lib/decoder/openbtsstuff/GSM610Tables.h37
-rw-r--r--gsm-receiver/src/lib/decoder/openbtsstuff/GSMCommon.cpp315
-rw-r--r--gsm-receiver/src/lib/decoder/openbtsstuff/GSMCommon.h537
-rw-r--r--gsm-receiver/src/lib/decoder/openbtsstuff/GSML1FEC.cpp256
-rw-r--r--gsm-receiver/src/lib/decoder/openbtsstuff/GSML1FEC.h146
-rw-r--r--gsm-receiver/src/lib/decoder/openbtsstuff/GSMTDMA.cpp337
-rw-r--r--gsm-receiver/src/lib/decoder/openbtsstuff/GSMTDMA.h358
-rw-r--r--gsm-receiver/src/lib/decoder/openbtsstuff/Makefile.am49
-rw-r--r--gsm-receiver/src/lib/decoder/openbtsstuff/RxBurst.h69
-rw-r--r--gsm-receiver/src/lib/decoder/openbtsstuff/Threads.cpp106
-rw-r--r--gsm-receiver/src/lib/decoder/openbtsstuff/Threads.h150
-rw-r--r--gsm-receiver/src/lib/decoder/openbtsstuff/Timeval.cpp93
-rw-r--r--gsm-receiver/src/lib/decoder/openbtsstuff/Timeval.h96
-rw-r--r--gsm-receiver/src/lib/decoder/openbtsstuff/Vector.h257
-rw-r--r--gsm-receiver/src/lib/decoder/openbtsstuff/VocoderFrame.h25
-rw-r--r--gsm-receiver/src/lib/decoder/out_pcap.c111
-rw-r--r--gsm-receiver/src/lib/decoder/out_pcap.h9
-rw-r--r--gsm-receiver/src/lib/decoder/sch.c333
-rw-r--r--gsm-receiver/src/lib/decoder/sch.h17
-rw-r--r--gsm-receiver/src/lib/decoder/system.h11
-rw-r--r--gsm-receiver/src/lib/decoder/tun.c125
-rw-r--r--gsm-receiver/src/lib/decoder/tun.h4
-rw-r--r--gsm-receiver/src/lib/gsm.i49
-rw-r--r--gsm-receiver/src/lib/gsm_constants.h150
-rw-r--r--gsm-receiver/src/lib/gsm_receiver_cf.cc853
-rw-r--r--gsm-receiver/src/lib/gsm_receiver_cf.h254
-rw-r--r--gsm-receiver/src/lib/gsm_receiver_config.cc84
-rw-r--r--gsm-receiver/src/lib/gsm_receiver_config.h164
-rw-r--r--gsm-receiver/src/lib/viterbi_detector.cc554
-rw-r--r--gsm-receiver/src/lib/viterbi_detector.h63
-rw-r--r--gsm-receiver/src/python/Makefile.am24
-rwxr-xr-xgsm-receiver/src/python/capture.sh45
-rw-r--r--gsm-receiver/src/python/cfilebin0 -> 640000 bytes
-rwxr-xr-xgsm-receiver/src/python/go.sh12
-rwxr-xr-xgsm-receiver/src/python/gsm_receive.py117
-rwxr-xr-xgsm-receiver/src/python/gsm_receive_usrp.py141
-rwxr-xr-xgsm-receiver/src/python/test.sh16
58 files changed, 9664 insertions, 0 deletions
diff --git a/gsm-receiver/src/Makefile.am b/gsm-receiver/src/Makefile.am
new file mode 100644
index 0000000..79cfa3e
--- /dev/null
+++ b/gsm-receiver/src/Makefile.am
@@ -0,0 +1,23 @@
+#
+# Copyright 2004 Free Software Foundation, Inc.
+#
+# This file is part of GNU Radio
+#
+# GNU Radio is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 3, or (at your option)
+# any later version.
+#
+# GNU Radio is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with GNU Radio; see the file COPYING. If not, write to
+# the Free Software Foundation, Inc., 51 Franklin Street,
+# Boston, MA 02110-1301, USA.
+#
+
+SUBDIRS = lib python
+
diff --git a/gsm-receiver/src/lib/Assert.h b/gsm-receiver/src/lib/Assert.h
new file mode 100644
index 0000000..acfb3f7
--- /dev/null
+++ b/gsm-receiver/src/lib/Assert.h
@@ -0,0 +1,67 @@
+/*
+* Copyright 2008 Free Software Foundation, Inc.
+*
+* This software is distributed under the terms of the GNU Public License.
+* See the COPYING file in the main directory for details.
+
+ This program is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
+
+*/
+
+
+#ifndef ASSERT_H
+#define ASSERT_H
+
+#include "stdio.h"
+#include <iostream>
+
+#define NDEBUG
+
+/**@name Macros for standard messages. */
+//@{
+#define COUT(text) { std::cout << text << std::endl; }
+#define CERR(text) { std::cerr << __FILE__ << ":" << __LINE__ << ": " << text; }
+#ifdef NDEBUG
+#define DCOUT(text) {}
+#define OBJDCOUT(text) {}
+#else
+#define DCOUT(text) { COUT(__FILE__ << ":" << __LINE__ << " " << text); }
+#define OBJDCOUT(text) { DCOUT(this << " " << text); }
+#endif
+//@}
+
+
+/** This is thrown by assert() so that gdb can catch it. */
+
+class Assertion
+{
+
+ public:
+
+ Assertion() {
+ fprintf( stderr,"Try setting a breakpoint @ %s:%u.\n",__FILE__,__LINE__ );
+ return;
+ }
+
+};
+
+#ifdef NDEBUG
+#define assert(EXPR) {};
+#else
+/** This replaces the regular assert() with a C++ exception. */
+#include "stdio.h"
+#define assert(E) { if (!(E)) { fprintf(stderr,"%s:%u failed assertion '%s'\n",__FILE__,__LINE__,#E); throw Assertion(); } }
+#endif
+
+#endif
diff --git a/gsm-receiver/src/lib/Makefile.am b/gsm-receiver/src/lib/Makefile.am
new file mode 100644
index 0000000..925eaca
--- /dev/null
+++ b/gsm-receiver/src/lib/Makefile.am
@@ -0,0 +1,109 @@
+#
+# Copyright 2004,2005,2006,2008 Free Software Foundation, Inc.
+#
+# This file is part of GNU Radio
+#
+# GNU Radio is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 3, or (at your option)
+# any later version.
+#
+# GNU Radio is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with GNU Radio; see the file COPYING. If not, write to
+# the Free Software Foundation, Inc., 51 Franklin Street,
+# Boston, MA 02110-1301, USA.
+#
+
+include $(top_srcdir)/Makefile.common
+
+SUBDIRS = decoder
+# Install this stuff so that it ends up as the gnuradio.howto module
+# This usually ends up at:
+# ${prefix}/lib/python${python_version}/site-packages/gnuradio
+
+ourpythondir = $(grpythondir)
+ourlibdir = $(grpyexecdir)
+
+AM_CPPFLAGS = $(STD_DEFINES_AND_INCLUDES) $(PYTHON_CPPFLAGS) $(WITH_INCLUDES)
+# -I$(OPEN_BTS_INCLUDES)
+
+SWIGPYTHONARGS = $(SWIGPYTHONFLAGS) $(SWIGGRFLAGS) $(WITH_SWIG_INCLUDES) \
+ $(WITH_INCLUDES)
+
+ALL_IFILES = \
+ $(LOCAL_IFILES) \
+ $(NON_LOCAL_IFILES)
+
+NON_LOCAL_IFILES = \
+ $(GNURADIO_CORE_INCLUDEDIR)/swig/gnuradio.i
+
+
+LOCAL_IFILES = \
+ $(top_srcdir)/src/lib/gsm.i
+
+# These files are built by SWIG. The first is the C++ glue.
+# The second is the python wrapper that loads the _howto shared library
+# and knows how to call our extensions.
+
+BUILT_SOURCES = \
+ gsm.cc \
+ gsm.py
+
+# This gets howto.py installed in the right place
+ourpython_PYTHON = \
+ gsm.py
+
+ourlib_LTLIBRARIES = _gsm.la
+
+lib_LTLIBRARIES = libgsmdemod.la
+
+# These are the source files that go into the shared library
+_gsm_la_SOURCES = \
+ gsm.cc
+
+libgsmdemod_la_SOURCES = \
+ gsm_receiver_cf.cc \
+ gsm_receiver_config.cc \
+ viterbi_detector.cc
+
+# magic flags
+_gsm_la_LDFLAGS = $(NO_UNDEFINED) -module -avoid-version
+
+# link the library against some comon swig runtime code and the
+# c++ standard library
+_gsm_la_LIBADD = \
+ $(PYTHON_LDFLAGS) \
+ libgsmdemod.la \
+ -lstdc++ \
+ $(DECODER_LA)
+
+#libgsmdemod_la_LIBADD =
+
+gsm.cc gsm.py: $(LOCAL_IFILES) $(ALL_IFILES)
+ $(SWIG) $(SWIGPYTHONARGS) -module gsm -o gsm.cc $(LOCAL_IFILES)
+
+# These headers get installed in ${prefix}/include/gnuradio
+grinclude_HEADERS = \
+ gsm_receiver_cf.h \
+ gsm_receiver_config.h
+
+noinst_HEADERS = \
+ gsm_constants.h \
+ viterbi_detector.h
+
+# These swig headers get installed in ${prefix}/include/gnuradio/swig
+swiginclude_HEADERS = \
+ $(LOCAL_IFILES)
+
+
+MOSTLYCLEANFILES = $(BUILT_SOURCES) *.pyc
+
+# Don't distribute output of swig
+dist-hook:
+ @for file in $(BUILT_SOURCES); do echo $(RM) $(distdir)/$$file; done
+ @for file in $(BUILT_SOURCES); do $(RM) $(distdir)/$$file; done
diff --git a/gsm-receiver/src/lib/decoder/AUTHORS b/gsm-receiver/src/lib/decoder/AUTHORS
new file mode 100644
index 0000000..dcc5998
--- /dev/null
+++ b/gsm-receiver/src/lib/decoder/AUTHORS
@@ -0,0 +1 @@
+Harald Welte <laforge@gnumonks.org>
diff --git a/gsm-receiver/src/lib/decoder/Makefile.am b/gsm-receiver/src/lib/decoder/Makefile.am
new file mode 100644
index 0000000..1726d68
--- /dev/null
+++ b/gsm-receiver/src/lib/decoder/Makefile.am
@@ -0,0 +1,56 @@
+#
+# Copyright 2001,2002,2004,2005,2006,2007,2008 Free Software Foundation, Inc.
+#
+# This file is part of GNU Radio
+#
+# GNU Radio is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 3, or (at your option)
+# any later version.
+#
+# GNU Radio is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with GNU Radio; see the file COPYING. If not, write to
+# the Free Software Foundation, Inc., 51 Franklin Street,
+# Boston, MA 02110-1301, USA.
+#
+
+include $(top_srcdir)/Makefile.common
+
+AM_CPPFLAGS = $(STD_DEFINES_AND_INCLUDES)
+
+SUBDIRS = openbtsstuff
+
+noinst_LTLIBRARIES = libdecoder.la
+
+libdecoder_la_LIBADD = \
+ openbtsstuff/libopenbtsdecoder.la
+
+libdecoder_la_SOURCES = \
+ sch.c \
+ cch.c \
+ fire_crc.c \
+ gsmstack.c \
+ interleave.c \
+ out_pcap.c \
+ tun.c
+# tch.c \
+# conv.c
+
+noinst_HEADERS = \
+ sch.h \
+ cch.h \
+ fire_crc.h \
+ gsmstack.h \
+ interleave.h \
+ out_pcap.h \
+ tun.h \
+ system.h \
+ gsmtap.h \
+ a5-1-2.h
+# tch.h \
+# conv.h
diff --git a/gsm-receiver/src/lib/decoder/a5-1-2.h b/gsm-receiver/src/lib/decoder/a5-1-2.h
new file mode 100644
index 0000000..de764ee
--- /dev/null
+++ b/gsm-receiver/src/lib/decoder/a5-1-2.h
@@ -0,0 +1,453 @@
+/*
+ * A pedagogical implementation of the GSM A5/1 and A5/2 "voice privacy"
+ * encryption algorithms.
+ *
+ * Copyright (C) 1998-1999: Marc Briceno, Ian Goldberg, and David Wagner
+ *
+ * The source code below is optimized for instructional value and clarity.
+ * Performance will be terrible, but that's not the point.
+ *
+ * This software may be export-controlled by US law.
+ *
+ * This software is free for commercial and non-commercial use as long as
+ * the following conditions are adhered to.
+ * Copyright remains the authors' and as such any Copyright notices in
+ * the code are not to be removed.
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE LIABLE FOR ANY
+ * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE
+ * GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER
+ * IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
+ * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN
+ * IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ * The license and distribution terms for any publicly available version
+ * or derivative of this code cannot be changed. i.e. this code cannot
+ * simply be copied and put under another distribution license
+ * [including the GNU Public License].
+ *
+ * Background: The Global System for Mobile communications is the most
+ * widely deployed digital cellular telephony system in the world. GSM
+ * makes use of four core cryptographic algorithms, none of which has
+ * been published by the GSM MOU. This failure to subject the
+ * algorithms to public review is all the more puzzling given that over
+ * 215 million GSM subscribers are expected to rely on the claimed
+ * security of the system.
+ *
+ * The four core GSM cryptographic algorithms are:
+ * A3 authentication algorithm
+ * A5/1 "stronger" over-the-air voice-privacy algorithm
+ * A5/2 "weaker" over-the-air voice-privacy algorithm
+ * A8 voice-privacy key generation algorithm
+ *
+ * In April of 1998, our group showed that COMP128, the algorithm used by the
+ * overwhelming majority of GSM providers for both A3 and A8 functionality
+ * is fatally flawed and allows for cloning of GSM mobile phones.
+ *
+ * Furthermore, we demonstrated that all A8 implementations we could locate,
+ * including the few that did not use COMP128 for key generation, had been
+ * deliberately weakened by reducing the keyspace from 64 bits to 54 bits.
+ * The remaining 10 bits are simply set to zero!
+ *
+ * See http://www.scard.org/gsm for additional information.
+ *
+ * [May 1999]
+ * One question so far unanswered is if A5/1, the "stronger" of the two
+ * widely deployed voice-privacy algorithm is at least as strong as the
+ * key. Meaning: "Does A5/1 have a work factor of at least 54 bits"?
+ * Absent a publicly available A5/1 reference implementation, this question
+ * could not be answered. We hope that our reference implementation below,
+ * which has been verified against official A5/1 test vectors, will provide
+ * the cryptographic community with the base on which to construct the
+ * answer to this important question.
+ *
+ * Initial indications about the strength of A5/1 are not encouraging.
+ * A variant of A5, while not A5/1 itself, has been estimated to have a
+ * work factor of well below 54 bits. See http://jya.com/crack-a5.htm for
+ * background information and references.
+ *
+ * With COMP128 broken and A5/1 published below, we will now turn our
+ * attention to A5/2.
+ *
+ * [August 1999]
+ * 19th Annual International Cryptology Conference - Crypto'99
+ * Santa Barbara, California
+ *
+ * A5/2 has been added to the previously published A5/1 source. Our
+ * implementation has been verified against official test vectors.
+ *
+ * This means that our group has now reverse engineered the entire set
+ * of cryptographic algorithms used in the overwhelming majority of GSM
+ * installations, including all the over-the-air "voice privacy" algorithms.
+ *
+ * The "voice privacy" algorithm A5/2 proved especially weak. Which perhaps
+ * should come as no surprise, since even GSM MOU members have admitted that
+ * A5/2 was designed with heavy input by intelligence agencies to ensure
+ * breakability. Just how insecure is A5/2? It can be broken in real time
+ * with a work factor of a mere 16 bits. GSM might just as well use no "voice
+ * privacy" algorithm at all.
+ *
+ * We announced the break of A5/2 at the Crypto'99 Rump Session.
+ * Details will be published in a scientific paper following soon.
+ *
+ *
+ * -- Marc Briceno <marc@scard.org>
+ * Voice: +1 (925) 798-4042
+ *
+ */
+
+
+#include <stdio.h>
+
+
+/* Masks for the shift registers */
+#define R1MASK 0x07FFFF /* 19 bits, numbered 0..18 */
+#define R2MASK 0x3FFFFF /* 22 bits, numbered 0..21 */
+#define R3MASK 0x7FFFFF /* 23 bits, numbered 0..22 */
+#ifdef A5_2
+#define R4MASK 0x01FFFF /* 17 bits, numbered 0..16 */
+#endif /* A5_2 */
+
+
+#ifndef A5_2
+/* Middle bit of each of the three shift registers, for clock control */
+#define R1MID 0x000100 /* bit 8 */
+#define R2MID 0x000400 /* bit 10 */
+#define R3MID 0x000400 /* bit 10 */
+#else /* A5_2 */
+/* A bit of R4 that controls each of the shift registers */
+#define R4TAP1 0x000400 /* bit 10 */
+#define R4TAP2 0x000008 /* bit 3 */
+#define R4TAP3 0x000080 /* bit 7 */
+#endif /* A5_2 */
+
+
+/* Feedback taps, for clocking the shift registers.
+ * These correspond to the primitive polynomials
+ * x^19 + x^5 + x^2 + x + 1, x^22 + x + 1,
+ * x^23 + x^15 + x^2 + x + 1, and x^17 + x^5 + 1. */
+
+
+#define R1TAPS 0x072000 /* bits 18,17,16,13 */
+#define R2TAPS 0x300000 /* bits 21,20 */
+#define R3TAPS 0x700080 /* bits 22,21,20,7 */
+#ifdef A5_2
+#define R4TAPS 0x010800 /* bits 16,11 */
+#endif /* A5_2 */
+
+
+typedef unsigned char byte;
+typedef unsigned long word;
+typedef word bit;
+
+
+/* Calculate the parity of a 32-bit word, i.e. the sum of its bits modulo 2
+*/
+bit parity(word x)
+{
+ x ^= x >> 16;
+ x ^= x >> 8;
+ x ^= x >> 4;
+ x ^= x >> 2;
+ x ^= x >> 1;
+ return x&1;
+}
+
+
+/* Clock one shift register. For A5/2, when the last bit of the frame
+ * is loaded in, one particular bit of each register is forced to '1';
+ * that bit is passed in as the last argument. */
+#ifndef A5_2
+word clockone(word reg, word mask, word taps)
+{
+#else /* A5_2 */
+word clockone(word reg, word mask, word taps, word loaded_bit) {
+#endif /* A5_2 */
+ word t = reg & taps;
+ reg = (reg << 1) & mask;
+ reg |= parity(t);
+#ifdef A5_2
+ reg |= loaded_bit;
+#endif /* A5_2 */
+ return reg;
+}
+
+
+/* The three shift registers. They're in global variables to make the code
+ * easier to understand.
+ * A better implementation would not use global variables. */
+word R1, R2, R3;
+#ifdef A5_2
+word R4;
+#endif /* A5_2 */
+
+
+/* Return 1 iff at least two of the parameter words are non-zero. */
+bit majority(word w1, word w2, word w3) {
+ int sum = (w1 != 0) + (w2 != 0) + (w3 != 0);
+ if (sum >= 2)
+ return 1;
+ else
+ return 0;
+}
+
+
+/* Clock two or three of R1,R2,R3, with clock control
+ * according to their middle bits.
+ * Specifically, we clock Ri whenever Ri's middle bit
+ * agrees with the majority value of the three middle bits. For A5/2,
+ * use particular bits of R4 instead of the middle bits. Also, for A5/2,
+ * always clock R4.
+ * If allP == 1, clock all three of R1,R2,R3, ignoring their middle bits.
+ * This is only used for key setup. If loaded == 1, then this is the last
+ * bit of the frame number, and if we're doing A5/2, we have to set a
+ * particular bit in each of the four registers. */
+void clock(int allP, int loaded) {
+#ifndef A5_2
+ bit maj = majority(R1 & R1MID, R2 & R2MID, R3 & R3MID);
+ if (allP || (((R1&R1MID) != 0) == maj))
+ R1 = clockone(R1, R1MASK, R1TAPS);
+ if (allP || (((R2&R2MID) != 0) == maj))
+ R2 = clockone(R2, R2MASK, R2TAPS);
+ if (allP || (((R3&R3MID) != 0) == maj))
+ R3 = clockone(R3, R3MASK, R3TAPS);
+#else /* A5_2 */
+ bit maj = majority(R4 & R4TAP1, R4 & R4TAP2, R4 & R4TAP3);
+ if (allP || (((R4&R4TAP1) != 0) == maj))
+ R1 = clockone(R1, R1MASK, R1TAPS, loaded << 15);
+ if (allP || (((R4&R4TAP2) != 0) == maj))
+ R2 = clockone(R2, R2MASK, R2TAPS, loaded << 16);
+ if (allP || (((R4&R4TAP3) != 0) == maj))
+ R3 = clockone(R3, R3MASK, R3TAPS, loaded << 18);
+ R4 = clockone(R4, R4MASK, R4TAPS, loaded << 10);
+#endif /* A5_2 */
+}
+
+
+/* Generate an output bit from the current state.
+ * You grab a bit from each register via the output generation taps;
+ * then you XOR the resulting three bits. For A5/2, in addition to
+ * the top bit of each of R1,R2,R3, also XOR in a majority function
+ * of three particular bits of the register (one of them complemented)
+ * to make it non-linear. Also, for A5/2, delay the output by one
+ * clock cycle for some reason. */
+bit getbit() {
+ bit topbits = (((R1 >> 18) ^ (R2 >> 21) ^ (R3 >> 22)) & 0x01);
+#ifndef A5_2
+ return topbits;
+#else /* A5_2 */
+ static bit delaybit = 0;
+ bit nowbit = delaybit;
+ delaybit = (
+ topbits
+ ^ majority(R1 & 0x8000, (~R1) & 0x4000, R1 & 0x1000)
+ ^ majority((~R2) & 0x10000, R2 & 0x2000, R2 & 0x200)
+ ^ majority(R3 & 0x40000, R3 & 0x10000, (~R3) & 0x2000)
+ );
+ return nowbit;
+#endif /* A5_2 */
+}
+
+
+/* Do the A5 key setup. This routine accepts a 64-bit key and
+ * a 22-bit frame number. */
+void keysetup(byte key_reversed[8], word frame) {
+ int i;
+ bit keybit, framebit;
+
+ byte key[8];
+ for(i=0; i<8; i++){
+ key[i] = key_reversed[7-i];
+ }
+ /* Zero out the shift registers. */
+ R1 = R2 = R3 = 0;
+#ifdef A5_2
+ R4 = 0;
+#endif /* A5_2 */
+
+
+ /* Load the key into the shift registers,
+ * LSB of first byte of key array first,
+ * clocking each register once for every
+ * key bit loaded. (The usual clock
+ * control rule is temporarily disabled.) */
+ for (i = 0; i < 64; i++) {
+ clock(1, 0); /* always clock */
+ keybit = (key[i/8] >> (i & 7)) & 1; /* The i-th bit of the key */
+ R1 ^= keybit;
+ R2 ^= keybit;
+ R3 ^= keybit;
+#ifdef A5_2
+ R4 ^= keybit;
+#endif /* A5_2 */
+ }
+
+
+ /* Load the frame number into the shift registers, LSB first,
+ * clocking each register once for every key bit loaded.
+ * (The usual clock control rule is still disabled.)
+ * For A5/2, signal when the last bit is being clocked in. */
+ for (i = 0; i < 22; i++) {
+ clock(1, i == 21); /* always clock */
+ framebit = (frame >> i) & 1; /* The i-th bit of the frame # */
+ R1 ^= framebit;
+ R2 ^= framebit;
+ R3 ^= framebit;
+#ifdef A5_2
+ R4 ^= framebit;
+#endif /* A5_2 */
+ }
+
+
+ /* Run the shift registers for 100 clocks
+ * to mix the keying material and frame number
+ * together with output generation disabled,
+ * so that there is sufficient avalanche.
+ * We re-enable the majority-based clock control
+ * rule from now on. */
+ for (i = 0; i < 100; i++) {
+ clock(0, 0);
+ }
+ /* For A5/2, we have to load the delayed output bit. This does _not_
+ * change the state of the registers. For A5/1, this is a no-op. */
+ getbit();
+
+
+ /* Now the key is properly set up. */
+}
+
+
+/* Generate output. We generate 228 bits of
+ * keystream output. The first 114 bits is for
+ * the A->B frame; the next 114 bits is for the
+ * B->A frame. You allocate a 15-byte buffer
+ * for each direction, and this function fills
+ * it in. */
+void run(byte AtoBkeystream[], byte BtoAkeystream[]) {
+ int i;
+
+
+ /* Zero out the output buffers. */
+ for (i = 0; i <= 113 / 8; i++)
+ AtoBkeystream[i] = BtoAkeystream[i] = 0;
+
+
+ /* Generate 114 bits of keystream for the
+ * A->B direction. Store it, MSB first. */
+ for (i = 0; i < 114; i++) {
+ clock(0, 0);
+ AtoBkeystream[i/8] |= getbit() << (7 - (i & 7));
+ }
+
+
+ /* Generate 114 bits of keystream for the
+ * B->A direction. Store it, MSB first. */
+ for (i = 0; i < 114; i++) {
+ clock(0, 0);
+ BtoAkeystream[i/8] |= getbit() << (7 - (i & 7));
+ }
+}
+
+void runA51(unsigned char AtoBkeystream[]) {
+ int i;
+
+ /* Zero out the output buffers. */
+ for (i = 0; i < 114; i++)
+ AtoBkeystream[i] = 0;
+
+
+ /* Generate 114 bits of keystream for the
+ * A->B direction. Store it, MSB first. */
+ for (i = 0; i < 114; i++) {
+ clock(0, 0);
+ AtoBkeystream[i] = getbit();
+ }
+}
+
+
+/* Test the code by comparing it against
+ * a known-good test vector. */
+void test() {
+#ifndef A5_2
+ byte key[8] = {0x12, 0x23, 0x45, 0x67, 0x89, 0xAB, 0xCD, 0xEF};
+ word frame = 0x134;
+ byte goodAtoB[15] = { 0x53, 0x4E, 0xAA, 0x58, 0x2F, 0xE8, 0x15,
+ 0x1A, 0xB6, 0xE1, 0x85, 0x5A, 0x72, 0x8C, 0x00
+ };
+ byte goodBtoA[15] = { 0x24, 0xFD, 0x35, 0xA3, 0x5D, 0x5F, 0xB6,
+ 0x52, 0x6D, 0x32, 0xF9, 0x06, 0xDF, 0x1A, 0xC0
+ };
+#else /* A5_2 */
+ byte key[8] = {0x00, 0xfc, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff};
+ word frame = 0x21;
+ byte goodAtoB[15] = { 0xf4, 0x51, 0x2c, 0xac, 0x13, 0x59, 0x37,
+ 0x64, 0x46, 0x0b, 0x72, 0x2d, 0xad, 0xd5, 0x00
+ };
+ byte goodBtoA[15] = { 0x48, 0x00, 0xd4, 0x32, 0x8e, 0x16, 0xa1,
+ 0x4d, 0xcd, 0x7b, 0x97, 0x22, 0x26, 0x51, 0x00
+ };
+#endif /* A5_2 */
+ byte AtoB[15], BtoA[15];
+ int i, failed = 0;
+
+
+ keysetup(key, frame);
+ run(AtoB, BtoA);
+
+
+ /* Compare against the test vector. */
+ for (i = 0; i < 15; i++)
+ if (AtoB[i] != goodAtoB[i])
+ failed = 1;
+ for (i = 0; i < 15; i++)
+ if (BtoA[i] != goodBtoA[i])
+ failed = 1;
+
+
+ /* Print some debugging output. */
+ printf("key: 0x");
+ for (i = 0; i < 8; i++)
+ printf("%02X", key[i]);
+ printf("\n");
+ printf("frame number: 0x%06X\n", (unsigned int)frame);
+ printf("known good output:\n");
+ printf(" A->B: 0x");
+ for (i = 0; i < 15; i++)
+ printf("%02X", goodAtoB[i]);
+ printf(" B->A: 0x");
+ for (i = 0; i < 15; i++)
+ printf("%02X", goodBtoA[i]);
+ printf("\n");
+ printf("observed output:\n");
+ printf(" A->B: 0x");
+ for (i = 0; i < 15; i++)
+ printf("%02X", AtoB[i]);
+ printf(" B->A: 0x");
+ for (i = 0; i < 15; i++)
+ printf("%02X", BtoA[i]);
+ printf("\n");
+
+
+ if (!failed) {
+ printf("Self-check succeeded: everything looks ok.\n");
+// exit(0);
+ } else {
+ /* Problems! The test vectors didn't compare*/
+ printf("\nI don't know why this broke; contact the authors.\n");
+ }
+}
+
diff --git a/gsm-receiver/src/lib/decoder/burst_types.h b/gsm-receiver/src/lib/decoder/burst_types.h
new file mode 100644
index 0000000..ee51f9a
--- /dev/null
+++ b/gsm-receiver/src/lib/decoder/burst_types.h
@@ -0,0 +1,214 @@
+// $Id: burst_types.h,v 1.5 2007/03/14 05:44:53 jl Exp $
+
+#pragma once
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#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[] = {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
+#if 0
+static const unsigned char n_tsc[][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
+ }
+};
+
+#endif
+
+/*
+ * 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[] = {
+ 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_compact_fb[] = {
+ 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[] = {
+ 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[] = {
+ 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[] = {
+ 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[] = {
+ 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[] = {
+ 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[] = {
+ 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[] = {
+ 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[] = {
+ 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);
+
+#ifdef __cplusplus
+}
+#endif
diff --git a/gsm-receiver/src/lib/decoder/cch.c b/gsm-receiver/src/lib/decoder/cch.c
new file mode 100644
index 0000000..f1da56d
--- /dev/null
+++ b/gsm-receiver/src/lib/decoder/cch.c
@@ -0,0 +1,482 @@
+//TODO: this file shouldn't be part of the GSM Receiver
+#include "system.h"
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <string.h>
+#include <ctype.h>
+
+//#include <exception>
+//#include <stdexcept>
+#include <math.h>
+//#include "burst_types.h"
+#include "cch.h"
+#include "fire_crc.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. (Output 184 + 40 + 4 = 228 bit)
+ * 2. Convolutional encode. (Output 228 * 2 = 456 bit)
+ * 3. Interleave. (Output 456 bit)
+ * 4. Map on bursts. (4 x 156 bit bursts with each 2x57 bit content data)
+ */
+
+
+/*
+ * 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];
+ }
+}
+ */
+
+
+#if 0
+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];
+ }
+}
+
+#endif
+
+/*
+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;
+}
+
+
+#if 0
+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);
+}
+
+#endif
+
+
+static unsigned char *decode_sacch(GS_CTX *ctx, unsigned char *burst, unsigned int *datalen) {
+
+ int errors, len, data_size;
+ unsigned char conv_data[CONV_SIZE], iBLOCK[BLOCKS][iBLOCK_SIZE],
+ hl, hn, decoded_data[PARITY_OUTPUT_SIZE];
+ FC_CTX fc_ctx;
+
+ data_size = sizeof ctx->msg;
+ if(datalen)
+ *datalen = 0;
+
+ // unmap the bursts
+ decode_burstmap(iBLOCK[0], burst, &hl, &hn); // XXX ignore stealing bits
+ decode_burstmap(iBLOCK[1], burst + 116, &hl, &hn);
+ decode_burstmap(iBLOCK[2], burst + 116 * 2, &hl, &hn);
+ decode_burstmap(iBLOCK[3], burst + 116 * 3, &hl, &hn);
+
+ // remove interleave
+ interleave_decode(&ctx->interleave_ctx, conv_data, (unsigned char *)iBLOCK);
+ //decode_interleave(conv_data, (unsigned char *)iBLOCK);
+
+ // Viterbi decode
+ errors = conv_decode(decoded_data, conv_data);
+ //DEBUGF("conv_decode: %d\n", errors);
+ if (errors)
+ return NULL;
+
+ // check parity
+ // If parity check error detected try to fix it.
+ if (parity_check(decoded_data))
+ {
+ FC_init(&fc_ctx, 40, 184);
+ unsigned char crc_result[224];
+ if (FC_check_crc(&fc_ctx, decoded_data, crc_result) == 0)
+ {
+ errors = -1;
+ DEBUGF("error: sacch: parity error (%d)\n", errors);
+ return NULL;
+ } else {
+ DEBUGF("Successfully corrected parity bits!\n");
+ memcpy(decoded_data, crc_result, sizeof crc_result);
+ errors = 0;
+ }
+ }
+
+ if((len = compress_bits(ctx->msg, data_size, decoded_data,
+ DATA_BLOCK_SIZE)) < 0) {
+ fprintf(stderr, "error: compress_bits\n");
+ return NULL;
+ }
+ if(len < data_size) {
+ fprintf(stderr, "error: buf too small (%d < %d)\n",
+ sizeof(ctx->msg), len);
+ return NULL;
+ }
+
+ if(datalen)
+ *datalen = (unsigned int)len;
+ return ctx->msg;
+}
+
+
+/*
+ * 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(GS_CTX *ctx, unsigned char *burst, unsigned int *datalen) {
+
+ return decode_sacch(ctx, burst, datalen);
+}
+
+
+#if 0
+unsigned char *decode_cch(GS_CTX *ctx, unsigned char *e, unsigned int *datalen) {
+
+ return decode_sacch(ctx, e, e + eBLOCK_SIZE, e + 2 * eBLOCK_SIZE,
+ e + 3 * eBLOCK_SIZE, datalen);
+}
+#endif
diff --git a/gsm-receiver/src/lib/decoder/cch.h b/gsm-receiver/src/lib/decoder/cch.h
new file mode 100644
index 0000000..a642721
--- /dev/null
+++ b/gsm-receiver/src/lib/decoder/cch.h
@@ -0,0 +1,56 @@
+//TODO: this file shouldn't be part of the GSM Receiver
+#ifndef __GSMSTACK_CCH_H__
+#define __GSMSTACK_CCH_H__ 1
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#include "gsmstack.h"
+
+/*
+ * 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(GS_CTX *ctx, unsigned char *burst, unsigned int *len);
+//unsigned char *decode_cch(GS_CTX *ctx, unsigned char *, unsigned char *, unsigned char *, unsigned char *, unsigned int *len);
+//unsigned char *decode_cch(GS_CTX *ctx, unsigned char *, unsigned int *);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
diff --git a/gsm-receiver/src/lib/decoder/fire_crc.c b/gsm-receiver/src/lib/decoder/fire_crc.c
new file mode 100644
index 0000000..7cdfc0b
--- /dev/null
+++ b/gsm-receiver/src/lib/decoder/fire_crc.c
@@ -0,0 +1,179 @@
+//TODO: this file shouldn't be part of the GSM Receiver
+/* -*- c++ -*- */
+/*
+ * Copyright 2005 Free Software Foundation, Inc.
+ *
+ * This file is part of GNU Radio
+ *
+ * GNU Radio is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2, or (at your option)
+ * any later version.
+ *
+ * GNU Radio is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with GNU Radio; see the file COPYING. If not, write to
+ * the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+#include "fire_crc.h"
+#include <stdio.h>
+#include <string.h>
+
+#define REM(x, y) (x) % (y)
+
+static int FC_syndrome_shift(FC_CTX *ctx, unsigned int bit);
+
+static int
+outit(int *data, int len)
+{
+ int i;
+
+ for (i = 0; i < len; i++)
+ printf("%d ", data[i]);
+ printf("\n");
+}
+
+int
+FC_init(FC_CTX *ctx, unsigned int crc_size, unsigned int data_size)
+{
+ ctx->crc_size = crc_size;
+ ctx->data_size = data_size;
+ ctx->syn_start = 0;
+
+ return 0;
+}
+
+int
+FC_check_crc(FC_CTX *ctx, unsigned char *input_bits, unsigned char *control_data)
+{
+ int j,error_count = 0, error_index = 0, success_flag = 0, syn_index = 0;
+ unsigned int i;
+
+ ctx->syn_start = 0;
+ // reset the syndrome register
+ memset(ctx->syndrome_reg, 0, sizeof ctx->syndrome_reg);
+
+ // shift in the data bits
+ for (i=0; i < ctx->data_size; i++) {
+ error_count = FC_syndrome_shift(ctx, input_bits[i]);
+ control_data[i] = input_bits[i];
+ }
+
+ // shift in the crc bits
+ for (i=0; i < ctx->crc_size; i++) {
+ error_count = FC_syndrome_shift(ctx, 1-input_bits[i+ctx->data_size]);
+ }
+
+ // Find position of error burst
+ if (error_count == 0) {
+ error_index = 0;
+ }
+ else {
+ error_index = 1;
+ error_count = FC_syndrome_shift(ctx, 0);
+ error_index += 1;
+ while (error_index < (ctx->data_size + ctx->crc_size) ) {
+ error_count = FC_syndrome_shift(ctx, 0);
+ error_index += 1;
+ if ( error_count == 0 ) break;
+ }
+ }
+
+ // Test for correctable errors
+ //printf("error_index %d\n",error_index);
+ if (error_index == 224) success_flag = 0;
+ else {
+
+ // correct index depending on the position of the error
+ if (error_index == 0) syn_index = error_index;
+ else syn_index = error_index - 1;
+
+ // error burst lies within data bits
+ if (error_index < 184) {
+ //printf("error < bit 184,%d\n",error_index);
+ j = error_index;
+ while ( j < (error_index+12) ) {
+ if (j < 184) {
+ control_data[j] = control_data[j] ^
+ ctx->syndrome_reg[REM(ctx->syn_start+39-j+syn_index,40)];
+ }
+ else break;
+ j = j + 1;
+ }
+ }
+ else if ( error_index > 212 ) {
+ //printf("error > bit 212,%d\n",error_index);
+ j = 0;
+ while ( j < (error_index - 212) ) {
+ control_data[j] = control_data[j] ^
+ ctx->syndrome_reg[REM(ctx->syn_start+39-j-224+syn_index,40)];
+ j = j + 1;
+ }
+ }
+ // for 183 < error_index < 213 error in parity alone so ignore
+ success_flag = 1;
+ }
+ return success_flag;
+}
+
+static int
+FC_syndrome_shift(FC_CTX *ctx, unsigned int bit)
+{
+ int error_count = 0;
+ unsigned int i;
+
+ if (ctx->syn_start == 0)
+ ctx->syn_start = 39;
+ else ctx->syn_start -= 1;
+
+ int temp_syndrome_reg[sizeof ctx->syndrome_reg];
+
+ memcpy(temp_syndrome_reg, ctx->syndrome_reg, sizeof temp_syndrome_reg);
+
+ temp_syndrome_reg[REM(ctx->syn_start+3,40)] =
+ ctx->syndrome_reg[REM(ctx->syn_start+3,40)] ^
+ ctx->syndrome_reg[ctx->syn_start];
+ temp_syndrome_reg[REM(ctx->syn_start+17,40)] =
+ ctx->syndrome_reg[REM(ctx->syn_start+17,40)] ^
+ ctx->syndrome_reg[ctx->syn_start];
+ temp_syndrome_reg[REM(ctx->syn_start+23,40)] =
+ ctx->syndrome_reg[REM(ctx->syn_start+23,40)] ^
+ ctx->syndrome_reg[ctx->syn_start];
+ temp_syndrome_reg[REM(ctx->syn_start+26,40)] =
+ ctx->syndrome_reg[REM(ctx->syn_start+26,40)] ^
+ ctx->syndrome_reg[ctx->syn_start];
+
+ temp_syndrome_reg[REM(ctx->syn_start+4,40)] =
+ ctx->syndrome_reg[REM(ctx->syn_start+4,40)] ^ bit;
+ temp_syndrome_reg[REM(ctx->syn_start+6,40)] =
+ ctx->syndrome_reg[REM(ctx->syn_start+6,40)] ^ bit;
+ temp_syndrome_reg[REM(ctx->syn_start+10,40)] =
+ ctx->syndrome_reg[REM(ctx->syn_start+10,40)] ^ bit;
+ temp_syndrome_reg[REM(ctx->syn_start+16,40)] =
+ ctx->syndrome_reg[REM(ctx->syn_start+16,40)] ^ bit;
+ temp_syndrome_reg[REM(ctx->syn_start+27,40)] =
+ ctx->syndrome_reg[REM(ctx->syn_start+27,40)] ^ bit;
+ temp_syndrome_reg[REM(ctx->syn_start+29,40)] =
+ ctx->syndrome_reg[REM(ctx->syn_start+29,40)] ^ bit;
+ temp_syndrome_reg[REM(ctx->syn_start+33,40)] =
+ ctx->syndrome_reg[REM(ctx->syn_start+33,40)] ^ bit;
+ temp_syndrome_reg[REM(ctx->syn_start+39,40)] =
+ ctx->syndrome_reg[REM(ctx->syn_start+39,40)] ^ bit;
+
+ temp_syndrome_reg[ctx->syn_start] = ctx->syndrome_reg[ctx->syn_start] ^ bit;
+
+ memcpy(ctx->syndrome_reg, temp_syndrome_reg, sizeof ctx->syndrome_reg);
+
+ for (i = 0; i < 28; i++) {
+ error_count = error_count + ctx->syndrome_reg[REM(ctx->syn_start+i,40)];
+ }
+ return error_count;
+}
+
+
diff --git a/gsm-receiver/src/lib/decoder/fire_crc.h b/gsm-receiver/src/lib/decoder/fire_crc.h
new file mode 100644
index 0000000..aa6319c
--- /dev/null
+++ b/gsm-receiver/src/lib/decoder/fire_crc.h
@@ -0,0 +1,47 @@
+//TODO: this file shouldn't be part of the GSM Receiver
+/* -*- c++ -*- */
+/*
+ * Copyright 2005 Free Software Foundation, Inc.
+ *
+ * This file is part of GNU Radio
+ *
+ * GNU Radio is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2, or (at your option)
+ * any later version.
+ *
+ * GNU Radio is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with GNU Radio; see the file COPYING. If not, write to
+ * the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+
+#ifndef INCLUDED_FIRE_CRC_H
+#define INCLUDED_FIRE_CRC_H
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+typedef struct
+{
+ unsigned int crc_size;
+ unsigned int data_size;
+ unsigned int syn_start;
+ int syndrome_reg[40];
+} FC_CTX;
+
+int FC_init(FC_CTX *ctx, unsigned int crc_size, unsigned int data_size);
+int FC_check_crc(FC_CTX *ctx, unsigned char *input_bits, unsigned char *control_data);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
diff --git a/gsm-receiver/src/lib/decoder/gsmstack.c b/gsm-receiver/src/lib/decoder/gsmstack.c
new file mode 100644
index 0000000..c3f1921
--- /dev/null
+++ b/gsm-receiver/src/lib/decoder/gsmstack.c
@@ -0,0 +1,206 @@
+/*
+ * Invoke gsmstack() with any kind of burst. Automaticly decode and retrieve
+ * information.
+ */
+#include "system.h"
+#include <stdlib.h>
+#include <stdio.h>
+#include <errno.h>
+#include <string.h>
+#include "gsmstack.h"
+//#include "gsm_constants.h"
+#include "interleave.h"
+//#include "sch.h"
+#include "cch.h"
+
+static const int USEFUL_BITS = 142;
+
+//#include "out_pcap.h"
+enum BURST_TYPE {
+ UNKNOWN,
+ FCCH,
+ PARTIAL_SCH, //successful correlation, but missing data ^
+ SCH,
+ CTS_SCH,
+ COMPACT_SCH,
+ NORMAL,
+ DUMMY,
+ ACCESS
+};
+static void out_gsmdecode(char type, int arfcn, int ts, int fn, char *data, int len);
+
+/* encode a decoded burst (1 bit per byte) into 8-bit-per-byte */
+static void burst_octify(unsigned char *dest,
+ const unsigned char *data, int length)
+{
+ int bitpos = 0;
+
+ while (bitpos < USEFUL_BITS) {
+ unsigned char tbyte;
+ int i;
+
+ tbyte = 0;
+ for (i = 0; (i < 8) && (bitpos < length); i++) {
+ tbyte <<= 1;
+ tbyte |= data[bitpos++];
+ }
+ if (i < 8)
+ tbyte <<= 8 - i;
+ *dest++ = tbyte;
+ }
+}
+
+
+#if 0
+static void
+diff_decode(char *dst, char *src, int len)
+{
+ const char *end = src + len;
+ unsigned char last;
+
+ src += 3;
+ last = 0;
+ memset(dst, 0, 3);
+ dst += 3;
+
+ while (src < end)
+ {
+ *dst = !*src ^ last;
+ last = *dst;
+ src++;
+ dst++;
+ }
+}
+#endif
+
+/*
+ * Initialize a new GSMSTACK context.
+ */
+int
+GS_new(GS_CTX *ctx)
+{
+ memset(ctx, 0, sizeof *ctx);
+ interleave_init(&ctx->interleave_ctx, 456, 114);
+ ctx->fn = -1;
+ ctx->bsic = -1;
+
+ ctx->tun_fd = mktun("gsm", ctx->ether_addr);
+ if (ctx->tun_fd < 0)
+ fprintf(stderr, "cannot open 'gsm' tun device, did you create it?\n");
+
+ ctx->pcap_fd = open_pcap_file("gsm-receiver.pcap");
+ if (ctx->pcap_fd < 0)
+ fprintf(stderr, "cannot open PCAP file: %s\n", strerror(errno));
+
+ ctx->burst_pcap_fd = open_pcap_file("gsm-receiver-burst.pcap");
+ if (ctx->burst_pcap_fd < 0)
+ fprintf(stderr, "cannot open burst PCAP file: %s\n", strerror(errno));
+
+ return 0;
+}
+
+#define BURST_BYTES ((USEFUL_BITS/8)+1)
+/*
+ * 142 bit
+ */
+int
+GS_process(GS_CTX *ctx, int ts, int type, const unsigned char *src, int fn)
+{
+// int fn;
+ int bsic;
+ int ret;
+ unsigned char *data;
+ int len;
+ struct gs_ts_ctx *ts_ctx = &ctx->ts_ctx[ts];
+ unsigned char octified[BURST_BYTES];
+
+ memset(ctx->msg, 0, sizeof(ctx->msg));
+
+ /* write burst to burst PCAP file */
+ burst_octify(octified, src, USEFUL_BITS);
+ write_pcap_packet(ctx->burst_pcap_fd, 0 /* arfcn */, ts, ctx->fn,
+ 1, type, octified, BURST_BYTES);
+
+#if 0
+ if (ts != 0) {
+ /* non-0 timeslots should end up in PCAP */
+ data = decode_cch(ctx, ctx->burst, &len);
+ if (data == NULL)
+ return -1;
+// write_pcap_packet(ctx->pcap_fd, 0 /* arfcn */, ts, ctx->fn, data, len);
+ return;
+ }
+#endif
+
+/* if (ts == 0) {
+ if (type == SCH) {
+// ret = decode_sch(src, &fn, &bsic);
+ if (ret != 0)
+ return 0;
+ if ((ctx->bsic > 0) && (bsic != ctx->bsic))
+ fprintf(stderr, "WARN: BSIC changed.\n");
+ //DEBUGF("FN %d, BSIC %d\n", fn, bsic);
+ ctx->fn = fn;
+ ctx->bsic = bsic;
+ /* Reset message concatenator */
+// ts_ctx->burst_count = 0;
+// return 0;
+// }
+
+ /* If we did not get Frame Number yet then return */
+// if (ctx->fn < 0)
+// return 0;
+
+// ctx->fn++;
+// }
+ ctx->fn = fn;
+ if (type == NORMAL) {
+ /* Interested in these frame numbers (cch)
+ * 2-5, 12-15, 22-25, 23-35, 42-45
+ * 6-9, 16-19, 26-29, 36-39, 46-49
+ */
+ /* Copy content data into new array */
+ //DEBUGF("burst count %d\n", ctx->burst_count);
+ memcpy(ts_ctx->burst + (116 * ts_ctx->burst_count), src, 58);
+ memcpy(ts_ctx->burst + (116 * ts_ctx->burst_count) + 58, src + 58 + 26, 58);
+ ts_ctx->burst_count++;
+ /* Return if not enough bursts for a full gsm message */
+ if (ts_ctx->burst_count < 4)
+ return 0;
+
+ ts_ctx->burst_count = 0;
+ data = decode_cch(ctx, ts_ctx->burst, &len);
+ if (data == NULL) {
+ DEBUGF("cannot decode fnr=0x%08x ts=%d\n", ctx->fn, ts);
+ return -1;
+ }
+ //DEBUGF("OK TS %d, len %d\n", ts, len);
+
+ out_gsmdecode(0, 0, ts, ctx->fn - 4, data, len);
+ write_interface(ctx->tun_fd, data+1, len-1, ctx->ether_addr);
+ write_pcap_packet(ctx->pcap_fd, 0 /* arfcn */, ts, ctx->fn,
+ 0, NORMAL, data, len);
+#if 0
+ if (ctx->fn % 51 != 0) && ( (((ctx->fn % 51 + 5) % 10 == 0) || (((ctx->fn % 51) + 1) % 10 ==0) ) )
+ ready = 1;
+#endif
+
+ return 0;
+ }
+}
+
+
+/*
+ * Output data so that it can be parsed from gsmdeocde.
+ */
+static void
+out_gsmdecode(char type, int arfcn, int ts, int fn, char *data, int len)
+{
+ char *end = data + len;
+
+ /* FIXME: speed this up by first printing into an array */
+ while (data < end)
+ printf(" %02.2x", (unsigned char)*data++);
+ printf("\n");
+ fflush(stdout);
+}
diff --git a/gsm-receiver/src/lib/decoder/gsmstack.h b/gsm-receiver/src/lib/decoder/gsmstack.h
new file mode 100644
index 0000000..9355785
--- /dev/null
+++ b/gsm-receiver/src/lib/decoder/gsmstack.h
@@ -0,0 +1,43 @@
+//TODO: this file shouldn't be part of the GSM Receiver
+#ifndef __GSMSTACK_H__
+#define __GSMSTACK_H__ 1
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#include <linux/if_ether.h>
+#include "interleave.h"
+
+struct gs_ts_ctx {
+ /* FIXME: later do this per each ts per each arfcn */
+ unsigned char burst[4 * 58 * 2];
+ int burst_count;
+};
+
+typedef struct
+{
+ int flags;
+ int fn;
+ int bsic;
+ char msg[23]; /* last decoded message */
+
+ INTERLEAVE_CTX interleave_ctx;
+
+ struct gs_ts_ctx ts_ctx[8];
+
+ int tun_fd;
+ unsigned char ether_addr[ETH_ALEN];
+
+ int pcap_fd;
+ int burst_pcap_fd;
+} GS_CTX;
+
+int GS_new(GS_CTX *ctx);
+int GS_process(GS_CTX *ctx, int ts, int type, const unsigned char *src, int fn);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
diff --git a/gsm-receiver/src/lib/decoder/gsmtap.h b/gsm-receiver/src/lib/decoder/gsmtap.h
new file mode 100644
index 0000000..1022194
--- /dev/null
+++ b/gsm-receiver/src/lib/decoder/gsmtap.h
@@ -0,0 +1,41 @@
+#ifndef _GSMTAP_H
+#define _GSMTAP_H
+
+/* gsmtap header, pseudo-header in front of the actua GSM payload*/
+
+#include <sys/types.h>
+
+#define GSMTAP_VERSION 0x01
+
+#define GSMTAP_TYPE_UM 0x01
+#define GSMTAP_TYPE_ABIS 0x02
+#define GSMTAP_TYPE_UM_BURST 0x03 /* raw burst bits */
+
+#define GSMTAP_BURST_UNKNOWN 0x00
+#define GSMTAP_BURST_FCCH 0x01
+#define GSMTAP_BURST_PARTIAL_SCH 0x02
+#define GSMTAP_BURST_SCH 0x03
+#define GSMTAP_BURST_CTS_SCH 0x04
+#define GSMTAP_BURST_COMPACT_SCH 0x05
+#define GSMTAP_BURST_NORMAL 0x06
+#define GSMTAP_BURST_DUMMY 0x07
+#define GSMTAP_BURST_ACCESS 0x08
+
+struct gsmtap_hdr {
+ u_int8_t version; /* version, set to 0x01 currently */
+ u_int8_t hdr_len; /* length in number of 32bit words */
+ u_int8_t type; /* see GSMTAP_TYPE_* */
+ u_int8_t timeslot; /* timeslot (0..7 on Um) */
+
+ u_int16_t arfcn; /* ARFCN (frequency) */
+ u_int8_t noise_db; /* noise figure in dB */
+ u_int8_t signal_db; /* signal level in dB */
+
+ u_int32_t frame_number; /* GSM Frame Number (FN) */
+
+ u_int8_t burst_type; /* Type of burst, see above */
+ u_int8_t antenna_nr; /* Antenna Number */
+ u_int16_t res; /* reserved for future use (RFU) */
+
+} __attribute__((packed));
+#endif /* _GSMTAP_H */
diff --git a/gsm-receiver/src/lib/decoder/interleave.c b/gsm-receiver/src/lib/decoder/interleave.c
new file mode 100644
index 0000000..7b45927
--- /dev/null
+++ b/gsm-receiver/src/lib/decoder/interleave.c
@@ -0,0 +1,47 @@
+//TODO: this file shouldn't be part of the GSM Receiver
+#include <stdlib.h>
+#include <stdio.h>
+#include "interleave.h"
+
+int
+interleave_init(INTERLEAVE_CTX *ictx, int size, int block_size)
+{
+ ictx->trans_size = size;
+ ictx->trans = (unsigned short *)malloc(size * sizeof *ictx->trans);
+
+// DEBUGF("size: %d\n", size);
+// DEBUGF("Block size: %d\n", block_size);
+ int j, k, B;
+ for (k = 0; k < size; k++)
+ {
+ B = k % 4;
+ j = 2 * ((49 * k) % 57) + ((k % 8) / 4);
+ ictx->trans[k] = B * block_size + j;
+ /* Mapping: pos1 goes to pos2: pos1 -> pos2 */
+// DEBUGF("%d -> %d\n", ictx->trans[k], k);
+ }
+// exit(0);
+ return 0;
+}
+
+int
+interleave_deinit(INTERLEAVE_CTX *ictx)
+{
+ if (ictx->trans != NULL)
+ {
+ free(ictx->trans);
+ ictx->trans = NULL;
+ }
+
+ return 0;
+}
+
+void
+interleave_decode(INTERLEAVE_CTX *ictx, unsigned char *dst, unsigned char *src)
+{
+
+ int k;
+ for (k = 0; k < ictx->trans_size; k++)
+ dst[k] = src[ictx->trans[k]];
+}
+
diff --git a/gsm-receiver/src/lib/decoder/interleave.h b/gsm-receiver/src/lib/decoder/interleave.h
new file mode 100644
index 0000000..fa1912f
--- /dev/null
+++ b/gsm-receiver/src/lib/decoder/interleave.h
@@ -0,0 +1,19 @@
+//TODO: this file shouldn't be part of the GSM Receiver
+/*
+ * $Id:$
+ */
+
+#ifndef __GSMSP_INTERLEAVE_H__
+#define __GSMSP_INTERLEAVE_H__ 1
+
+typedef struct _interleave_ctx
+{
+ unsigned short *trans;
+ int trans_size;
+} INTERLEAVE_CTX;
+
+int interleave_init(INTERLEAVE_CTX *ictx, int size, int block_size);
+int interleave_deinit(INTERLEAVE_CTX *ictx);
+void interleave_decode(INTERLEAVE_CTX *ictx, unsigned char *dst, unsigned char *src);
+
+#endif
diff --git a/gsm-receiver/src/lib/decoder/openbtsstuff/AUTHORS b/gsm-receiver/src/lib/decoder/openbtsstuff/AUTHORS
new file mode 100644
index 0000000..8075492
--- /dev/null
+++ b/gsm-receiver/src/lib/decoder/openbtsstuff/AUTHORS
@@ -0,0 +1,173 @@
+#
+# Copyright 2008 Free Software Foundation, Inc.
+#
+# This file is part of GNU Radio
+#
+# GNU Radio is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 3, or (at your option)
+# any later version.
+#
+# GNU Radio is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License along
+# with this program; if not, write to the Free Software Foundation, Inc.,
+# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+#
+
+David A. Burgess, dburgess@kestrelsp.com:
+ CommonLibs/Assert.h
+ CommonLibs/BitVector.cpp
+ CommonLibs/BitVectorTest.cpp
+ CommonLibs/Interthread.h
+ CommonLibs/InterthreadTest.cpp
+ CommonLibs/LinkedLists.cpp
+ CommonLibs/LinkedLists.h
+ CommonLibs/Sockets.cpp
+ CommonLibs/Sockets.h
+ CommonLibs/SocketsTest.cpp
+ CommonLibs/Threads.cpp
+ CommonLibs/Threads.h
+ CommonLibs/Timeval.cpp
+ CommonLibs/Timeval.h
+ CommonLibs/TimevalTest.cpp
+ CommonLibs/Vector.h
+ CommonLibs/VectorTest.cpp
+ Control/CallControl.cpp
+ Control/ControlCommon.cpp
+ Control/ControlCommon.h
+ Control/FACCHDispatch.cpp
+ Control/MobilityManagement.cpp
+ Control/PagerTest.cpp
+ Control/RadioResource.cpp
+ Control/SDCCHDispatch.cpp
+ GSM/GSM610Tables.cpp
+ GSM/GSM610Tables.h
+ GSM/GSMCommon.cpp
+ GSM/GSMCommon.h
+ GSM/GSMConfig.h
+ GSM/GSML1FEC.cpp
+ GSM/GSML1FEC.h
+ GSM/GSML2LAPDm.cpp
+ GSM/GSML2LAPDm.h
+ GSM/GSML3CCElements.cpp
+ GSM/GSML3CCElements.h
+ GSM/GSML3CCMessages.cpp
+ GSM/GSML3CCMessages.h
+ GSM/GSML3CommonElements.cpp
+ GSM/GSML3CommonElements.h
+ GSM/GSML3MMElements.cpp
+ GSM/GSML3MMElements.h
+ GSM/GSML3MMMessages.cpp
+ GSM/GSML3MMMessages.h
+ GSM/GSML3Message.cpp
+ GSM/GSML3Message.h
+ GSM/GSML3RRElements.cpp
+ GSM/GSML3RRElements.h
+ GSM/GSML3RRMessages.cpp
+ GSM/GSML3RRMessages.h
+ GSM/GSMLogicalChannel.h
+ GSM/GSMTDMA.cpp
+ GSM/GSMTDMA.h
+ GSM/GSMTransfer.cpp
+ GSM/GSMTransfer.h
+ LICENSEBLOCK
+ SIP/SIPEngine.h
+ SIP/SIPInterface.h
+ TRXManager/TRXManager.cpp
+ Transceiver/Complex.h
+ apps/OpenBTS900.cpp
+ tests/AGCHTest.cpp
+ tests/BeaconTest.cpp
+ tests/CallTest.cpp
+ tests/CallTest2.cpp
+ tests/LAPDmTest.cpp
+ tests/LoopbackTest.cpp
+ tests/RegistrationTest.cpp
+ tests/TRXSimulator.cpp
+
+Harvind S. Samra, hssamra@kestrelsp.com:
+ Control/PagerTest.cpp
+ Control/RadioResource.cpp
+ GSM/GSMConfig.h
+ GSM/GSMTransfer.h
+ LICENSEBLOCK
+ Transceiver/ComplexTest.cpp
+ Transceiver/Transceiver.cpp
+ Transceiver/Transceiver.h
+ Transceiver/USRPDevice.cpp
+ Transceiver/USRPDevice.h
+ Transceiver/USRPping.cpp
+ Transceiver/radioInterface.cpp
+ Transceiver/radioInterface.h
+ Transceiver/rcvLPF_651.h
+ Transceiver/runTransceiver.cpp
+ Transceiver/sendLPF_961.h
+ Transceiver/sigProcLib.cpp
+ Transceiver/sigProcLib.h
+ Transceiver/sigProcLibTest.cpp
+ Transceiver/sweepGenerator.cpp
+ Transceiver/testRadio.cpp
+
+Raffi Sevlian, raffisev@gmail.com:
+ Control/CallControl.cpp
+ Control/ControlCommon.cpp
+ Control/ControlCommon.h
+ Control/FACCHDispatch.cpp
+ Control/MobilityManagement.cpp
+ Control/PagerTest.cpp
+ Control/RadioResource.cpp
+ GSM/GSMCommon.h
+ GSM/GSMConfig.h
+ GSM/GSML1FEC.h
+ GSM/GSML3CCElements.cpp
+ GSM/GSML3CCElements.h
+ GSM/GSML3CCMessages.cpp
+ GSM/GSML3CCMessages.h
+ GSM/GSML3CommonElements.cpp
+ GSM/GSML3CommonElements.h
+ GSM/GSML3MMElements.cpp
+ GSM/GSML3MMElements.h
+ GSM/GSML3MMMessages.cpp
+ GSM/GSML3MMMessages.h
+ GSM/GSML3Message.cpp
+ GSM/GSML3Message.h
+ GSM/GSML3RRElements.cpp
+ GSM/GSML3RRElements.h
+ GSM/GSML3RRMessages.cpp
+ GSM/GSML3RRMessages.h
+ GSM/GSMLogicalChannel.h
+ GSM/GSMSAPMux.cpp
+ GSM/GSMSAPMux.h
+ GSM/GSMTransfer.h
+ LICENSEBLOCK
+ SIP/SIPEngine.cpp
+ SIP/SIPInterface.cpp
+ SIP/SIPInterface.h
+ SIP/SIPMessage.cpp
+ SIP/SIPMessage.h
+ SIP/SIPUtility.cpp
+ SIP/SIPUtility.h
+ SMS/CMMessage.cpp
+ SMS/CMMessage.h
+ SMS/CMProcessor.cpp
+ SMS/CMProcessor.h
+ SMS/CMTest.cpp
+ SMS/RLMessage.cpp
+ SMS/RLMessage.h
+ SMS/RLProcessor.cpp
+ SMS/RLProcessor.h
+ SMS/SMSMessages.cpp
+ SMS/SMSMessages.h
+ SMS/SMSProcessors.cpp
+ SMS/SMSProcessors.h
+ SMS/SMSTransfer.cpp
+ SMS/SMSTransfer.h
+ SMS/TLMessage.cpp
+ SMS/TLMessage.h
+ SMS/TLProcessor.cpp
+ SMS/TLProcessor.h
+ TRXManager/TRXManager.h
diff --git a/gsm-receiver/src/lib/decoder/openbtsstuff/Assert.h b/gsm-receiver/src/lib/decoder/openbtsstuff/Assert.h
new file mode 100644
index 0000000..00ad07d
--- /dev/null
+++ b/gsm-receiver/src/lib/decoder/openbtsstuff/Assert.h
@@ -0,0 +1,49 @@
+/*
+* Copyright 2008 Free Software Foundation, Inc.
+*
+* This software is distributed under the terms of the GNU Public License.
+* See the COPYING file in the main directory for details.
+
+ This program is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
+
+*/
+
+
+#ifndef ASSERT_H
+#define ASSERT_H
+
+#include "stdio.h"
+
+/** This is thrown by assert() so that gdb can catch it. */
+class Assertion {
+
+ public:
+
+ Assertion()
+ {
+ fprintf(stderr,"Try setting a breakpoint @ %s:%u.\n",__FILE__,__LINE__);
+ return;
+ }
+
+};
+
+#ifdef NDEBUG
+#define assert(EXPR) {};
+#else
+/** This replaces the regular assert() with a C++ exception. */
+#include "stdio.h"
+#define assert(E) { if (!(E)) { fprintf(stderr,"%s:%u failed assertion '%s'\n",__FILE__,__LINE__,#E); throw Assertion(); } }
+#endif
+
+#endif
diff --git a/gsm-receiver/src/lib/decoder/openbtsstuff/BitVector.cpp b/gsm-receiver/src/lib/decoder/openbtsstuff/BitVector.cpp
new file mode 100644
index 0000000..89d8d19
--- /dev/null
+++ b/gsm-receiver/src/lib/decoder/openbtsstuff/BitVector.cpp
@@ -0,0 +1,513 @@
+/*
+* Copyright 2008 Free Software Foundation, Inc.
+*
+* This software is distributed under the terms of the GNU Public License.
+* See the COPYING file in the main directory for details.
+
+ This program is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
+
+*/
+
+
+
+
+#include "BitVector.h"
+#include <iostream>
+
+using namespace std;
+
+
+/**
+ Apply a Galois polymonial to a binary seqeunce.
+ @param val The input sequence.
+ @param poly The polynomial.
+ @param order The order of the polynomial.
+ @return Single-bit result.
+*/
+unsigned applyPoly(uint64_t val, uint64_t poly, unsigned order)
+{
+ uint64_t prod = val & poly;
+ unsigned sum = prod;
+ for (unsigned i=1; i<order; i++) sum ^= prod>>i;
+ return sum & 0x01;
+}
+
+
+
+
+
+
+BitVector::BitVector(const char *valString)
+ :Vector<char>(strlen(valString))
+{
+ uint32_t accum = 0;
+ for (size_t i=0; i<size(); i++) {
+ accum <<= 1;
+ if (valString[i]=='1') accum |= 0x01;
+ mStart[i] = accum;
+ }
+}
+
+
+
+
+
+uint64_t BitVector::peekField(size_t readIndex, unsigned length) const
+{
+ uint64_t accum = 0;
+ char *dp = mStart + readIndex;
+ assert(dp+length <= mEnd);
+ for (unsigned i=0; i<length; i++) {
+ accum = (accum<<1) | ((*dp++) & 0x01);
+ }
+ return accum;
+}
+
+
+uint64_t BitVector::readField(size_t& readIndex, unsigned length) const
+{
+ const uint64_t retVal = peekField(readIndex,length);
+ readIndex += length;
+ return retVal;
+}
+
+
+void BitVector::fillField(size_t writeIndex, uint64_t value, unsigned length)
+{
+ char *dpBase = mStart + writeIndex;
+ char *dp = dpBase + length - 1;
+ assert(dp < mEnd);
+ while (dp>=dpBase) {
+ *dp-- = value & 0x01;
+ value >>= 1;
+ }
+}
+
+void BitVector::writeField(size_t& writeIndex, uint64_t value, unsigned length)
+{
+ fillField(writeIndex,value,length);
+ writeIndex += length;
+}
+
+
+void BitVector::invert()
+{
+ for (size_t i=0; i<size(); i++) {
+ mStart[i] = ~mStart[i];
+ }
+}
+
+
+
+
+void BitVector::reverse8()
+{
+ assert(size()>=8);
+
+ char tmp0 = mStart[0];
+ mStart[0] = mStart[7];
+ mStart[7] = tmp0;
+
+ char tmp1 = mStart[1];
+ mStart[1] = mStart[6];
+ mStart[6] = tmp1;
+
+ char tmp2 = mStart[2];
+ mStart[2] = mStart[5];
+ mStart[5] = tmp2;
+
+ char tmp3 = mStart[3];
+ mStart[3] = mStart[4];
+ mStart[4] = tmp3;
+}
+
+
+
+void BitVector::LSB8MSB()
+{
+ size_t size8 = 8*(size()/8);
+ size_t iTop = size8 - 8;
+ for (size_t i=0; i<=iTop; i+=8) segment(i,8).reverse8();
+}
+
+
+
+uint64_t BitVector::syndrome(Generator& gen) const
+{
+ gen.clear();
+ const char *dp = mStart;
+ while (dp<mEnd) gen.syndromeShift(*dp++);
+ return gen.state();
+}
+
+
+uint64_t BitVector::parity(Generator& gen) const
+{
+ gen.clear();
+ const char *dp = mStart;
+ while (dp<mEnd) gen.encoderShift(*dp++);
+ return gen.state();
+}
+
+
+void BitVector::encode(const ViterbiR2O4& coder, BitVector& target)
+{
+ size_t sz = size();
+ assert(sz*coder.iRate() == target.size());
+
+ // Build a "history" array where each element contains the full history.
+ uint32_t history[sz];
+ uint32_t accum = 0;
+ for (size_t i=0; i<sz; i++) {
+ accum = (accum<<1) | bit(i);
+ history[i] = accum;
+ }
+
+ // Look up histories in the pre-generated state table.
+ char *op = target.begin();
+ for (size_t i=0; i<sz; i++) {
+ unsigned index = coder.cMask() & history[i];
+ for (unsigned g=0; g<coder.iRate(); g++) {
+ *op++ = coder.stateTable(g,index);
+ }
+ }
+}
+
+
+
+unsigned BitVector::sum() const
+{
+ unsigned sum = 0;
+ for (size_t i=0; i<size(); i++) sum += mStart[i] & 0x01;
+ return sum;
+}
+
+
+
+
+void BitVector::map(const unsigned *map, size_t mapSize, BitVector& dest) const
+{
+ for (unsigned i=0; i<mapSize; i++) {
+ dest.mStart[i] = mStart[map[i]];
+ }
+}
+
+
+
+
+void BitVector::unmap(const unsigned *map, size_t mapSize, BitVector& dest) const
+{
+ for (unsigned i=0; i<mapSize; i++) {
+ dest.mStart[map[i]] = mStart[i];
+ }
+}
+
+
+
+
+
+
+
+
+
+
+ostream& operator<<(ostream& os, const BitVector& hv)
+{
+ for (size_t i=0; i<hv.size(); i++) {
+ if (hv.bit(i)) os << '1';
+ else os << '0';
+ }
+ return os;
+}
+
+
+
+
+ViterbiR2O4::ViterbiR2O4()
+{
+ assert(mDeferral < 32);
+ mCoeffs[0] = 0x019;
+ mCoeffs[1] = 0x01b;
+ computeStateTables(0);
+ computeStateTables(1);
+ computeGeneratorTable();
+}
+
+
+
+
+void ViterbiR2O4::initializeStates()
+{
+ for (unsigned i=0; i<mIStates; i++) clear(mSurvivors[i]);
+ for (unsigned i=0; i<mNumCands; i++) clear(mCandidates[i]);
+}
+
+
+
+void ViterbiR2O4::computeStateTables(unsigned g)
+{
+ assert(g<mIRate);
+ for (unsigned state=0; state<mIStates; state++) {
+ // 0 input
+ uint32_t inputVal = state<<1;
+ mStateTable[g][inputVal] = applyPoly(inputVal, mCoeffs[g], mOrder+1);
+ // 1 input
+ inputVal |= 1;
+ mStateTable[g][inputVal] = applyPoly(inputVal, mCoeffs[g], mOrder+1);
+ }
+}
+
+void ViterbiR2O4::computeGeneratorTable()
+{
+ for (unsigned index=0; index<mIStates*2; index++) {
+ mGeneratorTable[index] = (mStateTable[0][index]<<1) | mStateTable[1][index];
+ }
+}
+
+
+
+
+
+
+void ViterbiR2O4::branchCandidates()
+{
+ // Branch to generate new input states.
+ const vCand *sp = mSurvivors;
+ for (unsigned i=0; i<mNumCands; i+=2) {
+ // extend and suffix
+ const uint32_t iState0 = (sp->iState) << 1; // input state for 0
+ const uint32_t iState1 = iState0 | 0x01; // input state for 1
+ const uint32_t oStateShifted = (sp->oState) << mIRate; // shifted output
+ const float cost = sp->cost;
+ sp++;
+ // 0 input extension
+ mCandidates[i].cost = cost;
+ mCandidates[i].oState = oStateShifted | mGeneratorTable[iState0 & mCMask];
+ mCandidates[i].iState = iState0;
+ // 1 input extension
+ mCandidates[i+1].cost = cost;
+ mCandidates[i+1].oState = oStateShifted | mGeneratorTable[iState1 & mCMask];
+ mCandidates[i+1].iState = iState1;
+ }
+}
+
+
+void ViterbiR2O4::getSoftCostMetrics(const uint32_t inSample, const float *matchCost, const float *mismatchCost)
+{
+ const float *cTab[2] = {matchCost,mismatchCost};
+ for (unsigned i=0; i<mNumCands; i++) {
+ vCand& thisCand = mCandidates[i];
+ // We examine input bits 2 at a time for a rate 1/2 coder.
+ const unsigned mismatched = inSample ^ (thisCand.oState);
+ thisCand.cost += cTab[mismatched&0x01][1] + cTab[(mismatched>>1)&0x01][0];
+ }
+}
+
+
+void ViterbiR2O4::pruneCandidates()
+{
+ const vCand* c1 = mCandidates; // 0-prefix
+ const vCand* c2 = mCandidates + mIStates; // 1-prefix
+ for (unsigned i=0; i<mIStates; i++) {
+ if (c1[i].cost < c2[i].cost) mSurvivors[i] = c1[i];
+ else mSurvivors[i] = c2[i];
+ }
+}
+
+
+const ViterbiR2O4::vCand& ViterbiR2O4::minCost() const
+{
+ int minIndex = 0;
+ float minCost = mSurvivors[0].cost;
+ for (unsigned i=1; i<mIStates; i++) {
+ const float thisCost = mSurvivors[i].cost;
+ if (thisCost>=minCost) continue;
+ minCost = thisCost;
+ minIndex=i;
+ }
+ return mSurvivors[minIndex];
+}
+
+
+const ViterbiR2O4::vCand& ViterbiR2O4::step(uint32_t inSample, const float *probs, const float *iprobs)
+{
+ branchCandidates();
+ getSoftCostMetrics(inSample,probs,iprobs);
+ pruneCandidates();
+ return minCost();
+}
+
+
+uint64_t Parity::syndrome(const BitVector& receivedCodeword)
+{
+ return receivedCodeword.syndrome(*this);
+}
+
+
+void Parity::writeParityWord(const BitVector& data, BitVector& parityTarget, bool invert)
+{
+ uint64_t pWord = data.parity(*this);
+ if (invert) pWord = ~pWord;
+ parityTarget.fillField(0,pWord,size());
+}
+
+
+
+
+
+
+
+
+
+SoftVector::SoftVector(const BitVector& source)
+{
+ resize(source.size());
+ for (size_t i=0; i<size(); i++) {
+ if (source.bit(i)) mStart[i]=1.0F;
+ else mStart[i]=0.0F;
+ }
+}
+
+
+BitVector SoftVector::sliced() const
+{
+ size_t sz = size();
+ BitVector newSig(sz);
+ for (size_t i=0; i<sz; i++) {
+ if (mStart[i]>0.5F) newSig[i]=1;
+ else newSig[i] = 0;
+ }
+ return newSig;
+}
+
+
+
+void SoftVector::decode(ViterbiR2O4 &decoder, BitVector& target) const
+{
+ const size_t sz = size();
+ const unsigned deferral = decoder.deferral();
+ const size_t ctsz = sz + deferral;
+ assert(sz <= decoder.iRate()*target.size());
+
+ // Build a "history" array where each element contains the full history.
+ uint32_t history[ctsz];
+ {
+ BitVector bits = sliced();
+ uint32_t accum = 0;
+ for (size_t i=0; i<sz; i++) {
+ accum = (accum<<1) | bits.bit(i);
+ history[i] = accum;
+ }
+ // Repeat last bit at the end.
+ for (size_t i=sz; i<ctsz; i++) {
+ accum = (accum<<1) | (accum & 0x01);
+ history[i] = accum;
+ }
+ }
+
+ // Precompute metric tables.
+ float matchCostTable[ctsz];
+ float mismatchCostTable[ctsz];
+ {
+ const float *dp = mStart;
+ for (size_t i=0; i<sz; i++) {
+ // pVal is the probability that a bit is correct.
+ // ipVal is the probability that a bit is correct.
+ float pVal = dp[i];
+ if (pVal>0.5F) pVal = 1.0F-pVal;
+ float ipVal = 1.0F-pVal;
+ // This is a cheap approximation to an ideal cost function.
+ if (pVal<0.01F) pVal = 0.01;
+ if (ipVal<0.01F) ipVal = 0.01;
+ matchCostTable[i] = 0.25F/ipVal;
+ mismatchCostTable[i] = 0.25F/pVal;
+ }
+
+ // pad end of table with unknowns
+ for (size_t i=sz; i<ctsz; i++) {
+ matchCostTable[i] = 0.5F;
+ mismatchCostTable[i] = 0.5F;
+ }
+ }
+
+ {
+ decoder.initializeStates();
+ // Each sample of history[] carries its history.
+ // So we only have to process every iRate-th sample.
+ const unsigned step = decoder.iRate();
+ // input pointer
+ const uint32_t *ip = history + step - 1;
+ // output pointers
+ char *op = target.begin();
+ const char *const opt = target.end();
+ // table pointers
+ const float* match = matchCostTable;
+ const float* mismatch = mismatchCostTable;
+ size_t oCount = 0;
+ while (op<opt) {
+ // Viterbi algorithm
+ const ViterbiR2O4::vCand &minCost = decoder.step(*ip, match, mismatch);
+ ip += step;
+ match += step;
+ mismatch += step;
+ // output
+ if (oCount>=deferral) *op++ = (minCost.iState >> deferral);
+ oCount++;
+ }
+ }
+}
+
+
+
+
+ostream& operator<<(ostream& os, const SoftVector& sv)
+{
+ for (size_t i=0; i<sv.size(); i++) {
+ if (sv[i]<0.25) os << "0";
+ else if (sv[i]>0.75) os << "1";
+ else os << "-";
+ }
+ return os;
+}
+
+
+
+void BitVector::pack(unsigned char* targ) const
+{
+ // Assumes MSB-first packing.
+ unsigned bytes = size()/8;
+ for (unsigned i=0; i<bytes; i++) {
+ targ[i] = peekField(i*8,8);
+ }
+ unsigned whole = bytes*8;
+ unsigned rem = size() - whole;
+ if (rem==0) return;
+ targ[bytes] = peekField(whole,rem) << (8-rem);
+}
+
+
+void BitVector::unpack(const unsigned char* src)
+{
+ // Assumes MSB-first packing.
+ unsigned bytes = size()/8;
+ for (unsigned i=0; i<bytes; i++) {
+ fillField(i*8,src[i],8);
+ }
+ unsigned whole = bytes*8;
+ unsigned rem = size() - whole;
+ if (rem==0) return;
+ fillField(whole,src[bytes],rem);
+}
+
+// vim: ts=4 sw=4
diff --git a/gsm-receiver/src/lib/decoder/openbtsstuff/BitVector.h b/gsm-receiver/src/lib/decoder/openbtsstuff/BitVector.h
new file mode 100644
index 0000000..3019c2c
--- /dev/null
+++ b/gsm-receiver/src/lib/decoder/openbtsstuff/BitVector.h
@@ -0,0 +1,427 @@
+/*
+* Copyright 2008 Free Software Foundation, Inc.
+*
+* This software is distributed under the terms of the GNU Public License.
+* See the COPYING file in the main directory for details.
+
+ This program is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
+
+*/
+
+
+#ifndef FECVECTORS_H
+#define FECVECTORS_H
+
+#include "Vector.h"
+#include <stdint.h>
+
+
+class BitVector;
+class SoftVector;
+
+
+
+/** Shift-register (LFSR) generator. */
+class Generator {
+
+ private:
+
+ uint64_t mCoeff; ///< polynomial coefficients. LSB is zero exponent.
+ uint64_t mState; ///< shift register state. LSB is most recent.
+ uint64_t mMask; ///< mask for reading state
+ unsigned mLen; ///< number of bits used in shift register
+ unsigned mLen_1; ///< mLen - 1
+
+ public:
+
+ Generator(uint64_t wCoeff, unsigned wLen)
+ :mCoeff(wCoeff),mState(0),
+ mMask((1ULL<<wLen)-1),
+ mLen(wLen),mLen_1(wLen-1)
+ { assert(wLen<64); }
+
+ void clear() { mState=0; }
+
+ /**@name Accessors */
+ //@{
+ uint64_t state() const { return mState & mMask; }
+ unsigned size() const { return mLen; }
+ //@}
+
+ /**
+ Calculate one bit of a syndrome.
+ This is in the .h for inlining.
+ */
+ void syndromeShift(unsigned inBit)
+ {
+ const unsigned fb = (mState>>(mLen_1)) & 0x01;
+ mState = (mState<<1) ^ (inBit & 0x01);
+ if (fb) mState ^= mCoeff;
+ }
+
+ /**
+ Update the generator state by one cycle.
+ This is in the .h for inlining.
+ */
+ void encoderShift(unsigned inBit)
+ {
+ const unsigned fb = ((mState>>(mLen_1)) ^ inBit) & 0x01;
+ mState <<= 1;
+ if (fb) mState ^= mCoeff;
+ }
+
+
+};
+
+
+
+
+/** Parity (CRC-type) generator and checker based on a Generator. */
+class Parity : public Generator {
+
+ protected:
+
+ unsigned mCodewordSize;
+
+ public:
+
+ Parity(uint64_t wCoefficients, unsigned wParitySize, unsigned wCodewordSize)
+ :Generator(wCoefficients, wParitySize),
+ mCodewordSize(wCodewordSize)
+ { }
+
+ /** Compute the parity word and write it into the target segment. */
+ void writeParityWord(const BitVector& data, BitVector& parityWordTarget, bool invert=true);
+
+ /** Compute the syndrome of a received sequence. */
+ uint64_t syndrome(const BitVector& receivedCodeword);
+};
+
+
+
+
+/**
+ Class to represent convolutional coders/decoders of rate 1/2, memory length 4.
+ This is the "workhorse" coder for most GSM channels.
+*/
+class ViterbiR2O4 {
+
+ private:
+ /**name Lots of precomputed elements so the compiler can optimize like hell. */
+ //@{
+ /**@name Core values. */
+ //@{
+ static const unsigned mIRate = 2; ///< reciprocal of rate
+ static const unsigned mOrder = 4; ///< memory length of generators
+ //@}
+ /**@name Derived values. */
+ //@{
+ static const unsigned mIStates = 0x01 << mOrder; ///< number of states, number of survivors
+ static const uint32_t mSMask = mIStates-1; ///< survivor mask
+ static const uint32_t mCMask = (mSMask<<1) | 0x01; ///< candidate mask
+ static const uint32_t mOMask = (0x01<<mIRate)-1; ///< ouput mask, all iRate low bits set
+ static const unsigned mNumCands = mIStates*2; ///< number of candidates to generate during branching
+ static const unsigned mDeferral = 6*mOrder; ///< deferral to be used
+ //@}
+ //@}
+
+ /** Precomputed tables. */
+ //@{
+ uint32_t mCoeffs[mIRate]; ///< polynomial for each generator
+ uint32_t mStateTable[mIRate][2*mIStates]; ///< precomputed generator output tables
+ uint32_t mGeneratorTable[2*mIStates]; ///< precomputed coder output table
+ //@}
+
+ public:
+
+ /**
+ A candidate sequence in a Viterbi decoder.
+ The 32-bit state register can support a deferral of 6 with a 4th-order coder.
+ */
+ typedef struct candStruct {
+ uint32_t iState; ///< encoder input associated with this candidate
+ uint32_t oState; ///< encoder output associated with this candidate
+ float cost; ///< cost (metric value), float to support soft inputs
+ } vCand;
+
+ /** Clear a structure. */
+ void clear(vCand& v)
+ {
+ v.iState=0;
+ v.oState=0;
+ v.cost=0;
+ }
+
+
+ private:
+
+ /**@name Survivors and candidates. */
+ //@{
+ vCand mSurvivors[mIStates]; ///< current survivor pool
+ vCand mCandidates[2*mIStates]; ///< current candidate pool
+ //@}
+
+ public:
+
+ unsigned iRate() const { return mIRate; }
+ uint32_t cMask() const { return mCMask; }
+ uint32_t stateTable(unsigned g, unsigned i) const { return mStateTable[g][i]; }
+ unsigned deferral() const { return mDeferral; }
+
+
+ ViterbiR2O4();
+
+ /** Set all cost metrics to zero. */
+ void initializeStates();
+
+ /**
+ Full cycle of the Viterbi algorithm: branch, metrics, prune, select.
+ @return reference to minimum-cost candidate.
+ */
+ const vCand& step(uint32_t inSample, const float *probs, const float *iprobs);
+
+ private:
+
+ /** Branch survivors into new candidates. */
+ void branchCandidates();
+
+ /** Compute cost metrics for soft-inputs. */
+ void getSoftCostMetrics(uint32_t inSample, const float *probs, const float *iprobs);
+
+ /** Select survivors from the candidate set. */
+ void pruneCandidates();
+
+ /** Find the minimum cost survivor. */
+ const vCand& minCost() const;
+
+ /**
+ Precompute the state tables.
+ @param g Generator index 0..((1/rate)-1)
+ */
+ void computeStateTables(unsigned g);
+
+ /**
+ Precompute the generator outputs.
+ mCoeffs must be defined first.
+ */
+ void computeGeneratorTable();
+
+};
+
+
+
+
+class BitVector : public Vector<char> {
+
+
+ public:
+
+ /**@name Constructors. */
+ //@{
+
+ /**@name Casts of Vector constructors. */
+ //@{
+ BitVector(char* wData, char* wStart, char* wEnd)
+ :Vector<char>(wData,wStart,wEnd)
+ { }
+ BitVector(size_t len=0):Vector<char>(len) {}
+ BitVector(const Vector<char>& source):Vector<char>(source) {}
+ BitVector(Vector<char>& source):Vector<char>(source) {}
+ BitVector(const Vector<char>& source1, const Vector<char> source2):Vector<char>(source1,source2) {}
+ //@}
+
+ /** Construct from a string of "0" and "1". */
+ BitVector(const char* valString);
+ //@}
+
+ /** Index a single bit. */
+ bool bit(size_t index) const
+ {
+ // We put this code in .h for fast inlining.
+ const char *dp = mStart+index;
+ assert(dp<mEnd);
+ return (*dp) & 0x01;
+ }
+
+ /**@name Casts and overrides of Vector operators. */
+ //@{
+ BitVector segment(size_t start, size_t span)
+ {
+ char* wStart = mStart + start;
+ char* wEnd = wStart + span;
+ assert(wEnd<=mEnd);
+ return BitVector(NULL,wStart,wEnd);
+ }
+
+ BitVector alias()
+ { return segment(0,size()); }
+
+ const BitVector segment(size_t start, size_t span) const
+ { return (BitVector)(Vector<char>::segment(start,span)); }
+
+ BitVector head(size_t span) { return segment(0,span); }
+ const BitVector head(size_t span) const { return segment(0,span); }
+ BitVector tail(size_t start) { return segment(start,size()-start); }
+ const BitVector tail(size_t start) const { return segment(start,size()-start); }
+ //@}
+
+
+ void zero() { fill(0); }
+
+ /**@name FEC operations. */
+ //@{
+ /** Calculate the syndrome of the vector with the given Generator. */
+ uint64_t syndrome(Generator& gen) const;
+ /** Calculate the parity word for the vector with the given Generator. */
+ uint64_t parity(Generator& gen) const;
+ /** Encode the signal with the GSM rate 1/2 convolutional encoder. */
+ void encode(const ViterbiR2O4& encoder, BitVector& target);
+ //@}
+
+
+ /** Invert 0<->1. */
+ void invert();
+
+ /**@name Byte-wise operations. */
+ //@{
+ /** Reverse an 8-bit vector. */
+ void reverse8();
+ /** Reverse groups of 8 within the vector (byte reversal). */
+ void LSB8MSB();
+ //@}
+
+ /**@name Serialization and deserialization. */
+ //@{
+ uint64_t peekField(size_t readIndex, unsigned length) const;
+ uint64_t readField(size_t& readIndex, unsigned length) const;
+ void fillField(size_t writeIndex, uint64_t value, unsigned length);
+ void writeField(size_t& writeIndex, uint64_t value, unsigned length);
+ //@}
+
+ /** Sum of bits. */
+ unsigned sum() const;
+
+ /** Reorder bits, dest[i] = this[map[i]]. */
+ void map(const unsigned *map, size_t mapSize, BitVector& dest) const;
+
+ /** Reorder bits, dest[map[i]] = this[i]. */
+ void unmap(const unsigned *map, size_t mapSize, BitVector& dest) const;
+
+ /** Pack into a char array. */
+ void pack(unsigned char*) const;
+
+ /** Unopack from a char array. */
+ void unpack(const unsigned char*);
+
+};
+
+
+
+std::ostream& operator<<(std::ostream&, const BitVector&);
+
+
+
+
+
+
+/**
+ The SoftVector class is used to represent a soft-decision signal.
+ Values 0..1 represent probabilities that a bit is "true".
+ */
+class SoftVector: public Vector<float> {
+
+ public:
+
+ /** Build a SoftVector of a given length. */
+ SoftVector(size_t wSize=0):Vector<float>(wSize) {}
+
+ /** Construct a SoftVector from a C string of "0", "1", and "X". */
+ SoftVector(const char* valString);
+
+ /** Construct a SoftVector from a BitVector. */
+ SoftVector(const BitVector& source);
+
+ /**
+ Wrap a SoftVector around a block of floats.
+ The block will be delete[]ed upon desctuction.
+ */
+ SoftVector(float *wData, unsigned length)
+ :Vector<float>(wData,length)
+ {}
+
+ SoftVector(float* wData, float* wStart, float* wEnd)
+ :Vector<float>(wData,wStart,wEnd)
+ { }
+
+ /**
+ Casting from a Vector<float>.
+ Note that this is NOT pass-by-reference.
+ */
+ SoftVector(Vector<float> source)
+ :Vector<float>(source)
+ {}
+
+
+ /**@name Casts and overrides of Vector operators. */
+ //@{
+ SoftVector segment(size_t start, size_t span)
+ {
+ float* wStart = mStart + start;
+ float* wEnd = wStart + span;
+ assert(wEnd<=mEnd);
+ return SoftVector(NULL,wStart,wEnd);
+ }
+
+ SoftVector alias()
+ { return segment(0,size()); }
+
+ const SoftVector segment(size_t start, size_t span) const
+ { return (SoftVector)(Vector<float>::segment(start,span)); }
+
+ SoftVector head(size_t span) { return segment(0,span); }
+ const SoftVector head(size_t span) const { return segment(0,span); }
+ SoftVector tail(size_t start) { return segment(start,size()-start); }
+ const SoftVector tail(size_t start) const { return segment(start,size()-start); }
+ //@}
+
+ /** Decode soft symbols with the GSM rate-1/2 Viterbi decoder. */
+ void decode(ViterbiR2O4 &decoder, BitVector& target) const;
+
+ /** Fill with "unknown" values. */
+ void unknown() { fill(0.5F); }
+
+ /** Return a hard bit value from a given index by slicing. */
+ bool bit(size_t index) const
+ {
+ const float *dp = mStart+index;
+ assert(dp<mEnd);
+ return (*dp)>0.5F;
+ }
+
+ /** Slice the whole signal into bits. */
+ BitVector sliced() const;
+
+};
+
+
+
+std::ostream& operator<<(std::ostream&, const SoftVector&);
+
+
+
+
+
+
+#endif
+// vim: ts=4 sw=4
diff --git a/gsm-receiver/src/lib/decoder/openbtsstuff/GSM610Tables.cpp b/gsm-receiver/src/lib/decoder/openbtsstuff/GSM610Tables.cpp
new file mode 100644
index 0000000..38f643f
--- /dev/null
+++ b/gsm-receiver/src/lib/decoder/openbtsstuff/GSM610Tables.cpp
@@ -0,0 +1,492 @@
+/*
+* Copyright 2008 Free Software Foundation, Inc.
+*
+* This software is distributed under the terms of the GNU Public License.
+* See the COPYING file in the main directory for details.
+
+ This program is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
+
+*/
+
+
+
+#include "GSM610Tables.h"
+
+
+/*
+RFC 3551 RTP A/V Profile July 2003
+
+
+ Octet Bit 0 Bit 1 Bit 2 Bit 3 Bit 4 Bit 5 Bit 6 Bit 7
+ _____________________________________________________________________
+ 0 1 1 0 1 LARc0.0 LARc0.1 LARc0.2 LARc0.3
+ 1 LARc0.4 LARc0.5 LARc1.0 LARc1.1 LARc1.2 LARc1.3 LARc1.4 LARc1.5
+ 2 LARc2.0 LARc2.1 LARc2.2 LARc2.3 LARc2.4 LARc3.0 LARc3.1 LARc3.2
+ 3 LARc3.3 LARc3.4 LARc4.0 LARc4.1 LARc4.2 LARc4.3 LARc5.0 LARc5.1
+ 4 LARc5.2 LARc5.3 LARc6.0 LARc6.1 LARc6.2 LARc7.0 LARc7.1 LARc7.2
+ 5 Nc0.0 Nc0.1 Nc0.2 Nc0.3 Nc0.4 Nc0.5 Nc0.6 bc0.0
+ 6 bc0.1 Mc0.0 Mc0.1 xmaxc00 xmaxc01 xmaxc02 xmaxc03 xmaxc04
+ 7 xmaxc05 xmc0.0 xmc0.1 xmc0.2 xmc1.0 xmc1.1 xmc1.2 xmc2.0
+ 8 xmc2.1 xmc2.2 xmc3.0 xmc3.1 xmc3.2 xmc4.0 xmc4.1 xmc4.2
+ 9 xmc5.0 xmc5.1 xmc5.2 xmc6.0 xmc6.1 xmc6.2 xmc7.0 xmc7.1
+ 10 xmc7.2 xmc8.0 xmc8.1 xmc8.2 xmc9.0 xmc9.1 xmc9.2 xmc10.0
+ 11 xmc10.1 xmc10.2 xmc11.0 xmc11.1 xmc11.2 xmc12.0 xmc12.1 xcm12.2
+ 12 Nc1.0 Nc1.1 Nc1.2 Nc1.3 Nc1.4 Nc1.5 Nc1.6 bc1.0
+ 13 bc1.1 Mc1.0 Mc1.1 xmaxc10 xmaxc11 xmaxc12 xmaxc13 xmaxc14
+ 14 xmax15 xmc13.0 xmc13.1 xmc13.2 xmc14.0 xmc14.1 xmc14.2 xmc15.0
+ 15 xmc15.1 xmc15.2 xmc16.0 xmc16.1 xmc16.2 xmc17.0 xmc17.1 xmc17.2
+ 16 xmc18.0 xmc18.1 xmc18.2 xmc19.0 xmc19.1 xmc19.2 xmc20.0 xmc20.1
+ 17 xmc20.2 xmc21.0 xmc21.1 xmc21.2 xmc22.0 xmc22.1 xmc22.2 xmc23.0
+ 18 xmc23.1 xmc23.2 xmc24.0 xmc24.1 xmc24.2 xmc25.0 xmc25.1 xmc25.2
+ 19 Nc2.0 Nc2.1 Nc2.2 Nc2.3 Nc2.4 Nc2.5 Nc2.6 bc2.0
+ 20 bc2.1 Mc2.0 Mc2.1 xmaxc20 xmaxc21 xmaxc22 xmaxc23 xmaxc24
+ 21 xmaxc25 xmc26.0 xmc26.1 xmc26.2 xmc27.0 xmc27.1 xmc27.2 xmc28.0
+ 22 xmc28.1 xmc28.2 xmc29.0 xmc29.1 xmc29.2 xmc30.0 xmc30.1 xmc30.2
+ 23 xmc31.0 xmc31.1 xmc31.2 xmc32.0 xmc32.1 xmc32.2 xmc33.0 xmc33.1
+ 24 xmc33.2 xmc34.0 xmc34.1 xmc34.2 xmc35.0 xmc35.1 xmc35.2 xmc36.0
+ 25 Xmc36.1 xmc36.2 xmc37.0 xmc37.1 xmc37.2 xmc38.0 xmc38.1 xmc38.2
+ 26 Nc3.0 Nc3.1 Nc3.2 Nc3.3 Nc3.4 Nc3.5 Nc3.6 bc3.0
+ 27 bc3.1 Mc3.0 Mc3.1 xmaxc30 xmaxc31 xmaxc32 xmaxc33 xmaxc34
+ 28 xmaxc35 xmc39.0 xmc39.1 xmc39.2 xmc40.0 xmc40.1 xmc40.2 xmc41.0
+ 29 xmc41.1 xmc41.2 xmc42.0 xmc42.1 xmc42.2 xmc43.0 xmc43.1 xmc43.2
+ 30 xmc44.0 xmc44.1 xmc44.2 xmc45.0 xmc45.1 xmc45.2 xmc46.0 xmc46.1
+ 31 xmc46.2 xmc47.0 xmc47.1 xmc47.2 xmc48.0 xmc48.1 xmc48.2 xmc49.0
+ 32 xmc49.1 xmc49.2 xmc50.0 xmc50.1 xmc50.2 xmc51.0 xmc51.1 xmc51.2
+
+ Table 3: GSM payload format
+*/
+
+
+/*
+ This file encodes a mapping between
+ GSM 05.03 Table 2 and RFC-3551 Table 3.
+*/
+
+/*
+ Naming convention:
+ xxx_p position (bit index)
+ xxx_l length (bit field length)
+ LAR log area ratio
+ N LTP lag
+ b LTP gain
+ M grid
+ Xmax block amplitude
+ x RPE pulses
+*/
+
+
+/**@name Lengths of GSM 06.10 fields */
+//@{
+const unsigned int LAR1_l=6; ///< log area ratio
+const unsigned int LAR2_l=6; ///< log area ratio
+const unsigned int LAR3_l=5; ///< log area ratio
+const unsigned int LAR4_l=5; ///< log area ratio
+const unsigned int LAR5_l=4; ///< log area ratio
+const unsigned int LAR6_l=4; ///< log area ratio
+const unsigned int LAR7_l=3; ///< log area ratio
+const unsigned int LAR8_l=3; ///< log area ratio
+const unsigned int N_l=7; ///< LTP lag
+const unsigned int b_l=2; ///< LTP gain
+const unsigned int M_l=2; ///< grid position
+const unsigned int Xmax_l=6; ///< block amplitude
+const unsigned int x_l=3; ///< RPE pulses
+//@}
+
+
+
+/*@name Indecies of GSM 06.10 fields as they appear in RFC-3551 Table 3. */
+//@{
+
+/**@name Log area ratios, apply to whole frame. */
+//@{
+const unsigned int LAR1_p = 0;
+const unsigned int LAR2_p = LAR1_p + LAR1_l;
+const unsigned int LAR3_p = LAR2_p + LAR2_l;
+const unsigned int LAR4_p = LAR3_p + LAR3_l;
+const unsigned int LAR5_p = LAR4_p + LAR4_l;
+const unsigned int LAR6_p = LAR5_p + LAR5_l;
+const unsigned int LAR7_p = LAR6_p + LAR6_l;
+const unsigned int LAR8_p = LAR7_p + LAR7_l;
+//@}
+/**@name Subframe 1 */
+//@{
+const unsigned int N1_p = LAR8_p + LAR8_l;
+const unsigned int b1_p = N1_p + N_l;
+const unsigned int M1_p = b1_p + b_l;
+const unsigned int Xmax1_p = M1_p + M_l;
+const unsigned int x1_0_p = Xmax1_p + Xmax_l;
+const unsigned int x1_1_p = x1_0_p + x_l;
+const unsigned int x1_2_p = x1_1_p + x_l;
+const unsigned int x1_3_p = x1_2_p + x_l;
+const unsigned int x1_4_p = x1_3_p + x_l;
+const unsigned int x1_5_p = x1_4_p + x_l;
+const unsigned int x1_6_p = x1_5_p + x_l;
+const unsigned int x1_7_p = x1_6_p + x_l;
+const unsigned int x1_8_p = x1_7_p + x_l;
+const unsigned int x1_9_p = x1_8_p + x_l;
+const unsigned int x1_10_p = x1_9_p + x_l;
+const unsigned int x1_11_p = x1_10_p + x_l;
+const unsigned int x1_12_p = x1_11_p + x_l;
+//@}
+/**@name Subframe 2 */
+//@{
+const unsigned int N2_p = x1_12_p + x_l;
+const unsigned int b2_p = N2_p + N_l;
+const unsigned int M2_p = b2_p + b_l;
+const unsigned int Xmax2_p = M2_p + M_l;
+const unsigned int x2_0_p = Xmax2_p + Xmax_l;
+const unsigned int x2_1_p = x2_0_p + x_l;
+const unsigned int x2_2_p = x2_1_p + x_l;
+const unsigned int x2_3_p = x2_2_p + x_l;
+const unsigned int x2_4_p = x2_3_p + x_l;
+const unsigned int x2_5_p = x2_4_p + x_l;
+const unsigned int x2_6_p = x2_5_p + x_l;
+const unsigned int x2_7_p = x2_6_p + x_l;
+const unsigned int x2_8_p = x2_7_p + x_l;
+const unsigned int x2_9_p = x2_8_p + x_l;
+const unsigned int x2_10_p = x2_9_p + x_l;
+const unsigned int x2_11_p = x2_10_p + x_l;
+const unsigned int x2_12_p = x2_11_p + x_l;
+//@}
+/**@mame Subframe 3 */
+//@{
+const unsigned int N3_p = x2_12_p + x_l;
+const unsigned int b3_p = N3_p + N_l;
+const unsigned int M3_p = b3_p + b_l;
+const unsigned int Xmax3_p = M3_p + M_l;
+const unsigned int x3_0_p = Xmax3_p + Xmax_l;
+const unsigned int x3_1_p = x3_0_p + x_l;
+const unsigned int x3_2_p = x3_1_p + x_l;
+const unsigned int x3_3_p = x3_2_p + x_l;
+const unsigned int x3_4_p = x3_3_p + x_l;
+const unsigned int x3_5_p = x3_4_p + x_l;
+const unsigned int x3_6_p = x3_5_p + x_l;
+const unsigned int x3_7_p = x3_6_p + x_l;
+const unsigned int x3_8_p = x3_7_p + x_l;
+const unsigned int x3_9_p = x3_8_p + x_l;
+const unsigned int x3_10_p = x3_9_p + x_l;
+const unsigned int x3_11_p = x3_10_p + x_l;
+const unsigned int x3_12_p = x3_11_p + x_l;
+//@}
+/**@name Subframe 4 */
+//@{
+const unsigned int N4_p = x3_12_p + x_l;
+const unsigned int b4_p = N4_p + N_l;
+const unsigned int M4_p = b4_p + b_l;
+const unsigned int Xmax4_p = M4_p + M_l;
+const unsigned int x4_0_p = Xmax4_p + Xmax_l;
+const unsigned int x4_1_p = x4_0_p + x_l;
+const unsigned int x4_2_p = x4_1_p + x_l;
+const unsigned int x4_3_p = x4_2_p + x_l;
+const unsigned int x4_4_p = x4_3_p + x_l;
+const unsigned int x4_5_p = x4_4_p + x_l;
+const unsigned int x4_6_p = x4_5_p + x_l;
+const unsigned int x4_7_p = x4_6_p + x_l;
+const unsigned int x4_8_p = x4_7_p + x_l;
+const unsigned int x4_9_p = x4_8_p + x_l;
+const unsigned int x4_10_p = x4_9_p + x_l;
+const unsigned int x4_11_p = x4_10_p + x_l;
+const unsigned int x4_12_p = x4_11_p + x_l;
+//@}
+//@}
+
+
+/*
+ This array encodes GSM 05.03 Table 2.
+ It's also GSM 06.10 Table A2.1a.
+ This is the order of bits as they appear in
+ the d[] bits of the GSM TCH/F.
+ RTP[4+g610BitOrder[i]] <=> GSM[i]
+*/
+unsigned int GSM::g610BitOrder[260] = {
+/**@name importance class 1 */
+//@{
+/** LAR1:5 */ LAR1_p+LAR1_l-1-5, /* bit 0 */
+/** Xmax1:5 */ Xmax1_p+Xmax_l-1-5,
+/** Xmax2:5 */ Xmax2_p+Xmax_l-1-5,
+/** Xmax3:5 */ Xmax3_p+Xmax_l-1-5,
+/** Xmax4:5 */ Xmax4_p+Xmax_l-1-5,
+//@}
+/**@name importance class 2 */
+//@{
+/** LAR1:4 */ LAR1_p+LAR1_l-1-4,
+/** LAR2:5 */ LAR2_p+LAR2_l-1-5,
+/** LAR3:4 */ LAR3_p+LAR3_l-1-4,
+//@}
+/**@name importance class 3 */
+//@{
+/** LAR1:3 */ LAR1_p+LAR1_l-1-3,
+/** LAR2:4 */ LAR2_p+LAR2_l-1-4,
+/** LAR3:3 */ LAR3_p+LAR3_l-1-3, /* bit 10 */
+/** LAR4:4 */ LAR4_p+LAR4_l-1-4,
+/** N1:6 */ N1_p+N_l-1-6,
+/** N2:6 */ N2_p+N_l-1-6,
+/** N3:6 */ N3_p+N_l-1-6,
+/** N4:6 */ N4_p+N_l-1-6,
+/** Xmax1:4 */ Xmax1_p+Xmax_l-1-4,
+/** Xmax2:4 */ Xmax2_p+Xmax_l-1-4,
+/** Xmax3:4 */ Xmax3_p+Xmax_l-1-4,
+/** Xmax4:4 */ Xmax4_p+Xmax_l-1-4,
+/** LAR2:3 */ LAR2_p+LAR2_l-1-3, /* bit 20 */
+/** LAR5:3 */ LAR5_p+LAR5_l-1-3,
+/** LAR6:3 */ LAR6_p+LAR6_l-1-3,
+/** N1:5 */ N1_p+N_l-1-5,
+/** N2:5 */ N2_p+N_l-1-5,
+/** N3:5 */ N3_p+N_l-1-5,
+/** N4:5 */ N4_p+N_l-1-5,
+/** N1:4 */ N1_p+N_l-1-4,
+/** N2:4 */ N2_p+N_l-1-4,
+/** N3:4 */ N3_p+N_l-1-4,
+/** N4:4 */ N4_p+N_l-1-4, /* bit 30 */
+/** N1:3 */ N1_p+N_l-1-3,
+/** N2:3 */ N2_p+N_l-1-3,
+/** N3:3 */ N3_p+N_l-1-3,
+/** N4:3 */ N4_p+N_l-1-3,
+/** N1:2 */ N1_p+N_l-1-2,
+/** N2:2 */ N2_p+N_l-1-2,
+/** N3:2 */ N3_p+N_l-1-2,
+/** N4:2 */ N4_p+N_l-1-2,
+//@}
+/**@name importance class 4 */
+//@{
+/** Xmax1:3 */ Xmax1_p+Xmax_l-1-3,
+/** Xmax2:3 */ Xmax2_p+Xmax_l-1-3, /* bit 40 */
+/** Xmax3:3 */ Xmax3_p+Xmax_l-1-3,
+/** Xmax4:3 */ Xmax4_p+Xmax_l-1-3,
+/** LAR1:2 */ LAR1_p+LAR1_l-1-2,
+/** LAR4:3 */ LAR4_p+LAR4_l-1-3,
+/** LAR7:2 */ LAR7_p+LAR7_l-1-2,
+/** N1:1 */ N1_p+N_l-1-1,
+/** N2:1 */ N2_p+N_l-1-1,
+/** N3:1 */ N3_p+N_l-1-1,
+/** N4:1 */ N4_p+N_l-1-1,
+/** LAR5:2 */ LAR5_p+LAR5_l-1-2, /* bit 50 */
+/** LAR6:2 */ LAR6_p+LAR6_l-1-2,
+/** b1:1 */ b1_p+b_l-1-1,
+/** b2:1 */ b2_p+b_l-1-1,
+/** b3:1 */ b3_p+b_l-1-1,
+/** b4:1 */ b4_p+b_l-1-1,
+/** N1:0 */ N1_p+N_l-1-0,
+/** N2:0 */ N2_p+N_l-1-0,
+/** N3:0 */ N3_p+N_l-1-0,
+/** N4:0 */ N4_p+N_l-1-0,
+/** M1:1 */ M1_p+M_l-1-1, /* bit 60 */
+/** M2:1 */ M2_p+M_l-1-1,
+/** M3:1 */ M3_p+M_l-1-1,
+/** M4:1 */ M4_p+M_l-1-1,
+//@}
+/**@name importance class 5 */
+//@{
+/** LAR1:1 */ LAR1_p+LAR1_l-1-1,
+/** LAR2:2 */ LAR2_p+LAR2_l-1-2,
+/** LAR3:2 */ LAR3_p+LAR3_l-1-2,
+/** LAR8:2 */ LAR8_p+LAR8_l-1-2,
+/** LAR4:2 */ LAR4_p+LAR4_l-1-2,
+/** LAR5:1 */ LAR5_p+LAR5_l-1-1,
+/** LAR7:1 */ LAR7_p+LAR7_l-1-1, /* bit 70 */
+/** b1:0 */ b1_p+b_l-1-0,
+/** b2:0 */ b2_p+b_l-1-0,
+/** b3:0 */ b3_p+b_l-1-0,
+/** b4:0 */ b4_p+b_l-1-0,
+/** Xmax1:2 */ Xmax1_p+Xmax_l-1-2,
+/** Xmax2:2 */ Xmax2_p+Xmax_l-1-2,
+/** Xmax3:2 */ Xmax3_p+Xmax_l-1-2,
+/** Xmax4:2 */ Xmax4_p+Xmax_l-1-2,
+/** x1_0:2 */ x1_0_p+x_l-1-2,
+/** x1_1:2 */ x1_1_p+x_l-1-2, /* bit 80 */
+/** x1_2:2 */ x1_2_p+x_l-1-2,
+/** x1_3:2 */ x1_3_p+x_l-1-2,
+/** x1_4:2 */ x1_4_p+x_l-1-2,
+/** x1_5:2 */ x1_5_p+x_l-1-2,
+/** x1_6:2 */ x1_6_p+x_l-1-2,
+/** x1_7:2 */ x1_7_p+x_l-1-2,
+/** x1_8:2 */ x1_8_p+x_l-1-2,
+/** x1_9:2 */ x1_9_p+x_l-1-2,
+/** x1_10:2 */ x1_10_p+x_l-1-2,
+/** x1_11:2 */ x1_11_p+x_l-1-2, /* bit 90 */
+/** x1_12:2 */ x1_12_p+x_l-1-2,
+/** x2_0:2 */ x2_0_p+x_l-1-2,
+/** x2_1:2 */ x2_1_p+x_l-1-2,
+/** x2_2:2 */ x2_2_p+x_l-1-2,
+/** x2_3:2 */ x2_3_p+x_l-1-2,
+/** x2_4:2 */ x2_4_p+x_l-1-2,
+/** x2_5:2 */ x2_5_p+x_l-1-2,
+/** x2_6:2 */ x2_6_p+x_l-1-2,
+/** x2_7:2 */ x2_7_p+x_l-1-2,
+/** x2_8:2 */ x2_8_p+x_l-1-2, /* bit 100 */
+/** x2_9:2 */ x2_9_p+x_l-1-2,
+/** x2_10:2 */ x2_10_p+x_l-1-2,
+/** x2_11:2 */ x2_11_p+x_l-1-2,
+/** x2_12:2 */ x2_12_p+x_l-1-2,
+/** x3_0:2 */ x3_0_p+x_l-1-2,
+/** x3_1:2 */ x3_1_p+x_l-1-2,
+/** x3_2:2 */ x3_2_p+x_l-1-2,
+/** x3_3:2 */ x3_3_p+x_l-1-2,
+/** x3_4:2 */ x3_4_p+x_l-1-2,
+/** x3_5:2 */ x3_5_p+x_l-1-2, /* bit 110 */
+/** x3_6:2 */ x3_6_p+x_l-1-2,
+/** x3_7:2 */ x3_7_p+x_l-1-2,
+/** x3_8:2 */ x3_8_p+x_l-1-2,
+/** x3_9:2 */ x3_9_p+x_l-1-2,
+/** x3_10:2 */ x3_10_p+x_l-1-2,
+/** x3_11:2 */ x3_11_p+x_l-1-2,
+/** x3_12:2 */ x3_12_p+x_l-1-2,
+/** x4_0:2 */ x4_0_p+x_l-1-2,
+/** x4_1:2 */ x4_1_p+x_l-1-2,
+/** x4_2:2 */ x4_2_p+x_l-1-2, /* bit 120 */
+/** x4_3:2 */ x4_3_p+x_l-1-2,
+/** x4_4:2 */ x4_4_p+x_l-1-2,
+/** x4_5:2 */ x4_5_p+x_l-1-2,
+/** x4_6:2 */ x4_6_p+x_l-1-2,
+/** x4_7:2 */ x4_7_p+x_l-1-2,
+/** x4_8:2 */ x4_8_p+x_l-1-2,
+/** x4_9:2 */ x4_9_p+x_l-1-2,
+/** x4_10:2 */ x4_10_p+x_l-1-2,
+/** x4_11:2 */ x4_11_p+x_l-1-2,
+/** x4_12:2 */ x4_12_p+x_l-1-2, /* bit 130 */
+/** M1:0 */ M1_p+M_l-1-0,
+/** M2:0 */ M2_p+M_l-1-0,
+/** M3:0 */ M3_p+M_l-1-0,
+/** M4:0 */ M4_p+M_l-1-0,
+/** Xmax1:1 */ Xmax1_p+Xmax_l-1-1,
+/** Xmax2:1 */ Xmax2_p+Xmax_l-1-1,
+/** Xmax3:1 */ Xmax3_p+Xmax_l-1-1,
+/** Xmax4:1 */ Xmax4_p+Xmax_l-1-1,
+/** x1_0:1 */ x1_0_p+x_l-1-1,
+/** x1_1:1 */ x1_1_p+x_l-1-1, /* bit 140 */
+/** x1_2:1 */ x1_2_p+x_l-1-1,
+/** x1_3:1 */ x1_3_p+x_l-1-1,
+/** x1_4:1 */ x1_4_p+x_l-1-1,
+/** x1_5:1 */ x1_5_p+x_l-1-1,
+/** x1_6:1 */ x1_6_p+x_l-1-1,
+/** x1_7:1 */ x1_7_p+x_l-1-1,
+/** x1_8:1 */ x1_8_p+x_l-1-1,
+/** x1_9:1 */ x1_9_p+x_l-1-1,
+/** x1_10:1 */ x1_10_p+x_l-1-1,
+/** x1_11:1 */ x1_11_p+x_l-1-1, /* bit 150 */
+/** x1_12:1 */ x1_12_p+x_l-1-1,
+/** x2_0:1 */ x2_0_p+x_l-1-1,
+/** x2_1:1 */ x2_1_p+x_l-1-1,
+/** x2_2:1 */ x2_2_p+x_l-1-1,
+/** x2_3:1 */ x2_3_p+x_l-1-1,
+/** x2_4:1 */ x2_4_p+x_l-1-1,
+/** x2_5:1 */ x2_5_p+x_l-1-1,
+/** x2_6:1 */ x2_6_p+x_l-1-1,
+/** x2_7:1 */ x2_7_p+x_l-1-1,
+/** x2_8:1 */ x2_8_p+x_l-1-1, /* bit 160 */
+/** x2_9:1 */ x2_9_p+x_l-1-1,
+/** x2_10:1 */ x2_10_p+x_l-1-1,
+/** x2_11:1 */ x2_11_p+x_l-1-1,
+/** x2_12:1 */ x2_12_p+x_l-1-1,
+/** x3_0:1 */ x3_0_p+x_l-1-1,
+/** x3_1:1 */ x3_1_p+x_l-1-1,
+/** x3_2:1 */ x3_2_p+x_l-1-1,
+/** x3_3:1 */ x3_3_p+x_l-1-1,
+/** x3_4:1 */ x3_4_p+x_l-1-1,
+/** x3_5:1 */ x3_5_p+x_l-1-1, /* bit 170 */
+/** x3_6:1 */ x3_6_p+x_l-1-1,
+/** x3_7:1 */ x3_7_p+x_l-1-1,
+/** x3_8:1 */ x3_8_p+x_l-1-1,
+/** x3_9:1 */ x3_9_p+x_l-1-1,
+/** x3_10:1 */ x3_10_p+x_l-1-1,
+/** x3_11:1 */ x3_11_p+x_l-1-1,
+/** x3_12:1 */ x3_12_p+x_l-1-1,
+/** x4_0:1 */ x4_0_p+x_l-1-1,
+/** x4_1:1 */ x4_1_p+x_l-1-1,
+/** x4_2:1 */ x4_2_p+x_l-1-1, /* bit 180 */
+/** x4_3:1 */ x4_3_p+x_l-1-1,
+//@}
+/**@name importance class 6 */
+//@{
+/** x4_4:1 */ x4_4_p+x_l-1-1,
+/** x4_5:1 */ x4_5_p+x_l-1-1,
+/** x4_6:1 */ x4_6_p+x_l-1-1,
+/** x4_7:1 */ x4_7_p+x_l-1-1,
+/** x4_8:1 */ x4_8_p+x_l-1-1,
+/** x4_9:1 */ x4_9_p+x_l-1-1,
+/** x4_10:1 */ x4_10_p+x_l-1-1,
+/** x4_11:1 */ x4_11_p+x_l-1-1,
+/** x4_12:1 */ x4_12_p+x_l-1-1, /* bit 190 */
+/** LAR1:0 */ LAR1_p+LAR1_l-1-0,
+/** LAR2:1 */ LAR2_p+LAR2_l-1-1,
+/** LAR3:1 */ LAR3_p+LAR3_l-1-1,
+/** LAR6:1 */ LAR6_p+LAR6_l-1-1,
+/** LAR7:0 */ LAR7_p+LAR7_l-1-0,
+/** LAR8:1 */ LAR8_p+LAR8_l-1-1,
+/** LAR8:0 */ LAR8_p+LAR8_l-1-0,
+/** LAR3:0 */ LAR3_p+LAR3_l-1-0,
+/** LAR4:1 */ LAR4_p+LAR4_l-1-1,
+/** LAR4:0 */ LAR4_p+LAR4_l-1-0,
+/** LAR5:0 */ LAR5_p+LAR5_l-1-0,
+/** Xmax1:0 */ Xmax1_p+Xmax_l-1-0,
+/** Xmax2:0 */ Xmax2_p+Xmax_l-1-0,
+/** Xmax3:0 */ Xmax3_p+Xmax_l-1-0,
+/** Xmax4:0 */ Xmax4_p+Xmax_l-1-0,
+/** x1_0:0 */ x1_0_p+x_l-1-0,
+/** x1_1:0 */ x1_1_p+x_l-1-0,
+/** x1_2:0 */ x1_2_p+x_l-1-0,
+/** x1_3:0 */ x1_3_p+x_l-1-0,
+/** x1_4:0 */ x1_4_p+x_l-1-0,
+/** x1_5:0 */ x1_5_p+x_l-1-0,
+/** x1_6:0 */ x1_6_p+x_l-1-0,
+/** x1_7:0 */ x1_7_p+x_l-1-0,
+/** x1_8:0 */ x1_8_p+x_l-1-0,
+/** x1_9:0 */ x1_9_p+x_l-1-0,
+/** x1_10:0 */ x1_10_p+x_l-1-0,
+/** x1_11:0 */ x1_11_p+x_l-1-0,
+/** x1_12:0 */ x1_12_p+x_l-1-0,
+/** x2_0:0 */ x2_0_p+x_l-1-0,
+/** x2_1:0 */ x2_1_p+x_l-1-0,
+/** x2_2:0 */ x2_2_p+x_l-1-0,
+/** x2_3:0 */ x2_3_p+x_l-1-0,
+/** x2_4:0 */ x2_4_p+x_l-1-0,
+/** x2_5:0 */ x2_5_p+x_l-1-0,
+/** x2_6:0 */ x2_6_p+x_l-1-0,
+/** x2_7:0 */ x2_7_p+x_l-1-0,
+/** x2_8:0 */ x2_8_p+x_l-1-0,
+/** x2_9:0 */ x2_9_p+x_l-1-0,
+/** x2_10:0 */ x2_10_p+x_l-1-0,
+/** x2_11:0 */ x2_11_p+x_l-1-0,
+/** x2_12:0 */ x2_12_p+x_l-1-0,
+/** x3_0:0 */ x3_0_p+x_l-1-0,
+/** x3_1:0 */ x3_1_p+x_l-1-0,
+/** x3_2:0 */ x3_2_p+x_l-1-0,
+/** x3_3:0 */ x3_3_p+x_l-1-0,
+/** x3_4:0 */ x3_4_p+x_l-1-0,
+/** x3_5:0 */ x3_5_p+x_l-1-0,
+/** x3_6:0 */ x3_6_p+x_l-1-0,
+/** x3_7:0 */ x3_7_p+x_l-1-0,
+/** x3_8:0 */ x3_8_p+x_l-1-0,
+/** x3_9:0 */ x3_9_p+x_l-1-0,
+/** x3_10:0 */ x3_10_p+x_l-1-0,
+/** x3_11:0 */ x3_11_p+x_l-1-0,
+/** x3_12:0 */ x3_12_p+x_l-1-0,
+/** x4_0:0 */ x4_0_p+x_l-1-0,
+/** x4_1:0 */ x4_1_p+x_l-1-0,
+/** x4_2:0 */ x4_2_p+x_l-1-0,
+/** x4_3:0 */ x4_3_p+x_l-1-0,
+/** x4_4:0 */ x4_4_p+x_l-1-0,
+/** x4_5:0 */ x4_5_p+x_l-1-0,
+/** x4_6:0 */ x4_6_p+x_l-1-0,
+/** x4_7:0 */ x4_7_p+x_l-1-0,
+/** x4_8:0 */ x4_8_p+x_l-1-0,
+/** x4_9:0 */ x4_9_p+x_l-1-0,
+/** x4_10:0 */ x4_10_p+x_l-1-0,
+/** x4_11:0 */ x4_11_p+x_l-1-0,
+/** x4_12:0 */ x4_12_p+x_l-1-0,
+/** LAR2:0 */ LAR2_p+LAR2_l-1-0,
+/** LAR6:0 */ LAR6_p+LAR6_l-1-0
+//@}
+};
+
diff --git a/gsm-receiver/src/lib/decoder/openbtsstuff/GSM610Tables.h b/gsm-receiver/src/lib/decoder/openbtsstuff/GSM610Tables.h
new file mode 100644
index 0000000..53a8e1c
--- /dev/null
+++ b/gsm-receiver/src/lib/decoder/openbtsstuff/GSM610Tables.h
@@ -0,0 +1,37 @@
+/*
+* Copyright 2008 Free Software Foundation, Inc.
+*
+* This software is distributed under the terms of the GNU Public License.
+* See the COPYING file in the main directory for details.
+
+ This program is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
+
+*/
+
+
+
+#ifndef GSM610TABLES_H
+#define GSM610TABLES_H
+
+
+
+namespace GSM {
+
+/** Table #2 from GSM 05.03 */
+extern unsigned int g610BitOrder[260];
+
+}
+
+
+#endif
diff --git a/gsm-receiver/src/lib/decoder/openbtsstuff/GSMCommon.cpp b/gsm-receiver/src/lib/decoder/openbtsstuff/GSMCommon.cpp
new file mode 100644
index 0000000..0bec812
--- /dev/null
+++ b/gsm-receiver/src/lib/decoder/openbtsstuff/GSMCommon.cpp
@@ -0,0 +1,315 @@
+/*
+* Copyright 2008 Free Software Foundation, Inc.
+*
+* This software is distributed under the terms of the GNU Public License.
+* See the COPYING file in the main directory for details.
+
+ This program is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
+
+*/
+
+
+#include "GSMCommon.h"
+
+using namespace GSM;
+using namespace std;
+
+
+ostream& GSM::operator<<(ostream& os, L3PD val)
+{
+ switch (val) {
+ case L3CallControlPD: os << "Call Control"; break;
+ case L3MobilityManagementPD: os << "Mobility Management"; break;
+ case L3RadioResourcePD: os << "Radio Resource"; break;
+ default: os << hex << "0x" << (int)val << dec;
+ }
+ return os;
+}
+
+
+const BitVector GSM::gTrainingSequence[] = {
+ BitVector("00100101110000100010010111"),
+ BitVector("00101101110111100010110111"),
+ BitVector("01000011101110100100001110"),
+ BitVector("01000111101101000100011110"),
+ BitVector("00011010111001000001101011"),
+ BitVector("01001110101100000100111010"),
+ BitVector("10100111110110001010011111"),
+ BitVector("11101111000100101110111100"),
+};
+
+const BitVector GSM::gDummyBurst("0001111101101110110000010100100111000001001000100000001111100011100010111000101110001010111010010100011001100111001111010011111000100101111101010000");
+
+const BitVector GSM::gRACHSynchSequence("01001011011111111001100110101010001111000");
+
+
+
+char encodeGSMChar(char ascii)
+{
+ // Given an ASCII char, return the corresponding GSM char.
+ static char reverseTable[256]={0};
+ static bool init = false;
+ if (!init) {
+ for (size_t i=0; i<sizeof(gGSMAlphabet); i++) {
+ reverseTable[(unsigned)gGSMAlphabet[i]]=i;
+ }
+ init=true;
+ }
+ return reverseTable[(unsigned)ascii];
+}
+
+
+char encodeBCDChar(char ascii)
+{
+ // Given an ASCII char, return the corresponding BCD.
+ if ((ascii>='0') && (ascii<='9')) return ascii-'0';
+ switch (ascii) {
+ case '.': return 11;
+ case '*': return 11;
+ case '#': return 12;
+ case 'a': return 13;
+ case 'b': return 14;
+ case 'c': return 15;
+ default: return 15;
+ }
+}
+
+
+
+
+unsigned GSM::uplinkFreqKHz(GSMBand band, unsigned ARFCN)
+{
+ switch (band) {
+ case GSM850:
+ assert((ARFCN<252)&&(ARFCN>129));
+ return 824200+200*(ARFCN-128);
+ case EGSM900:
+ if (ARFCN<=124) return 890000+200*ARFCN;
+ assert((ARFCN>974)&&(ARFCN<1024));
+ return 890000+200*(ARFCN-1024);
+ case DCS1800:
+ assert((ARFCN>511)&&(ARFCN<886));
+ return 1710200+200*(ARFCN-512);
+ case PCS1900:
+ assert((ARFCN>511)&&(ARFCN<811));
+ return 1850200+200*(ARFCN-512);
+ default:
+ abort();
+ }
+}
+
+
+unsigned GSM::downlinkFreqKHz(GSMBand band, unsigned ARFCN)
+{
+ static unsigned uplinkOffset[] = {
+ 45000, // 850
+ 45000, // 900
+ 95000, // 1800
+ 80000 // 1900
+ };
+ return uplinkFreqKHz(band,ARFCN) + uplinkOffset[band];
+}
+
+
+
+
+int32_t GSM::FNDelta(int32_t v1, int32_t v2)
+{
+ static const int64_t halfModulus = gHyperframe/2;
+ int32_t delta = v1-v2;
+ if (delta>halfModulus) delta -= gHyperframe;
+ else if (delta<-halfModulus) delta += gHyperframe;
+ return (int32_t) delta;
+}
+
+int GSM::FNCompare(int32_t v1, int32_t v2)
+{
+ int32_t delta = FNDelta(v1,v2);
+ if (delta==0) return 0;
+ else if (delta>0) return 1;
+ else return -1;
+}
+
+
+
+
+ostream& GSM::operator<<(ostream& os, const Time& t)
+{
+ os << t.TN() << ":" << t.FN();
+ return os;
+}
+
+
+
+
+void Clock::set(const Time& when)
+{
+ mLock.lock();
+ mBaseTime = Timeval(0);
+ mBaseFN = when.FN();
+ mLock.unlock();
+}
+
+
+int32_t Clock::FN() const
+{
+ mLock.lock();
+ Timeval now;
+ int deltaSec = now.sec() - mBaseTime.sec();
+ int deltaUSec = now.usec() - mBaseTime.usec();
+ int elapsedUSec = 1000000*deltaSec + deltaUSec;
+ int elapsedFrames = elapsedUSec / gFrameMicroseconds;
+ int32_t currentFN = (mBaseFN + elapsedFrames) % gHyperframe;
+ mLock.unlock();
+ return currentFN;
+}
+
+
+void Clock::wait(const Time& when) const
+{
+ int32_t now = FN();
+ int32_t target = when.FN();
+ int32_t delta = FNDelta(target,now);
+ if (delta<1) return;
+ const int32_t maxSleep = 51*26;
+ if (delta>maxSleep) delta=maxSleep;
+ sleepFrames(delta);
+}
+
+
+
+
+
+
+
+ostream& GSM::operator<<(ostream& os, TypeOfNumber type)
+{
+ switch (type) {
+ case UnknownTypeOfNumber: os << "unknown"; break;
+ case InternationalNumber: os << "international"; break;
+ case NationalNumber: os << "national"; break;
+ case NetworkSpecificNumber: os << "network-specific"; break;
+ case ShortCodeNumber: os << "short code"; break;
+ default: os << "?" << type << "?";
+ }
+ return os;
+}
+
+
+ostream& GSM::operator<<(ostream& os, NumberingPlan plan)
+{
+ switch (plan) {
+ case UnknownPlan: os << "unknown"; break;
+ case E164Plan: os << "E.164/ISDN"; break;
+ case X121Plan: os << "X.121/data"; break;
+ case F69Plan: os << "F.69/Telex"; break;
+ case NationalPlan: os << "national"; break;
+ case PrivatePlan: os << "private"; break;
+ default: os << "?" << (int)plan << "?";
+ }
+ return os;
+}
+
+ostream& GSM::operator<<(ostream& os, MobileIDType wID)
+{
+ switch (wID) {
+ case NoIDType: os << "None"; break;
+ case IMSIType: os << "IMSI"; break;
+ case IMEIType: os << "IMEI"; break;
+ case TMSIType: os << "TMSI"; break;
+ case IMEISVType: os << "IMEISV"; break;
+ default: os << "?" << (int)wID << "?";
+ }
+ return os;
+}
+
+
+ostream& GSM::operator<<(ostream& os, TypeAndOffset tao)
+{
+ switch (tao) {
+ case TDMA_MISC: os << "(misc)"; break;
+ case TCHF_0: os << "TCH/F"; break;
+ case TCHH_0: os << "TCH/H-0"; break;
+ case TCHH_1: os << "TCH/H-1"; break;
+ case SDCCH_4_0: os << "SDCCH/4-0"; break;
+ case SDCCH_4_1: os << "SDCCH/4-1"; break;
+ case SDCCH_4_2: os << "SDCCH/4-2"; break;
+ case SDCCH_4_3: os << "SDCCH/4-3"; break;
+ case SDCCH_8_0: os << "SDCCH/8-0"; break;
+ case SDCCH_8_1: os << "SDCCH/8-1"; break;
+ case SDCCH_8_2: os << "SDCCH/8-2"; break;
+ case SDCCH_8_3: os << "SDCCH/8-3"; break;
+ case SDCCH_8_4: os << "SDCCH/8-4"; break;
+ case SDCCH_8_5: os << "SDCCH/8-5"; break;
+ case SDCCH_8_6: os << "SDCCH/8-6"; break;
+ case SDCCH_8_7: os << "SDCCH/8-7"; break;
+ case TDMA_BEACON: os << "(beacon)"; break;
+ default: os << "?" << (int)tao << "?";
+ }
+ return os;
+}
+
+ostream& GSM::operator<<(ostream& os, ChannelType val)
+{
+ switch (val) {
+ case UndefinedCHType: os << "undefined"; return os;
+ case SCHType: os << "SCH"; break;
+ case FCCHType: os << "FCCH"; break;
+ case BCCHType: os << "BCCH"; break;
+ case RACHType: os << "RACH"; break;
+ case SDCCHType: os << "SDCCH"; break;
+ case FACCHType: os << "FACCH"; break;
+ case CCCHType: os << "CCCH"; break;
+ case SACCHType: os << "SACCH"; break;
+ case TCHFType: os << "TCH/F"; break;
+ case TCHHType: os << "TCH/H"; break;
+ case AnyTCHType: os << "any TCH"; break;
+ case LoopbackFullType: os << "Loopback Full"; break;
+ case LoopbackHalfType: os << "Loopback Half"; break;
+ case AnyDCCHType: os << "any DCCH"; break;
+ default: os << "?" << (int)val << "?";
+ }
+ return os;
+}
+
+
+
+
+bool Z100Timer::expired() const
+{
+ // A non-active timer does not expire.
+ if (!mActive) return false;
+ return mEndTime.passed();
+}
+
+void Z100Timer::set()
+{
+ mEndTime = Timeval(mLimitTime);
+ mActive=true;
+}
+
+long Z100Timer::remaining() const
+{
+ if (!mActive) return 0;
+ long rem = mEndTime.remaining();
+ if (rem<0) rem=0;
+ return rem;
+}
+
+void Z100Timer::wait() const
+{
+ while (!expired()) msleep(remaining());
+}
+
+// vim: ts=4 sw=4
diff --git a/gsm-receiver/src/lib/decoder/openbtsstuff/GSMCommon.h b/gsm-receiver/src/lib/decoder/openbtsstuff/GSMCommon.h
new file mode 100644
index 0000000..9dc1c95
--- /dev/null
+++ b/gsm-receiver/src/lib/decoder/openbtsstuff/GSMCommon.h
@@ -0,0 +1,537 @@
+/**@file Common-use GSM declarations, most from the GSM 04.xx and 05.xx series. */
+/*
+* Copyright 2008 Free Software Foundation, Inc.
+*
+* This software is distributed under the terms of the GNU Public License.
+* See the COPYING file in the main directory for details.
+
+ This program is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
+
+*/
+
+
+
+#ifndef GSMCOMMON_H
+#define GSMCOMMON_H
+
+#include <stdlib.h>
+#include <sys/time.h>
+#include <ostream>
+#include <vector>
+
+#include <Threads.h>
+#include <Timeval.h>
+#include <BitVector.h>
+
+
+
+
+namespace GSM {
+
+/**@namespace GSM This namespace covers L1 FEC, L2 and L3 message translation. */
+
+
+/* forward references */
+class L1FEC;
+class L2LAPDm;
+class L3Processor;
+class LogicalChannel;
+class L2Header;
+
+
+/** A base class for GSM exceptions. */
+class GSMError {};
+
+/** Duration ofa GSM frame, in microseconds. */
+const unsigned gFrameMicroseconds = 4615;
+
+
+/** Sleep for a given number of GSM frame periods. */
+inline void sleepFrames(unsigned frames)
+ { usleep(frames*gFrameMicroseconds); }
+
+/** Sleep for 1 GSM frame period. */
+inline void sleepFrame()
+ { usleep(gFrameMicroseconds); }
+
+
+
+/** GSM Training sequences from GSM 05.02 5.2.3. */
+extern const BitVector gTrainingSequence[];
+
+/** C0T0 filler burst, GSM 05.02, 5.2.6 */
+extern const BitVector gDummyBurst;
+
+/** Random access burst synch. sequence */
+extern const BitVector gRACHSynchSequence;
+
+
+/**@name Support for GSM 7-bit alphabet, GSM 03.38 6.2.1. */
+//@{
+/** Indexed by GSM 7-bit, returns ASCII. */
+static const char gGSMAlphabet[] = "@\243$\245\350\351\371\354\362\347\n\330\370\r\305\345D_FGLOPCSTZ \306\346\337\311 !\"#\244%&\'()*+,-./0123456789:;<=>?\241ABCDEFGHIJKLMNOPQRSTUVWXYZ\304\326\321\334\247\277abcdefghijklmnopqrstuvwxyz\344\366\361\374\341";
+char encodeGSMChar(char ascii);
+inline char decodeGSMChar(char sms) { return gGSMAlphabet[(unsigned)sms]; }
+//@}
+
+
+/**@name BCD-ASCII mapping, GMS 04.08 Table 10.5.118. */
+//@{
+/** Indexed by BCD, returns ASCII. */
+static const char gBCDAlphabet[] = "0123456789.#abc";
+char encodeBCDChar(char ascii);
+inline char decodeBCDChar(char bcd) { return gBCDAlphabet[(unsigned)bcd]; }
+//@}
+
+
+/**@name Globally-fixed GSM timeout values (all in ms). */
+//@{
+/**@name GSM LAPDm timeouts, GSM 04.06 5.8, ITU-T Q.921 5.9 */
+//@{
+const unsigned T200ms = 900; ///< LAPDm ACK timeout, set for typical turnaround time
+//@}
+/**@name GSM timeouts for radio resource management, GSM 04.08 11.1. */
+//@{
+const unsigned T3101ms = 4000; ///< L1 timeout for SDCCH assignment
+const unsigned T3107ms = 3000; ///< L1 timeout for TCH/FACCH assignment
+const unsigned T3109ms = 10000; ///< L1 timeout for an existing channel
+const unsigned T3111ms = 2*T200ms; ///< L1 timeout for reassignment of a channel
+const unsigned T3113ms = 10000; ///< timeout for paging response
+const unsigned T3122ms = 2000; ///< RR access holdoff time (GSM 04.08 3.3.1.1.3.2)
+//@}
+/**@name GSM timeouts for mobility management, GSM 04.08 11.2. */
+//@{
+const unsigned T3212ms = 8*360000; ///< location updating period (in 6-min increments, 0-255)
+//const unsigned T3212ms = 0; ///< location updating period (in 6-min increments, 0-255), 0 disables
+//@}
+//@}
+
+
+
+
+/** GSM 04.08 Table 10.5.118 */
+enum TypeOfNumber {
+ UnknownTypeOfNumber = 0,
+ InternationalNumber = 1,
+ NationalNumber = 2,
+ NetworkSpecificNumber = 3,
+ ShortCodeNumber = 4
+};
+
+std::ostream& operator<<(std::ostream&, TypeOfNumber);
+
+
+/** GSM 04.08 Table 10.5.118 */
+enum NumberingPlan {
+ UnknownPlan = 0,
+ E164Plan = 1,
+ X121Plan = 3,
+ F69Plan = 4,
+ NationalPlan = 8,
+ PrivatePlan = 9
+};
+
+std::ostream& operator<<(std::ostream&, NumberingPlan);
+
+
+
+/** Codes for GSM band types, GSM 05.05 2. */
+enum GSMBand {
+ GSM850=0, ///< US cellular
+ EGSM900, ///< extended GSM
+ DCS1800, ///< worldwide DCS band
+ PCS1900 ///< US PCS band
+};
+
+
+/**@name Actual radio carrier frequencies, in kHz, GSM 05.05 2 */
+//@{
+unsigned uplinkFreqKHz(GSMBand wBand, unsigned wARFCN);
+unsigned downlinkFreqKHz(GSMBand wBand, unsigned wARFCN);
+//@}
+
+
+
+/**@name GSM Logical channel (LCH) types. */
+//@{
+/** Codes for logical channel types. */
+enum ChannelType {
+ ///@name Non-dedicated control channels.
+ //@{
+ SCHType, ///< sync
+ FCCHType, ///< frequency correction
+ BCCHType, ///< broadcast control
+ CCCHType, ///< common control, a combination of several sub-types
+ RACHType, ///< random access
+ SACCHType, ///< slow associated control (acutally dedicated, but...)
+ //@}
+ ///@name Dedicated control channels (DCCHs).
+ //@{
+ SDCCHType, ///< standalone dedicated control
+ FACCHType, ///< fast associated control
+ //@}
+ ///@name Traffic channels
+ //@{
+ TCHFType, ///< full-rate traffic
+ TCHHType, ///< half-rate traffic
+ AnyTCHType, ///< any TCH type
+ //@}
+ ///@name Special internal channel types.
+ //@{
+ LoopbackFullType, ///< loopback testing
+ LoopbackHalfType, ///< loopback testing
+ AnyDCCHType, ///< any dedicated control channel
+ UndefinedCHType, ///< undefined
+ //@}
+};
+
+
+/** Print channel type name to a stream. */
+std::ostream& operator<<(std::ostream& os, ChannelType val);
+
+
+//@}
+
+
+
+/** Mobile identity types, GSM 04.08 10.5.1.4 */
+enum MobileIDType {
+ NoIDType = 0,
+ IMSIType = 1,
+ IMEIType = 2,
+ IMEISVType = 3,
+ TMSIType = 4
+};
+
+std::ostream& operator<<(std::ostream& os, MobileIDType);
+
+
+/** Type and TDMA offset of a logical channel, from GSM 04.08 10.5.2.5 */
+enum TypeAndOffset {
+ TDMA_MISC=0,
+ TCHF_0=1,
+ TCHH_0=2, TCHH_1=3,
+ SDCCH_4_0=4, SDCCH_4_1=5, SDCCH_4_2=6, SDCCH_4_3=7,
+ SDCCH_8_0=8, SDCCH_8_1=9, SDCCH_8_2=10, SDCCH_8_3=11,
+ SDCCH_8_4=12, SDCCH_8_5=13, SDCCH_8_6=14, SDCCH_8_7=15,
+ /// An extra one for our internal use.
+ TDMA_BEACON=255
+};
+
+std::ostream& operator<<(std::ostream& os, TypeAndOffset);
+
+
+
+
+
+
+
+
+/**
+ L3 Protocol Discriminator, GSM 04.08 10.2, GSM 04.07 11.2.3.1.1.
+*/
+enum L3PD {
+ L3GroupCallControlPD=0x00,
+ L3BroadcastCallControlPD=0x01,
+ L3PDSS1PD=0x02,
+ L3CallControlPD=0x03,
+ L3PDSS2PD=0x04,
+ L3MobilityManagementPD=0x05,
+ L3RadioResourcePD=0x06,
+ L3MobilityManagementGPRSPD=0x08,
+ L3SMSPD=0x09,
+ L3GPRSSessionManagementPD=0x0a,
+ L3NonCallSSPD=0x0b,
+ L3LocationPD=0x0c,
+ L3ExtendedPD=0x0e,
+ L3TestProcedurePD=0x0f,
+ L3UndefinedPD=-1
+};
+
+
+
+std::ostream& operator<<(std::ostream& os, L3PD val);
+
+
+
+
+/**@name Modulus operations for frame numbers. */
+//@{
+/** The GSM hyperframe is largest time period in the GSM system, GSM 05.02 4.3.3. */
+const int32_t gHyperframe = 2048UL * 26UL * 51UL;
+
+/** Get a clock difference, within the modulus. */
+int32_t FNDelta(int32_t v1, int32_t v2);
+
+/**
+ Compare two frame clock values.
+ @return 1 if v1>v2, -1 if v1<v2, 0 if v1==v2
+*/
+int FNCompare(int32_t v1, int32_t v2);
+
+
+//@}
+
+
+
+
+/**
+ GSM frame clock value.
+ No internal thread sync.
+*/
+class Time {
+
+ private:
+
+ int mFN; ///< frame number in the hyperframe
+ unsigned mTN; ///< timeslot number
+
+ public:
+
+ Time(int wFN=0, unsigned wTN=0)
+ :mFN(wFN),mTN(wTN)
+ { }
+
+
+ /** Move the time forward to a given position in a given modulus. */
+ void rollForward(unsigned wFN, unsigned modulus)
+ { while ((mFN % modulus) != wFN) mFN++; }
+
+ /**@name Accessors. */
+ //@{
+ int FN() const { return mFN; }
+ void FN(unsigned wFN) { mFN = wFN; }
+ unsigned TN() const { return mTN; }
+ void TN(unsigned wTN) { mTN=wTN; }
+ //@}
+
+ /**@name Arithmetic. */
+ //@{
+
+ Time& operator++()
+ {
+ mFN = (mFN+1) % gHyperframe;
+ return *this;
+ }
+
+ Time& decTN(int step=1)
+ {
+ if ((int)mTN<step) mFN = *this - Time(1,0);
+ if (mTN-step < 0) mTN = (mTN-step+8) % 8;
+ else mTN = (mTN-step) % 8;
+ return *this;
+ }
+
+ Time& incTN(int step=1)
+ {
+ mFN = mFN + (mTN + step)/8;
+ mTN = (mTN+step) % 8;
+ return *this;
+ }
+
+ Time& operator+=(int step)
+ {
+ mFN = (mFN+step) % gHyperframe;
+ return *this;
+ }
+
+ Time operator+(int step) const
+ {
+ Time newVal = *this;
+ newVal += step;
+ return newVal;
+ }
+
+ Time operator-(int step) const
+ {
+ return operator+(-step);
+ }
+
+ Time operator+(const Time& other) const
+ {
+ unsigned newTN = (mTN + other.mTN) % 8;
+ uint64_t newFN = (mFN+other.mFN + (mTN + other.mTN)/8) % gHyperframe;
+ return Time(newFN,newTN);
+ }
+
+ int operator-(const Time& other) const
+ {
+ return FNDelta(mFN,other.mFN);
+ }
+
+ //@}
+
+
+ /**@name Comparisons. */
+ //@{
+
+ bool operator<(const Time& other) const
+ {
+ if (mFN==other.mFN) return (mTN<other.mTN);
+ return FNCompare(mFN,other.mFN)<0;
+ }
+
+ bool operator>(const Time& other) const
+ {
+ if (mFN==other.mFN) return (mTN>other.mTN);
+ return FNCompare(mFN,other.mFN)>0;
+ }
+
+ bool operator<=(const Time& other) const
+ {
+ if (mFN==other.mFN) return (mTN<=other.mTN);
+ return FNCompare(mFN,other.mFN)<=0;
+ }
+
+ bool operator>=(const Time& other) const
+ {
+ if (mFN==other.mFN) return (mTN>=other.mTN);
+ return FNCompare(mFN,other.mFN)>=0;
+ }
+
+ bool operator==(const Time& other) const
+ {
+ return (mFN == other.mFN) && (mTN==other.mTN);
+ }
+
+ //@}
+
+
+
+ /**@name Standard derivations. */
+ //@{
+
+ unsigned SFN() const { return mFN / (26*51); }
+
+ unsigned T1() const { return SFN() % 2048; }
+
+ unsigned T2() const { return mFN % 26; }
+
+ unsigned T3() const { return mFN % 51; }
+
+ /** GSM 05.02 3.3.2.2.1. */
+ unsigned T3p() const { return (T3()-1)/10; }
+
+ /** GSM 05.02 6.3.1.3. */
+ unsigned TC() const { return (FN()/51) % 8; }
+
+ /** GSM 04.08 10.5.2.30. */
+ unsigned T1p() const { return SFN() % 32; }
+
+ /** GSM 05.02 6.2.3 */
+ unsigned T1R() const { return T1() % 64; }
+
+ //@}
+};
+
+
+std::ostream& operator<<(std::ostream& os, const Time& ts);
+
+
+
+
+
+
+/**
+ A class for calculating the current GSM frame number.
+*/
+class Clock {
+
+ private:
+
+ mutable Mutex mLock;
+ int32_t mBaseFN;
+ Timeval mBaseTime;
+
+ public:
+
+ Clock(const Time& when = Time(0))
+ :mBaseFN(when.FN())
+ {}
+
+ /** Set the clock to a value. */
+ void set(const Time&);
+
+ /** Read the clock. */
+ int32_t FN() const;
+
+ /** Read the clock. */
+ Time get() const { return Time(FN()); }
+
+ /** Block until the clock passes a given time. */
+ void wait(const Time&) const;
+};
+
+
+
+
+
+
+
+
+/**
+ CCITT Z.100 activity timer, as described in GSM 04.06 5.1.
+ All times are in milliseconds.
+*/
+class Z100Timer {
+
+ private:
+
+ Timeval mEndTime; ///< the time at which this timer will expire
+ long mLimitTime; ///< timeout in milliseconds
+ bool mActive; ///< true if timer is active
+
+ public:
+
+ /** Create a timer with a given timeout in milliseconds. */
+ Z100Timer(long wLimitTime)
+ :mLimitTime(wLimitTime),
+ mActive(false)
+ {}
+
+ /** True if the timer is active and expired. */
+ bool expired() const;
+
+ /** Start or restart the timer. */
+ void set();
+
+ /** Stop the timer. */
+ void reset() { mActive = false; }
+
+ /** Returns true if the timer is active. */
+ bool active() const { return mActive; }
+
+ /**
+ Remaining time until expiration, in milliseconds.
+ Returns zero if the timer has expired.
+ */
+ long remaining() const;
+
+ /**
+ Block until the timer expires.
+ Returns immediately if the timer is not running.
+ */
+ void wait() const;
+};
+
+
+
+
+
+}; // namespace GSM
+
+
+#endif
+
+// vim: ts=4 sw=4
diff --git a/gsm-receiver/src/lib/decoder/openbtsstuff/GSML1FEC.cpp b/gsm-receiver/src/lib/decoder/openbtsstuff/GSML1FEC.cpp
new file mode 100644
index 0000000..1c99a0f
--- /dev/null
+++ b/gsm-receiver/src/lib/decoder/openbtsstuff/GSML1FEC.cpp
@@ -0,0 +1,256 @@
+/*
+* Copyright 2008 Free Software Foundation, Inc.
+*
+* This software is distributed under the terms of the GNU Public License.
+* See the COPYING file in the main directory for details.
+
+ This program is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
+
+*/
+
+
+#define NDEBUG
+
+
+#include "GSML1FEC.h"
+#include "GSMCommon.h"
+#include "RxBurst.h"
+//#include "GSMSAPMux.h"
+//#include "GSMConfig.h"
+#include "GSMTDMA.h"
+#include "GSM610Tables.h"
+#include "Assert.h"
+
+
+using namespace std;
+using namespace GSM;
+
+/*
+ Compilation flags:
+ NOCONTROL Compile without referencing control layer functions.
+*/
+
+
+/*
+
+ Notes on reading the GSM specifications.
+
+ Every FEC section in GSM 05.03 uses standard names for the bits at
+ different stages of the encoding/decoding process.
+
+ This is all described formally in GSM 05.03 2.2.
+
+ "d" -- data bits. The actual payloads from L2 and the vocoders.
+ "p" -- parity bits. These are calculated from d.
+ "u" -- uncoded bits. A concatenation of d, p and inner tail bits.
+ "c" -- coded bits. These are the convolutionally encoded from u.
+ "i" -- interleaved bits. These are the output of the interleaver.
+ "e" -- "encrypted" bits. These are the channel bits in the radio bursts.
+
+ The "e" bits are call "encrypted" even when encryption is not used.
+
+ The encoding process is:
+
+ L2 -> d -> -> calc p -> u -> c -> i -> e -> radio bursts
+
+ The decoding process is:
+
+ radio bursts -> e -> i -> c -> u -> check p -> d -> L2
+
+ Bit ordering in d is LSB-first in each octet.
+ Bit ordering everywhere else in the OpenBTS code is MSB-first
+ in every field to give contiguous fields across byte boundaries.
+ We use the BitVector::LSB8MSB() method to translate.
+
+*/
+
+TCHFACCHL1Decoder::TCHFACCHL1Decoder(const TDMAMapping& wMapping)
+ : mTCHU(189), mTCHD(260), mC(456),
+ mClass1_c(mC.head(378)), mClass1A_d(mTCHD.head(50)), mClass2_c(mC.segment(378, 78)),
+ mTCHParity(0x0b, 3, 50), mMapping(wMapping)
+{
+ for (int i = 0; i < 8; i++) {
+ mI[i] = SoftVector(114);
+ }
+}
+
+
+void TCHFACCHL1Decoder::writeLowSide(const RxBurst& inBurst)
+{
+ OBJDCOUT("TCHFACCHL1Decoder::writeLowSide " << inBurst);
+ // If the channel is closed, ignore the burst.
+// if (!active()) {
+// OBJDCOUT("TCHFACCHL1Decoder::writeLowSide not active, ignoring input");
+// return;
+// }
+ processBurst(inBurst);
+}
+
+bool TCHFACCHL1Decoder::processBurst( const RxBurst& inBurst)
+{
+ // Accept the burst into the deinterleaving buffer.
+ // Return true if we are ready to interleave.
+
+ // TODO -- One quick test of burst validity is to look at the tail bits.
+ // We could do that as a double-check against putting garbage into
+ // the interleaver or accepting bad parameters.
+
+ // Get the physical parameters of the burst.
+ // RSSI is dB wrt full scale.
+// mRSSI = inBurst.RSSI();
+ // Timing error is a float in symbol intervals.
+// mTimingError = inBurst.timingError();
+ // This flag is used as a half-ass semaphore.
+ // It is cleared when the new value is read.
+// mPhyNew = true;
+
+ // The reverse index runs 0..3 as the bursts arrive.
+ // It is the "B" index of GSM 05.03 3.1.3 and 3.1.4.
+ int B = mMapping.reverseMapping(inBurst.time().FN()) % 8;
+ // A negative value means that the demux is misconfigured.
+ assert(B >= 0);
+ OBJDCOUT("TCHFACCHL1Decoder::processBurst B=" << B << " " << inBurst);
+
+ // Pull the data fields (e-bits) out of the burst and put them into i[B][].
+ // GSM 05.03 3.1.4
+ inBurst.data1().copyToSegment(mI[B], 0);
+ inBurst.data2().copyToSegment(mI[B], 57);
+
+ // Every 4th frame is the start of a new block.
+ // So if this isn't a "4th" frame, return now.
+ if (B % 4 != 3) return false;
+
+ // Deinterleave according to the diagonal "phase" of B.
+ // See GSM 05.03 3.1.3.
+ // Deinterleaves i[] to c[]
+ if (B == 3) deinterleave(4);
+ else deinterleave(0);
+
+ // See if this was the end of a stolen frame, GSM 05.03 4.2.5.
+ bool stolen = inBurst.Hl();
+ OBJDCOUT("TCHFACCHL!Decoder::processBurst Hl=" << inBurst.Hl() << " Hu=" << inBurst.Hu());
+ /* if (stolen) {
+ if (decode()) {
+ OBJDCOUT("TCHFACCHL1Decoder::processBurst good FACCH frame");
+ countGoodFrame();
+ handleGoodFrame();
+ } else {
+ OBJDCOUT("TCHFACCHL1Decoder::processBurst bad FACCH frame");
+ countBadFrame();
+ }
+ }*/
+
+ // Always feed the traffic channel, even on a stolen frame.
+ // decodeTCH will handle the GSM 06.11 bad frmae processing.
+ bool traffic = decodeTCH(stolen);
+// if (traffic) {
+ OBJDCOUT("TCHFACCHL1Decoder::processBurst good TCH frame");
+// countGoodFrame();
+ // Don't let the channel timeout.
+ // mLock.lock();
+ // mT3109.set();
+ // mLock.unlock();
+// }
+// else countBadFrame();
+
+ return traffic;
+}
+
+void TCHFACCHL1Decoder::deinterleave(int blockOffset )
+{
+ OBJDCOUT("TCHFACCHL1Decoder::deinterleave blockOffset=" << blockOffset);
+ for (int k = 0; k < 456; k++) {
+ int B = ( k + blockOffset ) % 8;
+ int j = 2 * ((49 * k) % 57) + ((k % 8) / 4);
+ mC[k] = mI[B][j];
+ mI[B][j] = 0.5F;
+ //OBJDCOUT("deinterleave k="<<k<<" B="<<B<<" j="<<j);
+ }
+}
+
+bool TCHFACCHL1Decoder::decodeTCH(bool stolen)
+{
+ // GSM 05.02 3.1.2, but backwards
+
+ // If the frame wasn't stolen, we'll update this with parity later.
+ bool good = !stolen;
+
+ // Good or bad, we will be sending *something* to the speech channel.
+ // Allocate it in this scope.
+ unsigned char * newFrame = new unsigned char[33];
+
+ if (!stolen) {
+
+ // 3.1.2.2
+ // decode from c[] to u[]
+ mClass1_c.decode(mVCoder, mTCHU);
+ //mC.head(378).decode(mVCoder,mTCHU);
+
+ // 3.1.2.2
+ // copy class 2 bits c[] to d[]
+ mClass2_c.sliced().copyToSegment(mTCHD, 182);
+ //mC.segment(378,78).sliced().copyToSegment(mTCHD,182);
+
+ // 3.1.2.1
+ // copy class 1 bits u[] to d[]
+ for (unsigned k = 0; k <= 90; k++) {
+ mTCHD[2*k] = mTCHU[k];
+ mTCHD[2*k+1] = mTCHU[184-k];
+ }
+
+ // 3.1.2.1
+ // check parity of class 1A
+ unsigned sentParity = (~mTCHU.peekField(91, 3)) & 0x07;
+ //unsigned calcParity = mTCHD.head(50).parity(mTCHParity) & 0x07;
+ unsigned calcParity = mClass1A_d.parity(mTCHParity) & 0x07;
+
+ // 3.1.2.2
+ // Check the tail bits, too.
+ unsigned tail = mTCHU.peekField(185, 4);
+
+ OBJDCOUT("TCHFACCHL1Decoder::decodeTCH c[]=" << mC);
+ //OBJDCOUT("TCHFACCHL1Decoder::decodeTCH u[]=" << mTCHU);
+ OBJDCOUT("TCHFACCHL1Decoder::decodeTCH d[]=" << mTCHD);
+ OBJDCOUT("TCHFACCHL1Decoder::decodeTCH sentParity=" << sentParity
+ << " calcParity=" << calcParity << " tail=" << tail);
+ good = (sentParity == calcParity) && (tail == 0);
+ if (good) {
+ // Undo Um's importance-sorted bit ordering.
+ // See GSM 05.03 3.1 and Tablee 2.
+ BitVector payload = mVFrame.payload();
+ mTCHD.unmap(g610BitOrder, 260, payload);
+ mVFrame.pack(newFrame);
+ // Save a copy for bad frame processing.
+ memcpy(mPrevGoodFrame, newFrame, 33);
+ return true;
+ }
+ }
+
+ if (!good) {
+ // TODO -- Bad frame processing, GSM 06.11.
+ // For now, just repeat the last good frame.
+ // TODO -- Need to apply attenuation and randomization of grid positions.
+ memcpy(newFrame, mPrevGoodFrame, 33);
+ //d_gsm_file.write((char *)newFrame, 33);
+ }
+
+
+ // Good or bad, we must feed the speech channel.
+// mSpeechQ.write(newFrame);
+
+
+ return false;
+}
+
+// vim: ts=4 sw=4
diff --git a/gsm-receiver/src/lib/decoder/openbtsstuff/GSML1FEC.h b/gsm-receiver/src/lib/decoder/openbtsstuff/GSML1FEC.h
new file mode 100644
index 0000000..c367681
--- /dev/null
+++ b/gsm-receiver/src/lib/decoder/openbtsstuff/GSML1FEC.h
@@ -0,0 +1,146 @@
+/*
+* Copyright 2008 Free Software Foundation, Inc.
+*
+*
+* This program is free software: you can redistribute it and/or modify
+* it under the terms of the GNU General Public License as published by
+* the Free Software Foundation, either version 3 of the License, or
+* (at your option) any later version.
+*
+* This program is distributed in the hope that it will be useful,
+* but WITHOUT ANY WARRANTY; without even the implied warranty of
+* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+* GNU General Public License for more details.
+*
+* You should have received a copy of the GNU General Public License
+* along with this program. If not, see <http://www.gnu.org/licenses/>.
+*
+* This software is distributed under the terms of the GNU Public License.
+* See the COPYING file in the main directory for details.
+*/
+
+
+
+#ifndef GSML1FEC_H
+#define GSML1FEC_H
+
+//#include "Threads.h"
+#include "Assert.h"
+#include "BitVector.h"
+
+#include "GSMCommon.h"
+//#include "GSMTransfer.h"
+#include "GSMTDMA.h"
+#include "VocoderFrame.h"
+#include "RxBurst.h"
+//#include "GSM610Tables.h"
+#include <stdio.h>
+
+
+
+namespace GSM
+{
+
+
+//class SAPMux;
+ class L1FEC;
+ class L1Decoder;
+
+
+
+ /*
+ Naming convention for bit vectors follows GSM 05.03 Section 2.2.
+ d[k] data
+ u[k] data bits after first encoding step
+ c[k] data bits after second encoding step
+ i[B][k] interleaved data bits
+ e[B][k] bits in a burst
+ */
+
+
+
+
+ /** L1 decoder used for full rate TCH and FACCH -- mostly from GSM 05.03 3.1 and 4.2 */
+//: public XCCHL1Decoder
+ class TCHFACCHL1Decoder
+ {
+
+ protected:
+ SoftVector mI[8]; ///< deinterleaving history, 8 blocks instead of 4
+ SoftVector mC; ///< c[], as per GSM 05.03 2.2
+ BitVector mTCHU; ///< u[] (uncoded) in the spec
+ BitVector mTCHD; ///< d[] (data) in the spec
+ SoftVector mClass1_c; ///< the class 1 part of c[]
+ BitVector mClass1A_d; ///< the class 1A part of d[]
+ SoftVector mClass2_c; ///< the class 2 part of c[]
+ ViterbiR2O4 mVCoder;
+
+ VocoderFrame mVFrame; ///< unpacking buffer for vocoder frame
+ unsigned char mPrevGoodFrame[33]; ///< previous good frame.
+
+ Parity mTCHParity;
+ const TDMAMapping& mMapping; ///< multiplexing description
+
+// InterthreadQueue<unsigned char> mSpeechQ; ///< output queue for speech frames
+
+ static const unsigned mMaxQSize = 3;
+
+
+ public:
+
+ TCHFACCHL1Decoder( const TDMAMapping& wMapping );
+
+ ChannelType channelType() const {
+ return FACCHType;
+ }
+
+
+ /** TCH/FACCH has a special-case writeLowSide. */
+ void writeLowSide(const RxBurst& inBurst);
+
+ /**
+ Unlike other DCCHs, TCH/FACCH process burst calls
+ deinterleave, decode, handleGoodFrame.
+ */
+ bool processBurst( const RxBurst& );
+
+ /** Deinterleave i[] to c[]. */
+ void deinterleave(int blockOffset );
+
+ void replaceFACCH( int blockOffset );
+
+ /**
+ Decode a traffic frame from TCHI[] and enqueue it.
+ Return true if there's a good frame.
+ */
+ bool decodeTCH(bool stolen);
+
+ unsigned char * get_voice_frame(){
+ return mPrevGoodFrame;
+ }
+ /**
+ Receive a traffic frame.
+ Non-blocking. Returns NULL if queue is dry.
+ Caller is responsible for deleting the returned array.
+ */
+// unsigned char *recvTCH() { return mSpeechQ.read(0); }
+
+ /** Return count of internally-queued traffic frames. */
+// unsigned queueSize() const { return mSpeechQ.size(); }
+
+ };
+
+
+
+
+
+
+}; // namespace GSM
+
+
+
+
+
+#endif
+
+// vim: ts=4 sw=4
diff --git a/gsm-receiver/src/lib/decoder/openbtsstuff/GSMTDMA.cpp b/gsm-receiver/src/lib/decoder/openbtsstuff/GSMTDMA.cpp
new file mode 100644
index 0000000..27ebe0e
--- /dev/null
+++ b/gsm-receiver/src/lib/decoder/openbtsstuff/GSMTDMA.cpp
@@ -0,0 +1,337 @@
+/*
+* Copyright 2008 Free Software Foundation, Inc.
+*
+* This software is distributed under the terms of the GNU Public License.
+* See the COPYING file in the main directory for details.
+
+ This program is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
+
+*/
+
+
+#include "GSMTDMA.h"
+
+
+using namespace GSM;
+
+
+
+
+TDMAMapping::TDMAMapping(TypeAndOffset
+ wTypeAndOffset, bool wDownlink, bool wUplink, char wAllowedSlots, bool wC0Only,
+ unsigned wRepeatLength, unsigned wNumFrames, const unsigned *wFrameMapping)
+ :mTypeAndOffset(wTypeAndOffset),
+ mDownlink(wDownlink),mUplink(wUplink),mAllowedSlots(wAllowedSlots),mC0Only(wC0Only),
+ mRepeatLength(wRepeatLength),mNumFrames(wNumFrames),mFrameMapping(wFrameMapping)
+{
+ // Sanity check.
+ assert(mRepeatLength<=mMaxRepeatLength);
+
+ // Default, -1, means a non-occupied position.
+ for (unsigned i=0; i<mMaxRepeatLength; i++) mReverseMapping[i]=-1;
+
+ // Fill in the reverse map, precomputed for speed.
+ for (unsigned i=0; i<mNumFrames; i++) {
+ unsigned mapping = mFrameMapping[i];
+ assert(mapping<mRepeatLength);
+ mReverseMapping[mapping] = i;
+ }
+}
+
+
+
+
+
+/** A macro to save some typing when we set up TDMA maps. */
+#define MAKE_TDMA_MAPPING(NAME,TYPEANDOFFSET,DOWNLINK,UPLINK,ALLOWEDSLOTS,C0ONLY,REPEAT) \
+ const TDMAMapping GSM::g##NAME##Mapping(TYPEANDOFFSET,DOWNLINK,UPLINK,ALLOWEDSLOTS,C0ONLY, \
+ REPEAT,sizeof(NAME##Frames)/sizeof(unsigned),NAME##Frames)
+
+const unsigned LoopbackTestFullFrames[] = {0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37,38,39,40,41,42,43,44,45,46,47};
+MAKE_TDMA_MAPPING(LoopbackTestFull,TDMA_MISC,true,true,0xff,false,51);
+
+const unsigned FCCHFrames[] = {0,10,20,30,40};
+MAKE_TDMA_MAPPING(FCCH,TDMA_BEACON,true,false,0x01,true,51);
+
+const unsigned SCHFrames[] = {1,11,21,31,41};
+MAKE_TDMA_MAPPING(SCH,TDMA_BEACON,true,false,0x01,true,51);
+
+const unsigned BCCHFrames[] = {2,3,4,5};
+MAKE_TDMA_MAPPING(BCCH,TDMA_BEACON,true,false,0x55,true,51);
+
+// Note that we removed frames for the SDCCH components of the Combination-V C0T0.
+const unsigned RACHC5Frames[] = {4,5,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,45,46};
+MAKE_TDMA_MAPPING(RACHC5,TDMA_BEACON,false,true,0x55,true,51);
+
+// CCCH 0-2 are used in C-IV and C-V. The others are used in C-IV only.
+
+const unsigned CCCH_0Frames[] = {6,7,8,9};
+MAKE_TDMA_MAPPING(CCCH_0,TDMA_BEACON,true,false,0x55,true,51);
+
+const unsigned CCCH_1Frames[] = {12,13,14,15};
+MAKE_TDMA_MAPPING(CCCH_1,TDMA_BEACON,true,false,0x55,true,51);
+
+const unsigned CCCH_2Frames[] = {16,17,18,19};
+MAKE_TDMA_MAPPING(CCCH_2,TDMA_BEACON,true,false,0x55,true,51);
+
+const unsigned CCCH_3Frames[] = {22,23,24,25};
+MAKE_TDMA_MAPPING(CCCH_3,TDMA_BEACON,true,false,0x55,true,51);
+
+// TODO -- Other CCCH subchannels 4-8 for support of C-IV.
+
+const unsigned SDCCH_4_0DFrames[] = {22,23,24,25};
+MAKE_TDMA_MAPPING(SDCCH_4_0D,SDCCH_4_0,true,false,0x01,true,51);
+
+const unsigned SDCCH_4_0UFrames[] = {37,38,39,40};
+MAKE_TDMA_MAPPING(SDCCH_4_0U,SDCCH_4_0,false,true,0x01,true,51);
+
+const unsigned SDCCH_4_1DFrames[] = {26,27,28,29};
+MAKE_TDMA_MAPPING(SDCCH_4_1D,SDCCH_4_1,true,false,0x01,true,51);
+
+const unsigned SDCCH_4_1UFrames[] = {41,42,43,44};
+MAKE_TDMA_MAPPING(SDCCH_4_1U,SDCCH_4_1,false,true,0x01,true,51);
+
+const unsigned SDCCH_4_2DFrames[] = {32,33,34,35};
+MAKE_TDMA_MAPPING(SDCCH_4_2D,SDCCH_4_2,true,false,0x01,true,51);
+
+const unsigned SDCCH_4_2UFrames[] = {47,48,49,50};
+MAKE_TDMA_MAPPING(SDCCH_4_2U,SDCCH_4_2,false,true,0x01,true,51);
+
+const unsigned SDCCH_4_3DFrames[] = {36,37,38,39};
+MAKE_TDMA_MAPPING(SDCCH_4_3D,SDCCH_4_3,true,false,0x01,true,51);
+
+const unsigned SDCCH_4_3UFrames[] = {0,1,2,3};
+MAKE_TDMA_MAPPING(SDCCH_4_3U,SDCCH_4_3,false,true,0x01,true,51);
+
+
+const unsigned SACCH_C4_0DFrames[] = {42,43,44,45};
+MAKE_TDMA_MAPPING(SACCH_C4_0D,SDCCH_4_0,true,false,0x01,true,102);
+
+const unsigned SACCH_C4_0UFrames[] = {57,58,59,60};
+MAKE_TDMA_MAPPING(SACCH_C4_0U,SDCCH_4_0,false,true,0x01,true,102);
+
+const unsigned SACCH_C4_1DFrames[] = {46,47,48,49};
+MAKE_TDMA_MAPPING(SACCH_C4_1D,SDCCH_4_1,true,false,0x01,true,102);
+
+const unsigned SACCH_C4_1UFrames[] = {61,62,63,64};
+MAKE_TDMA_MAPPING(SACCH_C4_1U,SDCCH_4_1,false,true,0x01,true,102);
+
+const unsigned SACCH_C4_2DFrames[] = {93,94,95,96};
+MAKE_TDMA_MAPPING(SACCH_C4_2D,SDCCH_4_2,true,false,0x01,true,102);
+
+const unsigned SACCH_C4_2UFrames[] = {6,7,8,9};
+MAKE_TDMA_MAPPING(SACCH_C4_2U,SDCCH_4_2,false,true,0x01,true,102);
+
+const unsigned SACCH_C4_3DFrames[] = {97,98,99,100};
+MAKE_TDMA_MAPPING(SACCH_C4_3D,SDCCH_4_3,true,false,0x01,true,102);
+
+const unsigned SACCH_C4_3UFrames[] = {10,11,12,13};
+MAKE_TDMA_MAPPING(SACCH_C4_3U,SDCCH_4_3,false,true,0x01,true,102);
+
+
+const unsigned SDCCH_8_0DFrames[] = {0,1,2,3};
+MAKE_TDMA_MAPPING(SDCCH_8_0D,SDCCH_8_0,true,false,0xFF,true,51);
+
+const unsigned SDCCH_8_0UFrames[] = {15,16,17,18};
+MAKE_TDMA_MAPPING(SDCCH_8_0U,SDCCH_8_0,false,true,0xFF,true,51);
+
+const unsigned SDCCH_8_1DFrames[] = {4,5,6,7};
+MAKE_TDMA_MAPPING(SDCCH_8_1D,SDCCH_8_1,true,false,0xFF,true,51);
+
+const unsigned SDCCH_8_1UFrames[] = {19,20,21,22};
+MAKE_TDMA_MAPPING(SDCCH_8_1U,SDCCH_8_1,false,true,0xFF,true,51);
+
+const unsigned SDCCH_8_2DFrames[] = {8,9,10,11};
+MAKE_TDMA_MAPPING(SDCCH_8_2D,SDCCH_8_2,true,false,0xFF,true,51);
+
+const unsigned SDCCH_8_2UFrames[] = {23,24,25,26};
+MAKE_TDMA_MAPPING(SDCCH_8_2U,SDCCH_8_2,false,true,0xFF,true,51);
+
+const unsigned SDCCH_8_3DFrames[] = {12,13,14,15};
+MAKE_TDMA_MAPPING(SDCCH_8_3D,SDCCH_8_3,true,false,0xFF,true,51);
+
+const unsigned SDCCH_8_3UFrames[] = {27,28,29,30};
+MAKE_TDMA_MAPPING(SDCCH_8_3U,SDCCH_8_3,false,true,0xFF,true,51);
+
+const unsigned SDCCH_8_4DFrames[] = {16,17,18,19};
+MAKE_TDMA_MAPPING(SDCCH_8_4D,SDCCH_8_4,true,false,0xFF,true,51);
+
+const unsigned SDCCH_8_4UFrames[] = {31,32,33,34};
+MAKE_TDMA_MAPPING(SDCCH_8_4U,SDCCH_8_4,false,true,0xFF,true,51);
+
+const unsigned SDCCH_8_5DFrames[] = {20,21,22,23};
+MAKE_TDMA_MAPPING(SDCCH_8_5D,SDCCH_8_5,true,false,0xFF,true,51);
+
+const unsigned SDCCH_8_5UFrames[] = {35,36,37,38};
+MAKE_TDMA_MAPPING(SDCCH_8_5U,SDCCH_8_5,false,true,0xFF,true,51);
+
+const unsigned SDCCH_8_6DFrames[] = {24,25,26,27};
+MAKE_TDMA_MAPPING(SDCCH_8_6D,SDCCH_8_6,true,false,0xFF,true,51);
+
+const unsigned SDCCH_8_6UFrames[] = {39,40,41,42};
+MAKE_TDMA_MAPPING(SDCCH_8_6U,SDCCH_8_6,false,true,0xFF,true,51);
+
+const unsigned SDCCH_8_7DFrames[] = {28,29,30,31};
+MAKE_TDMA_MAPPING(SDCCH_8_7D,SDCCH_8_7,true,false,0xFF,true,51);
+
+const unsigned SDCCH_8_7UFrames[] = {43,44,45,46};
+MAKE_TDMA_MAPPING(SDCCH_8_7U,SDCCH_8_7,false,true,0xFF,true,51);
+
+
+const unsigned SACCH_C8_0DFrames[] = {32,33,34,35};
+MAKE_TDMA_MAPPING(SACCH_C8_0D,SDCCH_8_0,true,false,0xFF,true,102);
+
+const unsigned SACCH_C8_0UFrames[] = {47,48,49,50};
+MAKE_TDMA_MAPPING(SACCH_C8_0U,SDCCH_8_0,false,true,0xFF,true,102);
+
+const unsigned SACCH_C8_1DFrames[] = {36,37,38,39};
+MAKE_TDMA_MAPPING(SACCH_C8_1D,SDCCH_8_1,true,false,0xFF,true,102);
+
+const unsigned SACCH_C8_1UFrames[] = {51,52,53,54};
+MAKE_TDMA_MAPPING(SACCH_C8_1U,SDCCH_8_1,false,true,0xFF,true,102);
+
+const unsigned SACCH_C8_2DFrames[] = {40,41,42,43};
+MAKE_TDMA_MAPPING(SACCH_C8_2D,SDCCH_8_2,true,false,0xFF,true,102);
+
+const unsigned SACCH_C8_2UFrames[] = {55,56,57,58};
+MAKE_TDMA_MAPPING(SACCH_C8_2U,SDCCH_8_2,false,true,0xFF,true,102);
+
+const unsigned SACCH_C8_3DFrames[] = {44,45,46,47};
+MAKE_TDMA_MAPPING(SACCH_C8_3D,SDCCH_8_3,true,false,0xFF,true,102);
+
+const unsigned SACCH_C8_3UFrames[] = {59,60,61,62};
+MAKE_TDMA_MAPPING(SACCH_C8_3U,SDCCH_8_3,false,true,0xFF,true,102);
+
+const unsigned SACCH_C8_4DFrames[] = {82,84,85,86};
+MAKE_TDMA_MAPPING(SACCH_C8_4D,SDCCH_8_4,true,false,0xFF,true,102);
+
+const unsigned SACCH_C8_4UFrames[] = {98,99,100,101};
+MAKE_TDMA_MAPPING(SACCH_C8_4U,SDCCH_8_4,false,true,0xFF,true,102);
+
+const unsigned SACCH_C8_5DFrames[] = {87,88,89,90};
+MAKE_TDMA_MAPPING(SACCH_C8_5D,SDCCH_8_5,true,false,0xFF,true,102);
+
+const unsigned SACCH_C8_5UFrames[] = {0,1,2,3};
+MAKE_TDMA_MAPPING(SACCH_C8_5U,SDCCH_8_5,false,true,0xFF,true,102);
+
+const unsigned SACCH_C8_6DFrames[] = {91,92,93,94};
+MAKE_TDMA_MAPPING(SACCH_C8_6D,SDCCH_8_6,true,false,0xFF,true,102);
+
+const unsigned SACCH_C8_6UFrames[] = {4,5,6,7};
+MAKE_TDMA_MAPPING(SACCH_C8_6U,SDCCH_8_6,false,true,0xFF,true,102);
+
+const unsigned SACCH_C8_7DFrames[] = {95,96,97,98};
+MAKE_TDMA_MAPPING(SACCH_C8_7D,SDCCH_8_7,true,false,0xFF,true,102);
+
+const unsigned SACCH_C8_7UFrames[] = {8,9,10,11};
+MAKE_TDMA_MAPPING(SACCH_C8_7U,SDCCH_8_7,false,true,0xFF,true,102);
+
+
+
+const unsigned SACCH_TF_T0Frames[] = {12,38,64,90};
+MAKE_TDMA_MAPPING(SACCH_TF_T0,TCHF_0,true,true,0x01,true,104);
+
+const unsigned SACCH_TF_T1Frames[] = {25,51,77,103};
+MAKE_TDMA_MAPPING(SACCH_TF_T1,TCHF_0,true,true,0x02,true,104);
+
+const unsigned SACCH_TF_T2Frames[] = {38,64,90,12};
+MAKE_TDMA_MAPPING(SACCH_TF_T2,TCHF_0,true,true,0x04,true,104);
+
+const unsigned SACCH_TF_T3Frames[] = {51,77,103,25};
+MAKE_TDMA_MAPPING(SACCH_TF_T3,TCHF_0,true,true,0x08,true,104);
+
+const unsigned SACCH_TF_T4Frames[] = {64,90,12,38};
+MAKE_TDMA_MAPPING(SACCH_TF_T4,TCHF_0,true,true,0x10,true,104);
+
+const unsigned SACCH_TF_T5Frames[] = {77,103,25,51};
+MAKE_TDMA_MAPPING(SACCH_TF_T5,TCHF_0,true,true,0x20,true,104);
+
+const unsigned SACCH_TF_T6Frames[] = {90,12,38,64};
+MAKE_TDMA_MAPPING(SACCH_TF_T6,TCHF_0,true,true,0x40,true,104);
+
+const unsigned SACCH_TF_T7Frames[] = {103,25,51,77};
+MAKE_TDMA_MAPPING(SACCH_TF_T7,TCHF_0,true,true,0x80,true,104);
+
+const unsigned FACCH_TCHFFrames[] = {0,1,2,3,4,5,6,7,8,9,10,11,13,14,15,16,17,18,19,20,21,22,23,24};
+MAKE_TDMA_MAPPING(FACCH_TCHF,TCHF_0,true,true,0xff,true,26);
+
+
+
+
+
+
+
+const MappingPair GSM::gSDCCH_4_0Pair(gSDCCH_4_0DMapping,gSDCCH_4_0UMapping);
+const MappingPair GSM::gSDCCH_4_1Pair(gSDCCH_4_1DMapping,gSDCCH_4_1UMapping);
+const MappingPair GSM::gSDCCH_4_2Pair(gSDCCH_4_2DMapping,gSDCCH_4_2UMapping);
+const MappingPair GSM::gSDCCH_4_3Pair(gSDCCH_4_3DMapping,gSDCCH_4_3UMapping);
+const MappingPair GSM::gSDCCH_8_0Pair(gSDCCH_8_0DMapping,gSDCCH_8_0UMapping);
+const MappingPair GSM::gSDCCH_8_1Pair(gSDCCH_8_1DMapping,gSDCCH_8_1UMapping);
+const MappingPair GSM::gSDCCH_8_2Pair(gSDCCH_8_2DMapping,gSDCCH_8_2UMapping);
+const MappingPair GSM::gSDCCH_8_3Pair(gSDCCH_8_3DMapping,gSDCCH_8_3UMapping);
+const MappingPair GSM::gSDCCH_8_4Pair(gSDCCH_8_4DMapping,gSDCCH_8_4UMapping);
+const MappingPair GSM::gSDCCH_8_5Pair(gSDCCH_8_5DMapping,gSDCCH_8_5UMapping);
+const MappingPair GSM::gSDCCH_8_6Pair(gSDCCH_8_6DMapping,gSDCCH_8_6UMapping);
+const MappingPair GSM::gSDCCH_8_7Pair(gSDCCH_8_7DMapping,gSDCCH_8_7UMapping);
+
+const MappingPair GSM::gSACCH_C4_0Pair(gSACCH_C4_0DMapping,gSACCH_C4_0UMapping);
+const MappingPair GSM::gSACCH_C4_1Pair(gSACCH_C4_1DMapping,gSACCH_C4_1UMapping);
+const MappingPair GSM::gSACCH_C4_2Pair(gSACCH_C4_2DMapping,gSACCH_C4_2UMapping);
+const MappingPair GSM::gSACCH_C4_3Pair(gSACCH_C4_3DMapping,gSACCH_C4_3UMapping);
+const MappingPair GSM::gSACCH_C8_0Pair(gSACCH_C8_0DMapping,gSACCH_C8_0UMapping);
+const MappingPair GSM::gSACCH_C8_1Pair(gSACCH_C8_1DMapping,gSACCH_C8_1UMapping);
+const MappingPair GSM::gSACCH_C8_2Pair(gSACCH_C8_2DMapping,gSACCH_C8_2UMapping);
+const MappingPair GSM::gSACCH_C8_3Pair(gSACCH_C8_3DMapping,gSACCH_C8_3UMapping);
+const MappingPair GSM::gSACCH_C8_4Pair(gSACCH_C8_4DMapping,gSACCH_C8_4UMapping);
+const MappingPair GSM::gSACCH_C8_5Pair(gSACCH_C8_5DMapping,gSACCH_C8_5UMapping);
+const MappingPair GSM::gSACCH_C8_6Pair(gSACCH_C8_6DMapping,gSACCH_C8_6UMapping);
+const MappingPair GSM::gSACCH_C8_7Pair(gSACCH_C8_7DMapping,gSACCH_C8_7UMapping);
+
+const MappingPair GSM::gFACCH_TCHFPair(gFACCH_TCHFMapping,gFACCH_TCHFMapping);
+
+const MappingPair GSM::gSACCH_FT_T0Pair(gSACCH_TF_T0Mapping, gSACCH_TF_T0Mapping);
+const MappingPair GSM::gSACCH_FT_T1Pair(gSACCH_TF_T1Mapping, gSACCH_TF_T1Mapping);
+const MappingPair GSM::gSACCH_FT_T2Pair(gSACCH_TF_T2Mapping, gSACCH_TF_T2Mapping);
+const MappingPair GSM::gSACCH_FT_T3Pair(gSACCH_TF_T3Mapping, gSACCH_TF_T3Mapping);
+const MappingPair GSM::gSACCH_FT_T4Pair(gSACCH_TF_T4Mapping, gSACCH_TF_T4Mapping);
+const MappingPair GSM::gSACCH_FT_T5Pair(gSACCH_TF_T5Mapping, gSACCH_TF_T5Mapping);
+const MappingPair GSM::gSACCH_FT_T6Pair(gSACCH_TF_T6Mapping, gSACCH_TF_T6Mapping);
+const MappingPair GSM::gSACCH_FT_T7Pair(gSACCH_TF_T7Mapping, gSACCH_TF_T7Mapping);
+
+
+
+const CompleteMapping GSM::gSDCCH_4_0(gSDCCH_4_0Pair,gSACCH_C4_0Pair);
+const CompleteMapping GSM::gSDCCH_4_1(gSDCCH_4_1Pair,gSACCH_C4_1Pair);
+const CompleteMapping GSM::gSDCCH_4_2(gSDCCH_4_2Pair,gSACCH_C4_2Pair);
+const CompleteMapping GSM::gSDCCH_4_3(gSDCCH_4_3Pair,gSACCH_C4_3Pair);
+const CompleteMapping GSM::gSDCCH_8_0(gSDCCH_8_0Pair,gSACCH_C8_0Pair);
+const CompleteMapping GSM::gSDCCH_8_1(gSDCCH_8_1Pair,gSACCH_C8_1Pair);
+const CompleteMapping GSM::gSDCCH_8_2(gSDCCH_8_2Pair,gSACCH_C8_2Pair);
+const CompleteMapping GSM::gSDCCH_8_3(gSDCCH_8_3Pair,gSACCH_C8_3Pair);
+const CompleteMapping GSM::gSDCCH_8_4(gSDCCH_8_4Pair,gSACCH_C8_4Pair);
+const CompleteMapping GSM::gSDCCH_8_5(gSDCCH_8_5Pair,gSACCH_C8_5Pair);
+const CompleteMapping GSM::gSDCCH_8_6(gSDCCH_8_6Pair,gSACCH_C8_6Pair);
+const CompleteMapping GSM::gSDCCH_8_7(gSDCCH_8_7Pair,gSACCH_C8_7Pair);
+
+const CompleteMapping GSM::gTCHF_T0(gFACCH_TCHFPair,gSACCH_FT_T0Pair);
+const CompleteMapping GSM::gTCHF_T1(gFACCH_TCHFPair,gSACCH_FT_T1Pair);
+const CompleteMapping GSM::gTCHF_T2(gFACCH_TCHFPair,gSACCH_FT_T2Pair);
+const CompleteMapping GSM::gTCHF_T3(gFACCH_TCHFPair,gSACCH_FT_T3Pair);
+const CompleteMapping GSM::gTCHF_T4(gFACCH_TCHFPair,gSACCH_FT_T4Pair);
+const CompleteMapping GSM::gTCHF_T5(gFACCH_TCHFPair,gSACCH_FT_T5Pair);
+const CompleteMapping GSM::gTCHF_T6(gFACCH_TCHFPair,gSACCH_FT_T6Pair);
+const CompleteMapping GSM::gTCHF_T7(gFACCH_TCHFPair,gSACCH_FT_T7Pair);
+
+
+
diff --git a/gsm-receiver/src/lib/decoder/openbtsstuff/GSMTDMA.h b/gsm-receiver/src/lib/decoder/openbtsstuff/GSMTDMA.h
new file mode 100644
index 0000000..3b55b94
--- /dev/null
+++ b/gsm-receiver/src/lib/decoder/openbtsstuff/GSMTDMA.h
@@ -0,0 +1,358 @@
+/**@file Common-use GSM declarations, most from the GSM 04.xx and 05.xx series. */
+/*
+* Copyright 2008 Free Software Foundation, Inc.
+*
+* This software is distributed under the terms of the GNU Public License.
+* See the COPYING file in the main directory for details.
+
+ This program is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
+
+*/
+
+
+
+#ifndef GSMTDMA_H
+#define GSMTDMA_H
+
+
+#include "GSMCommon.h"
+
+
+namespace GSM {
+
+
+/**
+ A description of a channel's multiplexing pattern.
+ From GSM 05.02 Clause 7.
+ This object encodes a line from tables 1-4 in the spec.
+ The columns of interest in this encoding are:
+ - 1, Channel Designation
+ - 2, Subchannel
+ - 3, Direction
+ - 4, Allowable Time Slot Assignments
+ - 5, Allowable RF Channel Assignments
+ - 7, Repeat Length in TDMA Frames
+ - 8, Interleaved Block TDMA Frame Mapping
+
+ Col 6, Burst Type, is implied by 1 & 2 and encoded into the transcevier source code.
+*/
+class TDMAMapping {
+
+ public:
+
+ /// The longest "repeat length" of any channel we support is 104 for the SACCH/TF.
+ static const unsigned mMaxRepeatLength = 104;
+
+ private:
+
+ TypeAndOffset mTypeAndOffset; ///< col 1, 2, encoded as per GSM 04.08 10.5.2.5
+ bool mDownlink; ///< col 3, true for downlink channels
+ bool mUplink; ///< col 3, true for uplink channels
+ char mAllowedSlots; ///< col 4, an 8-bit mask
+ bool mC0Only; ///< col 5, true if channel is limited to C0
+ unsigned mRepeatLength; ///< col 7
+ unsigned mNumFrames; ///< number of occupied frames in col 8
+ const unsigned *mFrameMapping; ///< col 8
+ unsigned mReverseMapping[mMaxRepeatLength]; ///< index reversal of mapping, -1 means unused
+
+
+ public:
+
+
+ /**
+ Construct a TDMAMapping, encoding one line of GSM 05.02 Clause 7 Tables 1-4.
+ @param wTypeAndOffset Encoding of "Channel designnation". See GSM 04.08 10.5.2.5.
+ @param wDownlink True for downlink and bidirectional hannels
+ @param wUplink True for uplink and bidirectional channels
+ @param wRepeatLength "Repeat Length in TDMA Frames"
+ @param wNumFrames Number of occupied TDMA frames in frame mapping.
+ @param wFrameMapping "Interleaved Block TDMA Frame Mapping" -- MUST PERSIST!!
+ */
+ TDMAMapping(TypeAndOffset wTypeAndOffset,
+ bool wDownlink, bool wUplink, char wAllowedSlots, bool wC0Only,
+ unsigned wRepeatLength, unsigned wNumFrames, const unsigned *wFrameMapping);
+
+ /** Given a count of frames sent, return the corresponding frame number. */
+ unsigned frameMapping(unsigned count) const
+ { return mFrameMapping[count % mNumFrames]; }
+
+ /** Given a frame number, return the corresponding count, modulo patten length. */
+ int reverseMapping(unsigned FN) const
+ { return mReverseMapping[FN % mRepeatLength]; }
+
+ /**@name Simple accessors. */
+ //@{
+ unsigned numFrames() const { return mNumFrames; }
+
+ unsigned repeatLength() const { return mRepeatLength; }
+
+ TypeAndOffset typeAndOffset() const { return mTypeAndOffset; }
+
+ bool uplink() const { return mUplink; }
+
+ bool downlink() const { return mDownlink; }
+
+ bool C0Only() const { return mC0Only; }
+ //@}
+
+ ///< Return true if this channel is allowed on this slot.
+ bool allowedSlot(unsigned slot) const
+ { return mAllowedSlots & (1<<slot); }
+};
+
+
+
+/**@name Mux parameters for standard channels, from GSM 05.03 Clause 7 Tables 1-4. */
+//@{
+/**@name Beacon channels */
+//@{
+extern const TDMAMapping gFCCHMapping; ///< GSM 05.02 Clause 7 Table 3 Line 1 B0-B4
+extern const TDMAMapping gSCHMapping; ///< GSM 05.02 Clause 7 Table 3 Line 2 B0-B4
+extern const TDMAMapping gBCCHMapping; ///< GSM 05.02 Clause 7 Table 3 Line 3
+/// GSM 05.02 Clause 7 Table 3 Line 7 B0-B50, excluding C-V SDCCH parts (SDCCH/4 and SCCH/C4)
+extern const TDMAMapping gRACHC5Mapping;
+extern const TDMAMapping gCCCH_0Mapping; ///< GSM 05.02 Clause 7 Table 3 Line 5 B0
+extern const TDMAMapping gCCCH_1Mapping; ///< GSM 05.02 Clause 7 Table 3 Line 5 B1
+extern const TDMAMapping gCCCH_2Mapping; ///< GSM 05.02 Clause 7 Table 3 Line 5 B2
+extern const TDMAMapping gCCCH_3Mapping; ///< GSM 05.02 Clause 7 Table 3 Line 5 B3
+extern const TDMAMapping gCCCH_4Mapping; ///< GSM 05.02 Clause 7 Table 3 Line 5 B4
+extern const TDMAMapping gCCCH_5Mapping; ///< GSM 05.02 Clause 7 Table 3 Line 5 B5
+extern const TDMAMapping gCCCH_6Mapping; ///< GSM 05.02 Clause 7 Table 3 Line 5 B6
+extern const TDMAMapping gCCCH_7Mapping; ///< GSM 05.02 Clause 7 Table 3 Line 5 B7
+extern const TDMAMapping gCCCH_8Mapping; ///< GSM 05.02 Clause 7 Table 3 Line 5 B8
+//@}
+/**@name SDCCH */
+//@{
+///@name For Combination V
+//@{
+extern const TDMAMapping gSDCCH_4_0DMapping; ///< GSM 05.02 Clause 7 Table 3 Line 10/0D
+extern const TDMAMapping gSDCCH_4_0UMapping; ///< GSM 05.02 Clause 7 Table 3 Line 10/0U
+extern const TDMAMapping gSDCCH_4_1DMapping;
+extern const TDMAMapping gSDCCH_4_1UMapping;
+extern const TDMAMapping gSDCCH_4_2DMapping;
+extern const TDMAMapping gSDCCH_4_2UMapping;
+extern const TDMAMapping gSDCCH_4_3DMapping;
+extern const TDMAMapping gSDCCH_4_3UMapping;
+//@}
+///@name For Combination VII
+//@{
+extern const TDMAMapping gSDCCH_8_0DMapping;
+extern const TDMAMapping gSDCCH_8_0UMapping;
+extern const TDMAMapping gSDCCH_8_1DMapping;
+extern const TDMAMapping gSDCCH_8_1UMapping;
+extern const TDMAMapping gSDCCH_8_2DMapping;
+extern const TDMAMapping gSDCCH_8_2UMapping;
+extern const TDMAMapping gSDCCH_8_3DMapping;
+extern const TDMAMapping gSDCCH_8_3UMapping;
+extern const TDMAMapping gSDCCH_8_4DMapping;
+extern const TDMAMapping gSDCCH_8_4UMapping;
+extern const TDMAMapping gSDCCH_8_5DMapping;
+extern const TDMAMapping gSDCCH_8_5UMapping;
+extern const TDMAMapping gSDCCH_8_6DMapping;
+extern const TDMAMapping gSDCCH_8_6UMapping;
+extern const TDMAMapping gSDCCH_8_7DMapping;
+extern const TDMAMapping gSDCCH_8_7UMapping;
+//@}
+//@}
+/**@name SACCH */
+//@{
+/**name SACCH for SDCCH */
+//@{
+///@name For Combination V
+//@{
+extern const TDMAMapping gSACCH_C4_0DMapping;
+extern const TDMAMapping gSACCH_C4_0UMapping;
+extern const TDMAMapping gSACCH_C4_1DMapping;
+extern const TDMAMapping gSACCH_C4_1UMapping;
+extern const TDMAMapping gSACCH_C4_2DMapping;
+extern const TDMAMapping gSACCH_C4_2UMapping;
+extern const TDMAMapping gSACCH_C4_3DMapping;
+extern const TDMAMapping gSACCH_C4_3UMapping;
+//@}
+///@name For Combination VII
+//@{
+extern const TDMAMapping gSACCH_C8_0DMapping;
+extern const TDMAMapping gSACCH_C8_0UMapping;
+extern const TDMAMapping gSACCH_C8_1DMapping;
+extern const TDMAMapping gSACCH_C8_1UMapping;
+extern const TDMAMapping gSACCH_C8_2DMapping;
+extern const TDMAMapping gSACCH_C8_2UMapping;
+extern const TDMAMapping gSACCH_C8_3DMapping;
+extern const TDMAMapping gSACCH_C8_3UMapping;
+extern const TDMAMapping gSACCH_C8_4DMapping;
+extern const TDMAMapping gSACCH_C8_4UMapping;
+extern const TDMAMapping gSACCH_C8_5DMapping;
+extern const TDMAMapping gSACCH_C8_5UMapping;
+extern const TDMAMapping gSACCH_C8_6DMapping;
+extern const TDMAMapping gSACCH_C8_6UMapping;
+extern const TDMAMapping gSACCH_C8_7DMapping;
+extern const TDMAMapping gSACCH_C8_7UMapping;
+//@}
+//@}
+/**@name SACCH for TCH/F on different timeslots. */
+//@{
+extern const TDMAMapping gSACCH_TF_T0Mapping;
+extern const TDMAMapping gSACCH_TF_T1Mapping;
+extern const TDMAMapping gSACCH_TF_T2Mapping;
+extern const TDMAMapping gSACCH_TF_T3Mapping;
+extern const TDMAMapping gSACCH_TF_T4Mapping;
+extern const TDMAMapping gSACCH_TF_T5Mapping;
+extern const TDMAMapping gSACCH_TF_T6Mapping;
+extern const TDMAMapping gSACCH_TF_T7Mapping;
+//@}
+//@}
+/**name FACCH+TCH/F placement */
+//@{
+extern const TDMAMapping gFACCH_TCHFMapping;
+//@}
+/**@name Test fixtures. */
+extern const TDMAMapping gLoopbackTestFullMapping;
+extern const TDMAMapping gLoopbackTestHalfUMapping;
+extern const TDMAMapping gLoopbackTestHalfDMapping;
+//@}
+
+
+/** Combined uplink/downlink information. */
+class MappingPair {
+
+ private:
+
+ const TDMAMapping& mDownlink;
+ const TDMAMapping& mUplink;
+
+ public:
+
+ MappingPair(const TDMAMapping& wDownlink, const TDMAMapping& wUplink)
+ :mDownlink(wDownlink), mUplink(wUplink)
+ {}
+
+ MappingPair(const TDMAMapping& wMapping)
+ :mDownlink(wMapping), mUplink(wMapping)
+ {}
+
+ const TDMAMapping& downlink() const { return mDownlink; }
+ const TDMAMapping& uplink() const { return mUplink; }
+
+};
+
+
+/**@name Common placement pairs. */
+//@{
+/**@ SDCCH placement pairs. */
+//@{
+extern const MappingPair gSDCCH_4_0Pair;
+extern const MappingPair gSDCCH_4_1Pair;
+extern const MappingPair gSDCCH_4_2Pair;
+extern const MappingPair gSDCCH_4_3Pair;
+extern const MappingPair gSDCCH_8_0Pair;
+extern const MappingPair gSDCCH_8_1Pair;
+extern const MappingPair gSDCCH_8_2Pair;
+extern const MappingPair gSDCCH_8_3Pair;
+extern const MappingPair gSDCCH_8_4Pair;
+extern const MappingPair gSDCCH_8_5Pair;
+extern const MappingPair gSDCCH_8_6Pair;
+extern const MappingPair gSDCCH_8_7Pair;
+//@}
+/**@ SACCH-for-SDCCH placement pairs. */
+//@{
+extern const MappingPair gSACCH_C4_0Pair;
+extern const MappingPair gSACCH_C4_1Pair;
+extern const MappingPair gSACCH_C4_2Pair;
+extern const MappingPair gSACCH_C4_3Pair;
+extern const MappingPair gSACCH_C8_0Pair;
+extern const MappingPair gSACCH_C8_1Pair;
+extern const MappingPair gSACCH_C8_2Pair;
+extern const MappingPair gSACCH_C8_3Pair;
+extern const MappingPair gSACCH_C8_4Pair;
+extern const MappingPair gSACCH_C8_5Pair;
+extern const MappingPair gSACCH_C8_6Pair;
+extern const MappingPair gSACCH_C8_7Pair;
+//@}
+/**@name Traffic channels. */
+//@{
+extern const MappingPair gFACCH_TCHFPair;
+extern const MappingPair gSACCH_FT_T0Pair;
+extern const MappingPair gSACCH_FT_T1Pair;
+extern const MappingPair gSACCH_FT_T2Pair;
+extern const MappingPair gSACCH_FT_T3Pair;
+extern const MappingPair gSACCH_FT_T4Pair;
+extern const MappingPair gSACCH_FT_T5Pair;
+extern const MappingPair gSACCH_FT_T6Pair;
+extern const MappingPair gSACCH_FT_T7Pair;
+//@}
+//@}
+
+
+
+/** A CompleteMapping includes uplink, downlink and the SACCH. */
+class CompleteMapping {
+
+ private:
+
+ const MappingPair& mLCH;
+ const MappingPair& mSACCH;
+
+ public:
+
+ CompleteMapping(const MappingPair& wLCH, const MappingPair& wSACCH)
+ :mLCH(wLCH), mSACCH(wSACCH)
+ {}
+
+ const MappingPair& LCH() const { return mLCH; }
+ const MappingPair& SACCH() const { return mSACCH; }
+
+};
+
+
+
+/**@name Complete placements for common channel types. */
+//@{
+/**@name SDCCH/4 */
+//@{
+extern const CompleteMapping gSDCCH_4_0;
+extern const CompleteMapping gSDCCH_4_1;
+extern const CompleteMapping gSDCCH_4_2;
+extern const CompleteMapping gSDCCH_4_3;
+//@}
+/**@name SDCCH/8 */
+//@{
+extern const CompleteMapping gSDCCH_8_0;
+extern const CompleteMapping gSDCCH_8_1;
+extern const CompleteMapping gSDCCH_8_2;
+extern const CompleteMapping gSDCCH_8_3;
+extern const CompleteMapping gSDCCH_8_4;
+extern const CompleteMapping gSDCCH_8_5;
+extern const CompleteMapping gSDCCH_8_6;
+extern const CompleteMapping gSDCCH_8_7;
+//@}
+/**@name TCH/F on different slots. */
+//@{
+extern const CompleteMapping gTCHF_T0;
+extern const CompleteMapping gTCHF_T1;
+extern const CompleteMapping gTCHF_T2;
+extern const CompleteMapping gTCHF_T3;
+extern const CompleteMapping gTCHF_T4;
+extern const CompleteMapping gTCHF_T5;
+extern const CompleteMapping gTCHF_T6;
+extern const CompleteMapping gTCHF_T7;
+//@}
+//@}
+
+
+}; // namespace GSM
+
+
+#endif
+
+// vim: ts=4 sw=4
diff --git a/gsm-receiver/src/lib/decoder/openbtsstuff/Makefile.am b/gsm-receiver/src/lib/decoder/openbtsstuff/Makefile.am
new file mode 100644
index 0000000..5a14202
--- /dev/null
+++ b/gsm-receiver/src/lib/decoder/openbtsstuff/Makefile.am
@@ -0,0 +1,49 @@
+#
+# Copyright 2001,2002,2004,2005,2006,2007,2008 Free Software Foundation, Inc.
+#
+# This file is part of GNU Radio
+#
+# GNU Radio is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 3, or (at your option)
+# any later version.
+#
+# GNU Radio is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with GNU Radio; see the file COPYING. If not, write to
+# the Free Software Foundation, Inc., 51 Franklin Street,
+# Boston, MA 02110-1301, USA.
+#
+
+include $(top_srcdir)/Makefile.common
+
+AM_CPPFLAGS = $(STD_DEFINES_AND_INCLUDES)
+
+noinst_LTLIBRARIES = libopenbtsdecoder.la
+
+libopenbtsdecoder_la_SOURCES = \
+BitVector.cpp \
+GSM610Tables.cpp \
+GSMCommon.cpp \
+GSML1FEC.cpp \
+GSMTDMA.cpp \
+Threads.cpp \
+Timeval.cpp
+
+
+noinst_HEADERS = \
+Assert.h \
+BitVector.h \
+GSM610Tables.h \
+GSMCommon.h \
+GSML1FEC.h \
+GSMTDMA.h \
+RxBurst.h \
+Threads.h \
+Timeval.h \
+Vector.h \
+VocoderFrame.h
diff --git a/gsm-receiver/src/lib/decoder/openbtsstuff/RxBurst.h b/gsm-receiver/src/lib/decoder/openbtsstuff/RxBurst.h
new file mode 100644
index 0000000..4348fb8
--- /dev/null
+++ b/gsm-receiver/src/lib/decoder/openbtsstuff/RxBurst.h
@@ -0,0 +1,69 @@
+#ifndef _RXBURST_H
+#define _RXBURST_H
+
+#include "GSMCommon.h"
+#include "BitVector.h"
+
+namespace GSM {
+
+/**@name Positions of stealing bits within a normal burst, GSM 05.03 3.1.4. */
+//@{
+static const unsigned gHlIndex = 60; ///< index of first stealing bit, GSM 05.03 3.1.4
+static const unsigned gHuIndex = 87; ///< index of second stealing bit, GSM 05.03 3.1.4
+//@}
+
+static const unsigned gSlotLen = 148; ///< number of symbols per slot, not counting guard periods
+
+
+/**
+ Class to represent one timeslot of channel bits with soft encoding.
+*/
+class RxBurst : public SoftVector {
+
+ private:
+
+ Time mTime; ///< timeslot and frame on which this was received
+// float mTimingError; ///< Timing error in symbol steps, <0 means early.
+// float mRSSI; ///< RSSI estimate associated with the slot, dB wrt full scale.
+
+ public:
+
+ /** Wrap an RxBurst around an existing float array. */
+ RxBurst(float* wData, const Time &wTime)
+ :SoftVector(wData,gSlotLen),mTime(wTime)
+// mTimingError(wTimingError),mRSSI(wRSSI)
+ { }
+
+
+ Time time() const { return mTime; }
+
+ void time(const Time& wTime) { mTime = wTime; }
+
+// float RSSI() const { return mRSSI; }
+
+// float timingError() const { return mTimingError; }
+
+ /** Return a SoftVector alias to the first data field. */
+ const SoftVector data1() const { return segment(3, 57); }
+
+ /** Return a SoftVector alias to the second data field. */
+ const SoftVector data2() const { return segment(88, 57); }
+
+ /** Return upper stealing bit. */
+ bool Hu() const { return bit(gHuIndex); }
+
+ /** Return lower stealing bit. */
+ bool Hl() const { return bit(gHlIndex); }
+
+// friend std::ostream& operator<<(std::ostream& os, const RxBurst& ts);
+};
+
+// std::ostream& operator<<(std::ostream& os, const RxBurst& ts){
+// os << "time=" << ts.time();
+// os << " data=(" << (const SoftVector&)ts << ")" ;
+// return os;
+// }
+
+
+}
+#endif
diff --git a/gsm-receiver/src/lib/decoder/openbtsstuff/Threads.cpp b/gsm-receiver/src/lib/decoder/openbtsstuff/Threads.cpp
new file mode 100644
index 0000000..b97f3fe
--- /dev/null
+++ b/gsm-receiver/src/lib/decoder/openbtsstuff/Threads.cpp
@@ -0,0 +1,106 @@
+/*
+* Copyright 2008 Free Software Foundation, Inc.
+*
+* This software is distributed under the terms of the GNU Public License.
+* See the COPYING file in the main directory for details.
+
+ This program is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
+
+*/
+
+
+
+
+#include "Threads.h"
+#include "Timeval.h"
+
+
+using namespace std;
+
+
+
+
+Mutex gStreamLock; ///< Global lock to control access to cout and cerr.
+
+void lockCout()
+{
+ gStreamLock.lock();
+ Timeval entryTime;
+ cout << entryTime << " " << pthread_self() << ": ";
+}
+
+
+void unlockCout()
+{
+ cout << dec << endl << flush;
+ gStreamLock.unlock();
+}
+
+
+void lockCerr()
+{
+ gStreamLock.lock();
+ Timeval entryTime;
+ cerr << entryTime << " " << pthread_self() << ": ";
+}
+
+void unlockCerr()
+{
+ cerr << dec << endl << flush;
+ gStreamLock.unlock();
+}
+
+
+
+
+
+
+
+Mutex::Mutex()
+{
+ assert(!pthread_mutexattr_init(&mAttribs));
+ assert(!pthread_mutexattr_settype(&mAttribs,PTHREAD_MUTEX_RECURSIVE));
+ assert(!pthread_mutex_init(&mMutex,&mAttribs));
+}
+
+
+Mutex::~Mutex()
+{
+ pthread_mutex_destroy(&mMutex);
+ assert(!pthread_mutexattr_destroy(&mAttribs));
+}
+
+
+
+
+/** Block for the signal up to the cancellation timeout. */
+void Signal::wait(Mutex& wMutex, unsigned timeout) const
+{
+ struct timespec waitTime = Timeval(timeout).timespec();
+ // FIXME -- With -O3 optimzation in OS X this doesn't block. See bug #320.
+ pthread_cond_timedwait(&mSignal,&wMutex.mMutex,&waitTime);
+}
+
+
+void Thread::start(void *(*task)(void*), void *arg)
+{
+ assert(mThread==((pthread_t)0));
+ assert(!pthread_attr_init(&mAttrib));
+ assert(!pthread_attr_setstacksize(&mAttrib, mStackSize));
+ assert(!pthread_create(&mThread, &mAttrib, task, arg));
+}
+
+
+
+// vim: ts=4 sw=4
diff --git a/gsm-receiver/src/lib/decoder/openbtsstuff/Threads.h b/gsm-receiver/src/lib/decoder/openbtsstuff/Threads.h
new file mode 100644
index 0000000..14cff04
--- /dev/null
+++ b/gsm-receiver/src/lib/decoder/openbtsstuff/Threads.h
@@ -0,0 +1,150 @@
+/*
+* Copyright 2008 Free Software Foundation, Inc.
+*
+* This software is distributed under the terms of the GNU Public License.
+* See the COPYING file in the main directory for details.
+
+ This program is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
+
+*/
+
+
+#ifndef THREADS_H
+#define THREADS_H
+
+
+#include <pthread.h>
+#include <iostream>
+#include "Assert.h"
+
+class Mutex;
+
+
+/**@name Multithreaded access for standard streams. */
+//@{
+
+/**@name Functions for gStreamLock. */
+//@{
+extern Mutex gStreamLock; ///< global lock for cout and cerr
+void lockCerr(); ///< call prior to writing cerr
+void unlockCerr(); ///< call after writing cerr
+void lockCout(); ///< call prior to writing cout
+void unlockCout(); ///< call after writing cout
+//@}
+
+/**@name Macros for standard messages. */
+//@{
+#define COUT(text) { lockCout(); std::cout << text; unlockCout(); }
+#define CERR(text) { lockCerr(); std::cerr << __FILE__ << ":" << __LINE__ << ": " << text; unlockCerr(); }
+#ifdef NDEBUG
+#define DCOUT(text) {}
+#define OBJDCOUT(text) {}
+#else
+#define DCOUT(text) { COUT(__FILE__ << ":" << __LINE__ << " " << text); }
+#define OBJDCOUT(text) { DCOUT(this << " " << text); }
+#endif
+//@}
+//@}
+
+
+
+/**@defgroup C++ wrappers for pthread mechanisms. */
+//@{
+
+/** A class for recursive mutexes based on pthread_mutex. */
+class Mutex {
+
+ private:
+
+ pthread_mutex_t mMutex;
+ pthread_mutexattr_t mAttribs;
+
+ public:
+
+ Mutex();
+
+ ~Mutex();
+
+ void lock() { pthread_mutex_lock(&mMutex); }
+
+ void unlock() { pthread_mutex_unlock(&mMutex); }
+
+ friend class Signal;
+
+};
+
+
+
+/** A C++ interthread signal based on pthread condition variables. */
+class Signal {
+
+ private:
+
+ mutable pthread_cond_t mSignal;
+
+ public:
+
+ Signal() { assert(!pthread_cond_init(&mSignal,NULL)); }
+
+ ~Signal() { pthread_cond_destroy(&mSignal); }
+
+ /** Block for the signal up to the cancellation timeout. */
+ void wait(Mutex& wMutex, unsigned timeout=1000000000) const;
+
+ void signal() { pthread_cond_signal(&mSignal); }
+
+ void broadcast() { pthread_cond_broadcast(&mSignal); }
+
+};
+
+
+
+#define START_THREAD(thread,function,argument) \
+ thread.start((void *(*)(void*))function, (void*)argument);
+
+/** A C++ wrapper for pthread threads. */
+class Thread {
+
+ private:
+
+ pthread_t mThread;
+ pthread_attr_t mAttrib;
+ const static size_t mStackSize=4*65536;
+
+
+ public:
+
+ /** Create a thread in a non-running state. */
+ Thread():mThread((pthread_t)0) { }
+
+ /**
+ Destroy the Thread.
+ It should be stopped and joined.
+ */
+ ~Thread() { assert(!pthread_attr_destroy(&mAttrib)); }
+
+
+ /** Start the thread on a task. */
+ void start(void *(*task)(void*), void *arg);
+
+ /** Join a thread that will stop on its own. */
+ void join() { assert(!pthread_join(mThread,NULL)); }
+
+};
+
+
+
+
+#endif
+// vim: ts=4 sw=4
diff --git a/gsm-receiver/src/lib/decoder/openbtsstuff/Timeval.cpp b/gsm-receiver/src/lib/decoder/openbtsstuff/Timeval.cpp
new file mode 100644
index 0000000..e7590cb
--- /dev/null
+++ b/gsm-receiver/src/lib/decoder/openbtsstuff/Timeval.cpp
@@ -0,0 +1,93 @@
+/*
+* Copyright 2008 Free Software Foundation, Inc.
+*
+* This software is distributed under the terms of the GNU Public License.
+* See the COPYING file in the main directory for details.
+
+ This program is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
+
+*/
+
+
+
+#include "Timeval.h"
+
+using namespace std;
+
+void Timeval::future(unsigned offset)
+{
+ now();
+ unsigned sec = offset/1000;
+ unsigned msec = offset%1000;
+ mTimeval.tv_usec += msec*1000;
+ mTimeval.tv_sec += sec;
+ if (mTimeval.tv_usec>1000000) {
+ mTimeval.tv_usec -= 1000000;
+ mTimeval.tv_sec += 1;
+ }
+}
+
+
+struct timespec Timeval::timespec() const
+{
+ struct timespec retVal;
+ retVal.tv_sec = mTimeval.tv_sec;
+ retVal.tv_nsec = 1000 * (long)mTimeval.tv_usec;
+ return retVal;
+}
+
+
+bool Timeval::passed() const
+{
+ Timeval nowTime;
+ if (nowTime.mTimeval.tv_sec < mTimeval.tv_sec) return false;
+ if (nowTime.mTimeval.tv_sec > mTimeval.tv_sec) return true;
+ if (nowTime.mTimeval.tv_usec > mTimeval.tv_usec) return true;
+ return false;
+}
+
+double Timeval::seconds() const
+{
+ return ((double)mTimeval.tv_sec) + 1e-6*((double)mTimeval.tv_usec);
+}
+
+
+
+long Timeval::delta(const Timeval& other) const
+{
+ long deltaS = other.sec() - sec();
+ long deltaUs = other.usec() - usec();
+ return 1000*deltaS + deltaUs/1000;
+}
+
+
+
+
+ostream& operator<<(ostream& os, const Timeval& tv)
+{
+ os.setf( ios::fixed, ios::floatfield );
+ os << tv.seconds();
+ return os;
+}
+
+
+ostream& operator<<(ostream& os, const struct timespec& ts)
+{
+ os << ts.tv_sec << "," << ts.tv_nsec;
+ return os;
+}
+
+
+
+// vim: ts=4 sw=4
diff --git a/gsm-receiver/src/lib/decoder/openbtsstuff/Timeval.h b/gsm-receiver/src/lib/decoder/openbtsstuff/Timeval.h
new file mode 100644
index 0000000..44618bc
--- /dev/null
+++ b/gsm-receiver/src/lib/decoder/openbtsstuff/Timeval.h
@@ -0,0 +1,96 @@
+/*
+* Copyright 2008 Free Software Foundation, Inc.
+*
+* This software is distributed under the terms of the GNU Public License.
+* See the COPYING file in the main directory for details.
+
+ This program is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
+
+*/
+
+
+#ifndef TIMEVAL_H
+#define TIMEVAL_H
+
+#include "sys/time.h"
+#include <iostream>
+
+
+
+inline void msleep(long v) { usleep((v+500)/1000); }
+
+
+/** A C++ wrapper for struct timeval. */
+class Timeval {
+
+ private:
+
+ struct timeval mTimeval;
+
+ public:
+
+ /** Set the value to gettimeofday. */
+ void now() { gettimeofday(&mTimeval,NULL); }
+
+ /** Set the value to gettimeofday plus an offset. */
+ void future(unsigned offset);
+
+ //@{
+ Timeval(unsigned sec, unsigned usec)
+ {
+ mTimeval.tv_sec = sec;
+ mTimeval.tv_usec = usec;
+ }
+
+ Timeval(const struct timeval& wTimeval)
+ :mTimeval(wTimeval)
+ {}
+
+ /**
+ Create a Timeval offset into the future.
+ @param offset milliseconds
+ */
+ Timeval(unsigned offset=0) { future(offset); }
+ //@}
+
+ /** Convert to a struct timespec. */
+ struct timespec timespec() const;
+
+ /** Return total seconds. */
+ double seconds() const;
+
+ uint32_t sec() const { return mTimeval.tv_sec; }
+ uint32_t usec() const { return mTimeval.tv_usec; }
+
+ /** Return differnce from other (other-self), in ms. */
+ long delta(const Timeval& other) const;
+
+ /** Elapsed time in ms. */
+ long elapsed() const { return delta(Timeval()); }
+
+ /** Remaining time in ms. */
+ long remaining() const { return -elapsed(); }
+
+ /** Return true if the time has passed, as per gettimeofday. */
+ bool passed() const;
+
+};
+
+std::ostream& operator<<(std::ostream& os, const Timeval&);
+
+std::ostream& operator<<(std::ostream& os, const struct timespec&);
+
+
+#endif
+// vim: ts=4 sw=4
diff --git a/gsm-receiver/src/lib/decoder/openbtsstuff/Vector.h b/gsm-receiver/src/lib/decoder/openbtsstuff/Vector.h
new file mode 100644
index 0000000..cec278a
--- /dev/null
+++ b/gsm-receiver/src/lib/decoder/openbtsstuff/Vector.h
@@ -0,0 +1,257 @@
+/**@file Simplified Vector template with aliases. */
+/*
+* Copyright 2008 Free Software Foundation, Inc.
+*
+* This software is distributed under the terms of the GNU Public License.
+* See the COPYING file in the main directory for details.
+
+ This program is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
+
+*/
+
+
+
+
+#ifndef VECTOR_H
+#define VECTOR_H
+
+#include <string.h>
+#include <iostream>
+#include "Assert.h"
+
+
+/**
+ A simplified Vector template with aliases.
+ Unlike std::vector, this class does not support dynamic resizing.
+ Unlike std::vector, this class does support "aliases" and subvectors.
+*/
+template <class T> class Vector {
+
+ // TODO -- Replace memcpy calls with for-loops.
+
+ public:
+
+ /**@name Iterator types. */
+ //@{
+ typedef T* iterator;
+ typedef const T* const_iterator;
+ //@}
+
+ protected:
+
+ T* mData; ///< allocated data block, if any
+ T* mStart; ///< start of useful data
+ T* mEnd; ///< end of useful data + 1
+
+ public:
+
+ /** Return the size of the Vector. */
+ size_t size() const
+ {
+ assert(mStart>=mData);
+ assert(mEnd>=mStart);
+ return mEnd - mStart;
+ }
+
+ /** Return size in bytes. */
+ size_t bytes() const { return size()*sizeof(T); }
+
+ /** Change the size of the Vector, discarding content. */
+ void resize(size_t newSize)
+ {
+ if (mData!=NULL) delete[] mData;
+ if (newSize==0) mData=NULL;
+ else mData = new T[newSize];
+ mStart = mData;
+ mEnd = mStart + newSize;
+ }
+
+ /** Release memory and clear pointers. */
+ void clear() { resize(0); }
+
+
+ /** Copy data from another vector. */
+ void clone(const Vector<T>& other)
+ {
+ resize(other.size());
+ memcpy(mData,other.mStart,other.bytes());
+ }
+
+
+
+
+ //@{
+
+ /** Build an empty Vector of a given size. */
+ Vector(size_t wSize=0):mData(NULL) { resize(wSize); }
+
+ /** Build a Vector by shifting the data block. */
+ Vector(Vector<T>& other)
+ :mData(other.mData),mStart(other.mStart),mEnd(other.mEnd)
+ { other.mData=NULL; }
+
+ /** Build a Vector by copying another. */
+ Vector(const Vector<T>& other):mData(NULL) { clone(other); }
+
+ /** Build a Vector with explicit values. */
+ Vector(T* wData, T* wStart, T* wEnd)
+ :mData(wData),mStart(wStart),mEnd(wEnd)
+ { }
+
+ /** Build a vector from an existing block, NOT to be deleted upon destruction. */
+ Vector(T* wStart, size_t span)
+ :mData(NULL),mStart(wStart),mEnd(wStart+span)
+ { }
+
+ /** Build a Vector by concatenation. */
+ Vector(const Vector<T>& other1, const Vector<T>& other2)
+ :mData(NULL)
+ {
+ resize(other1.size()+other2.size());
+ memcpy(mStart, other1.mStart, other1.bytes());
+ memcpy(mStart+other1.size(), other2.mStart, other2.bytes());
+ }
+
+ //@}
+
+ /** Destroy a Vector, deleting held memory. */
+ ~Vector() { clear(); }
+
+
+
+
+ //@{
+
+ /** Assign from another Vector, shifting ownership. */
+ void operator=(Vector<T>& other)
+ {
+ clear();
+ mData=other.mData;
+ mStart=other.mStart;
+ mEnd=other.mEnd;
+ other.mData=NULL;
+ }
+
+ /** Assign from another Vector, copying. */
+ void operator=(const Vector<T>& other) { clone(other); }
+
+ //@}
+
+
+ //@{
+
+ /** Return an alias to a segment of this Vector. */
+ Vector<T> segment(size_t start, size_t span)
+ {
+ T* wStart = mStart + start;
+ T* wEnd = wStart + span;
+ assert(wEnd<=mEnd);
+ return Vector<T>(NULL,wStart,wEnd);
+ }
+
+ /** Return an alias to a segment of this Vector. */
+ const Vector<T> segment(size_t start, size_t span) const
+ {
+ T* wStart = mStart + start;
+ T* wEnd = wStart + span;
+ assert(wEnd<=mEnd);
+ return Vector<T>(NULL,wStart,wEnd);
+ }
+
+ Vector<T> head(size_t span) { return segment(0,span); }
+ const Vector<T> head(size_t span) const { return segment(0,span); }
+ Vector<T> tail(size_t start) { return segment(start,size()-start); }
+ const Vector<T> tail(size_t start) const { return segment(start,size()-start); }
+
+ /**
+ Copy part of this Vector to a segment of another Vector.
+ @param other The other vector.
+ @param start The start point in the other vector.
+ @param span The number of elements to copy.
+ */
+ void copyToSegment(Vector<T>& other, size_t start, size_t span) const
+ {
+ T* base = other.mStart + start;
+ assert(base+span<=other.mEnd);
+ assert(mStart+span<=mEnd);
+ memcpy(base,mStart,span*sizeof(T));
+ }
+
+ /** Copy all of this Vector to a segment of another Vector. */
+ void copyToSegment(Vector<T>& other, size_t start=0) const { copyToSegment(other,start,size()); }
+
+ void copyTo(Vector<T>& other) const { copyToSegment(other,0,size()); }
+
+ /**
+ Copy a segment of this vector into another.
+ @param other The other vector (to copt into starting at 0.)
+ @param start The start point in this vector.
+ @param span The number of elements to copy.
+ */
+ void segmentCopyTo(Vector<T>& other, size_t start, size_t span)
+ {
+ T* base = mStart + start;
+ assert(base+span<=mEnd);
+ assert(other.mStart+span<=other.mEnd);
+ memcpy(other.mStart,base,span*sizeof(T));
+ }
+
+ void fill(const T& val)
+ {
+ T* dp=mStart;
+ while (dp<mEnd) *dp++=val;
+ }
+
+
+ //@}
+
+
+ //@{
+
+ T& operator[](size_t index)
+ {
+ assert(mStart+index<mEnd);
+ return mStart[index];
+ }
+
+ const T& operator[](size_t index) const
+ {
+ assert(mStart+index<mEnd);
+ return mStart[index];
+ }
+
+ const T* begin() const { return mStart; }
+ T* begin() { return mStart; }
+ const T* end() const { return mEnd; }
+ T* end() { return mEnd; }
+ //@}
+
+
+};
+
+
+
+
+/** Basic print operator for Vector objects. */
+template <class T>
+std::ostream& operator<<(std::ostream& os, const Vector<T>& v)
+{
+ for (unsigned i=0; i<v.size(); i++) os << v[i] << " ";
+ return os;
+}
+
+
+
+#endif
+// vim: ts=4 sw=4
diff --git a/gsm-receiver/src/lib/decoder/openbtsstuff/VocoderFrame.h b/gsm-receiver/src/lib/decoder/openbtsstuff/VocoderFrame.h
new file mode 100644
index 0000000..c0e51c0
--- /dev/null
+++ b/gsm-receiver/src/lib/decoder/openbtsstuff/VocoderFrame.h
@@ -0,0 +1,25 @@
+#ifndef _VOCODERFRAME_H
+#define _VOCODERFRAME_H
+
+#include "BitVector.h"
+//#include "GSMCommon.h"
+
+class VocoderFrame : public BitVector {
+
+ public:
+
+ VocoderFrame()
+ :BitVector(264)
+ { fillField(0,0x0d,4); }
+
+ /** Construct by unpacking a char[33]. */
+ VocoderFrame(const unsigned char *src)
+ :BitVector(264)
+ { unpack(src); }
+
+ BitVector payload() { return tail(4); }
+ const BitVector payload() const { return tail(4); }
+
+};
+
+#endif
diff --git a/gsm-receiver/src/lib/decoder/out_pcap.c b/gsm-receiver/src/lib/decoder/out_pcap.c
new file mode 100644
index 0000000..4da5fd6
--- /dev/null
+++ b/gsm-receiver/src/lib/decoder/out_pcap.c
@@ -0,0 +1,111 @@
+/* PCAP support for gsm-tvoid
+ * (C) 2008 by Harald Welte <laforge@gnumonks.org>
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <fcntl.h>
+#include <pcap.h>
+#include <errno.h>
+#include <time.h>
+
+#include "out_pcap.h"
+#include "gsmtap.h"
+
+#ifndef LINKTYPE_GSMTAP
+#define LINKTYPE_GSMTAP 2342
+#endif
+
+#define TCPDUMP_MAGIC 0xa1b2c3d4
+
+struct pcap_timeval {
+ int32_t tv_sec;
+ int32_t tv_usec;
+};
+
+struct pcap_sf_pkthdr {
+ struct pcap_timeval ts; /* time stamp */
+ u_int32_t caplen; /* lenght of portion present */
+ u_int32_t len; /* length of this packet */
+};
+
+static int write_pcap_file_header(int fd)
+{
+ struct pcap_file_header pfh;
+
+ pfh.magic = TCPDUMP_MAGIC;
+ pfh.version_major = PCAP_VERSION_MAJOR;
+ pfh.version_minor = PCAP_VERSION_MINOR;
+ pfh.thiszone = timezone;
+ pfh.sigfigs = 0;
+ pfh.snaplen = 1024; /* FIXME */
+ pfh.linktype = LINKTYPE_GSMTAP;
+
+ if (write(fd, &pfh, sizeof(pfh)) < sizeof(pfh))
+ return -1;
+
+ return 0;
+}
+
+/* open pcap file and write header */
+int open_pcap_file(char *fname)
+{
+ int fd;
+ int rc;
+
+ fd = open(fname, O_WRONLY|O_CREAT|O_TRUNC, 0660);
+ if (fd < 0)
+ return fd;
+
+ rc = write_pcap_file_header(fd);
+ if (rc < 0) {
+ close(fd);
+ fd = -EIO;
+ }
+
+ return fd;
+}
+
+int write_pcap_packet(int fd, int arfcn, int ts, int fn,
+ int burst, int burst_type,
+ const unsigned char *data, unsigned int len)
+{
+ unsigned char buf[8192];
+ struct pcap_sf_pkthdr *ph;
+ struct gsmtap_hdr *gh;
+ struct timeval tv;
+ int rc;
+
+ if (fd < 0)
+ return -EINVAL;
+
+ ph = (struct pcap_sf_pkthdr *) &buf[0];
+ gh = (struct gsmtap_hdr *) &buf[sizeof(struct pcap_sf_pkthdr)];
+
+ gettimeofday(&tv, NULL);
+
+ ph->ts.tv_sec = tv.tv_sec;
+ ph->ts.tv_usec = tv.tv_usec;
+ ph->caplen = ph->len = len + sizeof(struct gsmtap_hdr);
+
+ gh->version = GSMTAP_VERSION;
+ gh->hdr_len = sizeof(struct gsmtap_hdr)>>2;
+ if (burst)
+ gh->type = GSMTAP_TYPE_UM_BURST;
+ else
+ gh->type = GSMTAP_TYPE_UM;
+ gh->timeslot = ts;
+ gh->arfcn = htons(arfcn);
+ /* we don't support signal/noise yet */
+ gh->noise_db = gh->signal_db = 0;
+ gh->frame_number = htonl(fn);
+ gh->burst_type = burst_type & 0xff;
+
+ memcpy(buf + sizeof(*ph) + sizeof(*gh), data, len);
+
+ rc = write(fd, buf, sizeof(*ph) + sizeof(*gh) + len);
+
+ //fsync(fd);
+
+ return rc;
+}
diff --git a/gsm-receiver/src/lib/decoder/out_pcap.h b/gsm-receiver/src/lib/decoder/out_pcap.h
new file mode 100644
index 0000000..5ae5e3c
--- /dev/null
+++ b/gsm-receiver/src/lib/decoder/out_pcap.h
@@ -0,0 +1,9 @@
+#ifndef _PCAP_IF_H
+#define _PCAP_IF_H
+
+extern int open_pcap_file(char *fname);
+
+int write_pcap_packet(int fd, int arfcn, int ts, int fn,
+ int burst, int burst_type,
+ const unsigned char *data, unsigned int len);
+#endif
diff --git a/gsm-receiver/src/lib/decoder/sch.c b/gsm-receiver/src/lib/decoder/sch.c
new file mode 100644
index 0000000..6f141dd
--- /dev/null
+++ b/gsm-receiver/src/lib/decoder/sch.c
@@ -0,0 +1,333 @@
+#include "system.h"
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <string.h>
+#include "gsm_constants.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 * t1_o, int * t2_o, int * t3_o, int * ncc_o, int * bcc_o)
+{
+
+ int errors, t1, t2, t3p, t3, ncc, bcc;
+ unsigned char data[CONV_SIZE], decoded_data[PARITY_OUTPUT_SIZE];
+
+ // extract encoded data from synchronization burst
+ /* buf, 39 bit */
+ /* buf + 39 + 64 = 103, 39 */
+ memcpy(data, buf, SCH_DATA_LEN);
+ memcpy(data + SCH_DATA_LEN, buf + SCH_DATA_LEN + N_SYNC_BITS, SCH_DATA_LEN);
+
+ // Viterbi decode
+ if (errors = conv_decode(data, decoded_data)) {
+ // fprintf(stderr, "error: sch: conv_decode (%d)\n", errors);
+ DEBUGF("ERR: conv_decode %d\n", errors);
+ return errors;
+ }
+
+ // check parity
+ if (parity_check(decoded_data)) {
+ // fprintf(stderr, "error: sch: parity failed\n");
+ DEBUGF("ERR: parity_check failed\n");
+ return 1;
+ }
+
+ // Synchronization channel information, 44.018 page 171. (V7.2.0)
+ ncc =
+ (decoded_data[ 7] << 2) |
+ (decoded_data[ 6] << 1) |
+ (decoded_data[ 5] << 0);
+ bcc =
+ (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 t3 - t2 mod 26
+// tt = ((t3 + 26) - 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)
+ if (t1_o && t2_o && t3_o && ncc_o && bcc_o) {
+ *t1_o = t1;
+ *t2_o = t2;
+ *t3_o = t3;
+ *bcc_o = bcc;
+ *ncc_o = ncc;
+ }
+
+ return 0;
+}
diff --git a/gsm-receiver/src/lib/decoder/sch.h b/gsm-receiver/src/lib/decoder/sch.h
new file mode 100644
index 0000000..4d47eb5
--- /dev/null
+++ b/gsm-receiver/src/lib/decoder/sch.h
@@ -0,0 +1,17 @@
+
+#ifndef __SCH_H__
+#define __SCH_H__ 1
+
+#ifdef __cplusplus
+extern "C"
+{
+#endif
+
+ int decode_sch(const unsigned char *buf, int * t1_o, int * t2_o, int * t3_o, int * ncc, int * bcc);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
+
diff --git a/gsm-receiver/src/lib/decoder/system.h b/gsm-receiver/src/lib/decoder/system.h
new file mode 100644
index 0000000..414730a
--- /dev/null
+++ b/gsm-receiver/src/lib/decoder/system.h
@@ -0,0 +1,11 @@
+
+#ifndef __GSMTVOID_SYSTEM_H__
+#define __GSMTVOID_SYSTEM_H__ 1
+
+#define DEBUGF(a...) { \
+ fprintf(stderr, "%s:%d ", __FILE__, __LINE__); \
+ fprintf(stderr, a); \
+} while (0)
+
+#endif
+
diff --git a/gsm-receiver/src/lib/decoder/tun.c b/gsm-receiver/src/lib/decoder/tun.c
new file mode 100644
index 0000000..2abda90
--- /dev/null
+++ b/gsm-receiver/src/lib/decoder/tun.c
@@ -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/gsm-receiver/src/lib/decoder/tun.h b/gsm-receiver/src/lib/decoder/tun.h
new file mode 100644
index 0000000..a7868c4
--- /dev/null
+++ b/gsm-receiver/src/lib/decoder/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/gsm-receiver/src/lib/gsm.i b/gsm-receiver/src/lib/gsm.i
new file mode 100644
index 0000000..3a5c561
--- /dev/null
+++ b/gsm-receiver/src/lib/gsm.i
@@ -0,0 +1,49 @@
+/* -*- c++ -*- */
+/*
+ * @file
+ * @author Piotr Krysik <pkrysik@stud.elka.pw.edu.pl>
+ * @section LICENSE
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 3, or (at your option)
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; see the file COPYING. If not, write to
+ * the Free Software Foundation, Inc., 51 Franklin Street,
+ * Boston, MA 02110-1301, USA.
+ */
+
+%feature("autodoc", "1"); // generate python docstrings
+
+%include "exception.i"
+%import "gnuradio.i" // the common stuff
+
+/* %include "gsm_constants.h" */
+
+%{
+#include "gnuradio_swig_bug_workaround.h" // mandatory bug fix
+#include "gsm_receiver_cf.h"
+#include <stdexcept>
+/* #include "gsm_constants.h" */
+%}
+
+// ----------------------------------------------------------------
+
+GR_SWIG_BLOCK_MAGIC(gsm,receiver_cf);
+
+gsm_receiver_cf_sptr gsm_make_receiver_cf ( gr_feval_dd *tuner, gr_feval_dd *synchronizer, int osr, std::string key);
+
+class gsm_receiver_cf : public gr_block
+{
+private:
+ gsm_receiver_cf ( gr_feval_dd *tuner, gr_feval_dd *synchronizer, int osr);
+};
+
+// ----------------------------------------------------------------
diff --git a/gsm-receiver/src/lib/gsm_constants.h b/gsm-receiver/src/lib/gsm_constants.h
new file mode 100644
index 0000000..939099d
--- /dev/null
+++ b/gsm-receiver/src/lib/gsm_constants.h
@@ -0,0 +1,150 @@
+#ifndef INCLUDED_GSM_CONSTANTS_H
+#define INCLUDED_GSM_CONSTANTS_H
+
+#define GSM_SYMBOL_RATE (1625000.0/6.0) //symbols per second
+#define GSM_SYMBOL_PERIOD (1.0/GSM_SYMBOL_RATE) //seconds per symbol
+
+//Burst timing
+#define TAIL_BITS 3
+#define GUARD_BITS 8
+#define GUARD_FRACTIONAL 0.25 //fractional part of guard period
+#define GUARD_PERIOD GUARD_BITS + GUARD_FRACTIONAL
+#define DATA_BITS 57 //size of 1 data block in normal burst
+#define STEALING_BIT 1
+#define N_TRAIN_BITS 26
+#define N_SYNC_BITS 64
+#define USEFUL_BITS 142 //(2*(DATA_BITS+STEALING_BIT) + N_TRAIN_BITS )
+#define FCCH_BITS USEFUL_BITS
+#define BURST_SIZE (USEFUL_BITS+2*TAIL_BITS)
+
+#define SCH_DATA_LEN 39
+#define TS_BITS (TAIL_BITS+USEFUL_BITS+TAIL_BITS+GUARD_BITS) //a full TS (156 bits)
+#define TS_PER_FRAME 8
+#define FRAME_BITS (TS_PER_FRAME * TS_BITS + 2) // 156.25 * 8
+#define FCCH_POS TAIL_BITS
+#define SYNC_POS 39
+#define TRAIN_POS ( TAIL_BITS + (DATA_BITS+STEALING_BIT) + 5) //first 5 bits of a training sequence
+ //aren't used for channel impulse response estimation
+#define TRAIN_BEGINNING 5
+#define SAFETY_MARGIN 6 //
+
+#define FCCH_HITS_NEEDED (USEFUL_BITS - 4)
+#define FCCH_MAX_MISSES 1
+#define FCCH_MAX_FREQ_OFFSET 100
+
+#define CHAN_IMP_RESP_LENGTH 5
+
+#define MAX_SCH_ERRORS 5 //maximum number of subsequent sch errors after which gsm receiver goes to find_next_fcch state
+
+typedef enum {empty, fcch_burst, sch_burst, normal_burst, rach_burst, dummy, dummy_or_normal} burst_type;
+typedef enum {unknown, multiframe_26, multiframe_51} multiframe_type;
+
+static const unsigned char SYNC_BITS[] = {
+ 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
+};
+
+const unsigned FCCH_FRAMES[] = {0, 10, 20, 30, 40};
+const unsigned SCH_FRAMES[] = {1, 11, 21, 31, 41};
+
+const unsigned BCCH_FRAMES[] = {2, 3, 4, 5}; //!!the receiver shouldn't care about logical
+ //!!channels so this will be removed from this header
+const unsigned TEST_CCH_FRAMES[] = {2, 3, 4, 5, 6, 7, 8, 9, 12, 13, 14, 15, 16, 17, 18, 19, 22, 23, 24, 25, 26, 27, 28, 29, 32, 33, 34, 35, 36, 37, 38, 39, 42, 43, 44, 45, 46, 47, 48, 49};
+const unsigned TRAFFIC_CHANNEL_F[] = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24};
+const unsigned TEST51[] = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50};
+
+
+#define TSC0 0
+#define TSC1 1
+#define TSC2 2
+#define TSC3 3
+#define TSC4 4
+#define TSC5 5
+#define TSC6 6
+#define TSC7 7
+#define TS_DUMMY 8
+
+#define TRAIN_SEQ_NUM 9
+
+#define TIMESLOT0 0
+#define TIMESLOT1 1
+#define TIMESLOT2 2
+#define TIMESLOT3 3
+#define TIMESLOT4 4
+#define TIMESLOT5 5
+#define TIMESLOT6 6
+#define TIMESLOT7 7
+
+
+static const unsigned char train_seq[TRAIN_SEQ_NUM][N_TRAIN_BITS] = {
+ {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},
+ {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},
+ {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},
+ {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},
+ {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},
+ {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},
+ {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},
+ {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},
+ {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} // DUMMY
+};
+
+
+//Dummy burst 0xFB 76 0A 4E 09 10 1F 1C 5C 5C 57 4A 33 39 E9 F1 2F A8
+static const unsigned char dummy_burst[] = {
+ 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 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 unsigned char fc_fb[] = { //I don't use this tables,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, //I copied this here from burst_types.h because
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, //the description is very informative - p.krysik
+ 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_compact_fb[] = {
+ 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
+};
+
+
+#endif /* INCLUDED_GSM_CONSTANTS_H */
diff --git a/gsm-receiver/src/lib/gsm_receiver_cf.cc b/gsm-receiver/src/lib/gsm_receiver_cf.cc
new file mode 100644
index 0000000..4742b33
--- /dev/null
+++ b/gsm-receiver/src/lib/gsm_receiver_cf.cc
@@ -0,0 +1,853 @@
+/* -*- c++ -*- */
+/*
+ * @file
+ * @author Piotr Krysik <pkrysik@stud.elka.pw.edu.pl>
+ * @section LICENSE
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 3, or (at your option)
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; see the file COPYING. If not, write to
+ * the Free Software Foundation, Inc., 51 Franklin Street,
+ * Boston, MA 02110-1301, USA.
+ */
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include <gr_io_signature.h>
+#include <gr_math.h>
+#include <math.h>
+#include <Assert.h>
+#include <boost/circular_buffer.hpp>
+#include <algorithm>
+#include <numeric>
+#include <gsm_receiver_cf.h>
+#include <viterbi_detector.h>
+#include <string.h>
+#include <sch.h>
+
+
+#include "RxBurst.h"
+#include "GSMCommon.h"
+
+#define SYNC_SEARCH_RANGE 30
+// #define TRAIN_SEARCH_RANGE 40
+//FIXME: decide to use this define or not
+
+//TODO: this shouldn't be here - remove it when gsm receiver's interface will be ready
+void decrypt(const unsigned char * burst_binary, byte * KC, float * decrypted_data, unsigned FN)
+{
+ byte AtoB[2*DATA_BITS];
+
+ keysetup(KC, FN);
+ runA51(AtoB);
+
+ for (int i = 0; i < 148; i++) {
+ decrypted_data[i] = burst_binary[i];
+ }
+
+ for (int i = 0; i < 57; i++) {
+ decrypted_data[i+3] = AtoB[i] ^ burst_binary[i+3];
+ }
+
+ for (int i = 0; i < 57; i++) {
+ decrypted_data[i+88] = AtoB[i+57] ^ burst_binary[i+88];
+ }
+}
+
+void gsm_receiver_cf::read_key(std::string key)
+{
+ int i;
+ int b;
+ for (i = 0;i < 8;i++) {
+ b = d_hex_to_int[(char)key[(i)*2]]*16 + d_hex_to_int[(char)key[i*2+1]];
+ d_KC[i] = (byte)b;
+ }
+}
+
+void gsm_receiver_cf::process_normal_burst(burst_counter burst_nr, const unsigned char * burst_binary)
+{
+// static byte KC[] = { 0xAD, 0x6A, 0x3E, 0xC2, 0xB4, 0x42, 0xE4, 0x00 };
+// static byte KC[] = { 0x2B, 0x08, 0x74, 0x9F, 0xDD, 0x0D, 0x9C, 0x00 };
+// printf("%x", KC[0]);
+ float decrypted_data[148];
+ unsigned char * voice_frame;
+
+// if (burst_nr.get_timeslot_nr() == 7) {
+ if (burst_nr.get_timeslot_nr() >= 1 && burst_nr.get_timeslot_nr() <= 7) {
+ decrypt(burst_binary, d_KC, decrypted_data, burst_nr.get_frame_nr_mod());
+
+ GSM::Time time(burst_nr.get_frame_nr(), burst_nr.get_timeslot_nr());
+ GSM::RxBurst rxbrst(decrypted_data, time);
+ switch (burst_nr.get_timeslot_nr()) {
+ case 1:
+ if ( d_tch_decoder1.processBurst( rxbrst ) == true) {
+ fwrite(d_tch_decoder1.get_voice_frame(), 1 , 33, d_gsm_file);
+ }
+ break;
+ case 2:
+ if ( d_tch_decoder2.processBurst( rxbrst ) == true) {
+ fwrite(d_tch_decoder2.get_voice_frame(), 1 , 33, d_gsm_file);
+ }
+ break;
+ case 3:
+ if ( d_tch_decoder3.processBurst( rxbrst ) == true) {
+ fwrite(d_tch_decoder3.get_voice_frame(), 1 , 33, d_gsm_file);
+ }
+ break;
+ case 4:
+ if ( d_tch_decoder4.processBurst( rxbrst ) == true) {
+ fwrite(d_tch_decoder4.get_voice_frame(), 1 , 33, d_gsm_file);
+ }
+ break;
+ case 5:
+ if ( d_tch_decoder5.processBurst( rxbrst ) == true) {
+ fwrite(d_tch_decoder5.get_voice_frame(), 1 , 33, d_gsm_file);
+ }
+ break;
+ case 6:
+ if ( d_tch_decoder6.processBurst( rxbrst ) == true) {
+ fwrite(d_tch_decoder6.get_voice_frame(), 1 , 33, d_gsm_file);
+ }
+ break;
+ case 7:
+ if ( d_tch_decoder7.processBurst( rxbrst ) == true) {
+ fwrite(d_tch_decoder7.get_voice_frame(), 1 , 33, d_gsm_file);
+ }
+ break;
+ }
+ }
+
+ if (burst_nr.get_timeslot_nr() == 0) {
+ GS_process(&d_gs_ctx, TIMESLOT0, 6, &burst_binary[3], burst_nr.get_frame_nr());
+ }
+}
+//TODO: this shouldn't be here also - the same reason
+void gsm_receiver_cf::configure_receiver()
+{
+ d_channel_conf.set_multiframe_type(TSC0, multiframe_51);
+
+ d_channel_conf.set_burst_types(TSC0, TEST_CCH_FRAMES, sizeof(TEST_CCH_FRAMES) / sizeof(unsigned), normal_burst);
+ d_channel_conf.set_burst_types(TSC0, FCCH_FRAMES, sizeof(FCCH_FRAMES) / sizeof(unsigned), fcch_burst);
+
+ d_channel_conf.set_multiframe_type(TIMESLOT1, multiframe_26);
+ d_channel_conf.set_burst_types(TIMESLOT1, TRAFFIC_CHANNEL_F, sizeof(TRAFFIC_CHANNEL_F) / sizeof(unsigned), dummy_or_normal);
+ d_channel_conf.set_multiframe_type(TIMESLOT2, multiframe_26);
+ d_channel_conf.set_burst_types(TIMESLOT2, TRAFFIC_CHANNEL_F, sizeof(TRAFFIC_CHANNEL_F) / sizeof(unsigned), dummy_or_normal);
+
+ d_channel_conf.set_multiframe_type(TIMESLOT3, multiframe_26);
+ d_channel_conf.set_burst_types(TIMESLOT3, TRAFFIC_CHANNEL_F, sizeof(TRAFFIC_CHANNEL_F) / sizeof(unsigned), dummy_or_normal);
+ d_channel_conf.set_multiframe_type(TIMESLOT4, multiframe_26);
+ d_channel_conf.set_burst_types(TIMESLOT4, TRAFFIC_CHANNEL_F, sizeof(TRAFFIC_CHANNEL_F) / sizeof(unsigned), dummy_or_normal);
+
+ d_channel_conf.set_multiframe_type(TIMESLOT5, multiframe_26);
+ d_channel_conf.set_burst_types(TIMESLOT5, TRAFFIC_CHANNEL_F, sizeof(TRAFFIC_CHANNEL_F) / sizeof(unsigned), dummy_or_normal);
+ d_channel_conf.set_multiframe_type(TIMESLOT6, multiframe_26);
+ d_channel_conf.set_burst_types(TIMESLOT6, TRAFFIC_CHANNEL_F, sizeof(TRAFFIC_CHANNEL_F) / sizeof(unsigned), dummy_or_normal);
+
+ d_channel_conf.set_multiframe_type(TIMESLOT7, multiframe_26);
+ d_channel_conf.set_burst_types(TIMESLOT7, TRAFFIC_CHANNEL_F, sizeof(TRAFFIC_CHANNEL_F) / sizeof(unsigned), dummy_or_normal);
+
+}
+
+
+typedef std::list<float> list_float;
+typedef std::vector<float> vector_float;
+
+typedef boost::circular_buffer<float> circular_buffer_float;
+
+gsm_receiver_cf_sptr
+gsm_make_receiver_cf(gr_feval_dd *tuner, gr_feval_dd *synchronizer, int osr, std::string key)
+{
+ return gsm_receiver_cf_sptr(new gsm_receiver_cf(tuner, synchronizer, osr, key));
+}
+
+static const int MIN_IN = 1; // mininum number of input streams
+static const int MAX_IN = 1; // maximum number of input streams
+static const int MIN_OUT = 0; // minimum number of output streams
+static const int MAX_OUT = 1; // maximum number of output streams
+
+/*
+ * The private constructor
+ */
+gsm_receiver_cf::gsm_receiver_cf(gr_feval_dd *tuner, gr_feval_dd *synchronizer, int osr, std::string key)
+ : gr_block("gsm_receiver",
+ gr_make_io_signature(MIN_IN, MAX_IN, sizeof(gr_complex)),
+ gr_make_io_signature(MIN_OUT, MAX_OUT, 142 * sizeof(float))),
+ d_OSR(osr),
+ d_chan_imp_length(CHAN_IMP_RESP_LENGTH),
+ d_tuner(tuner),
+ d_counter(0),
+ d_fcch_start_pos(0),
+ d_freq_offset(0),
+ d_state(first_fcch_search),
+ d_burst_nr(osr),
+ d_failed_sch(0),
+ d_tch_decoder1( GSM::gFACCH_TCHFMapping ),
+ d_tch_decoder2( GSM::gFACCH_TCHFMapping ),
+ d_tch_decoder3( GSM::gFACCH_TCHFMapping ),
+ d_tch_decoder4( GSM::gFACCH_TCHFMapping ),
+ d_tch_decoder5( GSM::gFACCH_TCHFMapping ),
+ d_tch_decoder6( GSM::gFACCH_TCHFMapping ),
+ d_tch_decoder7( GSM::gFACCH_TCHFMapping )
+{
+ int i;
+ gmsk_mapper(SYNC_BITS, N_SYNC_BITS, d_sch_training_seq, gr_complex(0.0, -1.0));
+ for (i = 0; i < TRAIN_SEQ_NUM; i++) {
+ gr_complex startpoint;
+ if (i == 6) { //this is nasty hack
+ startpoint = gr_complex(-1.0, 0.0); //if I don't change it here all bits of normal bursts for BTSes with bcc=6 will have reversed values
+ } else {
+ startpoint = gr_complex(1.0, 0.0); //I've checked this hack for bcc==0,1,2,3,4,6
+ } //I don't know what about bcc==5 and 7 yet
+ //TODO:find source of this situation - this is purely mathematical problem I guess
+
+ gmsk_mapper(train_seq[i], N_TRAIN_BITS, d_norm_training_seq[i], startpoint);
+ }
+ d_gsm_file = fopen( "speech.gsm", "wb" );
+
+ d_hex_to_int['0'] = 0;
+ d_hex_to_int['4'] = 4;
+ d_hex_to_int['8'] = 8;
+ d_hex_to_int['c'] = 0xc;
+ d_hex_to_int['1'] = 1;
+ d_hex_to_int['5'] = 5;
+ d_hex_to_int['9'] = 9;
+ d_hex_to_int['d'] = 0xd;
+ d_hex_to_int['2'] = 2;
+ d_hex_to_int['6'] = 6;
+ d_hex_to_int['a'] = 0xa;
+ d_hex_to_int['e'] = 0xe;
+ d_hex_to_int['3'] = 3;
+ d_hex_to_int['7'] = 7;
+ d_hex_to_int['b'] = 0xb;
+ d_hex_to_int['f'] = 0xf;
+ read_key(key);
+ /* Initialize GSM Stack */
+ GS_new(&d_gs_ctx); //TODO: remove it! it's not a right place for a decoder
+}
+
+/*
+ * Virtual destructor.
+ */
+gsm_receiver_cf::~gsm_receiver_cf()
+{
+}
+
+void gsm_receiver_cf::forecast(int noutput_items, gr_vector_int &nitems_items_required)
+{
+ nitems_items_required[0] = noutput_items * floor((TS_BITS + 2 * GUARD_PERIOD) * d_OSR);
+}
+
+int
+gsm_receiver_cf::general_work(int noutput_items,
+ gr_vector_int &nitems_items,
+ gr_vector_const_void_star &input_items,
+ gr_vector_void_star &output_items)
+{
+ const gr_complex *input = (const gr_complex *) input_items[0];
+ //float *out = (float *) output_items[0];
+ int produced_out = 0; //how many output elements were produced - this isn't used yet
+ //probably the gsm receiver will be changed into sink so this variable won't be necessary
+
+ switch (d_state) {
+ //bootstrapping
+ case first_fcch_search:
+ if (find_fcch_burst(input, nitems_items[0])) { //find frequency correction burst in the input buffer
+ set_frequency(d_freq_offset); //if fcch search is successful set frequency offset
+ //produced_out = 0;
+ d_state = next_fcch_search;
+ } else {
+ //produced_out = 0;
+ d_state = first_fcch_search;
+ }
+ break;
+
+ case next_fcch_search: { //this state is used because it takes some time (a bunch of buffered samples)
+ float prev_freq_offset = d_freq_offset; //before previous set_frequqency cause change
+ if (find_fcch_burst(input, nitems_items[0])) {
+ if (abs(prev_freq_offset - d_freq_offset) > FCCH_MAX_FREQ_OFFSET) {
+ set_frequency(d_freq_offset); //call set_frequncy only frequency offset change is greater than some value
+ }
+ //produced_out = 0;
+ d_state = sch_search;
+ } else {
+ //produced_out = 0;
+ d_state = next_fcch_search;
+ }
+ break;
+ }
+
+ case sch_search: {
+ vector_complex channel_imp_resp(CHAN_IMP_RESP_LENGTH*d_OSR);
+ int t1, t2, t3;
+ int burst_start = 0;
+ unsigned char output_binary[BURST_SIZE];
+
+ if (reach_sch_burst(nitems_items[0])) { //wait for a SCH burst
+ burst_start = get_sch_chan_imp_resp(input, &channel_imp_resp[0]); //get channel impulse response from it
+ detect_burst(input, &channel_imp_resp[0], burst_start, output_binary); //detect bits using MLSE detection
+ if (decode_sch(&output_binary[3], &t1, &t2, &t3, &d_ncc, &d_bcc) == 0) { //decode SCH burst
+ DCOUT("sch burst_start: " << burst_start);
+ DCOUT("bcc: " << d_bcc << " ncc: " << d_ncc << " t1: " << t1 << " t2: " << t2 << " t3: " << t3);
+ d_burst_nr.set(t1, t2, t3, 0); //set counter of bursts value
+
+ //configure the receiver - tell him where to find which burst type
+ d_channel_conf.set_multiframe_type(TIMESLOT0, multiframe_51); //in the timeslot nr.0 bursts changes according to t3 counter
+ configure_receiver();//TODO: this shouldn't be here - remove it when gsm receiver's interface will be ready
+ d_channel_conf.set_burst_types(TIMESLOT0, FCCH_FRAMES, sizeof(FCCH_FRAMES) / sizeof(unsigned), fcch_burst); //tell where to find fcch bursts
+ d_channel_conf.set_burst_types(TIMESLOT0, SCH_FRAMES, sizeof(SCH_FRAMES) / sizeof(unsigned), sch_burst); //sch bursts
+ d_channel_conf.set_burst_types(TIMESLOT0, BCCH_FRAMES, sizeof(BCCH_FRAMES) / sizeof(unsigned), normal_burst);//!and maybe normal bursts of the BCCH logical channel
+ d_burst_nr++;
+
+ consume_each(burst_start + BURST_SIZE * d_OSR); //consume samples up to next guard period
+ d_state = synchronized;
+ } else {
+ d_state = next_fcch_search; //if there is error in the sch burst go back to fcch search phase
+ }
+ } else {
+ d_state = sch_search;
+ }
+ break;
+ }
+ //in this state receiver is synchronized and it processes bursts according to burst type for given burst number
+ case synchronized: {
+ vector_complex channel_imp_resp(CHAN_IMP_RESP_LENGTH*d_OSR);
+ int burst_start;
+ int offset = 0;
+ int to_consume = 0;
+ unsigned char output_binary[BURST_SIZE];
+
+ burst_type b_type = d_channel_conf.get_burst_type(d_burst_nr); //get burst type for given burst number
+
+ switch (b_type) {
+ case fcch_burst: { //if it's FCCH burst
+ const unsigned first_sample = ceil((GUARD_PERIOD + 2 * TAIL_BITS) * d_OSR) + 1;
+ const unsigned last_sample = first_sample + USEFUL_BITS * d_OSR - TAIL_BITS * d_OSR;
+ double freq_offset = compute_freq_offset(input, first_sample, last_sample); //extract frequency offset from it
+
+ d_freq_offset_vals.push_front(freq_offset);
+
+ if (d_freq_offset_vals.size() >= 10) {
+ double sum = std::accumulate(d_freq_offset_vals.begin(), d_freq_offset_vals.end(), 0);
+ double mean_offset = sum / d_freq_offset_vals.size(); //compute mean
+ d_freq_offset_vals.clear();
+ if (abs(mean_offset) > FCCH_MAX_FREQ_OFFSET) {
+ d_freq_offset -= mean_offset; //and adjust frequency if it have changed beyond
+ set_frequency(d_freq_offset); //some limit
+ DCOUT("mean_offset: " << mean_offset);
+ DCOUT("Adjusting frequency, new frequency offset: " << d_freq_offset << "\n");
+ }
+ }
+ }
+ break;
+ case sch_burst: { //if it's SCH burst
+ int t1, t2, t3, d_ncc, d_bcc;
+ burst_start = get_sch_chan_imp_resp(input, &channel_imp_resp[0]); //get channel impulse response
+ detect_burst(input, &channel_imp_resp[0], burst_start, output_binary); //MLSE detection of bits
+ if (decode_sch(&output_binary[3], &t1, &t2, &t3, &d_ncc, &d_bcc) == 0) { //and decode SCH data
+ // d_burst_nr.set(t1, t2, t3, 0); //but only to check if burst_start value is correct
+ d_failed_sch = 0;
+ DCOUT("bcc: " << d_bcc << " ncc: " << d_ncc << " t1: " << t1 << " t2: " << t2 << " t3: " << t3);
+ offset = burst_start - floor((GUARD_PERIOD) * d_OSR); //compute offset from burst_start - burst should start after a guard period
+ DCOUT(offset);
+ to_consume += offset; //adjust with offset number of samples to be consumed
+ } else {
+ d_failed_sch++;
+ if (d_failed_sch >= MAX_SCH_ERRORS) {
+// d_state = next_fcch_search; //TODO: this isn't good, the receiver is going wild when it goes back to next_fcch_search from here
+// d_freq_offset_vals.clear();
+ DCOUT("many sch decoding errors");
+ }
+ }
+ }
+ break;
+
+ case normal_burst: //if it's normal burst
+ burst_start = get_norm_chan_imp_resp(input, &channel_imp_resp[0], d_bcc); //get channel impulse response for given training sequence number - d_bcc
+ detect_burst(input, &channel_imp_resp[0], burst_start, output_binary); //MLSE detection of bits
+ process_normal_burst(d_burst_nr, output_binary); //TODO: this shouldn't be here - remove it when gsm receiver's interface will be ready
+ break;
+
+ case dummy_or_normal: {
+ burst_start = get_norm_chan_imp_resp(input, &channel_imp_resp[0], TS_DUMMY);
+ detect_burst(input, &channel_imp_resp[0], burst_start, output_binary);
+
+ std::vector<unsigned char> v(20);
+ std::vector<unsigned char>::iterator it;
+ it = std::set_difference(output_binary + TRAIN_POS, output_binary + TRAIN_POS + 16, &train_seq[TS_DUMMY][5], &train_seq[TS_DUMMY][21], v.begin());
+ int different_bits = (it - v.begin());
+
+ if (different_bits > 2) {
+ burst_start = get_norm_chan_imp_resp(input, &channel_imp_resp[0], d_bcc);
+ detect_burst(input, &channel_imp_resp[0], burst_start, output_binary);
+ if (!output_binary[0] && !output_binary[1] && !output_binary[2]) {
+ process_normal_burst(d_burst_nr, output_binary); //TODO: this shouldn't be here - remove it when gsm receiver's interface will be ready
+ }
+ }
+ }
+ case rach_burst:
+ //implementation of this channel isn't possible in current gsm_receiver
+ //it would take some realtime processing, counter of samples from USRP to
+ //stay synchronized with this device and possibility to switch frequency from uplink
+ //to C0 (where sch is) back and forth
+
+ break;
+ case dummy: //if it's dummy
+ burst_start = get_norm_chan_imp_resp(input, &channel_imp_resp[0], TS_DUMMY); //read dummy
+ detect_burst(input, &channel_imp_resp[0], burst_start, output_binary); // but as far as I know it's pointless
+ break;
+ case empty: //if it's empty burst
+ break; //do nothing
+ }
+
+ d_burst_nr++; //go to next burst
+
+ to_consume += TS_BITS * d_OSR + d_burst_nr.get_offset(); //consume samples of the burst up to next guard period
+ //and add offset which is introduced by
+ //0.25 fractional part of a guard period
+ //burst_number computes this offset
+ //but choice of this class to do this was random
+ consume_each(to_consume);
+ }
+ break;
+ }
+
+ return produced_out;
+}
+
+bool gsm_receiver_cf::find_fcch_burst(const gr_complex *input, const int nitems)
+{
+ circular_buffer_float phase_diff_buffer(FCCH_HITS_NEEDED * d_OSR); //circular buffer used to scan throug signal to find
+ //best match for FCCH burst
+ float phase_diff = 0;
+ gr_complex conjprod;
+ int start_pos = -1;
+ int hit_count = 0;
+ int miss_count = 0;
+ float min_phase_diff;
+ float max_phase_diff;
+ double best_sum = 0;
+ float lowest_max_min_diff = 99999;
+
+ int to_consume = 0;
+ int sample_number = 0;
+ bool end = false;
+ bool result = false;
+ circular_buffer_float::iterator buffer_iter;
+
+ /**@name Possible states of FCCH search algorithm*/
+ //@{
+ enum states {
+ init, ///< initialize variables
+ search, ///< search for positive samples
+ found_something, ///< search for FCCH and the best position of it
+ fcch_found, ///< when FCCH was found
+ search_fail ///< when there is no FCCH in the input vector
+ } fcch_search_state;
+ //@}
+
+ fcch_search_state = init;
+
+ while (!end) {
+ switch (fcch_search_state) {
+
+ case init: //initialize variables
+ hit_count = 0;
+ miss_count = 0;
+ start_pos = -1;
+ lowest_max_min_diff = 99999;
+ phase_diff_buffer.clear();
+ fcch_search_state = search;
+
+ break;
+
+ case search: // search for positive samples
+ sample_number++;
+
+ if (sample_number > nitems - FCCH_HITS_NEEDED * d_OSR) { //if it isn't possible to find FCCH because
+ //there's too few samples left to look into,
+ to_consume = sample_number; //don't do anything with those samples which are left
+ //and consume only those which were checked
+ fcch_search_state = search_fail;
+ } else {
+ phase_diff = compute_phase_diff(input[sample_number], input[sample_number-1]);
+
+ if (phase_diff > 0) { //if a positive phase difference was found
+ to_consume = sample_number;
+ fcch_search_state = found_something; //switch to state in which searches for FCCH
+ } else {
+ fcch_search_state = search;
+ }
+ }
+
+ break;
+
+ case found_something: {// search for FCCH and the best position of it
+ if (phase_diff > 0) {
+ hit_count++; //positive phase differencies increases hits_count
+ } else {
+ miss_count++; //negative increases miss_count
+ }
+
+ if ((miss_count >= FCCH_MAX_MISSES * d_OSR) && (hit_count <= FCCH_HITS_NEEDED * d_OSR)) {
+ //if miss_count exceeds limit before hit_count
+ fcch_search_state = init; //go to init
+ continue;
+ } else if (((miss_count >= FCCH_MAX_MISSES * d_OSR) && (hit_count > FCCH_HITS_NEEDED * d_OSR)) || (hit_count > 2 * FCCH_HITS_NEEDED * d_OSR)) {
+ //if hit_count and miss_count exceeds limit then FCCH was found
+ fcch_search_state = fcch_found;
+ continue;
+ } else if ((miss_count < FCCH_MAX_MISSES * d_OSR) && (hit_count > FCCH_HITS_NEEDED * d_OSR)) {
+ //find difference between minimal and maximal element in the buffer
+ //for FCCH this value should be low
+ //this part is searching for a region where this value is lowest
+ min_phase_diff = * (min_element(phase_diff_buffer.begin(), phase_diff_buffer.end()));
+ max_phase_diff = * (max_element(phase_diff_buffer.begin(), phase_diff_buffer.end()));
+
+ if (lowest_max_min_diff > max_phase_diff - min_phase_diff) {
+ lowest_max_min_diff = max_phase_diff - min_phase_diff;
+ start_pos = sample_number - FCCH_HITS_NEEDED * d_OSR - FCCH_MAX_MISSES * d_OSR; //store start pos
+ best_sum = 0;
+
+ for (buffer_iter = phase_diff_buffer.begin();
+ buffer_iter != (phase_diff_buffer.end());
+ buffer_iter++) {
+ best_sum += *buffer_iter - (M_PI / 2) / d_OSR; //store best value of phase offset sum
+ }
+ }
+ }
+
+ sample_number++;
+
+ if (sample_number >= nitems) { //if there's no single sample left to check
+ fcch_search_state = search_fail;//FCCH search failed
+ continue;
+ }
+
+ phase_diff = compute_phase_diff(input[sample_number], input[sample_number-1]);
+ phase_diff_buffer.push_back(phase_diff);
+ fcch_search_state = found_something;
+ }
+ break;
+
+ case fcch_found: {
+ DCOUT("fcch found on position: " << d_counter + start_pos);
+ to_consume = start_pos + FCCH_HITS_NEEDED * d_OSR + 1; //consume one FCCH burst
+
+ d_fcch_start_pos = d_counter + start_pos;
+
+ //compute frequency offset
+ double phase_offset = best_sum / FCCH_HITS_NEEDED;
+ double freq_offset = phase_offset * 1625000.0 / (12.0 * M_PI);
+ d_freq_offset -= freq_offset;
+ DCOUT("freq_offset: " << d_freq_offset);
+
+ end = true;
+ result = true;
+ break;
+ }
+
+ case search_fail:
+ end = true;
+ result = false;
+ break;
+ }
+ }
+
+ d_counter += to_consume;
+ consume_each(to_consume);
+
+ return result;
+}
+
+double gsm_receiver_cf::compute_freq_offset(const gr_complex * input, unsigned first_sample, unsigned last_sample)
+{
+ double phase_sum = 0;
+ unsigned ii;
+
+ for (ii = first_sample; ii < last_sample; ii++) {
+ double phase_diff = compute_phase_diff(input[ii], input[ii-1]) - (M_PI / 2) / d_OSR;
+ phase_sum += phase_diff;
+ }
+
+ double phase_offset = phase_sum / (last_sample - first_sample);
+ double freq_offset = phase_offset * 1625000.0 / (12.0 * M_PI);
+ return freq_offset;
+}
+
+void gsm_receiver_cf::set_frequency(double freq_offset)
+{
+ d_tuner->calleval(freq_offset);
+}
+
+inline float gsm_receiver_cf::compute_phase_diff(gr_complex val1, gr_complex val2)
+{
+ gr_complex conjprod = val1 * conj(val2);
+ return gr_fast_atan2f(imag(conjprod), real(conjprod));
+}
+
+bool gsm_receiver_cf::reach_sch_burst(const int nitems)
+{
+ //it just consumes samples to get near to a SCH burst
+ int to_consume = 0;
+ bool result = false;
+ unsigned sample_nr_near_sch_start = d_fcch_start_pos + (FRAME_BITS - SAFETY_MARGIN) * d_OSR;
+
+ //consume samples until d_counter will be equal to sample_nr_near_sch_start
+ if (d_counter < sample_nr_near_sch_start) {
+ if (d_counter + nitems >= sample_nr_near_sch_start) {
+ to_consume = sample_nr_near_sch_start - d_counter;
+ } else {
+ to_consume = nitems;
+ }
+ result = false;
+ } else {
+ to_consume = 0;
+ result = true;
+ }
+
+ d_counter += to_consume;
+ consume_each(to_consume);
+ return result;
+}
+
+int gsm_receiver_cf::get_sch_chan_imp_resp(const gr_complex *input, gr_complex * chan_imp_resp)
+{
+ vector_complex correlation_buffer;
+ vector_float power_buffer;
+ vector_float window_energy_buffer;
+
+ int strongest_window_nr;
+ int burst_start = 0;
+ int chan_imp_resp_center = 0;
+ float max_correlation = 0;
+ float energy = 0;
+
+ for (int ii = SYNC_POS * d_OSR; ii < (SYNC_POS + SYNC_SEARCH_RANGE) *d_OSR; ii++) {
+ gr_complex correlation = correlate_sequence(&d_sch_training_seq[5], N_SYNC_BITS - 10, &input[ii]);
+ correlation_buffer.push_back(correlation);
+ power_buffer.push_back(pow(abs(correlation), 2));
+ }
+
+ //compute window energies
+ vector_float::iterator iter = power_buffer.begin();
+ bool loop_end = false;
+ while (iter != power_buffer.end()) {
+ vector_float::iterator iter_ii = iter;
+ energy = 0;
+
+ for (int ii = 0; ii < (d_chan_imp_length) *d_OSR; ii++, iter_ii++) {
+ if (iter_ii == power_buffer.end()) {
+ loop_end = true;
+ break;
+ }
+ energy += (*iter_ii);
+ }
+ if (loop_end) {
+ break;
+ }
+ iter++;
+ window_energy_buffer.push_back(energy);
+ }
+
+ strongest_window_nr = max_element(window_energy_buffer.begin(), window_energy_buffer.end()) - window_energy_buffer.begin();
+// d_channel_imp_resp.clear();
+
+ max_correlation = 0;
+ for (int ii = 0; ii < (d_chan_imp_length) *d_OSR; ii++) {
+ gr_complex correlation = correlation_buffer[strongest_window_nr + ii];
+ if (abs(correlation) > max_correlation) {
+ chan_imp_resp_center = ii;
+ max_correlation = abs(correlation);
+ }
+// d_channel_imp_resp.push_back(correlation);
+ chan_imp_resp[ii] = correlation;
+ }
+
+ burst_start = strongest_window_nr + chan_imp_resp_center - 48 * d_OSR - 2 * d_OSR + 2 + SYNC_POS * d_OSR;
+ return burst_start;
+}
+
+void gsm_receiver_cf::detect_burst(const gr_complex * input, gr_complex * chan_imp_resp, int burst_start, unsigned char * output_binary)
+{
+ float output[BURST_SIZE];
+ gr_complex rhh_temp[CHAN_IMP_RESP_LENGTH*d_OSR];
+ gr_complex rhh[CHAN_IMP_RESP_LENGTH];
+ gr_complex filtered_burst[BURST_SIZE];
+ int start_state = 3;
+ unsigned int stop_states[2] = {4, 12};
+
+ autocorrelation(chan_imp_resp, rhh_temp, d_chan_imp_length*d_OSR);
+ for (int ii = 0; ii < (d_chan_imp_length); ii++) {
+ rhh[ii] = conj(rhh_temp[ii*d_OSR]);
+ }
+
+ mafi(&input[burst_start], BURST_SIZE, chan_imp_resp, d_chan_imp_length*d_OSR, filtered_burst);
+
+ viterbi_detector(filtered_burst, BURST_SIZE, rhh, start_state, stop_states, 2, output);
+
+ for (int i = 0; i < BURST_SIZE ; i++) {
+ output_binary[i] = (output[i] > 0);
+ }
+}
+
+//TODO consider placing this funtion in a separate class for signal processing
+void gsm_receiver_cf::gmsk_mapper(const unsigned char * input, int nitems, gr_complex * gmsk_output, gr_complex start_point)
+{
+ gr_complex j = gr_complex(0.0, 1.0);
+
+ int current_symbol;
+ int encoded_symbol;
+ int previous_symbol = 2 * input[0] - 1;
+ gmsk_output[0] = start_point;
+
+ for (int i = 1; i < nitems; i++) {
+ //change bits representation to NRZ
+ current_symbol = 2 * input[i] - 1;
+ //differentially encode
+ encoded_symbol = current_symbol * previous_symbol;
+ //and do gmsk mapping
+ gmsk_output[i] = j * gr_complex(encoded_symbol, 0.0) * gmsk_output[i-1];
+ previous_symbol = current_symbol;
+ }
+}
+
+//TODO consider use of some generalized function for correlation and placing it in a separate class for signal processing
+gr_complex gsm_receiver_cf::correlate_sequence(const gr_complex * sequence, int length, const gr_complex * input)
+{
+ gr_complex result(0.0, 0.0);
+ int sample_number = 0;
+
+ for (int ii = 0; ii < length; ii++) {
+ sample_number = (ii * d_OSR) ;
+ result += sequence[ii] * conj(input[sample_number]);
+ }
+
+ result = result / gr_complex(length, 0);
+ return result;
+}
+
+//computes autocorrelation for positive arguments
+//TODO consider placing this funtion in a separate class for signal processing
+inline void gsm_receiver_cf::autocorrelation(const gr_complex * input, gr_complex * out, int nitems)
+{
+ int i, k;
+ for (k = nitems - 1; k >= 0; k--) {
+ out[k] = gr_complex(0, 0);
+ for (i = k; i < nitems; i++) {
+ out[k] += input[i] * conj(input[i-k]);
+ }
+ }
+}
+
+//TODO consider use of some generalized function for filtering and placing it in a separate class for signal processing
+inline void gsm_receiver_cf::mafi(const gr_complex * input, int nitems, gr_complex * filter, int filter_length, gr_complex * output)
+{
+ int ii = 0, n, a;
+
+ for (n = 0; n < nitems; n++) {
+ a = n * d_OSR;
+ output[n] = 0;
+ ii = 0;
+
+ while (ii < filter_length) {
+ if ((a + ii) >= nitems*d_OSR)
+ break;
+ output[n] += input[a+ii] * filter[ii];
+ ii++;
+ }
+ }
+}
+
+//TODO: get_norm_chan_imp_resp is similar to get_sch_chan_imp_resp - consider joining this two functions
+//TODO: this is place where most errors are introduced and can be corrected by improvements to this fuction
+//especially computations of strongest_window_nr
+int gsm_receiver_cf::get_norm_chan_imp_resp(const gr_complex *input, gr_complex * chan_imp_resp, int bcc)
+{
+ vector_complex correlation_buffer;
+ vector_float power_buffer;
+ vector_float window_energy_buffer;
+
+ int strongest_window_nr;
+ int burst_start = 0;
+ int chan_imp_resp_center = 0;
+ float max_correlation = 0;
+ float energy = 0;
+
+ int search_center = (int)((TRAIN_POS + GUARD_PERIOD) * d_OSR);
+ int search_start_pos = search_center + 1;
+// int search_start_pos = search_center - d_chan_imp_length * d_OSR;
+ int search_stop_pos = search_center + d_chan_imp_length * d_OSR + 2 * d_OSR;
+
+ for (int ii = search_start_pos; ii < search_stop_pos; ii++) {
+ gr_complex correlation = correlate_sequence(&d_norm_training_seq[bcc][TRAIN_BEGINNING], N_TRAIN_BITS - 10, &input[ii]);
+
+ correlation_buffer.push_back(correlation);
+ power_buffer.push_back(pow(abs(correlation), 2));
+ }
+
+ //compute window energies
+ vector_float::iterator iter = power_buffer.begin();
+ bool loop_end = false;
+ while (iter != power_buffer.end()) {
+ vector_float::iterator iter_ii = iter;
+ energy = 0;
+
+ for (int ii = 0; ii < (d_chan_imp_length - 2)*d_OSR; ii++, iter_ii++) {
+// for (int ii = 0; ii < (d_chan_imp_length)*d_OSR; ii++, iter_ii++) {
+ if (iter_ii == power_buffer.end()) {
+ loop_end = true;
+ break;
+ }
+ energy += (*iter_ii);
+ }
+ if (loop_end) {
+ break;
+ }
+ iter++;
+
+ window_energy_buffer.push_back(energy);
+ }
+ //!why doesn't this work
+ strongest_window_nr = max_element(window_energy_buffer.begin(), window_energy_buffer.end()) - window_energy_buffer.begin();
+ strongest_window_nr = 3; //! so I have to override it here
+
+ max_correlation = 0;
+ for (int ii = 0; ii < (d_chan_imp_length)*d_OSR; ii++) {
+ gr_complex correlation = correlation_buffer[strongest_window_nr + ii];
+ if (abs(correlation) > max_correlation) {
+ chan_imp_resp_center = ii;
+ max_correlation = abs(correlation);
+ }
+// d_channel_imp_resp.push_back(correlation);
+ chan_imp_resp[ii] = correlation;
+ }
+ // We want to use the first sample of the impulseresponse, and the
+ // corresponding samples of the received signal.
+ // the variable sync_w should contain the beginning of the used part of
+ // training sequence, which is 3+57+1+6=67 bits into the burst. That is
+ // we have that sync_t16 equals first sample in bit number 67.
+
+ burst_start = search_start_pos + chan_imp_resp_center + strongest_window_nr - TRAIN_POS * d_OSR;
+
+ // GMSK modulator introduces ISI - each bit is expanded for 3*Tb
+ // and it's maximum value is in the last bit period, so burst starts
+ // 2*Tb earlier
+ burst_start -= 2 * d_OSR;
+ burst_start += 2;
+ //std::cout << " burst_start: " << burst_start << " center: " << ((float)(search_start_pos + strongest_window_nr + chan_imp_resp_center)) / d_OSR << " stronegest window nr: " << strongest_window_nr << "\n";
+
+ return burst_start;
+}
+
diff --git a/gsm-receiver/src/lib/gsm_receiver_cf.h b/gsm-receiver/src/lib/gsm_receiver_cf.h
new file mode 100644
index 0000000..21cb1ff
--- /dev/null
+++ b/gsm-receiver/src/lib/gsm_receiver_cf.h
@@ -0,0 +1,254 @@
+/* -*- c++ -*- */
+/*
+ * @file
+ * @author Piotr Krysik <pkrysik@stud.elka.pw.edu.pl>
+ * @section LICENSE
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 3, or (at your option)
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; see the file COPYING. If not, write to
+ * the Free Software Foundation, Inc., 51 Franklin Street,
+ * Boston, MA 02110-1301, USA.
+ */
+#ifndef INCLUDED_GSM_RECEIVER_CF_H
+#define INCLUDED_GSM_RECEIVER_CF_H
+
+#include <vector>
+#include <list>
+#include <gr_block.h>
+#include <gr_complex.h>
+#include <gr_feval.h>
+#include <gsm_constants.h>
+#include <gsm_receiver_config.h>
+
+#include <gsmstack.h> //TODO: remember to remove this line in the future!
+#include "GSML1FEC.h" //!!
+#include <a5-1-2.h>//!!
+#include <string>//!!
+#include <map>//!!
+
+class gsm_receiver_cf;
+
+typedef boost::shared_ptr<gsm_receiver_cf> gsm_receiver_cf_sptr;
+typedef std::vector<gr_complex> vector_complex;
+
+gsm_receiver_cf_sptr gsm_make_receiver_cf(gr_feval_dd *tuner, gr_feval_dd *synchronizer, int osr, std::string key);
+
+/** GSM Receiver GNU Radio block
+ *
+ * GSM Receiver class supports frequency correction, synchronisation and
+ * MLSE (Maximum Likelihood Sequence Estimation) estimation of synchronisation
+ * bursts and normal bursts.
+ * \ingroup block
+ */
+
+class gsm_receiver_cf : public gr_block
+{
+ private:
+ std::map<char,int> d_hex_to_int;
+ FILE * d_gsm_file; //!!
+ byte d_KC[8]; //!!
+ GSM::TCHFACCHL1Decoder d_tch_decoder1; //!!
+ GSM::TCHFACCHL1Decoder d_tch_decoder2; //!!
+ GSM::TCHFACCHL1Decoder d_tch_decoder3; //!!
+ GSM::TCHFACCHL1Decoder d_tch_decoder4; //!!
+ GSM::TCHFACCHL1Decoder d_tch_decoder5; //!!
+ GSM::TCHFACCHL1Decoder d_tch_decoder6; //!!
+ GSM::TCHFACCHL1Decoder d_tch_decoder7; //!!
+ /**@name Configuration of the receiver */
+ //@{
+ const int d_OSR; ///< oversampling ratio
+ const int d_chan_imp_length; ///< channel impulse length
+ //@}
+
+ gr_complex d_sch_training_seq[N_SYNC_BITS]; ///<encoded training sequence of a SCH burst
+ gr_complex d_norm_training_seq[TRAIN_SEQ_NUM][N_TRAIN_BITS]; ///<encoded training sequences of a normal bursts and dummy bursts
+
+ gr_feval_dd *d_tuner; ///<callback to a python object which is used for frequency tunning
+ gr_feval_dd *d_synchronizer; ///<callback to a python object which is used to correct offset of USRP's internal clock
+
+ /** Countes samples consumed by the receiver
+ *
+ * It is used in beetween find_fcch_burst and reach_sch_burst calls.
+ * My intention was to synchronize this counter with some internal sample
+ * counter of the USRP. Simple access to such USRP's counter isn't possible
+ * so this variable isn't used in the "synchronized" state of the receiver yet.
+ */
+ unsigned d_counter;
+
+ /**@name Variables used to store result of the find_fcch_burst fuction */
+ //@{
+ unsigned d_fcch_start_pos; ///< position of the first sample of the fcch burst
+ float d_freq_offset; ///< frequency offset of the received signal
+ //@}
+ std::list<double> d_freq_offset_vals;
+
+ /**@name Identifiers of the BTS extracted from the SCH burst */
+ //@{
+ int d_ncc; ///< network color code
+ int d_bcc; ///< base station color code
+ //@}
+
+ /**@name Internal state of the gsm receiver */
+ //@{
+ enum states {
+ first_fcch_search, next_fcch_search, sch_search, // synchronization search part
+ synchronized // receiver is synchronized in this state
+ } d_state;
+ //@}
+
+ /**@name Variables which make internal state in the "synchronized" state */
+ //@{
+ burst_counter d_burst_nr; ///< frame number and timeslot number
+ channel_configuration d_channel_conf; ///< mapping of burst_counter to burst_type
+ //@}
+
+ unsigned d_failed_sch; ///< number of subsequent erroneous SCH bursts
+
+ // GSM Stack
+ GS_CTX d_gs_ctx;//TODO: remove it! it'a not right place for a decoder
+
+ friend gsm_receiver_cf_sptr gsm_make_receiver_cf(gr_feval_dd *tuner, gr_feval_dd *synchronizer, int osr, std::string key);
+ gsm_receiver_cf(gr_feval_dd *tuner, gr_feval_dd *synchronizer, int osr, std::string key);
+
+ /** Function whis is used to search a FCCH burst and to compute frequency offset before
+ * "synchronized" state of the receiver
+ *
+ * TODO: Describe the FCCH search algorithm in the documentation
+ * @param input vector with input signal
+ * @param nitems number of samples in the input vector
+ * @return
+ */
+ bool find_fcch_burst(const gr_complex *input, const int nitems);
+
+ /** Computes frequency offset from FCCH burst samples
+ *
+ * @param input vector with input samples
+ * @param first_sample number of the first sample of the FCCH busrt
+ * @param last_sample number of the last sample of the FCCH busrt
+ * @return frequency offset
+ */
+ double compute_freq_offset(const gr_complex * input, unsigned first_sample, unsigned last_sample);
+
+ /** Calls d_tuner's method to set frequency offset from Python level
+ *
+ * @param freq_offset absolute frequency offset of the received signal
+ */
+ void set_frequency(double freq_offset);
+
+ /** Computes angle between two complex numbers
+ *
+ * @param val1 first complex number
+ * @param val2 second complex number
+ * @return
+ */
+ inline float compute_phase_diff(gr_complex val1, gr_complex val2);
+
+ /** Function whis is used to get near to SCH burst
+ *
+ * @param nitems number of samples in the gsm_receiver's buffer
+ * @return true if SCH burst is near, false otherwise
+ */
+ bool reach_sch_burst(const int nitems);
+
+ /** Extracts channel impulse response from a SCH burst and computes first sample number of this burst
+ *
+ * @param input vector with input samples
+ * @param chan_imp_resp complex vector where channel impulse response will be stored
+ * @return number of first sample of the burst
+ */
+ int get_sch_chan_imp_resp(const gr_complex *input, gr_complex * chan_imp_resp);
+
+ /** MLSE detection of a burst bits
+ *
+ * Detects bits of burst using viterbi algorithm.
+ * @param input vector with input samples
+ * @param chan_imp_resp vector with the channel impulse response
+ * @param burst_start number of the first sample of the burst
+ * @param output_binary vector with output bits
+ */
+ void detect_burst(const gr_complex * input, gr_complex * chan_imp_resp, int burst_start, unsigned char * output_binary);
+
+ /** Encodes differentially input bits and maps them into MSK states
+ *
+ * @param input vector with input bits
+ * @param nitems number of samples in the "input" vector
+ * @param gmsk_output bits mapped into MSK states
+ * @param start_point first state
+ */
+ void gmsk_mapper(const unsigned char * input, int nitems, gr_complex * gmsk_output, gr_complex start_point);
+
+ /** Correlates MSK mapped sequence with input signal
+ *
+ * @param sequence MKS mapped sequence
+ * @param length length of the sequence
+ * @param input_signal vector with input samples
+ * @return correlation value
+ */
+ gr_complex correlate_sequence(const gr_complex * sequence, int length, const gr_complex * input);
+
+ /** Computes autocorrelation of input vector for positive arguments
+ *
+ * @param input vector with input samples
+ * @param out output vector
+ * @param nitems length of the input vector
+ */
+ inline void autocorrelation(const gr_complex * input, gr_complex * out, int nitems);
+
+ /** Filters input signal through channel impulse response
+ *
+ * @param input vector with input samples
+ * @param nitems number of samples to pass through filter
+ * @param filter filter taps - channel impulse response
+ * @param filter_length nember of filter taps
+ * @param output vector with filtered samples
+ */
+ inline void mafi(const gr_complex * input, int nitems, gr_complex * filter, int filter_length, gr_complex * output);
+
+ /** Extracts channel impulse response from a normal burst and computes first sample number of this burst
+ *
+ * @param input vector with input samples
+ * @param chan_imp_resp complex vector where channel impulse response will be stored
+ * @param search_range possible absolute offset of a channel impulse response start
+ * @param bcc base station color code - number of a training sequence
+ * @return first sample number of normal burst
+ */
+ int get_norm_chan_imp_resp(const gr_complex * input, gr_complex * chan_imp_resp, int bcc);
+
+
+ /**
+ *
+ */
+ void read_key(std::string key);
+
+ /**
+ *
+ */
+ void process_normal_burst(burst_counter burst_nr, const unsigned char * burst_binary);
+
+ /**
+ *
+ */
+ void configure_receiver();
+
+
+
+ public:
+ ~gsm_receiver_cf();
+ void forecast(int noutput_items, gr_vector_int &ninput_items_required);
+ int general_work(int noutput_items,
+ gr_vector_int &ninput_items,
+ gr_vector_const_void_star &input_items,
+ gr_vector_void_star &output_items);
+};
+
+#endif /* INCLUDED_GSM_RECEIVER_CF_H */
diff --git a/gsm-receiver/src/lib/gsm_receiver_config.cc b/gsm-receiver/src/lib/gsm_receiver_config.cc
new file mode 100644
index 0000000..44344f7
--- /dev/null
+++ b/gsm-receiver/src/lib/gsm_receiver_config.cc
@@ -0,0 +1,84 @@
+/* -*- c++ -*- */
+/*
+ * @file
+ * @author Piotr Krysik <pkrysik@stud.elka.pw.edu.pl>
+ * @section LICENSE
+ *
+ * GNU Radio is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 3, or (at your option)
+ * any later version.
+ *
+ * GNU Radio is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with GNU Radio; see the file COPYING. If not, write to
+ * the Free Software Foundation, Inc., 51 Franklin Street,
+ * Boston, MA 02110-1301, USA.
+ *
+ * @section DESCRIPTION
+ * This file contains classes which define gsm_receiver configuration
+ * and the burst_counter which is used to store internal state of the receiver
+ * when it's synchronized
+ */
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include <gsm_receiver_config.h>
+
+burst_counter & burst_counter::operator++(int)
+{
+ d_timeslot_nr++;
+ if (d_timeslot_nr == TS_PER_FRAME) {
+ d_timeslot_nr = 0;
+
+ if ((d_t2 == 25) && (d_t3 == 50)) {
+ d_t1 = (d_t1 + 1) % (1 << 11);
+ }
+
+ d_t2 = (d_t2 + 1) % 26;
+ d_t3 = (d_t3 + 1) % 51;
+ }
+
+ //update offset - this is integer for d_OSR which is multiple of four
+ d_offset_fractional += GUARD_FRACTIONAL * d_OSR;
+ d_offset_integer = floor(d_offset_fractional);
+ d_offset_fractional = d_offset_fractional - d_offset_integer;
+ return (*this);
+}
+
+void burst_counter::set(uint32_t t1, uint32_t t2, uint32_t t3, uint32_t timeslot_nr)
+{
+ d_t1 = t1;
+ d_t2 = t2;
+ d_t3 = t3;
+ d_timeslot_nr = timeslot_nr;
+ double first_sample_position = (get_frame_nr() * 8 + timeslot_nr) * TS_BITS;
+ d_offset_fractional = first_sample_position - floor(first_sample_position);
+ d_offset_integer = 0;
+}
+
+burst_type channel_configuration::get_burst_type(burst_counter burst_nr)
+{
+ uint32_t timeslot_nr = burst_nr.get_timeslot_nr();
+ multiframe_type m_type = d_timeslots_descriptions[timeslot_nr].get_type();
+ uint32_t nr;
+
+ switch (m_type) {
+ case multiframe_26:
+ nr = burst_nr.get_t2();
+ break;
+ case multiframe_51:
+ nr = burst_nr.get_t3();
+ break;
+ default:
+ nr = 0;
+ break;
+ }
+
+ return d_timeslots_descriptions[timeslot_nr].get_burst_type(nr);
+}
diff --git a/gsm-receiver/src/lib/gsm_receiver_config.h b/gsm-receiver/src/lib/gsm_receiver_config.h
new file mode 100644
index 0000000..b7ba43a
--- /dev/null
+++ b/gsm-receiver/src/lib/gsm_receiver_config.h
@@ -0,0 +1,164 @@
+/* -*- c++ -*- */
+/*
+ * @file
+ * @author Piotr Krysik <pkrysik@stud.elka.pw.edu.pl>
+ * @section LICENSE
+ *
+ * GNU Radio is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 3, or (at your option)
+ * any later version.
+ *
+ * GNU Radio is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with GNU Radio; see the file COPYING. If not, write to
+ * the Free Software Foundation, Inc., 51 Franklin Street,
+ * Boston, MA 02110-1301, USA.
+ *
+ * @section DESCRIPTION
+ * This file contains classes which define gsm_receiver configuration
+ * and the burst_counter which is used to store internal state of the receiver
+ * when it's synchronized
+ */
+#ifndef INCLUDED_GSM_RECEIVER_CONFIG_H
+#define INCLUDED_GSM_RECEIVER_CONFIG_H
+
+#include <vector>
+#include <algorithm>
+#include <math.h>
+#include <stdint.h>
+#include <gsm_constants.h>
+
+class multiframe_configuration
+{
+ private:
+ multiframe_type d_type;
+ std::vector<burst_type> d_burst_types;
+ public:
+ multiframe_configuration() {
+ d_type = unknown;
+ fill(d_burst_types.begin(), d_burst_types.end(), empty);
+ }
+
+ ~multiframe_configuration() {}
+
+ void set_type(multiframe_type type) {
+ if (type == multiframe_26) {
+ d_burst_types.resize(26);
+ } else {
+ d_burst_types.resize(51);
+ }
+
+ d_type = type;
+ }
+
+ void set_burst_type(int nr, burst_type type) {
+ d_burst_types[nr] = type;
+ }
+
+ multiframe_type get_type() {
+ return d_type;
+ }
+
+ burst_type get_burst_type(int nr) {
+ return d_burst_types[nr];
+ }
+};
+
+class burst_counter
+{
+ private:
+ const int d_OSR;
+ uint32_t d_t1, d_t2, d_t3, d_timeslot_nr;
+ double d_offset_fractional;
+ double d_offset_integer;
+ public:
+ burst_counter(int osr):
+ d_OSR(osr),
+ d_t1(0),
+ d_t2(0),
+ d_t3(0),
+ d_timeslot_nr(0),
+ d_offset_fractional(0.0),
+ d_offset_integer(0.0) {
+ }
+
+ burst_counter(int osr, uint32_t t1, uint32_t t2, uint32_t t3, uint32_t timeslot_nr):
+ d_OSR(osr),
+ d_t1(t1),
+ d_t2(t2),
+ d_t3(t3),
+ d_timeslot_nr(timeslot_nr),
+ d_offset_fractional(0.0),
+ d_offset_integer(0.0) {
+ double first_sample_position = (get_frame_nr() * 8 + timeslot_nr) * TS_BITS;
+ d_offset_integer = floor(first_sample_position);
+ d_offset_fractional = first_sample_position - floor(first_sample_position);
+ }
+
+ burst_counter & operator++(int);
+ void set(uint32_t t1, uint32_t t2, uint32_t t3, uint32_t timeslot_nr);
+
+ uint32_t get_t1() {
+ return d_t1;
+ }
+
+ uint32_t get_t2() {
+ return d_t2;
+ }
+
+ uint32_t get_t3() {
+ return d_t3;
+ }
+
+ uint32_t get_timeslot_nr() {
+ return d_timeslot_nr;
+ }
+
+ uint32_t get_frame_nr() {
+ return (51 * 26 * d_t1) + (51 * (((d_t3 + 26) - d_t2) % 26)) + d_t3;
+ }
+
+ uint32_t get_frame_nr_mod() {
+ return (d_t1 << 11) + (d_t3 << 5) + d_t2;
+ }
+
+ unsigned get_offset() {
+ return (unsigned)d_offset_integer;
+ }
+};
+
+class channel_configuration
+{
+ private:
+ multiframe_configuration d_timeslots_descriptions[TS_PER_FRAME];
+ public:
+ channel_configuration() {
+ for (int i = 0; i < TS_PER_FRAME; i++) {
+ d_timeslots_descriptions[i].set_type(unknown);
+ }
+ }
+
+ void set_multiframe_type(int timeslot_nr, multiframe_type type) {
+ d_timeslots_descriptions[timeslot_nr].set_type(type);
+ }
+
+ void set_burst_types(int timeslot_nr, const unsigned mapping[], unsigned mapping_size, burst_type b_type) {
+ unsigned i;
+ for (i = 0; i < mapping_size; i++) {
+ d_timeslots_descriptions[timeslot_nr].set_burst_type(mapping[i], b_type);
+ }
+ }
+
+ void set_single_burst_type(int timeslot_nr, int burst_nr, burst_type b_type) {
+ d_timeslots_descriptions[timeslot_nr].set_burst_type(burst_nr, b_type);
+ }
+
+ burst_type get_burst_type(burst_counter burst_nr);
+};
+
+#endif /* INCLUDED_GSM_RECEIVER_CONFIG_H */
diff --git a/gsm-receiver/src/lib/viterbi_detector.cc b/gsm-receiver/src/lib/viterbi_detector.cc
new file mode 100644
index 0000000..f3445cf
--- /dev/null
+++ b/gsm-receiver/src/lib/viterbi_detector.cc
@@ -0,0 +1,554 @@
+/* -*- c++ -*- */
+/*
+ * @file
+ * @author Piotr Krysik <pkrysik@stud.elka.pw.edu.pl>
+ * @section LICENSE
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 3, or (at your option)
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; see the file COPYING. If not, write to
+ * the Free Software Foundation, Inc., 51 Franklin Street,
+ * Boston, MA 02110-1301, USA.
+ */
+
+/*
+ * viterbi_detector:
+ * This part does the detection of received sequnece.
+ * Employed algorithm is viterbi Maximum Likehood Sequence Estimation.
+ * At this moment it gives hard decisions on the output, but
+ * it was designed with soft decisions in mind.
+ *
+ * SYNTAX: void viterbi_detector(
+ * const gr_complex * input,
+ * unsigned int samples_num,
+ * gr_complex * rhh,
+ * unsigned int start_state,
+ * const unsigned int * stop_states,
+ * unsigned int stops_num,
+ * float * output)
+ *
+ * INPUT: input: Complex received signal afted matched filtering.
+ * samples_num: Number of samples in the input table.
+ * rhh: The autocorrelation of the estimated channel
+ * impulse response.
+ * start_state: Number of the start point. In GSM each burst
+ * starts with sequence of three bits (0,0,0) which
+ * indicates start point of the algorithm.
+ * stop_states: Table with numbers of possible stop states.
+ * stops_num: Number of possible stop states
+ *
+ *
+ * OUTPUT: output: Differentially decoded hard output of the algorithm:
+ * -1 for logical "0" and 1 for logical "1"
+ *
+ * SUB_FUNC: none
+ *
+ * TEST(S): Tested with real world normal burst.
+ */
+
+#include <gnuradio/gr_complex.h>
+#include <gsm_constants.h>
+#define PATHS_NUM (1 << (CHAN_IMP_RESP_LENGTH-1))
+
+void viterbi_detector(const gr_complex * input, unsigned int samples_num, gr_complex * rhh, unsigned int start_state, const unsigned int * stop_states, unsigned int stops_num, float * output)
+{
+ float increment[8];
+ float path_metrics1[16];
+ float path_metrics2[16];
+ float * new_path_metrics;
+ float * old_path_metrics;
+ float * tmp;
+ float trans_table[BURST_SIZE][16];
+ float pm_candidate1, pm_candidate2;
+ bool real_imag;
+ float input_symbol_real, input_symbol_imag;
+ unsigned int i, sample_nr;
+
+/*
+* Setup first path metrics, so only state pointed by start_state is possible.
+* Start_state metric is equal to zero, the rest is written with some very low value,
+* which makes them practically impossible to occur.
+*/
+ for(i=0; i<PATHS_NUM; i++){
+ path_metrics1[i]=(-10e30);
+ }
+ path_metrics1[start_state]=0;
+
+/*
+* Compute Increment - a table of values which does not change for subsequent input samples.
+* Increment is table of reference levels for computation of branch metrics:
+* branch metric = (+/-)received_sample (+/-) reference_level
+*/
+ increment[0] = -rhh[1].imag() -rhh[2].real() -rhh[3].imag() +rhh[4].real();
+ increment[1] = rhh[1].imag() -rhh[2].real() -rhh[3].imag() +rhh[4].real();
+ increment[2] = -rhh[1].imag() +rhh[2].real() -rhh[3].imag() +rhh[4].real();
+ increment[3] = rhh[1].imag() +rhh[2].real() -rhh[3].imag() +rhh[4].real();
+ increment[4] = -rhh[1].imag() -rhh[2].real() +rhh[3].imag() +rhh[4].real();
+ increment[5] = rhh[1].imag() -rhh[2].real() +rhh[3].imag() +rhh[4].real();
+ increment[6] = -rhh[1].imag() +rhh[2].real() +rhh[3].imag() +rhh[4].real();
+ increment[7] = rhh[1].imag() +rhh[2].real() +rhh[3].imag() +rhh[4].real();
+
+
+/*
+* Computation of path metrics and decisions (Add-Compare-Select).
+* It's composed of two parts: one for odd input samples (imaginary numbers)
+* and one for even samples (real numbers).
+* Each part is composed of independent (parallelisable) statements like
+* this one:
+* pm_candidate1 = old_path_metrics[0] - input_symbol_real - increment[7];
+* pm_candidate2 = old_path_metrics[8] - input_symbol_real + increment[0];
+* if(pm_candidate1 > pm_candidate2){
+* new_path_metrics[0] = pm_candidate1;
+* trans_table[sample_nr][0] = -1.0;
+* }
+* else{
+* new_path_metrics[0] = pm_candidate2;
+* trans_table[sample_nr][0] = 1.0;
+* }
+* This is very good point for optimisations (SIMD or OpenMP) as it's most time
+* consuming part of this function.
+*/
+ sample_nr=0;
+ old_path_metrics=path_metrics1;
+ new_path_metrics=path_metrics2;
+ while(sample_nr<samples_num){
+ //Processing imag states
+ real_imag=1;
+ input_symbol_imag = input[sample_nr].imag();
+
+ pm_candidate1 = old_path_metrics[0] + input_symbol_imag - increment[2];
+ pm_candidate2 = old_path_metrics[8] + input_symbol_imag + increment[5];
+ if(pm_candidate1 > pm_candidate2){
+ new_path_metrics[0] = pm_candidate1;
+ trans_table[sample_nr][0] = -1.0;
+ }
+ else{
+ new_path_metrics[0] = pm_candidate2;
+ trans_table[sample_nr][0] = 1.0;
+ }
+
+ pm_candidate1 = old_path_metrics[0] - input_symbol_imag + increment[2];
+ pm_candidate2 = old_path_metrics[8] - input_symbol_imag - increment[5];
+ if(pm_candidate1 > pm_candidate2){
+ new_path_metrics[1] = pm_candidate1;
+ trans_table[sample_nr][1] = -1.0;
+ }
+ else{
+ new_path_metrics[1] = pm_candidate2;
+ trans_table[sample_nr][1] = 1.0;
+ }
+
+ pm_candidate1 = old_path_metrics[1] + input_symbol_imag - increment[3];
+ pm_candidate2 = old_path_metrics[9] + input_symbol_imag + increment[4];
+ if(pm_candidate1 > pm_candidate2){
+ new_path_metrics[2] = pm_candidate1;
+ trans_table[sample_nr][2] = -1.0;
+ }
+ else{
+ new_path_metrics[2] = pm_candidate2;
+ trans_table[sample_nr][2] = 1.0;
+ }
+
+ pm_candidate1 = old_path_metrics[1] - input_symbol_imag + increment[3];
+ pm_candidate2 = old_path_metrics[9] - input_symbol_imag - increment[4];
+ if(pm_candidate1 > pm_candidate2){
+ new_path_metrics[3] = pm_candidate1;
+ trans_table[sample_nr][3] = -1.0;
+ }
+ else{
+ new_path_metrics[3] = pm_candidate2;
+ trans_table[sample_nr][3] = 1.0;
+ }
+
+ pm_candidate1 = old_path_metrics[2] + input_symbol_imag - increment[0];
+ pm_candidate2 = old_path_metrics[10] + input_symbol_imag + increment[7];
+ if(pm_candidate1 > pm_candidate2){
+ new_path_metrics[4] = pm_candidate1;
+ trans_table[sample_nr][4] = -1.0;
+ }
+ else{
+ new_path_metrics[4] = pm_candidate2;
+ trans_table[sample_nr][4] = 1.0;
+ }
+
+ pm_candidate1 = old_path_metrics[2] - input_symbol_imag + increment[0];
+ pm_candidate2 = old_path_metrics[10] - input_symbol_imag - increment[7];
+ if(pm_candidate1 > pm_candidate2){
+ new_path_metrics[5] = pm_candidate1;
+ trans_table[sample_nr][5] = -1.0;
+ }
+ else{
+ new_path_metrics[5] = pm_candidate2;
+ trans_table[sample_nr][5] = 1.0;
+ }
+
+ pm_candidate1 = old_path_metrics[3] + input_symbol_imag - increment[1];
+ pm_candidate2 = old_path_metrics[11] + input_symbol_imag + increment[6];
+ if(pm_candidate1 > pm_candidate2){
+ new_path_metrics[6] = pm_candidate1;
+ trans_table[sample_nr][6] = -1.0;
+ }
+ else{
+ new_path_metrics[6] = pm_candidate2;
+ trans_table[sample_nr][6] = 1.0;
+ }
+
+ pm_candidate1 = old_path_metrics[3] - input_symbol_imag + increment[1];
+ pm_candidate2 = old_path_metrics[11] - input_symbol_imag - increment[6];
+ if(pm_candidate1 > pm_candidate2){
+ new_path_metrics[7] = pm_candidate1;
+ trans_table[sample_nr][7] = -1.0;
+ }
+ else{
+ new_path_metrics[7] = pm_candidate2;
+ trans_table[sample_nr][7] = 1.0;
+ }
+
+ pm_candidate1 = old_path_metrics[4] + input_symbol_imag - increment[6];
+ pm_candidate2 = old_path_metrics[12] + input_symbol_imag + increment[1];
+ if(pm_candidate1 > pm_candidate2){
+ new_path_metrics[8] = pm_candidate1;
+ trans_table[sample_nr][8] = -1.0;
+ }
+ else{
+ new_path_metrics[8] = pm_candidate2;
+ trans_table[sample_nr][8] = 1.0;
+ }
+
+ pm_candidate1 = old_path_metrics[4] - input_symbol_imag + increment[6];
+ pm_candidate2 = old_path_metrics[12] - input_symbol_imag - increment[1];
+ if(pm_candidate1 > pm_candidate2){
+ new_path_metrics[9] = pm_candidate1;
+ trans_table[sample_nr][9] = -1.0;
+ }
+ else{
+ new_path_metrics[9] = pm_candidate2;
+ trans_table[sample_nr][9] = 1.0;
+ }
+
+ pm_candidate1 = old_path_metrics[5] + input_symbol_imag - increment[7];
+ pm_candidate2 = old_path_metrics[13] + input_symbol_imag + increment[0];
+ if(pm_candidate1 > pm_candidate2){
+ new_path_metrics[10] = pm_candidate1;
+ trans_table[sample_nr][10] = -1.0;
+ }
+ else{
+ new_path_metrics[10] = pm_candidate2;
+ trans_table[sample_nr][10] = 1.0;
+ }
+
+ pm_candidate1 = old_path_metrics[5] - input_symbol_imag + increment[7];
+ pm_candidate2 = old_path_metrics[13] - input_symbol_imag - increment[0];
+ if(pm_candidate1 > pm_candidate2){
+ new_path_metrics[11] = pm_candidate1;
+ trans_table[sample_nr][11] = -1.0;
+ }
+ else{
+ new_path_metrics[11] = pm_candidate2;
+ trans_table[sample_nr][11] = 1.0;
+ }
+
+ pm_candidate1 = old_path_metrics[6] + input_symbol_imag - increment[4];
+ pm_candidate2 = old_path_metrics[14] + input_symbol_imag + increment[3];
+ if(pm_candidate1 > pm_candidate2){
+ new_path_metrics[12] = pm_candidate1;
+ trans_table[sample_nr][12] = -1.0;
+ }
+ else{
+ new_path_metrics[12] = pm_candidate2;
+ trans_table[sample_nr][12] = 1.0;
+ }
+
+ pm_candidate1 = old_path_metrics[6] - input_symbol_imag + increment[4];
+ pm_candidate2 = old_path_metrics[14] - input_symbol_imag - increment[3];
+ if(pm_candidate1 > pm_candidate2){
+ new_path_metrics[13] = pm_candidate1;
+ trans_table[sample_nr][13] = -1.0;
+ }
+ else{
+ new_path_metrics[13] = pm_candidate2;
+ trans_table[sample_nr][13] = 1.0;
+ }
+
+ pm_candidate1 = old_path_metrics[7] + input_symbol_imag - increment[5];
+ pm_candidate2 = old_path_metrics[15] + input_symbol_imag + increment[2];
+ if(pm_candidate1 > pm_candidate2){
+ new_path_metrics[14] = pm_candidate1;
+ trans_table[sample_nr][14] = -1.0;
+ }
+ else{
+ new_path_metrics[14] = pm_candidate2;
+ trans_table[sample_nr][14] = 1.0;
+ }
+
+ pm_candidate1 = old_path_metrics[7] - input_symbol_imag + increment[5];
+ pm_candidate2 = old_path_metrics[15] - input_symbol_imag - increment[2];
+ if(pm_candidate1 > pm_candidate2){
+ new_path_metrics[15] = pm_candidate1;
+ trans_table[sample_nr][15] = -1.0;
+ }
+ else{
+ new_path_metrics[15] = pm_candidate2;
+ trans_table[sample_nr][15] = 1.0;
+ }
+ tmp=old_path_metrics;
+ old_path_metrics=new_path_metrics;
+ new_path_metrics=tmp;
+
+ sample_nr++;
+ if(sample_nr==samples_num)
+ break;
+
+ //Processing real states
+ real_imag=0;
+ input_symbol_real = input[sample_nr].real();
+
+ pm_candidate1 = old_path_metrics[0] - input_symbol_real - increment[7];
+ pm_candidate2 = old_path_metrics[8] - input_symbol_real + increment[0];
+ if(pm_candidate1 > pm_candidate2){
+ new_path_metrics[0] = pm_candidate1;
+ trans_table[sample_nr][0] = -1.0;
+ }
+ else{
+ new_path_metrics[0] = pm_candidate2;
+ trans_table[sample_nr][0] = 1.0;
+ }
+
+ pm_candidate1 = old_path_metrics[0] + input_symbol_real + increment[7];
+ pm_candidate2 = old_path_metrics[8] + input_symbol_real - increment[0];
+ if(pm_candidate1 > pm_candidate2){
+ new_path_metrics[1] = pm_candidate1;
+ trans_table[sample_nr][1] = -1.0;
+ }
+ else{
+ new_path_metrics[1] = pm_candidate2;
+ trans_table[sample_nr][1] = 1.0;
+ }
+
+ pm_candidate1 = old_path_metrics[1] - input_symbol_real - increment[6];
+ pm_candidate2 = old_path_metrics[9] - input_symbol_real + increment[1];
+ if(pm_candidate1 > pm_candidate2){
+ new_path_metrics[2] = pm_candidate1;
+ trans_table[sample_nr][2] = -1.0;
+ }
+ else{
+ new_path_metrics[2] = pm_candidate2;
+ trans_table[sample_nr][2] = 1.0;
+ }
+
+ pm_candidate1 = old_path_metrics[1] + input_symbol_real + increment[6];
+ pm_candidate2 = old_path_metrics[9] + input_symbol_real - increment[1];
+ if(pm_candidate1 > pm_candidate2){
+ new_path_metrics[3] = pm_candidate1;
+ trans_table[sample_nr][3] = -1.0;
+ }
+ else{
+ new_path_metrics[3] = pm_candidate2;
+ trans_table[sample_nr][3] = 1.0;
+ }
+
+ pm_candidate1 = old_path_metrics[2] - input_symbol_real - increment[5];
+ pm_candidate2 = old_path_metrics[10] - input_symbol_real + increment[2];
+ if(pm_candidate1 > pm_candidate2){
+ new_path_metrics[4] = pm_candidate1;
+ trans_table[sample_nr][4] = -1.0;
+ }
+ else{
+ new_path_metrics[4] = pm_candidate2;
+ trans_table[sample_nr][4] = 1.0;
+ }
+
+ pm_candidate1 = old_path_metrics[2] + input_symbol_real + increment[5];
+ pm_candidate2 = old_path_metrics[10] + input_symbol_real - increment[2];
+ if(pm_candidate1 > pm_candidate2){
+ new_path_metrics[5] = pm_candidate1;
+ trans_table[sample_nr][5] = -1.0;
+ }
+ else{
+ new_path_metrics[5] = pm_candidate2;
+ trans_table[sample_nr][5] = 1.0;
+ }
+
+ pm_candidate1 = old_path_metrics[3] - input_symbol_real - increment[4];
+ pm_candidate2 = old_path_metrics[11] - input_symbol_real + increment[3];
+ if(pm_candidate1 > pm_candidate2){
+ new_path_metrics[6] = pm_candidate1;
+ trans_table[sample_nr][6] = -1.0;
+ }
+ else{
+ new_path_metrics[6] = pm_candidate2;
+ trans_table[sample_nr][6] = 1.0;
+ }
+
+ pm_candidate1 = old_path_metrics[3] + input_symbol_real + increment[4];
+ pm_candidate2 = old_path_metrics[11] + input_symbol_real - increment[3];
+ if(pm_candidate1 > pm_candidate2){
+ new_path_metrics[7] = pm_candidate1;
+ trans_table[sample_nr][7] = -1.0;
+ }
+ else{
+ new_path_metrics[7] = pm_candidate2;
+ trans_table[sample_nr][7] = 1.0;
+ }
+
+ pm_candidate1 = old_path_metrics[4] - input_symbol_real - increment[3];
+ pm_candidate2 = old_path_metrics[12] - input_symbol_real + increment[4];
+ if(pm_candidate1 > pm_candidate2){
+ new_path_metrics[8] = pm_candidate1;
+ trans_table[sample_nr][8] = -1.0;
+ }
+ else{
+ new_path_metrics[8] = pm_candidate2;
+ trans_table[sample_nr][8] = 1.0;
+ }
+
+ pm_candidate1 = old_path_metrics[4] + input_symbol_real + increment[3];
+ pm_candidate2 = old_path_metrics[12] + input_symbol_real - increment[4];
+ if(pm_candidate1 > pm_candidate2){
+ new_path_metrics[9] = pm_candidate1;
+ trans_table[sample_nr][9] = -1.0;
+ }
+ else{
+ new_path_metrics[9] = pm_candidate2;
+ trans_table[sample_nr][9] = 1.0;
+ }
+
+ pm_candidate1 = old_path_metrics[5] - input_symbol_real - increment[2];
+ pm_candidate2 = old_path_metrics[13] - input_symbol_real + increment[5];
+ if(pm_candidate1 > pm_candidate2){
+ new_path_metrics[10] = pm_candidate1;
+ trans_table[sample_nr][10] = -1.0;
+ }
+ else{
+ new_path_metrics[10] = pm_candidate2;
+ trans_table[sample_nr][10] = 1.0;
+ }
+
+ pm_candidate1 = old_path_metrics[5] + input_symbol_real + increment[2];
+ pm_candidate2 = old_path_metrics[13] + input_symbol_real - increment[5];
+ if(pm_candidate1 > pm_candidate2){
+ new_path_metrics[11] = pm_candidate1;
+ trans_table[sample_nr][11] = -1.0;
+ }
+ else{
+ new_path_metrics[11] = pm_candidate2;
+ trans_table[sample_nr][11] = 1.0;
+ }
+
+ pm_candidate1 = old_path_metrics[6] - input_symbol_real - increment[1];
+ pm_candidate2 = old_path_metrics[14] - input_symbol_real + increment[6];
+ if(pm_candidate1 > pm_candidate2){
+ new_path_metrics[12] = pm_candidate1;
+ trans_table[sample_nr][12] = -1.0;
+ }
+ else{
+ new_path_metrics[12] = pm_candidate2;
+ trans_table[sample_nr][12] = 1.0;
+ }
+
+ pm_candidate1 = old_path_metrics[6] + input_symbol_real + increment[1];
+ pm_candidate2 = old_path_metrics[14] + input_symbol_real - increment[6];
+ if(pm_candidate1 > pm_candidate2){
+ new_path_metrics[13] = pm_candidate1;
+ trans_table[sample_nr][13] = -1.0;
+ }
+ else{
+ new_path_metrics[13] = pm_candidate2;
+ trans_table[sample_nr][13] = 1.0;
+ }
+
+ pm_candidate1 = old_path_metrics[7] - input_symbol_real - increment[0];
+ pm_candidate2 = old_path_metrics[15] - input_symbol_real + increment[7];
+ if(pm_candidate1 > pm_candidate2){
+ new_path_metrics[14] = pm_candidate1;
+ trans_table[sample_nr][14] = -1.0;
+ }
+ else{
+ new_path_metrics[14] = pm_candidate2;
+ trans_table[sample_nr][14] = 1.0;
+ }
+
+ pm_candidate1 = old_path_metrics[7] + input_symbol_real + increment[0];
+ pm_candidate2 = old_path_metrics[15] + input_symbol_real - increment[7];
+ if(pm_candidate1 > pm_candidate2){
+ new_path_metrics[15] = pm_candidate1;
+ trans_table[sample_nr][15] = -1.0;
+ }
+ else{
+ new_path_metrics[15] = pm_candidate2;
+ trans_table[sample_nr][15] = 1.0;
+ }
+ tmp=old_path_metrics;
+ old_path_metrics=new_path_metrics;
+ new_path_metrics=tmp;
+
+ sample_nr++;
+ }
+
+/*
+* Find the best from the stop states by comparing their path metrics.
+* Not every stop state is always possible, so we are searching in
+* a subset of them.
+*/
+ unsigned int best_stop_state;
+ float stop_state_metric, max_stop_state_metric;
+ best_stop_state = stop_states[0];
+ max_stop_state_metric = old_path_metrics[best_stop_state];
+ for(i=1; i< stops_num; i++){
+ stop_state_metric = old_path_metrics[stop_states[i]];
+ if(stop_state_metric > max_stop_state_metric){
+ max_stop_state_metric = stop_state_metric;
+ best_stop_state = stop_states[i];
+ }
+ }
+
+/*
+* This table was generated with hope that it gives a litle speedup during
+* traceback stage.
+* Received bit is related to the number of state in the trellis.
+* I've numbered states so their parity (number of ones) is related
+* to a received bit.
+*/
+ static const unsigned int parity_table[PATHS_NUM] = { 0, 1, 1, 0, 0, 1, 1, 0, 0, 1, 1, 0, 0, 1, 1, 0, };
+
+/*
+* Table of previous states in the trellis diagram.
+* For GMSK modulation every state has two previous states.
+* Example:
+* previous_state_nr1 = prev_table[current_state_nr][0]
+* previous_state_nr2 = prev_table[current_state_nr][1]
+*/
+ static const unsigned int prev_table[PATHS_NUM][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}, };
+
+/*
+* Traceback and differential decoding of received sequence.
+* Decisions stored in trans_table are used to restore best path in the trellis.
+*/
+ sample_nr=samples_num;
+ unsigned int state_nr=best_stop_state;
+ unsigned int decision;
+ bool out_bit=0;
+
+ while(sample_nr>0){
+ sample_nr--;
+ decision = (trans_table[sample_nr][state_nr]>0);
+
+ if(decision != out_bit)
+ output[sample_nr]=-trans_table[sample_nr][state_nr];
+ else
+ output[sample_nr]=trans_table[sample_nr][state_nr];
+
+ out_bit = out_bit ^ real_imag ^ parity_table[state_nr];
+ state_nr = prev_table[state_nr][decision];
+ real_imag = !real_imag;
+ }
+}
diff --git a/gsm-receiver/src/lib/viterbi_detector.h b/gsm-receiver/src/lib/viterbi_detector.h
new file mode 100644
index 0000000..0360f83
--- /dev/null
+++ b/gsm-receiver/src/lib/viterbi_detector.h
@@ -0,0 +1,63 @@
+/* -*- c++ -*- */
+/*
+ * @file
+ * @author Piotr Krysik <pkrysik@stud.elka.pw.edu.pl>
+ * @section LICENSE
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 3, or (at your option)
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; see the file COPYING. If not, write to
+ * the Free Software Foundation, Inc., 51 Franklin Street,
+ * Boston, MA 02110-1301, USA.
+ */
+
+/*
+ * viterbi_detector:
+ * This part does the detection of received sequnece.
+ * Employed algorithm is viterbi Maximum Likehood Sequence Estimation.
+ * At this moment it gives hard decisions on the output, but
+ * it was designed with soft decisions in mind.
+ *
+ * SYNTAX: void viterbi_detector(
+ * const gr_complex * input,
+ * unsigned int samples_num,
+ * gr_complex * rhh,
+ * unsigned int start_state,
+ * const unsigned int * stop_states,
+ * unsigned int stops_num,
+ * float * output)
+ *
+ * INPUT: input: Complex received signal afted matched filtering.
+ * samples_num: Number of samples in the input table.
+ * rhh: The autocorrelation of the estimated channel
+ * impulse response.
+ * start_state: Number of the start point. In GSM each burst
+ * starts with sequence of three bits (0,0,0) which
+ * indicates start point of the algorithm.
+ * stop_states: Table with numbers of possible stop states.
+ * stops_num: Number of possible stop states
+ *
+ *
+ * OUTPUT: output: Differentially decoded hard output of the algorithm:
+ * -1 for logical "0" and 1 for logical "1"
+ *
+ * SUB_FUNC: none
+ *
+ * TEST(S): Tested with real world normal burst.
+ */
+
+#ifndef INCLUDED_VITERBI_DETECTOR_H
+#define INCLUDED_VITERBI_DETECTOR_H
+
+void viterbi_detector(const gr_complex * input, unsigned int samples_num, gr_complex * rhh, unsigned int start_state, const unsigned int * stop_states, unsigned int stops_num, float * output);
+
+#endif /* INCLUDED_VITERBI_DETECTOR_H */
diff --git a/gsm-receiver/src/python/Makefile.am b/gsm-receiver/src/python/Makefile.am
new file mode 100644
index 0000000..9a4befd
--- /dev/null
+++ b/gsm-receiver/src/python/Makefile.am
@@ -0,0 +1,24 @@
+#
+# Copyright 2004 Free Software Foundation, Inc.
+#
+# This file is part of GNU Radio
+#
+# GNU Radio is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 2, or (at your option)
+# any later version.
+#
+# GNU Radio is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with GNU Radio; see the file COPYING. If not, write to
+# the Free Software Foundation, Inc., 51 Franklin Street,
+# Boston, MA 02110-1301, USA.
+#
+
+include $(top_srcdir)/Makefile.common
+
+EXTRA_DIST = gsm_findfcch.py gsm_findfcch_usrp.py
diff --git a/gsm-receiver/src/python/capture.sh b/gsm-receiver/src/python/capture.sh
new file mode 100755
index 0000000..ef205d8
--- /dev/null
+++ b/gsm-receiver/src/python/capture.sh
@@ -0,0 +1,45 @@
+#! /bin/sh
+
+if [ $1"x" = x ]; then
+ echo "./capture.sh <freq> [duration==10] [decim==112] [gain==52]"
+ echo "Example: ./capture.sh 940.4M"
+ exit 1
+fi
+FREQ=$1
+
+DURATION=$2
+if [ $2"x" = x ]; then
+ DURATION=10
+fi
+DECIM=$3
+if [ $3"x" = x ]; then
+ DECIM=112
+fi
+
+GAIN=$4
+if [ $4"x" = x ]; then
+ GAIN=52
+fi
+
+
+USRP_PROG=usrp_rx_cfile.py
+while :; do
+ which "$USRP_PROG"
+ if [ $? -eq 0 ]; then
+ break
+ fi
+ USRP_PROG=/usr/share/gnuradio/usrp/usrp_rx_cfile.py
+ which "$USRP_PROG"
+ if [ $? -eq 0 ]; then
+ break
+ fi
+
+ echo "ERROR: usrp_rx_cfile.py not found. Make sure it's in your PATH!"
+ exit 1
+done
+
+FILE="capture_${FREQ}_${DECIM}.cfile"
+samples=`expr 64000000 / $DECIM '*' $DURATION`
+echo "Capturing for $DURATION seconds to $FILE ($samples samples)"
+$USRP_PROG -g $GAIN -d "$DECIM" -f "$FREQ" -N $samples $FILE
+
diff --git a/gsm-receiver/src/python/cfile b/gsm-receiver/src/python/cfile
new file mode 100644
index 0000000..943e35f
--- /dev/null
+++ b/gsm-receiver/src/python/cfile
Binary files differ
diff --git a/gsm-receiver/src/python/go.sh b/gsm-receiver/src/python/go.sh
new file mode 100755
index 0000000..e0d1290
--- /dev/null
+++ b/gsm-receiver/src/python/go.sh
@@ -0,0 +1,12 @@
+#! /bin/sh
+
+#echo "go.sh <file.cfile> [decim==112]"
+
+DECIM=$2
+FILE=$1
+
+if [ $DECIM"x" = x ]; then
+ DECIM=112
+fi
+
+./gsm_receive.py -d "$DECIM" -I "$FILE" | ../../../gsmdecode/src/gsmdecode -i
diff --git a/gsm-receiver/src/python/gsm_receive.py b/gsm-receiver/src/python/gsm_receive.py
new file mode 100755
index 0000000..4cfb876
--- /dev/null
+++ b/gsm-receiver/src/python/gsm_receive.py
@@ -0,0 +1,117 @@
+#!/usr/bin/env python
+
+from gnuradio import gr, gru, blks2
+#, gsm
+from gnuradio.eng_option import eng_option
+from optparse import OptionParser
+from os import sys
+
+for extdir in ['../../debug/src/lib','../../debug/src/lib/.libs','../lib','../lib/.libs','../..debug/src/lib/decoder/openbts/SIP']:
+ if extdir not in sys.path:
+ sys.path.append(extdir)
+import gsm
+
+class tuner(gr.feval_dd):
+ def __init__(self, top_block):
+ gr.feval_dd.__init__(self)
+ self.top_block = top_block
+ def eval(self, freq_offet):
+ self.top_block.set_center_frequency(freq_offet)
+ return freq_offet
+
+class synchronizer(gr.feval_dd):
+ def __init__(self, top_block):
+ gr.feval_dd.__init__(self)
+ self.top_block = top_block
+
+ def eval(self, timing_offset):
+ self.top_block.set_timing(timing_offset)
+ return freq_offet
+
+class gsm_receiver_first_blood(gr.top_block):
+ def __init__(self):
+ gr.top_block.__init__(self)
+ (options, args) = self._process_options()
+ self.tuner_callback = tuner(self)
+ self.synchronizer_callback = synchronizer(self)
+ self.options = options
+ self.args = args
+ self._set_rates()
+ self.source = self._set_source()
+ self.filtr = self._set_filter()
+ self.interpolator = self._set_interpolator()
+ self.receiver = self._set_receiver()
+ self.converter = self._set_converter()
+ self.sink = self._set_sink()
+
+ self.connect(self.source, self.filtr, self.interpolator, self.receiver, self.converter, self.sink)
+
+ def _set_sink(self):
+ nazwa_pliku_wy = self.options.outputfile
+ ujscie = gr.file_sink(gr.sizeof_float, nazwa_pliku_wy)
+ return ujscie
+
+ def _set_source(self):
+ nazwa_pliku = self.options.inputfile
+ zrodlo = gr.file_source(gr.sizeof_gr_complex, nazwa_pliku, False)
+ return zrodlo
+
+ def _set_rates(self):
+ options = self.options
+ clock_rate = 64e6
+ self.clock_rate = clock_rate
+ self.input_rate = clock_rate / options.decim
+ self.gsm_symb_rate = 1625000.0 / 6.0
+ self.sps = self.input_rate / self.gsm_symb_rate / self.options.osr
+
+ def _set_filter(self):
+ filter_cutoff = 145e3
+ filter_t_width = 10e3
+ offset = 0
+# print "input_rate:", self.input_rate, "sample rate:", self.sps, " filter_cutoff:", filter_cutoff, " filter_t_width:", filter_t_width
+ filter_taps = gr.firdes.low_pass(1.0, self.input_rate, filter_cutoff, filter_t_width, gr.firdes.WIN_HAMMING)
+ filtr = gr.freq_xlating_fir_filter_ccf(1, filter_taps, offset, self.input_rate)
+ return filtr
+
+ def _set_converter(self):
+ v2s = gr.vector_to_stream(gr.sizeof_float, 142)
+ return v2s
+
+ def _set_interpolator(self):
+ interpolator = gr.fractional_interpolator_cc(0, self.sps)
+ return interpolator
+
+ def _set_receiver(self):
+ receiver = gsm.receiver_cf(self.tuner_callback, self.synchronizer_callback, self.options.osr, self.options.key.replace(' ', '').lower())
+ return receiver
+
+ def _process_options(self):
+ parser = OptionParser(option_class=eng_option)
+ parser.add_option("-d", "--decim", type="int", default=128,
+ help="Set USRP decimation rate to DECIM [default=%default]")
+ parser.add_option("-r", "--osr", type="int", default=4,
+ help="Oversampling ratio [default=%default]")
+ parser.add_option("-I", "--inputfile", type="string", default="cfile",
+ help="Input filename")
+ parser.add_option("-O", "--outputfile", type="string", default="cfile2.out",
+ help="Output filename")
+ parser.add_option("-k", "--key", type="string", default="2B 08 74 9F DD 0D 9C 00",
+ help="KC session key")
+
+ (options, args) = parser.parse_args ()
+ return (options, args)
+
+ def set_center_frequency(self, center_freq):
+ self.filtr.set_center_freq(center_freq)
+
+ def set_timing(self, timing_offset):
+ pass
+
+def main():
+ try:
+ gsm_receiver_first_blood().run()
+ except KeyboardInterrupt:
+ pass
+
+if __name__ == '__main__':
+ main()
diff --git a/gsm-receiver/src/python/gsm_receive_usrp.py b/gsm-receiver/src/python/gsm_receive_usrp.py
new file mode 100755
index 0000000..a4e9720
--- /dev/null
+++ b/gsm-receiver/src/python/gsm_receive_usrp.py
@@ -0,0 +1,141 @@
+#!/usr/bin/env python
+#this file isn't ready to use now - gsm-receiver lacks realtime processing capability
+#there are many underruns of buffer for samples from usrp's, many blocks of samples get lost and
+#receiver isn't prepared for this situation too well
+
+from gnuradio import gr, gru, blks2
+#, gsm
+from gnuradio import usrp
+from gnuradio.eng_option import eng_option
+from optparse import OptionParser
+from os import sys
+
+for extdir in ['../../debug/src/lib','../../debug/src/lib/.libs']:
+ if extdir not in sys.path:
+ sys.path.append(extdir)
+import gsm
+
+def pick_subdevice(u):
+ if u.db[0][0].dbid() >= 0:
+ return (0, 0)
+ if u.db[1][0].dbid() >= 0:
+ return (1, 0)
+ return (0, 0)
+
+class tune(gr.feval_dd):
+ def __init__(self, top_block):
+ gr.feval_dd.__init__(self)
+ self.top_block = top_block
+ # self.center_freq = 0
+ def eval(self, freq_offet):
+ # self.center_freq = self.center_freq - freq_offet
+ self.top_block.set_frequency(freq_offet)
+ return freq_offet
+
+class gsm_receiver_first_blood(gr.top_block):
+ def __init__(self):
+ gr.top_block.__init__(self)
+ (options, args) = self._process_options()
+ self.tune_callback = tune(self)
+ self.options = options
+ self.args = args
+ self._set_rates()
+ self.source = self._set_source()
+ self.filtr = self._set_filter()
+ self.interpolator = self._set_interpolator()
+ self.receiver = self._set_receiver()
+ self.converter = self._set_converter()
+ self.sink = self._set_sink()
+
+ self.connect(self.source, self.filtr, self.interpolator, self.receiver, self.converter, self.sink)
+
+ def _set_sink(self):
+ nazwa_pliku_wy = self.options.outputfile
+ ujscie = gr.file_sink(gr.sizeof_float, nazwa_pliku_wy)
+ return ujscie
+
+ def _set_source(self):
+ options = self.options
+ fusb_block_size = gr.prefs().get_long('fusb', 'block_size', 4096)
+ fusb_nblocks = gr.prefs().get_long('fusb', 'nblocks', 16)
+ self.usrp = usrp.source_c(decim_rate=options.decim, fusb_block_size=fusb_block_size, fusb_nblocks=fusb_nblocks)
+
+ if options.rx_subdev_spec is None:
+ options.rx_subdev_spec = pick_subdevice(self.usrp)
+
+ self.usrp.set_mux(usrp.determine_rx_mux_value(self.usrp, options.rx_subdev_spec))
+ # determine the daughterboard subdevice
+ self.subdev = usrp.selected_subdev(self.usrp, options.rx_subdev_spec)
+ input_rate = self.usrp.adc_freq() / self.usrp.decim_rate()
+
+ # set initial values
+ if options.gain is None:
+ # if no gain was specified, use the mid-point in dB
+ g = self.subdev.gain_range()
+ options.gain = float(g[0]+g[1])/2
+
+ r = self.usrp.tune(0, self.subdev, options.freq)
+ self.subdev.set_gain(options.gain)
+ return self.usrp
+
+ def _set_rates(self):
+ options = self.options
+ clock_rate = 64e6
+ self.clock_rate = clock_rate
+ self.input_rate = clock_rate / options.decim
+ self.gsm_symb_rate = 1625000.0 / 6.0
+ self.sps = self.input_rate / self.gsm_symb_rate / self.options.osr
+
+ def _set_filter(self):
+ filter_cutoff = 145e3
+ filter_t_width = 10e3
+ offset = 0
+# print "input_rate:", self.input_rate, "sample rate:", self.sps, " filter_cutoff:", filter_cutoff, " filter_t_width:", filter_t_width
+ filter_taps = gr.firdes.low_pass(1.0, self.input_rate, filter_cutoff, filter_t_width, gr.firdes.WIN_HAMMING)
+ filtr = gr.freq_xlating_fir_filter_ccf(1, filter_taps, offset, self.input_rate)
+ return filtr
+
+ def _set_converter(self):
+ v2s = gr.vector_to_stream(gr.sizeof_float, 142)
+ return v2s
+
+ def _set_interpolator(self):
+ interpolator = gr.fractional_interpolator_cc(0, self.sps)
+ return interpolator
+
+ def _set_receiver(self):
+ receiver = gsm.receiver_cf(self.tune_callback, self.options.osr)
+ return receiver
+
+ def _process_options(self):
+ parser = OptionParser(option_class=eng_option)
+ parser.add_option("-d", "--decim", type="int", default=128,
+ help="Set USRP decimation rate to DECIM [default=%default]")
+ parser.add_option("-I", "--inputfile", type="string", default="cfile",
+ help="Input filename")
+ parser.add_option("-O", "--outputfile", type="string", default="cfile2.out",
+ help="Output filename")
+ parser.add_option("-R", "--rx-subdev-spec", type="subdev", default=None,
+ help="Select USRP Rx side A or B (default=first one with a daughterboard)")
+ parser.add_option("-r", "--osr", type="int", default=4,
+ help="Oversampling ratio [default=%default]")
+ parser.add_option("-f", "--freq", type="eng_float", default="950.4M",
+ help="set frequency to FREQ", metavar="FREQ")
+ parser.add_option("-g", "--gain", type="eng_float", default=None,
+ help="Set gain in dB (default is midpoint)")
+ (options, args) = parser.parse_args ()
+ return (options, args)
+
+ def set_frequency(self, center_freq):
+ self.filtr.set_center_freq(center_freq)
+
+def main():
+ try:
+ gsm_receiver_first_blood().run()
+ except KeyboardInterrupt:
+ pass
+
+if __name__ == '__main__':
+ main()
+
+
diff --git a/gsm-receiver/src/python/test.sh b/gsm-receiver/src/python/test.sh
new file mode 100755
index 0000000..0a828e9
--- /dev/null
+++ b/gsm-receiver/src/python/test.sh
@@ -0,0 +1,16 @@
+#!/bin/sh
+
+./gsm_receive.py -I cfile > receiver-test.out 2> /dev/null
+echo " 01 2b 2b 2b 2b 2b 2b 2b 2b 2b 2b 2b 2b 2b 2b 2b 2b 2b 2b 2b 2b 2b 2b\n 15 06 21 00 01 00 2b 2b 2b 2b 2b 2b 2b 2b 2b 2b 2b 2b 2b 2b 2b 2b 2b" > receiver-comp.out
+diff receiver-test.out receiver-comp.out > receiver-test-diff.out
+test_result=`cat receiver-test-diff.out`
+
+rm receiver-test.out receiver-test-diff.out receiver-comp.out
+
+if [ "x$test_result" = "x" ]; then
+ echo "Test: passed"
+ exit 0
+else
+ echo "Test: failed"
+ exit 1
+fi
personal git repositories of Harald Welte. Your mileage may vary