clean up op done
Some checks are pending
EIVE/eive-obsw/pipeline/head Build started...
EIVE/eive-obsw/pipeline/pr-develop Build started...

This commit is contained in:
2023-03-26 16:42:00 +02:00
parent e7eaaa9295
commit 63b50e6101
116 changed files with 630 additions and 601 deletions

View File

@ -0,0 +1,3 @@
target_sources(
${LIB_EIVE_MISSION} PRIVATE PayloadPcduHandler.cpp RadiationSensorHandler.cpp
ScexDeviceHandler.cpp scexHelpers.cpp defs.cpp)

View File

@ -0,0 +1,825 @@
#include <fsfw/src/fsfw/datapool/PoolReadGuard.h>
#include <mission/payload/PayloadPcduHandler.h>
#include "OBSWConfig.h"
#ifdef XIPHOS_Q7S
#include <fsfw_hal/linux/UnixFileGuard.h>
#include <fsfw_hal/linux/spi/SpiComIF.h>
#include <fsfw_hal/linux/spi/SpiCookie.h>
#include <fsfw_hal/linux/utility.h>
#include <sys/ioctl.h>
#endif
#include "devices/gpioIds.h"
PayloadPcduHandler::PayloadPcduHandler(object_id_t objectId, object_id_t comIF, CookieIF* cookie,
GpioIF* gpioIF, SdCardMountedIF* sdcMan,
Stack5VHandler& stackHandler, bool periodicPrintout)
: DeviceHandlerBase(objectId, comIF, cookie),
adcSet(this),
stackHandler(stackHandler),
periodicPrintout(periodicPrintout),
gpioIF(gpioIF),
sdcMan(sdcMan) {}
void PayloadPcduHandler::doStartUp() {
if (state > States::STACK_5V_CORRECT) {
// Config error
sif::error << "PayloadPcduHandler::doStartUp: Invalid state" << std::endl;
}
clearSetOnOffFlag = true;
if (state == States::PL_PCDU_OFF) {
state = States::STACK_5V_SWITCHING;
}
if (state == States::STACK_5V_SWITCHING) {
ReturnValue_t retval = stackHandler.deviceToOn(StackCommander::PL_PCDU, true);
if (retval == BUSY) {
return;
}
state = States::STACK_5V_PENDING;
}
if (state == States::STACK_5V_PENDING) {
if (stackHandler.isSwitchOn()) {
state = States::STACK_5V_CORRECT;
}
}
if (state == States::STACK_5V_CORRECT) {
quickTransitionAlreadyCalled = false;
setMode(_MODE_TO_ON);
}
}
void PayloadPcduHandler::doShutDown() {
if (not quickTransitionAlreadyCalled) {
quickTransitionBackToOff(false, false);
quickTransitionAlreadyCalled = true;
}
if (clearSetOnOffFlag) {
std::memset(adcSet.processed.value, 0, adcSet.processed.getSerializedSize());
clearSetOnOffFlag = false;
}
ReturnValue_t retval = stackHandler.deviceToOff(StackCommander::PL_PCDU, true);
if (retval == BUSY) {
return;
}
state = States::PL_PCDU_OFF;
// No need to set mode _MODE_POWER_DOWN, power switching was already handled
setMode(MODE_OFF);
}
void PayloadPcduHandler::doTransition(Mode_t modeFrom, Submode_t subModeFrom) {
if (getMode() == _MODE_TO_NORMAL) {
stateMachineToNormal(modeFrom, subModeFrom);
return;
} else if (getMode() == _MODE_TO_ON and modeFrom == MODE_NORMAL) {
gpioIF->pullLow(gpioIds::PLPCDU_ENB_HPA);
gpioIF->pullLow(gpioIds::PLPCDU_ENB_MPA);
gpioIF->pullLow(gpioIds::PLPCDU_ENB_TX);
gpioIF->pullLow(gpioIds::PLPCDU_ENB_X8);
gpioIF->pullLow(gpioIds::PLPCDU_ENB_DRO);
gpioIF->pullLow(gpioIds::PLPCDU_ENB_TX);
gpioIF->pullLow(gpioIds::PLPCDU_ENB_VBAT0);
gpioIF->pullLow(gpioIds::PLPCDU_ENB_VBAT1);
state = States::STACK_5V_CORRECT;
}
DeviceHandlerBase::doTransition(modeFrom, subModeFrom);
}
ReturnValue_t PayloadPcduHandler::stateMachineToNormal(Mode_t modeFrom, Submode_t subModeFrom) {
using namespace plpcdu;
bool doFinish = true;
if (((getSubmode() >> SOLID_STATE_RELAYS_ADC_ON) & 0b1) == 1) {
if (state == States::PL_PCDU_OFF) {
sif::error << "PayloadPcduHandler::stateMachineToNormal: Unexpected state PL_PCDU_OFF"
<< "detected" << std::endl;
setMode(MODE_OFF);
return returnvalue::FAILED;
}
if (state == States::STACK_5V_CORRECT) {
#if OBSW_VERBOSE_LEVEL >= 1
sif::info << "Switching on SSR VBAT0 & VBAT1 GPIOs" << std::endl;
#endif
// Switch on relays here
gpioIF->pullHigh(gpioIds::PLPCDU_ENB_VBAT0);
gpioIF->pullHigh(gpioIds::PLPCDU_ENB_VBAT1);
state = States::ON_TRANS_SSR;
transitionOk = true;
doFinish = false;
}
if (state == States::ON_TRANS_SSR) {
// If necessary, check whether a certain amount of time has elapsed
if (transitionOk) {
transitionOk = false;
state = States::ON_TRANS_ADC_CLOSE_ZERO;
adcCountdown.setTimeout(50);
adcCountdown.resetTimer();
adcState = AdcStates::BOOT_DELAY;
doFinish = false;
// If the values are not close to zero, we should not allow transition
monMode = MonitoringMode::CLOSE_TO_ZERO;
}
}
if (state == States::ON_TRANS_ADC_CLOSE_ZERO) {
if (adcState == AdcStates::BOOT_DELAY) {
doFinish = false;
if (adcCountdown.hasTimedOut()) {
adcState = AdcStates::SEND_SETUP;
adcCmdExecuted = false;
}
}
if (adcState == AdcStates::SEND_SETUP) {
if (adcCmdExecuted) {
adcState = AdcStates::NORMAL;
doFinish = true;
adcCountdown.setTimeout(100);
adcCountdown.resetTimer();
adcCmdExecuted = false;
}
}
}
}
auto switchHandler = [&](NormalSubmodeBits bit, gpioId_t id, std::string info) {
if (((diffMask >> bit) & 1) == 1) {
if (((getSubmode() >> bit) & 1) == 1) {
#if OBSW_VERBOSE_LEVEL >= 1
sif::info << "Enabling PL PCDU " << info << " module" << std::endl;
#endif
// Switch on DRO and start monitoring for negative voltages
updateSwitchGpio(id, gpio::Levels::HIGH);
} else {
#if OBSW_VERBOSE_LEVEL >= 1
sif::info << "Disabling PL PCDU " << info << " module" << std::endl;
#endif
updateSwitchGpio(id, gpio::Levels::LOW);
}
}
};
// sif::debug << "DIFF MASK: " << (int)diffMask << std::endl;
// No handling for the SSRs: If those are pulled low, the ADC is off
// and normal mode does not really make sense anyway
switchHandler(DRO_ON, gpioIds::PLPCDU_ENB_DRO, "DRO");
switchHandler(X8_ON, gpioIds::PLPCDU_ENB_X8, "X8");
switchHandler(TX_ON, gpioIds::PLPCDU_ENB_TX, "TX");
switchHandler(MPA_ON, gpioIds::PLPCDU_ENB_MPA, "MPA");
switchHandler(HPA_ON, gpioIds::PLPCDU_ENB_HPA, "HPA");
if (doFinish) {
setMode(MODE_NORMAL);
}
return returnvalue::OK;
}
ReturnValue_t PayloadPcduHandler::buildNormalDeviceCommand(DeviceCommandId_t* id) {
switch (adcState) {
case (AdcStates::SEND_SETUP): {
*id = plpcdu::SETUP_CMD;
return buildCommandFromCommand(*id, nullptr, 0);
}
case (AdcStates::NORMAL): {
*id = plpcdu::READ_WITH_TEMP_EXT;
return buildCommandFromCommand(*id, nullptr, 0);
}
default: {
break;
}
}
return NOTHING_TO_SEND;
}
ReturnValue_t PayloadPcduHandler::buildTransitionDeviceCommand(DeviceCommandId_t* id) {
if (adcState == AdcStates::SEND_SETUP) {
*id = plpcdu::SETUP_CMD;
return buildCommandFromCommand(*id, nullptr, 0);
}
if (getMode() == _MODE_TO_NORMAL) {
return buildNormalDeviceCommand(id);
}
return NOTHING_TO_SEND;
}
void PayloadPcduHandler::updateSwitchGpio(gpioId_t id, gpio::Levels level) {
if (level == gpio::Levels::HIGH) {
gpioIF->pullHigh(id);
} else {
gpioIF->pullLow(id);
}
adcCountdown.setTimeout(100);
adcCountdown.resetTimer();
}
void PayloadPcduHandler::fillCommandAndReplyMap() {
insertInCommandAndReplyMap(plpcdu::READ_CMD, 2, &adcSet);
insertInCommandAndReplyMap(plpcdu::READ_TEMP_EXT, 1, &adcSet);
insertInCommandAndReplyMap(plpcdu::READ_WITH_TEMP_EXT, 1, &adcSet);
insertInCommandAndReplyMap(plpcdu::SETUP_CMD, 1);
}
ReturnValue_t PayloadPcduHandler::buildCommandFromCommand(DeviceCommandId_t deviceCommand,
const uint8_t* commandData,
size_t commandDataLen) {
switch (deviceCommand) {
case (plpcdu::SETUP_CMD): {
cmdBuf[0] = plpcdu::SETUP_BYTE;
rawPacket = cmdBuf.data();
rawPacketLen = 1;
break;
}
case (plpcdu::READ_CMD): {
max1227::prepareExternallyClockedRead0ToN(cmdBuf.data(), plpcdu::CHANNEL_N, rawPacketLen);
rawPacket = cmdBuf.data();
break;
}
case (plpcdu::READ_TEMP_EXT): {
max1227::prepareExternallyClockedTemperatureRead(cmdBuf.data(), rawPacketLen);
rawPacket = cmdBuf.data();
break;
}
case (plpcdu::READ_WITH_TEMP_EXT): {
size_t sz = 0;
max1227::prepareExternallyClockedRead0ToN(cmdBuf.data(), plpcdu::CHANNEL_N, sz);
max1227::prepareExternallyClockedTemperatureRead(cmdBuf.data() + sz, sz);
rawPacketLen = sz;
rawPacket = cmdBuf.data();
break;
}
default: {
return DeviceHandlerIF::COMMAND_NOT_IMPLEMENTED;
}
}
return returnvalue::OK;
}
ReturnValue_t PayloadPcduHandler::scanForReply(const uint8_t* start, size_t remainingSize,
DeviceCommandId_t* foundId, size_t* foundLen) {
// SPI is full duplex
*foundId = getPendingCommand();
*foundLen = remainingSize;
return returnvalue::OK;
}
ReturnValue_t PayloadPcduHandler::interpretDeviceReply(DeviceCommandId_t id,
const uint8_t* packet) {
using namespace plpcdu;
switch (id) {
case (SETUP_CMD): {
if (getMode() == _MODE_TO_NORMAL) {
adcCmdExecuted = true;
}
break;
}
case (READ_TEMP_EXT): {
uint8_t tempStartIdx = TEMP_REPLY_SIZE - 2;
adcSet.tempC.value =
max1227::getTemperature(packet[tempStartIdx] << 8 | packet[tempStartIdx + 1]);
break;
}
case (READ_CMD): {
PoolReadGuard pg(&adcSet);
if (pg.getReadResult() != returnvalue::OK) {
return pg.getReadResult();
}
handleExtConvRead(packet);
checkAdcValues();
adcSet.setValidity(true, true);
handlePrintout();
break;
}
case (READ_WITH_TEMP_EXT): {
PoolReadGuard pg(&adcSet);
if (pg.getReadResult() != returnvalue::OK) {
return pg.getReadResult();
}
handleExtConvRead(packet);
uint8_t tempStartIdx = ADC_REPLY_SIZE + TEMP_REPLY_SIZE - 2;
adcSet.tempC.value =
max1227::getTemperature(packet[tempStartIdx] << 8 | packet[tempStartIdx + 1]);
checkAdcValues();
adcSet.setValidity(true, true);
handlePrintout();
break;
}
default: {
break;
}
}
return returnvalue::OK;
}
uint32_t PayloadPcduHandler::getTransitionDelayMs(Mode_t modeFrom, Mode_t modeTo) {
// 20 minutes transition delay is allowed
return 20 * 60 * 1000;
}
ReturnValue_t PayloadPcduHandler::initializeLocalDataPool(localpool::DataPool& localDataPoolMap,
LocalDataPoolManager& poolManager) {
localDataPoolMap.emplace(plpcdu::PlPcduPoolIds::CHANNEL_VEC, &channelValues);
localDataPoolMap.emplace(plpcdu::PlPcduPoolIds::PROCESSED_VEC, &processedValues);
localDataPoolMap.emplace(plpcdu::PlPcduPoolIds::TEMP, &tempC);
poolManager.subscribeForDiagPeriodicPacket(
subdp::DiagnosticsHkPeriodicParams(adcSet.getSid(), false, 5.0));
return returnvalue::OK;
}
void PayloadPcduHandler::setToGoToNormalModeImmediately(bool enable) {
this->goToNormalMode = enable;
}
void PayloadPcduHandler::handleExtConvRead(const uint8_t* bufStart) {
for (uint8_t idx = 0; idx < 12; idx++) {
adcSet.channels[idx] = bufStart[idx * 2 + 1] << 8 | bufStart[idx * 2 + 2];
}
}
void PayloadPcduHandler::handlePrintout() {
using namespace plpcdu;
if (periodicPrintout) {
if (opDivider.checkAndIncrement()) {
sif::info << "PL PCDU ADC hex [" << std::setfill('0') << std::hex;
for (uint8_t idx = 0; idx < 12; idx++) {
sif::info << std::setw(3) << adcSet.channels[idx];
if (idx < 11) {
sif::info << ",";
}
}
sif::info << "] | T[C] " << std::dec << adcSet.tempC.value << std::endl;
sif::info << "Neg V: " << adcSet.processed[U_NEG_V_FB] << std::endl;
sif::info << "I HPA [mA]: " << adcSet.processed[I_HPA] << std::endl;
sif::info << "U HPA [V]: " << adcSet.processed[U_HPA_DIV_6] << std::endl;
sif::info << "I MPA [mA]: " << adcSet.processed[I_MPA] << std::endl;
sif::info << "U MPA [V]: " << adcSet.processed[U_MPA_DIV_6] << std::endl;
sif::info << "I TX [mA]: " << adcSet.processed[I_TX] << std::endl;
sif::info << "U TX [V]: " << adcSet.processed[U_TX_DIV_6] << std::endl;
sif::info << "I X8 [mA]: " << adcSet.processed[I_X8] << std::endl;
sif::info << "U X8 [V]: " << adcSet.processed[U_X8_DIV_6] << std::endl;
sif::info << "I DRO [mA]: " << adcSet.processed[I_DRO] << std::endl;
sif::info << "U DRO [V]: " << adcSet.processed[U_DRO_DIV_6] << std::endl;
}
}
}
void PayloadPcduHandler::enablePeriodicPrintout(bool enable, uint8_t divider) {
this->periodicPrintout = enable;
opDivider.setDivider(divider);
}
void PayloadPcduHandler::quickTransitionBackToOff(bool startTransitionToOff, bool notifyFdir) {
States currentState = state;
gpioIF->pullLow(gpioIds::PLPCDU_ENB_HPA);
gpioIF->pullLow(gpioIds::PLPCDU_ENB_MPA);
gpioIF->pullLow(gpioIds::PLPCDU_ENB_TX);
gpioIF->pullLow(gpioIds::PLPCDU_ENB_X8);
gpioIF->pullLow(gpioIds::PLPCDU_ENB_DRO);
gpioIF->pullLow(gpioIds::PLPCDU_ENB_TX);
gpioIF->pullLow(gpioIds::PLPCDU_ENB_VBAT0);
gpioIF->pullLow(gpioIds::PLPCDU_ENB_VBAT1);
state = States::STACK_5V_SWITCHING;
adcState = AdcStates::OFF;
if (startTransitionToOff) {
startTransition(MODE_OFF, 0);
}
if (notifyFdir) {
triggerEvent(TRANSITION_BACK_TO_OFF, static_cast<uint32_t>(currentState));
}
}
void PayloadPcduHandler::checkAdcValues() {
using namespace plpcdu;
checkJsonFileInit();
adcSet.processed[U_BAT_DIV_6] =
static_cast<float>(adcSet.channels[0]) * VOLTAGE_DIV / MAX122X_BIT * MAX122X_VREF;
adcSet.processed[U_NEG_V_FB] =
V_POS - VOLTAGE_DIV_U_NEG *
(V_POS - static_cast<float>(adcSet.channels[1]) / MAX122X_BIT * MAX122X_VREF);
adcSet.processed[I_HPA] = static_cast<float>(adcSet.channels[2]) * SCALE_CURRENT_HPA * 1000.0;
adcSet.processed[U_HPA_DIV_6] = static_cast<float>(adcSet.channels[3]) * SCALE_VOLTAGE;
adcSet.processed[I_MPA] = static_cast<float>(adcSet.channels[4]) * SCALE_CURRENT_MPA * 1000.0;
adcSet.processed[U_MPA_DIV_6] = static_cast<float>(adcSet.channels[5]) * SCALE_VOLTAGE;
adcSet.processed[I_TX] = static_cast<float>(adcSet.channels[6]) * SCALE_CURRENT_TX * 1000.0;
adcSet.processed[U_TX_DIV_6] = static_cast<float>(adcSet.channels[7]) * SCALE_VOLTAGE;
adcSet.processed[I_X8] = static_cast<float>(adcSet.channels[8]) * SCALE_CURRENT_X8 * 1000.0;
adcSet.processed[U_X8_DIV_6] = static_cast<float>(adcSet.channels[9]) * SCALE_VOLTAGE;
adcSet.processed[I_DRO] = static_cast<float>(adcSet.channels[10]) * SCALE_CURRENT_DRO * 1000.0;
adcSet.processed[U_DRO_DIV_6] = static_cast<float>(adcSet.channels[11]) * SCALE_VOLTAGE;
float lowerBound = 0.0;
float upperBound = 0.0;
bool adcTransition = false;
adcTransition = state == States::ON_TRANS_DRO and adcCountdown.isBusy();
// Now check against voltage and current limits, depending on state
if (state >= States::ON_TRANS_DRO and not adcTransition) {
if (ssrToDroInjectionRequested) {
handleFailureInjection("SSR to DRO", NEG_V_OUT_OF_BOUNDS);
ssrToDroInjectionRequested = false;
return;
}
params.getValue(PARAM_KEY_MAP[NEG_V_LOWER_BOUND], lowerBound);
params.getValue(PARAM_KEY_MAP[NEG_V_UPPER_BOUND], upperBound);
if (not checkVoltage(adcSet.processed[U_NEG_V_FB], lowerBound, upperBound,
NEG_V_OUT_OF_BOUNDS)) {
return;
}
params.getValue(PARAM_KEY_MAP[DRO_U_LOWER_BOUND], lowerBound);
params.getValue(PARAM_KEY_MAP[DRO_U_UPPER_BOUND], upperBound);
if (not checkVoltage(adcSet.processed[U_DRO_DIV_6], lowerBound, upperBound,
U_DRO_OUT_OF_BOUNDS)) {
return;
}
params.getValue(PARAM_KEY_MAP[DRO_I_UPPER_BOUND], upperBound);
if (not checkCurrent(adcSet.processed[I_DRO], upperBound, I_DRO_OUT_OF_BOUNDS)) {
#if OBSW_VERBOSE_LEVEL >= 1
sif::warning << "Detected out of bounds current for DRO: " << adcSet.processed[I_DRO]
<< ", Raw: " << adcSet.channels[I_DRO] << std::endl;
#endif
return;
}
}
adcTransition = state == States::ON_TRANS_X8 and adcCountdown.isBusy();
if (state >= States::ON_TRANS_X8 and not adcTransition) {
if (droToX8InjectionRequested) {
handleFailureInjection("X8 to TX", U_X8_OUT_OF_BOUNDS);
droToX8InjectionRequested = false;
return;
}
params.getValue(PARAM_KEY_MAP[X8_U_LOWER_BOUND], lowerBound);
params.getValue(PARAM_KEY_MAP[X8_U_UPPER_BOUND], upperBound);
if (not checkVoltage(adcSet.processed[U_X8_DIV_6], lowerBound, upperBound,
U_X8_OUT_OF_BOUNDS)) {
return;
}
params.getValue(PARAM_KEY_MAP[X8_I_UPPER_BOUND], upperBound);
if (not checkCurrent(adcSet.processed[I_X8], upperBound, I_X8_OUT_OF_BOUNDS)) {
return;
}
}
adcTransition = state == States::ON_TRANS_TX and adcCountdown.isBusy();
if (state >= States::ON_TRANS_TX and not adcTransition) {
if (txToMpaInjectionRequested) {
handleFailureInjection("TX to MPA", U_TX_OUT_OF_BOUNDS);
txToMpaInjectionRequested = false;
return;
}
params.getValue(PARAM_KEY_MAP[TX_U_LOWER_BOUND], lowerBound);
params.getValue(PARAM_KEY_MAP[TX_U_UPPER_BOUND], upperBound);
if (not checkVoltage(adcSet.processed[U_TX_DIV_6], lowerBound, upperBound,
U_TX_OUT_OF_BOUNDS)) {
return;
}
params.getValue(PARAM_KEY_MAP[TX_I_UPPER_BOUND], upperBound);
if (not checkCurrent(adcSet.processed[I_TX], upperBound, I_TX_OUT_OF_BOUNDS)) {
return;
}
}
adcTransition = state == States::ON_TRANS_MPA and adcCountdown.isBusy();
if (state >= States::ON_TRANS_MPA and not adcTransition) {
if (mpaToHpaInjectionRequested) {
handleFailureInjection("MPA to HPA", U_HPA_OUT_OF_BOUNDS);
mpaToHpaInjectionRequested = false;
return;
}
params.getValue(PARAM_KEY_MAP[MPA_U_LOWER_BOUND], lowerBound);
params.getValue(PARAM_KEY_MAP[MPA_U_UPPER_BOUND], upperBound);
if (not checkVoltage(adcSet.processed[U_MPA_DIV_6], lowerBound, upperBound,
U_MPA_OUT_OF_BOUNDS)) {
return;
}
params.getValue(PARAM_KEY_MAP[MPA_I_UPPER_BOUND], upperBound);
if (not checkCurrent(adcSet.processed[I_MPA], upperBound, I_MPA_OUT_OF_BOUNDS)) {
return;
}
}
adcTransition = state == States::ON_TRANS_HPA and adcCountdown.isBusy();
if (state >= States::ON_TRANS_HPA and not adcTransition) {
if (allOnInjectRequested) {
handleFailureInjection("All On", U_HPA_OUT_OF_BOUNDS);
allOnInjectRequested = false;
return;
}
params.getValue(PARAM_KEY_MAP[HPA_U_LOWER_BOUND], lowerBound);
params.getValue(PARAM_KEY_MAP[HPA_U_UPPER_BOUND], upperBound);
if (not checkVoltage(adcSet.processed[U_HPA_DIV_6], lowerBound, upperBound,
U_HPA_OUT_OF_BOUNDS)) {
return;
}
params.getValue(PARAM_KEY_MAP[HPA_I_UPPER_BOUND], upperBound);
if (not checkCurrent(adcSet.processed[I_HPA], upperBound, I_HPA_OUT_OF_BOUNDS)) {
sif::warning << "PayloadPcduHandler::checkCurrent: I HPA exceeded limit: Measured "
<< adcSet.processed[I_HPA] << " mA" << std::endl;
return;
}
}
transitionOk = true;
}
void PayloadPcduHandler::checkJsonFileInit() {
if (not jsonFileInitComplete) {
auto activeSd = sdcMan->getActiveSdCard();
if (activeSd and sdcMan->isSdCardUsable(activeSd.value())) {
if (sdcMan->getCurrentMountPrefix() == nullptr) {
return;
}
params.initialize(sdcMan->getCurrentMountPrefix());
jsonFileInitComplete = true;
}
}
}
bool PayloadPcduHandler::checkVoltage(float val, float lowerBound, float upperBound, Event event) {
bool tooLarge = false;
if (val < lowerBound or val > upperBound) {
if (val > upperBound) {
tooLarge = true;
} else {
tooLarge = false;
}
uint32_t p2 = 0;
serializeFloat(p2, val);
triggerEvent(event, tooLarge, p2);
transitionOk = false;
quickTransitionBackToOff(true, true);
quickTransitionAlreadyCalled = true;
return false;
}
return true;
}
bool PayloadPcduHandler::checkCurrent(float val, float upperBound, Event event) {
if (val > upperBound) {
uint32_t p2 = 0;
serializeFloat(p2, val);
triggerEvent(event, true, p2);
transitionOk = false;
quickTransitionBackToOff(true, true);
quickTransitionAlreadyCalled = true;
return false;
}
return true;
}
ReturnValue_t PayloadPcduHandler::isModeCombinationValid(Mode_t mode, Submode_t submode) {
using namespace plpcdu;
if (mode == MODE_NORMAL) {
uint8_t dhbSubmode = getSubmode();
diffMask = submode ^ dhbSubmode;
// Also deals with the case where the mode is MODE_ON, submode should be 0 here
if ((((submode >> SOLID_STATE_RELAYS_ADC_ON) & 0b1) == SOLID_STATE_RELAYS_ADC_ON) and
(getMode() == MODE_NORMAL and dhbSubmode != ALL_OFF_SUBMODE)) {
return TRANS_NOT_ALLOWED;
}
if (((((submode >> DRO_ON) & 1) == 1) and
((dhbSubmode & 0b1) != (1 << SOLID_STATE_RELAYS_ADC_ON)))) {
return TRANS_NOT_ALLOWED;
}
if ((((submode >> X8_ON) & 1) == 1) and
((dhbSubmode & 0b11) != ((1 << SOLID_STATE_RELAYS_ADC_ON) | (1 << DRO_ON)))) {
return TRANS_NOT_ALLOWED;
}
if (((((submode >> TX_ON) & 1) == 1) and
((dhbSubmode & 0b111) !=
((1 << X8_ON) | (1 << DRO_ON) | (1 << SOLID_STATE_RELAYS_ADC_ON))))) {
return TRANS_NOT_ALLOWED;
}
if ((((submode >> MPA_ON) & 1) == 1 and
((dhbSubmode & 0b1111) !=
((1 << TX_ON) | (1 << X8_ON) | (1 << DRO_ON) | (1 << SOLID_STATE_RELAYS_ADC_ON))))) {
return TRANS_NOT_ALLOWED;
}
if ((((submode >> HPA_ON) & 1) == 1 and
((dhbSubmode & 0b11111) != ((1 << MPA_ON) | (1 << TX_ON) | (1 << X8_ON) | (1 << DRO_ON) |
(1 << SOLID_STATE_RELAYS_ADC_ON))))) {
return TRANS_NOT_ALLOWED;
}
return returnvalue::OK;
}
return DeviceHandlerBase::isModeCombinationValid(mode, submode);
}
ReturnValue_t PayloadPcduHandler::serializeFloat(uint32_t& param, float val) {
size_t dummy = 0;
return SerializeAdapter::serialize(&val, reinterpret_cast<uint8_t*>(&param), &dummy, 4,
SerializeIF::Endianness::NETWORK);
}
ReturnValue_t PayloadPcduHandler::getParameter(uint8_t domainId, uint8_t uniqueId,
ParameterWrapper* parameterWrapper,
const ParameterWrapper* newValues,
uint16_t startAtIndex) {
using namespace plpcdu;
switch (uniqueId) {
case (PlPcduParamIds::NEG_V_LOWER_BOUND):
case (PlPcduParamIds::NEG_V_UPPER_BOUND):
case (PlPcduParamIds::DRO_U_LOWER_BOUND):
case (PlPcduParamIds::DRO_U_UPPER_BOUND):
case (PlPcduParamIds::DRO_I_UPPER_BOUND):
case (PlPcduParamIds::X8_U_LOWER_BOUND):
case (PlPcduParamIds::X8_U_UPPER_BOUND):
case (PlPcduParamIds::X8_I_UPPER_BOUND):
case (PlPcduParamIds::TX_U_LOWER_BOUND):
case (PlPcduParamIds::TX_U_UPPER_BOUND):
case (PlPcduParamIds::TX_I_UPPER_BOUND):
case (PlPcduParamIds::MPA_U_LOWER_BOUND):
case (PlPcduParamIds::MPA_U_UPPER_BOUND):
case (PlPcduParamIds::MPA_I_UPPER_BOUND):
case (PlPcduParamIds::HPA_U_LOWER_BOUND):
case (PlPcduParamIds::HPA_U_UPPER_BOUND):
case (PlPcduParamIds::HPA_I_UPPER_BOUND):
case (PlPcduParamIds::SSR_TO_DRO_WAIT_TIME):
case (PlPcduParamIds::DRO_TO_X8_WAIT_TIME):
case (PlPcduParamIds::X8_TO_TX_WAIT_TIME):
case (PlPcduParamIds::TX_TO_MPA_WAIT_TIME):
case (PlPcduParamIds::MPA_TO_HPA_WAIT_TIME): {
handleDoubleParamUpdate(PARAM_KEY_MAP[static_cast<PlPcduParamIds>(uniqueId)],
parameterWrapper, newValues);
break;
}
case (PlPcduParamIds::INJECT_SSR_TO_DRO_FAILURE): {
ssrToDroInjectionRequested = true;
break;
}
case (PlPcduParamIds::INJECT_DRO_TO_X8_FAILURE): {
droToX8InjectionRequested = true;
break;
}
case (PlPcduParamIds::INJECT_X8_TO_TX_FAILURE): {
x8ToTxInjectionRequested = true;
break;
}
case (PlPcduParamIds::INJECT_TX_TO_MPA_FAILURE): {
txToMpaInjectionRequested = true;
break;
}
case (PlPcduParamIds::INJECT_MPA_TO_HPA_FAILURE): {
mpaToHpaInjectionRequested = true;
break;
}
case (PlPcduParamIds::INJECT_ALL_ON_FAILURE): {
allOnInjectRequested = true;
break;
}
default: {
return DeviceHandlerBase::getParameter(domainId, uniqueId, parameterWrapper, newValues,
startAtIndex);
}
}
return returnvalue::OK;
}
void PayloadPcduHandler::handleFailureInjection(std::string output, Event event) {
sif::info << "PayloadPcduHandler::handleFailureInjection: " << output
<< " failure injection. "
"Transitioning back to off"
<< std::endl;
triggerEvent(event, 0, 0);
transitionOk = false;
quickTransitionBackToOff(true, true);
quickTransitionAlreadyCalled = true;
droToX8InjectionRequested = false;
}
ReturnValue_t PayloadPcduHandler::handleDoubleParamUpdate(std::string key,
ParameterWrapper* parameterWrapper,
const ParameterWrapper* newValues) {
double newValue = 0.0;
ReturnValue_t result = newValues->getElement<double>(&newValue, 0, 0);
if (result != returnvalue::OK) {
return result;
}
params.setValue(key, newValue);
// Do this so the dumping and loading with the framework works as well
doubleDummy = newValue;
parameterWrapper->set(doubleDummy);
return params.writeJsonFile();
}
#ifdef XIPHOS_Q7S
ReturnValue_t PayloadPcduHandler::extConvAsTwoCallback(SpiComIF* comIf, SpiCookie* cookie,
const uint8_t* sendData, size_t sendLen,
void* args) {
auto handler = reinterpret_cast<PayloadPcduHandler*>(args);
if (handler == nullptr) {
sif::error << "GyroADIS16507Handler::spiSendCallback: Passed handler pointer is invalid!"
<< std::endl;
return returnvalue::FAILED;
}
DeviceCommandId_t currentCommand = handler->getPendingCommand();
switch (currentCommand) {
case (plpcdu::READ_WITH_TEMP_EXT): {
return transferAsTwo(comIf, cookie, sendData, sendLen, false);
}
case (plpcdu::READ_TEMP_EXT): {
return transferAsTwo(comIf, cookie, sendData, sendLen, true);
}
default: {
return comIf->performRegularSendOperation(cookie, sendData, sendLen);
}
}
return returnvalue::OK;
}
ReturnValue_t PayloadPcduHandler::transferAsTwo(SpiComIF* comIf, SpiCookie* cookie,
const uint8_t* sendData, size_t sendLen,
bool tempOnly) {
ReturnValue_t result = returnvalue::OK;
int retval = 0;
// Prepare transfer
int fileDescriptor = 0;
UnixFileGuard fileHelper(comIf->getSpiDev(), fileDescriptor, O_RDWR, "SpiComIF::sendMessage");
if (fileHelper.getOpenResult() != returnvalue::OK) {
return SpiComIF::OPENING_FILE_FAILED;
}
spi::SpiModes spiMode = spi::SpiModes::MODE_0;
uint32_t spiSpeed = 0;
cookie->getSpiParameters(spiMode, spiSpeed, nullptr);
comIf->setSpiSpeedAndMode(fileDescriptor, spiMode, spiSpeed);
cookie->assignWriteBuffer(sendData);
size_t transferLen = plpcdu::TEMP_REPLY_SIZE;
if (not tempOnly) {
transferLen += plpcdu::ADC_REPLY_SIZE;
}
cookie->setTransferSize(transferLen);
gpioId_t gpioId = cookie->getChipSelectPin();
GpioIF& gpioIF = comIf->getGpioInterface();
MutexIF::TimeoutType timeoutType = MutexIF::TimeoutType::WAITING;
uint32_t timeoutMs = 0;
MutexIF* mutex = comIf->getCsMutex();
if (mutex == nullptr) {
#if OBSW_VERBOSE_LEVEL >= 1
sif::warning << "GyroADIS16507Handler::spiSendCallback: "
"Mutex or GPIO interface invalid"
<< std::endl;
return returnvalue::FAILED;
#endif
}
if (gpioId != gpio::NO_GPIO) {
cookie->getMutexParams(timeoutType, timeoutMs);
result = mutex->lockMutex(timeoutType, timeoutMs);
if (result != returnvalue::OK) {
#if FSFW_CPP_OSTREAM_ENABLED == 1
sif::error << "SpiComIF::sendMessage: Failed to lock mutex" << std::endl;
#endif
return result;
}
}
spi_ioc_transfer* transferStruct = cookie->getTransferStructHandle();
uint64_t origTx = transferStruct->tx_buf;
uint64_t origRx = transferStruct->rx_buf;
if (tempOnly) {
transferLen = 1;
} else {
transferLen = plpcdu::ADC_REPLY_SIZE + 1;
}
transferStruct->len = transferLen;
// Pull SPI CS low. For now, no support for active high given
if (gpioId != gpio::NO_GPIO) {
gpioIF.pullLow(gpioId);
}
// Execute transfer
// Initiate a full duplex SPI transfer.
retval = ioctl(fileDescriptor, SPI_IOC_MESSAGE(1), cookie->getTransferStructHandle());
if (retval < 0) {
utility::handleIoctlError("SpiComIF::sendMessage: ioctl error.");
result = SpiComIF::FULL_DUPLEX_TRANSFER_FAILED;
}
#if FSFW_HAL_SPI_WIRETAPPING == 1
comIf->performSpiWiretapping(cookie);
#endif /* FSFW_LINUX_SPI_WIRETAPPING == 1 */
if (gpioId != gpio::NO_GPIO) {
gpioIF.pullHigh(gpioId);
}
transferStruct->tx_buf += transferLen;
transferStruct->rx_buf += transferLen;
transferStruct->len = plpcdu::TEMP_REPLY_SIZE - 1;
if (gpioId != gpio::NO_GPIO) {
gpioIF.pullLow(gpioId);
}
// Execute transfer
// Initiate a full duplex SPI transfer.
retval = ioctl(fileDescriptor, SPI_IOC_MESSAGE(1), cookie->getTransferStructHandle());
if (retval < 0) {
utility::handleIoctlError("SpiComIF::sendMessage: ioctl error.");
result = SpiComIF::FULL_DUPLEX_TRANSFER_FAILED;
}
#if FSFW_HAL_SPI_WIRETAPPING == 1
comIf->performSpiWiretapping(cookie);
#endif /* FSFW_LINUX_SPI_WIRETAPPING == 1 */
if (gpioId != gpio::NO_GPIO) {
gpioIF.pullHigh(gpioId);
}
transferStruct->tx_buf = origTx;
transferStruct->rx_buf = origRx;
if (gpioId != gpio::NO_GPIO) {
mutex->unlockMutex();
}
return returnvalue::OK;
}
#endif

