mirror of
https://github.com/f4exb/sdrangel.git
synced 2024-12-23 10:05:46 -05:00
FT8 support: fixed FFT moving plans in a simgleton with global mutex
This commit is contained in:
parent
e23db7e65c
commit
aaa244974b
@ -3,6 +3,8 @@ project(ft8)
|
||||
set(ft8_SOURCES
|
||||
fft.cpp
|
||||
ft8.cpp
|
||||
ft8plan.cpp
|
||||
ft8plans.cpp
|
||||
libldpc.cpp
|
||||
osd.cpp
|
||||
unpack.cpp
|
||||
@ -13,6 +15,8 @@ set(ft8_SOURCES
|
||||
set(ft8_HEADERS
|
||||
fft.h
|
||||
ft8.h
|
||||
ft8plan.h
|
||||
ft8plans.h
|
||||
libldpc.h
|
||||
osd.h
|
||||
unpack.h
|
||||
|
131
ft8/fft.cpp
131
ft8/fft.cpp
@ -20,91 +20,20 @@
|
||||
///////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
// #include <assert.h>
|
||||
#include <fftw3.h>
|
||||
#include <QDebug>
|
||||
#include "fft.h"
|
||||
#include "util.h"
|
||||
#include "ft8plan.h"
|
||||
#include "ft8plans.h"
|
||||
|
||||
namespace FT8 {
|
||||
|
||||
FFTEngine* FFTEngine::m_instance= nullptr;;
|
||||
FFTEngine::FFTEngine()
|
||||
{}
|
||||
|
||||
FFTEngine *FFTEngine::GetInstance()
|
||||
{
|
||||
if (!m_instance) {
|
||||
m_instance = new FFTEngine();
|
||||
}
|
||||
|
||||
return m_instance;
|
||||
}
|
||||
|
||||
FFTEngine::Plan *FFTEngine::get_plan(int n)
|
||||
{
|
||||
// cache fftw plans in the parent process,
|
||||
// so they will already be there for fork()ed children.
|
||||
|
||||
m_plansmu.lock();
|
||||
|
||||
for (int i = 0; i < m_nplans; i++)
|
||||
{
|
||||
if (m_plans[i]->n_ == n && m_plans[i]->type_ == M_FFTW_TYPE)
|
||||
{
|
||||
Plan *p = m_plans[i];
|
||||
m_plansmu.unlock();
|
||||
return p;
|
||||
}
|
||||
}
|
||||
|
||||
// fftw_make_planner_thread_safe();
|
||||
m_plansmu2.lock();
|
||||
|
||||
fftwf_set_timelimit(5);
|
||||
|
||||
//
|
||||
// real -> complex
|
||||
//
|
||||
|
||||
Plan *p = new Plan;
|
||||
|
||||
p->n_ = n;
|
||||
p->r_ = (float *)fftwf_malloc(n * sizeof(float));
|
||||
// assert(p->r_);
|
||||
p->c_ = (fftwf_complex *)fftwf_malloc(((n / 2) + 1) * sizeof(fftwf_complex));
|
||||
// assert(p->c_);
|
||||
|
||||
// FFTW_ESTIMATE
|
||||
// FFTW_MEASURE
|
||||
// FFTW_PATIENT
|
||||
// FFTW_EXHAUSTIVE
|
||||
int type = M_FFTW_TYPE;
|
||||
p->type_ = type;
|
||||
p->fwd_ = fftwf_plan_dft_r2c_1d(n, p->r_, p->c_, type);
|
||||
// assert(p->fwd_);
|
||||
p->rev_ = fftwf_plan_dft_c2r_1d(n, p->c_, p->r_, type);
|
||||
// assert(p->rev_);
|
||||
|
||||
//
|
||||
// complex -> complex
|
||||
//
|
||||
p->cc1_ = (fftwf_complex *)fftwf_malloc(n * sizeof(fftwf_complex));
|
||||
// assert(p->cc1_);
|
||||
p->cc2_ = (fftwf_complex *)fftwf_malloc(n * sizeof(fftwf_complex));
|
||||
// assert(p->cc2_);
|
||||
p->cfwd_ = fftwf_plan_dft_1d(n, p->cc1_, p->cc2_, FFTW_FORWARD, type);
|
||||
// assert(p->cfwd_);
|
||||
p->crev_ = fftwf_plan_dft_1d(n, p->cc2_, p->cc1_, FFTW_BACKWARD, type);
|
||||
// assert(p->crev_);
|
||||
|
||||
m_plansmu2.unlock();
|
||||
|
||||
// assert(m_nplans + 1 < 1000);
|
||||
|
||||
m_plans[m_nplans] = p;
|
||||
m_nplans += 1;
|
||||
|
||||
m_plansmu.unlock();
|
||||
|
||||
return p;
|
||||
}
|
||||
FFTEngine::~FFTEngine()
|
||||
{}
|
||||
|
||||
//
|
||||
// do just one FFT on samples[i0..i0+block]
|
||||
@ -114,8 +43,7 @@ FFTEngine::Plan *FFTEngine::get_plan(int n)
|
||||
std::vector<std::complex<float>> FFTEngine::one_fft(
|
||||
const std::vector<float> &samples,
|
||||
int i0,
|
||||
int block,
|
||||
FFTEngine::Plan *p
|
||||
int block
|
||||
)
|
||||
{
|
||||
// assert(i0 >= 0);
|
||||
@ -123,12 +51,9 @@ std::vector<std::complex<float>> FFTEngine::one_fft(
|
||||
|
||||
int nsamples = samples.size();
|
||||
int nbins = (block / 2) + 1;
|
||||
Plan *p = FT8Plans::GetInstance()->getPlan(block);
|
||||
|
||||
if (!p) {
|
||||
p = get_plan(block);
|
||||
}
|
||||
|
||||
fftwf_plan m_plan = p->fwd_;
|
||||
fftwf_plan plan = p->fwd_;
|
||||
|
||||
// assert((int)samples.size() - i0 >= block);
|
||||
|
||||
@ -157,7 +82,7 @@ std::vector<std::complex<float>> FFTEngine::one_fft(
|
||||
fftwf_complex *m_out = (fftwf_complex *)fftwf_malloc(sizeof(fftwf_complex) * ((p->n_ / 2) + 1));
|
||||
// assert(m_out);
|
||||
|
||||
fftwf_execute_dft_r2c(m_plan, m_in, m_out);
|
||||
fftwf_execute_dft_r2c(plan, m_in, m_out);
|
||||
|
||||
std::vector<std::complex<float>> out(nbins);
|
||||
|
||||
@ -168,8 +93,10 @@ std::vector<std::complex<float>> FFTEngine::one_fft(
|
||||
out[bi] = std::complex<float>(re, im);
|
||||
}
|
||||
|
||||
if (m_in_allocated)
|
||||
if (m_in_allocated) {
|
||||
fftwf_free(m_in);
|
||||
}
|
||||
|
||||
fftwf_free(m_out);
|
||||
|
||||
return out;
|
||||
@ -193,8 +120,8 @@ FFTEngine::ffts_t FFTEngine::ffts(const std::vector<float> &samples, int i0, int
|
||||
bins[si].resize(nbins);
|
||||
}
|
||||
|
||||
Plan *p = get_plan(block);
|
||||
fftwf_plan m_plan = p->fwd_;
|
||||
Plan *p = FT8Plans::GetInstance()->getPlan(block);
|
||||
fftwf_plan plan = p->fwd_;
|
||||
|
||||
// allocate our own b/c using p->m_in and p->m_out isn't thread-safe.
|
||||
float *m_in = (float *)fftwf_malloc(sizeof(float) * p->n_);
|
||||
@ -220,7 +147,7 @@ FFTEngine::ffts_t FFTEngine::ffts(const std::vector<float> &samples, int i0, int
|
||||
}
|
||||
}
|
||||
|
||||
fftwf_execute_dft_r2c(m_plan, m_in, m_out);
|
||||
fftwf_execute_dft_r2c(plan, m_in, m_out);
|
||||
|
||||
for (int bi = 0; bi < nbins; bi++)
|
||||
{
|
||||
@ -253,8 +180,8 @@ std::vector<std::complex<float>> FFTEngine::one_fft_c(
|
||||
|
||||
int nsamples = samples.size();
|
||||
|
||||
Plan *p = get_plan(block);
|
||||
fftwf_plan m_plan = p->cfwd_;
|
||||
Plan *p = FT8Plans::GetInstance()->getPlan(block);
|
||||
fftwf_plan plan = p->cfwd_;
|
||||
|
||||
fftwf_complex *m_in = (fftwf_complex *)fftwf_malloc(block * sizeof(fftwf_complex));
|
||||
fftwf_complex *m_out = (fftwf_complex *)fftwf_malloc(block * sizeof(fftwf_complex));
|
||||
@ -273,7 +200,7 @@ std::vector<std::complex<float>> FFTEngine::one_fft_c(
|
||||
m_in[i][1] = 0; // imaginary
|
||||
}
|
||||
|
||||
fftwf_execute_dft(m_plan, m_in, m_out);
|
||||
fftwf_execute_dft(plan, m_in, m_out);
|
||||
|
||||
std::vector<std::complex<float>> out(block);
|
||||
float norm = 1.0 / sqrt(block);
|
||||
@ -304,8 +231,8 @@ std::vector<std::complex<float>> FFTEngine::one_fft_cc(
|
||||
|
||||
int nsamples = samples.size();
|
||||
|
||||
Plan *p = get_plan(block);
|
||||
fftwf_plan m_plan = p->cfwd_;
|
||||
Plan *p = FT8Plans::GetInstance()->getPlan(block);
|
||||
fftwf_plan plan = p->cfwd_;
|
||||
|
||||
fftwf_complex *m_in = (fftwf_complex *)fftwf_malloc(block * sizeof(fftwf_complex));
|
||||
fftwf_complex *m_out = (fftwf_complex *)fftwf_malloc(block * sizeof(fftwf_complex));
|
||||
@ -325,7 +252,7 @@ std::vector<std::complex<float>> FFTEngine::one_fft_cc(
|
||||
}
|
||||
}
|
||||
|
||||
fftwf_execute_dft(m_plan, m_in, m_out);
|
||||
fftwf_execute_dft(plan, m_in, m_out);
|
||||
|
||||
std::vector<std::complex<float>> out(block);
|
||||
|
||||
@ -351,8 +278,8 @@ std::vector<std::complex<float>> FFTEngine::one_ifft_cc(
|
||||
{
|
||||
int block = bins.size();
|
||||
|
||||
Plan *p = get_plan(block);
|
||||
fftwf_plan m_plan = p->crev_;
|
||||
Plan *p = FT8Plans::GetInstance()->getPlan(block);
|
||||
fftwf_plan plan = p->crev_;
|
||||
|
||||
fftwf_complex *m_in = (fftwf_complex *)fftwf_malloc(block * sizeof(fftwf_complex));
|
||||
fftwf_complex *m_out = (fftwf_complex *)fftwf_malloc(block * sizeof(fftwf_complex));
|
||||
@ -366,7 +293,7 @@ std::vector<std::complex<float>> FFTEngine::one_ifft_cc(
|
||||
m_in[bi][1] = im;
|
||||
}
|
||||
|
||||
fftwf_execute_dft(m_plan, m_in, m_out);
|
||||
fftwf_execute_dft(plan, m_in, m_out);
|
||||
|
||||
std::vector<std::complex<float>> out(block);
|
||||
float norm = 1.0 / sqrt(block);
|
||||
@ -390,8 +317,8 @@ std::vector<float> FFTEngine::one_ifft(const std::vector<std::complex<float>> &b
|
||||
int nbins = bins.size();
|
||||
int block = (nbins - 1) * 2;
|
||||
|
||||
Plan *p = get_plan(block);
|
||||
fftwf_plan m_plan = p->rev_;
|
||||
Plan *p = FT8Plans::GetInstance()->getPlan(block);
|
||||
fftwf_plan plan = p->rev_;
|
||||
|
||||
fftwf_complex *m_in = (fftwf_complex *)fftwf_malloc(sizeof(fftwf_complex) * ((p->n_ / 2) + 1));
|
||||
float *m_out = (float *)fftwf_malloc(sizeof(float) * p->n_);
|
||||
@ -404,7 +331,7 @@ std::vector<float> FFTEngine::one_ifft(const std::vector<std::complex<float>> &b
|
||||
m_in[bi][1] = im;
|
||||
}
|
||||
|
||||
fftwf_execute_dft_c2r(m_plan, m_in, m_out);
|
||||
fftwf_execute_dft_c2r(plan, m_in, m_out);
|
||||
|
||||
std::vector<float> out(block);
|
||||
for (int i = 0; i < block; i++)
|
||||
|
48
ft8/fft.h
48
ft8/fft.h
@ -25,7 +25,6 @@
|
||||
#include <QMutex>
|
||||
#include <vector>
|
||||
#include <complex>
|
||||
#include <fftw3.h>
|
||||
|
||||
#include "export.h"
|
||||
|
||||
@ -34,39 +33,7 @@ namespace FT8
|
||||
class FT8_API FFTEngine
|
||||
{
|
||||
public:
|
||||
// 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
|
||||
|
||||
//
|
||||
// complex -> complex
|
||||
//
|
||||
fftwf_complex *cc1_; // n
|
||||
fftwf_complex *cc2_; // n
|
||||
fftwf_plan cfwd_; // forward plan
|
||||
fftwf_plan crev_; // reverse plan
|
||||
}; // Plan
|
||||
|
||||
FFTEngine(FFTEngine& other) = delete;
|
||||
void operator=(const FFTEngine &) = delete;
|
||||
static FFTEngine *GetInstance();
|
||||
|
||||
Plan *get_plan(int n);
|
||||
|
||||
std::vector<std::complex<float>> one_fft(const std::vector<float> &samples, int i0, int block, Plan *p);
|
||||
std::vector<std::complex<float>> one_fft(const std::vector<float> &samples, int i0, int block);
|
||||
std::vector<float> one_ifft(const std::vector<std::complex<float>> &bins);
|
||||
typedef std::vector<std::vector<std::complex<float>>> ffts_t;
|
||||
ffts_t ffts(const std::vector<float> &samples, int i0, int block);
|
||||
@ -75,20 +42,11 @@ public:
|
||||
std::vector<std::complex<float>> one_ifft_cc(const std::vector<std::complex<float>> &bins);
|
||||
std::vector<float> hilbert_shift(const std::vector<float> &x, float hz0, float hz1, int rate);
|
||||
|
||||
protected:
|
||||
FFTEngine() :
|
||||
m_nplans(0)
|
||||
{}
|
||||
static FFTEngine *m_instance;
|
||||
FFTEngine();
|
||||
~FFTEngine();
|
||||
|
||||
private:
|
||||
std::vector<std::complex<float>> analytic(const std::vector<float> &x);
|
||||
QMutex m_plansmu;
|
||||
QMutex m_plansmu2;
|
||||
Plan *m_plans[1000];
|
||||
int m_nplans;
|
||||
// MEASURE=0, ESTIMATE=64, PATIENT=32
|
||||
static const int M_FFTW_TYPE = FFTW_ESTIMATE;
|
||||
}; // FFTEngine
|
||||
|
||||
} // namespace FT8
|
||||
|
28
ft8/ft8.cpp
28
ft8/ft8.cpp
@ -352,7 +352,6 @@ FT8::FT8(
|
||||
hack_off_ = -1;
|
||||
hack_len_ = -1;
|
||||
|
||||
plan32_ = nullptr;
|
||||
fftEngine_ = fftEngine;
|
||||
npasses_ = 1;
|
||||
}
|
||||
@ -585,7 +584,7 @@ std::vector<float> FT8::reduce_rate(
|
||||
}
|
||||
|
||||
int alen = a.size();
|
||||
std::vector<std::complex<float>> bins1 = fftEngine_->one_fft(a, 0, alen, 0);
|
||||
std::vector<std::complex<float>> bins1 = fftEngine_->one_fft(a, 0, alen);
|
||||
int nbins1 = bins1.size();
|
||||
float bin_hz = arate / (float)alen;
|
||||
|
||||
@ -643,9 +642,6 @@ std::vector<float> FT8::reduce_rate(
|
||||
|
||||
void FT8::go(int npasses)
|
||||
{
|
||||
// cache to avoid cost of fftw planner mutex.
|
||||
plan32_ = fftEngine_->get_plan(32);
|
||||
|
||||
if (0)
|
||||
{
|
||||
fprintf(stderr, "go: %.0f .. %.0f, %.0f, rate=%d\n",
|
||||
@ -836,7 +832,7 @@ 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 = fftEngine_->one_fft(
|
||||
samples_, 0, samples_.size(), 0);
|
||||
samples_, 0, samples_.size());
|
||||
|
||||
for (int hz_frac_i = 0; hz_frac_i < params.coarse_hz_n; hz_frac_i++)
|
||||
{
|
||||
@ -931,7 +927,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 = fftEngine_->one_fft(samples200, off + (si + start) * 32, 32, plan32_);
|
||||
auto fft = fftEngine_->one_fft(samples200, off + (si + start) * 32, 32);
|
||||
for (int bi = 0; bi < 8; bi++)
|
||||
{
|
||||
float x = std::abs(fft[bin0 + bi]);
|
||||
@ -1008,7 +1004,7 @@ float FT8::one_strength_known(
|
||||
|
||||
for (int si = 0; si < 79; si += params.known_sparse)
|
||||
{
|
||||
auto fft = fftEngine_->one_fft(samples, off + si * block, block, 0);
|
||||
auto fft = fftEngine_->one_fft(samples, off + si * block, block);
|
||||
|
||||
if (params.known_strength_how == 7)
|
||||
{
|
||||
@ -1231,7 +1227,7 @@ void FT8::search_both_known(
|
||||
int best_off = 0;
|
||||
float best_strength = 0;
|
||||
|
||||
std::vector<std::complex<float>> bins = fftEngine_->one_fft(samples, 0, samples.size(), 0);
|
||||
std::vector<std::complex<float>> bins = fftEngine_->one_fft(samples, 0, samples.size());
|
||||
|
||||
float hz_start, hz_inc, hz_end;
|
||||
if (params.third_hz_n > 1)
|
||||
@ -1295,7 +1291,7 @@ std::vector<float> FT8::fft_shift(
|
||||
}
|
||||
else
|
||||
{
|
||||
bins = fftEngine_->one_fft(samples, off, len, 0);
|
||||
bins = fftEngine_->one_fft(samples, off, len);
|
||||
hack_bins_ = bins;
|
||||
hack_size_ = samples.size();
|
||||
hack_off_ = off;
|
||||
@ -2613,7 +2609,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 = fftEngine_->one_fft(samples, 0, len, 0);
|
||||
std::vector<std::complex<float>> bins = fftEngine_->one_fft(samples, 0, len);
|
||||
|
||||
return down_v7_f(bins, len, hz);
|
||||
}
|
||||
@ -3452,6 +3448,10 @@ std::vector<int> FT8::recode(int a174[])
|
||||
FT8Decoder::~FT8Decoder()
|
||||
{
|
||||
forceQuit(); // stop all remaining running threads if any
|
||||
|
||||
for (auto& fftEngine : fftEngines) {
|
||||
delete fftEngine;
|
||||
}
|
||||
}
|
||||
|
||||
//
|
||||
@ -3517,6 +3517,10 @@ void FT8Decoder::entry(
|
||||
hz0 = std::max(hz0, 0.0f);
|
||||
hz1 = std::min(hz1, (rate / 2.0f) - 50);
|
||||
|
||||
if (i == (int) fftEngines.size()) {
|
||||
fftEngines.push_back(new FFTEngine());
|
||||
}
|
||||
|
||||
FT8 *ft8 = new FT8(
|
||||
samples,
|
||||
hz0,
|
||||
@ -3529,7 +3533,7 @@ void FT8Decoder::entry(
|
||||
final_deadline,
|
||||
cb,
|
||||
prevdecs,
|
||||
FFTEngine::GetInstance()
|
||||
fftEngines[i]
|
||||
);
|
||||
ft8->getParams() = getParams(); // transfer parameters
|
||||
|
||||
|
@ -299,8 +299,6 @@ public:
|
||||
std::vector<std::complex<float>> hack_bins_;
|
||||
std::vector<cdecode> prevdecs_;
|
||||
|
||||
FFTEngine::Plan *plan32_;
|
||||
|
||||
FT8(
|
||||
const std::vector<float> &samples,
|
||||
float min_hz,
|
||||
@ -688,6 +686,7 @@ public:
|
||||
private:
|
||||
FT8Params params;
|
||||
std::vector<QThread*> threads;
|
||||
std::vector<FFTEngine*> fftEngines;
|
||||
}; // FT8Decoder
|
||||
|
||||
} // namespace FT8
|
||||
|
65
ft8/ft8plan.cpp
Normal file
65
ft8/ft8plan.cpp
Normal file
@ -0,0 +1,65 @@
|
||||
//////////////////////////////////////////////////////////////////////////////////
|
||||
// Copyright (C) 2023 Edouard Griffiths, F4EXB. //
|
||||
// //
|
||||
// This is the code from ft8mon: https://github.com/rtmrtmrtmrtm/ft8mon //
|
||||
// written by Robert Morris, AB1HL //
|
||||
// reformatted and adapted to Qt and SDRangel context //
|
||||
// //
|
||||
// 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/>. //
|
||||
///////////////////////////////////////////////////////////////////////////////////
|
||||
#include "ft8plan.h"
|
||||
|
||||
namespace FT8
|
||||
{
|
||||
|
||||
Plan::Plan(int n)
|
||||
{
|
||||
n_ = n;
|
||||
|
||||
r_ = (float *) fftwf_malloc(n * sizeof(float));
|
||||
c_ = (fftwf_complex *) fftwf_malloc(((n / 2) + 1) * sizeof(fftwf_complex));
|
||||
cc1_ = (fftwf_complex *)fftwf_malloc(n * sizeof(fftwf_complex));
|
||||
cc2_ = (fftwf_complex *)fftwf_malloc(n * sizeof(fftwf_complex));
|
||||
//
|
||||
// real -> complex
|
||||
//
|
||||
// FFTW_ESTIMATE
|
||||
// FFTW_MEASURE
|
||||
// FFTW_PATIENT
|
||||
// FFTW_EXHAUSTIVE
|
||||
int type = M_FFTW_TYPE;
|
||||
type_ = type;
|
||||
fwd_ = fftwf_plan_dft_r2c_1d(n, r_, c_, type);
|
||||
rev_ = fftwf_plan_dft_c2r_1d(n, c_, r_, type);
|
||||
|
||||
//
|
||||
// complex -> complex
|
||||
//
|
||||
cfwd_ = fftwf_plan_dft_1d(n, cc1_, cc2_, FFTW_FORWARD, type);
|
||||
crev_ = fftwf_plan_dft_1d(n, cc2_, cc1_, FFTW_BACKWARD, type);
|
||||
}
|
||||
|
||||
Plan::~Plan()
|
||||
{
|
||||
fftwf_destroy_plan(fwd_);
|
||||
fftwf_destroy_plan(rev_);
|
||||
fftwf_destroy_plan(cfwd_);
|
||||
fftwf_destroy_plan(crev_);
|
||||
fftwf_free(r_);
|
||||
fftwf_free(c_);
|
||||
fftwf_free(cc1_);
|
||||
fftwf_free(cc2_);
|
||||
}
|
||||
|
||||
} // namesoace FT8
|
60
ft8/ft8plan.h
Normal file
60
ft8/ft8plan.h
Normal file
@ -0,0 +1,60 @@
|
||||
///////////////////////////////////////////////////////////////////////////////////
|
||||
// Copyright (C) 2023 Edouard Griffiths, F4EXB. //
|
||||
// //
|
||||
// This is the code from ft8mon: https://github.com/rtmrtmrtmrtm/ft8mon //
|
||||
// written by Robert Morris, AB1HL //
|
||||
// reformatted and adapted to Qt and SDRangel context //
|
||||
// //
|
||||
// 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/>. //
|
||||
///////////////////////////////////////////////////////////////////////////////////
|
||||
#ifndef ft8plan_h
|
||||
#define ft8plan_h
|
||||
|
||||
#include <fftw3.h>
|
||||
#include <QMutex>
|
||||
|
||||
namespace FT8
|
||||
{
|
||||
|
||||
class Plan
|
||||
{
|
||||
public:
|
||||
Plan(int n);
|
||||
~Plan();
|
||||
|
||||
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
|
||||
|
||||
//
|
||||
// complex -> complex
|
||||
//
|
||||
fftwf_complex *cc1_; // n
|
||||
fftwf_complex *cc2_; // n
|
||||
fftwf_plan cfwd_; // forward plan
|
||||
fftwf_plan crev_; // reverse plan
|
||||
// MEASURE=0, ESTIMATE=64, PATIENT=32
|
||||
static const int M_FFTW_TYPE = FFTW_ESTIMATE;
|
||||
}; // Plan
|
||||
|
||||
}
|
||||
|
||||
#endif
|
64
ft8/ft8plans.cpp
Normal file
64
ft8/ft8plans.cpp
Normal file
@ -0,0 +1,64 @@
|
||||
///////////////////////////////////////////////////////////////////////////////////
|
||||
// Copyright (C) 2023 Edouard Griffiths, F4EXB. //
|
||||
// //
|
||||
// This is the code from ft8mon: https://github.com/rtmrtmrtmrtm/ft8mon //
|
||||
// written by Robert Morris, AB1HL //
|
||||
// reformatted and adapted to Qt and SDRangel context //
|
||||
// //
|
||||
// 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/>. //
|
||||
///////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
#include <QMutexLocker>
|
||||
|
||||
#include "ft8plan.h"
|
||||
#include "ft8plans.h"
|
||||
|
||||
namespace FT8
|
||||
{
|
||||
|
||||
FT8Plans* FT8Plans::m_instance= nullptr;
|
||||
QMutex FT8Plans::m_globalPlanMutex;
|
||||
|
||||
FT8Plans::FT8Plans()
|
||||
{}
|
||||
|
||||
FT8Plans *FT8Plans::GetInstance()
|
||||
{
|
||||
if (!m_instance) {
|
||||
m_instance = new FT8Plans();
|
||||
}
|
||||
|
||||
return m_instance;
|
||||
}
|
||||
|
||||
Plan *FT8Plans::getPlan(int n)
|
||||
{
|
||||
QMutexLocker mlock(&m_globalPlanMutex);
|
||||
|
||||
for (auto& plan : m_plans)
|
||||
{
|
||||
if ((plan->n_ == n) && (plan->type_ == Plan::M_FFTW_TYPE)) {
|
||||
return plan;
|
||||
}
|
||||
}
|
||||
|
||||
fftwf_set_timelimit(5);
|
||||
|
||||
Plan *p = new Plan(n);
|
||||
m_plans.push_back(p);
|
||||
|
||||
return p;
|
||||
}
|
||||
|
||||
}
|
54
ft8/ft8plans.h
Normal file
54
ft8/ft8plans.h
Normal file
@ -0,0 +1,54 @@
|
||||
///////////////////////////////////////////////////////////////////////////////////
|
||||
// Copyright (C) 2023 Edouard Griffiths, F4EXB. //
|
||||
// //
|
||||
// This is the code from ft8mon: https://github.com/rtmrtmrtmrtm/ft8mon //
|
||||
// written by Robert Morris, AB1HL //
|
||||
// reformatted and adapted to Qt and SDRangel context //
|
||||
// //
|
||||
// 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/>. //
|
||||
///////////////////////////////////////////////////////////////////////////////////
|
||||
#ifndef ft8plans_h
|
||||
#define ft8plans_h
|
||||
|
||||
#include <vector>
|
||||
#include <QMutex>
|
||||
|
||||
#include "export.h"
|
||||
|
||||
namespace FT8
|
||||
{
|
||||
|
||||
class Plan;
|
||||
|
||||
class FT8_API FT8Plans
|
||||
{
|
||||
public:
|
||||
FT8Plans(FT8Plans& other) = delete;
|
||||
void operator=(const FT8Plans &) = delete;
|
||||
static FT8Plans *GetInstance();
|
||||
Plan *getPlan(int n);
|
||||
|
||||
protected:
|
||||
FT8Plans();
|
||||
static FT8Plans *m_instance;
|
||||
|
||||
private:
|
||||
std::vector<Plan*> m_plans;
|
||||
static QMutex m_globalPlanMutex;
|
||||
|
||||
};
|
||||
|
||||
} // namespace FT8
|
||||
|
||||
#endif // ft8plans_h
|
Loading…
Reference in New Issue
Block a user