[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:
@@ -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.
|
||||
|
||||
Reference in New Issue
Block a user