Loading noctua/guider.py +32 −7 Original line number Diff line number Diff line Loading @@ -31,6 +31,8 @@ class Guider: mirror with automatic offload to the telescope when the stroke limit is reached. """ ONCE_MAX_ATTEMPTS = 3 def __init__(self): self.active = False Loading Loading @@ -135,14 +137,30 @@ class Guider: async def _loop(self): once_attempts = 0 while self.active: try: await asyncio.get_running_loop().run_in_executor(None, self._step) corrected = await asyncio.get_running_loop().run_in_executor(None, self._step) except Exception as e: msg = f"step error: {e}" log.error(f"Guider: {msg}") self.error.append(msg) corrected = False if self.once: once_attempts += 1 # A step can return False without a real failure (e.g. the # first "stick" step only locks the reference and applies # no correction yet) — keep trying for a bounded number of # attempts so "once" always means one *applied* correction, # not just one _step() call. if not corrected and once_attempts < self.ONCE_MAX_ATTEMPTS: await asyncio.sleep(0) continue if not corrected: msg = f"once: no correction applied after {once_attempts} attempts" log.warning(f"Guider: {msg}") self.error.append(msg) self.active = False break # No fixed delay: _step()/_acquire() already block on the camera Loading @@ -151,15 +169,16 @@ class Guider: await asyncio.sleep(0) def _step(self): """Run one guiding cycle. Returns True iff a correction was applied.""" cam = getattr(devices, self.camera, None) if cam is None: self.error.append(f"camera '{self.camera}' not found") return return False data, header = self._acquire(cam) if data is None: return return False ny, nx = data.shape cx = nx // 2 if self.box_center is None else int(self.box_center[0]) Loading @@ -176,7 +195,7 @@ class Guider: msg = f"fit_star: {e}" log.error(f"Guider: {msg}") self.error.append(msg) return return False star_x = x0 + float(fitted.xc) star_y = y0 + float(fitted.yc) Loading @@ -189,7 +208,7 @@ class Guider: if self._stick_target is None: self._stick_target = [star_x, star_y] log.info(f"Guider: stick locked at ({star_x:.1f}, {star_y:.1f})") return return False tgt_x, tgt_y = self._stick_target else: tgt_x, tgt_y = float(self.target[0]), float(self.target[1]) Loading @@ -212,19 +231,24 @@ class Guider: devices.tel.offset = updated self.last_offset = updated log.info(f"Guider: New offset value: alt={updated[0]:.2f}\" az={updated[1]:.2f}\"") return True except Exception as e: msg = f"telescope offset failed: {e}" log.error(f"Guider: {msg}") self.error.append(msg) return False elif self.actuator == "ao_x": self._step_ao_x(cam, header, nx, ny, dpx, dpy, tgt_x, tgt_y) return self._step_ao_x(cam, header, nx, ny, dpx, dpy, tgt_x, tgt_y) return False def _step_ao_x(self, cam, header, nx, ny, dpx, dpy, tgt_x, tgt_y): """Apply one AO-X correction (with offload if needed). Returns True iff applied.""" ao = getattr(devices, "ao", None) if ao is None or self._ao_calib_inv is None: return return False delta = self._ao_calib_inv @ np.array([dpx, dpy]) new_pos = [self.ao_pos[0] + delta[0], self.ao_pos[1] + delta[1]] Loading @@ -241,6 +265,7 @@ class Guider: self.ao_pos = new_pos self.last_offset = new_pos log.info(f"Guider: AO-X → ({new_pos[0]:.1f}, {new_pos[1]:.1f})") return True def _offload_to_telescope(self, cam, header, nx, ny, tgt_x, tgt_y): Loading Loading
noctua/guider.py +32 −7 Original line number Diff line number Diff line Loading @@ -31,6 +31,8 @@ class Guider: mirror with automatic offload to the telescope when the stroke limit is reached. """ ONCE_MAX_ATTEMPTS = 3 def __init__(self): self.active = False Loading Loading @@ -135,14 +137,30 @@ class Guider: async def _loop(self): once_attempts = 0 while self.active: try: await asyncio.get_running_loop().run_in_executor(None, self._step) corrected = await asyncio.get_running_loop().run_in_executor(None, self._step) except Exception as e: msg = f"step error: {e}" log.error(f"Guider: {msg}") self.error.append(msg) corrected = False if self.once: once_attempts += 1 # A step can return False without a real failure (e.g. the # first "stick" step only locks the reference and applies # no correction yet) — keep trying for a bounded number of # attempts so "once" always means one *applied* correction, # not just one _step() call. if not corrected and once_attempts < self.ONCE_MAX_ATTEMPTS: await asyncio.sleep(0) continue if not corrected: msg = f"once: no correction applied after {once_attempts} attempts" log.warning(f"Guider: {msg}") self.error.append(msg) self.active = False break # No fixed delay: _step()/_acquire() already block on the camera Loading @@ -151,15 +169,16 @@ class Guider: await asyncio.sleep(0) def _step(self): """Run one guiding cycle. Returns True iff a correction was applied.""" cam = getattr(devices, self.camera, None) if cam is None: self.error.append(f"camera '{self.camera}' not found") return return False data, header = self._acquire(cam) if data is None: return return False ny, nx = data.shape cx = nx // 2 if self.box_center is None else int(self.box_center[0]) Loading @@ -176,7 +195,7 @@ class Guider: msg = f"fit_star: {e}" log.error(f"Guider: {msg}") self.error.append(msg) return return False star_x = x0 + float(fitted.xc) star_y = y0 + float(fitted.yc) Loading @@ -189,7 +208,7 @@ class Guider: if self._stick_target is None: self._stick_target = [star_x, star_y] log.info(f"Guider: stick locked at ({star_x:.1f}, {star_y:.1f})") return return False tgt_x, tgt_y = self._stick_target else: tgt_x, tgt_y = float(self.target[0]), float(self.target[1]) Loading @@ -212,19 +231,24 @@ class Guider: devices.tel.offset = updated self.last_offset = updated log.info(f"Guider: New offset value: alt={updated[0]:.2f}\" az={updated[1]:.2f}\"") return True except Exception as e: msg = f"telescope offset failed: {e}" log.error(f"Guider: {msg}") self.error.append(msg) return False elif self.actuator == "ao_x": self._step_ao_x(cam, header, nx, ny, dpx, dpy, tgt_x, tgt_y) return self._step_ao_x(cam, header, nx, ny, dpx, dpy, tgt_x, tgt_y) return False def _step_ao_x(self, cam, header, nx, ny, dpx, dpy, tgt_x, tgt_y): """Apply one AO-X correction (with offload if needed). Returns True iff applied.""" ao = getattr(devices, "ao", None) if ao is None or self._ao_calib_inv is None: return return False delta = self._ao_calib_inv @ np.array([dpx, dpy]) new_pos = [self.ao_pos[0] + delta[0], self.ao_pos[1] + delta[1]] Loading @@ -241,6 +265,7 @@ class Guider: self.ao_pos = new_pos self.last_offset = new_pos log.info(f"Guider: AO-X → ({new_pos[0]:.1f}, {new_pos[1]:.1f})") return True def _offload_to_telescope(self, cam, header, nx, ny, tgt_x, tgt_y): Loading