Skip to content

TlmPacketizer Group Level Control Improvements#4668

Open
Lex-ari wants to merge 18 commits intonasa:develfrom
Lex-ari:Lex-ari-TlmPacketizer-Group-Level-Improvements
Open

TlmPacketizer Group Level Control Improvements#4668
Lex-ari wants to merge 18 commits intonasa:develfrom
Lex-ari:Lex-ari-TlmPacketizer-Group-Level-Improvements

Conversation

@Lex-ari
Copy link
Contributor

@Lex-ari Lex-ari commented Jan 28, 2026

Related Issue(s) #4531
Has Unit Tests (y/n) Y
Documentation Included (y/n) Y
Generative AI was used in this contribution (y/n) N

Change Description

Group Level Control of Telemetry and individually sampled rates.

  • Telemetry Groups can be enabled / disabled non-sequentially. (Ex Group 2 enabled, 0, 1 disabled)
  • PktSend output is now "Sections" of Telemetry Groups
    • Telemetry Groups get their own output index
    • Each section gets a block of indices on the PktSend out array for each telemetry group.
  • Telemetry groups are configurable with Min Delta / Max Delta between successive packets
    • Min Delta: Minimum amount of invocations before packet of a group is emitted on change
    • Max Delta: Maximum amount of invocations for a packet to be emitted
    • RateLogic enum dictates choosing behavior between SILENCED, ON_CHANGE_MIN, EVERY_MAX, and ON_CHANGE_MIN_OR_EVERY_MAX
  • Commands to support enabling / disabling separate Telemetry Sections
  • controlIn port to support enabling / disabling separate Telemetry Sections

Rationale

This is a wanted telemetry improvement for several projects. In the current implementation, Packets with telemetry groups are emitted only if the current level is at or above the entry's telemetry group. This means that packet specifications must be carefully designed in order to account which packets should be linked with which group, as there is no way to "disable" specific packets other than having sequential ones chosen.

Additionally, projects would want to change the stream throughput of packets in flight. This is currently a limitation backed by a single rate group and cannot be configured in-flight.

This implementation allows users to individually configure each group, which are then evaluated on an individual packet basis. Telemetry have a configurable minimum thresholds of Run port invocations before sending updates, preventing telemetry spam. Additionally, they have a configurable maximum threshold that will send packets, ensuring that feedback is continuously monitored. Packets can be configured to emit on changes or every standard interval in flight, without needing an update or restart.

Sections of telemetry groups allow downstream components of the tlmPacketizer to handle resampling of data at different rates. This could be lower fidelity live telemetry on one section, while high detailed sim reconstruction happens on another section. Each section is individually configurable. These sections can be turned on and off following different spacecraft modes.

Additionally, individual group ports per section allow downstream components to "listen in" or prioritize specific groups against other groups / packets (Ex on ComQueue, File Downlink / Events).

Testing/Review Recommendations

These changes were designed to be near backwards compatible in terms of Telemetry group send behavior. Without configuration, it works similarly to the current implementation of tlmPacketizer.

Major changes to topologies would include merging output ports of pktSend to downstream topology components. Packets are now sent on an array of indices on pktSend, which are also sent for however many "sections" is included in the configuration for TlmPacketizer.

CC @LeStarch @Willmac16

this->m_sendBuffers[buffer].updated = false;
}

// clear enabled sections
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Isn't this block enabling all sections rather than clearing enabled sections?

Comment on lines 121 to 126
for (FwIndexType section = 0; section < NUM_CONFIGURABLE_TLMPACKETIZER_SECTIONS; section++) {
for (FwChanIdType group = 0; group <= MAX_CONFIGURABLE_TLMPACKETIZER_GROUP; group++) {
this->m_groupConfigs[section][group].enabled =
group <= this->m_startLevel ? Fw::Enabled::ENABLED : Fw::Enabled::DISABLED;
}
}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nit: (you can realistically ignore this since it only happens on startup)
If swap the inner loop and the outer loop you can hoist the enabled calc outside of the inner loop.

