diff --git a/CMakeLists.txt b/CMakeLists.txt index 541fc7c8a..ebf9da713 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -780,10 +780,7 @@ add_subdirectory(devices) add_subdirectory(sdrbench) add_subdirectory(modemm17) - -if (LINUX) - add_subdirectory(ft8) -endif() +add_subdirectory(ft8) if (BUILD_GUI) add_subdirectory(sdrgui) diff --git a/ft8/ft8.cpp b/ft8/ft8.cpp index 6dbc63d3e..9683c9469 100644 --- a/ft8/ft8.cpp +++ b/ft8/ft8.cpp @@ -33,15 +33,13 @@ #include #include #include -#include #include #include #include #include #include -#include -#include -// #include + +#include #include "util.h" #include "ft8.h" @@ -356,14 +354,21 @@ FT8::FT8( plan32_ = nullptr; fftEngine_ = fftEngine; + npasses_ = 1; } FT8::~FT8() { } - // strength of costas block of signal with tone 0 at bi0, - // and symbol zero at si0. +void FT8::start_work() +{ + go(npasses_); + emit finished(); +} + +// strength of costas block of signal with tone 0 at bi0, +// and symbol zero at si0. float FT8::one_coarse_strength(const FFTEngine::ffts_t &bins, int bi0, int si0) { int costas[] = {3, 1, 4, 0, 6, 5, 2}; @@ -3448,8 +3453,13 @@ std::vector FT8::recode(int a174[]) return out79; } +FT8Decoder::~FT8Decoder() +{ + forceQuit(); // stop all remaining running threads if any +} + // -// Python calls these. +// Launch decoding // void FT8Decoder::entry( float xsamples[], @@ -3470,7 +3480,6 @@ 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 prevdecs; @@ -3494,7 +3503,6 @@ void FT8Decoder::entry( } float per = (max_hz - min_hz) / params.nthreads; - std::vector> thv; for (int i = 0; i < params.nthreads; i++) { @@ -3530,15 +3538,45 @@ void FT8Decoder::entry( ft8->getParams() = getParams(); // transfer parameters int npasses = nprevdecs > 0 ? params.npasses_two : params.npasses_one; - std::thread *th = new std::thread([ft8, npasses] () { ft8->go(npasses); }); - thv.push_back(std::pair(ft8, th)); + ft8->set_npasses(npasses); + QThread *th = new QThread(); + threads.push_back(th); + // std::thread *th = new std::thread([ft8, npasses] () { ft8->go(npasses); }); + // thv.push_back(std::pair(ft8, th)); + ft8->moveToThread(th); + QObject::connect(th, &QThread::started, ft8, &FT8::start_work); + QObject::connect(ft8, &FT8::finished, th, &QThread::quit, Qt::DirectConnection); + QObject::connect(th, &QThread::finished, ft8, &QObject::deleteLater); + QObject::connect(th, &QThread::finished, th, &QThread::deleteLater); + th->start(); } +} - for (int i = 0; i < (int)thv.size(); i++) +void FT8Decoder::wait(double time_left) +{ + unsigned long thread_timeout = time_left * 1000; + + while (threads.size() != 0) { - thv[i].second->join(); - delete thv[i].second; - delete thv[i].first; + bool success = threads.front()->wait(thread_timeout); + + if (!success) + { + qDebug("FT8::FT8Decoder::wait: thread timed out"); + thread_timeout = 50; // only 50ms for the rest + } + + threads.erase(threads.begin()); + } +} + +void FT8Decoder::forceQuit() +{ + while (threads.size() != 0) + { + threads.front()->quit(); + threads.front()->wait(); + threads.erase(threads.begin()); } } diff --git a/ft8/ft8.h b/ft8/ft8.h index 851b52999..5ab3f5ad0 100644 --- a/ft8/ft8.h +++ b/ft8/ft8.h @@ -21,11 +21,16 @@ #ifndef ft8_h #define ft8_h -#include -#include "fft.h" +#include +#include +#include + +#include "fft.h" #include "export.h" +class QThread; + namespace FT8 { // Callback interface to get the results class FT8_API CallbackInterface @@ -261,8 +266,9 @@ struct FT8_API FT8Params }; // class FT8Params // The FT8 worker -class FT8_API FT8 +class FT8_API FT8 : public QObject { + Q_OBJECT public: float min_hz_; float max_hz_; @@ -308,6 +314,10 @@ public: FFTEngine *fftEngine ); ~FT8(); + // Number of passes + void set_npasses(int npasses) { npasses_ = npasses; } + // Start the worker + void start_work(); // strength of costas block of signal with tone 0 at bi0, // and symbol zero at si0. float one_coarse_strength(const FFTEngine::ffts_t &bins, int bi0, int si0); @@ -642,14 +652,19 @@ public: ); FT8Params& getParams() { return params; } +signals: + void finished(); private: FT8Params params; FFTEngine *fftEngine_; + int npasses_; static const double apriori174[]; }; // class FT8 -class FT8_API FT8Decoder { +class FT8_API FT8Decoder : public QObject { + Q_OBJECT public: + ~FT8Decoder(); void entry( float xsamples[], int nsamples, @@ -665,9 +680,13 @@ public: int, struct cdecode * ); + void wait(double time_left); //!< wait for all threads to finish + void forceQuit(); //!< force quit all threads FT8Params& getParams() { return params; } private: + FFTEngine fftEngine; FT8Params params; + std::vector threads; }; // FT8Decoder } // namespace FT8 diff --git a/sdrbench/mainbench.cpp b/sdrbench/mainbench.cpp index efd9452e8..2de52a416 100644 --- a/sdrbench/mainbench.cpp +++ b/sdrbench/mainbench.cpp @@ -47,7 +47,8 @@ void MainBench::run() << " nsamples: " << m_parser.getNbSamples() << " repet: " << m_parser.getRepetition() << " log2f: " << m_parser.getLog2Factor() - << " file: " << m_parser.getFileName(); + << " file: " << m_parser.getFileName() + << " args: " << m_parser.getArgsStr(); if (m_parser.getTestType() == ParserBench::TestDecimatorsII) { testDecimateII(); @@ -64,7 +65,7 @@ void MainBench::run() } else if (m_parser.getTestType() == ParserBench::TestGolay2312) { testGolay2312(); } else if (m_parser.getTestType() == ParserBench::TestFT8) { - testFT8(m_parser.getFileName()); + testFT8(m_parser.getFileName(), m_parser.getArgsStr()); } else { qDebug() << "MainBench::run: unknown test type: " << m_parser.getTestType(); } diff --git a/sdrbench/mainbench.h b/sdrbench/mainbench.h index 200082217..11d34eca0 100644 --- a/sdrbench/mainbench.h +++ b/sdrbench/mainbench.h @@ -54,7 +54,7 @@ private: void testDecimateFI(); void testDecimateFF(); void testGolay2312(); - void testFT8(const QString& wavFile); //!< use with sdrbench/samples/ft8/230105_091630.wav in -f option + void testFT8(const QString& wavFile, const QString& argsStr); //!< use with sdrbench/samples/ft8/230105_091630.wav in -f option void decimateII(const qint16 *buf, int len); void decimateInfII(const qint16 *buf, int len); void decimateSupII(const qint16 *buf, int len); diff --git a/sdrbench/parserbench.cpp b/sdrbench/parserbench.cpp index a5a10b487..b789418e4 100644 --- a/sdrbench/parserbench.cpp +++ b/sdrbench/parserbench.cpp @@ -42,6 +42,10 @@ ParserBench::ParserBench() : m_fileOption(QStringList() << "f" << "file", "File to be used for the test.", "file", + ""), + m_argsOption(QStringList() << "a" << "args", + "Custom arguments string to be used for the test.", + "args", "") { m_testStr = "decimateii"; @@ -58,6 +62,7 @@ ParserBench::ParserBench() : m_parser.addOption(m_repetitionOption); m_parser.addOption(m_log2FactorOption); m_parser.addOption(m_fileOption); + m_parser.addOption(m_argsOption); } ParserBench::~ParserBench() @@ -120,6 +125,10 @@ void ParserBench::parse(const QCoreApplication& app) // file m_fileName = m_parser.value(m_fileOption); + + // custom args + + m_argsStr = m_parser.value(m_argsOption); } ParserBench::TestType ParserBench::getTestType() const diff --git a/sdrbench/parserbench.h b/sdrbench/parserbench.h index 0f13f9a9a..f98abe3d9 100644 --- a/sdrbench/parserbench.h +++ b/sdrbench/parserbench.h @@ -50,6 +50,7 @@ public: uint32_t getRepetition() const { return m_repetition; } uint32_t getLog2Factor() const { return m_log2Factor; } const QString& getFileName() const { return m_fileName; } + const QString& getArgsStr() const { return m_argsStr; } private: QString m_testStr; @@ -57,6 +58,7 @@ private: uint32_t m_repetition; uint32_t m_log2Factor; QString m_fileName; + QString m_argsStr; QCommandLineParser m_parser; QCommandLineOption m_testOption; @@ -64,6 +66,7 @@ private: QCommandLineOption m_repetitionOption; QCommandLineOption m_log2FactorOption; QCommandLineOption m_fileOption; + QCommandLineOption m_argsOption; }; diff --git a/sdrbench/test_ft8.cpp b/sdrbench/test_ft8.cpp index f702393af..f326c1231 100644 --- a/sdrbench/test_ft8.cpp +++ b/sdrbench/test_ft8.cpp @@ -102,11 +102,40 @@ int TestFT8Callback::hcb( return 2; // 2 => new decode, do subtract. } -void MainBench::testFT8(const QString& wavFile) +void MainBench::testFT8(const QString& wavFile, const QString& argsStr) { - qDebug("MainBench::testFT8: start"); + int nthreads = 8; // number of threads (default) + double budget = 2.5; // compute for this many seconds per cycle (default) + // 3,0.5 combinaion may be enough + + QStringList argElements = argsStr.split(','); // comma separated list of arguments + + for (int i = 0; i < argElements.size(); i++) + { + const QString& argStr = argElements.at(i); + bool ok; + + if (i == 0) // first is the number of threads (integer) + { + int nthreads_x = argStr.toInt(&ok); + + if (ok) { + nthreads = nthreads_x; + } + } + + if (i == 1) // second is the time budget in seconds (double) + { + double budget_x = argStr.toDouble(&ok); + + if (ok) { + budget = budget_x; + } + } + } + + qDebug("MainBench::testFT8: start nthreads: %d budget: %fs", nthreads, budget); int hints[2] = { 2, 0 }; // CQ - double budget = 2.5; // compute for this many seconds per cycle TestFT8Callback testft8Callback; std::ifstream wfile; @@ -169,6 +198,7 @@ void MainBench::testFT8(const QString& wavFile) wfile.close(); FT8::FT8Decoder decoder; + decoder.getParams().nthreads = nthreads; decoder.entry( samples.data(), @@ -185,8 +215,10 @@ void MainBench::testFT8(const QString& wavFile) 0, (struct FT8::cdecode *) nullptr ); - qDebug("MainBench::testFT8: done"); + + decoder.wait(budget + 1.0); // add one second to budget to force quit threads const std::map& msgMap = testft8Callback.getMsgMap(); + qDebug("MainBench::testFT8: done %lu decodes", msgMap.size()); if (msgMap.size() != 15) {