Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
- Added a new :ref:`facetedSRPEffector` module to compute the aggregate force and torque acting on the spacecraft due to impinging photons from the Sun. Unlike the original :ref:`facetSRPDynamicEffector` module, this new module (1) requires the facet data to be externally projected into the spacecraft body frame, (2) expects the facet sunlit projected areas to be provided externally; it does not compute projected area internally, and (3) does not read facet articulation-angle messages directly.
Original file line number Diff line number Diff line change
Expand Up @@ -69,6 +69,8 @@ void FacetedSpacecraftProjectedArea::Reset(uint64_t callTime) {
this->facetNHat_BList.push_back(Eigen::Vector3d::Zero());
this->facetProjectedAreaList.push_back(0.0);
}

this->writeOutputMessages(callTime);
}

/*! Module update method.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -62,7 +62,7 @@ class FacetedSpacecraftProjectedArea: public SysModel {
std::vector<Eigen::Vector3d> facetNHat_BList; //!< [-] List of facet normal vectors expressed in hub B frame components

/* Facet output message data */
std::vector<double> facetProjectedAreaList; //!< [m^2] List of facet projected areas
std::vector<double> facetProjectedAreaList{}; //!< [m^2] List of facet projected areas
double totalProjectedArea{}; //!< [m^2] Total projected area for all facets
};

Expand Down

Large diffs are not rendered by default.

208 changes: 208 additions & 0 deletions src/simulation/dynamics/facetedSRPEffector/facetedSRPEffector.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,208 @@
/*
ISC License

Copyright (c) 2026, Autonomous Vehicle Systems Lab, University of Colorado at Boulder

Permission to use, copy, modify, and/or distribute this software for any
purpose with or without fee is hereby granted, provided that the above
copyright notice and this permission notice appear in all copies.

THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.

*/

#include "facetedSRPEffector.h"
#include "architecture/utilities/avsEigenSupport.h"
#include "architecture/utilities/astroConstants.h"

/*! This method resets required module variables and checks the input messages to ensure they are linked.

@param currentSimNanos [ns] Time the method is called
*/
void FacetedSRPEffector::Reset(uint64_t currentSimNanos) {
// Check Sun state input message is linked
if (!this->sunStateInMsg.isLinked()) {
this->bskLogger.bskLog(BSK_ERROR, "FacetedSRPEffector.sunStateInMsg was not linked.");
return;
}

// Check all facet input messages are linked
for (uint64_t idx = 0; idx < this->facetElementBodyInMsgs.size(); idx++) {
if (!this->facetElementBodyInMsgs[idx].isLinked()) {
this->bskLogger.bskLog(BSK_ERROR,
"FacetedSRPEffector: Input message error. facetElementBodyInMsgs is not linked.");
return;
}
if (!this->facetProjectedAreaInMsgs[idx].isLinked()) {
this->bskLogger.bskLog(BSK_ERROR,
"FacetedSRPEffector: Input message error. facetProjectedAreaInMsgs is not linked.");
return;
}
}

// Clear, allocate and initialize data lists
this->facetAreaList.clear();
this->facetProjectedAreaList.clear();
this->facetR_CopB_BList.clear();
this->facetNHat_BList.clear();
this->facetDiffuseCoeffList.clear();
this->facetSpecularCoeffList.clear();
this->facetAreaList.reserve(this->numFacets);
this->facetProjectedAreaList.reserve(this->numFacets);
this->facetR_CopB_BList.reserve(this->numFacets);
this->facetNHat_BList.reserve(this->numFacets);
this->facetDiffuseCoeffList.reserve(this->numFacets);
this->facetSpecularCoeffList.reserve(this->numFacets);
for (uint64_t idx = 0; idx < this->numFacets; idx++) {
this->facetAreaList.push_back(0.0);
this->facetProjectedAreaList.push_back(0.0);
this->facetR_CopB_BList.push_back(Eigen::Vector3d::Zero());
this->facetNHat_BList.push_back(Eigen::Vector3d::Zero());
this->facetDiffuseCoeffList.push_back(0.0);
this->facetSpecularCoeffList.push_back(0.0);
}
}

