mirror of
https://github.com/smittix/intercept.git
synced 2026-05-17 13:24:50 -07:00
Harden APRS station plotting across payload variants
This commit is contained in:
@@ -429,7 +429,7 @@ def parse_position(data: str) -> Optional[dict]:
|
||||
data
|
||||
)
|
||||
|
||||
if pos_match:
|
||||
if pos_match:
|
||||
lat_deg = int(pos_match.group(1))
|
||||
lat_min = float(pos_match.group(2))
|
||||
lat_dir = pos_match.group(3)
|
||||
@@ -469,6 +469,49 @@ def parse_position(data: str) -> Optional[dict]:
|
||||
|
||||
return result
|
||||
|
||||
# Legacy/no-decimal variant occasionally seen in degraded decodes:
|
||||
# DDMMN/DDDMMW (symbol chars still present between/after coords).
|
||||
nodot_match = re.match(
|
||||
r'^(\d{2})(\d{2})([NS])(.)(\d{3})(\d{2})([EW])(.)?',
|
||||
data
|
||||
)
|
||||
if nodot_match:
|
||||
lat_deg = int(nodot_match.group(1))
|
||||
lat_min = float(nodot_match.group(2))
|
||||
lat_dir = nodot_match.group(3)
|
||||
symbol_table = nodot_match.group(4)
|
||||
lon_deg = int(nodot_match.group(5))
|
||||
lon_min = float(nodot_match.group(6))
|
||||
lon_dir = nodot_match.group(7)
|
||||
symbol_code = nodot_match.group(8) or ''
|
||||
|
||||
lat = lat_deg + lat_min / 60.0
|
||||
if lat_dir == 'S':
|
||||
lat = -lat
|
||||
|
||||
lon = lon_deg + lon_min / 60.0
|
||||
if lon_dir == 'W':
|
||||
lon = -lon
|
||||
|
||||
result = {
|
||||
'lat': round(lat, 6),
|
||||
'lon': round(lon, 6),
|
||||
'symbol': symbol_table + symbol_code,
|
||||
}
|
||||
|
||||
remaining = data[13:] if len(data) > 13 else ''
|
||||
|
||||
cs_match = re.search(r'(\d{3})/(\d{3})', remaining)
|
||||
if cs_match:
|
||||
result['course'] = int(cs_match.group(1))
|
||||
result['speed'] = int(cs_match.group(2))
|
||||
|
||||
alt_match = re.search(r'/A=(-?\d+)', remaining)
|
||||
if alt_match:
|
||||
result['altitude'] = int(alt_match.group(1))
|
||||
|
||||
return result
|
||||
|
||||
# Fallback: tolerate APRS ambiguity spaces in minute fields.
|
||||
# Example: 4903. N/07201. W
|
||||
if len(data) >= 18:
|
||||
|
||||
@@ -9488,8 +9488,13 @@
|
||||
if (Array.isArray(payload)) return payload;
|
||||
if (Array.isArray(payload.stations)) return payload.stations;
|
||||
if (Array.isArray(payload.data)) return payload.data;
|
||||
if (payload.data && Array.isArray(payload.data.stations)) return payload.data.stations;
|
||||
if (payload.data && Array.isArray(payload.data.data)) return payload.data.data;
|
||||
if (payload.result && Array.isArray(payload.result.stations)) return payload.result.stations;
|
||||
if (payload.result && Array.isArray(payload.result.data)) return payload.result.data;
|
||||
if (payload.data && payload.data.result && Array.isArray(payload.data.result.stations)) {
|
||||
return payload.data.result.stations;
|
||||
}
|
||||
return [];
|
||||
}
|
||||
|
||||
@@ -9508,6 +9513,8 @@
|
||||
stations.forEach((station) => {
|
||||
const callsign = String(station && station.callsign ? station.callsign : '').trim();
|
||||
if (!callsign) return;
|
||||
const lat = station.lat ?? station.latitude ?? null;
|
||||
const lon = station.lon ?? station.longitude ?? null;
|
||||
|
||||
const signature = getAprsStationSignature(station);
|
||||
if (aprsAgentStationSignatures.get(callsign) === signature) return;
|
||||
@@ -9525,12 +9532,50 @@
|
||||
processAprsPacket({
|
||||
type: 'aprs',
|
||||
...station,
|
||||
lat,
|
||||
lon,
|
||||
callsign,
|
||||
agent_name: station.agent_name || agentName || 'Remote Agent'
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
async function loadAprsStationSnapshot(isAgentMode = false) {
|
||||
try {
|
||||
const endpoint = (isAgentMode && aprsCurrentAgent)
|
||||
? `/controller/agents/${aprsCurrentAgent}/aprs/data`
|
||||
: '/aprs/stations';
|
||||
const response = await fetch(endpoint);
|
||||
if (!response.ok) return;
|
||||
const payload = await response.json();
|
||||
const stations = extractAprsStationsFromPayload(payload);
|
||||
if (!Array.isArray(stations) || stations.length === 0) return;
|
||||
if (isAgentMode) {
|
||||
processAprsAgentStations(stations, payload.agent_name);
|
||||
return;
|
||||
}
|
||||
|
||||
stations.forEach((station) => {
|
||||
const callsign = String(station && station.callsign ? station.callsign : '').trim();
|
||||
if (!callsign) return;
|
||||
const packet = {
|
||||
type: 'aprs',
|
||||
...station,
|
||||
callsign,
|
||||
lat: station.lat ?? station.latitude ?? null,
|
||||
lon: station.lon ?? station.longitude ?? null,
|
||||
packet_type: station.packet_type || 'position',
|
||||
};
|
||||
if (aprsHasValidCoordinates(packet.lat, packet.lon) && aprsMap) {
|
||||
updateAprsMarker(packet);
|
||||
}
|
||||
updateAprsStationList(packet);
|
||||
});
|
||||
} catch (err) {
|
||||
console.debug('APRS snapshot load failed:', err);
|
||||
}
|
||||
}
|
||||
|
||||
function startAprs() {
|
||||
// Get values from function bar controls
|
||||
const region = document.getElementById('aprsStripRegion').value;
|
||||
@@ -9627,6 +9672,9 @@
|
||||
if (customFreqInput) customFreqInput.disabled = true;
|
||||
startAprsMeterCheck();
|
||||
startAprsStream(isAgentMode);
|
||||
// Backfill current stations in case position packets arrived before
|
||||
// map initialization or SSE attachment.
|
||||
loadAprsStationSnapshot(isAgentMode);
|
||||
} else {
|
||||
alert('APRS Error: ' + (scanResult.message || scanResult.error || 'Failed to start'));
|
||||
updateAprsStatus('error');
|
||||
|
||||
@@ -41,3 +41,11 @@ def test_parse_aprs_packet_handles_ambiguous_uncompressed_position() -> None:
|
||||
assert packet["packet_type"] == "position"
|
||||
assert packet["lat"] == pytest.approx(49.05, rel=0, abs=1e-6)
|
||||
assert packet["lon"] == pytest.approx(-72.016667, rel=0, abs=1e-6)
|
||||
|
||||
|
||||
def test_parse_aprs_packet_handles_no_decimal_position_variant() -> None:
|
||||
packet = parse_aprs_packet("KJ7ABC-7>APRS,WIDE1-1:!4903N/07201W-Test")
|
||||
assert packet is not None
|
||||
assert packet["packet_type"] == "position"
|
||||
assert packet["lat"] == pytest.approx(49.05, rel=0, abs=1e-6)
|
||||
assert packet["lon"] == pytest.approx(-72.016667, rel=0, abs=1e-6)
|
||||
|
||||
Reference in New Issue
Block a user