# Reference

## pyhamtools.locator

pyhamtools.locator.calculate_distance(locator1, locator2)

  calculates the (shortpath) distance between two Maidenhead locators

  Args:

    locator1 (string): Locator, either 4, 6 or 8 characters locator2 (string): Locator, either 4, 6 or 8 characters

  Returns:

    float: Distance in km

  Raises:

    ValueError: When called with wrong or invalid maidenhead locator strings AttributeError: When args are not a string

  Example:

    The following calculates the distance between two Maidenhead locators in km

        >>> from pyhamtools.locator import calculate_distance
        >>> calculate_distance("JN48QM", "QF67bf")
        16466.413

  Note:

    Distances is calculated between the centers of the (sub) squares

pyhamtools.locator.calculate_distance_longpath(locator1, locator2)

  calculates the (longpath) distance between two Maidenhead locators

  Args:

    locator1 (string): Locator, either 4, 6 or 8 characters locator2 (string): Locator, either 4, 6 or 8 characters

  Returns:

    float: Distance in km

  Raises:

    ValueError: When called with wrong or invalid input arg AttributeError: When args are not a string

  Example:

    The following calculates the longpath distance between two Maidenhead locators in km

        >>> from pyhamtools.locator import calculate_distance_longpath
        >>> calculate_distance_longpath("JN48QM", "QF67bf")
        23541.5867

  Note:

    Distance is calculated between the centers of the (sub) squares

pyhamtools.locator.calculate_heading(locator1, locator2)

  calculates the heading from the first to the second locator

  Args:

    locator1 (string): Locator, either 4, 6 or 8 characters locator2 (string): Locator, either 4, 6 or 6 characters

  Returns:

    float: Heading in deg

  Raises:

    ValueError: When called with wrong or invalid input arg AttributeError: When args are not a string

  Example:

    The following calculates the heading from locator1 to locator2

        >>> from pyhamtools.locator import calculate_heading
        >>> calculate_heading("JN48QM", "QF67bf")
        74.3136

  Note:

    Heading is calculated between the centers of the (sub) squares

pyhamtools.locator.calculate_heading_longpath(locator1, locator2)

  calculates the heading from the first to the second locator (long path)

  Args:

    locator1 (string): Locator, either 4, 6 or 8 characters locator2 (string): Locator, either 4, 6 or 8 characters

  Returns:

    float: Long path heading in deg

  Raises:

    ValueError: When called with wrong or invalid input arg AttributeError: When args are not a string

  Example:

    The following calculates the long path heading from locator1 to locator2

        >>> from pyhamtools.locator import calculate_heading_longpath
        >>> calculate_heading_longpath("JN48QM", "QF67bf")
        254.3136

  Note:

    Distance is calculated between the centers of the (sub) squares

pyhamtools.locator.calculate_sunrise_sunset(locator, calc_date=None)

  calculates the next sunset and sunrise for a Maidenhead locator at a give date & time

  Args:

    locator1 (string): Maidenhead Locator, either 4, 6 or 8 characters calc_date (datetime, optional): Starting datetime for the calculations (UTC)

  Returns:

    dict: Containing datetimes for morning_dawn, sunrise, evening_dawn, sunset

  Raises:

    ValueError: When called with wrong or invalid input arg AttributeError: When args are not a string

  Example:

    The following calculates the next sunrise & sunset for JN48QM on the 1./Jan/2014

        >>> from pyhamtools.locator import calculate_sunrise_sunset
        >>> from datetime import datetime, timezone
        >>> myDate = datetime(year=2014, month=1, day=1, tzinfo=timezone.utc)
        >>> calculate_sunrise_sunset("JN48QM", myDate)
        {
            'morning_dawn': datetime.datetime(2014, 1, 1, 6, 36, 51, 710524, tzinfo=datetime.timezone.utc),
            'sunset': datetime.datetime(2014, 1, 1, 16, 15, 23, 31016, tzinfo=datetime.timezone.utc),
            'evening_dawn': datetime.datetime(2014, 1, 1, 15, 38, 8, 355315, tzinfo=datetime.timezone.utc),
            'sunrise': datetime.datetime(2014, 1, 1, 7, 14, 6, 162063, tzinfo=datetime.timezone.utc)
        }

