2020-09-11 18:16:10 +00:00
|
|
|
from datetime import datetime, timedelta, timezone
|
2020-10-10 19:37:35 +00:00
|
|
|
import math
|
2016-02-28 11:11:16 +00:00
|
|
|
|
2018-04-30 19:00:08 +00:00
|
|
|
FEETS_TO_METER = 0.3048 # ratio feets to meter
|
|
|
|
FPM_TO_MS = FEETS_TO_METER / 60 # ratio fpm to m/s
|
|
|
|
KNOTS_TO_MS = 0.5144 # ratio knots to m/s
|
2018-09-18 16:37:01 +00:00
|
|
|
KPH_TO_MS = 0.27778 # ratio kph to m/s
|
2018-04-30 19:45:28 +00:00
|
|
|
HPM_TO_DEGS = 180 / 60 # ratio between half turn per minute and degrees/s
|
2021-06-06 10:00:52 +00:00
|
|
|
INCH_TO_MM = 25.4 # ratio inch to mm
|
2016-02-28 11:11:16 +00:00
|
|
|
|
|
|
|
|
2020-10-09 07:23:10 +00:00
|
|
|
def fahrenheit_to_celsius(fahrenheit):
|
|
|
|
return (fahrenheit - 32.0) * 5.0 / 9.0
|
|
|
|
|
|
|
|
|
2016-09-01 11:01:44 +00:00
|
|
|
def parseAngle(dddmmhht):
|
|
|
|
return float(dddmmhht[:3]) + float(dddmmhht[3:]) / 60
|
2016-02-28 11:11:16 +00:00
|
|
|
|
|
|
|
|
2020-09-11 18:16:10 +00:00
|
|
|
def createTimestamp(time_string, reference_timestamp):
|
2018-05-01 09:21:28 +00:00
|
|
|
if time_string[-1] == "z":
|
|
|
|
dd = int(time_string[0:2])
|
|
|
|
hh = int(time_string[2:4])
|
|
|
|
mm = int(time_string[4:6])
|
|
|
|
|
|
|
|
result = datetime(reference_timestamp.year,
|
|
|
|
reference_timestamp.month,
|
|
|
|
dd,
|
2020-09-11 18:16:10 +00:00
|
|
|
hh, mm, 0,
|
|
|
|
tzinfo=timezone.utc if reference_timestamp.tzinfo is not None else None)
|
2017-09-30 16:25:02 +00:00
|
|
|
|
2021-04-05 11:49:16 +00:00
|
|
|
# correct wrong month
|
2018-05-01 09:21:28 +00:00
|
|
|
if result > reference_timestamp + timedelta(days=14):
|
2021-04-05 11:49:16 +00:00
|
|
|
result = (result.replace(day=1) - timedelta(days=14)).replace(day=result.day)
|
2018-05-01 09:21:28 +00:00
|
|
|
elif result < reference_timestamp - timedelta(days=14):
|
2021-04-05 11:49:16 +00:00
|
|
|
result = (result.replace(day=28) + timedelta(days=14)).replace(day=result.day)
|
2016-03-09 11:05:12 +00:00
|
|
|
else:
|
2018-05-01 09:21:28 +00:00
|
|
|
hh = int(time_string[0:2])
|
|
|
|
mm = int(time_string[2:4])
|
|
|
|
ss = int(time_string[4:6])
|
|
|
|
|
|
|
|
result = datetime(reference_timestamp.year,
|
|
|
|
reference_timestamp.month,
|
|
|
|
reference_timestamp.day,
|
2020-09-11 18:16:10 +00:00
|
|
|
hh, mm, ss,
|
|
|
|
tzinfo=timezone.utc if reference_timestamp.tzinfo is not None else None)
|
2018-05-01 09:21:28 +00:00
|
|
|
|
|
|
|
if result > reference_timestamp + timedelta(hours=12):
|
|
|
|
# shift timestamp to previous day
|
|
|
|
result -= timedelta(days=1)
|
|
|
|
elif result < reference_timestamp - timedelta(hours=12):
|
|
|
|
# shift timestamp to next day
|
|
|
|
result += timedelta(days=1)
|
|
|
|
|
|
|
|
return result
|
2020-10-10 19:37:35 +00:00
|
|
|
|
|
|
|
|
2020-10-12 17:59:41 +00:00
|
|
|
MATH_PI = 3.14159265359
|
|
|
|
|
|
|
|
|
2020-10-10 19:37:35 +00:00
|
|
|
class CheapRuler():
|
|
|
|
"""Extreme fast distance calculating for distances below 500km."""
|
|
|
|
|
|
|
|
def __init__(self, lat):
|
2020-10-12 17:59:41 +00:00
|
|
|
c = math.cos(lat * MATH_PI / 180)
|
2020-10-10 19:37:35 +00:00
|
|
|
c2 = 2 * c * c - 1
|
|
|
|
c3 = 2 * c * c2 - c
|
|
|
|
c4 = 2 * c * c3 - c2
|
|
|
|
c5 = 2 * c * c4 - c3
|
|
|
|
|
|
|
|
self.kx = 1000 * (111.41513 * c - 0.09455 * c3 + 0.00012 * c5) # longitude correction
|
|
|
|
self.ky = 1000 * (111.13209 - 0.56605 * c2 + 0.0012 * c4) # latitude correction
|
|
|
|
|
|
|
|
def distance(self, a, b):
|
2020-10-12 17:59:41 +00:00
|
|
|
"""Distance between point a and b. A point is a tuple(lon,lat)."""
|
2020-10-10 19:37:35 +00:00
|
|
|
|
|
|
|
dx = (a[0] - b[0]) * self.kx
|
|
|
|
dy = (a[1] - b[1]) * self.ky
|
|
|
|
return math.sqrt(dx * dx + dy * dy)
|
2020-10-12 17:59:41 +00:00
|
|
|
|
|
|
|
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
|
2020-10-12 18:28:09 +00:00
|
|
|
result = math.atan2(-dy, dx) * 180 / MATH_PI + 90
|
|
|
|
return result if result >= 0 else result + 360
|
2020-10-13 20:21:16 +00:00
|
|
|
|
|
|
|
|
|
|
|
def normalized_quality(distance, signal_quality):
|
|
|
|
"""Signal quality normalized to 10km."""
|
|
|
|
|
|
|
|
return signal_quality + 20.0 * math.log10(distance / 10000.0) if distance > 0 else None
|