# How to Implement Beam Steering with ADAR1000 Phase Shifters in a Phased Array Radar

> Learn to implement beam steering with ADAR1000 phase shifters in phased array radar. Discover how the PLFM_RADAR firmware simplifies beam control for your radar system.

- Repository: [NawfalMotii79/PLFM_RADAR](https://github.com/NawfalMotii79/PLFM_RADAR)
- Tags: how-to-guide
- Published: 2026-08-20

---

**The PLFM_RADAR firmware provides a high-level C++ API in [`ADAR1000_Manager.cpp`](https://github.com/NawfalMotii79/PLFM_RADAR/blob/main/ADAR1000_Manager.cpp) that converts target angles into 128-state phase settings using lookup tables and automatically steers the beam via SPI writes to the ADAR1000 vector modulators.**

The **PLFM_RADAR** open-source repository delivers a complete firmware stack for phased array radar systems built around the ADAR1000 beamformer IC. Implementing **beam steering with ADAR1000 phase shifters** requires precise translation of desired azimuth angles into discrete phase states, vector modulator configurations, and synchronized SPI commands across multiple array elements.

## Understanding the ADAR1000 Phase Shifter Architecture

The ADAR1000 device controls both phase and gain through vector modulator circuits. In [`9_Firmware/9_1_Microcontroller/9_1_1_C_Cpp_Libraries/ADAR1000_Manager.cpp`](https://github.com/NawfalMotii79/PLFM_RADAR/blob/main/9_Firmware/9_1_Microcontroller/9_1_1_C_Cpp_Libraries/ADAR1000_Manager.cpp) (lines 23-41), the firmware stores the **phase-lookup tables** `VM_I[128]` and `VM_Q[128]`. These arrays contain the 128-state phase values with 2.8125° resolution steps required by the ADAR1000 datasheet, mapping each phase index to specific I and Q control voltages.

## Converting Beam Angles to Phase Settings

### The Phase Calculation Formula

The core transformation from spatial angle to electrical phase occurs in `calculatePhaseSettings()` (lines 40-55). This function computes the incremental phase shift φ for each element based on the element spacing (½ λ) and target angle θ:

```

φ = (2π * d * sin(θ)) / λ

```

The resulting phase indices for all four antenna elements are stored in the `phase_settings[4]` array, ready for hardware transmission.

### High-Level Beam Angle API

The `setBeamAngle(float angle_degrees, BeamDirection direction)` method (lines 42-68) provides the primary interface for beam steering. This function automatically invokes the phase calculator, validates the hardware mode using `setAllDevicesTXMode()` or `setAllDevicesRXMode()`, and commits the configuration through `adarSetTxPhase()` or `adarSetRxPhase()`. Before any SPI transaction, the manager executes `verifyDeviceCommunication()` to ensure every ADAR1000 device is initialized and responsive.

## Implementing Custom Beam Patterns

### Static Patterns with setCustomBeamPattern()

For applications requiring arbitrary phase-gain maps rather than calculated angles, the `setCustomBeamPattern()` function (lines 70-83) accepts four-element arrays directly. This enables static null steering or specialized beam shapes by bypassing the angle-to-phase conversion.

### Full Array Control with setCustomBeamPattern16()

The 16-element helper `setCustomBeamPattern16()` (lines 72-85) extends control to the full phased array. This function manages four ADAR1000 devices simultaneously, mapping sixteen phase indices to the complete aperture for high-resolution beamforming.

## Automated Beam Sweeping

Continuous scanning without CPU blocking is implemented through the `updateBeamPosition()` timer callback (lines 96-112). The manager maintains internal sequence buffers `tx_beam_sequence_` and `rx_beam_sequence_` that store pre-computed beam configurations. The hardware updates automatically at configurable dwell times, enabling efficient raster scanning or adaptive beam schedules.

## Safety and Validation

Every beam steering operation includes hardware validation. The firmware confirms device initialization status before writing phase registers and verifies that the requested **BeamDirection** matches the current operational mode (TX or RX). These checks prevent configuration states that could damage the RF front end or produce invalid radiation patterns.

## Code Examples

Steering to a specific azimuth angle (e.g., 30°) in transmit mode:

```cpp
// Assume `adar_mgr` is a globally instantiated ADAR1000Manager
float target_angle = 30.0f;               // degrees
bool ok = adar_mgr.setBeamAngle(target_angle, ADAR1000Manager::BeamDirection::TX);
if (!ok) {
    // Handle error (e.g., device not initialized)
}

```

Setting a custom 4-element phase pattern for a static null:

```cpp
uint8_t phase[4] = {0, 32, 64, 96};      // phase indices (0-127)
uint8_t gain[4]  = {20, 20, 20, 20};     // VGA gain per element
adar_mgr.setCustomBeamPattern(phase, gain, ADAR1000Manager::BeamDirection::RX);

```

Using the 16-element helper for full array control:

```cpp
uint8_t pattern16[16] = {
    0, 8, 16, 24,   // Dev 0 channels 1-4
    32, 40, 48, 56, // Dev 1
    64, 72, 80, 88, // Dev 2
    96, 104, 112, 120  // Dev 3
};
adar_mgr.setCustomBeamPattern16(pattern16, ADAR1000Manager::BeamDirection::TX);

```

Starting an automated beam sweep from -45° to +45°:

```cpp
// Build a simple linear sweep from -45° to +45°
std::vector<ADAR1000Manager::BeamConfig> sweep;
for (int i = -45; i <= 45; i += 5) {
    uint8_t phases[4];
    adar_mgr.calculatePhaseSettings(static_cast<float>(i), phases);
    ADAR1000Manager::BeamConfig cfg = {
        .phase_settings = {phases[0], phases[1], phases[2], phases[3]},
        .gain_settings  = {20,20,20,20}
    };
    sweep.push_back(cfg);
}
adar_mgr.setBeamSequence(sweep, ADAR1000Manager::BeamDirection::TX);
adar_mgr.setBeamDwellTime(100);   // 100 ms per step
adar_mgr.startBeamSweeping();

```

## Key Source Files

- **[`9_Firmware/9_1_Microcontroller/9_1_1_C_Cpp_Libraries/ADAR1000_Manager.cpp`](https://github.com/NawfalMotii79/PLFM_RADAR/blob/main/9_Firmware/9_1_Microcontroller/9_1_1_C_Cpp_Libraries/ADAR1000_Manager.cpp)** – Implements the phase-lookup tables, angle-to-phase conversion, and all SPI writes for beam steering.
- **[`9_Firmware/9_1_Microcontroller/9_1_1_C_Cpp_Libraries/ADAR1000_Manager.h`](https://github.com/NawfalMotii79/PLFM_RADAR/blob/main/9_Firmware/9_1_Microcontroller/9_1_1_C_Cpp_Libraries/ADAR1000_Manager.h)** – Declaration of the `ADAR1000Manager` class and public APIs (`setBeamAngle`, `setCustomBeamPattern*`).
- **[`9_Firmware/tests/cross_layer/adar1000_vm_reference.py`](https://github.com/NawfalMotii79/PLFM_RADAR/blob/main/9_Firmware/tests/cross_layer/adar1000_vm_reference.py)** – Python reference encoding the same 128-state lookup tables for validation against the ADI Linux driver.
- **[`9_Firmware/tests/cross_layer/test_cross_layer_contract.py`](https://github.com/NawfalMotii79/PLFM_RADAR/blob/main/9_Firmware/tests/cross_layer/test_cross_layer_contract.py)** – Unit tests exercising `setBeamAngle`, custom beam patterns, and VM tables to guarantee beam steering specification compliance.

## Summary

- The **ADAR1000_Manager** class abstracts low-level SPI communication into angle-based APIs.
- **128-state lookup tables** (`VM_I` and `VM_Q`) provide 2.8125° phase resolution as defined in the ADAR1000 datasheet.
- **Automatic phase calculation** converts azimuth angles to per-element phase offsets using the ½ λ spacing formula.
- **Custom pattern APIs** support both 4-element and 16-element configurations for specialized beam shapes.
- **Timer-driven beam sweeping** enables continuous scanning without blocking the main processor.
- **Hardware verification** ensures safe operation through initialization checks and mode validation.

## Frequently Asked Questions

### What is the phase resolution of the ADAR1000 in this implementation?

The firmware utilizes 128 discrete phase states, providing a resolution of **2.8125° per step**. These values are stored in the `VM_I[128]` and `VM_Q[128]` lookup tables in [`ADAR1000_Manager.cpp`](https://github.com/NawfalMotii79/PLFM_RADAR/blob/main/ADAR1000_Manager.cpp) (lines 23-41) and map directly to the ADAR1000 datasheet specifications for vector modulator control.

### How does the firmware handle both transmit and receive beam steering?

The `setBeamAngle()` and `setCustomBeamPattern()` functions accept a **BeamDirection** enum parameter that specifies either TX or RX mode. The manager automatically configures the ADAR1000 devices into the appropriate state using `setAllDevicesTXMode()` or `setAllDevicesRXMode()` before writing phase values via `adarSetTxPhase()` or `adarSetRxPhase()`.

### Can I implement beam sweeping without blocking the main CPU?

Yes. The **PLFM_RADAR** firmware implements non-blocking beam sweeping through the `updateBeamPosition()` timer callback (lines 96-112). Pre-computed beam configurations are stored in `tx_beam_sequence_` or `rx_beam_sequence_` vectors, and the hardware updates automatically at intervals set by `setBeamDwellTime()`.

### Where are the phase lookup tables defined in the source code?

The phase lookup tables are defined in **[`9_Firmware/9_1_Microcontroller/9_1_1_C_Cpp_Libraries/ADAR1000_Manager.cpp`](https://github.com/NawfalMotii79/PLFM_RADAR/blob/main/9_Firmware/9_1_Microcontroller/9_1_1_C_Cpp_Libraries/ADAR1000_Manager.cpp)** at lines 23-41. The arrays `VM_I` and `VM_Q` contain the 128-state I and Q values required to drive the ADAR1000's vector modulators, and a Python reference implementation exists in [`9_Firmware/tests/cross_layer/adar1000_vm_reference.py`](https://github.com/NawfalMotii79/PLFM_RADAR/blob/main/9_Firmware/tests/cross_layer/adar1000_vm_reference.py) for cross-validation.