mirror of
https://github.com/JvanKatwijk/dabradio
synced 2025-10-05 15:52:42 +02:00
395 lines
12 KiB
C++
395 lines
12 KiB
C++
#
|
|
/*
|
|
* Copyright (C) 2014 .. 2017
|
|
* Jan van Katwijk (J.vanKatwijk@gmail.com)
|
|
* Lazy Chair Computing
|
|
*
|
|
* This file is part of the dabradio program
|
|
* dabradio 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.
|
|
*
|
|
* dabradio 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 dabradio if not, write to the Free Software
|
|
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
|
*/
|
|
#include "dab-processor.h"
|
|
#include "fic-handler.h"
|
|
#include "msc-handler.h"
|
|
#include "radio.h"
|
|
#include "dab-params.h"
|
|
#include "timesyncer.h"
|
|
//
|
|
/**
|
|
* \brief dabProcessor
|
|
* The dabProcessor class is the driver of the processing
|
|
* of the samplestream.
|
|
* It is the main interface to the qt-dab program,
|
|
* local are classes ofdmDecoder, ficHandler and mschandler.
|
|
*/
|
|
|
|
#define C_LEVEL_SIZE 50
|
|
dabProcessor::dabProcessor (RadioInterface *mr,
|
|
deviceHandler *theDevice,
|
|
uint8_t dabMode,
|
|
int16_t threshold,
|
|
int16_t diff_length,
|
|
QString picturesPath):
|
|
params (dabMode),
|
|
myReader (mr,
|
|
theDevice,
|
|
nullptr),
|
|
my_ficHandler (mr, dabMode),
|
|
my_mscHandler (mr, dabMode,
|
|
picturesPath),
|
|
phaseSynchronizer (mr,
|
|
dabMode,
|
|
threshold,
|
|
diff_length,
|
|
nullptr),
|
|
my_ofdmDecoder (mr,
|
|
dabMode,
|
|
theDevice -> bitDepth (),
|
|
nullptr) {
|
|
int32_t i;
|
|
|
|
this -> myRadioInterface = mr;
|
|
this -> theDevice = theDevice;
|
|
this -> T_null = params. get_T_null ();
|
|
this -> T_s = params. get_T_s ();
|
|
this -> T_u = params. get_T_u ();
|
|
this -> T_F = params. get_T_F ();
|
|
this -> nrBlocks = params. get_L ();
|
|
this -> carriers = params. get_carriers ();
|
|
this -> carrierDiff = params. get_carrierDiff ();
|
|
this -> scanMode = false;
|
|
|
|
ofdmBuffer. resize (2 * T_s);
|
|
ofdmBufferIndex = 0;
|
|
ofdmSymbolCount = 0;
|
|
tokenCount = 0;
|
|
fineOffset = 0;
|
|
correctionNeeded = true;
|
|
attempts = 0;
|
|
myReader. setRunning (false);
|
|
}
|
|
|
|
dabProcessor::~dabProcessor (void) {
|
|
if (isRunning ()) {
|
|
myReader. setRunning (false);
|
|
// exception to be raised
|
|
// through the getSample(s) functions.
|
|
msleep (100);
|
|
while (isRunning ()) {
|
|
usleep (1000);
|
|
}
|
|
}
|
|
}
|
|
|
|
void dabProcessor::start (int frequency, bool scanMode) {
|
|
this -> frequency = frequency;
|
|
this -> scanMode = scanMode;
|
|
startFailures = 0;
|
|
this -> QThread::start ();
|
|
}
|
|
|
|
/***
|
|
* \brief run
|
|
* The main thread, reading samples,
|
|
* time synchronization and frequency synchronization
|
|
* Identifying blocks in the DAB frame
|
|
* and sending them to the ofdmDecoder who will transfer the results
|
|
* Finally, estimating the small freqency error
|
|
*/
|
|
void dabProcessor::run (void) {
|
|
int32_t startIndex;
|
|
int32_t i;
|
|
int attempts;
|
|
|
|
std::complex<float> FreqCorr;
|
|
timeSyncer myTimeSyncer (&myReader);
|
|
|
|
fineOffset = 0;
|
|
correctionNeeded = true;
|
|
attempts = 0;
|
|
theDevice -> resetBuffer ();
|
|
theDevice -> restartReader (frequency);
|
|
coarseOffset = 0;
|
|
fineOffset = 0;
|
|
myReader. setRunning (true);
|
|
my_mscHandler. start ();
|
|
//
|
|
// to get some idea of the signal strength
|
|
try {
|
|
for (i = 0; i < T_F / 5; i ++) {
|
|
myReader. getSample (0);
|
|
}
|
|
//Initing:
|
|
notSynced:
|
|
setSynced (false);
|
|
switch (myTimeSyncer. sync (T_null, T_F)) {
|
|
case TIMESYNC_ESTABLISHED:
|
|
break; // yes, we are ready
|
|
|
|
case NO_DIP_FOUND:
|
|
if (scanMode && (++ attempts >= 5)) {
|
|
emit (No_Signal_Found ());
|
|
attempts = 0;
|
|
}
|
|
goto notSynced;
|
|
|
|
default: // does not happen
|
|
case NO_END_OF_DIP_FOUND:
|
|
goto notSynced;
|
|
}
|
|
|
|
/**
|
|
* The end of the null period is identified, the actual end
|
|
* is probably about 40 samples earlier.
|
|
*/
|
|
SyncOnPhase:
|
|
/**
|
|
* We now have to find the exact first sample of the non-null period.
|
|
* We use a correlation that will find the first sample after the
|
|
* cyclic prefix.
|
|
* When in "sync", i.e. pretty sure that we know were we are,
|
|
* we skip the "dip" identification and come here right away.
|
|
*
|
|
* now read in Tu samples. The precise number is not really important
|
|
* as long as we can be sure that the first sample to be identified
|
|
* is part of the samples read.
|
|
*/
|
|
myReader. getSamples (ofdmBuffer. data (),
|
|
T_u, coarseOffset + fineOffset);
|
|
//
|
|
// and then, call upon the phase synchronizer to verify/compute
|
|
// the real "first" sample
|
|
startIndex = phaseSynchronizer. findIndex (ofdmBuffer);
|
|
if (startIndex < 0) { // no sync, try again
|
|
if (!correctionNeeded) {
|
|
setSyncLost ();
|
|
}
|
|
startFailures ++;
|
|
if (scanMode && (startFailures > 3)) {
|
|
emit (No_Signal_Found ());
|
|
startFailures = 0;
|
|
}
|
|
goto notSynced;
|
|
}
|
|
|
|
startFailures = 0;
|
|
/**
|
|
* Once here, we are synchronized, we need to copy the data we
|
|
* used for synchronization for block 0
|
|
*/
|
|
memmove (ofdmBuffer. data (),
|
|
&((ofdmBuffer. data ()) [startIndex]),
|
|
(T_u - startIndex) * sizeof (std::complex<float>));
|
|
ofdmBufferIndex = T_u - startIndex;
|
|
|
|
Block_0:
|
|
/**
|
|
* Block 0 is special in that it is used for fine time synchronization,
|
|
* for coarse frequency synchronization
|
|
* and its content is used as a reference for decoding the
|
|
* first datablock.
|
|
* We read the missing samples in the ofdm buffer
|
|
*/
|
|
setSynced (true);
|
|
myReader. getSamples (&((ofdmBuffer. data ()) [ofdmBufferIndex]),
|
|
T_u - ofdmBufferIndex,
|
|
coarseOffset + fineOffset);
|
|
my_ofdmDecoder. processBlock_0 (ofdmBuffer);
|
|
|
|
// Here we look only at the block_0 when we need a coarse
|
|
// frequency synchronization.
|
|
correctionNeeded = !my_ficHandler. syncReached ();
|
|
if (correctionNeeded) {
|
|
int correction =
|
|
phaseSynchronizer. estimate_CarrierOffset (ofdmBuffer);
|
|
if (correction != 100) {
|
|
coarseOffset += correction * carrierDiff;
|
|
if (abs (coarseOffset) > Khz (35))
|
|
coarseOffset = 0;
|
|
}
|
|
}
|
|
/**
|
|
* after block 0, we will just read in the other (params -> L - 1) blocks
|
|
*/
|
|
Data_blocks:
|
|
/**
|
|
* The first ones are the FIC blocks. We immediately
|
|
* start with building up an average of the phase difference
|
|
* between the samples in the cyclic prefix and the
|
|
* corresponding samples in the datapart.
|
|
*/
|
|
FreqCorr = std::complex<float> (0, 0);
|
|
for (int ofdmSymbolCount = 1;
|
|
ofdmSymbolCount < nrBlocks; ofdmSymbolCount ++) {
|
|
std::vector<int16_t> ibits;
|
|
ibits. resize (2 * params. get_carriers ());
|
|
myReader. getSamples (ofdmBuffer. data (),
|
|
T_s, coarseOffset + fineOffset);
|
|
for (i = (int)T_u; i < (int)T_s; i ++)
|
|
FreqCorr += ofdmBuffer [i] * conj (ofdmBuffer [i - T_u]);
|
|
|
|
if (ofdmSymbolCount < 4) {
|
|
my_ofdmDecoder. decode (ofdmBuffer,
|
|
ofdmSymbolCount, ibits. data ());
|
|
my_ficHandler. process_ficBlock (ibits, ofdmSymbolCount);
|
|
}
|
|
my_mscHandler. process_Msc (&((ofdmBuffer. data ()) [T_g]),
|
|
ofdmSymbolCount);
|
|
}
|
|
|
|
NewOffset:
|
|
// we integrate the newly found frequency error with the
|
|
// existing frequency error.
|
|
fineOffset += 0.1 * arg (FreqCorr) / (2 * M_PI) * carrierDiff;
|
|
//
|
|
/**
|
|
* OK, here we are at the end of the frame
|
|
* Assume everything went well and skip T_null samples
|
|
*/
|
|
myReader. getSamples (ofdmBuffer. data (),
|
|
T_null, coarseOffset);
|
|
|
|
float sum = 0;
|
|
for (i = 0; i < T_null; i ++)
|
|
sum += abs (ofdmBuffer [i]);
|
|
sum /= T_null;
|
|
|
|
static float snr = 0;
|
|
snr = 0.9 * snr +
|
|
0.1 * 20 * log10 ((myReader. get_sLevel () + 0.005) / sum);
|
|
static int ccc = 0;
|
|
if (++ccc > 10) {
|
|
ccc = 0;
|
|
show_snr ((int)snr);
|
|
}
|
|
|
|
/**
|
|
* The first sample to be found for the next frame should be T_g
|
|
* samples ahead. Before going for the next frame, we
|
|
* we just check the fineCorrector
|
|
*/
|
|
if (fineOffset > carrierDiff / 2) {
|
|
coarseOffset += carrierDiff;
|
|
fineOffset -= carrierDiff;
|
|
}
|
|
else
|
|
if (fineOffset < -carrierDiff / 2) {
|
|
coarseOffset -= carrierDiff;
|
|
fineOffset += carrierDiff;
|
|
}
|
|
ReadyForNewFrame:
|
|
/// and off we go, up to the next frame
|
|
goto SyncOnPhase;
|
|
}
|
|
catch (int e) {
|
|
// fprintf (stderr, "dabProcessor is stopping\n");
|
|
;
|
|
}
|
|
my_mscHandler. stop ();
|
|
my_ficHandler. stop ();
|
|
}
|
|
|
|
void dabProcessor:: reset (void) {
|
|
myReader. setRunning (false);
|
|
while (isRunning ())
|
|
usleep (1000);
|
|
usleep (10000);
|
|
my_ficHandler. reset ();
|
|
QThread::start ();
|
|
}
|
|
|
|
void dabProcessor::stop (void) {
|
|
myReader. setRunning (false);
|
|
while (isRunning ())
|
|
usleep (1000);
|
|
theDevice -> stopReader ();
|
|
usleep (10000);
|
|
my_ficHandler. reset ();
|
|
}
|
|
|
|
void dabProcessor::coarseCorrectorOn (void) {
|
|
correctionNeeded = true;
|
|
coarseOffset = 0;
|
|
}
|
|
|
|
void dabProcessor::coarseCorrectorOff (void) {
|
|
correctionNeeded = false;
|
|
}
|
|
|
|
// we could have derived the dab processor from fic and msc handlers,
|
|
// however, from a logical point of view they are more delegates than
|
|
// parents.
|
|
uint8_t dabProcessor::kindofService (QString &s) {
|
|
return my_ficHandler. kindofService (s);
|
|
}
|
|
|
|
void dabProcessor::dataforAudioService (int16_t c, audiodata *dd) {
|
|
my_ficHandler. dataforAudioService (c, dd);
|
|
}
|
|
|
|
void dabProcessor::dataforAudioService (QString &s,audiodata *dd) {
|
|
my_ficHandler. dataforAudioService (s, dd, 0);
|
|
}
|
|
|
|
void dabProcessor::dataforAudioService (QString &s,
|
|
audiodata *d, int16_t c) {
|
|
my_ficHandler. dataforAudioService (s, d, c);
|
|
}
|
|
|
|
void dabProcessor::dataforDataService (int16_t c, packetdata *dd) {
|
|
my_ficHandler. dataforDataService (c, dd);
|
|
}
|
|
|
|
void dabProcessor::dataforDataService (QString &s, packetdata *dd) {
|
|
my_ficHandler. dataforDataService (s, dd, 0);
|
|
}
|
|
|
|
void dabProcessor::dataforDataService (QString &s,
|
|
packetdata *d, int16_t c) {
|
|
my_ficHandler. dataforDataService (s, d, c);
|
|
}
|
|
|
|
|
|
void dabProcessor::reset_msc (void) {
|
|
my_mscHandler. reset ();
|
|
}
|
|
|
|
void dabProcessor::set_audioChannel (audiodata *d,
|
|
RingBuffer<int16_t> *b,
|
|
RingBuffer<uint8_t> *db) {
|
|
my_mscHandler. set_Channel (d, b, (RingBuffer<uint8_t> *)nullptr);
|
|
for (int i = 1; i < 10; i ++) {
|
|
packetdata pd;
|
|
dataforDataService (d -> serviceName, &pd, i);
|
|
if (pd. defined) {
|
|
set_dataChannel (&pd, db);
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
void dabProcessor::set_dataChannel (packetdata *d,
|
|
RingBuffer<uint8_t> *b) {
|
|
my_mscHandler. set_Channel (d, (RingBuffer<int16_t> *)nullptr, b);
|
|
}
|
|
|
|
QString dabProcessor::get_ensembleName (void) {
|
|
return my_ficHandler. get_ensembleName ();
|
|
}
|
|
|
|
void dabProcessor::clearEnsemble (void) {
|
|
my_ficHandler. clearEnsemble ();
|
|
}
|
|
|