From 5968552b47f924342ffeee9309b658c2ebbc9c98 Mon Sep 17 00:00:00 2001 From: Vasil Velichkov Date: Sun, 6 May 2018 02:13:36 +0300 Subject: Add TCH/H decoder block with AMR multirate support Add new TCHH channel mode Add two new optional arguments -m CHAN_MODE, --mode=CHAN_MODE Channel mode. Valid options are 'BCCH' (Non-combined C0), 'BCCH_SDCCH4'(Combined C0), 'SDCCH8' (Stand-alone control channel) 'TCHF' (Traffic Channel, Full rate), 'TCHH' (Traffic Channel, Half rate) --sub-channel=TCH_H_CHANNEL TCH/H sub-channel. [default=0] --multi-rate=MULTI_RATE The MultiRrate configuration element from the Assigment Command message. Example: 28111a40. See 3GPP TS 44.018 - 10.5.2.21aa MultiRate configuration Example: grgsm_decode -m TCHH --sub-channel 0 --multi-rate 2811 -o voice.amr ... --- lib/decoding/CMakeLists.txt | 1 + lib/decoding/tch_h_decoder_impl.cc | 357 +++++++++++++++++++++++++++++++++++++ lib/decoding/tch_h_decoder_impl.h | 60 +++++++ 3 files changed, 418 insertions(+) create mode 100644 lib/decoding/tch_h_decoder_impl.cc create mode 100644 lib/decoding/tch_h_decoder_impl.h (limited to 'lib/decoding') diff --git a/lib/decoding/CMakeLists.txt b/lib/decoding/CMakeLists.txt index 154108a..423ab92 100644 --- a/lib/decoding/CMakeLists.txt +++ b/lib/decoding/CMakeLists.txt @@ -31,6 +31,7 @@ add_subdirectory(openbts) add_sources( control_channels_decoder_impl.cc tch_f_decoder_impl.cc + tch_h_decoder_impl.cc sch.c ) diff --git a/lib/decoding/tch_h_decoder_impl.cc b/lib/decoding/tch_h_decoder_impl.cc new file mode 100644 index 0000000..b7672e3 --- /dev/null +++ b/lib/decoding/tch_h_decoder_impl.cc @@ -0,0 +1,357 @@ +/* -*- c++ -*- */ +/* + * @file + * @author (C) 2018 by Vasil Velichkov + * @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. + */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include +#include "tch_h_decoder_impl.h" + +extern "C" { +#include "osmocom/gsm/protocol/gsm_04_08.h" +#include "osmocom/coding/gsm0503_coding.h" +} + +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_h_decoder::sptr + tch_h_decoder::make(unsigned int sub_channel, std::string multi_rate, bool boundary_check) + { + return gnuradio::get_initial_sptr + (new tch_h_decoder_impl(sub_channel, multi_rate, boundary_check)); + } + + /* + * Constructor + */ + tch_h_decoder_impl::tch_h_decoder_impl(unsigned int sub_channel, std::string multi_rate, bool boundary_check) + : gr::block("tch_h_decoder", + gr::io_signature::make(0, 0, 0), + gr::io_signature::make(0, 0, 0)), + d_collected_bursts_num(0), + d_tch_mode(TCH_HS), + d_sub_channel(sub_channel), + d_boundary_check(boundary_check), + d_boundary_decode(false), + d_header_sent(false), + d_ft(0), + d_cmr(0) + { + //setup input/output ports + message_port_register_in(pmt::mp("bursts")); + set_msg_handler(pmt::mp("bursts"), boost::bind(&tch_h_decoder_impl::decode, this, _1)); + message_port_register_out(pmt::mp("msgs")); + message_port_register_out(pmt::mp("voice")); + + if(multi_rate.length()) + { + std::cout<<"multi_rate configuration: "< binary; + for (std::string::const_iterator it = multi_rate.begin(); + it != multi_rate.end(); it += 2) + { + std::string byte(it, it + 2); + char* end = NULL; + errno = 0; + uint8_t b = strtoul(byte.c_str(), &end, 16); + if (errno != 0 || *end != '\0') + { + throw std::invalid_argument("Invalid multi_rate hexstring"); + } + binary.push_back(b); + } + + if (binary.size() < 2) { + throw std::invalid_argument("The multi_rate is too short"); + } + + //GSM A-I/F DTAP - Assignment Command + // Protocol Discriminator: Radio Resources Management messages (6) + // DTAP Radio Resources Management Message Type: Assignment Command (0x2e) + // Channel Description 2 - Description of the First Channel, after time + // Power Command + // Channel Mode - Mode of the First Channel(Channel Set 1) + // MultiRate configuration + // Element ID: 0x03 + // Length: 4 + // 001. .... = Multirate speech version: Adaptive Multirate speech version 1 (1) + // ...0 .... = NSCB: Noise Suppression Control Bit: Noise Suppression can be used (default) (0) + // .... 1... = ICMI: Initial Codec Mode Indicator: The initial codec mode is defined by the Start Mode field (1) + // .... ..00 = Start Mode: 0 + // 0... .... = 12,2 kbit/s codec rate: is not part of the subset + // .0.. .... = 10,2 kbit/s codec rate: is not part of the subset + // ..0. .... = 7,95 kbit/s codec rate: is not part of the subset + // ...1 .... = 7,40 kbit/s codec rate: is part of the subset + // .... 0... = 6,70 kbit/s codec rate: is not part of the subset + // .... .0.. = 5,90 kbit/s codec rate: is not part of the subset + // .... ..0. = 5,15 kbit/s codec rate: is not part of the subset + // .... ...1 = 4,75 kbit/s codec rate: is part of the subset + // ..01 1010 = AMR Threshold: 13.0 dB (26) + // 0100 .... = AMR Hysteresis: 2.0 dB (4) + + const uint8_t first = binary[0]; + uint8_t multirate_speech_ver = (first >> 5) & 0x07; + if (multirate_speech_ver == 1) + { + d_tch_mode = TCH_AFS4_75; + } + else if (multirate_speech_ver == 2) + { + throw std::invalid_argument("Adaptive Multirate speech version 2 is not supported"); + } + else + { + throw std::invalid_argument("Multirate speech version"); + } + + bool ncsb = (first >> 4) & 0x01; + bool icmi = (first >> 3) & 0x01; + uint8_t start = first & 0x03; + + const uint8_t codecs = binary[1]; + for (int i = 0; i < 8; i++) + { + if ((codecs >> i) & 1) + { + d_multi_rate_codes.push_back(i); + } + } + + std::cout<<"Enabled AMR Codecs:"<::const_iterator it = d_multi_rate_codes.begin(); + it != d_multi_rate_codes.end(); + it ++) + { + switch(*it) + { + case 0: + std::cout<<"4,75 kbit/s codec rate: is part of the subset"< 4) { + throw std::invalid_argument("More then 4 multirate codes"); + } + } + } + + tch_h_decoder_impl::~tch_h_decoder_impl() + { + } + + void tch_h_decoder_impl::decode(pmt::pmt_t msg) + { + d_bursts[d_collected_bursts_num++] = msg; + if (d_collected_bursts_num <= 7) + { + return; + } + + gsmtap_hdr* header = (gsmtap_hdr*)(pmt::blob_data(pmt::cdr(msg))); + uint32_t frame_nr = be32toh(header->frame_number); + bool uplink_burst = (be16toh(header->arfcn) & 0x4000) ? true : false; + + //TODO: Check in 3gpp specs which frames could contains facch/h frames + //and replace this ugly formula with table + int fn_is_odd = (((frame_nr - (uplink_burst ? 10 : 15)) % 26) >> 2) & 1; + + ubit_t bursts_u[116 * 6] = {0}; //facch/h is 6 bursts + + //reorganize data + for (int ii = 0; ii < 8; ii++) + { + //skip the 4th and 5th bursts + if (ii == 4 || ii == 5) continue; + + int8_t* burst_bits = (int8_t*)(pmt::blob_data(pmt::cdr(d_bursts[ii])))+sizeof(gsmtap_hdr); + + //copy 6th and 7th burst to 4th and 5th position + int n = ii < 6 ? ii : ii - 2; + + memcpy(&bursts_u[n*116], &burst_bits[3],58); + memcpy(&bursts_u[n*116+58], &burst_bits[3+57+1+26],58); + } + + //Convert to sbits + sbit_t bursts_s[116 * 6] = {0}; + ubits2sbits(bursts_u, bursts_s, 116 * 6); + + //Prepare burst for the next iteration by shifting them by 4 + for (int ii = 0; ii < 4; ii++) { + d_bursts[ii] = d_bursts[ii + 4]; + } + d_collected_bursts_num = 4; + + uint8_t frameBuffer[64]; + int frameLength = -1; + int n_errors, n_bits_total; + + if (d_tch_mode == TCH_HS) + { + frameLength = gsm0503_tch_hr_decode(frameBuffer, bursts_s, fn_is_odd, &n_errors, &n_bits_total); + } + else + { + frameLength = gsm0503_tch_ahs_decode(frameBuffer, bursts_s, fn_is_odd, + fn_is_odd, //int codec_mode_req, + &d_multi_rate_codes.front(), d_multi_rate_codes.size(), + &d_ft, + &d_cmr, + &n_errors, &n_bits_total); + } + + if (frameLength < 12) + { + if (!d_boundary_check || d_boundary_decode) { + std::cerr<<"Error! frame_nr:"<type = GSMTAP_TYPE_UM; + + pmt::pmt_t msg_binary_blob = pmt::make_blob(header_plus_data, frameLength + sizeof(gsmtap_hdr)); + pmt::pmt_t msg_out = pmt::cons(pmt::PMT_NIL, msg_binary_blob); + + message_port_pub(pmt::mp("msgs"), msg_out); + + // if d_boundary_check is enabled, we set d_boundary_decode to true, when a + // "Connect" or "Connect Acknowledge" message is received, and + // we set d_boundary_decode back to false, when "Release" message is received + if (d_boundary_check) + { + // check if this is a call control message + if ((frameBuffer[3] & 0x0f) == 0x03) + { + // Connect specified in GSM 04.08, 9.3.5 + if ((frameBuffer[4] & 0x3f) == 0x07) + { + d_boundary_decode = true; + } + // Connect Acknowledge specified in GSM 04.08, 9.3.6 + else if ((frameBuffer[4] & 0x3f) == 0x0f) + { + d_boundary_decode = true; + } + // Release specified in GSM 04.08, 9.3.18 + else if ((frameBuffer[4] & 0x3f) == 0x2d) + { + d_boundary_decode = false; + } + } + } + return; + } + + if (!d_header_sent && d_tch_mode != TCH_HS) + { + const unsigned char amr_nb_magic[7] = "#!AMR\n"; + message_port_pub(pmt::mp("voice"), pmt::cons(pmt::PMT_NIL, pmt::make_blob(amr_nb_magic, 6))); + d_header_sent = true; + } + + if (!n_errors && (!d_boundary_check || d_boundary_decode)) + { + //std::cerr<<"Voice frame_nr:"< + * @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 INCLUDED_GSM_TCH_H_DECODER_IMPL_H +#define INCLUDED_GSM_TCH_H_DECODER_IMPL_H + +#include +#include "tch_f_decoder_impl.h" + +namespace gr { + namespace gsm { + + class tch_h_decoder_impl : public tch_h_decoder + { + private: + unsigned int d_collected_bursts_num; + pmt::pmt_t d_bursts[8]; + + enum tch_mode d_tch_mode; + unsigned int d_sub_channel; + + std::vector d_multi_rate_codes; + + bool d_boundary_check; + bool d_boundary_decode; + bool d_header_sent; + + uint8_t d_ft; + uint8_t d_cmr; + + void decode(pmt::pmt_t msg); + public: + tch_h_decoder_impl(unsigned int sub_channel, std::string multi_rate, bool boundary_check=false); + ~tch_h_decoder_impl(); + }; + + } // namespace gsm +} // namespace gr + +#endif /* INCLUDED_GSM_TCH_H_DECODER_IMPL_H */ + -- cgit v1.2.3