introduce source classes for the map

This commit is contained in:
Jakob Ketterl 2023-09-06 16:36:38 +02:00
parent abc5cd177e
commit 26b8423193
7 changed files with 77 additions and 30 deletions

View file

@ -1,6 +1,6 @@
from csdr.module import PickleModule from csdr.module import PickleModule
from math import sqrt, atan2, pi, floor, acos, cos from math import sqrt, atan2, pi, floor, acos, cos
from owrx.map import LatLngLocation, IncrementalUpdate, Location, Map from owrx.map import LatLngLocation, IncrementalUpdate, Location, Map, Source
from owrx.metrics import Metrics, CounterMetric from owrx.metrics import Metrics, CounterMetric
from datetime import datetime, timedelta from datetime import datetime, timedelta
from enum import Enum from enum import Enum
@ -65,6 +65,17 @@ class AdsbLocation(AirplaneLocation):
return timedelta(seconds=30) return timedelta(seconds=30)
class IcaoSource(Source):
def __init__(self, icao: str):
self.icao = icao
def getKey(self) -> str:
return "icao:{}".format(self.icao)
def __dict__(self):
return {"icao": self.icao}
class CprRecordType(Enum): class CprRecordType(Enum):
AIR = ("air", 360) AIR = ("air", 360)
GROUND = ("ground", 90) GROUND = ("ground", 90)
@ -282,7 +293,7 @@ class ModeSParser(PickleModule):
if "icao" in message and AirplaneLocation.mapKeys & message.keys(): if "icao" in message and AirplaneLocation.mapKeys & message.keys():
data = {k: message[k] for k in AirplaneLocation.mapKeys if k in message} data = {k: message[k] for k in AirplaneLocation.mapKeys if k in message}
loc = AdsbLocation(data) loc = AdsbLocation(data)
Map.getSharedInstance().updateLocation({"icao": message['icao']}, loc, "ADS-B", None) Map.getSharedInstance().updateLocation(IcaoSource(message['icao']), loc, "ADS-B", None)
return message return message

View file

@ -1,4 +1,4 @@
from owrx.map import Map, LatLngLocation from owrx.map import Map, LatLngLocation, Source
from owrx.metrics import Metrics, CounterMetric from owrx.metrics import Metrics, CounterMetric
from owrx.bands import Bandplan from owrx.bands import Bandplan
from datetime import datetime, timezone from datetime import datetime, timezone
@ -155,6 +155,20 @@ class AprsLocation(LatLngLocation):
return res return res
class AprsSource(Source):
def __init__(self, source):
self.source = source
def getKey(self) -> str:
callsign = self.source["callsign"]
if "ssid" in self.source:
callsign += "-{}".format(self.source["ssid"])
return "aprs:{}".format(callsign)
def __dict__(self):
return self.source
class AprsParser(PickleModule): class AprsParser(PickleModule):
def __init__(self): def __init__(self):
super().__init__() super().__init__()
@ -215,7 +229,7 @@ class AprsParser(PickleModule):
source["item"] = mapData["item"] source["item"] = mapData["item"]
elif mapData["type"] == "object" and "object" in mapData: elif mapData["type"] == "object" and "object" in mapData:
source["object"] = mapData["object"] source["object"] = mapData["object"]
Map.getSharedInstance().updateLocation(source, loc, "APRS", self.band) Map.getSharedInstance().updateLocation(AprsSource(source), loc, "APRS", self.band)
def hasCompressedCoordinates(self, raw): def hasCompressedCoordinates(self, raw):
return raw[0] == "/" or raw[0] == "\\" return raw[0] == "/" or raw[0] == "\\"

View file

