# LFM Chirp Timing Parameters and Pulse Repetition Interval Configuration in PLFM_RADAR

> Configure LFM chirp timing parameters and pulse repetition interval in PLFM_RADAR. Learn how 167 µs PRI, 30 µs long chirps, and 0.5 µs short chirps are defined using Verilog and Python.

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

---

**The PLFM_RADAR system implements a 167 µs pulse repetition interval using coordinated FPGA Verilog timing constants and Python configuration dataclasses that define long (30 µs) and short (0.5 µs) LFM chirps with interleaved listen and guard periods.**

The **NawfalMotii79/PLFM_RADAR** repository implements a coherent radar waveform generator that pairs linear-frequency-modulated (LFM) chirp generation in FPGA firmware with host-side Python configuration. Understanding the **LFM chirp timing parameters and pulse repetition interval configuration** is essential for modifying system behavior or adapting the design to different radar requirements.

## FPGA-Side Timing Implementation in Verilog

The core timing logic resides in **`9_Firmware/9_2_FPGA/plfm_chirp_controller.v`**. This module implements a finite state machine (FSM) that sequences through five distinct timing states to complete one full PRI cycle.

### Timing Constants Defined in `plfm_chirp_controller.v`

| Parameter | Value | Duration | Purpose |
|-----------|-------|----------|---------|
| `T1_SAMPLES` | 3600 | 30 µs | **Long chirp** transmission at 120 MHz clock |
| `T1_RADAR_LISTENING` | 16440 | 137 µs | **Listen period** after long chirp |
| `GUARD_SAMPLES` | 21048 | 175.4 µs | **Guard interval** between chirp types |
| `T2_SAMPLES` | 60 | 0.5 µs | **Short chirp** transmission |
| `T2_RADAR_LISTENING` | 20940 | 174.5 µs | **Listen period** after short chirp |

The **pulse repetition interval (PRI)** derives from the sum of all active states:

```

PRI = T1_SAMPLES + T1_RADAR_LISTENING + GUARD_SAMPLES + T2_SAMPLES + T2_RADAR_LISTENING
PRI = 3600 + 16440 + 21048 + 60 + 20940 = 62088 samples
PRI = 62088 / 120 MHz = 167 µs

```

### FSM State Transitions

The `get_max_counter` helper function (lines 42-55) determines when each state expires. The state machine cycles through:

```

LONG_CHIRP → LONG_LISTEN → GUARD_TIME → SHORT_CHIRP → SHORT_LISTEN → (repeat)

```

Each state assertion controls DAC output enable and ADC sample gating.

### Chirp Count Configuration

The constant `CHIRP_MAX = 32` defines total chirps per beam position. The design alternates long and short chirps, yielding 16 long and 16 short chirps per coherent processing interval (CPI).

## Host-Side Configuration Using `WaveformConfig`

