2017-02-06 14:09:08 +01:00
|
|
|
#
|
|
|
|
/*
|
2025-07-20 19:25:14 +02:00
|
|
|
* Copyright (C) 2014 .. 2024
|
2017-02-06 14:09:08 +01:00
|
|
|
* Jan van Katwijk (J.vanKatwijk@gmail.com)
|
2017-07-17 08:33:17 +02:00
|
|
|
* Lazy Chair Computing
|
2017-02-06 14:09:08 +01:00
|
|
|
*
|
2017-03-11 14:25:25 +01:00
|
|
|
* This file is part of Qt-DAB
|
2020-11-26 19:00:51 +01:00
|
|
|
*
|
2017-03-11 14:25:25 +01:00
|
|
|
* Qt-DAB is free software; you can redistribute it and/or modify
|
2017-02-06 14:09:08 +01:00
|
|
|
* 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.
|
|
|
|
*
|
2017-03-11 14:25:25 +01:00
|
|
|
* Qt-DAB is distributed in the hope that it will be useful,
|
2017-02-06 14:09:08 +01:00
|
|
|
* 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
|
2017-03-11 14:25:25 +01:00
|
|
|
* along with Qt-DAB; if not, write to the Free Software
|
2017-02-06 14:09:08 +01:00
|
|
|
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
|
|
|
*
|
|
|
|
* Once the bits are "in", interpretation and manipulation
|
|
|
|
* should reconstruct the data blocks.
|
2018-07-03 13:03:12 +02:00
|
|
|
* Ofdm_decoder is called for Block_0 and the FIC blocks,
|
2017-02-06 14:09:08 +01:00
|
|
|
* its invocation results in 2 * Tu bits
|
|
|
|
*/
|
2018-01-03 18:46:52 +01:00
|
|
|
#include <vector>
|
2017-02-06 14:09:08 +01:00
|
|
|
#include "ofdm-decoder.h"
|
|
|
|
#include "radio.h"
|
|
|
|
#include "fic-handler.h"
|
|
|
|
#include "msc-handler.h"
|
|
|
|
#include "freq-interleaver.h"
|
2017-03-23 13:48:18 +01:00
|
|
|
#include "dab-params.h"
|
2023-12-10 12:07:02 +01:00
|
|
|
#include "dab-constants.h"
|
2017-02-06 14:09:08 +01:00
|
|
|
/**
|
|
|
|
* \brief ofdmDecoder
|
2017-10-01 19:07:22 +02:00
|
|
|
* The class ofdmDecoder is
|
2017-02-06 14:09:08 +01:00
|
|
|
* taking the data from the ofdmProcessor class in, and
|
|
|
|
* will extract the Tu samples, do an FFT and extract the
|
|
|
|
* carriers and map them on (soft) bits
|
|
|
|
*/
|
2025-09-18 20:13:06 +02:00
|
|
|
#define ALPHA 0.005f
|
2023-11-14 20:53:15 +01:00
|
|
|
static inline
|
|
|
|
Complex normalize (const Complex &V) {
|
2025-09-20 10:58:54 +02:00
|
|
|
DABFLOAT Length = jan_abs (V);
|
2023-11-24 19:37:25 +01:00
|
|
|
return Length == 0.0f ? Complex (0.0, 0.0) : V / (DABFLOAT)Length;
|
2023-11-14 20:53:15 +01:00
|
|
|
}
|
|
|
|
|
2017-02-06 14:09:08 +01:00
|
|
|
ofdmDecoder::ofdmDecoder (RadioInterface *mr,
|
2017-03-24 20:37:02 +01:00
|
|
|
uint8_t dabMode,
|
2018-09-04 13:30:30 +02:00
|
|
|
int16_t bitDepth,
|
2023-10-24 14:36:30 +02:00
|
|
|
RingBuffer<float> *devBuffer_i,
|
2023-10-14 11:27:18 +02:00
|
|
|
RingBuffer<Complex> *iqBuffer_i) :
|
2025-07-20 19:25:14 +02:00
|
|
|
myRadioInterface (mr),
|
|
|
|
params (dabMode),
|
|
|
|
theTable (dabMode),
|
|
|
|
myMapper (dabMode),
|
|
|
|
fft (params. get_T_u (), false),
|
|
|
|
devBuffer (devBuffer_i),
|
|
|
|
iqBuffer (iqBuffer_i),
|
|
|
|
phaseReference (params. get_T_u ()),
|
|
|
|
conjVector (params. get_T_u ()),
|
|
|
|
fft_buffer (params. get_T_u ()),
|
2025-09-04 20:12:34 +02:00
|
|
|
sigmaSQ_Vector (params. get_T_u ()),
|
2025-07-20 19:25:14 +02:00
|
|
|
meanLevelVector (params. get_T_u ()),
|
2025-08-31 20:55:36 +02:00
|
|
|
stdDevVector (params. get_T_u ()),
|
2025-09-02 16:37:34 +02:00
|
|
|
angleVector (params. get_T_u ()) {
|
2023-09-20 21:17:22 +02:00
|
|
|
(void)bitDepth;
|
2024-04-10 19:49:55 +02:00
|
|
|
connect (this, &ofdmDecoder::showIQ,
|
|
|
|
myRadioInterface, &RadioInterface::showIQ);
|
|
|
|
connect (this, &ofdmDecoder::show_quality,
|
|
|
|
myRadioInterface, &RadioInterface::show_quality);
|
|
|
|
connect (this, &ofdmDecoder::show_stdDev,
|
|
|
|
myRadioInterface, &RadioInterface::show_stdDev);
|
2017-06-06 18:43:50 +02:00
|
|
|
//
|
2025-07-20 19:25:14 +02:00
|
|
|
this -> T_s = params. get_T_s ();
|
|
|
|
this -> T_u = params. get_T_u ();
|
|
|
|
this -> nrBlocks = params. get_L ();
|
|
|
|
this -> carriers = params. get_carriers ();
|
|
|
|
this -> T_g = T_s - T_u;
|
2024-11-25 14:55:59 +01:00
|
|
|
|
2025-02-08 19:37:57 +01:00
|
|
|
repetitionCounter = 10;
|
2023-10-14 11:27:18 +02:00
|
|
|
|
2025-02-08 19:37:57 +01:00
|
|
|
reset ();
|
2024-11-25 14:55:59 +01:00
|
|
|
iqSelector = SHOW_DECODED;
|
2025-09-05 19:54:10 +02:00
|
|
|
// iqSelector = SHOW_RAW;
|
2024-11-25 14:55:59 +01:00
|
|
|
decoder = DECODER_1;
|
2025-09-20 10:58:54 +02:00
|
|
|
sqrt_2 = sqrt (2);
|
2017-02-06 14:09:08 +01:00
|
|
|
}
|
|
|
|
|
2023-03-04 11:53:24 +01:00
|
|
|
ofdmDecoder::~ofdmDecoder () {
|
2017-02-06 14:09:08 +01:00
|
|
|
}
|
2017-10-01 19:07:22 +02:00
|
|
|
//
|
2023-10-24 14:36:30 +02:00
|
|
|
void ofdmDecoder::stop () {
|
2017-02-06 14:09:08 +01:00
|
|
|
}
|
2025-02-08 19:37:57 +01:00
|
|
|
//
|
2023-10-24 14:36:30 +02:00
|
|
|
void ofdmDecoder::reset () {
|
2025-08-31 20:55:36 +02:00
|
|
|
for (int i = 0; i < T_u; i ++) {
|
2025-09-18 20:13:06 +02:00
|
|
|
sigmaSQ_Vector [i] = 0;
|
2025-08-31 20:55:36 +02:00
|
|
|
meanLevelVector [i] = 0;
|
|
|
|
stdDevVector [i] = 0;
|
2025-09-02 16:37:34 +02:00
|
|
|
angleVector [i] = M_PI_4;
|
2025-08-31 20:55:36 +02:00
|
|
|
}
|
2025-02-08 19:37:57 +01:00
|
|
|
meanValue = 1.0f;
|
2025-09-16 15:37:36 +02:00
|
|
|
avgBit = 1.0f;
|
2017-02-06 14:09:08 +01:00
|
|
|
}
|
2023-10-24 14:36:30 +02:00
|
|
|
//
|
2025-09-02 21:56:35 +02:00
|
|
|
void ofdmDecoder::processBlock_0 (std::vector <Complex> buffer) {
|
2023-05-21 20:03:29 +02:00
|
|
|
fft. fft (buffer);
|
2025-07-20 19:25:14 +02:00
|
|
|
// we are now in the frequency domain, and we keep the carriers
|
2025-08-31 20:55:36 +02:00
|
|
|
// for their phases.
|
2025-07-20 19:25:14 +02:00
|
|
|
//
|
2023-05-21 20:03:29 +02:00
|
|
|
memcpy (phaseReference. data (), buffer. data (),
|
2023-11-03 17:47:31 +01:00
|
|
|
T_u * sizeof (Complex));
|
2024-12-14 14:55:42 +01:00
|
|
|
|
2025-09-18 20:13:06 +02:00
|
|
|
Complex temp = Complex (0, 0);
|
2017-02-06 14:09:08 +01:00
|
|
|
}
|
2017-02-12 20:08:38 +01:00
|
|
|
//
|
|
|
|
// Just interested. In the ideal case the constellation of the
|
|
|
|
// decoded symbols is precisely in the four points
|
2017-11-11 21:13:03 +01:00
|
|
|
// k * (1, 1), k * (1, -1), k * (-1, -1), k * (-1, 1)
|
2017-06-01 16:16:10 +02:00
|
|
|
// To ease computation, we map all incoming values onto quadrant 1
|
2023-11-14 20:53:15 +01:00
|
|
|
//
|
|
|
|
// For the computation of the MER we use the definition
|
|
|
|
// from ETSI TR 101 290 (appendix C1)
|
2023-09-20 21:17:22 +02:00
|
|
|
float ofdmDecoder::computeQuality (Complex *v) {
|
2024-11-22 20:21:44 +01:00
|
|
|
static float f_n = 1;
|
|
|
|
static float f_d = 1;
|
2023-05-29 12:03:16 +02:00
|
|
|
for (int i = 0; i < carriers; i ++) {
|
2024-11-22 20:21:44 +01:00
|
|
|
Complex ss = v [T_u / 2 - carriers / 2 + i];
|
2025-08-31 20:55:36 +02:00
|
|
|
float ab = jan_abs (ss) / sqrt_2;
|
2024-11-25 20:20:42 +01:00
|
|
|
f_n = 0.99 * f_n + 0.01 * (jan_abs (ss) * jan_abs (ss));
|
2024-11-22 20:21:44 +01:00
|
|
|
float R = abs (abs (real (ss)) - ab);
|
|
|
|
float I = abs (abs (imag (ss)) - ab);
|
|
|
|
f_d = 0.99 * f_d + 0.01 * (R * R + I * I);
|
2017-02-11 13:03:34 +01:00
|
|
|
}
|
2024-11-22 20:21:44 +01:00
|
|
|
return 10 * log10 (f_n / f_d + 0.1);
|
2017-02-11 13:03:34 +01:00
|
|
|
}
|
2025-07-20 19:25:14 +02:00
|
|
|
|
|
|
|
// for the other blocks of data, the first step is to go from
|
|
|
|
// time to frequency domain, to get the carriers.
|
|
|
|
// we distinguish between FIC blocks and other blocks,
|
|
|
|
// only to spare a test. The mapping code is the same
|
2018-07-03 13:03:12 +02:00
|
|
|
|
|
|
|
static int cnt = 0;
|
2023-10-16 16:27:15 +02:00
|
|
|
|
2023-10-24 14:36:30 +02:00
|
|
|
//
|
|
|
|
// DAB (and DAB+) bits are encoded is DPSK, 2 bits per carrier,
|
|
|
|
// depending on the quadrant the carrier is in. There are
|
|
|
|
// of course two different approaches in decoding the bits
|
|
|
|
// One is looking at the X and Y components, and
|
|
|
|
// their length, relative to each other,
|
|
|
|
// Ideally, the X and Y are of equal size, in practice they are not.
|
|
|
|
|
2024-02-25 14:18:27 +01:00
|
|
|
int sign (DABFLOAT x) {
|
|
|
|
return x < 0 ? -1 : x > 0 ? 1 : 0;
|
|
|
|
}
|
|
|
|
|
2025-09-20 10:58:54 +02:00
|
|
|
void limit_symmetrically (DABFLOAT &v, DABFLOAT limit) {
|
2025-02-08 19:37:57 +01:00
|
|
|
if (v < -limit)
|
2025-05-08 12:55:36 +02:00
|
|
|
v = -limit;
|
2025-09-18 20:13:06 +02:00
|
|
|
if (v > limit)
|
2025-05-08 12:55:36 +02:00
|
|
|
v = limit;
|
2025-02-08 19:37:57 +01:00
|
|
|
}
|
2025-03-29 10:51:10 +01:00
|
|
|
//
|
2025-09-18 20:13:06 +02:00
|
|
|
// The decoders 1 and 2 are based on "Soft optimal 2" in
|
2025-09-02 16:37:34 +02:00
|
|
|
// "Soft decisions for DQPSK demodulation for the Viterbi
|
|
|
|
// decoding of the convolutional codes"
|
|
|
|
// Thushara C Hewavithana and Mike Brooks
|
|
|
|
//
|
2025-09-16 15:37:36 +02:00
|
|
|
// the formula (decoders 1 and 2) (in my own words)
|
2025-09-02 16:37:34 +02:00
|
|
|
// Corrector = sqrt (2) / (SigmaSQ * (1 /SNR + 2))
|
|
|
|
// where corrector is to be applied on real (symbol) and imag (symbol)
|
|
|
|
// The implied assumption here is that abs (symbol) == 1,
|
|
|
|
//
|
2025-09-03 21:13:58 +02:00
|
|
|
// The basic idea behind the formula is to enlarge the
|
|
|
|
// spread in sizes of the real (symb) and imag (symb),
|
|
|
|
// which is obviously inversely proportional with the sigmaSq
|
|
|
|
//
|
2025-09-02 16:37:34 +02:00
|
|
|
// It shows that the resulting values for "Corrector" are
|
|
|
|
// small, so for mapping those on -127 .. 127 a "scaler" is needed
|
|
|
|
// (Thanks to Rolf Zerr, aka Old-dab).
|
2025-09-16 15:37:36 +02:00
|
|
|
//
|
|
|
|
// Decoder 4 is an interpretation of the so-called "Optimal 3"
|
|
|
|
// version in the aforementioned paper.
|
|
|
|
// It is still experimental!!!
|
|
|
|
|
|
|
|
static inline
|
2025-09-20 10:58:54 +02:00
|
|
|
Complex w (DABFLOAT kn) {
|
|
|
|
DABFLOAT re = cos (kn * M_PI / 4);
|
|
|
|
DABFLOAT im = sin (kn * M_PI / 4);
|
|
|
|
return Complex (re, im);
|
2025-09-16 15:37:36 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
static inline
|
2025-09-20 10:58:54 +02:00
|
|
|
DABFLOAT makeA (int i, Complex S, Complex prevS) {
|
2025-09-16 15:37:36 +02:00
|
|
|
return abs (prevS + w (-i) * S);
|
|
|
|
}
|
|
|
|
|
|
|
|
static inline
|
2025-09-20 10:58:54 +02:00
|
|
|
DABFLOAT IO (DABFLOAT x) {
|
2025-09-16 15:37:36 +02:00
|
|
|
return std::cyl_bessel_i (0.0f, x);
|
|
|
|
}
|
2025-02-08 19:37:57 +01:00
|
|
|
|
2023-09-20 21:17:22 +02:00
|
|
|
void ofdmDecoder::decode (std::vector <Complex> &buffer,
|
2023-10-14 11:27:18 +02:00
|
|
|
int32_t blkno,
|
2025-08-31 20:55:36 +02:00
|
|
|
std::vector<int16_t> &softbits,
|
|
|
|
DABFLOAT snr) {
|
2023-10-16 16:27:15 +02:00
|
|
|
|
2025-09-20 10:58:54 +02:00
|
|
|
DABFLOAT sum = 0;
|
|
|
|
DABFLOAT bitSum = 0;
|
2025-09-18 20:13:06 +02:00
|
|
|
|
2024-03-13 19:45:53 +01:00
|
|
|
memcpy (fft_buffer. data (), &((buffer. data ()) [T_g]),
|
2023-11-23 16:13:49 +01:00
|
|
|
T_u * sizeof (Complex));
|
2023-10-16 16:27:15 +02:00
|
|
|
// first step: do the FFT
|
2024-03-13 19:45:53 +01:00
|
|
|
fft. fft (fft_buffer. data ());
|
2023-10-16 16:27:15 +02:00
|
|
|
|
|
|
|
// a little optimization: we do not interchange the
|
|
|
|
// positive/negative frequencies to their right positions.
|
|
|
|
// The de-interleaving understands this
|
2024-02-25 14:18:27 +01:00
|
|
|
|
2023-10-16 16:27:15 +02:00
|
|
|
for (int i = 0; i < carriers; i ++) {
|
2017-02-06 14:09:08 +01:00
|
|
|
int16_t index = myMapper. mapIn (i);
|
|
|
|
if (index < 0)
|
|
|
|
index += T_u;
|
2025-08-31 20:55:36 +02:00
|
|
|
Complex fftBin = fft_buffer [index] *
|
|
|
|
normalize (conj (phaseReference [index]));
|
|
|
|
conjVector [index] = fftBin;
|
2025-09-02 16:37:34 +02:00
|
|
|
DABFLOAT binAbsLevel = jan_abs (fftBin);
|
2025-09-16 15:37:36 +02:00
|
|
|
Complex prevS = phaseReference [index];
|
2025-09-02 16:37:34 +02:00
|
|
|
//
|
|
|
|
// updates
|
2025-08-31 20:55:36 +02:00
|
|
|
|
|
|
|
Complex fftBin_at_1 = Complex (abs (real (fftBin)),
|
|
|
|
abs (imag (fftBin)));
|
|
|
|
|
2025-09-02 16:37:34 +02:00
|
|
|
DABFLOAT angle = arg (fftBin_at_1) - angleVector [index];
|
|
|
|
angleVector [index] =
|
|
|
|
compute_avg (angleVector [index], angle, ALPHA);
|
2025-08-31 20:55:36 +02:00
|
|
|
stdDevVector [index] =
|
|
|
|
compute_avg (stdDevVector [index],
|
|
|
|
angle * angle, ALPHA);
|
|
|
|
|
2025-04-04 16:17:00 +02:00
|
|
|
meanLevelVector [index] =
|
2025-08-31 20:55:36 +02:00
|
|
|
compute_avg (meanLevelVector [index], binAbsLevel, ALPHA);
|
|
|
|
|
2025-09-19 19:52:42 +02:00
|
|
|
DABFLOAT d_x = abs (real (fftBin_at_1)) -
|
2025-09-04 20:12:34 +02:00
|
|
|
meanLevelVector [index] / sqrt_2;
|
2025-09-19 19:52:42 +02:00
|
|
|
DABFLOAT d_y = abs (imag (fftBin_at_1)) -
|
2025-09-04 20:12:34 +02:00
|
|
|
meanLevelVector [index] / sqrt_2;
|
2025-09-03 21:13:58 +02:00
|
|
|
DABFLOAT sigmaSQ = d_x * d_x + d_y * d_y;
|
2025-09-04 20:12:34 +02:00
|
|
|
sigmaSQ_Vector [index] =
|
|
|
|
compute_avg (sigmaSQ_Vector [index], sigmaSQ, ALPHA);
|
|
|
|
//
|
|
|
|
// Ran over quite a number of examples, I found DECODER_1
|
2025-09-18 20:13:06 +02:00
|
|
|
// working best
|
2025-08-31 20:55:36 +02:00
|
|
|
if (this -> decoder == DECODER_1) {
|
|
|
|
DABFLOAT corrector =
|
2025-09-19 19:52:42 +02:00
|
|
|
1.5 * meanLevelVector [index] / sigmaSQ_Vector [index];
|
2025-09-18 20:13:06 +02:00
|
|
|
corrector /= (1 / snr + 2);
|
2025-08-31 20:55:36 +02:00
|
|
|
Complex R1 = corrector * normalize (fftBin) *
|
2025-09-19 19:52:42 +02:00
|
|
|
(DABFLOAT)(sqrt (binAbsLevel *
|
|
|
|
jan_abs (phaseReference [index])));
|
2025-09-20 10:58:54 +02:00
|
|
|
DABFLOAT scaler = 140.0 / meanValue;
|
2025-08-31 20:55:36 +02:00
|
|
|
DABFLOAT leftBit = - real (R1) * scaler;
|
|
|
|
limit_symmetrically (leftBit, MAX_VITERBI);
|
|
|
|
softbits [i] = (int16_t)leftBit;
|
|
|
|
|
|
|
|
DABFLOAT rightBit = - imag (R1) * scaler;
|
|
|
|
limit_symmetrically (rightBit, MAX_VITERBI);
|
|
|
|
softbits [i + carriers] = (int16_t)rightBit;
|
|
|
|
|
|
|
|
sum += jan_abs (R1);
|
2023-11-14 20:53:15 +01:00
|
|
|
}
|
2025-08-31 20:55:36 +02:00
|
|
|
else
|
|
|
|
if (this -> decoder == DECODER_2) { // decoder 2
|
|
|
|
DABFLOAT corrector =
|
2025-09-18 20:13:06 +02:00
|
|
|
meanLevelVector [index] / sigmaSQ_Vector [index];
|
2025-08-31 20:55:36 +02:00
|
|
|
corrector /= (1 / snr + 3);
|
|
|
|
Complex R1 = corrector * normalize (fftBin) *
|
2025-09-19 19:52:42 +02:00
|
|
|
(DABFLOAT)(sqrt (binAbsLevel *
|
|
|
|
jan_abs (phaseReference [index])));
|
2025-09-20 10:58:54 +02:00
|
|
|
DABFLOAT scaler = 100.0 / meanValue;
|
2025-08-31 20:55:36 +02:00
|
|
|
DABFLOAT leftBit = - real (R1) * scaler;
|
|
|
|
limit_symmetrically (leftBit, MAX_VITERBI);
|
|
|
|
softbits [i] = (int16_t)leftBit;
|
|
|
|
|
|
|
|
DABFLOAT rightBit = - imag (R1) * scaler;
|
|
|
|
limit_symmetrically (rightBit, MAX_VITERBI);
|
|
|
|
softbits [i + carriers] = (int16_t)rightBit;
|
|
|
|
|
|
|
|
sum += jan_abs (R1);
|
|
|
|
}
|
2025-09-18 20:13:06 +02:00
|
|
|
else
|
2025-09-16 15:37:36 +02:00
|
|
|
if (this -> decoder == DECODER_3) { // decoder 3
|
2025-09-13 19:46:04 +02:00
|
|
|
softbits [i] = - real (fftBin) / binAbsLevel *
|
2025-09-18 20:13:06 +02:00
|
|
|
1.0 * MAX_VITERBI;
|
2025-08-31 20:55:36 +02:00
|
|
|
softbits [carriers + i]
|
2025-09-13 19:46:04 +02:00
|
|
|
= - imag (fftBin) / binAbsLevel *
|
2025-09-18 20:13:06 +02:00
|
|
|
1.0 * MAX_VITERBI;
|
2025-08-31 20:55:36 +02:00
|
|
|
}
|
2025-09-16 15:37:36 +02:00
|
|
|
else { // experimental decoder 4
|
2025-09-20 10:58:54 +02:00
|
|
|
DABFLOAT P1 = makeA (1, fftBin, prevS) /
|
2025-09-16 15:37:36 +02:00
|
|
|
sigmaSQ_Vector [index];
|
2025-09-20 10:58:54 +02:00
|
|
|
DABFLOAT P7 = makeA (7, fftBin, prevS) /
|
2025-09-16 15:37:36 +02:00
|
|
|
sigmaSQ_Vector [index];
|
2025-09-20 10:58:54 +02:00
|
|
|
DABFLOAT P3 = makeA (3, fftBin, prevS) /
|
2025-09-16 15:37:36 +02:00
|
|
|
sigmaSQ_Vector [index];
|
2025-09-20 10:58:54 +02:00
|
|
|
DABFLOAT P5 = makeA (5, fftBin, prevS) /
|
2025-09-16 15:37:36 +02:00
|
|
|
sigmaSQ_Vector [index];
|
|
|
|
|
2025-09-20 10:58:54 +02:00
|
|
|
DABFLOAT IO_P1 = IO (P1);
|
|
|
|
DABFLOAT IO_P7 = IO (P7);
|
|
|
|
DABFLOAT IO_P3 = IO (P3);
|
|
|
|
DABFLOAT IO_P5 = IO (P5);
|
2025-09-16 15:37:36 +02:00
|
|
|
|
2025-09-20 10:58:54 +02:00
|
|
|
DABFLOAT b1 = log ((IO_P1 + IO_P7) / (IO_P3 + IO_P5));
|
|
|
|
DABFLOAT b2 = log ((IO_P1 + IO_P3) / (IO_P5 + IO_P7));
|
2025-09-16 15:37:36 +02:00
|
|
|
|
|
|
|
bitSum += (abs (b1) + abs (b2)) / 2;
|
2025-09-20 10:58:54 +02:00
|
|
|
DABFLOAT scaler = 40 / avgBit;
|
2025-09-16 15:37:36 +02:00
|
|
|
int s1 = -real (fftBin) < 0 ? -1 : 1;
|
|
|
|
int s2 = -imag (fftBin) < 0 ? -1 : 1;
|
|
|
|
DABFLOAT xx1 = s1 * abs (b1) * scaler;
|
|
|
|
DABFLOAT xx2 = s2 * abs (b2) * scaler;
|
|
|
|
limit_symmetrically (xx1, MAX_VITERBI);
|
|
|
|
limit_symmetrically (xx2, MAX_VITERBI);
|
|
|
|
softbits [i] = (int16_t)xx1;
|
|
|
|
softbits [carriers + i] = (int16_t)xx2;
|
|
|
|
}
|
2025-08-31 20:55:36 +02:00
|
|
|
}
|
2025-09-18 20:13:06 +02:00
|
|
|
avgBit = compute_avg (avgBit, bitSum / carriers, 0.1);
|
2025-09-01 19:04:36 +02:00
|
|
|
meanValue = compute_avg (meanValue, sum /carriers, 0.1);
|
2025-08-31 20:55:36 +02:00
|
|
|
|
|
|
|
// end of decoding , now for displaying things //
|
|
|
|
//////////////////////////////////////////////////////////////////
|
|
|
|
|
2017-02-06 14:09:08 +01:00
|
|
|
// From time to time we show the constellation of symbol 2.
|
|
|
|
if (blkno == 2) {
|
2023-11-06 13:15:31 +01:00
|
|
|
if (++cnt > repetitionCounter) {
|
2025-08-31 20:55:36 +02:00
|
|
|
DABFLOAT maxAmp = 00;
|
2023-12-10 12:07:02 +01:00
|
|
|
for (int j = -carriers / 2; j < carriers / 2; j ++)
|
2024-08-15 13:16:44 +02:00
|
|
|
if (j != 0)
|
|
|
|
if (jan_abs (fft_buffer [(T_u + j) % T_u]) > maxAmp)
|
|
|
|
maxAmp = jan_abs (fft_buffer [(T_u + j) % T_u]);
|
2023-10-14 11:27:18 +02:00
|
|
|
Complex displayVector [carriers];
|
2023-12-10 12:07:02 +01:00
|
|
|
|
2023-10-14 11:27:18 +02:00
|
|
|
if (iqSelector == SHOW_RAW) {
|
2023-10-16 16:27:15 +02:00
|
|
|
for (int j = 0; j < carriers; j ++)
|
|
|
|
displayVector [j] =
|
|
|
|
fft_buffer [(T_u - carriers / 2 - 1 + j) % T_u] / maxAmp;
|
2023-10-14 11:27:18 +02:00
|
|
|
}
|
|
|
|
else {
|
2024-06-01 11:12:04 +02:00
|
|
|
for (int j = 1; j < carriers; j ++) {
|
2023-10-16 16:27:15 +02:00
|
|
|
displayVector [j] =
|
2023-12-10 12:07:02 +01:00
|
|
|
conjVector [T_u / 2 - carriers / 2 + j] / maxAmp;
|
2024-06-01 11:12:04 +02:00
|
|
|
}
|
2023-10-14 11:27:18 +02:00
|
|
|
}
|
2023-10-14 13:23:41 +02:00
|
|
|
iqBuffer -> putDataIntoBuffer (displayVector, carriers);
|
2023-10-14 11:27:18 +02:00
|
|
|
|
2025-05-05 16:17:06 +02:00
|
|
|
float freqOffset = compute_frequencyOffset (fft_buffer. data (),
|
|
|
|
phaseReference. data ());
|
2023-10-24 14:36:30 +02:00
|
|
|
if (devBuffer != nullptr) {
|
2023-10-16 16:27:15 +02:00
|
|
|
float tempVector [carriers];
|
|
|
|
for (int i = 0; i < carriers; i ++) {
|
|
|
|
tempVector [i] =
|
2025-02-08 19:37:57 +01:00
|
|
|
stdDevVector [(T_u - carriers / 2 + i) % T_u];
|
2023-10-16 16:27:15 +02:00
|
|
|
tempVector [i] = tempVector [i] / M_PI * 180.0;
|
|
|
|
}
|
2023-12-10 12:07:02 +01:00
|
|
|
|
2023-12-02 10:58:05 +01:00
|
|
|
devBuffer -> putDataIntoBuffer (tempVector, carriers);
|
2023-10-16 16:27:15 +02:00
|
|
|
show_stdDev (carriers);
|
|
|
|
}
|
2023-10-24 14:36:30 +02:00
|
|
|
|
2023-10-14 13:23:41 +02:00
|
|
|
showIQ (carriers);
|
2024-03-13 19:45:53 +01:00
|
|
|
float Quality = computeQuality (conjVector. data ());
|
|
|
|
float timeOffset = compute_timeOffset (fft_buffer. data (),
|
2021-12-24 19:34:24 +01:00
|
|
|
phaseReference. data ());
|
2023-10-24 14:36:30 +02:00
|
|
|
show_quality (Quality, timeOffset, freqOffset);
|
2017-02-06 14:09:08 +01:00
|
|
|
cnt = 0;
|
|
|
|
}
|
|
|
|
}
|
2025-02-08 19:37:57 +01:00
|
|
|
|
2024-03-13 19:45:53 +01:00
|
|
|
memcpy (phaseReference. data(), fft_buffer. data (),
|
2023-09-20 21:17:22 +02:00
|
|
|
T_u * sizeof (Complex));
|
2019-12-31 14:44:26 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
//
|
|
|
|
// While DAB symbols do not carry pilots, it is known that
|
|
|
|
// arg (carrier [i, j] * conj (carrier [i + 1, j])
|
|
|
|
// should be K * M_PI / 4, (k in {1, 3, 5, 7}) so basically
|
|
|
|
// carriers in decoded symbols can be used as if they were pilots
|
|
|
|
//
|
|
|
|
// so, with that in mind we experiment with formula 5.39
|
|
|
|
// and 5.40 from "OFDM Baseband Receiver Design for Wireless
|
|
|
|
// Communications (Chiueh and Tsai)"
|
2023-09-20 21:17:22 +02:00
|
|
|
float ofdmDecoder::compute_timeOffset (Complex *r, Complex *v) {
|
|
|
|
Complex sum = Complex (0, 0);
|
2019-12-31 14:44:26 +01:00
|
|
|
|
|
|
|
for (int i = -carriers / 2; i < carriers / 2; i += 6) {
|
|
|
|
int index_1 = i < 0 ? i + T_u : i;
|
|
|
|
int index_2 = (i + 1) < 0 ? (i + 1) + T_u : (i + 1);
|
2023-09-20 21:17:22 +02:00
|
|
|
Complex s = r [index_1] * conj (v [index_2]);
|
|
|
|
s = Complex (abs (real (s)), abs (imag (s)));
|
2023-10-14 11:27:18 +02:00
|
|
|
Complex leftTerm = s * conj (Complex (abs (s) / sqrt (2),
|
2019-12-31 14:44:26 +01:00
|
|
|
abs (s) / sqrt (2)));
|
|
|
|
s = r [index_2] * conj (v [index_2]);
|
2023-09-20 21:17:22 +02:00
|
|
|
s = Complex (abs (real (s)), abs (imag (s)));
|
2023-10-14 11:27:18 +02:00
|
|
|
Complex rightTerm = s * conj (Complex (abs (s) / sqrt (2),
|
2019-12-31 14:44:26 +01:00
|
|
|
abs (s) / sqrt (2)));
|
|
|
|
sum += conj (leftTerm) * rightTerm;
|
|
|
|
}
|
|
|
|
|
2021-12-24 19:34:24 +01:00
|
|
|
return arg (sum);
|
2019-12-31 14:44:26 +01:00
|
|
|
}
|
2025-05-05 16:17:06 +02:00
|
|
|
//
|
|
|
|
// Ideally, the processed carrier should have a value
|
|
|
|
// equal to (2 * k + 1) * PI / 4
|
|
|
|
// The offset is a measure of the frequency "error"
|
2025-07-20 19:25:14 +02:00
|
|
|
float ofdmDecoder::compute_frequencyOffset (Complex *r, Complex *c) {
|
2023-09-20 21:17:22 +02:00
|
|
|
Complex theta = Complex (0, 0);
|
2025-05-05 16:17:06 +02:00
|
|
|
static float vv = 0;
|
|
|
|
|
2019-12-31 14:44:26 +01:00
|
|
|
for (int i = - carriers / 2; i < carriers / 2; i += 6) {
|
|
|
|
int index = i < 0 ? i + T_u : i;
|
2023-09-20 21:17:22 +02:00
|
|
|
Complex val = r [index] * conj (c [index]);
|
2025-05-05 16:17:06 +02:00
|
|
|
val = Complex (abs (real (val)), abs (imag (val)));
|
2023-09-20 21:17:22 +02:00
|
|
|
theta += val * Complex (1, -1);
|
2019-12-31 14:44:26 +01:00
|
|
|
}
|
|
|
|
|
2025-06-15 20:47:32 +02:00
|
|
|
float uu = arg (theta) / (2 * M_PI) * SAMPLERATE / T_u;
|
2025-05-05 16:17:06 +02:00
|
|
|
vv = 0.9 * vv + 0.1 * abs (uu);;
|
|
|
|
return vv;
|
2017-02-06 14:09:08 +01:00
|
|
|
}
|
2019-12-31 14:44:26 +01:00
|
|
|
|
2025-06-15 20:47:32 +02:00
|
|
|
float ofdmDecoder::compute_clockOffset (Complex *r, Complex *v) {
|
2019-12-31 14:44:26 +01:00
|
|
|
float offsa = 0;
|
|
|
|
int offsb = 0;
|
|
|
|
|
|
|
|
for (int i = - carriers / 2; i < carriers / 2; i += 6) {
|
|
|
|
int index = i < 0 ? (i + T_u) : i;
|
|
|
|
int index_2 = i + carriers / 2;
|
2023-09-20 21:17:22 +02:00
|
|
|
Complex a1 =
|
|
|
|
Complex (abs (real (r [index])),
|
2020-12-17 17:57:37 +01:00
|
|
|
abs (imag (r [index])));
|
2023-09-20 21:17:22 +02:00
|
|
|
Complex a2 =
|
|
|
|
Complex (abs (real (v [index])),
|
2020-12-17 17:57:37 +01:00
|
|
|
abs (imag (v [index])));
|
|
|
|
float s = abs (arg (a1 * conj (a2)));
|
|
|
|
offsa += index * s;
|
2019-12-31 14:44:26 +01:00
|
|
|
offsb += index_2 * index_2;
|
|
|
|
}
|
|
|
|
|
|
|
|
float sampleClockOffset =
|
|
|
|
offsa / (2 * M_PI * (float)T_s/ T_u * offsb);
|
|
|
|
|
2021-12-24 19:34:24 +01:00
|
|
|
return sampleClockOffset;
|
2019-12-31 14:44:26 +01:00
|
|
|
}
|
|
|
|
|
2023-10-14 11:27:18 +02:00
|
|
|
void ofdmDecoder::handle_iqSelector () {
|
|
|
|
if (iqSelector == SHOW_RAW)
|
|
|
|
iqSelector = SHOW_DECODED;
|
|
|
|
else
|
|
|
|
iqSelector = SHOW_RAW;
|
|
|
|
}
|
2019-12-31 14:44:26 +01:00
|
|
|
|
2023-11-14 20:53:15 +01:00
|
|
|
void ofdmDecoder::handle_decoderSelector (int decoder) {
|
|
|
|
this -> decoder = decoder;
|
|
|
|
}
|
2025-08-31 20:55:36 +02:00
|
|
|
//
|
|
|
|
|