blob: 12163c94b0c0396e47e8096ca151b9d32207fa8e [file] [log] [blame]
/* -*- c++ -*- */
/*
* @file
* @author Roman Khassraf <rkhassraf@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.
*/
#ifdef HAVE_CONFIG_H
#include "config.h"
#endif
#include <gnuradio/io_signature.h>
#include <grgsm/gsmtap.h>
#include "stdio.h"
#include "tch_f_decoder_impl.h"
#define DATA_BYTES 23
namespace gr {
namespace gsm {
tch_f_decoder::sptr
tch_f_decoder::make(tch_mode mode, const std::string &file)
{
return gnuradio::get_initial_sptr
(new tch_f_decoder_impl(mode, file));
}
/*
* Constructor
*/
tch_f_decoder_impl::tch_f_decoder_impl(tch_mode mode, const std::string &file)
: gr::block("tch_f_decoder",
gr::io_signature::make(0, 0, 0),
gr::io_signature::make(0, 0, 0)),
d_tch_mode(mode),
d_collected_bursts_num(0),
mBlockCoder(0x10004820009ULL, 40, 224),
mU(228),
mP(mU.segment(184,40)), mD(mU.head(184)), mDP(mU.head(224)),
mC(CONV_SIZE),
mClass1_c(mC.head(378)),
mClass2_c(mC.segment(378, 78)),
mTCHU(189),
mTCHD(260),
mClass1A_d(mTCHD.head(50))
{
d_speech_file = fopen( file.c_str(), "wb" );
if (d_speech_file == NULL)
{
throw std::runtime_error("TCH/F Decoder: can't open file\n");
}
if (d_tch_mode != TCH_FS)
{
fwrite(amr_nb_magic, 1, 6, d_speech_file);
}
int j, k, B;
for (k = 0; k < CONV_SIZE; k++)
{
B = k % 8;
j = 2 * ((49 * k) % 57) + ((k % 8) / 4);
interleave_trans[k] = B * 114 + j;
}
//setup input/output ports
message_port_register_in(pmt::mp("bursts"));
set_msg_handler(pmt::mp("bursts"), boost::bind(&tch_f_decoder_impl::decode, this, _1));
message_port_register_out(pmt::mp("msgs"));
}
tch_f_decoder_impl::~tch_f_decoder_impl()
{
}
void tch_f_decoder_impl::decode(pmt::pmt_t msg)
{
d_bursts[d_collected_bursts_num] = msg;
d_collected_bursts_num++;
bool stolen = false;
if (d_collected_bursts_num == 8)
{
d_collected_bursts_num = 0;
// reorganize data
for (int ii = 0; ii < 8; 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*114+jj] = burst_bits[jj + 3];
iBLOCK[ii*114+jj+57] = burst_bits[jj + 88]; //88 = 3+57+1+26+1
}
if ((ii <= 3 && static_cast<int>(burst_bits[87]) == 1) || (ii >= 4 && static_cast<int>(burst_bits[60]) == 1))
{
stolen = true;
}
}
// deinterleave
for (int k = 0; k < CONV_SIZE; k++)
{
mC[k] = iBLOCK[interleave_trans[k]];
}
// Decode stolen frames as FACCH/F
if (stolen)
{
mVR204Coder.decode(mC, mU);
mP.invert();
unsigned syndrome = mBlockCoder.syndrome(mDP);
if (syndrome == 0)
{
unsigned char outmsg[27];
unsigned char sbuf_len=224;
int i, j, c, pos=0;
for(i = 0; i < sbuf_len; i += 8) {
for(j = 0, c = 0; (j < 8) && (i + j < sbuf_len); j++){
c |= (!!mU.bit(i + j)) << j;
}
outmsg[pos++] = c & 0xff;
}
pmt::pmt_t first_header_plus_burst = pmt::cdr(d_bursts[0]);
gsmtap_hdr * header = (gsmtap_hdr *)pmt::blob_data(first_header_plus_burst);
header->type = GSMTAP_TYPE_UM;
int8_t * header_content = (int8_t *)header;
int8_t header_plus_data[sizeof(gsmtap_hdr)+DATA_BYTES];
memcpy(header_plus_data, header_content, sizeof(gsmtap_hdr));
memcpy(header_plus_data+sizeof(gsmtap_hdr), outmsg, DATA_BYTES);
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);
message_port_pub(pmt::mp("msgs"), msg_out);
}
}
mVR204Coder.decode(mClass1_c, mTCHU);
mClass2_c.sliced().copyToSegment(mTCHD, 182);
// 3.1.2.1
// copy class 1 bits u[] to d[]
for (unsigned k = 0; k <= 90; k++) {
mTCHD[2*k] = mTCHU[k];
mTCHD[2*k+1] = mTCHU[184-k];
}
Parity mTCHParity(0x0b, 3, 50);
// 3.1.2.1
// check parity of class 1A
unsigned sentParity = (~mTCHU.peekField(91, 3)) & 0x07;
unsigned calcParity = mClass1A_d.parity(mTCHParity) & 0x07;
bool good = (sentParity == calcParity);
if (good)
{
unsigned char mTCHFrame[33];
unsigned int mTCHFrameLength;
if (d_tch_mode == TCH_FS) // GSM-FR
{
// Undo Um's importance-sorted bit ordering.
// See GSM 05.03 3.1 and Tablee 2.
VocoderFrame mVFrame;
BitVector payload = mVFrame.payload();
mTCHD.unmap(GSM::g610BitOrder, 260, payload);
mVFrame.pack(mTCHFrame);
mTCHFrameLength = 33;
}
else if (d_tch_mode == TCH_EFR) // GSM-EFR / AMR 12.2
{
VocoderAMRFrame mVFrameAMR;
BitVector payload = mVFrameAMR.payload();
BitVector TCHW(260), EFRBits(244);
// Undo Um's EFR bit ordering.
mTCHD.unmap(GSM::g660BitOrder, 260, TCHW);
// Remove repeating bits and CRC to get raw EFR frame (244 bits)
for (unsigned k=0; k<71; k++)
EFRBits[k] = TCHW[k] & 1;
for (unsigned k=73; k<123; k++)
EFRBits[k-2] = TCHW[k] & 1;
for (unsigned k=125; k<178; k++)
EFRBits[k-4] = TCHW[k] & 1;
for (unsigned k=180; k<230; k++)
EFRBits[k-6] = TCHW[k] & 1;
for (unsigned k=232; k<252; k++)
EFRBits[k-8] = TCHW[k] & 1;
// Map bits as AMR 12.2k
EFRBits.map(GSM::g690_12_2_BitOrder, 244, payload);
// Put the whole frame (hdr + payload)
mVFrameAMR.pack(mTCHFrame);
mTCHFrameLength = 32;
}
fwrite(mTCHFrame, 1 , mTCHFrameLength, d_speech_file);
}
}
}
} /* namespace gsm */
} /* namespace gr */