View File

@ -0,0 +1,183 @@
#ifndef LINUX_DEVICES_PLPCDUHANDLER_H_
#define LINUX_DEVICES_PLPCDUHANDLER_H_
#include <fsfw/devicehandlers/DeviceHandlerBase.h>
#include <fsfw/globalfunctions/PeriodicOperationDivider.h>
#include <fsfw/timemanager/Countdown.h>
#include <mission/payload/payloadPcduDefinitions.h>
#include <mission/system/objects/Stack5VHandler.h>
#include "events/subsystemIdRanges.h"
#include "fsfw/FSFW.h"
#include "fsfw_hal/common/gpio/GpioIF.h"
#include "mission/memory/SdCardMountedIF.h"
#include "mission/power/defs.h"
#include "mission/system/DualLanePowerStateMachine.h"
#ifdef FSFW_OSAL_LINUX
class SpiComIF;
class SpiCookie;
#endif
/**
* @brief Device handler for the EIVE Payload PCDU
* @details
* Documentation:
* https://egit.irs.uni-stuttgart.de/eive/eive_dokumente/src/branch/master/400_Raumsegment/412_PayloaPCDUDocumentation/release/EIVE-D-421-001_PLPCDU_Documentation.pdf
*
* Important components:
* - SSR - Solid State Relay: Decouples voltages from battery
* - DRO - Dielectric Resonsant Oscillator: Generates modulation signal
* - X8: Frequency X8 Multiplicator
* - TX: Transmitter/Sender module. Modulates data onto carrier signal
* - MPA - Medium Power Amplifier
* - HPA - High Power Amplifier
*/
class PayloadPcduHandler : public DeviceHandlerBase {
public:
static constexpr uint8_t SUBSYSTEM_ID = SUBSYSTEM_ID::PL_PCDU_HANDLER;
//! [EXPORT] : [COMMENT] Could not transition properly and went back to ALL OFF
static constexpr Event TRANSITION_BACK_TO_OFF =
event::makeEvent(SUBSYSTEM_ID, 0, severity::MEDIUM);
//! [EXPORT] : [COMMENT] P1: 0 -> too low, 1 -> too high P2: Float value
static constexpr Event NEG_V_OUT_OF_BOUNDS = event::makeEvent(SUBSYSTEM_ID, 1, severity::MEDIUM);
//! [EXPORT] : [COMMENT] P1: 0 -> too low, 1 -> too high P2: Float value
static constexpr Event U_DRO_OUT_OF_BOUNDS = event::makeEvent(SUBSYSTEM_ID, 2, severity::MEDIUM);
//! [EXPORT] : [COMMENT] P1: 0 -> too low, 1 -> too high P2: Float value
static constexpr Event I_DRO_OUT_OF_BOUNDS = event::makeEvent(SUBSYSTEM_ID, 3, severity::MEDIUM);
//! [EXPORT] : [COMMENT] P1: 0 -> too low, 1 -> too high P2: Float value
static constexpr Event U_X8_OUT_OF_BOUNDS = event::makeEvent(SUBSYSTEM_ID, 4, severity::MEDIUM);
//! [EXPORT] : [COMMENT] P1: 0 -> too low, 1 -> too high P2: Float value
static constexpr Event I_X8_OUT_OF_BOUNDS = event::makeEvent(SUBSYSTEM_ID, 5, severity::MEDIUM);
//! [EXPORT] : [COMMENT] P1: 0 -> too low, 1 -> too high P2: Float value
static constexpr Event U_TX_OUT_OF_BOUNDS = event::makeEvent(SUBSYSTEM_ID, 6, severity::MEDIUM);
//! [EXPORT] : [COMMENT] P1: 0 -> too low, 1 -> too high P2: Float value
static constexpr Event I_TX_OUT_OF_BOUNDS = event::makeEvent(SUBSYSTEM_ID, 7, severity::MEDIUM);
//! [EXPORT] : [COMMENT] P1: 0 -> too low, 1 -> too high P2: Float value
static constexpr Event U_MPA_OUT_OF_BOUNDS = event::makeEvent(SUBSYSTEM_ID, 8, severity::MEDIUM);
//! [EXPORT] : [COMMENT] P1: 0 -> too low, 1 -> too high P2: Float value
static constexpr Event I_MPA_OUT_OF_BOUNDS = event::makeEvent(SUBSYSTEM_ID, 9, severity::MEDIUM);
//! [EXPORT] : [COMMENT] P1: 0 -> too low, 1 -> too high P2: Float value
static constexpr Event U_HPA_OUT_OF_BOUNDS = event::makeEvent(SUBSYSTEM_ID, 10, severity::MEDIUM);
//! [EXPORT] : [COMMENT] P1: 0 -> too low, 1 -> too high P2: Float value
static constexpr Event I_HPA_OUT_OF_BOUNDS = event::makeEvent(SUBSYSTEM_ID, 11, severity::MEDIUM);
PayloadPcduHandler(object_id_t objectId, object_id_t comIF, CookieIF* cookie, GpioIF* gpioIF,
SdCardMountedIF* sdcMan, Stack5VHandler& stackHandler, bool periodicPrintout);
void setToGoToNormalModeImmediately(bool enable);
void enablePeriodicPrintout(bool enable, uint8_t divider);
#ifdef XIPHOS_Q7S
static ReturnValue_t extConvAsTwoCallback(SpiComIF* comIf, SpiCookie* cookie,
const uint8_t* sendData, size_t sendLen, void* args);
static ReturnValue_t transferAsTwo(SpiComIF* comIf, SpiCookie* cookie, const uint8_t* sendData,
size_t sendLen, bool tempOnly);
#endif
private:
enum class States : uint8_t {
PL_PCDU_OFF,
STACK_5V_SWITCHING,
STACK_5V_PENDING,
STACK_5V_CORRECT,
// Solid State Relay, enable battery voltages VBAT0 and VBAT1. This will also switch on
// the ADC
ON_TRANS_SSR,
ON_TRANS_ADC_CLOSE_ZERO,
// Enable Dielectric Resonant Oscillator and start monitoring voltages as
// soon as DRO voltage reaches 6V
ON_TRANS_DRO,
// Switch on X8 compoennt and monitor voltages for 5 seconds
ON_TRANS_X8,
// Switch on TX component and monitor voltages for 5 seconds
ON_TRANS_TX,
// Switch on MPA component and monitor voltages for 5 seconds
ON_TRANS_MPA,
// Switch on HPA component and monitor voltages for 5 seconds
ON_TRANS_HPA,
// All components of the experiment are on
PL_PCDU_ON,
} state = States::PL_PCDU_OFF;
duallane::Submodes pwrSubmode = duallane::Submodes::A_SIDE;
enum class AdcMode { EXT_CONV, INT_CONV } adcMode = AdcMode::INT_CONV;
enum class MonitoringMode { NONE, CLOSE_TO_ZERO, NEGATIVE } monMode = MonitoringMode::NONE;
enum class AdcStates { OFF, BOOT_DELAY, SEND_SETUP, NORMAL } adcState = AdcStates::OFF;
bool goToNormalMode = false;
plpcdu::PlPcduAdcSet adcSet;
Stack5VHandler& stackHandler;
std::array<uint8_t, plpcdu::MAX_ADC_REPLY_SIZE> cmdBuf = {};
// This variable is tied to DRO +6 V voltage. Voltages, currents are monitored and the experiment
// is shut down immediately if there is a negative voltage.
bool transitionOk = false;
bool commandExecuted = false;
bool adcCmdExecuted = false;
bool periodicPrintout = false;
bool jsonFileInitComplete = false;
double doubleDummy = 0.0;
bool ssrToDroInjectionRequested = false;
bool droToX8InjectionRequested = false;
bool x8ToTxInjectionRequested = false;
bool txToMpaInjectionRequested = false;
bool mpaToHpaInjectionRequested = false;
bool allOnInjectRequested = false;
bool clearSetOnOffFlag = true;
PeriodicOperationDivider opDivider = PeriodicOperationDivider(5);
uint8_t tempReadDivisor = 1;
Countdown countdown = Countdown(5000);
Countdown adcCountdown = Countdown(50);
GpioIF* gpioIF;
SdCardMountedIF* sdcMan;
plpcdu::PlPcduParameter params;
bool quickTransitionAlreadyCalled = true;
uint8_t diffMask = 0;
PoolEntry<uint16_t> channelValues = PoolEntry<uint16_t>({0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0});
PoolEntry<float> processedValues =
PoolEntry<float>({0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0});
PoolEntry<float> tempC = PoolEntry<float>({0.0});
void updateSwitchGpio(gpioId_t id, gpio::Levels level);
void doTransition(Mode_t modeFrom, Submode_t subModeFrom) override;
void doStartUp() override;
void doShutDown() override;
// Main FDIR function which goes from any PL PCDU state back to all off
void quickTransitionBackToOff(bool startTransitionToOff, bool notifyFdir);
ReturnValue_t buildNormalDeviceCommand(DeviceCommandId_t* id) override;
ReturnValue_t buildTransitionDeviceCommand(DeviceCommandId_t* id) override;
void fillCommandAndReplyMap() override;
ReturnValue_t buildCommandFromCommand(DeviceCommandId_t deviceCommand, const uint8_t* commandData,
size_t commandDataLen) override;
ReturnValue_t scanForReply(const uint8_t* start, size_t remainingSize, DeviceCommandId_t* foundId,
size_t* foundLen) override;
ReturnValue_t interpretDeviceReply(DeviceCommandId_t id, const uint8_t* packet) override;
uint32_t getTransitionDelayMs(Mode_t modeFrom, Mode_t modeTo) override;
ReturnValue_t initializeLocalDataPool(localpool::DataPool& localDataPoolMap,
LocalDataPoolManager& poolManager) override;
ReturnValue_t isModeCombinationValid(Mode_t mode, Submode_t submode) override;
ReturnValue_t getParameter(uint8_t domainId, uint8_t uniqueId, ParameterWrapper* parameterWrapper,
const ParameterWrapper* newValues, uint16_t startAtIndex) override;
void handleExtConvRead(const uint8_t* bufStart);
void handlePrintout();
void checkAdcValues();
void handleOutOfBoundsPrintout();
void checkJsonFileInit();
ReturnValue_t stateMachineToNormal(Mode_t modeFrom, Submode_t subModeFrom);
bool checkVoltage(float val, float lowerBound, float upperBound, Event event);
bool checkCurrent(float val, float upperBound, Event event);
void handleFailureInjection(std::string output, Event event);
ReturnValue_t serializeFloat(uint32_t& param, float val);
ReturnValue_t handleDoubleParamUpdate(std::string key, ParameterWrapper* parameterWrapper,
const ParameterWrapper* newValues);
};
#endif /* LINUX_DEVICES_PLPCDUHANDLER_H_ */

