Add POS MV as an alternative to the onboard dual RTK-GPS - #4
Open
belasi01 wants to merge 17 commits into
Open
Conversation
The ship's Applanix POS MV broadcasts INGGA/INZDA/INHDT/PASHR/INVTG sentences over UDP (port configurable, e.g. 12000). This adds a POSMV class that parses them and writes a synthesized $GPRMC frame to the shared data logger, keeping raw files compatible with downstream tools (prepSAS.py/HyperInSPACE) that expect $GPRMC. Modeled on GPS.format_data_as_gprmc(); heading/heading_accuracy are also captured for use as an optional autopilot heading source. Not wired into Runner yet -- inert until a [POSMV] config section exists and instantiates it. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Wires the optional POSMV interface into Runner: instantiated only if a [POSMV] config section exists, started continuously alongside GPS, and usable as ship_heading source via heading_source = posmv_heading. Inert by default -- heading_source stays gps_relative_position unless explicitly changed, so this has no effect on the currently deployed config. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Note the POS MV source address (10.0.0.16) for reference; the host config key itself is the local UDP bind address (0.0.0.0), not the sender's address. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Live testing against the ship's actual POS MV feed on the pySAS006 showed $INGGA (the longest sentence) gets split by the sender across two UDP datagrams, so treating each recvfrom() as one complete line silently dropped every INGGA and left datetime/position unset, blocking all $GPRMC writes. Accumulate a buffer across recvfrom() calls and split on newlines instead. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Displays runner.heading_source (e.g. RTK, POS MV, THS) so it's clear at a glance which source is driving the autopilot -- useful now that posmv_heading is a real option alongside the RTK GPS. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
make_umtwr_frame() always recomputed ship_heading directly from the RTK GPS regardless of heading_source, while the SAS heading field right next to it already used self.ship_heading (the active-source value). This meant switching heading_source (e.g. to posmv_heading) silently kept logging RTK data under "ship heading" in the UMTWR frames instead of the source actually steering the tower. Track ship_heading_accuracy per source in get_ship_heading() and reuse self.ship_heading/self.ship_heading_accuracy in make_umtwr_frame() so both fields stay consistent with whatever source is configured. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Lets the operator switch heading_source (RTK, GPS Motion, GPS Vehicle, THS, POS MV) from the Settings modal instead of editing the config file, applied live via runner.heading_source (no restart needed for already-configured sources). POS MV is shown disabled with an explanatory label when no [POSMV] section is configured, and save_settings rejects it defensively even if selected anyway. The sidebar's heading source label is now wired to the same 1s-refresh callback as the other telemetry fields instead of being frozen at page load, so it reflects live changes. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
get_ship_heading() routed posmv.heading through pilot.get_ship_heading(), which subtracts compass_zero (gps_orientation_on_ship, calibrated for the RTK antenna mounting offset). POS MV already outputs the ship's true heading directly, so subtracting that offset introduced a ~90 degree error (confirmed live: displayed 220 vs actual ship heading 312, a 92 degree gap matching compass_zero=90). Use normalize_angle() directly instead. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
The gps_motion/gps_vehicle/ths_heading options remain supported via the config file for advanced use, but are noisy for normal operators choosing between the two sources actually in use on this vessel. Also note in GPS Orientation's help text that it only applies to the RTK source, since POS MV reports true heading directly and ignores that offset (see previous fix removing compass_zero from posmv_heading). Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Explain the two operator-facing heading source choices (RTK vs POS MV), how to enable POS MV ([POSMV] config section + restart), and that the GPS Orientation setting only applies to RTK. Note the advanced/legacy sources still available via config file only. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
heading_source = posmv_heading only swapped the heading calculation; sun position and clock sync still hardcoded self.gps, and the onboard GPS kept writing its own $GPRMC alongside POS MV's, producing two interleaved position streams in the raw files. This defeated the actual goal: after one of the two RTK antennas failed at the end of leg 1, being able to run fully off the ship's POS MV without depending on pySAS's own GPS hardware at all. - Add POSMV.packet_pvt_received (named to match GPS's attribute so both can be used interchangeably as a position/time source). - Factor get_sun_position()/get_time_sync() through a shared _position_source() that follows heading_source, same no-fallback philosophy as get_ship_heading() (a stale/missing source fails loudly rather than silently reverting to GPS). - Skip self.gps.start_logging() in wakeup()/run_manual() while posmv_heading is active, and stop it immediately on Settings save, so only POS MV's $GPRMC ends up in the raw files. - Update the Settings FormText and README to describe the expanded scope (heading + sun position + clock sync + raw file logging). Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
…source get_fig_system_orientation() still hardcoded self.gps for the THS/IMU magnetic declination correction and the ship heading accuracy text, even when heading_source = posmv_heading. Route both through Runner._position_source() instead, matching the backend fix. Also relabel the polar plot's "Ship (Dual GPS)" trace to "Ship (POS MV)" when active, and add a Position (lat/lon) readout in the sidebar sourced from whichever system is actually driving heading/sun position. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Show Sun Azimuth below Sun Elevation, plus the angular difference between ship heading and sun azimuth. When that difference is within 5 degrees of 180 (sun near dead astern), display a red "Potential Ship Shadow on Lt" badge -- the ship's own hull/superstructure can shadow the water surface Lt is measuring in that geometry. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
…p) in Settings The pySAS tower was found mounted ~10 degrees off from the ship's bow. AutoPilot.tower_zero (indexing_table_orientation_on_ship) already exists for exactly this and is applied to ship_heading in steer()/make_umtwr_frame()/the polar plot regardless of heading_source -- unlike gps_orientation_on_ship (compass_zero), which only corrects the RTK antenna mounting and explicitly does not apply to POS MV. It just wasn't exposed in the UI, only settable by editing the config file. Add a "Tower Orientation" field mirroring GPS Orientation's pattern so it can be tuned live from Settings. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
blind_zone_center was shifted by ship heading only, while the tower trace right next to it also subtracts tower_zero -- both are derived from the same raw tower_limits (indexing table's own position frame), so both need the same correction. Left uncorrected, the gray blind zone sector drifted out of alignment with the actual unreachable sector as soon as tower_zero was set away from 0 (as it now is, to fix the ~10 degree mounting offset). Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
The separate IMU sensor has been unreliable in the field. Add motion_source (independent of heading_source) so the operator can pick which sensor's pitch/roll is displayed and logged, defaulting to THS (always available, embedded in HyperSAS). - POSMV: parse roll/pitch/roll_accuracy/pitch_accuracy from PASHR (previously only heading was read from it), and add format_data_as_satths()/_write_attitude() mirroring IMU's existing synthetic SATTHS frame writer, so downstream tools reading pitch/ roll from a SATTHS frame keep working when POS MV is selected. - IMU: add a _log_data gate (it previously always wrote, unconditionally) so its raw SATTHS frame stops when motion_source != 'imu', avoiding two conflicting synthetic frames in the raw files -- same principle as the earlier GPS/$GPRMC suppression under heading_source = posmv_heading. - Runner: sync both sensors' logging flags to motion_source at startup; ui.py's Settings toggles them live on save. - ui.py: collapse the timeseries plot's four fixed Pitch/Roll (IMU)/ (THS) traces into a single dynamically-labeled Pitch/Roll pair sourced via the new get_motion_source() helper. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
POSMV.start() unconditionally set _log_data = True the moment the UDP socket connected, and nothing ever set it back to False -- so once heading_source was switched to posmv_heading, POS MV kept writing a continuous $GPRMC stream to the raw files regardless of whether HyperSAS was actually measuring, unlike GPS whose logging is explicitly started/stopped alongside HyperSAS via start_logging()/ stop_logging(). Reported by Nils on PR review. Add the same start_logging()/stop_logging() pair to POSMV (for $GPRMC) plus start_logging_attitude()/stop_logging_attitude() (for the SATTHS-like pitch/roll frame, gated by motion_source), and wire them into the same four call sites GPS already uses: run_manual(), go_to_sleep(), wakeup(), and the manual-mode HyperSAS switch in ui.py, plus applied immediately on Settings save. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
On pySAS006, one of the two RTK GPS antennas failed mid-leg, and the RTK moving-baseline heading solution was separately found to occasionally lock onto a wrong integer ambiguity (heading briefly sweeping through most of the compass while still reporting deceptively tight accuracy). Since the vessel carries its own integrated GPS+IMU navigation system (Applanix POS MV) broadcasting NMEA-like sentences over UDP, this adds it as a selectable alternative source for heading, position/time, and pitch/roll -- so pySAS can keep operating normally if its own GPS antennas fail.
POSMVinterface (interfaces.py): parsesINGGA/INZDA/INHDT/PASHR/INVTGover UDP and writes a synthesized$GPRMCframe to the shared data logger, so downstream tools (prepSAS.py/HyperInSPACE) keep working unmodified.heading_source = posmv_heading: uses POS MV's true heading directly (no RTK-antenna mounting offset applied, unlike the RTK sources). When selected, sun position and clock sync also switch to POS MV, and the onboard GPS's own$GPRMClogging is suppressed, so pySAS becomes fully independent of its own GPS hardware end-to-end.motion_sourcesetting (independent ofheading_source): choose pitch/roll from THS (default), the separate IMU, or POS MV -- added after the IMU proved unreliable in the field. Writes a synthetic SATTHS frame for IMU/POS MV, mirroring the existing IMU writer.tower_orientation_on_shipsetting to correct the tower's own physical mounting offset relative to the ship's bow (found to be ~10 degrees off on this unit), applied consistently to both the displayed heading and the polar plot's blind-zone sector.All of this is opt-in via a
[POSMV]/[IMU]config section and the Settings dropdowns; a pySAS with neither configured behaves exactly as before (heading_source = gps_relative_position,motion_source = thsby default).Test plan
$GPRMC(position/time/speed/course match input).$GPRMCwrites.🤖 Generated with Claude Code