ADCS.helpers.simresults module

class ADCS.helpers.simresults.Any(*args, **kwargs)[source]

Bases: object

Special type indicating an unconstrained type.

  • Any is compatible with every type.

  • Any assumed to have all methods.

  • All values assumed to be instances of Any.

Note that all the above statements are true from the point of view of static type checkers. At runtime, Any should not be used with instance checks.

class ADCS.helpers.simresults.EstimatedSatellite(mass=1.0, COM=None, J_0=None, disturbances=[], sensors=[], actuators=[], boresight=None)[source]

Bases: Satellite

Satellite model with estimator-synchronized biases and disturbance parameters.

EstimatedSatellite extends Satellite by tracking additional estimator-driven quantities:

  • actuator bias states (for actuators with estimate_bias=True),

  • attitude sensor bias states (for sensors with estimate_bias=True),

  • disturbance parameter states (for disturbances with estimate_dist=True).

These extra states typically live in an estimator state vector, while the physical simulation maintains internal subsystem objects. This class provides a synchronization bridge between them via match_estimate().

Augmented estimator state layout

The estimator is assumed to output a stacked vector

\[\begin{split}\mathbf{x}_{\mathrm{est}} \;=\; \begin{bmatrix} \mathbf{x} \\ \mathbf{b}_{act} \\ \mathbf{b}_{sens} \\ \boldsymbol{\theta}_{dist} \end{bmatrix},\end{split}\]

where

  • \(\mathbf{x}\in\mathbb{R}^{n_x}\) is the base satellite state used by Satellite,

  • \(\mathbf{b}_{act}\in\mathbb{R}^{n_{ab}}\) concatenates actuator bias parameters,

  • \(\mathbf{b}_{sens}\in\mathbb{R}^{n_{sb}}\) concatenates sensor bias parameters,

  • \(\boldsymbol{\theta}_{dist}\in\mathbb{R}^{n_{dp}}\) concatenates disturbance parameters.

The total expected estimator dimension is:

\[n_{\mathrm{tot}} \;=\; n_x + n_{ab} + n_{sb} + n_{dp}.\]

Bias and parameter covariance mapping

The estimator provides a covariance \(\mathbf{P}\) and an integrated covariance \(\int \mathbf{P}\,dt\) (stored as int_cov). match_estimate() extracts the diagonal blocks corresponding to each bias/parameter group and maps them to per-subsystem standard deviations (square roots of block diagonals).

Note

During matching, noise is disabled in actuators/sensors and time variation is disabled in disturbances to ensure deterministic consistency when comparing propagation to estimation.

Raises:

ValueError – If an estimator state vector with incompatible dimension is provided to match_estimate().

Parameters:
  • mass (float)

  • COM (ndarray)

  • J_0 (ndarray)

  • disturbances (List[Disturbance])

  • sensors (List[Sensor])

  • actuators (List[Actuator])

  • boresight (dict[str, ndarray] | ndarray)

classmethod from_satellite(sat)[source]

Create an EstimatedSatellite by cloning a Satellite.

Parameters:

sat (Satellite)

Return type:

EstimatedSatellite

control_cov()[source]

Block-diagonal covariance of actuator input noise.

For each actuator \(j\), let its input noise covariance be \(\mathbf{R}_{u_j}\) returned by actuator.noise.cov(). This method constructs:

\[\mathbf{R}_u = \mathrm{blkdiag}\left(\mathbf{R}_{u_1}, \mathbf{R}_{u_2}, \dots, \mathbf{R}_{u_{N_u}}\right).\]

This covariance is commonly used as the control-noise covariance in stochastic propagation or estimator process-noise modeling.

Returns:

Block-diagonal actuator noise covariance matrix.

Return type:

numpy.ndarray

control_srcov()[source]

Block-diagonal square-root covariance of actuator input noise.

For each actuator \(j\), let its square-root covariance be \(\mathbf{S}_{u_j}\) returned by actuator.noise.srcov() such that:

\[\mathbf{R}_{u_j} = \mathbf{S}_{u_j}\mathbf{S}_{u_j}^\top.\]

This method constructs:

\[\mathbf{S}_u = \mathrm{blkdiag}\left(\mathbf{S}_{u_1}, \mathbf{S}_{u_2}, \dots, \mathbf{S}_{u_{N_u}}\right).\]
Returns:

Block-diagonal actuator noise square-root covariance matrix.

Return type:

numpy.ndarray

dist_torque_hess(x, vecs)[source]

Hessians of total disturbance torque w.r.t. state and disturbance parameters.

This overrides dist_torque_hess() by populating parameter–parameter and quaternion–parameter second derivatives for disturbances with estimated parameters.

Let \(\boldsymbol{\tau}_d(\mathbf{x},\boldsymbol{\theta}_d)\) be the total disturbance torque. This method returns:

\[\mathbf{H}_{xx} = \frac{\partial^2 \boldsymbol{\tau}_d}{\partial \mathbf{x}^2}, \qquad \mathbf{H}_{x\theta} = \frac{\partial^2 \boldsymbol{\tau}_d}{\partial \mathbf{x}\,\partial \boldsymbol{\theta}_d}, \qquad \mathbf{H}_{\theta\theta} = \frac{\partial^2 \boldsymbol{\tau}_d}{\partial \boldsymbol{\theta}_d^2}.\]

State Hessian sparsity

As in the base class, only quaternion components contribute to state second derivatives:

\[\mathbf{H}_{xx}[3:7,3:7,:] = \sum_i \frac{\partial^2 \boldsymbol{\tau}_{d,i}}{\partial \mathbf{q}^2}.\]

Parameter blocks

For each estimated disturbance \(i\) with parameter vector size \(\ell_i\), the method places:

\[\mathbf{H}_{\theta\theta}[k:k+\ell_i,\,k:k+\ell_i,:] = \frac{\partial^2 \boldsymbol{\tau}_{d,i}}{\partial \boldsymbol{\theta}_{d,i}^2},\]

and

\[\mathbf{H}_{x\theta}[3:7,\,k:k+\ell_i,:] = \frac{\partial^2 \boldsymbol{\tau}_{d,i}}{\partial \mathbf{q}\,\partial \boldsymbol{\theta}_{d,i}}.\]

Each estimated disturbance is expected to implement:

  • torque_qqhess(self, vecs)\(\partial^2 \boldsymbol{\tau}/\partial\mathbf{q}^2\),

  • torque_qvalhess(self, vecs)\(\partial^2 \boldsymbol{\tau}/\partial\mathbf{q}\partial\boldsymbol{\theta}\),

  • torque_valvalhess(self, vecs)\(\partial^2 \boldsymbol{\tau}/\partial\boldsymbol{\theta}^2\).

Parameters:
  • x (numpy.ndarray) – Current base spacecraft state vector, shape (state_len,).

  • vecs (dict[str, numpy.ndarray]) – Dictionary containing body-frame environment vectors and their derivatives.

Returns:

Tuple (dddist_torq__dxdx, dddist_torq__dxddmp, dddist_torq__ddmpddmp).

Return type:

tuple[numpy.ndarray, numpy.ndarray, numpy.ndarray]

Return shapes

Name

Shape

dddist_torq__dxdx

(state_len, state_len, 3)

dddist_torq__dxddmp

(state_len, dist_param_len, 3)

dddist_torq__ddmpddmp

(dist_param_len, dist_param_len, 3)

dist_torques_jacobian(x, vecs)[source]

Jacobians of total disturbance torque w.r.t. state and disturbance parameters.

This overrides dist_torques_jacobian() by additionally returning the derivative of the total disturbance torque w.r.t. estimated disturbance parameters.

Let the total disturbance torque be

\[\boldsymbol{\tau}_d(\mathbf{x},\boldsymbol{\theta}_d) = \sum_{i=1}^{N_d}\boldsymbol{\tau}_{d,i}(\mathbf{x},\boldsymbol{\theta}_{d,i}).\]

The method returns:

\[\mathbf{J}_x = \frac{\partial \boldsymbol{\tau}_d}{\partial \mathbf{x}}, \qquad \mathbf{J}_{\theta} = \frac{\partial \boldsymbol{\tau}_d}{\partial \boldsymbol{\theta}_d}.\]

State Jacobian structure

Only attitude (quaternion) affects rotated environment vectors and thus torque for the provided disturbance interface; therefore:

\[\mathbf{J}_x[3:7,:] = \sum_i \frac{\partial \boldsymbol{\tau}_{d,i}}{\partial \mathbf{q}},\qquad \mathbf{J}_x[k,:]=0\;\text{for}\;k\notin\{3,4,5,6\}.\]

Parameter Jacobian assembly

If self.dist_param_len > 0, the parameter Jacobian is formed by vertically stacking each estimated disturbance’s parameter Jacobian:

