state_derivative held both constant for want of an inertia tensor. With one
on the spacecraft it now routes d(sigma)/dt from the MRP kinematics and
d(omega)/dt from Euler's equation, and a torque with nowhere to go trips a
debug_assert instead of being dropped.
Modified Rodrigues Parameters carry attitude in three numbers with no norm
for an integrator to drift off, at the cost of a singularity at a full turn.
shadow_set avoids it; its documentation says the switch is a discontinuity
and belongs in a callback rather than in the derivative.
Context caches the inverse inertia, since a 3x3 inverse per derivative
evaluation would be comparable to the whole two-body derivative. The field
is private and derived so it cannot fall out of step with the spacecraft.
Torque-free motion conserves rotational energy and angular momentum to 1e-10
over two thousand steps, which checks the equation independently of the
formula it was written from.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01M9aqCoSmjgoaTbewDHAZhh
SurfaceModel is an enum, not a trait: the areas have to be produced in the
propagated scalar, so the methods would be generic, and a trait with a
generic method is not object-safe. Cannonball ignores direction; Faceted
sums the faces turned toward the flow, which is what makes an
attitude-dependent area expressible.
Schedule gives a piecewise-constant area over epochs. Its documentation says
plainly that an adaptive integrator cannot step across the discontinuity a
change creates, so a change belongs on a propagation boundary or a callback.
Context carries an optional Spacecraft, owned rather than borrowed to keep a
lifetime off every signature. A force now divides by the state's mass when
the layout has one and the vehicle's otherwise, which closes the gap left
when force and acceleration were split.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01M9aqCoSmjgoaTbewDHAZhh
Five conversions panicked on a condition the caller may have no control over:
elements from a non-inertial state, geodetic coordinates from a non-body-fixed
one, a look direction across frames, and equinoctial elements for a retrograde
orbit. Inclination and frame are data, so a catalog with a retrograde object or
a state read from a file would abort the process.
ConversionError covers the three cases, and reads as a failure of the value
rather than of the code. FrameError stays for a rotation that could not be
produced, which is a different thing.
The four should_panic tests became error assertions, which say more than they
did: the retrograde one now also checks that Keplerian elements still work,
since that is the fallback the error leaves open.
The debug_asserts in state_derivative are untouched. ordinary-diffeq's
right-hand side returns SVector and not Result, so a derivative cannot report
anything; moving those checks to assembly time is filed separately.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01M9aqCoSmjgoaTbewDHAZhh
Cartesian::to_frame rotates a state between orientations sharing a center,
velocity included: v' = R v + R_dot r. A test pins it against physics, since
a point fixed to the equator moves at omega*r inertially. ANISE's DCM is
f64, so the matrices are lifted into the propagated scalar; the rotation
depends on epoch and not state, so that is also the right derivative.
Geodetic converts to and from a body-fixed position and gives the local
east-north-up rotation. The height form is the one that survives the poles.
Adds pck11.pca, which reopens body-fixed frames for bodies other than Earth
and lets our body constants be checked against the kernel's. It is fetched
from storage.googleapis.com because public-data.nyxspace.com serves a
certificate that does not cover its own name.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01M9aqCoSmjgoaTbewDHAZhh