View File

@ -0,0 +1,239 @@
#include <OBSWConfig.h>
#include <devices/gpioIds.h>
#include <fsfw/datapool/PoolReadGuard.h>
#include <mission/payload/RadiationSensorHandler.h>
#include <mission/power/gsDefs.h>
#include <mission/tcs/max1227.h>
RadiationSensorHandler::RadiationSensorHandler(object_id_t objectId, object_id_t comIF,
CookieIF *comCookie, GpioIF *gpioIF,
Stack5VHandler &stackHandler)
: DeviceHandlerBase(objectId, comIF, comCookie),
dataset(this),
gpioIF(gpioIF),
stackHandler(stackHandler) {
if (comCookie == nullptr) {
sif::error << "RadiationSensorHandler: Invalid com cookie" << std::endl;
}
}
RadiationSensorHandler::~RadiationSensorHandler() {}
void RadiationSensorHandler::doStartUp() {
if (internalState == InternalState::OFF) {
ReturnValue_t retval = stackHandler.deviceToOn(StackCommander::RAD_SENSOR, true);
if (retval == BUSY) {
return;
}
internalState = InternalState::POWER_SWITCHING;
}
if (internalState == InternalState::POWER_SWITCHING) {
if (stackHandler.isSwitchOn()) {
internalState = InternalState::SETUP;
}
}
if (internalState == InternalState::CONFIGURED) {
if (goToNormalMode) {
setMode(MODE_NORMAL);
} else {
setMode(_MODE_TO_ON);
}
}
}
void RadiationSensorHandler::doShutDown() {
ReturnValue_t retval = stackHandler.deviceToOff(StackCommander::RAD_SENSOR, true);
if (retval == BUSY) {
return;
}
internalState = InternalState::OFF;
setMode(_MODE_POWER_DOWN);
}
ReturnValue_t RadiationSensorHandler::buildNormalDeviceCommand(DeviceCommandId_t *id) {
switch (communicationStep) {
case CommunicationStep::START_CONVERSION: {
*id = RAD_SENSOR::START_CONVERSION;
communicationStep = CommunicationStep::READ_CONVERSIONS;
break;
}
case CommunicationStep::READ_CONVERSIONS: {
*id = RAD_SENSOR::READ_CONVERSIONS;
communicationStep = CommunicationStep::START_CONVERSION;
break;
}
default: {
sif::debug << "RadiationSensorHandler::buildNormalDeviceCommand: Unknown communication "
<< "step" << std::endl;
return returnvalue::OK;
}
}
return buildCommandFromCommand(*id, nullptr, 0);
}
ReturnValue_t RadiationSensorHandler::buildTransitionDeviceCommand(DeviceCommandId_t *id) {
if (internalState == InternalState::SETUP) {
*id = RAD_SENSOR::WRITE_SETUP;
} else {
return NOTHING_TO_SEND;
}
return buildCommandFromCommand(*id, nullptr, 0);
}
ReturnValue_t RadiationSensorHandler::buildCommandFromCommand(DeviceCommandId_t deviceCommand,
const uint8_t *commandData,
size_t commandDataLen) {
switch (deviceCommand) {
case (RAD_SENSOR::WRITE_SETUP): {
cmdBuffer[0] = RAD_SENSOR::SETUP_DEFINITION;
rawPacket = cmdBuffer;
rawPacketLen = 1;
internalState = InternalState::CONFIGURED;
return returnvalue::OK;
}
case (RAD_SENSOR::START_CONVERSION): {
ReturnValue_t result = gpioIF->pullHigh(gpioIds::ENABLE_RADFET);
if (result != returnvalue::OK) {
#if OBSW_VERBOSE_LEVEL >= 1
sif::warning
<< "RadiationSensorHandler::buildCommandFromCommand: Pulling RADFET Enable pin "
"high failed"
<< std::endl;
#endif
}
/* First the fifo will be reset here */
cmdBuffer[0] = RAD_SENSOR::RESET_DEFINITION;
cmdBuffer[1] = RAD_SENSOR::CONVERSION_DEFINITION;
rawPacket = cmdBuffer;
rawPacketLen = 2;
return returnvalue::OK;
}
case (RAD_SENSOR::READ_CONVERSIONS): {
cmdBuffer[0] = RAD_SENSOR::DUMMY_BYTE;
std::memset(cmdBuffer, RAD_SENSOR::DUMMY_BYTE, RAD_SENSOR::READ_SIZE);
rawPacket = cmdBuffer;
rawPacketLen = RAD_SENSOR::READ_SIZE;
return returnvalue::OK;
}
case RAD_SENSOR::ENABLE_DEBUG_OUTPUT: {
printPeriodicData = true;
rawPacketLen = 0;
return returnvalue::OK;
}
case RAD_SENSOR::DISABLE_DEBUG_OUTPUT: {
rawPacketLen = 0;
printPeriodicData = false;
return returnvalue::OK;
}
default:
return DeviceHandlerIF::COMMAND_NOT_IMPLEMENTED;
}
return returnvalue::FAILED;
}
void RadiationSensorHandler::fillCommandAndReplyMap() {
this->insertInCommandMap(RAD_SENSOR::WRITE_SETUP);
this->insertInCommandMap(RAD_SENSOR::START_CONVERSION);
this->insertInCommandMap(RAD_SENSOR::ENABLE_DEBUG_OUTPUT);
this->insertInCommandMap(RAD_SENSOR::DISABLE_DEBUG_OUTPUT);
this->insertInCommandAndReplyMap(RAD_SENSOR::READ_CONVERSIONS, 1, &dataset,
RAD_SENSOR::READ_SIZE);
}
ReturnValue_t RadiationSensorHandler::scanForReply(const uint8_t *start, size_t remainingSize,
DeviceCommandId_t *foundId, size_t *foundLen) {
*foundId = this->getPendingCommand();
switch (*foundId) {
case RAD_SENSOR::START_CONVERSION:
case RAD_SENSOR::WRITE_SETUP:
*foundLen = remainingSize;
return IGNORE_REPLY_DATA;
case RAD_SENSOR::READ_CONVERSIONS: {
ReturnValue_t result = gpioIF->pullLow(gpioIds::ENABLE_RADFET);
if (result != returnvalue::OK) {
#if OBSW_VERBOSE_LEVEL >= 1
sif::warning << "RadiationSensorHandler::scanForReply; Pulling RADFET Enale pin "
"low failed"
<< std::endl;
#endif
}
break;
}
case RAD_SENSOR::ENABLE_DEBUG_OUTPUT:
case RAD_SENSOR::DISABLE_DEBUG_OUTPUT:
sif::info << "RadiationSensorHandler::scanForReply: " << remainingSize << std::endl;
break;
default:
break;
}
*foundLen = remainingSize;
return returnvalue::OK;
}
ReturnValue_t RadiationSensorHandler::interpretDeviceReply(DeviceCommandId_t id,
const uint8_t *packet) {
switch (id) {
case RAD_SENSOR::READ_CONVERSIONS: {
uint8_t offset = 0;
PoolReadGuard readSet(&dataset);
uint16_t tempRaw = ((packet[offset] & 0x0f) << 8) | packet[offset + 1];
dataset.temperatureCelcius = max1227::getTemperature(tempRaw);
offset += 2;
dataset.ain0 = (*(packet + offset) << 8) | *(packet + offset + 1);
offset += 2;
dataset.ain1 = (*(packet + offset) << 8) | *(packet + offset + 1);
offset += 6;
dataset.ain4 = (*(packet + offset) << 8) | *(packet + offset + 1);
offset += 2;
dataset.ain5 = (*(packet + offset) << 8) | *(packet + offset + 1);
offset += 2;
dataset.ain6 = (*(packet + offset) << 8) | *(packet + offset + 1);
offset += 2;
dataset.ain7 = (*(packet + offset) << 8) | *(packet + offset + 1);
dataset.setValidity(true, true);
if (printPeriodicData) {
sif::info << "Radiation sensor temperature: " << dataset.temperatureCelcius << " °C"
<< std::dec << std::endl;
sif::info << "Radiation sensor ADC value channel 0: " << dataset.ain0 << std::endl;
sif::info << "Radiation sensor ADC value channel 1: " << dataset.ain1 << std::endl;
sif::info << "Radiation sensor ADC value channel 4: " << dataset.ain4 << std::endl;
sif::info << "Radiation sensor ADC value channel 5: " << dataset.ain5 << std::endl;
sif::info << "Radiation sensor ADC value channel 6: " << dataset.ain6 << std::endl;
sif::info << "Radiation sensor ADC value channel 7: " << dataset.ain7 << std::endl;
}
break;
}
default: {
sif::debug << "RadiationSensorHandler::interpretDeviceReply: Unknown reply id" << std::endl;
return DeviceHandlerIF::UNKNOWN_DEVICE_REPLY;
}
}
return returnvalue::OK;
}
uint32_t RadiationSensorHandler::getTransitionDelayMs(Mode_t modeFrom, Mode_t modeTo) {
return 5000;
}
ReturnValue_t RadiationSensorHandler::initializeLocalDataPool(localpool::DataPool &localDataPoolMap,
LocalDataPoolManager &poolManager) {
localDataPoolMap.emplace(RAD_SENSOR::TEMPERATURE_C, new PoolEntry<float>({0.0}));
localDataPoolMap.emplace(RAD_SENSOR::AIN0, new PoolEntry<uint16_t>({0}));
localDataPoolMap.emplace(RAD_SENSOR::AIN1, new PoolEntry<uint16_t>({0}));
localDataPoolMap.emplace(RAD_SENSOR::AIN4, new PoolEntry<uint16_t>({0}));
localDataPoolMap.emplace(RAD_SENSOR::AIN5, new PoolEntry<uint16_t>({0}));
localDataPoolMap.emplace(RAD_SENSOR::AIN6, new PoolEntry<uint16_t>({0}));
localDataPoolMap.emplace(RAD_SENSOR::AIN7, new PoolEntry<uint16_t>({0}));
poolManager.subscribeForRegularPeriodicPacket(
subdp::RegularHkPeriodicParams(dataset.getSid(), false, 20.0));
return returnvalue::OK;
}
void RadiationSensorHandler::setToGoToNormalModeImmediately() { this->goToNormalMode = true; }
void RadiationSensorHandler::enablePeriodicDataPrint(bool enable) {
this->printPeriodicData = enable;
}