\[\begin{split}\mathbf{J}_{\theta} = \begin{bmatrix} \frac{\partial \boldsymbol{\tau}_{d,i_1}}{\partial \boldsymbol{\theta}_{d,i_1}} \\ \vdots \\ \frac{\partial \boldsymbol{\tau}_{d,i_m}}{\partial \boldsymbol{\theta}_{d,i_m}} \end{bmatrix}.\end{split}\]

Each estimated disturbance is expected to implement:

  • torque_qjac(self, vecs)\(\partial \boldsymbol{\tau}/\partial\mathbf{q}\),

  • torque_valjac(self, vecs)\(\partial \boldsymbol{\tau}/\partial\boldsymbol{\theta}\).

Parameters:
  • x (numpy.ndarray) – Current base spacecraft state vector, shape (state_len,).

  • vecs (dict[str, numpy.ndarray]) – Dictionary containing body-frame environment vectors and their quaternion derivatives, typically including keys like "b", "r", "s", "v", "rho", and derivative keys like "db", "dr", "ds", "dv".

Returns:

Tuple (ddist_torq__dx, ddist_torq__ddmp).

Return type:

tuple[numpy.ndarray, numpy.ndarray]

Return shapes

Name

Shape

ddist_torq__dx

(state_len, 3)

ddist_torq__ddmp | (dist_param_len, 3)

dynJacCore(x, u, orbital_state)[source]

Jacobians of augmented dynamics w.r.t. state, inputs, and estimated parameters.

This method extends the base linearization from dynJacCore() by additionally returning Jacobians of \(\dot{\mathbf{x}}\) w.r.t.:

  • actuator bias vector \(\mathbf{b}_{act}\),

  • sensor bias vector \(\mathbf{b}_{sens}\) (typically zeros for the process model),

  • disturbance parameter vector \(\boldsymbol{\theta}_{dist}\).

The local first-order model is:

\[\delta\dot{\mathbf{x}} \approx \mathbf{A}\,\delta\mathbf{x} + \mathbf{B}\,\delta\mathbf{u} + \mathbf{G}_{ab}\,\delta\mathbf{b}_{act} + \mathbf{G}_{sb}\,\delta\mathbf{b}_{sens} + \mathbf{G}_{dp}\,\delta\boldsymbol{\theta}_{dist}.\]

Returned blocks

The method returns a list:

\[\left[ \frac{\partial \dot{\mathbf{x}}}{\partial \mathbf{x}}, \frac{\partial \dot{\mathbf{x}}}{\partial \mathbf{u}}, \frac{\partial \dot{\mathbf{x}}}{\partial \mathbf{b}_{act}}, \frac{\partial \dot{\mathbf{x}}}{\partial \mathbf{b}_{sens}}, \frac{\partial \dot{\mathbf{x}}}{\partial \boldsymbol{\theta}_{dist}} \right].\]

Where the last three blocks are assembled via actuator- and disturbance-provided derivatives:

  • actuators implement dtorq__dbias and (if wheels) storage-torque bias derivatives,

  • disturbances provide parameter Jacobians via dist_torques_jacobian().

Note

Sensor bias Jacobians are typically zero in the process model because sensor biases affect measurements, not rotational dynamics; therefore dxdot__dsb is returned as zeros.

Parameters:
  • x (numpy.ndarray) – Current base state vector, shape (state_len,).

  • u (numpy.ndarray) – Current control vector, shape (control_len,).

  • orbital_state (Orbital_State) – Orbital/environmental state supplying ECI vectors and scalars used by torque models.

Returns:

List [dxdot__dx, dxdot__du, dxdot__dab, dxdot__dsb, dxdot__ddmp].

Return type:

list[numpy.ndarray]

Return shapes

Name

Shape

dxdot__dx

(state_len, state_len)

dxdot__du

(control_len, state_len)

dxdot__dab

(act_bias_len, state_len)

dxdot__dsb

(att_sens_bias_len, state_len)

dxdot__ddmp

(dist_param_len, state_len)

dynamics_Hessians(x, u, orbital_state)[source]

Second-order derivatives (Hessians) of augmented dynamics.

This method computes second derivatives of the base rotational dynamics and extends them to include curvature terms w.r.t. actuator biases and disturbance parameters. The Hessians are organized as a block matrix over the stacked variable vector

\[\begin{split}\mathbf{z} = \begin{bmatrix} \mathbf{x} \\ \mathbf{u} \\ \mathbf{b}_{act} \\ \mathbf{b}_{sens} \\ \boldsymbol{\theta}_{dist} \end{bmatrix}.\end{split}\]

For each state-derivative component \(\dot{x}_k\), the Hessian stores

\[\frac{\partial^2 \dot{x}_k}{\partial z_i\,\partial z_j}.\]

Returned structure

If reaction wheels are present (and extended estimation terms are active), the method returns:

[
  [ddxdot__dxdx,  ddxdot__dxdu,  ddxdot__dxdab, ddxdot__dxdsb, ddxdot__dxddmp],
  [ddxdot__dxdu.T, ddxdot__dudu, ddxdot__dudab, ddxdot__dudsb, ddxdot__duddmp],
  [0,              0,           ddxdot__dabdab, ddxdot__dabdsb, ddxdot__dabddmp],
  [0,              0,           0,             ddxdot__dsbdsb,  ddxdot__dsbddmp],
  [0,              0,           0,             0,              ddxdot__ddmpddmp]
]

If extended estimation terms are not used, it falls back to the base 2×2 structure:

[
  [ddxdot__dxdx, ddxdot__dxdu],
  [ddxdot__dxdu.T, ddxdot__dudu]
]

Key analytic contributions

  • Quaternion kinematics curvature terms from \(\dot{\mathbf{q}} = \tfrac12 W(\mathbf{q})^\top\boldsymbol{\omega}\).

  • Rigid-body dynamics curvature from the gyroscopic term \(-\boldsymbol{\omega}\times(\mathbf{J}\boldsymbol{\omega} + \mathbf{H}_{RW})\).

  • Disturbance torque curvature terms from dist_torque_hess().

  • Actuator torque curvature terms from actuator-provided second derivatives, including bias couplings.

Parameters:
  • x (numpy.ndarray) – Current base state vector, shape (state_len,).

  • u (numpy.ndarray) – Current control vector, shape (control_len,).

  • orbital_state (Orbital_State) – Orbital/environmental state providing ECI vectors and scalars for torque models.

Returns:

Nested list of Hessian tensors (see structure above).

Return type:

list[list[numpy.ndarray]]

Common tensor shapes (extended case)

Tensor

Shape

ddxdot__dxdx

(state_len, state_len, state_len)

ddxdot__dxdu

(state_len, control_len, state_len)

ddxdot__dudu

(control_len, control_len, state_len)

ddxdot__dxdab

(state_len, act_bias_len, state_len)

ddxdot__dxdsb

(state_len, att_sens_bias_len, state_len)

ddxdot__dxddmp

(state_len, dist_param_len, state_len)

ddxdot__dabdab

(act_bias_len, act_bias_len, state_len)

ddxdot__dsbdsb

(att_sens_bias_len, att_sens_bias_len, state_len)

ddxdot__ddmpddmp

(dist_param_len, dist_param_len, state_len)

match_estimate(est_state, dt)[source]

Synchronize the satellite instance with the estimator output.

This method maps the estimator output into the simulation model by:

  1. Extracting wheel momenta from the base state portion and updating wheel objects via update_RWhs().

  2. Writing actuator biases into each actuator’s bias object and setting per-bias standard deviations from the corresponding covariance block.

  3. Writing sensor biases into each attitude sensor’s bias object and setting per-bias standard deviations from the corresponding covariance block.

  4. Writing disturbance parameters into each disturbance model (for active disturbances) and setting their parameter standard deviations from covariance blocks.

  5. For deterministic matching, disabling: * actuator.use_noise = False for all actuators, * sensor.use_noise = False for all sensors, * disturbance.time_varying = False for all disturbances.

Estimator vector partition

With

\[\begin{split}\mathbf{x}_{\mathrm{est}} = \begin{bmatrix} \mathbf{x} \\ \mathbf{b}_{act} \\ \mathbf{b}_{sens} \\ \boldsymbol{\theta}_{dist} \end{bmatrix},\end{split}\]

indices are:

\[\begin{split}\begin{aligned} \mathbf{x} &\in \mathbb{R}^{n_x},\\ \mathbf{b}_{act} &\in \mathbb{R}^{n_{ab}},\\ \mathbf{b}_{sens} &\in \mathbb{R}^{n_{sb}},\\ \boldsymbol{\theta}_{dist} &\in \mathbb{R}^{n_{dp}}, \end{aligned}\end{split}\]

where

\[n_x=\texttt{self.state\_len},\; n_{ab}=\texttt{self.act\_bias\_len},\; n_{sb}=\texttt{self.att\_sens\_bias\_len},\; n_{dp}=\texttt{self.dist\_param\_len}.\]

