Complex Convolution/Correlation Example — Radar Matched Filter¶
This example demonstrates complex convolution and cross-correlation using a radar matched-filter scenario:
transforms.convolve_complex()— complex linear convolution (IMSL CCONV)transforms.correlate_complex()— complex cross-correlation (IMSL CCORL)
A complex exponential signal \(x = e^{2\pi i f_0 t}\) is convolved with a decaying complex kernel \(h = e^{-5t} \cdot e^{2\pi i f_0 t}\) to model channel filtering.
The matched-filter application transmits a short complex linear-FM (chirp)
pulse and embeds a delayed, noise-corrupted echo in a longer receive buffer.
Cross-correlating the received signal with the chirp template via
correlate_complex yields a peak at the true echo delay, demonstrating
time-of-flight (range) estimation.
Example Code¶
"""IMSL complex convolution/correlation example: radar matched filter.
Demonstrates:
- convolve_complex (IMSL CCONV) — complex linear convolution
- correlate_complex (IMSL CCORL) — complex cross-correlation
Application: matched-filter radar signal processing. The transmitted pulse
is a complex chirp; the return echo is a delayed, attenuated copy embedded in
noise. The matched filter (cross-correlation with the template) peaks at the
echo delay, allowing range estimation.
Outputs:
- Detection statistics printed to stdout
- SVG plot saved to test_output/example_imsl_complex_conv.svg
"""
from __future__ import annotations
from pathlib import Path
from typing import Dict
import matplotlib.pyplot as plt
import numpy as np
from transforms import convolve_complex, correlate_complex
def run_demo_imsl_complex_conv() -> Dict[str, object]:
"""Run IMSL complex convolution and correlation (matched filter) example.
Constructs a complex exponential signal x = exp(2πi·f0·t) and a
decaying complex kernel h = exp(-5t)·exp(2πi·f0·t), then computes
convolve_complex(x, h) to model channel filtering. A separate radar
scenario builds a complex chirp pulse, embeds a delayed copy in noise,
and uses correlate_complex as a matched filter whose peak identifies the
true echo delay.
Args:
None
Returns:
Dict[str, object]: Result dict with keys ``detected_delay`` (int),
``true_delay`` (int), and ``plot_path`` (str).
"""
fs = 200 # sample rate (Hz)
T = 0.5 # signal duration (s)
f0 = 10.0 # carrier frequency (Hz)
t = np.arange(int(fs * T)) / fs
# Complex exponential signal
x = np.exp(2j * np.pi * f0 * t)
# Decaying complex kernel (shorter than signal)
t_h = np.arange(int(fs * 0.2)) / fs
h = np.exp(-5.0 * t_h) * np.exp(2j * np.pi * f0 * t_h)
# --- Complex convolution: x * h ---
conv_out = convolve_complex(x, h)
# --- Radar matched filter ---
# Transmitted chirp: short complex linear-FM pulse
n_pulse = 20
t_pulse = np.arange(n_pulse) / fs
chirp = np.exp(2j * np.pi * (f0 + 30.0 * t_pulse) * t_pulse)
# Received signal: delayed echo buried in noise
true_delay = 45
rng = np.random.default_rng(42)
received = np.zeros(len(t) + n_pulse - 1, dtype=complex)
received[true_delay : true_delay + n_pulse] = chirp
noise_amp = 0.3
received += noise_amp * (
rng.standard_normal(len(received)) + 1j * rng.standard_normal(len(received))
)
# Matched filter: cross-correlate received with chirp template
mf_out = correlate_complex(received, chirp)
# Trim leading edge (length n_pulse-1) before finding peak
n_trim = n_pulse - 1
detected_delay = int(np.argmax(np.abs(mf_out[n_trim:])))
print("\nIMSL Complex Convolution/Correlation Example")
print("-" * 55)
print(f"{'Signal length (samples)':<42} {len(x):>8}")
print(f"{'Kernel length (samples)':<42} {len(h):>8}")
print(f"{'Convolution output length':<42} {len(conv_out):>8}")
print(f"{'Radar pulse length (samples)':<42} {n_pulse:>8}")
print(f"{'True delay (samples)':<42} {true_delay:>8}")
print(f"{'Detected delay (samples)':<42} {detected_delay:>8}")
print("-" * 55)
output_dir = Path("test_output")
output_dir.mkdir(parents=True, exist_ok=True)
plot_path = output_dir / "example_imsl_complex_conv.svg"
fig, axes = plt.subplots(3, 1, figsize=(10, 10))
# Convolution magnitude
t_conv = np.arange(len(conv_out)) / fs
axes[0].plot(t_conv, np.abs(conv_out), color="#7c3aed", linewidth=1.5)
axes[0].set_title("Complex Convolution |x * h|(t)")
axes[0].set_xlabel("Time (s)")
axes[0].set_ylabel("|conv(x, h)|")
axes[0].grid(True, alpha=0.3)
# Received radar signal
t_recv = np.arange(len(received)) / fs
axes[1].plot(t_recv, np.abs(received), color="#6d28d9", linewidth=1.0,
alpha=0.7, label="Received (noisy)")
axes[1].axvline(true_delay / fs, color="#dc2626", linestyle="--",
linewidth=1.5, label=f"True delay ({true_delay} samples)")
axes[1].set_title("Received Radar Signal (delayed echo + noise)")
axes[1].set_xlabel("Time (s)")
axes[1].set_ylabel("|r(t)|")
axes[1].legend()
axes[1].grid(True, alpha=0.3)
# Matched filter output
mf_plot = np.abs(mf_out[n_trim:])
t_mf = np.arange(len(mf_plot)) / fs
axes[2].plot(t_mf, mf_plot, color="#059669", linewidth=1.5, label="MF output")
axes[2].axvline(true_delay / fs, color="#dc2626", linestyle="--",
linewidth=1.5, label=f"True delay ({true_delay} samples)")
axes[2].axvline(detected_delay / fs, color="#f59e0b", linestyle=":",
linewidth=2.0, label=f"Detected ({detected_delay} samples)")
axes[2].set_title("Matched Filter Output |correlate_complex(r, chirp)|")
axes[2].set_xlabel("Time (s)")
axes[2].set_ylabel("|MF(t)|")
axes[2].legend()
axes[2].grid(True, alpha=0.3)
fig.tight_layout()
fig.savefig(plot_path, format="svg")
plt.close(fig)
return {
"detected_delay": detected_delay,
"true_delay": true_delay,
"plot_path": str(plot_path),
}
if __name__ == "__main__":
run_demo_imsl_complex_conv()
Plot Output¶
Top: magnitude of the complex convolution output |x ∗ h|(t). Middle: magnitude of the noisy received radar signal with the true echo delay marked. Bottom: matched filter output magnitude showing the detection peak at the correct delay.¶
Console Output¶
IMSL Complex Convolution/Correlation Example
-------------------------------------------------------
Signal length (samples) 100
Kernel length (samples) 40
Convolution output length 139
Radar pulse length (samples) 20
True delay (samples) 45
Detected delay (samples) 45
-------------------------------------------------------