GPS#
Great-circle geometry and motion estimated from positions.
Receivers that log only GPGGA sentences record position but neither speed
nor course. uataq.instruments.GPS fills both from the positions when
they are missing, and marks every filled value with the boolean columns
Speed_Estimated / Course_Estimated. Recorded values are never
overwritten.
import uataq
gps = uataq.read_data("trx01", instruments="gps", time_range="2017-03")["gps"]
moving = gps[gps.Speed_m_s > 1.0] # works in the GPGGA-only years too
Validated against a day of recorded 1 Hz speed and course from the TRAX
platform: correlation 0.995, median speed difference 0.00 m/s and median course
error 0.4 degrees while moving. Position noise becomes speed noise, so widen
estimate_window before thresholding a stationary
platform.
GPS helpers: great-circle geometry and motion estimated from positions.
When a receiver logs only GPGGA sentences (position, altitude, fix quality)
and not GPRMC, there is no recorded speed or course. Both can be recovered
from the positions themselves, which is what estimate_speed_course()
does; uataq.instruments.GPS calls it to fill whatever the files did
not record.
Formulas from https://www.movable-type.co.uk/scripts/latlong.html. They assume a spherical Earth, which is well under GPS noise at these distances.
- uataq.gps.EARTH_RADIUS_M = 6371000.0#
Mean Earth radius (m).
- uataq.gps.bearing(lat1, lon1, lat2, lon2)[source]#
Return the initial great-circle bearing from the first point to the second.
- Parameters:
lat1 (array_like) – Start point, in degrees.
lon1 (array_like) – Start point, in degrees.
lat2 (array_like) – End point, in degrees.
lon2 (array_like) – End point, in degrees.
- Returns:
Bearing in degrees clockwise from true north, in
[0, 360). Inputs are broadcast together. Coincident points give 0.- Return type:
- uataq.gps.estimate_speed_course(latitude, longitude, time=None, window=1, max_gap='60s')[source]#
Estimate speed and course from a sequence of positions.
Both are centered differences: the sample at position
iis described by the displacement fromi - windowtoi + window, so the estimate is symmetric in time and does not lag the track. Speed is the great-circle distance over the elapsed time; course is the bearing along it.- Parameters:
latitude (array_like) – Positions in degrees, ordered in time. A pandas Series with a DatetimeIndex supplies
timeon its own.longitude (array_like) – Positions in degrees, ordered in time. A pandas Series with a DatetimeIndex supplies
timeon its own.time (array_like, optional) – Sample times, convertible by
pandas.to_datetime(). Defaults to the index oflatitudewhen that is a Series.window (int) – Number of samples on each side of the centered difference.
1uses the immediate neighbors. A larger window averages over a longer baseline, which suppresses position noise at the cost of smoothing real accelerations and turns.max_gap (str or pandas.Timedelta, optional) – Longest interval between consecutive samples that may fall inside the difference. Where a larger gap is spanned the estimate is NaN, because a straight chord across a gap is not the path travelled.
Nonedisables the check.
- Returns:
Speed_m_sandCourse_deg, indexed bytime(namedTime_UTC). The first and lastwindowsamples are NaN, as are samples spanning a gap or a non-increasing time step.- Return type:
Notes
Position noise becomes speed noise: a receiver jittering by a few meters looks like it is moving at a few m/s when differenced over 1 s. Use a longer
window(or aggregate first) before thresholding a stationary platform, and treat the course as meaningless wherever the speed is at or below the noise level.
- uataq.gps.gps_time_from_time_of_day(logger_time, time_of_day)[source]#
Full GPS datetimes from a receiver’s time of day and the logger’s clock.
A receiver that logs only its time of day (
HHMMSS[.ss], e.g. horel’sGTIM) needs a date. Each value takes the date of its logger timestamp, moved a day either way when the two straddle midnight, so the logger clock only has to be within 12 h of GPS time.- Parameters:
logger_time (pd.DatetimeIndex) – The logger’s timestamps (naive UTC), one per row.
time_of_day (array-like) – The receiver’s time of day as an
HHMMSSnumber, aligned withlogger_time. NaN, or a value that isn’t a valid time of day, gives NaT.
- Returns:
GPS time (naive UTC), indexed like
logger_time.- Return type:
pd.Series
- uataq.gps.haversine(lat1, lon1, lat2, lon2, R=6371000.0)[source]#
Great-circle distance between two points (haversine formula).
- Parameters:
lat1 (array_like) – First point, in degrees.
lon1 (array_like) – First point, in degrees.
lat2 (array_like) – Second point, in degrees.
lon2 (array_like) – Second point, in degrees.
R (float) – Sphere radius. The result carries its units. Default is
EARTH_RADIUS_M, so distances come back in meters.
- Returns:
Distance in the units of
R. Inputs are broadcast together, so a single point against an array of points works.- Return type: