From faacc72413cb64544b8e46beded071ff91826d82 Mon Sep 17 00:00:00 2001 From: piotr Date: Sun, 20 Jul 2014 23:48:32 +0200 Subject: Added typical signalization channels (CCCH, BCCH, SDCCH) decoder and demapper for BCCH. The implementation is quite dirty at this moment. --- examples/receiver_file.grc | 84 ++-- examples/receiver_usrp.grc | 227 +++++++++-- examples/receiver_usrp_channelizer.grc | 400 +++++++++++-------- grc/CMakeLists.txt | 4 +- grc/gsm_control_channels_decoder.xml | 16 + grc/gsm_get_bcch_or_ccch_bursts.xml | 16 + include/gsm/CMakeLists.txt | 5 +- include/gsm/control_channels_decoder.h | 56 +++ include/gsm/get_bcch_or_ccch_bursts.h | 56 +++ include/gsm/gsmtap.h | 72 ++++ lib/CMakeLists.txt | 6 +- lib/burst_printer/bursts_printer_impl.cc | 4 +- lib/decoding/cch.c | 553 ++++++++++++++++++++++++++ lib/decoding/cch.h | 59 +++ lib/decoding/control_channels_decoder_impl.cc | 147 +++++++ lib/decoding/control_channels_decoder_impl.h | 48 +++ lib/decoding/fire_crc.c | 179 +++++++++ lib/decoding/fire_crc.h | 47 +++ lib/decoding/interleave.c | 71 ++++ lib/decoding/interleave.h | 28 ++ lib/demapping/get_bcch_or_ccch_bursts_impl.cc | 100 +++++ lib/demapping/get_bcch_or_ccch_bursts_impl.h | 44 ++ lib/receiver/gsmtap.h | 72 ---- lib/receiver/receiver_impl.h | 2 +- swig/gsm_swig.i | 6 + 25 files changed, 1979 insertions(+), 323 deletions(-) create mode 100644 grc/gsm_control_channels_decoder.xml create mode 100644 grc/gsm_get_bcch_or_ccch_bursts.xml create mode 100644 include/gsm/control_channels_decoder.h create mode 100644 include/gsm/get_bcch_or_ccch_bursts.h create mode 100644 include/gsm/gsmtap.h create mode 100644 lib/decoding/cch.c create mode 100644 lib/decoding/cch.h create mode 100644 lib/decoding/control_channels_decoder_impl.cc create mode 100644 lib/decoding/control_channels_decoder_impl.h create mode 100644 lib/decoding/fire_crc.c create mode 100644 lib/decoding/fire_crc.h create mode 100644 lib/decoding/interleave.c create mode 100644 lib/decoding/interleave.h create mode 100644 lib/demapping/get_bcch_or_ccch_bursts_impl.cc create mode 100644 lib/demapping/get_bcch_or_ccch_bursts_impl.h delete mode 100644 lib/receiver/gsmtap.h diff --git a/examples/receiver_file.grc b/examples/receiver_file.grc index e277cd8..883ea2e 100644 --- a/examples/receiver_file.grc +++ b/examples/receiver_file.grc @@ -1,6 +1,6 @@ - Sat Feb 8 13:47:51 2014 + Fri Jul 18 10:24:08 2014 options @@ -51,6 +51,10 @@ realtime_scheduling + + alias + + _coordinate (10, 10) @@ -74,6 +78,10 @@ value 1625000/6*4 + + alias + + _coordinate (10, 170) @@ -84,46 +92,26 @@ - blocks_file_source + blocks_message_debug id - blocks_file_source_0 + blocks_message_debug_0 _enabled True - file - /home/piotr/Odbiornik_gsm/reconstr_signal - - - type - complex - - - repeat - False - - - vlen - 1 + alias + affinity - - minoutbuf - 0 - - - maxoutbuf - 0 - _coordinate - (267, 220) + (630, 183) _rotation @@ -148,6 +136,14 @@ osr 4 + + arfcn + 0 + + + alias + + affinity @@ -162,7 +158,7 @@ _coordinate - (539, 220) + (352, 163) _rotation @@ -170,22 +166,50 @@ - blocks_message_debug + blocks_file_source id - blocks_message_debug_0 + blocks_file_source_0 _enabled True + + file + /home/piotr/Odbiornik_gsm/reconstr_signal + + + type + complex + + + repeat + False + + + vlen + 1 + + + alias + + affinity + + minoutbuf + 0 + + + maxoutbuf + 0 + _coordinate - (905, 249) + (118, 171) _rotation diff --git a/examples/receiver_usrp.grc b/examples/receiver_usrp.grc index b3be775..00d6852 100644 --- a/examples/receiver_usrp.grc +++ b/examples/receiver_usrp.grc @@ -1,6 +1,6 @@ - Sat Feb 8 22:28:34 2014 + Fri Jul 18 11:26:16 2014 options @@ -51,6 +51,10 @@ realtime_scheduling + + alias + + _coordinate (0, -1) @@ -74,6 +78,10 @@ value 100e6/80 + + alias + + _coordinate (-1, 160) @@ -109,6 +117,10 @@ short_id + + alias + + _coordinate (486, 10) @@ -164,6 +176,10 @@ notebook + + alias + + _coordinate (368, -1) @@ -173,6 +189,80 @@ 0 + + gsm_receiver_hier + + id + gsm_receiver_hier_0 + + + _enabled + True + + + input_rate + samp_rate + + + osr + 4 + + + arfcn + 0 + + + alias + + + + affinity + + + + minoutbuf + 0 + + + maxoutbuf + 0 + + + _coordinate + (348, 237) + + + _rotation + 0 + + + + gsm_bursts_printer + + id + gsm_bursts_printer_0 + + + _enabled + True + + + alias + + + + affinity + + + + _coordinate + (587, 257) + + + _rotation + 0 + + variable_slider @@ -193,15 +283,15 @@ min - 800 + 900 max - 1900 + 1000 num_steps - 1000 + 500 style @@ -219,6 +309,10 @@ notebook + + alias + + _coordinate (236, 0) @@ -229,38 +323,98 @@ - gsm_receiver_hier + wxgui_fftsink2 id - gsm_receiver_hier_0 + wxgui_fftsink2_0 _enabled True - input_rate + type + complex + + + title + FFT Plot + + + samp_rate samp_rate - osr - 4 + baseband_freq + fc - affinity - + y_per_div + 10 - minoutbuf + y_divs + 10 + + + ref_level 0 - maxoutbuf + ref_scale + 2.0 + + + fft_size + 1024 + + + fft_rate + 15 + + + peak_hold + False + + + average + False + + + avg_alpha 0 + + win + None + + + win_size + + + + grid_pos + + + + notebook + + + + freqvar + None + + + alias + + + + affinity + + _coordinate - (523, 275) + (654, 52) _rotation @@ -295,7 +449,11 @@ dev_addr - addr=192.168.11.2 + + + + dev_args + "" sync @@ -311,7 +469,7 @@ clock_source0 - + internal time_source0 @@ -925,6 +1083,10 @@ bw31 0 + + alias + + affinity @@ -939,36 +1101,19 @@ _coordinate - (235, 251) - - - _rotation - 0 - - - - gsm_bursts_printer - - id - gsm_bursts_printer_0 - - - _enabled - True - - - affinity - - - - _coordinate - (776, 287) + (101, 229) _rotation 0 + + uhd_usrp_source_0 + gsm_receiver_hier_0 + 0 + 0 + gsm_receiver_hier_0 gsm_bursts_printer_0 @@ -977,7 +1122,7 @@ uhd_usrp_source_0 - gsm_receiver_hier_0 + wxgui_fftsink2_0 0 0 diff --git a/examples/receiver_usrp_channelizer.grc b/examples/receiver_usrp_channelizer.grc index bae5c32..1c45f45 100644 --- a/examples/receiver_usrp_channelizer.grc +++ b/examples/receiver_usrp_channelizer.grc @@ -1,6 +1,6 @@ - Sun Feb 9 11:14:11 2014 + Fri Jul 18 10:24:38 2014 options @@ -51,6 +51,10 @@ realtime_scheduling + + alias + + _coordinate (0, -1) @@ -64,7 +68,7 @@ variable id - samp_rate2 + samp_rate _enabled @@ -72,11 +76,15 @@ value - samp_rate/25 + 100e6/10 + + + alias + _coordinate - (-1, 160) + (2, 223) _rotation @@ -87,7 +95,7 @@ variable id - samp_rate + samp_rate2 _enabled @@ -95,11 +103,15 @@ value - 100e6/10 + samp_rate/25 + + + alias + _coordinate - (2, 223) + (-1, 160) _rotation @@ -132,6 +144,10 @@ short_id + + alias + + _coordinate (486, 10) @@ -187,6 +203,10 @@ notebook + + alias + + _coordinate (368, -1) @@ -196,6 +216,198 @@ 0 + + gsm_bursts_printer + + id + gsm_bursts_printer_0 + + + _enabled + True + + + alias + + + + affinity + + + + _coordinate + (1019, 136) + + + _rotation + 0 + + + + variable_slider + + id + fc + + + _enabled + True + + + label + + + + value + 957 + + + min + 800 + + + max + 1000 + + + num_steps + 1000 + + + style + wx.SL_HORIZONTAL + + + converver + float_converter + + + grid_pos + + + + notebook + + + + alias + + + + _coordinate + (236, 0) + + + _rotation + 0 + + + + gsm_receiver_hier + + id + gsm_receiver_hier_0_0 + + + _enabled + True + + + input_rate + samp_rate2 + + + osr + 4 + + + arfcn + 0 + + + alias + + + + affinity + + + + minoutbuf + 0 + + + maxoutbuf + 0 + + + _coordinate + (755, 130) + + + _rotation + 0 + + + + pfb_channelizer_ccf + + id + pfb_channelizer_ccf_0 + + + _enabled + True + + + nchans + 25 + + + taps + + + + osr + 1 + + + atten + 100 + + + ch_map + [] + + + bus_conns + [[0,],] + + + alias + + + + affinity + + + + minoutbuf + 0 + + + maxoutbuf + 0 + + + _coordinate + (384, 217) + + + _rotation + 0 + + uhd_usrp_source @@ -224,7 +436,11 @@ dev_addr - addr=192.168.11.2 + + + + dev_args + "" sync @@ -855,173 +1071,9 @@ 0 - affinity - - - - minoutbuf - 0 - - - maxoutbuf - 0 - - - _coordinate - (126, 251) - - - _rotation - 0 - - - - gsm_bursts_printer - - id - gsm_bursts_printer_0 - - - _enabled - True - - - affinity - - - - _coordinate - (1019, 136) - - - _rotation - 0 - - - - variable_slider - - id - fc - - - _enabled - True - - - label - - - - value - 957 - - - min - 800 - - - max - 1000 - - - num_steps - 1000 - - - style - wx.SL_HORIZONTAL - - - converver - float_converter - - - grid_pos - - - - notebook - - - - _coordinate - (236, 0) - - - _rotation - 0 - - - - gsm_receiver_hier - - id - gsm_receiver_hier_0_0 - - - _enabled - True - - - input_rate - samp_rate2 - - - osr - 4 - - - affinity + alias - - minoutbuf - 0 - - - maxoutbuf - 0 - - - _coordinate - (755, 130) - - - _rotation - 0 - - - - pfb_channelizer_ccf - - id - pfb_channelizer_ccf_0 - - - _enabled - True - - - nchans - 25 - - - taps - - - - osr - 1 - - - atten - 100 - - - ch_map - [] - affinity @@ -1036,7 +1088,7 @@ _coordinate - (373, 282) + (126, 251) _rotation diff --git a/grc/CMakeLists.txt b/grc/CMakeLists.txt index 6de687a..15b3673 100644 --- a/grc/CMakeLists.txt +++ b/grc/CMakeLists.txt @@ -21,5 +21,7 @@ install(FILES gsm_bursts_printer.xml gsm_fcch_burst_tagger.xml gsm_sch_detector.xml - gsm_fcch_detector.xml DESTINATION share/gnuradio/grc/blocks + gsm_fcch_detector.xml + gsm_get_bcch_or_ccch_bursts.xml + gsm_control_channels_decoder.xml DESTINATION share/gnuradio/grc/blocks ) diff --git a/grc/gsm_control_channels_decoder.xml b/grc/gsm_control_channels_decoder.xml new file mode 100644 index 0000000..b106ff7 --- /dev/null +++ b/grc/gsm_control_channels_decoder.xml @@ -0,0 +1,16 @@ + + + Control channels decoder + gsm_control_channels_decoder + GSM + import gsm + gsm.control_channels_decoder() + + bursts + message + + + + + + diff --git a/grc/gsm_get_bcch_or_ccch_bursts.xml b/grc/gsm_get_bcch_or_ccch_bursts.xml new file mode 100644 index 0000000..c08bdd7 --- /dev/null +++ b/grc/gsm_get_bcch_or_ccch_bursts.xml @@ -0,0 +1,16 @@ + + + Demapper for BCCH and CCCH + gsm_get_bcch_or_ccch_bursts + GSM + import gsm + gsm.get_bcch_or_ccch_bursts() + + bursts + message + + + bursts + message + + diff --git a/include/gsm/CMakeLists.txt b/include/gsm/CMakeLists.txt index c7f4fb3..49f4a43 100644 --- a/include/gsm/CMakeLists.txt +++ b/include/gsm/CMakeLists.txt @@ -23,5 +23,8 @@ install(FILES api.h receiver.h - bursts_printer.h DESTINATION include/gsm + bursts_printer.h + get_bcch_or_ccch_bursts.h + control_channels_decoder.h + gsmtap.h DESTINATION include/gsm ) diff --git a/include/gsm/control_channels_decoder.h b/include/gsm/control_channels_decoder.h new file mode 100644 index 0000000..a44f43e --- /dev/null +++ b/include/gsm/control_channels_decoder.h @@ -0,0 +1,56 @@ +/* -*- c++ -*- */ +/* + * Copyright 2014 <+YOU OR YOUR COMPANY+>. + * + * This 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 software 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 software; see the file COPYING. If not, write to + * the Free Software Foundation, Inc., 51 Franklin Street, + * Boston, MA 02110-1301, USA. + */ + + +#ifndef INCLUDED_GSM_CONTROL_CHANNELS_DECODER_H +#define INCLUDED_GSM_CONTROL_CHANNELS_DECODER_H + +#include +#include + +namespace gr { + namespace gsm { + + /*! + * \brief <+description of block+> + * \ingroup gsm + * + */ + class GSM_API control_channels_decoder : virtual public gr::block + { + public: + typedef boost::shared_ptr sptr; + + /*! + * \brief Return a shared_ptr to a new instance of gsm::control_channels_decoder. + * + * To avoid accidental use of raw pointers, gsm::control_channels_decoder's + * constructor is in a private implementation + * class. gsm::control_channels_decoder::make is the public interface for + * creating new instances. + */ + static sptr make(); + }; + + } // namespace gsm +} // namespace gr + +#endif /* INCLUDED_GSM_CONTROL_CHANNELS_DECODER_H */ + diff --git a/include/gsm/get_bcch_or_ccch_bursts.h b/include/gsm/get_bcch_or_ccch_bursts.h new file mode 100644 index 0000000..d74137a --- /dev/null +++ b/include/gsm/get_bcch_or_ccch_bursts.h @@ -0,0 +1,56 @@ +/* -*- c++ -*- */ +/* + * Copyright 2014 <+YOU OR YOUR COMPANY+>. + * + * This 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 software 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 software; see the file COPYING. If not, write to + * the Free Software Foundation, Inc., 51 Franklin Street, + * Boston, MA 02110-1301, USA. + */ + + +#ifndef INCLUDED_GSM_GET_BCCH_OR_CCCH_BURSTS_H +#define INCLUDED_GSM_GET_BCCH_OR_CCCH_BURSTS_H + +#include +#include + +namespace gr { + namespace gsm { + + /*! + * \brief <+description of block+> + * \ingroup gsm + * + */ + class GSM_API get_bcch_or_ccch_bursts : virtual public gr::block + { + public: + typedef boost::shared_ptr sptr; + + /*! + * \brief Return a shared_ptr to a new instance of gsm::get_bcch_or_ccch_bursts. + * + * To avoid accidental use of raw pointers, gsm::get_bcch_or_ccch_bursts's + * constructor is in a private implementation + * class. gsm::get_bcch_or_ccch_bursts::make is the public interface for + * creating new instances. + */ + static sptr make(); + }; + + } // namespace gsm +} // namespace gr + +#endif /* INCLUDED_GSM_GET_BCCH_OR_CCCH_BURSTS_H */ + diff --git a/include/gsm/gsmtap.h b/include/gsm/gsmtap.h new file mode 100644 index 0000000..bf8226b --- /dev/null +++ b/include/gsm/gsmtap.h @@ -0,0 +1,72 @@ +#ifndef _GSMTAP_H +#define _GSMTAP_H + +/* gsmtap header, pseudo-header in front of the actua GSM payload */ + +/* GSMTAP is a generic header format for GSM protocol captures, + * it uses the IANA-assigned UDP port number 4729 and carries + * payload in various formats of GSM interfaces such as Um MAC + * blocks or Um bursts. + * + * Example programs generating GSMTAP data are airprobe + * (http://airprobe.org/) or OsmocomBB (http://bb.osmocom.org/) + */ + +#include + +#define GSMTAP_VERSION 0x02 + +#define GSMTAP_TYPE_UM 0x01 /* A Layer 2 MAC block (23 bytes) */ +#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 +#define GSMTAP_BURST_NONE 0x09 + +#define GSMTAP_CHANNEL_UNKNOWN 0x00 +#define GSMTAP_CHANNEL_BCCH 0x01 +#define GSMTAP_CHANNEL_CCCH 0x02 +#define GSMTAP_CHANNEL_RACH 0x03 +#define GSMTAP_CHANNEL_AGCH 0x04 +#define GSMTAP_CHANNEL_PCH 0x05 +#define GSMTAP_CHANNEL_SDCCH 0x06 +#define GSMTAP_CHANNEL_SDCCH4 0x07 +#define GSMTAP_CHANNEL_SDCCH8 0x08 +#define GSMTAP_CHANNEL_TCH_F 0x09 +#define GSMTAP_CHANNEL_TCH_H 0x0a +#define GSMTAP_CHANNEL_ACCH 0x80 + +#define GSMTAP_ARFCN_F_PCS 0x8000 +#define GSMTAP_ARFCN_F_UPLINK 0x4000 +#define GSMTAP_ARFCN_MASK 0x3fff + +#define GSMTAP_UDP_PORT 4729 /* officially registered with IANA */ + +struct gsmtap_hdr { + uint8_t version; /* version, set to GSMTAP_VERSION */ + uint8_t hdr_len; /* length in number of 32bit words */ + uint8_t type; /* see GSMTAP_TYPE_* */ + uint8_t timeslot; /* timeslot (0..7 on Um) */ + + uint16_t arfcn; /* ARFCN (frequency) */ + int8_t signal_dbm; /* signal level in dBm */ + int8_t snr_db; /* signal/noise ratio in dB */ + + uint32_t frame_number; /* GSM Frame Number (FN) */ + + uint8_t sub_type; /* Type of burst/channel, see above */ + uint8_t antenna_nr; /* Antenna Number */ + uint8_t sub_slot; /* sub-slot within timeslot */ + uint8_t res; /* reserved for future use (RFU) */ + +} __attribute__((packed)); + +#endif /* _GSMTAP_H */ diff --git a/lib/CMakeLists.txt b/lib/CMakeLists.txt index 931e0c1..79fa57a 100644 --- a/lib/CMakeLists.txt +++ b/lib/CMakeLists.txt @@ -30,7 +30,11 @@ list(APPEND gsm_sources receiver/viterbi_detector.cc receiver/sch.c burst_printer/bursts_printer_impl.cc - ) + demapping/get_bcch_or_ccch_bursts_impl.cc + decoding/control_channels_decoder_impl.cc + decoding/cch.c + decoding/fire_crc.c +) add_library(gnuradio-gsm SHARED ${gsm_sources}) target_link_libraries(gnuradio-gsm ${Boost_LIBRARIES} ${GNURADIO_RUNTIME_LIBRARIES} diff --git a/lib/burst_printer/bursts_printer_impl.cc b/lib/burst_printer/bursts_printer_impl.cc index dbb7023..c37838b 100644 --- a/lib/burst_printer/bursts_printer_impl.cc +++ b/lib/burst_printer/bursts_printer_impl.cc @@ -23,10 +23,10 @@ #endif #include -#include "bursts_printer_impl.h" -#include +#include #include #include +#include "bursts_printer_impl.h" namespace gr { namespace gsm { diff --git a/lib/decoding/cch.c b/lib/decoding/cch.c new file mode 100644 index 0000000..16db5eb --- /dev/null +++ b/lib/decoding/cch.c @@ -0,0 +1,553 @@ +//#include "system.h" +#include +#include +#include +#include +#include + +//#include +//#include +#include +//#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]; +} + */ + + +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; + } +} + */ + + +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)]; // accumulated error + 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);*/ + +/* // check parity*/ +/* // If parity check error detected try to fix it.*/ +/* if (parity_check(decoded_data))*/ +/* {*/ +/* unsigned char crc_result[224];*/ +/* if (FC_check_crc(&fc_ctx, decoded_data, crc_result) == 0)*/ +/* {*/ +/* errors = -1;*/ +/* //DEBUGF("error: sacch: parity error (%d fn=%d)\n",*/ +/* // errors, ctx->fn);*/ +/* return NULL;*/ +/* } else {*/ +/* //DEBUGF("Successfully corrected parity bits! (errors=%d fn=%d)\n",*/ +/* // errors, ctx->fn);*/ +/* memcpy(decoded_data, crc_result, sizeof crc_result);*/ +/* errors = 0;*/ +/* }*/ +/* }*/ + +/* if (errors)*/ +/* printf("WRN: errors=%d fn=%d\n", errors, ctx->fn);*/ + +/* 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 + +/*unsigned char *decode_facch(GS_CTX *ctx, unsigned char *burst, unsigned int *datalen, int offset) {*/ + +/* int errors, len, data_size;*/ +/* unsigned char conv_data[CONV_SIZE], iBLOCK[BLOCKS * 2][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);*/ +/* decode_burstmap(iBLOCK[4], burst + 116 * 4, &hl, &hn);*/ +/* decode_burstmap(iBLOCK[5], burst + 116 * 5, &hl, &hn);*/ +/* decode_burstmap(iBLOCK[6], burst + 116 * 6, &hl, &hn);*/ +/* decode_burstmap(iBLOCK[7], burst + 116 * 7, &hl, &hn);*/ + +/* // remove interleave*/ +/* if (offset == 0)*/ +/* interleave_decode(&ctx->interleave_facch_f1_ctx, conv_data, (unsigned char *)iBLOCK);*/ +/* else*/ +/* interleave_decode(&ctx->interleave_facch_f2_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);*/ + +/* // 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)*/ +/* {*/ +/* //DEBUGF("error: sacch: parity error (errors=%d fn=%d)\n", errors, ctx->fn);*/ +/* errors = -1;*/ +/* return NULL;*/ +/* } else {*/ +/* //DEBUGF("Successfully corrected parity bits! (errors=%d fn=%d)\n", errors, ctx->fn);*/ +/* memcpy(decoded_data, crc_result, sizeof crc_result);*/ +/* errors = 0;*/ +/* }*/ +/* }*/ + +/* if (errors)*/ +/* fprintf(stderr, "WRN: errors=%d fn=%d\n", errors, ctx->fn);*/ + +/* 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;*/ +/*}*/ diff --git a/lib/decoding/cch.h b/lib/decoding/cch.h new file mode 100644 index 0000000..efd5615 --- /dev/null +++ b/lib/decoding/cch.h @@ -0,0 +1,59 @@ +//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) + +int conv_decode(unsigned char *output, unsigned char *data); +int parity_check(unsigned char *d); +//unsigned char *decode_cch(GS_CTX *ctx, unsigned char *burst, unsigned int *len); +//unsigned char *decode_facch(GS_CTX *ctx, unsigned char *burst, unsigned int *len, int offset); +//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/lib/decoding/control_channels_decoder_impl.cc b/lib/decoding/control_channels_decoder_impl.cc new file mode 100644 index 0000000..544e6bb --- /dev/null +++ b/lib/decoding/control_channels_decoder_impl.cc @@ -0,0 +1,147 @@ +/* -*- c++ -*- */ +/* + * Copyright 2014 <+YOU OR YOUR COMPANY+>. + * + * This 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 software 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 software; 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 +#include +#include "control_channels_decoder_impl.h" + +namespace gr { + namespace gsm { + + control_channels_decoder::sptr + control_channels_decoder::make() + { + return gnuradio::get_initial_sptr + (new control_channels_decoder_impl()); + } + + /* + * The private constructor + */ + control_channels_decoder_impl::control_channels_decoder_impl() + : gr::block("control_channels_decoder", + gr::io_signature::make(0, 0, 0), + gr::io_signature::make(0, 0, 0)), + d_collected_bursts_num(0) + { + //initialize de/interleaver + int j, k, B; + for (k = 0; k < CONV_SIZE; k++) + { + B = k % 4; + j = 2 * ((49 * k) % 57) + ((k % 8) / 4); + interleave_trans[k] = B * 114 + j; //114=57 + 57 + } + + FC_init(&fc_ctx, 40, 184); + message_port_register_in(pmt::mp("bursts")); + set_msg_handler(pmt::mp("bursts"), boost::bind(&control_channels_decoder_impl::decode, this, _1)); +// message_port_register_out(pmt::mp("")); + } + + /* + * Our virtual destructor. + */ + control_channels_decoder_impl::~control_channels_decoder_impl() + { + } + + void control_channels_decoder_impl::decode(pmt::pmt_t msg) + { + unsigned char iBLOCK[BLOCKS*iBLOCK_SIZE], hl, hn, conv_data[CONV_SIZE], decoded_data[PARITY_OUTPUT_SIZE]; + d_bursts[d_collected_bursts_num] = msg; + d_collected_bursts_num++; + //get convecutive bursts + pmt::pmt_t header_blob = pmt::car(msg); + gsmtap_hdr * header = (gsmtap_hdr *)pmt::blob_data(header_blob); + + if(d_collected_bursts_num==4) + { + d_collected_bursts_num=0; + //reorganize data + for(int ii = 0; ii < 4; ii++) + { + pmt::pmt_t burst_content = pmt::cdr(d_bursts[ii]); + int8_t * burst_bits = (int8_t *)pmt::blob_data(burst_content); + + for(int jj = 0; jj < 57; jj++) + { + iBLOCK[ii*iBLOCK_SIZE+jj] = burst_bits[jj + 3]; + iBLOCK[ii*iBLOCK_SIZE+jj+57] = burst_bits[jj + 88]; //88 = 3+57+1+26+1 + } + } + //deinterleave + for (int k = 0; k < CONV_SIZE; k++) + { + conv_data[k] = iBLOCK[interleave_trans[k]]; + } + //convolutional code decode + int errors = conv_decode(decoded_data, conv_data); + //std::cout << "Errors:" << errors << " " << parity_check(decoded_data) << std::endl; + // 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[PARITY_OUTPUT_SIZE]; + if (FC_check_crc(&fc_ctx, decoded_data, crc_result) == 0) + { + //("error: sacch: parity error (errors=%d fn=%d)\n", errors, ctx->fn); + //std::cout << "Uncorrectable errors!" << std::endl; + errors = -1; + } else { + //DEBUGF("Successfully corrected parity bits! (errors=%d fn=%d)\n", errors, ctx->fn); + //std::cout << "Corrected some errors" << std::endl; + memcpy(decoded_data, crc_result, PARITY_OUTPUT_SIZE); + errors = 0; + } + } else { + //std::cout << "Everything correct" << std::endl; + } + //compress bits + unsigned char outmsg[27]; + unsigned char sbuf_len=224; + int i, j, c, pos=0; + for(i = 0; i < sbuf_len; i += 8) { + for(j = 0, c = 0; (j < 8) && (i + j < sbuf_len); j++){ + c |= (!!decoded_data[i + j]) << j; + } + outmsg[pos++] = c & 0xff; + } + + //printf("%6d %d:", (0 + 1), 0); + int jj=0; + while (jj < 23){ + printf(" %02x", outmsg[jj]);//decoded_data[jj]); + jj++; + } + printf("\n"); + fflush(stdout); + } + return; + } + } /* namespace gsm */ +} /* namespace gr */ + diff --git a/lib/decoding/control_channels_decoder_impl.h b/lib/decoding/control_channels_decoder_impl.h new file mode 100644 index 0000000..41404fa --- /dev/null +++ b/lib/decoding/control_channels_decoder_impl.h @@ -0,0 +1,48 @@ +/* -*- c++ -*- */ +/* + * Copyright 2014 <+YOU OR YOUR COMPANY+>. + * + * This 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 software 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 software; see the file COPYING. If not, write to + * the Free Software Foundation, Inc., 51 Franklin Street, + * Boston, MA 02110-1301, USA. + */ + +#ifndef INCLUDED_GSM_CONTROL_CHANNELS_DECODER_IMPL_H +#define INCLUDED_GSM_CONTROL_CHANNELS_DECODER_IMPL_H + +#include +#include "fire_crc.h" +#include "cch.h" + +namespace gr { + namespace gsm { + + class control_channels_decoder_impl : public control_channels_decoder + { + private: + unsigned int d_collected_bursts_num; + pmt::pmt_t d_bursts[4]; + unsigned short interleave_trans[CONV_SIZE]; + FC_CTX fc_ctx; + void decode(pmt::pmt_t msg); + public: + control_channels_decoder_impl(); + ~control_channels_decoder_impl(); + }; + + } // namespace gsm +} // namespace gr + +#endif /* INCLUDED_GSM_CONTROL_CHANNELS_DECODER_IMPL_H */ + diff --git a/lib/decoding/fire_crc.c b/lib/decoding/fire_crc.c new file mode 100644 index 0000000..7cdfc0b --- /dev/null +++ b/lib/decoding/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 +#include + +#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/lib/decoding/fire_crc.h b/lib/decoding/fire_crc.h new file mode 100644 index 0000000..aa6319c --- /dev/null +++ b/lib/decoding/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/lib/decoding/interleave.c b/lib/decoding/interleave.c new file mode 100644 index 0000000..8388a6f --- /dev/null +++ b/lib/decoding/interleave.c @@ -0,0 +1,71 @@ +//TODO: this file shouldn't be part of the GSM Receiver +#include +#include +#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 */ + //printf("%d -> %d\n", ictx->trans[k], k); + } +// exit(0); + return 0; +} + +int +interleave_init_facch_f(INTERLEAVE_CTX *ictx, int size, int block_size, int block_offset) +{ + 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 + block_offset) % 8; + 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) +{ + printf("Lol\n"); + int k; + for (k = 0; k < ictx->trans_size; k++) + { + printf("k=%d, ictx->trans[k]=%d\n", k, ictx->trans[k]); + dst[k] = src[ictx->trans[k]]; + } +} + diff --git a/lib/decoding/interleave.h b/lib/decoding/interleave.h new file mode 100644 index 0000000..a50434a --- /dev/null +++ b/lib/decoding/interleave.h @@ -0,0 +1,28 @@ +//TODO: this file shouldn't be part of the GSM Receiver +/* + * $Id:$ + */ + +#ifndef __GSMSP_INTERLEAVE_H__ +#define __GSMSP_INTERLEAVE_H__ 1 + +#ifdef __cplusplus +extern "C" { +#endif + +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_init_facch_f(INTERLEAVE_CTX *ictx, int size, int block_size, int block_offset); +int interleave_deinit(INTERLEAVE_CTX *ictx); +void interleave_decode(INTERLEAVE_CTX *ictx, unsigned char *dst, unsigned char *src); + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/lib/demapping/get_bcch_or_ccch_bursts_impl.cc b/lib/demapping/get_bcch_or_ccch_bursts_impl.cc new file mode 100644 index 0000000..4e8d369 --- /dev/null +++ b/lib/demapping/get_bcch_or_ccch_bursts_impl.cc @@ -0,0 +1,100 @@ +/* -*- c++ -*- */ +/* + * Copyright 2014 <+YOU OR YOUR COMPANY+>. + * + * This 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 software 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 software; 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 +#include +#include "get_bcch_or_ccch_bursts_impl.h" + +namespace gr { + namespace gsm { + + get_bcch_or_ccch_bursts::sptr + get_bcch_or_ccch_bursts::make() + { + return gnuradio::get_initial_sptr + (new get_bcch_or_ccch_bursts_impl()); + } + + /* + * The private constructor + */ + get_bcch_or_ccch_bursts_impl::get_bcch_or_ccch_bursts_impl() + : gr::block("get_bcch_or_ccch_bursts", + gr::io_signature::make(0, 0, 0), + gr::io_signature::make(0, 0, 0)) + { + message_port_register_in(pmt::mp("bursts")); + set_msg_handler(pmt::mp("bursts"), boost::bind(&get_bcch_or_ccch_bursts_impl::filter_ccch, this, _1)); + message_port_register_out(pmt::mp("bursts")); + } + + /* + * Our virtual destructor. + */ + get_bcch_or_ccch_bursts_impl::~get_bcch_or_ccch_bursts_impl() + { + } + + void get_bcch_or_ccch_bursts_impl::filter_ccch(pmt::pmt_t msg) + { + pmt::pmt_t header_blob = pmt::car(msg); + pmt::pmt_t content = pmt::cdr(msg); + gsmtap_hdr * header = (gsmtap_hdr *)pmt::blob_data(header_blob); + uint32_t frame_nr = header->frame_number; + + uint32_t fn_mod51 = header->frame_number % 51; + const int fn51_start = 6; + const int fn51_stop = 9; + + if(header->timeslot==0){ + if(fn_mod51>=fn51_start && fn_mod51<=fn51_stop){ + uint32_t ii = fn_mod51-fn51_start; + d_frame_numbers[ii]=header->frame_number; + d_bursts[ii] = msg; + } + + if(fn_mod51==fn51_stop){ + //check for a situation where some BCCH bursts were lost + //in this situation frame numbers won't be consecutive + bool frames_are_consecutive = true; + for(int jj=1; jj<4; jj++) + { + if((d_frame_numbers[jj]-d_frame_numbers[jj-1])!=1){ + frames_are_consecutive = false; + } + } + if(frames_are_consecutive) + { + //send bursts to the output + for(int jj=0; jj<4; jj++) + { + message_port_pub(pmt::mp("bursts"), d_bursts[jj]); + } + } + } + } + } + } /* namespace gsm */ +} /* namespace gr */ + diff --git a/lib/demapping/get_bcch_or_ccch_bursts_impl.h b/lib/demapping/get_bcch_or_ccch_bursts_impl.h new file mode 100644 index 0000000..c3c4c59 --- /dev/null +++ b/lib/demapping/get_bcch_or_ccch_bursts_impl.h @@ -0,0 +1,44 @@ +/* -*- c++ -*- */ +/* + * Copyright 2014 <+YOU OR YOUR COMPANY+>. + * + * This 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 software 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 software; see the file COPYING. If not, write to + * the Free Software Foundation, Inc., 51 Franklin Street, + * Boston, MA 02110-1301, USA. + */ + +#ifndef INCLUDED_GSM_GET_BCCH_OR_CCCH_BURSTS_IMPL_H +#define INCLUDED_GSM_GET_BCCH_OR_CCCH_BURSTS_IMPL_H + +#include + +namespace gr { + namespace gsm { + + class get_bcch_or_ccch_bursts_impl : public get_bcch_or_ccch_bursts + { + private: + uint32_t d_frame_numbers[4]; + pmt::pmt_t d_bursts[4]; + public: + get_bcch_or_ccch_bursts_impl(); + ~get_bcch_or_ccch_bursts_impl(); + void filter_ccch(pmt::pmt_t msg); + }; + + } // namespace gsm +} // namespace gr + +#endif /* INCLUDED_GSM_GET_BCCH_OR_CCCH_BURSTS_IMPL_H */ + diff --git a/lib/receiver/gsmtap.h b/lib/receiver/gsmtap.h deleted file mode 100644 index bf8226b..0000000 --- a/lib/receiver/gsmtap.h +++ /dev/null @@ -1,72 +0,0 @@ -#ifndef _GSMTAP_H -#define _GSMTAP_H - -/* gsmtap header, pseudo-header in front of the actua GSM payload */ - -/* GSMTAP is a generic header format for GSM protocol captures, - * it uses the IANA-assigned UDP port number 4729 and carries - * payload in various formats of GSM interfaces such as Um MAC - * blocks or Um bursts. - * - * Example programs generating GSMTAP data are airprobe - * (http://airprobe.org/) or OsmocomBB (http://bb.osmocom.org/) - */ - -#include - -#define GSMTAP_VERSION 0x02 - -#define GSMTAP_TYPE_UM 0x01 /* A Layer 2 MAC block (23 bytes) */ -#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 -#define GSMTAP_BURST_NONE 0x09 - -#define GSMTAP_CHANNEL_UNKNOWN 0x00 -#define GSMTAP_CHANNEL_BCCH 0x01 -#define GSMTAP_CHANNEL_CCCH 0x02 -#define GSMTAP_CHANNEL_RACH 0x03 -#define GSMTAP_CHANNEL_AGCH 0x04 -#define GSMTAP_CHANNEL_PCH 0x05 -#define GSMTAP_CHANNEL_SDCCH 0x06 -#define GSMTAP_CHANNEL_SDCCH4 0x07 -#define GSMTAP_CHANNEL_SDCCH8 0x08 -#define GSMTAP_CHANNEL_TCH_F 0x09 -#define GSMTAP_CHANNEL_TCH_H 0x0a -#define GSMTAP_CHANNEL_ACCH 0x80 - -#define GSMTAP_ARFCN_F_PCS 0x8000 -#define GSMTAP_ARFCN_F_UPLINK 0x4000 -#define GSMTAP_ARFCN_MASK 0x3fff - -#define GSMTAP_UDP_PORT 4729 /* officially registered with IANA */ - -struct gsmtap_hdr { - uint8_t version; /* version, set to GSMTAP_VERSION */ - uint8_t hdr_len; /* length in number of 32bit words */ - uint8_t type; /* see GSMTAP_TYPE_* */ - uint8_t timeslot; /* timeslot (0..7 on Um) */ - - uint16_t arfcn; /* ARFCN (frequency) */ - int8_t signal_dbm; /* signal level in dBm */ - int8_t snr_db; /* signal/noise ratio in dB */ - - uint32_t frame_number; /* GSM Frame Number (FN) */ - - uint8_t sub_type; /* Type of burst/channel, see above */ - uint8_t antenna_nr; /* Antenna Number */ - uint8_t sub_slot; /* sub-slot within timeslot */ - uint8_t res; /* reserved for future use (RFU) */ - -} __attribute__((packed)); - -#endif /* _GSMTAP_H */ diff --git a/lib/receiver/receiver_impl.h b/lib/receiver/receiver_impl.h index 42e6d01..200a951 100644 --- a/lib/receiver/receiver_impl.h +++ b/lib/receiver/receiver_impl.h @@ -22,9 +22,9 @@ #define INCLUDED_GSM_RECEIVER_IMPL_H #include +#include #include #include -#include namespace gr { namespace gsm { diff --git a/swig/gsm_swig.i b/swig/gsm_swig.i index 12f48a4..27163f5 100644 --- a/swig/gsm_swig.i +++ b/swig/gsm_swig.i @@ -10,6 +10,8 @@ %{ #include "gsm/receiver.h" #include "gsm/bursts_printer.h" +#include "gsm/get_bcch_or_ccch_bursts.h" +#include "gsm/control_channels_decoder.h" %} @@ -18,3 +20,7 @@ GR_SWIG_BLOCK_MAGIC2(gsm, receiver); %include "gsm/bursts_printer.h" GR_SWIG_BLOCK_MAGIC2(gsm, bursts_printer); +%include "gsm/get_bcch_or_ccch_bursts.h" +GR_SWIG_BLOCK_MAGIC2(gsm, get_bcch_or_ccch_bursts); +%include "gsm/control_channels_decoder.h" +GR_SWIG_BLOCK_MAGIC2(gsm, control_channels_decoder); -- cgit v1.2.3