From 740167ce99d53e35102cfb9a29caedc48ea0fb0c Mon Sep 17 00:00:00 2001 From: meggert Date: Wed, 11 Oct 2023 14:30:57 +0200 Subject: [PATCH 01/13] monotonic for pwr ctrl --- mission/controller/PowerController.cpp | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/mission/controller/PowerController.cpp b/mission/controller/PowerController.cpp index 7b7db9e8..202e9906 100644 --- a/mission/controller/PowerController.cpp +++ b/mission/controller/PowerController.cpp @@ -156,7 +156,7 @@ ReturnValue_t PowerController::checkModeCommand(Mode_t mode, Submode_t submode, void PowerController::calculateStateOfCharge() { // get time - Clock::getClock_timeval(&now); + Clock::getClockMonotonic(&now); // update EPS HK values ReturnValue_t result = updateEpsData(); @@ -285,7 +285,12 @@ ReturnValue_t PowerController::calculateOpenCircuitVoltageCharge() { } ReturnValue_t PowerController::calculateCoulombCounterCharge() { - double timeDiff = timevalOperations::toDouble(now - oldTime); + double timeDiff = 0.0; + if (timevalOperations::toDouble(oldTime) != 0) { + timeDiff = timevalOperations::toDouble(now - oldTime); + } else { + return returnvalue::FAILED; + } if (timeDiff > maxAllowedTimeDiff) { // should not be a permanent state so no spam protection required triggerEvent(power::TIMEDELTA_OUT_OF_BOUNDS, static_cast(timeDiff * 10)); -- 2.43.0 From f61da1002f8354020789159363bbf34a509435d5 Mon Sep 17 00:00:00 2001 From: meggert Date: Mon, 16 Oct 2023 11:38:07 +0200 Subject: [PATCH 02/13] breaking the ACS controller only for Robin --- mission/controller/AcsController.cpp | 20 +++++++ mission/controller/acs/SensorProcessing.cpp | 58 +++++++++++---------- mission/controller/acs/SensorProcessing.h | 17 +++--- 3 files changed, 56 insertions(+), 39 deletions(-) diff --git a/mission/controller/AcsController.cpp b/mission/controller/AcsController.cpp index 1350bcf0..1aac02c2 100644 --- a/mission/controller/AcsController.cpp +++ b/mission/controller/AcsController.cpp @@ -122,6 +122,26 @@ void AcsController::performControlOperation() { } } + timeval timeAbsolute; + Clock::getClock_timeval(&timeAbsolute); + timeval timeRelative; + Clock::getClockMonotonic(&timeRelative); + + ReturnValue_t result = navigation.useSpg4(timeAbsolute, &gpsDataProcessed); + if (result == Sgp4Propagator::TLE_TOO_OLD and not tleTooOldFlag) { + triggerEvent(acs::TLE_TOO_OLD); + tleTooOldFlag = true; + } else if (result != Sgp4Propagator::TLE_TOO_OLD) { + tleTooOldFlag = false; + } + + sensorProcessing.process(now, &sensorValues, &mgmDataProcessed, &susDataProcessed, + &gyrDataProcessed, &gpsDataProcessed, &acsParameters); + fusedRotationEstimation.estimateFusedRotationRateSafe(&susDataProcessed, &mgmDataProcessed, + &gyrDataProcessed, &fusedRotRateData); + result = navigation.useMekf(&sensorValues, &gyrDataProcessed, &mgmDataProcessed, + &susDataProcessed, &mekfData, &acsParameters); + switch (internalState) { case InternalState::STARTUP: { initialCountdown.resetTimer(); diff --git a/mission/controller/acs/SensorProcessing.cpp b/mission/controller/acs/SensorProcessing.cpp index 4405790c..c0d2a128 100644 --- a/mission/controller/acs/SensorProcessing.cpp +++ b/mission/controller/acs/SensorProcessing.cpp @@ -7,7 +7,7 @@ SensorProcessing::~SensorProcessing() {} void SensorProcessing::processMgm(const float *mgm0Value, bool mgm0valid, const float *mgm1Value, bool mgm1valid, const float *mgm2Value, bool mgm2valid, const float *mgm3Value, bool mgm3valid, const float *mgm4Value, - bool mgm4valid, timeval timeOfMgmMeasurement, + bool mgm4valid, timeval timeAbsolute, double timeDelta, const AcsParameters::MgmHandlingParameters *mgmParameters, acsctrl::GpsDataProcessed *gpsDataProcessed, acsctrl::MgmDataProcessed *mgmDataProcessed) { @@ -18,11 +18,12 @@ void SensorProcessing::processMgm(const float *mgm0Value, bool mgm0valid, const if (gpsDataProcessed->source.value != acs::GpsSource::NONE) { Igrf13Model igrf13; igrf13.schmidtNormalization(); - igrf13.updateCoeffGH(timeOfMgmMeasurement); + igrf13.updateCoeffGH(timeAbsolute); + // ToDo // maybe put a condition here, to only update after a full day, this // class function has around 700 steps to perform igrf13.magFieldComp(gpsDataProcessed->gdLongitude.value, gpsDataProcessed->gcLatitude.value, - gpsDataProcessed->altitude.value, timeOfMgmMeasurement, magIgrfModel); + gpsDataProcessed->altitude.value, timeAbsolute, magIgrfModel); gpsValid = true; } if (not mgm0valid and not mgm1valid and not mgm2valid and not mgm3valid and @@ -129,14 +130,12 @@ void SensorProcessing::processMgm(const float *mgm0Value, bool mgm0valid, const //-----------------------Mgm Rate Computation --------------------------------------------------- double mgmVecTotDerivative[3] = {0.0, 0.0, 0.0}; bool mgmVecTotDerivativeValid = false; - double timeDiff = timevalOperations::toDouble(timeOfMgmMeasurement - timeOfSavedMagFieldEst); - if (timeOfSavedMagFieldEst.tv_sec != 0 and timeDiff > 0 and - VectorOperations::norm(savedMgmVecTot, 3) != 0) { + if (timeDelta > 0 and VectorOperations::norm(savedMgmVecTot, 3) != 0) { VectorOperations::subtract(mgmVecTot, savedMgmVecTot, mgmVecTotDerivative, 3); - VectorOperations::mulScalar(mgmVecTotDerivative, 1. / timeDiff, mgmVecTotDerivative, 3); + VectorOperations::mulScalar(mgmVecTotDerivative, 1. / timeDelta, mgmVecTotDerivative, + 3); mgmVecTotDerivativeValid = true; } - timeOfSavedMagFieldEst = timeOfMgmMeasurement; std::memcpy(savedMgmVecTot, mgmVecTot, sizeof(savedMgmVecTot)); if (VectorOperations::norm(mgmVecTotDerivative, 3) != 0 and @@ -177,11 +176,12 @@ void SensorProcessing::processSus( const uint16_t *sus6Value, bool sus6valid, const uint16_t *sus7Value, bool sus7valid, const uint16_t *sus8Value, bool sus8valid, const uint16_t *sus9Value, bool sus9valid, const uint16_t *sus10Value, bool sus10valid, const uint16_t *sus11Value, bool sus11valid, - timeval timeOfSusMeasurement, const AcsParameters::SusHandlingParameters *susParameters, + timeval timeAbsolute, double timeDelta, + const AcsParameters::SusHandlingParameters *susParameters, const AcsParameters::SunModelParameters *sunModelParameters, acsctrl::SusDataProcessed *susDataProcessed) { /* -------- Sun Model Direction (IJK frame) ------- */ - double JD2000 = MathOperations::convertUnixToJD2000(timeOfSusMeasurement); + double JD2000 = MathOperations::convertUnixToJD2000(timeAbsolute); // Julean Centuries double sunIjkModel[3] = {0.0, 0.0, 0.0}; @@ -354,11 +354,10 @@ void SensorProcessing::processSus( double susVecTotDerivative[3] = {0.0, 0.0, 0.0}; bool susVecTotDerivativeValid = false; - double timeDiff = timevalOperations::toDouble(timeOfSusMeasurement - timeOfSavedSusDirEst); - if (timeOfSavedSusDirEst.tv_sec != 0 and timeDiff > 0 and - VectorOperations::norm(savedSusVecTot, 3) != 0) { + if (timeDelta > 0 and VectorOperations::norm(savedSusVecTot, 3) != 0) { VectorOperations::subtract(susVecTot, savedSusVecTot, susVecTotDerivative, 3); - VectorOperations::mulScalar(susVecTotDerivative, 1. / timeDiff, susVecTotDerivative, 3); + VectorOperations::mulScalar(susVecTotDerivative, 1. / timeDelta, susVecTotDerivative, + 3); susVecTotDerivativeValid = true; } std::memcpy(savedSusVecTot, susVecTot, sizeof(savedSusVecTot)); @@ -367,7 +366,6 @@ void SensorProcessing::processSus( lowPassFilter(susVecTotDerivative, susDataProcessed->susVecTotDerivative.value, susParameters->susRateFilterWeight); } - timeOfSavedSusDirEst = timeOfSusMeasurement; { PoolReadGuard pg(susDataProcessed); if (pg.getReadResult() == returnvalue::OK) { @@ -414,7 +412,7 @@ void SensorProcessing::processGyr( const double gyr2axXvalue, bool gyr2axXvalid, const double gyr2axYvalue, bool gyr2axYvalid, const double gyr2axZvalue, bool gyr2axZvalid, const double gyr3axXvalue, bool gyr3axXvalid, const double gyr3axYvalue, bool gyr3axYvalid, const double gyr3axZvalue, bool gyr3axZvalid, - timeval timeOfGyrMeasurement, const AcsParameters::GyrHandlingParameters *gyrParameters, + const AcsParameters::GyrHandlingParameters *gyrParameters, acsctrl::GyrDataProcessed *gyrDataProcessed) { bool gyr0valid = (gyr0axXvalid && gyr0axYvalid && gyr0axZvalid); bool gyr1valid = (gyr1axXvalid && gyr1axYvalid && gyr1axZvalid); @@ -521,7 +519,7 @@ void SensorProcessing::processGyr( } void SensorProcessing::processGps(const double gpsLatitude, const double gpsLongitude, - const double gpsAltitude, const double gpsUnixSeconds, + const double gpsAltitude, const double timeDelta, const bool validGps, const AcsParameters::GpsParameters *gpsParameters, acsctrl::GpsDataProcessed *gpsDataProcessed) { @@ -563,18 +561,14 @@ void SensorProcessing::processGps(const double gpsLatitude, const double gpsLong // Calculation of the satellite velocity in earth fixed frame double deltaDistance[3] = {0, 0, 0}; MathOperations::cartesianFromLatLongAlt(latitudeRad, gdLongitude, altitude, posSatE); - if (validSavedPosSatE and - (gpsUnixSeconds - timeOfSavedPosSatE) < (gpsParameters->timeDiffVelocityMax) and - (gpsUnixSeconds - timeOfSavedPosSatE) > 0) { + if (validSavedPosSatE and timeDelta < (gpsParameters->timeDiffVelocityMax) and timeDelta > 0) { VectorOperations::subtract(posSatE, savedPosSatE, deltaDistance, 3); - double timeDiffGpsMeas = gpsUnixSeconds - timeOfSavedPosSatE; - VectorOperations::mulScalar(deltaDistance, 1. / timeDiffGpsMeas, gpsVelocityE, 3); + VectorOperations::mulScalar(deltaDistance, 1. / timeDelta, gpsVelocityE, 3); } savedPosSatE[0] = posSatE[0]; savedPosSatE[1] = posSatE[1]; savedPosSatE[2] = posSatE[2]; - timeOfSavedPosSatE = gpsUnixSeconds; validSavedPosSatE = true; gpsSource = acs::GpsSource::GPS; @@ -594,13 +588,20 @@ void SensorProcessing::processGps(const double gpsLatitude, const double gpsLong } } -void SensorProcessing::process(timeval now, ACS::SensorValues *sensorValues, +void SensorProcessing::process(timeval timeAbsolute, timeval timeRelative, + ACS::SensorValues *sensorValues, acsctrl::MgmDataProcessed *mgmDataProcessed, acsctrl::SusDataProcessed *susDataProcessed, acsctrl::GyrDataProcessed *gyrDataProcessed, acsctrl::GpsDataProcessed *gpsDataProcessed, const AcsParameters *acsParameters) { sensorValues->update(); + double timeDelta = 0; + if (timeRelative.tv_sec != 0 and savedTimeRelative.tv_sec != 0) { + timeDelta = timevalOperations::toDouble(timeRelative - savedTimeRelative); + } + savedTimeRelative = timeRelative; + processGps( sensorValues->gpsSet.latitude.value, sensorValues->gpsSet.longitude.value, sensorValues->gpsSet.altitude.value, sensorValues->gpsSet.unixSeconds.value, @@ -617,7 +618,8 @@ void SensorProcessing::process(timeval now, ACS::SensorValues *sensorValues, sensorValues->mgm3Rm3100Set.fieldStrengths.value, sensorValues->mgm3Rm3100Set.fieldStrengths.isValid(), sensorValues->imtqMgmSet.mtmRawNt.value, sensorValues->imtqMgmSet.mtmRawNt.isValid(), - now, &acsParameters->mgmHandlingParameters, gpsDataProcessed, mgmDataProcessed); + timeAbsolute, timeDelta, &acsParameters->mgmHandlingParameters, gpsDataProcessed, + mgmDataProcessed); processSus(sensorValues->susSets[0].channels.value, sensorValues->susSets[0].channels.isValid(), sensorValues->susSets[1].channels.value, sensorValues->susSets[1].channels.isValid(), @@ -631,8 +633,8 @@ void SensorProcessing::process(timeval now, ACS::SensorValues *sensorValues, sensorValues->susSets[9].channels.value, sensorValues->susSets[9].channels.isValid(), sensorValues->susSets[10].channels.value, sensorValues->susSets[10].channels.isValid(), sensorValues->susSets[11].channels.value, sensorValues->susSets[11].channels.isValid(), - now, &acsParameters->susHandlingParameters, &acsParameters->sunModelParameters, - susDataProcessed); + timeAbsolute, timeDelta, &acsParameters->susHandlingParameters, + &acsParameters->sunModelParameters, susDataProcessed); processGyr( sensorValues->gyr0AdisSet.angVelocX.value, sensorValues->gyr0AdisSet.angVelocX.isValid(), @@ -646,7 +648,7 @@ void SensorProcessing::process(timeval now, ACS::SensorValues *sensorValues, sensorValues->gyr2AdisSet.angVelocZ.value, sensorValues->gyr2AdisSet.angVelocZ.isValid(), sensorValues->gyr3L3gSet.angVelocX.value, sensorValues->gyr3L3gSet.angVelocX.isValid(), sensorValues->gyr3L3gSet.angVelocY.value, sensorValues->gyr3L3gSet.angVelocY.isValid(), - sensorValues->gyr3L3gSet.angVelocZ.value, sensorValues->gyr3L3gSet.angVelocZ.isValid(), now, + sensorValues->gyr3L3gSet.angVelocZ.value, sensorValues->gyr3L3gSet.angVelocZ.isValid(), &acsParameters->gyrHandlingParameters, gyrDataProcessed); } diff --git a/mission/controller/acs/SensorProcessing.h b/mission/controller/acs/SensorProcessing.h index 77ff2869..70b78845 100644 --- a/mission/controller/acs/SensorProcessing.h +++ b/mission/controller/acs/SensorProcessing.h @@ -24,22 +24,21 @@ class SensorProcessing { SensorProcessing(); virtual ~SensorProcessing(); - void process(timeval now, ACS::SensorValues *sensorValues, + void process(timeval timeAbsolute, timeval timeRelative, ACS::SensorValues *sensorValues, acsctrl::MgmDataProcessed *mgmDataProcessed, acsctrl::SusDataProcessed *susDataProcessed, acsctrl::GyrDataProcessed *gyrDataProcessed, - acsctrl::GpsDataProcessed *gpsDataProcessed, - const AcsParameters *acsParameters); // Will call protected functions + acsctrl::GpsDataProcessed *gpsDataProcessed, const AcsParameters *acsParameters); + private: static constexpr float ZERO_VEC_F[3] = {0, 0, 0}; static constexpr double ZERO_VEC_D[3] = {0, 0, 0}; static constexpr double ECCENTRICITY_WGS84 = 0.0818195; protected: - // short description needed for every function void processMgm(const float *mgm0Value, bool mgm0valid, const float *mgm1Value, bool mgm1valid, const float *mgm2Value, bool mgm2valid, const float *mgm3Value, bool mgm3valid, - const float *mgm4Value, bool mgm4valid, timeval timeOfMgmMeasurement, + const float *mgm4Value, bool mgm4valid, timeval timeAbsolute, double timeDelta, const AcsParameters::MgmHandlingParameters *mgmParameters, acsctrl::GpsDataProcessed *gpsDataProcessed, acsctrl::MgmDataProcessed *mgmDataProcessed); @@ -52,7 +51,7 @@ class SensorProcessing { bool sus7valid, const uint16_t *sus8Value, bool sus8valid, const uint16_t *sus9Value, bool sus9valid, const uint16_t *sus10Value, bool sus10valid, const uint16_t *sus11Value, bool sus11valid, - timeval timeOfSusMeasurement, + timeval timeAbsolute, double timeDelta, const AcsParameters::SusHandlingParameters *susParameters, const AcsParameters::SunModelParameters *sunModelParameters, acsctrl::SusDataProcessed *susDataProcessed); @@ -65,7 +64,6 @@ class SensorProcessing { bool gyr2axYvalid, const double gyr2axZvalue, bool gyr2axZvalid, const double gyr3axXvalue, bool gyr3axXvalid, const double gyr3axYvalue, bool gyr3axYvalid, const double gyr3axZvalue, bool gyr3axZvalid, - timeval timeOfGyrMeasurement, const AcsParameters::GyrHandlingParameters *gyrParameters, acsctrl::GyrDataProcessed *gyrDataProcessed); @@ -76,14 +74,11 @@ class SensorProcessing { void lowPassFilter(double *newValue, double *oldValue, const double weight); + timeval savedTimeRelative; double savedMgmVecTot[3] = {0.0, 0.0, 0.0}; - timeval timeOfSavedMagFieldEst; double savedSusVecTot[3] = {0.0, 0.0, 0.0}; - timeval timeOfSavedSusDirEst; - bool validMagField = false; double savedPosSatE[3] = {0.0, 0.0, 0.0}; - double timeOfSavedPosSatE = 0.0; bool validSavedPosSatE = false; SusConverter susConverter; -- 2.43.0 From 25354ee7b43915df253e536c40c766a0a4e8bcde Mon Sep 17 00:00:00 2001 From: meggert Date: Mon, 16 Oct 2023 11:50:31 +0200 Subject: [PATCH 03/13] i hope i get a medal from Robin for this --- mission/controller/AcsController.cpp | 211 ++++++++++----------------- mission/controller/AcsController.h | 1 + 2 files changed, 78 insertions(+), 134 deletions(-) diff --git a/mission/controller/AcsController.cpp b/mission/controller/AcsController.cpp index 1aac02c2..14a2d83c 100644 --- a/mission/controller/AcsController.cpp +++ b/mission/controller/AcsController.cpp @@ -122,26 +122,6 @@ void AcsController::performControlOperation() { } } - timeval timeAbsolute; - Clock::getClock_timeval(&timeAbsolute); - timeval timeRelative; - Clock::getClockMonotonic(&timeRelative); - - ReturnValue_t result = navigation.useSpg4(timeAbsolute, &gpsDataProcessed); - if (result == Sgp4Propagator::TLE_TOO_OLD and not tleTooOldFlag) { - triggerEvent(acs::TLE_TOO_OLD); - tleTooOldFlag = true; - } else if (result != Sgp4Propagator::TLE_TOO_OLD) { - tleTooOldFlag = false; - } - - sensorProcessing.process(now, &sensorValues, &mgmDataProcessed, &susDataProcessed, - &gyrDataProcessed, &gpsDataProcessed, &acsParameters); - fusedRotationEstimation.estimateFusedRotationRateSafe(&susDataProcessed, &mgmDataProcessed, - &gyrDataProcessed, &fusedRotRateData); - result = navigation.useMekf(&sensorValues, &gyrDataProcessed, &mgmDataProcessed, - &susDataProcessed, &mekfData, &acsParameters); - switch (internalState) { case InternalState::STARTUP: { initialCountdown.resetTimer(); @@ -156,25 +136,7 @@ void AcsController::performControlOperation() { } case InternalState::READY: { if (mode != MODE_OFF) { - switch (mode) { - case acs::SAFE: - switch (submode) { - case SUBMODE_NONE: - performSafe(); - break; - case acs::DETUMBLE: - performDetumble(); - break; - } - break; - case acs::PTG_IDLE: - case acs::PTG_TARGET: - case acs::PTG_TARGET_GS: - case acs::PTG_NADIR: - case acs::PTG_INERTIAL: - performPointingCtrl(); - break; - } + performAttitudeControl(); } break; } @@ -183,39 +145,93 @@ void AcsController::performControlOperation() { } } -void AcsController::performSafe() { - timeval now; - Clock::getClock_timeval(&now); +void AcsController::performAttitudeControl() { + timeval timeAbsolute; + Clock::getClock_timeval(&timeAbsolute); + timeval timeRelative; + Clock::getClockMonotonic(&timeRelative); - ReturnValue_t result = navigation.useSpg4(now, &gpsDataProcessed); + ReturnValue_t result = navigation.useSpg4(timeAbsolute, &gpsDataProcessed); if (result == Sgp4Propagator::TLE_TOO_OLD and not tleTooOldFlag) { triggerEvent(acs::TLE_TOO_OLD); tleTooOldFlag = true; } else if (result != Sgp4Propagator::TLE_TOO_OLD) { tleTooOldFlag = false; } - sensorProcessing.process(now, &sensorValues, &mgmDataProcessed, &susDataProcessed, - &gyrDataProcessed, &gpsDataProcessed, &acsParameters); + + sensorProcessing.process(timeAbsolute, timeRelative, &sensorValues, &mgmDataProcessed, + &susDataProcessed, &gyrDataProcessed, &gpsDataProcessed, &acsParameters); fusedRotationEstimation.estimateFusedRotationRateSafe(&susDataProcessed, &mgmDataProcessed, &gyrDataProcessed, &fusedRotRateData); result = navigation.useMekf(&sensorValues, &gyrDataProcessed, &mgmDataProcessed, &susDataProcessed, &mekfData, &acsParameters); - if (result != MultiplicativeKalmanFilter::MEKF_RUNNING && - result != MultiplicativeKalmanFilter::MEKF_INITIALIZED) { - if (not mekfInvalidFlag) { - triggerEvent(acs::MEKF_INVALID_INFO, (uint32_t)mekfData.mekfStatus.value); - mekfInvalidFlag = true; - } - if (result == MultiplicativeKalmanFilter::MEKF_NOT_FINITE && !mekfLost) { - triggerEvent(acs::MEKF_AUTOMATIC_RESET); - navigation.resetMekf(&mekfData); - mekfLost = true; - } - } else if (mekfInvalidFlag) { - triggerEvent(acs::MEKF_RECOVERY); - mekfInvalidFlag = false; - } + switch (mode) { + case acs::SAFE: + if (result != MultiplicativeKalmanFilter::MEKF_RUNNING && + result != MultiplicativeKalmanFilter::MEKF_INITIALIZED) { + if (not mekfInvalidFlag) { + triggerEvent(acs::MEKF_INVALID_INFO, (uint32_t)mekfData.mekfStatus.value); + mekfInvalidFlag = true; + } + if (result == MultiplicativeKalmanFilter::MEKF_NOT_FINITE && !mekfLost) { + triggerEvent(acs::MEKF_AUTOMATIC_RESET); + navigation.resetMekf(&mekfData); + mekfLost = true; + } + } else if (mekfInvalidFlag) { + triggerEvent(acs::MEKF_RECOVERY); + mekfInvalidFlag = false; + } + switch (submode) { + case SUBMODE_NONE: + performSafe(); + break; + case acs::DETUMBLE: + performDetumble(); + break; + } + break; + case acs::PTG_IDLE: + case acs::PTG_TARGET: + case acs::PTG_TARGET_GS: + case acs::PTG_NADIR: + case acs::PTG_INERTIAL: + if (result != MultiplicativeKalmanFilter::MEKF_RUNNING && + result != MultiplicativeKalmanFilter::MEKF_INITIALIZED) { + mekfInvalidCounter++; + if (not mekfInvalidFlag) { + triggerEvent(acs::MEKF_INVALID_INFO, (uint32_t)mekfData.mekfStatus.value); + mekfInvalidFlag = true; + } + if (result == MultiplicativeKalmanFilter::MEKF_NOT_FINITE && !mekfLost) { + triggerEvent(acs::MEKF_AUTOMATIC_RESET); + navigation.resetMekf(&mekfData); + mekfLost = true; + } + if (mekfInvalidCounter > acsParameters.onBoardParams.mekfViolationTimer) { + // Trigger this so STR FDIR can set the device faulty. + EventManagerIF::triggerEvent(objects::STAR_TRACKER, acs::MEKF_INVALID_MODE_VIOLATION, 0, + 0); + mekfInvalidCounter = 0; + } + commandActuators(0, 0, 0, acsParameters.magnetorquerParameter.torqueDuration, + cmdSpeedRws[0], cmdSpeedRws[1], cmdSpeedRws[2], cmdSpeedRws[3], + acsParameters.rwHandlingParameters.rampTime); + return; + } else { + if (mekfInvalidFlag) { + triggerEvent(acs::MEKF_RECOVERY); + mekfInvalidFlag = false; + } + mekfInvalidCounter = 0; + } + performPointingCtrl(); + break; + } +} + +void AcsController::performSafe() { // get desired satellite rate, sun direction to align to and inertia double sunTargetDir[3] = {0, 0, 0}; guidance.getTargetParamsSafe(sunTargetDir); @@ -316,37 +332,6 @@ void AcsController::performSafe() { } void AcsController::performDetumble() { - timeval now; - Clock::getClock_timeval(&now); - - ReturnValue_t result = navigation.useSpg4(now, &gpsDataProcessed); - if (result == Sgp4Propagator::TLE_TOO_OLD and not tleTooOldFlag) { - triggerEvent(acs::TLE_TOO_OLD); - tleTooOldFlag = true; - } else { - tleTooOldFlag = false; - } - sensorProcessing.process(now, &sensorValues, &mgmDataProcessed, &susDataProcessed, - &gyrDataProcessed, &gpsDataProcessed, &acsParameters); - fusedRotationEstimation.estimateFusedRotationRateSafe(&susDataProcessed, &mgmDataProcessed, - &gyrDataProcessed, &fusedRotRateData); - result = navigation.useMekf(&sensorValues, &gyrDataProcessed, &mgmDataProcessed, - &susDataProcessed, &mekfData, &acsParameters); - if (result != MultiplicativeKalmanFilter::MEKF_RUNNING && - result != MultiplicativeKalmanFilter::MEKF_INITIALIZED) { - if (not mekfInvalidFlag) { - triggerEvent(acs::MEKF_INVALID_INFO, (uint32_t)mekfData.mekfStatus.value); - mekfInvalidFlag = true; - } - if (result == MultiplicativeKalmanFilter::MEKF_NOT_FINITE && !mekfLost) { - triggerEvent(acs::MEKF_AUTOMATIC_RESET); - navigation.resetMekf(&mekfData); - mekfLost = true; - } - } else if (mekfInvalidFlag) { - triggerEvent(acs::MEKF_RECOVERY); - mekfInvalidFlag = false; - } acs::SafeModeStrategy safeCtrlStrat = detumble.detumbleStrategy( mgmDataProcessed.mgmVecTot.isValid(), gyrDataProcessed.gyrVecTot.isValid(), mgmDataProcessed.mgmVecTotDerivative.isValid(), @@ -409,51 +394,9 @@ void AcsController::performDetumble() { } void AcsController::performPointingCtrl() { - timeval now; - Clock::getClock_timeval(&now); - - ReturnValue_t result = navigation.useSpg4(now, &gpsDataProcessed); - if (result == Sgp4Propagator::TLE_TOO_OLD and not tleTooOldFlag) { - triggerEvent(acs::TLE_TOO_OLD); - tleTooOldFlag = true; - } else { - tleTooOldFlag = false; - } - sensorProcessing.process(now, &sensorValues, &mgmDataProcessed, &susDataProcessed, - &gyrDataProcessed, &gpsDataProcessed, &acsParameters); - result = navigation.useMekf(&sensorValues, &gyrDataProcessed, &mgmDataProcessed, - &susDataProcessed, &mekfData, &acsParameters); - if (result != MultiplicativeKalmanFilter::MEKF_RUNNING && - result != MultiplicativeKalmanFilter::MEKF_INITIALIZED) { - mekfInvalidCounter++; - if (not mekfInvalidFlag) { - triggerEvent(acs::MEKF_INVALID_INFO, (uint32_t)mekfData.mekfStatus.value); - mekfInvalidFlag = true; - } - if (result == MultiplicativeKalmanFilter::MEKF_NOT_FINITE && !mekfLost) { - triggerEvent(acs::MEKF_AUTOMATIC_RESET); - navigation.resetMekf(&mekfData); - mekfLost = true; - } - if (mekfInvalidCounter > acsParameters.onBoardParams.mekfViolationTimer) { - // Trigger this so STR FDIR can set the device faulty. - EventManagerIF::triggerEvent(objects::STAR_TRACKER, acs::MEKF_INVALID_MODE_VIOLATION, 0, 0); - mekfInvalidCounter = 0; - } - commandActuators(0, 0, 0, acsParameters.magnetorquerParameter.torqueDuration, cmdSpeedRws[0], - cmdSpeedRws[1], cmdSpeedRws[2], cmdSpeedRws[3], - acsParameters.rwHandlingParameters.rampTime); - return; - } else { - if (mekfInvalidFlag) { - triggerEvent(acs::MEKF_RECOVERY); - mekfInvalidFlag = false; - } - mekfInvalidCounter = 0; - } uint8_t enableAntiStiction = true; double rwPseudoInv[4][3] = {{0, 0, 0}, {0, 0, 0}, {0, 0, 0}, {0, 0, 0}}; - result = guidance.getDistributionMatrixRw(&sensorValues, *rwPseudoInv); + ReturnValue_t result = guidance.getDistributionMatrixRw(&sensorValues, *rwPseudoInv); if (result == returnvalue::FAILED) { if (multipleRwUnavailableCounter >= acsParameters.rwHandlingParameters.multipleRwInvalidTimeout) { diff --git a/mission/controller/AcsController.h b/mission/controller/AcsController.h index e03f2a55..89919ccc 100644 --- a/mission/controller/AcsController.h +++ b/mission/controller/AcsController.h @@ -37,6 +37,7 @@ class AcsController : public ExtendedControllerBase, public ReceivesParameterMes uint16_t startAtIndex) override; protected: + void performAttitudeControl(); void performSafe(); void performDetumble(); void performPointingCtrl(); -- 2.43.0 From 17c253d19b3ec20593ebdefe06aeeaa0b2dbddf0 Mon Sep 17 00:00:00 2001 From: meggert Date: Mon, 16 Oct 2023 13:26:56 +0200 Subject: [PATCH 04/13] this might just work --- mission/controller/AcsController.cpp | 29 +++++++------ mission/controller/AcsController.h | 5 +++ mission/controller/acs/Guidance.cpp | 45 ++++++++++----------- mission/controller/acs/Guidance.h | 28 ++++++------- mission/controller/acs/SensorProcessing.cpp | 7 +--- mission/controller/acs/SensorProcessing.h | 3 +- 6 files changed, 58 insertions(+), 59 deletions(-) diff --git a/mission/controller/AcsController.cpp b/mission/controller/AcsController.cpp index 14a2d83c..89853b9f 100644 --- a/mission/controller/AcsController.cpp +++ b/mission/controller/AcsController.cpp @@ -146,11 +146,14 @@ void AcsController::performControlOperation() { } void AcsController::performAttitudeControl() { - timeval timeAbsolute; Clock::getClock_timeval(&timeAbsolute); - timeval timeRelative; Clock::getClockMonotonic(&timeRelative); + if (timeRelative.tv_sec != 0 and oldTimeRelative.tv_sec != 0) { + timeDelta = timevalOperations::toDouble(timeRelative - oldTimeRelative); + } + oldTimeRelative = timeRelative; + ReturnValue_t result = navigation.useSpg4(timeAbsolute, &gpsDataProcessed); if (result == Sgp4Propagator::TLE_TOO_OLD and not tleTooOldFlag) { triggerEvent(acs::TLE_TOO_OLD); @@ -159,7 +162,7 @@ void AcsController::performAttitudeControl() { tleTooOldFlag = false; } - sensorProcessing.process(timeAbsolute, timeRelative, &sensorValues, &mgmDataProcessed, + sensorProcessing.process(timeAbsolute, timeDelta, &sensorValues, &mgmDataProcessed, &susDataProcessed, &gyrDataProcessed, &gpsDataProcessed, &acsParameters); fusedRotationEstimation.estimateFusedRotationRateSafe(&susDataProcessed, &mgmDataProcessed, &gyrDataProcessed, &fusedRotRateData); @@ -168,13 +171,13 @@ void AcsController::performAttitudeControl() { switch (mode) { case acs::SAFE: - if (result != MultiplicativeKalmanFilter::MEKF_RUNNING && + if (result != MultiplicativeKalmanFilter::MEKF_RUNNING and result != MultiplicativeKalmanFilter::MEKF_INITIALIZED) { if (not mekfInvalidFlag) { triggerEvent(acs::MEKF_INVALID_INFO, (uint32_t)mekfData.mekfStatus.value); mekfInvalidFlag = true; } - if (result == MultiplicativeKalmanFilter::MEKF_NOT_FINITE && !mekfLost) { + if (result == MultiplicativeKalmanFilter::MEKF_NOT_FINITE and not mekfLost) { triggerEvent(acs::MEKF_AUTOMATIC_RESET); navigation.resetMekf(&mekfData); mekfLost = true; @@ -197,14 +200,14 @@ void AcsController::performAttitudeControl() { case acs::PTG_TARGET_GS: case acs::PTG_NADIR: case acs::PTG_INERTIAL: - if (result != MultiplicativeKalmanFilter::MEKF_RUNNING && + if (result != MultiplicativeKalmanFilter::MEKF_RUNNING and result != MultiplicativeKalmanFilter::MEKF_INITIALIZED) { mekfInvalidCounter++; if (not mekfInvalidFlag) { triggerEvent(acs::MEKF_INVALID_INFO, (uint32_t)mekfData.mekfStatus.value); mekfInvalidFlag = true; } - if (result == MultiplicativeKalmanFilter::MEKF_NOT_FINITE && !mekfLost) { + if (result == MultiplicativeKalmanFilter::MEKF_NOT_FINITE and not mekfLost) { triggerEvent(acs::MEKF_AUTOMATIC_RESET); navigation.resetMekf(&mekfData); mekfLost = true; @@ -418,7 +421,7 @@ void AcsController::performPointingCtrl() { switch (mode) { case acs::PTG_IDLE: - guidance.targetQuatPtgSun(now, susDataProcessed.sunIjkModel.value, targetQuat, + guidance.targetQuatPtgSun(timeDelta, susDataProcessed.sunIjkModel.value, targetQuat, targetSatRotRate); guidance.comparePtg(mekfData.quatMekf.value, mekfData.satRotRateMekf.value, targetQuat, targetSatRotRate, errorQuat, errorSatRotRate, errorAngle); @@ -439,7 +442,7 @@ void AcsController::performPointingCtrl() { break; case acs::PTG_TARGET: - guidance.targetQuatPtgThreeAxes(now, gpsDataProcessed.gpsPosition.value, + guidance.targetQuatPtgThreeAxes(timeAbsolute, timeDelta, gpsDataProcessed.gpsPosition.value, gpsDataProcessed.gpsVelocity.value, targetQuat, targetSatRotRate); guidance.comparePtg(mekfData.quatMekf.value, mekfData.satRotRateMekf.value, targetQuat, @@ -463,7 +466,7 @@ void AcsController::performPointingCtrl() { break; case acs::PTG_TARGET_GS: - guidance.targetQuatPtgGs(now, gpsDataProcessed.gpsPosition.value, + guidance.targetQuatPtgGs(timeAbsolute, timeDelta, gpsDataProcessed.gpsPosition.value, susDataProcessed.sunIjkModel.value, targetQuat, targetSatRotRate); guidance.comparePtg(mekfData.quatMekf.value, mekfData.satRotRateMekf.value, targetQuat, targetSatRotRate, errorQuat, errorSatRotRate, errorAngle); @@ -484,9 +487,9 @@ void AcsController::performPointingCtrl() { break; case acs::PTG_NADIR: - guidance.targetQuatPtgNadirThreeAxes(now, gpsDataProcessed.gpsPosition.value, - gpsDataProcessed.gpsVelocity.value, targetQuat, - targetSatRotRate); + guidance.targetQuatPtgNadirThreeAxes( + timeAbsolute, timeDelta, gpsDataProcessed.gpsPosition.value, + gpsDataProcessed.gpsVelocity.value, targetQuat, targetSatRotRate); guidance.comparePtg(mekfData.quatMekf.value, mekfData.satRotRateMekf.value, targetQuat, targetSatRotRate, acsParameters.nadirModeControllerParameters.quatRef, acsParameters.nadirModeControllerParameters.refRotRate, errorQuat, diff --git a/mission/controller/AcsController.h b/mission/controller/AcsController.h index 89919ccc..1b184b80 100644 --- a/mission/controller/AcsController.h +++ b/mission/controller/AcsController.h @@ -50,6 +50,11 @@ class AcsController : public ExtendedControllerBase, public ReceivesParameterMes bool enableHkSets = false; + timeval timeAbsolute; + timeval timeRelative; + double timeDelta = 0.0; + timeval oldTimeRelative; + AcsParameters acsParameters; SensorProcessing sensorProcessing; FusedRotationEstimation fusedRotationEstimation; diff --git a/mission/controller/acs/Guidance.cpp b/mission/controller/acs/Guidance.cpp index 1d82d317..b483372d 100644 --- a/mission/controller/acs/Guidance.cpp +++ b/mission/controller/acs/Guidance.cpp @@ -136,8 +136,9 @@ void Guidance::targetQuatPtgSingleAxis(timeval now, double posSatE[3], double ve QuaternionOperations::multiply(quatIB, targetQuat, targetQuat); } -void Guidance::targetQuatPtgThreeAxes(timeval now, double posSatE[3], double velSatE[3], - double targetQuat[4], double targetSatRotRate[3]) { +void Guidance::targetQuatPtgThreeAxes(const timeval timeAbsolute, const double timeDelta, + double posSatE[3], double velSatE[3], double targetQuat[4], + double targetSatRotRate[3]) { //------------------------------------------------------------------------------------- // Calculation of target quaternion for target pointing //------------------------------------------------------------------------------------- @@ -154,7 +155,7 @@ void Guidance::targetQuatPtgThreeAxes(timeval now, double posSatE[3], double vel double dcmEI[3][3] = {{0, 0, 0}, {0, 0, 0}, {0, 0, 0}}; double dcmIE[3][3] = {{0, 0, 0}, {0, 0, 0}, {0, 0, 0}}; double dcmEIDot[3][3] = {{0, 0, 0}, {0, 0, 0}, {0, 0, 0}}; - MathOperations::ecfToEciWithNutPre(now, *dcmEI, *dcmEIDot); + MathOperations::ecfToEciWithNutPre(timeAbsolute, *dcmEI, *dcmEIDot); MathOperations::inverseMatrixDimThree(*dcmEI, *dcmIE); double dcmIEDot[3][3] = {{0, 0, 0}, {0, 0, 0}, {0, 0, 0}}; @@ -199,11 +200,12 @@ void Guidance::targetQuatPtgThreeAxes(timeval now, double posSatE[3], double vel QuaternionOperations::fromDcm(dcmIX, targetQuat); int8_t timeElapsedMax = acsParameters->targetModeControllerParameters.timeElapsedMax; - targetRotationRate(timeElapsedMax, now, targetQuat, targetSatRotRate); + targetRotationRate(timeElapsedMax, timeDelta, targetQuat, targetSatRotRate); } -void Guidance::targetQuatPtgGs(timeval now, double posSatE[3], double sunDirI[3], - double targetQuat[4], double targetSatRotRate[3]) { +void Guidance::targetQuatPtgGs(const timeval timeAbsolute, const double timeDelta, + double posSatE[3], double sunDirI[3], double targetQuat[4], + double targetSatRotRate[3]) { //------------------------------------------------------------------------------------- // Calculation of target quaternion for ground station pointing //------------------------------------------------------------------------------------- @@ -221,7 +223,7 @@ void Guidance::targetQuatPtgGs(timeval now, double posSatE[3], double sunDirI[3] double dcmEI[3][3] = {{0, 0, 0}, {0, 0, 0}, {0, 0, 0}}; double dcmIE[3][3] = {{0, 0, 0}, {0, 0, 0}, {0, 0, 0}}; double dcmEIDot[3][3] = {{0, 0, 0}, {0, 0, 0}, {0, 0, 0}}; - MathOperations::ecfToEciWithNutPre(now, *dcmEI, *dcmEIDot); + MathOperations::ecfToEciWithNutPre(timeAbsolute, *dcmEI, *dcmEIDot); MathOperations::inverseMatrixDimThree(*dcmEI, *dcmIE); double dcmIEDot[3][3] = {{0, 0, 0}, {0, 0, 0}, {0, 0, 0}}; @@ -263,10 +265,10 @@ void Guidance::targetQuatPtgGs(timeval now, double posSatE[3], double sunDirI[3] QuaternionOperations::fromDcm(dcmTgt, targetQuat); int8_t timeElapsedMax = acsParameters->gsTargetModeControllerParameters.timeElapsedMax; - targetRotationRate(timeElapsedMax, now, targetQuat, targetSatRotRate); + targetRotationRate(timeElapsedMax, timeDelta, targetQuat, targetSatRotRate); } -void Guidance::targetQuatPtgSun(timeval now, double sunDirI[3], double targetQuat[4], +void Guidance::targetQuatPtgSun(double timeDelta, double sunDirI[3], double targetQuat[4], double targetSatRotRate[3]) { //------------------------------------------------------------------------------------- // Calculation of target quaternion to sun @@ -298,7 +300,7 @@ void Guidance::targetQuatPtgSun(timeval now, double sunDirI[3], double targetQua // Calculation of reference rotation rate //---------------------------------------------------------------------------- int8_t timeElapsedMax = acsParameters->gsTargetModeControllerParameters.timeElapsedMax; - targetRotationRate(timeElapsedMax, now, targetQuat, targetSatRotRate); + targetRotationRate(timeElapsedMax, timeDelta, targetQuat, targetSatRotRate); } void Guidance::targetQuatPtgNadirSingleAxis(timeval now, double posSatE[3], double quatBI[4], @@ -362,7 +364,8 @@ void Guidance::targetQuatPtgNadirSingleAxis(timeval now, double posSatE[3], doub QuaternionOperations::multiply(quatIB, targetQuat, targetQuat); } -void Guidance::targetQuatPtgNadirThreeAxes(timeval now, double posSatE[3], double velSatE[3], +void Guidance::targetQuatPtgNadirThreeAxes(const timeval timeAbsolute, const double timeDelta, + double posSatE[3], double velSatE[3], double targetQuat[4], double refSatRate[3]) { //------------------------------------------------------------------------------------- // Calculation of target quaternion for Nadir pointing @@ -371,7 +374,7 @@ void Guidance::targetQuatPtgNadirThreeAxes(timeval now, double posSatE[3], doubl double dcmEI[3][3] = {{0, 0, 0}, {0, 0, 0}, {0, 0, 0}}; double dcmIE[3][3] = {{0, 0, 0}, {0, 0, 0}, {0, 0, 0}}; double dcmEIDot[3][3] = {{0, 0, 0}, {0, 0, 0}, {0, 0, 0}}; - MathOperations::ecfToEciWithNutPre(now, *dcmEI, *dcmEIDot); + MathOperations::ecfToEciWithNutPre(timeAbsolute, *dcmEI, *dcmEIDot); MathOperations::inverseMatrixDimThree(*dcmEI, *dcmIE); double dcmIEDot[3][3] = {{0, 0, 0}, {0, 0, 0}, {0, 0, 0}}; @@ -407,7 +410,7 @@ void Guidance::targetQuatPtgNadirThreeAxes(timeval now, double posSatE[3], doubl QuaternionOperations::fromDcm(dcmTgt, targetQuat); int8_t timeElapsedMax = acsParameters->nadirModeControllerParameters.timeElapsedMax; - targetRotationRate(timeElapsedMax, now, targetQuat, refSatRate); + targetRotationRate(timeElapsedMax, timeDelta, targetQuat, refSatRate); } void Guidance::comparePtg(double currentQuat[4], double currentSatRotRate[3], double targetQuat[4], @@ -448,23 +451,21 @@ void Guidance::comparePtg(double currentQuat[4], double currentSatRotRate[3], do VectorOperations::subtract(currentSatRotRate, targetSatRotRate, errorSatRotRate, 3); } -void Guidance::targetRotationRate(int8_t timeElapsedMax, timeval now, double quatInertialTarget[4], - double *refSatRate) { +void Guidance::targetRotationRate(const int8_t timeElapsedMax, const double timeDelta, + double quatInertialTarget[4], double *refSatRate) { //------------------------------------------------------------------------------------- // Calculation of target rotation rate //------------------------------------------------------------------------------------- - double timeElapsed = now.tv_sec + now.tv_usec * 1e-6 - - (timeSavedQuaternion.tv_sec + timeSavedQuaternion.tv_usec * 1e-6); if (VectorOperations::norm(savedQuaternion, 4) == 0) { std::memcpy(savedQuaternion, quatInertialTarget, sizeof(savedQuaternion)); } - if (timeElapsed < timeElapsedMax) { + if (timeDelta < timeElapsedMax) { double q[4] = {0, 0, 0, 0}, qS[4] = {0, 0, 0, 0}; QuaternionOperations::inverse(quatInertialTarget, q); QuaternionOperations::inverse(savedQuaternion, qS); double qDiff[4] = {0, 0, 0, 0}; VectorOperations::subtract(q, qS, qDiff, 4); - VectorOperations::mulScalar(qDiff, 1 / timeElapsed, qDiff, 4); + VectorOperations::mulScalar(qDiff, 1 / timeDelta, qDiff, 4); double tgtQuatVec[3] = {q[0], q[1], q[2]}; double qDiffVec[3] = {qDiff[0], qDiff[1], qDiff[2]}; @@ -488,11 +489,7 @@ void Guidance::targetRotationRate(int8_t timeElapsedMax, timeval now, double qua refSatRate[2] = 0; } - timeSavedQuaternion = now; - savedQuaternion[0] = quatInertialTarget[0]; - savedQuaternion[1] = quatInertialTarget[1]; - savedQuaternion[2] = quatInertialTarget[2]; - savedQuaternion[3] = quatInertialTarget[3]; + std::memcpy(savedQuaternion, quatInertialTarget, sizeof(savedQuaternion)); } ReturnValue_t Guidance::getDistributionMatrixRw(ACS::SensorValues *sensorValues, diff --git a/mission/controller/acs/Guidance.h b/mission/controller/acs/Guidance.h index 7b81e411..371ae147 100644 --- a/mission/controller/acs/Guidance.h +++ b/mission/controller/acs/Guidance.h @@ -17,25 +17,26 @@ class Guidance { // Function to get the target quaternion and reference rotation rate from gps position and // position of the ground station - void targetQuatPtgSingleAxis(timeval now, double posSatE[3], double velSatE[3], double sunDirI[3], - double refDirB[3], double quatBI[4], double targetQuat[4], - double targetSatRotRate[3]); - void targetQuatPtgThreeAxes(timeval now, double posSatE[3], double velSatE[3], double quatIX[4], - double targetSatRotRate[3]); - void targetQuatPtgGs(timeval now, double posSatE[3], double sunDirI[3], double quatIX[4], - double targetSatRotRate[3]); + void targetQuatPtgSingleAxis(const double timeDelta, double posSatE[3], double velSatE[3], + double sunDirI[3], double refDirB[3], double quatBI[4], + double targetQuat[4], double targetSatRotRate[3]); + void targetQuatPtgThreeAxes(const timeval timeAbsolute, const double timeDelta, double posSatE[3], + double velSatE[3], double quatIX[4], double targetSatRotRate[3]); + void targetQuatPtgGs(const timeval timeAbsolute, const double timeDelta, double posSatE[3], + double sunDirI[3], double quatIX[4], double targetSatRotRate[3]); // Function to get the target quaternion and reference rotation rate for sun pointing after ground // station - void targetQuatPtgSun(timeval now, double sunDirI[3], double targetQuat[4], + void targetQuatPtgSun(const double timeDelta, double sunDirI[3], double targetQuat[4], double targetSatRotRate[3]); // Function to get the target quaternion and refence rotation rate from gps position for Nadir // pointing - void targetQuatPtgNadirSingleAxis(timeval now, double posSatE[3], double quatBI[4], + void targetQuatPtgNadirSingleAxis(const double timeDelta, double posSatE[3], double quatBI[4], double targetQuat[4], double refDirB[3], double refSatRate[3]); - void targetQuatPtgNadirThreeAxes(timeval now, double posSatE[3], double velSatE[3], - double targetQuat[4], double refSatRate[3]); + void targetQuatPtgNadirThreeAxes(const timeval timeAbsolute, const double timeDelta, + double posSatE[3], double velSatE[3], double targetQuat[4], + double refSatRate[3]); // @note: Calculates the error quaternion between the current orientation and the target // quaternion, considering a reference quaternion. Additionally the difference between the actual @@ -48,8 +49,8 @@ class Guidance { double targetSatRotRate[3], double errorQuat[4], double errorSatRotRate[3], double &errorAngle); - void targetRotationRate(int8_t timeElapsedMax, timeval now, double quatInertialTarget[4], - double *targetSatRotRate); + void targetRotationRate(const int8_t timeElapsedMax, const double timeDelta, + double quatInertialTarget[4], double *targetSatRotRate); // @note: will give back the pseudoinverse matrix for the reaction wheel depending on the valid // reation wheel maybe can be done in "commanding.h" @@ -59,7 +60,6 @@ class Guidance { const AcsParameters *acsParameters; bool strBlindAvoidFlag = false; - timeval timeSavedQuaternion; double savedQuaternion[4] = {0, 0, 0, 0}; double omegaRefSaved[3] = {0, 0, 0}; diff --git a/mission/controller/acs/SensorProcessing.cpp b/mission/controller/acs/SensorProcessing.cpp index c0d2a128..56217a8f 100644 --- a/mission/controller/acs/SensorProcessing.cpp +++ b/mission/controller/acs/SensorProcessing.cpp @@ -588,7 +588,7 @@ void SensorProcessing::processGps(const double gpsLatitude, const double gpsLong } } -void SensorProcessing::process(timeval timeAbsolute, timeval timeRelative, +void SensorProcessing::process(timeval timeAbsolute, double timeDelta, ACS::SensorValues *sensorValues, acsctrl::MgmDataProcessed *mgmDataProcessed, acsctrl::SusDataProcessed *susDataProcessed, @@ -596,11 +596,6 @@ void SensorProcessing::process(timeval timeAbsolute, timeval timeRelative, acsctrl::GpsDataProcessed *gpsDataProcessed, const AcsParameters *acsParameters) { sensorValues->update(); - double timeDelta = 0; - if (timeRelative.tv_sec != 0 and savedTimeRelative.tv_sec != 0) { - timeDelta = timevalOperations::toDouble(timeRelative - savedTimeRelative); - } - savedTimeRelative = timeRelative; processGps( sensorValues->gpsSet.latitude.value, sensorValues->gpsSet.longitude.value, diff --git a/mission/controller/acs/SensorProcessing.h b/mission/controller/acs/SensorProcessing.h index 70b78845..fea0fd01 100644 --- a/mission/controller/acs/SensorProcessing.h +++ b/mission/controller/acs/SensorProcessing.h @@ -24,7 +24,7 @@ class SensorProcessing { SensorProcessing(); virtual ~SensorProcessing(); - void process(timeval timeAbsolute, timeval timeRelative, ACS::SensorValues *sensorValues, + void process(timeval timeAbsolute, double timeDelta, ACS::SensorValues *sensorValues, acsctrl::MgmDataProcessed *mgmDataProcessed, acsctrl::SusDataProcessed *susDataProcessed, acsctrl::GyrDataProcessed *gyrDataProcessed, @@ -74,7 +74,6 @@ class SensorProcessing { void lowPassFilter(double *newValue, double *oldValue, const double weight); - timeval savedTimeRelative; double savedMgmVecTot[3] = {0.0, 0.0, 0.0}; double savedSusVecTot[3] = {0.0, 0.0, 0.0}; -- 2.43.0 From 823aa709542bd202b83ceab0f84894795f3ec645 Mon Sep 17 00:00:00 2001 From: meggert Date: Mon, 16 Oct 2023 17:01:42 +0200 Subject: [PATCH 05/13] fixed deprecated functions --- mission/controller/acs/Guidance.cpp | 19 +++++++++++-------- mission/controller/acs/Guidance.h | 4 ++-- 2 files changed, 13 insertions(+), 10 deletions(-) diff --git a/mission/controller/acs/Guidance.cpp b/mission/controller/acs/Guidance.cpp index b483372d..6067676e 100644 --- a/mission/controller/acs/Guidance.cpp +++ b/mission/controller/acs/Guidance.cpp @@ -16,9 +16,11 @@ Guidance::Guidance(AcsParameters *acsParameters_) { acsParameters = acsParameter Guidance::~Guidance() {} -void Guidance::targetQuatPtgSingleAxis(timeval now, double posSatE[3], double velSatE[3], - double sunDirI[3], double refDirB[3], double quatBI[4], - double targetQuat[4], double targetSatRotRate[3]) { +[[deprecated]] void Guidance::targetQuatPtgSingleAxis(const timeval timeAbsolute, double posSatE[3], + double velSatE[3], double sunDirI[3], + double refDirB[3], double quatBI[4], + double targetQuat[4], + double targetSatRotRate[3]) { //------------------------------------------------------------------------------------- // Calculation of target quaternion to groundstation or given latitude, longitude and altitude //------------------------------------------------------------------------------------- @@ -38,7 +40,7 @@ void Guidance::targetQuatPtgSingleAxis(timeval now, double posSatE[3], double ve double dcmEI[3][3] = {{0, 0, 0}, {0, 0, 0}, {0, 0, 0}}; double dcmIE[3][3] = {{0, 0, 0}, {0, 0, 0}, {0, 0, 0}}; double dcmEIDot[3][3] = {{0, 0, 0}, {0, 0, 0}, {0, 0, 0}}; - MathOperations::ecfToEciWithNutPre(now, *dcmEI, *dcmEIDot); + MathOperations::ecfToEciWithNutPre(timeAbsolute, *dcmEI, *dcmEIDot); MathOperations::inverseMatrixDimThree(*dcmEI, *dcmIE); double dcmIEDot[3][3] = {{0, 0, 0}, {0, 0, 0}, {0, 0, 0}}; @@ -303,9 +305,10 @@ void Guidance::targetQuatPtgSun(double timeDelta, double sunDirI[3], double targ targetRotationRate(timeElapsedMax, timeDelta, targetQuat, targetSatRotRate); } -void Guidance::targetQuatPtgNadirSingleAxis(timeval now, double posSatE[3], double quatBI[4], - double targetQuat[4], double refDirB[3], - double refSatRate[3]) { +[[deprecated]] void Guidance::targetQuatPtgNadirSingleAxis(const timeval timeAbsolute, + double posSatE[3], double quatBI[4], + double targetQuat[4], double refDirB[3], + double refSatRate[3]) { //------------------------------------------------------------------------------------- // Calculation of target quaternion for Nadir pointing //------------------------------------------------------------------------------------- @@ -316,7 +319,7 @@ void Guidance::targetQuatPtgNadirSingleAxis(timeval now, double posSatE[3], doub double dcmEI[3][3] = {{0, 0, 0}, {0, 0, 0}, {0, 0, 0}}; double dcmIE[3][3] = {{0, 0, 0}, {0, 0, 0}, {0, 0, 0}}; double dcmEIDot[3][3] = {{0, 0, 0}, {0, 0, 0}, {0, 0, 0}}; - MathOperations::ecfToEciWithNutPre(now, *dcmEI, *dcmEIDot); + MathOperations::ecfToEciWithNutPre(timeAbsolute, *dcmEI, *dcmEIDot); MathOperations::inverseMatrixDimThree(*dcmEI, *dcmIE); double dcmIEDot[3][3] = {{0, 0, 0}, {0, 0, 0}, {0, 0, 0}}; diff --git a/mission/controller/acs/Guidance.h b/mission/controller/acs/Guidance.h index 371ae147..1cf5b8a9 100644 --- a/mission/controller/acs/Guidance.h +++ b/mission/controller/acs/Guidance.h @@ -17,7 +17,7 @@ class Guidance { // Function to get the target quaternion and reference rotation rate from gps position and // position of the ground station - void targetQuatPtgSingleAxis(const double timeDelta, double posSatE[3], double velSatE[3], + void targetQuatPtgSingleAxis(const timeval timeAbsolute, double posSatE[3], double velSatE[3], double sunDirI[3], double refDirB[3], double quatBI[4], double targetQuat[4], double targetSatRotRate[3]); void targetQuatPtgThreeAxes(const timeval timeAbsolute, const double timeDelta, double posSatE[3], @@ -32,7 +32,7 @@ class Guidance { // Function to get the target quaternion and refence rotation rate from gps position for Nadir // pointing - void targetQuatPtgNadirSingleAxis(const double timeDelta, double posSatE[3], double quatBI[4], + void targetQuatPtgNadirSingleAxis(const timeval timeAbsolute, double posSatE[3], double quatBI[4], double targetQuat[4], double refDirB[3], double refSatRate[3]); void targetQuatPtgNadirThreeAxes(const timeval timeAbsolute, const double timeDelta, double posSatE[3], double velSatE[3], double targetQuat[4], -- 2.43.0 From 9482ceb2064216390e6b5a765a66a165ef9043a9 Mon Sep 17 00:00:00 2001 From: meggert Date: Tue, 17 Oct 2023 17:01:29 +0200 Subject: [PATCH 06/13] noice --- mission/controller/acs/Guidance.cpp | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/mission/controller/acs/Guidance.cpp b/mission/controller/acs/Guidance.cpp index 6067676e..fe0ba4dd 100644 --- a/mission/controller/acs/Guidance.cpp +++ b/mission/controller/acs/Guidance.cpp @@ -4,13 +4,11 @@ #include #include #include -#include +#include +#include #include - -#include "string.h" -#include "util/CholeskyDecomposition.h" -#include "util/MathOperations.h" +#include Guidance::Guidance(AcsParameters *acsParameters_) { acsParameters = acsParameters_; } -- 2.43.0 From 77a555debc13ba746a6001d97f3120209d232a8f Mon Sep 17 00:00:00 2001 From: meggert Date: Wed, 18 Oct 2023 11:00:43 +0200 Subject: [PATCH 07/13] small improvement --- mission/controller/PowerController.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/mission/controller/PowerController.cpp b/mission/controller/PowerController.cpp index e2862b80..f8a02c09 100644 --- a/mission/controller/PowerController.cpp +++ b/mission/controller/PowerController.cpp @@ -287,7 +287,7 @@ ReturnValue_t PowerController::calculateOpenCircuitVoltageCharge() { ReturnValue_t PowerController::calculateCoulombCounterCharge() { double timeDiff = 0.0; - if (timevalOperations::toDouble(oldTime) != 0) { + if (oldTime.tv_sec != 0) { timeDiff = timevalOperations::toDouble(now - oldTime); } else { return returnvalue::FAILED; -- 2.43.0 From d5e57501befabb25347d6c8f9557f235997c18ec Mon Sep 17 00:00:00 2001 From: meggert Date: Wed, 18 Oct 2023 11:03:52 +0200 Subject: [PATCH 08/13] div by 0 check --- mission/controller/acs/Guidance.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/mission/controller/acs/Guidance.cpp b/mission/controller/acs/Guidance.cpp index fe0ba4dd..bf834d12 100644 --- a/mission/controller/acs/Guidance.cpp +++ b/mission/controller/acs/Guidance.cpp @@ -460,13 +460,13 @@ void Guidance::targetRotationRate(const int8_t timeElapsedMax, const double time if (VectorOperations::norm(savedQuaternion, 4) == 0) { std::memcpy(savedQuaternion, quatInertialTarget, sizeof(savedQuaternion)); } - if (timeDelta < timeElapsedMax) { + if (timeDelta < timeElapsedMax and timeDelta != 0.0) { double q[4] = {0, 0, 0, 0}, qS[4] = {0, 0, 0, 0}; QuaternionOperations::inverse(quatInertialTarget, q); QuaternionOperations::inverse(savedQuaternion, qS); double qDiff[4] = {0, 0, 0, 0}; VectorOperations::subtract(q, qS, qDiff, 4); - VectorOperations::mulScalar(qDiff, 1 / timeDelta, qDiff, 4); + VectorOperations::mulScalar(qDiff, 1. / timeDelta, qDiff, 4); double tgtQuatVec[3] = {q[0], q[1], q[2]}; double qDiffVec[3] = {qDiff[0], qDiff[1], qDiff[2]}; -- 2.43.0 From 6d2bfbcfe6448af279c5a8e9a837500659d9e7e1 Mon Sep 17 00:00:00 2001 From: meggert Date: Fri, 1 Dec 2023 14:20:11 +0100 Subject: [PATCH 09/13] this is cleaner --- mission/controller/PowerController.cpp | 31 ++++++++++---------------- mission/controller/PowerController.h | 2 +- 2 files changed, 13 insertions(+), 20 deletions(-) diff --git a/mission/controller/PowerController.cpp b/mission/controller/PowerController.cpp index f8a02c09..719ef507 100644 --- a/mission/controller/PowerController.cpp +++ b/mission/controller/PowerController.cpp @@ -158,6 +158,11 @@ ReturnValue_t PowerController::checkModeCommand(Mode_t mode, Submode_t submode, void PowerController::calculateStateOfCharge() { // get time Clock::getClockMonotonic(&now); + double timeDelta = 0.0; + if (now.tv_sec != 0 and oldTime.tv_sec != 0) { + timeDelta = timevalOperations::toDouble(now - oldTime); + } + oldTime = now; // update EPS HK values ReturnValue_t result = updateEpsData(); @@ -173,8 +178,6 @@ void PowerController::calculateStateOfCharge() { pwrCtrlCoreHk.setValidity(false, true); } } - // store time for next run - oldTime = now; return; } @@ -195,12 +198,10 @@ void PowerController::calculateStateOfCharge() { pwrCtrlCoreHk.coulombCounterCharge.setValid(false); } } - // store time for next run - oldTime = now; return; } - result = calculateCoulombCounterCharge(); + result = calculateCoulombCounterCharge(timeDelta); if (result != returnvalue::OK) { // notifying events have already been triggered { @@ -215,9 +216,6 @@ void PowerController::calculateStateOfCharge() { pwrCtrlCoreHk.coulombCounterCharge.setValid(false); } } - // store time for next run - oldTime = now; - return; } // commit to dataset @@ -231,8 +229,6 @@ void PowerController::calculateStateOfCharge() { pwrCtrlCoreHk.setValidity(true, true); } } - // store time for next run - oldTime = now; } void PowerController::watchStateOfCharge() { @@ -285,17 +281,14 @@ ReturnValue_t PowerController::calculateOpenCircuitVoltageCharge() { return returnvalue::OK; } -ReturnValue_t PowerController::calculateCoulombCounterCharge() { - double timeDiff = 0.0; - if (oldTime.tv_sec != 0) { - timeDiff = timevalOperations::toDouble(now - oldTime); - } else { +ReturnValue_t PowerController::calculateCoulombCounterCharge(double timeDelta) { + if (timeDelta == 0.0) { return returnvalue::FAILED; } - if (timeDiff > maxAllowedTimeDiff) { + if (timeDelta > maxAllowedTimeDiff) { // should not be a permanent state so no spam protection required - triggerEvent(power::TIMEDELTA_OUT_OF_BOUNDS, static_cast(timeDiff * 10)); - sif::error << "Power Controller::Time delta too large for Coulomb Counter: " << timeDiff + triggerEvent(power::TIMEDELTA_OUT_OF_BOUNDS, static_cast(timeDelta * 10)); + sif::error << "Power Controller::Time delta too large for Coulomb Counter: " << timeDelta << std::endl; return returnvalue::FAILED; } @@ -303,7 +296,7 @@ ReturnValue_t PowerController::calculateCoulombCounterCharge() { coulombCounterCharge = openCircuitVoltageCharge; } else { coulombCounterCharge = - coulombCounterCharge + iBat * CONVERT_FROM_MILLI * timeDiff * SECONDS_TO_HOURS; + coulombCounterCharge + iBat * CONVERT_FROM_MILLI * timeDelta * SECONDS_TO_HOURS; if (coulombCounterCharge >= coulombCounterChargeUpperThreshold) { coulombCounterCharge = coulombCounterChargeUpperThreshold; } diff --git a/mission/controller/PowerController.h b/mission/controller/PowerController.h index b7c3d1a0..6df60732 100644 --- a/mission/controller/PowerController.h +++ b/mission/controller/PowerController.h @@ -45,7 +45,7 @@ class PowerController : public ExtendedControllerBase, public ReceivesParameterM void calculateStateOfCharge(); void watchStateOfCharge(); ReturnValue_t calculateOpenCircuitVoltageCharge(); - ReturnValue_t calculateCoulombCounterCharge(); + ReturnValue_t calculateCoulombCounterCharge(double timeDelta); ReturnValue_t updateEpsData(); float charge2stateOfCharge(float capacity, bool coulombCounter); ReturnValue_t lookUpTableOcvIdxFinder(float voltage, uint8_t& idx, bool paramCmd); -- 2.43.0 From aab705ca040baf148d853ca99045be640935955b Mon Sep 17 00:00:00 2001 From: meggert Date: Fri, 1 Dec 2023 14:22:10 +0100 Subject: [PATCH 10/13] changelog --- CHANGELOG.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index e0926cc4..f763c678 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -24,6 +24,8 @@ will consitute of a breaking change warranting a new major release: A filesystem change via `prefSD` on bootup, can lead to the TLE not being read, even though it is there. - Added action cmd to read the currently stored TLE. +- Both the `AcsController` and the `PwrController` now use the monotonic clock to calculate + the time difference. # [v7.4.0] 2023-11-30 -- 2.43.0 From 064200e7300142fe096bf9daa719abf4be0f5c9f Mon Sep 17 00:00:00 2001 From: Robin Mueller Date: Mon, 4 Dec 2023 09:58:37 +0100 Subject: [PATCH 11/13] yesyes clangd shush now --- mission/controller/acs/AcsParameters.h | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/mission/controller/acs/AcsParameters.h b/mission/controller/acs/AcsParameters.h index a9c9ab5d..130d9a91 100644 --- a/mission/controller/acs/AcsParameters.h +++ b/mission/controller/acs/AcsParameters.h @@ -75,9 +75,9 @@ class AcsParameters : public HasParametersIF { {-0.007534, 1.253879, 0.006812}, {-0.037072, 0.006812, 1.313158}}; - float mgm02variance[3] = {pow(3.2e-7, 2), pow(3.2e-7, 2), pow(4.1e-7, 2)}; - float mgm13variance[3] = {pow(1.5e-8, 2), pow(1.5e-8, 2), pow(1.5e-8, 2)}; - float mgm4variance[3] = {pow(1.7e-6, 2), pow(1.7e-6, 2), pow(1.7e-6, 2)}; + double mgm02variance[3] = {pow(3.2e-7, 2), pow(3.2e-7, 2), pow(4.1e-7, 2)}; + double mgm13variance[3] = {pow(1.5e-8, 2), pow(1.5e-8, 2), pow(1.5e-8, 2)}; + double mgm4variance[3] = {pow(1.7e-6, 2), pow(1.7e-6, 2), pow(1.7e-6, 2)}; float mgmVectorFilterWeight = 0.85; float mgmDerivativeFilterWeight = 0.99; uint8_t useMgm4 = false; @@ -787,10 +787,10 @@ class AcsParameters : public HasParametersIF { /* var = sigma^2, sigma = RND*sqrt(freq), following values are RND^2 and not var as freq is * assumed to be equal for the same class of sensors */ - float gyr02variance[3] = {pow(4.6e-3, 2), // RND_x = 3.0e-3 deg/s/sqrt(Hz) rms + double gyr02variance[3] = {pow(4.6e-3, 2), // RND_x = 3.0e-3 deg/s/sqrt(Hz) rms pow(4.6e-3, 2), // RND_y = 3.0e-3 deg/s/sqrt(Hz) rms pow(6.1e-3, 2)}; // RND_z = 4.3e-3 deg/s/sqrt(Hz) rms - float gyr13variance[3] = {pow(11e-3, 2), pow(11e-3, 2), pow(11e-3, 2)}; + double gyr13variance[3] = {pow(11e-3, 2), pow(11e-3, 2), pow(11e-3, 2)}; uint8_t preferAdis = false; float gyrFilterWeight = 0.6; } gyrHandlingParameters; -- 2.43.0 From 4ea1e16880ed4c75c0846d0f29ee71926444f735 Mon Sep 17 00:00:00 2001 From: meggert Date: Mon, 4 Dec 2023 10:59:44 +0100 Subject: [PATCH 12/13] cpu goes brrrr --- mission/controller/acs/SensorProcessing.cpp | 6 ++---- mission/controller/acs/SensorProcessing.h | 2 ++ 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/mission/controller/acs/SensorProcessing.cpp b/mission/controller/acs/SensorProcessing.cpp index 56217a8f..975b38b1 100644 --- a/mission/controller/acs/SensorProcessing.cpp +++ b/mission/controller/acs/SensorProcessing.cpp @@ -1,5 +1,7 @@ #include "SensorProcessing.h" +#include + SensorProcessing::SensorProcessing() {} SensorProcessing::~SensorProcessing() {} @@ -16,12 +18,8 @@ void SensorProcessing::processMgm(const float *mgm0Value, bool mgm0valid, const double magIgrfModel[3] = {0.0, 0.0, 0.0}; bool gpsValid = false; if (gpsDataProcessed->source.value != acs::GpsSource::NONE) { - Igrf13Model igrf13; igrf13.schmidtNormalization(); igrf13.updateCoeffGH(timeAbsolute); - // ToDo - // maybe put a condition here, to only update after a full day, this - // class function has around 700 steps to perform igrf13.magFieldComp(gpsDataProcessed->gdLongitude.value, gpsDataProcessed->gcLatitude.value, gpsDataProcessed->altitude.value, timeAbsolute, magIgrfModel); gpsValid = true; diff --git a/mission/controller/acs/SensorProcessing.h b/mission/controller/acs/SensorProcessing.h index fea0fd01..7caa29f8 100644 --- a/mission/controller/acs/SensorProcessing.h +++ b/mission/controller/acs/SensorProcessing.h @@ -35,6 +35,8 @@ class SensorProcessing { static constexpr double ZERO_VEC_D[3] = {0, 0, 0}; static constexpr double ECCENTRICITY_WGS84 = 0.0818195; + Igrf13Model igrf13; + protected: void processMgm(const float *mgm0Value, bool mgm0valid, const float *mgm1Value, bool mgm1valid, const float *mgm2Value, bool mgm2valid, const float *mgm3Value, bool mgm3valid, -- 2.43.0 From 41c8d7e0dd884f7a73983f4d4b6c3c2748e8137c Mon Sep 17 00:00:00 2001 From: meggert Date: Mon, 4 Dec 2023 11:15:08 +0100 Subject: [PATCH 13/13] replaced one ToDo with another one --- mission/controller/acs/SensorProcessing.cpp | 3 +++ mission/controller/acs/SensorProcessing.h | 2 -- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/mission/controller/acs/SensorProcessing.cpp b/mission/controller/acs/SensorProcessing.cpp index 975b38b1..23388e5f 100644 --- a/mission/controller/acs/SensorProcessing.cpp +++ b/mission/controller/acs/SensorProcessing.cpp @@ -18,6 +18,9 @@ void SensorProcessing::processMgm(const float *mgm0Value, bool mgm0valid, const double magIgrfModel[3] = {0.0, 0.0, 0.0}; bool gpsValid = false; if (gpsDataProcessed->source.value != acs::GpsSource::NONE) { + // There seems to be a bug here, which causes the model vector to drift until infinity, if the + // model class is not initialized new every time. Works for now, but should be investigated. + Igrf13Model igrf13; igrf13.schmidtNormalization(); igrf13.updateCoeffGH(timeAbsolute); igrf13.magFieldComp(gpsDataProcessed->gdLongitude.value, gpsDataProcessed->gcLatitude.value, diff --git a/mission/controller/acs/SensorProcessing.h b/mission/controller/acs/SensorProcessing.h index 7caa29f8..fea0fd01 100644 --- a/mission/controller/acs/SensorProcessing.h +++ b/mission/controller/acs/SensorProcessing.h @@ -35,8 +35,6 @@ class SensorProcessing { static constexpr double ZERO_VEC_D[3] = {0, 0, 0}; static constexpr double ECCENTRICITY_WGS84 = 0.0818195; - Igrf13Model igrf13; - protected: void processMgm(const float *mgm0Value, bool mgm0valid, const float *mgm1Value, bool mgm1valid, const float *mgm2Value, bool mgm2valid, const float *mgm3Value, bool mgm3valid, -- 2.43.0