[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);
|
read_error_streak = read_error_streak.saturating_add(1);
|
||||||
let err_lc = e.to_ascii_lowercase();
|
let err_lc = e.to_ascii_lowercase();
|
||||||
let is_overflow = err_lc.contains("overflow") || err_lc.contains("overrun");
|
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) {
|
let recovered = match source.handle_read_error(&e, read_error_streak) {
|
||||||
Ok(result) => result,
|
Ok(result) => result,
|
||||||
Err(recovery_err) => {
|
Err(recovery_err) => {
|
||||||
@@ -256,6 +257,12 @@ fn iq_read_loop(
|
|||||||
false
|
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 {
|
if is_overflow {
|
||||||
let now = Instant::now();
|
let now = Instant::now();
|
||||||
if overflow_log_window_start
|
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> {
|
fn handle_read_error(&mut self, err: &str, streak: u32) -> Result<bool, String> {
|
||||||
let err_lc = err.to_ascii_lowercase();
|
let err_lc = err.to_ascii_lowercase();
|
||||||
let is_overrun = err_lc.contains("overflow") || err_lc.contains("overrun");
|
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);
|
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);
|
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
|
self.stream
|
||||||
.activate(None)
|
.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)
|
Ok(true)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user