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 "fft.h"
#include <mutex>
// #include <unistd.h>
#include <assert.h> #include <assert.h>
// #include <sys/file.h>
// #include <sys/types.h>
// #include <sys/stat.h>
#include "util.h" #include "util.h"
#define TIMING 0 #define TIMING 0
namespace FT8 { namespace FT8 {
// MEASURE=0, ESTIMATE=64, PATIENT=32 FFTEngine::Plan *FFTEngine::get_plan(int n, const char *why)
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)
{ {
// cache fftw plans in the parent process, // cache fftw plans in the parent process,
// so they will already be there for fork()ed children. // so they will already be there for fork()ed children.
plansmu.lock(); plansmu.lock();
// if (plan_master_pid == 0)
// {
// plan_master_pid = getpid();
// }
for (int i = 0; i < nplans; i++) for (int i = 0; i < nplans; i++)
{ {
if (plans[i]->n_ == n && plans[i]->type_ == fftw_type if (plans[i]->n_ == n && plans[i]->type_ == fftw_type
@ -73,16 +54,6 @@ Plan *get_plan(int n, const char *why)
#endif #endif
// fftw_make_planner_thread_safe(); // 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(); plansmu2.lock();
fftwf_set_timelimit(5); fftwf_set_timelimit(5);
@ -109,10 +80,6 @@ Plan *get_plan(int n, const char *why)
// FFTW_PATIENT // FFTW_PATIENT
// FFTW_EXHAUSTIVE // FFTW_EXHAUSTIVE
int type = fftw_type; int type = fftw_type;
// if (getpid() != plan_master_pid)
// {
// type = FFTW_ESTIMATE;
// }
p->type_ = type; p->type_ = type;
p->fwd_ = fftwf_plan_dft_r2c_1d(n, p->r_, p->c_, type); p->fwd_ = fftwf_plan_dft_r2c_1d(n, p->r_, p->c_, type);
assert(p->fwd_); 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); p->crev_ = fftwf_plan_dft_1d(n, p->cc2_, p->cc1_, FFTW_BACKWARD, type);
assert(p->crev_); assert(p->crev_);
// flock(lockfd, LOCK_UN);
// close(lockfd);
plansmu2.unlock(); plansmu2.unlock();
assert(nplans + 1 < 1000); assert(nplans + 1 < 1000);
plans[nplans] = p; plans[nplans] = p;
// __sync_synchronize();
nplans += 1; nplans += 1;
#if TIMING #if TIMING
@ -160,12 +124,12 @@ Plan *get_plan(int n, const char *why)
// real inputs, complex outputs. // real inputs, complex outputs.
// output has (block / 2) + 1 points. // 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, const std::vector<float> &samples,
int i0, int i0,
int block, int block,
const char *why, const char *why,
Plan *p FFTEngine::Plan *p
) )
{ {
assert(i0 >= 0); assert(i0 >= 0);
@ -242,7 +206,7 @@ std::vector<std::complex<float>> one_fft(
// do a full set of FFTs, one per symbol-time. // do a full set of FFTs, one per symbol-time.
// bins[time][frequency] // 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(i0 >= 0);
assert(block > 1 && (block % 2) == 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. // real inputs, complex outputs.
// output has block points. // 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, const std::vector<float> &samples,
int i0, int i0,
int block, int block,
@ -373,7 +337,7 @@ std::vector<std::complex<float>> one_fft_c(
return out; 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, const std::vector<std::complex<float>> &samples,
int i0, int i0,
int block, int block,
@ -434,7 +398,7 @@ std::vector<std::complex<float>> one_fft_cc(
return out; 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 std::vector<std::complex<float>> &bins,
const char *why const char *why
) )
@ -483,7 +447,7 @@ std::vector<std::complex<float>> one_ifft_cc(
return out; 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 nbins = bins.size();
int block = (nbins - 1) * 2; 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. // 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(); 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(). // 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) // y = scipy.signal.hilbert(x)
std::vector<std::complex<float>> y = analytic(x, "hilbert_shift"); 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; return ret;
} }
void fft_stats() void FFTEngine::fft_stats()
{ {
for (int i = 0; i < nplans; i++) for (int i = 0; i < nplans; i++)
{ {

View File

@ -22,56 +22,73 @@
#ifndef FFT_H #ifndef FFT_H
#define FFT_H #define FFT_H
#include <mutex>
#include <vector> #include <vector>
#include <complex> #include <complex>
#include <fftw3.h> #include <fftw3.h>
namespace FT8 namespace FT8
{ {
// a cached fftw plan, for both of: class FFTEngine
// 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: public:
int n_; // a cached fftw plan, for both of:
int type_; // 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 // real -> complex
// //
fftwf_complex *c_; // (n_ / 2) + 1 of these fftwf_complex *c_; // (n_ / 2) + 1 of these
float *r_; // n_ of these float *r_; // n_ of these
fftwf_plan fwd_; // forward plan fftwf_plan fwd_; // forward plan
fftwf_plan rev_; // reverse plan fftwf_plan rev_; // reverse plan
// //
// complex -> complex // complex -> complex
// //
fftwf_complex *cc1_; // n fftwf_complex *cc1_; // n
fftwf_complex *cc2_; // n fftwf_complex *cc2_; // n
fftwf_plan cfwd_; // forward plan fftwf_plan cfwd_; // forward plan
fftwf_plan crev_; // reverse plan fftwf_plan crev_; // reverse plan
// how much CPU time spent in FFTs that use this plan. // how much CPU time spent in FFTs that use this plan.
#if TIMING #if TIMING
double time_; double time_;
#endif #endif
const char *why_; const char *why_;
int uses_; 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); Plan *get_plan(int n, const char *why);
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; std::vector<std::complex<float>> one_fft(const std::vector<float> &samples, int i0, int block, const char *why, Plan *p);
ffts_t ffts(const std::vector<float> &samples, int i0, int block, const char *why); std::vector<float> one_ifft(const std::vector<std::complex<float>> &bins, const char *why);
std::vector<std::complex<float>> one_fft_c(const std::vector<float> &samples, int i0, int block, const char *why); typedef std::vector<std::vector<std::complex<float>>> ffts_t;
std::vector<std::complex<float>> one_fft_cc(const std::vector<std::complex<float>> &samples, int i0, int block, const char *why); ffts_t ffts(const std::vector<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>> one_fft_c(const std::vector<float> &samples, int i0, int block, const char *why);
std::vector<std::complex<float>> analytic(const std::vector<float> &x, 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<float> hilbert_shift(const std::vector<float> &x, float hz0, float hz1, int rate); 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 } // namespace FT8

View File

@ -326,7 +326,8 @@ FT8::FT8(
double deadline, double deadline,
double final_deadline, double final_deadline,
CallbackInterface *cb, CallbackInterface *cb,
std::vector<cdecode> prevdecs std::vector<cdecode> prevdecs,
FFTEngine *fftEngine
) )
{ {
samples_ = samples; samples_ = samples;
@ -349,11 +350,12 @@ FT8::FT8(
} }
hack_size_ = -1; hack_size_ = -1;
hack_data_ = 0; hack_data_ = nullptr;
hack_off_ = -1; hack_off_ = -1;
hack_len_ = -1; hack_len_ = -1;
plan32_ = 0; plan32_ = nullptr;
fftEngine_ = fftEngine;
} }
FT8::~FT8() FT8::~FT8()
@ -362,7 +364,7 @@ FT8::~FT8()
// strength of costas block of signal with tone 0 at bi0, // strength of costas block of signal with tone 0 at bi0,
// and symbol zero at si0. // 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}; 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 // look for potential signals by searching FFT bins for Costas symbol
// blocks. returns a vector of candidate positions. // 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 block = blocksize(rate_);
int nbins = bins[0].size(); int nbins = bins[0].size();
@ -579,8 +581,8 @@ std::vector<float> FT8::reduce_rate(
} }
int alen = a.size(); int alen = a.size();
std::vector<std::complex<float>> bins1 = one_fft(a, 0, alen, std::vector<std::complex<float>> bins1 = fftEngine_->one_fft(
"reduce_rate1", 0); a, 0, alen, "reduce_rate1", 0);
int nbins1 = bins1.size(); int nbins1 = bins1.size();
float bin_hz = arate / (float)alen; float bin_hz = arate / (float)alen;
@ -630,7 +632,7 @@ std::vector<float> FT8::reduce_rate(
} }
// use ifft to reduce the 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; delta_hz = delta * bin_hz;
@ -640,7 +642,7 @@ std::vector<float> FT8::reduce_rate(
void FT8::go(int npasses) void FT8::go(int npasses)
{ {
// cache to avoid cost of fftw planner mutex. // cache to avoid cost of fftw planner mutex.
plan32_ = get_plan(32, "cache32"); plan32_ = fftEngine_->get_plan(32, "cache32");
if (0) if (0)
{ {
@ -831,8 +833,8 @@ void FT8::go(int npasses)
// just do this once, re-use for every fractional fft_shift // just do this once, re-use for every fractional fft_shift
// and down_v7_f() to 200 sps. // and down_v7_f() to 200 sps.
std::vector<std::complex<float>> bins = one_fft(samples_, 0, samples_.size(), std::vector<std::complex<float>> bins = fftEngine_->one_fft(
"go1", 0); samples_, 0, samples_.size(), "go1", 0);
for (int hz_frac_i = 0; hz_frac_i < params.coarse_hz_n; hz_frac_i++) 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++) 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); 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); std::vector<Strength> oo = coarse(bins, si0, si1);
for (int i = 0; i < (int)oo.size(); i++) 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]; int start = starts[which];
for (int si = 0; si < 7; si++) 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++) for (int bi = 0; bi < 8; bi++)
{ {
float x = std::abs(fft[bin0 + 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) 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) if (params.known_strength_how == 7)
{ {
std::complex<float> c = fft[bin0 + syms[si]]; std::complex<float> c = fft[bin0 + syms[si]];
@ -1226,7 +1229,7 @@ void FT8::search_both_known(
int best_off = 0; int best_off = 0;
float best_strength = 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; float hz_start, hz_inc, hz_end;
if (params.third_hz_n > 1) if (params.third_hz_n > 1)
@ -1290,7 +1293,7 @@ std::vector<float> FT8::fft_shift(
} }
else else
{ {
bins = one_fft(samples, off, len, "fft_shift", 0); bins = fftEngine_->one_fft(samples, off, len, "fft_shift", 0);
hack_bins_ = bins; hack_bins_ = bins;
hack_size_ = samples.size(); hack_size_ = samples.size();
hack_off_ = off; hack_off_ = off;
@ -1331,7 +1334,7 @@ std::vector<float> FT8::fft_shift_f(
bins1[i] = 0; bins1[i] = 0;
} }
} }
std::vector<float> out = one_ifft(bins1, "fft_shift"); std::vector<float> out = fftEngine_->one_ifft(bins1, "fft_shift");
return out; return out;
} }
@ -1356,12 +1359,12 @@ std::vector<float> FT8::shift200(
} }
// returns a mini-FFT of 79 8-tone symbols. // 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++) for (int si = 0; si < 79; si++)
{ {
m79[si].resize(8); 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. // 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}; int map[] = {0, 1, 3, 2, 6, 4, 5, 7};
for (int si = 0; si < 79; si++) 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 // number of cycles and thus preserves phase from one symbol to the
// next. // 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<std::vector<float>> m79(79);
std::vector<float> raw_phases(79); // of strongest tone in each symbol time 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. // 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); 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. // 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}; int costas[] = {3, 1, 4, 0, 6, 5, 2};
std::complex<float> maxes[79]; 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 // that they have the same phase, by summing the complex
// correlations for each possible pair and using the max. // correlations for each possible pair and using the max.
void FT8::soft_decode_pairs( void FT8::soft_decode_pairs(
const ffts_t &m79x, const FFTEngine::ffts_t &m79x,
float ll174[] float ll174[]
) )
{ {
ffts_t m79 = c_convert_to_snr(m79x); FFTEngine::ffts_t m79 = c_convert_to_snr(m79x);
struct BitInfo struct BitInfo
{ {
@ -2319,11 +2322,11 @@ void FT8::soft_decode_pairs(
} }
void FT8::soft_decode_triples( void FT8::soft_decode_triples(
const ffts_t &m79x, const FFTEngine::ffts_t &m79x,
float ll174[] float ll174[]
) )
{ {
ffts_t m79 = c_convert_to_snr(m79x); FFTEngine::ffts_t m79 = c_convert_to_snr(m79x);
struct BitInfo 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) std::vector<float> FT8::down_v7(const std::vector<float> &samples, float hz)
{ {
int len = samples.size(); 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); 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); std::vector<std::complex<float>> bbins(blen / 2 + 1);
for (int i = 0; i < (int)bbins.size(); i++) for (int i = 0; i < (int)bbins.size(); i++)
bbins[i] = bins1[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; 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. // estimate SNR, yielding numbers vaguely similar to WSJT-X.
// m79 is a 79x8 complex FFT output. // 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}; int costas[] = {3, 1, 4, 0, 6, 5, 2};
float noises = 0; 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. // adj_off is the amount to change the offset, in samples.
// should be subtracted from offset. // 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_hz = 0.0;
adj_off = 0.0; adj_off = 0.0;
@ -2991,7 +2994,7 @@ int FT8::one_iter1(
best_hz); best_hz);
// mini 79x8 FFT. // 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 // look at symbol-to-symbol phase change to try
// to improve best_hz and best_off. // to improve best_hz and best_off.
@ -3157,9 +3160,9 @@ void FT8::subtract(
// move nsamples so that signal is centered in bin0. // move nsamples so that signal is centered in bin0.
float diff0 = (bin0 * bin_hz) - hz0; float diff0 = (bin0 * bin_hz) - hz0;
float diff1 = (bin0 * bin_hz) - hz1; 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()) if (bin0 + 8 > (int)bins[0].size())
return; 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, float,
int use_osd, int use_osd,
const char *comment1, const char *comment1,
const ffts_t &m79 const FFTEngine::ffts_t &m79
) )
{ {
int a174[174]; int a174[174];
@ -3467,6 +3470,7 @@ void FT8Decoder::entry(
double t0 = now(); double t0 = now();
double deadline = t0 + time_left; double deadline = t0 + time_left;
double final_deadline = t0 + total_time_left; double final_deadline = t0 + total_time_left;
FFTEngine fftEngine;
// decodes from previous runs, for subtraction. // decodes from previous runs, for subtraction.
std::vector<cdecode> prevdecs; std::vector<cdecode> prevdecs;
@ -3520,7 +3524,8 @@ void FT8Decoder::entry(
deadline, deadline,
final_deadline, final_deadline,
cb, cb,
prevdecs prevdecs,
&fftEngine
); );
ft8->getParams() = getParams(); // transfer parameters ft8->getParams() = getParams(); // transfer parameters

View File

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