Source code for ADCS.satellite_factory.sensors.create_cubesat_sunpair

__all__ = [
    'create_Clydespace_3U_array',
    'create_elmos_sun_sensors',
    'create_gnb_sun_sensors',
    'create_hamamatsu_s3931_sun_sensors',
    'create_nano_iss60_sun_sensors',
    'create_osram_sfh2430_sun_sensors',
]

import numpy as np
from typing import Optional, List

from ADCS.satellite_hardware.sensors import SunPair, SunSensor
from ADCS.satellite_hardware.errors import Noise, Bias
from ADCS.helpers.math_helpers import random_n_unit_vec


def _axes_from_az_el_deg(az_el_deg: np.ndarray) -> np.ndarray:
    angles = np.deg2rad(np.asarray(az_el_deg, dtype=float))
    az = angles[:, 0]
    el = angles[:, 1]
    return np.column_stack((np.cos(el) * np.cos(az), np.cos(el) * np.sin(az), np.sin(el)))

[docs] def create_Clydespace_3U_array(axis: np.ndarray = np.array([1, 0, 0]), bias: Bias | None = None, noise: Noise | None = None, estimate_bias: bool = False) -> List[SunPair]: r""" Create a Clyde Space 3U solar-array SunPair proxy. Default error model provenance: The default bias/noise values in this helper are package-level representative simulation values for BeaverCube-style examples. They are not currently tied to a published Clyde Space data sheet table or BeaverCube flight-calibration result. Pass explicit ``bias`` and ``noise`` objects when a traceable mission or vendor model is required. """ sun_eff = 0.3 if bias is None: e_bias = np.random.uniform(0,0.2)*sun_eff std_bias = 0.00001*sun_eff bias = Bias(bias=e_bias, std_bias=std_bias) if noise is None: e_noise = 0 std_noise = 0.001*sun_eff noise = Noise(noise=e_noise, std_noise=std_noise) return [SunPair(axis=axis, efficiency=(sun_eff, sun_eff), bias=bias, noise=noise, estimate_bias=estimate_bias)]
[docs] def create_gnb_sun_sensors( axes: np.ndarray = np.vstack((np.eye(3), -np.eye(3))), bias: List[Bias] | None = None, noise: List[Noise] | None = None, estimate_bias: bool = False, ) -> List[SunSensor]: r""" Create the final BRITE/GNB six-face dedicated Sun-sensor set. The public GNB summary gives one dedicated sensor per spacecraft face and accuracy/resolution specifications, but not statistical noise or bias values for the final dedicated sensors. The default ``noise`` uses ``sin(1 deg)`` as a conservative scalar cosine-measurement noise floor so estimator measurement covariances remain non-singular. This noise floor is an approximate modeling assumption, not a source-backed calibration value. """ n_axes = len(axes) if bias is None: bias = [Bias() for _ in range(n_axes)] if noise is None: std_noise = np.sin(1.0 * np.pi / 180.0) noise = [Noise(noise=0.0, std_noise=std_noise) for _ in range(n_axes)] return [ SunSensor(axis=axes[j], efficiency=1.0, bias=bias[j], noise=noise[j], estimate_bias=estimate_bias) for j in range(n_axes) ]
[docs] def create_elmos_sun_sensors( axes: np.ndarray | None = None, bias: List[Bias] | None = None, noise: List[Noise] | None = None, estimate_bias: bool = False, ) -> List[SunSensor]: r""" Create the LightSail 2 coarse Sun-sensor set. LightSail 2 had four Elmos Sun sensors on deployable solar panels and one on the -Z spacecraft face. Exact deployed-panel boresight vectors are not encoded here, so panel sensors use a nominal +/-X/+Y layout and the -Z sensor uses the documented body vector. The default ``noise`` uses ``sin(5 deg)`` as a conservative scalar cosine-measurement noise floor so estimator measurement covariances remain non-singular. This noise floor is an approximate modeling assumption, not a source-backed calibration value. """ if axes is None: axes = np.array([ [1.0, 0.0, 0.0], [-1.0, 0.0, 0.0], [0.0, 1.0, 0.0], [0.0, -1.0, 0.0], [0.0, 0.0, -1.0], ]) n_axes = len(axes) if bias is None: bias = [Bias() for _ in range(n_axes)] if noise is None: std_noise = np.sin(5.0 * np.pi / 180.0) noise = [Noise(noise=0.0, std_noise=std_noise) for _ in range(n_axes)] return [ SunSensor(axis=axes[j], efficiency=1.0, bias=bias[j], noise=noise[j], estimate_bias=estimate_bias) for j in range(n_axes) ]
[docs] def create_hamamatsu_s3931_sun_sensors( axes: np.ndarray = np.array([[1, 0, 0]]), bias: List[Bias] | None = None, noise: List[Noise] | None = None, estimate_bias: bool = False, ) -> List[SunSensor]: r""" Create one ESTCube-1 Hamamatsu S3931 one-dimensional Sun sensor channel. ESTCube-1 had six custom Sun sensor assemblies, one per spacecraft face. Each assembly used two Hamamatsu S3931 one-dimensional PSDs under perpendicular slits. This helper represents one such one-dimensional channel. The preflight simulator used angular noise of approximately 1.25 deg and initial angular bias uniformly distributed over +/-1 deg. Default error model provenance: ``bias`` approximates the published +/-1 deg initial angular bias as a scalar cosine-measurement offset of ``sin(1 deg)``. ``noise`` approximates the published 1.25 deg Gaussian angular simulator noise as scalar cosine-measurement noise of ``sin(1.25 deg)``. The useful flight region was approximately +/-36.7 deg with incidence-angle-dependent flight covariance; the current sensor model does not encode that finite field-of-view or covariance schedule. Source: J. Slavinskis et al., "ESTCube-1 attitude determination system flight results," *Journal of Aerospace Engineering*, 2016. https://ascelibrary.org/doi/10.1061/%28ASCE%29AS.1943-5525.0000504 """ n_axes = len(axes) if bias is None: bias_bound = np.sin(1.0 * np.pi / 180.0) e_bias = np.random.uniform(-bias_bound, bias_bound, size=n_axes) bias = [Bias(bias=e_bias[j], std_bias=0.0, bounds=(-bias_bound, bias_bound)) for j in range(n_axes)] if noise is None: std_noise = np.sin(1.25 * np.pi / 180.0) noise = [Noise(noise=0.0, std_noise=std_noise) for _ in range(n_axes)] return [ SunSensor(axis=axes[j], efficiency=1.0, bias=bias[j], noise=noise[j], estimate_bias=estimate_bias) for j in range(n_axes) ]
[docs] def create_osram_sfh2430_sun_sensors( az_el_deg: np.ndarray | None = None, bias: List[Bias] | None = None, noise: List[Noise] | None = None, estimate_bias: bool = False, ) -> List[SunSensor]: r""" Create OSRAM SFH2430 coarse Sun-sensor photodiode channels used on RAX. By default this helper creates one photodiode aligned with ``+X``. RAX-1 used 9 SFH2430 photodiodes and RAX-2 used 17; pass the mission-specific azimuth/elevation table through ``az_el_deg`` to create those arrays. The published RAX estimator simulations used 0.05 V additive white noise, with scale factors around 2.5-4 V. This factory uses an efficiency of 3.0 V so the sensor output and noise are in the same approximate voltage units. Default error model provenance: ``noise`` defaults to the 0.05 V additive white-noise value from the RAX photodiode calibration/simulation tables. The default additive ``bias`` is zero because the RAX calibration estimated photodiode scale factor and mounting azimuth/elevation errors rather than publishing a single additive voltage-bias value. The cited calibration work reports that 0.05 V corresponds to roughly 1.0-5.7 deg angular uncertainty, depending on incidence angle and scale factor. Source: J. C. Springmann and J. W. Cutler, "On-orbit calibration of photodiodes for attitude determination," *Journal of Guidance, Control, and Dynamics*, 2014. https://deepblue.lib.umich.edu/bitstream/handle/2027.42/140645/1.g000175.pdf?sequence=1 """ if az_el_deg is None: az_el_deg = np.array([[0, 0]]) axes = _axes_from_az_el_deg(az_el_deg) n_axes = len(axes) if bias is None: bias = [Bias() for _ in range(n_axes)] if noise is None: noise = [Noise(noise=0.0, std_noise=0.05) for _ in range(n_axes)] return [ SunSensor(axis=axes[j], efficiency=3.0, bias=bias[j], noise=noise[j], estimate_bias=estimate_bias) for j in range(n_axes) ]
[docs] def create_nano_iss60_sun_sensors( axes: np.ndarray = np.array([[1, 0, 0]]), bias: List[Bias] | None = None, noise: List[Noise] | None = None, estimate_bias: bool = False, ) -> List[SunSensor]: r""" Create one Solar MEMS NANO-ISS60 Sun sensor proxy used on MOVE-II. A NANO-ISS60 is a two-axis analog Sun position sensor. It is represented here by one body-normal :class:`~ADCS.satellite_hardware.sensors.SunSensor` proxy because the current package has scalar Sun-sensor channels rather than a native two-axis NANO-ISS60 model. Default error model provenance: ``noise`` approximates the MOVE-II HIL angular Sun-vector noise ``sigma = 0.06 deg = 1.047e-3 rad`` as scalar cosine-measurement noise ``sin(0.06 deg)``. No nominal fixed Sun-sensor bias was given in the accessible tables, so ``bias`` defaults to zero. The source documents a 10 deg Sun-sensor bias sensitivity case, but that is not the nominal default. Source: `Hardware-in-the-Loop Verification of the Distributed, Magnetorquer-Based ADCS of the CubeSat MOVE-II <https://mediatum.ub.tum.de/doc/1483411/document.pdf>`__ """ n_axes = len(axes) if bias is None: bias = [Bias() for _ in range(n_axes)] if noise is None: std_noise = np.sin(0.06 * np.pi / 180.0) noise = [Noise(noise=0.0, std_noise=std_noise) for _ in range(n_axes)] return [ SunSensor(axis=axes[j], efficiency=1.0, bias=bias[j], noise=noise[j], estimate_bias=estimate_bias) for j in range(n_axes) ]