FT8 demod: make FFT engine

This commit is contained in:
f4exb 2023-01-10 23:27:17 +01:00
parent 6adac5f45b
commit 22acbebab6
4 changed files with 126 additions and 138 deletions

View File

@ -20,39 +20,20 @@
///////////////////////////////////////////////////////////////////////////////////
#include "fft.h"
#include <mutex>
// #include <unistd.h>
#include <assert.h>
// #include <sys/file.h>
// #include <sys/types.h>
// #include <sys/stat.h>
#include "util.h"
#define TIMING 0
namespace FT8 {
// MEASURE=0, ESTIMATE=64, PATIENT=32
int fftw_type = FFTW_ESTIMATE;
static std::mutex plansmu;
static std::mutex plansmu2;
static Plan *plans[1000];
static int nplans;
// static int plan_master_pid = 0;
Plan *get_plan(int n, const char *why)
FFTEngine::Plan *FFTEngine::get_plan(int n, const char *why)
{
// cache fftw plans in the parent process,
// so they will already be there for fork()ed children.
plansmu.lock();
// if (plan_master_pid == 0)
// {
// plan_master_pid = getpid();
// }
for (int i = 0; i < nplans; i++)
{
if (plans[i]->n_ == n && plans[i]->type_ == fftw_type
@ -73,16 +54,6 @@ Plan *get_plan(int n, const char *why)
#endif
// fftw_make_planner_thread_safe();
// the fftw planner is not thread-safe.
// can't rely on plansmu because both ft8.so
// and snd.so may be using separate copies of fft.cc.
// the lock file really should be per process.
// int lockfd = creat("/tmp/fft-plan-lock", 0666);
// assert(lockfd >= 0);
// fchmod(lockfd, 0666);
// int lockret = flock(lockfd, LOCK_EX);
// assert(lockret == 0);
plansmu2.lock();
fftwf_set_timelimit(5);
@ -109,10 +80,6 @@ Plan *get_plan(int n, const char *why)
// FFTW_PATIENT
// FFTW_EXHAUSTIVE
int type = fftw_type;
// if (getpid() != plan_master_pid)
// {
// type = FFTW_ESTIMATE;
// }
p->type_ = type;
p->fwd_ = fftwf_plan_dft_r2c_1d(n, p->r_, p->c_, type);
assert(p->fwd_);
@ -131,14 +98,11 @@ Plan *get_plan(int n, const char *why)
p->crev_ = fftwf_plan_dft_1d(n, p->cc2_, p->cc1_, FFTW_BACKWARD, type);
assert(p->crev_);
// flock(lockfd, LOCK_UN);
// close(lockfd);
plansmu2.unlock();
assert(nplans + 1 < 1000);
plans[nplans] = p;
// __sync_synchronize();
nplans += 1;
#if TIMING
@ -160,12 +124,12 @@ Plan *get_plan(int n, const char *why)
// real inputs, complex outputs.
// output has (block / 2) + 1 points.
//
std::vector<std::complex<float>> one_fft(
std::vector<std::complex<float>> FFTEngine::one_fft(
const std::vector<float> &samples,
int i0,
int block,
const char *why,
Plan *p
FFTEngine::Plan *p
)
{
assert(i0 >= 0);
@ -242,7 +206,7 @@ std::vector<std::complex<float>> one_fft(
// do a full set of FFTs, one per symbol-time.
// bins[time][frequency]
//
ffts_t ffts(const std::vector<float> &samples, int i0, int block, const char *why)
FFTEngine::ffts_t FFTEngine::ffts(const std::vector<float> &samples, int i0, int block, const char *why)
{
assert(i0 >= 0);
assert(block > 1 && (block % 2) == 0);
@ -313,7 +277,7 @@ ffts_t ffts(const std::vector<float> &samples, int i0, int block, const char *wh
// real inputs, complex outputs.
// output has block points.
//
std::vector<std::complex<float>> one_fft_c(
std::vector<std::complex<float>> FFTEngine::one_fft_c(
const std::vector<float> &samples,
int i0,
int block,
@ -373,7 +337,7 @@ std::vector<std::complex<float>> one_fft_c(
return out;
}
std::vector<std::complex<float>> one_fft_cc(
std::vector<std::complex<float>> FFTEngine::one_fft_cc(
const std::vector<std::complex<float>> &samples,
int i0,
int block,
@ -434,7 +398,7 @@ std::vector<std::complex<float>> one_fft_cc(
return out;
}
std::vector<std::complex<float>> one_ifft_cc(
std::vector<std::complex<float>> FFTEngine::one_ifft_cc(
const std::vector<std::complex<float>> &bins,
const char *why
)
@ -483,7 +447,7 @@ std::vector<std::complex<float>> one_ifft_cc(
return out;
}
std::vector<float> one_ifft(const std::vector<std::complex<float>> &bins, const char *why)
std::vector<float> FFTEngine::one_ifft(const std::vector<std::complex<float>> &bins, const char *why)
{
int nbins = bins.size();
int block = (nbins - 1) * 2;
@ -531,7 +495,7 @@ std::vector<float> one_ifft(const std::vector<std::complex<float>> &bins, const
//
// the return value is x + iy, where y is the hilbert transform of x.
//
std::vector<std::complex<float>> analytic(const std::vector<float> &x, const char *why)
std::vector<std::complex<float>> FFTEngine::analytic(const std::vector<float> &x, const char *why)
{
ulong n = x.size();
@ -573,7 +537,7 @@ std::vector<std::complex<float>> analytic(const std::vector<float> &x, const cha
//
// like weakutil.py's freq_shift().
//
std::vector<float> hilbert_shift(const std::vector<float> &x, float hz0, float hz1, int rate)
std::vector<float> FFTEngine::hilbert_shift(const std::vector<float> &x, float hz0, float hz1, int rate)
{
// y = scipy.signal.hilbert(x)
std::vector<std::complex<float>> y = analytic(x, "hilbert_shift");
@ -595,7 +559,7 @@ std::vector<float> hilbert_shift(const std::vector<float> &x, float hz0, float h
return ret;
}
void fft_stats()
void FFTEngine::fft_stats()
{
for (int i = 0; i < nplans; i++)
{

View File

@ -22,56 +22,73 @@
#ifndef FFT_H
#define FFT_H
#include <mutex>
#include <vector>
#include <complex>
#include <fftw3.h>
namespace FT8
{
// a cached fftw plan, for both of:
// fftwf_plan_dft_r2c_1d(n, m_in, m_out, FFTW_ESTIMATE);
// fftwf_plan_dft_c2r_1d(n, m_in, m_out, FFTW_ESTIMATE);
class Plan
class FFTEngine
{
public:
int n_;
int type_;
// a cached fftw plan, for both of:
// fftwf_plan_dft_r2c_1d(n, m_in, m_out, FFTW_ESTIMATE);
// fftwf_plan_dft_c2r_1d(n, m_in, m_out, FFTW_ESTIMATE);
class Plan
{
public:
int n_;
int type_;
//
// real -> complex
//
fftwf_complex *c_; // (n_ / 2) + 1 of these
float *r_; // n_ of these
fftwf_plan fwd_; // forward plan
fftwf_plan rev_; // reverse plan
//
// real -> complex
//
fftwf_complex *c_; // (n_ / 2) + 1 of these
float *r_; // n_ of these
fftwf_plan fwd_; // forward plan
fftwf_plan rev_; // reverse plan
//
// complex -> complex
//
fftwf_complex *cc1_; // n
fftwf_complex *cc2_; // n
fftwf_plan cfwd_; // forward plan
fftwf_plan crev_; // reverse plan
//
// complex -> complex
//
fftwf_complex *cc1_; // n
fftwf_complex *cc2_; // n
fftwf_plan cfwd_; // forward plan
fftwf_plan crev_; // reverse plan
// how much CPU time spent in FFTs that use this plan.
#if TIMING
double time_;
#endif
const char *why_;
int uses_;
};
// how much CPU time spent in FFTs that use this plan.
#if TIMING
double time_;
#endif
const char *why_;
int uses_;
}; // Plan
Plan *get_plan(int n, const char *why);
FFTEngine() : nplans(0)
{}
std::vector<std::complex<float>> one_fft(const std::vector<float> &samples, int i0, int block, const char *why, Plan *p);
std::vector<float> one_ifft(const std::vector<std::complex<float>> &bins, const char *why);
typedef std::vector<std::vector<std::complex<float>>> ffts_t;
ffts_t ffts(const std::vector<float> &samples, int i0, int block, const char *why);
std::vector<std::complex<float>> one_fft_c(const std::vector<float> &samples, int i0, int block, const char *why);
std::vector<std::complex<float>> one_fft_cc(const std::vector<std::complex<float>> &samples, int i0, int block, const char *why);
std::vector<std::complex<float>> one_ifft_cc(const std::vector<std::complex<float>> &bins, const char *why);
std::vector<std::complex<float>> analytic(const std::vector<float> &x, const char *why);
std::vector<float> hilbert_shift(const std::vector<float> &x, float hz0, float hz1, int rate);
Plan *get_plan(int n, const char *why);
std::vector<std::complex<float>> one_fft(const std::vector<float> &samples, int i0, int block, const char *why, Plan *p);
std::vector<float> one_ifft(const std::vector<std::complex<float>> &bins, const char *why);
typedef std::vector<std::vector<std::complex<float>>> ffts_t;
ffts_t ffts(const std::vector<float> &samples, int i0, int block, const char *why);
std::vector<std::complex<float>> one_fft_c(const std::vector<float> &samples, int i0, int block, const char *why);
std::vector<std::complex<float>> one_fft_cc(const std::vector<std::complex<float>> &samples, int i0, int block, const char *why);
std::vector<std::complex<float>> one_ifft_cc(const std::vector<std::complex<float>> &bins, const char *why);
std::vector<std::complex<float>> analytic(const std::vector<float> &x, const char *why);
std::vector<float> hilbert_shift(const std::vector<float> &x, float hz0, float hz1, int rate);
private:
void fft_stats();
std::mutex plansmu;
std::mutex plansmu2;
Plan *plans[1000];
int nplans;
// MEASURE=0, ESTIMATE=64, PATIENT=32
static const int fftw_type = FFTW_ESTIMATE;
}; // FFTEngine
} // namespace FT8

View File

@ -326,7 +326,8 @@ FT8::FT8(
double deadline,
double final_deadline,
CallbackInterface *cb,
std::vector<cdecode> prevdecs
std::vector<cdecode> prevdecs,
FFTEngine *fftEngine
)
{
samples_ = samples;
@ -349,11 +350,12 @@ FT8::FT8(
}
hack_size_ = -1;
hack_data_ = 0;
hack_data_ = nullptr;
hack_off_ = -1;
hack_len_ = -1;
plan32_ = 0;
plan32_ = nullptr;
fftEngine_ = fftEngine;
}
FT8::~FT8()
@ -362,7 +364,7 @@ FT8::~FT8()
// strength of costas block of signal with tone 0 at bi0,
// and symbol zero at si0.
float FT8::one_coarse_strength(const ffts_t &bins, int bi0, int si0)
float FT8::one_coarse_strength(const FFTEngine::ffts_t &bins, int bi0, int si0)
{
int costas[] = {3, 1, 4, 0, 6, 5, 2};
@ -491,7 +493,7 @@ int FT8::blocksize(int rate)
// look for potential signals by searching FFT bins for Costas symbol
// blocks. returns a vector of candidate positions.
//
std::vector<Strength> FT8::coarse(const ffts_t &bins, int si0, int si1)
std::vector<Strength> FT8::coarse(const FFTEngine::ffts_t &bins, int si0, int si1)
{
int block = blocksize(rate_);
int nbins = bins[0].size();
@ -579,8 +581,8 @@ std::vector<float> FT8::reduce_rate(
}
int alen = a.size();
std::vector<std::complex<float>> bins1 = one_fft(a, 0, alen,
"reduce_rate1", 0);
std::vector<std::complex<float>> bins1 = fftEngine_->one_fft(
a, 0, alen, "reduce_rate1", 0);
int nbins1 = bins1.size();
float bin_hz = arate / (float)alen;
@ -630,7 +632,7 @@ std::vector<float> FT8::reduce_rate(
}
// use ifft to reduce the rate.
std::vector<float> vvv = one_ifft(bbins, "reduce_rate2");
std::vector<float> vvv = fftEngine_->one_ifft(bbins, "reduce_rate2");
delta_hz = delta * bin_hz;
@ -640,7 +642,7 @@ std::vector<float> FT8::reduce_rate(
void FT8::go(int npasses)
{
// cache to avoid cost of fftw planner mutex.
plan32_ = get_plan(32, "cache32");
plan32_ = fftEngine_->get_plan(32, "cache32");
if (0)
{
@ -831,8 +833,8 @@ void FT8::go(int npasses)
// just do this once, re-use for every fractional fft_shift
// and down_v7_f() to 200 sps.
std::vector<std::complex<float>> bins = one_fft(samples_, 0, samples_.size(),
"go1", 0);
std::vector<std::complex<float>> bins = fftEngine_->one_fft(
samples_, 0, samples_.size(), "go1", 0);
for (int hz_frac_i = 0; hz_frac_i < params.coarse_hz_n; hz_frac_i++)
{
@ -851,7 +853,7 @@ void FT8::go(int npasses)
for (int off_frac_i = 0; off_frac_i < params.coarse_off_n; off_frac_i++)
{
int off_frac = off_frac_i * (block / params.coarse_off_n);
ffts_t bins = ffts(samples1, off_frac, block, "go2");
FFTEngine::ffts_t bins = fftEngine_->ffts(samples1, off_frac, block, "go2");
std::vector<Strength> oo = coarse(bins, si0, si1);
for (int i = 0; i < (int)oo.size(); i++)
{
@ -927,7 +929,7 @@ float FT8::one_strength(const std::vector<float> &samples200, float hz, int off)
int start = starts[which];
for (int si = 0; si < 7; si++)
{
auto fft = one_fft(samples200, off + (si + start) * 32, 32, "one_strength", plan32_);
auto fft = fftEngine_->one_fft(samples200, off + (si + start) * 32, 32, "one_strength", plan32_);
for (int bi = 0; bi < 8; bi++)
{
float x = std::abs(fft[bin0 + bi]);
@ -1004,7 +1006,8 @@ float FT8::one_strength_known(
for (int si = 0; si < 79; si += params.known_sparse)
{
auto fft = one_fft(samples, off + si * block, block, "one_strength_known", 0);
auto fft = fftEngine_->one_fft(samples, off + si * block, block, "one_strength_known", 0);
if (params.known_strength_how == 7)
{
std::complex<float> c = fft[bin0 + syms[si]];
@ -1226,7 +1229,7 @@ void FT8::search_both_known(
int best_off = 0;
float best_strength = 0;
std::vector<std::complex<float>> bins = one_fft(samples, 0, samples.size(), "stfk", 0);
std::vector<std::complex<float>> bins = fftEngine_->one_fft(samples, 0, samples.size(), "stfk", 0);
float hz_start, hz_inc, hz_end;
if (params.third_hz_n > 1)
@ -1290,7 +1293,7 @@ std::vector<float> FT8::fft_shift(
}
else
{
bins = one_fft(samples, off, len, "fft_shift", 0);
bins = fftEngine_->one_fft(samples, off, len, "fft_shift", 0);
hack_bins_ = bins;
hack_size_ = samples.size();
hack_off_ = off;
@ -1331,7 +1334,7 @@ std::vector<float> FT8::fft_shift_f(
bins1[i] = 0;
}
}
std::vector<float> out = one_ifft(bins1, "fft_shift");
std::vector<float> out = fftEngine_->one_ifft(bins1, "fft_shift");
return out;
}
@ -1356,12 +1359,12 @@ std::vector<float> FT8::shift200(
}
// returns a mini-FFT of 79 8-tone symbols.
ffts_t FT8::extract(const std::vector<float> &samples200, float, int off)
FFTEngine::ffts_t FT8::extract(const std::vector<float> &samples200, float, int off)
{
ffts_t bins3 = ffts(samples200, off, 32, "extract");
FFTEngine::ffts_t bins3 = fftEngine_->ffts(samples200, off, 32, "extract");
FFTEngine::ffts_t m79(79);
ffts_t m79(79);
for (int si = 0; si < 79; si++)
{
m79[si].resize(8);
@ -1388,9 +1391,9 @@ ffts_t FT8::extract(const std::vector<float> &samples200, float, int off)
//
// m79 is a 79x8 array of complex.
//
ffts_t FT8::un_gray_code_c(const ffts_t &m79)
FFTEngine::ffts_t FT8::un_gray_code_c(const FFTEngine::ffts_t &m79)
{
ffts_t m79a(79);
FFTEngine::ffts_t m79a(79);
int map[] = {0, 1, 3, 2, 6, 4, 5, 7};
for (int si = 0; si < 79; si++)
@ -1687,7 +1690,7 @@ void FT8::make_stats(
// number of cycles and thus preserves phase from one symbol to the
// next.
//
std::vector<std::vector<float>> FT8::soft_c2m(const ffts_t &c79)
std::vector<std::vector<float>> FT8::soft_c2m(const FFTEngine::ffts_t &c79)
{
std::vector<std::vector<float>> m79(79);
std::vector<float> raw_phases(79); // of strongest tone in each symbol time
@ -1859,7 +1862,7 @@ float FT8::bayes(
//
// c79 is 79x8 complex tones, before un-gray-coding.
//
void FT8::soft_decode(const ffts_t &c79, float ll174[])
void FT8::soft_decode(const FFTEngine::ffts_t &c79, float ll174[])
{
std::vector<std::vector<float>> m79(79);
@ -1974,9 +1977,9 @@ void FT8::soft_decode(const ffts_t &c79, float ll174[])
//
// c79 is 79x8 complex tones, before un-gray-coding.
//
void FT8::c_soft_decode(const ffts_t &c79x, float ll174[])
void FT8::c_soft_decode(const FFTEngine::ffts_t &c79x, float ll174[])
{
ffts_t c79 = c_convert_to_snr(c79x);
FFTEngine::ffts_t c79 = c_convert_to_snr(c79x);
int costas[] = {3, 1, 4, 0, 6, 5, 2};
std::complex<float> maxes[79];
@ -2185,11 +2188,11 @@ std::vector<float> FT8::extract_bits(const std::vector<int> &syms, const std::ve
// that they have the same phase, by summing the complex
// correlations for each possible pair and using the max.
void FT8::soft_decode_pairs(
const ffts_t &m79x,
const FFTEngine::ffts_t &m79x,
float ll174[]
)
{
ffts_t m79 = c_convert_to_snr(m79x);
FFTEngine::ffts_t m79 = c_convert_to_snr(m79x);
struct BitInfo
{
@ -2319,11 +2322,11 @@ void FT8::soft_decode_pairs(
}
void FT8::soft_decode_triples(
const ffts_t &m79x,
const FFTEngine::ffts_t &m79x,
float ll174[]
)
{
ffts_t m79 = c_convert_to_snr(m79x);
FFTEngine::ffts_t m79 = c_convert_to_snr(m79x);
struct BitInfo
{
@ -2610,7 +2613,7 @@ std::vector<std::complex<float>> FT8::fbandpass(
std::vector<float> FT8::down_v7(const std::vector<float> &samples, float hz)
{
int len = samples.size();
std::vector<std::complex<float>> bins = one_fft(samples, 0, len, "down_v7a", 0);
std::vector<std::complex<float>> bins = fftEngine_->one_fft(samples, 0, len, "down_v7a", 0);
return down_v7_f(bins, len, hz);
}
@ -2655,7 +2658,7 @@ std::vector<float> FT8::down_v7_f(const std::vector<std::complex<float>> &bins,
std::vector<std::complex<float>> bbins(blen / 2 + 1);
for (int i = 0; i < (int)bbins.size(); i++)
bbins[i] = bins1[i];
std::vector<float> out = one_ifft(bbins, "down_v7b");
std::vector<float> out = fftEngine_->one_ifft(bbins, "down_v7b");
return out;
}
@ -2731,7 +2734,7 @@ int FT8::one_iter(const std::vector<float> &samples200, int best_off, float hz_f
// estimate SNR, yielding numbers vaguely similar to WSJT-X.
// m79 is a 79x8 complex FFT output.
//
float FT8::guess_snr(const ffts_t &m79)
float FT8::guess_snr(const FFTEngine::ffts_t &m79)
{
int costas[] = {3, 1, 4, 0, 6, 5, 2};
float noises = 0;
@ -2798,7 +2801,7 @@ float FT8::guess_snr(const ffts_t &m79)
// adj_off is the amount to change the offset, in samples.
// should be subtracted from offset.
//
void FT8::fine(const ffts_t &m79, int, float &adj_hz, float &adj_off)
void FT8::fine(const FFTEngine::ffts_t &m79, int, float &adj_hz, float &adj_off)
{
adj_hz = 0.0;
adj_off = 0.0;
@ -2991,7 +2994,7 @@ int FT8::one_iter1(
best_hz);
// mini 79x8 FFT.
ffts_t m79 = extract(samples200, 25, best_off);
FFTEngine::ffts_t m79 = extract(samples200, 25, best_off);
// look at symbol-to-symbol phase change to try
// to improve best_hz and best_off.
@ -3157,9 +3160,9 @@ void FT8::subtract(
// move nsamples so that signal is centered in bin0.
float diff0 = (bin0 * bin_hz) - hz0;
float diff1 = (bin0 * bin_hz) - hz1;
std::vector<float> moved = hilbert_shift(nsamples_, diff0, diff1, rate_);
std::vector<float> moved = fftEngine_->hilbert_shift(nsamples_, diff0, diff1, rate_);
ffts_t bins = ffts(moved, off0, block, "subtract");
FFTEngine::ffts_t bins = fftEngine_->ffts(moved, off0, block, "subtract");
if (bin0 + 8 > (int)bins[0].size())
return;
@ -3291,7 +3294,7 @@ void FT8::subtract(
}
}
nsamples_ = hilbert_shift(moved, -diff0, -diff1, rate_);
nsamples_ = fftEngine_->hilbert_shift(moved, -diff0, -diff1, rate_);
}
//
@ -3311,7 +3314,7 @@ int FT8::try_decode(
float,
int use_osd,
const char *comment1,
const ffts_t &m79
const FFTEngine::ffts_t &m79
)
{
int a174[174];
@ -3467,6 +3470,7 @@ void FT8Decoder::entry(
double t0 = now();
double deadline = t0 + time_left;
double final_deadline = t0 + total_time_left;
FFTEngine fftEngine;
// decodes from previous runs, for subtraction.
std::vector<cdecode> prevdecs;
@ -3520,7 +3524,8 @@ void FT8Decoder::entry(
deadline,
final_deadline,
cb,
prevdecs
prevdecs,
&fftEngine
);
ft8->getParams() = getParams(); // transfer parameters

View File

@ -292,7 +292,7 @@ public:
std::vector<std::complex<float>> hack_bins_;
std::vector<cdecode> prevdecs_;
Plan *plan32_;
FFTEngine::Plan *plan32_;
FT8(
const std::vector<float> &samples,
@ -305,12 +305,13 @@ public:
double deadline,
double final_deadline,
CallbackInterface *cb,
std::vector<cdecode> prevdecs
std::vector<cdecode> prevdecs,
FFTEngine *fftEngine
);
~FT8();
// strength of costas block of signal with tone 0 at bi0,
// and symbol zero at si0.
float one_coarse_strength(const ffts_t &bins, int bi0, int si0);
float one_coarse_strength(const FFTEngine::ffts_t &bins, int bi0, int si0);
// return symbol length in samples at the given rate.
// insist on integer symbol lengths so that we can
// use whole FFT bins.
@ -319,7 +320,7 @@ public:
// look for potential signals by searching FFT bins for Costas symbol
// blocks. returns a vector of candidate positions.
//
std::vector<Strength> coarse(const ffts_t &bins, int si0, int si1);
std::vector<Strength> coarse(const FFTEngine::ffts_t &bins, int si0, int si1);
//
// reduce the sample rate from arate to brate.
// center hz0..hz1 in the new nyquist range.
@ -425,11 +426,11 @@ public:
float hz
);
// returns a mini-FFT of 79 8-tone symbols.
ffts_t extract(const std::vector<float> &samples200, float, int off);
FFTEngine::ffts_t extract(const std::vector<float> &samples200, float, int off);
//
// m79 is a 79x8 array of complex.
//
ffts_t un_gray_code_c(const ffts_t &m79);
FFTEngine::ffts_t un_gray_code_c(const FFTEngine::ffts_t &m79);
//
// m79 is a 79x8 array of float.
//
@ -468,7 +469,7 @@ public:
// number of cycles and thus preserves phase from one symbol to the
// next.
//
std::vector<std::vector<float>> soft_c2m(const ffts_t &c79);
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
@ -486,11 +487,11 @@ public:
//
// c79 is 79x8 complex tones, before un-gray-coding.
//
void soft_decode(const ffts_t &c79, float ll174[]);
void soft_decode(const FFTEngine::ffts_t &c79, float ll174[]);
//
// c79 is 79x8 complex tones, before un-gray-coding.
//
void c_soft_decode(const ffts_t &c79x, float ll174[]);
void c_soft_decode(const FFTEngine::ffts_t &c79x, float ll174[]);
//
// turn 79 symbol numbers into 174 bits.
// strip out the three Costas sync blocks,
@ -506,11 +507,11 @@ public:
// that they have the same phase, by summing the complex
// correlations for each possible pair and using the max.
void soft_decode_pairs(
const ffts_t &m79x,
const FFTEngine::ffts_t &m79x,
float ll174[]
);
void soft_decode_triples(
const ffts_t &m79x,
const FFTEngine::ffts_t &m79x,
float ll174[]
);
//
@ -563,7 +564,7 @@ public:
// estimate SNR, yielding numbers vaguely similar to WSJT-X.
// m79 is a 79x8 complex FFT output.
//
float guess_snr(const ffts_t &m79);
float guess_snr(const FFTEngine::ffts_t &m79);
//
// compare phases of successive symbols to guess whether
// the starting offset is a little too high or low.
@ -584,7 +585,7 @@ public:
// adj_off is the amount to change the offset, in samples.
// should be subtracted from offset.
//
void fine(const ffts_t &m79, int, float &adj_hz, float &adj_off);
void fine(const FFTEngine::ffts_t &m79, int, float &adj_hz, float &adj_off);
//
// subtract a corrected decoded signal from nsamples_,
// perhaps revealing a weaker signal underneath,
@ -615,7 +616,7 @@ public:
float,
int use_osd,
const char *comment1,
const ffts_t &m79
const FFTEngine::ffts_t &m79
);
//
// given 174 bits corrected by LDPC, work
@ -644,6 +645,7 @@ public:
FT8Params& getParams() { return params; }
private:
FT8Params params;
FFTEngine *fftEngine_;
static const double apriori174[];
}; // class FT8