Concept: NAK timing - Deferred vs Asynchronous mode
Used in: Case B Outcome 2 (Deferred, post-EOF repair), and the frozen/multi-pass scenarios in Case A's Variant and Case C (Asynchronous, pre-EOF)
CFDP has several distinct NAK-triggering modes under "Incremental Lost Segment Detection Procedures", plus a separate "Deferred" procedure. This page covers the two that actually matter for our setup:
- Deferred - only after EOF has been received. Genuinely needs a stored timer (an expiry timestamp).
- Asynchronous - can fire before EOF is ever received. For a batch/pass-oriented receiver like PLDP, needs no stored timer at all - see Section B below.
There's also Immediate mode, which assumes something closer to continuous/live gap monitoring ("each gap... detected... shall cause a NAK sequence immediately"). That doesn't match PLDP's architecture (batch processing, not live) - noted here for completeness, not used in the examples below.
Section A - Deferred mode (post-EOF)
Part 1 - The pure fundamental mechanic
Once EOF (No error) arrives, if data is still missing, two things happen:
- Immediately: check completeness. If gaps exist that weren't already covered by a prior NAK, issue an immediate NAK for exactly those gaps.
- Start a timer. The expiry period is entirely the implementation's choice, and can even change between rounds.
When that timer expires: re-check completeness. If still incomplete, issue a NAK whose scope spans the entire file (not just the newest gap), and reset the timer. If complete, nothing happens - the cycle simply stops on its own.
Worked example
A 100-octet file, NAK timer expiry set to 5 minutes (purely illustrative - the spec leaves this to the implementation):
| Time | What happens |
|---|---|
| T = 0 | EOF(No error) arrives. Bytes 40-60 missing. Immediate NAK: scope [0,100), segment request [40,60). Timer starts, expires at T=5min. |
| T = 5min | Timer expires. Re-check: 40-50 arrived, 50-60 still missing. New NAK: scope [0,100) (whole file, per the rule), segment request just [50,60). Timer resets, next expiry T=10min. |
| T = 10min | Repeat, until the completeness check finally passes and the loop stops. |
The sibling bound is the NAK Limit - an implementation-specific cap on how many times this can repeat before a fault fires. That fault is exactly what can trigger Case B's Outcome 1 (Cancel). This limit is shared across all modes, not Deferred-specific.
Part 2 - Applied to PLDP's post-LOS batch model
Key reframing: CFDP never requires this timer to be continuously ticking in a live process. It only requires that when the entity next looks, it correctly knows whether the deadline has passed. That's a wall-clock comparison, not a running countdown - and that's exactly what makes it compatible with a receiver that only does its CFDP-level work at discrete moments, like PLDP does post-LOS.
The natural implementation: store the NAK timer's expiry as an absolute timestamp in the transaction's persisted state, and evaluate it lazily - only when PLDP happens to process that transaction again anyway.
Worked example, real clock times - LEO cadence
LEO orbital period is famously ~90 minutes - that's the natural "how often could a pass even recur" baseline for this kind of mission, so this example uses it directly instead of an arbitrary gap. (Whether a single ground station actually sees every orbit, or only some of them, depends on ground-station coverage/latitude - still open, Q11. This assumes a fairly dense schedule, typical of LEO imaging missions with good coverage - treat the exact cadence as illustrative until Q11 lands.)
- Pass 1, 10:00-10:10 (10 min pass, LOS at 10:10). PLDP's post-LOS processing actually runs at 10:15 (real lag). Finds EOF + gap [40,60), issues the Immediate NAK, and writes
nak_timer_expires_at = 10:15 + 90min = 11:45into the transaction's stored state. Nothing runs or watches this in between. - Pass 2 (next orbit, ~90 min later), 11:30-11:40 (LOS 11:40). Say the retransmission for [40,60) arrives this pass. PLDP processes at 11:45.
- At 11:45, PLDP notices
nak_timer_expires_at(11:45) has just reached/passed - "expired" purely as a fact discovered at this check, not something that fired an alarm on its own. - PLDP is also processing this pass's new File Data right now, and the completeness check shows the file is now complete. So the "timer expired, need a new NAK" branch is moot - normal closure happens instead.
- At 11:45, PLDP notices
- Alternative branch - if the repair did not arrive in Pass 2: the 11:45 check finds both "still incomplete" and "timer already expired" - issues a fresh whole-file-scope NAK, and writes a new
nak_timer_expires_at = 11:45 + 90min = 13:15for Pass 3 (~13:00-13:10, processed ~13:15) to discover.
The "absolute meaning" separation
The transaction's state (its byte-range map - what's actually been received) is timeless, absolute data; it doesn't care about clocks at all. The timer is purely a wall-clock-driven trigger layered on top, deciding when to act on that state. PLDP has to persist both correctly - the byte-range map (already an established need) and the timer's expiry timestamp - as part of the same stored transaction record, so that whichever pass next touches this transaction can correctly evaluate both "what's missing" and "is it time to re-NAK" from cold storage, with no assumption that anything ran continuously in between.
Section B - Asynchronous mode (pre-EOF, frozen/multi-pass transactions)
This is the mode that actually applies to a transaction that freezes or is deliberately paused/scheduled across multiple passes before it ever reaches EOF - Case A's Variant (LOS cuts off the last transaction) and Case C (large image, deliberately multi-pass).
Why not Immediate mode
Immediate mode fires "as each gap is detected," which implicitly assumes something close to live, continuous monitoring. PLDP has no such thing - it evaluates a whole pass's worth of data in one batch scan, once, after LOS. Immediate mode's framing doesn't match this architecture.
Why Asynchronous mode fits
The spec's own example of such an event: "the opening of a window of opportunity, for missions having sporadic contact." That is close to a literal description of a pass occurring. PLDP's post-LOS processing run is the external event - one event, one NAK sequence, which naturally lists however many gaps are currently known (or is empty, per the spec, "a single NAK PDU containing no segment requests," if nothing is missing yet).
Nuance - genuine loss vs. "not sent yet"
This turns out to be a hard protocol rule, not just a reasonable design choice. Per §4.6.4.3.3(d)(3): before EOF has been received, a NAK sequence's end of scope is "the current reception progress at the time of the event that caused issuance of the NAK sequence" - it cannot extend into data that hasn't been sent yet, full stop. Combined with the gap-detection conditions themselves (§4.6.4.3.1), a "gap" is only ever defined between two already-received extents - there's no protocol concept of a gap ahead of current progress. So PLDP's Asynchronous-mode NAK naturally only ever covers an interior gap (data exists on both sides - unambiguous evidence something was skipped); a trailing unsent range (nothing yet, but nothing after it either) is left to the sender's own natural continuation once it resumes - not because we chose to design it that way, but because CFDP itself won't let a pre-EOF NAK reach past current progress. The worked examples below follow this mandatory behavior.
Worked example 1 - Case A Variant style (interior gap, then freeze)
Pass 1, 10:00-10:10 (LOS 10:10). Tx4 sends File Data covering [0,20) and [30,45) - bytes [20,30) were lost to RF noise (data exists on both sides, so this is an unambiguous interior gap). The pass then ends before the sender reaches [45,100) - not a loss, just not yet attempted.
PLDP processes at 10:15 (the external event). Finds the confirmed interior gap [20,30) → issues an Asynchronous NAK sequence right now: segment request [20,30) only. The trailing [45,100) is not included - that's the sender's own resume job, not a repair.
This NAK sits ready in PLDP's outbound queue - transmitted on whatever the next real uplink opportunity is (Pass 2's S-band window, per the routing model in 07_strategic_scheduling_nak_routing.txt).
Pass 2, ~11:30-11:40. Satellite resumes Tx4 from its freeze: retransmits [20,30) (servicing the NAK, if it arrived in time) and naturally continues with [45,100) as ordinary continuation - not because of any NAK, just resuming where it left off. Tx4 completes.
Worked example 2 - Case C style (deliberate multi-pass large image)
Applies to either of Case C's candidates: a Tiling chunk sent during some pass, or a stretch of the single Pause/Resume transaction, can each have their own interior gap (some frames lost in transit) discovered before that transaction's own EOF - which, for a genuinely multi-pass delivery, might not arrive until the last of several scheduled passes either way. PLDP's post-LOS processing (the event) finds this confirmed gap and issues an Asynchronous NAK for it immediately - well before that EOF.
This NAK simply joins the queue of things needing an uplink opportunity. Since the transaction is deliberately paused between scheduled passes (not frozen by accident), the request competes for the same uplink windows as everything else already discussed in Case B/Topic 7 - repair and deliberate continuation aren't functionally different once they're both just "pending uplink-bound content."
Connects to: the earlier open idea (question_bank.txt Q7.4) of caching an incoming NAK non-volatilely on the satellite if it arrives while the target transaction is itself frozen there. This is exactly where that becomes concrete rather than hypothetical - an Asynchronous-mode NAK generated in Pass 1 can easily arrive at a satellite that hasn't resumed Tx4 yet.