2023-11-18 06:02:48 -05:00
|
|
|
|
///////////////////////////////////////////////////////////////////////////////////////
|
|
|
|
|
// Copyright (C) 2019-2023 Edouard Griffiths, F4EXB <f4exb06@gmail.com> //
|
|
|
|
|
// Copyright (C) 2019 Davide Gerhard <rainbow@irh.it> //
|
|
|
|
|
// Copyright (C) 2022 Jon Beniston, M7RCE <jon@beniston.com> //
|
|
|
|
|
// //
|
|
|
|
|
// This file is part of LeanSDR Copyright (C) 2016-2019 <pabr@pabr.org>. //
|
|
|
|
|
// //
|
|
|
|
|
// This program 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 as version 3 of the License, or //
|
|
|
|
|
// (at your option) any later version. //
|
|
|
|
|
// //
|
|
|
|
|
// This program 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 V3 for more details. //
|
|
|
|
|
// //
|
|
|
|
|
// You should have received a copy of the GNU General Public License //
|
|
|
|
|
// along with this program. If not, see <http://www.gnu.org/licenses/>. //
|
|
|
|
|
///////////////////////////////////////////////////////////////////////////////////////
|
2019-03-17 16:31:42 -04:00
|
|
|
|
|
2021-03-24 17:18:22 -04:00
|
|
|
|
// Latest updates:
|
|
|
|
|
// | S2: Revert to tracking all symbols. pabr committed on Mar 6, 2019
|
|
|
|
|
// | Cleanup: Remove debug code. pabr committed on Mar 6, 2019
|
|
|
|
|
// | S2: Dummy PLFRAME handling pabr committed on Mar 26, 2019
|
|
|
|
|
// | S2: Preliminary support for GSE pabr committed on Mar 26, 2019
|
|
|
|
|
// | leandvbtx: Signal S2 MATYPE as TS. pabr committed on Mar 26, 2019
|
|
|
|
|
// | DVB-S2 VCM support, suitable for ACM reception (not MIS). pabr committed on Nov 24, 2019
|
|
|
|
|
// | Remove unused constants. pabr committed on Dec 4, 2019
|
|
|
|
|
// | S2 RX: Capture TED decision history in sampler_state. pabr committed on Dec 5, 2019
|
|
|
|
|
// | S2 RX: Print error rate on PLS symbols. pabr committed on Dec 5, 2019
|
|
|
|
|
// | New DVB-S2 receiver with PL-based carrier recovery. modcod/framesize filtering for VCM. pabr committed on Jan 9, 2020
|
2021-03-25 19:57:52 -04:00
|
|
|
|
// skip Soft-decoding of S2 PLSCODE. pabr committed on Jan 16, 2020
|
|
|
|
|
// skip Validate PLHEADER soft-decoding when DEBUG_CARRIER==1. pabr committed on Jan 17, 2020
|
|
|
|
|
// | Cleanup scope of some S2 constants. pabr committed on Apr 29, 2020
|
2021-03-25 21:17:38 -04:00
|
|
|
|
// skip Fix overflow to state_out stream. pabr committed on Apr 29, 2020
|
|
|
|
|
// | S2 deframer: Set TEI bit on TS packets with bad CRC8. pabr committed on Jul 29, 2020
|
2021-03-24 17:18:22 -04:00
|
|
|
|
|
2019-03-17 16:31:42 -04:00
|
|
|
|
#ifndef LEANSDR_DVBS2_H
|
|
|
|
|
#define LEANSDR_DVBS2_H
|
|
|
|
|
|
2019-07-14 09:35:45 -04:00
|
|
|
|
/*
|
2019-03-17 16:31:42 -04:00
|
|
|
|
#include "leansdr/bch.h"
|
|
|
|
|
#include "leansdr/crc.h"
|
|
|
|
|
#include "leansdr/dvb.h"
|
|
|
|
|
#include "leansdr/ldpc.h"
|
|
|
|
|
#include "leansdr/sdr.h"
|
|
|
|
|
#include "leansdr/softword.h"
|
2019-07-14 09:35:45 -04:00
|
|
|
|
*/
|
|
|
|
|
|
2021-03-02 00:02:38 -05:00
|
|
|
|
#include <stdlib.h>
|
2021-03-05 22:46:26 -05:00
|
|
|
|
#include <deque>
|
2021-03-27 00:38:28 -04:00
|
|
|
|
#include <bitset>
|
2019-07-14 09:35:45 -04:00
|
|
|
|
|
2021-03-02 00:02:38 -05:00
|
|
|
|
#include "bch.h"
|
2019-07-14 09:35:45 -04:00
|
|
|
|
#include "crc.h"
|
|
|
|
|
#include "dvb.h"
|
|
|
|
|
#include "softword.h"
|
|
|
|
|
#include "ldpc.h"
|
|
|
|
|
#include "sdr.h"
|
2019-03-17 16:31:42 -04:00
|
|
|
|
|
2021-03-10 01:46:25 -05:00
|
|
|
|
#include <signal.h>
|
2022-07-18 11:40:00 -04:00
|
|
|
|
#ifdef LINUX
|
2021-03-10 01:46:25 -05:00
|
|
|
|
#include <sys/wait.h>
|
2022-07-18 11:40:00 -04:00
|
|
|
|
#endif
|
|
|
|
|
#ifdef _MSC_VER
|
|
|
|
|
#include <BaseTsd.h>
|
|
|
|
|
typedef SSIZE_T ssize_t;
|
|
|
|
|
#endif
|
2021-03-02 00:02:38 -05:00
|
|
|
|
#include "ldpctool/layered_decoder.h"
|
|
|
|
|
#include "ldpctool/testbench.h"
|
|
|
|
|
#include "ldpctool/algorithms.h"
|
2022-07-18 11:40:00 -04:00
|
|
|
|
#include "ldpctool/ldpcworker.h"
|
2021-03-02 00:02:38 -05:00
|
|
|
|
|
2019-03-17 16:31:42 -04:00
|
|
|
|
namespace leansdr
|
|
|
|
|
{
|
|
|
|
|
|
|
|
|
|
// S2 THRESHOLDS (for comparing demodulators)
|
|
|
|
|
|
2021-03-27 00:38:28 -04:00
|
|
|
|
static const uint32_t S2_MAX_ERR_SOF = 13; // 26 bits
|
|
|
|
|
static const uint64_t S2_MAX_ERR_PLSCODE = 8; // 64 bits, dmin=32
|
2019-03-17 16:31:42 -04:00
|
|
|
|
|
|
|
|
|
static const int pilot_length = 36;
|
|
|
|
|
|
|
|
|
|
// S2 SOF
|
|
|
|
|
// EN 302 307-1 section 5.5.2.1 SOF field
|
|
|
|
|
|
|
|
|
|
template <typename T>
|
|
|
|
|
struct s2_sof
|
|
|
|
|
{
|
|
|
|
|
static const uint32_t VALUE = 0x18d2e82;
|
|
|
|
|
static const uint32_t MASK = 0x3ffffff;
|
|
|
|
|
static const int LENGTH = 26;
|
2021-03-28 23:25:32 -04:00
|
|
|
|
std::complex<T> symbols[LENGTH];
|
2021-03-21 21:06:26 -04:00
|
|
|
|
|
2019-03-17 16:31:42 -04:00
|
|
|
|
s2_sof()
|
|
|
|
|
{
|
|
|
|
|
for (int s = 0; s < LENGTH; ++s)
|
|
|
|
|
{
|
|
|
|
|
int angle = ((VALUE >> (LENGTH - 1 - s)) & 1) * 2 + (s & 1); // pi/2-BPSK
|
2021-03-28 23:25:32 -04:00
|
|
|
|
symbols[s].real(cstln_amp * cosf(M_PI / 4 + 2 * M_PI * angle / 4));
|
|
|
|
|
symbols[s].imag(cstln_amp * sinf(M_PI / 4 + 2 * M_PI * angle / 4));
|
2019-03-17 16:31:42 -04:00
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}; // s2_sof
|
|
|
|
|
|
|
|
|
|
// S2 PLS CODES
|
|
|
|
|
// Precomputes the PLS code sequences.
|
|
|
|
|
// EN 302 307-1 section 5.5.2.4 PLS code
|
|
|
|
|
|
|
|
|
|
template <typename T>
|
|
|
|
|
struct s2_plscodes
|
|
|
|
|
{
|
|
|
|
|
// PLS index format MODCOD[4:0]|SHORTFRAME|PILOTS
|
|
|
|
|
static const int COUNT = 128;
|
|
|
|
|
static const int LENGTH = 64;
|
|
|
|
|
uint64_t codewords[COUNT];
|
2021-03-28 23:25:32 -04:00
|
|
|
|
std::complex<T> symbols[COUNT][LENGTH];
|
2021-03-21 21:06:26 -04:00
|
|
|
|
|
2019-03-17 16:31:42 -04:00
|
|
|
|
s2_plscodes()
|
|
|
|
|
{
|
|
|
|
|
uint32_t G[6] = {0x55555555,
|
|
|
|
|
0x33333333,
|
|
|
|
|
0x0f0f0f0f,
|
|
|
|
|
0x00ff00ff,
|
|
|
|
|
0x0000ffff,
|
|
|
|
|
0xffffffff};
|
2021-03-21 21:06:26 -04:00
|
|
|
|
|
2019-03-17 16:31:42 -04:00
|
|
|
|
for (int index = 0; index < COUNT; ++index)
|
|
|
|
|
{
|
|
|
|
|
uint32_t y = 0;
|
2021-03-21 21:06:26 -04:00
|
|
|
|
|
2019-03-17 16:31:42 -04:00
|
|
|
|
for (int row = 0; row < 6; ++row)
|
2021-03-21 21:06:26 -04:00
|
|
|
|
{
|
|
|
|
|
if ((index >> (6 - row)) & 1) {
|
2019-03-17 16:31:42 -04:00
|
|
|
|
y ^= G[row];
|
2021-03-21 21:06:26 -04:00
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2019-03-17 16:31:42 -04:00
|
|
|
|
uint64_t code = 0;
|
2021-03-21 21:06:26 -04:00
|
|
|
|
|
2019-03-17 16:31:42 -04:00
|
|
|
|
for (int bit = 31; bit >= 0; --bit)
|
|
|
|
|
{
|
|
|
|
|
int yi = (y >> bit) & 1;
|
2021-03-21 21:06:26 -04:00
|
|
|
|
|
|
|
|
|
if (index & 1) {
|
2019-03-17 16:31:42 -04:00
|
|
|
|
code = (code << 2) | (yi << 1) | (yi ^ 1);
|
2021-03-21 21:06:26 -04:00
|
|
|
|
} else {
|
2019-03-17 16:31:42 -04:00
|
|
|
|
code = (code << 2) | (yi << 1) | yi;
|
2021-03-21 21:06:26 -04:00
|
|
|
|
}
|
2019-03-17 16:31:42 -04:00
|
|
|
|
}
|
|
|
|
|
// Scrambling
|
|
|
|
|
code ^= SCRAMBLING;
|
|
|
|
|
// Store precomputed codeword.
|
|
|
|
|
codewords[index] = code;
|
|
|
|
|
// Also store as symbols.
|
2021-03-21 21:06:26 -04:00
|
|
|
|
|
2019-03-17 16:31:42 -04:00
|
|
|
|
for (int i = 0; i < LENGTH; ++i)
|
|
|
|
|
{
|
|
|
|
|
int yi = (code >> (LENGTH - 1 - i)) & 1;
|
|
|
|
|
int nyi = yi ^ (i & 1);
|
2021-03-28 23:25:32 -04:00
|
|
|
|
symbols[index][i].real(cstln_amp * (1 - 2 * nyi) / sqrtf(2));
|
|
|
|
|
symbols[index][i].imag(cstln_amp * (1 - 2 * yi) / sqrtf(2));
|
2019-03-17 16:31:42 -04:00
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
static const uint64_t SCRAMBLING = 0x719d83c953422dfa;
|
|
|
|
|
}; // s2_plscodes
|
|
|
|
|
|
2021-03-24 17:18:22 -04:00
|
|
|
|
static const int PILOT_LENGTH = 36;
|
|
|
|
|
|
|
|
|
|
// Date about pilots.
|
|
|
|
|
// Mostly for consistency with s2_sof and s2_plscodes.
|
|
|
|
|
|
|
|
|
|
template<typename T>
|
|
|
|
|
struct s2_pilot
|
|
|
|
|
{
|
|
|
|
|
static const int LENGTH = PILOT_LENGTH;
|
2021-03-28 23:25:32 -04:00
|
|
|
|
std::complex<T> symbol;
|
2021-03-24 17:18:22 -04:00
|
|
|
|
|
2021-03-28 23:25:32 -04:00
|
|
|
|
s2_pilot()
|
|
|
|
|
{
|
|
|
|
|
symbol.real(cstln_amp*0.707107);
|
|
|
|
|
symbol.imag(cstln_amp*0.707107);
|
2021-03-24 17:18:22 -04:00
|
|
|
|
}
|
|
|
|
|
}; // s2_pilot
|
|
|
|
|
|
2019-03-17 16:31:42 -04:00
|
|
|
|
// S2 SCRAMBLING
|
|
|
|
|
// Precomputes the symbol rotations for PL scrambling.
|
|
|
|
|
// EN 302 307-1 section 5.5.4 Physical layer scrambling
|
|
|
|
|
|
|
|
|
|
struct s2_scrambling
|
|
|
|
|
{
|
|
|
|
|
uint8_t Rn[131072]; // 0..3 (* 2pi/4)
|
2021-03-21 21:06:26 -04:00
|
|
|
|
|
2019-03-17 16:31:42 -04:00
|
|
|
|
s2_scrambling(int codenum = 0)
|
|
|
|
|
{
|
|
|
|
|
uint32_t stx = 0x00001, sty = 0x3ffff;
|
2021-03-21 21:06:26 -04:00
|
|
|
|
|
2019-03-17 16:31:42 -04:00
|
|
|
|
// x starts at codenum, wraps at index 2^18-1 by design
|
2021-03-21 21:06:26 -04:00
|
|
|
|
for (int i = 0; i < codenum; ++i) {
|
2019-03-17 16:31:42 -04:00
|
|
|
|
stx = lfsr_x(stx);
|
2021-03-21 21:06:26 -04:00
|
|
|
|
}
|
|
|
|
|
|
2019-03-17 16:31:42 -04:00
|
|
|
|
// First half of sequence is LSB of scrambling angle
|
|
|
|
|
for (int i = 0; i < 131072; ++i)
|
|
|
|
|
{
|
|
|
|
|
int zn = (stx ^ sty) & 1;
|
|
|
|
|
Rn[i] = zn;
|
|
|
|
|
stx = lfsr_x(stx);
|
|
|
|
|
sty = lfsr_y(sty);
|
|
|
|
|
}
|
2021-03-21 21:06:26 -04:00
|
|
|
|
|
2019-03-17 16:31:42 -04:00
|
|
|
|
// Second half is MSB
|
|
|
|
|
for (int i = 0; i < 131072; ++i)
|
|
|
|
|
{
|
|
|
|
|
int zn = (stx ^ sty) & 1;
|
|
|
|
|
Rn[i] |= zn << 1;
|
|
|
|
|
stx = lfsr_x(stx);
|
|
|
|
|
sty = lfsr_y(sty);
|
|
|
|
|
}
|
|
|
|
|
}
|
2021-03-21 21:06:26 -04:00
|
|
|
|
|
2019-03-17 16:31:42 -04:00
|
|
|
|
uint32_t lfsr_x(uint32_t X)
|
|
|
|
|
{
|
|
|
|
|
int bit = ((X >> 7) ^ X) & 1;
|
|
|
|
|
return ((bit << 18) | X) >> 1;
|
|
|
|
|
}
|
2021-03-21 21:06:26 -04:00
|
|
|
|
|
2019-03-17 16:31:42 -04:00
|
|
|
|
uint32_t lfsr_y(uint32_t Y)
|
|
|
|
|
{
|
|
|
|
|
int bit = ((Y >> 10) ^ (Y >> 7) ^ (Y >> 5) ^ Y) & 1;
|
|
|
|
|
return ((bit << 18) | Y) >> 1;
|
|
|
|
|
}
|
|
|
|
|
}; // s2_scrambling
|
|
|
|
|
|
|
|
|
|
// S2 BBSCRAMBLING
|
|
|
|
|
// Precomputes the xor pattern for baseband scrambling.
|
|
|
|
|
// EN 302 307-1 section 5.2.2 BB scrambling
|
|
|
|
|
|
|
|
|
|
struct s2_bbscrambling
|
|
|
|
|
{
|
|
|
|
|
s2_bbscrambling()
|
|
|
|
|
{
|
|
|
|
|
uint16_t st = 0x00a9; // 000 0000 1010 1001 (Fig 5 reversed)
|
2021-03-21 21:06:26 -04:00
|
|
|
|
|
2020-11-14 16:08:06 -05:00
|
|
|
|
for (unsigned int i = 0; i < sizeof(pattern); ++i)
|
2019-03-17 16:31:42 -04:00
|
|
|
|
{
|
|
|
|
|
uint8_t out = 0;
|
2021-03-21 21:06:26 -04:00
|
|
|
|
|
2019-03-17 16:31:42 -04:00
|
|
|
|
for (int n = 8; n--;)
|
|
|
|
|
{
|
|
|
|
|
int bit = ((st >> 13) ^ (st >> 14)) & 1; // Taps
|
|
|
|
|
out = (out << 1) | bit; // MSB first
|
|
|
|
|
st = (st << 1) | bit; // Feedback
|
|
|
|
|
}
|
2021-03-21 21:06:26 -04:00
|
|
|
|
|
2019-03-17 16:31:42 -04:00
|
|
|
|
pattern[i] = out;
|
|
|
|
|
}
|
|
|
|
|
}
|
2021-03-21 21:06:26 -04:00
|
|
|
|
|
2019-03-17 16:31:42 -04:00
|
|
|
|
void transform(const uint8_t *in, int bbsize, uint8_t *out)
|
|
|
|
|
{
|
2021-03-21 21:06:26 -04:00
|
|
|
|
for (int i = 0; i < bbsize; ++i) {
|
2019-03-17 16:31:42 -04:00
|
|
|
|
out[i] = in[i] ^ pattern[i];
|
2021-03-21 21:06:26 -04:00
|
|
|
|
}
|
2019-03-17 16:31:42 -04:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private:
|
|
|
|
|
uint8_t pattern[58192]; // Values 0..3
|
2021-03-21 21:06:26 -04:00
|
|
|
|
}; // s2_bbscrambling
|
2019-03-17 16:31:42 -04:00
|
|
|
|
|
|
|
|
|
// S2 PHYSICAL LAYER SIGNALLING
|
|
|
|
|
|
|
|
|
|
struct s2_pls
|
|
|
|
|
{
|
|
|
|
|
int modcod; // 0..31
|
|
|
|
|
bool sf;
|
|
|
|
|
bool pilots;
|
2021-03-21 21:06:26 -04:00
|
|
|
|
|
|
|
|
|
int framebits() const {
|
|
|
|
|
return sf ? 16200 : 64800;
|
|
|
|
|
}
|
2021-03-24 17:18:22 -04:00
|
|
|
|
|
|
|
|
|
bool is_dummy() {
|
|
|
|
|
return (modcod==0);
|
|
|
|
|
}
|
2019-03-17 16:31:42 -04:00
|
|
|
|
};
|
|
|
|
|
|
2021-03-24 17:18:22 -04:00
|
|
|
|
static const int PLSLOT_LENGTH = 90;
|
|
|
|
|
|
2019-03-17 16:31:42 -04:00
|
|
|
|
template <typename SOFTSYMB>
|
|
|
|
|
struct plslot
|
|
|
|
|
{
|
2021-03-24 17:18:22 -04:00
|
|
|
|
static const int LENGTH = PLSLOT_LENGTH;
|
2019-03-17 16:31:42 -04:00
|
|
|
|
bool is_pls;
|
|
|
|
|
union {
|
|
|
|
|
s2_pls pls;
|
|
|
|
|
SOFTSYMB symbols[LENGTH];
|
|
|
|
|
};
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
// EN 302 307-1 section 5.5.2.2 MODCOD field
|
|
|
|
|
// EN 302 307-1 section 6 Error performance
|
|
|
|
|
|
|
|
|
|
const struct modcod_info
|
|
|
|
|
{
|
2021-03-25 19:57:52 -04:00
|
|
|
|
static const int MIN_SLOTS_PER_FRAME = 144;
|
|
|
|
|
static const int MIN_SYMBOLS_PER_FRAME =
|
|
|
|
|
(1+MIN_SLOTS_PER_FRAME) * PLSLOT_LENGTH;
|
2019-03-17 16:31:42 -04:00
|
|
|
|
static const int MAX_SLOTS_PER_FRAME = 360;
|
|
|
|
|
static const int MAX_SYMBOLS_PER_FRAME =
|
2021-03-25 19:57:52 -04:00
|
|
|
|
(1 + MAX_SLOTS_PER_FRAME) * PLSLOT_LENGTH +
|
|
|
|
|
((MAX_SLOTS_PER_FRAME - 1) / 16) * PILOT_LENGTH;
|
2019-03-17 16:31:42 -04:00
|
|
|
|
int nslots_nf; // Number of 90-symbol slots per normal frame
|
|
|
|
|
int nsymbols; // Symbols in the constellation
|
|
|
|
|
cstln_base::predef c;
|
|
|
|
|
code_rate rate;
|
|
|
|
|
// Ideal Es/N0 for normal frames
|
|
|
|
|
// EN 302 307 section 6 Error performance
|
|
|
|
|
float esn0_nf;
|
|
|
|
|
// Radii for APSK
|
|
|
|
|
// EN 302 307, section 5.4.3, Table 9
|
|
|
|
|
// EN 302 307, section 5.4.4, Table 10
|
|
|
|
|
float g1, g2, g3;
|
|
|
|
|
} modcod_infos[32] = {
|
2020-11-14 20:19:06 -05:00
|
|
|
|
{0, 0, cstln_base::BPSK, FEC12, 0.0, 0.0, 0.0, 0.0},
|
2019-03-17 16:31:42 -04:00
|
|
|
|
// 1 - 11
|
2020-11-14 20:19:06 -05:00
|
|
|
|
{360, 4, cstln_base::QPSK, FEC14, -2.35, 0.0, 0.0, 0.0},
|
|
|
|
|
{360, 4, cstln_base::QPSK, FEC13, -1.24, 0.0, 0.0, 0.0},
|
|
|
|
|
{360, 4, cstln_base::QPSK, FEC25, -0.30, 0.0, 0.0, 0.0},
|
|
|
|
|
{360, 4, cstln_base::QPSK, FEC12, 1.00, 0.0, 0.0, 0.0},
|
|
|
|
|
{360, 4, cstln_base::QPSK, FEC35, 2.23, 0.0, 0.0, 0.0},
|
|
|
|
|
{360, 4, cstln_base::QPSK, FEC23, 3.10, 0.0, 0.0, 0.0},
|
|
|
|
|
{360, 4, cstln_base::QPSK, FEC34, 4.03, 0.0, 0.0, 0.0},
|
|
|
|
|
{360, 4, cstln_base::QPSK, FEC45, 4.68, 0.0, 0.0, 0.0},
|
|
|
|
|
{360, 4, cstln_base::QPSK, FEC56, 5.18, 0.0, 0.0, 0.0},
|
|
|
|
|
{360, 4, cstln_base::QPSK, FEC89, 6.20, 0.0, 0.0, 0.0},
|
|
|
|
|
{360, 4, cstln_base::QPSK, FEC910, 6.42, 0.0, 0.0, 0.0},
|
2019-03-17 16:31:42 -04:00
|
|
|
|
// 12 - 17
|
2020-11-14 20:19:06 -05:00
|
|
|
|
{240, 8, cstln_base::PSK8, FEC35, 5.50, 0.0, 0.0, 0.0},
|
|
|
|
|
{240, 8, cstln_base::PSK8, FEC23, 6.62, 0.0, 0.0, 0.0},
|
|
|
|
|
{240, 8, cstln_base::PSK8, FEC34, 7.91, 0.0, 0.0, 0.0},
|
|
|
|
|
{240, 8, cstln_base::PSK8, FEC56, 9.35, 0.0, 0.0, 0.0},
|
|
|
|
|
{240, 8, cstln_base::PSK8, FEC89, 10.69, 0.0, 0.0, 0.0},
|
|
|
|
|
{240, 8, cstln_base::PSK8, FEC910, 10.98, 0.0, 0.0, 0.0},
|
2019-03-17 16:31:42 -04:00
|
|
|
|
// 18 - 23
|
2020-11-14 20:19:06 -05:00
|
|
|
|
{180, 16, cstln_base::APSK16, FEC23, 8.97, 3.15, 0.0, 0.0},
|
|
|
|
|
{180, 16, cstln_base::APSK16, FEC34, 10.21, 2.85, 0.0, 0.0},
|
|
|
|
|
{180, 16, cstln_base::APSK16, FEC45, 11.03, 2.75, 0.0, 0.0},
|
|
|
|
|
{180, 16, cstln_base::APSK16, FEC56, 11.61, 2.70, 0.0, 0.0},
|
|
|
|
|
{180, 16, cstln_base::APSK16, FEC89, 12.89, 2.60, 0.0, 0.0},
|
|
|
|
|
{180, 16, cstln_base::APSK16, FEC910, 13.13, 2.57, 0.0, 0.0},
|
2019-03-17 16:31:42 -04:00
|
|
|
|
// 24 - 28
|
2020-11-14 20:19:06 -05:00
|
|
|
|
{144, 32, cstln_base::APSK32, FEC34, 12.73, 2.84, 5.27, 0.0},
|
|
|
|
|
{144, 32, cstln_base::APSK32, FEC45, 13.64, 2.72, 4.87, 0.0},
|
|
|
|
|
{144, 32, cstln_base::APSK32, FEC56, 14.28, 2.64, 4.64, 0.0},
|
|
|
|
|
{144, 32, cstln_base::APSK32, FEC89, 15.69, 2.54, 4.33, 0.0},
|
|
|
|
|
{144, 32, cstln_base::APSK32, FEC910, 16.05, 2.53, 4.30, 0.0},
|
2019-03-17 16:31:42 -04:00
|
|
|
|
// 29 - 31
|
2020-11-14 20:19:06 -05:00
|
|
|
|
{0, 0, cstln_base::BPSK, FEC12, 0.0, 0.0, 0.0, 0.0},
|
|
|
|
|
{0, 0, cstln_base::BPSK, FEC12, 0.0, 0.0, 0.0, 0.0},
|
|
|
|
|
{0, 0, cstln_base::BPSK, FEC12, 0.0, 0.0, 0.0, 0.0}
|
|
|
|
|
};
|
2019-03-17 16:31:42 -04:00
|
|
|
|
|
|
|
|
|
// Assert that a MODCOD number is valid
|
|
|
|
|
const modcod_info *check_modcod(int m)
|
|
|
|
|
{
|
2021-03-21 21:06:26 -04:00
|
|
|
|
if (m < 0 || m > 31) {
|
2019-03-17 16:31:42 -04:00
|
|
|
|
fail("Invalid MODCOD number");
|
2021-03-21 21:06:26 -04:00
|
|
|
|
}
|
|
|
|
|
|
2019-03-17 16:31:42 -04:00
|
|
|
|
const modcod_info *r = &modcod_infos[m];
|
2021-03-21 21:06:26 -04:00
|
|
|
|
|
|
|
|
|
if (!r->nslots_nf) {
|
2019-03-17 16:31:42 -04:00
|
|
|
|
fail("Unsupported MODCOD");
|
2021-03-21 21:06:26 -04:00
|
|
|
|
}
|
|
|
|
|
|
2019-03-17 16:31:42 -04:00
|
|
|
|
return r;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// S2 FRAME TRANSMITTER
|
|
|
|
|
|
|
|
|
|
template <typename T>
|
|
|
|
|
struct s2_frame_transmitter : runnable
|
|
|
|
|
{
|
2021-03-12 18:13:00 -05:00
|
|
|
|
s2_frame_transmitter(
|
|
|
|
|
scheduler *sch,
|
|
|
|
|
pipebuf<plslot<hard_ss>> &_in,
|
2021-03-28 23:25:32 -04:00
|
|
|
|
pipebuf<std::complex<T>> &_out
|
2021-03-12 18:13:00 -05:00
|
|
|
|
) :
|
|
|
|
|
runnable(sch, "S2 frame transmitter"),
|
|
|
|
|
in(_in),
|
2021-03-24 17:18:22 -04:00
|
|
|
|
out(_out, modcod_info::MAX_SYMBOLS_PER_FRAME)
|
2019-03-17 16:31:42 -04:00
|
|
|
|
{
|
|
|
|
|
float amp = cstln_amp / sqrtf(2);
|
2021-03-28 23:25:32 -04:00
|
|
|
|
qsymbols[0].real() = +amp;
|
|
|
|
|
qsymbols[0].imag() = +amp;
|
|
|
|
|
qsymbols[1].real() = +amp;
|
|
|
|
|
qsymbols[1].imag() = -amp;
|
|
|
|
|
qsymbols[2].real() = -amp;
|
|
|
|
|
qsymbols[2].imag() = +amp;
|
|
|
|
|
qsymbols[3].real() = -amp;
|
|
|
|
|
qsymbols[3].imag() = -amp;
|
2021-03-24 17:18:22 -04:00
|
|
|
|
|
|
|
|
|
// Clear the constellation cache.
|
|
|
|
|
for (int i = 0; i < 32; ++i) {
|
|
|
|
|
pcsymbols[i] = nullptr;
|
|
|
|
|
}
|
2019-03-17 16:31:42 -04:00
|
|
|
|
}
|
2021-03-21 21:06:26 -04:00
|
|
|
|
|
|
|
|
|
~s2_frame_transmitter()
|
|
|
|
|
{
|
|
|
|
|
}
|
|
|
|
|
|
2019-03-17 16:31:42 -04:00
|
|
|
|
void run()
|
|
|
|
|
{
|
|
|
|
|
while (in.readable() >= 1)
|
|
|
|
|
{
|
|
|
|
|
plslot<hard_ss> *pin = in.rd();
|
2021-03-21 21:06:26 -04:00
|
|
|
|
|
|
|
|
|
if (!pin->is_pls) {
|
2019-03-17 16:31:42 -04:00
|
|
|
|
fail("Expected PLS pseudo-slot");
|
2021-03-21 21:06:26 -04:00
|
|
|
|
}
|
|
|
|
|
|
2019-03-17 16:31:42 -04:00
|
|
|
|
s2_pls *pls = &pin->pls;
|
|
|
|
|
const modcod_info *mcinfo = check_modcod(pls->modcod);
|
|
|
|
|
int nslots = (pls->sf ? mcinfo->nslots_nf / 4 : mcinfo->nslots_nf);
|
2021-03-21 21:06:26 -04:00
|
|
|
|
|
|
|
|
|
if (in.readable() < 1 + nslots) {
|
2019-03-17 16:31:42 -04:00
|
|
|
|
break;
|
2021-03-21 21:06:26 -04:00
|
|
|
|
}
|
|
|
|
|
|
2019-03-17 16:31:42 -04:00
|
|
|
|
// Require room for BBHEADER + slots + optional pilots.
|
|
|
|
|
int nsymbols = ((1 + nslots) * plslot<hard_ss>::LENGTH +
|
|
|
|
|
(pls->pilots ? ((nslots - 1) / 16) * pilot_length : 0));
|
2021-03-21 21:06:26 -04:00
|
|
|
|
|
|
|
|
|
if (out.writable() < nsymbols) {
|
2019-03-17 16:31:42 -04:00
|
|
|
|
break;
|
2021-03-21 21:06:26 -04:00
|
|
|
|
}
|
|
|
|
|
|
2019-03-17 16:31:42 -04:00
|
|
|
|
int nw = run_frame(pls, mcinfo, pin + 1, nslots, out.wr());
|
2021-03-21 21:06:26 -04:00
|
|
|
|
|
|
|
|
|
if (nw != nsymbols) {
|
2019-03-17 16:31:42 -04:00
|
|
|
|
fail("Bug: s2_frame_transmitter overflow");
|
2021-03-21 21:06:26 -04:00
|
|
|
|
}
|
|
|
|
|
|
2019-03-17 16:31:42 -04:00
|
|
|
|
in.read(1 + nslots);
|
|
|
|
|
out.written(nsymbols);
|
|
|
|
|
}
|
|
|
|
|
}
|
2021-03-21 21:06:26 -04:00
|
|
|
|
|
|
|
|
|
int run_frame(
|
|
|
|
|
s2_pls *pls,
|
|
|
|
|
const modcod_info *mcinfo,
|
|
|
|
|
const plslot<hard_ss> *pin,
|
|
|
|
|
int nslots,
|
2021-03-28 23:25:32 -04:00
|
|
|
|
std::complex<T> *pout
|
2021-03-21 21:06:26 -04:00
|
|
|
|
)
|
2019-03-17 16:31:42 -04:00
|
|
|
|
{
|
2021-03-28 23:25:32 -04:00
|
|
|
|
std::complex<T> *pout0 = pout; // For sanity check
|
2019-03-17 16:31:42 -04:00
|
|
|
|
// PLHEADER: SOF AND PLSCODE
|
|
|
|
|
// EN 302 307-1 section 5.5.2 PL signalling
|
|
|
|
|
memcpy(pout, sof.symbols, sof.LENGTH * sizeof(*pout));
|
|
|
|
|
pout += sof.LENGTH;
|
|
|
|
|
int pls_index = (pls->modcod << 2) | (pls->sf << 1) | pls->pilots;
|
|
|
|
|
memcpy(pout, plscodes.symbols[pls_index], plscodes.LENGTH * sizeof(*pout));
|
|
|
|
|
pout += plscodes.LENGTH;
|
2021-03-28 23:25:32 -04:00
|
|
|
|
std::complex<T> *csymbols = get_csymbols(pls->modcod);
|
2019-03-17 16:31:42 -04:00
|
|
|
|
// Slots and pilots
|
|
|
|
|
int till_next_pilot = pls->pilots ? 16 : nslots;
|
|
|
|
|
uint8_t *scr = &scrambling.Rn[0];
|
2021-03-21 21:06:26 -04:00
|
|
|
|
|
2019-03-17 16:31:42 -04:00
|
|
|
|
for (int S = 0; S < nslots; ++S, ++pin, --till_next_pilot)
|
|
|
|
|
{
|
|
|
|
|
if (till_next_pilot == 0)
|
|
|
|
|
{
|
|
|
|
|
// Send pilot
|
2021-03-21 21:06:26 -04:00
|
|
|
|
for (int s = 0; s < pilot_length; ++s, ++scr, ++pout) {
|
2019-03-17 16:31:42 -04:00
|
|
|
|
scramble(&qsymbols[0], *scr, pout);
|
2021-03-21 21:06:26 -04:00
|
|
|
|
}
|
2019-03-17 16:31:42 -04:00
|
|
|
|
till_next_pilot = 16;
|
|
|
|
|
}
|
2021-03-21 21:06:26 -04:00
|
|
|
|
|
2019-03-17 16:31:42 -04:00
|
|
|
|
// Send slot
|
2021-03-21 21:06:26 -04:00
|
|
|
|
if (pin->is_pls) {
|
2019-03-17 16:31:42 -04:00
|
|
|
|
fail("s2_frame_transmitter: bad input sequence");
|
2021-03-21 21:06:26 -04:00
|
|
|
|
}
|
|
|
|
|
|
2019-03-17 16:31:42 -04:00
|
|
|
|
const hard_ss *ps = pin->symbols;
|
2021-03-21 21:06:26 -04:00
|
|
|
|
|
|
|
|
|
for (int s = 0; s < pin->LENGTH; ++s, ++ps, ++scr, ++pout) {
|
2019-03-17 16:31:42 -04:00
|
|
|
|
scramble(&csymbols[*ps], *scr, pout);
|
2021-03-21 21:06:26 -04:00
|
|
|
|
}
|
2019-03-17 16:31:42 -04:00
|
|
|
|
}
|
2021-03-21 21:06:26 -04:00
|
|
|
|
|
2019-03-17 16:31:42 -04:00
|
|
|
|
return pout - pout0;
|
|
|
|
|
}
|
2021-03-21 21:06:26 -04:00
|
|
|
|
|
2021-03-28 23:25:32 -04:00
|
|
|
|
inline void scramble(const std::complex<T> *src, uint8_t r, std::complex<T> *dst)
|
2019-03-17 16:31:42 -04:00
|
|
|
|
{
|
|
|
|
|
switch (r)
|
|
|
|
|
{
|
|
|
|
|
case 3:
|
|
|
|
|
dst->re = src->im;
|
|
|
|
|
dst->im = -src->re;
|
|
|
|
|
break;
|
|
|
|
|
case 2:
|
|
|
|
|
dst->re = -src->re;
|
|
|
|
|
dst->im = -src->im;
|
|
|
|
|
break;
|
|
|
|
|
case 1:
|
|
|
|
|
dst->re = -src->im;
|
|
|
|
|
dst->im = src->re;
|
|
|
|
|
break;
|
|
|
|
|
default:
|
|
|
|
|
*dst = *src;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private:
|
|
|
|
|
pipereader<plslot<hard_ss>> in;
|
2021-03-28 23:25:32 -04:00
|
|
|
|
pipewriter<std::complex<T>> out;
|
|
|
|
|
std::complex<T> *pcsymbols[32]; // Constellations in use, indexed by modcod
|
2021-03-21 21:06:26 -04:00
|
|
|
|
|
2021-03-28 23:25:32 -04:00
|
|
|
|
std::complex<T> *get_csymbols(int modcod)
|
2019-03-17 16:31:42 -04:00
|
|
|
|
{
|
2021-03-24 17:18:22 -04:00
|
|
|
|
if (!pcsymbols[modcod])
|
2019-03-17 16:31:42 -04:00
|
|
|
|
{
|
2021-03-24 17:18:22 -04:00
|
|
|
|
const modcod_info *mcinfo = check_modcod(modcod);
|
2021-03-21 21:06:26 -04:00
|
|
|
|
|
2021-03-24 17:18:22 -04:00
|
|
|
|
if (sch->debug)
|
|
|
|
|
{
|
|
|
|
|
fprintf(
|
|
|
|
|
stderr,
|
|
|
|
|
"Building constellation %s ratecode %d\n",
|
|
|
|
|
cstln_base::names[mcinfo->c],
|
|
|
|
|
mcinfo->rate
|
|
|
|
|
);
|
2021-03-21 21:06:26 -04:00
|
|
|
|
}
|
|
|
|
|
|
2019-03-17 16:31:42 -04:00
|
|
|
|
// TBD Different Es/N0 for short frames ?
|
2021-03-24 17:18:22 -04:00
|
|
|
|
cstln_lut<hard_ss,256> cstln(
|
2021-03-21 21:06:26 -04:00
|
|
|
|
mcinfo->c,
|
|
|
|
|
mcinfo->esn0_nf,
|
|
|
|
|
mcinfo->g1,
|
|
|
|
|
mcinfo->g2,
|
|
|
|
|
mcinfo->g3
|
|
|
|
|
);
|
|
|
|
|
|
2021-03-28 23:25:32 -04:00
|
|
|
|
pcsymbols[modcod] = new std::complex<T>[cstln.nsymbols];
|
2021-03-21 21:06:26 -04:00
|
|
|
|
|
2021-03-24 17:18:22 -04:00
|
|
|
|
for ( int s=0; s<cstln.nsymbols; ++s )
|
2019-03-17 16:31:42 -04:00
|
|
|
|
{
|
2021-03-28 23:25:32 -04:00
|
|
|
|
pcsymbols[modcod][s].real() = cstln.symbols[s].real();
|
|
|
|
|
pcsymbols[modcod][s].imag() = cstln.symbols[s].imag();
|
2019-03-17 16:31:42 -04:00
|
|
|
|
}
|
|
|
|
|
}
|
2021-03-24 17:18:22 -04:00
|
|
|
|
|
|
|
|
|
return pcsymbols[modcod];
|
2019-03-17 16:31:42 -04:00
|
|
|
|
}
|
2021-03-21 21:06:26 -04:00
|
|
|
|
|
2021-03-28 23:25:32 -04:00
|
|
|
|
std::complex<T> qsymbols[4]; // RMS cstln_amp
|
2019-03-17 16:31:42 -04:00
|
|
|
|
s2_sof<T> sof;
|
|
|
|
|
s2_plscodes<T> plscodes;
|
|
|
|
|
s2_scrambling scrambling;
|
|
|
|
|
}; // s2_frame_transmitter
|
|
|
|
|
|
|
|
|
|
// S2 FRAME RECEIVER
|
|
|
|
|
|
|
|
|
|
#define TEST_DIVERSITY 0
|
|
|
|
|
|
2021-03-24 17:18:22 -04:00
|
|
|
|
template<typename T, typename SOFTSYMB>
|
2019-03-17 16:31:42 -04:00
|
|
|
|
struct s2_frame_receiver : runnable
|
|
|
|
|
{
|
|
|
|
|
sampler_interface<T> *sampler;
|
|
|
|
|
int meas_decimation;
|
2021-03-24 17:18:22 -04:00
|
|
|
|
float Ftune; // Tuning bias in cycles per symbol
|
|
|
|
|
bool allow_drift; // Unbounded carrier tracking
|
|
|
|
|
float omega0; // Samples per symbol
|
|
|
|
|
bool strongpls; // PL symbols at max amplitude (default: RMS)
|
|
|
|
|
uint32_t modcods; // Bitmask of desired modcods
|
|
|
|
|
uint8_t framesizes; // Bitmask of desired frame sizes
|
2024-07-10 16:37:52 -04:00
|
|
|
|
bool fastlock; // Synchronize more aggressively
|
2021-03-24 17:18:22 -04:00
|
|
|
|
bool fastdrift; // Carrier drift faster than pilots
|
|
|
|
|
float freq_tol; // Tolerance on carrier frequency
|
|
|
|
|
float sr_tol; // Tolerance on symbol rate
|
2021-03-12 18:13:00 -05:00
|
|
|
|
|
|
|
|
|
s2_frame_receiver(
|
|
|
|
|
scheduler *sch,
|
|
|
|
|
sampler_interface<T> *_sampler,
|
2021-03-28 23:25:32 -04:00
|
|
|
|
pipebuf< std::complex<T> > &_in,
|
2021-03-24 17:18:22 -04:00
|
|
|
|
pipebuf< plslot<SOFTSYMB> > &_out,
|
|
|
|
|
pipebuf<float> *_freq_out=NULL,
|
|
|
|
|
pipebuf<float> *_ss_out=NULL,
|
|
|
|
|
pipebuf<float> *_mer_out=NULL,
|
2021-03-28 23:25:32 -04:00
|
|
|
|
pipebuf< std::complex<float> > *_cstln_out=NULL,
|
|
|
|
|
pipebuf< std::complex<float> > *_cstln_pls_out=NULL,
|
|
|
|
|
pipebuf< std::complex<float> > *_symbols_out=NULL,
|
2021-03-24 17:18:22 -04:00
|
|
|
|
pipebuf<int> *_state_out=NULL
|
2021-03-12 18:13:00 -05:00
|
|
|
|
) :
|
|
|
|
|
runnable(sch, "S2 frame receiver"),
|
|
|
|
|
sampler(_sampler),
|
|
|
|
|
meas_decimation(1048576),
|
2021-03-24 17:18:22 -04:00
|
|
|
|
Ftune(0),
|
|
|
|
|
allow_drift(false),
|
2021-03-12 18:13:00 -05:00
|
|
|
|
strongpls(false),
|
2021-03-24 17:18:22 -04:00
|
|
|
|
modcods(0xffffffff),
|
|
|
|
|
framesizes(0x03),
|
|
|
|
|
fastlock(false),
|
|
|
|
|
fastdrift(false),
|
|
|
|
|
freq_tol(0.25),
|
|
|
|
|
sr_tol(100e-6),
|
|
|
|
|
cstln(NULL),
|
|
|
|
|
in(_in), out(_out,1+modcod_info::MAX_SLOTS_PER_FRAME),
|
2021-03-12 18:13:00 -05:00
|
|
|
|
meas_count(0),
|
|
|
|
|
freq_out(opt_writer(_freq_out)),
|
|
|
|
|
ss_out(opt_writer(_ss_out)),
|
|
|
|
|
mer_out(opt_writer(_mer_out)),
|
2021-03-24 17:18:22 -04:00
|
|
|
|
cstln_out(opt_writer(_cstln_out,1024)),
|
|
|
|
|
cstln_pls_out(opt_writer(_cstln_pls_out,1024)),
|
2021-03-25 19:57:52 -04:00
|
|
|
|
symbols_out(opt_writer(_symbols_out, modcod_info::MAX_SYMBOLS_PER_FRAME)),
|
2021-03-12 18:13:00 -05:00
|
|
|
|
state_out(opt_writer(_state_out)),
|
2021-03-24 17:18:22 -04:00
|
|
|
|
first_run(true),
|
2021-03-12 18:13:00 -05:00
|
|
|
|
scrambling(0),
|
2021-03-24 17:18:22 -04:00
|
|
|
|
pls_total_errors(0),
|
|
|
|
|
pls_total_count(0),
|
2021-03-12 18:13:00 -05:00
|
|
|
|
m_modcodType(-1),
|
2021-03-24 17:18:22 -04:00
|
|
|
|
m_modcodRate(-1),
|
2021-03-28 22:02:22 -04:00
|
|
|
|
m_locked(false),
|
2021-03-24 17:18:22 -04:00
|
|
|
|
diffs(nullptr),
|
|
|
|
|
sspilots(nullptr)
|
2019-03-17 16:31:42 -04:00
|
|
|
|
{
|
|
|
|
|
// Constellation for PLS
|
2021-03-24 17:18:22 -04:00
|
|
|
|
qpsk = new cstln_lut<SOFTSYMB,256>(cstln_base::QPSK);
|
|
|
|
|
|
|
|
|
|
// Clear the constellation cache.
|
|
|
|
|
for (int i=0; i<32; ++i) {
|
|
|
|
|
cstlns[i] = NULL;
|
|
|
|
|
}
|
|
|
|
|
|
2019-03-17 16:31:42 -04:00
|
|
|
|
#if TEST_DIVERSITY
|
|
|
|
|
fprintf(stderr, "** DEBUG: Diversity test mode (slower)\n");
|
|
|
|
|
#endif
|
|
|
|
|
}
|
|
|
|
|
|
2021-03-12 18:13:00 -05:00
|
|
|
|
~s2_frame_receiver()
|
2019-03-17 16:31:42 -04:00
|
|
|
|
{
|
2021-03-12 18:13:00 -05:00
|
|
|
|
delete qpsk;
|
2021-03-21 21:06:26 -04:00
|
|
|
|
|
2021-03-24 17:18:22 -04:00
|
|
|
|
for (int i=0; i<32; ++i)
|
|
|
|
|
{
|
|
|
|
|
if (cstlns[i]) {
|
|
|
|
|
delete cstlns[i];
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (diffs) {
|
|
|
|
|
delete[] diffs;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (sspilots) {
|
|
|
|
|
delete[] sspilots;
|
2021-03-21 21:06:26 -04:00
|
|
|
|
}
|
2021-03-12 18:13:00 -05:00
|
|
|
|
}
|
2019-03-17 16:31:42 -04:00
|
|
|
|
|
2021-03-24 17:18:22 -04:00
|
|
|
|
enum {
|
|
|
|
|
FRAME_DETECT, // Looking for PLHEADER
|
|
|
|
|
FRAME_PROBE, // Aligned with PLHEADER, ready to recover carrier
|
|
|
|
|
FRAME_LOCKED, // Demodulating
|
|
|
|
|
} state;
|
|
|
|
|
|
|
|
|
|
// sampler_state holds the entire state of the PLL.
|
|
|
|
|
// Useful for looking ahead (e.g. next pilots/SOF) then rewinding.
|
|
|
|
|
|
|
|
|
|
struct sampler_state {
|
2021-03-28 23:25:32 -04:00
|
|
|
|
std::complex<T> *p; // Pointer to samples (update when entering run())
|
2021-03-24 17:18:22 -04:00
|
|
|
|
float mu; // Time of next symbol, counted from p
|
|
|
|
|
float omega; // Samples per symbol
|
|
|
|
|
float gain; // Scaling factor toward cstln_amp
|
|
|
|
|
float ph16; // Carrier phase at next symbol (cycles * 65536)
|
|
|
|
|
float fw16; // Carrier frequency (cycles per symbol * 65536)
|
|
|
|
|
uint8_t *scr; // Position in scrambling sequence for next symbol
|
|
|
|
|
|
|
|
|
|
void normalize() {
|
|
|
|
|
ph16 = fmodf(ph16, 65536.0f); // Rounding direction irrelevant
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void skip_symbols(int ns)
|
|
|
|
|
{
|
|
|
|
|
mu += omega * ns;
|
|
|
|
|
p += (int)floorf(mu);
|
|
|
|
|
mu -= floorf(mu);
|
|
|
|
|
ph16 += fw16 * ns;
|
|
|
|
|
normalize();
|
|
|
|
|
scr += ns;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
char *format() {
|
|
|
|
|
static char buf[256];
|
|
|
|
|
sprintf(
|
|
|
|
|
buf,
|
|
|
|
|
"%9.2lf %+6.0f ppm %+3.0f ° %f",
|
2021-03-28 23:25:32 -04:00
|
|
|
|
(double)((p-(std::complex<T>*)NULL)&262143)+mu, // Arbitrary wrap
|
2021-03-24 17:18:22 -04:00
|
|
|
|
fw16*1e6/65536,
|
|
|
|
|
fmodfs(ph16,65536.0f)*360/65536,
|
|
|
|
|
gain
|
|
|
|
|
);
|
|
|
|
|
return buf;
|
|
|
|
|
}
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
float min_freqw16, max_freqw16;
|
|
|
|
|
|
|
|
|
|
// State during FRAME_SEARCH and FRAME_LOCKED
|
|
|
|
|
sampler_state ss_cache;
|
|
|
|
|
|
2019-03-17 16:31:42 -04:00
|
|
|
|
void run()
|
|
|
|
|
{
|
2021-03-24 17:18:22 -04:00
|
|
|
|
if (strongpls) {
|
|
|
|
|
fail("--strongpls is broken.");
|
|
|
|
|
}
|
|
|
|
|
|
2019-03-17 16:31:42 -04:00
|
|
|
|
// Require enough samples to detect one plheader,
|
|
|
|
|
// TBD margin ?
|
2021-03-25 19:57:52 -04:00
|
|
|
|
int min_samples = (1 + modcod_info::MAX_SYMBOLS_PER_FRAME +
|
2021-03-24 17:18:22 -04:00
|
|
|
|
sof.LENGTH+plscodes.LENGTH)*omega0 * 2;
|
|
|
|
|
|
2019-03-17 16:31:42 -04:00
|
|
|
|
while (in.readable() >= min_samples + sampler->readahead() &&
|
2021-03-24 17:18:22 -04:00
|
|
|
|
out.writable() >= 1+modcod_info::MAX_SLOTS_PER_FRAME &&
|
|
|
|
|
opt_writable(freq_out, 1) &&
|
|
|
|
|
opt_writable(ss_out, 1) &&
|
|
|
|
|
opt_writable(mer_out, 1) &&
|
2021-03-25 19:57:52 -04:00
|
|
|
|
opt_writable(symbols_out, modcod_info::MAX_SYMBOLS_PER_FRAME) &&
|
2021-03-24 17:18:22 -04:00
|
|
|
|
opt_writable(state_out, 1))
|
|
|
|
|
{
|
|
|
|
|
if (first_run)
|
2019-03-17 16:31:42 -04:00
|
|
|
|
{
|
2021-03-24 17:18:22 -04:00
|
|
|
|
enter_frame_detect();
|
|
|
|
|
first_run = false;
|
2019-03-17 16:31:42 -04:00
|
|
|
|
}
|
2021-03-24 17:18:22 -04:00
|
|
|
|
|
|
|
|
|
switch ( state )
|
2019-03-17 16:31:42 -04:00
|
|
|
|
{
|
2021-03-24 17:18:22 -04:00
|
|
|
|
case FRAME_DETECT:
|
|
|
|
|
run_frame_detect();
|
2019-03-17 16:31:42 -04:00
|
|
|
|
break;
|
2021-03-24 17:18:22 -04:00
|
|
|
|
case FRAME_PROBE:
|
|
|
|
|
run_frame_probe();
|
2019-03-17 16:31:42 -04:00
|
|
|
|
break;
|
|
|
|
|
case FRAME_LOCKED:
|
|
|
|
|
run_frame_locked();
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2024-07-10 16:37:52 -04:00
|
|
|
|
// State transition
|
2021-03-24 17:18:22 -04:00
|
|
|
|
void enter_frame_detect()
|
2019-03-17 16:31:42 -04:00
|
|
|
|
{
|
2021-03-24 17:18:22 -04:00
|
|
|
|
state = FRAME_DETECT;
|
|
|
|
|
// Setup sampler for interpolation only.
|
|
|
|
|
ss_cache.fw16 = 65536 * Ftune;
|
|
|
|
|
ss_cache.ph16 = 0;
|
|
|
|
|
ss_cache.mu = 0;
|
|
|
|
|
ss_cache.gain = 1;
|
|
|
|
|
ss_cache.omega = omega0;
|
2021-03-21 21:06:26 -04:00
|
|
|
|
|
2021-03-24 17:18:22 -04:00
|
|
|
|
// Set frequency tracking boundaries around tuning frequency.
|
|
|
|
|
if (allow_drift)
|
2019-03-17 16:31:42 -04:00
|
|
|
|
{
|
2021-03-24 17:18:22 -04:00
|
|
|
|
// Track across the whole baseband.
|
|
|
|
|
min_freqw16 = ss_cache.fw16 - omega0*65536;
|
|
|
|
|
max_freqw16 = ss_cache.fw16 + omega0*65536;
|
2019-03-17 16:31:42 -04:00
|
|
|
|
}
|
2021-03-24 17:18:22 -04:00
|
|
|
|
else
|
2019-03-17 16:31:42 -04:00
|
|
|
|
{
|
2021-03-24 17:18:22 -04:00
|
|
|
|
min_freqw16 = ss_cache.fw16 - freq_tol*65536.0;
|
|
|
|
|
max_freqw16 = ss_cache.fw16 + freq_tol*65536.0;
|
2019-03-17 16:31:42 -04:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
opt_write(state_out, 0);
|
2021-03-21 21:06:26 -04:00
|
|
|
|
|
|
|
|
|
if (sch->debug) {
|
2021-03-28 12:40:43 -04:00
|
|
|
|
fprintf(stderr, "enter_frame_detect\n");
|
2021-03-21 21:06:26 -04:00
|
|
|
|
}
|
|
|
|
|
|
2021-03-24 17:18:22 -04:00
|
|
|
|
if (fastlock || first_run)
|
|
|
|
|
{
|
|
|
|
|
discard = 0;
|
2021-03-21 21:06:26 -04:00
|
|
|
|
}
|
2021-03-24 17:18:22 -04:00
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
// Discard some data so that CPU usage during PLHEADER detection
|
|
|
|
|
// is at same level as during steady-state demodulation.
|
|
|
|
|
// This has no effect if the first detection is successful.
|
|
|
|
|
float duty_factor = 5;
|
2021-03-27 06:55:24 -04:00
|
|
|
|
discard = modcod_info::MAX_SYMBOLS_PER_FRAME * omega0 * (duty_factor+rand_compat()-0.5);
|
2021-03-21 21:06:26 -04:00
|
|
|
|
}
|
2021-03-24 17:18:22 -04:00
|
|
|
|
}
|
2021-03-21 21:06:26 -04:00
|
|
|
|
|
2021-03-24 17:18:22 -04:00
|
|
|
|
long discard;
|
2019-03-17 16:31:42 -04:00
|
|
|
|
|
2021-03-24 17:18:22 -04:00
|
|
|
|
void run_frame_detect()
|
|
|
|
|
{
|
2021-03-30 23:03:10 -04:00
|
|
|
|
cstln->m_rateCode = -1;
|
|
|
|
|
cstln->m_typeCode = -1;
|
|
|
|
|
cstln->m_setByModcod = cstln->m_typeCode != -1;
|
|
|
|
|
|
|
|
|
|
if (discard)
|
2019-03-17 16:31:42 -04:00
|
|
|
|
{
|
2021-03-24 17:18:22 -04:00
|
|
|
|
size_t d = std::min(discard, in.readable());
|
|
|
|
|
in.read(d);
|
|
|
|
|
discard -= d;
|
|
|
|
|
return;
|
2019-03-17 16:31:42 -04:00
|
|
|
|
}
|
|
|
|
|
|
2021-03-24 17:18:22 -04:00
|
|
|
|
sampler->update_freq(ss_cache.fw16/omega0);
|
2021-03-21 21:06:26 -04:00
|
|
|
|
|
2021-03-25 19:57:52 -04:00
|
|
|
|
const int search_range = modcod_info::MAX_SYMBOLS_PER_FRAME;
|
2021-03-24 17:18:22 -04:00
|
|
|
|
ss_cache.p = in.rd();
|
|
|
|
|
find_plheader(&ss_cache, search_range);
|
|
|
|
|
#if DEBUG_CARRIER
|
|
|
|
|
fprintf(stderr, "CARRIER diffcorr: %.0f%% %s\n",
|
|
|
|
|
q*100, ss_cache.format());
|
|
|
|
|
#endif
|
|
|
|
|
in.read(ss_cache.p-in.rd());
|
|
|
|
|
enter_frame_probe();
|
2019-03-17 16:31:42 -04:00
|
|
|
|
}
|
|
|
|
|
|
2021-03-24 17:18:22 -04:00
|
|
|
|
void enter_frame_probe()
|
2019-03-17 16:31:42 -04:00
|
|
|
|
{
|
2021-03-21 21:06:26 -04:00
|
|
|
|
if (sch->debug) {
|
2021-03-24 17:18:22 -04:00
|
|
|
|
fprintf(stderr, "PROBE\n");
|
2021-03-21 21:06:26 -04:00
|
|
|
|
}
|
|
|
|
|
|
2021-03-28 22:02:22 -04:00
|
|
|
|
if (m_locked)
|
|
|
|
|
{
|
|
|
|
|
fprintf(stderr, "UNLOCKED\n");
|
|
|
|
|
m_locked = false;
|
|
|
|
|
}
|
|
|
|
|
|
2021-03-24 17:18:22 -04:00
|
|
|
|
state = FRAME_PROBE;
|
2019-03-17 16:31:42 -04:00
|
|
|
|
}
|
|
|
|
|
|
2021-03-24 17:18:22 -04:00
|
|
|
|
void run_frame_probe() {
|
|
|
|
|
return run_frame_probe_locked();
|
|
|
|
|
}
|
2019-03-17 16:31:42 -04:00
|
|
|
|
|
2021-03-24 17:18:22 -04:00
|
|
|
|
void enter_frame_locked()
|
2019-03-17 16:31:42 -04:00
|
|
|
|
{
|
2021-03-24 17:18:22 -04:00
|
|
|
|
state = FRAME_LOCKED;
|
2021-03-21 21:06:26 -04:00
|
|
|
|
|
2021-03-24 17:18:22 -04:00
|
|
|
|
if (sch->debug) {
|
|
|
|
|
fprintf(stderr, "LOCKED\n");
|
2019-03-17 16:31:42 -04:00
|
|
|
|
}
|
2021-03-21 21:06:26 -04:00
|
|
|
|
|
2021-03-28 22:02:22 -04:00
|
|
|
|
if (!m_locked)
|
|
|
|
|
{
|
|
|
|
|
fprintf(stderr, "LOCKED\n");
|
|
|
|
|
m_locked = true;
|
|
|
|
|
}
|
|
|
|
|
|
2021-03-24 17:18:22 -04:00
|
|
|
|
opt_write(state_out, 1);
|
|
|
|
|
}
|
2019-03-17 16:31:42 -04:00
|
|
|
|
|
2021-03-24 17:18:22 -04:00
|
|
|
|
void run_frame_locked() {
|
|
|
|
|
return run_frame_probe_locked();
|
2019-03-17 16:31:42 -04:00
|
|
|
|
}
|
|
|
|
|
|
2021-03-24 17:18:22 -04:00
|
|
|
|
// Process one frame.
|
|
|
|
|
// Perform additional carrier estimation if state==FRAME_PROBE.
|
|
|
|
|
|
|
|
|
|
void run_frame_probe_locked()
|
2019-03-17 16:31:42 -04:00
|
|
|
|
{
|
2021-03-28 23:25:32 -04:00
|
|
|
|
std::complex<float> *psampled; // Data symbols (one per slot)
|
2021-03-24 17:18:22 -04:00
|
|
|
|
|
|
|
|
|
if (cstln_out && cstln_out->writable()>=1024) {
|
2019-03-17 16:31:42 -04:00
|
|
|
|
psampled = cstln_out->wr();
|
2021-03-21 21:06:26 -04:00
|
|
|
|
} else {
|
2021-03-24 17:18:22 -04:00
|
|
|
|
psampled = NULL;
|
2021-03-21 21:06:26 -04:00
|
|
|
|
}
|
|
|
|
|
|
2021-03-28 23:25:32 -04:00
|
|
|
|
std::complex<float> *psampled_pls; // PLHEADER symbols
|
2021-03-21 21:06:26 -04:00
|
|
|
|
|
2021-03-24 17:18:22 -04:00
|
|
|
|
if (cstln_pls_out && cstln_pls_out->writable()>=1024) {
|
2019-03-17 16:31:42 -04:00
|
|
|
|
psampled_pls = cstln_pls_out->wr();
|
2021-03-21 21:06:26 -04:00
|
|
|
|
} else {
|
2021-03-24 17:18:22 -04:00
|
|
|
|
psampled_pls = NULL;
|
2021-03-21 21:06:26 -04:00
|
|
|
|
}
|
2019-03-17 16:31:42 -04:00
|
|
|
|
#if TEST_DIVERSITY
|
2021-03-28 23:25:32 -04:00
|
|
|
|
std::complex<float> *psymbols = symbols_out ? symbols_out->wr() : NULL;
|
2019-03-17 16:31:42 -04:00
|
|
|
|
float scale_symbols = 1.0 / cstln_amp;
|
|
|
|
|
#endif
|
2021-03-24 17:18:22 -04:00
|
|
|
|
sampler_state ss = ss_cache;
|
|
|
|
|
ss.p = in.rd();
|
|
|
|
|
ss.normalize();
|
|
|
|
|
sampler->update_freq(ss.fw16/omega0);
|
|
|
|
|
#if DEBUG_CARRIER
|
|
|
|
|
fprintf(stderr, "CARRIER frame: %s\n", ss.format());
|
|
|
|
|
#endif
|
|
|
|
|
// Interpolate PLHEADER.
|
|
|
|
|
|
|
|
|
|
const int PLH_LENGTH = sof.LENGTH + plscodes.LENGTH;
|
2021-03-28 23:25:32 -04:00
|
|
|
|
std::complex<float> plh_symbols[PLH_LENGTH];
|
2019-03-17 16:31:42 -04:00
|
|
|
|
|
2021-03-24 17:18:22 -04:00
|
|
|
|
for (int s=0; s<PLH_LENGTH; ++s)
|
|
|
|
|
{
|
2021-03-28 23:25:32 -04:00
|
|
|
|
std::complex<float> p = interp_next(&ss) * ss.gain;
|
2021-03-24 17:18:22 -04:00
|
|
|
|
plh_symbols[s] = p;
|
2019-03-17 16:31:42 -04:00
|
|
|
|
|
2021-03-24 17:18:22 -04:00
|
|
|
|
if (psampled_pls) {
|
|
|
|
|
*psampled_pls++ = p;
|
|
|
|
|
}
|
|
|
|
|
}
|
2019-03-17 16:31:42 -04:00
|
|
|
|
|
2021-03-24 17:18:22 -04:00
|
|
|
|
// Decode SOF.
|
2019-03-17 16:31:42 -04:00
|
|
|
|
|
2021-03-24 17:18:22 -04:00
|
|
|
|
uint32_t sof_bits = 0;
|
2021-03-21 21:06:26 -04:00
|
|
|
|
|
2021-03-27 00:38:28 -04:00
|
|
|
|
// Optimization with half loop and even/odd processing in a single step
|
|
|
|
|
for (int i = 0; i < sof.LENGTH/2; ++i)
|
2019-03-17 16:31:42 -04:00
|
|
|
|
{
|
2021-03-28 23:25:32 -04:00
|
|
|
|
std::complex<float> p0 = plh_symbols[2*i];
|
|
|
|
|
std::complex<float> p1 = plh_symbols[2*i+1];
|
|
|
|
|
float d0 = p0.imag() + p0.real();
|
|
|
|
|
float d1 = p1.imag() - p1.real();
|
2021-03-27 00:38:28 -04:00
|
|
|
|
sof_bits = (sof_bits<<2) | ((d0<0)<<1) | (d1<0);
|
2021-03-24 17:18:22 -04:00
|
|
|
|
}
|
2021-03-21 21:06:26 -04:00
|
|
|
|
|
2021-03-27 00:38:28 -04:00
|
|
|
|
uint32_t sof_errors = hamming_weight(sof_bits ^ sof.VALUE);
|
2021-03-24 17:18:22 -04:00
|
|
|
|
pls_total_errors += sof_errors;
|
|
|
|
|
pls_total_count += sof.LENGTH;
|
|
|
|
|
|
|
|
|
|
if (sof_errors >= S2_MAX_ERR_SOF)
|
|
|
|
|
{
|
|
|
|
|
if (sch->debug2) {
|
2021-03-30 23:03:10 -04:00
|
|
|
|
fprintf(stderr, "Too many errors in SOF (%u/%u)\n", sof_errors, S2_MAX_ERR_SOF);
|
2021-03-21 21:06:26 -04:00
|
|
|
|
}
|
|
|
|
|
|
2021-03-24 17:18:22 -04:00
|
|
|
|
in.read(ss.p-in.rd());
|
|
|
|
|
enter_frame_detect();
|
|
|
|
|
return;
|
2019-03-17 16:31:42 -04:00
|
|
|
|
}
|
2021-03-21 21:06:26 -04:00
|
|
|
|
|
2021-03-24 17:18:22 -04:00
|
|
|
|
// Decode PLSCODE.
|
|
|
|
|
|
|
|
|
|
uint64_t plscode = 0;
|
2021-03-21 21:06:26 -04:00
|
|
|
|
|
2021-03-27 00:38:28 -04:00
|
|
|
|
// Optimization with half loop and even/odd processing in a single step
|
|
|
|
|
for (int i=0; i<plscodes.LENGTH/2; ++i)
|
2019-03-17 16:31:42 -04:00
|
|
|
|
{
|
2021-03-28 23:25:32 -04:00
|
|
|
|
std::complex<float> p0 = plh_symbols[sof.LENGTH+2*i];
|
|
|
|
|
std::complex<float> p1 = plh_symbols[sof.LENGTH+2*i+1];
|
|
|
|
|
float d0 = p0.imag() + p0.real();
|
|
|
|
|
float d1 = p1.imag() - p1.real();
|
2021-03-27 00:38:28 -04:00
|
|
|
|
plscode = (plscode<<2) | ((d0<0)<<1) | (d1<0);
|
2021-03-24 17:18:22 -04:00
|
|
|
|
}
|
2021-03-21 21:06:26 -04:00
|
|
|
|
|
2021-03-27 00:38:28 -04:00
|
|
|
|
uint32_t plscode_errors = plscodes.LENGTH + 1;
|
2021-03-24 17:18:22 -04:00
|
|
|
|
int plscode_index = -1; // Avoid compiler warning.
|
|
|
|
|
|
2021-03-27 00:38:28 -04:00
|
|
|
|
// TBD: Optimization?
|
|
|
|
|
#if 1
|
|
|
|
|
for (int i = 0; i < plscodes.COUNT; ++i)
|
2021-03-24 17:18:22 -04:00
|
|
|
|
{
|
2021-03-27 00:38:28 -04:00
|
|
|
|
// number of different bits from codeword
|
|
|
|
|
uint32_t e = std::bitset<64>(plscode ^ plscodes.codewords[i]).count();
|
2021-03-24 17:18:22 -04:00
|
|
|
|
|
2021-03-27 00:38:28 -04:00
|
|
|
|
if (e < plscode_errors )
|
|
|
|
|
{
|
|
|
|
|
plscode_errors = e;
|
|
|
|
|
plscode_index = i;
|
2019-03-17 16:31:42 -04:00
|
|
|
|
}
|
|
|
|
|
}
|
2021-03-27 00:38:28 -04:00
|
|
|
|
#else
|
|
|
|
|
uint64_t pls_candidates[plscodes.COUNT];
|
|
|
|
|
std::fill(pls_candidates, pls_candidates + plscodes.COUNT, plscode);
|
|
|
|
|
std::transform(pls_candidates, pls_candidates + plscodes.COUNT, plscodes.codewords, pls_candidates, [](uint64_t x, uint64_t y) {
|
|
|
|
|
return hamming_weight(x^y); // number of different bits with codeword
|
|
|
|
|
});
|
|
|
|
|
uint64_t *pls_elected = std::min_element(pls_candidates, pls_candidates + plscodes.COUNT); // choose the one with lowest difference
|
|
|
|
|
plscode_errors = *pls_elected;
|
|
|
|
|
plscode_index = pls_elected - pls_candidates;
|
|
|
|
|
#endif
|
2021-03-24 17:18:22 -04:00
|
|
|
|
pls_total_errors += plscode_errors;
|
|
|
|
|
pls_total_count += plscodes.LENGTH;
|
|
|
|
|
|
|
|
|
|
if (plscode_errors >= S2_MAX_ERR_PLSCODE)
|
2019-03-17 16:31:42 -04:00
|
|
|
|
{
|
2021-03-21 21:06:26 -04:00
|
|
|
|
if (sch->debug2) {
|
2021-03-28 12:40:43 -04:00
|
|
|
|
fprintf(stderr, "Too many errors in plscode (%u/%lu)\n", plscode_errors, S2_MAX_ERR_PLSCODE);
|
2021-03-21 21:06:26 -04:00
|
|
|
|
}
|
|
|
|
|
|
2021-03-24 17:18:22 -04:00
|
|
|
|
in.read(ss.p-in.rd());
|
|
|
|
|
enter_frame_detect();
|
2019-03-17 16:31:42 -04:00
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
2021-03-24 17:18:22 -04:00
|
|
|
|
// ss now points to first data slot.
|
|
|
|
|
ss.scr = scrambling.Rn;
|
2019-03-17 16:31:42 -04:00
|
|
|
|
|
2021-03-28 23:25:32 -04:00
|
|
|
|
std::complex<float> plh_expected[PLH_LENGTH];
|
2021-03-24 17:18:22 -04:00
|
|
|
|
|
2021-03-27 00:38:28 -04:00
|
|
|
|
std::copy(sof.symbols, sof.symbols + sof.LENGTH, plh_expected);
|
|
|
|
|
std::copy(plscodes.symbols[plscode_index], plscodes.symbols[plscode_index] + plscodes.LENGTH, &plh_expected[sof.LENGTH]);
|
2021-03-24 17:18:22 -04:00
|
|
|
|
|
|
|
|
|
if ( state == FRAME_PROBE )
|
|
|
|
|
{
|
|
|
|
|
// Carrier frequency from differential detector is still not reliable.
|
|
|
|
|
// Use known PLH symbols to improve.
|
|
|
|
|
match_freq(plh_expected, plh_symbols, PLH_LENGTH, &ss);
|
|
|
|
|
#if DEBUG_CARRIER
|
|
|
|
|
fprintf(stderr, "CARRIER freq: %s\n", ss.format());
|
2019-03-17 16:31:42 -04:00
|
|
|
|
#endif
|
2019-07-16 20:51:46 -04:00
|
|
|
|
}
|
2021-03-21 21:06:26 -04:00
|
|
|
|
|
2021-03-24 17:18:22 -04:00
|
|
|
|
// Use known PLH symbols to estimate carrier phase and amplitude.
|
|
|
|
|
|
|
|
|
|
float mer2 = match_ph_amp(plh_expected, plh_symbols, PLH_LENGTH, &ss);
|
|
|
|
|
float mer = 10*log10f(mer2);
|
|
|
|
|
#if DEBUG_CARRIER
|
|
|
|
|
fprintf(stderr, "CARRIER plheader: %s MER %.1f dB\n", ss.format(), mer);
|
|
|
|
|
#endif
|
|
|
|
|
// Parse PLSCODE.
|
|
|
|
|
|
|
|
|
|
s2_pls pls;
|
|
|
|
|
pls.modcod = plscode_index >> 2; // Guaranteed 0..31
|
|
|
|
|
pls.sf = plscode_index & 2;
|
|
|
|
|
pls.pilots = plscode_index & 1;
|
|
|
|
|
|
|
|
|
|
plslot<SOFTSYMB> *pout=out.wr(), *pout0 = pout;
|
|
|
|
|
|
|
|
|
|
if (sch->debug2)
|
|
|
|
|
{
|
|
|
|
|
fprintf(
|
|
|
|
|
stderr,
|
2021-03-27 00:38:28 -04:00
|
|
|
|
"PLS: mc=%2d, sf=%d, pilots=%d (%2u/90) %4.1f dB ",
|
2021-03-24 17:18:22 -04:00
|
|
|
|
pls.modcod,
|
|
|
|
|
pls.sf,
|
|
|
|
|
pls.pilots,
|
2021-03-27 00:38:28 -04:00
|
|
|
|
sof_errors + plscode_errors,
|
2021-03-24 17:18:22 -04:00
|
|
|
|
mer
|
|
|
|
|
);
|
2019-07-16 20:51:46 -04:00
|
|
|
|
}
|
2019-03-17 16:31:42 -04:00
|
|
|
|
|
2021-03-24 17:18:22 -04:00
|
|
|
|
// Determine contents of frame.
|
|
|
|
|
|
|
|
|
|
int S; // Data slots in this frame
|
|
|
|
|
cstln_lut<SOFTSYMB,256> *dcstln; // Constellation for data slots
|
|
|
|
|
|
|
|
|
|
if (pls.is_dummy())
|
|
|
|
|
{
|
|
|
|
|
S = 36;
|
|
|
|
|
dcstln = qpsk;
|
|
|
|
|
}
|
|
|
|
|
else
|
2019-03-17 16:31:42 -04:00
|
|
|
|
{
|
2021-03-24 17:18:22 -04:00
|
|
|
|
const modcod_info *mcinfo = &modcod_infos[pls.modcod];
|
|
|
|
|
|
|
|
|
|
if (! mcinfo->nslots_nf)
|
2019-03-17 16:31:42 -04:00
|
|
|
|
{
|
2021-03-24 17:18:22 -04:00
|
|
|
|
fprintf(stderr, "Unsupported or corrupted MODCOD\n");
|
|
|
|
|
in.read(ss.p-in.rd());
|
|
|
|
|
enter_frame_detect();
|
|
|
|
|
return;
|
2019-03-17 16:31:42 -04:00
|
|
|
|
}
|
2021-03-21 21:06:26 -04:00
|
|
|
|
|
2021-03-28 22:02:22 -04:00
|
|
|
|
if (mer < mcinfo->esn0_nf - 3.0f) // was -1.0f
|
2021-03-24 17:18:22 -04:00
|
|
|
|
{
|
|
|
|
|
// False positive from PLHEADER detection.
|
2021-03-28 12:40:43 -04:00
|
|
|
|
if (sch->debug) {
|
2021-03-28 22:02:22 -04:00
|
|
|
|
fprintf(stderr, "Insufficient MER (%f/%f)\n", mer, mcinfo->esn0_nf - 3.0f);
|
2021-03-28 12:40:43 -04:00
|
|
|
|
}
|
|
|
|
|
|
2021-03-24 17:18:22 -04:00
|
|
|
|
in.read(ss.p-in.rd());
|
|
|
|
|
enter_frame_detect();
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
2021-03-28 12:40:43 -04:00
|
|
|
|
if (pls.sf && mcinfo->rate == FEC910)
|
2021-03-24 17:18:22 -04:00
|
|
|
|
{ // TBD use fec_infos
|
|
|
|
|
fprintf(stderr, "Unsupported or corrupted FEC\n");
|
|
|
|
|
in.read(ss.p-in.rd());
|
|
|
|
|
enter_frame_detect();
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Store current MODCOD info
|
|
|
|
|
if (mcinfo->c != m_modcodType) {
|
2021-03-28 12:40:43 -04:00
|
|
|
|
m_modcodType = mcinfo->c < cstln_base::predef::COUNT ? mcinfo->c : -1;
|
2021-03-24 17:18:22 -04:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (mcinfo->rate != m_modcodRate) {
|
2021-03-28 12:40:43 -04:00
|
|
|
|
m_modcodRate = mcinfo->rate < code_rate::FEC_COUNT ? mcinfo->rate : -1;
|
2021-03-24 17:18:22 -04:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
S = pls.sf ? mcinfo->nslots_nf/4 : mcinfo->nslots_nf;
|
|
|
|
|
// Constellation for data slots.
|
|
|
|
|
dcstln = get_cstln(pls.modcod);
|
|
|
|
|
cstln = dcstln; // Used by GUI
|
2021-03-28 12:40:43 -04:00
|
|
|
|
cstln->m_rateCode = mcinfo->rate < code_rate::FEC_COUNT ? mcinfo->rate : -1;
|
|
|
|
|
cstln->m_typeCode = mcinfo->c < cstln_base::predef::COUNT ? mcinfo->c : -1;
|
2021-03-30 23:03:10 -04:00
|
|
|
|
cstln->m_setByModcod = cstln->m_typeCode != -1;
|
2021-03-24 17:18:22 -04:00
|
|
|
|
// Output special slot with PLS information.
|
|
|
|
|
pout->is_pls = true;
|
|
|
|
|
pout->pls = pls;
|
|
|
|
|
++pout;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Now we know the frame structure.
|
|
|
|
|
// Estimate carrier over known symbols.
|
|
|
|
|
|
|
|
|
|
#if DEBUG_LOOKAHEAD
|
|
|
|
|
static int plh_counter = 0; // For debugging only
|
|
|
|
|
fprintf(stderr, "\nLOOKAHEAD %d PLH sr %+3.0f %s %.1f dB\n",
|
|
|
|
|
plh_counter, (1-ss.omega/omega0)*1e6,
|
|
|
|
|
ss.format(), 10*log10f(mer2));
|
2019-03-17 16:31:42 -04:00
|
|
|
|
#endif
|
2021-03-24 17:18:22 -04:00
|
|
|
|
// Next SOF
|
|
|
|
|
sampler_state ssnext;
|
|
|
|
|
|
|
|
|
|
{
|
|
|
|
|
ssnext = ss;
|
|
|
|
|
int ns = (S*PLSLOT_LENGTH +
|
|
|
|
|
(pls.pilots?((S-1)/16)*pilot.LENGTH:0));
|
|
|
|
|
// Find next SOP at expected position +- tolerance on symbol rate.
|
|
|
|
|
int ns_tol = lrintf(ns*sr_tol);
|
|
|
|
|
ssnext.omega = omega0;
|
|
|
|
|
ssnext.skip_symbols(ns-ns_tol);
|
|
|
|
|
find_plheader(&ssnext, ns_tol*2);
|
|
|
|
|
// Don't trust frequency from differential correlator.
|
|
|
|
|
// Our current estimate should be better.
|
|
|
|
|
ssnext.fw16 = ss.fw16;
|
|
|
|
|
interp_match_sof(&ssnext);
|
|
|
|
|
#if DEBUG_CARRIER
|
|
|
|
|
fprintf(stderr, "\nCARRIER next: %.0f%% %s %.1f dB\n",
|
|
|
|
|
q*100, ssnext.format(), 10*log10f(m2));
|
|
|
|
|
#endif
|
|
|
|
|
// Estimate symbol rate (considered stable over whole frame)
|
|
|
|
|
float dist = (ssnext.p-ss.p) + (ssnext.mu-ss.mu);
|
|
|
|
|
// Set symbol period accordingly.
|
|
|
|
|
ss.omega = dist / (ns+sof.LENGTH);
|
2019-03-17 16:31:42 -04:00
|
|
|
|
}
|
|
|
|
|
|
2021-03-24 17:18:22 -04:00
|
|
|
|
// Pilots
|
|
|
|
|
int npilots = (S-1) / 16;
|
2019-03-17 16:31:42 -04:00
|
|
|
|
|
2021-03-24 17:18:22 -04:00
|
|
|
|
if (sspilots) {
|
|
|
|
|
delete[] sspilots;
|
|
|
|
|
}
|
2019-03-17 16:31:42 -04:00
|
|
|
|
|
2021-03-24 17:18:22 -04:00
|
|
|
|
sspilots = new sampler_state[npilots];
|
2019-03-17 16:31:42 -04:00
|
|
|
|
|
2021-03-24 17:18:22 -04:00
|
|
|
|
// Detect pilots
|
|
|
|
|
if (pls.pilots)
|
|
|
|
|
{
|
|
|
|
|
sampler_state ssp = ss;
|
|
|
|
|
|
|
|
|
|
for ( int i=0; i<npilots; ++i )
|
|
|
|
|
{
|
|
|
|
|
ssp.skip_symbols(16*PLSLOT_LENGTH);
|
|
|
|
|
interp_match_pilot(&ssp);
|
|
|
|
|
sspilots[i] = ssp;
|
|
|
|
|
#if DEBUG_LOOKAHEAD
|
|
|
|
|
fprintf(stderr, "LOOKAHEAD %d PILOT%02d sr %+3.0f %s %.1f dB\n",
|
|
|
|
|
plh_counter, i, (1-ssp.omega/omega0)*1e6,
|
|
|
|
|
ssp.format(), 10*log10f(mer2));
|
|
|
|
|
#endif
|
|
|
|
|
}
|
|
|
|
|
}
|
2019-03-17 16:31:42 -04:00
|
|
|
|
|
2021-03-24 17:18:22 -04:00
|
|
|
|
if (pls.pilots)
|
|
|
|
|
{
|
|
|
|
|
// Measure average frequency, using pilots to unwrap.
|
|
|
|
|
float totalph = 0;
|
|
|
|
|
float prevph = ss.ph16;
|
|
|
|
|
int span = 16*PLSLOT_LENGTH + pilot.LENGTH;
|
|
|
|
|
// Wrap phase around current freq estimate, pilot by pilot.
|
2019-03-17 16:31:42 -04:00
|
|
|
|
|
2021-03-24 17:18:22 -04:00
|
|
|
|
for (int i=0; i<npilots; ++i)
|
|
|
|
|
{
|
|
|
|
|
float dph = sspilots[i].ph16 - (prevph+ss.fw16*span);
|
|
|
|
|
totalph += fmodfs(dph, 65536.0f);
|
|
|
|
|
prevph = sspilots[i].ph16;
|
|
|
|
|
}
|
2019-03-17 16:31:42 -04:00
|
|
|
|
|
2021-03-24 17:18:22 -04:00
|
|
|
|
// TBD Use data between last pilot and next SOF ?
|
|
|
|
|
// Tricky when there is still an integer ambiguity.
|
|
|
|
|
ss.fw16 += totalph / (span*npilots);
|
|
|
|
|
}
|
|
|
|
|
else
|
2019-03-17 16:31:42 -04:00
|
|
|
|
{
|
2021-03-24 17:18:22 -04:00
|
|
|
|
// Measure average frequency over whole frame.
|
|
|
|
|
// Mostly useful for null frames.
|
|
|
|
|
int span = S*plslot<SOFTSYMB>::LENGTH + sof.LENGTH;
|
|
|
|
|
float dph = ssnext.ph16 - (ss.ph16+ss.fw16*span);
|
|
|
|
|
ss.fw16 += fmodfs(dph,65536.0f) / span;
|
|
|
|
|
}
|
|
|
|
|
#if DEBUG_LOOKAHEAD
|
|
|
|
|
fprintf(stderr, "LOOKAHEAD %d NEXTSOF sr %+3.0f %s %.1f dB\n",
|
|
|
|
|
plh_counter, (1-ssnext.omega/omega0)*1e6,
|
|
|
|
|
ssnext.format(), 10*log10f(mer2));
|
|
|
|
|
++plh_counter;
|
|
|
|
|
#endif
|
|
|
|
|
|
|
|
|
|
if (state == FRAME_PROBE)
|
|
|
|
|
{
|
|
|
|
|
float fw0 = ss.fw16;
|
|
|
|
|
match_frame(&ss, &pls, S, dcstln);
|
|
|
|
|
// Apply retroactively from midpoint of pilots and next SOF.
|
|
|
|
|
float fw_adj = ss.fw16 - fw0;
|
|
|
|
|
|
|
|
|
|
if (pls.pilots)
|
2019-03-17 16:31:42 -04:00
|
|
|
|
{
|
2021-03-24 17:18:22 -04:00
|
|
|
|
for (int i=0; i<npilots; ++i) {
|
|
|
|
|
sspilots[i].ph16 += fw_adj * PILOT_LENGTH / 2;
|
|
|
|
|
}
|
|
|
|
|
}
|
2021-03-21 21:06:26 -04:00
|
|
|
|
|
2021-03-24 17:18:22 -04:00
|
|
|
|
ssnext.ph16 += fw_adj * sof.LENGTH / 2;
|
|
|
|
|
#if DEBUG_CARRIER
|
|
|
|
|
fprintf(stderr, "CARRIER disambiguated: %s\n", ss.format());
|
2019-03-17 16:31:42 -04:00
|
|
|
|
#endif
|
2021-03-24 17:18:22 -04:00
|
|
|
|
}
|
2021-03-21 21:06:26 -04:00
|
|
|
|
|
2021-03-24 17:18:22 -04:00
|
|
|
|
// TBD In FRAME_LOCKED, match_frame with sliprange +-1
|
|
|
|
|
// to avoid broken locks ?
|
2021-03-21 21:06:26 -04:00
|
|
|
|
|
2021-03-24 17:18:22 -04:00
|
|
|
|
// Per-frame statistics on carrier frequency.
|
|
|
|
|
// Useful at very low symbol rates to detect situations where
|
|
|
|
|
// the carrier fluctuates too fast for pilot-aided recovery.
|
|
|
|
|
statistics<float> freq_stats;
|
2021-03-21 21:06:26 -04:00
|
|
|
|
|
2021-03-24 17:18:22 -04:00
|
|
|
|
// Read slots and pilots
|
|
|
|
|
#if DEBUG_CARRIER
|
|
|
|
|
fprintf(stderr, "CARRIER data: %s\n", ss.format());
|
|
|
|
|
#endif
|
2021-03-21 21:06:26 -04:00
|
|
|
|
|
2021-03-24 17:18:22 -04:00
|
|
|
|
// int pilot_errors = 0;
|
2021-03-21 21:06:26 -04:00
|
|
|
|
|
2021-03-24 17:18:22 -04:00
|
|
|
|
for (int slot=0; slot<S; ++slot,++pout)
|
|
|
|
|
{
|
|
|
|
|
// Pilot before this slot ?
|
2021-03-21 21:06:26 -04:00
|
|
|
|
|
2021-03-24 17:18:22 -04:00
|
|
|
|
if (pls.pilots && !(slot&15) && slot)
|
|
|
|
|
{
|
|
|
|
|
ss.skip_symbols(pilot.LENGTH);
|
|
|
|
|
ss.ph16 = sspilots[slot/16-1].ph16;
|
2019-03-17 16:31:42 -04:00
|
|
|
|
}
|
2021-03-21 21:06:26 -04:00
|
|
|
|
|
2021-03-24 17:18:22 -04:00
|
|
|
|
// Time for pilot-aided carrier recovery ?
|
|
|
|
|
if ( pls.pilots && !(slot&15) && slot+16<S )
|
|
|
|
|
{
|
|
|
|
|
// Sequence of data slots followed by pilots
|
|
|
|
|
sampler_state *ssp = &sspilots[slot/16];
|
|
|
|
|
int span = 16*pout->LENGTH + pilot.LENGTH;
|
|
|
|
|
float dph = ssp->ph16 - (ss.ph16+ss.fw16*span);
|
|
|
|
|
ss.fw16 += fmodfs(dph,65536.0f) / span;
|
|
|
|
|
}
|
|
|
|
|
else if (( pls.pilots && !(slot&15) && slot+16>=S) ||
|
|
|
|
|
(!pls.pilots && !slot ))
|
|
|
|
|
{
|
|
|
|
|
// Sequence of data slots followed by SOF
|
|
|
|
|
int span = (S-slot)*pout->LENGTH + sof.LENGTH;
|
|
|
|
|
float dph = ssnext.ph16 - (ss.ph16+ss.fw16*span);
|
|
|
|
|
ss.fw16 += fmodfs(dph,65536.0f) / span;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Read slot.
|
|
|
|
|
freq_stats.add(ss.fw16);
|
2019-03-17 16:31:42 -04:00
|
|
|
|
pout->is_pls = false;
|
2021-03-28 23:25:32 -04:00
|
|
|
|
std::complex<float> p; // Export last symbols for cstln_out
|
2021-03-21 21:06:26 -04:00
|
|
|
|
|
2021-03-24 17:18:22 -04:00
|
|
|
|
for (int s=0; s<pout->LENGTH; ++s)
|
2019-03-17 16:31:42 -04:00
|
|
|
|
{
|
2021-03-24 17:18:22 -04:00
|
|
|
|
p = interp_next(&ss) * ss.gain;
|
2019-03-17 16:31:42 -04:00
|
|
|
|
#if TEST_DIVERSITY
|
2021-03-24 17:18:22 -04:00
|
|
|
|
if ( psymbols )
|
2019-03-17 16:31:42 -04:00
|
|
|
|
*psymbols++ = p * scale_symbols;
|
|
|
|
|
#endif
|
2021-03-24 17:18:22 -04:00
|
|
|
|
if (!pls.pilots || fastdrift) {
|
|
|
|
|
(void) track_symbol(&ss, p, dcstln); // SLOW
|
|
|
|
|
}
|
|
|
|
|
|
2021-03-28 23:25:32 -04:00
|
|
|
|
std::complex<float> d = descramble(&ss, p);
|
2021-03-24 17:18:22 -04:00
|
|
|
|
#if 1 // Slow
|
2021-03-28 23:25:32 -04:00
|
|
|
|
SOFTSYMB *symb = &dcstln->lookup(d.real(), d.imag())->ss;
|
2021-03-24 17:18:22 -04:00
|
|
|
|
#else // Avoid scaling floats. May wrap at very low SNR.
|
2021-03-28 23:25:32 -04:00
|
|
|
|
SOFTSYMB *symb = &dcstln->lookup((int)d.real(), (int)d.imag())->ss;
|
2019-03-17 16:31:42 -04:00
|
|
|
|
#endif
|
2021-03-24 17:18:22 -04:00
|
|
|
|
pout->symbols[s] = *symb;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
ss.normalize();
|
2021-03-21 21:06:26 -04:00
|
|
|
|
|
|
|
|
|
if (psampled) {
|
2019-03-17 16:31:42 -04:00
|
|
|
|
*psampled++ = p;
|
2021-03-21 21:06:26 -04:00
|
|
|
|
}
|
2021-03-24 17:18:22 -04:00
|
|
|
|
} // slot
|
2019-03-17 16:31:42 -04:00
|
|
|
|
|
2021-03-24 17:18:22 -04:00
|
|
|
|
if (sch->debug2)
|
2020-11-14 18:03:34 -05:00
|
|
|
|
{
|
2021-03-24 17:18:22 -04:00
|
|
|
|
fprintf(
|
|
|
|
|
stderr,
|
|
|
|
|
"sr%+.0f fs=%.0f\n",
|
|
|
|
|
(1-ss.omega/omega0)*1e6,
|
|
|
|
|
(freq_stats.max()-freq_stats.min())*1e6/65536.0f
|
|
|
|
|
);
|
2020-11-14 18:03:34 -05:00
|
|
|
|
}
|
|
|
|
|
|
2021-03-24 17:18:22 -04:00
|
|
|
|
// Commit whole frame after final SOF.
|
|
|
|
|
if (! pls.is_dummy())
|
2019-03-17 16:31:42 -04:00
|
|
|
|
{
|
2021-03-24 17:18:22 -04:00
|
|
|
|
if ((modcods&(1<<pls.modcod)) && (framesizes&(1<<pls.sf)))
|
|
|
|
|
{
|
|
|
|
|
out.written(pout-pout0);
|
2021-03-21 21:06:26 -04:00
|
|
|
|
}
|
2021-03-24 17:18:22 -04:00
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
if (sch->debug2) {
|
|
|
|
|
fprintf(stderr, "modcod %d size %d rejected\n", pls.modcod, pls.sf);
|
|
|
|
|
}
|
2021-03-21 21:06:26 -04:00
|
|
|
|
}
|
2019-03-17 16:31:42 -04:00
|
|
|
|
}
|
2021-03-21 21:06:26 -04:00
|
|
|
|
|
2019-03-17 16:31:42 -04:00
|
|
|
|
// Write back sampler progress
|
|
|
|
|
meas_count += ss.p - in.rd();
|
2021-03-24 17:18:22 -04:00
|
|
|
|
in.read(ss.p-in.rd());
|
|
|
|
|
ss_cache = ss;
|
2019-03-17 16:31:42 -04:00
|
|
|
|
|
|
|
|
|
// Measurements
|
2021-03-21 21:06:26 -04:00
|
|
|
|
if (psampled) {
|
2021-03-24 17:18:22 -04:00
|
|
|
|
cstln_out->written(psampled-cstln_out->wr());
|
2021-03-21 21:06:26 -04:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (psampled_pls) {
|
2021-03-24 17:18:22 -04:00
|
|
|
|
cstln_pls_out->written(psampled_pls-cstln_pls_out->wr());
|
2021-03-21 21:06:26 -04:00
|
|
|
|
}
|
2019-03-17 16:31:42 -04:00
|
|
|
|
#if TEST_DIVERSITY
|
2021-03-24 17:18:22 -04:00
|
|
|
|
if ( psymbols ) symbols_out->written(psymbols-symbols_out->wr());
|
2019-03-17 16:31:42 -04:00
|
|
|
|
#endif
|
|
|
|
|
if (meas_count >= meas_decimation)
|
|
|
|
|
{
|
2021-03-24 17:18:22 -04:00
|
|
|
|
opt_write(freq_out, ss_cache.fw16/65536/ss_cache.omega);
|
|
|
|
|
opt_write(ss_out, cstln_amp / ss_cache.gain);
|
|
|
|
|
opt_write(mer_out, mer2 ? 10*log10f(mer2) : -99);
|
2019-03-17 16:31:42 -04:00
|
|
|
|
meas_count -= meas_decimation;
|
|
|
|
|
}
|
|
|
|
|
|
2021-03-24 17:18:22 -04:00
|
|
|
|
if (state == FRAME_PROBE)
|
|
|
|
|
{
|
|
|
|
|
// First frame completed successfully. Validate the lock.
|
|
|
|
|
enter_frame_locked();
|
|
|
|
|
}
|
2021-03-21 21:06:26 -04:00
|
|
|
|
|
2021-03-24 17:18:22 -04:00
|
|
|
|
if (ss_cache.fw16<min_freqw16 || ss_cache.fw16>max_freqw16)
|
|
|
|
|
{
|
2021-03-28 12:40:43 -04:00
|
|
|
|
fprintf(stderr, "Carrier out of bounds\n");
|
2021-03-24 17:18:22 -04:00
|
|
|
|
enter_frame_detect();
|
2021-03-21 21:06:26 -04:00
|
|
|
|
}
|
2021-03-24 17:18:22 -04:00
|
|
|
|
} // run_frame_probe_locked
|
2019-03-17 16:31:42 -04:00
|
|
|
|
|
2021-03-24 17:18:22 -04:00
|
|
|
|
// Find most likely PLHEADER between *pss and *pss+search_range
|
|
|
|
|
// by differential correlation.
|
|
|
|
|
// Align symbol timing.
|
|
|
|
|
// Estimate carrier frequency (to about +-10kppm).
|
|
|
|
|
// Estimate carrier phase (to about +-PI/4).
|
|
|
|
|
// Adjust *ss accordingly.
|
|
|
|
|
// Initialize AGC.
|
|
|
|
|
// Return confidence (unbounded, 0=bad, 1=nominal).
|
2019-03-17 16:31:42 -04:00
|
|
|
|
|
2021-03-24 17:18:22 -04:00
|
|
|
|
float find_plheader(sampler_state *pss, int search_range)
|
|
|
|
|
{
|
2021-03-28 23:25:32 -04:00
|
|
|
|
std::complex<T> best_corr = 0;
|
2021-03-24 17:18:22 -04:00
|
|
|
|
int best_imu = 0; // Avoid compiler warning.
|
|
|
|
|
int best_pos = 0; // Avoid compiler warning.
|
|
|
|
|
|
|
|
|
|
// Symbol clock is not recovered yet, so we try fractional symbols.
|
|
|
|
|
const int interp = 8;
|
|
|
|
|
|
|
|
|
|
for (int imu=0; imu<interp; ++imu)
|
|
|
|
|
{
|
|
|
|
|
const int ndiffs = search_range + sof.LENGTH + plscodes.LENGTH;
|
|
|
|
|
|
|
|
|
|
if (diffs) {
|
|
|
|
|
delete[] diffs;
|
|
|
|
|
}
|
|
|
|
|
|
2021-03-28 23:25:32 -04:00
|
|
|
|
diffs = new std::complex<T>[ndiffs];
|
2021-03-24 17:18:22 -04:00
|
|
|
|
sampler_state ss = *pss;
|
|
|
|
|
ss.mu += imu * ss.omega / interp;
|
|
|
|
|
|
|
|
|
|
// Compute rotation between consecutive symbols.
|
2021-03-28 23:25:32 -04:00
|
|
|
|
std::complex<T> prev = 0;
|
2021-03-24 17:18:22 -04:00
|
|
|
|
for (int i=0; i<ndiffs; ++i)
|
|
|
|
|
{
|
2021-03-28 23:25:32 -04:00
|
|
|
|
std::complex<T> p = interp_next(&ss);
|
2021-03-24 17:18:22 -04:00
|
|
|
|
diffs[i] = conjprod(prev, p);
|
|
|
|
|
prev = p;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Find best PLHEADER candidate position.
|
|
|
|
|
const int ncorrs = search_range;
|
|
|
|
|
for (int i=0; i<ncorrs; ++i)
|
|
|
|
|
{
|
2021-03-28 23:25:32 -04:00
|
|
|
|
std::complex<T> c = correlate_plheader_diff(&diffs[i]);
|
2021-03-24 17:18:22 -04:00
|
|
|
|
//if ( cnorm2(c) > cnorm2(best_corr) ) {
|
2021-03-28 23:25:32 -04:00
|
|
|
|
// c.imag()>0 enforces frequency error +-Fm/4
|
|
|
|
|
if (cnorm2(c)>cnorm2(best_corr) && c.imag()>0)
|
2021-03-24 17:18:22 -04:00
|
|
|
|
{
|
|
|
|
|
best_corr = c;
|
|
|
|
|
best_imu = imu;
|
|
|
|
|
best_pos = i;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
} // imu
|
|
|
|
|
|
|
|
|
|
// Setup sampler according to best match.
|
|
|
|
|
pss->mu += best_imu * pss->omega / interp;
|
|
|
|
|
pss->skip_symbols(best_pos);
|
|
|
|
|
pss->normalize();
|
|
|
|
|
#if 0
|
|
|
|
|
// Lowpass-filter the differential correlator.
|
|
|
|
|
// (Does not help much.)
|
2021-03-28 23:25:32 -04:00
|
|
|
|
static std::complex<float> acc = 0;
|
2021-03-24 17:18:22 -04:00
|
|
|
|
static const float k = 0.05;
|
|
|
|
|
acc = best_corr*k + acc*(1-k);
|
|
|
|
|
best_corr = acc;
|
|
|
|
|
#endif
|
|
|
|
|
// Get rough estimate of carrier frequency from differential correlator.
|
|
|
|
|
// (best_corr is nominally +j).
|
2021-03-28 23:25:32 -04:00
|
|
|
|
float freqw = atan2f(-best_corr.real(), best_corr.imag());
|
2021-03-24 17:18:22 -04:00
|
|
|
|
pss->fw16 += freqw * 65536 / (2*M_PI);
|
|
|
|
|
// Force refresh because correction may be large.
|
|
|
|
|
sampler->update_freq(pss->fw16/omega0);
|
|
|
|
|
|
|
|
|
|
// Interpolate SOF with initial frequency estimation.
|
|
|
|
|
// Estimate phase by correlation.
|
|
|
|
|
// Initialize AGC (naive).
|
|
|
|
|
float q;
|
|
|
|
|
|
|
|
|
|
{
|
|
|
|
|
sampler_state ss = *pss;
|
|
|
|
|
float power = 0;
|
2021-03-28 23:25:32 -04:00
|
|
|
|
std::complex<T> symbs[sof.LENGTH];
|
2021-03-24 17:18:22 -04:00
|
|
|
|
|
|
|
|
|
for (int i=0; i<sof.LENGTH; ++i)
|
|
|
|
|
{
|
|
|
|
|
symbs[i] = interp_next(&ss);
|
|
|
|
|
power += cnorm2(symbs[i]);
|
|
|
|
|
}
|
|
|
|
|
|
2021-03-28 23:25:32 -04:00
|
|
|
|
std::complex<float> c = conjprod(sof.symbols, symbs, sof.LENGTH);
|
2021-03-24 17:18:22 -04:00
|
|
|
|
c *= 1.0f / sof.LENGTH;
|
|
|
|
|
align_phase(pss, c);
|
|
|
|
|
float signal_amp = sqrtf(power/sof.LENGTH);
|
|
|
|
|
q = sqrtf(cnorm2(c)) / (cstln_amp*signal_amp);
|
|
|
|
|
pss->gain = cstln_amp / signal_amp;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return q;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Correlate PLHEADER.
|
|
|
|
|
|
2021-03-28 23:25:32 -04:00
|
|
|
|
std::complex<float> correlate_plheader_diff(std::complex<T> *diffs)
|
2019-03-17 16:31:42 -04:00
|
|
|
|
{
|
2021-03-28 23:25:32 -04:00
|
|
|
|
std::complex<T> csof = correlate_sof_diff(diffs);
|
|
|
|
|
std::complex<T> cplsc = correlate_plscode_diff(&diffs[sof.LENGTH]);
|
2021-03-24 17:18:22 -04:00
|
|
|
|
// Use csof+cplsc or csof-cplsc, whichever maximizes likelihood.
|
2021-03-28 23:25:32 -04:00
|
|
|
|
std::complex<T> c0 = csof + cplsc; // Best when b7==0 (pilots off)
|
|
|
|
|
std::complex<T> c1 = csof - cplsc; // Best when b7==1 (pilots on)
|
|
|
|
|
std::complex<T> c = (cnorm2(c0)>cnorm2(c1)) ? c0 : c1;
|
2021-03-24 17:18:22 -04:00
|
|
|
|
return c * (1.0f/(26-1+64/2));
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Correlate 25 differential transitions in SOF.
|
|
|
|
|
|
2021-03-28 23:25:32 -04:00
|
|
|
|
std::complex<float> correlate_sof_diff(std::complex<T> *diffs)
|
2021-03-24 17:18:22 -04:00
|
|
|
|
{
|
2021-03-28 23:25:32 -04:00
|
|
|
|
std::complex<T> c = 0;
|
2021-03-24 17:18:22 -04:00
|
|
|
|
const uint32_t dsof = sof.VALUE ^ (sof.VALUE>>1);
|
|
|
|
|
|
|
|
|
|
for (int i=0; i<sof.LENGTH; ++i)
|
|
|
|
|
{
|
|
|
|
|
// Constant odd bit => +PI/4
|
|
|
|
|
// Constant even bit => -PI/4
|
|
|
|
|
// Toggled odd bit => -PI/4
|
|
|
|
|
// Toggled even bit => +PI/4
|
|
|
|
|
if (((dsof>>(sof.LENGTH-1-i)) ^ i) & 1) {
|
|
|
|
|
c += diffs[i];
|
|
|
|
|
} else {
|
|
|
|
|
c -= diffs[i];
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return c;
|
2019-03-17 16:31:42 -04:00
|
|
|
|
}
|
|
|
|
|
|
2021-03-24 17:18:22 -04:00
|
|
|
|
// Correlate 32 data-independent transitions in PLSCODE.
|
|
|
|
|
|
2021-03-28 23:25:32 -04:00
|
|
|
|
std::complex<float> correlate_plscode_diff(std::complex<T> *diffs)
|
2019-03-17 16:31:42 -04:00
|
|
|
|
{
|
2021-03-28 23:25:32 -04:00
|
|
|
|
std::complex<T> c = 0;
|
2021-03-24 17:18:22 -04:00
|
|
|
|
uint64_t dscr = plscodes.SCRAMBLING ^ (plscodes.SCRAMBLING>>1);
|
2021-03-21 21:06:26 -04:00
|
|
|
|
|
2021-03-24 17:18:22 -04:00
|
|
|
|
for (int i=1; i<plscodes.LENGTH; i+=2)
|
|
|
|
|
{
|
|
|
|
|
if ( (dscr>>(plscodes.LENGTH-1-i)) & 1 ) {
|
|
|
|
|
c -= diffs[i];
|
|
|
|
|
} else {
|
|
|
|
|
c += diffs[i];
|
|
|
|
|
}
|
2021-03-21 21:06:26 -04:00
|
|
|
|
}
|
|
|
|
|
|
2021-03-24 17:18:22 -04:00
|
|
|
|
return c;
|
2019-03-17 16:31:42 -04:00
|
|
|
|
}
|
2021-03-21 21:06:26 -04:00
|
|
|
|
|
2021-03-24 17:18:22 -04:00
|
|
|
|
// Adjust carrier frequency in *pss to match target phase after ns symbols.
|
|
|
|
|
|
|
|
|
|
void adjust_freq(sampler_state *pss, int ns, const sampler_state *pnext)
|
2019-03-17 16:31:42 -04:00
|
|
|
|
{
|
2021-03-24 17:18:22 -04:00
|
|
|
|
// Note: Minimum deviation from current estimate.
|
|
|
|
|
float adj = fmodfs(pnext->ph16-(pss->ph16+pss->fw16*ns),65536.0f) / ns;
|
|
|
|
|
// 2sps vcm avec adj 10711K
|
|
|
|
|
// 2sps vcm avec adj/2 10265k
|
|
|
|
|
// 2sps vcm sans adj 8060K
|
|
|
|
|
// 5sps 2MS1 avec adj 2262K
|
|
|
|
|
// 5sps 2MS1 avec adj/2 3109K
|
|
|
|
|
// 5sps 2MS1 avec adj/3 3254K
|
|
|
|
|
// 5sps 2MS1 sans adj 3254K
|
|
|
|
|
// 1.2sps oldbeacon avec adj 5774K
|
|
|
|
|
// 1.2sps oldbeacon avec adj/2 15M
|
|
|
|
|
// 1.2sps oldbeacon avec adj/3 15.6M max 17M
|
|
|
|
|
// 1.2sps oldbeacon sans adj 14M malgré drift
|
|
|
|
|
pss->fw16 += adj;
|
2019-03-17 16:31:42 -04:00
|
|
|
|
}
|
|
|
|
|
|
2021-03-24 17:18:22 -04:00
|
|
|
|
// Estimate frequency from known symbols by differential correlation.
|
|
|
|
|
// Adjust *ss accordingly.
|
|
|
|
|
// Retroactively frequency-shift the samples in-place.
|
|
|
|
|
//
|
|
|
|
|
// *ss must point after the sampled symbols.
|
|
|
|
|
// expect[] must be PSK with amplitude cstln_amp.
|
|
|
|
|
// Idempotent.
|
|
|
|
|
// Not affected by phase error nor by gain mismatch.
|
|
|
|
|
// Can handle +-25% error.
|
|
|
|
|
// Result is typically such that residual phase error over a PLHEADER
|
|
|
|
|
// spans less than 90°.
|
|
|
|
|
|
|
|
|
|
void match_freq(
|
2021-03-28 23:25:32 -04:00
|
|
|
|
std::complex<float> *expect,
|
|
|
|
|
std::complex<float> *recv,
|
2021-03-24 17:18:22 -04:00
|
|
|
|
int ns,
|
|
|
|
|
sampler_state *ss)
|
2019-03-17 16:31:42 -04:00
|
|
|
|
{
|
2021-03-28 22:02:22 -04:00
|
|
|
|
if (sch->debug) {
|
|
|
|
|
fprintf(stderr, "match_freq\n");
|
|
|
|
|
}
|
|
|
|
|
|
2021-03-28 23:25:32 -04:00
|
|
|
|
std::complex<float> diff = 0;
|
2021-03-21 21:06:26 -04:00
|
|
|
|
|
2021-03-24 17:18:22 -04:00
|
|
|
|
for (int i=0; i<ns-1; ++i)
|
|
|
|
|
{
|
2021-03-28 23:25:32 -04:00
|
|
|
|
std::complex<float> de = conjprod(expect[i], expect[i+1]);
|
|
|
|
|
std::complex<float> dr = conjprod(recv[i], recv[i+1]);
|
2021-03-24 17:18:22 -04:00
|
|
|
|
diff += conjprod(de, dr);
|
2021-03-21 21:06:26 -04:00
|
|
|
|
}
|
|
|
|
|
|
2021-03-28 23:25:32 -04:00
|
|
|
|
float dfw16 = atan2f(diff.imag(),diff.real()) * 65536 / (2*M_PI);
|
2021-03-24 17:18:22 -04:00
|
|
|
|
|
|
|
|
|
// Derotate.
|
|
|
|
|
for (int i=0; i<ns; ++i) {
|
|
|
|
|
recv[i] *= trig.expi(-dfw16*i);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
ss->fw16 += dfw16;
|
|
|
|
|
ss->ph16 += dfw16 * ns; // Retroactively
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Interpolate a pilot block.
|
|
|
|
|
// Perform ML match.
|
|
|
|
|
|
|
|
|
|
float interp_match_pilot(sampler_state *pss)
|
|
|
|
|
{
|
2021-03-28 23:25:32 -04:00
|
|
|
|
std::complex<T> symbols[pilot.LENGTH];
|
|
|
|
|
std::complex<T> expected[pilot.LENGTH];
|
2021-03-24 17:18:22 -04:00
|
|
|
|
|
|
|
|
|
for (int i=0; i<pilot.LENGTH; ++i)
|
2019-03-17 16:31:42 -04:00
|
|
|
|
{
|
2021-03-28 23:25:32 -04:00
|
|
|
|
std::complex<float> p = interp_next(pss) * pss->gain;
|
2021-03-24 17:18:22 -04:00
|
|
|
|
symbols[i] = descramble(pss, p);
|
|
|
|
|
expected[i] = pilot.symbol;
|
2021-03-28 23:25:32 -04:00
|
|
|
|
//fprintf(stderr, "%f %f\n", symbols[i].real(), symbols[i].imag());
|
2019-03-17 16:31:42 -04:00
|
|
|
|
}
|
2021-03-24 17:18:22 -04:00
|
|
|
|
|
|
|
|
|
return match_ph_amp(expected, symbols, pilot.LENGTH, pss);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Interpolate a SOF.
|
|
|
|
|
// Perform ML match.
|
|
|
|
|
|
|
|
|
|
float interp_match_sof(sampler_state *pss)
|
|
|
|
|
{
|
2021-03-28 23:25:32 -04:00
|
|
|
|
std::complex<T> symbols[pilot.LENGTH];
|
2021-03-24 17:18:22 -04:00
|
|
|
|
|
|
|
|
|
for (int i=0; i<sof.LENGTH; ++i) {
|
|
|
|
|
symbols[i] = interp_next(pss) * pss->gain;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return match_ph_amp(sof.symbols, symbols, sof.LENGTH, pss);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Estimate phase and amplitude from known symbols.
|
|
|
|
|
// Adjust *ss accordingly.
|
|
|
|
|
// Retroactively derotate and scale the samples in-place.
|
|
|
|
|
// Return MER^2.
|
|
|
|
|
//
|
|
|
|
|
// *ss must point after the sampled symbols, with gain applied.
|
|
|
|
|
// expect[] must be PSK (not APSK) with amplitude cstln_amp.
|
|
|
|
|
// Idempotent.
|
|
|
|
|
|
|
|
|
|
float match_ph_amp(
|
2021-03-28 23:25:32 -04:00
|
|
|
|
std::complex<float> *expect,
|
|
|
|
|
std::complex<float> *recv,
|
2021-03-24 17:18:22 -04:00
|
|
|
|
int ns,
|
|
|
|
|
sampler_state *ss
|
|
|
|
|
)
|
|
|
|
|
{
|
2021-03-28 23:25:32 -04:00
|
|
|
|
std::complex<float> rr = 0;
|
2021-03-24 17:18:22 -04:00
|
|
|
|
|
|
|
|
|
for (int i=0; i<ns; ++i) {
|
|
|
|
|
rr += conjprod(expect[i], recv[i]);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
rr *= 1.0f / (ns*cstln_amp);
|
2021-03-28 23:25:32 -04:00
|
|
|
|
float dph16 = atan2f(rr.imag(),rr.real()) * 65536 / (2*M_PI);
|
2021-03-24 17:18:22 -04:00
|
|
|
|
ss->ph16 += dph16;
|
|
|
|
|
rr *= trig.expi(-dph16);
|
2021-03-28 23:25:32 -04:00
|
|
|
|
// rr.real() is now the modulation amplitude.
|
|
|
|
|
float dgain = cstln_amp / rr.real();
|
2021-03-24 17:18:22 -04:00
|
|
|
|
ss->gain *= dgain;
|
|
|
|
|
// Rotate and scale. Compute error power.
|
2021-03-28 23:25:32 -04:00
|
|
|
|
std::complex<float> adj = trig.expi(-dph16) * dgain;
|
2021-03-24 17:18:22 -04:00
|
|
|
|
float ev2 = 0;
|
|
|
|
|
|
|
|
|
|
for (int i=0; i<ns; ++i)
|
|
|
|
|
{
|
|
|
|
|
recv[i] *= adj;
|
|
|
|
|
ev2 += cnorm2(recv[i]-expect[i]);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Return MER^2.
|
|
|
|
|
ev2 *= 1.0f / (ns*cstln_amp*cstln_amp);
|
|
|
|
|
return 1.0f / ev2;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Adjust frequency in *pss by an integer number of cycles per data block
|
|
|
|
|
// so that phases align best on data symbols.
|
|
|
|
|
//
|
|
|
|
|
// *pss must point to the beginning of a frame.
|
|
|
|
|
|
|
|
|
|
void match_frame(
|
|
|
|
|
sampler_state *pss,
|
|
|
|
|
const s2_pls *pls,
|
|
|
|
|
int S,
|
|
|
|
|
cstln_lut<SOFTSYMB,256> *dcstln
|
|
|
|
|
)
|
|
|
|
|
{
|
2021-03-28 22:02:22 -04:00
|
|
|
|
if (sch->debug) {
|
|
|
|
|
fprintf(stderr, "match_frame\n");
|
|
|
|
|
}
|
|
|
|
|
|
2021-03-30 23:03:10 -04:00
|
|
|
|
bool pilots = pls->pilots; // force to true for lighter processing
|
2021-03-24 17:18:22 -04:00
|
|
|
|
// With pilots: Use first block of data slots.
|
|
|
|
|
// Without pilots: Use whole frame.
|
2021-03-30 23:03:10 -04:00
|
|
|
|
int ns = pilots ? 16*90 : S*90;
|
2021-03-24 17:18:22 -04:00
|
|
|
|
// Pilots: steps of ~700 ppm (= 1 cycle between pilots)
|
|
|
|
|
// No pilots, normal frames: steps of ~30(QPSK) - ~80(32APSK) ppm
|
|
|
|
|
// No pilots, short frames: steps of ~120(QPSK) - 300(32APSK) ppm
|
2021-03-30 23:03:10 -04:00
|
|
|
|
int nwrap = pilots ? 16*90+pilot.LENGTH : S*90+sof.LENGTH;
|
2021-03-24 17:18:22 -04:00
|
|
|
|
// Frequency search range.
|
2021-03-30 23:03:10 -04:00
|
|
|
|
int sliprange = pilots ? 10 : 50; // TBD Customizable ?
|
2021-03-24 17:18:22 -04:00
|
|
|
|
float besterr = 1e99;
|
|
|
|
|
float bestslip = 0; // Avoid compiler warning
|
2021-03-30 23:03:10 -04:00
|
|
|
|
int err_div = ns * cstln_amp * cstln_amp;
|
2021-03-24 17:18:22 -04:00
|
|
|
|
|
2021-03-28 22:02:22 -04:00
|
|
|
|
for (int slip = -sliprange; slip <= sliprange; ++slip)
|
2021-03-24 17:18:22 -04:00
|
|
|
|
{
|
|
|
|
|
sampler_state ssl = *pss;
|
|
|
|
|
float dfw = slip * 65536.0f / nwrap;
|
|
|
|
|
ssl.fw16 += dfw;
|
|
|
|
|
// Apply retroactively from midpoint of preceeding PLHEADER,
|
|
|
|
|
// where the phase from match_ph_amp is most reliable.
|
2021-03-28 22:02:22 -04:00
|
|
|
|
ssl.ph16 += dfw * (plscodes.LENGTH + sof.LENGTH) / 2;
|
2021-03-24 17:18:22 -04:00
|
|
|
|
float err = 0;
|
|
|
|
|
|
2021-03-28 22:02:22 -04:00
|
|
|
|
for (int s = 0; s < ns; ++s)
|
2021-03-24 17:18:22 -04:00
|
|
|
|
{
|
2021-03-28 23:25:32 -04:00
|
|
|
|
std::complex<float> p = interp_next(&ssl) * ssl.gain;
|
2021-03-24 17:18:22 -04:00
|
|
|
|
typename cstln_lut<SOFTSYMB,256>::result *cr =
|
2021-03-28 23:25:32 -04:00
|
|
|
|
dcstln->lookup(p.real(), p.imag());
|
2021-03-30 23:03:10 -04:00
|
|
|
|
float evreal = p.real() - dcstln->symbols[cr->symbol].real();
|
|
|
|
|
float evimag = p.imag() - dcstln->symbols[cr->symbol].imag();
|
|
|
|
|
err += evreal*evreal + evimag*evimag;
|
2021-03-24 17:18:22 -04:00
|
|
|
|
}
|
|
|
|
|
|
2021-03-30 23:03:10 -04:00
|
|
|
|
err /= err_div;
|
2021-03-24 17:18:22 -04:00
|
|
|
|
|
|
|
|
|
if (err < besterr)
|
|
|
|
|
{
|
2021-03-28 22:02:22 -04:00
|
|
|
|
besterr = err;
|
|
|
|
|
bestslip = slip;
|
2021-03-24 17:18:22 -04:00
|
|
|
|
}
|
|
|
|
|
#if DEBUG_CARRIER
|
|
|
|
|
fprintf(stderr, "slip %+3d %6.0f ppm err=%f\n",
|
|
|
|
|
slip, ssl.fw16*1e6/65536, err);
|
|
|
|
|
#endif
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
pss->fw16 += bestslip * 65536.0f / nwrap;
|
|
|
|
|
} // match_frame
|
|
|
|
|
|
|
|
|
|
void shutdown()
|
|
|
|
|
{
|
|
|
|
|
if (sch->verbose)
|
2019-03-17 16:31:42 -04:00
|
|
|
|
{
|
2021-03-24 17:18:22 -04:00
|
|
|
|
fprintf(
|
|
|
|
|
stderr,
|
|
|
|
|
"PL errors: %d/%d (%.0f ppm)\n",
|
|
|
|
|
pls_total_errors,
|
|
|
|
|
pls_total_count,
|
|
|
|
|
1e6 * pls_total_errors / pls_total_count
|
|
|
|
|
);
|
2019-03-17 16:31:42 -04:00
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2021-03-28 23:25:32 -04:00
|
|
|
|
std::complex<float> descramble(sampler_state *ss, const std::complex<float> &p)
|
2019-03-17 16:31:42 -04:00
|
|
|
|
{
|
|
|
|
|
int r = *ss->scr++;
|
2021-03-28 23:25:32 -04:00
|
|
|
|
std::complex<float> res;
|
2021-03-21 21:06:26 -04:00
|
|
|
|
|
2019-03-17 16:31:42 -04:00
|
|
|
|
switch (r)
|
|
|
|
|
{
|
|
|
|
|
case 3:
|
2021-03-28 23:25:32 -04:00
|
|
|
|
res.real(-p.imag());
|
|
|
|
|
res.imag(p.real());
|
2019-03-17 16:31:42 -04:00
|
|
|
|
break;
|
|
|
|
|
case 2:
|
2021-03-28 23:25:32 -04:00
|
|
|
|
res.real(-p.real());
|
|
|
|
|
res.imag(-p.imag());
|
2019-03-17 16:31:42 -04:00
|
|
|
|
break;
|
|
|
|
|
case 1:
|
2021-03-28 23:25:32 -04:00
|
|
|
|
res.real(p.imag());
|
|
|
|
|
res.imag(-p.real());
|
2019-03-17 16:31:42 -04:00
|
|
|
|
break;
|
|
|
|
|
default:
|
|
|
|
|
res = p;
|
|
|
|
|
}
|
2021-03-21 21:06:26 -04:00
|
|
|
|
|
2019-03-17 16:31:42 -04:00
|
|
|
|
return res;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Interpolator
|
|
|
|
|
|
2021-03-28 23:25:32 -04:00
|
|
|
|
inline std::complex<float> interp_next(sampler_state *ss)
|
2019-03-17 16:31:42 -04:00
|
|
|
|
{
|
|
|
|
|
// Skip to next sample
|
|
|
|
|
while (ss->mu >= 1)
|
|
|
|
|
{
|
|
|
|
|
++ss->p;
|
2021-03-24 17:18:22 -04:00
|
|
|
|
ss->mu-=1.0f;
|
2019-03-17 16:31:42 -04:00
|
|
|
|
}
|
|
|
|
|
// Interpolate
|
|
|
|
|
#if 0
|
2021-03-24 17:18:22 -04:00
|
|
|
|
// Interpolate linearly then derotate.
|
|
|
|
|
// This will fail with large carrier offsets (e.g. --tune).
|
|
|
|
|
float cmu = 1.0f - ss->mu;
|
2021-03-28 23:25:32 -04:00
|
|
|
|
std::complex<float> s(ss->p[0].real()*cmu + ss->p[1].real()*ss->mu,
|
|
|
|
|
ss->p[0].imag()*cmu + ss->p[1].imag()*ss->mu);
|
2021-03-24 17:18:22 -04:00
|
|
|
|
ss->mu += ss->omega;
|
|
|
|
|
// Derotate
|
2021-03-28 23:25:32 -04:00
|
|
|
|
const std::complex<float> &rot = trig.expi(-ss->ph16);
|
2021-03-24 17:18:22 -04:00
|
|
|
|
ss->ph16 += ss->fw16;
|
|
|
|
|
return rot * s;
|
2019-03-17 16:31:42 -04:00
|
|
|
|
#else
|
|
|
|
|
// Use generic interpolator
|
2021-03-28 23:25:32 -04:00
|
|
|
|
std::complex<float> s = sampler->interp(ss->p, ss->mu, ss->ph16);
|
2021-03-24 17:18:22 -04:00
|
|
|
|
ss->mu += ss->omega;
|
2019-03-17 16:31:42 -04:00
|
|
|
|
ss->ph16 += ss->fw16;
|
|
|
|
|
return s;
|
|
|
|
|
#endif
|
|
|
|
|
}
|
|
|
|
|
|
2021-03-24 17:18:22 -04:00
|
|
|
|
// Adjust phase in [ss] to cancel offset observed as [c].
|
|
|
|
|
|
2021-03-28 23:25:32 -04:00
|
|
|
|
void align_phase(sampler_state *ss, const std::complex<float> &c)
|
2019-03-17 16:31:42 -04:00
|
|
|
|
{
|
2021-03-28 23:25:32 -04:00
|
|
|
|
float err = atan2f(c.imag(),c.real()) * 65536 / (2*M_PI);
|
2019-03-17 16:31:42 -04:00
|
|
|
|
ss->ph16 += err;
|
|
|
|
|
}
|
|
|
|
|
|
2021-03-24 17:18:22 -04:00
|
|
|
|
inline uint8_t track_symbol(
|
|
|
|
|
sampler_state *ss,
|
2021-03-28 23:25:32 -04:00
|
|
|
|
const std::complex<float> &p,
|
2021-03-24 17:18:22 -04:00
|
|
|
|
cstln_lut<SOFTSYMB,256> *c
|
|
|
|
|
)
|
2019-03-17 16:31:42 -04:00
|
|
|
|
{
|
2021-03-24 17:18:22 -04:00
|
|
|
|
static const float kph = 4e-2;
|
|
|
|
|
static const float kfw = 1e-4;
|
2019-03-17 16:31:42 -04:00
|
|
|
|
// Decision
|
2021-03-28 23:25:32 -04:00
|
|
|
|
typename cstln_lut<SOFTSYMB,256>::result *cr = c->lookup(p.real(), p.imag());
|
2019-03-17 16:31:42 -04:00
|
|
|
|
// Carrier tracking
|
2021-03-24 17:18:22 -04:00
|
|
|
|
ss->ph16 += cr->phase_error * kph;
|
|
|
|
|
ss->fw16 += cr->phase_error * kfw;
|
2021-03-21 21:06:26 -04:00
|
|
|
|
|
|
|
|
|
if (ss->fw16 < min_freqw16) {
|
2019-03-17 16:31:42 -04:00
|
|
|
|
ss->fw16 = min_freqw16;
|
2021-03-21 21:06:26 -04:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (ss->fw16 > max_freqw16) {
|
2019-03-17 16:31:42 -04:00
|
|
|
|
ss->fw16 = max_freqw16;
|
2021-03-21 21:06:26 -04:00
|
|
|
|
}
|
|
|
|
|
|
2019-03-17 16:31:42 -04:00
|
|
|
|
return cr->symbol;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public:
|
2021-03-24 17:18:22 -04:00
|
|
|
|
cstln_lut<SOFTSYMB,256> *qpsk;
|
2019-03-17 16:31:42 -04:00
|
|
|
|
s2_plscodes<T> plscodes;
|
2021-03-24 17:18:22 -04:00
|
|
|
|
cstln_lut<SOFTSYMB,256> *cstlns[32]; // Constellations in use, by modcod
|
2021-03-21 21:06:26 -04:00
|
|
|
|
|
2021-03-24 17:18:22 -04:00
|
|
|
|
cstln_lut<SOFTSYMB,256> *get_cstln(int modcod)
|
2019-03-17 16:31:42 -04:00
|
|
|
|
{
|
2021-03-24 17:18:22 -04:00
|
|
|
|
if (!cstlns[modcod])
|
2019-03-17 16:31:42 -04:00
|
|
|
|
{
|
2021-03-24 17:18:22 -04:00
|
|
|
|
const modcod_info *mcinfo = &modcod_infos[modcod];
|
2021-03-21 21:06:26 -04:00
|
|
|
|
|
2021-03-24 17:18:22 -04:00
|
|
|
|
if (sch->debug)
|
2019-03-17 16:31:42 -04:00
|
|
|
|
{
|
2021-03-24 17:18:22 -04:00
|
|
|
|
fprintf(
|
|
|
|
|
stderr,
|
|
|
|
|
"Creating LUT for %s ratecode %d\n",
|
|
|
|
|
cstln_base::names[mcinfo->c],
|
|
|
|
|
mcinfo->rate
|
|
|
|
|
);
|
2019-03-17 16:31:42 -04:00
|
|
|
|
}
|
2021-03-21 21:06:26 -04:00
|
|
|
|
|
2021-03-24 17:18:22 -04:00
|
|
|
|
cstlns[modcod] = new cstln_lut<SOFTSYMB,256>(
|
|
|
|
|
mcinfo->c,
|
|
|
|
|
mcinfo->esn0_nf,
|
|
|
|
|
mcinfo->g1,
|
|
|
|
|
mcinfo->g2,
|
|
|
|
|
mcinfo->g3
|
|
|
|
|
);
|
|
|
|
|
#if 0
|
|
|
|
|
fprintf(stderr, "Dumping constellation LUT to stdout.\n");
|
|
|
|
|
cstlns[modcod]->dump(stdout);
|
|
|
|
|
exit(0);
|
|
|
|
|
#endif
|
2019-03-17 16:31:42 -04:00
|
|
|
|
}
|
2021-03-24 17:18:22 -04:00
|
|
|
|
|
|
|
|
|
return cstlns[modcod];
|
2019-03-17 16:31:42 -04:00
|
|
|
|
}
|
|
|
|
|
|
2021-03-24 17:18:22 -04:00
|
|
|
|
cstln_lut<SOFTSYMB,256> *cstln; // Last seen, or NULL (legacy)
|
2019-03-17 16:31:42 -04:00
|
|
|
|
trig16 trig;
|
2021-03-28 23:25:32 -04:00
|
|
|
|
pipereader< std::complex<T> > in;
|
2021-03-24 17:18:22 -04:00
|
|
|
|
pipewriter< plslot<SOFTSYMB> > out;
|
2019-03-17 16:31:42 -04:00
|
|
|
|
int meas_count;
|
|
|
|
|
pipewriter<float> *freq_out, *ss_out, *mer_out;
|
2021-03-28 23:25:32 -04:00
|
|
|
|
pipewriter< std::complex<float> > *cstln_out;
|
|
|
|
|
pipewriter< std::complex<float> > *cstln_pls_out;
|
|
|
|
|
pipewriter< std::complex<float> > *symbols_out;
|
2019-03-17 16:31:42 -04:00
|
|
|
|
pipewriter<int> *state_out;
|
2021-03-24 17:18:22 -04:00
|
|
|
|
bool first_run;
|
2019-03-17 16:31:42 -04:00
|
|
|
|
// S2 constants
|
|
|
|
|
s2_scrambling scrambling;
|
|
|
|
|
s2_sof<T> sof;
|
2021-03-24 17:18:22 -04:00
|
|
|
|
s2_pilot<T> pilot;
|
|
|
|
|
// Performance stats on PL signalling
|
|
|
|
|
uint32_t pls_total_errors, pls_total_count;
|
2019-07-16 20:51:46 -04:00
|
|
|
|
int m_modcodType;
|
|
|
|
|
int m_modcodRate;
|
2021-03-28 22:02:22 -04:00
|
|
|
|
bool m_locked;
|
2021-03-24 17:18:22 -04:00
|
|
|
|
|
|
|
|
|
private:
|
2021-03-28 23:25:32 -04:00
|
|
|
|
std::complex<T> *diffs;
|
2021-03-24 17:18:22 -04:00
|
|
|
|
sampler_state *sspilots;
|
|
|
|
|
}; // s2_frame_receiver
|
2019-03-17 16:31:42 -04:00
|
|
|
|
|
|
|
|
|
template <typename SOFTBYTE>
|
|
|
|
|
struct fecframe
|
|
|
|
|
{
|
|
|
|
|
s2_pls pls;
|
|
|
|
|
SOFTBYTE bytes[64800 / 8]; // Contains 16200/8 or 64800/8 bytes.
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
// S2 INTERLEAVER
|
|
|
|
|
// EN 302 307-1 section 5.3.3 Bit Interleaver
|
|
|
|
|
|
|
|
|
|
struct s2_interleaver : runnable
|
|
|
|
|
{
|
2021-03-12 18:13:00 -05:00
|
|
|
|
s2_interleaver(
|
|
|
|
|
scheduler *sch,
|
|
|
|
|
pipebuf<fecframe<hard_sb>> &_in,
|
|
|
|
|
pipebuf<plslot<hard_ss>> &_out
|
|
|
|
|
) :
|
|
|
|
|
runnable(sch, "S2 interleaver"),
|
|
|
|
|
in(_in),
|
|
|
|
|
out(_out, 1 + 360)
|
2019-03-17 16:31:42 -04:00
|
|
|
|
{
|
|
|
|
|
}
|
2021-03-21 21:06:26 -04:00
|
|
|
|
|
2019-03-17 16:31:42 -04:00
|
|
|
|
void run()
|
|
|
|
|
{
|
|
|
|
|
while (in.readable() >= 1)
|
|
|
|
|
{
|
|
|
|
|
const s2_pls *pls = &in.rd()->pls;
|
|
|
|
|
const modcod_info *mcinfo = check_modcod(pls->modcod);
|
|
|
|
|
int nslots = pls->sf ? mcinfo->nslots_nf / 4 : mcinfo->nslots_nf;
|
2021-03-21 21:06:26 -04:00
|
|
|
|
|
|
|
|
|
if (out.writable() < 1 + nslots) {
|
2019-03-17 16:31:42 -04:00
|
|
|
|
return;
|
2021-03-21 21:06:26 -04:00
|
|
|
|
}
|
|
|
|
|
|
2019-03-17 16:31:42 -04:00
|
|
|
|
const hard_sb *pbytes = in.rd()->bytes;
|
|
|
|
|
// Output pseudo slot with PLS.
|
|
|
|
|
plslot<hard_ss> *ppls = out.wr();
|
|
|
|
|
ppls->is_pls = true;
|
|
|
|
|
ppls->pls = *pls;
|
|
|
|
|
out.written(1);
|
|
|
|
|
// Interleave
|
|
|
|
|
plslot<hard_ss> *pout = out.wr();
|
2021-03-21 21:06:26 -04:00
|
|
|
|
|
2019-03-17 16:31:42 -04:00
|
|
|
|
if (mcinfo->nsymbols == 4)
|
2021-03-21 21:06:26 -04:00
|
|
|
|
{
|
2019-03-17 16:31:42 -04:00
|
|
|
|
serialize_qpsk(pbytes, nslots, pout);
|
2021-03-21 21:06:26 -04:00
|
|
|
|
}
|
2019-03-17 16:31:42 -04:00
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
int bps = log2(mcinfo->nsymbols);
|
|
|
|
|
int rows = pls->framebits() / bps;
|
2021-03-24 17:18:22 -04:00
|
|
|
|
|
2021-03-21 21:06:26 -04:00
|
|
|
|
if (mcinfo->nsymbols == 8 && mcinfo->rate == FEC35) {
|
2019-03-17 16:31:42 -04:00
|
|
|
|
interleave(bps, rows, pbytes, nslots, false, pout);
|
2021-03-21 21:06:26 -04:00
|
|
|
|
} else {
|
2019-03-17 16:31:42 -04:00
|
|
|
|
interleave(bps, rows, pbytes, nslots, true, pout);
|
2021-03-21 21:06:26 -04:00
|
|
|
|
}
|
2019-03-17 16:31:42 -04:00
|
|
|
|
}
|
2021-03-21 21:06:26 -04:00
|
|
|
|
|
2019-03-17 16:31:42 -04:00
|
|
|
|
in.read(1);
|
|
|
|
|
out.written(nslots);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private:
|
|
|
|
|
// Fill slots with serialized QPSK symbols, MSB first.
|
|
|
|
|
static void serialize_qpsk(const hard_sb *pin, int nslots,
|
|
|
|
|
plslot<hard_ss> *pout)
|
|
|
|
|
{
|
|
|
|
|
#if 0 // For reference
|
|
|
|
|
hard_sb acc;
|
|
|
|
|
int nacc = 0;
|
|
|
|
|
for ( ; nslots; --nslots,++pout ) {
|
|
|
|
|
pout->is_pls = false;
|
|
|
|
|
hard_ss *ps = pout->symbols;
|
|
|
|
|
for ( int ns=pout->LENGTH; ns--; ++ps ) {
|
|
|
|
|
if ( nacc < 2 ) { acc=*pin++; nacc=8; }
|
|
|
|
|
*ps = acc>>6;
|
|
|
|
|
acc <<= 2;
|
|
|
|
|
nacc -= 2;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
if ( nacc ) fail("Bug: s2_interleaver");
|
|
|
|
|
#else
|
2021-03-21 21:06:26 -04:00
|
|
|
|
if (nslots % 2) {
|
2019-03-17 16:31:42 -04:00
|
|
|
|
fatal("Bug: Truncated byte");
|
2021-03-21 21:06:26 -04:00
|
|
|
|
}
|
|
|
|
|
|
2019-03-17 16:31:42 -04:00
|
|
|
|
for (; nslots; nslots -= 2)
|
|
|
|
|
{
|
|
|
|
|
hard_sb b;
|
|
|
|
|
hard_ss *ps;
|
|
|
|
|
// Slot 0 (mod 2)
|
|
|
|
|
pout->is_pls = false;
|
|
|
|
|
ps = pout->symbols;
|
2021-03-21 21:06:26 -04:00
|
|
|
|
|
2019-03-17 16:31:42 -04:00
|
|
|
|
for (int i = 0; i < 22; ++i)
|
|
|
|
|
{
|
|
|
|
|
b = *pin++;
|
|
|
|
|
*ps++ = (b >> 6);
|
|
|
|
|
*ps++ = (b >> 4) & 3;
|
|
|
|
|
*ps++ = (b >> 2) & 3;
|
|
|
|
|
*ps++ = (b)&3;
|
|
|
|
|
}
|
2021-03-21 21:06:26 -04:00
|
|
|
|
|
2019-03-17 16:31:42 -04:00
|
|
|
|
b = *pin++;
|
|
|
|
|
*ps++ = (b >> 6);
|
|
|
|
|
*ps++ = (b >> 4) & 3;
|
|
|
|
|
// Slot 1 (mod 2)
|
|
|
|
|
++pout;
|
|
|
|
|
pout->is_pls = false;
|
|
|
|
|
ps = pout->symbols;
|
|
|
|
|
*ps++ = (b >> 2) & 3;
|
|
|
|
|
*ps++ = (b)&3;
|
2021-03-21 21:06:26 -04:00
|
|
|
|
|
2019-03-17 16:31:42 -04:00
|
|
|
|
for (int i = 0; i < 22; ++i)
|
|
|
|
|
{
|
|
|
|
|
b = *pin++;
|
|
|
|
|
*ps++ = (b >> 6);
|
|
|
|
|
*ps++ = (b >> 4) & 3;
|
|
|
|
|
*ps++ = (b >> 2) & 3;
|
|
|
|
|
*ps++ = (b)&3;
|
|
|
|
|
}
|
2021-03-21 21:06:26 -04:00
|
|
|
|
|
2019-03-17 16:31:42 -04:00
|
|
|
|
++pout;
|
|
|
|
|
}
|
|
|
|
|
#endif
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Fill slots with interleaved symbols.
|
|
|
|
|
// EN 302 307-1 figures 7 and 8
|
|
|
|
|
#if 0 // For reference
|
|
|
|
|
static void interleave(int bps, int rows,
|
|
|
|
|
const hard_sb *pin, int nslots,
|
|
|
|
|
bool msb_first, plslot<hard_ss> *pout) {
|
|
|
|
|
if ( bps==4 && rows==4050 && msb_first )
|
|
|
|
|
return interleave4050(pin, nslots, pout);
|
|
|
|
|
if ( rows % 8 ) fatal("modcod/framesize combination not supported\n");
|
|
|
|
|
int stride = rows/8; // Offset to next column, in bytes
|
|
|
|
|
hard_sb accs[bps]; // One accumulator per column
|
|
|
|
|
int nacc = 0; // Bits in each column accumulator
|
|
|
|
|
for ( ; nslots; --nslots,++pout ) {
|
|
|
|
|
pout->is_pls = false;
|
|
|
|
|
hard_ss *ps = pout->symbols;
|
|
|
|
|
for ( int ns=pout->LENGTH; ns--; ++ps ) {
|
|
|
|
|
if ( ! nacc ) {
|
|
|
|
|
const hard_sb *pi = pin;
|
|
|
|
|
for ( int b=0; b<bps; ++b,pi+=stride ) accs[b] = *pi;
|
|
|
|
|
++pin;
|
|
|
|
|
nacc = 8;
|
|
|
|
|
}
|
|
|
|
|
hard_ss symb = 0;
|
|
|
|
|
if ( msb_first )
|
|
|
|
|
for ( int b=0; b<bps; ++b ) {
|
|
|
|
|
symb = (symb<<1) | (accs[b]>>7);
|
|
|
|
|
accs[b] <<= 1;
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
for ( int b=bps; b--; ) {
|
|
|
|
|
symb = (symb<<1) | (accs[b]>>7);
|
|
|
|
|
accs[b] <<= 1;
|
|
|
|
|
}
|
|
|
|
|
--nacc;
|
|
|
|
|
*ps = symb;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
if ( nacc ) fail("Bug: s2_interleaver");
|
|
|
|
|
}
|
|
|
|
|
#else // reference
|
|
|
|
|
static void interleave(int bps, int rows,
|
|
|
|
|
const hard_sb *pin, int nslots,
|
|
|
|
|
bool msb_first, plslot<hard_ss> *pout)
|
|
|
|
|
{
|
|
|
|
|
void (*func)(int rows, const hard_sb *pin, int nslots,
|
|
|
|
|
plslot<hard_ss> *pout) = 0;
|
2021-03-21 21:06:26 -04:00
|
|
|
|
|
2019-03-17 16:31:42 -04:00
|
|
|
|
if (msb_first)
|
2021-03-21 21:06:26 -04:00
|
|
|
|
{
|
2019-03-17 16:31:42 -04:00
|
|
|
|
switch (bps)
|
|
|
|
|
{
|
|
|
|
|
case 2:
|
|
|
|
|
func = interleave<1, 2>;
|
|
|
|
|
break;
|
|
|
|
|
case 3:
|
|
|
|
|
func = interleave<1, 3>;
|
|
|
|
|
break;
|
|
|
|
|
case 4:
|
|
|
|
|
func = interleave<1, 4>;
|
|
|
|
|
break;
|
|
|
|
|
case 5:
|
|
|
|
|
func = interleave<1, 5>;
|
|
|
|
|
break;
|
|
|
|
|
default:
|
|
|
|
|
fail("Bad bps");
|
|
|
|
|
}
|
2021-03-21 21:06:26 -04:00
|
|
|
|
}
|
2019-03-17 16:31:42 -04:00
|
|
|
|
else
|
2021-03-21 21:06:26 -04:00
|
|
|
|
{
|
2019-03-17 16:31:42 -04:00
|
|
|
|
switch (bps)
|
|
|
|
|
{
|
|
|
|
|
case 2:
|
|
|
|
|
func = interleave<0, 2>;
|
|
|
|
|
break;
|
|
|
|
|
case 3:
|
|
|
|
|
func = interleave<0, 3>;
|
|
|
|
|
break;
|
|
|
|
|
case 4:
|
|
|
|
|
func = interleave<0, 4>;
|
|
|
|
|
break;
|
|
|
|
|
case 5:
|
|
|
|
|
func = interleave<0, 5>;
|
|
|
|
|
break;
|
|
|
|
|
default:
|
|
|
|
|
fail("Bad bps");
|
|
|
|
|
}
|
2021-03-21 21:06:26 -04:00
|
|
|
|
}
|
|
|
|
|
|
2019-03-17 16:31:42 -04:00
|
|
|
|
(*func)(rows, pin, nslots, pout);
|
|
|
|
|
}
|
2021-03-21 21:06:26 -04:00
|
|
|
|
|
2019-03-17 16:31:42 -04:00
|
|
|
|
template <int MSB_FIRST, int BPS>
|
|
|
|
|
static void interleave(int rows, const hard_sb *pin, int nslots,
|
|
|
|
|
plslot<hard_ss> *pout)
|
|
|
|
|
{
|
2021-03-21 21:06:26 -04:00
|
|
|
|
if (BPS == 4 && rows == 4050 && MSB_FIRST) {
|
2019-03-17 16:31:42 -04:00
|
|
|
|
return interleave4050(pin, nslots, pout);
|
2021-03-21 21:06:26 -04:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (rows % 8) {
|
2019-03-17 16:31:42 -04:00
|
|
|
|
fatal("modcod/framesize combination not supported\n");
|
2021-03-21 21:06:26 -04:00
|
|
|
|
}
|
|
|
|
|
|
2019-03-17 16:31:42 -04:00
|
|
|
|
int stride = rows / 8; // Offset to next column, in bytes
|
2021-03-21 21:06:26 -04:00
|
|
|
|
|
|
|
|
|
if (nslots % 4) {
|
2019-03-17 16:31:42 -04:00
|
|
|
|
fatal("Bug: Truncated byte");
|
2021-03-21 21:06:26 -04:00
|
|
|
|
}
|
|
|
|
|
|
2019-03-17 16:31:42 -04:00
|
|
|
|
// plslot::symbols[] are not packed across slots,
|
|
|
|
|
// so we need tos split bytes at boundaries.
|
|
|
|
|
for (; nslots; nslots -= 4)
|
|
|
|
|
{
|
|
|
|
|
hard_sb accs[BPS]; // One accumulator per column
|
|
|
|
|
hard_ss *ps;
|
|
|
|
|
// Slot 0 (mod 4): 88+2
|
|
|
|
|
pout->is_pls = false;
|
|
|
|
|
ps = pout->symbols;
|
2021-03-21 21:06:26 -04:00
|
|
|
|
|
2019-03-17 16:31:42 -04:00
|
|
|
|
for (int i = 0; i < 11; ++i)
|
|
|
|
|
{
|
|
|
|
|
split_byte<BPS>(pin++, stride, accs);
|
|
|
|
|
pop_symbols<MSB_FIRST, BPS>(accs, &ps, 8);
|
|
|
|
|
}
|
2021-03-21 21:06:26 -04:00
|
|
|
|
|
2019-03-17 16:31:42 -04:00
|
|
|
|
split_byte<BPS>(pin++, stride, accs);
|
|
|
|
|
pop_symbols<MSB_FIRST, BPS>(accs, &ps, 2);
|
|
|
|
|
++pout;
|
|
|
|
|
// Slot 1 (mod 4): 6+80+4
|
|
|
|
|
pout->is_pls = false;
|
|
|
|
|
ps = pout->symbols;
|
|
|
|
|
pop_symbols<MSB_FIRST, BPS>(accs, &ps, 6);
|
2021-03-21 21:06:26 -04:00
|
|
|
|
|
2019-03-17 16:31:42 -04:00
|
|
|
|
for (int i = 0; i < 10; ++i)
|
|
|
|
|
{
|
|
|
|
|
split_byte<BPS>(pin++, stride, accs);
|
|
|
|
|
pop_symbols<MSB_FIRST, BPS>(accs, &ps, 8);
|
|
|
|
|
}
|
2021-03-21 21:06:26 -04:00
|
|
|
|
|
2019-03-17 16:31:42 -04:00
|
|
|
|
split_byte<BPS>(pin++, stride, accs);
|
|
|
|
|
pop_symbols<MSB_FIRST, BPS>(accs, &ps, 4);
|
|
|
|
|
++pout;
|
|
|
|
|
// Slot 2 (mod 4): 4+80+6
|
|
|
|
|
pout->is_pls = false;
|
|
|
|
|
ps = pout->symbols;
|
|
|
|
|
pop_symbols<MSB_FIRST, BPS>(accs, &ps, 4);
|
2021-03-21 21:06:26 -04:00
|
|
|
|
|
2019-03-17 16:31:42 -04:00
|
|
|
|
for (int i = 0; i < 10; ++i)
|
|
|
|
|
{
|
|
|
|
|
split_byte<BPS>(pin++, stride, accs);
|
|
|
|
|
pop_symbols<MSB_FIRST, BPS>(accs, &ps, 8);
|
|
|
|
|
}
|
2021-03-21 21:06:26 -04:00
|
|
|
|
|
2019-03-17 16:31:42 -04:00
|
|
|
|
split_byte<BPS>(pin++, stride, accs);
|
|
|
|
|
pop_symbols<MSB_FIRST, BPS>(accs, &ps, 6);
|
|
|
|
|
++pout;
|
|
|
|
|
// Slot 3 (mod 4): 2+88
|
|
|
|
|
pout->is_pls = false;
|
|
|
|
|
ps = pout->symbols;
|
|
|
|
|
pop_symbols<MSB_FIRST, BPS>(accs, &ps, 2);
|
2021-03-21 21:06:26 -04:00
|
|
|
|
|
2019-03-17 16:31:42 -04:00
|
|
|
|
for (int i = 0; i < 11; ++i)
|
|
|
|
|
{
|
|
|
|
|
split_byte<BPS>(pin++, stride, accs);
|
|
|
|
|
pop_symbols<MSB_FIRST, BPS>(accs, &ps, 8);
|
|
|
|
|
}
|
2021-03-21 21:06:26 -04:00
|
|
|
|
|
2019-03-17 16:31:42 -04:00
|
|
|
|
++pout;
|
|
|
|
|
}
|
|
|
|
|
}
|
2021-03-21 21:06:26 -04:00
|
|
|
|
|
2019-03-17 16:31:42 -04:00
|
|
|
|
template <int BPS>
|
|
|
|
|
static inline void split_byte(const hard_sb *pi, int stride,
|
|
|
|
|
hard_sb accs[BPS])
|
|
|
|
|
{
|
|
|
|
|
// TBD Pass stride as template parameter.
|
2021-03-21 21:06:26 -04:00
|
|
|
|
for (int b = 0; b < BPS; ++b, pi += stride) {
|
2019-03-17 16:31:42 -04:00
|
|
|
|
accs[b] = *pi;
|
2021-03-21 21:06:26 -04:00
|
|
|
|
}
|
2019-03-17 16:31:42 -04:00
|
|
|
|
}
|
2021-03-21 21:06:26 -04:00
|
|
|
|
|
2019-03-17 16:31:42 -04:00
|
|
|
|
template <int MSB_FIRST, int BPS>
|
|
|
|
|
static void pop_symbols(hard_sb accs[BPS], hard_ss **ps, int ns)
|
|
|
|
|
{
|
|
|
|
|
for (int i = 0; i < ns; ++i)
|
|
|
|
|
{
|
|
|
|
|
hard_ss symb = 0;
|
|
|
|
|
// Check unrolling and constant propagation.
|
|
|
|
|
for (int b = 0; b < BPS; ++b)
|
2021-03-21 21:06:26 -04:00
|
|
|
|
{
|
|
|
|
|
if (MSB_FIRST) {
|
2019-03-17 16:31:42 -04:00
|
|
|
|
symb = (symb << 1) | (accs[b] >> 7);
|
2021-03-21 21:06:26 -04:00
|
|
|
|
} else {
|
2019-03-17 16:31:42 -04:00
|
|
|
|
symb = (symb << 1) | (accs[BPS - 1 - b] >> 7);
|
2021-03-21 21:06:26 -04:00
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
for (int b = 0; b < BPS; ++b) {
|
2019-03-17 16:31:42 -04:00
|
|
|
|
accs[b] <<= 1;
|
2021-03-21 21:06:26 -04:00
|
|
|
|
}
|
|
|
|
|
|
2019-03-17 16:31:42 -04:00
|
|
|
|
*(*ps)++ = symb;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
#endif // reference
|
|
|
|
|
|
|
|
|
|
// Special case for 16APSK short frames.
|
|
|
|
|
// 4050 rows is not a multiple of 8.
|
|
|
|
|
static void interleave4050(const hard_sb *pin, int nslots,
|
|
|
|
|
plslot<hard_ss> *pout)
|
|
|
|
|
{
|
|
|
|
|
hard_sb accs[4]; // One accumulator per column
|
|
|
|
|
int nacc = 0; // Bits in each column accumulator
|
2021-03-21 21:06:26 -04:00
|
|
|
|
|
2019-03-17 16:31:42 -04:00
|
|
|
|
for (; nslots; --nslots, ++pout)
|
|
|
|
|
{
|
|
|
|
|
pout->is_pls = false;
|
|
|
|
|
hard_ss *ps = pout->symbols;
|
2021-03-21 21:06:26 -04:00
|
|
|
|
|
2019-03-17 16:31:42 -04:00
|
|
|
|
for (int ns = pout->LENGTH; ns--; ++ps)
|
|
|
|
|
{
|
|
|
|
|
if (!nacc)
|
|
|
|
|
{
|
|
|
|
|
if (nslots == 1 && ns == 1)
|
|
|
|
|
{
|
|
|
|
|
// Special case just to avoid reading beyond end of buffer
|
|
|
|
|
accs[0] = pin[0];
|
|
|
|
|
accs[1] = (pin[506] << 2) | (pin[507] >> 6);
|
|
|
|
|
accs[2] = (pin[1012] << 4) | (pin[1013] >> 4);
|
|
|
|
|
accs[3] = (pin[1518] << 6);
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
accs[0] = pin[0];
|
|
|
|
|
accs[1] = (pin[506] << 2) | (pin[507] >> 6);
|
|
|
|
|
accs[2] = (pin[1012] << 4) | (pin[1013] >> 4);
|
|
|
|
|
accs[3] = (pin[1518] << 6) | (pin[1519] >> 2);
|
|
|
|
|
}
|
2021-03-21 21:06:26 -04:00
|
|
|
|
|
2019-03-17 16:31:42 -04:00
|
|
|
|
++pin;
|
|
|
|
|
nacc = 8;
|
|
|
|
|
}
|
2021-03-21 21:06:26 -04:00
|
|
|
|
|
2019-03-17 16:31:42 -04:00
|
|
|
|
hard_ss symb = 0;
|
2021-03-21 21:06:26 -04:00
|
|
|
|
|
2019-03-17 16:31:42 -04:00
|
|
|
|
for (int b = 0; b < 4; ++b)
|
|
|
|
|
{
|
|
|
|
|
symb = (symb << 1) | (accs[b] >> 7);
|
|
|
|
|
accs[b] <<= 1;
|
|
|
|
|
}
|
2021-03-21 21:06:26 -04:00
|
|
|
|
|
2019-03-17 16:31:42 -04:00
|
|
|
|
--nacc;
|
|
|
|
|
*ps = symb;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
pipereader<fecframe<hard_sb>> in;
|
|
|
|
|
pipewriter<plslot<hard_ss>> out;
|
|
|
|
|
}; // s2_interleaver
|
|
|
|
|
|
|
|
|
|
// S2 DEINTERLEAVER
|
|
|
|
|
// EN 302 307-1 section 5.3.3 Bit Interleaver
|
|
|
|
|
|
|
|
|
|
template <typename SOFTSYMB, typename SOFTBYTE>
|
|
|
|
|
struct s2_deinterleaver : runnable
|
|
|
|
|
{
|
2021-03-12 18:13:00 -05:00
|
|
|
|
s2_deinterleaver(
|
|
|
|
|
scheduler *sch,
|
|
|
|
|
pipebuf<plslot<SOFTSYMB>> &_in,
|
|
|
|
|
pipebuf<fecframe<SOFTBYTE>> &_out
|
|
|
|
|
) :
|
|
|
|
|
runnable(sch, "S2 deinterleaver"),
|
|
|
|
|
in(_in),
|
|
|
|
|
out(_out)
|
2019-03-17 16:31:42 -04:00
|
|
|
|
{
|
|
|
|
|
}
|
2021-03-12 18:13:00 -05:00
|
|
|
|
|
2019-03-17 16:31:42 -04:00
|
|
|
|
void run()
|
|
|
|
|
{
|
|
|
|
|
while (in.readable() >= 1 && out.writable() >= 1)
|
|
|
|
|
{
|
|
|
|
|
plslot<SOFTSYMB> *pin = in.rd();
|
2021-03-21 21:06:26 -04:00
|
|
|
|
|
|
|
|
|
if (!pin->is_pls) {
|
2019-03-17 16:31:42 -04:00
|
|
|
|
fail("s2_deinterleaver: bad input sequence");
|
2021-03-21 21:06:26 -04:00
|
|
|
|
}
|
|
|
|
|
|
2019-03-17 16:31:42 -04:00
|
|
|
|
s2_pls *pls = &pin->pls;
|
|
|
|
|
const modcod_info *mcinfo = check_modcod(pls->modcod);
|
|
|
|
|
int nslots = pls->sf ? mcinfo->nslots_nf / 4 : mcinfo->nslots_nf;
|
2021-03-21 21:06:26 -04:00
|
|
|
|
|
|
|
|
|
if (in.readable() < 1 + nslots) {
|
2019-03-17 16:31:42 -04:00
|
|
|
|
return;
|
2021-03-21 21:06:26 -04:00
|
|
|
|
}
|
|
|
|
|
|
2019-03-17 16:31:42 -04:00
|
|
|
|
fecframe<SOFTBYTE> *pout = out.wr();
|
|
|
|
|
pout->pls = *pls;
|
|
|
|
|
SOFTBYTE *pbytes = pout->bytes;
|
2021-03-21 21:06:26 -04:00
|
|
|
|
|
2019-03-17 16:31:42 -04:00
|
|
|
|
if (mcinfo->nsymbols == 4)
|
2021-03-21 21:06:26 -04:00
|
|
|
|
{
|
2019-03-17 16:31:42 -04:00
|
|
|
|
deserialize_qpsk(pin + 1, nslots, pbytes);
|
2021-03-21 21:06:26 -04:00
|
|
|
|
}
|
2019-03-17 16:31:42 -04:00
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
int bps = log2(mcinfo->nsymbols);
|
|
|
|
|
int rows = pls->framebits() / bps;
|
2021-03-21 21:06:26 -04:00
|
|
|
|
|
2021-03-24 17:18:22 -04:00
|
|
|
|
if ((mcinfo->nsymbols == 8) && (mcinfo->rate == FEC35)) {
|
2019-03-17 16:31:42 -04:00
|
|
|
|
deinterleave(bps, rows, pin + 1, nslots, false, pbytes);
|
2021-03-21 21:06:26 -04:00
|
|
|
|
} else {
|
2019-03-17 16:31:42 -04:00
|
|
|
|
deinterleave(bps, rows, pin + 1, nslots, true, pbytes);
|
2021-03-21 21:06:26 -04:00
|
|
|
|
}
|
2019-03-17 16:31:42 -04:00
|
|
|
|
}
|
2021-03-21 21:06:26 -04:00
|
|
|
|
|
2019-03-17 16:31:42 -04:00
|
|
|
|
in.read(1 + nslots);
|
|
|
|
|
out.written(1);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private:
|
|
|
|
|
// Deserialize slots of QPSK symbols, MSB first.
|
|
|
|
|
static void deserialize_qpsk(plslot<SOFTSYMB> *pin, int nslots,
|
|
|
|
|
SOFTBYTE *pout)
|
|
|
|
|
{
|
|
|
|
|
SOFTBYTE acc;
|
|
|
|
|
softword_clear(&acc); // gcc warning
|
|
|
|
|
int nacc = 0;
|
2021-03-21 21:06:26 -04:00
|
|
|
|
|
2019-03-17 16:31:42 -04:00
|
|
|
|
for (; nslots; --nslots, ++pin)
|
|
|
|
|
{
|
|
|
|
|
SOFTSYMB *ps = pin->symbols;
|
2021-03-21 21:06:26 -04:00
|
|
|
|
|
2019-03-17 16:31:42 -04:00
|
|
|
|
for (int ns = pin->LENGTH; ns--; ++ps)
|
|
|
|
|
{
|
|
|
|
|
pack_qpsk_symbol(*ps, &acc, nacc);
|
|
|
|
|
nacc += 2;
|
2021-03-21 21:06:26 -04:00
|
|
|
|
|
2019-03-17 16:31:42 -04:00
|
|
|
|
if (nacc == 8)
|
|
|
|
|
{ // TBD unroll
|
|
|
|
|
*pout++ = acc;
|
|
|
|
|
nacc = 0;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Deinterleave slots of symbols.
|
|
|
|
|
// EN 302 307-1 figures 7 and 8
|
|
|
|
|
#if 0 // For reference
|
|
|
|
|
static void deinterleave(int bps, int rows,
|
|
|
|
|
const plslot<SOFTSYMB> *pin, int nslots,
|
|
|
|
|
bool msb_first, SOFTBYTE *pout) {
|
|
|
|
|
if ( bps==4 && rows==4050 && msb_first )
|
|
|
|
|
return deinterleave4050(pin, nslots, pout);
|
|
|
|
|
if ( rows % 8 ) fatal("modcod/framesize combination not supported\n");
|
|
|
|
|
int stride = rows/8; // Offset to next column, in bytes
|
|
|
|
|
SOFTBYTE accs[bps];
|
|
|
|
|
for ( int b=0; b<bps; ++b ) softword_clear(&accs[b]); // gcc warning
|
|
|
|
|
int nacc = 0;
|
|
|
|
|
for ( ; nslots; --nslots,++pin ) {
|
|
|
|
|
const SOFTSYMB *ps = pin->symbols;
|
|
|
|
|
for ( int ns=pin->LENGTH; ns--; ++ps ) {
|
|
|
|
|
split_symbol(*ps, bps, accs, nacc, msb_first);
|
|
|
|
|
++nacc;
|
|
|
|
|
if ( nacc == 8 ) {
|
|
|
|
|
SOFTBYTE *po = pout;
|
|
|
|
|
for ( int b=0; b<bps; ++b,po+=stride ) *po = accs[b];
|
|
|
|
|
++pout;
|
|
|
|
|
nacc = 0;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
if ( nacc ) fail("Bug: s2_deinterleaver");
|
|
|
|
|
}
|
|
|
|
|
#else // reference
|
|
|
|
|
static void deinterleave(int bps, int rows,
|
|
|
|
|
const plslot<SOFTSYMB> *pin, int nslots,
|
|
|
|
|
bool msb_first, SOFTBYTE *pout)
|
|
|
|
|
{
|
|
|
|
|
void (*func)(int rows, const plslot<SOFTSYMB> *pin, int nslots,
|
|
|
|
|
SOFTBYTE *pout) = 0;
|
2021-03-21 21:06:26 -04:00
|
|
|
|
|
2019-03-17 16:31:42 -04:00
|
|
|
|
if (msb_first)
|
2021-03-21 21:06:26 -04:00
|
|
|
|
{
|
2019-03-17 16:31:42 -04:00
|
|
|
|
switch (bps)
|
|
|
|
|
{
|
|
|
|
|
case 2:
|
|
|
|
|
func = deinterleave<1, 2>;
|
|
|
|
|
break;
|
|
|
|
|
case 3:
|
|
|
|
|
func = deinterleave<1, 3>;
|
|
|
|
|
break;
|
|
|
|
|
case 4:
|
|
|
|
|
func = deinterleave<1, 4>;
|
|
|
|
|
break;
|
|
|
|
|
case 5:
|
|
|
|
|
func = deinterleave<1, 5>;
|
|
|
|
|
break;
|
|
|
|
|
default:
|
|
|
|
|
fail("Bad bps");
|
|
|
|
|
}
|
2021-03-21 21:06:26 -04:00
|
|
|
|
}
|
2019-03-17 16:31:42 -04:00
|
|
|
|
else
|
2021-03-21 21:06:26 -04:00
|
|
|
|
{
|
2019-03-17 16:31:42 -04:00
|
|
|
|
switch (bps)
|
|
|
|
|
{
|
|
|
|
|
case 2:
|
|
|
|
|
func = deinterleave<0, 2>;
|
|
|
|
|
break;
|
|
|
|
|
case 3:
|
|
|
|
|
func = deinterleave<0, 3>;
|
|
|
|
|
break;
|
|
|
|
|
case 4:
|
|
|
|
|
func = deinterleave<0, 4>;
|
|
|
|
|
break;
|
|
|
|
|
case 5:
|
|
|
|
|
func = deinterleave<0, 5>;
|
|
|
|
|
break;
|
|
|
|
|
default:
|
|
|
|
|
fail("Bad bps");
|
|
|
|
|
}
|
2021-03-21 21:06:26 -04:00
|
|
|
|
}
|
|
|
|
|
|
2019-03-17 16:31:42 -04:00
|
|
|
|
(*func)(rows, pin, nslots, pout);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
template <int MSB_FIRST, int BPS>
|
|
|
|
|
static void deinterleave(int rows, const plslot<SOFTSYMB> *pin, int nslots,
|
|
|
|
|
SOFTBYTE *pout)
|
|
|
|
|
{
|
2021-03-21 21:06:26 -04:00
|
|
|
|
if (BPS == 4 && rows == 4050 && MSB_FIRST) {
|
2019-03-17 16:31:42 -04:00
|
|
|
|
return deinterleave4050(pin, nslots, pout);
|
2021-03-21 21:06:26 -04:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (rows % 8) {
|
2019-03-17 16:31:42 -04:00
|
|
|
|
fatal("modcod/framesize combination not supported\n");
|
2021-03-21 21:06:26 -04:00
|
|
|
|
}
|
|
|
|
|
|
2019-03-17 16:31:42 -04:00
|
|
|
|
int stride = rows / 8; // Offset to next column, in bytes
|
|
|
|
|
SOFTBYTE accs[BPS];
|
2021-03-21 21:06:26 -04:00
|
|
|
|
|
|
|
|
|
for (int b = 0; b < BPS; ++b) {
|
2019-03-17 16:31:42 -04:00
|
|
|
|
softword_clear(&accs[b]); // gcc warning
|
2021-03-21 21:06:26 -04:00
|
|
|
|
}
|
|
|
|
|
|
2019-03-17 16:31:42 -04:00
|
|
|
|
int nacc = 0;
|
2021-03-21 21:06:26 -04:00
|
|
|
|
|
2019-03-17 16:31:42 -04:00
|
|
|
|
for (; nslots; --nslots, ++pin)
|
|
|
|
|
{
|
|
|
|
|
const SOFTSYMB *ps = pin->symbols;
|
2021-03-21 21:06:26 -04:00
|
|
|
|
|
2019-03-17 16:31:42 -04:00
|
|
|
|
for (int ns = pin->LENGTH; ns--; ++ps)
|
|
|
|
|
{
|
|
|
|
|
split_symbol(*ps, BPS, accs, nacc, MSB_FIRST);
|
|
|
|
|
++nacc;
|
2021-03-21 21:06:26 -04:00
|
|
|
|
|
2019-03-17 16:31:42 -04:00
|
|
|
|
if (nacc == 8)
|
|
|
|
|
{ // TBD Unroll, same as interleave()
|
|
|
|
|
SOFTBYTE *po = pout;
|
2021-03-21 21:06:26 -04:00
|
|
|
|
|
2019-03-17 16:31:42 -04:00
|
|
|
|
// TBD Pass stride as template parameter.
|
2021-03-21 21:06:26 -04:00
|
|
|
|
for (int b = 0; b < BPS; ++b, po += stride) {
|
2019-03-17 16:31:42 -04:00
|
|
|
|
*po = accs[b];
|
2021-03-21 21:06:26 -04:00
|
|
|
|
}
|
|
|
|
|
|
2019-03-17 16:31:42 -04:00
|
|
|
|
++pout;
|
|
|
|
|
nacc = 0;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
2021-03-21 21:06:26 -04:00
|
|
|
|
|
|
|
|
|
if (nacc) {
|
2019-03-17 16:31:42 -04:00
|
|
|
|
fail("Bug: s2_deinterleaver");
|
2021-03-21 21:06:26 -04:00
|
|
|
|
}
|
2019-03-17 16:31:42 -04:00
|
|
|
|
}
|
|
|
|
|
#endif // reference
|
|
|
|
|
|
|
|
|
|
// Special case for 16APSK short frames.
|
|
|
|
|
// 4050 rows is not a multiple of 8
|
|
|
|
|
// so we process rows one at a time rather than in chunks of 8.
|
|
|
|
|
static void deinterleave4050(const plslot<SOFTSYMB> *pin, int nslots,
|
|
|
|
|
SOFTBYTE *pout)
|
|
|
|
|
{
|
|
|
|
|
const int rows = 4050;
|
|
|
|
|
SOFTBYTE accs[4];
|
2021-03-21 21:06:26 -04:00
|
|
|
|
|
|
|
|
|
for (int b = 0; b < 4; ++b) {
|
2019-03-17 16:31:42 -04:00
|
|
|
|
softword_clear(&accs[b]); // gcc warning
|
2021-03-21 21:06:26 -04:00
|
|
|
|
}
|
|
|
|
|
|
2019-03-17 16:31:42 -04:00
|
|
|
|
int nacc = 0;
|
2021-03-21 21:06:26 -04:00
|
|
|
|
|
2019-03-17 16:31:42 -04:00
|
|
|
|
for (; nslots; --nslots, ++pin)
|
|
|
|
|
{
|
|
|
|
|
const SOFTSYMB *ps = pin->symbols;
|
2021-03-21 21:06:26 -04:00
|
|
|
|
|
2019-03-17 16:31:42 -04:00
|
|
|
|
for (int ns = pin->LENGTH; ns--; ++ps)
|
|
|
|
|
{
|
|
|
|
|
split_symbol(*ps, 4, accs, nacc, true);
|
|
|
|
|
++nacc;
|
2021-03-21 21:06:26 -04:00
|
|
|
|
|
2019-03-17 16:31:42 -04:00
|
|
|
|
if (nacc == 8)
|
|
|
|
|
{
|
|
|
|
|
for (int b = 0; b < 8; ++b)
|
|
|
|
|
{
|
|
|
|
|
softwords_set(pout, rows * 0 + b, softword_get(accs[0], b));
|
|
|
|
|
softwords_set(pout, rows * 1 + b, softword_get(accs[1], b));
|
|
|
|
|
softwords_set(pout, rows * 2 + b, softword_get(accs[2], b));
|
|
|
|
|
softwords_set(pout, rows * 3 + b, softword_get(accs[3], b));
|
|
|
|
|
}
|
2021-03-21 21:06:26 -04:00
|
|
|
|
|
2019-03-17 16:31:42 -04:00
|
|
|
|
++pout;
|
|
|
|
|
nacc = 0;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
2021-03-21 21:06:26 -04:00
|
|
|
|
|
|
|
|
|
if (nacc != 2) {
|
2019-03-17 16:31:42 -04:00
|
|
|
|
fatal("Bug: Expected 2 leftover rows\n");
|
2021-03-21 21:06:26 -04:00
|
|
|
|
}
|
|
|
|
|
|
2019-03-17 16:31:42 -04:00
|
|
|
|
// Pad with random symbol so that we can use accs[].
|
2021-03-21 21:06:26 -04:00
|
|
|
|
for (int b = nacc; b < 8; ++b) {
|
2019-03-17 16:31:42 -04:00
|
|
|
|
split_symbol(pin->symbols[0], 4, accs, b, true);
|
2021-03-21 21:06:26 -04:00
|
|
|
|
}
|
|
|
|
|
|
2019-03-17 16:31:42 -04:00
|
|
|
|
for (int b = 0; b < nacc; ++b)
|
|
|
|
|
{
|
|
|
|
|
softwords_set(pout, rows * 0 + b, softword_get(accs[0], b));
|
|
|
|
|
softwords_set(pout, rows * 1 + b, softword_get(accs[1], b));
|
|
|
|
|
softwords_set(pout, rows * 2 + b, softword_get(accs[2], b));
|
|
|
|
|
softwords_set(pout, rows * 3 + b, softword_get(accs[3], b));
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Spread LLR symbol across hard columns.
|
|
|
|
|
// Must call 8 times before using result because we use bit shifts.
|
|
|
|
|
static inline void split_symbol(const llr_ss &ps, int bps,
|
|
|
|
|
hard_sb accs[/*bps*/], int nacc,
|
|
|
|
|
bool msb_first)
|
|
|
|
|
{
|
2020-11-14 05:13:32 -05:00
|
|
|
|
(void) nacc;
|
2021-03-21 21:06:26 -04:00
|
|
|
|
|
2019-03-17 16:31:42 -04:00
|
|
|
|
if (msb_first)
|
|
|
|
|
{
|
2021-03-21 21:06:26 -04:00
|
|
|
|
for (int b = 0; b < bps; ++b) {
|
2019-03-17 16:31:42 -04:00
|
|
|
|
accs[b] = (accs[b] << 1) | llr_harden(ps.bits[bps - 1 - b]);
|
2021-03-21 21:06:26 -04:00
|
|
|
|
}
|
2019-03-17 16:31:42 -04:00
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
2021-03-21 21:06:26 -04:00
|
|
|
|
for (int b = 0; b < bps; ++b) {
|
2019-03-17 16:31:42 -04:00
|
|
|
|
accs[b] = (accs[b] << 1) | llr_harden(ps.bits[b]);
|
2021-03-21 21:06:26 -04:00
|
|
|
|
}
|
2019-03-17 16:31:42 -04:00
|
|
|
|
}
|
|
|
|
|
}
|
2021-03-21 21:06:26 -04:00
|
|
|
|
|
2019-03-17 16:31:42 -04:00
|
|
|
|
// Fast variant
|
|
|
|
|
template <int MSB_FIRST, int BPS>
|
|
|
|
|
static inline void split_symbol(const llr_ss &ps,
|
|
|
|
|
hard_sb accs[/*bps*/], int nacc)
|
|
|
|
|
{
|
|
|
|
|
if (MSB_FIRST)
|
|
|
|
|
{
|
2021-03-21 21:06:26 -04:00
|
|
|
|
for (int b = 0; b < BPS; ++b) {
|
2019-03-17 16:31:42 -04:00
|
|
|
|
accs[b] = (accs[b] << 1) | llr_harden(ps.bits[BPS - 1 - b]);
|
2021-03-21 21:06:26 -04:00
|
|
|
|
}
|
2019-03-17 16:31:42 -04:00
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
2021-03-21 21:06:26 -04:00
|
|
|
|
for (int b = 0; b < BPS; ++b) {
|
2019-03-17 16:31:42 -04:00
|
|
|
|
accs[b] = (accs[b] << 1) | llr_harden(ps.bits[b]);
|
2021-03-21 21:06:26 -04:00
|
|
|
|
}
|
2019-03-17 16:31:42 -04:00
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Spread LLR symbol across LLR columns.
|
2021-03-21 21:06:26 -04:00
|
|
|
|
static inline void split_symbol(
|
|
|
|
|
const llr_ss &ps,
|
|
|
|
|
int bps,
|
|
|
|
|
llr_sb accs[/*bps*/],
|
|
|
|
|
int nacc,
|
|
|
|
|
bool msb_first
|
|
|
|
|
)
|
2019-03-17 16:31:42 -04:00
|
|
|
|
{
|
|
|
|
|
if (msb_first)
|
|
|
|
|
{
|
2021-03-21 21:06:26 -04:00
|
|
|
|
for (int b = 0; b < bps; ++b) {
|
2019-03-17 16:31:42 -04:00
|
|
|
|
accs[b].bits[nacc] = ps.bits[bps - 1 - b];
|
2021-03-21 21:06:26 -04:00
|
|
|
|
}
|
2019-03-17 16:31:42 -04:00
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
2021-03-21 21:06:26 -04:00
|
|
|
|
for (int b = 0; b < bps; ++b) {
|
2019-03-17 16:31:42 -04:00
|
|
|
|
accs[b].bits[nacc] = ps.bits[b];
|
2021-03-21 21:06:26 -04:00
|
|
|
|
}
|
2019-03-17 16:31:42 -04:00
|
|
|
|
}
|
|
|
|
|
}
|
2021-03-21 21:06:26 -04:00
|
|
|
|
|
2019-03-17 16:31:42 -04:00
|
|
|
|
// Fast variant
|
|
|
|
|
template <int MSB_FIRST, int BPS>
|
2021-03-21 21:06:26 -04:00
|
|
|
|
static inline void split_symbol(
|
|
|
|
|
const llr_ss &ps,
|
|
|
|
|
llr_sb accs[/*bps*/],
|
|
|
|
|
int nacc)
|
2019-03-17 16:31:42 -04:00
|
|
|
|
{
|
|
|
|
|
if (MSB_FIRST)
|
|
|
|
|
{
|
2021-03-21 21:06:26 -04:00
|
|
|
|
for (int b = 0; b < BPS; ++b) {
|
2019-03-17 16:31:42 -04:00
|
|
|
|
accs[b].bits[nacc] = ps.bits[BPS - 1 - b];
|
2021-03-21 21:06:26 -04:00
|
|
|
|
}
|
2019-03-17 16:31:42 -04:00
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
2021-03-21 21:06:26 -04:00
|
|
|
|
for (int b = 0; b < BPS; ++b) {
|
2019-03-17 16:31:42 -04:00
|
|
|
|
accs[b].bits[nacc] = ps.bits[b];
|
2021-03-21 21:06:26 -04:00
|
|
|
|
}
|
2019-03-17 16:31:42 -04:00
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Merge QPSK LLR symbol into hard byte.
|
2021-03-21 21:06:26 -04:00
|
|
|
|
static inline void pack_qpsk_symbol(
|
|
|
|
|
const llr_ss &ps,
|
|
|
|
|
hard_sb *acc,
|
|
|
|
|
int nacc
|
|
|
|
|
)
|
2019-03-17 16:31:42 -04:00
|
|
|
|
{
|
2020-11-14 05:13:32 -05:00
|
|
|
|
(void) nacc;
|
2019-03-17 16:31:42 -04:00
|
|
|
|
// TBD Must match LLR law, see softsymb_harden.
|
|
|
|
|
uint8_t s = llr_harden(ps.bits[0]) | (llr_harden(ps.bits[1]) << 1);
|
|
|
|
|
*acc = (*acc << 2) | s;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Merge QPSK LLR symbol into LLR byte.
|
2021-03-21 21:06:26 -04:00
|
|
|
|
static inline void pack_qpsk_symbol(
|
|
|
|
|
const llr_ss &ps,
|
|
|
|
|
llr_sb *acc,
|
|
|
|
|
int nacc
|
|
|
|
|
)
|
2019-03-17 16:31:42 -04:00
|
|
|
|
{
|
|
|
|
|
acc->bits[nacc] = ps.bits[1];
|
|
|
|
|
acc->bits[nacc + 1] = ps.bits[0];
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
pipereader<plslot<SOFTSYMB>> in;
|
|
|
|
|
pipewriter<fecframe<SOFTBYTE>> out;
|
|
|
|
|
}; // s2_deinterleaver
|
|
|
|
|
|
|
|
|
|
typedef ldpc_table<uint16_t> s2_ldpc_table;
|
|
|
|
|
typedef ldpc_engine<bool, hard_sb, 8, uint16_t> s2_ldpc_engine;
|
|
|
|
|
|
|
|
|
|
#include "dvbs2_data.h"
|
|
|
|
|
|
|
|
|
|
static const struct fec_info
|
|
|
|
|
{
|
|
|
|
|
static const int KBCH_MAX = 58192;
|
|
|
|
|
int Kbch; // BCH message size (bits)
|
|
|
|
|
int kldpc; // LDPC message size (= BCH codeword size) (bits)
|
|
|
|
|
int t; // BCH error correction
|
|
|
|
|
const s2_ldpc_table *ldpc;
|
2021-03-21 21:06:26 -04:00
|
|
|
|
}
|
|
|
|
|
fec_infos[2][FEC_COUNT] =
|
|
|
|
|
{
|
2019-03-17 16:31:42 -04:00
|
|
|
|
{
|
2019-07-17 07:57:50 -04:00
|
|
|
|
// Normal frames - must respect enum code_rate order
|
|
|
|
|
{32208, 32400, 12, &ldpc_nf_fec12}, // FEC12 (was [FEC12] = {...} and so on. Does not compile with MSVC)
|
|
|
|
|
{43040, 43200, 10, &ldpc_nf_fec23}, // FEC23
|
2020-11-14 20:19:06 -05:00
|
|
|
|
{0, 0, 0, nullptr}, // FEC46
|
2019-07-17 07:57:50 -04:00
|
|
|
|
{48408, 48600, 12, &ldpc_nf_fec34}, // FEC34
|
|
|
|
|
{53840, 54000, 10, &ldpc_nf_fec56}, // FEC56
|
2020-11-14 20:19:06 -05:00
|
|
|
|
{0, 0, 0, nullptr}, // FEC78
|
2019-07-17 07:57:50 -04:00
|
|
|
|
{51648, 51840, 12, &ldpc_nf_fec45}, // FEC45
|
|
|
|
|
{57472, 57600, 8, &ldpc_nf_fec89}, // FEC89
|
|
|
|
|
{58192, 58320, 8, &ldpc_nf_fec910}, // FEC910
|
|
|
|
|
{16008, 16200, 12, &ldpc_nf_fec14}, // FEC14
|
|
|
|
|
{21408, 21600, 12, &ldpc_nf_fec13}, // FEC13
|
|
|
|
|
{25728, 25920, 12, &ldpc_nf_fec25}, // FEC25
|
|
|
|
|
{38688, 38880, 12, &ldpc_nf_fec35}, // FEC35
|
2019-03-17 16:31:42 -04:00
|
|
|
|
},
|
|
|
|
|
{
|
2019-07-17 07:57:50 -04:00
|
|
|
|
// Short frames - must respect enum code_rate order
|
|
|
|
|
{7032, 7200, 12, &ldpc_sf_fec12}, // FEC12 (was [FEC12] = {...} and so on. Does not compile with MSVC)
|
|
|
|
|
{10632, 10800, 12, &ldpc_sf_fec23}, // FEC23
|
2020-11-14 20:19:06 -05:00
|
|
|
|
{0, 0, 0, nullptr}, // FEC46
|
2019-07-17 07:57:50 -04:00
|
|
|
|
{11712, 11880, 12, &ldpc_sf_fec34}, // FEC34
|
|
|
|
|
{13152, 13320, 12, &ldpc_sf_fec56}, // FEC56
|
2020-11-14 20:19:06 -05:00
|
|
|
|
{0, 0, 0, nullptr}, // FEC78
|
2019-07-17 07:57:50 -04:00
|
|
|
|
{12432, 12600, 12, &ldpc_sf_fec45}, // FEC45
|
|
|
|
|
{14232, 14400, 12, &ldpc_sf_fec89}, // FEC89
|
2020-11-14 20:19:06 -05:00
|
|
|
|
{0, 0, 0, nullptr}, // FEC910
|
2019-07-17 07:57:50 -04:00
|
|
|
|
{3072, 3240, 12, &ldpc_sf_fec14}, // FEC14
|
|
|
|
|
{5232, 5400, 12, &ldpc_sf_fec13}, // FEC13
|
|
|
|
|
{6312, 6480, 12, &ldpc_sf_fec25}, // FEC25
|
|
|
|
|
{9552, 9720, 12, &ldpc_sf_fec35}, // FEC35
|
2019-03-17 16:31:42 -04:00
|
|
|
|
},
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
struct bbframe
|
|
|
|
|
{
|
|
|
|
|
s2_pls pls;
|
|
|
|
|
uint8_t bytes[58192 / 8]; // Kbch/8 max
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
// S2_LDPC_ENGINES
|
|
|
|
|
// Initializes LDPC engines for all DVB-S2 FEC settings.
|
|
|
|
|
|
|
|
|
|
template <typename SOFTBIT, typename SOFTBYTE>
|
|
|
|
|
struct s2_ldpc_engines
|
|
|
|
|
{
|
|
|
|
|
typedef ldpc_engine<SOFTBIT, SOFTBYTE, 8, uint16_t> s2_ldpc_engine;
|
|
|
|
|
s2_ldpc_engine *ldpcs[2][FEC_COUNT]; // [shortframes][fec]
|
2021-03-21 21:06:26 -04:00
|
|
|
|
|
2019-03-17 16:31:42 -04:00
|
|
|
|
s2_ldpc_engines()
|
|
|
|
|
{
|
2021-03-21 21:06:26 -04:00
|
|
|
|
// memset(ldpcs, 0, sizeof(ldpcs)); // useless as initialization comes next
|
|
|
|
|
|
2019-03-17 16:31:42 -04:00
|
|
|
|
for (int sf = 0; sf <= 1; ++sf)
|
|
|
|
|
{
|
|
|
|
|
for (int fec = 0; fec < FEC_COUNT; ++fec)
|
|
|
|
|
{
|
|
|
|
|
const fec_info *fi = &fec_infos[sf][fec];
|
2021-03-21 21:06:26 -04:00
|
|
|
|
|
2019-03-17 16:31:42 -04:00
|
|
|
|
if (!fi->ldpc)
|
|
|
|
|
{
|
2021-03-06 01:37:44 -05:00
|
|
|
|
ldpcs[sf][fec] = nullptr;
|
2019-03-17 16:31:42 -04:00
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
int n = (sf ? 64800 / 4 : 64800);
|
|
|
|
|
int k = fi->kldpc;
|
|
|
|
|
ldpcs[sf][fec] = new s2_ldpc_engine(fi->ldpc, k, n);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
2021-03-21 21:06:26 -04:00
|
|
|
|
|
|
|
|
|
~s2_ldpc_engines()
|
|
|
|
|
{
|
|
|
|
|
for (int sf = 0; sf <= 1; ++sf)
|
|
|
|
|
{
|
|
|
|
|
for (int fec = 0; fec < FEC_COUNT; ++fec)
|
|
|
|
|
{
|
|
|
|
|
if (ldpcs[sf][fec]) {
|
|
|
|
|
delete ldpcs[sf][fec];
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2019-03-17 16:31:42 -04:00
|
|
|
|
void print_node_stats()
|
|
|
|
|
{
|
|
|
|
|
for (int sf = 0; sf <= 1; ++sf)
|
2021-03-21 21:06:26 -04:00
|
|
|
|
{
|
2019-03-17 16:31:42 -04:00
|
|
|
|
for (int fec = 0; fec < FEC_COUNT; ++fec)
|
|
|
|
|
{
|
|
|
|
|
s2_ldpc_engine *ldpc = ldpcs[sf][fec];
|
2021-03-21 21:06:26 -04:00
|
|
|
|
|
|
|
|
|
if (ldpc) {
|
2019-03-17 16:31:42 -04:00
|
|
|
|
ldpc->print_node_stats();
|
2021-03-21 21:06:26 -04:00
|
|
|
|
}
|
2019-03-17 16:31:42 -04:00
|
|
|
|
}
|
2021-03-21 21:06:26 -04:00
|
|
|
|
}
|
2019-03-17 16:31:42 -04:00
|
|
|
|
}
|
|
|
|
|
}; // s2_ldpc_engines
|
|
|
|
|
|
|
|
|
|
// S2_BCH_ENGINES
|
|
|
|
|
// Initializes BCH engines for all DVB-S2 FEC settings.
|
|
|
|
|
|
|
|
|
|
struct s2_bch_engines
|
|
|
|
|
{
|
|
|
|
|
bch_interface *bchs[2][FEC_COUNT];
|
|
|
|
|
// N=t*m
|
|
|
|
|
// The generator of GF(2^m) is always g1.
|
|
|
|
|
// Normal frames with 8, 10 or 12 polynomials.
|
|
|
|
|
typedef bch_engine<uint32_t, 192, 17, 16, uint16_t, 0x002d> s2_bch_engine_nf12;
|
|
|
|
|
typedef bch_engine<uint32_t, 160, 17, 16, uint16_t, 0x002d> s2_bch_engine_nf10;
|
|
|
|
|
typedef bch_engine<uint32_t, 128, 17, 16, uint16_t, 0x002d> s2_bch_engine_nf8;
|
|
|
|
|
// Short frames with 12 polynomials.
|
|
|
|
|
typedef bch_engine<uint32_t, 168, 17, 14, uint16_t, 0x002b> s2_bch_engine_sf12;
|
2021-03-21 21:06:26 -04:00
|
|
|
|
|
2019-03-17 16:31:42 -04:00
|
|
|
|
s2_bch_engines()
|
|
|
|
|
{
|
|
|
|
|
bitvect<uint32_t, 17> bch_polys[2][12]; // [shortframes][polyindex]
|
2021-03-21 21:06:26 -04:00
|
|
|
|
|
2019-03-17 16:31:42 -04:00
|
|
|
|
// EN 302 307-1 5.3.1 Table 6a (polynomials for normal frames)
|
|
|
|
|
bch_polys[0][0] = bitvect<uint32_t, 17>(0x1002d); // g1
|
|
|
|
|
bch_polys[0][1] = bitvect<uint32_t, 17>(0x10173); // g2
|
|
|
|
|
bch_polys[0][2] = bitvect<uint32_t, 17>(0x10fbd); // g3
|
|
|
|
|
bch_polys[0][3] = bitvect<uint32_t, 17>(0x15a55); // g4
|
|
|
|
|
bch_polys[0][4] = bitvect<uint32_t, 17>(0x11f2f); // g5
|
|
|
|
|
bch_polys[0][5] = bitvect<uint32_t, 17>(0x1f7b5); // g6
|
|
|
|
|
bch_polys[0][6] = bitvect<uint32_t, 17>(0x1af65); // g7
|
|
|
|
|
bch_polys[0][7] = bitvect<uint32_t, 17>(0x17367); // g8
|
|
|
|
|
bch_polys[0][8] = bitvect<uint32_t, 17>(0x10ea1); // g9
|
|
|
|
|
bch_polys[0][9] = bitvect<uint32_t, 17>(0x175a7); // g10
|
|
|
|
|
bch_polys[0][10] = bitvect<uint32_t, 17>(0x13a2d); // g11
|
|
|
|
|
bch_polys[0][11] = bitvect<uint32_t, 17>(0x11ae3); // g12
|
2021-03-21 21:06:26 -04:00
|
|
|
|
|
2019-03-17 16:31:42 -04:00
|
|
|
|
// EN 302 307-1 5.3.1 Table 6b (polynomials for short frames)
|
|
|
|
|
bch_polys[1][0] = bitvect<uint32_t, 17>(0x402b); // g1
|
|
|
|
|
bch_polys[1][1] = bitvect<uint32_t, 17>(0x4941); // g2
|
|
|
|
|
bch_polys[1][2] = bitvect<uint32_t, 17>(0x4647); // g3
|
|
|
|
|
bch_polys[1][3] = bitvect<uint32_t, 17>(0x5591); // g4
|
|
|
|
|
bch_polys[1][4] = bitvect<uint32_t, 17>(0x6b55); // g5
|
|
|
|
|
bch_polys[1][5] = bitvect<uint32_t, 17>(0x6389); // g6
|
|
|
|
|
bch_polys[1][6] = bitvect<uint32_t, 17>(0x6ce5); // g7
|
|
|
|
|
bch_polys[1][7] = bitvect<uint32_t, 17>(0x4f21); // g8
|
|
|
|
|
bch_polys[1][8] = bitvect<uint32_t, 17>(0x460f); // g9
|
|
|
|
|
bch_polys[1][9] = bitvect<uint32_t, 17>(0x5a49); // g10
|
|
|
|
|
bch_polys[1][10] = bitvect<uint32_t, 17>(0x5811); // g11
|
|
|
|
|
bch_polys[1][11] = bitvect<uint32_t, 17>(0x65ef); // g12
|
2021-03-21 21:06:26 -04:00
|
|
|
|
|
2019-03-17 16:31:42 -04:00
|
|
|
|
// Redundant with fec_infos[], but needs static template argument.
|
2021-03-21 21:06:26 -04:00
|
|
|
|
|
|
|
|
|
for (int sf = 0; sf <= 1; ++sf)
|
|
|
|
|
{
|
|
|
|
|
for (int fec = 0; fec < FEC_COUNT; ++fec) {
|
|
|
|
|
bchs[sf][fec] = nullptr;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2019-03-17 16:31:42 -04:00
|
|
|
|
bchs[0][FEC12] = new s2_bch_engine_nf12(bch_polys[0], 12);
|
|
|
|
|
bchs[0][FEC23] = new s2_bch_engine_nf10(bch_polys[0], 10);
|
|
|
|
|
bchs[0][FEC34] = new s2_bch_engine_nf12(bch_polys[0], 12);
|
|
|
|
|
bchs[0][FEC56] = new s2_bch_engine_nf10(bch_polys[0], 10);
|
|
|
|
|
bchs[0][FEC45] = new s2_bch_engine_nf12(bch_polys[0], 12);
|
|
|
|
|
bchs[0][FEC89] = new s2_bch_engine_nf8(bch_polys[0], 8);
|
|
|
|
|
bchs[0][FEC910] = new s2_bch_engine_nf8(bch_polys[0], 8);
|
|
|
|
|
bchs[0][FEC14] = new s2_bch_engine_nf12(bch_polys[0], 12);
|
|
|
|
|
bchs[0][FEC13] = new s2_bch_engine_nf12(bch_polys[0], 12);
|
|
|
|
|
bchs[0][FEC25] = new s2_bch_engine_nf12(bch_polys[0], 12);
|
|
|
|
|
bchs[0][FEC35] = new s2_bch_engine_nf12(bch_polys[0], 12);
|
2021-03-21 21:06:26 -04:00
|
|
|
|
|
2019-03-17 16:31:42 -04:00
|
|
|
|
bchs[1][FEC12] = new s2_bch_engine_sf12(bch_polys[1], 12);
|
|
|
|
|
bchs[1][FEC23] = new s2_bch_engine_sf12(bch_polys[1], 12);
|
|
|
|
|
bchs[1][FEC34] = new s2_bch_engine_sf12(bch_polys[1], 12);
|
|
|
|
|
bchs[1][FEC56] = new s2_bch_engine_sf12(bch_polys[1], 12);
|
|
|
|
|
bchs[1][FEC45] = new s2_bch_engine_sf12(bch_polys[1], 12);
|
|
|
|
|
bchs[1][FEC89] = new s2_bch_engine_sf12(bch_polys[1], 12);
|
|
|
|
|
bchs[1][FEC14] = new s2_bch_engine_sf12(bch_polys[1], 12);
|
|
|
|
|
bchs[1][FEC13] = new s2_bch_engine_sf12(bch_polys[1], 12);
|
|
|
|
|
bchs[1][FEC25] = new s2_bch_engine_sf12(bch_polys[1], 12);
|
|
|
|
|
bchs[1][FEC35] = new s2_bch_engine_sf12(bch_polys[1], 12);
|
|
|
|
|
}
|
2021-03-21 21:06:26 -04:00
|
|
|
|
|
|
|
|
|
~s2_bch_engines()
|
|
|
|
|
{
|
|
|
|
|
for (int sf = 0; sf <= 1; ++sf)
|
|
|
|
|
{
|
|
|
|
|
for (int fec = 0; fec < FEC_COUNT; ++fec)
|
|
|
|
|
{
|
|
|
|
|
if (bchs[sf][fec]) {
|
|
|
|
|
delete bchs[sf][fec];
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
2019-03-17 16:31:42 -04:00
|
|
|
|
}; // s2_bch_engines
|
|
|
|
|
|
|
|
|
|
// S2 BASEBAND DESCRAMBLER AND FEC ENCODER
|
|
|
|
|
// EN 302 307-1 section 5.2.2
|
|
|
|
|
// EN 302 307-1 section 5.3
|
|
|
|
|
|
|
|
|
|
struct s2_fecenc : runnable
|
|
|
|
|
{
|
|
|
|
|
typedef ldpc_engine<bool, hard_sb, 8, uint16_t> s2_ldpc_engine;
|
2021-03-21 21:06:26 -04:00
|
|
|
|
|
2021-03-12 18:13:00 -05:00
|
|
|
|
s2_fecenc(
|
|
|
|
|
scheduler *sch,
|
|
|
|
|
pipebuf<bbframe> &_in,
|
|
|
|
|
pipebuf<fecframe<hard_sb>> &_out
|
|
|
|
|
) :
|
|
|
|
|
runnable(sch, "S2 fecenc"),
|
|
|
|
|
in(_in),
|
|
|
|
|
out(_out)
|
2019-03-17 16:31:42 -04:00
|
|
|
|
{
|
2021-03-21 21:06:26 -04:00
|
|
|
|
if (sch->debug) {
|
2019-03-17 16:31:42 -04:00
|
|
|
|
s2ldpc.print_node_stats();
|
2021-03-21 21:06:26 -04:00
|
|
|
|
}
|
2019-03-17 16:31:42 -04:00
|
|
|
|
}
|
2021-03-21 21:06:26 -04:00
|
|
|
|
|
2019-03-17 16:31:42 -04:00
|
|
|
|
void run()
|
|
|
|
|
{
|
|
|
|
|
while (in.readable() >= 1 && out.writable() >= 1)
|
|
|
|
|
{
|
|
|
|
|
run_frame(in.rd(), out.wr());
|
|
|
|
|
in.read(1);
|
|
|
|
|
out.written(1);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private:
|
|
|
|
|
void run_frame(const bbframe *pin, fecframe<hard_sb> *pout)
|
|
|
|
|
{
|
|
|
|
|
const modcod_info *mcinfo = check_modcod(pin->pls.modcod);
|
|
|
|
|
const fec_info *fi = &fec_infos[pin->pls.sf][mcinfo->rate];
|
|
|
|
|
pout->pls = pin->pls;
|
|
|
|
|
hard_sb *pbytes = pout->bytes;
|
|
|
|
|
bbscrambling.transform(pin->bytes, fi->Kbch / 8, pbytes);
|
2021-03-21 21:06:26 -04:00
|
|
|
|
|
2019-03-17 16:31:42 -04:00
|
|
|
|
{ // BCH
|
|
|
|
|
size_t msgbytes = fi->Kbch / 8;
|
|
|
|
|
bch_interface *bch = s2bch.bchs[pin->pls.sf][mcinfo->rate];
|
|
|
|
|
bch->encode(pbytes, msgbytes, pbytes + msgbytes);
|
|
|
|
|
}
|
2021-03-21 21:06:26 -04:00
|
|
|
|
|
2019-03-17 16:31:42 -04:00
|
|
|
|
{ // LDPC
|
|
|
|
|
size_t msgbits = fi->kldpc;
|
|
|
|
|
size_t cwbits = pin->pls.framebits();
|
|
|
|
|
s2_ldpc_engine *ldpc = s2ldpc.ldpcs[pin->pls.sf][mcinfo->rate];
|
|
|
|
|
ldpc->encode(fi->ldpc, pbytes, msgbits, cwbits, pbytes + msgbits / 8);
|
|
|
|
|
}
|
|
|
|
|
}
|
2021-03-21 21:06:26 -04:00
|
|
|
|
|
2019-03-17 16:31:42 -04:00
|
|
|
|
pipereader<bbframe> in;
|
|
|
|
|
pipewriter<fecframe<hard_sb>> out;
|
|
|
|
|
s2_bbscrambling bbscrambling;
|
|
|
|
|
s2_bch_engines s2bch;
|
|
|
|
|
s2_ldpc_engines<bool, hard_sb> s2ldpc;
|
|
|
|
|
}; // s2_fecenc
|
|
|
|
|
|
|
|
|
|
// S2 FEC DECODER AND BASEBAND DESCRAMBLER
|
|
|
|
|
// EN 302 307-1 section 5.3
|
|
|
|
|
// EN 302 307-1 section 5.2.2
|
|
|
|
|
|
|
|
|
|
template <typename SOFTBIT, typename SOFTBYTE>
|
|
|
|
|
struct s2_fecdec : runnable
|
|
|
|
|
{
|
|
|
|
|
int bitflips;
|
2021-03-21 21:06:26 -04:00
|
|
|
|
|
2021-03-12 18:13:00 -05:00
|
|
|
|
s2_fecdec(
|
|
|
|
|
scheduler *sch,
|
|
|
|
|
pipebuf<fecframe<SOFTBYTE>> &_in, pipebuf<bbframe> &_out,
|
|
|
|
|
pipebuf<int> *_bitcount = nullptr,
|
|
|
|
|
pipebuf<int> *_errcount = nullptr
|
|
|
|
|
) :
|
|
|
|
|
runnable(sch, "S2 fecdec"),
|
|
|
|
|
bitflips(0),
|
|
|
|
|
in(_in),
|
|
|
|
|
out(_out),
|
|
|
|
|
bitcount(opt_writer(_bitcount, 1)),
|
|
|
|
|
errcount(opt_writer(_errcount, 1))
|
2019-03-17 16:31:42 -04:00
|
|
|
|
{
|
2021-03-21 21:06:26 -04:00
|
|
|
|
if (sch->debug) {
|
2019-03-17 16:31:42 -04:00
|
|
|
|
s2ldpc.print_node_stats();
|
2021-03-21 21:06:26 -04:00
|
|
|
|
}
|
2019-03-17 16:31:42 -04:00
|
|
|
|
}
|
2021-03-21 21:06:26 -04:00
|
|
|
|
|
2019-03-17 16:31:42 -04:00
|
|
|
|
void run()
|
|
|
|
|
{
|
|
|
|
|
while (in.readable() >= 1 && out.writable() >= 1 &&
|
|
|
|
|
opt_writable(bitcount, 1) && opt_writable(errcount, 1))
|
|
|
|
|
{
|
|
|
|
|
fecframe<SOFTBYTE> *pin = in.rd();
|
|
|
|
|
const modcod_info *mcinfo = check_modcod(pin->pls.modcod);
|
|
|
|
|
const fec_info *fi = &fec_infos[pin->pls.sf][mcinfo->rate];
|
|
|
|
|
bool corrupted = false;
|
|
|
|
|
bool residual_errors;
|
2021-03-21 21:06:26 -04:00
|
|
|
|
|
2019-03-17 16:31:42 -04:00
|
|
|
|
if (true)
|
|
|
|
|
{
|
|
|
|
|
// LDPC decode
|
|
|
|
|
size_t cwbits = pin->pls.framebits();
|
|
|
|
|
size_t msgbits = fi->kldpc;
|
|
|
|
|
s2_ldpc_engine *ldpc = s2ldpc.ldpcs[pin->pls.sf][mcinfo->rate];
|
|
|
|
|
int ncorr = ldpc->decode_bitflip(fi->ldpc, pin->bytes, msgbits, cwbits, bitflips);
|
2021-03-21 21:06:26 -04:00
|
|
|
|
|
|
|
|
|
if (sch->debug2) {
|
2019-03-17 16:31:42 -04:00
|
|
|
|
fprintf(stderr, "LDPCCORR = %d\n", ncorr);
|
2021-03-21 21:06:26 -04:00
|
|
|
|
}
|
2019-03-17 16:31:42 -04:00
|
|
|
|
}
|
2021-03-21 21:06:26 -04:00
|
|
|
|
|
2019-03-17 16:31:42 -04:00
|
|
|
|
uint8_t *hardbytes = softbytes_harden(pin->bytes, fi->kldpc / 8, bch_buf);
|
2021-03-21 21:06:26 -04:00
|
|
|
|
|
2019-03-17 16:31:42 -04:00
|
|
|
|
if (true)
|
|
|
|
|
{
|
|
|
|
|
// BCH decode
|
|
|
|
|
size_t cwbytes = fi->kldpc / 8;
|
|
|
|
|
// Decode with suitable BCH decoder for this MODCOD
|
|
|
|
|
bch_interface *bch = s2bch.bchs[pin->pls.sf][mcinfo->rate];
|
|
|
|
|
int ncorr = bch->decode(hardbytes, cwbytes);
|
2021-03-21 21:06:26 -04:00
|
|
|
|
|
|
|
|
|
if (sch->debug2) {
|
2019-03-17 16:31:42 -04:00
|
|
|
|
fprintf(stderr, "BCHCORR = %d\n", ncorr);
|
2021-03-21 21:06:26 -04:00
|
|
|
|
}
|
|
|
|
|
|
2019-03-17 16:31:42 -04:00
|
|
|
|
corrupted = (ncorr < 0);
|
|
|
|
|
residual_errors = (ncorr != 0);
|
2021-03-21 21:06:26 -04:00
|
|
|
|
|
2019-03-17 16:31:42 -04:00
|
|
|
|
// Report VER
|
|
|
|
|
opt_write(bitcount, fi->Kbch);
|
|
|
|
|
opt_write(errcount, (ncorr >= 0) ? ncorr : fi->Kbch);
|
|
|
|
|
}
|
2021-03-21 21:06:26 -04:00
|
|
|
|
|
2019-03-17 16:31:42 -04:00
|
|
|
|
int bbsize = fi->Kbch / 8;
|
|
|
|
|
// TBD Some decoders want the bad packets.
|
|
|
|
|
#if 0
|
|
|
|
|
if ( corrupted ) {
|
|
|
|
|
fprintf(stderr, "Passing bad frame\n");
|
|
|
|
|
corrupted = false;
|
|
|
|
|
}
|
|
|
|
|
#endif
|
|
|
|
|
if (!corrupted)
|
|
|
|
|
{
|
|
|
|
|
// Descramble and output
|
|
|
|
|
bbframe *pout = out.wr();
|
|
|
|
|
pout->pls = pin->pls;
|
|
|
|
|
bbscrambling.transform(hardbytes, bbsize, pout->bytes);
|
|
|
|
|
out.written(1);
|
|
|
|
|
}
|
2021-03-21 21:06:26 -04:00
|
|
|
|
|
|
|
|
|
if (sch->debug) {
|
2019-03-17 16:31:42 -04:00
|
|
|
|
fprintf(stderr, "%c", corrupted ? ':' : residual_errors ? '.' : '_');
|
2021-03-21 21:06:26 -04:00
|
|
|
|
}
|
|
|
|
|
|
2019-03-17 16:31:42 -04:00
|
|
|
|
in.read(1);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private:
|
|
|
|
|
s2_ldpc_engines<SOFTBIT, SOFTBYTE> s2ldpc;
|
|
|
|
|
uint8_t bch_buf[64800 / 8]; // Temp storage for hardening before BCH
|
|
|
|
|
s2_bch_engines s2bch;
|
|
|
|
|
s2_bbscrambling bbscrambling;
|
|
|
|
|
pipereader<fecframe<SOFTBYTE>> in;
|
|
|
|
|
pipewriter<bbframe> out;
|
|
|
|
|
pipewriter<int> *bitcount, *errcount;
|
|
|
|
|
}; // s2_fecdec
|
|
|
|
|
|
2022-08-04 10:13:25 -04:00
|
|
|
|
#ifdef LINUX
|
|
|
|
|
|
2021-03-02 00:02:38 -05:00
|
|
|
|
// Soft LDPC decoder
|
|
|
|
|
// Internally implemented LDPC tool. Replaces external LDPC decoder
|
|
|
|
|
|
|
|
|
|
template <typename SOFTBIT, typename SOFTBYTE>
|
|
|
|
|
struct s2_fecdec_soft : runnable
|
|
|
|
|
{
|
2021-03-12 18:13:00 -05:00
|
|
|
|
s2_fecdec_soft(
|
|
|
|
|
scheduler *sch,
|
|
|
|
|
pipebuf<fecframe<SOFTBYTE>> &_in,
|
|
|
|
|
pipebuf<bbframe> &_out,
|
|
|
|
|
int _modcod,
|
|
|
|
|
bool _shortframes = true,
|
|
|
|
|
int _max_trials = 25,
|
|
|
|
|
pipebuf<int> *_bitcount = nullptr,
|
|
|
|
|
pipebuf<int> *_errcount = nullptr
|
|
|
|
|
) :
|
|
|
|
|
runnable(sch, "S2 fecdec soft"),
|
|
|
|
|
in(_in),
|
|
|
|
|
out(_out),
|
|
|
|
|
modcod(_modcod < 0 ? 0 : _modcod > 31 ? 31 : _modcod),
|
|
|
|
|
shortframes(_shortframes ? 1 : 0),
|
|
|
|
|
max_trials(_max_trials),
|
|
|
|
|
bitcount(opt_writer(_bitcount, 1)),
|
|
|
|
|
errcount(opt_writer(_errcount, 1))
|
2021-03-02 00:02:38 -05:00
|
|
|
|
{
|
2021-03-06 06:47:42 -05:00
|
|
|
|
const char *tabname = ldpctool::LDPCInterface::mc_tabnames[shortframes][modcod];
|
|
|
|
|
fprintf(stderr, "s2_fecdec_soft::s2_fecdec_soft: tabname: %s\n", tabname);
|
|
|
|
|
|
|
|
|
|
if (tabname)
|
|
|
|
|
{
|
|
|
|
|
ldpc = ldpctool::create_ldpc((char *)"S2", tabname[0], atoi(tabname + 1));
|
|
|
|
|
code = new ldpctool::code_type[ldpc->code_len()];
|
|
|
|
|
aligned_buffer = aligned_alloc(sizeof(ldpctool::simd_type), sizeof(ldpctool::simd_type) * ldpc->code_len());
|
|
|
|
|
simd = reinterpret_cast<ldpctool::simd_type *>(aligned_buffer);
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
ldpc = nullptr;
|
|
|
|
|
aligned_buffer = nullptr;
|
|
|
|
|
code = nullptr;
|
|
|
|
|
}
|
2021-03-02 00:02:38 -05:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
~s2_fecdec_soft()
|
|
|
|
|
{
|
2021-03-21 21:06:26 -04:00
|
|
|
|
if (ldpc) {
|
|
|
|
|
delete ldpc;
|
|
|
|
|
}
|
2021-03-06 06:47:42 -05:00
|
|
|
|
if (aligned_buffer) {
|
|
|
|
|
free(aligned_buffer);
|
|
|
|
|
}
|
|
|
|
|
if (code) {
|
|
|
|
|
delete[] code;
|
|
|
|
|
}
|
2021-03-02 00:02:38 -05:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void run()
|
|
|
|
|
{
|
2021-03-06 06:47:42 -05:00
|
|
|
|
while (in.readable() >= 1 &&
|
|
|
|
|
out.writable() >= 1 &&
|
|
|
|
|
opt_writable(bitcount, 1) &&
|
|
|
|
|
opt_writable(errcount, 1))
|
2021-03-02 00:02:38 -05:00
|
|
|
|
{
|
|
|
|
|
fecframe<SOFTBYTE> *pin = in.rd();
|
2021-03-06 06:47:42 -05:00
|
|
|
|
const modcod_info *mcinfo = check_modcod(pin->pls.modcod);
|
|
|
|
|
const fec_info *fi = &fec_infos[pin->pls.sf][mcinfo->rate];
|
|
|
|
|
bool corrupted = false;
|
|
|
|
|
bool residual_errors;
|
2021-03-02 00:02:38 -05:00
|
|
|
|
|
2021-03-06 06:47:42 -05:00
|
|
|
|
if (ldpc)
|
2021-03-02 00:02:38 -05:00
|
|
|
|
{
|
2021-03-06 06:47:42 -05:00
|
|
|
|
decode.init(ldpc);
|
|
|
|
|
int count = decode(simd, simd + ldpc->data_len(), max_trials, 1);
|
2021-03-02 00:02:38 -05:00
|
|
|
|
|
2021-03-06 06:47:42 -05:00
|
|
|
|
if (count < 0) {
|
|
|
|
|
fprintf(stderr, "s2_fecdec_soft::run: decoder failed at converging to a code word in %d trials\n", max_trials);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
for (int i = 0; i < ldpc->code_len(); ++i) {
|
|
|
|
|
code[i] = reinterpret_cast<ldpctool::code_type *>(simd + i)[0];
|
2021-03-02 00:02:38 -05:00
|
|
|
|
}
|
|
|
|
|
|
2021-03-06 06:47:42 -05:00
|
|
|
|
SOFTBYTE *ldpc_buf = reinterpret_cast<SOFTBYTE*>(code);
|
|
|
|
|
uint8_t *hardbytes = softbytes_harden(ldpc_buf, fi->kldpc / 8, bch_buf);
|
|
|
|
|
|
|
|
|
|
// BCH decode
|
|
|
|
|
size_t cwbytes = fi->kldpc / 8;
|
|
|
|
|
// Decode with suitable BCH decoder for this MODCOD
|
|
|
|
|
bch_interface *bch = s2bch.bchs[pin->pls.sf][mcinfo->rate];
|
|
|
|
|
int ncorr = bch->decode(hardbytes, cwbytes);
|
2021-03-21 21:06:26 -04:00
|
|
|
|
|
|
|
|
|
if (sch->debug2) {
|
2021-03-06 06:47:42 -05:00
|
|
|
|
fprintf(stderr, "BCHCORR = %d\n", ncorr);
|
2021-03-21 21:06:26 -04:00
|
|
|
|
}
|
|
|
|
|
|
2021-03-06 06:47:42 -05:00
|
|
|
|
corrupted = (ncorr < 0);
|
|
|
|
|
residual_errors = (ncorr != 0);
|
|
|
|
|
// Report VER
|
|
|
|
|
opt_write(bitcount, fi->Kbch);
|
|
|
|
|
opt_write(errcount, (ncorr >= 0) ? ncorr : fi->Kbch);
|
|
|
|
|
int bbsize = fi->Kbch / 8;
|
2021-03-02 00:02:38 -05:00
|
|
|
|
|
2021-03-06 06:47:42 -05:00
|
|
|
|
if (!corrupted)
|
2021-03-02 00:02:38 -05:00
|
|
|
|
{
|
2021-03-06 06:47:42 -05:00
|
|
|
|
// Descramble and output
|
|
|
|
|
bbframe *pout = out.wr();
|
|
|
|
|
pout->pls = pin->pls;
|
|
|
|
|
bbscrambling.transform(hardbytes, bbsize, pout->bytes);
|
|
|
|
|
out.written(1);
|
2021-03-02 00:02:38 -05:00
|
|
|
|
}
|
|
|
|
|
|
2021-03-06 06:47:42 -05:00
|
|
|
|
if (sch->debug) {
|
|
|
|
|
fprintf(stderr, "%c", corrupted ? ':' : residual_errors ? '.' : '_');
|
2021-03-02 00:02:38 -05:00
|
|
|
|
}
|
2021-03-06 06:47:42 -05:00
|
|
|
|
} // ldpc engine allocated
|
2021-03-02 00:02:38 -05:00
|
|
|
|
|
|
|
|
|
in.read(1);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private:
|
|
|
|
|
pipereader<fecframe<SOFTBYTE>> in;
|
|
|
|
|
pipewriter<bbframe> out;
|
|
|
|
|
int modcod;
|
|
|
|
|
int shortframes;
|
|
|
|
|
int max_trials;
|
|
|
|
|
pipewriter<int> *bitcount, *errcount;
|
|
|
|
|
|
2021-03-06 06:47:42 -05:00
|
|
|
|
typedef ldpctool::NormalUpdate<ldpctool::simd_type> update_type;
|
|
|
|
|
//typedef SelfCorrectedUpdate<simd_type> update_type;
|
|
|
|
|
|
|
|
|
|
//typedef MinSumAlgorithm<simd_type, update_type> algorithm_type;
|
|
|
|
|
//typedef OffsetMinSumAlgorithm<simd_type, update_type, FACTOR> algorithm_type;
|
|
|
|
|
typedef ldpctool::MinSumCAlgorithm<ldpctool::simd_type, update_type, ldpctool::FACTOR> algorithm_type;
|
|
|
|
|
//typedef LogDomainSPA<simd_type, update_type> algorithm_type;
|
|
|
|
|
//typedef LambdaMinAlgorithm<simd_type, update_type, 3> algorithm_type;
|
|
|
|
|
//typedef SumProductAlgorithm<simd_type, update_type> algorithm_type;
|
|
|
|
|
|
|
|
|
|
ldpctool::LDPCDecoder<ldpctool::simd_type, algorithm_type> decode;
|
|
|
|
|
|
2021-03-02 00:02:38 -05:00
|
|
|
|
ldpctool::LDPCInterface *ldpc;
|
|
|
|
|
ldpctool::code_type *code;
|
|
|
|
|
void *aligned_buffer;
|
|
|
|
|
ldpctool::simd_type *simd;
|
|
|
|
|
uint8_t bch_buf[64800 / 8]; // Temp storage for hardening before BCH
|
|
|
|
|
s2_bch_engines s2bch;
|
|
|
|
|
s2_bbscrambling bbscrambling;
|
2021-03-06 01:37:44 -05:00
|
|
|
|
}; // s2_fecdec_soft
|
2021-03-02 00:02:38 -05:00
|
|
|
|
|
2022-08-04 10:13:25 -04:00
|
|
|
|
#endif
|
|
|
|
|
|
2019-03-17 16:31:42 -04:00
|
|
|
|
// External LDPC decoder
|
|
|
|
|
// Spawns a user-specified command, FEC frames on stdin/stdout.
|
|
|
|
|
template <typename T, int _SIZE>
|
|
|
|
|
struct simplequeue
|
|
|
|
|
{
|
|
|
|
|
static const int SIZE = _SIZE;
|
2021-03-21 21:06:26 -04:00
|
|
|
|
|
|
|
|
|
simplequeue()
|
|
|
|
|
{
|
|
|
|
|
rd = wr = count = 0;
|
|
|
|
|
}
|
|
|
|
|
|
2019-03-17 16:31:42 -04:00
|
|
|
|
bool full() { return count == SIZE; }
|
2021-03-21 21:06:26 -04:00
|
|
|
|
|
2019-03-17 16:31:42 -04:00
|
|
|
|
T *put()
|
|
|
|
|
{
|
|
|
|
|
T *res = &q[wr];
|
|
|
|
|
wr = (wr + 1) % SIZE;
|
|
|
|
|
++count;
|
|
|
|
|
return res;
|
|
|
|
|
}
|
2021-03-21 21:06:26 -04:00
|
|
|
|
|
2019-03-17 16:31:42 -04:00
|
|
|
|
bool empty() { return count == 0; }
|
|
|
|
|
const T *peek() { return &q[rd]; }
|
2021-03-21 21:06:26 -04:00
|
|
|
|
|
2019-03-17 16:31:42 -04:00
|
|
|
|
const T *get()
|
|
|
|
|
{
|
|
|
|
|
const T *res = &q[rd];
|
|
|
|
|
rd = (rd + 1) % SIZE;
|
|
|
|
|
--count;
|
|
|
|
|
return res;
|
|
|
|
|
}
|
2021-03-21 21:06:26 -04:00
|
|
|
|
|
|
|
|
|
private:
|
2019-03-17 16:31:42 -04:00
|
|
|
|
int rd, wr, count;
|
|
|
|
|
T q[SIZE];
|
|
|
|
|
};
|
|
|
|
|
|
2022-07-18 11:40:00 -04:00
|
|
|
|
#if defined(USE_LDPC_TOOL) && !defined(_MSC_VER)
|
|
|
|
|
|
2019-03-17 16:31:42 -04:00
|
|
|
|
template <typename SOFTBIT, typename SOFTBYTE>
|
|
|
|
|
struct s2_fecdec_helper : runnable
|
|
|
|
|
{
|
|
|
|
|
int batch_size;
|
|
|
|
|
int nhelpers;
|
|
|
|
|
bool must_buffer;
|
2021-03-06 18:29:01 -05:00
|
|
|
|
int max_trials;
|
|
|
|
|
|
2021-03-12 18:13:00 -05:00
|
|
|
|
s2_fecdec_helper(
|
|
|
|
|
scheduler *sch,
|
|
|
|
|
pipebuf<fecframe<SOFTBYTE>> &_in,
|
|
|
|
|
pipebuf<bbframe> &_out,
|
|
|
|
|
const char *_command,
|
|
|
|
|
pipebuf<int> *_bitcount = nullptr,
|
|
|
|
|
pipebuf<int> *_errcount = nullptr
|
|
|
|
|
) :
|
|
|
|
|
runnable(sch, "S2 fecdec io"),
|
|
|
|
|
batch_size(16),
|
|
|
|
|
nhelpers(1),
|
|
|
|
|
must_buffer(false),
|
|
|
|
|
max_trials(8),
|
|
|
|
|
in(_in),
|
|
|
|
|
out(_out),
|
|
|
|
|
bitcount(opt_writer(_bitcount, 1)),
|
|
|
|
|
errcount(opt_writer(_errcount, 1))
|
2019-03-17 16:31:42 -04:00
|
|
|
|
{
|
2021-03-06 18:29:01 -05:00
|
|
|
|
command = strdup(_command);
|
2021-03-21 21:06:26 -04:00
|
|
|
|
|
|
|
|
|
for (int mc = 0; mc < 32; ++mc) {
|
|
|
|
|
for (int sf = 0; sf < 2; ++sf) {
|
2021-03-06 01:37:44 -05:00
|
|
|
|
pools[mc][sf].procs = nullptr;
|
2021-03-21 21:06:26 -04:00
|
|
|
|
}
|
|
|
|
|
}
|
2019-03-17 16:31:42 -04:00
|
|
|
|
}
|
2021-03-06 18:29:01 -05:00
|
|
|
|
|
|
|
|
|
~s2_fecdec_helper()
|
|
|
|
|
{
|
|
|
|
|
free(command);
|
2021-03-21 21:06:26 -04:00
|
|
|
|
killall(); // also deletes pools[mc][sf].procs if necessary
|
2021-03-06 18:29:01 -05:00
|
|
|
|
}
|
|
|
|
|
|
2019-03-17 16:31:42 -04:00
|
|
|
|
void run()
|
|
|
|
|
{
|
|
|
|
|
// Send work until all helpers block.
|
|
|
|
|
while (in.readable() >= 1 && !jobs.full())
|
|
|
|
|
{
|
2021-03-06 06:47:42 -05:00
|
|
|
|
if ((bbframe_q.size() != 0) && (out.writable() >= 1))
|
2021-03-06 01:37:44 -05:00
|
|
|
|
{
|
2021-03-06 06:47:42 -05:00
|
|
|
|
bbframe *pout = out.wr();
|
|
|
|
|
pout->pls = bbframe_q.front().pls;
|
|
|
|
|
std::copy(bbframe_q.front().bytes, bbframe_q.front().bytes + (58192 / 8), pout->bytes);
|
|
|
|
|
bbframe_q.pop_front();
|
|
|
|
|
out.written(1);
|
2021-03-06 01:37:44 -05:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if ((bitcount_q.size() != 0) && opt_writable(bitcount, 1))
|
|
|
|
|
{
|
|
|
|
|
opt_write(bitcount, bitcount_q.front());
|
|
|
|
|
bitcount_q.pop_front();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if ((errcount_q.size() != 0) && opt_writable(errcount, 1))
|
|
|
|
|
{
|
|
|
|
|
opt_write(errcount, errcount_q.front());
|
|
|
|
|
errcount_q.pop_front();
|
|
|
|
|
}
|
|
|
|
|
|
2021-03-06 06:47:42 -05:00
|
|
|
|
if (!jobs.empty() && jobs.peek()->h->b_out) {
|
|
|
|
|
receive_frame(jobs.get());
|
|
|
|
|
}
|
|
|
|
|
|
2021-03-08 02:34:53 -05:00
|
|
|
|
send_frame(in.rd());
|
2019-03-17 16:31:42 -04:00
|
|
|
|
in.read(1);
|
2021-03-05 02:25:26 -05:00
|
|
|
|
}
|
2019-03-17 16:31:42 -04:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private:
|
|
|
|
|
struct helper_instance
|
|
|
|
|
{
|
|
|
|
|
int fd_tx; // To helper
|
|
|
|
|
int fd_rx; // From helper
|
|
|
|
|
int batch_size; // Latency
|
|
|
|
|
int b_in; // Jobs in input queue
|
|
|
|
|
int b_out; // Jobs in output queue
|
2021-03-10 01:46:25 -05:00
|
|
|
|
int pid; // PID of the child
|
2019-03-17 16:31:42 -04:00
|
|
|
|
};
|
|
|
|
|
struct pool
|
|
|
|
|
{
|
2021-03-06 01:37:44 -05:00
|
|
|
|
helper_instance *procs; // nullptr or [nprocs]
|
2019-03-17 16:31:42 -04:00
|
|
|
|
int nprocs;
|
2021-03-13 12:14:41 -05:00
|
|
|
|
int shift;
|
2019-03-17 16:31:42 -04:00
|
|
|
|
} pools[32][2]; // [modcod][sf]
|
|
|
|
|
struct helper_job
|
|
|
|
|
{
|
|
|
|
|
s2_pls pls;
|
|
|
|
|
helper_instance *h;
|
|
|
|
|
};
|
2021-03-04 15:19:11 -05:00
|
|
|
|
|
2019-03-17 16:31:42 -04:00
|
|
|
|
simplequeue<helper_job, 1024> jobs;
|
2021-03-04 15:19:11 -05:00
|
|
|
|
|
2019-03-17 16:31:42 -04:00
|
|
|
|
// Try to send a frame. Return false if helper was busy.
|
|
|
|
|
bool send_frame(fecframe<SOFTBYTE> *pin)
|
|
|
|
|
{
|
|
|
|
|
pool *p = get_pool(&pin->pls);
|
2021-03-04 15:19:11 -05:00
|
|
|
|
|
2021-03-13 12:14:41 -05:00
|
|
|
|
for (int j = 0; j < p->nprocs; ++j)
|
2019-03-17 16:31:42 -04:00
|
|
|
|
{
|
2021-03-13 12:14:41 -05:00
|
|
|
|
int i = (p->shift + j) % p->nprocs;
|
2019-03-17 16:31:42 -04:00
|
|
|
|
helper_instance *h = &p->procs[i];
|
2021-03-04 02:43:04 -05:00
|
|
|
|
int iosize = (pin->pls.framebits() / 8) * sizeof(SOFTBYTE);
|
2019-03-17 16:31:42 -04:00
|
|
|
|
// fprintf(stderr, "Writing %lu to fd %d\n", iosize, h->fd_tx);
|
|
|
|
|
int nw = write(h->fd_tx, pin->bytes, iosize);
|
2021-03-04 15:19:11 -05:00
|
|
|
|
|
2019-03-17 16:31:42 -04:00
|
|
|
|
if (nw < 0 && errno == EWOULDBLOCK)
|
2021-03-10 01:46:25 -05:00
|
|
|
|
{
|
2021-03-13 12:14:41 -05:00
|
|
|
|
//lseek(h->fd_tx, 0, SEEK_SET); // allow new writes on this worker
|
|
|
|
|
//fprintf(stderr, "s2_fecdec_helper::send_frame: %d worker is busy\n", h->pid);
|
2021-03-04 15:19:11 -05:00
|
|
|
|
continue; // next worker
|
2021-03-10 01:46:25 -05:00
|
|
|
|
}
|
2021-03-12 18:13:00 -05:00
|
|
|
|
|
2021-03-21 21:06:26 -04:00
|
|
|
|
if (nw < 0) {
|
2019-03-17 16:31:42 -04:00
|
|
|
|
fatal("write(LDPC helper");
|
2021-03-21 21:06:26 -04:00
|
|
|
|
} else if (nw != iosize) {
|
2019-03-17 16:31:42 -04:00
|
|
|
|
fatal("partial write(LDPC helper)");
|
2021-03-21 21:06:26 -04:00
|
|
|
|
}
|
2021-03-04 15:19:11 -05:00
|
|
|
|
|
2021-03-13 12:14:41 -05:00
|
|
|
|
p->shift = i;
|
2019-03-17 16:31:42 -04:00
|
|
|
|
helper_job *job = jobs.put();
|
|
|
|
|
job->pls = pin->pls;
|
|
|
|
|
job->h = h;
|
|
|
|
|
++h->b_in;
|
2021-03-04 15:19:11 -05:00
|
|
|
|
|
2019-03-17 16:31:42 -04:00
|
|
|
|
if (h->b_in >= h->batch_size)
|
|
|
|
|
{
|
|
|
|
|
h->b_in -= h->batch_size;
|
|
|
|
|
h->b_out += h->batch_size;
|
|
|
|
|
}
|
2021-03-04 15:19:11 -05:00
|
|
|
|
|
|
|
|
|
return true; // done sent to worker
|
2019-03-17 16:31:42 -04:00
|
|
|
|
}
|
2021-03-04 15:19:11 -05:00
|
|
|
|
|
2021-03-10 01:46:25 -05:00
|
|
|
|
fprintf(stderr, "s2_fecdec_helper::send_frame: WARNING: all %d workers were busy: modcod=%d sf=%d)\n",
|
2021-03-08 02:34:53 -05:00
|
|
|
|
p->nprocs, pin->pls.modcod, pin->pls.sf);
|
2021-03-10 01:46:25 -05:00
|
|
|
|
return false; // all workers were busy
|
2019-03-17 16:31:42 -04:00
|
|
|
|
}
|
2021-03-04 15:19:11 -05:00
|
|
|
|
|
2019-03-17 16:31:42 -04:00
|
|
|
|
// Return a pool of running helpers for a given modcod.
|
|
|
|
|
pool *get_pool(const s2_pls *pls)
|
|
|
|
|
{
|
|
|
|
|
pool *p = &pools[pls->modcod][pls->sf];
|
2021-03-08 02:34:53 -05:00
|
|
|
|
|
2019-03-17 16:31:42 -04:00
|
|
|
|
if (!p->procs)
|
|
|
|
|
{
|
2021-03-08 02:34:53 -05:00
|
|
|
|
fprintf(stderr, "s2_fecdec_helper::get_pool: allocate %d workers: modcod=%d sf=%d\n",
|
|
|
|
|
nhelpers, pls->modcod, pls->sf);
|
2019-03-17 16:31:42 -04:00
|
|
|
|
p->procs = new helper_instance[nhelpers];
|
2021-03-21 21:06:26 -04:00
|
|
|
|
|
|
|
|
|
for (int i = 0; i < nhelpers; ++i) {
|
2023-03-30 05:22:19 -04:00
|
|
|
|
spawn_helper(&p->procs[i], pls, i);
|
2021-03-21 21:06:26 -04:00
|
|
|
|
}
|
|
|
|
|
|
2019-03-17 16:31:42 -04:00
|
|
|
|
p->nprocs = nhelpers;
|
2021-03-13 12:14:41 -05:00
|
|
|
|
p->shift = 0;
|
2019-03-17 16:31:42 -04:00
|
|
|
|
}
|
2021-03-08 02:34:53 -05:00
|
|
|
|
|
2019-03-17 16:31:42 -04:00
|
|
|
|
return p;
|
|
|
|
|
}
|
2021-03-04 15:19:11 -05:00
|
|
|
|
|
2021-03-10 01:46:25 -05:00
|
|
|
|
void killall()
|
|
|
|
|
{
|
|
|
|
|
fprintf(stderr, "s2_fecdec_helper::killall\n");
|
|
|
|
|
|
|
|
|
|
for (int i = 0; i < 32; i++) // all MODCODs
|
|
|
|
|
{
|
|
|
|
|
for (int j = 0; j < 2; j++) // long and short frames
|
|
|
|
|
{
|
|
|
|
|
pool *p = &pools[i][j];
|
|
|
|
|
|
|
|
|
|
if (p->procs)
|
|
|
|
|
{
|
|
|
|
|
for (int i = 0; i < p->nprocs; ++i)
|
|
|
|
|
{
|
|
|
|
|
helper_instance *h = &p->procs[i];
|
|
|
|
|
fprintf(stderr, "s2_fecdec_helper::killall: killing %d\n", h->pid);
|
|
|
|
|
int rc = kill(h->pid, SIGKILL);
|
2021-03-21 21:06:26 -04:00
|
|
|
|
|
|
|
|
|
if (rc < 0)
|
|
|
|
|
{
|
2021-03-10 01:46:25 -05:00
|
|
|
|
fatal("s2_fecdec_helper::killall");
|
2021-03-21 21:06:26 -04:00
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
2021-03-10 01:46:25 -05:00
|
|
|
|
int cs;
|
|
|
|
|
waitpid(h->pid, &cs, 0);
|
|
|
|
|
}
|
2021-03-21 21:06:26 -04:00
|
|
|
|
|
2021-03-12 18:13:00 -05:00
|
|
|
|
// close pipes
|
|
|
|
|
close(h->fd_tx);
|
|
|
|
|
close(h->fd_rx);
|
2021-03-10 01:46:25 -05:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
delete p->procs;
|
|
|
|
|
p->procs = nullptr;
|
|
|
|
|
p->nprocs = 0;
|
|
|
|
|
}
|
|
|
|
|
} // long and short frames
|
|
|
|
|
} // all MODCODs
|
|
|
|
|
}
|
|
|
|
|
|
2019-03-17 16:31:42 -04:00
|
|
|
|
// Spawn a helper process.
|
2023-03-30 05:22:19 -04:00
|
|
|
|
void spawn_helper(helper_instance *h, const s2_pls *pls, imt)
|
2019-03-17 16:31:42 -04:00
|
|
|
|
{
|
2021-03-21 21:06:26 -04:00
|
|
|
|
if (sch->debug) {
|
2021-03-08 02:34:53 -05:00
|
|
|
|
fprintf(stderr, "Spawning LDPC helper: modcod=%d sf=%d\n", pls->modcod, pls->sf);
|
2021-03-21 21:06:26 -04:00
|
|
|
|
}
|
|
|
|
|
|
2019-03-17 16:31:42 -04:00
|
|
|
|
int tx[2], rx[2];
|
2021-03-21 21:06:26 -04:00
|
|
|
|
|
|
|
|
|
if (pipe(tx) || pipe(rx)) {
|
2019-03-17 16:31:42 -04:00
|
|
|
|
fatal("pipe");
|
2021-03-21 21:06:26 -04:00
|
|
|
|
}
|
|
|
|
|
|
2019-03-17 16:31:42 -04:00
|
|
|
|
// Size the pipes so that the helper never runs out of work to do.
|
|
|
|
|
int pipesize = 64800 * batch_size;
|
2019-11-01 05:55:26 -04:00
|
|
|
|
// macOS does not have F_SETPIPE_SZ and there
|
|
|
|
|
// is no way to change the buffer size
|
|
|
|
|
#ifndef __APPLE__
|
2021-03-10 01:46:25 -05:00
|
|
|
|
long min_pipe_size = (long) fcntl(tx[0], F_GETPIPE_SZ);
|
|
|
|
|
long pipe_size = (long) fcntl(rx[0], F_GETPIPE_SZ);
|
|
|
|
|
min_pipe_size = std::min(min_pipe_size, pipe_size);
|
|
|
|
|
pipe_size = (long) fcntl(tx[1], F_GETPIPE_SZ);
|
|
|
|
|
min_pipe_size = std::min(min_pipe_size, pipe_size);
|
|
|
|
|
pipe_size = (long) fcntl(rx[1], F_GETPIPE_SZ);
|
|
|
|
|
min_pipe_size = std::min(min_pipe_size, pipe_size);
|
|
|
|
|
|
|
|
|
|
if (min_pipe_size < pipesize)
|
|
|
|
|
{
|
|
|
|
|
if (fcntl(tx[0], F_SETPIPE_SZ, pipesize) < 0 ||
|
|
|
|
|
fcntl(rx[0], F_SETPIPE_SZ, pipesize) < 0 ||
|
|
|
|
|
fcntl(tx[1], F_SETPIPE_SZ, pipesize) < 0 ||
|
|
|
|
|
fcntl(rx[1], F_SETPIPE_SZ, pipesize) < 0)
|
|
|
|
|
{
|
|
|
|
|
fprintf(stderr,
|
|
|
|
|
"*** Failed to increase pipe size from %ld.\n"
|
|
|
|
|
"*** Try echo %d > /proc/sys/fs/pipe-max-size\n",
|
|
|
|
|
min_pipe_size,
|
|
|
|
|
pipesize);
|
2021-03-21 21:06:26 -04:00
|
|
|
|
|
|
|
|
|
if (must_buffer) {
|
2021-03-10 01:46:25 -05:00
|
|
|
|
fatal("F_SETPIPE_SZ");
|
2021-03-21 21:06:26 -04:00
|
|
|
|
} else {
|
2021-03-10 01:46:25 -05:00
|
|
|
|
fprintf(stderr, "*** Throughput will be suboptimal.\n");
|
2021-03-21 21:06:26 -04:00
|
|
|
|
}
|
2021-03-10 01:46:25 -05:00
|
|
|
|
}
|
2019-03-17 16:31:42 -04:00
|
|
|
|
}
|
2019-11-01 05:55:26 -04:00
|
|
|
|
#endif
|
2021-03-06 01:37:44 -05:00
|
|
|
|
// vfork() differs from fork(2) in that the calling thread is
|
|
|
|
|
// suspended until the child terminates
|
|
|
|
|
int child = fork();
|
|
|
|
|
|
2019-03-17 16:31:42 -04:00
|
|
|
|
if (!child)
|
|
|
|
|
{
|
|
|
|
|
// Child process
|
|
|
|
|
close(tx[1]);
|
|
|
|
|
dup2(tx[0], 0);
|
|
|
|
|
close(rx[0]);
|
|
|
|
|
dup2(rx[1], 1);
|
2021-03-06 18:29:01 -05:00
|
|
|
|
char max_trials_arg[16];
|
|
|
|
|
sprintf(max_trials_arg, "%d", max_trials);
|
2021-03-06 01:37:44 -05:00
|
|
|
|
char batch_size_arg[16];
|
|
|
|
|
sprintf(batch_size_arg, "%d", batch_size);
|
2021-03-06 18:29:01 -05:00
|
|
|
|
char mc_arg[16];
|
|
|
|
|
sprintf(mc_arg, "%d", pls->modcod);
|
2021-03-06 01:37:44 -05:00
|
|
|
|
const char *sf_arg = pls->sf ? "--shortframes" : nullptr;
|
2021-03-06 18:29:01 -05:00
|
|
|
|
const char *argv[] = {
|
|
|
|
|
command,
|
|
|
|
|
"--trials", max_trials_arg,
|
|
|
|
|
"--batch-size", batch_size_arg,
|
|
|
|
|
"--modcod", mc_arg,
|
|
|
|
|
sf_arg,
|
|
|
|
|
nullptr
|
|
|
|
|
};
|
2021-03-06 01:37:44 -05:00
|
|
|
|
execve(command, (char *const *)argv, nullptr);
|
2019-03-17 16:31:42 -04:00
|
|
|
|
fatal(command);
|
|
|
|
|
}
|
2021-03-06 01:37:44 -05:00
|
|
|
|
|
2021-03-10 01:46:25 -05:00
|
|
|
|
h->pid = child;
|
2019-03-17 16:31:42 -04:00
|
|
|
|
h->fd_tx = tx[1];
|
|
|
|
|
close(tx[0]);
|
|
|
|
|
h->fd_rx = rx[0];
|
|
|
|
|
close(rx[1]);
|
2021-03-06 01:37:44 -05:00
|
|
|
|
h->batch_size = batch_size; // TBD
|
2019-03-17 16:31:42 -04:00
|
|
|
|
h->b_in = h->b_out = 0;
|
2021-03-10 01:46:25 -05:00
|
|
|
|
int flags_tx = fcntl(h->fd_tx, F_GETFL);
|
2021-03-21 21:06:26 -04:00
|
|
|
|
|
|
|
|
|
if (fcntl(h->fd_tx, F_SETFL, flags_tx | O_NONBLOCK)) {
|
2021-03-10 01:46:25 -05:00
|
|
|
|
fatal("fcntl_tx(helper)");
|
2021-03-21 21:06:26 -04:00
|
|
|
|
}
|
|
|
|
|
|
2021-03-10 01:46:25 -05:00
|
|
|
|
int flags_rx = fcntl(h->fd_rx, F_GETFL);
|
2021-03-21 21:06:26 -04:00
|
|
|
|
|
|
|
|
|
if (fcntl(h->fd_rx, F_SETFL, flags_rx | O_NONBLOCK)) {
|
2021-03-10 01:46:25 -05:00
|
|
|
|
fatal("fcntl_rx(helper)");
|
2021-03-21 21:06:26 -04:00
|
|
|
|
}
|
2019-03-17 16:31:42 -04:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Receive a finished job.
|
|
|
|
|
void receive_frame(const helper_job *job)
|
|
|
|
|
{
|
|
|
|
|
// Read corrected frame from helper
|
|
|
|
|
const s2_pls *pls = &job->pls;
|
2021-03-04 02:43:04 -05:00
|
|
|
|
int iosize = (pls->framebits() / 8) * sizeof(ldpc_buf[0]);
|
2019-03-17 16:31:42 -04:00
|
|
|
|
int nr = read(job->h->fd_rx, ldpc_buf, iosize);
|
2021-03-10 01:46:25 -05:00
|
|
|
|
|
2019-03-17 16:31:42 -04:00
|
|
|
|
if (nr < 0)
|
2021-03-10 01:46:25 -05:00
|
|
|
|
{
|
|
|
|
|
if (errno != EAGAIN) { // if no data then try again next time
|
|
|
|
|
fatal("s2_fecdec_helper::receive_frame read error");
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
else if (nr != iosize)
|
|
|
|
|
{
|
2021-03-12 18:13:00 -05:00
|
|
|
|
fprintf(stderr, "s2_fecdec_helper::receive_frame: %d bytes read vs %d\n", nr, iosize);
|
2021-03-10 01:46:25 -05:00
|
|
|
|
}
|
|
|
|
|
|
2019-03-17 16:31:42 -04:00
|
|
|
|
--job->h->b_out;
|
|
|
|
|
// Decode BCH.
|
|
|
|
|
const modcod_info *mcinfo = check_modcod(job->pls.modcod);
|
|
|
|
|
const fec_info *fi = &fec_infos[job->pls.sf][mcinfo->rate];
|
|
|
|
|
uint8_t *hardbytes = softbytes_harden(ldpc_buf, fi->kldpc / 8, bch_buf);
|
|
|
|
|
size_t cwbytes = fi->kldpc / 8;
|
2021-03-04 02:43:04 -05:00
|
|
|
|
//size_t msgbytes = fi->Kbch / 8;
|
|
|
|
|
//size_t chkbytes = cwbytes - msgbytes;
|
2019-03-17 16:31:42 -04:00
|
|
|
|
bch_interface *bch = s2bch.bchs[job->pls.sf][mcinfo->rate];
|
|
|
|
|
int ncorr = bch->decode(hardbytes, cwbytes);
|
2021-03-21 21:06:26 -04:00
|
|
|
|
|
|
|
|
|
if (sch->debug2) {
|
2019-03-17 16:31:42 -04:00
|
|
|
|
fprintf(stderr, "BCHCORR = %d\n", ncorr);
|
2021-03-21 21:06:26 -04:00
|
|
|
|
}
|
|
|
|
|
|
2019-03-17 16:31:42 -04:00
|
|
|
|
bool corrupted = (ncorr < 0);
|
|
|
|
|
// Report VBER
|
2021-03-05 22:46:26 -05:00
|
|
|
|
bitcount_q.push_back(fi->Kbch);
|
|
|
|
|
//opt_write(bitcount, fi->Kbch);
|
|
|
|
|
errcount_q.push_front((ncorr >= 0) ? ncorr : fi->Kbch);
|
|
|
|
|
//opt_write(errcount, (ncorr >= 0) ? ncorr : fi->Kbch);
|
2019-03-17 16:31:42 -04:00
|
|
|
|
#if 0
|
|
|
|
|
// TBD Some decoders want the bad packets.
|
|
|
|
|
if ( corrupted ) {
|
|
|
|
|
fprintf(stderr, "Passing bad frame\n");
|
|
|
|
|
corrupted = false;
|
|
|
|
|
}
|
|
|
|
|
#endif
|
|
|
|
|
if (!corrupted)
|
|
|
|
|
{
|
|
|
|
|
// Descramble and output
|
2021-03-05 22:46:26 -05:00
|
|
|
|
bbframe_q.emplace_back();
|
|
|
|
|
//bbframe *pout = out.wr();
|
|
|
|
|
bbframe_q.back().pls = job->pls;
|
|
|
|
|
bbscrambling.transform(hardbytes, fi->Kbch / 8, bbframe_q.back().bytes);
|
|
|
|
|
//out.written(1);
|
2019-03-17 16:31:42 -04:00
|
|
|
|
}
|
2021-03-21 21:06:26 -04:00
|
|
|
|
|
|
|
|
|
if (sch->debug) {
|
2019-03-17 16:31:42 -04:00
|
|
|
|
fprintf(stderr, "%c", corrupted ? '!' : ncorr ? '.' : '_');
|
2021-03-21 21:06:26 -04:00
|
|
|
|
}
|
2019-03-17 16:31:42 -04:00
|
|
|
|
}
|
2021-03-04 15:19:11 -05:00
|
|
|
|
|
2019-03-17 16:31:42 -04:00
|
|
|
|
pipereader<fecframe<SOFTBYTE>> in;
|
|
|
|
|
pipewriter<bbframe> out;
|
2021-03-06 18:29:01 -05:00
|
|
|
|
char *command;
|
2019-03-17 16:31:42 -04:00
|
|
|
|
SOFTBYTE ldpc_buf[64800 / 8];
|
|
|
|
|
uint8_t bch_buf[64800 / 8]; // Temp storage for hardening before BCH
|
|
|
|
|
s2_bch_engines s2bch;
|
|
|
|
|
s2_bbscrambling bbscrambling;
|
2021-03-05 22:46:26 -05:00
|
|
|
|
std::deque<bbframe> bbframe_q;
|
|
|
|
|
std::deque<int> bitcount_q;
|
|
|
|
|
std::deque<int> errcount_q;
|
2019-03-17 16:31:42 -04:00
|
|
|
|
pipewriter<int> *bitcount, *errcount;
|
|
|
|
|
}; // s2_fecdec_helper
|
2022-07-18 11:40:00 -04:00
|
|
|
|
|
|
|
|
|
#else // USE_LDPC_TOOL
|
|
|
|
|
|
|
|
|
|
template <typename SOFTBIT, typename SOFTBYTE>
|
|
|
|
|
struct s2_fecdec_helper : runnable
|
|
|
|
|
{
|
|
|
|
|
int batch_size;
|
|
|
|
|
int nhelpers;
|
|
|
|
|
bool must_buffer;
|
|
|
|
|
int max_trials;
|
|
|
|
|
|
|
|
|
|
s2_fecdec_helper(
|
|
|
|
|
scheduler *sch,
|
|
|
|
|
pipebuf<fecframe<SOFTBYTE>> &_in,
|
|
|
|
|
pipebuf<bbframe> &_out,
|
|
|
|
|
const char *_command,
|
|
|
|
|
pipebuf<int> *_bitcount = nullptr,
|
|
|
|
|
pipebuf<int> *_errcount = nullptr
|
|
|
|
|
) :
|
|
|
|
|
runnable(sch, "S2 fecdec io"),
|
2023-03-30 05:22:19 -04:00
|
|
|
|
batch_size(32),
|
2022-07-18 11:40:00 -04:00
|
|
|
|
nhelpers(1),
|
|
|
|
|
must_buffer(false),
|
|
|
|
|
max_trials(8),
|
|
|
|
|
in(_in),
|
|
|
|
|
out(_out),
|
|
|
|
|
bitcount(opt_writer(_bitcount, 1)),
|
|
|
|
|
errcount(opt_writer(_errcount, 1))
|
|
|
|
|
{
|
|
|
|
|
command = strdup(_command);
|
|
|
|
|
|
|
|
|
|
for (int mc = 0; mc < 32; ++mc) {
|
|
|
|
|
for (int sf = 0; sf < 2; ++sf) {
|
|
|
|
|
pools[mc][sf].procs = nullptr;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
~s2_fecdec_helper()
|
|
|
|
|
{
|
|
|
|
|
free(command);
|
|
|
|
|
killall(); // also deletes pools[mc][sf].procs if necessary
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void run()
|
|
|
|
|
{
|
|
|
|
|
// Send work until all helpers block.
|
|
|
|
|
while (in.readable() >= 1 && !jobs.full())
|
|
|
|
|
{
|
|
|
|
|
if ((bbframe_q.size() != 0) && (out.writable() >= 1))
|
|
|
|
|
{
|
|
|
|
|
bbframe *pout = out.wr();
|
|
|
|
|
pout->pls = bbframe_q.front().pls;
|
|
|
|
|
std::copy(bbframe_q.front().bytes, bbframe_q.front().bytes + (58192 / 8), pout->bytes);
|
|
|
|
|
bbframe_q.pop_front();
|
|
|
|
|
out.written(1);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if ((bitcount_q.size() != 0) && opt_writable(bitcount, 1))
|
|
|
|
|
{
|
|
|
|
|
opt_write(bitcount, bitcount_q.front());
|
|
|
|
|
bitcount_q.pop_front();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if ((errcount_q.size() != 0) && opt_writable(errcount, 1))
|
|
|
|
|
{
|
|
|
|
|
opt_write(errcount, errcount_q.front());
|
|
|
|
|
errcount_q.pop_front();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (!jobs.empty() && jobs.peek()->h->b_out) {
|
|
|
|
|
receive_frame(jobs.get());
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
send_frame(in.rd());
|
|
|
|
|
in.read(1);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private:
|
|
|
|
|
struct helper_instance
|
|
|
|
|
{
|
|
|
|
|
QThread *m_thread;
|
|
|
|
|
LDPCWorker *m_worker;
|
|
|
|
|
int batch_size;
|
|
|
|
|
int b_in; // Jobs in input queue
|
|
|
|
|
int b_out; // Jobs in output queue
|
|
|
|
|
};
|
|
|
|
|
struct pool
|
|
|
|
|
{
|
|
|
|
|
helper_instance *procs; // nullptr or [nprocs]
|
|
|
|
|
int nprocs;
|
|
|
|
|
int shift;
|
|
|
|
|
} pools[32][2]; // [modcod][sf]
|
|
|
|
|
struct helper_job
|
|
|
|
|
{
|
|
|
|
|
s2_pls pls;
|
|
|
|
|
helper_instance *h;
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
simplequeue<helper_job, 1024> jobs;
|
|
|
|
|
|
|
|
|
|
// Try to send a frame. Return false if helper was busy.
|
|
|
|
|
bool send_frame(fecframe<SOFTBYTE> *pin)
|
|
|
|
|
{
|
|
|
|
|
pool *p = get_pool(&pin->pls);
|
|
|
|
|
|
|
|
|
|
for (int j = 0; j < p->nprocs; ++j)
|
|
|
|
|
{
|
|
|
|
|
int i = (p->shift + j) % p->nprocs;
|
|
|
|
|
helper_instance *h = &p->procs[i];
|
|
|
|
|
int iosize = (pin->pls.framebits() / 8) * sizeof(SOFTBYTE);
|
|
|
|
|
|
|
|
|
|
if (h->m_worker->busy()) {
|
|
|
|
|
continue;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
QByteArray data((char *)pin->bytes, iosize);
|
|
|
|
|
QMetaObject::invokeMethod(h->m_worker, "process", Qt::QueuedConnection, Q_ARG(QByteArray, data));
|
|
|
|
|
|
|
|
|
|
p->shift = i;
|
|
|
|
|
helper_job *job = jobs.put();
|
|
|
|
|
job->pls = pin->pls;
|
|
|
|
|
job->h = h;
|
|
|
|
|
++h->b_in;
|
|
|
|
|
|
|
|
|
|
if (h->b_in >= h->batch_size)
|
|
|
|
|
{
|
|
|
|
|
h->b_in -= h->batch_size;
|
|
|
|
|
h->b_out += h->batch_size;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return true; // done sent to worker
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
fprintf(stderr, "s2_fecdec_helper::send_frame: WARNING: all %d workers were busy: modcod=%d sf=%d)\n",
|
|
|
|
|
p->nprocs, pin->pls.modcod, pin->pls.sf);
|
|
|
|
|
return false; // all workers were busy
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Return a pool of running helpers for a given modcod.
|
|
|
|
|
pool *get_pool(const s2_pls *pls)
|
|
|
|
|
{
|
|
|
|
|
pool *p = &pools[pls->modcod][pls->sf];
|
|
|
|
|
|
|
|
|
|
if (!p->procs)
|
|
|
|
|
{
|
|
|
|
|
fprintf(stderr, "s2_fecdec_helper::get_pool: allocate %d workers: modcod=%d sf=%d\n",
|
|
|
|
|
nhelpers, pls->modcod, pls->sf);
|
|
|
|
|
p->procs = new helper_instance[nhelpers];
|
|
|
|
|
|
|
|
|
|
for (int i = 0; i < nhelpers; ++i) {
|
2023-03-30 05:22:19 -04:00
|
|
|
|
spawn_helper(&p->procs[i], pls, i);
|
2022-07-18 11:40:00 -04:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
p->nprocs = nhelpers;
|
|
|
|
|
p->shift = 0;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return p;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void killall()
|
|
|
|
|
{
|
|
|
|
|
qDebug() << "s2_fecdec_helper::killall";
|
|
|
|
|
|
|
|
|
|
for (int i = 0; i < 32; i++) // all MODCODs
|
|
|
|
|
{
|
|
|
|
|
for (int j = 0; j < 2; j++) // long and short frames
|
|
|
|
|
{
|
|
|
|
|
pool *p = &pools[i][j];
|
|
|
|
|
|
|
|
|
|
if (p->procs)
|
|
|
|
|
{
|
|
|
|
|
for (int i = 0; i < p->nprocs; ++i)
|
|
|
|
|
{
|
|
|
|
|
helper_instance *h = &p->procs[i];
|
|
|
|
|
h->m_thread->quit();
|
|
|
|
|
h->m_thread->wait();
|
|
|
|
|
delete h->m_thread;
|
|
|
|
|
h->m_thread = nullptr;
|
|
|
|
|
delete h->m_worker;
|
|
|
|
|
h->m_worker = nullptr;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
delete p->procs;
|
|
|
|
|
p->procs = nullptr;
|
|
|
|
|
p->nprocs = 0;
|
|
|
|
|
}
|
|
|
|
|
} // long and short frames
|
|
|
|
|
} // all MODCODs
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Spawn a helper thread.
|
2023-03-30 05:22:19 -04:00
|
|
|
|
void spawn_helper(helper_instance *h, const s2_pls *pls, int helper_index)
|
2022-07-18 11:40:00 -04:00
|
|
|
|
{
|
|
|
|
|
qDebug() << "s2_fecdec_helper: Spawning LDPC thread: modcod=" << pls->modcod << " sf=" << pls->sf;
|
|
|
|
|
h->m_thread = new QThread();
|
2023-03-30 05:22:19 -04:00
|
|
|
|
h->m_thread->setObjectName(QString("ldpcDVBS2_%1").arg(helper_index));
|
2022-07-18 11:40:00 -04:00
|
|
|
|
h->m_worker = new LDPCWorker(pls->modcod, max_trials, batch_size, pls->sf);
|
|
|
|
|
h->m_worker->moveToThread(h->m_thread);
|
|
|
|
|
h->batch_size = batch_size;
|
|
|
|
|
h->b_in = h->b_out = 0;
|
|
|
|
|
h->m_thread->start();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Receive a finished job.
|
|
|
|
|
void receive_frame(const helper_job *job)
|
|
|
|
|
{
|
|
|
|
|
// Read corrected frame from helper
|
|
|
|
|
const s2_pls *pls = &job->pls;
|
|
|
|
|
int iosize = (pls->framebits() / 8) * sizeof(ldpc_buf[0]);
|
|
|
|
|
|
2022-07-19 11:42:24 -04:00
|
|
|
|
// Non blocking read - will do the next time if no adata is available
|
|
|
|
|
if (job->h->m_worker->dataAvailable())
|
|
|
|
|
{
|
|
|
|
|
QByteArray data = job->h->m_worker->data();
|
|
|
|
|
memcpy(ldpc_buf, data.data(), data.size());
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
return;
|
|
|
|
|
}
|
2022-07-18 11:40:00 -04:00
|
|
|
|
|
|
|
|
|
--job->h->b_out;
|
|
|
|
|
// Decode BCH.
|
|
|
|
|
const modcod_info *mcinfo = check_modcod(job->pls.modcod);
|
|
|
|
|
const fec_info *fi = &fec_infos[job->pls.sf][mcinfo->rate];
|
|
|
|
|
uint8_t *hardbytes = softbytes_harden(ldpc_buf, fi->kldpc / 8, bch_buf);
|
|
|
|
|
size_t cwbytes = fi->kldpc / 8;
|
|
|
|
|
//size_t msgbytes = fi->Kbch / 8;
|
|
|
|
|
//size_t chkbytes = cwbytes - msgbytes;
|
|
|
|
|
bch_interface *bch = s2bch.bchs[job->pls.sf][mcinfo->rate];
|
|
|
|
|
int ncorr = bch->decode(hardbytes, cwbytes);
|
|
|
|
|
|
|
|
|
|
if (sch->debug2) {
|
|
|
|
|
fprintf(stderr, "BCHCORR = %d\n", ncorr);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
bool corrupted = (ncorr < 0);
|
|
|
|
|
// Report VBER
|
|
|
|
|
bitcount_q.push_back(fi->Kbch);
|
|
|
|
|
//opt_write(bitcount, fi->Kbch);
|
|
|
|
|
errcount_q.push_front((ncorr >= 0) ? ncorr : fi->Kbch);
|
|
|
|
|
//opt_write(errcount, (ncorr >= 0) ? ncorr : fi->Kbch);
|
|
|
|
|
#if 0
|
|
|
|
|
// TBD Some decoders want the bad packets.
|
|
|
|
|
if ( corrupted ) {
|
|
|
|
|
fprintf(stderr, "Passing bad frame\n");
|
|
|
|
|
corrupted = false;
|
|
|
|
|
}
|
2021-03-07 01:41:19 -05:00
|
|
|
|
#endif
|
2022-07-18 11:40:00 -04:00
|
|
|
|
if (!corrupted)
|
|
|
|
|
{
|
|
|
|
|
// Descramble and output
|
|
|
|
|
bbframe_q.emplace_back();
|
|
|
|
|
//bbframe *pout = out.wr();
|
|
|
|
|
bbframe_q.back().pls = job->pls;
|
|
|
|
|
bbscrambling.transform(hardbytes, fi->Kbch / 8, bbframe_q.back().bytes);
|
|
|
|
|
//out.written(1);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (sch->debug) {
|
|
|
|
|
fprintf(stderr, "%c", corrupted ? '!' : ncorr ? '.' : '_');
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
pipereader<fecframe<SOFTBYTE>> in;
|
|
|
|
|
pipewriter<bbframe> out;
|
|
|
|
|
char *command;
|
|
|
|
|
SOFTBYTE ldpc_buf[64800 / 8];
|
|
|
|
|
uint8_t bch_buf[64800 / 8]; // Temp storage for hardening before BCH
|
|
|
|
|
s2_bch_engines s2bch;
|
|
|
|
|
s2_bbscrambling bbscrambling;
|
|
|
|
|
std::deque<bbframe> bbframe_q;
|
|
|
|
|
std::deque<int> bitcount_q;
|
|
|
|
|
std::deque<int> errcount_q;
|
|
|
|
|
pipewriter<int> *bitcount, *errcount;
|
|
|
|
|
}; // s2_fecdec_helper
|
|
|
|
|
|
|
|
|
|
#endif // USE_LDPC_TOOL
|
2019-03-17 16:31:42 -04:00
|
|
|
|
|
|
|
|
|
// S2 FRAMER
|
|
|
|
|
// EN 302 307-1 section 5.1 Mode adaptation
|
|
|
|
|
|
|
|
|
|
struct s2_framer : runnable
|
|
|
|
|
{
|
|
|
|
|
uint8_t rolloff_code; // 0=0.35, 1=0.25, 2=0.20, 3=reserved
|
2021-03-24 17:18:22 -04:00
|
|
|
|
// User must provide pls_seq[n_pls_seq].
|
|
|
|
|
// For ACM, user can change pls_seq[0] at runtime.
|
|
|
|
|
// For VCM with a repeating pattern, use n_pls_seq>=2.
|
|
|
|
|
s2_pls *pls_seq;
|
|
|
|
|
int n_pls_seq;
|
|
|
|
|
|
2021-03-12 18:13:00 -05:00
|
|
|
|
|
|
|
|
|
s2_framer(
|
|
|
|
|
scheduler *sch,
|
|
|
|
|
pipebuf<tspacket> &_in,
|
|
|
|
|
pipebuf<bbframe> &_out
|
|
|
|
|
) :
|
|
|
|
|
runnable(sch, "S2 framer"),
|
2021-03-24 17:18:22 -04:00
|
|
|
|
n_pls_seq(0),
|
|
|
|
|
pls_index(0),
|
2021-03-12 18:13:00 -05:00
|
|
|
|
in(_in),
|
|
|
|
|
out(_out)
|
2019-03-17 16:31:42 -04:00
|
|
|
|
{
|
|
|
|
|
nremain = 0;
|
|
|
|
|
remcrc = 0; // CRC for nonexistent previous packet
|
|
|
|
|
}
|
2021-03-21 21:06:26 -04:00
|
|
|
|
|
2019-03-17 16:31:42 -04:00
|
|
|
|
void run()
|
|
|
|
|
{
|
|
|
|
|
while (out.writable() >= 1)
|
|
|
|
|
{
|
2021-03-24 17:18:22 -04:00
|
|
|
|
if (!n_pls_seq ) {
|
|
|
|
|
fail("PLS not specified");
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
s2_pls *pls = &pls_seq[pls_index];
|
|
|
|
|
const modcod_info *mcinfo = check_modcod(pls->modcod);
|
|
|
|
|
const fec_info *fi = &fec_infos[pls->sf][mcinfo->rate];
|
2019-03-17 16:31:42 -04:00
|
|
|
|
int framebytes = fi->Kbch / 8;
|
2021-03-21 21:06:26 -04:00
|
|
|
|
|
|
|
|
|
if (!framebytes) {
|
2019-03-17 16:31:42 -04:00
|
|
|
|
fail("MODCOD/framesize combination not allowed");
|
2021-03-21 21:06:26 -04:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (10 + nremain + 188 * in.readable() < framebytes) {
|
2019-03-17 16:31:42 -04:00
|
|
|
|
break; // Not enough data to fill a frame
|
2021-03-21 21:06:26 -04:00
|
|
|
|
}
|
|
|
|
|
|
2019-03-17 16:31:42 -04:00
|
|
|
|
bbframe *pout = out.wr();
|
2021-03-24 17:18:22 -04:00
|
|
|
|
pout->pls = *pls;
|
2019-03-17 16:31:42 -04:00
|
|
|
|
uint8_t *buf = pout->bytes;
|
|
|
|
|
uint8_t *end = buf + framebytes;
|
|
|
|
|
// EN 302 307-1 section 5.1.6 Base-Band Header insertion
|
|
|
|
|
uint8_t *bbheader = buf;
|
2021-03-24 17:18:22 -04:00
|
|
|
|
uint8_t matype1 = 0;
|
|
|
|
|
matype1 |= 0xc0; // TS
|
|
|
|
|
matype1 |= 0x20; // SIS
|
|
|
|
|
matype1 |= (n_pls_seq==1) ? 0x10 : 0x00; // CCM/ACM
|
|
|
|
|
// TBD ISSY/NPD required for ACM ?
|
|
|
|
|
matype1 |= rolloff_code;
|
|
|
|
|
*buf++ = matype1; // MATYPE-1
|
2019-03-17 16:31:42 -04:00
|
|
|
|
*buf++ = 0; // MATYPE-2
|
|
|
|
|
uint16_t upl = 188 * 8;
|
|
|
|
|
*buf++ = upl >> 8; // UPL MSB
|
|
|
|
|
*buf++ = upl; // UPL LSB
|
|
|
|
|
uint16_t dfl = (framebytes - 10) * 8;
|
|
|
|
|
*buf++ = dfl >> 8; // DFL MSB
|
|
|
|
|
*buf++ = dfl; // DFL LSB
|
|
|
|
|
*buf++ = 0x47; // SYNC
|
|
|
|
|
uint16_t syncd = nremain * 8;
|
|
|
|
|
*buf++ = syncd >> 8; // SYNCD MSB
|
|
|
|
|
*buf++ = syncd; // SYNCD LSB
|
|
|
|
|
*buf++ = crc8.compute(bbheader, 9);
|
|
|
|
|
// Data field
|
|
|
|
|
memcpy(buf, rembuf, nremain); // Leftover from previous runs
|
|
|
|
|
buf += nremain;
|
2021-03-21 21:06:26 -04:00
|
|
|
|
|
2019-03-17 16:31:42 -04:00
|
|
|
|
while (buf < end)
|
|
|
|
|
{
|
|
|
|
|
tspacket *tsp = in.rd();
|
2021-03-21 21:06:26 -04:00
|
|
|
|
|
|
|
|
|
if (tsp->data[0] != MPEG_SYNC) {
|
2019-03-17 16:31:42 -04:00
|
|
|
|
fail("Invalid TS");
|
2021-03-21 21:06:26 -04:00
|
|
|
|
}
|
|
|
|
|
|
2019-03-17 16:31:42 -04:00
|
|
|
|
*buf++ = remcrc; // Replace SYNC with CRC of previous.
|
|
|
|
|
remcrc = crc8.compute(tsp->data + 1, tspacket::SIZE - 1);
|
|
|
|
|
int nused = end - buf;
|
2021-03-21 21:06:26 -04:00
|
|
|
|
|
|
|
|
|
if (nused > tspacket::SIZE - 1) {
|
2019-03-17 16:31:42 -04:00
|
|
|
|
nused = tspacket::SIZE - 1;
|
2021-03-21 21:06:26 -04:00
|
|
|
|
}
|
|
|
|
|
|
2019-03-17 16:31:42 -04:00
|
|
|
|
memcpy(buf, tsp->data + 1, nused);
|
|
|
|
|
buf += nused;
|
2021-03-21 21:06:26 -04:00
|
|
|
|
|
2019-03-17 16:31:42 -04:00
|
|
|
|
if (buf == end)
|
|
|
|
|
{
|
|
|
|
|
nremain = (tspacket::SIZE - 1) - nused;
|
|
|
|
|
memcpy(rembuf, tsp->data + 1 + nused, nremain);
|
|
|
|
|
}
|
2021-03-21 21:06:26 -04:00
|
|
|
|
|
2019-03-17 16:31:42 -04:00
|
|
|
|
in.read(1);
|
|
|
|
|
}
|
2021-03-21 21:06:26 -04:00
|
|
|
|
|
|
|
|
|
if (buf != end) {
|
2019-03-17 16:31:42 -04:00
|
|
|
|
fail("Bug: s2_framer");
|
2021-03-21 21:06:26 -04:00
|
|
|
|
}
|
|
|
|
|
|
2019-03-17 16:31:42 -04:00
|
|
|
|
out.written(1);
|
2021-03-24 17:18:22 -04:00
|
|
|
|
++pls_index;
|
|
|
|
|
|
|
|
|
|
if (pls_index == n_pls_seq) {
|
|
|
|
|
pls_index = 0;
|
|
|
|
|
}
|
2019-03-17 16:31:42 -04:00
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2021-03-24 17:18:22 -04:00
|
|
|
|
private:
|
|
|
|
|
int pls_index; // Next slot to use in pls_seq
|
2019-03-17 16:31:42 -04:00
|
|
|
|
pipereader<tspacket> in;
|
|
|
|
|
pipewriter<bbframe> out;
|
|
|
|
|
crc8_engine crc8;
|
|
|
|
|
int nremain;
|
|
|
|
|
uint8_t rembuf[tspacket::SIZE];
|
|
|
|
|
uint8_t remcrc;
|
|
|
|
|
}; // s2_framer
|
|
|
|
|
|
|
|
|
|
// S2 DEFRAMER
|
|
|
|
|
// EN 302 307-1 section 5.1 Mode adaptation
|
|
|
|
|
|
|
|
|
|
struct s2_deframer : runnable
|
|
|
|
|
{
|
2021-03-24 17:18:22 -04:00
|
|
|
|
int fd_gse; // FD for generic streams, or -1
|
|
|
|
|
|
2021-03-12 18:13:00 -05:00
|
|
|
|
s2_deframer(
|
|
|
|
|
scheduler *sch,
|
|
|
|
|
pipebuf<bbframe> &_in,
|
|
|
|
|
pipebuf<tspacket> &_out,
|
|
|
|
|
pipebuf<int> *_state_out = nullptr,
|
|
|
|
|
pipebuf<unsigned long> *_locktime_out = nullptr
|
|
|
|
|
) :
|
|
|
|
|
runnable(sch, "S2 deframer"),
|
2021-03-24 17:18:22 -04:00
|
|
|
|
fd_gse(-1),
|
2021-03-25 21:17:38 -04:00
|
|
|
|
nleftover(-1),
|
2021-03-12 18:13:00 -05:00
|
|
|
|
in(_in),
|
|
|
|
|
out(_out, MAX_TS_PER_BBFRAME),
|
|
|
|
|
current_state(false),
|
|
|
|
|
state_out(opt_writer(_state_out, 2)),
|
|
|
|
|
report_state(true),
|
|
|
|
|
locktime(0),
|
|
|
|
|
locktime_out(opt_writer(_locktime_out, MAX_TS_PER_BBFRAME))
|
2019-03-17 16:31:42 -04:00
|
|
|
|
{
|
|
|
|
|
}
|
2021-03-21 21:06:26 -04:00
|
|
|
|
|
2019-03-17 16:31:42 -04:00
|
|
|
|
void run()
|
|
|
|
|
{
|
|
|
|
|
while (in.readable() >= 1 && out.writable() >= MAX_TS_PER_BBFRAME &&
|
|
|
|
|
opt_writable(state_out, 2) &&
|
|
|
|
|
opt_writable(locktime_out, MAX_TS_PER_BBFRAME))
|
|
|
|
|
{
|
|
|
|
|
if (report_state)
|
|
|
|
|
{
|
|
|
|
|
// Report unlocked state on first invocation.
|
|
|
|
|
opt_write(state_out, 0);
|
|
|
|
|
report_state = false;
|
|
|
|
|
}
|
2021-03-21 21:06:26 -04:00
|
|
|
|
|
2019-03-17 16:31:42 -04:00
|
|
|
|
run_bbframe(in.rd());
|
|
|
|
|
in.read(1);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2021-03-24 17:18:22 -04:00
|
|
|
|
private:
|
2019-03-17 16:31:42 -04:00
|
|
|
|
void run_bbframe(bbframe *pin)
|
|
|
|
|
{
|
|
|
|
|
uint8_t *bbh = pin->bytes;
|
2021-03-24 17:18:22 -04:00
|
|
|
|
// EN 302 307 section 5.1.6 Base-Band Header Insertion
|
|
|
|
|
uint8_t streamtype = bbh[0] >> 6;
|
|
|
|
|
bool sis = bbh[0] & 32;
|
|
|
|
|
bool ccm = bbh[0] & 16;
|
|
|
|
|
bool issyi = bbh[0] & 8;
|
|
|
|
|
bool npd = bbh[0] & 4;
|
|
|
|
|
int ro_code = bbh[0] & 3;
|
|
|
|
|
uint8_t isi = bbh[1]; // if !sis
|
2019-03-17 16:31:42 -04:00
|
|
|
|
uint16_t upl = (bbh[2] << 8) | bbh[3];
|
|
|
|
|
uint16_t dfl = (bbh[4] << 8) | bbh[5];
|
|
|
|
|
uint8_t sync = bbh[6];
|
|
|
|
|
uint16_t syncd = (bbh[7] << 8) | bbh[8];
|
|
|
|
|
uint8_t crcexp = crc8.compute(bbh, 9);
|
|
|
|
|
uint8_t crc = bbh[9];
|
|
|
|
|
uint8_t *data = bbh + 10;
|
2021-03-21 21:06:26 -04:00
|
|
|
|
|
2019-03-17 16:31:42 -04:00
|
|
|
|
if (sch->debug2)
|
|
|
|
|
{
|
2021-03-24 17:18:22 -04:00
|
|
|
|
static const char *stnames[] = { "GP", "GC", "??", "TS" };
|
2019-03-17 16:31:42 -04:00
|
|
|
|
static float ro_values[] = {0.35, 0.25, 0.20, 0};
|
2021-03-24 17:18:22 -04:00
|
|
|
|
fprintf(stderr, "BBH: crc %02x/%02x(%s) %s %s(ISI=%d) %s%s%s ro=%.2f"
|
|
|
|
|
" upl=%d dfl=%d sync=%02x syncd=%d\n",
|
|
|
|
|
crc,
|
|
|
|
|
crcexp,
|
|
|
|
|
(crc == crcexp) ? "OK" : "KO",
|
|
|
|
|
stnames[streamtype],
|
|
|
|
|
(sis ? "SIS" : "MIS"),
|
|
|
|
|
isi,
|
|
|
|
|
(ccm ? "CCM" : "ACM"),
|
|
|
|
|
(issyi ? " ISSYI": ""),
|
|
|
|
|
(npd ? " NPD" : ""),
|
|
|
|
|
ro_values[ro_code],
|
|
|
|
|
upl,
|
|
|
|
|
dfl,
|
|
|
|
|
sync,
|
|
|
|
|
syncd
|
|
|
|
|
);
|
2019-03-17 16:31:42 -04:00
|
|
|
|
}
|
2021-03-21 21:06:26 -04:00
|
|
|
|
|
2021-03-24 17:18:22 -04:00
|
|
|
|
if (crc != crcexp || dfl > fec_info::KBCH_MAX)
|
2019-03-17 16:31:42 -04:00
|
|
|
|
{
|
|
|
|
|
// Note: Maybe accept syncd=65535
|
2021-03-06 01:37:44 -05:00
|
|
|
|
if (sch->debug) {
|
|
|
|
|
fprintf(stderr, "Bad bbframe\n");
|
|
|
|
|
}
|
2021-03-21 21:06:26 -04:00
|
|
|
|
|
2021-03-25 21:17:38 -04:00
|
|
|
|
nleftover = -1;
|
2019-03-17 16:31:42 -04:00
|
|
|
|
info_unlocked();
|
2021-03-25 21:17:38 -04:00
|
|
|
|
return; // Max one state_out per loop
|
2019-03-17 16:31:42 -04:00
|
|
|
|
}
|
2021-03-21 21:06:26 -04:00
|
|
|
|
|
2021-03-24 17:18:22 -04:00
|
|
|
|
// TBD: Supporting byte-oriented payloads only.
|
|
|
|
|
if ((dfl&7) || (syncd&7))
|
|
|
|
|
{
|
|
|
|
|
fprintf(stderr, "Unsupported bbframe\n");
|
2021-03-25 21:17:38 -04:00
|
|
|
|
nleftover = -1;
|
2021-03-24 17:18:22 -04:00
|
|
|
|
info_unlocked();
|
2021-03-25 21:17:38 -04:00
|
|
|
|
return; // Max one state_out per loop
|
2021-03-24 17:18:22 -04:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (streamtype==3 && upl==188*8 && sync==0x47 && syncd<=dfl)
|
|
|
|
|
{
|
|
|
|
|
handle_ts(data, dfl, syncd, sync);
|
|
|
|
|
}
|
|
|
|
|
else if (streamtype == 1)
|
|
|
|
|
{
|
|
|
|
|
if (fd_gse >= 0)
|
|
|
|
|
{
|
|
|
|
|
ssize_t nw = write(fd_gse, data, dfl/8);
|
|
|
|
|
|
|
|
|
|
if (nw < 0) {
|
|
|
|
|
fatal("write(gse)");
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (nw != dfl/8) {
|
|
|
|
|
fail("partial write(gse");
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
fprintf(stderr, "Unrecognized bbframe\n");
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void handle_ts(uint8_t *data, uint16_t dfl, uint16_t syncd, uint8_t sync)
|
|
|
|
|
{
|
2019-03-17 16:31:42 -04:00
|
|
|
|
int pos; // Start of useful data in this bbframe
|
2021-03-21 21:06:26 -04:00
|
|
|
|
|
2021-03-25 21:17:38 -04:00
|
|
|
|
if (nleftover < 0)
|
2019-03-17 16:31:42 -04:00
|
|
|
|
{
|
2021-03-25 21:17:38 -04:00
|
|
|
|
// Not synced. Skip unusable data at beginning of bbframe
|
2019-03-17 16:31:42 -04:00
|
|
|
|
pos = syncd / 8;
|
2021-03-21 21:06:26 -04:00
|
|
|
|
|
2021-03-06 01:37:44 -05:00
|
|
|
|
if (sch->debug) {
|
|
|
|
|
fprintf(stderr, "Start TS at %d\n", pos);
|
|
|
|
|
}
|
2021-03-21 21:06:26 -04:00
|
|
|
|
|
2021-03-25 21:17:38 -04:00
|
|
|
|
nleftover = 0;
|
2019-03-17 16:31:42 -04:00
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
// Sanity check
|
2021-03-25 21:17:38 -04:00
|
|
|
|
if (syncd / 8 != 188 - nleftover)
|
2019-03-17 16:31:42 -04:00
|
|
|
|
{
|
2021-03-06 01:37:44 -05:00
|
|
|
|
if (sch->debug) {
|
|
|
|
|
fprintf(stderr, "Lost a bbframe ?\n");
|
|
|
|
|
}
|
2021-03-21 21:06:26 -04:00
|
|
|
|
|
2021-03-25 21:17:38 -04:00
|
|
|
|
nleftover = -1;
|
2019-03-17 16:31:42 -04:00
|
|
|
|
info_unlocked();
|
2021-03-25 21:17:38 -04:00
|
|
|
|
return; // Max one state_out per loop
|
2019-03-17 16:31:42 -04:00
|
|
|
|
}
|
|
|
|
|
pos = 0;
|
|
|
|
|
}
|
2021-03-21 21:06:26 -04:00
|
|
|
|
|
2021-03-25 21:17:38 -04:00
|
|
|
|
while ( pos+(188-nleftover)+1 <= dfl/8 )
|
2019-03-17 16:31:42 -04:00
|
|
|
|
{
|
2021-03-25 21:17:38 -04:00
|
|
|
|
// Enough data available for one packet and its CRC.
|
2019-03-17 16:31:42 -04:00
|
|
|
|
tspacket *pout = out.wr();
|
2021-03-25 21:17:38 -04:00
|
|
|
|
memcpy(pout->data, leftover, nleftover); // NOP most of the time
|
|
|
|
|
memcpy(pout->data+nleftover, data+pos, 188-nleftover);
|
2019-03-17 16:31:42 -04:00
|
|
|
|
pout->data[0] = sync; // Replace CRC
|
2021-03-25 21:17:38 -04:00
|
|
|
|
uint8_t crc = crc8.compute(pout->data+1, 188-1);
|
|
|
|
|
|
|
|
|
|
if (data[pos+(188-nleftover)] == crc)
|
|
|
|
|
{
|
|
|
|
|
info_good_packet();
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
pout->data[1] |= 0x80; // Set TEI bit
|
|
|
|
|
|
|
|
|
|
if (sch->debug) {
|
|
|
|
|
fprintf(stderr, "C");
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2019-03-17 16:31:42 -04:00
|
|
|
|
out.written(1);
|
2021-03-25 21:17:38 -04:00
|
|
|
|
pos += 188 - nleftover;
|
|
|
|
|
nleftover = 0;
|
2019-03-17 16:31:42 -04:00
|
|
|
|
}
|
2021-03-21 21:06:26 -04:00
|
|
|
|
|
2019-03-17 16:31:42 -04:00
|
|
|
|
int remain = dfl / 8 - pos;
|
2021-03-21 21:06:26 -04:00
|
|
|
|
|
2021-03-25 21:17:38 -04:00
|
|
|
|
if (nleftover + remain > (int) sizeof(leftover)) {
|
|
|
|
|
fail("Bug: TS deframer");
|
2019-03-17 16:31:42 -04:00
|
|
|
|
}
|
2021-03-25 21:17:38 -04:00
|
|
|
|
|
|
|
|
|
memcpy(leftover+nleftover, data+pos, remain);
|
|
|
|
|
nleftover += remain;
|
2019-03-17 16:31:42 -04:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void info_unlocked()
|
|
|
|
|
{
|
|
|
|
|
info_is_locked(false);
|
|
|
|
|
locktime = 0;
|
|
|
|
|
}
|
2021-03-21 21:06:26 -04:00
|
|
|
|
|
2019-03-17 16:31:42 -04:00
|
|
|
|
void info_good_packet()
|
|
|
|
|
{
|
|
|
|
|
info_is_locked(true);
|
|
|
|
|
++locktime;
|
|
|
|
|
opt_write(locktime_out, locktime);
|
|
|
|
|
}
|
2021-03-21 21:06:26 -04:00
|
|
|
|
|
2019-03-17 16:31:42 -04:00
|
|
|
|
void info_is_locked(bool newstate)
|
|
|
|
|
{
|
|
|
|
|
if (newstate != current_state)
|
|
|
|
|
{
|
|
|
|
|
opt_write(state_out, newstate ? 1 : 0);
|
|
|
|
|
current_state = newstate;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
crc8_engine crc8;
|
2021-03-25 21:17:38 -04:00
|
|
|
|
int nleftover; // Bytes in leftover[]:
|
|
|
|
|
// -1 = not synced.
|
|
|
|
|
// 0 = no leftover data
|
|
|
|
|
// 1 - 187 = incomplete packet
|
|
|
|
|
// 188 = waiting for CRC
|
2019-03-17 16:31:42 -04:00
|
|
|
|
uint8_t leftover[188];
|
|
|
|
|
static const int MAX_TS_PER_BBFRAME = fec_info::KBCH_MAX / 8 / 188 + 1;
|
|
|
|
|
bool locked;
|
|
|
|
|
pipereader<bbframe> in;
|
|
|
|
|
pipewriter<tspacket> out;
|
|
|
|
|
int current_state;
|
|
|
|
|
pipewriter<int> *state_out;
|
|
|
|
|
bool report_state;
|
|
|
|
|
unsigned long locktime;
|
|
|
|
|
pipewriter<unsigned long> *locktime_out;
|
|
|
|
|
}; // s2_deframer
|
|
|
|
|
|
|
|
|
|
} // namespace leansdr
|
|
|
|
|
|
|
|
|
|
#endif // LEANSDR_DVBS2_H
|