Compare commits
3 Commits
macos
...
9ced0da0b2
| Author | SHA1 | Date | |
|---|---|---|---|
| 9ced0da0b2 | |||
| 73589204f5 | |||
| 6ffa76139c |
@@ -223,9 +223,10 @@ without logging out.
|
||||
previous two UTC days if today's stream file is unavailable. Current and next-minute satellite
|
||||
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
|
||||
line shows the UTC time of the last successful download or cache load.
|
||||
- Complete UART channel snapshots such as `tracked satellites=5 prns=[...]` replace the previous
|
||||
connected-PRN set, so satellites that were lost no longer remain green.
|
||||
line shows the system-local time of the last successful download or cache load.
|
||||
- Complete UART channel snapshots such as `tracked satellites=5 prn_indices=[...]` replace the
|
||||
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
|
||||
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,
|
||||
|
||||
+22
-8
@@ -234,10 +234,11 @@ QSet<QString> LocationParser::parseGpsPrns(const QString &line)
|
||||
QSet<QString> result;
|
||||
const auto addPrn = [&result](QString value, bool zeroBased = false) {
|
||||
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;
|
||||
int number = value.toInt(&ok);
|
||||
if (ok && zeroBased) ++number;
|
||||
if (ok && zeroBased && !explicitGpsPrn) ++number;
|
||||
if (ok && number >= 1 && number <= 63) {
|
||||
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);
|
||||
while (gpsMatches.hasNext()) addPrn(gpsMatches.next().captured(QStringLiteral("prn")));
|
||||
|
||||
static const QRegularExpression labeledList(
|
||||
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*[\])]?)"),
|
||||
// Current firmware versions called their zero-based hardware channel values
|
||||
// "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);
|
||||
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);
|
||||
auto listMatches = labeledList.globalMatch(line);
|
||||
while (listMatches.hasNext()) {
|
||||
const QRegularExpressionMatch listMatch = listMatches.next();
|
||||
const bool zeroBased = listMatch.captured(QStringLiteral("label")).startsWith(
|
||||
QStringLiteral("SVN"), Qt::CaseInsensitive);
|
||||
QString label = listMatch.captured(QStringLiteral("label")).toUpper();
|
||||
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")));
|
||||
while (values.hasNext()) addPrn(values.next().captured(1), zeroBased);
|
||||
while (values.hasNext()) addPrn(values.next().captured(0), zeroBased);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
+1
-1
@@ -520,7 +520,7 @@ void MainWindow::handleSerialLine(const QString &line)
|
||||
QRegularExpression::CaseInsensitiveOption);
|
||||
static const QRegularExpression trackingSnapshotRegex(
|
||||
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);
|
||||
const bool solveStarted = solveStartRegex.match(line.trimmed()).hasMatch();
|
||||
const bool trackingSnapshot = trackingSnapshotRegex.match(line).hasMatch();
|
||||
|
||||
@@ -158,10 +158,11 @@ void RinexNavigation::handleDownload(const QByteArray &compressed, const QDate &
|
||||
m_ephemerides = std::move(ephemerides);
|
||||
setLoading(false);
|
||||
publishCurrentMotion();
|
||||
emit statusChanged(QStringLiteral("%1 GPS uydusu · RINEX %2 · %3 UTC")
|
||||
emit statusChanged(QStringLiteral("%1 GPS uydusu · RINEX %2 · %3")
|
||||
.arg(positions.size())
|
||||
.arg(date.toString(QStringLiteral("dd.MM.yyyy")))
|
||||
.arg(now.time().toString(QStringLiteral("HH:mm:ss"))), false);
|
||||
.arg(now.toLocalTime().toString(
|
||||
QStringLiteral("HH:mm:ss t"))), false);
|
||||
}
|
||||
|
||||
bool RinexNavigation::loadCache()
|
||||
@@ -176,9 +177,10 @@ bool RinexNavigation::loadCache()
|
||||
if (positions.isEmpty()) return false;
|
||||
m_ephemerides = std::move(ephemerides);
|
||||
publishCurrentMotion();
|
||||
emit statusChanged(QStringLiteral("%1 GPS uydusu · onbellekteki RINEX · %2 UTC")
|
||||
emit statusChanged(QStringLiteral("%1 GPS uydusu · onbellekteki RINEX · %2")
|
||||
.arg(positions.size())
|
||||
.arg(now.time().toString(QStringLiteral("HH:mm:ss"))), true);
|
||||
.arg(now.toLocalTime().toString(
|
||||
QStringLiteral("HH:mm:ss t"))), true);
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
@@ -89,13 +89,13 @@ int main(int argc, char **argv)
|
||||
assert(sideSplitter != nullptr);
|
||||
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);
|
||||
auto *globe = window.findChild<GlobeWidget *>();
|
||||
assert(globe != nullptr);
|
||||
assert(globe->observedPrns()
|
||||
== 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(QMetaObject::invokeMethod(map, "referenceLocationSelected", Qt::DirectConnection,
|
||||
|
||||
@@ -186,6 +186,25 @@ int main(int argc, char **argv)
|
||||
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(
|
||||
"[OK] [NAV] DOP GDOP=7.0907891622 PDOP=5.6333862175 "
|
||||
@@ -257,6 +276,17 @@ int main(int argc, char **argv)
|
||||
assert(closeTo(satellites[0].x, semiMajorAxis * std::cos(node), 0.1));
|
||||
assert(closeTo(satellites[0].y, semiMajorAxis * std::sin(node), 0.1));
|
||||
assert(closeTo(satellites[0].z, 0.0, 0.1));
|
||||
|
||||
// A local wall-clock representation must not move the satellites.
|
||||
// 11:48 at UTC+03 and 08:48 UTC represent the same physical instant.
|
||||
const auto localTimeSatellites = RinexNavigation::parseGpsNavigation(
|
||||
syntheticGpsNavigation(gpsLabel, gpsWeek, toe),
|
||||
matchingUtc.toOffsetFromUtc(3 * 60 * 60), &error);
|
||||
assert(error.isEmpty());
|
||||
assert(localTimeSatellites.size() == satellites.size());
|
||||
assert(closeTo(localTimeSatellites[0].x, satellites[0].x, 0.001));
|
||||
assert(closeTo(localTimeSatellites[0].y, satellites[0].y, 0.001));
|
||||
assert(closeTo(localTimeSatellites[0].z, satellites[0].z, 0.001));
|
||||
}
|
||||
|
||||
const QString rinexPath = qEnvironmentVariable("LOCATION_PLOTTER_TEST_RINEX");
|
||||
|
||||
Reference in New Issue
Block a user