Automatic pier flip in TelescopeBase (replaces chimera-autopierchange) - #281
Automatic pier flip in TelescopeBase (replaces chimera-autopierchange)#281wschoenell wants to merge 2 commits into
Conversation
lst - ra is not an hour angle until it is wrapped: with LST at 0.5 h and an object at RA 23.5 h, ra_to_ha() answered -23 h for an object one hour past the meridian. Anything comparing the result against a limit -- the pier flip below, dome geometry -- reads that as "still 23 hours to go" and never fires. Wrap it into [-12, +12), the range the rest of the code already assumes: east of the meridian negative, west positive.
Folds the chimera-autopierchange plugin into the telescope itself, the way per-filter focus offsets went into FilterWheelBase (astroufsc#259): behaviour that only the instrument can get right belongs on the instrument, not in a controller watching it from outside. TelescopeBase.control() becomes concrete: it runs the pier flip check on every cycle and then calls the driver's new _control() primitive, so drivers keep their periodic work and no longer decide whether the loop runs at all. FakeTelescope is migrated here. The loop rate drops to one cycle every 5 s, since what the base now runs there asks the mount where it is; a driver wanting a faster loop calls set_hz() in __start__ as before. A German equatorial mount is flipped by re-slewing it to the position it is already pointing at, which is what makes the mount pick the other side of the pier: telescope: name: fake type: FakeTelescope pier_flip_ha: 0 Leaving pier_flip_ha unset -- the default, and the whole story on a fork or alt-az mount -- disables the check before it queries the mount at all. Only a mount that reached the limit *by tracking* is flipped; one that slewed straight to an object already past it is left on the side its own driver chose. The plugin tracked that with a TelescopePierSide it inferred from the hour angle at the end of every slew, using the opposite sense of the ASCOM convention the enum otherwise carries, and only for slews it happened to see. The state here is one flag, armed by observing the mount track east of the limit, so a chimera restarted mid-target simply waits for the next crossing instead of guessing. The plugin also computed the hour angle from a bare lst - ra, so a target either side of the 0/24 h wrap was 23 hours from a flip that then never came; see the previous commit. A flip that raises leaves the check armed and is retried on the next cycle -- the mount is tracking into the pier, so quietly giving up is the one thing this must not do. The re-slew fires slew_begin/slew_complete like any other slew, so a dome in track mode follows along.
Codecov Report❌ Patch coverage is
Additional details and impacted files@@ Coverage Diff @@
## master #281 +/- ##
==========================================
+ Coverage 51.55% 51.68% +0.13%
==========================================
Files 102 102
Lines 8517 8542 +25
Branches 978 982 +4
==========================================
+ Hits 4391 4415 +24
- Misses 3928 3929 +1
Partials 198 198 ☔ View full report in Codecov by Harness. 🚀 New features to boost your workflow:
|
| Coord.from_h(ra), Coord.from_r(self.lst_in_rads()) | ||
| ).to_h() | ||
| ) | ||
| return (ha + 12) % 24 - 12 |
There was a problem hiding this comment.
move the coord convention adjustment to CoordUtil.ra_to_ha itself.
| self._park_position = None | ||
| # True once the mount has been seen tracking east of pier_flip_ha, i.e. | ||
| # heading for the flip. See _check_pier_flip(). | ||
| self._pier_flip_armed = False |
There was a problem hiding this comment.
This boolean is not needed, you can just take the desired flip/not-flip and return inside _check_pier_flip. A decision doesn't carry for the next cycle, it is always evaluated again.
| return | ||
|
|
||
| self.log.info(f"Hour angle {ha:.3f} h is past {flip_ha} h, flipping the pier.") | ||
| self.slew_to_ra_dec(*self.get_position_ra_dec()) |
There was a problem hiding this comment.
double check if the epoch return by get_position_ra_dec would be the expected one for the next slew so we don't move targets by mistake.
Folds the chimera-autopierchange plugin into the telescope itself, the way per-filter focus offsets went into
FilterWheelBasein #259. A German equatorial mount that cannot be trusted to track past the meridian is not something a controller should be watching from outside; it is part of what driving that mount means.What changes
TelescopeBase.control()becomes concrete: it runs the pier flip check on every cycle and then calls the driver's new_control()primitive. Drivers keep their periodic work and stop deciding whether the control loop runs at all.FakeTelescopeis migrated here.Leaving
pier_flip_haunset — the default, and the whole story on a fork or alt-az mount — returns before the check asks the mount anything.The mount is flipped by re-slewing it to the position it is already pointing at, which is what makes it pick the other side of the pier. Only a mount that reached the limit by tracking is flipped: one that slewed straight to an object already past it is left on the side its own driver chose.
The telescope's loop rate drops to one cycle every 5 s, since what the base now runs there asks the mount where it is and a flip is a minutes-scale event. A driver wanting a faster loop still calls
set_hz()in__start__, which runs after__init__.Why not leave it in the plugin
TelescopePierSidewith the opposite sense of the ASCOM convention that enum otherwise carries (pierEastis the normal pointing state, looking west). It also only saw the slews it was subscribed for, so a restart mid-target left it onUNKNOWNforever. What the check actually needs is one flag: armed by observing the mount track east of the limit, cleared by the flip. A chimera restarted mid-target then waits for the next crossing instead of guessing.lst - ra, never wrapped. With LST at 0.5 h and a target at RA 23.5 h — one hour past the meridian, i.e. overdue — it computed −23 h and concluded there were 23 hours to go. The flip never came. Fixed at the root inSite.ra_to_ha(), which now returns[-12, +12), in its own commit with its own test.except Exceptionaround the whole loop body. The mount is tracking into the pier at that point, so this is the one failure that must not be quiet: the check now stays armed and retries on the next cycle, and the exception reaches the control loop, which logs it (core: control loop survives exceptions from control() #272).Two options are gone.
dome(the plugin re-synced the dome itself): the flip firesslew_begin/slew_completelike any other slew, so a dome intrackmode follows on its own.check_interval: the check runs on the object's existing control loop.Compatibility
Out-of-tree drivers that still implement
control()keep working — they just don't get the flip, exactly as before this PR.TheSkyXTelescope,AlpacaTelescopeand the rest that never overrode it get it from config alone.chimera-astelcooverridescontrol()and wants a one-line rename to_control()to get the flip; issue opened there. So doeschimera-bisque's legacyTheSkyTelescope, superseded byTheSkyXTelescope.Users of the plugin drop the
AutoPierChangecontroller block and moveha_fliponto the telescope aspier_flip_ha; documented indocs/configuring.rst.Testing
8 new tests in
tests/chimera/instruments/test_telescope.pycover the crossing, the flip firing exactly once, a slew that lands past the limit not flipping, slewing and not-tracking disarming the check, a non-zero limit, the driver's_control()still running every cycle, and a failed flip being retried. One more intests/chimera/core/test_site.pypins the hour angle range across the 0/24 h wrap. Full suite: 273 passed, 8 skipped.Verified end to end against a live
chimerawithFakeTelescopeand the site clock at 60× sidereal, slewing to 18 minutes east of the meridian and letting it track across:and in the server log: