diff --git a/src/acquisition.cpp b/src/acquisition.cpp index fb006db..93640a5 100644 --- a/src/acquisition.cpp +++ b/src/acquisition.cpp @@ -143,32 +143,39 @@ std::uint32_t Acquisition::build_stamps(const udps_frame_t* frame, int time_sign stamps_.assign(n, 0.0); abs_stamps_.assign(n, 0.0); - // Relative mode: the stamp is a pure function of position and the *declared* rate. Nothing - // below this block runs -- not the arrival-cadence override, not the degeneracy guard, not - // the glitch bridge, not the monotonic clamp's reason for existing -- because a position - // cannot jitter, cannot fail to advance and cannot jump. - if (cfg_.relative_time) { - if (batch_ == 0) { - batch_ = n; + // Samples per packet, needed in both modes: relative stamping must advance `slot_base_` + // past the reserved hole before placing this frame's own stamps, and `on_data` must size + // the hole before calling this function -- both operations require knowing how many samples + // a missing packet represents, which is only knowable from the first frame that arrives. + if (batch_ == 0) { + batch_ = n; + if (cfg_.relative_time) { std::fprintf(stderr, "[udpscope] relative time: batch=%u samples/packet, rate=%.6g S/s declared\n", batch_, model_.partition().base_dt > 0.0 ? 1.0 / model_.partition().base_dt : 0.0); std::fflush(stderr); - } else if (n != batch_) { - // A frame whose length disagrees with the first one is an inconsistency in the - // producer, not a loss: the counter still names one packet. Warn once and keep the - // established batch, which is only used to size holes -- the timeline itself is built - // from the lengths actually seen, so it stays exact for this frame either way. - if (!warned_batch_) { - warned_batch_ = true; - std::fprintf(stderr, - "[udpscope] warning: packet carries %u samples but the stream's first " - "carried %u; keeping %u as the packet length a lost packet is worth.\n", - n, batch_, batch_); - std::fflush(stderr); - } } + } else if (n != batch_) { + // A frame whose length disagrees with the first one is an inconsistency in the + // producer, not a loss: the counter still names one packet. Warn once and keep the + // established batch, which is only used to size holes -- the timeline itself is built + // from the lengths actually seen, so it stays exact for this frame either way. + if (!warned_batch_) { + warned_batch_ = true; + std::fprintf(stderr, + "[udpscope] warning: packet carries %u samples but the stream's first " + "carried %u; keeping %u as the packet length a lost packet is worth.\n", + n, batch_, batch_); + std::fflush(stderr); + } + } + + // Relative mode: the stamp is a pure function of position and the *declared* rate. Nothing + // below this block runs -- not the arrival-cadence override, not the degeneracy guard, not + // the glitch bridge, not the monotonic clamp's reason for existing -- because a position + // cannot jitter, cannot fail to advance and cannot jump. + if (cfg_.relative_time) { double dt = model_.partition().base_dt; if (!(dt > 0.0)) dt = 1.0e-3; // The reserved hole for the missing packets advances `slot_base_` so this frame's