Covariance-to-standard-deviation mapping

For each extracted block covariance \(\mathbf{P}_{block}\), per-element standard deviations are assigned as:

\[\boldsymbol{\sigma} = \sqrt{\operatorname{diag}(\mathbf{P}_{block})}.\]

In the implementation, the estimator provides an integrated covariance block (int_cov) that may be dimension-shifted by one element in some configurations (e.g., quaternion handling). The method applies an index adjustment adj when int_cov is off-by-one.

Parameters:
  • est_state (EstimatedArray) – Estimator output container providing val (state vector), cov (covariance), and int_cov (integrated covariance).

  • dt (float) – Estimator propagation step (s). Used by some pipelines to normalize integrated covariance. (The current implementation partitions int_cov directly.)

Returns:

None.

Return type:

None

Raises:

ValueError – If est_state.val does not have length self.state_len + self.act_bias_len + self.att_sens_bias_len + self.dist_param_len.

sensor_bias_slice(att_sensor_index)[source]

Slice for an attitude sensor’s bias block in the full estimator state vector.

The full estimator state is assumed to be ordered as:

\[\begin{split}\mathbf{x}_{\mathrm{est}} = \begin{bmatrix} \mathbf{x} \\ \mathbf{b}_{act} \\ \mathbf{b}_{sens} \\ \boldsymbol{\theta}_{dist} \end{bmatrix},\end{split}\]

where the sensor-bias block starts at offset:

\[k_0 = n_x + n_{ab} \;=\; \texttt{self.state\_len} + \texttt{self.act\_bias\_len}.\]

This method returns the Python slice(start, stop) selecting the bias elements for self.attitude_sensors[att_sensor_index] within \(\mathbf{x}_{\mathrm{est}}\). If the specified sensor does not have an estimated bias (i.e., not in att_sens_bias_inds), the method returns None.

Parameters:

att_sensor_index (int) – Index into self.attitude_sensors.

Returns:

Slice selecting the sensor’s bias portion in the estimator vector, or None.

Return type:

slice | None

Raises:

RuntimeError – If the sensor index is listed as bias-estimated but the slice cannot be constructed.

sensor_cov(which_sensors)[source]

Block-diagonal covariance of attitude sensor noise (and wheel momentum measurement noise).

For each attitude sensor \(i\) with covariance \(\mathbf{R}_{y_i}\) (from sensor.noise.cov()), the attitude-sensor covariance is:

\[\mathbf{R}_y = \mathrm{blkdiag}\left(\mathbf{R}_{y_{i_1}}, \dots, \mathbf{R}_{y_{i_m}}\right),\]

where the set \(\{i_1,\dots,i_m\}\) is selected by which_sensors.

If reaction wheels are present, each wheel contributes an additional measurement covariance for its momentum measurement model (e.g., rw.h_meas_noise.cov()), appended to the block diagonal.

Parameters:

which_sensors (list[bool]) – Boolean selector list of length len(self.attitude_sensors) indicating which attitude sensors to include. If None is passed in the implementation, all are included.

Returns:

Block-diagonal measurement covariance matrix. If no sensors are selected, returns a (0,0) array.

Return type:

numpy.ndarray

sensor_srcov(which_sensors)[source]

Block-diagonal square-root covariance of attitude sensor noise (and wheel momentum measurement noise).

For each attitude sensor \(i\) with square-root covariance \(\mathbf{S}_{y_i}\) (from sensor.noise.srcov()) such that \(\mathbf{R}_{y_i}=\mathbf{S}_{y_i}\mathbf{S}_{y_i}^\top\), this method constructs

\[\mathbf{S}_y = \mathrm{blkdiag}\left(\mathbf{S}_{y_{i_1}}, \dots, \mathbf{S}_{y_{i_m}}\right),\]

with selection controlled by which_sensors.

If reaction wheels are present, each wheel contributes an additional square-root covariance block for its momentum measurement model (e.g., rw.h_meas_noise.srcov()).

Parameters:

which_sensors (list[bool]) – Boolean selector list of length len(self.attitude_sensors) indicating which attitude sensors to include. If None is passed in the implementation, all are included.

Returns:

Block-diagonal measurement square-root covariance matrix. If no sensors are selected, returns a (0,0) array.

Return type:

numpy.ndarray

class ADCS.helpers.simresults.Orbital_State(ephem, J2000, R, V, S=None, B=None, rho=None, density_model=None, fast=False)[source]

Bases: object

Complete dynamical and environmental representation of a spacecraft orbital state.

The state is defined by inertial position and velocity in the ECI/ICRF frame at an epoch expressed in J2000 centuries. The class also stores commonly-used derived quantities and environment vectors, including Earth-fixed coordinates, Sun vector, geomagnetic field vector (IGRF), atmospheric density, and frame transforms.

Notes

The fast argument is accepted for backward compatibility but is ignored: this implementation always computes real Sun and geomagnetic field vectors and frame transforms.

Parameters:
  • ephem (Ephemeris) – Ephemeris object used for Earth and Sun position queries. If None, a new Ephemeris is constructed.

  • J2000 (float) – Epoch in Julian centuries since J2000.

  • R (numpy.ndarray) – Position vector in ECI frame [km].

  • V (numpy.ndarray) – Velocity vector in ECI frame [km/s].

  • S (numpy.ndarray or None) – Optional Sun position vector in ECI frame [km]. If None, it is computed.

  • B (numpy.ndarray or None) – Optional geomagnetic field vector in ECI frame [T]. If None, it is computed.

  • rho (float or None) – Optional atmospheric density [kg/m^3]. If None, it is computed using DensityModel.

  • density_model (DensityModel or None) – Atmospheric density interpolation model. If None, a new DensityModel is constructed.

  • fast (bool) – Backward-compatible parameter (ignored). Real environment vectors are always computed.

classmethod from_dict(d, ephem, density_model=None, fast=True)[source]

Construct an orbital state from a dictionary.

Notes

The fast argument is accepted for backward compatibility but is ignored.

Parameters:
  • d (dict) – Dictionary containing orbital state fields.

  • ephem (Ephemeris) – Ephemeris object.

  • density_model (DensityModel or None) – Atmospheric density model.

  • fast (bool) – Backward-compatible parameter (ignored).

Returns:

Reconstructed orbital state.

Return type:

Orbital_State

average(orbital_state_2, ratio=0.5, fast=False)[source]

Linearly interpolate between two orbital states.

Parameters:
  • orbital_state_2 (Orbital_State) – Second orbital state.

  • ratio (float) – Interpolation ratio in [0, 1].

  • fast (bool) – Backward-compatible parameter (ignored).

Returns:

Interpolated orbital state.

Return type:

Orbital_State

copy()[source]

Return a deep copy of the orbital state.

This implementation avoids recomputing environment vectors and frame transforms.

Returns:

Independent copy of the orbital state.

Return type:

Orbital_State

ecef_to_eci(vec)[source]

Transform a vector from ECEF to ECI coordinates.

Parameters:

vec (numpy.ndarray) – Vector in ECEF frame.

Returns:

Vector in ECI frame.

Return type:

numpy.ndarray

ecef_to_geocentric(vec)[source]

Transform an ECEF vector to local geocentric coordinates.

Parameters:

vec (numpy.ndarray) – Vector in ECEF frame.

Returns:

Vector in geocentric basis.

Return type:

numpy.ndarray

eci_to_ecef(vec)[source]

Transform a vector from ECI to ECEF coordinates.

Parameters:

vec (numpy.ndarray) – Vector in ECI frame.

Returns:

Vector in ECEF frame.

Return type:

numpy.ndarray

eci_to_enu(vec)[source]

Transform a vector from ECI to local ENU frame.

Parameters:

vec (numpy.ndarray) – Vector in ECI frame.

Returns:

Vector in ENU frame.

Return type:

numpy.ndarray

enu_to_eci(vec)[source]

Transform a vector from ENU to ECI frame.

Parameters:

vec (numpy.ndarray) – Vector in ENU frame.

Returns:

Vector in ECI frame.

Return type:

numpy.ndarray

geocentric_to_ecef(vec)[source]

Transform a geocentric vector to ECEF coordinates.

Parameters:

vec (numpy.ndarray) – Vector in geocentric basis.

Returns:

Vector in ECEF frame.

Return type:

numpy.ndarray

get_b_eci()[source]

Compute the geomagnetic field vector in the ECI frame.

Returns:

Magnetic field vector [Tesla].

Return type:

numpy.ndarray

get_state_vector(x)[source]

Retrieve cached or updated body-frame vectors.

Parameters:

x (numpy.ndarray or None) – Current spacecraft state vector.

Returns:

Dictionary of vectors and derivatives.

Return type:

dict[str, numpy.ndarray]

get_sun_eci()[source]

Compute the Sun position vector in the ECI frame.

Returns:

Sun vector in ECI coordinates [km].

Return type:

numpy.ndarray

is_sunlit()[source]

Determine whether the spacecraft is illuminated by the Sun.

Returns:

True if sunlit, False otherwise.

Return type:

bool

j2000_to_tai()[source]

Convert J2000 centuries to TAI Julian date.

Returns:

TAI Julian date.

Return type:

float

orbit_dynamics(J2_perturbation_on=True)[source]

Compute translational orbital dynamics at the current state.

Parameters:

J2_perturbation_on (bool) – Enable J2 perturbation.

Returns:

Time derivatives of position and velocity.

Return type:

tuple[numpy.ndarray, numpy.ndarray]

orbit_dynamics_jacobians(J2_perturbation_on=True)[source]

Compute Jacobians of the translational dynamics.

Parameters:

J2_perturbation_on (bool) – Enable J2 perturbation.

Returns:

Jacobian matrices of the dynamics.

Return type:

tuple[numpy.ndarray, numpy.ndarray, numpy.ndarray, numpy.ndarray]

propagate_jacobians(dt, J2_perturbation_on=True)[source]

Propagate state transition Jacobians using first-order integration.

Parameters:
  • dt (float) – Time step in seconds.

  • J2_perturbation_on (bool) – Enable J2 perturbation.

Returns:

State transition Jacobian blocks.

Return type:

tuple[numpy.ndarray, numpy.ndarray, numpy.ndarray, numpy.ndarray]

propagate_jacobians_rk4(dt, J2_perturbation_on=True)[source]

Propagate state transition Jacobians using RK4 integration.

Parameters:
  • dt (float) – Time step in seconds.

  • J2_perturbation_on (bool) – Enable J2 perturbation.

Returns:

State transition Jacobian blocks.

Return type:

tuple[numpy.ndarray, numpy.ndarray, numpy.ndarray, numpy.ndarray]

propagate_orbit(dt, J2_perturbation_on=True, fast=True)[source]

Propagate the orbital state forward using first-order integration.

Parameters:
  • dt (float) – Time step in seconds.

  • J2_perturbation_on (bool) – Enable J2 perturbation.

  • fast (bool) – Backward-compatible parameter (ignored).

Returns:

Propagated orbital state.

Return type:

Orbital_State

propagate_orbit_rk4(dt, J2_perturbation_on=True, fast=True)[source]

Propagate the orbital state using fourth-order Runge–Kutta integration.

Parameters:
  • dt (float) – Time step in seconds.

  • J2_perturbation_on (bool) – Enable J2 perturbation.

  • fast (bool) – Backward-compatible parameter (ignored).

Returns:

Propagated orbital state.

Return type:

Orbital_State

to_dict()[source]

Serialize the orbital state to a dictionary.

Returns:

Dictionary representation of the orbital state.

Return type:

dict

update_vecs(x)[source]

Update body-frame vectors and their derivatives from a state vector.

Parameters:

x (numpy.ndarray) – Full spacecraft state vector including attitude quaternion.

Returns:

None

Return type:

None

class ADCS.helpers.simresults.Path(*args, **kwargs)[source]

Bases: PurePath

PurePath subclass that can make system calls.

Path represents a filesystem path but unlike PurePath, also offers methods to do system calls on path objects. Depending on your system, instantiating a Path will return either a PosixPath or a WindowsPath object. You can also instantiate a PosixPath or WindowsPath directly, but cannot instantiate a WindowsPath on a POSIX system or vice versa.

classmethod cwd()[source]

Return a new path pointing to the current working directory.

classmethod home()[source]

Return a new path pointing to the user’s home directory (as returned by os.path.expanduser(‘~’)).

absolute()[source]

Return an absolute version of this path by prepending the current working directory. No normalization or symlink resolution is performed.

Use resolve() to get the canonical path to a file.

chmod(mode, *, follow_symlinks=True)[source]

Change the permissions of the path, like os.chmod().

exists(*, follow_symlinks=True)[source]

Whether this path exists.

This method normally follows symlinks; to check whether a symlink exists, add the argument follow_symlinks=False.

expanduser()[source]

Return a new path with expanded ~ and ~user constructs (as returned by os.path.expanduser)

glob(pattern, *, case_sensitive=None)[source]

Iterate over this subtree and yield all existing files (of any kind, including directories) matching the given relative pattern.

group()[source]

Return the group name of the file gid.

Make this path a hard link pointing to the same file as target.

Note the order of arguments (self, target) is the reverse of os.link’s.

is_block_device()[source]

Whether this path is a block device.

is_char_device()[source]

Whether this path is a character device.

is_dir()[source]

Whether this path is a directory.

is_fifo()[source]

Whether this path is a FIFO.

is_file()[source]

Whether this path is a regular file (also True for symlinks pointing to regular files).

is_junction()[source]

Whether this path is a junction.

is_mount()[source]

Check if this path is a mount point

is_socket()[source]

Whether this path is a socket.

Whether this path is a symbolic link.

iterdir()[source]

Yield path objects of the directory contents.

The children are yielded in arbitrary order, and the special entries ‘.’ and ‘..’ are not included.

lchmod(mode)[source]

Like chmod(), except if the path points to a symlink, the symlink’s permissions are changed, rather than its target’s.

lstat()[source]

Like stat(), except if the path points to a symlink, the symlink’s status information is returned, rather than its target’s.

mkdir(mode=511, parents=False, exist_ok=False)[source]

Create a new directory at this given path.

open(mode='r', buffering=-1, encoding=None, errors=None, newline=None)[source]

Open the file pointed to by this path and return a file object, as the built-in open() function does.

owner()[source]

Return the login name of the file owner.

read_bytes()[source]

Open the file in bytes mode, read it, and close the file.

read_text(encoding=None, errors=None)[source]

Open the file in text mode, read it, and close the file.

Return the path to which the symbolic link points.

rename(target)[source]

Rename this path to the target path.

The target path may be absolute or relative. Relative paths are interpreted relative to the current working directory, not the directory of the Path object.

Returns the new Path instance pointing to the target path.

replace(target)[source]

Rename this path to the target path, overwriting if that path exists.

The target path may be absolute or relative. Relative paths are interpreted relative to the current working directory, not the directory of the Path object.

Returns the new Path instance pointing to the target path.

resolve(strict=False)[source]

Make the path absolute, resolving all symlinks on the way and also normalizing it.

rglob(pattern, *, case_sensitive=None)[source]

Recursively yield all existing files (of any kind, including directories) matching the given relative pattern, anywhere in this subtree.

rmdir()[source]

Remove this directory. The directory must be empty.

samefile(other_path)[source]

Return whether other_path is the same or not as this file (as returned by os.path.samefile()).

stat(*, follow_symlinks=True)[source]

Return the result of the stat() system call on this path, like os.stat() does.

Make this path a symlink pointing to the target path. Note the order of arguments (link, target) is the reverse of os.symlink.

touch(mode=438, exist_ok=True)[source]

Create this file with the given access mode, if it doesn’t exist.

Remove this file or link. If the path is a directory, use rmdir() instead.

walk(top_down=True, on_error=None, follow_symlinks=False)[source]

Walk the directory tree from this directory, similar to os.walk().

write_bytes(data)[source]

Open the file in bytes mode, write to it, and close the file.

write_text(data, encoding=None, errors=None, newline=None)[source]

Open the file in text mode, write to it, and close the file.

class ADCS.helpers.simresults.RunResults(satellite: 'Satellite', est_satellite: 'Optional[EstimatedSatellite]' = None, time_J2000: 'Optional[np.ndarray]' = None, time_s: 'Optional[np.ndarray]' = None, os_hist: 'Optional[List[Orbital_State]]' = None, est_os_hist: 'Optional[List[Orbital_State]]' = None, os_cov_hist: 'Optional[List[np.ndarray]]' = None, state_hist: 'Optional[np.ndarray]' = None, est_state_hist: 'Optional[np.ndarray]' = None, state_cov_hist: 'Optional[List[np.ndarray]]' = None, sensor_bias: 'Optional[np.ndarray]' = None, est_sensor_bias: 'Optional[np.ndarray]' = None, actuator_bias: 'Optional[np.ndarray]' = None, est_actuator_bias: 'Optional[np.ndarray]' = None, target_hist: 'Optional[np.ndarray]' = None, w_target_hist: 'Optional[np.ndarray]' = None, boresight_hist: 'Optional[np.ndarray]' = None, clean_sensor_hist: 'Optional[np.ndarray]' = None, sensor_hist: 'Optional[np.ndarray]' = None, control_hist: 'Optional[np.ndarray]' = None, control_rpc_time_hist: 'Optional[np.ndarray]' = None, control_rpc_server_time_hist: 'Optional[np.ndarray]' = None, env_local_time_hist: 'Optional[np.ndarray]' = None, dynamics_time_hist: 'Optional[np.ndarray]' = None)[source]

Bases: object

Parameters:
  • satellite (Satellite)

  • est_satellite (EstimatedSatellite | None)

  • time_J2000 (ndarray | None)

  • time_s (ndarray | None)

  • os_hist (List[Orbital_State] | None)

  • est_os_hist (List[Orbital_State] | None)

  • os_cov_hist (List[ndarray] | None)

  • state_hist (ndarray | None)

  • est_state_hist (ndarray | None)

  • state_cov_hist (List[ndarray] | None)

  • sensor_bias (ndarray | None)

  • est_sensor_bias (ndarray | None)

  • actuator_bias (ndarray | None)

  • est_actuator_bias (ndarray | None)

  • target_hist (ndarray | None)

  • w_target_hist (ndarray | None)

  • boresight_hist (ndarray | None)

  • clean_sensor_hist (ndarray | None)

  • sensor_hist (ndarray | None)

  • control_hist (ndarray | None)

  • control_rpc_time_hist (ndarray | None)

  • control_rpc_server_time_hist (ndarray | None)

  • env_local_time_hist (ndarray | None)

  • dynamics_time_hist (ndarray | None)

classmethod inflate(data, ephem=None)[source]
Parameters:
  • data (Dict[str, Any])

  • ephem (Any)

Return type:

RunResults

flatten()[source]
Return type:

Dict[str, Any]

record(*, k, **kwargs)[source]
Parameters:

k (int)

actuator_bias: ndarray | None = None
boresight_hist: ndarray | None = None
clean_sensor_hist: ndarray | None = None
control_hist: ndarray | None = None
control_rpc_server_time_hist: ndarray | None = None
control_rpc_time_hist: ndarray | None = None
dynamics_time_hist: ndarray | None = None
env_local_time_hist: ndarray | None = None
est_actuator_bias: ndarray | None = None
est_os_hist: List[Orbital_State] | None = None
est_satellite: EstimatedSatellite | None = None
est_sensor_bias: ndarray | None = None
est_state_hist: ndarray | None = None
os_cov_hist: List[ndarray] | None = None
os_hist: List[Orbital_State] | None = None
satellite: Satellite
sensor_bias: ndarray | None = None
sensor_hist: ndarray | None = None
state_cov_hist: List[ndarray] | None = None
state_hist: ndarray | None = None
target_hist: ndarray | None = None
time_J2000: ndarray | None = None
time_s: ndarray | None = None
w_target_hist: ndarray | None = None
class ADCS.helpers.simresults.Satellite(mass=1.0, COM=None, J_0=None, disturbances=[], sensors=[], actuators=[], boresight=None)[source]

Bases: object

Rigid-body spacecraft model with sensors, actuators, and environmental disturbances.

This class bundles the spacecraft physical properties (mass, center of mass, inertia), a set of actuators (e.g., RW, MTQ), sensors (e.g., Gyro, MTM, SunSensor, GPS), and disturbance models (e.g., SRP_Disturbance).

The rotational state is represented by body angular velocity and a unit quaternion. Reaction wheel angular momentum states may be appended.

State definition

Let the full state be

\[\begin{split}\mathbf{x} \;=\; \begin{bmatrix} \boldsymbol{\omega} \\ \mathbf{q} \\ \mathbf{h}_{\mathrm{RW}} \end{bmatrix} \in \mathbb{R}^{7+n_{\mathrm{RW}}},\end{split}\]

where

  • \(\boldsymbol{\omega}\in\mathbb{R}^3\) is the body angular velocity (rad/s),

  • \(\mathbf{q}\in\mathbb{R}^4\) is a unit quaternion (Hamilton convention, body→ECI),

  • \(\mathbf{h}_{\mathrm{RW}}\in\mathbb{R}^{n_{\mathrm{RW}}}\) are wheel momenta.

Kinematics

Quaternion kinematics are

\[\dot{\mathbf{q}} = \frac{1}{2}\,\Omega(\boldsymbol{\omega})\,\mathbf{q},\]

with an equivalent matrix form implemented via Wmat().

Dynamics (bus + wheels)

Define the total external torque

\[\boldsymbol{\tau} \;=\; \boldsymbol{\tau}_{\mathrm{dist}}(\mathbf{x}, \mathrm{os}) \;+\; \boldsymbol{\tau}_{\mathrm{act}}(\mathbf{x}, \mathbf{u}, \mathrm{os}).\]

The rigid-body rotational equation is

\[\mathbf{J}\,\dot{\boldsymbol{\omega}} \;=\; -\boldsymbol{\omega}\times\Big(\mathbf{J}\boldsymbol{\omega} + \mathbf{H}_{\mathrm{RW}}\Big) \;+\; \boldsymbol{\tau},\]

where \(\mathbf{H}_{\mathrm{RW}}\) is the body-frame contribution of stored wheel momentum. When wheels exist, wheel momentum dynamics follow actuator storage torques and the coupling term induced by \(\dot{\boldsymbol{\omega}}\).

Frames and environment

Environmental vectors are provided by Orbital_State and rotated into the body frame via rot_mat().

Inertia shifting

The inertia tensor about the center of mass is computed using the parallel axis theorem:

\[\mathbf{J}_{\mathrm{COM}} \;=\; \mathbf{J}_0 \;-\; m\left(\|\mathbf{r}_{\mathrm{COM}}\|^2\mathbf{I} \;-\;\mathbf{r}_{\mathrm{COM}}\mathbf{r}_{\mathrm{COM}}^\top\right),\]

where \(\mathbf{J}_0\) is the inertia about the reference origin and \(\mathbf{r}_{\mathrm{COM}}\) is the COM offset in the same frame.

Note

Many methods accept an ErrorMode to control bias/noise injection for Monte-Carlo simulation vs. deterministic propagation.

Stored attributes (selected)

Attribute

Meaning

mass

Total mass (kg)

COM

Center of mass vector (m)

J_0

Inertia about reference (kg·m²)

J_COM

Inertia about COM (kg·m²)

J_noRW

Bus inertia excluding wheels

state_len

7 + number_RW

control_len

len(actuators)

Raises:

ValueError – If COM is not shape (3,) or J_0 is not shape (3,3).

Parameters:
  • mass (float)

  • COM (ndarray)

  • J_0 (ndarray)

  • disturbances (List[Disturbance])

  • sensors (List[Sensor])

  • actuators (List[Actuator])

  • boresight (dict[str, ndarray] | ndarray)

GPS_readings(x, os)[source]

Return GPS sensor readings.

GPS sensors are identified as instances of GPS.

Parameters:
  • x (numpy.ndarray) – Current state vector, shape (state_len,).

  • os (Orbital_State) – Orbital/environmental state.

Returns:

List of GPS reading arrays (model-specific shapes).

Return type:

list[numpy.ndarray]

RW_readings(x, os)[source]

Return reaction wheel momentum measurements (with wheel measurement model behavior).

Delegates to each wheel’s measure_momentum().

Parameters:
  • x (numpy.ndarray) – Current state vector (unused by some wheel models but included for interface consistency).

  • os (Orbital_State) – Orbital/environmental state (unused by some wheel models but included for interface consistency).

Returns:

List of per-wheel measurement arrays.

Return type:

list[numpy.ndarray]

RWhs()[source]

Return the current reaction wheel momenta as a vector.

The returned vector is ordered according to self.momentum_inds (indices of RW within self.actuators).

Returns:

Reaction wheel momentum vector, shape (number_RW,).

Return type:

numpy.ndarray

RWhs_from_state(state)[source]

Extract the reaction wheel momenta subvector from a full state vector.

Using the state layout

\[\mathbf{x} = \begin{bmatrix} \boldsymbol{\omega} & \mathbf{q} & \mathbf{h}_{RW} \end{bmatrix}^\top,\]

this returns \(\mathbf{h}_{RW} = \mathbf{x}[7:]\).

Parameters:

state (numpy.ndarray) – Full state vector, shape (state_len,).

Returns:

Reaction wheel momentum vector, shape (number_RW,).

Return type:

numpy.ndarray

act_torque(x, u, os, dmode=None)[source]

Compute the total actuator torque.

The total actuator torque is

\[\boldsymbol{\tau}_{act}(\mathbf{x},\mathbf{u},\mathrm{os}) \;=\;\sum_{j=1}^{N_u} \boldsymbol{\tau}_{act,j}(u_j,\mathbf{x},\mathrm{os}),\]

where each actuator is a subclass of Actuator.

Parameters:
  • x (numpy.ndarray) – State vector, shape (state_len,).

  • u (numpy.ndarray) – Control input vector, shape (control_len,).

  • os (Orbital_State) – Orbital/environmental state.

  • dmode (ErrorMode | None) – Error/noise mode passed to actuator torque models.

Returns:

Actuator torque vector in body frame, shape (3,) (N·m).

Return type:

numpy.ndarray

dist_torque_hess(x, vecs)[source]

Hessian tensors of total disturbance torque.

Returns second derivatives of \(\boldsymbol{\tau}_{dist}\):

\[\frac{\partial^2 \boldsymbol{\tau}_{dist}}{\partial \mathbf{x}^2}, \qquad \frac{\partial^2 \boldsymbol{\tau}_{dist}}{\partial \mathbf{x}\,\partial \boldsymbol{\theta}_d}, \qquad \frac{\partial^2 \boldsymbol{\tau}_{dist}}{\partial \boldsymbol{\theta}_d^2}.\]

As with dist_torques_jacobian(), only quaternion components contribute in the current formulation, and each disturbance must implement:

  • torque_qqhess(self, vecs)\(\partial^2 \boldsymbol{\tau}/\partial \mathbf{q}^2\).

Parameters:
  • x (numpy.ndarray) – Current state vector, shape (state_len,).

  • vecs (dict[str, numpy.ndarray]) – Dictionary of body-frame environment vectors and their first/second quaternion derivatives.

Returns:

Triple (dddist_torq__dxdx, dddist_torq__dxddmp, dddist_torq__ddmpddmp).

Return type:

tuple[numpy.ndarray, numpy.ndarray, numpy.ndarray]

Return shapes

Name

Shape

dddist_torq__dxdx

(state_len, state_len, 3)

dddist_torq__dxddmp

(state_len, dist_param_len, 3)

dddist_torq__ddmpddmp

(dist_param_len, dist_param_len, 3)

dist_torques(x, os, dmode=None)[source]

Compute the total disturbance torque.

The total disturbance torque is the sum over all registered disturbances:

\[\boldsymbol{\tau}_{dist}(\mathbf{x},\mathrm{os}) \;=\;\sum_{i=1}^{N_d} \boldsymbol{\tau}_{dist,i}(\mathbf{x},\mathrm{os}).\]

Disturbance implementations may optionally accept a sat keyword argument; if so, the current Satellite instance is provided.

Parameters:
  • x (numpy.ndarray) – State vector, shape (state_len,).

  • os (Orbital_State) – Orbital/environmental state.

  • dmode (ErrorMode | None) – Optional error/noise mode (typically used inside disturbances if supported).

Returns:

Disturbance torque vector in body frame, shape (3,) (N·m).

Return type:

numpy.ndarray

dist_torques_jacobian(x, vecs)[source]

Jacobians of total disturbance torque with respect to the state and disturbance parameters.

Let \(\boldsymbol{\tau}_{dist}(\mathbf{x})\) be the total disturbance torque. This method returns:

\[\frac{\partial \boldsymbol{\tau}_{dist}}{\partial \mathbf{x}} \in \mathbb{R}^{n_x\times 3}, \qquad \frac{\partial \boldsymbol{\tau}_{dist}}{\partial \boldsymbol{\theta}_d} \in \mathbb{R}^{n_{\theta_d}\times 3}.\]

In the provided implementation, only the quaternion components affect the disturbance torque Jacobian, because environment vectors are rotated by attitude. Thus, the nonzero block is

\[\left[\frac{\partial \boldsymbol{\tau}_{dist}}{\partial \mathbf{x}}\right]_{3:7,:} = \sum_i \frac{\partial \boldsymbol{\tau}_{dist,i}}{\partial \mathbf{q}}.\]

Each disturbance model must implement:

  • torque_qjac(self, vecs)\(\partial \boldsymbol{\tau}/\partial \mathbf{q}\).

Parameters:
  • x (numpy.ndarray) – Current state vector, shape (state_len,).

  • vecs (dict[str, numpy.ndarray]) – Dictionary of body-frame environment vectors and their quaternion derivatives, e.g. {"b","r","s","v","rho","db","dr","ds","dv","os"}.

Returns:

Pair (ddist_torq__dx, ddist_torq__ddmp).

Return type:

tuple[numpy.ndarray, numpy.ndarray]

Return shapes

Name

Shape

ddist_torq__dx

(state_len, 3)

ddist_torq__ddmp

(dist_param_len, 3)

dynJacCore(x, u, orbital_state)[source]

Compute Jacobians of the dynamics with respect to state and input.

This method returns the first-order linearization

\[\delta \dot{\mathbf{x}} \approx \mathbf{A}\,\delta\mathbf{x} + \mathbf{B}\,\delta\mathbf{u},\]

where

\[\mathbf{A} = \frac{\partial f}{\partial \mathbf{x}},\qquad \mathbf{B} = \frac{\partial f}{\partial \mathbf{u}}.\]

Environmental vectors (magnetic field, sun vector, velocity, position) are rotated into the body frame:

\[\mathbf{v}_B(\mathbf{q}) = R(\mathbf{q})^\top \mathbf{v}_{ECI},\]

and derivatives \(\partial \mathbf{v}_B/\partial \mathbf{q}\) are obtained via drotmatTvecdq().

Actuator torque derivatives are aggregated via each actuator’s methods, e.g. dtorq__dbasestate and dtorq__du; disturbance derivatives are aggregated via dist_torques_jacobian().

Parameters:
  • x (numpy.ndarray) – Current state vector, shape (state_len,).

  • u (numpy.ndarray) – Current control vector, shape (control_len,).

  • orbital_state (Orbital_State) – Orbital/environmental state.

Returns:

List [dxdot__dx, dxdot__du].

Return type:

list[numpy.ndarray]

Return shapes

Name

Shape

dxdot__dx

(state_len, state_len)

dxdot__du

(control_len, state_len)

dynamics_Hessians(x, u, orbital_state)[source]

Compute second-order partial derivatives (Hessians) of the dynamics.

Returns the second derivatives of the dynamics map \(\dot{\mathbf{x}} = f(\mathbf{x},\mathbf{u},\mathrm{os})\) with respect to state and input:

\[\frac{\partial^2 f}{\partial \mathbf{x}^2}, \qquad \frac{\partial^2 f}{\partial \mathbf{x}\partial \mathbf{u}}, \qquad \frac{\partial^2 f}{\partial \mathbf{u}^2}.\]

These are useful for second-order trajectory optimization methods (DDP/iLQR) and nonlinear uncertainty propagation.

Frame rotation derivatives

Environmental vectors are rotated into body frame and their first and second derivatives with respect to the quaternion are computed:

\[\mathbf{v}_B(\mathbf{q}) = R(\mathbf{q})^\top \mathbf{v}_{ECI},\quad \frac{\partial \mathbf{v}_B}{\partial \mathbf{q}},\quad \frac{\partial^2 \mathbf{v}_B}{\partial \mathbf{q}^2},\]

using drotmatTvecdq() and ddrotmatTvecdqdq().

Output structure

The return is a nested list representing the block Hessians:

[
  [ ddxdot__dxdx, ddxdot__dxdu ],
  [ ddxdot__dxdu.T, ddxdot__dudu ]
]

where the final tensor index selects the component of \(\dot{\mathbf{x}}\).

Parameters:
  • x (numpy.ndarray) – Current state vector, shape (state_len,).

  • u (numpy.ndarray) – Current control vector, shape (control_len,).

  • orbital_state (Orbital_State) – Orbital/environmental state.

Returns:

Nested list of Hessian tensors [[ddxdot__dxdx, ddxdot__dxdu],[ddxdot__dxdu.T, ddxdot__dudu]].

Return type:

list[list[numpy.ndarray]]

Return shapes

Name

Shape

ddxdot__dxdx

(state_len, state_len, state_len)

ddxdot__dxdu

(state_len, control_len, state_len)

ddxdot__dudu

(control_len, control_len, state_len)

dynamics_core(x, u, orbital_state, dmode=None, verbose=False)[source]

Continuous-time attitude dynamics \(\dot{\mathbf{x}} = f(\mathbf{x},\mathbf{u},\mathrm{os})\).

This is the primary nonlinear dynamics model used by propagation, simulation, and linearization routines.

Kinematics

Quaternion kinematics are computed as

\[\dot{\mathbf{q}} \;=\;\frac{1}{2}\,W(\mathbf{q})^\top\,\boldsymbol{\omega},\]

where \(W(\mathbf{q})\) is produced by Wmat().

Torques

Disturbance and actuator torques are summed:

\[\boldsymbol{\tau}_{dist} = \sum_i \boldsymbol{\tau}_{dist,i}(\mathbf{x},\mathrm{os}), \qquad \boldsymbol{\tau}_{act} = \sum_j \boldsymbol{\tau}_{act,j}(\mathbf{x},\mathbf{u},\mathrm{os}),\]

using dist_torques() and act_torque().

