Case B - NAK-driven repair cascading into the forward pass schedule

A transaction falls behind on repairs and competes with the plan for future passes. Two ways it can end. constructed

The setup

Ground plans out roughly 10 passes ahead, based on how much data fits in each one. If a pass loses some data and needs repair (a NAK - "please resend this"), that repair has to compete for space in a schedule that was already made assuming nothing would go wrong. Eventually, for any one transaction stuck in this backlog, there are exactly two ways it can end - both shown below.

Outcome 1 - Give up: Cancel

This is a manual decision only - there's no autonomous auto-Cancel (that's deferred pending team discussion). Hitting the NAK Limit doesn't trigger Cancel by itself; it only raises an alert. The operator, working from PPS (a NAK backlog directly competes with PPS's own forward pass planning, so it's the natural place ops already looks), reviews the backlog PLDP surfaces - retry count, outstanding gap, elapsed passes - and decides. From PLDP's side this arrives as an abstract "Cancel Tx X" event/API call, not a CFDP-aware exchange - PLDP doesn't need to know the decision came from PPS specifically. Once decided, a Cancel.request is issued. ("Cancel" is the actual book term - "abort" doesn't appear anywhere in the spec.)

What actually happens

CFDP lets either entity initiate a cancel, and the two directions use genuinely different message sequences - not just "whoever decides sends the same thing." Since PLDP is the one deciding here (Q19), this uses the receiving-entity-initiated procedure, not the sending-entity one:

  • PLDP decides, closes its own copy of the transaction locally, and sends a single Finished (cancel) message straight to the satellite. No EOF(cancel) is involved in this direction at all - that PDU only appears in the opposite (satellite-initiated) flow.
  • How the satellite actually finds out: receiving that Finished (cancel) message is the instruction - the spec ties the satellite's own local cancellation directly to receiving it. It isn't told separately or asked to decide anything.
  • The satellite replies with a positively-acknowledged ACK (Finished), matching reason code - cancelling doesn't skip the confirmation round-trip, it just needs one PDU + one ACK in this direction, not two.
CCSDS 727.0-B-5 §4.11.2.3 (Notice of Cancellation at the receiving entity): PLDP issues Finished(cancel) directly, no EOF(cancel) needed. §4.6.6.2.1 (Cancel Response Procedures at the sending entity): "Receipt of a Finished (cancel) PDU shall cause the sending CFDP entity to issue a Notice of Completion (Canceled)" - this is literally how the satellite finds out.
Case B Cancel sequence diagram: PPS decides, PLDP issues Finished(cancel) directly to SAT, SAT closes locally on receipt and ACKs, with the sibling sending-entity-initiated path and Abandon fallback both noted

Rendered from diagram/case_b_cancel.puml (PlantUML)

Result: the transaction formally closes as CANCELLED on both sides, not complete. Whatever already passed its checksum check may still be usable downstream (same "usable before formal closure" idea from Case A) - but CFDP's own bookkeeping records this transfer as incomplete, on purpose.
What if cancelling itself goes wrong?

There's a second, more abrupt mechanism called Abandon - and it's genuinely different from Cancel, not just a stronger version of it. Abandon is purely local: the entity just gives up and tells its own local software, with no message exchange with the other side at all - the spec literally says the effect of this on the receiving user is "undefined."

Abandon isn't a general alternative to Cancel - it's specifically the fallback for when Cancel itself can't be completed cleanly (here, a fault while PLDP is transferring the Finished(cancel) message). Cancel is always the first choice; Abandon only fires when even that breaks down.

CCSDS 727.0-B-5 §4.11.2.3.2(d): "Any fault declared in the course of transferring this PDU [Finished (cancel)] must result in abandonment of the transaction."
What about the other direction - satellite-initiated cancel?

CFDP also allows the sending entity to initiate a cancel on its own - e.g. a local fault declared onboard. In that direction, the satellite would issue EOF (cancel) first (file size = progress so far, not the intended full size), and PLDP would react per the mirror-image procedure [§4.6.6.1]. That's not this case's actual trigger path - Q19 fixed the decision at ground/PPS - but it's a real, spec-supported path noted here for completeness, and it's what the diagram previously (incorrectly) modeled as the only flow.