View File

@ -0,0 +1,57 @@
#ifndef MISSION_PAYLOAD_RADIATIONSENSORHANDLER_H_
#define MISSION_PAYLOAD_RADIATIONSENSORHANDLER_H_
#include <fsfw/devicehandlers/DeviceHandlerBase.h>
#include <fsfw_hal/common/gpio/GpioIF.h>
#include <mission/payload/radSensorDefinitions.h>
#include <mission/system/objects/Stack5VHandler.h>
/**
* @brief This is the device handler class for radiation sensor on the OBC IF Board. The
* sensor is based on the MAX1227 ADC converter.
*
* @details Datasheet of MAX1227: https://datasheets.maximintegrated.com/en/ds/MAX1227-MAX1231.pdf
*
* @author J. Meier
*/
class RadiationSensorHandler : public DeviceHandlerBase {
public:
RadiationSensorHandler(object_id_t objectId, object_id_t comIF, CookieIF *comCookie,
GpioIF *gpioIF, Stack5VHandler &handler);
virtual ~RadiationSensorHandler();
void setToGoToNormalModeImmediately();
void enablePeriodicDataPrint(bool enable);
protected:
void doStartUp() override;
void doShutDown() override;
ReturnValue_t buildNormalDeviceCommand(DeviceCommandId_t *id) override;
ReturnValue_t buildTransitionDeviceCommand(DeviceCommandId_t *id) override;
void fillCommandAndReplyMap() override;
ReturnValue_t buildCommandFromCommand(DeviceCommandId_t deviceCommand, const uint8_t *commandData,
size_t commandDataLen) override;
ReturnValue_t scanForReply(const uint8_t *start, size_t remainingSize, DeviceCommandId_t *foundId,
size_t *foundLen) override;
ReturnValue_t interpretDeviceReply(DeviceCommandId_t id, const uint8_t *packet) override;
uint32_t getTransitionDelayMs(Mode_t modeFrom, Mode_t modeTo) override;
ReturnValue_t initializeLocalDataPool(localpool::DataPool &localDataPoolMap,
LocalDataPoolManager &poolManager) override;
private:
enum class CommunicationStep { START_CONVERSION, READ_CONVERSIONS };
enum class InternalState { OFF, POWER_SWITCHING, SETUP, CONFIGURED };
bool printPeriodicData = false;
RAD_SENSOR::RadSensorDataset dataset;
static const uint8_t MAX_CMD_LEN = RAD_SENSOR::READ_SIZE;
GpioIF *gpioIF = nullptr;
Stack5VHandler &stackHandler;
bool goToNormalMode = false;
uint8_t cmdBuffer[MAX_CMD_LEN];
InternalState internalState = InternalState::OFF;
CommunicationStep communicationStep = CommunicationStep::START_CONVERSION;
};
#endif /* MISSION_PAYLOAD_RADIATIONSENSORHANDLER_H_ */

