diff --git a/ExoMy_Software-master/gui/admin.html b/ExoMy_Software-master/gui/admin.html
index 95842b1..4b82cfd 100644
--- a/ExoMy_Software-master/gui/admin.html
+++ b/ExoMy_Software-master/gui/admin.html
@@ -427,6 +427,21 @@
Für eine gute Fix-Qualität sind niedrige DOP-Werte und hohe, gleichmäßig verteilte Satelliten sinnvoll. Der Himmelsplot zeigt nur Satelliten, die der Empfänger per GSV mit Richtung und Höhe meldet.
+
+
+
+
+
GPS-Rohdaten
+
NMEA
+
+
+ Letzte direkt empfangene NMEA-Sätze des Empfängers. Die Ansicht scrollt intern und ist für Diagnose bei Abschattung, Drift und fehlerhaften Fixen gedacht.
+
+
+ Rohdaten kopieren
+
+
+
@@ -663,6 +678,7 @@
utcTime: null,
lastFixAgeS: null,
satellites: [],
+ rawSentences: [],
selectedSatellitePrn: null,
plottedSatellites: []
};
@@ -734,6 +750,7 @@
gpsDiagnosticsState.utcTime = null;
gpsDiagnosticsState.lastFixAgeS = null;
gpsDiagnosticsState.satellites = [];
+ gpsDiagnosticsState.rawSentences = [];
updateGpsDiagnosticsPanel();
}
@@ -1003,9 +1020,47 @@
setGpsQualityState('gps_diag_vdop', gpsDiagnosticsState.vdop);
setText('gps_diag_sat_used', gpsDiagnosticsState.satellitesUsed);
setText('gps_diag_sat_visible', gpsDiagnosticsState.satellitesVisible);
+ setGpsRawOutput(gpsDiagnosticsState.rawSentences);
drawGpsSkyPlot();
}
+ function setGpsRawOutput(sentences) {
+ var node = document.getElementById('gps_raw_output');
+ if (!node) return;
+ if (!Array.isArray(sentences) || !sentences.length) {
+ node.value = '--';
+ return;
+ }
+ var wasNearBottom = (node.scrollTop + node.clientHeight + 24) >= node.scrollHeight;
+ node.value = sentences.join('\n');
+ if (wasNearBottom) node.scrollTop = node.scrollHeight;
+ }
+
+ async function copyGpsRawData() {
+ var node = document.getElementById('gps_raw_output');
+ if (!node) return;
+ var text = String(node.value || '').trim();
+ if (!text || text === '--') {
+ setAdminStatus('Keine GPS-Rohdaten zum Kopieren');
+ return;
+ }
+
+ try {
+ if (navigator.clipboard && navigator.clipboard.writeText) {
+ await navigator.clipboard.writeText(text);
+ } else {
+ node.focus();
+ node.select();
+ document.execCommand('copy');
+ node.setSelectionRange(node.value.length, node.value.length);
+ }
+ setAdminStatus('GPS-Rohdaten kopiert');
+ window.setTimeout(function() { setAdminStatus('Bereit'); }, 1800);
+ } catch (error) {
+ setAdminStatus('Kopieren fehlgeschlagen');
+ }
+ }
+
function getSelectedSatellite() {
if (!gpsDiagnosticsState.satellites.length) return null;
var selected = gpsDiagnosticsState.satellites.find(function(item) {
@@ -1172,6 +1227,7 @@
gpsDiagnosticsState.utcTime = data.utc_time || null;
gpsDiagnosticsState.lastFixAgeS = typeof data.last_fix_age_s === 'number' ? data.last_fix_age_s : null;
gpsDiagnosticsState.satellites = Array.isArray(data.satellites) ? data.satellites : [];
+ gpsDiagnosticsState.rawSentences = Array.isArray(data.raw_sentences) ? data.raw_sentences : [];
if (gpsDiagnosticsState.selectedSatellitePrn) {
var stillExists = gpsDiagnosticsState.satellites.some(function(item) {
return item.prn === gpsDiagnosticsState.selectedSatellitePrn;
diff --git a/ExoMy_Software-master/gui/app.css b/ExoMy_Software-master/gui/app.css
index 3ff9903..92b1bd9 100644
--- a/ExoMy_Software-master/gui/app.css
+++ b/ExoMy_Software-master/gui/app.css
@@ -1295,6 +1295,25 @@ body.page-admin .gps-copy{
body.page-admin .gps-copy_compact{
border-bottom:none;
}
+body.page-admin .gps-raw-output{
+ width:100%;
+ min-height:220px;
+ max-height:320px;
+ resize:vertical;
+ overflow:auto;
+ margin-top:14px;
+ padding:12px 14px;
+ border:1px solid rgba(0,255,145,.18);
+ border-radius:12px;
+ background:#07111c;
+ color:#b8ffe1;
+ font:12px/1.45 Consolas, "Courier New", monospace;
+ box-shadow:inset 0 0 0 1px rgba(255,255,255,.02);
+}
+body.page-admin .gps-raw-output:focus{
+ outline:none;
+ border-color:rgba(0,255,145,.28);
+}
body.page-admin .gps-sky-stage{
padding:18px;
position:relative;
diff --git a/ExoMy_Software-master/gui/index.html b/ExoMy_Software-master/gui/index.html
index 3ac7641..4d21960 100644
--- a/ExoMy_Software-master/gui/index.html
+++ b/ExoMy_Software-master/gui/index.html
@@ -285,6 +285,7 @@ var roverCommandListener = null;
var motorCommandsListener = null;
var gpsFixListener = null;
var gpsStatusListener = null;
+var gpsDiagnosticsListener = null;
var publishTimer = null;
var statusTimer = null;
var axes = [0, 0, 0, 0, 0, 0];
@@ -294,6 +295,7 @@ var motorsEnabled = true;
var inputMode = "joystick";
var metBase = Date.now();
var dragging = false;
+var lastGpsDiagnostics = null;
var REQUIRED_CALIBRATION_POINTS = [0, 20, 30, 40, 50, 60, 80, 100];
var driveEstimator = {
drivePwmNeutralAverage: 307,
@@ -603,41 +605,86 @@ function updateAxesDisplay(x, y) {
setText("dpy", formatAxis(y));
}
-function setGpsAccuracyDisplay(valueMeters) {
+function setGpsAccuracyDisplay(label) {
var node = document.getElementById("gps-accuracy");
if (!node) return;
node.classList.remove("gps-quality-good", "gps-quality-mid", "gps-quality-bad");
- if (typeof valueMeters !== "number" || isNaN(valueMeters) || valueMeters <= 0) {
+ if (typeof label !== "string" || !label) {
node.textContent = "--";
return;
}
- node.textContent = formatNumber(valueMeters, 1) + " m";
- if (valueMeters <= 1.0) {
+ node.textContent = label;
+}
+
+function setGpsAccuracyState(label, tone) {
+ var node = document.getElementById("gps-accuracy");
+ if (!node) return;
+
+ node.classList.remove("gps-quality-good", "gps-quality-mid", "gps-quality-bad");
+ node.textContent = label || "--";
+ if (tone === "good") {
node.classList.add("gps-quality-good");
- } else if (valueMeters <= 3.0) {
+ } else if (tone === "mid") {
node.classList.add("gps-quality-mid");
- } else {
+ } else if (tone === "bad") {
node.classList.add("gps-quality-bad");
}
}
+function updateGpsAccuracyFromDiagnostics() {
+ if (!lastGpsDiagnostics || !lastGpsDiagnostics.connected || !lastGpsDiagnostics.valid) {
+ setGpsAccuracyState("--", "");
+ return;
+ }
+
+ var fixQuality = Number(lastGpsDiagnostics.fix_quality || 0);
+ var hdop = Number(lastGpsDiagnostics.hdop);
+ var pdop = Number(lastGpsDiagnostics.pdop);
+ var satellitesUsed = Number(lastGpsDiagnostics.satellites_used || 0);
+ var lastFixAge = Number(lastGpsDiagnostics.last_fix_age_s || 0);
+
+ if (fixQuality <= 0 || isNaN(hdop)) {
+ setGpsAccuracyState("schlecht", "bad");
+ return;
+ }
+
+ var label = "schlecht";
+ var tone = "bad";
+
+ if (hdop <= 1.2 && (isNaN(pdop) || pdop <= 2.0) && satellitesUsed >= 10 && lastFixAge <= 2.0) {
+ label = "sehr gut";
+ tone = "good";
+ } else if (hdop <= 2.5 && (isNaN(pdop) || pdop <= 4.0) && satellitesUsed >= 6 && lastFixAge <= 4.0) {
+ label = "gut";
+ tone = "good";
+ } else if (hdop <= 4.5 && (isNaN(pdop) || pdop <= 7.0) && satellitesUsed >= 4 && lastFixAge <= 8.0) {
+ label = "mäßig";
+ tone = "mid";
+ }
+
+ if (satellitesUsed < 5 || lastFixAge > 8.0) {
+ label = "schlecht";
+ tone = "bad";
+ }
+
+ setGpsAccuracyState(label, tone);
+}
+
function updateGpsOverlayFromFix(message) {
if (!message) return;
markGpsFresh();
var hasFix = message.status && message.status.status >= 0;
if (!hasFix) {
- setGpsAccuracyDisplay(NaN);
+ setGpsAccuracyState("--", "");
setText("gps-lat", "--");
setText("gps-lon", "--");
return;
}
- var covariance = Array.isArray(message.position_covariance) ? Number(message.position_covariance[0]) : NaN;
- var accuracyMeters = covariance > 0 ? Math.sqrt(covariance) : NaN;
- setGpsAccuracyDisplay(accuracyMeters);
+ updateGpsAccuracyFromDiagnostics();
setText("gps-lat", formatNumber(Number(message.latitude || 0), 5));
setText("gps-lon", formatNumber(Number(message.longitude || 0), 5));
}
@@ -648,6 +695,15 @@ function updateGpsOverlayFromStatus(text) {
setText("gps-state", value);
}
+function updateGpsOverlayFromDiagnostics(text) {
+ try {
+ lastGpsDiagnostics = JSON.parse(String(text || "{}"));
+ } catch (error) {
+ lastGpsDiagnostics = null;
+ }
+ updateGpsAccuracyFromDiagnostics();
+}
+
function updateModeDisplay() {
var label = "Ackermann";
if (driveMode === "turn-on-point") {
@@ -748,7 +804,7 @@ function clearRosLiveDisplay() {
liveIndicator.classList.add("is-offline");
}
setText("gps-state", "Offline");
- setGpsAccuracyDisplay(NaN);
+ setGpsAccuracyState("--", "");
setText("gps-lat", "--");
setText("gps-lon", "--");
setText("overlay-speed", "--");
@@ -1226,6 +1282,15 @@ window.addEventListener("load", function () {
updateGpsOverlayFromStatus(message.data);
});
+ gpsDiagnosticsListener = new ROSLIB.Topic({
+ ros: ros,
+ name: "/gps/diagnostics_json",
+ messageType: "std_msgs/String"
+ });
+ gpsDiagnosticsListener.subscribe(function (message) {
+ updateGpsOverlayFromDiagnostics(message.data);
+ });
+
var joySubscriber = new ROSLIB.Topic({
ros: ros,
name: "/joy",
diff --git a/ExoMy_Software-master/src/gps_node.py b/ExoMy_Software-master/src/gps_node.py
index 7d91624..7dcce5d 100644
--- a/ExoMy_Software-master/src/gps_node.py
+++ b/ExoMy_Software-master/src/gps_node.py
@@ -4,6 +4,7 @@ import json
import os
import select
import termios
+from collections import deque
from datetime import datetime
import rospy
@@ -86,6 +87,7 @@ class GpsState(object):
self.visible_satellites = {}
self.visible_satellite_count = 0
self._gsv_groups = {}
+ self.raw_sentences = deque(maxlen=200)
def summary_text(self):
if not self.connected:
@@ -334,6 +336,8 @@ def parse_sentence(sentence, state):
if not verify_checksum(sentence):
return False
+ state.raw_sentences.append(sentence)
+
fields = sentence.split('*', 1)[0].split(',')
if not fields:
return False
@@ -379,7 +383,8 @@ def build_diagnostics_payload(state, frame_id):
'utc_time': state.utc_datetime.strftime('%H:%M:%S') if state.utc_datetime else None,
'utc_date': state.utc_datetime.strftime('%Y-%m-%d') if state.utc_datetime else None,
'last_fix_age_s': last_fix_age,
- 'satellites': list(state.visible_satellites.values())
+ 'satellites': list(state.visible_satellites.values()),
+ 'raw_sentences': list(state.raw_sentences)
}