Outcome 2 - Keep trying: NAK-based repair

Instead of giving up, the receiver just keeps asking for the missing pieces, across however many passes it takes, until the file is fully covered.

What actually happens

  • A pass sends the file, but some frames get lost along the way.
  • After that pass ends, PLDP notices the gaps once it processes the recording, and sends a NAK listing exactly the missing byte ranges - not the whole file, just what's actually gone.
  • That NAK travels the same route as everything else uplink-bound: PLDP → MCS → next S-band window → satellite.
  • The satellite resends only the requested ranges. In practice, this often can't happen the very next pass either - that pass's downlink time may already be spoken for by other planned content, so the actual repair can land two or more passes later.
  • If something's still missing after that, the cycle repeats - and each repeat sweep re-checks the whole file, not just the newest gap. There's a limit on how many times this is allowed to repeat before a fault fires - but that fault now only raises an alert to PPS, it doesn't automatically trigger Outcome 1 (Cancel). Ops still decides manually either way.
  • Once everything's finally accounted for, it closes normally - the same confirmation exchange from Case A, just arriving several passes later than usual.
CCSDS 727.0-B-5 §4.6.4.6 (Deferred mode): NAK sequences issued after EOF are scoped to the entire file on each repeat, not just the latest gap. §4.6.4.7 (NAK Limit): an implementation-specific cap on how many times this can repeat before a fault fires.

For the full mechanics of this timer - both the pure concept and how it maps onto PLDP's post-LOS batch processing - see the NAK Timer concept page.

Case B NAK repair sequence diagram: SAT, MCS, PLDP across multiple passes, NAK requesting missing ranges, retransmission, eventual completion

Rendered from diagram/case_b_nak_repair.puml (PlantUML)

"Keep trying" can itself be stood down and resumed

"Keep trying" doesn't have to mean continuously, uninterrupted retrying. Ops (via PPS) can decide to explicitly stand a repair down for a while - Suspend Tx X - if something else needs the pass bandwidth more urgently, then resume it later. This is where two separate NAK stores come in, confirmed via a conversation with PAVI, each solving a different timing gap:

StoreBridgesWhen it applies
Ground (PLDP)"gap detected" → "MCS has an uplink window"Always - the ordinary X-band-down/S-band-up scheduling gap
Satellite (onboard, non-volatile)"NAK arrived" → "Tx is actually resumed"Only when the Tx is deliberately Suspended when the NAK lands

Ordinary freeze/thaw (a pass simply ending, not a deliberate stand-down) needs neither store on the satellite side - thaw always happens right when the next contact opens, the same moment uplink becomes possible again, so a NAK sent "at the next window" never actually arrives to a still-frozen Tx. Only a deliberate Suspend creates real exposure to this.

Why this actually protects downlink bandwidth - not just a design choice, a hard guarantee: Suspend at the sending entity (SAT) requires it to "suspend transmission of Metadata PDU, file segments, and EOF PDU." While Tx X is suspended, SAT is protocol-barred from transmitting file segments for it, period - regardless of whether a NAK has arrived, is arriving, or already arrived. The repair literally cannot touch a future pass's downlink bandwidth while suspended; this isn't SAT choosing not to serve it, it's the sending entity being unable to.

What's spec-mandated vs. PAVI's implementation choice: the spec guarantees the transmission block above, but says nothing about how an inbound NAK PDU arriving during that suspended window actually gets handled - that's a genuine gap the spec leaves open. The non-volatile satellite-side store (confirmed via PAVI) is the implementation choice that fills it - not something the suspension procedures themselves require or even mention.

CCSDS 727.0-B-5 §4.11.2.6.1(a): Notice of Suspension at the Sending Entity - "suspend transmission of Metadata PDU, file segments, and EOF PDU." The hard guarantee behind the callout above.

The full stand-down/resume sequence, worked through

