mirror of
https://github.com/f4exb/sdrangel.git
synced 2025-04-22 19:38:55 -04:00
FT8 demod: implemented decoder
This commit is contained in:
parent
0d77b37ec1
commit
b1cf15213c
ft8
plugins/channelrx/demodft8
CMakeLists.txtft8demod.cppft8demodbaseband.cppft8demodgui.cppft8demodgui.hft8demodgui.uift8demodsettings.cppft8demodsettings.hft8demodworker.cppft8demodworker.h
sdrbase
swagger/sdrangel
api/swagger/include
code
@ -37,4 +37,7 @@ target_link_libraries(ft8
|
||||
sdrbase
|
||||
)
|
||||
|
||||
# remove or comment when debugging is done
|
||||
# set_property(TARGET ft8 PROPERTY COMPILE_OPTIONS "$<$<CONFIG:Debug>:-Og>")
|
||||
|
||||
install(TARGETS ft8 DESTINATION ${INSTALL_LIB_DIR})
|
||||
|
109
ft8/unpack.cpp
109
ft8/unpack.cpp
@ -27,19 +27,17 @@
|
||||
|
||||
namespace FT8 {
|
||||
|
||||
int Packing::ihashcall(std::string call, int m)
|
||||
int Packing::ihashcall(std::string rawcall, int m)
|
||||
{
|
||||
while (call.size() > 0 && call[0] == ' ')
|
||||
call.erase(0, 1);
|
||||
while (call.size() > 0 && call[call.size() - 1] == ' ')
|
||||
call.erase(call.end() - 1);
|
||||
|
||||
std::string call = trim(rawcall);
|
||||
const char *chars = " 0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ/";
|
||||
|
||||
while (call.size() < 11)
|
||||
while (call.size() < 11) {
|
||||
call += " ";
|
||||
}
|
||||
|
||||
unsigned long long x = 0;
|
||||
|
||||
for (int i = 0; i < 11; i++)
|
||||
{
|
||||
int c = call[i];
|
||||
@ -48,6 +46,7 @@ int Packing::ihashcall(std::string call, int m)
|
||||
int j = p - chars;
|
||||
x = 38 * x + j;
|
||||
}
|
||||
|
||||
x = x * 47055833459LL;
|
||||
x = x >> (64 - m);
|
||||
|
||||
@ -66,17 +65,24 @@ std::string Packing::unpackcall(int x)
|
||||
const char *c3 = "0123456789";
|
||||
const char *c4 = " ABCDEFGHIJKLMNOPQRSTUVWXYZ";
|
||||
|
||||
if (x == 0)
|
||||
if (x == 0) {
|
||||
return "DE";
|
||||
if (x == 1)
|
||||
}
|
||||
|
||||
if (x == 1) {
|
||||
return "QRZ";
|
||||
if (x == 2)
|
||||
}
|
||||
|
||||
if (x == 2) {
|
||||
return "CQ";
|
||||
}
|
||||
|
||||
if (x <= 1002)
|
||||
{
|
||||
sprintf(tmp, "CQ %d", x - 3);
|
||||
return tmp;
|
||||
return std::string(tmp);
|
||||
}
|
||||
|
||||
if (x <= 532443)
|
||||
{
|
||||
x -= 1003;
|
||||
@ -88,11 +94,10 @@ std::string Packing::unpackcall(int x)
|
||||
x %= 27;
|
||||
int ci4 = x;
|
||||
sprintf(tmp, "CQ %c%c%c%c", c4[ci1], c4[ci2], c4[ci3], c4[ci4]);
|
||||
return tmp;
|
||||
return std::string(tmp);
|
||||
}
|
||||
|
||||
if (x < NTOKENS)
|
||||
{
|
||||
if (x < NTOKENS) {
|
||||
return "<TOKEN>";
|
||||
}
|
||||
|
||||
@ -103,14 +108,13 @@ std::string Packing::unpackcall(int x)
|
||||
// 22-bit hash...
|
||||
std::string s;
|
||||
hashes_mu.lock();
|
||||
if (hashes22.count(x) > 0)
|
||||
{
|
||||
|
||||
if (hashes22.count(x) > 0) {
|
||||
s = hashes22[x];
|
||||
}
|
||||
else
|
||||
{
|
||||
} else {
|
||||
s = "<...22>";
|
||||
}
|
||||
|
||||
hashes_mu.unlock();
|
||||
return s;
|
||||
}
|
||||
@ -133,7 +137,7 @@ std::string Packing::unpackcall(int x)
|
||||
|
||||
a[6] = '\0';
|
||||
|
||||
return a;
|
||||
return std::string(a);
|
||||
}
|
||||
|
||||
// unpack a 15-bit grid square &c.
|
||||
@ -172,44 +176,41 @@ std::string Packing::unpackgrid(int ng, int ir, int i3)
|
||||
|
||||
ng -= NGBASE;
|
||||
|
||||
if (ng == 1)
|
||||
{
|
||||
if (ng == 1) {
|
||||
return " "; // ???
|
||||
}
|
||||
if (ng == 2)
|
||||
{
|
||||
if (ng == 2) {
|
||||
return "RRR ";
|
||||
}
|
||||
if (ng == 3)
|
||||
{
|
||||
if (ng == 3) {
|
||||
return "RR73";
|
||||
}
|
||||
if (ng == 4)
|
||||
{
|
||||
if (ng == 4) {
|
||||
return "73 ";
|
||||
}
|
||||
|
||||
int db = ng - 35;
|
||||
char tmp[16];
|
||||
if (db >= 0)
|
||||
{
|
||||
|
||||
if (db >= 0) {
|
||||
sprintf(tmp, "%s+%02d", ir ? "R" : "", db);
|
||||
}
|
||||
else
|
||||
{
|
||||
} else {
|
||||
sprintf(tmp, "%s-%02d", ir ? "R" : "", 0 - db);
|
||||
}
|
||||
return tmp;
|
||||
|
||||
return std::string(tmp);
|
||||
}
|
||||
|
||||
void Packing::remember_call(std::string call)
|
||||
{
|
||||
hashes_mu.lock();
|
||||
|
||||
if (call.size() >= 3 && call[0] != '<')
|
||||
{
|
||||
hashes22[ihashcall(call, 22)] = call;
|
||||
hashes12[ihashcall(call, 12)] = call;
|
||||
}
|
||||
|
||||
hashes_mu.unlock();
|
||||
}
|
||||
|
||||
@ -236,11 +237,14 @@ std::string Packing::unpack_4(int a77[], std::string& call1str, std::string& cal
|
||||
}
|
||||
|
||||
call[11] = '\0';
|
||||
std::string callstr(call);
|
||||
|
||||
remember_call(call);
|
||||
remember_call(callstr);
|
||||
|
||||
if (un64(a77, 73, 1) == 1) {
|
||||
return std::string("CQ ") + call;
|
||||
if (un64(a77, 73, 1) == 1)
|
||||
{
|
||||
call1str = std::string("CQ ") + callstr;
|
||||
return call1str;
|
||||
}
|
||||
|
||||
int x12 = un64(a77, 0, 12);
|
||||
@ -255,7 +259,6 @@ std::string Packing::unpack_4(int a77[], std::string& call1str, std::string& cal
|
||||
}
|
||||
|
||||
hashes_mu.unlock();
|
||||
|
||||
int swap = un64(a77, 70, 1);
|
||||
std::string msg;
|
||||
|
||||
@ -274,14 +277,16 @@ std::string Packing::unpack_4(int a77[], std::string& call1str, std::string& cal
|
||||
|
||||
int suffix = un64(a77, 71, 2);
|
||||
|
||||
if (suffix == 1) {
|
||||
msg += " RRR";
|
||||
if (suffix == 1)
|
||||
{
|
||||
locstr = " RRR";
|
||||
} else if (suffix == 2) {
|
||||
msg += " RR73";
|
||||
locstr = " RR73";
|
||||
} else if (suffix == 3) {
|
||||
msg += " 73";
|
||||
locstr = " 73";
|
||||
}
|
||||
|
||||
msg += locstr;
|
||||
return msg;
|
||||
}
|
||||
|
||||
@ -323,7 +328,7 @@ std::string Packing::unpack_1(int a77[], std::string& call1str, std::string& cal
|
||||
remember_call(call1str);
|
||||
remember_call(call2str);
|
||||
|
||||
const char *pr = (i3 == 1 ? "/R" : "/P");
|
||||
const std::string pr = (i3 == 1 ? "/R" : "/P");
|
||||
|
||||
return call1str + (rover1 ? pr : "") + " " + call2str + (rover2 ? pr : "") + " " + locstr;
|
||||
}
|
||||
@ -340,11 +345,13 @@ std::string Packing::unpack_0_0(int a77[], std::string& call1str, std::string& c
|
||||
const char *cc = " 0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ+-./?";
|
||||
boost::multiprecision::int128_t x = un128(a77, 0, 71);
|
||||
std::string msg = "0123456789123";
|
||||
|
||||
for (int i = 0; i < 13; i++)
|
||||
{
|
||||
msg[13 - 1 - i] = cc[(int) (x % 42)];
|
||||
x = x / 42;
|
||||
}
|
||||
|
||||
call1str = msg;
|
||||
return msg;
|
||||
}
|
||||
@ -392,6 +399,7 @@ std::string Packing::unpack_3(int a77[], std::string& call1str, std::string& cal
|
||||
int statei = serial - 8001;
|
||||
std::string serialstr;
|
||||
int nstates = sizeof(ru_states) / sizeof(ru_states[0]);
|
||||
|
||||
if (serial > 8000 && statei < nstates)
|
||||
{
|
||||
serialstr = ru_states[statei];
|
||||
@ -400,16 +408,17 @@ std::string Packing::unpack_3(int a77[], std::string& call1str, std::string& cal
|
||||
{
|
||||
char tmp[32];
|
||||
sprintf(tmp, "%04d", serial);
|
||||
serialstr = tmp;
|
||||
serialstr = std::string(tmp);
|
||||
}
|
||||
|
||||
std::string msg;
|
||||
|
||||
if (tu)
|
||||
{
|
||||
if (tu) {
|
||||
msg += "TU; ";
|
||||
}
|
||||
|
||||
msg += call1str + " " + call2str + " ";
|
||||
|
||||
if (r)
|
||||
{
|
||||
msg += "R ";
|
||||
@ -417,8 +426,9 @@ std::string Packing::unpack_3(int a77[], std::string& call1str, std::string& cal
|
||||
{
|
||||
char tmp[16];
|
||||
sprintf(tmp, "%d ", rst);
|
||||
msg += tmp;
|
||||
msg += std::string(tmp);
|
||||
}
|
||||
|
||||
msg += serialstr;
|
||||
|
||||
remember_call(call1str);
|
||||
@ -479,7 +489,7 @@ std::string Packing::unpack_0_3(int a77[], int n3, std::string& call1str, std::s
|
||||
{
|
||||
char tmp[16];
|
||||
sprintf(tmp, "%d%c ", n_transmitters + 1, clss + 'A');
|
||||
msg += tmp;
|
||||
msg += std::string(tmp);
|
||||
}
|
||||
|
||||
if (section - 1 >= 0 && section - 1 < (int)(sizeof(sections) / sizeof(sections[0]))) {
|
||||
@ -531,8 +541,9 @@ std::string Packing::unpack(int a77[], std::string& call1, std::string& call2, s
|
||||
}
|
||||
|
||||
char tmp[64];
|
||||
call1 = "UNK";
|
||||
sprintf(tmp, "UNK i3=%d n3=%d", i3, n3);
|
||||
return tmp;
|
||||
return std::string(tmp);
|
||||
}
|
||||
|
||||
} // namespace FT8
|
||||
|
@ -24,7 +24,7 @@
|
||||
#include <string>
|
||||
#include <map>
|
||||
|
||||
#include <QMutex>
|
||||
#include <QRecursiveMutex>
|
||||
|
||||
#include "export.h"
|
||||
|
||||
@ -36,7 +36,7 @@ public:
|
||||
std::string unpack(int a91[], std::string& call1str, std::string& call2str, std::string& locstr);
|
||||
|
||||
private:
|
||||
int ihashcall(std::string call, int m);
|
||||
static int ihashcall(std::string call, int m);
|
||||
std::string unpackcall(int x);
|
||||
std::string unpackgrid(int ng, int ir, int i3);
|
||||
void remember_call(std::string call);
|
||||
@ -46,7 +46,7 @@ private:
|
||||
std::string unpack_3(int a77[], std::string& call1str, std::string& call2str, std::string& locstr);
|
||||
std::string unpack_0_3(int a77[], int n3, std::string& call1str, std::string& call2str, std::string& locstr);
|
||||
|
||||
QMutex hashes_mu;
|
||||
QRecursiveMutex hashes_mu;
|
||||
std::map<int, std::string> hashes12;
|
||||
std::map<int, std::string> hashes22;
|
||||
|
||||
|
@ -24,6 +24,7 @@ set(demodft8_HEADERS
|
||||
|
||||
include_directories(
|
||||
${CMAKE_SOURCE_DIR}/swagger/sdrangel/code/qt5/client
|
||||
${CMAKE_SOURCE_DIR}/ft8
|
||||
)
|
||||
|
||||
if(NOT SERVER_MODE)
|
||||
@ -57,6 +58,7 @@ target_link_libraries(${TARGET_NAME}
|
||||
sdrbase
|
||||
${TARGET_LIB_GUI}
|
||||
swagger
|
||||
ft8
|
||||
)
|
||||
|
||||
install(TARGETS ${TARGET_NAME} DESTINATION ${INSTALL_FOLDER})
|
||||
|
@ -276,6 +276,18 @@ void FT8Demod::applySettings(const FT8DemodSettings& settings, bool force)
|
||||
if ((m_settings.m_agc != settings.m_agc) || force) {
|
||||
reverseAPIKeys.append("agc");
|
||||
}
|
||||
if ((m_settings.m_recordWav != settings.m_recordWav) || force) {
|
||||
reverseAPIKeys.append("recordWav");
|
||||
}
|
||||
if ((m_settings.m_logMessages != settings.m_logMessages) || force) {
|
||||
reverseAPIKeys.append("logMessages");
|
||||
}
|
||||
if ((m_settings.m_nbDecoderThreads != settings.m_nbDecoderThreads) || force) {
|
||||
reverseAPIKeys.append("nbDecoderThreads");
|
||||
}
|
||||
if ((m_settings.m_decoderTimeBudget != settings.m_decoderTimeBudget) || force) {
|
||||
reverseAPIKeys.append("decoderTimeBudget");
|
||||
}
|
||||
|
||||
if (m_settings.m_streamIndex != settings.m_streamIndex)
|
||||
{
|
||||
@ -444,6 +456,18 @@ void FT8Demod::webapiUpdateChannelSettings(
|
||||
if (channelSettingsKeys.contains("agc")) {
|
||||
settings.m_agc = response.getFt8DemodSettings()->getAgc() != 0;
|
||||
}
|
||||
if (channelSettingsKeys.contains("recordWav")) {
|
||||
settings.m_recordWav = response.getFt8DemodSettings()->getRecordWav() != 0;
|
||||
}
|
||||
if (channelSettingsKeys.contains("m_logMessages")) {
|
||||
settings.m_logMessages = response.getFt8DemodSettings()->getLogMessages() != 0;
|
||||
}
|
||||
if (channelSettingsKeys.contains("nbDecoderThreads")) {
|
||||
settings.m_nbDecoderThreads = response.getFt8DemodSettings()->getNbDecoderThreads();
|
||||
}
|
||||
if (channelSettingsKeys.contains("decoderTimeBudget")) {
|
||||
settings.m_decoderTimeBudget = response.getFt8DemodSettings()->getDecoderTimeBudget();
|
||||
}
|
||||
if (channelSettingsKeys.contains("rgbColor")) {
|
||||
settings.m_rgbColor = response.getFt8DemodSettings()->getRgbColor();
|
||||
}
|
||||
@ -500,6 +524,10 @@ void FT8Demod::webapiFormatChannelSettings(SWGSDRangel::SWGChannelSettings& resp
|
||||
response.getFt8DemodSettings()->setFftWindow((int) settings.m_filterBank[settings.m_filterIndex].m_fftWindow);
|
||||
response.getFt8DemodSettings()->setVolume(settings.m_volume);
|
||||
response.getFt8DemodSettings()->setAgc(settings.m_agc ? 1 : 0);
|
||||
response.getFt8DemodSettings()->setRecordWav(settings.m_recordWav ? 1 : 0);
|
||||
response.getFt8DemodSettings()->setLogMessages(settings.m_logMessages ? 1 : 0);
|
||||
response.getFt8DemodSettings()->setNbDecoderThreads(settings.m_nbDecoderThreads);
|
||||
response.getFt8DemodSettings()->setDecoderTimeBudget(settings.m_decoderTimeBudget);
|
||||
response.getFt8DemodSettings()->setRgbColor(settings.m_rgbColor);
|
||||
|
||||
if (response.getFt8DemodSettings()->getTitle()) {
|
||||
@ -671,6 +699,18 @@ void FT8Demod::webapiFormatChannelSettings(
|
||||
if (channelSettingsKeys.contains("agc") || force) {
|
||||
swgFT8DemodSettings->setAgc(settings.m_agc ? 1 : 0);
|
||||
}
|
||||
if (channelSettingsKeys.contains("recordWav") || force) {
|
||||
swgFT8DemodSettings->setRecordWav(settings.m_recordWav ? 1 : 0);
|
||||
}
|
||||
if (channelSettingsKeys.contains("logMessages") || force) {
|
||||
swgFT8DemodSettings->setRecordWav(settings.m_logMessages ? 1 : 0);
|
||||
}
|
||||
if (channelSettingsKeys.contains("nbDecoderThreads") || force) {
|
||||
swgFT8DemodSettings->setNbDecoderThreads(settings.m_nbDecoderThreads);
|
||||
}
|
||||
if (channelSettingsKeys.contains("decoderTimeBudget") || force) {
|
||||
swgFT8DemodSettings->setDecoderTimeBudget(settings.m_decoderTimeBudget);
|
||||
}
|
||||
if (channelSettingsKeys.contains("rgbColor") || force) {
|
||||
swgFT8DemodSettings->setRgbColor(settings.m_rgbColor);
|
||||
}
|
||||
|
@ -58,7 +58,8 @@ FT8DemodBaseband::FT8DemodBaseband() :
|
||||
this,
|
||||
&FT8DemodBaseband::bufferReady,
|
||||
m_ft8DemodWorker,
|
||||
&FT8DemodWorker::processBuffer
|
||||
&FT8DemodWorker::processBuffer,
|
||||
Qt::QueuedConnection
|
||||
);
|
||||
|
||||
m_workerThread->start();
|
||||
@ -204,6 +205,30 @@ void FT8DemodBaseband::applySettings(const FT8DemodSettings& settings, bool forc
|
||||
}
|
||||
}
|
||||
|
||||
if ((m_settings.m_filterBank[m_settings.m_filterIndex].m_lowCutoff != settings.m_filterBank[settings.m_filterIndex].m_lowCutoff) || force) {
|
||||
m_ft8DemodWorker->setLowFrequency(settings.m_filterBank[settings.m_filterIndex].m_lowCutoff);
|
||||
}
|
||||
|
||||
if ((m_settings.m_filterBank[m_settings.m_filterIndex].m_rfBandwidth != settings.m_filterBank[settings.m_filterIndex].m_rfBandwidth) || force) {
|
||||
m_ft8DemodWorker->setHighFrequency(settings.m_filterBank[settings.m_filterIndex].m_rfBandwidth);
|
||||
}
|
||||
|
||||
if ((settings.m_recordWav != m_settings.m_recordWav) || force) {
|
||||
m_ft8DemodWorker->setRecordSamples(settings.m_recordWav);
|
||||
}
|
||||
|
||||
if ((settings.m_logMessages != m_settings.m_logMessages) || force) {
|
||||
m_ft8DemodWorker->setLogMessages(settings.m_logMessages);
|
||||
}
|
||||
|
||||
if ((settings.m_nbDecoderThreads != m_settings.m_nbDecoderThreads) || force) {
|
||||
m_ft8DemodWorker->setNbDecoderThreads(settings.m_nbDecoderThreads);
|
||||
}
|
||||
|
||||
if ((settings.m_decoderTimeBudget != m_settings.m_decoderTimeBudget) || force) {
|
||||
m_ft8DemodWorker->setDecoderTimeBudget(settings.m_decoderTimeBudget);
|
||||
}
|
||||
|
||||
m_sink.applySettings(settings, force);
|
||||
m_settings = settings;
|
||||
}
|
||||
|
@ -204,6 +204,32 @@ void FT8DemodGUI::on_filterIndex_valueChanged(int value)
|
||||
applyBandwidths(m_settings.m_filterBank[m_settings.m_filterIndex].m_spanLog2, true); // does applySettings(true)
|
||||
}
|
||||
|
||||
void FT8DemodGUI::on_recordWav_toggled(bool checked)
|
||||
{
|
||||
m_settings.m_recordWav = checked;
|
||||
applySettings();
|
||||
}
|
||||
|
||||
void FT8DemodGUI::on_logMessages_toggled(bool checked)
|
||||
{
|
||||
m_settings.m_logMessages = checked;
|
||||
applySettings();
|
||||
}
|
||||
|
||||
void FT8DemodGUI::on_nbThreads_valueChanged(int value)
|
||||
{
|
||||
ui->nbThreadsText->setText(tr("%1").arg(value));
|
||||
m_settings.m_nbDecoderThreads = value;
|
||||
applySettings();
|
||||
}
|
||||
|
||||
void FT8DemodGUI::on_timeBudget_valueChanged(int value)
|
||||
{
|
||||
m_settings.m_decoderTimeBudget = value / 10.0f;
|
||||
ui->timeBudgetText->setText(tr("%1").arg(m_settings.m_decoderTimeBudget, 0, 'f', 1));
|
||||
applySettings();
|
||||
}
|
||||
|
||||
void FT8DemodGUI::onMenuDialogCalled(const QPoint &p)
|
||||
{
|
||||
if (m_contextMenuType == ContextMenuChannelSettings)
|
||||
@ -335,6 +361,9 @@ FT8DemodGUI::FT8DemodGUI(PluginAPI* pluginAPI, DeviceUISet *deviceUISet, Baseban
|
||||
|
||||
applyBandwidths(m_settings.m_filterBank[m_settings.m_filterIndex].m_spanLog2, true); // does applySettings(true)
|
||||
DialPopup::addPopupsToChildDials(this);
|
||||
|
||||
// Resize the table using dummy data
|
||||
resizeMessageTable();
|
||||
}
|
||||
|
||||
FT8DemodGUI::~FT8DemodGUI()
|
||||
@ -500,6 +529,13 @@ void FT8DemodGUI::displaySettings()
|
||||
ui->volume->setValue(volume);
|
||||
ui->volumeText->setText(QString("%1").arg(volume));
|
||||
|
||||
ui->recordWav->setChecked(m_settings.m_recordWav);
|
||||
ui->logMessages->setChecked(m_settings.m_logMessages);
|
||||
ui->nbThreads->setValue(m_settings.m_nbDecoderThreads);
|
||||
ui->nbThreadsText->setText(tr("%1").arg(m_settings.m_nbDecoderThreads));
|
||||
ui->timeBudget->setValue(m_settings.m_decoderTimeBudget*10);
|
||||
ui->timeBudgetText->setText(tr("%1").arg(m_settings.m_decoderTimeBudget, 0, 'f', 1));
|
||||
|
||||
updateIndexLabel();
|
||||
|
||||
getRollupContents()->restoreState(m_rollupState);
|
||||
@ -549,9 +585,33 @@ void FT8DemodGUI::makeUIConnections()
|
||||
QObject::connect(ui->spanLog2, &QSlider::valueChanged, this, &FT8DemodGUI::on_spanLog2_valueChanged);
|
||||
QObject::connect(ui->fftWindow, QOverload<int>::of(&QComboBox::currentIndexChanged), this, &FT8DemodGUI::on_fftWindow_currentIndexChanged);
|
||||
QObject::connect(ui->filterIndex, &QDial::valueChanged, this, &FT8DemodGUI::on_filterIndex_valueChanged);
|
||||
QObject::connect(ui->recordWav, &ButtonSwitch::toggled, this, &FT8DemodGUI::on_recordWav_toggled);
|
||||
QObject::connect(ui->logMessages, &ButtonSwitch::toggled, this, &FT8DemodGUI::on_logMessages_toggled);
|
||||
QObject::connect(ui->nbThreads, &QDial::valueChanged, this, &FT8DemodGUI::on_nbThreads_valueChanged);
|
||||
QObject::connect(ui->timeBudget, &QDial::valueChanged, this, &FT8DemodGUI::on_timeBudget_valueChanged);
|
||||
}
|
||||
|
||||
void FT8DemodGUI::updateAbsoluteCenterFrequency()
|
||||
{
|
||||
setStatusFrequency(m_deviceCenterFrequency + m_settings.m_inputFrequencyOffset);
|
||||
}
|
||||
|
||||
void FT8DemodGUI::resizeMessageTable()
|
||||
{
|
||||
// Fill table with a row of dummy data that will size the columns nicely
|
||||
// Trailing spaces are for sort arrow
|
||||
int row = ui->messages->rowCount();
|
||||
ui->messages->setRowCount(row + 1);
|
||||
ui->messages->setItem(row, MESSAGE_COL_UTC, new QTableWidgetItem("000000"));
|
||||
ui->messages->setItem(row, MESSAGE_COL_N, new QTableWidgetItem("0"));
|
||||
ui->messages->setItem(row, MESSAGE_COL_SNR, new QTableWidgetItem("-24"));
|
||||
ui->messages->setItem(row, MESSAGE_COL_DEC, new QTableWidgetItem("174"));
|
||||
ui->messages->setItem(row, MESSAGE_COL_DT, new QTableWidgetItem("0.0"));
|
||||
ui->messages->setItem(row, MESSAGE_COL_DF, new QTableWidgetItem("0000"));
|
||||
ui->messages->setItem(row, MESSAGE_COL_CALL1, new QTableWidgetItem("123456789ABCD"));
|
||||
ui->messages->setItem(row, MESSAGE_COL_CALL2, new QTableWidgetItem("HF7SIEMA"));
|
||||
ui->messages->setItem(row, MESSAGE_COL_LOC, new QTableWidgetItem("JN00"));
|
||||
ui->messages->setItem(row, MESSAGE_COL_INFO, new QTableWidgetItem("hint1"));
|
||||
ui->messages->resizeColumnsToContents();
|
||||
ui->messages->removeRow(row);
|
||||
}
|
||||
|
@ -100,6 +100,21 @@ private:
|
||||
void leaveEvent(QEvent*);
|
||||
void enterEvent(EnterEventType*);
|
||||
|
||||
void resizeMessageTable();
|
||||
|
||||
enum MessageCol {
|
||||
MESSAGE_COL_UTC,
|
||||
MESSAGE_COL_N,
|
||||
MESSAGE_COL_SNR,
|
||||
MESSAGE_COL_DEC,
|
||||
MESSAGE_COL_DT,
|
||||
MESSAGE_COL_DF,
|
||||
MESSAGE_COL_CALL1,
|
||||
MESSAGE_COL_CALL2,
|
||||
MESSAGE_COL_LOC,
|
||||
MESSAGE_COL_INFO,
|
||||
};
|
||||
|
||||
private slots:
|
||||
void on_deltaFrequency_changed(qint64 value);
|
||||
void on_BW_valueChanged(int value);
|
||||
@ -109,6 +124,10 @@ private slots:
|
||||
void on_spanLog2_valueChanged(int value);
|
||||
void on_fftWindow_currentIndexChanged(int index);
|
||||
void on_filterIndex_valueChanged(int value);
|
||||
void on_recordWav_toggled(bool checked);
|
||||
void on_logMessages_toggled(bool checked);
|
||||
void on_nbThreads_valueChanged(int value);
|
||||
void on_timeBudget_valueChanged(int value);
|
||||
void onWidgetRolled(QWidget* widget, bool rollDown);
|
||||
void onMenuDialogCalled(const QPoint& p);
|
||||
void handleInputMessages();
|
||||
|
@ -7,7 +7,7 @@
|
||||
<x>0</x>
|
||||
<y>0</y>
|
||||
<width>414</width>
|
||||
<height>190</height>
|
||||
<height>731</height>
|
||||
</rect>
|
||||
</property>
|
||||
<property name="sizePolicy">
|
||||
@ -667,9 +667,9 @@
|
||||
<property name="geometry">
|
||||
<rect>
|
||||
<x>10</x>
|
||||
<y>170</y>
|
||||
<y>193</y>
|
||||
<width>218</width>
|
||||
<height>284</height>
|
||||
<height>261</height>
|
||||
</rect>
|
||||
</property>
|
||||
<property name="sizePolicy">
|
||||
@ -737,6 +737,298 @@
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
<widget class="QWidget" name="tft8Container" native="true">
|
||||
<property name="geometry">
|
||||
<rect>
|
||||
<x>0</x>
|
||||
<y>460</y>
|
||||
<width>412</width>
|
||||
<height>251</height>
|
||||
</rect>
|
||||
</property>
|
||||
<property name="sizePolicy">
|
||||
<sizepolicy hsizetype="Expanding" vsizetype="Expanding">
|
||||
<horstretch>0</horstretch>
|
||||
<verstretch>0</verstretch>
|
||||
</sizepolicy>
|
||||
</property>
|
||||
<property name="minimumSize">
|
||||
<size>
|
||||
<width>412</width>
|
||||
<height>0</height>
|
||||
</size>
|
||||
</property>
|
||||
<property name="windowTitle">
|
||||
<string>FT8 details</string>
|
||||
</property>
|
||||
<layout class="QVBoxLayout" name="verticalLayout_2">
|
||||
<property name="spacing">
|
||||
<number>2</number>
|
||||
</property>
|
||||
<property name="leftMargin">
|
||||
<number>2</number>
|
||||
</property>
|
||||
<property name="topMargin">
|
||||
<number>3</number>
|
||||
</property>
|
||||
<property name="rightMargin">
|
||||
<number>2</number>
|
||||
</property>
|
||||
<property name="bottomMargin">
|
||||
<number>3</number>
|
||||
</property>
|
||||
<item>
|
||||
<layout class="QHBoxLayout" name="ft8Settings">
|
||||
<item>
|
||||
<widget class="QLabel" name="nbThreadsLabel">
|
||||
<property name="text">
|
||||
<string>Th</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QDial" name="nbThreads">
|
||||
<property name="maximumSize">
|
||||
<size>
|
||||
<width>24</width>
|
||||
<height>24</height>
|
||||
</size>
|
||||
</property>
|
||||
<property name="toolTip">
|
||||
<string>Number of decoder threads</string>
|
||||
</property>
|
||||
<property name="minimum">
|
||||
<number>2</number>
|
||||
</property>
|
||||
<property name="maximum">
|
||||
<number>12</number>
|
||||
</property>
|
||||
<property name="singleStep">
|
||||
<number>1</number>
|
||||
</property>
|
||||
<property name="pageStep">
|
||||
<number>1</number>
|
||||
</property>
|
||||
<property name="value">
|
||||
<number>6</number>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QLabel" name="nbThreadsText">
|
||||
<property name="text">
|
||||
<string>6</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="Line" name="line_2">
|
||||
<property name="orientation">
|
||||
<enum>Qt::Vertical</enum>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QLabel" name="timeBudgetLabel">
|
||||
<property name="text">
|
||||
<string>TB</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QDial" name="timeBudget">
|
||||
<property name="maximumSize">
|
||||
<size>
|
||||
<width>24</width>
|
||||
<height>24</height>
|
||||
</size>
|
||||
</property>
|
||||
<property name="toolTip">
|
||||
<string>Decoder time budget (seconds)</string>
|
||||
</property>
|
||||
<property name="minimum">
|
||||
<number>5</number>
|
||||
</property>
|
||||
<property name="maximum">
|
||||
<number>50</number>
|
||||
</property>
|
||||
<property name="singleStep">
|
||||
<number>1</number>
|
||||
</property>
|
||||
<property name="pageStep">
|
||||
<number>1</number>
|
||||
</property>
|
||||
<property name="value">
|
||||
<number>25</number>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QLabel" name="timeBudgetText">
|
||||
<property name="text">
|
||||
<string>2.5</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="Line" name="line_5">
|
||||
<property name="orientation">
|
||||
<enum>Qt::Vertical</enum>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QLabel" name="nbDecodesLabel">
|
||||
<property name="text">
|
||||
<string>Dec</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QLabel" name="nbDecodesText">
|
||||
<property name="toolTip">
|
||||
<string>Number of messages decoded in the last sequence</string>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>00</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<spacer name="horizontalSpacer">
|
||||
<property name="orientation">
|
||||
<enum>Qt::Horizontal</enum>
|
||||
</property>
|
||||
<property name="sizeHint" stdset="0">
|
||||
<size>
|
||||
<width>40</width>
|
||||
<height>20</height>
|
||||
</size>
|
||||
</property>
|
||||
</spacer>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="ButtonSwitch" name="logMessages">
|
||||
<property name="toolTip">
|
||||
<string>Log messages</string>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>...</string>
|
||||
</property>
|
||||
<property name="icon">
|
||||
<iconset resource="../../../sdrgui/resources/res.qrc">
|
||||
<normaloff>:/listing.png</normaloff>:/listing.png</iconset>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="ButtonSwitch" name="recordWav">
|
||||
<property name="toolTip">
|
||||
<string>Record wav files</string>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string/>
|
||||
</property>
|
||||
<property name="icon">
|
||||
<iconset resource="../../../sdrgui/resources/res.qrc">
|
||||
<normaloff>:/record_off.png</normaloff>:/record_off.png</iconset>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QTableWidget" name="messages">
|
||||
<property name="toolTip">
|
||||
<string>Decoded messages</string>
|
||||
</property>
|
||||
<property name="editTriggers">
|
||||
<set>QAbstractItemView::NoEditTriggers</set>
|
||||
</property>
|
||||
<column>
|
||||
<property name="text">
|
||||
<string>UTC</string>
|
||||
</property>
|
||||
<property name="toolTip">
|
||||
<string>Sequence UTC time HHMMSS</string>
|
||||
</property>
|
||||
</column>
|
||||
<column>
|
||||
<property name="text">
|
||||
<string>P</string>
|
||||
</property>
|
||||
<property name="toolTip">
|
||||
<string>Successful decoder pass index</string>
|
||||
</property>
|
||||
</column>
|
||||
<column>
|
||||
<property name="text">
|
||||
<string>SNR</string>
|
||||
</property>
|
||||
<property name="toolTip">
|
||||
<string>Signal to noise ratio (dB) in 2.5 kHz bandwidth</string>
|
||||
</property>
|
||||
</column>
|
||||
<column>
|
||||
<property name="text">
|
||||
<string>OKb</string>
|
||||
</property>
|
||||
<property name="toolTip">
|
||||
<string>Number of correct bits before correction</string>
|
||||
</property>
|
||||
</column>
|
||||
<column>
|
||||
<property name="text">
|
||||
<string>dt</string>
|
||||
</property>
|
||||
<property name="toolTip">
|
||||
<string>Time delay</string>
|
||||
</property>
|
||||
</column>
|
||||
<column>
|
||||
<property name="text">
|
||||
<string>df</string>
|
||||
</property>
|
||||
<property name="toolTip">
|
||||
<string>Frequency shift</string>
|
||||
</property>
|
||||
</column>
|
||||
<column>
|
||||
<property name="text">
|
||||
<string>Call1</string>
|
||||
</property>
|
||||
<property name="toolTip">
|
||||
<string>Fist call area</string>
|
||||
</property>
|
||||
</column>
|
||||
<column>
|
||||
<property name="text">
|
||||
<string>Call2</string>
|
||||
</property>
|
||||
<property name="toolTip">
|
||||
<string>Second call area</string>
|
||||
</property>
|
||||
</column>
|
||||
<column>
|
||||
<property name="text">
|
||||
<string>Loc</string>
|
||||
</property>
|
||||
<property name="toolTip">
|
||||
<string>Locator area</string>
|
||||
</property>
|
||||
</column>
|
||||
<column>
|
||||
<property name="text">
|
||||
<string>Info</string>
|
||||
</property>
|
||||
<property name="toolTip">
|
||||
<string>Extra decoder information</string>
|
||||
</property>
|
||||
</column>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
</widget>
|
||||
<customwidgets>
|
||||
<customwidget>
|
||||
|
@ -43,6 +43,10 @@ FT8DemodSettings::FT8DemodSettings() :
|
||||
void FT8DemodSettings::resetToDefaults()
|
||||
{
|
||||
m_agc = false;
|
||||
m_recordWav = false;
|
||||
m_logMessages = false;
|
||||
m_nbDecoderThreads = 6;
|
||||
m_decoderTimeBudget = 0.5;
|
||||
m_volume = 1.0;
|
||||
m_inputFrequencyOffset = 0;
|
||||
m_rgbColor = QColor(0, 192, 255).rgb();
|
||||
@ -69,6 +73,10 @@ QByteArray FT8DemodSettings::serialize() const
|
||||
}
|
||||
|
||||
s.writeU32(5, m_rgbColor);
|
||||
s.writeBool(6, m_recordWav);
|
||||
s.writeBool(7, m_logMessages);
|
||||
s.writeS32(8, m_nbDecoderThreads);
|
||||
s.writeFloat(9, m_decoderTimeBudget);
|
||||
s.writeBool(11, m_agc);
|
||||
s.writeString(16, m_title);
|
||||
s.writeBool(18, m_useReverseAPI);
|
||||
@ -126,6 +134,10 @@ bool FT8DemodSettings::deserialize(const QByteArray& data)
|
||||
}
|
||||
|
||||
d.readU32(5, &m_rgbColor);
|
||||
d.readBool(6, &m_recordWav, false);
|
||||
d.readBool(7, &m_logMessages, false);
|
||||
d.readS32(8, &m_nbDecoderThreads, 6);
|
||||
d.readFloat(9, &m_decoderTimeBudget, 0.5);
|
||||
d.readBool(11, &m_agc, false);
|
||||
d.readString(16, &m_title, "SSB Demodulator");
|
||||
d.readBool(18, &m_useReverseAPI, false);
|
||||
|
@ -46,8 +46,11 @@ struct FT8DemodSettings
|
||||
// Real m_rfBandwidth;
|
||||
// Real m_lowCutoff;
|
||||
Real m_volume;
|
||||
// int m_spanLog2;
|
||||
bool m_agc;
|
||||
bool m_recordWav;
|
||||
bool m_logMessages;
|
||||
int m_nbDecoderThreads;
|
||||
float m_decoderTimeBudget;
|
||||
quint32 m_rgbColor;
|
||||
QString m_title;
|
||||
int m_streamIndex; //!< MIMO channel. Not relevant when connected to SI (single Rx).
|
||||
|
@ -20,11 +20,84 @@
|
||||
#include <QDateTime>
|
||||
|
||||
#include "dsp/wavfilerecord.h"
|
||||
#include "util/messagequeue.h"
|
||||
#include "util/ft8message.h"
|
||||
|
||||
#include "ft8demodsettings.h"
|
||||
#include "ft8demodworker.h"
|
||||
|
||||
FT8DemodWorker::FT8DemodWorker()
|
||||
FT8DemodWorker::FT8Callback::FT8Callback(const QDateTime& periodTS, FT8::Packing& packing) :
|
||||
m_packing(packing),
|
||||
m_periodTS(periodTS)
|
||||
{
|
||||
m_msgReportFT8Messages = MsgReportFT8Messages::create();
|
||||
}
|
||||
|
||||
int FT8DemodWorker::FT8Callback::hcb(
|
||||
int *a91,
|
||||
float hz0,
|
||||
float off,
|
||||
const char *comment,
|
||||
float snr,
|
||||
int pass,
|
||||
int correct_bits
|
||||
)
|
||||
{
|
||||
std::string call1;
|
||||
std::string call2;
|
||||
std::string loc;
|
||||
std::string msg = m_packing.unpack(a91, call1, call2, loc);
|
||||
|
||||
cycle_mu.lock();
|
||||
|
||||
if (cycle_already.count(msg) > 0)
|
||||
{
|
||||
// already decoded this message on this cycle
|
||||
cycle_mu.unlock();
|
||||
return 1; // 1 => already seen, don't subtract.
|
||||
}
|
||||
|
||||
cycle_already[msg] = true;
|
||||
cycle_mu.unlock();
|
||||
|
||||
QList<FT8Message>& ft8Messages = m_msgReportFT8Messages->getFT8Messages();
|
||||
ft8Messages.push_back(FT8Message());
|
||||
FT8Message& ft8Message = ft8Messages.back();
|
||||
|
||||
ft8Message.ts = m_periodTS;
|
||||
ft8Message.pass = pass;
|
||||
ft8Message.snr = (int) snr;
|
||||
ft8Message.nbCorrectBits = correct_bits;
|
||||
ft8Message.dt = off - 0.5;
|
||||
ft8Message.df = hz0;
|
||||
ft8Message.call1 = QString(call1.c_str());
|
||||
ft8Message.call2 = QString(call2.c_str());
|
||||
ft8Message.loc = QString(loc.c_str());
|
||||
ft8Message.decoderInfo = QString(comment);
|
||||
|
||||
qDebug("FT8DemodWorker::FT8Callback::hcb: %d %3d %3d %5.2f %6.1f %s [%s:%s:%s] (%s)",
|
||||
pass,
|
||||
(int)snr,
|
||||
correct_bits,
|
||||
off - 0.5,
|
||||
hz0,
|
||||
msg.c_str(),
|
||||
call1.c_str(),
|
||||
call2.c_str(),
|
||||
loc.c_str(),
|
||||
comment
|
||||
);
|
||||
|
||||
return 2; // 2 => new decode, do subtract.
|
||||
}
|
||||
|
||||
FT8DemodWorker::FT8DemodWorker() :
|
||||
m_recordSamples(false),
|
||||
m_nbDecoderThreads(6),
|
||||
m_decoderTimeBudget(0.5),
|
||||
m_lowFreq(200),
|
||||
m_highFreq(3000),
|
||||
m_reportingMessageQueue(nullptr)
|
||||
{
|
||||
QString relPath = "sdrangel/ft8/save";
|
||||
QDir dir(QStandardPaths::writableLocation(QStandardPaths::GenericDataLocation));
|
||||
@ -38,16 +111,58 @@ FT8DemodWorker::~FT8DemodWorker()
|
||||
|
||||
void FT8DemodWorker::processBuffer(int16_t *buffer, QDateTime periodTS)
|
||||
{
|
||||
qDebug("FT8DemodWorker::processBuffer: %s", qPrintable(periodTS.toString("yyyy-MM-dd HH:mm:ss")));
|
||||
WavFileRecord *wavFileRecord = new WavFileRecord(FT8DemodSettings::m_ft8SampleRate);
|
||||
QFileInfo wfi(QDir(m_samplesPath), periodTS.toString("yyyyMMdd_HHmmss"));
|
||||
QString wpath = wfi.absoluteFilePath();
|
||||
qDebug("FT8DemodWorker::processBuffer: WAV file: %s.wav", qPrintable(wpath));
|
||||
wavFileRecord->setFileName(wpath);
|
||||
wavFileRecord->setFileBaseIsFileName(true);
|
||||
wavFileRecord->setMono(true);
|
||||
wavFileRecord->startRecording();
|
||||
wavFileRecord->writeMono(buffer, 15*FT8DemodSettings::m_ft8SampleRate);
|
||||
wavFileRecord->stopRecording();
|
||||
delete wavFileRecord;
|
||||
qDebug("FT8DemodWorker::processBuffer: %s %d:%f [%d:%d]", qPrintable(periodTS.toString("yyyy-MM-dd HH:mm:ss")),
|
||||
m_nbDecoderThreads, m_decoderTimeBudget, m_lowFreq, m_highFreq);
|
||||
|
||||
if (m_recordSamples)
|
||||
{
|
||||
WavFileRecord *wavFileRecord = new WavFileRecord(FT8DemodSettings::m_ft8SampleRate);
|
||||
QFileInfo wfi(QDir(m_samplesPath), periodTS.toString("yyyyMMdd_HHmmss"));
|
||||
QString wpath = wfi.absoluteFilePath();
|
||||
qDebug("FT8DemodWorker::processBuffer: WAV file: %s.wav", qPrintable(wpath));
|
||||
wavFileRecord->setFileName(wpath);
|
||||
wavFileRecord->setFileBaseIsFileName(true);
|
||||
wavFileRecord->setMono(true);
|
||||
wavFileRecord->startRecording();
|
||||
wavFileRecord->writeMono(buffer, 15*FT8DemodSettings::m_ft8SampleRate);
|
||||
wavFileRecord->stopRecording();
|
||||
delete wavFileRecord;
|
||||
}
|
||||
|
||||
int hints[2] = { 2, 0 }; // CQ
|
||||
FT8Callback ft8Callback(periodTS, m_packing);
|
||||
m_ft8Decoder.getParams().nthreads = m_nbDecoderThreads;
|
||||
std::vector<float> samples(15*FT8DemodSettings::m_ft8SampleRate);
|
||||
|
||||
std::transform(
|
||||
buffer,
|
||||
buffer + (15*FT8DemodSettings::m_ft8SampleRate),
|
||||
samples.begin(),
|
||||
[](const int16_t& s) -> float { return s / 32768.0f; }
|
||||
);
|
||||
|
||||
m_ft8Decoder.entry(
|
||||
samples.data(),
|
||||
samples.size(),
|
||||
0.5 * FT8DemodSettings::m_ft8SampleRate,
|
||||
FT8DemodSettings::m_ft8SampleRate,
|
||||
m_lowFreq,
|
||||
m_highFreq,
|
||||
hints,
|
||||
hints,
|
||||
m_decoderTimeBudget,
|
||||
m_decoderTimeBudget,
|
||||
&ft8Callback,
|
||||
0,
|
||||
(struct FT8::cdecode *) nullptr
|
||||
);
|
||||
|
||||
m_ft8Decoder.wait(m_decoderTimeBudget + 1.0); // add one second to budget to force quit threads
|
||||
qDebug("FT8DemodWorker::processBuffer: done: %d messages", ft8Callback.getReportMessage()->getFT8Messages().size());
|
||||
|
||||
if (m_reportingMessageQueue) {
|
||||
m_reportingMessageQueue->push(ft8Callback.getReportMessage());
|
||||
} else {
|
||||
delete ft8Callback.getReportMessage();
|
||||
}
|
||||
}
|
||||
|
@ -20,7 +20,12 @@
|
||||
|
||||
#include <QObject>
|
||||
|
||||
#include "ft8.h"
|
||||
#include "unpack.h"
|
||||
|
||||
class QDateTime;
|
||||
class MessageQueue;
|
||||
class MsgReportFT8Messages;
|
||||
|
||||
class FT8DemodWorker : public QObject
|
||||
{
|
||||
@ -30,9 +35,52 @@ public:
|
||||
~FT8DemodWorker();
|
||||
|
||||
void processBuffer(int16_t *buffer, QDateTime periodTS);
|
||||
void setRecordSamples(bool recordSamples) { m_recordSamples = recordSamples; }
|
||||
void setLogMessages(bool logMessages) { m_logMessages = logMessages; }
|
||||
void setNbDecoderThreads(int nbDecoderThreads) { m_nbDecoderThreads = nbDecoderThreads; }
|
||||
void setDecoderTimeBudget(float decoderTimeBudget) { m_decoderTimeBudget = decoderTimeBudget; }
|
||||
void setLowFrequency(int lowFreq) { m_lowFreq = lowFreq; }
|
||||
void setHighFrequency(int highFreq) { m_highFreq = highFreq; }
|
||||
void setReportingMessageQueue(MessageQueue *messageQueue) { m_reportingMessageQueue = messageQueue; }
|
||||
|
||||
private:
|
||||
class FT8Callback : public FT8::CallbackInterface
|
||||
{
|
||||
public:
|
||||
FT8Callback(const QDateTime& periodTS, FT8::Packing& packing);
|
||||
virtual int hcb(
|
||||
int *a91,
|
||||
float hz0,
|
||||
float off,
|
||||
const char *comment,
|
||||
float snr,
|
||||
int pass,
|
||||
int correct_bits
|
||||
);
|
||||
const std::map<std::string, bool>& getMsgMap() {
|
||||
return cycle_already;
|
||||
}
|
||||
MsgReportFT8Messages *getReportMessage() {
|
||||
return m_msgReportFT8Messages;
|
||||
}
|
||||
private:
|
||||
QMutex cycle_mu;
|
||||
std::map<std::string, bool> cycle_already;
|
||||
FT8::Packing& m_packing;
|
||||
MsgReportFT8Messages *m_msgReportFT8Messages;
|
||||
const QDateTime& m_periodTS;
|
||||
};
|
||||
|
||||
QString m_samplesPath;
|
||||
bool m_recordSamples;
|
||||
bool m_logMessages;
|
||||
int m_nbDecoderThreads;
|
||||
float m_decoderTimeBudget;
|
||||
int m_lowFreq;
|
||||
int m_highFreq;
|
||||
FT8::FT8Decoder m_ft8Decoder;
|
||||
FT8::Packing m_packing;
|
||||
MessageQueue *m_reportingMessageQueue;
|
||||
};
|
||||
|
||||
#endif // INCLUDE_FT8DEMODWORKER_H
|
||||
|
@ -185,6 +185,7 @@ set(sdrbase_SOURCES
|
||||
util/fixedtraits.cpp
|
||||
util/fits.cpp
|
||||
util/flightinformation.cpp
|
||||
util/ft8message.cpp
|
||||
util/giro.cpp
|
||||
util/golay2312.cpp
|
||||
util/httpdownloadmanager.cpp
|
||||
@ -408,6 +409,7 @@ set(sdrbase_HEADERS
|
||||
util/fixedtraits.h
|
||||
util/fits.h
|
||||
util/flightinformation.h
|
||||
util/ft8message.h
|
||||
util/giro.h
|
||||
util/golay2312.h
|
||||
util/httpdownloadmanager.h
|
||||
|
@ -5611,6 +5611,22 @@ margin-bottom: 20px;
|
||||
"type" : "integer",
|
||||
"description" : "AGC (1 if AGC active else 0)"
|
||||
},
|
||||
"recordWav" : {
|
||||
"type" : "integer",
|
||||
"description" : "Record received audio as 12000S/s 16 bit .wav file\n * 0 - Do not record\n * 1 - Record as sdrangel/ft8/save/YYYYMMDD_HHMMSS.wav in writable location\n"
|
||||
},
|
||||
"logMessages" : {
|
||||
"type" : "integer",
|
||||
"description" : "Log decoded messages in WSJT CALL.TXT format\n * 0 - Do not log messages\n * 1 - Log messages as sdrangel/ft8/logs/YYYYMMDD_call.txt in writable location (daily files)\n"
|
||||
},
|
||||
"nbDecoderThreads" : {
|
||||
"type" : "integer",
|
||||
"description" : "Number of threads in the FT8 decoder"
|
||||
},
|
||||
"decoderTimeBudget" : {
|
||||
"type" : "number",
|
||||
"format" : "float"
|
||||
},
|
||||
"rgbColor" : {
|
||||
"type" : "integer"
|
||||
},
|
||||
@ -56873,7 +56889,7 @@ except ApiException as e:
|
||||
</div>
|
||||
<div id="generator">
|
||||
<div class="content">
|
||||
Generated 2023-01-17T00:44:14.657+01:00
|
||||
Generated 2023-01-19T23:18:51.935+01:00
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
@ -33,6 +33,25 @@ FT8DemodSettings:
|
||||
agc:
|
||||
description: AGC (1 if AGC active else 0)
|
||||
type: integer
|
||||
recordWav:
|
||||
type: integer
|
||||
description: >
|
||||
Record received audio as 12000S/s 16 bit .wav file
|
||||
* 0 - Do not record
|
||||
* 1 - Record as sdrangel/ft8/save/YYYYMMDD_HHMMSS.wav in writable location
|
||||
logMessages:
|
||||
type: integer
|
||||
description: >
|
||||
Log decoded messages in WSJT CALL.TXT format
|
||||
* 0 - Do not log messages
|
||||
* 1 - Log messages as sdrangel/ft8/logs/YYYYMMDD_call.txt in writable location (daily files)
|
||||
nbDecoderThreads:
|
||||
type: integer
|
||||
description: Number of threads in the FT8 decoder
|
||||
decoderTimeBudget:
|
||||
type: number
|
||||
format: float
|
||||
desctiption: Decoder time budget in seconds (will stop after running this time)
|
||||
rgbColor:
|
||||
type: integer
|
||||
title:
|
||||
|
20
sdrbase/util/ft8message.cpp
Normal file
20
sdrbase/util/ft8message.cpp
Normal file
@ -0,0 +1,20 @@
|
||||
///////////////////////////////////////////////////////////////////////////////////
|
||||
// Copyright (C) 2023 Edouard Griffiths, F4EXB. //
|
||||
// //
|
||||
// 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 "ft8message.h"
|
||||
|
||||
MESSAGE_CLASS_DEFINITION(MsgReportFT8Messages, Message)
|
58
sdrbase/util/ft8message.h
Normal file
58
sdrbase/util/ft8message.h
Normal file
@ -0,0 +1,58 @@
|
||||
///////////////////////////////////////////////////////////////////////////////////
|
||||
// Copyright (C) 2023 Edouard Griffiths, F4EXB. //
|
||||
// //
|
||||
// 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 INCLUDE_FT8MESSAGE_H
|
||||
#define INCLUDE_FT8MESSAGE_H
|
||||
|
||||
#include <QString>
|
||||
#include <QDateTime>
|
||||
#include <QList>
|
||||
|
||||
#include "export.h"
|
||||
#include "message.h"
|
||||
|
||||
struct SDRBASE_API FT8Message
|
||||
{
|
||||
QDateTime ts;
|
||||
int pass;
|
||||
int snr;
|
||||
int nbCorrectBits;
|
||||
float dt;
|
||||
float df;
|
||||
QString call1;
|
||||
QString call2;
|
||||
QString loc;
|
||||
QString decoderInfo;
|
||||
};
|
||||
|
||||
class FT8_API MsgReportFT8Messages : public Message {
|
||||
MESSAGE_CLASS_DECLARATION
|
||||
public:
|
||||
QList<FT8Message>& getFT8Messages() { return m_ft8Messages; }
|
||||
|
||||
static MsgReportFT8Messages* create() {
|
||||
return new MsgReportFT8Messages();
|
||||
}
|
||||
|
||||
private:
|
||||
QList<FT8Message> m_ft8Messages;
|
||||
|
||||
MsgReportFT8Messages() :
|
||||
Message()
|
||||
{ }
|
||||
};
|
||||
|
||||
#endif // INCLUDE_FT8MESSAGE_H
|
@ -33,6 +33,25 @@ FT8DemodSettings:
|
||||
agc:
|
||||
description: AGC (1 if AGC active else 0)
|
||||
type: integer
|
||||
recordWav:
|
||||
type: integer
|
||||
description: >
|
||||
Record received audio as 12000S/s 16 bit .wav file
|
||||
* 0 - Do not record
|
||||
* 1 - Record as sdrangel/ft8/save/YYYYMMDD_HHMMSS.wav in writable location
|
||||
logMessages:
|
||||
type: integer
|
||||
description: >
|
||||
Log decoded messages in WSJT CALL.TXT format
|
||||
* 0 - Do not log messages
|
||||
* 1 - Log messages as sdrangel/ft8/logs/YYYYMMDD_call.txt in writable location (daily files)
|
||||
nbDecoderThreads:
|
||||
type: integer
|
||||
description: Number of threads in the FT8 decoder
|
||||
decoderTimeBudget:
|
||||
type: number
|
||||
format: float
|
||||
desctiption: Decoder time budget in seconds (will stop after running this time)
|
||||
rgbColor:
|
||||
type: integer
|
||||
title:
|
||||
|
@ -5611,6 +5611,22 @@ margin-bottom: 20px;
|
||||
"type" : "integer",
|
||||
"description" : "AGC (1 if AGC active else 0)"
|
||||
},
|
||||
"recordWav" : {
|
||||
"type" : "integer",
|
||||
"description" : "Record received audio as 12000S/s 16 bit .wav file\n * 0 - Do not record\n * 1 - Record as sdrangel/ft8/save/YYYYMMDD_HHMMSS.wav in writable location\n"
|
||||
},
|
||||
"logMessages" : {
|
||||
"type" : "integer",
|
||||
"description" : "Log decoded messages in WSJT CALL.TXT format\n * 0 - Do not log messages\n * 1 - Log messages as sdrangel/ft8/logs/YYYYMMDD_call.txt in writable location (daily files)\n"
|
||||
},
|
||||
"nbDecoderThreads" : {
|
||||
"type" : "integer",
|
||||
"description" : "Number of threads in the FT8 decoder"
|
||||
},
|
||||
"decoderTimeBudget" : {
|
||||
"type" : "number",
|
||||
"format" : "float"
|
||||
},
|
||||
"rgbColor" : {
|
||||
"type" : "integer"
|
||||
},
|
||||
@ -56873,7 +56889,7 @@ except ApiException as e:
|
||||
</div>
|
||||
<div id="generator">
|
||||
<div class="content">
|
||||
Generated 2023-01-17T00:44:14.657+01:00
|
||||
Generated 2023-01-19T23:18:51.935+01:00
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
@ -44,6 +44,14 @@ SWGFT8DemodSettings::SWGFT8DemodSettings() {
|
||||
m_volume_isSet = false;
|
||||
agc = 0;
|
||||
m_agc_isSet = false;
|
||||
record_wav = 0;
|
||||
m_record_wav_isSet = false;
|
||||
log_messages = 0;
|
||||
m_log_messages_isSet = false;
|
||||
nb_decoder_threads = 0;
|
||||
m_nb_decoder_threads_isSet = false;
|
||||
decoder_time_budget = 0.0f;
|
||||
m_decoder_time_budget_isSet = false;
|
||||
rgb_color = 0;
|
||||
m_rgb_color_isSet = false;
|
||||
title = nullptr;
|
||||
@ -90,6 +98,14 @@ SWGFT8DemodSettings::init() {
|
||||
m_volume_isSet = false;
|
||||
agc = 0;
|
||||
m_agc_isSet = false;
|
||||
record_wav = 0;
|
||||
m_record_wav_isSet = false;
|
||||
log_messages = 0;
|
||||
m_log_messages_isSet = false;
|
||||
nb_decoder_threads = 0;
|
||||
m_nb_decoder_threads_isSet = false;
|
||||
decoder_time_budget = 0.0f;
|
||||
m_decoder_time_budget_isSet = false;
|
||||
rgb_color = 0;
|
||||
m_rgb_color_isSet = false;
|
||||
title = new QString("");
|
||||
@ -125,6 +141,10 @@ SWGFT8DemodSettings::cleanup() {
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
if(title != nullptr) {
|
||||
delete title;
|
||||
}
|
||||
@ -174,6 +194,14 @@ SWGFT8DemodSettings::fromJsonObject(QJsonObject &pJson) {
|
||||
|
||||
::SWGSDRangel::setValue(&agc, pJson["agc"], "qint32", "");
|
||||
|
||||
::SWGSDRangel::setValue(&record_wav, pJson["recordWav"], "qint32", "");
|
||||
|
||||
::SWGSDRangel::setValue(&log_messages, pJson["logMessages"], "qint32", "");
|
||||
|
||||
::SWGSDRangel::setValue(&nb_decoder_threads, pJson["nbDecoderThreads"], "qint32", "");
|
||||
|
||||
::SWGSDRangel::setValue(&decoder_time_budget, pJson["decoderTimeBudget"], "float", "");
|
||||
|
||||
::SWGSDRangel::setValue(&rgb_color, pJson["rgbColor"], "qint32", "");
|
||||
|
||||
::SWGSDRangel::setValue(&title, pJson["title"], "QString", "QString");
|
||||
@ -236,6 +264,18 @@ SWGFT8DemodSettings::asJsonObject() {
|
||||
if(m_agc_isSet){
|
||||
obj->insert("agc", QJsonValue(agc));
|
||||
}
|
||||
if(m_record_wav_isSet){
|
||||
obj->insert("recordWav", QJsonValue(record_wav));
|
||||
}
|
||||
if(m_log_messages_isSet){
|
||||
obj->insert("logMessages", QJsonValue(log_messages));
|
||||
}
|
||||
if(m_nb_decoder_threads_isSet){
|
||||
obj->insert("nbDecoderThreads", QJsonValue(nb_decoder_threads));
|
||||
}
|
||||
if(m_decoder_time_budget_isSet){
|
||||
obj->insert("decoderTimeBudget", QJsonValue(decoder_time_budget));
|
||||
}
|
||||
if(m_rgb_color_isSet){
|
||||
obj->insert("rgbColor", QJsonValue(rgb_color));
|
||||
}
|
||||
@ -353,6 +393,46 @@ SWGFT8DemodSettings::setAgc(qint32 agc) {
|
||||
this->m_agc_isSet = true;
|
||||
}
|
||||
|
||||
qint32
|
||||
SWGFT8DemodSettings::getRecordWav() {
|
||||
return record_wav;
|
||||
}
|
||||
void
|
||||
SWGFT8DemodSettings::setRecordWav(qint32 record_wav) {
|
||||
this->record_wav = record_wav;
|
||||
this->m_record_wav_isSet = true;
|
||||
}
|
||||
|
||||
qint32
|
||||
SWGFT8DemodSettings::getLogMessages() {
|
||||
return log_messages;
|
||||
}
|
||||
void
|
||||
SWGFT8DemodSettings::setLogMessages(qint32 log_messages) {
|
||||
this->log_messages = log_messages;
|
||||
this->m_log_messages_isSet = true;
|
||||
}
|
||||
|
||||
qint32
|
||||
SWGFT8DemodSettings::getNbDecoderThreads() {
|
||||
return nb_decoder_threads;
|
||||
}
|
||||
void
|
||||
SWGFT8DemodSettings::setNbDecoderThreads(qint32 nb_decoder_threads) {
|
||||
this->nb_decoder_threads = nb_decoder_threads;
|
||||
this->m_nb_decoder_threads_isSet = true;
|
||||
}
|
||||
|
||||
float
|
||||
SWGFT8DemodSettings::getDecoderTimeBudget() {
|
||||
return decoder_time_budget;
|
||||
}
|
||||
void
|
||||
SWGFT8DemodSettings::setDecoderTimeBudget(float decoder_time_budget) {
|
||||
this->decoder_time_budget = decoder_time_budget;
|
||||
this->m_decoder_time_budget_isSet = true;
|
||||
}
|
||||
|
||||
qint32
|
||||
SWGFT8DemodSettings::getRgbColor() {
|
||||
return rgb_color;
|
||||
@ -492,6 +572,18 @@ SWGFT8DemodSettings::isSet(){
|
||||
if(m_agc_isSet){
|
||||
isObjectUpdated = true; break;
|
||||
}
|
||||
if(m_record_wav_isSet){
|
||||
isObjectUpdated = true; break;
|
||||
}
|
||||
if(m_log_messages_isSet){
|
||||
isObjectUpdated = true; break;
|
||||
}
|
||||
if(m_nb_decoder_threads_isSet){
|
||||
isObjectUpdated = true; break;
|
||||
}
|
||||
if(m_decoder_time_budget_isSet){
|
||||
isObjectUpdated = true; break;
|
||||
}
|
||||
if(m_rgb_color_isSet){
|
||||
isObjectUpdated = true; break;
|
||||
}
|
||||
|
@ -69,6 +69,18 @@ public:
|
||||
qint32 getAgc();
|
||||
void setAgc(qint32 agc);
|
||||
|
||||
qint32 getRecordWav();
|
||||
void setRecordWav(qint32 record_wav);
|
||||
|
||||
qint32 getLogMessages();
|
||||
void setLogMessages(qint32 log_messages);
|
||||
|
||||
qint32 getNbDecoderThreads();
|
||||
void setNbDecoderThreads(qint32 nb_decoder_threads);
|
||||
|
||||
float getDecoderTimeBudget();
|
||||
void setDecoderTimeBudget(float decoder_time_budget);
|
||||
|
||||
qint32 getRgbColor();
|
||||
void setRgbColor(qint32 rgb_color);
|
||||
|
||||
@ -130,6 +142,18 @@ private:
|
||||
qint32 agc;
|
||||
bool m_agc_isSet;
|
||||
|
||||
qint32 record_wav;
|
||||
bool m_record_wav_isSet;
|
||||
|
||||
qint32 log_messages;
|
||||
bool m_log_messages_isSet;
|
||||
|
||||
qint32 nb_decoder_threads;
|
||||
bool m_nb_decoder_threads_isSet;
|
||||
|
||||
float decoder_time_budget;
|
||||
bool m_decoder_time_budget_isSet;
|
||||
|
||||
qint32 rgb_color;
|
||||
bool m_rgb_color_isSet;
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user