/*! This method gives the module access to the hub inertial attitude and position.

@param states Dynamic parameter states
*/
void FacetedSRPEffector::linkInStates(DynParamManager& states) {
this->hubSigma = states.getStateObject(this->stateNameOfSigma);
this->hubPosition = states.getStateObject(this->stateNameOfPosition);
}

/*! This method computes the srp force and torque acting about the hub point B in B frame components.

@param callTime [s] Time the method is called
@param timeStep [s] Simulation time step
*/
void FacetedSRPEffector::computeForceTorque(double callTime, double timeStep) {

this->forceExternal_B.setZero();
this->torqueExternalPntB_B.setZero();
if (this->facetAreaList.size() != this->numFacets) {
this->bskLogger.bskLog(BSK_ERROR,
"FacetedSRPEffector: Size of facetAreaList must equal numFacets");
return;
}

// Read the sun state input message
SpicePlanetStateMsgPayload sunStateIn{};
Eigen::Vector3d r_SN_N = Eigen::Vector3d::Zero();
if (this->sunStateInMsg.isWritten()) {
sunStateIn = this->sunStateInMsg();
r_SN_N = cArray2EigenVector3d(sunStateIn.PositionVector);
} else {
this->bskLogger.bskLog(BSK_ERROR,
"FacetedSRPEffector: Input message error. sunStateInMsg was not written.");
return;
}
Comment thread
leahkiner marked this conversation as resolved.

// Read optional sun eclipse message
this->sunVisibilityFactor = 1.0;
if (this->sunEclipseInMsg.isLinked() && this->sunEclipseInMsg.isWritten()) {
Comment thread
leahkiner marked this conversation as resolved.
EclipseMsgPayload sunEclipseIn = this->sunEclipseInMsg();
this->sunVisibilityFactor = sunEclipseIn.illuminationFactor;
}

// Compute the sun direction unit vector in spacecraft body frame components
Eigen::MRPd sigma_BN;
sigma_BN = (Eigen::Vector3d)this->hubSigma->getState();
Eigen::Matrix3d dcm_BN = sigma_BN.toRotationMatrix().transpose();
Eigen::Vector3d r_BN_N = this->hubPosition->getState();
Eigen::Vector3d r_SB_B = dcm_BN * (r_SN_N - r_BN_N);

Eigen::Vector3d sunDirHat_B = Eigen::Vector3d::Zero();
double norm = r_SB_B.norm();
if (norm > 1e-12) {
sunDirHat_B = r_SB_B / norm;
} else {
this->forceExternal_B = Eigen::Vector3d::Zero();
this->torqueExternalPntB_B = Eigen::Vector3d::Zero();
this->bskLogger.bskLog(BSK_ERROR,
"FacetedSRPEffector: No unique body heading vector can be resolved.");
return;
}

// Read the facet geometry and projected area input messages
FacetElementBodyMsgPayload facetElementBodyIn{};
ProjectedAreaMsgPayload facetProjectedAreaIn{};
for (uint64_t idx = 0; idx < this->numFacets; idx++) {
if (this->facetElementBodyInMsgs[idx].isWritten() && this->facetProjectedAreaInMsgs[idx].isWritten()) {
facetElementBodyIn = this->facetElementBodyInMsgs[idx]();
this->facetAreaList[idx] = facetElementBodyIn.area;
this->facetR_CopB_BList[idx] = cArray2EigenVector3d(facetElementBodyIn.r_CopB_B);
Comment thread
leahkiner marked this conversation as resolved.
this->facetNHat_BList[idx] = cArray2EigenVector3d(facetElementBodyIn.nHat_B);
this->facetDiffuseCoeffList[idx] = facetElementBodyIn.c_diffuse;
this->facetSpecularCoeffList[idx] = facetElementBodyIn.c_specular;

facetProjectedAreaIn = this->facetProjectedAreaInMsgs[idx]();
this->facetProjectedAreaList[idx] = facetProjectedAreaIn.area;
} else {
this->bskLogger.bskLog(BSK_ERROR,
"FacetedSRPEffector: Input message error. facetElementBodyInMsgs or facetProjectedAreaInMsgs was not written.");
return;
}
}

// Calculate the SRP pressure acting at the current spacecraft location
double numAU = AU * 1000 / r_SB_B.norm();
double SRPPressure = (SOLAR_FLUX_EARTH / SPEED_LIGHT) * numAU * numAU; // [Pa]

// Compute the per-facet and total SRP force and torque
Eigen::Vector3d facetSRPForcePntB_B = Eigen::Vector3d::Zero();
Eigen::Vector3d facetSRPTorquePntB_B = Eigen::Vector3d::Zero();
Eigen::Vector3d totalSRPForcePntB_B = Eigen::Vector3d::Zero();
Eigen::Vector3d totalSRPTorquePntB_B = Eigen::Vector3d::Zero();

for (uint64_t idx = 0; idx < this->numFacets; idx++) {
if (this->facetProjectedAreaList[idx] > 0.0) {
double cosTheta = facetProjectedAreaList[idx] / facetAreaList[idx];

facetSRPForcePntB_B = -SRPPressure * this->facetProjectedAreaList[idx]
* ((1 - this->facetSpecularCoeffList[idx])
* sunDirHat_B + 2 * ((this->facetDiffuseCoeffList[idx] / 3)
+ this->facetSpecularCoeffList[idx] * cosTheta)
* this->facetNHat_BList[idx]); // [N]
facetSRPTorquePntB_B = this->facetR_CopB_BList[idx].cross(facetSRPForcePntB_B); // [Nm]

// Add the facet contribution to the total SRP force and torque acting on the spacecraft
totalSRPForcePntB_B += facetSRPForcePntB_B;
totalSRPTorquePntB_B += facetSRPTorquePntB_B;
}
}

// Update the force and torque vectors in the dynamic effector base class
this->forceExternal_B = this->sunVisibilityFactor * totalSRPForcePntB_B;
this->torqueExternalPntB_B = this->sunVisibilityFactor * totalSRPTorquePntB_B;
}

