DX Cluster support

This commit is contained in:
Ian Renton
2025-09-26 23:26:39 +01:00
parent c34821dc9b
commit fd2ffb47a0
6 changed files with 86 additions and 6 deletions

View File

@@ -3,6 +3,7 @@ from data.band import Band
# General software # General software
SOFTWARE_NAME = "Metaspot by M0TRT" SOFTWARE_NAME = "Metaspot by M0TRT"
SOFTWARE_VERSION = "0.1" SOFTWARE_VERSION = "0.1"
SERVER_OWNER_CALLSIGN = "M0TRT"
# Band definitions # Band definitions
BANDS = [ BANDS = [

View File

@@ -1,6 +1,8 @@
from dataclasses import dataclass from dataclasses import dataclass
from datetime import datetime from datetime import datetime
from pyhamtools.locator import locator_to_latlong, latlong_to_locator
from core.constants import DXCC_FLAGS from core.constants import DXCC_FLAGS
from core.utils import infer_mode_family_from_mode, infer_band_from_freq, infer_continent_from_callsign, \ from core.utils import infer_mode_family_from_mode, infer_band_from_freq, infer_continent_from_callsign, \
infer_country_from_callsign, infer_cq_zone_from_callsign, infer_itu_zone_from_callsign, infer_dxcc_id_from_callsign infer_country_from_callsign, infer_cq_zone_from_callsign, infer_itu_zone_from_callsign, infer_dxcc_id_from_callsign
@@ -98,3 +100,13 @@ class Spot:
if self.mode and not self.mode_family: if self.mode and not self.mode_family:
self.mode_family=infer_mode_family_from_mode(self.mode) self.mode_family=infer_mode_family_from_mode(self.mode)
if self.grid and not self.latitude:
ll = locator_to_latlong(self.grid)
self.latitude = ll[0]
self.longitude = ll[1]
if self.latitude and self.longitude and not self.grid:
self.grid = latlong_to_locator(self.latitude, self.longitude, 8)
# TODO lat/lon from DXCC centre?

View File

@@ -2,6 +2,7 @@
import signal import signal
from time import sleep from time import sleep
from providers.dxcluster import DXCluster
from providers.pota import POTA from providers.pota import POTA
@@ -17,7 +18,10 @@ if __name__ == '__main__':
signal.signal(signal.SIGINT, shutdown) signal.signal(signal.SIGINT, shutdown)
# Create providers # Create providers
providers = [POTA()] # todo all other providers providers = [
POTA(),
DXCluster("hrd.wa9pie.net", 8000)
] # todo all other providers
# Set up spot list # Set up spot list
spot_list = [] spot_list = []
# Set up data providers # Set up data providers
@@ -32,9 +36,6 @@ if __name__ == '__main__':
# Todo serve apidocs # Todo serve apidocs
# Todo serve website # Todo serve website
sleep(2)
print(len(spot_list))
print(spot_list[0])
# NOTES FOR FIELD SPOTTER # NOTES FOR FIELD SPOTTER

65
providers/dxcluster.py Normal file
View File

@@ -0,0 +1,65 @@
from datetime import datetime, timezone
from threading import Thread
import pytz
from core.constants import SERVER_OWNER_CALLSIGN
from data.spot import Spot
from providers.provider import Provider
import telnetlib3
import re
callsign_pattern = "([a-z|0-9|/]+)"
frequency_pattern = "([0-9|.]+)"
pattern = re.compile("^DX de "+callsign_pattern+":\\s+"+frequency_pattern+"\\s+"+callsign_pattern+"\\s+(.*)\\s+(\\d{4}Z)", re.IGNORECASE)
class DXCluster(Provider):
# Constructor requires hostname and port
def __init__(self, hostname, port):
super().__init__()
self.hostname = hostname
self.port = port
self.telnet = None
self.thread = None
self.run = True
def name(self):
return "DX Cluster " + self.hostname + " " + str(self.port)
def start(self):
self.thread = Thread(target=self.handle)
self.thread.start()
def stop(self):
self.run = False
self.telnet.close()
self.thread.join()
def handle(self):
self.status = "Connecting"
self.telnet = telnetlib3.Telnet(self.hostname, self.port)
self.telnet.read_until("login: ".encode("ascii"))
self.telnet.write((SERVER_OWNER_CALLSIGN + "\n").encode("ascii"))
self.status = "Waiting for Data"
while self.run:
# Check new telnet info against regular expression
telnet_output = self.telnet.read_until("\n".encode("ascii"))
match = pattern.match(telnet_output.decode("ascii"))
if match:
spot_time = datetime.strptime(match.group(5), "%H%MZ")
spot_datetime = datetime.combine(datetime.today(), spot_time.time()).replace(tzinfo=pytz.UTC)
spot = Spot(source=self.name(),
dx_call=match.group(3),
de_call=match.group(1),
freq=float(match.group(2)),
comment=match.group(4).strip(),
time=spot_datetime)
# Fill in any blanks
spot.infer_missing()
# Add to our list
self.submit([spot])
self.status = "OK"
self.last_update_time = datetime.now(timezone.utc)

View File

@@ -31,7 +31,7 @@ class POTA(Provider):
# Iterate through source data # Iterate through source data
for source_spot in source_data: for source_spot in source_data:
# Convert to our spot format # Convert to our spot format
spot = Spot(source="POTA", spot = Spot(source=self.name(),
source_id=source_spot["spotId"], source_id=source_spot["spotId"],
dx_call=source_spot["activator"], dx_call=source_spot["activator"],
de_call=source_spot["spotter"], de_call=source_spot["spotter"],

View File

@@ -1 +1,2 @@
pytz
requests~=2.32.5 requests~=2.32.5