feat(turret): per-machine objective turret pulse offset#575
Open
Alpaca233 wants to merge 1 commit into
Open
Conversation
Normally the objective turret's homing limit switch coincides with slot 1, so after homing (which zeroes the counter at the switch) every objective lands at the correct angle. On one unit the switch does not sit exactly at the first slot, so all objectives are rotated by the same small constant. Add a per-machine config knob OBJECTIVE_TURRET_OFFSET_PULSES (default 0, may be negative), loaded from the machine .ini [GENERAL] section and added to every slot target in _rotate_to as a single global offset. The NiMotion hardware homing-offset register can't carry this: home() unconditionally issues SET_ZERO after the seek (re-zeroing the counter at the switch) and _HOMING_PARAMS forces the register to 0 on every connect, so the correction is applied purely in software. The value is parsed from the machine .ini (via json.loads) and could be a float/str/bool, so it is validated to be an int at controller init (raising ValueError otherwise). Threaded through the controller constructor mirroring the existing `positions` fallback and wired from microscope.py; the simulation twin accepts the kwarg for constructor parity. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Contributor
There was a problem hiding this comment.
Pull request overview
Adds a per-machine objective turret pulse offset so units whose homing limit switch is not exactly aligned with slot 1 can apply a uniform correction in software without changing homing behavior.
Changes:
- Introduces
OBJECTIVE_TURRET_OFFSET_PULSESincontrol._defand plumbs it through microscope construction into the objective turret controller. - Applies the offset in
ObjectiveTurret4PosController._rotate_to()when computing absolute target pulses. - Adds unit tests covering default/positive/negative offsets, non-int validation,
_deffallback, and simulation kwarg parity.
Reviewed changes
Copilot reviewed 4 out of 4 changed files in this pull request and generated 1 comment.
| File | Description |
|---|---|
| software/control/objective_turret_controller.py | Adds offset_pulses init + validation and applies offset to computed slot target pulses. |
| software/control/microscope.py | Passes the per-machine offset into the turret controller kwargs (real + simulation). |
| software/control/_def.py | Defines OBJECTIVE_TURRET_OFFSET_PULSES default and documents intended usage. |
| software/tests/control/test_objective_turret_controller.py | Adds test coverage for offset behavior, validation, and simulation constructor parity. |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
Comment on lines
+267
to
+272
| offset = offset_pulses if offset_pulses is not None else OBJECTIVE_TURRET_OFFSET_PULSES | ||
| # Comes from per-machine .ini parsing; reject non-int so misconfiguration fails | ||
| # fast here instead of deep in the signed Modbus write (bool is an int subclass). | ||
| if isinstance(offset, bool) or not isinstance(offset, int): | ||
| raise ValueError(f"OBJECTIVE_TURRET_OFFSET_PULSES must be an integer number of pulses, got {offset!r}") | ||
| self._offset_pulses = offset |
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.
Problem
Normally the objective turret's homing limit switch coincides with slot 1: after
home()seeks the switch and zeroes the position counter there, every objective lands at the correct angle. On one unit the switch does not sit exactly at slot 1, so all four objectives are rotated by the same small constant.Change
Adds a per-machine config knob
OBJECTIVE_TURRET_OFFSET_PULSES(default0, may be negative) — a single global offset added to every slot target in_rotate_to, shifting the whole turret frame uniformly relative to the homing zero:Set it per machine in that unit's
configuration*.ini:Every other machine keeps
0→ unchanged behavior.Why software, not the hardware homing register
The NiMotion's own
REG_HOMING_OFFSET(0x0069) can't carry this correction:home()unconditionally issuesSET_ZEROafter the limit-switch seek, re-zeroing the counter at the physical stop — cancelling any homing-register offset._HOMING_PARAMSactively forcesREG_HOMING_OFFSETback to0on every connect (and saves to EEPROM)._rotate_tois the single place a slot index becomes an absolute pulse target, so it's the right seam to apply the correction.home()is untouched — the counter still zeros at the switch.Naming
Named
OBJECTIVE_TURRET_OFFSET_PULSES(notHOME_OFFSETorSLOT1_OFFSET) because it's a single global offset applied uniformly to all slots, acting at move time rather than home time — and to avoid colliding with the file's hardware-homing terms (HOMING_ORIGIN_OFFSET,REG_HOMING_OFFSET) that this value deliberately does not touch.Validation
The value is parsed from the machine
.ini(viajson.loads), so it can come back as a float/string/bool. The controller validates the resolved offset is anintat init and raisesValueErrorotherwise, so misconfiguration fails fast with a clear message rather than deep in the signed Modbus write.Plumbing
Threaded through the controller constructor mirroring the existing
positionsfallback, wired frommicroscope.py's sharedturret_kwargs. The simulation twin accepts the kwarg for constructor parity (it tracks objectives by name and never computes pulses).Tests
tests/control/test_objective_turret_controller.py(29 pass, black clean):(N−1)·pulses_per_position37.5,"30",True) → raisesValueErrorat init_deffallback when the kwarg isn't passed🤖 Generated with Claude Code