Merge pull request 'PTME Bandwidth Allocation Table' (#495) from ptme_bat_priority_enable into develop
All checks were successful
EIVE/eive-obsw/pipeline/head This commit looks good

Reviewed-on: #495
This commit is contained in:
Robin Müller 2023-03-21 14:44:03 +01:00
commit 4277a98c16
10 changed files with 158 additions and 35 deletions

View File

@ -16,6 +16,8 @@ will consitute of a breaking change warranting a new major release:
# [unreleased] # [unreleased]
Requires firmware update for new FPGA design where reset line is routed into the software.
## Added ## Added
- Added NaN and Inf check for the `MEKF`. If these are detected, the `AcsController` will reset - Added NaN and Inf check for the `MEKF`. If these are detected, the `AcsController` will reset
@ -36,6 +38,13 @@ will consitute of a breaking change warranting a new major release:
Furthermore, the submode in the NORMAL mode now should be 0 instead of some ON mode submode. Furthermore, the submode in the NORMAL mode now should be 0 instead of some ON mode submode.
- Updated GYR bias values to newest measurements. This also corrects the ADIS values to always - Updated GYR bias values to newest measurements. This also corrects the ADIS values to always
consit of just one digit. consit of just one digit.
- The CCSDS IP core handler now exposes a parameter to enable the priority select mode
for the PTME core. This mode prioritizes virtual channels with a lower index, so for example
the virtual channel (VC0) will have the highest priority, while VC3 will have the lowestg
priority. This mode will be enabled by default for now, but can be set via the parameter IF with
the unique parameter ID 0. The update of this mode requires a PTME reset. Therefore, it will only
be performed when the transmitter is off to avoid weird bugs.
- Connect and handle reset line for the PTME core in the software now.
- Safe mode controller failure event now only triggers once per minute. - Safe mode controller failure event now only triggers once per minute.
# [v1.38.0] 2023-03-17 # [v1.38.0] 2023-03-17

View File

@ -90,6 +90,8 @@ static constexpr char PAPB_BUSY_SIGNAL_VC2[] = "papb_busy_signal_vc2";
static constexpr char PAPB_EMPTY_SIGNAL_VC2[] = "papb_empty_signal_vc2"; static constexpr char PAPB_EMPTY_SIGNAL_VC2[] = "papb_empty_signal_vc2";
static constexpr char PAPB_BUSY_SIGNAL_VC3[] = "papb_busy_signal_vc3"; static constexpr char PAPB_BUSY_SIGNAL_VC3[] = "papb_busy_signal_vc3";
static constexpr char PAPB_EMPTY_SIGNAL_VC3[] = "papb_empty_signal_vc3"; static constexpr char PAPB_EMPTY_SIGNAL_VC3[] = "papb_empty_signal_vc3";
static constexpr char PTME_RESETN[] = "ptme_resetn";
static constexpr char RS485_EN_TX_CLOCK[] = "tx_clock_enable_ltc2872"; static constexpr char RS485_EN_TX_CLOCK[] = "tx_clock_enable_ltc2872";
static constexpr char RS485_EN_TX_DATA[] = "tx_data_enable_ltc2872"; static constexpr char RS485_EN_TX_DATA[] = "tx_data_enable_ltc2872";
static constexpr char RS485_EN_RX_CLOCK[] = "rx_clock_enable_ltc2872"; static constexpr char RS485_EN_RX_CLOCK[] = "rx_clock_enable_ltc2872";

View File

@ -748,6 +748,9 @@ ReturnValue_t ObjectFactory::createCcsdsComponents(CcsdsComponentArgs& args) {
gpioCookiePtmeIp->addGpio(gpioIds::VC3_PAPB_BUSY, gpio); gpioCookiePtmeIp->addGpio(gpioIds::VC3_PAPB_BUSY, gpio);
gpio = new GpiodRegularByLineName(q7s::gpioNames::PAPB_EMPTY_SIGNAL_VC3, "PAPB VC3"); gpio = new GpiodRegularByLineName(q7s::gpioNames::PAPB_EMPTY_SIGNAL_VC3, "PAPB VC3");
gpioCookiePtmeIp->addGpio(gpioIds::VC3_PAPB_EMPTY, gpio); gpioCookiePtmeIp->addGpio(gpioIds::VC3_PAPB_EMPTY, gpio);
gpio = new GpiodRegularByLineName(q7s::gpioNames::PTME_RESETN, "PTME RESETN",
gpio::Direction::OUT, gpio::Levels::HIGH);
gpioCookiePtmeIp->addGpio(gpioIds::PTME_RESETN, gpio);
gpioChecker(args.gpioComIF.addGpios(gpioCookiePtmeIp), "PTME PAPB VCs"); gpioChecker(args.gpioComIF.addGpios(gpioCookiePtmeIp), "PTME PAPB VCs");
// Creating virtual channel interfaces // Creating virtual channel interfaces
VirtualChannelIF* vc0 = VirtualChannelIF* vc0 =
@ -772,9 +775,14 @@ ReturnValue_t ObjectFactory::createCcsdsComponents(CcsdsComponentArgs& args) {
new AxiPtmeConfig(objects::AXI_PTME_CONFIG, q7s::UIO_PTME, q7s::uiomapids::PTME_CONFIG); new AxiPtmeConfig(objects::AXI_PTME_CONFIG, q7s::UIO_PTME, q7s::uiomapids::PTME_CONFIG);
PtmeConfig* ptmeConfig = new PtmeConfig(objects::PTME_CONFIG, axiPtmeConfig); PtmeConfig* ptmeConfig = new PtmeConfig(objects::PTME_CONFIG, axiPtmeConfig);
*args.ipCoreHandler = new CcsdsIpCoreHandler( PtmeGpios gpios;
objects::CCSDS_HANDLER, objects::CCSDS_PACKET_DISTRIBUTOR, *ptmeConfig, LINK_STATE, gpios.enableTxClock = gpioIds::RS485_EN_TX_CLOCK;
&args.gpioComIF, gpioIds::RS485_EN_TX_CLOCK, gpioIds::RS485_EN_TX_DATA); gpios.enableTxData = gpioIds::RS485_EN_TX_CLOCK;
gpios.ptmeResetn = gpioIds::PTME_RESETN;
*args.ipCoreHandler =
new CcsdsIpCoreHandler(objects::CCSDS_HANDLER, objects::CCSDS_PACKET_DISTRIBUTOR, *ptmeConfig,
LINK_STATE, &args.gpioComIF, gpios);
// This VC will receive all live TM // This VC will receive all live TM
auto* vcWithQueue = auto* vcWithQueue =
new VirtualChannelWithQueue(objects::PTME_VC0_LIVE_TM, ccsds::VC0, "PTME VC0 LIVE TM", *ptme, new VirtualChannelWithQueue(objects::PTME_VC0_LIVE_TM, ccsds::VC0, "PTME VC0 LIVE TM", *ptme,

View File

@ -102,6 +102,7 @@ enum gpioId_t {
VC2_PAPB_BUSY, VC2_PAPB_BUSY,
VC3_PAPB_EMPTY, VC3_PAPB_EMPTY,
VC3_PAPB_BUSY, VC3_PAPB_BUSY,
PTME_RESETN,
PDEC_RESET, PDEC_RESET,

View File

@ -71,6 +71,22 @@ ReturnValue_t AxiPtmeConfig::disableTxclockInversion() {
return returnvalue::OK; return returnvalue::OK;
} }
ReturnValue_t AxiPtmeConfig::enableBatPriorityBit() {
ReturnValue_t result = writeBit(COMMON_CONFIG_REG, true, BitPos::EN_BAT_PRIORITY);
if (result != returnvalue::OK) {
return result;
}
return returnvalue::OK;
}
ReturnValue_t AxiPtmeConfig::disableBatPriorityBit() {
ReturnValue_t result = writeBit(COMMON_CONFIG_REG, false, BitPos::EN_BAT_PRIORITY);
if (result != returnvalue::OK) {
return result;
}
return returnvalue::OK;
}
ReturnValue_t AxiPtmeConfig::writeReg(uint32_t regOffset, uint32_t writeVal) { ReturnValue_t AxiPtmeConfig::writeReg(uint32_t regOffset, uint32_t writeVal) {
ReturnValue_t result = returnvalue::OK; ReturnValue_t result = returnvalue::OK;
result = mutex->lockMutex(timeoutType, mutexTimeout); result = mutex->lockMutex(timeoutType, mutexTimeout);

View File

@ -54,6 +54,9 @@ class AxiPtmeConfig : public SystemObject {
ReturnValue_t enableTxclockInversion(); ReturnValue_t enableTxclockInversion();
ReturnValue_t disableTxclockInversion(); ReturnValue_t disableTxclockInversion();
ReturnValue_t enableBatPriorityBit();
ReturnValue_t disableBatPriorityBit();
private: private:
// Address of register storing the bitrate configuration parameter // Address of register storing the bitrate configuration parameter
static const uint32_t CADU_BITRATE_REG = 0x0; static const uint32_t CADU_BITRATE_REG = 0x0;
@ -61,7 +64,7 @@ class AxiPtmeConfig : public SystemObject {
static const uint32_t COMMON_CONFIG_REG = 0x4; static const uint32_t COMMON_CONFIG_REG = 0x4;
static const uint32_t ADRESS_DIVIDER = 4; static const uint32_t ADRESS_DIVIDER = 4;
enum class BitPos : uint32_t { EN_TX_CLK_MANIPULATOR, INVERT_CLOCK }; enum class BitPos : uint32_t { EN_TX_CLK_MANIPULATOR = 0, INVERT_CLOCK = 1, EN_BAT_PRIORITY = 2 };
std::string axiUio; std::string axiUio;
std::string uioMap; std::string uioMap;

View File

@ -48,3 +48,11 @@ ReturnValue_t PtmeConfig::configTxManipulator(bool enable) {
} }
return result; return result;
} }
ReturnValue_t PtmeConfig::enableBatPriorityBit(bool enable) {
if (enable) {
return axiPtmeConfig->enableBatPriorityBit();
} else {
return axiPtmeConfig->disableBatPriorityBit();
}
}

View File

@ -1,6 +1,8 @@
#ifndef LINUX_OBC_PTMECONFIG_H_ #ifndef LINUX_OBC_PTMECONFIG_H_
#define LINUX_OBC_PTMECONFIG_H_ #define LINUX_OBC_PTMECONFIG_H_
#include <fsfw_hal/common/gpio/gpioDefinitions.h>
#include "AxiPtmeConfig.h" #include "AxiPtmeConfig.h"
#include "fsfw/objectmanager/SystemObject.h" #include "fsfw/objectmanager/SystemObject.h"
#include "fsfw/returnvalues/returnvalue.h" #include "fsfw/returnvalues/returnvalue.h"
@ -53,6 +55,15 @@ class PtmeConfig : public SystemObject {
*/ */
ReturnValue_t configTxManipulator(bool enable); ReturnValue_t configTxManipulator(bool enable);
/**
* Enable the bat priority bit in the PTME wrapper component.
* Please note that a reset of the PTME is still required as specified in the documentation.
* This is done by a higher level component.
* @param enable
* @return
*/
ReturnValue_t enableBatPriorityBit(bool enable);
private: private:
static const uint8_t INTERFACE_ID = CLASS_ID::RATE_SETTER; static const uint8_t INTERFACE_ID = CLASS_ID::RATE_SETTER;

View File

@ -14,7 +14,7 @@
CcsdsIpCoreHandler::CcsdsIpCoreHandler(object_id_t objectId, object_id_t tcDestination, CcsdsIpCoreHandler::CcsdsIpCoreHandler(object_id_t objectId, object_id_t tcDestination,
PtmeConfig& ptmeConfig, std::atomic_bool& linkState, PtmeConfig& ptmeConfig, std::atomic_bool& linkState,
GpioIF* gpioIF, gpioId_t enTxClock, gpioId_t enTxData) GpioIF* gpioIF, PtmeGpios gpioIds)
: SystemObject(objectId), : SystemObject(objectId),
linkState(linkState), linkState(linkState),
tcDestination(tcDestination), tcDestination(tcDestination),
@ -22,9 +22,8 @@ CcsdsIpCoreHandler::CcsdsIpCoreHandler(object_id_t objectId, object_id_t tcDesti
actionHelper(this, nullptr), actionHelper(this, nullptr),
modeHelper(this), modeHelper(this),
ptmeConfig(ptmeConfig), ptmeConfig(ptmeConfig),
gpioIF(gpioIF), ptmeGpios(gpioIds),
enTxClock(enTxClock), gpioIF(gpioIF) {
enTxData(enTxData) {
commandQueue = QueueFactory::instance()->createMessageQueue(QUEUE_SIZE); commandQueue = QueueFactory::instance()->createMessageQueue(QUEUE_SIZE);
auto mqArgs = MqArgs(objectId, static_cast<void*>(this)); auto mqArgs = MqArgs(objectId, static_cast<void*>(this));
eventQueue = eventQueue =
@ -71,6 +70,12 @@ ReturnValue_t CcsdsIpCoreHandler::initialize() {
return ObjectManagerIF::CHILD_INIT_FAILED; return ObjectManagerIF::CHILD_INIT_FAILED;
} }
if (batPriorityParam == 0) {
disablePrioritySelectMode();
} else {
enablePrioritySelectMode();
}
#if OBSW_SYRLINKS_SIMULATED == 1 #if OBSW_SYRLINKS_SIMULATED == 1
// Update data on rising edge // Update data on rising edge
ptmeConfig.invertTxClock(false); ptmeConfig.invertTxClock(false);
@ -111,7 +116,24 @@ ReturnValue_t CcsdsIpCoreHandler::getParameter(uint8_t domainId, uint8_t uniqueI
ParameterWrapper* parameterWrapper, ParameterWrapper* parameterWrapper,
const ParameterWrapper* newValues, const ParameterWrapper* newValues,
uint16_t startAtIndex) { uint16_t startAtIndex) {
return returnvalue::OK; if ((domainId == 0) and (uniqueIdentifier == ParamId::BAT_PRIORITY)) {
uint8_t newVal = 0;
ReturnValue_t result = newValues->getElement(&newVal);
if (result != returnvalue::OK) {
return result;
}
if (newVal > 1) {
return HasParametersIF::INVALID_VALUE;
}
parameterWrapper->set(batPriorityParam);
if (mode == MODE_ON) {
updateBatPriorityOnTxOff = true;
} else if (mode == MODE_OFF) {
updateBatPriorityFromParam();
}
return returnvalue::OK;
}
return HasParametersIF::INVALID_IDENTIFIER_ID;
} }
uint32_t CcsdsIpCoreHandler::getIdentifier() const { return 0; } uint32_t CcsdsIpCoreHandler::getIdentifier() const { return 0; }
@ -184,8 +206,8 @@ void CcsdsIpCoreHandler::updateLinkState() { linkState = LINK_UP; }
void CcsdsIpCoreHandler::enableTransmit() { void CcsdsIpCoreHandler::enableTransmit() {
#ifndef TE0720_1CFA #ifndef TE0720_1CFA
gpioIF->pullHigh(enTxClock); gpioIF->pullHigh(ptmeGpios.enableTxClock);
gpioIF->pullHigh(enTxData); gpioIF->pullHigh(ptmeGpios.enableTxData);
#endif #endif
linkState = LINK_UP; linkState = LINK_UP;
} }
@ -211,14 +233,8 @@ ReturnValue_t CcsdsIpCoreHandler::checkModeCommand(Mode_t mode, Submode_t submod
} }
void CcsdsIpCoreHandler::startTransition(Mode_t mode, Submode_t submode) { void CcsdsIpCoreHandler::startTransition(Mode_t mode, Submode_t submode) {
auto rateHigh = [&]() { auto rateSet = [&](uint32_t rate) {
ReturnValue_t result = ptmeConfig.setRate(RATE_500KBPS); ReturnValue_t result = ptmeConfig.setRate(rate);
if (result == returnvalue::OK) {
this->mode = HasModesIF::MODE_ON;
}
};
auto rateLow = [&]() {
ReturnValue_t result = ptmeConfig.setRate(RATE_100KBPS);
if (result == returnvalue::OK) { if (result == returnvalue::OK) {
this->mode = HasModesIF::MODE_ON; this->mode = HasModesIF::MODE_ON;
} }
@ -228,14 +244,14 @@ void CcsdsIpCoreHandler::startTransition(Mode_t mode, Submode_t submode) {
if (submode == static_cast<Submode_t>(com::CcsdsSubmode::DATARATE_DEFAULT)) { if (submode == static_cast<Submode_t>(com::CcsdsSubmode::DATARATE_DEFAULT)) {
com::Datarate currentDatarate = com::getCurrentDatarate(); com::Datarate currentDatarate = com::getCurrentDatarate();
if (currentDatarate == com::Datarate::LOW_RATE_MODULATION_BPSK) { if (currentDatarate == com::Datarate::LOW_RATE_MODULATION_BPSK) {
rateLow(); rateSet(RATE_100KBPS);
} else if (currentDatarate == com::Datarate::HIGH_RATE_MODULATION_0QPSK) { } else if (currentDatarate == com::Datarate::HIGH_RATE_MODULATION_0QPSK) {
rateHigh(); rateSet(RATE_500KBPS);
} }
} else if (submode == static_cast<Submode_t>(com::CcsdsSubmode::DATARATE_HIGH)) { } else if (submode == static_cast<Submode_t>(com::CcsdsSubmode::DATARATE_HIGH)) {
rateHigh(); rateSet(RATE_500KBPS);
} else if (submode == static_cast<Submode_t>(com::CcsdsSubmode::DATARATE_LOW)) { } else if (submode == static_cast<Submode_t>(com::CcsdsSubmode::DATARATE_LOW)) {
rateLow(); rateSet(RATE_100KBPS);
} }
} else if (mode == HasModesIF::MODE_OFF) { } else if (mode == HasModesIF::MODE_OFF) {
@ -250,11 +266,16 @@ void CcsdsIpCoreHandler::startTransition(Mode_t mode, Submode_t submode) {
void CcsdsIpCoreHandler::announceMode(bool recursive) { triggerEvent(MODE_INFO, mode, submode); } void CcsdsIpCoreHandler::announceMode(bool recursive) { triggerEvent(MODE_INFO, mode, submode); }
void CcsdsIpCoreHandler::disableTransmit() { void CcsdsIpCoreHandler::disableTransmit() {
ptmeConfig.enableBatPriorityBit(false);
#ifndef TE0720_1CFA #ifndef TE0720_1CFA
gpioIF->pullLow(enTxClock); gpioIF->pullLow(ptmeGpios.enableTxClock);
gpioIF->pullLow(enTxData); gpioIF->pullLow(ptmeGpios.enableTxData);
#endif #endif
linkState = LINK_DOWN; linkState = LINK_DOWN;
if (updateBatPriorityOnTxOff) {
updateBatPriorityFromParam();
updateBatPriorityOnTxOff = false;
}
} }
const char* CcsdsIpCoreHandler::getName() const { return "CCSDS Handler"; } const char* CcsdsIpCoreHandler::getName() const { return "CCSDS Handler"; }
@ -270,3 +291,25 @@ ReturnValue_t CcsdsIpCoreHandler::connectModeTreeParent(HasModeTreeChildrenIF& p
ModeTreeChildIF& CcsdsIpCoreHandler::getModeTreeChildIF() { return *this; } ModeTreeChildIF& CcsdsIpCoreHandler::getModeTreeChildIF() { return *this; }
object_id_t CcsdsIpCoreHandler::getObjectId() const { return SystemObject::getObjectId(); } object_id_t CcsdsIpCoreHandler::getObjectId() const { return SystemObject::getObjectId(); }
void CcsdsIpCoreHandler::enablePrioritySelectMode() {
ptmeConfig.enableBatPriorityBit(true);
// Reset the PTME
gpioIF->pullLow(ptmeGpios.ptmeResetn);
gpioIF->pullHigh(ptmeGpios.ptmeResetn);
}
void CcsdsIpCoreHandler::disablePrioritySelectMode() {
ptmeConfig.enableBatPriorityBit(false);
// Reset the PTME
gpioIF->pullLow(ptmeGpios.ptmeResetn);
gpioIF->pullHigh(ptmeGpios.ptmeResetn);
}
void CcsdsIpCoreHandler::updateBatPriorityFromParam() {
if (batPriorityParam == 0) {
disablePrioritySelectMode();
} else {
enablePrioritySelectMode();
}
}

View File

@ -25,12 +25,25 @@
#include "linux/ipcore/PtmeConfig.h" #include "linux/ipcore/PtmeConfig.h"
#include "mission/comDefs.h" #include "mission/comDefs.h"
struct PtmeGpios {
gpioId_t enableTxClock = gpio::NO_GPIO;
gpioId_t enableTxData = gpio::NO_GPIO;
gpioId_t ptmeResetn = gpio::NO_GPIO;
};
/** /**
* @brief This class handles the data exchange with the CCSDS IP cores implemented in the * @brief This class handles the data exchange with the CCSDS IP cores implemented in the
* programmable logic of the Q7S. * programmable logic of the Q7S.
* *
* @details After reboot default CADU bitrate is always set to 100 kbps (results in downlink rate * @details
* of 200 kbps due to convolutional code added by syrlinks transceiver) * After reboot default CADU bitrate is always set to 100 kbps (results in downlink rate
* of 200 kbps due to convolutional code added by syrlinks transceiver). The IP core handler exposes
* a parameter to enable the priority selection mode for the PTME core.
*
* If the transmitter is on, the selection mode will be enabled when the transmitter goes off.
* If the transmitter is off, the update of the PTME will be done immediately on a parameter update.
* This is done because changing this parameter requires a reset of the PTME core to avoid bugs
* while the transmitter is enabled.
* *
* @author J. Meier * @author J. Meier
*/ */
@ -39,11 +52,12 @@ class CcsdsIpCoreHandler : public SystemObject,
public ModeTreeChildIF, public ModeTreeChildIF,
public ModeTreeConnectionIF, public ModeTreeConnectionIF,
public HasModesIF, public HasModesIF,
// public AcceptsTelemetryIF,
public AcceptsTelecommandsIF, public AcceptsTelecommandsIF,
public ReceivesParameterMessagesIF, public ReceivesParameterMessagesIF,
public HasActionsIF { public HasActionsIF {
public: public:
enum ParamId : uint8_t { BAT_PRIORITY = 0 };
static const bool LINK_UP = true; static const bool LINK_UP = true;
static const bool LINK_DOWN = false; static const bool LINK_DOWN = false;
using VcId_t = uint8_t; using VcId_t = uint8_t;
@ -61,8 +75,7 @@ class CcsdsIpCoreHandler : public SystemObject,
* @param enTxData GPIO ID of RS485 tx data enable * @param enTxData GPIO ID of RS485 tx data enable
*/ */
CcsdsIpCoreHandler(object_id_t objectId, object_id_t tcDestination, PtmeConfig& ptmeConfig, CcsdsIpCoreHandler(object_id_t objectId, object_id_t tcDestination, PtmeConfig& ptmeConfig,
std::atomic_bool& linkState, GpioIF* gpioIF, gpioId_t enTxClock, std::atomic_bool& linkState, GpioIF* gpioIF, PtmeGpios gpioIds);
gpioId_t enTxData);
~CcsdsIpCoreHandler(); ~CcsdsIpCoreHandler();
@ -138,15 +151,14 @@ class CcsdsIpCoreHandler : public SystemObject,
MessageQueueId_t tcDistributorQueueId = MessageQueueIF::NO_QUEUE; MessageQueueId_t tcDistributorQueueId = MessageQueueIF::NO_QUEUE;
PtmeConfig& ptmeConfig; PtmeConfig& ptmeConfig;
PtmeGpios ptmeGpios;
// BAT priority bit on by default to enable priority selection mode for the PTME.
uint8_t batPriorityParam = 1;
bool updateBatPriorityOnTxOff = false;
GpioIF* gpioIF = nullptr; GpioIF* gpioIF = nullptr;
// GPIO to enable RS485 transceiver for TX clock
gpioId_t enTxClock = gpio::NO_GPIO;
// GPIO to enable RS485 transceiver for TX data signal
gpioId_t enTxData = gpio::NO_GPIO;
void readCommandQueue(void); void readCommandQueue(void);
void handleTelemetry();
/** /**
* @brief Forward link state to virtual channels. * @brief Forward link state to virtual channels.
@ -163,6 +175,16 @@ class CcsdsIpCoreHandler : public SystemObject,
* RS485 transceiver chips to high. * RS485 transceiver chips to high.
*/ */
void disableTransmit(); void disableTransmit();
/**
* The following set of functions configure the mode of the PTME bandwith allocation table (BAT)
* module. This consists of the following 2 steps:
* 1. Update the BAT priority bit in the PTME wrapper
* 2. Reset the PTME as specified in the datasheet.
*/
void enablePrioritySelectMode();
void disablePrioritySelectMode();
void updateBatPriorityFromParam();
}; };
#endif /* CCSDSHANDLER_H_ */ #endif /* CCSDSHANDLER_H_ */