pyhamtools.locator.latlong_to_locator(latitude, longitude, precision=6)

  converts WGS84 coordinates into the corresponding Maidenhead Locator

  Args:

    latitude (float): Latitude longitude (float): Longitude precision (int): 4,6,8,10 chars (default 6)

  Returns:

    string: Maidenhead locator

  Raises:

    ValueError: When called with wrong or invalid input args TypeError: When args are non float values

  Example:

    The following example converts latitude and longitude into the Maidenhead locator

        >>> from pyhamtools.locator import latlong_to_locator
        >>> latitude = 48.5208333
        >>> longitude = 9.375
        >>> latlong_to_locator(latitude, longitude)
        'JN48QM'

  Note:

    Latitude (negative = West, positive = East) Longitude (negative = South, positive = North)

pyhamtools.locator.locator_to_latlong(locator, center=True)

  converts Maidenhead locator in the corresponding WGS84 coordinates

  Args:

    locator (string): Locator, either 4, 6 or 8 characters center (bool): Center of (sub)square. By default True. If False, the south/western corner will be returned

  Returns:

    tuple (float, float): Latitude, Longitude

  Raises:

    ValueError: When called with wrong or invalid Maidenhead locator string TypeError: When arg is not a string

  Example:

    The following example converts a Maidenhead locator into Latitude and Longitude

        >>> from pyhamtools.locator import locator_to_latlong
        >>> latitude, longitude = locator_to_latlong("JN48QM")
        >>> print latitude, longitude
        48.5208333333 9.375

  Note:

    Latitude (negative = West, positive = East) Longitude (negative = South, positive = North)

## pyhamtools.qsl

pyhamtools.qsl.get_clublog_users(**kwargs)

  Download the latest official list of `Clublog`__ users.

  > Args:

  >   url (str, optional): Download URL

  > Returns:
  >   dict: Dictionary containing (if data available) the fields:

  >     firstqso, lastqso, last-lotw, lastupload (datetime), locator (string) and oqrs (boolean)

  > Raises:

  >   IOError: When network is unavailable, file can’t be downloaded or processed

  > Example:

  >   The following example downloads the Clublog user list and returns a dictionary with the data of HC2/AL1O:

  >       >>> from pyhamtools.qsl import get_clublog_users
  >       >>> clublog = get_lotw_users()
  >       >>> clublog['HC2/AL1O']
  >       {'firstqso': datetime.datetime(2012, 1, 1, 19, 59, 27),
  >        'last-lotw': datetime.datetime(2013, 5, 9, 1, 56, 23),
  >        'lastqso': datetime.datetime(2013, 5, 5, 6, 39, 3),
  >        'lastupload': datetime.datetime(2013, 5, 8, 15, 0, 6),
  >        'oqrs': True}

pyhamtools.qsl.get_eqsl_users(**kwargs)

  Download the latest official list of `EQSL.cc`__ users. The list of users can be found here.

  > Args:

  >   url (str, optional): Download URL

  > Returns:

  >   list: List containing the callsigns of EQSL users (unicode)

  > Raises:

  >   IOError: When network is unavailable, file can’t be downloaded or processed

  > Example:

  >   The following example downloads the EQSL user list and checks if DH1TW is a user:

  >       >>> from pyhamtools.qsl import get_eqsl_users
  >       >>> mylist = get_eqsl_users()
  >       >>> try:
  >       >>>    mylist.index('DH1TW')
  >       >>> except ValueError as e:
  >       >>>    print e
  >       'DH1TW' is not in list

pyhamtools.qsl.get_lotw_users(**kwargs)

  Download the latest official list of `ARRL Logbook of the World (LOTW)`__ users.

  > Args:

  >   url (str, optional): Download URL

  > Returns:

  >   dict: Dictionary containing the callsign (unicode) date of the last LOTW upload (datetime)

  > Raises:

  >   IOError: When network is unavailable, file can’t be downloaded or processed

  >   ValueError: Raised when data from file can’t be read

  > Example:

  >   The following example downloads the LOTW user list and check when DH1TW has made his last LOTW upload:

  >       >>> from pyhamtools.qsl import get_lotw_users
  >       >>> mydict = get_lotw_users()
  >       >>> mydict['DH1TW']
  >       datetime.datetime(2014, 9, 7, 0, 0)

## pyhamtools.frequency

pyhamtools.frequency.freq_to_band(freq)

  converts a Frequency [kHz] into the band and mode according to the IARU bandplan

  Args:

    frequency (float): Frequency in kHz

  Returns:

    dict: Dictionary containing the band (int) and mode (str)

  Raises:

    KeyError: Wrong frequency or out of band

  Example:

    The following example converts the frequency 14005.3 kHz into band and mode.

        >>> from pyhamtools.utils import freq_to_band
        >>> print freq_to_band(14005.3)
        {
             'band': 20,
             'mode': CW
        }

  Note:

  > Modes are:

  > > - CW

  > > - USB

  > > - LSB

  > > - DIGITAL

