[fix](trx-rds): apply RRC pulse shaping in test signal generator

The `chips_to_rds_signal` test helper was generating rectangular chip
pulses, but the receiver expects RRC-shaped transmit pulses so that
RRC(tx) × RRC(rx) = raised cosine with zero ISI. The rectangular
pulses caused ISI that drifted the symbol clock sampling point,
consistently skipping PS segment 2 in the end-to-end test.

Replace rectangular pulses with an impulse train convolved with the
same RRC taps used by the receiver. All 15 tests now pass including
`end_to_end_clean_signal_decodes_ps`.

https://claude.ai/code/session_01N2UcGaLDzYiM3gNrZ6kFBj
Signed-off-by: Claude <noreply@anthropic.com>
This commit is contained in:
Claude
2026-03-27 00:51:51 +00:00
committed by Stan Grams
parent 57e88b3590
commit 06796342e7
+36 -9
View File
@@ -1155,19 +1155,46 @@ mod tests {
/// Modulate chip stream as BPSK on the 57 kHz RDS subcarrier.
/// Returns a composite FM-baseband signal for `RdsDecoder::process_sample`.
///
/// Each chip is RRC pulse-shaped so that RRC(tx) × RRC(rx) = raised cosine,
/// giving zero ISI at the receiver's optimal sampling instants.
fn chips_to_rds_signal(chips: &[i8], sample_rate: f32) -> Vec<f32> {
let samples_per_chip = sample_rate / RDS_CHIP_RATE;
let n = (chips.len() as f32 * samples_per_chip).ceil() as usize;
let mut sig = vec![0.0f32; n];
let spc = sample_rate / RDS_CHIP_RATE;
let n = (chips.len() as f32 * spc).ceil() as usize;
// Build the transmit RRC pulse shape (same taps as the receiver).
let taps = build_rrc_taps(sample_rate, RDS_CHIP_RATE);
// Create baseband impulse train and convolve with RRC taps.
let mut baseband = vec![0.0f32; n];
for (ci, &chip) in chips.iter().enumerate() {
let t0 = (ci as f32 * samples_per_chip) as usize;
let t1 = ((ci + 1) as f32 * samples_per_chip) as usize;
for t in t0..t1.min(n) {
let center = ((ci as f32 + 0.5) * spc).round() as usize;
if center < n {
baseband[center] = chip as f32;
}
}
// Convolve baseband impulse train with RRC taps (direct FIR).
let half = taps.len() / 2;
let mut shaped = vec![0.0f32; n];
for (i, &impulse) in baseband.iter().enumerate() {
if impulse == 0.0 {
continue;
}
for (j, &tap) in taps.iter().enumerate() {
let idx = i + j;
if idx >= half && idx - half < n {
shaped[idx - half] += impulse * tap;
}
}
}
// BPSK modulate onto the 57 kHz subcarrier.
for t in 0..n {
let phase = TAU * RDS_SUBCARRIER_HZ * t as f32 / sample_rate;
sig[t] = chip as f32 * phase.cos();
shaped[t] *= phase.cos();
}
}
sig
shaped
}
/// Add AWGN at the given SNR (dB) relative to the signal's actual power.