Comment on lines 372 to 374
for (FwIndexType port = 0; port < NUM_CONFIGURABLE_TLMPACKETIZER_SECTIONS; port++) {
this->m_packetFlags[port][pkt].updateFlag = true;
}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Move this from port -> section if that's what you're consistently using

Suggested change
for (FwIndexType port = 0; port < NUM_CONFIGURABLE_TLMPACKETIZER_SECTIONS; port++) {
this->m_packetFlags[port][pkt].updateFlag = true;
}
for (FwIndexType section = 0; section < NUM_CONFIGURABLE_TLMPACKETIZER_SECTIONS; section++) {
this->m_packetFlags[section][pkt].updateFlag = true;
}

Comment on lines 6 to 7

Uses can change the individual rates at which groups per group instance are outputted upon a `run()` port call. Groups are configured with a MIN number of `run()` invocations to emit updated telemetry, and a MAX number of `run()` invocations that shall output a packet if it exceeds MAX. Packets are evaluated individually and have a counter since last invocation. MIN and MAX logic are selectable, for users that desire only "on change" telemetry, MAX invocations between telemetry points, both, or none at all.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I personally like the phrasing of "Sched ticks," "Rate Group ticks," or simply "ticks" to contrast other unscheduled, non-thead-providing port invocations

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

"each group on each output section can have its own telemetry rate resampling configuration"

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

"Packets can be sent on change with a rate limiting enforced minimum number of ticks between updates. Or packets can be sent with at a guaranteed rate of a maximum number of ticks between updates."

Telemetry Point: An emitted value.
Telemetry Channel: A tagged type and identifier for emitting telemetry points.
Telemetry Packets: A group of telemetry channels.
Telemetry Group / Level: An identifier for a telemetry packet used to determine which packets get transmitted.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

"A set of telemetry packets with shared control"

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

// If a packet in the group has been updated since last sent on section
if (pktEntryFlags.updateFlag) {
// If delta min is disabled (Disable on change, Only send on Delta Max)
if (entryGroupConfig.rateLogic == Svc::TlmPacketizer_RateLogic::EVERY_MAX) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

See my bitfield comment in the fpp. Regardless, I think you also need to check for SILENCED here

Comment on lines 393 to 395
if (pktEntryFlags.prevSentCounter < 0xFFFFFFFF) {
pktEntryFlags.prevSentCounter++;
}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I would add a comment about preventing rollover & how you init the counter to int max to prevent a delay before first tlm packet on startup. Only counterpoint to the int max thing is you might create a whole bunch of tlm at the same time that would normally spread out.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Telemetry Spam only applies when the tlmpacketizer is configured to PACKET_UPDATE_ALWAYS. Otherwise, it waits until first change before ON_CHANGE / AFTER_FIRST_CHANGE logic applies.

Comment on lines 407 to 410
} else if (pktEntryFlags.prevSentCounter < entryGroupConfig.min) {
// Keep flag true but do not send.
continue;
}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I believe this doesn't account for you being in in MIN_AND_MAX mode (at least w/ min > max)

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I suspect the logic will have fewer checks if you avoid using continue to early escape. But that hunch may be very wrong

pktEntryFlags.updateFlag = true;
}

if (not((entryGroupConfig.enabled and this->m_sectionEnabled[section] == Fw::Enabled::ENABLED) or
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I would probably do the enabled checks first, & for those I think early exit w/ continue does make sense

GroupConfig& entryGroupConfig = this->m_groupConfigs[section][entryGroup];

// If a packet in the group has been updated since last sent on section
if (pktEntryFlags.updateFlag) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I would recommend using a separate updateFlag from sendFlag for clarity

@Lex-ari Lex-ari marked this pull request as draft January 29, 2026 19:01
if (pktEntryFlags.updateFlag == UpdateFlag::NEVER_UPDATED) continue; // Avoid No Data

// Update Counter, prevent overflow.
if (pktEntryFlags.prevSentCounter < 0xFFFFFFFF) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can you use std::numeric_limits here?

Comment on lines 488 to 489

void TlmPacketizer ::SEND_PKT_cmdHandler(const FwOpcodeType opCode, const U32 cmdSeq, U32 id) {
void TlmPacketizer ::SEND_PKT_cmdHandler(FwOpcodeType opCode, U32 cmdSeq, U32 id, FwIndexType section) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why no longer const?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

impl

Comment on lines 382 to 384
if (this->m_sendBuffers[pkt].updated) {
this->m_packetFlags[section][pkt].updateFlag = UpdateFlag::NEW;
}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I really like how you're using the UpdateEnum. Only catch I've seen: I think you need to check here that updateFlag != REQUESTED otherwise an update might downgrade a request to something that needs to get past the min checks

this->m_startLevel = startLevel;

} // end packet list
FW_ASSERT(maxLevel <= MAX_CONFIGURABLE_TLMPACKETIZER_GROUP, maxLevel);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
FW_ASSERT(maxLevel <= MAX_CONFIGURABLE_TLMPACKETIZER_GROUP, maxLevel);
FW_ASSERT(maxLevel <= MAX_CONFIGURABLE_TLMPACKETIZER_GROUP, static_cast<FwAssertArgType>(maxLevel));

break;
case PACKET_UPDATE_ALWAYS:
this->m_packetFlags[section][group].updateFlag = UpdateFlag::PAST;
[[fallthrough]]; // Intentional Fallthrough (Both are configured for EVERY_MAX)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fallthrough doesn't seem to work on c++ 14

Fw::Time::SERIALIZED_SIZE);
Fw::SerializeStatus stat = buff.serializeFrom(this->m_sendBuffers[pkt].latestTime);
FW_ASSERT(Fw::FW_SERIALIZE_OK == stat, stat);
this->PktSend_out(outIndex, this->m_sendBuffers[pkt].buffer, 0);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What if you sent the prevSentCounter as the context field?

async command SEND_PKT(
$id: U32 @< The packet ID
$id: U32 @< The packet ID
section: FwIndexType @< Section to emit packet
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Probably should default section to 0 for backwards compatibility

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Not sure if I can assign a default here unless I create another alias type.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Believe FwIndexType defaults at 0 too

Comment on lines 431 to 433
if (pktEntryFlags.updateFlag == UpdateFlag::REQUESTED) {
sendOutFlag = true;
}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Might be my coverage visualization glitching out, but make sure your SEND_PKT tests hit these lines

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

UpdateFlag:: New Bug

TlmPacketizer_RateLogic rateLogic = TlmPacketizer_RateLogic::ON_CHANGE_MIN;
U32 min = 0; //!< Default for Backwards Compatible Behavior
U32 max = 0; //!< Default for Backwards Compatible Behavior
} m_groupConfigs[NUM_CONFIGURABLE_TLMPACKETIZER_SECTIONS][MAX_CONFIGURABLE_TLMPACKETIZER_GROUP + 1]{};
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
} m_groupConfigs[NUM_CONFIGURABLE_TLMPACKETIZER_SECTIONS][MAX_CONFIGURABLE_TLMPACKETIZER_GROUP + 1]{};
} m_groupConfigs[NUM_CONFIGURABLE_TLMPACKETIZER_SECTIONS][NUM_CONFIGURABLE_TLMPACKETIZER_GROUPS]{};

Comment on lines 345 to 348
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think just get rid of this guy

@Lex-ari Lex-ari marked this pull request as ready for review January 30, 2026 21:39
Copy link
Collaborator

@LeStarch LeStarch left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I did a first pass and will come back and do more. However, I want to understand one thing:

What is the use case for "Telemetry Groups get their own output index"? It seems that section maps to the thing that is handling the output.....so what is the advantage of breaking the groups into individual output ports too? This seems to complicate topologies, group definitions, etc.

Note: I am sure we discussed this before, but I am failing to remember.

Also, I need @timcanham to look through this.


The `Svc::TlmPacketizer` Component is used to store telemetry values written by other components. The values are stored in serialized form. TlmPacketizer differs from `Svc::TlmChan` in that it stores telemetry in defined packets instead of streaming the updates as they come. The defined packets are passed in as a table to the `setPacketList()` public method. When telemetry updates are passed to the component, they are placed at the offset in a packet buffer defined by the table. When the `run()` port is called, all the defined packets are sent to the output port with the most recent . This is meant to replace `Svc::TlmCham` for use cases where a more compact packet format is desired. The disadvantage is that all channels are pushed whether or not they have been updated.