The Python dataclass **`WaveformConfig`** in [`9_Firmware/9_3_GUI/v7/models.py`](https://github.com/NawfalMotii79/PLFM_RADAR/blob/main/9_Firmware/9_3_GUI/v7/models.py) mirrors the FPGA timing for consistent physical-unit calculations.

### Core Timing Fields

```python
from models import WaveformConfig

cfg = WaveformConfig()

print(f"chirp_duration_s: {cfg.chirp_duration_s}")  # 30e-6

print(f"pri_s: {cfg.pri_s}")                        # 167e-6

print(f"sample_rate_hz: {cfg.sample_rate_hz}")      # 100e6

```

| Field | Default Value | Physical Meaning |
|-------|---------------|----------------|
| `chirp_duration_s` | 30e-6 s | Long chirp ramp time |
| `pri_s` | 167e-6 s | Complete PRI (matches FPGA) |
| `sample_rate_hz` | 100e6 Hz | Post-DDC baseband rate |
| `bandwidth_hz` | 20e6 Hz | LFM sweep bandwidth |
| `chirps_per_subframe` | 16 | Chirps per Doppler sub-frame |
| `n_doppler_bins` | 32 | Total Doppler bins (2 sub-frames × 16) |

### Derived Resolution Properties

Lines 22-41 of [`models.py`](https://github.com/NawfalMotii79/PLFM_RADAR/blob/main/models.py) implement resolution calculations:

```python
cfg = WaveformConfig()

# Range resolution from bandwidth

print(f"range_resolution_m: {cfg.range_resolution_m}")  # ~7.5 m

# Velocity resolution from PRI and chirp count

print(f"velocity_resolution_mps: {cfg.velocity_resolution_mps}")

```

The **velocity resolution** uses the standard formula:

$$v_{\text{res}} = \frac{\lambda}{2 \cdot N_{\text{chirps}} \cdot \text{PRI}}$$

Where `N_chirps = 16` (per sub-frame) and λ derives from the carrier frequency.

## Practical Code Examples

### Verify Configuration Consistency

```python
from models import WaveformConfig

wf = WaveformConfig()

# Assert FPGA-Python timing alignment

assert wf.chirp_duration_s == 30e-6, "Long chirp mismatch with T1_SAMPLES"
assert wf.pri_s == 167e-6, "PRI mismatch with FPGA sum"

# Calculate expected FPGA samples

clock_hz = 120e6
expected_t1 = int(wf.chirp_duration_s * clock_hz)  # 3600

expected_pri = int(wf.pri_s * clock_hz)            # 62088

print(f"T1 samples: {expected_t1}, PRI samples: {expected_pri}")

```

### Simulate Complete Chirp Sequence

```python
import numpy as np

# Load FPGA LUTs

long_lut = np.loadtxt('9_Firmware/9_2_FPGA/long_chirp_lut.mem', 
                      dtype=np.uint16, base=16)

# Build timing segments (matching Verilog parameters)

t1 = long_lut[:3600]                           # long chirp

listen1 = np.zeros(16440, dtype=np.int16)      # T1 listening

guard = np.zeros(21048, dtype=np.int16)        # guard interval

t2 = long_lut[:60]  # placeholder: actual short LUT differs

listen2 = np.zeros(20940, dtype=np.int16)      # T2 listening

full_pri = np.concatenate([t1, listen1, guard, t2, listen2])
print(f"Simulated PRI: {len(full_pri)} samples = {len(full_pri)/120e6*1e6:.1f} µs")

```

### Access Register-Level Timing Updates

The [`radar_protocol.py`](https://github.com/NawfalMotii79/PLFM_RADAR/blob/main/radar_protocol.py) module handles host-to-FPGA communication. While timing constants are typically fixed at compile time, the protocol supports runtime register access:

```python

# From 9_Firmware/9_3_GUI/v7/radar_protocol.py structure

# (Actual opcode values defined in source)

class RadarProtocol:
    REG_T1_SAMPLES = 0x10      # hypothetical; verify in source

    REG_PRI_HIGH = 0x11
    REG_PRI_LOW = 0x12
    
    def set_timing(self, t1_samples: int, pri_samples: int):
        self.write_register(self.REG_T1_SAMPLES, t1_samples)
        self.write_register(self.REG_PRI_HIGH, pri_samples >> 16)
        self.write_register(self.REG_PRI_LOW, pri_samples & 0xFFFF)

```

## Key Source Files for Timing Configuration

| Path | Function |
|------|----------|
| `9_Firmware/9_2_FPGA/plfm_chirp_controller.v` | Verilog FSM implementing chirp sequencing and PRI timing |
| [`9_Firmware/9_3_GUI/v7/models.py`](https://github.com/NawfalMotii79/PLFM_RADAR/blob/main/9_Firmware/9_3_GUI/v7/models.py) | `WaveformConfig` dataclass with SI-unit timing and resolutions |
| [`9_Firmware/9_3_GUI/v7/radar_protocol.py`](https://github.com/NawfalMotii79/PLFM_RADAR/blob/main/9_Firmware/9_3_GUI/v7/radar_protocol.py) | Register access protocol for FPGA communication |
| [`9_Firmware/9_3_GUI/v7/test_v7.py`](https://github.com/NawfalMotii79/PLFM_RADAR/blob/main/9_Firmware/9_3_GUI/v7/test_v7.py) | Unit tests validating default timing values |
| `9_Firmware/9_2_FPGA/long_chirp_lut.mem` | Waveform lookup table for DAC playback |

## Summary

- **PRI = 167 µs** derives from five sequential timing blocks in `plfm_chirp_controller.v`
- **FPGA timing uses sample counts** at 120 MHz; Python uses `WaveformConfig` for physical units
- **Long chirp: 30 µs / 3600 samples**; **short chirp: 0.5 µs / 60 samples**
- **32 total chirps** per CPI (16 long, 16 short) with alternating pattern
- Consistency between FPGA and host is enforced through matching default values in [`models.py`](https://github.com/NawfalMotii79/PLFM_RADAR/blob/main/models.py) and explicit unit tests

## Frequently Asked Questions

### How is the 167 µs PRI calculated in PLFM_RADAR?

The PRI equals the sum of all state durations in `plfm_chirp_controller.v`: `T1_SAMPLES` (3600) + `T1_RADAR_LISTENING` (16440) + `GUARD_SAMPLES` (21048) + `T2_SAMPLES` (60) + `T2_RADAR_LISTENING` (20940) = 62088 samples. At the 120 MHz system clock, this yields 62088 / 120e6 = **167 µs exactly**.

### Why does the design use both long and short LFM chirps?

The long chirp (30 µs) provides **fine range resolution** for target detection, while the short chirp (0.5 µs) enables **unambiguous velocity measurement** and mitigates range-Doppler coupling. The guard interval prevents cross-interference between chirp types.

### Where are the timing parameters validated between FPGA and Python?

The [`test_v7.py`](https://github.com/NawfalMotii79/PLFM_RADAR/blob/main/test_v7.py) module contains assertions that `WaveformConfig.chirp_duration_s == 30e-6` and `pri_s == 167e-6`, ensuring the Python configuration matches the Verilog compile-time constants before system operation.

### Can the PRI be modified without recompiling the FPGA bitstream?

No—the timing constants in `plfm_chirp_controller.v` are **compile-time parameters** used for state machine counter limits. Changing PRI requires modifying `T1_SAMPLES`, `T1_RADAR_LISTENING`, and related constants, then regenerating the bitstream. The host-side `WaveformConfig` must be updated to match.