[fix](trx-backend-soapysdr): ensure IQ stream recovery after overflow
Two problems prevented reliable recovery after a persistent overflow: 1. Restart storm: once read_error_streak >= 3, every subsequent read failure triggered a deactivate/activate cycle, potentially preventing the hardware from stabilising. Fix: after a successful restart, reset read_error_streak to 1 so the stream gets 2 more failures before the next restart attempt. 2. Stuck-deactivated stream: if activate() failed after overflow, the stream was left deactivated. Subsequent reads returned non-overflow errors which handle_read_error ignored (Ok(false)), so the stream was never reactivated. Fix: add a high-streak fallback (>= 10 consecutive errors of any kind) that also attempts a full deactivate/activate restart, covering the stuck-deactivated case. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> Signed-off-by: Stan Grams <sjg@haxx.space>
This commit is contained in:
@@ -246,6 +246,7 @@ fn iq_read_loop(
|
||||
read_error_streak = read_error_streak.saturating_add(1);
|
||||
let err_lc = e.to_ascii_lowercase();
|
||||
let is_overflow = err_lc.contains("overflow") || err_lc.contains("overrun");
|
||||
let restarted = read_error_streak >= 3;
|
||||
let recovered = match source.handle_read_error(&e, read_error_streak) {
|
||||
Ok(result) => result,
|
||||
Err(recovery_err) => {
|
||||
@@ -256,6 +257,12 @@ fn iq_read_loop(
|
||||
false
|
||||
}
|
||||
};
|
||||
// After a stream restart, reset the streak so we don't
|
||||
// immediately trigger another restart on the first transient
|
||||
// failure during hardware stabilisation.
|
||||
if restarted && recovered {
|
||||
read_error_streak = 1;
|
||||
}
|
||||
if is_overflow {
|
||||
let now = Instant::now();
|
||||
if overflow_log_window_start
|
||||
|
||||
@@ -182,23 +182,35 @@ impl IqSource for RealIqSource {
|
||||
fn handle_read_error(&mut self, err: &str, streak: u32) -> Result<bool, String> {
|
||||
let err_lc = err.to_ascii_lowercase();
|
||||
let is_overrun = err_lc.contains("overflow") || err_lc.contains("overrun");
|
||||
if !is_overrun {
|
||||
|
||||
if is_overrun {
|
||||
// Overflow is often transient; avoid immediate stream restart churn.
|
||||
// Only restart after several consecutive overflow failures.
|
||||
if streak < 3 {
|
||||
return Ok(true);
|
||||
}
|
||||
tracing::warn!(
|
||||
"SoapySDR RX overflow persists (streak={}); restarting RX stream",
|
||||
streak
|
||||
);
|
||||
} else if streak >= 10 {
|
||||
// Non-overflow errors at a high streak (e.g. reads on a
|
||||
// deactivated stream after a failed activate) — attempt a
|
||||
// full restart to recover.
|
||||
tracing::warn!(
|
||||
"SoapySDR RX persistent error (streak={}): {}; restarting RX stream",
|
||||
streak,
|
||||
err
|
||||
);
|
||||
} else {
|
||||
return Ok(false);
|
||||
}
|
||||
// Overflow is often transient; avoid immediate stream restart churn.
|
||||
// Only restart after several consecutive read failures.
|
||||
if streak < 3 {
|
||||
return Ok(true);
|
||||
}
|
||||
tracing::warn!(
|
||||
"SoapySDR RX overflow persists (streak={}); restarting RX stream",
|
||||
streak
|
||||
);
|
||||
|
||||
let _ = self.stream.deactivate(None);
|
||||
std::thread::sleep(std::time::Duration::from_millis(25));
|
||||
std::thread::sleep(std::time::Duration::from_millis(50));
|
||||
self.stream
|
||||
.activate(None)
|
||||
.map_err(|e| format!("Failed to reactivate RX stream after overflow: {}", e))?;
|
||||
.map_err(|e| format!("Failed to reactivate RX stream: {}", e))?;
|
||||
Ok(true)
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user