Uses can change the individual rates at which groups per group instance are outputted upon a `run()` sched tick. Each group on each output section has independently configurable telemetry resampling rates. Packets can be sent on change with a rate limiting enforced minimum number of ticks between updates. Or packets can be sent with at a guaranteed rate of a maximum number of ticks between updates. Packets are evaluated individually and have a counter since last invocation. MIN and MAX logic are selectable, for users that desire only "on change" telemetry, MAX invocations between telemetry points, both, or none at all.
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
Uses can change the individual rates at which groups per group instance are outputted upon a `run()` sched tick. Each group on each output section has independently configurable telemetry resampling rates. Packets can be sent on change with a rate limiting enforced minimum number of ticks between updates. Or packets can be sent with at a guaranteed rate of a maximum number of ticks between updates. Packets are evaluated individually and have a counter since last invocation. MIN and MAX logic are selectable, for users that desire only "on change" telemetry, MAX invocations between telemetry points, both, or none at all.
Users can change the individual rates at which telemetry groups are output upon a `run()` sched tick for each telemetry section. Each group for each section has independently configurable telemetry resampling rates. Packets can be sent on change with a rate limiting enforced minimum number of ticks between updates. Alternatively, packets can be sent with at a guaranteed rate of a maximum number of ticks between updates. Packets are evaluated individually and have a counter since last invocation. MIN and MAX logic are selectable, for users that desire only "on change" telemetry, MAX invocations between telemetry points, both, or none at all.

# ----------------------------------------------------------------------

