Wideband Beamforming#

This guide covers wideband effects in phased arrays and techniques for mitigation, including true time delay (TTD) and hybrid architectures.

Beam Squint Problem#

Phase shifters provide a frequency-independent phase shift. When used for beam steering, the actual time delay varies with frequency, causing the beam to point at different angles across the bandwidth. This is called beam squint.

import phased_array as pa
import numpy as np

# Array at 10 GHz center frequency
wavelength = 0.03  # 10 GHz
geom = pa.create_rectangular_array(32, 32, dx=0.5, dy=0.5, wavelength=wavelength)

# Analyze beam squint for 20% bandwidth
center_freq = 10e9
frequencies = np.linspace(9e9, 11e9, 21)

results = pa.compute_beam_squint(
    geom.x, geom.y,
    theta0_deg=45, phi0_deg=0,
    center_frequency=center_freq,
    frequencies=frequencies,
    steering_mode='phase'
)

# At 45 deg scan, expect ~1-2 deg squint at band edges
print(f"Squint range: {results['squint'].min():.2f} to {results['squint'].max():.2f} deg")

Squint increases with:

  • Larger scan angles

  • Larger array aperture

  • Greater frequency offset from center

Beam Squint Formula#

Approximate squint for a linear array:

\[\Delta\theta \approx \frac{\Delta f}{f_0} \tan\theta_0\]

where \(\Delta f\) is frequency offset, \(f_0\) is center frequency, and \(\theta_0\) is scan angle.

Instantaneous Bandwidth#

The instantaneous bandwidth (IBW) is the frequency range over which the beam remains within acceptable pointing error (typically 3 dB beamwidth).

# Analyze IBW for different scan angles
scan_angles = [0, 15, 30, 45, 60]

for angle in scan_angles:
    ibw = pa.analyze_instantaneous_bandwidth(
        geom.x, geom.y,
        theta0_deg=angle,
        phi0_deg=0,
        center_frequency=10e9,
        max_squint_deg=2.0,  # Acceptable pointing error
        steering_mode='phase'
    )
    print(f"Scan {angle} deg: IBW = {ibw/1e6:.0f} MHz")

True Time Delay (TTD) Steering#

TTD uses actual time delays instead of phase shifts. Since a time delay produces frequency-proportional phase, the beam points correctly at all frequencies.

# TTD steering vector
weights_ttd = pa.steering_vector_ttd(
    geom.x, geom.y,
    theta0_deg=45, phi0_deg=0,
    frequency=10e9  # Design frequency (any frequency works)
)

# Analyze squint (should be nearly zero)
results_ttd = pa.compute_beam_squint(
    geom.x, geom.y, 45, 0, 10e9, frequencies,
    steering_mode='ttd'
)
print(f"TTD squint: {np.max(np.abs(results_ttd['squint'])):.3f} deg")

Time Delay Values#

Get the actual time delays required:

delays = pa.steering_delays_ttd(
    geom.x, geom.y,
    theta0_deg=45, phi0_deg=0
)

# Delays in seconds
print(f"Max delay: {np.max(delays)*1e12:.1f} ps")
print(f"Delay range: {(np.max(delays) - np.min(delays))*1e12:.1f} ps")

TTD vs Phase Steering Comparison#

# Compare patterns at band edges
for f, label in [(9e9, 'Low'), (10e9, 'Center'), (11e9, 'High')]:
    k = pa.frequency_to_k(f)

    # Phase steering (designed at center frequency)
    weights_phase = pa.steering_vector(
        pa.frequency_to_k(10e9), geom.x, geom.y, 45, 0
    )

    # TTD steering
    weights_ttd = pa.steering_vector_ttd(geom.x, geom.y, 45, 0, f)

    # Compute patterns and compare peak directions
    # Phase steering: peak moves with frequency
    # TTD: peak stays at 45 deg

Hybrid Phase/TTD Architecture#

Full TTD per element is expensive. A common compromise uses TTD at the subarray level and phase shifters within subarrays.

Creating Hybrid Architecture#

# 64x64 array with 8x8 element subarrays
architecture = pa.create_rectangular_subarrays(
    Nx_total=64, Ny_total=64,
    Nx_sub=8, Ny_sub=8,
    dx=0.5, dy=0.5,
    wavelength=wavelength
)

print(f"Total elements: {architecture.geometry.n_elements}")  # 4096
print(f"Subarrays: {architecture.n_subarrays}")  # 64 (one TTD each)

Hybrid Steering#

# Compute hybrid steering weights
weights_hybrid = pa.steering_vector_hybrid(
    architecture,
    theta0_deg=45, phi0_deg=0,
    frequency=10e9
)

# Subarrays use TTD, elements within use phase
results_hybrid = pa.compute_beam_squint(
    architecture.geometry.x, architecture.geometry.y,
    45, 0, 10e9, frequencies,
    steering_mode='hybrid',
    architecture=architecture
)

# Squint is reduced but not eliminated
print(f"Hybrid squint: {np.max(np.abs(results_hybrid['squint'])):.3f} deg")

Subarray Delay Values#

subarray_delays = pa.compute_subarray_delays_ttd(
    architecture,
    theta0_deg=45, phi0_deg=0
)

print(f"Number of TTD units needed: {len(subarray_delays)}")
print(f"Max subarray delay: {np.max(subarray_delays)*1e12:.1f} ps")

Pattern vs Frequency#

Compute and visualize how the pattern changes across the band:

patterns = pa.compute_pattern_vs_frequency(
    geom.x, geom.y,
    theta0_deg=30, phi0_deg=0,
    center_frequency=10e9,
    frequencies=frequencies,
    steering_mode='phase',
    n_points=361
)

# patterns['frequencies'] - frequency array
# patterns['theta_deg'] - angle array
# patterns['patterns_dB'] - 2D array (n_freq x n_theta)

Visualization#

Plot beam squint:

# Compare all steering modes
fig = pa.plot_beam_squint(
    geom.x, geom.y,
    theta0_deg=45, phi0_deg=0,
    center_frequency=10e9,
    bandwidth=2e9,
    steering_modes=['phase', 'ttd', 'hybrid'],
    architecture=architecture
)
fig.show()

Plot pattern vs frequency:

# Waterfall plot of pattern vs frequency
fig = pa.plot_pattern_vs_frequency_plotly(
    geom.x, geom.y,
    theta0_deg=30, phi0_deg=0,
    center_frequency=10e9,
    bandwidth=2e9,
    n_freqs=11,
    steering_mode='phase'
)
fig.show()

Subarray delay visualization:

fig = pa.plot_subarray_delays(
    architecture,
    theta0_deg=45, phi0_deg=0
)
fig.show()

Design Guidelines#

Choosing a Steering Approach#

Approach

Use When

Considerations

Phase-only

Narrowband, small apertures, cost-sensitive

IBW limits operational bandwidth

Full TTD

Wideband radar, EW, 5G FR2

Expensive, high complexity

Hybrid

Moderate bandwidth, large arrays

Balance of cost and performance

IBW Requirements#

For a typical 256-element planar array:

  • Phase-only at 45 deg scan: IBW ~ 2-5% of center frequency

  • Hybrid (8x8 subarrays): IBW ~ 10-20%

  • Full TTD: IBW ~ 100% (limited by elements, not steering)

Subarray Sizing#

Larger subarrays = fewer TTD units = more squint remaining

Rule of thumb: Subarray aperture should be small enough that internal squint is less than half the beamwidth:

\[D_{sub} < \frac{\lambda_0^2 \cdot f_0}{4 \cdot \Delta f \cdot \sin\theta_{max}}\]