Skip to content

Commit be87252

Browse files
secupclaude
andcommitted
Fix false-sync escalation blocking ACK decode and two OFDM code bugs
- Add LLR quality gate before 4-CW escalation in streaming decoder: false syncs from fading-corrupted LTS produce |llr|_avg ~1.0, while legitimate interleaved data has |llr|_avg > 3.0. Escalating on garbage wasted ~1.5s on 4x failed LDPC (50 iters each), blocking the decoder from processing real ACK frames and causing cascading timeout retransmissions. R1/4 file transfer retransmissions: 21 -> 8, timeouts: 18 -> 0. - Fix ConnectFrame re-encode using wrong code rate (streaming_encoder.cpp): after patching total_cw field, re-encode incorrectly used code_rate_ instead of CodeRate::R1_4 for control frames. - Deduplicate PILOT_RNG_SEED constant (modulator.cpp): removed duplicate definition, now uses single source from demodulator_constants.hpp. Divergence would silently break TX/RX pilot sequence matching. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
1 parent 6f6a452 commit be87252

File tree

3 files changed

+31
-9
lines changed

3 files changed

+31
-9
lines changed

src/gui/modem/streaming_decoder.cpp

Lines changed: 27 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1174,10 +1174,35 @@ void StreamingDecoder::decodeCurrentFrame() {
11741174
}
11751175

11761176
if (!peek_fell_through) {
1177+
// Check LLR quality before expensive 4-CW escalation.
1178+
// False syncs (e.g. from fading-corrupted LTS) produce low |llr|_avg (~1.0),
1179+
// while legitimate interleaved data frames have |llr|_avg > 3.0.
1180+
// Escalating on garbage wastes ~1.5s on 4x failed LDPC (50 iters each),
1181+
// blocking the decoder from processing real frames arriving in the buffer.
1182+
float llr_abs_sum = 0.0f;
1183+
size_t llr_count = std::min(soft_bits.size(), LDPC_BLOCK);
1184+
for (size_t i = 0; i < llr_count; ++i) {
1185+
llr_abs_sum += std::abs(soft_bits[i]);
1186+
}
1187+
float llr_abs_avg = llr_abs_sum / static_cast<float>(llr_count);
1188+
1189+
constexpr float MIN_LLR_FOR_ESCALATION = 3.0f;
1190+
if (llr_abs_avg < MIN_LLR_FOR_ESCALATION) {
1191+
LOG_MODEM(INFO, "[%s] OFDM CW0 peek: decode failed, |llr|_avg=%.1f too low — skipping 4-CW escalation (likely false sync)",
1192+
log_prefix_.c_str(), llr_abs_avg);
1193+
{
1194+
std::lock_guard<std::mutex> lock(buffer_mutex_);
1195+
correlation_pos_ = (sync_position_ + frame_len) % MAX_BUFFER_SAMPLES;
1196+
last_decoded_sync_pos_ = sync_position_;
1197+
}
1198+
state_ = DecoderState::SEARCHING;
1199+
return;
1200+
}
1201+
11771202
pending_total_cw_ = v2::FIXED_FRAME_CODEWORDS; // 4
11781203
state_ = DecoderState::SYNC_FOUND;
1179-
LOG_MODEM(INFO, "[%s] OFDM CW0 peek: decode failed, escalating to %d CWs",
1180-
log_prefix_.c_str(), pending_total_cw_);
1204+
LOG_MODEM(INFO, "[%s] OFDM CW0 peek: decode failed (|llr|_avg=%.1f), escalating to %d CWs",
1205+
log_prefix_.c_str(), llr_abs_avg, pending_total_cw_);
11811206
return;
11821207
}
11831208
}

src/gui/modem/streaming_encoder.cpp

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -609,8 +609,8 @@ Bytes StreamingEncoder::encodeFrameBytes(const Bytes& frame_data) {
609609
uint16_t fcrc = v2::ControlFrame::calculateCRC(tx_data.data(), fcrc_offset);
610610
tx_data[fcrc_offset] = (fcrc >> 8) & 0xFF;
611611
tx_data[fcrc_offset + 1] = fcrc & 0xFF;
612-
// Re-encode with patched header
613-
cws = v2::encodeFrameWithLDPC(tx_data, code_rate_);
612+
// Re-encode with patched header (always R1/4 for control frames)
613+
cws = v2::encodeFrameWithLDPC(tx_data, CodeRate::R1_4);
614614
}
615615

616616
Bytes encoded;

src/ofdm/modulator.cpp

Lines changed: 2 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
#include "ultra/ofdm.hpp"
44
#include "ultra/dsp.hpp"
55
#include "ultra/logging.hpp"
6+
#include "demodulator_constants.hpp"
67
#include <random>
78

89
namespace ultra {
@@ -34,10 +35,6 @@ Complex qam16_point(int bits) {
3435
return Complex(levels[i_bits] * QAM16_SCALE, levels[q_bits] * QAM16_SCALE);
3536
}
3637

37-
// Pilot sequence RNG seed - must match between modulator and demodulator
38-
// Using a fixed seed ensures TX/RX pilot sequences are identical
39-
constexpr uint32_t PILOT_RNG_SEED = 0x50494C54; // "PILT" in ASCII
40-
4138
// 32-QAM rectangular constellation (5 bits per symbol)
4239
// Uses 8×4 grid: 8 Q levels × 4 I levels = 32 points
4340
// Gray-coded for minimum bit errors on adjacent symbol errors
@@ -194,7 +191,7 @@ struct OFDMModulator::Impl {
194191

195192
// Pilot sequence: known BPSK pattern (pseudo-random but deterministic)
196193
pilot_sequence.resize(pilot_carrier_indices.size());
197-
std::mt19937 rng(PILOT_RNG_SEED);
194+
std::mt19937 rng(demod_constants::PILOT_RNG_SEED);
198195
for (size_t i = 0; i < pilot_sequence.size(); ++i) {
199196
pilot_sequence[i] = (rng() & 1) ? Complex(1, 0) : Complex(-1, 0);
200197
}

0 commit comments

Comments
 (0)