Compare commits
3 Commits
8b7256ed9d
...
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
|
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.
|
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 UTC 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
@@ -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
@@ -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();
|
||||||
|
|||||||
@@ -158,10 +158,11 @@ void RinexNavigation::handleDownload(const QByteArray &compressed, const QDate &
|
|||||||
m_ephemerides = std::move(ephemerides);
|
m_ephemerides = std::move(ephemerides);
|
||||||
setLoading(false);
|
setLoading(false);
|
||||||
publishCurrentMotion();
|
publishCurrentMotion();
|
||||||
emit statusChanged(QStringLiteral("%1 GPS uydusu · RINEX %2 · %3 UTC")
|
emit statusChanged(QStringLiteral("%1 GPS uydusu · RINEX %2 · %3")
|
||||||
.arg(positions.size())
|
.arg(positions.size())
|
||||||
.arg(date.toString(QStringLiteral("dd.MM.yyyy")))
|
.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()
|
bool RinexNavigation::loadCache()
|
||||||
@@ -176,9 +177,10 @@ bool RinexNavigation::loadCache()
|
|||||||
if (positions.isEmpty()) return false;
|
if (positions.isEmpty()) return false;
|
||||||
m_ephemerides = std::move(ephemerides);
|
m_ephemerides = std::move(ephemerides);
|
||||||
publishCurrentMotion();
|
publishCurrentMotion();
|
||||||
emit statusChanged(QStringLiteral("%1 GPS uydusu · onbellekteki RINEX · %2 UTC")
|
emit statusChanged(QStringLiteral("%1 GPS uydusu · onbellekteki RINEX · %2")
|
||||||
.arg(positions.size())
|
.arg(positions.size())
|
||||||
.arg(now.time().toString(QStringLiteral("HH:mm:ss"))), true);
|
.arg(now.toLocalTime().toString(
|
||||||
|
QStringLiteral("HH:mm:ss t"))), true);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -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,
|
||||||
|
|||||||
@@ -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 "
|
||||||
@@ -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].x, semiMajorAxis * std::cos(node), 0.1));
|
||||||
assert(closeTo(satellites[0].y, semiMajorAxis * std::sin(node), 0.1));
|
assert(closeTo(satellites[0].y, semiMajorAxis * std::sin(node), 0.1));
|
||||||
assert(closeTo(satellites[0].z, 0.0, 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");
|
const QString rinexPath = qEnvironmentVariable("LOCATION_PLOTTER_TEST_RINEX");
|
||||||
|
|||||||
Reference in New Issue
Block a user