2014-04-28 01:59:18 +02:00
|
|
|
import re
|
|
|
|
|
import logging
|
|
|
|
|
from datetime import datetime
|
2014-04-30 08:56:08 +02:00
|
|
|
import sys
|
2014-04-26 00:22:57 +02:00
|
|
|
|
2014-04-28 01:59:18 +02:00
|
|
|
import pytz
|
|
|
|
|
|
|
|
|
|
from pyhamtools.consts import LookupConventions as const
|
|
|
|
|
|
2015-04-06 20:00:30 +02:00
|
|
|
from pyhamtools.callsign_exceptions import callsign_exceptions
|
2014-04-28 01:59:18 +02:00
|
|
|
|
|
|
|
|
UTC = pytz.UTC
|
|
|
|
|
|
2014-04-30 08:56:08 +02:00
|
|
|
if sys.version_info < (2, 7, ):
|
|
|
|
|
class NullHandler(logging.Handler):
|
|
|
|
|
def emit(self, record):
|
|
|
|
|
pass
|
2014-04-26 00:22:57 +02:00
|
|
|
|
2014-06-15 09:06:17 +02:00
|
|
|
|
2014-04-26 00:22:57 +02:00
|
|
|
class Callinfo(object):
|
|
|
|
|
"""
|
2014-04-30 08:56:08 +02:00
|
|
|
The purpose of this class is to return data (country, latitude, longitude, CQ Zone...etc) for an
|
|
|
|
|
Amateur Radio callsign. The class can be used with any lookup database,
|
|
|
|
|
provided through an Instance of :py:class:`LookupLib`.
|
|
|
|
|
An instance of :py:class:`Lookuplib` has to be injected on object construction.
|
|
|
|
|
|
|
|
|
|
Args:
|
|
|
|
|
lookuplib (:py:class:`LookupLib`) : instance of :py:class:`LookupLib`
|
|
|
|
|
logger (logging.getLogger(__name__), optional): Python logger
|
|
|
|
|
|
2014-04-26 00:22:57 +02:00
|
|
|
"""
|
|
|
|
|
|
2014-04-30 08:56:08 +02:00
|
|
|
def __init__(self, lookuplib, logger=None):
|
2014-04-26 00:22:57 +02:00
|
|
|
|
2014-04-28 01:59:18 +02:00
|
|
|
self._logger = None
|
|
|
|
|
if logger:
|
|
|
|
|
self._logger = logger
|
|
|
|
|
else:
|
|
|
|
|
self._logger = logging.getLogger(__name__)
|
2014-04-30 08:56:08 +02:00
|
|
|
if sys.version_info[:2] == (2, 6):
|
|
|
|
|
self._logger.addHandler(NullHandler())
|
|
|
|
|
else:
|
|
|
|
|
self._logger.addHandler(logging.NullHandler())
|
2014-04-28 01:59:18 +02:00
|
|
|
|
|
|
|
|
self._lookuplib = lookuplib
|
|
|
|
|
self._callsign_info = None
|
|
|
|
|
|
2014-06-15 09:06:17 +02:00
|
|
|
@staticmethod
|
|
|
|
|
def get_homecall(callsign):
|
2014-09-25 23:46:47 +02:00
|
|
|
"""Strips off country prefixes (HC2/DH1TW) and activity suffixes (DH1TW/P).
|
2014-04-30 08:56:08 +02:00
|
|
|
|
|
|
|
|
Args:
|
|
|
|
|
callsign (str): Amateur Radio callsign
|
|
|
|
|
|
|
|
|
|
Returns:
|
|
|
|
|
str: callsign without country/activity pre/suffixes
|
|
|
|
|
|
|
|
|
|
Raises:
|
|
|
|
|
ValueError: No callsign found in string
|
|
|
|
|
|
|
|
|
|
Example:
|
2014-06-15 09:06:17 +02:00
|
|
|
The following code retrieves the home call for "HC2/DH1TW/P"
|
2014-04-30 08:56:08 +02:00
|
|
|
|
2014-06-15 09:06:17 +02:00
|
|
|
>>> from pyhamtools import LookupLib, Callinfo
|
|
|
|
|
>>> my_lookuplib = LookupLib(lookuptype="countryfile")
|
|
|
|
|
>>> cic = Callinfo(my_lookuplib)
|
|
|
|
|
>>> cic.get_homecall("HC2/DH1TW/P")
|
|
|
|
|
DH1TW
|
2014-04-30 08:56:08 +02:00
|
|
|
|
|
|
|
|
"""
|
2014-04-26 00:22:57 +02:00
|
|
|
|
2014-04-28 01:59:18 +02:00
|
|
|
callsign = callsign.upper()
|
2022-12-05 01:01:45 +01:00
|
|
|
homecall = re.search('[\\d]{0,1}[A-Z]{1,2}\\d([A-Z]{1,4}|\\d{3,3}|\\d{1,3}[A-Z])[A-Z]{0,5}', callsign)
|
2014-04-28 01:59:18 +02:00
|
|
|
if homecall:
|
|
|
|
|
homecall = homecall.group(0)
|
|
|
|
|
return homecall
|
|
|
|
|
else:
|
2014-04-30 08:56:08 +02:00
|
|
|
raise ValueError
|
|
|
|
|
|
Don't use timestamps as parameter defaults
Constructs like
def calculate_sunrise_sunset(locator, calc_date=datetime.utcnow()):
are actually wrong because they initialize the parameter default at
module load time, not at function call time. If the program is running
over some time, the result will be wrong.
As a side-effect, this fix makes the docs (and the whole project) build
reproducibly because previously the build time was embedded in the
sphinx docs:
lookup_prefix(prefix, timestamp=datetime.datetime(2019, 11, 27, 3, 4, 36, 93157, tzinfo=<UTC>))
2019-12-15 22:21:46 +01:00
|
|
|
def _iterate_prefix(self, callsign, timestamp=None):
|
2014-04-28 01:59:18 +02:00
|
|
|
"""truncate call until it corresponds to a Prefix in the database"""
|
|
|
|
|
prefix = callsign
|
Don't use timestamps as parameter defaults
Constructs like
def calculate_sunrise_sunset(locator, calc_date=datetime.utcnow()):
are actually wrong because they initialize the parameter default at
module load time, not at function call time. If the program is running
over some time, the result will be wrong.
As a side-effect, this fix makes the docs (and the whole project) build
reproducibly because previously the build time was embedded in the
sphinx docs:
lookup_prefix(prefix, timestamp=datetime.datetime(2019, 11, 27, 3, 4, 36, 93157, tzinfo=<UTC>))
2019-12-15 22:21:46 +01:00
|
|
|
if timestamp is None:
|
|
|
|
|
timestamp = datetime.utcnow().replace(tzinfo=UTC)
|
2016-01-11 23:38:28 +01:00
|
|
|
|
2015-04-06 20:00:30 +02:00
|
|
|
if re.search('(VK|AX|VI)9[A-Z]{3}', callsign): #special rule for VK9 calls
|
|
|
|
|
if timestamp > datetime(2006,1,1, tzinfo=UTC):
|
|
|
|
|
prefix = callsign[0:3]+callsign[4:5]
|
2014-04-28 01:59:18 +02:00
|
|
|
|
2014-06-15 09:06:17 +02:00
|
|
|
while len(prefix) > 0:
|
2014-04-28 01:59:18 +02:00
|
|
|
try:
|
|
|
|
|
return self._lookuplib.lookup_prefix(prefix, timestamp)
|
|
|
|
|
except KeyError:
|
|
|
|
|
prefix = prefix.replace(' ', '')[:-1]
|
|
|
|
|
continue
|
|
|
|
|
raise KeyError
|
|
|
|
|
|
2014-06-15 09:06:17 +02:00
|
|
|
@staticmethod
|
|
|
|
|
def check_if_mm(callsign):
|
2018-01-28 01:35:58 +01:00
|
|
|
check = callsign[-3:].upper()
|
|
|
|
|
return "/MM" in check
|
2014-05-08 15:03:16 +02:00
|
|
|
|
2014-06-15 09:06:17 +02:00
|
|
|
@staticmethod
|
|
|
|
|
def check_if_am(callsign):
|
2018-01-28 01:35:58 +01:00
|
|
|
check = callsign[-3:].upper()
|
|
|
|
|
return "/AM" in check
|
2014-05-08 15:03:16 +02:00
|
|
|
|
2014-06-15 09:06:17 +02:00
|
|
|
@staticmethod
|
|
|
|
|
def check_if_beacon(callsign):
|
2018-01-28 01:35:58 +01:00
|
|
|
check = callsign[-4:].upper()
|
|
|
|
|
return "/B" in check or "/BCN" in check
|
2014-05-08 15:03:16 +02:00
|
|
|
|
Don't use timestamps as parameter defaults
Constructs like
def calculate_sunrise_sunset(locator, calc_date=datetime.utcnow()):
are actually wrong because they initialize the parameter default at
module load time, not at function call time. If the program is running
over some time, the result will be wrong.
As a side-effect, this fix makes the docs (and the whole project) build
reproducibly because previously the build time was embedded in the
sphinx docs:
lookup_prefix(prefix, timestamp=datetime.datetime(2019, 11, 27, 3, 4, 36, 93157, tzinfo=<UTC>))
2019-12-15 22:21:46 +01:00
|
|
|
def _dismantle_callsign(self, callsign, timestamp=None):
|
2014-04-30 08:56:08 +02:00
|
|
|
""" try to identify the callsign's identity by analyzing it in the following order:
|
|
|
|
|
|
|
|
|
|
Args:
|
|
|
|
|
callsign (str): Amateur Radio callsign
|
|
|
|
|
timestamp (datetime, optional): datetime in UTC (tzinfo=pytz.UTC)
|
|
|
|
|
|
|
|
|
|
Raises:
|
|
|
|
|
KeyError: Callsign could not be identified
|
2014-04-28 01:59:18 +02:00
|
|
|
|
2014-04-30 08:56:08 +02:00
|
|
|
|
|
|
|
|
"""
|
2014-04-28 01:59:18 +02:00
|
|
|
entire_callsign = callsign.upper()
|
Don't use timestamps as parameter defaults
Constructs like
def calculate_sunrise_sunset(locator, calc_date=datetime.utcnow()):
are actually wrong because they initialize the parameter default at
module load time, not at function call time. If the program is running
over some time, the result will be wrong.
As a side-effect, this fix makes the docs (and the whole project) build
reproducibly because previously the build time was embedded in the
sphinx docs:
lookup_prefix(prefix, timestamp=datetime.datetime(2019, 11, 27, 3, 4, 36, 93157, tzinfo=<UTC>))
2019-12-15 22:21:46 +01:00
|
|
|
if timestamp is None:
|
|
|
|
|
timestamp = datetime.utcnow().replace(tzinfo=UTC)
|
2014-04-28 01:59:18 +02:00
|
|
|
|
2022-12-05 01:01:45 +01:00
|
|
|
if re.search('[/A-Z0-9\\-]{3,15}', entire_callsign): # make sure the call has at least 3 characters
|
2014-04-28 01:59:18 +02:00
|
|
|
|
2022-12-05 01:01:45 +01:00
|
|
|
if re.search('\\-\\d{1,3}$', entire_callsign): # cut off any -10 / -02 appendixes
|
|
|
|
|
callsign = re.sub('\\-\\d{1,3}$', '', entire_callsign)
|
2014-04-28 01:59:18 +02:00
|
|
|
|
2016-08-18 14:54:50 +02:00
|
|
|
if re.search('/[A-Z0-9]{1,4}/[A-Z0-9]{1,4}$', callsign):
|
2014-04-28 01:59:18 +02:00
|
|
|
callsign = re.sub('/[A-Z0-9]{1,4}$', '', callsign) # cut off 2. appendix DH1TW/HC2/P -> DH1TW/HC2
|
|
|
|
|
|
|
|
|
|
# multiple character appendix (callsign/xxx)
|
2014-09-27 01:48:59 +02:00
|
|
|
if re.search('[A-Z0-9]{4,10}/[A-Z0-9]{2,4}$', callsign): # case call/xxx, but ignoring /p and /m or /5
|
2014-04-28 01:59:18 +02:00
|
|
|
appendix = re.search('/[A-Z0-9]{2,4}$', callsign)
|
|
|
|
|
appendix = re.sub('/', '', appendix.group(0))
|
|
|
|
|
self._logger.debug("appendix: " + appendix)
|
2016-01-11 23:38:28 +01:00
|
|
|
|
2014-04-28 01:59:18 +02:00
|
|
|
if appendix == 'MM': # special case Martime Mobile
|
|
|
|
|
#self._mm = True
|
2014-06-15 09:06:17 +02:00
|
|
|
return {
|
2014-05-08 15:03:16 +02:00
|
|
|
'adif': 999,
|
|
|
|
|
'continent': '',
|
|
|
|
|
'country': 'MARITIME MOBILE',
|
|
|
|
|
'cqz': 0,
|
|
|
|
|
'latitude': 0.0,
|
|
|
|
|
'longitude': 0.0
|
|
|
|
|
}
|
2014-04-28 01:59:18 +02:00
|
|
|
elif appendix == 'AM': # special case Aeronautic Mobile
|
2014-06-15 09:06:17 +02:00
|
|
|
return {
|
2014-05-08 15:03:16 +02:00
|
|
|
'adif': 998,
|
|
|
|
|
'continent': '',
|
|
|
|
|
'country': 'AIRCAFT MOBILE',
|
|
|
|
|
'cqz': 0,
|
|
|
|
|
'latitude': 0.0,
|
|
|
|
|
'longitude': 0.0
|
|
|
|
|
}
|
2014-04-28 01:59:18 +02:00
|
|
|
elif appendix == 'QRP': # special case QRP
|
|
|
|
|
callsign = re.sub('/QRP', '', callsign)
|
|
|
|
|
return self._iterate_prefix(callsign, timestamp)
|
|
|
|
|
elif appendix == 'QRPP': # special case QRPP
|
|
|
|
|
callsign = re.sub('/QRPP', '', callsign)
|
|
|
|
|
return self._iterate_prefix(callsign, timestamp)
|
2014-06-15 09:06:17 +02:00
|
|
|
elif appendix == 'BCN': # filter all beacons
|
2014-04-28 01:59:18 +02:00
|
|
|
callsign = re.sub('/BCN', '', callsign)
|
2014-05-08 15:03:16 +02:00
|
|
|
data = self._iterate_prefix(callsign, timestamp).copy()
|
|
|
|
|
data[const.BEACON] = True
|
|
|
|
|
return data
|
2014-06-15 09:06:17 +02:00
|
|
|
elif appendix == "LH": # Filter all Lighthouses
|
2014-04-28 01:59:18 +02:00
|
|
|
callsign = re.sub('/LH', '', callsign)
|
|
|
|
|
return self._iterate_prefix(callsign, timestamp)
|
2015-04-06 20:00:30 +02:00
|
|
|
elif re.search('[A-Z]{3}', appendix): #case of US county(?) contest N3HBX/UAL
|
|
|
|
|
callsign = re.sub('/[A-Z]{3}$', '', callsign)
|
2016-01-11 23:38:28 +01:00
|
|
|
return self._iterate_prefix(callsign, timestamp)
|
|
|
|
|
|
2014-04-28 01:59:18 +02:00
|
|
|
else:
|
2014-06-15 09:06:17 +02:00
|
|
|
# check if the appendix is a valid country prefix
|
2014-04-28 01:59:18 +02:00
|
|
|
return self._iterate_prefix(re.sub('/', '', appendix), timestamp)
|
|
|
|
|
|
|
|
|
|
# Single character appendix (callsign/x)
|
|
|
|
|
elif re.search('/[A-Z0-9]$', callsign): # case call/p or /b /m or /5 etc.
|
|
|
|
|
appendix = re.search('/[A-Z0-9]$', callsign)
|
|
|
|
|
appendix = re.sub('/', '', appendix.group(0))
|
|
|
|
|
|
2014-06-15 09:06:17 +02:00
|
|
|
if appendix == 'B': # special case Beacon
|
2014-04-28 01:59:18 +02:00
|
|
|
callsign = re.sub('/B', '', callsign)
|
2014-06-15 09:06:17 +02:00
|
|
|
data = self._iterate_prefix(callsign, timestamp).copy()
|
2014-05-08 15:03:16 +02:00
|
|
|
data[const.BEACON] = True
|
|
|
|
|
return data
|
2014-04-28 01:59:18 +02:00
|
|
|
|
2022-12-05 01:01:45 +01:00
|
|
|
elif re.search('\\d$', appendix):
|
|
|
|
|
area_nr = re.search('\\d$', appendix).group(0)
|
|
|
|
|
callsign = re.sub('/\\d$', '', callsign) #remove /number
|
|
|
|
|
if len(re.findall(r'\\d+', callsign)) == 1: #call has just on digit e.g. DH1TW
|
|
|
|
|
callsign = re.sub('[\\d]+', area_nr, callsign)
|
2016-08-18 14:54:50 +02:00
|
|
|
else: # call has several digits e.g. 7N4AAL
|
2022-06-03 23:45:50 +02:00
|
|
|
pass # no (two) digit prefix countries known where appendix would change entity
|
2014-04-28 01:59:18 +02:00
|
|
|
return self._iterate_prefix(callsign, timestamp)
|
|
|
|
|
|
|
|
|
|
else:
|
|
|
|
|
return self._iterate_prefix(callsign, timestamp)
|
|
|
|
|
|
|
|
|
|
# regular callsigns, without prefix or appendix
|
2022-12-26 21:13:13 +01:00
|
|
|
elif re.match('^[\\d]{0,1}[A-Z]{1,2}\\d{1,2}[A-Z]{1,2}([A-Z]{1,4}|\\d{1,3})[A-Z]{0,5}$', callsign):
|
2014-04-28 01:59:18 +02:00
|
|
|
return self._iterate_prefix(callsign, timestamp)
|
|
|
|
|
|
|
|
|
|
# callsigns with prefixes (xxx/callsign)
|
|
|
|
|
elif re.search('^[A-Z0-9]{1,4}/', entire_callsign):
|
|
|
|
|
pfx = re.search('^[A-Z0-9]{1,4}/', entire_callsign)
|
|
|
|
|
pfx = re.sub('/', '', pfx.group(0))
|
2015-04-06 20:00:30 +02:00
|
|
|
#make sure that the remaining part is actually a callsign (avoid: OZ/JO81)
|
|
|
|
|
rest = re.search('/[A-Z0-9]+', entire_callsign)
|
2022-12-18 14:01:18 +01:00
|
|
|
if rest is None:
|
2022-12-18 14:40:20 +01:00
|
|
|
self._logger.warning(u"non latin characters in callsign '{0}'".format(entire_callsign))
|
2022-12-18 14:01:18 +01:00
|
|
|
raise KeyError
|
|
|
|
|
rest = re.sub('/', '', rest.group(0))
|
2022-12-05 01:01:45 +01:00
|
|
|
if re.match('^[\\d]{0,1}[A-Z]{1,2}\\d([A-Z]{1,4}|\\d{3,3}|\\d{1,3}[A-Z])[A-Z]{0,5}$', rest):
|
2015-04-06 20:00:30 +02:00
|
|
|
return self._iterate_prefix(pfx)
|
|
|
|
|
|
2016-01-11 23:38:28 +01:00
|
|
|
if entire_callsign in callsign_exceptions:
|
2015-04-06 20:00:30 +02:00
|
|
|
return self._iterate_prefix(callsign_exceptions[entire_callsign])
|
2016-01-11 23:38:28 +01:00
|
|
|
|
2014-04-28 01:59:18 +02:00
|
|
|
self._logger.debug("Could not decode " + callsign)
|
2015-04-06 20:00:30 +02:00
|
|
|
raise KeyError("Callsign could not be decoded")
|
2014-04-28 01:59:18 +02:00
|
|
|
|
Don't use timestamps as parameter defaults
Constructs like
def calculate_sunrise_sunset(locator, calc_date=datetime.utcnow()):
are actually wrong because they initialize the parameter default at
module load time, not at function call time. If the program is running
over some time, the result will be wrong.
As a side-effect, this fix makes the docs (and the whole project) build
reproducibly because previously the build time was embedded in the
sphinx docs:
lookup_prefix(prefix, timestamp=datetime.datetime(2019, 11, 27, 3, 4, 36, 93157, tzinfo=<UTC>))
2019-12-15 22:21:46 +01:00
|
|
|
def _lookup_callsign(self, callsign, timestamp=None):
|
|
|
|
|
if timestamp is None:
|
|
|
|
|
timestamp = datetime.utcnow().replace(tzinfo=UTC)
|
2014-04-28 01:59:18 +02:00
|
|
|
|
|
|
|
|
# Check if operation is invalid
|
|
|
|
|
invalid = False
|
|
|
|
|
try:
|
2016-08-18 14:54:50 +02:00
|
|
|
invalid = self._lookuplib.is_invalid_operation(callsign, timestamp)
|
|
|
|
|
if invalid:
|
2014-04-28 01:59:18 +02:00
|
|
|
raise KeyError
|
|
|
|
|
except KeyError:
|
|
|
|
|
if invalid:
|
|
|
|
|
raise
|
|
|
|
|
|
2014-05-08 15:03:16 +02:00
|
|
|
if self.check_if_mm(callsign):
|
2014-06-15 09:06:17 +02:00
|
|
|
return {
|
2014-05-08 15:03:16 +02:00
|
|
|
'adif': 999,
|
|
|
|
|
'continent': '',
|
|
|
|
|
'country': 'MARITIME MOBILE',
|
|
|
|
|
'cqz': 0,
|
|
|
|
|
'latitude': 0.0,
|
|
|
|
|
'longitude': 0.0
|
|
|
|
|
}
|
|
|
|
|
elif self.check_if_am(callsign):
|
2014-06-15 09:06:17 +02:00
|
|
|
return {
|
2014-05-08 15:03:16 +02:00
|
|
|
'adif': 998,
|
|
|
|
|
'continent': '',
|
|
|
|
|
'country': 'AIRCAFT MOBILE',
|
|
|
|
|
'cqz': 0,
|
|
|
|
|
'latitude': 0.0,
|
|
|
|
|
'longitude': 0.0
|
|
|
|
|
}
|
|
|
|
|
|
2015-04-06 20:00:30 +02:00
|
|
|
# Check if a dedicated entry/exception exists for the callsign
|
2014-04-28 01:59:18 +02:00
|
|
|
try:
|
2014-05-08 15:03:16 +02:00
|
|
|
data = self._lookuplib.lookup_callsign(callsign, timestamp).copy()
|
|
|
|
|
if self.check_if_beacon(callsign):
|
|
|
|
|
data[const.BEACON] = True
|
|
|
|
|
return data
|
2014-04-28 01:59:18 +02:00
|
|
|
except KeyError:
|
|
|
|
|
pass
|
|
|
|
|
|
|
|
|
|
# Dismantel the callsign and check if the prefix is known
|
|
|
|
|
return self._dismantle_callsign(callsign, timestamp)
|
|
|
|
|
|
Don't use timestamps as parameter defaults
Constructs like
def calculate_sunrise_sunset(locator, calc_date=datetime.utcnow()):
are actually wrong because they initialize the parameter default at
module load time, not at function call time. If the program is running
over some time, the result will be wrong.
As a side-effect, this fix makes the docs (and the whole project) build
reproducibly because previously the build time was embedded in the
sphinx docs:
lookup_prefix(prefix, timestamp=datetime.datetime(2019, 11, 27, 3, 4, 36, 93157, tzinfo=<UTC>))
2019-12-15 22:21:46 +01:00
|
|
|
def get_all(self, callsign, timestamp=None):
|
2014-04-30 08:56:08 +02:00
|
|
|
""" Lookup a callsign and return all data available from the underlying database
|
|
|
|
|
|
|
|
|
|
Args:
|
|
|
|
|
callsign (str): Amateur Radio callsign
|
|
|
|
|
timestamp (datetime, optional): datetime in UTC (tzinfo=pytz.UTC)
|
|
|
|
|
|
|
|
|
|
Returns:
|
|
|
|
|
dict: Dictionary containing the callsign specific data
|
|
|
|
|
|
|
|
|
|
Raises:
|
|
|
|
|
KeyError: Callsign could not be identified
|
|
|
|
|
|
|
|
|
|
Example:
|
2014-06-15 09:06:17 +02:00
|
|
|
The following code returns all available information from the country-files.com database for the
|
|
|
|
|
callsign "DH1TW"
|
|
|
|
|
|
|
|
|
|
>>> from pyhamtools import LookupLib, Callinfo
|
|
|
|
|
>>> my_lookuplib = LookupLib(lookuptype="countryfile")
|
|
|
|
|
>>> cic = Callinfo(my_lookuplib)
|
|
|
|
|
>>> cic.get_all("DH1TW")
|
|
|
|
|
{
|
2014-04-30 08:56:08 +02:00
|
|
|
'country': 'Fed. Rep. of Germany',
|
|
|
|
|
'adif': 230,
|
|
|
|
|
'continent': 'EU',
|
|
|
|
|
'latitude': 51.0,
|
|
|
|
|
'longitude': -10.0,
|
|
|
|
|
'cqz': 14,
|
|
|
|
|
'ituz': 28
|
2014-06-15 09:06:17 +02:00
|
|
|
}
|
2014-04-30 08:56:08 +02:00
|
|
|
|
|
|
|
|
Note:
|
|
|
|
|
The content of the returned data depends entirely on the injected
|
|
|
|
|
:py:class:`LookupLib` (and the used database). While the country-files.com provides
|
|
|
|
|
for example the ITU Zone, Clublog doesn't. Consequently, the item "ituz"
|
|
|
|
|
would be missing with Clublog (API or XML) :py:class:`LookupLib`.
|
|
|
|
|
|
|
|
|
|
"""
|
2020-03-03 01:22:46 +01:00
|
|
|
|
|
|
|
|
callsign = callsign.upper()
|
|
|
|
|
|
Don't use timestamps as parameter defaults
Constructs like
def calculate_sunrise_sunset(locator, calc_date=datetime.utcnow()):
are actually wrong because they initialize the parameter default at
module load time, not at function call time. If the program is running
over some time, the result will be wrong.
As a side-effect, this fix makes the docs (and the whole project) build
reproducibly because previously the build time was embedded in the
sphinx docs:
lookup_prefix(prefix, timestamp=datetime.datetime(2019, 11, 27, 3, 4, 36, 93157, tzinfo=<UTC>))
2019-12-15 22:21:46 +01:00
|
|
|
if timestamp is None:
|
|
|
|
|
timestamp = datetime.utcnow().replace(tzinfo=UTC)
|
|
|
|
|
|
2016-01-11 23:38:28 +01:00
|
|
|
callsign_data = self._lookup_callsign(callsign, timestamp)
|
2014-04-28 01:59:18 +02:00
|
|
|
|
|
|
|
|
try:
|
|
|
|
|
cqz = self._lookuplib.lookup_zone_exception(callsign, timestamp)
|
|
|
|
|
callsign_data[const.CQZ] = cqz
|
|
|
|
|
except KeyError:
|
|
|
|
|
pass
|
|
|
|
|
|
|
|
|
|
return callsign_data
|
|
|
|
|
|
Don't use timestamps as parameter defaults
Constructs like
def calculate_sunrise_sunset(locator, calc_date=datetime.utcnow()):
are actually wrong because they initialize the parameter default at
module load time, not at function call time. If the program is running
over some time, the result will be wrong.
As a side-effect, this fix makes the docs (and the whole project) build
reproducibly because previously the build time was embedded in the
sphinx docs:
lookup_prefix(prefix, timestamp=datetime.datetime(2019, 11, 27, 3, 4, 36, 93157, tzinfo=<UTC>))
2019-12-15 22:21:46 +01:00
|
|
|
def is_valid_callsign(self, callsign, timestamp=None):
|
2014-04-30 08:56:08 +02:00
|
|
|
""" Checks if a callsign is valid
|
|
|
|
|
|
|
|
|
|
Args:
|
|
|
|
|
callsign (str): Amateur Radio callsign
|
|
|
|
|
timestamp (datetime, optional): datetime in UTC (tzinfo=pytz.UTC)
|
|
|
|
|
|
|
|
|
|
Returns:
|
|
|
|
|
bool: True / False
|
|
|
|
|
|
|
|
|
|
Example:
|
2014-06-15 09:06:17 +02:00
|
|
|
The following checks if "DH1TW" is a valid callsign
|
2014-04-30 08:56:08 +02:00
|
|
|
|
2014-06-15 09:06:17 +02:00
|
|
|
>>> from pyhamtools import LookupLib, Callinfo
|
|
|
|
|
>>> my_lookuplib = LookupLib(lookuptype="countryfile")
|
|
|
|
|
>>> cic = Callinfo(my_lookuplib)
|
|
|
|
|
>>> cic.is_valid_callsign("DH1TW")
|
|
|
|
|
True
|
2014-04-30 08:56:08 +02:00
|
|
|
|
|
|
|
|
"""
|
Don't use timestamps as parameter defaults
Constructs like
def calculate_sunrise_sunset(locator, calc_date=datetime.utcnow()):
are actually wrong because they initialize the parameter default at
module load time, not at function call time. If the program is running
over some time, the result will be wrong.
As a side-effect, this fix makes the docs (and the whole project) build
reproducibly because previously the build time was embedded in the
sphinx docs:
lookup_prefix(prefix, timestamp=datetime.datetime(2019, 11, 27, 3, 4, 36, 93157, tzinfo=<UTC>))
2019-12-15 22:21:46 +01:00
|
|
|
if timestamp is None:
|
|
|
|
|
timestamp = datetime.utcnow().replace(tzinfo=UTC)
|
|
|
|
|
|
2014-04-28 01:59:18 +02:00
|
|
|
try:
|
|
|
|
|
if self.get_all(callsign, timestamp):
|
|
|
|
|
return True
|
2014-04-30 08:56:08 +02:00
|
|
|
except KeyError:
|
2014-04-28 01:59:18 +02:00
|
|
|
return False
|
|
|
|
|
|
Don't use timestamps as parameter defaults
Constructs like
def calculate_sunrise_sunset(locator, calc_date=datetime.utcnow()):
are actually wrong because they initialize the parameter default at
module load time, not at function call time. If the program is running
over some time, the result will be wrong.
As a side-effect, this fix makes the docs (and the whole project) build
reproducibly because previously the build time was embedded in the
sphinx docs:
lookup_prefix(prefix, timestamp=datetime.datetime(2019, 11, 27, 3, 4, 36, 93157, tzinfo=<UTC>))
2019-12-15 22:21:46 +01:00
|
|
|
def get_lat_long(self, callsign, timestamp=None):
|
2014-04-30 08:56:08 +02:00
|
|
|
""" Returns Latitude and Longitude for a callsign
|
|
|
|
|
|
|
|
|
|
Args:
|
|
|
|
|
callsign (str): Amateur Radio callsign
|
|
|
|
|
timestamp (datetime, optional): datetime in UTC (tzinfo=pytz.UTC)
|
|
|
|
|
|
|
|
|
|
Returns:
|
|
|
|
|
dict: Containing Latitude and Longitude
|
|
|
|
|
|
|
|
|
|
Raises:
|
|
|
|
|
KeyError: No data found for callsign
|
|
|
|
|
|
|
|
|
|
Example:
|
2014-06-15 09:06:17 +02:00
|
|
|
The following code returns Latitude & Longitude for "DH1TW"
|
2014-04-30 08:56:08 +02:00
|
|
|
|
2014-06-15 09:06:17 +02:00
|
|
|
>>> from pyhamtools import LookupLib, Callinfo
|
|
|
|
|
>>> my_lookuplib = LookupLib(lookuptype="countryfile")
|
|
|
|
|
>>> cic = Callinfo(my_lookuplib)
|
|
|
|
|
>>> cic.get_lat_long("DH1TW")
|
|
|
|
|
{
|
|
|
|
|
'latitude': 51.0,
|
|
|
|
|
'longitude': -10.0
|
|
|
|
|
}
|
2014-04-30 08:56:08 +02:00
|
|
|
|
|
|
|
|
Note:
|
|
|
|
|
Unfortunately, in most cases the returned Latitude and Longitude are not very precise.
|
|
|
|
|
Clublog and Country-files.com use the country's capital coordinates in most cases, if no
|
2015-04-06 21:03:37 +02:00
|
|
|
dedicated entry in the database exists. Best results will be retrieved with QRZ.com Lookup.
|
2014-04-30 08:56:08 +02:00
|
|
|
|
|
|
|
|
"""
|
Don't use timestamps as parameter defaults
Constructs like
def calculate_sunrise_sunset(locator, calc_date=datetime.utcnow()):
are actually wrong because they initialize the parameter default at
module load time, not at function call time. If the program is running
over some time, the result will be wrong.
As a side-effect, this fix makes the docs (and the whole project) build
reproducibly because previously the build time was embedded in the
sphinx docs:
lookup_prefix(prefix, timestamp=datetime.datetime(2019, 11, 27, 3, 4, 36, 93157, tzinfo=<UTC>))
2019-12-15 22:21:46 +01:00
|
|
|
if timestamp is None:
|
|
|
|
|
timestamp = datetime.utcnow().replace(tzinfo=UTC)
|
|
|
|
|
|
2014-05-02 01:36:12 +02:00
|
|
|
callsign_data = self.get_all(callsign, timestamp=timestamp)
|
2014-04-28 01:59:18 +02:00
|
|
|
return {
|
2014-06-15 09:06:17 +02:00
|
|
|
const.LATITUDE: callsign_data[const.LATITUDE],
|
|
|
|
|
const.LONGITUDE: callsign_data[const.LONGITUDE]
|
2014-04-28 01:59:18 +02:00
|
|
|
}
|
|
|
|
|
|
Don't use timestamps as parameter defaults
Constructs like
def calculate_sunrise_sunset(locator, calc_date=datetime.utcnow()):
are actually wrong because they initialize the parameter default at
module load time, not at function call time. If the program is running
over some time, the result will be wrong.
As a side-effect, this fix makes the docs (and the whole project) build
reproducibly because previously the build time was embedded in the
sphinx docs:
lookup_prefix(prefix, timestamp=datetime.datetime(2019, 11, 27, 3, 4, 36, 93157, tzinfo=<UTC>))
2019-12-15 22:21:46 +01:00
|
|
|
def get_cqz(self, callsign, timestamp=None):
|
2014-04-30 08:56:08 +02:00
|
|
|
""" Returns CQ Zone of a callsign
|
|
|
|
|
|
|
|
|
|
Args:
|
|
|
|
|
callsign (str): Amateur Radio callsign
|
|
|
|
|
timestamp (datetime, optional): datetime in UTC (tzinfo=pytz.UTC)
|
|
|
|
|
|
|
|
|
|
Returns:
|
|
|
|
|
int: containing the callsign's CQ Zone
|
|
|
|
|
|
|
|
|
|
Raises:
|
|
|
|
|
KeyError: no CQ Zone found for callsign
|
|
|
|
|
|
|
|
|
|
"""
|
Don't use timestamps as parameter defaults
Constructs like
def calculate_sunrise_sunset(locator, calc_date=datetime.utcnow()):
are actually wrong because they initialize the parameter default at
module load time, not at function call time. If the program is running
over some time, the result will be wrong.
As a side-effect, this fix makes the docs (and the whole project) build
reproducibly because previously the build time was embedded in the
sphinx docs:
lookup_prefix(prefix, timestamp=datetime.datetime(2019, 11, 27, 3, 4, 36, 93157, tzinfo=<UTC>))
2019-12-15 22:21:46 +01:00
|
|
|
if timestamp is None:
|
|
|
|
|
timestamp = datetime.utcnow().replace(tzinfo=UTC)
|
|
|
|
|
|
2014-04-28 01:59:18 +02:00
|
|
|
return self.get_all(callsign, timestamp)[const.CQZ]
|
2014-04-26 00:22:57 +02:00
|
|
|
|
Don't use timestamps as parameter defaults
Constructs like
def calculate_sunrise_sunset(locator, calc_date=datetime.utcnow()):
are actually wrong because they initialize the parameter default at
module load time, not at function call time. If the program is running
over some time, the result will be wrong.
As a side-effect, this fix makes the docs (and the whole project) build
reproducibly because previously the build time was embedded in the
sphinx docs:
lookup_prefix(prefix, timestamp=datetime.datetime(2019, 11, 27, 3, 4, 36, 93157, tzinfo=<UTC>))
2019-12-15 22:21:46 +01:00
|
|
|
def get_ituz(self, callsign, timestamp=None):
|
2014-04-30 08:56:08 +02:00
|
|
|
""" Returns ITU Zone of a callsign
|
|
|
|
|
|
|
|
|
|
Args:
|
|
|
|
|
callsign (str): Amateur Radio callsign
|
|
|
|
|
timestamp (datetime, optional): datetime in UTC (tzinfo=pytz.UTC)
|
|
|
|
|
|
|
|
|
|
Returns:
|
|
|
|
|
int: containing the callsign's CQ Zone
|
|
|
|
|
|
|
|
|
|
Raises:
|
|
|
|
|
KeyError: No ITU Zone found for callsign
|
|
|
|
|
|
|
|
|
|
Note:
|
|
|
|
|
Currently, only Country-files.com lookup database contains ITU Zones
|
|
|
|
|
|
|
|
|
|
"""
|
Don't use timestamps as parameter defaults
Constructs like
def calculate_sunrise_sunset(locator, calc_date=datetime.utcnow()):
are actually wrong because they initialize the parameter default at
module load time, not at function call time. If the program is running
over some time, the result will be wrong.
As a side-effect, this fix makes the docs (and the whole project) build
reproducibly because previously the build time was embedded in the
sphinx docs:
lookup_prefix(prefix, timestamp=datetime.datetime(2019, 11, 27, 3, 4, 36, 93157, tzinfo=<UTC>))
2019-12-15 22:21:46 +01:00
|
|
|
if timestamp is None:
|
|
|
|
|
timestamp = datetime.utcnow().replace(tzinfo=UTC)
|
|
|
|
|
|
2014-04-28 01:59:18 +02:00
|
|
|
return self.get_all(callsign, timestamp)[const.ITUZ]
|
2014-04-26 00:22:57 +02:00
|
|
|
|
Don't use timestamps as parameter defaults
Constructs like
def calculate_sunrise_sunset(locator, calc_date=datetime.utcnow()):
are actually wrong because they initialize the parameter default at
module load time, not at function call time. If the program is running
over some time, the result will be wrong.
As a side-effect, this fix makes the docs (and the whole project) build
reproducibly because previously the build time was embedded in the
sphinx docs:
lookup_prefix(prefix, timestamp=datetime.datetime(2019, 11, 27, 3, 4, 36, 93157, tzinfo=<UTC>))
2019-12-15 22:21:46 +01:00
|
|
|
def get_country_name(self, callsign, timestamp=None):
|
2014-04-30 08:56:08 +02:00
|
|
|
""" Returns the country name where the callsign is located
|
|
|
|
|
|
|
|
|
|
Args:
|
|
|
|
|
callsign (str): Amateur Radio callsign
|
|
|
|
|
timestamp (datetime, optional): datetime in UTC (tzinfo=pytz.UTC)
|
|
|
|
|
|
|
|
|
|
Returns:
|
|
|
|
|
str: name of the Country
|
|
|
|
|
|
|
|
|
|
Raises:
|
|
|
|
|
KeyError: No Country found for callsign
|
|
|
|
|
|
|
|
|
|
Note:
|
|
|
|
|
Don't rely on the country name when working with several instances of
|
2014-05-02 01:36:12 +02:00
|
|
|
py:class:`Callinfo`. Clublog and Country-files.org use slightly different names
|
2014-06-15 09:06:17 +02:00
|
|
|
for countries. Example:
|
2014-05-02 01:36:12 +02:00
|
|
|
|
|
|
|
|
- Country-files.com: "Fed. Rep. of Germany"
|
|
|
|
|
- Clublog: "FEDERAL REPUBLIC OF GERMANY"
|
2014-04-30 08:56:08 +02:00
|
|
|
|
|
|
|
|
"""
|
Don't use timestamps as parameter defaults
Constructs like
def calculate_sunrise_sunset(locator, calc_date=datetime.utcnow()):
are actually wrong because they initialize the parameter default at
module load time, not at function call time. If the program is running
over some time, the result will be wrong.
As a side-effect, this fix makes the docs (and the whole project) build
reproducibly because previously the build time was embedded in the
sphinx docs:
lookup_prefix(prefix, timestamp=datetime.datetime(2019, 11, 27, 3, 4, 36, 93157, tzinfo=<UTC>))
2019-12-15 22:21:46 +01:00
|
|
|
if timestamp is None:
|
|
|
|
|
timestamp = datetime.utcnow().replace(tzinfo=UTC)
|
|
|
|
|
|
2014-04-28 01:59:18 +02:00
|
|
|
return self.get_all(callsign, timestamp)[const.COUNTRY]
|
2014-04-26 00:22:57 +02:00
|
|
|
|
Don't use timestamps as parameter defaults
Constructs like
def calculate_sunrise_sunset(locator, calc_date=datetime.utcnow()):
are actually wrong because they initialize the parameter default at
module load time, not at function call time. If the program is running
over some time, the result will be wrong.
As a side-effect, this fix makes the docs (and the whole project) build
reproducibly because previously the build time was embedded in the
sphinx docs:
lookup_prefix(prefix, timestamp=datetime.datetime(2019, 11, 27, 3, 4, 36, 93157, tzinfo=<UTC>))
2019-12-15 22:21:46 +01:00
|
|
|
def get_adif_id(self, callsign, timestamp=None):
|
2014-04-30 08:56:08 +02:00
|
|
|
""" Returns ADIF id of a callsign's country
|
2014-04-26 00:22:57 +02:00
|
|
|
|
2014-04-30 08:56:08 +02:00
|
|
|
Args:
|
|
|
|
|
callsign (str): Amateur Radio callsign
|
|
|
|
|
timestamp (datetime, optional): datetime in UTC (tzinfo=pytz.UTC)
|
2014-04-26 00:22:57 +02:00
|
|
|
|
2014-04-30 08:56:08 +02:00
|
|
|
Returns:
|
|
|
|
|
int: containing the country ADIF id
|
2014-04-26 00:22:57 +02:00
|
|
|
|
2014-04-30 08:56:08 +02:00
|
|
|
Raises:
|
|
|
|
|
KeyError: No Country found for callsign
|
2014-04-26 00:22:57 +02:00
|
|
|
|
2014-04-30 08:56:08 +02:00
|
|
|
"""
|
Don't use timestamps as parameter defaults
Constructs like
def calculate_sunrise_sunset(locator, calc_date=datetime.utcnow()):
are actually wrong because they initialize the parameter default at
module load time, not at function call time. If the program is running
over some time, the result will be wrong.
As a side-effect, this fix makes the docs (and the whole project) build
reproducibly because previously the build time was embedded in the
sphinx docs:
lookup_prefix(prefix, timestamp=datetime.datetime(2019, 11, 27, 3, 4, 36, 93157, tzinfo=<UTC>))
2019-12-15 22:21:46 +01:00
|
|
|
if timestamp is None:
|
|
|
|
|
timestamp = datetime.utcnow().replace(tzinfo=UTC)
|
|
|
|
|
|
2014-04-30 08:56:08 +02:00
|
|
|
return self.get_all(callsign, timestamp)[const.ADIF]
|
2014-04-26 00:22:57 +02:00
|
|
|
|
Don't use timestamps as parameter defaults
Constructs like
def calculate_sunrise_sunset(locator, calc_date=datetime.utcnow()):
are actually wrong because they initialize the parameter default at
module load time, not at function call time. If the program is running
over some time, the result will be wrong.
As a side-effect, this fix makes the docs (and the whole project) build
reproducibly because previously the build time was embedded in the
sphinx docs:
lookup_prefix(prefix, timestamp=datetime.datetime(2019, 11, 27, 3, 4, 36, 93157, tzinfo=<UTC>))
2019-12-15 22:21:46 +01:00
|
|
|
def get_continent(self, callsign, timestamp=None):
|
2014-04-30 08:56:08 +02:00
|
|
|
""" Returns the continent Identifier of a callsign
|
|
|
|
|
|
|
|
|
|
Args:
|
|
|
|
|
callsign (str): Amateur Radio callsign
|
|
|
|
|
timestamp (datetime, optional): datetime in UTC (tzinfo=pytz.UTC)
|
|
|
|
|
|
|
|
|
|
Returns:
|
|
|
|
|
str: continent identified
|
|
|
|
|
|
|
|
|
|
Raises:
|
|
|
|
|
KeyError: No Continent found for callsign
|
|
|
|
|
|
|
|
|
|
Note:
|
|
|
|
|
The following continent identifiers are used:
|
|
|
|
|
|
|
|
|
|
- EU: Europe
|
|
|
|
|
- NA: North America
|
|
|
|
|
- SA: South America
|
|
|
|
|
- AS: Asia
|
|
|
|
|
- AF: Africa
|
|
|
|
|
- OC: Oceania
|
|
|
|
|
- AN: Antarctica
|
|
|
|
|
"""
|
Don't use timestamps as parameter defaults
Constructs like
def calculate_sunrise_sunset(locator, calc_date=datetime.utcnow()):
are actually wrong because they initialize the parameter default at
module load time, not at function call time. If the program is running
over some time, the result will be wrong.
As a side-effect, this fix makes the docs (and the whole project) build
reproducibly because previously the build time was embedded in the
sphinx docs:
lookup_prefix(prefix, timestamp=datetime.datetime(2019, 11, 27, 3, 4, 36, 93157, tzinfo=<UTC>))
2019-12-15 22:21:46 +01:00
|
|
|
if timestamp is None:
|
|
|
|
|
timestamp = datetime.utcnow().replace(tzinfo=UTC)
|
|
|
|
|
|
2016-01-11 23:38:28 +01:00
|
|
|
return self.get_all(callsign, timestamp)[const.CONTINENT]
|