Added module for Locator based calculations (Distance, Heading, Locator -> Lat / Long....etc)

This commit is contained in:
dh1tw 2014-09-21 19:45:43 +02:00
parent 0fabb0c90b
commit eec8dc2008
7 changed files with 633 additions and 1 deletions

69
test/test_dxcluster.py Normal file
View file

@ -0,0 +1,69 @@
import pytest
from datetime import datetime
import pytz
from pyhamtools.consts import LookupConventions as const
from pyhamtools.dxcluster import decode_char_spot, decode_pc11_message, decode_pc61_message
UTC = pytz.UTC
fix_spot1 = "DX de CT3FW: 21004.8 HC2AO 599 TKS(CW)QSL READ,QRZ.COM 2132Z"
fix_spot1_broken_spotter_call = "DX de $QRM: 21004.8 HC2AO 599 TKS(CW)QSL READ,QRZ.COM 2132Z"
fix_spot_pc11 = "PC11^14010.0^R155AP^30-Apr-2014^2252Z^CQ CQ^R9CAC^RN6BN^H95^~"
fix_spot_pc61 = "PC61^14030.5^ZF2NUE^30-Apr-2014^2253Z^ ^ND4X^W4NJA^72.51.152.150^H96^~"
fix_spot2 = "DX de DL6NAA: 10368887.0 DL7VTX/B 55s in JO50VFjo62 never hrd B4 1505Z"
fix_spot3 = "DX de CT3FW: 21004.8 IDIOT 599 TKS(CW)QSL READ,QRZ.COM 2132Z"
fix_spot4 = "DX de OK1TEH: 144000.0 C0NTEST -> www.darkside.cz/qrv.php 1328Z JO70"
fix_spot5 = "DX de DK7UK: 50099.0 EA5/ON4CAU JN48QT<ES>IM98 QRP 5W LOOP ANT 1206Z"
fix_spot6 = "DX de UA3ZBK: 14170.0 UR8EW/QRP POWER 2-GU81+SPYDER 1211Z"
fix_spot7 = "DX de 9K2/K2SES 14205.0 DK0HY 0921Z" #missing semicolon
fix_spot8 = "DX de DK1CS:9330368887.0 DL7VTX/B 1505Z"
fix_spot9 = "DX de DH1TW: 23.0 DS1TW 1505Z"
fix_spot10 = "DX de DH1TW 234.0 DS1TW 1505Z"
fix_spot11 = "DX de DH1TW: 234.0 DS1TW 1505Z"
fix_spot12 = "DX de DH1TW: 50105.0 ZD6DYA 1505Z"
response_spot1 = {
const.SPOTTER: "CT3FW",
const.DX: "HC2AO",
const.BAND: 15,
const.MODE: "CW",
const.COMMENT: "599 TKS(CW)QSL READ,QRZ.COM",
const.TIME: datetime.utcnow().replace( hour=21, minute=32, second=0, microsecond = 0, tzinfo=UTC)
}
class TestDXClusterSpots:
def test_spots(self):
assert decode_char_spot(fix_spot1)[const.SPOTTER] == "CT3FW"
assert decode_char_spot(fix_spot1)[const.DX] == "HC2AO"
assert decode_char_spot(fix_spot1)[const.FREQUENCY] == 21004.8
assert decode_char_spot(fix_spot1)[const.COMMENT] == "599 TKS(CW)QSL READ,QRZ.COM"
assert isinstance(decode_char_spot(fix_spot1)[const.TIME], datetime)
with pytest.raises(ValueError):
decode_char_spot(fix_spot1_broken_spotter_call)
def test_spots_pc11(self):
assert decode_pc11_message(fix_spot_pc11)[const.SPOTTER] == "R9CAC"
assert decode_pc11_message(fix_spot_pc11)[const.DX] == "R155AP"
assert decode_pc11_message(fix_spot_pc11)[const.FREQUENCY] == 14010.0
assert decode_pc11_message(fix_spot_pc11)[const.COMMENT] == "CQ CQ"
assert decode_pc11_message(fix_spot_pc11)["node"] == "RN6BN"
assert isinstance(decode_pc11_message(fix_spot_pc11)[const.TIME], datetime)
def test_spots_pc61(self):
assert decode_pc61_message(fix_spot_pc61)[const.SPOTTER] == "ND4X"
assert decode_pc61_message(fix_spot_pc61)[const.DX] == "ZF2NUE"
assert decode_pc61_message(fix_spot_pc61)[const.FREQUENCY] == 14030.5
assert decode_pc61_message(fix_spot_pc61)[const.COMMENT] == " "
assert decode_pc61_message(fix_spot_pc61)["node"] == "W4NJA"
assert decode_pc61_message(fix_spot_pc61)["ip"] == "72.51.152.150"
assert isinstance(decode_pc61_message(fix_spot_pc61)[const.TIME], datetime)

