Implementation of TCH/F decoder supporting GSM-FR and GSM-EFR. Issue #50
diff --git a/lib/decoding/tch_f_decoder_impl.cc b/lib/decoding/tch_f_decoder_impl.cc
new file mode 100644
index 0000000..75d2402
--- /dev/null
+++ b/lib/decoding/tch_f_decoder_impl.cc
@@ -0,0 +1,242 @@
+/* -*- c++ -*- */
+/*
+ * @file
+ * @author 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.
+ */
+
+#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)
+    {
+        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");
+        }
+
+        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)
+        {
+            unsigned char iBLOCK[2*BLOCKS*iBLOCK_SIZE];
+            SoftVector mC(CONV_SIZE);
+            SoftVector mClass1_c(mC.head(378));
+            SoftVector mClass2_c(mC.segment(378, 78));
+            BitVector mTCHU(189);
+            BitVector mTCHD(260);
+            BitVector mClass1A_d(mTCHD.head(50));
+            ViterbiR2O4 mVCoder;
+
+            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)
+            {
+                BitVector mU(228);
+                BitVector mP(mU.segment(184,40));
+                BitVector mD(mU.head(184));
+                BitVector mDP(mU.head(224));
+                Parity mBlockCoder(0x10004820009ULL, 40, 224);
+
+                mC.decode(mVCoder, 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);
+                }
+            }
+
+            mClass1_c.decode(mVCoder, 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 = mTCHD.head(50).parity(mTCHParity) & 0x07;
+            unsigned calcParity = mClass1A_d.parity(mTCHParity) & 0x07;
+
+            // 3.1.2.2
+            // Check the tail bits, too.
+            unsigned tail = mTCHU.peekField(185, 4);
+
+            bool good = (sentParity == calcParity) && (tail == 0);
+
+            if (good)
+            {
+                unsigned char mTCHFrame[33];
+                unsigned int  mTCHFrameLength;
+
+                if (d_tch_mode == MODE_SPEECH_FR) // 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 == MODE_SPEECH_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 */
+