Rigid-body dynamics

If no reaction wheels are present:

\[\dot{\boldsymbol{\omega}} \;=\;\mathbf{J}_{noRW}^{-1}\Big( -\boldsymbol{\omega}\times(\mathbf{J}\boldsymbol{\omega}) + \boldsymbol{\tau} \Big).\]

If reaction wheels are present, let \(\mathbf{A}\in\mathbb{R}^{n_{RW}\times 3}\) stack wheel spin axes as rows, and let \(\mathbf{h}\in\mathbb{R}^{n_{RW}}\) be wheel momenta. Then the coupled term uses the body-frame stored momentum \(\mathbf{H}_{RW} = \mathbf{h}^\top \mathbf{A}\) and

\[\dot{\boldsymbol{\omega}} \;=\;\mathbf{J}_{noRW}^{-1}\Big( -\boldsymbol{\omega}\times(\mathbf{J}\boldsymbol{\omega} + \mathbf{H}_{RW}) + \boldsymbol{\tau} \Big).\]

Wheel momentum dynamics use the actuator storage torque commands (from each RW) and subtract the coupling term from body acceleration:

\[\dot{\mathbf{h}} \;=\;\mathbf{u}_{RW} \;-\; \mathbf{A}\,\dot{\boldsymbol{\omega}}\,\mathrm{diag}(J_{RW}),\]

matching the implementation structure.

Parameters:
  • x (numpy.ndarray) – State vector [w(3), q(4), h(number_RW)], shape (state_len,).

  • u (numpy.ndarray) – Control vector, one element per actuator, shape (control_len,).

  • orbital_state (Orbital_State) – Orbital/environmental state providing vectors (e.g. magnetic field), used by torques.

  • dmode (ErrorMode | None) – Error/noise/bias mode used by sensors/actuators/disturbances. If None, a default with bias+noise enabled and updated is used.

  • verbose (bool) – If True, prints a detailed torque and derivative breakdown.

Returns:

Time derivative vector \(\dot{\mathbf{x}}\), shape (state_len,).

Return type:

numpy.ndarray

dynamics_for_solver(t, x, u, os0, os1)[source]

Wrapper for ODE solvers expecting signature f(t, x).

This method interpolates the orbital/environmental state between os0 and os1 using the fraction of elapsed time

\[\alpha = \frac{t}{\Delta t},\qquad \Delta t = (J2000_1 - J2000_0)\cdot C_{\mathrm{cent}\to \mathrm{sec}},\]

and calls dynamics_core() with an ErrorMode that does not update bias/noise so the solver uses a consistent noise sample.

Parameters:
  • t (float) – Time since start of the interval (s).

  • x (numpy.ndarray) – Current state vector, shape (state_len,).

  • u (numpy.ndarray) – Constant (or externally provided) control input for the interval, shape (control_len,).

  • os0 (Orbital_State) – Orbital state at the interval start.

  • os1 (Orbital_State) – Orbital state at the interval end.

Returns:

State derivative \(\dot{\mathbf{x}}\), shape (state_len,).

Return type:

numpy.ndarray

gen_dist_off(ind=None)[source]

Turn off general disturbances.

General disturbances correspond to General_Disturbance.

Parameters:

ind (int | None) – Optional index to toggle only one disturbance instance.

Returns:

None.

Return type:

None

gen_dist_on(ind=None)[source]

Turn on general disturbances.

General disturbances correspond to General_Disturbance.

Parameters:

ind (int | None) – Optional index to toggle only one disturbance instance.

Returns:

None.

Return type:

None

get_boresight(name=None)[source]
Parameters:

name (str | None)

Return type:

ndarray

gyro_readings(x, os)[source]

Return readings from all gyroscopes.

Filters self.attitude_sensors by Gyro and returns sensor.reading(x, os) for each.

Parameters:
  • x (numpy.ndarray) – Current state vector, shape (state_len,).

  • os (Orbital_State) – Orbital/environmental state.

Returns:

List of gyro measurement arrays (typically angular rate, rad/s).

Return type:

list[numpy.ndarray]

mtm_readings(x, os)[source]

Return readings from all magnetometers.

Filters self.attitude_sensors by MTM.

Parameters:
  • x (numpy.ndarray) – Current state vector, shape (state_len,).

  • os (Orbital_State) – Orbital/environmental state.

Returns:

List of magnetometer measurement arrays (typically magnetic field, T).

Return type:

list[numpy.ndarray]

noiseless_RW_readings(x, os)[source]

Return noiseless reaction wheel momentum measurements.

Delegates to each wheel’s measure_momentum_noiseless() (implementation-specific).

Parameters:
  • x (numpy.ndarray) – Current state vector (unused by some wheel models but included for interface consistency).

  • os (Orbital_State) – Orbital/environmental state (unused by some wheel models but included for interface consistency).

Returns:

List of per-wheel measurement arrays.

Return type:

list[numpy.ndarray]

noiseless_rk4(x, u, dt, orbital_state0, orbital_state1, verbose=False, mid_orbital_state=None, quat_as_vec=True, give_err_est=False)[source]

Propagate the state forward one step using RK4 (and optional embedded error estimate).

Quaternion components are normalized at each substep to avoid drift.

RK4 update

\[\begin{split}\begin{aligned} \mathbf{k}_1 &= f(\mathbf{x}_n,\mathbf{u},\mathrm{os}_0) \\ \mathbf{k}_2 &= f(\mathbf{x}_n+\tfrac{dt}{2}\mathbf{k}_1,\mathbf{u},\mathrm{os}_{1/2}) \\ \mathbf{k}_3 &= f(\mathbf{x}_n+\tfrac{dt}{2}\mathbf{k}_2,\mathbf{u},\mathrm{os}_{1/2}) \\ \mathbf{k}_4 &= f(\mathbf{x}_n+dt\,\mathbf{k}_3,\mathbf{u},\mathrm{os}_1) \\ \mathbf{x}_{n+1} &= \mathbf{x}_n + \tfrac{dt}{6}\left(\mathbf{k}_1 + 2\mathbf{k}_2 + 2\mathbf{k}_3 + \mathbf{k}_4\right) \end{aligned}\end{split}\]

where \(\mathrm{os}_{1/2}\) is the midpoint orbital state; if not provided it is computed via average().

Embedded error estimate (optional)

When give_err_est=True, a third-order embedded estimate \(\mathbf{x}^{(3)}_{n+1}\) is computed, and the elementwise difference is returned as a local truncation error proxy.

Noise/bias behavior

This routine enforces a deterministic propagation by using an ErrorMode with all noise/bias additions disabled.

Parameters:
  • x (numpy.ndarray) – Current state vector, shape (state_len,).

  • u (numpy.ndarray) – Control vector, shape (control_len,).

  • dt (float) – Integration time step (s).

  • orbital_state0 (Orbital_State) – Orbital state at the start of the step.

  • orbital_state1 (Orbital_State) – Orbital state at the end of the step.

  • verbose (bool) – If True, prints intermediate RK stages.

  • mid_orbital_state (Orbital_State | None) – Optional precomputed midpoint orbital state.

  • quat_as_vec (bool) – If True, propagate quaternions using RK4 on the quaternion components with normalization. If False, use a commutator-free Lie group method (CG5) for quaternion propagation.

  • give_err_est (bool) – If True, return (x_next, err_est) using an embedded lower-order estimate.

Returns:

Next state, or (next_state, error_estimate) if give_err_est=True.

Return type:

numpy.ndarray | tuple[numpy.ndarray, numpy.ndarray]

Raises:

ValueError – If give_err_est=True while using the CG5 quaternion propagation variant.

noiseless_sensor_readings(x, os)[source]

Compute sensor readings with noise disabled (bias may be included but not updated).

Uses an ErrorMode configured as:

  • add_bias = True

  • add_noise = False

  • update_bias = False

  • update_noise = False

This is useful for deterministic filtering tests where bias is treated as a fixed offset.

Parameters:
  • x (numpy.ndarray) – Current state vector, shape (state_len,).

  • os (Orbital_State) – Orbital/environmental state.

Returns:

Concatenated measurement vector.

Return type:

numpy.ndarray

prop_dist_off(ind=None)[source]

Turn off propulsion disturbances.

Propulsion disturbances correspond to Prop_Disturbance.

Parameters:

ind (int | None) – Optional index to toggle only one disturbance instance.

Returns:

None.

Return type:

None

prop_dist_on(ind=None)[source]

Turn on propulsion disturbances.

Propulsion disturbances correspond to Prop_Disturbance.

Parameters:

ind (int | None) – Optional index to toggle only one disturbance instance.

Returns:

None.

Return type:

None

sensor_readings(x, os, dmode=None)[source]

Compute concatenated sensor readings (attitude sensors + wheel momentum measurements).