View file

@ -0,0 +1,60 @@
import pytest
from pyhamtools.locator import calculate_distance, calculate_distance_longpath, calculate_heading, calculate_heading_longpath
from pyhamtools.consts import LookupConventions as const
class Test_calculate_distance():
def test_calculate_distance_edge_cases(self):
assert calculate_distance("JN48QM", "JN48QM") == 0
assert calculate_distance("JN48", "JN48") == 0
assert abs(calculate_distance("AA00AA", "rr00xx") - 19009) < 1
def test_calculate_distance_normal_case(self):
assert abs(calculate_distance("JN48QM", "FN44AB") - 5965) < 1
assert abs(calculate_distance("FN44AB", "JN48QM") - 5965) < 1
assert abs(calculate_distance("JN48QM", "QF67bf") - 16467) < 1
def test_calculate_distance_invalid_inputs(self):
with pytest.raises(AttributeError):
calculate_distance(5, 12)
with pytest.raises(ValueError):
calculate_distance("XX0XX", "ZZ0Z")
def test_calculate_distance_longpath_normal_case(self):
assert abs(calculate_distance_longpath("JN48QM", "FN44AB") - 34042) < 1
assert abs(calculate_distance_longpath("JN48QM", "QF67bf") - 23541) < 1
def test_calculate_distance_longpath_edge_cases(self):
assert abs(calculate_distance_longpath("JN48QM", "JN48QM") - 40008) < 1
assert abs(calculate_distance_longpath("JN48QM", "AE15UU") - 20645) < 1 #ZL7 Chatham - almost antipods
class Test_calculate_heading():
def test_calculate_heading_normal_cases(self):
assert abs(calculate_heading("JN48QM", "FN44AB") - 298) < 1
assert abs(calculate_heading("FN44AB", "JN48QM") - 54) < 1
assert abs(calculate_heading("JN48QM", "QF67bf") - 74) < 1
assert abs(calculate_heading("QF67BF", "JN48QM") - 310) < 1
def test_calculate_heading_edge_cases(self):
assert abs(calculate_heading("JN48QM", "JN48QM") - 0 ) < 1
def test_calculate_heading_longpath(self):
assert abs(calculate_heading_longpath("JN48QM", "FN44AB") - 118) < 1
assert abs(calculate_heading_longpath("FN44AB", "JN48QM") - 234) < 1
assert abs(calculate_heading_longpath("JN48QM", "QF67BF") - 254) < 1
assert abs(calculate_heading_longpath("QF67BF", "JN48QM") - 130) < 1
def test_calculate_heading_longpath_edge_cases(self):
assert abs(calculate_heading_longpath("JN48QM", "JN48QM") - 180 ) < 1

View file

@ -0,0 +1,33 @@
import pytest
from pyhamtools.locator import latlong_to_locator
from pyhamtools.consts import LookupConventions as const
class Test_latlong_to_locator():
def test_latlong_to_locator_edge_cases(self):
assert latlong_to_locator(-89.97916, -179.95833) == "AA00AA"
assert latlong_to_locator(89.97916, 179.9583) == "RR99XX"
def test_latlong_to_locator_normal_case(self):
assert latlong_to_locator(48.52083, 9.3750000) == "JN48QM"
assert latlong_to_locator(48.5, 9.0) == "JN48MM" #center of the square
def test_latlong_to_locator_invalid_characters(self):
with pytest.raises(ValueError):
latlong_to_locator("JN48QM", "test")
with pytest.raises(ValueError):
latlong_to_locator("", "")
def test_latlong_to_locator_out_of_boundry(self):
with pytest.raises(ValueError):
latlong_to_locator(-90, -180)
with pytest.raises(ValueError):
latlong_to_locator(90, 180)
with pytest.raises(ValueError):
latlong_to_locator(10000, 120000)

View file

