fix(meade): build declination from wire components without losing the sign - #302
Open
rbhbokka wants to merge 1 commit into
Open
fix(meade): build declination from wire components without losing the sign#302rbhbokka wants to merge 1 commit into
rbhbokka wants to merge 1 commit into
Conversation
… sign Follow-up to OpenAstroTech#300. The hemisphere conversion it restored is correct; the component assembly feeding it is not. Declination::fromCelestialDegrees() computed const long wireSecs = ((60L * deg) + min) * 60L + sec; but meade::DecCoordinate stores minutes and seconds as uint8_t magnitudes and carries the sign only in degrees, so for a negative declination they were added toward zero instead of away from it. The error is 2 * (min * 60 + sec) arcseconds -- up to 1.9994 degrees -- on every celestial declination in (-90, 0) with non-zero arcminutes, in both hemispheres. It is directly observable, because the getter is right and the setter is wrong: :Sd-05*30:00# followed by :Gd# reads back -04*30:00. getCelestialDegrees() delegates the split to core::DayTime::splitSeconds(), while the join was hand-rolled. :CM sync takes the same path, so a plate-solve sync at a negative declination writes the error into the mount's home reference rather than into a single slew. The tests did not catch it because platformio.ini's native build_src_filter covers only core, ports and adapters, so src/Declination.cpp is never compiled into the test binary. OpenAstroTech#300's tests exercise axisToCelestialSeconds, celestialToAxisSeconds and splitSeconds, all of which are correct and are unchanged here. Add core::DayTime::joinSeconds() beside splitSeconds as its inverse, refactor DayTime(int, int, int) onto it -- that constructor already applied the sign correctly, so this lifts existing behaviour rather than introducing new arithmetic -- and reduce fromCelestialDegrees() to composition, leaving no bare arithmetic outside core. Co-authored-by: Claude <[email protected]>
rbhbokka
marked this pull request as ready for review
September 7, 2026 03:21
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.
Follow-up to #300, which restored the DEC hemisphere conversion. The conversion itself is correct; the component assembly that feeds it is not.
The defect
Declination::fromCelestialDegrees()(src/Declination.cpp) builds the wire-seconds total asmeade::DecCoordinateisint16_t degrees; uint8_t minutes; uint8_t seconds;— minutes and seconds are unsigned magnitudes, and onlydegreescarries the sign. For a negative declination they are therefore added toward zero rather than away from it.Error =
2 × (minutes × 60 + seconds)arcseconds, i.e. 0 to 1.9994°, on every celestial declination in (−90°, 0°) with non-zero arcminutes, in both hemispheres.The setter and the getter disagree as a result, since
getCelestialDegrees()splits correctly viacore::DayTime::splitSeconds()while the join was hand-rolled —:Sd-05*30:00#followed by:Gd#returns-04*30:00.-05*30:00-24*23:00(M8)-69*06:00(LMC)-89*59:59:CMsync goes through the samedecFromWire()path, so a plate-solve sync at a negative declination writes the error into the mount's home reference rather than into a single slew.These figures are derived by exercising the shipped
fromCelestialDegrees()text against the realcore::conversions, not from a hardware session — I have not reproduced it on a mount.Why the tests did not catch it
platformio.inisetsbuild_src_filter = +<./core> +<./ports> +<./adapters>, sosrc/Declination.cppis never compiled into the native test binary. #300's tests exercisecore::Declination::axisToCelestialSeconds,celestialToAxisSecondsandcore::DayTime::splitSeconds— all three are correct and are unchanged here. The one piece of new arithmetic that landed outsidesrc/coreis the one that was wrong.The fix
The asymmetry is the bug:
getCelestialDegrees()delegates the split to the tested primitivecore::DayTime::splitSeconds(), whilefromCelestialDegrees()hand-rolled the join, because that inverse was never written.This adds
core::DayTime::joinSeconds()next tosplitSeconds()as its exact inverse, refactors the existingDayTime(int, int, int)constructor onto it — that constructor already applied the sign correctly, so this lifts existing behaviour rather than introducing new arithmetic — and reducesfromCelestialDegrees()to pure composition, leaving no bare arithmetic outsidecore.Refactoring the constructor onto the new primitive is deliberate: two pre-existing constructor tests now also guard the join, so the two can no longer drift apart.
Verification
pio test -e native: 275 pass (269 ondevelop+ 6 added).readDecCoordinateaccepts, both hemispheres, is now exact everywhere; before this change 1,771,746 of 3,543,852 inputs were wrong, worst case 14,400 arcsec.-Werrorforoaeboardv1(ESP32) andramps(AVR, 16-bitint).Not fixed here
DecCoordinatecarries the sign only indegrees, so-00*30:00is unrepresentable and still parses as+00*30:00regardless of this change. That is a separate, wider issue across all three coordinate structs; it is noted in a comment here and addressed onfix/meade-sign-of-zero.File overlap
None with the other Meade fixes I have open — this touches
src/Declination.cpp,src/core/types/DayTime.*and thetest_core/typestests only.