From 2b23ddfb31d0f28d87ca4532606eabc284c4cbb2 Mon Sep 17 00:00:00 2001 From: Marat Fayzullin Date: Sun, 16 Jul 2023 21:18:58 -0400 Subject: [PATCH] Placing EIBI stations on the map, with the current frequencies. --- htdocs/map.js | 24 ++++++++++++-- owrx/eibi.py | 88 +++++++++++++++++++++++++++++++++++++++---------- owrx/markers.py | 25 +++++++------- 3 files changed, 106 insertions(+), 31 deletions(-) diff --git a/htdocs/map.js b/htdocs/map.js index 0ed1ad37..c81d57e4 100644 --- a/htdocs/map.js +++ b/htdocs/map.js @@ -214,6 +214,7 @@ $(function(){ marker.altitude = update.location.altitude; marker.device = update.location.device; marker.antenna = update.location.antenna; + marker.schedule = update.location.schedule; if (expectedCallsign && expectedCallsign == update.callsign) { map.panTo(pos); @@ -457,7 +458,7 @@ $(function(){ return '' + callsign + ''; - }; + } var distanceKm = function(p1, p2) { // Earth radius in km @@ -521,7 +522,8 @@ $(function(){ var makeListItem = function(name, value) { return '
' - + '' + name + '' + + '' + name.replace(/[ \r\n]+/gm, ' ') + + '    ' + '' + value + '' + '
'; } @@ -664,6 +666,7 @@ $(function(){ var marker = markers[name]; var commentString = ""; var detailsString = ""; + var scheduleString = ""; var nameString = ""; var distance = ""; @@ -692,17 +695,32 @@ $(function(){ detailsString += makeListItem('Antenna', truncate(marker.antenna, 24)); } + if (marker.schedule) { + for (var j=0 ; j' + + Math.round(marker.schedule[j].freq/1000) + 'kHz'; + scheduleString += makeListItem(marker.schedule[j].name, freq); + } + } + if (detailsString.length > 0) { detailsString = '

' + makeListTitle('Details') + detailsString + '

'; } + if (scheduleString.length > 0) { + scheduleString = '

' + makeListTitle('Schedule') + scheduleString + '

'; + } + if (receiverMarker) { distance = " at " + distanceKm(receiverMarker.position, marker.position) + " km"; } infowindow.setContent( '

' + nameString + distance + '

' + - commentString + detailsString + commentString + detailsString + scheduleString ); infowindow.open(map, marker); diff --git a/owrx/eibi.py b/owrx/eibi.py index 475cae63..15a6094a 100644 --- a/owrx/eibi.py +++ b/owrx/eibi.py @@ -46,6 +46,7 @@ class EIBI(object): def __init__(self): self.refreshPeriod = 60*60*24*30 self.event = threading.Event() + self.lock = threading.Lock() self.schedule = [] self.thread = None @@ -74,11 +75,11 @@ class EIBI(object): ts = os.path.getmtime(file) if os.path.isfile(file) else 0 # Try loading cached schedule from file first, unless stale - if time.time() - ts < self.refreshPeriod: - logger.debug("Loading cached schedule from '{0}'...".format(file)) - self.schedule = self.loadSchedule(file) - else: - self.schedule = self.updateSchedule() + with self.lock: + if time.time() - ts < self.refreshPeriod: + self.schedule = self.loadSchedule(file) + else: + self.schedule = self.updateSchedule() while not self.event.is_set(): # Sleep until it is time to update schedule @@ -87,22 +88,25 @@ class EIBI(object): if not self.event.is_set(): # Update schedule logger.debug("Refreshing schedule...") - self.schedule = self.updateSchedule() + with self.lock: + self.schedule = self.updateSchedule() # Done logger.debug("Stopped EIBI main thread.") self.thread = None # Load schedule from a given JSON file - def loadSchedule(self, fileName: str): + def loadSchedule(self, file: str): + logger.debug("Loading schedule from '{0}'...".format(file)) try: - with open(fileName, "r") as f: + with open(file, "r") as f: result = json.load(f) f.close() except Exception as e: logger.debug("loadSchedule() exception: {0}".format(e)) result = [] # Done + logger.debug("Loaded {0} entries from '{1}'...".format(len(result), file)) return result # Update schedule @@ -122,33 +126,66 @@ class EIBI(object): # Done return schedule + # Find all current broadcasts for a given source def findBySource(self, src: str): # Get entries active at the current time now = datetime.utcnow() now = now.hour * 100 + now.minute result = [] # Search for entries originating from given source at current time - for entry in self.schedule: - if entry["time1"] <= now and entry["time2"] > now: - if entry["itu"] + entry["src"] == src: - result.append(entry) + with self.lock: + for entry in self.schedule: + if entry["time1"] <= now and entry["time2"] > now: + if entry["itu"] + entry["src"] == src: + result.append(entry) # Done return result + # Find all current broadcasts for a given frequency range def findCurrent(self, freq1: int, freq2: int): # Get entries active at the current time now = datetime.utcnow() now = now.hour * 100 + now.minute return self.find(freq1, freq2, now, now) + # Find all broadcasts for given frequency and time ranges def find(self, freq1: int, freq2: int, time1: int, time2: int): result = [] # Search for entries within given frequency and time ranges - for entry in self.schedule: - f = entry["freq"] - if f >= freq1 and f <= freq2: - if entry["time1"] <= time2 and entry["time2"] > time1: - result.append(entry) + with self.lock: + for entry in self.schedule: + f = entry["freq"] + if f >= freq1 and f <= freq2: + if entry["time1"] <= time2 and entry["time2"] > time1: + result.append(entry) + # Done + return result + + # Create list of currently broadcasting locations + def currentTransmitters(self): + # Get entries active at the current time + now = datetime.utcnow() + day = str(now.weekday() + 1) + now = now.hour * 100 + now.minute + result = {} + # Search for current entries + with self.lock: + for entry in self.schedule: + # For every current schedule entry... + if entry["time1"] <= now and entry["time2"] > now and day in entry["days"]: + src = entry["itu"] + entry["src"] + # Find all matching transmitter locations + for loc in EIBI_Locations: + if loc["code"] == src: + # Add location to the result + name = loc["name"] + logger.debug("Found {0} .. {1} .. {2} from {3} ({4})".format( + entry["time1"], now, entry["time2"], name, src)) + if name not in result: + result[name] = loc.copy() + result[name]["schedule"] = [] + # Add schedule entry to the location + result[name]["schedule"].append(entry) # Done return result @@ -197,8 +234,25 @@ class EIBI(object): # When we encounter a location... m = pattern.match(line) if m is not None: + # Guess modulation by language and name fields + name = m.group(6).lower() + lang = m.group(7) + mode = ( + "hfdl" if lang == "-HF" else + "rtty450" if lang == "-TY" else + "cw" if lang == "-CW" else + "fax" if " fax" in name else + "usb" if "volmet" in name else + "usb" if "ldoc" in name else + "usb" if "car-" in name else + "usb" if "nat-" in name else + "usb" if " usb" in name else + "usb" if "fsk" in name else + "am") + # Append a new entry to the result result.append({ "freq" : int(float(m.group(1)) * 1000), + "mode" : mode, "time1" : int(m.group(2)), "time2" : int(m.group(3)), "days" : self.convertDays(m.group(4)), diff --git a/owrx/markers.py b/owrx/markers.py index 0b48ab10..71ae0cf4 100644 --- a/owrx/markers.py +++ b/owrx/markers.py @@ -2,7 +2,7 @@ from owrx.config.core import CoreConfig from owrx.map import Map, Location from owrx.aprs import getSymbolData from json import JSONEncoder -from owrx.eibi import EIBI_Locations +from owrx.eibi import EIBI_Locations, EIBI import urllib import threading @@ -107,25 +107,23 @@ class Markers(object): # Load markers from local files for file in self.fileList: if os.path.isfile(file): - logger.debug("Loading markers from '{0}'...".format(file)) self.markers.update(self.loadMarkers(file)) - # Load markers from the EIBI database - #logger.debug("Loading EIBI transmitter locations...") - #self.markers.update(self.loadEIBI()) - # This file contains cached database file = self._getCachedMarkersFile() ts = os.path.getmtime(file) if os.path.isfile(file) else 0 # Try loading cached database from file first, unless stale if time.time() - ts < self.refreshPeriod: - logger.debug("Loading cached markers from '{0}'...".format(file)) self.markers.update(self.loadMarkers(file)) else: # Add scraped data to the database self.markers.update(self.updateCache()) + # Load markers from the EIBI database + time.sleep(30) + self.markers.update(self.loadEIBI()) + while not self.event.is_set(): # Update map with markers logger.debug("Updating map...") @@ -143,10 +141,11 @@ class Markers(object): self.thread = None # Load markers from a given file - def loadMarkers(self, fileName: str): + def loadMarkers(self, file: str): + logger.debug("Loading markers from '{0}'...".format(file)) # Load markers list from JSON file try: - with open(fileName, "r") as f: + with open(file, "r") as f: db = json.load(f) f.close() except Exception as e: @@ -160,6 +159,7 @@ class Markers(object): result[key] = MarkerLocation(attrs) # Done + logger.debug("Loaded {0} markers from '{1}'.".format(len(result), file)) return result # Update markers on the map @@ -194,11 +194,12 @@ class Markers(object): # def loadEIBI(self): + logger.debug("Loading EIBI transmitter locations...") #url = "https://www.short-wave.info/index.php?txsite=" url = "https://www.google.com/search?q=" result = {} # Load transmitter sites from EIBI database - for entry in EIBI_Locations: + for entry in EIBI.getSharedInstance().currentTransmitters().values(): rl = MarkerLocation({ "type" : "feature", "mode" : "Stations", @@ -206,10 +207,12 @@ class Markers(object): "id" : entry["name"], "lat" : entry["lat"], "lon" : entry["lon"], - "url" : url + urllib.parse.quote_plus(entry["name"]) + "url" : url + urllib.parse.quote_plus(entry["name"]), + "schedule": entry["schedule"] }) result[rl.getId()] = rl # Done + logger.debug("Loaded {0} EIBI transmitter locations.".format(len(result))) return result def scrapeOWRX(self, url: str = "https://www.receiverbook.de/map"):