SDP: Sensing Data Protocol for Scalable Wireless Sensing
Published and maintained by SDP8.org — the official platform for reproducible wireless sensing.
📖 Citation
If you use SDP in your research, please cite:
@ARTICLE{11652923,
author={Zhang, Di and Huang, Jiawei and Cui, Yuanhao and Cao, Xiaowen and Han, Tony Xiao and Jing, Xiaojun},
journal={IEEE Transactions on Mobile Computing},
title={SDP: A Unified Protocol and Benchmarking Framework for Reproducible Wi-Fi Sensing},
year={2026},
volume={},
number={},
pages={1-14},
keywords={Wireless fidelity;Modeling;Frequency;Protocols;Training;Streams;Accuracy;Measurement;Tensors;Antennas;Benchmark;canonical representation;channel state information (CSI);integrated sensing and communications (ISAC);reproducibility;wireless sensing},
doi={10.1109/TMC.2026.3723025}}
🆕 What's New in v0.5.2
- Modular algorithm pipeline -- freely compose preprocessing steps with
AlgorithmStepandexecute_algorithm_steps() - Pluggable readers -- bring your own file-format reader via
register_reader()andpipeline(..., reader=)(CLI--reader) - Per-dataset pipeline presets --
widar,gait,xrf55,elderAL,ztevia--algorithm-preset - Compatibility & stability fixes -- clearer errors when an algorithm doesn't support a dataset, checkpoint always saved, Python 3.9 fix, XRF55 subset split fix
🎯 What is SDP?
SDP is a protocol-level abstraction and unified benchmark for reproducible wireless sensing.
⚠️ SDP is not a new neural network, but a standardized protocol that unifies CSI representations for fair comparison.
🆚 Why Choose WSDP?
| Capability | WSDP | SenseFi (2023) | CSIKit |
|---|---|---|---|
| Built-in Models | 19 (MLP→Mamba/GNN) | 11 (MLP→ViT) | ❌ |
| Preprocessing Algorithms | 26+ (Wavelet, STC, etc.) | ❌ | Basic |
| Datasets | 5 | 4 | ❌ |
| Pluggable Architecture | ✅ Registry | ❌ | ❌ |
| Protocol Abstraction | ✅ Unique | ❌ | ❌ |
| Training Pipeline | ✅ | ✅ | ❌ |
| CLI | ✅ Full | Basic | ✅ |
Verified from official GitHub repos on 2026-03-17.
🧠 Model Zoo (19 Models, Baseline → SOTA)
| Category | Models | Use Case |
|---|---|---|
| Baseline | MLP, CNN1D, CNN2D, LSTM | Quick experiments, comparisons |
| Mainstream | ResNet1D, ResNet2D, BiLSTM+Attn, EfficientNet | Production use |
| SOTA | ViT, Mamba, GNN, CSIModel | Cutting-edge research |
| Specialized | THAT, CSITime, PA_CSI | Task-specific architectures |
| Lightweight | WiFlexFormer, AttentionGRU | Efficient deployment |
| Cross-Domain | EI, FewSense | Domain adaptation & few-shot |
from wsdp.models import create_model, list_models
model = create_model("ResNet1D", num_classes=10, input_shape=(20, 30, 3))
⚠️ Baseline Model Architecture Note
Baseline models (MLP, CNN1D, CNN2D, LSTM) use a Spatial Encoder (Conv2d-based) to compress the
(F, A)antenna dimension before temporal processing. This prevents parameter explosion from direct(T, F, A)flattening. SeeCHANGELOG.mdfor details.
🧪 Algorithm Library (26+ Algorithms in 7 Categories)
| Category | Algorithms | Count |
|---|---|---|
| Denoising | Wavelet, Butterworth, Savitzky-Golay, Bandpass, Hampel | 5 |
| Phase Calibration | Linear, Polynomial, STC, Robust | 4 |
| Amplitude | Z-Score, Min-Max, IQR Outlier, AGC Compensation | 4 |
| Interpolation | Linear, Cubic, Nearest, Anti-alias Decimate | 4 |
| Features | Doppler, Entropy, CSI Ratio, Tensor, Conjugate Multiply, PCA Fusion | 6 |
| Detection | Variance, Change Point | 2 |
| Composition | Pipeline presets, YAML config | - |
from wsdp.algorithms import denoise, calibrate, normalize
denoised = denoise(csi, method='butterworth', order=5, cutoff=0.3)
calibrated = calibrate(csi, method='stc')
See Model Guide and Algorithm Guide.
🎯 What is SDP? (Cont.)
The Problem
Wireless sensing research often suffers from:
- ❌ Hardware-specific CSI formats
- ❌ Inconsistent preprocessing pipelines
- ❌ Unstable training results
- ❌ Large performance variance across random seeds
Result: Models cannot be fairly compared.
The Solution
SDP solves this at the protocol level, not the model level:
| Feature | Raw CSI | Other Tools | SDP |
|---|---|---|---|
| Standardized Format | ❌ Hardware-specific | ⚠️ Partial | ✅ Unified CSIFrame |
| Multi-Dataset Support | ❌ Manual parsing | ⚠️ 2-3 datasets | ✅ 5 datasets built-in |
| Preprocessing | ❌ DIY | ⚠️ Basic only | ✅ Wavelet + Phase Calib |
| Reproducibility | ❌ Random | ⚠️ Varies | ✅ 5-seed standard |
| Deep Learning | ❌ From scratch | ⚠️ Limited | ✅ CNN+Transformer |
| CLI Interface | ❌ None | ⚠️ Partial | ✅ Full CLI support |
SDP projects raw CSI into a fixed canonical frequency grid (K=30), ensuring cross-hardware comparability.
Performance Highlights
| Metric | Result |
|---|---|
| Accuracy | SOTA on 5 datasets |
| Reproducibility | 5-seed evaluation standard |
| Stability | Low variance across runs |
Figure 1: Accuracy comparison across datasets
Figure 2: Reproducibility and stability analysis
Figure 3: Ablation study results
🚀 Quick Start (3 Steps, 5 Minutes)
Step 1: Install (30 seconds)
pip install wsdp
Verify installation:
wsdp --version
Step 2: Download Dataset (2 minutes)
🔑 Required: Create a free account at SDP8.org — your account credentials are needed for dataset downloads.
🌐 VPN is recommended for downloading datasets.
Option A: From CLI (Recommended for testing)
All datasets hosted on SDP8.org:
# elderAL = smallest dataset, fastest for testing
# Use your SDP8.org email/password:
wsdp download elderAL ./data --email you@example.com --password yourpassword
# Or use a JWT token (from SDP8.org dashboard):
wsdp download elderAL ./data --token YOUR_JWT_TOKEN
# Download larger datasets:
# wsdp download widar ./data
# wsdp download gait ./data
# wsdp download xrf55 ./data
# wsdp download zte ./data --email you@example.com --password yourpassword
# ⚠️ zte requires applying for access on the SDP platform first
Option B: From SDP8.org Web Interface
Log in at sdp8.org and download datasets manually.
Required Dataset Structure:
data/
├── elderAL/ # Dataset name
│ ├── action0_static_new/ # Activity folder
│ │ ├── user0_position1_activity0/ # Sample folder
│ │ │ ├── sample1.csv
│ │ │ └── ...
│ │ └── ...
│ ├── action1_walk_new/
│ └── ...
├── widar/
│
├── gait/
│
├── xrf55/
│ └── WIFI/
│ └── sample.npy
└── zte/
Step 3: Train & Evaluate (2 minutes)
🐍 Python API (Recommended for research):
Create train.py:
from wsdp import pipeline
# Minimal call - uses default hyperparameters
pipeline("./data/elderAL", "./output", "elderAL")
# Or with custom hyperparameters
pipeline(
input_path="./data/elderAL",
output_folder="./output",
dataset="elderAL",
learning_rate=1e-3,
num_epochs=50,
batch_size=64,
)
Run:
python train.py
💻 CLI (Quick & Simple):
# Basic training
wsdp run ./data/elderAL ./output elderAL
# With hyperparameter override
wsdp run ./data/elderAL ./output elderAL --lr 0.001 --epochs 50 --batch-size 64
# With hyperparameter config file (YAML, top-level key = dataset name)
wsdp run ./data/elderAL ./output elderAL --config my_config.yaml
# Swap the model: a registered model name, or your own .py file
wsdp run ./data/elderAL ./output elderAL --model THAT
wsdp run ./data/elderAL ./output elderAL -m custom_model.py
# Custom preprocessing: algorithm config (YAML/JSON) or a preset
wsdp run ./data/elderAL ./output elderAL --algorithm-config my_algorithms.yaml
wsdp run ./data/elderAL ./output elderAL --algorithm-preset high_quality
📊 What You Get:
After training, check ./output/ (one set of files per random seed, 5 seeds by default):
output/
├── best_checkpoint_<seed>.pth # Best model checkpoint for each seed
├── training_history_<seed>.csv # Per-epoch loss & accuracy for each seed
└── cm_rs_<seed>.png # Confusion matrix for each seed
Mean and variance of Top-1 accuracy across seeds are printed to the console at the end.
✅ If you see these files, SDP is working correctly!
📊 Supported Datasets
| Dataset | Format | Subcarriers | Complex | Scenarios | Size |
|---|---|---|---|---|---|
| Widar | .dat (bfee) | 30 | ✅ | Gesture recognition | ~2GB |
| Gait | .dat (bfee, Intel IWL5300) | 30 | ✅ | Gait recognition | ~1GB |
| XRF55 | .npy | 30 | ✅ | Human activity | ~3GB |
| ElderAL | .csv | varies | ❌ | Elderly activity | ~500MB |
| ZTE | .csv | 512 | ✅ | CSI with I/Q | ~4GB |
More datasets coming soon!
🔬 Research & Customization
🧠 Plug in Your Own Model
Step 1: Create custom_model.py:
import torch
import torch.nn as nn
class YourCustomModel(nn.Module):
def __init__(self, num_classes=6):
super().__init__()
# Your architecture here
# Input shape: (Batch, Timestamp, Frequency, Antenna)
def forward(self, x):
# Your forward pass
return output
# Required: expose model class
model = YourCustomModel
Step 2: Run with your model:
wsdp run ./data/elderAL ./output elderAL -m custom_model.py
📁 Use Your Own Dataset
Labels and split groups are parsed from filenames, so name your files after
one of the built-in dataset conventions (e.g. XRF55 user_action_trial) and pass
that dataset name. If your files use a custom format, register a reader for it:
from wsdp import pipeline
from wsdp.readers import BaseReader, register_reader
class MyReader(BaseReader):
def sniff(self, file_path): return file_path.endswith(".myfmt")
def read_file(self, file_path): ... # parse the file into CSIData
register_reader("my_format", MyReader)
# Filenames follow the XRF55 convention -> dataset="xrf55" + custom reader for the format
pipeline("./data/my_dataset", "./output", "xrf55", reader="my_format")
End-to-end example: examples/scripts/custom_reader_algorithm.py.
🗺️ Codebase Map
Want to go deeper? Here's where to modify:
| Directory | Purpose | What to Modify |
|---|---|---|
models/ |
Architectures | Define or compare model architectures |
algorithms/ |
Signal Processing | Denoising, calibration, etc. |
datasets/ |
Dataset Wrappers | Add new dataset loaders |
readers/ |
File Readers | Add new format parsers |
structure/ |
Data Structures | Modify CSIFrame format |
processors/ |
Protocol Logic | Adjust canonical projection |
🔌 Pluggable Algorithm Architecture
WSDP features a Registry Pattern that makes algorithms pluggable:
from wsdp.algorithms import denoise, calibrate, register_algorithm
# Unified API — switch methods with one parameter
denoised = denoise(csi, method='butterworth', order=5)
calibrated = calibrate(csi, method='stc')
# Register your own algorithm
def my_denoise(csi, **kwargs):
return my_custom_filter(csi)
register_algorithm('denoise', 'my_method', my_denoise)
result = denoise(csi, method='my_method') # Works like built-in!
You can try this in examples/getting_started.ipynb or just in your custom pipeline!
Configuration file support:
# examples/configs/algorithms_config.yaml
denoise:
method: butterworth
params:
order: 5
cutoff: 0.3
calibrate:
method: stc
normalize:
method: z-score
from wsdp.algorithms import load_config, execute_pipeline
config = load_config('examples/configs/algorithms_config.yaml')
processed = execute_pipeline(csi, config)
Or:
pipeline(
"./data/elderAL",
"./output",
"elderAL",
algorithm_config_file='./examples/configs/algorithms_config.yaml',
)
Pipeline presets:
from wsdp.algorithms import apply_preset, execute_pipeline
# Choose a preset for your use case
steps = apply_preset('high_quality') # or 'fast', 'robust', etc.
processed = execute_pipeline(csi, steps)
Modular pipeline (compose or skip steps freely):
from wsdp.algorithms import AlgorithmStep
from wsdp.processors import ModularProcessor
steps = [
AlgorithmStep(category="denoise", method="wavelet", params={"level": 2}),
AlgorithmStep(category="normalize", method="z-score"), # calibration skipped
]
data, labels, groups = ModularProcessor(steps).process(csi_data_list, dataset="xrf55")
Config-file form: examples/configs/modular_pipeline.yaml.
📊 Algorithm Library
| Category | Algorithm | Key Function | Reference |
|---|---|---|---|
| Denoising | Wavelet | wavelet_denoise_csi() |
Donoho & Johnstone, 1994 |
| Butterworth | butterworth_denoise() |
Butterworth, 1930 | |
| Savitzky-Golay | savgol_denoise() |
Savitzky & Golay, 1964 | |
| Bandpass | bandpass_filter() |
Standard DSP | |
| Hampel | hampel_filter() |
Hampel, 1974 | |
| Phase Calibration | Linear | phase_calibration() |
Halperin et al., 2010 |
| Polynomial | polynomial_calibration() |
Extension of linear | |
| STC | stc_calibration() |
Xie et al., IEEE TWC 2019 | |
| Robust | robust_phase_sanitization() |
Wang et al., ICPADS 2012 | |
| Normalization | Z-Score | normalize_amplitude() |
Standard statistical |
| Min-Max | normalize_amplitude() |
Standard statistical | |
| AGC Compensation | agc_compensate() |
AGC gain correction | |
| Interpolation | Linear/Cubic/Nearest | interpolate_grid() |
de Boor, 1978 |
| Anti-alias Decimate | decimate() |
Anti-alias downsampling | |
| Features | Doppler | doppler_spectrum() |
Ali et al., MobiCom 2015 |
| Entropy | entropy_features() |
Shannon, 1948 | |
| CSI Ratio | csi_ratio() |
Halperin et al., 2011 | |
| Tensor Decomposition | tensor_decomposition() |
Kolda & Bader, SIAM 2009 | |
| Conjugate Multiply | conjugate_multiply() |
Antenna pair correlation | |
| PCA Fusion | pca_fusion() |
Dimensionality reduction | |
| Detection | Activity | detect_activity() |
Zhou et al., 2013 |
| Change Point | change_point_detection() |
Adams & MacKay, 2007 |
Built-in Presets:
| Preset | Denoise | Calibrate | Use Case |
|---|---|---|---|
high_quality |
Butterworth (order=5) | STC | Maximum accuracy |
fast |
Savitzky-Golay | Linear | Speed-optimized |
robust |
Wavelet | Robust | Noisy environments |
gesture_recognition |
Butterworth (order=4) | STC | Gesture tasks |
activity_detection |
Savitzky-Golay | Polynomial | HAR tasks |
localization |
Wavelet | Robust | Localization tasks |
Per-dataset presets (
widar,gait,xrf55,elderAL,zte) are also available; they currently mirror the legacy default chain (linear calibration
- wavelet denoise).
🧪 Understanding SDP (10-Min Deep Dive)
The SDP Pipeline
Raw CSI
↓
[Deterministic Sanitization]
- Phase calibration
- Wavelet denoising
↓
[Canonical Tensor Construction]
- K=30 frequency grid
- Standardized shape
↓
[Deep Learning Model]
↓
Prediction
Canonical Tensor Format
After sanitization, SDP constructs a Canonical CSI Tensor:
$$X \in \mathbb{C}^{A \times K \times T}$$
Where:
- $A$ = Number of antennas
- $K$ = 30 (fixed frequency grid)
- $T$ = Time samples
This ensures cross-hardware comparability.
Why Deterministic?
Raw CSI contains hardware distortions:
- Phase offsets
- Sampling time offsets
- Noise fluctuations
SDP enforces deterministic calibration and denoising, guaranteeing:
- ✅ Same raw CSI → Same cleaned tensor
- ✅ Reproducibility is enforced, not optional
📚 Documentation & Resources
🎓 Tutorials (Recommended Order)
| # | Resource | What You'll Learn |
|---|---|---|
| 1 | Quickstart Notebook | 5-min intro — registry exploration & processor customization |
| 2 | Getting Started Notebook | Algorithm deep-dive — phase calibration & denoising with step-by-step visualizations |
| 3 | Full Tutorial Notebook |
End-to-end workflow — install → preprocess → train → evaluate → CLI |
📘 User Guide
| Resource | Description |
|---|---|
| Installation | Setup & environment configuration |
| Quickstart Guide | First steps with WSDP |
| Algorithm Guide | How to choose and chain preprocessing algorithms |
| Python API | Programmatic usage in detail |
| CLI Reference | Command-line interface usage |
| Configuration | YAML config files & pipeline presets |
📊 Reference
| Resource | Description |
|---|---|
| Full Documentation Site | Complete MkDocs documentation |
| API Reference | All public APIs |
| Dataset Overview | Format details & download guide for all 5 datasets |
| Model Guide | All 19 models with architecture details |
| Leaderboard | Benchmark comparison across models & datasets |
| Changelog | Version history |
| Contributing | Development guide & PR process |
🗺️ Roadmap
- v0.1 - Initial protocol design
- v0.2 - 5 datasets support, CLI tool
- v0.3 - More datasets (WiFi-HAR, CSI-HAR, etc.)
- v0.4 - 19 models, 26+ algorithms, leaderboard, CI/CD, scientific bug fixes
- v0.5 - PyPI official release, online demo platform
- v1.0 - Full protocol standardization
Want a specific dataset? Open an issue and let us know!
🤝 Contributing
We welcome contributions! See CONTRIBUTING.md for:
- Development setup
- Coding guidelines
- Pull request process
📄 License
MIT License - see LICENSE file.
Made with ❤️ by the WSDP Team
Metadata
Release files for wsdp 0.5.2
For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.
Source distribution (sdist)
| File | Size | Uploaded | |
|---|---|---|---|
| wsdp-0.5.2.tar.gz | 108.6 kB | Details |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Reset |
|---|---|---|---|---|
| wsdp-0.5.2-py3-none-any.whl | Python 3 | none | any | Details |
Total release size: 227.5 kB
Release files / wsdp-0.5.2.tar.gz
| Download URL | wsdp-0.5.2.tar.gz |
|---|---|
| Size | 108.6 kB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
438a4b279b4e90695b2635e995eff2d2826f744226741b597bea8580dc7bb78a
|
|
BLAKE2b-256 checksum How to use checksums |
7c91227cfdfcdddf84489a152142e5ce27577026b4abc10fd2f8c66cbe6f04a3
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
twine/6.2.0 CPython/3.13.0
|
Release files / wsdp-0.5.2-py3-none-any.whl
| Download URL | wsdp-0.5.2-py3-none-any.whl |
|---|---|
| Size | 118.9 kB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
60843a40ab367bfd1c0281ee0d9cbfc5d945992006837d68d6bf32bfe3a0cb63
|
|
BLAKE2b-256 checksum How to use checksums |
2bb8825e8404ebb16bf6952def8be4aa237b7c3a70457796f1f699926458c1e8
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
twine/6.2.0 CPython/3.13.0
|