Readings are collected from all non-GPS sensors in self.attitude_sensors and appended with reaction wheel momentum measurements from each RW.

Let \(\mathbf{y}\) denote the stacked measurement vector:

\[\begin{split}\mathbf{y} = \begin{bmatrix} \mathbf{y}_1 \\ \vdots \\ \mathbf{y}_{N_s} \\ \hat{\mathbf{h}}_{RW} \end{bmatrix},\end{split}\]

where each \(\mathbf{y}_i\) is a sensor-specific output (possibly vector-valued).

Parameters:
  • x (numpy.ndarray) – Current state vector, shape (state_len,).

  • os (Orbital_State) – Orbital/environmental state.

  • dmode (ErrorMode | None) – Error/noise mode controlling sensor noise/bias injection.

Returns:

Concatenated measurement vector.

Return type:

numpy.ndarray

specific_dist_off(ind)[source]

Turn off a specific disturbance model by index.

Parameters:

ind (int) – Index into self.disturbances.

Returns:

None.

Return type:

None

Raises:

IndexError – If ind is out of range.

specific_dist_on(ind)[source]

Turn on a specific disturbance model by index.

Parameters:

ind (int) – Index into self.disturbances.

Returns:

None.

Return type:

None

Raises:

IndexError – If ind is out of range.

srp_dist_off()[source]

Turn off all Solar Radiation Pressure disturbances.

Equivalent to calling:

self._toggle_disturbance(SRP_Disturbance, on=False)
Returns:

None.

Return type:

None

srp_dist_on()[source]

Turn on all Solar Radiation Pressure disturbances.

Equivalent to calling:

self._toggle_disturbance(SRP_Disturbance, on=True)
Returns:

None.

Return type:

None

sunpair_readings(x, os)[source]

Return readings from all sun-pair sensors.

Filters self.attitude_sensors by SunPair.

Parameters:
  • x (numpy.ndarray) – Current state vector, shape (state_len,).

  • os (Orbital_State) – Orbital/environmental state.

Returns:

List of sun-pair measurement arrays (model-specific shapes).

Return type:

list[numpy.ndarray]

sunsensor_readings(x, os)[source]

Return readings from all sun sensors.

Filters self.attitude_sensors by SunSensor.

Parameters:
  • x (numpy.ndarray) – Current state vector, shape (state_len,).

  • os (Orbital_State) – Orbital/environmental state.

Returns:

List of sun sensor measurement arrays (model-specific shapes).

Return type:

list[numpy.ndarray]

update_J(J_0=None, COM=None)[source]

Update inertia matrices and cached inverses.

This method validates the inertia tensor, enforces symmetry, checks positive definiteness, shifts the inertia to the center of mass, and computes additional derived inertias.

Validation

The inertia is required to be real, symmetric, and positive definite:

\[\mathbf{J} = \mathbf{J}^\top,\qquad \lambda_i(\mathbf{J})>0.\]

Parallel axis theorem

If \(\mathbf{J}_0\) is about the reference origin and \(\mathbf{r}_{COM}\) is the COM offset, then

\[\mathbf{J}_{COM} \;=\; \mathbf{J}_0 \;-\; m\left(\|\mathbf{r}_{COM}\|^2\mathbf{I} - \mathbf{r}_{COM}\mathbf{r}_{COM}^\top\right).\]

Excluding reaction wheel inertia

If wheels are present, the bus-only inertia is approximated by subtracting wheel spin-axis contributions:

\[\mathbf{J}_{noRW} \;=\; \mathbf{J}_{COM} \;-\; \sum_{k=1}^{n_{RW}} J_{RW,k}\,\hat{\mathbf{a}}_k \hat{\mathbf{a}}_k^\top,\]

where \(J_{RW,k}\) and \(\hat{\mathbf{a}}_k\) are the wheel rotor inertia and spin axis.

Cached inverses are computed for efficient dynamics evaluation.

Parameters:
  • J_0 (numpy.ndarray | None) – Inertia tensor about the reference origin, shape (3,3). If None, uses self.J_0.

  • COM (numpy.ndarray | None) – Center of mass offset, shape (3,). If None, uses self.COM.

Returns:

None.

Return type:

None

Raises:

ValueError – If J_0 cannot be reshaped to (3,3), contains non-real values, is not symmetric within tolerance, or is not positive definite.

update_RWhs(state_or_RWhs)[source]

Update stored wheel momentum values in each RW.

If state_or_RWhs has length self.state_len, it is interpreted as the full state \(\mathbf{x}\) and wheel momenta are extracted via RWhs_from_state(). Otherwise, it is interpreted as the wheel momentum vector directly.

Parameters:

state_or_RWhs (numpy.ndarray) – Either full state vector (shape (state_len,)) or wheel momenta (shape (number_RW,)).

Returns:

None.

Return type:

None

Raises:

ValueError – If the inferred wheel momentum vector length does not equal self.number_RW.

class ADCS.helpers.simresults.SimulationResults(runs: 'List[RunResults]', configs: 'Optional[List[Dict[str, Any]]]' = None, run_ids: 'Optional[List[int]]' = None)[source]

Bases: object

Parameters:
  • runs (List[RunResults])

  • configs (List[Dict[str, Any]] | None)

  • run_ids (List[int] | None)

classmethod load(path, ephem=None)[source]
Parameters:
Return type:

SimulationResults

first()[source]
Return type:

RunResults

map(attr)[source]
Parameters:

attr (str)

Return type:

List[Any]

save(name, out_dir='output', compress=True)[source]
Parameters:
  • name (str)

  • out_dir (str | Path)

  • compress (bool)

Return type:

Path

stack_control()[source]
Return type:

ndarray

stack_state()[source]
Return type:

ndarray

stack_time()[source]
Return type:

ndarray

configs: List[Dict[str, Any]] | None = None
property est_satellite: EstimatedSatellite | None
run_ids: List[int] | None = None
runs: List[RunResults]
property satellite: Satellite
class ADCS.helpers.simresults.datetime(year, month, day[, hour[, minute[, second[, microsecond[, tzinfo]]]]])

Bases: date

The year, month and day arguments are required. tzinfo may be None, or an instance of a tzinfo subclass. The remaining arguments may be ints.

classmethod combine()

date, time -> datetime with same date and time fields

classmethod fromisoformat()

string -> datetime from a string in most ISO 8601 formats

classmethod fromtimestamp()

timestamp[, tz] -> tz’s local time from POSIX timestamp.

classmethod now(tz=None)

Returns new datetime object representing current time local to tz.

tz

Timezone object.

If no tz is specified, uses local timezone.

classmethod strptime()

string, format -> new datetime parsed from a string (like time.strptime()).

classmethod utcfromtimestamp()

Construct a naive UTC datetime from a POSIX timestamp.

classmethod utcnow()

Return a new datetime representing UTC day and time.

astimezone()

tz -> convert to local time in new timezone tz

ctime()

Return ctime() style string.

date()

Return date object with same year, month and day.

dst()

Return self.tzinfo.dst(self).

isoformat()

[sep] -> string in ISO 8601 format, YYYY-MM-DDT[HH[:MM[:SS[.mmm[uuu]]]]][+HH:MM]. sep is used to separate the year from the time, and defaults to ‘T’. The optional argument timespec specifies the number of additional terms of the time to include. Valid options are ‘auto’, ‘hours’, ‘minutes’, ‘seconds’, ‘milliseconds’ and ‘microseconds’.

replace()

Return datetime with new specified fields.

time()

Return time object with same time but with tzinfo=None.

timestamp()

Return POSIX timestamp as float.

timetuple()

Return time tuple, compatible with time.localtime().

timetz()

Return time object with same time and tzinfo.

tzname()

Return self.tzinfo.tzname(self).

utcoffset()

Return self.tzinfo.utcoffset(self).

utctimetuple()

Return UTC time tuple, compatible with time.localtime().

fold
hour
max = datetime.datetime(9999, 12, 31, 23, 59, 59, 999999)
microsecond
min = datetime.datetime(1, 1, 1, 0, 0)
minute
resolution = datetime.timedelta(microseconds=1)
second
tzinfo
ADCS.helpers.simresults.dataclass(cls=None, /, *, init=True, repr=True, eq=True, order=False, unsafe_hash=False, frozen=False, match_args=True, kw_only=False, slots=False, weakref_slot=False)[source]

Add dunder methods based on the fields defined in the class.

Examines PEP 526 __annotations__ to determine fields.

If init is true, an __init__() method is added to the class. If repr is true, a __repr__() method is added. If order is true, rich comparison dunder methods are added. If unsafe_hash is true, a __hash__() method is added. If frozen is true, fields may not be assigned to after instance creation. If match_args is true, the __match_args__ tuple is added. If kw_only is true, then by default all fields are keyword-only. If slots is true, a new class with a __slots__ attribute is returned.