diff --git a/ft8/CMakeLists.txt b/ft8/CMakeLists.txt index 494750f51..7faf9e5ea 100644 --- a/ft8/CMakeLists.txt +++ b/ft8/CMakeLists.txt @@ -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 diff --git a/ft8/fft.cpp b/ft8/fft.cpp index 083e3fbcc..282d5ea6f 100644 --- a/ft8/fft.cpp +++ b/ft8/fft.cpp @@ -20,91 +20,20 @@ /////////////////////////////////////////////////////////////////////////////////// // #include +#include #include #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> FFTEngine::one_fft( const std::vector &samples, int i0, - int block, - FFTEngine::Plan *p + int block ) { // assert(i0 >= 0); @@ -123,12 +51,9 @@ std::vector> 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> 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> out(nbins); @@ -168,8 +93,10 @@ std::vector> FFTEngine::one_fft( out[bi] = std::complex(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 &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 &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> 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> 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> out(block); float norm = 1.0 / sqrt(block); @@ -304,8 +231,8 @@ std::vector> 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> FFTEngine::one_fft_cc( } } - fftwf_execute_dft(m_plan, m_in, m_out); + fftwf_execute_dft(plan, m_in, m_out); std::vector> out(block); @@ -351,8 +278,8 @@ std::vector> 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> 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> out(block); float norm = 1.0 / sqrt(block); @@ -390,8 +317,8 @@ std::vector FFTEngine::one_ifft(const std::vector> &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 FFTEngine::one_ifft(const std::vector> &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 out(block); for (int i = 0; i < block; i++) diff --git a/ft8/fft.h b/ft8/fft.h index c0c462170..a7ad5ec79 100644 --- a/ft8/fft.h +++ b/ft8/fft.h @@ -25,7 +25,6 @@ #include #include #include -#include #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> one_fft(const std::vector &samples, int i0, int block, Plan *p); + std::vector> one_fft(const std::vector &samples, int i0, int block); std::vector one_ifft(const std::vector> &bins); typedef std::vector>> ffts_t; ffts_t ffts(const std::vector &samples, int i0, int block); @@ -75,20 +42,11 @@ public: std::vector> one_ifft_cc(const std::vector> &bins); std::vector hilbert_shift(const std::vector &x, float hz0, float hz1, int rate); -protected: - FFTEngine() : - m_nplans(0) - {} - static FFTEngine *m_instance; + FFTEngine(); + ~FFTEngine(); private: std::vector> analytic(const std::vector &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 diff --git a/ft8/ft8.cpp b/ft8/ft8.cpp index ec3bcf58a..216989993 100644 --- a/ft8/ft8.cpp +++ b/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 FT8::reduce_rate( } int alen = a.size(); - std::vector> bins1 = fftEngine_->one_fft(a, 0, alen, 0); + std::vector> bins1 = fftEngine_->one_fft(a, 0, alen); int nbins1 = bins1.size(); float bin_hz = arate / (float)alen; @@ -643,9 +642,6 @@ std::vector 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> 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 &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> bins = fftEngine_->one_fft(samples, 0, samples.size(), 0); + std::vector> 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 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> FT8::fbandpass( std::vector FT8::down_v7(const std::vector &samples, float hz) { int len = samples.size(); - std::vector> bins = fftEngine_->one_fft(samples, 0, len, 0); + std::vector> bins = fftEngine_->one_fft(samples, 0, len); return down_v7_f(bins, len, hz); } @@ -3452,6 +3448,10 @@ std::vector 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 diff --git a/ft8/ft8.h b/ft8/ft8.h index 79cfeddfe..047ce6afb 100644 --- a/ft8/ft8.h +++ b/ft8/ft8.h @@ -299,8 +299,6 @@ public: std::vector> hack_bins_; std::vector prevdecs_; - FFTEngine::Plan *plan32_; - FT8( const std::vector &samples, float min_hz, @@ -688,6 +686,7 @@ public: private: FT8Params params; std::vector threads; + std::vector fftEngines; }; // FT8Decoder } // namespace FT8 diff --git a/ft8/ft8plan.cpp b/ft8/ft8plan.cpp new file mode 100644 index 000000000..7af1ada3c --- /dev/null +++ b/ft8/ft8plan.cpp @@ -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 . // +/////////////////////////////////////////////////////////////////////////////////// +#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 diff --git a/ft8/ft8plan.h b/ft8/ft8plan.h new file mode 100644 index 000000000..08be7bd28 --- /dev/null +++ b/ft8/ft8plan.h @@ -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 . // +/////////////////////////////////////////////////////////////////////////////////// +#ifndef ft8plan_h +#define ft8plan_h + +#include +#include + +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 diff --git a/ft8/ft8plans.cpp b/ft8/ft8plans.cpp new file mode 100644 index 000000000..4f22c790e --- /dev/null +++ b/ft8/ft8plans.cpp @@ -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 . // +/////////////////////////////////////////////////////////////////////////////////// + +#include + +#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; +} + +} diff --git a/ft8/ft8plans.h b/ft8/ft8plans.h new file mode 100644 index 000000000..d191c3a42 --- /dev/null +++ b/ft8/ft8plans.h @@ -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 . // +/////////////////////////////////////////////////////////////////////////////////// +#ifndef ft8plans_h +#define ft8plans_h + +#include +#include + +#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 m_plans; + static QMutex m_globalPlanMutex; + +}; + +} // namespace FT8 + +#endif // ft8plans_h