# /* * Copyright (C) 2016 .. 2024 * Jan van Katwijk (J.vanKatwijk@gmail.com) * Lazy Chair Computing * * This file is part of Qt-DAB * * Qt-DAB 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. * * Qt-DAB 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 Qt-DAB; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #include "radio.h" #include "fic-handler.h" #include "crc-handlers.h" #include "protTables.h" #include "dab-params.h" // // The 3072 bits of the serial motherword shall be split into // 24 blocks of 128 bits each. // The first 21 blocks shall be subjected to // puncturing (per 32 bits) according to PI_16 // The next three blocks shall be subjected to // puncturing (per 32 bits) according to PI_15 // The last 24 bits shall be subjected to puncturing // according to the table 8 #define FIC_BLOCKSIZE 3072 #define FIC_RESIDU 24 #define FIC_INPUT 2304 /** * \class ficHandler * We get in - through process_ficBlock - the FIC data * in units of 768 bits (i.e. FIC_BLOCKSIZE / 4) * We follow the standard and apply convolution decoding and * puncturing. * The data is sent through to the fib processor */ ficHandler::ficHandler (RadioInterface *mr, uint8_t dabMode, uint8_t cpuSupport): fibDecoder (mr), params (dabMode), myViterbi (768, true, cpuSupport) { int16_t shiftRegister [9] = {1, 1, 1, 1, 1, 1, 1, 1, 1}; BitsperBlock = 2 * params. get_carriers(); index = 0; ficno = 0; ficBlocks = 0; starter = 0; ficErrors = 0; ficBits = 0; for (int i = 0; i < FIC_BLOCKSIZE / 4; i ++) { PRBS [i] = shiftRegister [8] ^ shiftRegister [4]; for (int j = 8; j > 0; j --) shiftRegister [j] = shiftRegister [j - 1]; shiftRegister [0] = PRBS [i]; } // // Since the depuncturing is the same throughout all calls // (even through all instances, so we could create a static // table), we make an punctureTable that contains the indices of // the ofdmInput table memset (punctureTable, (uint8_t)false, (FIC_BLOCKSIZE + FIC_RESIDU) * sizeof (uint8_t)); int local = 0; for (int i = 0; i < 21; i ++) { for (int k = 0; k < 32 * 4; k ++) { if (get_PCodes (16 - 1) [k % 32] != 0) punctureTable [local] = true; local ++; } } /** * In the second step * we have 3 blocks with puncturing according to PI_15 * each 128 bit block contains 4 subblocks of 32 bits * on which the given puncturing is applied */ for (int i = 0; i < 3; i ++) { for (int k = 0; k < 32 * 4; k ++) { if (get_PCodes (15 - 1) [k % 32] != 0) punctureTable [local] = true; local ++; } } /** * we have a final block of 24 bits with puncturing according to PI_X * This block constitues the 6 * 4 bits of the register itself. */ for (int k = 0; k < 24; k ++) { if (get_PCodes (8 - 1) [k] != 0) punctureTable [local] = true; local ++; } connect (this, &ficHandler::showFICQuality, mr, &RadioInterface::show_ficQuality); connect (this, &ficHandler::showFICBER, mr, &RadioInterface::show_ficBER); ficPointer = 0; ficDumpPointer = nullptr; fibCounter = 1; successRatio = 0; } ficHandler::~ficHandler () { } /** * \brief process_ficBlock * The number of bits to be processed per incoming block * is 2 * p -> K, which still depends on the Mode. * for Mode I it is 2 * 1536, for Mode II, it is 2 * 384, * for Mode III it is 192, Mode IV gives 2 * 768. * for Mode II we will get the 2304 bits after having read * the 3 FIC blocks, each with 768 bits. * for Mode IV we will get 3 * 2 * 768 = 4608, i.e. two resulting blocks * Note that Mode III is NOT supported * * The function is called with a blkno. This should be 1, 2 or 3 * for each time 2304 bits are in, we call process_ficInput */ // // pre data. size () >= BitsperBlock void ficHandler::processFICBlock (std::vector &data, int16_t blkno) { if (blkno == 1) { index = 0; ficno = 0; } if (starter == 0) { if (blkno != 1) { return; } } if (starter < 6) { starter ++; return; } if ((1 <= blkno) && (blkno <= 3)) { for (int i = 0; i < BitsperBlock; i ++) { ofdm_input [index ++] = data [i]; if (index >= FIC_INPUT) { processFICInput (ficno, &ficValid [ficno]); index = 0; ficno ++; } } } else fprintf (stderr, "You should not call ficBlock here\n"); // we are pretty sure now that after block 4, we end up // with index = 0 } /** * \brief process_ficInput * we have a vector of 2304 (0 .. 2303) soft bits that has * to be de-punctured and de-conv-ed into a block of 768 bits * In this approach we first create the full 3072 block (i.e. * we first depuncture, and then we apply the deconvolution * In the next coding step, we will combine this function with the * one above */ void ficHandler::processFICInput (int16_t ficno, bool *valid) { static int16_t viterbiBlock [FIC_BLOCKSIZE + FIC_RESIDU] = {0}; static uint8_t checkBlock [FIC_BLOCKSIZE + FIC_RESIDU] = {0}; int16_t inputCount = 0; if (!running. load ()) return; memset (viterbiBlock, 0, (FIC_BLOCKSIZE + FIC_RESIDU) * sizeof (int16_t)); for (int i = 0; i < FIC_BLOCKSIZE + FIC_RESIDU; i ++) if (punctureTable [i]) viterbiBlock [i] = ofdm_input [inputCount ++]; /** * Now we have the full word ready for deconvolution * deconvolution is according to DAB standard section 11.2 */ myViterbi. deconvolve (viterbiBlock, bitBuffer_out); // // we reconstruct the input as it should have been: myViterbi. convolve (bitBuffer_out, checkBlock, FIC_BLOCKSIZE / 4); // // and compute the errors for (int i = 0; i < 3072 + 24; i ++) { if (punctureTable [i]) { if ((checkBlock [i] == 0) && viterbiBlock [i] >= 0) ficErrors ++; else if ((checkBlock [i] != 0) && viterbiBlock [i] < 0) ficErrors ++; } } ficBits += FIC_BLOCKSIZE + FIC_RESIDU; ficBlocks ++; if (ficBlocks >= 40) { // 4 blocks per frame, app 10 frames per sec emit showFICBER ((float)ficErrors / ficBits); ficBlocks = 0; ficErrors /= 2; ficBits /= 2; } /** * if everything worked as planned, we now have a * 768 bit vector containing three FIB's * * first step: energy dispersal according to the DAB standard * We use a predefined vector PRBS */ for (int i = 0; i < FIC_BLOCKSIZE / 4; i ++) bitBuffer_out [i] ^= PRBS [i]; for (int i = 0; i < FIC_BLOCKSIZE / 4; i ++) fibBits [ficno * FIC_BLOCKSIZE / 4 + i] = bitBuffer_out [i]; /** * each of the fib blocks is protected by a crc * (we know that there are three fib blocks each time we are here) * we keep track of the successrate * and show that per 100 fic blocks * One issue is what to do when we really believe the synchronization * was lost. */ #define RANGE 50 // default *valid = true; // default, can be changed for (int i = ficno * 3; i < ficno * 3 + 3; i ++) { uint8_t *p = &bitBuffer_out [(i % 3) * 256]; fibCounter ++; if (fibCounter >= RANGE) fibCounter = 0; if (!check_CRC_bits (p, 256)) { *valid = false; if (successRatio > 0) successRatio --; if (fibCounter == 0) showFICQuality (successRatio, 100 / 50); continue; } for (int j = 0; j < 32; j ++) { ficBuffer [j] = 0; for (int k = 0; k < 8; k ++) { ficBuffer [j] <<= 1; ficBuffer [j] &= 0xFE; ficBuffer [j] |= p [8 * j + k] ? 1 : 0; } } ficLocker. lock (); if (ficDumpPointer != nullptr) fwrite (ficBuffer, 1, 32, ficDumpPointer); ficLocker. unlock (); if (successRatio < RANGE) successRatio ++; if (fibCounter == 0) showFICQuality (successRatio, 100 / RANGE); fibDecoder::processFIB (p, ficno); } } void ficHandler::stop () { disconnectChannel (); // clearEnsemble (); running. store (false); } void ficHandler::restart () { // clearEnsemble (); index = 0; ficno = 0; ficBlocks = 0; ficErrors = 0; ficBits = 0; starter = 0; connectChannel (); running. store (true); } void ficHandler::startFICDump (FILE *f) { if (ficDumpPointer != nullptr) return; ficDumpPointer = f; } void ficHandler::stopFICDump () { ficLocker. lock (); ficDumpPointer = nullptr; ficLocker. unlock (); } void ficHandler::getFIBBits (uint8_t *v, bool *b) { for (int i = 0; i < 4 * 768; i ++) v [i] = fibBits [i]; for (int i = 0; i < 4; i ++) b [i] = ficValid [i]; } int ficHandler::getFICQuality () { return successRatio * 100 / RANGE; }