@ -2,13 +2,24 @@ from pycsdr.modules import ExecModule
from pycsdr.types import Format from pycsdr.types import Format
from csdr.module import JsonParser from csdr.module import JsonParser
from owrx.adsb.modes import AirplaneLocation from owrx.adsb.modes import AirplaneLocation
from owrx.map import Map from owrx.map import Map, Source
class HfdlAirplaneLocation(AirplaneLocation): class HfdlAirplaneLocation(AirplaneLocation):
pass pass
class HfdlSource(Source):
def __init__(self, flight):
self.flight = flight
def getKey(self) -> str:
return "hfdl:{}".format(self.flight)
def __dict__(self):
return {"flight": self.flight}
class DumpHFDLModule(ExecModule): class DumpHFDLModule(ExecModule):
def __init__(self): def __init__(self):
super().__init__( super().__init__(
@ -42,5 +53,5 @@ class HFDLMessageParser(JsonParser):
if "pos" in hfnpdu: if "pos" in hfnpdu:
pos = hfnpdu['pos'] pos = hfnpdu['pos']
if abs(pos['lat']) <= 90 and abs(pos['lon']) <= 180: if abs(pos['lat']) <= 90 and abs(pos['lon']) <= 180:
Map.getSharedInstance().updateLocation({"flight": hfnpdu["flight_id"]}, HfdlAirplaneLocation(pos), "HFDL") Map.getSharedInstance().updateLocation(HfdlSource(hfnpdu["flight_id"]), HfdlAirplaneLocation(pos), "HFDL")
return msg return msg

View file

@ -3,7 +3,7 @@ from owrx.audio.chopper import AudioChopperParser
import re import re
from js8py import Js8 from js8py import Js8
from js8py.frames import Js8FrameHeartbeat, Js8FrameCompound from js8py.frames import Js8FrameHeartbeat, Js8FrameCompound
from owrx.map import Map, LocatorLocation from owrx.map import Map, LocatorLocation, CallsignSource
from owrx.metrics import Metrics, CounterMetric from owrx.metrics import Metrics, CounterMetric
from owrx.config import Config from owrx.config import Config
from abc import ABCMeta, abstractmethod from abc import ABCMeta, abstractmethod
@ -103,7 +103,7 @@ class Js8Parser(AudioChopperParser):
if (isinstance(frame, Js8FrameHeartbeat) or isinstance(frame, Js8FrameCompound)) and frame.grid: if (isinstance(frame, Js8FrameHeartbeat) or isinstance(frame, Js8FrameCompound)) and frame.grid:
Map.getSharedInstance().updateLocation( Map.getSharedInstance().updateLocation(
frame.source, LocatorLocation(frame.grid), "JS8", band CallsignSource(**frame.source), LocatorLocation(frame.grid), "JS8", band
) )
ReportingEngine.getSharedInstance().spot( ReportingEngine.getSharedInstance().spot(
{ {

View file

@ -1,7 +1,7 @@
from datetime import datetime, timedelta from datetime import datetime, timedelta
from owrx.config import Config from owrx.config import Config
from owrx.bands import Band from owrx.bands import Band
from abc import abstractmethod, ABCMeta from abc import abstractmethod, ABC, ABCMeta
import threading import threading
import time import time
import sys import sys
@ -22,6 +22,15 @@ class Location(object):
} }
class Source(ABC):
@abstractmethod
def getKey(self) -> str:
pass
def __dict__(self):
return {}
class Map(object): class Map(object):
sharedInstance = None sharedInstance = None
creationLock = threading.Lock() creationLock = threading.Lock()
@ -67,7 +76,7 @@ class Map(object):
client.write_update( client.write_update(
[ [
{ {
"source": record["source"], "source": record["source"].__dict__(),
"location": record["location"].__dict__(), "location": record["location"].__dict__(),
"lastseen": record["updated"].timestamp() * 1000, "lastseen": record["updated"].timestamp() * 1000,
"mode": record["mode"], "mode": record["mode"],
@ -83,18 +92,9 @@ class Map(object):
except ValueError: except ValueError:
pass pass
def _sourceToKey(self, source): def updateLocation(self, source: Source, loc: Location, mode: str, band: Band = None):
if "ssid" in source:
return "{callsign}-{ssid}".format(**source)
elif "icao" in source:
return source["icao"]
elif "flight" in source:
return source["flight"]
return source["callsign"]
def updateLocation(self, source, loc: Location, mode: str, band: Band = None):
ts = datetime.now() ts = datetime.now()
key = self._sourceToKey(source) key = source.getKey()
with self.positionsLock: with self.positionsLock:
if isinstance(loc, IncrementalUpdate) and key in self.positions: if isinstance(loc, IncrementalUpdate) and key in self.positions:
loc.update(self.positions[key]["location"]) loc.update(self.positions[key]["location"])
@ -102,7 +102,7 @@ class Map(object):
self.broadcast( self.broadcast(
[ [
{ {
"source": source, "source": source.__dict__(),
"location": loc.__dict__(), "location": loc.__dict__(),
"lastseen": ts.timestamp() * 1000, "lastseen": ts.timestamp() * 1000,
"mode": mode, "mode": mode,
@ -111,14 +111,14 @@ class Map(object):
] ]
) )
def touchLocation(self, source): def touchLocation(self, source: Source):
# not implemented on the client side yet, so do not use! # not implemented on the client side yet, so do not use!
ts = datetime.now() ts = datetime.now()
key = self._sourceToKey(source) key = source.getKey()
with self.positionsLock: with self.positionsLock:
if key in self.positions: if key in self.positions:
self.positions[key]["updated"] = ts self.positions[key]["updated"] = ts
self.broadcast([{"source": source, "lastseen": ts.timestamp() * 1000}]) self.broadcast([{"source": source.__dict__(), "lastseen": ts.timestamp() * 1000}])
def removeLocation(self, key): def removeLocation(self, key):
with self.positionsLock: with self.positionsLock:
@ -172,3 +172,14 @@ class IncrementalUpdate(Location, metaclass=ABCMeta):
@abstractmethod @abstractmethod
def update(self, previousLocation: Location): def update(self, previousLocation: Location):
pass pass
class CallsignSource(Source):
def __init__(self, callsign: str):
self.callsign = callsign
def getKey(self) -> str:
return "callsign:{}".format(self.callsign)
def __dict__(self):
return {"callsign": self.callsign}

View file

@ -11,7 +11,7 @@ from urllib.error import HTTPError
from csdr.module import PickleModule from csdr.module import PickleModule
from owrx.aprs import AprsParser, AprsLocation from owrx.aprs import AprsParser, AprsLocation
from owrx.config import Config from owrx.config import Config
from owrx.map import Map, LatLngLocation from owrx.map import Map, LatLngLocation, CallsignSource
from owrx.bands import Bandplan from owrx.bands import Bandplan
logger = logging.getLogger(__name__) logger = logging.getLogger(__name__)
@ -129,7 +129,7 @@ class DigihamEnricher(Enricher, metaclass=ABCMeta):
callsign = self.getCallsign(meta) callsign = self.getCallsign(meta)
if callsign is not None and "lat" in meta and "lon" in meta: if callsign is not None and "lat" in meta and "lon" in meta:
loc = LatLngLocation(meta["lat"], meta["lon"]) loc = LatLngLocation(meta["lat"], meta["lon"])
Map.getSharedInstance().updateLocation({"callsign": callsign}, loc, mode, self.parser.getBand()) Map.getSharedInstance().updateLocation(CallsignSource(callsign), loc, mode, self.parser.getBand())
return meta return meta
@abstractmethod @abstractmethod
@ -202,7 +202,7 @@ class DStarEnricher(DigihamEnricher):
if "ourcall" in meta: if "ourcall" in meta:
# send location info to map as well (it will show up with the correct symbol there!) # send location info to map as well (it will show up with the correct symbol there!)
loc = AprsLocation(data) loc = AprsLocation(data)
Map.getSharedInstance().updateLocation({"callsign": meta["ourcall"]}, loc, "DPRS", self.parser.getBand()) Map.getSharedInstance().updateLocation(CallsignSource(meta["ourcall"]), loc, "DPRS", self.parser.getBand())
except Exception: except Exception:
logger.exception("Error while parsing DPRS data") logger.exception("Error while parsing DPRS data")

View file

@ -1,6 +1,6 @@
from datetime import datetime, timezone from datetime import datetime, timezone
from typing import List from typing import List
from owrx.map import Map, LocatorLocation from owrx.map import Map, LocatorLocation, CallsignSource
from owrx.metrics import Metrics, CounterMetric from owrx.metrics import Metrics, CounterMetric
from owrx.reporting import ReportingEngine from owrx.reporting import ReportingEngine
from owrx.audio import AudioChopperProfile, StaticProfileSource, ConfigWiredProfileSource from owrx.audio import AudioChopperProfile, StaticProfileSource, ConfigWiredProfileSource
@ -289,7 +289,7 @@ class WsjtParser(AudioChopperParser):
self.pushDecode(mode, band) self.pushDecode(mode, band)
if "source" in out and "locator" in out: if "source" in out and "locator" in out:
Map.getSharedInstance().updateLocation( Map.getSharedInstance().updateLocation(
out["source"], LocatorLocation(out["locator"]), mode, band CallsignSource(**out["source"]), LocatorLocation(out["locator"]), mode, band
) )
ReportingEngine.getSharedInstance().spot(out) ReportingEngine.getSharedInstance().spot(out)