/////////////////////////////////////////////////////////////////////////////////// // Copyright (C) 2024 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 . // /////////////////////////////////////////////////////////////////////////////////// #include #include #include #include #include #include "mainbench.h" #ifndef HAS_FT8 void MainBench::testFT8(const QString& wavFile, const QString& argsStr) { (void) wavFile; (void) argsStr; qWarning("MainBench::testFT8: this version has no FT8 support"); } #else #include "ft8/ft8.h" #include "ft8/packing.h" class TestFT8Protocols { public: static void testMsg1(const QStringList& argElements, bool runLDPC = false); static void testMsg00(const QStringList& argElements, bool runLDPC = false); static void testOnesZeroes(const QStringList& argElements); static void testSoftDecode(const QStringList& argElements); private: static bool testLDPC(int a77[]); static bool compareBits(int a[], int r[], int nbBits = 174); static void debugIntArray(int a[], int length); }; void MainBench::testFT8Protocols(const QString& argsStr) { QStringList argElements = argsStr.split(','); // comma separated list of arguments if (argElements.size() == 0) { qWarning("MainBench::testFT8Protocols: no arguments"); return; } QString& testType = argElements[0]; if (testType == "msg1") { TestFT8Protocols::testMsg1(argElements); // type 1 message test } else if (testType == "msg00") { TestFT8Protocols::testMsg00(argElements); // type 0.0 message test } else if (testType == "msg1L") { TestFT8Protocols::testMsg1(argElements, true); // type 1 message test with LDPC encoding/decoding test } else if (testType == "msg00L") { TestFT8Protocols::testMsg00(argElements, true); // type 0.0 message test with LDPC encoding/decoding test } else if (testType == "zeroones") { TestFT8Protocols::testOnesZeroes(argElements); } else if (testType == "softdec") { TestFT8Protocols::testSoftDecode(argElements); } else { qWarning("MainBench::testFT8Protocols: unrecognized test type"); } } void TestFT8Protocols::testMsg1(const QStringList& argElements, bool runLDPC) { if (argElements.size() < 4) { qWarning("TestFT8Protocols::testMsg1: missing callsigns and locator/report in argument"); return; } int c28_1, c28_2, g15; if (!FT8::Packing::packcall_std(c28_1, argElements[1].toStdString())) { qWarning("TestFT8Protocols::testMsg1: callsign %s is not a standard callsign", qPrintable(argElements[1])); return; } if (!FT8::Packing::packcall_std(c28_2, argElements[2].toStdString())) { qWarning("TestFT8Protocols::testMsg1: callsign %s is not a standard callsign", qPrintable(argElements[2])); return; } std::string locstr; int reply; if (argElements[3].startsWith("R+") || argElements[3].startsWith("R-")) { reply = 1; locstr = argElements[3].mid(1).toStdString(); } else { reply = 0; locstr = argElements[3].toStdString(); } if (!FT8::Packing::packgrid(g15, locstr)) { qWarning("TestFT8Protocols::testMsg1: locator or report %s is not valid", locstr.c_str()); return; } qDebug("TestFT8Protocols::testMsg1: c28_1: %d c28_2: %d g15: %d", c28_1, c28_2, g15); int a77[77]; std::fill(a77, a77 + 77, 0); FT8::Packing::pack1(a77, c28_1, c28_2, g15, reply); FT8::Packing packing; std::string call1, call2, loc; std::string msg = packing.unpack_1(a77, call1, call2, loc); qInfo("TestFT8Protocols::testMsg1: msg: %s, call1: %s, call2: %s, loc: %s", msg.c_str(), call1.c_str(), call2.c_str(), loc.c_str()); if (runLDPC) { if (testLDPC(a77)) { qInfo("TestFT8Protocols::testMsg1: LDPC test suceeded"); } else { qWarning("TestFT8Protocols::testMsg1: LDPC test failed"); } } } void TestFT8Protocols::testMsg00(const QStringList& argElements, bool runLDPC) { if (argElements.size() < 2) { qWarning("TestFT8Protocols::testMsg00: missing free text in argument"); return; } int a77[77]; std::fill(a77, a77 + 77, 0); if (!FT8::Packing::packfree(a77, argElements[1].toStdString())) { qWarning("TestFT8Protocols::testMsg00: message %s is not valid", qPrintable(argElements[1])); return; } std::string call1, call2, loc; std::string msg = FT8::Packing::unpack_0_0(a77, call1, call2, loc); qInfo("TestFT8Protocols::testMsg00: msg: %s, call1: %s", msg.c_str(), call1.c_str()); if (runLDPC) { if (testLDPC(a77)) { qInfo("TestFT8Protocols::testMsg00: LDPC test suceeded"); } else { qWarning("TestFT8Protocols::testMsg00: LDPC test failed"); } } } bool TestFT8Protocols::testLDPC(int a77[]) { int a174[174], r174[174]; FT8::FT8::encode(a174, a77); FT8::FT8Params ft8Params; float ll174[174]; FT8::FT8Params params; std::string comments; std::transform( a174, a174+174, ll174, [](const int& s) -> float { return s == 1 ? -1.0 : 1.0; } ); if (FT8::FT8::decode(ll174, r174, params, 0, comments) == 0) { qInfo("TestFT8Protocols::testLDPC(: LDPC or CRC check failed"); return false; } else { return compareBits(a174, r174); } } bool TestFT8Protocols::compareBits(int a[], int r[], int nbBits) { for (int i=0; i < nbBits; i++) { if (a[i] != r[i]) { qDebug("TestFT8Protocols::compareBits: failed at index %d: %d != %d", i, a[i], r[i]); return false; } } return true; } void TestFT8Protocols::debugIntArray(int a[], int length) { QString s; QTextStream os(&s); for (int i=0; i < length; i++) { os << a[i] << " "; } qDebug("TestFT8Protocols::debugIntArray: %s", qPrintable(s)); } void TestFT8Protocols::testOnesZeroes(const QStringList& argElements) { if (argElements.size() < 3) { qWarning("TestFT8Protocols::testOnesZeroes: not enough elements"); return; } int nbBits, bitIndex; bool intOK; nbBits = argElements[1].toInt(&intOK); if (!intOK) { qWarning("TestFT8Protocols::testOnesZeroes: first argument is not numeric: %s", qPrintable(argElements[1])); return; } bitIndex = argElements[2].toInt(&intOK); if (!intOK) { qWarning("TestFT8Protocols::testOnesZeroes: second argument is not numeric: %s", qPrintable(argElements[2])); return; } if (nbBits < 2) { qWarning("TestFT8Protocols::testOnesZeroes: nbBits too small: %d", nbBits); return; } bitIndex = bitIndex > nbBits - 1 ? nbBits - 1 : bitIndex; int *ones = new int[1< 12)) { qWarning("TestFT8Protocols::testSoftDecode: bits peer symbols invalid: %d", nbBits); return; } int symbolSize = 1< magSymbols(symbolSize); std::vector> mags; std::random_device rd; std::mt19937 gen(rd()); std::uniform_real_distribution dist(0.0, 0.01); for (int i = 2; i < argElements.size(); i++) { int symbol = argElements[i].toInt(&intOK); if (!intOK) { qWarning("TestFT8Protocols::testSoftDecode: symbol is not numeric: %s", qPrintable(argElements[i])); return; } for (auto& m : magSymbols) { m = 0.01 + dist(gen); } symbol = symbol % symbolSize; symbol = symbol ^(symbol >> 1); // Gray code magSymbols[symbol] += 0.015; mags.push_back(magSymbols); } QString s; QTextStream os(&s); qDebug("TestFT8Protocols::testSoftDecode: mags:"); for (const auto& magrow : mags) { for (const auto& mag : magrow) { os << mag << " "; } qDebug("TestFT8Protocols::testSoftDecode: %s", qPrintable(s)); s.clear(); } float *lls = new float[mags.size()*nbBits]; std::fill(lls, lls+mags.size()*nbBits, 0.0); FT8::FT8Params params; FT8::FT8::soft_decode_mags(params, mags, nbBits, lls); for (unsigned int si = 0; si < mags.size(); si++) { for (int biti = 0; biti < nbBits; biti++) { os << " " << lls[nbBits*si + biti]; } os << " "; } // for (unsigned int i = 0; i < mags.size()*nbBits; i++) { // os << " " << lls[i]; // } qInfo("TestFT8Protocols::testSoftDecode: lls: %s", qPrintable(s)); delete[] lls; } #endif