mirror of
https://github.com/jketterl/openwebrx.git
synced 2025-12-06 07:12:09 +01:00
introduce source classes for the map
This commit is contained in:
parent
abc5cd177e
commit
26b8423193
|
|
@ -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
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -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] == "\\"
|
||||||
|
|
|
||||||
|
|
@ -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
|
||||||
|
|
|
||||||
|
|
@ -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(
|
||||||
{
|
{
|
||||||
|
|
|
||||||
45
owrx/map.py
45
owrx/map.py
|
|
@ -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}
|
||||||
|
|
|
||||||
|
|
@ -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")
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -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)
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue