Skip to content

fix(meade): accept the unsigned site longitude INDI sends - #304

Draft
rbhbokka wants to merge 1 commit into
OpenAstroTech:developfrom
rbhbokka:fix/meade-unsigned-site-longitude
Draft

fix(meade): accept the unsigned site longitude INDI sends#304
rbhbokka wants to merge 1 commit into
OpenAstroTech:developfrom
rbhbokka:fix/meade-unsigned-site-longitude

Conversation

@rbhbokka

@rbhbokka rbhbokka commented Sep 7, 2026

Copy link
Copy Markdown

Regression from #291. Draft, because it also raises a protocol-convention question I'd like a maintainer to rule on — see the last section, and note that #296 asserts the opposite convention for the signed form.

The defect

readLongitude() uses Cursor::signed3(), which makes the sign mandatory. INDI sends :Sg121*53# unsigned for a site 121°53′ west, so the command is answered 0 and the mount silently keeps whatever longitude it had.

Captured off the wire on an INDI connect to an OpenAstroExplorer:

:SL10:42:31  :SC09/05/26  :SG+7.0  :Sg121*53  :St+37*20

An unsigned Meade site longitude is the legacy count running westward from Greenwich, so it folds into the east-positive range the mount stores by negating modulo a full circle.

Not by 180 - value. That is the other reflection — the one that puts Greenwich at 180 — and it is what the (now dead) Longitude::ParseFromMeade computes and what MeadeProtocol.hpp described. It turns a 121°53′ west site into 58°07′ east: exactly 180°, i.e. 12 hours of local sidereal time, from where it belongs, which mispoints every subsequent GOTO.

None of this is visible in readback — :Gg# applies the inverse of whatever the setter did, so a wrong convention round-trips perfectly. It was verified instead by comparing :XGL# against an independently computed LST.

Also fixed

Range validation. digits(3, …) accepted 0..999 and the wrap was a single if, not a loop, so :Sg600*00# yielded −240°. Nothing downstream caught it: core::Longitude(int, int, int) never calls checkHours(), and EEPROMStore::storeLongitude clamps degrees * 100 into an int16 (±327.67°) — that clamp destroys the mod-360 equivalence and persists a genuinely wrong site across reboots. Values outside one full circle, and minutes above 59, are now refused.

Signed overflow on AVR. The arcminute arithmetic is widened to long. int is 16-bit on ATmega2560, so deg * 60 + mm exceeded INT16_MAX from :Sg545*69# onward — undefined behaviour in an -O2 build, and it did diverge between 16- and 32-bit targets. The range check also prevents reaching it; both are in.

Deliberately unchanged

The signed form. Which hemisphere its sign denotes is a separate question — MeadeProtocol.hpp says negative goes east, the dead Longitude::ParseFromMeade implemented that, #291 dropped it, and #296 proposes restoring it. This change passes signed values through exactly as develop does and adds a test pinning that, so the discussion starts from a documented baseline. I have no evidence either way for the signed form and am not trying to settle it here.

Known limitation

A west longitude under one degree (000*01..000*59) still loses its sign, because MeadeLongitude carries the sign only in degrees and -0 is unrepresentable. There is a KNOWN LIMITATION comment at the exact spot and a test named for it. The structural fix is fix/meade-sign-of-zero, which gives all three coordinate structs a real sign channel; I kept it out of this PR to keep the convention argument separable from the struct change.

The question for maintainers

MeadeProtocol.hpp currently documents the unsigned form as "0 to 360 going WEST with 180 at Greenwich. So 369 is 179W and 1 is 179E." No live code implements that, and it disagrees with what INDI puts on the wire. This PR updates that block to match observed client behaviour. If the document is right and INDI is wrong, this PR is wrong too — please say so and I'll close it.

Verification

pio test -e native: 281 pass (269 on develop + 12 added, none removed or weakened). An exhaustive sweep of all 21,600 unsigned wire values decodes exactly, except the 59 in the documented sub-degree band. Builds clean under -Werror for oaeboardv1 and ramps; avr-g++ -mmcu=atmega2560 -Werror -Wconversion clean on the parser.

File overlap

Same two files as #303 (:SG), in different blocks, and fix/meade-sign-of-zero rewrites the three readers this touches. All are independently mergeable; whichever lands second gets a trivial rebase from me.

Regression from OpenAstroTech#291. readLongitude() uses Cursor::signed3(), which makes the
sign mandatory, but INDI sends ":Sg121*53#" unsigned for a site 121d53' west.
The command is answered "0" and the mount keeps whatever longitude it had.

An unsigned Meade site longitude is the legacy count running westward from
Greenwich, so it folds into the east-positive range the mount stores by
negating modulo a full circle. Not by 180 - value, which is what the (dead)
Longitude::ParseFromMeade computes and what MeadeProtocol.hpp described: that
reflection turns a 121d53' west site into 58d07' east, exactly 180 degrees --
12 hours of local sidereal time -- from where it belongs. None of this is
visible in readback, because :Gg# applies the inverse of whatever the setter
did, so a wrong convention round-trips perfectly; it was verified instead by
comparing :XGL# against an independently computed LST.

Also reject values outside one full circle and minutes above 59. Nothing
downstream did: core::Longitude(int, int, int) never calls checkHours(), and
EEPROMStore::storeLongitude clamps degrees*100 into an int16, which destroys
the mod-360 equivalence and persists a genuinely wrong site across reboots.
The arithmetic is widened to long, which also removes a signed overflow that
was undefined behaviour on the 16-bit AVR target from ":Sg545*69#" onward.

The signed form is deliberately left exactly as it is. Which hemisphere its
sign denotes is a separate question, contested by OpenAstroTech#296, and this change does
not answer it.

Known limitation, documented in the code and in MeadeProtocol.hpp: a west
longitude under one degree still loses its sign, because MeadeLongitude carries
the sign only in `degrees`. Fixing that means letting the struct hold the sign
separately.

Co-authored-by: Claude <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant