GCC Code Coverage Report


Directory: ./
File: FrameDetector/FprimeFrameDetector.cpp
Date: 2026-09-03 22:13:46
Exec Total Coverage
Lines: 0 53 0.0%
Functions: 0 1 0.0%
Branches: 0 43 0.0%

Line Branch Exec Source
1 // ======================================================================
2 // \title FprimeFrameDetector.hpp
3 // \author thomas-bc
4 // \brief hpp file for fprime frame detector definitions
5 // ======================================================================
6
7 #include "Svc/FrameAccumulator/FrameDetector/FprimeFrameDetector.hpp"
8
9 namespace Svc {
10 namespace FrameDetectors {
11
12 FrameDetector::Status FprimeFrameDetector::detect(const Types::CircularBuffer& data, FwSizeType& size_out) const {
13 // If not enough data for header + trailer, report MORE_DATA_NEEDED
14 if (data.get_allocated_size() <
15 FprimeProtocol::FrameHeader::SERIALIZED_SIZE + FprimeProtocol::FrameTrailer::SERIALIZED_SIZE) {
16 size_out = FprimeProtocol::FrameHeader::SERIALIZED_SIZE + FprimeProtocol::FrameTrailer::SERIALIZED_SIZE;
17 return Status::MORE_DATA_NEEDED;
18 }
19
20 // NOTE: it is understood and accepted that the following code is not as efficient as it could technically be
21 // We are leveraging the FPP autocoded types to do the deserialization for us.
22 // In its current implementation, CircularBuffer is not a LinearBufferBase, which prevents us from deserializing
23 // directly from the CircularBuffer into FrameHeader/FrameTrailer. Instead, we have to copy the data into
24 // a temporary SerializeBuffer, and then deserialize from that buffer into the FrameHeader/FrameTrailer objects.
25 // A better implementation would be to have CircularBuffer implement a shared interface with LinearBufferBase,
26 // and then we could pass the CircularBuffer directly into the FrameHeader/FrameTrailer deserializers. This is left
27 // as a TODO for future improvement as it is a significant refactor
28
29 FprimeProtocol::FrameHeader header;
30 FprimeProtocol::FrameTrailer trailer;
31
32 // ---------------- Frame Header ----------------
33 // Copy CircularBuffer data into linear buffer, for serialization into FrameHeader object
34 U8 header_data[FprimeProtocol::FrameHeader::SERIALIZED_SIZE];
35 Fw::SerializeStatus status = data.peek(header_data, FprimeProtocol::FrameHeader::SERIALIZED_SIZE, 0);
36 if (status != Fw::FW_SERIALIZE_OK) {
37 return Status::NO_FRAME_DETECTED;
38 }
39 Fw::ExternalSerializeBuffer header_ser_buffer(header_data, FprimeProtocol::FrameHeader::SERIALIZED_SIZE);
40 status = header_ser_buffer.setBuffLen(FprimeProtocol::FrameHeader::SERIALIZED_SIZE);
41 FW_ASSERT(status == Fw::FW_SERIALIZE_OK, static_cast<FwAssertArgType>(status));
42 // Attempt to deserialize data into the FrameHeader object
43 status = header.deserializeFrom(header_ser_buffer);
44 if (status != Fw::FW_SERIALIZE_OK) {
45 return Status::NO_FRAME_DETECTED;
46 }
47 // Check that deserialized start_word token matches expected value (default start_word value in the FPP object)
48 FprimeProtocol::FrameHeader default_value;
49 if (header.get_startWord() != default_value.get_startWord()) {
50 return Status::NO_FRAME_DETECTED;
51 }
52 // Validate size before proceeding.
53 // Use a static_assert to guarantee the two fixed overhead constants don't themselves overflow
54 // when added together. This is a compile-time check so it carries zero runtime cost, but it
55 // protects the runtime guard below if SERIALIZED_SIZE values are ever changed.
56 static_assert(FprimeProtocol::FrameHeader::SERIALIZED_SIZE <=
57 std::numeric_limits<FwSizeType>::max() - FprimeProtocol::FrameTrailer::SERIALIZED_SIZE,
58 "FrameHeader::SERIALIZED_SIZE + FrameTrailer::SERIALIZED_SIZE overflows FwSizeType");
59 constexpr FwSizeType header_trailer_overhead =
60 FprimeProtocol::FrameHeader::SERIALIZED_SIZE + FprimeProtocol::FrameTrailer::SERIALIZED_SIZE;
61
62 // Guard: reject frames whose declared length would overflow FwSizeType when added to the
63 // fixed overhead. Using subtraction on unsigned types (as in the prior implementation) is
64 // fragile — if the constants change sign or width the subtraction itself can wrap silently.
65 // An explicit addition-based check is clearer and easier to audit.
66 if (header.get_lengthField() > std::numeric_limits<FwSizeType>::max() - header_trailer_overhead) {
67 // lengthField + overhead would overflow — frame is invalid
68 return Status::NO_FRAME_DETECTED;
69 }
70
71 // We expect the frame size to be size of header + body (of size specified in header) + trailer.
72 // Overflow is impossible here: the guard above ensures
73 // lengthField <= MAX - header_trailer_overhead
74 const FwSizeType expected_frame_size = header.get_lengthField() + header_trailer_overhead;
75 // If the frame will never fit, then report NO_FRAME_DETECTED to drop the erroneous frame
76 if (data.get_capacity() < expected_frame_size) {
77 return Status::NO_FRAME_DETECTED;
78 }
79 // If the frame could fit but we haven't received enough data yet, report MORE_DATA_NEEDED
80 else if (data.get_allocated_size() < expected_frame_size) {
81 size_out = expected_frame_size;
82 return Status::MORE_DATA_NEEDED;
83 }
84
85 // ---------------- Frame Trailer ----------------
86 U8 trailer_data[FprimeProtocol::FrameTrailer::SERIALIZED_SIZE];
87 Fw::ExternalSerializeBuffer trailer_ser_buffer(trailer_data, FprimeProtocol::FrameTrailer::SERIALIZED_SIZE);
88 status = data.peek(trailer_data, FprimeProtocol::FrameTrailer::SERIALIZED_SIZE,
89 FprimeProtocol::FrameHeader::SERIALIZED_SIZE + header.get_lengthField());
90 if (status != Fw::FW_SERIALIZE_OK) {
91 return Status::NO_FRAME_DETECTED;
92 }
93 status = trailer_ser_buffer.setBuffLen(FprimeProtocol::FrameTrailer::SERIALIZED_SIZE);
94 FW_ASSERT(status == Fw::FW_SERIALIZE_OK, static_cast<FwAssertArgType>(status));
95 // Deserialize trailer from circular buffer (peeked data) into trailer object
96 status = trailer.deserializeFrom(trailer_ser_buffer);
97 if (status != Fw::FW_SERIALIZE_OK) {
98 return Status::NO_FRAME_DETECTED;
99 }
100
101 Utils::Hash hash;
102 Utils::HashBuffer hashBuffer;
103 // Compute CRC over the transmitted data (header + body).
104 // Safety invariant: the guard above ensures
105 // lengthField <= MAX - header_trailer_overhead
106 // <= MAX - HEADER_SIZE - TRAILER_SIZE
107 // < MAX - HEADER_SIZE
108 // so this addition cannot overflow. The assert makes that contract explicit at the
109 // point of use so it remains correct if this code is ever moved or refactored.
110 FW_ASSERT(header.get_lengthField() <=
111 std::numeric_limits<FwSizeType>::max() - FprimeProtocol::FrameHeader::SERIALIZED_SIZE,
112 static_cast<FwAssertArgType>(header.get_lengthField()));
113 FwSizeType hash_field_size = header.get_lengthField() + FprimeProtocol::FrameHeader::SERIALIZED_SIZE;
114 hash.init();
115 for (FwSizeType i = 0; i < hash_field_size; i++) {
116 U8 byte = 0;
117 status = data.peek(byte, i);
118 FW_ASSERT(status == Fw::FW_SERIALIZE_OK, status);
119 hash.update(&byte, 1);
120 }
121 hash.finalize(hashBuffer);
122
123 // Compare the transmitted CRC with the computed one
124 if (trailer.get_crcField() != hashBuffer.asBigEndianU32()) {
125 // CRC mismatch - there likely was data corruption. The F Prime protocol
126 // being very simple, we don't have a way to recover from this.
127 // So we report NO_FRAME_DETECTED and drop the frame
128 return Status::NO_FRAME_DETECTED;
129 }
130 // All checks passed - we have detected a frame of size expected_frame_size
131 size_out = expected_frame_size;
132 return Status::FRAME_DETECTED;
133 }
134
135 } // namespace FrameDetectors
136 } // namespace Svc
137