PLDP suspends itself too, immediately, for free - this is the part that actually holds the already-generated NAK. The moment ops decides to stand Tx X down, PLDP invokes Suspend.request locally on its own copy - no uplink needed, purely internal. Per §4.11.2.7(a), this "suspend[s] transmission of NAK PDUs" - the NAK sitting in PLDP's ground store is now blocked from being released, by protocol definition, not by choice. SAT still needs its own separate signal to suspend (the Q7.12 mechanism, which costs a round trip), so there's a brief window where PLDP is already suspended but SAT is still merely frozen - harmless, since PLDP isn't releasing the NAK during it anyway.

Release is tied to the target pass's downlink request, not to when the NAK was generated: when ops later decides "serve this repair in Pass X," that decision is the same moment PPS is already constructing Pass X's downlink request - not two separate events needing separate coordination. PLDP resumes itself locally at that moment (free, immediate; per §4.6.7.3.1(a) this "resume[s] transmission of NAK PDUs"), and hands the now-eligible NAK to MCS bundled with that same downlink request uplink, rather than as a separate transmission.

Why this matters for the satellite-side storage cost: this is what keeps the satellite-side NV store's job small. The NAK is never pushed up eagerly the moment it's generated - it sits for free on the ground for however long the stand-down lasts, and only travels up right before the pass it's meant for. So the satellite only ever holds it for at most the gap between that uplink landing and the target pass actually starting - not the whole suspension duration.

Two messages, one uplink event, two onboard routes: the NAK PDU (reserved CFDP-uplink APID, straight to the CFDP entity's queue) and the "Resume Tx X" command (ordinary PUS APID, through the command processor to the payload app, which locally invokes Resume.request) can ride the same uplink frame if ready together (ordinary packet-blocking) but take different paths once demuxed onboard by APID. A small race is possible - the NAK could get processed a moment before the Resume command finishes taking effect - but that's exactly what the satellite's NV store is for: a brief, bounded hold, not the open-ended one from before.

Case B suspend-and-resume repair diagram: PLDP suspends itself locally to block NAK release, ops decision to resume is bundled with PPS's downlink request for the target pass, NAK and resume command uplink together on different APIDs, brief race handled by the satellite's NV store as a bounded backstop

Rendered from diagram/case_b_suspend_repair.puml (PlantUML)

Deliberately out of scope: once Tx X resumes and the satellite starts serving its backlog of stored NAKs, the order/pacing it chooses is PAVI's team's onboard design responsibility - not something modeled here. Our diagram shows the store existing and holding-until-resume, not what happens inside it.
Result: the transaction formally closes as COMPLETE. Nothing was actually lost for good - the cost was time, not data.

How the two outcomes relate

These aren't two competing designs to pick between, the way Case A's batch-vs-pipeline question was. Both have to exist in the same implementation. Outcome 2 (keep trying) is the default behavior; Outcome 1 (Cancel) only happens when ops, via PPS, actively decides to invoke it - either because Outcome 2's NAK Limit fault raised an alert worth acting on, or because ops decides ahead of that limit that the backlog isn't worth it. Hitting the NAK Limit is a prompt to decide, not an automatic trigger - nothing switches from one outcome to the other by itself.

Open questions this case raised

Q20 - What should OUR actual give-up threshold be? CFDP doesn't hand us a default, and the decision is confirmed manual (Q19) - we still need guidance for PPS operators (a NAK-limit reference point, a time/pass-count rule, or pure judgment).
Not yet modeled: the real scheduling tradeoff of what gets bumped in Pass N+1/N+2 to make room for a repair. This case shows CFDP's mechanics, not the ground-planning decision of what loses its slot.
Q21 - Explicitly not ours to solve: how the satellite paces/orders serving its backlog of stored NAKs once resumed is PAVI's team's onboard design responsibility. Tracked here so it isn't lost.
Q7.5 - the ground-side NAK store itself is confirmed needed, but whether it must be non-volatile (survive a PLDP restart/power cycle) is still genuinely open - directly relevant here since Outcome 2's dual-store discussion leans on this store existing.