-
Notifications
You must be signed in to change notification settings - Fork 117
new facet srp effector module #1340
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Merged
leahkiner
merged 7 commits into
develop
from
feature/1332-new-facet-srp-effector-module
Apr 15, 2026
+883
−1
Merged
Changes from all commits
Commits
Show all changes
7 commits
Select commit
Hold shift + click to select a range
b41ac9c
[#1332] Add new facetedSRPEffector module
leahkiner d2f6e94
[#1332] Add module unit test
leahkiner ba07df4
[#1332] Add module rst documentation
leahkiner a0fd97b
[#1332] Add bsk release note snippet
leahkiner 4beaaf0
[#1332] Write output messages after projected area module reset
leahkiner c9a4f06
[#1332] Update module priority order in unit test
leahkiner c3b6343
[#1332] Add eclipse message to facetedSRPEffector
leahkiner File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
1 change: 1 addition & 0 deletions
1
...aseNotesSnippets/1332-new-faceted-solar-radiation-pressure-dynamic-effector.rst
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| 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. |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
332 changes: 332 additions & 0 deletions
332
src/simulation/dynamics/facetedSRPEffector/_UnitTest/test_facetedSRPEffector.py
Large diffs are not rendered by default.
Oops, something went wrong.
208 changes: 208 additions & 0 deletions
208
src/simulation/dynamics/facetedSRPEffector/facetedSRPEffector.cpp
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| 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; | ||
| } | ||
|
|
||
| // Read optional sun eclipse message | ||
| this->sunVisibilityFactor = 1.0; | ||
| if (this->sunEclipseInMsg.isLinked() && this->sunEclipseInMsg.isWritten()) { | ||
|
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); | ||
|
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
66
src/simulation/dynamics/facetedSRPEffector/facetedSRPEffector.h
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| 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
57
src/simulation/dynamics/facetedSRPEffector/facetedSRPEffector.i
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| 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__]) | ||
| %} |
Oops, something went wrong.
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Uh oh!
There was an error while loading. Please reload this page.