View File

@ -0,0 +1,395 @@
#include <fsfw/filesystem/HasFileSystemIF.h>
#include <linux/payload/ScexHelper.h>
#include <mission/memory/SdCardMountedIF.h>
#include <mission/payload/ScexDeviceHandler.h>
#include <mission/payload/scexHelpers.h>
#include <algorithm>
#include <ctime>
#include <filesystem>
#include <fstream>
#include <iostream>
#include <random>
#include "fsfw/globalfunctions/CRC.h"
using std::ofstream;
using namespace returnvalue;
ScexDeviceHandler::ScexDeviceHandler(object_id_t objectId, ScexUartReader& reader, CookieIF* cookie,
SdCardMountedIF& sdcMan)
: DeviceHandlerBase(objectId, reader.getObjectId(), cookie), sdcMan(sdcMan), reader(reader) {}
ScexDeviceHandler::~ScexDeviceHandler() {}
void ScexDeviceHandler::doStartUp() { setMode(MODE_ON); }
void ScexDeviceHandler::doShutDown() {
reader.reset();
commandActive = false;
multiFileFinishOutstanding = false;
setMode(_MODE_POWER_DOWN);
}
ReturnValue_t ScexDeviceHandler::buildNormalDeviceCommand(DeviceCommandId_t* id) { return OK; }
ReturnValue_t ScexDeviceHandler::buildTransitionDeviceCommand(DeviceCommandId_t* id) { return OK; }
ReturnValue_t ScexDeviceHandler::buildCommandFromCommand(DeviceCommandId_t deviceCommand,
const uint8_t* commandData,
size_t commandDataLen) {
using namespace scex;
auto cmdTyped = static_cast<scex::Cmds>(deviceCommand);
if (std::find(VALID_CMDS.begin(), VALID_CMDS.end(), deviceCommand) == VALID_CMDS.end()) {
return DeviceHandlerIF::COMMAND_NOT_IMPLEMENTED;
}
bool tempCheck = false;
if (commandDataLen == 1) {
tempCheck = commandData[0];
}
if (commandActive) {
return DeviceHandlerIF::BUSY;
}
switch (deviceCommand) {
case (PING): {
finishCountdown.setTimeout(SHORT_CD);
// countdown starten
finishCountdown.resetTimer();
prepareScexCmd(cmdTyped, {cmdBuf.data(), cmdBuf.size()}, rawPacketLen, {nullptr, 0},
tempCheck);
break;
}
case (EXP_STATUS_CMD): {
finishCountdown.setTimeout(SHORT_CD);
// countdown starten
finishCountdown.resetTimer();
prepareScexCmd(cmdTyped, {cmdBuf.data(), cmdBuf.size()}, rawPacketLen, {nullptr, 0},
tempCheck);
break;
}
case (ION_CMD): {
finishCountdown.setTimeout(SHORT_CD);
// countdown starten
finishCountdown.resetTimer();
prepareScexCmd(cmdTyped, {cmdBuf.data(), cmdBuf.size()}, rawPacketLen, {nullptr, 0},
tempCheck);
break;
}
case (TEMP_CMD): {
finishCountdown.setTimeout(SHORT_CD);
// countdown starten
finishCountdown.resetTimer();
prepareScexCmd(cmdTyped, {cmdBuf.data(), cmdBuf.size()}, rawPacketLen, {nullptr, 0},
tempCheck);
break;
}
case (FRAM): {
finishCountdown.setTimeout(LONG_CD);
// countdown starten
finishCountdown.resetTimer();
if (debugMode) {
uint32_t remainingMillis = finishCountdown.getRemainingMillis();
sif::info << "ScexDeviceHandler::buildCommandFromCommand: RemainingMillis: "
<< remainingMillis << std::endl;
}
multiFileFinishOutstanding = true;
prepareScexCmd(cmdTyped, {cmdBuf.data(), cmdBuf.size()}, rawPacketLen,
{commandData + 1, commandDataLen - 1}, tempCheck);
updatePeriodicReply(true, deviceCommand);
break;
}
case (ONE_CELL): {
finishCountdown.setTimeout(LONG_CD);
// countdown starts
finishCountdown.resetTimer();
multiFileFinishOutstanding = true;
prepareScexCmd(cmdTyped, {cmdBuf.data(), cmdBuf.size()}, rawPacketLen,
{commandData + 1, commandDataLen - 1}, tempCheck);
updatePeriodicReply(true, deviceCommand);
break;
}
case (ALL_CELLS_CMD): {
finishCountdown.setTimeout(LONG_CD);
// countdown starts
finishCountdown.resetTimer();
multiFileFinishOutstanding = true;
prepareScexCmd(cmdTyped, {cmdBuf.data(), cmdBuf.size()}, rawPacketLen,
{commandData + 1, commandDataLen - 1}, tempCheck);
updatePeriodicReply(true, deviceCommand);
break;
}
default: {
return DeviceHandlerIF::COMMAND_NOT_IMPLEMENTED;
}
}
commandActive = true;
rawPacket = cmdBuf.data();
return OK;
}
void ScexDeviceHandler::fillCommandAndReplyMap() {
insertInCommandAndReplyMap(scex::Cmds::PING, 5, nullptr, 0, false, false, 0, &finishCountdown);
insertInCommandAndReplyMap(scex::Cmds::ION_CMD, 3, nullptr, 0, false, false, 0, &finishCountdown);
insertInCommandAndReplyMap(scex::Cmds::TEMP_CMD, 3, nullptr, 0, false, false, 0,
&finishCountdown);
insertInCommandAndReplyMap(scex::Cmds::EXP_STATUS_CMD, 3, nullptr, 0, false, false, 0,
&finishCountdown);
insertInCommandAndReplyMap(scex::Cmds::ALL_CELLS_CMD, 0, nullptr, 0, true, false,
scex::Cmds::ALL_CELLS_CMD, &finishCountdown);
insertInCommandAndReplyMap(scex::Cmds::ONE_CELL, 0, nullptr, 0, true, false, scex::Cmds::ONE_CELL,
&finishCountdown);
insertInCommandAndReplyMap(scex::Cmds::FRAM, 0, nullptr, 0, true, false, scex::Cmds::FRAM,
&finishCountdown);
insertInReplyMap(scex::Cmds::ERROR_REPLY, 3);
}
ReturnValue_t ScexDeviceHandler::scanForReply(const uint8_t* start, size_t remainingSize,
DeviceCommandId_t* foundId, size_t* foundLen) {
size_t len = remainingSize;
ReturnValue_t result = helper.deSerialize(&start, &len);
if (result == ScexHelper::INVALID_CRC) {
sif::warning << "ScexDeviceHandler::scanForReply: CRC invalid" << std::endl;
*foundLen = remainingSize;
} else {
result = handleValidReply(remainingSize, foundId, foundLen);
}
return result;
}
ReturnValue_t ScexDeviceHandler::handleValidReply(size_t remSize, DeviceCommandId_t* foundId,
size_t* foundLen) {
using namespace scex;
ReturnValue_t result = OK;
switch (helper.getCmd()) {
case (FRAM): {
if (debugMode) {
uint32_t remainingMillis = finishCountdown.getRemainingMillis();
sif::info << "ScexDeviceHandler::handleValidReply: RemMillis: " << remainingMillis
<< std::endl;
}
result = APERIODIC_REPLY;
break;
}
case (ONE_CELL): {
result = APERIODIC_REPLY;
break;
}
case (ALL_CELLS_CMD): {
result = APERIODIC_REPLY;
break;
}
default: {
break;
}
}
if (result == APERIODIC_REPLY and multiFileFinishOutstanding) {
finishAction(true, helper.getCmd(), OK);
multiFileFinishOutstanding = false;
}
*foundId = helper.getCmd();
*foundLen = remSize;
return result;
}
ReturnValue_t ScexDeviceHandler::interpretDeviceReply(DeviceCommandId_t id, const uint8_t* packet) {
using namespace scex;
ReturnValue_t status = OK;
auto oneFileHandler = [&](std::string cmdName) {
auto activeSd = sdcMan.getActiveSdCard();
if (not activeSd) {
return HasFileSystemIF::FILESYSTEM_INACTIVE;
}
fileId = date_time_string();
std::ostringstream oss;
auto prefix = sdcMan.getCurrentMountPrefix();
if (prefix == nullptr) {
return returnvalue::FAILED;
}
oss << prefix << "/scex/scex-" << cmdName << fileId << ".bin";
fileName = oss.str();
ofstream out(fileName, ofstream::binary);
if (out.bad()) {
sif::error << "ScexDeviceHandler::interpretDeviceReply: Could not open file " << fileName
<< std::endl;
return FAILED;
}
out << helper;
return OK;
};
auto multiFileHandler = [&](std::string cmdName) {
if ((helper.getPacketCounter() == 1) or (not fileNameSet)) {
auto activeSd = sdcMan.getActiveSdCard();
if (not activeSd) {
return HasFileSystemIF::FILESYSTEM_INACTIVE;
}
fileId = date_time_string();
std::ostringstream oss;
auto prefix = sdcMan.getCurrentMountPrefix();
if (prefix == nullptr) {
return returnvalue::FAILED;
}
oss << prefix << "/scex/scex-" << cmdName << fileId << ".bin";
fileName = oss.str();
fileNameSet = true;
ofstream out(fileName, ofstream::binary);
if (out.bad()) {
sif::error << "ScexDeviceHandler::handleValidReply: Could not open file " << fileName
<< std::endl;
return FAILED;
}
out << helper;
} else {
ofstream out(fileName,
ofstream::binary | ofstream::app); // append
if (out.bad()) {
sif::error << "ScexDeviceHandler::handleValidReply: Could not open file " << fileName
<< std::endl;
return FAILED;
}
out << helper;
}
return OK;
};
id = helper.getCmd();
switch (id) {
case (PING): {
status = oneFileHandler("ping_");
break;
}
case (ION_CMD): {
status = oneFileHandler("ion_");
break;
}
case (TEMP_CMD): {
status = oneFileHandler("temp_");
break;
}
case (EXP_STATUS_CMD): {
status = oneFileHandler("exp_status_");
break;
}
case (FRAM): {
status = multiFileHandler("fram_");
break;
}
case (ONE_CELL): {
status = multiFileHandler("one_cell_");
break;
}
case (ALL_CELLS_CMD): {
status = multiFileHandler("multi_cell_");
break;
}
default:
// Unknown DeviceCommand
return DeviceHandlerIF::COMMAND_NOT_IMPLEMENTED;
}
if (helper.getPacketCounter() == helper.getTotalPacketCounter()) {
reader.finish();
commandActive = false;
if (id != PING) {
fileNameSet = false;
}
if (id == FRAM or id == ALL_CELLS_CMD or id == ONE_CELL) {
triggerEvent(MULTI_PACKET_COMMAND_DONE, id);
updatePeriodicReply(false, id);
}
}
if (debugMode) {
uint32_t remainingMillis = finishCountdown.getRemainingMillis();
sif::info << __FILE__ << __func__ << "(" << __LINE__ << ") RemMillis: " << remainingMillis
<< std::endl;
}
return status;
}
void ScexDeviceHandler::performOperationHook() {
auto mntPrefix = sdcMan.getCurrentMountPrefix();
if (mntPrefix != nullptr) {
std::filesystem::path fullFilePath = mntPrefix;
std::error_code e;
fullFilePath /= "scex";
bool fileExists = std::filesystem::exists(fullFilePath, e);
if (not fileExists) {
bool created = std::filesystem::create_directory(fullFilePath, e);
if (not created) {
sif::error << "Could not create SCEX directory: " << e << std::endl;
}
}
}
uint32_t remainingMillis = finishCountdown.getRemainingMillis();
if (commandActive and finishCountdown.hasTimedOut()) {
triggerEvent(scex::EXPERIMENT_TIMEDOUT, currCmd, 0);
reader.finish();
sif::warning << "ScexDeviceHandler::scanForReply: Reader timeout; RemMillis: "
<< remainingMillis << std::endl;
fileNameSet = false;
commandActive = false;
}
}
uint32_t ScexDeviceHandler::getTransitionDelayMs(Mode_t modeFrom, Mode_t modeTo) { return OK; }
ReturnValue_t ScexDeviceHandler::getSwitches(const uint8_t** switches, uint8_t* numberOfSwitches) {
if (switchId) {
*numberOfSwitches = 1;
*switches = &switchId.value();
}
return OK;
}
ReturnValue_t ScexDeviceHandler::initializeLocalDataPool(localpool::DataPool& localDataPoolMap,
LocalDataPoolManager& poolManager) {
return OK;
}
std::string ScexDeviceHandler::date_time_string() {
using namespace std;
string date_time;
Clock::TimeOfDay_t tod;
Clock::getDateAndTime(&tod);
ostringstream oss(std::ostringstream::ate);
if (tod.hour < 10) {
oss << tod.year << tod.month << tod.day << "_0" << tod.hour;
} else {
oss << tod.year << tod.month << tod.day << "_" << tod.hour;
}
if (tod.minute < 10) {
oss << 0 << tod.minute;
} else {
oss << tod.minute;
}
if (tod.second < 10) {
oss << 0 << tod.second;
} else {
oss << tod.second;
}
date_time = oss.str();
return date_time;
}
void ScexDeviceHandler::modeChanged() {}
void ScexDeviceHandler::setPowerSwitcher(PowerSwitchIF& powerSwitcher, power::Switch_t switchId) {
DeviceHandlerBase::setPowerSwitcher(&powerSwitcher);
this->switchId = switchId;
}
ReturnValue_t ScexDeviceHandler::initializeAfterTaskCreation() {
return DeviceHandlerBase::initializeAfterTaskCreation();
}