## pyhamtools.callinfo

class pyhamtools.callinfo.Callinfo(lookuplib, logger=None)

  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 `LookupLib`. An instance of `Lookuplib` has to be injected on object construction.

  Args:

    lookuplib (`LookupLib`) : instance of `LookupLib` logger (logging.getLogger(__name__), optional): Python logger

  get_adif_id(callsign, timestamp=None)

    Returns ADIF id of a callsign’s country

    Args:

      callsign (str): Amateur Radio callsign timestamp (datetime, optional): datetime in UTC (tzinfo=timezone.utc)

    Returns:

      int: containing the country ADIF id

    Raises:

      KeyError: No Country found for callsign

  get_all(callsign, timestamp=None)

    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=timezone.utc)

    Returns:

      dict: Dictionary containing the callsign specific data

    Raises:

      KeyError: Callsign could not be identified

    Example:

      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")
          {
              'country': 'Fed. Rep. of Germany',
              'adif': 230,
              'continent': 'EU',
              'latitude': 51.0,
              'longitude': -10.0,
              'cqz': 14,
              'ituz': 28
          }

    Note:

      The content of the returned data depends entirely on the injected `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) `LookupLib`.

  get_continent(callsign, timestamp=None)

    Returns the continent Identifier of a callsign

    Args:

      callsign (str): Amateur Radio callsign timestamp (datetime, optional): datetime in UTC (tzinfo=timezone.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

  get_country_name(callsign, timestamp=None)

    Returns the country name where the callsign is located

    Args:

      callsign (str): Amateur Radio callsign timestamp (datetime, optional): datetime in UTC (tzinfo=timezone.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 py:class:Callinfo. Clublog and Country-files.org use slightly different names for countries. Example:

      - Country-files.com: “Fed. Rep. of Germany”

      - Clublog: “FEDERAL REPUBLIC OF GERMANY”

  get_cqz(callsign, timestamp=None)

    Returns CQ Zone of a callsign

    Args:

      callsign (str): Amateur Radio callsign timestamp (datetime, optional): datetime in UTC (tzinfo=timezone.utc)

    Returns:

      int: containing the callsign’s CQ Zone

    Raises:

      KeyError: no CQ Zone found for callsign

  static get_homecall(callsign)

    Strips off country prefixes (HC2/DH1TW) and activity suffixes (DH1TW/P).

    Args:

      callsign (str): Amateur Radio callsign

    Returns:

      str: callsign without country/activity pre/suffixes

    Raises:

      ValueError: No callsign found in string

    Example:

      The following code retrieves the home call for “HC2/DH1TW/P”

          >>> from pyhamtools import LookupLib, Callinfo
          >>> my_lookuplib = LookupLib(lookuptype="countryfile")
          >>> cic = Callinfo(my_lookuplib)
          >>> cic.get_homecall("HC2/DH1TW/P")
          DH1TW

  get_ituz(callsign, timestamp=None)

    Returns ITU Zone of a callsign

    Args:

      callsign (str): Amateur Radio callsign timestamp (datetime, optional): datetime in UTC (tzinfo=timezone.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

  get_lat_long(callsign, timestamp=None)

    Returns Latitude and Longitude for a callsign

    Args:

      callsign (str): Amateur Radio callsign timestamp (datetime, optional): datetime in UTC (tzinfo=timezone.utc)

    Returns:

      dict: Containing Latitude and Longitude

    Raises:

      KeyError: No data found for callsign

    Example:

      The following code returns Latitude & Longitude for “DH1TW”

          >>> 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
          }

    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 dedicated entry in the database exists. Best results will be retrieved with QRZ.com Lookup.

  is_valid_callsign(callsign, timestamp=None)

    Checks if a callsign is valid

    Args:

      callsign (str): Amateur Radio callsign timestamp (datetime, optional): datetime in UTC (tzinfo=timezone.utc)

    Returns:

      bool: True / False

    Example:

      The following checks if “DH1TW” is a valid callsign

          >>> from pyhamtools import LookupLib, Callinfo
          >>> my_lookuplib = LookupLib(lookuptype="countryfile")
          >>> cic = Callinfo(my_lookuplib)
          >>> cic.is_valid_callsign("DH1TW")
          True

## pyhamtools.lookuplib

class pyhamtools.lookuplib.LookupLib(lookuptype='countryfile', apikey=None, apiv='1.3.3', filename=None, logger=None, username=None, pwd=None, redis_instance=None, redis_prefix=None)

  This class is a wrapper for the following three Amateur Radio databases:

  1. Clublog.org (daily updated XML File)

  2. Clublog.org (HTTPS lookup)

  3. Country-files.com (infrequently updated PLIST File)

  4. QRZ.com (HTTP / XML Lookup)

  It’s aim is to provide a homogeneous interface to different databases.

  Typically an instance of this class is injected as a dependency in the `Callinfo` class, but it can also be used directly.

  Even the interface is the same for all lookup sources, the returning data can be different. The documentation of the various methods provide more detail.

  By default, LookupLib requires an Internet connection to download the libraries or perform the lookup against the Clublog API or QRZ.com.

  The entire lookup data (where database files are downloaded) can also be copied into Redis, which an extremely fast in-memory Key/Value store. A LookupLib object can be instantiated to perform then all lookups in Redis, instead processing and loading the data from Internet / File. This saves some time and allows several instances of `LookupLib` to query the same data concurrently.

  Args:

    lookuptype (str) : “clublogxml” or “clublogapi” or “countryfile” or “redis” or “qrz” apikey (str): Clublog API Key username (str): QRZ.com username pwd (str): QRZ.com password apiv (str, optional): QRZ.com API Version filename (str, optional): Filename for Clublog XML or Country-files.com cty.plist file. When a local file is used, no Internet connection not API Key is necessary. logger (logging.getLogger(__name__), optional): Python logger redis_instance (redis.Redis(), optional): Instance of Redis redis_prefix (str, optional): Prefix to identify the lookup data set in Redis

  copy_data_in_redis(redis_prefix, redis_instance)

    Copy the complete lookup data into redis. Old data will be overwritten.

    Args:

      redis_prefix (str): Prefix to distinguish the data in redis for the different looktypes redis_instance (str): an Instance of Redis

    Returns:

      bool: returns True when the data has been copied successfully into Redis

    Example:

      Copy the entire lookup data from the Country-files.com PLIST File into Redis. This example requires a running instance of Redis, as well the python Redis connector (pip install redis-py).

          >>> from pyhamtools import LookupLib
          >>> import redis
          >>> r = redis.Redis()
          >>> my_lookuplib = LookupLib(lookuptype="countryfile")
          >>> print my_lookuplib.copy_data_in_redis(redis_prefix="CF", redis_instance=r)
          True

      Now let’s create an instance of LookupLib, using Redis to query the data

          >>> from pyhamtools import LookupLib
          >>> import redis
          >>> r = redis.Redis()
          >>> my_lookuplib = LookupLib(lookuptype="redis", redis_instance=r, redis_prefix="CF")
          >>> my_lookuplib.lookup_callsign("3D2RI")
          {
            u'adif': 460,
            u'continent': u'OC',
            u'country': u'Rotuma Island',
            u'cqz': 32,
            u'ituz': 56,
            u'latitude': -12.48,
            u'longitude': 177.08
          }

    Note:

      This method is available for the following lookup type

      - clublogxml

      - countryfile

  is_invalid_operation(callsign, timestamp=None)

    Returns True if an operations is known as invalid

    Args:

      callsign (string): Amateur Radio callsign timestamp (datetime, optional): datetime in UTC (tzinfo=timezone.utc)

    Returns:

      bool: True if a record exists for this callsign (at the given time)

    Raises:

      KeyError: No matching callsign found APIKeyMissingError: API Key for Clublog missing or incorrect

    Example:

      The following code checks the Clublog XML database if the operation is valid for two dates.

          >>> from pyhamtools import LookupLib
          >>> from datetime import datetime, timezone
          >>> my_lookuplib = LookupLib(lookuptype="clublogxml", apikey="myapikey")
          >>> print my_lookuplib.is_invalid_operation("5W1CFN")
          True
          >>> try:
          >>>   timestamp = datetime(year=2012, month=1, day=31, tzinfo=timezone.utc)
          >>>   my_lookuplib.is_invalid_operation("5W1CFN", timestamp)
          >>> except KeyError:
          >>>   print "Seems to be invalid operation before 31.1.2012"
          Seems to be an invalid operation before 31.1.2012

    Note:

      This method is available for

      - clublogxml

      - redis

  lookup_callsign(callsign=None, timestamp=None)

    Returns lookup data if an exception exists for a callsign

    Args:

      callsign (string): Amateur radio callsign timestamp (datetime, optional): datetime in UTC (tzinfo=timezone.utc)

    Returns:

      dict: Dictionary containing the country specific data of the callsign

    Raises:

      KeyError: No matching callsign found APIKeyMissingError: API Key for Clublog missing or incorrect

    Example:

      The following code queries the the online Clublog API for the callsign “VK9XO” on a specific date.

          >>> from pyhamtools import LookupLib
          >>> from datetime import datetime, timezone
          >>> my_lookuplib = LookupLib(lookuptype="clublogapi", apikey="myapikey")
          >>> timestamp = datetime(year=1962, month=7, day=7, tzinfo=timezone.utc)
          >>> print my_lookuplib.lookup_callsign("VK9XO", timestamp)
          {
           'country': u'CHRISTMAS ISLAND',
           'longitude': 105.7,
           'cqz': 29,
           'adif': 35,
           'latitude': -10.5,
           'continent': u'OC'
          }

    Note:

      This method is available for

      - clublogxml

      - clublogapi

      - countryfile

      - qrz.com

      - redis

  lookup_entity(entity=None)

    Returns lookup data of an ADIF Entity

    Args:

      entity (int): ADIF identifier of country

    Returns:

      dict: Dictionary containing the country specific data

    Raises:

      KeyError: No matching entity found

    Example:

      The following code queries the the Clublog XML database for the ADIF entity Turkmenistan, which has the id 273.

          >>> from pyhamtools import LookupLib
          >>> my_lookuplib = LookupLib(lookuptype="clublogapi", apikey="myapikey")
          >>> print my_lookuplib.lookup_entity(273)
          {
           'deleted': False,
           'country': u'TURKMENISTAN',
           'longitude': 58.4,
           'cqz': 17,
           'prefix': u'EZ',
           'latitude': 38.0,
           'continent': u'AS'
          }

    Note:

      This method is available for the following lookup type

      - clublogxml

      - redis

      - qrz.com

  lookup_prefix(prefix, timestamp=None)

    Returns lookup data of a Prefix

    Args:

      prefix (string): Prefix of a Amateur Radio callsign timestamp (datetime, optional): datetime in UTC (tzinfo=timezone.utc)

    Returns:

      dict: Dictionary containing the country specific data of the Prefix

    Raises:

      KeyError: No matching Prefix found APIKeyMissingError: API Key for Clublog missing or incorrect

    Example:

      The following code shows how to obtain the information for the prefix “DH” from the countryfile.com database (default database).

          >>> from pyhamtools import LookupLib
          >>> myLookupLib = LookupLib()
          >>> print myLookupLib.lookup_prefix("DH")
          {
           'adif': 230,
           'country': u'Fed. Rep. of Germany',
           'longitude': 10.0,
           'cqz': 14,
           'ituz': 28,
           'latitude': 51.0,
           'continent': u'EU'
          }

    Note:

      This method is available for

      - clublogxml

      - countryfile

      - redis

  lookup_zone_exception(callsign, timestamp=None)

    Returns a CQ Zone if an exception exists for the given callsign

    Args: callsign (string): Amateur radio callsign timestamp (datetime, optional): datetime in UTC (tzinfo=timezone.utc)

    Returns:

      int: Value of the the CQ Zone exception which exists for this callsign (at the given time)

    Raises:

      KeyError: No matching callsign found APIKeyMissingError: API Key for Clublog missing or incorrect

    Example:

      The following code checks the Clublog XML database if a CQ Zone exception exists for the callsign DP0GVN.

          >>> from pyhamtools import LookupLib
          >>> my_lookuplib = LookupLib(lookuptype="clublogxml", apikey="myapikey")
          >>> print my_lookuplib.lookup_zone_exception("DP0GVN")
          38

      The prefix “DP” It is assigned to Germany, but the station is located in Antarctica, and therefore in CQ Zone 38

    Note:

      This method is available for

      - clublogxml

      - redis

## pyhamtools.utils (deprecated)

pyhamtools.utils.freq_to_band(freq)

  converts a Frequency [kHz] into the band and mode according to the IARU bandplan

  Note:

    DEPRECATION NOTICE This function has been moved to pyhamtools.frequency with PyHamTools 0.4.1 Please don’t use this module/function anymore. It will be removed soon.