diff --git a/README.md b/README.md index b6a18947..ff2c359d 100644 --- a/README.md +++ b/README.md @@ -11,13 +11,14 @@ 4. [Useful and Common Host Commands](#host-commands) 5. [Setting up Prerequisites](#set-up-prereq) 6. [Remote Debugging](#remote-debugging) -7. [Direct Debugging](#direct-debugging) -8. [Transfering Files to the Q7S](#file-transfer) -9. [Q7S OBC](#q7s) -10. [Static Code Analysis](#static-code-analysis) -11. [Eclipse](#eclipse) -12. [Running the OBSW on a Raspberry Pi](#rpi) -13. [FSFW](#fsfw) +7. [TMTC testing](#tmtc-testing) +8. [Direct Debugging](#direct-debugging) +9. [Transfering Files to the Q7S](#file-transfer) +10. [Q7S OBC](#q7s) +11. [Static Code Analysis](#static-code-analysis) +12. [Eclipse](#eclipse) +13. [Running the OBSW on a Raspberry Pi](#rpi) +14. [FSFW](#fsfw) # General information @@ -574,6 +575,40 @@ alias or shell script to do this quickly. Note: When now setting up a debug session in the Xilinx SDK or Eclipse, the host must be set to localhost instead of the IP address of the Q7S. +# TMTC testing + +The OBSW supports sending PUS TM packets via TCP or the PDEC IP Core which transmits the data as +CADU frames. To make the CADU frames receivabel by the +[TMTC porgram](https://egit.irs.uni-stuttgart.de/eive/eive-tmtc), a python script is running as +`systemd` service named `tmtc_bridge` on the flatsat PC which forwards TCP commands to the TCP +server of the OBC and reads CADU frames from a serial interface. + +You can check whether the service is running the following command on the flatsat PC + +```sh +systemctl status tmtc_bridge +``` + +The PUS packets transported with the CADU frames are extracted +and forwared to the TMTC program's TCP client. The code of the TMTC bridge can be found +[here](https://egit.irs.uni-stuttgart.de/eive/tmtc-bridge). To connect the TMTC program to the +TMTC-bridge a port forwarding from a host must be set up with the following command: + +```sh +ssh -L 1537:127.0.0.1:7100 eive@2001:7c0:2018:1099:babe:0:e1fe:f1a5 -t bash +``` + +You can print the output of the `systemd` service with + +```sh +journalctl -u tmtc_bridge +``` + +This can be helpful to determine whether any TCs arrive or TMs are coming back. + +Note: The encoding of the TM packets and conversion of CADU frames takes some time. +Thus the replies are received with a larger delay compared to a direct TCP connection. + # Direct Debugging 1. Assign static IP address to Q7S diff --git a/bsp_q7s/boardconfig/busConf.h b/bsp_q7s/boardconfig/busConf.h index 1149a54e..8905a125 100644 --- a/bsp_q7s/boardconfig/busConf.h +++ b/bsp_q7s/boardconfig/busConf.h @@ -52,6 +52,16 @@ namespace gpioNames { static constexpr char EN_RW_4[] = "enable_rw_4"; static constexpr char SPI_MUX_SELECT[] = "spi_mux_select"; static constexpr char RAD_SENSOR_CHIP_SELECT[] = "rad_sensor_chip_select"; + static constexpr char PAPB_BUSY_SIGNAL_VC0[] = "papb_busy_signal_vc0"; + static constexpr char PAPB_EMPTY_SIGNAL_VC0[] = "papb_empty_signal_vc0"; + static constexpr char PAPB_BUSY_SIGNAL_VC1[] = "papb_busy_signal_vc1"; + static constexpr char PAPB_EMPTY_SIGNAL_VC1[] = "papb_empty_signal_vc1"; + 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_BUSY_SIGNAL_VC3[] = "papb_busy_signal_vc3"; + static constexpr char PAPB_EMPTY_SIGNAL_VC3[] = "papb_empty_signal_vc3"; + static constexpr char RS485_EN_TX_CLOCK[] = "tx_clock_enable_ltc2872"; + static constexpr char RS485_EN_TX_DATA[] = "tx_data_enable_ltc2872"; } } diff --git a/bsp_q7s/core/InitMission.cpp b/bsp_q7s/core/InitMission.cpp index deda0ed8..1715cada 100644 --- a/bsp_q7s/core/InitMission.cpp +++ b/bsp_q7s/core/InitMission.cpp @@ -98,6 +98,15 @@ void initmission::initTasks() { } #endif +#if OBSW_USE_CCSDS_IP_CORE == 1 + PeriodicTaskIF* ccsdsHandlerTask = factory->createPeriodicTask( + "UDP_POLLING", 50, PeriodicTaskIF::MINIMUM_STACK_SIZE, 2.0, missedDeadlineFunc); + result = ccsdsHandlerTask->addComponent(objects::CCSDS_HANDLER); + if(result != HasReturnvaluesIF::RETURN_OK) { + initmission::printAddObjectError("CCSDS Handler", objects::CCSDS_HANDLER); + } +#endif /* OBSW_USE_CCSDS_IP_CORE == 1 */ + # if BOARD_TE0720 == 0 // FS task, task interval does not matter because it runs in permanent loop, priority low // because it is a non-essential background task @@ -147,6 +156,10 @@ void initmission::initTasks() { tmtcPollingTask->startTask(); #endif +#if OBSW_USE_CCSDS_IP_CORE == 1 + ccsdsHandlerTask->startTask(); +#endif /* OBSW_USE_CCSDS_IP_CORE == 1 */ + #if BOARD_TE0720 == 0 coreController->startTask(); #endif diff --git a/bsp_q7s/core/ObjectFactory.cpp b/bsp_q7s/core/ObjectFactory.cpp index b916977a..47b26a2a 100644 --- a/bsp_q7s/core/ObjectFactory.cpp +++ b/bsp_q7s/core/ObjectFactory.cpp @@ -1,8 +1,8 @@ #include - #include "ObjectFactory.h" #include "OBSWConfig.h" #include "devConf.h" +#include "ccsdsConfig.h" #include "busConf.h" #include "tmtc/apid.h" #include "devices/addresses.h" @@ -25,8 +25,6 @@ #include "linux/devices/SusHandler.h" #include "linux/csp/CspCookie.h" #include "linux/csp/CspComIF.h" -#include "linux/obc/CCSDSIPCoreBridge.h" - #include "mission/core/GenericFactory.h" #include "mission/devices/PDU1Handler.h" #include "mission/devices/PDU2Handler.h" @@ -50,6 +48,8 @@ #include "mission/devices/devicedefinitions/RwDefinitions.h" #include "mission/devices/devicedefinitions/StarTrackerDefinitions.h" #include "mission/devices/GPSHyperionHandler.h" +#include "mission/tmtc/CCSDSHandler.h" +#include "mission/tmtc/VirtualChannel.h" #include "mission/utility/TmFunnel.h" #include "fsfw_hal/linux/uart/UartComIF.h" @@ -75,6 +75,10 @@ #include "linux/boardtest/LibgpiodTest.h" #endif +#include +#include +#include + ResetArgs resetArgsGnss0; ResetArgs resetArgsGnss1; @@ -91,7 +95,12 @@ void Factory::setStaticFrameworkObjectIds() { //DeviceHandlerBase::powerSwitcherId = objects::PCDU_HANDLER; DeviceHandlerBase::powerSwitcherId = objects::NO_OBJECT; + +#if OBSW_TM_TO_PTME == 1 + TmFunnel::downlinkDestination = objects::CCSDS_HANDLER; +#else TmFunnel::downlinkDestination = objects::TMTC_BRIDGE; +#endif /* OBSW_TM_TO_PTME == 1 */ // No storage object for now. TmFunnel::storageDestination = objects::NO_OBJECT; @@ -164,6 +173,10 @@ void ObjectFactory::produce(void* args) { #endif /* TE7020 != 0 */ +#if OBSW_USE_CCSDS_IP_CORE == 1 + createCcsdsComponents(gpioComIF); +#endif /* OBSW_USE_CCSDS_IP_CORE == 1 */ + /* Test Task */ #if OBSW_ADD_TEST_CODE == 1 createTestComponents(gpioComIF); @@ -863,6 +876,85 @@ void ObjectFactory::createReactionWheelComponents(LinuxLibgpioIF* gpioComIF) { rw4SpiCookie->setCallbackArgs(rwHandler4); } +void ObjectFactory::createCcsdsComponents(LinuxLibgpioIF *gpioComIF) { + // GPIO definitions of signals connected to the virtual channel interfaces of the PTME IP Core + GpioCookie* gpioCookiePtmeIp = new GpioCookie; + GpiodRegularByLineName* gpio = nullptr; + std::stringstream consumer; + consumer << "0x" << std::hex << objects::PAPB_VC0; + gpio = new GpiodRegularByLineName(q7s::gpioNames::PAPB_BUSY_SIGNAL_VC0, consumer.str()); + gpioCookiePtmeIp->addGpio(gpioIds::VC0_PAPB_BUSY, gpio); + consumer.str(""); + consumer << "0x" << std::hex << objects::PAPB_VC0; + gpio = new GpiodRegularByLineName(q7s::gpioNames::PAPB_EMPTY_SIGNAL_VC0, consumer.str()); + gpioCookiePtmeIp->addGpio(gpioIds::VC0_PAPB_EMPTY, gpio); + consumer.str(""); + consumer << "0x" << std::hex << objects::PAPB_VC1; + gpio = new GpiodRegularByLineName(q7s::gpioNames::PAPB_BUSY_SIGNAL_VC1, consumer.str()); + gpioCookiePtmeIp->addGpio(gpioIds::VC1_PAPB_BUSY, gpio); + consumer.str(""); + consumer << "0x" << std::hex << objects::PAPB_VC1; + gpio = new GpiodRegularByLineName(q7s::gpioNames::PAPB_EMPTY_SIGNAL_VC1, consumer.str()); + gpioCookiePtmeIp->addGpio(gpioIds::VC1_PAPB_EMPTY, gpio); + consumer.str(""); + consumer << "0x" << std::hex << objects::PAPB_VC2; + gpio = new GpiodRegularByLineName(q7s::gpioNames::PAPB_BUSY_SIGNAL_VC2, consumer.str()); + gpioCookiePtmeIp->addGpio(gpioIds::VC2_PAPB_BUSY, gpio); + consumer.str(""); + consumer << "0x" << std::hex << objects::PAPB_VC2; + gpio = new GpiodRegularByLineName(q7s::gpioNames::PAPB_EMPTY_SIGNAL_VC2, consumer.str()); + gpioCookiePtmeIp->addGpio(gpioIds::VC2_PAPB_EMPTY, gpio); + consumer.str(""); + consumer << "0x" << std::hex << objects::PAPB_VC3; + gpio = new GpiodRegularByLineName(q7s::gpioNames::PAPB_BUSY_SIGNAL_VC3, consumer.str()); + gpioCookiePtmeIp->addGpio(gpioIds::VC3_PAPB_BUSY, gpio); + consumer.str(""); + consumer << "0x" << std::hex << objects::PAPB_VC3; + gpio = new GpiodRegularByLineName(q7s::gpioNames::PAPB_EMPTY_SIGNAL_VC3, consumer.str()); + gpioCookiePtmeIp->addGpio(gpioIds::VC3_PAPB_EMPTY, gpio); + + gpioComIF->addGpios(gpioCookiePtmeIp); + + // Creating virtual channel interfaces + VcInterfaceIF* vc0 = new PapbVcInterface(objects::PAPB_VC0, gpioComIF, gpioIds::VC0_PAPB_BUSY, + gpioIds::VC0_PAPB_EMPTY, PtmeConfig::VC0_OFFSETT); + VcInterfaceIF* vc1 = new PapbVcInterface(objects::PAPB_VC1, gpioComIF, gpioIds::VC1_PAPB_BUSY, + gpioIds::VC1_PAPB_EMPTY, PtmeConfig::VC1_OFFSETT); + VcInterfaceIF* vc2 = new PapbVcInterface(objects::PAPB_VC2, gpioComIF, gpioIds::VC2_PAPB_BUSY, + gpioIds::VC2_PAPB_EMPTY, PtmeConfig::VC2_OFFSETT); + VcInterfaceIF* vc3 = new PapbVcInterface(objects::PAPB_VC3, gpioComIF, gpioIds::VC3_PAPB_BUSY, + gpioIds::VC3_PAPB_EMPTY, PtmeConfig::VC3_OFFSETT); + + // Creating ptme object and adding virtual channel interfaces + Ptme* ptme = new Ptme(objects::PTME); + ptme->addVcInterface(ccsds::VC0, vc0); + ptme->addVcInterface(ccsds::VC1, vc1); + ptme->addVcInterface(ccsds::VC2, vc2); + ptme->addVcInterface(ccsds::VC3, vc3); + + CCSDSHandler* ccsdsHandler = new CCSDSHandler(objects::CCSDS_HANDLER, objects::PTME); + + VirtualChannel* vc = nullptr; + vc = new VirtualChannel(ccsds::VC0, config::VC0_QUEUE_SIZE); + ccsdsHandler->addVirtualChannel(ccsds::VC0, vc); + vc = new VirtualChannel(ccsds::VC1, config::VC1_QUEUE_SIZE); + ccsdsHandler->addVirtualChannel(ccsds::VC1, vc); + vc = new VirtualChannel(ccsds::VC2, config::VC2_QUEUE_SIZE); + ccsdsHandler->addVirtualChannel(ccsds::VC2, vc); + vc = new VirtualChannel(ccsds::VC3, config::VC3_QUEUE_SIZE); + ccsdsHandler->addVirtualChannel(ccsds::VC3, vc); + + GpioCookie* gpioRS485Chip = new GpioCookie; + gpio = new GpiodRegularByLineName(q7s::gpioNames::RS485_EN_TX_CLOCK, "RS485 Transceiver", + gpio::Direction::OUT, gpio::HIGH); + gpioRS485Chip->addGpio(gpioIds::RS485_EN_TX_CLOCK, gpio); + gpio = new GpiodRegularByLineName(q7s::gpioNames::RS485_EN_TX_DATA, "RS485 Transceiver", + gpio::Direction::OUT, gpio::HIGH); + gpioRS485Chip->addGpio(gpioIds::RS485_EN_TX_DATA, gpio); + + gpioComIF->addGpios(gpioRS485Chip); +} + void ObjectFactory::createTestComponents(LinuxLibgpioIF* gpioComIF) { #if BOARD_TE0720 == 0 @@ -901,10 +993,10 @@ void ObjectFactory::createTestComponents(LinuxLibgpioIF* gpioComIF) { #if BOARD_TE0720 == 1 && OBSW_TEST_CCSDS_BRIDGE == 1 GpioCookie* gpioCookieCcsdsIp = new GpioCookie; - GpiodRegular* papbBusyN = new GpiodRegular(std::string("gpiochip0"), 0, std::string("PAPBBusy_N")); + GpiodRegular* papbBusyN = new GpiodRegular(std::string("gpiochip0"), 0, std::string("PAPBBusy_VC0")); gpioCookieCcsdsIp->addGpio(gpioIds::PAPB_BUSY_N, papbBusyN); GpiodRegular* papbEmpty = new GpiodRegular(std::string("gpiochip0"), 1, - std::string("Chip Select Sus Sensor")); + std::string("PAPBEmpty_VC0")); gpioCookieCcsdsIp->addGpio(gpioIds::PAPB_EMPTY, papbEmpty); gpioComIF->addGpios(gpioCookieCcsdsIp); diff --git a/bsp_q7s/core/ObjectFactory.h b/bsp_q7s/core/ObjectFactory.h index ad9533a5..0e723142 100644 --- a/bsp_q7s/core/ObjectFactory.h +++ b/bsp_q7s/core/ObjectFactory.h @@ -22,6 +22,7 @@ void createSolarArrayDeploymentComponents(); void createSyrlinksComponents(); void createRtdComponents(LinuxLibgpioIF* gpioComIF); void createReactionWheelComponents(LinuxLibgpioIF* gpioComIF); +void createCcsdsComponents(LinuxLibgpioIF *gpioComIF); void createTestComponents(LinuxLibgpioIF* gpioComIF); }; diff --git a/common/config/ccsdsConfig.h b/common/config/ccsdsConfig.h new file mode 100644 index 00000000..8466a9fa --- /dev/null +++ b/common/config/ccsdsConfig.h @@ -0,0 +1,15 @@ +#ifndef COMMON_CONFIG_CCSDSCONFIG_H_ +#define COMMON_CONFIG_CCSDSCONFIG_H_ + +namespace ccsds { +enum { + VC0, + VC1, + VC2, + VC3 +}; +} + + + +#endif /* COMMON_CONFIG_CCSDSCONFIG_H_ */ diff --git a/common/config/commonClassIds.h b/common/config/commonClassIds.h index 5c8e263a..bddea49a 100644 --- a/common/config/commonClassIds.h +++ b/common/config/commonClassIds.h @@ -17,6 +17,7 @@ enum commonClassIds: uint8_t { PLOC_SUPERVISOR_HANDLER, //PLSV SUS_HANDLER, //SUSS CCSDS_IP_CORE_BRIDGE, //IPCI + PTME, //PTME PLOC_UPDATER, //PLUD GOM_SPACE_HANDLER, //GOMS PLOC_MEMORY_DUMPER, //PLMEMDUMP diff --git a/common/config/commonConfig.h.in b/common/config/commonConfig.h.in index 055f86aa..c8607fae 100644 --- a/common/config/commonConfig.h.in +++ b/common/config/commonConfig.h.in @@ -9,6 +9,13 @@ #define OBSW_ADD_TCPIP_BRIDGE 1 // Use TCP instead of UDP for the TMTC bridge. This allows using the TMTC client locally // because UDP packets are not allowed in the VPN +// This will cause the OBSW to initialize the TMTC bridge responsible for exchanging data with the +// CCSDS IP Cores. +#define OBSW_USE_CCSDS_IP_CORE 1 +// Set to 1 if all telemetry should be sent to the PTME IP Core +#define OBSW_TM_TO_PTME 1 +// Set to 1 if telecommands are received via the PDEC IP Core +#define OBSW_TC_FROM_PDEC 0 #define OBSW_USE_TCP_BRIDGE 1 namespace common { diff --git a/common/config/commonObjects.h b/common/config/commonObjects.h index e44e0a1a..43cd396c 100644 --- a/common/config/commonObjects.h +++ b/common/config/commonObjects.h @@ -11,6 +11,12 @@ enum commonObjects: uint32_t { TMTC_BRIDGE = 0x50000300, TMTC_POLLING_TASK = 0x50000400, FILE_SYSTEM_HANDLER = 0x50000500, + PTME = 0x50000600, + PAPB_VC0 = 0x50000700, + PAPB_VC1 = 0x50000701, + PAPB_VC2 = 0x50000702, + PAPB_VC3 = 0x50000703, + CCSDS_HANDLER = 0x50000800, /* 0x43 ('C') for Controllers */ THERMAL_CONTROLLER = 0x43400001, diff --git a/linux/obc/CCSDSIPCoreBridge.cpp b/linux/archive/tmtc/CCSDSIPCoreBridge.cpp similarity index 98% rename from linux/obc/CCSDSIPCoreBridge.cpp rename to linux/archive/tmtc/CCSDSIPCoreBridge.cpp index fe5a7007..982f7fc2 100644 --- a/linux/obc/CCSDSIPCoreBridge.cpp +++ b/linux/archive/tmtc/CCSDSIPCoreBridge.cpp @@ -1,7 +1,7 @@ #include #include -#include +#include CCSDSIPCoreBridge::CCSDSIPCoreBridge(object_id_t objectId, object_id_t tcDestination, object_id_t tmStoreId, object_id_t tcStoreId, LinuxLibgpioIF* gpioComIF, diff --git a/linux/obc/CCSDSIPCoreBridge.h b/linux/archive/tmtc/CCSDSIPCoreBridge.h similarity index 100% rename from linux/obc/CCSDSIPCoreBridge.h rename to linux/archive/tmtc/CCSDSIPCoreBridge.h diff --git a/linux/fsfwconfig/OBSWConfig.h.in b/linux/fsfwconfig/OBSWConfig.h.in index 4feb45e2..924026af 100644 --- a/linux/fsfwconfig/OBSWConfig.h.in +++ b/linux/fsfwconfig/OBSWConfig.h.in @@ -86,6 +86,7 @@ debugging. */ #define OBSW_TEST_TE7020_HEATER 0 #define OBSW_TEST_GPIO_OPEN_BY_LABEL 0 #define OBSW_TEST_GPIO_OPEN_BY_LINE_NAME 0 +#define OBSW_LINK_IS_UP 1 #define OBSW_DEBUG_P60DOCK 0 #define OBSW_DEBUG_PDU1 0 @@ -123,6 +124,14 @@ namespace config { /* Add mission configuration flags here */ static constexpr uint32_t OBSW_FILESYSTEM_HANDLER_QUEUE_SIZE = 50; static constexpr uint32_t PLOC_UPDATER_QUEUE_SIZE = 50; +static constexpr uint32_t CCSDS_HANDLER_QUEUE_SIZE = 50; +static constexpr uint8_t NUMBER_OF_VIRTUAL_CHANNELS = 4; +static constexpr uint8_t VC0_QUEUE_SIZE = 50; +static constexpr uint8_t VC1_QUEUE_SIZE = 50; +static constexpr uint8_t VC2_QUEUE_SIZE = 50; +static constexpr uint8_t VC3_QUEUE_SIZE = 50; + +static constexpr uint8_t LIVE_TM = 0; #ifdef __cplusplus } diff --git a/linux/fsfwconfig/devices/gpioIds.h b/linux/fsfwconfig/devices/gpioIds.h index b450bf8b..5f09e5f6 100644 --- a/linux/fsfwconfig/devices/gpioIds.h +++ b/linux/fsfwconfig/devices/gpioIds.h @@ -89,7 +89,20 @@ enum gpioId_t { EN_RW_CS, - SPI_MUX + SPI_MUX, + + VC0_PAPB_EMPTY, + VC0_PAPB_BUSY, + VC1_PAPB_EMPTY, + VC1_PAPB_BUSY, + VC2_PAPB_EMPTY, + VC2_PAPB_BUSY, + VC3_PAPB_EMPTY, + VC3_PAPB_BUSY, + + + RS485_EN_TX_DATA, + RS485_EN_TX_CLOCK }; } diff --git a/linux/obc/CMakeLists.txt b/linux/obc/CMakeLists.txt index 79d9ba9b..315a0d33 100644 --- a/linux/obc/CMakeLists.txt +++ b/linux/obc/CMakeLists.txt @@ -1,5 +1,6 @@ target_sources(${TARGET_NAME} PUBLIC - CCSDSIPCoreBridge.cpp + PapbVcInterface.cpp + Ptme.cpp ) diff --git a/linux/obc/PapbVcInterface.cpp b/linux/obc/PapbVcInterface.cpp new file mode 100644 index 00000000..5bb72066 --- /dev/null +++ b/linux/obc/PapbVcInterface.cpp @@ -0,0 +1,104 @@ +#include +#include "fsfw/serviceinterface/ServiceInterface.h" + +PapbVcInterface::PapbVcInterface(object_id_t objectId, LinuxLibgpioIF* gpioComIF, + gpioId_t papbBusyId, gpioId_t papbEmptyId, uint32_t vcOffset) : + SystemObject(objectId), gpioComIF(gpioComIF), papbBusyId( + papbBusyId), papbEmptyId(papbEmptyId), vcOffset(vcOffset) { +} + +PapbVcInterface::~PapbVcInterface() { +} + + +void PapbVcInterface::setRegisterAddress(uint32_t* ptmeBaseAddress) { + vcBaseReg = ptmeBaseAddress + vcOffset; +} + +ReturnValue_t PapbVcInterface::write(const uint8_t * data, size_t size) { + + if(pollPapbBusySignal() == RETURN_OK) { + startPacketTransfer(); + } + + for(size_t idx = 0; idx < size; idx++) { + if(pollPapbBusySignal() == RETURN_OK) { + *(vcBaseReg + DATA_REG_OFFSET) = static_cast(*(data + idx)); + } + else { + sif::warning << "PapbVcInterface::write: Only written " << idx << " of " + << size << " data" << std::endl; + return RETURN_FAILED; + } + } + + if(pollPapbBusySignal() == RETURN_OK) { + endPacketTransfer(); + } + return RETURN_OK; +} + +void PapbVcInterface::startPacketTransfer() { + *vcBaseReg = CONFIG_START; +} + +void PapbVcInterface::endPacketTransfer() { + *vcBaseReg = CONFIG_END; +} + +ReturnValue_t PapbVcInterface::pollPapbBusySignal() { + int papbBusyState = 0; + ReturnValue_t result = RETURN_OK; + + /** Check if PAPB interface is ready to receive data */ + result = gpioComIF->readGpio(papbBusyId, &papbBusyState); + if (result != RETURN_OK) { + sif::warning << "PapbVcInterface::pollPapbBusySignal: Failed to read papb busy signal" + << std::endl; + return RETURN_FAILED; + } + if (!papbBusyState) { + sif::warning << "PapbVcInterface::pollPapbBusySignal: PAPB busy" << std::endl; + return PAPB_BUSY; + } + + return RETURN_OK; +} + +void PapbVcInterface::isVcInterfaceBufferEmpty() { + ReturnValue_t result = RETURN_OK; + int papbEmptyState = 1; + + result = gpioComIF->readGpio(papbEmptyId, &papbEmptyState); + + if (result != RETURN_OK) { + sif::warning << "PapbVcInterface::isVcInterfaceBufferEmpty: Failed to read papb empty signal" + << std::endl; + return; + } + + if (papbEmptyState == 1) { + sif::debug << "PapbVcInterface::isVcInterfaceBufferEmpty: Buffer is empty" << std::endl; + } + else { + sif::debug << "PapbVcInterface::isVcInterfaceBufferEmpty: Buffer is not empty" << std::endl; + } + return; +} + +ReturnValue_t PapbVcInterface::sendTestFrame() { + /** Size of one complete transfer frame data field amounts to 1105 bytes */ + uint8_t testPacket[1105]; + + /** Fill one test packet */ + for(int idx = 0; idx < 1105; idx++) { + testPacket[idx] = static_cast(idx & 0xFF); + } + + ReturnValue_t result = write(testPacket, 1105); + if(result != RETURN_OK) { + return result; + } + + return RETURN_OK; +} diff --git a/linux/obc/PapbVcInterface.h b/linux/obc/PapbVcInterface.h new file mode 100644 index 00000000..937f4280 --- /dev/null +++ b/linux/obc/PapbVcInterface.h @@ -0,0 +1,112 @@ +#ifndef LINUX_OBC_PAPBVCINTERFACE_H_ +#define LINUX_OBC_PAPBVCINTERFACE_H_ + +#include "OBSWConfig.h" +#include "linux/obc/VcInterfaceIF.h" +#include +#include +#include "fsfw/returnvalues/HasReturnvaluesIF.h" +#include "fsfw/objectmanager/ObjectManager.h" + +/** + * @brief This class handles the transmission of data to a virtual channel of the PTME IP Core + * via the PAPB interface. + * + * @author J. Meier + */ +class PapbVcInterface: public SystemObject, + public VcInterfaceIF, + public HasReturnvaluesIF { +public: + /** + * @brief Constructor + * + * @param objectId + * @param papbBusyId The ID of the GPIO which is connected to the PAPBBusy_N signal of the + * VcInterface IP Core. A low logic level indicates the VcInterface is not + * ready to receive more data. + * @param papbEmptyId The ID of the GPIO which is connected to the PAPBEmpty signal of the + * VcInterface IP Core. The signal is high when there are no packets in the + * external buffer memory (BRAM). + */ + PapbVcInterface(object_id_t objectId, LinuxLibgpioIF* gpioComIF, gpioId_t papbBusyId, + gpioId_t papbEmptyId, uint32_t vcOffset); + virtual ~PapbVcInterface(); + + ReturnValue_t write(const uint8_t* data, size_t size) override; + + void setRegisterAddress(uint32_t* ptmeBaseAddress) override; + +private: + + static const uint8_t INTERFACE_ID = CLASS_ID::CCSDS_IP_CORE_BRIDGE; + + static const ReturnValue_t PAPB_BUSY = MAKE_RETURN_CODE(0xA0); + + /** + * Configuration bits: + * bit[1:0]: Size of data (1,2,3 or 4 bytes). 1 Byte <=> b00 + * bit[2]: Set this bit to 1 to abort a transfered packet + * bit[3]: Signals to VcInterface the start of a new telemetry packet + */ + static const uint32_t CONFIG_START = 0x8; + + /** + * Writing this word to the VcInterface base address signals to the virtual channel interface + * that a complete tm packet has been transferred. + */ + static const uint32_t CONFIG_END = 0x0; + + /** + * Writing to this offset within the memory space of a virtual channel will insert data for + * encoding to the external buffer memory of the PTME IP Core. + * The address offset is 0x400 (= 4 * 256) + */ + static const int DATA_REG_OFFSET = 256; + + LinuxLibgpioIF* gpioComIF = nullptr; + + /** Pulled to low when virtual channel not ready to receive data */ + gpioId_t papbBusyId = gpio::NO_GPIO; + /** High when external buffer memory of virtual channel is empty */ + gpioId_t papbEmptyId = gpio::NO_GPIO; + + uint32_t* vcBaseReg = nullptr; + + uint32_t vcOffset = 0; + + /** + * @brief This function sends the config byte to the virtual channel of the PTME IP Core + * to initiate a packet transfer. + */ + void startPacketTransfer(); + + /** + * @brief This function sends the config byte to the virtual channel interface of the PTME + * IP Core to signal the end of a packet transfer. + */ + void endPacketTransfer(); + + /** + * @brief This function reads the papb busy signal indicating whether the virtual channel + * interface is ready to receive more data or not. PAPB is ready when + * PAPB_Busy_N == '1'. + * + * @return RETURN_OK when ready to receive data else PAPB_BUSY. + */ + ReturnValue_t pollPapbBusySignal(); + + /** + * @brief This function can be used for debugging to check whether there are packets in + * the packet buffer of the virtual channel or not. + */ + void isVcInterfaceBufferEmpty(); + + /** + * @brief This function sends a complete telemetry transfer frame data field (1105 bytes) + * to the papb interface of the PTME IP Core. Can be used to test the implementation. + */ + ReturnValue_t sendTestFrame(); +}; + +#endif /* LINUX_OBC_PAPBVCINTERFACE_H_ */ diff --git a/linux/obc/Ptme.cpp b/linux/obc/Ptme.cpp new file mode 100644 index 00000000..80f9945c --- /dev/null +++ b/linux/obc/Ptme.cpp @@ -0,0 +1,73 @@ +#include +#include + +#include +#include "fsfw/serviceinterface/ServiceInterface.h" +#include "PtmeConfig.h" + +Ptme::Ptme(object_id_t objectId) : + SystemObject(objectId) { +} + +Ptme::~Ptme() { +} + +ReturnValue_t Ptme::initialize() { + + int fd = open(PtmeConfig::UIO_DEVICE_FILE, O_RDWR); + if (fd < 1) { + sif::warning << "Ptme::initialize: Invalid UIO device file" << std::endl; + return RETURN_FAILED; + } + + /** + * Map uio device in virtual address space + * PROT_WRITE: Map uio device in writable only mode + */ + ptmeBaseAddress = static_cast(mmap(NULL, MAP_SIZE, PROT_WRITE, + MAP_SHARED, fd, 0)); + + if (ptmeBaseAddress == MAP_FAILED) { + sif::error << "Ptme::initialize: Failed to map uio address" << std::endl; + return RETURN_FAILED; + } + + VcInterfaceMapIter iter; + for (iter = vcInterfaceMap.begin(); iter != vcInterfaceMap.end(); iter++) { + iter->second->setRegisterAddress(ptmeBaseAddress); + } + + return RETURN_OK; +} + +ReturnValue_t Ptme::writeToVc(uint8_t vcId, const uint8_t * data, size_t size) { + ReturnValue_t result = RETURN_OK; + VcInterfaceMapIter vcInterfaceMapIter = vcInterfaceMap.find(vcId); + if (vcInterfaceMapIter == vcInterfaceMap.end()) { + sif::warning << "Ptme::writeToVc: No virtual channel interface found for the virtual " + "channel with id " << static_cast(vcId) << std::endl; + return UNKNOWN_VC_ID; + } + result = vcInterfaceMapIter->second->write(data, size); + return result; +} + +void Ptme::addVcInterface(VcId_t vcId, VcInterfaceIF* vc) { + + if (vcId > config::NUMBER_OF_VIRTUAL_CHANNELS) { + sif::warning << "Ptme::addVcInterface: Invalid virtual channel ID" << std::endl; + return; + } + + if (vc == nullptr) { + sif::warning << "Ptme::addVcInterface: Invalid virtual channel interface" << std::endl; + return; + } + + auto status = vcInterfaceMap.emplace(vcId, vc); + if (status.second == false) { + sif::warning << "Ptme::addVcInterface: Failed to add virtual channel interface to " + "virtual channel map" << std::endl; + return; + } +} diff --git a/linux/obc/Ptme.h b/linux/obc/Ptme.h new file mode 100644 index 00000000..be687e17 --- /dev/null +++ b/linux/obc/Ptme.h @@ -0,0 +1,91 @@ +#ifndef LINUX_OBC_PTME_H_ +#define LINUX_OBC_PTME_H_ + +#include "OBSWConfig.h" +#include "linux/obc/PtmeIF.h" +#include "linux/obc/VcInterfaceIF.h" +#include +#include +#include "fsfw/returnvalues/HasReturnvaluesIF.h" + +#include +#include + +/** + * @brief This class handles the interfacing to the telemetry (PTME) IP core responsible for the + * encoding of telemetry packets according to the CCSDS standards CCSDS 131.0-B-3 (TM Synchro- + * nization and channel coding) and CCSDS 132.0-B-2 (TM Space Data Link Protocoll). + * The IP cores are implemented on the programmable logic and are accessible through the + * linux UIO driver. + */ +class Ptme : public PtmeIF, + public SystemObject, + public HasReturnvaluesIF { +public: + + using VcId_t = uint8_t; + + /** + * @brief Constructor + * + * @param objectId + */ + Ptme(object_id_t objectId); + virtual ~Ptme(); + + ReturnValue_t initialize() override; + ReturnValue_t writeToVc(uint8_t vcId, const uint8_t* data, size_t size) override; + + /** + * @brief This function adds the reference to a virtual channel interface to the vcInterface + * map. + */ + void addVcInterface(VcId_t vcId, VcInterfaceIF* vc); + +private: + + static const uint8_t INTERFACE_ID = CLASS_ID::PTME; + + static const ReturnValue_t UNKNOWN_VC_ID = MAKE_RETURN_CODE(0xA0); + +#if BOARD_TE0720 == 1 + /** Size of mapped address space */ + static const int MAP_SIZE = 0x40000; +#else + /** Size of mapped address space */ + static const int MAP_SIZE = 0x40000; +#endif /* BOARD_TE0720 == 1 */ + + /** + * Configuration bits: + * bit[1:0]: Size of data (1,2,3 or 4 bytes). 1 Byte <=> b00 + * bit[2]: Set this bit to 1 to abort a transfered packet + * bit[3]: Signals to PTME the start of a new telemetry packet + */ + static const uint32_t PTME_CONFIG_START = 0x8; + + /** + * Writing this word to the ptme base address signals to the PTME that a complete tm packet has + * been transferred. + */ + static const uint32_t PTME_CONFIG_END = 0x0; + + /** + * Writing to this offset within the PTME memory space will insert data for encoding to the + * PTME IP core. + * The address offset is 0x400 (= 4 * 256) + */ + static const int PTME_DATA_REG_OFFSET = 256; + + /** The file descriptor of the UIO driver */ + int fd; + + uint32_t* ptmeBaseAddress = nullptr; + + using VcInterfaceMap = std::unordered_map; + using VcInterfaceMapIter = VcInterfaceMap::iterator; + + VcInterfaceMap vcInterfaceMap; +}; + +#endif /* LINUX_OBC_PTME_H_ */ diff --git a/linux/obc/PtmeConfig.h b/linux/obc/PtmeConfig.h new file mode 100644 index 00000000..f4468570 --- /dev/null +++ b/linux/obc/PtmeConfig.h @@ -0,0 +1,26 @@ +#ifndef LINUX_OBC_PTMECONFIG_H_ +#define LINUX_OBC_PTMECONFIG_H_ + +#include "fsfw/returnvalues/HasReturnvaluesIF.h" +#include + + +/** + * @brief Configuration parameters derived from FPGA design and device tree. + * + * @author J. Meier + */ +namespace PtmeConfig { + /** + * Offset of virtual channels mapped into address space + * 0x10000 = (0x4000 * 4) + */ + static const uint32_t VC0_OFFSETT = 0; + static const uint32_t VC1_OFFSETT = 0x4000; + static const uint32_t VC2_OFFSETT = 0x8000; + static const uint32_t VC3_OFFSETT = 0xC000; + + static const char UIO_DEVICE_FILE[] = "/dev/uio0"; +}; + +#endif /* LINUX_OBC_PTMECONFIG_H_ */ diff --git a/linux/obc/PtmeIF.h b/linux/obc/PtmeIF.h new file mode 100644 index 00000000..53d9498b --- /dev/null +++ b/linux/obc/PtmeIF.h @@ -0,0 +1,28 @@ +#ifndef LINUX_OBC_PTMEIF_H_ +#define LINUX_OBC_PTMEIF_H_ + +#include "fsfw/returnvalues/HasReturnvaluesIF.h" + + +/** + * @brief Interface class for managing the PTME IP Core implemented in the programmable logic. + * + * @details PTME IP Core: https://www.esa.int/Enabling_Support/Space_Engineering_Technology/ + * Microelectronics/PTME + * @author J. Meier + */ +class PtmeIF { +public: + virtual ~PtmeIF(){}; + + /** + * @brief Implements to function to write to a specific virtual channel. + * + * @param vcId Virtual channel to write to + * @param data Pointer to buffer holding the data to write + * @param size Number of bytes to write + */ + virtual ReturnValue_t writeToVc(uint8_t vcId, const uint8_t* data, size_t size) = 0; +}; + +#endif /* LINUX_OBC_PTMEIF_H_ */ diff --git a/linux/obc/VcInterfaceIF.h b/linux/obc/VcInterfaceIF.h new file mode 100644 index 00000000..28b8d1f1 --- /dev/null +++ b/linux/obc/VcInterfaceIF.h @@ -0,0 +1,29 @@ +#ifndef LINUX_OBC_VCINTERFACEIF_H_ +#define LINUX_OBC_VCINTERFACEIF_H_ + +#include +#include "fsfw/returnvalues/HasReturnvaluesIF.h" + +/** + * @brief Interface class for managing different virtual channels of the PTME IP core implemented + * in the programmable logic. + * + * @author J. Meier + */ +class VcInterfaceIF { +public: + virtual ~VcInterfaceIF(){}; + + /** + * @brief Implememts the functionality to write data in the virtual channel of the PTME IP + * Core. + * + * @param data Pointer to buffer holding the data to write + * @param size Number of bytes to write + */ + virtual ReturnValue_t write(const uint8_t* data, size_t size) = 0; + + virtual void setRegisterAddress(uint32_t* ptmeBaseAddress) = 0; +}; + +#endif /* LINUX_OBC_VCINTERFACEIF_H_ */ diff --git a/mission/CMakeLists.txt b/mission/CMakeLists.txt index 46f562a5..876201a8 100644 --- a/mission/CMakeLists.txt +++ b/mission/CMakeLists.txt @@ -2,3 +2,4 @@ add_subdirectory(core) add_subdirectory(devices) add_subdirectory(utility) add_subdirectory(memory) +add_subdirectory(tmtc) diff --git a/mission/tmtc/CCSDSHandler.cpp b/mission/tmtc/CCSDSHandler.cpp new file mode 100644 index 00000000..95d557ee --- /dev/null +++ b/mission/tmtc/CCSDSHandler.cpp @@ -0,0 +1,122 @@ +#include "fsfw/serviceinterface/ServiceInterface.h" +#include "fsfw/serviceinterface/serviceInterfaceDefintions.h" +#include "fsfw/objectmanager/ObjectManager.h" +#include "fsfw/ipc/QueueFactory.h" + +#include "CCSDSHandler.h" + +CCSDSHandler::CCSDSHandler(object_id_t objectId, object_id_t ptmeId) : + SystemObject(objectId), ptmeId(ptmeId), parameterHelper(this) { + commandQueue = QueueFactory::instance()->createMessageQueue(QUEUE_SIZE); +} + +CCSDSHandler::~CCSDSHandler() { +} + +ReturnValue_t CCSDSHandler::performOperation(uint8_t operationCode) { + readCommandQueue(); + handleTelemetry(); + handleTelecommands(); + return RETURN_OK; +} + +void CCSDSHandler::handleTelemetry() { + VirtualChannelMapIter iter; + for (iter = virtualChannelMap.begin(); iter != virtualChannelMap.end(); iter++) { + iter->second->performOperation(); + } +} + +void CCSDSHandler::handleTelecommands() { + +} + +ReturnValue_t CCSDSHandler::initialize() { + ReturnValue_t result = RETURN_OK; + PtmeIF* ptme = ObjectManager::instance()->get(ptmeId); + if (ptme == nullptr) { + sif::warning << "Invalid PTME object" << std::endl; + return ObjectManagerIF::CHILD_INIT_FAILED; + } + + result = parameterHelper.initialize(); + if (result != HasReturnvaluesIF::RETURN_OK) { + return result; + } + + VirtualChannelMapIter iter; + for (iter = virtualChannelMap.begin(); iter != virtualChannelMap.end(); iter++) { + result = iter->second->initialize(); + if (result != RETURN_OK) { + return result; + } + iter->second->setPtmeObject(ptme); + } + + return result; +} + +void CCSDSHandler::readCommandQueue(void) { + CommandMessage commandMessage; + ReturnValue_t result = RETURN_FAILED; + + result = commandQueue->receiveMessage(&commandMessage); + if (result == RETURN_OK) { + result = parameterHelper.handleParameterMessage(&commandMessage); + if (result == RETURN_OK) { + return; + } + CommandMessage reply; + reply.setReplyRejected(CommandMessage::UNKNOWN_COMMAND, + commandMessage.getCommand()); + commandQueue->reply(&reply); + } +} + +MessageQueueId_t CCSDSHandler::getCommandQueue() const { + return commandQueue->getId(); +} + +void CCSDSHandler::addVirtualChannel(VcId_t vcId, VirtualChannel* virtualChannel) { + if (vcId > config::NUMBER_OF_VIRTUAL_CHANNELS) { + sif::warning << "CCSDSHandler::addVirtualChannel: Invalid virtual channel ID" << std::endl; + return; + } + + if (virtualChannel == nullptr) { + sif::warning << "CCSDSHandler::addVirtualChannel: Invalid virtual channel interface" << std::endl; + return; + } + + auto status = virtualChannelMap.emplace(vcId, virtualChannel); + if (status.second == false) { + sif::warning << "CCSDSHandler::addVirtualChannel: Failed to add virtual channel to " + "virtual channel map" << std::endl; + return; + } +} + +MessageQueueId_t CCSDSHandler::getReportReceptionQueue(uint8_t virtualChannel) { + if (virtualChannel < config::NUMBER_OF_VIRTUAL_CHANNELS) { + VirtualChannelMapIter iter = virtualChannelMap.find(virtualChannel); + if (iter != virtualChannelMap.end()) { + return iter->second->getReportReceptionQueue(); + } + else { + sif::warning << "CCSDSHandler::getReportReceptionQueue: Virtual channel with ID " + << static_cast(virtualChannel) << " not in virtual channel map" + << std::endl; + return MessageQueueIF::NO_QUEUE; + } + } else { + sif::debug << "CCSDSHandler::getReportReceptionQueue: Invalid virtual channel requested"; + + } + return MessageQueueIF::NO_QUEUE; +} + +ReturnValue_t CCSDSHandler::getParameter(uint8_t domainId, uint8_t uniqueIdentifier, + ParameterWrapper *parameterWrapper, const ParameterWrapper *newValues, + uint16_t startAtIndex) { + return RETURN_OK; +} diff --git a/mission/tmtc/CCSDSHandler.h b/mission/tmtc/CCSDSHandler.h new file mode 100644 index 00000000..05693a38 --- /dev/null +++ b/mission/tmtc/CCSDSHandler.h @@ -0,0 +1,75 @@ +#ifndef CCSDSHANDLER_H_ +#define CCSDSHANDLER_H_ + +#include "OBSWConfig.h" +#include "fsfw/objectmanager/SystemObject.h" +#include "fsfw/tasks/ExecutableObjectIF.h" +#include "fsfw/returnvalues/HasReturnvaluesIF.h" +#include "fsfw/parameters/ParameterHelper.h" +#include "VirtualChannel.h" +#include + +/** + * @brief This class handles the data exchange with the CCSDS IP cores implemented in the + * programmable logic of the Q7S. + * + * @author J. Meier + */ +class CCSDSHandler: public SystemObject, + public ExecutableObjectIF, + public AcceptsTelemetryIF, + public HasReturnvaluesIF, + public ReceivesParameterMessagesIF { +public: + + using VcId_t = uint8_t; + + /** + * @brief Constructor + * + * @param objectId Object ID of the CCSDS handler + * @param ptmeId Object ID of the PTME object providing access to the PTME IP Core. + */ + CCSDSHandler(object_id_t objectId, object_id_t ptmeId); + + ~CCSDSHandler(); + + ReturnValue_t performOperation(uint8_t operationCode = 0) override; + ReturnValue_t initialize(); + MessageQueueId_t getCommandQueue() const; + + /** + * @brief Function to add a virtual channel + * + * @param virtualChannelId ID of the virtual channel to add + * @param virtualChannel Pointer to virtual channel object + */ + void addVirtualChannel(VcId_t virtualChannelId, VirtualChannel* virtualChannel); + + MessageQueueId_t getReportReceptionQueue(uint8_t virtualChannel = 0); + ReturnValue_t getParameter(uint8_t domainId, uint8_t uniqueIdentifier, + ParameterWrapper *parameterWrapper, const ParameterWrapper *newValues, + uint16_t startAtIndex); + +private: + + static const uint32_t QUEUE_SIZE = config::CCSDS_HANDLER_QUEUE_SIZE; + + using VirtualChannelMap = std::unordered_map; + using VirtualChannelMapIter = VirtualChannelMap::iterator; + + VirtualChannelMap virtualChannelMap; + + // Object ID of PTME object + object_id_t ptmeId; + + MessageQueueIF* commandQueue = nullptr; + + ParameterHelper parameterHelper; + + void readCommandQueue(void); + void handleTelemetry(); + void handleTelecommands(); +}; + +#endif /* CCSDSHANDLER_H_ */ diff --git a/mission/tmtc/CMakeLists.txt b/mission/tmtc/CMakeLists.txt new file mode 100644 index 00000000..7da87b6c --- /dev/null +++ b/mission/tmtc/CMakeLists.txt @@ -0,0 +1,6 @@ +target_sources(${TARGET_NAME} PUBLIC + CCSDSHandler.cpp + VirtualChannel.cpp +) + + diff --git a/mission/tmtc/VirtualChannel.cpp b/mission/tmtc/VirtualChannel.cpp new file mode 100644 index 00000000..3423692b --- /dev/null +++ b/mission/tmtc/VirtualChannel.cpp @@ -0,0 +1,69 @@ +#include "CCSDSHandler.h" +#include "VirtualChannel.h" +#include "OBSWConfig.h" + +#include "fsfw/serviceinterface/ServiceInterfaceStream.h" +#include "fsfw/objectmanager/ObjectManager.h" +#include "fsfw/tmtcservices/TmTcMessage.h" +#include "fsfw/ipc/QueueFactory.h" + + +VirtualChannel::VirtualChannel(uint8_t vcId, uint32_t tmQueueDepth) : + vcId(vcId) { + tmQueue = QueueFactory::instance()->createMessageQueue(tmQueueDepth, + MessageQueueMessage::MAX_MESSAGE_SIZE); +} + +ReturnValue_t VirtualChannel::initialize() { + tmStore = ObjectManager::instance()->get(objects::TM_STORE); + if(tmStore == nullptr) { + sif::error << "VirtualChannel::initialize: Failed to get tm store" << std::endl; + return RETURN_FAILED; + } + return RETURN_OK; +} + +ReturnValue_t VirtualChannel::performOperation() { + ReturnValue_t result = RETURN_OK; + TmTcMessage message; + + while(tmQueue->receiveMessage(&message) == RETURN_OK) { + store_address_t storeId = message.getStorageId(); + const uint8_t* data = nullptr; + size_t size = 0; + result = tmStore->getData(storeId, &data, &size); + if (result != RETURN_OK) { + sif::warning << "VirtualChannel::performOperation: Failed to read data from IPC store" + << std::endl; + tmStore->deleteData(storeId); + return result; + } + + if (linkIsUp) { + result = ptme->writeToVc(vcId, data, size); + } + + tmStore->deleteData(storeId); + + if (result != RETURN_OK) { + return result; + } + } + return result; +} + +MessageQueueId_t VirtualChannel::getReportReceptionQueue(uint8_t virtualChannel) { + return tmQueue->getId(); +} + +void VirtualChannel::setPtmeObject(PtmeIF* ptme_) { + if (ptme_ == nullptr) { + sif::warning << "VirtualChannel::setPtmeObject: Invalid ptme object" << std::endl; + return; + } + ptme = ptme_; +} + +void VirtualChannel::setLinkState(bool linkIsUp) { + linkIsUp = linkIsUp; +} diff --git a/mission/tmtc/VirtualChannel.h b/mission/tmtc/VirtualChannel.h new file mode 100644 index 00000000..d7d95cb7 --- /dev/null +++ b/mission/tmtc/VirtualChannel.h @@ -0,0 +1,58 @@ +#ifndef VIRTUALCHANNEL_H_ +#define VIRTUALCHANNEL_H_ + +#include "OBSWConfig.h" +#include "fsfw/tmtcservices/AcceptsTelemetryIF.h" +#include "fsfw/returnvalues/HasReturnvaluesIF.h" +#include +#include + +/** + * @brief This class represents a virtual channel. Sending a tm message to an object of this class + * will forward the tm packet to the respective virtual channel of the PTME IP Core. + * + * @author J. Meier + */ +class VirtualChannel: public AcceptsTelemetryIF, public HasReturnvaluesIF { + public: + /** + * @brief Constructor + * + * @param vcId The virtual channel id assigned to this object + * @param tmQueueDepth Queue depth of queue receiving telemetry from other objects + */ + VirtualChannel(uint8_t vcId, uint32_t tmQueueDepth); + + ReturnValue_t initialize(); + MessageQueueId_t getReportReceptionQueue(uint8_t virtualChannel = 0) override; + ReturnValue_t performOperation(); + + /** + * @brief Sets the PTME object which handles access to the PTME IP Core. + * + * @param ptme Pointer to ptme object + */ + void setPtmeObject(PtmeIF* ptme_); + + /** + * @brief Can be used by the owner to set the link state. Packets will be discarded if link + * to ground station is down. + */ + void setLinkState(bool linkIsUp); + +private: + + PtmeIF* ptme = nullptr; + MessageQueueIF* tmQueue = nullptr; + uint8_t vcId; + +#if OBSW_LINK_IS_UP == 1 + bool linkIsUp = true; +#else + bool linkIsUp = false; +#endif /* OBSW_LINK_IS_UP == 1 */ + + StorageManagerIF* tmStore = nullptr; +}; + +#endif /* VIRTUALCHANNEL_H_ */ diff --git a/mission/utility/TmFunnel.cpp b/mission/utility/TmFunnel.cpp index c52fa6f9..4b9eebdf 100644 --- a/mission/utility/TmFunnel.cpp +++ b/mission/utility/TmFunnel.cpp @@ -1,3 +1,4 @@ +#include "OBSWConfig.h" #include #include #include @@ -45,7 +46,7 @@ ReturnValue_t TmFunnel::performOperation(uint8_t operationCode) { ReturnValue_t TmFunnel::handlePacket(TmTcMessage* message) { uint8_t* packetData = nullptr; size_t size = 0; - ReturnValue_t result = tmPool->modifyData(message->getStorageId(), + ReturnValue_t result = tmStore->modifyData(message->getStorageId(), &packetData, &size); if(result != HasReturnvaluesIF::RETURN_OK){ return result; @@ -59,7 +60,7 @@ ReturnValue_t TmFunnel::handlePacket(TmTcMessage* message) { result = tmQueue->sendToDefault(message); if(result != HasReturnvaluesIF::RETURN_OK){ - tmPool->deleteData(message->getStorageId()); + tmStore->deleteData(message->getStorageId()); sif::error << "TmFunnel::handlePacket: Error sending to downlink " "handler" << std::endl; return result; @@ -68,7 +69,7 @@ ReturnValue_t TmFunnel::handlePacket(TmTcMessage* message) { if(storageDestination != objects::NO_OBJECT) { result = storageQueue->sendToDefault(message); if(result != HasReturnvaluesIF::RETURN_OK){ - tmPool->deleteData(message->getStorageId()); + tmStore->deleteData(message->getStorageId()); sif::error << "TmFunnel::handlePacket: Error sending to storage " "handler" << std::endl; return result; @@ -79,8 +80,8 @@ ReturnValue_t TmFunnel::handlePacket(TmTcMessage* message) { ReturnValue_t TmFunnel::initialize() { - tmPool = ObjectManager::instance()->get(objects::TM_STORE); - if(tmPool == nullptr) { + tmStore = ObjectManager::instance()->get(objects::TM_STORE); + if(tmStore == nullptr) { sif::error << "TmFunnel::initialize: TM store not set." << std::endl; sif::error << "Make sure the tm store is set up properly" @@ -97,7 +98,13 @@ ReturnValue_t TmFunnel::initialize() { "properly and implements AcceptsTelemetryIF" << std::endl; return ObjectManagerIF::CHILD_INIT_FAILED; } + +#if OBSW_TM_TO_PTME == 1 + // Live TM will be sent via the virtual channel 0 + tmQueue->setDefaultDestination(tmTarget->getReportReceptionQueue(config::LIVE_TM)); +#else tmQueue->setDefaultDestination(tmTarget->getReportReceptionQueue()); +#endif /* OBSW_TM_TO_PTME == 1 */ // Storage destination is optional. if(storageDestination == objects::NO_OBJECT) { diff --git a/mission/utility/TmFunnel.h b/mission/utility/TmFunnel.h index 2d4575a0..b85a09e0 100644 --- a/mission/utility/TmFunnel.h +++ b/mission/utility/TmFunnel.h @@ -41,7 +41,7 @@ private: MessageQueueIF* tmQueue = nullptr; MessageQueueIF* storageQueue = nullptr; - StorageManagerIF* tmPool = nullptr; + StorageManagerIF* tmStore = nullptr; uint32_t messageDepth = 0; ReturnValue_t handlePacket(TmTcMessage* message); diff --git a/scripts/q7s-port.sh b/scripts/q7s-port.sh index e475e8b7..e350d4c2 100755 --- a/scripts/q7s-port.sh +++ b/scripts/q7s-port.sh @@ -2,10 +2,12 @@ echo "Setting up all Q7S ports" echo "-L 1534:192.168.133.10:1534 for connection to TCF agent" echo "-L 1535:192.168.133.10:22 for file transfers" -echo "-L 1536:192.168.133.10:7301 for TMTC commanding" +echo "-L 1536:192.168.133.10:7301 for TMTC commanding using the TCP/IP IF" +echo "-L 1537:127.0.0.1:7100 for TMTC commanding using the CCSDS IF" ssh -L 1534:192.168.133.10:1534 \ -L 1535:192.168.133.10:22 \ -L 1536:192.168.133.10:7301 \ + -L 1537:127.0.0.1:7100 \ eive@2001:7c0:2018:1099:babe:0:e1fe:f1a5 \ -t 'CONSOLE_PREFIX="[Q7S Tunnel]" /bin/bash' diff --git a/tmtc b/tmtc index 56717935..7b49babb 160000 --- a/tmtc +++ b/tmtc @@ -1 +1 @@ -Subproject commit 56717935e6d90e8a501894f44cd250aec8d12d03 +Subproject commit 7b49babb1febb3959f0f817aa4b4ce4c32d6d21c