1
0
mirror of https://github.com/f4exb/sdrangel.git synced 2026-03-18 14:09:37 -04:00

FT8 demod: fix duplicated code (1)

This commit is contained in:
f4exb 2026-03-13 18:37:09 +01:00
parent 71d6208fe9
commit 81f7d2ff99
4 changed files with 104 additions and 331 deletions

View File

@ -1547,88 +1547,6 @@ std::vector<std::vector<float>> FT4::soft_c2m(const FFTEngine::ffts_t &c103)
return m103;
}
//
// guess the probability that a bit is zero vs one,
// based on strengths of strongest tones that would
// give it those values. for soft LDPC decoding.
//
// returns log-likelihood, zero is positive, one is negative.
//
float FT4::bayes(
FT4Params& params,
float best_zero,
float best_one,
int lli,
Stats &bests,
Stats &all
)
{
float maxlog = 4.97;
float ll = 0;
float pzero = 0.5;
float pone = 0.5;
if (params.use_apriori)
{
pzero = 1.0 - apriori174[lli];
pone = apriori174[lli];
}
//
// Bayes combining rule normalization from:
// http://cs.wellesley.edu/~anderson/writing/naive-bayes.pdf
//
// a = P(zero)P(e0|zero)P(e1|zero)
// b = P(one)P(e0|one)P(e1|one)
// p = a / (a + b)
//
// also see Mark Owen's book Practical Signal Processing,
// Chapter 6.
//
// zero
float a = pzero * bests.problt(best_zero) * (1.0 - all.problt(best_one));
// printf("FT8::bayes: a: %f bp: %f ap: %f \n", a, bests.problt(best_zero), all.problt(best_one));
if (params.bayes_how == 1) {
a *= all.problt(all.mean() + (best_zero - best_one));
}
// one
float b = pone * bests.problt(best_one) * (1.0 - all.problt(best_zero));
// printf("FT8::bayes: b: %f bp: %f ap: %f \n", b, bests.problt(best_one), all.problt(best_zero));
if (params.bayes_how == 1) {
b *= all.problt(all.mean() + (best_one - best_zero));
}
float p;
if (a + b == 0) {
p = 0.5;
} else {
p = a / (a + b);
}
// printf("FT8::bayes: all.mean: %f a: %f b: %f p: %f\n", all.mean(), a, b, p);
if (1 - p == 0.0) {
ll = maxlog;
} else {
ll = log(p / (1 - p));
}
if (ll > maxlog) {
ll = maxlog;
}
if (ll < -maxlog) {
ll = -maxlog;
}
return ll;
}
//
// c103 is 103x4 complex tones, before un-gray-coding.
//
@ -1720,7 +1638,7 @@ void FT4::soft_decode(const FFTEngine::ffts_t &c103, float ll174[])
}
}
float ll = bayes(params, best_zero, best_one, lli, bests, all);
float ll = FT8::bayes(params, best_zero, best_one, lli, bests, all);
ll174[lli++] = ll;
}
}
@ -1909,7 +1827,7 @@ void FT4::c_soft_decode(const FFTEngine::ffts_t &c103x, float ll174[])
}
}
float ll = bayes(params, best_zero, best_one, lli, bests, all);
float ll = FT8::bayes(params, best_zero, best_one, lli, bests, all);
ll174[lli++] = ll;
}
}
@ -2084,7 +2002,7 @@ void FT4::soft_decode_pairs(
float best_zero = bitinfo[si * 2 + i].zero;
float best_one = bitinfo[si * 2 + i].one;
// ll174[lli++] = best_zero > best_one ? 4.99 : -4.99;
float ll = bayes(params, best_zero, best_one, lli, bests, all);
float ll = FT8::bayes(params, best_zero, best_one, lli, bests, all);
ll174[lli++] = ll;
}
}
@ -2262,7 +2180,7 @@ void FT4::soft_decode_triples(
float best_zero = bitinfo[si * 2 + i].zero;
float best_one = bitinfo[si * 2 + i].one;
// ll174[lli++] = best_zero > best_one ? 4.99 : -4.99;
float ll = bayes(params, best_zero, best_one, lli, bests, all);
float ll = FT8::bayes(params, best_zero, best_one, lli, bests, all);
ll174[lli++] = ll;
}
}

