1
0
mirror of https://github.com/JvanKatwijk/qt-dab.git synced 2025-10-06 00:02:40 +02:00
Files
SDR-DAB_Qt-DAB/sources/backend/msc-handler.cpp
2025-09-08 13:38:08 +02:00

291 lines
9.3 KiB
C++

#
/*
* Copyright (C) 2014 .. 2025
* Jan van Katwijk (J.vanKatwijk@gmail.com)
* Lazy Chair Computing
*
* This file is part of the Qt-DAB program
*
* 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 "dab-constants.h"
#include "radio.h"
#include "msc-handler.h"
#include "ofdm-decoder.h"
#include "backend.h"
#include "dab-params.h"
#include "logger.h"
//
// Interface program for processing the MSC.
// The dabProcessor assumes the existence of an msc-handler, whether
// a service is selected or not.
//
// For "slower" computers (i.e. an RPI 3), the ofdm decoding is
// split up, the FIC blocks are handled in the thread of the
// ofdmHandler, the "payload" blocks in a separate thread here
#define CUSize (4 * 16)
static int cifTable [] = {18, 72, 0, 36};
// Note CIF counts from 0 .. 3
//
mscHandler::mscHandler (RadioInterface *mr,
uint8_t dabMode,
RingBuffer<uint8_t> *frameBuffer_i,
logger *theLogger,
uint8_t cpuSupport):
params (dabMode),
myMapper (dabMode),
myRadioInterface (mr),
frameBuffer (frameBuffer_i)
#ifdef __MSC_THREAD__
,fft (params. get_T_u (), false)
,bufferSpace (params. get_L())
#endif
{
this -> theLogger = theLogger;
this -> cpuSupport = cpuSupport;
cifVector. resize (55296);
BitsperBlock = 2 * params. get_carriers();
softBits. resize (BitsperBlock);
nrBlocks = params. get_L();
connect (this, &mscHandler::nrServices,
mr, &RadioInterface::nrActiveServices);
numberofblocksperCIF = cifTable [(dabMode - 1) & 03];
#ifdef __MSC_THREAD__
phaseReference .resize (params. get_T_u());
command. resize (nrBlocks);
for (int i = 0; i < nrBlocks; i ++)
command [i]. resize (params. get_T_u());
amount = 0;
running. store (false);
start ();
#endif
}
mscHandler::~mscHandler () {
#ifdef __MSC_THREAD__
running. store (false);
while (isRunning())
usleep (100);
#endif
locker. lock();
for (auto &b : theBackends) {
b -> stopRunning();
delete b;
}
theBackends. resize (0);
locker. unlock();
}
//
// Input is put into a buffer, a the code in a separate thread
// will handle the data from the buffer
void mscHandler::processBlock_0 (Complex *b) {
#ifdef __MSC_THREAD__
bufferSpace. acquire (1);
memcpy (command [0]. data(), b,
params. get_T_u() * sizeof (Complex));
helper. lock();
amount ++;
commandHandler. wakeOne();
helper. unlock();
#else
(void)b;
fprintf (stderr, "Why am I called?\n");
#endif
}
#ifdef __MSC_THREAD__
void mscHandler::processMsc (std::vector<Complex> &b,
int offset, int blkno) {
bufferSpace. acquire (1);
memcpy (command [blkno]. data (), &(b. data ())[offset],
params. get_T_u() * sizeof (Complex));
helper. lock();
amount ++;
commandHandler. wakeOne();
helper. unlock();
}
void mscHandler::run () {
int currentBlock = 0;
Complex fft_buffer [params. get_T_u ()];
Complex conjVector [params. get_T_u ()];
int carriers = params. get_carriers ();
if (running. load ()) {
fprintf (stderr, "already running\n");
return;
}
running. store (true);
while (running. load()) {
helper. lock();
commandHandler. wait (&helper, 100);
helper. unlock();
while ((amount > 0) && running. load()) {
memcpy (fft_buffer, command [currentBlock]. data(),
params. get_T_u() * sizeof (Complex));
//
// block 3 and up are needed as basis for demodulation the "mext" block
// "our" msc blocks start with blkno 4
fft. fft (fft_buffer);
if (currentBlock >= 4) {
for (int i = 0; i < carriers; i ++) {
int16_t index = myMapper. mapIn (i);
if (index < 0)
index += params. get_T_u();
Complex r1 = fft_buffer [index] *
conj (phaseReference [index]);
conjVector [index] = r1;
float ab1 = jan_abs (r1);
softBbits [i] = (int16_t) (- real (r1) * MAX_VITERBI / ab1);
softBits [carriers + i] =
(int16_t) (- imag (r1) * MAX_VITERBI / ab1);
}
processMscBlock (softBits, currentBlock);
}
memcpy (phaseReference. data (), fft_buffer,
params. get_T_u() * sizeof (Complex));
bufferSpace. release (1);
helper. lock();
currentBlock = (currentBlock + 1) % (nrBlocks);
amount -= 1;
helper. unlock();
}
}
}
#else
void mscHandler::processMsc (std::vector<Complex> &b,
int offset, int blkno) {
(void)b;
(void)offset;
(void)blkno;
fprintf (stderr, "I should not be called\n");
}
#endif
//
// Note, the set_Channel function is called from within a
// different thread than the process_mscBlock method is,
// so, a little bit of locking seems wise while
// the actual changing of the settings is done in the
// thread executing process_mscBlock
void mscHandler::resetBuffers () {
resetChannel ();
#ifdef __MSC_THREAD__
running. store (false);
while (isRunning ())
wait (100);
bufferSpace. release (params. get_L () - bufferSpace. available ());
start ();
#endif
}
void mscHandler::resetChannel () {
// fprintf (stderr, "channel reset: all services will be stopped\n");
locker. lock ();
for (auto &b : theBackends) {
b -> stopRunning();
delete b;
}
theBackends. resize (0);
locker. unlock ();
nrServices ((int)(theBackends. size ()));
}
void mscHandler::stopBackend (const QString &serviceName,
int subchId, int flag) {
locker. lock ();
for (int i = 0; i < (int)(theBackends. size ()); i ++) {
Backend *b = theBackends. at (i);
if ((b -> serviceName == serviceName) &&
(b -> subChId == subchId) && (b -> borf == flag)) {
b -> stopRunning ();
delete b;
theBackends. erase (theBackends. begin () + i);
break;
}
}
locker. unlock ();
nrServices ((int)(theBackends. size ()));
}
//
// Note that - in general - the backens run in their own thread
bool mscHandler::startBackend (descriptorType &d,
RingBuffer<std::complex<int16_t>> *audioBuffer,
RingBuffer<uint8_t> *dataBuffer,
FILE *dump, int flag) {
// fprintf (stderr, "going to open %s\n",
// d. serviceName. toLatin1 (). data ());
theBackends. push_back (new Backend (myRadioInterface,
theLogger,
&d,
audioBuffer,
dataBuffer,
frameBuffer,
dump,
flag,
cpuSupport));
nrServices ((int)(theBackends. size ()));
return true;
}
bool mscHandler::serviceRuns (uint32_t SId, uint16_t subChId) {
for (auto &backend : theBackends)
if ((backend -> serviceId == (int) SId) && (backend -> subChId == subChId))
return true;
return false;
}
//
// add blocks. First is (should be) block 4, last is (should be)
// nrBlocks -1.
// Note that this method is called from within the ofdm-processor thread
// while the set_xxx methods are called from within the
// gui thread, so some locking is added
//
void mscHandler::processMscBlock (std::vector<int16_t> &softBits,
int16_t blkno) {
int16_t currentblk = (blkno - 4) % numberofblocksperCIF;
// and the normal operation is:
memcpy (&cifVector [currentblk * BitsperBlock],
softBits. data(), BitsperBlock * sizeof (int16_t));
if (currentblk < numberofblocksperCIF - 1)
return;
// OK, now we have a full CIF and it seems there is some work to
// be done. We assume that the backend itself
// does the work in a separate thread.
locker. lock ();
for (auto & b: theBackends) {
int16_t startAddr = b -> startAddr;
int16_t Length = b -> Length;
if (Length > 0) // Length = 0? should not happen
(void) b -> process (&cifVector [startAddr * CUSize],
Length * CUSize);
}
locker. unlock();
}