CSI Data Structure in WiFi DensePose: A Complete Technical Guide

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. 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)
  • RouterCSIParser: Handles router-specific CSI exports (lines 102-143 in 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 (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 (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 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:

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:

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:

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) 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) 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) 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) 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) 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. 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 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.

Have a question about this repo?

These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:

Share the following with your agent to get started:
curl -s "https://instagit.com/install.md"

Works with
Claude Codex Cursor VS Code OpenClaw Any MCP Client

Maintain an open-source project? Get it listed too →