@@ -138,6 +138,10 @@ StreamingDecoder::~StreamingDecoder() {
138138 stop ();
139139}
140140
141+ void StreamingDecoder::setBurstInterleaveGroupSize (int size) {
142+ burst_group_size_ = std::clamp (size, 2 , 8 );
143+ }
144+
141145// ============================================================================
142146// AUDIO THREAD - Just buffer samples, nothing else
143147// ============================================================================
@@ -1975,16 +1979,20 @@ DecodeResult StreamingDecoder::decodeFrame(const std::vector<float>& soft_bits,
19751979// ============================================================================
19761980
19771981void StreamingDecoder::accumulateBurstFrames () {
1982+ const int burst_group_size = std::max (2 , burst_group_size_);
1983+ const int burst_timeout_ms =
1984+ static_cast <int >(BURST_TIMEOUT_MS_BASE * (burst_group_size / 4 .0f ));
1985+
19781986 // Timeout check
19791987 auto elapsed = std::chrono::duration_cast<std::chrono::milliseconds>(
19801988 std::chrono::steady_clock::now () - burst_start_time_).count ();
1981- if (elapsed > BURST_TIMEOUT_MS ) {
1989+ if (elapsed > burst_timeout_ms ) {
19821990 LOG_MODEM (WARN, " [%s] Burst group timeout: got %zu/%d frames" ,
1983- log_prefix_.c_str (), burst_soft_buffer_.size (), BURST_GROUP_SIZE );
1991+ log_prefix_.c_str (), burst_soft_buffer_.size (), burst_group_size );
19841992 // Discard — TX used 4-frame interleaving, partial is undecodable
19851993 {
19861994 std::lock_guard<std::mutex> slock (stats_mutex_);
1987- stats_.frames_failed += BURST_GROUP_SIZE ;
1995+ stats_.frames_failed += burst_group_size ;
19881996 }
19891997 burst_soft_buffer_.clear ();
19901998 {
@@ -2001,10 +2009,10 @@ void StreamingDecoder::accumulateBurstFrames() {
20012009 if (result == BurstFrameResult::FAILED) {
20022010 // Hard failure (energy lost or process error) — abort immediately
20032011 LOG_MODEM (WARN, " [%s] Burst group aborted: hard failure at frame %zu/%d" ,
2004- log_prefix_.c_str (), burst_soft_buffer_.size () + 1 , BURST_GROUP_SIZE );
2012+ log_prefix_.c_str (), burst_soft_buffer_.size () + 1 , burst_group_size );
20052013 {
20062014 std::lock_guard<std::mutex> slock (stats_mutex_);
2007- stats_.frames_failed += BURST_GROUP_SIZE ;
2015+ stats_.frames_failed += burst_group_size ;
20082016 }
20092017 burst_soft_buffer_.clear ();
20102018 {
@@ -2020,7 +2028,7 @@ void StreamingDecoder::accumulateBurstFrames() {
20202028 }
20212029
20222030 // SUCCESS — check if group complete
2023- if (static_cast <int >(burst_soft_buffer_.size ()) == BURST_GROUP_SIZE ) {
2031+ if (static_cast <int >(burst_soft_buffer_.size ()) == burst_group_size ) {
20242032 finalizeBurstGroup ();
20252033 burst_soft_buffer_.clear ();
20262034 {
@@ -2033,6 +2041,8 @@ void StreamingDecoder::accumulateBurstFrames() {
20332041}
20342042
20352043StreamingDecoder::BurstFrameResult StreamingDecoder::tryDemodulateNextBurstFrame () {
2044+ const int burst_group_size = std::max (2 , burst_group_size_);
2045+
20362046 // Check available samples at burst_next_pos_
20372047 size_t next_available;
20382048 {
@@ -2071,7 +2081,7 @@ StreamingDecoder::BurstFrameResult StreamingDecoder::tryDemodulateNextBurstFrame
20712081 if (next_rms < BURST_ENERGY_THRESHOLD) {
20722082 LOG_MODEM (WARN, " [%s] Burst frame %zu/%d: energy lost (RMS=%.4f)" ,
20732083 log_prefix_.c_str (), burst_soft_buffer_.size () + 1 ,
2074- BURST_GROUP_SIZE , next_rms);
2084+ burst_group_size , next_rms);
20752085 burst_next_pos_ = (burst_next_pos_ + burst_min_block_) % MAX_BUFFER_SAMPLES;
20762086 return BurstFrameResult::FAILED;
20772087 }
@@ -2081,7 +2091,7 @@ StreamingDecoder::BurstFrameResult StreamingDecoder::tryDemodulateNextBurstFrame
20812091 bool ok = waveform_->process (SampleSpan (block.data (), block.size ()));
20822092 if (!ok) {
20832093 LOG_MODEM (WARN, " [%s] Burst frame %zu/%d: process() failed" ,
2084- log_prefix_.c_str (), burst_soft_buffer_.size () + 1 , BURST_GROUP_SIZE );
2094+ log_prefix_.c_str (), burst_soft_buffer_.size () + 1 , burst_group_size );
20852095 burst_next_pos_ = (burst_next_pos_ + burst_min_block_) % MAX_BUFFER_SAMPLES;
20862096 return BurstFrameResult::FAILED;
20872097 }
@@ -2109,17 +2119,18 @@ StreamingDecoder::BurstFrameResult StreamingDecoder::tryDemodulateNextBurstFrame
21092119
21102120 LOG_MODEM (INFO, " [%s] Burst frame %zu/%d demodulated, RMS=%.4f" ,
21112121 log_prefix_.c_str (), burst_soft_buffer_.size (),
2112- BURST_GROUP_SIZE , next_rms);
2122+ burst_group_size , next_rms);
21132123 return BurstFrameResult::SUCCESS;
21142124}
21152125
21162126void StreamingDecoder::finalizeBurstGroup () {
2127+ const int burst_group_size = std::max (2 , burst_group_size_);
21172128 LOG_MODEM (INFO, " [%s] Burst group complete (%d frames), deinterleaving..." ,
2118- log_prefix_.c_str (), BURST_GROUP_SIZE );
2129+ log_prefix_.c_str (), burst_group_size );
21192130
21202131 auto logical_soft = fec::BurstInterleaver::deinterleave (burst_soft_buffer_);
21212132
2122- for (int i = 0 ; i < BURST_GROUP_SIZE ; i++) {
2133+ for (int i = 0 ; i < burst_group_size ; i++) {
21232134 DecodeResult result = decodeFrame (logical_soft[i], burst_snr_, burst_cfo_);
21242135
21252136 {
@@ -2137,7 +2148,7 @@ void StreamingDecoder::finalizeBurstGroup() {
21372148 }
21382149
21392150 LOG_MODEM (INFO, " [%s] Burst logical frame %d/%d: %s (%d/%d CWs)" ,
2140- log_prefix_.c_str (), i + 1 , BURST_GROUP_SIZE ,
2151+ log_prefix_.c_str (), i + 1 , burst_group_size ,
21412152 result.success ? " OK" : " FAIL" ,
21422153 result.codewords_ok , result.codewords_ok + result.codewords_failed );
21432154 }
0 commit comments