Skip to content

Introduction of downlinkHandling module to communication architecture#1323

Draft
dhutererprats wants to merge 10 commits intodevelopfrom
downlinkHandling
Draft

Introduction of downlinkHandling module to communication architecture#1323
dhutererprats wants to merge 10 commits intodevelopfrom
downlinkHandling

Conversation

@dhutererprats
Copy link
Contributor

  • Tickets addressed: TBD (bsk-xxxx)
  • Review: By commit
  • Merge strategy: Merge (no squash)

Description

This PR introduces the downlinkHandling communication module and all supporting interfaces, tests, and documentation required to use and validate it in Basilisk.

The module maps link quality from linkBudget into realistic downlink outcomes, including:

  • BER/PER from CNR and user-selected bit rate
  • retry-limited ARQ success/drop behavior
  • attempted, delivered, dropped, and storage-removal data rates
  • storage-aware downlink gating and drain behavior
  • receiver-path selection using CNR1/CNR2 and antenna RX state

Core implementation and integration work includes:

  • New downlink message payload and diagnostics field updates:

    • src/architecture/msgPayloadDefC/DownlinkHandlingMsgPayload.h
  • New/hardened downlinkHandling module implementation and interface:

    • src/simulation/communication/downlinkHandling/downlinkHandling.h
    • src/simulation/communication/downlinkHandling/downlinkHandling.cpp
    • src/simulation/communication/downlinkHandling/downlinkHandling.i
    • src/simulation/communication/downlinkHandling/Custom.cmake
  • Unit test coverage:

    • src/simulation/communication/downlinkHandling/_UnitTest/test_downlinkHandling.py
  • Documentation and figures:

    • src/simulation/communication/downlinkHandling/downlinkHandling.rst
    • src/simulation/communication/downlinkHandling/_Documentation/Images/DownlinkHandlingFlow.svg
    • src/simulation/communication/downlinkHandling/_Documentation/Images/DownlinkHandlingReliabilityChain.svg
  • Release note snippet:

    • docs/source/Support/bskReleaseNotesSnippets/downlinkHandling.rst

Commit organization is by feature layer:

  1. Core module + API hardening + removal policy + payload updates
  2. Unit test expansion and debug toggle support
  3. Documentation alignment and release-note snippet

Verification

Validation commands for this branch:

  • cmake --build dist3 -j4
  • .venv/bin/pytest -q src/simulation/communication/downlinkHandling/_UnitTest/test_downlinkHandling.py

Test coverage includes:

  • C++ output parity with Python-equivalent BER/PER/ARQ formulation
  • invalid-link zero-flow behavior
  • retry-cap behavior and drop probability effects
  • removal policy behavior (REMOVE_ATTEMPTED vs REMOVE_DELIVERED_ONLY)
  • storage-limited capping and drain behavior
  • auto and forced receiver-path selection (CNR1/CNR2, antenna RX states)
  • invalid setter input rejection and retained prior valid config
  • duplicate storage-message registration rejection
  • largest-partition selection across multiple storage status inputs

Documentation

Documentation delivered in:

  • src/simulation/communication/downlinkHandling/downlinkHandling.rst

It includes:

  • model scope, assumptions, and integration role
  • implemented equations (C/N0, Eb/N0, BER, PER, retry-limited ARQ, throughput scaling)
  • message interfaces and outputs
  • removal-policy semantics
  • storage-selection behavior and integration caveats
  • usage and validation guidance

Future work

  • Add explicit packet-level ACK/NACK timing model (current ARQ is expectation-based).
  • Add optional coding/modulation and empirical BER-table support.

@dhutererprats dhutererprats self-assigned this Mar 11, 2026
@dhutererprats dhutererprats added the enhancement New feature or request label Mar 11, 2026
@dhutererprats dhutererprats force-pushed the downlinkHandling branch 2 times, most recently from f6af0cf to 3be776c Compare March 11, 2026 02:35
@dhutererprats
Copy link
Contributor Author

@codex review

Copy link

@chatgpt-codex-connector chatgpt-codex-connector bot left a comment

