Beamforming#

This guide covers beamforming techniques including beam steering, amplitude tapering for sidelobe control, null steering for interference rejection, and multi-beam synthesis.

Beam Steering#

Basic steering points the main beam toward a desired direction by applying appropriate phase shifts to each element.

import phased_array as pa
import numpy as np

# Create array
geom = pa.create_rectangular_array(16, 16, dx=0.5, dy=0.5)
k = pa.wavelength_to_k(1.0)

# Steer to theta=30 deg, phi=45 deg
weights = pa.steering_vector(
    k, geom.x, geom.y,
    theta0_deg=30,
    phi0_deg=45
)

# All weights have unit magnitude, varying phase
print(f"Magnitude range: {np.abs(weights).min():.2f} to {np.abs(weights).max():.2f}")

The steering vector applies phase shifts:

\[w_n = \exp\left(-jk(x_n u_0 + y_n v_0)\right)\]

where \(u_0 = \sin\theta_0\cos\phi_0\) and \(v_0 = \sin\theta_0\sin\phi_0\).

Amplitude Tapering#

Amplitude tapering (windowing) reduces sidelobe levels at the cost of increased beamwidth and reduced aperture efficiency.

Taylor Taper#

Most commonly used for radar arrays. Provides specified sidelobe level with a controlled number of nearly-equal sidelobes before rolloff.

# -30 dB sidelobes, 4 nearly-equal sidelobes
taper = pa.taylor_taper_2d(16, 16, sidelobe_dB=-30, nbar=4)

# Apply to steering weights
weights = pa.steering_vector(k, geom.x, geom.y, theta0_deg=20, phi0_deg=0)
weights_tapered = weights * taper

# Check efficiency loss
efficiency = pa.compute_taper_efficiency(taper)
loss_dB = pa.compute_taper_directivity_loss(taper)
print(f"Efficiency: {efficiency:.2%}, Loss: {loss_dB:.2f} dB")

Chebyshev Taper#

Provides equi-ripple sidelobes (all sidelobes at the same level). Offers the narrowest beamwidth for a given sidelobe level.

taper = pa.chebyshev_taper_2d(16, 16, sidelobe_dB=-30)

Comparison of Tapers#

Taper

Characteristics

Typical Use

Efficiency

Uniform

Narrowest beam, -13 dB SLL

Maximum directivity needed

100%

Taylor

Specified SLL, controlled rolloff

Radar, communications

~85-95%

Chebyshev

Equi-ripple sidelobes

Minimum beamwidth for SLL

~80-90%

Hamming

Good SLL, simple

General purpose

~73%

Gaussian

Very low sidelobes, no nulls

Low-intercept radar

~70-85%

Example comparing tapers:

import matplotlib.pyplot as plt

tapers = {
    'Uniform': np.ones(256),
    'Taylor -30dB': pa.taylor_taper_2d(16, 16, sidelobe_dB=-30),
    'Chebyshev -30dB': pa.chebyshev_taper_2d(16, 16, sidelobe_dB=-30),
    'Hamming': pa.hamming_taper_2d(16, 16),
}

for name, taper in tapers.items():
    weights = pa.steering_vector(k, geom.x, geom.y, 0, 0) * taper
    theta_deg, E_plane, _ = pa.compute_pattern_cuts(geom.x, geom.y, weights, k)
    plt.plot(theta_deg, E_plane, label=name)

plt.xlabel('Theta (deg)')
plt.ylabel('Pattern (dB)')
plt.legend()
plt.grid(True)
plt.ylim(-60, 0)

Null Steering#

Null steering places pattern nulls in specific directions to reject interference while maintaining gain in the desired direction.

Projection Method#

Projects the desired steering vector onto the null space of interference directions. Simple and effective for a few nulls.

# Main beam at 20 deg, nulls at 35 and 50 deg
null_directions = [(35, 0), (50, 0)]

weights = pa.null_steering_projection(
    geom, k,
    theta_main_deg=20,
    phi_main_deg=0,
    null_directions=null_directions
)

# Verify null depth
for theta_null, phi_null in null_directions:
    depth = pa.compute_null_depth(geom, k, weights, (theta_null, phi_null))
    print(f"Null at {theta_null} deg: {depth:.1f} dB")

LCMV Beamformer#

Linearly Constrained Minimum Variance - more flexible, allows specifying response at multiple directions.

# Constraints: (theta, phi, desired_response)
constraints = [
    (20, 0, 1.0+0j),   # Unity gain at 20 deg
    (35, 0, 0.0+0j),   # Null at 35 deg
    (50, 0, 0.0+0j),   # Null at 50 deg
]

weights = pa.null_steering_lcmv(
    geom, k,
    constraints=constraints
)

Multi-Beam Synthesis#

Generate multiple simultaneous beams for tracking multiple targets or providing spatial coverage.

Superposition Method#

Simple sum of steering vectors. Beams share the available gain.

# Beams at 15, 30, and 45 degrees
beam_directions = [(15, 0), (30, 0), (45, 0)]

weights = pa.multi_beam_weights_superposition(
    geom, k,
    beam_directions
)

# Each beam is ~3 dB below single-beam gain

Orthogonal Beams#

Minimizes inter-beam coupling using orthogonalization.

weights_list = pa.multi_beam_weights_orthogonal(
    geom, k,
    beam_directions
)

# Returns list of weight vectors, one per beam
# Beams are designed to be orthogonal to each other

# Check beam isolation
isolation = pa.compute_beam_isolation(geom, k, weights_list, beam_directions)
print(f"Beam isolation: {isolation:.1f} dB")

Monopulse Patterns#

Sum and difference patterns for angle tracking.

weights_sum, weights_diff = pa.monopulse_weights(
    geom, k,
    theta0_deg=20,
    phi0_deg=0,
    plane='azimuth'  # or 'elevation'
)

# Sum pattern: conventional beam
# Difference pattern: null on axis, used for tracking

Applying Tapers to Arbitrary Geometries#

For non-rectangular arrays, use apply_taper_to_geometry:

# Create elliptical array
geom = pa.create_elliptical_array(a=4, b=3, dx=0.5)

# Apply taper based on position
weights = pa.apply_taper_to_geometry(
    geom,
    taper_type='taylor',
    sidelobe_dB=-30
)

Best Practices#

  1. Start with Taylor taper for most applications - good balance of beamwidth and sidelobe control.

  2. Use nbar >= 4 for Taylor tapers to avoid excessive beamwidth increase.

  3. Verify null depths after null steering - finite array size limits achievable null depth.

  4. Consider efficiency loss when selecting tapers - aggressive sidelobe control can cost 2-3 dB of directivity.

  5. For multi-beam, check isolation between beams when directions are close.