I'm in the process of a complete re-write of a stagnant and ill-conceived project. I would like some feedback on the following Python 2.7-3.4 code, before I go any deeper into it.
I would hope it is self-explanatory, but that is always the illusion when you stare at something long enough. The other dilemma is glossing over errors and squirrelly logic.
#!/usr/bin/env python
# -*- coding: utf-8 -*-
""" waypoint.py
A collection of waypoint and distance functions for the NxGPS Project:
Getting the most out of a $30 gps, for Navigatrix (http://navigatrix.net)
NxGPS project repository is (https://github.com/wadda/NxGPS)
"""
from __future__ import print_function
from pyproj import Geod # sudo pip3 install pyproj
from cmath import asin, sin
import math
# from math import radians, degrees
# import gps3
conversion = {'nautical': 1852.0, 'imperial': 1609.344, 'metric': 1000.0, 'meters': 1.0}
class Odometer(object):
"""Odometer is a wrapper around pyproj.Geod distance calculations"""
def __init__(self):
self.bearing_to = None
self.bearing_fro = None
self.distance = {}
def do_to_fro_distance(self, lat1, lon1, lat2, lon2, units='meters'):
"""lat lon from point A and point B, returns True North
bearings 'to', A-B(1-2), and 'fro', B-A (2-1) and distance in always
fashionable meters with optional nautical, imperial, or (kilo)metric"""
bearing_to = bearing_fro = self.distance['meters'] = None
try:
geoid = Geod(ellps='WGS84')
bearing_to, bearing_fro, self.distance['meters'] = geoid.inv(lon1, lat1, lon2, lat2)
except Exception as error:
print("Can't calculate to/fro because:", error)
finally:
if units not in 'meters':
self.distance[units] = self.distance['meters'] / conversion[units]
self.bearing_to = bearing_to % 360
self.bearing_fro = bearing_fro % 360
return self.bearing_to, self.bearing_fro, self.distance
odometer = Odometer()
class Crosstrack(object):
def __init__(self):
self.startpoint_lat = None
self.startpoint_lon = None
self.a2b_radians = None
self.trip_distance = {} # similar to 'odometer.distance', except A to C distance from last reset.
self.distance = {}
def do_crosstrack(self, current_lat, current_lon, start_lat=None, start_lon=None,
end_lat=None, end_lon=None, units='meters'):
"""
return crosstrack distance from current position, and last set track or
return crosstrack distance from current position, start, and end positions
Formula from:
http: // williams.best.vwh.net / avform.htm # XTE
"""
default_lat = -15.560615 # Apataki Carenage
default_lon = -146.241122 # Apataki Carenage
default_a2b_radians = 4.2538533202126025 # bearing in radians Apataki to Kaputar
earth_radius = 6371009.0 # meters...It's a mean radius, but nice enough.
crosstrack_distance = {}
try:
if all([start_lon, start_lon, end_lat, end_lon]): # both start and end shall start and end together.
# A to B
a2b_bearing, _, _ = odometer.do_to_fro_distance(start_lat, start_lon, end_lat, end_lon)
self.a2b_radians = math.radians(a2b_bearing)
self.startpoint_lat = start_lat
self.startpoint_lon = start_lon
# A to C
if not any([start_lat, start_lon, end_lat, end_lon]):
if not self.startpoint_lat: # one is enough, or one is not enough, depending on perspective
self.startpoint_lat = default_lat # resort to defaults
self.startpoint_lon = default_lon
self.a2b_radians = default_a2b_radians
start_lat = self.startpoint_lat
start_lon = self.startpoint_lon
bearing_to, _, distance = odometer.do_to_fro_distance(start_lat, start_lon, current_lat, current_lon)
a2c_radians = math.radians(bearing_to)
self.trip_distance = distance # Distance A to C in meters
# Crosstrack calculations in radians, output as a complex numbers {'meters': (281.8893996162284+0j)
crosstrack_distance['meters'] = (asin(sin(distance['meters']) * sin(a2c_radians - self.a2b_radians))
* earth_radius)
except Exception as error:
print('Can\'t calculate crosstrack because: ', error)
finally:
if units not in 'meters':
crosstrack_distance[units] = crosstrack_distance['meters'] / conversion[units]
self.trip_distance[units] = self.trip_distance['meters'] / conversion[units]
self.distance = crosstrack_distance.copy()
# Printing for testing
print('Positive should mean right of course, negative means left with the other choice.')
for k, v in crosstrack_distance.items():
print(k, ":", v.real) # extract 'real' number
print("Scabed on xtrk trip distance:", self.trip_distance)
print('Real and imaginary in complex numbers:\n')
return self.distance
crosstrack = Crosstrack()
# WIP
class BestScenario(object):
""" Hypothetical 'if' calculations for speculation, fairy tales, and general BS
deceptively based on a few 'real world' inputs. Variations on a theme requiring
current position
current time
current speed
current course
waypoint, distance and bearing
"""
pass
def closest_approach(self):
""" Present course will pass X distance from waypoint,
or course required to maintain minimum distance (inverse crosstrack)
current position
waypoint
"""
pass
def eta(self):
""" ETA given present course and speed
waypoint distance and bearing
current speed
current course
current time
"""
pass
def shortest(self):
""" Shortest distance to waypoint given present course
waypoint distance and bearing
current course
"""
pass
def vmg(self):
""" Velocity Made Good given present course and speed
waypoint bearing
current course
current speed
"""
pass
def whentack(self):
""" When to tack given present course, speed, and pre-determined angle
waypoint bearing
current course
course delta required
current speed
"""
pass
crosstrack.do_crosstrack(41.257307,-100.73410,41.29100,-100.73410,41.21998,-100.73410, units='nautical')
Less so;crosstrack.do_crosstrack(41.257307,-100.73415)
\$\endgroup\$