Skip to content
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions FPrimeCfs/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ add_fprime_subdirectory("${CMAKE_CURRENT_LIST_DIR}/CfsRouter")
add_fprime_subdirectory("${CMAKE_CURRENT_LIST_DIR}/EvsMirror")
add_fprime_subdirectory("${CMAKE_CURRENT_LIST_DIR}/CfsSystemTime")
add_fprime_subdirectory("${CMAKE_CURRENT_LIST_DIR}/CfsTlmFramer")
add_fprime_subdirectory("${CMAKE_CURRENT_LIST_DIR}/CfsTlmStripper")
add_fprime_subdirectory("${CMAKE_CURRENT_LIST_DIR}/Ports")
add_fprime_subdirectory("${CMAKE_CURRENT_LIST_DIR}/PollingTimer")
add_fprime_subdirectory("${CMAKE_CURRENT_LIST_DIR}/SchAppDriver")
Expand Down
2 changes: 1 addition & 1 deletion FPrimeCfs/CfsAppBridge/CfsAppBridge.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,7 @@ void CfsAppBridge ::cfsCommandIn_handler(FwIndexType portNum, U8 functionCode, F
void CfsAppBridge ::comIn_handler(FwIndexType portNum, Fw::ComBuffer& data, U32 context) {
ComCfg::FrameContext frameContext;
frameContext.set_apid(this->mapDescriptorToApid(data));
this->copyAndSend(data.getBuffAddr(), data.getBuffLength(), frameContext);
this->copyAndSend(data.getBuffAddr(), data.getSize(), frameContext);
}

void CfsAppBridge ::dataReturnIn_handler(FwIndexType portNum,
Expand Down
17 changes: 17 additions & 0 deletions FPrimeCfs/CfsTlmStripper/CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
####
# F Prime CMakeLists.txt:
#
# SOURCES: list of source files (to be compiled)
# AUTOCODER_INPUTS: list of files to be passed to the autocoders
# DEPENDS: list of libraries that this module depends on
#
# More information in the F´ CMake API documentation:
# https://fprime.jpl.nasa.gov/latest/docs/reference/api/cmake/API/
#
####
register_fprime_module(
AUTOCODER_INPUTS
"${CMAKE_CURRENT_LIST_DIR}/CfsTlmStripper.fpp"
SOURCES
"${CMAKE_CURRENT_LIST_DIR}/CfsTlmStripper.cpp"
)
90 changes: 90 additions & 0 deletions FPrimeCfs/CfsTlmStripper/CfsTlmStripper.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,90 @@
// ======================================================================
// \title CfsTlmStripper.cpp
// \brief cpp file for CfsTlmStripper component implementation class
// ======================================================================

#include "FPrimeCfs/CfsTlmStripper/CfsTlmStripper.hpp"
#include "Fw/FPrimeBasicTypes.hpp"
#include <cstring>

namespace FPrimeCfs {

// ----------------------------------------------------------------------
// Component construction and destruction
// ----------------------------------------------------------------------

CfsTlmStripper ::CfsTlmStripper(const char* const compName) : CfsTlmStripperComponentBase(compName) {}

CfsTlmStripper ::~CfsTlmStripper() {}

// ----------------------------------------------------------------------
// Handler implementations for typed input ports
// ----------------------------------------------------------------------

void CfsTlmStripper ::dataIn_handler(FwIndexType portNum, Fw::Buffer& data, const ComCfg::FrameContext& context) {
const U8* const bytes = data.getData();
const FwSizeType size = data.getSize();
if (size < CFS_TLM_STRIPPER_PRI_HDR_SIZE) {
this->log_WARNING_HI_MalformedPacket(static_cast<U32>(size));
this->dataReturnOut_out(0, data, context);
return;
}

// Telemetry space packets carrying a cFS secondary header have it stripped; all
// other packets (commands, packets without the secondary header flag) pass
// through unchanged. The output is always an allocated copy so the incoming
// buffer can be returned to its sender synchronously.
const bool strip = ((bytes[0] & CFS_TLM_STRIPPER_TYPE_FLAG) == 0) &&
((bytes[0] & CFS_TLM_STRIPPER_SEC_HDR_FLAG) != 0) &&
(size >= (CFS_TLM_STRIPPER_PRI_HDR_SIZE + CFS_TLM_STRIPPER_SEC_HDR_SIZE + 1));
const FwSizeType outSize = strip ? (size - CFS_TLM_STRIPPER_SEC_HDR_SIZE) : size;

Fw::Buffer out = this->bufferAllocate_out(0, static_cast<Fw::Buffer::SizeType>(outSize));
if (out.getSize() < outSize) {
this->log_WARNING_HI_AllocationFailed(static_cast<U32>(outSize));
if (out.getSize() > 0) {
this->bufferDeallocate_out(0, out);
}
this->dataReturnOut_out(0, data, context);
return;
}

U8* const outBytes = out.getData();
ComCfg::FrameContext outContext = context;
if (strip) {
(void)std::memcpy(outBytes, bytes, CFS_TLM_STRIPPER_PRI_HDR_SIZE);
(void)std::memcpy(&outBytes[CFS_TLM_STRIPPER_PRI_HDR_SIZE],
&bytes[CFS_TLM_STRIPPER_PRI_HDR_SIZE + CFS_TLM_STRIPPER_SEC_HDR_SIZE],
size - CFS_TLM_STRIPPER_PRI_HDR_SIZE - CFS_TLM_STRIPPER_SEC_HDR_SIZE);
// Clear the secondary header flag and shrink the packet data length field
outBytes[0] &= static_cast<U8>(~CFS_TLM_STRIPPER_SEC_HDR_FLAG);
const FwSizeType lengthToken =
((static_cast<FwSizeType>(bytes[CFS_TLM_STRIPPER_LENGTH_OFFSET]) << 8) |
static_cast<FwSizeType>(bytes[CFS_TLM_STRIPPER_LENGTH_OFFSET + 1])) -
CFS_TLM_STRIPPER_SEC_HDR_SIZE;
outBytes[CFS_TLM_STRIPPER_LENGTH_OFFSET] = static_cast<U8>((lengthToken >> 8) & 0xFF);
outBytes[CFS_TLM_STRIPPER_LENGTH_OFFSET + 1] = static_cast<U8>(lengthToken & 0xFF);
outContext.set_hasSecHdr(false);
} else {
(void)std::memcpy(outBytes, bytes, size);
}
out.setSize(static_cast<Fw::Buffer::SizeType>(outSize));

this->dataOut_out(0, out, outContext);
this->dataReturnOut_out(0, data, context); // return ownership of the original data buffer
}

void CfsTlmStripper ::comStatusIn_handler(FwIndexType portNum, Fw::Success& condition) {
if (this->isConnected_comStatusOut_OutputPort(portNum)) {
this->comStatusOut_out(portNum, condition);
}
}

void CfsTlmStripper ::dataReturnIn_handler(FwIndexType portNum,
Fw::Buffer& frameBuffer,
const ComCfg::FrameContext& context) {
// dataReturnIn is the allocated buffer coming back from the dataOut port
this->bufferDeallocate_out(0, frameBuffer);
}

} // namespace FPrimeCfs
53 changes: 53 additions & 0 deletions FPrimeCfs/CfsTlmStripper/CfsTlmStripper.fpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
module FPrimeCfs {

@ Framer stage that strips the cFS telemetry secondary header (big-endian
@ 4-byte seconds, 2-byte subseconds) from complete telemetry space packets,
@ rebuilding the CCSDS primary header (length field and secondary header
@ flag). The inverse of FPrimeCfs.CfsTlmFramer, for ground-facing bridges
@ (e.g. a GDS bridge) whose downstream consumers expect bare F Prime
@ packets inside the space packets. Command packets and packets without
@ the secondary header flag are forwarded unchanged (as a copy).
passive component CfsTlmStripper {

import Svc.Framer

@ Port to allocate a buffer for the stripped packet
output port bufferAllocate: Fw.BufferGet

@ Port to deallocate a buffer once the stripped packet is returned
output port bufferDeallocate: Fw.BufferSend

# ----------------------------------------------------------------------
# Events
# ----------------------------------------------------------------------

@ The allocator did not return a buffer large enough for the stripped packet;
@ the packet was dropped
event AllocationFailed(
$size: U32 @< The requested allocation size in bytes
) \
severity warning high \
format "Failed to allocate a {} byte buffer for a stripped telemetry packet"

@ An incoming buffer was not a complete space packet; the packet was dropped
event MalformedPacket(
$size: U32 @< The size of the incoming buffer in bytes
) \
severity warning high \
format "Dropped a {} byte buffer that does not hold a complete space packet"

###############################################################################
# Standard AC Ports for Events
###############################################################################

@ Port for requesting the current time
time get port timeCaller

@ Port for sending textual representation of events
text event port logTextOut

@ Port for sending events to downlink
event port logOut

}
}
62 changes: 62 additions & 0 deletions FPrimeCfs/CfsTlmStripper/CfsTlmStripper.hpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
// ======================================================================
// \title CfsTlmStripper.hpp
// \brief hpp file for CfsTlmStripper component implementation class
// ======================================================================

#ifndef FPrimeCfs_CfsTlmStripper_HPP
#define FPrimeCfs_CfsTlmStripper_HPP

#include "FPrimeCfs/CfsTlmStripper/CfsTlmStripperComponentAc.hpp"

namespace FPrimeCfs {

//! Size in bytes of the CCSDS space packet primary header
constexpr FwSizeType CFS_TLM_STRIPPER_PRI_HDR_SIZE = 6;

//! Size in bytes of the cFS telemetry secondary header (4-byte seconds, 2-byte subseconds)
constexpr FwSizeType CFS_TLM_STRIPPER_SEC_HDR_SIZE = 6;

//! Secondary header flag mask within the high byte of the stream identifier
constexpr U8 CFS_TLM_STRIPPER_SEC_HDR_FLAG = 0x08;

//! Packet type (command) bit mask within the high byte of the stream identifier
constexpr U8 CFS_TLM_STRIPPER_TYPE_FLAG = 0x10;

//! Byte offset of the packet data length field within the primary header
constexpr FwSizeType CFS_TLM_STRIPPER_LENGTH_OFFSET = 4;

class CfsTlmStripper final : public CfsTlmStripperComponentBase {
public:
// ----------------------------------------------------------------------
// Component construction and destruction
// ----------------------------------------------------------------------

//! Construct CfsTlmStripper object
CfsTlmStripper(const char* const compName //!< The component name
);

//! Destroy CfsTlmStripper object
~CfsTlmStripper();

private:
// ----------------------------------------------------------------------
// Handler implementations for typed input ports
// ----------------------------------------------------------------------

//! Handler implementation for dataIn
void dataIn_handler(FwIndexType portNum,
Fw::Buffer& data,
const ComCfg::FrameContext& context) override;

//! Handler implementation for dataReturnIn
void dataReturnIn_handler(FwIndexType portNum,
Fw::Buffer& frameBuffer,
const ComCfg::FrameContext& context) override;

//! Handler implementation for comStatusIn
void comStatusIn_handler(FwIndexType portNum, Fw::Success& condition) override;
};

} // namespace FPrimeCfs

#endif
40 changes: 40 additions & 0 deletions FPrimeCfs/CfsTlmStripper/docs/sdd.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
# FPrimeCfs::CfsTlmStripper

A framer stage that strips the cFS telemetry secondary header (big-endian 4-byte seconds, 2-byte subseconds)
from complete telemetry space packets, rebuilding the CCSDS primary header. It is the inverse of
`FPrimeCfs.CfsTlmFramer`, intended for ground-facing bridges (e.g. a GDS bridge application) whose downstream
consumers expect bare F Prime packets inside the space packets.

## Usage

```
FPrimeCfs.CfsBridge.dataOut -> CfsTlmStripper.dataIn
CfsTlmStripper.dataOut -> [downlink framer, e.g. Svc.Ccsds.TmFramer]
[downlink framer].dataReturnOut -> CfsTlmStripper.dataReturnIn
CfsTlmStripper.dataReturnOut -> FPrimeCfs.CfsBridge.dataReturnIn
CfsTlmStripper.bufferAllocate / bufferDeallocate -> [buffer manager]
```

## Behavior

For each incoming buffer holding a complete space packet:

- Telemetry packets (packet type bit clear) with the secondary header flag set have the 6-byte cFS telemetry
secondary header removed. The primary header is rebuilt: the packet data length field shrinks by 6 and the
secondary header flag is cleared. The forwarded context has `hasSecHdr = false`.
- All other packets (commands, packets without the secondary header flag) are forwarded unchanged.

The output is always an allocated copy, so upstream producers operating in zero-copy mode (e.g.
`FPrimeCfs.CfsBridge` polling the software bus without an allocator) are supported: the incoming buffer is
returned to its sender on `dataReturnOut` before the handler returns.

Buffers too small to hold a primary header are dropped with a `MalformedPacket` warning event. Allocation
failures drop the packet with an `AllocationFailed` warning event.

## Buffer ownership

- `dataIn`: a new buffer is allocated via `bufferAllocate` for the outgoing packet; the incoming buffer is
returned to the sender on `dataReturnOut` immediately with its original context.
- `dataReturnIn`: the allocated buffer coming back from downstream is deallocated via `bufferDeallocate`.

`comStatusIn` is passed through to `comStatusOut` unchanged.
Loading