Returns a CQ Zone if an exception exists for the given callsign
Args:
callsign (string): Amateur radio callsign
diff --git a/docs/build/html/objects.inv b/docs/build/html/objects.inv
index 122f8d0..479e5aa 100644
--- a/docs/build/html/objects.inv
+++ b/docs/build/html/objects.inv
@@ -2,5 +2,7 @@
# Project: pyhamtools
# Version: 0.1
# The remainder of this file is compressed using zlib.
-xڭn0D|rU\PJoU^+6UCpBVpxZNפ
- 33^&ze5+ФDzPm٤ƨhxS5,\(Kb;emg;;,"l)9Z*"'fok"9;hU@`
k^Qˇ
$=El!r-T#kZt~#e
asg1
\ No newline at end of file
+xڭSAj0j\sBJZՒW4qjf43VZA(@l\ TЦ{ED YO i&*ظ&iki,bI?c Yv.Bk%M#8GNjRl{4ˢ&.9/QqrЈd'̊EVˢ%Wx
+K
+Kj
+ERocYIpJӮ%ysmKKBcV+<d
\ No newline at end of file
diff --git a/docs/build/html/searchindex.js b/docs/build/html/searchindex.js
index 53333c6..c8bb064 100644
--- a/docs/build/html/searchindex.js
+++ b/docs/build/html/searchindex.js
@@ -1 +1 @@
-Search.setIndex({envversion:42,terms:{oper:4,all:[4,3],code:[0,4],identifi:4,show:4,illustr:[],text:[],obtain:[4,3],myapikei:4,prefix:4,radio:[0,4],invalid:4,permiss:3,follow:[0,4,3],lookup:4,apikeymissingerror:4,entiti:4,xml:4,content:[],clublogxml:4,depend:4,copyright:3,also:4,"true":4,explain:[],tobia:3,except:4,should:[],tort:3,other:3,dict:4,ituz:4,queri:4,tzinfo:4,aris:3,logger:4,therefor:4,local:4,match:4,merchant:3,applic:0,lookup_ent:4,sourc:4,"return":[1,4],string:4,variou:4,countryfil:4,python:[0,4],timestamp:4,express:3,dai:4,"import":4,veri:[],liabl:3,month:4,associ:3,requir:4,warranti:3,like:0,specif:4,complic:[],"try":4,provid:[4,3],stuff:[],necessari:[0,4],contain:[0,4],found:4,readthedoc:0,page:0,impli:3,right:3,deal:3,example_gener:[],replac:4,some:0,see:0,callsign:[0,1,4],connect:[4,3],arg:4,download:4,germani:4,event:3,librari:[0,4],out:3,even:4,index:0,lookuptyp:4,turkmenistan:4,lookup_prefix:4,databas:4,rep:4,publish:3,lookup_zone_except:4,current:0,delet:4,written:[],version:0,inject:4,action:3,internet:4,print:4,condit:3,irc:0,bla:[],method:4,str:4,kei:4,longitud:4,given:4,free:3,dictionari:4,locat:4,come:0,lookup_callsign:4,valu:4,christma:4,search:0,fit:3,island:4,cty:4,against:4,datetim:4,instanc:4,doctest:[],permit:3,fals:4,countri:4,typic:4,dh1tw:[0,2,3],com:[0,4],assign:4,frequent:0,"default":4,vp5:1,softwar:3,adif:4,directli:4,modul:[],damag:3,three:4,qrp:1,filenam:4,api:4,strip:1,ea1:1,noresult:[],miss:4,merg:3,liabil:3,differ:4,from:[4,3],log:4,wai:[],cqz:4,modifi:3,etc:1,contact:[],two:4,wrapper:4,github:0,avail:4,whom:3,station:4,interfac:4,includ:3,paramet:4,call:1,daili:4,type:4,more:4,"function":[],amateur:[0,4],option:4,copi:3,keyerror:4,notic:3,is_invalid_oper:4,pytz:4,pars:0,callinfo:[],particular:3,known:4,herebi:3,holder:3,utc:4,kind:3,whether:3,access:[],structur:[],exampl:4,aim:4,record:4,contin:4,limit:3,can:4,abov:3,webirc:0,otherwis:3,hamtest:0,purpos:3,latitud:4,claim:3,file:[0,4,3],creat:0,"int":4,descript:[],year:4,my_lookuplib:4,ani:[1,3],dp0gvn:4,antarctica:4,inform:[1,4],exist:4,contract:3,substanti:3,onlin:4,seem:4,deltaxrai:0,plist:4,sell:3,vk9xo:4,mylookuplib:4,date:4,author:3,perform:4,apikei:4,when:4,detail:4,same:4,check:4,note:[],how:4,valid:4,bool:4,which:[0,4],none:4,verifi:1,you:0,subject:3,mit:[0,3],lookuplib:[],updat:4,incorrect:4,"5w1cfn":4,http:[0,4],clublog:4,shall:3,org:[0,4],fed:4,restrict:3,befor:4,zone:4,rais:4,distribut:3,sublicens:3,bsd:[],data:4,"class":[0,1,4],charg:3,homogen:4,noninfring:3,infrequ:4,off:1,faster:0,"__name__":4,grant:3,clublogapi:4,wellnitz:3,without:3,issu:0,person:3,portion:3,getter:[],gethomecal:1,getlogg:4,thi:[1,4,3],time:4,format:[],hello:[],furnish:3},objtypes:{"0":"py:module","1":"py:method","2":"py:class"},objnames:{"0":["py","module","Python module"],"1":["py","method","Python method"],"2":["py","class","Python class"]},filenames:["index","Callinfo","help","license","LookupLib"],titles:["PyHamTools","Callinfo","help","license","LookupLib"],objects:{pyhamtools:{callinfo:[1,0,0,"-"],lookuplib:[4,0,0,"-"]},"pyhamtools.callinfo.Callinfo":{getHomeCall:[1,1,1,""]},"pyhamtools.lookuplib":{LookupLib:[4,2,1,""]},"pyhamtools.lookuplib.LookupLib":{lookup_prefix:[4,1,1,""],lookup_entity:[4,1,1,""],lookup_zone_exception:[4,1,1,""],lookup_callsign:[4,1,1,""],is_invalid_operation:[4,1,1,""]},"pyhamtools.callinfo":{Callinfo:[1,2,1,""]}},titleterms:{help:2,anoth:[],licens:3,clase:0,simpl:[],indic:0,content:[],header:[],contact:3,tabl:0,pyhamtool:0,document:[],callinfo:1,modul:0,lookuplib:4,welcom:[]}})
\ No newline at end of file
+Search.setIndex({envversion:42,terms:{vp5:[],all:[1,4,3],code:[0,1,4],identifi:[1,4],show:4,illustr:[],text:[],obtain:[4,3],damag:3,myapikei:4,prefix:[1,4],get_contin:1,publish:3,permiss:3,follow:[0,1,4,3],itu:1,apikeymissingerror:4,entiti:4,xml:[1,4],content:[],onli:1,clublogxml:4,depend:[1,4],copyright:3,also:4,adif:[1,4],"true":[1,4],explain:[],tobia:3,except:4,should:[],tort:3,other:3,dict:[1,4],ituz:[1,4],queri:4,tzinfo:[1,4],aris:3,logger:[1,4],therefor:4,local:4,match:4,merchant:3,applic:0,lookup_ent:4,sourc:4,"return":[1,4],string:[1,4],variou:4,format:[],python:[0,1,4],timestamp:[1,4],express:3,dai:4,feder:1,"import":[1,4],veri:1,liabl:3,month:4,geo:[],requir:4,name:1,warranti:3,like:0,specif:[1,4],descript:[],complic:[],"try":4,whether:3,republ:1,stuff:[],necessari:[0,4],contain:[0,1,4],found:[1,4],readthedoc:0,where:1,page:0,get_cqz:1,impli:3,right:3,deal:3,replac:4,some:0,consequ:1,see:0,callsign:[0,1,4],connect:[4,3],arg:4,download:4,home:1,event:3,librari:[0,4],out:3,even:4,index:0,oceania:1,lookuptyp:[1,4],turkmenistan:4,item:1,lookup_prefix:4,databas:[1,4],rep:[1,4],asia:1,lookup_zone_except:4,current:[0,1],delet:4,written:[],version:0,inject:[1,4],internet:4,print:4,condit:3,irc:0,bla:[],method:4,provid:[1,4,3],str:[1,4],kei:4,longitud:[1,4],differ:[1,4],free:3,entir:1,dictionari:[1,4],europ:1,come:0,lookup_callsign:4,valu:4,christma:4,hc2:1,sell:3,search:0,perform:4,fit:3,most:1,germani:[1,4],island:4,could:1,slightli:1,cty:4,against:4,unfortun:1,precis:1,datetim:[1,4],instanc:[1,4],doctest:[],permit:3,fals:[1,4],countri:[1,4],typic:4,dh1tw:[0,1,2,3],com:[0,1,4],assign:4,frequent:0,oper:4,softwar:3,get_ituz:1,suffix:1,directli:4,activ:1,modul:[],africa:1,three:4,qrp:[],filenam:4,api:[1,4],wrapper:4,strip:1,ea1:[],noresult:[],miss:[1,4],fed:[1,4],merg:3,liabil:3,given:4,latitud:[1,4],from:[1,4,3],log:[1,4],wai:[],cqz:[1,4],modifi:3,daili:4,etc:1,contact:[],two:4,construct:1,github:0,avail:[1,4],whom:3,reli:1,station:4,interfac:4,includ:3,paramet:[1,4],call:1,underli:1,locat:[1,4],type:[1,4],valueerror:1,more:4,"function":[],plist:4,capit:1,through:1,option:[1,4],copi:3,keyerror:[1,4],notic:3,lookup:[1,4],is_invalid_oper:4,pytz:[1,4],pars:0,callinfo:[],particular:3,known:4,off:1,herebi:3,holder:3,cic:1,utc:[1,4],kind:3,dedic:1,retriev:1,"default":4,access:[],none:[1,4],structur:[],exampl:[1,4],aim:4,record:4,"while":1,contin:[1,4],limit:3,can:[1,4],abov:3,webirc:0,otherwis:3,hamtest:0,purpos:[1,3],radio:[0,1,4],associ:3,pre:1,claim:3,would:1,file:[0,1,4,3],creat:0,"int":[1,4],get_homecal:1,year:4,my_lookuplib:[1,4],ani:[1,3],doesn:1,dp0gvn:4,"case":1,antarctica:[1,4],inform:[1,4],exist:[1,4],contract:3,substanti:3,onlin:4,seem:4,deltaxrai:0,action:3,sever:1,incorrect:4,vk9xo:4,mylookuplib:4,date:4,author:3,check:[1,4],get_country_nam:1,apikei:4,when:[1,4],detail:4,same:4,get_lat_long:1,note:[],how:4,amateur:[0,1,4],valid:[1,4],bool:[1,4],which:[0,4],get_adif_id:1,verifi:[],you:0,subject:3,mit:[0,3],lookuplib:[],updat:4,furnish:3,example_gener:[],"5w1cfn":4,http:[0,4],clublog:[1,4],shall:3,org:[0,1,4],object:1,restrict:3,invalid:4,befor:4,zone:[1,4],rais:[1,4],countryfil:[1,4],work:1,distribut:3,sublicens:3,bsd:[],america:1,data:[1,4],"class":[0,1,4],charg:3,is_valid_callsign:1,homogen:4,noninfring:3,infrequ:4,don:1,faster:0,coordin:1,entri:1,"__name__":[1,4],grant:3,clublogapi:4,wellnitz:3,without:[1,3],issu:0,person:3,portion:3,getter:[],gethomecal:[],south:1,thi:[1,4,3],time:4,north:1,get_al:1,hello:[],getlogg:[1,4]},objtypes:{"0":"py:module","1":"py:method","2":"py:class"},objnames:{"0":["py","module","Python module"],"1":["py","method","Python method"],"2":["py","class","Python class"]},filenames:["index","Callinfo","help","license","LookupLib"],titles:["PyHamTools","Callinfo","help","license","LookupLib"],objects:{pyhamtools:{callinfo:[1,0,0,"-"],lookuplib:[4,0,0,"-"]},"pyhamtools.callinfo.Callinfo":{get_continent:[1,1,1,""],get_cqz:[1,1,1,""],get_homecall:[1,1,1,""],get_country_name:[1,1,1,""],get_all:[1,1,1,""],get_lat_long:[1,1,1,""],is_valid_callsign:[1,1,1,""],get_adif_id:[1,1,1,""],get_ituz:[1,1,1,""]},"pyhamtools.lookuplib":{LookupLib:[4,2,1,""]},"pyhamtools.lookuplib.LookupLib":{lookup_prefix:[4,1,1,""],lookup_entity:[4,1,1,""],lookup_zone_exception:[4,1,1,""],lookup_callsign:[4,1,1,""],is_invalid_operation:[4,1,1,""]},"pyhamtools.callinfo":{Callinfo:[1,2,1,""]}},titleterms:{help:2,anoth:[],licens:3,clase:0,simpl:[],indic:0,content:[],header:[],contact:3,tabl:0,pyhamtool:0,document:[],callinfo:1,modul:0,lookuplib:4,welcom:[]}})
\ No newline at end of file
diff --git a/pyhamtools/__init__.py b/pyhamtools/__init__.py
index ce01da8..10baab5 100644
--- a/pyhamtools/__init__.py
+++ b/pyhamtools/__init__.py
@@ -1,4 +1,3 @@
from pyhamtools.lookuplib import LookupLib
from pyhamtools.callinfo import Callinfo
-
diff --git a/pyhamtools/callinfo.py b/pyhamtools/callinfo.py
index 90542ea..02abd69 100644
--- a/pyhamtools/callinfo.py
+++ b/pyhamtools/callinfo.py
@@ -1,6 +1,7 @@
import re
import logging
from datetime import datetime
+import sys
import pytz
@@ -13,26 +14,61 @@ from pyhamtools.consts import LookupConventions as const
UTC = pytz.UTC
timestamp_now = datetime.utcnow().replace(tzinfo=UTC)
+if sys.version_info < (2, 7, ):
+ class NullHandler(logging.Handler):
+ def emit(self, record):
+ pass
class Callinfo(object):
"""
- This is going to going to return information for a callsign
+ 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
+
"""
- def __init__(self, lookuplib=LookupLib(), logger=None):
+ def __init__(self, lookuplib, logger=None):
self._logger = None
if logger:
self._logger = logger
else:
self._logger = logging.getLogger(__name__)
- self._logger.addHandler(logging.NullHandler())
+ if sys.version_info[:2] == (2, 6):
+ self._logger.addHandler(NullHandler())
+ else:
+ self._logger.addHandler(logging.NullHandler())
self._lookuplib = lookuplib
self._callsign_info = None
def get_homecall(self, callsign):
- """verify call and strip off any /ea1 vp5/ /qrp etc"""
+ """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
+
+ """
callsign = callsign.upper()
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)
@@ -40,7 +76,8 @@ class Callinfo(object):
homecall = homecall.group(0)
return homecall
else:
- return
+ raise ValueError
+
def _iterate_prefix(self, callsign, timestamp=timestamp_now):
"""truncate call until it corresponds to a Prefix in the database"""
@@ -55,7 +92,17 @@ class Callinfo(object):
raise KeyError
def _dismantle_callsign(self, callsign, timestamp=timestamp_now):
+ """ 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
+
+
+ """
entire_callsign = callsign.upper()
if re.search('[/A-Z0-9\-]{3,15}', entire_callsign): # make sure the call has at least 3 characters
@@ -150,7 +197,43 @@ class Callinfo(object):
def get_all(self, callsign, timestamp=timestamp_now):
+ """ 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:
+ 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
+ :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`.
+
+ """
callsign_data = self._lookup_callsign(callsign, timestamp_now)
try:
@@ -159,18 +242,65 @@ class Callinfo(object):
except KeyError:
pass
- print callsign_data
-
return callsign_data
def is_valid_callsign(self, callsign, timestamp=timestamp_now):
+ """ 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:
+ 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
+
+ """
try:
if self.get_all(callsign, timestamp):
return True
- except:
+ except KeyError:
return False
def get_lat_long(self, callsign):
+ """ 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:
+ 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.
+
+ """
callsign_data = self.get_all(callsign, timestamp=timestamp_now)
return {
const.LATITUDE : callsign_data[const.LATITUDE],
@@ -178,30 +308,101 @@ class Callinfo(object):
}
def get_cqz(self, callsign, timestamp=timestamp_now):
+ """ 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
+
+ """
return self.get_all(callsign, timestamp)[const.CQZ]
def get_ituz(self, callsign, timestamp=timestamp_now):
+ """ 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
+
+ """
return self.get_all(callsign, timestamp)[const.ITUZ]
def get_country_name(self, callsign, timestamp=timestamp_now):
+ """ 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
+ :Callinfo. Clublog and Country-files.org use slightly different names
+ for countrys. Example:
+ Country-files.com: "Fed. Rep. of Germany"
+ Clublog: "FEDERAL REPUBLIC OF GERMANY"
+
+ """
return self.get_all(callsign, timestamp)[const.COUNTRY]
def get_adif_id(self, callsign, timestamp=timestamp_now):
+ """ Returns ADIF id of a callsign's country
+
+ Args:
+ callsign (str): Amateur Radio callsign
+ timestamp (datetime, optional): datetime in UTC (tzinfo=pytz.UTC)
+
+ Returns:
+ int: containing the country ADIF id
+
+ Raises:
+ KeyError: No Country found for callsign
+
+ """
return self.get_all(callsign, timestamp)[const.ADIF]
def get_continent(self, callsign, timestamp=timestamp_now):
- return self.get_all(callsign, timestamp)[const.CONTINENT]
+ """ Returns the continent Identifier of a callsign
-if __name__ == "__main__":
- import logging.config
- logging.config.fileConfig("logging.ini")
- logger = logging.getLogger(__name__)
+ Args:
+ callsign (str): Amateur Radio callsign
+ timestamp (datetime, optional): datetime in UTC (tzinfo=pytz.UTC)
+ Returns:
+ str: continent identified
- from pyhamtools import LookupLib
- apikey = "67547d6ce7a37276373b0568e3e52c1d3e2cb0e5"
- l = LookupLib("clublogxml", apikey=apikey)
- c = Callinfo(l)
- print c._iterate_prefix("DH1TW")
- print c._iterate_prefix("QRM")
+ 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
+ """
+ return self.get_all(callsign, timestamp)[const.CONTINENT]
\ No newline at end of file
diff --git a/pyhamtools/consts.py b/pyhamtools/consts.py
index 9210ada..965497d 100644
--- a/pyhamtools/consts.py
+++ b/pyhamtools/consts.py
@@ -17,4 +17,24 @@ class LookupConventions:
WHITELIST = "whitelist"
WHITELIST_START = "whitelist_start"
WHITELIST_END = "whitelist_end"
- DELETED = "deleted"
\ No newline at end of file
+ DELETED = "deleted"
+
+class Modes:
+ """ Constants for Operating modes """
+
+ CW = "CW"
+ USB = "USB"
+ LSB = "LSB"
+ DIGITAL = "DIGITAL"
+ FM = "FM"
+
+class DXSpot:
+ """ Constants used for DX Spots """
+
+ SPOTTER = "spotter"
+ DX = "dx"
+ FREQUENCY = "frequency"
+ COMMENT = "comment"
+ TIME = "time"
+ BAND = "band"
+ MODE = "mode"
\ No newline at end of file
diff --git a/pyhamtools/dxcluster.py b/pyhamtools/dxcluster.py
new file mode 100644
index 0000000..9e35737
--- /dev/null
+++ b/pyhamtools/dxcluster.py
@@ -0,0 +1,60 @@
+__author__ = 'dh1tw'
+
+from datetime import datetime
+import re
+
+
+import pytz
+
+UTC = pytz.UTC
+
+from pyhamtools.utils import freq_to_band
+from pyhamtools.consts import Modes as mode
+from pyhamtools.consts import DXSpot as dxspot
+
+
+def decode_spot(raw_string):
+ """Chop Line from DX-Cluster into pieces and return a dict with the spot data"""
+
+ spotter_call = None
+ dx_call = None
+ frequency = None
+ comment = None
+ spot_time = None
+ band = None
+ mode = None
+ bandmode = None
+
+ # Spotter callsign
+ if re.match('[A-Za-z0-9\/]+[:$]', raw_string[6:15]):
+ spotter_call = re.sub(':', '', re.match('[A-Za-z0-9\/]+[:$]', raw_string[6:15]).group(0))
+ else:
+ raise ValueError
+
+ if re.search('[0-9\.]{5,12}', raw_string[10:25]):
+ frequency = float(re.search('[0-9\.]{5,12}', raw_string[10:25]).group(0))
+ else:
+ raise ValueError
+
+ dx_call = re.sub('[^A-Za-z0-9\/]+', '', raw_string[26:38])
+ comment = re.sub('[^\sA-Za-z0-9\.,;\#\+\-!\?\$\(\)@\/]+', ' ', raw_string[39:69])
+ spot_time_ = re.sub('[^0-9]+', '', raw_string[70:74])
+ spot_time = datetime(hour=int(spot_time_[0:2]), minute=int(spot_time_[2:4]), second=0, microsecond = 0, tzinfo=UTC)
+
+ try:
+ bandmode = freq_to_band(frequency)
+ band = bandmode["band"]
+ mode = bandmode["mode"]
+ except KeyError:
+ raise ValueError
+
+ data = {
+ dxspot.SPOTTER: spotter_call,
+ dxspot.DX: dx_call,
+ dxspot.BAND: band,
+ dxspot.MODE: mode,
+ dxspot.COMMENT: comment,
+ dxspot.TIME: spot_time
+ }
+
+ return data
diff --git a/pyhamtools/logging.ini b/pyhamtools/logging.ini
new file mode 100644
index 0000000..4c8fca4
--- /dev/null
+++ b/pyhamtools/logging.ini
@@ -0,0 +1,22 @@
+[loggers]
+keys=root
+
+[handlers]
+keys=consoleHandler
+
+[formatters]
+keys=simpleFormatter
+
+[logger_root]
+level=DEBUG
+handlers=consoleHandler
+
+[handler_consoleHandler]
+class=StreamHandler
+level=DEBUG
+formatter=simpleFormatter
+args=(sys.stdout,)
+
+[formatter_simpleFormatter]
+format=%(asctime)s - %(name)s - %(levelname)s - %(message)s
+datefmt=
\ No newline at end of file
diff --git a/pyhamtools/lookuplib.py b/pyhamtools/lookuplib.py
index dc514e0..408064f 100644
--- a/pyhamtools/lookuplib.py
+++ b/pyhamtools/lookuplib.py
@@ -8,7 +8,7 @@ import xml.etree.ElementTree as ET
import urllib
import json
import copy
-
+import sys
import requests
from requests.exceptions import ConnectionError, HTTPError, Timeout
@@ -21,6 +21,10 @@ from exceptions import APIKeyMissingError
UTC = pytz.UTC
timestamp_now = datetime.utcnow().replace(tzinfo=UTC)
+if sys.version_info < (2, 7,):
+ class NullHandler(logging.Handler):
+ def emit(self, record):
+ pass
class LookupLib(object):
"""
@@ -55,7 +59,10 @@ class LookupLib(object):
self._logger = logger
else:
self._logger = logging.getLogger(__name__)
- self._logger.addHandler(logging.NullHandler())
+ if sys.version_info[:2] == (2, 6):
+ self._logger.addHandler(NullHandler())
+ else:
+ self._logger.addHandler(logging.NullHandler())
self._apikey = apikey
self._download = True
@@ -201,7 +208,6 @@ class LookupLib(object):
if self._callsign_exceptions[item][const.START] < timestamp:
callsign_data = copy.deepcopy(self._callsign_exceptions[item])
del callsign_data[const.START]
- print callsign + ": " + "here1"
return callsign_data
# enddate > timestamp
@@ -209,7 +215,6 @@ class LookupLib(object):
if self._callsign_exceptions[item][const.END] > timestamp:
callsign_data = copy.deepcopy(self._callsign_exceptions[item])
del callsign_data[const.END]
- print callsign + ": " + "here2"
return callsign_data
# startdate > timestamp > enddate
@@ -219,15 +224,12 @@ class LookupLib(object):
callsign_data = copy.deepcopy(self._callsign_exceptions[item])
del callsign_data[const.START]
del callsign_data[const.END]
- print callsign + ": " + "here3"
return callsign_data
# no startdate or enddate available
elif not const.START in self._callsign_exceptions[item] and not const.END in self._callsign_exceptions[item]:
- print callsign + ": " + "here4"
return self._callsign_exceptions[item]
- print callsign + ": " + "here5"
# no matching case
raise KeyError
@@ -588,11 +590,15 @@ class LookupLib(object):
# unzip file, if gz
if os.path.splitext(download_file_path)[1][1:] == "gz":
- with gzip.open(download_file_path, "r") as download_file:
+
+ download_file = gzip.open(download_file_path, "r")
+ try:
cty_file_path = os.path.join(os.path.splitext(download_file_path)[0])
with open(cty_file_path, "w") as cty_file:
cty_file.write(download_file.read())
- self._logger.debug(str(cty_file_path) + " successfully extracted")
+ self._logger.debug(str(cty_file_path) + " successfully extracted")
+ finally:
+ download_file.close()
else:
cty_file_path = download_file_path
diff --git a/pyhamtools/utils.py b/pyhamtools/utils.py
new file mode 100644
index 0000000..b2e8f14
--- /dev/null
+++ b/pyhamtools/utils.py
@@ -0,0 +1,135 @@
+from pyhamtools.consts import Modes as const
+
+
+def freq_to_band(freq):
+ """converts a frequency [kHz] into the band and looks up the mode"""
+ band = None
+ mode = None
+ if ((freq >= 135) and (freq <= 138)):
+ band = 2190
+ mode = const.CW
+ elif ((freq >= 1800) and (freq <= 2000)):
+ band = 160
+ if ((freq >= 1800) and (freq < 1838)):
+ mode = const.CW
+ elif ((freq >= 1838) and (freq < 1840)):
+ mode = const.DIGITAL
+ elif ((freq >= 1840) and (freq < 2000)):
+ mode = const.LSB
+ elif ((freq >= 3500) and (freq <= 4000)):
+ band = 80
+ if ((freq >= 3500) and (freq < 3580)):
+ mode = const.CW
+ elif ((freq >= 3580) and (freq < 3600)):
+ mode = const.DIGITAL
+ elif ((freq >= 3600) and (freq < 4000)):
+ mode = const.LSB
+ elif ((freq >= 5000) and (freq <= 5500)):
+ band = 60
+ elif ((freq >= 7000) and (freq <= 7300)):
+ band = 40
+ if ((freq >= 7000) and (freq < 7040)):
+ mode = const.CW
+ elif ((freq >= 7040) and (freq < 7050)):
+ mode = const.DIGITAL
+ elif ((freq >= 7050) and (freq < 7300)):
+ mode = const.LSB
+ elif ((freq >= 10100) and (freq <= 10150)):
+ band = 30
+ if ((freq >= 10100) and (freq < 10140)):
+ mode = const.CW
+ elif ((freq >= 10140) and (freq < 10150)):
+ mode = const.DIGITAL
+ elif ((freq >= 14000) and (freq <= 14350)):
+ band = 20
+ if ((freq >= 14000) and (freq < 14070)):
+ mode = const.CW
+ elif ((freq >= 14070) and (freq < 14099)):
+ mode = const.DIGITAL
+ elif ((freq >= 14100) and (freq < 14350)):
+ mode = const.USB
+ elif ((freq >= 18068) and (freq <= 18268)):
+ band = 17
+ if ((freq >= 18068) and (freq < 18095)):
+ mode = const.CW
+ elif ((freq >= 18095) and (freq < 18110)):
+ mode = const.DIGITAL
+ elif ((freq >= 18110) and (freq < 18268)):
+ mode = const.USB
+ elif ((freq >= 21000) and (freq <= 21450)):
+ band = 15
+ if ((freq >= 21000) and (freq < 21070)):
+ mode = const.CW
+ elif ((freq >= 21070) and (freq < 21150)):
+ mode = const.DIGITAL
+ elif ((freq >= 21150) and (freq < 21450)):
+ mode = const.USB
+ elif ((freq >= 24890) and (freq <= 24990)):
+ band = 12
+ if ((freq >= 24890) and (freq < 24915)):
+ mode = const.CW
+ elif ((freq >= 24915) and (freq < 24930)):
+ mode = const.DIGITAL
+ elif ((freq >= 24930) and (freq < 24990)):
+ mode = const.USB
+ elif ((freq >= 28000) and (freq <= 29700)):
+ band = 10
+ if ((freq >= 28000) and (freq < 28070)):
+ mode = const.CW
+ elif ((freq >= 28070) and (freq < 28190)):
+ mode = const.DIGITAL
+ elif ((freq >= 28300) and (freq < 29700)):
+ mode = const.USB
+ elif ((freq >= 50000) and (freq <= 54000)):
+ band = 6
+ if ((freq >= 50000) and (freq < 50100)):
+ mode = const.CW
+ elif ((freq >= 50100) and (freq < 50500)):
+ mode = const.USB
+ elif ((freq >= 50500) and (freq < 51000)):
+ mode = const.DIGITAL
+ elif ((freq >= 70000) and (freq <= 71000)):
+ band = 4
+ mode = None
+ elif ((freq >= 144000) and (freq <= 148000)):
+ band = 2
+ if ((freq >= 144000) and (freq < 144150)):
+ mode = const.CW
+ elif ((freq >= 144150) and (freq < 144400)):
+ mode = const.USB
+ elif ((freq >= 144400) and (freq < 148000)):
+ mode = None
+ elif ((freq >= 220000) and (freq <= 226000)):
+ band = 1.25 #1.25m
+ mode = None
+ elif ((freq >= 420000) and (freq <= 470000)):
+ band = 0.7 #70cm
+ mode = None
+ elif ((freq >= 902000) and (freq <= 928000)):
+ band = 0.33 #33cm US
+ mode = None
+ elif ((freq >= 1200000) and (freq <= 1300000)):
+ band = 0.23 #23cm
+ mode = None
+ elif ((freq >= 2390000) and (freq <= 2450000)):
+ band = 0.13 #13cm
+ mode = None
+ elif ((freq >= 3300000) and (freq <= 3500000)):
+ band = 0.09 #9cm
+ mode = None
+ elif ((freq >= 5650000) and (freq <= 5850000)):
+ band = 0.053 #5.3cm
+ mode = None
+ elif ((freq >= 10000000) and (freq <= 10500000)):
+ band = 0.03 #3cm
+ mode = None
+ elif ((freq >= 24000000) and (freq <= 24050000)):
+ band = 0.0125 #1,25cm
+ mode = None
+ elif ((freq >= 47000000) and (freq <= 47200000)):
+ band = 0.0063 #6,3mm
+ mode = None
+ else:
+ raise KeyError
+
+ return {"band": band, "mode": mode}
\ No newline at end of file
diff --git a/setup.py b/setup.py
index a49a534..e012857 100755
--- a/setup.py
+++ b/setup.py
@@ -1,16 +1,22 @@
#!/usr/bin/env python
-
+import sys
from distutils.core import setup
+kw = {}
+
+if sys.version_info >= (3,):
+ kw['use_2to3'] = True
+
setup(name='pyhamtools',
- version='0.1',
- description='Amateur Radio Callsign Lookup',
+ version='0.1.1',
+ description='Collection of Tools for Amateur Radio developers',
author='Tobias Wellnitz, DH1TW',
author_email='Tobias@dh1tw.de',
- url='http://github.com/dh1tw',
+ url='http://github.com/dh1tw/pyhamtools',
packages=['pyhamtools'],
install_requires=[
"pytz",
"requests",
- ]
+ ],
+ **kw
)
diff --git a/test/conftest.py b/test/conftest.py
index bfaecf2..0452e52 100644
--- a/test/conftest.py
+++ b/test/conftest.py
@@ -4,13 +4,6 @@ import os
from apikey import APIKEY
-#
-# @pytest.fixture()
-# def cleandir():
-# newpath = tempfile.mkdtemp()
-# os.chdir(newpath)
-#
-
from pyhamtools import LookupLib
from pyhamtools import Callinfo
diff --git a/test/test_utils_freq_to_band.py b/test/test_utils_freq_to_band.py
new file mode 100644
index 0000000..74885a4
--- /dev/null
+++ b/test/test_utils_freq_to_band.py
@@ -0,0 +1,81 @@
+import pytest
+from pyhamtools.utils import freq_to_band
+from pyhamtools.consts import Modes as mode
+
+class Test_utils_freq_to_band():
+
+ def test_hf_frequencies(self):
+ assert freq_to_band(137) == {"band" : 2190, "mode":mode.CW}
+
+ assert freq_to_band(1805) == {"band" : 160, "mode":mode.CW}
+ assert freq_to_band(1838) == {"band" : 160, "mode":mode.DIGITAL}
+ assert freq_to_band(1870) == {"band" : 160, "mode":mode.LSB}
+
+ assert freq_to_band(3500) == {"band" : 80, "mode":mode.CW}
+ assert freq_to_band(3580) == {"band" : 80, "mode":mode.DIGITAL}
+ assert freq_to_band(3799) == {"band" : 80, "mode":mode.LSB}
+
+ assert freq_to_band(5200) == {"band" : 60, "mode":None}
+
+ assert freq_to_band(7000) == {"band" : 40, "mode":mode.CW}
+ assert freq_to_band(7044) == {"band" : 40, "mode":mode.DIGITAL}
+ assert freq_to_band(7139) == {"band" : 40, "mode":mode.LSB}
+
+ assert freq_to_band(10100) == {"band" : 30, "mode":mode.CW}
+ assert freq_to_band(10141) == {"band" : 30, "mode":mode.DIGITAL}
+
+ assert freq_to_band(14000) == {"band" : 20, "mode":mode.CW}
+ assert freq_to_band(14070) == {"band" : 20, "mode":mode.DIGITAL}
+ assert freq_to_band(14349) == {"band" : 20, "mode":mode.USB}
+
+ assert freq_to_band(18068) == {"band" : 17, "mode":mode.CW}
+ assert freq_to_band(18096) == {"band" : 17, "mode":mode.DIGITAL}
+ assert freq_to_band(18250) == {"band" : 17, "mode":mode.USB}
+
+ assert freq_to_band(21000) == {"band" : 15, "mode":mode.CW}
+ assert freq_to_band(21070) == {"band" : 15, "mode":mode.DIGITAL}
+ assert freq_to_band(21449) == {"band" : 15, "mode":mode.USB}
+
+ assert freq_to_band(24890) == {"band" : 12, "mode":mode.CW}
+ assert freq_to_band(24916) == {"band" : 12, "mode":mode.DIGITAL}
+ assert freq_to_band(24965) == {"band" : 12, "mode":mode.USB}
+
+ assert freq_to_band(28000) == {"band" : 10, "mode":mode.CW}
+ assert freq_to_band(28070) == {"band" : 10, "mode":mode.DIGITAL}
+ assert freq_to_band(28500) == {"band" : 10, "mode":mode.USB}
+
+ assert freq_to_band(50000) == {"band" : 6, "mode":mode.CW}
+ assert freq_to_band(50100) == {"band" : 6, "mode":mode.USB}
+ assert freq_to_band(50500) == {"band" : 6, "mode":mode.DIGITAL}
+
+ def test_vhf_frequencies(self):
+ assert freq_to_band(70001) == {"band" : 4, "mode":None}
+
+ assert freq_to_band(144000) == {"band" : 2, "mode":mode.CW}
+ assert freq_to_band(144150) == {"band" : 2, "mode":mode.USB}
+ assert freq_to_band(144400) == {"band" : 2, "mode":None}
+
+ assert freq_to_band(220000) == {"band" : 1.25, "mode":None}
+
+ def test_uhf_frequencies(self):
+ assert freq_to_band(420000) == {"band" : 0.7, "mode":None}
+
+ assert freq_to_band(902000) == {"band" : 0.33, "mode":None}
+
+ assert freq_to_band(1200000) == {"band" : 0.23, "mode":None}
+
+ def test_shf_frequencies(self):
+ assert freq_to_band(2390000) == {"band" : 0.13, "mode":None}
+
+ assert freq_to_band(3300000) == {"band" : 0.09, "mode":None}
+
+ assert freq_to_band(5650000) == {"band" : 0.053, "mode":None}
+
+ assert freq_to_band(10000000) == {"band" : 0.03, "mode":None}
+
+ assert freq_to_band(24000000) == {"band" : 0.0125, "mode":None}
+
+ assert freq_to_band(47000000) == {"band" : 0.0063, "mode":None}
+
+ with pytest.raises(KeyError):
+ freq_to_band(16304)
\ No newline at end of file
diff --git a/tox.ini b/tox.ini
new file mode 100644
index 0000000..8f93aa7
--- /dev/null
+++ b/tox.ini
@@ -0,0 +1,13 @@
+# Tox (http://tox.testrun.org/) is a tool for running tests
+# in multiple virtualenvs. This configuration file will run the
+# test suite on all supported python versions. To use it, "pip install tox"
+# and then run "tox" from this directory.
+
+[tox]
+envlist = py26,py27,pypy
+
+[testenv]
+deps=pytest
+commands =
+ py.test \
+ {posargs}