# CSI Data Structure in WiFi DensePose: A Complete Technical Guide

> Understand CSI data structure in WiFi DensePose. Explore the standardized CSIData dataclass with amplitude, phase matrices, and hardware metadata for ESP32 and router integration. Get the technical guide.

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

---

**WiFi DensePose represents Channel State Information as a standardized `CSIData` dataclass containing 2-D amplitude and phase matrices, hardware metadata, and validation fields, enabling seamless processing across ESP32 and router hardware sources.**

The ruvnet/wifi-densepose repository implements a unified CSI data architecture that standardizes WiFi Channel State Information for human pose estimation. Understanding the `CSIData` structure is essential for developers working with ESP32 chips, router interfaces, or custom hardware integrations, as it serves as the canonical interchange format throughout the entire pipeline.

## Core CSIData Structure

The foundation of WiFi DensePose's data layer is the `CSIData` dataclass defined in [`v1/src/hardware/csi_extractor.py`](https://github.com/ruvnet/wifi-densepose/blob/main/v1/src/hardware/csi_extractor.py). This object encapsulates all raw measurement values and metadata required by downstream pose estimation pipelines.

### Field Specifications

| Field | Type | Description |
|-------|------|-------------|
| `timestamp` | `datetime` | UTC capture time |
| `amplitude` | `np.ndarray` | 2-D array **(antennas × sub-carriers)** of magnitude values |
| `phase` | `np.ndarray` | 2-D array **(antennas × sub-carriers)** of phase values in radians |
| `frequency` | `float` | Carrier frequency in Hz |
| `bandwidth` | `float` | Channel bandwidth in Hz |
| `num_subcarriers` | `int` | Count of OFDM sub-carriers |
| `num_antennas` | `int` | Number of antenna elements |
| `snr` | `float` | Signal-to-Noise Ratio in dB |
| `metadata` | `Dict[str, Any]` | Extensible key-value storage for hardware-specific attributes |

## How CSI Data Is Created

The creation pipeline follows a three-stage process from raw hardware bytes to structured objects.

### 1. Raw Byte Acquisition

Hardware interfaces transmit CSI measurements as raw byte streams. The `CSIExtractor` class coordinates acquisition based on configuration parameters including sampling rate and buffer size.

### 2. Parser Implementation

Concrete parser classes handle hardware-specific formats:

