Skip to content

Commit

Permalink
FT8 demod: adapted to QThread
Browse files Browse the repository at this point in the history
  • Loading branch information
f4exb committed Jan 12, 2023
1 parent aaab8b5 commit 6ecd110
Show file tree
Hide file tree
Showing 8 changed files with 128 additions and 29 deletions.
5 changes: 1 addition & 4 deletions CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand Down
68 changes: 53 additions & 15 deletions ft8/ft8.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -33,15 +33,13 @@
#include <math.h>
#include <complex>
#include <fftw3.h>
#include <vector>
#include <algorithm>
#include <complex>
#include <random>
#include <functional>
#include <map>
#include <utility>
#include <thread>
// #include <QDebug>

#include <QThread>

#include "util.h"
#include "ft8.h"
Expand Down Expand Up @@ -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};
Expand Down Expand Up @@ -3448,8 +3453,13 @@ std::vector<int> 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[],
Expand All @@ -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<cdecode> prevdecs;
Expand All @@ -3494,7 +3503,6 @@ void FT8Decoder::entry(
}

float per = (max_hz - min_hz) / params.nthreads;
std::vector<std::pair<FT8*, std::thread*>> thv;

for (int i = 0; i < params.nthreads; i++)
{
Expand Down Expand Up @@ -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*, std::thread*>(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*, std::thread*>(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();
}
}

void FT8Decoder::wait(double time_left)
{
unsigned long thread_timeout = time_left * 1000;

for (int i = 0; i < (int)thv.size(); i++)
while (threads.size() != 0)
{
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)
{
thv[i].second->join();
delete thv[i].second;
delete thv[i].first;
threads.front()->quit();
threads.front()->wait();
threads.erase(threads.begin());
}
}

Expand Down
25 changes: 22 additions & 3 deletions ft8/ft8.h
Original file line number Diff line number Diff line change
Expand Up @@ -21,11 +21,16 @@
#ifndef ft8_h
#define ft8_h

#include <vector>

#include <QObject>
#include <QMutex>
#include "fft.h"

#include "fft.h"
#include "export.h"

class QThread;

namespace FT8 {
// Callback interface to get the results
class FT8_API CallbackInterface
Expand Down Expand Up @@ -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_;
Expand Down Expand Up @@ -308,6 +314,10 @@ class FT8_API FT8
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);
Expand Down Expand Up @@ -642,14 +652,19 @@ class FT8_API FT8
);

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,
Expand All @@ -665,9 +680,13 @@ class FT8_API FT8Decoder {
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<QThread*> threads;
}; // FT8Decoder

} // namespace FT8
Expand Down
5 changes: 3 additions & 2 deletions sdrbench/mainbench.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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();
Expand All @@ -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();
}
Expand Down
2 changes: 1 addition & 1 deletion sdrbench/mainbench.h
Original file line number Diff line number Diff line change
Expand Up @@ -54,7 +54,7 @@ public slots:
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);
Expand Down
9 changes: 9 additions & 0 deletions sdrbench/parserbench.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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";
Expand All @@ -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()
Expand Down Expand Up @@ -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
Expand Down
3 changes: 3 additions & 0 deletions sdrbench/parserbench.h
Original file line number Diff line number Diff line change
Expand Up @@ -50,20 +50,23 @@ class SDRBENCH_API ParserBench
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;
uint32_t m_nbSamples;
uint32_t m_repetition;
uint32_t m_log2Factor;
QString m_fileName;
QString m_argsStr;

QCommandLineParser m_parser;
QCommandLineOption m_testOption;
QCommandLineOption m_nbSamplesOption;
QCommandLineOption m_repetitionOption;
QCommandLineOption m_log2FactorOption;
QCommandLineOption m_fileOption;
QCommandLineOption m_argsOption;
};


Expand Down
40 changes: 36 additions & 4 deletions sdrbench/test_ft8.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -169,6 +198,7 @@ void MainBench::testFT8(const QString& wavFile)
wfile.close();

FT8::FT8Decoder decoder;
decoder.getParams().nthreads = nthreads;

decoder.entry(
samples.data(),
Expand All @@ -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<std::string, bool>& msgMap = testft8Callback.getMsgMap();
qDebug("MainBench::testFT8: done %lu decodes", msgMap.size());

if (msgMap.size() != 15)
{
Expand Down

0 comments on commit 6ecd110

Please sign in to comment.