diff --git a/htdocs/map.js b/htdocs/map.js
index 7075e960..30eeb60a 100644
--- a/htdocs/map.js
+++ b/htdocs/map.js
@@ -142,7 +142,7 @@ $(function(){
}, aprsOptions, getMarkerOpacityOptions(update.lastseen) ));
marker.lastseen = update.lastseen;
marker.mode = update.mode;
- marker.direct = update.direct;
+ marker.path = update.path;
marker.band = update.band;
marker.comment = update.location.comment;
marker.weather = update.location.weather;
@@ -447,6 +447,7 @@ $(function(){
var commentString = "";
var weatherString = "";
var detailsString = "";
+ var pathString = "";
var indirect = "";
var distance = "";
@@ -548,7 +549,13 @@ $(function(){
distance = " at " + distanceKm(receiverMarker.position, marker.position) + " km";
}
- if (!marker.direct) {
+ if (marker.path && marker.path.length > 0) {
+ var path = marker.path.toString().split(',');
+ path.forEach(function(part, index, path) {
+ path[index] = linkifyCallsign(part);
+ });
+
+ pathString = '
via ' + path.join(', ') + '
';
indirect = 'indirect ';
}
@@ -556,7 +563,8 @@ $(function(){
'' + linkifyCallsign(callsign) + distance + '
' +
'' + timestring + ' using ' + indirect +
marker.mode + ( marker.band ? ' on ' + marker.band : '' ) +
- '
' + commentString + weatherString + detailsString
+ '' + commentString + weatherString + detailsString +
+ pathString
);
infowindow.open(map, marker);
diff --git a/owrx/aprs/__init__.py b/owrx/aprs/__init__.py
index 61c5e336..81725b21 100644
--- a/owrx/aprs/__init__.py
+++ b/owrx/aprs/__init__.py
@@ -33,7 +33,7 @@ thirdpartyeRegex = re.compile("^([a-zA-Z0-9-]+)>((([a-zA-Z0-9-]+\\*?,)*)([a-zA-Z
messageIdRegex = re.compile("^(.*){([0-9]{1,5})$")
# regex to filter pseudo "WIDE" path elements
-widePattern = re.compile("^WIDE[0-9]-[0-9]$")
+widePattern = re.compile("^WIDE[0-9](-[0-9])?$")
def decodeBase91(input):
@@ -187,6 +187,17 @@ class AprsParser(PickleModule):
return False
return True
+ def getPath(self, aprsData):
+ path = []
+ if "source" in aprsData:
+ if aprsData["source"] == "AIS":
+ return path;
+ if "type" in aprsData and aprsData["type"] in ["thirdparty", "item", "object"]:
+ path += [ aprsData["source"] ]
+ if "path" in aprsData and len(aprsData["path"]) > 0:
+ path += [host for host in aprsData["path"] if widePattern.match(host) is None]
+ return path
+
def process(self, data):
try:
# TODO how can we tell if this is an APRS frame at all?
@@ -208,6 +219,7 @@ class AprsParser(PickleModule):
def updateMap(self, mapData):
mode = mapData["mode"] if "mode" in mapData else "APRS"
+ path = self.getPath(mapData)
direct = self.isDirect(mapData)
if "type" in mapData and mapData["type"] == "thirdparty" and "data" in mapData:
mapData = mapData["data"]
@@ -219,7 +231,7 @@ class AprsParser(PickleModule):
source = mapData["item"]
elif mapData["type"] == "object":
source = mapData["object"]
- Map.getSharedInstance().updateLocation(source, loc, mode, self.band, direct)
+ Map.getSharedInstance().updateLocation(source, loc, mode, self.band, path)
def hasCompressedCoordinates(self, raw):
return raw[0] == "/" or raw[0] == "\\"
diff --git a/owrx/map.py b/owrx/map.py
index 0b59b02c..2f1d1210 100644
--- a/owrx/map.py
+++ b/owrx/map.py
@@ -66,6 +66,7 @@ class Map(object):
"lastseen": record["updated"].timestamp() * 1000,
"mode": record["mode"],
"band": record["band"].getName() if record["band"] is not None else None,
+ "path": record["path"],
}
for (callsign, record) in self.positions.items()
]
@@ -77,22 +78,29 @@ class Map(object):
except ValueError:
pass
- def updateLocation(self, callsign, loc: Location, mode: str, band: Band = None, direct: bool = True):
+ def updateLocation(self, callsign, loc: Location, mode: str, band: Band = None, path: list[str] = []):
+ needBroadcast = False
ts = datetime.now()
+
with self.positionsLock:
- self.positions[callsign] = {"location": loc, "updated": ts, "mode": mode, "band": band}
- self.broadcast(
- [
- {
- "callsign": callsign,
- "location": loc.__dict__(),
- "lastseen": ts.timestamp() * 1000,
- "mode": mode,
- "band": band.getName() if band is not None else None,
- "direct": direct,
- }
- ]
- )
+ # Ignore indirect messages if there is a prior direct message
+ if len(path)>0 or callsign not in self.positions or not self.positions[callsign]["direct"]:
+ self.positions[callsign] = {"location": loc, "updated": ts, "mode": mode, "band": band, "path": path }
+ needBroadcast = True
+
+ if needBroadcast:
+ self.broadcast(
+ [
+ {
+ "callsign": callsign,
+ "location": loc.__dict__(),
+ "lastseen": ts.timestamp() * 1000,
+ "mode": mode,
+ "band": band.getName() if band is not None else None,
+ "path": path,
+ }
+ ]
+ )
def touchLocation(self, callsign):
# not implemented on the client side yet, so do not use!