Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
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
3 changes: 2 additions & 1 deletion Ref/Top/RefPackets.fppi
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,8 @@ telemetry packets RefPackets {
ComCcsds.commsBufferManager.TotalBuffs
ComCcsds.commsBufferManager.CurrBuffs
ComCcsds.commsBufferManager.HiBuffs
#ComCcsds.tlmSend.SendLevel
#ComCcsds.tlmSend.GroupConfigs
#ComCcsds.tlmSend.SectionEnabled

Ref.rateGroup1Comp.RgMaxTime
Ref.rateGroup2Comp.RgMaxTime
Expand Down
6 changes: 3 additions & 3 deletions Svc/Subtopologies/CdhCore/CdhCoreConfig/CdhCoreTlmConfig.fpp
Original file line number Diff line number Diff line change
Expand Up @@ -6,11 +6,11 @@ module CdhCore{
priority CdhCoreConfig.Priorities.tlmSend \

# Uncomment the following block and comment the above block to use TlmPacketizer instead of TlmChan
#instance tlmSend: Svc.TlmPacketizer base id CdhCoreConfig.BASE_ID + 0x06000 \
# instance tlmSend: Svc.TlmPacketizer base id CdhCoreConfig.BASE_ID + 0x06000 \
# queue size CdhCoreConfig.QueueSizes.tlmSend \
# stack size CdhCoreConfig.StackSizes.tlmSend \
# priority CdhCoreConfig.Priorities.tlmSend \
#{
# {
# # NOTE: The Name Ref is specific to the Reference deployment, Ref
# # This name will need to be updated if wishing to use this in a custom deployment
# phase Fpp.ToCpp.Phases.configComponents """
Expand All @@ -20,5 +20,5 @@ module CdhCore{
# 1
# );
# """
#}
# }
}
247 changes: 205 additions & 42 deletions Svc/TlmPacketizer/TlmPacketizer.cpp

Large diffs are not rendered by default.

86 changes: 82 additions & 4 deletions Svc/TlmPacketizer/TlmPacketizer.fpp
Original file line number Diff line number Diff line change
@@ -1,14 +1,47 @@
module Svc {

port EnableSection (
section: FwIndexType @< Section to enable (Primary, Secondary, etc...)
enabled: Fw.Enabled @< Enable / Disable Section
)
@ A component for storing telemetry
active component TlmPacketizer {
# ----------------------------------------------------------------------
# Types
# ----------------------------------------------------------------------

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.

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....

ON_CHANGE_MIN,
ON_CHANGE_MIN_OR_EVERY_MAX,
}
Comment on lines +12 to +17
Copy link
Contributor

Choose a reason for hiding this comment

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

You can ignore if you want, but with explicit indexing (I think it technically already works) you could make this a bitfield of min and max ruleset enable (i.e. rateLogic & 0x01 is MAX_ENABLED and rateLogic & 0x02 is MIN_ENABLED)

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.


struct GroupConfig {
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

max: U32 @< Maximum Sched Ticks
} default {
enabled = Fw.Enabled.ENABLED
forceEnabled = Fw.Enabled.DISABLED
rateLogic = RateLogic.ON_CHANGE_MIN
min = 0
max = 0
}

array GroupConfigs = [NUM_CONFIGURABLE_TLMPACKETIZER_GROUPS] GroupConfig
array SectionConfigs = [NUM_CONFIGURABLE_TLMPACKETIZER_SECTIONS] GroupConfigs
array SectionEnabled = [NUM_CONFIGURABLE_TLMPACKETIZER_SECTIONS] Fw.Enabled default Fw.Enabled.ENABLED

# ----------------------------------------------------------------------
# General ports
# ----------------------------------------------------------------------

@ 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 input port controlIn: EnableSection

@ Ping input port
async input port pingIn: Svc.Ping
Expand Down Expand Up @@ -62,10 +95,46 @@ module Svc {

@ Force a packet to be sent
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

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

) \
opcode 1

@ Enable / disable telemetry of a group on a section
async command ENABLE_SECTION(
section: FwIndexType @< Section grouping to configure
enable: Fw.Enabled @< Section enabled or disabled
) \
opcode 2

@ Enable / disable telemetry of a group on a section
Copy link
Collaborator

Choose a reason for hiding this comment

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

Formatting, left align the @

async command ENABLE_GROUP(
section: FwIndexType @< Section grouping to configure
tlmGroup: FwChanIdType @< Group Identifier
enable: Fw.Enabled @< Section enabled or disabled
) \
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

section: FwIndexType @< Section grouping
tlmGroup: FwChanIdType @< Group Identifier
enable: Fw.Enabled @< Section enabled or disabled
) \
opcode 4

@ Set Min and Max Deltas between successive packets
async command SET_GROUP_DELTAS(
Copy link
Collaborator

Choose a reason for hiding this comment

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

Call this CONFIGURE_GROUP_RATES

section: FwIndexType @< Section grouping
tlmGroup: FwChanIdType @< Group Identifier
rateLogic: RateLogic @< Rate Logic
minDelta: U32 @< Minimum Sched Ticks to send packets on updates
maxDelta: U32 @< Maximum Sched Ticks to send packets
) \
opcode 5



# ----------------------------------------------------------------------
# Events
# ----------------------------------------------------------------------
Expand Down Expand Up @@ -111,12 +180,21 @@ module Svc {
id 4 \
format "Could not find packet ID {}"

event SectionUnconfigurable(
section: FwIndexType @< The Section
enable: Fw.Enabled @< Attempted Configuration
) \
severity warning low \
id 5 \
format "Section {} is unconfigurable and cannot be set to {}"

# ----------------------------------------------------------------------
# Telemetry
# ----------------------------------------------------------------------

@ Telemetry send level
telemetry SendLevel: FwChanIdType id 0
telemetry GroupConfigs: SectionConfigs id 0
telemetry SectionEnabled: SectionEnabled id 1

}

Expand Down
64 changes: 60 additions & 4 deletions Svc/TlmPacketizer/TlmPacketizer.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@
#ifndef TlmPacketizer_HPP
#define TlmPacketizer_HPP

#include "Fw/Types/EnabledEnumAc.hpp"
#include "Os/Mutex.hpp"
#include "Svc/TlmPacketizer/TlmPacketizerComponentAc.hpp"
#include "Svc/TlmPacketizer/TlmPacketizerTypes.hpp"
Expand All @@ -19,6 +20,8 @@
namespace Svc {

class TlmPacketizer final : public TlmPacketizerComponentBase {
friend class TlmPacketizerTester;

public:
// ----------------------------------------------------------------------
// Construction, initialization, and destruction
Expand Down Expand Up @@ -57,6 +60,12 @@
U32 context /*!< The call order*/
) override;

//! Handler implementation for controlIn
void controlIn_handler(FwIndexType portNum, //!< The port number
FwIndexType section, //!< Section to enable (Primary, Secondary, etc...)
const Fw::Enabled& enabled //!< Enable / Disable Section
) override;

//! Handler implementation for pingIn
//!
void pingIn_handler(const FwIndexType portNum, /*!< The port number*/
Expand All @@ -82,9 +91,44 @@
//! Force a packet to be sent
void SEND_PKT_cmdHandler(const FwOpcodeType opCode, /*!< The opcode*/
const U32 cmdSeq, /*!< The command sequence number*/
U32 id /*!< The packet ID*/
const U32 id, /*!< The packet ID*/
const FwIndexType section /*!< Section to emit packet*/
) override;

//! Handler implementation for command ENABLE_SECTION
void ENABLE_SECTION_cmdHandler(FwOpcodeType opCode, //!< The opcode
U32 cmdSeq, //!< The command sequence number
FwIndexType section, //!< Section to configure
Fw::Enabled enable //!< Active Sending Group
) override;

//! Handler implementation for command ENABLE_GROUP
//!
//! Enable / disable telemetry of a group on a section
void ENABLE_GROUP_cmdHandler(FwOpcodeType opCode, //!< The opcode
U32 cmdSeq, //!< The command sequence number
FwIndexType section, //!< section grouping to configure
FwChanIdType tlmGroup, //!< Group Level
Fw::Enabled enable //!< Active Sending Group
) override;

//! Handler implementation for command FORCE_GROUP
void FORCE_GROUP_cmdHandler(FwOpcodeType opCode, //!< The opcode
U32 cmdSeq, //!< The command sequence number
FwIndexType section, //!< Section to configure
FwChanIdType tlmGroup, //!< Group Level
Fw::Enabled enable //!< Active Sending Group
) override;

//! Handler implementation for command SET_GROUP_DELTAS
void SET_GROUP_DELTAS_cmdHandler(FwOpcodeType opCode, //!< The opcode
U32 cmdSeq, //!< The command sequence number
FwIndexType section, //!< Section to configure
FwChanIdType tlmGroup, //!< Group Level
Svc::TlmPacketizer_RateLogic rateLogic, //!< Rate Logic
U32 minDelta,
U32 maxDelta) override;

// number of packets to fill
FwChanIdType m_numPackets;
// Array of packet buffers to send
Expand All @@ -96,7 +140,6 @@
FwChanIdType id; //!< channel id
FwChanIdType level; //!< channel level
bool updated; //!< if packet had any updates during last cycle
bool requested; //!< if the packet was requested with SEND_PKT in the last cycle
};

// buffers for filling with telemetry
Expand Down Expand Up @@ -139,8 +182,21 @@

TlmEntry* findBucket(FwChanIdType id);

FwChanIdType m_startLevel; //!< initial level for sending packets
FwChanIdType m_maxLevel; //!< maximum level in all packets
TlmPacketizer_SectionEnabled m_sectionEnabled{};

TlmPacketizer_SectionConfigs m_groupConfigs{};

enum UpdateFlag : U8 {
NEVER_UPDATED = 0,
PAST = 1,
NEW = 2,
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.
U32 prevSentCounter = std::numeric_limits<U32>::max(); // Prevent Start up spam
UpdateFlag updateFlag = UpdateFlag::NEVER_UPDATED;
} m_packetFlags[NUM_CONFIGURABLE_TLMPACKETIZER_SECTIONS][MAX_PACKETIZER_PACKETS]{};
};

} // end namespace Svc
Expand Down
Binary file modified Svc/TlmPacketizer/docs/img/TlmPacketizerBDD.jpg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
41 changes: 39 additions & 2 deletions Svc/TlmPacketizer/docs/sdd.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,8 @@

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.

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.


## 2. Requirements

The requirements for `Svc::TlmPacketizer` are as follows:
Expand All @@ -29,26 +31,60 @@ The `Svc::TlmPacketizer` component has the following component diagram:

#### 3.1.2 Ports

The `Svc::TlmChan` component uses the following port types:
The `Svc::TlmPacketizer` component uses the following port types:

Port Data Type | Name | Direction | Kind | Usage
-------------- | ---- | --------- | ---- | -----
[`Svc::Sched`](../../Sched/docs/sdd.md) | Run | Input | Asynchronous | Execute a cycle to write changed telemetry channels
[`Fw::Tlm`](../../../Fw/Tlm/docs/sdd.md) | TlmRecv | Input | Synchronous Input | Update a telemetry channel
[`Fw::Com`](../../../Fw/Com/docs/sdd.md) | PktSend | Output | n/a | Write a set of packets with updated telemetry
`Svc::EnableSection` | controlIn | Input | Asynchronous | Enable / Disable sections of telemetry groups

#### 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!

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.

Telemetry Section: A resampling of telemetry groups. Each Section typically is destined for a different destination (e.g. realtime downlink vs store & forward)

#### 3.2 Functional Description

The `Svc::TlmPacketizer` component has an input port `TlmRecv` that receives channel updates from other components in the system. These calls from the other components are made by the component implementation classes, but the generated code in the base classes takes the type specific channel value and serializes it, then makes the call to the output port. The `Svc::TlmPacketizer` component can then store the channel value as generic data. The channel ID is used to look up offsets for the channel in each of the defined packets. A channel can be defined in more than one packet. The time tag is stripped from the incoming telemetry value. The time tag of the channel will become the time tag of the entire frame when it is sent.

The implementation uses a hashing function to find the location of telemetry channels that is tuned in the configuration file `TlmPacketizerImplCfg.hpp`. See section 3.5 for description.

When a call to the `Run()` interface is called, the packet writes are locked and all the packets are copied to a second set of packets. Once the copy is complete, the packets writes are unlocked. The destination packet set gets updated with the current time tag and are sent out the `pktSend()` port.
When a call to the `Run()` interface is called, the packet writes are locked and all the packets are copied to a second set of packets. Once the copy is complete, the packets writes are unlocked. The destination packet set gets updated with the current time tag and are sent out the `pktSend` port.

Each telemetry group, depending on section and group configurations, are sent out on several indices of the `pktSend` port. Since each section holds a continuous range of ports, a packet with group 1 with a TlmPacketizer configuration of 3 sections (0, 1, and 2), and a max group of 3 (0 and 3 inclusive), will be sent on 3 indices: 1, 5, and 9. Port invocations to `controlIn` or the command, `ENABLE_SECTION` are used to enable / disable each section, supporting downstream components that rely on different samplings of groups of telemetry. Each group instance is separately sampled from each other, allowing for individual rates per section and group.

### 3.3 Scenarios

#### 3.3.1 External User Option

#### 3.3.2 Configuring Telemetry Group Rates Per Port

The `Svc::TlmPacketizer` is configurable to have multiple `PktSend` group outputs using the fpp constant, `MAX_CONFIGURABLE_TLMPACKETIZER_GROUP`. Doing so allows each packet group have different configurations for each `PktSend` output section.

`PktSend` output is ordered by `[section][group]`, where within each section telemetry groups are emitted. Each group within each section are separately sampled, and have their own configuration rates and logic independent from each other.

Each group is configured with min/max delta parameters, as well as a logic gate determining its output rate behavior. Deltas are counters of sched ticks invoked through the `run()` port. Min Delta is the least number of `Run()` invocations between successive packets before a packet with an updated channel is allowed to be sent. Updated packets will not be sent until their counter reaches Min Delta. This mitigates against telemetry spam while allowing users to benefit from asynchronously updating channels (e.g. those that don't occupy a set schedule). Max Delta is the maximum number of `Run()` invocations between successive packets. Upon reaching this counter, the packet would be sent, regardless of change.

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.

* `ON_CHANGE_MIN_OR_EVERY_MAX`: Packet will be sent on updates at at least Min Delta intervals, or at most Max Delta intervals.

Groups are configured using the `SET_GROUP_DELTAS` command.

#### 3.3.3 Switching Telemetry Sections
`Svc::TlmPacketizer` also has a configurable constant `NUM_CONFIGURABLE_TLMPACKETIZER_SECTIONS` that allows separately streamed outputs of packets of the same group. This is useful for downstream components that would handle different telemetry groups at different rates. Examples of this configuration includes live telemetry throughput, detailed sim reconstruction, or critical data operations.

Telemetry sections are enabled and disabled upon spacecraft state transitions through `controlIn()` port invocations or via operator commands. A section and group pair must be both enabled to emit a telemetry packet.
* `ENABLE_SECTION` / `controlIn()`: Enable / Disable telemetry sections.
* `ENABLE_GROUP`: Within a section, Enable / Disable a telemetry group.
* `FORCE_GROUP`: If set to Enabled, telemetry of the chosen group in a section will be emitted regardless if the section or group within the section is disabled.

### 3.4 State

`Svc::TlmPacketizer` has no state machines.
Expand All @@ -71,4 +107,5 @@ To see unit test coverage run fprime-util check --coverage
Date | Description
---- | -----------
12/14/2017 | Initial version
01/23/2026 | Added group level rate logic

Loading
Loading