@@ -865,8 +865,6 @@ bool OFDMDemodulator::process(SampleSpan samples) {
865865 LOG_SYNC (INFO, " SYNC: offset=%zu, corr=%.3f, CFO=%.1f Hz, buffer=%zu" ,
866866 sync_offset, sync_corr, coarse_cfo, impl_->rx_buffer .size ());
867867
868- impl_->last_sync_offset = sync_offset;
869-
870868 // LTS fine timing
871869 size_t sts_start = sync_offset;
872870 size_t refined_lts_start = impl_->refineLTSTiming (sts_start);
@@ -886,18 +884,28 @@ bool OFDMDemodulator::process(SampleSpan samples) {
886884 LOG_SYNC (INFO, " LTS fine timing: coarse_lts=%zu, refined=%zu, delta=%+d samples" ,
887885 coarse_lts_pos, refined_lts_start, timing_refinement);
888886
887+ // Report true preamble start for diagnostics/tests.
888+ // In practice the Schmidl-Cox coarse peak tends to land one symbol late,
889+ // so refined_lts_start is commonly the SECOND LTS symbol.
890+ // Preamble layout: 4 STS + 2 LTS.
891+ if (refined_lts_start >= 5 * preamble_symbol_len) {
892+ impl_->last_sync_offset = refined_lts_start - 5 * preamble_symbol_len;
893+ } else {
894+ impl_->last_sync_offset = 0 ;
895+ }
896+
889897 // Check if differential mode for LTS phase extraction
890898 bool is_differential = (impl_->config .modulation == Modulation::DQPSK ||
891899 impl_->config .modulation == Modulation::D8PSK ||
892900 impl_->config .modulation == Modulation::DBPSK);
893901 LOG_SYNC (DEBUG, " LTS phase check: is_differential=%d, use_pilots=%d" ,
894902 is_differential, impl_->config .use_pilots );
895903
896- // Consume preamble: from start up through LTS
897- // Data starts after: refined_lts_start + 2 LTS symbols
898- size_t consume = refined_lts_start + 2 * preamble_symbol_len + impl_->manual_timing_offset ;
899- LOG_SYNC (DEBUG, " Consume calc: refined_lts=%zu + 2* preamble_sym=%zu + offset=%d = %zu" ,
900- refined_lts_start, 2 * preamble_symbol_len, impl_->manual_timing_offset , consume);
904+ // Consume preamble up through last LTS. With second-LTS lock, data starts
905+ // one symbol after refined_lts_start.
906+ size_t consume = refined_lts_start + preamble_symbol_len + impl_->manual_timing_offset ;
907+ LOG_SYNC (DEBUG, " Consume calc: refined_lts=%zu + preamble_sym=%zu + offset=%d = %zu" ,
908+ refined_lts_start, preamble_symbol_len, impl_->manual_timing_offset , consume);
901909 impl_->rx_buffer .erase (impl_->rx_buffer .begin (),
902910 impl_->rx_buffer .begin () + consume);
903911
@@ -945,14 +953,19 @@ bool OFDMDemodulator::process(SampleSpan samples) {
945953 if (impl_->state .load () == Impl::State::SYNCED) {
946954 // Check for new preamble (mid-frame detection)
947955 bool should_check_preamble = impl_->synced_symbol_count .load () > 0 &&
948- impl_->idle_call_count .load () >= 2 ;
956+ impl_->idle_call_count .load () >= 2 &&
957+ impl_->soft_bits .empty ();
949958
950959 if (should_check_preamble && impl_->rx_buffer .size () >= preamble_total_len) {
951960 size_t search_limit = std::min (impl_->rx_buffer .size () - preamble_total_len,
952961 impl_->symbol_samples * 2 );
953962 constexpr size_t STEP = 8 ;
954963
955964 for (size_t offset = 0 ; offset <= search_limit; offset += STEP) {
965+ if (!impl_->hasMinimumEnergy (offset, correlation_window)) {
966+ continue ;
967+ }
968+
956969 float corr = impl_->measureCorrelation (offset);
957970 if (corr > impl_->sync_threshold ) {
958971 size_t sts_start = offset;
@@ -963,7 +976,7 @@ bool OFDMDemodulator::process(SampleSpan samples) {
963976 continue ;
964977 }
965978
966- size_t consume = refined_lts_start + 2 * preamble_symbol_len;
979+ size_t consume = refined_lts_start + preamble_symbol_len;
967980
968981 LOG_SYNC (INFO, " SYNCED preamble: sts=%zu, refined_lts=%zu, consume=%zu" ,
969982 sts_start, refined_lts_start, consume);
@@ -1462,6 +1475,7 @@ bool OFDMDemodulator::searchForSync(SampleSpan samples, size_t& out_position, fl
14621475 // Search for preamble
14631476 bool found_sync = false ;
14641477 size_t sync_offset = 0 ;
1478+ size_t refined_lts_offset = 0 ;
14651479 float sync_cfo = 0 .0f ;
14661480
14671481 size_t search_end = impl_->rx_buffer .size () - preamble_total_len - correlation_window;
@@ -1501,10 +1515,11 @@ bool OFDMDemodulator::searchForSync(SampleSpan samples, size_t& out_position, fl
15011515 if (refined_lts != SIZE_MAX) {
15021516 found_sync = true ;
15031517 sync_offset = peak_pos;
1518+ refined_lts_offset = refined_lts;
15041519 sync_cfo = impl_->estimateCoarseCFO (peak_pos);
15051520
1506- LOG_SYNC (INFO, " searchForSync: found at %zu, corr=%.3f, CFO=%.1f Hz" ,
1507- sync_offset, peak_corr, sync_cfo);
1521+ LOG_SYNC (INFO, " searchForSync: found at %zu (LTS=%zu) , corr=%.3f, CFO=%.1f Hz" ,
1522+ sync_offset, refined_lts_offset, peak_corr, sync_cfo);
15081523 break ;
15091524 }
15101525 }
@@ -1517,12 +1532,10 @@ bool OFDMDemodulator::searchForSync(SampleSpan samples, size_t& out_position, fl
15171532 if (found_sync) {
15181533 out_cfo_hz = sync_cfo;
15191534
1520- // Calculate where LTS (training) starts
1521- // Preamble: 4 STS + 2 LTS = 6 symbols
1522- // processPresynced expects samples starting at LTS (it needs training for channel est)
1523- // So we return position of LTS start (after 4 STS symbols)
1524- size_t sts_symbols = 4 ;
1525- out_position = sync_offset + sts_symbols * preamble_symbol_len;
1535+ // processPresynced expects samples starting at FIRST LTS.
1536+ out_position = (refined_lts_offset >= preamble_symbol_len)
1537+ ? (refined_lts_offset - preamble_symbol_len)
1538+ : refined_lts_offset;
15261539 }
15271540
15281541 return found_sync;
0 commit comments