/*! Setter method for the total number of spacecraft facets.
@param numFacets [-]
*/
void FacetedSRPEffector::setNumFacets(const uint64_t numFacets) {
this->numFacets = numFacets;

this->facetElementBodyInMsgs.clear();
this->facetProjectedAreaInMsgs.clear();
this->facetElementBodyInMsgs.reserve(this->numFacets);
this->facetProjectedAreaInMsgs.reserve(this->numFacets);

// Push back facet message vectors
for (uint64_t idx = 0; idx < this->numFacets; ++idx) {
this->facetElementBodyInMsgs.push_back(ReadFunctor<FacetElementBodyMsgPayload>{});
this->facetProjectedAreaInMsgs.push_back(ReadFunctor<ProjectedAreaMsgPayload>());
}
}

/*! Getter method for the total number of spacecraft facets.
@return const uint64_t
*/
const uint64_t FacetedSRPEffector::getNumFacets() const { return this->numFacets; }
66 changes: 66 additions & 0 deletions src/simulation/dynamics/facetedSRPEffector/facetedSRPEffector.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
/*
ISC License

Copyright (c) 2026, Autonomous Vehicle Systems Lab, University of Colorado at Boulder

Permission to use, copy, modify, and/or distribute this software for any
purpose with or without fee is hereby granted, provided that the above
copyright notice and this permission notice appear in all copies.

THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.

*/

#ifndef FACETED_SRP_EFFECTOR_H
#define FACETED_SRP_EFFECTOR_H

#include <Eigen/Dense>
#include <vector>
#include "simulation/dynamics/_GeneralModuleFiles/dynamicEffector.h"
#include "architecture/_GeneralModuleFiles/sys_model.h"
#include "architecture/utilities/bskLogging.h"
#include "architecture/messaging/messaging.h"
#include "architecture/msgPayloadDefC/EclipseMsgPayload.h"
#include "architecture/msgPayloadDefC/FacetElementBodyMsgPayload.h"
#include "architecture/msgPayloadDefC/ProjectedAreaMsgPayload.h"
#include "architecture/msgPayloadDefC/SpicePlanetStateMsgPayload.h"
#include "simulation/dynamics/_GeneralModuleFiles/stateData.h"

