Coverage for core / src / sensorkit / std / mount.py: 100%
88 statements
« prev ^ index » next coverage.py v7.13.5, created at 2026-09-02 00:03 +0000
« prev ^ index » next coverage.py v7.13.5, created at 2026-09-02 00:03 +0000
1# SPDX-License-Identifier: Apache-2.0
2"""Standard mount commands, keywords, traits, and archetype."""
4import enum
5from dataclasses import dataclass
7from pydantic import BaseModel, Field
9import sensorkit.api as sk
10from sensorkit.astro.common import RADecPointing
11from sensorkit.astro.target import Target
12from sensorkit.std.traits import Deinit, Home, Init, MoveToPark, SetParkPosition, Stop
15class MountAxis(enum.StrEnum):
16 """Identifies a physical axis of a telescope mount."""
18 ALTITUDE = "altitude"
19 AZIMUTH = "azimuth"
20 RIGHT_ASCENSION = "right_ascension"
21 DECLINATION = "declination"
24class EnableAxis(sk.DeviceCommand):
25 """Enable motion control for a mount axis."""
27 axis: MountAxis
30class DisableAxis(sk.DeviceCommand):
31 """Disable motion control for a mount axis."""
33 axis: MountAxis
36@sk.declare_keyword
37class AxisEnabled(BaseModel):
38 """Keyword reporting whether a specific mount axis is enabled."""
40 enabled: bool
41 axis: MountAxis
44@sk.declare_keyword
45class AxisRate(BaseModel):
46 """Keyword reporting position, velocity, and acceleration for a single mount axis."""
48 axis: MountAxis
50 mechanical_position: float | None = Field(None, description="Current Mechanical position (degrees)")
51 min_mechanical_position: float | None = Field(None, description="Maximum mechanical position (degrees)")
52 max_mechanical_position: float | None = Field(None, description="Maximum mechanical position (degrees)")
54 velocity: float | None = Field(None, description="Current Velocity (Degrees per Second)")
55 max_velocity: float | None = Field(None, description="Max Velocity (Degrees per Second)")
57 acceleration: float | None = Field(None, description="Current Acceleration (Degrees per Second ^2)")
58 max_acceleration: float | None = Field(None, description="Max Acceleration (Degrees per Second ^2)")
60 measured_current: float | None = Field(None, description="Measured current (Amps)")
63@sk.declare_keyword
64class AxisRates(BaseModel):
65 """Aggregated AxisRate data for all mount axes."""
67 azimuth: AxisRate | None = None
68 altitude: AxisRate | None = None
69 right_ascension: AxisRate | None = None
70 declination: AxisRate | None = None
73@sk.declare_keyword
74class AxisTargetDistance(BaseModel):
75 """Distance between current and target axis position, in arcseconds."""
77 distance_arcseconds: float
78 rms_error_arcseconds: float | None = None
79 axis: MountAxis
82@sk.declare_keyword
83class Slewing(BaseModel):
84 """Indicates whether a mount is slewing."""
86 is_slewing: bool
89@sk.declare_keyword
90class Tracking(BaseModel):
91 """Indicates whether a mount is tracking."""
93 is_tracking: bool
96@sk.declare_keyword
97class MountStatus:
98 """Mount status keyword."""
99 is_moving: bool
100 target_acquired: bool
101 target: Target
104@dataclass
105class AltAzArcseconds:
106 """An angular offset expressed in altitude and azimuth arcseconds."""
108 azimuth_arcseconds: float
109 altitude_arcseconds: float
112@dataclass
113class RADecArcseconds:
114 """An angular offset expressed in right-ascension and declination arcseconds."""
116 right_ascension_arcseconds: float
117 declination_arcseconds: float
120class ApplyOffset(sk.DeviceCommand):
121 """Apply an angular offset to the mount's current pointing."""
123 offset: AltAzArcseconds | RADecArcseconds
126class FollowTarget(sk.DeviceCommand):
127 """Slew to a target and continuously track it."""
129 target: Target
132@sk.declare_keyword
133class AzimuthWrapRange(BaseModel):
134 """Keyword reporting the configured azimuth wrap range for the mount."""
136 min: float
137 max: float | None = None
140class SetAzimuthWrapRangeMin(sk.DeviceCommand):
141 """Set the minimum azimuth of the mount's wrap range."""
143 min: float
146class ModelAddPoint(sk.DeviceCommand):
147 """Add a calibration point to the mount's pointing model."""
149 point: RADecPointing
152class ModelDeletePoint(sk.DeviceCommand):
153 """Delete pointing-model points by index."""
155 indexes: list[int]
158class ModelEnablePoint(sk.DeviceCommand):
159 """Enable pointing-model points by index."""
161 indexes: list[int]
164class ModelDisablePoint(sk.DeviceCommand):
165 """Disable pointing-model points by index."""
167 indexes: list[int]
170class ModelClearPoints(sk.DeviceCommand):
171 """Remove all points from the mount's pointing model."""
174class ModelSave(sk.DeviceCommand):
175 """Save the mount's pointing model to a named file."""
177 filename: str
180class ModelLoad(sk.DeviceCommand):
181 """Load a mount pointing model from a named file."""
183 filename: str
186HasPointingModel = sk.declare_trait(
187 "HasPointingModel",
188 required_commands=(
189 ModelAddPoint,
190 ModelDeletePoint,
191 ModelEnablePoint,
192 ModelDisablePoint,
193 ModelClearPoints,
194 ModelSave,
195 ModelLoad,
196 ),
197)
198"""A mount that maintains a persistent pointing model."""
201StandardMount = sk.declare_archetype(
202 "mount",
203 required_commands=(
204 Init,
205 Deinit,
206 MoveToPark,
207 SetParkPosition,
208 Stop,
209 Home,
210 FollowTarget,
211 ),
212 optional_commands=(
213 EnableAxis,
214 DisableAxis,
215 ApplyOffset,
216 ),
217)
218"""A telescope mount that can follow targets and park."""