Choose a reason for hiding this comment

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

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 3be776cccd

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment on lines +195 to +197
void DownlinkHandling::customWriteMessages(uint64_t CurrentClock)
{
this->downlinkOutMsg.write(&this->downlinkOutBuffer, this->moduleID, CurrentClock);

Choose a reason for hiding this comment

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

P2 Badge Clear stale diagnostics when the node is disabled

When this module is turned off through nodeStatusInMsg (dataStatus <= 0), DataNodeBase skips evaluateDataModel, so downlinkOutBuffer is never refreshed for that step; however customWriteMessages still publishes the previous buffer values. In that scenario, downstream consumers can read stale non-zero throughput/link fields even though the node is disabled and nodeDataOutMsg has been zeroed, which can mislead mode logic and telemetry analysis.

Useful? React with 👍 / 👎.

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 was real: when nodeStatusInMsg.deviceCmd <= 0, DataNodeBase skips evaluateDataModel(), so downlinkOutBuffer could remain stale while still being published.

I fixed this by explicitly clearing downlinkOutBuffer inside customWriteMessages() when dataStatus <= 0 before writing downlinkOutMsg. This prevents stale non-zero link/throughput diagnostics from being emitted while the node is off.

I also added a regression test (test_downlink_disabling_node_clears_diagnostics) that toggles the node from ON to OFF in one run and verifies the downlink diagnostics are zeroed after disable.

@dhutererprats dhutererprats force-pushed the downlinkHandling branch 2 times, most recently from 5ba35e8 to edec469 Compare March 12, 2026 01:01
@dhutererprats
Copy link
Contributor Author

@codex review

Copy link

@chatgpt-codex-connector chatgpt-codex-connector bot left a comment

Choose a reason for hiding this comment

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

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: edec469cda

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

{
*dataUsageMsg = this->nodeDataOutMsg.zeroMsgPayload;

double computedTimeStep = currentTime - this->previousTime; // [s]

Choose a reason for hiding this comment

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

P1 Badge Advance timing state across disabled updates

computedTimeStep is calculated from previousTime, but previousTime is only updated inside evaluateDataModel() when the node is active. When nodeStatusInMsg disables this model for multiple ticks, re-enabling it makes the next active step integrate the entire off-duration as if downlink was running, which inflates removedBitsThisStep and all cumulative bit counters in a single cycle. Updating the timing state while disabled (or resetting/clamping on re-enable) avoids this off-time throughput corruption.

Useful? React with 👍 / 👎.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Agreed, this was valid.

previousTime was only advanced in evaluateDataModel(), so disabled periods could be lumped into the first re-enabled step.

Fixed by advancing timing while disabled in customWriteMessages():

  • previousTime = CurrentClock * NANO2SEC
  • currentTimeStep = 0.0

I also added a regression test (test_downlink_reenable_does_not_integrate_disabled_downtime) that disables for multiple ticks, re-enables, and verifies no off-time burst in timestep/cumulative removal, so this issue should be fixed now

@dhutererprats dhutererprats force-pushed the downlinkHandling branch 2 times, most recently from 72e48b4 to 76f68ab Compare March 12, 2026 01:39
@dhutererprats
Copy link
Contributor Author

@codex review

@dhutererprats dhutererprats moved this to 🏗 In progress in Basilisk Mar 12, 2026
Copy link

@chatgpt-codex-connector chatgpt-codex-connector bot left a comment

Choose a reason for hiding this comment

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

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 76f68abd32

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment on lines +236 to +237
this->setDataNameFromStorageSelection(selection, this->nodeDataName, sizeof(this->nodeDataName));
std::strncpy(dataUsageMsg->dataName, this->nodeDataName, sizeof(dataUsageMsg->dataName) - 1);

Choose a reason for hiding this comment

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

P1 Badge Route removals to a unique storage unit

Selecting a target partition across multiple storageUnitInMsgs is unsafe because the emitted DataNodeUsageMsgPayload is keyed only by dataName; if two connected storage units share a partition name (common with default "STORED DATA"), both units will consume the same negative baud command. This silently drains non-selected buffers and corrupts downlink/removal accounting in multi-storage scenarios, since downstream storage routing matches by name only (DataStorageUnitBase::messageInStoredData).

Useful? React with 👍 / 👎.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I addressed it by adding a uniqueness guard in downlinkHandling before applying removal:

  • if the selected dataName is not unique across linked storageUnitInMsgs, removal is forced to zero for that step and a BSK_WARNING is emitted in that case to make the configuration issue visible.
  • this will prevent ambiguous negative-baud routing from draining multiple storage units unintentionally

I also added a regression test:

  • test_downlink_ambiguous_multi_storage_name_blocks_removal

And updated the module docs to describe this safeguard and the recommendation to keep partition names globally unique across linked storage units.

dhutererprats added a commit that referenced this pull request Mar 12, 2026
dhutererprats added a commit that referenced this pull request Mar 12, 2026
dhutererprats added a commit that referenced this pull request Mar 12, 2026
dhutererprats added a commit that referenced this pull request Mar 12, 2026
dhutererprats added a commit that referenced this pull request Mar 12, 2026
@dhutererprats
Copy link
Contributor Author

@codex review

@chatgpt-codex-connector
Copy link

Codex Review: Didn't find any major issues. Another round soon, please!

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

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

Labels

enhancement New feature or request

Projects

Status: 🏗 In progress

Development

Successfully merging this pull request may close these issues.

1 participant