View File

@ -0,0 +1,65 @@
#ifndef MISSION_PAYLOAD_SCEXDEVICEHANDLER_H_
#define MISSION_PAYLOAD_SCEXDEVICEHANDLER_H_
#include <fsfw/devicehandlers/DeviceHandlerBase.h>
#include <linux/payload/ScexHelper.h>
#include <linux/payload/ScexUartReader.h>
#include <optional>
#include "eive/eventSubsystemIds.h"
class SdCardMountedIF;
class ScexDeviceHandler : public DeviceHandlerBase {
public:
ScexDeviceHandler(object_id_t objectId, ScexUartReader &reader, CookieIF *cookie,
SdCardMountedIF &sdcMan);
void setPowerSwitcher(PowerSwitchIF &powerSwitcher, power::Switch_t switchId);
virtual ~ScexDeviceHandler();
private:
static constexpr uint32_t LONG_CD = 180 * 1000;
static constexpr uint32_t SHORT_CD = 12000;
std::array<uint8_t, 64> cmdBuf = {};
std::optional<power::Switch_t> switchId;
std::string fileId = "";
std::string fileName = "";
bool fileNameSet = false;
bool commandActive = false;
bool multiFileFinishOutstanding = false;
bool debugMode = false;
scex::Cmds currCmd = scex::Cmds::PING;
SdCardMountedIF &sdcMan;
Countdown finishCountdown = Countdown(LONG_CD);
std::string date_time_string();
// DeviceHandlerBase private function implementation
void doStartUp() override;
void doShutDown() override;
ScexHelper helper;
ScexUartReader &reader;
void performOperationHook() override;
ReturnValue_t buildNormalDeviceCommand(DeviceCommandId_t *id) override;
ReturnValue_t buildTransitionDeviceCommand(DeviceCommandId_t *id) override;
ReturnValue_t buildCommandFromCommand(DeviceCommandId_t deviceCommand, const uint8_t *commandData,
size_t commandDataLen) override;
void fillCommandAndReplyMap() override;
ReturnValue_t scanForReply(const uint8_t *start, size_t remainingSize, DeviceCommandId_t *foundId,
size_t *foundLen) override;
ReturnValue_t interpretDeviceReply(DeviceCommandId_t id, const uint8_t *packet) override;
ReturnValue_t handleValidReply(size_t remSize, DeviceCommandId_t *foundId, size_t *foundLen);
uint32_t getTransitionDelayMs(Mode_t modeFrom, Mode_t modeTo) override;
ReturnValue_t getSwitches(const uint8_t **switches, uint8_t *numberOfSwitches) override;
ReturnValue_t initializeLocalDataPool(localpool::DataPool &localDataPoolMap,
LocalDataPoolManager &poolManager) override;
ReturnValue_t initializeAfterTaskCreation() override;
void modeChanged() override;
};
#endif /* MISSION_PAYLOAD_SCEXDEVICEHANDLER_H_ */

32
mission/payload/defs.cpp Normal file
View File

@ -0,0 +1,32 @@
#include <mission/payload/defs.h>
const char* payload::getModeStr(Mode mode) {
static const char* modeStr = "UNKNOWN";
switch (mode) {
case (Mode::CAM_STREAM): {
modeStr = "CAM STREAM";
break;
}
case (Mode::EARTH_OBSV): {
modeStr = "EARTH OBSV";
break;
}
case (Mode::MPSOC_STREAM): {
modeStr = "MPSOC STREAM";
break;
}
case (Mode::OFF): {
modeStr = "OFF";
break;
}
case (Mode::SUPV_ONLY): {
modeStr = "SUPV ONLY";
break;
}
case (Mode::SCEX): {
modeStr = "SCEX";
break;
}
}
return modeStr;
}

24
mission/payload/defs.h Normal file
View File

@ -0,0 +1,24 @@
#pragma once
#include <cstdint>
namespace payload {
enum Mode {
OFF = 0,
SUPV_ONLY = 10,
MPSOC_STREAM = 11,
CAM_STREAM = 12,
EARTH_OBSV = 13,
SCEX = 14
};
extern const char* getModeStr(Mode mode);
namespace ploc {
enum Modes { OFF = 0, SUPV_ONLY = 1, MPSOC_ON = 2 };
}
} // namespace payload

View File

