import logging import simplejson from pyhamtools.frequency import freq_to_band from pyhamtools.locator import latlong_to_locator from core.constants import UNKNOWN_BAND, BANDS, CW_MODES, PHONE_MODES, DATA_MODES, MODE_ALIASES, ALL_MODES from core.data_store import DATA_STORE from data.callsign import Callsign def safe_json_dumps(obj): """Safe version of json.dumps that also converts objects to dicts so they can be output, and ignores NaN floats which are invalid in JSON.""" return simplejson.dumps(obj, ensure_ascii=False, ignore_nan=True, default=lambda o: o.__dict__) def infer_mode_from_comment(comment): """Infer a mode from the comment""" for mode in ALL_MODES: if mode in comment.upper(): return mode for mode in MODE_ALIASES.keys(): if mode in comment.upper(): return MODE_ALIASES[mode] return None def infer_mode_type_from_mode(mode): """Infer a "mode family" from a 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.warning("Found an unrecognised mode: " + mode + ". Developer should categorise this.") return None def infer_band_from_freq(freq): """Infer a band from a frequency in Hz""" for b in BANDS: if b.start_freq <= freq <= b.end_freq: return b return UNKNOWN_BAND def infer_mode_from_frequency(freq): """Infer a mode from the frequency (in Hz) according to the band plan. Just a guess really.""" try: khz = freq / 1000.0 mode = freq_to_band(khz)["mode"] # Some additional common digimode ranges in addition to what the 3rd-party freq_to_band function returns. # This is mostly here just because freq_to_band is very specific about things like FT8 frequencies, and e.g. # a spot at 7074.5 kHz will be indicated as LSB, even though it's clearly in the FT8 range. Future updates # might include other common digimode centres of activity here, but this achieves the main goal of keeping # large numbers of clearly-FT* spots off the list of people filtering out digimodes. if (7074 <= khz < 7077) or (10136 <= khz < 10139) or (14074 <= khz < 14077) or (18100 <= khz < 18103) or ( 21074 <= khz < 21077) or (24915 <= khz < 24918) or (28074 <= khz < 28077): mode = "FT8" if (7047.5 <= khz < 7050.5) or (10140 <= khz < 10143) or (14080 <= khz < 14083) or ( 18104 <= khz < 18107) or (21140 <= khz < 21143) or (24919 <= khz < 24922) or (28180 <= khz < 28183): mode = "FT4" return mode except KeyError: return None def get_flag_for_dxcc(dxcc): """Get an emoji flag for a given DXCC entity ID""" dxcc_data = DATA_STORE.dxcc_data[dxcc] if dxcc in DATA_STORE.dxcc_data else None return dxcc_data["flag"] if dxcc_data else None def get_callsign_object_from_pyhamtools_callinfo(callsign, callinfo): """Utility function to take the data provided by a PyHamTools CallInfo object and populate our own Callsign data object from it""" try: home_call = callinfo.get_homecall(callsign) data = callinfo.get_all(callsign) country = data["country"] if "country" in data else None dxcc_id = data["adif"] if "adif" in data else None continent = data["continent"] if "continent" in data else None cq_zone = data["cqz"] if "cqz" in data else None itu_zone = data["ituz"] if "ituz" in data else None lat = float(data["latitude"]) if "latitude" in data else None lon = float(data["longitude"]) if "longitude" in data else None grid = None if lat and lon: grid = latlong_to_locator(lat, lon) return Callsign(call=callsign, home_call=home_call, country=country, dxcc_id=dxcc_id, continent=continent, cq_zone=cq_zone, itu_zone=itu_zone, latitude=lat, longitude=lon, grid=grid, location_source="DXCC") except (KeyError, ValueError): # Unknown callsign, can't look anything up, return a Callsign object with basic data so that gets cached return Callsign(call=callsign)