improved parsing of software uart

This commit is contained in:
Priec
2025-11-19 20:40:13 +01:00
parent de4e04787b
commit ef56483016

View File

@@ -115,53 +115,85 @@ pub fn decode_uart_samples(
let frame_bits = 1 + nbits + parity_bits + stop_bits_count;
let frame_len = frame_bits * ovs;
// Helper: Majority vote over 3 samples centered at `i`
let get_bit = |i: usize| -> u8 {
let mut votes = 0;
// Check i-1, i, i+1. Saturating sub/add handles boundaries roughly.
if i > 0 && samples.get(i - 1).map_or(true, |&x| x != 0) {
votes += 1;
}
if samples.get(i).map_or(true, |&x| x != 0) {
votes += 1;
}
if samples.get(i + 1).map_or(true, |&x| x != 0) {
votes += 1;
}
if votes >= 2 {
1
} else {
0
}
};
// We loop while we have enough remaining samples for a full frame
while idx + frame_len <= samples.len() {
// Wait for falling edge (High -> Low)
// samples[idx] == 1 (Idle/Stop) && samples[idx+1] == 0 (Start)
if samples[idx] != 0 && samples[idx + 1] == 0 {
// Align to center of START bit
// Start bit begins at idx+1. Center is at idx + 1 + (ovs/2)
let center_offset = 1 + (ovs / 2);
let mut scan_idx = idx + center_offset;
// Sanity check: is the middle of the start bit actually 0?
if samples.get(scan_idx).copied().unwrap_or(1) != 0 {
idx += 1;
// 1. Validate Start Bit (Must be 0)
if get_bit(scan_idx) != 0 {
idx += 1; // False start (noise), move on
continue;
}
// Move to center of first data bit
scan_idx += ovs;
// Sample data bits
// 2. Read Data Bits
let mut data: u8 = 0;
for bit in 0..nbits {
let bit_val = samples
.get(scan_idx)
.map(|&b| if b != 0 { 1u8 } else { 0u8 })
.unwrap_or(1); // Default to high if out of bounds (shouldn't happen)
data |= bit_val << bit;
if get_bit(scan_idx) == 1 {
data |= 1 << bit;
}
scan_idx += ovs;
}
// Parity: skip
match cfg.parity {
Parity::None => {}
Parity::Even | Parity::Odd => {
scan_idx += ovs;
}
// 3. Skip Parity (if any)
if cfg.parity != Parity::None {
scan_idx += ovs;
}
// We successfully read a byte.
// Advance main `idx` past this frame.
// We processed `frame_len` samples starting from `idx` (which was the idle bit before start).
// Actually, to be precise, we should advance by roughly the frame length.
idx += frame_len;
// If overflow happens in push (unlikely given 256 limit), ignore
// 4. Validate Stop Bit (Must be 1)
// If stop bit is 0, it's a framing error. We reject the whole byte.
if get_bit(scan_idx) == 0 {
idx += 1; // Try to find a real start bit on the next sample
continue;
}
// 5. Byte is valid
let _ = out.push(data);
// 6. Active Resync: Fast-forward through the stop bit(s) and idle time
// scan_idx is currently at the center of the Stop bit.
idx = scan_idx;
// Advance while we are reading High (1).
// As soon as we see Low (0), we stop. That 0 is the beginning of the NEXT start bit.
// The outer loop expects `idx` to be the High *before* the start bit, so we will handle that.
while idx < samples.len() && samples[idx] != 0 {
idx += 1;
}
// Back up one step.
// The outer loop logic is: `if samples[idx] != 0 && samples[idx+1] == 0`.
// If we stopped at `idx` because it was 0, then `idx-1` was the last 1 (Idle).
if idx > 0 {
idx -= 1;
}
} else {
// No start bit detected here, move to next sample
idx += 1;