aboutsummaryrefslogtreecommitdiffstats
path: root/lib
diff options
context:
space:
mode:
Diffstat (limited to 'lib')
-rw-r--r--lib/CMakeLists.txt117
-rw-r--r--lib/constants.cc.in69
-rw-r--r--lib/decoding/CMakeLists.txt32
-rw-r--r--lib/decoding/GSM610Tables.h37
-rw-r--r--lib/decoding/Viterbi.h35
-rw-r--r--lib/decoding/cch.c558
-rw-r--r--lib/decoding/cch.h63
-rw-r--r--lib/decoding/control_channels_decoder_impl.cc109
-rw-r--r--lib/decoding/control_channels_decoder_impl.h12
-rw-r--r--lib/decoding/fire_crc.c163
-rw-r--r--lib/decoding/fire_crc.h30
-rw-r--r--lib/decoding/interleave.c75
-rw-r--r--lib/decoding/interleave.h32
-rw-r--r--lib/decoding/openbts/AmrCoder.cpp (renamed from lib/decoding/AmrCoder.cpp)107
-rw-r--r--lib/decoding/openbts/AmrCoder.h (renamed from lib/decoding/AmrCoder.h)31
-rw-r--r--lib/decoding/openbts/BitVector.cpp (renamed from lib/decoding/BitVector.cpp)46
-rw-r--r--lib/decoding/openbts/BitVector.h (renamed from lib/decoding/BitVector.h)43
-rw-r--r--lib/decoding/openbts/CMakeLists.txt27
-rw-r--r--lib/decoding/openbts/GSM503Tables.cpp (renamed from lib/decoding/GSM503Tables.cpp)32
-rw-r--r--lib/decoding/openbts/GSM503Tables.h (renamed from lib/decoding/GSM503Tables.h)32
-rw-r--r--lib/decoding/openbts/GSM610Tables.cpp (renamed from lib/decoding/GSM610Tables.cpp)39
-rw-r--r--lib/decoding/openbts/GSM610Tables.h34
-rw-r--r--lib/decoding/openbts/GSM660Tables.cpp (renamed from lib/decoding/GSM660Tables.cpp)15
-rw-r--r--lib/decoding/openbts/GSM660Tables.h (renamed from lib/decoding/GSM660Tables.h)11
-rw-r--r--lib/decoding/openbts/Vector.h (renamed from lib/decoding/Vector.h)41
-rw-r--r--lib/decoding/openbts/Viterbi.h39
-rw-r--r--lib/decoding/openbts/ViterbiR204.cpp (renamed from lib/decoding/ViterbiR204.cpp)56
-rw-r--r--lib/decoding/openbts/ViterbiR204.h (renamed from lib/decoding/ViterbiR204.h)42
-rw-r--r--lib/decoding/osmocom/codec/CMakeLists.txt6
-rw-r--r--lib/decoding/osmocom/codec/codec.h81
-rw-r--r--lib/decoding/osmocom/codec/gsm610.c336
-rw-r--r--lib/decoding/osmocom/codec/gsm610_bits.h272
-rw-r--r--lib/decoding/osmocom/codec/gsm620.c297
-rw-r--r--lib/decoding/osmocom/codec/gsm660.c259
-rw-r--r--lib/decoding/osmocom/codec/gsm690.c318
-rw-r--r--lib/decoding/osmocom/coding/CMakeLists.txt27
-rw-r--r--lib/decoding/osmocom/coding/gsm0503_coding.c2066
-rw-r--r--lib/decoding/osmocom/coding/gsm0503_coding.h85
-rw-r--r--lib/decoding/osmocom/coding/gsm0503_conv.c1918
-rw-r--r--lib/decoding/osmocom/coding/gsm0503_interleaving.c685
-rw-r--r--lib/decoding/osmocom/coding/gsm0503_interleaving.h61
-rw-r--r--lib/decoding/osmocom/coding/gsm0503_mapping.c300
-rw-r--r--lib/decoding/osmocom/coding/gsm0503_mapping.h44
-rw-r--r--lib/decoding/osmocom/coding/gsm0503_parity.c137
-rw-r--r--lib/decoding/osmocom/coding/gsm0503_parity.h23
-rw-r--r--lib/decoding/osmocom/coding/gsm0503_tables.c1747
-rw-r--r--lib/decoding/osmocom/coding/gsm0503_tables.h61
-rw-r--r--lib/decoding/osmocom/core/CMakeLists.txt11
-rw-r--r--lib/decoding/osmocom/core/bit16gen.h105
-rw-r--r--lib/decoding/osmocom/core/bit32gen.h105
-rw-r--r--lib/decoding/osmocom/core/bit64gen.h105
-rw-r--r--lib/decoding/osmocom/core/bits.c311
-rw-r--r--lib/decoding/osmocom/core/bits.h122
-rw-r--r--lib/decoding/osmocom/core/bitvec.c708
-rw-r--r--lib/decoding/osmocom/core/bitvec.h87
-rw-r--r--lib/decoding/osmocom/core/conv.c644
-rw-r--r--lib/decoding/osmocom/core/conv.h139
-rw-r--r--lib/decoding/osmocom/core/conv_acc.c720
-rw-r--r--lib/decoding/osmocom/core/conv_acc_generic.c213
-rw-r--r--lib/decoding/osmocom/core/crc16gen.c120
-rw-r--r--lib/decoding/osmocom/core/crc16gen.h56
-rw-r--r--lib/decoding/osmocom/core/crc32gen.h56
-rw-r--r--lib/decoding/osmocom/core/crc64gen.c120
-rw-r--r--lib/decoding/osmocom/core/crc64gen.h56
-rw-r--r--lib/decoding/osmocom/core/crc8gen.c120
-rw-r--r--lib/decoding/osmocom/core/crc8gen.h56
-rw-r--r--lib/decoding/osmocom/core/crcgen.h34
-rw-r--r--lib/decoding/osmocom/core/defs.h53
-rw-r--r--lib/decoding/osmocom/core/endian.h62
-rw-r--r--lib/decoding/osmocom/core/linuxlist.h409
-rw-r--r--lib/decoding/osmocom/core/panic.c100
-rw-r--r--lib/decoding/osmocom/core/panic.h15
-rw-r--r--lib/decoding/osmocom/core/utils.h129
-rw-r--r--lib/decoding/osmocom/crypt/auth.h111
-rw-r--r--lib/decoding/osmocom/gsm/CMakeLists.txt6
-rw-r--r--lib/decoding/osmocom/gsm/a5.c446
-rw-r--r--lib/decoding/osmocom/gsm/a5.h55
-rw-r--r--lib/decoding/osmocom/gsm/auth_core.c252
-rw-r--r--lib/decoding/osmocom/gsm/gsm0503.h285
-rw-r--r--lib/decoding/osmocom/gsm/gsm48_ie.c1247
-rw-r--r--lib/decoding/osmocom/gsm/gsm48_ie.h116
-rw-r--r--lib/decoding/osmocom/gsm/kasumi.c188
-rw-r--r--lib/decoding/osmocom/gsm/kasumi.h48
-rw-r--r--lib/decoding/osmocom/gsm/protocol/gsm_04_08.h1683
-rw-r--r--lib/decoding/sch.c132
-rw-r--r--lib/decoding/tch_f_decoder_impl.cc54
-rw-r--r--lib/decoding/tch_f_decoder_impl.h14
-rw-r--r--lib/decryption/CMakeLists.txt22
-rw-r--r--lib/decryption/decryption_impl.cc16
-rw-r--r--lib/decryption/decryption_impl.h10
-rw-r--r--lib/demapping/CMakeLists.txt24
-rw-r--r--lib/demapping/tch_f_chans_demapper_impl.cc4
-rw-r--r--lib/demapping/tch_f_chans_demapper_impl.h4
-rw-r--r--lib/demapping/universal_ctrl_chans_demapper_impl.cc2
-rw-r--r--lib/demapping/universal_ctrl_chans_demapper_impl.h2
-rw-r--r--lib/flow_control/CMakeLists.txt29
-rw-r--r--lib/flow_control/burst_fnr_filter_impl.cc58
-rw-r--r--lib/flow_control/burst_fnr_filter_impl.h17
-rw-r--r--lib/flow_control/burst_sdcch_subslot_filter_impl.cc59
-rw-r--r--lib/flow_control/burst_sdcch_subslot_filter_impl.h14
-rw-r--r--lib/flow_control/burst_sdcch_subslot_splitter_impl.cc17
-rw-r--r--lib/flow_control/burst_sdcch_subslot_splitter_impl.h6
-rw-r--r--lib/flow_control/burst_timeslot_filter_impl.cc46
-rw-r--r--lib/flow_control/burst_timeslot_filter_impl.h11
-rw-r--r--lib/flow_control/burst_timeslot_splitter_impl.cc2
-rw-r--r--lib/flow_control/burst_timeslot_splitter_impl.h2
-rw-r--r--lib/flow_control/dummy_burst_filter_impl.cc27
-rw-r--r--lib/flow_control/dummy_burst_filter_impl.h8
-rw-r--r--lib/flow_control/uplink_downlink_splitter_impl.cc2
-rw-r--r--lib/flow_control/uplink_downlink_splitter_impl.h2
-rw-r--r--lib/misc_utils/CMakeLists.txt36
-rw-r--r--lib/misc_utils/burst_file_sink_impl.cc2
-rw-r--r--lib/misc_utils/burst_file_sink_impl.h2
-rw-r--r--lib/misc_utils/burst_file_source_impl.cc2
-rw-r--r--lib/misc_utils/burst_file_source_impl.h2
-rw-r--r--lib/misc_utils/bursts_printer_impl.cc4
-rw-r--r--lib/misc_utils/bursts_printer_impl.h2
-rw-r--r--[-rwxr-xr-x]lib/misc_utils/collect_system_info_impl.cc4
-rw-r--r--[-rwxr-xr-x]lib/misc_utils/collect_system_info_impl.h2
-rw-r--r--lib/misc_utils/controlled_fractional_resampler_cc_impl.cc2
-rw-r--r--lib/misc_utils/controlled_fractional_resampler_cc_impl.h2
-rw-r--r--lib/misc_utils/controlled_rotator_cc_impl.cc3
-rw-r--r--lib/misc_utils/controlled_rotator_cc_impl.h2
-rw-r--r--[-rwxr-xr-x]lib/misc_utils/extract_cmc_impl.cc4
-rw-r--r--[-rwxr-xr-x]lib/misc_utils/extract_cmc_impl.h2
-rw-r--r--lib/misc_utils/extract_immediate_assignment_impl.cc4
-rw-r--r--lib/misc_utils/extract_immediate_assignment_impl.h2
-rw-r--r--lib/misc_utils/extract_system_info_impl.cc6
-rw-r--r--lib/misc_utils/extract_system_info_impl.h2
-rw-r--r--lib/misc_utils/message_file_sink_impl.cc2
-rw-r--r--lib/misc_utils/message_file_sink_impl.h2
-rw-r--r--lib/misc_utils/message_file_source_impl.cc2
-rw-r--r--lib/misc_utils/message_file_source_impl.h2
-rw-r--r--lib/misc_utils/message_printer_impl.cc2
-rw-r--r--lib/misc_utils/message_printer_impl.h2
-rw-r--r--lib/misc_utils/msg_to_tag_impl.cc2
-rw-r--r--lib/misc_utils/msg_to_tag_impl.h2
-rw-r--r--lib/misc_utils/tmsi_dumper_impl.cc2
-rw-r--r--lib/misc_utils/tmsi_dumper_impl.h2
-rw-r--r--lib/qa_utils/CMakeLists.txt25
-rw-r--r--lib/qa_utils/burst_sink_impl.cc2
-rw-r--r--lib/qa_utils/burst_sink_impl.h2
-rw-r--r--lib/qa_utils/burst_source_impl.cc2
-rw-r--r--lib/qa_utils/burst_source_impl.h2
-rw-r--r--lib/qa_utils/message_sink_impl.cc2
-rw-r--r--lib/qa_utils/message_sink_impl.h2
-rw-r--r--lib/qa_utils/message_source_impl.cc2
-rw-r--r--lib/qa_utils/message_source_impl.h2
-rw-r--r--lib/receiver/CMakeLists.txt26
-rw-r--r--lib/receiver/clock_offset_control_impl.cc4
-rw-r--r--lib/receiver/clock_offset_control_impl.h2
-rw-r--r--lib/receiver/cx_channel_hopper_impl.cc2
-rw-r--r--lib/receiver/cx_channel_hopper_impl.h2
-rw-r--r--lib/receiver/gsm_constants.h2
-rw-r--r--lib/receiver/receiver_config.cc8
-rw-r--r--lib/receiver/receiver_config.h3
-rw-r--r--lib/receiver/receiver_impl.cc1658
-rw-r--r--lib/receiver/receiver_impl.h13
-rw-r--r--lib/receiver/sch.c339
-rw-r--r--lib/receiver/sch.h21
-rw-r--r--lib/receiver/viterbi_detector.cc3
-rw-r--r--lib/receiver/viterbi_detector.h2
162 files changed, 20015 insertions, 2543 deletions
diff --git a/lib/CMakeLists.txt b/lib/CMakeLists.txt
index 680c41f..ebe685e 100644
--- a/lib/CMakeLists.txt
+++ b/lib/CMakeLists.txt
@@ -17,69 +17,88 @@
# the Free Software Foundation, Inc., 51 Franklin Street,
# Boston, MA 02110-1301, USA.
+include(GrPlatform) #define LIB_SUFFIX
+include(GrMiscUtils)
+
########################################################################
-# Setup library
+# Add sources macro
########################################################################
-include(GrPlatform) #define LIB_SUFFIX
+set(grgsm_sources "")
+
+macro (add_sources)
+ file (RELATIVE_PATH _relPath "${PROJECT_SOURCE_DIR}/lib" "${CMAKE_CURRENT_SOURCE_DIR}")
+ foreach (_src ${ARGN})
+ if (_relPath)
+ list (APPEND grgsm_sources "${_relPath}/${_src}")
+ else()
+ list (APPEND grgsm_sources "${_src}")
+ endif()
+ endforeach()
+ if (_relPath)
+ # propagate grgsm_sources to parent directory
+ set (grgsm_sources ${grgsm_sources} PARENT_SCOPE)
+ endif()
+endmacro()
+
+########################################################################
+# Handle the generated constants
+########################################################################
+execute_process(COMMAND ${PYTHON_EXECUTABLE} -c
+ "import time;print time.strftime('%a, %d %b %Y %H:%M:%S', time.gmtime())"
+ OUTPUT_VARIABLE BUILD_DATE OUTPUT_STRIP_TRAILING_WHITESPACE
+)
+message(STATUS "Loading build date ${BUILD_DATE} into constants...")
+message(STATUS "Loading version ${VERSION} into constants...")
+#double escape for windows backslash path separators
+string(REPLACE "\\" "\\\\" prefix "${prefix}")
+
+configure_file(
+ ${CMAKE_CURRENT_SOURCE_DIR}/constants.cc.in
+ ${CMAKE_CURRENT_BINARY_DIR}/constants.cc
+ ESCAPE_QUOTES
+@ONLY)
+
+list(APPEND grgsm_sources ${CMAKE_CURRENT_BINARY_DIR}/constants.cc)
+#########################################################################
+# Subdirecories
+#########################################################################
+add_subdirectory(decoding)
+add_subdirectory(decryption)
+add_subdirectory(demapping)
+add_subdirectory(flow_control)
+add_subdirectory(misc_utils)
+add_subdirectory(qa_utils)
+add_subdirectory(receiver)
+
+########################################################################
+# Setup library
+########################################################################
include_directories(${Boost_INCLUDE_DIR} receiver)
link_directories(${Boost_LIBRARY_DIRS})
-list(APPEND grgsm_sources
- receiver/receiver_impl.cc
- receiver/receiver_config.cc
- receiver/viterbi_detector.cc
- receiver/sch.c
- receiver/clock_offset_control_impl.cc
- receiver/cx_channel_hopper_impl.cc
- demapping/universal_ctrl_chans_demapper_impl.cc
- demapping/tch_f_chans_demapper_impl.cc
- decoding/control_channels_decoder_impl.cc
- decoding/cch.c
- decoding/fire_crc.c
- decoding/tch_f_decoder_impl.cc
- decoding/AmrCoder.cpp
- decoding/BitVector.cpp
- decoding/GSM610Tables.cpp
- decoding/GSM660Tables.cpp
- decoding/GSM503Tables.cpp
- decoding/ViterbiR204.cpp
- flow_control/burst_timeslot_splitter_impl.cc
- flow_control/burst_sdcch_subslot_splitter_impl.cc
- flow_control/burst_timeslot_filter_impl.cc
- flow_control/burst_sdcch_subslot_filter_impl.cc
- flow_control/burst_fnr_filter_impl.cc
- flow_control/dummy_burst_filter_impl.cc
- flow_control/uplink_downlink_splitter_impl.cc
- misc_utils/collect_system_info_impl.cc
- misc_utils/controlled_rotator_cc_impl.cc
- misc_utils/controlled_fractional_resampler_cc_impl.cc
- misc_utils/msg_to_tag_impl.cc
- misc_utils/message_printer_impl.cc
- misc_utils/tmsi_dumper_impl.cc
- misc_utils/burst_file_sink_impl.cc
- misc_utils/burst_file_source_impl.cc
- misc_utils/message_file_sink_impl.cc
- misc_utils/message_file_source_impl.cc
- misc_utils/bursts_printer_impl.cc
- misc_utils/extract_system_info_impl.cc
- misc_utils/extract_immediate_assignment_impl.cc
- misc_utils/extract_cmc_impl.cc
- qa_utils/burst_sink_impl.cc
- qa_utils/burst_source_impl.cc
- qa_utils/message_source_impl.cc
- qa_utils/message_sink_impl.cc
- decryption/decryption_impl.cc
-)
+
+set(grgsm_link_libraries "")
+list (APPEND grgsm_link_libraries ${Boost_LIBRARIES})
+list (APPEND grgsm_link_libraries ${GNURADIO_ALL_LIBRARIES})
+list (APPEND grgsm_link_libraries ${VOLK_LIBRARIES})
+list (APPEND grgsm_link_libraries ${Boost_LIBRARIES})
+if(WIN32)
+ list (APPEND grgsm_link_libraries wsock32)
+ list (APPEND grgsm_link_libraries ws2_32)
+endif()
add_library(grgsm SHARED ${grgsm_sources})
-target_link_libraries(grgsm ${Boost_LIBRARIES} ${GNURADIO_ALL_LIBRARIES} ${VOLK_LIBRARIES} ${LIBOSMOCORE_LIBRARIES}
+target_link_libraries(grgsm ${grgsm_link_libraries} #${LIBOSMOCORE_LIBRARIES}
+# ${LIBOSMOCODEC_LIBRARIES} ${LIBOSMOCORE_LIBRARIES} ${LIBOSMOCODING_LIBRARIES}
# libraries required by plotting.h - have troubles to be installed by pybombs
# boost_iostreams
# boost_system
# boost_filesystem
)
+
set_target_properties(grgsm PROPERTIES DEFINE_SYMBOL "grgsm_EXPORTS")
+GR_LIBRARY_FOO(grgsm)
########################################################################
# Install built library files
diff --git a/lib/constants.cc.in b/lib/constants.cc.in
new file mode 100644
index 0000000..e6d5a51
--- /dev/null
+++ b/lib/constants.cc.in
@@ -0,0 +1,69 @@
+/* -*- c++ -*- */
+/*
+ * Copyright 2006,2009,2013 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.
+ */
+
+#if HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <stdlib.h>
+#include <grgsm/constants.h>
+
+namespace gr {
+ namespace gsm{
+ const std::string
+ build_date()
+ {
+ return "@BUILD_DATE@";
+ }
+
+ const std::string
+ version()
+ {
+ return "@VERSION@";
+ }
+
+ // Return individual parts of the version
+ const std::string
+ major_version()
+ {
+ return "@MAJOR_VERSION@";
+ }
+
+ const std::string
+ api_version()
+ {
+ return "@API_COMPAT@";
+ }
+
+ const std::string
+ minor_version()
+ {
+ return "@MINOR_VERSION@";
+ }
+
+ const std::string
+ maint_version()
+ {
+ return "@MAINT_VERSION@";
+ }
+ } /* namespace gsm */
+} /* namespace gr */
diff --git a/lib/decoding/CMakeLists.txt b/lib/decoding/CMakeLists.txt
new file mode 100644
index 0000000..cbf2a8e
--- /dev/null
+++ b/lib/decoding/CMakeLists.txt
@@ -0,0 +1,32 @@
+# Copyright 2011,2012 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.
+
+add_subdirectory(osmocom/coding)
+add_subdirectory(osmocom/core)
+add_subdirectory(osmocom/codec)
+add_subdirectory(osmocom/gsm)
+add_subdirectory(osmocom/crypt)
+add_subdirectory(openbts)
+
+add_sources(
+ control_channels_decoder_impl.cc
+ tch_f_decoder_impl.cc
+ sch.c
+)
+
diff --git a/lib/decoding/GSM610Tables.h b/lib/decoding/GSM610Tables.h
deleted file mode 100644
index 53a8e1c..0000000
--- a/lib/decoding/GSM610Tables.h
+++ /dev/null
@@ -1,37 +0,0 @@
-/*
-* 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/lib/decoding/Viterbi.h b/lib/decoding/Viterbi.h
deleted file mode 100644
index 8635bf5..0000000
--- a/lib/decoding/Viterbi.h
+++ /dev/null
@@ -1,35 +0,0 @@
-/*
-* Copyright 2013, 2014 Range Networks, Inc.
-*
-* This software is distributed under multiple licenses;
-* see the COPYING file in the main directory for licensing
-* information for this specific distribution.
-*
-* This use of this software may be subject to additional restrictions.
-* See the LEGAL file in the main directory for details.
-
- 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.
-
-*/
-
-
-#ifndef _VITERBI_H_
-#define _VITERBI_H_ 1
-
-// (pat) Virtual base class for Viterbi and Turbo coder/decoders.
-class ViterbiBase {
- public:
- virtual void encode(const BitVector &in, BitVector& target) const = 0;
- virtual void decode(const SoftVector &in, BitVector& target) = 0;
- // (pat) Return error count from most recent decoder run.
- // If you get -1 from this, the method is not defined in the Viterbi class.
- virtual int getBEC() { return -1; }
- //virtual ~ViterbiBase(); Currently None of these have destructors.
-
- // These functions are logically part of the Viterbi functionality, even though they do not use any class variables.
- unsigned applyPoly(uint64_t val, uint64_t poly);
- unsigned applyPoly(uint64_t val, uint64_t poly, unsigned order);
-};
-#endif
diff --git a/lib/decoding/cch.c b/lib/decoding/cch.c
deleted file mode 100644
index df83d3e..0000000
--- a/lib/decoding/cch.c
+++ /dev/null
@@ -1,558 +0,0 @@
-/*
- The Hacker's Choice - http://www.thc.org
- Part of THC's GSM SCANNER PROJECT
-*/
-
-//#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];
-}
- */
-
-
-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
deleted file mode 100644
index e371213..0000000
--- a/lib/decoding/cch.h
+++ /dev/null
@@ -1,63 +0,0 @@
-/*
- The Hacker's Choice - http://www.thc.org
- Part of THC's GSM SCANNER PROJECT
-*/
-
-#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
index 532e255..a6abaa3 100644
--- a/lib/decoding/control_channels_decoder_impl.cc
+++ b/lib/decoding/control_channels_decoder_impl.cc
@@ -1,7 +1,7 @@
/* -*- c++ -*- */
/*
* @file
- * @author Piotr Krysik <ptrkrysik@gmail.com>
+ * @author (C) 2014 by Piotr Krysik <ptrkrysik@gmail.com>
* @section LICENSE
*
* Gr-gsm is free software; you can redistribute it and/or modify
@@ -33,6 +33,25 @@
namespace gr {
namespace gsm {
+ static int ubits2sbits(ubit_t *ubits, sbit_t *sbits, int count)
+ {
+ int i;
+
+ for (i = 0; i < count; i++) {
+ if (*ubits == 0x23) {
+ ubits++;
+ sbits++;
+ continue;
+ }
+ if ((*ubits++) & 1)
+ *sbits++ = -127;
+ else
+ *sbits++ = 127;
+ }
+
+ return count;
+ }
+
control_channels_decoder::sptr
control_channels_decoder::make()
{
@@ -49,18 +68,6 @@ namespace gr {
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
- }
-
- //initialize decoder
- FC_init(&fc_ctx, 40, 184);
-
//setup input/output ports
message_port_register_in(pmt::mp("bursts"));
set_msg_handler(pmt::mp("bursts"), boost::bind(&control_channels_decoder_impl::decode, this, _1));
@@ -73,78 +80,44 @@ namespace gr {
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];
+ ubit_t bursts_u[116 * 4];
+ sbit_t bursts_s[116 * 4];
+ uint8_t result[23];
+ int n_errors, n_bits_total;
+ int8_t header_plus_data[sizeof(gsmtap_hdr)+DATA_BYTES];
+
d_bursts[d_collected_bursts_num] = msg;
d_collected_bursts_num++;
+
//get convecutive bursts
-
if(d_collected_bursts_num==4)
{
d_collected_bursts_num=0;
- //reorganize data
+ //reorganize data from input bursts
for(int ii = 0; ii < 4; ii++)
{
pmt::pmt_t header_plus_burst = pmt::cdr(d_bursts[ii]);
int8_t * burst_bits = (int8_t *)(pmt::blob_data(header_plus_burst))+sizeof(gsmtap_hdr);
- 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]];
+ memcpy(&bursts_u[ii*116], &burst_bits[3],58);
+ memcpy(&bursts_u[ii*116+58], &burst_bits[3+57+1+26],58);
}
- //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.
+ //convert to soft bits
+ ubits2sbits(bursts_u, bursts_s, 116 * 4);
+ //decode
+ gsm0503_xcch_decode(result, bursts_s, &n_errors, &n_bits_total);
- 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;
- return;
- } 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[28];
- 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;
- }
-
- //send message with header of the first burst
+ //extract header of the first burst of the four bursts
pmt::pmt_t first_header_plus_burst = pmt::cdr(d_bursts[0]);
gsmtap_hdr * header = (gsmtap_hdr *)pmt::blob_data(first_header_plus_burst);
- int8_t header_plus_data[sizeof(gsmtap_hdr)+DATA_BYTES];
+ //copy header and data
memcpy(header_plus_data, header, sizeof(gsmtap_hdr));
- memcpy(header_plus_data+sizeof(gsmtap_hdr), outmsg, DATA_BYTES);
+ memcpy(header_plus_data+sizeof(gsmtap_hdr), result, DATA_BYTES);
+ //set data type in the header
((gsmtap_hdr*)header_plus_data)->type = GSMTAP_TYPE_UM;
-
- pmt::pmt_t msg_binary_blob = pmt::make_blob(header_plus_data,DATA_BYTES+sizeof(gsmtap_hdr));
- pmt::pmt_t msg_out = pmt::cons(pmt::PMT_NIL, msg_binary_blob);
-
+ //prepare message
+ pmt::pmt_t msg_out = pmt::cons(pmt::PMT_NIL, pmt::make_blob(header_plus_data,DATA_BYTES+sizeof(gsmtap_hdr)));
+ //send message to the output
message_port_pub(pmt::mp("msgs"), msg_out);
}
return;
diff --git a/lib/decoding/control_channels_decoder_impl.h b/lib/decoding/control_channels_decoder_impl.h
index 2fa3dce..3464324 100644
--- a/lib/decoding/control_channels_decoder_impl.h
+++ b/lib/decoding/control_channels_decoder_impl.h
@@ -1,7 +1,7 @@
/* -*- c++ -*- */
/*
* @file
- * @author Piotr Krysik <ptrkrysik@gmail.com>
+ * @author (C) 2014 by Piotr Krysik <ptrkrysik@gmail.com>
* @section LICENSE
*
* Gr-gsm is free software; you can redistribute it and/or modify
@@ -24,8 +24,9 @@
#define INCLUDED_GSM_CONTROL_CHANNELS_DECODER_IMPL_H
#include <grgsm/decoding/control_channels_decoder.h>
-#include "fire_crc.h"
-#include "cch.h"
+extern "C" {
+ #include "osmocom/coding/gsm0503_coding.h"
+}
namespace gr {
namespace gsm {
@@ -35,9 +36,8 @@ namespace gr {
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);
+
+ void decode(pmt::pmt_t msg);
public:
control_channels_decoder_impl();
~control_channels_decoder_impl();
diff --git a/lib/decoding/fire_crc.c b/lib/decoding/fire_crc.c
deleted file mode 100644
index 5800517..0000000
--- a/lib/decoding/fire_crc.c
+++ /dev/null
@@ -1,163 +0,0 @@
-/*
- The Hacker's Choice - http://www.thc.org
- Part of THC's GSM SCANNER PROJECT
-*/
-
-
-#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");
- return 0;
-}
-
-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
deleted file mode 100644
index d02021e..0000000
--- a/lib/decoding/fire_crc.h
+++ /dev/null
@@ -1,30 +0,0 @@
-/*
- The Hacker's Choice - http://www.thc.org
- Part of THC's GSM SCANNER PROJECT
-*/
-
-
-
-#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
deleted file mode 100644
index c6304db..0000000
--- a/lib/decoding/interleave.c
+++ /dev/null
@@ -1,75 +0,0 @@
-/*
- The Hacker's Choice - http://www.thc.org
- Part of THC's GSM SCANNER PROJECT
-*/
-
-#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 */
- //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
deleted file mode 100644
index aba1dfd..0000000
--- a/lib/decoding/interleave.h
+++ /dev/null
@@ -1,32 +0,0 @@
-/*
- The Hacker's Choice - http://www.thc.org
- Part of THC's GSM SCANNER PROJECT
-*/
-
-/*
- * $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/decoding/AmrCoder.cpp b/lib/decoding/openbts/AmrCoder.cpp
index d02e69c..db5b2d8 100644
--- a/lib/decoding/AmrCoder.cpp
+++ b/lib/decoding/openbts/AmrCoder.cpp
@@ -1,18 +1,22 @@
/*
-* Copyright 2013, 2014 Range Networks, Inc.
-*
-* This software is distributed under multiple licenses;
-* see the COPYING file in the main directory for licensing
-* information for this specific distribution.
-*
-* This use of this software may be subject to additional restrictions.
-* See the LEGAL file in the main directory for details.
-
- 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.
-
-*/
+ * Copyright 2013, 2014 Range Networks, Inc.
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero 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 Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ *
+ * This use of this software may be subject to additional restrictions.
+ * See the LEGAL file in the main directory for details.
+ */
#include "BitVector.h"
@@ -20,11 +24,10 @@
#include <iostream>
#include <stdio.h>
#include <sstream>
+#include <cstdlib>
using namespace std;
-
-
ViterbiTCH_AFS12_2::ViterbiTCH_AFS12_2()
{
assert(mDeferral < 32);
@@ -374,7 +377,7 @@ void ViterbiTCH_AFS12_2::decode(const SoftVector &in, BitVector& target)
assert(sz == decoder.iRate()*target.size());
// Build a "history" array where each element contains the full history.
- uint32_t history[ctsz];
+ uint32_t * history = (uint32_t *)malloc(sizeof(uint32_t)*ctsz);
{
BitVector bits = in.sliced();
uint32_t accum = 0;
@@ -390,8 +393,8 @@ void ViterbiTCH_AFS12_2::decode(const SoftVector &in, BitVector& target)
}
// Precompute metric tables.
- float matchCostTable[ctsz];
- float mismatchCostTable[ctsz];
+ float * matchCostTable = (float *)malloc(sizeof(float)*ctsz);
+ float * mismatchCostTable = (float *)malloc(sizeof(float)*ctsz);
{
const float *dp = in.begin();
for (size_t i=0; i<sz; i++) {
@@ -441,6 +444,9 @@ void ViterbiTCH_AFS12_2::decode(const SoftVector &in, BitVector& target)
oCount++;
}
}
+ free(history);
+ free(matchCostTable);
+ free(mismatchCostTable);
}
@@ -578,7 +584,7 @@ void ViterbiTCH_AFS10_2::decode(const SoftVector &in, BitVector& target)
assert(sz == decoder.iRate()*target.size());
// Build a "history" array where each element contains the full history.
- uint32_t history[ctsz];
+ uint32_t * history = (uint32_t *)malloc(sizeof(uint32_t)*ctsz);
{
BitVector bits = in.sliced();
uint32_t accum = 0;
@@ -594,8 +600,8 @@ void ViterbiTCH_AFS10_2::decode(const SoftVector &in, BitVector& target)
}
// Precompute metric tables.
- float matchCostTable[ctsz];
- float mismatchCostTable[ctsz];
+ float * matchCostTable = (float *)malloc(sizeof(float)*ctsz);
+ float * mismatchCostTable = (float *)malloc(sizeof(float)*ctsz);
{
const float *dp = in.begin();
for (size_t i=0; i<sz; i++) {
@@ -645,6 +651,9 @@ void ViterbiTCH_AFS10_2::decode(const SoftVector &in, BitVector& target)
oCount++;
}
}
+ free(history);
+ free(matchCostTable);
+ free(mismatchCostTable);
}
@@ -782,7 +791,7 @@ void ViterbiTCH_AFS7_95::decode(const SoftVector &in, BitVector& target)
assert(sz == decoder.iRate()*target.size());
// Build a "history" array where each element contains the full history.
- uint32_t history[ctsz];
+ uint32_t * history = (uint32_t *)malloc(sizeof(uint32_t)*ctsz);
{
BitVector bits = in.sliced();
uint32_t accum = 0;
@@ -798,8 +807,8 @@ void ViterbiTCH_AFS7_95::decode(const SoftVector &in, BitVector& target)
}
// Precompute metric tables.
- float matchCostTable[ctsz];
- float mismatchCostTable[ctsz];
+ float * matchCostTable = (float *)malloc(sizeof(float)*ctsz);
+ float * mismatchCostTable = (float *)malloc(sizeof(float)*ctsz);
{
const float *dp = in.begin();
for (size_t i=0; i<sz; i++) {
@@ -849,6 +858,9 @@ void ViterbiTCH_AFS7_95::decode(const SoftVector &in, BitVector& target)
oCount++;
}
}
+ free(history);
+ free(matchCostTable);
+ free(mismatchCostTable);
}
@@ -986,7 +998,7 @@ void ViterbiTCH_AFS7_4::decode(const SoftVector &in, BitVector& target)
assert(sz == decoder.iRate()*target.size());
// Build a "history" array where each element contains the full history.
- uint32_t history[ctsz];
+ uint32_t * history = (uint32_t *)malloc(sizeof(uint32_t)*ctsz);
{
BitVector bits = in.sliced();
uint32_t accum = 0;
@@ -1002,8 +1014,8 @@ void ViterbiTCH_AFS7_4::decode(const SoftVector &in, BitVector& target)
}
// Precompute metric tables.
- float matchCostTable[ctsz];
- float mismatchCostTable[ctsz];
+ float * matchCostTable = (float *)malloc(sizeof(float)*ctsz);
+ float * mismatchCostTable = (float *)malloc(sizeof(float)*ctsz);
{
const float *dp = in.begin();
for (size_t i=0; i<sz; i++) {
@@ -1053,6 +1065,9 @@ void ViterbiTCH_AFS7_4::decode(const SoftVector &in, BitVector& target)
oCount++;
}
}
+ free(history);
+ free(matchCostTable);
+ free(mismatchCostTable);
}
@@ -1192,7 +1207,7 @@ void ViterbiTCH_AFS6_7::decode(const SoftVector &in, BitVector& target)
assert(sz == decoder.iRate()*target.size());
// Build a "history" array where each element contains the full history.
- uint32_t history[ctsz];
+ uint32_t * history = (uint32_t *)malloc(sizeof(uint32_t)*ctsz);
{
BitVector bits = in.sliced();
uint32_t accum = 0;
@@ -1208,8 +1223,8 @@ void ViterbiTCH_AFS6_7::decode(const SoftVector &in, BitVector& target)
}
// Precompute metric tables.
- float matchCostTable[ctsz];
- float mismatchCostTable[ctsz];
+ float * matchCostTable = (float *)malloc(sizeof(float)*ctsz);
+ float * mismatchCostTable = (float *)malloc(sizeof(float)*ctsz);
{
const float *dp = in.begin();
for (size_t i=0; i<sz; i++) {
@@ -1259,6 +1274,9 @@ void ViterbiTCH_AFS6_7::decode(const SoftVector &in, BitVector& target)
oCount++;
}
}
+ free(history);
+ free(matchCostTable);
+ free(mismatchCostTable);
}
@@ -1398,7 +1416,7 @@ void ViterbiTCH_AFS5_9::decode(const SoftVector &in, BitVector& target)
assert(sz == decoder.iRate()*target.size());
// Build a "history" array where each element contains the full history.
- uint32_t history[ctsz];
+ uint32_t * history = (uint32_t *)malloc(sizeof(uint32_t)*ctsz);
{
BitVector bits = in.sliced();
uint32_t accum = 0;
@@ -1414,8 +1432,8 @@ void ViterbiTCH_AFS5_9::decode(const SoftVector &in, BitVector& target)
}
// Precompute metric tables.
- float matchCostTable[ctsz];
- float mismatchCostTable[ctsz];
+ float * matchCostTable = (float *)malloc(sizeof(float)*ctsz);
+ float * mismatchCostTable = (float *)malloc(sizeof(float)*ctsz);
{
const float *dp = in.begin();
for (size_t i=0; i<sz; i++) {
@@ -1465,6 +1483,9 @@ void ViterbiTCH_AFS5_9::decode(const SoftVector &in, BitVector& target)
oCount++;
}
}
+ free(history);
+ free(matchCostTable);
+ free(mismatchCostTable);
}
@@ -1606,7 +1627,7 @@ void ViterbiTCH_AFS5_15::decode(const SoftVector &in, BitVector& target)
assert(sz == decoder.iRate()*target.size());
// Build a "history" array where each element contains the full history.
- uint32_t history[ctsz];
+ uint32_t * history = (uint32_t *)malloc(sizeof(uint32_t)*ctsz);
{
BitVector bits = in.sliced();
uint32_t accum = 0;
@@ -1622,8 +1643,8 @@ void ViterbiTCH_AFS5_15::decode(const SoftVector &in, BitVector& target)
}
// Precompute metric tables.
- float matchCostTable[ctsz];
- float mismatchCostTable[ctsz];
+ float * matchCostTable = (float *)malloc(sizeof(float)*ctsz);
+ float * mismatchCostTable = (float *)malloc(sizeof(float)*ctsz);
{
const float *dp = in.begin();
for (size_t i=0; i<sz; i++) {
@@ -1673,6 +1694,9 @@ void ViterbiTCH_AFS5_15::decode(const SoftVector &in, BitVector& target)
oCount++;
}
}
+ free(history);
+ free(matchCostTable);
+ free(mismatchCostTable);
}
@@ -1814,7 +1838,7 @@ void ViterbiTCH_AFS4_75::decode(const SoftVector &in, BitVector& target)
assert(sz == decoder.iRate()*target.size());
// Build a "history" array where each element contains the full history.
- uint32_t history[ctsz];
+ uint32_t * history = (uint32_t *)malloc(sizeof(uint32_t)*ctsz);
{
BitVector bits = in.sliced();
uint32_t accum = 0;
@@ -1830,8 +1854,8 @@ void ViterbiTCH_AFS4_75::decode(const SoftVector &in, BitVector& target)
}
// Precompute metric tables.
- float matchCostTable[ctsz];
- float mismatchCostTable[ctsz];
+ float * matchCostTable = (float *)malloc(sizeof(float)*ctsz);
+ float * mismatchCostTable = (float *)malloc(sizeof(float)*ctsz);
{
const float *dp = in.begin();
for (size_t i=0; i<sz; i++) {
@@ -1881,6 +1905,9 @@ void ViterbiTCH_AFS4_75::decode(const SoftVector &in, BitVector& target)
oCount++;
}
}
+ free(history);
+ free(matchCostTable);
+ free(mismatchCostTable);
}
diff --git a/lib/decoding/AmrCoder.h b/lib/decoding/openbts/AmrCoder.h
index c1df823..ae49bd0 100644
--- a/lib/decoding/AmrCoder.h
+++ b/lib/decoding/openbts/AmrCoder.h
@@ -1,18 +1,23 @@
/*
-* Copyright 2013, 2014 Range Networks, Inc.
-*
-* This software is distributed under multiple licenses;
-* see the COPYING file in the main directory for licensing
-* information for this specific distribution.
-*
-* This use of this software may be subject to additional restrictions.
-* See the LEGAL file in the main directory for details.
-
- 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.
+ * Copyright 2013, 2014 Range Networks, Inc.
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero 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 Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ *
+ * This use of this software may be subject to additional restrictions.
+ * See the LEGAL file in the main directory for details.
+ */
-*/
#ifndef _AMRCODER_H_
#define _AMRCODER_H_
#include <stdint.h>
diff --git a/lib/decoding/BitVector.cpp b/lib/decoding/openbts/BitVector.cpp
index 86f326a..00730f6 100644
--- a/lib/decoding/BitVector.cpp
+++ b/lib/decoding/openbts/BitVector.cpp
@@ -1,31 +1,23 @@
/*
-* Copyright 2008, 2009, 2014 Free Software Foundation, Inc.
-* Copyright 2014 Range Networks, Inc.
-*
-*
-* This software is distributed under the terms of the GNU Affero Public License.
-* See the COPYING file in the main directory for details.
-*
-* This use of this software may be subject to additional restrictions.
-* See the LEGAL 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 Affero 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 Affero General Public License for more details.
-
- You should have received a copy of the GNU Affero General Public License
- along with this program. If not, see <http://www.gnu.org/licenses/>.
-
-*/
-
-
-
+ * Copyright 2008, 2009, 2014 Free Software Foundation, Inc.
+ * Copyright 2014 Range Networks, Inc.
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero 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 Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ *
+ * This use of this software may be subject to additional restrictions.
+ * See the LEGAL file in the main directory for details.
+ */
#include "BitVector.h"
#include <iostream>
diff --git a/lib/decoding/BitVector.h b/lib/decoding/openbts/BitVector.h
index 0899817..c8782eb 100644
--- a/lib/decoding/BitVector.h
+++ b/lib/decoding/openbts/BitVector.h
@@ -1,28 +1,23 @@
/*
-* Copyright 2008, 2009, 2014 Free Software Foundation, Inc.
-* Copyright 2014 Range Networks, Inc.
-*
-* This software is distributed under the terms of the GNU Affero Public License.
-* See the COPYING file in the main directory for details.
-*
-* This use of this software may be subject to additional restrictions.
-* See the LEGAL 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 Affero 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 Affero General Public License for more details.
-
- You should have received a copy of the GNU Affero General Public License
- along with this program. If not, see <http://www.gnu.org/licenses/>.
-
-*/
-
+ * Copyright 2008, 2009, 2014 Free Software Foundation, Inc.
+ * Copyright 2014 Range Networks, Inc.
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero 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 Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ *
+ * This use of this software may be subject to additional restrictions.
+ * See the LEGAL file in the main directory for details.
+ */
#ifndef BITVECTORS_H
#define BITVECTORS_H
diff --git a/lib/decoding/openbts/CMakeLists.txt b/lib/decoding/openbts/CMakeLists.txt
new file mode 100644
index 0000000..18977b1
--- /dev/null
+++ b/lib/decoding/openbts/CMakeLists.txt
@@ -0,0 +1,27 @@
+# Copyright 2011,2012 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.
+
+add_sources(
+ AmrCoder.cpp
+ BitVector.cpp
+ GSM610Tables.cpp
+ GSM660Tables.cpp
+ GSM503Tables.cpp
+ ViterbiR204.cpp
+)
diff --git a/lib/decoding/GSM503Tables.cpp b/lib/decoding/openbts/GSM503Tables.cpp
index 02150f2..fb0f235 100644
--- a/lib/decoding/GSM503Tables.cpp
+++ b/lib/decoding/openbts/GSM503Tables.cpp
@@ -1,18 +1,22 @@
/*
-* Copyright 2012, 2014 Range Networks, Inc.
-*
-* This software is distributed under multiple licenses; see the COPYING file in the main directory for licensing information for this specific distribution.
-*
-* This use of this software may be subject to additional restrictions.
-* See the LEGAL file in the main directory for details.
-
- 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.
-
-*/
-
-
+ * Copyright 2012, 2014 Range Networks, Inc.
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero 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 Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ *
+ * This use of this software may be subject to additional restrictions.
+ * See the LEGAL file in the main directory for details.
+ */
#include "GSM503Tables.h"
diff --git a/lib/decoding/GSM503Tables.h b/lib/decoding/openbts/GSM503Tables.h
index 6b6327c..1fe405e 100644
--- a/lib/decoding/GSM503Tables.h
+++ b/lib/decoding/openbts/GSM503Tables.h
@@ -1,18 +1,22 @@
/*
-* Copyright 2012, 2014 Range Networks, Inc.
-*
-* This software is distributed under multiple licenses; see the COPYING file in the main directory for licensing information for this specific distribution.
-*
-* This use of this software may be subject to additional restrictions.
-* See the LEGAL file in the main directory for details.
-
- 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.
-
-*/
-
-
+ * Copyright 2012, 2014 Range Networks, Inc.
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero 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 Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ *
+ * This use of this software may be subject to additional restrictions.
+ * See the LEGAL file in the main directory for details.
+ */
#ifndef GSM503TABLES_H
#define GSM503TABLES_H
diff --git a/lib/decoding/GSM610Tables.cpp b/lib/decoding/openbts/GSM610Tables.cpp
index 38f643f..1b08496 100644
--- a/lib/decoding/GSM610Tables.cpp
+++ b/lib/decoding/openbts/GSM610Tables.cpp
@@ -1,25 +1,22 @@
/*
-* 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/>.
-
-*/
-
-
+ * 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 Affero 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 Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ *
+ * This use of this software may be subject to additional restrictions.
+ * See the LEGAL file in the main directory for details.
+ */
#include "GSM610Tables.h"
diff --git a/lib/decoding/openbts/GSM610Tables.h b/lib/decoding/openbts/GSM610Tables.h
new file mode 100644
index 0000000..0b8d64f
--- /dev/null
+++ b/lib/decoding/openbts/GSM610Tables.h
@@ -0,0 +1,34 @@
+/*
+ * 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 Affero 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 Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ *
+ * This use of this software may be subject to additional restrictions.
+ * See the LEGAL file in the main directory for details.
+ */
+
+#ifndef GSM610TABLES_H
+#define GSM610TABLES_H
+
+
+
+namespace GSM {
+
+/** Table #2 from GSM 05.03 */
+extern unsigned int g610BitOrder[260];
+
+}
+
+
+#endif
diff --git a/lib/decoding/GSM660Tables.cpp b/lib/decoding/openbts/GSM660Tables.cpp
index 520a6c1..e5687c3 100644
--- a/lib/decoding/GSM660Tables.cpp
+++ b/lib/decoding/openbts/GSM660Tables.cpp
@@ -1,23 +1,26 @@
-/* EFR (GSM 06.60) importance bit ordering */
-
/*
* Copyright 2010 Sylvain Munaut <tnt@246tNt.com>
* All Rights Reserved
*
* 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
+ * it under the terms of the GNU Affero 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.
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
*
- * 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 use of this software may be subject to additional restrictions.
+ * See the LEGAL file in the main directory for details.
*/
+/* EFR (GSM 06.60) importance bit ordering */
+
#include "GSM660Tables.h"
unsigned int GSM::g660BitOrder[260] = {
diff --git a/lib/decoding/GSM660Tables.h b/lib/decoding/openbts/GSM660Tables.h
index 9d639fc..9052c3b 100644
--- a/lib/decoding/GSM660Tables.h
+++ b/lib/decoding/openbts/GSM660Tables.h
@@ -5,17 +5,20 @@
* All Rights Reserved
*
* 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
+ * it under the terms of the GNU Affero 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.
+ * GNU Affero 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/>.
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ *
+ * This use of this software may be subject to additional restrictions.
+ * See the LEGAL file in the main directory for details.
*/
#ifndef GSM660TABLES_H
diff --git a/lib/decoding/Vector.h b/lib/decoding/openbts/Vector.h
index 23b4dd3..e1224e8 100644
--- a/lib/decoding/Vector.h
+++ b/lib/decoding/openbts/Vector.h
@@ -1,26 +1,25 @@
-/**@file Simplified Vector template with aliases. */
/*
-* Copyright 2008 Free Software Foundation, Inc.
-* Copyright 2014 Range Networks, Inc.
-*
-* This software is distributed under the terms of the GNU Affero Public License.
-* See the COPYING file in the main directory for details.
-*
-* This use of this software may be subject to additional restrictions.
-* See the LEGAL 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 Affero 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 Affero General Public License for more details.
- You should have received a copy of the GNU Affero General Public License
- along with this program. If not, see <http://www.gnu.org/licenses/>.
-*/
-
+ * Copyright 2008 Free Software Foundation, Inc.
+ * Copyright 2014 Range Networks, Inc.
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero 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 Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ *
+ * This use of this software may be subject to additional restrictions.
+ * See the LEGAL file in the main directory for details.
+ */
+/**@file Simplified Vector template with aliases. */
#ifndef VECTOR_H
diff --git a/lib/decoding/openbts/Viterbi.h b/lib/decoding/openbts/Viterbi.h
new file mode 100644
index 0000000..77bd599
--- /dev/null
+++ b/lib/decoding/openbts/Viterbi.h
@@ -0,0 +1,39 @@
+/*
+ * Copyright 2013, 2014 Range Networks, Inc.
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero 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 Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ *
+ * This use of this software may be subject to additional restrictions.
+ * See the LEGAL file in the main directory for details.
+ */
+
+
+#ifndef _VITERBI_H_
+#define _VITERBI_H_ 1
+
+// (pat) Virtual base class for Viterbi and Turbo coder/decoders.
+class ViterbiBase {
+ public:
+ virtual void encode(const BitVector &in, BitVector& target) const = 0;
+ virtual void decode(const SoftVector &in, BitVector& target) = 0;
+ // (pat) Return error count from most recent decoder run.
+ // If you get -1 from this, the method is not defined in the Viterbi class.
+ virtual int getBEC() { return -1; }
+ //virtual ~ViterbiBase(); Currently None of these have destructors.
+
+ // These functions are logically part of the Viterbi functionality, even though they do not use any class variables.
+ unsigned applyPoly(uint64_t val, uint64_t poly);
+ unsigned applyPoly(uint64_t val, uint64_t poly, unsigned order);
+};
+#endif
diff --git a/lib/decoding/ViterbiR204.cpp b/lib/decoding/openbts/ViterbiR204.cpp
index 999b31b..fe31aad 100644
--- a/lib/decoding/ViterbiR204.cpp
+++ b/lib/decoding/openbts/ViterbiR204.cpp
@@ -1,28 +1,23 @@
/*
-* Copyright 2008, 2009, 2014 Free Software Foundation, Inc.
-* Copyright 2014 Range Networks, Inc.
-*
-*
-* This software is distributed under the terms of the GNU Affero Public License.
-* See the COPYING file in the main directory for details.
-*
-* This use of this software may be subject to additional restrictions.
-* See the LEGAL 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 Affero 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 Affero General Public License for more details.
-
- You should have received a copy of the GNU Affero General Public License
- along with this program. If not, see <http://www.gnu.org/licenses/>.
-
-*/
+ * Copyright 2008, 2009, 2014 Free Software Foundation, Inc.
+ * Copyright 2014 Range Networks, Inc.
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero 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 Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ *
+ * This use of this software may be subject to additional restrictions.
+ * See the LEGAL file in the main directory for details.
+ */
@@ -33,6 +28,7 @@
#include <stdio.h>
#include <sstream>
#include <string.h>
+#include <cstdlib>
using namespace std;
@@ -75,7 +71,7 @@ void ViterbiR2O4::encode(const BitVector& in, BitVector& target) const
assert(sz*coder.iRate() == target.size());
// Build a "history" array where each element contains the full history.
- uint32_t history[sz];
+ uint32_t * history = (uint32_t *) malloc(sizeof(uint32_t)*sz);
uint32_t accum = 0;
for (size_t i=0; i<sz; i++) {
accum = (accum<<1) | in.bit(i);
@@ -90,6 +86,7 @@ void ViterbiR2O4::encode(const BitVector& in, BitVector& target) const
*op++ = coder.stateTable(g,index);
}
}
+ free(history);
}
@@ -229,7 +226,7 @@ void ViterbiR2O4::decode(const SoftVector &in, BitVector& target)
// Build a "history" array where each element contains the full history.
// (pat) We only use every other history element, so why are we setting them?
- uint32_t history[ctsz];
+ uint32_t * history = (uint32_t *)malloc(sizeof(uint32_t)*ctsz);
{
BitVector bits = in.sliced();
uint32_t accum = 0;
@@ -246,8 +243,8 @@ void ViterbiR2O4::decode(const SoftVector &in, BitVector& target)
}
// Precompute metric tables.
- float matchCostTable[ctsz];
- float mismatchCostTable[ctsz];
+ float * matchCostTable = (float *)malloc(sizeof(float)*ctsz);
+ float * mismatchCostTable = (float *)malloc(sizeof(float)*ctsz);
{
const float *dp = in.begin();
for (size_t i=0; i<sz; i++) {
@@ -301,6 +298,9 @@ void ViterbiR2O4::decode(const SoftVector &in, BitVector& target)
// Dont think minCost == NULL can happen.
mBitErrorCnt = minCost ? minCost->bitErrorCnt : 0;
}
+ free(history);
+ free(matchCostTable);
+ free(mismatchCostTable);
}
// vim: ts=4 sw=4
diff --git a/lib/decoding/ViterbiR204.h b/lib/decoding/openbts/ViterbiR204.h
index 4fd053f..090f1e8 100644
--- a/lib/decoding/ViterbiR204.h
+++ b/lib/decoding/openbts/ViterbiR204.h
@@ -1,27 +1,23 @@
/*
-* Copyright 2008, 2009, 2014 Free Software Foundation, Inc.
-* Copyright 2014 Range Networks, Inc.
-*
-* This software is distributed under the terms of the GNU Affero Public License.
-* See the COPYING file in the main directory for details.
-*
-* This use of this software may be subject to additional restrictions.
-* See the LEGAL 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 Affero 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 Affero General Public License for more details.
-
- You should have received a copy of the GNU Affero General Public License
- along with this program. If not, see <http://www.gnu.org/licenses/>.
-
-*/
+ * Copyright 2008, 2009, 2014 Free Software Foundation, Inc.
+ * Copyright 2014 Range Networks, Inc.
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero 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 Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ *
+ * This use of this software may be subject to additional restrictions.
+ * See the LEGAL file in the main directory for details.
+ */
#ifndef _VITERBIR204_H_
#define _VITERBIR204_H_ 1
diff --git a/lib/decoding/osmocom/codec/CMakeLists.txt b/lib/decoding/osmocom/codec/CMakeLists.txt
new file mode 100644
index 0000000..d5c3997
--- /dev/null
+++ b/lib/decoding/osmocom/codec/CMakeLists.txt
@@ -0,0 +1,6 @@
+add_sources(
+gsm610.c
+gsm620.c
+gsm660.c
+gsm690.c
+)
diff --git a/lib/decoding/osmocom/codec/codec.h b/lib/decoding/osmocom/codec/codec.h
new file mode 100644
index 0000000..6a1bf9f
--- /dev/null
+++ b/lib/decoding/osmocom/codec/codec.h
@@ -0,0 +1,81 @@
+/*! \file codec.h */
+
+#pragma once
+
+#include <stdint.h>
+#include <stdbool.h>
+
+#include <osmocom/core/utils.h>
+
+/* TS 101318 Chapter 5.1: 260 bits + 4bit sig */
+#define GSM_FR_BYTES 33
+/* TS 101318 Chapter 5.2: 112 bits, no sig */
+#define GSM_HR_BYTES 14
+/* TS 101318 Chapter 5.3: 244 bits + 4bit sig */
+#define GSM_EFR_BYTES 31
+
+extern const uint16_t gsm610_bitorder[]; /* FR */
+extern const uint16_t gsm620_unvoiced_bitorder[]; /* HR unvoiced */
+extern const uint16_t gsm620_voiced_bitorder[]; /* HR voiced */
+extern const uint16_t gsm660_bitorder[]; /* EFR */
+
+extern const uint16_t gsm690_12_2_bitorder[]; /* AMR 12.2 kbits */
+extern const uint16_t gsm690_10_2_bitorder[]; /* AMR 10.2 kbits */
+extern const uint16_t gsm690_7_95_bitorder[]; /* AMR 7.95 kbits */
+extern const uint16_t gsm690_7_4_bitorder[]; /* AMR 7.4 kbits */
+extern const uint16_t gsm690_6_7_bitorder[]; /* AMR 6.7 kbits */
+extern const uint16_t gsm690_5_9_bitorder[]; /* AMR 5.9 kbits */
+extern const uint16_t gsm690_5_15_bitorder[]; /* AMR 5.15 kbits */
+extern const uint16_t gsm690_4_75_bitorder[]; /* AMR 4.75 kbits */
+
+extern const struct value_string osmo_amr_type_names[];
+
+enum osmo_amr_type {
+ AMR_4_75 = 0,
+ AMR_5_15 = 1,
+ AMR_5_90 = 2,
+ AMR_6_70 = 3,
+ AMR_7_40 = 4,
+ AMR_7_95 = 5,
+ AMR_10_2 = 6,
+ AMR_12_2 = 7,
+ AMR_SID = 8,
+ AMR_GSM_EFR_SID = 9,
+ AMR_TDMA_EFR_SID = 10,
+ AMR_PDC_EFR_SID = 11,
+ AMR_NO_DATA = 15,
+};
+
+enum osmo_amr_quality {
+ AMR_BAD = 0,
+ AMR_GOOD = 1
+};
+
+/*! Check if given AMR Frame Type is a speech frame
+ * \param[in] ft AMR Frame Type
+ * \returns true if AMR with given Frame Type contains voice, false otherwise
+ */
+static inline bool osmo_amr_is_speech(enum osmo_amr_type ft)
+{
+ switch (ft) {
+ case AMR_4_75:
+ case AMR_5_15:
+ case AMR_5_90:
+ case AMR_6_70:
+ case AMR_7_40:
+ case AMR_7_95:
+ case AMR_10_2:
+ case AMR_12_2:
+ return true;
+ default:
+ return false;
+ }
+}
+
+bool osmo_fr_check_sid(const uint8_t *rtp_payload, size_t payload_len);
+bool osmo_hr_check_sid(const uint8_t *rtp_payload, size_t payload_len);
+int osmo_amr_rtp_enc(uint8_t *payload, uint8_t cmr, enum osmo_amr_type ft,
+ enum osmo_amr_quality bfi);
+int osmo_amr_rtp_dec(const uint8_t *payload, int payload_len, uint8_t *cmr,
+ int8_t *cmi, enum osmo_amr_type *ft,
+ enum osmo_amr_quality *bfi, int8_t *sti);
diff --git a/lib/decoding/osmocom/codec/gsm610.c b/lib/decoding/osmocom/codec/gsm610.c
new file mode 100644
index 0000000..a05eaba
--- /dev/null
+++ b/lib/decoding/osmocom/codec/gsm610.c
@@ -0,0 +1,336 @@
+/*! \file gsm610.c
+ * GSM 06.10 - GSM FR codec. */
+/*
+ * (C) 2010 Sylvain Munaut <tnt@246tNt.com>
+ *
+ * All Rights Reserved
+ *
+ * SPDX-License-Identifier: GPL-2.0+
+ *
+ * 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 2 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, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ */
+
+#include <stdint.h>
+#include <stdbool.h>
+
+#include <osmocom/core/bitvec.h>
+#include <osmocom/core/utils.h>
+#include <osmocom/codec/codec.h>
+
+/* GSM FR - subjective importance bit ordering */
+ /* This array encodes GSM 05.03 Table 2.
+ * It's also GSM 06.10 Table A.2.1a
+ *
+ * It converts between serial parameter output by the encoder and the
+ * order needed before channel encoding.
+ */
+const uint16_t gsm610_bitorder[260] = {
+ 0, /* LARc0:5 */
+ 47, /* Xmaxc0:5 */
+ 103, /* Xmaxc1:5 */
+ 159, /* Xmaxc2:5 */
+ 215, /* Xmaxc3:5 */
+ 1, /* LARc0:4 */
+ 6, /* LARc1:5 */
+ 12, /* LARc2:4 */
+ 2, /* LARc0:3 */
+ 7, /* LARc1:4 */
+ 13, /* LARc2:3 */
+ 17, /* LARc3:4 */
+ 36, /* Nc0:6 */
+ 92, /* Nc1:6 */
+ 148, /* Nc2:6 */
+ 204, /* Nc3:6 */
+ 48, /* Xmaxc0:4 */
+ 104, /* Xmaxc1:4 */
+ 160, /* Xmaxc2:4 */
+ 216, /* Xmaxc3:4 */
+ 8, /* LARc1:3 */
+ 22, /* LARc4:3 */
+ 26, /* LARc5:3 */
+ 37, /* Nc0:5 */
+ 93, /* Nc1:5 */
+ 149, /* Nc2:5 */
+ 205, /* Nc3:5 */
+ 38, /* Nc0:4 */
+ 94, /* Nc1:4 */
+ 150, /* Nc2:4 */
+ 206, /* Nc3:4 */
+ 39, /* Nc0:3 */
+ 95, /* Nc1:3 */
+ 151, /* Nc2:3 */
+ 207, /* Nc3:3 */
+ 40, /* Nc0:2 */
+ 96, /* Nc1:2 */
+ 152, /* Nc2:2 */
+ 208, /* Nc3:2 */
+ 49, /* Xmaxc0:3 */
+ 105, /* Xmaxc1:3 */
+ 161, /* Xmaxc2:3 */
+ 217, /* Xmaxc3:3 */
+ 3, /* LARc0:2 */
+ 18, /* LARc3:3 */
+ 30, /* LARc6:2 */
+ 41, /* Nc0:1 */
+ 97, /* Nc1:1 */
+ 153, /* Nc2:1 */
+ 209, /* Nc3:1 */
+ 23, /* LARc4:2 */
+ 27, /* LARc5:2 */
+ 43, /* bc0:1 */
+ 99, /* bc1:1 */
+ 155, /* bc2:1 */
+ 211, /* bc3:1 */
+ 42, /* Nc0:0 */
+ 98, /* Nc1:0 */
+ 154, /* Nc2:0 */
+ 210, /* Nc3:0 */
+ 45, /* Mc0:1 */
+ 101, /* Mc1:1 */
+ 157, /* Mc2:1 */
+ 213, /* Mc3:1 */
+ 4, /* LARc0:1 */
+ 9, /* LARc1:2 */
+ 14, /* LARc2:2 */
+ 33, /* LARc7:2 */
+ 19, /* LARc3:2 */
+ 24, /* LARc4:1 */
+ 31, /* LARc6:1 */
+ 44, /* bc0:0 */
+ 100, /* bc1:0 */
+ 156, /* bc2:0 */
+ 212, /* bc3:0 */
+ 50, /* Xmaxc0:2 */
+ 106, /* Xmaxc1:2 */
+ 162, /* Xmaxc2:2 */
+ 218, /* Xmaxc3:2 */
+ 53, /* xmc0_0:2 */
+ 56, /* xmc0_1:2 */
+ 59, /* xmc0_2:2 */
+ 62, /* xmc0_3:2 */
+ 65, /* xmc0_4:2 */
+ 68, /* xmc0_5:2 */
+ 71, /* xmc0_6:2 */
+ 74, /* xmc0_7:2 */
+ 77, /* xmc0_8:2 */
+ 80, /* xmc0_9:2 */
+ 83, /* xmc0_10:2 */
+ 86, /* xmc0_11:2 */
+ 89, /* xmc0_12:2 */
+ 109, /* xmc1_0:2 */
+ 112, /* xmc1_1:2 */
+ 115, /* xmc1_2:2 */
+ 118, /* xmc1_3:2 */
+ 121, /* xmc1_4:2 */
+ 124, /* xmc1_5:2 */
+ 127, /* xmc1_6:2 */
+ 130, /* xmc1_7:2 */
+ 133, /* xmc1_8:2 */
+ 136, /* xmc1_9:2 */
+ 139, /* xmc1_10:2 */
+ 142, /* xmc1_11:2 */
+ 145, /* xmc1_12:2 */
+ 165, /* xmc2_0:2 */
+ 168, /* xmc2_1:2 */
+ 171, /* xmc2_2:2 */
+ 174, /* xmc2_3:2 */
+ 177, /* xmc2_4:2 */
+ 180, /* xmc2_5:2 */
+ 183, /* xmc2_6:2 */
+ 186, /* xmc2_7:2 */
+ 189, /* xmc2_8:2 */
+ 192, /* xmc2_9:2 */
+ 195, /* xmc2_10:2 */
+ 198, /* xmc2_11:2 */
+ 201, /* xmc2_12:2 */
+ 221, /* xmc3_0:2 */
+ 224, /* xmc3_1:2 */
+ 227, /* xmc3_2:2 */
+ 230, /* xmc3_3:2 */
+ 233, /* xmc3_4:2 */
+ 236, /* xmc3_5:2 */
+ 239, /* xmc3_6:2 */
+ 242, /* xmc3_7:2 */
+ 245, /* xmc3_8:2 */
+ 248, /* xmc3_9:2 */
+ 251, /* xmc3_10:2 */
+ 254, /* xmc3_11:2 */
+ 257, /* xmc3_12:2 */
+ 46, /* Mc0:0 */
+ 102, /* Mc1:0 */
+ 158, /* Mc2:0 */
+ 214, /* Mc3:0 */
+ 51, /* Xmaxc0:1 */
+ 107, /* Xmaxc1:1 */
+ 163, /* Xmaxc2:1 */
+ 219, /* Xmaxc3:1 */
+ 54, /* xmc0_0:1 */
+ 57, /* xmc0_1:1 */
+ 60, /* xmc0_2:1 */
+ 63, /* xmc0_3:1 */
+ 66, /* xmc0_4:1 */
+ 69, /* xmc0_5:1 */
+ 72, /* xmc0_6:1 */
+ 75, /* xmc0_7:1 */
+ 78, /* xmc0_8:1 */
+ 81, /* xmc0_9:1 */
+ 84, /* xmc0_10:1 */
+ 87, /* xmc0_11:1 */
+ 90, /* xmc0_12:1 */
+ 110, /* xmc1_0:1 */
+ 113, /* xmc1_1:1 */
+ 116, /* xmc1_2:1 */
+ 119, /* xmc1_3:1 */
+ 122, /* xmc1_4:1 */
+ 125, /* xmc1_5:1 */
+ 128, /* xmc1_6:1 */
+ 131, /* xmc1_7:1 */
+ 134, /* xmc1_8:1 */
+ 137, /* xmc1_9:1 */
+ 140, /* xmc1_10:1 */
+ 143, /* xmc1_11:1 */
+ 146, /* xmc1_12:1 */
+ 166, /* xmc2_0:1 */
+ 169, /* xmc2_1:1 */
+ 172, /* xmc2_2:1 */
+ 175, /* xmc2_3:1 */
+ 178, /* xmc2_4:1 */
+ 181, /* xmc2_5:1 */
+ 184, /* xmc2_6:1 */
+ 187, /* xmc2_7:1 */
+ 190, /* xmc2_8:1 */
+ 193, /* xmc2_9:1 */
+ 196, /* xmc2_10:1 */
+ 199, /* xmc2_11:1 */
+ 202, /* xmc2_12:1 */
+ 222, /* xmc3_0:1 */
+ 225, /* xmc3_1:1 */
+ 228, /* xmc3_2:1 */
+ 231, /* xmc3_3:1 */
+ 234, /* xmc3_4:1 */
+ 237, /* xmc3_5:1 */
+ 240, /* xmc3_6:1 */
+ 243, /* xmc3_7:1 */
+ 246, /* xmc3_8:1 */
+ 249, /* xmc3_9:1 */
+ 252, /* xmc3_10:1 */
+ 255, /* xmc3_11:1 */
+ 258, /* xmc3_12:1 */
+ 5, /* LARc0:0 */
+ 10, /* LARc1:1 */
+ 15, /* LARc2:1 */
+ 28, /* LARc5:1 */
+ 32, /* LARc6:0 */
+ 34, /* LARc7:1 */
+ 35, /* LARc7:0 */
+ 16, /* LARc2:0 */
+ 20, /* LARc3:1 */
+ 21, /* LARc3:0 */
+ 25, /* LARc4:0 */
+ 52, /* Xmaxc0:0 */
+ 108, /* Xmaxc1:0 */
+ 164, /* Xmaxc2:0 */
+ 220, /* Xmaxc3:0 */
+ 55, /* xmc0_0:0 */
+ 58, /* xmc0_1:0 */
+ 61, /* xmc0_2:0 */
+ 64, /* xmc0_3:0 */
+ 67, /* xmc0_4:0 */
+ 70, /* xmc0_5:0 */
+ 73, /* xmc0_6:0 */
+ 76, /* xmc0_7:0 */
+ 79, /* xmc0_8:0 */
+ 82, /* xmc0_9:0 */
+ 85, /* xmc0_10:0 */
+ 88, /* xmc0_11:0 */
+ 91, /* xmc0_12:0 */
+ 111, /* xmc1_0:0 */
+ 114, /* xmc1_1:0 */
+ 117, /* xmc1_2:0 */
+ 120, /* xmc1_3:0 */
+ 123, /* xmc1_4:0 */
+ 126, /* xmc1_5:0 */
+ 129, /* xmc1_6:0 */
+ 132, /* xmc1_7:0 */
+ 135, /* xmc1_8:0 */
+ 138, /* xmc1_9:0 */
+ 141, /* xmc1_10:0 */
+ 144, /* xmc1_11:0 */
+ 147, /* xmc1_12:0 */
+ 167, /* xmc2_0:0 */
+ 170, /* xmc2_1:0 */
+ 173, /* xmc2_2:0 */
+ 176, /* xmc2_3:0 */
+ 179, /* xmc2_4:0 */
+ 182, /* xmc2_5:0 */
+ 185, /* xmc2_6:0 */
+ 188, /* xmc2_7:0 */
+ 191, /* xmc2_8:0 */
+ 194, /* xmc2_9:0 */
+ 197, /* xmc2_10:0 */
+ 200, /* xmc2_11:0 */
+ 203, /* xmc2_12:0 */
+ 223, /* xmc3_0:0 */
+ 226, /* xmc3_1:0 */
+ 229, /* xmc3_2:0 */
+ 232, /* xmc3_3:0 */
+ 235, /* xmc3_4:0 */
+ 238, /* xmc3_5:0 */
+ 241, /* xmc3_6:0 */
+ 244, /* xmc3_7:0 */
+ 247, /* xmc3_8:0 */
+ 250, /* xmc3_9:0 */
+ 253, /* xmc3_10:0 */
+ 256, /* xmc3_11:0 */
+ 259, /* xmc3_12:0 */
+ 11, /* LARc1:0 */
+ 29, /* LARc5:0 */
+};
+
+/*! Check whether RTP frame contains FR SID code word according to
+ * TS 101 318 §5.1.2
+ * \param[in] rtp_payload Buffer with RTP payload
+ * \param[in] payload_len Length of payload
+ * \returns true if code word is found, false otherwise
+ */
+bool osmo_fr_check_sid(const uint8_t *rtp_payload, size_t payload_len)
+{
+ struct bitvec bv;
+ uint16_t i, z_bits[] = { 57, 58, 60, 61, 63, 64, 66, 67, 69, 70, 72, 73,
+ 75, 76, 78, 79, 81, 82, 84, 85, 87, 88, 90, 91,
+ 93, 94, 113, 114, 116, 117, 119, 120, 122, 123,
+ 125, 126, 128, 129, 131, 132, 134, 135, 137,
+ 138, 140, 141, 143, 144, 146, 147, 149, 150,
+ 169, 170, 172, 173, 175, 176, 178, 179, 181,
+ 182, 184, 185, 187, 188, 190, 191, 193, 194,
+ 196, 197, 199, 200, 202, 203, 205, 206, 225,
+ 226, 228, 229, 231, 232, 234, 235, 237, 240,
+ 243, 246, 249, 252, 255, 258, 261 };
+
+ /* signature does not match Full Rate SID */
+ if ((rtp_payload[0] >> 4) != 0xD)
+ return false;
+
+ bv.data = (uint8_t *) rtp_payload;
+ bv.data_len = payload_len;
+
+ /* code word is all 0 at given bits, numbered from 1 */
+ for (i = 0; i < ARRAY_SIZE(z_bits); i++)
+ if (bitvec_get_bit_pos(&bv, z_bits[i]) != ZERO)
+ return false;
+
+ return true;
+}
diff --git a/lib/decoding/osmocom/codec/gsm610_bits.h b/lib/decoding/osmocom/codec/gsm610_bits.h
new file mode 100644
index 0000000..cef4cf4
--- /dev/null
+++ b/lib/decoding/osmocom/codec/gsm610_bits.h
@@ -0,0 +1,272 @@
+/*! \file gsm610_bits.h */
+
+#pragma once
+
+/* This enumeration describs a GSM-FR (GSM 06.10) frame in ints RTP bit order
+ * representation. See also RFC 3551 Table 3: GSM payload format */
+enum gsm610_rtp_bit_offsets {
+ GSM610_RTP_SIGNATURE_0,
+ GSM610_RTP_SIGNATURE_1,
+ GSM610_RTP_SIGNATURE_2,
+ GSM610_RTP_SIGNATURE_3,
+ GSM610_RTP_LARC0_0,
+ GSM610_RTP_LARC0_1,
+ GSM610_RTP_LARC0_2,
+ GSM610_RTP_LARC0_3,
+ GSM610_RTP_LARC0_4,
+ GSM610_RTP_LARC0_5,
+ GSM610_RTP_LARC1_0,
+ GSM610_RTP_LARC1_1,
+ GSM610_RTP_LARC1_2,
+ GSM610_RTP_LARC1_3,
+ GSM610_RTP_LARC1_4,
+ GSM610_RTP_LARC1_5,
+ GSM610_RTP_LARC2_0,
+ GSM610_RTP_LARC2_1,
+ GSM610_RTP_LARC2_2,
+ GSM610_RTP_LARC2_3,
+ GSM610_RTP_LARC2_4,
+ GSM610_RTP_LARC3_0,
+ GSM610_RTP_LARC3_1,
+ GSM610_RTP_LARC3_2,
+ GSM610_RTP_LARC3_3,
+ GSM610_RTP_LARC3_4,
+ GSM610_RTP_LARC4_0,
+ GSM610_RTP_LARC4_1,
+ GSM610_RTP_LARC4_2,
+ GSM610_RTP_LARC4_3,
+ GSM610_RTP_LARC5_0,
+ GSM610_RTP_LARC5_1,
+ GSM610_RTP_LARC5_2,
+ GSM610_RTP_LARC5_3,
+ GSM610_RTP_LARC6_0,
+ GSM610_RTP_LARC6_1,
+ GSM610_RTP_LARC6_2,
+ GSM610_RTP_LARC7_0,
+ GSM610_RTP_LARC7_1,
+ GSM610_RTP_LARC7_2,
+ GSM610_RTP_NC0_0,
+ GSM610_RTP_NC0_1,
+ GSM610_RTP_NC0_2,
+ GSM610_RTP_NC0_3,
+ GSM610_RTP_NC0_4,
+ GSM610_RTP_NC0_5,
+ GSM610_RTP_NC0_6,
+ GSM610_RTP_BC0_0,
+ GSM610_RTP_BC0_1,
+ GSM610_RTP_MC0_0,
+ GSM610_RTP_MC0_1,
+ GSM610_RTP_XMAXC00,
+ GSM610_RTP_XMAXC01,
+ GSM610_RTP_XMAXC02,
+ GSM610_RTP_XMAXC03,
+ GSM610_RTP_XMAXC04,
+ GSM610_RTP_XMAXC05,
+ GSM610_RTP_XMC0_0,
+ GSM610_RTP_XMC0_1,
+ GSM610_RTP_XMC0_2,
+ GSM610_RTP_XMC1_0,
+ GSM610_RTP_XMC1_1,
+ GSM610_RTP_XMC1_2,
+ GSM610_RTP_XMC2_0,
+ GSM610_RTP_XMC2_1,
+ GSM610_RTP_XMC2_2,
+ GSM610_RTP_XMC3_0,
+ GSM610_RTP_XMC3_1,
+ GSM610_RTP_XMC3_2,
+ GSM610_RTP_XMC4_0,
+ GSM610_RTP_XMC4_1,
+ GSM610_RTP_XMC4_2,
+ GSM610_RTP_XMC5_0,
+ GSM610_RTP_XMC5_1,
+ GSM610_RTP_XMC5_2,
+ GSM610_RTP_XMC6_0,
+ GSM610_RTP_XMC6_1,
+ GSM610_RTP_XMC6_2,
+ GSM610_RTP_XMC7_0,
+ GSM610_RTP_XMC7_1,
+ GSM610_RTP_XMC7_2,
+ GSM610_RTP_XMC8_0,
+ GSM610_RTP_XMC8_1,
+ GSM610_RTP_XMC8_2,
+ GSM610_RTP_XMC9_0,
+ GSM610_RTP_XMC9_1,
+ GSM610_RTP_XMC9_2,
+ GSM610_RTP_XMC10_0,
+ GSM610_RTP_XMC10_1,
+ GSM610_RTP_XMC10_2,
+ GSM610_RTP_XMC11_0,
+ GSM610_RTP_XMC11_1,
+ GSM610_RTP_XMC11_2,
+ GSM610_RTP_XMC12_0,
+ GSM610_RTP_XMC12_1,
+ GSM610_RTP_XCM12_2,
+ GSM610_RTP_NC1_0,
+ GSM610_RTP_NC1_1,
+ GSM610_RTP_NC1_2,
+ GSM610_RTP_NC1_3,
+ GSM610_RTP_NC1_4,
+ GSM610_RTP_NC1_5,
+ GSM610_RTP_NC1_6,
+ GSM610_RTP_BC1_0,
+ GSM610_RTP_BC1_1,
+ GSM610_RTP_MC1_0,
+ GSM610_RTP_MC1_1,
+ GSM610_RTP_XMAXC10,
+ GSM610_RTP_XMAXC11,
+ GSM610_RTP_XMAXC12,
+ GSM610_RTP_XMAXC13,
+ GSM610_RTP_XMAXC14,
+ GSM610_RTP_XMAX15,
+ GSM610_RTP_XMC13_0,
+ GSM610_RTP_XMC13_1,
+ GSM610_RTP_XMC13_2,
+ GSM610_RTP_XMC14_0,
+ GSM610_RTP_XMC14_1,
+ GSM610_RTP_XMC14_2,
+ GSM610_RTP_XMC15_0,
+ GSM610_RTP_XMC15_1,
+ GSM610_RTP_XMC15_2,
+ GSM610_RTP_XMC16_0,
+ GSM610_RTP_XMC16_1,
+ GSM610_RTP_XMC16_2,
+ GSM610_RTP_XMC17_0,
+ GSM610_RTP_XMC17_1,
+ GSM610_RTP_XMC17_2,
+ GSM610_RTP_XMC18_0,
+ GSM610_RTP_XMC18_1,
+ GSM610_RTP_XMC18_2,
+ GSM610_RTP_XMC19_0,
+ GSM610_RTP_XMC19_1,
+ GSM610_RTP_XMC19_2,
+ GSM610_RTP_XMC20_0,
+ GSM610_RTP_XMC20_1,
+ GSM610_RTP_XMC20_2,
+ GSM610_RTP_XMC21_0,
+ GSM610_RTP_XMC21_1,
+ GSM610_RTP_XMC21_2,
+ GSM610_RTP_XMC22_0,
+ GSM610_RTP_XMC22_1,
+ GSM610_RTP_XMC22_2,
+ GSM610_RTP_XMC23_0,
+ GSM610_RTP_XMC23_1,
+ GSM610_RTP_XMC23_2,
+ GSM610_RTP_XMC24_0,
+ GSM610_RTP_XMC24_1,
+ GSM610_RTP_XMC24_2,
+ GSM610_RTP_XMC25_0,
+ GSM610_RTP_XMC25_1,
+ GSM610_RTP_XMC25_2,
+ GSM610_RTP_NC2_0,
+ GSM610_RTP_NC2_1,
+ GSM610_RTP_NC2_2,
+ GSM610_RTP_NC2_3,
+ GSM610_RTP_NC2_4,
+ GSM610_RTP_NC2_5,
+ GSM610_RTP_NC2_6,
+ GSM610_RTP_BC2_0,
+ GSM610_RTP_BC2_1,
+ GSM610_RTP_MC2_0,
+ GSM610_RTP_MC2_1,
+ GSM610_RTP_XMAXC20,
+ GSM610_RTP_XMAXC21,
+ GSM610_RTP_XMAXC22,
+ GSM610_RTP_XMAXC23,
+ GSM610_RTP_XMAXC24,
+ GSM610_RTP_XMAXC25,
+ GSM610_RTP_XMC26_0,
+ GSM610_RTP_XMC26_1,
+ GSM610_RTP_XMC26_2,
+ GSM610_RTP_XMC27_0,
+ GSM610_RTP_XMC27_1,
+ GSM610_RTP_XMC27_2,
+ GSM610_RTP_XMC28_0,
+ GSM610_RTP_XMC28_1,
+ GSM610_RTP_XMC28_2,
+ GSM610_RTP_XMC29_0,
+ GSM610_RTP_XMC29_1,
+ GSM610_RTP_XMC29_2,
+ GSM610_RTP_XMC30_0,
+ GSM610_RTP_XMC30_1,
+ GSM610_RTP_XMC30_2,
+ GSM610_RTP_XMC31_0,
+ GSM610_RTP_XMC31_1,
+ GSM610_RTP_XMC31_2,
+ GSM610_RTP_XMC32_0,
+ GSM610_RTP_XMC32_1,
+ GSM610_RTP_XMC32_2,
+ GSM610_RTP_XMC33_0,
+ GSM610_RTP_XMC33_1,
+ GSM610_RTP_XMC33_2,
+ GSM610_RTP_XMC34_0,
+ GSM610_RTP_XMC34_1,
+ GSM610_RTP_XMC34_2,
+ GSM610_RTP_XMC35_0,
+ GSM610_RTP_XMC35_1,
+ GSM610_RTP_XMC35_2,
+ GSM610_RTP_XMC36_0,
+ GSM610_RTP_XMC36_1,
+ GSM610_RTP_XMC36_2,
+ GSM610_RTP_XMC37_0,
+ GSM610_RTP_XMC37_1,
+ GSM610_RTP_XMC37_2,
+ GSM610_RTP_XMC38_0,
+ GSM610_RTP_XMC38_1,
+ GSM610_RTP_XMC38_2,
+ GSM610_RTP_NC3_0,
+ GSM610_RTP_NC3_1,
+ GSM610_RTP_NC3_2,
+ GSM610_RTP_NC3_3,
+ GSM610_RTP_NC3_4,
+ GSM610_RTP_NC3_5,
+ GSM610_RTP_NC3_6,
+ GSM610_RTP_BC3_0,
+ GSM610_RTP_BC3_1,
+ GSM610_RTP_MC3_0,
+ GSM610_RTP_MC3_1,
+ GSM610_RTP_XMAXC30,
+ GSM610_RTP_XMAXC31,
+ GSM610_RTP_XMAXC32,
+ GSM610_RTP_XMAXC33,
+ GSM610_RTP_XMAXC34,
+ GSM610_RTP_XMAXC35,
+ GSM610_RTP_XMC39_0,
+ GSM610_RTP_XMC39_1,
+ GSM610_RTP_XMC39_2,
+ GSM610_RTP_XMC40_0,
+ GSM610_RTP_XMC40_1,
+ GSM610_RTP_XMC40_2,
+ GSM610_RTP_XMC41_0,
+ GSM610_RTP_XMC41_1,
+ GSM610_RTP_XMC41_2,
+ GSM610_RTP_XMC42_0,
+ GSM610_RTP_XMC42_1,
+ GSM610_RTP_XMC42_2,
+ GSM610_RTP_XMC43_0,
+ GSM610_RTP_XMC43_1,
+ GSM610_RTP_XMC43_2,
+ GSM610_RTP_XMC44_0,
+ GSM610_RTP_XMC44_1,
+ GSM610_RTP_XMC44_2,
+ GSM610_RTP_XMC45_0,
+ GSM610_RTP_XMC45_1,
+ GSM610_RTP_XMC45_2,
+ GSM610_RTP_XMC46_0,
+ GSM610_RTP_XMC46_1,
+ GSM610_RTP_XMC46_2,
+ GSM610_RTP_XMC47_0,
+ GSM610_RTP_XMC47_1,
+ GSM610_RTP_XMC47_2,
+ GSM610_RTP_XMC48_0,
+ GSM610_RTP_XMC48_1,
+ GSM610_RTP_XMC48_2,
+ GSM610_RTP_XMC49_0,
+ GSM610_RTP_XMC49_1,
+ GSM610_RTP_XMC49_2,
+ GSM610_RTP_XMC50_0,
+ GSM610_RTP_XMC50_1,
+ GSM610_RTP_XMC50_2,
+ GSM610_RTP_XMC51_0,
+ GSM610_RTP_XMC51_1,
+ GSM610_RTP_XMC51_2
+};
diff --git a/lib/decoding/osmocom/codec/gsm620.c b/lib/decoding/osmocom/codec/gsm620.c
new file mode 100644
index 0000000..282781f
--- /dev/null
+++ b/lib/decoding/osmocom/codec/gsm620.c
@@ -0,0 +1,297 @@
+/*! \file gsm620.c
+ * GSM 06.20 - GSM HR codec. */
+/*
+ * (C) 2010 Sylvain Munaut <tnt@246tNt.com>
+ *
+ * All Rights Reserved
+ *
+ * SPDX-License-Identifier: GPL-2.0+
+ *
+ * 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 2 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, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ */
+
+#include <stdint.h>
+#include <stdbool.h>
+
+#include <osmocom/core/bitvec.h>
+#include <osmocom/core/utils.h>
+#include <osmocom/codec/codec.h>
+
+/* GSM HR unvoiced (mode=0) frames - subjective importance bit ordering */
+ /* This array encode mapping between GSM 05.03 Table 3a (bits
+ * ordering before channel coding on TCH) and GSM 06.20 Table B.1
+ * (bit ordering on A-bis */
+const uint16_t gsm620_unvoiced_bitorder[112] = {
+ 3, /* R0:1 */
+ 25, /* LPC 3:7 */
+ 52, /* GSP 0-1:2 */
+ 71, /* GSP 0-2:2 */
+ 90, /* GSP 0-3:2 */
+ 109, /* GSP 0-4:2 */
+ 15, /* LPC 1:0 */
+ 19, /* LPC 2:5 */
+ 20, /* LPC 2:4 */
+ 21, /* LPC 2:3 */
+ 22, /* LPC 2:2 */
+ 23, /* LPC 2:1 */
+ 26, /* LPC 3:6 */
+ 27, /* LPC 3:5 */
+ 28, /* LPC 3:4 */
+ 29, /* LPC 3:3 */
+ 30, /* LPC 3:2 */
+ 31, /* LPC 3:1 */
+ 61, /* Code 1-2:0 */
+ 62, /* Code 2-2:6 */
+ 63, /* Code 2-2:5 */
+ 64, /* Code 2-2:4 */
+ 65, /* Code 2-2:3 */
+ 66, /* Code 2-2:2 */
+ 67, /* Code 2-2:1 */
+ 68, /* Code 2-2:0 */
+ 74, /* Code 1-3:6 */
+ 75, /* Code 1-3:5 */
+ 76, /* Code 1-3:4 */
+ 77, /* Code 1-3:3 */
+ 78, /* Code 1-3:2 */
+ 79, /* Code 1-3:1 */
+ 80, /* Code 1-3:0 */
+ 81, /* Code 2-3:6 */
+ 82, /* Code 2-3:5 */
+ 83, /* Code 2-3:4 */
+ 84, /* Code 2-3:3 */
+ 32, /* LPC 3:0 */
+ 4, /* R0:0 */
+ 33, /* INT-LPC:0 */
+ 60, /* Code 1-2:1 */
+ 59, /* Code 1-2:2 */
+ 58, /* Code 1-2:3 */
+ 57, /* Code 1-2:4 */
+ 56, /* Code 1-2:5 */
+ 55, /* Code 1-2:6 */
+ 49, /* Code 2-1:0 */
+ 48, /* Code 2-1:1 */
+ 47, /* Code 2-1:2 */
+ 46, /* Code 2-1:3 */
+ 45, /* Code 2-1:4 */
+ 44, /* Code 2-1:5 */
+ 43, /* Code 2-1:6 */
+ 42, /* Code 1-1:0 */
+ 41, /* Code 1-1:1 */
+ 40, /* Code 1-1:2 */
+ 39, /* Code 1-1:3 */
+ 38, /* Code 1-1:4 */
+ 37, /* Code 1-1:5 */
+ 36, /* Code 1-1:6 */
+ 111, /* GSP 0-4:0 */
+ 92, /* GSP 0-3:0 */
+ 73, /* GSP 0-2:0 */
+ 54, /* GSP 0-1:0 */
+ 24, /* LPC 2:0 */
+ 110, /* GSP 0-4:1 */
+ 91, /* GSP 0-3:1 */
+ 72, /* GSP 0-2:1 */
+ 53, /* GSP 0-1:1 */
+ 14, /* LPC 1:1 */
+ 13, /* LPC 1:2 */
+ 12, /* LPC 1:3 */
+ 11, /* LPC 1:4 */
+ 10, /* LPC 1:5 */
+ 108, /* GSP 0-4:3 */
+ 89, /* GSP 0-3:3 */
+ 70, /* GSP 0-2:3 */
+ 51, /* GSP 0-1:3 */
+ 16, /* LPC 2:8 */
+ 17, /* LPC 2:7 */
+ 18, /* LPC 2:6 */
+ 107, /* GSP 0-4:4 */
+ 88, /* GSP 0-3:4 */
+ 69, /* GSP 0-2:4 */
+ 50, /* GSP 0-1:4 */
+ 9, /* LPC 1:6 */
+ 8, /* LPC 1:7 */
+ 7, /* LPC 1:8 */
+ 6, /* LPC 1:9 */
+ 2, /* R0:2 */
+ 5, /* LPC 1:10 */
+ 1, /* R0:3 */
+ 0, /* R0:4 */
+ 35, /* Mode:0 */
+ 34, /* Mode:1 */
+ 106, /* Code 2-4:0 */
+ 105, /* Code 2-4:1 */
+ 104, /* Code 2-4:2 */
+ 103, /* Code 2-4:3 */
+ 102, /* Code 2-4:4 */
+ 101, /* Code 2-4:5 */
+ 100, /* Code 2-4:6 */
+ 99, /* Code 1-4:0 */
+ 98, /* Code 1-4:1 */
+ 97, /* Code 1-4:2 */
+ 96, /* Code 1-4:3 */
+ 95, /* Code 1-4:4 */
+ 94, /* Code 1-4:5 */
+ 93, /* Code 1-4:6 */
+ 87, /* Code 2-3:0 */
+ 86, /* Code 2-3:1 */
+ 85, /* Code 2-3:2 */
+};
+
+/* GSM HR voiced (mode=1,2,3) frames - subjective importance bit ordering */
+ /* This array encode mapping between GSM 05.03 Table 3b (bits
+ * ordering before channel coding on TCH) and GSM 06.20 Table B.2
+ * (bit ordering on A-bis */
+const uint16_t gsm620_voiced_bitorder[112] = {
+ 13, /* LPC 1:2 */
+ 14, /* LPC 1:1 */
+ 18, /* LPC 2:6 */
+ 19, /* LPC 2:5 */
+ 20, /* LPC 2:4 */
+ 53, /* GSP 0-1:4 */
+ 71, /* GSP 0-2:4 */
+ 89, /* GSP 0-3:4 */
+ 107, /* GSP 0-4:4 */
+ 54, /* GSP 0-1:3 */
+ 72, /* GSP 0-2:3 */
+ 90, /* GSP 0-3:3 */
+ 108, /* GSP 0-4:3 */
+ 55, /* GSP 0-1:2 */
+ 73, /* GSP 0-2:2 */
+ 91, /* GSP 0-3:2 */
+ 109, /* GSP 0-4:2 */
+ 44, /* Code 1:8 */
+ 45, /* Code 1:7 */
+ 46, /* Code 1:6 */
+ 47, /* Code 1:5 */
+ 48, /* Code 1:4 */
+ 49, /* Code 1:3 */
+ 50, /* Code 1:2 */
+ 51, /* Code 1:1 */
+ 52, /* Code 1:0 */
+ 62, /* Code 2:8 */
+ 63, /* Code 2:7 */
+ 64, /* Code 2:6 */
+ 65, /* Code 2:5 */
+ 68, /* Code 2:2 */
+ 69, /* Code 2:1 */
+ 70, /* Code 2:0 */
+ 80, /* Code 3:8 */
+ 66, /* Code 2:4 */
+ 67, /* Code 2:3 */
+ 56, /* GSP 0-1:1 */
+ 74, /* GSP 0-2:1 */
+ 92, /* GSP 0-3:1 */
+ 110, /* GSP 0-4:1 */
+ 57, /* GSP 0-1:0 */
+ 75, /* GSP 0-2:0 */
+ 93, /* GSP 0-3:0 */
+ 111, /* GSP 0-4:0 */
+ 33, /* INT-LPC:0 */
+ 24, /* LPC 2:0 */
+ 32, /* LPC 3:0 */
+ 97, /* LAG 4:0 */
+ 31, /* LPC 3:1 */
+ 23, /* LPC 2:1 */
+ 96, /* LAG 4:1 */
+ 79, /* LAG 3:0 */
+ 61, /* LAG 2:0 */
+ 43, /* LAG 1:0 */
+ 95, /* LAG 4:2 */
+ 78, /* LAG 3:1 */
+ 60, /* LAG 2:1 */
+ 42, /* LAG 1:1 */
+ 30, /* LPC 3:2 */
+ 29, /* LPC 3:3 */
+ 28, /* LPC 3:4 */
+ 22, /* LPC 2:2 */
+ 27, /* LPC 3:5 */
+ 26, /* LPC 3:6 */
+ 21, /* LPC 2:3 */
+ 4, /* R0:0 */
+ 25, /* LPC 3:7 */
+ 15, /* LPC 1:0 */
+ 94, /* LAG 4:3 */
+ 77, /* LAG 3:2 */
+ 59, /* LAG 2:2 */
+ 41, /* LAG 1:2 */
+ 3, /* R0:1 */
+ 76, /* LAG 3:3 */
+ 58, /* LAG 2:3 */
+ 40, /* LAG 1:3 */
+ 39, /* LAG 1:4 */
+ 17, /* LPC 2:7 */
+ 16, /* LPC 2:8 */
+ 12, /* LPC 1:3 */
+ 11, /* LPC 1:4 */
+ 10, /* LPC 1:5 */
+ 9, /* LPC 1:6 */
+ 2, /* R0:2 */
+ 38, /* LAG 1:5 */
+ 37, /* LAG 1:6 */
+ 36, /* LAG 1:7 */
+ 8, /* LPC 1:7 */
+ 7, /* LPC 1:8 */
+ 6, /* LPC 1:9 */
+ 5, /* LPC 1:10 */
+ 1, /* R0:3 */
+ 0, /* R0:4 */
+ 35, /* Mode:0 */
+ 34, /* Mode:1 */
+ 106, /* Code 4:0 */
+ 105, /* Code 4:1 */
+ 104, /* Code 4:2 */
+ 103, /* Code 4:3 */
+ 102, /* Code 4:4 */
+ 101, /* Code 4:5 */
+ 100, /* Code 4:6 */
+ 99, /* Code 4:7 */
+ 98, /* Code 4:8 */
+ 88, /* Code 3:0 */
+ 87, /* Code 3:1 */
+ 86, /* Code 3:2 */
+ 85, /* Code 3:3 */
+ 84, /* Code 3:4 */
+ 83, /* Code 3:5 */
+ 82, /* Code 3:6 */
+ 81, /* Code 3:7 */
+};
+
+static inline uint16_t mask(const uint8_t msb)
+{
+ const uint16_t m = (uint16_t)1 << (msb - 1);
+ return (m - 1) ^ m;
+}
+
+/*! Check whether RTP frame contains HR SID code word according to
+ * TS 101 318 §5.2.2
+ * \param[in] rtp_payload Buffer with RTP payload
+ * \param[in] payload_len Length of payload
+ * \returns true if code word is found, false otherwise
+ */
+bool osmo_hr_check_sid(const uint8_t *rtp_payload, size_t payload_len)
+{
+ uint8_t i, bits[] = { 1, 2, 8, 9, 5, 4, 9, 5, 4, 9, 5, 4, 9, 5 };
+ struct bitvec bv;
+ bv.data = (uint8_t *) rtp_payload;
+ bv.data_len = payload_len;
+ bv.cur_bit = 33;
+
+ /* code word is all 1 at given bits, numbered from 1, MODE is always 3 */
+ for (i = 0; i < ARRAY_SIZE(bits); i++)
+ if (bitvec_get_uint(&bv, bits[i]) != mask(bits[i]))
+ return false;
+
+ return true;
+}
diff --git a/lib/decoding/osmocom/codec/gsm660.c b/lib/decoding/osmocom/codec/gsm660.c
new file mode 100644
index 0000000..4f7bb09
--- /dev/null
+++ b/lib/decoding/osmocom/codec/gsm660.c
@@ -0,0 +1,259 @@
+/*! \file gsm660.c
+ * GSM 06.60 - GSM EFR Codec. */
+/*
+ * (C) 2010 Sylvain Munaut <tnt@246tNt.com>
+ *
+ * All Rights Reserved
+ *
+ * SPDX-License-Identifier: GPL-2.0+
+ *
+ * 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 2 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, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ */
+
+#include <stdint.h>
+#include <osmocom/codec/codec.h>
+
+/* GSM EFR - subjective importance bit ordering */
+ /* This array encodes GSM 05.03 Table 6.
+ *
+ * It converts between serial parameter output (as described in
+ * GSM 06.60 Table 6 and GSM 05.03 Table 5) and the order needed
+ * before channel encoding. CRC poly and bit repetition must be
+ * applied prior to this table, as in GSM 05.03 3.1.1, to get 260
+ * bits from a 244 bits raw EFR frame.
+ */
+const uint16_t gsm660_bitorder[260] = {
+ 38, 39, 40, 41, 42, 43, /* 0 -> LTP-LAG 1: b8..b3 */
+ 145, 146, 147, 148, 149, 150, /* 6 -> LTP-LAG 3: b8..b3 */
+ 93, 94, /* 12 -> LTP-LAG 2: b5..b4 */
+ 200, 201, /* 14 -> LTP-LAG 4: b5..b4 */
+ 47, /* 16 -> LTP-GAIN 1: b3 */
+ 88, /* 17 -> FCB-GAIN 1: b4 */
+ 99, /* 18 -> LTP-GAIN 2: b3 */
+ 140, /* 19 -> FCB-GAIN 2: b4 */
+ 44, /* 20 -> LTP-LAG 1: b2 */
+ 151, /* 21 -> LTP-LAG 3: b2 */
+ 95, /* 22 -> LTP-LAG 2: b3 */
+ 202, /* 23 -> LTP-LAG 4: b3 */
+ 1, 2, /* 24 -> LPC 1: b5..b4 */
+ 7, /* 26 -> LPC 2: b7 */
+ 9, /* 27 -> LPC 2: b5 */
+ 17, 18, /* 28 -> LPC 3: b6..b5 */
+ 23, /* 30 -> LPC 3: b0 */
+ 45, 46, /* 31 -> LTP-LAG 1: b1..b0 */
+ 152, 153, /* 33 -> LTP-LAG 3: b1..b0 */
+ 96, /* 35 -> LTP-LAG 2: b2 */
+ 203, /* 36 -> LTP-LAG 4: b2 */
+ 3, 4, /* 37 -> LPC 1: b3..b2 */
+ 10, 11, /* 39 -> LPC 2: b4..b3 */
+ 15, /* 41 -> LPC 3: b8 */
+ 8, /* 42 -> LPC 2: b6 */
+ 5, 6, /* 43 -> LPC 1: b1..b0 */
+ 12, /* 45 -> LPC 2: b2 */
+ 16, /* 46 -> LPC 3: b7 */
+ 19, /* 47 -> LPC 3: b4 */
+ 97, /* 48 -> LTP-LAG 2: b1 */
+ 204, /* 49 -> LTP-LAG 4: b1 */
+ 0, /* 50 -> LPC 1: b6 */
+ 13, 14, /* 51 -> LPC 2: b1..b0 */
+ 20, /* 53 -> LPC 3: b3 */
+ 24, 25, /* 54 -> LPC 4: b7..b6 */
+ 27, /* 56 -> LPC 4: b4 */
+ 154, /* 57 -> LTP-GAIN 3: b3 */
+ 206, /* 58 -> LTP-GAIN 4: b3 */
+ 195, /* 59 -> FCB-GAIN 3: b4 */
+ 247, /* 60 -> FCB-GAIN 4: b4 */
+ 89, /* 61 -> FCB-GAIN 1: b3 */
+ 141, /* 62 -> FCB-GAIN 2: b3 */
+ 196, /* 63 -> FCB-GAIN 3: b3 */
+ 248, /* 64 -> FCB-GAIN 4: b3 */
+ 252, 253, 254, 255, 256, 257, 258, 259, /* 65 -> CRC-POLY: b7..b0 */
+ 48, /* 73 -> LTP-GAIN 1: b2 */
+ 100, /* 74 -> LTP-GAIN 2: b2 */
+ 155, /* 75 -> LTP-GAIN 3: b2 */
+ 207, /* 76 -> LTP-GAIN 4: b2 */
+ 21, 22, /* 77 -> LPC 3: b2..b1 */
+ 26, /* 79 -> LPC 4: b5 */
+ 28, /* 80 -> LPC 4: b3 */
+ 51, /* 81 -> PULSE 1_1: b3 */
+ 55, /* 82 -> PULSE 1_2: b3 */
+ 59, /* 83 -> PULSE 1_3: b3 */
+ 63, /* 84 -> PULSE 1_4: b3 */
+ 67, /* 85 -> PULSE 1_5: b3 */
+ 103, /* 86 -> PULSE 2_1: b3 */
+ 107, /* 87 -> PULSE 2_2: b3 */
+ 111, /* 88 -> PULSE 2_3: b3 */
+ 115, /* 89 -> PULSE 2_4: b3 */
+ 119, /* 90 -> PULSE 2_5: b3 */
+ 158, /* 91 -> PULSE 3_1: b3 */
+ 162, /* 92 -> PULSE 3_2: b3 */
+ 166, /* 93 -> PULSE 3_3: b3 */
+ 170, /* 94 -> PULSE 3_4: b3 */
+ 174, /* 95 -> PULSE 3_5: b3 */
+ 210, /* 96 -> PULSE 4_1: b3 */
+ 214, /* 97 -> PULSE 4_2: b3 */
+ 218, /* 98 -> PULSE 4_3: b3 */
+ 222, /* 99 -> PULSE 4_4: b3 */
+ 226, /* 100 -> PULSE 4_5: b3 */
+ 90, /* 101 -> FCB-GAIN 1: b2 */
+ 142, /* 102 -> FCB-GAIN 2: b2 */
+ 197, /* 103 -> FCB-GAIN 3: b2 */
+ 249, /* 104 -> FCB-GAIN 4: b2 */
+ 49, /* 105 -> LTP-GAIN 1: b1 */
+ 101, /* 106 -> LTP-GAIN 2: b1 */
+ 156, /* 107 -> LTP-GAIN 3: b1 */
+ 208, /* 108 -> LTP-GAIN 4: b1 */
+ 29, 30, 31, /* 109 -> LPC 4: b2..b0 */
+ 32, 33, 34, 35, /* 112 -> LPC 5: b5..b2 */
+ 98, /* 116 -> LTP-LAG 2: b0 */
+ 205, /* 117 -> LTP-LAG 4: b0 */
+ 52, /* 118 -> PULSE 1_1: b2 */
+ 56, /* 119 -> PULSE 1_2: b2 */
+ 60, /* 120 -> PULSE 1_3: b2 */
+ 64, /* 121 -> PULSE 1_4: b2 */
+ 68, /* 122 -> PULSE 1_5: b2 */
+ 104, /* 123 -> PULSE 2_1: b2 */
+ 108, /* 124 -> PULSE 2_2: b2 */
+ 112, /* 125 -> PULSE 2_3: b2 */
+ 116, /* 126 -> PULSE 2_4: b2 */
+ 120, /* 127 -> PULSE 2_5: b2 */
+ 159, /* 128 -> PULSE 3_1: b2 */
+ 163, /* 129 -> PULSE 3_2: b2 */
+ 167, /* 130 -> PULSE 3_3: b2 */
+ 171, /* 131 -> PULSE 3_4: b2 */
+ 175, /* 132 -> PULSE 3_5: b2 */
+ 211, /* 133 -> PULSE 4_1: b2 */
+ 215, /* 134 -> PULSE 4_2: b2 */
+ 219, /* 135 -> PULSE 4_3: b2 */
+ 223, /* 136 -> PULSE 4_4: b2 */
+ 227, /* 137 -> PULSE 4_5: b2 */
+ 53, /* 138 -> PULSE 1_1: b1 */
+ 57, /* 139 -> PULSE 1_2: b1 */
+ 61, /* 140 -> PULSE 1_3: b1 */
+ 65, /* 141 -> PULSE 1_4: b1 */
+ 105, /* 142 -> PULSE 2_1: b1 */
+ 109, /* 143 -> PULSE 2_2: b1 */
+ 113, /* 144 -> PULSE 2_3: b1 */
+ 117, /* 145 -> PULSE 2_4: b1 */
+ 160, /* 146 -> PULSE 3_1: b1 */
+ 164, /* 147 -> PULSE 3_2: b1 */
+ 168, /* 148 -> PULSE 3_3: b1 */
+ 172, /* 149 -> PULSE 3_4: b1 */
+ 212, /* 150 -> PULSE 4_1: b1 */
+ 220, /* 151 -> PULSE 4_3: b1 */
+ 224, /* 152 -> PULSE 4_4: b1 */
+ 91, /* 153 -> FCB-GAIN 1: b1 */
+ 143, /* 154 -> FCB-GAIN 2: b1 */
+ 198, /* 155 -> FCB-GAIN 3: b1 */
+ 250, /* 156 -> FCB-GAIN 4: b1 */
+ 50, /* 157 -> LTP-GAIN 1: b0 */
+ 102, /* 158 -> LTP-GAIN 2: b0 */
+ 157, /* 159 -> LTP-GAIN 3: b0 */
+ 209, /* 160 -> LTP-GAIN 4: b0 */
+ 92, /* 161 -> FCB-GAIN 1: b0 */
+ 144, /* 162 -> FCB-GAIN 2: b0 */
+ 199, /* 163 -> FCB-GAIN 3: b0 */
+ 251, /* 164 -> FCB-GAIN 4: b0 */
+ 54, /* 165 -> PULSE 1_1: b0 */
+ 58, /* 166 -> PULSE 1_2: b0 */
+ 62, /* 167 -> PULSE 1_3: b0 */
+ 66, /* 168 -> PULSE 1_4: b0 */
+ 106, /* 169 -> PULSE 2_1: b0 */
+ 110, /* 170 -> PULSE 2_2: b0 */
+ 114, /* 171 -> PULSE 2_3: b0 */
+ 118, /* 172 -> PULSE 2_4: b0 */
+ 161, /* 173 -> PULSE 3_1: b0 */
+ 165, /* 174 -> PULSE 3_2: b0 */
+ 169, /* 175 -> PULSE 3_3: b0 */
+ 173, /* 176 -> PULSE 3_4: b0 */
+ 213, /* 177 -> PULSE 4_1: b0 */
+ 221, /* 178 -> PULSE 4_3: b0 */
+ 225, /* 179 -> PULSE 4_4: b0 */
+ 36, 37, /* 180 -> LPC 5: b1..b0 */
+ 69, /* 182 -> PULSE 1_5: b1 */
+ 71, 72, /* 183 -> PULSE 1_5: b1..b1 */
+ 121, /* 185 -> PULSE 2_5: b1 */
+ 123, 124, /* 186 -> PULSE 2_5: b1..b1 */
+ 176, /* 188 -> PULSE 3_5: b1 */
+ 178, 179, /* 189 -> PULSE 3_5: b1..b1 */
+ 228, /* 191 -> PULSE 4_5: b1 */
+ 230, 231, /* 192 -> PULSE 4_5: b1..b1 */
+ 216, 217, /* 194 -> PULSE 4_2: b1..b0 */
+ 70, /* 196 -> PULSE 1_5: b0 */
+ 122, /* 197 -> PULSE 2_5: b0 */
+ 177, /* 198 -> PULSE 3_5: b0 */
+ 229, /* 199 -> PULSE 4_5: b0 */
+ 73, /* 200 -> PULSE 1_6: b2 */
+ 76, /* 201 -> PULSE 1_7: b2 */
+ 79, /* 202 -> PULSE 1_8: b2 */
+ 82, /* 203 -> PULSE 1_9: b2 */
+ 85, /* 204 -> PULSE 1_10: b2 */
+ 125, /* 205 -> PULSE 2_6: b2 */
+ 128, /* 206 -> PULSE 2_7: b2 */
+ 131, /* 207 -> PULSE 2_8: b2 */
+ 134, /* 208 -> PULSE 2_9: b2 */
+ 137, /* 209 -> PULSE 2_10: b2 */
+ 180, /* 210 -> PULSE 3_6: b2 */
+ 183, /* 211 -> PULSE 3_7: b2 */
+ 186, /* 212 -> PULSE 3_8: b2 */
+ 189, /* 213 -> PULSE 3_9: b2 */
+ 192, /* 214 -> PULSE 3_10: b2 */
+ 232, /* 215 -> PULSE 4_6: b2 */
+ 235, /* 216 -> PULSE 4_7: b2 */
+ 238, /* 217 -> PULSE 4_8: b2 */
+ 241, /* 218 -> PULSE 4_9: b2 */
+ 244, /* 219 -> PULSE 4_10: b2 */
+ 74, /* 220 -> PULSE 1_6: b1 */
+ 77, /* 221 -> PULSE 1_7: b1 */
+ 80, /* 222 -> PULSE 1_8: b1 */
+ 83, /* 223 -> PULSE 1_9: b1 */
+ 86, /* 224 -> PULSE 1_10: b1 */
+ 126, /* 225 -> PULSE 2_6: b1 */
+ 129, /* 226 -> PULSE 2_7: b1 */
+ 132, /* 227 -> PULSE 2_8: b1 */
+ 135, /* 228 -> PULSE 2_9: b1 */
+ 138, /* 229 -> PULSE 2_10: b1 */
+ 181, /* 230 -> PULSE 3_6: b1 */
+ 184, /* 231 -> PULSE 3_7: b1 */
+ 187, /* 232 -> PULSE 3_8: b1 */
+ 190, /* 233 -> PULSE 3_9: b1 */
+ 193, /* 234 -> PULSE 3_10: b1 */
+ 233, /* 235 -> PULSE 4_6: b1 */
+ 236, /* 236 -> PULSE 4_7: b1 */
+ 239, /* 237 -> PULSE 4_8: b1 */
+ 242, /* 238 -> PULSE 4_9: b1 */
+ 245, /* 239 -> PULSE 4_10: b1 */
+ 75, /* 240 -> PULSE 1_6: b0 */
+ 78, /* 241 -> PULSE 1_7: b0 */
+ 81, /* 242 -> PULSE 1_8: b0 */
+ 84, /* 243 -> PULSE 1_9: b0 */
+ 87, /* 244 -> PULSE 1_10: b0 */
+ 127, /* 245 -> PULSE 2_6: b0 */
+ 130, /* 246 -> PULSE 2_7: b0 */
+ 133, /* 247 -> PULSE 2_8: b0 */
+ 136, /* 248 -> PULSE 2_9: b0 */
+ 139, /* 249 -> PULSE 2_10: b0 */
+ 182, /* 250 -> PULSE 3_6: b0 */
+ 185, /* 251 -> PULSE 3_7: b0 */
+ 188, /* 252 -> PULSE 3_8: b0 */
+ 191, /* 253 -> PULSE 3_9: b0 */
+ 194, /* 254 -> PULSE 3_10: b0 */
+ 234, /* 255 -> PULSE 4_6: b0 */
+ 237, /* 256 -> PULSE 4_7: b0 */
+ 240, /* 257 -> PULSE 4_8: b0 */
+ 243, /* 258 -> PULSE 4_9: b0 */
+ 246, /* 259 -> PULSE 4_10: b0 */
+};
diff --git a/lib/decoding/osmocom/codec/gsm690.c b/lib/decoding/osmocom/codec/gsm690.c
new file mode 100644
index 0000000..1955716
--- /dev/null
+++ b/lib/decoding/osmocom/codec/gsm690.c
@@ -0,0 +1,318 @@
+/*! \file gsm690.c
+ * GSM 06.90 - GSM AMR Codec. */
+/*
+ * (C) 2010 Sylvain Munaut <tnt@246tNt.com>
+ *
+ * All Rights Reserved
+ *
+ * SPDX-License-Identifier: GPL-2.0+
+ *
+ * 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 2 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, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ */
+
+#include <stdint.h>
+#include <errno.h>
+#include <stdio.h>
+#include <stdlib.h>
+
+#include <osmocom/core/utils.h>
+#include <osmocom/codec/codec.h>
+/*
+ * These table map between the raw encoder parameter output and
+ * the format used before channel coding. Both in GSM and in various
+ * file/network format (same tables used in several specs).
+ */
+
+/* AMR 12.2 kbits - subjective importance bit ordering */
+ /* This array encodes GSM 05.03 Table 7
+ * It's also TS 26.101 Table B.8
+ */
+const uint16_t gsm690_12_2_bitorder[244] = {
+ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9,
+ 10, 11, 12, 13, 14, 23, 15, 16, 17, 18,
+ 19, 20, 21, 22, 24, 25, 26, 27, 28, 38,
+ 141, 39, 142, 40, 143, 41, 144, 42, 145, 43,
+ 146, 44, 147, 45, 148, 46, 149, 47, 97, 150,
+ 200, 48, 98, 151, 201, 49, 99, 152, 202, 86,
+ 136, 189, 239, 87, 137, 190, 240, 88, 138, 191,
+ 241, 91, 194, 92, 195, 93, 196, 94, 197, 95,
+ 198, 29, 30, 31, 32, 33, 34, 35, 50, 100,
+ 153, 203, 89, 139, 192, 242, 51, 101, 154, 204,
+ 55, 105, 158, 208, 90, 140, 193, 243, 59, 109,
+ 162, 212, 63, 113, 166, 216, 67, 117, 170, 220,
+ 36, 37, 54, 53, 52, 58, 57, 56, 62, 61,
+ 60, 66, 65, 64, 70, 69, 68, 104, 103, 102,
+ 108, 107, 106, 112, 111, 110, 116, 115, 114, 120,
+ 119, 118, 157, 156, 155, 161, 160, 159, 165, 164,
+ 163, 169, 168, 167, 173, 172, 171, 207, 206, 205,
+ 211, 210, 209, 215, 214, 213, 219, 218, 217, 223,
+ 222, 221, 73, 72, 71, 76, 75, 74, 79, 78,
+ 77, 82, 81, 80, 85, 84, 83, 123, 122, 121,
+ 126, 125, 124, 129, 128, 127, 132, 131, 130, 135,
+ 134, 133, 176, 175, 174, 179, 178, 177, 182, 181,
+ 180, 185, 184, 183, 188, 187, 186, 226, 225, 224,
+ 229, 228, 227, 232, 231, 230, 235, 234, 233, 238,
+ 237, 236, 96, 199,
+};
+
+/* AMR 10.2 kbits - subjective importance bit ordering */
+ /* This array encodes GSM 05.03 Table 8
+ * It's also TS 26.101 Table B.7
+ */
+const uint16_t gsm690_10_2_bitorder[204] = {
+ 7, 6, 5, 4, 3, 2, 1, 0, 16, 15,
+ 14, 13, 12, 11, 10, 9, 8, 26, 27, 28,
+ 29, 30, 31, 115, 116, 117, 118, 119, 120, 72,
+ 73, 161, 162, 65, 68, 69, 108, 111, 112, 154,
+ 157, 158, 197, 200, 201, 32, 33, 121, 122, 74,
+ 75, 163, 164, 66, 109, 155, 198, 19, 23, 21,
+ 22, 18, 17, 20, 24, 25, 37, 36, 35, 34,
+ 80, 79, 78, 77, 126, 125, 124, 123, 169, 168,
+ 167, 166, 70, 67, 71, 113, 110, 114, 159, 156,
+ 160, 202, 199, 203, 76, 165, 81, 82, 92, 91,
+ 93, 83, 95, 85, 84, 94, 101, 102, 96, 104,
+ 86, 103, 87, 97, 127, 128, 138, 137, 139, 129,
+ 141, 131, 130, 140, 147, 148, 142, 150, 132, 149,
+ 133, 143, 170, 171, 181, 180, 182, 172, 184, 174,
+ 173, 183, 190, 191, 185, 193, 175, 192, 176, 186,
+ 38, 39, 49, 48, 50, 40, 52, 42, 41, 51,
+ 58, 59, 53, 61, 43, 60, 44, 54, 194, 179,
+ 189, 196, 177, 195, 178, 187, 188, 151, 136, 146,
+ 153, 134, 152, 135, 144, 145, 105, 90, 100, 107,
+ 88, 106, 89, 98, 99, 62, 47, 57, 64, 45,
+ 63, 46, 55, 56,
+};
+
+/* AMR 7.95 kbits - subjective importance bit ordering */
+ /* This array encodes GSM 05.03 Table 9
+ * It's also TS 26.101 Table B.6
+ */
+const uint16_t gsm690_7_95_bitorder[159] = {
+ 8, 7, 6, 5, 4, 3, 2, 14, 16, 9,
+ 10, 12, 13, 15, 11, 17, 20, 22, 24, 23,
+ 19, 18, 21, 56, 88, 122, 154, 57, 89, 123,
+ 155, 58, 90, 124, 156, 52, 84, 118, 150, 53,
+ 85, 119, 151, 27, 93, 28, 94, 29, 95, 30,
+ 96, 31, 97, 61, 127, 62, 128, 63, 129, 59,
+ 91, 125, 157, 32, 98, 64, 130, 1, 0, 25,
+ 26, 33, 99, 34, 100, 65, 131, 66, 132, 54,
+ 86, 120, 152, 60, 92, 126, 158, 55, 87, 121,
+ 153, 117, 116, 115, 46, 78, 112, 144, 43, 75,
+ 109, 141, 40, 72, 106, 138, 36, 68, 102, 134,
+ 114, 149, 148, 147, 146, 83, 82, 81, 80, 51,
+ 50, 49, 48, 47, 45, 44, 42, 39, 35, 79,
+ 77, 76, 74, 71, 67, 113, 111, 110, 108, 105,
+ 101, 145, 143, 142, 140, 137, 133, 41, 73, 107,
+ 139, 37, 69, 103, 135, 38, 70, 104, 136,
+};
+
+/* AMR 7.4 kbits - subjective importance bit ordering */
+ /* This array encodes GSM 05.03 Table 10
+ * It's also TS 26.101 Table B.5
+ */
+const uint16_t gsm690_7_4_bitorder[148] = {
+ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9,
+ 10, 11, 12, 13, 14, 15, 16, 26, 87, 27,
+ 88, 28, 89, 29, 90, 30, 91, 51, 80, 112,
+ 141, 52, 81, 113, 142, 54, 83, 115, 144, 55,
+ 84, 116, 145, 58, 119, 59, 120, 21, 22, 23,
+ 17, 18, 19, 31, 60, 92, 121, 56, 85, 117,
+ 146, 20, 24, 25, 50, 79, 111, 140, 57, 86,
+ 118, 147, 49, 78, 110, 139, 48, 77, 53, 82,
+ 114, 143, 109, 138, 47, 76, 108, 137, 32, 33,
+ 61, 62, 93, 94, 122, 123, 41, 42, 43, 44,
+ 45, 46, 70, 71, 72, 73, 74, 75, 102, 103,
+ 104, 105, 106, 107, 131, 132, 133, 134, 135, 136,
+ 34, 63, 95, 124, 35, 64, 96, 125, 36, 65,
+ 97, 126, 37, 66, 98, 127, 38, 67, 99, 128,
+ 39, 68, 100, 129, 40, 69, 101, 130,
+};
+
+/* AMR 6.7 kbits - subjective importance bit ordering */
+ /* This array encodes GSM 05.03 Table 11
+ * It's also TS 26.101 Table B.4
+ */
+const uint16_t gsm690_6_7_bitorder[134] = {
+ 0, 1, 4, 3, 5, 6, 13, 7, 2, 8,
+ 9, 11, 15, 12, 14, 10, 28, 82, 29, 83,
+ 27, 81, 26, 80, 30, 84, 16, 55, 109, 56,
+ 110, 31, 85, 57, 111, 48, 73, 102, 127, 32,
+ 86, 51, 76, 105, 130, 52, 77, 106, 131, 58,
+ 112, 33, 87, 19, 23, 53, 78, 107, 132, 21,
+ 22, 18, 17, 20, 24, 25, 50, 75, 104, 129,
+ 47, 72, 101, 126, 54, 79, 108, 133, 46, 71,
+ 100, 125, 128, 103, 74, 49, 45, 70, 99, 124,
+ 42, 67, 96, 121, 39, 64, 93, 118, 38, 63,
+ 92, 117, 35, 60, 89, 114, 34, 59, 88, 113,
+ 44, 69, 98, 123, 43, 68, 97, 122, 41, 66,
+ 95, 120, 40, 65, 94, 119, 37, 62, 91, 116,
+ 36, 61, 90, 115,
+};
+
+/* AMR 5.9 kbits - subjective importance bit ordering */
+ /* This array encodes GSM 05.03 Table 12
+ * It's also TS 26.101 Table B.3
+ */
+const uint16_t gsm690_5_9_bitorder[118] = {
+ 0, 1, 4, 5, 3, 6, 7, 2, 13, 15,
+ 8, 9, 11, 12, 14, 10, 16, 28, 74, 29,
+ 75, 27, 73, 26, 72, 30, 76, 51, 97, 50,
+ 71, 96, 117, 31, 77, 52, 98, 49, 70, 95,
+ 116, 53, 99, 32, 78, 33, 79, 48, 69, 94,
+ 115, 47, 68, 93, 114, 46, 67, 92, 113, 19,
+ 21, 23, 22, 18, 17, 20, 24, 111, 43, 89,
+ 110, 64, 65, 44, 90, 25, 45, 66, 91, 112,
+ 54, 100, 40, 61, 86, 107, 39, 60, 85, 106,
+ 36, 57, 82, 103, 35, 56, 81, 102, 34, 55,
+ 80, 101, 42, 63, 88, 109, 41, 62, 87, 108,
+ 38, 59, 84, 105, 37, 58, 83, 104,
+};
+
+/* AMR 5.15 kbits - subjective importance bit ordering */
+ /* This array encodes GSM 05.03 Table 13
+ * It's also TS 26.101 Table B.2
+ */
+const uint16_t gsm690_5_15_bitorder[103] = {
+ 7, 6, 5, 4, 3, 2, 1, 0, 15, 14,
+ 13, 12, 11, 10, 9, 8, 23, 24, 25, 26,
+ 27, 46, 65, 84, 45, 44, 43, 64, 63, 62,
+ 83, 82, 81, 102, 101, 100, 42, 61, 80, 99,
+ 28, 47, 66, 85, 18, 41, 60, 79, 98, 29,
+ 48, 67, 17, 20, 22, 40, 59, 78, 97, 21,
+ 30, 49, 68, 86, 19, 16, 87, 39, 38, 58,
+ 57, 77, 35, 54, 73, 92, 76, 96, 95, 36,
+ 55, 74, 93, 32, 51, 33, 52, 70, 71, 89,
+ 90, 31, 50, 69, 88, 37, 56, 75, 94, 34,
+ 53, 72, 91,
+};
+
+/* AMR 4.75 kbits - subjective importance bit ordering */
+ /* This array encodes GSM 05.03 Table 14
+ * It's also TS 26.101 Table B.1
+ */
+const uint16_t gsm690_4_75_bitorder[95] = {
+ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9,
+ 10, 11, 12, 13, 14, 15, 23, 24, 25, 26,
+ 27, 28, 48, 49, 61, 62, 82, 83, 47, 46,
+ 45, 44, 81, 80, 79, 78, 17, 18, 20, 22,
+ 77, 76, 75, 74, 29, 30, 43, 42, 41, 40,
+ 38, 39, 16, 19, 21, 50, 51, 59, 60, 63,
+ 64, 72, 73, 84, 85, 93, 94, 32, 33, 35,
+ 36, 53, 54, 56, 57, 66, 67, 69, 70, 87,
+ 88, 90, 91, 34, 55, 68, 89, 37, 58, 71,
+ 92, 31, 52, 65, 86,
+};
+
+static const uint8_t amr_len_by_ft[16] = {
+ 12, 13, 15, 17, 19, 20, 26, 31, 7, 0, 0, 0, 0, 0, 0, 0
+};
+
+const struct value_string osmo_amr_type_names[] = {
+ { AMR_4_75, "AMR 4,75 kbits/s" },
+ { AMR_5_15, "AMR 5,15 kbit/s" },
+ { AMR_5_90, "AMR 5,90 kbit/s" },
+ { AMR_6_70, "AMR 6,70 kbit/s (PDC-EFR)" },
+ { AMR_7_40, "AMR 7,40 kbit/s (TDMA-EFR)" },
+ { AMR_7_95, "AMR 7,95 kbit/s" },
+ { AMR_10_2, "AMR 10,2 kbit/s" },
+ { AMR_12_2, "AMR 12,2 kbit/s (GSM-EFR)" },
+ { AMR_SID, "AMR SID" },
+ { AMR_GSM_EFR_SID, "GSM-EFR SID" },
+ { AMR_TDMA_EFR_SID, "TDMA-EFR SID" },
+ { AMR_PDC_EFR_SID, "PDC-EFR SID" },
+ { AMR_NO_DATA, "No Data/NA" },
+ { 0, NULL },
+};
+
+/*! Decode various AMR parameters from RTP payload (RFC 4867) acording to
+ * 3GPP TS 26.101
+ * \param[in] rtppayload Payload from RTP packet
+ * \param[in] payload_len length of rtppayload
+ * \param[out] cmr AMR Codec Mode Request, not filled if NULL
+ * \param[out] cmi AMR Codec Mode Indicator, -1 if not applicable for this type,
+ * not filled if NULL
+ * \param[out] ft AMR Frame Type, not filled if NULL
+ * \param[out] bfi AMR Bad Frame Indicator, not filled if NULL
+ * \param[out] sti AMR SID Type Indicator, -1 if not applicable for this type,
+ * not filled if NULL
+ * \returns length of AMR data or negative value on error
+ */
+int osmo_amr_rtp_dec(const uint8_t *rtppayload, int payload_len, uint8_t *cmr,
+ int8_t *cmi, enum osmo_amr_type *ft,
+ enum osmo_amr_quality *bfi, int8_t *sti)
+{
+ if (payload_len < 2 || !rtppayload)
+ return -EINVAL;
+
+ /* RFC 4867 § 4.4.2 ToC - compound payloads are not supported: F = 0 */
+ uint8_t type = (rtppayload[1] >> 3) & 0xf;
+
+ /* compound payloads are not supported */
+ if (rtppayload[1] >> 7)
+ return -ENOTSUP;
+
+ if (payload_len < amr_len_by_ft[type])
+ return -ENOTSUP;
+
+ if (ft)
+ *ft = type;
+
+ if (cmr)
+ *cmr = rtppayload[0] >> 4;
+
+ if (bfi)
+ *bfi = (rtppayload[1] >> 2) & 1;
+
+ /* Table 6 in 3GPP TS 26.101 */
+ if (cmi)
+ *cmi = (type == AMR_SID) ? ((rtppayload[6] >> 1) & 7) : -1;
+
+ if (sti)
+ *sti = (type == AMR_SID) ? (rtppayload[6] & 0x10) : -1;
+
+ return 2 + amr_len_by_ft[type];
+}
+
+/*! Encode various AMR parameters from RTP payload (RFC 4867)
+ * \param[out] payload Payload for RTP packet, contains speech data (if any)
+ * except for have 2 first bytes where header will be built
+ * \param[in] cmr AMR codec Mode Request
+ * \param[in] ft AMR Frame Type
+ * \param[in] bfi AMR Bad Frame Indicator
+ * \returns length of AMR data (header + ToC + speech data) or negative value
+ * on error
+ *
+ * Note: only octet-aligned mode is supported so the header occupies 2 full
+ * bytes. Optional interleaving header is not supported.
+ */
+int osmo_amr_rtp_enc(uint8_t *payload, uint8_t cmr, enum osmo_amr_type ft,
+ enum osmo_amr_quality bfi)
+{
+ if (cmr > 15)
+ return -EINVAL;
+
+ if (ft > 15)
+ return -ENOTSUP;
+
+ /* RFC 4867 § 4.3.1 payload header */
+ payload[0] = cmr << 4;
+
+ /* RFC 4867 § 4.4.2 ToC - compound payloads are not supported: F = 0 */
+ payload[1] = (((uint8_t)ft) << 3) | (((uint8_t)bfi) << 2);
+
+ /* speech data */
+ return 2 + amr_len_by_ft[ft];
+}
diff --git a/lib/decoding/osmocom/coding/CMakeLists.txt b/lib/decoding/osmocom/coding/CMakeLists.txt
new file mode 100644
index 0000000..c0a4d92
--- /dev/null
+++ b/lib/decoding/osmocom/coding/CMakeLists.txt
@@ -0,0 +1,27 @@
+# Copyright 2011,2012 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.
+
+add_sources(
+ gsm0503_conv.c
+ gsm0503_coding.c
+ gsm0503_interleaving.c
+ gsm0503_mapping.c
+ gsm0503_parity.c
+ gsm0503_tables.c
+)
diff --git a/lib/decoding/osmocom/coding/gsm0503_coding.c b/lib/decoding/osmocom/coding/gsm0503_coding.c
new file mode 100644
index 0000000..e83e536
--- /dev/null
+++ b/lib/decoding/osmocom/coding/gsm0503_coding.c
@@ -0,0 +1,2066 @@
+/*
+ * (C) 2013 by Andreas Eversberg <jolly@eversberg.eu>
+ * (C) 2015 by Alexander Chemeris <Alexander.Chemeris@fairwaves.co>
+ * (C) 2016 by Tom Tsou <tom.tsou@ettus.com>
+ * (C) 2017 by Harald Welte <laforge@gnumonks.org>
+ *
+ * All Rights Reserved
+ *
+ * SPDX-License-Identifier: GPL-2.0+
+ *
+ * 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 2 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, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+
+#include <stdio.h>
+#include <stdint.h>
+#include <string.h>
+#include <stdlib.h>
+#include <errno.h>
+
+#define GSM_MACBLOCK_LEN 23
+
+#include <osmocom/core/bits.h>
+#include <osmocom/core/conv.h>
+//#include <osmocom/core/utils.h>
+#include <osmocom/core/crcgen.h>
+#include <osmocom/core/endian.h>
+
+//#include <osmocom/gsm/protocol/gsm_04_08.h>
+/*#include <osmocom/gprs/protocol/gsm_04_60.h>*/
+/*#include <osmocom/gprs/gprs_rlc.h>*/
+
+#include <osmocom/gsm/gsm0503.h>
+#include <osmocom/codec/codec.h>
+
+#include <osmocom/coding/gsm0503_interleaving.h>
+#include <osmocom/coding/gsm0503_mapping.h>
+#include <osmocom/coding/gsm0503_tables.h>
+#include <osmocom/coding/gsm0503_coding.h>
+#include <osmocom/coding/gsm0503_parity.h>
+
+/*! \mainpage libosmocoding Documentation
+ *
+ * \section sec_intro Introduction
+ * This library is a collection of definitions, tables and functions
+ * implementing the GSM/GPRS/EGPRS channel coding (and decoding) as
+ * specified in 3GPP TS 05.03 / 45.003.
+ *
+ * libosmocoding is developed as part of the Osmocom (Open Source Mobile
+ * Communications) project, a community-based, collaborative development
+ * project to create Free and Open Source implementations of mobile
+ * communications systems. For more information about Osmocom, please
+ * see https://osmocom.org/
+ *
+ * \section sec_copyright Copyright and License
+ * Copyright © 2013 by Andreas Eversberg\n
+ * Copyright © 2015 by Alexander Chemeris\n
+ * Copyright © 2016 by Tom Tsou\n
+ * Documentation Copyright © 2017 by Harald Welte\n
+ * All rights reserved. \n\n
+ * The source code of libosmocoding is licensed under the terms of the GNU
+ * General Public License as published by the Free Software Foundation;
+ * either version 2 of the License, or (at your option) any later
+ * version.\n
+ * See <http://www.gnu.org/licenses/> or COPYING included in the source
+ * code package istelf.\n
+ * The information detailed here is provided AS IS with NO WARRANTY OF
+ * ANY KIND, INCLUDING THE WARRANTY OF DESIGN, MERCHANTABILITY AND
+ * FITNESS FOR A PARTICULAR PURPOSE.
+ * \n\n
+ *
+ * \section sec_tracker Homepage + Issue Tracker
+ * libosmocoding is distributed as part of libosmocore and shares its
+ * project page at http://osmocom.org/projects/libosmocore
+ *
+ * An Issue Tracker can be found at
+ * https://osmocom.org/projects/libosmocore/issues
+ *
+ * \section sec_contact Contact and Support
+ * Community-based support is available at the OpenBSC mailing list
+ * <http://lists.osmocom.org/mailman/listinfo/openbsc>\n
+ * Commercial support options available upon request from
+ * <http://sysmocom.de/>
+ */
+
+
+/*! \addtogroup coding
+ * @{
+ *
+ * GSM TS 05.03 coding
+ *
+ * This module is the "master module" of libosmocoding. It uses the
+ * various other modules (mapping, parity, interleaving) in order to
+ * implement the complete channel coding (and decoding) chain for the
+ * various channel types as defined in TS 05.03 / 45.003.
+ *
+ * \file gsm0503_coding.c */
+
+/*
+ * EGPRS coding limits
+ */
+
+/* Max header size with parity bits */
+#define EGPRS_HDR_UPP_MAX 54
+
+/* Max encoded header size */
+#define EGPRS_HDR_C_MAX 162
+
+/* Max punctured header size */
+#define EGPRS_HDR_HC_MAX 160
+
+/* Max data block size with parity bits */
+#define EGPRS_DATA_U_MAX 612
+
+/* Max encoded data block size */
+#define EGPRS_DATA_C_MAX 1836
+
+/* Max single block punctured data size */
+#define EGPRS_DATA_DC_MAX 1248
+
+/* Dual block punctured data size */
+#define EGPRS_DATA_C1 612
+#define EGPRS_DATA_C2 EGPRS_DATA_C1
+
+/*! union across the three different EGPRS Uplink header types *
+union gprs_rlc_ul_hdr_egprs {
+ struct gprs_rlc_ul_header_egprs_1 type1;
+ struct gprs_rlc_ul_header_egprs_2 type2;
+ struct gprs_rlc_ul_header_egprs_3 type3;
+};
+
+/*! union across the three different EGPRS Downlink header types *
+union gprs_rlc_dl_hdr_egprs {
+ struct gprs_rlc_dl_header_egprs_1 type1;
+ struct gprs_rlc_dl_header_egprs_2 type2;
+ struct gprs_rlc_dl_header_egprs_3 type3;
+};
+
+/*! Structure describing a Modulation and Coding Scheme */
+struct gsm0503_mcs_code {
+ /*! Modulation and Coding Scheme (MSC) number */
+ uint8_t mcs;
+ /*! Length of Uplink Stealing Flag (USF) in bits */
+ uint8_t usf_len;
+
+ /* Header coding */
+ /*! Length of header (bits) */
+ uint8_t hdr_len;
+ /*! Length of header convolutional code */
+ uint8_t hdr_code_len;
+ /*! Length of header code puncturing sequence */
+ uint8_t hdr_punc_len;
+ /*! header convolutional code */
+ const struct osmo_conv_code *hdr_conv;
+ /*! header puncturing sequence */
+ const uint8_t *hdr_punc;
+
+ /* Data coding */
+ /*! length of data (bits) */
+ uint16_t data_len;
+ /*! length of data convolutional code */
+ uint16_t data_code_len;
+ /*! length of data code puncturing sequence */
+ uint16_t data_punc_len;
+ /*! data convolutional code */
+ const struct osmo_conv_code *data_conv;
+ /*! data puncturing sequences */
+ const uint8_t *data_punc[3];
+};
+
+static int osmo_conv_decode_ber(const struct osmo_conv_code *code,
+ const sbit_t *input, ubit_t *output,
+ int *n_errors, int *n_bits_total)
+{
+ int res, i, coded_len;
+ ubit_t recoded[EGPRS_DATA_C_MAX];
+
+ res = osmo_conv_decode(code, input, output);
+
+ if (n_bits_total || n_errors) {
+ coded_len = osmo_conv_encode(code, output, recoded);
+ //OSMO_ASSERT(sizeof(recoded) / sizeof(recoded[0]) >= coded_len);
+ }
+
+ /* Count bit errors */
+ if (n_errors) {
+ *n_errors = 0;
+ for (i = 0; i < coded_len; i++) {
+ if (!((recoded[i] && input[i] < 0) ||
+ (!recoded[i] && input[i] > 0)) )
+ *n_errors += 1;
+ }
+ }
+
+ if (n_bits_total)
+ *n_bits_total = coded_len;
+
+ return res;
+}
+
+/*! convenience wrapper for decoding coded bits
+ * \param[out] l2_data caller-allocated buffer for L2 Frame
+ * \param[in] cB 456 coded (soft) bits as per TS 05.03 4.1.3
+ * \param[out] n_errors Number of detected errors
+ * \param[out] n_bits_total Number of total coded bits
+ * \returns 0 on success; -1 on CRC error */
+static int _xcch_decode_cB(uint8_t *l2_data, const sbit_t *cB,
+ int *n_errors, int *n_bits_total)
+{
+ ubit_t conv[224];
+ int rv;
+
+ osmo_conv_decode_ber(&gsm0503_xcch, cB,
+ conv, n_errors, n_bits_total);
+
+ rv = osmo_crc64gen_check_bits(&gsm0503_fire_crc40,
+ conv, 184, conv + 184);
+ if (rv)
+ return -1;
+
+ osmo_ubit2pbit_ext(l2_data, 0, conv, 0, 184, 1);
+
+ return 0;
+}
+
+/*! convenience wrapper for encoding to coded bits
+ * \param[out] cB caller-allocated buffer for 456 coded bits as per TS 05.03 4.1.3
+ * \param[out] l2_data to-be-encoded L2 Frame
+ * \returns 0 */
+static int _xcch_encode_cB(ubit_t *cB, const uint8_t *l2_data)
+{
+ ubit_t conv[224];
+
+ osmo_pbit2ubit_ext(conv, 0, l2_data, 0, 184, 1);
+
+ osmo_crc64gen_set_bits(&gsm0503_fire_crc40, conv, 184, conv + 184);
+
+ osmo_conv_encode(&gsm0503_xcch, conv, cB);
+
+ return 0;
+}
+
+/*
+ * GSM xCCH block transcoding
+ */
+
+/*! Decoding of xCCH data from bursts to L2 frame
+ * \param[out] l2_data caller-allocated output data buffer
+ * \param[in] bursts four GSM bursts in soft-bits
+ * \param[out] n_errors Number of detected errors
+ * \param[out] n_bits_total Number of total coded bits
+ */
+int gsm0503_xcch_decode(uint8_t *l2_data, const sbit_t *bursts,
+ int *n_errors, int *n_bits_total)
+{
+ sbit_t iB[456], cB[456];
+ int i;
+
+ for (i = 0; i < 4; i++)
+ gsm0503_xcch_burst_unmap(&iB[i * 114], &bursts[i * 116], NULL, NULL);
+
+ gsm0503_xcch_deinterleave(cB, iB);
+
+ return _xcch_decode_cB(l2_data, cB, n_errors, n_bits_total);
+}
+
+/*! Encoding of xCCH data from L2 frame to bursts
+ * \param[out] bursts caller-allocated burst data (unpacked bits)
+ * \param[in] l2_data L2 input data (MAC block)
+ * \returns 0
+ */
+int gsm0503_xcch_encode(ubit_t *bursts, const uint8_t *l2_data)
+{
+ ubit_t iB[456], cB[456], hl = 1, hn = 1;
+ int i;
+
+ _xcch_encode_cB(cB, l2_data);
+
+ gsm0503_xcch_interleave(cB, iB);
+
+ for (i = 0; i < 4; i++)
+ gsm0503_xcch_burst_map(&iB[i * 114], &bursts[i * 116], &hl, &hn);
+
+ return 0;
+}
+
+
+
+/*
+ * GSM PDTCH block transcoding
+ */
+
+/*! Decode GPRS PDTCH
+ * \param[out] l2_data caller-allocated buffer for L2 Frame
+ * \param[in] bursts burst input data as soft unpacked bits
+ * \param[out] usf_p uplink stealing flag
+ * \param[out] n_errors number of detected bit-errors
+ * \param[out] n_bits_total total number of dcoded bits
+ * \returns 0 on success; negative on error *
+int gsm0503_pdtch_decode(uint8_t *l2_data, const sbit_t *bursts, uint8_t *usf_p,
+ int *n_errors, int *n_bits_total)
+{
+ sbit_t iB[456], cB[676], hl_hn[8];
+ ubit_t conv[456];
+ int i, j, k, rv, best = 0, cs = 0, usf = 0; /* make GCC happy *
+
+ for (i = 0; i < 4; i++)
+ gsm0503_xcch_burst_unmap(&iB[i * 114], &bursts[i * 116],
+ hl_hn + i * 2, hl_hn + i * 2 + 1);
+
+ for (i = 0; i < 4; i++) {
+ for (j = 0, k = 0; j < 8; j++)
+ k += abs(((int)gsm0503_pdtch_hl_hn_sbit[i][j]) - ((int)hl_hn[j]));
+
+ if (i == 0 || k < best) {
+ best = k;
+ cs = i + 1;
+ }
+ }
+
+ gsm0503_xcch_deinterleave(cB, iB);
+
+ switch (cs) {
+ case 1:
+ osmo_conv_decode_ber(&gsm0503_xcch, cB,
+ conv, n_errors, n_bits_total);
+
+ rv = osmo_crc64gen_check_bits(&gsm0503_fire_crc40,
+ conv, 184, conv + 184);
+ if (rv)
+ return -1;
+
+ osmo_ubit2pbit_ext(l2_data, 0, conv, 0, 184, 1);
+
+ return 23;
+ case 2:
+ for (i = 587, j = 455; i >= 0; i--) {
+ if (!gsm0503_puncture_cs2[i])
+ cB[i] = cB[j--];
+ else
+ cB[i] = 0;
+ }
+
+ osmo_conv_decode_ber(&gsm0503_cs2_np, cB,
+ conv, n_errors, n_bits_total);
+
+ for (i = 0; i < 8; i++) {
+ for (j = 0, k = 0; j < 6; j++)
+ k += abs(((int)gsm0503_usf2six[i][j]) - ((int)conv[j]));
+
+ if (i == 0 || k < best) {
+ best = k;
+ usf = i;
+ }
+ }
+
+ conv[3] = usf & 1;
+ conv[4] = (usf >> 1) & 1;
+ conv[5] = (usf >> 2) & 1;
+ if (usf_p)
+ *usf_p = usf;
+
+ rv = osmo_crc16gen_check_bits(&gsm0503_cs234_crc16,
+ conv + 3, 271, conv + 3 + 271);
+ if (rv)
+ return -1;
+
+ osmo_ubit2pbit_ext(l2_data, 0, conv, 3, 271, 1);
+
+ return 34;
+ case 3:
+ for (i = 675, j = 455; i >= 0; i--) {
+ if (!gsm0503_puncture_cs3[i])
+ cB[i] = cB[j--];
+ else
+ cB[i] = 0;
+ }
+
+ osmo_conv_decode_ber(&gsm0503_cs3_np, cB,
+ conv, n_errors, n_bits_total);
+
+ for (i = 0; i < 8; i++) {
+ for (j = 0, k = 0; j < 6; j++)
+ k += abs(((int)gsm0503_usf2six[i][j]) - ((int)conv[j]));
+
+ if (i == 0 || k < best) {
+ best = k;
+ usf = i;
+ }
+ }
+
+ conv[3] = usf & 1;
+ conv[4] = (usf >> 1) & 1;
+ conv[5] = (usf >> 2) & 1;
+ if (usf_p)
+ *usf_p = usf;
+
+ rv = osmo_crc16gen_check_bits(&gsm0503_cs234_crc16,
+ conv + 3, 315, conv + 3 + 315);
+ if (rv)
+ return -1;
+
+ osmo_ubit2pbit_ext(l2_data, 0, conv, 3, 315, 1);
+
+ return 40;
+ case 4:
+ for (i = 12; i < 456; i++)
+ conv[i] = (cB[i] < 0) ? 1 : 0;
+
+ for (i = 0; i < 8; i++) {
+ for (j = 0, k = 0; j < 12; j++)
+ k += abs(((int)gsm0503_usf2twelve_sbit[i][j]) - ((int)cB[j]));
+
+ if (i == 0 || k < best) {
+ best = k;
+ usf = i;
+ }
+ }
+
+ conv[9] = usf & 1;
+ conv[10] = (usf >> 1) & 1;
+ conv[11] = (usf >> 2) & 1;
+ if (usf_p)
+ *usf_p = usf;
+
+ rv = osmo_crc16gen_check_bits(&gsm0503_cs234_crc16,
+ conv + 9, 431, conv + 9 + 431);
+ if (rv) {
+ *n_bits_total = 456 - 12;
+ *n_errors = *n_bits_total;
+ return -1;
+ }
+
+ *n_bits_total = 456 - 12;
+ *n_errors = 0;
+
+ osmo_ubit2pbit_ext(l2_data, 0, conv, 9, 431, 1);
+
+ return 54;
+ default:
+ *n_bits_total = 0;
+ *n_errors = 0;
+ break;
+ }
+
+ return -1;
+}
+*/
+
+/*! GPRS DL message encoding
+ * \param[out] bursts caller-allocated buffer for unpacked burst bits
+ * \param[in] l2_data L2 (MAC) block to be encoded
+ * \param[in] l2_len length of l2_data in bytes, used to determine CS
+ * \returns 0 on success; negative on error */
+int gsm0503_pdtch_encode(ubit_t *bursts, const uint8_t *l2_data, uint8_t l2_len)
+{
+ ubit_t iB[456], cB[676];
+ const ubit_t *hl_hn;
+ ubit_t conv[334];
+ int i, j, usf;
+
+ switch (l2_len) {
+ case 23:
+ osmo_pbit2ubit_ext(conv, 0, l2_data, 0, 184, 1);
+
+ osmo_crc64gen_set_bits(&gsm0503_fire_crc40, conv, 184, conv + 184);
+
+ osmo_conv_encode(&gsm0503_xcch, conv, cB);
+
+ hl_hn = gsm0503_pdtch_hl_hn_ubit[0];
+
+ break;
+ /*case 34:
+ osmo_pbit2ubit_ext(conv, 3, l2_data, 0, 271, 1);
+ usf = l2_data[0] & 0x7;
+
+ osmo_crc16gen_set_bits(&gsm0503_cs234_crc16, conv + 3,
+ 271, conv + 3 + 271);
+
+ memcpy(conv, gsm0503_usf2six[usf], 6);
+
+ osmo_conv_encode(&gsm0503_cs2_np, conv, cB);
+
+ for (i = 0, j = 0; i < 588; i++)
+ if (!gsm0503_puncture_cs2[i])
+ cB[j++] = cB[i];
+
+ hl_hn = gsm0503_pdtch_hl_hn_ubit[1];
+
+ break;
+ case 40:
+ osmo_pbit2ubit_ext(conv, 3, l2_data, 0, 315, 1);
+ usf = l2_data[0] & 0x7;
+
+ osmo_crc16gen_set_bits(&gsm0503_cs234_crc16, conv + 3,
+ 315, conv + 3 + 315);
+
+ memcpy(conv, gsm0503_usf2six[usf], 6);
+
+ osmo_conv_encode(&gsm0503_cs3_np, conv, cB);
+
+ for (i = 0, j = 0; i < 676; i++)
+ if (!gsm0503_puncture_cs3[i])
+ cB[j++] = cB[i];
+
+ hl_hn = gsm0503_pdtch_hl_hn_ubit[2];
+
+ break;*/
+ case 54:
+ osmo_pbit2ubit_ext(cB, 9, l2_data, 0, 431, 1);
+ usf = l2_data[0] & 0x7;
+
+ osmo_crc16gen_set_bits(&gsm0503_cs234_crc16, cB + 9,
+ 431, cB + 9 + 431);
+
+ memcpy(cB, gsm0503_usf2twelve_ubit[usf], 12);
+
+ hl_hn = gsm0503_pdtch_hl_hn_ubit[3];
+
+ break;
+ default:
+ return -1;
+ }
+
+ gsm0503_xcch_interleave(cB, iB);
+
+ for (i = 0; i < 4; i++) {
+ gsm0503_xcch_burst_map(&iB[i * 114], &bursts[i * 116],
+ hl_hn + i * 2, hl_hn + i * 2 + 1);
+ }
+
+ return GSM0503_GPRS_BURSTS_NBITS;
+}
+
+/*
+ * GSM TCH/F FR/EFR transcoding
+ */
+
+/*! assemble a FR codec frame in format as used inside RTP
+ * \param[out] tch_data Codec frame in RTP format
+ * \param[in] b_bits Codec frame in 'native' format
+ * \param[in] net_order FIXME */
+static void tch_fr_reassemble(uint8_t *tch_data,
+ const ubit_t *b_bits, int net_order)
+{
+ int i, j, k, l, o;
+
+ tch_data[0] = 0xd << 4;
+ memset(tch_data + 1, 0, 32);
+
+ if (net_order) {
+ for (i = 0, j = 4; i < 260; i++, j++)
+ tch_data[j >> 3] |= (b_bits[i] << (7 - (j & 7)));
+
+ return;
+ }
+
+ /* reassemble d-bits */
+ i = 0; /* counts bits */
+ j = 4; /* counts output bits */
+ k = gsm0503_gsm_fr_map[0]-1; /* current number bit in element */
+ l = 0; /* counts element bits */
+ o = 0; /* offset input bits */
+ while (i < 260) {
+ tch_data[j >> 3] |= (b_bits[k + o] << (7 - (j & 7)));
+ if (--k < 0) {
+ o += gsm0503_gsm_fr_map[l];
+ k = gsm0503_gsm_fr_map[++l]-1;
+ }
+ i++;
+ j++;
+ }
+}
+
+static void tch_fr_disassemble(ubit_t *b_bits,
+ const uint8_t *tch_data, int net_order)
+{
+ int i, j, k, l, o;
+
+ if (net_order) {
+ for (i = 0, j = 4; i < 260; i++, j++)
+ b_bits[i] = (tch_data[j >> 3] >> (7 - (j & 7))) & 1;
+
+ return;
+ }
+
+ i = 0; /* counts bits */
+ j = 4; /* counts input bits */
+ k = gsm0503_gsm_fr_map[0] - 1; /* current number bit in element */
+ l = 0; /* counts element bits */
+ o = 0; /* offset output bits */
+ while (i < 260) {
+ b_bits[k + o] = (tch_data[j >> 3] >> (7 - (j & 7))) & 1;
+ if (--k < 0) {
+ o += gsm0503_gsm_fr_map[l];
+ k = gsm0503_gsm_fr_map[++l] - 1;
+ }
+ i++;
+ j++;
+ }
+}
+
+/* assemble a HR codec frame in format as used inside RTP */
+static void tch_hr_reassemble(uint8_t *tch_data, const ubit_t *b_bits)
+{
+ int i, j;
+
+ tch_data[0] = 0x00; /* F = 0, FT = 000 */
+ memset(tch_data + 1, 0, 14);
+
+ for (i = 0, j = 8; i < 112; i++, j++)
+ tch_data[j >> 3] |= (b_bits[i] << (7 - (j & 7)));
+}
+
+static void tch_hr_disassemble(ubit_t *b_bits, const uint8_t *tch_data)
+{
+ int i, j;
+
+ for (i = 0, j = 8; i < 112; i++, j++)
+ b_bits[i] = (tch_data[j >> 3] >> (7 - (j & 7))) & 1;
+}
+
+/* assemble a EFR codec frame in format as used inside RTP */
+static void tch_efr_reassemble(uint8_t *tch_data, const ubit_t *b_bits)
+{
+ int i, j;
+
+ tch_data[0] = 0xc << 4;
+ memset(tch_data + 1, 0, 30);
+
+ for (i = 0, j = 4; i < 244; i++, j++)
+ tch_data[j >> 3] |= (b_bits[i] << (7 - (j & 7)));
+}
+
+static void tch_efr_disassemble(ubit_t *b_bits, const uint8_t *tch_data)
+{
+ int i, j;
+
+ for (i = 0, j = 4; i < 244; i++, j++)
+ b_bits[i] = (tch_data[j >> 3] >> (7 - (j & 7))) & 1;
+}
+
+/* assemble a AMR codec frame in format as used inside RTP */
+static void tch_amr_reassemble(uint8_t *tch_data, const ubit_t *d_bits, int len)
+{
+ int i, j;
+
+ memset(tch_data, 0, (len + 7) >> 3);
+
+ for (i = 0, j = 0; i < len; i++, j++)
+ tch_data[j >> 3] |= (d_bits[i] << (7 - (j & 7)));
+}
+
+static void tch_amr_disassemble(ubit_t *d_bits, const uint8_t *tch_data, int len)
+{
+ int i, j;
+
+ for (i = 0, j = 0; i < len; i++, j++)
+ d_bits[i] = (tch_data[j >> 3] >> (7 - (j & 7))) & 1;
+}
+
+/* re-arrange according to TS 05.03 Table 2 (receiver) */
+static void tch_fr_d_to_b(ubit_t *b_bits, const ubit_t *d_bits)
+{
+ int i;
+
+ for (i = 0; i < 260; i++)
+ b_bits[gsm610_bitorder[i]] = d_bits[i];
+}
+
+/* re-arrange according to TS 05.03 Table 2 (transmitter) */
+static void tch_fr_b_to_d(ubit_t *d_bits, const ubit_t *b_bits)
+{
+ int i;
+
+ for (i = 0; i < 260; i++)
+ d_bits[i] = b_bits[gsm610_bitorder[i]];
+}
+
+/* re-arrange according to TS 05.03 Table 3a (receiver) */
+static void tch_hr_d_to_b(ubit_t *b_bits, const ubit_t *d_bits)
+{
+ int i;
+
+ const uint16_t *map;
+
+ if (!d_bits[93] && !d_bits[94])
+ map = gsm620_unvoiced_bitorder;
+ else
+ map = gsm620_voiced_bitorder;
+
+ for (i = 0; i < 112; i++)
+ b_bits[map[i]] = d_bits[i];
+}
+
+/* re-arrange according to TS 05.03 Table 3a (transmitter) */
+static void tch_hr_b_to_d(ubit_t *d_bits, const ubit_t *b_bits)
+{
+ int i;
+ const uint16_t *map;
+
+ if (!b_bits[34] && !b_bits[35])
+ map = gsm620_unvoiced_bitorder;
+ else
+ map = gsm620_voiced_bitorder;
+
+ for (i = 0; i < 112; i++)
+ d_bits[i] = b_bits[map[i]];
+}
+
+/* re-arrange according to TS 05.03 Table 6 (receiver) */
+static void tch_efr_d_to_w(ubit_t *b_bits, const ubit_t *d_bits)
+{
+ int i;
+
+ for (i = 0; i < 260; i++)
+ b_bits[gsm660_bitorder[i]] = d_bits[i];
+}
+
+/* re-arrange according to TS 05.03 Table 6 (transmitter) */
+static void tch_efr_w_to_d(ubit_t *d_bits, const ubit_t *b_bits)
+{
+ int i;
+
+ for (i = 0; i < 260; i++)
+ d_bits[i] = b_bits[gsm660_bitorder[i]];
+}
+
+/* extract the 65 protected class1a+1b bits */
+static void tch_efr_protected(const ubit_t *s_bits, ubit_t *b_bits)
+{
+ int i;
+
+ for (i = 0; i < 65; i++)
+ b_bits[i] = s_bits[gsm0503_gsm_efr_protected_bits[i] - 1];
+}
+
+static void tch_fr_unreorder(ubit_t *d, ubit_t *p, const ubit_t *u)
+{
+ int i;
+
+ for (i = 0; i < 91; i++) {
+ d[i << 1] = u[i];
+ d[(i << 1) + 1] = u[184 - i];
+ }
+
+ for (i = 0; i < 3; i++)
+ p[i] = u[91 + i];
+}
+
+static void tch_fr_reorder(ubit_t *u, const ubit_t *d, const ubit_t *p)
+{
+ int i;
+
+ for (i = 0; i < 91; i++) {
+ u[i] = d[i << 1];
+ u[184 - i] = d[(i << 1) + 1];
+ }
+
+ for (i = 0; i < 3; i++)
+ u[91 + i] = p[i];
+}
+
+static void tch_hr_unreorder(ubit_t *d, ubit_t *p, const ubit_t *u)
+{
+ memcpy(d, u, 95);
+ memcpy(p, u + 95, 3);
+}
+
+static void tch_hr_reorder(ubit_t *u, const ubit_t *d, const ubit_t *p)
+{
+ memcpy(u, d, 95);
+ memcpy(u + 95, p, 3);
+}
+
+static void tch_efr_reorder(ubit_t *w, const ubit_t *s, const ubit_t *p)
+{
+ memcpy(w, s, 71);
+ w[71] = w[72] = s[69];
+ memcpy(w + 73, s + 71, 50);
+ w[123] = w[124] = s[119];
+ memcpy(w + 125, s + 121, 53);
+ w[178] = w[179] = s[172];
+ memcpy(w + 180, s + 174, 50);
+ w[230] = w[231] = s[222];
+ memcpy(w + 232, s + 224, 20);
+ memcpy(w + 252, p, 8);
+}
+
+static void tch_efr_unreorder(ubit_t *s, ubit_t *p, const ubit_t *w)
+{
+ int sum;
+
+ memcpy(s, w, 71);
+ sum = s[69] + w[71] + w[72];
+ s[69] = (sum >= 2);
+ memcpy(s + 71, w + 73, 50);
+ sum = s[119] + w[123] + w[124];
+ s[119] = (sum >= 2);
+ memcpy(s + 121, w + 125, 53);
+ sum = s[172] + w[178] + w[179];
+ s[172] = (sum > 2);
+ memcpy(s + 174, w + 180, 50);
+ sum = s[222] + w[230] + w[231];
+ s[222] = (sum >= 2);
+ memcpy(s + 224, w + 232, 20);
+ memcpy(p, w + 252, 8);
+}
+
+static void tch_amr_merge(ubit_t *u, const ubit_t *d, const ubit_t *p, int len, int prot)
+{
+ memcpy(u, d, prot);
+ memcpy(u + prot, p, 6);
+ memcpy(u + prot + 6, d + prot, len - prot);
+}
+
+static void tch_amr_unmerge(ubit_t *d, ubit_t *p, const ubit_t *u, int len, int prot)
+{
+ memcpy(d, u, prot);
+ memcpy(p, u + prot, 6);
+ memcpy(d + prot, u + prot + 6, len - prot);
+}
+
+/*! Perform channel decoding of a FR/EFR channel according TS 05.03
+ * \param[out] tch_data Codec frame in RTP payload format
+ * \param[in] bursts buffer containing the symbols of 8 bursts
+ * \param[in] net_order FIXME
+ * \param[in] efr Is this channel using EFR (1) or FR (0)
+ * \param[out] n_errors Number of detected bit errors
+ * \param[out] n_bits_total Total number of bits
+ * \returns length of bytes used in \a tch_data output buffer */
+int gsm0503_tch_fr_decode(uint8_t *tch_data, const sbit_t *bursts,
+ int net_order, int efr, int *n_errors, int *n_bits_total)
+{
+ sbit_t iB[912], cB[456], h;
+ ubit_t conv[185], s[244], w[260], b[65], d[260], p[8];
+ int i, rv, len, steal = 0;
+
+ /* map from 8 bursts to interleaved data bits (iB) */
+ for (i = 0; i < 8; i++) {
+ gsm0503_tch_burst_unmap(&iB[i * 114],
+ &bursts[i * 116], &h, i >> 2);
+ steal -= h;
+ }
+ /* we now have the bits of the four bursts (interface 4 in
+ * Figure 1a of TS 05.03 */
+
+ gsm0503_tch_fr_deinterleave(cB, iB);
+ /* we now have the coded bits c(B): interface 3 in Fig. 1a */
+
+ if (steal > 0) {
+ rv = _xcch_decode_cB(tch_data, cB, n_errors, n_bits_total);
+ if (rv) {
+ /* Error decoding FACCH frame */
+ return -1;
+ }
+
+ return 23;
+ }
+
+ osmo_conv_decode_ber(&gsm0503_tch_fr, cB, conv, n_errors, n_bits_total);
+ /* we now have the data bits 'u': interface 2 in Fig. 1a */
+
+ /* input: 'conv', output: d[ata] + p[arity] */
+ tch_fr_unreorder(d, p, conv);
+
+ for (i = 0; i < 78; i++)
+ d[i + 182] = (cB[i + 378] < 0) ? 1 : 0;
+
+ /* check if parity of first 50 (class 1) 'd'-bits match 'p' */
+ rv = osmo_crc8gen_check_bits(&gsm0503_tch_fr_crc3, d, 50, p);
+ if (rv) {
+ /* Error checking CRC8 for the FR part of an EFR/FR frame */
+ return -1;
+ }
+
+ if (efr) {
+ tch_efr_d_to_w(w, d);
+ /* we now have the preliminary-coded bits w(k) */
+
+ tch_efr_unreorder(s, p, w);
+ /* we now have the data delivered to the preliminary
+ * channel encoding unit s(k) */
+
+ /* extract the 65 most important bits according TS 05.03 3.1.1.1 */
+ tch_efr_protected(s, b);
+
+ /* perform CRC-8 on 65 most important bits (50 bits of
+ * class 1a + 15 bits of class 1b) */
+ rv = osmo_crc8gen_check_bits(&gsm0503_tch_efr_crc8, b, 65, p);
+ if (rv) {
+ /* Error checking CRC8 for the EFR part of an EFR frame */
+ return -1;
+ }
+
+ tch_efr_reassemble(tch_data, s);
+
+ len = GSM_EFR_BYTES;
+ } else {
+ tch_fr_d_to_b(w, d);
+
+ tch_fr_reassemble(tch_data, w, net_order);
+
+ len = GSM_FR_BYTES;
+ }
+
+ return len;
+}
+
+/*! Perform channel encoding on a TCH/FS channel according to TS 05.03
+ * \param[out] bursts caller-allocated output buffer for bursts bits
+ * \param[in] tch_data Codec input data in RTP payload format
+ * \param[in] len Length of \a tch_data in bytes
+ * \param[in] net_order FIXME
+ * \returns 0 in case of success; negative on error */
+int gsm0503_tch_fr_encode(ubit_t *bursts, const uint8_t *tch_data,
+ int len, int net_order)
+{
+ ubit_t iB[912], cB[456], h;
+ ubit_t conv[185], w[260], b[65], s[244], d[260], p[8];
+ int i;
+
+ switch (len) {
+ case GSM_EFR_BYTES: /* TCH EFR */
+
+ tch_efr_disassemble(s, tch_data);
+
+ tch_efr_protected(s, b);
+
+ osmo_crc8gen_set_bits(&gsm0503_tch_efr_crc8, b, 65, p);
+
+ tch_efr_reorder(w, s, p);
+
+ tch_efr_w_to_d(d, w);
+
+ goto coding_efr_fr;
+ case GSM_FR_BYTES: /* TCH FR */
+ tch_fr_disassemble(w, tch_data, net_order);
+
+ tch_fr_b_to_d(d, w);
+
+coding_efr_fr:
+ osmo_crc8gen_set_bits(&gsm0503_tch_fr_crc3, d, 50, p);
+
+ tch_fr_reorder(conv, d, p);
+
+ memcpy(cB + 378, d + 182, 78);
+
+ osmo_conv_encode(&gsm0503_tch_fr, conv, cB);
+
+ h = 0;
+
+ break;
+ case GSM_MACBLOCK_LEN: /* FACCH */
+ _xcch_encode_cB(cB, tch_data);
+
+ h = 1;
+
+ break;
+ default:
+ return -1;
+ }
+
+ gsm0503_tch_fr_interleave(cB, iB);
+
+ for (i = 0; i < 8; i++) {
+ gsm0503_tch_burst_map(&iB[i * 114],
+ &bursts[i * 116], &h, i >> 2);
+ }
+
+ return 0;
+}
+
+/*! Perform channel decoding of a HR(v1) channel according TS 05.03
+ * \param[out] tch_data Codec frame in RTP payload format
+ * \param[in] bursts buffer containing the symbols of 8 bursts
+ * \param[in] odd Odd (1) or even (0) frame number
+ * \param[out] n_errors Number of detected bit errors
+ * \param[out] n_bits_total Total number of bits
+ * \returns length of bytes used in \a tch_data output buffer */
+int gsm0503_tch_hr_decode(uint8_t *tch_data, const sbit_t *bursts, int odd,
+ int *n_errors, int *n_bits_total)
+{
+ sbit_t iB[912], cB[456], h;
+ ubit_t conv[98], b[112], d[112], p[3];
+ int i, rv, steal = 0;
+
+ /* Only unmap the stealing bits */
+ if (!odd) {
+ for (i = 0; i < 4; i++) {
+ gsm0503_tch_burst_unmap(NULL, &bursts[i * 116], &h, 0);
+ steal -= h;
+ }
+
+ for (i = 2; i < 5; i++) {
+ gsm0503_tch_burst_unmap(NULL, &bursts[i * 116], &h, 1);
+ steal -= h;
+ }
+ }
+
+ /* If we found a stole FACCH, but only at correct alignment */
+ if (steal > 0) {
+ for (i = 0; i < 6; i++) {
+ gsm0503_tch_burst_unmap(&iB[i * 114],
+ &bursts[i * 116], NULL, i >> 2);
+ }
+
+ for (i = 2; i < 4; i++) {
+ gsm0503_tch_burst_unmap(&iB[i * 114 + 456],
+ &bursts[i * 116], NULL, 1);
+ }
+
+ gsm0503_tch_fr_deinterleave(cB, iB);
+
+ rv = _xcch_decode_cB(tch_data, cB, n_errors, n_bits_total);
+ if (rv) {
+ /* Error decoding FACCH frame */
+ return -1;
+ }
+
+ return GSM_MACBLOCK_LEN;
+ }
+
+ for (i = 0; i < 4; i++) {
+ gsm0503_tch_burst_unmap(&iB[i * 114],
+ &bursts[i * 116], NULL, i >> 1);
+ }
+
+ gsm0503_tch_hr_deinterleave(cB, iB);
+
+ osmo_conv_decode_ber(&gsm0503_tch_hr, cB, conv, n_errors, n_bits_total);
+
+ tch_hr_unreorder(d, p, conv);
+
+ for (i = 0; i < 17; i++)
+ d[i + 95] = (cB[i + 211] < 0) ? 1 : 0;
+
+ rv = osmo_crc8gen_check_bits(&gsm0503_tch_fr_crc3, d + 73, 22, p);
+ if (rv) {
+ /* Error checking CRC8 for an HR frame */
+ return -1;
+ }
+
+ tch_hr_d_to_b(b, d);
+
+ tch_hr_reassemble(tch_data, b);
+
+ return 15;
+}
+
+/*! Perform channel encoding on a TCH/HS channel according to TS 05.03
+ * \param[out] bursts caller-allocated output buffer for bursts bits
+ * \param[in] tch_data Codec input data in RTP payload format
+ * \param[in] len Length of \a tch_data in bytes
+ * \returns 0 in case of success; negative on error */
+int gsm0503_tch_hr_encode(ubit_t *bursts, const uint8_t *tch_data, int len)
+{
+ ubit_t iB[912], cB[456], h;
+ ubit_t conv[98], b[112], d[112], p[3];
+ int i;
+
+ switch (len) {
+ case 15: /* TCH HR */
+ tch_hr_disassemble(b, tch_data);
+
+ tch_hr_b_to_d(d, b);
+
+ osmo_crc8gen_set_bits(&gsm0503_tch_fr_crc3, d + 73, 22, p);
+
+ tch_hr_reorder(conv, d, p);
+
+ osmo_conv_encode(&gsm0503_tch_hr, conv, cB);
+
+ memcpy(cB + 211, d + 95, 17);
+
+ h = 0;
+
+ gsm0503_tch_hr_interleave(cB, iB);
+
+ for (i = 0; i < 4; i++) {
+ gsm0503_tch_burst_map(&iB[i * 114],
+ &bursts[i * 116], &h, i >> 1);
+ }
+
+ break;
+ case GSM_MACBLOCK_LEN: /* FACCH */
+ _xcch_encode_cB(cB, tch_data);
+
+ h = 1;
+
+ gsm0503_tch_fr_interleave(cB, iB);
+
+ for (i = 0; i < 6; i++) {
+ gsm0503_tch_burst_map(&iB[i * 114],
+ &bursts[i * 116], &h, i >> 2);
+ }
+
+ for (i = 2; i < 4; i++) {
+ gsm0503_tch_burst_map(&iB[i * 114 + 456],
+ &bursts[i * 116], &h, 1);
+ }
+
+ break;
+ default:
+ return -1;
+ }
+
+ return 0;
+}
+
+/*! Perform channel decoding of a TCH/AFS channel according TS 05.03
+ * \param[out] tch_data Codec frame in RTP payload format
+ * \param[in] bursts buffer containing the symbols of 8 bursts
+ * \param[in] codec_mode_req is this CMR (1) or CMC (0)
+ * \param[in] codec array of active codecs (active codec set)
+ * \param[in] codecs number of codecs in \a codec
+ * \param ft Frame Type; Input if \a codec_mode_req = 1, Output * otherwise
+ * \param[out] cmr Output in \a codec_mode_req = 1
+ * \param[out] n_errors Number of detected bit errors
+ * \param[out] n_bits_total Total number of bits
+ * \returns length of bytes used in \a tch_data output buffer */
+int gsm0503_tch_afs_decode(uint8_t *tch_data, const sbit_t *bursts,
+ int codec_mode_req, uint8_t *codec, int codecs, uint8_t *ft,
+ uint8_t *cmr, int *n_errors, int *n_bits_total)
+{
+ sbit_t iB[912], cB[456], h;
+ ubit_t d[244], p[6], conv[250];
+ int i, j, k, best = 0, rv, len, steal = 0, id = 0;
+ *n_errors = 0; *n_bits_total = 0;
+
+ for (i=0; i<8; i++) {
+ gsm0503_tch_burst_unmap(&iB[i * 114], &bursts[i * 116], &h, i >> 2);
+ steal -= h;
+ }
+
+ gsm0503_tch_fr_deinterleave(cB, iB);
+
+ if (steal > 0) {
+ rv = _xcch_decode_cB(tch_data, cB, n_errors, n_bits_total);
+ if (rv) {
+ /* Error decoding FACCH frame */
+ return -1;
+ }
+
+ return GSM_MACBLOCK_LEN;
+ }
+
+ for (i = 0; i < 4; i++) {
+ for (j = 0, k = 0; j < 8; j++)
+ k += abs(((int)gsm0503_afs_ic_sbit[i][j]) - ((int)cB[j]));
+
+ if (i == 0 || k < best) {
+ best = k;
+ id = i;
+ }
+ }
+
+ /* Check if indicated codec fits into range of codecs */
+ if (id >= codecs) {
+ /* Codec mode out of range, return id */
+ return id;
+ }
+
+ switch ((codec_mode_req) ? codec[*ft] : codec[id]) {
+ case 7: /* TCH/AFS12.2 */
+ osmo_conv_decode_ber(&gsm0503_tch_afs_12_2, cB + 8,
+ conv, n_errors, n_bits_total);
+
+ tch_amr_unmerge(d, p, conv, 244, 81);
+
+ rv = osmo_crc8gen_check_bits(&gsm0503_amr_crc6, d, 81, p);
+ if (rv) {
+ /* Error checking CRC8 for an AMR 12.2 frame */
+ return -1;
+ }
+
+ tch_amr_reassemble(tch_data, d, 244);
+
+ len = 31;
+
+ break;
+ case 6: /* TCH/AFS10.2 */
+ osmo_conv_decode_ber(&gsm0503_tch_afs_10_2, cB + 8,
+ conv, n_errors, n_bits_total);
+
+ tch_amr_unmerge(d, p, conv, 204, 65);
+
+ rv = osmo_crc8gen_check_bits(&gsm0503_amr_crc6, d, 65, p);
+ if (rv) {
+ /* Error checking CRC8 for an AMR 10.2 frame */
+ return -1;
+ }
+
+ tch_amr_reassemble(tch_data, d, 204);
+
+ len = 26;
+
+ break;
+ case 5: /* TCH/AFS7.95 */
+ osmo_conv_decode_ber(&gsm0503_tch_afs_7_95, cB + 8,
+ conv, n_errors, n_bits_total);
+
+ tch_amr_unmerge(d, p, conv, 159, 75);
+
+ rv = osmo_crc8gen_check_bits(&gsm0503_amr_crc6, d, 75, p);
+ if (rv) {
+ /* Error checking CRC8 for an AMR 7.95 frame */
+ return -1;
+ }
+
+ tch_amr_reassemble(tch_data, d, 159);
+
+ len = 20;
+
+ break;
+ case 4: /* TCH/AFS7.4 */
+ osmo_conv_decode_ber(&gsm0503_tch_afs_7_4, cB + 8,
+ conv, n_errors, n_bits_total);
+
+ tch_amr_unmerge(d, p, conv, 148, 61);
+
+ rv = osmo_crc8gen_check_bits(&gsm0503_amr_crc6, d, 61, p);
+ if (rv) {
+ /* Error checking CRC8 for an AMR 7.4 frame */
+ return -1;
+ }
+
+ tch_amr_reassemble(tch_data, d, 148);
+
+ len = 19;
+
+ break;
+ case 3: /* TCH/AFS6.7 */
+ osmo_conv_decode_ber(&gsm0503_tch_afs_6_7, cB + 8,
+ conv, n_errors, n_bits_total);
+
+ tch_amr_unmerge(d, p, conv, 134, 55);
+
+ rv = osmo_crc8gen_check_bits(&gsm0503_amr_crc6, d, 55, p);
+ if (rv) {
+ /* Error checking CRC8 for an AMR 6.7 frame */
+ return -1;
+ }
+
+ tch_amr_reassemble(tch_data, d, 134);
+
+ len = 17;
+
+ break;
+ case 2: /* TCH/AFS5.9 */
+ osmo_conv_decode_ber(&gsm0503_tch_afs_5_9, cB + 8,
+ conv, n_errors, n_bits_total);
+
+ tch_amr_unmerge(d, p, conv, 118, 55);
+
+ rv = osmo_crc8gen_check_bits(&gsm0503_amr_crc6, d, 55, p);
+ if (rv) {
+ /* Error checking CRC8 for an AMR 5.9 frame */
+ return -1;
+ }
+
+ tch_amr_reassemble(tch_data, d, 118);
+
+ len = 15;
+
+ break;
+ case 1: /* TCH/AFS5.15 */
+ osmo_conv_decode_ber(&gsm0503_tch_afs_5_15, cB + 8,
+ conv, n_errors, n_bits_total);
+
+ tch_amr_unmerge(d, p, conv, 103, 49);
+
+ rv = osmo_crc8gen_check_bits(&gsm0503_amr_crc6, d, 49, p);
+ if (rv) {
+ /* Error checking CRC8 for an AMR 5.15 frame */
+ return -1;
+ }
+
+ tch_amr_reassemble(tch_data, d, 103);
+
+ len = 13;
+
+ break;
+ case 0: /* TCH/AFS4.75 */
+ osmo_conv_decode_ber(&gsm0503_tch_afs_4_75, cB + 8,
+ conv, n_errors, n_bits_total);
+
+ tch_amr_unmerge(d, p, conv, 95, 39);
+
+ rv = osmo_crc8gen_check_bits(&gsm0503_amr_crc6, d, 39, p);
+ if (rv) {
+ /* Error checking CRC8 for an AMR 4.75 frame */
+ return -1;
+ }
+
+ tch_amr_reassemble(tch_data, d, 95);
+
+ len = 12;
+
+ break;
+ default:
+ /* Unknown frame type */
+ *n_bits_total = 448;
+ *n_errors = *n_bits_total;
+ return -1;
+ }
+
+ /* Change codec request / indication, if frame is valid */
+ if (codec_mode_req)
+ *cmr = id;
+ else
+ *ft = id;
+
+ return len;
+}
+
+/*! Perform channel encoding on a TCH/AFS channel according to TS 05.03
+ * \param[out] bursts caller-allocated output buffer for bursts bits
+ * \param[in] tch_data Codec input data in RTP payload format
+ * \param[in] len Length of \a tch_data in bytes
+ * \param[in] codec_mode_req Use CMR (1) or FT (0)
+ * \param[in] codec Array of codecs (active codec set)
+ * \param[in] codecs Number of entries in \a codec
+ * \param[in] ft Frame Type to be used for encoding (index to \a codec)
+ * \param[in] cmr Codec Mode Request (used in codec_mode_req = 1 only)
+ * \returns 0 in case of success; negative on error */
+int gsm0503_tch_afs_encode(ubit_t *bursts, const uint8_t *tch_data, int len,
+ int codec_mode_req, uint8_t *codec, int codecs, uint8_t ft,
+ uint8_t cmr)
+{
+ ubit_t iB[912], cB[456], h;
+ ubit_t d[244], p[6], conv[250];
+ int i;
+ uint8_t id;
+
+ if (len == GSM_MACBLOCK_LEN) { /* FACCH */
+ _xcch_encode_cB(cB, tch_data);
+
+ h = 1;
+
+ goto facch;
+ }
+
+ h = 0;
+
+ if (codec_mode_req) {
+ if (cmr >= codecs) {
+ /* FIXME: CMR ID is not in codec list! */
+ return -1;
+ }
+ id = cmr;
+ } else {
+ if (ft >= codecs) {
+ /* FIXME: FT ID is not in codec list! */
+ return -1;
+ }
+ id = ft;
+ }
+
+ switch (codec[ft]) {
+ case 7: /* TCH/AFS12.2 */
+ if (len != 31)
+ goto invalid_length;
+
+ tch_amr_disassemble(d, tch_data, 244);
+
+ osmo_crc8gen_set_bits(&gsm0503_amr_crc6, d, 81, p);
+
+ tch_amr_merge(conv, d, p, 244, 81);
+
+ osmo_conv_encode(&gsm0503_tch_afs_12_2, conv, cB + 8);
+
+ break;
+ case 6: /* TCH/AFS10.2 */
+ if (len != 26)
+ goto invalid_length;
+
+ tch_amr_disassemble(d, tch_data, 204);
+
+ osmo_crc8gen_set_bits(&gsm0503_amr_crc6, d, 65, p);
+
+ tch_amr_merge(conv, d, p, 204, 65);
+
+ osmo_conv_encode(&gsm0503_tch_afs_10_2, conv, cB + 8);
+
+ break;
+ case 5: /* TCH/AFS7.95 */
+ if (len != 20)
+ goto invalid_length;
+
+ tch_amr_disassemble(d, tch_data, 159);
+
+ osmo_crc8gen_set_bits(&gsm0503_amr_crc6, d, 75, p);
+
+ tch_amr_merge(conv, d, p, 159, 75);
+
+ osmo_conv_encode(&gsm0503_tch_afs_7_95, conv, cB + 8);
+
+ break;
+ case 4: /* TCH/AFS7.4 */
+ if (len != 19)
+ goto invalid_length;
+
+ tch_amr_disassemble(d, tch_data, 148);
+
+ osmo_crc8gen_set_bits(&gsm0503_amr_crc6, d, 61, p);
+
+ tch_amr_merge(conv, d, p, 148, 61);
+
+ osmo_conv_encode(&gsm0503_tch_afs_7_4, conv, cB + 8);
+
+ break;
+ case 3: /* TCH/AFS6.7 */
+ if (len != 17)
+ goto invalid_length;
+
+ tch_amr_disassemble(d, tch_data, 134);
+
+ osmo_crc8gen_set_bits(&gsm0503_amr_crc6, d, 55, p);
+
+ tch_amr_merge(conv, d, p, 134, 55);
+
+ osmo_conv_encode(&gsm0503_tch_afs_6_7, conv, cB + 8);
+
+ break;
+ case 2: /* TCH/AFS5.9 */
+ if (len != 15)
+ goto invalid_length;
+
+ tch_amr_disassemble(d, tch_data, 118);
+
+ osmo_crc8gen_set_bits(&gsm0503_amr_crc6, d, 55, p);
+
+ tch_amr_merge(conv, d, p, 118, 55);
+
+ osmo_conv_encode(&gsm0503_tch_afs_5_9, conv, cB + 8);
+
+ break;
+ case 1: /* TCH/AFS5.15 */
+ if (len != 13)
+ goto invalid_length;
+
+ tch_amr_disassemble(d, tch_data, 103);
+
+ osmo_crc8gen_set_bits(&gsm0503_amr_crc6, d, 49, p);
+
+ tch_amr_merge(conv, d, p, 103, 49);
+
+ osmo_conv_encode(&gsm0503_tch_afs_5_15, conv, cB + 8);
+
+ break;
+ case 0: /* TCH/AFS4.75 */
+ if (len != 12)
+ goto invalid_length;
+
+ tch_amr_disassemble(d, tch_data, 95);
+
+ osmo_crc8gen_set_bits(&gsm0503_amr_crc6, d, 39, p);
+
+ tch_amr_merge(conv, d, p, 95, 39);
+
+ osmo_conv_encode(&gsm0503_tch_afs_4_75, conv, cB + 8);
+
+ break;
+ default:
+ /* FIXME: FT %ft is not supported */
+ return -1;
+ }
+
+ memcpy(cB, gsm0503_afs_ic_ubit[id], 8);
+
+facch:
+ gsm0503_tch_fr_interleave(cB, iB);
+
+ for (i = 0; i < 8; i++) {
+ gsm0503_tch_burst_map(&iB[i * 114],
+ &bursts[i * 116], &h, i >> 2);
+ }
+
+ return 0;
+
+invalid_length:
+ /* FIXME: payload length %len does not comply with codec type %ft */
+ return -1;
+}
+
+/*! Perform channel decoding of a TCH/AFS channel according TS 05.03
+ * \param[out] tch_data Codec frame in RTP payload format
+ * \param[in] bursts buffer containing the symbols of 8 bursts
+ * \param[in] odd Is this an odd (1) or even (0) frame number?
+ * \param[in] codec_mode_req is this CMR (1) or CMC (0)
+ * \param[in] codec array of active codecs (active codec set)
+ * \param[in] codecs number of codecs in \a codec
+ * \param ft Frame Type; Input if \a codec_mode_req = 1, Output * otherwise
+ * \param[out] cmr Output in \a codec_mode_req = 1
+ * \param[out] n_errors Number of detected bit errors
+ * \param[out] n_bits_total Total number of bits
+ * \returns length of bytes used in \a tch_data output buffer */
+int gsm0503_tch_ahs_decode(uint8_t *tch_data, const sbit_t *bursts, int odd,
+ int codec_mode_req, uint8_t *codec, int codecs, uint8_t *ft,
+ uint8_t *cmr, int *n_errors, int *n_bits_total)
+{
+ sbit_t iB[912], cB[456], h;
+ ubit_t d[244], p[6], conv[135];
+ int i, j, k, best = 0, rv, len, steal = 0, id = 0;
+
+ /* only unmap the stealing bits */
+ if (!odd) {
+ for (i = 0; i < 4; i++) {
+ gsm0503_tch_burst_unmap(NULL, &bursts[i * 116], &h, 0);
+ steal -= h;
+ }
+ for (i = 2; i < 5; i++) {
+ gsm0503_tch_burst_unmap(NULL, &bursts[i * 116], &h, 1);
+ steal -= h;
+ }
+ }
+
+ /* if we found a stole FACCH, but only at correct alignment */
+ if (steal > 0) {
+ for (i = 0; i < 6; i++) {
+ gsm0503_tch_burst_unmap(&iB[i * 114],
+ &bursts[i * 116], NULL, i >> 2);
+ }
+
+ for (i = 2; i < 4; i++) {
+ gsm0503_tch_burst_unmap(&iB[i * 114 + 456],
+ &bursts[i * 116], NULL, 1);
+ }
+
+ gsm0503_tch_fr_deinterleave(cB, iB);
+
+ rv = _xcch_decode_cB(tch_data, cB, n_errors, n_bits_total);
+ if (rv) {
+ /* Error decoding FACCH frame */
+ return -1;
+ }
+
+ return GSM_MACBLOCK_LEN;
+ }
+
+ for (i = 0; i < 4; i++) {
+ gsm0503_tch_burst_unmap(&iB[i * 114],
+ &bursts[i * 116], NULL, i >> 1);
+ }
+
+ gsm0503_tch_hr_deinterleave(cB, iB);
+
+ for (i = 0; i < 4; i++) {
+ for (j = 0, k = 0; j < 4; j++)
+ k += abs(((int)gsm0503_ahs_ic_sbit[i][j]) - ((int)cB[j]));
+
+ if (i == 0 || k < best) {
+ best = k;
+ id = i;
+ }
+ }
+
+ /* Check if indicated codec fits into range of codecs */
+ if (id >= codecs) {
+ /* Codec mode out of range, return id */
+ return id;
+ }
+
+ switch ((codec_mode_req) ? codec[*ft] : codec[id]) {
+ case 5: /* TCH/AHS7.95 */
+ osmo_conv_decode_ber(&gsm0503_tch_ahs_7_95, cB + 4,
+ conv, n_errors, n_bits_total);
+
+ tch_amr_unmerge(d, p, conv, 123, 67);
+
+ rv = osmo_crc8gen_check_bits(&gsm0503_amr_crc6, d, 67, p);
+ if (rv) {
+ /* Error checking CRC8 for an AMR 7.95 frame */
+ return -1;
+ }
+
+ for (i = 0; i < 36; i++)
+ d[i + 123] = (cB[i + 192] < 0) ? 1 : 0;
+
+ tch_amr_reassemble(tch_data, d, 159);
+
+ len = 20;
+
+ break;
+ case 4: /* TCH/AHS7.4 */
+ osmo_conv_decode_ber(&gsm0503_tch_ahs_7_4, cB + 4,
+ conv, n_errors, n_bits_total);
+
+ tch_amr_unmerge(d, p, conv, 120, 61);
+
+ rv = osmo_crc8gen_check_bits(&gsm0503_amr_crc6, d, 61, p);
+ if (rv) {
+ /* Error checking CRC8 for an AMR 7.4 frame */
+ return -1;
+ }
+
+ for (i = 0; i < 28; i++)
+ d[i + 120] = (cB[i + 200] < 0) ? 1 : 0;
+
+ tch_amr_reassemble(tch_data, d, 148);
+
+ len = 19;
+
+ break;
+ case 3: /* TCH/AHS6.7 */
+ osmo_conv_decode_ber(&gsm0503_tch_ahs_6_7, cB + 4,
+ conv, n_errors, n_bits_total);
+
+ tch_amr_unmerge(d, p, conv, 110, 55);
+
+ rv = osmo_crc8gen_check_bits(&gsm0503_amr_crc6, d, 55, p);
+ if (rv) {
+ /* Error checking CRC8 for an AMR 6.7 frame */
+ return -1;
+ }
+
+ for (i = 0; i < 24; i++)
+ d[i + 110] = (cB[i + 204] < 0) ? 1 : 0;
+
+ tch_amr_reassemble(tch_data, d, 134);
+
+ len = 17;
+
+ break;
+ case 2: /* TCH/AHS5.9 */
+ osmo_conv_decode_ber(&gsm0503_tch_ahs_5_9, cB + 4,
+ conv, n_errors, n_bits_total);
+
+ tch_amr_unmerge(d, p, conv, 102, 55);
+
+ rv = osmo_crc8gen_check_bits(&gsm0503_amr_crc6, d, 55, p);
+ if (rv) {
+ /* Error checking CRC8 for an AMR 5.9 frame */
+ return -1;
+ }
+
+ for (i = 0; i < 16; i++)
+ d[i + 102] = (cB[i + 212] < 0) ? 1 : 0;
+
+ tch_amr_reassemble(tch_data, d, 118);
+
+ len = 15;
+
+ break;
+ case 1: /* TCH/AHS5.15 */
+ osmo_conv_decode_ber(&gsm0503_tch_ahs_5_15, cB + 4,
+ conv, n_errors, n_bits_total);
+
+ tch_amr_unmerge(d, p, conv, 91, 49);
+
+ rv = osmo_crc8gen_check_bits(&gsm0503_amr_crc6, d, 49, p);
+ if (rv) {
+ /* Error checking CRC8 for an AMR 5.15 frame */
+ return -1;
+ }
+
+ for (i = 0; i < 12; i++)
+ d[i + 91] = (cB[i + 216] < 0) ? 1 : 0;
+
+ tch_amr_reassemble(tch_data, d, 103);
+
+ len = 13;
+
+ break;
+ case 0: /* TCH/AHS4.75 */
+ osmo_conv_decode_ber(&gsm0503_tch_ahs_4_75, cB + 4,
+ conv, n_errors, n_bits_total);
+
+ tch_amr_unmerge(d, p, conv, 83, 39);
+
+ rv = osmo_crc8gen_check_bits(&gsm0503_amr_crc6, d, 39, p);
+ if (rv) {
+ /* Error checking CRC8 for an AMR 4.75 frame */
+ return -1;
+ }
+
+ for (i = 0; i < 12; i++)
+ d[i + 83] = (cB[i + 216] < 0) ? 1 : 0;
+
+ tch_amr_reassemble(tch_data, d, 95);
+
+ len = 12;
+
+ break;
+ default:
+ /* Unknown frame type */
+ *n_bits_total = 159;
+ *n_errors = *n_bits_total;
+ return -1;
+ }
+
+ /* Change codec request / indication, if frame is valid */
+ if (codec_mode_req)
+ *cmr = id;
+ else
+ *ft = id;
+
+ return len;
+}
+
+/*! Perform channel encoding on a TCH/AHS channel according to TS 05.03
+ * \param[out] bursts caller-allocated output buffer for bursts bits
+ * \param[in] tch_data Codec input data in RTP payload format
+ * \param[in] len Length of \a tch_data in bytes
+ * \param[in] codec_mode_req Use CMR (1) or FT (0)
+ * \param[in] codec Array of codecs (active codec set)
+ * \param[in] codecs Number of entries in \a codec
+ * \param[in] ft Frame Type to be used for encoding (index to \a codec)
+ * \param[in] cmr Codec Mode Request (used in codec_mode_req = 1 only)
+ * \returns 0 in case of success; negative on error */
+int gsm0503_tch_ahs_encode(ubit_t *bursts, const uint8_t *tch_data, int len,
+ int codec_mode_req, uint8_t *codec, int codecs, uint8_t ft,
+ uint8_t cmr)
+{
+ ubit_t iB[912], cB[456], h;
+ ubit_t d[244], p[6], conv[135];
+ int i;
+ uint8_t id;
+
+ if (len == GSM_MACBLOCK_LEN) { /* FACCH */
+ _xcch_encode_cB(cB, tch_data);
+
+ h = 1;
+
+ gsm0503_tch_fr_interleave(cB, iB);
+
+ for (i = 0; i < 6; i++)
+ gsm0503_tch_burst_map(&iB[i * 114], &bursts[i * 116],
+ &h, i >> 2);
+ for (i = 2; i < 4; i++)
+ gsm0503_tch_burst_map(&iB[i * 114 + 456],
+ &bursts[i * 116], &h, 1);
+
+ return 0;
+ }
+
+ h = 0;
+
+ if (codec_mode_req) {
+ if (cmr >= codecs) {
+ /* FIXME: CMR ID %d not in codec list */
+ return -1;
+ }
+ id = cmr;
+ } else {
+ if (ft >= codecs) {
+ /* FIXME: FT ID %d not in codec list */
+ return -1;
+ }
+ id = ft;
+ }
+
+ switch (codec[ft]) {
+ case 5: /* TCH/AHS7.95 */
+ if (len != 20)
+ goto invalid_length;
+
+ tch_amr_disassemble(d, tch_data, 159);
+
+ osmo_crc8gen_set_bits(&gsm0503_amr_crc6, d, 67, p);
+
+ tch_amr_merge(conv, d, p, 123, 67);
+
+ osmo_conv_encode(&gsm0503_tch_ahs_7_95, conv, cB + 4);
+
+ memcpy(cB + 192, d + 123, 36);
+
+ break;
+ case 4: /* TCH/AHS7.4 */
+ if (len != 19)
+ goto invalid_length;
+
+ tch_amr_disassemble(d, tch_data, 148);
+
+ osmo_crc8gen_set_bits(&gsm0503_amr_crc6, d, 61, p);
+
+ tch_amr_merge(conv, d, p, 120, 61);
+
+ osmo_conv_encode(&gsm0503_tch_ahs_7_4, conv, cB + 4);
+
+ memcpy(cB + 200, d + 120, 28);
+
+ break;
+ case 3: /* TCH/AHS6.7 */
+ if (len != 17)
+ goto invalid_length;
+
+ tch_amr_disassemble(d, tch_data, 134);
+
+ osmo_crc8gen_set_bits(&gsm0503_amr_crc6, d, 55, p);
+
+ tch_amr_merge(conv, d, p, 110, 55);
+
+ osmo_conv_encode(&gsm0503_tch_ahs_6_7, conv, cB + 4);
+
+ memcpy(cB + 204, d + 110, 24);
+
+ break;
+ case 2: /* TCH/AHS5.9 */
+ if (len != 15)
+ goto invalid_length;
+
+ tch_amr_disassemble(d, tch_data, 118);
+
+ osmo_crc8gen_set_bits(&gsm0503_amr_crc6, d, 55, p);
+
+ tch_amr_merge(conv, d, p, 102, 55);
+
+ osmo_conv_encode(&gsm0503_tch_ahs_5_9, conv, cB + 4);
+
+ memcpy(cB + 212, d + 102, 16);
+
+ break;
+ case 1: /* TCH/AHS5.15 */
+ if (len != 13)
+ goto invalid_length;
+
+ tch_amr_disassemble(d, tch_data, 103);
+
+ osmo_crc8gen_set_bits(&gsm0503_amr_crc6, d, 49, p);
+
+ tch_amr_merge(conv, d, p, 91, 49);
+
+ osmo_conv_encode(&gsm0503_tch_ahs_5_15, conv, cB + 4);
+
+ memcpy(cB + 216, d + 91, 12);
+
+ break;
+ case 0: /* TCH/AHS4.75 */
+ if (len != 12)
+ goto invalid_length;
+
+ tch_amr_disassemble(d, tch_data, 95);
+
+ osmo_crc8gen_set_bits(&gsm0503_amr_crc6, d, 39, p);
+
+ tch_amr_merge(conv, d, p, 83, 39);
+
+ osmo_conv_encode(&gsm0503_tch_ahs_4_75, conv, cB + 4);
+
+ memcpy(cB + 216, d + 83, 12);
+
+ break;
+ default:
+ /* FIXME: FT %ft is not supported */
+ return -1;
+ }
+
+ memcpy(cB, gsm0503_afs_ic_ubit[id], 4);
+
+ gsm0503_tch_hr_interleave(cB, iB);
+
+ for (i = 0; i < 4; i++)
+ gsm0503_tch_burst_map(&iB[i * 114], &bursts[i * 116], &h, i >> 1);
+
+ return 0;
+
+invalid_length:
+ /* FIXME: payload length %len does not comply with codec type %ft */
+ return -1;
+}
+
+/*
+ * GSM RACH transcoding
+ */
+
+/*
+ * GSM RACH apply BSIC to parity
+ *
+ * p(j) = p(j) xor b(j) j = 0, ..., 5
+ * b(0) = MSB of PLMN colour code
+ * b(5) = LSB of BS colour code
+ */
+static inline void rach_apply_bsic(ubit_t *d, uint8_t bsic, uint8_t start)
+{
+ int i;
+
+ /* Apply it */
+ for (i = 0; i < 6; i++)
+ d[start + i] ^= ((bsic >> (5 - i)) & 1);
+}
+/*
+static inline int16_t rach_decode_ber(const sbit_t *burst, uint8_t bsic, bool is_11bit,
+ int *n_errors, int *n_bits_total)
+{
+ ubit_t conv[17];
+ uint8_t ra[2] = { 0 }, nbits = is_11bit ? 11 : 8;
+ int rv;
+
+ osmo_conv_decode_ber(is_11bit ? &gsm0503_rach_ext : &gsm0503_rach, burst, conv,
+ n_errors, n_bits_total);
+
+ rach_apply_bsic(conv, bsic, nbits);
+
+ rv = osmo_crc8gen_check_bits(&gsm0503_rach_crc6, conv, nbits, conv + nbits);
+ if (rv)
+ return -1;
+
+ osmo_ubit2pbit_ext(ra, 0, conv, 0, nbits, 1);
+
+ return is_11bit ? osmo_load16le(ra) : ra[0];
+}
+*/
+
+/*! Decode the Extended (11-bit) RACH according to 3GPP TS 45.003
+ * \param[out] ra output buffer for RACH data
+ * \param[in] burst Input burst data
+ * \param[in] bsic BSIC used in this cell
+ * \returns 0 on success; negative on error (e.g. CRC error) *
+int gsm0503_rach_ext_decode(uint16_t *ra, const sbit_t *burst, uint8_t bsic)
+{
+ int16_t r = rach_decode_ber(burst, bsic, true, NULL, NULL);
+
+ if (r < 0)
+ return r;
+
+ *ra = r;
+
+ return 0;
+}
+
+/*! Decode the (8-bit) RACH according to TS 05.03
+ * \param[out] ra output buffer for RACH data
+ * \param[in] burst Input burst data
+ * \param[in] bsic BSIC used in this cell
+ * \returns 0 on success; negative on error (e.g. CRC error) *
+int gsm0503_rach_decode(uint8_t *ra, const sbit_t *burst, uint8_t bsic)
+{
+ int16_t r = rach_decode_ber(burst, bsic, false, NULL, NULL);
+ if (r < 0)
+ return r;
+
+ *ra = r;
+ return 0;
+}
+
+/*! Decode the Extended (11-bit) RACH according to 3GPP TS 45.003
+ * \param[out] ra output buffer for RACH data
+ * \param[in] burst Input burst data
+ * \param[in] bsic BSIC used in this cell
+ * \param[out] n_errors Number of detected bit errors
+ * \param[out] n_bits_total Total number of bits
+ * \returns 0 on success; negative on error (e.g. CRC error) *
+int gsm0503_rach_ext_decode_ber(uint16_t *ra, const sbit_t *burst, uint8_t bsic,
+ int *n_errors, int *n_bits_total)
+{
+ int16_t r = rach_decode_ber(burst, bsic, true, n_errors, n_bits_total);
+ if (r < 0)
+ return r;
+
+ *ra = r;
+ return 0;
+}
+
+/*! Decode the (8-bit) RACH according to TS 05.03
+ * \param[out] ra output buffer for RACH data
+ * \param[in] burst Input burst data
+ * \param[in] bsic BSIC used in this cell
+ * \param[out] n_errors Number of detected bit errors
+ * \param[out] n_bits_total Total number of bits
+ * \returns 0 on success; negative on error (e.g. CRC error) *
+int gsm0503_rach_decode_ber(uint8_t *ra, const sbit_t *burst, uint8_t bsic,
+ int *n_errors, int *n_bits_total)
+{
+ int16_t r = rach_decode_ber(burst, bsic, false, n_errors, n_bits_total);
+
+ if (r < 0)
+ return r;
+
+ *ra = r;
+
+ return 0;
+}
+
+/*! Encode the (8-bit) RACH according to TS 05.03
+ * \param[out] burst Caller-allocated output burst buffer
+ * \param[in] ra Input RACH data
+ * \param[in] bsic BSIC used in this cell
+ * \returns 0 on success; negative on error *
+int gsm0503_rach_encode(ubit_t *burst, const uint8_t *ra, uint8_t bsic)
+{
+ return gsm0503_rach_ext_encode(burst, *ra, bsic, false);
+}
+
+/*! Encode the Extended (11-bit) or regular (8-bit) RACH according to 3GPP TS 45.003
+ * \param[out] burst Caller-allocated output burst buffer
+ * \param[in] ra11 Input RACH data
+ * \param[in] bsic BSIC used in this cell
+ * \param[in] is_11bit whether given RA is 11 bit or not
+ * \returns 0 on success; negative on error *
+int gsm0503_rach_ext_encode(ubit_t *burst, uint16_t ra11, uint8_t bsic, bool is_11bit)
+{
+ ubit_t conv[17];
+ uint8_t ra[2] = { 0 }, nbits = 8;
+
+ if (is_11bit) {
+ osmo_store16le(ra11, ra);
+ nbits = 11;
+ } else
+ ra[0] = (uint8_t)ra11;
+
+ osmo_pbit2ubit_ext(conv, 0, ra, 0, nbits, 1);
+
+ osmo_crc8gen_set_bits(&gsm0503_rach_crc6, conv, nbits, conv + nbits);
+
+ rach_apply_bsic(conv, bsic, nbits);
+
+ osmo_conv_encode(is_11bit ? &gsm0503_rach_ext : &gsm0503_rach, conv, burst);
+
+ return 0;
+}
+
+/*
+ * GSM SCH transcoding
+ */
+
+/*! Decode the SCH according to TS 05.03
+ * \param[out] sb_info output buffer for SCH data
+ * \param[in] burst Input burst data
+ * \returns 0 on success; negative on error (e.g. CRC error) */
+int gsm0503_sch_decode(uint8_t *sb_info, const sbit_t *burst)
+{
+ ubit_t conv[35];
+ int rv;
+
+ osmo_conv_decode(&gsm0503_sch, burst, conv);
+
+ rv = osmo_crc16gen_check_bits(&gsm0503_sch_crc10, conv, 25, conv + 25);
+ if (rv)
+ return -1;
+
+ osmo_ubit2pbit_ext(sb_info, 0, conv, 0, 25, 1);
+
+ return 0;
+}
+
+/*! Encode the SCH according to TS 05.03
+ * \param[out] burst Caller-allocated output burst buffer
+ * \param[in] sb_info Input SCH data
+ * \returns 0 on success; negative on error */
+int gsm0503_sch_encode(ubit_t *burst, const uint8_t *sb_info)
+{
+ ubit_t conv[35];
+
+ osmo_pbit2ubit_ext(conv, 0, sb_info, 0, 25, 1);
+
+ osmo_crc16gen_set_bits(&gsm0503_sch_crc10, conv, 25, conv + 25);
+
+ osmo_conv_encode(&gsm0503_sch, conv, burst);
+
+ return 0;
+}
+
+/*! @} */
diff --git a/lib/decoding/osmocom/coding/gsm0503_coding.h b/lib/decoding/osmocom/coding/gsm0503_coding.h
new file mode 100644
index 0000000..b9848b7
--- /dev/null
+++ b/lib/decoding/osmocom/coding/gsm0503_coding.h
@@ -0,0 +1,85 @@
+/*! \file gsm0503_coding.h
+ * GSM TS 05.03 coding
+ */
+
+#pragma once
+
+#include <stdint.h>
+
+#include <osmocom/core/defs.h>
+#include <osmocom/core/bits.h>
+
+#include <stdbool.h> //!!
+
+/*! \addtogroup coding
+ * @{
+ * \file gsm0503_coding.h */
+
+#define GSM0503_GPRS_BURSTS_NBITS (116 * 4)
+#define GSM0503_EGPRS_BURSTS_NBITS (348 * 4)
+
+enum gsm0503_egprs_mcs {
+ EGPRS_MCS0,
+ EGPRS_MCS1,
+ EGPRS_MCS2,
+ EGPRS_MCS3,
+ EGPRS_MCS4,
+ EGPRS_MCS5,
+ EGPRS_MCS6,
+ EGPRS_MCS7,
+ EGPRS_MCS8,
+ EGPRS_MCS9,
+ EGPRS_NUM_MCS,
+};
+
+int gsm0503_xcch_encode(ubit_t *bursts, const uint8_t *l2_data);
+int gsm0503_xcch_decode(uint8_t *l2_data, const sbit_t *bursts,
+ int *n_errors, int *n_bits_total);
+
+int gsm0503_pdtch_encode(ubit_t *bursts, const uint8_t *l2_data, uint8_t l2_len);
+int gsm0503_pdtch_decode(uint8_t *l2_data, const sbit_t *bursts, uint8_t *usf_p,
+ int *n_errors, int *n_bits_total);
+
+int gsm0503_pdtch_egprs_encode(ubit_t *bursts, const uint8_t *l2_data,
+ uint8_t l2_len);
+int gsm0503_pdtch_egprs_decode(uint8_t *l2_data, const sbit_t *bursts,
+ uint16_t nbits, uint8_t *usf_p, int *n_errors, int *n_bits_total);
+
+int gsm0503_tch_fr_encode(ubit_t *bursts, const uint8_t *tch_data, int len,
+ int net_order);
+int gsm0503_tch_fr_decode(uint8_t *tch_data, const sbit_t *bursts, int net_order,
+ int efr, int *n_errors, int *n_bits_total);
+
+int gsm0503_tch_hr_encode(ubit_t *bursts, const uint8_t *tch_data, int len);
+int gsm0503_tch_hr_decode(uint8_t *tch_data, const sbit_t *bursts, int odd,
+ int *n_errors, int *n_bits_total);
+
+int gsm0503_tch_afs_encode(ubit_t *bursts, const uint8_t *tch_data, int len,
+ int codec_mode_req, uint8_t *codec, int codecs, uint8_t ft,
+ uint8_t cmr);
+int gsm0503_tch_afs_decode(uint8_t *tch_data, const sbit_t *bursts,
+ int codec_mode_req, uint8_t *codec, int codecs, uint8_t *ft,
+ uint8_t *cmr, int *n_errors, int *n_bits_total);
+
+int gsm0503_tch_ahs_encode(ubit_t *bursts, const uint8_t *tch_data, int len,
+ int codec_mode_req, uint8_t *codec, int codecs, uint8_t ft, uint8_t cmr);
+int gsm0503_tch_ahs_decode(uint8_t *tch_data, const sbit_t *bursts, int odd,
+ int codec_mode_req, uint8_t *codec, int codecs, uint8_t *ft,
+ uint8_t *cmr, int *n_errors, int *n_bits_total);
+
+//int gsm0503_rach_ext_encode(ubit_t *burst, uint16_t ra, uint8_t bsic, bool is_11bit);
+//int gsm0503_rach_encode(ubit_t *burst, const uint8_t *ra, uint8_t bsic) OSMO_DEPRECATED("Use gsm0503_rach_ext_encode() instead");
+
+//int gsm0503_rach_decode(uint8_t *ra, const sbit_t *burst, uint8_t bsic)
+// OSMO_DEPRECATED("Use gsm0503_rach_decode_ber() instead");
+//int gsm0503_rach_decode_ber(uint8_t *ra, const sbit_t *burst, uint8_t bsic,
+ //int *n_errors, int *n_bits_total);
+//int gsm0503_rach_ext_decode(uint16_t *ra, const sbit_t *burst, uint8_t bsic)
+// OSMO_DEPRECATED("Use gsm0503_rach_ext_decode_ber() instead");
+//int gsm0503_rach_ext_decode_ber(uint16_t *ra, const sbit_t *burst, uint8_t bsic,
+// int *n_errors, int *n_bits_total);
+
+int gsm0503_sch_encode(ubit_t *burst, const uint8_t *sb_info);
+int gsm0503_sch_decode(uint8_t *sb_info, const sbit_t *burst);
+
+/*! @} */
diff --git a/lib/decoding/osmocom/coding/gsm0503_conv.c b/lib/decoding/osmocom/coding/gsm0503_conv.c
new file mode 100644
index 0000000..5ab63a6
--- /dev/null
+++ b/lib/decoding/osmocom/coding/gsm0503_conv.c
@@ -0,0 +1,1918 @@
+/*
+ * Copyright (C) 2011-2016 Sylvain Munaut <tnt@246tNt.com>
+ * Copyright (C) 2016 sysmocom s.f.m.c. GmbH
+ *
+ * All Rights Reserved
+ *
+ * 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, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+
+#include <stdint.h>
+#include <osmocom/core/conv.h>
+
+static const uint8_t xcch_state[][2] = {
+ { 0, 1 }, { 2, 3 }, { 4, 5 }, { 6, 7 },
+ { 8, 9 }, { 10, 11 }, { 12, 13 }, { 14, 15 },
+ { 0, 1 }, { 2, 3 }, { 4, 5 }, { 6, 7 },
+ { 8, 9 }, { 10, 11 }, { 12, 13 }, { 14, 15 },
+};
+
+static const uint8_t xcch_output[][2] = {
+ { 0, 3 }, { 1, 2 }, { 0, 3 }, { 1, 2 },
+ { 3, 0 }, { 2, 1 }, { 3, 0 }, { 2, 1 },
+ { 3, 0 }, { 2, 1 }, { 3, 0 }, { 2, 1 },
+ { 0, 3 }, { 1, 2 }, { 0, 3 }, { 1, 2 },
+};
+
+/**
+ * xCCH convolutional code:
+ * 228 bits blocks, rate 1/2, k = 5
+ * G0 = 1 + D3 + D4
+ * G1 = 1 + D + D3 + D4
+ */
+const struct osmo_conv_code gsm0503_xcch = {
+ .N = 2,
+ .K = 5,
+ .len = 224,
+ .next_output = xcch_output,
+ .next_state = xcch_state,
+};
+
+static const uint8_t rach_state[][2] = {
+ { 0, 1 }, { 2, 3 }, { 4, 5 }, { 6, 7 },
+ { 8, 9 }, { 10, 11 }, { 12, 13 }, { 14, 15 },
+ { 0, 1 }, { 2, 3 }, { 4, 5 }, { 6, 7 },
+ { 8, 9 }, { 10, 11 }, { 12, 13 }, { 14, 15 },
+};
+
+static const uint8_t rach_output[][2] = {
+ { 0, 3 }, { 1, 2 }, { 0, 3 }, { 1, 2 },
+ { 3, 0 }, { 2, 1 }, { 3, 0 }, { 2, 1 },
+ { 3, 0 }, { 2, 1 }, { 3, 0 }, { 2, 1 },
+ { 0, 3 }, { 1, 2 }, { 0, 3 }, { 1, 2 },
+};
+
+/**
+ * RACH convolutional code
+ */
+const struct osmo_conv_code gsm0503_rach = {
+ .N = 2,
+ .K = 5,
+ .len = 14,
+ .next_output = rach_output,
+ .next_state = rach_state,
+};
+
+static const uint8_t sch_state[][2] = {
+ { 0, 1 }, { 2, 3 }, { 4, 5 }, { 6, 7 },
+ { 8, 9 }, { 10, 11 }, { 12, 13 }, { 14, 15 },
+ { 0, 1 }, { 2, 3 }, { 4, 5 }, { 6, 7 },
+ { 8, 9 }, { 10, 11 }, { 12, 13 }, { 14, 15 },
+};
+
+static const uint8_t sch_output[][2] = {
+ { 0, 3 }, { 1, 2 }, { 0, 3 }, { 1, 2 },
+ { 3, 0 }, { 2, 1 }, { 3, 0 }, { 2, 1 },
+ { 3, 0 }, { 2, 1 }, { 3, 0 }, { 2, 1 },
+ { 0, 3 }, { 1, 2 }, { 0, 3 }, { 1, 2 },
+};
+
+/**
+ * SCH convolutional code
+ */
+const struct osmo_conv_code gsm0503_sch = {
+ .N = 2,
+ .K = 5,
+ .len = 35,
+ .next_output = sch_output,
+ .next_state = sch_state,
+};
+
+static const uint8_t cs2_state[][2] = {
+ { 0, 1 }, { 2, 3 }, { 4, 5 }, { 6, 7 },
+ { 8, 9 }, { 10, 11 }, { 12, 13 }, { 14, 15 },
+ { 0, 1 }, { 2, 3 }, { 4, 5 }, { 6, 7 },
+ { 8, 9 }, { 10, 11 }, { 12, 13 }, { 14, 15 },
+};
+
+static const uint8_t cs2_output[][2] = {
+ { 0, 3 }, { 1, 2 }, { 0, 3 }, { 1, 2 },
+ { 3, 0 }, { 2, 1 }, { 3, 0 }, { 2, 1 },
+ { 3, 0 }, { 2, 1 }, { 3, 0 }, { 2, 1 },
+ { 0, 3 }, { 1, 2 }, { 0, 3 }, { 1, 2 },
+};
+
+static const int cs2_puncture[] = {
+ 15, 19, 23, 27, 31, 35, 43, 47, 51, 55, 59, 63,
+ 67, 71, 75, 79, 83, 91, 95, 99, 103, 107, 111, 115,
+ 119, 123, 127, 131, 139, 143, 147, 151, 155, 159, 163, 167,
+ 171, 175, 179, 187, 191, 195, 199, 203, 207, 211, 215, 219,
+ 223, 227, 235, 239, 243, 247, 251, 255, 259, 263, 267, 271,
+ 275, 283, 287, 291, 295, 299, 303, 307, 311, 315, 319, 323,
+ 331, 335, 339, 343, 347, 351, 355, 359, 363, 367, 371, 379,
+ 383, 387, 391, 395, 399, 403, 407, 411, 415, 419, 427, 431,
+ 435, 439, 443, 447, 451, 455, 459, 463, 467, 475, 479, 483,
+ 487, 491, 495, 499, 503, 507, 511, 515, 523, 527, 531, 535,
+ 539, 543, 547, 551, 555, 559, 563, 571, 575, 579, 583, 587,
+ -1,
+};
+
+/**
+ * CS2 convolutional code:
+ * G0 = 1 + D3 + D4
+ * G1 = 1 + D + D3 + D4
+ */
+const struct osmo_conv_code gsm0503_cs2 = {
+ .N = 2,
+ .K = 5,
+ .len = 290,
+ .next_output = cs2_output,
+ .next_state = cs2_state,
+ .puncture = cs2_puncture,
+};
+
+static const uint8_t cs3_state[][2] = {
+ { 0, 1 }, { 2, 3 }, { 4, 5 }, { 6, 7 },
+ { 8, 9 }, { 10, 11 }, { 12, 13 }, { 14, 15 },
+ { 0, 1 }, { 2, 3 }, { 4, 5 }, { 6, 7 },
+ { 8, 9 }, { 10, 11 }, { 12, 13 }, { 14, 15 },
+};
+
+static const uint8_t cs3_output[][2] = {
+ { 0, 3 }, { 1, 2 }, { 0, 3 }, { 1, 2 },
+ { 3, 0 }, { 2, 1 }, { 3, 0 }, { 2, 1 },
+ { 3, 0 }, { 2, 1 }, { 3, 0 }, { 2, 1 },
+ { 0, 3 }, { 1, 2 }, { 0, 3 }, { 1, 2 },
+};
+
+static const int cs3_puncture[] = {
+ 15, 17, 21, 23, 27, 29, 33, 35, 39, 41, 45, 47,
+ 51, 53, 57, 59, 63, 65, 69, 71, 75, 77, 81, 83,
+ 87, 89, 93, 95, 99, 101, 105, 107, 111, 113, 117, 119,
+ 123, 125, 129, 131, 135, 137, 141, 143, 147, 149, 153, 155,
+ 159, 161, 165, 167, 171, 173, 177, 179, 183, 185, 189, 191,
+ 195, 197, 201, 203, 207, 209, 213, 215, 219, 221, 225, 227,
+ 231, 233, 237, 239, 243, 245, 249, 251, 255, 257, 261, 263,
+ 267, 269, 273, 275, 279, 281, 285, 287, 291, 293, 297, 299,
+ 303, 305, 309, 311, 315, 317, 321, 323, 327, 329, 333, 335,
+ 339, 341, 345, 347, 351, 353, 357, 359, 363, 365, 369, 371,
+ 375, 377, 381, 383, 387, 389, 393, 395, 399, 401, 405, 407,
+ 411, 413, 417, 419, 423, 425, 429, 431, 435, 437, 441, 443,
+ 447, 449, 453, 455, 459, 461, 465, 467, 471, 473, 477, 479,
+ 483, 485, 489, 491, 495, 497, 501, 503, 507, 509, 513, 515,
+ 519, 521, 525, 527, 531, 533, 537, 539, 543, 545, 549, 551,
+ 555, 557, 561, 563, 567, 569, 573, 575, 579, 581, 585, 587,
+ 591, 593, 597, 599, 603, 605, 609, 611, 615, 617, 621, 623,
+ 627, 629, 633, 635, 639, 641, 645, 647, 651, 653, 657, 659,
+ 663, 665, 669, 671, -1,
+};
+
+/**
+ * CS3 convolutional code:
+ * G0 = 1 + D3 + D4
+ * G1 = 1 + D + D3 + D4
+ */
+const struct osmo_conv_code gsm0503_cs3 = {
+ .N = 2,
+ .K = 5,
+ .len = 334,
+ .next_output = cs3_output,
+ .next_state = cs3_state,
+ .puncture = cs3_puncture,
+};
+
+static const uint8_t tch_afs_12_2_state[][2] = {
+ { 0, 1 }, { 2, 3 }, { 4, 5 }, { 6, 7 },
+ { 9, 8 }, { 11, 10 }, { 13, 12 }, { 15, 14 },
+ { 1, 0 }, { 3, 2 }, { 5, 4 }, { 7, 6 },
+ { 8, 9 }, { 10, 11 }, { 12, 13 }, { 14, 15 },
+};
+
+static const uint8_t tch_afs_12_2_output[][2] = {
+ { 0, 3 }, { 1, 2 }, { 0, 3 }, { 1, 2 },
+ { 0, 3 }, { 1, 2 }, { 0, 3 }, { 1, 2 },
+ { 0, 3 }, { 1, 2 }, { 0, 3 }, { 1, 2 },
+ { 0, 3 }, { 1, 2 }, { 0, 3 }, { 1, 2 },
+};
+
+static const uint8_t tch_afs_12_2_term_state[] = {
+ 0, 2, 4, 6, 8, 10, 12, 14, 0, 2, 4, 6,
+ 8, 10, 12, 14,
+};
+
+static const uint8_t tch_afs_12_2_term_output[] = {
+ 0, 1, 0, 1, 3, 2, 3, 2, 3, 2, 3, 2,
+ 0, 1, 0, 1,
+};
+
+static const int tch_afs_12_2_puncture[] = {
+ 321, 325, 329, 333, 337, 341, 345, 349, 353, 357, 361, 363,
+ 365, 369, 373, 377, 379, 381, 385, 389, 393, 395, 397, 401,
+ 405, 409, 411, 413, 417, 421, 425, 427, 429, 433, 437, 441,
+ 443, 445, 449, 453, 457, 459, 461, 465, 469, 473, 475, 477,
+ 481, 485, 489, 491, 493, 495, 497, 499, 501, 503, 505, 507,
+ -1,
+};
+
+/**
+ * TCH/AFS 12.2 kbits convolutional code:
+ * 250 bits block, rate 1/2, punctured
+ * G0/G0 = 1
+ * G1/G0 = 1 + D + D3 + D4 / 1 + D3 + D4
+ */
+const struct osmo_conv_code gsm0503_tch_afs_12_2 = {
+ .N = 2,
+ .K = 5,
+ .len = 250,
+ .next_output = tch_afs_12_2_output,
+ .next_state = tch_afs_12_2_state,
+ .next_term_output = tch_afs_12_2_term_output,
+ .next_term_state = tch_afs_12_2_term_state,
+ .puncture = tch_afs_12_2_puncture,
+};
+
+static const uint8_t tch_afs_10_2_state[][2] = {
+ { 0, 1 }, { 3, 2 }, { 5, 4 }, { 6, 7 },
+ { 9, 8 }, { 10, 11 }, { 12, 13 }, { 15, 14 },
+ { 1, 0 }, { 2, 3 }, { 4, 5 }, { 7, 6 },
+ { 8, 9 }, { 11, 10 }, { 13, 12 }, { 14, 15 },
+};
+
+static const uint8_t tch_afs_10_2_output[][2] = {
+ { 0, 7 }, { 2, 5 }, { 4, 3 }, { 6, 1 },
+ { 2, 5 }, { 0, 7 }, { 6, 1 }, { 4, 3 },
+ { 0, 7 }, { 2, 5 }, { 4, 3 }, { 6, 1 },
+ { 2, 5 }, { 0, 7 }, { 6, 1 }, { 4, 3 },
+};
+
+static const uint8_t tch_afs_10_2_term_state[] = {
+ 0, 2, 4, 6, 8, 10, 12, 14, 0, 2, 4, 6,
+ 8, 10, 12, 14,
+};
+
+static const uint8_t tch_afs_10_2_term_output[] = {
+ 0, 5, 3, 6, 5, 0, 6, 3, 7, 2, 4, 1,
+ 2, 7, 1, 4,
+};
+
+static const int tch_afs_10_2_puncture[] = {
+ 1, 4, 7, 10, 16, 19, 22, 28, 31, 34, 40, 43,
+ 46, 52, 55, 58, 64, 67, 70, 76, 79, 82, 88, 91,
+ 94, 100, 103, 106, 112, 115, 118, 124, 127, 130, 136, 139,
+ 142, 148, 151, 154, 160, 163, 166, 172, 175, 178, 184, 187,
+ 190, 196, 199, 202, 208, 211, 214, 220, 223, 226, 232, 235,
+ 238, 244, 247, 250, 256, 259, 262, 268, 271, 274, 280, 283,
+ 286, 292, 295, 298, 304, 307, 310, 316, 319, 322, 325, 328,
+ 331, 334, 337, 340, 343, 346, 349, 352, 355, 358, 361, 364,
+ 367, 370, 373, 376, 379, 382, 385, 388, 391, 394, 397, 400,
+ 403, 406, 409, 412, 415, 418, 421, 424, 427, 430, 433, 436,
+ 439, 442, 445, 448, 451, 454, 457, 460, 463, 466, 469, 472,
+ 475, 478, 481, 484, 487, 490, 493, 496, 499, 502, 505, 508,
+ 511, 514, 517, 520, 523, 526, 529, 532, 535, 538, 541, 544,
+ 547, 550, 553, 556, 559, 562, 565, 568, 571, 574, 577, 580,
+ 583, 586, 589, 592, 595, 598, 601, 604, 607, 609, 610, 613,
+ 616, 619, 621, 622, 625, 627, 628, 631, 633, 634, 636, 637,
+ 639, 640, -1,
+};
+
+/**
+ * TCH/AFS 10.2 kbits convolutional code:
+ * G1/G3 = 1 + D + D3 + D4 / 1 + D + D2 + D3 + D4
+ * G2/G3 = 1 + D2 + D4 / 1 + D + D2 + D3 + D4
+ * G3/G3 = 1
+ */
+const struct osmo_conv_code gsm0503_tch_afs_10_2 = {
+ .N = 3,
+ .K = 5,
+ .len = 210,
+ .next_output = tch_afs_10_2_output,
+ .next_state = tch_afs_10_2_state,
+ .next_term_output = tch_afs_10_2_term_output,
+ .next_term_state = tch_afs_10_2_term_state,
+ .puncture = tch_afs_10_2_puncture,
+};
+
+static const uint8_t tch_afs_7_95_state[][2] = {
+ { 0, 1 }, { 2, 3 }, { 5, 4 }, { 7, 6 },
+ { 9, 8 }, { 11, 10 }, { 12, 13 }, { 14, 15 },
+ { 16, 17 }, { 18, 19 }, { 21, 20 }, { 23, 22 },
+ { 25, 24 }, { 27, 26 }, { 28, 29 }, { 30, 31 },
+ { 33, 32 }, { 35, 34 }, { 36, 37 }, { 38, 39 },
+ { 40, 41 }, { 42, 43 }, { 45, 44 }, { 47, 46 },
+ { 49, 48 }, { 51, 50 }, { 52, 53 }, { 54, 55 },
+ { 56, 57 }, { 58, 59 }, { 61, 60 }, { 63, 62 },
+ { 1, 0 }, { 3, 2 }, { 4, 5 }, { 6, 7 },
+ { 8, 9 }, { 10, 11 }, { 13, 12 }, { 15, 14 },
+ { 17, 16 }, { 19, 18 }, { 20, 21 }, { 22, 23 },
+ { 24, 25 }, { 26, 27 }, { 29, 28 }, { 31, 30 },
+ { 32, 33 }, { 34, 35 }, { 37, 36 }, { 39, 38 },
+ { 41, 40 }, { 43, 42 }, { 44, 45 }, { 46, 47 },
+ { 48, 49 }, { 50, 51 }, { 53, 52 }, { 55, 54 },
+ { 57, 56 }, { 59, 58 }, { 60, 61 }, { 62, 63 },
+};
+
+static const uint8_t tch_afs_7_95_output[][2] = {
+ { 0, 7 }, { 3, 4 }, { 2, 5 }, { 1, 6 },
+ { 2, 5 }, { 1, 6 }, { 0, 7 }, { 3, 4 },
+ { 3, 4 }, { 0, 7 }, { 1, 6 }, { 2, 5 },
+ { 1, 6 }, { 2, 5 }, { 3, 4 }, { 0, 7 },
+ { 3, 4 }, { 0, 7 }, { 1, 6 }, { 2, 5 },
+ { 1, 6 }, { 2, 5 }, { 3, 4 }, { 0, 7 },
+ { 0, 7 }, { 3, 4 }, { 2, 5 }, { 1, 6 },
+ { 2, 5 }, { 1, 6 }, { 0, 7 }, { 3, 4 },
+ { 0, 7 }, { 3, 4 }, { 2, 5 }, { 1, 6 },
+ { 2, 5 }, { 1, 6 }, { 0, 7 }, { 3, 4 },
+ { 3, 4 }, { 0, 7 }, { 1, 6 }, { 2, 5 },
+ { 1, 6 }, { 2, 5 }, { 3, 4 }, { 0, 7 },
+ { 3, 4 }, { 0, 7 }, { 1, 6 }, { 2, 5 },
+ { 1, 6 }, { 2, 5 }, { 3, 4 }, { 0, 7 },
+ { 0, 7 }, { 3, 4 }, { 2, 5 }, { 1, 6 },
+ { 2, 5 }, { 1, 6 }, { 0, 7 }, { 3, 4 },
+};
+
+static const uint8_t tch_afs_7_95_term_state[] = {
+ 0, 2, 4, 6, 8, 10, 12, 14, 16, 18, 20, 22,
+ 24, 26, 28, 30, 32, 34, 36, 38, 40, 42, 44, 46,
+ 48, 50, 52, 54, 56, 58, 60, 62, 0, 2, 4, 6,
+ 8, 10, 12, 14, 16, 18, 20, 22, 24, 26, 28, 30,
+ 32, 34, 36, 38, 40, 42, 44, 46, 48, 50, 52, 54,
+ 56, 58, 60, 62,
+};
+
+static const uint8_t tch_afs_7_95_term_output[] = {
+ 0, 3, 5, 6, 5, 6, 0, 3, 3, 0, 6, 5,
+ 6, 5, 3, 0, 4, 7, 1, 2, 1, 2, 4, 7,
+ 7, 4, 2, 1, 2, 1, 7, 4, 7, 4, 2, 1,
+ 2, 1, 7, 4, 4, 7, 1, 2, 1, 2, 4, 7,
+ 3, 0, 6, 5, 6, 5, 3, 0, 0, 3, 5, 6,
+ 5, 6, 0, 3,
+};
+
+static const int tch_afs_7_95_puncture[] = {
+ 1, 2, 4, 5, 8, 22, 70, 118, 166, 214, 262, 310,
+ 317, 319, 325, 332, 334, 341, 343, 349, 356, 358, 365, 367,
+ 373, 380, 382, 385, 389, 391, 397, 404, 406, 409, 413, 415,
+ 421, 428, 430, 433, 437, 439, 445, 452, 454, 457, 461, 463,
+ 469, 476, 478, 481, 485, 487, 490, 493, 500, 502, 503, 505,
+ 506, 508, 509, 511, 512, -1,
+};
+
+/**
+ * TCH/AFS 7.95 kbits convolutional code:
+ * G4/G4 = 1
+ * G5/G4 = 1 + D + D4 + D6 / 1 + D2 + D3 + D5 + D6
+ * G6/G4 = 1 + D + D2 + D3 + D4 + D6 / 1 + D2 + D3 + D5 + D6
+ */
+const struct osmo_conv_code gsm0503_tch_afs_7_95 = {
+ .N = 3,
+ .K = 7,
+ .len = 165,
+ .next_output = tch_afs_7_95_output,
+ .next_state = tch_afs_7_95_state,
+ .next_term_output = tch_afs_7_95_term_output,
+ .next_term_state = tch_afs_7_95_term_state,
+ .puncture = tch_afs_7_95_puncture,
+};
+
+static const uint8_t tch_afs_7_4_state[][2] = {
+ { 0, 1 }, { 3, 2 }, { 5, 4 }, { 6, 7 },
+ { 9, 8 }, { 10, 11 }, { 12, 13 }, { 15, 14 },
+ { 1, 0 }, { 2, 3 }, { 4, 5 }, { 7, 6 },
+ { 8, 9 }, { 11, 10 }, { 13, 12 }, { 14, 15 },
+};
+
+static const uint8_t tch_afs_7_4_output[][2] = {
+ { 0, 7 }, { 2, 5 }, { 4, 3 }, { 6, 1 },
+ { 2, 5 }, { 0, 7 }, { 6, 1 }, { 4, 3 },
+ { 0, 7 }, { 2, 5 }, { 4, 3 }, { 6, 1 },
+ { 2, 5 }, { 0, 7 }, { 6, 1 }, { 4, 3 },
+};
+
+static const uint8_t tch_afs_7_4_term_state[] = {
+ 0, 2, 4, 6, 8, 10, 12, 14, 0, 2, 4, 6,
+ 8, 10, 12, 14,
+};
+
+static const uint8_t tch_afs_7_4_term_output[] = {
+ 0, 5, 3, 6, 5, 0, 6, 3, 7, 2, 4, 1,
+ 2, 7, 1, 4,
+};
+
+static const int tch_afs_7_4_puncture[] = {
+ 0, 355, 361, 367, 373, 379, 385, 391, 397, 403, 409, 415,
+ 421, 427, 433, 439, 445, 451, 457, 460, 463, 466, 468, 469,
+ 471, 472, -1,
+};
+
+/**
+ * TCH/AFS 7.4 kbits convolutional code:
+ * G1/G3 = 1 + D + D3 + D4 / 1 + D + D2 + D3 + D4
+ * G2/G3 = 1 + D2 + D4 / 1 + D + D2 + D3 + D4
+ * G3/G3 = 1
+ */
+const struct osmo_conv_code gsm0503_tch_afs_7_4 = {
+ .N = 3,
+ .K = 5,
+ .len = 154,
+ .next_output = tch_afs_7_4_output,
+ .next_state = tch_afs_7_4_state,
+ .next_term_output = tch_afs_7_4_term_output,
+ .next_term_state = tch_afs_7_4_term_state,
+ .puncture = tch_afs_7_4_puncture,
+};
+
+static const uint8_t tch_afs_6_7_state[][2] = {
+ { 0, 1 }, { 3, 2 }, { 5, 4 }, { 6, 7 },
+ { 9, 8 }, { 10, 11 }, { 12, 13 }, { 15, 14 },
+ { 1, 0 }, { 2, 3 }, { 4, 5 }, { 7, 6 },
+ { 8, 9 }, { 11, 10 }, { 13, 12 }, { 14, 15 },
+};
+
+static const uint8_t tch_afs_6_7_output[][2] = {
+ { 0, 15 }, { 4, 11 }, { 8, 7 }, { 12, 3 },
+ { 4, 11 }, { 0, 15 }, { 12, 3 }, { 8, 7 },
+ { 0, 15 }, { 4, 11 }, { 8, 7 }, { 12, 3 },
+ { 4, 11 }, { 0, 15 }, { 12, 3 }, { 8, 7 },
+};
+
+static const uint8_t tch_afs_6_7_term_state[] = {
+ 0, 2, 4, 6, 8, 10, 12, 14, 0, 2, 4, 6,
+ 8, 10, 12, 14,
+};
+
+static const uint8_t tch_afs_6_7_term_output[] = {
+ 0, 11, 7, 12, 11, 0, 12, 7, 15, 4, 8, 3,
+ 4, 15, 3, 8,
+};
+
+static const int tch_afs_6_7_puncture[] = {
+ 1, 3, 7, 11, 15, 27, 39, 55, 67, 79, 95, 107,
+ 119, 135, 147, 159, 175, 187, 199, 215, 227, 239, 255, 267,
+ 279, 287, 291, 295, 299, 303, 307, 311, 315, 319, 323, 327,
+ 331, 335, 339, 343, 347, 351, 355, 359, 363, 367, 369, 371,
+ 375, 377, 379, 383, 385, 387, 391, 393, 395, 399, 401, 403,
+ 407, 409, 411, 415, 417, 419, 423, 425, 427, 431, 433, 435,
+ 439, 441, 443, 447, 449, 451, 455, 457, 459, 463, 465, 467,
+ 471, 473, 475, 479, 481, 483, 487, 489, 491, 495, 497, 499,
+ 503, 505, 507, 511, 513, 515, 519, 521, 523, 527, 529, 531,
+ 535, 537, 539, 543, 545, 547, 549, 551, 553, 555, 557, 559,
+ 561, 563, 565, 567, 569, 571, 573, 575, -1,
+};
+
+/**
+ * TCH/AFS 6.7 kbits convolutional code:
+ * G1/G3 = 1 + D + D3 + D4 / 1 + D + D2 + D3 + D4
+ * G2/G3 = 1 + D2 + D4 / 1 + D + D2 + D3 + D4
+ * G3/G3 = 1
+ * G3/G3 = 1
+ */
+const struct osmo_conv_code gsm0503_tch_afs_6_7 = {
+ .N = 4,
+ .K = 5,
+ .len = 140,
+ .next_output = tch_afs_6_7_output,
+ .next_state = tch_afs_6_7_state,
+ .next_term_output = tch_afs_6_7_term_output,
+ .next_term_state = tch_afs_6_7_term_state,
+ .puncture = tch_afs_6_7_puncture,
+};
+
+static const uint8_t tch_afs_5_9_state[][2] = {
+ { 0, 1 }, { 3, 2 }, { 5, 4 }, { 6, 7 },
+ { 9, 8 }, { 10, 11 }, { 12, 13 }, { 15, 14 },
+ { 17, 16 }, { 18, 19 }, { 20, 21 }, { 23, 22 },
+ { 24, 25 }, { 27, 26 }, { 29, 28 }, { 30, 31 },
+ { 32, 33 }, { 35, 34 }, { 37, 36 }, { 38, 39 },
+ { 41, 40 }, { 42, 43 }, { 44, 45 }, { 47, 46 },
+ { 49, 48 }, { 50, 51 }, { 52, 53 }, { 55, 54 },
+ { 56, 57 }, { 59, 58 }, { 61, 60 }, { 62, 63 },
+ { 1, 0 }, { 2, 3 }, { 4, 5 }, { 7, 6 },
+ { 8, 9 }, { 11, 10 }, { 13, 12 }, { 14, 15 },
+ { 16, 17 }, { 19, 18 }, { 21, 20 }, { 22, 23 },
+ { 25, 24 }, { 26, 27 }, { 28, 29 }, { 31, 30 },
+ { 33, 32 }, { 34, 35 }, { 36, 37 }, { 39, 38 },
+ { 40, 41 }, { 43, 42 }, { 45, 44 }, { 46, 47 },
+ { 48, 49 }, { 51, 50 }, { 53, 52 }, { 54, 55 },
+ { 57, 56 }, { 58, 59 }, { 60, 61 }, { 63, 62 },
+};
+
+static const uint8_t tch_afs_5_9_output[][2] = {
+ { 0, 15 }, { 8, 7 }, { 4, 11 }, { 12, 3 },
+ { 4, 11 }, { 12, 3 }, { 0, 15 }, { 8, 7 },
+ { 8, 7 }, { 0, 15 }, { 12, 3 }, { 4, 11 },
+ { 12, 3 }, { 4, 11 }, { 8, 7 }, { 0, 15 },
+ { 8, 7 }, { 0, 15 }, { 12, 3 }, { 4, 11 },
+ { 12, 3 }, { 4, 11 }, { 8, 7 }, { 0, 15 },
+ { 0, 15 }, { 8, 7 }, { 4, 11 }, { 12, 3 },
+ { 4, 11 }, { 12, 3 }, { 0, 15 }, { 8, 7 },
+ { 0, 15 }, { 8, 7 }, { 4, 11 }, { 12, 3 },
+ { 4, 11 }, { 12, 3 }, { 0, 15 }, { 8, 7 },
+ { 8, 7 }, { 0, 15 }, { 12, 3 }, { 4, 11 },
+ { 12, 3 }, { 4, 11 }, { 8, 7 }, { 0, 15 },
+ { 8, 7 }, { 0, 15 }, { 12, 3 }, { 4, 11 },
+ { 12, 3 }, { 4, 11 }, { 8, 7 }, { 0, 15 },
+ { 0, 15 }, { 8, 7 }, { 4, 11 }, { 12, 3 },
+ { 4, 11 }, { 12, 3 }, { 0, 15 }, { 8, 7 },
+};
+
+static const uint8_t tch_afs_5_9_term_state[] = {
+ 0, 2, 4, 6, 8, 10, 12, 14, 16, 18, 20, 22,
+ 24, 26, 28, 30, 32, 34, 36, 38, 40, 42, 44, 46,
+ 48, 50, 52, 54, 56, 58, 60, 62, 0, 2, 4, 6,
+ 8, 10, 12, 14, 16, 18, 20, 22, 24, 26, 28, 30,
+ 32, 34, 36, 38, 40, 42, 44, 46, 48, 50, 52, 54,
+ 56, 58, 60, 62,
+};
+
+static const uint8_t tch_afs_5_9_term_output[] = {
+ 0, 7, 11, 12, 11, 12, 0, 7, 7, 0, 12, 11,
+ 12, 11, 7, 0, 8, 15, 3, 4, 3, 4, 8, 15,
+ 15, 8, 4, 3, 4, 3, 15, 8, 15, 8, 4, 3,
+ 4, 3, 15, 8, 8, 15, 3, 4, 3, 4, 8, 15,
+ 7, 0, 12, 11, 12, 11, 7, 0, 0, 7, 11, 12,
+ 11, 12, 0, 7,
+};
+
+static const int tch_afs_5_9_puncture[] = {
+ 0, 1, 3, 5, 7, 11, 15, 31, 47, 63, 79, 95,
+ 111, 127, 143, 159, 175, 191, 207, 223, 239, 255, 271, 287,
+ 303, 319, 327, 331, 335, 343, 347, 351, 359, 363, 367, 375,
+ 379, 383, 391, 395, 399, 407, 411, 415, 423, 427, 431, 439,
+ 443, 447, 455, 459, 463, 467, 471, 475, 479, 483, 487, 491,
+ 495, 499, 503, 507, 509, 511, 512, 513, 515, 516, 517, 519,
+ -1,
+};
+
+/**
+ * TCH/AFS 5.9 kbits convolutional code:
+ * 124 bits
+ * G4/G6 = 1 + D2 + D3 + D5 + D6 / 1 + D + D2 + D3 + D4 + D6
+ * G5/G6 = 1 + D + D4 + D6 / 1 + D + D2 + D3 + D4 + D6
+ * G6/G6 = 1
+ * G6/G6 = 1
+ */
+const struct osmo_conv_code gsm0503_tch_afs_5_9 = {
+ .N = 4,
+ .K = 7,
+ .len = 124,
+ .next_output = tch_afs_5_9_output,
+ .next_state = tch_afs_5_9_state,
+ .next_term_output = tch_afs_5_9_term_output,
+ .next_term_state = tch_afs_5_9_term_state,
+ .puncture = tch_afs_5_9_puncture,
+};
+
+static const uint8_t tch_afs_5_15_state[][2] = {
+ { 0, 1 }, { 3, 2 }, { 5, 4 }, { 6, 7 },
+ { 9, 8 }, { 10, 11 }, { 12, 13 }, { 15, 14 },
+ { 1, 0 }, { 2, 3 }, { 4, 5 }, { 7, 6 },
+ { 8, 9 }, { 11, 10 }, { 13, 12 }, { 14, 15 },
+};
+
+static const uint8_t tch_afs_5_15_output[][2] = {
+ { 0, 31 }, { 4, 27 }, { 24, 7 }, { 28, 3 },
+ { 4, 27 }, { 0, 31 }, { 28, 3 }, { 24, 7 },
+ { 0, 31 }, { 4, 27 }, { 24, 7 }, { 28, 3 },
+ { 4, 27 }, { 0, 31 }, { 28, 3 }, { 24, 7 },
+};
+
+static const uint8_t tch_afs_5_15_term_state[] = {
+ 0, 2, 4, 6, 8, 10, 12, 14, 0, 2, 4, 6,
+ 8, 10, 12, 14,
+};
+
+static const uint8_t tch_afs_5_15_term_output[] = {
+ 0, 27, 7, 28, 27, 0, 28, 7, 31, 4, 24, 3,
+ 4, 31, 3, 24,
+};
+
+static const int tch_afs_5_15_puncture[] = {
+ 0, 4, 5, 9, 10, 14, 15, 20, 25, 30, 35, 40,
+ 50, 60, 70, 80, 90, 100, 110, 120, 130, 140, 150, 160,
+ 170, 180, 190, 200, 210, 220, 230, 240, 250, 260, 270, 280,
+ 290, 300, 310, 315, 320, 325, 330, 334, 335, 340, 344, 345,
+ 350, 354, 355, 360, 364, 365, 370, 374, 375, 380, 384, 385,
+ 390, 394, 395, 400, 404, 405, 410, 414, 415, 420, 424, 425,
+ 430, 434, 435, 440, 444, 445, 450, 454, 455, 460, 464, 465,
+ 470, 474, 475, 480, 484, 485, 490, 494, 495, 500, 504, 505,
+ 510, 514, 515, 520, 524, 525, 529, 530, 534, 535, 539, 540,
+ 544, 545, 549, 550, 554, 555, 559, 560, 564, -1,
+};
+
+/**
+ * TCH/AFS 5.15 kbits convolutional code:
+ * G1/G3 = 1 + D + D3 + D4 / 1 + D + D2 + D3 + D4
+ * G1/G3 = 1 + D + D3 + D4 / 1 + D + D2 + D3 + D4
+ * G2/G3 = 1 + D2 + D4 / 1 + D + D2 + D3 + D4
+ * G3/G3 = 1
+ * G3/G3 = 1
+ */
+const struct osmo_conv_code gsm0503_tch_afs_5_15 = {
+ .N = 5,
+ .K = 5,
+ .len = 109,
+ .next_output = tch_afs_5_15_output,
+ .next_state = tch_afs_5_15_state,
+ .next_term_output = tch_afs_5_15_term_output,
+ .next_term_state = tch_afs_5_15_term_state,
+ .puncture = tch_afs_5_15_puncture,
+};
+
+static const uint8_t tch_afs_4_75_state[][2] = {
+ { 0, 1 }, { 3, 2 }, { 5, 4 }, { 6, 7 },
+ { 9, 8 }, { 10, 11 }, { 12, 13 }, { 15, 14 },
+ { 17, 16 }, { 18, 19 }, { 20, 21 }, { 23, 22 },
+ { 24, 25 }, { 27, 26 }, { 29, 28 }, { 30, 31 },
+ { 32, 33 }, { 35, 34 }, { 37, 36 }, { 38, 39 },
+ { 41, 40 }, { 42, 43 }, { 44, 45 }, { 47, 46 },
+ { 49, 48 }, { 50, 51 }, { 52, 53 }, { 55, 54 },
+ { 56, 57 }, { 59, 58 }, { 61, 60 }, { 62, 63 },
+ { 1, 0 }, { 2, 3 }, { 4, 5 }, { 7, 6 },
+ { 8, 9 }, { 11, 10 }, { 13, 12 }, { 14, 15 },
+ { 16, 17 }, { 19, 18 }, { 21, 20 }, { 22, 23 },
+ { 25, 24 }, { 26, 27 }, { 28, 29 }, { 31, 30 },
+ { 33, 32 }, { 34, 35 }, { 36, 37 }, { 39, 38 },
+ { 40, 41 }, { 43, 42 }, { 45, 44 }, { 46, 47 },
+ { 48, 49 }, { 51, 50 }, { 53, 52 }, { 54, 55 },
+ { 57, 56 }, { 58, 59 }, { 60, 61 }, { 63, 62 },
+};
+
+static const uint8_t tch_afs_4_75_output[][2] = {
+ { 0, 31 }, { 24, 7 }, { 4, 27 }, { 28, 3 },
+ { 4, 27 }, { 28, 3 }, { 0, 31 }, { 24, 7 },
+ { 24, 7 }, { 0, 31 }, { 28, 3 }, { 4, 27 },
+ { 28, 3 }, { 4, 27 }, { 24, 7 }, { 0, 31 },
+ { 24, 7 }, { 0, 31 }, { 28, 3 }, { 4, 27 },
+ { 28, 3 }, { 4, 27 }, { 24, 7 }, { 0, 31 },
+ { 0, 31 }, { 24, 7 }, { 4, 27 }, { 28, 3 },
+ { 4, 27 }, { 28, 3 }, { 0, 31 }, { 24, 7 },
+ { 0, 31 }, { 24, 7 }, { 4, 27 }, { 28, 3 },
+ { 4, 27 }, { 28, 3 }, { 0, 31 }, { 24, 7 },
+ { 24, 7 }, { 0, 31 }, { 28, 3 }, { 4, 27 },
+ { 28, 3 }, { 4, 27 }, { 24, 7 }, { 0, 31 },
+ { 24, 7 }, { 0, 31 }, { 28, 3 }, { 4, 27 },
+ { 28, 3 }, { 4, 27 }, { 24, 7 }, { 0, 31 },
+ { 0, 31 }, { 24, 7 }, { 4, 27 }, { 28, 3 },
+ { 4, 27 }, { 28, 3 }, { 0, 31 }, { 24, 7 },
+};
+
+static const uint8_t tch_afs_4_75_term_state[] = {
+ 0, 2, 4, 6, 8, 10, 12, 14, 16, 18, 20, 22,
+ 24, 26, 28, 30, 32, 34, 36, 38, 40, 42, 44, 46,
+ 48, 50, 52, 54, 56, 58, 60, 62, 0, 2, 4, 6,
+ 8, 10, 12, 14, 16, 18, 20, 22, 24, 26, 28, 30,
+ 32, 34, 36, 38, 40, 42, 44, 46, 48, 50, 52, 54,
+ 56, 58, 60, 62,
+};
+
+static const uint8_t tch_afs_4_75_term_output[] = {
+ 0, 7, 27, 28, 27, 28, 0, 7, 7, 0, 28, 27,
+ 28, 27, 7, 0, 24, 31, 3, 4, 3, 4, 24, 31,
+ 31, 24, 4, 3, 4, 3, 31, 24, 31, 24, 4, 3,
+ 4, 3, 31, 24, 24, 31, 3, 4, 3, 4, 24, 31,
+ 7, 0, 28, 27, 28, 27, 7, 0, 0, 7, 27, 28,
+ 27, 28, 0, 7,
+};
+
+static const int tch_afs_4_75_puncture[] = {
+ 0, 1, 2, 4, 5, 7, 9, 15, 25, 35, 45, 55,
+ 65, 75, 85, 95, 105, 115, 125, 135, 145, 155, 165, 175,
+ 185, 195, 205, 215, 225, 235, 245, 255, 265, 275, 285, 295,
+ 305, 315, 325, 335, 345, 355, 365, 375, 385, 395, 400, 405,
+ 410, 415, 420, 425, 430, 435, 440, 445, 450, 455, 459, 460,
+ 465, 470, 475, 479, 480, 485, 490, 495, 499, 500, 505, 509,
+ 510, 515, 517, 519, 520, 522, 524, 525, 526, 527, 529, 530,
+ 531, 532, 534, -1,
+};
+
+/**
+ * TCH/AFS 4.75 kbits convolutional code:
+ * G4/G6 = 1 + D2 + D3 + D5 + D6 / 1 + D + D2 + D3 + D4 + D6
+ * G4/G6 = 1 + D2 + D3 + D5 + D6 / 1 + D + D2 + D3 + D4 + D6
+ * G5/G6 = 1 + D + D4 + D6 / 1 + D + D2 + D3 + D4 + D6
+ * G6/G6 = 1
+ * G6/G6 = 1
+ */
+const struct osmo_conv_code gsm0503_tch_afs_4_75 = {
+ .N = 5,
+ .K = 7,
+ .len = 101,
+ .next_output = tch_afs_4_75_output,
+ .next_state = tch_afs_4_75_state,
+ .next_term_output = tch_afs_4_75_term_output,
+ .next_term_state = tch_afs_4_75_term_state,
+ .puncture = tch_afs_4_75_puncture,
+};
+
+static const uint8_t tch_fr_state[][2] = {
+ { 0, 1 }, { 2, 3 }, { 4, 5 }, { 6, 7 },
+ { 8, 9 }, { 10, 11 }, { 12, 13 }, { 14, 15 },
+ { 0, 1 }, { 2, 3 }, { 4, 5 }, { 6, 7 },
+ { 8, 9 }, { 10, 11 }, { 12, 13 }, { 14, 15 },
+};
+
+static const uint8_t tch_fr_output[][2] = {
+ { 0, 3 }, { 1, 2 }, { 0, 3 }, { 1, 2 },
+ { 3, 0 }, { 2, 1 }, { 3, 0 }, { 2, 1 },
+ { 3, 0 }, { 2, 1 }, { 3, 0 }, { 2, 1 },
+ { 0, 3 }, { 1, 2 }, { 0, 3 }, { 1, 2 },
+};
+
+/**
+ * TCH/F convolutional code
+ */
+const struct osmo_conv_code gsm0503_tch_fr = {
+ .N = 2,
+ .K = 5,
+ .len = 185,
+ .next_output = tch_fr_output,
+ .next_state = tch_fr_state,
+};
+
+static const uint8_t tch_hr_state[][2] = {
+ { 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, 51 }, { 52, 53 }, { 54, 55 },
+ { 56, 57 }, { 58, 59 }, { 60, 61 }, { 62, 63 },
+ { 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, 51 }, { 52, 53 }, { 54, 55 },
+ { 56, 57 }, { 58, 59 }, { 60, 61 }, { 62, 63 },
+};
+
+static const uint8_t tch_hr_output[][2] = {
+ { 0, 7 }, { 3, 4 }, { 5, 2 }, { 6, 1 },
+ { 5, 2 }, { 6, 1 }, { 0, 7 }, { 3, 4 },
+ { 3, 4 }, { 0, 7 }, { 6, 1 }, { 5, 2 },
+ { 6, 1 }, { 5, 2 }, { 3, 4 }, { 0, 7 },
+ { 4, 3 }, { 7, 0 }, { 1, 6 }, { 2, 5 },
+ { 1, 6 }, { 2, 5 }, { 4, 3 }, { 7, 0 },
+ { 7, 0 }, { 4, 3 }, { 2, 5 }, { 1, 6 },
+ { 2, 5 }, { 1, 6 }, { 7, 0 }, { 4, 3 },
+ { 7, 0 }, { 4, 3 }, { 2, 5 }, { 1, 6 },
+ { 2, 5 }, { 1, 6 }, { 7, 0 }, { 4, 3 },
+ { 4, 3 }, { 7, 0 }, { 1, 6 }, { 2, 5 },
+ { 1, 6 }, { 2, 5 }, { 4, 3 }, { 7, 0 },
+ { 3, 4 }, { 0, 7 }, { 6, 1 }, { 5, 2 },
+ { 6, 1 }, { 5, 2 }, { 3, 4 }, { 0, 7 },
+ { 0, 7 }, { 3, 4 }, { 5, 2 }, { 6, 1 },
+ { 5, 2 }, { 6, 1 }, { 0, 7 }, { 3, 4 },
+};
+
+static const int tch_hr_puncture[] = {
+ 1, 4, 7, 10, 13, 16, 19, 22, 25, 28, 31, 34,
+ 37, 40, 43, 46, 49, 52, 55, 58, 61, 64, 67, 70,
+ 73, 76, 79, 82, 85, 88, 91, 94, 97, 100, 103, 106,
+ 109, 112, 115, 118, 121, 124, 127, 130, 133, 136, 139, 142,
+ 145, 148, 151, 154, 157, 160, 163, 166, 169, 172, 175, 178,
+ 181, 184, 187, 190, 193, 196, 199, 202, 205, 208, 211, 214,
+ 217, 220, 223, 226, 229, 232, 235, 238, 241, 244, 247, 250,
+ 253, 256, 259, 262, 265, 268, 271, 274, 277, 280, 283, 295,
+ 298, 301, 304, 307, 310, -1,
+};
+
+/**
+ * TCH/H convolutional code
+ */
+const struct osmo_conv_code gsm0503_tch_hr = {
+ .N = 3,
+ .K = 7,
+ .len = 98,
+ .next_output = tch_hr_output,
+ .next_state = tch_hr_state,
+ .puncture = tch_hr_puncture,
+};
+
+static const uint8_t tch_ahs_7_95_state[][2] = {
+ { 0, 1 }, { 2, 3 }, { 4, 5 }, { 6, 7 },
+ { 9, 8 }, { 11, 10 }, { 13, 12 }, { 15, 14 },
+ { 1, 0 }, { 3, 2 }, { 5, 4 }, { 7, 6 },
+ { 8, 9 }, { 10, 11 }, { 12, 13 }, { 14, 15 },
+};
+
+static const uint8_t tch_ahs_7_95_output[][2] = {
+ { 0, 3 }, { 1, 2 }, { 0, 3 }, { 1, 2 },
+ { 0, 3 }, { 1, 2 }, { 0, 3 }, { 1, 2 },
+ { 0, 3 }, { 1, 2 }, { 0, 3 }, { 1, 2 },
+ { 0, 3 }, { 1, 2 }, { 0, 3 }, { 1, 2 },
+};
+
+static const uint8_t tch_ahs_7_95_term_state[] = {
+ 0, 2, 4, 6, 8, 10, 12, 14, 0, 2, 4, 6,
+ 8, 10, 12, 14,
+};
+
+static const uint8_t tch_ahs_7_95_term_output[] = {
+ 0, 1, 0, 1, 3, 2, 3, 2, 3, 2, 3, 2,
+ 0, 1, 0, 1,
+};
+
+static const int tch_ahs_7_95_puncture[] = {
+ 1, 3, 5, 7, 11, 15, 19, 23, 27, 31, 35, 43,
+ 47, 51, 55, 59, 63, 67, 71, 79, 83, 87, 91, 95,
+ 99, 103, 107, 115, 119, 123, 127, 131, 135, 139, 143, 151,
+ 155, 159, 163, 167, 171, 175, 177, 179, 183, 185, 187, 191,
+ 193, 195, 197, 199, 203, 205, 207, 211, 213, 215, 219, 221,
+ 223, 227, 229, 231, 233, 235, 239, 241, 243, 247, 249, 251,
+ 255, 257, 259, 261, 263, 265, -1,
+};
+
+/**
+ * TCH/AHS 7.95 kbits convolutional code
+ */
+const struct osmo_conv_code gsm0503_tch_ahs_7_95 = {
+ .N = 2,
+ .K = 5,
+ .len = 129,
+ .next_output = tch_ahs_7_95_output,
+ .next_state = tch_ahs_7_95_state,
+ .next_term_output = tch_ahs_7_95_term_output,
+ .next_term_state = tch_ahs_7_95_term_state,
+ .puncture = tch_ahs_7_95_puncture,
+};
+
+static const uint8_t tch_ahs_7_4_state[][2] = {
+ { 0, 1 }, { 2, 3 }, { 4, 5 }, { 6, 7 },
+ { 9, 8 }, { 11, 10 }, { 13, 12 }, { 15, 14 },
+ { 1, 0 }, { 3, 2 }, { 5, 4 }, { 7, 6 },
+ { 8, 9 }, { 10, 11 }, { 12, 13 }, { 14, 15 },
+};
+
+static const uint8_t tch_ahs_7_4_output[][2] = {
+ { 0, 3 }, { 1, 2 }, { 0, 3 }, { 1, 2 },
+ { 0, 3 }, { 1, 2 }, { 0, 3 }, { 1, 2 },
+ { 0, 3 }, { 1, 2 }, { 0, 3 }, { 1, 2 },
+ { 0, 3 }, { 1, 2 }, { 0, 3 }, { 1, 2 },
+};
+
+static const uint8_t tch_ahs_7_4_term_state[] = {
+ 0, 2, 4, 6, 8, 10, 12, 14, 0, 2, 4, 6,
+ 8, 10, 12, 14,
+};
+
+static const uint8_t tch_ahs_7_4_term_output[] = {
+ 0, 1, 0, 1, 3, 2, 3, 2, 3, 2, 3, 2,
+ 0, 1, 0, 1,
+};
+
+static const int tch_ahs_7_4_puncture[] = {
+ 1, 3, 7, 11, 19, 23, 27, 35, 39, 43, 51, 55,
+ 59, 67, 71, 75, 83, 87, 91, 99, 103, 107, 115, 119,
+ 123, 131, 135, 139, 143, 147, 151, 155, 159, 163, 167, 171,
+ 175, 179, 183, 187, 191, 195, 199, 203, 207, 211, 215, 219,
+ 221, 223, 227, 229, 231, 235, 237, 239, 243, 245, 247, 251,
+ 253, 255, 257, 259, -1,
+};
+
+/**
+ * TCH/AHS 7.4 kbits convolutional code
+ */
+const struct osmo_conv_code gsm0503_tch_ahs_7_4 = {
+ .N = 2,
+ .K = 5,
+ .len = 126,
+ .next_output = tch_ahs_7_4_output,
+ .next_state = tch_ahs_7_4_state,
+ .next_term_output = tch_ahs_7_4_term_output,
+ .next_term_state = tch_ahs_7_4_term_state,
+ .puncture = tch_ahs_7_4_puncture,
+};
+
+static const uint8_t tch_ahs_6_7_state[][2] = {
+ { 0, 1 }, { 2, 3 }, { 4, 5 }, { 6, 7 },
+ { 9, 8 }, { 11, 10 }, { 13, 12 }, { 15, 14 },
+ { 1, 0 }, { 3, 2 }, { 5, 4 }, { 7, 6 },
+ { 8, 9 }, { 10, 11 }, { 12, 13 }, { 14, 15 },
+};
+
+static const uint8_t tch_ahs_6_7_output[][2] = {
+ { 0, 3 }, { 1, 2 }, { 0, 3 }, { 1, 2 },
+ { 0, 3 }, { 1, 2 }, { 0, 3 }, { 1, 2 },
+ { 0, 3 }, { 1, 2 }, { 0, 3 }, { 1, 2 },
+ { 0, 3 }, { 1, 2 }, { 0, 3 }, { 1, 2 },
+};
+
+static const uint8_t tch_ahs_6_7_term_state[] = {
+ 0, 2, 4, 6, 8, 10, 12, 14, 0, 2, 4, 6,
+ 8, 10, 12, 14,
+};
+
+static const uint8_t tch_ahs_6_7_term_output[] = {
+ 0, 1, 0, 1, 3, 2, 3, 2, 3, 2, 3, 2,
+ 0, 1, 0, 1,
+};
+
+static const int tch_ahs_6_7_puncture[] = {
+ 1, 3, 9, 19, 29, 39, 49, 59, 69, 79, 89, 99,
+ 109, 119, 129, 139, 149, 159, 167, 169, 177, 179, 187, 189,
+ 197, 199, 203, 207, 209, 213, 217, 219, 223, 227, 229, 231,
+ 233, 235, 237, 239, -1,
+};
+
+/**
+ * TCH/AHS 6.7 kbits convolutional code
+ */
+const struct osmo_conv_code gsm0503_tch_ahs_6_7 = {
+ .N = 2,
+ .K = 5,
+ .len = 116,
+ .next_output = tch_ahs_6_7_output,
+ .next_state = tch_ahs_6_7_state,
+ .next_term_output = tch_ahs_6_7_term_output,
+ .next_term_state = tch_ahs_6_7_term_state,
+ .puncture = tch_ahs_6_7_puncture,
+};
+
+static const uint8_t tch_ahs_5_9_state[][2] = {
+ { 0, 1 }, { 2, 3 }, { 4, 5 }, { 6, 7 },
+ { 9, 8 }, { 11, 10 }, { 13, 12 }, { 15, 14 },
+ { 1, 0 }, { 3, 2 }, { 5, 4 }, { 7, 6 },
+ { 8, 9 }, { 10, 11 }, { 12, 13 }, { 14, 15 },
+};
+
+static const uint8_t tch_ahs_5_9_output[][2] = {
+ { 0, 3 }, { 1, 2 }, { 0, 3 }, { 1, 2 },
+ { 0, 3 }, { 1, 2 }, { 0, 3 }, { 1, 2 },
+ { 0, 3 }, { 1, 2 }, { 0, 3 }, { 1, 2 },
+ { 0, 3 }, { 1, 2 }, { 0, 3 }, { 1, 2 },
+};
+
+static const uint8_t tch_ahs_5_9_term_state[] = {
+ 0, 2, 4, 6, 8, 10, 12, 14, 0, 2, 4, 6,
+ 8, 10, 12, 14,
+};
+
+static const uint8_t tch_ahs_5_9_term_output[] = {
+ 0, 1, 0, 1, 3, 2, 3, 2, 3, 2, 3, 2,
+ 0, 1, 0, 1,
+};
+
+static const int tch_ahs_5_9_puncture[] = {
+ 1, 15, 71, 127, 139, 151, 163, 175, 187, 195, 203, 211,
+ 215, 219, 221, 223, -1,
+};
+
+/**
+ * TCH/AHS 5.9 kbits convolutional code
+ */
+const struct osmo_conv_code gsm0503_tch_ahs_5_9 = {
+ .N = 2,
+ .K = 5,
+ .len = 108,
+ .next_output = tch_ahs_5_9_output,
+ .next_state = tch_ahs_5_9_state,
+ .next_term_output = tch_ahs_5_9_term_output,
+ .next_term_state = tch_ahs_5_9_term_state,
+ .puncture = tch_ahs_5_9_puncture,
+};
+
+static const uint8_t tch_ahs_5_15_state[][2] = {
+ { 0, 1 }, { 3, 2 }, { 5, 4 }, { 6, 7 },
+ { 9, 8 }, { 10, 11 }, { 12, 13 }, { 15, 14 },
+ { 1, 0 }, { 2, 3 }, { 4, 5 }, { 7, 6 },
+ { 8, 9 }, { 11, 10 }, { 13, 12 }, { 14, 15 },
+};
+
+static const uint8_t tch_ahs_5_15_output[][2] = {
+ { 0, 7 }, { 2, 5 }, { 4, 3 }, { 6, 1 },
+ { 2, 5 }, { 0, 7 }, { 6, 1 }, { 4, 3 },
+ { 0, 7 }, { 2, 5 }, { 4, 3 }, { 6, 1 },
+ { 2, 5 }, { 0, 7 }, { 6, 1 }, { 4, 3 },
+};
+
+static const uint8_t tch_ahs_5_15_term_state[] = {
+ 0, 2, 4, 6, 8, 10, 12, 14, 0, 2, 4, 6,
+ 8, 10, 12, 14,
+};
+
+static const uint8_t tch_ahs_5_15_term_output[] = {
+ 0, 5, 3, 6, 5, 0, 6, 3, 7, 2, 4, 1,
+ 2, 7, 1, 4,
+};
+
+static const int tch_ahs_5_15_puncture[] = {
+ 0, 1, 3, 4, 6, 9, 12, 15, 18, 21, 27, 33,
+ 39, 45, 51, 54, 57, 63, 69, 75, 81, 87, 90, 93,
+ 99, 105, 111, 117, 123, 126, 129, 135, 141, 147, 153, 159,
+ 162, 165, 168, 171, 174, 177, 180, 183, 186, 189, 192, 195,
+ 198, 201, 204, 207, 210, 213, 216, 219, 222, 225, 228, 231,
+ 234, 237, 240, 243, 244, 246, 249, 252, 255, 256, 258, 261,
+ 264, 267, 268, 270, 273, 276, 279, 280, 282, 285, 288, 289,
+ 291, 294, 295, 297, 298, 300, 301, -1,
+};
+
+/**
+ * TCH/AHS 5.15 kbits convolutional code
+ */
+const struct osmo_conv_code gsm0503_tch_ahs_5_15 = {
+ .N = 3,
+ .K = 5,
+ .len = 97,
+ .next_output = tch_ahs_5_15_output,
+ .next_state = tch_ahs_5_15_state,
+ .next_term_output = tch_ahs_5_15_term_output,
+ .next_term_state = tch_ahs_5_15_term_state,
+ .puncture = tch_ahs_5_15_puncture,
+};
+
+static const uint8_t tch_ahs_4_75_state[][2] = {
+ { 0, 1 }, { 2, 3 }, { 5, 4 }, { 7, 6 },
+ { 9, 8 }, { 11, 10 }, { 12, 13 }, { 14, 15 },
+ { 16, 17 }, { 18, 19 }, { 21, 20 }, { 23, 22 },
+ { 25, 24 }, { 27, 26 }, { 28, 29 }, { 30, 31 },
+ { 33, 32 }, { 35, 34 }, { 36, 37 }, { 38, 39 },
+ { 40, 41 }, { 42, 43 }, { 45, 44 }, { 47, 46 },
+ { 49, 48 }, { 51, 50 }, { 52, 53 }, { 54, 55 },
+ { 56, 57 }, { 58, 59 }, { 61, 60 }, { 63, 62 },
+ { 1, 0 }, { 3, 2 }, { 4, 5 }, { 6, 7 },
+ { 8, 9 }, { 10, 11 }, { 13, 12 }, { 15, 14 },
+ { 17, 16 }, { 19, 18 }, { 20, 21 }, { 22, 23 },
+ { 24, 25 }, { 26, 27 }, { 29, 28 }, { 31, 30 },
+ { 32, 33 }, { 34, 35 }, { 37, 36 }, { 39, 38 },
+ { 41, 40 }, { 43, 42 }, { 44, 45 }, { 46, 47 },
+ { 48, 49 }, { 50, 51 }, { 53, 52 }, { 55, 54 },
+ { 57, 56 }, { 59, 58 }, { 60, 61 }, { 62, 63 },
+};
+
+static const uint8_t tch_ahs_4_75_output[][2] = {
+ { 0, 7 }, { 3, 4 }, { 2, 5 }, { 1, 6 },
+ { 2, 5 }, { 1, 6 }, { 0, 7 }, { 3, 4 },
+ { 3, 4 }, { 0, 7 }, { 1, 6 }, { 2, 5 },
+ { 1, 6 }, { 2, 5 }, { 3, 4 }, { 0, 7 },
+ { 3, 4 }, { 0, 7 }, { 1, 6 }, { 2, 5 },
+ { 1, 6 }, { 2, 5 }, { 3, 4 }, { 0, 7 },
+ { 0, 7 }, { 3, 4 }, { 2, 5 }, { 1, 6 },
+ { 2, 5 }, { 1, 6 }, { 0, 7 }, { 3, 4 },
+ { 0, 7 }, { 3, 4 }, { 2, 5 }, { 1, 6 },
+ { 2, 5 }, { 1, 6 }, { 0, 7 }, { 3, 4 },
+ { 3, 4 }, { 0, 7 }, { 1, 6 }, { 2, 5 },
+ { 1, 6 }, { 2, 5 }, { 3, 4 }, { 0, 7 },
+ { 3, 4 }, { 0, 7 }, { 1, 6 }, { 2, 5 },
+ { 1, 6 }, { 2, 5 }, { 3, 4 }, { 0, 7 },
+ { 0, 7 }, { 3, 4 }, { 2, 5 }, { 1, 6 },
+ { 2, 5 }, { 1, 6 }, { 0, 7 }, { 3, 4 },
+};
+
+static const uint8_t tch_ahs_4_75_term_state[] = {
+ 0, 2, 4, 6, 8, 10, 12, 14, 16, 18, 20, 22,
+ 24, 26, 28, 30, 32, 34, 36, 38, 40, 42, 44, 46,
+ 48, 50, 52, 54, 56, 58, 60, 62, 0, 2, 4, 6,
+ 8, 10, 12, 14, 16, 18, 20, 22, 24, 26, 28, 30,
+ 32, 34, 36, 38, 40, 42, 44, 46, 48, 50, 52, 54,
+ 56, 58, 60, 62,
+};
+
+static const uint8_t tch_ahs_4_75_term_output[] = {
+ 0, 3, 5, 6, 5, 6, 0, 3, 3, 0, 6, 5,
+ 6, 5, 3, 0, 4, 7, 1, 2, 1, 2, 4, 7,
+ 7, 4, 2, 1, 2, 1, 7, 4, 7, 4, 2, 1,
+ 2, 1, 7, 4, 4, 7, 1, 2, 1, 2, 4, 7,
+ 3, 0, 6, 5, 6, 5, 3, 0, 0, 3, 5, 6,
+ 5, 6, 0, 3,
+};
+
+static const int tch_ahs_4_75_puncture[] = {
+ 1, 2, 4, 5, 7, 8, 10, 13, 16, 22, 28, 34,
+ 40, 46, 52, 58, 64, 70, 76, 82, 88, 94, 100, 106,
+ 112, 118, 124, 130, 136, 142, 148, 151, 154, 160, 163, 166,
+ 172, 175, 178, 184, 187, 190, 196, 199, 202, 208, 211, 214,
+ 220, 223, 226, 232, 235, 238, 241, 244, 247, 250, 253, 256,
+ 259, 262, 265, 268, 271, 274, 275, 277, 278, 280, 281, 283,
+ 284, -1,
+};
+
+/**
+ * TCH/AHS 4.75 kbits convolutional code
+ */
+const struct osmo_conv_code gsm0503_tch_ahs_4_75 = {
+ .N = 3,
+ .K = 7,
+ .len = 89,
+ .next_output = tch_ahs_4_75_output,
+ .next_state = tch_ahs_4_75_state,
+ .next_term_output = tch_ahs_4_75_term_output,
+ .next_term_state = tch_ahs_4_75_term_state,
+ .puncture = tch_ahs_4_75_puncture,
+};
+
+static const uint8_t mcs1_dl_hdr_state[][2] = {
+ { 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, 51 }, { 52, 53 }, { 54, 55 },
+ { 56, 57 }, { 58, 59 }, { 60, 61 }, { 62, 63 },
+ { 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, 51 }, { 52, 53 }, { 54, 55 },
+ { 56, 57 }, { 58, 59 }, { 60, 61 }, { 62, 63 },
+};
+
+static const uint8_t mcs1_dl_hdr_output[][2] = {
+ { 0, 7 }, { 3, 4 }, { 6, 1 }, { 5, 2 },
+ { 6, 1 }, { 5, 2 }, { 0, 7 }, { 3, 4 },
+ { 1, 6 }, { 2, 5 }, { 7, 0 }, { 4, 3 },
+ { 7, 0 }, { 4, 3 }, { 1, 6 }, { 2, 5 },
+ { 4, 3 }, { 7, 0 }, { 2, 5 }, { 1, 6 },
+ { 2, 5 }, { 1, 6 }, { 4, 3 }, { 7, 0 },
+ { 5, 2 }, { 6, 1 }, { 3, 4 }, { 0, 7 },
+ { 3, 4 }, { 0, 7 }, { 5, 2 }, { 6, 1 },
+ { 7, 0 }, { 4, 3 }, { 1, 6 }, { 2, 5 },
+ { 1, 6 }, { 2, 5 }, { 7, 0 }, { 4, 3 },
+ { 6, 1 }, { 5, 2 }, { 0, 7 }, { 3, 4 },
+ { 0, 7 }, { 3, 4 }, { 6, 1 }, { 5, 2 },
+ { 3, 4 }, { 0, 7 }, { 5, 2 }, { 6, 1 },
+ { 5, 2 }, { 6, 1 }, { 3, 4 }, { 0, 7 },
+ { 2, 5 }, { 1, 6 }, { 4, 3 }, { 7, 0 },
+ { 4, 3 }, { 7, 0 }, { 2, 5 }, { 1, 6 },
+};
+
+/**
+ * EDGE MCS-1 DL header convolutional code:
+ * 42 bits blocks, rate 1/3, k = 7
+ * G4 = 1 + D2 + D3 + D5 + D6
+ * G7 = 1 + D + D2 + D3 + D6
+ * G5 = 1 + D + D4 + D6
+ */
+const struct osmo_conv_code gsm0503_mcs1_dl_hdr = {
+ .N = 3,
+ .K = 7,
+ .len = 36,
+ .next_output = mcs1_dl_hdr_output,
+ .next_state = mcs1_dl_hdr_state,
+ .term = CONV_TERM_TAIL_BITING,
+};
+
+static const uint8_t mcs1_ul_hdr_state[][2] = {
+ { 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, 51 }, { 52, 53 }, { 54, 55 },
+ { 56, 57 }, { 58, 59 }, { 60, 61 }, { 62, 63 },
+ { 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, 51 }, { 52, 53 }, { 54, 55 },
+ { 56, 57 }, { 58, 59 }, { 60, 61 }, { 62, 63 },
+};
+
+static const uint8_t mcs1_ul_hdr_output[][2] = {
+ { 0, 7 }, { 3, 4 }, { 6, 1 }, { 5, 2 },
+ { 6, 1 }, { 5, 2 }, { 0, 7 }, { 3, 4 },
+ { 1, 6 }, { 2, 5 }, { 7, 0 }, { 4, 3 },
+ { 7, 0 }, { 4, 3 }, { 1, 6 }, { 2, 5 },
+ { 4, 3 }, { 7, 0 }, { 2, 5 }, { 1, 6 },
+ { 2, 5 }, { 1, 6 }, { 4, 3 }, { 7, 0 },
+ { 5, 2 }, { 6, 1 }, { 3, 4 }, { 0, 7 },
+ { 3, 4 }, { 0, 7 }, { 5, 2 }, { 6, 1 },
+ { 7, 0 }, { 4, 3 }, { 1, 6 }, { 2, 5 },
+ { 1, 6 }, { 2, 5 }, { 7, 0 }, { 4, 3 },
+ { 6, 1 }, { 5, 2 }, { 0, 7 }, { 3, 4 },
+ { 0, 7 }, { 3, 4 }, { 6, 1 }, { 5, 2 },
+ { 3, 4 }, { 0, 7 }, { 5, 2 }, { 6, 1 },
+ { 5, 2 }, { 6, 1 }, { 3, 4 }, { 0, 7 },
+ { 2, 5 }, { 1, 6 }, { 4, 3 }, { 7, 0 },
+ { 4, 3 }, { 7, 0 }, { 2, 5 }, { 1, 6 },
+};
+
+/**
+ * EDGE MCS-1 UL header convolutional code:
+ * 45 bits blocks, rate 1/3, k = 7
+ * G4 = 1 + D2 + D3 + D5 + D6
+ * G7 = 1 + D + D2 + D3 + D6
+ * G5 = 1 + D + D4 + D6
+ */
+const struct osmo_conv_code gsm0503_mcs1_ul_hdr = {
+ .N = 3,
+ .K = 7,
+ .len = 39,
+ .next_output = mcs1_ul_hdr_output,
+ .next_state = mcs1_ul_hdr_state,
+ .term = CONV_TERM_TAIL_BITING,
+};
+
+static const uint8_t mcs1_state[][2] = {
+ { 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, 51 }, { 52, 53 }, { 54, 55 },
+ { 56, 57 }, { 58, 59 }, { 60, 61 }, { 62, 63 },
+ { 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, 51 }, { 52, 53 }, { 54, 55 },
+ { 56, 57 }, { 58, 59 }, { 60, 61 }, { 62, 63 },
+};
+
+static const uint8_t mcs1_output[][2] = {
+ { 0, 7 }, { 3, 4 }, { 6, 1 }, { 5, 2 },
+ { 6, 1 }, { 5, 2 }, { 0, 7 }, { 3, 4 },
+ { 1, 6 }, { 2, 5 }, { 7, 0 }, { 4, 3 },
+ { 7, 0 }, { 4, 3 }, { 1, 6 }, { 2, 5 },
+ { 4, 3 }, { 7, 0 }, { 2, 5 }, { 1, 6 },
+ { 2, 5 }, { 1, 6 }, { 4, 3 }, { 7, 0 },
+ { 5, 2 }, { 6, 1 }, { 3, 4 }, { 0, 7 },
+ { 3, 4 }, { 0, 7 }, { 5, 2 }, { 6, 1 },
+ { 7, 0 }, { 4, 3 }, { 1, 6 }, { 2, 5 },
+ { 1, 6 }, { 2, 5 }, { 7, 0 }, { 4, 3 },
+ { 6, 1 }, { 5, 2 }, { 0, 7 }, { 3, 4 },
+ { 0, 7 }, { 3, 4 }, { 6, 1 }, { 5, 2 },
+ { 3, 4 }, { 0, 7 }, { 5, 2 }, { 6, 1 },
+ { 5, 2 }, { 6, 1 }, { 3, 4 }, { 0, 7 },
+ { 2, 5 }, { 1, 6 }, { 4, 3 }, { 7, 0 },
+ { 4, 3 }, { 7, 0 }, { 2, 5 }, { 1, 6 },
+};
+
+/**
+ * EDGE MCS-1 data convolutional code:
+ * 196 bits blocks, rate 1/3, k = 7
+ * G4 = 1 + D2 + D3 + D5 + D6
+ * G7 = 1 + D + D2 + D3 + D6
+ * G5 = 1 + D + D4 + D6
+ */
+const struct osmo_conv_code gsm0503_mcs1 = {
+ .N = 3,
+ .K = 7,
+ .len = 190,
+ .next_output = mcs1_output,
+ .next_state = mcs1_state,
+};
+
+static const uint8_t mcs2_state[][2] = {
+ { 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, 51 }, { 52, 53 }, { 54, 55 },
+ { 56, 57 }, { 58, 59 }, { 60, 61 }, { 62, 63 },
+ { 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, 51 }, { 52, 53 }, { 54, 55 },
+ { 56, 57 }, { 58, 59 }, { 60, 61 }, { 62, 63 },
+};
+
+static const uint8_t mcs2_output[][2] = {
+ { 0, 7 }, { 3, 4 }, { 6, 1 }, { 5, 2 },
+ { 6, 1 }, { 5, 2 }, { 0, 7 }, { 3, 4 },
+ { 1, 6 }, { 2, 5 }, { 7, 0 }, { 4, 3 },
+ { 7, 0 }, { 4, 3 }, { 1, 6 }, { 2, 5 },
+ { 4, 3 }, { 7, 0 }, { 2, 5 }, { 1, 6 },
+ { 2, 5 }, { 1, 6 }, { 4, 3 }, { 7, 0 },
+ { 5, 2 }, { 6, 1 }, { 3, 4 }, { 0, 7 },
+ { 3, 4 }, { 0, 7 }, { 5, 2 }, { 6, 1 },
+ { 7, 0 }, { 4, 3 }, { 1, 6 }, { 2, 5 },
+ { 1, 6 }, { 2, 5 }, { 7, 0 }, { 4, 3 },
+ { 6, 1 }, { 5, 2 }, { 0, 7 }, { 3, 4 },
+ { 0, 7 }, { 3, 4 }, { 6, 1 }, { 5, 2 },
+ { 3, 4 }, { 0, 7 }, { 5, 2 }, { 6, 1 },
+ { 5, 2 }, { 6, 1 }, { 3, 4 }, { 0, 7 },
+ { 2, 5 }, { 1, 6 }, { 4, 3 }, { 7, 0 },
+ { 4, 3 }, { 7, 0 }, { 2, 5 }, { 1, 6 },
+};
+
+/**
+ * EDGE MCS-2 data convolutional code:
+ * 244 bits blocks, rate 1/3, k = 7
+ * G4 = 1 + D2 + D3 + D5 + D6
+ * G7 = 1 + D + D2 + D3 + D6
+ * G5 = 1 + D + D4 + D6
+ */
+const struct osmo_conv_code gsm0503_mcs2 = {
+ .N = 3,
+ .K = 7,
+ .len = 238,
+ .next_output = mcs2_output,
+ .next_state = mcs2_state,
+};
+
+static const uint8_t mcs3_state[][2] = {
+ { 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, 51 }, { 52, 53 }, { 54, 55 },
+ { 56, 57 }, { 58, 59 }, { 60, 61 }, { 62, 63 },
+ { 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, 51 }, { 52, 53 }, { 54, 55 },
+ { 56, 57 }, { 58, 59 }, { 60, 61 }, { 62, 63 },
+};
+
+static const uint8_t mcs3_output[][2] = {
+ { 0, 7 }, { 3, 4 }, { 6, 1 }, { 5, 2 },
+ { 6, 1 }, { 5, 2 }, { 0, 7 }, { 3, 4 },
+ { 1, 6 }, { 2, 5 }, { 7, 0 }, { 4, 3 },
+ { 7, 0 }, { 4, 3 }, { 1, 6 }, { 2, 5 },
+ { 4, 3 }, { 7, 0 }, { 2, 5 }, { 1, 6 },
+ { 2, 5 }, { 1, 6 }, { 4, 3 }, { 7, 0 },
+ { 5, 2 }, { 6, 1 }, { 3, 4 }, { 0, 7 },
+ { 3, 4 }, { 0, 7 }, { 5, 2 }, { 6, 1 },
+ { 7, 0 }, { 4, 3 }, { 1, 6 }, { 2, 5 },
+ { 1, 6 }, { 2, 5 }, { 7, 0 }, { 4, 3 },
+ { 6, 1 }, { 5, 2 }, { 0, 7 }, { 3, 4 },
+ { 0, 7 }, { 3, 4 }, { 6, 1 }, { 5, 2 },
+ { 3, 4 }, { 0, 7 }, { 5, 2 }, { 6, 1 },
+ { 5, 2 }, { 6, 1 }, { 3, 4 }, { 0, 7 },
+ { 2, 5 }, { 1, 6 }, { 4, 3 }, { 7, 0 },
+ { 4, 3 }, { 7, 0 }, { 2, 5 }, { 1, 6 },
+};
+
+/**
+ * EDGE MCS-3 data convolutional code:
+ * 316 bits blocks, rate 1/3, k = 7
+ * G4 = 1 + D2 + D3 + D5 + D6
+ * G7 = 1 + D + D2 + D3 + D6
+ * G5 = 1 + D + D4 + D6
+ */
+const struct osmo_conv_code gsm0503_mcs3 = {
+ .N = 3,
+ .K = 7,
+ .len = 310,
+ .next_output = mcs3_output,
+ .next_state = mcs3_state,
+};
+
+static const uint8_t mcs4_state[][2] = {
+ { 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, 51 }, { 52, 53 }, { 54, 55 },
+ { 56, 57 }, { 58, 59 }, { 60, 61 }, { 62, 63 },
+ { 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, 51 }, { 52, 53 }, { 54, 55 },
+ { 56, 57 }, { 58, 59 }, { 60, 61 }, { 62, 63 },
+};
+
+static const uint8_t mcs4_output[][2] = {
+ { 0, 7 }, { 3, 4 }, { 6, 1 }, { 5, 2 },
+ { 6, 1 }, { 5, 2 }, { 0, 7 }, { 3, 4 },
+ { 1, 6 }, { 2, 5 }, { 7, 0 }, { 4, 3 },
+ { 7, 0 }, { 4, 3 }, { 1, 6 }, { 2, 5 },
+ { 4, 3 }, { 7, 0 }, { 2, 5 }, { 1, 6 },
+ { 2, 5 }, { 1, 6 }, { 4, 3 }, { 7, 0 },
+ { 5, 2 }, { 6, 1 }, { 3, 4 }, { 0, 7 },
+ { 3, 4 }, { 0, 7 }, { 5, 2 }, { 6, 1 },
+ { 7, 0 }, { 4, 3 }, { 1, 6 }, { 2, 5 },
+ { 1, 6 }, { 2, 5 }, { 7, 0 }, { 4, 3 },
+ { 6, 1 }, { 5, 2 }, { 0, 7 }, { 3, 4 },
+ { 0, 7 }, { 3, 4 }, { 6, 1 }, { 5, 2 },
+ { 3, 4 }, { 0, 7 }, { 5, 2 }, { 6, 1 },
+ { 5, 2 }, { 6, 1 }, { 3, 4 }, { 0, 7 },
+ { 2, 5 }, { 1, 6 }, { 4, 3 }, { 7, 0 },
+ { 4, 3 }, { 7, 0 }, { 2, 5 }, { 1, 6 },
+};
+
+/**
+ * EDGE MCS-4 data convolutional code:
+ * 372 bits blocks, rate 1/3, k = 7
+ * G4 = 1 + D2 + D3 + D5 + D6
+ * G7 = 1 + D + D2 + D3 + D6
+ * G5 = 1 + D + D4 + D6
+ */
+const struct osmo_conv_code gsm0503_mcs4 = {
+ .N = 3,
+ .K = 7,
+ .len = 366,
+ .next_output = mcs4_output,
+ .next_state = mcs4_state,
+};
+
+static const uint8_t mcs5_dl_hdr_state[][2] = {
+ { 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, 51 }, { 52, 53 }, { 54, 55 },
+ { 56, 57 }, { 58, 59 }, { 60, 61 }, { 62, 63 },
+ { 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, 51 }, { 52, 53 }, { 54, 55 },
+ { 56, 57 }, { 58, 59 }, { 60, 61 }, { 62, 63 },
+};
+
+static const uint8_t mcs5_dl_hdr_output[][2] = {
+ { 0, 7 }, { 3, 4 }, { 6, 1 }, { 5, 2 },
+ { 6, 1 }, { 5, 2 }, { 0, 7 }, { 3, 4 },
+ { 1, 6 }, { 2, 5 }, { 7, 0 }, { 4, 3 },
+ { 7, 0 }, { 4, 3 }, { 1, 6 }, { 2, 5 },
+ { 4, 3 }, { 7, 0 }, { 2, 5 }, { 1, 6 },
+ { 2, 5 }, { 1, 6 }, { 4, 3 }, { 7, 0 },
+ { 5, 2 }, { 6, 1 }, { 3, 4 }, { 0, 7 },
+ { 3, 4 }, { 0, 7 }, { 5, 2 }, { 6, 1 },
+ { 7, 0 }, { 4, 3 }, { 1, 6 }, { 2, 5 },
+ { 1, 6 }, { 2, 5 }, { 7, 0 }, { 4, 3 },
+ { 6, 1 }, { 5, 2 }, { 0, 7 }, { 3, 4 },
+ { 0, 7 }, { 3, 4 }, { 6, 1 }, { 5, 2 },
+ { 3, 4 }, { 0, 7 }, { 5, 2 }, { 6, 1 },
+ { 5, 2 }, { 6, 1 }, { 3, 4 }, { 0, 7 },
+ { 2, 5 }, { 1, 6 }, { 4, 3 }, { 7, 0 },
+ { 4, 3 }, { 7, 0 }, { 2, 5 }, { 1, 6 },
+};
+
+/**
+ * EDGE MCS-5 DL header convolutional code:
+ * 39 bits blocks, rate 1/3, k = 7
+ * G4 = 1 + D2 + D3 + D5 + D6
+ * G7 = 1 + D + D2 + D3 + D6
+ * G5 = 1 + D + D4 + D6
+ */
+const struct osmo_conv_code gsm0503_mcs5_dl_hdr = {
+ .N = 3,
+ .K = 7,
+ .len = 33,
+ .next_output = mcs5_dl_hdr_output,
+ .next_state = mcs5_dl_hdr_state,
+ .term = CONV_TERM_TAIL_BITING,
+};
+
+static const uint8_t mcs5_ul_hdr_state[][2] = {
+ { 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, 51 }, { 52, 53 }, { 54, 55 },
+ { 56, 57 }, { 58, 59 }, { 60, 61 }, { 62, 63 },
+ { 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, 51 }, { 52, 53 }, { 54, 55 },
+ { 56, 57 }, { 58, 59 }, { 60, 61 }, { 62, 63 },
+};
+
+static const uint8_t mcs5_ul_hdr_output[][2] = {
+ { 0, 7 }, { 3, 4 }, { 6, 1 }, { 5, 2 },
+ { 6, 1 }, { 5, 2 }, { 0, 7 }, { 3, 4 },
+ { 1, 6 }, { 2, 5 }, { 7, 0 }, { 4, 3 },
+ { 7, 0 }, { 4, 3 }, { 1, 6 }, { 2, 5 },
+ { 4, 3 }, { 7, 0 }, { 2, 5 }, { 1, 6 },
+ { 2, 5 }, { 1, 6 }, { 4, 3 }, { 7, 0 },
+ { 5, 2 }, { 6, 1 }, { 3, 4 }, { 0, 7 },
+ { 3, 4 }, { 0, 7 }, { 5, 2 }, { 6, 1 },
+ { 7, 0 }, { 4, 3 }, { 1, 6 }, { 2, 5 },
+ { 1, 6 }, { 2, 5 }, { 7, 0 }, { 4, 3 },
+ { 6, 1 }, { 5, 2 }, { 0, 7 }, { 3, 4 },
+ { 0, 7 }, { 3, 4 }, { 6, 1 }, { 5, 2 },
+ { 3, 4 }, { 0, 7 }, { 5, 2 }, { 6, 1 },
+ { 5, 2 }, { 6, 1 }, { 3, 4 }, { 0, 7 },
+ { 2, 5 }, { 1, 6 }, { 4, 3 }, { 7, 0 },
+ { 4, 3 }, { 7, 0 }, { 2, 5 }, { 1, 6 },
+};
+
+/**
+ * EDGE MCS-5 UL header convolutional code:
+ * 51 bits blocks, rate 1/3, k = 7
+ * G4 = 1 + D2 + D3 + D5 + D6
+ * G7 = 1 + D + D2 + D3 + D6
+ * G5 = 1 + D + D4 + D6
+ */
+const struct osmo_conv_code gsm0503_mcs5_ul_hdr = {
+ .N = 3,
+ .K = 7,
+ .len = 45,
+ .next_output = mcs5_ul_hdr_output,
+ .next_state = mcs5_ul_hdr_state,
+ .term = CONV_TERM_TAIL_BITING,
+};
+
+static const uint8_t mcs5_state[][2] = {
+ { 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, 51 }, { 52, 53 }, { 54, 55 },
+ { 56, 57 }, { 58, 59 }, { 60, 61 }, { 62, 63 },
+ { 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, 51 }, { 52, 53 }, { 54, 55 },
+ { 56, 57 }, { 58, 59 }, { 60, 61 }, { 62, 63 },
+};
+
+static const uint8_t mcs5_output[][2] = {
+ { 0, 7 }, { 3, 4 }, { 6, 1 }, { 5, 2 },
+ { 6, 1 }, { 5, 2 }, { 0, 7 }, { 3, 4 },
+ { 1, 6 }, { 2, 5 }, { 7, 0 }, { 4, 3 },
+ { 7, 0 }, { 4, 3 }, { 1, 6 }, { 2, 5 },
+ { 4, 3 }, { 7, 0 }, { 2, 5 }, { 1, 6 },
+ { 2, 5 }, { 1, 6 }, { 4, 3 }, { 7, 0 },
+ { 5, 2 }, { 6, 1 }, { 3, 4 }, { 0, 7 },
+ { 3, 4 }, { 0, 7 }, { 5, 2 }, { 6, 1 },
+ { 7, 0 }, { 4, 3 }, { 1, 6 }, { 2, 5 },
+ { 1, 6 }, { 2, 5 }, { 7, 0 }, { 4, 3 },
+ { 6, 1 }, { 5, 2 }, { 0, 7 }, { 3, 4 },
+ { 0, 7 }, { 3, 4 }, { 6, 1 }, { 5, 2 },
+ { 3, 4 }, { 0, 7 }, { 5, 2 }, { 6, 1 },
+ { 5, 2 }, { 6, 1 }, { 3, 4 }, { 0, 7 },
+ { 2, 5 }, { 1, 6 }, { 4, 3 }, { 7, 0 },
+ { 4, 3 }, { 7, 0 }, { 2, 5 }, { 1, 6 },
+};
+
+/**
+ * EDGE MCS-5 data convolutional code:
+ * 468 bits blocks, rate 1/3, k = 7
+ * G4 = 1 + D2 + D3 + D5 + D6
+ * G7 = 1 + D + D2 + D3 + D6
+ * G5 = 1 + D + D4 + D6
+ */
+const struct osmo_conv_code gsm0503_mcs5 = {
+ .N = 3,
+ .K = 7,
+ .len = 462,
+ .next_output = mcs5_output,
+ .next_state = mcs5_state,
+};
+
+static const uint8_t mcs6_state[][2] = {
+ { 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, 51 }, { 52, 53 }, { 54, 55 },
+ { 56, 57 }, { 58, 59 }, { 60, 61 }, { 62, 63 },
+ { 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, 51 }, { 52, 53 }, { 54, 55 },
+ { 56, 57 }, { 58, 59 }, { 60, 61 }, { 62, 63 },
+};
+
+static const uint8_t mcs6_output[][2] = {
+ { 0, 7 }, { 3, 4 }, { 6, 1 }, { 5, 2 },
+ { 6, 1 }, { 5, 2 }, { 0, 7 }, { 3, 4 },
+ { 1, 6 }, { 2, 5 }, { 7, 0 }, { 4, 3 },
+ { 7, 0 }, { 4, 3 }, { 1, 6 }, { 2, 5 },
+ { 4, 3 }, { 7, 0 }, { 2, 5 }, { 1, 6 },
+ { 2, 5 }, { 1, 6 }, { 4, 3 }, { 7, 0 },
+ { 5, 2 }, { 6, 1 }, { 3, 4 }, { 0, 7 },
+ { 3, 4 }, { 0, 7 }, { 5, 2 }, { 6, 1 },
+ { 7, 0 }, { 4, 3 }, { 1, 6 }, { 2, 5 },
+ { 1, 6 }, { 2, 5 }, { 7, 0 }, { 4, 3 },
+ { 6, 1 }, { 5, 2 }, { 0, 7 }, { 3, 4 },
+ { 0, 7 }, { 3, 4 }, { 6, 1 }, { 5, 2 },
+ { 3, 4 }, { 0, 7 }, { 5, 2 }, { 6, 1 },
+ { 5, 2 }, { 6, 1 }, { 3, 4 }, { 0, 7 },
+ { 2, 5 }, { 1, 6 }, { 4, 3 }, { 7, 0 },
+ { 4, 3 }, { 7, 0 }, { 2, 5 }, { 1, 6 },
+};
+
+/**
+ * EDGE MCS-6 data convolutional code:
+ * 612 bits blocks, rate 1/3, k = 7
+ * G4 = 1 + D2 + D3 + D5 + D6
+ * G7 = 1 + D + D2 + D3 + D6
+ * G5 = 1 + D + D4 + D6
+ */
+const struct osmo_conv_code gsm0503_mcs6 = {
+ .N = 3,
+ .K = 7,
+ .len = 606,
+ .next_output = mcs6_output,
+ .next_state = mcs6_state,
+};
+
+static const uint8_t mcs7_dl_hdr_state[][2] = {
+ { 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, 51 }, { 52, 53 }, { 54, 55 },
+ { 56, 57 }, { 58, 59 }, { 60, 61 }, { 62, 63 },
+ { 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, 51 }, { 52, 53 }, { 54, 55 },
+ { 56, 57 }, { 58, 59 }, { 60, 61 }, { 62, 63 },
+};
+
+static const uint8_t mcs7_dl_hdr_output[][2] = {
+ { 0, 7 }, { 3, 4 }, { 6, 1 }, { 5, 2 },
+ { 6, 1 }, { 5, 2 }, { 0, 7 }, { 3, 4 },
+ { 1, 6 }, { 2, 5 }, { 7, 0 }, { 4, 3 },
+ { 7, 0 }, { 4, 3 }, { 1, 6 }, { 2, 5 },
+ { 4, 3 }, { 7, 0 }, { 2, 5 }, { 1, 6 },
+ { 2, 5 }, { 1, 6 }, { 4, 3 }, { 7, 0 },
+ { 5, 2 }, { 6, 1 }, { 3, 4 }, { 0, 7 },
+ { 3, 4 }, { 0, 7 }, { 5, 2 }, { 6, 1 },
+ { 7, 0 }, { 4, 3 }, { 1, 6 }, { 2, 5 },
+ { 1, 6 }, { 2, 5 }, { 7, 0 }, { 4, 3 },
+ { 6, 1 }, { 5, 2 }, { 0, 7 }, { 3, 4 },
+ { 0, 7 }, { 3, 4 }, { 6, 1 }, { 5, 2 },
+ { 3, 4 }, { 0, 7 }, { 5, 2 }, { 6, 1 },
+ { 5, 2 }, { 6, 1 }, { 3, 4 }, { 0, 7 },
+ { 2, 5 }, { 1, 6 }, { 4, 3 }, { 7, 0 },
+ { 4, 3 }, { 7, 0 }, { 2, 5 }, { 1, 6 },
+};
+
+/**
+ * EDGE MCS-7 DL header convolutional code:
+ * 51 bits blocks, rate 1/3, k = 7
+ * G4 = 1 + D2 + D3 + D5 + D6
+ * G7 = 1 + D + D2 + D3 + D6
+ * G5 = 1 + D + D4 + D6
+ */
+const struct osmo_conv_code gsm0503_mcs7_dl_hdr = {
+ .N = 3,
+ .K = 7,
+ .len = 45,
+ .next_output = mcs7_dl_hdr_output,
+ .next_state = mcs7_dl_hdr_state,
+ .term = CONV_TERM_TAIL_BITING,
+};
+
+static const uint8_t mcs7_ul_hdr_state[][2] = {
+ { 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, 51 }, { 52, 53 }, { 54, 55 },
+ { 56, 57 }, { 58, 59 }, { 60, 61 }, { 62, 63 },
+ { 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, 51 }, { 52, 53 }, { 54, 55 },
+ { 56, 57 }, { 58, 59 }, { 60, 61 }, { 62, 63 },
+};
+
+static const uint8_t mcs7_ul_hdr_output[][2] = {
+ { 0, 7 }, { 3, 4 }, { 6, 1 }, { 5, 2 },
+ { 6, 1 }, { 5, 2 }, { 0, 7 }, { 3, 4 },
+ { 1, 6 }, { 2, 5 }, { 7, 0 }, { 4, 3 },
+ { 7, 0 }, { 4, 3 }, { 1, 6 }, { 2, 5 },
+ { 4, 3 }, { 7, 0 }, { 2, 5 }, { 1, 6 },
+ { 2, 5 }, { 1, 6 }, { 4, 3 }, { 7, 0 },
+ { 5, 2 }, { 6, 1 }, { 3, 4 }, { 0, 7 },
+ { 3, 4 }, { 0, 7 }, { 5, 2 }, { 6, 1 },
+ { 7, 0 }, { 4, 3 }, { 1, 6 }, { 2, 5 },
+ { 1, 6 }, { 2, 5 }, { 7, 0 }, { 4, 3 },
+ { 6, 1 }, { 5, 2 }, { 0, 7 }, { 3, 4 },
+ { 0, 7 }, { 3, 4 }, { 6, 1 }, { 5, 2 },
+ { 3, 4 }, { 0, 7 }, { 5, 2 }, { 6, 1 },
+ { 5, 2 }, { 6, 1 }, { 3, 4 }, { 0, 7 },
+ { 2, 5 }, { 1, 6 }, { 4, 3 }, { 7, 0 },
+ { 4, 3 }, { 7, 0 }, { 2, 5 }, { 1, 6 },
+};
+
+/**
+ * EDGE MCS-7 UL header convolutional code:
+ * 60 bits blocks, rate 1/3, k = 7
+ * G4 = 1 + D2 + D3 + D5 + D6
+ * G7 = 1 + D + D2 + D3 + D6
+ * G5 = 1 + D + D4 + D6
+ */
+const struct osmo_conv_code gsm0503_mcs7_ul_hdr = {
+ .N = 3,
+ .K = 7,
+ .len = 54,
+ .next_output = mcs7_ul_hdr_output,
+ .next_state = mcs7_ul_hdr_state,
+ .term = CONV_TERM_TAIL_BITING,
+};
+
+static const uint8_t mcs7_state[][2] = {
+ { 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, 51 }, { 52, 53 }, { 54, 55 },
+ { 56, 57 }, { 58, 59 }, { 60, 61 }, { 62, 63 },
+ { 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, 51 }, { 52, 53 }, { 54, 55 },
+ { 56, 57 }, { 58, 59 }, { 60, 61 }, { 62, 63 },
+};
+
+static const uint8_t mcs7_output[][2] = {
+ { 0, 7 }, { 3, 4 }, { 6, 1 }, { 5, 2 },
+ { 6, 1 }, { 5, 2 }, { 0, 7 }, { 3, 4 },
+ { 1, 6 }, { 2, 5 }, { 7, 0 }, { 4, 3 },
+ { 7, 0 }, { 4, 3 }, { 1, 6 }, { 2, 5 },
+ { 4, 3 }, { 7, 0 }, { 2, 5 }, { 1, 6 },
+ { 2, 5 }, { 1, 6 }, { 4, 3 }, { 7, 0 },
+ { 5, 2 }, { 6, 1 }, { 3, 4 }, { 0, 7 },
+ { 3, 4 }, { 0, 7 }, { 5, 2 }, { 6, 1 },
+ { 7, 0 }, { 4, 3 }, { 1, 6 }, { 2, 5 },
+ { 1, 6 }, { 2, 5 }, { 7, 0 }, { 4, 3 },
+ { 6, 1 }, { 5, 2 }, { 0, 7 }, { 3, 4 },
+ { 0, 7 }, { 3, 4 }, { 6, 1 }, { 5, 2 },
+ { 3, 4 }, { 0, 7 }, { 5, 2 }, { 6, 1 },
+ { 5, 2 }, { 6, 1 }, { 3, 4 }, { 0, 7 },
+ { 2, 5 }, { 1, 6 }, { 4, 3 }, { 7, 0 },
+ { 4, 3 }, { 7, 0 }, { 2, 5 }, { 1, 6 },
+};
+
+/**
+ * EDGE MCS-7 data convolutional code:
+ * 468 bits blocks, rate 1/3, k = 7
+ * G4 = 1 + D2 + D3 + D5 + D6
+ * G7 = 1 + D + D2 + D3 + D6
+ * G5 = 1 + D + D4 + D6
+ */
+const struct osmo_conv_code gsm0503_mcs7 = {
+ .N = 3,
+ .K = 7,
+ .len = 462,
+ .next_output = mcs7_output,
+ .next_state = mcs7_state,
+};
+
+static const uint8_t mcs8_state[][2] = {
+ { 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, 51 }, { 52, 53 }, { 54, 55 },
+ { 56, 57 }, { 58, 59 }, { 60, 61 }, { 62, 63 },
+ { 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, 51 }, { 52, 53 }, { 54, 55 },
+ { 56, 57 }, { 58, 59 }, { 60, 61 }, { 62, 63 },
+};
+
+static const uint8_t mcs8_output[][2] = {
+ { 0, 7 }, { 3, 4 }, { 6, 1 }, { 5, 2 },
+ { 6, 1 }, { 5, 2 }, { 0, 7 }, { 3, 4 },
+ { 1, 6 }, { 2, 5 }, { 7, 0 }, { 4, 3 },
+ { 7, 0 }, { 4, 3 }, { 1, 6 }, { 2, 5 },
+ { 4, 3 }, { 7, 0 }, { 2, 5 }, { 1, 6 },
+ { 2, 5 }, { 1, 6 }, { 4, 3 }, { 7, 0 },
+ { 5, 2 }, { 6, 1 }, { 3, 4 }, { 0, 7 },
+ { 3, 4 }, { 0, 7 }, { 5, 2 }, { 6, 1 },
+ { 7, 0 }, { 4, 3 }, { 1, 6 }, { 2, 5 },
+ { 1, 6 }, { 2, 5 }, { 7, 0 }, { 4, 3 },
+ { 6, 1 }, { 5, 2 }, { 0, 7 }, { 3, 4 },
+ { 0, 7 }, { 3, 4 }, { 6, 1 }, { 5, 2 },
+ { 3, 4 }, { 0, 7 }, { 5, 2 }, { 6, 1 },
+ { 5, 2 }, { 6, 1 }, { 3, 4 }, { 0, 7 },
+ { 2, 5 }, { 1, 6 }, { 4, 3 }, { 7, 0 },
+ { 4, 3 }, { 7, 0 }, { 2, 5 }, { 1, 6 },
+};
+
+/**
+ * EDGE MCS-8 data convolutional code:
+ * 564 bits blocks, rate 1/3, k = 7
+ * G4 = 1 + D2 + D3 + D5 + D6
+ * G7 = 1 + D + D2 + D3 + D6
+ * G5 = 1 + D + D4 + D6
+ */
+const struct osmo_conv_code gsm0503_mcs8 = {
+ .N = 3,
+ .K = 7,
+ .len = 558,
+ .next_output = mcs8_output,
+ .next_state = mcs8_state,
+};
+
+static const uint8_t mcs9_state[][2] = {
+ { 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, 51 }, { 52, 53 }, { 54, 55 },
+ { 56, 57 }, { 58, 59 }, { 60, 61 }, { 62, 63 },
+ { 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, 51 }, { 52, 53 }, { 54, 55 },
+ { 56, 57 }, { 58, 59 }, { 60, 61 }, { 62, 63 },
+};
+
+static const uint8_t mcs9_output[][2] = {
+ { 0, 7 }, { 3, 4 }, { 6, 1 }, { 5, 2 },
+ { 6, 1 }, { 5, 2 }, { 0, 7 }, { 3, 4 },
+ { 1, 6 }, { 2, 5 }, { 7, 0 }, { 4, 3 },
+ { 7, 0 }, { 4, 3 }, { 1, 6 }, { 2, 5 },
+ { 4, 3 }, { 7, 0 }, { 2, 5 }, { 1, 6 },
+ { 2, 5 }, { 1, 6 }, { 4, 3 }, { 7, 0 },
+ { 5, 2 }, { 6, 1 }, { 3, 4 }, { 0, 7 },
+ { 3, 4 }, { 0, 7 }, { 5, 2 }, { 6, 1 },
+ { 7, 0 }, { 4, 3 }, { 1, 6 }, { 2, 5 },
+ { 1, 6 }, { 2, 5 }, { 7, 0 }, { 4, 3 },
+ { 6, 1 }, { 5, 2 }, { 0, 7 }, { 3, 4 },
+ { 0, 7 }, { 3, 4 }, { 6, 1 }, { 5, 2 },
+ { 3, 4 }, { 0, 7 }, { 5, 2 }, { 6, 1 },
+ { 5, 2 }, { 6, 1 }, { 3, 4 }, { 0, 7 },
+ { 2, 5 }, { 1, 6 }, { 4, 3 }, { 7, 0 },
+ { 4, 3 }, { 7, 0 }, { 2, 5 }, { 1, 6 },
+};
+
+/**
+ * EDGE MCS-9 data convolutional code:
+ * 612 bits blocks, rate 1/3, k = 7
+ * G4 = 1 + D2 + D3 + D5 + D6
+ * G7 = 1 + D + D2 + D3 + D6
+ * G5 = 1 + D + D4 + D6
+ */
+const struct osmo_conv_code gsm0503_mcs9 = {
+ .N = 3,
+ .K = 7,
+ .len = 606,
+ .next_output = mcs9_output,
+ .next_state = mcs9_state,
+};
+
diff --git a/lib/decoding/osmocom/coding/gsm0503_interleaving.c b/lib/decoding/osmocom/coding/gsm0503_interleaving.c
new file mode 100644
index 0000000..d5008d0
--- /dev/null
+++ b/lib/decoding/osmocom/coding/gsm0503_interleaving.c
@@ -0,0 +1,685 @@
+/*
+ * (C) 2013 by Andreas Eversberg <jolly@eversberg.eu>
+ * (C) 2016 by Tom Tsou <tom.tsou@ettus.com>
+ * (C) 2017 by Hrald Welte <laforge@gnumonks.org>
+ *
+ * All Rights Reserved
+ *
+ * SPDX-License-Identifier: GPL-2.0+
+ *
+ * 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 2 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, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+
+#include <stdint.h>
+#include <string.h>
+
+#include <osmocom/core/bits.h>
+#include <osmocom/coding/gsm0503_tables.h>
+#include <osmocom/coding/gsm0503_interleaving.h>
+
+/*! \addtogroup interleaving
+ * @{
+ * GSM TS 05.03 interleaving
+ *
+ * This module contains interleaving / de-interleaving routines for
+ * various channel types, as defined in 3GPP TS 05.03 / 45.003.
+ *
+ * GSM xCCH 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 hl(B) and hn(B) are bits in burst B indicating flags.
+ *
+ * GSM TCH HR/AHS interleaving and burst mapping:
+ *
+ * Interleaving:
+ *
+ * Given 288 coded input bits, form 4 blocks of 114 bits,
+ * where even bits of the first 2 blocks and odd bits of the last 2 blocks
+ * are used:
+ *
+ * i(B, j) = c(n, k) k = 0, ..., 227
+ * n = 0, ..., N, N + 1, ...
+ * B = B_0 + 2n + b
+ * j, b = table[k];
+ *
+ * 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 hl(B) and hn(B) are bits in burst B indicating flags.
+ *
+ * \file gsm0503_interleaving.c */
+
+/*! De-Interleave burst bits according to TS 05.03 4.1.4
+ * \param[out] cB caller-allocated output buffer for 456 soft coded bits
+ * \param[in] iB 456 soft input bits */
+void gsm0503_xcch_deinterleave(sbit_t *cB, const sbit_t *iB)
+{
+ int j, k, B;
+
+ for (k = 0; k < 456; k++) {
+ B = k & 3;
+ j = 2 * ((49 * k) % 57) + ((k & 7) >> 2);
+ cB[k] = iB[B * 114 + j];
+ }
+}
+
+/*! Interleave burst bits according to TS 05.03 4.1.4
+ * \param[out] iB caller-allocated output buffer for 456 soft interleaved bits
+ * \param[in] cB 456 soft input coded bits */
+void gsm0503_xcch_interleave(const ubit_t *cB, ubit_t *iB)
+{
+ int j, k, B;
+
+ for (k = 0; k < 456; k++) {
+ B = k & 3;
+ j = 2 * ((49 * k) % 57) + ((k & 7) >> 2);
+ iB[B * 114 + j] = cB[k];
+ }
+}
+
+/*! De-Interleave MCS1 DL burst bits according to TS 05.03 5.1.5.1.5
+ * \param[out] u caller-allocated output buffer for 12 soft coded bits
+ * \param[out] hc caller-allocated output buffer for 68 soft coded bits
+ * \param[out] dc caller-allocated output buffer for 372 soft coded bits
+ * \param[in] iB 452 interleaved soft input bits */
+void gsm0503_mcs1_dl_deinterleave(sbit_t *u, sbit_t *hc,
+ sbit_t *dc, const sbit_t *iB)
+{
+ int k;
+ sbit_t c[452];
+ sbit_t cp[456];
+
+ gsm0503_xcch_deinterleave(cp, iB);
+
+ for (k = 0; k < 25; k++)
+ c[k] = cp[k];
+ for (k = 26; k < 82; k++)
+ c[k - 1] = cp[k];
+ for (k = 83; k < 139; k++)
+ c[k - 2] = cp[k];
+ for (k = 140; k < 424; k++)
+ c[k - 3] = cp[k];
+ for (k = 425; k < 456; k++)
+ c[k - 4] = cp[k];
+
+ if (u) {
+ for (k = 0; k < 12; k++)
+ u[k] = c[k];
+ }
+
+ if (hc) {
+ for (k = 12; k < 80; k++)
+ hc[k - 12] = c[k];
+ }
+
+ if (dc) {
+ for (k = 80; k < 452; k++)
+ dc[k - 80] = c[k];
+ }
+}
+
+/*! Interleave MCS1 DL burst bits according to TS 05.03 5.1.5.1.5
+ * \param[in] up 12 input soft coded bits (usf)
+ * \param[in] hc 68 input soft coded bits (header)
+ * \param[in] dc 372 input soft bits (data)
+ * \param[out] iB 456 interleaved soft output bits */
+void gsm0503_mcs1_dl_interleave(const ubit_t *up, const ubit_t *hc,
+ const ubit_t *dc, ubit_t *iB)
+{
+ int k;
+ ubit_t c[452];
+ ubit_t cp[456];
+
+ for (k = 0; k < 12; k++)
+ c[k] = up[k];
+ for (k = 12; k < 80; k++)
+ c[k] = hc[k - 12];
+ for (k = 80; k < 452; k++)
+ c[k] = dc[k - 80];
+
+ for (k = 0; k < 25; k++)
+ cp[k] = c[k];
+ for (k = 26; k < 82; k++)
+ cp[k] = c[k - 1];
+ for (k = 83; k < 139; k++)
+ cp[k] = c[k - 2];
+ for (k = 140; k < 424; k++)
+ cp[k] = c[k - 3];
+ for (k = 425; k < 456; k++)
+ cp[k] = c[k - 4];
+
+ cp[25] = 0;
+ cp[82] = 0;
+ cp[139] = 0;
+ cp[424] = 0;
+
+ gsm0503_xcch_interleave(cp, iB);
+}
+
+/*! Interleave MCS1 UL burst bits according to TS 05.03 5.1.5.2.4
+ * \param[out] hc caller-allocated output buffer for 80 soft coded header bits
+ * \param[out] dc caller-allocated output buffer for 372 soft coded data bits
+ * \param[in] iB 456 interleaved soft input bits */
+void gsm0503_mcs1_ul_deinterleave(sbit_t *hc, sbit_t *dc, const sbit_t *iB)
+{
+ int k;
+ sbit_t c[452];
+ sbit_t cp[456];
+
+ gsm0503_xcch_deinterleave(cp, iB);
+
+ for (k = 0; k < 25; k++)
+ c[k] = cp[k];
+ for (k = 26; k < 82; k++)
+ c[k - 1] = cp[k];
+ for (k = 83; k < 139; k++)
+ c[k - 2] = cp[k];
+ for (k = 140; k < 424; k++)
+ c[k - 3] = cp[k];
+ for (k = 425; k < 456; k++)
+ c[k - 4] = cp[k];
+
+ if (hc) {
+ for (k = 0; k < 80; k++)
+ hc[k] = c[k];
+ }
+
+ if (dc) {
+ for (k = 80; k < 452; k++)
+ dc[k - 80] = c[k];
+ }
+}
+
+/*! Interleave MCS1 DL burst bits according to TS 05.03 5.1.5.2.4
+ * \param[in] hc 80 input coded bits (header)
+ * \param[in] dc 372 input bits (data)
+ * \param[out] iB 456 interleaved output bits */
+void gsm0503_mcs1_ul_interleave(const ubit_t *hc, const ubit_t *dc, ubit_t *iB)
+{
+ int k;
+ ubit_t c[452];
+ ubit_t cp[456];
+
+ for (k = 0; k < 80; k++)
+ c[k] = hc[k];
+ for (k = 80; k < 452; k++)
+ c[k] = dc[k - 80];
+
+ for (k = 0; k < 25; k++)
+ cp[k] = c[k];
+ for (k = 26; k < 82; k++)
+ cp[k] = c[k - 1];
+ for (k = 83; k < 139; k++)
+ cp[k] = c[k - 2];
+ for (k = 140; k < 424; k++)
+ cp[k] = c[k - 3];
+ for (k = 425; k < 456; k++)
+ cp[k] = c[k - 4];
+
+ cp[25] = 0;
+ cp[82] = 0;
+ cp[139] = 0;
+ cp[424] = 0;
+
+ gsm0503_xcch_interleave(cp, iB);
+}
+
+/*! Interleave MCS5 UL burst bits according to TS 05.03 5.1.9.2.4
+ * \param[in] hc 136 soft coded header input bits
+ * \param[in] dc 1248 soft coded data input bits
+ * \param[out] hi 136 interleaved header output bits
+ * \param[out] di 1248 interleaved data output bits */
+void gsm0503_mcs5_ul_interleave(const ubit_t *hc, const ubit_t *dc,
+ ubit_t *hi, ubit_t *di)
+{
+ int j, k;
+
+ /* Header */
+ for (k = 0; k < 136; k++) {
+ j = 34 * (k % 4) + 2 * (11 * k % 17) + k % 8 / 4;
+ hi[j] = hc[k];
+ }
+
+ /* Data */
+ for (k = 0; k < 1248; k++) {
+ j = gsm0503_interleave_mcs5[k];
+ di[j] = dc[k];
+ }
+}
+
+/*! De-Interleave MCS5 UL burst bits according to TS 05.03 5.1.9.2.4
+ * \param[out] hc caller-allocated output buffer for 136 soft coded header bits
+ * \param[out] dc caller-allocated output buffer for 1248 soft coded data bits
+ * \param[in] iB interleaved soft input bits */
+void gsm0503_mcs5_ul_deinterleave(sbit_t *hc, sbit_t *dc,
+ const sbit_t *hi, const sbit_t *di)
+{
+ int j, k;
+
+ /* Header */
+ if (hc) {
+ for (k = 0; k < 136; k++) {
+ j = 34 * (k % 4) + 2 * (11 * k % 17) + k % 8 / 4;
+ hc[k] = hi[j];
+ }
+ }
+
+ /* Data */
+ if (dc) {
+ for (k = 0; k < 1248; k++) {
+ j = gsm0503_interleave_mcs5[k];
+ dc[k] = di[j];
+ }
+ }
+}
+
+/*! Interleave MCS5 DL burst bits according to TS 05.03 5.1.9.1.5
+ * \param[in] hc 100 soft coded header input bits
+ * \param[in] dc 1248 soft coded data input bits
+ * \param[out] hi 100 interleaved header output bits
+ * \param[out] di 1248 interleaved data output bits */
+void gsm0503_mcs5_dl_interleave(const ubit_t *hc, const ubit_t *dc,
+ ubit_t *hi, ubit_t *di)
+{
+ int j, k;
+
+ /* Header */
+ for (k = 0; k < 100; k++) {
+ j = 25 * (k % 4) + ((17 * k) % 25);
+ hi[j] = hc[k];
+ }
+
+ /* Data */
+ for (k = 0; k < 1248; k++) {
+ j = gsm0503_interleave_mcs5[k];
+ di[j] = dc[k];
+ }
+}
+
+/*! De-Interleave MCS5 UL burst bits according to TS 05.03 5.1.9.1.5
+ * \param[out] hc caller-allocated output buffer for 100 soft coded header bits
+ * \param[out] dc caller-allocated output buffer for 1248 soft coded data bits
+ * \param[in] iB interleaved soft input bits */
+void gsm0503_mcs5_dl_deinterleave(sbit_t *hc, sbit_t *dc,
+ const sbit_t *hi, const sbit_t *di)
+{
+ int j, k;
+
+ /* Header */
+ if (hc) {
+ for (k = 0; k < 100; k++) {
+ j = 25 * (k % 4) + ((17 * k) % 25);
+ hc[k] = hi[j];
+ }
+ }
+
+ /* Data */
+ if (dc) {
+ for (k = 0; k < 1248; k++) {
+ j = gsm0503_interleave_mcs5[k];
+ dc[k] = di[j];
+ }
+ }
+}
+
+/*! Interleave MCS7 DL burst bits according to TS 05.03 5.1.11.1.5
+ * \param[in] hc 124 soft coded header input bits
+ * \param[in] c1 612 soft coded data input bits
+ * \param[in] c2 612 soft coded data input bits
+ * \param[out] hi 124 interleaved header output bits
+ * \param[out] di 1224 interleaved data output bits */
+void gsm0503_mcs7_dl_interleave(const ubit_t *hc, const ubit_t *c1,
+ const ubit_t *c2, ubit_t *hi, ubit_t *di)
+{
+ int j, k;
+ ubit_t dc[1224];
+
+ /* Header */
+ for (k = 0; k < 124; k++) {
+ j = 31 * (k % 4) + ((17 * k) % 31);
+ hi[j] = hc[k];
+ }
+
+ memcpy(&dc[0], c1, 612);
+ memcpy(&dc[612], c2, 612);
+
+ /* Data */
+ for (k = 0; k < 1224; k++) {
+ j = 306 * (k % 4) + 3 * (44 * k % 102 + k / 4 % 2) +
+ (k + 2 - k / 408) % 3;
+ di[j] = dc[k];
+ }
+}
+
+/*! De-Interleave MCS7 DL burst bits according to TS 05.03 5.1.11.1.5
+ * \param[out] hc caller-allocated output buffer for 124 soft coded header bits
+ * \param[out] c1 caller-allocated output buffer for 612 soft coded data bits
+ * \param[out] c2 caller-allocated output buffer for 612 soft coded data bits
+ * \param[in] hi interleaved soft input header bits
+ * \param[in] di interleaved soft input data bits */
+void gsm0503_mcs7_dl_deinterleave(sbit_t *hc, sbit_t *c1, sbit_t *c2,
+ const sbit_t *hi, const sbit_t *di)
+{
+ int j, k;
+ ubit_t dc[1224];
+
+ /* Header */
+ if (hc) {
+ for (k = 0; k < 124; k++) {
+ j = 31 * (k % 4) + ((17 * k) % 31);
+ hc[k] = hi[j];
+ }
+ }
+
+ /* Data */
+ if (c1 && c2) {
+ for (k = 0; k < 1224; k++) {
+ j = 306 * (k % 4) + 3 * (44 * k % 102 + k / 4 % 2) +
+ (k + 2 - k / 408) % 3;
+ dc[k] = di[j];
+ }
+
+ memcpy(c1, &dc[0], 612);
+ memcpy(c2, &dc[612], 612);
+ }
+}
+
+/*! Interleave MCS7 UL burst bits according to TS 05.03 5.1.11.2.4
+ * \param[in] hc 124 soft coded header input bits
+ * \param[in] c1 612 soft coded data input bits
+ * \param[in] c2 612 soft coded data input bits
+ * \param[out] hi 124 interleaved header output bits
+ * \param[out] di 1224 interleaved data output bits */
+void gsm0503_mcs7_ul_interleave(const ubit_t *hc, const ubit_t *c1,
+ const ubit_t *c2, ubit_t *hi, ubit_t *di)
+{
+ int j, k;
+ ubit_t dc[1224];
+
+ /* Header */
+ for (k = 0; k < 160; k++) {
+ j = 40 * (k % 4) + 2 * (13 * (k / 8) % 20) + k % 8 / 4;
+ hi[j] = hc[k];
+ }
+
+ memcpy(&dc[0], c1, 612);
+ memcpy(&dc[612], c2, 612);
+
+ /* Data */
+ for (k = 0; k < 1224; k++) {
+ j = 306 * (k % 4) + 3 * (44 * k % 102 + k / 4 % 2) +
+ (k + 2 - k / 408) % 3;
+ di[j] = dc[k];
+ }
+}
+
+/*! De-Interleave MCS7 UL burst bits according to TS 05.03 5.1.11.2.4
+ * \param[out] hc caller-allocated output buffer for 160 soft coded header bits
+ * \param[out] c1 caller-allocated output buffer for 612 soft coded data bits
+ * \param[out] c2 caller-allocated output buffer for 612 soft coded data bits
+ * \param[in] hi interleaved soft input header bits
+ * \param[in] di interleaved soft input data bits */
+void gsm0503_mcs7_ul_deinterleave(sbit_t *hc, sbit_t *c1, sbit_t *c2,
+ const sbit_t *hi, const sbit_t *di)
+{
+ int j, k;
+ ubit_t dc[1224];
+
+ /* Header */
+ if (hc) {
+ for (k = 0; k < 160; k++) {
+ j = 40 * (k % 4) + 2 * (13 * (k / 8) % 20) + k % 8 / 4;
+ hc[k] = hi[j];
+ }
+ }
+
+ /* Data */
+ if (c1 && c2) {
+ for (k = 0; k < 1224; k++) {
+ j = 306 * (k % 4) + 3 * (44 * k % 102 + k / 4 % 2) +
+ (k + 2 - k / 408) % 3;
+ dc[k] = di[j];
+ }
+
+ memcpy(c1, &dc[0], 612);
+ memcpy(c2, &dc[612], 612);
+ }
+}
+
+/*! Interleave MCS8 UL burst bits according to TS 05.03 5.1.12.2.4
+ * \param[in] hc 160 soft coded header input bits
+ * \param[in] c1 612 soft coded data input bits
+ * \param[in] c2 612 soft coded data input bits
+ * \param[out] hi 160 interleaved header output bits
+ * \param[out] di 1224 interleaved data output bits */
+void gsm0503_mcs8_ul_interleave(const ubit_t *hc, const ubit_t *c1,
+ const ubit_t *c2, ubit_t *hi, ubit_t *di)
+{
+ int j, k;
+ ubit_t dc[1224];
+
+ /* Header */
+ for (k = 0; k < 160; k++) {
+ j = 40 * (k % 4) + 2 * (13 * (k / 8) % 20) + k % 8 / 4;
+ hi[j] = hc[k];
+ }
+
+ memcpy(&dc[0], c1, 612);
+ memcpy(&dc[612], c2, 612);
+
+ /* Data */
+ for (k = 0; k < 1224; k++) {
+ j = 306 * (2 * (k / 612) + (k % 2)) +
+ 3 * (74 * k % 102 + k / 2 % 2) + (k + 2 - k / 204) % 3;
+ di[j] = dc[k];
+ }
+}
+
+
+/*! De-Interleave MCS8 UL burst bits according to TS 05.03 5.1.12.2.4
+ * \param[out] hc caller-allocated output buffer for 160 soft coded header bits
+ * \param[out] c1 caller-allocated output buffer for 612 soft coded data bits
+ * \param[out] c2 caller-allocated output buffer for 612 soft coded data bits
+ * \param[in] hi interleaved soft input header bits
+ * \param[in] di interleaved soft input data bits */
+void gsm0503_mcs8_ul_deinterleave(sbit_t *hc, sbit_t *c1, sbit_t *c2,
+ const sbit_t *hi, const sbit_t *di)
+{
+ int j, k;
+ ubit_t dc[1224];
+
+ /* Header */
+ if (hc) {
+ for (k = 0; k < 160; k++) {
+ j = 40 * (k % 4) + 2 * (13 * (k / 8) % 20) + k % 8 / 4;
+ hc[k] = hi[j];
+ }
+ }
+
+ /* Data */
+ if (c1 && c2) {
+ for (k = 0; k < 1224; k++) {
+ j = 306 * (2 * (k / 612) + (k % 2)) +
+ 3 * (74 * k % 102 + k / 2 % 2) + (k + 2 - k / 204) % 3;
+ dc[k] = di[j];
+ }
+
+ memcpy(c1, &dc[0], 612);
+ memcpy(c2, &dc[612], 612);
+ }
+}
+
+/*! Interleave MCS8 DL burst bits according to TS 05.03 5.1.12.1.5
+ * \param[in] hc 124 soft coded header input bits
+ * \param[in] c1 612 soft coded data input bits
+ * \param[in] c2 612 soft coded data input bits
+ * \param[out] hi 124 interleaved header output bits
+ * \param[out] di 1224 interleaved data output bits */
+void gsm0503_mcs8_dl_interleave(const ubit_t *hc, const ubit_t *c1,
+ const ubit_t *c2, ubit_t *hi, ubit_t *di)
+{
+ int j, k;
+ ubit_t dc[1224];
+
+ /* Header */
+ for (k = 0; k < 124; k++) {
+ j = 31 * (k % 4) + ((17 * k) % 31);
+ hi[j] = hc[k];
+ }
+
+ memcpy(&dc[0], c1, 612);
+ memcpy(&dc[612], c2, 612);
+
+ /* Data */
+ for (k = 0; k < 1224; k++) {
+ j = 306 * (2 * (k / 612) + (k % 2)) +
+ 3 * (74 * k % 102 + k / 2 % 2) + (k + 2 - k / 204) % 3;
+ di[j] = dc[k];
+ }
+}
+
+/*! De-Interleave MCS8 DL burst bits according to TS 05.03 5.1.12.1.5
+ * \param[out] hc caller-allocated output buffer for 124 soft coded header bits
+ * \param[out] c1 caller-allocated output buffer for 612 soft coded data bits
+ * \param[out] c2 caller-allocated output buffer for 612 soft coded data bits
+ * \param[in] hi interleaved soft input header bits
+ * \param[in] di interleaved soft input data bits */
+void gsm0503_mcs8_dl_deinterleave(sbit_t *hc, sbit_t *c1, sbit_t *c2,
+ const sbit_t *hi, const sbit_t *di)
+{
+ int j, k;
+ ubit_t dc[1224];
+
+ /* Header */
+ if (hc) {
+ for (k = 0; k < 124; k++) {
+ j = 31 * (k % 4) + ((17 * k) % 31);
+ hc[k] = hi[j];
+ }
+ }
+
+ /* Data */
+ if (c1 && c2) {
+ for (k = 0; k < 1224; k++) {
+ j = 306 * (2 * (k / 612) + (k % 2)) +
+ 3 * (74 * k % 102 + k / 2 % 2) + (k + 2 - k / 204) % 3;
+ dc[k] = di[j];
+ }
+
+ memcpy(c1, &dc[0], 612);
+ memcpy(c2, &dc[612], 612);
+ }
+}
+
+/*
+ * GSM TCH FR/EFR/AFS interleaving and burst mapping
+ *
+ * Interleaving:
+ *
+ * Given 456 coded input bits, form 8 blocks of 114 bits,
+ * where even bits of the first 4 blocks and odd bits of the last 4 blocks
+ * are used:
+ *
+ * i(B, j) = c(n, k) k = 0, ..., 455
+ * n = 0, ..., N, N + 1, ...
+ * B = B_0 + 4n + (k mod 8)
+ * 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 hl(B) and hn(B) are bits in burst B indicating flags.
+ */
+
+/*! GSM TCH FR/EFR/AFS De-Interleaving and burst mapping
+ * \param[out] cB caller-allocated buffer for 456 unpacked output bits
+ * \param[in] iB 456 unpacked interleaved input bits */
+void gsm0503_tch_fr_deinterleave(sbit_t *cB, const sbit_t *iB)
+{
+ int j, k, B;
+
+ for (k = 0; k < 456; k++) {
+ B = k & 7;
+ j = 2 * ((49 * k) % 57) + ((k & 7) >> 2);
+ cB[k] = iB[B * 114 + j];
+ }
+}
+
+/*! GSM TCH FR/EFR/AFS Interleaving and burst mapping
+ * \param[in] cB caller-allocated buffer for 456 unpacked input bits
+ * \param[out] iB 456 unpacked interleaved output bits */
+void gsm0503_tch_fr_interleave(const ubit_t *cB, ubit_t *iB)
+{
+ int j, k, B;
+
+ for (k = 0; k < 456; k++) {
+ B = k & 7;
+ j = 2 * ((49 * k) % 57) + ((k & 7) >> 2);
+ iB[B * 114 + j] = cB[k];
+ }
+}
+
+/*! GSM TCH HR/AHS De-Interleaving and burst mapping
+ * \param[out] cB caller-allocated buffer for 228 unpacked output bits
+ * \param[in] iB 228 unpacked interleaved input bits */
+void gsm0503_tch_hr_deinterleave(sbit_t *cB, const sbit_t *iB)
+{
+ int j, k, B;
+
+ for (k = 0; k < 228; k++) {
+ B = gsm0503_tch_hr_interleaving[k][1];
+ j = gsm0503_tch_hr_interleaving[k][0];
+ cB[k] = iB[B * 114 + j];
+ }
+}
+
+/*! GSM TCH HR/AHS Interleaving and burst mapping
+ * \param[in] cB caller-allocated buffer for 228 unpacked input bits
+ * \param[out] iB 228 unpacked interleaved output bits */
+void gsm0503_tch_hr_interleave(const ubit_t *cB, ubit_t *iB)
+{
+ int j, k, B;
+
+ for (k = 0; k < 228; k++) {
+ B = gsm0503_tch_hr_interleaving[k][1];
+ j = gsm0503_tch_hr_interleaving[k][0];
+ iB[B * 114 + j] = cB[k];
+ }
+}
+
+/*! @} */
diff --git a/lib/decoding/osmocom/coding/gsm0503_interleaving.h b/lib/decoding/osmocom/coding/gsm0503_interleaving.h
new file mode 100644
index 0000000..05b5e27
--- /dev/null
+++ b/lib/decoding/osmocom/coding/gsm0503_interleaving.h
@@ -0,0 +1,61 @@
+/*! \file gsm0503_interleaving.h
+ * GSM TS 05.03 interleaving.
+ */
+
+#pragma once
+
+#include <osmocom/core/bits.h>
+
+/*! \addtogroup interleaving
+ * @{
+ * \file gsm0503_interleaving.h */
+
+void gsm0503_xcch_deinterleave(sbit_t *cB, const sbit_t *iB);
+void gsm0503_xcch_interleave(const ubit_t *cB, ubit_t *iB);
+
+void gsm0503_tch_fr_deinterleave(sbit_t *cB, const sbit_t *iB);
+void gsm0503_tch_fr_interleave(const ubit_t *cB, ubit_t *iB);
+
+void gsm0503_tch_hr_deinterleave(sbit_t *cB, const sbit_t *iB);
+void gsm0503_tch_hr_interleave(const ubit_t *cB, ubit_t *iB);
+
+void gsm0503_mcs1_ul_deinterleave(sbit_t *hc, sbit_t *dc, const sbit_t *iB);
+void gsm0503_mcs1_ul_interleave(const ubit_t *hc,
+ const ubit_t *dc, ubit_t *iB);
+
+void gsm0503_mcs1_dl_deinterleave(sbit_t *u, sbit_t *hc,
+ sbit_t *dc, const sbit_t *iB);
+void gsm0503_mcs1_dl_interleave(const ubit_t *up, const ubit_t *hc,
+ const ubit_t *dc, ubit_t *iB);
+
+void gsm0503_mcs5_ul_deinterleave(sbit_t *hc, sbit_t *dc,
+ const sbit_t *hi, const sbit_t *di);
+void gsm0503_mcs5_ul_interleave(const ubit_t *hc, const ubit_t *dc,
+ ubit_t *hi, ubit_t *di);
+
+void gsm0503_mcs5_dl_deinterleave(sbit_t *hc, sbit_t *dc,
+ const sbit_t *hi, const sbit_t *di);
+void gsm0503_mcs5_dl_interleave(const ubit_t *hc, const ubit_t *dc,
+ ubit_t *hi, ubit_t *di);
+
+void gsm0503_mcs7_ul_deinterleave(sbit_t *hc, sbit_t *c1, sbit_t *c2,
+ const sbit_t *hi, const sbit_t *di);
+void gsm0503_mcs7_ul_interleave(const ubit_t *hc, const ubit_t *c1,
+ const ubit_t *c2, ubit_t *hi, ubit_t *di);
+
+void gsm0503_mcs7_dl_deinterleave(sbit_t *hc, sbit_t *c1, sbit_t *c2,
+ const sbit_t *hi, const sbit_t *di);
+void gsm0503_mcs7_dl_interleave(const ubit_t *hc, const ubit_t *c1,
+ const ubit_t *c2, ubit_t *hi, ubit_t *di);
+
+void gsm0503_mcs8_ul_deinterleave(sbit_t *hc, sbit_t *c1, sbit_t *c2,
+ const sbit_t *hi, const sbit_t *di);
+void gsm0503_mcs8_ul_interleave(const ubit_t *hc, const ubit_t *c1,
+ const ubit_t *c2, ubit_t *hi, ubit_t *di);
+
+void gsm0503_mcs8_dl_deinterleave(sbit_t *hc, sbit_t *c1, sbit_t *c2,
+ const sbit_t *hi, const sbit_t *di);
+void gsm0503_mcs8_dl_interleave(const ubit_t *hc, const ubit_t *c1,
+ const ubit_t *c2, ubit_t *hi, ubit_t *di);
+
+/*! @} */
diff --git a/lib/decoding/osmocom/coding/gsm0503_mapping.c b/lib/decoding/osmocom/coding/gsm0503_mapping.c
new file mode 100644
index 0000000..f7532eb
--- /dev/null
+++ b/lib/decoding/osmocom/coding/gsm0503_mapping.c
@@ -0,0 +1,300 @@
+/*
+ * (C) 2013 by Andreas Eversberg <jolly@eversberg.eu>
+ * (C) 2016 by Tom Tsou <tom.tsou@ettus.com>
+ *
+ * All Rights Reserved
+ *
+ * SPDX-License-Identifier: GPL-2.0+
+ *
+ * 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 2 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, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+
+#include <stdint.h>
+#include <string.h>
+
+#include <osmocom/core/bits.h>
+#include <osmocom/coding/gsm0503_mapping.h>
+
+/*! \addtogroup mapping
+ * @{
+ *
+ * GSM TS 05.03 burst mapping
+ *
+ * This module contains burst mapping routines as specified in 3GPP TS
+ * 05.03 / 45.003.
+ *
+ * \file gsm0503_mapping.c */
+
+void gsm0503_xcch_burst_unmap(sbit_t *iB, const sbit_t *eB,
+ sbit_t *hl, sbit_t *hn)
+{
+ memcpy(iB, eB, 57);
+ memcpy(iB + 57, eB + 59, 57);
+
+ if (hl)
+ *hl = eB[57];
+
+ if (hn)
+ *hn = eB[58];
+}
+
+void gsm0503_xcch_burst_map(const ubit_t *iB, ubit_t *eB, const ubit_t *hl,
+ const ubit_t *hn)
+{
+ memcpy(eB, iB, 57);
+ memcpy(eB + 59, iB + 57, 57);
+
+ if (hl)
+ eB[57] = *hl;
+ if (hn)
+ eB[58] = *hn;
+}
+
+void gsm0503_tch_burst_unmap(sbit_t *iB, const sbit_t *eB, sbit_t *h, int odd)
+{
+ int i;
+
+ /* brainfuck: only copy even or odd bits */
+ if (iB) {
+ for (i = odd; i < 57; i += 2)
+ iB[i] = eB[i];
+ for (i = 58 - odd; i < 114; i += 2)
+ iB[i] = eB[i + 2];
+ }
+
+ if (h) {
+ if (!odd)
+ *h = eB[58];
+ else
+ *h = eB[57];
+ }
+}
+
+void gsm0503_tch_burst_map(const ubit_t *iB, ubit_t *eB, const ubit_t *h, int odd)
+{
+ int i;
+
+ /* brainfuck: only copy even or odd bits */
+ if (eB) {
+ for (i = odd; i < 57; i += 2)
+ eB[i] = iB[i];
+ for (i = 58 - odd; i < 114; i += 2)
+ eB[i + 2] = iB[i];
+ if (h)
+ eB[odd ? 57 : 58] = *h;
+ }
+}
+
+void gsm0503_mcs5_dl_burst_map(const ubit_t *di, ubit_t *eB,
+ const ubit_t *hi, const ubit_t *up, int B)
+{
+ int j;
+ int q[8] = { 0, 0, 0, 0, 0, 0, 0, 0, };
+
+ for (j = 0; j < 156; j++)
+ eB[j] = di[312 * B + j];
+ for (j = 156; j < 168; j++)
+ eB[j] = hi[25 * B + j - 156];
+ for (j = 168; j < 174; j++)
+ eB[j] = up[9 * B + j - 168];
+ for (j = 174; j < 176; j++)
+ eB[j] = q[2 * B + j - 174];
+ for (j = 176; j < 179; j++)
+ eB[j] = up[9 * B + j - 170];
+ for (j = 179; j < 192; j++)
+ eB[j] = hi[25 * B + j - 167];
+ for (j = 192; j < 348; j++)
+ eB[j] = di[312 * B + j - 36];
+}
+
+void gsm0503_mcs5_dl_burst_unmap(sbit_t *di, const sbit_t *eB,
+ sbit_t *hi, sbit_t *up, int B)
+{
+ int j;
+
+ for (j = 0; j < 156; j++)
+ di[312 * B + j] = eB[j];
+ for (j = 156; j < 168; j++)
+ hi[25 * B + j - 156] = eB[j];
+ for (j = 168; j < 174; j++)
+ up[9 * B + j - 168] = eB[j];
+
+ for (j = 176; j < 179; j++)
+ up[9 * B + j - 170] = eB[j];
+ for (j = 179; j < 192; j++)
+ hi[25 * B + j - 167] = eB[j];
+ for (j = 192; j < 348; j++)
+ di[312 * B + j - 36] = eB[j];
+}
+
+void gsm0503_mcs5_ul_burst_map(const ubit_t *di, ubit_t *eB,
+ const ubit_t *hi, int B)
+{
+ int j;
+
+ for (j = 0; j < 156; j++)
+ eB[j] = di[312 * B + j];
+ for (j = 156; j < 174; j++)
+ eB[j] = hi[34 * B + j - 156];
+ for (j = 174; j < 176; j++)
+ eB[j] = 0;
+ for (j = 176; j < 192; j++)
+ eB[j] = hi[34 * B + j - 158];
+ for (j = 192; j < 348; j++)
+ eB[j] = di[312 * B + j - 36];
+}
+
+void gsm0503_mcs5_ul_burst_unmap(sbit_t *di, const sbit_t *eB,
+ sbit_t *hi, int B)
+{
+ int j;
+
+ for (j = 0; j < 156; j++)
+ di[312 * B + j] = eB[j];
+ for (j = 156; j < 174; j++)
+ hi[34 * B + j - 156] = eB[j];
+ for (j = 176; j < 192; j++)
+ hi[34 * B + j - 158] = eB[j];
+ for (j = 192; j < 348; j++)
+ di[312 * B + j - 36] = eB[j];
+}
+
+void gsm0503_mcs7_dl_burst_map(const ubit_t *di, ubit_t *eB,
+ const ubit_t *hi, const ubit_t *up, int B)
+{
+ int j;
+ int q[8] = { 1, 1, 1, 0, 0, 1, 1, 1, };
+
+ for (j = 0; j < 153; j++)
+ eB[j] = di[306 * B + j];
+ for (j = 153; j < 168; j++)
+ eB[j] = hi[31 * B + j - 153];
+ for (j = 168; j < 174; j++)
+ eB[j] = up[9 * B + j - 168];
+ for (j = 174; j < 176; j++)
+ eB[j] = q[2 * B + j - 174];
+ for (j = 176; j < 179; j++)
+ eB[j] = up[9 * B + j - 170];
+ for (j = 179; j < 195; j++)
+ eB[j] = hi[31 * B + j - 164];
+ for (j = 195; j < 348; j++)
+ eB[j] = di[306 * B + j - 42];
+}
+
+void gsm0503_mcs7_dl_burst_unmap(sbit_t *di, const sbit_t *eB,
+ sbit_t *hi, sbit_t *up, int B)
+{
+ int j;
+
+ for (j = 0; j < 153; j++)
+ di[306 * B + j] = eB[j];
+ for (j = 153; j < 168; j++)
+ hi[31 * B + j - 153] = eB[j];
+ for (j = 168; j < 174; j++)
+ up[9 * B + j - 168] = eB[j];
+
+ for (j = 176; j < 179; j++)
+ up[9 * B + j - 170] = eB[j];
+ for (j = 179; j < 195; j++)
+ hi[31 * B + j - 164] = eB[j];
+ for (j = 195; j < 348; j++)
+ di[306 * B + j - 42] = eB[j];
+}
+
+void gsm0503_mcs7_ul_burst_map(const ubit_t *di, ubit_t *eB,
+ const ubit_t *hi, int B)
+{
+ int j;
+ int q[8] = { 1, 1, 1, 0, 0, 1, 1, 1, };
+
+ for (j = 0; j < 153; j++)
+ eB[j] = di[306 * B + j];
+ for (j = 153; j < 174; j++)
+ eB[j] = hi[40 * B + j - 153];
+ for (j = 174; j < 176; j++)
+ eB[j] = q[2 * B + j - 174];
+ for (j = 176; j < 195; j++)
+ eB[j] = hi[40 * B + j - 155];
+ for (j = 195; j < 348; j++)
+ eB[j] = di[306 * B + j - 42];
+}
+
+void gsm0503_mcs7_ul_burst_unmap(sbit_t *di, const sbit_t *eB,
+ sbit_t *hi, int B)
+{
+ int j;
+
+ for (j = 0; j < 153; j++)
+ di[306 * B + j] = eB[j];
+ for (j = 153; j < 174; j++)
+ hi[40 * B + j - 153] = eB[j];
+
+ for (j = 176; j < 195; j++)
+ hi[40 * B + j - 155] = eB[j];
+ for (j = 195; j < 348; j++)
+ di[306 * B + j - 42] = eB[j];
+}
+
+void gsm0503_mcs5_burst_swap(sbit_t *eB)
+{
+ sbit_t t[14];
+
+ t[0] = eB[155];
+ t[1] = eB[158];
+ t[2] = eB[161];
+ t[3] = eB[164];
+ t[4] = eB[167];
+ t[5] = eB[170];
+ t[6] = eB[173];
+ t[7] = eB[195];
+ t[8] = eB[196];
+ t[9] = eB[198];
+ t[10] = eB[199];
+ t[11] = eB[201];
+ t[12] = eB[202];
+ t[13] = eB[204];
+
+ eB[155] = eB[142];
+ eB[158] = eB[144];
+ eB[161] = eB[145];
+ eB[164] = eB[147];
+ eB[167] = eB[148];
+ eB[170] = eB[150];
+ eB[173] = eB[151];
+ eB[195] = eB[176];
+ eB[196] = eB[179];
+ eB[198] = eB[182];
+ eB[199] = eB[185];
+ eB[201] = eB[188];
+ eB[202] = eB[191];
+ eB[204] = eB[194];
+
+ eB[142] = t[0];
+ eB[144] = t[1];
+ eB[145] = t[2];
+ eB[147] = t[3];
+ eB[148] = t[4];
+ eB[150] = t[5];
+ eB[151] = t[6];
+ eB[176] = t[7];
+ eB[179] = t[8];
+ eB[182] = t[9];
+ eB[185] = t[10];
+ eB[188] = t[11];
+ eB[191] = t[12];
+ eB[194] = t[13];
+}
+
+/*! @} */
diff --git a/lib/decoding/osmocom/coding/gsm0503_mapping.h b/lib/decoding/osmocom/coding/gsm0503_mapping.h
new file mode 100644
index 0000000..f114853
--- /dev/null
+++ b/lib/decoding/osmocom/coding/gsm0503_mapping.h
@@ -0,0 +1,44 @@
+/*! \file gsm0503_mapping.c
+ * GSM TS 05.03 burst mapping.
+ */
+
+#pragma once
+
+#include <osmocom/core/bits.h>
+//#include "bits.h"
+
+/*! \addtogroup mapping
+ * @{
+ * \file gsm0503_mapping.h */
+
+void gsm0503_xcch_burst_unmap(sbit_t *iB, const sbit_t *eB,
+ sbit_t *hl, sbit_t *hn);
+void gsm0503_xcch_burst_map(const ubit_t *iB, ubit_t *eB, const ubit_t *hl,
+ const ubit_t *hn);
+
+void gsm0503_tch_burst_unmap(sbit_t *iB, const sbit_t *eB, sbit_t *h, int odd);
+void gsm0503_tch_burst_map(const ubit_t *iB, ubit_t *eB, const ubit_t *h, int odd);
+
+void gsm0503_mcs5_ul_burst_map(const ubit_t *di, ubit_t *eB,
+ const ubit_t *hi, int B);
+void gsm0503_mcs5_ul_burst_unmap(sbit_t *di, const sbit_t *eB,
+ sbit_t *hi, int B);
+
+void gsm0503_mcs7_ul_burst_map(const ubit_t *di, ubit_t *eB,
+ const ubit_t *hi, int B);
+void gsm0503_mcs7_ul_burst_unmap(sbit_t *di, const sbit_t *eB,
+ sbit_t *hi, int B);
+
+void gsm0503_mcs5_dl_burst_map(const ubit_t *di, ubit_t *eB,
+ const ubit_t *hi, const ubit_t *up, int B);
+void gsm0503_mcs5_dl_burst_unmap(sbit_t *di, const sbit_t *eB,
+ sbit_t *hi, sbit_t *up, int B);
+
+void gsm0503_mcs7_dl_burst_map(const ubit_t *di, ubit_t *eB,
+ const ubit_t *hi, const ubit_t *up, int B);
+void gsm0503_mcs7_dl_burst_unmap(sbit_t *di, const sbit_t *eB,
+ sbit_t *hi, sbit_t *up, int B);
+
+void gsm0503_mcs5_burst_swap(sbit_t *eB);
+
+/*! @} */
diff --git a/lib/decoding/osmocom/coding/gsm0503_parity.c b/lib/decoding/osmocom/coding/gsm0503_parity.c
new file mode 100644
index 0000000..874114f
--- /dev/null
+++ b/lib/decoding/osmocom/coding/gsm0503_parity.c
@@ -0,0 +1,137 @@
+/*
+ * (C) 2013 by Andreas Eversberg <jolly@eversberg.eu>
+ * (C) 2016 by Tom Tsou <tom.tsou@ettus.com>
+ *
+ * All Rights Reserved
+ *
+ * SPDX-License-Identifier: GPL-2.0+
+ *
+ * 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 2 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, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+
+#include <stdint.h>
+
+#include <osmocom/core/crcgen.h>
+#include <osmocom/coding/gsm0503_parity.h>
+
+/*! \addtogroup parity
+ * @{
+ *
+ * GSM TS 05.03 parity
+ *
+ * This module contains parity/crc code definitions for the various
+ * parity/crc schemes as defined in 3GPP TS 05.03 / 45.003
+ *
+ * \file gsm0503_parity.c */
+
+/*! GSM (SACCH) parity (FIRE code)
+ *
+ * g(x) = (x^23 + 1)(x^17 + x^3 + 1)
+ * = x^40 + x^26 + x^23 + x^17 + x^3 + a1
+ */
+const struct osmo_crc64gen_code gsm0503_fire_crc40 = {
+ .bits = 40,
+ .poly = 0x0004820009ULL,
+ .init = 0x0000000000ULL,
+ .remainder = 0xffffffffffULL,
+};
+
+/*! GSM PDTCH CS-2, CS-3, CS-4 parity
+ *
+ * g(x) = x^16 + x^12 + x^5 + 1
+ */
+const struct osmo_crc16gen_code gsm0503_cs234_crc16 = {
+ .bits = 16,
+ .poly = 0x1021,
+ .init = 0x0000,
+ .remainder = 0xffff,
+};
+
+/*! EDGE MCS header parity
+ *
+ */
+const struct osmo_crc8gen_code gsm0503_mcs_crc8_hdr = {
+ .bits = 8,
+ .poly = 0x49,
+ .init = 0x00,
+ .remainder = 0xff,
+};
+
+/*! EDGE MCS data parity
+ *
+ */
+const struct osmo_crc16gen_code gsm0503_mcs_crc12 = {
+ .bits = 12,
+ .poly = 0x0d31,
+ .init = 0x0000,
+ .remainder = 0x0fff,
+};
+
+/*! GSM RACH parity
+ *
+ * g(x) = x^6 + x^5 + x^3 + x^2 + x^1 + 1
+ */
+const struct osmo_crc8gen_code gsm0503_rach_crc6 = {
+ .bits = 6,
+ .poly = 0x2f,
+ .init = 0x00,
+ .remainder = 0x3f,
+};
+
+/*! GSM SCH parity
+ *
+ * g(x) = x^10 + x^8 + x^6 + x^5 + x^4 + x^2 + 1
+ */
+const struct osmo_crc16gen_code gsm0503_sch_crc10 = {
+ .bits = 10,
+ .poly = 0x175,
+ .init = 0x000,
+ .remainder = 0x3ff,
+};
+
+/*! GSM TCH FR/HR/EFR parity
+ *
+ * g(x) = x^3 + x + 1
+ */
+const struct osmo_crc8gen_code gsm0503_tch_fr_crc3 = {
+ .bits = 3,
+ .poly = 0x3,
+ .init = 0x0,
+ .remainder = 0x7,
+};
+
+/*! GSM TCH EFR parity
+ *
+ * g(x) = x^8 + x^4 + x^3 + x^2 + 1
+ */
+const struct osmo_crc8gen_code gsm0503_tch_efr_crc8 = {
+ .bits = 8,
+ .poly = 0x1d,
+ .init = 0x00,
+ .remainder = 0x00,
+};
+
+/*! GSM AMR parity
+ *
+ * g(x) = x^6 + x^5 + x^3 + x^2 + x^1 + 1
+ */
+const struct osmo_crc8gen_code gsm0503_amr_crc6 = {
+ .bits = 6,
+ .poly = 0x2f,
+ .init = 0x00,
+ .remainder = 0x3f,
+};
+
+/*! @} */
diff --git a/lib/decoding/osmocom/coding/gsm0503_parity.h b/lib/decoding/osmocom/coding/gsm0503_parity.h
new file mode 100644
index 0000000..28a5444
--- /dev/null
+++ b/lib/decoding/osmocom/coding/gsm0503_parity.h
@@ -0,0 +1,23 @@
+/*! \file gsm0503_parity.h
+ * GSM TS 05.03 parity.
+ */
+
+#pragma once
+
+#include <osmocom/core/crcgen.h>
+
+/*! \addtogroup parity
+ * @{
+ * \file gsm0503_parity.h */
+
+const struct osmo_crc64gen_code gsm0503_fire_crc40;
+const struct osmo_crc16gen_code gsm0503_cs234_crc16;
+const struct osmo_crc8gen_code gsm0503_mcs_crc8_hdr;
+const struct osmo_crc16gen_code gsm0503_mcs_crc12;
+const struct osmo_crc8gen_code gsm0503_rach_crc6;
+const struct osmo_crc16gen_code gsm0503_sch_crc10;
+const struct osmo_crc8gen_code gsm0503_tch_fr_crc3;
+const struct osmo_crc8gen_code gsm0503_tch_efr_crc8;
+const struct osmo_crc8gen_code gsm0503_amr_crc6;
+
+/*! @} */
diff --git a/lib/decoding/osmocom/coding/gsm0503_tables.c b/lib/decoding/osmocom/coding/gsm0503_tables.c
new file mode 100644
index 0000000..5fe634b
--- /dev/null
+++ b/lib/decoding/osmocom/coding/gsm0503_tables.c
@@ -0,0 +1,1747 @@
+/*
+ * (C) 2013 by Andreas Eversberg <jolly@eversberg.eu>
+ * (C) 2016 by Tom Tsou <tom.tsou@ettus.com>
+ *
+ * All Rights Reserved
+ *
+ * SPDX-License-Identifier: GPL-2.0+
+ *
+ * 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 2 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, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+
+#include <stdint.h>
+
+#include <osmocom/core/bits.h>
+#include <osmocom/coding/gsm0503_tables.h>
+
+/*! \addtogroup tables
+ * @{
+ *
+ * GSM TS 05.03 tables.
+ *
+ * This module contains various tables defining parts of 3GPP TS 05.03
+ * / 45.003, primarily for the purpose of (de)puncturing, interleaving,
+ * etc.
+ *
+ * \file gsm0503_tables.c */
+
+const ubit_t gsm0503_pdtch_hl_hn_ubit[4][8] = {
+ { 1,1, 1,1, 1,1, 1,1 },
+ { 1,1, 0,0, 1,0, 0,0 },
+ { 0,0, 1,0, 0,0, 0,1 },
+ { 0,0, 0,1, 0,1, 1,0 },
+};
+
+const ubit_t gsm0503_pdtch_edge_hl_hn_ubit[3][8] = {
+ { 0,0, 0,1, 0,1, 1,0 },
+ { 0,0, 0,0, 0,0, 0,0 },
+ { 1,1, 1,0, 0,1, 1,1 },
+};
+
+const sbit_t gsm0503_pdtch_hl_hn_sbit[4][8] = {
+ { -127,-127, -127,-127, -127,-127, -127,-127 },
+ { -127,-127, 127, 127, -127, 127, 127, 127 },
+ { 127, 127, -127, 127, 127, 127, 127,-127 },
+ { 127, 127, 127,-127, 127,-127, -127, 127 },
+};
+
+const sbit_t gsm0503_pdtch_edge_hl_hn_sbit[3][8] = {
+ { 127, 127, 127,-127, 127,-127, -127, 127 },
+ { 127, 127, 127, 127, 127, 127, 127, 127 },
+ { -127,-127, -127, 127, 127,-127, -127,-127 },
+};
+
+const ubit_t gsm0503_usf2six[8][6] = {
+ { 0,0,0, 0,0,0 },
+ { 1,0,0, 1,0,1 },
+ { 0,1,0, 1,1,0 },
+ { 1,1,0, 0,1,1 },
+ { 0,0,1, 0,1,1 },
+ { 1,0,1, 1,1,0 },
+ { 0,1,1, 1,0,1 },
+ { 1,1,1, 0,0,0 },
+};
+
+const ubit_t gsm0503_usf2twelve_ubit[8][12] = {
+ { 0,0,0, 0,0,0, 0,0,0, 0,0,0 },
+ { 1,1,0, 1,0,0, 0,0,1, 0,1,1 },
+ { 0,0,1, 1,0,1, 1,1,0, 1,1,0 },
+ { 1,1,1, 0,0,1, 1,1,1, 1,0,1 },
+ { 0,0,0, 0,1,1, 0,1,1, 1,0,1 },
+ { 1,1,0, 1,1,1, 0,1,0, 1,1,0 },
+ { 0,0,1, 1,1,0, 1,0,1, 0,1,1 },
+ { 1,1,1, 0,1,0, 1,0,0, 0,0,0 },
+};
+
+const sbit_t gsm0503_usf2twelve_sbit[8][12] = {
+ { 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127 },
+ { -127,-127, 127, -127, 127, 127, 127, 127,-127, 127,-127,-127 },
+ { 127, 127,-127, -127, 127,-127, -127,-127, 127, -127,-127, 127 },
+ { -127,-127,-127, 127, 127,-127, -127,-127,-127, -127, 127,-127 },
+ { 127, 127, 127, 127,-127,-127, 127,-127,-127, -127, 127,-127 },
+ { -127,-127, 127, -127,-127,-127, 127,-127, 127, -127,-127, 127 },
+ { 127, 127,-127, -127,-127, 127, -127, 127,-127, 127,-127,-127 },
+ { -127,-127,-127, 127,-127, 127, -127, 127, 127, 127, 127, 127 },
+};
+
+const uint8_t gsm0503_puncture_cs2[588] = {
+ 0,0,0,0, 0,0,0,0, 0,0,0,0, 0,0,0,1, 0,0,0,1, 0,0,0,1, 0,0,0,1, 0,0,0,1,
+ 0,0,0,1, 0,0,0,0, 0,0,0,1, 0,0,0,1, 0,0,0,1, 0,0,0,1, 0,0,0,1, 0,0,0,1,
+ 0,0,0,1, 0,0,0,1, 0,0,0,1, 0,0,0,1, 0,0,0,1, 0,0,0,0, 0,0,0,1, 0,0,0,1,
+ 0,0,0,1, 0,0,0,1, 0,0,0,1, 0,0,0,1, 0,0,0,1, 0,0,0,1, 0,0,0,1, 0,0,0,1,
+ 0,0,0,1, 0,0,0,0, 0,0,0,1, 0,0,0,1, 0,0,0,1, 0,0,0,1, 0,0,0,1, 0,0,0,1,
+ 0,0,0,1, 0,0,0,1, 0,0,0,1, 0,0,0,1, 0,0,0,1, 0,0,0,0, 0,0,0,1, 0,0,0,1,
+ 0,0,0,1, 0,0,0,1, 0,0,0,1, 0,0,0,1, 0,0,0,1, 0,0,0,1, 0,0,0,1, 0,0,0,1,
+ 0,0,0,1, 0,0,0,0, 0,0,0,1, 0,0,0,1, 0,0,0,1, 0,0,0,1, 0,0,0,1, 0,0,0,1,
+ 0,0,0,1, 0,0,0,1, 0,0,0,1, 0,0,0,1, 0,0,0,1, 0,0,0,0, 0,0,0,1, 0,0,0,1,
+ 0,0,0,1, 0,0,0,1, 0,0,0,1, 0,0,0,1, 0,0,0,1, 0,0,0,1, 0,0,0,1, 0,0,0,1,
+ 0,0,0,1, 0,0,0,0, 0,0,0,1, 0,0,0,1, 0,0,0,1, 0,0,0,1, 0,0,0,1, 0,0,0,1,
+ 0,0,0,1, 0,0,0,1, 0,0,0,1, 0,0,0,1, 0,0,0,1, 0,0,0,0, 0,0,0,1, 0,0,0,1,
+ 0,0,0,1, 0,0,0,1, 0,0,0,1, 0,0,0,1, 0,0,0,1, 0,0,0,1, 0,0,0,1, 0,0,0,1,
+ 0,0,0,1, 0,0,0,0, 0,0,0,1, 0,0,0,1, 0,0,0,1, 0,0,0,1, 0,0,0,1, 0,0,0,1,
+ 0,0,0,1, 0,0,0,1, 0,0,0,1, 0,0,0,1, 0,0,0,1, 0,0,0,0, 0,0,0,1, 0,0,0,1,
+ 0,0,0,1, 0,0,0,1, 0,0,0,1, 0,0,0,1, 0,0,0,1, 0,0,0,1, 0,0,0,1, 0,0,0,1,
+ 0,0,0,1, 0,0,0,0, 0,0,0,1, 0,0,0,1, 0,0,0,1, 0,0,0,1, 0,0,0,1, 0,0,0,1,
+ 0,0,0,1, 0,0,0,1, 0,0,0,1, 0,0,0,1, 0,0,0,1, 0,0,0,0, 0,0,0,1, 0,0,0,1,
+ 0,0,0,1, 0,0,0,1, 0,0,0,1
+};
+
+const uint8_t gsm0503_puncture_cs3[676] = {
+ 0,0,0,0,0,0, 0,0,0,0,0,0, 0,0,0,1,0,1, 0,0,0,1,0,1, 0,0,0,1,0,1,
+ 0,0,0,1,0,1, 0,0,0,1,0,1, 0,0,0,1,0,1, 0,0,0,1,0,1, 0,0,0,1,0,1,
+ 0,0,0,1,0,1, 0,0,0,1,0,1, 0,0,0,1,0,1, 0,0,0,1,0,1, 0,0,0,1,0,1,
+ 0,0,0,1,0,1, 0,0,0,1,0,1, 0,0,0,1,0,1, 0,0,0,1,0,1, 0,0,0,1,0,1,
+ 0,0,0,1,0,1, 0,0,0,1,0,1, 0,0,0,1,0,1, 0,0,0,1,0,1, 0,0,0,1,0,1,
+ 0,0,0,1,0,1, 0,0,0,1,0,1, 0,0,0,1,0,1, 0,0,0,1,0,1, 0,0,0,1,0,1,
+ 0,0,0,1,0,1, 0,0,0,1,0,1, 0,0,0,1,0,1, 0,0,0,1,0,1, 0,0,0,1,0,1,
+ 0,0,0,1,0,1, 0,0,0,1,0,1, 0,0,0,1,0,1, 0,0,0,1,0,1, 0,0,0,1,0,1,
+ 0,0,0,1,0,1, 0,0,0,1,0,1, 0,0,0,1,0,1, 0,0,0,1,0,1, 0,0,0,1,0,1,
+ 0,0,0,1,0,1, 0,0,0,1,0,1, 0,0,0,1,0,1, 0,0,0,1,0,1, 0,0,0,1,0,1,
+ 0,0,0,1,0,1, 0,0,0,1,0,1, 0,0,0,1,0,1, 0,0,0,1,0,1, 0,0,0,1,0,1,
+ 0,0,0,1,0,1, 0,0,0,1,0,1, 0,0,0,1,0,1, 0,0,0,1,0,1, 0,0,0,1,0,1,
+ 0,0,0,1,0,1, 0,0,0,1,0,1, 0,0,0,1,0,1, 0,0,0,1,0,1, 0,0,0,1,0,1,
+ 0,0,0,1,0,1, 0,0,0,1,0,1, 0,0,0,1,0,1, 0,0,0,1,0,1, 0,0,0,1,0,1,
+ 0,0,0,1,0,1, 0,0,0,1,0,1, 0,0,0,1,0,1, 0,0,0,1,0,1, 0,0,0,1,0,1,
+ 0,0,0,1,0,1, 0,0,0,1,0,1, 0,0,0,1,0,1, 0,0,0,1,0,1, 0,0,0,1,0,1,
+ 0,0,0,1,0,1, 0,0,0,1,0,1, 0,0,0,1,0,1, 0,0,0,1,0,1, 0,0,0,1,0,1,
+ 0,0,0,1,0,1, 0,0,0,1,0,1, 0,0,0,1,0,1, 0,0,0,1,0,1, 0,0,0,1,0,1,
+ 0,0,0,1,0,1, 0,0,0,1,0,1, 0,0,0,1,0,1, 0,0,0,1,0,1, 0,0,0,1,0,1,
+ 0,0,0,1,0,1, 0,0,0,1,0,1, 0,0,0,1,0,1, 0,0,0,1,0,1, 0,0,0,1,0,1,
+ 0,0,0,1,0,1, 0,0,0,1,0,1, 0,0,0,1,0,1, 0,0,0,1,0,1, 0,0,0,1,0,1,
+ 0,0,0,1,0,1, 0,0,0,1,0,1, 0,0,0,1,0,1, 0,0,0,1,0,1, 0,0,0,1,0,1,
+ 0,0,0,1,0,1, 0,0,0,1,0,1, 0,0,0,0
+};
+
+const uint8_t gsm0503_puncture_mcs1_dl_hdr[108] = {
+ 0,0,1, 0,0,1, 0,0,1, 0,0,1, 0,0,1, 0,0,1, 0,0,1, 0,0,1, 0,0,1, 0,0,1,
+ 0,0,1, 0,1,1, 0,0,1, 0,0,1, 0,0,1, 0,0,1, 0,0,1, 0,0,1, 0,0,1, 0,1,1,
+ 0,0,1, 0,0,1, 0,0,1, 0,0,1, 0,0,1, 0,0,1, 0,0,1, 0,1,1, 0,0,1, 0,0,1,
+ 0,0,1, 0,0,1, 0,0,1, 0,0,1, 0,0,1, 0,1,1,
+};
+
+const uint8_t gsm0503_puncture_mcs1_ul_hdr[117] = {
+ 0,0,0,0,0,1,0,0,1,0,0,1,
+ 0,0,0,0,0,1,0,0,1,0,0,1,
+ 0,0,1,0,0,1,0,0,1,0,0,1,
+ 0,0,1,0,0,1,0,0,1,0,0,1,
+ 0,0,1,0,0,1,0,0,1,0,0,1,
+ 0,0,1,0,0,1,0,0,1,0,0,1,
+ 0,0,1,0,0,1,0,0,1,0,0,1,
+ 0,0,1,0,0,1,0,0,1,0,0,1,
+ 0,0,1,0,0,1,0,0,1,0,0,1,
+ 0,0,1,0,0,1,0,0,1,
+};
+
+const uint8_t gsm0503_puncture_mcs1_p1[588] = {
+ 0,0,1,0,0,1,0,0,1,0,1,1,0,0,1,0,0,1,0,0,1,
+ 0,0,1,0,0,1,0,0,1,0,1,1,0,0,1,0,0,1,0,0,1,
+ 0,0,1,0,0,1,0,0,1,0,1,1,0,0,1,0,0,1,0,0,1,
+ 0,0,1,0,0,1,0,0,0,0,1,1,0,0,1,0,0,1,0,0,1,
+ 0,0,1,0,0,1,0,0,1,0,1,1,0,0,1,0,0,1,0,0,1,
+ 0,0,1,0,0,1,0,0,1,0,1,1,0,0,1,0,0,1,0,0,1,
+ 0,0,1,0,0,1,0,0,1,0,0,1,0,0,1,0,0,1,0,0,1,
+ 0,0,1,0,0,1,0,0,1,0,1,1,0,0,1,0,0,1,0,0,1,
+ 0,0,1,0,0,1,0,0,1,0,1,1,0,0,1,0,0,1,0,0,1,
+ 0,0,1,0,0,1,0,0,1,0,0,1,0,0,1,0,0,1,0,0,1,
+ 0,0,1,0,0,1,0,0,1,0,1,1,0,0,1,0,0,1,0,0,1,
+ 0,0,1,0,0,1,0,0,1,0,1,1,0,0,1,0,0,1,0,0,1,
+ 0,0,1,0,0,1,0,0,1,0,0,1,0,0,1,0,0,1,0,0,1,
+ 0,0,1,0,0,1,0,0,1,0,1,1,0,0,1,0,0,1,0,0,1,
+ 0,0,1,0,0,1,0,0,1,0,1,1,0,0,1,0,0,1,0,0,1,
+ 0,0,1,0,0,1,0,0,1,0,0,1,0,0,1,0,0,1,0,0,1,
+ 0,0,1,0,0,1,0,0,1,0,1,1,0,0,1,0,0,1,0,0,1,
+ 0,0,1,0,0,1,0,0,1,0,1,1,0,0,1,0,0,1,0,0,1,
+ 0,0,1,0,0,1,0,0,1,0,0,1,0,0,1,0,0,1,0,0,1,
+ 0,0,1,0,0,1,0,0,1,0,1,1,0,0,1,0,0,1,0,0,1,
+ 0,0,1,0,0,1,0,0,1,0,1,1,0,0,1,0,0,1,0,0,1,
+ 0,0,1,0,0,1,0,0,1,0,0,1,0,0,1,0,0,1,0,0,1,
+ 0,0,1,0,0,1,0,0,1,0,1,1,0,0,1,0,0,1,0,0,1,
+ 0,0,1,0,0,1,0,0,1,0,1,1,0,0,1,0,0,1,0,0,1,
+ 0,0,1,0,0,1,0,0,1,0,0,1,0,0,1,0,0,1,0,0,1,
+ 0,0,1,0,0,1,0,0,1,0,1,1,0,0,1,0,0,1,0,0,1,
+ 0,0,1,0,0,1,0,0,1,0,1,1,0,0,1,0,0,1,0,0,1,
+ 0,0,1,0,0,1,0,0,1,0,1,1,0,0,1,0,0,1,0,0,1,
+};
+
+const uint8_t gsm0503_puncture_mcs1_p2[588] = {
+ 0,1,0,0,1,0,0,1,0,1,0,0,0,1,0,1,1,0,0,1,0,
+ 0,1,0,0,1,0,0,1,0,1,0,0,0,1,0,1,1,0,0,1,0,
+ 0,1,0,0,1,0,0,1,0,1,0,0,0,1,0,1,1,0,0,1,0,
+ 0,1,0,0,1,0,0,1,0,1,0,0,0,1,0,0,1,0,0,1,0,
+ 0,1,0,0,1,0,0,1,0,1,0,0,0,1,0,1,1,0,0,1,0,
+ 0,1,0,0,1,0,0,1,0,1,0,0,0,1,0,1,1,0,0,1,0,
+ 0,1,0,0,1,0,0,1,0,1,0,0,0,1,0,0,1,0,0,1,0,
+ 0,1,0,0,1,0,0,1,0,1,0,0,0,1,0,1,1,0,0,1,0,
+ 0,1,0,0,1,0,0,1,0,1,0,0,0,1,0,1,1,0,0,1,0,
+ 0,1,0,0,1,0,0,1,0,1,0,0,0,1,0,0,1,0,0,1,0,
+ 0,1,0,0,1,0,0,1,0,1,0,0,0,1,0,1,1,0,0,1,0,
+ 0,1,0,0,1,0,0,1,0,1,0,0,0,1,0,1,1,0,0,1,0,
+ 0,1,0,0,1,0,0,1,0,1,0,0,0,1,0,0,1,0,0,1,0,
+ 0,1,0,0,1,0,0,1,0,1,0,0,0,1,0,1,1,0,0,1,0,
+ 0,1,0,0,1,0,0,1,0,1,0,0,0,1,0,1,1,0,0,1,0,
+ 0,1,0,0,1,0,0,1,0,1,0,0,0,1,0,0,1,0,0,1,0,
+ 0,1,0,0,1,0,0,1,0,1,0,0,0,1,0,1,1,0,0,1,0,
+ 0,1,0,0,1,0,0,1,0,1,0,0,0,1,0,1,1,0,0,1,0,
+ 0,1,0,0,1,0,0,1,0,1,0,0,0,1,0,0,1,0,0,1,0,
+ 0,1,0,0,1,0,0,1,0,1,0,0,0,1,0,1,1,0,0,1,0,
+ 0,1,0,0,1,0,0,1,0,1,0,0,0,1,0,1,1,0,0,1,0,
+ 0,1,0,0,1,0,0,1,0,1,0,0,0,1,0,0,1,0,0,1,0,
+ 0,1,0,0,1,0,0,1,0,1,0,0,0,1,0,1,1,0,0,1,0,
+ 0,1,0,0,1,0,0,1,0,1,0,0,0,1,0,1,1,0,0,1,0,
+ 0,1,0,0,1,0,0,1,0,1,0,0,0,1,0,0,1,0,0,1,0,
+ 0,1,0,0,1,0,0,1,0,1,0,0,0,1,0,1,1,0,0,1,0,
+ 0,1,0,0,1,0,0,1,0,1,0,0,0,1,0,1,1,0,0,1,0,
+ 0,1,0,0,1,0,0,1,0,1,0,0,0,1,0,1,1,0,0,1,0,
+};
+
+const uint8_t gsm0503_puncture_mcs2_p1[732] = {
+ 0,0,1,1,1,0, 0,0,1,1,1,0, 0,0,1,1,1,0, 0,0,1,1,1,0, 0,0,1,1,1,0,
+ 0,0,1,1,1,0, 0,0,1,1,1,0, 0,0,1,1,1,0, 0,0,1,1,1,0, 0,0,1,0,1,0,
+ 0,0,1,1,1,0, 0,0,1,1,1,0, 0,0,1,1,1,0, 0,0,1,1,1,0, 0,0,1,1,1,0,
+ 0,0,1,1,1,0, 0,0,1,1,1,0, 0,0,1,1,1,0, 0,0,1,1,1,0, 0,0,1,1,1,0,
+ 0,0,1,1,1,0, 0,0,1,1,1,0, 0,0,1,1,1,0, 0,0,1,1,1,0, 0,0,1,1,1,0,
+ 0,0,1,1,1,0, 0,0,1,1,1,0, 0,0,1,1,1,0, 0,0,1,0,1,0, 0,0,1,1,1,0,
+ 0,0,1,1,1,0, 0,0,1,1,1,0, 0,0,1,1,1,0, 0,0,1,1,1,0, 0,0,1,1,1,0,
+ 0,0,1,1,1,0, 0,0,1,1,1,0, 0,0,1,1,1,0, 0,0,1,1,1,0, 0,0,1,1,1,0,
+ 0,0,1,1,1,0, 0,0,1,1,1,0, 0,0,1,1,1,0, 0,0,1,1,1,0, 0,0,1,1,1,0,
+ 0,0,1,1,1,0, 0,0,1,1,1,0, 0,0,1,0,1,0, 0,0,1,1,1,0, 0,0,1,1,1,0,
+ 0,0,1,1,1,0, 0,0,1,1,1,0, 0,0,1,1,1,0, 0,0,1,1,1,0, 0,0,1,1,1,0,
+ 0,0,1,1,1,0, 0,0,1,1,1,0, 0,0,1,1,1,0, 0,0,1,1,1,0, 0,0,1,1,1,0,
+ 0,0,1,1,1,0, 0,0,1,1,1,0, 0,0,1,1,1,0, 0,0,1,1,1,0, 0,0,1,1,1,0,
+ 0,0,1,1,1,0, 0,0,1,0,1,0, 0,0,1,1,1,0, 0,0,1,1,1,0, 0,0,1,1,1,0,
+ 0,0,1,1,1,0, 0,0,1,1,1,0, 0,0,1,1,1,0, 0,0,1,1,1,0, 0,0,1,1,1,0,
+ 0,0,1,1,1,0, 0,0,1,1,1,0, 0,0,1,1,1,0, 0,0,1,1,1,0, 0,0,1,1,1,0,
+ 0,0,1,1,1,0, 0,0,1,1,1,0, 0,0,1,1,1,0, 0,0,1,1,1,0, 0,0,1,1,1,0,
+ 0,0,1,0,1,0, 0,0,1,1,1,0, 0,0,1,1,1,0, 0,0,1,1,1,0, 0,0,1,1,1,0,
+ 0,0,1,1,1,0, 0,0,1,1,1,0, 0,0,1,1,1,0, 0,0,1,1,1,0, 0,0,1,1,1,0,
+ 0,0,1,1,1,0, 0,0,1,1,1,0, 0,0,1,1,1,0, 0,0,1,1,1,0, 0,0,1,1,1,0,
+ 0,0,1,1,1,0, 0,0,1,1,1,0, 0,0,1,1,1,0, 0,0,1,1,1,0, 0,0,1,0,1,0,
+ 0,0,1,1,1,0, 0,0,1,1,1,0, 0,0,1,1,1,0, 0,0,1,1,1,0, 0,0,1,1,1,0,
+ 0,0,1,1,1,0, 0,0,1,1,1,0, 0,0,1,1,1,0, 0,0,1,1,1,0, 0,0,1,1,1,0,
+ 0,0,1,1,1,0, 0,0,1,1,1,0, 0,0,1,1,1,0, 0,0,1,1,1,0, 0,0,1,1,1,0,
+ 0,0,1,1,1,0, 0,0,1,1,1,0,
+};
+
+const uint8_t gsm0503_puncture_mcs2_p2[732] = {
+ 1,1,0,0,0,1, 1,1,0,0,0,1, 1,1,0,0,0,1, 1,1,0,0,0,1, 1,1,0,0,0,1,
+ 1,1,0,0,0,1, 1,1,0,0,0,1, 1,1,0,0,0,1, 1,1,0,0,0,1, 1,1,0,0,0,1,
+ 1,1,0,0,0,1, 1,1,0,0,0,1, 1,1,0,0,0,1, 1,1,0,0,0,1, 1,1,0,0,0,1,
+ 1,1,0,0,0,1, 1,1,0,0,0,1, 1,1,0,0,0,1, 0,1,0,0,0,1, 1,1,0,0,0,1,
+ 1,1,0,0,0,1, 1,1,0,0,0,1, 1,1,0,0,0,1, 1,1,0,0,0,1, 1,1,0,0,0,1,
+ 1,1,0,0,0,1, 1,1,0,0,0,1, 1,1,0,0,0,1, 1,1,0,0,0,1, 1,1,0,0,0,1,
+ 1,1,0,0,0,1, 1,1,0,0,0,1, 1,1,0,0,0,1, 1,1,0,0,0,1, 1,1,0,0,0,1,
+ 1,1,0,0,0,1, 1,1,0,0,0,1, 0,1,0,0,0,1, 1,1,0,0,0,1, 1,1,0,0,0,1,
+ 1,1,0,0,0,1, 1,1,0,0,0,1, 1,1,0,0,0,1, 1,1,0,0,0,1, 1,1,0,0,0,1,
+ 1,1,0,0,0,1, 1,1,0,0,0,1, 1,1,0,0,0,1, 1,1,0,0,0,1, 1,1,0,0,0,1,
+ 1,1,0,0,0,1, 1,1,0,0,0,1, 1,1,0,0,0,1, 1,1,0,0,0,1, 1,1,0,0,0,1,
+ 1,1,0,0,0,1, 0,1,0,0,0,1, 1,1,0,0,0,1, 1,1,0,0,0,1, 1,1,0,0,0,1,
+ 1,1,0,0,0,1, 1,1,0,0,0,1, 1,1,0,0,0,1, 1,1,0,0,0,1, 1,1,0,0,0,1,
+ 1,1,0,0,0,1, 1,1,0,0,0,1, 1,1,0,0,0,1, 1,1,0,0,0,1, 1,1,0,0,0,1,
+ 1,1,0,0,0,1, 1,1,0,0,0,1, 1,1,0,0,0,1, 1,1,0,0,0,1, 1,1,0,0,0,1,
+ 0,1,0,0,0,1, 1,1,0,0,0,1, 1,1,0,0,0,1, 1,1,0,0,0,1, 1,1,0,0,0,1,
+ 1,1,0,0,0,1, 1,1,0,0,0,1, 1,1,0,0,0,1, 1,1,0,0,0,1, 1,1,0,0,0,1,
+ 1,1,0,0,0,1, 1,1,0,0,0,1, 1,1,0,0,0,1, 1,1,0,0,0,1, 1,1,0,0,0,1,
+ 1,1,0,0,0,1, 1,1,0,0,0,1, 1,1,0,0,0,1, 1,1,0,0,0,1, 0,1,0,0,0,1,
+ 1,1,0,0,0,1, 1,1,0,0,0,1, 1,1,0,0,0,1, 1,1,0,0,0,1, 1,1,0,0,0,1,
+ 1,1,0,0,0,1, 1,1,0,0,0,1, 1,1,0,0,0,1, 1,1,0,0,0,1, 1,1,0,0,0,1,
+ 1,1,0,0,0,1, 1,1,0,0,0,1, 1,1,0,0,0,1, 1,1,0,0,0,1, 1,1,0,0,0,1,
+ 1,1,0,0,0,1, 1,1,0,0,0,1, 1,1,0,0,0,1, 0,1,0,0,0,1, 1,1,0,0,0,1,
+ 1,1,0,0,0,1, 1,1,0,0,0,1, 1,1,0,0,0,1, 1,1,0,0,0,1, 1,1,0,0,0,1,
+ 1,1,0,0,0,1, 1,1,0,0,0,1,
+};
+
+const uint8_t gsm0503_puncture_mcs3_p1[948] = {
+ 0,0,1,0,1,1,0,1,1,1,0,1,1,1,0,1,1,0,
+ 0,0,1,0,1,1,0,1,1,1,0,1,1,1,0,1,1,0,
+ 0,0,1,0,1,1,0,1,1,1,0,1,1,1,0,1,1,0,
+ 0,0,1,0,1,1,0,1,1,1,0,1,1,1,0,1,1,0,
+ 0,0,1,0,1,1,0,1,1,1,0,1,1,1,0,1,1,0,
+ 0,0,1,0,1,1,0,1,1,1,0,1,1,1,0,1,1,0,
+ 0,0,1,0,1,1,0,1,1,1,0,1,1,1,0,1,1,0,
+ 0,0,1,0,1,1,0,1,1,1,0,1,1,1,0,1,1,0,
+ 0,0,1,0,1,1,0,1,1,1,0,1,1,1,0,1,1,0,
+ 0,0,1,0,1,1,0,1,1,1,0,1,1,1,0,1,1,0,
+ 0,0,1,0,1,1,0,1,1,1,0,1,1,1,0,1,1,0,
+ 0,0,1,0,1,1,0,1,1,1,0,1,1,1,0,1,1,0,
+ 0,0,1,0,1,1,0,1,1,1,0,1,1,1,0,1,1,0,
+ 0,0,1,0,1,1,0,0,1,1,0,1,1,1,0,1,1,0,
+ 0,0,1,0,1,1,0,1,1,1,0,1,1,1,0,1,1,0,
+ 0,0,1,0,1,1,0,1,1,1,0,1,1,1,0,1,1,0,
+ 0,0,1,0,1,1,0,1,1,1,0,1,1,1,0,1,1,0,
+ 0,0,1,0,1,1,0,1,1,1,0,1,1,1,0,1,1,0,
+ 0,0,1,0,1,1,0,1,1,1,0,1,1,1,0,1,1,0,
+ 0,0,1,0,1,1,0,1,1,1,0,1,1,1,0,1,1,0,
+ 0,0,1,0,1,1,0,1,1,1,0,1,1,1,0,1,1,0,
+ 0,0,1,0,1,1,0,1,1,1,0,1,1,1,0,1,1,0,
+ 0,0,1,0,1,1,0,1,1,1,0,1,1,1,0,1,1,0,
+ 0,0,1,0,1,1,0,1,1,1,0,1,1,1,0,1,1,0,
+ 0,0,1,0,1,1,0,1,1,1,0,1,1,1,0,1,1,0,
+ 0,0,1,0,1,1,0,1,1,1,0,1,1,1,0,1,1,0,
+ 0,0,1,0,1,1,0,0,1,1,0,1,1,1,0,1,1,0,
+ 0,0,1,0,1,1,0,1,1,1,0,1,1,1,0,1,1,0,
+ 0,0,1,0,1,1,0,1,1,1,0,1,1,1,0,1,1,0,
+ 0,0,1,0,1,1,0,1,1,1,0,1,1,1,0,1,1,0,
+ 0,0,1,0,1,1,0,1,1,1,0,1,1,1,0,1,1,0,
+ 0,0,1,0,1,1,0,1,1,1,0,1,1,1,0,1,1,0,
+ 0,0,1,0,1,1,0,1,1,1,0,1,1,1,0,1,1,0,
+ 0,0,1,0,1,1,0,1,1,1,0,1,1,1,0,1,1,0,
+ 0,0,1,0,1,1,0,1,1,1,0,1,1,1,0,1,1,0,
+ 0,0,1,0,1,1,0,1,1,1,0,1,1,1,0,1,1,0,
+ 0,0,1,0,1,1,0,1,1,1,0,1,1,1,0,1,1,0,
+ 0,0,1,0,1,1,0,1,1,1,0,1,1,1,0,1,1,0,
+ 0,0,1,0,1,1,0,1,1,1,0,1,1,1,0,1,1,0,
+ 0,0,1,0,1,1,0,0,1,1,0,1,1,1,0,1,1,0,
+ 0,0,1,0,1,1,0,1,1,1,0,1,1,1,0,1,1,0,
+ 0,0,1,0,1,1,0,1,1,1,0,1,1,1,0,1,1,0,
+ 0,0,1,0,1,1,0,1,1,1,0,1,1,1,0,1,1,0,
+ 0,0,1,0,1,1,0,1,1,1,0,1,1,1,0,1,1,0,
+ 0,0,1,0,1,1,0,1,1,1,0,1,1,1,0,1,1,0,
+ 0,0,1,0,1,1,0,1,1,1,0,1,1,1,0,1,1,0,
+ 0,0,1,0,1,1,0,1,1,1,0,1,1,1,0,1,1,0,
+ 0,0,1,0,1,1,0,1,1,1,0,1,1,1,0,1,1,0,
+ 0,0,1,0,1,1,0,1,1,1,0,1,1,1,0,1,1,0,
+ 0,0,1,0,1,1,0,1,1,1,0,1,1,1,0,1,1,0,
+ 0,0,1,0,1,1,0,1,1,1,0,1,1,1,0,1,1,0,
+ 0,0,1,0,1,1,0,1,1,1,0,1,1,1,0,1,1,0,
+ 0,0,1,0,1,1,0,1,1,1,0,1,
+};
+
+const uint8_t gsm0503_puncture_mcs3_p2[948] = {
+ 1,1,0,1,1,0,0,0,1,0,1,1,0,1,1,1,0,1,
+ 1,1,0,1,1,0,0,0,1,0,1,1,0,1,1,1,0,1,
+ 1,1,0,1,1,0,0,0,1,0,1,1,0,1,1,1,0,1,
+ 1,1,0,1,1,0,0,0,1,0,1,1,0,1,1,1,0,1,
+ 1,1,0,1,1,0,0,0,1,0,1,1,0,1,1,1,0,1,
+ 1,1,0,1,1,0,0,0,1,0,1,1,0,1,1,1,0,1,
+ 1,1,0,1,1,0,0,0,1,0,1,1,0,0,1,1,0,1,
+ 1,1,0,1,1,0,0,0,1,0,1,1,0,1,1,1,0,1,
+ 1,1,0,1,1,0,0,0,1,0,1,1,0,1,1,1,0,1,
+ 1,1,0,1,1,0,0,0,1,0,1,1,0,1,1,1,0,1,
+ 1,1,0,1,1,0,0,0,1,0,1,1,0,1,1,1,0,1,
+ 1,1,0,1,1,0,0,0,1,0,1,1,0,1,1,1,0,1,
+ 1,1,0,1,1,0,0,0,1,0,1,1,0,1,1,1,0,1,
+ 1,1,0,1,1,0,0,0,1,0,1,1,0,1,1,1,0,1,
+ 1,1,0,1,1,0,0,0,1,0,1,1,0,1,1,1,0,1,
+ 1,1,0,1,1,0,0,0,1,0,1,1,0,1,1,1,0,1,
+ 1,1,0,1,1,0,0,0,1,0,1,1,0,1,1,1,0,1,
+ 1,1,0,1,1,0,0,0,1,0,1,1,0,1,1,1,0,1,
+ 1,1,0,1,1,0,0,0,1,0,1,1,0,1,1,1,0,1,
+ 1,1,0,1,1,0,0,0,1,0,1,1,0,0,1,1,0,1,
+ 1,1,0,1,1,0,0,0,1,0,1,1,0,1,1,1,0,1,
+ 1,1,0,1,1,0,0,0,1,0,1,1,0,1,1,1,0,1,
+ 1,1,0,1,1,0,0,0,1,0,1,1,0,1,1,1,0,1,
+ 1,1,0,1,1,0,0,0,1,0,1,1,0,1,1,1,0,1,
+ 1,1,0,1,1,0,0,0,1,0,1,1,0,1,1,1,0,1,
+ 1,1,0,1,1,0,0,0,1,0,1,1,0,1,1,1,0,1,
+ 1,1,0,1,1,0,0,0,1,0,1,1,0,1,1,1,0,1,
+ 1,1,0,1,1,0,0,0,1,0,1,1,0,1,1,1,0,1,
+ 1,1,0,1,1,0,0,0,1,0,1,1,0,1,1,1,0,1,
+ 1,1,0,1,1,0,0,0,1,0,1,1,0,1,1,1,0,1,
+ 1,1,0,1,1,0,0,0,1,0,1,1,0,1,1,1,0,1,
+ 1,1,0,1,1,0,0,0,1,0,1,1,0,1,1,1,0,1,
+ 1,1,0,1,1,0,0,0,1,0,1,1,0,0,1,1,0,1,
+ 1,1,0,1,1,0,0,0,1,0,1,1,0,1,1,1,0,1,
+ 1,1,0,1,1,0,0,0,1,0,1,1,0,1,1,1,0,1,
+ 1,1,0,1,1,0,0,0,1,0,1,1,0,1,1,1,0,1,
+ 1,1,0,1,1,0,0,0,1,0,1,1,0,1,1,1,0,1,
+ 1,1,0,1,1,0,0,0,1,0,1,1,0,1,1,1,0,1,
+ 1,1,0,1,1,0,0,0,1,0,1,1,0,1,1,1,0,1,
+ 1,1,0,1,1,0,0,0,1,0,1,1,0,1,1,1,0,1,
+ 1,1,0,1,1,0,0,0,1,0,1,1,0,1,1,1,0,1,
+ 1,1,0,1,1,0,0,0,1,0,1,1,0,1,1,1,0,1,
+ 1,1,0,1,1,0,0,0,1,0,1,1,0,1,1,1,0,1,
+ 1,1,0,1,1,0,0,0,1,0,1,1,0,1,1,1,0,1,
+ 1,1,0,1,1,0,0,0,1,0,1,1,0,1,1,1,0,1,
+ 1,1,0,1,1,0,0,0,1,0,1,1,0,1,1,1,0,1,
+ 1,1,0,1,1,0,0,0,1,0,1,1,0,1,1,1,0,1,
+ 1,1,0,1,1,0,0,0,1,0,1,1,0,1,1,1,0,1,
+ 1,1,0,1,1,0,0,0,1,0,1,1,0,1,1,1,0,1,
+ 1,1,0,1,1,0,0,0,1,0,1,1,0,1,1,1,0,1,
+ 1,1,0,1,1,0,0,0,1,0,1,1,0,1,1,1,0,1,
+ 1,1,0,1,1,0,0,0,1,0,1,1,0,1,1,1,0,1,
+ 1,1,0,1,1,0,0,0,1,0,1,1,
+};
+
+const uint8_t gsm0503_puncture_mcs3_p3[948] = {
+ 0,1,1,1,0,1,1,1,0,1,1,0,0,0,1,0,1,1,
+ 0,1,1,1,0,1,1,1,0,1,1,0,0,0,1,0,1,1,
+ 0,1,1,1,0,1,1,1,0,1,1,0,0,0,1,0,1,1,
+ 0,1,1,1,0,1,1,1,0,1,1,0,0,0,1,0,1,1,
+ 0,1,1,1,0,1,1,1,0,1,1,0,0,0,1,0,1,1,
+ 0,1,1,1,0,1,1,1,0,1,1,0,0,0,1,0,1,1,
+ 0,1,1,1,0,1,1,1,0,1,1,0,0,0,1,0,1,1,
+ 0,1,1,1,0,1,1,1,0,1,1,0,0,0,1,0,1,1,
+ 0,1,1,1,0,1,1,1,0,1,1,0,0,0,1,0,1,1,
+ 0,1,1,1,0,1,1,1,0,1,1,0,0,0,1,0,1,1,
+ 0,0,1,1,0,1,1,1,0,1,1,0,0,0,1,0,1,1,
+ 0,1,1,1,0,1,1,1,0,1,1,0,0,0,1,0,1,1,
+ 0,1,1,1,0,1,1,1,0,1,1,0,0,0,1,0,1,1,
+ 0,1,1,1,0,1,1,1,0,1,1,0,0,0,1,0,1,1,
+ 0,1,1,1,0,1,1,1,0,1,1,0,0,0,1,0,1,1,
+ 0,1,1,1,0,1,1,1,0,1,1,0,0,0,1,0,1,1,
+ 0,0,1,1,0,1,1,1,0,1,1,0,0,0,1,0,1,1,
+ 0,1,1,1,0,1,1,1,0,1,1,0,0,0,1,0,1,1,
+ 0,1,1,1,0,1,1,1,0,1,1,0,0,0,1,0,1,1,
+ 0,1,1,1,0,1,1,1,0,1,1,0,0,0,1,0,1,1,
+ 0,1,1,1,0,1,1,1,0,1,1,0,0,0,1,0,1,1,
+ 0,1,1,1,0,1,1,1,0,1,1,0,0,0,1,0,1,1,
+ 0,1,1,1,0,1,1,1,0,1,1,0,0,0,1,0,1,1,
+ 0,1,1,1,0,1,1,1,0,1,1,0,0,0,1,0,1,1,
+ 0,1,1,1,0,1,1,1,0,1,1,0,0,0,1,0,1,1,
+ 0,1,1,1,0,1,1,1,0,1,1,0,0,0,1,0,1,1,
+ 0,1,1,1,0,1,1,1,0,1,1,0,0,0,1,0,1,1,
+ 0,1,1,1,0,1,1,1,0,1,1,0,0,0,1,0,1,1,
+ 0,1,1,1,0,1,1,1,0,1,1,0,0,0,1,0,1,1,
+ 0,0,1,1,0,1,1,1,0,1,1,0,0,0,1,0,1,1,
+ 0,1,1,1,0,1,1,1,0,1,1,0,0,0,1,0,1,1,
+ 0,1,1,1,0,1,1,1,0,1,1,0,0,0,1,0,1,1,
+ 0,1,1,1,0,1,1,1,0,1,1,0,0,0,1,0,1,1,
+ 0,1,1,1,0,1,1,1,0,1,1,0,0,0,1,0,1,1,
+ 0,1,1,1,0,1,1,1,0,1,1,0,0,0,1,0,1,1,
+ 0,1,1,1,0,1,1,1,0,1,1,0,0,0,1,0,1,1,
+ 0,1,1,1,0,1,1,1,0,1,1,0,0,0,1,0,1,1,
+ 0,1,1,1,0,1,1,1,0,1,1,0,0,0,1,0,1,1,
+ 0,1,1,1,0,1,1,1,0,1,1,0,0,0,1,0,1,1,
+ 0,1,1,1,0,1,1,1,0,1,1,0,0,0,1,0,1,1,
+ 0,1,1,1,0,1,1,1,0,1,1,0,0,0,1,0,1,1,
+ 0,1,1,1,0,1,1,1,0,1,1,0,0,0,1,0,1,1,
+ 0,1,1,1,0,1,1,1,0,1,1,0,0,0,1,0,1,1,
+ 0,1,1,1,0,1,1,1,0,1,1,0,0,0,1,0,1,1,
+ 0,1,1,1,0,1,1,1,0,1,1,0,0,0,1,0,1,1,
+ 0,0,1,1,0,1,1,1,0,1,1,0,0,0,1,0,1,1,
+ 0,1,1,1,0,1,1,1,0,1,1,0,0,0,1,0,1,1,
+ 0,1,1,1,0,1,1,1,0,1,1,0,0,0,1,0,1,1,
+ 0,1,1,1,0,1,1,1,0,1,1,0,0,0,1,0,1,1,
+ 0,1,1,1,0,1,1,1,0,1,1,0,0,0,1,0,1,1,
+ 0,1,1,1,0,1,1,1,0,1,1,0,0,0,1,0,1,1,
+ 0,1,1,1,0,1,1,1,0,1,1,0,0,0,1,0,1,1,
+ 0,1,1,1,0,1,1,1,0,1,1,0,
+};
+
+const uint8_t gsm0503_puncture_mcs4_p1[1116] = {
+ 0,1,1, 0,1,1, 0,1,1, 0,1,1, 0,1,1, 0,1,1, 0,1,1, 0,1,1, 0,1,1, 0,1,1,
+ 0,1,1, 0,1,1, 0,1,1, 0,1,1, 0,1,1, 0,1,1, 0,1,1, 0,1,1, 0,1,1, 0,1,1,
+ 0,1,1, 0,1,1, 0,1,1, 0,1,1, 0,1,1, 0,1,1, 0,1,1, 0,1,1, 0,1,1, 0,1,1,
+ 0,1,1, 0,1,1, 0,1,1, 0,1,1, 0,1,1, 0,1,1, 0,1,1, 0,1,1, 0,1,1, 0,1,1,
+ 0,1,1, 0,1,1, 0,1,1, 0,1,1, 0,1,1, 0,1,1, 0,1,1, 0,1,1, 0,1,1, 0,1,1,
+ 0,1,1, 0,1,1, 0,1,1, 0,1,1, 0,1,1, 0,1,1, 0,1,1, 0,1,1, 0,1,1, 0,1,1,
+ 0,1,1, 0,1,1, 0,1,1, 0,1,1, 0,1,1, 0,1,1, 0,1,1, 0,1,1, 0,1,1, 0,1,1,
+ 0,1,1, 0,1,1, 0,1,1, 0,1,1, 0,1,1, 0,1,1, 0,1,1, 0,1,1, 0,1,1, 0,1,1,
+ 0,1,1, 0,1,1, 0,1,1, 0,1,1, 0,1,1, 0,1,1, 0,1,1, 0,1,1, 0,1,1, 0,1,1,
+ 0,1,1, 0,1,1, 0,1,1, 0,1,1, 0,1,1, 0,1,1, 0,1,1, 0,1,1, 0,1,1, 0,1,1,
+ 0,1,1, 0,1,1, 0,1,1, 0,1,1, 0,1,1, 0,1,1, 0,1,1, 0,1,1, 0,1,1, 0,1,1,
+ 0,1,1, 0,1,1, 0,1,1, 0,1,1, 0,1,1, 0,1,1, 0,1,1, 0,1,1, 0,1,1, 0,1,1,
+ 0,1,1, 0,1,1, 0,1,1, 0,1,1, 0,1,1, 0,1,1, 0,1,1, 0,1,1, 0,1,1, 0,1,1,
+ 0,1,1, 0,1,1, 0,1,1, 0,1,1, 0,1,1, 0,1,1, 0,1,1, 0,1,1, 0,1,1, 0,1,1,
+ 0,1,1, 0,1,1, 0,1,1, 0,1,1, 0,1,1, 0,1,1, 0,1,1, 0,1,1, 0,1,1, 0,1,1,
+ 0,1,1, 0,1,1, 0,1,1, 0,1,1, 0,1,1, 0,1,1, 0,1,1, 0,1,1, 0,1,1, 0,1,1,
+ 0,1,1, 0,1,1, 0,1,1, 0,1,1, 0,1,1, 0,1,1, 0,1,1, 0,1,1, 0,1,1, 0,1,1,
+ 0,1,1, 0,1,1, 0,1,1, 0,1,1, 0,1,1, 0,1,1, 0,1,1, 0,1,1, 0,1,1, 0,1,1,
+ 0,1,1, 0,1,1, 0,1,1, 0,1,1, 0,1,1, 0,1,1, 0,1,1, 0,1,1, 0,1,1, 0,1,1,
+ 0,1,1, 0,1,1, 0,1,1, 0,1,1, 0,1,1, 0,1,1, 0,1,1, 0,1,1, 0,1,1, 0,1,1,
+ 0,1,1, 0,1,1, 0,1,1, 0,1,1, 0,1,1, 0,1,1, 0,1,1, 0,1,1, 0,1,1, 0,1,1,
+ 0,1,1, 0,1,1, 0,1,1, 0,1,1, 0,1,1, 0,1,1, 0,1,1, 0,1,1, 0,1,1, 0,1,1,
+ 0,1,1, 0,1,1, 0,1,1, 0,1,1, 0,1,1, 0,1,1, 0,1,1, 0,1,1, 0,1,1, 0,1,1,
+ 0,1,1, 0,1,1, 0,1,1, 0,1,1, 0,1,1, 0,1,1, 0,1,1, 0,1,1, 0,1,1, 0,1,1,
+ 0,1,1, 0,1,1, 0,1,1, 0,1,1, 0,1,1, 0,1,1, 0,1,1, 0,1,1, 0,1,1, 0,1,1,
+ 0,1,1, 0,1,1, 0,1,1, 0,1,1, 0,1,1, 0,1,1, 0,1,1, 0,1,1, 0,1,1, 0,1,1,
+ 0,1,1, 0,1,1, 0,1,1, 0,1,1, 0,1,1, 0,1,1, 0,1,1, 0,1,1, 0,1,1, 0,1,1,
+ 0,1,1, 0,1,1, 0,1,1, 0,1,1, 0,1,1, 0,1,1, 0,1,1, 0,1,1, 0,1,1, 0,1,1,
+ 0,1,1, 0,1,1, 0,1,1, 0,1,1, 0,1,1, 0,1,1, 0,1,1, 0,1,1, 0,1,1, 0,1,1,
+ 0,1,1, 0,1,1, 0,1,1, 0,1,1, 0,1,1, 0,1,1, 0,1,1, 0,1,1, 0,1,1, 0,1,1,
+ 0,1,1, 0,1,1, 0,1,1, 0,1,1, 0,1,1, 0,1,1, 0,1,1, 0,1,1, 0,1,1, 0,1,1,
+ 0,1,1, 0,1,1, 0,1,1, 0,1,1, 0,1,1, 0,1,1, 0,1,1, 0,1,1, 0,1,1, 0,1,1,
+ 0,1,1, 0,1,1, 0,1,1, 0,1,1, 0,1,1, 0,1,1, 0,1,1, 0,1,1, 0,1,1, 0,1,1,
+ 0,1,1, 0,1,1, 0,1,1, 0,1,1, 0,1,1, 0,1,1, 0,1,1, 0,1,1, 0,1,1, 0,1,1,
+ 0,1,1, 0,1,1, 0,1,1, 0,1,1, 0,1,1, 0,1,1, 0,1,1, 0,1,1, 0,1,1, 0,1,1,
+ 0,1,1, 0,1,1, 0,1,1, 0,1,1, 0,1,1, 0,1,1, 0,1,1, 0,1,1, 0,1,1, 0,1,1,
+ 0,1,1, 0,1,1, 0,1,1, 0,1,1, 0,1,1, 0,1,1, 0,1,1, 0,1,1, 0,1,1, 0,1,1,
+ 0,1,1, 0,1,1,
+};
+
+const uint8_t gsm0503_puncture_mcs4_p2[1116] = {
+ 1,0,1, 1,0,1, 1,0,1, 1,0,1, 1,0,1, 1,0,1, 1,0,1, 1,0,1, 1,0,1, 1,0,1,
+ 1,0,1, 1,0,1, 1,0,1, 1,0,1, 1,0,1, 1,0,1, 1,0,1, 1,0,1, 1,0,1, 1,0,1,
+ 1,0,1, 1,0,1, 1,0,1, 1,0,1, 1,0,1, 1,0,1, 1,0,1, 1,0,1, 1,0,1, 1,0,1,
+ 1,0,1, 1,0,1, 1,0,1, 1,0,1, 1,0,1, 1,0,1, 1,0,1, 1,0,1, 1,0,1, 1,0,1,
+ 1,0,1, 1,0,1, 1,0,1, 1,0,1, 1,0,1, 1,0,1, 1,0,1, 1,0,1, 1,0,1, 1,0,1,
+ 1,0,1, 1,0,1, 1,0,1, 1,0,1, 1,0,1, 1,0,1, 1,0,1, 1,0,1, 1,0,1, 1,0,1,
+ 1,0,1, 1,0,1, 1,0,1, 1,0,1, 1,0,1, 1,0,1, 1,0,1, 1,0,1, 1,0,1, 1,0,1,
+ 1,0,1, 1,0,1, 1,0,1, 1,0,1, 1,0,1, 1,0,1, 1,0,1, 1,0,1, 1,0,1, 1,0,1,
+ 1,0,1, 1,0,1, 1,0,1, 1,0,1, 1,0,1, 1,0,1, 1,0,1, 1,0,1, 1,0,1, 1,0,1,
+ 1,0,1, 1,0,1, 1,0,1, 1,0,1, 1,0,1, 1,0,1, 1,0,1, 1,0,1, 1,0,1, 1,0,1,
+ 1,0,1, 1,0,1, 1,0,1, 1,0,1, 1,0,1, 1,0,1, 1,0,1, 1,0,1, 1,0,1, 1,0,1,
+ 1,0,1, 1,0,1, 1,0,1, 1,0,1, 1,0,1, 1,0,1, 1,0,1, 1,0,1, 1,0,1, 1,0,1,
+ 1,0,1, 1,0,1, 1,0,1, 1,0,1, 1,0,1, 1,0,1, 1,0,1, 1,0,1, 1,0,1, 1,0,1,
+ 1,0,1, 1,0,1, 1,0,1, 1,0,1, 1,0,1, 1,0,1, 1,0,1, 1,0,1, 1,0,1, 1,0,1,
+ 1,0,1, 1,0,1, 1,0,1, 1,0,1, 1,0,1, 1,0,1, 1,0,1, 1,0,1, 1,0,1, 1,0,1,
+ 1,0,1, 1,0,1, 1,0,1, 1,0,1, 1,0,1, 1,0,1, 1,0,1, 1,0,1, 1,0,1, 1,0,1,
+ 1,0,1, 1,0,1, 1,0,1, 1,0,1, 1,0,1, 1,0,1, 1,0,1, 1,0,1, 1,0,1, 1,0,1,
+ 1,0,1, 1,0,1, 1,0,1, 1,0,1, 1,0,1, 1,0,1, 1,0,1, 1,0,1, 1,0,1, 1,0,1,
+ 1,0,1, 1,0,1, 1,0,1, 1,0,1, 1,0,1, 1,0,1, 1,0,1, 1,0,1, 1,0,1, 1,0,1,
+ 1,0,1, 1,0,1, 1,0,1, 1,0,1, 1,0,1, 1,0,1, 1,0,1, 1,0,1, 1,0,1, 1,0,1,
+ 1,0,1, 1,0,1, 1,0,1, 1,0,1, 1,0,1, 1,0,1, 1,0,1, 1,0,1, 1,0,1, 1,0,1,
+ 1,0,1, 1,0,1, 1,0,1, 1,0,1, 1,0,1, 1,0,1, 1,0,1, 1,0,1, 1,0,1, 1,0,1,
+ 1,0,1, 1,0,1, 1,0,1, 1,0,1, 1,0,1, 1,0,1, 1,0,1, 1,0,1, 1,0,1, 1,0,1,
+ 1,0,1, 1,0,1, 1,0,1, 1,0,1, 1,0,1, 1,0,1, 1,0,1, 1,0,1, 1,0,1, 1,0,1,
+ 1,0,1, 1,0,1, 1,0,1, 1,0,1, 1,0,1, 1,0,1, 1,0,1, 1,0,1, 1,0,1, 1,0,1,
+ 1,0,1, 1,0,1, 1,0,1, 1,0,1, 1,0,1, 1,0,1, 1,0,1, 1,0,1, 1,0,1, 1,0,1,
+ 1,0,1, 1,0,1, 1,0,1, 1,0,1, 1,0,1, 1,0,1, 1,0,1, 1,0,1, 1,0,1, 1,0,1,
+ 1,0,1, 1,0,1, 1,0,1, 1,0,1, 1,0,1, 1,0,1, 1,0,1, 1,0,1, 1,0,1, 1,0,1,
+ 1,0,1, 1,0,1, 1,0,1, 1,0,1, 1,0,1, 1,0,1, 1,0,1, 1,0,1, 1,0,1, 1,0,1,
+ 1,0,1, 1,0,1, 1,0,1, 1,0,1, 1,0,1, 1,0,1, 1,0,1, 1,0,1, 1,0,1, 1,0,1,
+ 1,0,1, 1,0,1, 1,0,1, 1,0,1, 1,0,1, 1,0,1, 1,0,1, 1,0,1, 1,0,1, 1,0,1,
+ 1,0,1, 1,0,1, 1,0,1, 1,0,1, 1,0,1, 1,0,1, 1,0,1, 1,0,1, 1,0,1, 1,0,1,
+ 1,0,1, 1,0,1, 1,0,1, 1,0,1, 1,0,1, 1,0,1, 1,0,1, 1,0,1, 1,0,1, 1,0,1,
+ 1,0,1, 1,0,1, 1,0,1, 1,0,1, 1,0,1, 1,0,1, 1,0,1, 1,0,1, 1,0,1, 1,0,1,
+ 1,0,1, 1,0,1, 1,0,1, 1,0,1, 1,0,1, 1,0,1, 1,0,1, 1,0,1, 1,0,1, 1,0,1,
+ 1,0,1, 1,0,1, 1,0,1, 1,0,1, 1,0,1, 1,0,1, 1,0,1, 1,0,1, 1,0,1, 1,0,1,
+ 1,0,1, 1,0,1, 1,0,1, 1,0,1, 1,0,1, 1,0,1, 1,0,1, 1,0,1, 1,0,1, 1,0,1,
+ 1,0,1, 1,0,1,
+};
+
+const uint8_t gsm0503_puncture_mcs4_p3[1116] = {
+ 1,1,0, 1,1,0, 1,1,0, 1,1,0, 1,1,0, 1,1,0, 1,1,0, 1,1,0, 1,1,0, 1,1,0,
+ 1,1,0, 1,1,0, 1,1,0, 1,1,0, 1,1,0, 1,1,0, 1,1,0, 1,1,0, 1,1,0, 1,1,0,
+ 1,1,0, 1,1,0, 1,1,0, 1,1,0, 1,1,0, 1,1,0, 1,1,0, 1,1,0, 1,1,0, 1,1,0,
+ 1,1,0, 1,1,0, 1,1,0, 1,1,0, 1,1,0, 1,1,0, 1,1,0, 1,1,0, 1,1,0, 1,1,0,
+ 1,1,0, 1,1,0, 1,1,0, 1,1,0, 1,1,0, 1,1,0, 1,1,0, 1,1,0, 1,1,0, 1,1,0,
+ 1,1,0, 1,1,0, 1,1,0, 1,1,0, 1,1,0, 1,1,0, 1,1,0, 1,1,0, 1,1,0, 1,1,0,
+ 1,1,0, 1,1,0, 1,1,0, 1,1,0, 1,1,0, 1,1,0, 1,1,0, 1,1,0, 1,1,0, 1,1,0,
+ 1,1,0, 1,1,0, 1,1,0, 1,1,0, 1,1,0, 1,1,0, 1,1,0, 1,1,0, 1,1,0, 1,1,0,
+ 1,1,0, 1,1,0, 1,1,0, 1,1,0, 1,1,0, 1,1,0, 1,1,0, 1,1,0, 1,1,0, 1,1,0,
+ 1,1,0, 1,1,0, 1,1,0, 1,1,0, 1,1,0, 1,1,0, 1,1,0, 1,1,0, 1,1,0, 1,1,0,
+ 1,1,0, 1,1,0, 1,1,0, 1,1,0, 1,1,0, 1,1,0, 1,1,0, 1,1,0, 1,1,0, 1,1,0,
+ 1,1,0, 1,1,0, 1,1,0, 1,1,0, 1,1,0, 1,1,0, 1,1,0, 1,1,0, 1,1,0, 1,1,0,
+ 1,1,0, 1,1,0, 1,1,0, 1,1,0, 1,1,0, 1,1,0, 1,1,0, 1,1,0, 1,1,0, 1,1,0,
+ 1,1,0, 1,1,0, 1,1,0, 1,1,0, 1,1,0, 1,1,0, 1,1,0, 1,1,0, 1,1,0, 1,1,0,
+ 1,1,0, 1,1,0, 1,1,0, 1,1,0, 1,1,0, 1,1,0, 1,1,0, 1,1,0, 1,1,0, 1,1,0,
+ 1,1,0, 1,1,0, 1,1,0, 1,1,0, 1,1,0, 1,1,0, 1,1,0, 1,1,0, 1,1,0, 1,1,0,
+ 1,1,0, 1,1,0, 1,1,0, 1,1,0, 1,1,0, 1,1,0, 1,1,0, 1,1,0, 1,1,0, 1,1,0,
+ 1,1,0, 1,1,0, 1,1,0, 1,1,0, 1,1,0, 1,1,0, 1,1,0, 1,1,0, 1,1,0, 1,1,0,
+ 1,1,0, 1,1,0, 1,1,0, 1,1,0, 1,1,0, 1,1,0, 1,1,0, 1,1,0, 1,1,0, 1,1,0,
+ 1,1,0, 1,1,0, 1,1,0, 1,1,0, 1,1,0, 1,1,0, 1,1,0, 1,1,0, 1,1,0, 1,1,0,
+ 1,1,0, 1,1,0, 1,1,0, 1,1,0, 1,1,0, 1,1,0, 1,1,0, 1,1,0, 1,1,0, 1,1,0,
+ 1,1,0, 1,1,0, 1,1,0, 1,1,0, 1,1,0, 1,1,0, 1,1,0, 1,1,0, 1,1,0, 1,1,0,
+ 1,1,0, 1,1,0, 1,1,0, 1,1,0, 1,1,0, 1,1,0, 1,1,0, 1,1,0, 1,1,0, 1,1,0,
+ 1,1,0, 1,1,0, 1,1,0, 1,1,0, 1,1,0, 1,1,0, 1,1,0, 1,1,0, 1,1,0, 1,1,0,
+ 1,1,0, 1,1,0, 1,1,0, 1,1,0, 1,1,0, 1,1,0, 1,1,0, 1,1,0, 1,1,0, 1,1,0,
+ 1,1,0, 1,1,0, 1,1,0, 1,1,0, 1,1,0, 1,1,0, 1,1,0, 1,1,0, 1,1,0, 1,1,0,
+ 1,1,0, 1,1,0, 1,1,0, 1,1,0, 1,1,0, 1,1,0, 1,1,0, 1,1,0, 1,1,0, 1,1,0,
+ 1,1,0, 1,1,0, 1,1,0, 1,1,0, 1,1,0, 1,1,0, 1,1,0, 1,1,0, 1,1,0, 1,1,0,
+ 1,1,0, 1,1,0, 1,1,0, 1,1,0, 1,1,0, 1,1,0, 1,1,0, 1,1,0, 1,1,0, 1,1,0,
+ 1,1,0, 1,1,0, 1,1,0, 1,1,0, 1,1,0, 1,1,0, 1,1,0, 1,1,0, 1,1,0, 1,1,0,
+ 1,1,0, 1,1,0, 1,1,0, 1,1,0, 1,1,0, 1,1,0, 1,1,0, 1,1,0, 1,1,0, 1,1,0,
+ 1,1,0, 1,1,0, 1,1,0, 1,1,0, 1,1,0, 1,1,0, 1,1,0, 1,1,0, 1,1,0, 1,1,0,
+ 1,1,0, 1,1,0, 1,1,0, 1,1,0, 1,1,0, 1,1,0, 1,1,0, 1,1,0, 1,1,0, 1,1,0,
+ 1,1,0, 1,1,0, 1,1,0, 1,1,0, 1,1,0, 1,1,0, 1,1,0, 1,1,0, 1,1,0, 1,1,0,
+ 1,1,0, 1,1,0, 1,1,0, 1,1,0, 1,1,0, 1,1,0, 1,1,0, 1,1,0, 1,1,0, 1,1,0,
+ 1,1,0, 1,1,0, 1,1,0, 1,1,0, 1,1,0, 1,1,0, 1,1,0, 1,1,0, 1,1,0, 1,1,0,
+ 1,1,0, 1,1,0, 1,1,0, 1,1,0, 1,1,0, 1,1,0, 1,1,0, 1,1,0, 1,1,0, 1,1,0,
+ 1,1,0, 1,1,0,
+};
+
+const uint8_t gsm0503_puncture_mcs5_p1[1404] = {
+ 0,0,1,0,0,0,0,0,0, 0,0,1,0,0,0,0,0,0, 0,0,1,0,0,0,0,0,0,
+ 0,0,1,0,0,0,0,0,0, 0,0,1,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,
+ 0,0,1,0,0,0,0,0,0, 0,0,1,0,0,0,0,0,0, 0,0,1,0,0,0,0,0,0,
+ 0,0,1,0,0,0,0,0,0, 0,0,1,0,0,0,0,0,0, 0,0,1,0,0,0,0,0,0,
+ 0,0,1,0,0,0,0,0,0, 0,0,1,0,0,0,0,0,0, 0,0,1,0,0,0,0,0,0,
+ 0,0,1,0,0,0,0,0,0, 0,0,1,0,0,0,0,0,0, 0,0,1,0,0,0,0,0,0,
+ 0,0,1,0,0,0,0,0,0, 0,0,1,0,0,0,0,0,0, 0,0,1,0,0,0,0,0,0,
+ 0,0,1,0,0,0,0,0,0, 0,0,1,0,0,0,0,0,0, 0,0,1,0,0,0,0,0,0,
+ 0,0,1,0,0,0,0,0,0, 0,0,1,0,0,0,0,0,0, 0,0,1,0,0,0,0,0,0,
+ 0,0,1,0,0,0,0,0,0, 0,0,1,0,0,0,0,0,0, 0,0,1,0,0,0,0,0,0,
+ 0,0,1,0,0,0,0,0,0, 0,0,1,0,0,0,0,0,0, 0,0,1,0,0,0,0,0,0,
+ 0,0,1,0,0,0,0,0,0, 0,0,1,0,0,0,0,0,0, 0,0,1,0,0,0,0,0,0,
+ 0,0,1,0,0,0,0,0,0, 0,0,1,0,0,0,0,0,0, 0,0,1,0,0,0,0,0,0,
+ 0,0,1,0,0,0,0,0,0, 0,0,1,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,
+ 0,0,1,0,0,0,0,0,0, 0,0,1,0,0,0,0,0,0, 0,0,1,0,0,0,0,0,0,
+ 0,0,1,0,0,0,0,0,0, 0,0,1,0,0,0,0,0,0, 0,0,1,0,0,0,0,0,0,
+ 0,0,1,0,0,0,0,0,0, 0,0,1,0,0,0,0,0,0, 0,0,1,0,0,0,0,0,0,
+ 0,0,1,0,0,0,0,0,0, 0,0,1,0,0,0,0,0,0, 0,0,1,0,0,0,0,0,0,
+ 0,0,1,0,0,0,0,0,0, 0,0,1,0,0,0,0,0,0, 0,0,1,0,0,0,0,0,0,
+ 0,0,1,0,0,0,0,0,0, 0,0,1,0,0,0,0,0,0, 0,0,1,0,0,0,0,0,0,
+ 0,0,1,0,0,0,0,0,0, 0,0,1,0,0,0,0,0,0, 0,0,1,0,0,0,0,0,0,
+ 0,0,1,0,0,0,0,0,0, 0,0,1,0,0,0,0,0,0, 0,0,1,0,0,0,0,0,0,
+ 0,0,1,0,0,0,0,0,0, 0,0,1,0,0,0,0,0,0, 0,0,1,0,0,0,0,0,0,
+ 0,0,1,0,0,0,0,0,0, 0,0,1,0,0,0,0,0,0, 0,0,1,0,0,0,0,0,0,
+ 0,0,1,0,0,0,0,0,0, 0,0,1,0,0,0,0,0,0, 0,0,1,0,0,0,0,0,0,
+ 0,0,1,0,0,0,0,0,0, 0,0,1,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,
+ 0,0,1,0,0,0,0,0,0, 0,0,1,0,0,0,0,0,0, 0,0,1,0,0,0,0,0,0,
+ 0,0,1,0,0,0,0,0,0, 0,0,1,0,0,0,0,0,0, 0,0,1,0,0,0,0,0,0,
+ 0,0,1,0,0,0,0,0,0, 0,0,1,0,0,0,0,0,0, 0,0,1,0,0,0,0,0,0,
+ 0,0,1,0,0,0,0,0,0, 0,0,1,0,0,0,0,0,0, 0,0,1,0,0,0,0,0,0,
+ 0,0,1,0,0,0,0,0,0, 0,0,1,0,0,0,0,0,0, 0,0,1,0,0,0,0,0,0,
+ 0,0,1,0,0,0,0,0,0, 0,0,1,0,0,0,0,0,0, 0,0,1,0,0,0,0,0,0,
+ 0,0,1,0,0,0,0,0,0, 0,0,1,0,0,0,0,0,0, 0,0,1,0,0,0,0,0,0,
+ 0,0,1,0,0,0,0,0,0, 0,0,1,0,0,0,0,0,0, 0,0,1,0,0,0,0,0,0,
+ 0,0,1,0,0,0,0,0,0, 0,0,1,0,0,0,0,0,0, 0,0,1,0,0,0,0,0,0,
+ 0,0,1,0,0,0,0,0,0, 0,0,1,0,0,0,0,0,0, 0,0,1,0,0,0,0,0,0,
+ 0,0,1,0,0,0,0,0,0, 0,0,1,0,0,0,0,0,0, 0,0,1,0,0,0,0,0,0,
+ 0,0,1,0,0,0,0,0,0, 0,0,1,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,
+ 0,0,1,0,0,0,0,0,0, 0,0,1,0,0,0,0,0,0, 0,0,1,0,0,0,0,0,0,
+ 0,0,1,0,0,0,0,0,0, 0,0,1,0,0,0,0,0,0, 0,0,1,0,0,0,0,0,0,
+ 0,0,1,0,0,0,0,0,0, 0,0,1,0,0,0,0,0,0, 0,0,1,0,0,0,0,0,0,
+ 0,0,1,0,0,0,0,0,0, 0,0,1,0,0,0,0,0,0, 0,0,1,0,0,0,0,0,0,
+ 0,0,1,0,0,0,0,0,0, 0,0,1,0,0,0,0,0,0, 0,0,1,0,0,0,0,0,0,
+ 0,0,1,0,0,0,0,0,0, 0,0,1,0,0,0,0,0,0, 0,0,1,0,0,0,0,0,0,
+ 0,0,1,0,0,0,0,0,0, 0,0,1,0,0,0,0,0,0, 0,0,1,0,0,0,0,0,0,
+ 0,0,1,0,0,0,0,0,0, 0,0,1,0,0,0,0,0,0, 0,0,1,0,0,0,0,0,0,
+ 0,0,1,0,0,0,0,0,0, 0,0,1,0,0,0,0,0,0, 0,0,1,0,0,0,0,0,0,
+ 0,0,1,0,0,0,0,0,0, 0,0,1,0,0,0,0,0,0, 0,0,1,0,0,0,0,0,0,
+ 0,0,1,0,0,0,0,0,0, 0,0,1,0,0,0,0,0,0, 0,0,1,0,0,0,0,0,0,
+ 0,0,1,0,0,0,0,0,0, 0,0,1,0,0,0,0,0,0, 0,0,1,0,0,0,0,0,0,
+ 0,0,1,0,0,0,0,0,0, 0,0,1,0,0,0,0,0,0, 0,0,1,0,0,0,0,0,0,
+ 0,0,1,0,0,0,0,0,0, 0,0,1,0,0,1,0,0,1, 0,0,1,0,0,1,0,0,1,
+};
+
+const uint8_t gsm0503_puncture_mcs5_p2[1404] = {
+ 0,1,0,0,0,0,0,0,0, 0,1,0,0,0,0,0,0,0, 0,1,0,0,0,0,0,0,0,
+ 0,1,0,0,0,0,0,0,0, 0,1,0,0,0,0,0,0,0, 0,1,0,0,0,0,0,0,0,
+ 0,1,0,0,0,0,0,0,0, 0,1,0,0,0,0,0,0,0, 0,1,0,0,0,0,0,0,0,
+ 0,1,0,0,0,0,0,0,0, 0,1,0,0,0,0,0,0,0, 0,1,0,0,0,0,0,0,0,
+ 0,1,0,0,0,0,0,0,0, 0,1,0,0,0,0,0,0,0, 0,1,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,0, 0,1,0,0,0,0,0,0,0, 0,1,0,0,0,0,0,0,0,
+ 0,1,0,0,0,0,0,0,0, 0,1,0,0,0,0,0,0,0, 0,1,0,0,0,0,0,0,0,
+ 0,1,0,0,0,0,0,0,0, 0,1,0,0,0,0,0,0,0, 0,1,0,0,0,0,0,0,0,
+ 0,1,0,0,0,0,0,0,0, 0,1,0,0,0,0,0,0,0, 0,1,0,0,0,0,0,0,0,
+ 0,1,0,0,0,0,0,0,0, 0,1,0,0,0,0,0,0,0, 0,1,0,0,0,0,0,0,0,
+ 0,1,0,0,0,0,0,0,0, 0,1,0,0,0,0,0,0,0, 0,1,0,0,0,0,0,0,0,
+ 0,1,0,0,0,0,0,0,0, 0,1,0,0,0,0,0,0,0, 0,1,0,0,0,0,0,0,0,
+ 0,1,0,0,0,0,0,0,0, 0,1,0,0,0,0,0,0,0, 0,1,0,0,0,0,0,0,0,
+ 0,1,0,0,0,0,0,0,0, 0,1,0,0,0,0,0,0,0, 0,1,0,0,0,0,0,0,0,
+ 0,1,0,0,0,0,0,0,0, 0,1,0,0,0,0,0,0,0, 0,1,0,0,0,0,0,0,0,
+ 0,1,0,0,0,0,0,0,0, 0,1,0,0,0,0,0,0,0, 0,1,0,0,0,0,0,0,0,
+ 0,1,0,0,0,0,0,0,0, 0,1,0,0,0,0,0,0,0, 0,1,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,0, 0,1,0,0,0,0,0,0,0, 0,1,0,0,0,0,0,0,0,
+ 0,1,0,0,0,0,0,0,0, 0,1,0,0,0,0,0,0,0, 0,1,0,0,0,0,0,0,0,
+ 0,1,0,0,0,0,0,0,0, 0,1,0,0,0,0,0,0,0, 0,1,0,0,0,0,0,0,0,
+ 0,1,0,0,0,0,0,0,0, 0,1,0,0,0,0,0,0,0, 0,1,0,0,0,0,0,0,0,
+ 0,1,0,0,0,0,0,0,0, 0,1,0,0,0,0,0,0,0, 0,1,0,0,0,0,0,0,0,
+ 0,1,0,0,0,0,0,0,0, 0,1,0,0,0,0,0,0,0, 0,1,0,0,0,0,0,0,0,
+ 0,1,0,0,0,0,0,0,0, 0,1,0,0,0,0,0,0,0, 0,1,0,0,0,0,0,0,0,
+ 0,1,0,0,0,0,0,0,0, 0,1,0,0,0,0,0,0,0, 0,1,0,0,0,0,0,0,0,
+ 0,1,0,0,0,0,0,0,0, 0,1,0,0,0,0,0,0,0, 0,1,0,0,0,0,0,0,0,
+ 0,1,0,0,0,0,0,0,0, 0,1,0,0,0,0,0,0,0, 0,1,0,0,0,0,0,0,0,
+ 0,1,0,0,0,0,0,0,0, 0,1,0,0,0,0,0,0,0, 0,1,0,0,0,0,0,0,0,
+ 0,1,0,0,0,0,0,0,0, 0,1,0,0,0,0,0,0,0, 0,1,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,0, 0,1,0,0,0,0,0,0,0, 0,1,0,0,0,0,0,0,0,
+ 0,1,0,0,0,0,0,0,0, 0,1,0,0,0,0,0,0,0, 0,1,0,0,0,0,0,0,0,
+ 0,1,0,0,0,0,0,0,0, 0,1,0,0,0,0,0,0,0, 0,1,0,0,0,0,0,0,0,
+ 0,1,0,0,0,0,0,0,0, 0,1,0,0,0,0,0,0,0, 0,1,0,0,0,0,0,0,0,
+ 0,1,0,0,0,0,0,0,0, 0,1,0,0,0,0,0,0,0, 0,1,0,0,0,0,0,0,0,
+ 0,1,0,0,0,0,0,0,0, 0,1,0,0,0,0,0,0,0, 0,1,0,0,0,0,0,0,0,
+ 0,1,0,0,0,0,0,0,0, 0,1,0,0,0,0,0,0,0, 0,1,0,0,0,0,0,0,0,
+ 0,1,0,0,0,0,0,0,0, 0,1,0,0,0,0,0,0,0, 0,1,0,0,0,0,0,0,0,
+ 0,1,0,0,0,0,0,0,0, 0,1,0,0,0,0,0,0,0, 0,1,0,0,0,0,0,0,0,
+ 0,1,0,0,0,0,0,0,0, 0,1,0,0,0,0,0,0,0, 0,1,0,0,0,0,0,0,0,
+ 0,1,0,0,0,0,0,0,0, 0,1,0,0,0,0,0,0,0, 0,1,0,0,0,0,0,0,0,
+ 0,1,0,0,0,0,0,0,0, 0,1,0,0,0,0,0,0,0, 0,1,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,0, 0,1,0,0,0,0,0,0,0, 0,1,0,0,0,0,0,0,0,
+ 0,1,0,0,0,0,0,0,0, 0,1,0,0,0,0,0,0,0, 0,1,0,0,0,0,0,0,0,
+ 0,1,0,0,0,0,0,0,0, 0,1,0,0,0,0,0,0,0, 0,1,0,0,0,0,0,0,0,
+ 0,1,0,0,0,0,0,0,0, 0,1,0,0,0,0,0,0,0, 0,1,0,0,0,0,0,0,0,
+ 0,1,0,0,0,0,0,0,0, 0,1,0,0,0,0,0,0,0, 0,1,0,0,0,0,0,0,0,
+ 0,1,0,0,0,0,0,0,0, 0,1,0,0,0,0,0,0,0, 0,1,0,0,0,0,0,0,0,
+ 0,1,0,0,0,0,0,0,0, 0,1,0,0,0,0,0,0,0, 0,1,0,0,0,0,0,0,0,
+ 0,1,0,0,0,0,0,0,0, 0,1,0,0,0,0,0,0,0, 0,1,0,0,0,0,0,0,0,
+ 0,1,0,0,0,0,0,0,0, 0,1,0,0,0,0,0,0,0, 0,1,0,0,0,0,0,0,0,
+ 0,1,0,0,0,0,0,0,0, 0,1,0,0,0,0,0,0,0, 0,1,0,0,0,0,0,0,0,
+ 0,1,0,0,0,0,0,0,0, 0,1,0,0,1,0,0,1,0, 0,1,0,0,1,0,0,1,0,
+};
+
+const uint8_t gsm0503_puncture_mcs6_p1[1836] = {
+ 0,0,1, 0,0,1, 0,0,1, 0,0,1, 0,0,1, 0,0,1, 0,0,1, 0,0,1, 0,0,1, 0,0,1,
+ 0,0,0, 0,0,1, 0,0,1, 0,0,1, 0,0,1, 0,0,1, 0,0,1, 0,0,1, 0,0,1, 0,0,1,
+ 0,0,1, 0,0,1, 0,0,1, 0,0,1, 0,0,1, 0,0,1, 0,0,1, 0,0,1, 0,0,1, 0,0,1,
+ 0,0,1, 0,0,1, 0,0,0, 0,0,1, 0,0,1, 0,0,1, 0,0,1, 0,0,1, 0,0,1, 0,0,1,
+ 0,0,1, 0,0,1, 0,0,1, 0,0,1, 0,0,1, 0,0,1, 0,0,1, 0,0,1, 0,0,1, 0,0,1,
+ 0,0,1, 0,0,1, 0,0,1, 0,0,1, 0,0,0, 0,0,1, 0,0,1, 0,0,1, 0,0,1, 0,0,1,
+ 0,0,1, 0,0,1, 0,0,1, 0,0,1, 0,0,1, 0,0,1, 0,0,1, 0,0,1, 0,0,1, 0,0,1,
+ 0,0,1, 0,0,1, 0,0,1, 0,0,1, 0,0,1, 0,0,1, 0,0,0, 0,0,1, 0,0,1, 0,0,1,
+ 0,0,1, 0,0,1, 0,0,1, 0,0,1, 0,0,1, 0,0,1, 0,0,1, 0,0,1, 0,0,1, 0,0,1,
+ 0,0,1, 0,0,1, 0,0,1, 0,0,1, 0,0,1, 0,0,1, 0,0,1, 0,0,1, 0,0,0, 0,0,1,
+ 0,0,1, 0,0,1, 0,0,1, 0,0,1, 0,0,1, 0,0,1, 0,0,1, 0,0,1, 0,0,1, 0,0,1,
+ 0,0,1, 0,0,1, 0,0,1, 0,0,1, 0,0,1, 0,0,1, 0,0,1, 0,0,1, 0,0,1, 0,0,1,
+ 0,0,1, 0,0,1, 0,0,1, 0,0,1, 0,0,1, 0,0,1, 0,0,1, 0,0,1, 0,0,1, 0,0,1,
+ 0,0,1, 0,0,1, 0,0,1, 0,0,1, 0,0,1, 0,0,1, 0,0,1, 0,0,1, 0,0,1, 0,0,1,
+ 0,0,1, 0,0,1, 0,0,0, 0,0,1, 0,0,1, 0,0,1, 0,0,1, 0,0,1, 0,0,1, 0,0,1,
+ 0,0,1, 0,0,1, 0,0,1, 0,0,1, 0,0,1, 0,0,1, 0,0,1, 0,0,1, 0,0,1, 0,0,1,
+ 0,0,1, 0,0,1, 0,0,1, 0,0,1, 0,0,0, 0,0,1, 0,0,1, 0,0,1, 0,0,1, 0,0,1,
+ 0,0,1, 0,0,1, 0,0,1, 0,0,1, 0,0,1, 0,0,1, 0,0,1, 0,0,1, 0,0,1, 0,0,1,
+ 0,0,1, 0,0,1, 0,0,1, 0,0,1, 0,0,1, 0,0,1, 0,0,0, 0,0,1, 0,0,1, 0,0,1,
+ 0,0,1, 0,0,1, 0,0,1, 0,0,1, 0,0,1, 0,0,1, 0,0,1, 0,0,1, 0,0,1, 0,0,1,
+ 0,0,1, 0,0,1, 0,0,1, 0,0,1, 0,0,1, 0,0,1, 0,0,1, 0,0,1, 0,0,0, 0,0,1,
+ 0,0,1, 0,0,1, 0,0,1, 0,0,1, 0,0,1, 0,0,1, 0,0,1, 0,0,1, 0,0,1, 0,0,1,
+ 0,0,1, 0,0,1, 0,0,1, 0,0,1, 0,0,1, 0,0,1, 0,0,1, 0,0,1, 0,0,1, 0,0,1,
+ 0,0,0, 0,0,1, 0,0,1, 0,0,1, 0,0,1, 0,0,1, 0,0,1, 0,0,1, 0,0,1, 0,0,1,
+ 0,0,1, 0,0,1, 0,0,1, 0,0,1, 0,0,1, 0,0,1, 0,0,1, 0,0,1, 0,0,1, 0,0,1,
+ 0,0,1, 0,0,1, 0,0,1, 0,0,1, 0,0,1, 0,0,1, 0,0,1, 0,0,1, 0,0,1, 0,0,1,
+ 0,0,1, 0,0,1, 0,0,1, 0,0,1, 0,0,1, 0,0,1, 0,0,1, 0,0,1, 0,0,1, 0,0,1,
+ 0,0,1, 0,0,1, 0,0,1, 0,0,1, 0,0,0, 0,0,1, 0,0,1, 0,0,1, 0,0,1, 0,0,1,
+ 0,0,1, 0,0,1, 0,0,1, 0,0,1, 0,0,1, 0,0,1, 0,0,1, 0,0,1, 0,0,1, 0,0,1,
+ 0,0,1, 0,0,1, 0,0,1, 0,0,1, 0,0,1, 0,0,1, 0,0,0, 0,0,1, 0,0,1, 0,0,1,
+ 0,0,1, 0,0,1, 0,0,1, 0,0,1, 0,0,1, 0,0,1, 0,0,1, 0,0,1, 0,0,1, 0,0,1,
+ 0,0,1, 0,0,1, 0,0,1, 0,0,1, 0,0,1, 0,0,1, 0,0,1, 0,0,1, 0,0,0, 0,0,1,
+ 0,0,1, 0,0,1, 0,0,1, 0,0,1, 0,0,1, 0,0,1, 0,0,1, 0,0,1, 0,0,1, 0,0,1,
+ 0,0,1, 0,0,1, 0,0,1, 0,0,1, 0,0,1, 0,0,1, 0,0,1, 0,0,1, 0,0,1, 0,0,1,
+ 0,0,0, 0,0,1, 0,0,1, 0,0,1, 0,0,1, 0,0,1, 0,0,1, 0,0,1, 0,0,1, 0,0,1,
+ 0,0,1, 0,0,1, 0,0,1, 0,0,1, 0,0,1, 0,0,1, 0,0,1, 0,0,1, 0,0,1, 0,0,1,
+ 0,0,1, 0,0,1, 0,0,0, 0,0,1, 0,0,1, 0,0,1, 0,0,1, 0,0,1, 0,0,1, 0,0,1,
+ 0,0,1, 0,0,1, 0,0,1, 0,0,1, 0,0,1, 0,0,1, 0,0,1, 0,0,1, 0,0,1, 0,0,1,
+ 0,0,1, 0,0,1, 0,0,1, 0,0,1, 0,0,1, 0,0,1, 0,0,1, 0,0,1, 0,0,1, 0,0,1,
+ 0,0,1, 0,0,1, 0,0,1, 0,0,1, 0,0,1, 0,0,1, 0,0,1, 0,0,1, 0,0,1, 0,0,1,
+ 0,0,1, 0,0,1, 0,0,1, 0,0,1, 0,0,1, 0,0,1, 0,0,0, 0,0,1, 0,0,1, 0,0,1,
+ 0,0,1, 0,0,1, 0,0,1, 0,0,1, 0,0,1, 0,0,1, 0,0,1, 0,0,1, 0,0,1, 0,0,1,
+ 0,0,1, 0,0,1, 0,0,1, 0,0,1, 0,0,1, 0,0,1, 0,0,1, 0,0,1, 0,0,0, 0,0,1,
+ 0,0,1, 0,0,1, 0,0,1, 0,0,1, 0,0,1, 0,0,1, 0,0,1, 0,0,1, 0,0,1, 0,0,1,
+ 0,0,1, 0,0,1, 0,0,1, 0,0,1, 0,0,1, 0,0,1, 0,0,1, 0,0,1, 0,0,1, 0,0,1,
+ 0,0,0, 0,0,1, 0,0,1, 0,0,1, 0,0,1, 0,0,1, 0,0,1, 0,0,1, 0,0,1, 0,0,1,
+ 0,0,1, 0,0,1, 0,0,1, 0,0,1, 0,0,1, 0,0,1, 0,0,1, 0,0,1, 0,0,1, 0,0,1,
+ 0,0,1, 0,0,1, 0,0,0, 0,0,1, 0,0,1, 0,0,1, 0,0,1, 0,0,1, 0,0,1, 0,0,1,
+ 0,0,1, 0,0,1, 0,0,1, 0,0,1, 0,0,1, 0,0,1, 0,0,1, 0,0,1, 0,0,1, 0,0,1,
+ 0,0,1, 0,0,1, 0,0,1, 0,0,1, 0,0,0, 0,0,1, 0,0,1, 0,0,1, 0,0,1, 0,0,1,
+ 0,0,1, 0,0,1, 0,0,1, 0,0,1, 0,0,1, 0,0,1, 0,0,1, 0,0,1, 0,0,1, 0,0,1,
+ 0,0,1, 0,0,1, 0,0,1, 0,0,1, 0,0,1, 0,0,1, 0,0,1, 0,0,1, 0,0,1, 0,0,1,
+ 0,0,1, 0,0,1, 0,0,1, 0,0,1, 0,0,1, 0,0,1, 0,0,1, 0,0,1, 0,0,1, 0,0,1,
+ 0,0,1, 0,0,1, 0,0,1, 0,0,1, 0,0,1, 0,0,1, 0,0,1, 0,0,1, 0,0,0, 0,0,1,
+ 0,0,1, 0,0,1, 0,0,1, 0,0,1, 0,0,1, 0,0,1, 0,0,1, 0,0,1, 0,0,1, 0,0,1,
+ 0,0,1, 0,0,1, 0,0,1, 0,0,1, 0,0,1, 0,0,1, 0,0,1, 0,0,1, 0,0,1, 0,0,1,
+ 0,0,0, 0,0,1, 0,0,1, 0,0,1, 0,0,1, 0,0,1, 0,0,1, 0,0,1, 0,0,1, 0,0,1,
+ 0,0,1, 0,0,1, 0,0,1, 0,0,1, 0,0,1, 0,0,1, 0,0,1, 0,0,1, 0,0,1, 0,0,1,
+ 0,0,1, 0,0,1, 0,0,0, 0,0,1, 0,0,1, 0,0,1, 0,0,1, 0,0,1, 0,0,1, 0,0,1,
+ 0,0,1, 0,0,1, 0,0,1, 0,0,1, 0,0,1, 0,0,1, 0,0,1, 0,0,1, 0,0,1, 0,0,1,
+ 0,0,1, 0,0,1, 0,0,1, 0,0,1, 0,0,0, 0,0,1, 0,0,1, 0,0,1, 0,0,1, 0,0,1,
+ 0,0,1, 0,0,1,
+};
+
+const uint8_t gsm0503_puncture_mcs6_p2[1836] = {
+ 0,1,0, 0,1,0, 0,1,0, 0,1,0, 0,1,0, 0,0,0, 0,1,0, 0,1,0, 0,1,0, 0,1,0,
+ 0,1,0, 0,1,0, 0,1,0, 0,1,0, 0,1,0, 0,1,0, 0,1,0, 0,1,0, 0,1,0, 0,1,0,
+ 0,1,0, 0,1,0, 0,1,0, 0,1,0, 0,1,0, 0,1,0, 0,1,0, 0,0,0, 0,1,0, 0,1,0,
+ 0,1,0, 0,1,0, 0,1,0, 0,1,0, 0,1,0, 0,1,0, 0,1,0, 0,1,0, 0,1,0, 0,1,0,
+ 0,1,0, 0,1,0, 0,1,0, 0,1,0, 0,1,0, 0,1,0, 0,1,0, 0,1,0, 0,1,0, 0,0,0,
+ 0,1,0, 0,1,0, 0,1,0, 0,1,0, 0,1,0, 0,1,0, 0,1,0, 0,1,0, 0,1,0, 0,1,0,
+ 0,1,0, 0,1,0, 0,1,0, 0,1,0, 0,1,0, 0,1,0, 0,1,0, 0,1,0, 0,1,0, 0,1,0,
+ 0,1,0, 0,0,0, 0,1,0, 0,1,0, 0,1,0, 0,1,0, 0,1,0, 0,1,0, 0,1,0, 0,1,0,
+ 0,1,0, 0,1,0, 0,1,0, 0,1,0, 0,1,0, 0,1,0, 0,1,0, 0,1,0, 0,1,0, 0,1,0,
+ 0,1,0, 0,1,0, 0,1,0, 0,0,0, 0,1,0, 0,1,0, 0,1,0, 0,1,0, 0,1,0, 0,1,0,
+ 0,1,0, 0,1,0, 0,1,0, 0,1,0, 0,1,0, 0,1,0, 0,1,0, 0,1,0, 0,1,0, 0,1,0,
+ 0,1,0, 0,1,0, 0,1,0, 0,1,0, 0,1,0, 0,1,0, 0,1,0, 0,1,0, 0,1,0, 0,1,0,
+ 0,1,0, 0,1,0, 0,1,0, 0,1,0, 0,1,0, 0,1,0, 0,1,0, 0,1,0, 0,1,0, 0,1,0,
+ 0,1,0, 0,1,0, 0,1,0, 0,1,0, 0,1,0, 0,1,0, 0,1,0, 0,0,0, 0,1,0, 0,1,0,
+ 0,1,0, 0,1,0, 0,1,0, 0,1,0, 0,1,0, 0,1,0, 0,1,0, 0,1,0, 0,1,0, 0,1,0,
+ 0,1,0, 0,1,0, 0,1,0, 0,1,0, 0,1,0, 0,1,0, 0,1,0, 0,1,0, 0,1,0, 0,0,0,
+ 0,1,0, 0,1,0, 0,1,0, 0,1,0, 0,1,0, 0,1,0, 0,1,0, 0,1,0, 0,1,0, 0,1,0,
+ 0,1,0, 0,1,0, 0,1,0, 0,1,0, 0,1,0, 0,1,0, 0,1,0, 0,1,0, 0,1,0, 0,1,0,
+ 0,1,0, 0,0,0, 0,1,0, 0,1,0, 0,1,0, 0,1,0, 0,1,0, 0,1,0, 0,1,0, 0,1,0,
+ 0,1,0, 0,1,0, 0,1,0, 0,1,0, 0,1,0, 0,1,0, 0,1,0, 0,1,0, 0,1,0, 0,1,0,
+ 0,1,0, 0,1,0, 0,1,0, 0,0,0, 0,1,0, 0,1,0, 0,1,0, 0,1,0, 0,1,0, 0,1,0,
+ 0,1,0, 0,1,0, 0,1,0, 0,1,0, 0,1,0, 0,1,0, 0,1,0, 0,1,0, 0,1,0, 0,1,0,
+ 0,1,0, 0,1,0, 0,1,0, 0,1,0, 0,1,0, 0,0,0, 0,1,0, 0,1,0, 0,1,0, 0,1,0,
+ 0,1,0, 0,1,0, 0,1,0, 0,1,0, 0,1,0, 0,1,0, 0,1,0, 0,1,0, 0,1,0, 0,1,0,
+ 0,1,0, 0,1,0, 0,1,0, 0,1,0, 0,1,0, 0,1,0, 0,1,0, 0,1,0, 0,1,0, 0,1,0,
+ 0,1,0, 0,1,0, 0,1,0, 0,1,0, 0,1,0, 0,1,0, 0,1,0, 0,1,0, 0,1,0, 0,1,0,
+ 0,1,0, 0,1,0, 0,1,0, 0,1,0, 0,1,0, 0,1,0, 0,1,0, 0,1,0, 0,1,0, 0,0,0,
+ 0,1,0, 0,1,0, 0,1,0, 0,1,0, 0,1,0, 0,1,0, 0,1,0, 0,1,0, 0,1,0, 0,1,0,
+ 0,1,0, 0,1,0, 0,1,0, 0,1,0, 0,1,0, 0,1,0, 0,1,0, 0,1,0, 0,1,0, 0,1,0,
+ 0,1,0, 0,0,0, 0,1,0, 0,1,0, 0,1,0, 0,1,0, 0,1,0, 0,1,0, 0,1,0, 0,1,0,
+ 0,1,0, 0,1,0, 0,1,0, 0,1,0, 0,1,0, 0,1,0, 0,1,0, 0,1,0, 0,1,0, 0,1,0,
+ 0,1,0, 0,1,0, 0,1,0, 0,0,0, 0,1,0, 0,1,0, 0,1,0, 0,1,0, 0,1,0, 0,1,0,
+ 0,1,0, 0,1,0, 0,1,0, 0,1,0, 0,1,0, 0,1,0, 0,1,0, 0,1,0, 0,1,0, 0,1,0,
+ 0,1,0, 0,1,0, 0,1,0, 0,1,0, 0,1,0, 0,0,0, 0,1,0, 0,1,0, 0,1,0, 0,1,0,
+ 0,1,0, 0,1,0, 0,1,0, 0,1,0, 0,1,0, 0,1,0, 0,1,0, 0,1,0, 0,1,0, 0,1,0,
+ 0,1,0, 0,1,0, 0,1,0, 0,1,0, 0,1,0, 0,1,0, 0,1,0, 0,0,0, 0,1,0, 0,1,0,
+ 0,1,0, 0,1,0, 0,1,0, 0,1,0, 0,1,0, 0,1,0, 0,1,0, 0,1,0, 0,1,0, 0,1,0,
+ 0,1,0, 0,1,0, 0,1,0, 0,1,0, 0,1,0, 0,1,0, 0,1,0, 0,1,0, 0,1,0, 0,1,0,
+ 0,1,0, 0,1,0, 0,1,0, 0,1,0, 0,1,0, 0,1,0, 0,1,0, 0,1,0, 0,1,0, 0,1,0,
+ 0,1,0, 0,1,0, 0,1,0, 0,1,0, 0,1,0, 0,1,0, 0,1,0, 0,1,0, 0,1,0, 0,1,0,
+ 0,1,0, 0,0,0, 0,1,0, 0,1,0, 0,1,0, 0,1,0, 0,1,0, 0,1,0, 0,1,0, 0,1,0,
+ 0,1,0, 0,1,0, 0,1,0, 0,1,0, 0,1,0, 0,1,0, 0,1,0, 0,1,0, 0,1,0, 0,1,0,
+ 0,1,0, 0,1,0, 0,1,0, 0,0,0, 0,1,0, 0,1,0, 0,1,0, 0,1,0, 0,1,0, 0,1,0,
+ 0,1,0, 0,1,0, 0,1,0, 0,1,0, 0,1,0, 0,1,0, 0,1,0, 0,1,0, 0,1,0, 0,1,0,
+ 0,1,0, 0,1,0, 0,1,0, 0,1,0, 0,1,0, 0,0,0, 0,1,0, 0,1,0, 0,1,0, 0,1,0,
+ 0,1,0, 0,1,0, 0,1,0, 0,1,0, 0,1,0, 0,1,0, 0,1,0, 0,1,0, 0,1,0, 0,1,0,
+ 0,1,0, 0,1,0, 0,1,0, 0,1,0, 0,1,0, 0,1,0, 0,1,0, 0,0,0, 0,1,0, 0,1,0,
+ 0,1,0, 0,1,0, 0,1,0, 0,1,0, 0,1,0, 0,1,0, 0,1,0, 0,1,0, 0,1,0, 0,1,0,
+ 0,1,0, 0,1,0, 0,1,0, 0,1,0, 0,1,0, 0,1,0, 0,1,0, 0,1,0, 0,1,0, 0,0,0,
+ 0,1,0, 0,1,0, 0,1,0, 0,1,0, 0,1,0, 0,1,0, 0,1,0, 0,1,0, 0,1,0, 0,1,0,
+ 0,1,0, 0,1,0, 0,1,0, 0,1,0, 0,1,0, 0,1,0, 0,1,0, 0,1,0, 0,1,0, 0,1,0,
+ 0,1,0, 0,1,0, 0,1,0, 0,1,0, 0,1,0, 0,1,0, 0,1,0, 0,1,0, 0,1,0, 0,1,0,
+ 0,1,0, 0,1,0, 0,1,0, 0,1,0, 0,1,0, 0,1,0, 0,1,0, 0,1,0, 0,1,0, 0,1,0,
+ 0,1,0, 0,1,0, 0,1,0, 0,0,0, 0,1,0, 0,1,0, 0,1,0, 0,1,0, 0,1,0, 0,1,0,
+ 0,1,0, 0,1,0, 0,1,0, 0,1,0, 0,1,0, 0,1,0, 0,1,0, 0,1,0, 0,1,0, 0,1,0,
+ 0,1,0, 0,1,0, 0,1,0, 0,1,0, 0,1,0, 0,0,0, 0,1,0, 0,1,0, 0,1,0, 0,1,0,
+ 0,1,0, 0,1,0, 0,1,0, 0,1,0, 0,1,0, 0,1,0, 0,1,0, 0,1,0, 0,1,0, 0,1,0,
+ 0,1,0, 0,1,0, 0,1,0, 0,1,0, 0,1,0, 0,1,0, 0,1,0, 0,0,0, 0,1,0, 0,1,0,
+ 0,1,0, 0,1,0, 0,1,0, 0,1,0, 0,1,0, 0,1,0, 0,1,0, 0,1,0, 0,1,0, 0,1,0,
+ 0,1,0, 0,1,0, 0,1,0, 0,1,0, 0,1,0, 0,1,0, 0,1,0, 0,1,0, 0,1,0, 0,0,0,
+ 0,1,0, 0,1,0, 0,1,0, 0,1,0, 0,1,0, 0,1,0, 0,1,0, 0,1,0, 0,1,0, 0,1,0,
+ 0,1,0, 0,1,0,
+};
+
+const uint8_t gsm0503_puncture_mcs7_dl_hdr[135] = {
+ 0,0,0,0,0,0,0,0,0,0,
+ 0,0,0,0,1,0,0,0,0,0,
+ 0,0,0,1,0,0,0,0,0,0,
+ 0,0,0,1,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,0,0,
+ 1,0,0,0,0,0,0,0,0,1,
+ 0,0,0,0,0,0,0,0,0,1,
+ 0,0,0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,1,0,0,0,
+ 0,0,0,0,0,1,0,0,0,0,
+ 0,0,0,0,0,1,0,0,0,0,
+ 0,0,0,0,0,0,0,0,0,0,
+ 0,0,1,0,0,0,0,0,0,0,
+ 0,1,0,0,0,
+};
+
+const uint8_t gsm0503_puncture_mcs7_ul_hdr[162] = {
+ 0,0,0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,1,0,0,0,0,
+ 0,0,0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,0,0,
+ 0,1,0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,0,0,
+ 0,0,
+};
+
+const uint8_t gsm0503_puncture_mcs7_p1[1404] = {
+ 0,1,1,1,0,1,1,1,0,1,1,0,0,0,1,0,1,1,
+ 0,1,1,1,0,1,1,1,0,1,1,0,0,0,1,0,1,1,
+ 0,1,1,1,0,1,1,1,0,1,1,0,0,0,1,0,1,1,
+ 0,0,1,1,0,1,1,1,0,1,1,0,0,0,1,0,1,1,
+ 0,0,1,1,0,1,1,1,0,1,1,0,0,0,1,0,1,1,
+ 0,0,1,1,0,1,1,1,0,1,1,0,0,0,1,0,1,1,
+ 0,0,1,1,0,1,1,1,0,1,1,0,0,0,1,0,1,1,
+ 0,0,1,1,0,1,1,1,0,1,1,0,0,0,1,0,1,1,
+ 0,0,1,1,0,1,1,1,0,1,1,0,0,0,1,0,1,1,
+ 0,0,1,1,0,1,1,1,0,1,1,0,0,0,1,0,1,1,
+ 0,0,1,1,0,1,1,1,0,1,1,0,0,0,1,0,1,1,
+ 0,0,1,1,0,1,1,1,0,1,1,0,0,0,1,0,1,1,
+ 0,0,1,1,0,1,1,1,0,1,1,0,0,0,1,0,1,1,
+ 0,1,1,1,0,1,1,1,0,1,1,0,0,0,1,0,1,1,
+ 0,0,1,1,0,1,1,1,0,1,1,0,0,0,1,0,1,1,
+ 0,0,1,1,0,1,1,1,0,1,1,0,0,0,1,0,1,1,
+ 0,0,1,1,0,1,1,1,0,1,1,0,0,0,1,0,1,1,
+ 0,0,1,1,0,1,1,1,0,1,1,0,0,0,1,0,1,1,
+ 0,0,1,1,0,1,1,1,0,1,1,0,0,0,1,0,1,1,
+ 0,0,1,1,0,1,1,1,0,1,1,0,0,0,1,0,1,1,
+ 0,0,1,1,0,1,1,1,0,1,1,0,0,0,1,0,1,1,
+ 0,0,1,1,0,1,1,1,0,1,1,0,0,0,1,0,1,1,
+ 0,0,1,1,0,1,1,1,0,1,1,0,0,0,1,0,1,1,
+ 0,1,1,1,0,1,1,1,0,1,1,0,0,0,1,0,1,1,
+ 0,0,1,1,0,1,1,1,0,1,1,0,0,0,1,0,1,1,
+ 0,0,1,1,0,1,1,1,0,1,1,0,0,0,1,0,1,1,
+ 0,0,1,1,0,1,1,1,0,1,1,0,0,0,1,0,1,1,
+ 0,0,1,1,0,1,1,1,0,1,1,0,0,0,1,0,1,1,
+ 0,0,1,1,0,1,1,1,0,1,1,0,0,0,1,0,1,1,
+ 0,0,1,1,0,1,1,1,0,1,1,0,0,0,1,0,1,1,
+ 0,0,1,1,0,1,1,1,0,1,1,0,0,0,1,0,1,1,
+ 0,0,1,1,0,1,1,1,0,1,1,0,0,0,1,0,1,1,
+ 0,0,1,1,0,1,1,1,0,1,1,0,0,0,1,0,1,1,
+ 0,1,1,1,0,1,1,1,0,1,1,0,0,0,1,0,1,1,
+ 0,0,1,1,0,1,1,1,0,1,1,0,0,0,1,0,1,1,
+ 0,0,1,1,0,1,1,1,0,1,1,0,0,0,1,0,1,1,
+ 0,0,1,1,0,1,1,1,0,1,1,0,0,0,1,0,1,1,
+ 0,0,1,1,0,1,1,1,0,1,1,0,0,0,1,0,1,1,
+ 0,0,1,1,0,1,1,1,0,1,1,0,0,0,1,0,1,1,
+ 0,0,1,1,0,1,1,1,0,1,1,0,0,0,1,0,1,1,
+ 0,0,1,1,0,1,1,1,0,1,1,0,0,0,1,0,1,1,
+ 0,0,1,1,0,1,1,1,0,1,1,0,0,0,1,0,1,1,
+ 0,0,1,1,0,1,1,1,0,1,1,0,0,0,1,0,1,1,
+ 0,1,1,1,0,1,1,1,0,1,1,0,0,0,1,0,1,1,
+ 0,0,1,1,0,1,1,1,0,1,1,0,0,0,1,0,1,1,
+ 0,0,1,1,0,1,1,1,0,1,1,0,0,0,1,0,1,1,
+ 0,0,1,1,0,1,1,1,0,1,1,0,0,0,1,0,1,1,
+ 0,0,1,1,0,1,1,1,0,1,1,0,0,0,1,0,1,1,
+ 0,0,1,1,0,1,1,1,0,1,1,0,0,0,1,0,1,1,
+ 0,0,1,1,0,1,1,1,0,1,1,0,0,0,1,0,1,1,
+ 0,0,1,1,0,1,1,1,0,1,1,0,0,0,1,0,1,1,
+ 0,0,1,1,0,1,1,1,0,1,1,0,0,0,1,0,1,1,
+ 0,0,1,1,0,1,1,1,0,1,1,0,0,0,1,0,1,1,
+ 0,1,1,1,0,1,1,1,0,1,1,0,0,0,1,0,1,1,
+ 0,0,1,1,0,1,1,1,0,1,1,0,0,0,1,0,1,1,
+ 0,0,1,1,0,1,1,1,0,1,1,0,0,0,1,0,1,1,
+ 0,0,1,1,0,1,1,1,0,1,1,0,0,0,1,0,1,1,
+ 0,0,1,1,0,1,1,1,0,1,1,0,0,0,1,0,1,1,
+ 0,0,1,1,0,1,1,1,0,1,1,0,0,0,1,0,1,1,
+ 0,0,1,1,0,1,1,1,0,1,1,0,0,0,1,0,1,1,
+ 0,0,1,1,0,1,1,1,0,1,1,0,0,0,1,0,1,1,
+ 0,0,1,1,0,1,1,1,0,1,1,0,0,0,1,0,1,1,
+ 0,0,1,1,0,1,1,1,0,1,1,0,0,0,1,0,1,1,
+ 0,1,1,1,0,1,1,1,0,1,1,0,0,0,1,0,1,1,
+ 0,0,1,1,0,1,1,1,0,1,1,0,0,0,1,0,1,1,
+ 0,0,1,1,0,1,1,1,0,1,1,0,0,0,1,0,1,1,
+ 0,0,1,1,0,1,1,1,0,1,1,0,0,0,1,0,1,1,
+ 0,0,1,1,0,1,1,1,0,1,1,0,0,0,1,0,1,1,
+ 0,0,1,1,0,1,1,1,0,1,1,0,0,0,1,0,1,1,
+ 0,0,1,1,0,1,1,1,0,1,1,0,0,0,1,0,1,1,
+ 0,0,1,1,0,1,1,1,0,1,1,0,0,0,1,0,1,1,
+ 0,0,1,1,0,1,1,1,0,1,1,0,0,0,1,0,1,1,
+ 0,0,1,1,0,1,1,1,0,1,1,0,0,0,1,0,1,1,
+ 0,0,1,1,0,1,1,1,0,1,1,0,0,0,1,0,1,1,
+ 0,0,1,1,0,1,1,1,0,1,1,0,0,0,1,0,1,1,
+ 0,1,1,1,0,1,1,1,0,1,1,0,0,0,1,0,1,1,
+ 0,1,1,1,0,1,1,1,0,1,1,0,0,0,1,0,1,1,
+ 0,1,1,1,0,1,1,1,0,1,1,0,0,0,1,0,1,1,
+};
+
+const uint8_t gsm0503_puncture_mcs7_p2[1404] = {
+ 1,1,0,0,1,0,0,1,1,1,0,1,1,1,0,1,1,0,
+ 1,1,0,0,1,0,0,1,1,1,0,1,1,1,0,1,1,0,
+ 1,1,0,0,1,0,0,1,1,1,0,1,1,1,0,1,1,0,
+ 1,1,0,0,1,0,0,1,1,1,0,1,1,1,0,1,0,0,
+ 1,1,0,0,1,0,0,1,1,1,0,1,1,1,0,1,0,0,
+ 1,1,0,0,1,0,0,1,1,1,0,1,1,1,0,1,0,0,
+ 1,1,0,0,1,0,0,1,1,1,0,1,1,1,0,1,0,0,
+ 1,1,0,0,1,0,0,1,1,1,0,1,1,1,0,1,0,0,
+ 1,1,0,0,1,0,0,1,1,1,0,1,1,1,0,1,0,0,
+ 1,1,0,0,1,0,0,1,1,1,0,1,1,1,0,1,0,0,
+ 1,1,0,0,1,0,0,1,1,1,0,1,1,1,0,1,1,0,
+ 1,1,0,0,1,0,0,1,1,1,0,1,1,1,0,1,0,0,
+ 1,1,0,0,1,0,0,1,1,1,0,1,1,1,0,1,0,0,
+ 1,1,0,0,1,0,0,1,1,1,0,1,1,1,0,1,0,0,
+ 1,1,0,0,1,0,0,1,1,1,0,1,1,1,0,1,0,0,
+ 1,1,0,0,1,0,0,1,1,1,0,1,1,1,0,1,0,0,
+ 1,1,0,0,1,0,0,1,1,1,0,1,1,1,0,1,0,0,
+ 1,1,0,0,1,0,0,1,1,1,0,1,1,1,0,1,0,0,
+ 1,1,0,0,1,0,0,1,1,1,0,1,1,1,0,1,0,0,
+ 1,1,0,0,1,0,0,1,1,1,0,1,1,1,0,1,0,0,
+ 1,1,0,0,1,0,0,1,1,1,0,1,1,1,0,1,1,0,
+ 1,1,0,0,1,0,0,1,1,1,0,1,1,1,0,1,0,0,
+ 1,1,0,0,1,0,0,1,1,1,0,1,1,1,0,1,0,0,
+ 1,1,0,0,1,0,0,1,1,1,0,1,1,1,0,1,0,0,
+ 1,1,0,0,1,0,0,1,1,1,0,1,1,1,0,1,0,0,
+ 1,1,0,0,1,0,0,1,1,1,0,1,1,1,0,1,0,0,
+ 1,1,0,0,1,0,0,1,1,1,0,1,1,1,0,1,0,0,
+ 1,1,0,0,1,0,0,1,1,1,0,1,1,1,0,1,0,0,
+ 1,1,0,0,1,0,0,1,1,1,0,1,1,1,0,1,0,0,
+ 1,1,0,0,1,0,0,1,1,1,0,1,1,1,0,1,0,0,
+ 1,1,0,0,1,0,0,1,1,1,0,1,1,1,0,1,1,0,
+ 1,1,0,0,1,0,0,1,1,1,0,1,1,1,0,1,0,0,
+ 1,1,0,0,1,0,0,1,1,1,0,1,1,1,0,1,0,0,
+ 1,1,0,0,1,0,0,1,1,1,0,1,1,1,0,1,0,0,
+ 1,1,0,0,1,0,0,1,1,1,0,1,1,1,0,1,0,0,
+ 1,1,0,0,1,0,0,1,1,1,0,1,1,1,0,1,0,0,
+ 1,1,0,0,1,0,0,1,1,1,0,1,1,1,0,1,0,0,
+ 1,1,0,0,1,0,0,1,1,1,0,1,1,1,0,1,0,0,
+ 1,1,0,0,1,0,0,1,1,1,0,1,1,1,0,1,0,0,
+ 1,1,0,0,1,0,0,1,1,1,0,1,1,1,0,1,0,0,
+ 1,1,0,0,1,0,0,1,1,1,0,1,1,1,0,1,1,0,
+ 1,1,0,0,1,0,0,1,1,1,0,1,1,1,0,1,0,0,
+ 1,1,0,0,1,0,0,1,1,1,0,1,1,1,0,1,0,0,
+ 1,1,0,0,1,0,0,1,1,1,0,1,1,1,0,1,0,0,
+ 1,1,0,0,1,0,0,1,1,1,0,1,1,1,0,1,0,0,
+ 1,1,0,0,1,0,0,1,1,1,0,1,1,1,0,1,0,0,
+ 1,1,0,0,1,0,0,1,1,1,0,1,1,1,0,1,0,0,
+ 1,1,0,0,1,0,0,1,1,1,0,1,1,1,0,1,0,0,
+ 1,1,0,0,1,0,0,1,1,1,0,1,1,1,0,1,0,0,
+ 1,1,0,0,1,0,0,1,1,1,0,1,1,1,0,1,0,0,
+ 1,1,0,0,1,0,0,1,1,1,0,1,1,1,0,1,1,0,
+ 1,1,0,0,1,0,0,1,1,1,0,1,1,1,0,1,0,0,
+ 1,1,0,0,1,0,0,1,1,1,0,1,1,1,0,1,0,0,
+ 1,1,0,0,1,0,0,1,1,1,0,1,1,1,0,1,0,0,
+ 1,1,0,0,1,0,0,1,1,1,0,1,1,1,0,1,0,0,
+ 1,1,0,0,1,0,0,1,1,1,0,1,1,1,0,1,0,0,
+ 1,1,0,0,1,0,0,1,1,1,0,1,1,1,0,1,0,0,
+ 1,1,0,0,1,0,0,1,1,1,0,1,1,1,0,1,0,0,
+ 1,1,0,0,1,0,0,1,1,1,0,1,1,1,0,1,0,0,
+ 1,1,0,0,1,0,0,1,1,1,0,1,1,1,0,1,0,0,
+ 1,1,0,0,1,0,0,1,1,1,0,1,1,1,0,1,1,0,
+ 1,1,0,0,1,0,0,1,1,1,0,1,1,1,0,1,0,0,
+ 1,1,0,0,1,0,0,1,1,1,0,1,1,1,0,1,0,0,
+ 1,1,0,0,1,0,0,1,1,1,0,1,1,1,0,1,0,0,
+ 1,1,0,0,1,0,0,1,1,1,0,1,1,1,0,1,0,0,
+ 1,1,0,0,1,0,0,1,1,1,0,1,1,1,0,1,0,0,
+ 1,1,0,0,1,0,0,1,1,1,0,1,1,1,0,1,0,0,
+ 1,1,0,0,1,0,0,1,1,1,0,1,1,1,0,1,0,0,
+ 1,1,0,0,1,0,0,1,1,1,0,1,1,1,0,1,0,0,
+ 1,1,0,0,1,0,0,1,1,1,0,1,1,1,0,1,0,0,
+ 1,1,0,0,1,0,0,1,1,1,0,1,1,1,0,1,0,0,
+ 1,1,0,0,1,0,0,1,1,1,0,1,1,1,0,1,0,0,
+ 1,1,0,0,1,0,0,1,1,1,0,1,1,1,0,1,0,0,
+ 1,1,0,0,1,0,0,1,1,1,0,1,1,1,0,1,0,0,
+ 1,1,0,0,1,0,0,1,1,1,0,1,1,1,0,1,0,0,
+ 1,1,0,0,1,0,0,1,1,1,0,1,1,1,0,1,1,0,
+ 1,1,0,0,1,0,0,1,1,1,0,1,1,1,0,1,1,0,
+ 1,1,0,0,1,0,0,1,1,1,0,1,1,1,0,1,1,0,
+};
+
+const uint8_t gsm0503_puncture_mcs7_p3[1404] = {
+ 1,1,0,1,1,0,0,0,1,0,1,1,0,1,1,1,0,1,
+ 1,1,0,1,1,0,0,0,1,0,1,1,0,1,1,1,0,1,
+ 1,1,0,1,1,0,0,0,1,0,1,1,0,1,1,1,0,1,
+ 1,1,0,1,1,0,0,0,1,0,1,1,0,0,1,1,0,1,
+ 1,1,0,1,1,0,0,0,1,0,1,1,0,0,1,1,0,1,
+ 1,1,0,1,1,0,0,0,1,0,1,1,0,0,1,1,0,1,
+ 1,1,0,1,1,0,0,0,1,0,1,1,0,0,1,1,0,1,
+ 1,1,0,1,1,0,0,0,1,0,1,1,0,0,1,1,0,1,
+ 1,1,0,1,1,0,0,0,1,0,1,1,0,0,1,1,0,1,
+ 1,1,0,1,1,0,0,0,1,0,1,1,0,0,1,1,0,1,
+ 1,1,0,1,1,0,0,0,1,0,1,1,0,0,1,1,0,1,
+ 1,1,0,1,1,0,0,0,1,0,1,1,0,0,1,1,0,1,
+ 1,1,0,1,1,0,0,0,1,0,1,1,0,0,1,1,0,1,
+ 1,1,0,1,1,0,0,0,1,0,1,1,0,0,1,1,0,1,
+ 1,1,0,1,1,0,0,0,1,0,1,1,0,0,1,1,0,1,
+ 1,1,0,1,1,0,0,0,1,0,1,1,0,0,1,1,0,1,
+ 1,1,0,1,1,0,0,0,1,0,1,1,0,1,1,1,0,1,
+ 1,1,0,1,1,0,0,0,1,0,1,1,0,0,1,1,0,1,
+ 1,1,0,1,1,0,0,0,1,0,1,1,0,0,1,1,0,1,
+ 1,1,0,1,1,0,0,0,1,0,1,1,0,0,1,1,0,1,
+ 1,1,0,1,1,0,0,0,1,0,1,1,0,0,1,1,0,1,
+ 1,1,0,1,1,0,0,0,1,0,1,1,0,0,1,1,0,1,
+ 1,1,0,1,1,0,0,0,1,0,1,1,0,0,1,1,0,1,
+ 1,1,0,1,1,0,0,0,1,0,1,1,0,0,1,1,0,1,
+ 1,1,0,1,1,0,0,0,1,0,1,1,0,0,1,1,0,1,
+ 1,1,0,1,1,0,0,0,1,0,1,1,0,0,1,1,0,1,
+ 1,1,0,1,1,0,0,0,1,0,1,1,0,1,1,1,0,1,
+ 1,1,0,1,1,0,0,0,1,0,1,1,0,0,1,1,0,1,
+ 1,1,0,1,1,0,0,0,1,0,1,1,0,0,1,1,0,1,
+ 1,1,0,1,1,0,0,0,1,0,1,1,0,0,1,1,0,1,
+ 1,1,0,1,1,0,0,0,1,0,1,1,0,0,1,1,0,1,
+ 1,1,0,1,1,0,0,0,1,0,1,1,0,0,1,1,0,1,
+ 1,1,0,1,1,0,0,0,1,0,1,1,0,0,1,1,0,1,
+ 1,1,0,1,1,0,0,0,1,0,1,1,0,0,1,1,0,1,
+ 1,1,0,1,1,0,0,0,1,0,1,1,0,0,1,1,0,1,
+ 1,1,0,1,1,0,0,0,1,0,1,1,0,0,1,1,0,1,
+ 1,1,0,1,1,0,0,0,1,0,1,1,0,1,1,1,0,1,
+ 1,1,0,1,1,0,0,0,1,0,1,1,0,0,1,1,0,1,
+ 1,1,0,1,1,0,0,0,1,0,1,1,0,0,1,1,0,1,
+ 1,1,0,1,1,0,0,0,1,0,1,1,0,0,1,1,0,1,
+ 1,1,0,1,1,0,0,0,1,0,1,1,0,0,1,1,0,1,
+ 1,1,0,1,1,0,0,0,1,0,1,1,0,0,1,1,0,1,
+ 1,1,0,1,1,0,0,0,1,0,1,1,0,0,1,1,0,1,
+ 1,1,0,1,1,0,0,0,1,0,1,1,0,0,1,1,0,1,
+ 1,1,0,1,1,0,0,0,1,0,1,1,0,0,1,1,0,1,
+ 1,1,0,1,1,0,0,0,1,0,1,1,0,0,1,1,0,1,
+ 1,1,0,1,1,0,0,0,1,0,1,1,0,1,1,1,0,1,
+ 1,1,0,1,1,0,0,0,1,0,1,1,0,0,1,1,0,1,
+ 1,1,0,1,1,0,0,0,1,0,1,1,0,0,1,1,0,1,
+ 1,1,0,1,1,0,0,0,1,0,1,1,0,0,1,1,0,1,
+ 1,1,0,1,1,0,0,0,1,0,1,1,0,0,1,1,0,1,
+ 1,1,0,1,1,0,0,0,1,0,1,1,0,0,1,1,0,1,
+ 1,1,0,1,1,0,0,0,1,0,1,1,0,0,1,1,0,1,
+ 1,1,0,1,1,0,0,0,1,0,1,1,0,0,1,1,0,1,
+ 1,1,0,1,1,0,0,0,1,0,1,1,0,0,1,1,0,1,
+ 1,1,0,1,1,0,0,0,1,0,1,1,0,0,1,1,0,1,
+ 1,1,0,1,1,0,0,0,1,0,1,1,0,1,1,1,0,1,
+ 1,1,0,1,1,0,0,0,1,0,1,1,0,0,1,1,0,1,
+ 1,1,0,1,1,0,0,0,1,0,1,1,0,0,1,1,0,1,
+ 1,1,0,1,1,0,0,0,1,0,1,1,0,0,1,1,0,1,
+ 1,1,0,1,1,0,0,0,1,0,1,1,0,0,1,1,0,1,
+ 1,1,0,1,1,0,0,0,1,0,1,1,0,0,1,1,0,1,
+ 1,1,0,1,1,0,0,0,1,0,1,1,0,0,1,1,0,1,
+ 1,1,0,1,1,0,0,0,1,0,1,1,0,0,1,1,0,1,
+ 1,1,0,1,1,0,0,0,1,0,1,1,0,0,1,1,0,1,
+ 1,1,0,1,1,0,0,0,1,0,1,1,0,0,1,1,0,1,
+ 1,1,0,1,1,0,0,0,1,0,1,1,0,1,1,1,0,1,
+ 1,1,0,1,1,0,0,0,1,0,1,1,0,0,1,1,0,1,
+ 1,1,0,1,1,0,0,0,1,0,1,1,0,0,1,1,0,1,
+ 1,1,0,1,1,0,0,0,1,0,1,1,0,0,1,1,0,1,
+ 1,1,0,1,1,0,0,0,1,0,1,1,0,0,1,1,0,1,
+ 1,1,0,1,1,0,0,0,1,0,1,1,0,0,1,1,0,1,
+ 1,1,0,1,1,0,0,0,1,0,1,1,0,0,1,1,0,1,
+ 1,1,0,1,1,0,0,0,1,0,1,1,0,0,1,1,0,1,
+ 1,1,0,1,1,0,0,0,1,0,1,1,0,0,1,1,0,1,
+ 1,1,0,1,1,0,0,0,1,0,1,1,0,1,1,1,0,1,
+ 1,1,0,1,1,0,0,0,1,0,1,1,0,1,1,1,0,1,
+ 1,1,0,1,1,0,0,0,1,0,1,1,0,1,1,1,0,1,
+};
+
+const uint8_t gsm0503_puncture_mcs8_p1[1692] = {
+ 0,1,0,1,1,0,0,1,1,1,0,1,1,0,1,1,0,1,1,1,0,1,1,0,0,1,1,0,1,1,1,0,1,1,1,0,
+ 0,1,0,1,1,0,0,1,1,1,0,1,1,0,1,1,0,1,1,1,0,1,1,0,0,1,1,0,1,1,1,0,1,1,1,0,
+ 0,1,0,1,1,0,0,1,1,1,0,1,1,0,1,1,0,1,1,1,0,1,1,0,0,1,1,0,1,1,1,0,1,1,1,0,
+ 0,1,0,1,1,0,0,1,1,1,0,1,1,0,1,1,0,1,1,1,0,1,1,0,0,1,1,0,1,1,1,0,1,1,1,0,
+ 0,1,0,1,1,0,0,1,1,1,0,1,1,0,1,1,0,1,1,1,0,1,1,0,0,1,1,0,1,1,1,0,1,1,1,0,
+ 0,1,0,1,1,0,0,1,1,1,0,1,1,0,1,1,0,1,1,1,0,1,1,0,0,1,1,0,1,1,1,0,1,1,1,0,
+ 0,1,0,1,1,0,0,1,1,1,0,1,1,0,1,1,0,1,1,1,0,1,1,0,0,1,1,0,1,1,1,0,1,1,1,0,
+ 0,1,0,1,1,0,0,1,1,1,0,1,1,0,1,1,0,1,1,1,0,1,1,0,0,1,1,0,1,1,1,0,1,1,1,0,
+ 0,1,0,1,1,0,0,1,1,1,0,1,1,0,1,1,0,1,1,1,0,1,1,0,0,1,1,0,1,1,1,0,1,1,1,0,
+ 0,1,0,1,1,0,0,1,1,1,0,1,1,0,1,1,0,1,1,1,0,1,1,0,0,1,1,0,1,1,1,0,1,1,1,0,
+ 0,1,0,1,1,0,0,1,1,1,0,1,1,0,1,1,0,1,1,1,0,1,1,0,0,1,1,0,1,1,1,0,1,1,1,0,
+ 0,1,0,1,1,0,0,1,1,1,0,1,1,0,1,1,0,1,1,1,0,1,1,0,0,1,1,0,1,1,1,0,1,1,1,0,
+ 0,1,0,1,1,0,0,1,1,1,0,1,1,0,1,1,0,1,1,1,0,1,1,0,0,1,1,0,1,1,1,0,1,1,1,0,
+ 0,1,0,1,1,0,0,1,1,1,0,1,1,0,1,1,0,1,1,1,0,1,1,0,0,1,1,0,1,1,1,0,1,1,1,0,
+ 0,1,0,1,1,0,0,1,1,1,0,1,1,0,1,1,0,1,1,1,0,1,1,0,0,1,1,0,1,1,1,0,1,1,1,0,
+ 0,1,0,1,1,0,0,1,1,1,0,1,1,0,1,1,0,1,1,1,0,1,1,0,0,1,1,0,1,1,1,0,1,1,1,0,
+ 0,1,0,1,1,0,0,1,1,1,0,1,1,0,1,1,0,1,1,1,0,1,1,0,0,1,1,0,1,1,1,0,1,1,1,0,
+ 0,1,0,1,1,0,0,1,1,1,0,1,1,0,1,1,0,1,1,1,0,1,1,0,0,1,1,0,1,1,1,0,1,1,1,0,
+ 0,1,0,1,1,0,0,1,1,1,0,1,1,0,1,1,0,1,1,1,0,1,1,0,0,1,1,0,1,1,1,0,1,1,1,0,
+ 0,1,0,1,1,0,0,1,1,1,0,1,1,0,1,1,0,1,1,1,0,1,1,0,0,1,1,0,1,1,1,0,1,1,1,0,
+ 0,1,0,1,1,0,0,1,1,1,0,1,1,0,1,1,0,1,1,1,0,1,1,0,0,1,1,0,1,1,1,0,1,1,1,0,
+ 0,1,0,1,1,0,0,1,1,1,0,1,1,0,1,1,0,1,1,1,0,1,1,0,0,1,1,0,1,1,1,0,1,1,1,0,
+ 0,1,0,1,1,0,0,1,1,1,0,1,1,0,1,1,0,1,1,1,0,1,1,0,0,1,1,0,1,1,1,0,1,1,1,0,
+ 0,1,0,1,1,0,0,1,1,1,0,1,1,0,1,1,0,0,1,1,0,1,1,0,0,1,1,0,1,1,1,0,1,1,1,0,
+ 0,1,0,1,1,0,0,1,1,1,0,1,1,0,1,1,0,1,1,1,0,1,1,0,0,1,1,0,1,1,1,0,1,1,1,0,
+ 0,1,0,1,1,0,0,1,1,1,0,1,1,0,1,1,0,1,1,1,0,1,1,0,0,1,1,0,1,1,1,0,1,1,1,0,
+ 0,1,0,1,1,0,0,1,1,1,0,1,1,0,1,1,0,1,1,1,0,1,1,0,0,1,1,0,1,1,1,0,1,1,1,0,
+ 0,1,0,1,1,0,0,1,1,1,0,1,1,0,1,1,0,1,1,1,0,1,1,0,0,1,1,0,1,1,1,0,1,1,1,0,
+ 0,1,0,1,1,0,0,1,1,1,0,1,1,0,1,1,0,1,1,1,0,1,1,0,0,1,1,0,1,1,1,0,1,1,1,0,
+ 0,1,0,1,1,0,0,1,1,1,0,1,1,0,1,1,0,1,1,1,0,1,1,0,0,1,1,0,1,1,1,0,1,1,1,0,
+ 0,1,0,1,1,0,0,1,1,1,0,1,1,0,1,1,0,1,1,1,0,1,1,0,0,1,1,0,1,1,1,0,1,1,1,0,
+ 0,1,0,1,1,0,0,1,1,1,0,1,1,0,1,1,0,1,1,1,0,1,1,0,0,1,1,0,1,1,1,0,1,1,1,0,
+ 0,1,0,1,1,0,0,1,1,1,0,1,1,0,1,1,0,1,1,1,0,1,1,0,0,1,1,0,1,1,1,0,1,1,1,0,
+ 0,1,0,1,1,0,0,1,1,1,0,1,1,0,1,1,0,1,1,1,0,1,1,0,0,1,1,0,1,1,1,0,1,1,1,0,
+ 0,1,0,1,1,0,0,1,1,1,0,1,1,0,1,1,0,1,1,1,0,1,1,0,0,1,1,0,1,1,1,0,1,1,1,0,
+ 0,1,0,1,1,0,0,1,1,1,0,1,1,0,1,1,0,1,1,1,0,1,1,0,0,1,1,0,1,1,1,0,1,1,1,0,
+ 0,1,0,1,1,0,0,1,1,1,0,1,1,0,1,1,0,1,1,1,0,1,1,0,0,1,1,0,1,1,1,0,1,1,1,0,
+ 0,1,0,1,1,0,0,1,1,1,0,1,1,0,1,1,0,1,1,1,0,1,1,0,0,1,1,0,1,1,1,0,1,1,1,0,
+ 0,1,0,1,1,0,0,1,1,1,0,1,1,0,1,1,0,1,1,1,0,1,1,0,0,1,1,0,1,1,1,0,1,1,1,0,
+ 0,1,0,1,1,0,0,1,1,1,0,1,1,0,1,1,0,1,1,1,0,1,1,0,0,1,1,0,1,1,1,0,1,1,1,0,
+ 0,1,0,1,1,0,0,1,1,1,0,1,1,0,1,1,0,1,1,1,0,1,1,0,0,1,1,0,1,1,1,0,1,1,1,0,
+ 0,1,0,1,1,0,0,1,1,1,0,1,1,0,1,1,0,1,1,1,0,1,1,0,0,1,1,0,1,1,1,0,1,1,1,0,
+ 0,1,0,1,1,0,0,1,1,1,0,1,1,0,1,1,0,1,1,1,0,1,1,0,0,1,1,0,1,1,1,0,1,1,1,0,
+ 0,1,0,1,1,0,0,1,1,1,0,1,1,0,1,1,0,1,1,1,0,1,1,0,0,1,1,0,1,1,1,0,1,1,1,0,
+ 0,1,0,1,1,0,0,1,1,1,0,1,1,0,1,1,0,1,1,1,0,1,1,0,0,1,1,0,1,1,1,0,1,1,1,0,
+ 0,1,0,1,1,0,0,1,1,1,0,1,1,0,1,1,0,1,1,1,0,1,1,0,0,1,1,0,1,1,1,0,1,1,1,0,
+ 0,1,0,1,1,0,0,1,1,1,0,1,1,0,1,1,0,1,1,1,0,1,1,0,0,1,1,0,1,1,1,0,1,1,1,0,
+};
+
+const uint8_t gsm0503_puncture_mcs8_p2[1692] = {
+ 1,0,1,1,0,1,1,1,0,1,1,0,0,1,1,0,1,0,1,0,1,1,0,1,1,0,1,1,0,1,0,1,1,0,1,1,
+ 1,0,1,1,0,1,1,1,0,1,1,0,0,1,1,0,1,0,1,0,1,1,0,1,1,0,1,1,0,1,0,1,1,0,1,1,
+ 1,0,1,1,0,1,1,1,0,1,1,0,0,1,1,0,1,0,1,0,1,1,0,1,1,0,1,1,0,1,0,1,1,0,1,1,
+ 1,0,1,1,0,1,1,1,0,1,1,0,0,1,1,0,1,0,1,0,1,1,0,1,1,0,1,1,0,1,0,1,1,0,1,1,
+ 1,0,1,1,0,1,1,1,0,1,1,0,0,1,1,0,1,0,1,0,1,1,0,1,1,0,1,1,0,1,0,1,1,0,1,1,
+ 1,0,1,1,0,1,1,1,0,1,1,0,0,1,1,0,1,0,1,0,1,1,0,1,1,0,1,1,0,1,0,1,1,0,1,1,
+ 1,0,1,1,0,1,1,1,0,1,1,0,0,1,1,0,1,0,1,0,1,1,0,1,1,0,1,1,0,1,0,1,1,0,1,1,
+ 1,0,1,1,0,1,1,1,0,1,1,0,0,1,1,0,1,0,1,0,1,1,0,1,1,0,1,1,0,1,0,1,1,0,1,1,
+ 1,0,1,1,0,1,1,1,0,1,1,0,0,1,1,0,1,0,1,0,1,1,0,1,1,0,1,1,0,1,0,1,1,0,1,1,
+ 1,0,1,1,0,1,1,1,0,1,1,0,0,1,1,0,1,0,1,0,1,1,0,1,1,0,1,1,0,1,0,1,1,0,1,1,
+ 1,0,1,1,0,1,1,1,0,1,1,0,0,1,1,0,1,0,1,0,1,1,0,1,1,0,1,1,0,1,0,1,1,0,1,1,
+ 1,0,1,1,0,1,1,1,0,1,1,0,0,1,1,0,1,0,1,0,1,1,0,1,1,0,1,1,0,1,0,1,1,0,1,1,
+ 1,0,1,1,0,1,1,1,0,1,1,0,0,1,1,0,1,0,1,0,1,1,0,1,1,0,1,1,0,1,0,1,1,0,1,1,
+ 1,0,1,1,0,1,1,1,0,1,1,0,0,1,1,0,1,0,1,0,1,1,0,1,1,0,1,1,0,1,0,1,1,0,1,1,
+ 1,0,1,1,0,1,1,1,0,1,1,0,0,1,1,0,1,0,1,0,1,1,0,1,1,0,1,1,0,1,0,1,1,0,1,1,
+ 1,0,1,1,0,1,1,1,0,1,1,0,0,1,1,0,1,0,1,0,1,1,0,1,1,0,1,1,0,1,0,1,1,0,1,1,
+ 1,0,1,1,0,1,0,1,0,1,1,0,0,1,1,0,1,0,1,0,1,1,0,1,1,0,1,1,0,1,0,1,1,0,1,1,
+ 1,0,1,1,0,1,1,1,0,1,1,0,0,1,1,0,1,0,1,0,1,1,0,1,1,0,1,1,0,1,0,1,1,0,1,1,
+ 1,0,1,1,0,1,1,1,0,1,1,0,0,1,1,0,1,0,1,0,1,1,0,1,1,0,1,1,0,1,0,1,1,0,1,1,
+ 1,0,1,1,0,1,1,1,0,1,1,0,0,1,1,0,1,0,1,0,1,1,0,1,1,0,1,1,0,1,0,1,1,0,1,1,
+ 1,0,1,1,0,1,1,1,0,1,1,0,0,1,1,0,1,0,1,0,1,1,0,1,1,0,1,1,0,1,0,1,1,0,1,1,
+ 1,0,1,1,0,1,1,1,0,1,1,0,0,1,1,0,1,0,1,0,1,1,0,1,1,0,1,1,0,1,0,1,1,0,1,1,
+ 1,0,1,1,0,1,1,1,0,1,1,0,0,1,1,0,1,0,1,0,1,1,0,1,1,0,1,1,0,1,0,1,1,0,1,1,
+ 1,0,1,1,0,1,1,1,0,1,1,0,0,1,1,0,1,0,1,0,1,1,0,1,1,0,1,1,0,1,0,1,1,0,1,1,
+ 1,0,1,1,0,1,1,1,0,1,1,0,0,1,1,0,1,0,1,0,1,1,0,1,1,0,1,1,0,1,0,1,1,0,1,1,
+ 1,0,1,1,0,1,1,1,0,1,1,0,0,1,1,0,1,0,1,0,1,1,0,1,1,0,1,1,0,1,0,1,1,0,1,1,
+ 1,0,1,1,0,1,1,1,0,1,1,0,0,1,1,0,1,0,1,0,1,1,0,1,1,0,1,1,0,1,0,1,1,0,1,1,
+ 1,0,1,1,0,1,1,1,0,1,1,0,0,1,1,0,1,0,1,0,1,1,0,1,1,0,1,1,0,1,0,1,1,0,1,1,
+ 1,0,1,1,0,1,1,1,0,1,1,0,0,1,1,0,1,0,1,0,1,1,0,1,1,0,1,1,0,1,0,1,1,0,1,1,
+ 1,0,1,1,0,1,1,1,0,1,1,0,0,1,1,0,1,0,1,0,1,1,0,1,1,0,1,1,0,1,0,1,1,0,1,1,
+ 1,0,1,1,0,1,1,1,0,1,1,0,0,1,1,0,1,0,1,0,1,1,0,1,1,0,1,1,0,1,0,1,1,0,1,1,
+ 1,0,1,1,0,1,1,1,0,1,1,0,0,1,1,0,1,0,1,0,1,1,0,1,1,0,1,1,0,1,0,1,1,0,1,1,
+ 1,0,1,1,0,1,1,1,0,1,1,0,0,1,1,0,1,0,1,0,1,1,0,1,1,0,1,1,0,1,0,1,1,0,1,1,
+ 1,0,1,1,0,1,1,1,0,1,1,0,0,1,1,0,1,0,1,0,1,1,0,1,1,0,1,1,0,1,0,1,1,0,1,1,
+ 1,0,1,1,0,1,1,1,0,1,1,0,0,1,1,0,1,0,1,0,1,1,0,1,1,0,1,1,0,1,0,1,1,0,1,1,
+ 1,0,1,1,0,1,1,1,0,1,1,0,0,1,1,0,1,0,1,0,1,1,0,1,1,0,1,1,0,1,0,1,1,0,1,1,
+ 1,0,1,1,0,1,1,1,0,1,1,0,0,1,1,0,1,0,1,0,1,1,0,1,1,0,1,1,0,1,0,1,1,0,1,1,
+ 1,0,1,1,0,1,1,1,0,1,1,0,0,1,1,0,1,0,1,0,1,1,0,1,1,0,1,1,0,1,0,1,1,0,1,1,
+ 1,0,1,1,0,1,1,1,0,1,1,0,0,1,1,0,1,0,1,0,1,1,0,1,1,0,1,1,0,1,0,1,1,0,1,1,
+ 1,0,1,1,0,1,1,1,0,1,1,0,0,1,1,0,1,0,1,0,1,1,0,1,1,0,1,1,0,1,0,1,1,0,1,1,
+ 1,0,1,1,0,1,1,1,0,1,1,0,0,1,1,0,1,0,1,0,1,1,0,1,1,0,1,1,0,1,0,1,1,0,1,1,
+ 1,0,1,1,0,1,1,1,0,1,1,0,0,1,1,0,1,0,1,0,1,1,0,1,1,0,1,1,0,1,0,1,1,0,1,1,
+ 1,0,1,1,0,1,1,1,0,1,1,0,0,1,1,0,1,0,1,0,1,1,0,1,1,0,1,1,0,1,0,1,1,0,1,1,
+ 1,0,1,1,0,1,1,1,0,1,1,0,0,1,1,0,1,0,1,0,1,1,0,1,1,0,1,1,0,1,0,1,1,0,1,1,
+ 1,0,1,1,0,1,1,1,0,1,1,0,0,1,1,0,1,0,1,0,1,1,0,1,1,0,1,1,0,1,0,1,1,0,1,1,
+ 1,0,1,1,0,1,1,1,0,1,1,0,0,1,1,0,1,0,1,0,1,1,0,1,1,0,1,1,0,1,0,1,1,0,1,1,
+ 1,0,1,1,0,1,1,1,0,1,1,0,0,1,1,0,1,0,1,0,1,1,0,1,1,0,1,1,0,1,0,1,1,0,1,1,
+};
+
+const uint8_t gsm0503_puncture_mcs8_p3[1692] = {
+ 1,1,0,0,1,1,1,0,1,0,1,1,1,1,0,1,1,0,0,1,1,0,1,1,1,1,0,0,1,0,1,1,0,1,0,1,
+ 1,1,0,0,1,1,1,0,1,0,1,1,1,1,0,1,1,0,0,1,1,0,1,1,1,1,0,0,1,0,1,1,0,1,0,1,
+ 1,1,0,0,1,1,1,0,1,0,1,1,1,1,0,1,1,0,0,1,1,0,1,1,1,1,0,0,1,0,1,1,0,1,0,1,
+ 1,1,0,0,1,1,1,0,1,0,1,1,1,1,0,1,1,0,0,1,1,0,1,1,1,1,0,0,1,0,1,1,0,1,0,1,
+ 1,1,0,0,1,1,1,0,1,0,1,1,1,1,0,1,1,0,0,1,1,0,1,1,1,1,0,0,1,0,1,1,0,1,0,1,
+ 1,1,0,0,1,1,1,0,1,0,1,1,1,1,0,1,1,0,0,1,1,0,1,1,1,1,0,0,1,0,1,1,0,1,0,1,
+ 1,1,0,0,1,1,1,0,1,0,1,1,1,1,0,1,1,0,0,1,1,0,1,1,1,1,0,0,1,0,1,1,0,1,0,1,
+ 1,1,0,0,1,1,1,0,1,0,1,1,1,1,0,1,1,0,0,1,1,0,1,1,1,1,0,0,1,0,1,1,0,1,0,1,
+ 1,1,0,0,1,1,1,0,1,0,1,1,1,1,0,1,1,0,0,1,1,0,1,1,1,1,0,0,1,0,1,1,0,1,0,1,
+ 1,1,0,0,1,1,1,0,1,0,1,1,1,1,0,1,1,0,0,1,1,0,1,1,1,1,0,0,1,0,1,1,0,1,0,1,
+ 1,1,0,0,1,1,1,0,1,0,1,1,1,1,0,1,1,0,0,1,1,0,1,1,1,1,0,0,1,0,1,1,0,1,0,1,
+ 1,1,0,0,1,1,1,0,1,0,1,1,1,1,0,1,1,0,0,1,1,0,1,1,1,1,0,0,1,0,1,1,0,1,0,1,
+ 1,1,0,0,1,1,1,0,1,0,1,1,1,1,0,1,1,0,0,1,1,0,1,1,1,1,0,0,1,0,1,1,0,1,0,1,
+ 1,1,0,0,1,1,1,0,1,0,1,1,1,1,0,1,1,0,0,1,1,0,1,1,1,1,0,0,1,0,1,1,0,1,0,1,
+ 1,1,0,0,1,1,1,0,1,0,1,1,1,1,0,1,1,0,0,1,1,0,1,1,1,1,0,0,1,0,1,1,0,1,0,1,
+ 1,1,0,0,1,1,1,0,1,0,1,1,1,1,0,1,1,0,0,1,1,0,1,1,1,1,0,0,1,0,1,1,0,1,0,1,
+ 1,1,0,0,1,1,1,0,1,0,1,1,1,1,0,1,1,0,0,1,1,0,1,1,1,1,0,0,1,0,1,1,0,1,0,1,
+ 1,1,0,0,1,1,1,0,1,0,1,1,1,1,0,1,1,0,0,1,1,0,1,1,1,1,0,0,1,0,1,1,0,1,0,1,
+ 1,1,0,0,1,1,1,0,1,0,1,1,1,1,0,1,1,0,0,1,1,0,1,1,1,1,0,0,1,0,1,1,0,1,0,1,
+ 1,1,0,0,1,1,1,0,1,0,1,1,1,1,0,1,1,0,0,1,1,0,1,1,1,1,0,0,1,0,1,1,0,1,0,1,
+ 1,1,0,0,1,1,1,0,1,0,1,1,1,1,0,1,1,0,0,1,1,0,1,1,1,1,0,0,1,0,1,1,0,1,0,1,
+ 1,1,0,0,1,1,1,0,1,0,1,1,1,1,0,1,1,0,0,1,1,0,1,1,1,1,0,0,1,0,1,1,0,1,0,1,
+ 1,1,0,0,1,1,1,0,1,0,1,1,1,1,0,1,1,0,0,1,1,0,1,1,1,1,0,0,1,0,1,1,0,1,0,1,
+ 1,1,0,0,1,1,1,0,1,0,1,1,1,1,0,1,1,0,0,1,1,0,1,1,1,1,0,0,1,0,1,1,0,1,0,1,
+ 1,1,0,0,1,1,1,0,1,0,1,1,1,1,0,1,1,0,0,1,1,0,1,1,1,1,0,0,1,0,1,1,0,1,0,1,
+ 1,1,0,0,1,1,1,0,1,0,1,1,1,1,0,1,1,0,0,1,1,0,1,1,1,1,0,0,1,0,1,1,0,1,0,1,
+ 1,1,0,0,1,1,1,0,1,0,1,1,1,1,0,1,1,0,0,1,1,0,1,1,1,1,0,0,1,0,1,1,0,1,0,1,
+ 1,1,0,0,1,1,1,0,1,0,1,1,1,1,0,1,1,0,0,1,1,0,1,1,1,1,0,0,1,0,1,1,0,1,0,1,
+ 1,1,0,0,1,1,1,0,1,0,1,1,1,1,0,1,1,0,0,1,1,0,1,1,1,1,0,0,1,0,1,1,0,1,0,1,
+ 1,1,0,0,1,1,1,0,1,0,1,1,1,1,0,1,1,0,0,1,1,0,1,1,1,1,0,0,1,0,1,1,0,1,0,1,
+ 1,1,0,0,1,1,1,0,1,0,1,1,1,1,0,1,1,0,0,1,1,0,1,1,1,1,0,0,1,0,1,1,0,1,0,1,
+ 1,1,0,0,1,1,1,0,1,0,1,1,1,1,0,1,1,0,0,1,1,0,1,1,1,1,0,0,1,0,1,1,0,1,0,1,
+ 1,1,0,0,0,1,1,0,1,0,1,1,1,1,0,1,1,0,0,1,1,0,1,1,1,1,0,0,1,0,1,1,0,1,0,1,
+ 1,1,0,0,1,1,1,0,1,0,1,1,1,1,0,1,1,0,0,1,1,0,1,1,1,1,0,0,1,0,1,1,0,1,0,1,
+ 1,1,0,0,1,1,1,0,1,0,1,1,1,1,0,1,1,0,0,1,1,0,1,1,1,1,0,0,1,0,1,1,0,1,0,1,
+ 1,1,0,0,1,1,1,0,1,0,1,1,1,1,0,1,1,0,0,1,1,0,1,1,1,1,0,0,1,0,1,1,0,1,0,1,
+ 1,1,0,0,1,1,1,0,1,0,1,1,1,1,0,1,1,0,0,1,1,0,1,1,1,1,0,0,1,0,1,1,0,1,0,1,
+ 1,1,0,0,1,1,1,0,1,0,1,1,1,1,0,1,1,0,0,1,1,0,1,1,1,1,0,0,1,0,1,1,0,1,0,1,
+ 1,1,0,0,1,1,1,0,1,0,1,1,1,1,0,1,1,0,0,1,1,0,1,1,1,1,0,0,1,0,1,1,0,1,0,1,
+ 1,1,0,0,1,1,1,0,1,0,1,1,1,1,0,1,1,0,0,1,1,0,1,1,1,1,0,0,1,0,1,1,0,1,0,1,
+ 1,1,0,0,1,1,1,0,1,0,1,1,1,1,0,1,1,0,0,1,1,0,1,1,1,1,0,0,1,0,1,1,0,1,0,1,
+ 1,1,0,0,1,1,1,0,1,0,1,1,1,1,0,1,1,0,0,1,1,0,1,1,1,1,0,0,1,0,1,1,0,1,0,1,
+ 1,1,0,0,1,1,1,0,1,0,1,1,1,1,0,1,1,0,0,1,1,0,1,1,1,1,0,0,1,0,1,1,0,1,0,1,
+ 1,1,0,0,1,1,1,0,1,0,1,1,1,1,0,1,1,0,0,1,1,0,1,1,1,1,0,0,1,0,1,1,0,1,0,1,
+ 1,1,0,0,1,1,1,0,1,0,1,1,1,1,0,1,1,0,0,1,1,0,1,1,1,1,0,0,1,0,1,1,0,1,0,1,
+ 1,1,0,0,1,1,1,0,1,0,1,1,1,1,0,1,1,0,0,1,1,0,1,1,1,1,0,0,1,0,1,1,0,1,0,1,
+ 1,1,0,0,1,1,1,0,1,0,1,1,1,1,0,1,1,0,0,1,1,0,1,1,1,1,0,0,1,0,1,1,0,1,0,1,
+};
+
+const uint8_t gsm0503_puncture_mcs9_p1[1836] = {
+ 0,1,1, 0,1,1, 0,1,1, 0,1,1, 0,1,1, 0,1,1,
+ 0,1,1, 0,1,1, 0,1,1, 0,1,1, 0,1,1, 0,1,1,
+ 0,1,1, 0,1,1, 0,1,1, 0,1,1, 0,1,1, 0,1,1,
+ 0,1,1, 0,1,1, 0,1,1, 0,1,1, 0,1,1, 0,1,1,
+ 0,1,1, 0,1,1, 0,1,1, 0,1,1, 0,1,1, 0,1,1,
+ 0,1,1, 0,1,1, 0,1,1, 0,1,1, 0,1,1, 0,1,1,
+ 0,1,1, 0,1,1, 0,1,1, 0,1,1, 0,1,1, 0,1,1,
+ 0,1,1, 0,1,1, 0,1,1, 0,1,1, 0,1,1, 0,1,1,
+ 0,1,1, 0,1,1, 0,1,1, 0,1,1, 0,1,1, 0,1,1,
+ 0,1,1, 0,1,1, 0,1,1, 0,1,1, 0,1,1, 0,1,1,
+ 0,1,1, 0,1,1, 0,1,1, 0,1,1, 0,1,1, 0,1,1,
+ 0,1,1, 0,1,1, 0,1,1, 0,1,1, 0,1,1, 0,1,1,
+ 0,1,1, 0,1,1, 0,1,1, 0,1,1, 0,1,1, 0,1,1,
+ 0,1,1, 0,1,1, 0,1,1, 0,1,1, 0,1,1, 0,1,1,
+ 0,1,1, 0,1,1, 0,1,1, 0,1,1, 0,1,1, 0,1,1,
+ 0,1,1, 0,1,1, 0,1,1, 0,1,1, 0,1,1, 0,1,1,
+ 0,1,1, 0,1,1, 0,1,1, 0,1,1, 0,1,1, 0,1,1,
+ 0,1,1, 0,1,1, 0,1,1, 0,1,1, 0,1,1, 0,1,1,
+ 0,1,1, 0,1,1, 0,1,1, 0,1,1, 0,1,1, 0,1,1,
+ 0,1,1, 0,1,1, 0,1,1, 0,1,1, 0,1,1, 0,1,1,
+ 0,1,1, 0,1,1, 0,1,1, 0,1,1, 0,1,1, 0,1,1,
+ 0,1,1, 0,1,1, 0,1,1, 0,1,1, 0,1,1, 0,1,1,
+ 0,1,1, 0,1,1, 0,1,1, 0,1,1, 0,1,1, 0,1,1,
+ 0,1,1, 0,1,1, 0,1,1, 0,1,1, 0,1,1, 0,1,1,
+ 0,1,1, 0,1,1, 0,1,1, 0,1,1, 0,1,1, 0,1,1,
+ 0,1,1, 0,1,1, 0,1,1, 0,1,1, 0,1,1, 0,1,1,
+ 0,1,1, 0,1,1, 0,1,1, 0,1,1, 0,1,1, 0,1,1,
+ 0,1,1, 0,1,1, 0,1,1, 0,1,1, 0,1,1, 0,1,1,
+ 0,1,1, 0,1,1, 0,1,1, 0,1,1, 0,1,1, 0,1,1,
+ 0,1,1, 0,1,1, 0,1,1, 0,1,1, 0,1,1, 0,1,1,
+ 0,1,1, 0,1,1, 0,1,1, 0,1,1, 0,1,1, 0,1,1,
+ 0,1,1, 0,1,1, 0,1,1, 0,1,1, 0,1,1, 0,1,1,
+ 0,1,1, 0,1,1, 0,1,1, 0,1,1, 0,1,1, 0,1,1,
+ 0,1,1, 0,1,1, 0,1,1, 0,1,1, 0,1,1, 0,1,1,
+ 0,1,1, 0,1,1, 0,1,1, 0,1,1, 0,1,1, 0,1,1,
+ 0,1,1, 0,1,1, 0,1,1, 0,1,1, 0,1,1, 0,1,1,
+ 0,1,1, 0,1,1, 0,1,1, 0,1,1, 0,1,1, 0,1,1,
+ 0,1,1, 0,1,1, 0,1,1, 0,1,1, 0,1,1, 0,1,1,
+ 0,1,1, 0,1,1, 0,1,1, 0,1,1, 0,1,1, 0,1,1,
+ 0,1,1, 0,1,1, 0,1,1, 0,1,1, 0,1,1, 0,1,1,
+ 0,1,1, 0,1,1, 0,1,1, 0,1,1, 0,1,1, 0,1,1,
+ 0,1,1, 0,1,1, 0,1,1, 0,1,1, 0,1,1, 0,1,1,
+ 0,1,1, 0,1,1, 0,1,1, 0,1,1, 0,1,1, 0,1,1,
+ 0,1,1, 0,1,1, 0,1,1, 0,1,1, 0,1,1, 0,1,1,
+ 0,1,1, 0,1,1, 0,1,1, 0,1,1, 0,1,1, 0,1,1,
+ 0,1,1, 0,1,1, 0,1,1, 0,1,1, 0,1,1, 0,1,1,
+ 0,1,1, 0,1,1, 0,1,1, 0,1,1, 0,1,1, 0,1,1,
+ 0,1,1, 0,1,1, 0,1,1, 0,1,1, 0,1,1, 0,1,1,
+ 0,1,1, 0,1,1, 0,1,1, 0,1,1, 0,1,1, 0,1,1,
+ 0,1,1, 0,1,1, 0,1,1, 0,1,1, 0,1,1, 0,1,1,
+ 0,1,1, 0,1,1, 0,1,1, 0,1,1, 0,1,1, 0,1,1,
+ 0,1,1, 0,1,1, 0,1,1, 0,1,1, 0,1,1, 0,1,1,
+ 0,1,1, 0,1,1, 0,1,1, 0,1,1, 0,1,1, 0,1,1,
+ 0,1,1, 0,1,1, 0,1,1, 0,1,1, 0,1,1, 0,1,1,
+ 0,1,1, 0,1,1, 0,1,1, 0,1,1, 0,1,1, 0,1,1,
+ 0,1,1, 0,1,1, 0,1,1, 0,1,1, 0,1,1, 0,1,1,
+ 0,1,1, 0,1,1, 0,1,1, 0,1,1, 0,1,1, 0,1,1,
+ 0,1,1, 0,1,1, 0,1,1, 0,1,1, 0,1,1, 0,1,1,
+ 0,1,1, 0,1,1, 0,1,1, 0,1,1, 0,1,1, 0,1,1,
+ 0,1,1, 0,1,1, 0,1,1, 0,1,1, 0,1,1, 0,1,1,
+ 0,1,1, 0,1,1, 0,1,1, 0,1,1, 0,1,1, 0,1,1,
+ 0,1,1, 0,1,1, 0,1,1, 0,1,1, 0,1,1, 0,1,1,
+ 0,1,1, 0,1,1, 0,1,1, 0,1,1, 0,1,1, 0,1,1,
+ 0,1,1, 0,1,1, 0,1,1, 0,1,1, 0,1,1, 0,1,1,
+ 0,1,1, 0,1,1, 0,1,1, 0,1,1, 0,1,1, 0,1,1,
+ 0,1,1, 0,1,1, 0,1,1, 0,1,1, 0,1,1, 0,1,1,
+ 0,1,1, 0,1,1, 0,1,1, 0,1,1, 0,1,1, 0,1,1,
+ 0,1,1, 0,1,1, 0,1,1, 0,1,1, 0,1,1, 0,1,1,
+ 0,1,1, 0,1,1, 0,1,1, 0,1,1, 0,1,1, 0,1,1,
+ 0,1,1, 0,1,1, 0,1,1, 0,1,1, 0,1,1, 0,1,1,
+ 0,1,1, 0,1,1, 0,1,1, 0,1,1, 0,1,1, 0,1,1,
+ 0,1,1, 0,1,1, 0,1,1, 0,1,1, 0,1,1, 0,1,1,
+ 0,1,1, 0,1,1, 0,1,1, 0,1,1, 0,1,1, 0,1,1,
+ 0,1,1, 0,1,1, 0,1,1, 0,1,1, 0,1,1, 0,1,1,
+ 0,1,1, 0,1,1, 0,1,1, 0,1,1, 0,1,1, 0,1,1,
+ 0,1,1, 0,1,1, 0,1,1, 0,1,1, 0,1,1, 0,1,1,
+ 0,1,1, 0,1,1, 0,1,1, 0,1,1, 0,1,1, 0,1,1,
+ 0,1,1, 0,1,1, 0,1,1, 0,1,1, 0,1,1, 0,1,1,
+ 0,1,1, 0,1,1, 0,1,1, 0,1,1, 0,1,1, 0,1,1,
+ 0,1,1, 0,1,1, 0,1,1, 0,1,1, 0,1,1, 0,1,1,
+ 0,1,1, 0,1,1, 0,1,1, 0,1,1, 0,1,1, 0,1,1,
+ 0,1,1, 0,1,1, 0,1,1, 0,1,1, 0,1,1, 0,1,1,
+ 0,1,1, 0,1,1, 0,1,1, 0,1,1, 0,1,1, 0,1,1,
+ 0,1,1, 0,1,1, 0,1,1, 0,1,1, 0,1,1, 0,1,1,
+ 0,1,1, 0,1,1, 0,1,1, 0,1,1, 0,1,1, 0,1,1,
+ 0,1,1, 0,1,1, 0,1,1, 0,1,1, 0,1,1, 0,1,1,
+ 0,1,1, 0,1,1, 0,1,1, 0,1,1, 0,1,1, 0,1,1,
+ 0,1,1, 0,1,1, 0,1,1, 0,1,1, 0,1,1, 0,1,1,
+ 0,1,1, 0,1,1, 0,1,1, 0,1,1, 0,1,1, 0,1,1,
+ 0,1,1, 0,1,1, 0,1,1, 0,1,1, 0,1,1, 0,1,1,
+ 0,1,1, 0,1,1, 0,1,1, 0,1,1, 0,1,1, 0,1,1,
+ 0,1,1, 0,1,1, 0,1,1, 0,1,1, 0,1,1, 0,1,1,
+ 0,1,1, 0,1,1, 0,1,1, 0,1,1, 0,1,1, 0,1,1,
+ 0,1,1, 0,1,1, 0,1,1, 0,1,1, 0,1,1, 0,1,1,
+ 0,1,1, 0,1,1, 0,1,1, 0,1,1, 0,1,1, 0,1,1,
+ 0,1,1, 0,1,1, 0,1,1, 0,1,1, 0,1,1, 0,1,1,
+ 0,1,1, 0,1,1, 0,1,1, 0,1,1, 0,1,1, 0,1,1,
+ 0,1,1, 0,1,1, 0,1,1, 0,1,1, 0,1,1, 0,1,1,
+ 0,1,1, 0,1,1, 0,1,1, 0,1,1, 0,1,1, 0,1,1,
+ 0,1,1, 0,1,1, 0,1,1, 0,1,1, 0,1,1, 0,1,1,
+ 0,1,1, 0,1,1, 0,1,1, 0,1,1, 0,1,1, 0,1,1,
+ 0,1,1, 0,1,1, 0,1,1, 0,1,1, 0,1,1, 0,1,1,
+};
+
+const uint8_t gsm0503_puncture_mcs9_p2[1836] = {
+ 1,0,1, 1,0,1, 1,0,1, 1,0,1, 1,0,1, 1,0,1,
+ 1,0,1, 1,0,1, 1,0,1, 1,0,1, 1,0,1, 1,0,1,
+ 1,0,1, 1,0,1, 1,0,1, 1,0,1, 1,0,1, 1,0,1,
+ 1,0,1, 1,0,1, 1,0,1, 1,0,1, 1,0,1, 1,0,1,
+ 1,0,1, 1,0,1, 1,0,1, 1,0,1, 1,0,1, 1,0,1,
+ 1,0,1, 1,0,1, 1,0,1, 1,0,1, 1,0,1, 1,0,1,
+ 1,0,1, 1,0,1, 1,0,1, 1,0,1, 1,0,1, 1,0,1,
+ 1,0,1, 1,0,1, 1,0,1, 1,0,1, 1,0,1, 1,0,1,
+ 1,0,1, 1,0,1, 1,0,1, 1,0,1, 1,0,1, 1,0,1,
+ 1,0,1, 1,0,1, 1,0,1, 1,0,1, 1,0,1, 1,0,1,
+ 1,0,1, 1,0,1, 1,0,1, 1,0,1, 1,0,1, 1,0,1,
+ 1,0,1, 1,0,1, 1,0,1, 1,0,1, 1,0,1, 1,0,1,
+ 1,0,1, 1,0,1, 1,0,1, 1,0,1, 1,0,1, 1,0,1,
+ 1,0,1, 1,0,1, 1,0,1, 1,0,1, 1,0,1, 1,0,1,
+ 1,0,1, 1,0,1, 1,0,1, 1,0,1, 1,0,1, 1,0,1,
+ 1,0,1, 1,0,1, 1,0,1, 1,0,1, 1,0,1, 1,0,1,
+ 1,0,1, 1,0,1, 1,0,1, 1,0,1, 1,0,1, 1,0,1,
+ 1,0,1, 1,0,1, 1,0,1, 1,0,1, 1,0,1, 1,0,1,
+ 1,0,1, 1,0,1, 1,0,1, 1,0,1, 1,0,1, 1,0,1,
+ 1,0,1, 1,0,1, 1,0,1, 1,0,1, 1,0,1, 1,0,1,
+ 1,0,1, 1,0,1, 1,0,1, 1,0,1, 1,0,1, 1,0,1,
+ 1,0,1, 1,0,1, 1,0,1, 1,0,1, 1,0,1, 1,0,1,
+ 1,0,1, 1,0,1, 1,0,1, 1,0,1, 1,0,1, 1,0,1,
+ 1,0,1, 1,0,1, 1,0,1, 1,0,1, 1,0,1, 1,0,1,
+ 1,0,1, 1,0,1, 1,0,1, 1,0,1, 1,0,1, 1,0,1,
+ 1,0,1, 1,0,1, 1,0,1, 1,0,1, 1,0,1, 1,0,1,
+ 1,0,1, 1,0,1, 1,0,1, 1,0,1, 1,0,1, 1,0,1,
+ 1,0,1, 1,0,1, 1,0,1, 1,0,1, 1,0,1, 1,0,1,
+ 1,0,1, 1,0,1, 1,0,1, 1,0,1, 1,0,1, 1,0,1,
+ 1,0,1, 1,0,1, 1,0,1, 1,0,1, 1,0,1, 1,0,1,
+ 1,0,1, 1,0,1, 1,0,1, 1,0,1, 1,0,1, 1,0,1,
+ 1,0,1, 1,0,1, 1,0,1, 1,0,1, 1,0,1, 1,0,1,
+ 1,0,1, 1,0,1, 1,0,1, 1,0,1, 1,0,1, 1,0,1,
+ 1,0,1, 1,0,1, 1,0,1, 1,0,1, 1,0,1, 1,0,1,
+ 1,0,1, 1,0,1, 1,0,1, 1,0,1, 1,0,1, 1,0,1,
+ 1,0,1, 1,0,1, 1,0,1, 1,0,1, 1,0,1, 1,0,1,
+ 1,0,1, 1,0,1, 1,0,1, 1,0,1, 1,0,1, 1,0,1,
+ 1,0,1, 1,0,1, 1,0,1, 1,0,1, 1,0,1, 1,0,1,
+ 1,0,1, 1,0,1, 1,0,1, 1,0,1, 1,0,1, 1,0,1,
+ 1,0,1, 1,0,1, 1,0,1, 1,0,1, 1,0,1, 1,0,1,
+ 1,0,1, 1,0,1, 1,0,1, 1,0,1, 1,0,1, 1,0,1,
+ 1,0,1, 1,0,1, 1,0,1, 1,0,1, 1,0,1, 1,0,1,
+ 1,0,1, 1,0,1, 1,0,1, 1,0,1, 1,0,1, 1,0,1,
+ 1,0,1, 1,0,1, 1,0,1, 1,0,1, 1,0,1, 1,0,1,
+ 1,0,1, 1,0,1, 1,0,1, 1,0,1, 1,0,1, 1,0,1,
+ 1,0,1, 1,0,1, 1,0,1, 1,0,1, 1,0,1, 1,0,1,
+ 1,0,1, 1,0,1, 1,0,1, 1,0,1, 1,0,1, 1,0,1,
+ 1,0,1, 1,0,1, 1,0,1, 1,0,1, 1,0,1, 1,0,1,
+ 1,0,1, 1,0,1, 1,0,1, 1,0,1, 1,0,1, 1,0,1,
+ 1,0,1, 1,0,1, 1,0,1, 1,0,1, 1,0,1, 1,0,1,
+ 1,0,1, 1,0,1, 1,0,1, 1,0,1, 1,0,1, 1,0,1,
+ 1,0,1, 1,0,1, 1,0,1, 1,0,1, 1,0,1, 1,0,1,
+ 1,0,1, 1,0,1, 1,0,1, 1,0,1, 1,0,1, 1,0,1,
+ 1,0,1, 1,0,1, 1,0,1, 1,0,1, 1,0,1, 1,0,1,
+ 1,0,1, 1,0,1, 1,0,1, 1,0,1, 1,0,1, 1,0,1,
+ 1,0,1, 1,0,1, 1,0,1, 1,0,1, 1,0,1, 1,0,1,
+ 1,0,1, 1,0,1, 1,0,1, 1,0,1, 1,0,1, 1,0,1,
+ 1,0,1, 1,0,1, 1,0,1, 1,0,1, 1,0,1, 1,0,1,
+ 1,0,1, 1,0,1, 1,0,1, 1,0,1, 1,0,1, 1,0,1,
+ 1,0,1, 1,0,1, 1,0,1, 1,0,1, 1,0,1, 1,0,1,
+ 1,0,1, 1,0,1, 1,0,1, 1,0,1, 1,0,1, 1,0,1,
+ 1,0,1, 1,0,1, 1,0,1, 1,0,1, 1,0,1, 1,0,1,
+ 1,0,1, 1,0,1, 1,0,1, 1,0,1, 1,0,1, 1,0,1,
+ 1,0,1, 1,0,1, 1,0,1, 1,0,1, 1,0,1, 1,0,1,
+ 1,0,1, 1,0,1, 1,0,1, 1,0,1, 1,0,1, 1,0,1,
+ 1,0,1, 1,0,1, 1,0,1, 1,0,1, 1,0,1, 1,0,1,
+ 1,0,1, 1,0,1, 1,0,1, 1,0,1, 1,0,1, 1,0,1,
+ 1,0,1, 1,0,1, 1,0,1, 1,0,1, 1,0,1, 1,0,1,
+ 1,0,1, 1,0,1, 1,0,1, 1,0,1, 1,0,1, 1,0,1,
+ 1,0,1, 1,0,1, 1,0,1, 1,0,1, 1,0,1, 1,0,1,
+ 1,0,1, 1,0,1, 1,0,1, 1,0,1, 1,0,1, 1,0,1,
+ 1,0,1, 1,0,1, 1,0,1, 1,0,1, 1,0,1, 1,0,1,
+ 1,0,1, 1,0,1, 1,0,1, 1,0,1, 1,0,1, 1,0,1,
+ 1,0,1, 1,0,1, 1,0,1, 1,0,1, 1,0,1, 1,0,1,
+ 1,0,1, 1,0,1, 1,0,1, 1,0,1, 1,0,1, 1,0,1,
+ 1,0,1, 1,0,1, 1,0,1, 1,0,1, 1,0,1, 1,0,1,
+ 1,0,1, 1,0,1, 1,0,1, 1,0,1, 1,0,1, 1,0,1,
+ 1,0,1, 1,0,1, 1,0,1, 1,0,1, 1,0,1, 1,0,1,
+ 1,0,1, 1,0,1, 1,0,1, 1,0,1, 1,0,1, 1,0,1,
+ 1,0,1, 1,0,1, 1,0,1, 1,0,1, 1,0,1, 1,0,1,
+ 1,0,1, 1,0,1, 1,0,1, 1,0,1, 1,0,1, 1,0,1,
+ 1,0,1, 1,0,1, 1,0,1, 1,0,1, 1,0,1, 1,0,1,
+ 1,0,1, 1,0,1, 1,0,1, 1,0,1, 1,0,1, 1,0,1,
+ 1,0,1, 1,0,1, 1,0,1, 1,0,1, 1,0,1, 1,0,1,
+ 1,0,1, 1,0,1, 1,0,1, 1,0,1, 1,0,1, 1,0,1,
+ 1,0,1, 1,0,1, 1,0,1, 1,0,1, 1,0,1, 1,0,1,
+ 1,0,1, 1,0,1, 1,0,1, 1,0,1, 1,0,1, 1,0,1,
+ 1,0,1, 1,0,1, 1,0,1, 1,0,1, 1,0,1, 1,0,1,
+ 1,0,1, 1,0,1, 1,0,1, 1,0,1, 1,0,1, 1,0,1,
+ 1,0,1, 1,0,1, 1,0,1, 1,0,1, 1,0,1, 1,0,1,
+ 1,0,1, 1,0,1, 1,0,1, 1,0,1, 1,0,1, 1,0,1,
+ 1,0,1, 1,0,1, 1,0,1, 1,0,1, 1,0,1, 1,0,1,
+ 1,0,1, 1,0,1, 1,0,1, 1,0,1, 1,0,1, 1,0,1,
+ 1,0,1, 1,0,1, 1,0,1, 1,0,1, 1,0,1, 1,0,1,
+ 1,0,1, 1,0,1, 1,0,1, 1,0,1, 1,0,1, 1,0,1,
+ 1,0,1, 1,0,1, 1,0,1, 1,0,1, 1,0,1, 1,0,1,
+ 1,0,1, 1,0,1, 1,0,1, 1,0,1, 1,0,1, 1,0,1,
+ 1,0,1, 1,0,1, 1,0,1, 1,0,1, 1,0,1, 1,0,1,
+ 1,0,1, 1,0,1, 1,0,1, 1,0,1, 1,0,1, 1,0,1,
+ 1,0,1, 1,0,1, 1,0,1, 1,0,1, 1,0,1, 1,0,1,
+ 1,0,1, 1,0,1, 1,0,1, 1,0,1, 1,0,1, 1,0,1,
+ 1,0,1, 1,0,1, 1,0,1, 1,0,1, 1,0,1, 1,0,1,
+};
+
+const uint8_t gsm0503_puncture_mcs9_p3[1836] = {
+ 1,1,0, 1,1,0, 1,1,0, 1,1,0, 1,1,0, 1,1,0,
+ 1,1,0, 1,1,0, 1,1,0, 1,1,0, 1,1,0, 1,1,0,
+ 1,1,0, 1,1,0, 1,1,0, 1,1,0, 1,1,0, 1,1,0,
+ 1,1,0, 1,1,0, 1,1,0, 1,1,0, 1,1,0, 1,1,0,
+ 1,1,0, 1,1,0, 1,1,0, 1,1,0, 1,1,0, 1,1,0,
+ 1,1,0, 1,1,0, 1,1,0, 1,1,0, 1,1,0, 1,1,0,
+ 1,1,0, 1,1,0, 1,1,0, 1,1,0, 1,1,0, 1,1,0,
+ 1,1,0, 1,1,0, 1,1,0, 1,1,0, 1,1,0, 1,1,0,
+ 1,1,0, 1,1,0, 1,1,0, 1,1,0, 1,1,0, 1,1,0,
+ 1,1,0, 1,1,0, 1,1,0, 1,1,0, 1,1,0, 1,1,0,
+ 1,1,0, 1,1,0, 1,1,0, 1,1,0, 1,1,0, 1,1,0,
+ 1,1,0, 1,1,0, 1,1,0, 1,1,0, 1,1,0, 1,1,0,
+ 1,1,0, 1,1,0, 1,1,0, 1,1,0, 1,1,0, 1,1,0,
+ 1,1,0, 1,1,0, 1,1,0, 1,1,0, 1,1,0, 1,1,0,
+ 1,1,0, 1,1,0, 1,1,0, 1,1,0, 1,1,0, 1,1,0,
+ 1,1,0, 1,1,0, 1,1,0, 1,1,0, 1,1,0, 1,1,0,
+ 1,1,0, 1,1,0, 1,1,0, 1,1,0, 1,1,0, 1,1,0,
+ 1,1,0, 1,1,0, 1,1,0, 1,1,0, 1,1,0, 1,1,0,
+ 1,1,0, 1,1,0, 1,1,0, 1,1,0, 1,1,0, 1,1,0,
+ 1,1,0, 1,1,0, 1,1,0, 1,1,0, 1,1,0, 1,1,0,
+ 1,1,0, 1,1,0, 1,1,0, 1,1,0, 1,1,0, 1,1,0,
+ 1,1,0, 1,1,0, 1,1,0, 1,1,0, 1,1,0, 1,1,0,
+ 1,1,0, 1,1,0, 1,1,0, 1,1,0, 1,1,0, 1,1,0,
+ 1,1,0, 1,1,0, 1,1,0, 1,1,0, 1,1,0, 1,1,0,
+ 1,1,0, 1,1,0, 1,1,0, 1,1,0, 1,1,0, 1,1,0,
+ 1,1,0, 1,1,0, 1,1,0, 1,1,0, 1,1,0, 1,1,0,
+ 1,1,0, 1,1,0, 1,1,0, 1,1,0, 1,1,0, 1,1,0,
+ 1,1,0, 1,1,0, 1,1,0, 1,1,0, 1,1,0, 1,1,0,
+ 1,1,0, 1,1,0, 1,1,0, 1,1,0, 1,1,0, 1,1,0,
+ 1,1,0, 1,1,0, 1,1,0, 1,1,0, 1,1,0, 1,1,0,
+ 1,1,0, 1,1,0, 1,1,0, 1,1,0, 1,1,0, 1,1,0,
+ 1,1,0, 1,1,0, 1,1,0, 1,1,0, 1,1,0, 1,1,0,
+ 1,1,0, 1,1,0, 1,1,0, 1,1,0, 1,1,0, 1,1,0,
+ 1,1,0, 1,1,0, 1,1,0, 1,1,0, 1,1,0, 1,1,0,
+ 1,1,0, 1,1,0, 1,1,0, 1,1,0, 1,1,0, 1,1,0,
+ 1,1,0, 1,1,0, 1,1,0, 1,1,0, 1,1,0, 1,1,0,
+ 1,1,0, 1,1,0, 1,1,0, 1,1,0, 1,1,0, 1,1,0,
+ 1,1,0, 1,1,0, 1,1,0, 1,1,0, 1,1,0, 1,1,0,
+ 1,1,0, 1,1,0, 1,1,0, 1,1,0, 1,1,0, 1,1,0,
+ 1,1,0, 1,1,0, 1,1,0, 1,1,0, 1,1,0, 1,1,0,
+ 1,1,0, 1,1,0, 1,1,0, 1,1,0, 1,1,0, 1,1,0,
+ 1,1,0, 1,1,0, 1,1,0, 1,1,0, 1,1,0, 1,1,0,
+ 1,1,0, 1,1,0, 1,1,0, 1,1,0, 1,1,0, 1,1,0,
+ 1,1,0, 1,1,0, 1,1,0, 1,1,0, 1,1,0, 1,1,0,
+ 1,1,0, 1,1,0, 1,1,0, 1,1,0, 1,1,0, 1,1,0,
+ 1,1,0, 1,1,0, 1,1,0, 1,1,0, 1,1,0, 1,1,0,
+ 1,1,0, 1,1,0, 1,1,0, 1,1,0, 1,1,0, 1,1,0,
+ 1,1,0, 1,1,0, 1,1,0, 1,1,0, 1,1,0, 1,1,0,
+ 1,1,0, 1,1,0, 1,1,0, 1,1,0, 1,1,0, 1,1,0,
+ 1,1,0, 1,1,0, 1,1,0, 1,1,0, 1,1,0, 1,1,0,
+ 1,1,0, 1,1,0, 1,1,0, 1,1,0, 1,1,0, 1,1,0,
+ 1,1,0, 1,1,0, 1,1,0, 1,1,0, 1,1,0, 1,1,0,
+ 1,1,0, 1,1,0, 1,1,0, 1,1,0, 1,1,0, 1,1,0,
+ 1,1,0, 1,1,0, 1,1,0, 1,1,0, 1,1,0, 1,1,0,
+ 1,1,0, 1,1,0, 1,1,0, 1,1,0, 1,1,0, 1,1,0,
+ 1,1,0, 1,1,0, 1,1,0, 1,1,0, 1,1,0, 1,1,0,
+ 1,1,0, 1,1,0, 1,1,0, 1,1,0, 1,1,0, 1,1,0,
+ 1,1,0, 1,1,0, 1,1,0, 1,1,0, 1,1,0, 1,1,0,
+ 1,1,0, 1,1,0, 1,1,0, 1,1,0, 1,1,0, 1,1,0,
+ 1,1,0, 1,1,0, 1,1,0, 1,1,0, 1,1,0, 1,1,0,
+ 1,1,0, 1,1,0, 1,1,0, 1,1,0, 1,1,0, 1,1,0,
+ 1,1,0, 1,1,0, 1,1,0, 1,1,0, 1,1,0, 1,1,0,
+ 1,1,0, 1,1,0, 1,1,0, 1,1,0, 1,1,0, 1,1,0,
+ 1,1,0, 1,1,0, 1,1,0, 1,1,0, 1,1,0, 1,1,0,
+ 1,1,0, 1,1,0, 1,1,0, 1,1,0, 1,1,0, 1,1,0,
+ 1,1,0, 1,1,0, 1,1,0, 1,1,0, 1,1,0, 1,1,0,
+ 1,1,0, 1,1,0, 1,1,0, 1,1,0, 1,1,0, 1,1,0,
+ 1,1,0, 1,1,0, 1,1,0, 1,1,0, 1,1,0, 1,1,0,
+ 1,1,0, 1,1,0, 1,1,0, 1,1,0, 1,1,0, 1,1,0,
+ 1,1,0, 1,1,0, 1,1,0, 1,1,0, 1,1,0, 1,1,0,
+ 1,1,0, 1,1,0, 1,1,0, 1,1,0, 1,1,0, 1,1,0,
+ 1,1,0, 1,1,0, 1,1,0, 1,1,0, 1,1,0, 1,1,0,
+ 1,1,0, 1,1,0, 1,1,0, 1,1,0, 1,1,0, 1,1,0,
+ 1,1,0, 1,1,0, 1,1,0, 1,1,0, 1,1,0, 1,1,0,
+ 1,1,0, 1,1,0, 1,1,0, 1,1,0, 1,1,0, 1,1,0,
+ 1,1,0, 1,1,0, 1,1,0, 1,1,0, 1,1,0, 1,1,0,
+ 1,1,0, 1,1,0, 1,1,0, 1,1,0, 1,1,0, 1,1,0,
+ 1,1,0, 1,1,0, 1,1,0, 1,1,0, 1,1,0, 1,1,0,
+ 1,1,0, 1,1,0, 1,1,0, 1,1,0, 1,1,0, 1,1,0,
+ 1,1,0, 1,1,0, 1,1,0, 1,1,0, 1,1,0, 1,1,0,
+ 1,1,0, 1,1,0, 1,1,0, 1,1,0, 1,1,0, 1,1,0,
+ 1,1,0, 1,1,0, 1,1,0, 1,1,0, 1,1,0, 1,1,0,
+ 1,1,0, 1,1,0, 1,1,0, 1,1,0, 1,1,0, 1,1,0,
+ 1,1,0, 1,1,0, 1,1,0, 1,1,0, 1,1,0, 1,1,0,
+ 1,1,0, 1,1,0, 1,1,0, 1,1,0, 1,1,0, 1,1,0,
+ 1,1,0, 1,1,0, 1,1,0, 1,1,0, 1,1,0, 1,1,0,
+ 1,1,0, 1,1,0, 1,1,0, 1,1,0, 1,1,0, 1,1,0,
+ 1,1,0, 1,1,0, 1,1,0, 1,1,0, 1,1,0, 1,1,0,
+ 1,1,0, 1,1,0, 1,1,0, 1,1,0, 1,1,0, 1,1,0,
+ 1,1,0, 1,1,0, 1,1,0, 1,1,0, 1,1,0, 1,1,0,
+ 1,1,0, 1,1,0, 1,1,0, 1,1,0, 1,1,0, 1,1,0,
+ 1,1,0, 1,1,0, 1,1,0, 1,1,0, 1,1,0, 1,1,0,
+ 1,1,0, 1,1,0, 1,1,0, 1,1,0, 1,1,0, 1,1,0,
+ 1,1,0, 1,1,0, 1,1,0, 1,1,0, 1,1,0, 1,1,0,
+ 1,1,0, 1,1,0, 1,1,0, 1,1,0, 1,1,0, 1,1,0,
+ 1,1,0, 1,1,0, 1,1,0, 1,1,0, 1,1,0, 1,1,0,
+ 1,1,0, 1,1,0, 1,1,0, 1,1,0, 1,1,0, 1,1,0,
+ 1,1,0, 1,1,0, 1,1,0, 1,1,0, 1,1,0, 1,1,0,
+ 1,1,0, 1,1,0, 1,1,0, 1,1,0, 1,1,0, 1,1,0,
+ 1,1,0, 1,1,0, 1,1,0, 1,1,0, 1,1,0, 1,1,0,
+ 1,1,0, 1,1,0, 1,1,0, 1,1,0, 1,1,0, 1,1,0,
+ 1,1,0, 1,1,0, 1,1,0, 1,1,0, 1,1,0, 1,1,0,
+};
+
+const uint16_t gsm0503_interleave_mcs5[1248] = {
+ 0, 463, 890, 1038, 220, 371, 795, 946, 582, 733, 1160, 63, 490, 641, 277, 428,
+ 852, 1003, 185, 333, 1223, 120, 547, 698, 1122, 28, 915, 1066, 242, 390, 817, 968,
+ 610, 761, 1185, 85, 512, 660, 305, 453, 880, 1031, 204, 355, 782, 1242, 148, 575,
+ 723, 1150, 50, 474, 625, 1088, 267, 418, 845, 993, 169, 320, 1207, 113, 537, 688,
+ 1115, 12, 902, 1050, 232, 383, 807, 958, 594, 745, 1172, 75, 502, 653, 289, 440,
+ 864, 1015, 197, 345, 1235, 132, 559, 710, 1134, 40, 927, 1078, 254, 402, 829, 980,
+ 159, 622, 773, 1197, 97, 524, 672, 1099, 5, 465, 892, 1043, 216, 367, 794, 942,
+ 587, 735, 1162, 62, 486, 637, 279, 430, 857, 1005, 181, 332, 1219, 125, 549, 700,
+ 1127, 24, 914, 1062, 244, 395, 819, 970, 606, 757, 1184, 87, 514, 665, 301, 452,
+ 876, 1027, 209, 357, 784, 1247, 144, 571, 722, 1146, 52, 479, 627, 1090, 266, 414,
+ 841, 992, 171, 322, 1209, 109, 536, 684, 1111, 17, 904, 1055, 228, 379, 806, 954,
+ 599, 747, 1174, 74, 498, 649, 291, 442, 869, 1017, 193, 344, 1231, 137, 561, 712,
+ 1139, 36, 926, 1074, 256, 407, 831, 982, 158, 618, 769, 1196, 99, 526, 677, 1101,
+ 7, 458, 894, 1033, 227, 363, 802, 941, 577, 740, 1152, 70, 485, 645, 284, 420,
+ 859, 998, 189, 328, 1215, 127, 542, 702, 1117, 35, 922, 1061, 246, 385, 824, 960,
+ 605, 765, 1180, 92, 504, 667, 309, 448, 887, 1023, 211, 350, 786, 1237, 155, 567,
+ 730, 1145, 54, 469, 632, 1080, 274, 413, 849, 988, 176, 312, 1202, 117, 532, 695,
+ 1107, 19, 906, 1045, 239, 375, 814, 953, 589, 752, 1164, 82, 497, 657, 296, 432,
+ 871, 1010, 201, 340, 1227, 139, 554, 714, 1129, 47, 934, 1073, 258, 397, 836, 972,
+ 166, 617, 777, 1192, 104, 516, 679, 1094, 9, 460, 899, 1035, 223, 362, 798, 937,
+ 579, 742, 1157, 66, 481, 644, 286, 425, 861, 1000, 188, 324, 1214, 129, 544, 707,
+ 1119, 31, 918, 1057, 251, 387, 826, 965, 601, 764, 1176, 94, 509, 669, 308, 444,
+ 883, 1022, 213, 352, 791, 1239, 151, 566, 726, 1141, 59, 471, 634, 1085, 270, 409,
+ 848, 984, 178, 317, 1204, 116, 528, 691, 1106, 21, 911, 1047, 235, 374, 810, 949,
+ 591, 754, 1169, 78, 493, 656, 298, 437, 873, 1012, 200, 336, 1226, 141, 556, 719,
+ 1131, 43, 930, 1069, 263, 399, 838, 977, 162, 613, 776, 1188, 106, 521, 681, 1096,
+ 2, 462, 889, 1040, 219, 370, 797, 945, 584, 732, 1159, 65, 489, 640, 276, 427,
+ 854, 1002, 184, 335, 1222, 122, 546, 697, 1124, 27, 917, 1065, 241, 392, 816, 967,
+ 609, 760, 1187, 84, 511, 662, 304, 455, 879, 1030, 206, 354, 781, 1244, 147, 574,
+ 725, 1149, 49, 476, 624, 1087, 269, 417, 844, 995, 168, 319, 1206, 112, 539, 687,
+ 1114, 14, 901, 1052, 231, 382, 809, 957, 596, 744, 1171, 77, 501, 652, 288, 439,
+ 866, 1014, 196, 347, 1234, 134, 558, 709, 1136, 39, 929, 1077, 253, 404, 828, 979,
+ 161, 621, 772, 1199, 96, 523, 674, 1098, 4, 467, 891, 1042, 218, 366, 793, 944,
+ 586, 737, 1161, 61, 488, 636, 281, 429, 856, 1007, 180, 331, 1218, 124, 551, 699,
+ 1126, 26, 913, 1064, 243, 394, 821, 969, 608, 756, 1183, 89, 513, 664, 300, 451,
+ 878, 1026, 208, 359, 783, 1246, 146, 570, 721, 1148, 51, 478, 629, 1089, 265, 416,
+ 840, 991, 173, 321, 1211, 108, 535, 686, 1110, 16, 903, 1054, 230, 378, 805, 956,
+ 598, 749, 1173, 73, 500, 648, 293, 441, 868, 1019, 192, 343, 1230, 136, 563, 711,
+ 1138, 38, 925, 1076, 255, 406, 833, 981, 157, 620, 768, 1195, 101, 525, 676, 1103,
+ 6, 457, 896, 1032, 226, 365, 801, 940, 576, 739, 1154, 69, 484, 647, 283, 422,
+ 858, 997, 191, 327, 1217, 126, 541, 704, 1116, 34, 921, 1060, 248, 384, 823, 962,
+ 604, 767, 1179, 91, 506, 666, 311, 447, 886, 1025, 210, 349, 788, 1236, 154, 569,
+ 729, 1144, 56, 468, 631, 1082, 273, 412, 851, 987, 175, 314, 1201, 119, 531, 694,
+ 1109, 18, 908, 1044, 238, 377, 813, 952, 588, 751, 1166, 81, 496, 659, 295, 434,
+ 870, 1009, 203, 339, 1229, 138, 553, 716, 1128, 46, 933, 1072, 260, 396, 835, 974,
+ 165, 616, 779, 1191, 103, 518, 678, 1093, 11, 459, 898, 1037, 222, 361, 800, 936,
+ 581, 741, 1156, 68, 480, 643, 285, 424, 863, 999, 187, 326, 1213, 131, 543, 706,
+ 1121, 30, 920, 1056, 250, 389, 825, 964, 600, 763, 1178, 93, 508, 671, 307, 446,
+ 882, 1021, 215, 351, 790, 1241, 150, 565, 728, 1140, 58, 473, 633, 1084, 272, 408,
+ 847, 986, 177, 316, 1203, 115, 530, 690, 1105, 23, 910, 1049, 234, 373, 812, 948,
+ 593, 753, 1168, 80, 492, 655, 297, 436, 875, 1011, 199, 338, 1225, 143, 555, 718,
+ 1133, 42, 932, 1068, 262, 401, 837, 976, 164, 612, 775, 1190, 105, 520, 683, 1095,
+ 1, 464, 888, 1039, 221, 369, 796, 947, 583, 734, 1158, 64, 491, 639, 278, 426,
+ 853, 1004, 183, 334, 1221, 121, 548, 696, 1123, 29, 916, 1067, 240, 391, 818, 966,
+ 611, 759, 1186, 86, 510, 661, 303, 454, 881, 1029, 205, 356, 780, 1243, 149, 573,
+ 724, 1151, 48, 475, 626, 1086, 268, 419, 843, 994, 170, 318, 1208, 111, 538, 689,
+ 1113, 13, 900, 1051, 233, 381, 808, 959, 595, 746, 1170, 76, 503, 651, 290, 438,
+ 865, 1016, 195, 346, 1233, 133, 560, 708, 1135, 41, 928, 1079, 252, 403, 830, 978,
+ 160, 623, 771, 1198, 98, 522, 673, 1100, 3, 466, 893, 1041, 217, 368, 792, 943,
+ 585, 736, 1163, 60, 487, 638, 280, 431, 855, 1006, 182, 330, 1220, 123, 550, 701,
+ 1125, 25, 912, 1063, 245, 393, 820, 971, 607, 758, 1182, 88, 515, 663, 302, 450,
+ 877, 1028, 207, 358, 785, 1245, 145, 572, 720, 1147, 53, 477, 628, 1091, 264, 415,
+ 842, 990, 172, 323, 1210, 110, 534, 685, 1112, 15, 905, 1053, 229, 380, 804, 955,
+ 597, 748, 1175, 72, 499, 650, 292, 443, 867, 1018, 194, 342, 1232, 135, 562, 713,
+ 1137, 37, 924, 1075, 257, 405, 832, 983, 156, 619, 770, 1194, 100, 527, 675, 1102,
+ 8, 456, 895, 1034, 225, 364, 803, 939, 578, 738, 1153, 71, 483, 646, 282, 421,
+ 860, 996, 190, 329, 1216, 128, 540, 703, 1118, 33, 923, 1059, 247, 386, 822, 961,
+ 603, 766, 1181, 90, 505, 668, 310, 449, 885, 1024, 212, 348, 787, 1238, 153, 568,
+ 731, 1143, 55, 470, 630, 1081, 275, 411, 850, 989, 174, 313, 1200, 118, 533, 693,
+ 1108, 20, 907, 1046, 237, 376, 815, 951, 590, 750, 1165, 83, 495, 658, 294, 433,
+ 872, 1008, 202, 341, 1228, 140, 552, 715, 1130, 45, 935, 1071, 259, 398, 834, 973,
+ 167, 615, 778, 1193, 102, 517, 680, 1092, 10, 461, 897, 1036, 224, 360, 799, 938,
+ 580, 743, 1155, 67, 482, 642, 287, 423, 862, 1001, 186, 325, 1212, 130, 545, 705,
+ 1120, 32, 919, 1058, 249, 388, 827, 963, 602, 762, 1177, 95, 507, 670, 306, 445,
+ 884, 1020, 214, 353, 789, 1240, 152, 564, 727, 1142, 57, 472, 635, 1083, 271, 410,
+ 846, 985, 179, 315, 1205, 114, 529, 692, 1104, 22, 909, 1048, 236, 372, 811, 950,
+ 592, 755, 1167, 79, 494, 654, 299, 435, 874, 1013, 198, 337, 1224, 142, 557, 717,
+ 1132, 44, 931, 1070, 261, 400, 839, 975, 163, 614, 774, 1189, 107, 519, 682, 1097,
+};
+
+/* this corresponds to the bit-lengths of the individual codec
+ * parameters as indicated in Table 1.1 of TS 06.10 */
+const uint8_t gsm0503_gsm_fr_map[76] = {
+ 6, 6, 5, 5, 4, 4, 3, 3, 7, 2, 2, 6, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3,
+ 3, 7, 2, 2, 6, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 7, 2, 2, 6, 3, 3,
+ 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 7, 2, 2, 6, 3, 3, 3, 3, 3, 3, 3, 3, 3,
+ 3, 3, 3, 3
+};
+
+/* this table describes the 65 most importaint bits from EFR coded
+ * bits as indicated in TS 05.03 (3.1.1.1) */
+const uint8_t gsm0503_gsm_efr_protected_bits[65] = {
+ 39, 40, 41, 42, 43, 44, 48, 87, 45, 2,
+ 3, 8, 10, 18, 19, 24, 46, 47,142,143,
+ 144,145,146,147, 92, 93,195,196, 98,137,
+ 148, 94,197,149,150, 95,198, 4, 5, 11,
+ 12, 16, 9, 6, 7, 13, 17, 20, 96,199,
+ 1, 14, 15, 21, 25, 26, 28,151,201,190,
+ 240, 88,138,191,241
+};
+
+/* Encoded in-band data for speech frames */
+const ubit_t gsm0503_afs_ic_ubit[4][8] = {
+ { 0,0,0,0,0,0,0,0 },
+ { 0,1,0,1,1,1,0,1 },
+ { 1,0,1,1,1,0,1,0 },
+ { 1,1,1,0,0,1,1,1 },
+};
+
+const sbit_t gsm0503_afs_ic_sbit[4][8] = {
+ { 127, 127, 127, 127, 127, 127, 127, 127 },
+ { 127,-127, 127,-127,-127,-127, 127,-127 },
+ { -127, 127,-127,-127,-127, 127,-127, 127 },
+ { -127,-127,-127, 127, 127,-127,-127,-127 },
+};
+
+const ubit_t gsm0503_ahs_ic_ubit[4][4] = {
+ { 0,0,0,0 },
+ { 1,0,0,1 },
+ { 1,1,1,0 },
+ { 0,1,1,1 },
+};
+
+const sbit_t gsm0503_ahs_ic_sbit[4][4] = {
+ { 127, 127, 127, 127 },
+ { -127, 127, 127,-127 },
+ { -127,-127,-127, 127 },
+ { 127,-127,-127,-127 },
+};
+
+const uint8_t gsm0503_tch_hr_interleaving[228][2] = {
+ { 0 ,0 }, { 1 ,2 }, { 78 ,1 }, { 79 ,3 }, { 48 ,0 }, { 49 ,2 },
+ { 54 ,1 }, { 55 ,3 }, { 24 ,0 }, { 25 ,2 }, { 30 ,1 }, { 31 ,3 },
+ { 72 ,0 }, { 73 ,2 }, { 6 ,1 }, { 7 ,3 }, { 96 ,0 }, { 97 ,2 },
+ { 12 ,0 }, { 13 ,2 }, { 102,1 }, { 103,3 }, { 60 ,0 }, { 61 ,2 },
+ { 66 ,1 }, { 67 ,3 }, { 90 ,1 }, { 91 ,3 }, { 36 ,0 }, { 37 ,2 },
+ { 42 ,1 }, { 43 ,3 }, { 18 ,1 }, { 19 ,3 }, { 84 ,0 }, { 85 ,2 },
+ { 108,0 }, { 109,2 }, { 2 ,0 }, { 3 ,2 }, { 80 ,1 }, { 81 ,3 },
+ { 50 ,0 }, { 51 ,2 }, { 56 ,1 }, { 57 ,3 }, { 26 ,0 }, { 27 ,2 },
+ { 32 ,1 }, { 33 ,3 }, { 74 ,0 }, { 75 ,2 }, { 8 ,1 }, { 9 ,3 },
+ { 98 ,0 }, { 99 ,2 }, { 14 ,0 }, { 15 ,2 }, { 104,1 }, { 105,3 },
+ { 62 ,0 }, { 63 ,2 }, { 68 ,1 }, { 69 ,3 }, { 92 ,1 }, { 93 ,3 },
+ { 38 ,0 }, { 39 ,2 }, { 44 ,1 }, { 45 ,3 }, { 20 ,1 }, { 21 ,3 },
+ { 86 ,0 }, { 87 ,2 }, { 110,0 }, { 111,2 }, { 4 ,0 }, { 5 ,2 },
+ { 82 ,1 }, { 83 ,3 }, { 52 ,0 }, { 53 ,2 }, { 58 ,1 }, { 59 ,3 },
+ { 28 ,0 }, { 29 ,2 }, { 34 ,1 }, { 35 ,3 }, { 76 ,0 }, { 77 ,2 },
+ { 10 ,1 }, { 11 ,3 }, { 100,0 }, { 101,2 }, { 16 ,0 }, { 17 ,2 },
+ { 106,1 }, { 107,3 }, { 64 ,0 }, { 65 ,2 }, { 70 ,1 }, { 71 ,3 },
+ { 94 ,1 }, { 95 ,3 }, { 40 ,0 }, { 41 ,2 }, { 46 ,1 }, { 47 ,3 },
+ { 22 ,1 }, { 23 ,3 }, { 88 ,0 }, { 89 ,2 }, { 112,0 }, { 113,2 },
+ { 6 ,0 }, { 7 ,2 }, { 84 ,1 }, { 85 ,3 }, { 54 ,0 }, { 55 ,2 },
+ { 60 ,1 }, { 61 ,3 }, { 30 ,0 }, { 31 ,2 }, { 36 ,1 }, { 37 ,3 },
+ { 78 ,0 }, { 79 ,2 }, { 12 ,1 }, { 13 ,3 }, { 102,0 }, { 103,2 },
+ { 18 ,0 }, { 19 ,2 }, { 108,1 }, { 109,3 }, { 66 ,0 }, { 67 ,2 },
+ { 72 ,1 }, { 73 ,3 }, { 96 ,1 }, { 97 ,3 }, { 42 ,0 }, { 43 ,2 },
+ { 48 ,1 }, { 49 ,3 }, { 24 ,1 }, { 25 ,3 }, { 90 ,0 }, { 91 ,2 },
+ { 0 ,1 }, { 1 ,3 }, { 8 ,0 }, { 9 ,2 }, { 86 ,1 }, { 87 ,3 },
+ { 56 ,0 }, { 57 ,2 }, { 62 ,1 }, { 63 ,3 }, { 32 ,0 }, { 33 ,2 },
+ { 38 ,1 }, { 39 ,3 }, { 80 ,0 }, { 81 ,2 }, { 14 ,1 }, { 15 ,3 },
+ { 104,0 }, { 105,2 }, { 20 ,0 }, { 21 ,2 }, { 110,1 }, { 111,3 },
+ { 68 ,0 }, { 69 ,2 }, { 74 ,1 }, { 75 ,3 }, { 98 ,1 }, { 99 ,3 },
+ { 44 ,0 }, { 45 ,2 }, { 50 ,1 }, { 51 ,3 }, { 26 ,1 }, { 27 ,3 },
+ { 92 ,0 }, { 93 ,2 }, { 2 ,1 }, { 3 ,3 }, { 10 ,0 }, { 11 ,2 },
+ { 88 ,1 }, { 89 ,3 }, { 58 ,0 }, { 59 ,2 }, { 64 ,1 }, { 65 ,3 },
+ { 34 ,0 }, { 35 ,2 }, { 40 ,1 }, { 41 ,3 }, { 82 ,0 }, { 83 ,2 },
+ { 16 ,1 }, { 17 ,3 }, { 106,0 }, { 107,2 }, { 22 ,0 }, { 23 ,2 },
+ { 112,1 }, { 113,3 }, { 70 ,0 }, { 71 ,2 }, { 76 ,1 }, { 77 ,3 },
+ { 100,1 }, { 101,3 }, { 46 ,0 }, { 47 ,2 }, { 52 ,1 }, { 53 ,3 },
+ { 28 ,1 }, { 29 ,3 }, { 94 ,0 }, { 95 ,2 }, { 4 ,1 }, { 5 ,3 },
+};
+
+/*
+ * 3GPP TS 05.03 5.1.9.1.2 "USF precoding"
+ */
+const ubit_t gsm0503_mcs5_usf_precode_table[8][36] = {
+ { 0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0, },
+ { 1,1,1,1,1,0,0,0,0, 1,1,1,1,0,0,0,0,0, 1,1,1,1,1,1,0,0,0, 1,1,1,1,1,0,0,0,1, },
+ { 1,1,1,0,0,1,1,1,0, 1,1,1,0,1,1,1,0,0, 1,1,0,0,0,0,1,1,0, 1,1,0,0,0,1,1,0,0, },
+ { 1,0,0,1,1,1,1,0,0, 1,1,0,0,0,0,0,1,1, 1,0,1,1,1,0,1,1,1, 0,0,1,0,0,1,1,1,1, },
+ { 0,0,0,1,1,0,0,1,1, 0,0,1,0,1,1,0,1,0, 1,0,0,0,0,1,1,0,1, 1,1,1,1,1,1,1,1,0, },
+ { 1,1,0,1,0,1,0,1,1, 0,0,0,1,1,0,1,0,1, 0,1,1,1,0,1,0,1,1, 1,0,0,1,0,1,0,1,1, },
+ { 0,0,1,0,0,1,1,0,1, 1,0,1,1,1,1,1,1,1, 0,1,1,0,1,0,0,0,1, 0,0,1,1,1,0,1,0,0, },
+ { 0,1,1,0,1,0,1,1,1, 0,1,0,1,0,1,1,1,1, 0,0,0,1,1,1,1,1,0, 0,1,0,0,1,0,0,1,1, },
+};
+
+/*! @} */
diff --git a/lib/decoding/osmocom/coding/gsm0503_tables.h b/lib/decoding/osmocom/coding/gsm0503_tables.h
new file mode 100644
index 0000000..c442549
--- /dev/null
+++ b/lib/decoding/osmocom/coding/gsm0503_tables.h
@@ -0,0 +1,61 @@
+/*! \file gsm0503_tables.h
+ * GSM TS 05.03 tables.
+ */
+
+#pragma once
+
+#include <stdint.h>
+#include <osmocom/core/bits.h>
+//#include "bits.h"
+
+/*! \addtogroup tables
+ * @{
+ * \file gsm0503_tables.h */
+
+extern const ubit_t gsm0503_pdtch_hl_hn_ubit[4][8];
+extern const ubit_t gsm0503_pdtch_edge_hl_hn_ubit[3][8];
+extern const sbit_t gsm0503_pdtch_hl_hn_sbit[4][8];
+extern const sbit_t gsm0503_pdtch_edge_hl_hn_sbit[3][8];
+extern const ubit_t gsm0503_usf2six[8][6];
+extern const ubit_t gsm0503_usf2twelve_ubit[8][12];
+extern const sbit_t gsm0503_usf2twelve_sbit[8][12];
+extern const uint8_t gsm0503_puncture_cs2[588];
+extern const uint8_t gsm0503_puncture_cs3[676];
+extern const uint8_t gsm0503_puncture_mcs1_dl_hdr[108];
+extern const uint8_t gsm0503_puncture_mcs1_ul_hdr[117];
+extern const uint8_t gsm0503_puncture_mcs1_p1[588];
+extern const uint8_t gsm0503_puncture_mcs1_p2[588];
+extern const uint8_t gsm0503_puncture_mcs2_p1[732];
+extern const uint8_t gsm0503_puncture_mcs2_p2[732];
+extern const uint8_t gsm0503_puncture_mcs3_p1[948];
+extern const uint8_t gsm0503_puncture_mcs3_p2[948];
+extern const uint8_t gsm0503_puncture_mcs3_p3[948];
+extern const uint8_t gsm0503_puncture_mcs4_p1[1116];
+extern const uint8_t gsm0503_puncture_mcs4_p2[1116];
+extern const uint8_t gsm0503_puncture_mcs4_p3[1116];
+extern const uint8_t gsm0503_puncture_mcs5_p1[1404];
+extern const uint8_t gsm0503_puncture_mcs5_p2[1404];
+extern const uint8_t gsm0503_puncture_mcs6_p1[1836];
+extern const uint8_t gsm0503_puncture_mcs6_p2[1836];
+extern const uint8_t gsm0503_puncture_mcs7_dl_hdr[135];
+extern const uint8_t gsm0503_puncture_mcs7_ul_hdr[162];
+extern const uint8_t gsm0503_puncture_mcs7_p1[1404];
+extern const uint8_t gsm0503_puncture_mcs7_p2[1404];
+extern const uint8_t gsm0503_puncture_mcs7_p3[1404];
+extern const uint8_t gsm0503_puncture_mcs8_p1[1692];
+extern const uint8_t gsm0503_puncture_mcs8_p2[1692];
+extern const uint8_t gsm0503_puncture_mcs8_p3[1692];
+extern const uint8_t gsm0503_puncture_mcs9_p1[1836];
+extern const uint8_t gsm0503_puncture_mcs9_p2[1836];
+extern const uint8_t gsm0503_puncture_mcs9_p3[1836];
+extern const uint16_t gsm0503_interleave_mcs5[1248];
+extern const uint8_t gsm0503_gsm_fr_map[76];
+extern const uint8_t gsm0503_gsm_efr_protected_bits[65];
+extern const ubit_t gsm0503_afs_ic_ubit[4][8];
+extern const sbit_t gsm0503_afs_ic_sbit[4][8];
+extern const ubit_t gsm0503_ahs_ic_ubit[4][4];
+extern const sbit_t gsm0503_ahs_ic_sbit[4][4];
+extern const uint8_t gsm0503_tch_hr_interleaving[228][2];
+extern const ubit_t gsm0503_mcs5_usf_precode_table[8][36];
+
+/*! @} */
diff --git a/lib/decoding/osmocom/core/CMakeLists.txt b/lib/decoding/osmocom/core/CMakeLists.txt
new file mode 100644
index 0000000..ce8e60f
--- /dev/null
+++ b/lib/decoding/osmocom/core/CMakeLists.txt
@@ -0,0 +1,11 @@
+add_sources(
+bits.c
+bitvec.c
+conv_acc.c
+conv_acc_generic.c
+conv.c
+crc16gen.c
+crc64gen.c
+crc8gen.c
+panic.c
+)
diff --git a/lib/decoding/osmocom/core/bit16gen.h b/lib/decoding/osmocom/core/bit16gen.h
new file mode 100644
index 0000000..5c6162c
--- /dev/null
+++ b/lib/decoding/osmocom/core/bit16gen.h
@@ -0,0 +1,105 @@
+/*
+ * bit16gen.h
+ *
+ * Copyright (C) 2014 Max <max.suraev@fairwaves.co>
+ *
+ * All Rights Reserved
+ *
+ * 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 2 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, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+
+#pragma once
+
+/*! \brief load unaligned n-byte integer (little-endian encoding) into uint16_t
+ * \param[in] p Buffer where integer is stored
+ * \param[in] n Number of bytes stored in p
+ * \returns 16 bit unsigned integer
+ */
+static inline uint16_t osmo_load16le_ext(const void *p, uint8_t n)
+{
+ uint8_t i;
+ uint16_t r = 0;
+ const uint8_t *q = (uint8_t *)p;
+ for(i = 0; i < n; r |= ((uint16_t)q[i] << (8 * i)), i++);
+ return r;
+}
+
+/*! \brief load unaligned n-byte integer (big-endian encoding) into uint16_t
+ * \param[in] p Buffer where integer is stored
+ * \param[in] n Number of bytes stored in p
+ * \returns 16 bit unsigned integer
+ */
+static inline uint16_t osmo_load16be_ext(const void *p, uint8_t n)
+{
+ uint8_t i;
+ uint16_t r = 0;
+ const uint8_t *q = (uint8_t *)p;
+ for(i = 0; i < n; r |= ((uint16_t)q[i] << (16 - 8* (1 + i))), i++);
+ return r;
+}
+
+
+/*! \brief store unaligned n-byte integer (little-endian encoding) from uint16_t
+ * \param[in] x unsigned 16 bit integer
+ * \param[out] p Buffer to store integer
+ * \param[in] n Number of bytes to store
+ */
+static inline void osmo_store16le_ext(uint16_t x, void *p, uint8_t n)
+{
+ uint8_t i;
+ uint8_t *q = (uint8_t *)p;
+ for(i = 0; i < n; q[i] = (x >> i * 8) & 0xFF, i++);
+}
+
+/*! \brief store unaligned n-byte integer (big-endian encoding) from uint16_t
+ * \param[in] x unsigned 16 bit integer
+ * \param[out] p Buffer to store integer
+ * \param[in] n Number of bytes to store
+ */
+static inline void osmo_store16be_ext(uint16_t x, void *p, uint8_t n)
+{
+ uint8_t i;
+ uint8_t *q = (uint8_t *)p;
+ for(i = 0; i < n; q[i] = (x >> ((n - 1 - i) * 8)) & 0xFF, i++);
+}
+
+
+/* Convenience function for most-used cases */
+
+
+/*! \brief load unaligned 16-bit integer (little-endian encoding) */
+static inline uint16_t osmo_load16le(const void *p)
+{
+ return osmo_load16le_ext(p, 16 / 8);
+}
+
+/*! \brief load unaligned 16-bit integer (big-endian encoding) */
+static inline uint16_t osmo_load16be(const void *p)
+{
+ return osmo_load16be_ext(p, 16 / 8);
+}
+
+
+/*! \brief store unaligned 16-bit integer (little-endian encoding) */
+static inline void osmo_store16le(uint16_t x, void *p)
+{
+ osmo_store16le_ext(x, p, 16 / 8);
+}
+
+/*! \brief store unaligned 16-bit integer (big-endian encoding) */
+static inline void osmo_store16be(uint16_t x, void *p)
+{
+ osmo_store16be_ext(x, p, 16 / 8);
+}
diff --git a/lib/decoding/osmocom/core/bit32gen.h b/lib/decoding/osmocom/core/bit32gen.h
new file mode 100644
index 0000000..6640e76
--- /dev/null
+++ b/lib/decoding/osmocom/core/bit32gen.h
@@ -0,0 +1,105 @@
+/*
+ * bit32gen.h
+ *
+ * Copyright (C) 2014 Max <max.suraev@fairwaves.co>
+ *
+ * All Rights Reserved
+ *
+ * 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 2 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, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+
+#pragma once
+
+/*! \brief load unaligned n-byte integer (little-endian encoding) into uint32_t
+ * \param[in] p Buffer where integer is stored
+ * \param[in] n Number of bytes stored in p
+ * \returns 32 bit unsigned integer
+ */
+static inline uint32_t osmo_load32le_ext(const void *p, uint8_t n)
+{
+ uint8_t i;
+ uint32_t r = 0;
+ const uint8_t *q = (uint8_t *)p;
+ for(i = 0; i < n; r |= ((uint32_t)q[i] << (8 * i)), i++);
+ return r;
+}
+
+/*! \brief load unaligned n-byte integer (big-endian encoding) into uint32_t
+ * \param[in] p Buffer where integer is stored
+ * \param[in] n Number of bytes stored in p
+ * \returns 32 bit unsigned integer
+ */
+static inline uint32_t osmo_load32be_ext(const void *p, uint8_t n)
+{
+ uint8_t i;
+ uint32_t r = 0;
+ const uint8_t *q = (uint8_t *)p;
+ for(i = 0; i < n; r |= ((uint32_t)q[i] << (32 - 8* (1 + i))), i++);
+ return r;
+}
+
+
+/*! \brief store unaligned n-byte integer (little-endian encoding) from uint32_t
+ * \param[in] x unsigned 32 bit integer
+ * \param[out] p Buffer to store integer
+ * \param[in] n Number of bytes to store
+ */
+static inline void osmo_store32le_ext(uint32_t x, void *p, uint8_t n)
+{
+ uint8_t i;
+ uint8_t *q = (uint8_t *)p;
+ for(i = 0; i < n; q[i] = (x >> i * 8) & 0xFF, i++);
+}
+
+/*! \brief store unaligned n-byte integer (big-endian encoding) from uint32_t
+ * \param[in] x unsigned 32 bit integer
+ * \param[out] p Buffer to store integer
+ * \param[in] n Number of bytes to store
+ */
+static inline void osmo_store32be_ext(uint32_t x, void *p, uint8_t n)
+{
+ uint8_t i;
+ uint8_t *q = (uint8_t *)p;
+ for(i = 0; i < n; q[i] = (x >> ((n - 1 - i) * 8)) & 0xFF, i++);
+}
+
+
+/* Convenience function for most-used cases */
+
+
+/*! \brief load unaligned 32-bit integer (little-endian encoding) */
+static inline uint32_t osmo_load32le(const void *p)
+{
+ return osmo_load32le_ext(p, 32 / 8);
+}
+
+/*! \brief load unaligned 32-bit integer (big-endian encoding) */
+static inline uint32_t osmo_load32be(const void *p)
+{
+ return osmo_load32be_ext(p, 32 / 8);
+}
+
+
+/*! \brief store unaligned 32-bit integer (little-endian encoding) */
+static inline void osmo_store32le(uint32_t x, void *p)
+{
+ osmo_store32le_ext(x, p, 32 / 8);
+}
+
+/*! \brief store unaligned 32-bit integer (big-endian encoding) */
+static inline void osmo_store32be(uint32_t x, void *p)
+{
+ osmo_store32be_ext(x, p, 32 / 8);
+}
diff --git a/lib/decoding/osmocom/core/bit64gen.h b/lib/decoding/osmocom/core/bit64gen.h
new file mode 100644
index 0000000..8c7b709
--- /dev/null
+++ b/lib/decoding/osmocom/core/bit64gen.h
@@ -0,0 +1,105 @@
+/*
+ * bit64gen.h
+ *
+ * Copyright (C) 2014 Max <max.suraev@fairwaves.co>
+ *
+ * All Rights Reserved
+ *
+ * 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 2 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, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+
+#pragma once
+
+/*! \brief load unaligned n-byte integer (little-endian encoding) into uint64_t
+ * \param[in] p Buffer where integer is stored
+ * \param[in] n Number of bytes stored in p
+ * \returns 64 bit unsigned integer
+ */
+static inline uint64_t osmo_load64le_ext(const void *p, uint8_t n)
+{
+ uint8_t i;
+ uint64_t r = 0;
+ const uint8_t *q = (uint8_t *)p;
+ for(i = 0; i < n; r |= ((uint64_t)q[i] << (8 * i)), i++);
+ return r;
+}
+
+/*! \brief load unaligned n-byte integer (big-endian encoding) into uint64_t
+ * \param[in] p Buffer where integer is stored
+ * \param[in] n Number of bytes stored in p
+ * \returns 64 bit unsigned integer
+ */
+static inline uint64_t osmo_load64be_ext(const void *p, uint8_t n)
+{
+ uint8_t i;
+ uint64_t r = 0;
+ const uint8_t *q = (uint8_t *)p;
+ for(i = 0; i < n; r |= ((uint64_t)q[i] << (64 - 8* (1 + i))), i++);
+ return r;
+}
+
+
+/*! \brief store unaligned n-byte integer (little-endian encoding) from uint64_t
+ * \param[in] x unsigned 64 bit integer
+ * \param[out] p Buffer to store integer
+ * \param[in] n Number of bytes to store
+ */
+static inline void osmo_store64le_ext(uint64_t x, void *p, uint8_t n)
+{
+ uint8_t i;
+ uint8_t *q = (uint8_t *)p;
+ for(i = 0; i < n; q[i] = (x >> i * 8) & 0xFF, i++);
+}
+
+/*! \brief store unaligned n-byte integer (big-endian encoding) from uint64_t
+ * \param[in] x unsigned 64 bit integer
+ * \param[out] p Buffer to store integer
+ * \param[in] n Number of bytes to store
+ */
+static inline void osmo_store64be_ext(uint64_t x, void *p, uint8_t n)
+{
+ uint8_t i;
+ uint8_t *q = (uint8_t *)p;
+ for(i = 0; i < n; q[i] = (x >> ((n - 1 - i) * 8)) & 0xFF, i++);
+}
+
+
+/* Convenience function for most-used cases */
+
+
+/*! \brief load unaligned 64-bit integer (little-endian encoding) */
+static inline uint64_t osmo_load64le(const void *p)
+{
+ return osmo_load64le_ext(p, 64 / 8);
+}
+
+/*! \brief load unaligned 64-bit integer (big-endian encoding) */
+static inline uint64_t osmo_load64be(const void *p)
+{
+ return osmo_load64be_ext(p, 64 / 8);
+}
+
+
+/*! \brief store unaligned 64-bit integer (little-endian encoding) */
+static inline void osmo_store64le(uint64_t x, void *p)
+{
+ osmo_store64le_ext(x, p, 64 / 8);
+}
+
+/*! \brief store unaligned 64-bit integer (big-endian encoding) */
+static inline void osmo_store64be(uint64_t x, void *p)
+{
+ osmo_store64be_ext(x, p, 64 / 8);
+}
diff --git a/lib/decoding/osmocom/core/bits.c b/lib/decoding/osmocom/core/bits.c
new file mode 100644
index 0000000..8837c1f
--- /dev/null
+++ b/lib/decoding/osmocom/core/bits.c
@@ -0,0 +1,311 @@
+/*
+ * (C) 2011 by Harald Welte <laforge@gnumonks.org>
+ * (C) 2011 by Sylvain Munaut <tnt@246tNt.com>
+ *
+ * All Rights Reserved
+ *
+ * SPDX-License-Identifier: GPL-2.0+
+ *
+ * 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 2 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, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ */
+
+#include <stdint.h>
+
+#include <osmocom/core/bits.h>
+
+/*! \addtogroup bits
+ * @{
+ * Osmocom bit level support code.
+ *
+ * This module implements the notion of different bit-fields, such as
+ * - unpacked bits (\ref ubit_t), i.e. 1 bit per byte
+ * - packed bits (\ref pbit_t), i.e. 8 bits per byte
+ * - soft bits (\ref sbit_t), 1 bit per byte from -127 to 127
+ *
+ * \file bits.c */
+
+/*! convert unpacked bits to packed bits, return length in bytes
+ * \param[out] out output buffer of packed bits
+ * \param[in] in input buffer of unpacked bits
+ * \param[in] num_bits number of bits
+ */
+int osmo_ubit2pbit(pbit_t *out, const ubit_t *in, unsigned int num_bits)
+{
+ unsigned int i;
+ uint8_t curbyte = 0;
+ pbit_t *outptr = out;
+
+ for (i = 0; i < num_bits; i++) {
+ uint8_t bitnum = 7 - (i % 8);
+
+ curbyte |= (in[i] << bitnum);
+
+ if(i % 8 == 7){
+ *outptr++ = curbyte;
+ curbyte = 0;
+ }
+ }
+ /* we have a non-modulo-8 bitcount */
+ if (i % 8)
+ *outptr++ = curbyte;
+
+ return outptr - out;
+}
+
+/*! Shift unaligned input to octet-aligned output
+ * \param[out] out output buffer, unaligned
+ * \param[in] in input buffer, octet-aligned
+ * \param[in] num_nibbles number of nibbles
+ */
+void osmo_nibble_shift_right(uint8_t *out, const uint8_t *in,
+ unsigned int num_nibbles)
+{
+ unsigned int i, num_whole_bytes = num_nibbles / 2;
+ if (!num_whole_bytes)
+ return;
+
+ /* first byte: upper nibble empty, lower nibble from src */
+ out[0] = (in[0] >> 4);
+
+ /* bytes 1.. */
+ for (i = 1; i < num_whole_bytes; i++)
+ out[i] = ((in[i - 1] & 0xF) << 4) | (in[i] >> 4);
+
+ /* shift the last nibble, in case there's an odd count */
+ i = num_whole_bytes;
+ if (num_nibbles & 1)
+ out[i] = ((in[i - 1] & 0xF) << 4) | (in[i] >> 4);
+ else
+ out[i] = (in[i - 1] & 0xF) << 4;
+}
+
+/*! Shift unaligned input to octet-aligned output
+ * \param[out] out output buffer, octet-aligned
+ * \param[in] in input buffer, unaligned
+ * \param[in] num_nibbles number of nibbles
+ */
+void osmo_nibble_shift_left_unal(uint8_t *out, const uint8_t *in,
+ unsigned int num_nibbles)
+{
+ unsigned int i, num_whole_bytes = num_nibbles / 2;
+ if (!num_whole_bytes)
+ return;
+
+ for (i = 0; i < num_whole_bytes; i++)
+ out[i] = ((in[i] & 0xF) << 4) | (in[i + 1] >> 4);
+
+ /* shift the last nibble, in case there's an odd count */
+ i = num_whole_bytes;
+ if (num_nibbles & 1)
+ out[i] = (in[i] & 0xF) << 4;
+}
+
+/*! convert unpacked bits to soft bits
+ * \param[out] out output buffer of soft bits
+ * \param[in] in input buffer of unpacked bits
+ * \param[in] num_bits number of bits
+ */
+void osmo_ubit2sbit(sbit_t *out, const ubit_t *in, unsigned int num_bits)
+{
+ unsigned int i;
+ for (i = 0; i < num_bits; i++)
+ out[i] = in[i] ? -127 : 127;
+}
+
+/*! convert soft bits to unpacked bits
+ * \param[out] out output buffer of unpacked bits
+ * \param[in] in input buffer of soft bits
+ * \param[in] num_bits number of bits
+ */
+void osmo_sbit2ubit(ubit_t *out, const sbit_t *in, unsigned int num_bits)
+{
+ unsigned int i;
+ for (i = 0; i < num_bits; i++)
+ out[i] = in[i] < 0;
+}
+
+/*! convert packed bits to unpacked bits, return length in bytes
+ * \param[out] out output buffer of unpacked bits
+ * \param[in] in input buffer of packed bits
+ * \param[in] num_bits number of bits
+ * \return number of bytes used in \ref out
+ */
+int osmo_pbit2ubit(ubit_t *out, const pbit_t *in, unsigned int num_bits)
+{
+ unsigned int i;
+ ubit_t *cur = out;
+ ubit_t *limit = out + num_bits;
+
+ for (i = 0; i < (num_bits/8)+1; i++) {
+ pbit_t byte = in[i];
+ *cur++ = (byte >> 7) & 1;
+ if (cur >= limit)
+ break;
+ *cur++ = (byte >> 6) & 1;
+ if (cur >= limit)
+ break;
+ *cur++ = (byte >> 5) & 1;
+ if (cur >= limit)
+ break;
+ *cur++ = (byte >> 4) & 1;
+ if (cur >= limit)
+ break;
+ *cur++ = (byte >> 3) & 1;
+ if (cur >= limit)
+ break;
+ *cur++ = (byte >> 2) & 1;
+ if (cur >= limit)
+ break;
+ *cur++ = (byte >> 1) & 1;
+ if (cur >= limit)
+ break;
+ *cur++ = (byte >> 0) & 1;
+ if (cur >= limit)
+ break;
+ }
+ return cur - out;
+}
+
+/*! convert unpacked bits to packed bits (extended options)
+ * \param[out] out output buffer of packed bits
+ * \param[in] out_ofs offset into output buffer
+ * \param[in] in input buffer of unpacked bits
+ * \param[in] in_ofs offset into input buffer
+ * \param[in] num_bits number of bits
+ * \param[in] lsb_mode Encode bits in LSB orde instead of MSB
+ * \returns length in bytes (max written offset of output buffer + 1)
+ */
+int osmo_ubit2pbit_ext(pbit_t *out, unsigned int out_ofs,
+ const ubit_t *in, unsigned int in_ofs,
+ unsigned int num_bits, int lsb_mode)
+{
+ int i, op, bn;
+ for (i=0; i<num_bits; i++) {
+ op = out_ofs + i;
+ bn = lsb_mode ? (op&7) : (7-(op&7));
+ if (in[in_ofs+i])
+ out[op>>3] |= 1 << bn;
+ else
+ out[op>>3] &= ~(1 << bn);
+ }
+ return ((out_ofs + num_bits - 1) >> 3) + 1;
+}
+
+/*! convert packed bits to unpacked bits (extended options)
+ * \param[out] out output buffer of unpacked bits
+ * \param[in] out_ofs offset into output buffer
+ * \param[in] in input buffer of packed bits
+ * \param[in] in_ofs offset into input buffer
+ * \param[in] num_bits number of bits
+ * \param[in] lsb_mode Encode bits in LSB orde instead of MSB
+ * \returns length in bytes (max written offset of output buffer + 1)
+ */
+int osmo_pbit2ubit_ext(ubit_t *out, unsigned int out_ofs,
+ const pbit_t *in, unsigned int in_ofs,
+ unsigned int num_bits, int lsb_mode)
+{
+ int i, ip, bn;
+ for (i=0; i<num_bits; i++) {
+ ip = in_ofs + i;
+ bn = lsb_mode ? (ip&7) : (7-(ip&7));
+ out[out_ofs+i] = !!(in[ip>>3] & (1<<bn));
+ }
+ return out_ofs + num_bits;
+}
+
+/*! generalized bit reversal function
+ * \param[in] x the 32bit value to be reversed
+ * \param[in] k the type of reversal requested
+ * \returns the reversed 32bit dword
+ *
+ * This function reverses the bit order within a 32bit word. Depending
+ * on "k", it either reverses all bits in a 32bit dword, or the bytes in
+ * the dword, or the bits in each byte of a dword, or simply swaps the
+ * two 16bit words in a dword. See Chapter 7 "Hackers Delight"
+ */
+uint32_t osmo_bit_reversal(uint32_t x, enum osmo_br_mode k)
+{
+ if (k & 1) x = (x & 0x55555555) << 1 | (x & 0xAAAAAAAA) >> 1;
+ if (k & 2) x = (x & 0x33333333) << 2 | (x & 0xCCCCCCCC) >> 2;
+ if (k & 4) x = (x & 0x0F0F0F0F) << 4 | (x & 0xF0F0F0F0) >> 4;
+ if (k & 8) x = (x & 0x00FF00FF) << 8 | (x & 0xFF00FF00) >> 8;
+ if (k & 16) x = (x & 0x0000FFFF) << 16 | (x & 0xFFFF0000) >> 16;
+
+ return x;
+}
+
+/*! reverse the bit-order in each byte of a dword
+ * \param[in] x 32bit input value
+ * \returns 32bit value where bits of each byte have been reversed
+ *
+ * See Chapter 7 "Hackers Delight"
+ */
+uint32_t osmo_revbytebits_32(uint32_t x)
+{
+ x = (x & 0x55555555) << 1 | (x & 0xAAAAAAAA) >> 1;
+ x = (x & 0x33333333) << 2 | (x & 0xCCCCCCCC) >> 2;
+ x = (x & 0x0F0F0F0F) << 4 | (x & 0xF0F0F0F0) >> 4;
+
+ return x;
+}
+
+/*! reverse the bit order in a byte
+ * \param[in] x 8bit input value
+ * \returns 8bit value where bits order has been reversed
+ *
+ * See Chapter 7 "Hackers Delight"
+ */
+uint32_t osmo_revbytebits_8(uint8_t x)
+{
+ x = (x & 0x55) << 1 | (x & 0xAA) >> 1;
+ x = (x & 0x33) << 2 | (x & 0xCC) >> 2;
+ x = (x & 0x0F) << 4 | (x & 0xF0) >> 4;
+
+ return x;
+}
+
+/*! reverse bit-order of each byte in a buffer
+ * \param[in] buf buffer containing bytes to be bit-reversed
+ * \param[in] len length of buffer in bytes
+ *
+ * This function reverses the bits in each byte of the buffer
+ */
+void osmo_revbytebits_buf(uint8_t *buf, int len)
+{
+ unsigned int i;
+ unsigned int unaligned_cnt;
+ int len_remain = len;
+
+ unaligned_cnt = ((unsigned long)buf & 3);
+ for (i = 0; i < unaligned_cnt; i++) {
+ buf[i] = osmo_revbytebits_8(buf[i]);
+ len_remain--;
+ if (len_remain <= 0)
+ return;
+ }
+
+ for (i = unaligned_cnt; i + 3 < len; i += 4) {
+ osmo_store32be(osmo_revbytebits_32(osmo_load32be(buf + i)), buf + i);
+ len_remain -= 4;
+ }
+
+ for (i = len - len_remain; i < len; i++) {
+ buf[i] = osmo_revbytebits_8(buf[i]);
+ len_remain--;
+ }
+}
+
+/*! @} */
diff --git a/lib/decoding/osmocom/core/bits.h b/lib/decoding/osmocom/core/bits.h
new file mode 100644
index 0000000..b1b8040
--- /dev/null
+++ b/lib/decoding/osmocom/core/bits.h
@@ -0,0 +1,122 @@
+/*! \file bits.h
+ * Osmocom bit level support code.
+ *
+ */
+
+#pragma once
+
+#include <stdint.h>
+#include <stddef.h>
+
+#include <osmocom/core/bit16gen.h>
+#include <osmocom/core/bit32gen.h>
+#include <osmocom/core/bit64gen.h>
+
+/*! \defgroup bits soft, unpacked and packed bits
+ * @{
+ * \file bits.h */
+
+/*! soft bit with value (-127...127), as commonly used in
+ * communications receivers such as [viterbi] decoders */
+typedef int8_t sbit_t;
+
+/*! unpacked bit (0 or 1): 1 bit per byte */
+typedef uint8_t ubit_t;
+
+/*! packed bits (8 bits in a byte).
+ * NOTE on the endian-ness of \ref pbit_t:
+ * - Bits in a \ref pbit_t are ordered MSB first, i.e. 0x80 is the first bit.
+ * - Bit i in a \ref pbit_t array is array[i/8] & (1<<(7-i%8)) */
+typedef uint8_t pbit_t;
+
+/*! determine how many bytes we would need for \a num_bits packed bits
+ * \param[in] num_bits Number of packed bits
+ * \returns number of bytes needed for \a num_bits packed bits
+ */
+static inline unsigned int osmo_pbit_bytesize(unsigned int num_bits)
+{
+ unsigned int pbit_bytesize = num_bits / 8;
+
+ if (num_bits % 8)
+ pbit_bytesize++;
+
+ return pbit_bytesize;
+}
+
+int osmo_ubit2pbit(pbit_t *out, const ubit_t *in, unsigned int num_bits);
+
+int osmo_pbit2ubit(ubit_t *out, const pbit_t *in, unsigned int num_bits);
+
+void osmo_nibble_shift_right(uint8_t *out, const uint8_t *in,
+ unsigned int num_nibbles);
+void osmo_nibble_shift_left_unal(uint8_t *out, const uint8_t *in,
+ unsigned int num_nibbles);
+
+void osmo_ubit2sbit(sbit_t *out, const ubit_t *in, unsigned int num_bits);
+void osmo_sbit2ubit(ubit_t *out, const sbit_t *in, unsigned int num_bits);
+
+int osmo_ubit2pbit_ext(pbit_t *out, unsigned int out_ofs,
+ const ubit_t *in, unsigned int in_ofs,
+ unsigned int num_bits, int lsb_mode);
+
+int osmo_pbit2ubit_ext(ubit_t *out, unsigned int out_ofs,
+ const pbit_t *in, unsigned int in_ofs,
+ unsigned int num_bits, int lsb_mode);
+
+#define OSMO_BIN_SPEC "%d%d%d%d%d%d%d%d"
+#define OSMO_BIN_PRINT(byte) \
+ (byte & 0x80 ? 1 : 0), \
+ (byte & 0x40 ? 1 : 0), \
+ (byte & 0x20 ? 1 : 0), \
+ (byte & 0x10 ? 1 : 0), \
+ (byte & 0x08 ? 1 : 0), \
+ (byte & 0x04 ? 1 : 0), \
+ (byte & 0x02 ? 1 : 0), \
+ (byte & 0x01 ? 1 : 0)
+
+#define OSMO_BIT_SPEC "%c%c%c%c%c%c%c%c"
+#define OSMO_BIT_PRINT_EX(byte, ch) \
+ (byte & 0x80 ? ch : '.'), \
+ (byte & 0x40 ? ch : '.'), \
+ (byte & 0x20 ? ch : '.'), \
+ (byte & 0x10 ? ch : '.'), \
+ (byte & 0x08 ? ch : '.'), \
+ (byte & 0x04 ? ch : '.'), \
+ (byte & 0x02 ? ch : '.'), \
+ (byte & 0x01 ? ch : '.')
+
+#define OSMO_BIT_PRINT(byte) OSMO_BIT_PRINT_EX(byte, '1')
+
+/* BIT REVERSAL */
+
+/*! bit-reversal mode for osmo_bit_reversal() */
+enum osmo_br_mode {
+ /*! reverse all bits in a 32bit dword */
+ OSMO_BR_BITS_IN_DWORD = 31,
+ /*! reverse byte order in a 32bit dword */
+ OSMO_BR_BYTES_IN_DWORD = 24,
+ /*! reverse bits of each byte in a 32bit dword */
+ OSMO_BR_BITS_IN_BYTE = 7,
+ /*! swap the two 16bit words in a 32bit dword */
+ OSMO_BR_WORD_SWAP = 16,
+};
+
+uint32_t osmo_bit_reversal(uint32_t x, enum osmo_br_mode k);
+
+uint32_t osmo_revbytebits_32(uint32_t x);
+
+uint32_t osmo_revbytebits_8(uint8_t x);
+
+void osmo_revbytebits_buf(uint8_t *buf, int len);
+
+/*! left circular shift
+ * \param[in] in The 16 bit unsigned integer to be rotated
+ * \param[in] shift Number of bits to shift \a in to, [0;16] bits
+ * \returns shifted value
+ */
+static inline uint16_t osmo_rol16(uint16_t in, unsigned shift)
+{
+ return (in << shift) | (in >> (16 - shift));
+}
+
+/*! @} */
diff --git a/lib/decoding/osmocom/core/bitvec.c b/lib/decoding/osmocom/core/bitvec.c
new file mode 100644
index 0000000..414c719
--- /dev/null
+++ b/lib/decoding/osmocom/core/bitvec.c
@@ -0,0 +1,708 @@
+/* (C) 2009 by Harald Welte <laforge@gnumonks.org>
+ * (C) 2012 Ivan Klyuchnikov
+ * (C) 2015 by sysmocom - s.f.m.c. GmbH
+ *
+ * All Rights Reserved
+ *
+ * SPDX-License-Identifier: GPL-2.0+
+ *
+ * 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 2 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, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ */
+
+/*! \addtogroup bitvec
+ * @{
+ * Osmocom bit vector abstraction utility routines.
+ *
+ * These functions assume a MSB (most significant bit) first layout of the
+ * bits, so that for instance the 5 bit number abcde (a is MSB) can be
+ * embedded into a byte sequence like in xxxxxxab cdexxxxx. The bit count
+ * starts with the MSB, so the bits in a byte are numbered (MSB) 01234567 (LSB).
+ * Note that there are other incompatible encodings, like it is used
+ * for the EGPRS RLC data block headers (there the bits are numbered from LSB
+ * to MSB).
+ *
+ * \file bitvec.c */
+
+#include <errno.h>
+#include <stdint.h>
+#include <string.h>
+#include <stdio.h>
+#include <stdbool.h>
+
+#include <osmocom/core/bits.h>
+#include <osmocom/core/bitvec.h>
+
+#define BITNUM_FROM_COMP(byte, bit) ((byte*8)+bit)
+
+static inline unsigned int bytenum_from_bitnum(unsigned int bitnum)
+{
+ unsigned int bytenum = bitnum / 8;
+
+ return bytenum;
+}
+
+/* convert ZERO/ONE/L/H to a bitmask at given pos in a byte */
+static uint8_t bitval2mask(enum bit_value bit, uint8_t bitnum)
+{
+ int bitval;
+
+ switch (bit) {
+ case ZERO:
+ bitval = (0 << bitnum);
+ break;
+ case ONE:
+ bitval = (1 << bitnum);
+ break;
+ case L:
+ bitval = ((0x2b ^ (0 << bitnum)) & (1 << bitnum));
+ break;
+ case H:
+ bitval = ((0x2b ^ (1 << bitnum)) & (1 << bitnum));
+ break;
+ default:
+ return 0;
+ }
+ return bitval;
+}
+
+/*! check if the bit is 0 or 1 for a given position inside a bitvec
+ * \param[in] bv the bit vector on which to check
+ * \param[in] bitnr the bit number inside the bit vector to check
+ * \return value of the requested bit
+ */
+enum bit_value bitvec_get_bit_pos(const struct bitvec *bv, unsigned int bitnr)
+{
+ unsigned int bytenum = bytenum_from_bitnum(bitnr);
+ unsigned int bitnum = 7 - (bitnr % 8);
+ uint8_t bitval;
+
+ if (bytenum >= bv->data_len)
+ return -EINVAL;
+
+ bitval = bitval2mask(ONE, bitnum);
+
+ if (bv->data[bytenum] & bitval)
+ return ONE;
+
+ return ZERO;
+}
+
+/*! check if the bit is L or H for a given position inside a bitvec
+ * \param[in] bv the bit vector on which to check
+ * \param[in] bitnr the bit number inside the bit vector to check
+ * \return value of the requested bit
+ */
+enum bit_value bitvec_get_bit_pos_high(const struct bitvec *bv,
+ unsigned int bitnr)
+{
+ unsigned int bytenum = bytenum_from_bitnum(bitnr);
+ unsigned int bitnum = 7 - (bitnr % 8);
+ uint8_t bitval;
+
+ if (bytenum >= bv->data_len)
+ return -EINVAL;
+
+ bitval = bitval2mask(H, bitnum);
+
+ if ((bv->data[bytenum] & (1 << bitnum)) == bitval)
+ return H;
+
+ return L;
+}
+
+/*! get the Nth set bit inside the bit vector
+ * \param[in] bv the bit vector to use
+ * \param[in] n the bit number to get
+ * \returns the bit number (offset) of the Nth set bit in \a bv
+ */
+unsigned int bitvec_get_nth_set_bit(const struct bitvec *bv, unsigned int n)
+{
+ unsigned int i, k = 0;
+
+ for (i = 0; i < bv->data_len*8; i++) {
+ if (bitvec_get_bit_pos(bv, i) == ONE) {
+ k++;
+ if (k == n)
+ return i;
+ }
+ }
+
+ return 0;
+}
+
+/*! set a bit at given position in a bit vector
+ * \param[in] bv bit vector on which to operate
+ * \param[in] bitnr number of bit to be set
+ * \param[in] bit value to which the bit is to be set
+ * \returns 0 on success, negative value on error
+ */
+inline int bitvec_set_bit_pos(struct bitvec *bv, unsigned int bitnr,
+ enum bit_value bit)
+{
+ unsigned int bytenum = bytenum_from_bitnum(bitnr);
+ unsigned int bitnum = 7 - (bitnr % 8);
+ uint8_t bitval;
+
+ if (bytenum >= bv->data_len)
+ return -EINVAL;
+
+ /* first clear the bit */
+ bitval = bitval2mask(ONE, bitnum);
+ bv->data[bytenum] &= ~bitval;
+
+ /* then set it to desired value */
+ bitval = bitval2mask(bit, bitnum);
+ bv->data[bytenum] |= bitval;
+
+ return 0;
+}
+
+/*! set the next bit inside a bitvec
+ * \param[in] bv bit vector to be used
+ * \param[in] bit value of the bit to be set
+ * \returns 0 on success, negative value on error
+ */
+inline int bitvec_set_bit(struct bitvec *bv, enum bit_value bit)
+{
+ int rc;
+
+ rc = bitvec_set_bit_pos(bv, bv->cur_bit, bit);
+ if (!rc)
+ bv->cur_bit++;
+
+ return rc;
+}
+
+/*! get the next bit (low/high) inside a bitvec
+ * \return value of th next bit in the vector */
+int bitvec_get_bit_high(struct bitvec *bv)
+{
+ int rc;
+
+ rc = bitvec_get_bit_pos_high(bv, bv->cur_bit);
+ if (rc >= 0)
+ bv->cur_bit++;
+
+ return rc;
+}
+
+/*! set multiple bits (based on array of bitvals) at current pos
+ * \param[in] bv bit vector
+ * \param[in] bits array of \ref bit_value
+ * \param[in] count number of bits to set
+ * \return 0 on success; negative in case of error */
+int bitvec_set_bits(struct bitvec *bv, const enum bit_value *bits, unsigned int count)
+{
+ int i, rc;
+
+ for (i = 0; i < count; i++) {
+ rc = bitvec_set_bit(bv, bits[i]);
+ if (rc)
+ return rc;
+ }
+
+ return 0;
+}
+
+/*! set multiple bits (based on numeric value) at current pos.
+ * \param[in] bv bit vector.
+ * \param[in] v mask representing which bits needs to be set.
+ * \param[in] num_bits number of meaningful bits in the mask.
+ * \param[in] use_lh whether to interpret the bits as L/H values or as 0/1.
+ * \return 0 on success; negative in case of error. */
+int bitvec_set_u64(struct bitvec *bv, uint64_t v, uint8_t num_bits, bool use_lh)
+{
+ uint8_t i;
+
+ if (num_bits > 64)
+ return -E2BIG;
+
+ for (i = 0; i < num_bits; i++) {
+ int rc;
+ enum bit_value bit = use_lh ? L : 0;
+
+ if (v & ((uint64_t)1 << (num_bits - i - 1)))
+ bit = use_lh ? H : 1;
+
+ rc = bitvec_set_bit(bv, bit);
+ if (rc != 0)
+ return rc;
+ }
+
+ return 0;
+}
+
+/*! set multiple bits (based on numeric value) at current pos.
+ * \return 0 in case of success; negative in case of error. */
+int bitvec_set_uint(struct bitvec *bv, unsigned int ui, unsigned int num_bits)
+{
+ return bitvec_set_u64(bv, ui, num_bits, false);
+}
+
+/*! get multiple bits (num_bits) from beginning of vector (MSB side)
+ * \return 16bit signed integer retrieved from bit vector */
+int16_t bitvec_get_int16_msb(const struct bitvec *bv, unsigned int num_bits)
+{
+ if (num_bits > 15 || bv->cur_bit < num_bits)
+ return -EINVAL;
+
+ if (num_bits < 9)
+ return bv->data[0] >> (8 - num_bits);
+
+ return osmo_load16be(bv->data) >> (16 - num_bits);
+}
+
+/*! get multiple bits (based on numeric value) from current pos
+ * \return integer value retrieved from bit vector */
+int bitvec_get_uint(struct bitvec *bv, unsigned int num_bits)
+{
+ int i;
+ unsigned int ui = 0;
+
+ for (i = 0; i < num_bits; i++) {
+ int bit = bitvec_get_bit_pos(bv, bv->cur_bit);
+ if (bit < 0)
+ return bit;
+ if (bit)
+ ui |= (1 << (num_bits - i - 1));
+ bv->cur_bit++;
+ }
+
+ return ui;
+}
+
+/*! fill num_bits with \fill starting from the current position
+ * \return 0 on success; negative otherwise (out of vector boundary)
+ */
+int bitvec_fill(struct bitvec *bv, unsigned int num_bits, enum bit_value fill)
+{
+ unsigned i, stop = bv->cur_bit + num_bits;
+ for (i = bv->cur_bit; i < stop; i++)
+ if (bitvec_set_bit(bv, fill) < 0)
+ return -EINVAL;
+
+ return 0;
+}
+
+/*! pad all remaining bits up to num_bits
+ * \return 0 on success; negative otherwise */
+int bitvec_spare_padding(struct bitvec *bv, unsigned int up_to_bit)
+{
+ int n = up_to_bit - bv->cur_bit + 1;
+ if (n < 1)
+ return 0;
+
+ return bitvec_fill(bv, n, L);
+}
+
+/*! find first bit set in bit vector
+ * \return 0 on success; negative otherwise */
+int bitvec_find_bit_pos(const struct bitvec *bv, unsigned int n,
+ enum bit_value val)
+{
+ unsigned int i;
+
+ for (i = n; i < bv->data_len*8; i++) {
+ if (bitvec_get_bit_pos(bv, i) == val)
+ return i;
+ }
+
+ return -1;
+}
+
+/*! get multiple bytes from current pos
+ * Assumes MSB first encoding.
+ * \param[in] bv bit vector
+ * \param[in] bytes array
+ * \param[in] count number of bytes to copy
+ * \return 0 on success; negative otherwise
+ */
+int bitvec_get_bytes(struct bitvec *bv, uint8_t *bytes, unsigned int count)
+{
+ int byte_offs = bytenum_from_bitnum(bv->cur_bit);
+ int bit_offs = bv->cur_bit % 8;
+ uint8_t c, last_c;
+ int i;
+ uint8_t *src;
+
+ if (byte_offs + count + (bit_offs ? 1 : 0) > bv->data_len)
+ return -EINVAL;
+
+ if (bit_offs == 0) {
+ memcpy(bytes, bv->data + byte_offs, count);
+ } else {
+ src = bv->data + byte_offs;
+ last_c = *(src++);
+ for (i = count; i > 0; i--) {
+ c = *(src++);
+ *(bytes++) =
+ (last_c << bit_offs) |
+ (c >> (8 - bit_offs));
+ last_c = c;
+ }
+ }
+
+ bv->cur_bit += count * 8;
+ return 0;
+}
+
+/*! set multiple bytes at current pos
+ * Assumes MSB first encoding.
+ * \param[in] bv bit vector
+ * \param[in] bytes array
+ * \param[in] count number of bytes to copy
+ * \return 0 on success; negative otherwise
+ */
+int bitvec_set_bytes(struct bitvec *bv, const uint8_t *bytes, unsigned int count)
+{
+ int byte_offs = bytenum_from_bitnum(bv->cur_bit);
+ int bit_offs = bv->cur_bit % 8;
+ uint8_t c, last_c;
+ int i;
+ uint8_t *dst;
+
+ if (byte_offs + count + (bit_offs ? 1 : 0) > bv->data_len)
+ return -EINVAL;
+
+ if (bit_offs == 0) {
+ memcpy(bv->data + byte_offs, bytes, count);
+ } else if (count > 0) {
+ dst = bv->data + byte_offs;
+ /* Get lower bits of first dst byte */
+ last_c = *dst >> (8 - bit_offs);
+ for (i = count; i > 0; i--) {
+ c = *(bytes++);
+ *(dst++) =
+ (last_c << (8 - bit_offs)) |
+ (c >> bit_offs);
+ last_c = c;
+ }
+ /* Overwrite lower bits of N+1 dst byte */
+ *dst = (*dst & ((1 << (8 - bit_offs)) - 1)) |
+ (last_c << (8 - bit_offs));
+ }
+
+ bv->cur_bit += count * 8;
+ return 0;
+}
+
+/*! Allocate a bit vector
+ * \param[in] size Number of bits in the vector
+ * \param[in] ctx Context from which to allocate
+ * \return pointer to allocated vector; NULL in case of error /
+struct bitvec *bitvec_alloc(unsigned int size, TALLOC_CTX *ctx)
+{
+ struct bitvec *bv = talloc_zero(ctx, struct bitvec);
+ if (!bv)
+ return NULL;
+
+ bv->data = talloc_zero_array(bv, uint8_t, size);
+ if (!(bv->data)) {
+ talloc_free(bv);
+ return NULL;
+ }
+
+ bv->data_len = size;
+ bv->cur_bit = 0;
+ return bv;
+}
+
+/*! Free a bit vector (release its memory)
+ * \param[in] bit vector to free *
+void bitvec_free(struct bitvec *bv)
+{
+ talloc_free(bv->data);
+ talloc_free(bv);
+}
+*/
+/*! Export a bit vector to a buffer
+ * \param[in] bitvec (unpacked bits)
+ * \param[out] buffer for the unpacked bits
+ * \return number of bytes (= bits) copied */
+unsigned int bitvec_pack(const struct bitvec *bv, uint8_t *buffer)
+{
+ unsigned int i = 0;
+ for (i = 0; i < bv->data_len; i++)
+ buffer[i] = bv->data[i];
+
+ return i;
+}
+
+/*! Copy buffer of unpacked bits into bit vector
+ * \param[in] buffer unpacked input bits
+ * \param[out] bv unpacked bit vector
+ * \return number of bytes (= bits) copied */
+unsigned int bitvec_unpack(struct bitvec *bv, const uint8_t *buffer)
+{
+ unsigned int i = 0;
+ for (i = 0; i < bv->data_len; i++)
+ bv->data[i] = buffer[i];
+
+ return i;
+}
+
+/*! read hexadecimap string into a bit vector
+ * \param[in] src string containing hex digits
+ * \param[out] bv unpacked bit vector
+ * \return 0 in case of success; 1 in case of error
+ */
+int bitvec_unhex(struct bitvec *bv, const char *src)
+{
+ unsigned i;
+ unsigned val;
+ unsigned write_index = 0;
+ unsigned digits = bv->data_len * 2;
+
+ for (i = 0; i < digits; i++) {
+ if (sscanf(src + i, "%1x", &val) < 1) {
+ return 1;
+ }
+ bitvec_write_field(bv, &write_index, val, 4);
+ }
+ return 0;
+}
+
+/*! read part of the vector
+ * \param[in] bv The boolean vector to work on
+ * \param[in,out] read_index Where reading supposed to start in the vector
+ * \param[in] len How many bits to read from vector
+ * \returns read bits or negative value on error
+ */
+uint64_t bitvec_read_field(struct bitvec *bv, unsigned int *read_index, unsigned int len)
+{
+ unsigned int i;
+ uint64_t ui = 0;
+ bv->cur_bit = *read_index;
+
+ for (i = 0; i < len; i++) {
+ int bit = bitvec_get_bit_pos((const struct bitvec *)bv, bv->cur_bit);
+ if (bit < 0)
+ return bit;
+ if (bit)
+ ui |= ((uint64_t)1 << (len - i - 1));
+ bv->cur_bit++;
+ }
+ *read_index += len;
+ return ui;
+}
+
+/*! write into the vector
+ * \param[in] bv The boolean vector to work on
+ * \param[in,out] write_index Where writing supposed to start in the vector
+ * \param[in] len How many bits to write
+ * \returns next write index or negative value on error
+ */
+int bitvec_write_field(struct bitvec *bv, unsigned int *write_index, uint64_t val, unsigned int len)
+{
+ int rc;
+
+ bv->cur_bit = *write_index;
+
+ rc = bitvec_set_u64(bv, val, len, false);
+ if (rc != 0)
+ return rc;
+
+ *write_index += len;
+
+ return 0;
+}
+
+/*! convert enum to corresponding character
+ * \param v input value (bit)
+ * \return single character, either 0, 1, L or H */
+char bit_value_to_char(enum bit_value v)
+{
+ switch (v) {
+ case ZERO: return '0';
+ case ONE: return '1';
+ case L: return 'L';
+ case H: return 'H';
+ default: abort();
+ }
+}
+
+/*! prints bit vector to provided string
+ * It's caller's responsibility to ensure that we won't shoot him in the foot:
+ * the provided buffer should be at lest cur_bit + 1 bytes long
+ */
+void bitvec_to_string_r(const struct bitvec *bv, char *str)
+{
+ unsigned i, pos = 0;
+ char *cur = str;
+ for (i = 0; i < bv->cur_bit; i++) {
+ if (0 == i % 8)
+ *cur++ = ' ';
+ *cur++ = bit_value_to_char(bitvec_get_bit_pos(bv, i));
+ pos++;
+ }
+ *cur = 0;
+}
+
+/* we assume that x have at least 1 non-b bit */
+static inline unsigned leading_bits(uint8_t x, bool b)
+{
+ if (b) {
+ if (x < 0x80) return 0;
+ if (x < 0xC0) return 1;
+ if (x < 0xE0) return 2;
+ if (x < 0xF0) return 3;
+ if (x < 0xF8) return 4;
+ if (x < 0xFC) return 5;
+ if (x < 0xFE) return 6;
+ } else {
+ if (x > 0x7F) return 0;
+ if (x > 0x3F) return 1;
+ if (x > 0x1F) return 2;
+ if (x > 0xF) return 3;
+ if (x > 7) return 4;
+ if (x > 3) return 5;
+ if (x > 1) return 6;
+ }
+ return 7;
+}
+/*! force bit vector to all 0 and current bit to the beginnig of the vector */
+void bitvec_zero(struct bitvec *bv)
+{
+ bv->cur_bit = 0;
+ memset(bv->data, 0, bv->data_len);
+}
+
+/*! Return number (bits) of uninterrupted bit run in vector starting from the MSB
+ * \param[in] bv The boolean vector to work on
+ * \param[in] b The boolean, sequence of which is looked at from the vector start
+ * \returns Number of consecutive bits of \p b in \p bv
+ */
+unsigned bitvec_rl(const struct bitvec *bv, bool b)
+{
+ unsigned i;
+ for (i = 0; i < (bv->cur_bit % 8 ? bv->cur_bit / 8 + 1 : bv->cur_bit / 8); i++) {
+ if ( (b ? 0xFF : 0) != bv->data[i])
+ return i * 8 + leading_bits(bv->data[i], b);
+ }
+
+ return bv->cur_bit;
+}
+
+/*! Return number (bits) of uninterrupted bit run in vector
+ * starting from the current bit
+ * \param[in] bv The boolean vector to work on
+ * \param[in] b The boolean, sequence of 1's or 0's to be checked
+ * \param[in] max_bits Total Number of Uncmopresed bits
+ * \returns Number of consecutive bits of \p b in \p bv and cur_bit will
+ * \go to cur_bit + number of consecutive bit
+ */
+unsigned bitvec_rl_curbit(struct bitvec *bv, bool b, int max_bits)
+{
+ unsigned i = 0;
+ unsigned j = 8;
+ int temp_res = 0;
+ int count = 0;
+ unsigned readIndex = bv->cur_bit;
+ unsigned remaining_bits = max_bits % 8;
+ unsigned remaining_bytes = max_bits / 8;
+ unsigned byte_mask = 0xFF;
+
+ if (readIndex % 8) {
+ for (j -= (readIndex % 8) ; j > 0 ; j--) {
+ if (readIndex < max_bits && bitvec_read_field(bv, &readIndex, 1) == b)
+ temp_res++;
+ else {
+ bv->cur_bit--;
+ return temp_res;
+ }
+ }
+ }
+ for (i = (readIndex / 8);
+ i < (remaining_bits ? remaining_bytes + 1 : remaining_bytes);
+ i++, count++) {
+ if ((b ? byte_mask : 0) != bv->data[i]) {
+ bv->cur_bit = (count * 8 +
+ leading_bits(bv->data[i], b) + readIndex);
+ return count * 8 +
+ leading_bits(bv->data[i], b) + temp_res;
+ }
+ }
+ bv->cur_bit = (temp_res + (count * 8)) + readIndex;
+ if (bv->cur_bit > max_bits)
+ bv->cur_bit = max_bits;
+ return (bv->cur_bit - readIndex + temp_res);
+}
+
+/*! Shifts bitvec to the left, n MSB bits lost */
+void bitvec_shiftl(struct bitvec *bv, unsigned n)
+{
+ if (0 == n)
+ return;
+ if (n >= bv->cur_bit) {
+ bitvec_zero(bv);
+ return;
+ }
+
+ memmove(bv->data, bv->data + n / 8, bv->data_len - n / 8);
+
+ uint8_t tmp[2];
+ unsigned i;
+ for (i = 0; i < bv->data_len - 2; i++) {
+ uint16_t t = osmo_load16be(bv->data + i);
+ osmo_store16be(t << (n % 8), &tmp);
+ bv->data[i] = tmp[0];
+ }
+
+ bv->data[bv->data_len - 1] <<= (n % 8);
+ bv->cur_bit -= n;
+}
+
+/*! Add given array to bitvec
+ * \param[in,out] bv bit vector to work with
+ * \param[in] array elements to be added
+ * \param[in] array_len length of array
+ * \param[in] dry_run indicates whether to return number of bits required
+ * instead of adding anything to bv for real
+ * \param[in] num_bits number of bits to consider in each element of array
+ * \returns number of bits necessary to add array elements if dry_run is true,
+ * 0 otherwise (only in this case bv is actually changed)
+ *
+ * N. B: no length checks are performed on bv - it's caller's job to ensure
+ * enough space is available - for example by calling with dry_run = true first.
+ *
+ * Useful for common pattern in CSN.1 spec which looks like:
+ * { 1 < XXX : bit (num_bits) > } ** 0
+ * which means repeat any times (between 0 and infinity),
+ * start each repetition with 1, mark end of repetitions with 0 bit
+ * see app. note in 3GPP TS 24.007 § B.2.1 Rule A2
+ */
+unsigned int bitvec_add_array(struct bitvec *bv, const uint32_t *array,
+ unsigned int array_len, bool dry_run,
+ unsigned int num_bits)
+{
+ unsigned i, bits = 1; /* account for stop bit */
+ for (i = 0; i < array_len; i++) {
+ if (dry_run) {
+ bits += (1 + num_bits);
+ } else {
+ bitvec_set_bit(bv, 1);
+ bitvec_set_uint(bv, array[i], num_bits);
+ }
+ }
+
+ if (dry_run)
+ return bits;
+
+ bitvec_set_bit(bv, 0); /* stop bit - end of the sequence */
+ return 0;
+}
+
+/*! @} */
diff --git a/lib/decoding/osmocom/core/bitvec.h b/lib/decoding/osmocom/core/bitvec.h
new file mode 100644
index 0000000..84db9a5
--- /dev/null
+++ b/lib/decoding/osmocom/core/bitvec.h
@@ -0,0 +1,87 @@
+/* (C) 2009 by Harald Welte <laforge@gnumonks.org>
+ * (C) 2012 Ivan Klyuchnikov
+ * (C) 2015 sysmocom - s.f.m.c. GmbH
+ *
+ * All Rights Reserved
+ *
+ * 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 2 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, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ */
+
+#pragma once
+
+/*! \defgroup bitvec Bit vectors
+ * @{
+ * \file bitvec.h */
+
+#include <stdint.h>
+//#include <osmocom/core/talloc.h>
+#include <osmocom/core/defs.h>
+#include <stdbool.h>
+
+/*! A single GSM bit
+ *
+ * In GSM mac blocks, every bit can be 0 or 1, or L or H. L/H are
+ * defined relative to the 0x2b padding pattern */
+enum bit_value {
+ ZERO = 0, /*!< A zero (0) bit */
+ ONE = 1, /*!< A one (1) bit */
+ L = 2, /*!< A CSN.1 "L" bit */
+ H = 3, /*!< A CSN.1 "H" bit */
+};
+
+/*! structure describing a bit vector */
+struct bitvec {
+ unsigned int cur_bit; /*!< cursor to the next unused bit */
+ unsigned int data_len; /*!< length of data array in bytes */
+ uint8_t *data; /*!< pointer to data array */
+};
+
+enum bit_value bitvec_get_bit_pos(const struct bitvec *bv, unsigned int bitnr);
+enum bit_value bitvec_get_bit_pos_high(const struct bitvec *bv,
+ unsigned int bitnr);
+unsigned int bitvec_get_nth_set_bit(const struct bitvec *bv, unsigned int n);
+int bitvec_set_bit_pos(struct bitvec *bv, unsigned int bitnum,
+ enum bit_value bit);
+int bitvec_set_bit(struct bitvec *bv, enum bit_value bit);
+int bitvec_get_bit_high(struct bitvec *bv);
+int bitvec_set_bits(struct bitvec *bv, const enum bit_value *bits, unsigned int count);
+int bitvec_set_u64(struct bitvec *bv, uint64_t v, uint8_t num_bits, bool use_lh);
+int bitvec_set_uint(struct bitvec *bv, unsigned int in, unsigned int count);
+int bitvec_get_uint(struct bitvec *bv, unsigned int num_bits);
+int bitvec_find_bit_pos(const struct bitvec *bv, unsigned int n, enum bit_value val);
+int bitvec_spare_padding(struct bitvec *bv, unsigned int up_to_bit);
+int bitvec_get_bytes(struct bitvec *bv, uint8_t *bytes, unsigned int count);
+int bitvec_set_bytes(struct bitvec *bv, const uint8_t *bytes, unsigned int count);
+/*struct bitvec *bitvec_alloc(unsigned int size, TALLOC_CTX *bvctx);*/
+/*void bitvec_free(struct bitvec *bv);*/
+int bitvec_unhex(struct bitvec *bv, const char *src);
+unsigned int bitvec_pack(const struct bitvec *bv, uint8_t *buffer);
+unsigned int bitvec_unpack(struct bitvec *bv, const uint8_t *buffer);
+uint64_t bitvec_read_field(struct bitvec *bv, unsigned int *read_index, unsigned int len);
+int bitvec_write_field(struct bitvec *bv, unsigned int *write_index, uint64_t val, unsigned int len);
+int bitvec_fill(struct bitvec *bv, unsigned int num_bits, enum bit_value fill);
+char bit_value_to_char(enum bit_value v);
+void bitvec_to_string_r(const struct bitvec *bv, char *str);
+void bitvec_zero(struct bitvec *bv);
+unsigned bitvec_rl(const struct bitvec *bv, bool b);
+unsigned bitvec_rl_curbit(struct bitvec *bv, bool b, int max_bits);
+void bitvec_shiftl(struct bitvec *bv, unsigned int n);
+int16_t bitvec_get_int16_msb(const struct bitvec *bv, unsigned int num_bits);
+unsigned int bitvec_add_array(struct bitvec *bv, const uint32_t *array,
+ unsigned int array_len, bool dry_run,
+ unsigned int num_bits);
+
+/*! @} */
diff --git a/lib/decoding/osmocom/core/conv.c b/lib/decoding/osmocom/core/conv.c
new file mode 100644
index 0000000..e60ce35
--- /dev/null
+++ b/lib/decoding/osmocom/core/conv.c
@@ -0,0 +1,644 @@
+/*! \file conv.c
+ * Generic convolutional encoding / decoding. */
+/*
+ * Copyright (C) 2011 Sylvain Munaut <tnt@246tNt.com>
+ *
+ * All Rights Reserved
+ *
+ * SPDX-License-Identifier: GPL-2.0+
+ *
+ * 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 2 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, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+
+/*! \addtogroup conv
+ * @{
+ * Osmocom convolutional encoder and decoder.
+ *
+ * \file conv.c */
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#ifdef HAVE_ALLOCA_H
+#include <alloca.h>
+#endif
+#include <stdint.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include <osmocom/core/bits.h>
+#include <osmocom/core/conv.h>
+
+
+/* ------------------------------------------------------------------------ */
+/* Common */
+/* ------------------------------------------------------------------------ */
+
+int
+osmo_conv_get_input_length(const struct osmo_conv_code *code, int len)
+{
+ return len <= 0 ? code->len : len;
+}
+
+int
+osmo_conv_get_output_length(const struct osmo_conv_code *code, int len)
+{
+ int pbits, in_len, out_len;
+
+ /* Input length */
+ in_len = osmo_conv_get_input_length(code, len);
+
+ /* Output length */
+ out_len = in_len * code->N;
+
+ if (code->term == CONV_TERM_FLUSH)
+ out_len += code->N * (code->K - 1);
+
+ /* Count punctured bits */
+ if (code->puncture) {
+ for (pbits=0; code->puncture[pbits] >= 0; pbits++);
+ out_len -= pbits;
+ }
+
+ return out_len;
+}
+
+
+/* ------------------------------------------------------------------------ */
+/* Encoding */
+/* ------------------------------------------------------------------------ */
+
+/*! Initialize a convolutional encoder
+ * \param[in,out] encoder Encoder state to initialize
+ * \param[in] code Description of convolutional code
+ */
+void
+osmo_conv_encode_init(struct osmo_conv_encoder *encoder,
+ const struct osmo_conv_code *code)
+{
+ memset(encoder, 0x00, sizeof(struct osmo_conv_encoder));
+ encoder->code = code;
+}
+
+void
+osmo_conv_encode_load_state(struct osmo_conv_encoder *encoder,
+ const ubit_t *input)
+{
+ int i;
+ uint8_t state = 0;
+
+ for (i=0; i<(encoder->code->K-1); i++)
+ state = (state << 1) | input[i];
+
+ encoder->state = state;
+}
+
+static inline int
+_conv_encode_do_output(struct osmo_conv_encoder *encoder,
+ uint8_t out, ubit_t *output)
+{
+ const struct osmo_conv_code *code = encoder->code;
+ int o_idx = 0;
+ int j;
+
+ if (code->puncture) {
+ for (j=0; j<code->N; j++)
+ {
+ int bit_no = code->N - j - 1;
+ int r_idx = encoder->i_idx * code->N + j;
+
+ if (code->puncture[encoder->p_idx] == r_idx)
+ encoder->p_idx++;
+ else
+ output[o_idx++] = (out >> bit_no) & 1;
+ }
+ } else {
+ for (j=0; j<code->N; j++)
+ {
+ int bit_no = code->N - j - 1;
+ output[o_idx++] = (out >> bit_no) & 1;
+ }
+ }
+
+ return o_idx;
+}
+
+int
+osmo_conv_encode_raw(struct osmo_conv_encoder *encoder,
+ const ubit_t *input, ubit_t *output, int n)
+{
+ const struct osmo_conv_code *code = encoder->code;
+ uint8_t state;
+ int i;
+ int o_idx;
+
+ o_idx = 0;
+ state = encoder->state;
+
+ for (i=0; i<n; i++) {
+ int bit = input[i];
+ uint8_t out;
+
+ out = code->next_output[state][bit];
+ state = code->next_state[state][bit];
+
+ o_idx += _conv_encode_do_output(encoder, out, &output[o_idx]);
+
+ encoder->i_idx++;
+ }
+
+ encoder->state = state;
+
+ return o_idx;
+}
+
+int
+osmo_conv_encode_flush(struct osmo_conv_encoder *encoder,
+ ubit_t *output)
+{
+ const struct osmo_conv_code *code = encoder->code;
+ uint8_t state;
+ int n;
+ int i;
+ int o_idx;
+
+ n = code->K - 1;
+
+ o_idx = 0;
+ state = encoder->state;
+
+ for (i=0; i<n; i++) {
+ uint8_t out;
+
+ if (code->next_term_output) {
+ out = code->next_term_output[state];
+ state = code->next_term_state[state];
+ } else {
+ out = code->next_output[state][0];
+ state = code->next_state[state][0];
+ }
+
+ o_idx += _conv_encode_do_output(encoder, out, &output[o_idx]);
+
+ encoder->i_idx++;
+ }
+
+ encoder->state = state;
+
+ return o_idx;
+}
+
+/*! All-in-one convolutional encoding function
+ * \param[in] code description of convolutional code to be used
+ * \param[in] input array of unpacked bits (uncoded)
+ * \param[out] output array of unpacked bits (encoded)
+ * \return Number of produced output bits
+ *
+ * This is an all-in-one function, taking care of
+ * \ref osmo_conv_init, \ref osmo_conv_encode_load_state,
+ * \ref osmo_conv_encode_raw and \ref osmo_conv_encode_flush as needed.
+ */
+int
+osmo_conv_encode(const struct osmo_conv_code *code,
+ const ubit_t *input, ubit_t *output)
+{
+ struct osmo_conv_encoder encoder;
+ int l;
+
+ osmo_conv_encode_init(&encoder, code);
+
+ if (code->term == CONV_TERM_TAIL_BITING) {
+ int eidx = code->len - code->K + 1;
+ osmo_conv_encode_load_state(&encoder, &input[eidx]);
+ }
+
+ l = osmo_conv_encode_raw(&encoder, input, output, code->len);
+
+ if (code->term == CONV_TERM_FLUSH)
+ l += osmo_conv_encode_flush(&encoder, &output[l]);
+
+ return l;
+}
+
+
+/* ------------------------------------------------------------------------ */
+/* Decoding (viterbi) */
+/* ------------------------------------------------------------------------ */
+
+#define MAX_AE 0x00ffffff
+
+/* Forward declaration for accerlated decoding with certain codes */
+int
+osmo_conv_decode_acc(const struct osmo_conv_code *code,
+ const sbit_t *input, ubit_t *output);
+
+void
+osmo_conv_decode_init(struct osmo_conv_decoder *decoder,
+ const struct osmo_conv_code *code, int len, int start_state)
+{
+ int n_states;
+
+ /* Init */
+ if (len <= 0)
+ len = code->len;
+
+ n_states = 1 << (code->K - 1);
+
+ memset(decoder, 0x00, sizeof(struct osmo_conv_decoder));
+
+ decoder->code = code;
+ decoder->n_states = n_states;
+ decoder->len = len;
+
+ /* Allocate arrays */
+ decoder->ae = malloc(sizeof(unsigned int) * n_states);
+ decoder->ae_next = malloc(sizeof(unsigned int) * n_states);
+
+ decoder->state_history = malloc(sizeof(uint8_t) * n_states * (len + decoder->code->K - 1));
+
+ /* Classic reset */
+ osmo_conv_decode_reset(decoder, start_state);
+}
+
+void
+osmo_conv_decode_reset(struct osmo_conv_decoder *decoder, int start_state)
+{
+ int i;
+
+ /* Reset indexes */
+ decoder->o_idx = 0;
+ decoder->p_idx = 0;
+
+ /* Initial error */
+ if (start_state < 0) {
+ /* All states possible */
+ memset(decoder->ae, 0x00, sizeof(unsigned int) * decoder->n_states);
+ } else {
+ /* Fixed start state */
+ for (i=0; i<decoder->n_states; i++) {
+ decoder->ae[i] = (i == start_state) ? 0 : MAX_AE;
+ }
+ }
+}
+
+void
+osmo_conv_decode_rewind(struct osmo_conv_decoder *decoder)
+{
+ int i;
+ unsigned int min_ae = MAX_AE;
+
+ /* Reset indexes */
+ decoder->o_idx = 0;
+ decoder->p_idx = 0;
+
+ /* Initial error normalize (remove constant) */
+ for (i=0; i<decoder->n_states; i++) {
+ if (decoder->ae[i] < min_ae)
+ min_ae = decoder->ae[i];
+ }
+
+ for (i=0; i<decoder->n_states; i++)
+ decoder->ae[i] -= min_ae;
+}
+
+void
+osmo_conv_decode_deinit(struct osmo_conv_decoder *decoder)
+{
+ free(decoder->ae);
+ free(decoder->ae_next);
+ free(decoder->state_history);
+
+ memset(decoder, 0x00, sizeof(struct osmo_conv_decoder));
+}
+
+int
+osmo_conv_decode_scan(struct osmo_conv_decoder *decoder,
+ const sbit_t *input, int n)
+{
+ const struct osmo_conv_code *code = decoder->code;
+
+ int i, s, b, j;
+
+ int n_states;
+ unsigned int *ae;
+ unsigned int *ae_next;
+ uint8_t *state_history;
+ sbit_t *in_sym;
+
+ int i_idx, p_idx;
+
+ /* Prepare */
+ n_states = decoder->n_states;
+
+ ae = decoder->ae;
+ ae_next = decoder->ae_next;
+ state_history = &decoder->state_history[n_states * decoder->o_idx];
+
+ in_sym = malloc(sizeof(sbit_t) * code->N);
+
+ i_idx = 0;
+ p_idx = decoder->p_idx;
+
+ /* Scan the treillis */
+ for (i=0; i<n; i++)
+ {
+ /* Reset next accumulated error */
+ for (s=0; s<n_states; s++) {
+ ae_next[s] = MAX_AE;
+ }
+
+ /* Get input */
+ if (code->puncture) {
+ /* Hard way ... */
+ for (j=0; j<code->N; j++) {
+ int idx = ((decoder->o_idx + i) * code->N) + j;
+ if (idx == code->puncture[p_idx]) {
+ in_sym[j] = 0; /* Undefined */
+ p_idx++;
+ } else {
+ in_sym[j] = input[i_idx];
+ i_idx++;
+ }
+ }
+ } else {
+ /* Easy, just copy N bits */
+ memcpy(in_sym, &input[i_idx], code->N);
+ i_idx += code->N;
+ }
+
+ /* Scan all state */
+ for (s=0; s<n_states; s++)
+ {
+ /* Scan possible input bits */
+ for (b=0; b<2; b++)
+ {
+ int nae, ov, e;
+ uint8_t m;
+
+ /* Next output and state */
+ uint8_t out = code->next_output[s][b];
+ uint8_t state = code->next_state[s][b];
+
+ /* New error for this path */
+ nae = ae[s]; /* start from last error */
+ m = 1 << (code->N - 1); /* mask for 'out' bit selection */
+
+ for (j=0; j<code->N; j++) {
+ int is = (int)in_sym[j];
+ if (is) {
+ ov = (out & m) ? -127 : 127; /* sbit_t value for it */
+ e = is - ov; /* raw error for this bit */
+ nae += (e * e) >> 9; /* acc the squared/scaled value */
+ }
+ m >>= 1; /* next mask bit */
+ }
+
+ /* Is it survivor ? */
+ if (ae_next[state] > nae) {
+ ae_next[state] = nae;
+ state_history[(n_states * i) + state] = s;
+ }
+ }
+ }
+
+ /* Copy accumulated error */
+ memcpy(ae, ae_next, sizeof(unsigned int) * n_states);
+ }
+
+ /* Update decoder state */
+ decoder->p_idx = p_idx;
+ decoder->o_idx += n;
+
+ free(in_sym);
+ return i_idx;
+}
+
+int
+osmo_conv_decode_flush(struct osmo_conv_decoder *decoder,
+ const sbit_t *input)
+{
+ const struct osmo_conv_code *code = decoder->code;
+
+ int i, s, j;
+
+ int n_states;
+ unsigned int *ae;
+ unsigned int *ae_next;
+ uint8_t *state_history;
+ sbit_t *in_sym;
+
+ int i_idx, p_idx;
+
+ /* Prepare */
+ n_states = decoder->n_states;
+
+ ae = decoder->ae;
+ ae_next = decoder->ae_next;
+ state_history = &decoder->state_history[n_states * decoder->o_idx];
+
+ in_sym = malloc(sizeof(sbit_t) * code->N);
+
+ i_idx = 0;
+ p_idx = decoder->p_idx;
+
+ /* Scan the treillis */
+ for (i=0; i<code->K-1; i++)
+ {
+ /* Reset next accumulated error */
+ for (s=0; s<n_states; s++) {
+ ae_next[s] = MAX_AE;
+ }
+
+ /* Get input */
+ if (code->puncture) {
+ /* Hard way ... */
+ for (j=0; j<code->N; j++) {
+ int idx = ((decoder->o_idx + i) * code->N) + j;
+ if (idx == code->puncture[p_idx]) {
+ in_sym[j] = 0; /* Undefined */
+ p_idx++;
+ } else {
+ in_sym[j] = input[i_idx];
+ i_idx++;
+ }
+ }
+ } else {
+ /* Easy, just copy N bits */
+ memcpy(in_sym, &input[i_idx], code->N);
+ i_idx += code->N;
+ }
+
+ /* Scan all state */
+ for (s=0; s<n_states; s++)
+ {
+ int nae, ov, e;
+ uint8_t m;
+
+ /* Next output and state */
+ uint8_t out;
+ uint8_t state;
+
+ if (code->next_term_output) {
+ out = code->next_term_output[s];
+ state = code->next_term_state[s];
+ } else {
+ out = code->next_output[s][0];
+ state = code->next_state[s][0];
+ }
+
+ /* New error for this path */
+ nae = ae[s]; /* start from last error */
+ m = 1 << (code->N - 1); /* mask for 'out' bit selection */
+
+ for (j=0; j<code->N; j++) {
+ int is = (int)in_sym[j];
+ if (is) {
+ ov = (out & m) ? -127 : 127; /* sbit_t value for it */
+ e = is - ov; /* raw error for this bit */
+ nae += (e * e) >> 9; /* acc the squared/scaled value */
+ }
+ m >>= 1; /* next mask bit */
+ }
+
+ /* Is it survivor ? */
+ if (ae_next[state] > nae) {
+ ae_next[state] = nae;
+ state_history[(n_states * i) + state] = s;
+ }
+ }
+
+ /* Copy accumulated error */
+ memcpy(ae, ae_next, sizeof(unsigned int) * n_states);
+ }
+
+ /* Update decoder state */
+ decoder->p_idx = p_idx;
+ decoder->o_idx += code->K - 1;
+
+ free(in_sym);
+ return i_idx;
+}
+
+int
+osmo_conv_decode_get_output(struct osmo_conv_decoder *decoder,
+ ubit_t *output, int has_flush, int end_state)
+{
+ const struct osmo_conv_code *code = decoder->code;
+
+ int min_ae;
+ uint8_t min_state, cur_state;
+ int i, s, n;
+
+ uint8_t *sh_ptr;
+
+ /* End state ? */
+ if (end_state < 0) {
+ /* Find state with least error */
+ min_ae = MAX_AE;
+ min_state = 0xff;
+
+ for (s=0; s<decoder->n_states; s++)
+ {
+ if (decoder->ae[s] < min_ae) {
+ min_ae = decoder->ae[s];
+ min_state = s;
+ }
+ }
+
+ if (min_state == 0xff)
+ return -1;
+ } else {
+ min_state = (uint8_t) end_state;
+ min_ae = decoder->ae[end_state];
+ }
+
+ /* Traceback */
+ cur_state = min_state;
+
+ n = decoder->o_idx;
+
+ sh_ptr = &decoder->state_history[decoder->n_states * (n-1)];
+
+ /* No output for the K-1 termination input bits */
+ if (has_flush) {
+ for (i=0; i<code->K-1; i++) {
+ cur_state = sh_ptr[cur_state];
+ sh_ptr -= decoder->n_states;
+ }
+ n -= code->K - 1;
+ }
+
+ /* Generate output backward */
+ for (i=n-1; i>=0; i--)
+ {
+ min_state = cur_state;
+ cur_state = sh_ptr[cur_state];
+
+ sh_ptr -= decoder->n_states;
+
+ if (code->next_state[cur_state][0] == min_state)
+ output[i] = 0;
+ else
+ output[i] = 1;
+ }
+
+ return min_ae;
+}
+
+/*! All-in-one convolutional decoding function
+ * \param[in] code description of convolutional code to be used
+ * \param[in] input array of soft bits (coded)
+ * \param[out] output array of unpacked bits (decoded)
+ *
+ * This is an all-in-one function, taking care of
+ * \ref osmo_conv_decode_init, \ref osmo_conv_decode_scan,
+ * \ref osmo_conv_decode_flush, \ref osmo_conv_decode_get_output and
+ * \ref osmo_conv_decode_deinit.
+ */
+int
+osmo_conv_decode(const struct osmo_conv_code *code,
+ const sbit_t *input, ubit_t *output)
+{
+ struct osmo_conv_decoder decoder;
+ int rv, l;
+
+ /* Use accelerated implementation for supported codes */
+ if ((code->N <= 4) && ((code->K == 5) || (code->K == 7)))
+ return osmo_conv_decode_acc(code, input, output);
+
+ osmo_conv_decode_init(&decoder, code, 0, 0);
+
+ if (code->term == CONV_TERM_TAIL_BITING) {
+ osmo_conv_decode_scan(&decoder, input, code->len);
+ osmo_conv_decode_rewind(&decoder);
+ }
+
+ l = osmo_conv_decode_scan(&decoder, input, code->len);
+
+ if (code->term == CONV_TERM_FLUSH)
+ osmo_conv_decode_flush(&decoder, &input[l]);
+
+ rv = osmo_conv_decode_get_output(&decoder, output,
+ code->term == CONV_TERM_FLUSH, /* has_flush */
+ code->term == CONV_TERM_FLUSH ? 0 : -1 /* end_state */
+ );
+
+ osmo_conv_decode_deinit(&decoder);
+
+ return rv;
+}
+
+/*! @} */
diff --git a/lib/decoding/osmocom/core/conv.h b/lib/decoding/osmocom/core/conv.h
new file mode 100644
index 0000000..8b344f4
--- /dev/null
+++ b/lib/decoding/osmocom/core/conv.h
@@ -0,0 +1,139 @@
+/*! \file conv.h
+ * Osmocom convolutional encoder and decoder. */
+/*
+ * Copyright (C) 2011 Sylvain Munaut <tnt@246tNt.com>
+ *
+ * All Rights Reserved
+ *
+ * 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 2 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, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+
+/*! \defgroup conv Convolutional encoding and decoding routines
+ * @{
+ * \file conv.h */
+
+#pragma once
+
+#include <stdint.h>
+
+#include <osmocom/core/bits.h>
+
+/*! possibe termination types
+ *
+ * The termination type will determine which state the encoder/decoder
+ * can start/end with. This is mostly taken care of in the high level API
+ * call. So if you use the low level API, you must take care of making the
+ * proper calls yourself.
+ */
+enum osmo_conv_term {
+ CONV_TERM_FLUSH = 0, /*!< Flush encoder state */
+ CONV_TERM_TRUNCATION, /*!< Direct truncation */
+ CONV_TERM_TAIL_BITING, /*!< Tail biting */
+};
+
+/*! structure describing a given convolutional code
+ *
+ * The only required fields are N,K and the next_output/next_state arrays. The
+ * other can be left to default value of zero depending on what the code does.
+ * If 'len' is left at 0 then only the low level API can be used.
+ */
+struct osmo_conv_code {
+ int N; /*!< Inverse of code rate */
+ int K; /*!< Constraint length */
+ int len; /*!< # of data bits */
+
+ enum osmo_conv_term term; /*!< Termination type */
+
+ const uint8_t (*next_output)[2];/*!< Next output array */
+ const uint8_t (*next_state)[2]; /*!< Next state array */
+
+ const uint8_t *next_term_output;/*!< Flush termination output */
+ const uint8_t *next_term_state; /*!< Flush termination state */
+
+ const int *puncture; /*!< Punctured bits indexes */
+};
+
+
+/* Common */
+
+int osmo_conv_get_input_length(const struct osmo_conv_code *code, int len);
+int osmo_conv_get_output_length(const struct osmo_conv_code *code, int len);
+
+
+/* Encoding */
+
+ /* Low level API */
+
+/*! convolutional encoder state */
+struct osmo_conv_encoder {
+ const struct osmo_conv_code *code; /*!< for which code? */
+ int i_idx; /*!< Next input bit index */
+ int p_idx; /*!< Current puncture index */
+ uint8_t state; /*!< Current state */
+};
+
+void osmo_conv_encode_init(struct osmo_conv_encoder *encoder,
+ const struct osmo_conv_code *code);
+void osmo_conv_encode_load_state(struct osmo_conv_encoder *encoder,
+ const ubit_t *input);
+int osmo_conv_encode_raw(struct osmo_conv_encoder *encoder,
+ const ubit_t *input, ubit_t *output, int n);
+int osmo_conv_encode_flush(struct osmo_conv_encoder *encoder, ubit_t *output);
+
+ /* All-in-one */
+int osmo_conv_encode(const struct osmo_conv_code *code,
+ const ubit_t *input, ubit_t *output);
+
+
+/* Decoding */
+
+ /* Low level API */
+
+/*! convolutional decoder state */
+struct osmo_conv_decoder {
+ const struct osmo_conv_code *code; /*!< for which code? */
+
+ int n_states; /*!< number of states */
+
+ int len; /*!< Max o_idx (excl. termination) */
+
+ int o_idx; /*!< output index */
+ int p_idx; /*!< puncture index */
+
+ unsigned int *ae; /*!< accumulated error */
+ unsigned int *ae_next; /*!< next accumulated error (tmp in scan) */
+ uint8_t *state_history; /*!< state history [len][n_states] */
+};
+
+void osmo_conv_decode_init(struct osmo_conv_decoder *decoder,
+ const struct osmo_conv_code *code,
+ int len, int start_state);
+void osmo_conv_decode_reset(struct osmo_conv_decoder *decoder, int start_state);
+void osmo_conv_decode_rewind(struct osmo_conv_decoder *decoder);
+void osmo_conv_decode_deinit(struct osmo_conv_decoder *decoder);
+
+int osmo_conv_decode_scan(struct osmo_conv_decoder *decoder,
+ const sbit_t *input, int n);
+int osmo_conv_decode_flush(struct osmo_conv_decoder *decoder,
+ const sbit_t *input);
+int osmo_conv_decode_get_output(struct osmo_conv_decoder *decoder,
+ ubit_t *output, int has_flush, int end_state);
+
+ /* All-in-one */
+int osmo_conv_decode(const struct osmo_conv_code *code,
+ const sbit_t *input, ubit_t *output);
+
+
+/*! @} */
diff --git a/lib/decoding/osmocom/core/conv_acc.c b/lib/decoding/osmocom/core/conv_acc.c
new file mode 100644
index 0000000..dce2682
--- /dev/null
+++ b/lib/decoding/osmocom/core/conv_acc.c
@@ -0,0 +1,720 @@
+/*! \file conv_acc.c
+ * Accelerated Viterbi decoder implementation. */
+/*
+ * Copyright (C) 2013, 2014 Thomas Tsou <tom@tsou.cc>
+ *
+ * All Rights Reserved
+ *
+ * SPDX-License-Identifier: GPL-2.0+
+ *
+ * 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 2 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, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+
+#include <stdlib.h>
+#include <string.h>
+#include <errno.h>
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#define __attribute__(_arg_)
+
+#include <osmocom/core/conv.h>
+
+#define BIT2NRZ(REG,N) (((REG >> N) & 0x01) * 2 - 1) * -1
+#define NUM_STATES(K) (K == 7 ? 64 : 16)
+
+#define INIT_POINTERS(simd) \
+{ \
+ osmo_conv_metrics_k5_n2 = osmo_conv_##simd##_metrics_k5_n2; \
+ osmo_conv_metrics_k5_n3 = osmo_conv_##simd##_metrics_k5_n3; \
+ osmo_conv_metrics_k5_n4 = osmo_conv_##simd##_metrics_k5_n4; \
+ osmo_conv_metrics_k7_n2 = osmo_conv_##simd##_metrics_k7_n2; \
+ osmo_conv_metrics_k7_n3 = osmo_conv_##simd##_metrics_k7_n3; \
+ osmo_conv_metrics_k7_n4 = osmo_conv_##simd##_metrics_k7_n4; \
+ vdec_malloc = &osmo_conv_##simd##_vdec_malloc; \
+ vdec_free = &osmo_conv_##simd##_vdec_free; \
+}
+
+static int init_complete = 0;
+
+__attribute__ ((visibility("hidden"))) int avx2_supported = 0;
+__attribute__ ((visibility("hidden"))) int ssse3_supported = 0;
+__attribute__ ((visibility("hidden"))) int sse41_supported = 0;
+
+/**
+ * These pointers are being initialized at runtime by the
+ * osmo_conv_init() depending on supported SIMD extensions.
+ */
+static int16_t *(*vdec_malloc)(size_t n);
+static void (*vdec_free)(int16_t *ptr);
+
+void (*osmo_conv_metrics_k5_n2)(const int8_t *seq,
+ const int16_t *out, int16_t *sums, int16_t *paths, int norm);
+void (*osmo_conv_metrics_k5_n3)(const int8_t *seq,
+ const int16_t *out, int16_t *sums, int16_t *paths, int norm);
+void (*osmo_conv_metrics_k5_n4)(const int8_t *seq,
+ const int16_t *out, int16_t *sums, int16_t *paths, int norm);
+void (*osmo_conv_metrics_k7_n2)(const int8_t *seq,
+ const int16_t *out, int16_t *sums, int16_t *paths, int norm);
+void (*osmo_conv_metrics_k7_n3)(const int8_t *seq,
+ const int16_t *out, int16_t *sums, int16_t *paths, int norm);
+void (*osmo_conv_metrics_k7_n4)(const int8_t *seq,
+ const int16_t *out, int16_t *sums, int16_t *paths, int norm);
+
+/* Forward malloc wrappers */
+int16_t *osmo_conv_gen_vdec_malloc(size_t n);
+void osmo_conv_gen_vdec_free(int16_t *ptr);
+
+#if defined(HAVE_SSSE3)
+int16_t *osmo_conv_sse_vdec_malloc(size_t n);
+void osmo_conv_sse_vdec_free(int16_t *ptr);
+#endif
+
+#if defined(HAVE_SSSE3) && defined(HAVE_AVX2)
+int16_t *osmo_conv_sse_avx_vdec_malloc(size_t n);
+void osmo_conv_sse_avx_vdec_free(int16_t *ptr);
+#endif
+
+/* Forward Metric Units */
+void osmo_conv_gen_metrics_k5_n2(const int8_t *seq, const int16_t *out,
+ int16_t *sums, int16_t *paths, int norm);
+void osmo_conv_gen_metrics_k5_n3(const int8_t *seq, const int16_t *out,
+ int16_t *sums, int16_t *paths, int norm);
+void osmo_conv_gen_metrics_k5_n4(const int8_t *seq, const int16_t *out,
+ int16_t *sums, int16_t *paths, int norm);
+void osmo_conv_gen_metrics_k7_n2(const int8_t *seq, const int16_t *out,
+ int16_t *sums, int16_t *paths, int norm);
+void osmo_conv_gen_metrics_k7_n3(const int8_t *seq, const int16_t *out,
+ int16_t *sums, int16_t *paths, int norm);
+void osmo_conv_gen_metrics_k7_n4(const int8_t *seq, const int16_t *out,
+ int16_t *sums, int16_t *paths, int norm);
+
+#if defined(HAVE_SSSE3)
+void osmo_conv_sse_metrics_k5_n2(const int8_t *seq, const int16_t *out,
+ int16_t *sums, int16_t *paths, int norm);
+void osmo_conv_sse_metrics_k5_n3(const int8_t *seq, const int16_t *out,
+ int16_t *sums, int16_t *paths, int norm);
+void osmo_conv_sse_metrics_k5_n4(const int8_t *seq, const int16_t *out,
+ int16_t *sums, int16_t *paths, int norm);
+void osmo_conv_sse_metrics_k7_n2(const int8_t *seq, const int16_t *out,
+ int16_t *sums, int16_t *paths, int norm);
+void osmo_conv_sse_metrics_k7_n3(const int8_t *seq, const int16_t *out,
+ int16_t *sums, int16_t *paths, int norm);
+void osmo_conv_sse_metrics_k7_n4(const int8_t *seq, const int16_t *out,
+ int16_t *sums, int16_t *paths, int norm);
+#endif
+
+#if defined(HAVE_SSSE3) && defined(HAVE_AVX2)
+void osmo_conv_sse_avx_metrics_k5_n2(const int8_t *seq, const int16_t *out,
+ int16_t *sums, int16_t *paths, int norm);
+void osmo_conv_sse_avx_metrics_k5_n3(const int8_t *seq, const int16_t *out,
+ int16_t *sums, int16_t *paths, int norm);
+void osmo_conv_sse_avx_metrics_k5_n4(const int8_t *seq, const int16_t *out,
+ int16_t *sums, int16_t *paths, int norm);
+void osmo_conv_sse_avx_metrics_k7_n2(const int8_t *seq, const int16_t *out,
+ int16_t *sums, int16_t *paths, int norm);
+void osmo_conv_sse_avx_metrics_k7_n3(const int8_t *seq, const int16_t *out,
+ int16_t *sums, int16_t *paths, int norm);
+void osmo_conv_sse_avx_metrics_k7_n4(const int8_t *seq, const int16_t *out,
+ int16_t *sums, int16_t *paths, int norm);
+#endif
+
+/* Trellis State
+ * state - Internal lshift register value
+ * prev - Register values of previous 0 and 1 states
+ */
+struct vstate {
+ unsigned state;
+ unsigned prev[2];
+};
+
+/* Trellis Object
+ * num_states - Number of states in the trellis
+ * sums - Accumulated path metrics
+ * outputs - Trellis output values
+ * vals - Input value that led to each state
+ */
+struct vtrellis {
+ int num_states;
+ int16_t *sums;
+ int16_t *outputs;
+ uint8_t *vals;
+};
+
+/* Viterbi Decoder
+ * n - Code order
+ * k - Constraint length
+ * len - Horizontal length of trellis
+ * recursive - Set to '1' if the code is recursive
+ * intrvl - Normalization interval
+ * trellis - Trellis object
+ * paths - Trellis paths
+ */
+struct vdecoder {
+ int n;
+ int k;
+ int len;
+ int recursive;
+ int intrvl;
+ struct vtrellis trellis;
+ int16_t **paths;
+
+ void (*metric_func)(const int8_t *, const int16_t *,
+ int16_t *, int16_t *, int);
+};
+
+/* Accessor calls */
+static inline int conv_code_recursive(const struct osmo_conv_code *code)
+{
+ return code->next_term_output ? 1 : 0;
+}
+
+/* Left shift and mask for finding the previous state */
+static unsigned vstate_lshift(unsigned reg, int k, int val)
+{
+ unsigned mask;
+
+ if (k == 5)
+ mask = 0x0e;
+ else if (k == 7)
+ mask = 0x3e;
+ else
+ mask = 0;
+
+ return ((reg << 1) & mask) | val;
+}
+
+/* Bit endian manipulators */
+static inline unsigned bitswap2(unsigned v)
+{
+ return ((v & 0x02) >> 1) | ((v & 0x01) << 1);
+}
+
+static inline unsigned bitswap3(unsigned v)
+{
+ return ((v & 0x04) >> 2) | ((v & 0x02) >> 0) |
+ ((v & 0x01) << 2);
+}
+
+static inline unsigned bitswap4(unsigned v)
+{
+ return ((v & 0x08) >> 3) | ((v & 0x04) >> 1) |
+ ((v & 0x02) << 1) | ((v & 0x01) << 3);
+}
+
+static inline unsigned bitswap5(unsigned v)
+{
+ return ((v & 0x10) >> 4) | ((v & 0x08) >> 2) | ((v & 0x04) >> 0) |
+ ((v & 0x02) << 2) | ((v & 0x01) << 4);
+}
+
+static inline unsigned bitswap6(unsigned v)
+{
+ return ((v & 0x20) >> 5) | ((v & 0x10) >> 3) | ((v & 0x08) >> 1) |
+ ((v & 0x04) << 1) | ((v & 0x02) << 3) | ((v & 0x01) << 5);
+}
+
+static unsigned bitswap(unsigned v, unsigned n)
+{
+ switch (n) {
+ case 1:
+ return v;
+ case 2:
+ return bitswap2(v);
+ case 3:
+ return bitswap3(v);
+ case 4:
+ return bitswap4(v);
+ case 5:
+ return bitswap5(v);
+ case 6:
+ return bitswap6(v);
+ default:
+ return 0;
+ }
+}
+
+/* Generate non-recursive state output from generator state table
+ * Note that the shift register moves right (i.e. the most recent bit is
+ * shifted into the register at k-1 bit of the register), which is typical
+ * textbook representation. The API transition table expects the most recent
+ * bit in the low order bit, or left shift. A bitswap operation is required
+ * to accommodate the difference.
+ */
+static unsigned gen_output(struct vstate *state, int val,
+ const struct osmo_conv_code *code)
+{
+ unsigned out, prev;
+
+ prev = bitswap(state->prev[0], code->K - 1);
+ out = code->next_output[prev][val];
+ out = bitswap(out, code->N);
+
+ return out;
+}
+
+/* Populate non-recursive trellis state
+ * For a given state defined by the k-1 length shift register, find the
+ * value of the input bit that drove the trellis to that state. Also
+ * generate the N outputs of the generator polynomial at that state.
+ */
+static int gen_state_info(uint8_t *val, unsigned reg,
+ int16_t *output, const struct osmo_conv_code *code)
+{
+ int i;
+ unsigned out;
+ struct vstate state;
+
+ /* Previous '0' state */
+ state.state = reg;
+ state.prev[0] = vstate_lshift(reg, code->K, 0);
+ state.prev[1] = vstate_lshift(reg, code->K, 1);
+
+ *val = (reg >> (code->K - 2)) & 0x01;
+
+ /* Transition output */
+ out = gen_output(&state, *val, code);
+
+ /* Unpack to NRZ */
+ for (i = 0; i < code->N; i++)
+ output[i] = BIT2NRZ(out, i);
+
+ return 0;
+}
+
+/* Generate recursive state output from generator state table */
+static unsigned gen_recursive_output(struct vstate *state,
+ uint8_t *val, unsigned reg,
+ const struct osmo_conv_code *code, int pos)
+{
+ int val0, val1;
+ unsigned out, prev;
+
+ /* Previous '0' state */
+ prev = vstate_lshift(reg, code->K, 0);
+ prev = bitswap(prev, code->K - 1);
+
+ /* Input value */
+ val0 = (reg >> (code->K - 2)) & 0x01;
+ val1 = (code->next_term_output[prev] >> pos) & 0x01;
+ *val = val0 == val1 ? 0 : 1;
+
+ /* Wrapper for osmocom state access */
+ prev = bitswap(state->prev[0], code->K - 1);
+
+ /* Compute the transition output */
+ out = code->next_output[prev][*val];
+ out = bitswap(out, code->N);
+
+ return out;
+}
+
+/* Populate recursive trellis state
+ * The bit position of the systematic bit is not explicitly marked by the
+ * API, so it must be extracted from the generator table. Otherwise,
+ * populate the trellis similar to the non-recursive version.
+ * Non-systematic recursive codes are not supported.
+ */
+static int gen_recursive_state_info(uint8_t *val,
+ unsigned reg, int16_t *output, const struct osmo_conv_code *code)
+{
+ int i, j, pos = -1;
+ int ns = NUM_STATES(code->K);
+ unsigned out;
+ struct vstate state;
+
+ /* Previous '0' and '1' states */
+ state.state = reg;
+ state.prev[0] = vstate_lshift(reg, code->K, 0);
+ state.prev[1] = vstate_lshift(reg, code->K, 1);
+
+ /* Find recursive bit location */
+ for (i = 0; i < code->N; i++) {
+ for (j = 0; j < ns; j++) {
+ if ((code->next_output[j][0] >> i) & 0x01)
+ break;
+ }
+
+ if (j == ns) {
+ pos = i;
+ break;
+ }
+ }
+
+ /* Non-systematic recursive code not supported */
+ if (pos < 0)
+ return -EPROTO;
+
+ /* Transition output */
+ out = gen_recursive_output(&state, val, reg, code, pos);
+
+ /* Unpack to NRZ */
+ for (i = 0; i < code->N; i++)
+ output[i] = BIT2NRZ(out, i);
+
+ return 0;
+}
+
+/* Release the trellis */
+static void free_trellis(struct vtrellis *trellis)
+{
+ if (!trellis)
+ return;
+
+ vdec_free(trellis->outputs);
+ vdec_free(trellis->sums);
+ free(trellis->vals);
+}
+
+/* Initialize the trellis object
+ * Initialization consists of generating the outputs and output value of a
+ * given state. Due to trellis symmetry and anti-symmetry, only one of the
+ * transition paths is utilized by the butterfly operation in the forward
+ * recursion, so only one set of N outputs is required per state variable.
+ */
+static int generate_trellis(struct vdecoder *dec,
+ const struct osmo_conv_code *code)
+{
+ struct vtrellis *trellis = &dec->trellis;
+ int16_t *outputs;
+ int i, rc;
+
+ int ns = NUM_STATES(code->K);
+ int olen = (code->N == 2) ? 2 : 4;
+
+ trellis->num_states = ns;
+ trellis->sums = vdec_malloc(ns);
+ trellis->outputs = vdec_malloc(ns * olen);
+ trellis->vals = (uint8_t *) malloc(ns * sizeof(uint8_t));
+
+ if (!trellis->sums || !trellis->outputs || !trellis->vals) {
+ rc = -ENOMEM;
+ goto fail;
+ }
+
+ /* Populate the trellis state objects */
+ for (i = 0; i < ns; i++) {
+ outputs = &trellis->outputs[olen * i];
+ if (dec->recursive) {
+ rc = gen_recursive_state_info(&trellis->vals[i],
+ i, outputs, code);
+ } else {
+ rc = gen_state_info(&trellis->vals[i],
+ i, outputs, code);
+ }
+
+ if (rc < 0)
+ goto fail;
+
+ /* Set accumulated path metrics to zero */
+ trellis->sums[i] = 0;
+ }
+
+ /**
+ * For termination other than tail-biting, initialize the zero state
+ * as the encoder starting state. Initialize with the maximum
+ * accumulated sum at length equal to the constraint length.
+ */
+ if (code->term != CONV_TERM_TAIL_BITING)
+ trellis->sums[0] = INT8_MAX * code->N * code->K;
+
+ return 0;
+
+fail:
+ free_trellis(trellis);
+ return rc;
+}
+
+static void _traceback(struct vdecoder *dec,
+ unsigned state, uint8_t *out, int len)
+{
+ int i;
+ unsigned path;
+
+ for (i = len - 1; i >= 0; i--) {
+ path = dec->paths[i][state] + 1;
+ out[i] = dec->trellis.vals[state];
+ state = vstate_lshift(state, dec->k, path);
+ }
+}
+
+static void _traceback_rec(struct vdecoder *dec,
+ unsigned state, uint8_t *out, int len)
+{
+ int i;
+ unsigned path;
+
+ for (i = len - 1; i >= 0; i--) {
+ path = dec->paths[i][state] + 1;
+ out[i] = path ^ dec->trellis.vals[state];
+ state = vstate_lshift(state, dec->k, path);
+ }
+}
+
+/* Traceback and generate decoded output
+ * Find the largest accumulated path metric at the final state except for
+ * the zero terminated case, where we assume the final state is always zero.
+ */
+static int traceback(struct vdecoder *dec, uint8_t *out, int term, int len)
+{
+ int i, sum, max = -1;
+ unsigned path, state = 0;
+
+ if (term != CONV_TERM_FLUSH) {
+ for (i = 0; i < dec->trellis.num_states; i++) {
+ sum = dec->trellis.sums[i];
+ if (sum > max) {
+ max = sum;
+ state = i;
+ }
+ }
+
+ if (max < 0)
+ return -EPROTO;
+ }
+
+ for (i = dec->len - 1; i >= len; i--) {
+ path = dec->paths[i][state] + 1;
+ state = vstate_lshift(state, dec->k, path);
+ }
+
+ if (dec->recursive)
+ _traceback_rec(dec, state, out, len);
+ else
+ _traceback(dec, state, out, len);
+
+ return 0;
+}
+
+/* Release decoder object */
+static void vdec_deinit(struct vdecoder *dec)
+{
+ if (!dec)
+ return;
+
+ free_trellis(&dec->trellis);
+
+ if (dec->paths != NULL) {
+ vdec_free(dec->paths[0]);
+ free(dec->paths);
+ }
+}
+
+/* Initialize decoder object with code specific params
+ * Subtract the constraint length K on the normalization interval to
+ * accommodate the initialization path metric at state zero.
+ */
+static int vdec_init(struct vdecoder *dec, const struct osmo_conv_code *code)
+{
+ int i, ns, rc;
+
+ ns = NUM_STATES(code->K);
+
+ dec->n = code->N;
+ dec->k = code->K;
+ dec->recursive = conv_code_recursive(code);
+ dec->intrvl = INT16_MAX / (dec->n * INT8_MAX) - dec->k;
+
+ if (dec->k == 5) {
+ switch (dec->n) {
+ case 2:
+ dec->metric_func = osmo_conv_metrics_k5_n2;
+ break;
+ case 3:
+ dec->metric_func = osmo_conv_metrics_k5_n3;
+ break;
+ case 4:
+ dec->metric_func = osmo_conv_metrics_k5_n4;
+ break;
+ default:
+ return -EINVAL;
+ }
+ } else if (dec->k == 7) {
+ switch (dec->n) {
+ case 2:
+ dec->metric_func = osmo_conv_metrics_k7_n2;
+ break;
+ case 3:
+ dec->metric_func = osmo_conv_metrics_k7_n3;
+ break;
+ case 4:
+ dec->metric_func = osmo_conv_metrics_k7_n4;
+ break;
+ default:
+ return -EINVAL;
+ }
+ } else {
+ return -EINVAL;
+ }
+
+ if (code->term == CONV_TERM_FLUSH)
+ dec->len = code->len + code->K - 1;
+ else
+ dec->len = code->len;
+
+ rc = generate_trellis(dec, code);
+ if (rc)
+ return rc;
+
+ dec->paths = (int16_t **) malloc(sizeof(int16_t *) * dec->len);
+ if (!dec->paths)
+ goto enomem;
+
+ dec->paths[0] = vdec_malloc(ns * dec->len);
+ if (!dec->paths[0])
+ goto enomem;
+
+ for (i = 1; i < dec->len; i++)
+ dec->paths[i] = &dec->paths[0][i * ns];
+
+ return 0;
+
+enomem:
+ vdec_deinit(dec);
+ return -ENOMEM;
+}
+
+/* Depuncture sequence with nagative value terminated puncturing matrix */
+static int depuncture(const int8_t *in, const int *punc, int8_t *out, int len)
+{
+ int i, n = 0, m = 0;
+
+ for (i = 0; i < len; i++) {
+ if (i == punc[n]) {
+ out[i] = 0;
+ n++;
+ continue;
+ }
+
+ out[i] = in[m++];
+ }
+
+ return 0;
+}
+
+/* Forward trellis recursion
+ * Generate branch metrics and path metrics with a combined function. Only
+ * accumulated path metric sums and path selections are stored. Normalize on
+ * the interval specified by the decoder.
+ */
+static void forward_traverse(struct vdecoder *dec, const int8_t *seq)
+{
+ int i;
+
+ for (i = 0; i < dec->len; i++) {
+ dec->metric_func(&seq[dec->n * i],
+ dec->trellis.outputs,
+ dec->trellis.sums,
+ dec->paths[i],
+ !(i % dec->intrvl));
+ }
+}
+
+/* Convolutional decode with a decoder object
+ * Initial puncturing run if necessary followed by the forward recursion.
+ * For tail-biting perform a second pass before running the backward
+ * traceback operation.
+ */
+static int conv_decode(struct vdecoder *dec, const int8_t *seq,
+ const int *punc, uint8_t *out, int len, int term)
+{
+ //int8_t depunc[dec->len * dec->n]; //!! this isn't portable, in strict C you can't use size of an array that is not known at compile time
+ int8_t * depunc = malloc(sizeof(int8_t)*dec->len * dec->n);
+
+
+ if (punc) {
+ depuncture(seq, punc, depunc, dec->len * dec->n);
+ seq = depunc;
+ }
+
+ /* Propagate through the trellis with interval normalization */
+ forward_traverse(dec, seq);
+
+ if (term == CONV_TERM_TAIL_BITING)
+ forward_traverse(dec, seq);
+
+ free(depunc);
+ return traceback(dec, out, term, len);
+}
+
+static void osmo_conv_init(void)
+{
+ init_complete = 1;
+
+#ifdef HAVE___BUILTIN_CPU_SUPPORTS
+ /* Detect CPU capabilities */
+ #ifdef HAVE_AVX2
+ avx2_supported = __builtin_cpu_supports("avx2");
+ #endif
+
+ #ifdef HAVE_SSSE3
+ ssse3_supported = __builtin_cpu_supports("ssse3");
+ #endif
+
+ #ifdef HAVE_SSE4_1
+ sse41_supported = __builtin_cpu_supports("sse4.1");
+ #endif
+#endif
+
+/**
+ * Usage of curly braces is mandatory,
+ * because we use multi-line define.
+ */
+#if defined(HAVE_SSSE3) && defined(HAVE_AVX2)
+ if (ssse3_supported && avx2_supported) {
+ INIT_POINTERS(sse_avx);
+ } else if (ssse3_supported) {
+ INIT_POINTERS(sse);
+ } else {
+ INIT_POINTERS(gen);
+ }
+#elif defined(HAVE_SSSE3)
+ if (ssse3_supported) {
+ INIT_POINTERS(sse);
+ } else {
+ INIT_POINTERS(gen);
+ }
+#else
+ INIT_POINTERS(gen);
+#endif
+}
+
+/* All-in-one Viterbi decoding */
+int osmo_conv_decode_acc(const struct osmo_conv_code *code,
+ const sbit_t *input, ubit_t *output)
+{
+ int rc;
+ struct vdecoder dec;
+
+ if (!init_complete)
+ osmo_conv_init();
+
+ if ((code->N < 2) || (code->N > 4) || (code->len < 1) ||
+ ((code->K != 5) && (code->K != 7)))
+ return -EINVAL;
+
+ rc = vdec_init(&dec, code);
+ if (rc)
+ return rc;
+
+ rc = conv_decode(&dec, input, code->puncture,
+ output, code->len, code->term);
+
+ vdec_deinit(&dec);
+
+ return rc;
+}
diff --git a/lib/decoding/osmocom/core/conv_acc_generic.c b/lib/decoding/osmocom/core/conv_acc_generic.c
new file mode 100644
index 0000000..7da0213
--- /dev/null
+++ b/lib/decoding/osmocom/core/conv_acc_generic.c
@@ -0,0 +1,213 @@
+/*! \file conv_acc_generic.c
+ * Accelerated Viterbi decoder implementation
+ * for generic architectures without SSE support. */
+/*
+ * Copyright (C) 2013, 2014 Thomas Tsou <tom@tsou.cc>
+ *
+ * All Rights Reserved
+ *
+ * SPDX-License-Identifier: GPL-2.0+
+ *
+ * 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 2 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, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+
+#include <stdlib.h>
+#include <stdint.h>
+#include <string.h>
+
+#define __attribute__(_arg_)
+
+/* Add-Compare-Select (ACS-Butterfly)
+ * Compute 4 accumulated path metrics and 4 path selections. Note that path
+ * selections are store as -1 and 0 rather than 0 and 1. This is to match
+ * the output format of the SSE packed compare instruction 'pmaxuw'.
+ */
+
+static void acs_butterfly(int state, int num_states,
+ int16_t metric, int16_t *sum,
+ int16_t *new_sum, int16_t *path)
+{
+ int state0, state1;
+ int sum0, sum1, sum2, sum3;
+
+ state0 = *(sum + (2 * state + 0));
+ state1 = *(sum + (2 * state + 1));
+
+ sum0 = state0 + metric;
+ sum1 = state1 - metric;
+ sum2 = state0 - metric;
+ sum3 = state1 + metric;
+
+ if (sum0 >= sum1) {
+ *new_sum = sum0;
+ *path = -1;
+ } else {
+ *new_sum = sum1;
+ *path = 0;
+ }
+
+ if (sum2 >= sum3) {
+ *(new_sum + num_states / 2) = sum2;
+ *(path + num_states / 2) = -1;
+ } else {
+ *(new_sum + num_states / 2) = sum3;
+ *(path + num_states / 2) = 0;
+ }
+}
+
+/* Branch metrics unit N=2 */
+static void gen_branch_metrics_n2(int num_states, const int8_t *seq,
+ const int16_t *out, int16_t *metrics)
+{
+ int i;
+
+ for (i = 0; i < num_states / 2; i++) {
+ metrics[i] = seq[0] * out[2 * i + 0] +
+ seq[1] * out[2 * i + 1];
+ }
+}
+
+/* Branch metrics unit N=3 */
+static void gen_branch_metrics_n3(int num_states, const int8_t *seq,
+ const int16_t *out, int16_t *metrics)
+{
+ int i;
+
+ for (i = 0; i < num_states / 2; i++) {
+ metrics[i] = seq[0] * out[4 * i + 0] +
+ seq[1] * out[4 * i + 1] +
+ seq[2] * out[4 * i + 2];
+ }
+}
+
+/* Branch metrics unit N=4 */
+static void gen_branch_metrics_n4(int num_states, const int8_t *seq,
+ const int16_t *out, int16_t *metrics)
+{
+ int i;
+
+ for (i = 0; i < num_states / 2; i++) {
+ metrics[i] = seq[0] * out[4 * i + 0] +
+ seq[1] * out[4 * i + 1] +
+ seq[2] * out[4 * i + 2] +
+ seq[3] * out[4 * i + 3];
+ }
+}
+
+/* Path metric unit */
+static void gen_path_metrics(int num_states, int16_t *sums,
+ int16_t *metrics, int16_t *paths, int norm)
+{
+ int i;
+ int16_t min;
+ int16_t * new_sums = malloc(sizeof(int16_t)*num_states);
+
+ for (i = 0; i < num_states / 2; i++)
+ acs_butterfly(i, num_states, metrics[i],
+ sums, &new_sums[i], &paths[i]);
+
+ if (norm) {
+ min = new_sums[0];
+
+ for (i = 1; i < num_states; i++)
+ if (new_sums[i] < min)
+ min = new_sums[i];
+
+ for (i = 0; i < num_states; i++)
+ new_sums[i] -= min;
+ }
+
+ free(new_sums);
+ memcpy(sums, new_sums, num_states * sizeof(int16_t));
+}
+
+/* Not-aligned Memory Allocator */
+__attribute__ ((visibility("hidden")))
+int16_t *osmo_conv_gen_vdec_malloc(size_t n)
+{
+ return (int16_t *) malloc(sizeof(int16_t) * n);
+}
+
+__attribute__ ((visibility("hidden")))
+void osmo_conv_gen_vdec_free(int16_t *ptr)
+{
+ free(ptr);
+}
+
+/* 16-state branch-path metrics units (K=5) */
+__attribute__ ((visibility("hidden")))
+void osmo_conv_gen_metrics_k5_n2(const int8_t *seq, const int16_t *out,
+ int16_t *sums, int16_t *paths, int norm)
+{
+ int16_t metrics[8];
+
+ gen_branch_metrics_n2(16, seq, out, metrics);
+ gen_path_metrics(16, sums, metrics, paths, norm);
+}
+
+__attribute__ ((visibility("hidden")))
+void osmo_conv_gen_metrics_k5_n3(const int8_t *seq, const int16_t *out,
+ int16_t *sums, int16_t *paths, int norm)
+{
+ int16_t metrics[8];
+
+ gen_branch_metrics_n3(16, seq, out, metrics);
+ gen_path_metrics(16, sums, metrics, paths, norm);
+
+}
+
+__attribute__ ((visibility("hidden")))
+void osmo_conv_gen_metrics_k5_n4(const int8_t *seq, const int16_t *out,
+ int16_t *sums, int16_t *paths, int norm)
+{
+ int16_t metrics[8];
+
+ gen_branch_metrics_n4(16, seq, out, metrics);
+ gen_path_metrics(16, sums, metrics, paths, norm);
+
+}
+
+/* 64-state branch-path metrics units (K=7) */
+__attribute__ ((visibility("hidden")))
+void osmo_conv_gen_metrics_k7_n2(const int8_t *seq, const int16_t *out,
+ int16_t *sums, int16_t *paths, int norm)
+{
+ int16_t metrics[32];
+
+ gen_branch_metrics_n2(64, seq, out, metrics);
+ gen_path_metrics(64, sums, metrics, paths, norm);
+
+}
+
+__attribute__ ((visibility("hidden")))
+void osmo_conv_gen_metrics_k7_n3(const int8_t *seq, const int16_t *out,
+ int16_t *sums, int16_t *paths, int norm)
+{
+ int16_t metrics[32];
+
+ gen_branch_metrics_n3(64, seq, out, metrics);
+ gen_path_metrics(64, sums, metrics, paths, norm);
+
+}
+
+__attribute__ ((visibility("hidden")))
+void osmo_conv_gen_metrics_k7_n4(const int8_t *seq, const int16_t *out,
+ int16_t *sums, int16_t *paths, int norm)
+{
+ int16_t metrics[32];
+
+ gen_branch_metrics_n4(64, seq, out, metrics);
+ gen_path_metrics(64, sums, metrics, paths, norm);
+}
diff --git a/lib/decoding/osmocom/core/crc16gen.c b/lib/decoding/osmocom/core/crc16gen.c
new file mode 100644
index 0000000..ea69d88
--- /dev/null
+++ b/lib/decoding/osmocom/core/crc16gen.c
@@ -0,0 +1,120 @@
+/*
+ * crc16gen.c
+ *
+ * Generic CRC routines (for max 16 bits poly)
+ *
+ * Copyright (C) 2011 Sylvain Munaut <tnt@246tNt.com>
+ *
+ * All Rights Reserved
+ *
+ * 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 2 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, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+
+/*! \addtogroup crcgen
+ * @{
+ */
+
+/*! \file crc16gen.c
+ * Osmocom generic CRC routines (for max 16 bits poly)
+ */
+
+#include <stdint.h>
+
+#include <osmocom/core/bits.h>
+#include <osmocom/core/crc16gen.h>
+
+
+/*! \brief Compute the CRC value of a given array of hard-bits
+ * \param[in] code The CRC code description to apply
+ * \param[in] in Array of hard bits
+ * \param[in] len Length of the array of hard bits
+ * \returns The CRC value
+ */
+uint16_t
+osmo_crc16gen_compute_bits(const struct osmo_crc16gen_code *code,
+ const ubit_t *in, int len)
+{
+ const uint16_t poly = code->poly;
+ uint16_t crc = code->init;
+ int i, n = code->bits-1;
+
+ for (i=0; i<len; i++) {
+ uint16_t bit = in[i] & 1;
+ crc ^= (bit << n);
+ if (crc & ((uint16_t)1 << n)) {
+ crc <<= 1;
+ crc ^= poly;
+ } else {
+ crc <<= 1;
+ }
+ crc &= ((uint16_t)1 << code->bits) - 1;
+ }
+
+ crc ^= code->remainder;
+
+ return crc;
+}
+
+
+/*! \brief Checks the CRC value of a given array of hard-bits
+ * \param[in] code The CRC code description to apply
+ * \param[in] in Array of hard bits
+ * \param[in] len Length of the array of hard bits
+ * \param[in] crc_bits Array of hard bits with the alleged CRC
+ * \returns 0 if CRC matches. 1 in case of error.
+ *
+ * The crc_bits array must have a length of code->len
+ */
+int
+osmo_crc16gen_check_bits(const struct osmo_crc16gen_code *code,
+ const ubit_t *in, int len, const ubit_t *crc_bits)
+{
+ uint16_t crc;
+ int i;
+
+ crc = osmo_crc16gen_compute_bits(code, in, len);
+
+ for (i=0; i<code->bits; i++)
+ if (crc_bits[i] ^ ((crc >> (code->bits-i-1)) & 1))
+ return 1;
+
+ return 0;
+}
+
+
+/*! \brief Computes and writes the CRC value of a given array of bits
+ * \param[in] code The CRC code description to apply
+ * \param[in] in Array of hard bits
+ * \param[in] len Length of the array of hard bits
+ * \param[in] crc_bits Array of hard bits to write the computed CRC to
+ *
+ * The crc_bits array must have a length of code->len
+ */
+void
+osmo_crc16gen_set_bits(const struct osmo_crc16gen_code *code,
+ const ubit_t *in, int len, ubit_t *crc_bits)
+{
+ uint16_t crc;
+ int i;
+
+ crc = osmo_crc16gen_compute_bits(code, in, len);
+
+ for (i=0; i<code->bits; i++)
+ crc_bits[i] = ((crc >> (code->bits-i-1)) & 1);
+}
+
+/*! @} */
+
+/* vim: set syntax=c: */
diff --git a/lib/decoding/osmocom/core/crc16gen.h b/lib/decoding/osmocom/core/crc16gen.h
new file mode 100644
index 0000000..567b74e
--- /dev/null
+++ b/lib/decoding/osmocom/core/crc16gen.h
@@ -0,0 +1,56 @@
+/*
+ * crc16gen.h
+ *
+ * Copyright (C) 2011 Sylvain Munaut <tnt@246tNt.com>
+ *
+ * All Rights Reserved
+ *
+ * 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 2 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, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+
+#pragma once
+
+/*! \addtogroup crcgen
+ * @{
+ */
+
+/*! \file crc16gen.h
+ * Osmocom generic CRC routines (for max 16 bits poly) header
+ */
+
+
+#include <stdint.h>
+#include <osmocom/core/bits.h>
+
+
+/*! \brief structure describing a given CRC code of max 16 bits */
+struct osmo_crc16gen_code {
+ int bits; /*!< \brief Actual number of bits of the CRC */
+ uint16_t poly; /*!< \brief Polynom (normal representation, MSB omitted */
+ uint16_t init; /*!< \brief Initialization value of the CRC state */
+ uint16_t remainder; /*!< \brief Remainder of the CRC (final XOR) */
+};
+
+uint16_t osmo_crc16gen_compute_bits(const struct osmo_crc16gen_code *code,
+ const ubit_t *in, int len);
+int osmo_crc16gen_check_bits(const struct osmo_crc16gen_code *code,
+ const ubit_t *in, int len, const ubit_t *crc_bits);
+void osmo_crc16gen_set_bits(const struct osmo_crc16gen_code *code,
+ const ubit_t *in, int len, ubit_t *crc_bits);
+
+
+/*! @} */
+
+/* vim: set syntax=c: */
diff --git a/lib/decoding/osmocom/core/crc32gen.h b/lib/decoding/osmocom/core/crc32gen.h
new file mode 100644
index 0000000..3b95338
--- /dev/null
+++ b/lib/decoding/osmocom/core/crc32gen.h
@@ -0,0 +1,56 @@
+/*
+ * crc32gen.h
+ *
+ * Copyright (C) 2011 Sylvain Munaut <tnt@246tNt.com>
+ *
+ * All Rights Reserved
+ *
+ * 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 2 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, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+
+#pragma once
+
+/*! \addtogroup crcgen
+ * @{
+ */
+
+/*! \file crc32gen.h
+ * Osmocom generic CRC routines (for max 32 bits poly) header
+ */
+
+
+#include <stdint.h>
+#include <osmocom/core/bits.h>
+
+
+/*! \brief structure describing a given CRC code of max 32 bits */
+struct osmo_crc32gen_code {
+ int bits; /*!< \brief Actual number of bits of the CRC */
+ uint32_t poly; /*!< \brief Polynom (normal representation, MSB omitted */
+ uint32_t init; /*!< \brief Initialization value of the CRC state */
+ uint32_t remainder; /*!< \brief Remainder of the CRC (final XOR) */
+};
+
+uint32_t osmo_crc32gen_compute_bits(const struct osmo_crc32gen_code *code,
+ const ubit_t *in, int len);
+int osmo_crc32gen_check_bits(const struct osmo_crc32gen_code *code,
+ const ubit_t *in, int len, const ubit_t *crc_bits);
+void osmo_crc32gen_set_bits(const struct osmo_crc32gen_code *code,
+ const ubit_t *in, int len, ubit_t *crc_bits);
+
+
+/*! @} */
+
+/* vim: set syntax=c: */
diff --git a/lib/decoding/osmocom/core/crc64gen.c b/lib/decoding/osmocom/core/crc64gen.c
new file mode 100644
index 0000000..3e700d4
--- /dev/null
+++ b/lib/decoding/osmocom/core/crc64gen.c
@@ -0,0 +1,120 @@
+/*
+ * crc64gen.c
+ *
+ * Generic CRC routines (for max 64 bits poly)
+ *
+ * Copyright (C) 2011 Sylvain Munaut <tnt@246tNt.com>
+ *
+ * All Rights Reserved
+ *
+ * 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 2 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, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+
+/*! \addtogroup crcgen
+ * @{
+ */
+
+/*! \file crc64gen.c
+ * Osmocom generic CRC routines (for max 64 bits poly)
+ */
+
+#include <stdint.h>
+
+#include <osmocom/core/bits.h>
+#include <osmocom/core/crc64gen.h>
+
+
+/*! \brief Compute the CRC value of a given array of hard-bits
+ * \param[in] code The CRC code description to apply
+ * \param[in] in Array of hard bits
+ * \param[in] len Length of the array of hard bits
+ * \returns The CRC value
+ */
+uint64_t
+osmo_crc64gen_compute_bits(const struct osmo_crc64gen_code *code,
+ const ubit_t *in, int len)
+{
+ const uint64_t poly = code->poly;
+ uint64_t crc = code->init;
+ int i, n = code->bits-1;
+
+ for (i=0; i<len; i++) {
+ uint64_t bit = in[i] & 1;
+ crc ^= (bit << n);
+ if (crc & ((uint64_t)1 << n)) {
+ crc <<= 1;
+ crc ^= poly;
+ } else {
+ crc <<= 1;
+ }
+ crc &= ((uint64_t)1 << code->bits) - 1;
+ }
+
+ crc ^= code->remainder;
+
+ return crc;
+}
+
+
+/*! \brief Checks the CRC value of a given array of hard-bits
+ * \param[in] code The CRC code description to apply
+ * \param[in] in Array of hard bits
+ * \param[in] len Length of the array of hard bits
+ * \param[in] crc_bits Array of hard bits with the alleged CRC
+ * \returns 0 if CRC matches. 1 in case of error.
+ *
+ * The crc_bits array must have a length of code->len
+ */
+int
+osmo_crc64gen_check_bits(const struct osmo_crc64gen_code *code,
+ const ubit_t *in, int len, const ubit_t *crc_bits)
+{
+ uint64_t crc;
+ int i;
+
+ crc = osmo_crc64gen_compute_bits(code, in, len);
+
+ for (i=0; i<code->bits; i++)
+ if (crc_bits[i] ^ ((crc >> (code->bits-i-1)) & 1))
+ return 1;
+
+ return 0;
+}
+
+
+/*! \brief Computes and writes the CRC value of a given array of bits
+ * \param[in] code The CRC code description to apply
+ * \param[in] in Array of hard bits
+ * \param[in] len Length of the array of hard bits
+ * \param[in] crc_bits Array of hard bits to write the computed CRC to
+ *
+ * The crc_bits array must have a length of code->len
+ */
+void
+osmo_crc64gen_set_bits(const struct osmo_crc64gen_code *code,
+ const ubit_t *in, int len, ubit_t *crc_bits)
+{
+ uint64_t crc;
+ int i;
+
+ crc = osmo_crc64gen_compute_bits(code, in, len);
+
+ for (i=0; i<code->bits; i++)
+ crc_bits[i] = ((crc >> (code->bits-i-1)) & 1);
+}
+
+/*! @} */
+
+/* vim: set syntax=c: */
diff --git a/lib/decoding/osmocom/core/crc64gen.h b/lib/decoding/osmocom/core/crc64gen.h
new file mode 100644
index 0000000..aa02e04
--- /dev/null
+++ b/lib/decoding/osmocom/core/crc64gen.h
@@ -0,0 +1,56 @@
+/*
+ * crc64gen.h
+ *
+ * Copyright (C) 2011 Sylvain Munaut <tnt@246tNt.com>
+ *
+ * All Rights Reserved
+ *
+ * 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 2 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, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+
+#pragma once
+
+/*! \addtogroup crcgen
+ * @{
+ */
+
+/*! \file crc64gen.h
+ * Osmocom generic CRC routines (for max 64 bits poly) header
+ */
+
+
+#include <stdint.h>
+#include <osmocom/core/bits.h>
+
+
+/*! \brief structure describing a given CRC code of max 64 bits */
+struct osmo_crc64gen_code {
+ int bits; /*!< \brief Actual number of bits of the CRC */
+ uint64_t poly; /*!< \brief Polynom (normal representation, MSB omitted */
+ uint64_t init; /*!< \brief Initialization value of the CRC state */
+ uint64_t remainder; /*!< \brief Remainder of the CRC (final XOR) */
+};
+
+uint64_t osmo_crc64gen_compute_bits(const struct osmo_crc64gen_code *code,
+ const ubit_t *in, int len);
+int osmo_crc64gen_check_bits(const struct osmo_crc64gen_code *code,
+ const ubit_t *in, int len, const ubit_t *crc_bits);
+void osmo_crc64gen_set_bits(const struct osmo_crc64gen_code *code,
+ const ubit_t *in, int len, ubit_t *crc_bits);
+
+
+/*! @} */
+
+/* vim: set syntax=c: */
diff --git a/lib/decoding/osmocom/core/crc8gen.c b/lib/decoding/osmocom/core/crc8gen.c
new file mode 100644
index 0000000..0b85b0d
--- /dev/null
+++ b/lib/decoding/osmocom/core/crc8gen.c
@@ -0,0 +1,120 @@
+/*
+ * crc8gen.c
+ *
+ * Generic CRC routines (for max 8 bits poly)
+ *
+ * Copyright (C) 2011 Sylvain Munaut <tnt@246tNt.com>
+ *
+ * All Rights Reserved
+ *
+ * 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 2 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, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+
+/*! \addtogroup crcgen
+ * @{
+ */
+
+/*! \file crc8gen.c
+ * Osmocom generic CRC routines (for max 8 bits poly)
+ */
+
+#include <stdint.h>
+
+#include <osmocom/core/bits.h>
+#include <osmocom/core/crc8gen.h>
+
+
+/*! \brief Compute the CRC value of a given array of hard-bits
+ * \param[in] code The CRC code description to apply
+ * \param[in] in Array of hard bits
+ * \param[in] len Length of the array of hard bits
+ * \returns The CRC value
+ */
+uint8_t
+osmo_crc8gen_compute_bits(const struct osmo_crc8gen_code *code,
+ const ubit_t *in, int len)
+{
+ const uint8_t poly = code->poly;
+ uint8_t crc = code->init;
+ int i, n = code->bits-1;
+
+ for (i=0; i<len; i++) {
+ uint8_t bit = in[i] & 1;
+ crc ^= (bit << n);
+ if (crc & ((uint8_t)1 << n)) {
+ crc <<= 1;
+ crc ^= poly;
+ } else {
+ crc <<= 1;
+ }
+ crc &= ((uint8_t)1 << code->bits) - 1;
+ }
+
+ crc ^= code->remainder;
+
+ return crc;
+}
+
+
+/*! \brief Checks the CRC value of a given array of hard-bits
+ * \param[in] code The CRC code description to apply
+ * \param[in] in Array of hard bits
+ * \param[in] len Length of the array of hard bits
+ * \param[in] crc_bits Array of hard bits with the alleged CRC
+ * \returns 0 if CRC matches. 1 in case of error.
+ *
+ * The crc_bits array must have a length of code->len
+ */
+int
+osmo_crc8gen_check_bits(const struct osmo_crc8gen_code *code,
+ const ubit_t *in, int len, const ubit_t *crc_bits)
+{
+ uint8_t crc;
+ int i;
+
+ crc = osmo_crc8gen_compute_bits(code, in, len);
+
+ for (i=0; i<code->bits; i++)
+ if (crc_bits[i] ^ ((crc >> (code->bits-i-1)) & 1))
+ return 1;
+
+ return 0;
+}
+
+
+/*! \brief Computes and writes the CRC value of a given array of bits
+ * \param[in] code The CRC code description to apply
+ * \param[in] in Array of hard bits
+ * \param[in] len Length of the array of hard bits
+ * \param[in] crc_bits Array of hard bits to write the computed CRC to
+ *
+ * The crc_bits array must have a length of code->len
+ */
+void
+osmo_crc8gen_set_bits(const struct osmo_crc8gen_code *code,
+ const ubit_t *in, int len, ubit_t *crc_bits)
+{
+ uint8_t crc;
+ int i;
+
+ crc = osmo_crc8gen_compute_bits(code, in, len);
+
+ for (i=0; i<code->bits; i++)
+ crc_bits[i] = ((crc >> (code->bits-i-1)) & 1);
+}
+
+/*! @} */
+
+/* vim: set syntax=c: */
diff --git a/lib/decoding/osmocom/core/crc8gen.h b/lib/decoding/osmocom/core/crc8gen.h
new file mode 100644
index 0000000..9513276
--- /dev/null
+++ b/lib/decoding/osmocom/core/crc8gen.h
@@ -0,0 +1,56 @@
+/*
+ * crc8gen.h
+ *
+ * Copyright (C) 2011 Sylvain Munaut <tnt@246tNt.com>
+ *
+ * All Rights Reserved
+ *
+ * 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 2 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, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+
+#pragma once
+
+/*! \addtogroup crcgen
+ * @{
+ */
+
+/*! \file crc8gen.h
+ * Osmocom generic CRC routines (for max 8 bits poly) header
+ */
+
+
+#include <stdint.h>
+#include <osmocom/core/bits.h>
+
+
+/*! \brief structure describing a given CRC code of max 8 bits */
+struct osmo_crc8gen_code {
+ int bits; /*!< \brief Actual number of bits of the CRC */
+ uint8_t poly; /*!< \brief Polynom (normal representation, MSB omitted */
+ uint8_t init; /*!< \brief Initialization value of the CRC state */
+ uint8_t remainder; /*!< \brief Remainder of the CRC (final XOR) */
+};
+
+uint8_t osmo_crc8gen_compute_bits(const struct osmo_crc8gen_code *code,
+ const ubit_t *in, int len);
+int osmo_crc8gen_check_bits(const struct osmo_crc8gen_code *code,
+ const ubit_t *in, int len, const ubit_t *crc_bits);
+void osmo_crc8gen_set_bits(const struct osmo_crc8gen_code *code,
+ const ubit_t *in, int len, ubit_t *crc_bits);
+
+
+/*! @} */
+
+/* vim: set syntax=c: */
diff --git a/lib/decoding/osmocom/core/crcgen.h b/lib/decoding/osmocom/core/crcgen.h
new file mode 100644
index 0000000..7cfe869
--- /dev/null
+++ b/lib/decoding/osmocom/core/crcgen.h
@@ -0,0 +1,34 @@
+/*! \file crcgen.h
+ * Osmocom generic CRC routines global header. */
+/*
+ * Copyright (C) 2011 Sylvain Munaut <tnt@246tNt.com>
+ *
+ * All Rights Reserved
+ *
+ * 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 2 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, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+
+#pragma once
+
+/*! \defgroup crc Osmocom CRC routines
+ * @{
+ * \file crcgen.h */
+
+#include <osmocom/core/crc8gen.h>
+#include <osmocom/core/crc16gen.h>
+#include <osmocom/core/crc32gen.h>
+#include <osmocom/core/crc64gen.h>
+
+/*! @} */
diff --git a/lib/decoding/osmocom/core/defs.h b/lib/decoding/osmocom/core/defs.h
new file mode 100644
index 0000000..5e5aa90
--- /dev/null
+++ b/lib/decoding/osmocom/core/defs.h
@@ -0,0 +1,53 @@
+/*! \file defs.h
+ * General definitions that are meant to be included from header files.
+ */
+
+#pragma once
+
+/*! \defgroup utils General-purpose utility functions
+ * @{
+ * \file defs.h */
+
+/*! Check for gcc and version.
+ *
+ * \note Albeit glibc provides a features.h file that contains a similar
+ * definition (__GNUC_PREREQ), this definition has been copied from there
+ * to have it available with other libraries, too.
+ *
+ * \return != 0 iff gcc is used and it's version is at least maj.min.
+ */
+#if defined __GNUC__ && defined __GNUC_MINOR__
+# define OSMO_GNUC_PREREQ(maj, min) \
+ ((__GNUC__ << 16) + __GNUC_MINOR__ >= ((maj) << 16) + (min))
+#else
+# define OSMO_GNUC_PREREQ(maj, min) 0
+#endif
+
+/*! Set the deprecated attribute with a message.
+ */
+#if defined(__clang__)
+# define _OSMO_HAS_ATTRIBUTE_DEPRECATED __has_attribute(deprecated)
+# define _OSMO_HAS_ATTRIBUTE_DEPRECATED_WITH_MESSAGE __has_extension(attribute_deprecated_with_message)
+#elif defined(__GNUC__)
+# define _OSMO_HAS_ATTRIBUTE_DEPRECATED 1
+# define _OSMO_HAS_ATTRIBUTE_DEPRECATED_WITH_MESSAGE OSMO_GNUC_PREREQ(4,5)
+#endif
+
+#if _OSMO_HAS_ATTRIBUTE_DEPRECATED_WITH_MESSAGE
+# define OSMO_DEPRECATED(text) __attribute__((__deprecated__(text)))
+#elif _OSMO_HAS_ATTRIBUTE_DEPRECATED
+# define OSMO_DEPRECATED(text) __attribute__((__deprecated__))
+#else
+# define OSMO_DEPRECATED(text)
+#endif
+
+#if BUILDING_LIBOSMOCORE
+# define OSMO_DEPRECATED_OUTSIDE_LIBOSMOCORE
+#else
+# define OSMO_DEPRECATED_OUTSIDE_LIBOSMOCORE OSMO_DEPRECATED("For internal use inside libosmocore only.")
+#endif
+
+#undef _OSMO_HAS_ATTRIBUTE_DEPRECATED_WITH_MESSAGE
+#undef _OSMO_HAS_ATTRIBUTE_DEPRECATED
+
+/*! @} */
diff --git a/lib/decoding/osmocom/core/endian.h b/lib/decoding/osmocom/core/endian.h
new file mode 100644
index 0000000..6107b12
--- /dev/null
+++ b/lib/decoding/osmocom/core/endian.h
@@ -0,0 +1,62 @@
+/*! \file endian.h
+ *
+ * GNU and FreeBSD have various ways to express the
+ * endianess but none of them is similiar enough. This
+ * will create two defines that allows to decide on the
+ * endian. The following will be defined to either 0 or
+ * 1 at the end of the file.
+ *
+ * OSMO_IS_LITTLE_ENDIAN
+ * OSMO_IS_BIG_ENDIAN
+ *
+ */
+
+#pragma once
+
+#if defined(__FreeBSD__)
+#include <sys/endian.h>
+ #if BYTE_ORDER == LITTLE_ENDIAN
+ #define OSMO_IS_LITTLE_ENDIAN 1
+ #define OSMO_IS_BIG_ENDIAN 0
+ #elif BYTE_ORDER == BIG_ENDIAN
+ #define OSMO_IS_LITTLE_ENDIAN 0
+ #define OSMO_IS_BIG_ENDIAN 1
+ #else
+ #error "Unknown endian"
+ #endif
+#elif defined(__APPLE__)
+#include <machine/endian.h>
+ #if defined(__DARWIN_LITTLE_ENDIAN)
+ #define OSMO_IS_LITTLE_ENDIAN 1
+ #define OSMO_IS_BIG_ENDIAN 0
+ #elif defined(__DARWIN_BIG_ENDIAN)
+ #define OSMO_IS_LITTLE_ENDIAN 0
+ #define OSMO_IS_BIG_ENDIAN 1
+ #else
+ #error "Unknown endian"
+ #endif
+#elif defined(__linux__)
+#include <endian.h>
+ #if __BYTE_ORDER == __LITTLE_ENDIAN
+ #define OSMO_IS_LITTLE_ENDIAN 1
+ #define OSMO_IS_BIG_ENDIAN 0
+ #elif __BYTE_ORDER == __BIG_ENDIAN
+ #define OSMO_IS_LITTLE_ENDIAN 0
+ #define OSMO_IS_BIG_ENDIAN 1
+ #else
+ #error "Unknown endian"
+ #endif
+#else
+ /* let's try to rely on the compiler. GCC and CLANG/LLVM seem
+ * to support this ... */
+ #if __BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__
+ #define OSMO_IS_LITTLE_ENDIAN 1
+ #define OSMO_IS_BIG_ENDIAN 0
+ #elif __BYTE_ORDER__ == __ORDER_BIG_ENDIAN__
+ #define OSMO_IS_LITTLE_ENDIAN 0
+ #define OSMO_IS_BIG_ENDIAN 1
+ #else
+ #error "Unknown endian"
+ #endif
+#endif
+
diff --git a/lib/decoding/osmocom/core/linuxlist.h b/lib/decoding/osmocom/core/linuxlist.h
new file mode 100644
index 0000000..fee0cc0
--- /dev/null
+++ b/lib/decoding/osmocom/core/linuxlist.h
@@ -0,0 +1,409 @@
+/*! \file linuxlist.h
+ *
+ * Simple doubly linked list implementation.
+ *
+ * Some of the internal functions ("__xxx") are useful when
+ * manipulating whole llists rather than single entries, as
+ * sometimes we already know the next/prev entries and we can
+ * generate better code by using them directly rather than
+ * using the generic single-entry routines.
+ */
+
+#pragma once
+
+/*! \defgroup linuxlist Simple doubly linked list implementation
+ * @{
+ * \file linuxlist.h */
+
+#include <stddef.h>
+
+#if (defined(_WIN16) || defined(_WIN32) || defined(_WIN64)) && !defined(__WINDOWS__)
+# define __WINDOWS__
+#endif
+
+#ifndef inline
+# ifndef __WINDOWS__
+# define inline __inline__
+# else
+# define inline __inline
+# endif
+#endif
+
+static inline void prefetch(const void *x) {;}
+
+/*! cast a member of a structure out to the containing structure
+ *
+ * \param[in] ptr the pointer to the member.
+ * \param[in] type the type of the container struct this is embedded in.
+ * \param[in] member the name of the member within the struct.
+ */
+#define container_of(ptr, type, member) ({ \
+ const typeof( ((type *)0)->member ) *__mptr = (ptr); \
+ (type *)( (char *)__mptr - offsetof(type, member) );})
+
+
+/*!
+ * These are non-NULL pointers that will result in page faults
+ * under normal circumstances, used to verify that nobody uses
+ * non-initialized llist entries.
+ */
+#define LLIST_POISON1 ((void *) 0x00100100)
+#define LLIST_POISON2 ((void *) 0x00200200)
+
+/*! (double) linked list header structure */
+struct llist_head {
+ /*! Pointer to next and previous item */
+ struct llist_head *next, *prev;
+};
+
+#define LLIST_HEAD_INIT(name) { &(name), &(name) }
+
+/*! define a statically-initialized \ref llist_head
+ * \param[in] name Variable name
+ *
+ * This is a helper macro that will define a named variable of type
+ * \ref llist_head and initialize it */
+#define LLIST_HEAD(name) \
+ struct llist_head name = LLIST_HEAD_INIT(name)
+
+/*! initialize a \ref llist_head to point back to self */
+#define INIT_LLIST_HEAD(ptr) do { \
+ (ptr)->next = (ptr); (ptr)->prev = (ptr); \
+} while (0)
+
+/*! Insert a new entry between two known consecutive entries.
+ *
+ * This is only for internal llist manipulation where we know
+ * the prev/next entries already!
+ */
+static inline void __llist_add(struct llist_head *_new,
+ struct llist_head *prev,
+ struct llist_head *next)
+{
+ next->prev = _new;
+ _new->next = next;
+ _new->prev = prev;
+ prev->next = _new;
+}
+
+/*! add a new entry into a linked list (at head)
+ * \param _new New entry to be added
+ * \param head \ref llist_head to add it after
+ *
+ * Insert a new entry after the specified head.
+ * This is good for implementing stacks.
+ */
+static inline void llist_add(struct llist_head *_new, struct llist_head *head)
+{
+ __llist_add(_new, head, head->next);
+}
+
+/*! add a new entry into a linked list (at tail)
+ * \param _new New entry to be added
+ * \param head Head of linked list to whose tail we shall add \a _new
+ *
+ * Insert a new entry before the specified head.
+ * This is useful for implementing queues.
+ */
+static inline void llist_add_tail(struct llist_head *_new, struct llist_head *head)
+{
+ __llist_add(_new, head->prev, head);
+}
+
+/*
+ * Delete a llist entry by making the prev/next entries
+ * point to each other.
+ *
+ * This is only for internal llist manipulation where we know
+ * the prev/next entries already!
+ */
+static inline void __llist_del(struct llist_head * prev, struct llist_head * next)
+{
+ next->prev = prev;
+ prev->next = next;
+}
+
+/*! Delete entry from linked list
+ * \param entry The element to delete from the llist
+ *
+ * Note: llist_empty on entry does not return true after this, the entry is
+ * in an undefined state.
+ */
+static inline void llist_del(struct llist_head *entry)
+{
+ __llist_del(entry->prev, entry->next);
+ entry->next = (struct llist_head *)LLIST_POISON1;
+ entry->prev = (struct llist_head *)LLIST_POISON2;
+}
+
+/*! Delete entry from linked list and reinitialize it
+ * \param entry The element to delete from the list
+ */
+static inline void llist_del_init(struct llist_head *entry)
+{
+ __llist_del(entry->prev, entry->next);
+ INIT_LLIST_HEAD(entry);
+}
+
+/*! Delete from one llist and add as another's head
+ * \param llist The entry to move
+ * \param head The head that will precede our entry
+ */
+static inline void llist_move(struct llist_head *llist, struct llist_head *head)
+{
+ __llist_del(llist->prev, llist->next);
+ llist_add(llist, head);
+}
+
+/*! Delete from one llist and add as another's tail
+ * \param llist The entry to move
+ * \param head The head that will follow our entry
+ */
+static inline void llist_move_tail(struct llist_head *llist,
+ struct llist_head *head)
+{
+ __llist_del(llist->prev, llist->next);
+ llist_add_tail(llist, head);
+}
+
+/*! Test whether a linked list is empty
+ * \param[in] head The llist to test.
+ * \returns 1 if the list is empty, 0 otherwise
+ */
+static inline int llist_empty(const struct llist_head *head)
+{
+ return head->next == head;
+}
+
+static inline void __llist_splice(struct llist_head *llist,
+ struct llist_head *head)
+{
+ struct llist_head *first = llist->next;
+ struct llist_head *last = llist->prev;
+ struct llist_head *at = head->next;
+
+ first->prev = head;
+ head->next = first;
+
+ last->next = at;
+ at->prev = last;
+}
+
+/*! Join two llists
+ * \param llist The new linked list to add
+ * \param head The place to add \a llist in the other list
+ */
+static inline void llist_splice(struct llist_head *llist, struct llist_head *head)
+{
+ if (!llist_empty(llist))
+ __llist_splice(llist, head);
+}
+
+/*! join two llists and reinitialise the emptied llist.
+ * \param llist The new linked list to add.
+ * \param head The place to add it in the first llist.
+ *
+ * The llist at @llist is reinitialised
+ */
+static inline void llist_splice_init(struct llist_head *llist,
+ struct llist_head *head)
+{
+ if (!llist_empty(llist)) {
+ __llist_splice(llist, head);
+ INIT_LLIST_HEAD(llist);
+ }
+}
+
+/*! Get the struct containing this list entry
+ * \param ptr The \ref llist_head pointer
+ * \param type The type of the struct this is embedded in
+ * \param @member The name of the \ref llist_head within the struct
+ */
+#define llist_entry(ptr, type, member) \
+ container_of(ptr, type, member)
+
+/*! Get the first element from a list
+ * \param ptr the list head to take the element from.
+ * \param type the type of the struct this is embedded in.
+ * \param member the name of the list_head within the struct.
+ *
+ * Note, that list is expected to be not empty.
+ */
+#define llist_first_entry(ptr, type, member) \
+ llist_entry((ptr)->next, type, member)
+
+/*! Get the last element from a list
+ * \param ptr the list head to take the element from.
+ * \param type the type of the struct this is embedded in.
+ * \param member the name of the llist_head within the struct.
+ *
+ * Note, that list is expected to be not empty.
+ */
+#define llist_last_entry(ptr, type, member) \
+ llist_entry((ptr)->prev, type, member)
+
+/*! Get the first element from a list, or NULL
+ * \param ptr the list head to take the element from.
+ * \param type the type of the struct this is embedded in.
+ * \param member the name of the list_head within the struct.
+ *
+ * Note that if the list is empty, it returns NULL.
+ */
+#define llist_first_entry_or_null(ptr, type, member) \
+ (!llist_empty(ptr) ? llist_first_entry(ptr, type, member) : NULL)
+
+/*! Iterate over a linked list
+ * \param pos The \ref llist_head to use as a loop counter
+ * \param head The head of the list over which to iterate
+ */
+#define llist_for_each(pos, head) \
+ for (pos = (head)->next, prefetch(pos->next); pos != (head); \
+ pos = pos->next, prefetch(pos->next))
+
+/*! Iterate over a llist (no prefetch)
+ * \param pos The \ref llist_head to use as a loop counter
+ * \param head The head of the list over which to iterate
+ *
+ * This variant differs from llist_for_each() in that it's the
+ * simplest possible llist iteration code, no prefetching is done.
+ * Use this for code that knows the llist to be very short (empty
+ * or 1 entry) most of the time.
+ */
+#define __llist_for_each(pos, head) \
+ for (pos = (head)->next; pos != (head); pos = pos->next)
+
+/*! Iterate over a llist backwards
+ * \param pos The \ref llist_head to use as a loop counter
+ * \param head The head of the list over which to iterate
+ */
+#define llist_for_each_prev(pos, head) \
+ for (pos = (head)->prev, prefetch(pos->prev); pos != (head); \
+ pos = pos->prev, prefetch(pos->prev))
+
+/*! Iterate over a list; safe against removal of llist entry
+ * \param pos The \ref llist_head to use as a loop counter
+ * \param n Another \ref llist_head to use as temporary storage
+ * \param head The head of the list over which to iterate
+ */
+#define llist_for_each_safe(pos, n, head) \
+ for (pos = (head)->next, n = pos->next; pos != (head); \
+ pos = n, n = pos->next)
+
+/*! Iterate over llist of given type
+ * \param pos The 'type *' to use as a loop counter
+ * \param head The head of the list over which to iterate
+ * \param member The name of the \ref llist_head within struct \a pos
+ */
+#define llist_for_each_entry(pos, head, member) \
+ for (pos = llist_entry((head)->next, typeof(*pos), member), \
+ prefetch(pos->member.next); \
+ &pos->member != (head); \
+ pos = llist_entry(pos->member.next, typeof(*pos), member), \
+ prefetch(pos->member.next))
+
+/*! Iterate backwards over llist of given type.
+ * \param pos The 'type *' to use as a loop counter
+ * \param head The head of the list over which to iterate
+ * \param member The name of the \ref llist_head within struct \a pos
+ */
+#define llist_for_each_entry_reverse(pos, head, member) \
+ for (pos = llist_entry((head)->prev, typeof(*pos), member), \
+ prefetch(pos->member.prev); \
+ &pos->member != (head); \
+ pos = llist_entry(pos->member.prev, typeof(*pos), member), \
+ prefetch(pos->member.prev))
+
+/*! iterate over llist of given type continuing after existing
+ * point
+ * \param pos The 'type *' to use as a loop counter
+ * \param head The head of the list over which to iterate
+ * \param member The name of the \ref llist_head within struct \a pos
+ */
+#define llist_for_each_entry_continue(pos, head, member) \
+ for (pos = llist_entry(pos->member.next, typeof(*pos), member), \
+ prefetch(pos->member.next); \
+ &pos->member != (head); \
+ pos = llist_entry(pos->member.next, typeof(*pos), member), \
+ prefetch(pos->member.next))
+
+/*! iterate over llist of given type, safe against removal of
+ * non-consecutive(!) llist entries
+ * \param pos The 'type *' to use as a loop counter
+ * \param n Another type * to use as temporary storage
+ * \param head The head of the list over which to iterate
+ * \param member The name of the \ref llist_head within struct \a pos
+ */
+#define llist_for_each_entry_safe(pos, n, head, member) \
+ for (pos = llist_entry((head)->next, typeof(*pos), member), \
+ n = llist_entry(pos->member.next, typeof(*pos), member); \
+ &pos->member != (head); \
+ pos = n, n = llist_entry(n->member.next, typeof(*n), member))
+
+/**
+ * llist_for_each_rcu - iterate over an rcu-protected llist
+ * @pos: the &struct llist_head to use as a loop counter.
+ * @head: the head for your llist.
+ */
+#define llist_for_each_rcu(pos, head) \
+ for (pos = (head)->next, prefetch(pos->next); pos != (head); \
+ pos = pos->next, ({ smp_read_barrier_depends(); 0;}), prefetch(pos->next))
+
+#define __llist_for_each_rcu(pos, head) \
+ for (pos = (head)->next; pos != (head); \
+ pos = pos->next, ({ smp_read_barrier_depends(); 0;}))
+
+/**
+ * llist_for_each_safe_rcu - iterate over an rcu-protected llist safe
+ * against removal of llist entry
+ * @pos: the &struct llist_head to use as a loop counter.
+ * @n: another &struct llist_head to use as temporary storage
+ * @head: the head for your llist.
+ */
+#define llist_for_each_safe_rcu(pos, n, head) \
+ for (pos = (head)->next, n = pos->next; pos != (head); \
+ pos = n, ({ smp_read_barrier_depends(); 0;}), n = pos->next)
+
+/**
+ * llist_for_each_entry_rcu - iterate over rcu llist of given type
+ * @pos: the type * to use as a loop counter.
+ * @head: the head for your llist.
+ * @member: the name of the llist_struct within the struct.
+ */
+#define llist_for_each_entry_rcu(pos, head, member) \
+ for (pos = llist_entry((head)->next, typeof(*pos), member), \
+ prefetch(pos->member.next); \
+ &pos->member != (head); \
+ pos = llist_entry(pos->member.next, typeof(*pos), member), \
+ ({ smp_read_barrier_depends(); 0;}), \
+ prefetch(pos->member.next))
+
+
+/**
+ * llist_for_each_continue_rcu - iterate over an rcu-protected llist
+ * continuing after existing point.
+ * @pos: the &struct llist_head to use as a loop counter.
+ * @head: the head for your llist.
+ */
+#define llist_for_each_continue_rcu(pos, head) \
+ for ((pos) = (pos)->next, prefetch((pos)->next); (pos) != (head); \
+ (pos) = (pos)->next, ({ smp_read_barrier_depends(); 0;}), prefetch((pos)->next))
+
+/*! count nr of llist items by iterating.
+ * \param head The llist head to count items of.
+ * \returns Number of items.
+ *
+ * This function is not efficient, mostly useful for small lists and non time
+ * critical cases like unit tests.
+ */
+static inline unsigned int llist_count(struct llist_head *head)
+{
+ struct llist_head *entry;
+ unsigned int i = 0;
+ llist_for_each(entry, head)
+ i++;
+ return i;
+}
+
+/*!
+ * @}
+ */
diff --git a/lib/decoding/osmocom/core/panic.c b/lib/decoding/osmocom/core/panic.c
new file mode 100644
index 0000000..d545a60
--- /dev/null
+++ b/lib/decoding/osmocom/core/panic.c
@@ -0,0 +1,100 @@
+/*! \file panic.c
+ * Routines for panic handling. */
+/*
+ * (C) 2010 by Sylvain Munaut <tnt@246tNt.com>
+ *
+ * All Rights Reserved
+ *
+ * SPDX-License-Identifier: GPL-2.0+
+ *
+ * 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 2 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, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ */
+
+/*! \addtogroup utils
+ * @{
+ * \file panic.c */
+
+#include <osmocom/core/panic.h>
+//#include <osmocom/core/backtrace.h>
+
+//#include "../config.h"
+
+
+static osmo_panic_handler_t osmo_panic_handler = (void*)0;
+
+
+#ifndef PANIC_INFLOOP
+
+#include <stdio.h>
+#include <stdlib.h>
+
+static void osmo_panic_default(const char *fmt, va_list args)
+{
+ vfprintf(stderr, fmt, args);
+ //osmo_generate_backtrace();
+ abort();
+}
+
+#else
+
+static void osmo_panic_default(const char *fmt, va_list args)
+{
+ while (1);
+}
+
+#endif
+
+
+/*! Terminate the current program with a panic
+ *
+ * You can call this function in case some severely unexpected situation
+ * is detected and the program is supposed to terminate in a way that
+ * reports the fact that it terminates.
+ *
+ * The application can register a panic handler function using \ref
+ * osmo_set_panic_handler. If it doesn't, a default panic handler
+ * function is called automatically.
+ *
+ * The default function on most systems will generate a backtrace and
+ * then abort() the process.
+ */
+void osmo_panic(const char *fmt, ...)
+{
+ va_list args;
+
+ va_start(args, fmt);
+
+ if (osmo_panic_handler)
+ osmo_panic_handler(fmt, args);
+ else
+ osmo_panic_default(fmt, args);
+
+ va_end(args);
+}
+
+
+/*! Set the panic handler
+ * \param[in] h New panic handler function
+ *
+ * This changes the panic handling function from the currently active
+ * function to a new call-back function supplied by the caller.
+ */
+void osmo_set_panic_handler(osmo_panic_handler_t h)
+{
+ osmo_panic_handler = h;
+}
+
+/*! @} */
diff --git a/lib/decoding/osmocom/core/panic.h b/lib/decoding/osmocom/core/panic.h
new file mode 100644
index 0000000..2bb4240
--- /dev/null
+++ b/lib/decoding/osmocom/core/panic.h
@@ -0,0 +1,15 @@
+#pragma once
+
+/*! \addtogroup utils
+ * @{
+ * \file panic.h */
+
+#include <stdarg.h>
+
+/*! panic handler callback function type */
+typedef void (*osmo_panic_handler_t)(const char *fmt, va_list args);
+
+extern void osmo_panic(const char *fmt, ...);
+extern void osmo_set_panic_handler(osmo_panic_handler_t h);
+
+/*! @} */
diff --git a/lib/decoding/osmocom/core/utils.h b/lib/decoding/osmocom/core/utils.h
new file mode 100644
index 0000000..54c8216
--- /dev/null
+++ b/lib/decoding/osmocom/core/utils.h
@@ -0,0 +1,129 @@
+#pragma once
+
+#include <stdbool.h>
+
+//#include <osmocom/core/backtrace.h>
+//#include <osmocom/core/talloc.h>
+
+/*! \defgroup utils General-purpose utility functions
+ * @{
+ * \file utils.h */
+
+/*! Determine number of elements in an array of static size */
+#define ARRAY_SIZE(x) (sizeof(x) / sizeof((x)[0]))
+/*! Return the maximum of two specified values */
+#define OSMO_MAX(a, b) ((a) >= (b) ? (a) : (b))
+/*! Return the minimum of two specified values */
+#define OSMO_MIN(a, b) ((a) >= (b) ? (b) : (a))
+/*! Stringify the name of a macro x, e.g. an FSM event name.
+ * Note: if nested within another preprocessor macro, this will
+ * stringify the value of x instead of its name. */
+#define OSMO_STRINGIFY(x) #x
+/*! Stringify the value of a macro x, e.g. a port number. */
+#define OSMO_STRINGIFY_VAL(x) OSMO_STRINGIFY(x)
+/*! Make a value_string entry from an enum value name */
+#define OSMO_VALUE_STRING(x) { x, #x }
+/*! Number of bytes necessary to store given BITS */
+#define OSMO_BYTES_FOR_BITS(BITS) ((BITS + 8 - 1) / 8)
+
+#include <stdbool.h>
+#include <stdint.h>
+#include <stdio.h>
+
+#define __attribute__(_arg_)
+#define __deprecated__
+
+/*! A mapping between human-readable string and numeric value */
+struct value_string {
+ unsigned int value; /*!< numeric value */
+ const char *str; /*!< human-readable string */
+};
+
+//const char *get_value_string(const struct value_string *vs, uint32_t val);
+const char *get_value_string_or_null(const struct value_string *vs,
+ uint32_t val);
+
+int get_string_value(const struct value_string *vs, const char *str);
+
+char osmo_bcd2char(uint8_t bcd);
+/* only works for numbers in ascci */
+uint8_t osmo_char2bcd(char c);
+
+int osmo_hexparse(const char *str, uint8_t *b, int max_len);
+
+char *osmo_ubit_dump(const uint8_t *bits, unsigned int len);
+char *osmo_hexdump(const unsigned char *buf, int len);
+char *osmo_hexdump_nospc(const unsigned char *buf, int len);
+char *osmo_osmo_hexdump_nospc(const unsigned char *buf, int len) __attribute__((__deprecated__));
+
+#define osmo_static_assert(exp, name) typedef int dummy##name [(exp) ? 1 : -1] __attribute__((__unused__));
+
+void osmo_str2lower(char *out, const char *in);
+void osmo_str2upper(char *out, const char *in);
+
+#define OSMO_SNPRINTF_RET(ret, rem, offset, len) \
+do { \
+ len += ret; \
+ if (ret > rem) \
+ ret = rem; \
+ offset += ret; \
+ rem -= ret; \
+} while (0)
+
+/*! Helper macro to terminate when an assertion failes
+ * \param[in] exp Predicate to verify
+ * This function will generate a backtrace and terminate the program if
+ * the predicate evaluates to false (0).
+ */
+#define OSMO_ASSERT(exp) \
+ if (!(exp)) { \
+ fprintf(stderr, "Assert failed %s %s:%d\n", #exp, __BASE_FILE__, __LINE__); \
+ /*osmo_generate_backtrace(); \ */\
+ abort(); \
+ }
+
+/*! duplicate a string using talloc and release its prior content (if any)
+ * \param[in] ctx Talloc context to use for allocation
+ * \param[out] dst pointer to string, will be updated with ptr to new string
+ * \param[in] newstr String that will be copieed to newly allocated string */
+/*static inline void osmo_talloc_replace_string(void *ctx, char **dst, const char *newstr)*/
+/*{*/
+/* if (*dst)*/
+/* talloc_free(*dst);*/
+/* *dst = talloc_strdup(ctx, newstr);*/
+/*}*/
+
+/*! Append to a string and re-/allocate if necessary.
+ * \param[in] ctx Talloc context to use for initial allocation.
+ * \param[in,out] dest char* to re-/allocate and append to.
+ * \param[in] fmt printf-like string format.
+ * \param[in] args Arguments for fmt.
+ *
+ * \a dest may be passed in NULL, or a string previously allocated by talloc.
+ * If an existing string is passed in, it will remain associated with whichever
+ * ctx it was allocated before, regardless whether it matches \a ctx or not.
+ */
+/*#define osmo_talloc_asprintf(ctx, dest, fmt, args ...) \*/
+/* do { \*/
+/* if (!dest) \*/
+/* dest = talloc_asprintf(ctx, fmt, ## args); \*/
+/* else \*/
+/* dest = talloc_asprintf_append((char*)dest, fmt, ## args); \*/
+/* } while (0)*/
+
+int osmo_constant_time_cmp(const uint8_t *exp, const uint8_t *rel, const int count);
+uint64_t osmo_decode_big_endian(const uint8_t *data, size_t data_len);
+uint8_t *osmo_encode_big_endian(uint64_t value, size_t data_len);
+
+size_t osmo_strlcpy(char *dst, const char *src, size_t siz);
+
+bool osmo_is_hexstr(const char *str, int min_digits, int max_digits,
+ bool require_even);
+
+bool osmo_identifier_valid(const char *str);
+bool osmo_separated_identifiers_valid(const char *str, const char *sep_chars);
+
+const char *osmo_escape_str(const char *str, int len);
+const char *osmo_escape_str_buf(const char *str, int in_len, char *buf, size_t bufsize);
+
+/*! @} */
diff --git a/lib/decoding/osmocom/crypt/auth.h b/lib/decoding/osmocom/crypt/auth.h
new file mode 100644
index 0000000..5c45a5d
--- /dev/null
+++ b/lib/decoding/osmocom/crypt/auth.h
@@ -0,0 +1,111 @@
+#pragma once
+
+/*! \defgroup auth GSM/GPRS/3G Authentication
+ * @{
+ * \file auth.h */
+
+#include <stdint.h>
+
+#include <osmocom/core/linuxlist.h>
+#include <osmocom/core/utils.h>
+
+#define OSMO_A5_MAX_KEY_LEN_BYTES (128/8)
+#define OSMO_MILENAGE_IND_BITLEN_MAX 28
+
+/*! Authentication Type (GSM/UMTS) */
+enum osmo_sub_auth_type {
+ OSMO_AUTH_TYPE_NONE = 0x00,
+ OSMO_AUTH_TYPE_GSM = 0x01,
+ OSMO_AUTH_TYPE_UMTS = 0x02,
+};
+
+extern const struct value_string osmo_sub_auth_type_names[];
+/*static inline const char *osmo_sub_auth_type_name(enum osmo_sub_auth_type val)
+{ return get_value_string(osmo_sub_auth_type_names, val); }
+*/
+
+/*! Authentication Algorithm.
+ * See also osmo_auth_alg_name() and osmo_auth_alg_parse(). */
+enum osmo_auth_algo {
+ OSMO_AUTH_ALG_NONE,
+ OSMO_AUTH_ALG_COMP128v1,
+ OSMO_AUTH_ALG_COMP128v2,
+ OSMO_AUTH_ALG_COMP128v3,
+ OSMO_AUTH_ALG_XOR,
+ OSMO_AUTH_ALG_MILENAGE,
+ _OSMO_AUTH_ALG_NUM,
+};
+
+/*! permanent (secret) subscriber auth data */
+struct osmo_sub_auth_data {
+ enum osmo_sub_auth_type type;
+ enum osmo_auth_algo algo;
+ union {
+ struct {
+ uint8_t opc[16]; /*!< operator invariant value */
+ uint8_t k[16]; /*!< secret key of the subscriber */
+ uint8_t amf[2];
+ uint64_t sqn; /*!< sequence number (in: prev sqn; out: used sqn) */
+ int opc_is_op; /*!< is the OPC field OPC (0) or OP (1) ? */
+ unsigned int ind_bitlen; /*!< nr of bits not in SEQ, only SQN */
+ unsigned int ind; /*!< which IND slot to use an SQN from */
+ uint64_t sqn_ms; /*!< sqn from AUTS (output value only) */
+ } umts;
+ struct {
+ uint8_t ki[OSMO_A5_MAX_KEY_LEN_BYTES]; /*!< secret key */
+ } gsm;
+ } u;
+};
+
+/* data structure describing a computed auth vector, generated by AuC */
+struct osmo_auth_vector {
+ uint8_t rand[16]; /*!< random challenge */
+ uint8_t autn[16]; /*!< authentication nonce */
+ uint8_t ck[16]; /*!< ciphering key */
+ uint8_t ik[16]; /*!< integrity key */
+ uint8_t res[16]; /*!< authentication result */
+ uint8_t res_len; /*!< length (in bytes) of res */
+ uint8_t kc[8]; /*!< Kc for GSM encryption (A5) */
+ uint8_t sres[4]; /*!< authentication result for GSM */
+ uint32_t auth_types; /*!< bitmask of OSMO_AUTH_TYPE_* */
+};
+
+/* An implementation of an authentication algorithm */
+struct osmo_auth_impl {
+ struct llist_head list;
+ enum osmo_auth_algo algo; /*!< algorithm we implement */
+ const char *name; /*!< name of the implementation */
+ unsigned int priority; /*!< priority value (resp. othe implementations */
+
+ /*! callback for generate authentication vectors */
+ int (*gen_vec)(struct osmo_auth_vector *vec,
+ struct osmo_sub_auth_data *aud,
+ const uint8_t *_rand);
+
+ /* callback for generationg auth vectors + re-sync */
+ int (*gen_vec_auts)(struct osmo_auth_vector *vec,
+ struct osmo_sub_auth_data *aud,
+ const uint8_t *auts, const uint8_t *rand_auts,
+ const uint8_t *_rand);
+};
+
+int osmo_auth_gen_vec(struct osmo_auth_vector *vec,
+ struct osmo_sub_auth_data *aud, const uint8_t *_rand);
+
+int osmo_auth_gen_vec_auts(struct osmo_auth_vector *vec,
+ struct osmo_sub_auth_data *aud,
+ const uint8_t *auts, const uint8_t *rand_auts,
+ const uint8_t *_rand);
+
+int osmo_auth_register(struct osmo_auth_impl *impl);
+
+int osmo_auth_load(const char *path);
+
+int osmo_auth_supported(enum osmo_auth_algo algo);
+void osmo_c4(uint8_t *ck, const uint8_t *kc);
+const char *osmo_auth_alg_name(enum osmo_auth_algo alg);
+//enum osmo_auth_algo osmo_auth_alg_parse(const char *name);
+
+void osmo_auth_c3(uint8_t kc[], const uint8_t ck[], const uint8_t ik[]);
+
+/* @} */
diff --git a/lib/decoding/osmocom/gsm/CMakeLists.txt b/lib/decoding/osmocom/gsm/CMakeLists.txt
new file mode 100644
index 0000000..945d1c2
--- /dev/null
+++ b/lib/decoding/osmocom/gsm/CMakeLists.txt
@@ -0,0 +1,6 @@
+add_sources(
+a5.c
+auth_core.c
+gsm48_ie.c
+kasumi.c
+)
diff --git a/lib/decoding/osmocom/gsm/a5.c b/lib/decoding/osmocom/gsm/a5.c
new file mode 100644
index 0000000..9f4ede1
--- /dev/null
+++ b/lib/decoding/osmocom/gsm/a5.c
@@ -0,0 +1,446 @@
+/*
+ * Copyright (C) 2011 Sylvain Munaut <tnt@246tNt.com>
+ *
+ * All Rights Reserved
+ *
+ * SPDX-License-Identifier: GPL-2.0+
+ *
+ * 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 2 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, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+
+/*! \addtogroup a5
+ * @{
+ * Osmocom GSM ciphering algorithm implementation
+ *
+ * Full reimplementation of A5/1,2,3,4 (split and threadsafe).
+ *
+ * The logic behind the algorithm is taken from "A pedagogical implementation
+ * of the GSM A5/1 and A5/2 "voice privacy" encryption algorithms." by
+ * Marc Briceno, Ian Goldberg, and David Wagner.
+ */
+
+#include <errno.h>
+#include <string.h>
+#include <stdbool.h>
+
+#include <osmocom/gsm/a5.h>
+#include <osmocom/gsm/kasumi.h>
+//#include <osmocom/crypt/auth.h>
+
+/* Somme OS (like Nuttx) don't have ENOTSUP */
+#ifndef ENOTSUP
+#define ENOTSUP EINVAL
+#endif
+
+/* ------------------------------------------------------------------------ */
+/* A5/3&4 */
+/* ------------------------------------------------------------------------ */
+
+/*! Generate a GSM A5/4 cipher stream
+ * \param[in] key 16 byte array for the key (as received from the SIM)
+ * \param[in] fn Frame number
+ * \param[out] dl Pointer to array of ubits to return Downlink cipher stream
+ * \param[out] ul Pointer to array of ubits to return Uplink cipher stream
+ * \param[in] fn_correct true if fn is a real GSM frame number and thus requires internal conversion
+ *
+ * Either (or both) of dl/ul should be NULL if not needed.
+ *
+ * Implementation based on specifications from 3GPP TS 55.216, 3GPP TR 55.919 and ETSI TS 135 202
+ * with slight simplifications (CE hardcoded to 0).
+ */
+void
+_a5_4(const uint8_t *ck, uint32_t fn, ubit_t *dl, ubit_t *ul, bool fn_correct)
+{
+ uint8_t i, gamma[32], uplink[15];
+ uint32_t fn_count = (fn_correct) ? osmo_a5_fn_count(fn) : fn;
+
+ if (ul) {
+ _kasumi_kgcore(0xF, 0, fn_count, 0, ck, gamma, 228);
+ for(i = 0; i < 15; i++) uplink[i] = (gamma[i + 14] << 2) + (gamma[i + 15] >> 6);
+ osmo_pbit2ubit(ul, uplink, 114);
+ }
+ if (dl) {
+ _kasumi_kgcore(0xF, 0, fn_count, 0, ck, gamma, 114);
+ osmo_pbit2ubit(dl, gamma, 114);
+ }
+}
+
+/*! Generate a GSM A5/3 cipher stream
+ * \param[in] key 8 byte array for the key (as received from the SIM)
+ * \param[in] fn Frame number
+ * \param[out] dl Pointer to array of ubits to return Downlink cipher stream
+ * \param[out] ul Pointer to array of ubits to return Uplink cipher stream
+ * \param[in] fn_correct true if fn is a real GSM frame number and thus requires internal conversion
+ *
+ * Either (or both) of dl/ul should be NULL if not needed.
+ *
+ * Implementation based on specifications from 3GPP TS 55.216, 3GPP TR 55.919 and ETSI TS 135 202
+ * with slight simplifications (CE hardcoded to 0).
+ */
+void
+_a5_3(const uint8_t *key, uint32_t fn, ubit_t *dl, ubit_t *ul, bool fn_correct)
+{
+ uint8_t ck[16];
+ osmo_c4(ck, key);
+ /* internal function require 128 bit key so we expand by concatenating supplied 64 bit key */
+ _a5_4(ck, fn, dl, ul, fn_correct);
+}
+
+/* ------------------------------------------------------------------------ */
+/* A5/1&2 common stuff */
+/* ------------------------------------------------------------------------ */
+
+#define A5_R1_LEN 19
+#define A5_R2_LEN 22
+#define A5_R3_LEN 23
+#define A5_R4_LEN 17 /* A5/2 only */
+
+#define A5_R1_MASK ((1<<A5_R1_LEN)-1)
+#define A5_R2_MASK ((1<<A5_R2_LEN)-1)
+#define A5_R3_MASK ((1<<A5_R3_LEN)-1)
+#define A5_R4_MASK ((1<<A5_R4_LEN)-1)
+
+#define A5_R1_TAPS 0x072000 /* x^19 + x^18 + x^17 + x^14 + 1 */
+#define A5_R2_TAPS 0x300000 /* x^22 + x^21 + 1 */
+#define A5_R3_TAPS 0x700080 /* x^23 + x^22 + x^21 + x^8 + 1 */
+#define A5_R4_TAPS 0x010800 /* x^17 + x^12 + 1 */
+
+/*! Computes parity of a 32-bit word
+ * \param[in] x 32 bit word
+ * \return Parity bit (xor of all bits) as 0 or 1
+ */
+static inline uint32_t
+_a5_12_parity(uint32_t x)
+{
+ x ^= x >> 16;
+ x ^= x >> 8;
+ x ^= x >> 4;
+ x &= 0xf;
+ return (0x6996 >> x) & 1;
+}
+
+/*! Compute majority bit from 3 taps
+ * \param[in] v1 LFSR state ANDed with tap-bit
+ * \param[in] v2 LFSR state ANDed with tap-bit
+ * \param[in] v3 LFSR state ANDed with tap-bit
+ * \return The majority bit (0 or 1)
+ */
+static inline uint32_t
+_a5_12_majority(uint32_t v1, uint32_t v2, uint32_t v3)
+{
+ return (!!v1 + !!v2 + !!v3) >= 2;
+}
+
+/*! Compute the next LFSR state
+ * \param[in] r Current state
+ * \param[in] mask LFSR mask
+ * \param[in] taps LFSR taps
+ * \return Next state
+ */
+static inline uint32_t
+_a5_12_clock(uint32_t r, uint32_t mask, uint32_t taps)
+{
+ return ((r << 1) & mask) | _a5_12_parity(r & taps);
+}
+
+
+/* ------------------------------------------------------------------------ */
+/* A5/1 */
+/* ------------------------------------------------------------------------ */
+
+#define A51_R1_CLKBIT 0x000100
+#define A51_R2_CLKBIT 0x000400
+#define A51_R3_CLKBIT 0x000400
+
+/*! GSM A5/1 Clocking function
+ * \param[in] r Register state
+ * \param[in] force Non-zero value disable conditional clocking
+ */
+static inline void
+_a5_1_clock(uint32_t r[], int force)
+{
+ int cb[3], maj;
+
+ cb[0] = !!(r[0] & A51_R1_CLKBIT);
+ cb[1] = !!(r[1] & A51_R2_CLKBIT);
+ cb[2] = !!(r[2] & A51_R3_CLKBIT);
+
+ maj = _a5_12_majority(cb[0], cb[1], cb[2]);
+
+ if (force || (maj == cb[0]))
+ r[0] = _a5_12_clock(r[0], A5_R1_MASK, A5_R1_TAPS);
+
+ if (force || (maj == cb[1]))
+ r[1] = _a5_12_clock(r[1], A5_R2_MASK, A5_R2_TAPS);
+
+ if (force || (maj == cb[2]))
+ r[2] = _a5_12_clock(r[2], A5_R3_MASK, A5_R3_TAPS);
+}
+
+/*! GSM A5/1 Output function
+ * \param[in] r Register state
+ * \return The A5/1 output function bit
+ */
+static inline uint8_t
+_a5_1_get_output(uint32_t r[])
+{
+ return (r[0] >> (A5_R1_LEN-1)) ^
+ (r[1] >> (A5_R2_LEN-1)) ^
+ (r[2] >> (A5_R3_LEN-1));
+}
+
+/*! Generate a GSM A5/1 cipher stream
+ * \param[in] key 8 byte array for the key (as received from the SIM)
+ * \param[in] fn Frame number
+ * \param[out] dl Pointer to array of ubits to return Downlink cipher stream
+ * \param[out] ul Pointer to array of ubits to return Uplink cipher stream
+ *
+ * Either (or both) of dl/ul can be NULL if not needed.
+ */
+void
+_a5_1(const uint8_t *key, uint32_t fn, ubit_t *dl, ubit_t *ul)
+{
+ uint32_t r[3] = {0, 0, 0};
+ uint32_t fn_count;
+ uint32_t b;
+ int i;
+
+ /* Key load */
+ for (i=0; i<64; i++)
+ {
+ b = ( key[7 - (i>>3)] >> (i&7) ) & 1;
+
+ _a5_1_clock(r, 1);
+
+ r[0] ^= b;
+ r[1] ^= b;
+ r[2] ^= b;
+ }
+
+ /* Frame count load */
+ fn_count = osmo_a5_fn_count(fn);
+
+ for (i=0; i<22; i++)
+ {
+ b = (fn_count >> i) & 1;
+
+ _a5_1_clock(r, 1);
+
+ r[0] ^= b;
+ r[1] ^= b;
+ r[2] ^= b;
+ }
+
+ /* Mix */
+ for (i=0; i<100; i++)
+ {
+ _a5_1_clock(r, 0);
+ }
+
+ /* Output */
+ for (i=0; i<114; i++) {
+ _a5_1_clock(r, 0);
+ if (dl)
+ dl[i] = _a5_1_get_output(r);
+ }
+
+ for (i=0; i<114; i++) {
+ _a5_1_clock(r, 0);
+ if (ul)
+ ul[i] = _a5_1_get_output(r);
+ }
+}
+
+void osmo_a5_1(const uint8_t *key, uint32_t fn, ubit_t *dl, ubit_t *ul)
+{
+ osmo_a5(1, key, fn, dl, ul);
+}
+
+/* ------------------------------------------------------------------------ */
+/* A5/2 */
+/* ------------------------------------------------------------------------ */
+
+#define A52_R4_CLKBIT0 0x000400
+#define A52_R4_CLKBIT1 0x000008
+#define A52_R4_CLKBIT2 0x000080
+
+/*! GSM A5/2 Clocking function
+ * \param[in] r Register state
+ * \param[in] force Non-zero value disable conditional clocking
+ */
+static inline void
+_a5_2_clock(uint32_t r[], int force)
+{
+ int cb[3], maj;
+
+ cb[0] = !!(r[3] & A52_R4_CLKBIT0);
+ cb[1] = !!(r[3] & A52_R4_CLKBIT1);
+ cb[2] = !!(r[3] & A52_R4_CLKBIT2);
+
+ maj = (cb[0] + cb[1] + cb[2]) >= 2;
+
+ if (force || (maj == cb[0]))
+ r[0] = _a5_12_clock(r[0], A5_R1_MASK, A5_R1_TAPS);
+
+ if (force || (maj == cb[1]))
+ r[1] = _a5_12_clock(r[1], A5_R2_MASK, A5_R2_TAPS);
+
+ if (force || (maj == cb[2]))
+ r[2] = _a5_12_clock(r[2], A5_R3_MASK, A5_R3_TAPS);
+
+ r[3] = _a5_12_clock(r[3], A5_R4_MASK, A5_R4_TAPS);
+}
+
+/*! GSM A5/2 Output function
+ * \param[in] r Register state
+ * \return The A5/2 output function bit
+ */
+static inline uint8_t
+_a5_2_get_output(uint32_t r[])
+{
+ uint8_t b;
+
+ b = (r[0] >> (A5_R1_LEN-1)) ^
+ (r[1] >> (A5_R2_LEN-1)) ^
+ (r[2] >> (A5_R3_LEN-1)) ^
+ _a5_12_majority( r[0] & 0x08000, ~r[0] & 0x04000, r[0] & 0x1000) ^
+ _a5_12_majority(~r[1] & 0x10000, r[1] & 0x02000, r[1] & 0x0200) ^
+ _a5_12_majority( r[2] & 0x40000, r[2] & 0x10000, ~r[2] & 0x2000);
+
+ return b;
+}
+
+/*! Generate a GSM A5/1 cipher stream
+ * \param[in] key 8 byte array for the key (as received from the SIM)
+ * \param[in] fn Frame number
+ * \param[out] dl Pointer to array of ubits to return Downlink cipher stream
+ * \param[out] ul Pointer to array of ubits to return Uplink cipher stream
+ *
+ * Either (or both) of dl/ul can be NULL if not needed.
+ */
+void
+_a5_2(const uint8_t *key, uint32_t fn, ubit_t *dl, ubit_t *ul)
+{
+ uint32_t r[4] = {0, 0, 0, 0};
+ uint32_t fn_count;
+ uint32_t b;
+ int i;
+
+ /* Key load */
+ for (i=0; i<64; i++)
+ {
+ b = ( key[7 - (i>>3)] >> (i&7) ) & 1;
+
+ _a5_2_clock(r, 1);
+
+ r[0] ^= b;
+ r[1] ^= b;
+ r[2] ^= b;
+ r[3] ^= b;
+ }
+
+ /* Frame count load */
+ fn_count = osmo_a5_fn_count(fn);
+
+ for (i=0; i<22; i++)
+ {
+ b = (fn_count >> i) & 1;
+
+ _a5_2_clock(r, 1);
+
+ r[0] ^= b;
+ r[1] ^= b;
+ r[2] ^= b;
+ r[3] ^= b;
+ }
+
+ r[0] |= 1 << 15;
+ r[1] |= 1 << 16;
+ r[2] |= 1 << 18;
+ r[3] |= 1 << 10;
+
+ /* Mix */
+ for (i=0; i<99; i++)
+ {
+ _a5_2_clock(r, 0);
+ }
+
+ /* Output */
+ for (i=0; i<114; i++) {
+ _a5_2_clock(r, 0);
+ if (dl)
+ dl[i] = _a5_2_get_output(r);
+ }
+
+ for (i=0; i<114; i++) {
+ _a5_2_clock(r, 0);
+ if (ul)
+ ul[i] = _a5_2_get_output(r);
+ }
+}
+
+void osmo_a5_2(const uint8_t *key, uint32_t fn, ubit_t *dl, ubit_t *ul)
+{
+ osmo_a5(2, key, fn, dl, ul);
+}
+
+/*! Main method to generate a A5/x cipher stream
+ * \param[in] n Which A5/x method to use
+ * \param[in] key 8 or 16 (for a5/4) byte array for the key (as received from the SIM)
+ * \param[in] fn Frame number
+ * \param[out] dl Pointer to array of ubits to return Downlink cipher stream
+ * \param[out] ul Pointer to array of ubits to return Uplink cipher stream
+ * \returns 0 for success, -ENOTSUP for invalid cipher selection.
+ *
+ * Currently A5/[0-4] are supported.
+ * Either (or both) of dl/ul can be NULL if not needed.
+ */
+int
+osmo_a5(int n, const uint8_t *key, uint32_t fn, ubit_t *dl, ubit_t *ul)
+{
+ switch (n)
+ {
+ case 0:
+ if (dl)
+ memset(dl, 0x00, 114);
+ if (ul)
+ memset(ul, 0x00, 114);
+ break;
+
+ case 1:
+ _a5_1(key, fn, dl, ul);
+ break;
+
+ case 2:
+ _a5_2(key, fn, dl, ul);
+ break;
+
+ case 3:
+ _a5_3(key, fn, dl, ul, true);
+ break;
+
+ case 4:
+ _a5_4(key, fn, dl, ul, true);
+ break;
+
+ default:
+ /* a5/[5..7] not supported here/yet */
+ return -ENOTSUP;
+ }
+
+ return 0;
+}
+
+/*! @} */
diff --git a/lib/decoding/osmocom/gsm/a5.h b/lib/decoding/osmocom/gsm/a5.h
new file mode 100644
index 0000000..fa63246
--- /dev/null
+++ b/lib/decoding/osmocom/gsm/a5.h
@@ -0,0 +1,55 @@
+/*
+ * Copyright (C) 2011 Sylvain Munaut <tnt@246tNt.com>
+ *
+ * All Rights Reserved
+ *
+ * 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 2 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, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+
+#pragma once
+
+#include <stdint.h>
+
+#include <osmocom/core/defs.h>
+#include <osmocom/core/bits.h>
+
+/*! \defgroup a5 GSM A5 ciphering algorithm
+ * @{
+ * \file a5.h */
+
+/*! Converts a frame number into the 22 bit number used in A5/x
+ * \param[in] fn The true framenumber
+ * \return 22 bit word
+ */
+static inline uint32_t
+osmo_a5_fn_count(uint32_t fn)
+{
+ int t1 = fn / (26 * 51);
+ int t2 = fn % 26;
+ int t3 = fn % 51;
+ return (t1 << 11) | (t3 << 5) | t2;
+}
+
+ /* Notes:
+ * - key must be 8 or 16 (for a5/4) bytes long (or NULL for A5/0)
+ * - the dl and ul pointer must be either NULL or 114 bits long
+ * - fn is the _real_ GSM frame number.
+ * (converted internally to fn_count)
+ */
+int osmo_a5(int n, const uint8_t *key, uint32_t fn, ubit_t *dl, ubit_t *ul);
+void osmo_a5_1(const uint8_t *key, uint32_t fn, ubit_t *dl, ubit_t *ul) OSMO_DEPRECATED("Use generic osmo_a5() instead");
+void osmo_a5_2(const uint8_t *key, uint32_t fn, ubit_t *dl, ubit_t *ul) OSMO_DEPRECATED("Use generic osmo_a5() instead");
+
+/*! @} */
diff --git a/lib/decoding/osmocom/gsm/auth_core.c b/lib/decoding/osmocom/gsm/auth_core.c
new file mode 100644
index 0000000..230500e
--- /dev/null
+++ b/lib/decoding/osmocom/gsm/auth_core.c
@@ -0,0 +1,252 @@
+/* (C) 2010-2012 by Harald Welte <laforge@gnumonks.org>
+ *
+ * All Rights Reserved
+ *
+ * SPDX-License-Identifier: GPL-2.0+
+ *
+ * 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 2 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, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ */
+
+//#include "config.h"
+
+#include <errno.h>
+#include <stdint.h>
+#include <string.h>
+
+#include <osmocom/core/utils.h>
+#include <osmocom/core/linuxlist.h>
+//#include <osmocom/core/plugin.h>
+
+#include <osmocom/crypt/auth.h>
+
+/*! \addtogroup auth
+ * @{
+ * GSM/GPRS/3G authentication core infrastructure
+ *
+ * \file auth_core.c */
+
+static LLIST_HEAD(osmo_auths);
+
+static struct osmo_auth_impl *selected_auths[_OSMO_AUTH_ALG_NUM];
+
+/*! Register an authentication algorithm implementation with the core
+ * \param[in] impl Structure describing implementation and it's callbacks
+ * \returns 0 on success, or a negative error code on failure
+ *
+ * This function is called by an authentication implementation plugin to
+ * register itself with the authentication core.
+ */
+int osmo_auth_register(struct osmo_auth_impl *impl)
+{
+ if (impl->algo >= ARRAY_SIZE(selected_auths))
+ return -ERANGE;
+
+ llist_add_tail(&impl->list, &osmo_auths);
+
+ /* check if we want to select this implementation over others */
+ if (!selected_auths[impl->algo] ||
+ (selected_auths[impl->algo]->priority > impl->priority))
+ selected_auths[impl->algo] = impl;
+
+ return 0;
+}
+
+/*! Load all available authentication plugins from the given path
+ * \param[in] path Path name of the directory containing the plugins
+ * \returns number of plugins loaded in case of success, negative in case of error
+ *
+ * This function will load all plugins contained in the specified path.
+ */
+int osmo_auth_load(const char *path)
+{
+ /* load all plugins available from path */
+/*#if !defined(EMBEDDED)
+ return osmo_plugin_load_all(path);
+#else*/
+ return -1;
+/*#endif*/
+}
+
+/*! Determine if a given authentication algorithm is supported
+ * \param[in] algo Algorithm which should be checked
+ * \returns 1 if algo is supported, 0 if not, negative error on failure
+ *
+ * This function is used by an application to determine at runtime if a
+ * given authentication algorithm is supported or not.
+ */
+int osmo_auth_supported(enum osmo_auth_algo algo)
+{
+ if (algo >= ARRAY_SIZE(selected_auths))
+ return -ERANGE;
+
+ if (selected_auths[algo])
+ return 1;
+
+ return 0;
+}
+
+/* C5 function to derive UMTS IK from GSM Kc */
+static inline void c5_function(uint8_t *ik, const uint8_t *kc)
+{
+ unsigned int i;
+
+ for (i = 0; i < 4; i++)
+ ik[i] = kc[i] ^ kc[i+4];
+ memcpy(ik+4, kc, 8);
+ for (i = 12; i < 16; i++)
+ ik[i] = ik[i-12];
+}
+
+/* C4 function to derive UMTS CK from GSM Kc */
+void osmo_c4(uint8_t *ck, const uint8_t *kc)
+{
+ memcpy(ck, kc, 8);
+ memcpy(ck+8, kc, 8);
+}
+
+/*! Generate 3G CK + IK from 2G authentication vector
+ * \param vec Authentication Vector to be modified
+ * \returns 1 if the vector was changed, 0 otherwise
+ *
+ * This function performs the C5 and C4 functions to derive the UMTS key
+ * material from the GSM key material in the supplied vector, _if_ the input
+ * vector doesn't yet have UMTS authentication capability.
+ */
+int osmo_auth_3g_from_2g(struct osmo_auth_vector *vec)
+{
+ if ((vec->auth_types & OSMO_AUTH_TYPE_GSM) &&
+ !(vec->auth_types & OSMO_AUTH_TYPE_UMTS)) {
+ c5_function(vec->ik, vec->kc);
+ osmo_c4(vec->ck, vec->kc);
+ /* We cannot actually set OSMO_AUTH_TYPE_UMTS as we have no
+ * AUTN and no RES, and thus can only perform GSM
+ * authentication with this tuple.
+ */
+ return 1;
+ }
+
+ return 0;
+}
+
+/*! Generate authentication vector
+ * \param[out] vec Generated authentication vector
+ * \param[in] aud Subscriber-specific key material
+ * \param[in] _rand Random challenge to be used
+ * \returns 0 on success, negative error on failure
+ *
+ * This function performs the core cryptographic function of the AUC,
+ * computing authentication triples/quintuples based on the permanent
+ * subscriber data and a random value. The result is what is forwarded
+ * by the AUC via HLR and VLR to the MSC which will then be able to
+ * invoke authentication with the MS
+ */
+int osmo_auth_gen_vec(struct osmo_auth_vector *vec,
+ struct osmo_sub_auth_data *aud,
+ const uint8_t *_rand)
+{
+ struct osmo_auth_impl *impl = selected_auths[aud->algo];
+ int rc;
+
+ if (!impl)
+ return -ENOENT;
+
+ rc = impl->gen_vec(vec, aud, _rand);
+ if (rc < 0)
+ return rc;
+
+ memcpy(vec->rand, _rand, sizeof(vec->rand));
+
+ return 0;
+}
+
+/*! Generate authentication vector and re-sync sequence
+ * \param[out] vec Generated authentication vector
+ * \param[in] aud Subscriber-specific key material
+ * \param[in] auts AUTS value sent by the SIM/MS
+ * \param[in] rand_auts RAND value sent by the SIM/MS
+ * \param[in] _rand Random challenge to be used to generate vector
+ * \returns 0 on success, negative error on failure
+ *
+ * This function performs a special variant of the core cryptographic
+ * function of the AUC: computing authentication triples/quintuples
+ * based on the permanent subscriber data, a random value as well as the
+ * AUTS and RAND values returned by the SIM/MS. This special variant is
+ * needed if the sequence numbers between MS and AUC have for some
+ * reason become different.
+ */
+int osmo_auth_gen_vec_auts(struct osmo_auth_vector *vec,
+ struct osmo_sub_auth_data *aud,
+ const uint8_t *auts, const uint8_t *rand_auts,
+ const uint8_t *_rand)
+{
+ struct osmo_auth_impl *impl = selected_auths[aud->algo];
+ int rc;
+
+ if (!impl || !impl->gen_vec_auts)
+ return -ENOENT;
+
+ rc = impl->gen_vec_auts(vec, aud, auts, rand_auts, _rand);
+ if (rc < 0)
+ return rc;
+
+ memcpy(vec->rand, _rand, sizeof(vec->rand));
+
+ return 0;
+}
+
+static const struct value_string auth_alg_vals[] = {
+ { OSMO_AUTH_ALG_NONE, "None" },
+ { OSMO_AUTH_ALG_COMP128v1, "COMP128v1" },
+ { OSMO_AUTH_ALG_COMP128v2, "COMP128v2" },
+ { OSMO_AUTH_ALG_COMP128v3, "COMP128v3" },
+ { OSMO_AUTH_ALG_XOR, "XOR" },
+ { OSMO_AUTH_ALG_MILENAGE, "MILENAGE" },
+ { 0, NULL }
+};
+
+/*! Get human-readable name of authentication algorithm *
+const char *osmo_auth_alg_name(enum osmo_auth_algo alg)
+{
+ return get_value_string(auth_alg_vals, alg);
+}
+
+/*! Parse human-readable name of authentication algorithm */
+/*enum osmo_auth_algo osmo_auth_alg_parse(const char *name)
+{
+ return get_string_value(auth_alg_vals, name);
+}
+*/
+const struct value_string osmo_sub_auth_type_names[] = {
+ { OSMO_AUTH_TYPE_NONE, "None" },
+ { OSMO_AUTH_TYPE_GSM, "GSM" },
+ { OSMO_AUTH_TYPE_UMTS, "UMTS" },
+ { 0, NULL }
+};
+
+/* Derive GSM AKA ciphering key Kc from UMTS AKA CK and IK (auth function c3 from 3GPP TS 33.103 §
+ * 4.6.1).
+ * \param[out] kc GSM AKA Kc, 8 byte target buffer.
+ * \param[in] ck UMTS AKA CK, 16 byte input buffer.
+ * \param[in] ik UMTS AKA IK, 16 byte input buffer.
+ */
+void osmo_auth_c3(uint8_t kc[], const uint8_t ck[], const uint8_t ik[])
+{
+ int i;
+ for (i = 0; i < 8; i++)
+ kc[i] = ck[i] ^ ck[i + 8] ^ ik[i] ^ ik[i + 8];
+}
+
+/*! @} */
diff --git a/lib/decoding/osmocom/gsm/gsm0503.h b/lib/decoding/osmocom/gsm/gsm0503.h
new file mode 100644
index 0000000..44df4ad
--- /dev/null
+++ b/lib/decoding/osmocom/gsm/gsm0503.h
@@ -0,0 +1,285 @@
+
+/*
+ * Copyright (C) 2011-2016 Sylvain Munaut <tnt@246tNt.com>
+ * Copyright (C) 2016 sysmocom s.f.m.c. GmbH
+ *
+ * All Rights Reserved
+ *
+ * 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, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+
+#pragma once
+
+#include <stdint.h>
+#include <osmocom/core/conv.h>
+
+/*! structure describing xCCH convolutional code:.
+ * 228 bits blocks, rate 1/2, k = 5
+ * G0 = 1 + D3 + D4
+ * G1 = 1 + D + D3 + D4
+ */
+extern const struct osmo_conv_code gsm0503_xcch;
+
+/*! structure describing RACH convolutional code.
+ */
+extern const struct osmo_conv_code gsm0503_rach;
+
+/*! structure describing Extended RACH (11 bit) convolutional code.
+ */
+//extern const struct osmo_conv_code gsm0503_rach_ext;
+
+/*! structure describing SCH convolutional code.
+ */
+extern const struct osmo_conv_code gsm0503_sch;
+
+/*! structure describing CS2 convolutional code:.
+ * G0 = 1 + D3 + D4
+ * G1 = 1 + D + D3 + D4
+ */
+extern const struct osmo_conv_code gsm0503_cs2;
+
+/*! structure describing CS3 convolutional code:.
+ * G0 = 1 + D3 + D4
+ * G1 = 1 + D + D3 + D4
+ */
+extern const struct osmo_conv_code gsm0503_cs3;
+
+/*! structure describing CS2 convolutional code (non-punctured):.
+ * G0 = 1 + D3 + D4
+ * G1 = 1 + D + D3 + D4
+ */
+//extern const struct osmo_conv_code gsm0503_cs2_np;
+
+/*! structure describing CS3 convolutional code (non-punctured):.
+ * G0 = 1 + D3 + D4
+ * G1 = 1 + D + D3 + D4
+ */
+//extern const struct osmo_conv_code gsm0503_cs3_np;
+
+/*! structure describing TCH/AFS 12.2 kbits convolutional code:.
+ * 250 bits block, rate 1/2, punctured
+ * G0/G0 = 1
+ * G1/G0 = 1 + D + D3 + D4 / 1 + D3 + D4
+ */
+extern const struct osmo_conv_code gsm0503_tch_afs_12_2;
+
+/*! structure describing TCH/AFS 10.2 kbits convolutional code:.
+ * G1/G3 = 1 + D + D3 + D4 / 1 + D + D2 + D3 + D4
+ * G2/G3 = 1 + D2 + D4 / 1 + D + D2 + D3 + D4
+ * G3/G3 = 1
+ */
+extern const struct osmo_conv_code gsm0503_tch_afs_10_2;
+
+/*! structure describing TCH/AFS 7.95 kbits convolutional code:.
+ * G4/G4 = 1
+ * G5/G4 = 1 + D + D4 + D6 / 1 + D2 + D3 + D5 + D6
+ * G6/G4 = 1 + D + D2 + D3 + D4 + D6 / 1 + D2 + D3 + D5 + D6
+ */
+extern const struct osmo_conv_code gsm0503_tch_afs_7_95;
+
+/*! structure describing TCH/AFS 7.4 kbits convolutional code:.
+ * G1/G3 = 1 + D + D3 + D4 / 1 + D + D2 + D3 + D4
+ * G2/G3 = 1 + D2 + D4 / 1 + D + D2 + D3 + D4
+ * G3/G3 = 1
+ */
+extern const struct osmo_conv_code gsm0503_tch_afs_7_4;
+
+/*! structure describing TCH/AFS 6.7 kbits convolutional code:.
+ * G1/G3 = 1 + D + D3 + D4 / 1 + D + D2 + D3 + D4
+ * G2/G3 = 1 + D2 + D4 / 1 + D + D2 + D3 + D4
+ * G3/G3 = 1
+ * G3/G3 = 1
+ */
+extern const struct osmo_conv_code gsm0503_tch_afs_6_7;
+
+/*! structure describing TCH/AFS 5.9 kbits convolutional code:.
+ * 124 bits
+ * G4/G6 = 1 + D2 + D3 + D5 + D6 / 1 + D + D2 + D3 + D4 + D6
+ * G5/G6 = 1 + D + D4 + D6 / 1 + D + D2 + D3 + D4 + D6
+ * G6/G6 = 1
+ * G6/G6 = 1
+ */
+extern const struct osmo_conv_code gsm0503_tch_afs_5_9;
+
+/*! structure describing TCH/AFS 5.15 kbits convolutional code:.
+ * G1/G3 = 1 + D + D3 + D4 / 1 + D + D2 + D3 + D4
+ * G1/G3 = 1 + D + D3 + D4 / 1 + D + D2 + D3 + D4
+ * G2/G3 = 1 + D2 + D4 / 1 + D + D2 + D3 + D4
+ * G3/G3 = 1
+ * G3/G3 = 1
+ */
+extern const struct osmo_conv_code gsm0503_tch_afs_5_15;
+
+/*! structure describing TCH/AFS 4.75 kbits convolutional code:.
+ * G4/G6 = 1 + D2 + D3 + D5 + D6 / 1 + D + D2 + D3 + D4 + D6
+ * G4/G6 = 1 + D2 + D3 + D5 + D6 / 1 + D + D2 + D3 + D4 + D6
+ * G5/G6 = 1 + D + D4 + D6 / 1 + D + D2 + D3 + D4 + D6
+ * G6/G6 = 1
+ * G6/G6 = 1
+ */
+extern const struct osmo_conv_code gsm0503_tch_afs_4_75;
+
+/*! structure describing TCH/F convolutional code.
+ */
+extern const struct osmo_conv_code gsm0503_tch_fr;
+
+/*! structure describing TCH/H convolutional code.
+ */
+extern const struct osmo_conv_code gsm0503_tch_hr;
+
+/*! structure describing TCH/AHS 7.95 kbits convolutional code.
+ */
+extern const struct osmo_conv_code gsm0503_tch_ahs_7_95;
+
+/*! structure describing TCH/AHS 7.4 kbits convolutional code.
+ */
+extern const struct osmo_conv_code gsm0503_tch_ahs_7_4;
+
+/*! structure describing TCH/AHS 6.7 kbits convolutional code.
+ */
+extern const struct osmo_conv_code gsm0503_tch_ahs_6_7;
+
+/*! structure describing TCH/AHS 5.9 kbits convolutional code.
+ */
+extern const struct osmo_conv_code gsm0503_tch_ahs_5_9;
+
+/*! structure describing TCH/AHS 5.15 kbits convolutional code.
+ */
+extern const struct osmo_conv_code gsm0503_tch_ahs_5_15;
+
+/*! structure describing TCH/AHS 4.75 kbits convolutional code.
+ */
+extern const struct osmo_conv_code gsm0503_tch_ahs_4_75;
+
+/*! structure describing EDGE MCS-1 DL header convolutional code:.
+ * 42 bits blocks, rate 1/3, k = 7
+ * G4 = 1 + D2 + D3 + D5 + D6
+ * G7 = 1 + D + D2 + D3 + D6
+ * G5 = 1 + D + D4 + D6
+ */
+extern const struct osmo_conv_code gsm0503_mcs1_dl_hdr;
+
+/*! structure describing EDGE MCS-1 UL header convolutional code:.
+ * 45 bits blocks, rate 1/3, k = 7
+ * G4 = 1 + D2 + D3 + D5 + D6
+ * G7 = 1 + D + D2 + D3 + D6
+ * G5 = 1 + D + D4 + D6
+ */
+extern const struct osmo_conv_code gsm0503_mcs1_ul_hdr;
+
+/*! structure describing EDGE MCS-1 data convolutional code:.
+ * 196 bits blocks, rate 1/3, k = 7
+ * G4 = 1 + D2 + D3 + D5 + D6
+ * G7 = 1 + D + D2 + D3 + D6
+ * G5 = 1 + D + D4 + D6
+ */
+extern const struct osmo_conv_code gsm0503_mcs1;
+
+/*! structure describing EDGE MCS-2 data convolutional code:.
+ * 244 bits blocks, rate 1/3, k = 7
+ * G4 = 1 + D2 + D3 + D5 + D6
+ * G7 = 1 + D + D2 + D3 + D6
+ * G5 = 1 + D + D4 + D6
+ */
+extern const struct osmo_conv_code gsm0503_mcs2;
+
+/*! structure describing EDGE MCS-3 data convolutional code:.
+ * 316 bits blocks, rate 1/3, k = 7
+ * G4 = 1 + D2 + D3 + D5 + D6
+ * G7 = 1 + D + D2 + D3 + D6
+ * G5 = 1 + D + D4 + D6
+ */
+extern const struct osmo_conv_code gsm0503_mcs3;
+
+/*! structure describing EDGE MCS-4 data convolutional code:.
+ * 372 bits blocks, rate 1/3, k = 7
+ * G4 = 1 + D2 + D3 + D5 + D6
+ * G7 = 1 + D + D2 + D3 + D6
+ * G5 = 1 + D + D4 + D6
+ */
+extern const struct osmo_conv_code gsm0503_mcs4;
+
+/*! structure describing EDGE MCS-5 DL header convolutional code:.
+ * 39 bits blocks, rate 1/3, k = 7
+ * G4 = 1 + D2 + D3 + D5 + D6
+ * G7 = 1 + D + D2 + D3 + D6
+ * G5 = 1 + D + D4 + D6
+ */
+extern const struct osmo_conv_code gsm0503_mcs5_dl_hdr;
+
+/*! structure describing EDGE MCS-5 UL header convolutional code:.
+ * 51 bits blocks, rate 1/3, k = 7
+ * G4 = 1 + D2 + D3 + D5 + D6
+ * G7 = 1 + D + D2 + D3 + D6
+ * G5 = 1 + D + D4 + D6
+ */
+extern const struct osmo_conv_code gsm0503_mcs5_ul_hdr;
+
+/*! structure describing EDGE MCS-5 data convolutional code:.
+ * 468 bits blocks, rate 1/3, k = 7
+ * G4 = 1 + D2 + D3 + D5 + D6
+ * G7 = 1 + D + D2 + D3 + D6
+ * G5 = 1 + D + D4 + D6
+ */
+extern const struct osmo_conv_code gsm0503_mcs5;
+
+/*! structure describing EDGE MCS-6 data convolutional code:.
+ * 612 bits blocks, rate 1/3, k = 7
+ * G4 = 1 + D2 + D3 + D5 + D6
+ * G7 = 1 + D + D2 + D3 + D6
+ * G5 = 1 + D + D4 + D6
+ */
+extern const struct osmo_conv_code gsm0503_mcs6;
+
+/*! structure describing EDGE MCS-7 DL header convolutional code:.
+ * 51 bits blocks, rate 1/3, k = 7
+ * G4 = 1 + D2 + D3 + D5 + D6
+ * G7 = 1 + D + D2 + D3 + D6
+ * G5 = 1 + D + D4 + D6
+ */
+extern const struct osmo_conv_code gsm0503_mcs7_dl_hdr;
+
+/*! structure describing EDGE MCS-7 UL header convolutional code:.
+ * 60 bits blocks, rate 1/3, k = 7
+ * G4 = 1 + D2 + D3 + D5 + D6
+ * G7 = 1 + D + D2 + D3 + D6
+ * G5 = 1 + D + D4 + D6
+ */
+extern const struct osmo_conv_code gsm0503_mcs7_ul_hdr;
+
+/*! structure describing EDGE MCS-7 data convolutional code:.
+ * 468 bits blocks, rate 1/3, k = 7
+ * G4 = 1 + D2 + D3 + D5 + D6
+ * G7 = 1 + D + D2 + D3 + D6
+ * G5 = 1 + D + D4 + D6
+ */
+extern const struct osmo_conv_code gsm0503_mcs7;
+
+/*! structure describing EDGE MCS-8 data convolutional code:.
+ * 564 bits blocks, rate 1/3, k = 7
+ * G4 = 1 + D2 + D3 + D5 + D6
+ * G7 = 1 + D + D2 + D3 + D6
+ * G5 = 1 + D + D4 + D6
+ */
+extern const struct osmo_conv_code gsm0503_mcs8;
+
+/*! structure describing EDGE MCS-9 data convolutional code:.
+ * 612 bits blocks, rate 1/3, k = 7
+ * G4 = 1 + D2 + D3 + D5 + D6
+ * G7 = 1 + D + D2 + D3 + D6
+ * G5 = 1 + D + D4 + D6
+ */
+extern const struct osmo_conv_code gsm0503_mcs9;
+
diff --git a/lib/decoding/osmocom/gsm/gsm48_ie.c b/lib/decoding/osmocom/gsm/gsm48_ie.c
new file mode 100644
index 0000000..3881b18
--- /dev/null
+++ b/lib/decoding/osmocom/gsm/gsm48_ie.c
@@ -0,0 +1,1247 @@
+/*! \file gsm48_ie.c
+ * GSM Mobile Radio Interface Layer 3 messages.
+ * 3GPP TS 04.08 version 7.21.0 Release 1998 / ETSI TS 100 940 V7.21.0. */
+/*
+ * (C) 2008 by Harald Welte <laforge@gnumonks.org>
+ * (C) 2009-2010 by Andreas Eversberg
+ *
+ * All Rights Reserved
+ *
+ * SPDX-License-Identifier: GPL-2.0+
+ *
+ * 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 2 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, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ */
+
+
+#include <stdint.h>
+#include <string.h>
+#include <errno.h>
+
+#include <osmocom/core/utils.h>
+/*#include <osmocom/core/msgb.h>*/
+/* #include <osmocom/gsm/tlv.h> */
+/* #include <osmocom/gsm/mncc.h> */
+#include <osmocom/gsm/protocol/gsm_04_08.h>
+#include <osmocom/gsm/gsm48_ie.h>
+
+/*! \addtogroup gsm0408
+ * @{
+ */
+
+//static const char bcd_num_digits[] = {
+// '0', '1', '2', '3', '4', '5', '6', '7',
+// '8', '9', '*', '#', 'a', 'b', 'c', '\0'
+//};
+
+///*! decode a 'called/calling/connect party BCD number' as in 10.5.4.7
+// * \param[out] Caller-provided output buffer
+// * \param[in] bcd_lv Length-Value portion of to-be-decoded IE
+// * \param[in] h_len Length of an optional heder between L and V portion
+// * \returns - in case of success; negative on error */
+//int gsm48_decode_bcd_number(char *output, int output_len,
+// const uint8_t *bcd_lv, int h_len)
+//{
+// uint8_t in_len = bcd_lv[0];
+// int i;
+
+// for (i = 1 + h_len; i <= in_len; i++) {
+// /* lower nibble */
+// output_len--;
+// if (output_len <= 1)
+// break;
+// *output++ = bcd_num_digits[bcd_lv[i] & 0xf];
+
+// /* higher nibble */
+// output_len--;
+// if (output_len <= 1)
+// break;
+// *output++ = bcd_num_digits[bcd_lv[i] >> 4];
+// }
+// if (output_len >= 1)
+// *output++ = '\0';
+
+// return 0;
+//}
+
+///*! convert a single ASCII character to call-control BCD */
+//static int asc_to_bcd(const char asc)
+//{
+// int i;
+
+// for (i = 0; i < ARRAY_SIZE(bcd_num_digits); i++) {
+// if (bcd_num_digits[i] == asc)
+// return i;
+// }
+// return -EINVAL;
+//}
+
+///*! convert a ASCII phone number to 'called/calling/connect party BCD number'
+// * \param[out] bcd_lv Caller-provided output buffer
+// * \param[in] max_len Maximum Length of \a bcd_lv
+// * \param[in] h_len Length of an optional heder between L and V portion
+// * \param[in] input phone number as 0-terminated ASCII
+// * \returns number of bytes used in \a bcd_lv */
+//int gsm48_encode_bcd_number(uint8_t *bcd_lv, uint8_t max_len,
+// int h_len, const char *input)
+//{
+// int in_len = strlen(input);
+// int i;
+// uint8_t *bcd_cur = bcd_lv + 1 + h_len;
+
+// /* two digits per byte, plus type byte */
+// bcd_lv[0] = in_len/2 + h_len;
+// if (in_len % 2)
+// bcd_lv[0]++;
+
+// if (bcd_lv[0] > max_len)
+// return -EIO;
+
+// for (i = 0; i < in_len; i++) {
+// int rc = asc_to_bcd(input[i]);
+// if (rc < 0)
+// return rc;
+// if (i % 2 == 0)
+// *bcd_cur = rc;
+// else
+// *bcd_cur++ |= (rc << 4);
+// }
+// /* append padding nibble in case of odd length */
+// if (i % 2)
+// *bcd_cur++ |= 0xf0;
+
+// /* return how many bytes we used */
+// return (bcd_cur - bcd_lv);
+//}
+
+///*! Decode TS 04.08 Bearer Capability IE (10.5.4.5)
+// * \param[out] Caller-provided memory for decoded output
+// * \[aram[in] LV portion of TS 04.08 Bearer Capability
+// * \returns 0 on success; negative on error */
+//int gsm48_decode_bearer_cap(struct gsm_mncc_bearer_cap *bcap,
+// const uint8_t *lv)
+//{
+// uint8_t in_len = lv[0];
+// int i, s;
+
+// if (in_len < 1)
+// return -EINVAL;
+
+// bcap->speech_ver[0] = -1; /* end of list, of maximum 7 values */
+
+// /* octet 3 */
+// bcap->transfer = lv[1] & 0x07;
+// bcap->mode = (lv[1] & 0x08) >> 3;
+// bcap->coding = (lv[1] & 0x10) >> 4;
+// bcap->radio = (lv[1] & 0x60) >> 5;
+
+// switch (bcap->transfer) {
+// case GSM_MNCC_BCAP_SPEECH:
+// i = 1;
+// s = 0;
+// while(!(lv[i] & 0x80)) {
+// i++; /* octet 3a etc */
+// if (in_len < i)
+// return 0;
+// bcap->speech_ver[s++] = lv[i] & 0x0f;
+// bcap->speech_ver[s] = -1; /* end of list */
+// if (i == 2) /* octet 3a */
+// bcap->speech_ctm = (lv[i] & 0x20) >> 5;
+// if (s == 7) /* maximum speech versions + end of list */
+// return 0;
+// }
+// break;
+// case GSM_MNCC_BCAP_UNR_DIG:
+// case GSM_MNCC_BCAP_FAX_G3:
+// i = 1;
+// while(!(lv[i] & 0x80)) {
+// i++; /* octet 3a etc */
+// if (in_len < i)
+// return 0;
+// /* ignore them */
+// }
+// /* octet 4: skip */
+// i++;
+// /* octet 5 */
+// i++;
+// if (in_len < i)
+// return 0;
+// bcap->data.rate_adaption = (lv[i] >> 3) & 3;
+// bcap->data.sig_access = lv[i] & 7;
+// while(!(lv[i] & 0x80)) {
+// i++; /* octet 5a etc */
+// if (in_len < i)
+// return 0;
+// /* ignore them */
+// }
+// /* octet 6 */
+// i++;
+// if (in_len < i)
+// return 0;
+// bcap->data.async = lv[i] & 1;
+// if (!(lv[i] & 0x80)) {
+// i++;
+// if (in_len < i)
+// return 0;
+// /* octet 6a */
+// bcap->data.nr_stop_bits = ((lv[i] >> 7) & 1) + 1;
+// if (lv[i] & 0x10)
+// bcap->data.nr_data_bits = 8;
+// else
+// bcap->data.nr_data_bits = 7;
+// bcap->data.user_rate = lv[i] & 0xf;
+
+// if (!(lv[i] & 0x80)) {
+// i++;
+// if (in_len < i)
+// return 0;
+// /* octet 6b */
+// bcap->data.parity = lv[i] & 7;
+// bcap->data.interm_rate = (lv[i] >> 5) & 3;
+
+// /* octet 6c */
+// if (!(lv[i] & 0x80)) {
+// i++;
+// if (in_len < i)
+// return 0;
+// bcap->data.transp = (lv[i] >> 5) & 3;
+// bcap->data.modem_type = lv[i] & 0x1F;
+// }
+// }
+
+// }
+// break;
+// default:
+// i = 1;
+// while (!(lv[i] & 0x80)) {
+// i++; /* octet 3a etc */
+// if (in_len < i)
+// return 0;
+// /* ignore them */
+// }
+// /* FIXME: implement OCTET 4+ parsing */
+// break;
+// }
+
+// return 0;
+//}
+
+///*! Encode TS 04.08 Bearer Capability IE (10.5.4.5)
+// * \param[out] msg Message Buffer to which IE is to be appended
+// * \param[in] lv_only Write only LV portion (1) or TLV (0)
+// * \param[in] bcap Decoded Bearer Capability to be encoded
+// * \returns 0 on success; negative on error */
+//int gsm48_encode_bearer_cap(struct msgb *msg, int lv_only,
+// const struct gsm_mncc_bearer_cap *bcap)
+//{
+// uint8_t lv[32 + 1];
+// int i = 1, s;
+
+// lv[1] = bcap->transfer;
+// lv[1] |= bcap->mode << 3;
+// lv[1] |= bcap->coding << 4;
+// lv[1] |= bcap->radio << 5;
+
+// switch (bcap->transfer) {
+// case GSM_MNCC_BCAP_SPEECH:
+// for (s = 0; bcap->speech_ver[s] >= 0; s++) {
+// i++; /* octet 3a etc */
+// lv[i] = bcap->speech_ver[s];
+// if (i == 2) /* octet 3a */
+// lv[i] |= bcap->speech_ctm << 5;
+// }
+// lv[i] |= 0x80; /* last IE of octet 3 etc */
+// break;
+// case GSM48_BCAP_ITCAP_UNR_DIG_INF:
+// case GSM48_BCAP_ITCAP_FAX_G3:
+// lv[i++] |= 0x80; /* last IE of octet 3 etc */
+// /* octet 4 */
+// lv[i++] = 0xb8;
+// /* octet 5 */
+// lv[i++] = 0x80 | ((bcap->data.rate_adaption & 3) << 3)
+// | (bcap->data.sig_access & 7);
+// /* octet 6 */
+// lv[i++] = 0x20 | (bcap->data.async & 1);
+// /* octet 6a */
+// lv[i++] = (bcap->data.user_rate & 0xf) |
+// (bcap->data.nr_data_bits == 8 ? 0x10 : 0x00) |
+// (bcap->data.nr_stop_bits == 2 ? 0x40 : 0x00);
+// /* octet 6b */
+// lv[i++] = (bcap->data.parity & 7) |
+// ((bcap->data.interm_rate & 3) << 5);
+// /* octet 6c */
+// lv[i] = 0x80 | (bcap->data.modem_type & 0x1f);
+// break;
+// default:
+// return -EINVAL;
+// }
+
+// lv[0] = i;
+// if (lv_only)
+// msgb_lv_put(msg, lv[0], lv+1);
+// else
+// msgb_tlv_put(msg, GSM48_IE_BEARER_CAP, lv[0], lv+1);
+
+// return 0;
+//}
+
+///*! Decode TS 04.08 Call Control Capabilities IE (10.5.4.5a)
+// * \param[out] Caller-provided memory for decoded CC capabilities
+// * \param[in] lv Length-Value of IE
+// * \retursns 0 on success; negative on error */
+//int gsm48_decode_cccap(struct gsm_mncc_cccap *ccap, const uint8_t *lv)
+//{
+// uint8_t in_len = lv[0];
+
+// if (in_len < 1)
+// return -EINVAL;
+
+// /* octet 3 */
+// ccap->dtmf = lv[1] & 0x01;
+// ccap->pcp = (lv[1] & 0x02) >> 1;
+
+// return 0;
+//}
+
+///*! Encodoe TS 04.08 Call Control Capabilities (10.5.4.5a)
+// * \param[out] msg Message Buffer to which to append IE (as TLV)
+// * \param[in] ccap Decoded CC Capabilities to be encoded
+// * \returns 0 on success; negative on error */
+//int gsm48_encode_cccap(struct msgb *msg,
+// const struct gsm_mncc_cccap *ccap)
+//{
+// uint8_t lv[2];
+
+// lv[0] = 1;
+// lv[1] = 0;
+// if (ccap->dtmf)
+// lv [1] |= 0x01;
+// if (ccap->pcp)
+// lv [1] |= 0x02;
+
+// msgb_tlv_put(msg, GSM48_IE_CC_CAP, lv[0], lv+1);
+
+// return 0;
+//}
+
+///*! Decode TS 04.08 Called Party BCD Number IE (10.5.4.7)
+// * \param[out] called Caller-provided memory for decoded number
+// * \param[in] lv Length-Value portion of IE
+// * \returns 0 on success; negative on error */
+//int gsm48_decode_called(struct gsm_mncc_number *called,
+// const uint8_t *lv)
+//{
+// uint8_t in_len = lv[0];
+
+// if (in_len < 1)
+// return -EINVAL;
+
+// /* octet 3 */
+// called->plan = lv[1] & 0x0f;
+// called->type = (lv[1] & 0x70) >> 4;
+
+// /* octet 4..N */
+// gsm48_decode_bcd_number(called->number, sizeof(called->number), lv, 1);
+
+// return 0;
+//}
+
+///*! Encode TS 04.08 Called Party IE (10.5.4.7)
+// * \param[out] msg Mesage Buffer to which to append IE (as TLV)
+// * \param[in] called MNCC Number to encode/append
+// * \returns 0 on success; negative on error */
+//int gsm48_encode_called(struct msgb *msg,
+// const struct gsm_mncc_number *called)
+//{
+// uint8_t lv[18];
+// int ret;
+
+// /* octet 3 */
+// lv[1] = 0x80; /* no extension */
+// lv[1] |= called->plan;
+// lv[1] |= called->type << 4;
+
+// /* octet 4..N, octet 2 */
+// ret = gsm48_encode_bcd_number(lv, sizeof(lv), 1, called->number);
+// if (ret < 0)
+// return ret;
+
+// msgb_tlv_put(msg, GSM48_IE_CALLED_BCD, lv[0], lv+1);
+
+// return 0;
+//}
+
+///*! Decode TS 04.08 Caller ID
+// * \param[out] called Caller-provided memory for decoded number
+// * \param[in] lv Length-Value portion of IE
+// * \returns 0 on success; negative on error */
+//int gsm48_decode_callerid(struct gsm_mncc_number *callerid,
+// const uint8_t *lv)
+//{
+// uint8_t in_len = lv[0];
+// int i = 1;
+
+// if (in_len < 1)
+// return -EINVAL;
+
+// /* octet 3 */
+// callerid->plan = lv[1] & 0x0f;
+// callerid->type = (lv[1] & 0x70) >> 4;
+
+// /* octet 3a */
+// if (!(lv[1] & 0x80)) {
+// callerid->screen = lv[2] & 0x03;
+// callerid->present = (lv[2] & 0x60) >> 5;
+// i = 2;
+// }
+
+// /* octet 4..N */
+// gsm48_decode_bcd_number(callerid->number, sizeof(callerid->number), lv, i);
+
+// return 0;
+//}
+
+///*! Encode TS 04.08 Caller ID IE
+// * \param[out] msg Mesage Buffer to which to append IE (as TLV)
+// * \param[in] ie IE Identifier (tag)
+// * \param[in] max_len maximum generated output in bytes
+// * \param[in] callerid MNCC Number to encode/append
+// * \returns 0 on success; negative on error */
+//int gsm48_encode_callerid(struct msgb *msg, int ie, int max_len,
+// const struct gsm_mncc_number *callerid)
+//{
+// uint8_t * lv = malloc(sizeof(uint8_t)*(max_len - 1));
+// int h_len = 1;
+// int ret;
+
+// /* octet 3 */
+// lv[1] = callerid->plan;
+// lv[1] |= callerid->type << 4;
+
+// if (callerid->present || callerid->screen) {
+// /* octet 3a */
+// lv[2] = callerid->screen;
+// lv[2] |= callerid->present << 5;
+// lv[2] |= 0x80;
+// h_len++;
+// } else
+// lv[1] |= 0x80;
+
+// /* octet 4..N, octet 2 */
+// ret = gsm48_encode_bcd_number(lv, sizeof(lv), h_len, callerid->number);
+// if (ret < 0)
+// return ret;
+
+// msgb_tlv_put(msg, ie, lv[0], lv+1);
+// free(lv);
+// return 0;
+//}
+
+///*! Decode TS 04.08 Cause IE (10.5.4.11)
+// * \param[out] cause Caller-provided memory for output
+// * \param[in] lv LV portion of Cause IE
+// * \returns 0 on success; negative on error */
+//int gsm48_decode_cause(struct gsm_mncc_cause *cause,
+// const uint8_t *lv)
+//{
+// uint8_t in_len = lv[0];
+// int i;
+
+// if (in_len < 2)
+// return -EINVAL;
+
+// cause->diag_len = 0;
+
+// /* octet 3 */
+// cause->location = lv[1] & 0x0f;
+// cause->coding = (lv[1] & 0x60) >> 5;
+
+// i = 1;
+// if (!(lv[i] & 0x80)) {
+// i++; /* octet 3a */
+// if (in_len < i+1)
+// return 0;
+// cause->rec = 1;
+// cause->rec_val = lv[i] & 0x7f;
+// }
+// i++;
+
+// /* octet 4 */
+// cause->value = lv[i] & 0x7f;
+// i++;
+
+// if (in_len < i) /* no diag */
+// return 0;
+
+// if (in_len - (i-1) > 32) /* maximum 32 octets */
+// return 0;
+
+// /* octet 5-N */
+// memcpy(cause->diag, lv + i, in_len - (i-1));
+// cause->diag_len = in_len - (i-1);
+
+// return 0;
+//}
+
+///*! Encode TS 04.08 Cause IE (10.5.4.11)
+// * \param[out] msg Message Buffer to which to append IE
+// * \param[in] lv_only Encode as LV (1) or TLV (0)
+// * \param[in] cause Cause value to be encoded
+// * \returns 0 on success; negative on error */
+//int gsm48_encode_cause(struct msgb *msg, int lv_only,
+// const struct gsm_mncc_cause *cause)
+//{
+// uint8_t lv[32+4];
+// int i;
+
+// if (cause->diag_len > 32)
+// return -EINVAL;
+
+// /* octet 3 */
+// lv[1] = cause->location;
+// lv[1] |= cause->coding << 5;
+
+// i = 1;
+// if (cause->rec) {
+// i++; /* octet 3a */
+// lv[i] = cause->rec_val;
+// }
+// lv[i] |= 0x80; /* end of octet 3 */
+
+// /* octet 4 */
+// i++;
+// lv[i] = 0x80 | cause->value;
+
+// /* octet 5-N */
+// if (cause->diag_len) {
+// memcpy(lv + i, cause->diag, cause->diag_len);
+// i += cause->diag_len;
+// }
+
+// lv[0] = i;
+// if (lv_only)
+// msgb_lv_put(msg, lv[0], lv+1);
+// else
+// msgb_tlv_put(msg, GSM48_IE_CAUSE, lv[0], lv+1);
+
+// return 0;
+//}
+
+///*! Decode TS 04.08 Calling Number IE (10.5.4.9) */
+//int gsm48_decode_calling(struct gsm_mncc_number *calling,
+// const uint8_t *lv)
+//{
+// return gsm48_decode_callerid(calling, lv);
+//}
+
+///*! Encode TS 04.08 Calling Number IE (10.5.4.9) */
+//int gsm48_encode_calling(struct msgb *msg,
+// const struct gsm_mncc_number *calling)
+//{
+// return gsm48_encode_callerid(msg, GSM48_IE_CALLING_BCD, 14, calling);
+//}
+
+///*! Decode TS 04.08 Connected Number IE (10.5.4.13) */
+//int gsm48_decode_connected(struct gsm_mncc_number *connected,
+// const uint8_t *lv)
+//{
+// return gsm48_decode_callerid(connected, lv);
+//}
+
+///*! Encode TS 04.08 Connected Number IE (10.5.4.13) */
+//int gsm48_encode_connected(struct msgb *msg,
+// const struct gsm_mncc_number *connected)
+//{
+// return gsm48_encode_callerid(msg, GSM48_IE_CONN_BCD, 14, connected);
+//}
+
+///*! Decode TS 04.08 Redirecting Number IE (10.5.4.21b) */
+//int gsm48_decode_redirecting(struct gsm_mncc_number *redirecting,
+// const uint8_t *lv)
+//{
+// return gsm48_decode_callerid(redirecting, lv);
+//}
+
+///*! Encode TS 04.08 Redirecting Number IE (10.5.4.21b) */
+//int gsm48_encode_redirecting(struct msgb *msg,
+// const struct gsm_mncc_number *redirecting)
+//{
+// return gsm48_encode_callerid(msg, GSM48_IE_REDIR_BCD, 19, redirecting);
+//}
+
+///*! Decode TS 04.08 Facility IE (10.5.4.15) */
+//int gsm48_decode_facility(struct gsm_mncc_facility *facility,
+// const uint8_t *lv)
+//{
+// uint8_t in_len = lv[0];
+
+// if (in_len < 1)
+// return -EINVAL;
+
+// if (in_len > sizeof(facility->info))
+// return -EINVAL;
+
+// memcpy(facility->info, lv+1, in_len);
+// facility->len = in_len;
+
+// return 0;
+//}
+
+///*! Encode TS 04.08 Facility IE (10.5.4.15) */
+//int gsm48_encode_facility(struct msgb *msg, int lv_only,
+// const struct gsm_mncc_facility *facility)
+//{
+// uint8_t lv[GSM_MAX_FACILITY + 1];
+
+// if (facility->len < 1 || facility->len > GSM_MAX_FACILITY)
+// return -EINVAL;
+
+// memcpy(lv+1, facility->info, facility->len);
+// lv[0] = facility->len;
+// if (lv_only)
+// msgb_lv_put(msg, lv[0], lv+1);
+// else
+// msgb_tlv_put(msg, GSM48_IE_FACILITY, lv[0], lv+1);
+
+// return 0;
+//}
+
+///*! Decode TS 04.08 Notify IE (10.5.4.20) */
+//int gsm48_decode_notify(int *notify, const uint8_t *v)
+//{
+// *notify = v[0] & 0x7f;
+
+// return 0;
+//}
+
+///*! Encode TS 04.08 Notify IE (10.5.4.20) */
+//int gsm48_encode_notify(struct msgb *msg, int notify)
+//{
+// msgb_v_put(msg, notify | 0x80);
+
+// return 0;
+//}
+
+///*! Decode TS 04.08 Signal IE (10.5.4.23) */
+//int gsm48_decode_signal(int *signal, const uint8_t *v)
+//{
+// *signal = v[0];
+
+// return 0;
+//}
+
+///*! Encode TS 04.08 Signal IE (10.5.4.23) */
+//int gsm48_encode_signal(struct msgb *msg, int signal)
+//{
+// msgb_tv_put(msg, GSM48_IE_SIGNAL, signal);
+
+// return 0;
+//}
+
+///*! Decode TS 04.08 Keypad IE (10.5.4.17) */
+//int gsm48_decode_keypad(int *keypad, const uint8_t *lv)
+//{
+// uint8_t in_len = lv[0];
+
+// if (in_len < 1)
+// return -EINVAL;
+
+// *keypad = lv[1] & 0x7f;
+
+// return 0;
+//}
+
+///*! Encode TS 04.08 Keypad IE (10.5.4.17) */
+//int gsm48_encode_keypad(struct msgb *msg, int keypad)
+//{
+// msgb_tv_put(msg, GSM48_IE_KPD_FACILITY, keypad);
+
+// return 0;
+//}
+
+///*! Decode TS 04.08 Progress IE (10.5.4.21) */
+//int gsm48_decode_progress(struct gsm_mncc_progress *progress,
+// const uint8_t *lv)
+//{
+// uint8_t in_len = lv[0];
+
+// if (in_len < 2)
+// return -EINVAL;
+
+// progress->coding = (lv[1] & 0x60) >> 5;
+// progress->location = lv[1] & 0x0f;
+// progress->descr = lv[2] & 0x7f;
+
+// return 0;
+//}
+
+///*! Encode TS 04.08 Progress IE (10.5.4.21) */
+//int gsm48_encode_progress(struct msgb *msg, int lv_only,
+// const struct gsm_mncc_progress *p)
+//{
+// uint8_t lv[3];
+
+// lv[0] = 2;
+// lv[1] = 0x80 | ((p->coding & 0x3) << 5) | (p->location & 0xf);
+// lv[2] = 0x80 | (p->descr & 0x7f);
+// if (lv_only)
+// msgb_lv_put(msg, lv[0], lv+1);
+// else
+// msgb_tlv_put(msg, GSM48_IE_PROGR_IND, lv[0], lv+1);
+
+// return 0;
+//}
+
+///*! Decode TS 04.08 User-User IE (10.5.4.25) */
+//int gsm48_decode_useruser(struct gsm_mncc_useruser *uu,
+// const uint8_t *lv)
+//{
+// uint8_t in_len = lv[0];
+// char *info = uu->info;
+// int info_len = sizeof(uu->info);
+// int i;
+
+// if (in_len < 1)
+// return -EINVAL;
+
+// uu->proto = lv[1];
+
+// for (i = 2; i <= in_len; i++) {
+// info_len--;
+// if (info_len <= 1)
+// break;
+// *info++ = lv[i];
+// }
+// if (info_len >= 1)
+// *info++ = '\0';
+
+// return 0;
+//}
+
+///*! Encode TS 04.08 User-User IE (10.5.4.25) */
+//int gsm48_encode_useruser(struct msgb *msg, int lv_only,
+// const struct gsm_mncc_useruser *uu)
+//{
+// uint8_t lv[GSM_MAX_USERUSER + 2];
+
+// if (strlen(uu->info) > GSM_MAX_USERUSER)
+// return -EINVAL;
+
+// lv[0] = 1 + strlen(uu->info);
+// lv[1] = uu->proto;
+// memcpy(lv + 2, uu->info, strlen(uu->info));
+// if (lv_only)
+// msgb_lv_put(msg, lv[0], lv+1);
+// else
+// msgb_tlv_put(msg, GSM48_IE_USER_USER, lv[0], lv+1);
+
+// return 0;
+//}
+
+///*! Decode TS 04.08 SS Version IE (10.5.4.24) */
+//int gsm48_decode_ssversion(struct gsm_mncc_ssversion *ssv,
+// const uint8_t *lv)
+//{
+// uint8_t in_len = lv[0];
+
+// if (in_len < 1 || in_len < sizeof(ssv->info))
+// return -EINVAL;
+
+// memcpy(ssv->info, lv + 1, in_len);
+// ssv->len = in_len;
+
+// return 0;
+//}
+
+///*! Encode TS 04.08 SS Version IE (10.5.4.24) */
+//int gsm48_encode_ssversion(struct msgb *msg,
+// const struct gsm_mncc_ssversion *ssv)
+//{
+// uint8_t lv[GSM_MAX_SSVERSION + 1];
+
+// if (ssv->len > GSM_MAX_SSVERSION)
+// return -EINVAL;
+
+// lv[0] = ssv->len;
+// memcpy(lv + 1, ssv->info, ssv->len);
+// msgb_tlv_put(msg, GSM48_IE_SS_VERS, lv[0], lv+1);
+
+// return 0;
+//}
+
+///* decode 'more data' does not require a function, because it has no value */
+
+///*! Encode TS 04.08 More Data IE (10.5.4.19) */
+//int gsm48_encode_more(struct msgb *msg)
+//{
+// uint8_t *ie;
+
+// ie = msgb_put(msg, 1);
+// ie[0] = GSM48_IE_MORE_DATA;
+
+// return 0;
+//}
+
+static int32_t smod(int32_t n, int32_t m)
+{
+ int32_t res;
+
+ res = n % m;
+
+ if (res <= 0)
+ res += m;
+
+ return res;
+}
+
+/*! Decode TS 04.08 Cell Channel Description IE (10.5.2.1b) and other frequency lists
+ * \param[out] f Caller-provided output memory
+ * \param[in] cd Cell Channel Description IE
+ * \param[in] len Length of \a cd in bytes
+ * \returns 0 on success; negative on error */
+int gsm48_decode_freq_list(struct gsm_sysinfo_freq *f, uint8_t *cd,
+ uint8_t len, uint8_t mask, uint8_t frqt)
+{
+ int i;
+
+ /* NOTES:
+ *
+ * The Range format uses "SMOD" computation.
+ * e.g. "n SMOD m" equals "((n - 1) % m) + 1"
+ * A cascade of multiple SMOD computations is simpified:
+ * "(n SMOD m) SMOD o" equals "(((n - 1) % m) % o) + 1"
+ *
+ * The Range format uses 16 octets of data in SYSTEM INFORMATION.
+ * When used in dedicated messages, the length can be less.
+ * In this case the ranges are decoded for all frequencies that
+ * fit in the block of given length.
+ */
+
+ /* tabula rasa */
+ for (i = 0; i < 1024; i++)
+ f[i].mask &= ~frqt;
+
+ /* 00..XXX. */
+ if ((cd[0] & 0xc0 & mask) == 0x00) {
+ /* Bit map 0 format */
+ if (len < 16)
+ return -EINVAL;
+ for (i = 1; i <= 124; i++)
+ if ((cd[15 - ((i-1) >> 3)] & (1 << ((i-1) & 7))))
+ f[i].mask |= frqt;
+
+ return 0;
+ }
+
+ /* 10..0XX. */
+ if ((cd[0] & 0xc8 & mask) == 0x80) {
+ /* Range 1024 format */
+ uint16_t w[17]; /* 1..16 */
+ struct gsm48_range_1024 *r = (struct gsm48_range_1024 *)cd;
+
+ if (len < 2)
+ return -EINVAL;
+ memset(w, 0, sizeof(w));
+ if (r->f0)
+ f[0].mask |= frqt;
+ w[1] = (r->w1_hi << 8) | r->w1_lo;
+ if (len >= 4)
+ w[2] = (r->w2_hi << 1) | r->w2_lo;
+ if (len >= 5)
+ w[3] = (r->w3_hi << 2) | r->w3_lo;
+ if (len >= 6)
+ w[4] = (r->w4_hi << 2) | r->w4_lo;
+ if (len >= 7)
+ w[5] = (r->w5_hi << 2) | r->w5_lo;
+ if (len >= 8)
+ w[6] = (r->w6_hi << 2) | r->w6_lo;
+ if (len >= 9)
+ w[7] = (r->w7_hi << 2) | r->w7_lo;
+ if (len >= 10)
+ w[8] = (r->w8_hi << 1) | r->w8_lo;
+ if (len >= 10)
+ w[9] = r->w9;
+ if (len >= 11)
+ w[10] = r->w10;
+ if (len >= 12)
+ w[11] = (r->w11_hi << 6) | r->w11_lo;
+ if (len >= 13)
+ w[12] = (r->w12_hi << 5) | r->w12_lo;
+ if (len >= 14)
+ w[13] = (r->w13_hi << 4) | r->w13_lo;
+ if (len >= 15)
+ w[14] = (r->w14_hi << 3) | r->w14_lo;
+ if (len >= 16)
+ w[15] = (r->w15_hi << 2) | r->w15_lo;
+ if (len >= 16)
+ w[16] = r->w16;
+ if (w[1])
+ f[w[1]].mask |= frqt;
+ if (w[2])
+ f[smod(w[1] - 512 + w[2], 1023)].mask |= frqt;
+ if (w[3])
+ f[smod(w[1] + w[3], 1023)].mask |= frqt;
+ if (w[4])
+ f[smod(w[1] - 512 + smod(w[2] - 256 + w[4], 511), 1023)].mask |= frqt;
+ if (w[5])
+ f[smod(w[1] + smod(w[3] - 256 + w[5], 511), 1023)].mask |= frqt;
+ if (w[6])
+ f[smod(w[1] - 512 + smod(w[2] + w[6], 511), 1023)].mask |= frqt;
+ if (w[7])
+ f[smod(w[1] + smod(w[3] + w[7], 511), 1023)].mask |= frqt;
+ if (w[8])
+ f[smod(w[1] - 512 + smod(w[2] - 256 + smod(w[4] - 128 + w[8] , 255), 511), 1023)].mask |= frqt;
+ if (w[9])
+ f[smod(w[1] + smod(w[3] - 256 + smod(w[5] - 128 + w[9] , 255), 511), 1023)].mask |= frqt;
+ if (w[10])
+ f[smod(w[1] - 512 + smod(w[2] + smod(w[6] - 128 + w[10], 255), 511), 1023)].mask |= frqt;
+ if (w[11])
+ f[smod(w[1] + smod(w[3] + smod(w[7] - 128 + w[11], 255), 511), 1023)].mask |= frqt;
+ if (w[12])
+ f[smod(w[1] - 512 + smod(w[2] - 256 + smod(w[4] + w[12], 255), 511), 1023)].mask |= frqt;
+ if (w[13])
+ f[smod(w[1] + smod(w[3] - 256 + smod(w[5] + w[13], 255), 511), 1023)].mask |= frqt;
+ if (w[14])
+ f[smod(w[1] - 512 + smod(w[2] + smod(w[6] + w[14], 255), 511), 1023)].mask |= frqt;
+ if (w[15])
+ f[smod(w[1] + smod(w[3] + smod(w[7] + w[15], 255), 511), 1023)].mask |= frqt;
+ if (w[16])
+ f[smod(w[1] - 512 + smod(w[2] - 256 + smod(w[4] - 128 + smod(w[8] - 64 + w[16], 127), 255), 511), 1023)].mask |= frqt;
+
+ return 0;
+ }
+ /* 10..100. */
+ if ((cd[0] & 0xce & mask) == 0x88) {
+ /* Range 512 format */
+ uint16_t w[18]; /* 1..17 */
+ struct gsm48_range_512 *r = (struct gsm48_range_512 *)cd;
+
+ if (len < 4)
+ return -EINVAL;
+ memset(w, 0, sizeof(w));
+ w[0] = (r->orig_arfcn_hi << 9) | (r->orig_arfcn_mid << 1) | r->orig_arfcn_lo;
+ w[1] = (r->w1_hi << 2) | r->w1_lo;
+ if (len >= 5)
+ w[2] = (r->w2_hi << 2) | r->w2_lo;
+ if (len >= 6)
+ w[3] = (r->w3_hi << 2) | r->w3_lo;
+ if (len >= 7)
+ w[4] = (r->w4_hi << 1) | r->w4_lo;
+ if (len >= 7)
+ w[5] = r->w5;
+ if (len >= 8)
+ w[6] = r->w6;
+ if (len >= 9)
+ w[7] = (r->w7_hi << 6) | r->w7_lo;
+ if (len >= 10)
+ w[8] = (r->w8_hi << 4) | r->w8_lo;
+ if (len >= 11)
+ w[9] = (r->w9_hi << 2) | r->w9_lo;
+ if (len >= 11)
+ w[10] = r->w10;
+ if (len >= 12)
+ w[11] = r->w11;
+ if (len >= 13)
+ w[12] = (r->w12_hi << 4) | r->w12_lo;
+ if (len >= 14)
+ w[13] = (r->w13_hi << 2) | r->w13_lo;
+ if (len >= 14)
+ w[14] = r->w14;
+ if (len >= 15)
+ w[15] = r->w15;
+ if (len >= 16)
+ w[16] = (r->w16_hi << 3) | r->w16_lo;
+ if (len >= 16)
+ w[17] = r->w17;
+ f[w[0]].mask |= frqt;
+ if (w[1])
+ f[(w[0] + w[1]) % 1024].mask |= frqt;
+ if (w[2])
+ f[(w[0] + smod(w[1] - 256 + w[2], 511)) % 1024].mask |= frqt;
+ if (w[3])
+ f[(w[0] + smod(w[1] + w[3], 511)) % 1024].mask |= frqt;
+ if (w[4])
+ f[(w[0] + smod(w[1] - 256 + smod(w[2] - 128 + w[4], 255), 511)) % 1024].mask |= frqt;
+ if (w[5])
+ f[(w[0] + smod(w[1] + smod(w[3] - 128 + w[5], 255), 511)) % 1024].mask |= frqt;
+ if (w[6])
+ f[(w[0] + smod(w[1] - 256 + smod(w[2] + w[6], 255), 511)) % 1024].mask |= frqt;
+ if (w[7])
+ f[(w[0] + smod(w[1] + smod(w[3] + w[7], 255), 511)) % 1024].mask |= frqt;
+ if (w[8])
+ f[(w[0] + smod(w[1] - 256 + smod(w[2] - 128 + smod(w[4] - 64 + w[8] , 127), 255), 511)) % 1024].mask |= frqt;
+ if (w[9])
+ f[(w[0] + smod(w[1] + smod(w[3] - 128 + smod(w[5] - 64 + w[9] , 127), 255), 511)) % 1024].mask |= frqt;
+ if (w[10])
+ f[(w[0] + smod(w[1] - 256 + smod(w[2] + smod(w[6] - 64 + w[10], 127), 255), 511)) % 1024].mask |= frqt;
+ if (w[11])
+ f[(w[0] + smod(w[1] + smod(w[3] + smod(w[7] - 64 + w[11], 127), 255), 511)) % 1024].mask |= frqt;
+ if (w[12])
+ f[(w[0] + smod(w[1] - 256 + smod(w[2] - 128 + smod(w[4] + w[12], 127), 255), 511)) % 1024].mask |= frqt;
+ if (w[13])
+ f[(w[0] + smod(w[1] + smod(w[3] - 128 + smod(w[5] + w[13], 127), 255), 511)) % 1024].mask |= frqt;
+ if (w[14])
+ f[(w[0] + smod(w[1] - 256 + smod(w[2] + smod(w[6] + w[14], 127), 255), 511)) % 1024].mask |= frqt;
+ if (w[15])
+ f[(w[0] + smod(w[1] + smod(w[3] + smod(w[7] + w[15], 127), 255), 511)) % 1024].mask |= frqt;
+ if (w[16])
+ f[(w[0] + smod(w[1] - 256 + smod(w[2] - 128 + smod(w[4] - 64 + smod(w[8] - 32 + w[16], 63), 127), 255), 511)) % 1024].mask |= frqt;
+ if (w[17])
+ f[(w[0] + smod(w[1] + smod(w[3] - 128 + smod(w[5] - 64 + smod(w[9] - 32 + w[17], 63), 127), 255), 511)) % 1024].mask |= frqt;
+
+ return 0;
+ }
+ /* 10..101. */
+ if ((cd[0] & 0xce & mask) == 0x8a) {
+ /* Range 256 format */
+ uint16_t w[22]; /* 1..21 */
+ struct gsm48_range_256 *r = (struct gsm48_range_256 *)cd;
+
+ if (len < 4)
+ return -EINVAL;
+ memset(w, 0, sizeof(w));
+ w[0] = (r->orig_arfcn_hi << 9) | (r->orig_arfcn_mid << 1) | r->orig_arfcn_lo;
+ w[1] = (r->w1_hi << 1) | r->w1_lo;
+ if (len >= 4)
+ w[2] = r->w2;
+ if (len >= 5)
+ w[3] = r->w3;
+ if (len >= 6)
+ w[4] = (r->w4_hi << 5) | r->w4_lo;
+ if (len >= 7)
+ w[5] = (r->w5_hi << 3) | r->w5_lo;
+ if (len >= 8)
+ w[6] = (r->w6_hi << 1) | r->w6_lo;
+ if (len >= 8)
+ w[7] = r->w7;
+ if (len >= 9)
+ w[8] = (r->w8_hi << 4) | r->w8_lo;
+ if (len >= 10)
+ w[9] = (r->w9_hi << 1) | r->w9_lo;
+ if (len >= 10)
+ w[10] = r->w10;
+ if (len >= 11)
+ w[11] = (r->w11_hi << 3) | r->w11_lo;
+ if (len >= 11)
+ w[12] = r->w12;
+ if (len >= 12)
+ w[13] = r->w13;
+ if (len >= 13)
+ w[14] = (r->w14_hi << 2) | r->w14_lo;
+ if (len >= 13)
+ w[15] = r->w15;
+ if (len >= 14)
+ w[16] = (r->w16_hi << 3) | r->w16_lo;
+ if (len >= 14)
+ w[17] = r->w17;
+ if (len >= 15)
+ w[18] = (r->w18_hi << 3) | r->w18_lo;
+ if (len >= 15)
+ w[19] = r->w19;
+ if (len >= 16)
+ w[20] = (r->w20_hi << 3) | r->w20_lo;
+ if (len >= 16)
+ w[21] = r->w21;
+ f[w[0]].mask |= frqt;
+ if (w[1])
+ f[(w[0] + w[1]) % 1024].mask |= frqt;
+ if (w[2])
+ f[(w[0] + smod(w[1] - 128 + w[2], 255)) % 1024].mask |= frqt;
+ if (w[3])
+ f[(w[0] + smod(w[1] + w[3], 255)) % 1024].mask |= frqt;
+ if (w[4])
+ f[(w[0] + smod(w[1] - 128 + smod(w[2] - 64 + w[4], 127), 255)) % 1024].mask |= frqt;
+ if (w[5])
+ f[(w[0] + smod(w[1] + smod(w[3] - 64 + w[5], 127), 255)) % 1024].mask |= frqt;
+ if (w[6])
+ f[(w[0] + smod(w[1] - 128 + smod(w[2] + w[6], 127), 255)) % 1024].mask |= frqt;
+ if (w[7])
+ f[(w[0] + smod(w[1] + smod(w[3] + w[7], 127), 255)) % 1024].mask |= frqt;
+ if (w[8])
+ f[(w[0] + smod(w[1] - 128 + smod(w[2] - 64 + smod(w[4] - 32 + w[8] , 63), 127), 255)) % 1024].mask |= frqt;
+ if (w[9])
+ f[(w[0] + smod(w[1] + smod(w[3] - 64 + smod(w[5] - 32 + w[9] , 63), 127), 255)) % 1024].mask |= frqt;
+ if (w[10])
+ f[(w[0] + smod(w[1] - 128 + smod(w[2] + smod(w[6] - 32 + w[10], 63), 127), 255)) % 1024].mask |= frqt;
+ if (w[11])
+ f[(w[0] + smod(w[1] + smod(w[3] + smod(w[7] - 32 + w[11], 63), 127), 255)) % 1024].mask |= frqt;
+ if (w[12])
+ f[(w[0] + smod(w[1] - 128 + smod(w[2] - 64 + smod(w[4] + w[12], 63), 127), 255)) % 1024].mask |= frqt;
+ if (w[13])
+ f[(w[0] + smod(w[1] + smod(w[3] - 64 + smod(w[5] + w[13], 63), 127), 255)) % 1024].mask |= frqt;
+ if (w[14])
+ f[(w[0] + smod(w[1] - 128 + smod(w[2] + smod(w[6] + w[14], 63), 127), 255)) % 1024].mask |= frqt;
+ if (w[15])
+ f[(w[0] + smod(w[1] + smod(w[3] + smod(w[7] + w[15], 63), 127), 255)) % 1024].mask |= frqt;
+ if (w[16])
+ f[(w[0] + smod(w[1] - 128 + smod(w[2] - 64 + smod(w[4] - 32 + smod(w[8] - 16 + w[16], 31), 63), 127), 255)) % 1024].mask |= frqt;
+ if (w[17])
+ f[(w[0] + smod(w[1] + smod(w[3] - 64 + smod(w[5] - 32 + smod(w[9] - 16 + w[17], 31), 63), 127), 255)) % 1024].mask |= frqt;
+ if (w[18])
+ f[(w[0] + smod(w[1] - 128 + smod(w[2] + smod(w[6] - 32 + smod(w[10] - 16 + w[18], 31), 63), 127), 255)) % 1024].mask |= frqt;
+ if (w[19])
+ f[(w[0] + smod(w[1] + smod(w[3] + smod(w[7] - 32 + smod(w[11] - 16 + w[19], 31), 63), 127), 255)) % 1024].mask |= frqt;
+ if (w[20])
+ f[(w[0] + smod(w[1] - 128 + smod(w[2] - 64 + smod(w[4] + smod(w[12] - 16 + w[20], 31), 63), 127), 255)) % 1024].mask |= frqt;
+ if (w[21])
+ f[(w[0] + smod(w[1] + smod(w[3] - 64 + smod(w[5] + smod(w[13] - 16 + w[21], 31), 63), 127), 255)) % 1024].mask |= frqt;
+
+ return 0;
+ }
+ /* 10..110. */
+ if ((cd[0] & 0xce & mask) == 0x8c) {
+ /* Range 128 format */
+ uint16_t w[29]; /* 1..28 */
+ struct gsm48_range_128 *r = (struct gsm48_range_128 *)cd;
+
+ if (len < 3)
+ return -EINVAL;
+ memset(w, 0, sizeof(w));
+ w[0] = (r->orig_arfcn_hi << 9) | (r->orig_arfcn_mid << 1) | r->orig_arfcn_lo;
+ w[1] = r->w1;
+ if (len >= 4)
+ w[2] = r->w2;
+ if (len >= 5)
+ w[3] = (r->w3_hi << 4) | r->w3_lo;
+ if (len >= 6)
+ w[4] = (r->w4_hi << 1) | r->w4_lo;
+ if (len >= 6)
+ w[5] = r->w5;
+ if (len >= 7)
+ w[6] = (r->w6_hi << 3) | r->w6_lo;
+ if (len >= 7)
+ w[7] = r->w7;
+ if (len >= 8)
+ w[8] = r->w8;
+ if (len >= 8)
+ w[9] = r->w9;
+ if (len >= 9)
+ w[10] = r->w10;
+ if (len >= 9)
+ w[11] = r->w11;
+ if (len >= 10)
+ w[12] = r->w12;
+ if (len >= 10)
+ w[13] = r->w13;
+ if (len >= 11)
+ w[14] = r->w14;
+ if (len >= 11)
+ w[15] = r->w15;
+ if (len >= 12)
+ w[16] = r->w16;
+ if (len >= 12)
+ w[17] = r->w17;
+ if (len >= 13)
+ w[18] = (r->w18_hi << 1) | r->w18_lo;
+ if (len >= 13)
+ w[19] = r->w19;
+ if (len >= 13)
+ w[20] = r->w20;
+ if (len >= 14)
+ w[21] = (r->w21_hi << 2) | r->w21_lo;
+ if (len >= 14)
+ w[22] = r->w22;
+ if (len >= 14)
+ w[23] = r->w23;
+ if (len >= 15)
+ w[24] = r->w24;
+ if (len >= 15)
+ w[25] = r->w25;
+ if (len >= 16)
+ w[26] = (r->w26_hi << 1) | r->w26_lo;
+ if (len >= 16)
+ w[27] = r->w27;
+ if (len >= 16)
+ w[28] = r->w28;
+ f[w[0]].mask |= frqt;
+ if (w[1])
+ f[(w[0] + w[1]) % 1024].mask |= frqt;
+ if (w[2])
+ f[(w[0] + smod(w[1] - 64 + w[2], 127)) % 1024].mask |= frqt;
+ if (w[3])
+ f[(w[0] + smod(w[1] + w[3], 127)) % 1024].mask |= frqt;
+ if (w[4])
+ f[(w[0] + smod(w[1] - 64 + smod(w[2] - 32 + w[4], 63), 127)) % 1024].mask |= frqt;
+ if (w[5])
+ f[(w[0] + smod(w[1] + smod(w[3] - 32 + w[5], 63), 127)) % 1024].mask |= frqt;
+ if (w[6])
+ f[(w[0] + smod(w[1] - 64 + smod(w[2] + w[6], 63), 127)) % 1024].mask |= frqt;
+ if (w[7])
+ f[(w[0] + smod(w[1] + smod(w[3] + w[7], 63), 127)) % 1024].mask |= frqt;
+ if (w[8])
+ f[(w[0] + smod(w[1] - 64 + smod(w[2] - 32 + smod(w[4] - 16 + w[8] , 31), 63), 127)) % 1024].mask |= frqt;
+ if (w[9])
+ f[(w[0] + smod(w[1] + smod(w[3] - 32 + smod(w[5] - 16 + w[9] , 31), 63), 127)) % 1024].mask |= frqt;
+ if (w[10])
+ f[(w[0] + smod(w[1] - 64 + smod(w[2] + smod(w[6] - 16 + w[10], 31), 63), 127)) % 1024].mask |= frqt;
+ if (w[11])
+ f[(w[0] + smod(w[1] + smod(w[3] + smod(w[7] - 16 + w[11], 31), 63), 127)) % 1024].mask |= frqt;
+ if (w[12])
+ f[(w[0] + smod(w[1] - 64 + smod(w[2] - 32 + smod(w[4] + w[12], 31), 63), 127)) % 1024].mask |= frqt;
+ if (w[13])
+ f[(w[0] + smod(w[1] + smod(w[3] - 32 + smod(w[5] + w[13], 31), 63), 127)) % 1024].mask |= frqt;
+ if (w[14])
+ f[(w[0] + smod(w[1] - 64 + smod(w[2] + smod(w[6] + w[14], 31), 63), 127)) % 1024].mask |= frqt;
+ if (w[15])
+ f[(w[0] + smod(w[1] + smod(w[3] + smod(w[7] + w[15], 31), 63), 127)) % 1024].mask |= frqt;
+ if (w[16])
+ f[(w[0] + smod(w[1] - 64 + smod(w[2] - 32 + smod(w[4] - 16 + smod(w[8] - 8 + w[16], 15), 31), 63), 127)) % 1024].mask |= frqt;
+ if (w[17])
+ f[(w[0] + smod(w[1] + smod(w[3] - 32 + smod(w[5] - 16 + smod(w[9] - 8 + w[17], 15), 31), 63), 127)) % 1024].mask |= frqt;
+ if (w[18])
+ f[(w[0] + smod(w[1] - 64 + smod(w[2] + smod(w[6] - 16 + smod(w[10] - 8 + w[18], 15), 31), 63), 127)) % 1024].mask |= frqt;
+ if (w[19])
+ f[(w[0] + smod(w[1] + smod(w[3] + smod(w[7] - 16 + smod(w[11] - 8 + w[19], 15), 31), 63), 127)) % 1024].mask |= frqt;
+ if (w[20])
+ f[(w[0] + smod(w[1] - 64 + smod(w[2] - 32 + smod(w[4] + smod(w[12] - 8 + w[20], 15), 31), 63), 127)) % 1024].mask |= frqt;
+ if (w[21])
+ f[(w[0] + smod(w[1] + smod(w[3] - 32 + smod(w[5] + smod(w[13] - 8 + w[21], 15), 31), 63), 127)) % 1024].mask |= frqt;
+ if (w[22])
+ f[(w[0] + smod(w[1] - 64 + smod(w[2] + smod(w[6] + smod(w[14] - 8 + w[22], 15), 31), 63), 127)) % 1024].mask |= frqt;
+ if (w[23])
+ f[(w[0] + smod(w[1] + smod(w[3] + smod(w[7] + smod(w[15] - 8 + w[23], 15), 31), 63), 127)) % 1024].mask |= frqt;
+ if (w[24])
+ f[(w[0] + smod(w[1] - 64 + smod(w[2] - 32 + smod(w[4] - 16 + smod(w[8] + w[24], 15), 31), 63), 127)) % 1024].mask |= frqt;
+ if (w[25])
+ f[(w[0] + smod(w[1] + smod(w[3] - 32 + smod(w[5] - 16 + smod(w[9] + w[25], 15), 31), 63), 127)) % 1024].mask |= frqt;
+ if (w[26])
+ f[(w[0] + smod(w[1] - 64 + smod(w[2] + smod(w[6] - 16 + smod(w[10] + w[26], 15), 31), 63), 127)) % 1024].mask |= frqt;
+ if (w[27])
+ f[(w[0] + smod(w[1] + smod(w[3] + smod(w[7] - 16 + smod(w[11] + w[27], 15), 31), 63), 127)) % 1024].mask |= frqt;
+ if (w[28])
+ f[(w[0] + smod(w[1] - 64 + smod(w[2] - 32 + smod(w[4] + smod(w[12] + w[28], 15), 31), 63), 127)) % 1024].mask |= frqt;
+
+ return 0;
+ }
+ /* 10..111. */
+ if ((cd[0] & 0xce & mask) == 0x8e) {
+ /* Variable bitmap format (can be any length >= 3) */
+ uint16_t orig = 0;
+ struct gsm48_var_bit *r = (struct gsm48_var_bit *)cd;
+
+ if (len < 3)
+ return -EINVAL;
+ orig = (r->orig_arfcn_hi << 9) | (r->orig_arfcn_mid << 1) | r->orig_arfcn_lo;
+ f[orig].mask |= frqt;
+ for (i = 1; 2 + (i >> 3) < len; i++)
+ if ((cd[2 + (i >> 3)] & (0x80 >> (i & 7))))
+ f[(orig + i) % 1024].mask |= frqt;
+
+ return 0;
+ }
+
+ return 0;
+}
+/*! @} */
diff --git a/lib/decoding/osmocom/gsm/gsm48_ie.h b/lib/decoding/osmocom/gsm/gsm48_ie.h
new file mode 100644
index 0000000..19e0b25
--- /dev/null
+++ b/lib/decoding/osmocom/gsm/gsm48_ie.h
@@ -0,0 +1,116 @@
+/*! \file gsm48_ie.h */
+
+#pragma once
+
+#include <stdint.h>
+#include <string.h>
+#include <errno.h>
+
+/* #include <osmocom/core/msgb.h> */
+/* #include <osmocom/gsm/tlv.h> */
+/* #include <osmocom/gsm/mncc.h> */
+#include <osmocom/gsm/protocol/gsm_04_08.h>
+
+/* decode a 'called/calling/connect party BCD number' as in 10.5.4.7 */
+//int gsm48_decode_bcd_number(char *output, int output_len,
+// const uint8_t *bcd_lv, int h_len);
+
+///* convert a ASCII phone number to 'called/calling/connect party BCD number' */
+//int gsm48_encode_bcd_number(uint8_t *bcd_lv, uint8_t max_len,
+// int h_len, const char *input);
+///* decode 'bearer capability' */
+//int gsm48_decode_bearer_cap(struct gsm_mncc_bearer_cap *bcap,
+// const uint8_t *lv);
+///* encode 'bearer capability' */
+//int gsm48_encode_bearer_cap(struct msgb *msg, int lv_only,
+// const struct gsm_mncc_bearer_cap *bcap);
+///* decode 'call control cap' */
+//int gsm48_decode_cccap(struct gsm_mncc_cccap *ccap, const uint8_t *lv);
+///* encode 'call control cap' */
+//int gsm48_encode_cccap(struct msgb *msg,
+// const struct gsm_mncc_cccap *ccap);
+///* decode 'called party BCD number' */
+//int gsm48_decode_called(struct gsm_mncc_number *called,
+// const uint8_t *lv);
+///* encode 'called party BCD number' */
+//int gsm48_encode_called(struct msgb *msg,
+// const struct gsm_mncc_number *called);
+///* decode callerid of various IEs */
+//int gsm48_decode_callerid(struct gsm_mncc_number *callerid,
+// const uint8_t *lv);
+///* encode callerid of various IEs */
+//int gsm48_encode_callerid(struct msgb *msg, int ie, int max_len,
+// const struct gsm_mncc_number *callerid);
+///* decode 'cause' */
+//int gsm48_decode_cause(struct gsm_mncc_cause *cause,
+// const uint8_t *lv);
+///* encode 'cause' */
+//int gsm48_encode_cause(struct msgb *msg, int lv_only,
+// const struct gsm_mncc_cause *cause);
+///* decode 'calling number' */
+//int gsm48_decode_calling(struct gsm_mncc_number *calling,
+// const uint8_t *lv);
+///* encode 'calling number' */
+//int gsm48_encode_calling(struct msgb *msg,
+// const struct gsm_mncc_number *calling);
+///* decode 'connected number' */
+//int gsm48_decode_connected(struct gsm_mncc_number *connected,
+// const uint8_t *lv);
+///* encode 'connected number' */
+//int gsm48_encode_connected(struct msgb *msg,
+// const struct gsm_mncc_number *connected);
+///* decode 'redirecting number' */
+//int gsm48_decode_redirecting(struct gsm_mncc_number *redirecting,
+// const uint8_t *lv);
+///* encode 'redirecting number' */
+//int gsm48_encode_redirecting(struct msgb *msg,
+// const struct gsm_mncc_number *redirecting);
+///* decode 'facility' */
+//int gsm48_decode_facility(struct gsm_mncc_facility *facility,
+// const uint8_t *lv);
+///* encode 'facility' */
+//int gsm48_encode_facility(struct msgb *msg, int lv_only,
+// const struct gsm_mncc_facility *facility);
+///* decode 'notify' */
+//int gsm48_decode_notify(int *notify, const uint8_t *v);
+///* encode 'notify' */
+//int gsm48_encode_notify(struct msgb *msg, int notify);
+///* decode 'signal' */
+//int gsm48_decode_signal(int *signal, const uint8_t *v);
+///* encode 'signal' */
+//int gsm48_encode_signal(struct msgb *msg, int signal);
+///* decode 'keypad' */
+//int gsm48_decode_keypad(int *keypad, const uint8_t *lv);
+///* encode 'keypad' */
+//int gsm48_encode_keypad(struct msgb *msg, int keypad);
+///* decode 'progress' */
+//int gsm48_decode_progress(struct gsm_mncc_progress *progress,
+// const uint8_t *lv);
+///* encode 'progress' */
+//int gsm48_encode_progress(struct msgb *msg, int lv_only,
+// const struct gsm_mncc_progress *p);
+///* decode 'user-user' */
+//int gsm48_decode_useruser(struct gsm_mncc_useruser *uu,
+// const uint8_t *lv);
+///* encode 'useruser' */
+//int gsm48_encode_useruser(struct msgb *msg, int lv_only,
+// const struct gsm_mncc_useruser *uu);
+///* decode 'ss version' */
+//int gsm48_decode_ssversion(struct gsm_mncc_ssversion *ssv,
+// const uint8_t *lv);
+///* encode 'ss version' */
+//int gsm48_encode_ssversion(struct msgb *msg,
+// const struct gsm_mncc_ssversion *ssv);
+///* decode 'more data' does not require a function, because it has no value */
+///* encode 'more data' */
+//int gsm48_encode_more(struct msgb *msg);
+
+/* structure of one frequency */
+struct gsm_sysinfo_freq {
+ /* if the frequency included in the sysinfo */
+ uint8_t mask;
+};
+
+/* decode "Cell Channel Description" (10.5.2.1b) and other frequency lists */
+int gsm48_decode_freq_list(struct gsm_sysinfo_freq *f, uint8_t *cd,
+ uint8_t len, uint8_t mask, uint8_t frqt);
diff --git a/lib/decoding/osmocom/gsm/kasumi.c b/lib/decoding/osmocom/gsm/kasumi.c
new file mode 100644
index 0000000..7de5cd0
--- /dev/null
+++ b/lib/decoding/osmocom/gsm/kasumi.c
@@ -0,0 +1,188 @@
+/*! \file kasumi.c
+ * Kasumi cipher and KGcore functions. */
+/*
+ * (C) 2013 by Max <Max.Suraev@fairwaves.ru>
+ * All Rights Reserved
+ *
+ * SPDX-License-Identifier: GPL-2.0+
+ *
+ * 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 2 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, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ */
+
+#include <stdint.h>
+#include <osmocom/core/bits.h>
+#include <osmocom/gsm/kasumi.h>
+
+/* See TS 135 202 for constants and full Kasumi spec. */
+inline static uint16_t kasumi_FI(uint16_t I, uint16_t skey)
+{
+ static const uint16_t S7[] = {
+ 54, 50, 62, 56, 22, 34, 94, 96, 38, 6, 63, 93, 2, 18, 123, 33,
+ 55, 113, 39, 114, 21, 67, 65, 12, 47, 73, 46, 27, 25, 111, 124, 81,
+ 53, 9, 121, 79, 52, 60, 58, 48, 101, 127, 40, 120, 104, 70, 71, 43,
+ 20, 122, 72, 61, 23, 109, 13, 100, 77, 1, 16, 7, 82, 10, 105, 98,
+ 117, 116, 76, 11, 89, 106, 0,125,118, 99, 86, 69, 30, 57, 126, 87,
+ 112, 51, 17, 5, 95, 14, 90, 84, 91, 8, 35,103, 32, 97, 28, 66,
+ 102, 31, 26, 45, 75, 4, 85, 92, 37, 74, 80, 49, 68, 29, 115, 44,
+ 64, 107, 108, 24, 110, 83, 36, 78, 42, 19, 15, 41, 88, 119, 59, 3
+ };
+ static const uint16_t S9[] = {
+ 167, 239, 161, 379, 391, 334, 9, 338, 38, 226, 48, 358, 452, 385, 90, 397,
+ 183, 253, 147, 331, 415, 340, 51, 362, 306, 500, 262, 82, 216, 159, 356, 177,
+ 175, 241, 489, 37, 206, 17, 0, 333, 44, 254, 378, 58, 143, 220, 81, 400,
+ 95, 3, 315, 245, 54, 235, 218, 405, 472, 264, 172, 494, 371, 290, 399, 76,
+ 165, 197, 395, 121, 257, 480, 423, 212, 240, 28, 462, 176, 406, 507, 288, 223,
+ 501, 407, 249, 265, 89, 186, 221, 428,164, 74, 440, 196, 458, 421, 350, 163,
+ 232, 158, 134, 354, 13, 250, 491, 142,191, 69, 193, 425, 152, 227, 366, 135,
+ 344, 300, 276, 242, 437, 320, 113, 278, 11, 243, 87, 317, 36, 93, 496, 27,
+ 487, 446, 482, 41, 68, 156, 457, 131, 326, 403, 339, 20, 39, 115, 442, 124,
+ 475, 384, 508, 53, 112, 170, 479, 151, 126, 169, 73, 268, 279, 321, 168, 364,
+ 363, 292, 46, 499, 393, 327, 324, 24, 456, 267, 157, 460, 488, 426, 309, 229,
+ 439, 506, 208, 271, 349, 401, 434, 236, 16, 209, 359, 52, 56, 120, 199, 277,
+ 465, 416, 252, 287, 246, 6, 83, 305, 420, 345, 153,502, 65, 61, 244, 282,
+ 173, 222, 418, 67, 386, 368, 261, 101, 476, 291, 195,430, 49, 79, 166, 330,
+ 280, 383, 373, 128, 382, 408, 155, 495, 367, 388, 274, 107, 459, 417, 62, 454,
+ 132, 225, 203, 316, 234, 14, 301, 91, 503, 286, 424, 211, 347, 307, 140, 374,
+ 35, 103, 125, 427, 19, 214, 453, 146, 498, 314, 444, 230, 256, 329, 198, 285,
+ 50, 116, 78, 410, 10, 205, 510, 171, 231, 45, 139, 467, 29, 86, 505, 32,
+ 72, 26, 342, 150, 313, 490, 431, 238, 411, 325, 149, 473, 40, 119, 174, 355,
+ 185, 233, 389, 71, 448, 273, 372, 55, 110, 178, 322, 12, 469, 392, 369, 190,
+ 1, 109, 375, 137, 181, 88, 75, 308, 260, 484, 98, 272, 370, 275, 412, 111,
+ 336, 318, 4, 504, 492, 259, 304, 77, 337, 435, 21, 357, 303, 332, 483, 18,
+ 47, 85, 25, 497, 474, 289, 100, 269, 296, 478, 270, 106, 31, 104, 433, 84,
+ 414, 486, 394, 96, 99, 154, 511, 148, 413, 361, 409, 255, 162, 215, 302, 201,
+ 266, 351, 343, 144, 441, 365, 108, 298, 251, 34, 182, 509, 138, 210, 335, 133,
+ 311, 352, 328, 141, 396, 346, 123, 319, 450, 281, 429, 228, 443, 481, 92, 404,
+ 485, 422, 248, 297, 23, 213, 130, 466, 22, 217, 283, 70, 294, 360, 419, 127,
+ 312, 377, 7, 468, 194, 2, 117, 295, 463, 258, 224, 447, 247, 187, 80, 398,
+ 284, 353, 105, 390, 299, 471, 470, 184, 57, 200, 348, 63, 204, 188, 33, 451,
+ 97, 30, 310, 219, 94, 160, 129, 493, 64, 179, 263, 102, 189, 207, 114, 402,
+ 438, 477, 387, 122, 192, 42, 381, 5, 145, 118, 180, 449, 293, 323, 136, 380,
+ 43, 66, 60, 455, 341, 445, 202, 432, 8, 237, 15, 376, 436, 464, 59, 461
+ };
+ uint16_t L, R;
+
+ /* Split 16 bit input into two unequal halves: 9 and 7 bits, same for subkey */
+ L = I >> 7; /* take 9 bits */
+ R = I & 0x7F; /* take 7 bits */
+
+ L = S9[L] ^ R;
+ R = S7[R] ^ (L & 0x7F);
+
+ L ^= (skey & 0x1FF);
+ R ^= (skey >> 9);
+
+ L = S9[L] ^ R;
+ R = S7[R] ^ (L & 0x7F);
+
+ return (R << 9) + L;
+}
+
+inline static uint32_t kasumi_FO(uint32_t I, const uint16_t *KOi1, const uint16_t *KOi2, const uint16_t *KOi3, const uint16_t *KIi1, const uint16_t *KIi2, const uint16_t *KIi3, unsigned i)
+{
+ uint16_t L = I >> 16, R = I; /* Split 32 bit input into Left and Right parts */
+
+ L ^= KOi1[i];
+ L = kasumi_FI(L, KIi1[i]);
+ L ^= R;
+
+ R ^= KOi2[i];
+ R = kasumi_FI(R, KIi2[i]);
+ R ^= L;
+
+ L ^= KOi3[i];
+ L = kasumi_FI(L, KIi3[i]);
+ L ^= R;
+
+ return (((uint32_t)R) << 16) + L;
+}
+
+inline static uint32_t kasumi_FL(uint32_t I, const uint16_t *KLi1, const uint16_t *KLi2, unsigned i)
+{
+ uint16_t L = I >> 16, R = I, tmp; /* Split 32 bit input into Left and Right parts */
+
+ tmp = L & KLi1[i];
+ R ^= osmo_rol16(tmp, 1);
+
+ tmp = R | KLi2[i];
+ L ^= osmo_rol16(tmp, 1);
+
+ return (((uint32_t)L) << 16) + R;
+}
+
+uint64_t _kasumi(uint64_t P, const uint16_t *KLi1, const uint16_t *KLi2, const uint16_t *KOi1, const uint16_t *KOi2, const uint16_t *KOi3, const uint16_t *KIi1, const uint16_t *KIi2, const uint16_t *KIi3)
+{
+ uint32_t i, L = P >> 32, R = P; /* Split 64 bit input into Left and Right parts */
+
+ for (i = 0; i < 8; i++) {
+ R ^= kasumi_FO(kasumi_FL(L, KLi1, KLi2, i), KOi1, KOi2, KOi3, KIi1, KIi2, KIi3, i); /* odd round */
+ i++;
+ L ^= kasumi_FL(kasumi_FO(R, KOi1, KOi2, KOi3, KIi1, KIi2, KIi3, i), KLi1, KLi2, i); /* even round */
+ }
+ return (((uint64_t)L) << 32) + R; /* Concatenate Left and Right 32 bits into 64 bit ciphertext */
+}
+
+void _kasumi_key_expand(const uint8_t *key, uint16_t *KLi1, uint16_t *KLi2, uint16_t *KOi1, uint16_t *KOi2, uint16_t *KOi3, uint16_t *KIi1, uint16_t *KIi2, uint16_t *KIi3)
+{
+ uint16_t i, C[] = { 0x0123, 0x4567, 0x89AB, 0xCDEF, 0xFEDC, 0xBA98, 0x7654, 0x3210 };
+
+ /* Work with 16 bit subkeys and create prime subkeys */
+ for (i = 0; i < 8; i++)
+ C[i] ^= osmo_load16be(key + i * 2);
+ /* C[] now stores K-prime[] */
+
+ /* Create round-specific subkeys */
+ for (i = 0; i < 8; i++) {
+ KLi1[i] = osmo_rol16(osmo_load16be(key + i * 2), 1);
+ KLi2[i] = C[(i + 2) & 0x7];
+
+ KOi1[i] = osmo_rol16(osmo_load16be(key + ((2 * (i + 1)) & 0xE)), 5);
+ KOi2[i] = osmo_rol16(osmo_load16be(key + ((2 * (i + 5)) & 0xE)), 8);
+ KOi3[i] = osmo_rol16(osmo_load16be(key + ((2 * (i + 6)) & 0xE)), 13);
+
+ KIi1[i] = C[(i + 4) & 0x7];
+ KIi2[i] = C[(i + 3) & 0x7];
+ KIi3[i] = C[(i + 7) & 0x7];
+ }
+}
+
+void _kasumi_kgcore(uint8_t CA, uint8_t cb, uint32_t cc, uint8_t cd, const uint8_t *ck, uint8_t *co, uint16_t cl)
+{
+ uint16_t KLi1[8], KLi2[8], KOi1[8], KOi2[8], KOi3[8], KIi1[8], KIi2[8], KIi3[8], i;
+ uint64_t A = ((uint64_t)cc) << 32, BLK = 0, _ca = ((uint64_t)CA << 16) ;
+ A |= _ca;
+ _ca = (uint64_t)((cb << 3) | (cd << 2)) << 24;
+ A |= _ca;
+ /* Register loading complete: see TR 55.919 8.2 and TS 55.216 3.2 */
+
+ uint8_t ck_km[16];
+ for (i = 0; i < 16; i++)
+ ck_km[i] = ck[i] ^ 0x55;
+ /* Modified key established */
+
+ /* preliminary round with modified key */
+ _kasumi_key_expand(ck_km, KLi1, KLi2, KOi1, KOi2, KOi3, KIi1, KIi2, KIi3);
+ A = _kasumi(A, KLi1, KLi2, KOi1, KOi2, KOi3, KIi1, KIi2, KIi3);
+
+ /* Run Kasumi in OFB to obtain enough data for gamma. */
+ _kasumi_key_expand(ck, KLi1, KLi2, KOi1, KOi2, KOi3, KIi1, KIi2, KIi3);
+
+ /* i is a block counter */
+ for (i = 0; i < cl / 64 + 1; i++) {
+ BLK = _kasumi(A ^ i ^ BLK, KLi1, KLi2, KOi1, KOi2, KOi3, KIi1, KIi2, KIi3);
+ osmo_store64be(BLK, co + (i * 8));
+ }
+}
diff --git a/lib/decoding/osmocom/gsm/kasumi.h b/lib/decoding/osmocom/gsm/kasumi.h
new file mode 100644
index 0000000..d9de10b
--- /dev/null
+++ b/lib/decoding/osmocom/gsm/kasumi.h
@@ -0,0 +1,48 @@
+/*! \file kasumi.h
+ * KASUMI header.
+ *
+ * See kasumi.c for details
+ * The parameters are described in TS 135 202.
+ */
+
+#pragma once
+
+#include <stdint.h>
+
+/*! Single iteration of KASUMI cipher
+ * \param[in] P Block, 64 bits to be processed in this round
+ * \param[in] KLi1 Expanded subkeys
+ * \param[in] KLi2 Expanded subkeys
+ * \param[in] KOi1 Expanded subkeys
+ * \param[in] KOi2 Expanded subkeys
+ * \param[in] KOi3 Expanded subkeys
+ * \param[in] KIi1 Expanded subkeys
+ * \param[in] KIi2 Expanded subkeys
+ * \param[in] KIi3 Expanded subkeys
+ * \returns processed block of 64 bits
+ */
+uint64_t _kasumi(uint64_t P, const uint16_t *KLi1, const uint16_t *KLi2, const uint16_t *KOi1, const uint16_t *KOi2, const uint16_t *KOi3, const uint16_t *KIi1, const uint16_t *KIi2, const uint16_t *KIi3);
+
+/*! Implementation of the KGCORE algorithm (used by A5/3, A5/4, GEA3, GEA4 and ECSD)
+ * \param[in] CA
+ * \param[in] cb
+ * \param[in] cc
+ * \param[in] cd
+ * \param[in] ck 8-bytes long key
+ * \param[out] co cl-dependent
+ * \param[in] cl
+ */
+void _kasumi_kgcore(uint8_t CA, uint8_t cb, uint32_t cc, uint8_t cd, const uint8_t *ck, uint8_t *co, uint16_t cl);
+
+/*! Expand key into set of subkeys - see TS 135 202 for details
+ * \param[in] key (128 bits) as array of bytes
+ * \param[out] KLi1 Expanded subkeys
+ * \param[out] KLi2 Expanded subkeys
+ * \param[out] KOi1 Expanded subkeys
+ * \param[out] KOi2 Expanded subkeys
+ * \param[out] KOi3 Expanded subkeys
+ * \param[out] KIi1 Expanded subkeys
+ * \param[out] KIi2 Expanded subkeys
+ * \param[out] KIi3 Expanded subkeys
+ */
+void _kasumi_key_expand(const uint8_t *key, uint16_t *KLi1, uint16_t *KLi2, uint16_t *KOi1, uint16_t *KOi2, uint16_t *KOi3, uint16_t *KIi1, uint16_t *KIi2, uint16_t *KIi3);
diff --git a/lib/decoding/osmocom/gsm/protocol/gsm_04_08.h b/lib/decoding/osmocom/gsm/protocol/gsm_04_08.h
new file mode 100644
index 0000000..a92e025
--- /dev/null
+++ b/lib/decoding/osmocom/gsm/protocol/gsm_04_08.h
@@ -0,0 +1,1683 @@
+/*! \file gsm_04_08.h
+ * GSM TS 04.08 definitions. */
+
+#pragma once
+
+#include <stdint.h>
+#include <stdbool.h>
+
+#include <osmocom/core/utils.h>
+#include <osmocom/core/endian.h>
+
+struct gsm_lchan;
+
+/* Chapter 10.5.1.5 */
+struct gsm48_classmark1 {
+ uint8_t pwr_lev:3,
+ a5_1:1,
+ es_ind:1,
+ rev_lev:2,
+ spare:1;
+} __attribute__ ((packed));
+
+/* Chapter 10.5.1.6 */
+struct gsm48_classmark2 {
+ uint8_t pwr_lev:3,
+ a5_1:1,
+ es_ind:1,
+ rev_lev:2,
+ spare:1;
+ uint8_t fc:1,
+ vgcs:1,
+ vbs:1,
+ sm_cap:1,
+ ss_scr:2,
+ ps_cap:1,
+ spare2:1;
+ uint8_t a5_2:1,
+ a5_3:1,
+ cmsp:1,
+ solsa:1,
+ spare3:1,
+ lcsva_cap:1,
+ spare4:1,
+ cm3:1;
+} __attribute__ ((packed));
+
+/* Chapter 10.5.2.1b.3 */
+#if OSMO_IS_LITTLE_ENDIAN == 1
+struct gsm48_range_1024 {
+ uint8_t w1_hi:2,
+ f0:1,
+ form_id:5;
+ uint8_t w1_lo;
+ uint8_t w2_hi;
+ uint8_t w3_hi:7,
+ w2_lo:1;
+ uint8_t w4_hi:6,
+ w3_lo:2;
+ uint8_t w5_hi:6,
+ w4_lo:2;
+ uint8_t w6_hi:6,
+ w5_lo:2;
+ uint8_t w7_hi:6,
+ w6_lo:2;
+ uint8_t w8_hi:6,
+ w7_lo:2;
+ uint8_t w9:7,
+ w8_lo:1;
+ uint8_t w11_hi:1,
+ w10:7;
+ uint8_t w12_hi:2,
+ w11_lo:6;
+ uint8_t w13_hi:3,
+ w12_lo:5;
+ uint8_t w14_hi:4,
+ w13_lo:4;
+ uint8_t w15_hi:5,
+ w14_lo:3;
+ uint8_t w16:6,
+ w15_lo:2;
+} __attribute__ ((packed));
+#else
+struct gsm48_range_1024 {
+ uint8_t form_id:5,
+ f0:1,
+ w1_hi:2;
+ uint8_t w1_lo;
+ uint8_t w2_hi;
+ uint8_t w2_lo:1,
+ w3_hi:7;
+ uint8_t w3_lo:2,
+ w4_hi:6;
+ uint8_t w4_lo:2,
+ w5_hi:6;
+ uint8_t w5_lo:2,
+ w6_hi:6;
+ uint8_t w6_lo:2,
+ w7_hi:6;
+ uint8_t w7_lo:2,
+ w8_hi:6;
+ uint8_t w8_lo:1,
+ w9:7;
+ uint8_t w10:7,
+ w11_hi:1;
+ uint8_t w11_lo:6,
+ w12_hi:2;
+ uint8_t w12_lo:5,
+ w13_hi:3;
+ uint8_t w13_lo:4,
+ w14_hi:4;
+ uint8_t w14_lo:3,
+ w15_hi:5;
+ uint8_t w15_lo:2,
+ w16:6;
+} __attribute__ ((packed));
+#endif
+
+/* Chapter 10.5.2.1b.4 */
+#if OSMO_IS_LITTLE_ENDIAN == 1
+struct gsm48_range_512 {
+ uint8_t orig_arfcn_hi:1,
+ form_id:7;
+ uint8_t orig_arfcn_mid;
+ uint8_t w1_hi:7,
+ orig_arfcn_lo:1;
+ uint8_t w2_hi:6,
+ w1_lo:2;
+ uint8_t w3_hi:6,
+ w2_lo:2;
+ uint8_t w4_hi:6,
+ w3_lo:2;
+ uint8_t w5:7,
+ w4_lo:1;
+ uint8_t w7_hi:1,
+ w6:7;
+ uint8_t w8_hi:2,
+ w7_lo:6;
+ uint8_t w9_hi:4,
+ w8_lo:4;
+ uint8_t w10:6,
+ w9_lo:2;
+ uint8_t w12_hi:2,
+ w11:6;
+ uint8_t w13_hi:4,
+ w12_lo:4;
+ uint8_t w14:6,
+ w13_lo:2;
+ uint8_t w16_hi:2,
+ w15:6;
+ uint8_t w17:5,
+ w16_lo:3;
+} __attribute__ ((packed));
+#else
+struct gsm48_range_512 {
+ uint8_t form_id:7,
+ orig_arfcn_hi:1;
+ uint8_t orig_arfcn_mid;
+ uint8_t orig_arfcn_lo:1,
+ w1_hi:7;
+ uint8_t w1_lo:2,
+ w2_hi:6;
+ uint8_t w2_lo:2,
+ w3_hi:6;
+ uint8_t w3_lo:2,
+ w4_hi:6;
+ uint8_t w4_lo:1,
+ w5:7;
+ uint8_t w6:7,
+ w7_hi:1;
+ uint8_t w7_lo:6,
+ w8_hi:2;
+ uint8_t w8_lo:4,
+ w9_hi:4;
+ uint8_t w9_lo:2,
+ w10:6;
+ uint8_t w11:6,
+ w12_hi:2;
+ uint8_t w12_lo:4,
+ w13_hi:4;
+ uint8_t w13_lo:2,
+ w14:6;
+ uint8_t w15:6,
+ w16_hi:2;
+ uint8_t w16_lo:3,
+ w17:5;
+} __attribute__ ((packed));
+#endif
+
+/* Chapter 10.5.2.1b.5 */
+#if OSMO_IS_LITTLE_ENDIAN == 1
+struct gsm48_range_256 {
+ uint8_t orig_arfcn_hi:1,
+ form_id:7;
+ uint8_t orig_arfcn_mid;
+ uint8_t w1_hi:7,
+ orig_arfcn_lo:1;
+ uint8_t w2:7,
+ w1_lo:1;
+ uint8_t w4_hi:1,
+ w3:7;
+ uint8_t w5_hi:3,
+ w4_lo:5;
+ uint8_t w6_hi:5,
+ w5_lo:3;
+ uint8_t w8_hi:1,
+ w7:6,
+ w6_lo:1;
+ uint8_t w9_hi:4,
+ w8_lo:4;
+ uint8_t w11_hi:2,
+ w10:5,
+ w9_lo:1;
+ uint8_t w12:5,
+ w11_lo:3;
+ uint8_t w14_hi:3,
+ w13:5;
+ uint8_t w16_hi:1,
+ w15:5,
+ w14_lo:2;
+ uint8_t w18_hi:1,
+ w17:4,
+ w16_lo:3;
+ uint8_t w20_hi:1,
+ w19:4,
+ w18_lo:3;
+ uint8_t spare:1,
+ w21:4,
+ w20_lo:3;
+} __attribute__ ((packed));
+#else
+struct gsm48_range_256 {
+ uint8_t form_id:7,
+ orig_arfcn_hi:1;
+ uint8_t orig_arfcn_mid;
+ uint8_t orig_arfcn_lo:1,
+ w1_hi:7;
+ uint8_t w1_lo:1,
+ w2:7;
+ uint8_t w3:7,
+ w4_hi:1;
+ uint8_t w4_lo:5,
+ w5_hi:3;
+ uint8_t w5_lo:3,
+ w6_hi:5;
+ uint8_t w6_lo:1,
+ w7:6,
+ w8_hi:1;
+ uint8_t w8_lo:4,
+ w9_hi:4;
+ uint8_t w9_lo:1,
+ w10:5,
+ w11_hi:2;
+ uint8_t w11_lo:3,
+ w12:5;
+ uint8_t w13:5,
+ w14_hi:3;
+ uint8_t w14_lo:2,
+ w15:5,
+ w16_hi:1;
+ uint8_t w16_lo:3,
+ w17:4,
+ w18_hi:1;
+ uint8_t w18_lo:3,
+ w19:4,
+ w20_hi:1;
+ uint8_t w20_lo:3,
+ w21:4,
+ spare:1;
+} __attribute__ ((packed));
+#endif
+
+/* Chapter 10.5.2.1b.6 */
+#if OSMO_IS_LITTLE_ENDIAN == 1
+struct gsm48_range_128 {
+ uint8_t orig_arfcn_hi:1,
+ form_id:7;
+ uint8_t orig_arfcn_mid;
+ uint8_t w1:7,
+ orig_arfcn_lo:1;
+ uint8_t w3_hi:2,
+ w2:6;
+ uint8_t w4_hi:4,
+ w3_lo:4;
+ uint8_t w6_hi:2,
+ w5:5,
+ w4_lo:1;
+ uint8_t w7:5,
+ w6_lo:3;
+ uint8_t w9:4,
+ w8:4;
+ uint8_t w11:4,
+ w10:4;
+ uint8_t w13:4,
+ w12:4;
+ uint8_t w15:4,
+ w14:4;
+ uint8_t w18_hi:2,
+ w17:3,
+ w16:3;
+ uint8_t w21_hi:1,
+ w20:3,
+ w19:3,
+ w18_lo:1;
+ uint8_t w23:3,
+ w22:3,
+ w21_lo:2;
+ uint8_t w26_hi:2,
+ w25:3,
+ w24:3;
+ uint8_t spare:1,
+ w28:3,
+ w27:3,
+ w26_lo:1;
+} __attribute__ ((packed));
+#else
+struct gsm48_range_128 {
+ uint8_t form_id:7,
+ orig_arfcn_hi:1;
+ uint8_t orig_arfcn_mid;
+ uint8_t orig_arfcn_lo:1,
+ w1:7;
+ uint8_t w2:6,
+ w3_hi:2;
+ uint8_t w3_lo:4,
+ w4_hi:4;
+ uint8_t w4_lo:1,
+ w5:5,
+ w6_hi:2;
+ uint8_t w6_lo:3,
+ w7:5;
+ uint8_t w8:4,
+ w9:4;
+ uint8_t w10:4,
+ w11:4;
+ uint8_t w12:4,
+ w13:4;
+ uint8_t w14:4,
+ w15:4;
+ uint8_t w16:3,
+ w17:3,
+ w18_hi:2;
+ uint8_t w18_lo:1,
+ w19:3,
+ w20:3,
+ w21_hi:1;
+ uint8_t w21_lo:2,
+ w22:3,
+ w23:3;
+ uint8_t w24:3,
+ w25:3,
+ w26_hi:2;
+ uint8_t w26_lo:1,
+ w27:3,
+ w28:3,
+ spare:1;
+} __attribute__ ((packed));
+#endif
+
+/* Chapter 10.5.2.1b.7 */
+struct gsm48_var_bit {
+ uint8_t orig_arfcn_hi:1,
+ form_id:7;
+ uint8_t orig_arfcn_mid;
+ uint8_t rrfcn1_7:7,
+ orig_arfcn_lo:1;
+ uint8_t rrfcn8_111[13];
+} __attribute__ ((packed));
+
+/* Chapter 10.5.2.5 */
+struct gsm48_chan_desc {
+ uint8_t chan_nr;
+ union {
+ struct {
+ uint8_t maio_high:4,
+ h:1,
+ tsc:3;
+ uint8_t hsn:6,
+ maio_low:2;
+ } __attribute__ ((packed)) h1;
+ struct {
+ uint8_t arfcn_high:2,
+ spare:2,
+ h:1,
+ tsc:3;
+ uint8_t arfcn_low;
+ } __attribute__ ((packed)) h0;
+ } __attribute__ ((packed));
+} __attribute__ ((packed));
+
+/* Chapter 10.5.2.20 */
+struct gsm48_meas_res {
+ uint8_t rxlev_full:6,
+ dtx_used:1,
+ ba_used:1;
+ uint8_t rxlev_sub:6,
+ meas_valid:1,
+ spare:1;
+ uint8_t no_nc_n_hi:1,
+ rxqual_sub:3,
+ rxqual_full:3,
+ spare2:1;
+ uint8_t rxlev_nc1:6,
+ no_nc_n_lo:2;
+ uint8_t bsic_nc1_hi:3,
+ bcch_f_nc1:5;
+ uint8_t rxlev_nc2_hi:5,
+ bsic_nc1_lo:3;
+ uint8_t bsic_nc2_hi:2,
+ bcch_f_nc2:5,
+ rxlev_nc2_lo:1;
+ uint8_t rxlev_nc3_hi:4,
+ bsic_nc2_lo:4;
+ uint8_t bsic_nc3_hi:1,
+ bcch_f_nc3:5,
+ rxlev_nc3_lo:2;
+ uint8_t rxlev_nc4_hi:3,
+ bsic_nc3_lo:5;
+ uint8_t bcch_f_nc4:5,
+ rxlev_nc4_lo:3;
+ uint8_t rxlev_nc5_hi:2,
+ bsic_nc4:6;
+ uint8_t bcch_f_nc5_hi:4,
+ rxlev_nc5_lo:4;
+ uint8_t rxlev_nc6_hi:1,
+ bsic_nc5:6,
+ bcch_f_nc5_lo:1;
+ uint8_t bcch_f_nc6_hi:3,
+ rxlev_nc6_lo:5;
+ uint8_t bsic_nc6:6,
+ bcch_f_nc6_lo:2;
+} __attribute__ ((packed));
+
+/* Chapter 10.5.2.21aa */
+struct gsm48_multi_rate_conf {
+ uint8_t smod : 2,
+ spare: 1,
+ icmi : 1,
+ nscb : 1,
+ ver : 3;
+ uint8_t m4_75 : 1,
+ m5_15 : 1,
+ m5_90 : 1,
+ m6_70 : 1,
+ m7_40 : 1,
+ m7_95 : 1,
+ m10_2 : 1,
+ m12_2 : 1;
+} __attribute__((packed));
+
+/* Chapter 10.5.2.28(a) */
+struct gsm48_power_cmd {
+ uint8_t power_level:5,
+ spare:2,
+ atc:1;
+} __attribute__((packed));
+
+/* Chapter 10.5.2.29 */
+struct gsm48_rach_control {
+ uint8_t re :1,
+ cell_bar :1,
+ tx_integer :4,
+ max_trans :2;
+ uint8_t t2; /* ACC 8-15 barred flags */
+ uint8_t t3; /* ACC 0-7 barred flags */
+} __attribute__ ((packed));
+
+
+/* Chapter 10.5.2.30 */
+struct gsm48_req_ref {
+ uint8_t ra;
+ uint8_t t3_high:3,
+ t1:5;
+ uint8_t t2:5,
+ t3_low:3;
+} __attribute__ ((packed));
+
+/* Chapter 10.5.2.38 */
+struct gsm48_start_time {
+ uint8_t t3_high:3,
+ t1:5;
+ uint8_t t2:5,
+ t3_low:3;
+} __attribute__ ((packed));
+
+/* Chapter 10.5.2.39 */
+struct gsm48_sync_ind {
+ uint8_t si:2,
+ rot:1,
+ nci:1,
+ sync_ie:4;
+} __attribute__((packed));
+
+/*
+ * Chapter 9.1.5/9.1.6
+ *
+ * For 9.1.6 the chan_desc has the meaning of 10.5.2.5a
+ */
+struct gsm48_chan_mode_modify {
+ struct gsm48_chan_desc chan_desc;
+ uint8_t mode;
+} __attribute__ ((packed));
+
+enum gsm48_chan_mode {
+ GSM48_CMODE_SIGN = 0x00,
+ GSM48_CMODE_SPEECH_V1 = 0x01,
+ GSM48_CMODE_SPEECH_EFR = 0x21,
+ GSM48_CMODE_SPEECH_AMR = 0x41,
+ GSM48_CMODE_DATA_14k5 = 0x0f,
+ GSM48_CMODE_DATA_12k0 = 0x03,
+ GSM48_CMODE_DATA_6k0 = 0x0b,
+ GSM48_CMODE_DATA_3k6 = 0x13,
+};
+
+extern const struct value_string gsm48_chan_mode_names[];
+
+/* Chapter 9.1.2 */
+struct gsm48_ass_cmd {
+ /* Semantic is from 10.5.2.5a */
+ struct gsm48_chan_desc chan_desc;
+ uint8_t power_command;
+ uint8_t data[0];
+} __attribute__((packed));
+
+/* Chapter 9.1.13 */
+struct gsm48_frq_redef {
+ /* Semantic is from 10.5.2.5a */
+ struct gsm48_chan_desc chan_desc;
+ uint8_t mob_alloc_len;
+ uint8_t mob_alloc[0];
+} __attribute__((packed));
+
+/* Chapter 9.1.13b GPRS suspension request */
+struct gsm48_gprs_susp_req {
+ uint32_t tlli;
+ uint8_t ra_id[6];
+ uint8_t cause;
+ uint8_t options[0];
+} __attribute__ ((packed));
+
+/* Chapter 10.5.2.2 */
+struct gsm48_cell_desc {
+ uint8_t bcc:3,
+ ncc:3,
+ arfcn_hi:2;
+ uint8_t arfcn_lo;
+} __attribute__((packed));
+
+/* Chapter 9.1.15 */
+struct gsm48_ho_cmd {
+ struct gsm48_cell_desc cell_desc;
+ struct gsm48_chan_desc chan_desc;
+ uint8_t ho_ref;
+ uint8_t power_command;
+ uint8_t data[0];
+} __attribute__((packed));
+
+/* Chapter 9.1.18 */
+struct gsm48_imm_ass {
+ uint8_t l2_plen;
+ uint8_t proto_discr;
+ uint8_t msg_type;
+ uint8_t page_mode;
+ struct gsm48_chan_desc chan_desc;
+ struct gsm48_req_ref req_ref;
+ uint8_t timing_advance;
+ uint8_t mob_alloc_len;
+ uint8_t mob_alloc[0];
+} __attribute__ ((packed));
+
+/* Chapter 9.1.25 */
+struct gsm48_pag_resp {
+ uint8_t spare:4,
+ key_seq:4;
+ uint32_t classmark2;
+ uint8_t mi_len;
+ uint8_t mi[0];
+} __attribute__ ((packed));
+
+/* Chapter 10.5.1.3 */
+struct gsm48_loc_area_id {
+ uint8_t digits[3]; /* BCD! */
+ uint16_t lac;
+} __attribute__ ((packed));
+
+/* Section 9.2.2 */
+struct gsm48_auth_req {
+ uint8_t key_seq:4,
+ spare:4;
+ uint8_t rand[16];
+} __attribute__ ((packed));
+
+/* Section 9.2.3 */
+struct gsm48_auth_resp {
+ uint8_t sres[4];
+} __attribute__ ((packed));
+
+/* Section 9.2.15 */
+struct gsm48_loc_upd_req {
+ uint8_t type:4,
+ key_seq:4;
+ struct gsm48_loc_area_id lai;
+ struct gsm48_classmark1 classmark1;
+ uint8_t mi_len;
+ uint8_t mi[0];
+} __attribute__ ((packed));
+
+/* Section 10.1 */
+struct gsm48_hdr {
+ uint8_t proto_discr;
+ uint8_t msg_type;
+ uint8_t data[0];
+} __attribute__ ((packed));
+
+/* Section 9.1.3x System information Type header */
+struct gsm48_system_information_type_header {
+ uint8_t l2_plen;
+ uint8_t rr_protocol_discriminator :4,
+ skip_indicator:4;
+ uint8_t system_information;
+} __attribute__ ((packed));
+
+/* Section 10.5.2.4 Cell Selection Parameters */
+struct gsm48_cell_sel_par {
+ uint8_t ms_txpwr_max_ccch:5, /* GSM 05.08 MS-TXPWR-MAX-CCCH */
+ cell_resel_hyst:3; /* GSM 05.08 CELL-RESELECT-HYSTERESIS */
+ uint8_t rxlev_acc_min:6, /* GSM 05.08 RXLEV-ACCESS-MIN */
+ neci:1,
+ acs:1;
+} __attribute__ ((packed));
+
+/* 3GPP TS 44.018 Section 10.5.2.11 Control Channel Description */
+struct gsm48_control_channel_descr {
+ uint8_t ccch_conf :3,
+ bs_ag_blks_res :3,
+ att :1,
+ mscr :1;
+ uint8_t bs_pa_mfrms : 3,
+ spare_1 :2,
+ cbq3 :2,
+ spare_2 :1;
+ uint8_t t3212;
+} __attribute__ ((packed));
+
+enum gsm48_dtx_mode {
+ GSM48_DTX_MAY_BE_USED,
+ GSM48_DTX_SHALL_BE_USED,
+ GSM48_DTX_SHALL_NOT_BE_USED
+};
+
+/* Cell Options for SI6, SACCH (10.5.2.3a.2) or SI3, BCCH (Table 10.5.2.3.1),
+ 3GPP TS 44.018 */
+struct gsm48_cell_options {
+ uint8_t radio_link_timeout:4,
+ dtx:2,
+ pwrc:1,
+ /* either DN-IND or top bit of DTX IND */
+ d:1;
+} __attribute__ ((packed));
+
+/* Section 9.2.9 CM service request */
+struct gsm48_service_request {
+ uint8_t cm_service_type : 4,
+ cipher_key_seq : 4;
+ /* length + 3 bytes */
+ uint32_t classmark;
+ uint8_t mi_len;
+ uint8_t mi[0];
+ /* optional priority level */
+} __attribute__ ((packed));
+
+/* Section 9.1.31 System information Type 1 */
+struct gsm48_system_information_type_1 {
+ struct gsm48_system_information_type_header header;
+ uint8_t cell_channel_description[16];
+ struct gsm48_rach_control rach_control;
+ uint8_t rest_octets[0]; /* NCH position on the CCCH */
+} __attribute__ ((packed));
+
+/* Section 9.1.32 System information Type 2 */
+struct gsm48_system_information_type_2 {
+ struct gsm48_system_information_type_header header;
+ uint8_t bcch_frequency_list[16];
+ uint8_t ncc_permitted;
+ struct gsm48_rach_control rach_control;
+} __attribute__ ((packed));
+
+/* Section 9.1.33 System information Type 2bis */
+struct gsm48_system_information_type_2bis {
+ struct gsm48_system_information_type_header header;
+ uint8_t bcch_frequency_list[16];
+ struct gsm48_rach_control rach_control;
+ uint8_t rest_octets[0];
+} __attribute__ ((packed));
+
+/* Section 9.1.34 System information Type 2ter */
+struct gsm48_system_information_type_2ter {
+ struct gsm48_system_information_type_header header;
+ uint8_t ext_bcch_frequency_list[16];
+ uint8_t rest_octets[0];
+} __attribute__ ((packed));
+
+/* Section 9.1.34a System information Type 2quater */
+struct gsm48_system_information_type_2quater {
+ struct gsm48_system_information_type_header header;
+ uint8_t rest_octets[0];
+} __attribute__ ((packed));
+
+/* Section 9.1.35 System information Type 3 */
+struct gsm48_system_information_type_3 {
+ struct gsm48_system_information_type_header header;
+ uint16_t cell_identity;
+ struct gsm48_loc_area_id lai;
+ struct gsm48_control_channel_descr control_channel_desc;
+ struct gsm48_cell_options cell_options;
+ struct gsm48_cell_sel_par cell_sel_par;
+ struct gsm48_rach_control rach_control;
+ uint8_t rest_octets[0];
+} __attribute__ ((packed));
+
+/* Section 9.1.36 System information Type 4 */
+struct gsm48_system_information_type_4 {
+ struct gsm48_system_information_type_header header;
+ struct gsm48_loc_area_id lai;
+ struct gsm48_cell_sel_par cell_sel_par;
+ struct gsm48_rach_control rach_control;
+ /* optional CBCH conditional CBCH... followed by
+ mandantory SI 4 Reset Octets
+ */
+ uint8_t data[0];
+} __attribute__ ((packed));
+
+/* Section 9.1.37 System information Type 5 */
+struct gsm48_system_information_type_5 {
+ uint8_t rr_protocol_discriminator :4,
+ skip_indicator:4;
+ uint8_t system_information;
+ uint8_t bcch_frequency_list[16];
+} __attribute__ ((packed));
+
+/* Section 9.1.38 System information Type 5bis */
+struct gsm48_system_information_type_5bis {
+ uint8_t rr_protocol_discriminator :4,
+ skip_indicator:4;
+ uint8_t system_information;
+ uint8_t bcch_frequency_list[16];
+} __attribute__ ((packed));
+
+/* Section 9.1.39 System information Type 5ter */
+struct gsm48_system_information_type_5ter {
+ uint8_t rr_protocol_discriminator :4,
+ skip_indicator:4;
+ uint8_t system_information;
+ uint8_t bcch_frequency_list[16];
+} __attribute__ ((packed));
+
+/* Section 9.1.40 System information Type 6 */
+struct gsm48_system_information_type_6 {
+ uint8_t rr_protocol_discriminator :4,
+ skip_indicator:4;
+ uint8_t system_information;
+ uint16_t cell_identity;
+ struct gsm48_loc_area_id lai;
+ struct gsm48_cell_options cell_options;
+ uint8_t ncc_permitted;
+ uint8_t rest_octets[0];
+} __attribute__ ((packed));
+
+/* Section 9.1.43a System Information type 13 */
+struct gsm48_system_information_type_13 {
+ struct gsm48_system_information_type_header header;
+ uint8_t rest_octets[0];
+} __attribute__ ((packed));
+
+/* Section 9.2.12 IMSI Detach Indication */
+struct gsm48_imsi_detach_ind {
+ struct gsm48_classmark1 classmark1;
+ uint8_t mi_len;
+ uint8_t mi[0];
+} __attribute__ ((packed));
+
+/* Section 9.1.1 */
+struct gsm48_add_ass {
+ /* Semantic is from 10.5.2.5 */
+ struct gsm48_chan_desc chan_desc;
+ uint8_t data[0];
+} __attribute__((packed));
+
+/* Section 9.1.3 */
+struct gsm48_ass_cpl {
+ uint8_t rr_cause;
+} __attribute__((packed));
+
+/* Section 9.1.4 */
+struct gsm48_ass_fail {
+ uint8_t rr_cause;
+} __attribute__((packed));
+
+/* Section 9.1.3 */
+struct gsm48_ho_cpl {
+ uint8_t rr_cause;
+ uint8_t data[0];
+} __attribute__((packed));
+
+/* Section 9.1.4 */
+struct gsm48_ho_fail {
+ uint8_t rr_cause;
+} __attribute__((packed));
+
+/* Section 9.1.7 */
+struct gsm48_chan_rel {
+ uint8_t rr_cause;
+ uint8_t data[0];
+} __attribute__((packed));
+
+/* Section 9.1.9 */
+struct gsm48_cip_mode_cmd {
+ uint8_t sc:1,
+ alg_id:3,
+ cr:1,
+ spare:3;
+} __attribute__((packed));
+
+/* Section 9.1.11 */
+struct gsm48_cm_change {
+ uint8_t cm2_len;
+ struct gsm48_classmark2 cm2;
+ uint8_t data[0];
+} __attribute__((packed));
+
+/* Section 9.1.19 */
+struct gsm48_imm_ass_ext {
+ uint8_t l2_plen;
+ uint8_t proto_discr;
+ uint8_t msg_type;
+ uint8_t page_mode;
+ struct gsm48_chan_desc chan_desc1;
+ struct gsm48_req_ref req_ref1;
+ uint8_t timing_advance1;
+ struct gsm48_chan_desc chan_desc2;
+ struct gsm48_req_ref req_ref2;
+ uint8_t timing_advance2;
+ uint8_t mob_alloc_len;
+ uint8_t mob_alloc[0];
+} __attribute__ ((packed));
+
+/* Section 9.1.20 */
+struct gsm48_imm_ass_rej {
+ uint8_t l2_plen;
+ uint8_t proto_discr;
+ uint8_t msg_type;
+ uint8_t page_mode;
+ struct gsm48_req_ref req_ref1;
+ uint8_t wait_ind1;
+ struct gsm48_req_ref req_ref2;
+ uint8_t wait_ind2;
+ struct gsm48_req_ref req_ref3;
+ uint8_t wait_ind3;
+ struct gsm48_req_ref req_ref4;
+ uint8_t wait_ind4;
+ uint8_t rest[0];
+} __attribute__ ((packed));
+
+/* Section 9.1.22 */
+struct gsm48_paging1 {
+ uint8_t l2_plen;
+ uint8_t proto_discr;
+ uint8_t msg_type;
+ uint8_t pag_mode:2,
+ spare:2,
+ cneed1:2,
+ cneed2:2;
+ uint8_t data[0];
+} __attribute__((packed));
+
+/* Section 9.1.23 */
+struct gsm48_paging2 {
+ uint8_t l2_plen;
+ uint8_t proto_discr;
+ uint8_t msg_type;
+ uint8_t pag_mode:2,
+ spare:2,
+ cneed1:2,
+ cneed2:2;
+ uint32_t tmsi1;
+ uint32_t tmsi2;
+ uint8_t data[0];
+} __attribute__((packed));
+
+/* Section 9.1.24 */
+struct gsm48_paging3 {
+ uint8_t l2_plen;
+ uint8_t proto_discr;
+ uint8_t msg_type;
+ uint8_t pag_mode:2,
+ spare:2,
+ cneed1:2,
+ cneed2:2;
+ uint32_t tmsi1;
+ uint32_t tmsi2;
+ uint32_t tmsi3;
+ uint32_t tmsi4;
+ uint8_t cneed3:2,
+ cneed4:2,
+ spare2:4;
+ uint8_t rest[0];
+} __attribute__((packed));
+
+/* Section 9.1.25 */
+struct gsm48_pag_rsp {
+ uint8_t key_seq:3,
+ spare:5;
+ uint8_t cm2_len;
+ struct gsm48_classmark2 cm2;
+ uint8_t data[0];
+} __attribute__((packed));
+
+/* Section 9.1.29 */
+struct gsm48_rr_status {
+ uint8_t rr_cause;
+} __attribute__((packed));
+
+/* Section 10.2 + GSM 04.07 12.2.3.1.1 + 3GPP TS 24.007 11.2.3.1.1 */
+#define GSM48_PDISC_GROUP_CC 0x00
+#define GSM48_PDISC_BCAST_CC 0x01
+#define GSM48_PDISC_PDSS1 0x02 /* 04.07 only */
+#define GSM48_PDISC_CC 0x03
+#define GSM48_PDISC_PDSS2 0x04 /* 04.07 only */
+#define GSM48_PDISC_GTTP 0x04 /* 24.007 only */
+#define GSM48_PDISC_MM 0x05
+#define GSM48_PDISC_RR 0x06
+#define GSM48_PDISC_MM_GPRS 0x08
+#define GSM48_PDISC_SMS 0x09
+#define GSM48_PDISC_SM_GPRS 0x0a
+#define GSM48_PDISC_NC_SS 0x0b
+#define GSM48_PDISC_LOC 0x0c
+#define GSM48_PDISC_EXTEND 0x0e
+#define GSM48_PDISC_TEST 0x0f /* as per 11.10, 04.14 */
+#define GSM48_PDISC_MASK 0x0f
+#define GSM48_PDISC_USSD 0x11
+
+extern const struct value_string gsm48_pdisc_names[];
+/*static inline const char *gsm48_pdisc_name(uint8_t val)
+{ return get_value_string(gsm48_pdisc_names, val); }*/
+
+bool gsm48_hdr_gmm_cipherable(const struct gsm48_hdr *hdr);
+
+static inline uint8_t gsm48_hdr_pdisc(const struct gsm48_hdr *hdr)
+{
+ /*
+ * 3GPP TS 24.007 version 12.0.0 Release 12,
+ * 11.2.3.1.1 Protocol discriminator
+ */
+ uint8_t pdisc = hdr->proto_discr & GSM48_PDISC_MASK;
+ if (pdisc == GSM48_PDISC_EXTEND)
+ return hdr->proto_discr;
+ return pdisc;
+}
+
+static inline uint8_t gsm48_hdr_trans_id(const struct gsm48_hdr *hdr)
+{
+ /*
+ * 3GPP TS 24.007 version 12.0.0 Release 12,
+ * 11.2.3.1.3 Transaction identifier
+ */
+ return (hdr->proto_discr & 0xf0) >> 4;
+}
+
+#define GSM48_TA_INVALID 220
+
+/*! Check if TA is valid according to 3GPP TS 44.018 § 10.5.2.40
+ * \param[in] ta Timing Advance value
+ * \returns true if ta is valid, false otherwise
+ * Note: Rules for GSM400 band are ignored as it's not implemented in practice.
+ */
+static inline bool gsm48_ta_is_valid(uint8_t ta)
+{
+ return (ta < 64);
+}
+
+static inline uint8_t gsm48_hdr_trans_id_flip_ti(const struct gsm48_hdr *hdr)
+{
+ return gsm48_hdr_trans_id(hdr) ^ 0x08;
+}
+
+static inline uint8_t gsm48_hdr_trans_id_no_ti(const struct gsm48_hdr *hdr)
+{
+ return gsm48_hdr_trans_id(hdr) & 0x07;
+}
+
+static inline uint8_t gsm48_hdr_msg_type_r98(const struct gsm48_hdr *hdr)
+{
+ /*
+ * 3GPP TS 24.007 version 12.0.0 Release 12,
+ * 11.2.3.2.1 Message type octet (when accessing Release 98 and older
+ * networks only)
+ */
+ switch (gsm48_hdr_pdisc(hdr)) {
+ case GSM48_PDISC_MM:
+ case GSM48_PDISC_CC:
+ case GSM48_PDISC_NC_SS:
+ case GSM48_PDISC_GROUP_CC:
+ case GSM48_PDISC_BCAST_CC:
+ case GSM48_PDISC_LOC:
+ return hdr->msg_type & 0x3f;
+ default:
+ return hdr->msg_type;
+ }
+}
+
+static inline uint8_t gsm48_hdr_msg_type_r99(const struct gsm48_hdr *hdr)
+{
+ /*
+ * 3GPP TS 24.007 version 12.0.0 Release 12,
+ * 11.2.3.2.2 Message type octet (when accessing Release 99 and newer
+ * networks)
+ */
+ switch (gsm48_hdr_pdisc(hdr)) {
+ case GSM48_PDISC_MM:
+ case GSM48_PDISC_CC:
+ case GSM48_PDISC_NC_SS:
+ return hdr->msg_type & 0x3f;
+ case GSM48_PDISC_GROUP_CC:
+ case GSM48_PDISC_BCAST_CC:
+ case GSM48_PDISC_LOC:
+ return hdr->msg_type & 0x3f;
+ default:
+ return hdr->msg_type;
+ }
+}
+
+void gsm48_set_dtx(struct gsm48_cell_options *op, enum gsm48_dtx_mode full,
+ enum gsm48_dtx_mode half, bool is_bcch);
+
+#define gsm48_hdr_msg_type gsm48_hdr_msg_type_r99
+
+/* Section 10.4 */
+#define GSM48_MT_RR_INIT_REQ 0x3c
+#define GSM48_MT_RR_ADD_ASS 0x3b
+#define GSM48_MT_RR_IMM_ASS 0x3f
+#define GSM48_MT_RR_IMM_ASS_EXT 0x39
+#define GSM48_MT_RR_IMM_ASS_REJ 0x3a
+#define GSM48_MT_RR_DTM_ASS_FAIL 0x48
+#define GSM48_MT_RR_DTM_REJECT 0x49
+#define GSM48_MT_RR_DTM_REQUEST 0x4A
+#define GSM48_MT_RR_PACKET_ASS 0x4B
+
+#define GSM48_MT_RR_CIPH_M_CMD 0x35
+#define GSM48_MT_RR_CIPH_M_COMPL 0x32
+
+#define GSM48_MT_RR_CFG_CHG_CMD 0x30
+#define GSM48_MT_RR_CFG_CHG_ACK 0x31
+#define GSM48_MT_RR_CFG_CHG_REJ 0x33
+
+#define GSM48_MT_RR_ASS_CMD 0x2e
+#define GSM48_MT_RR_ASS_COMPL 0x29
+#define GSM48_MT_RR_ASS_FAIL 0x2f
+#define GSM48_MT_RR_HANDO_CMD 0x2b
+#define GSM48_MT_RR_HANDO_COMPL 0x2c
+#define GSM48_MT_RR_HANDO_FAIL 0x28
+#define GSM48_MT_RR_HANDO_INFO 0x2d
+#define GSM48_MT_RR_HANDO_INFO 0x2d
+#define GSM48_MT_RR_DTM_ASS_CMD 0x4c
+
+#define GSM48_MT_RR_CELL_CHG_ORDER 0x08
+#define GSM48_MT_RR_PDCH_ASS_CMD 0x23
+
+#define GSM48_MT_RR_CHAN_REL 0x0d
+#define GSM48_MT_RR_PART_REL 0x0a
+#define GSM48_MT_RR_PART_REL_COMP 0x0f
+
+#define GSM48_MT_RR_PAG_REQ_1 0x21
+#define GSM48_MT_RR_PAG_REQ_2 0x22
+#define GSM48_MT_RR_PAG_REQ_3 0x24
+#define GSM48_MT_RR_PAG_RESP 0x27
+#define GSM48_MT_RR_NOTIF_NCH 0x20
+#define GSM48_MT_RR_NOTIF_FACCH 0x25 /* (Reserved) */
+#define GSM48_MT_RR_NOTIF_RESP 0x26
+#define GSM48_MT_RR_PACKET_NOTIF 0x4e
+#define GSM48_MT_RR_UTRAN_CLSM_CHG 0x60
+#define GSM48_MT_RR_CDMA2K_CLSM_CHG 0x62
+#define GSM48_MT_RR_IS_TO_UTRAN_HANDO 0x63
+#define GSM48_MT_RR_IS_TO_CDMA2K_HANDO 0x64
+
+#define GSM48_MT_RR_SYSINFO_8 0x18
+#define GSM48_MT_RR_SYSINFO_1 0x19
+#define GSM48_MT_RR_SYSINFO_2 0x1a
+#define GSM48_MT_RR_SYSINFO_3 0x1b
+#define GSM48_MT_RR_SYSINFO_4 0x1c
+#define GSM48_MT_RR_SYSINFO_5 0x1d
+#define GSM48_MT_RR_SYSINFO_6 0x1e
+#define GSM48_MT_RR_SYSINFO_7 0x1f
+
+#define GSM48_MT_RR_SYSINFO_2bis 0x02
+#define GSM48_MT_RR_SYSINFO_2ter 0x03
+#define GSM48_MT_RR_SYSINFO_2quater 0x07
+#define GSM48_MT_RR_SYSINFO_5bis 0x05
+#define GSM48_MT_RR_SYSINFO_5ter 0x06
+#define GSM48_MT_RR_SYSINFO_9 0x04
+#define GSM48_MT_RR_SYSINFO_13 0x00
+
+#define GSM48_MT_RR_SYSINFO_16 0x3d
+#define GSM48_MT_RR_SYSINFO_17 0x3e
+
+#define GSM48_MT_RR_SYSINFO_18 0x40
+#define GSM48_MT_RR_SYSINFO_19 0x41
+#define GSM48_MT_RR_SYSINFO_20 0x42
+
+#define GSM48_MT_RR_CHAN_MODE_MODIF 0x10
+#define GSM48_MT_RR_STATUS 0x12
+#define GSM48_MT_RR_CHAN_MODE_MODIF_ACK 0x17
+#define GSM48_MT_RR_FREQ_REDEF 0x14
+#define GSM48_MT_RR_MEAS_REP 0x15
+#define GSM48_MT_RR_CLSM_CHG 0x16
+#define GSM48_MT_RR_CLSM_ENQ 0x13
+#define GSM48_MT_RR_EXT_MEAS_REP 0x36
+#define GSM48_MT_RR_EXT_MEAS_REP_ORD 0x37
+#define GSM48_MT_RR_GPRS_SUSP_REQ 0x34
+#define GSM48_MT_RR_DTM_INFO 0x4d
+
+#define GSM48_MT_RR_VGCS_UPL_GRANT 0x09
+#define GSM48_MT_RR_UPLINK_RELEASE 0x0e
+#define GSM48_MT_RR_UPLINK_FREE 0x0c
+#define GSM48_MT_RR_UPLINK_BUSY 0x2a
+#define GSM48_MT_RR_TALKER_IND 0x11
+
+#define GSM48_MT_RR_APP_INFO 0x38
+
+/* Table 10.2/3GPP TS 04.08 */
+#define GSM48_MT_MM_IMSI_DETACH_IND 0x01
+#define GSM48_MT_MM_LOC_UPD_ACCEPT 0x02
+#define GSM48_MT_MM_LOC_UPD_REJECT 0x04
+#define GSM48_MT_MM_LOC_UPD_REQUEST 0x08
+
+#define GSM48_MT_MM_AUTH_REJ 0x11
+#define GSM48_MT_MM_AUTH_REQ 0x12
+#define GSM48_MT_MM_AUTH_RESP 0x14
+#define GSM48_MT_MM_AUTH_FAIL 0x1c
+#define GSM48_MT_MM_ID_REQ 0x18
+#define GSM48_MT_MM_ID_RESP 0x19
+#define GSM48_MT_MM_TMSI_REALL_CMD 0x1a
+#define GSM48_MT_MM_TMSI_REALL_COMPL 0x1b
+
+#define GSM48_MT_MM_CM_SERV_ACC 0x21
+#define GSM48_MT_MM_CM_SERV_REJ 0x22
+#define GSM48_MT_MM_CM_SERV_ABORT 0x23
+#define GSM48_MT_MM_CM_SERV_REQ 0x24
+#define GSM48_MT_MM_CM_SERV_PROMPT 0x25
+#define GSM48_MT_MM_CM_REEST_REQ 0x28
+#define GSM48_MT_MM_ABORT 0x29
+
+#define GSM48_MT_MM_NULL 0x30
+#define GSM48_MT_MM_STATUS 0x31
+#define GSM48_MT_MM_INFO 0x32
+
+/* Table 10.3/3GPP TS 04.08 */
+#define GSM48_MT_CC_ALERTING 0x01
+#define GSM48_MT_CC_CALL_CONF 0x08
+#define GSM48_MT_CC_CALL_PROC 0x02
+#define GSM48_MT_CC_CONNECT 0x07
+#define GSM48_MT_CC_CONNECT_ACK 0x0f
+#define GSM48_MT_CC_EMERG_SETUP 0x0e
+#define GSM48_MT_CC_PROGRESS 0x03
+#define GSM48_MT_CC_ESTAB 0x04
+#define GSM48_MT_CC_ESTAB_CONF 0x06
+#define GSM48_MT_CC_RECALL 0x0b
+#define GSM48_MT_CC_START_CC 0x09
+#define GSM48_MT_CC_SETUP 0x05
+
+#define GSM48_MT_CC_MODIFY 0x17
+#define GSM48_MT_CC_MODIFY_COMPL 0x1f
+#define GSM48_MT_CC_MODIFY_REJECT 0x13
+#define GSM48_MT_CC_USER_INFO 0x10
+#define GSM48_MT_CC_HOLD 0x18
+#define GSM48_MT_CC_HOLD_ACK 0x19
+#define GSM48_MT_CC_HOLD_REJ 0x1a
+#define GSM48_MT_CC_RETR 0x1c
+#define GSM48_MT_CC_RETR_ACK 0x1d
+#define GSM48_MT_CC_RETR_REJ 0x1e
+
+#define GSM48_MT_CC_DISCONNECT 0x25
+#define GSM48_MT_CC_RELEASE 0x2d
+#define GSM48_MT_CC_RELEASE_COMPL 0x2a
+
+#define GSM48_MT_CC_CONG_CTRL 0x39
+#define GSM48_MT_CC_NOTIFY 0x3e
+#define GSM48_MT_CC_STATUS 0x3d
+#define GSM48_MT_CC_STATUS_ENQ 0x34
+#define GSM48_MT_CC_START_DTMF 0x35
+#define GSM48_MT_CC_STOP_DTMF 0x31
+#define GSM48_MT_CC_STOP_DTMF_ACK 0x32
+#define GSM48_MT_CC_START_DTMF_ACK 0x36
+#define GSM48_MT_CC_START_DTMF_REJ 0x37
+#define GSM48_MT_CC_FACILITY 0x3a
+
+extern const struct value_string gsm48_rr_msgtype_names[];
+extern const struct value_string gsm48_mm_msgtype_names[];
+extern const struct value_string gsm48_cc_msgtype_names[];
+const char *gsm48_pdisc_msgtype_name(uint8_t pdisc, uint8_t msg_type);
+
+/* FIXME: Table 10.4 / 10.4a (GPRS) */
+
+/* Section 10.5.3.3 CM service type */
+#define GSM48_CMSERV_MO_CALL_PACKET 1
+#define GSM48_CMSERV_EMERGENCY 2
+#define GSM48_CMSERV_SMS 4
+#define GSM48_CMSERV_SUP_SERV 8
+#define GSM48_CMSERV_VGCS 9
+#define GSM48_CMSERV_VBS 10
+#define GSM48_CMSERV_LOC_SERV 11
+
+/* Section 10.5.2.26, Table 10.5.64 */
+#define GSM48_PM_MASK 0x03
+#define GSM48_PM_NORMAL 0x00
+#define GSM48_PM_EXTENDED 0x01
+#define GSM48_PM_REORG 0x02
+#define GSM48_PM_SAME 0x03
+
+/* Chapter 10.5.3.5 / Table 10.5.93 */
+#define GSM48_LUPD_NORMAL 0x0
+#define GSM48_LUPD_PERIODIC 0x1
+#define GSM48_LUPD_IMSI_ATT 0x2
+#define GSM48_LUPD_RESERVED 0x3
+
+/* Table 10.5.4 */
+#define GSM_MI_TYPE_MASK 0x07
+#define GSM_MI_TYPE_NONE 0x00
+#define GSM_MI_TYPE_IMSI 0x01
+#define GSM_MI_TYPE_IMEI 0x02
+#define GSM_MI_TYPE_IMEISV 0x03
+#define GSM_MI_TYPE_TMSI 0x04
+#define GSM_MI_ODD 0x08
+
+#define GSM48_IE_MOBILE_ID 0x17 /* 10.5.1.4 */
+#define GSM48_IE_NAME_LONG 0x43 /* 10.5.3.5a */
+#define GSM48_IE_NAME_SHORT 0x45 /* 10.5.3.5a */
+#define GSM48_IE_UTC 0x46 /* 10.5.3.8 */
+#define GSM48_IE_NET_TIME_TZ 0x47 /* 10.5.3.9 */
+#define GSM48_IE_LSA_IDENT 0x48 /* 10.5.3.11 */
+#define GSM48_IE_NET_DST 0x49 /* 10.5.3.12 [24.008] */
+
+#define GSM48_IE_BEARER_CAP 0x04 /* 10.5.4.5 */
+#define GSM48_IE_CAUSE 0x08 /* 10.5.4.11 */
+#define GSM48_IE_CC_CAP 0x15 /* 10.5.4.5a */
+#define GSM48_IE_ALERT 0x19 /* 10.5.4.26 */
+#define GSM48_IE_FACILITY 0x1c /* 10.5.4.15 */
+#define GSM48_IE_PROGR_IND 0x1e /* 10.5.4.21 */
+#define GSM48_IE_AUX_STATUS 0x24 /* 10.5.4.4 */
+#define GSM48_IE_NOTIFY 0x27 /* 10.5.4.20 */
+#define GSM48_IE_KPD_FACILITY 0x2c /* 10.5.4.17 */
+#define GSM48_IE_SIGNAL 0x34 /* 10.5.4.23 */
+#define GSM48_IE_CONN_BCD 0x4c /* 10.5.4.13 */
+#define GSM48_IE_CONN_SUB 0x4d /* 10.5.4.14 */
+#define GSM48_IE_CALLING_BCD 0x5c /* 10.5.4.9 */
+#define GSM48_IE_CALLING_SUB 0x5d /* 10.5.4.10 */
+#define GSM48_IE_CALLED_BCD 0x5e /* 10.5.4.7 */
+#define GSM48_IE_CALLED_SUB 0x6d /* 10.5.4.8 */
+#define GSM48_IE_REDIR_BCD 0x74 /* 10.5.4.21a */
+#define GSM48_IE_REDIR_SUB 0x75 /* 10.5.4.21b */
+#define GSM48_IE_LOWL_COMPAT 0x7c /* 10.5.4.18 */
+#define GSM48_IE_HIGHL_COMPAT 0x7d /* 10.5.4.16 */
+#define GSM48_IE_USER_USER 0x7e /* 10.5.4.25 */
+#define GSM48_IE_SS_VERS 0x7f /* 10.5.4.24 */
+#define GSM48_IE_MORE_DATA 0xa0 /* 10.5.4.19 */
+#define GSM48_IE_CLIR_SUPP 0xa1 /* 10.5.4.11a */
+#define GSM48_IE_CLIR_INVOC 0xa2 /* 10.5.4.11b */
+#define GSM48_IE_REV_C_SETUP 0xa3 /* 10.5.4.22a */
+#define GSM48_IE_REPEAT_CIR 0xd1 /* 10.5.4.22 */
+#define GSM48_IE_REPEAT_SEQ 0xd3 /* 10.5.4.22 */
+
+/* Section 10.5.4.11 / Table 10.5.122 */
+#define GSM48_CAUSE_CS_GSM 0x60
+
+/* Section 9.1.2 / Table 9.3 */
+/* RR elements */
+#define GSM48_IE_VGCS_TARGET 0x01
+//#define GSM48_IE_VGCS_T_MODE_I 0x01
+#define GSM48_IE_FRQSHORT_AFTER 0x02
+#define GSM48_IE_MUL_RATE_CFG 0x03 /* 10.5.2.21aa */
+#define GSM48_IE_FREQ_L_AFTER 0x05
+#define GSM48_IE_MSLOT_DESC 0x10
+#define GSM48_IE_CHANMODE_2 0x11
+#define GSM48_IE_FRQSHORT_BEFORE 0x12
+//#define GSM48_IE_FRQSHORT_BEFOR 0x12
+#define GSM48_IE_CHANMODE_3 0x13
+#define GSM48_IE_CHANMODE_4 0x14
+#define GSM48_IE_CHANMODE_5 0x15
+#define GSM48_IE_CHANMODE_6 0x16
+#define GSM48_IE_CHANMODE_7 0x17
+#define GSM48_IE_CHANMODE_8 0x18
+#define GSM48_IE_CHANDESC_2 0x64
+#define GSM48_IE_MA_AFTER 0x72
+#define GSM48_IE_START_TIME 0x7c
+#define GSM48_IE_FREQ_L_BEFORE 0x19
+//#define GSM48_IE_FRQLIST_BEFORE 0x19
+#define GSM48_IE_CH_DESC_1_BEFORE 0x1c
+//#define GSM48_IE_CHDES_1_BEFORE 0x1c
+#define GSM48_IE_CH_DESC_2_BEFORE 0x1d
+//#define GSM48_IE_CHDES_2_BEFORE 0x1d
+#define GSM48_IE_F_CH_SEQ_BEFORE 0x1e
+//#define GSM48_IE_FRQSEQ_BEFORE 0x1e
+#define GSM48_IE_CLASSMARK3 0x20
+#define GSM48_IE_MA_BEFORE 0x21
+#define GSM48_IE_RR_PACKET_UL 0x22
+#define GSM48_IE_RR_PACKET_DL 0x23
+#define GSM48_IE_CELL_CH_DESC 0x62
+#define GSM48_IE_CHANMODE_1 0x63
+#define GSM48_IE_CHDES_2_AFTER 0x64
+#define GSM48_IE_MODE_SEC_CH 0x66
+#define GSM48_IE_F_CH_SEQ_AFTER 0x69
+#define GSM48_IE_MA_AFTER 0x72
+#define GSM48_IE_BA_RANGE 0x73
+#define GSM48_IE_GROUP_CHDES 0x74
+#define GSM48_IE_BA_LIST_PREF 0x75
+#define GSM48_IE_MOB_OVSERV_DIF 0x77
+#define GSM48_IE_REALTIME_DIFF 0x7b
+#define GSM48_IE_START_TIME 0x7c
+#define GSM48_IE_TIMING_ADVANCE 0x7d
+#define GSM48_IE_GROUP_CIP_SEQ 0x80
+#define GSM48_IE_CIP_MODE_SET 0x90
+#define GSM48_IE_GPRS_RESUMPT 0xc0
+#define GSM48_IE_SYNC_IND 0xd0
+/* System Information 4 (types are equal IEs above) */
+#define GSM48_IE_CBCH_CHAN_DESC 0x64
+#define GSM48_IE_CBCH_MOB_AL 0x72
+
+/* Additional MM elements */
+#define GSM48_IE_LOCATION_AREA 0x13
+#define GSM48_IE_AUTN 0x20
+#define GSM48_IE_AUTH_RES_EXT 0x21
+#define GSM48_IE_AUTS 0x22
+#define GSM48_IE_PRIORITY_LEV 0x80
+#define GSM48_IE_FOLLOW_ON_PROC 0xa1
+#define GSM48_IE_CTS_PERMISSION 0xa2
+
+/* Section 10.5.4.23 / Table 10.5.130 */
+enum gsm48_signal_val {
+ GSM48_SIGNAL_DIALTONE = 0x00,
+ GSM48_SIGNAL_RINGBACK = 0x01,
+ GSM48_SIGNAL_INTERCEPT = 0x02,
+ GSM48_SIGNAL_NET_CONG = 0x03,
+ GSM48_SIGNAL_BUSY = 0x04,
+ GSM48_SIGNAL_CONFIRM = 0x05,
+ GSM48_SIGNAL_ANSWER = 0x06,
+ GSM48_SIGNAL_CALL_WAIT = 0x07,
+ GSM48_SIGNAL_OFF_HOOK = 0x08,
+ GSM48_SIGNAL_OFF = 0x3f,
+ GSM48_SIGNAL_ALERT_OFF = 0x4f,
+};
+
+enum gsm48_cause_loc {
+ GSM48_CAUSE_LOC_USER = 0x00,
+ GSM48_CAUSE_LOC_PRN_S_LU = 0x01,
+ GSM48_CAUSE_LOC_PUN_S_LU = 0x02,
+ GSM48_CAUSE_LOC_TRANS_NET = 0x03,
+ GSM48_CAUSE_LOC_PUN_S_RU = 0x04,
+ GSM48_CAUSE_LOC_PRN_S_RU = 0x05,
+ /* not defined */
+ GSM48_CAUSE_LOC_INN_NET = 0x07,
+ GSM48_CAUSE_LOC_NET_BEYOND = 0x0a,
+};
+
+/* Section 10.5.2.31 RR Cause / Table 10.5.70 */
+enum gsm48_rr_cause {
+ GSM48_RR_CAUSE_NORMAL = 0x00,
+ GSM48_RR_CAUSE_ABNORMAL_UNSPEC = 0x01,
+ GSM48_RR_CAUSE_ABNORMAL_UNACCT = 0x02,
+ GSM48_RR_CAUSE_ABNORMAL_TIMER = 0x03,
+ GSM48_RR_CAUSE_ABNORMAL_NOACT = 0x04,
+ GSM48_RR_CAUSE_PREMPTIVE_REL = 0x05,
+ GSM48_RR_CAUSE_HNDOVER_IMP = 0x08,
+ GSM48_RR_CAUSE_CHAN_MODE_UNACCT = 0x09,
+ GSM48_RR_CAUSE_FREQ_NOT_IMPL = 0x0a,
+ GSM48_RR_CAUSE_CALL_CLEARED = 0x41,
+ GSM48_RR_CAUSE_SEMANT_INCORR = 0x5f,
+ GSM48_RR_CAUSE_INVALID_MAND_INF = 0x60,
+ GSM48_RR_CAUSE_MSG_TYPE_N = 0x61,
+ GSM48_RR_CAUSE_MSG_TYPE_N_COMPAT= 0x62,
+ GSM48_RR_CAUSE_COND_IE_ERROR = 0x64,
+ GSM48_RR_CAUSE_NO_CELL_ALLOC_A = 0x65,
+ GSM48_RR_CAUSE_PROT_ERROR_UNSPC = 0x6f,
+};
+
+/* Section 10.5.4.11 CC Cause / Table 10.5.123 */
+enum gsm48_cc_cause {
+ GSM48_CC_CAUSE_UNASSIGNED_NR = 1,
+ GSM48_CC_CAUSE_NO_ROUTE = 3,
+ GSM48_CC_CAUSE_CHAN_UNACCEPT = 6,
+ GSM48_CC_CAUSE_OP_DET_BARRING = 8,
+ GSM48_CC_CAUSE_NORM_CALL_CLEAR = 16,
+ GSM48_CC_CAUSE_USER_BUSY = 17,
+ GSM48_CC_CAUSE_USER_NOTRESPOND = 18,
+ GSM48_CC_CAUSE_USER_ALERTING_NA = 19,
+ GSM48_CC_CAUSE_CALL_REJECTED = 21,
+ GSM48_CC_CAUSE_NUMBER_CHANGED = 22,
+ GSM48_CC_CAUSE_PRE_EMPTION = 25,
+ GSM48_CC_CAUSE_NONSE_USER_CLR = 26,
+ GSM48_CC_CAUSE_DEST_OOO = 27,
+ GSM48_CC_CAUSE_INV_NR_FORMAT = 28,
+ GSM48_CC_CAUSE_FACILITY_REJ = 29,
+ GSM48_CC_CAUSE_RESP_STATUS_INQ = 30,
+ GSM48_CC_CAUSE_NORMAL_UNSPEC = 31,
+ GSM48_CC_CAUSE_NO_CIRCUIT_CHAN = 34,
+ GSM48_CC_CAUSE_NETWORK_OOO = 38,
+ GSM48_CC_CAUSE_TEMP_FAILURE = 41,
+ GSM48_CC_CAUSE_SWITCH_CONG = 42,
+ GSM48_CC_CAUSE_ACC_INF_DISCARD = 43,
+ GSM48_CC_CAUSE_REQ_CHAN_UNAVAIL = 44,
+ GSM48_CC_CAUSE_RESOURCE_UNAVAIL = 47,
+ GSM48_CC_CAUSE_QOS_UNAVAIL = 49,
+ GSM48_CC_CAUSE_REQ_FAC_NOT_SUBSC= 50,
+ GSM48_CC_CAUSE_INC_BARRED_CUG = 55,
+ GSM48_CC_CAUSE_BEARER_CAP_UNAUTH= 57,
+ GSM48_CC_CAUSE_BEARER_CA_UNAVAIL= 58,
+ GSM48_CC_CAUSE_SERV_OPT_UNAVAIL = 63,
+ GSM48_CC_CAUSE_BEARERSERV_UNIMPL= 65,
+ GSM48_CC_CAUSE_ACM_GE_ACM_MAX = 68,
+ GSM48_CC_CAUSE_REQ_FAC_NOTIMPL = 69,
+ GSM48_CC_CAUSE_RESTR_BCAP_AVAIL = 70,
+ GSM48_CC_CAUSE_SERV_OPT_UNIMPL = 79,
+ GSM48_CC_CAUSE_INVAL_TRANS_ID = 81,
+ GSM48_CC_CAUSE_USER_NOT_IN_CUG = 87,
+ GSM48_CC_CAUSE_INCOMPAT_DEST = 88,
+ GSM48_CC_CAUSE_INVAL_TRANS_NET = 91,
+ GSM48_CC_CAUSE_SEMANTIC_INCORR = 95,
+ GSM48_CC_CAUSE_INVAL_MAND_INF = 96,
+ GSM48_CC_CAUSE_MSGTYPE_NOTEXIST = 97,
+ GSM48_CC_CAUSE_MSGTYPE_INCOMPAT = 98,
+ GSM48_CC_CAUSE_IE_NOTEXIST = 99,
+ GSM48_CC_CAUSE_COND_IE_ERR = 100,
+ GSM48_CC_CAUSE_MSG_INCOMP_STATE = 101,
+ GSM48_CC_CAUSE_RECOVERY_TIMER = 102,
+ GSM48_CC_CAUSE_PROTO_ERR = 111,
+ GSM48_CC_CAUSE_INTERWORKING = 127,
+};
+
+/* Annex G, GSM specific cause values for mobility management */
+enum gsm48_reject_value {
+ GSM48_REJECT_IMSI_UNKNOWN_IN_HLR = 2,
+ GSM48_REJECT_ILLEGAL_MS = 3,
+ GSM48_REJECT_IMSI_UNKNOWN_IN_VLR = 4,
+ GSM48_REJECT_IMEI_NOT_ACCEPTED = 5,
+ GSM48_REJECT_ILLEGAL_ME = 6,
+ GSM48_REJECT_PLMN_NOT_ALLOWED = 11,
+ GSM48_REJECT_LOC_NOT_ALLOWED = 12,
+ GSM48_REJECT_ROAMING_NOT_ALLOWED = 13,
+ GSM48_REJECT_NETWORK_FAILURE = 17,
+ GSM48_REJECT_SYNCH_FAILURE = 21,
+ GSM48_REJECT_CONGESTION = 22,
+ GSM48_REJECT_SRV_OPT_NOT_SUPPORTED = 32,
+ GSM48_REJECT_RQD_SRV_OPT_NOT_SUPPORTED = 33,
+ GSM48_REJECT_SRV_OPT_TMP_OUT_OF_ORDER = 34,
+ GSM48_REJECT_CALL_CAN_NOT_BE_IDENTIFIED = 38,
+ GSM48_REJECT_INCORRECT_MESSAGE = 95,
+ GSM48_REJECT_INVALID_MANDANTORY_INF = 96,
+ GSM48_REJECT_MSG_TYPE_NOT_IMPLEMENTED = 97,
+ GSM48_REJECT_MSG_TYPE_NOT_COMPATIBLE = 98,
+ GSM48_REJECT_INF_ELEME_NOT_IMPLEMENTED = 99,
+ GSM48_REJECT_CONDTIONAL_IE_ERROR = 100,
+ GSM48_REJECT_MSG_NOT_COMPATIBLE = 101,
+ GSM48_REJECT_PROTOCOL_ERROR = 111,
+
+ /* according to G.6 Additional cause codes for GMM */
+ GSM48_REJECT_GPRS_NOT_ALLOWED = 7,
+ GSM48_REJECT_SERVICES_NOT_ALLOWED = 8,
+ GSM48_REJECT_MS_IDENTITY_NOT_DERVIVABLE = 9,
+ GSM48_REJECT_IMPLICITLY_DETACHED = 10,
+ GSM48_REJECT_GPRS_NOT_ALLOWED_IN_PLMN = 14,
+ GSM48_REJECT_MSC_TMP_NOT_REACHABLE = 16,
+};
+
+enum chreq_type {
+ CHREQ_T_EMERG_CALL,
+ CHREQ_T_CALL_REEST_TCH_F,
+ CHREQ_T_CALL_REEST_TCH_H,
+ CHREQ_T_CALL_REEST_TCH_H_DBL,
+ CHREQ_T_SDCCH,
+ CHREQ_T_TCH_F,
+ CHREQ_T_VOICE_CALL_TCH_H,
+ CHREQ_T_DATA_CALL_TCH_H,
+ CHREQ_T_LOCATION_UPD,
+ CHREQ_T_PAG_R_ANY_NECI0,
+ CHREQ_T_PAG_R_ANY_NECI1,
+ CHREQ_T_PAG_R_TCH_F,
+ CHREQ_T_PAG_R_TCH_FH,
+ CHREQ_T_LMU,
+ CHREQ_T_RESERVED_SDCCH,
+ CHREQ_T_RESERVED_IGNORE,
+ CHREQ_T_PDCH_ONE_PHASE,
+ CHREQ_T_PDCH_TWO_PHASE,
+ _NUM_CHREQ_T,
+};
+
+/* Chapter 11.3 */
+#define GSM48_T301 180, 0
+#define GSM48_T303 30, 0
+#define GSM48_T305 30, 0
+#define GSM48_T306 30, 0
+#define GSM48_T308 10, 0 /* no spec default */
+#define GSM48_T310 30, 0 /* no spec default */
+#define GSM48_T313 30, 0 /* no spec default */
+#define GSM48_T323 30, 0
+#define GSM48_T331 30, 0 /* no spec default */
+#define GSM48_T333 30, 0 /* no spec default */
+#define GSM48_T334 25, 0 /* min 15s */
+#define GSM48_T338 30, 0 /* no spec default */
+#define GSM48_T303_MS 30, 0
+#define GSM48_T305_MS 30, 0
+#define GSM48_T308_MS 30, 0
+#define GSM48_T310_MS 30, 0
+#define GSM48_T313_MS 30, 0
+#define GSM48_T323_MS 30, 0
+#define GSM48_T332_MS 30, 0
+#define GSM48_T335_MS 30, 0
+
+/* Chapter 5.1.2.2 */
+#define GSM_CSTATE_NULL 0
+#define GSM_CSTATE_INITIATED 1
+#define GSM_CSTATE_MM_CONNECTION_PEND 2 /* see 10.5.4.6 */
+#define GSM_CSTATE_MO_CALL_PROC 3
+#define GSM_CSTATE_CALL_DELIVERED 4
+#define GSM_CSTATE_CALL_PRESENT 6
+#define GSM_CSTATE_CALL_RECEIVED 7
+#define GSM_CSTATE_CONNECT_REQUEST 8
+#define GSM_CSTATE_MO_TERM_CALL_CONF 9
+#define GSM_CSTATE_ACTIVE 10
+#define GSM_CSTATE_DISCONNECT_REQ 12
+#define GSM_CSTATE_DISCONNECT_IND 12
+#define GSM_CSTATE_RELEASE_REQ 19
+#define GSM_CSTATE_MO_ORIG_MODIFY 26
+#define GSM_CSTATE_MO_TERM_MODIFY 27
+#define GSM_CSTATE_CONNECT_IND 28
+
+#define SBIT(a) (1 << a)
+#define ALL_STATES 0xffffffff
+
+/* Table 10.5.3/3GPP TS 04.08: Location Area Identification information element */
+#define GSM_LAC_RESERVED_DETACHED 0x0
+#define GSM_LAC_RESERVED_ALL_BTS 0xfffe
+
+/* GSM 04.08 Bearer Capability: Information Transfer Capability */
+enum gsm48_bcap_itcap {
+ GSM48_BCAP_ITCAP_SPEECH = 0,
+ GSM48_BCAP_ITCAP_UNR_DIG_INF = 1,
+ GSM48_BCAP_ITCAP_3k1_AUDIO = 2,
+ GSM48_BCAP_ITCAP_FAX_G3 = 3,
+ GSM48_BCAP_ITCAP_OTHER = 5,
+ GSM48_BCAP_ITCAP_RESERVED = 7,
+};
+
+/* GSM 04.08 Bearer Capability: Transfer Mode */
+enum gsm48_bcap_tmod {
+ GSM48_BCAP_TMOD_CIRCUIT = 0,
+ GSM48_BCAP_TMOD_PACKET = 1,
+};
+
+/* GSM 04.08 Bearer Capability: Coding Standard */
+enum gsm48_bcap_coding {
+ GSM48_BCAP_CODING_GSM_STD = 0,
+};
+
+/* GSM 04.08 Bearer Capability: Radio Channel Requirements */
+enum gsm48_bcap_rrq {
+ GSM48_BCAP_RRQ_FR_ONLY = 1,
+ GSM48_BCAP_RRQ_DUAL_HR = 2,
+ GSM48_BCAP_RRQ_DUAL_FR = 3,
+};
+
+/* GSM 04.08 Bearer Capability: Rate Adaption */
+enum gsm48_bcap_ra {
+ GSM48_BCAP_RA_NONE = 0,
+ GSM48_BCAP_RA_V110_X30 = 1,
+ GSM48_BCAP_RA_X31 = 2,
+ GSM48_BCAP_RA_OTHER = 3,
+};
+
+/* GSM 04.08 Bearer Capability: Signalling access protocol */
+enum gsm48_bcap_sig_access {
+ GSM48_BCAP_SA_I440_I450 = 1,
+ GSM48_BCAP_SA_X21 = 2,
+ GSM48_BCAP_SA_X28_DP_IN = 3,
+ GSM48_BCAP_SA_X28_DP_UN = 4,
+ GSM48_BCAP_SA_X28_NDP = 5,
+ GSM48_BCAP_SA_X32 = 6,
+};
+
+/* GSM 04.08 Bearer Capability: User Rate */
+enum gsm48_bcap_user_rate {
+ GSM48_BCAP_UR_300 = 1,
+ GSM48_BCAP_UR_1200 = 2,
+ GSM48_BCAP_UR_2400 = 3,
+ GSM48_BCAP_UR_4800 = 4,
+ GSM48_BCAP_UR_9600 = 5,
+ GSM48_BCAP_UR_12000 = 6,
+ GSM48_BCAP_UR_1200_75 = 7,
+};
+
+/* GSM 04.08 Bearer Capability: Parity */
+enum gsm48_bcap_parity {
+ GSM48_BCAP_PAR_ODD = 0,
+ GSM48_BCAP_PAR_EVEN = 2,
+ GSM48_BCAP_PAR_NONE = 3,
+ GSM48_BCAP_PAR_ZERO = 4,
+ GSM48_BCAP_PAR_ONE = 5,
+};
+
+/* GSM 04.08 Bearer Capability: Intermediate Rate */
+enum gsm48_bcap_interm_rate {
+ GSM48_BCAP_IR_8k = 2,
+ GSM48_BCAP_IR_16k = 3,
+};
+
+/* GSM 04.08 Bearer Capability: Transparency */
+enum gsm48_bcap_transp {
+ GSM48_BCAP_TR_TRANSP = 0,
+ GSM48_BCAP_TR_RLP = 1,
+ GSM48_BCAP_TR_TR_PREF = 2,
+ GSM48_BCAP_TR_RLP_PREF = 3,
+};
+
+/* GSM 04.08 Bearer Capability: Modem Type */
+enum gsm48_bcap_modem_type {
+ GSM48_BCAP_MT_NONE = 0,
+ GSM48_BCAP_MT_V21 = 1,
+ GSM48_BCAP_MT_V22 = 2,
+ GSM48_BCAP_MT_V22bis = 3,
+ GSM48_BCAP_MT_V23 = 4,
+ GSM48_BCAP_MT_V26ter = 5,
+ GSM48_BCAP_MT_V32 = 6,
+ GSM48_BCAP_MT_UNDEF = 7,
+ GSM48_BCAP_MT_AUTO_1 = 8,
+};
+
+/*! GSM 04.08 Bearer Capability: Speech Version Indication
+ * (See also 3GPP TS 24.008, Table 10.5.103) */
+enum gsm48_bcap_speech_ver {
+ GSM48_BCAP_SV_FR = 0, /*!< GSM FR V1 (GSM FR) */
+ GSM48_BCAP_SV_HR = 1, /*!< GSM HR V1 (GSM HR) */
+ GSM48_BCAP_SV_EFR = 2, /*!< GSM FR V2 (GSM EFR) */
+ GSM48_BCAP_SV_AMR_F = 4, /*!< GSM FR V3 (FR AMR) */
+ GSM48_BCAP_SV_AMR_H = 5, /*!< GSM HR V3 (HR_AMR) */
+ GSM48_BCAP_SV_AMR_OFW = 6, /*!< GSM FR V4 (OFR AMR-WB) */
+ GSM48_BCAP_SV_AMR_OHW = 7, /*!< GSM HR V4 (OHR AMR-WB) */
+ GSM48_BCAP_SV_AMR_FW = 8, /*!< GSM FR V5 (FR AMR-WB) */
+ GSM48_BCAP_SV_AMR_OH = 11, /*!< GSM HR V6 (OHR AMR) */
+};
+
+#define GSM48_TMSI_LEN 5
+#define GSM48_MID_TMSI_LEN (GSM48_TMSI_LEN + 2)
+#define GSM48_MI_SIZE 32
+
+/* 3GPP TS 24.008 § 10.5.5.15 Routing area identification */
+struct gsm48_ra_id {
+ uint8_t digits[3]; /* MCC + MNC BCD digits */
+ uint16_t lac; /* Location Area Code */
+ uint8_t rac; /* Routing Area Code */
+} __attribute__ ((packed));
+
+#define GSM48_CELL_CHAN_DESC_SIZE 16
+
+#define GSM_MACBLOCK_LEN 23
+#define GSM_MACBLOCK_PADDING 0x2b
+
+/* Table 10.5.118 / 3GPP TS 24.008 Section 10.5.4.7 */
+enum gsm48_type_of_number {
+ GSM48_TON_UNKNOWN = 0,
+ GSM48_TON_INTERNATIONAL = 1,
+ GSM48_TON_NATIONAL = 2,
+ GSM48_TON_NET_SPEC = 3,
+ GSM48_TON_SHORT_CODE = 4,
+ /* reserved */
+};
+
+/* Table 10.5.118 / 3GPP TS 24.008 Section 10.5.4.7 */
+enum gsm48_numbering_plan {
+ GSM48_NPI_UNKNOWN = 0,
+ GSM48_NPI_ISDN_E164 = 1,
+ GSM48_NPI_DATA_X121 = 3,
+ GSM48_NPI_TELEX_F69 = 4,
+ GSM48_NPI_NATIONAL = 8,
+ GSM48_NPI_PRIVATE = 9,
+ GSM48_NPI_CTS = 11,
+ /* reserved */
+};
diff --git a/lib/decoding/sch.c b/lib/decoding/sch.c
new file mode 100644
index 0000000..21ded53
--- /dev/null
+++ b/lib/decoding/sch.c
@@ -0,0 +1,132 @@
+/* -*- c++ -*- */
+/*
+ * @file
+ * @author (C) 2014 by Piotr Krysik <ptrkrysik@gmail.com>
+ * @section LICENSE
+ *
+ * Gr-gsm 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.
+ *
+ * Gr-gsm 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 gr-gsm; see the file COPYING. If not, write to
+ * the Free Software Foundation, Inc., 51 Franklin Street,
+ * Boston, MA 02110-1301, USA.
+ */
+
+#include <string.h>
+#include "gsm_constants.h"
+
+#include <osmocom/coding/gsm0503_coding.h>
+#include <osmocom/core/utils.h>
+
+static int ubits2sbits(ubit_t *ubits, sbit_t *sbits, int count)
+{
+ int i;
+
+ for (i = 0; i < count; i++) {
+ if (*ubits == 0x23) {
+ ubits++;
+ sbits++;
+ continue;
+ }
+ if ((*ubits++) & 1)
+ *sbits++ = -127;
+ else
+ *sbits++ = 127;
+ }
+
+ return count;
+}
+
+int decode_sch(const unsigned char *buf, int * t1_o, int * t2_o, int * t3_o, int * ncc_o, int * bcc_o)
+{
+
+ int t1, t2, t3p, t3, ncc, bcc;
+
+ uint8_t result[4];
+ ubit_t bursts_u[SCH_DATA_LEN*2];
+ sbit_t bursts_s[SCH_DATA_LEN*2];
+
+ // extract encoded data from synchronization burst
+ /* buf, 39 bit */
+ /* buf + 39 + 64 = 103, 39 */
+ memcpy(bursts_u, buf, SCH_DATA_LEN);
+ memcpy(bursts_u + SCH_DATA_LEN, buf + SCH_DATA_LEN + N_SYNC_BITS, SCH_DATA_LEN);
+
+ ubits2sbits(bursts_u, bursts_s, SCH_DATA_LEN*2);
+ if(gsm0503_sch_decode(result, bursts_s)==-1){
+ return 1;
+ }
+
+ // Synchronization channel information, 44.018 page 171. (V7.2.0)
+ uint8_t decoded_data[25];
+ osmo_pbit2ubit_ext(decoded_data, 0, result, 0, 25, 1);
+ 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/lib/decoding/tch_f_decoder_impl.cc b/lib/decoding/tch_f_decoder_impl.cc
index 41e5f53..02dc45a 100644
--- a/lib/decoding/tch_f_decoder_impl.cc
+++ b/lib/decoding/tch_f_decoder_impl.cc
@@ -1,7 +1,8 @@
/* -*- c++ -*- */
/*
* @file
- * @author Roman Khassraf <rkhassraf@gmail.com>
+ * @author (C) 2015 by Roman Khassraf <rkhassraf@gmail.com>
+ * (C) 2017 by Piotr Krysik <ptrkrysik@gmail.com>
* @section LICENSE
*
* Gr-gsm is free software; you can redistribute it and/or modify
@@ -29,11 +30,34 @@
#include "stdio.h"
#include "tch_f_decoder_impl.h"
+extern "C" {
+ #include "osmocom/coding/gsm0503_coding.h"
+}
+
#define DATA_BYTES 23
namespace gr {
namespace gsm {
+ static int ubits2sbits(ubit_t *ubits, sbit_t *sbits, int count)
+ {
+ int i;
+
+ for (i = 0; i < count; i++) {
+ if (*ubits == 0x23) {
+ ubits++;
+ sbits++;
+ continue;
+ }
+ if ((*ubits++) & 1)
+ *sbits++ = -127;
+ else
+ *sbits++ = 127;
+ }
+
+ return count;
+ }
+
tch_f_decoder::sptr
tch_f_decoder::make(tch_mode mode, bool boundary_check)
{
@@ -107,6 +131,7 @@ namespace gr {
if (d_collected_bursts_num == 8)
{
+ ubit_t bursts_u[116 * 8];
d_collected_bursts_num = 0;
// reorganize data
@@ -115,6 +140,9 @@ namespace gr {
pmt::pmt_t header_plus_burst = pmt::cdr(d_bursts[ii]);
int8_t * burst_bits = (int8_t *)(pmt::blob_data(header_plus_burst))+sizeof(gsmtap_hdr);
+ memcpy(&bursts_u[ii*116], &burst_bits[3],58);
+ memcpy(&bursts_u[ii*116+58], &burst_bits[3+57+1+26],58);
+
for (int jj = 0; jj < 57; jj++)
{
iBLOCK[ii*114+jj] = burst_bits[jj + 3];
@@ -271,23 +299,17 @@ namespace gr {
if (good)
{
- unsigned char frameBuffer[33];
+ uint8_t frameBuffer[33];
+ sbit_t bursts_s[116 * 8];
+ int n_errors, n_bits_total;
unsigned int mTCHFrameLength;
+ ubits2sbits(bursts_u, bursts_s, 116 * 8);
if (d_tch_mode == TCH_FS) // GSM-FR
{
- unsigned char mFrameHeader = 0x0d;
mTCHFrameLength = 33;
-
- // Undo Um's importance-sorted bit ordering.
- // See GSM 05.03 3.1 and Table 2.
- BitVector frFrame(260 + 4); // FR has a frameheader of 4 bits only
- frFrame.fillField(0, mFrameHeader, 4);
- BitVector payload = frFrame.tail(4);
-
- mTCHD.unmap(GSM::g610BitOrder, 260, payload);
- frFrame.pack(frameBuffer);
-
+ gsm0503_tch_fr_decode(frameBuffer, bursts_s, 1, 0, &n_errors, &n_bits_total);
+ //std::cout << "Errors: " << n_errors << std::endl;
}
else if (d_tch_mode == TCH_EFR) // GSM-EFR
{
@@ -327,7 +349,8 @@ namespace gr {
// Put the whole frame (hdr + payload)
mTCHFrameLength = 32;
amrFrame.pack(frameBuffer);
-
+ //when itegrating with libosmocore lines above can be removed and line below uncommented, efr decoding with libosmocore need to be tested however
+ //gsm0503_tch_fr_decode(frameBuffer, bursts_s, 1, 1, &n_errors, &n_bits_total);
}
message_port_pub(pmt::mp("voice"), pmt::cons(pmt::PMT_NIL, pmt::make_blob(frameBuffer,mTCHFrameLength)));
}
@@ -365,7 +388,7 @@ namespace gr {
if (good)
{
- unsigned char frameBuffer[mAMRFrameLth];
+ unsigned char * frameBuffer = new unsigned char [mAMRFrameLth];
// AMR Frame, consisting of a 8 bit frame header, plus the payload from decoding
BitVector amrFrame(mKd + 8);
BitVector payload = amrFrame.tail(8);
@@ -379,6 +402,7 @@ namespace gr {
mTCHD.copyTo(payload);
amrFrame.pack(frameBuffer);
message_port_pub(pmt::mp("voice"), pmt::cons(pmt::PMT_NIL, pmt::make_blob(frameBuffer,mAMRFrameLth)));
+ delete[] frameBuffer;
}
}
}
diff --git a/lib/decoding/tch_f_decoder_impl.h b/lib/decoding/tch_f_decoder_impl.h
index 0119b5a..cac245a 100644
--- a/lib/decoding/tch_f_decoder_impl.h
+++ b/lib/decoding/tch_f_decoder_impl.h
@@ -1,7 +1,7 @@
/* -*- c++ -*- */
/*
* @file
- * @author Roman Khassraf <rkhassraf@gmail.com>
+ * @author (C) 2015 by Roman Khassraf <rkhassraf@gmail.com>
* @section LICENSE
*
* Gr-gsm is free software; you can redistribute it and/or modify
@@ -23,12 +23,12 @@
#ifndef INCLUDED_GSM_TCH_F_DECODER_IMPL_H
#define INCLUDED_GSM_TCH_F_DECODER_IMPL_H
-#include "AmrCoder.h"
-#include "BitVector.h"
-#include "GSM503Tables.h"
-#include "GSM610Tables.h"
-#include "GSM660Tables.h"
-#include "ViterbiR204.h"
+#include "openbts/AmrCoder.h"
+#include "openbts/BitVector.h"
+#include "openbts/GSM503Tables.h"
+#include "openbts/GSM610Tables.h"
+#include "openbts/GSM660Tables.h"
+#include "openbts/ViterbiR204.h"
#include <grgsm/decoding/tch_f_decoder.h>
diff --git a/lib/decryption/CMakeLists.txt b/lib/decryption/CMakeLists.txt
new file mode 100644
index 0000000..7ccd99a
--- /dev/null
+++ b/lib/decryption/CMakeLists.txt
@@ -0,0 +1,22 @@
+# Copyright 2011,2012 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.
+
+add_sources(
+ decryption_impl.cc
+)
diff --git a/lib/decryption/decryption_impl.cc b/lib/decryption/decryption_impl.cc
index 9e6112b..551afa4 100644
--- a/lib/decryption/decryption_impl.cc
+++ b/lib/decryption/decryption_impl.cc
@@ -1,19 +1,22 @@
/* -*- c++ -*- */
/*
- * Copyright 2014 <+YOU OR YOUR COMPANY+>.
+ * @file
+ * @author (C) 2014 by Piotr Krysik <ptrkrysik@gmail.com>
+ * @author (C) 2015 by Roman Khassraf <rkhassraf@gmail.com>
+ * @section LICENSE
*
- * This is free software; you can redistribute it and/or modify
+ * Gr-gsm 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,
+ * Gr-gsm 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
+ * along with gr-gsm; see the file COPYING. If not, write to
* the Free Software Foundation, Inc., 51 Franklin Street,
* Boston, MA 02110-1301, USA.
*/
@@ -57,7 +60,6 @@ namespace gr {
set_a5_version(a5_version);
validate_k_c();
-// std::cout << "Be careful with decryption block - it wasn't tested yet!" << std::endl;
message_port_register_in(pmt::mp("bursts"));
set_msg_handler(pmt::mp("bursts"), boost::bind(&decryption_impl::decrypt, this, _1));
message_port_register_out(pmt::mp("bursts"));
@@ -138,7 +140,7 @@ namespace gr {
for (int i = 0; i < 3; i++) {
decrypted_data[i] = burst_binary[i];
}
- //encrypt first part of the burst
+ //decrypt first part of the burst
for (int i = 0; i < 57; i++) {
decrypted_data[i+3] = keystream[i] ^ burst_binary[i+3];
}
@@ -146,7 +148,7 @@ namespace gr {
for (int i = 60; i < 88; i++) {
decrypted_data[i] = burst_binary[i];
}
- //encrypt second part of the burst
+ //decrypt second part of the burst
for (int i = 0; i < 57; i++) {
decrypted_data[i+88] = keystream[i+57] ^ burst_binary[i+88];
}
diff --git a/lib/decryption/decryption_impl.h b/lib/decryption/decryption_impl.h
index 68fbb9c..aa6e5ac 100644
--- a/lib/decryption/decryption_impl.h
+++ b/lib/decryption/decryption_impl.h
@@ -1,19 +1,21 @@
/* -*- c++ -*- */
/*
- * Copyright 2014 <+YOU OR YOUR COMPANY+>.
+ * @file
+ * @author (C) 2014 by Piotr Krysik <ptrkrysik@gmail.com>
+ * @section LICENSE
*
- * This is free software; you can redistribute it and/or modify
+ * Gr-gsm 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,
+ * Gr-gsm 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
+ * along with gr-gsm; see the file COPYING. If not, write to
* the Free Software Foundation, Inc., 51 Franklin Street,
* Boston, MA 02110-1301, USA.
*/
diff --git a/lib/demapping/CMakeLists.txt b/lib/demapping/CMakeLists.txt
new file mode 100644
index 0000000..57d2d1a
--- /dev/null
+++ b/lib/demapping/CMakeLists.txt
@@ -0,0 +1,24 @@
+# Copyright 2011,2012 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.
+
+add_sources(
+ tch_f_chans_demapper_impl.cc
+ universal_ctrl_chans_demapper_impl.cc
+)
+
diff --git a/lib/demapping/tch_f_chans_demapper_impl.cc b/lib/demapping/tch_f_chans_demapper_impl.cc
index 53f46fb..9865a47 100644
--- a/lib/demapping/tch_f_chans_demapper_impl.cc
+++ b/lib/demapping/tch_f_chans_demapper_impl.cc
@@ -1,7 +1,7 @@
/* -*- c++ -*- */
/*
* @file
- * @author Roman Khassraf <rkhassraf@gmail.com>
+ * @author (C) 2015 by Roman Khassraf <rkhassraf@gmail.com>
* @section LICENSE
*
* Gr-gsm is free software; you can redistribute it and/or modify
@@ -109,7 +109,7 @@ namespace gr {
}
}
- void tch_f_chans_demapper_impl::sacch_tch_demapper(uint32_t fn_mod13, u_int32_t fn_mod26, uint32_t frame_nr,
+ void tch_f_chans_demapper_impl::sacch_tch_demapper(uint32_t fn_mod13, uint32_t fn_mod26, uint32_t frame_nr,
pmt::pmt_t *d_bursts_sacch,
uint32_t *d_frame_numbers_sacch, pmt::pmt_t d_bursts[3][8],
uint32_t d_frame_numbers[3][8], pmt::pmt_t msg_out)
diff --git a/lib/demapping/tch_f_chans_demapper_impl.h b/lib/demapping/tch_f_chans_demapper_impl.h
index 01c97fa..bef018d 100644
--- a/lib/demapping/tch_f_chans_demapper_impl.h
+++ b/lib/demapping/tch_f_chans_demapper_impl.h
@@ -1,7 +1,7 @@
/* -*- c++ -*- */
/*
* @file
- * @author Roman Khassraf <rkhassraf@gmail.com>
+ * @author (C) 2015 by Roman Khassraf <rkhassraf@gmail.com>
* @section LICENSE
*
* Gr-gsm is free software; you can redistribute it and/or modify
@@ -44,7 +44,7 @@ namespace gr {
pmt::pmt_t d_bursts_ul[3][8]; // for tch output headers+bursts
pmt::pmt_t d_bursts_sacch_ul[4]; // for sacch output bursts
- void sacch_tch_demapper(uint32_t fn_mod13, u_int32_t fn_mod26, uint32_t frame_nr, pmt::pmt_t *d_bursts_sacch,
+ void sacch_tch_demapper(uint32_t fn_mod13, uint32_t fn_mod26, uint32_t frame_nr, pmt::pmt_t *d_bursts_sacch,
uint32_t *d_frame_numbers_sacch, pmt::pmt_t d_bursts[3][8],
uint32_t d_frame_numbers[3][8], pmt::pmt_t msg_out);
diff --git a/lib/demapping/universal_ctrl_chans_demapper_impl.cc b/lib/demapping/universal_ctrl_chans_demapper_impl.cc
index dc1670d..98bcc9c 100644
--- a/lib/demapping/universal_ctrl_chans_demapper_impl.cc
+++ b/lib/demapping/universal_ctrl_chans_demapper_impl.cc
@@ -1,7 +1,7 @@
/* -*- c++ -*- */
/*
* @file
- * @author Piotr Krysik <ptrkrysik@gmail.com>
+ * @author (C) 2016 by Piotr Krysik <ptrkrysik@gmail.com>
* @section LICENSE
*
* Gr-gsm is free software; you can redistribute it and/or modify
diff --git a/lib/demapping/universal_ctrl_chans_demapper_impl.h b/lib/demapping/universal_ctrl_chans_demapper_impl.h
index 9545ab6..6ba4973 100644
--- a/lib/demapping/universal_ctrl_chans_demapper_impl.h
+++ b/lib/demapping/universal_ctrl_chans_demapper_impl.h
@@ -1,7 +1,7 @@
/* -*- c++ -*- */
/*
* @file
- * @author Piotr Krysik <ptrkrysik@gmail.com>
+ * @author (C) 2016 by Piotr Krysik <ptrkrysik@gmail.com>
* @section LICENSE
*
* Gr-gsm is free software; you can redistribute it and/or modify
diff --git a/lib/flow_control/CMakeLists.txt b/lib/flow_control/CMakeLists.txt
new file mode 100644
index 0000000..30a5f50
--- /dev/null
+++ b/lib/flow_control/CMakeLists.txt
@@ -0,0 +1,29 @@
+# Copyright 2011,2012 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.
+
+add_sources(
+ burst_fnr_filter_impl.cc
+ burst_sdcch_subslot_filter_impl.cc
+ burst_sdcch_subslot_splitter_impl.cc
+ burst_timeslot_filter_impl.cc
+ burst_timeslot_splitter_impl.cc
+ dummy_burst_filter_impl.cc
+ uplink_downlink_splitter_impl.cc
+)
+
diff --git a/lib/flow_control/burst_fnr_filter_impl.cc b/lib/flow_control/burst_fnr_filter_impl.cc
index e665acf..49ddc4c 100644
--- a/lib/flow_control/burst_fnr_filter_impl.cc
+++ b/lib/flow_control/burst_fnr_filter_impl.cc
@@ -1,6 +1,6 @@
/* -*- c++ -*- */
/* @file
- * @author Roman Khassraf <rkhassraf@gmail.com>
+ * @author (C) 2015 by Roman Khassraf <rkhassraf@gmail.com>
* @section LICENSE
*
* Gr-gsm is free software; you can redistribute it and/or modify
@@ -49,7 +49,8 @@ namespace gr {
gr::io_signature::make(0, 0, 0),
gr::io_signature::make(0, 0, 0)),
d_mode(mode),
- d_framenr(fnr)
+ d_framenr(fnr),
+ d_filter_policy(FILTER_POLICY_DEFAULT)
{
message_port_register_in(pmt::mp("in"));
message_port_register_out(pmt::mp("out"));
@@ -64,6 +65,14 @@ namespace gr {
void burst_fnr_filter_impl::process_burst(pmt::pmt_t msg)
{
+ if (d_filter_policy == FILTER_POLICY_DROP_ALL)
+ return;
+
+ if (d_filter_policy == FILTER_POLICY_PASS_ALL) {
+ message_port_pub(pmt::mp("out"), msg);
+ return;
+ }
+
pmt::pmt_t header_plus_burst = pmt::cdr(msg);
gsmtap_hdr * header = (gsmtap_hdr *)pmt::blob_data(header_plus_burst);
@@ -75,5 +84,50 @@ namespace gr {
message_port_pub(pmt::mp("out"), msg);
}
}
+
+ /* External API */
+ unsigned int
+ burst_fnr_filter_impl::get_fn(void)
+ {
+ return d_framenr;
+ }
+
+ unsigned int
+ burst_fnr_filter_impl::set_fn(unsigned int fn)
+ {
+ if (fn <= GSM_HYPERFRAME)
+ d_framenr = fn;
+
+ return d_framenr;
+ }
+
+
+ filter_mode
+ burst_fnr_filter_impl::get_mode(void)
+ {
+ return d_mode;
+ }
+
+ filter_mode
+ burst_fnr_filter_impl::set_mode(filter_mode mode)
+ {
+ d_mode = mode;
+ return d_mode;
+ }
+
+ /* Filtering policy */
+ filter_policy
+ burst_fnr_filter_impl::get_policy(void)
+ {
+ return d_filter_policy;
+ }
+
+ filter_policy
+ burst_fnr_filter_impl::set_policy(filter_policy policy)
+ {
+ d_filter_policy = policy;
+ return d_filter_policy;
+ }
+
} /* namespace gsm */
} /* namespace gr */
diff --git a/lib/flow_control/burst_fnr_filter_impl.h b/lib/flow_control/burst_fnr_filter_impl.h
index 5d9850d..5abd1c2 100644
--- a/lib/flow_control/burst_fnr_filter_impl.h
+++ b/lib/flow_control/burst_fnr_filter_impl.h
@@ -1,6 +1,6 @@
/* -*- c++ -*- */
/* @file
- * @author Roman Khassraf <rkhassraf@gmail.com>
+ * @author (C) 2015 by Roman Khassraf <rkhassraf@gmail.com>
* @section LICENSE
*
* Gr-gsm is free software; you can redistribute it and/or modify
@@ -25,18 +25,33 @@
#include <grgsm/flow_control/burst_fnr_filter.h>
+#define GSM_SUPERFRAME (26 * 51)
+#define GSM_HYPERFRAME (2048 * GSM_SUPERFRAME)
+
namespace gr {
namespace gsm {
class burst_fnr_filter_impl : public burst_fnr_filter
{
private:
+ filter_policy d_filter_policy;
unsigned int d_framenr;
filter_mode d_mode;
public:
burst_fnr_filter_impl(filter_mode mode, unsigned int fnr);
~burst_fnr_filter_impl();
void process_burst(pmt::pmt_t msg);
+
+ /* External API */
+ unsigned int get_fn(void);
+ unsigned int set_fn(unsigned int fn);
+
+ filter_mode get_mode(void);
+ filter_mode set_mode(filter_mode mode);
+
+ /* Filtering policy */
+ filter_policy get_policy(void);
+ filter_policy set_policy(filter_policy policy);
};
} // namespace gsm
diff --git a/lib/flow_control/burst_sdcch_subslot_filter_impl.cc b/lib/flow_control/burst_sdcch_subslot_filter_impl.cc
index 31e2989..cc72f99 100644
--- a/lib/flow_control/burst_sdcch_subslot_filter_impl.cc
+++ b/lib/flow_control/burst_sdcch_subslot_filter_impl.cc
@@ -1,6 +1,6 @@
/* -*- c++ -*- */
/* @file
- * @author Roman Khassraf <rkhassraf@gmail.com>
+ * @author (C) 2015 by Roman Khassraf <rkhassraf@gmail.com>
* @section LICENSE
*
* Gr-gsm is free software; you can redistribute it and/or modify
@@ -48,7 +48,8 @@ namespace gr {
gr::io_signature::make(0, 0, 0),
gr::io_signature::make(0, 0, 0)),
d_mode(mode),
- d_subslot(subslot)
+ d_subslot(subslot),
+ d_filter_policy(FILTER_POLICY_DEFAULT)
{
message_port_register_in(pmt::mp("in"));
message_port_register_out(pmt::mp("out"));
@@ -73,6 +74,14 @@ namespace gr {
0, 0, 0, 0, 1, 1, 1, 1, 2, 2, 2, 2, 3, 3, 3, 3, 4, 4, 4, 4, 5, 5, 5, 5, 6, 6, 6, 6, 7, 7, 7, 7, 0, 0, 0, 0, 1, 1, 1, 1, 2, 2, 2, 2, 3, 3, 3, 3,-1,-1,-1,
0, 0, 0, 0, 1, 1, 1, 1, 2, 2, 2, 2, 3, 3, 3, 3, 4, 4, 4, 4, 5, 5, 5, 5, 6, 6, 6, 6, 7, 7, 7, 7, 4, 4, 4, 4, 5, 5, 5, 5, 6, 6, 6, 6, 7, 7, 7, 7,-1,-1,-1
};
+
+ if (d_filter_policy == FILTER_POLICY_DROP_ALL)
+ return;
+
+ if (d_filter_policy == FILTER_POLICY_PASS_ALL) {
+ message_port_pub(pmt::mp("out"), msg);
+ return;
+ }
pmt::pmt_t header_plus_burst = pmt::cdr(msg);
gsmtap_hdr * header = (gsmtap_hdr *)pmt::blob_data(header_plus_burst);
@@ -101,5 +110,51 @@ namespace gr {
message_port_pub(pmt::mp("out"), msg);
}
}
+
+ /* External API */
+ unsigned int
+ burst_sdcch_subslot_filter_impl::get_ss(void)
+ {
+ return d_subslot;
+ }
+
+ unsigned int
+ burst_sdcch_subslot_filter_impl::set_ss(unsigned int ss)
+ {
+ if ((d_mode == SS_FILTER_SDCCH8 && ss < 8)
+ || (d_mode == SS_FILTER_SDCCH4 && ss < 4))
+ d_subslot = ss;
+
+ return d_subslot;
+ }
+
+
+ subslot_filter_mode
+ burst_sdcch_subslot_filter_impl::get_mode(void)
+ {
+ return d_mode;
+ }
+
+ subslot_filter_mode
+ burst_sdcch_subslot_filter_impl::set_mode(subslot_filter_mode mode)
+ {
+ d_mode = mode;
+ return d_mode;
+ }
+
+ /* Filtering policy */
+ filter_policy
+ burst_sdcch_subslot_filter_impl::get_policy(void)
+ {
+ return d_filter_policy;
+ }
+
+ filter_policy
+ burst_sdcch_subslot_filter_impl::set_policy(filter_policy policy)
+ {
+ d_filter_policy = policy;
+ return d_filter_policy;
+ }
+
} /* namespace gsm */
} /* namespace gr */
diff --git a/lib/flow_control/burst_sdcch_subslot_filter_impl.h b/lib/flow_control/burst_sdcch_subslot_filter_impl.h
index 40a603e..b5ac5ae 100644
--- a/lib/flow_control/burst_sdcch_subslot_filter_impl.h
+++ b/lib/flow_control/burst_sdcch_subslot_filter_impl.h
@@ -1,6 +1,6 @@
/* -*- c++ -*- */
/* @file
- * @author Roman Khassraf <rkhassraf@gmail.com>
+ * @author (C) 2015 by Roman Khassraf <rkhassraf@gmail.com>
* @section LICENSE
*
* Gr-gsm is free software; you can redistribute it and/or modify
@@ -31,12 +31,24 @@ namespace gr {
class burst_sdcch_subslot_filter_impl : public burst_sdcch_subslot_filter
{
private:
+ filter_policy d_filter_policy;
subslot_filter_mode d_mode;
unsigned int d_subslot;
public:
burst_sdcch_subslot_filter_impl(subslot_filter_mode mode, unsigned int subslot);
~burst_sdcch_subslot_filter_impl();
void process_burst(pmt::pmt_t msg);
+
+ /* External API */
+ unsigned int get_ss(void);
+ unsigned int set_ss(unsigned int ss);
+
+ subslot_filter_mode get_mode(void);
+ subslot_filter_mode set_mode(subslot_filter_mode mode);
+
+ /* Filtering policy */
+ filter_policy get_policy(void);
+ filter_policy set_policy(filter_policy policy);
};
} // namespace gsm
diff --git a/lib/flow_control/burst_sdcch_subslot_splitter_impl.cc b/lib/flow_control/burst_sdcch_subslot_splitter_impl.cc
index 90237d9..267f6b4 100644
--- a/lib/flow_control/burst_sdcch_subslot_splitter_impl.cc
+++ b/lib/flow_control/burst_sdcch_subslot_splitter_impl.cc
@@ -1,6 +1,6 @@
/* -*- c++ -*- */
/* @file
- * @author Roman Khassraf <rkhassraf@gmail.com>
+ * @author (C) 2015 by Roman Khassraf <rkhassraf@gmail.com>
* @section LICENSE
*
* Gr-gsm is free software; you can redistribute it and/or modify
@@ -141,5 +141,20 @@ namespace gr {
message_port_pub(pmt::mp(port), msg);
}
+
+ /* External API */
+ splitter_mode
+ burst_sdcch_subslot_splitter_impl::get_mode(void)
+ {
+ return d_mode;
+ }
+
+ splitter_mode
+ burst_sdcch_subslot_splitter_impl::set_mode(splitter_mode mode)
+ {
+ d_mode = mode;
+ return d_mode;
+ }
+
} /* namespace gsm */
} /* namespace gr */
diff --git a/lib/flow_control/burst_sdcch_subslot_splitter_impl.h b/lib/flow_control/burst_sdcch_subslot_splitter_impl.h
index b8eaaea..9eac070 100644
--- a/lib/flow_control/burst_sdcch_subslot_splitter_impl.h
+++ b/lib/flow_control/burst_sdcch_subslot_splitter_impl.h
@@ -1,6 +1,6 @@
/* -*- c++ -*- */
/* @file
- * @author Roman Khassraf <rkhassraf@gmail.com>
+ * @author (C) 2015 by Roman Khassraf <rkhassraf@gmail.com>
* @section LICENSE
*
* Gr-gsm is free software; you can redistribute it and/or modify
@@ -36,6 +36,10 @@ namespace gr {
burst_sdcch_subslot_splitter_impl(splitter_mode mode);
~burst_sdcch_subslot_splitter_impl();
void process_burst(pmt::pmt_t msg);
+
+ /* External API */
+ splitter_mode get_mode(void);
+ splitter_mode set_mode(splitter_mode mode);
};
} // namespace gsm
diff --git a/lib/flow_control/burst_timeslot_filter_impl.cc b/lib/flow_control/burst_timeslot_filter_impl.cc
index ccf4d4c..4fcd847 100644
--- a/lib/flow_control/burst_timeslot_filter_impl.cc
+++ b/lib/flow_control/burst_timeslot_filter_impl.cc
@@ -1,6 +1,6 @@
/* -*- c++ -*- */
/* @file
- * @author Roman Khassraf <rkhassraf@gmail.com>
+ * @author (C) 2015 by Roman Khassraf <rkhassraf@gmail.com>
* @section LICENSE
*
* Gr-gsm is free software; you can redistribute it and/or modify
@@ -48,7 +48,8 @@ namespace gr {
: gr::block("burst_timeslot_filter",
gr::io_signature::make(0, 0, 0),
gr::io_signature::make(0, 0, 0)),
- d_timeslot(timeslot)
+ d_timeslot(timeslot),
+ d_filter_policy(FILTER_POLICY_DEFAULT)
{
message_port_register_in(pmt::mp("in"));
message_port_register_out(pmt::mp("out"));
@@ -63,6 +64,14 @@ namespace gr {
void burst_timeslot_filter_impl::process_burst(pmt::pmt_t msg)
{
+ if (d_filter_policy == FILTER_POLICY_DROP_ALL)
+ return;
+
+ if (d_filter_policy == FILTER_POLICY_PASS_ALL) {
+ message_port_pub(pmt::mp("out"), msg);
+ return;
+ }
+
pmt::pmt_t header_plus_burst = pmt::cdr(msg);
gsmtap_hdr * header = (gsmtap_hdr *)pmt::blob_data(header_plus_burst);
@@ -73,5 +82,38 @@ namespace gr {
message_port_pub(pmt::mp("out"), msg);
}
}
+
+ /*
+ * External API
+ */
+ unsigned int
+ burst_timeslot_filter_impl::get_tn(void)
+ {
+ return d_timeslot;
+ }
+
+ unsigned int
+ burst_timeslot_filter_impl::set_tn(unsigned int tn)
+ {
+ if (tn < 8)
+ d_timeslot = tn;
+
+ return d_timeslot;
+ }
+
+ /* Filtering policy */
+ filter_policy
+ burst_timeslot_filter_impl::get_policy(void)
+ {
+ return d_filter_policy;
+ }
+
+ filter_policy
+ burst_timeslot_filter_impl::set_policy(filter_policy policy)
+ {
+ d_filter_policy = policy;
+ return d_filter_policy;
+ }
+
} /* namespace gsm */
} /* namespace gr */
diff --git a/lib/flow_control/burst_timeslot_filter_impl.h b/lib/flow_control/burst_timeslot_filter_impl.h
index f7b0b2d..eb1953c 100644
--- a/lib/flow_control/burst_timeslot_filter_impl.h
+++ b/lib/flow_control/burst_timeslot_filter_impl.h
@@ -1,6 +1,6 @@
/* -*- c++ -*- */
/* @file
- * @author Roman Khassraf <rkhassraf@gmail.com>
+ * @author (C) 2015 by Roman Khassraf <rkhassraf@gmail.com>
* @section LICENSE
*
* Gr-gsm is free software; you can redistribute it and/or modify
@@ -31,11 +31,20 @@ namespace gr {
class burst_timeslot_filter_impl : public burst_timeslot_filter
{
private:
+ filter_policy d_filter_policy;
unsigned int d_timeslot;
public:
burst_timeslot_filter_impl(unsigned int timeslot);
~burst_timeslot_filter_impl();
void process_burst(pmt::pmt_t msg);
+
+ /* External API */
+ unsigned int get_tn(void);
+ unsigned int set_tn(unsigned int tn);
+
+ /* Filtering policy */
+ filter_policy get_policy(void);
+ filter_policy set_policy(filter_policy policy);
};
} // namespace gsm
diff --git a/lib/flow_control/burst_timeslot_splitter_impl.cc b/lib/flow_control/burst_timeslot_splitter_impl.cc
index 99964b7..1f1572c 100644
--- a/lib/flow_control/burst_timeslot_splitter_impl.cc
+++ b/lib/flow_control/burst_timeslot_splitter_impl.cc
@@ -1,6 +1,6 @@
/* -*- c++ -*- */
/* @file
- * @author Roman Khassraf <rkhassraf@gmail.com>
+ * @author (C) 2015 by Roman Khassraf <rkhassraf@gmail.com>
* @section LICENSE
*
* Gr-gsm is free software; you can redistribute it and/or modify
diff --git a/lib/flow_control/burst_timeslot_splitter_impl.h b/lib/flow_control/burst_timeslot_splitter_impl.h
index 47a9115..6abcb00 100644
--- a/lib/flow_control/burst_timeslot_splitter_impl.h
+++ b/lib/flow_control/burst_timeslot_splitter_impl.h
@@ -1,6 +1,6 @@
/* -*- c++ -*- */
/* @file
- * @author Roman Khassraf <rkhassraf@gmail.com>
+ * @author (C) 2015 by Roman Khassraf <rkhassraf@gmail.com>
* @section LICENSE
*
* Gr-gsm is free software; you can redistribute it and/or modify
diff --git a/lib/flow_control/dummy_burst_filter_impl.cc b/lib/flow_control/dummy_burst_filter_impl.cc
index 33c24cb..4621187 100644
--- a/lib/flow_control/dummy_burst_filter_impl.cc
+++ b/lib/flow_control/dummy_burst_filter_impl.cc
@@ -1,6 +1,6 @@
/* -*- c++ -*- */
/* @file
- * @author Roman Khassraf <rkhassraf@gmail.com>
+ * @author (C) 2015 by Roman Khassraf <rkhassraf@gmail.com>
* @section LICENSE
*
* Gr-gsm is free software; you can redistribute it and/or modify
@@ -60,7 +60,8 @@ namespace gr {
dummy_burst_filter_impl::dummy_burst_filter_impl()
: gr::block("dummy_burst_filter",
gr::io_signature::make(0, 0, 0),
- gr::io_signature::make(0, 0, 0))
+ gr::io_signature::make(0, 0, 0)),
+ d_filter_policy(FILTER_POLICY_DEFAULT)
{
message_port_register_in(pmt::mp("in"));
message_port_register_out(pmt::mp("out"));
@@ -75,6 +76,14 @@ namespace gr {
void dummy_burst_filter_impl::process_burst(pmt::pmt_t msg)
{
+ if (d_filter_policy == FILTER_POLICY_DROP_ALL)
+ return;
+
+ if (d_filter_policy == FILTER_POLICY_PASS_ALL) {
+ message_port_pub(pmt::mp("out"), msg);
+ return;
+ }
+
pmt::pmt_t header_plus_burst = pmt::cdr(msg);
int8_t * burst = (int8_t *)(pmt::blob_data(header_plus_burst)) + sizeof(gsmtap_hdr);
size_t burst_len = pmt::blob_length(header_plus_burst) - sizeof(gsmtap_hdr);
@@ -100,6 +109,20 @@ namespace gr {
}
return true;
}
+
+ /* Filtering policy */
+ filter_policy
+ dummy_burst_filter_impl::get_policy(void)
+ {
+ return d_filter_policy;
+ }
+
+ filter_policy
+ dummy_burst_filter_impl::set_policy(filter_policy policy)
+ {
+ d_filter_policy = policy;
+ return d_filter_policy;
+ }
} /* namespace gsm */
} /* namespace gr */
diff --git a/lib/flow_control/dummy_burst_filter_impl.h b/lib/flow_control/dummy_burst_filter_impl.h
index ce9c741..3433313 100644
--- a/lib/flow_control/dummy_burst_filter_impl.h
+++ b/lib/flow_control/dummy_burst_filter_impl.h
@@ -1,6 +1,6 @@
/* -*- c++ -*- */
/* @file
- * @author Roman Khassraf <rkhassraf@gmail.com>
+ * @author (C) 2015 by Roman Khassraf <rkhassraf@gmail.com>
* @section LICENSE
*
* Gr-gsm is free software; you can redistribute it and/or modify
@@ -35,10 +35,16 @@ namespace gr {
private:
bool is_dummy_burst(int8_t *burst, size_t burst_len);
static const int8_t d_dummy_burst[];
+ filter_policy d_filter_policy;
public:
dummy_burst_filter_impl();
~dummy_burst_filter_impl();
void process_burst(pmt::pmt_t msg);
+
+ /* External API */
+ /* Filtering policy */
+ filter_policy get_policy(void);
+ filter_policy set_policy(filter_policy policy);
};
} // namespace gsm
diff --git a/lib/flow_control/uplink_downlink_splitter_impl.cc b/lib/flow_control/uplink_downlink_splitter_impl.cc
index e98c668..691feb3 100644
--- a/lib/flow_control/uplink_downlink_splitter_impl.cc
+++ b/lib/flow_control/uplink_downlink_splitter_impl.cc
@@ -1,6 +1,6 @@
/* -*- c++ -*- */
/* @file
- * @author Piotr Krysik <ptrkrysik@gmail.com>
+ * @author (C) 2016 by Piotr Krysik <ptrkrysik@gmail.com>
* @section LICENSE
*
* Gr-gsm is free software; you can redistribute it and/or modify
diff --git a/lib/flow_control/uplink_downlink_splitter_impl.h b/lib/flow_control/uplink_downlink_splitter_impl.h
index bc4368a..fb8b2b8 100644
--- a/lib/flow_control/uplink_downlink_splitter_impl.h
+++ b/lib/flow_control/uplink_downlink_splitter_impl.h
@@ -1,6 +1,6 @@
/* -*- c++ -*- */
/* @file
- * @author Piotr Krysik <ptrkrysik@gmail.com>
+ * @author (C) 2016 by Piotr Krysik <ptrkrysik@gmail.com>
* @section LICENSE
*
* Gr-gsm is free software; you can redistribute it and/or modify
diff --git a/lib/misc_utils/CMakeLists.txt b/lib/misc_utils/CMakeLists.txt
new file mode 100644
index 0000000..1c51fb6
--- /dev/null
+++ b/lib/misc_utils/CMakeLists.txt
@@ -0,0 +1,36 @@
+# Copyright 2011,2012 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.
+
+add_sources(
+ burst_file_sink_impl.cc
+ burst_file_source_impl.cc
+ bursts_printer_impl.cc
+ collect_system_info_impl.cc
+ controlled_fractional_resampler_cc_impl.cc
+ controlled_rotator_cc_impl.cc
+ extract_cmc_impl.cc
+ extract_immediate_assignment_impl.cc
+ extract_system_info_impl.cc
+ message_file_sink_impl.cc
+ message_file_source_impl.cc
+ message_printer_impl.cc
+ msg_to_tag_impl.cc
+ tmsi_dumper_impl.cc
+)
+
diff --git a/lib/misc_utils/burst_file_sink_impl.cc b/lib/misc_utils/burst_file_sink_impl.cc
index 25a262e..9990d86 100644
--- a/lib/misc_utils/burst_file_sink_impl.cc
+++ b/lib/misc_utils/burst_file_sink_impl.cc
@@ -1,6 +1,6 @@
/* -*- c++ -*- */
/* @file
- * @author Roman Khassraf <rkhassraf@gmail.com>
+ * @author (C) 2015 by Roman Khassraf <rkhassraf@gmail.com>
* @section LICENSE
*
* Gr-gsm is free software; you can redistribute it and/or modify
diff --git a/lib/misc_utils/burst_file_sink_impl.h b/lib/misc_utils/burst_file_sink_impl.h
index 898c609..3d8dbe4 100644
--- a/lib/misc_utils/burst_file_sink_impl.h
+++ b/lib/misc_utils/burst_file_sink_impl.h
@@ -1,6 +1,6 @@
/* -*- c++ -*- */
/* @file
- * @author Roman Khassraf <rkhassraf@gmail.com>
+ * @author (C) 2015 by Roman Khassraf <rkhassraf@gmail.com>
* @section LICENSE
*
* Gr-gsm is free software; you can redistribute it and/or modify
diff --git a/lib/misc_utils/burst_file_source_impl.cc b/lib/misc_utils/burst_file_source_impl.cc
index 4661dfb..4367150 100644
--- a/lib/misc_utils/burst_file_source_impl.cc
+++ b/lib/misc_utils/burst_file_source_impl.cc
@@ -1,6 +1,6 @@
/* -*- c++ -*- */
/* @file
- * @author Roman Khassraf <rkhassraf@gmail.com>
+ * @author (C) 2015 by Roman Khassraf <rkhassraf@gmail.com>
* @section LICENSE
*
* Gr-gsm is free software; you can redistribute it and/or modify
diff --git a/lib/misc_utils/burst_file_source_impl.h b/lib/misc_utils/burst_file_source_impl.h
index 383dc90..8856520 100644
--- a/lib/misc_utils/burst_file_source_impl.h
+++ b/lib/misc_utils/burst_file_source_impl.h
@@ -1,6 +1,6 @@
/* -*- c++ -*- */
/* @file
- * @author Roman Khassraf <rkhassraf@gmail.com>
+ * @author (C) 2015 by Roman Khassraf <rkhassraf@gmail.com>
* @section LICENSE
*
* Gr-gsm is free software; you can redistribute it and/or modify
diff --git a/lib/misc_utils/bursts_printer_impl.cc b/lib/misc_utils/bursts_printer_impl.cc
index ab134aa..c22e60b 100644
--- a/lib/misc_utils/bursts_printer_impl.cc
+++ b/lib/misc_utils/bursts_printer_impl.cc
@@ -1,7 +1,7 @@
/* -*- c++ -*- */
/*
* @file
- * @author Piotr Krysik <ptrkrysik@gmail.com>
+ * @author (C) 2014 by Piotr Krysik <ptrkrysik@gmail.com>
* @section LICENSE
*
* Gr-gsm is free software; you can redistribute it and/or modify
@@ -30,7 +30,7 @@
#include <iterator>
#include <algorithm>
#include "bursts_printer_impl.h"
-#include <unistd.h>
+//#include <unistd.h>
#include <iostream>
extern "C" {
#include <osmocom/gsm/a5.h>
diff --git a/lib/misc_utils/bursts_printer_impl.h b/lib/misc_utils/bursts_printer_impl.h
index e438c37..e475b0f 100644
--- a/lib/misc_utils/bursts_printer_impl.h
+++ b/lib/misc_utils/bursts_printer_impl.h
@@ -1,7 +1,7 @@
/* -*- c++ -*- */
/*
* @file
- * @author Piotr Krysik <ptrkrysik@gmail.com>
+ * @author (C) 2014 by Piotr Krysik <ptrkrysik@gmail.com>
* @section LICENSE
*
* Gr-gsm is free software; you can redistribute it and/or modify
diff --git a/lib/misc_utils/collect_system_info_impl.cc b/lib/misc_utils/collect_system_info_impl.cc
index 76b3288..072734a 100755..100644
--- a/lib/misc_utils/collect_system_info_impl.cc
+++ b/lib/misc_utils/collect_system_info_impl.cc
@@ -1,7 +1,7 @@
/* -*- c++ -*- */
/*
* @file
- * @author Roman Khassraf <rkhassraf@gmail.com>
+ * @author (C) 2017 by Roman Khassraf <rkhassraf@gmail.com>
* @section LICENSE
*
* Gr-gsm is free software; you can redistribute it and/or modify
@@ -26,7 +26,7 @@
#include <gnuradio/io_signature.h>
#include <grgsm/gsmtap.h>
-#include <unistd.h>
+//#include <unistd.h>
#include <grgsm/endian.h>
#include "collect_system_info_impl.h"
diff --git a/lib/misc_utils/collect_system_info_impl.h b/lib/misc_utils/collect_system_info_impl.h
index df017f6..b320b5e 100755..100644
--- a/lib/misc_utils/collect_system_info_impl.h
+++ b/lib/misc_utils/collect_system_info_impl.h
@@ -1,7 +1,7 @@
/* -*- c++ -*- */
/*
* @file
- * @author Roman Khassraf <rkhassraf@gmail.com>
+ * @author (C) 2017 by Roman Khassraf <rkhassraf@gmail.com>
* @section LICENSE
*
* Gr-gsm is free software; you can redistribute it and/or modify
diff --git a/lib/misc_utils/controlled_fractional_resampler_cc_impl.cc b/lib/misc_utils/controlled_fractional_resampler_cc_impl.cc
index cf15a04..3376c67 100644
--- a/lib/misc_utils/controlled_fractional_resampler_cc_impl.cc
+++ b/lib/misc_utils/controlled_fractional_resampler_cc_impl.cc
@@ -1,6 +1,6 @@
/* -*- c++ -*- */
/* @file
- * @author Piotr Krysik <ptrkrysik@gmail.com>
+ * @author (C) 2016 by Piotr Krysik <ptrkrysik@gmail.com>
* @section LICENSE
*
* Gr-gsm is free software; you can redistribute it and/or modify
diff --git a/lib/misc_utils/controlled_fractional_resampler_cc_impl.h b/lib/misc_utils/controlled_fractional_resampler_cc_impl.h
index de32c34..24e00ed 100644
--- a/lib/misc_utils/controlled_fractional_resampler_cc_impl.h
+++ b/lib/misc_utils/controlled_fractional_resampler_cc_impl.h
@@ -1,6 +1,6 @@
/* -*- c++ -*- */
/* @file
- * @author Piotr Krysik <ptrkrysik@gmail.com>
+ * @author (C) 2016 by Piotr Krysik <ptrkrysik@gmail.com>
* @section LICENSE
*
* Gr-gsm is free software; you can redistribute it and/or modify
diff --git a/lib/misc_utils/controlled_rotator_cc_impl.cc b/lib/misc_utils/controlled_rotator_cc_impl.cc
index a91fd08..9cb5316 100644
--- a/lib/misc_utils/controlled_rotator_cc_impl.cc
+++ b/lib/misc_utils/controlled_rotator_cc_impl.cc
@@ -1,7 +1,7 @@
/* -*- c++ -*- */
/*
* @file
- * @author Piotr Krysik <ptrkrysik@gmail.com>
+ * @author (C) 2014 by Piotr Krysik <ptrkrysik@gmail.com>
* @section LICENSE
*
* Gr-gsm is free software; you can redistribute it and/or modify
@@ -26,7 +26,6 @@
#include <gnuradio/io_signature.h>
#include "controlled_rotator_cc_impl.h"
-#include <math.h>
namespace gr {
namespace gsm {
diff --git a/lib/misc_utils/controlled_rotator_cc_impl.h b/lib/misc_utils/controlled_rotator_cc_impl.h
index fb29356..13bc6fc 100644
--- a/lib/misc_utils/controlled_rotator_cc_impl.h
+++ b/lib/misc_utils/controlled_rotator_cc_impl.h
@@ -1,7 +1,7 @@
/* -*- c++ -*- */
/*
* @file
- * @author Piotr Krysik <ptrkrysik@gmail.com>
+ * @author (C) 2014 by Piotr Krysik <ptrkrysik@gmail.com>
* @section LICENSE
*
* Gr-gsm is free software; you can redistribute it and/or modify
diff --git a/lib/misc_utils/extract_cmc_impl.cc b/lib/misc_utils/extract_cmc_impl.cc
index 9d59736..c28e49f 100755..100644
--- a/lib/misc_utils/extract_cmc_impl.cc
+++ b/lib/misc_utils/extract_cmc_impl.cc
@@ -1,7 +1,7 @@
/* -*- c++ -*- */
/*
* @file
- * @author Roman Khassraf <rkhassraf@gmail.com>
+ * @author (C) 2016 by Roman Khassraf <rkhassraf@gmail.com>
* @section LICENSE
*
* Gr-gsm is free software; you can redistribute it and/or modify
@@ -26,7 +26,7 @@
#include <gnuradio/io_signature.h>
#include <grgsm/gsmtap.h>
-#include <unistd.h>
+//#include <unistd.h>
#include <grgsm/endian.h>
#include "extract_cmc_impl.h"
diff --git a/lib/misc_utils/extract_cmc_impl.h b/lib/misc_utils/extract_cmc_impl.h
index 5fca80f..1dd02cb 100755..100644
--- a/lib/misc_utils/extract_cmc_impl.h
+++ b/lib/misc_utils/extract_cmc_impl.h
@@ -1,7 +1,7 @@
/* -*- c++ -*- */
/*
* @file
- * @author Roman Khassraf <rkhassraf@gmail.com>
+ * @author (C) 2016 by Roman Khassraf <rkhassraf@gmail.com>
* @section LICENSE
*
* Gr-gsm is free software; you can redistribute it and/or modify
diff --git a/lib/misc_utils/extract_immediate_assignment_impl.cc b/lib/misc_utils/extract_immediate_assignment_impl.cc
index 75a1edf..a6d1658 100644
--- a/lib/misc_utils/extract_immediate_assignment_impl.cc
+++ b/lib/misc_utils/extract_immediate_assignment_impl.cc
@@ -1,7 +1,7 @@
/* -*- c++ -*- */
/*
* @file
- * @author Roman Khassraf <rkhassraf@gmail.com>
+ * @author (C) 2015 by Roman Khassraf <rkhassraf@gmail.com>
* @section LICENSE
*
* Gr-gsm is free software; you can redistribute it and/or modify
@@ -26,7 +26,7 @@
#include <gnuradio/io_signature.h>
#include <grgsm/gsmtap.h>
-#include <unistd.h>
+//#include <unistd.h>
#include <map>
#include <grgsm/endian.h>
#include <boost/foreach.hpp>
diff --git a/lib/misc_utils/extract_immediate_assignment_impl.h b/lib/misc_utils/extract_immediate_assignment_impl.h
index cf007e2..44a3830 100644
--- a/lib/misc_utils/extract_immediate_assignment_impl.h
+++ b/lib/misc_utils/extract_immediate_assignment_impl.h
@@ -1,7 +1,7 @@
/* -*- c++ -*- */
/*
* @file
- * @author Roman Khassraf <rkhassraf@gmail.com>
+ * @author (C) 2015 by Roman Khassraf <rkhassraf@gmail.com>
* @section LICENSE
*
* Gr-gsm is free software; you can redistribute it and/or modify
diff --git a/lib/misc_utils/extract_system_info_impl.cc b/lib/misc_utils/extract_system_info_impl.cc
index 6f745a0..ad7adb8 100644
--- a/lib/misc_utils/extract_system_info_impl.cc
+++ b/lib/misc_utils/extract_system_info_impl.cc
@@ -1,7 +1,7 @@
/* -*- c++ -*- */
/*
* @file
- * @author Piotr Krysik <ptrkrysik@gmail.com>
+ * @author (C) 2014 by Piotr Krysik <ptrkrysik@gmail.com>
* @section LICENSE
*
* Gr-gsm is free software; you can redistribute it and/or modify
@@ -26,7 +26,7 @@
#include <gnuradio/io_signature.h>
#include <grgsm/gsmtap.h>
-#include <unistd.h>
+//#include <unistd.h>
#include <map>
#include <iterator>
#include <algorithm>
@@ -180,7 +180,7 @@ namespace gr {
info.pwr_db = header->signal_dbm;
boost::mutex::scoped_lock lock(extract_mutex);
//read cell arfcn's
- gsm48_decode_freq_list(freq, &msg_elements[3], 16, 0x8c, 0x01);
+ gsm48_decode_freq_list(freq, &msg_elements[3], 16, 0x8e, 0x01);
if(d_c0_channels.find(info.id) != d_c0_channels.end()){
d_c0_channels[info.id].copy_nonzero_elements(info);
} else {
diff --git a/lib/misc_utils/extract_system_info_impl.h b/lib/misc_utils/extract_system_info_impl.h
index 20f5adc..2ddc84d 100644
--- a/lib/misc_utils/extract_system_info_impl.h
+++ b/lib/misc_utils/extract_system_info_impl.h
@@ -1,7 +1,7 @@
/* -*- c++ -*- */
/*
* @file
- * @author Piotr Krysik <ptrkrysik@gmail.com>
+ * @author (C) 2014 by Piotr Krysik <ptrkrysik@gmail.com>
* @section LICENSE
*
* Gr-gsm is free software; you can redistribute it and/or modify
diff --git a/lib/misc_utils/message_file_sink_impl.cc b/lib/misc_utils/message_file_sink_impl.cc
index 7e52993..7857e6b 100644
--- a/lib/misc_utils/message_file_sink_impl.cc
+++ b/lib/misc_utils/message_file_sink_impl.cc
@@ -1,6 +1,6 @@
/* -*- c++ -*- */
/* @file
- * @author Roman Khassraf <rkhassraf@gmail.com>
+ * @author (C) 2015 by Roman Khassraf <rkhassraf@gmail.com>
* @section LICENSE
*
* Gr-gsm is free software; you can redistribute it and/or modify
diff --git a/lib/misc_utils/message_file_sink_impl.h b/lib/misc_utils/message_file_sink_impl.h
index 6757e69..9f05a0b 100644
--- a/lib/misc_utils/message_file_sink_impl.h
+++ b/lib/misc_utils/message_file_sink_impl.h
@@ -1,6 +1,6 @@
/* -*- c++ -*- */
/* @file
- * @author Roman Khassraf <rkhassraf@gmail.com>
+ * @author (C) 2015 by Roman Khassraf <rkhassraf@gmail.com>
* @section LICENSE
*
* Gr-gsm is free software; you can redistribute it and/or modify
diff --git a/lib/misc_utils/message_file_source_impl.cc b/lib/misc_utils/message_file_source_impl.cc
index 540fe9e..8164f92 100644
--- a/lib/misc_utils/message_file_source_impl.cc
+++ b/lib/misc_utils/message_file_source_impl.cc
@@ -1,6 +1,6 @@
/* -*- c++ -*- */
/* @file
- * @author Roman Khassraf <rkhassraf@gmail.com>
+ * @author (C) 2015 by Roman Khassraf <rkhassraf@gmail.com>
* @section LICENSE
*
* Gr-gsm is free software; you can redistribute it and/or modify
diff --git a/lib/misc_utils/message_file_source_impl.h b/lib/misc_utils/message_file_source_impl.h
index 42f65cb..312174d 100644
--- a/lib/misc_utils/message_file_source_impl.h
+++ b/lib/misc_utils/message_file_source_impl.h
@@ -1,6 +1,6 @@
/* -*- c++ -*- */
/* @file
- * @author Roman Khassraf <rkhassraf@gmail.com>
+ * @author (C) 2015 by Roman Khassraf <rkhassraf@gmail.com>
* @section LICENSE
*
* Gr-gsm is free software; you can redistribute it and/or modify
diff --git a/lib/misc_utils/message_printer_impl.cc b/lib/misc_utils/message_printer_impl.cc
index 7d19830..e8aa2d3 100644
--- a/lib/misc_utils/message_printer_impl.cc
+++ b/lib/misc_utils/message_printer_impl.cc
@@ -1,7 +1,7 @@
/* -*- c++ -*- */
/*
* @file
- * @author Piotr Krysik <ptrkrysik@gmail.com>
+ * @author (C) 2014 by Piotr Krysik <ptrkrysik@gmail.com>
* @section LICENSE
*
* Gr-gsm is free software; you can redistribute it and/or modify
diff --git a/lib/misc_utils/message_printer_impl.h b/lib/misc_utils/message_printer_impl.h
index 86d27d7..8fdcdfd 100644
--- a/lib/misc_utils/message_printer_impl.h
+++ b/lib/misc_utils/message_printer_impl.h
@@ -1,7 +1,7 @@
/* -*- c++ -*- */
/*
* @file
- * @author Piotr Krysik <ptrkrysik@gmail.com>
+ * @author (C) 2014 by Piotr Krysik <ptrkrysik@gmail.com>
* @section LICENSE
*
* Gr-gsm is free software; you can redistribute it and/or modify
diff --git a/lib/misc_utils/msg_to_tag_impl.cc b/lib/misc_utils/msg_to_tag_impl.cc
index e572864..0ab65e7 100644
--- a/lib/misc_utils/msg_to_tag_impl.cc
+++ b/lib/misc_utils/msg_to_tag_impl.cc
@@ -1,6 +1,6 @@
/* -*- c++ -*- */
/* @file
- * @author Piotr Krysik <ptrkrysik@gmail.com>
+ * @author (C) 2016 by Piotr Krysik <ptrkrysik@gmail.com>
* @section LICENSE
*
* Gr-gsm is free software; you can redistribute it and/or modify
diff --git a/lib/misc_utils/msg_to_tag_impl.h b/lib/misc_utils/msg_to_tag_impl.h
index 4af794c..39c6ca3 100644
--- a/lib/misc_utils/msg_to_tag_impl.h
+++ b/lib/misc_utils/msg_to_tag_impl.h
@@ -1,6 +1,6 @@
/* -*- c++ -*- */
/* @file
- * @author Piotr Krysik <ptrkrysik@gmail.com>
+ * @author (C) 2016 by Piotr Krysik <ptrkrysik@gmail.com>
* @section LICENSE
*
* Gr-gsm is free software; you can redistribute it and/or modify
diff --git a/lib/misc_utils/tmsi_dumper_impl.cc b/lib/misc_utils/tmsi_dumper_impl.cc
index e53aad3..043d935 100644
--- a/lib/misc_utils/tmsi_dumper_impl.cc
+++ b/lib/misc_utils/tmsi_dumper_impl.cc
@@ -1,6 +1,6 @@
/* -*- c++ -*- */
/* @file
- * @author Piotr Krysik <ptrkrysik@gmail.com>
+ * @author (C) 2015 by Piotr Krysik <ptrkrysik@gmail.com>
* @section LICENSE
*
* Gr-gsm is free software; you can redistribute it and/or modify
diff --git a/lib/misc_utils/tmsi_dumper_impl.h b/lib/misc_utils/tmsi_dumper_impl.h
index 8b5a038..3019587 100644
--- a/lib/misc_utils/tmsi_dumper_impl.h
+++ b/lib/misc_utils/tmsi_dumper_impl.h
@@ -1,6 +1,6 @@
/* -*- c++ -*- */
/* @file
- * @author Piotr Krysik <ptrkrysik@gmail.com>
+ * @author (C) 2015 by Piotr Krysik <ptrkrysik@gmail.com>
* @section LICENSE
*
* Gr-gsm is free software; you can redistribute it and/or modify
diff --git a/lib/qa_utils/CMakeLists.txt b/lib/qa_utils/CMakeLists.txt
new file mode 100644
index 0000000..a70967b
--- /dev/null
+++ b/lib/qa_utils/CMakeLists.txt
@@ -0,0 +1,25 @@
+# Copyright 2011,2012 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.
+
+add_sources(
+ burst_sink_impl.cc
+ burst_source_impl.cc
+ message_sink_impl.cc
+ message_source_impl.cc
+)
diff --git a/lib/qa_utils/burst_sink_impl.cc b/lib/qa_utils/burst_sink_impl.cc
index 5f1ae64..102c5be 100644
--- a/lib/qa_utils/burst_sink_impl.cc
+++ b/lib/qa_utils/burst_sink_impl.cc
@@ -1,6 +1,6 @@
/* -*- c++ -*- */
/* @file
- * @author Roman Khassraf <rkhassraf@gmail.com>
+ * @author (C) 2015 by Roman Khassraf <rkhassraf@gmail.com>
* @section LICENSE
*
* Gr-gsm is free software; you can redistribute it and/or modify
diff --git a/lib/qa_utils/burst_sink_impl.h b/lib/qa_utils/burst_sink_impl.h
index 42e1e4a..1035430 100644
--- a/lib/qa_utils/burst_sink_impl.h
+++ b/lib/qa_utils/burst_sink_impl.h
@@ -1,6 +1,6 @@
/* -*- c++ -*- */
/* @file
- * @author Roman Khassraf <rkhassraf@gmail.com>
+ * @author (C) 2015 by Roman Khassraf <rkhassraf@gmail.com>
* @section LICENSE
*
* Gr-gsm is free software; you can redistribute it and/or modify
diff --git a/lib/qa_utils/burst_source_impl.cc b/lib/qa_utils/burst_source_impl.cc
index 7391872..db3d85a 100644
--- a/lib/qa_utils/burst_source_impl.cc
+++ b/lib/qa_utils/burst_source_impl.cc
@@ -1,6 +1,6 @@
/* -*- c++ -*- */
/* @file
- * @author Roman Khassraf <rkhassraf@gmail.com>
+ * @author (C) 2015 by Roman Khassraf <rkhassraf@gmail.com>
* @section LICENSE
*
* Gr-gsm is free software; you can redistribute it and/or modify
diff --git a/lib/qa_utils/burst_source_impl.h b/lib/qa_utils/burst_source_impl.h
index 969faf9..cc7d9b4 100644
--- a/lib/qa_utils/burst_source_impl.h
+++ b/lib/qa_utils/burst_source_impl.h
@@ -1,6 +1,6 @@
/* -*- c++ -*- */
/* @file
- * @author Roman Khassraf <rkhassraf@gmail.com>
+ * @author (C) 2015 by Roman Khassraf <rkhassraf@gmail.com>
* @section LICENSE
*
* Gr-gsm is free software; you can redistribute it and/or modify
diff --git a/lib/qa_utils/message_sink_impl.cc b/lib/qa_utils/message_sink_impl.cc
index 269cada..c16f133 100644
--- a/lib/qa_utils/message_sink_impl.cc
+++ b/lib/qa_utils/message_sink_impl.cc
@@ -1,6 +1,6 @@
/* -*- c++ -*- */
/* @file
- * @author Roman Khassraf <rkhassraf@gmail.com>
+ * @author (C) 2015 by Roman Khassraf <rkhassraf@gmail.com>
* @section LICENSE
*
* Gr-gsm is free software; you can redistribute it and/or modify
diff --git a/lib/qa_utils/message_sink_impl.h b/lib/qa_utils/message_sink_impl.h
index dd11819..b7458af 100644
--- a/lib/qa_utils/message_sink_impl.h
+++ b/lib/qa_utils/message_sink_impl.h
@@ -1,6 +1,6 @@
/* -*- c++ -*- */
/* @file
- * @author Roman Khassraf <rkhassraf@gmail.com>
+ * @author (C) 2015 by Roman Khassraf <rkhassraf@gmail.com>
* @section LICENSE
*
* Gr-gsm is free software; you can redistribute it and/or modify
diff --git a/lib/qa_utils/message_source_impl.cc b/lib/qa_utils/message_source_impl.cc
index 2e71ba4..a2f28d1 100644
--- a/lib/qa_utils/message_source_impl.cc
+++ b/lib/qa_utils/message_source_impl.cc
@@ -1,6 +1,6 @@
/* -*- c++ -*- */
/* @file
- * @author Roman Khassraf <rkhassraf@gmail.com>
+ * @author (C) 2015 by Roman Khassraf <rkhassraf@gmail.com>
* @section LICENSE
*
* Gr-gsm is free software; you can redistribute it and/or modify
diff --git a/lib/qa_utils/message_source_impl.h b/lib/qa_utils/message_source_impl.h
index 9856f78..b5ffdcb 100644
--- a/lib/qa_utils/message_source_impl.h
+++ b/lib/qa_utils/message_source_impl.h
@@ -1,6 +1,6 @@
/* -*- c++ -*- */
/* @file
- * @author Roman Khassraf <rkhassraf@gmail.com>
+ * @author (C) 2015 by Roman Khassraf <rkhassraf@gmail.com>
* @section LICENSE
*
* Gr-gsm is free software; you can redistribute it and/or modify
diff --git a/lib/receiver/CMakeLists.txt b/lib/receiver/CMakeLists.txt
new file mode 100644
index 0000000..343d2f8
--- /dev/null
+++ b/lib/receiver/CMakeLists.txt
@@ -0,0 +1,26 @@
+# Copyright 2011,2012 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.
+
+add_sources(
+ clock_offset_control_impl.cc
+ cx_channel_hopper_impl.cc
+ receiver_config.cc
+ receiver_impl.cc
+ viterbi_detector.cc
+)
diff --git a/lib/receiver/clock_offset_control_impl.cc b/lib/receiver/clock_offset_control_impl.cc
index 7fef8fa..868cbc2 100644
--- a/lib/receiver/clock_offset_control_impl.cc
+++ b/lib/receiver/clock_offset_control_impl.cc
@@ -1,8 +1,7 @@
-
/* -*- c++ -*- */
/*
* @file
- * @author Piotr Krysik <ptrkrysik@gmail.com>
+ * @author (C) 2015-2017 Piotr Krysik <ptrkrysik@gmail.com>
* @section LICENSE
*
* Gr-gsm is free software; you can redistribute it and/or modify
@@ -175,6 +174,7 @@ void clock_offset_control_impl::send_ctrl_messages(float freq_offset)
messages = dict_add(messages, pmt::string_to_symbol("set_phase_inc"), pmt::from_double(-2*M_PI*freq_offset/(d_osr * GSM_SYMBOL_RATE)));
messages = dict_add(messages, pmt::string_to_symbol("set_resamp_ratio"), pmt::from_double((1-(freq_offset/d_fc))*samp_rate_ratio));
messages = dict_add(messages, pmt::string_to_symbol("setting_freq_offset"), pmt::from_double(-freq_offset));
+ messages = dict_add(messages, pmt::string_to_symbol("clock_offset_in_ppm"), pmt::from_double(-freq_offset/d_fc*1.0e6));
message_port_pub(pmt::intern("ctrl"), messages);
}
diff --git a/lib/receiver/clock_offset_control_impl.h b/lib/receiver/clock_offset_control_impl.h
index 60c3df6..fc90f45 100644
--- a/lib/receiver/clock_offset_control_impl.h
+++ b/lib/receiver/clock_offset_control_impl.h
@@ -1,7 +1,7 @@
/* -*- c++ -*- */
/*
* @file
- * @author Piotr Krysik <ptrkrysik@gmail.com>
+ * @author (C) 2015-2017 by Piotr Krysik <ptrkrysik@gmail.com>
* @section LICENSE
*
* Gr-gsm is free software; you can redistribute it and/or modify
diff --git a/lib/receiver/cx_channel_hopper_impl.cc b/lib/receiver/cx_channel_hopper_impl.cc
index 27a0cae..e5be27a 100644
--- a/lib/receiver/cx_channel_hopper_impl.cc
+++ b/lib/receiver/cx_channel_hopper_impl.cc
@@ -1,6 +1,6 @@
/* -*- c++ -*- */
/* @file
- * @author Piotr Krysik <ptrkrysik@gmail.com>
+ * @author (C) 2015 by Pieter Robyns <pieter.robyns@uhasselt.be>
* @section LICENSE
*
* Gr-gsm is free software; you can redistribute it and/or modify
diff --git a/lib/receiver/cx_channel_hopper_impl.h b/lib/receiver/cx_channel_hopper_impl.h
index 2e630db..7ac9bf4 100644
--- a/lib/receiver/cx_channel_hopper_impl.h
+++ b/lib/receiver/cx_channel_hopper_impl.h
@@ -1,6 +1,6 @@
/* -*- c++ -*- */
/* @file
- * @author Piotr Krysik <ptrkrysik@gmail.com>
+ * @author (C) 2015 by Pieter Robyns <pieter.robyns@uhasselt.be>
* @section LICENSE
*
* Gr-gsm is free software; you can redistribute it and/or modify
diff --git a/lib/receiver/gsm_constants.h b/lib/receiver/gsm_constants.h
index 131d923..9052290 100644
--- a/lib/receiver/gsm_constants.h
+++ b/lib/receiver/gsm_constants.h
@@ -1,7 +1,7 @@
/* -*- c++ -*- */
/*
* @file
- * @author Piotr Krysik <ptrkrysik@gmail.com>
+ * @author (C) 2014 by Piotr Krysik <ptrkrysik@gmail.com>
* @section LICENSE
*
* Gr-gsm is free software; you can redistribute it and/or modify
diff --git a/lib/receiver/receiver_config.cc b/lib/receiver/receiver_config.cc
index 0abf200..5c3363a 100644
--- a/lib/receiver/receiver_config.cc
+++ b/lib/receiver/receiver_config.cc
@@ -1,7 +1,7 @@
/* -*- c++ -*- */
/*
* @file
- * @author Piotr Krysik <ptrkrysik@gmail.com>
+ * @author (C) 2009-2017 by Piotr Krysik <ptrkrysik@gmail.com>
* @section LICENSE
*
* Gr-gsm is free software; you can redistribute it and/or modify
@@ -23,8 +23,10 @@
#include "config.h"
#endif
+#include <cmath>
#include <receiver_config.h>
+
burst_counter & burst_counter::operator++(int)
{
d_timeslot_nr++;
@@ -53,8 +55,8 @@ burst_counter burst_counter::subtract_timeslots(unsigned int number_of_timeslots
if (timeslot_nr < 0) {
timeslot_nr = timeslot_nr + 8;
- t2 = (d_t2 - 1) % 26;
- t3 = (d_t3 - 1) % 51;
+ t2 = (d_t2+26 - 1) % 26;
+ t3 = (d_t3+51 - 1) % 51;
if ((d_t2 == 0) && (d_t3 == 0)) {
t1 = (d_t1 - 1) % (1 << 11);
diff --git a/lib/receiver/receiver_config.h b/lib/receiver/receiver_config.h
index 17b98c8..6ad43a1 100644
--- a/lib/receiver/receiver_config.h
+++ b/lib/receiver/receiver_config.h
@@ -1,7 +1,7 @@
/* -*- c++ -*- */
/*
* @file
- * @author Piotr Krysik <ptrkrysik@gmail.com>
+ * @author (C) 2009-2017 by Piotr Krysik <ptrkrysik@gmail.com>
* @section LICENSE
*
* Gr-gsm is free software; you can redistribute it and/or modify
@@ -24,7 +24,6 @@
#include <vector>
#include <algorithm>
-#include <math.h>
#include <stdint.h>
#include <gsm_constants.h>
diff --git a/lib/receiver/receiver_impl.cc b/lib/receiver/receiver_impl.cc
index e69183f..c7626b5 100644
--- a/lib/receiver/receiver_impl.cc
+++ b/lib/receiver/receiver_impl.cc
@@ -1,7 +1,7 @@
/* -*- c++ -*- */
/*
* @file
- * @author Piotr Krysik <ptrkrysik@gmail.com>
+ * @author (C) 2009-2017 by Piotr Krysik <ptrkrysik@gmail.com>
* @section LICENSE
*
* Gr-gsm is free software; you can redistribute it and/or modify
@@ -26,884 +26,1072 @@
#include <gnuradio/io_signature.h>
#include <gnuradio/math.h>
-#include <math.h>
-#include <boost/circular_buffer.hpp>
+
#include <algorithm>
-#include <numeric>
-#include <vector>
-#include <viterbi_detector.h>
#include <string.h>
#include <iostream>
-//#include <iomanip>
-#include <boost/scoped_ptr.hpp>
+#include <numeric>
+#include <vector>
-#include <sch.h>
-#include "receiver_impl.h"
+#include <boost/circular_buffer.hpp>
+#include <boost/scoped_ptr.hpp>
#include <grgsm/endian.h>
-//files included for debuging
-//#include "plotting/plotting.hpp"
-//#include <pthread.h>
+#include "receiver_impl.h"
+#include "viterbi_detector.h"
+#include "sch.h"
+
+#if 0
+/* Files included for debuging */
+#include "plotting/plotting.hpp"
+#include <pthread.h>
+#include <iomanip>
+#endif
#define SYNC_SEARCH_RANGE 30
namespace gr
{
-namespace gsm
-{
-receiver::sptr
-receiver::make(int osr, const std::vector<int> &cell_allocation, const std::vector<int> &tseq_nums, bool process_uplink)
-{
- return gnuradio::get_initial_sptr
- (new receiver_impl(osr, cell_allocation, tseq_nums, process_uplink));
-}
+ namespace gsm
+ {
+
+ /* The public constructor */
+ receiver::sptr
+ receiver::make(
+ int osr, const std::vector<int> &cell_allocation,
+ const std::vector<int> &tseq_nums, bool process_uplink)
+ {
+ return gnuradio::get_initial_sptr
+ (new receiver_impl(osr, cell_allocation,
+ tseq_nums, process_uplink));
+ }
-/*
- * The private constructor
- */
-receiver_impl::receiver_impl(int osr, const std::vector<int> &cell_allocation, const std::vector<int> &tseq_nums, bool process_uplink)
- : gr::sync_block("receiver",
- gr::io_signature::make(1, -1, sizeof(gr_complex)),
- gr::io_signature::make(0, 0, 0)),
- d_OSR(osr),
- d_process_uplink(process_uplink),
- d_chan_imp_length(CHAN_IMP_RESP_LENGTH),
- d_counter(0),
- d_fcch_start_pos(0),
- d_freq_offset_setting(0),
- d_state(fcch_search),
- d_burst_nr(osr),
- d_failed_sch(0),
- d_signal_dbm(-120),
- d_tseq_nums(tseq_nums),
- d_cell_allocation(cell_allocation),
- d_last_time(0.0)
-{
- int i;
- //don't send samples to the receiver until there are at least samples for one
- set_output_multiple(floor((TS_BITS + 2 * GUARD_PERIOD) * d_OSR)); // burst and two gurad periods (one gurard period is an arbitrary overlap)
- 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++)
+ /* The private constructor */
+ receiver_impl::receiver_impl(
+ int osr, const std::vector<int> &cell_allocation,
+ const std::vector<int> &tseq_nums, bool process_uplink
+ ) : gr::sync_block("receiver",
+ gr::io_signature::make(1, -1, sizeof(gr_complex)),
+ gr::io_signature::make(0, 0, 0)),
+ d_OSR(osr),
+ d_process_uplink(process_uplink),
+ d_chan_imp_length(CHAN_IMP_RESP_LENGTH),
+ d_counter(0),
+ d_fcch_start_pos(0),
+ d_freq_offset_setting(0),
+ d_state(fcch_search),
+ d_burst_nr(osr),
+ d_failed_sch(0),
+ d_signal_dbm(-120),
+ d_tseq_nums(tseq_nums),
+ d_cell_allocation(cell_allocation),
+ d_last_time(0.0)
{
- gr_complex startpoint = (train_seq[i][0]==0) ? gr_complex(1.0, 0.0) : gr_complex(-1.0, 0.0); //if first bit of the seqeunce ==0 first symbol ==1
- //if first bit of the seqeunce ==1 first symbol ==-1
- gmsk_mapper(train_seq[i], N_TRAIN_BITS, d_norm_training_seq[i], startpoint);
+ /**
+ * Don't send samples to the receiver
+ * until there are at least samples for one
+ */
+ set_output_multiple(floor((TS_BITS + 2 * GUARD_PERIOD) * d_OSR));
+
+ /**
+ * Prepare SCH sequence bits
+ *
+ * (TS_BITS + 2 * GUARD_PERIOD)
+ * Burst and two guard periods
+ * (one guard period is an arbitrary overlap)
+ */
+ gmsk_mapper(SYNC_BITS, N_SYNC_BITS,
+ d_sch_training_seq, gr_complex(0.0, -1.0));
+
+ /* Prepare bits of training sequences */
+ for (int i = 0; i < TRAIN_SEQ_NUM; i++) {
+ /**
+ * If first bit of the sequence is 0
+ * => first symbol is 1, else -1
+ */
+ gr_complex startpoint = train_seq[i][0] == 0 ?
+ gr_complex(1.0, 0.0) : gr_complex(-1.0, 0.0);
+ gmsk_mapper(train_seq[i], N_TRAIN_BITS,
+ d_norm_training_seq[i], startpoint);
+ }
+
+ /* Register output ports */
+ message_port_register_out(pmt::mp("C0"));
+ message_port_register_out(pmt::mp("CX"));
+ message_port_register_out(pmt::mp("measurements"));
+
+ /**
+ * Configure the receiver,
+ * i.e. tell it where to find which burst type
+ */
+ configure_receiver();
}
- message_port_register_out(pmt::mp("C0"));
- message_port_register_out(pmt::mp("CX"));
- message_port_register_out(pmt::mp("measurements"));
- configure_receiver(); //configure the receiver - tell it where to find which burst type
-}
-/*
- * Our virtual destructor.
- */
-receiver_impl::~receiver_impl()
-{
-}
+ /* Our virtual destructor */
+ receiver_impl::~receiver_impl() {}
-int
-receiver_impl::work(int noutput_items,
- gr_vector_const_void_star &input_items,
- gr_vector_void_star &output_items)
-{
-// std::vector<const gr_complex *> iii = (std::vector<const gr_complex *>) input_items; // jak zrobić to rzutowanie poprawnie
- gr_complex * input = (gr_complex *) input_items[0];
- std::vector<tag_t> freq_offset_tags;
- uint64_t start = nitems_read(0);
- uint64_t stop = start + noutput_items;
-
- float current_time = static_cast<float>(start)/(GSM_SYMBOL_RATE*d_OSR);
- if((current_time - d_last_time) > 0.1)
+ int
+ receiver_impl::work(
+ int noutput_items,
+ gr_vector_const_void_star &input_items,
+ gr_vector_void_star &output_items)
{
- pmt::pmt_t msg = pmt::make_tuple(pmt::mp("current_time"),pmt::from_double(current_time));
+ gr_complex *input = (gr_complex *) input_items[0];
+ uint64_t start = nitems_read(0);
+ uint64_t stop = start + noutput_items;
+ d_freq_offset_tag_in_fcch = false;
+
+#if 0
+ /* FIXME: jak zrobić to rzutowanie poprawnie */
+ std::vector<const gr_complex *> iii =
+ (std::vector<const gr_complex *>) input_items;
+#endif
+
+ /* Time synchronization loop */
+ float current_time =
+ static_cast<float>(start / (GSM_SYMBOL_RATE * d_OSR));
+ if ((current_time - d_last_time) > 0.1) {
+ pmt::pmt_t msg = pmt::make_tuple(pmt::mp("current_time"),
+ pmt::from_double(current_time));
message_port_pub(pmt::mp("measurements"), msg);
d_last_time = current_time;
- }
+ }
+
+ /* Frequency correction loop */
+ std::vector<tag_t> freq_offset_tags;
+ pmt::pmt_t key = pmt::string_to_symbol("setting_freq_offset");
+ get_tags_in_range(freq_offset_tags, 0, start, stop, key);
- pmt::pmt_t key = pmt::string_to_symbol("setting_freq_offset");
- get_tags_in_range(freq_offset_tags, 0, start, stop, key);
- bool freq_offset_tag_in_fcch = false;
-
- if(!freq_offset_tags.empty()){
+ if (!freq_offset_tags.empty()) {
tag_t freq_offset_tag = freq_offset_tags[0];
uint64_t tag_offset = freq_offset_tag.offset - start;
-
+ d_freq_offset_setting = pmt::to_double(freq_offset_tag.value);
+
burst_type b_type = d_channel_conf.get_burst_type(d_burst_nr);
- if(d_state == synchronized && b_type == fcch_burst){
- uint64_t last_sample_nr = ceil((GUARD_PERIOD + 2.0 * TAIL_BITS + 156.25) * d_OSR) + 1;
- if(tag_offset < last_sample_nr){
- freq_offset_tag_in_fcch = true;
- }
+ if (d_state == synchronized && b_type == fcch_burst){
+ uint64_t last_sample_nr =
+ ceil((GUARD_PERIOD + 2.0 * TAIL_BITS + 156.25) * d_OSR) + 1;
+ d_freq_offset_tag_in_fcch = tag_offset < last_sample_nr;
}
- d_freq_offset_setting = pmt::to_double(freq_offset_tag.value);
+ }
+
+ /* Main state machine */
+ switch (d_state) {
+ case fcch_search:
+ fcch_search_handler(input, noutput_items);
+ break;
+ case sch_search:
+ sch_search_handler(input, noutput_items);
+ break;
+ case synchronized:
+ synchronized_handler(input, input_items, noutput_items);
+ break;
+ }
+
+ return 0;
}
-
- switch (d_state)
- {
- //bootstrapping
- case fcch_search:
+
+ void
+ receiver_impl::fcch_search_handler(gr_complex *input, int noutput_items)
{
- double freq_offset_tmp;
- if (find_fcch_burst(input, noutput_items,freq_offset_tmp))
- {
- pmt::pmt_t msg = pmt::make_tuple(pmt::mp("freq_offset"),pmt::from_double(freq_offset_tmp-d_freq_offset_setting),pmt::mp("fcch_search"));
- message_port_pub(pmt::mp("measurements"), msg);
+ double freq_offset_tmp;
- d_state = sch_search;
- }
- else
- {
- d_state = fcch_search;
- }
- break;
+ /* Check if received samples is a FCCN burst */
+ if (!find_fcch_burst(input, noutput_items, freq_offset_tmp))
+ return;
+
+ /* We found it, compose a message */
+ pmt::pmt_t msg = pmt::make_tuple(
+ pmt::mp("freq_offset"),
+ pmt::from_double(freq_offset_tmp - d_freq_offset_setting),
+ pmt::mp("fcch_search")
+ );
+
+ /* Notify FCCH loop */
+ message_port_pub(pmt::mp("measurements"), msg);
+
+ /* Update current state */
+ d_state = sch_search;
}
- case sch_search:
+ void
+ receiver_impl::sch_search_handler(gr_complex *input, int noutput_items)
{
- std::vector<gr_complex> channel_imp_resp(CHAN_IMP_RESP_LENGTH*d_OSR);
- int t1, t2, t3;
- int burst_start = 0;
- unsigned char output_binary[BURST_SIZE];
+ std::vector<gr_complex> channel_imp_resp(CHAN_IMP_RESP_LENGTH * d_OSR);
+ unsigned char burst_buf[BURST_SIZE];
+ int rc, t1, t2, t3;
+ int burst_start;
+
+ /* Wait until we get a SCH burst */
+ if (!reach_sch_burst(noutput_items))
+ return;
+
+ /* Get channel impulse response from it */
+ burst_start = get_sch_chan_imp_resp(input, &channel_imp_resp[0]);
+
+ /* Detect bits using MLSE detection */
+ detect_burst(input, &channel_imp_resp[0], burst_start, burst_buf);
+
+ /* Attempt to decode BSIC and frame number */
+ rc = decode_sch(&burst_buf[3], &t1, &t2, &t3, &d_ncc, &d_bcc);
+ if (rc) {
+ /**
+ * There is error in the SCH burst,
+ * go back to the FCCH search state
+ */
+ d_state = fcch_search;
+ return;
+ }
+
+ /* Set counter of bursts value */
+ d_burst_nr.set(t1, t2, t3, 0);
+ d_burst_nr++;
+
+ /* Consume samples up to the next guard period */
+ consume_each(burst_start + BURST_SIZE * d_OSR + 4 * d_OSR);
+
+ /* Update current state */
+ d_state = synchronized;
+ }
- if (reach_sch_burst(noutput_items)) //wait for a SCH burst
+ void
+ receiver_impl::synchronized_handler(gr_complex *input,
+ gr_vector_const_void_star &input_items, int noutput_items)
+ {
+ /**
+ * In this state receiver is synchronized and it processes
+ * bursts according to burst type for given burst number
+ */
+ std::vector<gr_complex> channel_imp_resp(CHAN_IMP_RESP_LENGTH * d_OSR);
+ size_t inputs_to_process = d_cell_allocation.size();
+ unsigned char output_binary[BURST_SIZE];
+ burst_type b_type;
+ int to_consume = 0;
+ int offset = 0;
+
+ if (d_process_uplink)
+ inputs_to_process *= 2;
+
+ /* Process all connected inputs */
+ for (size_t input_nr = 0; input_nr < inputs_to_process; input_nr++) {
+ input = (gr_complex *) input_items[input_nr];
+ double signal_pwr = 0;
+
+ for (int ii = GUARD_PERIOD; ii < TS_BITS; ii++)
+ signal_pwr += abs(input[ii]) * abs(input[ii]);
+
+ signal_pwr = signal_pwr / (TS_BITS);
+ d_signal_dbm = round(10 * log10(signal_pwr / 50));
+
+ if (input_nr == 0)
+ d_c0_signal_dbm = d_signal_dbm;
+
+ /* Get burst type for given burst number */
+ b_type = input_nr == 0 ?
+ d_channel_conf.get_burst_type(d_burst_nr) : normal_or_noise;
+
+ /* Process burst according to its type */
+ switch (b_type) {
+ case fcch_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
- {
- d_burst_nr.set(t1, t2, t3, 0); //set counter of bursts value
- d_burst_nr++;
-
- consume_each(burst_start + BURST_SIZE * d_OSR + 4*d_OSR); //consume samples up to next guard period
- d_state = synchronized;
- }
- else
- {
- d_state = fcch_search; //if there is error in the sch burst go back to fcch search phase
+ if (d_freq_offset_tag_in_fcch)
+ break;
+
+ /* Send all-zero sequence message */
+ send_burst(d_burst_nr, fc_fb, GSMTAP_BURST_FCCH, input_nr);
+
+ /* Extract frequency offset */
+ 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_tmp =
+ compute_freq_offset(input, first_sample, last_sample);
+
+ /* Frequency correction loop */
+ pmt::pmt_t msg = pmt::make_tuple(
+ pmt::mp("freq_offset"),
+ pmt::from_double(freq_offset_tmp - d_freq_offset_setting),
+ pmt::mp("synchronized"));
+ message_port_pub(pmt::mp("measurements"), msg);
+
+ break;
+ }
+
+ case sch_burst:
+ {
+ int d_ncc, d_bcc;
+ int t1, t2, t3;
+ int rc;
+
+ /* Get channel impulse response */
+ d_c0_burst_start = get_sch_chan_imp_resp(input,
+ &channel_imp_resp[0]);
+
+ /* Perform MLSE detection */
+ detect_burst(input, &channel_imp_resp[0],
+ d_c0_burst_start, output_binary);
+
+ /* Compose a message with GSMTAP header and bits */
+ send_burst(d_burst_nr, output_binary,
+ GSMTAP_BURST_SCH, input_nr);
+
+ /* Attempt to decode SCH burst */
+ rc = decode_sch(&output_binary[3], &t1, &t2, &t3, &d_ncc, &d_bcc);
+ if (rc) {
+ if (++d_failed_sch >= MAX_SCH_ERRORS) {
+ /* We have to resynchronize, change state */
+ d_state = fcch_search;
+
+ /* Frequency correction loop */
+ pmt::pmt_t msg = pmt::make_tuple(pmt::mp("freq_offset"),
+ pmt::from_double(0.0),pmt::mp("sync_loss"));
+ message_port_pub(pmt::mp("measurements"), msg);
}
+
+ break;
+ }
+
+ /**
+ * Decoding was successful, now
+ * compute offset from burst_start,
+ * burst should start after a guard period.
+ */
+ offset = d_c0_burst_start - floor((GUARD_PERIOD) * d_OSR);
+ to_consume += offset;
+ d_failed_sch = 0;
+
+ break;
}
- else
+
+ case normal_burst:
{
- d_state = sch_search;
+ float normal_corr_max;
+ /**
+ * Get channel impulse response for given
+ * training sequence number - d_bcc
+ */
+ d_c0_burst_start = get_norm_chan_imp_resp(input,
+ &channel_imp_resp[0], &normal_corr_max, d_bcc);
+
+ /* Perform MLSE detection */
+ detect_burst(input, &channel_imp_resp[0],
+ d_c0_burst_start, output_binary);
+
+ /* Compose a message with GSMTAP header and bits */
+ send_burst(d_burst_nr, output_binary,
+ GSMTAP_BURST_NORMAL, input_nr);
+
+ break;
}
- break;
- }
- //in this state receiver is synchronized and it processes bursts according to burst type for given burst number
- case synchronized:
- {
- std::vector<gr_complex> channel_imp_resp(CHAN_IMP_RESP_LENGTH*d_OSR);
- int offset = 0;
- int to_consume = 0;
- unsigned char output_binary[BURST_SIZE];
- burst_type b_type;
- unsigned int inputs_to_process=d_cell_allocation.size();
-
- if(d_process_uplink)
+
+ case dummy_or_normal:
{
- inputs_to_process = 2*inputs_to_process;
+ unsigned int normal_burst_start, dummy_burst_start;
+ float dummy_corr_max, normal_corr_max;
+
+ dummy_burst_start = get_norm_chan_imp_resp(input,
+ &channel_imp_resp[0], &dummy_corr_max, TS_DUMMY);
+ normal_burst_start = get_norm_chan_imp_resp(input,
+ &channel_imp_resp[0], &normal_corr_max, d_bcc);
+
+ if (normal_corr_max > dummy_corr_max) {
+ d_c0_burst_start = normal_burst_start;
+
+ /* Perform MLSE detection */
+ detect_burst(input, &channel_imp_resp[0],
+ normal_burst_start, output_binary);
+
+ /* Compose a message with GSMTAP header and bits */
+ send_burst(d_burst_nr, output_binary,
+ GSMTAP_BURST_NORMAL, input_nr);
+ } else {
+ d_c0_burst_start = dummy_burst_start;
+
+ /* Compose a message with GSMTAP header and bits */
+ send_burst(d_burst_nr, dummy_burst,
+ GSMTAP_BURST_DUMMY, input_nr);
+ }
+
+ break;
}
-
- for(int input_nr=0; input_nr<inputs_to_process; input_nr++)
+
+ case normal_or_noise:
{
- double signal_pwr = 0;
- input = (gr_complex *)input_items[input_nr];
-
- for(int ii=GUARD_PERIOD;ii<TS_BITS;ii++)
- {
- signal_pwr += abs(input[ii])*abs(input[ii]);
- }
- signal_pwr = signal_pwr/(TS_BITS);
- d_signal_dbm = round(10*log10(signal_pwr/50));
- if(input_nr==0){
- d_c0_signal_dbm = d_signal_dbm;
- }
-
- if(input_nr==0) //for c0 channel burst type is controlled by channel configuration
- {
- b_type = d_channel_conf.get_burst_type(d_burst_nr); //get burst type for given burst number
- }
- else
- {
- b_type = normal_or_noise; //for the rest it can be only normal burst or noise (at least at this moment of development)
+ std::vector<gr_complex> v(input, input + noutput_items);
+ float normal_corr_max = -1e6;
+// float normal_corr_max_tmp;
+ unsigned int burst_start;
+ int max_tn, tseq_num;
+
+ if (d_tseq_nums.size() == 0) {
+ /**
+ * There is no information about training sequence,
+ * however the receiver can detect it with use of a
+ * very simple algorithm based on finding
+ */
+ get_norm_chan_imp_resp(input, &channel_imp_resp[0],
+ &normal_corr_max, 0);
+
+ float ts_max = normal_corr_max;
+ int ts_max_num = 0;
+
+ for (int ss = 1; ss <= 7; ss++) {
+ get_norm_chan_imp_resp(input, &channel_imp_resp[0],
+ &normal_corr_max, ss);
+
+ if (ts_max < normal_corr_max) {
+ ts_max = normal_corr_max;
+ ts_max_num = ss;
+ }
}
-
- switch (b_type)
- {
- case fcch_burst: //if it's FCCH burst
- {
- if(freq_offset_tag_in_fcch==false)
- {
- 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_tmp = compute_freq_offset(input, first_sample, last_sample); //extract frequency offset from it
- send_burst(d_burst_nr, fc_fb, GSMTAP_BURST_FCCH, input_nr);
-
- pmt::pmt_t msg = pmt::make_tuple(pmt::mp("freq_offset"),pmt::from_double(freq_offset_tmp-d_freq_offset_setting),pmt::mp("synchronized"));
- message_port_pub(pmt::mp("measurements"), msg);
- }
- break;
- }
- case sch_burst: //if it's SCH burst
- {
- int t1, t2, t3, d_ncc, d_bcc;
- d_c0_burst_start = get_sch_chan_imp_resp(input, &channel_imp_resp[0]); //get channel impulse response
-
- detect_burst(input, &channel_imp_resp[0], d_c0_burst_start, output_binary); //MLSE detection of bits
- send_burst(d_burst_nr, output_binary, GSMTAP_BURST_SCH, input_nr);
- 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;
- offset = d_c0_burst_start - floor((GUARD_PERIOD) * d_OSR); //compute offset from burst_start - burst should start after a guard period
- 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 = fcch_search;
- pmt::pmt_t msg = pmt::make_tuple(pmt::mp("freq_offset"),pmt::from_double(0.0),pmt::mp("sync_loss"));
- message_port_pub(pmt::mp("measurements"), msg);
- //DCOUT("Re-Synchronization!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!");
- }
- }
- break;
- }
- case normal_burst:
- {
- float normal_corr_max; //if it's normal burst
- d_c0_burst_start = get_norm_chan_imp_resp(input, &channel_imp_resp[0], &normal_corr_max, d_bcc); //get channel impulse response for given training sequence number - d_bcc
- detect_burst(input, &channel_imp_resp[0], d_c0_burst_start, output_binary); //MLSE detection of bits
- send_burst(d_burst_nr, output_binary, GSMTAP_BURST_NORMAL, input_nr);
- break;
- }
- case dummy_or_normal:
- {
- unsigned int normal_burst_start, dummy_burst_start;
- float dummy_corr_max, normal_corr_max;
-
- dummy_burst_start = get_norm_chan_imp_resp(input, &channel_imp_resp[0], &dummy_corr_max, TS_DUMMY);
- normal_burst_start = get_norm_chan_imp_resp(input, &channel_imp_resp[0], &normal_corr_max, d_bcc);
-
- if (normal_corr_max > dummy_corr_max)
- {
- d_c0_burst_start = normal_burst_start;
- detect_burst(input, &channel_imp_resp[0], normal_burst_start, output_binary);
- send_burst(d_burst_nr, output_binary, GSMTAP_BURST_NORMAL, input_nr);
- }
- else
- {
- d_c0_burst_start = dummy_burst_start;
- send_burst(d_burst_nr, dummy_burst, GSMTAP_BURST_DUMMY, input_nr);
- }
- break;
- }
- case rach_burst:
- break;
- case dummy:
- send_burst(d_burst_nr, dummy_burst, GSMTAP_BURST_DUMMY, input_nr);
- break;
- case normal_or_noise:
- {
- unsigned int burst_start;
- float normal_corr_max_tmp;
- float normal_corr_max=-1e6;
- int max_tn;
- std::vector<gr_complex> v(input, input + noutput_items);
- //if(d_signal_dbm>=d_c0_signal_dbm-13)
- {
- if(d_tseq_nums.size()==0) //there is no information about training sequence
- { //however the receiver can detect it
- get_norm_chan_imp_resp(input, &channel_imp_resp[0], &normal_corr_max, 0);
- float ts_max=normal_corr_max; //with use of a very simple algorithm based on finding
- int ts_max_num=0; //maximum correlation
- for(int ss=1; ss<=7; ss++)
- {
- get_norm_chan_imp_resp(input, &channel_imp_resp[0], &normal_corr_max, ss);
- if(ts_max<normal_corr_max)
- {
- ts_max = normal_corr_max;
- ts_max_num = ss;
- }
- }
- d_tseq_nums.push_back(ts_max_num);
- }
- int tseq_num;
- if(input_nr<=d_tseq_nums.size())
- {
- tseq_num = d_tseq_nums[input_nr-1];
- } else
- {
- tseq_num = d_tseq_nums.back();
- }
- burst_start = get_norm_chan_imp_resp(input, &channel_imp_resp[0], &normal_corr_max, tseq_num);
-// if(abs(d_c0_burst_start-burst_start)<=2){ //unused check/filter based on timing
- // if((normal_corr_max/sqrt(signal_pwr))>=0.9)
- {
- detect_burst(input, &channel_imp_resp[0], burst_start, output_binary);
- send_burst(d_burst_nr, output_binary, GSMTAP_BURST_NORMAL, input_nr);
- }
- }
- break;
- }
- case empty: //if it's empty burst
- break; //do nothing
- }
-
- if(input_nr==input_items.size()-1)
- {
- 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
- consume_each(to_consume);
- }
- //and add offset which is introduced by
- //0.25 fractional part of a guard period
+
+ d_tseq_nums.push_back(ts_max_num);
+ }
+
+ /* Choose proper training sequence number */
+ tseq_num = input_nr <= d_tseq_nums.size() ?
+ d_tseq_nums[input_nr - 1] : d_tseq_nums.back();
+
+ /* Get channel impulse response */
+ burst_start = get_norm_chan_imp_resp(input, &channel_imp_resp[0],
+ &normal_corr_max, tseq_num);
+
+ /* Perform MLSE detection */
+ detect_burst(input, &channel_imp_resp[0],
+ burst_start, output_binary);
+
+ /* Compose a message with GSMTAP header and bits */
+ send_burst(d_burst_nr, output_binary, GSMTAP_BURST_NORMAL, input_nr);
+
+ break;
}
- }
- break;
- }
- return 0;
-}
-bool receiver_impl::find_fcch_burst(const gr_complex *input, const int nitems, double & computed_freq_offset)
-{
- boost::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;
- boost::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;
- //@}
+ case dummy:
+ send_burst(d_burst_nr, dummy_burst, GSMTAP_BURST_DUMMY, input_nr);
+ break;
- fcch_search_state = init;
+ case rach_burst:
+ case empty:
+ /* Do nothing */
+ break;
+ }
- while (!end)
- {
- switch (fcch_search_state)
- {
+ if (input_nr == input_items.size() - 1) {
+ /* Go to the next burst */
+ d_burst_nr++;
- 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;
+ /* Consume samples of the burst up to next guard period */
+ to_consume += TS_BITS * d_OSR + d_burst_nr.get_offset();
+ consume_each(to_consume);
+ }
+ }
+ }
- break;
+ bool
+ receiver_impl::find_fcch_burst(const gr_complex *input,
+ const int nitems, double &computed_freq_offset)
+ {
+ /* Circular buffer used to scan through signal to find */
+ boost::circular_buffer<float>
+ phase_diff_buffer(FCCH_HITS_NEEDED * d_OSR);
+ boost::circular_buffer<float>::iterator buffer_iter;
+
+ float lowest_max_min_diff;
+ float phase_diff; /* Best match for FCCH burst */
+ float min_phase_diff;
+ float max_phase_diff;
+ double best_sum = 0;
+ gr_complex conjprod;
+ int start_pos;
+ int hit_count;
+ int miss_count;
+
+ int sample_number = 0;
+ int to_consume = 0;
+ bool result = false;
+ bool end = false;
+
+ /* 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;
+
+ /* Set initial state */
+ fcch_search_state = init;
+
+ while (!end)
+ {
+ switch (fcch_search_state) {
+ case init:
+ {
+ hit_count = 0;
+ miss_count = 0;
+ start_pos = -1;
+ lowest_max_min_diff = 99999;
+ phase_diff_buffer.clear();
- case search: // search for positive samples
- sample_number++;
+ /* Change current state */
+ fcch_search_state = search;
- 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 search:
+ {
+ sample_number++;
+
+ if (sample_number > nitems - FCCH_HITS_NEEDED * d_OSR) {
+ /**
+ * If it isn't possible to find FCCH, because
+ * there is too few samples left to look into,
+ * don't do anything with those samples which are left
+ * and consume only those which were checked
+ */
+ to_consume = sample_number;
+ fcch_search_state = search_fail;
break;
+ }
+
+ phase_diff = compute_phase_diff(input[sample_number],
+ input[sample_number - 1]);
+
+ /**
+ * If a positive phase difference was found
+ * switch to state in which searches for FCCH
+ */
+ if (phase_diff > 0) {
+ to_consume = sample_number;
+ fcch_search_state = found_something;
+ } else {
+ fcch_search_state = search;
+ }
+
+ break;
+ }
- case found_something: // search for FCCH and the best position of it
+ case found_something:
{
- if (phase_diff > 0)
- {
- hit_count++; //positive phase differencies increases hits_count
- }
- else
- {
- miss_count++; //negative increases miss_count
+ if (phase_diff > 0)
+ hit_count++;
+ else
+ 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;
+ continue;
+ }
+
+ 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;
+ }
+
+ 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;
+ best_sum = 0;
+
+ for (buffer_iter = phase_diff_buffer.begin();
+ buffer_iter != (phase_diff_buffer.end());
+ buffer_iter++) {
+ /* Store best value of phase offset sum */
+ best_sum += *buffer_iter - (M_PI / 2) / d_OSR;
+ }
}
+ }
- 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 there is no single sample left to check */
+ if (++sample_number >= nitems) {
+ fcch_search_state = search_fail;
+ continue;
+ }
- 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;
- 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;
}
- break;
case fcch_found:
{
- 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/6 / (2 * M_PI); //1625000.0/6 - GMSK symbol rate in GSM
- computed_freq_offset = freq_offset;
-
- end = true;
- result = true;
- break;
+ /* Consume one FCCH burst */
+ to_consume = start_pos + FCCH_HITS_NEEDED * d_OSR + 1;
+ d_fcch_start_pos = d_counter + start_pos;
+
+ /**
+ * Compute frequency offset
+ *
+ * 1625000.0 / 6 - GMSK symbol rate in GSM
+ */
+ double phase_offset = best_sum / FCCH_HITS_NEEDED;
+ double freq_offset = phase_offset * 1625000.0 / 6 / (2 * M_PI);
+ computed_freq_offset = freq_offset;
+
+ end = true;
+ result = true;
+
+ break;
}
case search_fail:
- end = true;
- result = false;
- break;
+ end = true;
+ result = false;
+ break;
}
- }
+ }
- d_counter += to_consume;
- consume_each(to_consume);
+ d_counter += to_consume;
+ consume_each(to_consume);
- return result;
-}
-
-double receiver_impl::compute_freq_offset(const gr_complex * input, unsigned first_sample, unsigned last_sample)
-{
- double phase_sum = 0;
- unsigned ii;
+ return result;
+ }
- for (ii = first_sample; ii < last_sample; ii++)
+ double
+ receiver_impl::compute_freq_offset(const gr_complex * input,
+ unsigned first_sample, unsigned last_sample)
{
- double phase_diff = compute_phase_diff(input[ii], input[ii-1]) - (M_PI / 2) / d_OSR;
- phase_sum += phase_diff;
- }
+ double phase_sum = 0;
+ unsigned ii;
- double phase_offset = phase_sum / (last_sample - first_sample);
- double freq_offset = phase_offset * 1625000.0 / (12.0 * M_PI);
- return freq_offset;
-}
+ 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;
+ }
-inline float receiver_impl::compute_phase_diff(gr_complex val1, gr_complex val2)
-{
- gr_complex conjprod = val1 * conj(val2);
- return fast_atan2f(imag(conjprod), real(conjprod));
-}
+ double phase_offset = phase_sum / (last_sample - first_sample);
+ double freq_offset = phase_offset * 1625000.0 / (12.0 * M_PI);
-bool receiver_impl::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;
+ return freq_offset;
+ }
- //consume samples until d_counter will be equal to sample_nr_near_sch_start
- if (d_counter < sample_nr_near_sch_start)
+ inline float
+ receiver_impl::compute_phase_diff(gr_complex val1, gr_complex val2)
{
- if (d_counter + nitems >= sample_nr_near_sch_start)
- {
- to_consume = sample_nr_near_sch_start - d_counter;
- }
- else
- {
- to_consume = nitems;
- }
- result = false;
+ gr_complex conjprod = val1 * conj(val2);
+ return fast_atan2f(imag(conjprod), real(conjprod));
}
- else
+
+ bool
+ receiver_impl::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 = d_fcch_start_pos
+ + (FRAME_BITS - SAFETY_MARGIN) * d_OSR;
+
+ /* Consume samples until d_counter will be equal to sample_nr */
+ if (d_counter < sample_nr) {
+ to_consume = d_counter + nitems >= sample_nr ?
+ sample_nr - d_counter : nitems;
+ } else {
to_consume = 0;
result = true;
- }
+ }
- d_counter += to_consume;
- consume_each(to_consume);
- return result;
-}
+ d_counter += to_consume;
+ consume_each(to_consume);
-int receiver_impl::get_sch_chan_imp_resp(const gr_complex *input, gr_complex * chan_imp_resp)
-{
- std::vector<gr_complex> correlation_buffer;
- std::vector<float> power_buffer;
- std::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;
+ return result;
+ }
- for (int ii = SYNC_POS * d_OSR; ii < (SYNC_POS + SYNC_SEARCH_RANGE) *d_OSR; ii++)
+ int
+ receiver_impl::get_sch_chan_imp_resp(const gr_complex *input,
+ gr_complex * chan_imp_resp)
{
- gr_complex correlation = correlate_sequence(&d_sch_training_seq[5], N_SYNC_BITS - 10, &input[ii]);
+ std::vector<gr_complex> correlation_buffer;
+ std::vector<float> window_energy_buffer;
+ std::vector<float> power_buffer;
+
+ int chan_imp_resp_center = 0;
+ int strongest_window_nr;
+ int burst_start;
+ float energy = 0;
+
+ int len = (SYNC_POS + SYNC_SEARCH_RANGE) * d_OSR;
+ for (int ii = SYNC_POS * d_OSR; ii < len; 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(std::pow(abs(correlation), 2));
- }
- //compute window energies
- std::vector<float>::iterator iter = power_buffer.begin();
- bool loop_end = false;
- while (iter != power_buffer.end())
- {
+ }
+
+ /* Compute window energies */
+ std::vector<float>::iterator iter = power_buffer.begin();
+ while (iter != power_buffer.end()) {
std::vector<float>::iterator iter_ii = iter;
+ bool loop_end = false;
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)
- {
+ 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);
}
- iter++;
+
+ if (loop_end)
+ break;
+
window_energy_buffer.push_back(energy);
- }
+ iter++;
+ }
- strongest_window_nr = max_element(window_energy_buffer.begin(), window_energy_buffer.end()) - window_energy_buffer.begin();
- // d_channel_imp_resp.clear();
+ strongest_window_nr = max_element(window_energy_buffer.begin(),
+ window_energy_buffer.end()) - window_energy_buffer.begin();
- max_correlation = 0;
- for (int ii = 0; ii < (d_chan_imp_length) *d_OSR; ii++)
- {
+#if 0
+ d_channel_imp_resp.clear();
+#endif
+
+ float 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);
+ if (abs(correlation) > max_correlation) {
+ chan_imp_resp_center = ii;
+ max_correlation = abs(correlation);
}
- // d_channel_imp_resp.push_back(correlation);
+
+#if 0
+ d_channel_imp_resp.push_back(correlation);
+#endif
+
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;
-}
+ burst_start = strongest_window_nr + chan_imp_resp_center
+ - 48 * d_OSR - 2 * d_OSR + 2 + SYNC_POS * d_OSR;
+ return burst_start;
+ }
-void receiver_impl::detect_burst(const gr_complex * input, gr_complex * chan_imp_resp, int burst_start, unsigned char * output_binary)
-{
- float output[BURST_SIZE];
- std::vector<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[0], d_chan_imp_length*d_OSR);
- for (int ii = 0; ii < (d_chan_imp_length); ii++)
+ void
+ receiver_impl::detect_burst(const gr_complex * input,
+ gr_complex * chan_imp_resp, int burst_start,
+ unsigned char * output_binary)
{
+ std::vector<gr_complex> rhh_temp(CHAN_IMP_RESP_LENGTH * d_OSR);
+ unsigned int stop_states[2] = {4, 12};
+ gr_complex filtered_burst[BURST_SIZE];
+ gr_complex rhh[CHAN_IMP_RESP_LENGTH];
+ float output[BURST_SIZE];
+ int start_state = 3;
+
+ autocorrelation(chan_imp_resp, &rhh_temp[0], 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);
+ 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);
+ 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);
+ for (int i = 0; i < BURST_SIZE; i++)
+ output_binary[i] = output[i] > 0;
}
-}
-void receiver_impl::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);
+ void
+ receiver_impl::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);
+ gmsk_output[0] = start_point;
- int current_symbol;
- int encoded_symbol;
- int previous_symbol = 2 * input[0] - 1;
- gmsk_output[0] = start_point;
+ int previous_symbol = 2 * input[0] - 1;
+ int current_symbol;
+ int encoded_symbol;
- for (int i = 1; i < nitems; i++)
- {
- //change bits representation to NRZ
+ for (int i = 1; i < nitems; i++) {
+ /* Change bits representation to NRZ */
current_symbol = 2 * input[i] - 1;
- //differentially encode
+
+ /* 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];
+
+ /* And do GMSK mapping */
+ gmsk_output[i] = j * gr_complex(encoded_symbol, 0.0)
+ * gmsk_output[i-1];
+
previous_symbol = current_symbol;
+ }
}
-}
-
-gr_complex receiver_impl::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++)
+ gr_complex
+ receiver_impl::correlate_sequence(const gr_complex * sequence,
+ int length, const gr_complex * input)
{
- sample_number = (ii * d_OSR) ;
- result += sequence[ii] * conj(input[sample_number]);
- }
+ gr_complex result(0.0, 0.0);
- result = result / gr_complex(length, 0);
- return result;
-}
+ for (int ii = 0; ii < length; ii++)
+ result += sequence[ii] * conj(input[ii * d_OSR]);
-//computes autocorrelation for positive arguments
-inline void receiver_impl::autocorrelation(const gr_complex * input, gr_complex * out, int nitems)
-{
- int i, k;
- for (k = nitems - 1; k >= 0; k--)
+ return result / gr_complex(length, 0);
+ }
+
+ /* Computes autocorrelation for positive arguments */
+ inline void
+ receiver_impl::autocorrelation(const gr_complex * input,
+ gr_complex * out, int nitems)
{
+ for (int 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]);
- }
+ for (int i = k; i < nitems; i++)
+ out[k] += input[i] * conj(input[i - k]);
+ }
}
-}
-
-inline void receiver_impl::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++)
+ inline void
+ receiver_impl::mafi(const gr_complex * input, int nitems,
+ gr_complex * filter, int filter_length, gr_complex * output)
{
- a = n * d_OSR;
+ for (int n = 0; n < nitems; n++) {
+ int 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++;
+ for (int ii = 0; ii < filter_length; ii++) {
+ if ((a + ii) >= nitems * d_OSR)
+ break;
+
+ output[n] += input[a + ii] * filter[ii];
}
+ }
}
-}
-//especially computations of strongest_window_nr
-int receiver_impl::get_norm_chan_imp_resp(const gr_complex *input, gr_complex * chan_imp_resp, float *corr_max, int bcc)
-{
- std::vector<gr_complex> correlation_buffer;
- std::vector<float> power_buffer;
- std::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 - 5*d_OSR;
- // int search_start_pos = search_center - d_chan_imp_length * d_OSR;
- int search_stop_pos = search_center + d_chan_imp_length * d_OSR + 5 * 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(std::pow(abs(correlation), 2));
- }
-// plot(power_buffer);
- //compute window energies
- std::vector<float>::iterator iter = power_buffer.begin();
- bool loop_end = false;
- while (iter != power_buffer.end())
+ /* Especially computations of strongest_window_nr */
+ int
+ receiver_impl::get_norm_chan_imp_resp(const gr_complex *input,
+ gr_complex *chan_imp_resp, float *corr_max, int bcc)
{
+ std::vector<gr_complex> correlation_buffer;
+ std::vector<float> window_energy_buffer;
+ std::vector<float> power_buffer;
+
+ int search_center = (int) (TRAIN_POS + GUARD_PERIOD) * d_OSR;
+ int search_start_pos = search_center + 1 - 5 * d_OSR;
+ int search_stop_pos = search_center
+ + d_chan_imp_length * d_OSR + 5 * 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(std::pow(abs(correlation), 2));
+ }
+
+#if 0
+ plot(power_buffer);
+#endif
+
+ /* Compute window energies */
+ std::vector<float>::iterator iter = power_buffer.begin();
+ while (iter != power_buffer.end()) {
std::vector<float>::iterator iter_ii = iter;
- energy = 0;
+ bool loop_end = false;
+ float 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)
- {
+ int len = d_chan_imp_length * d_OSR;
+ for (int ii = 0; ii < len; ii++, iter_ii++) {
+ if (iter_ii == power_buffer.end()) {
+ loop_end = true;
break;
+ }
+
+ energy += (*iter_ii);
}
- iter++;
+
+ if (loop_end)
+ break;
window_energy_buffer.push_back(energy);
- }
+ iter++;
+ }
- strongest_window_nr = max_element(window_energy_buffer.begin(), window_energy_buffer.end()-((d_chan_imp_length)*d_OSR)) - window_energy_buffer.begin();
- //strongest_window_nr = strongest_window_nr-d_OSR;
- if(strongest_window_nr<0){
- strongest_window_nr = 0;
- }
-
- max_correlation = 0;
- for (int ii = 0; ii < (d_chan_imp_length)*d_OSR; ii++)
- {
+ /* Calculate the strongest window number */
+ int strongest_window_nr = max_element(window_energy_buffer.begin(),
+ window_energy_buffer.end() - d_chan_imp_length * d_OSR)
+ - window_energy_buffer.begin();
+
+ if (strongest_window_nr < 0)
+ strongest_window_nr = 0;
+
+ float 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;
- }
-
- *corr_max = max_correlation;
+ max_correlation = abs(correlation);
- //DCOUT("strongest_window_nr_new: " << strongest_window_nr);
- burst_start = search_start_pos + strongest_window_nr - TRAIN_POS * d_OSR; //compute first sample posiiton which corresponds to the first sample of the impulse response
-
- //DCOUT("burst_start: " << burst_start);
- return burst_start;
-}
+#if 0
+ d_channel_imp_resp.push_back(correlation);
+#endif
+ chan_imp_resp[ii] = correlation;
+ }
+
+ *corr_max = max_correlation;
-void receiver_impl::send_burst(burst_counter burst_nr, const unsigned char * burst_binary, uint8_t burst_type, unsigned int input_nr)
-{
- boost::scoped_ptr<gsmtap_hdr> tap_header(new gsmtap_hdr());
-
- tap_header->version = GSMTAP_VERSION;
- tap_header->hdr_len = sizeof(gsmtap_hdr)/4;
- tap_header->type = GSMTAP_TYPE_UM_BURST;
- tap_header->sub_type = burst_type;
- bool uplink_burst = (input_nr >= d_cell_allocation.size());
- if(!uplink_burst) // downlink burst
- {
- tap_header->timeslot = static_cast<uint8_t>(d_burst_nr.get_timeslot_nr());
- tap_header->frame_number = htobe32(d_burst_nr.get_frame_nr());
- tap_header->arfcn = htobe16(d_cell_allocation[input_nr]) ;
+ /**
+ * Compute first sample position, which corresponds
+ * to the first sample of the impulse response
+ */
+ return search_start_pos + strongest_window_nr - TRAIN_POS * d_OSR;
}
- else //uplink burst
+
+
+ void
+ receiver_impl::send_burst(burst_counter burst_nr,
+ const unsigned char * burst_binary, uint8_t burst_type,
+ size_t input_nr)
{
- tap_header->timeslot = static_cast<uint8_t>(d_burst_nr.subtract_timeslots(3).get_timeslot_nr());
- tap_header->frame_number = htobe32(d_burst_nr.subtract_timeslots(3).get_frame_nr());
- input_nr = input_nr - d_cell_allocation.size();
- tap_header->arfcn = htobe16(d_cell_allocation[input_nr] | 0x4000);
- }
- tap_header->signal_dbm = static_cast<int8_t>(d_signal_dbm);
- tap_header->snr_db = 0;
-
- int8_t header_plus_burst[sizeof(gsmtap_hdr)+BURST_SIZE];
- memcpy(header_plus_burst, tap_header.get(), sizeof(gsmtap_hdr));
- memcpy(header_plus_burst+sizeof(gsmtap_hdr), burst_binary, BURST_SIZE);
-
- pmt::pmt_t blob_header_plus_burst = pmt::make_blob(header_plus_burst,sizeof(gsmtap_hdr)+BURST_SIZE);
- pmt::pmt_t msg = pmt::cons(pmt::PMT_NIL, blob_header_plus_burst);
-
- if(input_nr==0){
+ /* Buffer for GSMTAP header and burst */
+ uint8_t buf[sizeof(gsmtap_hdr) + BURST_SIZE];
+ uint32_t frame_number;
+ uint16_t arfcn;
+ uint8_t tn;
+
+ /* Set pointers to GSMTAP header and burst inside buffer */
+ struct gsmtap_hdr *tap_header = (struct gsmtap_hdr *) buf;
+ uint8_t *burst = buf + sizeof(gsmtap_hdr);
+
+ tap_header->version = GSMTAP_VERSION;
+ tap_header->hdr_len = sizeof(gsmtap_hdr) / 4;
+ tap_header->type = GSMTAP_TYPE_UM_BURST;
+ tap_header->sub_type = burst_type;
+
+ bool dl_burst = !(input_nr >= d_cell_allocation.size());
+ if (dl_burst) {
+ tn = static_cast<uint8_t>(d_burst_nr.get_timeslot_nr());
+ frame_number = htobe32(d_burst_nr.get_frame_nr());
+ arfcn = htobe16(d_cell_allocation[input_nr]);
+ } else {
+ input_nr -= d_cell_allocation.size();
+ tn = static_cast<uint8_t>
+ (d_burst_nr.subtract_timeslots(3).get_timeslot_nr());
+ frame_number = htobe32(
+ d_burst_nr.subtract_timeslots(3).get_frame_nr());
+ arfcn = htobe16(
+ d_cell_allocation[input_nr] | GSMTAP_ARFCN_F_UPLINK);
+ }
+
+ tap_header->frame_number = frame_number;
+ tap_header->timeslot = tn;
+ tap_header->arfcn = arfcn;
+
+ tap_header->signal_dbm = static_cast<int8_t>(d_signal_dbm);
+ tap_header->snr_db = 0; /* FIXME: Can we calculate this? */
+
+ /* Copy burst to the buffer */
+ memcpy(burst, burst_binary, BURST_SIZE);
+
+ /* Allocate a new message */
+ pmt::pmt_t blob = pmt::make_blob(buf, sizeof(gsmtap_hdr) + BURST_SIZE);
+ pmt::pmt_t msg = pmt::cons(pmt::PMT_NIL, blob);
+
+ /* Send message */
+ if (input_nr == 0)
message_port_pub(pmt::mp("C0"), msg);
- } else {
+ else
message_port_pub(pmt::mp("CX"), msg);
}
-}
-void receiver_impl::configure_receiver()
-{
- d_channel_conf.set_multiframe_type(TIMESLOT0, multiframe_51);
- d_channel_conf.set_burst_types(TIMESLOT0, TEST51, sizeof(TEST51) / sizeof(unsigned), dummy_or_normal);
-
- d_channel_conf.set_burst_types(TIMESLOT0, TEST_CCH_FRAMES, sizeof(TEST_CCH_FRAMES) / sizeof(unsigned), dummy_or_normal);
- d_channel_conf.set_burst_types(TIMESLOT0, FCCH_FRAMES, sizeof(FCCH_FRAMES) / sizeof(unsigned), fcch_burst);
- d_channel_conf.set_burst_types(TIMESLOT0, SCH_FRAMES, sizeof(SCH_FRAMES) / sizeof(unsigned), sch_burst);
-
- d_channel_conf.set_multiframe_type(TIMESLOT1, multiframe_51);
- d_channel_conf.set_burst_types(TIMESLOT1, TEST51, sizeof(TEST51) / sizeof(unsigned), dummy_or_normal);
- d_channel_conf.set_multiframe_type(TIMESLOT2, multiframe_51);
- d_channel_conf.set_burst_types(TIMESLOT2, TEST51, sizeof(TEST51) / sizeof(unsigned), dummy_or_normal);
- d_channel_conf.set_multiframe_type(TIMESLOT3, multiframe_51);
- d_channel_conf.set_burst_types(TIMESLOT3, TEST51, sizeof(TEST51) / sizeof(unsigned), dummy_or_normal);
- d_channel_conf.set_multiframe_type(TIMESLOT4, multiframe_51);
- d_channel_conf.set_burst_types(TIMESLOT4, TEST51, sizeof(TEST51) / sizeof(unsigned), dummy_or_normal);
- d_channel_conf.set_multiframe_type(TIMESLOT5, multiframe_51);
- d_channel_conf.set_burst_types(TIMESLOT5, TEST51, sizeof(TEST51) / sizeof(unsigned), dummy_or_normal);
- d_channel_conf.set_multiframe_type(TIMESLOT6, multiframe_51);
- d_channel_conf.set_burst_types(TIMESLOT6, TEST51, sizeof(TEST51) / sizeof(unsigned), dummy_or_normal);
- d_channel_conf.set_multiframe_type(TIMESLOT7, multiframe_51);
- d_channel_conf.set_burst_types(TIMESLOT7, TEST51, sizeof(TEST51) / sizeof(unsigned), dummy_or_normal);
-}
-
-void receiver_impl::set_cell_allocation(const std::vector<int> &cell_allocation)
-{
- d_cell_allocation = cell_allocation;
-}
+ void
+ receiver_impl::configure_receiver(void)
+ {
+ d_channel_conf.set_multiframe_type(TIMESLOT0, multiframe_51);
+ d_channel_conf.set_burst_types(TIMESLOT0, TEST51,
+ sizeof(TEST51) / sizeof(unsigned), dummy_or_normal);
+ d_channel_conf.set_burst_types(TIMESLOT0, TEST_CCH_FRAMES,
+ sizeof(TEST_CCH_FRAMES) / sizeof(unsigned), dummy_or_normal);
+ d_channel_conf.set_burst_types(TIMESLOT0, FCCH_FRAMES,
+ sizeof(FCCH_FRAMES) / sizeof(unsigned), fcch_burst);
+ d_channel_conf.set_burst_types(TIMESLOT0, SCH_FRAMES,
+ sizeof(SCH_FRAMES) / sizeof(unsigned), sch_burst);
+
+ d_channel_conf.set_multiframe_type(TIMESLOT1, multiframe_51);
+ d_channel_conf.set_burst_types(TIMESLOT1, TEST51,
+ sizeof(TEST51) / sizeof(unsigned), dummy_or_normal);
+
+ d_channel_conf.set_multiframe_type(TIMESLOT2, multiframe_51);
+ d_channel_conf.set_burst_types(TIMESLOT2, TEST51,
+ sizeof(TEST51) / sizeof(unsigned), dummy_or_normal);
+
+ d_channel_conf.set_multiframe_type(TIMESLOT3, multiframe_51);
+ d_channel_conf.set_burst_types(TIMESLOT3, TEST51,
+ sizeof(TEST51) / sizeof(unsigned), dummy_or_normal);
+
+ d_channel_conf.set_multiframe_type(TIMESLOT4, multiframe_51);
+ d_channel_conf.set_burst_types(TIMESLOT4, TEST51,
+ sizeof(TEST51) / sizeof(unsigned), dummy_or_normal);
+
+ d_channel_conf.set_multiframe_type(TIMESLOT5, multiframe_51);
+ d_channel_conf.set_burst_types(TIMESLOT5, TEST51,
+ sizeof(TEST51) / sizeof(unsigned), dummy_or_normal);
+
+ d_channel_conf.set_multiframe_type(TIMESLOT6, multiframe_51);
+ d_channel_conf.set_burst_types(TIMESLOT6, TEST51,
+ sizeof(TEST51) / sizeof(unsigned), dummy_or_normal);
+
+ d_channel_conf.set_multiframe_type(TIMESLOT7, multiframe_51);
+ d_channel_conf.set_burst_types(TIMESLOT7, TEST51,
+ sizeof(TEST51) / sizeof(unsigned), dummy_or_normal);
+ }
-void receiver_impl::set_tseq_nums(const std::vector<int> & tseq_nums)
-{
- d_tseq_nums = tseq_nums;
-}
+ void
+ receiver_impl::set_cell_allocation(
+ const std::vector<int> &cell_allocation)
+ {
+ d_cell_allocation = cell_allocation;
+ }
-void receiver_impl::reset()
-{
- d_state = fcch_search;
-}
+ void
+ receiver_impl::set_tseq_nums(const std::vector<int> &tseq_nums)
+ {
+ d_tseq_nums = tseq_nums;
+ }
-} /* namespace gsm */
-} /* namespace gr */
+ void
+ receiver_impl::reset(void)
+ {
+ d_state = fcch_search;
+ }
+ } /* namespace gsm */
+} /* namespace gr */
diff --git a/lib/receiver/receiver_impl.h b/lib/receiver/receiver_impl.h
index 6074dd5..0b032db 100644
--- a/lib/receiver/receiver_impl.h
+++ b/lib/receiver/receiver_impl.h
@@ -1,7 +1,7 @@
/* -*- c++ -*- */
/*
* @file
- * @author Piotr Krysik <ptrkrysik@gmail.com>
+ * @author (C) 2009-2017 by Piotr Krysik <ptrkrysik@gmail.com>
* @section LICENSE
*
* Gr-gsm is free software; you can redistribute it and/or modify
@@ -63,6 +63,7 @@ namespace gr {
/**@name Variables used to store result of the find_fcch_burst fuction */
//@{
+ bool d_freq_offset_tag_in_fcch; ///< frequency offset tag presence
unsigned d_fcch_start_pos; ///< position of the first sample of the fcch burst
float d_freq_offset_setting; ///< frequency offset set in frequency shifter located upstream
//@}
@@ -194,15 +195,19 @@ namespace gr {
* @param burst_binary - content of the burst
* @b_type - type of the burst
*/
- void send_burst(burst_counter burst_nr, const unsigned char * burst_binary, uint8_t burst_type, unsigned int input_nr);
+ void send_burst(burst_counter burst_nr, const unsigned char * burst_binary, uint8_t burst_type, size_t input_nr);
/**
* Configures burst types in different channels
*/
void configure_receiver();
-
-
+ /* State machine handlers */
+ void fcch_search_handler(gr_complex *input, int noutput_items);
+ void sch_search_handler(gr_complex *input, int noutput_items);
+ void synchronized_handler(gr_complex *input,
+ gr_vector_const_void_star &input_items, int noutput_items);
+
public:
receiver_impl(int osr, const std::vector<int> &cell_allocation, const std::vector<int> &tseq_nums, bool process_uplink);
~receiver_impl();
diff --git a/lib/receiver/sch.c b/lib/receiver/sch.c
deleted file mode 100644
index 3273237..0000000
--- a/lib/receiver/sch.c
+++ /dev/null
@@ -1,339 +0,0 @@
-#include <stdio.h>
-#include <stdlib.h>
-#include <unistd.h>
-#include <string.h>
-#include "gsm_constants.h"
-
-#define DEBUGF(a...) { \
- fprintf(stderr, "%s:%d ", __FILE__, __LINE__); \
- fprintf(stderr, a); \
-} while (0)
-
-
-
-/*
- * 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/lib/receiver/sch.h b/lib/receiver/sch.h
index 7b5a657..5eaf554 100644
--- a/lib/receiver/sch.h
+++ b/lib/receiver/sch.h
@@ -1,3 +1,24 @@
+/* -*- c++ -*- */
+/*
+ * @file
+ * @author (C) 2014 by Piotr Krysik <ptrkrysik@gmail.com>
+ * @section LICENSE
+ *
+ * Gr-gsm 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.
+ *
+ * Gr-gsm 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 gr-gsm; see the file COPYING. If not, write to
+ * the Free Software Foundation, Inc., 51 Franklin Street,
+ * Boston, MA 02110-1301, USA.
+ */
#ifndef __SCH_H__
#define __SCH_H__ 1
diff --git a/lib/receiver/viterbi_detector.cc b/lib/receiver/viterbi_detector.cc
index c3a54fe..6a64a2c 100644
--- a/lib/receiver/viterbi_detector.cc
+++ b/lib/receiver/viterbi_detector.cc
@@ -1,7 +1,7 @@
/* -*- c++ -*- */
/*
* @file
- * @author Piotr Krysik <ptrkrysik@gmail.com>
+ * @author (C) 2009 by Piotr Krysik <ptrkrysik@gmail.com>
* @section LICENSE
*
* Gr-gsm is free software; you can redistribute it and/or modify
@@ -57,7 +57,6 @@
#include <gnuradio/gr_complex.h>
#include <gsm_constants.h>
-#include <cmath>
#define PATHS_NUM (1 << (CHAN_IMP_RESP_LENGTH-1))
diff --git a/lib/receiver/viterbi_detector.h b/lib/receiver/viterbi_detector.h
index ef74cd0..a823003 100644
--- a/lib/receiver/viterbi_detector.h
+++ b/lib/receiver/viterbi_detector.h
@@ -1,7 +1,7 @@
/* -*- c++ -*- */
/*
* @file
- * @author Piotr Krysik <ptrkrysik@gmail.com>
+ * @author (C) 2009 Piotr Krysik <ptrkrysik@gmail.com>
* @section LICENSE
*
* Gr-gsm is free software; you can redistribute it and/or modify