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!