mirror of
https://github.com/JvanKatwijk/qt-dab.git
synced 2025-10-06 00:02:40 +02:00
326 lines
9.1 KiB
C++
326 lines
9.1 KiB
C++
#
|
|
/*
|
|
* 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<int16_t> &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;
|
|
}
|