/* -*- 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) { // Alerting if ((frameBuffer[4] & 0x3f) == 0x01) { if ((frameBuffer[5] == 0x1e) && //element id (frameBuffer[6] == 2) && //length ((frameBuffer[8] & 0x7f) == 0x08)) { //.000 1000 = Progress description: In-band information or appropriate pattern now available (8) d_boundary_decode = true; } } // Progress else if ((frameBuffer[4] & 0x3f) == 0x03) { if ((frameBuffer[5] == 2) && //length (frameBuffer[7] & 0x7f) == 0x08) { //.000 1000 = Progress description: In-band information or appropriate pattern now available (8) d_boundary_decode = true; } } // Connect specified in GSM 04.08, 9.3.5 else 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:"<