- **ESP32CSIParser**: Processes ESP32 CSI frame formats (lines 44-98 in [`csi_extractor.py`](https://github.com/ruvnet/wifi-densepose/blob/main/csi_extractor.py))
- **RouterCSIParser**: Handles router-specific CSI exports (lines 102-143 in [`csi_extractor.py`](https://github.com/ruvnet/wifi-densepose/blob/main/csi_extractor.py))

### 3. Dataclass Instantiation

Parsers populate `CSIData` fields by extracting amplitude and phase matrices, calculating SNR, and recording hardware metadata. The resulting object is then passed to validation and streaming components.

## Validation and Error Handling

The `CSIExtractor.validate_csi_data` method enforces data integrity before downstream processing. Located in [`v1/src/hardware/csi_extractor.py`](https://github.com/ruvnet/wifi-densepose/blob/main/v1/src/hardware/csi_extractor.py) (lines 56-88), this validator performs the following checks:

- **Array validation**: Confirms amplitude and phase arrays are non-empty and dimensionally consistent
- **Physical constraints**: Verifies frequency, bandwidth, sub-carrier count, and antenna count are positive values
- **SNR bounds**: Ensures Signal-to-Noise Ratio falls within realistic bounds of -50 dB to +50 dB

Failed validations raise `CSIValidationError`, preventing corrupted or incomplete CSI samples from entering the pose estimation pipeline.

## Usage Across the Pipeline

The `CSIData` structure serves as the universal interchange format throughout the WiFi DensePose architecture.

### Hardware Interfaces

The `RouterInterface` class in [`v1/src/hardware/router_interface.py`](https://github.com/ruvnet/wifi-densepose/blob/main/v1/src/hardware/router_interface.py) (lines 37-50) returns `CSIData` objects from its `_parse_csi_response` method, standardizing router output regardless of underlying hardware differences.

### Streaming and Extraction

`CSIExtractor` streams validated `CSIData` samples to downstream components via asynchronous callbacks. The extractor manages buffering, sampling rate control, and hardware abstraction.

### Model Input

The dense pose estimation head in [`v1/src/models/densepose_head.py`](https://github.com/ruvnet/wifi-densepose/blob/main/v1/src/models/densepose_head.py) consumes batches of CSI-derived tensors. The model expects input tensors shaped according to the antenna-subcarrier dimensions defined in `CSIData`, typically transformed through preprocessing pipelines that convert raw amplitude and phase values into feature representations suitable for convolutional or transformer architectures.

## Practical Code Examples

### Parsing Raw ESP32 CSI Packets

The following example demonstrates parsing a raw ESP32 CSI byte string into a structured `CSIData` object:

```python
from src.hardware.csi_extractor import ESP32CSIParser

# Raw CSI packet from ESP32 hardware

raw = b"CSI_DATA:1700000000,3,56,2400,20,15.5,[...],[...]"

parser = ESP32CSIParser()
csi = parser.parse(raw)

print(f"Timestamp: {csi.timestamp}")
print(f"Amplitude shape: {csi.amplitude.shape}")
print(f"SNR: {csi.snr:.1f} dB")

```

### Streaming CSI Data with Validation

This example configures a `CSIExtractor` to stream validated CSI samples asynchronously:

```python
import asyncio
from src.hardware.csi_extractor import CSIExtractor

# Hardware configuration

cfg = {
    "hardware_type": "esp32",
    "sampling_rate": 10,   # Hz

    "buffer_size": 100,
    "timeout": 5,
    "validation_enabled": True,
}

extractor = CSIExtractor(cfg)

async def on_sample(sample):
    print("Got CSI sample @", sample.timestamp)

async def main():
    await extractor.connect()
    await extractor.start_streaming(on_sample)

asyncio.run(main())

```

### Retrieving CSI from Router Hardware

The following code connects to a router via SSH and retrieves structured CSI data:

```python
import asyncio
from src.hardware.router_interface import RouterInterface

# Router connection configuration

cfg = {
    "host": "192.168.1.1",
    "port": 22,
    "username": "admin",
    "password": "secret",  # In production, load from a vault

}

router = RouterInterface(cfg)

async def demo():
    await router.connect()
    csi = await router.get_csi_data()
    print("Router CSI SNR:", csi.snr)

asyncio.run(demo())

```

## Key Source Files

The WiFi DensePose CSI implementation is organized across the following critical files:

| File | Role |
|------|------|
| [[`v1/src/hardware/csi_extractor.py`](https://github.com/ruvnet/wifi-densepose/blob/main/v1/src/hardware/csi_extractor.py)](https://github.com/ruvnet/wifi-densepose/blob/main/v1/src/hardware/csi_extractor.py) | Defines `CSIData` dataclass, ESP32 and Router parsers, validation logic, and streaming extractor |
| [[`v1/src/hardware/router_interface.py`](https://github.com/ruvnet/wifi-densepose/blob/main/v1/src/hardware/router_interface.py)](https://github.com/ruvnet/wifi-densepose/blob/main/v1/src/hardware/router_interface.py) | SSH-based router client that returns `CSIData` objects from `_parse_csi_response` |
| [[`v1/src/models/densepose_head.py`](https://github.com/ruvnet/wifi-densepose/blob/main/v1/src/models/densepose_head.py)](https://github.com/ruvnet/wifi-densepose/blob/main/v1/src/models/densepose_head.py) | Dense pose estimation model that consumes CSI-derived tensors |
| [[`v1/tests/fixtures/csi_data.py`](https://github.com/ruvnet/wifi-densepose/blob/main/v1/tests/fixtures/csi_data.py)](https://github.com/ruvnet/wifi-densepose/blob/main/v1/tests/fixtures/csi_data.py) | Test fixtures providing mock `CSIData` instances for unit testing |
| [[`v1/tests/unit/test_csi_extractor.py`](https://github.com/ruvnet/wifi-densepose/blob/main/v1/tests/unit/test_csi_extractor.py)](https://github.com/ruvnet/wifi-densepose/blob/main/v1/tests/unit/test_csi_extractor.py) | Unit tests verifying parser correctness, validation rules, and streaming behavior |

## Summary

- **CSIData dataclass**: The universal structure storing amplitude, phase, metadata, and hardware parameters for every WiFi CSI measurement in the ruvnet/wifi-densepose pipeline.
- **Hardware abstraction**: Concrete parsers (`ESP32CSIParser`, `RouterCSIParser`) convert raw bytes into standardized `CSIData` objects regardless of source hardware.
- **Validation pipeline**: `CSIExtractor.validate_csi_data` enforces physical constraints and data integrity, raising `CSIValidationError` for malformed samples.
- **Downstream consumption**: Router interfaces, streaming extractors, and the dense pose estimation head all rely on `CSIData` as the canonical interchange format.

## Frequently Asked Questions

### What is the exact shape of the amplitude and phase arrays in CSIData?

The `amplitude` and `phase` fields are 2-D NumPy arrays with shape `(num_antennas, num_subcarriers)`. This matrix structure maps each antenna element to its corresponding OFDM sub-carrier measurements, enabling spatial frequency analysis required for dense pose estimation.

### How does WiFi DensePose handle different hardware sources?

The repository uses polymorphic parser classes defined in [`v1/src/hardware/csi_extractor.py`](https://github.com/ruvnet/wifi-densepose/blob/main/v1/src/hardware/csi_extractor.py). `ESP32CSIParser` handles embedded ESP32 CSI frame formats, while `RouterCSIParser` processes router-specific exports. Both implement a common interface that outputs standardized `CSIData` objects, allowing the pose estimation pipeline to remain hardware-agnostic.

### What validation checks are performed on incoming CSI data?

The `CSIExtractor.validate_csi_data` method enforces four critical constraints: non-empty amplitude and phase arrays, positive values for frequency, bandwidth, sub-carrier count, and antenna count, and SNR values within realistic bounds of -50 dB to +50 dB. Failed validations raise `CSIValidationError` to prevent corrupted data from reaching the dense pose model.

### Where is CSIData converted into model input tensors?

The dense pose estimation head in [`v1/src/models/densepose_head.py`](https://github.com/ruvnet/wifi-densepose/blob/main/v1/src/models/densepose_head.py) consumes batches of CSI-derived tensors. Preprocessing pipelines transform the raw `CSIData` amplitude and phase matrices into feature representations suitable for the model's convolutional or transformer architectures, maintaining the antenna-subcarrier dimensional relationships defined in the original data structure.