177
ft8/ft4.h
View File

@ -17,162 +17,23 @@ class QThread;
namespace FT8 {
// 1920-point FFT at 12000 samples/second
// 6.25 Hz spacing, 0.16 seconds/symbol
// FT4 characteristics:
// 576-point FFT at 12000 samples/second
// 23.4 Hz spacing, 0.048 seconds/symbol
// encode chain:
// 77 bits
// append 14 bits CRC (for 91 bits)
// LDPC(174,91) yields 174 bits
// that's 58 3-bit FSK-8 symbols
// gray code each 3 bits
// insert three 7-symbol Costas sync arrays
// at symbol #s 0, 36, 72 of final signal
// thus: 79 FSK-8 symbols
// total transmission time is 12.64 seconds
// that's 87 2-bit FSK-4 symbols
// gray code each 2 bits
// insert four 4-symbol Costas sync arrays
// at positions: 0-3, 33-36, 66-69, 99-102
// thus: 103 FSK-4 symbols
// add 2 ramp symbols at start and end to make 105 symbols
// total transmission time is 5.04 seconds
// tunable parameters
class FT8_API FT4Params
{
public:
int nthreads; // number of parallel threads, for multi-core
int npasses_one; // number of spectral subtraction passes
int npasses_two; // number of spectral subtraction passes
int ldpc_iters; // how hard LDPC decoding should work
int snr_win; // averaging window, in symbols, for SNR conversion
int snr_how; // technique to measure "N" for SNR. 0 means median of the 8 tones.
float shoulder200; // for 200 sps bandpass filter
float shoulder200_extra; // for bandpass filter
float second_hz_win; // +/- hz
int second_hz_n; // divide total window into this many pieces
float second_off_win; // +/- search window in symbol-times
int second_off_n;
int third_hz_n;
float third_hz_win;
int third_off_n;
float third_off_win;
float log_tail;
float log_rate;
int problt_how_noise;
int problt_how_sig;
int use_apriori;
int use_hints; // 1 means use all hints, 2 means just CQ hints
int win_type;
int use_osd;
int osd_depth; // 6; // don't increase beyond 6, produces too much garbage
int osd_ldpc_thresh; // demand this many correct LDPC parity bits before OSD
int ncoarse; // number of offsets per hz produced by coarse()
int ncoarse_blocks;
float tminus; // start looking at 0.5 - tminus seconds
float tplus;
int coarse_off_n;
int coarse_hz_n;
float already_hz;
float overlap;
int overlap_edges;
float nyquist;
int oddrate;
float pass0_frac;
int reduce_how;
float go_extra;
int do_reduce;
int pass_threshold;
int strength_how;
int known_strength_how;
int coarse_strength_how;
float reduce_shoulder;
float reduce_factor;
float reduce_extra;
float coarse_all;
int second_count;
int soft_phase_win;
float subtract_ramp;
int subtract_edge_symbols; // model one extra tapered symbol at frame start/end during subtraction (does not yield significant improvement)
int soft_ones;
int soft_pairs;
int soft_triples;
int do_second;
int do_fine_hz;
int do_fine_off;
int do_third;
float fine_thresh;
int fine_max_off;
int fine_max_tone;
int known_sparse;
float c_soft_weight;
int c_soft_win;
int bayes_how;
FT4Params()
{
nthreads = 8; // number of parallel threads, for multi-core
npasses_one = 3; // number of spectral subtraction passes
npasses_two = 3; // number of spectral subtraction passes
ldpc_iters = 25; // how hard LDPC decoding should work
snr_win = 7; // averaging window, in symbols, for SNR conversion
snr_how = 3; // technique to measure "N" for SNR. 0 means median of the 8 tones.
shoulder200 = 10; // for 200 sps bandpass filter
shoulder200_extra = 0.0; // for bandpass filter
second_hz_win = 3.5; // +/- hz
second_hz_n = 8; // divide total window into this many pieces
second_off_win = 0.5; // +/- search window in symbol-times
second_off_n = 10;
third_hz_n = 3;
third_hz_win = 0.25;
third_off_n = 4;
third_off_win = 0.075;
log_tail = 0.1;
log_rate = 8.0;
problt_how_noise = 0; // Gaussian
problt_how_sig = 0; // Gaussian
use_apriori = 1;
use_hints = 2; // 1 means use all hints, 2 means just CQ hints
win_type = 1;
use_osd = 1;
osd_depth = 0; // 6; // don't increase beyond 6, produces too much garbage
osd_ldpc_thresh = 70; // demand this many correct LDPC parity bits before OSD
ncoarse = 1; // number of offsets per hz produced by coarse()
ncoarse_blocks = 1;
tminus = 2.2; // start looking at 0.5 - tminus seconds
tplus = 2.4;
coarse_off_n = 4;
coarse_hz_n = 4;
already_hz = 27;
overlap = 20;
overlap_edges = 0;
nyquist = 0.925;
oddrate = 1;
pass0_frac = 1.0;
reduce_how = 2;
go_extra = 3.5;
do_reduce = 1;
pass_threshold = 1;
strength_how = 4;
known_strength_how = 7;
coarse_strength_how = 6;
reduce_shoulder = -1;
reduce_factor = 0.25;
reduce_extra = 0;
coarse_all = -1;
second_count = 3;
soft_phase_win = 2;
subtract_ramp = 0.11;
subtract_edge_symbols = 0;
soft_ones = 2;
soft_pairs = 1;
soft_triples = 1;
do_second = 1;
do_fine_hz = 1;
do_fine_off = 1;
do_third = 2;
fine_thresh = 0.19;
fine_max_off = 2;
fine_max_tone = 4;
known_sparse = 1;
c_soft_weight = 7;
c_soft_win = 2;
bayes_how = 1;
}
}; // class FT4Params
using FT4Params = FT8Params;
class FT8_API FT4ParamsLight
{
@ -393,22 +254,6 @@ private:
// next.
//
std::vector<std::vector<float>> soft_c2m(const FFTEngine::ffts_t &c79);
//
// guess the probability that a bit is zero vs one,
// based on strengths of strongest tones that would
// give it those values. for soft LDPC decoding.
//
// returns log-likelihood, zero is positive, one is negative.
//
static float bayes(
FT4Params& params,
float best_zero,
float best_one,
int lli,
Stats &bests,
Stats &all
);
//
// c79 is 79x8 complex tones, before un-gray-coding.
//
void soft_decode(const FFTEngine::ffts_t &c79, float ll174[]);

View File

@ -47,6 +47,71 @@
namespace FT8 {
namespace {
template<typename ParamsT>
float bayesImpl(
const double apriori174[],
ParamsT& params,
float best_zero,
float best_one,
int lli,
Stats &bests,
Stats &all
)
{
float maxlog = 4.97;
float ll = 0;
float pzero = 0.5;
float pone = 0.5;
if (params.use_apriori)
{
pzero = 1.0 - apriori174[lli];
pone = apriori174[lli];
}
// zero
float a = pzero * bests.problt(best_zero) * (1.0 - all.problt(best_one));
if (params.bayes_how == 1) {
a *= all.problt(all.mean() + (best_zero - best_one));
}
// one
float b = pone * bests.problt(best_one) * (1.0 - all.problt(best_zero));
if (params.bayes_how == 1) {
b *= all.problt(all.mean() + (best_one - best_zero));
}
float p;
if (a + b == 0) {
p = 0.5;
} else {
p = a / (a + b);
}
if (1 - p == 0.0) {
ll = maxlog;
} else {
ll = log(p / (1 - p));
}
if (ll > maxlog) {
ll = maxlog;
}
if (ll < -maxlog) {
ll = -maxlog;
}
return ll;
}
} // namespace
// a-priori probability of each of the 174 LDPC codeword
// bits being one. measured from reconstructed correct
// codewords, into ft8bits, then python bprob.py.
@ -1675,72 +1740,10 @@ float FT8::bayes(
Stats &all
)
{
float maxlog = 4.97;
float ll = 0;
float pzero = 0.5;
float pone = 0.5;
if (params.use_apriori)
{
pzero = 1.0 - apriori174[lli];
pone = apriori174[lli];
}
//
// Bayes combining rule normalization from:
// http://cs.wellesley.edu/~anderson/writing/naive-bayes.pdf
//
// a = P(zero)P(e0|zero)P(e1|zero)
// b = P(one)P(e0|one)P(e1|one)
// p = a / (a + b)
//
// also see Mark Owen's book Practical Signal Processing,
// Chapter 6.
//
// zero
float a = pzero * bests.problt(best_zero) * (1.0 - all.problt(best_one));
// printf("FT8::bayes: a: %f bp: %f ap: %f \n", a, bests.problt(best_zero), all.problt(best_one));
if (params.bayes_how == 1) {
a *= all.problt(all.mean() + (best_zero - best_one));
}
// one
float b = pone * bests.problt(best_one) * (1.0 - all.problt(best_zero));
// printf("FT8::bayes: b: %f bp: %f ap: %f \n", b, bests.problt(best_one), all.problt(best_zero));
if (params.bayes_how == 1) {
b *= all.problt(all.mean() + (best_one - best_zero));
}
float p;
if (a + b == 0) {
p = 0.5;
} else {
p = a / (a + b);
}
// printf("FT8::bayes: all.mean: %f a: %f b: %f p: %f\n", all.mean(), a, b, p);
if (1 - p == 0.0) {
ll = maxlog;
} else {
ll = log(p / (1 - p));
}
if (ll > maxlog) {
ll = maxlog;
}
if (ll < -maxlog) {
ll = -maxlog;
}
return ll;
return bayesImpl(apriori174, params, best_zero, best_one, lli, bests, all);
}
//
// c79 is 79x8 complex tones, before un-gray-coding.
//

View File

@ -36,6 +36,21 @@
class QThread;
namespace FT8 {
// FT8 characteristics:
// 1920-point FFT at 12000 samples/second
// 6.25 Hz spacing, 0.16 seconds/symbol
// encode chain:
// 77 bits
// append 14 bits CRC (for 91 bits)
// LDPC(174,91) yields 174 bits
// that's 58 3-bit FSK-8 symbols
// gray code each 3 bits
// insert three 7-symbol Costas sync arrays
// at symbol #s 0, 36, 72 of final signal
// thus: 79 FSK-8 symbols
// total transmission time is 12.64 seconds
// Callback interface to get the results
class FT8_API CallbackInterface
{
@ -52,7 +67,6 @@ public:
virtual QString get_name() = 0;
};
class FT8_API Strength
{
public:
@ -71,20 +85,7 @@ struct FT8_API cdecode
int *bits; // 174
};
// 1920-point FFT at 12000 samples/second
// 6.25 Hz spacing, 0.16 seconds/symbol
// encode chain:
// 77 bits
// append 14 bits CRC (for 91 bits)
// LDPC(174,91) yields 174 bits
// that's 58 3-bit FSK-8 symbols
// gray code each 3 bits
// insert three 7-symbol Costas sync arrays
// at symbol #s 0, 36, 72 of final signal
// thus: 79 FSK-8 symbols
// total transmission time is 12.64 seconds
// tunable parameters
// tunable parameters for all FT decoders
class FT8_API FT8Params
{
public:
@ -140,6 +141,7 @@ public:
int second_count;
int soft_phase_win;
float subtract_ramp;
int subtract_edge_symbols; // model one extra tapered symbol at frame start/end during subtraction
int soft_ones;
int soft_pairs;
int soft_triples;
@ -209,6 +211,7 @@ public:
second_count = 3;
soft_phase_win = 2;
subtract_ramp = 0.11;
subtract_edge_symbols = 0;
soft_ones = 2;
soft_pairs = 1;
soft_triples = 1;
@ -230,6 +233,8 @@ public:
class FT8_API FT8 : public QObject
{
Q_OBJECT
friend class FT4;
public:
FT8(
const std::vector<float> &samples,
@ -457,6 +462,7 @@ private:
// next.
//
std::vector<std::vector<float>> soft_c2m(const FFTEngine::ffts_t &c79);
public:
//
// guess the probability that a bit is zero vs one,
// based on strengths of strongest tones that would
@ -472,6 +478,7 @@ private:
Stats &bests,
Stats &all
);
private:
//
// c79 is 79x8 complex tones, before un-gray-coding.
//