enum RateLogic {
SILENCED,
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should add some annotations to explain what each of these mean.


@ Packet send port
output port PktSend: Fw.Com
output port PktSend: [NUM_CONFIGURABLE_TLMPACKETIZER_SECTIONS * NUM_CONFIGURABLE_TLMPACKETIZER_GROUPS] Fw.Com
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Shouldn't this be just NUM_CONFIGURABLE_TLMPACKETIZER_SECTIONS? That is the number of output destinations (realtime, recorded, etc).

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is by design - wanted an output port for each telemetry group within each section. The PktSend array is ordered by Section, Group.

async command SEND_PKT(
$id: U32 @< The packet ID
$id: U32 @< The packet ID
section: FwIndexType @< Section to emit packet
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is there an options to "emit to all"?

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is titled SEND_PKT, but the FORCE_GROUP command is called FORCE_GROUP. We should either use SEND_PACKET and SEND_GROUP or FORCE_PACKET and FORCE_GROUP

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The Send PKT is a one time request by an operator, while a FORCE_GROUP is a toggleable switch for a group within a section

// Packet is updated and not REQUESTED (Prevent Downgrading)
if (this->m_sendBuffers[pkt].updated and
this->m_packetFlags[section][pkt].updateFlag != UpdateFlag::REQUESTED)
this->m_packetFlags[section][pkt].updateFlag = UpdateFlag::NEW;
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Style: add {} around if clauses

Suggested change
this->m_packetFlags[section][pkt].updateFlag = UpdateFlag::NEW;
{
this->m_packetFlags[section][pkt].updateFlag = UpdateFlag::NEW;
}

5. The packet has data (marked updated in the past or new)
*/
if (not this->isConnected_PktSend_OutputPort(outIndex))
continue;
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

add {}

Suggested change
continue;
{
continue;
}

@LeStarch LeStarch requested a review from timcanham February 2, 2026 19:20
@Lex-ari
Copy link
Contributor Author

Lex-ari commented Feb 2, 2026

I did a first pass and will come back and do more. However, I want to understand one thing:

What is the use case for "Telemetry Groups get their own output index"? It seems that section maps to the thing that is handling the output.....so what is the advantage of breaking the groups into individual output ports too? This seems to complicate topologies, group definitions, etc.

Note: I am sure we discussed this before, but I am failing to remember.

Also, I need @timcanham to look through this.

@LeStarch Appreciate it, thanks!

We wanted to separately output different telemetry groups to change and configure their priority on downstream components based on group, rather than having them all set on a single port, or several sections. Some groups can receive a higher priority than other connections to, for example, a ComQueue, such as Events or FileDownlink operations.
Sections allows enabling and disabling resamplings of telemetry groups with a single action through a command / port invocation, so would like to keep that separate to maintain configurability and quick operation.

Copy link
Collaborator

@LeStarch LeStarch left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Still looking for the section -> enum change, but I finished the deeper review!

# ======================================================================
module Svc {
@ The number of sections of ports (Primary = 0, Secondary = 1, etc...)
constant NUM_CONFIGURABLE_TLMPACKETIZER_SECTIONS = 3;
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

When made into an enumeration, this should be captured as a MAX constant or NUMBER constant. e.g:

 enum ABC {
    ABC
    DEF
   NUMBER_ABC
}

#ifndef TESTER_HPP
#define TESTER_HPP

#include <queue>
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This appears to be unused. Can we remove it?


enum RateLogic {
SILENCED,
EVERY_MAX,
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I find the MIN and MAX parts of these names a bit confusing? Can we add annotations for what these mean?

e.g.

ON_CHANGE_MIN, //@< Send on change with minimum delay between sends
EVERY_MAX, //@<  Send with maximum delay between sends
ON....

Comment on lines +9 to +14
enum RateLogic {
SILENCED,
EVERY_MAX,
ON_CHANGE_MIN,
ON_CHANGE_MIN_OR_EVERY_MAX,
}
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Just be careful that the resulting code isn't "too clever". If it cleans the code up, then go for it.

enabled: Fw.Enabled @< Enable / Disable Telemetry Output
forceEnabled: Fw.Enabled @< Force Enable / Disable Telemetry Output
rateLogic: RateLogic @< Rate Logic Configuration
min: U32 @< Minimum Sched Ticks
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Note in the annotation that this only applies to the MIN setting, same with max and MAX

opcode 3

@ Force telemetering a group on a section, even if disabled
async command FORCE_GROUP(
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

SEND_GROUP to be consistent with SEND_PKT?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

FORCE_GROUP is a toggleable switch that forces logic on a telemetry group. SEND_PKT is an operator requested command

this->m_sendBuffers[pkt].updated = false;
}
this->m_sendBuffers[pkt] = this->m_fillBuffers[pkt];
this->m_fillBuffers[pkt].updated = false;
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This would imply that if all sections disable this packet's group, then this update would be cleared but the packet would not be sent. This would be worth updating in the SDD.

for (FwIndexType section = 0; section < NUM_CONFIGURABLE_TLMPACKETIZER_SECTIONS; section++) {
// Packet is updated and not REQUESTED (Prevent Downgrading)
if (this->m_sendBuffers[pkt].updated and
this->m_packetFlags[section][pkt].updateFlag != UpdateFlag::REQUESTED)
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

must enclose all ifs with {} regardless of being 1 line.


// Iterate through output sections
for (FwIndexType section = 0; section < NUM_CONFIGURABLE_TLMPACKETIZER_SECTIONS; section++) {
// Packet is updated and not REQUESTED (Prevent Downgrading)
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't know what PREVENT Downgrading means. Elaborate. "When not REQUESTED updated packets need to be marked as NEW" or something.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Comment left to describe packets marked as REQUESTED don't accidentally get marked as NEW, as REQUESTED packets bypass MIN/MAX logic

4. The rate logic is not SILENCED.
5. The packet has data (marked updated in the past or new)
*/
if (not this->isConnected_PktSend_OutputPort(outIndex))
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Enclosing {}


The `Svc::TlmPacketizer` Component is used to store telemetry values written by other components. The values are stored in serialized form. TlmPacketizer differs from `Svc::TlmChan` in that it stores telemetry in defined packets instead of streaming the updates as they come. The defined packets are passed in as a table to the `setPacketList()` public method. When telemetry updates are passed to the component, they are placed at the offset in a packet buffer defined by the table. When the `run()` port is called, all the defined packets are sent to the output port with the most recent . This is meant to replace `Svc::TlmCham` for use cases where a more compact packet format is desired. The disadvantage is that all channels are pushed whether or not they have been updated.

Uses can change the individual rates at which groups per group instance are outputted upon a `run()` sched tick. Each group on each output section has independently configurable telemetry resampling rates. Packets can be sent on change with a rate limiting enforced minimum number of ticks between updates. Or packets can be sent with at a guaranteed rate of a maximum number of ticks between updates. Packets are evaluated individually and have a counter since last invocation. MIN and MAX logic are selectable, for users that desire only "on change" telemetry, MAX invocations between telemetry points, both, or none at all.
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

"MIN and MAX logic are selectable, for users that desire only "on change" telemetry, MAX invocations between telemetry points, both, or none at all." Might need rephrasing.


#### 3.1.3 Terminology
Telemetry Point: An emitted value.
Telemetry Channel: A tagged type and identifier for emitting telemetry points.
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Also has time!

Each telemetry group per section is configured with a `RateLogic` parameter:
* `SILENCED`: Packet will never be sent
* `EVERY_MAX`: Packet will be sent every Max Delta intervals
* `ON_CHANGE_MIN`: Packet will only be sent on updates at at least Min Delta intervals.
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

No faster than min delta intervals, yes?

Copy link
Contributor Author

@Lex-ari Lex-ari Feb 5, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

In the current implementation, MAX will take priority over MIN if MIN is greater than MAX (when set to ON_CHANGE_MIN_OR_EVERY_MAX). Will add to sdd.

Copy link
Collaborator

@LeStarch LeStarch left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Somehow it approved, meant to request changes.

Comment on lines +126 to +129
for (PktSendCounters& pkt : this->m_packetFlags[section]) {
// Trigger sending packets even if they're empty.
pkt.updateFlag = UpdateFlag::PAST;
}

Check notice

Code scanning / CodeQL

Multiple variable declarations on one line Note

Multiple variable declarations on the same line.
}

void TlmPacketizer ::SEND_PKT_cmdHandler(const FwOpcodeType opCode, const U32 cmdSeq, U32 id) {
void TlmPacketizer ::SEND_PKT_cmdHandler(const FwOpcodeType opCode,

Check notice

Code scanning / CodeQL

Long function without assertion Note

All functions of more than 10 lines should have at least one assertion.
this->cmdResponse_out(opCode, cmdSeq, Fw::CmdResponse::OK);
}

void TlmPacketizer ::ENABLE_SECTION_cmdHandler(FwOpcodeType opCode,

Check notice

Code scanning / CodeQL

Long function without assertion Note

All functions of more than 10 lines should have at least one assertion.
this->cmdResponse_out(opCode, cmdSeq, Fw::CmdResponse::OK);
}

void TlmPacketizer ::ENABLE_GROUP_cmdHandler(FwOpcodeType opCode,

Check notice

Code scanning / CodeQL

Long function without assertion Note

All functions of more than 10 lines should have at least one assertion.
this->cmdResponse_out(opCode, cmdSeq, Fw::CmdResponse::OK);
}

void TlmPacketizer ::FORCE_GROUP_cmdHandler(FwOpcodeType opCode,

Check notice

Code scanning / CodeQL

Long function without assertion Note

All functions of more than 10 lines should have at least one assertion.
this->cmdResponse_out(opCode, cmdSeq, Fw::CmdResponse::OK);
}

void TlmPacketizer ::SET_GROUP_DELTAS_cmdHandler(FwOpcodeType opCode,

Check notice

Code scanning / CodeQL

Function too long Note

SET_GROUP_DELTAS_cmdHandler has too many parameters (7, while 6 are allowed).
this->cmdResponse_out(opCode, cmdSeq, Fw::CmdResponse::OK);
}

void TlmPacketizer ::SET_GROUP_DELTAS_cmdHandler(FwOpcodeType opCode,

Check notice

Code scanning / CodeQL

Long function without assertion Note

All functions of more than 10 lines should have at least one assertion.
REQUESTED = 3,
};

struct PktSendCounters {

Check notice

Code scanning / CodeQL

More than one statement per line Note

This line contains 2 statements; only one is allowed.
Copy link

@github-advanced-security github-advanced-security bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

CodeQL found more than 20 potential problems in the proposed changes. Check the Files changed tab for more details.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants