fix(meade): build declination from wire components without losing the sign - #302
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 <noreply@anthropic.com>
|
After this commit, oat hangs immediately after displaying the version; the connection to the PC is also lost. |
Sorry you hit that. I don't think this commit can be the cause, though, and I'd rather narrow it down with you than guess. The function it changed, My guess is "after this commit" means "after I pulled", and that same pull also brought #300, #297 and #291. Things that would narrow it down a lot:
Unrelated, but I spotted it while looking: |
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.