@ -0,0 +1,58 @@
import pytest
from pyhamtools.locator import locator_to_latlong
from pyhamtools.consts import LookupConventions as const
class Test_locator_to_latlong():
def test_locator_to_latlong_edge_cases(self):
latitude, longitude = locator_to_latlong("AA00AA")
assert abs(latitude + 89.97916) < 0.00001
assert abs(longitude +179.95833) < 0.0001
latitude, longitude = locator_to_latlong("RR99XX")
assert abs(latitude - 89.97916) < 0.00001
assert abs(longitude - 179.9583) < 0.0001
def test_locator_to_latlong_normal_case(self):
latitude, longitude = locator_to_latlong("JN48QM")
assert abs(latitude - 48.52083) < 0.00001
assert abs(longitude - 9.3750000) < 0.0001
latitude, longitude = locator_to_latlong("JN48")
assert abs(latitude - 48.5) < 0.001
assert abs(longitude - 9.000) < 0.001
def test_locator_to_latlong_mixed_signs(self):
latitude, longitude = locator_to_latlong("Jn48qM")
assert abs(latitude - 48.52083) < 0.00001
assert abs(longitude - 9.3750000) < 0.0001
def test_locator_to_latlong_wrong_amount_of_characters(self):
with pytest.raises(ValueError):
latitude, longitude = locator_to_latlong("J")
with pytest.raises(ValueError):
latitude, longitude = locator_to_latlong("JN")
with pytest.raises(ValueError):
latitude, longitude = locator_to_latlong("JN4")
with pytest.raises(ValueError):
latitude, longitude = locator_to_latlong("JN8Q")
def test_locator_to_latlong_invalid_characters(self):
with pytest.raises(ValueError):
latitude, longitude = locator_to_latlong("21XM99")
with pytest.raises(ValueError):
latitude, longitude = locator_to_latlong("****")
def test_locator_to_latlong_out_of_boundry(self):
with pytest.raises(ValueError):
latitude, longitude = locator_to_latlong("RR99XY")

View file

@ -0,0 +1,57 @@
from datetime import datetime, timedelta
import pytest
import pytz
from pyhamtools.locator import calculate_sunrise_sunset
UTC = pytz.UTC
class Test_calculate_sunrise_sunset_normal_case():
def test_calculate_sunrise_sunset(self):
time_margin = timedelta(minutes=1)
locator = "JN48QM"
test_time = datetime(year=2014, month=1, day=1, tzinfo=UTC)
result_JN48QM_1_1_2014_evening_dawn = datetime(2014, 1, 1, 15, 38, tzinfo=UTC)
result_JN48QM_1_1_2014_morning_dawn = datetime(2014, 1, 1, 6, 36, tzinfo=UTC)
result_JN48QM_1_1_2014_sunrise = datetime(2014, 1, 1, 7, 14, tzinfo=UTC)
result_JN48QM_1_1_2014_sunset = datetime(2014, 1, 1, 16, 15, 23, 31016, tzinfo=UTC)
assert calculate_sunrise_sunset(locator, test_time)['morning_dawn'] - result_JN48QM_1_1_2014_morning_dawn < time_margin
assert calculate_sunrise_sunset(locator, test_time)['evening_dawn'] - result_JN48QM_1_1_2014_evening_dawn < time_margin
assert calculate_sunrise_sunset(locator, test_time)['sunset'] - result_JN48QM_1_1_2014_sunset < time_margin
assert calculate_sunrise_sunset(locator, test_time)['sunrise'] - result_JN48QM_1_1_2014_sunrise < time_margin
def test_calculate_distance_edge_case(self):
time_margin = timedelta(minutes=1)
locator = "AA00AA"
# no sunrise or sunset at southpol during arctic summer
test_time = datetime(year=2014, month=1, day=1, tzinfo=UTC)
result_AA00AA_1_1_2014_evening_dawn = datetime(2014, 1, 1, 15, 38, tzinfo=UTC)
result_AA00AA_1_1_2014_morning_dawn = datetime(2014, 1, 1, 6, 36, tzinfo=UTC)
result_AA00AA_1_1_2014_sunrise = datetime(2014, 1, 1, 7, 14, tzinfo=UTC)
result_AA00AA_1_1_2014_sunset = datetime(2014, 1, 1, 16, 15, 23, 31016, tzinfo=UTC)
assert calculate_sunrise_sunset(locator, test_time)['morning_dawn'] == None
assert calculate_sunrise_sunset(locator, test_time)['evening_dawn'] == None
assert calculate_sunrise_sunset(locator, test_time)['sunset'] == None
assert calculate_sunrise_sunset(locator, test_time)['sunrise'] == None
def test_calculate_distance_invalid_inputs(self):
with pytest.raises(ValueError):
calculate_sunrise_sunset("", "")
with pytest.raises(ValueError):
calculate_sunrise_sunset("JN48QM", "")
with pytest.raises(ValueError):
calculate_sunrise_sunset("JN48", 55)
with pytest.raises(AttributeError):
calculate_sunrise_sunset(33, datetime.now())