Radar Detection Modeling¶
phased-array-systems provides radar detection performance analysis based on the radar range equation and detection theory.
Overview¶
The radar detection model calculates:
- Single-pulse SNR: Signal-to-noise ratio for one pulse
- Integrated SNR: SNR after pulse integration
- Required SNR: SNR needed for detection
- Detection range: Maximum range for given Pd/Pfa
- SNR margin: Margin above detection threshold
Radar Range Equation¶
The fundamental radar equation:
Where:
- \(P_t\) = Peak transmit power (W)
- \(G\) = Antenna gain (linear)
- \(\lambda\) = Wavelength (m)
- \(\sigma\) = Target radar cross section (m²)
- \(R\) = Target range (m)
- \(k\) = Boltzmann constant
- \(T_s\) = System noise temperature (K)
- \(B_n\) = Noise bandwidth (Hz)
- \(L_s\) = System losses (linear)
Basic Usage¶
from phased_array_systems.architecture import Architecture, ArrayConfig, RFChainConfig
from phased_array_systems.scenarios import RadarDetectionScenario
from phased_array_systems.evaluate import evaluate_case
# Define architecture
arch = Architecture(
array=ArrayConfig(nx=64, ny=64, dx_lambda=0.5, dy_lambda=0.5),
rf=RFChainConfig(
tx_power_w_per_elem=10.0,
pa_efficiency=0.25,
noise_figure_db=4.0,
),
)
# Define scenario
scenario = RadarDetectionScenario(
freq_hz=10e9, # X-band
bandwidth_hz=100e3, # matched to a 10 us pulse (B ~ 1/tau)
range_m=100e3, # 100 km
target_rcs_dbsm=0.0, # 1 m^2 target, expressed in dBsm
pd_required=0.9, # 90% detection probability
pfa=1e-6, # 1e-6 false alarm rate
prf_hz=1000, # 1 kHz PRF
n_pulses=10, # Integrate 10 pulses
integration_type="coherent",
swerling=1,
)
# Evaluate
metrics = evaluate_case(arch, scenario)
print(f"Single-Pulse SNR: {metrics['snr_single_pulse_db']:.1f} dB")
print(f"Integrated SNR: {metrics['snr_integrated_db']:.1f} dB")
print(f"Required SNR: {metrics['snr_required_db']:.1f} dB")
print(f"SNR Margin: {metrics['snr_margin_db']:.1f} dB")
Target cross-section is given in dBsm, so a 1 m^2 target is 0.0 and a 2 m^2
target is 3.0. There is no pulse-width field: set bandwidth_hz directly,
which for an uncompressed pulse is about 1/tau.
Output Metrics¶
| Metric | Units | Description |
|---|---|---|
snr_single_pulse_db |
dB | SNR for one pulse |
snr_integrated_db |
dB | SNR after integration |
snr_required_db |
dB | Required SNR for Pd/Pfa |
snr_margin_db |
dB | Margin above required |
detection_range_m |
m | Max range for requirements |
Detection Probability¶
Required SNR Calculation¶
The required SNR depends on:
- Desired detection probability (Pd)
- False alarm probability (Pfa)
- Target fluctuation model (Swerling)
For Swerling 0 (non-fluctuating):
Swerling Target Models¶
| Model | Description | Typical Targets |
|---|---|---|
| 0 | Non-fluctuating | Sphere, corner reflector |
| 1 | Slow fluctuation, Rayleigh | Aircraft (scan-to-scan) |
| 2 | Fast fluctuation, Rayleigh | Aircraft (pulse-to-pulse) |
| 3 | Slow, one dominant + many | Ship, complex target |
| 4 | Fast, one dominant + many | Propeller aircraft |
# Different Swerling models
scenario_sw0 = RadarDetectionScenario(..., swerling=0) # Steady target
scenario_sw1 = RadarDetectionScenario(..., swerling=1) # Typical aircraft
scenario_sw3 = RadarDetectionScenario(..., swerling=3) # Ship
Pulse Integration¶
Coherent Integration¶
Maintains phase information; provides linear SNR improvement:
scenario = RadarDetectionScenario(
...,
n_pulses=16,
integration_type="coherent",
)
# SNR improves by 10*log10(16) = 12 dB
Non-Coherent Integration¶
Magnitude-only; provides approximately √N improvement:
scenario = RadarDetectionScenario(
...,
n_pulses=16,
integration_type="noncoherent",
)
# SNR improves by approximately 10*log10(√16) = 6 dB
Using the Radar Model Directly¶
For advanced use cases:
from phased_array_systems.models.radar.equation import RadarModel
from phased_array_systems.models.radar.detection import compute_snr_for_pd
# Calculate required SNR
snr_req = compute_snr_for_pd(
pd=0.9,
pfa=1e-6,
swerling=1,
n_pulses=10,
)
print(f"Required SNR: {snr_req:.1f} dB") # 13.5 dB
# Use the radar model directly. It reads antenna gain and beamwidths from
# `context`; an empty context falls back to defaults, so in normal use pass
# the antenna model's metrics through as `evaluate_case` does.
model = RadarModel()
metrics = model.evaluate(arch, scenario, context={})
Detection Range Calculation¶
Solve for range at which SNR equals required SNR:
Every evaluated case already carries it, scaled from the SNR margin by the fourth-power range law:
metrics = evaluate_case(arch, scenario)
print(f"Detection range: {metrics['detection_range_m']/1000:.1f} km")
To solve from radar parameters without building an Architecture:
from phased_array_systems.models.radar.equation import compute_detection_range
max_range_m = compute_detection_range(
peak_power_w=1000.0,
g_ant_db=35.0,
freq_hz=10e9,
rcs_dbsm=0.0,
noise_temp_k=290.0,
bandwidth_hz=1e6,
noise_figure_db=4.0,
system_loss_db=3.0,
snr_required_db=13.0,
)
print(f"Detection range: {max_range_m/1000:.1f} km") # 10.3 km
Example: Search Radar¶
# Long-range search radar
arch = Architecture(
array=ArrayConfig(nx=32, ny=32, dx_lambda=0.5, dy_lambda=0.5),
rf=RFChainConfig(
tx_power_w_per_elem=20.0, # High power
pa_efficiency=0.20,
noise_figure_db=3.5,
),
)
scenario = RadarDetectionScenario(
freq_hz=3e9, # S-band (longer range)
bandwidth_hz=20e3, # matched to a 50 us pulse
range_m=200e3, # 200 km search
target_rcs_dbsm=3.0, # 2 m^2, medium aircraft
pd_required=0.8, # 80% Pd
pfa=1e-6,
prf_hz=300,
n_pulses=20, # Long integration
integration_type="noncoherent",
swerling=1,
)
metrics = evaluate_case(arch, scenario)
print(f"SNR Margin at 200 km: {metrics['snr_margin_db']:.1f} dB")
print(f"Detection range: {metrics['detection_range_m']/1000:.1f} km")
Half a dB short at the stated range, which the detection range restates as 194 km rather than 200 km.
Example: Tracking Radar¶
Setting target_accel_max_ms2 turns on the track-accuracy metrics: the
measurement errors that the detection SNR buys, and the steady-state filter
performance that follows from them and the revisit rate. See
Track Accuracy for the equations.
from phased_array_systems import Architecture, ArrayConfig, RFChainConfig, evaluate_case
from phased_array_systems.scenarios import RadarDetectionScenario
arch = Architecture(
array=ArrayConfig(nx=64, ny=64, dx_lambda=0.5, dy_lambda=0.5),
rf=RFChainConfig(
tx_power_w_per_elem=10.0,
pa_efficiency=0.30,
noise_figure_db=3.0,
),
)
scenario = RadarDetectionScenario(
freq_hz=10e9, # X-band
bandwidth_hz=10e6, # 15 m range resolution
range_m=50e3,
target_rcs_dbsm=0.0,
pd_required=0.99, # high Pd for track maintenance
pfa=1e-4, # relaxed Pfa on a confirmed target
n_pulses=64,
prf_hz=5000,
integration_type="coherent",
swerling=0, # stabilized target
track_revisit_s=1.0, # track update rate
target_accel_max_ms2=40.0, # ~4 g maneuver
)
metrics = evaluate_case(arch, scenario)
print(f"SNR: {metrics['snr_integrated_db']:.1f} dB")
print(f"sigma_range: {metrics['sigma_range_m']:.2f} m")
print(f"sigma_crossrng: {metrics['sigma_crossrange_az_m']:.1f} m")
print(f"track position: {metrics['track_pos_rms_crossrange_m']:.1f} m")
Cross-range error is 57x the range error here, and only a larger aperture
reduces it. monopulse_snr_ok reports whether the case sits above the 13 dB
floor where the angle-accuracy relation is valid.
Radar Trade Studies¶
Combine with DOE for systematic analysis:
from phased_array_systems.trades import DesignSpace, generate_doe, BatchRunner
from phased_array_systems.requirements import Requirement, RequirementSet
# Define requirements
requirements = RequirementSet(requirements=[
Requirement("DET-001", "Positive SNR Margin", "snr_margin_db", ">=", 0.0, severity="must"),
Requirement("COST-001", "Max Cost", "cost_usd", "<=", 1000000.0, severity="must"),
])
# Define design space
space = (
DesignSpace()
.add_variable("array.nx", type="categorical", values=[8, 16, 32])
.add_variable("array.ny", type="categorical", values=[8, 16, 32])
.add_variable("rf.tx_power_w_per_elem", type="float", low=5.0, high=20.0)
# ... other parameters
)
# Run trade study
doe = generate_doe(space, method="lhs", n_samples=100, seed=42)
runner = BatchRunner(scenario, requirements)
results = runner.run(doe)
# Find Pareto-optimal designs
from phased_array_systems.trades import filter_feasible, extract_pareto
feasible = filter_feasible(results, requirements)
pareto = extract_pareto(feasible, [
("cost_usd", "minimize"),
("snr_margin_db", "maximize"),
])
Sensitivity Analysis¶
Analyze how parameters affect detection:
import numpy as np
import pandas as pd
# Vary range
ranges = np.linspace(50e3, 200e3, 20)
results = []
for range_m in ranges:
scenario.range_m = range_m
metrics = evaluate_case(arch, scenario)
results.append({
"range_km": range_m / 1000,
"snr_margin_db": metrics["snr_margin_db"],
})
df = pd.DataFrame(results)
print(df)
Key Considerations¶
Power-Aperture Product¶
Radar performance scales with power × aperture:
Trade off between:
- More power (higher cost, heat)
- Larger aperture (more elements, higher cost)
Frequency Selection¶
| Lower Frequency | Higher Frequency |
|---|---|
| Longer range | Better resolution |
| Larger aperture for same gain | Smaller components |
| Better rain penetration | More atmospheric loss |
Integration Time¶
More pulses = better SNR, but:
- Longer dwell time per beam position
- Target motion limits coherent integration
- Faster scan requires fewer pulses
See Also¶
- Theory: Radar Equation - Detailed derivations
- Scenarios - Configure radar scenarios
- Trade Studies - Systematic radar analysis
- API Reference - Full API documentation