@ -0,0 +1,242 @@
#ifndef LINUX_DEVICES_DEVICEDEFINITIONS_PAYLOADPCDUDEFINITIONS_H_
#define LINUX_DEVICES_DEVICEDEFINITIONS_PAYLOADPCDUDEFINITIONS_H_
#include <fsfw/datapoollocal/StaticLocalDataSet.h>
#include <fsfw/devicehandlers/DeviceHandlerIF.h>
#include <mission/memory/NvmParameterBase.h>
#include <mission/tcs/max1227.h>
#include <cstddef>
#include <filesystem>
#include <nlohmann/json.hpp>
#include "OBSWConfig.h"
namespace plpcdu {
using namespace max1227;
enum PlPcduAdcChannels : uint8_t {
U_BAT_DIV_6 = 0,
// According to schematic, will be 2.2158V for Vneg = +0V and 0.2446V for Vneg = -6V
// Full Forumula: V_neg = V_post - (R1 + R2) / R1 * (V_pos - V_out) with R1 being 27.4k
// and R2 being 49.9k. FB = Feedback
U_NEG_V_FB = 1,
I_HPA = 2,
U_HPA_DIV_6 = 3,
I_MPA = 4,
U_MPA_DIV_6 = 5,
I_TX = 6,
U_TX_DIV_6 = 7,
I_X8 = 8,
U_X8_DIV_6 = 9,
I_DRO = 10,
U_DRO_DIV_6 = 11,
NUM_CHANNELS = 12
};
enum PlPcduParamIds : uint8_t {
NEG_V_LOWER_BOUND = 0,
NEG_V_UPPER_BOUND = 1,
DRO_U_LOWER_BOUND = 2,
DRO_U_UPPER_BOUND = 3,
DRO_I_UPPER_BOUND = 4,
X8_U_LOWER_BOUND = 5,
X8_U_UPPER_BOUND = 6,
X8_I_UPPER_BOUND = 7,
TX_U_LOWER_BOUND = 8,
TX_U_UPPER_BOUND = 9,
TX_I_UPPER_BOUND = 10,
MPA_U_LOWER_BOUND = 11,
MPA_U_UPPER_BOUND = 12,
MPA_I_UPPER_BOUND = 13,
HPA_U_LOWER_BOUND = 14,
HPA_U_UPPER_BOUND = 15,
HPA_I_UPPER_BOUND = 16,
SSR_TO_DRO_WAIT_TIME = 17,
DRO_TO_X8_WAIT_TIME = 18,
X8_TO_TX_WAIT_TIME = 19,
TX_TO_MPA_WAIT_TIME = 20,
MPA_TO_HPA_WAIT_TIME = 21,
INJECT_SSR_TO_DRO_FAILURE = 30,
INJECT_DRO_TO_X8_FAILURE = 31,
INJECT_X8_TO_TX_FAILURE = 32,
INJECT_TX_TO_MPA_FAILURE = 33,
INJECT_MPA_TO_HPA_FAILURE = 34,
INJECT_ALL_ON_FAILURE = 35
};
static std::map<PlPcduParamIds, std::string> PARAM_KEY_MAP = {
{NEG_V_LOWER_BOUND, "negVoltLowerBound"}, {NEG_V_UPPER_BOUND, "negVoltUpperBound"},
{DRO_U_LOWER_BOUND, "droVoltLowerBound"}, {DRO_U_UPPER_BOUND, "droVoltUpperBound"},
{DRO_I_UPPER_BOUND, "droCurrUpperBound"}, {X8_U_LOWER_BOUND, "x8VoltLowerBound"},
{X8_U_UPPER_BOUND, "x8VoltUpperBound"}, {X8_I_UPPER_BOUND, "x8CurrUpperBound"},
{TX_U_LOWER_BOUND, "txVoltLowerBound"}, {TX_U_UPPER_BOUND, "txVoltUpperBound"},
{TX_I_UPPER_BOUND, "txCurrUpperBound"}, {MPA_U_LOWER_BOUND, "mpaVoltLowerBound"},
{MPA_U_UPPER_BOUND, "mpaVoltUpperBound"}, {MPA_I_UPPER_BOUND, "mpaCurrUpperBound"},
{HPA_U_LOWER_BOUND, "hpaVoltLowerBound"}, {HPA_U_UPPER_BOUND, "hpaVoltUpperBound"},
{HPA_I_UPPER_BOUND, "hpaCurrUpperBound"}, {SSR_TO_DRO_WAIT_TIME, "ssrToDroWait"},
{DRO_TO_X8_WAIT_TIME, "droToX8Wait"}, {X8_TO_TX_WAIT_TIME, "x8ToTxWait"},
{TX_TO_MPA_WAIT_TIME, "txToMpaWait"}, {MPA_TO_HPA_WAIT_TIME, "mpaToHpaWait"},
};
enum PlPcduPoolIds : uint32_t { CHANNEL_VEC = 0, PROCESSED_VEC = 1, TEMP = 2 };
static constexpr size_t MAX_ADC_REPLY_SIZE = 64;
static constexpr DeviceCommandId_t READ_CMD = 0;
static constexpr DeviceCommandId_t SETUP_CMD = 1;
static constexpr DeviceCommandId_t READ_TEMP_EXT = 2;
static constexpr DeviceCommandId_t READ_WITH_TEMP_EXT = 3;
enum NormalSubmodeBits {
SOLID_STATE_RELAYS_ADC_ON = 0,
DRO_ON = 1,
X8_ON = 2,
TX_ON = 3,
MPA_ON = 4,
HPA_ON = 5
};
static constexpr Submode_t ALL_OFF_SUBMODE = 0;
static constexpr Submode_t ALL_ON_SUBMODE = (1 << HPA_ON) | (1 << MPA_ON) | (1 << TX_ON) |
(1 << X8_ON) | (1 << DRO_ON) |
(1 << SOLID_STATE_RELAYS_ADC_ON);
// 12 ADC values * 2 + trailing zero
static constexpr size_t ADC_REPLY_SIZE = 25;
// Conversion byte + 24 * zero
static constexpr size_t TEMP_REPLY_SIZE = 25;
static constexpr uint8_t SETUP_BYTE =
max1227::buildSetupByte(ClkSel::EXT_CONV_EXT_TIMED, RefSel::INT_REF_NO_WAKEUP, DiffSel::NONE_0);
static constexpr uint32_t ADC_SET_ID = READ_CMD;
static constexpr uint8_t CHANNELS_NUM = 12;
static constexpr uint8_t CHANNEL_N = CHANNELS_NUM - 1;
// Store temperature as well
static constexpr size_t DATASET_ENTRIES = CHANNELS_NUM + 1;
static constexpr uint8_t VOLTAGE_DIV = 6;
// 12-bit ADC: 2 to the power of 12 minus 1
static constexpr uint16_t MAX122X_BIT = 4095;
static constexpr float MAX122X_VREF = 2.5;
static constexpr float GAIN_INA169 = 100.0;
static constexpr float R_SHUNT_HPA = 0.008;
static constexpr float R_SHUNT_MPA = 0.015;
static constexpr float R_SHUNT_TX = 0.05;
static constexpr float R_SHUNT_X8 = 0.015;
static constexpr float R_SHUNT_DRO = 0.22;
static constexpr float V_POS = 3.3;
static constexpr float VOLTAGE_DIV_U_NEG = (49.9 + 27.4) / 27.4;
static constexpr float MAX122X_SCALE = MAX122X_VREF / MAX122X_BIT;
static constexpr float SCALE_VOLTAGE = MAX122X_SCALE * VOLTAGE_DIV;
static constexpr float SCALE_CURRENT_HPA = MAX122X_SCALE / (GAIN_INA169 * R_SHUNT_HPA);
static constexpr float SCALE_CURRENT_MPA = MAX122X_SCALE / (GAIN_INA169 * R_SHUNT_MPA);
static constexpr float SCALE_CURRENT_TX = MAX122X_SCALE / (GAIN_INA169 * R_SHUNT_TX);
static constexpr float SCALE_CURRENT_X8 = MAX122X_SCALE / (GAIN_INA169 * R_SHUNT_X8);
static constexpr float SCALE_CURRENT_DRO = MAX122X_SCALE / (GAIN_INA169 * R_SHUNT_DRO);
// TODO: Make these configurable parameters using a JSON file
// Upper bound of currents in milliamperes [mA]
static constexpr double DFT_NEG_V_LOWER_BOUND = -6.5;
static constexpr double DFT_NEG_V_UPPER_BOUND = -2.7;
static constexpr double DFT_DRO_U_LOWER_BOUND = 5.0;
static constexpr double DFT_DRO_U_UPPER_BOUND = 7.0;
// Max Current DRO + Max Current Neg V | 40 + 15
static constexpr double DFT_DRO_I_UPPER_BOUND = 55.0;
static constexpr double DFT_X8_U_LOWER_BOUND = 2.6;
static constexpr double DFT_X8_U_UPPER_BOUND = 4.0;
static constexpr double DFT_X8_I_UPPER_BOUND = 100.0;
static constexpr double DFT_TX_U_LOWER_BOUND = 2.6;
static constexpr double DFT_TX_U_UPPER_BOUND = 4.0;
static constexpr double DFT_TX_I_UPPER_BOUND = 250.0;
static constexpr double DFT_MPA_U_LOWER_BOUND = 2.6;
static constexpr double DFT_MPA_U_UPPER_BOUND = 4.0;
static constexpr double DFT_MPA_I_UPPER_BOUND = 650.0;
static constexpr double DFT_HPA_U_LOWER_BOUND = 9.4;
static constexpr double DFT_HPA_U_UPPER_BOUND = 11.0;
static constexpr double DFT_HPA_I_UPPER_BOUND = 3800.0;
// Wait time in floating point seconds
static constexpr double DFT_SSR_TO_DRO_WAIT_TIME = 5.0;
static constexpr double DFT_DRO_TO_X8_WAIT_TIME = 905.0;
static constexpr double DFT_X8_TO_TX_WAIT_TIME = 5.0;
static constexpr double DFT_TX_TO_MPA_WAIT_TIME = 5.0;
static constexpr double DFT_MPA_TO_HPA_WAIT_TIME = 5.0;
/**
* The current of the processed values is calculated and stored as a milliamperes [mA].
* The voltages are stored as Volt values.
*/
class PlPcduAdcSet : public StaticLocalDataSet<DATASET_ENTRIES> {
public:
PlPcduAdcSet(HasLocalDataPoolIF* owner) : StaticLocalDataSet(owner, ADC_SET_ID) {}
PlPcduAdcSet(object_id_t objectId) : StaticLocalDataSet(sid_t(objectId, ADC_SET_ID)) {}
lp_vec_t<uint16_t, 12> channels = lp_vec_t<uint16_t, 12>(sid.objectId, CHANNEL_VEC, this);
lp_vec_t<float, 12> processed = lp_vec_t<float, 12>(sid.objectId, PROCESSED_VEC, this);
lp_var_t<float> tempC = lp_var_t<float>(sid.objectId, TEMP, this);
};
class PlPcduParameter : public NVMParameterBase {
public:
PlPcduParameter() : NVMParameterBase(""), mountPrefix("") {
using namespace plpcdu;
// Initialize with default values
resetValues();
}
ReturnValue_t initialize(std::string mountPrefix) {
setFullName(mountPrefix + "/conf/plpcdu.json");
ReturnValue_t result = readJsonFile();
if (result != returnvalue::OK) {
// File does not exist or reading JSON failed for various reason. Rewrite the JSON file
#if OBSW_VERBOSE_LEVEL >= 1
sif::info << "Creating PL PCDU JSON file at " << getFullName() << std::endl;
#endif
resetValues();
writeJsonFile();
}
return returnvalue::OK;
}
void resetValues() {
insertValue(PARAM_KEY_MAP[SSR_TO_DRO_WAIT_TIME], DFT_SSR_TO_DRO_WAIT_TIME);
insertValue(PARAM_KEY_MAP[DRO_TO_X8_WAIT_TIME], DFT_DRO_TO_X8_WAIT_TIME);
insertValue(PARAM_KEY_MAP[X8_TO_TX_WAIT_TIME], DFT_X8_TO_TX_WAIT_TIME);
insertValue(PARAM_KEY_MAP[TX_TO_MPA_WAIT_TIME], DFT_TX_TO_MPA_WAIT_TIME);
insertValue(PARAM_KEY_MAP[MPA_TO_HPA_WAIT_TIME], DFT_MPA_TO_HPA_WAIT_TIME);
insertValue(PARAM_KEY_MAP[NEG_V_LOWER_BOUND], DFT_NEG_V_LOWER_BOUND);
insertValue(PARAM_KEY_MAP[NEG_V_UPPER_BOUND], DFT_NEG_V_UPPER_BOUND);
insertValue(PARAM_KEY_MAP[DRO_U_LOWER_BOUND], DFT_DRO_U_LOWER_BOUND);
insertValue(PARAM_KEY_MAP[DRO_U_UPPER_BOUND], DFT_DRO_U_UPPER_BOUND);
insertValue(PARAM_KEY_MAP[DRO_I_UPPER_BOUND], DFT_DRO_I_UPPER_BOUND);
insertValue(PARAM_KEY_MAP[X8_U_LOWER_BOUND], DFT_X8_U_LOWER_BOUND);
insertValue(PARAM_KEY_MAP[X8_U_UPPER_BOUND], DFT_X8_U_UPPER_BOUND);
insertValue(PARAM_KEY_MAP[X8_I_UPPER_BOUND], DFT_X8_I_UPPER_BOUND);
insertValue(PARAM_KEY_MAP[TX_U_LOWER_BOUND], DFT_TX_U_LOWER_BOUND);
insertValue(PARAM_KEY_MAP[TX_U_UPPER_BOUND], DFT_TX_U_UPPER_BOUND);
insertValue(PARAM_KEY_MAP[TX_I_UPPER_BOUND], DFT_TX_I_UPPER_BOUND);
insertValue(PARAM_KEY_MAP[MPA_U_LOWER_BOUND], DFT_MPA_U_LOWER_BOUND);
insertValue(PARAM_KEY_MAP[MPA_U_UPPER_BOUND], DFT_MPA_U_UPPER_BOUND);
insertValue(PARAM_KEY_MAP[MPA_I_UPPER_BOUND], DFT_MPA_I_UPPER_BOUND);
insertValue(PARAM_KEY_MAP[HPA_U_LOWER_BOUND], DFT_HPA_U_LOWER_BOUND);
insertValue(PARAM_KEY_MAP[HPA_U_UPPER_BOUND], DFT_HPA_U_UPPER_BOUND);
insertValue(PARAM_KEY_MAP[HPA_I_UPPER_BOUND], DFT_HPA_I_UPPER_BOUND);
}
private:
std::string mountPrefix;
};
} // namespace plpcdu
#endif /* LINUX_DEVICES_DEVICEDEFINITIONS_PAYLOADPCDUDEFINITIONS_H_ */

View File

@ -0,0 +1,121 @@
#ifndef MISSION_PAYLOAD_PLOCSPBASE_H_
#define MISSION_PAYLOAD_PLOCSPBASE_H_
#include <fsfw/globalfunctions/CRC.h>
#include <fsfw/tmtcpacket/ccsds/SpacePacketCreator.h>
#include <fsfw/tmtcpacket/ccsds/SpacePacketReader.h>
#include <cstdint>
namespace ploc {
struct SpTcParams {
SpTcParams(SpacePacketCreator& creator) : creator(creator) {}
SpTcParams(SpacePacketCreator& creator, uint8_t* buf, size_t maxSize)
: creator(creator), buf(buf), maxSize(maxSize) {}
// Set full payload length. Must also consider the two CRC bytes
void setFullPayloadLen(size_t fullPayloadLen_) { fullPayloadLen = fullPayloadLen_; }
SpacePacketCreator& creator;
uint8_t* buf = nullptr;
size_t maxSize = 0;
size_t fullPayloadLen = 0;
};
class SpTcBase {
public:
SpTcBase(SpTcParams params) : SpTcBase(params, 0x00, 1, 0) {}
SpTcBase(SpTcParams params, uint16_t apid, size_t payloadLen)
: SpTcBase(params, apid, payloadLen, 0) {}
SpTcBase(SpTcParams params, uint16_t apid, size_t payloadLen, uint16_t seqCount)
: spParams(params) {
spParams.creator.setApid(apid);
spParams.creator.setSeqCount(seqCount);
payloadStart = spParams.buf + ccsds::HEADER_LEN;
spParams.fullPayloadLen = payloadLen;
updateSpFields();
}
void updateSpFields() {
updateLenFromParams();
spParams.creator.setPacketType(ccsds::PacketType::TC);
}
void updateLenFromParams() { spParams.creator.setDataLenField(spParams.fullPayloadLen - 1); }
const uint8_t* getFullPacket() const { return spParams.buf; }
const uint8_t* getPacketData() const { return spParams.buf + ccsds::HEADER_LEN; }
size_t getFullPacketLen() const { return spParams.creator.getFullPacketLen(); }
uint16_t getApid() const { return spParams.creator.getApid(); }
uint16_t getSeqCount() const { return spParams.creator.getSequenceCount(); }
ReturnValue_t checkPayloadLen() {
if (ccsds::HEADER_LEN + spParams.fullPayloadLen > spParams.maxSize) {
return SerializeIF::BUFFER_TOO_SHORT;
}
return returnvalue::OK;
}
ReturnValue_t serializeHeader() {
updateSpFields();
size_t serLen = 0;
return spParams.creator.serializeBe(spParams.buf, serLen, spParams.maxSize);
}
ReturnValue_t checkSizeAndSerializeHeader() {
ReturnValue_t result = checkPayloadLen();
if (result != returnvalue::OK) {
return result;
}
return serializeHeader();
}
ReturnValue_t calcAndSetCrc() {
/* Calculate crc */
uint16_t crc = CRC::crc16ccitt(spParams.buf, getFullPacketLen() - 2);
/* Add crc to packet data field of space packet */
size_t serializedSize = 0;
return SerializeAdapter::serialize(&crc, spParams.buf + getFullPacketLen() - 2, &serializedSize,
spParams.maxSize, SerializeIF::Endianness::BIG);
}
protected:
ploc::SpTcParams spParams;
uint8_t* payloadStart;
};
/**
* @brief Class for handling tm replies of the supervisor.
*/
class SpTmReader : public SpacePacketReader {
public:
SpTmReader() = default;
/**
* @brief Constructor creates idle packet and sets length field to maximum allowed size.
*/
SpTmReader(const uint8_t* buf, size_t maxSize) : SpacePacketReader(buf, maxSize) {}
ReturnValue_t setData(const uint8_t* buf, size_t maxSize) {
return setReadOnlyData(buf, maxSize);
}
ReturnValue_t checkCrc() const {
if (CRC::crc16ccitt(getFullData(), getFullPacketLen()) != 0) {
return returnvalue::FAILED;
}
return returnvalue::OK;
}
};
} // namespace ploc
#endif /* MISSION_PAYLOAD_PLOCSPBASE_H_ */

View File

@ -0,0 +1,83 @@
#ifndef MISSION_DEVICES_DEVICEDEFINITIONS_RADSENSOR_H_
#define MISSION_DEVICES_DEVICEDEFINITIONS_RADSENSOR_H_
namespace RAD_SENSOR {
static const DeviceCommandId_t NONE = 0x0; // Set when no command is pending
/**
* This command initiates the ADC conversion for all channels including the internal
* temperature sensor.
*/
static const DeviceCommandId_t WRITE_SETUP = 1;
static const DeviceCommandId_t START_CONVERSION = 2;
static const DeviceCommandId_t READ_CONVERSIONS = 3;
static const DeviceCommandId_t ENABLE_DEBUG_OUTPUT = 4;
static const DeviceCommandId_t DISABLE_DEBUG_OUTPUT = 5;
/**
* @brief This is the configuration byte which will be written to the setup register after
* power on.
*
* @note Bit1 (DIFFSEL1) - Bit0 (DIFFSEL0): 0b00, no data follows the setup byte
* Bit3 (REFSEL1) - Bit2 (REFSEL0): 0b10, internal reference, no wake-up delay
* Bit5 (CLKSEL1) - Bit4 (CLKSEL0): 0b10, MAX1227 uses internal oscillator for timing
* Bit7 - Bit6: 0b01, tells MAX1227 that this is the setup register
*
*/
static const uint8_t SETUP_DEFINITION = 0b01101000;
/**
* @brief This value will always be written to the ADC conversion register to specify the
* conversions to perform.
* @details Bit0: 1 - Enables temperature conversion
* Bit2 (SCAN1) and Bit1 (SCAN0): 0b00 (channel conversion from 0 to N)
* Bit6 - Bit3 defines N: 0b0111 (N = 7)
* Bit7: Always 1. Tells the ADC that this is the conversion register.
*/
static const uint8_t CONVERSION_DEFINITION = 0b10111001;
// static const uint8_t CONVERSION_DEFINITION = 0b10111111;
/**
* @brief Writing this value resets the fifo of the MAX1227.
*/
static const uint8_t RESET_DEFINITION = 0b00011000;
static const uint8_t DUMMY_BYTE = 0xFF;
static const uint8_t RAD_SENSOR_DATA_SET_ID = READ_CONVERSIONS;
static const uint8_t DATASET_ENTRIES = 7;
/**
* One temperature value and conversions for AIN0 - AIN7
*/
static const uint8_t READ_SIZE = 18;
enum Max1227PoolIds : lp_id_t {
TEMPERATURE_C,
AIN0,
AIN1,
AIN4,
AIN5,
AIN6,
AIN7,
};
class RadSensorDataset : public StaticLocalDataSet<DATASET_ENTRIES> {
public:
RadSensorDataset(HasLocalDataPoolIF* owner) : StaticLocalDataSet(owner, RAD_SENSOR_DATA_SET_ID) {}
RadSensorDataset(object_id_t objectId)
: StaticLocalDataSet(sid_t(objectId, RAD_SENSOR_DATA_SET_ID)) {}
lp_var_t<float> temperatureCelcius = lp_var_t<float>(sid.objectId, TEMPERATURE_C, this);
lp_var_t<uint16_t> ain0 = lp_var_t<uint16_t>(sid.objectId, AIN0, this);
lp_var_t<uint16_t> ain1 = lp_var_t<uint16_t>(sid.objectId, AIN1, this);
lp_var_t<uint16_t> ain4 = lp_var_t<uint16_t>(sid.objectId, AIN4, this);
lp_var_t<uint16_t> ain5 = lp_var_t<uint16_t>(sid.objectId, AIN5, this);
lp_var_t<uint16_t> ain6 = lp_var_t<uint16_t>(sid.objectId, AIN6, this);
lp_var_t<uint16_t> ain7 = lp_var_t<uint16_t>(sid.objectId, AIN7, this);
};
} // namespace RAD_SENSOR
#endif /* MISSION_DEVICES_DEVICEDEFINITIONS_RADSENSOR_H_ */

View File

@ -0,0 +1,34 @@
#include <fsfw/globalfunctions/CRC.h>
#include <mission/payload/scexHelpers.h>
#include <cstring>
uint8_t scex::createCmdByte(Cmds cmd, bool tempCheck) {
return (IDLE_BIT_0_DEF_STATE << 7) | (IDLE_BIT_1_DEF_STATE << 6) | (cmd << 1) | tempCheck;
}
ReturnValue_t scex::prepareScexCmd(Cmds cmd, std::pair<uint8_t*, size_t> cmdBufPair, size_t& cmdLen,
std::pair<const uint8_t*, size_t> usrDataPair, bool tempCheck) {
using namespace scex;
uint8_t* cmdBuf = cmdBufPair.first;
const uint8_t* userData = usrDataPair.first;
// Send command
if (cmdBuf == nullptr or (cmdBufPair.second < usrDataPair.second + HEADER_LEN + CRC_LEN) or
(usrDataPair.second > 0 and userData == nullptr)) {
cmdLen = 0;
return returnvalue::FAILED;
}
cmdBuf[0] = createCmdByte(cmd, tempCheck);
// These two fields are the packet counter and the total packet count. Those are 1 and 1 for each
// telecommand so far
cmdBuf[1] = 1;
cmdBuf[2] = 1;
cmdBuf[3] = (usrDataPair.second >> 8) & 0xff;
cmdBuf[4] = usrDataPair.second & 0xff;
std::memcpy(cmdBuf + HEADER_LEN, userData, usrDataPair.second);
uint16_t crc = CRC::crc16ccitt(cmdBuf, usrDataPair.second + HEADER_LEN);
cmdBuf[usrDataPair.second + HEADER_LEN] = (crc >> 8) & 0xff;
cmdBuf[usrDataPair.second + HEADER_LEN + 1] = crc & 0xff;
cmdLen = usrDataPair.second + HEADER_LEN + CRC_LEN;
return returnvalue::OK;
}

View File

@ -0,0 +1,52 @@
#ifndef MISSION_PAYLOAD_SCEXHELPERS_H_
#define MISSION_PAYLOAD_SCEXHELPERS_H_
#include <fsfw/devicehandlers/DeviceHandlerIF.h>
#include <fsfw/events/Event.h>
#include <cstdint>
#include <vector>
#include "eive/eventSubsystemIds.h"
#include "eive/objects.h"
// Definitions for the Solar Cell Experiment
namespace scex {
static constexpr uint8_t SUBSYSTEM_ID = SUBSYSTEM_ID::SCEX_HANDLER;
static constexpr Event MISSING_PACKET = event::makeEvent(SUBSYSTEM_ID, 0, severity::LOW);
static constexpr Event EXPERIMENT_TIMEDOUT = event::makeEvent(SUBSYSTEM_ID, 1, severity::LOW);
//! FRAM, One Cell or All cells command finished. P1: Command ID
static constexpr Event MULTI_PACKET_COMMAND_DONE =
event::makeEvent(SUBSYSTEM_ID, 2, severity::INFO);
enum Cmds : DeviceCommandId_t {
PING = 0b00111,
ALL_CELLS_CMD = 0b00101,
ONE_CELL = 0b00110,
FRAM = 0b00001,
EXP_STATUS_CMD = 0b00010,
TEMP_CMD = 0b00011,
ION_CMD = 0b00100,
ERROR_REPLY = 0b01000,
INVALID = 255
};
static const std::vector<DeviceCommandId_t> VALID_CMDS = {
PING, ALL_CELLS_CMD, ONE_CELL, FRAM, EXP_STATUS_CMD, TEMP_CMD, ION_CMD};
static constexpr uint8_t HEADER_LEN = 5;
static constexpr uint8_t CRC_LEN = 2;
static constexpr uint8_t IDLE_BIT_0_DEF_STATE = 0;
static constexpr uint8_t IDLE_BIT_1_DEF_STATE = 1;
uint8_t createCmdByte(Cmds cmd, bool tempCheck = false);
ReturnValue_t prepareScexCmd(Cmds cmd, std::pair<uint8_t*, size_t> cmdBufPair, size_t& cmdLen,
std::pair<const uint8_t*, size_t> usrDataPair, bool tempCheck = false);
} // namespace scex
#endif /* MISSION_PAYLOAD_SCEXHELPERS_H_ */