diff --git a/CHANGELOG.md b/CHANGELOG.md index 974ff97..9bd8757 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,7 +1,7 @@ # CHANGELOG ## not released - client: changed socket mode from blocking to timeout (fixes #89) -- parser: Added optionally distance calculation (fixes #86) +- parser: Added optional distance/bearing calculation (fixes #86) - parser: Added support for weather data from FANET ground stations - parser: Added support for latency in receiver messages (OGNSDR) (fixes #87) - parser: Added support for reference_timestamp with tzinfo (fixes #84) diff --git a/ogn/parser/parse.py b/ogn/parser/parse.py index c420810..4c079a2 100644 --- a/ogn/parser/parse.py +++ b/ogn/parser/parse.py @@ -21,7 +21,7 @@ from ogn.parser.aprs_comment.generic_parser import GenericParser positions = {} -def parse(aprs_message, reference_timestamp=None, calculate_distances=False): +def parse(aprs_message, reference_timestamp=None, calculate_relations=False): global positions if reference_timestamp is None: @@ -33,11 +33,12 @@ def parse(aprs_message, reference_timestamp=None, calculate_distances=False): dstcall=message['dstcall'], aprs_type=message['aprs_type'])) - if message['aprs_type'].startswith('position') and calculate_distances is True: + if message['aprs_type'].startswith('position') and calculate_relations is True: positions[message['name']] = (message['longitude'], message['latitude']) if message['receiver_name'] in positions: cheap_ruler = CheapRuler((message['latitude'] + positions[message['receiver_name']][1]) / 2.0) message['distance'] = cheap_ruler.distance((message['longitude'], message['latitude']), positions[message['receiver_name']]) + message['bearing'] = cheap_ruler.bearing((message['longitude'], message['latitude']), positions[message['receiver_name']]) return message diff --git a/ogn/parser/utils.py b/ogn/parser/utils.py index b4733db..b31b1e9 100644 --- a/ogn/parser/utils.py +++ b/ogn/parser/utils.py @@ -55,11 +55,14 @@ def createTimestamp(time_string, reference_timestamp): return result +MATH_PI = 3.14159265359 + + class CheapRuler(): """Extreme fast distance calculating for distances below 500km.""" def __init__(self, lat): - c = math.cos(lat * 3.14159265359 / 180) + c = math.cos(lat * MATH_PI / 180) c2 = 2 * c * c - 1 c3 = 2 * c * c2 - c c4 = 2 * c * c3 - c2 @@ -69,8 +72,18 @@ class CheapRuler(): self.ky = 1000 * (111.13209 - 0.56605 * c2 + 0.0012 * c4) # latitude correction def distance(self, a, b): - """Points a and b are from tuple(lon,lat).""" + """Distance between point a and b. A point is a tuple(lon,lat).""" dx = (a[0] - b[0]) * self.kx dy = (a[1] - b[1]) * self.ky return math.sqrt(dx * dx + dy * dy) + + def bearing(self, a, b): + """Returns the bearing from point a to point b.""" + + dx = (b[0] - a[0]) * self.kx + dy = (b[1] - a[1]) * self.ky + if dx == 0 and dy == 0: + return 0 + else: + return math.atan2(-dy, dx) * 180 / MATH_PI + 90 diff --git a/tests/parser/test_utils.py b/tests/parser/test_utils.py index dab012f..901aab3 100644 --- a/tests/parser/test_utils.py +++ b/tests/parser/test_utils.py @@ -40,7 +40,7 @@ class TestStringMethods(unittest.TestCase): self.proceed_test_data(test_data) - def test_cheap_ruler(self): + def test_cheap_ruler_distance(self): koenigsdf = (11.465353, 47.829825) hochkoenig = (13.062405, 47.420516) @@ -48,6 +48,14 @@ class TestStringMethods(unittest.TestCase): distance = cheap_ruler.distance(koenigsdf, hochkoenig) self.assertAlmostEqual(distance, 128381.47612138899) + def test_cheap_ruler_bearing(self): + koenigsdf = (11.465353, 47.829825) + hochkoenig = (13.062405, 47.420516) + + cheap_ruler = CheapRuler((koenigsdf[1] + hochkoenig[1]) / 2) + bearing = cheap_ruler.bearing(koenigsdf, hochkoenig) + self.assertAlmostEqual(bearing, 110.761300063515) + if __name__ == '__main__': unittest.main()