2022-08-15 09:58:18 +02:00
|
|
|
#include "AcsController.h"
|
2022-08-12 12:29:28 +02:00
|
|
|
|
2022-08-15 17:25:16 +02:00
|
|
|
#include <fsfw/datapool/PoolReadGuard.h>
|
2023-02-23 11:06:45 +01:00
|
|
|
#include <mission/acsDefs.h>
|
|
|
|
#include <mission/config/torquer.h>
|
2022-10-20 15:08:33 +02:00
|
|
|
|
2022-08-15 09:58:18 +02:00
|
|
|
AcsController::AcsController(object_id_t objectId)
|
2022-11-04 14:19:43 +01:00
|
|
|
: ExtendedControllerBase(objectId),
|
2022-10-06 15:37:41 +02:00
|
|
|
sensorProcessing(&acsParameters),
|
|
|
|
navigation(&acsParameters),
|
|
|
|
actuatorCmd(&acsParameters),
|
2022-10-12 10:28:44 +02:00
|
|
|
guidance(&acsParameters),
|
2022-10-20 11:07:45 +02:00
|
|
|
safeCtrl(&acsParameters),
|
2022-10-06 15:37:41 +02:00
|
|
|
detumble(&acsParameters),
|
2022-10-12 10:28:44 +02:00
|
|
|
ptgCtrl(&acsParameters),
|
2023-01-10 09:06:09 +01:00
|
|
|
parameterHelper(this),
|
2022-10-24 10:41:28 +02:00
|
|
|
mgmDataRaw(this),
|
|
|
|
mgmDataProcessed(this),
|
|
|
|
susDataRaw(this),
|
|
|
|
susDataProcessed(this),
|
|
|
|
gyrDataRaw(this),
|
|
|
|
gyrDataProcessed(this),
|
|
|
|
gpsDataProcessed(this),
|
|
|
|
mekfData(this),
|
2022-11-03 10:43:27 +01:00
|
|
|
ctrlValData(this),
|
2022-10-24 10:41:28 +02:00
|
|
|
actuatorCmdData(this) {}
|
2022-08-12 12:29:28 +02:00
|
|
|
|
2023-02-17 11:11:48 +01:00
|
|
|
ReturnValue_t AcsController::initialize() {
|
|
|
|
ReturnValue_t result = parameterHelper.initialize();
|
|
|
|
if (result != returnvalue::OK) {
|
|
|
|
return result;
|
|
|
|
}
|
|
|
|
return ExtendedControllerBase::initialize();
|
|
|
|
}
|
|
|
|
|
2023-02-08 13:40:03 +01:00
|
|
|
ReturnValue_t AcsController::handleCommandMessage(CommandMessage *message) {
|
2023-01-10 09:06:09 +01:00
|
|
|
ReturnValue_t result = actionHelper.handleActionMessage(message);
|
|
|
|
if (result == returnvalue::OK) {
|
|
|
|
return result;
|
|
|
|
}
|
|
|
|
result = parameterHelper.handleParameterMessage(message);
|
|
|
|
if (result == returnvalue::OK) {
|
|
|
|
return result;
|
|
|
|
}
|
|
|
|
return result;
|
|
|
|
}
|
|
|
|
|
2023-02-22 16:10:30 +01:00
|
|
|
ReturnValue_t AcsController::executeAction(ActionId_t actionId, MessageQueueId_t commandedBy,
|
|
|
|
const uint8_t *data, size_t size) {
|
|
|
|
switch (actionId) {
|
2023-02-22 17:10:42 +01:00
|
|
|
case SOLAR_ARRAY_DEPLOYMENT_SUCCESSFUL: {
|
2023-02-23 11:26:43 +01:00
|
|
|
ReturnValue_t result = guidance.solarArrayDeploymentComplete();
|
|
|
|
if (result == returnvalue::FAILED) {
|
|
|
|
return FILE_DELETION_FAILED;
|
|
|
|
}
|
2023-02-22 17:10:42 +01:00
|
|
|
return HasActionsIF::EXECUTION_FINISHED;
|
|
|
|
}
|
|
|
|
case RESET_MEKF: {
|
|
|
|
navigation.resetMekf(&mekfData);
|
2023-02-22 16:10:30 +01:00
|
|
|
return HasActionsIF::EXECUTION_FINISHED;
|
|
|
|
}
|
|
|
|
default: {
|
|
|
|
return HasActionsIF::INVALID_ACTION_ID;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2023-01-10 09:06:09 +01:00
|
|
|
MessageQueueId_t AcsController::getCommandQueue() const { return commandQueue->getId(); }
|
|
|
|
|
|
|
|
ReturnValue_t AcsController::getParameter(uint8_t domainId, uint8_t parameterId,
|
2023-02-08 13:40:03 +01:00
|
|
|
ParameterWrapper *parameterWrapper,
|
|
|
|
const ParameterWrapper *newValues,
|
2023-01-10 09:06:09 +01:00
|
|
|
uint16_t startAtIndex) {
|
2023-01-10 09:16:13 +01:00
|
|
|
return acsParameters.getParameter(domainId, parameterId, parameterWrapper, newValues,
|
|
|
|
startAtIndex);
|
2022-08-15 09:58:18 +02:00
|
|
|
}
|
2022-08-12 12:29:28 +02:00
|
|
|
|
2022-08-15 10:50:19 +02:00
|
|
|
void AcsController::performControlOperation() {
|
2023-02-14 10:59:35 +01:00
|
|
|
#if OBSW_THREAD_TRACING == 1
|
|
|
|
trace::threadTrace(opCounter, "ACS & TCS PST");
|
|
|
|
#endif
|
2023-02-24 15:34:51 +01:00
|
|
|
{
|
|
|
|
PoolReadGuard pg(&mgmDataRaw);
|
|
|
|
if (pg.getReadResult() == returnvalue::OK) {
|
|
|
|
copyMgmData();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
{
|
|
|
|
PoolReadGuard pg(&susDataRaw);
|
|
|
|
if (pg.getReadResult() == returnvalue::OK) {
|
|
|
|
copySusData();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
{
|
|
|
|
PoolReadGuard pg(&gyrDataRaw);
|
|
|
|
if (pg.getReadResult() == returnvalue::OK) {
|
|
|
|
copyGyrData();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2022-08-15 10:50:19 +02:00
|
|
|
switch (internalState) {
|
|
|
|
case InternalState::STARTUP: {
|
|
|
|
initialCountdown.resetTimer();
|
|
|
|
internalState = InternalState::INITIAL_DELAY;
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
case InternalState::INITIAL_DELAY: {
|
|
|
|
if (initialCountdown.hasTimedOut()) {
|
|
|
|
internalState = InternalState::READY;
|
|
|
|
}
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
case InternalState::READY: {
|
2022-10-04 13:45:13 +02:00
|
|
|
if (mode != MODE_OFF) {
|
|
|
|
switch (submode) {
|
2023-02-02 16:27:50 +01:00
|
|
|
case acs::SAFE:
|
2022-11-08 13:48:50 +01:00
|
|
|
performSafe();
|
2022-10-04 13:45:13 +02:00
|
|
|
break;
|
2023-02-02 16:27:50 +01:00
|
|
|
case acs::DETUMBLE:
|
2022-10-10 09:54:06 +02:00
|
|
|
performDetumble();
|
2022-10-04 13:45:13 +02:00
|
|
|
break;
|
2023-02-07 13:09:42 +01:00
|
|
|
case acs::PTG_IDLE:
|
2023-02-02 16:27:50 +01:00
|
|
|
case acs::PTG_TARGET:
|
2023-02-07 13:09:42 +01:00
|
|
|
case acs::PTG_TARGET_GS:
|
|
|
|
case acs::PTG_NADIR:
|
|
|
|
case acs::PTG_INERTIAL:
|
2022-10-12 10:28:44 +02:00
|
|
|
performPointingCtrl();
|
2022-11-08 13:48:50 +01:00
|
|
|
break;
|
2022-10-04 13:45:13 +02:00
|
|
|
}
|
|
|
|
}
|
2022-08-15 10:50:19 +02:00
|
|
|
break;
|
|
|
|
}
|
|
|
|
default:
|
|
|
|
break;
|
|
|
|
}
|
2023-01-23 16:34:52 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
void AcsController::performSafe() {
|
|
|
|
timeval now;
|
|
|
|
Clock::getClock_timeval(&now);
|
|
|
|
|
|
|
|
sensorProcessing.process(now, &sensorValues, &mgmDataProcessed, &susDataProcessed,
|
|
|
|
&gyrDataProcessed, &gpsDataProcessed, &acsParameters);
|
2023-02-21 17:10:59 +01:00
|
|
|
ReturnValue_t result = navigation.useMekf(&sensorValues, &gyrDataProcessed, &mgmDataProcessed,
|
|
|
|
&susDataProcessed, &mekfData);
|
2023-02-23 11:26:43 +01:00
|
|
|
if (result != MultiplicativeKalmanFilter::MEKF_RUNNING &&
|
|
|
|
result != MultiplicativeKalmanFilter::MEKF_INITIALIZED) {
|
2023-02-21 17:10:59 +01:00
|
|
|
if (not mekfInvalidFlag) {
|
2023-02-22 14:50:38 +01:00
|
|
|
triggerEvent(acs::MEKF_INVALID_INFO);
|
2023-02-21 17:10:59 +01:00
|
|
|
mekfInvalidFlag = true;
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
mekfInvalidFlag = false;
|
|
|
|
}
|
2023-02-21 11:30:42 +01:00
|
|
|
// get desired satellite rate and sun direction to align
|
2023-01-23 16:34:52 +01:00
|
|
|
double satRateSafe[3] = {0, 0, 0}, sunTargetDir[3] = {0, 0, 0};
|
|
|
|
guidance.getTargetParamsSafe(sunTargetDir, satRateSafe);
|
2023-02-10 11:27:25 +01:00
|
|
|
// if MEKF is working
|
2023-01-23 16:34:52 +01:00
|
|
|
double magMomMtq[3] = {0, 0, 0}, errAng = 0.0;
|
2023-02-23 11:26:43 +01:00
|
|
|
if (result == MultiplicativeKalmanFilter::MEKF_RUNNING) {
|
2023-03-02 17:52:36 +01:00
|
|
|
result = safeCtrl.safeMekf(now, mekfData.quatMekf.value, mekfData.quatMekf.isValid(),
|
|
|
|
mgmDataProcessed.magIgrfModel.value,
|
|
|
|
mgmDataProcessed.magIgrfModel.isValid(),
|
|
|
|
susDataProcessed.sunIjkModel.value, susDataProcessed.isValid(),
|
|
|
|
mekfData.satRotRateMekf.value, mekfData.satRotRateMekf.isValid(),
|
|
|
|
sunTargetDir, satRateSafe, &errAng, magMomMtq);
|
2023-01-23 16:34:52 +01:00
|
|
|
} else {
|
2023-03-02 17:52:36 +01:00
|
|
|
result = safeCtrl.safeNoMekf(
|
2023-01-23 16:34:52 +01:00
|
|
|
now, susDataProcessed.susVecTot.value, susDataProcessed.susVecTot.isValid(),
|
|
|
|
susDataProcessed.susVecTotDerivative.value, susDataProcessed.susVecTotDerivative.isValid(),
|
|
|
|
mgmDataProcessed.mgmVecTot.value, mgmDataProcessed.mgmVecTot.isValid(),
|
|
|
|
mgmDataProcessed.mgmVecTotDerivative.value, mgmDataProcessed.mgmVecTotDerivative.isValid(),
|
2023-03-02 17:52:36 +01:00
|
|
|
sunTargetDir, satRateSafe, &errAng, magMomMtq);
|
|
|
|
}
|
|
|
|
if (result == returnvalue::FAILED) {
|
|
|
|
// ToDo: this should never ever happen or we are dead. prob add an event at least
|
2022-10-20 11:07:45 +02:00
|
|
|
}
|
|
|
|
|
2023-02-10 11:27:25 +01:00
|
|
|
actuatorCmd.cmdDipolMtq(magMomMtq, cmdDipolMtqs);
|
2022-10-20 11:07:45 +02:00
|
|
|
|
2023-02-22 14:50:38 +01:00
|
|
|
// detumble check and switch
|
2023-01-23 16:34:52 +01:00
|
|
|
if (mekfData.satRotRateMekf.isValid() &&
|
|
|
|
VectorOperations<double>::norm(mekfData.satRotRateMekf.value, 3) >
|
|
|
|
acsParameters.detumbleParameter.omegaDetumbleStart) {
|
|
|
|
detumbleCounter++;
|
|
|
|
} else if (gyrDataProcessed.gyrVecTot.isValid() &&
|
|
|
|
VectorOperations<double>::norm(gyrDataProcessed.gyrVecTot.value, 3) >
|
|
|
|
acsParameters.detumbleParameter.omegaDetumbleStart) {
|
|
|
|
detumbleCounter++;
|
|
|
|
} else {
|
|
|
|
detumbleCounter = 0;
|
|
|
|
}
|
|
|
|
if (detumbleCounter > acsParameters.detumbleParameter.detumblecounter) {
|
|
|
|
detumbleCounter = 0;
|
2023-02-03 14:31:22 +01:00
|
|
|
// Triggers detumble mode transition in subsystem
|
2023-02-02 16:27:50 +01:00
|
|
|
triggerEvent(acs::SAFE_RATE_VIOLATION);
|
2023-01-23 16:34:52 +01:00
|
|
|
}
|
2022-11-03 10:43:27 +01:00
|
|
|
|
2023-02-17 13:28:29 +01:00
|
|
|
updateCtrlValData(errAng);
|
2023-02-17 11:11:48 +01:00
|
|
|
updateActuatorCmdData(cmdDipolMtqs);
|
2023-03-04 17:19:36 +01:00
|
|
|
// commandActuators(cmdDipolMtqs[0], cmdDipolMtqs[1], cmdDipolMtqs[2]/*500, 500, 500*/,
|
|
|
|
// acsParameters.magnetorquesParameter.torqueDuration, 0, 0, 0, 0,
|
|
|
|
// acsParameters.rwHandlingParameters.rampTime);
|
2023-01-23 16:34:52 +01:00
|
|
|
}
|
2022-08-12 12:29:28 +02:00
|
|
|
|
2023-01-23 16:34:52 +01:00
|
|
|
void AcsController::performDetumble() {
|
|
|
|
timeval now;
|
|
|
|
Clock::getClock_timeval(&now);
|
|
|
|
|
|
|
|
sensorProcessing.process(now, &sensorValues, &mgmDataProcessed, &susDataProcessed,
|
|
|
|
&gyrDataProcessed, &gpsDataProcessed, &acsParameters);
|
2023-02-21 17:10:59 +01:00
|
|
|
ReturnValue_t result = navigation.useMekf(&sensorValues, &gyrDataProcessed, &mgmDataProcessed,
|
|
|
|
&susDataProcessed, &mekfData);
|
2023-02-23 11:26:43 +01:00
|
|
|
if (result != MultiplicativeKalmanFilter::MEKF_RUNNING &&
|
|
|
|
result != MultiplicativeKalmanFilter::MEKF_INITIALIZED) {
|
2023-02-21 17:10:59 +01:00
|
|
|
if (not mekfInvalidFlag) {
|
2023-02-22 14:50:38 +01:00
|
|
|
triggerEvent(acs::MEKF_INVALID_INFO);
|
2023-02-21 17:10:59 +01:00
|
|
|
mekfInvalidFlag = true;
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
mekfInvalidFlag = false;
|
|
|
|
}
|
2023-01-23 16:34:52 +01:00
|
|
|
double magMomMtq[3] = {0, 0, 0};
|
|
|
|
detumble.bDotLaw(mgmDataProcessed.mgmVecTotDerivative.value,
|
|
|
|
mgmDataProcessed.mgmVecTotDerivative.isValid(), mgmDataProcessed.mgmVecTot.value,
|
|
|
|
mgmDataProcessed.mgmVecTot.isValid(), magMomMtq);
|
2023-02-10 11:27:25 +01:00
|
|
|
actuatorCmd.cmdDipolMtq(magMomMtq, cmdDipolMtqs);
|
2023-01-23 16:34:52 +01:00
|
|
|
|
|
|
|
if (mekfData.satRotRateMekf.isValid() &&
|
|
|
|
VectorOperations<double>::norm(mekfData.satRotRateMekf.value, 3) <
|
|
|
|
acsParameters.detumbleParameter.omegaDetumbleEnd) {
|
|
|
|
detumbleCounter++;
|
|
|
|
} else if (gyrDataProcessed.gyrVecTot.isValid() &&
|
|
|
|
VectorOperations<double>::norm(gyrDataProcessed.gyrVecTot.value, 3) <
|
|
|
|
acsParameters.detumbleParameter.omegaDetumbleEnd) {
|
|
|
|
detumbleCounter++;
|
|
|
|
} else {
|
|
|
|
detumbleCounter = 0;
|
|
|
|
}
|
|
|
|
if (detumbleCounter > acsParameters.detumbleParameter.detumblecounter) {
|
|
|
|
detumbleCounter = 0;
|
2023-02-03 14:21:36 +01:00
|
|
|
// Triggers safe mode transition in subsystem
|
|
|
|
triggerEvent(acs::SAFE_RATE_RECOVERY);
|
2023-01-23 16:34:52 +01:00
|
|
|
}
|
|
|
|
|
2023-02-21 11:13:06 +01:00
|
|
|
disableCtrlValData();
|
2023-02-17 13:28:29 +01:00
|
|
|
updateActuatorCmdData(cmdDipolMtqs);
|
2023-02-10 11:30:52 +01:00
|
|
|
// commandActuators(cmdDipolMtqs[0], cmdDipolMtqs[1], cmdDipolMtqs[2],
|
|
|
|
// acsParameters.magnetorquesParameter.torqueDuration, 0, 0, 0, 0,
|
|
|
|
// acsParameters.rwHandlingParameters.rampTime);
|
2023-01-23 16:34:52 +01:00
|
|
|
}
|
2022-10-12 10:28:44 +02:00
|
|
|
|
2023-01-23 16:34:52 +01:00
|
|
|
void AcsController::performPointingCtrl() {
|
|
|
|
timeval now;
|
|
|
|
Clock::getClock_timeval(&now);
|
|
|
|
|
|
|
|
sensorProcessing.process(now, &sensorValues, &mgmDataProcessed, &susDataProcessed,
|
|
|
|
&gyrDataProcessed, &gpsDataProcessed, &acsParameters);
|
2023-02-21 17:10:59 +01:00
|
|
|
ReturnValue_t result = navigation.useMekf(&sensorValues, &gyrDataProcessed, &mgmDataProcessed,
|
|
|
|
&susDataProcessed, &mekfData);
|
2023-02-23 11:26:43 +01:00
|
|
|
if (result != MultiplicativeKalmanFilter::MEKF_RUNNING &&
|
|
|
|
result != MultiplicativeKalmanFilter::MEKF_INITIALIZED) {
|
2023-02-21 17:10:59 +01:00
|
|
|
if (not mekfInvalidFlag) {
|
2023-02-22 14:50:38 +01:00
|
|
|
triggerEvent(acs::MEKF_INVALID_INFO);
|
2023-02-21 17:10:59 +01:00
|
|
|
mekfInvalidFlag = true;
|
|
|
|
}
|
2023-02-22 14:50:38 +01:00
|
|
|
if (mekfInvalidCounter > 4) {
|
|
|
|
triggerEvent(acs::MEKF_INVALID_MODE_VIOLATION);
|
|
|
|
}
|
|
|
|
mekfInvalidCounter++;
|
2023-02-24 09:11:13 +01:00
|
|
|
// commandActuators(0, 0, 0, acsParameters.magnetorquesParameter.torqueDuration,
|
|
|
|
// cmdSpeedRws[0],
|
|
|
|
// cmdSpeedRws[1], cmdSpeedRws[2], cmdSpeedRws[3],
|
|
|
|
// acsParameters.rwHandlingParameters.rampTime);
|
2023-02-21 17:10:59 +01:00
|
|
|
return;
|
|
|
|
} else {
|
|
|
|
mekfInvalidFlag = false;
|
2023-02-22 14:50:38 +01:00
|
|
|
mekfInvalidCounter = 0;
|
2023-02-21 17:10:59 +01:00
|
|
|
}
|
2023-01-23 16:34:52 +01:00
|
|
|
uint8_t enableAntiStiction = true;
|
|
|
|
double rwPseudoInv[4][3] = {{0, 0, 0}, {0, 0, 0}, {0, 0, 0}, {0, 0, 0}};
|
2023-02-21 17:10:59 +01:00
|
|
|
result = guidance.getDistributionMatrixRw(&sensorValues, *rwPseudoInv);
|
2023-02-16 15:40:16 +01:00
|
|
|
if (result == returnvalue::FAILED) {
|
2023-02-17 09:21:30 +01:00
|
|
|
if (multipleRwUnavailableCounter > 4) {
|
|
|
|
triggerEvent(acs::MULTIPLE_RW_INVALID);
|
|
|
|
}
|
2023-02-22 14:50:38 +01:00
|
|
|
multipleRwUnavailableCounter++;
|
2023-02-16 15:40:16 +01:00
|
|
|
return;
|
2023-02-17 09:21:30 +01:00
|
|
|
} else {
|
|
|
|
multipleRwUnavailableCounter = 0;
|
2023-02-16 15:40:16 +01:00
|
|
|
}
|
2023-01-23 16:34:52 +01:00
|
|
|
double torquePtgRws[4] = {0, 0, 0, 0}, rwTrqNs[4] = {0, 0, 0, 0};
|
|
|
|
double torqueRws[4] = {0, 0, 0, 0}, torqueRwsScaled[4] = {0, 0, 0, 0};
|
2023-02-10 11:27:25 +01:00
|
|
|
double mgtDpDes[3] = {0, 0, 0};
|
2023-01-23 16:34:52 +01:00
|
|
|
|
2023-02-17 15:57:07 +01:00
|
|
|
// Variables required for guidance
|
2023-02-20 16:00:17 +01:00
|
|
|
double targetQuat[4] = {0, 0, 0, 1}, targetSatRotRate[3] = {0, 0, 0}, errorQuat[4] = {0, 0, 0, 1},
|
|
|
|
errorAngle = 0, errorSatRotRate[3] = {0, 0, 0};
|
2023-01-23 16:34:52 +01:00
|
|
|
switch (submode) {
|
2023-02-07 13:09:42 +01:00
|
|
|
case acs::PTG_IDLE:
|
2023-02-20 15:59:01 +01:00
|
|
|
guidance.targetQuatPtgSun(susDataProcessed.sunIjkModel.value, targetQuat, targetSatRotRate);
|
|
|
|
guidance.comparePtg(mekfData.quatMekf.value, mekfData.satRotRateMekf.value, targetQuat,
|
|
|
|
targetSatRotRate, errorQuat, errorSatRotRate, errorAngle);
|
2023-01-23 16:34:52 +01:00
|
|
|
ptgCtrl.ptgNullspace(
|
2023-02-20 15:59:01 +01:00
|
|
|
&acsParameters.idleModeControllerParameters, &(sensorValues.rw1Set.currSpeed.value),
|
2023-01-23 16:34:52 +01:00
|
|
|
&(sensorValues.rw2Set.currSpeed.value), &(sensorValues.rw3Set.currSpeed.value),
|
|
|
|
&(sensorValues.rw4Set.currSpeed.value), rwTrqNs);
|
|
|
|
VectorOperations<double>::add(torquePtgRws, rwTrqNs, torqueRws, 4);
|
|
|
|
actuatorCmd.scalingTorqueRws(torqueRws, torqueRwsScaled);
|
|
|
|
ptgCtrl.ptgDesaturation(
|
2023-02-20 15:59:01 +01:00
|
|
|
&acsParameters.idleModeControllerParameters, mgmDataProcessed.mgmVecTot.value,
|
2023-01-23 16:34:52 +01:00
|
|
|
mgmDataProcessed.mgmVecTot.isValid(), mekfData.satRotRateMekf.value,
|
|
|
|
&(sensorValues.rw1Set.currSpeed.value), &(sensorValues.rw2Set.currSpeed.value),
|
|
|
|
&(sensorValues.rw3Set.currSpeed.value), &(sensorValues.rw4Set.currSpeed.value), mgtDpDes);
|
2023-02-20 15:59:01 +01:00
|
|
|
enableAntiStiction = acsParameters.idleModeControllerParameters.enableAntiStiction;
|
2023-01-23 16:34:52 +01:00
|
|
|
break;
|
2022-12-14 11:46:58 +01:00
|
|
|
|
2023-02-07 13:09:42 +01:00
|
|
|
case acs::PTG_TARGET:
|
2023-02-17 14:46:41 +01:00
|
|
|
guidance.targetQuatPtgThreeAxes(now, gpsDataProcessed.gpsPosition.value,
|
|
|
|
gpsDataProcessed.gpsVelocity.value, targetQuat,
|
|
|
|
targetSatRotRate);
|
2023-02-20 11:52:53 +01:00
|
|
|
guidance.comparePtg(mekfData.quatMekf.value, mekfData.satRotRateMekf.value, targetQuat,
|
|
|
|
targetSatRotRate, acsParameters.targetModeControllerParameters.quatRef,
|
|
|
|
acsParameters.targetModeControllerParameters.refRotRate, errorQuat,
|
|
|
|
errorSatRotRate, errorAngle);
|
|
|
|
ptgCtrl.ptgLaw(&acsParameters.targetModeControllerParameters, errorQuat, errorSatRotRate,
|
2023-01-23 16:34:52 +01:00
|
|
|
*rwPseudoInv, torquePtgRws);
|
|
|
|
ptgCtrl.ptgNullspace(
|
|
|
|
&acsParameters.targetModeControllerParameters, &(sensorValues.rw1Set.currSpeed.value),
|
|
|
|
&(sensorValues.rw2Set.currSpeed.value), &(sensorValues.rw3Set.currSpeed.value),
|
|
|
|
&(sensorValues.rw4Set.currSpeed.value), rwTrqNs);
|
|
|
|
VectorOperations<double>::add(torquePtgRws, rwTrqNs, torqueRws, 4);
|
|
|
|
actuatorCmd.scalingTorqueRws(torqueRws, torqueRwsScaled);
|
|
|
|
ptgCtrl.ptgDesaturation(
|
|
|
|
&acsParameters.targetModeControllerParameters, mgmDataProcessed.mgmVecTot.value,
|
|
|
|
mgmDataProcessed.mgmVecTot.isValid(), mekfData.satRotRateMekf.value,
|
|
|
|
&(sensorValues.rw1Set.currSpeed.value), &(sensorValues.rw2Set.currSpeed.value),
|
|
|
|
&(sensorValues.rw3Set.currSpeed.value), &(sensorValues.rw4Set.currSpeed.value), mgtDpDes);
|
2023-02-20 15:59:01 +01:00
|
|
|
enableAntiStiction = acsParameters.targetModeControllerParameters.enableAntiStiction;
|
2023-01-23 16:34:52 +01:00
|
|
|
break;
|
2022-11-08 13:48:50 +01:00
|
|
|
|
2023-02-07 13:09:42 +01:00
|
|
|
case acs::PTG_TARGET_GS:
|
2023-02-20 15:59:01 +01:00
|
|
|
guidance.targetQuatPtgGs(now, gpsDataProcessed.gpsPosition.value,
|
|
|
|
susDataProcessed.sunIjkModel.value, targetQuat, targetSatRotRate);
|
|
|
|
guidance.comparePtg(mekfData.quatMekf.value, mekfData.satRotRateMekf.value, targetQuat,
|
|
|
|
targetSatRotRate, errorQuat, errorSatRotRate, errorAngle);
|
|
|
|
ptgCtrl.ptgLaw(&acsParameters.targetModeControllerParameters, errorQuat, errorSatRotRate,
|
2023-01-23 16:34:52 +01:00
|
|
|
*rwPseudoInv, torquePtgRws);
|
|
|
|
ptgCtrl.ptgNullspace(
|
|
|
|
&acsParameters.targetModeControllerParameters, &(sensorValues.rw1Set.currSpeed.value),
|
|
|
|
&(sensorValues.rw2Set.currSpeed.value), &(sensorValues.rw3Set.currSpeed.value),
|
|
|
|
&(sensorValues.rw4Set.currSpeed.value), rwTrqNs);
|
|
|
|
VectorOperations<double>::add(torquePtgRws, rwTrqNs, torqueRws, 4);
|
|
|
|
actuatorCmd.scalingTorqueRws(torqueRws, torqueRwsScaled);
|
|
|
|
ptgCtrl.ptgDesaturation(
|
|
|
|
&acsParameters.targetModeControllerParameters, mgmDataProcessed.mgmVecTot.value,
|
|
|
|
mgmDataProcessed.mgmVecTot.isValid(), mekfData.satRotRateMekf.value,
|
|
|
|
&(sensorValues.rw1Set.currSpeed.value), &(sensorValues.rw2Set.currSpeed.value),
|
|
|
|
&(sensorValues.rw3Set.currSpeed.value), &(sensorValues.rw4Set.currSpeed.value), mgtDpDes);
|
2023-02-20 15:59:01 +01:00
|
|
|
enableAntiStiction = acsParameters.targetModeControllerParameters.enableAntiStiction;
|
2023-01-23 16:34:52 +01:00
|
|
|
break;
|
|
|
|
|
2023-02-07 13:09:42 +01:00
|
|
|
case acs::PTG_NADIR:
|
2023-02-20 16:20:54 +01:00
|
|
|
guidance.targetQuatPtgNadirThreeAxes(now, 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,
|
|
|
|
errorSatRotRate, errorAngle);
|
2023-02-20 15:59:01 +01:00
|
|
|
ptgCtrl.ptgLaw(&acsParameters.nadirModeControllerParameters, errorQuat, errorSatRotRate,
|
2023-01-23 16:34:52 +01:00
|
|
|
*rwPseudoInv, torquePtgRws);
|
|
|
|
ptgCtrl.ptgNullspace(
|
|
|
|
&acsParameters.nadirModeControllerParameters, &(sensorValues.rw1Set.currSpeed.value),
|
|
|
|
&(sensorValues.rw2Set.currSpeed.value), &(sensorValues.rw3Set.currSpeed.value),
|
|
|
|
&(sensorValues.rw4Set.currSpeed.value), rwTrqNs);
|
|
|
|
VectorOperations<double>::add(torquePtgRws, rwTrqNs, torqueRws, 4);
|
|
|
|
actuatorCmd.scalingTorqueRws(torqueRws, torqueRwsScaled);
|
|
|
|
ptgCtrl.ptgDesaturation(
|
|
|
|
&acsParameters.nadirModeControllerParameters, mgmDataProcessed.mgmVecTot.value,
|
|
|
|
mgmDataProcessed.mgmVecTot.isValid(), mekfData.satRotRateMekf.value,
|
|
|
|
&(sensorValues.rw1Set.currSpeed.value), &(sensorValues.rw2Set.currSpeed.value),
|
|
|
|
&(sensorValues.rw3Set.currSpeed.value), &(sensorValues.rw4Set.currSpeed.value), mgtDpDes);
|
2023-02-20 15:59:01 +01:00
|
|
|
enableAntiStiction = acsParameters.nadirModeControllerParameters.enableAntiStiction;
|
2023-01-23 16:34:52 +01:00
|
|
|
break;
|
|
|
|
|
2023-02-07 13:09:42 +01:00
|
|
|
case acs::PTG_INERTIAL:
|
2023-02-20 16:30:53 +01:00
|
|
|
std::memcpy(targetQuat, acsParameters.inertialModeControllerParameters.tgtQuat,
|
2023-01-23 16:34:52 +01:00
|
|
|
4 * sizeof(double));
|
2023-02-20 16:30:53 +01:00
|
|
|
guidance.comparePtg(mekfData.quatMekf.value, mekfData.satRotRateMekf.value, targetQuat,
|
|
|
|
targetSatRotRate, acsParameters.inertialModeControllerParameters.quatRef,
|
|
|
|
acsParameters.inertialModeControllerParameters.refRotRate, errorQuat,
|
|
|
|
errorSatRotRate, errorAngle);
|
|
|
|
ptgCtrl.ptgLaw(&acsParameters.inertialModeControllerParameters, errorQuat, errorSatRotRate,
|
2023-01-23 16:34:52 +01:00
|
|
|
*rwPseudoInv, torquePtgRws);
|
|
|
|
ptgCtrl.ptgNullspace(
|
|
|
|
&acsParameters.inertialModeControllerParameters, &(sensorValues.rw1Set.currSpeed.value),
|
|
|
|
&(sensorValues.rw2Set.currSpeed.value), &(sensorValues.rw3Set.currSpeed.value),
|
|
|
|
&(sensorValues.rw4Set.currSpeed.value), rwTrqNs);
|
|
|
|
VectorOperations<double>::add(torquePtgRws, rwTrqNs, torqueRws, 4);
|
|
|
|
actuatorCmd.scalingTorqueRws(torqueRws, torqueRwsScaled);
|
|
|
|
ptgCtrl.ptgDesaturation(
|
|
|
|
&acsParameters.inertialModeControllerParameters, mgmDataProcessed.mgmVecTot.value,
|
|
|
|
mgmDataProcessed.mgmVecTot.isValid(), mekfData.satRotRateMekf.value,
|
|
|
|
&(sensorValues.rw1Set.currSpeed.value), &(sensorValues.rw2Set.currSpeed.value),
|
|
|
|
&(sensorValues.rw3Set.currSpeed.value), &(sensorValues.rw4Set.currSpeed.value), mgtDpDes);
|
2023-02-20 15:59:01 +01:00
|
|
|
enableAntiStiction = acsParameters.inertialModeControllerParameters.enableAntiStiction;
|
2023-01-23 16:34:52 +01:00
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (enableAntiStiction) {
|
2023-02-17 09:21:30 +01:00
|
|
|
ptgCtrl.rwAntistiction(&sensorValues, torqueRwsScaled);
|
2023-01-23 16:34:52 +01:00
|
|
|
}
|
|
|
|
|
2023-02-10 11:27:25 +01:00
|
|
|
actuatorCmd.cmdSpeedToRws(sensorValues.rw1Set.currSpeed.value,
|
|
|
|
sensorValues.rw2Set.currSpeed.value,
|
|
|
|
sensorValues.rw3Set.currSpeed.value,
|
|
|
|
sensorValues.rw4Set.currSpeed.value, torqueRwsScaled, cmdSpeedRws);
|
|
|
|
actuatorCmd.cmdDipolMtq(mgtDpDes, cmdDipolMtqs);
|
2023-01-23 16:34:52 +01:00
|
|
|
|
2023-02-24 15:34:51 +01:00
|
|
|
updateCtrlValData(targetQuat, errorQuat, errorAngle, targetSatRotRate);
|
2023-02-17 11:11:48 +01:00
|
|
|
updateActuatorCmdData(rwTrqNs, cmdSpeedRws, cmdDipolMtqs);
|
2023-02-10 11:30:52 +01:00
|
|
|
// commandActuators(cmdDipolMtqs[0], cmdDipolMtqs[1], cmdDipolMtqs[2],
|
|
|
|
// acsParameters.magnetorquesParameter.torqueDuration, cmdSpeedRws[0],
|
|
|
|
// cmdSpeedRws[1], cmdSpeedRws[2], cmdSpeedRws[3],
|
|
|
|
// acsParameters.rwHandlingParameters.rampTime);
|
2023-02-10 11:27:25 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
ReturnValue_t AcsController::commandActuators(int16_t xDipole, int16_t yDipole, int16_t zDipole,
|
|
|
|
uint16_t dipoleTorqueDuration, int32_t rw1Speed,
|
|
|
|
int32_t rw2Speed, int32_t rw3Speed, int32_t rw4Speed,
|
|
|
|
uint16_t rampTime) {
|
|
|
|
{
|
|
|
|
PoolReadGuard pg(&dipoleSet);
|
2023-03-02 15:32:12 +01:00
|
|
|
MutexGuard mg(torquer::lazyLock(), torquer::LOCK_TYPE, torquer::LOCK_TIMEOUT,
|
|
|
|
torquer::LOCK_CTX);
|
2023-02-10 11:27:25 +01:00
|
|
|
torquer::NEW_ACTUATION_FLAG = true;
|
|
|
|
dipoleSet.setDipoles(xDipole, yDipole, zDipole, dipoleTorqueDuration);
|
|
|
|
}
|
|
|
|
{
|
|
|
|
PoolReadGuard pg(&rw1SpeedSet);
|
|
|
|
rw1SpeedSet.setRwSpeed(rw1Speed, rampTime);
|
|
|
|
}
|
|
|
|
{
|
|
|
|
PoolReadGuard pg(&rw2SpeedSet);
|
|
|
|
rw2SpeedSet.setRwSpeed(rw2Speed, rampTime);
|
|
|
|
}
|
|
|
|
{
|
|
|
|
PoolReadGuard pg(&rw3SpeedSet);
|
|
|
|
rw3SpeedSet.setRwSpeed(rw3Speed, rampTime);
|
|
|
|
}
|
|
|
|
{
|
|
|
|
PoolReadGuard pg(&rw4SpeedSet);
|
|
|
|
rw4SpeedSet.setRwSpeed(rw4Speed, rampTime);
|
|
|
|
}
|
|
|
|
return returnvalue::OK;
|
2023-01-23 16:34:52 +01:00
|
|
|
}
|
|
|
|
|
2023-02-23 18:34:28 +01:00
|
|
|
void AcsController::updateActuatorCmdData(const int16_t *mtqTargetDipole) {
|
2023-02-23 18:39:14 +01:00
|
|
|
updateActuatorCmdData(RW_OFF_TORQUE, RW_OFF_SPEED, mtqTargetDipole);
|
2023-02-17 11:11:48 +01:00
|
|
|
}
|
|
|
|
|
2023-02-23 18:34:28 +01:00
|
|
|
void AcsController::updateActuatorCmdData(const double *rwTargetTorque,
|
|
|
|
const int32_t *rwTargetSpeed,
|
|
|
|
const int16_t *mtqTargetDipole) {
|
2023-02-23 18:39:14 +01:00
|
|
|
PoolReadGuard pg(&actuatorCmdData);
|
|
|
|
if (pg.getReadResult() == returnvalue::OK) {
|
|
|
|
std::memcpy(actuatorCmdData.rwTargetTorque.value, rwTargetTorque, 4 * sizeof(double));
|
|
|
|
std::memcpy(actuatorCmdData.rwTargetSpeed.value, rwTargetSpeed, 4 * sizeof(int32_t));
|
|
|
|
std::memcpy(actuatorCmdData.mtqTargetDipole.value, mtqTargetDipole, 3 * sizeof(int16_t));
|
|
|
|
actuatorCmdData.setValidity(true, true);
|
2023-02-17 11:11:48 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2023-02-17 13:28:29 +01:00
|
|
|
void AcsController::updateCtrlValData(double errAng) {
|
2023-02-23 18:34:28 +01:00
|
|
|
PoolReadGuard pg(&ctrlValData);
|
|
|
|
if (pg.getReadResult() == returnvalue::OK) {
|
|
|
|
std::memcpy(ctrlValData.tgtQuat.value, UNIT_QUAT, 4 * sizeof(double));
|
|
|
|
ctrlValData.tgtQuat.setValid(false);
|
|
|
|
std::memcpy(ctrlValData.errQuat.value, UNIT_QUAT, 4 * sizeof(double));
|
|
|
|
ctrlValData.errQuat.setValid(false);
|
|
|
|
ctrlValData.errAng.value = errAng;
|
|
|
|
ctrlValData.errAng.setValid(true);
|
2023-02-24 15:34:51 +01:00
|
|
|
std::memcpy(ctrlValData.tgtRotRate.value, ZERO_VEC, 3 * sizeof(double));
|
|
|
|
ctrlValData.tgtRotRate.setValid(false);
|
2023-02-23 18:34:28 +01:00
|
|
|
ctrlValData.setValidity(true, false);
|
2023-02-17 13:28:29 +01:00
|
|
|
}
|
|
|
|
}
|
2023-02-21 11:13:06 +01:00
|
|
|
|
2023-02-24 15:34:51 +01:00
|
|
|
void AcsController::updateCtrlValData(const double *tgtQuat, const double *errQuat, double errAng,
|
|
|
|
const double *tgtRotRate) {
|
2023-02-23 18:39:14 +01:00
|
|
|
PoolReadGuard pg(&ctrlValData);
|
|
|
|
if (pg.getReadResult() == returnvalue::OK) {
|
|
|
|
std::memcpy(ctrlValData.tgtQuat.value, tgtQuat, 4 * sizeof(double));
|
|
|
|
std::memcpy(ctrlValData.errQuat.value, errQuat, 4 * sizeof(double));
|
|
|
|
ctrlValData.errAng.value = errAng;
|
2023-02-24 15:34:51 +01:00
|
|
|
std::memcpy(ctrlValData.tgtRotRate.value, tgtRotRate, 3 * sizeof(double));
|
2023-02-23 18:39:14 +01:00
|
|
|
ctrlValData.setValidity(true, true);
|
2023-02-17 13:28:29 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2023-02-21 11:13:06 +01:00
|
|
|
void AcsController::disableCtrlValData() {
|
2023-02-23 18:39:14 +01:00
|
|
|
PoolReadGuard pg(&ctrlValData);
|
|
|
|
if (pg.getReadResult() == returnvalue::OK) {
|
|
|
|
std::memcpy(ctrlValData.tgtQuat.value, UNIT_QUAT, 4 * sizeof(double));
|
|
|
|
std::memcpy(ctrlValData.errQuat.value, UNIT_QUAT, 4 * sizeof(double));
|
|
|
|
ctrlValData.errAng.value = 0;
|
2023-02-24 15:34:51 +01:00
|
|
|
std::memcpy(ctrlValData.tgtRotRate.value, ZERO_VEC, 3 * sizeof(double));
|
2023-02-23 18:39:14 +01:00
|
|
|
ctrlValData.setValidity(false, true);
|
2023-02-21 11:13:06 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2022-08-15 09:58:18 +02:00
|
|
|
ReturnValue_t AcsController::initializeLocalDataPool(localpool::DataPool &localDataPoolMap,
|
|
|
|
LocalDataPoolManager &poolManager) {
|
2023-01-23 16:34:52 +01:00
|
|
|
// MGM Raw
|
|
|
|
localDataPoolMap.emplace(acsctrl::PoolIds::MGM_0_LIS3_UT, &mgm0VecRaw);
|
|
|
|
localDataPoolMap.emplace(acsctrl::PoolIds::MGM_1_RM3100_UT, &mgm1VecRaw);
|
|
|
|
localDataPoolMap.emplace(acsctrl::PoolIds::MGM_2_LIS3_UT, &mgm2VecRaw);
|
|
|
|
localDataPoolMap.emplace(acsctrl::PoolIds::MGM_3_RM3100_UT, &mgm3VecRaw);
|
|
|
|
localDataPoolMap.emplace(acsctrl::PoolIds::MGM_IMTQ_CAL_NT, &imtqMgmVecRaw);
|
|
|
|
localDataPoolMap.emplace(acsctrl::PoolIds::MGM_IMTQ_CAL_ACT_STATUS, &imtqCalActStatus);
|
|
|
|
poolManager.subscribeForRegularPeriodicPacket({mgmDataRaw.getSid(), false, 5.0});
|
|
|
|
// MGM Processed
|
|
|
|
localDataPoolMap.emplace(acsctrl::PoolIds::MGM_0_VEC, &mgm0VecProc);
|
|
|
|
localDataPoolMap.emplace(acsctrl::PoolIds::MGM_1_VEC, &mgm1VecProc);
|
|
|
|
localDataPoolMap.emplace(acsctrl::PoolIds::MGM_2_VEC, &mgm2VecProc);
|
|
|
|
localDataPoolMap.emplace(acsctrl::PoolIds::MGM_3_VEC, &mgm3VecProc);
|
|
|
|
localDataPoolMap.emplace(acsctrl::PoolIds::MGM_4_VEC, &mgm4VecProc);
|
|
|
|
localDataPoolMap.emplace(acsctrl::PoolIds::MGM_VEC_TOT, &mgmVecTot);
|
|
|
|
localDataPoolMap.emplace(acsctrl::PoolIds::MGM_VEC_TOT_DERIVATIVE, &mgmVecTotDer);
|
|
|
|
localDataPoolMap.emplace(acsctrl::PoolIds::MAG_IGRF_MODEL, &magIgrf);
|
|
|
|
poolManager.subscribeForRegularPeriodicPacket({mgmDataProcessed.getSid(), false, 5.0});
|
|
|
|
// SUS Raw
|
|
|
|
localDataPoolMap.emplace(acsctrl::PoolIds::SUS_0_N_LOC_XFYFZM_PT_XF, &sus0ValRaw);
|
|
|
|
localDataPoolMap.emplace(acsctrl::PoolIds::SUS_1_N_LOC_XBYFZM_PT_XB, &sus1ValRaw);
|
|
|
|
localDataPoolMap.emplace(acsctrl::PoolIds::SUS_2_N_LOC_XFYBZB_PT_YB, &sus2ValRaw);
|
|
|
|
localDataPoolMap.emplace(acsctrl::PoolIds::SUS_3_N_LOC_XFYBZF_PT_YF, &sus3ValRaw);
|
|
|
|
localDataPoolMap.emplace(acsctrl::PoolIds::SUS_4_N_LOC_XMYFZF_PT_ZF, &sus4ValRaw);
|
|
|
|
localDataPoolMap.emplace(acsctrl::PoolIds::SUS_5_N_LOC_XFYMZB_PT_ZB, &sus5ValRaw);
|
|
|
|
localDataPoolMap.emplace(acsctrl::PoolIds::SUS_6_R_LOC_XFYBZM_PT_XF, &sus6ValRaw);
|
|
|
|
localDataPoolMap.emplace(acsctrl::PoolIds::SUS_7_R_LOC_XBYBZM_PT_XB, &sus7ValRaw);
|
|
|
|
localDataPoolMap.emplace(acsctrl::PoolIds::SUS_8_R_LOC_XBYBZB_PT_YB, &sus8ValRaw);
|
|
|
|
localDataPoolMap.emplace(acsctrl::PoolIds::SUS_9_R_LOC_XBYBZB_PT_YF, &sus9ValRaw);
|
|
|
|
localDataPoolMap.emplace(acsctrl::PoolIds::SUS_10_N_LOC_XMYBZF_PT_ZF, &sus10ValRaw);
|
|
|
|
localDataPoolMap.emplace(acsctrl::PoolIds::SUS_11_R_LOC_XBYMZB_PT_ZB, &sus11ValRaw);
|
|
|
|
poolManager.subscribeForRegularPeriodicPacket({susDataRaw.getSid(), false, 5.0});
|
|
|
|
// SUS Processed
|
|
|
|
localDataPoolMap.emplace(acsctrl::PoolIds::SUS_0_VEC, &sus0VecProc);
|
|
|
|
localDataPoolMap.emplace(acsctrl::PoolIds::SUS_1_VEC, &sus1VecProc);
|
|
|
|
localDataPoolMap.emplace(acsctrl::PoolIds::SUS_2_VEC, &sus2VecProc);
|
|
|
|
localDataPoolMap.emplace(acsctrl::PoolIds::SUS_3_VEC, &sus3VecProc);
|
|
|
|
localDataPoolMap.emplace(acsctrl::PoolIds::SUS_4_VEC, &sus4VecProc);
|
|
|
|
localDataPoolMap.emplace(acsctrl::PoolIds::SUS_5_VEC, &sus5VecProc);
|
|
|
|
localDataPoolMap.emplace(acsctrl::PoolIds::SUS_6_VEC, &sus6VecProc);
|
|
|
|
localDataPoolMap.emplace(acsctrl::PoolIds::SUS_7_VEC, &sus7VecProc);
|
|
|
|
localDataPoolMap.emplace(acsctrl::PoolIds::SUS_8_VEC, &sus8VecProc);
|
|
|
|
localDataPoolMap.emplace(acsctrl::PoolIds::SUS_9_VEC, &sus9VecProc);
|
|
|
|
localDataPoolMap.emplace(acsctrl::PoolIds::SUS_10_VEC, &sus10VecProc);
|
|
|
|
localDataPoolMap.emplace(acsctrl::PoolIds::SUS_11_VEC, &sus11VecProc);
|
|
|
|
localDataPoolMap.emplace(acsctrl::PoolIds::SUS_VEC_TOT, &susVecTot);
|
|
|
|
localDataPoolMap.emplace(acsctrl::PoolIds::SUS_VEC_TOT_DERIVATIVE, &susVecTotDer);
|
|
|
|
localDataPoolMap.emplace(acsctrl::PoolIds::SUN_IJK_MODEL, &sunIjk);
|
|
|
|
poolManager.subscribeForRegularPeriodicPacket({susDataProcessed.getSid(), false, 5.0});
|
|
|
|
// GYR Raw
|
|
|
|
localDataPoolMap.emplace(acsctrl::PoolIds::GYR_0_ADIS, &gyr0VecRaw);
|
|
|
|
localDataPoolMap.emplace(acsctrl::PoolIds::GYR_1_L3, &gyr1VecRaw);
|
|
|
|
localDataPoolMap.emplace(acsctrl::PoolIds::GYR_2_ADIS, &gyr2VecRaw);
|
|
|
|
localDataPoolMap.emplace(acsctrl::PoolIds::GYR_3_L3, &gyr3VecRaw);
|
2023-01-18 15:58:38 +01:00
|
|
|
poolManager.subscribeForDiagPeriodicPacket({gyrDataRaw.getSid(), false, 5.0});
|
2023-01-23 16:34:52 +01:00
|
|
|
// GYR Processed
|
|
|
|
localDataPoolMap.emplace(acsctrl::PoolIds::GYR_0_VEC, &gyr0VecProc);
|
|
|
|
localDataPoolMap.emplace(acsctrl::PoolIds::GYR_1_VEC, &gyr1VecProc);
|
|
|
|
localDataPoolMap.emplace(acsctrl::PoolIds::GYR_2_VEC, &gyr2VecProc);
|
|
|
|
localDataPoolMap.emplace(acsctrl::PoolIds::GYR_3_VEC, &gyr3VecProc);
|
|
|
|
localDataPoolMap.emplace(acsctrl::PoolIds::GYR_VEC_TOT, &gyrVecTot);
|
2023-01-18 15:58:38 +01:00
|
|
|
poolManager.subscribeForDiagPeriodicPacket({gyrDataProcessed.getSid(), false, 5.0});
|
2023-01-23 16:34:52 +01:00
|
|
|
// GPS Processed
|
|
|
|
localDataPoolMap.emplace(acsctrl::PoolIds::GC_LATITUDE, &gcLatitude);
|
|
|
|
localDataPoolMap.emplace(acsctrl::PoolIds::GD_LONGITUDE, &gdLongitude);
|
|
|
|
localDataPoolMap.emplace(acsctrl::PoolIds::GPS_POSITION, &gpsPosition);
|
|
|
|
localDataPoolMap.emplace(acsctrl::PoolIds::GPS_VELOCITY, &gpsVelocity);
|
|
|
|
poolManager.subscribeForRegularPeriodicPacket({gpsDataProcessed.getSid(), false, 5.0});
|
|
|
|
// MEKF
|
|
|
|
localDataPoolMap.emplace(acsctrl::PoolIds::QUAT_MEKF, &quatMekf);
|
|
|
|
localDataPoolMap.emplace(acsctrl::PoolIds::SAT_ROT_RATE_MEKF, &satRotRateMekf);
|
2023-02-21 16:02:26 +01:00
|
|
|
localDataPoolMap.emplace(acsctrl::PoolIds::MEKF_STATUS, &mekfStatus);
|
2023-01-18 16:02:57 +01:00
|
|
|
poolManager.subscribeForDiagPeriodicPacket({mekfData.getSid(), false, 5.0});
|
2023-01-23 16:34:52 +01:00
|
|
|
// Ctrl Values
|
|
|
|
localDataPoolMap.emplace(acsctrl::PoolIds::TGT_QUAT, &tgtQuat);
|
|
|
|
localDataPoolMap.emplace(acsctrl::PoolIds::ERROR_QUAT, &errQuat);
|
|
|
|
localDataPoolMap.emplace(acsctrl::PoolIds::ERROR_ANG, &errAng);
|
2023-02-17 15:15:47 +01:00
|
|
|
localDataPoolMap.emplace(acsctrl::PoolIds::TGT_ROT_RATE, &tgtRotRate);
|
2023-01-23 16:34:52 +01:00
|
|
|
poolManager.subscribeForRegularPeriodicPacket({ctrlValData.getSid(), false, 5.0});
|
|
|
|
// Actuator CMD
|
|
|
|
localDataPoolMap.emplace(acsctrl::PoolIds::RW_TARGET_TORQUE, &rwTargetTorque);
|
|
|
|
localDataPoolMap.emplace(acsctrl::PoolIds::RW_TARGET_SPEED, &rwTargetSpeed);
|
|
|
|
localDataPoolMap.emplace(acsctrl::PoolIds::MTQ_TARGET_DIPOLE, &mtqTargetDipole);
|
|
|
|
poolManager.subscribeForRegularPeriodicPacket({actuatorCmdData.getSid(), false, 5.0});
|
|
|
|
return returnvalue::OK;
|
|
|
|
}
|
|
|
|
|
2022-08-16 11:56:17 +02:00
|
|
|
LocalPoolDataSetBase *AcsController::getDataSetHandle(sid_t sid) {
|
2023-01-23 16:34:52 +01:00
|
|
|
switch (sid.ownerSetId) {
|
|
|
|
case acsctrl::MGM_SENSOR_DATA:
|
|
|
|
return &mgmDataRaw;
|
|
|
|
case acsctrl::MGM_PROCESSED_DATA:
|
|
|
|
return &mgmDataProcessed;
|
|
|
|
case acsctrl::SUS_SENSOR_DATA:
|
|
|
|
return &susDataRaw;
|
|
|
|
case acsctrl::SUS_PROCESSED_DATA:
|
|
|
|
return &susDataProcessed;
|
|
|
|
case acsctrl::GYR_SENSOR_DATA:
|
|
|
|
return &gyrDataRaw;
|
|
|
|
case acsctrl::GYR_PROCESSED_DATA:
|
|
|
|
return &gyrDataProcessed;
|
|
|
|
case acsctrl::GPS_PROCESSED_DATA:
|
|
|
|
return &gpsDataProcessed;
|
|
|
|
case acsctrl::MEKF_DATA:
|
|
|
|
return &mekfData;
|
|
|
|
case acsctrl::CTRL_VAL_DATA:
|
|
|
|
return &ctrlValData;
|
|
|
|
case acsctrl::ACTUATOR_CMD_DATA:
|
|
|
|
return &actuatorCmdData;
|
|
|
|
default:
|
|
|
|
return nullptr;
|
|
|
|
}
|
|
|
|
return nullptr;
|
|
|
|
}
|
|
|
|
|
|
|
|
ReturnValue_t AcsController::checkModeCommand(Mode_t mode, Submode_t submode,
|
2022-08-15 09:58:18 +02:00
|
|
|
uint32_t *msToReachTheMode) {
|
2023-01-23 16:34:52 +01:00
|
|
|
if (mode == MODE_OFF) {
|
|
|
|
if (submode == SUBMODE_NONE) {
|
|
|
|
return returnvalue::OK;
|
|
|
|
} else {
|
|
|
|
return INVALID_SUBMODE;
|
2022-09-30 11:06:17 +02:00
|
|
|
}
|
2023-01-23 16:34:52 +01:00
|
|
|
} else if ((mode == MODE_ON) || (mode == MODE_NORMAL)) {
|
2023-02-08 13:40:03 +01:00
|
|
|
if ((submode < acs::AcsMode::SAFE) or (submode > acs::AcsMode::PTG_INERTIAL)) {
|
2023-01-23 16:34:52 +01:00
|
|
|
return INVALID_SUBMODE;
|
|
|
|
} else {
|
|
|
|
return returnvalue::OK;
|
2022-09-30 11:06:17 +02:00
|
|
|
}
|
|
|
|
}
|
2023-01-23 16:34:52 +01:00
|
|
|
return INVALID_MODE;
|
|
|
|
}
|
2022-08-15 10:50:19 +02:00
|
|
|
|
2023-02-13 01:26:30 +01:00
|
|
|
void AcsController::modeChanged(Mode_t mode, Submode_t submode) {
|
|
|
|
return ExtendedControllerBase::modeChanged(mode, submode);
|
|
|
|
}
|
2022-10-04 13:45:13 +02:00
|
|
|
|
2023-02-13 01:26:30 +01:00
|
|
|
void AcsController::announceMode(bool recursive) {
|
2023-02-13 10:03:16 +01:00
|
|
|
const char *modeStr = "UNKNOWN";
|
|
|
|
if (mode == HasModesIF::MODE_OFF) {
|
2023-02-13 09:56:57 +01:00
|
|
|
modeStr = "OFF";
|
2023-02-13 10:03:16 +01:00
|
|
|
} else if (mode == HasModesIF::MODE_ON) {
|
2023-02-13 09:56:57 +01:00
|
|
|
modeStr = "ON";
|
2023-02-13 10:03:16 +01:00
|
|
|
} else if (mode == DeviceHandlerIF::MODE_NORMAL) {
|
2023-02-13 09:56:57 +01:00
|
|
|
modeStr = "NORMAL";
|
|
|
|
}
|
2023-02-13 10:03:16 +01:00
|
|
|
const char *submodeStr = acs::getModeStr(static_cast<acs::AcsMode>(submode));
|
|
|
|
sif::info << "ACS controller is now in " << modeStr << " mode with " << submodeStr << " submode"
|
|
|
|
<< std::endl;
|
2023-02-13 01:26:30 +01:00
|
|
|
return ExtendedControllerBase::announceMode(recursive);
|
|
|
|
}
|
2022-10-04 13:45:13 +02:00
|
|
|
|
2023-01-23 16:34:52 +01:00
|
|
|
void AcsController::copyMgmData() {
|
|
|
|
{
|
|
|
|
PoolReadGuard pg(&sensorValues.mgm0Lis3Set);
|
|
|
|
if (pg.getReadResult() == returnvalue::OK) {
|
|
|
|
std::memcpy(mgmDataRaw.mgm0Lis3.value, sensorValues.mgm0Lis3Set.fieldStrengths.value,
|
|
|
|
3 * sizeof(float));
|
|
|
|
mgmDataRaw.mgm0Lis3.setValid(sensorValues.mgm0Lis3Set.fieldStrengths.isValid());
|
2022-08-15 11:19:08 +02:00
|
|
|
}
|
2023-01-23 16:34:52 +01:00
|
|
|
}
|
|
|
|
{
|
|
|
|
PoolReadGuard pg(&sensorValues.mgm1Rm3100Set);
|
|
|
|
if (pg.getReadResult() == returnvalue::OK) {
|
|
|
|
std::memcpy(mgmDataRaw.mgm1Rm3100.value, sensorValues.mgm1Rm3100Set.fieldStrengths.value,
|
|
|
|
3 * sizeof(float));
|
|
|
|
mgmDataRaw.mgm1Rm3100.setValid(sensorValues.mgm1Rm3100Set.fieldStrengths.isValid());
|
2022-08-15 11:19:08 +02:00
|
|
|
}
|
2023-01-23 16:34:52 +01:00
|
|
|
}
|
|
|
|
{
|
|
|
|
PoolReadGuard pg(&sensorValues.mgm2Lis3Set);
|
|
|
|
if (pg.getReadResult() == returnvalue::OK) {
|
|
|
|
std::memcpy(mgmDataRaw.mgm2Lis3.value, sensorValues.mgm2Lis3Set.fieldStrengths.value,
|
|
|
|
3 * sizeof(float));
|
|
|
|
mgmDataRaw.mgm2Lis3.setValid(sensorValues.mgm2Lis3Set.fieldStrengths.isValid());
|
2022-08-15 11:19:08 +02:00
|
|
|
}
|
2023-01-23 16:34:52 +01:00
|
|
|
}
|
|
|
|
{
|
|
|
|
PoolReadGuard pg(&sensorValues.mgm3Rm3100Set);
|
|
|
|
if (pg.getReadResult() == returnvalue::OK) {
|
|
|
|
std::memcpy(mgmDataRaw.mgm3Rm3100.value, sensorValues.mgm3Rm3100Set.fieldStrengths.value,
|
|
|
|
3 * sizeof(float));
|
|
|
|
mgmDataRaw.mgm3Rm3100.setValid(sensorValues.mgm3Rm3100Set.fieldStrengths.isValid());
|
2022-08-15 11:19:08 +02:00
|
|
|
}
|
2023-01-23 16:34:52 +01:00
|
|
|
}
|
|
|
|
{
|
|
|
|
PoolReadGuard pg(&sensorValues.imtqMgmSet);
|
|
|
|
if (pg.getReadResult() == returnvalue::OK) {
|
|
|
|
std::memcpy(mgmDataRaw.imtqRaw.value, sensorValues.imtqMgmSet.mtmRawNt.value,
|
|
|
|
3 * sizeof(float));
|
|
|
|
mgmDataRaw.imtqRaw.setValid(sensorValues.imtqMgmSet.mtmRawNt.isValid());
|
|
|
|
mgmDataRaw.actuationCalStatus.value = sensorValues.imtqMgmSet.coilActuationStatus.value;
|
|
|
|
mgmDataRaw.actuationCalStatus.setValid(sensorValues.imtqMgmSet.coilActuationStatus.isValid());
|
2022-08-15 11:19:08 +02:00
|
|
|
}
|
|
|
|
}
|
2023-01-23 16:34:52 +01:00
|
|
|
}
|
2022-09-28 15:27:51 +02:00
|
|
|
|
2023-01-23 16:34:52 +01:00
|
|
|
void AcsController::copySusData() {
|
|
|
|
{
|
|
|
|
PoolReadGuard pg(&sensorValues.susSets[0]);
|
|
|
|
if (pg.getReadResult() == returnvalue::OK) {
|
|
|
|
std::memcpy(susDataRaw.sus0.value, sensorValues.susSets[0].channels.value,
|
|
|
|
6 * sizeof(uint16_t));
|
|
|
|
susDataRaw.sus0.setValid(sensorValues.susSets[0].channels.isValid());
|
2022-10-24 10:41:28 +02:00
|
|
|
}
|
2023-01-23 16:34:52 +01:00
|
|
|
}
|
|
|
|
{
|
|
|
|
PoolReadGuard pg(&sensorValues.susSets[1]);
|
|
|
|
if (pg.getReadResult() == returnvalue::OK) {
|
|
|
|
std::memcpy(susDataRaw.sus1.value, sensorValues.susSets[1].channels.value,
|
|
|
|
6 * sizeof(uint16_t));
|
|
|
|
susDataRaw.sus1.setValid(sensorValues.susSets[1].channels.isValid());
|
2022-10-24 10:41:28 +02:00
|
|
|
}
|
2023-01-23 16:34:52 +01:00
|
|
|
}
|
|
|
|
{
|
|
|
|
PoolReadGuard pg(&sensorValues.susSets[2]);
|
|
|
|
if (pg.getReadResult() == returnvalue::OK) {
|
|
|
|
std::memcpy(susDataRaw.sus2.value, sensorValues.susSets[2].channels.value,
|
|
|
|
6 * sizeof(uint16_t));
|
|
|
|
susDataRaw.sus2.setValid(sensorValues.susSets[2].channels.isValid());
|
2022-10-24 10:41:28 +02:00
|
|
|
}
|
2023-01-23 16:34:52 +01:00
|
|
|
}
|
|
|
|
{
|
|
|
|
PoolReadGuard pg(&sensorValues.susSets[3]);
|
|
|
|
if (pg.getReadResult() == returnvalue::OK) {
|
|
|
|
std::memcpy(susDataRaw.sus3.value, sensorValues.susSets[3].channels.value,
|
|
|
|
6 * sizeof(uint16_t));
|
|
|
|
susDataRaw.sus3.setValid(sensorValues.susSets[3].channels.isValid());
|
2022-09-28 15:27:51 +02:00
|
|
|
}
|
2023-01-23 16:34:52 +01:00
|
|
|
}
|
|
|
|
{
|
|
|
|
PoolReadGuard pg(&sensorValues.susSets[4]);
|
|
|
|
if (pg.getReadResult() == returnvalue::OK) {
|
|
|
|
std::memcpy(susDataRaw.sus4.value, sensorValues.susSets[4].channels.value,
|
|
|
|
6 * sizeof(uint16_t));
|
|
|
|
susDataRaw.sus4.setValid(sensorValues.susSets[4].channels.isValid());
|
2022-09-28 15:27:51 +02:00
|
|
|
}
|
2023-01-23 16:34:52 +01:00
|
|
|
}
|
|
|
|
{
|
|
|
|
PoolReadGuard pg(&sensorValues.susSets[5]);
|
|
|
|
if (pg.getReadResult() == returnvalue::OK) {
|
|
|
|
std::memcpy(susDataRaw.sus5.value, sensorValues.susSets[5].channels.value,
|
|
|
|
6 * sizeof(uint16_t));
|
|
|
|
susDataRaw.sus5.setValid(sensorValues.susSets[5].channels.isValid());
|
2022-09-28 15:27:51 +02:00
|
|
|
}
|
2023-01-23 16:34:52 +01:00
|
|
|
}
|
|
|
|
{
|
|
|
|
PoolReadGuard pg(&sensorValues.susSets[6]);
|
|
|
|
if (pg.getReadResult() == returnvalue::OK) {
|
|
|
|
std::memcpy(susDataRaw.sus6.value, sensorValues.susSets[6].channels.value,
|
|
|
|
6 * sizeof(uint16_t));
|
|
|
|
susDataRaw.sus6.setValid(sensorValues.susSets[6].channels.isValid());
|
2022-09-28 15:27:51 +02:00
|
|
|
}
|
2023-01-23 16:34:52 +01:00
|
|
|
}
|
|
|
|
{
|
|
|
|
PoolReadGuard pg(&sensorValues.susSets[7]);
|
|
|
|
if (pg.getReadResult() == returnvalue::OK) {
|
|
|
|
std::memcpy(susDataRaw.sus7.value, sensorValues.susSets[7].channels.value,
|
|
|
|
6 * sizeof(uint16_t));
|
|
|
|
susDataRaw.sus7.setValid(sensorValues.susSets[7].channels.isValid());
|
2022-09-28 15:27:51 +02:00
|
|
|
}
|
2023-01-23 16:34:52 +01:00
|
|
|
}
|
|
|
|
{
|
|
|
|
PoolReadGuard pg(&sensorValues.susSets[8]);
|
|
|
|
if (pg.getReadResult() == returnvalue::OK) {
|
|
|
|
std::memcpy(susDataRaw.sus8.value, sensorValues.susSets[8].channels.value,
|
|
|
|
6 * sizeof(uint16_t));
|
|
|
|
susDataRaw.sus8.setValid(sensorValues.susSets[8].channels.isValid());
|
2022-09-28 15:27:51 +02:00
|
|
|
}
|
2023-01-23 16:34:52 +01:00
|
|
|
}
|
|
|
|
{
|
|
|
|
PoolReadGuard pg(&sensorValues.susSets[9]);
|
|
|
|
if (pg.getReadResult() == returnvalue::OK) {
|
|
|
|
std::memcpy(susDataRaw.sus9.value, sensorValues.susSets[9].channels.value,
|
|
|
|
6 * sizeof(uint16_t));
|
|
|
|
susDataRaw.sus9.setValid(sensorValues.susSets[9].channels.isValid());
|
2022-09-28 15:27:51 +02:00
|
|
|
}
|
2023-01-23 16:34:52 +01:00
|
|
|
}
|
|
|
|
{
|
|
|
|
PoolReadGuard pg(&sensorValues.susSets[10]);
|
|
|
|
if (pg.getReadResult() == returnvalue::OK) {
|
|
|
|
std::memcpy(susDataRaw.sus10.value, sensorValues.susSets[10].channels.value,
|
|
|
|
6 * sizeof(uint16_t));
|
|
|
|
susDataRaw.sus10.setValid(sensorValues.susSets[10].channels.isValid());
|
2022-09-28 15:27:51 +02:00
|
|
|
}
|
2023-01-23 16:34:52 +01:00
|
|
|
}
|
|
|
|
{
|
|
|
|
PoolReadGuard pg(&sensorValues.susSets[11]);
|
|
|
|
if (pg.getReadResult() == returnvalue::OK) {
|
|
|
|
std::memcpy(susDataRaw.sus11.value, sensorValues.susSets[11].channels.value,
|
|
|
|
6 * sizeof(uint16_t));
|
|
|
|
susDataRaw.sus11.setValid(sensorValues.susSets[11].channels.isValid());
|
2022-09-28 15:27:51 +02:00
|
|
|
}
|
|
|
|
}
|
2023-01-23 16:34:52 +01:00
|
|
|
}
|
2022-10-24 10:41:28 +02:00
|
|
|
|
2023-01-23 16:34:52 +01:00
|
|
|
void AcsController::copyGyrData() {
|
|
|
|
{
|
|
|
|
PoolReadGuard pg(&sensorValues.gyr0AdisSet);
|
|
|
|
if (pg.getReadResult() == returnvalue::OK) {
|
|
|
|
gyrDataRaw.gyr0Adis.value[0] = sensorValues.gyr0AdisSet.angVelocX.value;
|
|
|
|
gyrDataRaw.gyr0Adis.value[1] = sensorValues.gyr0AdisSet.angVelocY.value;
|
|
|
|
gyrDataRaw.gyr0Adis.value[2] = sensorValues.gyr0AdisSet.angVelocZ.value;
|
|
|
|
gyrDataRaw.gyr0Adis.setValid(sensorValues.gyr0AdisSet.angVelocX.isValid() &&
|
|
|
|
sensorValues.gyr0AdisSet.angVelocY.isValid() &&
|
|
|
|
sensorValues.gyr0AdisSet.angVelocZ.isValid());
|
2022-09-28 15:27:51 +02:00
|
|
|
}
|
2023-01-23 16:34:52 +01:00
|
|
|
}
|
|
|
|
{
|
|
|
|
PoolReadGuard pg(&sensorValues.gyr1L3gSet);
|
|
|
|
if (pg.getReadResult() == returnvalue::OK) {
|
|
|
|
gyrDataRaw.gyr1L3.value[0] = sensorValues.gyr1L3gSet.angVelocX.value;
|
|
|
|
gyrDataRaw.gyr1L3.value[1] = sensorValues.gyr1L3gSet.angVelocY.value;
|
|
|
|
gyrDataRaw.gyr1L3.value[2] = sensorValues.gyr1L3gSet.angVelocZ.value;
|
|
|
|
gyrDataRaw.gyr1L3.setValid(sensorValues.gyr1L3gSet.angVelocX.isValid() &&
|
|
|
|
sensorValues.gyr1L3gSet.angVelocY.isValid() &&
|
|
|
|
sensorValues.gyr1L3gSet.angVelocZ.isValid());
|
2022-09-28 15:27:51 +02:00
|
|
|
}
|
2023-01-23 16:34:52 +01:00
|
|
|
}
|
|
|
|
{
|
|
|
|
PoolReadGuard pg(&sensorValues.gyr2AdisSet);
|
|
|
|
if (pg.getReadResult() == returnvalue::OK) {
|
|
|
|
gyrDataRaw.gyr2Adis.value[0] = sensorValues.gyr2AdisSet.angVelocX.value;
|
|
|
|
gyrDataRaw.gyr2Adis.value[1] = sensorValues.gyr2AdisSet.angVelocY.value;
|
|
|
|
gyrDataRaw.gyr2Adis.value[2] = sensorValues.gyr2AdisSet.angVelocZ.value;
|
|
|
|
gyrDataRaw.gyr2Adis.setValid(sensorValues.gyr2AdisSet.angVelocX.isValid() &&
|
|
|
|
sensorValues.gyr2AdisSet.angVelocY.isValid() &&
|
|
|
|
sensorValues.gyr2AdisSet.angVelocZ.isValid());
|
2022-09-28 15:27:51 +02:00
|
|
|
}
|
2023-01-23 16:34:52 +01:00
|
|
|
}
|
|
|
|
{
|
|
|
|
PoolReadGuard pg(&sensorValues.gyr3L3gSet);
|
|
|
|
if (pg.getReadResult() == returnvalue::OK) {
|
|
|
|
gyrDataRaw.gyr3L3.value[0] = sensorValues.gyr3L3gSet.angVelocX.value;
|
|
|
|
gyrDataRaw.gyr3L3.value[1] = sensorValues.gyr3L3gSet.angVelocY.value;
|
|
|
|
gyrDataRaw.gyr3L3.value[2] = sensorValues.gyr3L3gSet.angVelocZ.value;
|
|
|
|
gyrDataRaw.gyr3L3.setValid(sensorValues.gyr3L3gSet.angVelocX.isValid() &&
|
|
|
|
sensorValues.gyr3L3gSet.angVelocY.isValid() &&
|
|
|
|
sensorValues.gyr3L3gSet.angVelocZ.isValid());
|
2022-09-28 15:27:51 +02:00
|
|
|
}
|
|
|
|
}
|
2023-01-23 16:34:52 +01:00
|
|
|
}
|