mirror of
https://git.ianrenton.com/ian/spothole.git
synced 2025-10-27 08:49:27 +00:00
158 lines
5.0 KiB
Python
158 lines
5.0 KiB
Python
import logging
|
|
from datetime import datetime
|
|
|
|
from diskcache import Cache
|
|
from pyhamtools import LookupLib, Callinfo
|
|
from pyhamtools.frequency import freq_to_band
|
|
from pyhamtools.locator import latlong_to_locator
|
|
|
|
from core.config import config
|
|
from core.constants import BANDS, UNKNOWN_BAND, CW_MODES, PHONE_MODES, DATA_MODES, ALL_MODES
|
|
|
|
# Lookup helpers from pyhamtools
|
|
LOOKUP_LIB_BASIC = LookupLib(lookuptype="countryfile")
|
|
CALL_INFO_BASIC = Callinfo(LOOKUP_LIB_BASIC)
|
|
QRZ_AVAILABLE = config["qrz-password"] != ""
|
|
if QRZ_AVAILABLE:
|
|
LOOKUP_LIB_QRZ = LookupLib(lookuptype="qrz", username=config["qrz-username"], pwd=config["qrz-password"])
|
|
# Cache of QRZ.com callsign lookups, so we don't repeatedly call the API for stuff we already know
|
|
QRZ_CALLSIGN_DATA_CACHE = Cache('.qrz_callsign_lookup_cache')
|
|
|
|
# Infer a mode from the comment
|
|
def infer_mode_from_comment(comment):
|
|
for mode in ALL_MODES:
|
|
if mode in comment.upper():
|
|
return mode
|
|
return None
|
|
|
|
# Infer a "mode family" from a mode.
|
|
def infer_mode_type_from_mode(mode):
|
|
if mode.upper() in CW_MODES:
|
|
return "CW"
|
|
elif mode.upper() in PHONE_MODES:
|
|
return "PHONE"
|
|
elif mode.upper() in DATA_MODES:
|
|
return "DATA"
|
|
else:
|
|
if mode.upper() != "OTHER":
|
|
logging.warn("Found an unrecognised mode: " + mode + ". Developer should categorise this.")
|
|
return None
|
|
|
|
# Infer a band from a frequency in Hz
|
|
def infer_band_from_freq(freq):
|
|
for b in BANDS:
|
|
if b.start_freq <= freq <= b.end_freq:
|
|
return b
|
|
return UNKNOWN_BAND
|
|
|
|
# Infer a country name from a callsign
|
|
def infer_country_from_callsign(call):
|
|
try:
|
|
return CALL_INFO_BASIC.get_country_name(call)
|
|
except KeyError as e:
|
|
return None
|
|
|
|
# Infer a DXCC ID from a callsign
|
|
def infer_dxcc_id_from_callsign(call):
|
|
try:
|
|
return CALL_INFO_BASIC.get_adif_id(call)
|
|
except KeyError as e:
|
|
return None
|
|
|
|
# Infer a continent shortcode from a callsign
|
|
def infer_continent_from_callsign(call):
|
|
try:
|
|
return CALL_INFO_BASIC.get_continent(call)
|
|
except KeyError as e:
|
|
return None
|
|
|
|
# Infer a CQ zone from a callsign
|
|
def infer_cq_zone_from_callsign(call):
|
|
try:
|
|
return CALL_INFO_BASIC.get_cqz(call)
|
|
except KeyError as e:
|
|
return None
|
|
|
|
# Infer a ITU zone from a callsign
|
|
def infer_itu_zone_from_callsign(call):
|
|
try:
|
|
return CALL_INFO_BASIC.get_ituz(call)
|
|
except KeyError as e:
|
|
return None
|
|
|
|
# Utility method to get QRZ.com data from cache if possible, if not get it from the API and cache it
|
|
def get_qrz_data_for_callsign(call):
|
|
# Fetch from cache if we can, otherwise fetch from the API and cache it
|
|
qrz_data = QRZ_CALLSIGN_DATA_CACHE.get(call)
|
|
if qrz_data:
|
|
return qrz_data
|
|
elif QRZ_AVAILABLE:
|
|
try:
|
|
data = LOOKUP_LIB_QRZ.lookup_callsign(callsign=call)
|
|
QRZ_CALLSIGN_DATA_CACHE.add(call, data, expire=604800) # 1 week in seconds
|
|
return data
|
|
except KeyError:
|
|
# QRZ had no info for the call, that's OK
|
|
return None
|
|
else:
|
|
return None
|
|
|
|
# Infer an operator name from a callsign (requires QRZ.com)
|
|
def infer_name_from_callsign(call):
|
|
data = get_qrz_data_for_callsign(call)
|
|
if data and "fname" in data:
|
|
name = data["fname"]
|
|
if "name" in data:
|
|
name = name + " " + data["name"]
|
|
return name
|
|
else:
|
|
return None
|
|
|
|
# Infer a latitude and longitude from a callsign (requires QRZ.com)
|
|
def infer_latlon_from_callsign_qrz(call):
|
|
data = get_qrz_data_for_callsign(call)
|
|
if data and "latitude" in data and "longitude" in data:
|
|
return [data["latitude"], data["longitude"]]
|
|
else:
|
|
return None
|
|
|
|
# Infer a grid locator from a callsign (requires QRZ.com)
|
|
def infer_grid_from_callsign_qrz(call):
|
|
data = get_qrz_data_for_callsign(call)
|
|
if data and "locator" in data:
|
|
return data["locator"]
|
|
else:
|
|
return None
|
|
|
|
# Infer a latitude and longitude from a callsign (using DXCC, probably very inaccurate)
|
|
def infer_latlon_from_callsign_dxcc(call):
|
|
try:
|
|
data = CALL_INFO_BASIC.get_lat_long(call)
|
|
if data and "latitude" in data and "longitude" in data:
|
|
return [data["latitude"], data["longitude"]]
|
|
else:
|
|
return None
|
|
except KeyError:
|
|
return None
|
|
|
|
# Infer a grid locator from a callsign (using DXCC, probably very inaccurate)
|
|
def infer_grid_from_callsign_dxcc(call):
|
|
latlon = infer_latlon_from_callsign_dxcc(call)
|
|
return latlong_to_locator(latlon[0], latlon[1], 8)
|
|
|
|
|
|
# Infer a mode from the frequency (in Hz) according to the band plan. Just a guess really.
|
|
def infer_mode_from_frequency(freq):
|
|
try:
|
|
return freq_to_band(freq / 1000.0)["mode"]
|
|
except KeyError:
|
|
return None
|
|
|
|
|
|
# Convert objects to serialisable things. Used by JSON serialiser as a default when it encounters unserializable things.
|
|
# Converts datetimes to ISO.
|
|
# Anything else it tries to convert to a dict.
|
|
def serialize_everything(obj):
|
|
if isinstance(obj, datetime):
|
|
return obj.isoformat()
|
|
return obj.__dict__ |