diff options
author | Piotr Krysik <perper@o2.pl> | 2009-06-30 23:10:11 +0200 |
---|---|---|
committer | Piotr Krysik <perper@o2.pl> | 2009-06-30 23:10:11 +0200 |
commit | 3f91ce4f1c4a047a94497041b82c77c815d52a1f (patch) | |
tree | ed787719fb4e89670cc2161ecd4136c554834686 /gsm-receiver/src/lib | |
parent | 26a95cc318fcc8022a42f679d81b41d949771b8d (diff) | |
parent | 8d2bc49fb9e0c9a5fbd75aa3cad207608e72bf99 (diff) |
Merge branch 'moving_to_airprobe'
Diffstat (limited to 'gsm-receiver/src/lib')
50 files changed, 9286 insertions, 0 deletions
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 */ |