/*! @brief Faceted solar radiation pressure dynamic effector */
class FacetedSRPEffector: public SysModel, public DynamicEffector {
public:
FacetedSRPEffector() = default; //!< Constructor
~FacetedSRPEffector() = default; //!< Destructor
void linkInStates(DynParamManager& states) override; //!< Method for giving the effector access to the hub states
void computeForceTorque(double callTime, double timeStep) override; //!< Method for computing the total SRP force and torque about point B
void Reset(uint64_t currentSimNanos) override; //!< Reset method
void setNumFacets(const uint64_t numFacets); //!< Setter method for total number of spacecraft facets
const uint64_t getNumFacets() const; //!< Getter method for total number of spacecraft facets

std::vector<ReadFunctor<FacetElementBodyMsgPayload>> facetElementBodyInMsgs; //!< List of facet geometry input data (Expressed in hub B frame)
std::vector<ReadFunctor<ProjectedAreaMsgPayload>> facetProjectedAreaInMsgs; //!< List of facet projected area input messages
ReadFunctor<SpicePlanetStateMsgPayload> sunStateInMsg; //!< Sun spice ephemeris input message
ReadFunctor<EclipseMsgPayload> sunEclipseInMsg; //!< (optional) Sun eclipse input message

private:
/* Facet input message data */
std::vector<double> facetAreaList; //!< [m^2] List of facet areas
std::vector<double> facetProjectedAreaList; //!< [m^2] List of facet projected areas
std::vector<Eigen::Vector3d> facetR_CopB_BList; //!< [m] List of facet center of pressure locations wrt point B expressed in B frame components
std::vector<Eigen::Vector3d> facetNHat_BList; //!< [-] List of facet normal vectors expressed in hub B frame components
std::vector<double> facetDiffuseCoeffList; //!< [-] List of facet diffuse reflection optical coefficient list
std::vector<double> facetSpecularCoeffList; //!< [-] List of facet specular reflection optical coefficient list

uint64_t numFacets{}; //!< Number of spacecraft facets
StateData *hubPosition = nullptr; //!< [m] Hub inertial position vector
StateData *hubSigma = nullptr; //!< Hub MRP inertial attitude
double sunVisibilityFactor = 1.0; //!< Sun visibility factor (0 = full shadow, 1 = full sunlight)
};

#endif
57 changes: 57 additions & 0 deletions src/simulation/dynamics/facetedSRPEffector/facetedSRPEffector.i
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
/*
ISC License

Copyright (c) 2026, Autonomous Vehicle Systems Lab, University of Colorado at Boulder

Permission to use, copy, modify, and/or distribute this software for any
purpose with or without fee is hereby granted, provided that the above
copyright notice and this permission notice appear in all copies.

THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.

*/


%module facetedSRPEffector

%include "architecture/utilities/bskException.swg"
%default_bsk_exception();

%{
#include "facetedSRPEffector.h"
%}

%pythoncode %{
from Basilisk.architecture.swig_common_model import *
%}
%include "std_string.i"
%include "swig_eigen.i"
%include "swig_conly_data.i"

%include "sys_model.i"
%include "simulation/dynamics/_GeneralModuleFiles/dynParamManager.i"
%include "simulation/dynamics/_GeneralModuleFiles/dynamicEffector.h"
%include "facetedSRPEffector.h"

%include "architecture/msgPayloadDefC/FacetElementBodyMsgPayload.h"
struct FacetElementBodyMsg_C;

%include "architecture/msgPayloadDefC/ProjectedAreaMsgPayload.h"
struct ProjectedAreaMsg_C;

%include "architecture/msgPayloadDefC/SpicePlanetStateMsgPayload.h"
struct SpicePlanetStateMsg_C;

%include "architecture/msgPayloadDefC/EclipseMsgPayload.h"
struct EclipseMsg_C;

%pythoncode %{
import sys
protectAllClasses(sys.modules[__name__])
%}
Loading
Loading