Normalize zero-based firmware satellite indices
Ubuntu build / build-and-test (ubuntu-22.04) (push) Has been cancelled
Ubuntu build / build-and-test (ubuntu-24.04) (push) Has been cancelled

This commit is contained in:
2026-07-24 12:06:09 +03:00
parent 73589204f5
commit 9ced0da0b2
5 changed files with 47 additions and 13 deletions
+3 -2
View File
@@ -224,8 +224,9 @@ without logging out.
positions are propagated from the cached ephemeris, then smoothly interpolated at 10 FPS. positions are propagated from the cached ephemeris, then smoothly interpolated at 10 FPS.
- The 3D Earth tab includes a `RINEX Guncelle` button for an immediate manual refresh. Its status - The 3D Earth tab includes a `RINEX Guncelle` button for an immediate manual refresh. Its status
line shows the system-local time of the last successful download or cache load. line shows the system-local time of the last successful download or cache load.
- Complete UART channel snapshots such as `tracked satellites=5 prns=[...]` replace the previous - Complete UART channel snapshots such as `tracked satellites=5 prn_indices=[...]` replace the
connected-PRN set, so satellites that were lost no longer remain green. previous connected-PRN set, so satellites that were lost no longer remain green. Firmware
channel indices are zero-based and are normalized for display (`0` is GPS PRN `G01`).
- RINEX text is parsed once per download and retained as compact ephemeris records. The static - RINEX text is parsed once per download and retained as compact ephemeris records. The static
globe is cached between camera changes, reducing CPU and allocation load during orbit animation. globe is cached between camera changes, reducing CPU and allocation load during orbit animation.
- The globe uses an embedded Natural Earth coastline mask with terrain-like land colouring, - The globe uses an embedded Natural Earth coastline mask with terrain-like land colouring,
+22 -8
View File
@@ -234,10 +234,11 @@ QSet<QString> LocationParser::parseGpsPrns(const QString &line)
QSet<QString> result; QSet<QString> result;
const auto addPrn = [&result](QString value, bool zeroBased = false) { const auto addPrn = [&result](QString value, bool zeroBased = false) {
value = value.trimmed().toUpper(); value = value.trimmed().toUpper();
if (value.startsWith(QLatin1Char('G'))) value.remove(0, 1); const bool explicitGpsPrn = value.startsWith(QLatin1Char('G'));
if (explicitGpsPrn) value.remove(0, 1);
bool ok = false; bool ok = false;
int number = value.toInt(&ok); int number = value.toInt(&ok);
if (ok && zeroBased) ++number; if (ok && zeroBased && !explicitGpsPrn) ++number;
if (ok && number >= 1 && number <= 63) { if (ok && number >= 1 && number <= 63) {
result.insert(QStringLiteral("G%1").arg(number, 2, 10, QLatin1Char('0'))); result.insert(QStringLiteral("G%1").arg(number, 2, 10, QLatin1Char('0')));
} }
@@ -249,18 +250,31 @@ QSet<QString> LocationParser::parseGpsPrns(const QString &line)
auto gpsMatches = gpsToken.globalMatch(line); auto gpsMatches = gpsToken.globalMatch(line);
while (gpsMatches.hasNext()) addPrn(gpsMatches.next().captured(QStringLiteral("prn"))); while (gpsMatches.hasNext()) addPrn(gpsMatches.next().captured(QStringLiteral("prn")));
static const QRegularExpression labeledList( // Current firmware versions called their zero-based hardware channel values
QStringLiteral(R"((?<label>\bPRNS?\b|\bSVS?\b|\bSVNS?\b|\bSVIDS?\b)\s*(?:=|:|->)?\s*[\[(]?\s*(?<list>(?:G?\d{1,2})(?:\s*[,;/| ]\s*G?\d{1,2})*)\s*[\])]?)"), // "prns" in complete tracking snapshots. Keep accepting those messages while
// newer firmware uses the unambiguous "prn_indices" label.
static const QRegularExpression legacyTrackingIndices(
QStringLiteral(R"(\btracked\s+satellites\s*=\s*\d+\s+prns?\s*=)"),
QRegularExpression::CaseInsensitiveOption); QRegularExpression::CaseInsensitiveOption);
static const QRegularExpression number(QStringLiteral(R"(G?(\d{1,2}))"), const bool legacyZeroBasedPrns = legacyTrackingIndices.match(line).hasMatch();
static const QRegularExpression labeledList(
QStringLiteral(R"((?<label>\bPRN[_ -]?INDICES\b|\bPRNS?\b|\bSVS?\b|\bSVNS?\b|\bSVIDS?\b)\s*(?:=|:|->)?\s*[\[(]?\s*(?<list>(?:G?\d{1,2})(?:\s*[,;/| ]\s*G?\d{1,2})*)\s*[\])]?)"),
QRegularExpression::CaseInsensitiveOption);
static const QRegularExpression number(QStringLiteral(R"(G?\d{1,2})"),
QRegularExpression::CaseInsensitiveOption); QRegularExpression::CaseInsensitiveOption);
auto listMatches = labeledList.globalMatch(line); auto listMatches = labeledList.globalMatch(line);
while (listMatches.hasNext()) { while (listMatches.hasNext()) {
const QRegularExpressionMatch listMatch = listMatches.next(); const QRegularExpressionMatch listMatch = listMatches.next();
const bool zeroBased = listMatch.captured(QStringLiteral("label")).startsWith( QString label = listMatch.captured(QStringLiteral("label")).toUpper();
QStringLiteral("SVN"), Qt::CaseInsensitive); label.remove(QLatin1Char('_'));
label.remove(QLatin1Char('-'));
label.remove(QLatin1Char(' '));
const bool zeroBased = label.startsWith(QStringLiteral("SVN"))
|| label == QStringLiteral("PRNINDICES")
|| (legacyZeroBasedPrns && label.startsWith(QStringLiteral("PRN")));
auto values = number.globalMatch(listMatch.captured(QStringLiteral("list"))); auto values = number.globalMatch(listMatch.captured(QStringLiteral("list")));
while (values.hasNext()) addPrn(values.next().captured(1), zeroBased); while (values.hasNext()) addPrn(values.next().captured(0), zeroBased);
} }
return result; return result;
} }
+1 -1
View File
@@ -520,7 +520,7 @@ void MainWindow::handleSerialLine(const QString &line)
QRegularExpression::CaseInsensitiveOption); QRegularExpression::CaseInsensitiveOption);
static const QRegularExpression trackingSnapshotRegex( static const QRegularExpression trackingSnapshotRegex(
QStringLiteral( QStringLiteral(
R"(\btracked\s+satellites\s*=\s*\d+\s+(?:prns?|svns?)\s*=\s*\[[^\]]*\])"), R"(\btracked\s+satellites\s*=\s*\d+\s+(?:prn[_ -]?indices|prns?|svns?)\s*=\s*\[[^\]]*\])"),
QRegularExpression::CaseInsensitiveOption); QRegularExpression::CaseInsensitiveOption);
const bool solveStarted = solveStartRegex.match(line.trimmed()).hasMatch(); const bool solveStarted = solveStartRegex.match(line.trimmed()).hasMatch();
const bool trackingSnapshot = trackingSnapshotRegex.match(line).hasMatch(); const bool trackingSnapshot = trackingSnapshotRegex.match(line).hasMatch();
+2 -2
View File
@@ -89,13 +89,13 @@ int main(int argc, char **argv)
assert(sideSplitter != nullptr); assert(sideSplitter != nullptr);
assert(sideSplitter->orientation() == Qt::Vertical); assert(sideSplitter->orientation() == Qt::Vertical);
sendLine(window, QStringLiteral("[TRK] tracked satellites=2 prns=[3,8]")); sendLine(window, QStringLiteral("[TRK] tracked satellites=2 prn_indices=[2,7]"));
assert(map != nullptr); assert(map != nullptr);
auto *globe = window.findChild<GlobeWidget *>(); auto *globe = window.findChild<GlobeWidget *>();
assert(globe != nullptr); assert(globe != nullptr);
assert(globe->observedPrns() assert(globe->observedPrns()
== QSet<QString>({QStringLiteral("G03"), QStringLiteral("G08")})); == QSet<QString>({QStringLiteral("G03"), QStringLiteral("G08")}));
sendLine(window, QStringLiteral("[TRK] tracked satellites=1 prns=[14]")); sendLine(window, QStringLiteral("[TRK] tracked satellites=1 prn_indices=[13]"));
assert(globe->observedPrns() == QSet<QString>({QStringLiteral("G14")})); assert(globe->observedPrns() == QSet<QString>({QStringLiteral("G14")}));
assert(QMetaObject::invokeMethod(map, "referenceLocationSelected", Qt::DirectConnection, assert(QMetaObject::invokeMethod(map, "referenceLocationSelected", Qt::DirectConnection,
+19
View File
@@ -186,6 +186,25 @@ int main(int argc, char **argv)
assert(prns == QSet<QString>({QStringLiteral("G01"), QStringLiteral("G32")})); assert(prns == QSet<QString>({QStringLiteral("G01"), QStringLiteral("G32")}));
} }
{
const QSet<QString> prns = LocationParser::parseGpsPrns(
QStringLiteral("[TRK] tracked satellites=3 prn_indices=[0,7,31]"));
assert(prns == QSet<QString>({QStringLiteral("G01"), QStringLiteral("G08"),
QStringLiteral("G32")}));
}
{
const QSet<QString> prns = LocationParser::parseGpsPrns(
QStringLiteral("[TRK] tracked satellites=2 prns=[0,7]"));
assert(prns == QSet<QString>({QStringLiteral("G01"), QStringLiteral("G08")}));
}
{
const QSet<QString> prns = LocationParser::parseGpsPrns(
QStringLiteral("[TRK] tracked satellites=2 prn_indices=[0,G08]"));
assert(prns == QSet<QString>({QStringLiteral("G01"), QStringLiteral("G08")}));
}
{ {
const auto parsed = LocationParser::parseLine(QStringLiteral( const auto parsed = LocationParser::parseLine(QStringLiteral(
"[OK] [NAV] DOP GDOP=7.0907891622 PDOP=5.6333862175 " "[OK] [NAV] DOP GDOP=7.0907891622 PDOP=5.6333862175 "