# What Are Doppler Features in CSI and Why They Matter for Wi-Fi Sensing

> Discover Doppler features in CSI and their crucial role in Wi-Fi sensing. Learn how these features enable precise motion detection, velocity tracking, and even breathing analysis for advanced applications.

- Repository: [rUv/wifi-densepose](https://github.com/ruvnet/wifi-densepose)
- Tags: deep-dive
- Published: 2026-02-19

---

**Doppler features in Channel State Information (CSI) encode the frequency shifts caused by motion, enabling Wi-Fi signals to detect velocity, direction, and even micro-movements like breathing.**

In the `ruvnet/wifi-densepose` repository, Doppler features transform raw Wi-Fi packets into motion-aware representations that power human detection, pose estimation, and vital-signs monitoring. While static CSI captures environmental geometry, Doppler features specifically track how the phase of each sub-carrier changes across successive packets, revealing the dynamic components of the channel.

## Understanding Doppler Features in Channel State Information

**Channel State Information (CSI)** provides complex-valued amplitude and phase measurements for every Wi-Fi sub-carrier. In static environments, these values remain relatively constant, reflecting the geometry of walls, furniture, and other fixed objects.

**Doppler features** extract the temporal variation from this CSI stream. When objects move—whether a person walking, a hand gesturing, or a chest expanding during breathing—they induce minute frequency shifts in the reflected Wi-Fi signals. By analyzing how phase changes across consecutive packets, the system calculates the **Doppler shift** for each sub-carrier, yielding a velocity-sensitive feature vector.

## How Doppler Features Are Extracted in WiFi-DensePose

The `wifi-densepose` pipeline implements a four-stage extraction process that converts raw CSI histories into compact Doppler descriptors.

### Temporal Stacking and FFT Processing

The system first collects CSI amplitude or phase values for each sub-carrier over a short temporal window. This creates a time-series matrix where rows represent sub-carriers and columns represent consecutive packets.

A **Fast Fourier Transform (FFT)** is then applied across the time axis for each sub-carrier. This converts the temporal variations into a frequency spectrum, where peaks correspond to periodic motion patterns.

### Peak Detection and Feature Summarization

Following the FFT, the algorithm identifies the **peak Doppler frequency** for each sub-carrier by locating the frequency bin with the largest magnitude, excluding the DC (zero-frequency) component.

The system then populates a `DopplerFeatures` structure with statistical summaries:
- **Mean magnitude** across all sub-carriers
- **Spread** (standard deviation) indicating motion diversity
- **Per-sub-carrier shift vector** capturing individual frequency offsets

### Implementation Locations

In [`v1/src/core/csi_processor.py`](https://github.com/ruvnet/wifi-densepose/blob/main/v1/src/core/csi_processor.py), the `_extract_doppler_features` method handles Python-side extraction (referenced around lines 169-170). The Rust implementation in [`rust-port/wifi-densepose-rs/crates/wifi-densepose-signal/src/features.rs`](https://github.com/ruvnet/wifi-densepose/blob/main/rust-port/wifi-densepose-rs/crates/wifi-densepose-signal/src/features.rs) provides the `DopplerFeatures::from_csi_history` function (lines 323-388) for high-performance signal processing.

## Why Doppler Features Are Critical for Wi-Fi Sensing

Doppler features provide capabilities that static CSI measurements cannot achieve alone, making them essential for advanced Wi-Fi sensing applications.

### Motion Detection and Velocity Estimation

The primary advantage of Doppler analysis is direct **velocity measurement**. Moving objects induce frequency shifts proportional to their speed and direction relative to the transceiver. This enables the `detect_human_presence` function in [`csi_processor.py`](https://github.com/ruvnet/wifi-densepose/blob/main/csi_processor.py) to distinguish between stationary environments and moving humans, even when amplitude changes are subtle.

### Micro-Doppler for Vital Signs Monitoring

**Micro-Doppler** refers to minute frequency shifts caused by tiny movements—specifically chest displacements during breathing (0.1–0.5 Hz) and heartbeats (0.8–2 Hz). The WiFi-Mat disaster-response module described in the repository's [`README.md`](https://github.com/ruvnet/wifi-densepose/blob/main/README.md) leverages these micro-Doppler signatures to detect vital signs through walls, enabling search-and-rescue operations without visual contact.

### Robustness to Multipath Environments

Static CSI suffers in environments with heavy multipath reflections, where fixed objects create complex interference patterns. **Doppler features are inherently robust to static multipath** because they measure frequency changes rather than absolute phase values. Static reflections contribute to the DC component (which is excluded during peak detection), while dynamic targets produce distinct frequency peaks, improving discrimination of moving targets against cluttered backgrounds.

### Machine Learning Input for Pose Estimation

The `DensePose` head in the system consumes `CSIFeatures` structures that include Doppler vectors alongside amplitude, phase, and correlation features. Doppler provides a **compact, physics-based motion descriptor** that neural networks can interpret as velocity cues, improving the accuracy of human pose estimation from Wi-Fi signals alone.

## Implementation Examples

### Python – Extracting Doppler with the Processor

The `CsiProcessor` class provides a high-level interface for Doppler extraction:

```python
from v1.src.core.csi_processor import CsiProcessor
import numpy as np

# Mock CSI frame: 10 sub-carriers, single antenna

csi_frame = np.random.rand(10)

processor = CsiProcessor(enable_feature_extraction=True)
features = processor.extract_features(csi_frame)

# Doppler shift is a NumPy array of length = #sub-carriers

print("Doppler shifts (Hz):", features.doppler_shift)   # → shape (10,)

```

This extraction occurs in `_extract_doppler_features` within [`v1/src/core/csi_processor.py`](https://github.com/ruvnet/wifi-densepose/blob/main/v1/src/core/csi_processor.py) (lines 169-170).

### Rust – Building DopplerFeatures from CSI History

For performance-critical applications, the Rust implementation processes CSI histories directly:

```rust
use wifi_densepose_signal::features::DopplerFeatures;
use wifi_densepose_signal::types::CsiData;

// History must contain at least two consecutive CSI frames
let doppler = DopplerFeatures::from_csi_history(&history, sampling_rate);

// Access per-sub-carrier shifts
println!("Shifts per sub-carrier: {:?}", doppler.shifts);
println!("Peak frequency: {:.2} Hz", doppler.peak_frequency);

```

The implementation resides in [`rust-port/wifi-densepose-rs/crates/wifi-densepose-signal/src/features.rs`](https://github.com/ruvnet/wifi-densepose/blob/main/rust-port/wifi-densepose-rs/crates/wifi-densepose-signal/src/features.rs) (lines 323-388).

### Unit Test Validation

The repository includes tests verifying Doppler extraction accuracy:

```python
def test_doppler_shape(sample_features):
    assert sample_features.doppler_shift.shape == (10,)

```

This test is located in [`v1/tests/unit/test_csi_processor_tdd.py`](https://github.com/ruvnet/wifi-densepose/blob/main/v1/tests/unit/test_csi_processor_tdd.py) (line 437).

## Summary

- **Doppler features in CSI** encode motion-induced frequency shifts by analyzing phase changes across consecutive Wi-Fi packets.
- The extraction pipeline involves **temporal stacking, FFT processing, and peak detection**, implemented in both Python ([`csi_processor.py`](https://github.com/ruvnet/wifi-densepose/blob/main/csi_processor.py)) and Rust ([`features.rs`](https://github.com/ruvnet/wifi-densepose/blob/main/features.rs)).
- These features enable **velocity estimation, micro-Doppler vital-signs detection, and robust human presence sensing** in multipath environments.
- Doppler vectors provide **physics-based motion descriptors** that improve machine learning models for human pose estimation in the DensePose pipeline.

## Frequently Asked Questions

### How do Doppler features differ from regular CSI amplitude and phase measurements?

While standard CSI captures static geometric information about the environment through amplitude and absolute phase values, **Doppler features specifically measure the rate of change** in these signals over time. Static CSI reflects fixed objects like walls and furniture, whereas Doppler features isolate dynamic components caused by motion, making them insensitive to static multipath interference while highly responsive to moving targets.

### What is the minimum temporal resolution required to extract meaningful Doppler features?

The `wifi-densepose` system requires **at least two consecutive CSI frames** to calculate initial Doppler shifts, though practical implementations use short temporal windows of multiple packets to improve spectral resolution. For detecting human motion (walking, gestures), sampling rates of 100-1000 Hz provide sufficient resolution, while micro-Doppler applications like heartbeat detection (0.8-2 Hz) require longer observation windows to resolve sub-Hz frequency shifts.

### Why are Doppler features more robust to multipath effects than static CSI measurements?

Static CSI suffers in environments with heavy multipath because fixed reflections create complex interference patterns that obscure target signatures. **Doppler features filter out the DC (zero-frequency) component**, which represents static multipath, and focus exclusively on frequency shifts caused by motion. Since static objects produce no frequency shift, they effectively disappear from the Doppler spectrum, allowing the system to discriminate dynamic human targets against cluttered backgrounds with greater reliability.

### How does the Rust implementation of Doppler extraction differ from the Python version?

The **Rust implementation** in [`features.rs`](https://github.com/ruvnet/wifi-densepose/blob/main/features.rs) (lines 323-388) provides a high-performance, memory-safe implementation through `DopplerFeatures::from_csi_history`, making it suitable for real-time processing and embedded deployments. The **Python implementation** in [`csi_processor.py`](https://github.com/ruvnet/wifi-densepose/blob/main/csi_processor.py) (around lines 169-170) through `_extract_doppler_features` offers greater flexibility for research and rapid prototyping, integrating seamlessly with NumPy-based machine learning pipelines. Both implementations follow the same mathematical pipeline—temporal stacking, FFT, and peak detection—but the Rust version optimizes for throughput while Python prioritizes ease of experimentation.