SpacecraftDynamics
このコンテンツはまだ日本語訳がありません。
Defined in: dynamics.rs:33
pub struct SpacecraftDynamics\<G: [GravityField](../../traits/gravityfield/), F: [Eci](../../../../arika/api/traits/eci/) = [arika::frame::SimpleEci](../../../../arika/api/type-aliases/simpleeci/)\>Coupled orbit-attitude dynamics for a rigid spacecraft.
Composes a gravitational field, inertia tensor, external load models,
and state effectors (e.g. reaction wheels) into a DynamicalSystem
for the augmented spacecraft state.
Parameterized by the inertial frame F (default SimpleEci),
matching SpacecraftState<F>.
The state type is AugmentedState<SpacecraftState<F>> — the 14D plant
state (orbit 6D + attitude 7D + mass 1D) plus concatenated auxiliary
variables from registered StateEffectors (e.g. RW angular
momentum). When no effectors are registered, aux is empty and the
dynamics are equivalent to the pre-effector version.
Equations of motion:
- Translation: dr/dt = v, dv/dt = a_gravity + Σ a_loads
- Rotation: dq/dt = ½ q ⊗ (0,ω), dω/dt = I⁻¹(τ − ω × Iω)
- Auxiliary: daux/dt from registered effectors
Methods
Section titled “Methods”fn new(mu: f64, gravity: G, inertia: Matrix3<f64>) -> Self
Create with gravitational parameter, gravity model, and inertia tensor.
Panics
Section titled “Panics”Panics if inertia is singular (not invertible).
with_model()
Section titled “with_model()”fn with_model(self, model: impl [Model<SpacecraftState<F>>](../../traits/model/) + ‘static) -> Self
Add an external model (builder pattern).
with_propellant()
Section titled “with_propellant()”fn with_propellant(self, pool: PropellantPool) -> Self
The propellant this spacecraft carries, as a floor under its mass.
One pool per spacecraft: every model registered with
with_propulsion(Self::with_propulsion) draws from it, and what is
left is the mass the state carries above the floor.
The pool is registered as an effector, which is how running dry becomes a boundary the propagation locates: it carries no auxiliary state of its own — the mass is the plant’s — but it carries the mode that says whether there is anything left to burn, and it declares the floor as a boundary.
Panics
Section titled “Panics”Panics if a pool is already registered. Two pools would be two floors, which is what this exists to prevent.
with_propulsion()
Section titled “with_propulsion()”fn with_propulsion(self, model: impl [Model<SpacecraftState<F>>](../../traits/model/) + ‘static) -> Self
Add a model that burns propellant (builder pattern).
Its loads are evaluated exactly as with_model(Self::with_model)‘s
are, until the pool is empty — then this one is not asked at all, and
neither is any other that draws on the same propellant. A propulsion
model registered through with_model would keep thrusting on an empty
tank.
What “empty” needs
Section titled “What “empty” needs”The pool’s mode is what stops the burn, and only a propagation that
handles boundaries moves it: a group, the CLI’s controlled path, or
walk_to_target(crate::boundary::walk_to_target) directly. Stepping
this system through Integrator(utsuroi::Integrator) yourself runs no
search and settles nothing, so the mode stays
Free(crate::effector::ConstraintMode::Free) and the burn continues
below the floor — the same limitation the momentum limit of
RwAssembly(crate::spacecraft::RwAssembly) has.
Panics
Section titled “Panics”Panics without a pool to draw from: the floor is what says when the spacecraft is empty, and thrust with no floor has no end.
pool()
Section titled “pool()”fn pool(&self) -> [Option<PropellantPool>](https://doc.rust-lang.org/std/option/enum.Option.html)
The propellant pool, if this spacecraft carries any.
declared_aux_bounds()
Section titled “declared_aux_bounds()”fn declared_aux_bounds(&self) -> Vec<(f64, f64)>
The projection bounds every registered effector declared, concatenated in registration order.
What initial_augmented_state(Self::initial_augmented_state) puts in
a new state, and what the start-state check measures a handed-over
state against.
with_effector()
Section titled “with_effector()”fn with_effector(self, effector: impl [StateEffector<SpacecraftState<F>>](../../traits/stateeffector/) + ‘static) -> Self
Add a state effector (builder pattern).
A PropellantPool registered here is registered as the pool, exactly
as with_propellant(Self::with_propellant) would: it declares a
boundary and carries the mode the propulsion is gated on, and a pool
the system did not know about would declare a floor that stops nothing.
Effectors have auxiliary state (e.g. RW angular momentum) that
is integrated alongside the plant state. The effector must produce
its loads in this system’s inertial frame F (its
StateEffector::derivatives returns ExternalLoads<F>), so the
dynamics never re-tag coordinates. See issue #103.
with_epoch()
Section titled “with_epoch()”fn with_epoch(self, epoch: Epoch) -> Self
Set the initial epoch corresponding to integration time t = 0.
with_body_radius()
Section titled “with_body_radius()”fn with_body_radius(self, radius: f64) -> Self
Set the central body radius for event detection.
initial_augmented_state()
Section titled “initial_augmented_state()”fn initial_augmented_state(&self, plant: SpacecraftState<F>) -> [AugmentedState<SpacecraftState<F>>](../augmentedstate/)
Create an initial augmented state with the given plant state.
Auxiliary state is initialized to zeros; bounds are collected from all registered effectors.
effector()
Section titled “effector()”fn effector<T: [StateEffector<SpacecraftState<F>>](../../traits/stateeffector/) + ‘static>(&self, index: usize) -> Option<&T>
Downcast a state effector by index (immutable).
effector_mut()
Section titled “effector_mut()”fn effector_mut<T: [StateEffector<SpacecraftState<F>>](../../traits/stateeffector/) + ‘static>(&mut self, index: usize) -> Option<&mut T>
Downcast a state effector by index (mutable).
effector_by_name()
Section titled “effector_by_name()”fn effector_by_name<T: [StateEffector<SpacecraftState<F>>](../../traits/stateeffector/) + ‘static>(&self, name: &str) -> Option<&T>
Find and downcast a state effector by name (immutable).
effector_by_name_mut()
Section titled “effector_by_name_mut()”fn effector_by_name_mut<T: [StateEffector<SpacecraftState<F>>](../../traits/stateeffector/) + ‘static>(&mut self, name: &str) -> Option<&mut T>
Find and downcast a state effector by name (mutable).
registry()
Section titled “registry()”fn registry(&self) -> &AuxRegistry
Get the auxiliary state registry.
inertia()
Section titled “inertia()”fn inertia(&self) -> &Matrix3<f64>
Get the inertia tensor.
body_radius()
Section titled “body_radius()”fn body_radius(&self) -> Option<f64>
Get the central body radius (if set).
replace_model()
Section titled “replace_model()”fn replace_model(&mut self, name: &str, new_model: [Box<dyn [Model<SpacecraftState<F>>](../../traits/model/)>](https://doc.rust-lang.org/std/boxed/struct.Box.html)) -> [Option<[Box<dyn [Model<SpacecraftState<F>>](../../traits/model/)>](https://doc.rust-lang.org/std/boxed/struct.Box.html)>](https://doc.rust-lang.org/std/option/enum.Option.html)
Replace a model by name, returning the old one (if found).
This is used to swap in a model with updated commanded state
between integration segments (e.g., MtqAssembly with a new
command).
A propulsion model is replaced where it was registered, so a rebuilt thruster assembly stays propulsion and the pool it draws from is untouched: the propellant is the spacecraft’s, and a command does not refill it.
model_names()
Section titled “model_names()”fn model_names(&self) -> Vec<&str>
Names of the registered models, propulsion last.
Every one of them, whether it acts at a given state or not: this takes no state, so it cannot say whether a tank still has propellant. The breakdowns do — they skip evaluating a propulsion model the pool has switched off, and keep its entry as a zero, so a record has one column per name here whatever the tank holds.
model_breakdown()
Section titled “model_breakdown()”fn model_breakdown(&self, t: f64, state: &[AugmentedState<SpacecraftState<F>>](../augmentedstate/)) -> [Vec<(&str, ExternalLoads<F>)>](https://doc.rust-lang.org/std/vec/struct.Vec.html)
Per-model load breakdown at the given state.
The augmented state rather than the plant alone, because whether the
propulsion burns is the pool’s mode — the same value the right-hand
side gates on. Reading the mass here instead would make a record
disagree with the trajectory on a path that settles no boundaries: the
mode stays Free and the thrust keeps being integrated, while a
mass-based comparison would report none.
A state whose mass is not positive gets the same treatment it gets in the right-hand side, for the same reason: the acceleration a model reports there is an infinity, and a record of an infinity says nothing the trajectory did.
Every registered model gets an entry, in the order
model_names(Self::model_names) lists them. A propulsion model the
pool has switched off is not evaluated — that is the point of the mode —
but its entry is there, a zero: the telemetry that reads this builds one
column per model, and a row that drops an entry after depletion leaves
that column sparse where a reader wants to see the thrust go to zero.
Panics
Section titled “Panics”If the state’s mode vector is not the length the registry says (see
check_modes(Self::check_modes)).
torque_breakdown()
Section titled “torque_breakdown()”fn torque_breakdown(&self, t: f64, state: &[AugmentedState<SpacecraftState<F>>](../augmentedstate/)) -> [Vec<(&str, [arika::frame::Vec3<arika::frame::Body>](../../../../arika/api/structs/vec3/))>](https://doc.rust-lang.org/std/vec/struct.Vec.html)
Per-model disturbance torque in the body frame [N·m], for telemetry.
The vector rather than a magnitude, because a magnitude carries neither the sign nor the axis, and those are what a torque is read for: an attitude disturbance that turns the wrong way looks the same size as one that turns the right way.
The gravity field is absent. It acts on the centre of mass, so it exerts
no torque about it; a gravity-gradient torque is a
Model(crate::model::Model) and answers under its own name. This is
why the list is one entry per model, where
acceleration_breakdown(Self::acceleration_breakdown) leads with
"gravity".
Every model answers, not only the environmental ones. A magnetorquer and
a thruster assembly are Models and are installed as such, so their
commanded torques appear here under their own names. A reaction wheel is
a StateEffector — it carries its own state — and is absent; its
momentum is read from the effector.
load_breakdown()
Section titled “load_breakdown()”fn load_breakdown(&self, t: f64, state: &[AugmentedState<SpacecraftState<F>>](../augmentedstate/)) -> LoadBreakdown<’_>
Both breakdowns from one evaluation of every model.
ExternalLoads carries the acceleration and the torque together, so a
caller that wants both — telemetry reporting one sample — has no reason
to evaluate every model twice. The panel models make that visible: the
shadow geometry is most of their cost, and a 22-panel spacecraft is
50 µs an evaluation.
The two halves are what
acceleration_breakdown(Self::acceleration_breakdown) and
torque_breakdown(Self::torque_breakdown) answer. Each of those
projects from model_breakdown on its own, so a caller wanting one half
does not build the other; this exists for the caller wanting both.
acceleration_breakdown()
Section titled “acceleration_breakdown()”fn acceleration_breakdown(&self, t: f64, state: &[AugmentedState<SpacecraftState<F>>](../augmentedstate/)) -> Vec<(&str, f64)>
Acceleration breakdown for telemetry.