Standardize the file contract
A dataset adapter reads the native layout and writes paired NPZ and JSON files with named arrays, axes, labels, and provenance.
source files→NPZ + JSONONE SHARED GUIDE
Dataset pages show only what is unique. This guide explains the shared axes, task-based time rules, data splits, masks, and Python workflow once.
THE CORE IDEA
The original release remains intact. WiSenseHub creates auditable outputs next to it.
A dataset adapter reads the native layout and writes paired NPZ and JSON files with named arrays, axes, labels, and provenance.
source files→NPZ + JSONThe task chooses the time rate and window length. Subcarriers, Tx, and Rx stay at their original sizes unless you ask for different sizes.
CSI [T, S, Tx, Rx]·T = timeTIME BY TASK
WiSenseHub chooses a time rate and a flexible window length for each task.
[T, S, Tx, Rx]Up to 30 seconds; native S, Tx, and Rx[T, S, Tx, Rx]Up to 3 seconds; fall, activity, location, identity, motion, and proximityT = time · S = subcarriers · Tx = tx_link · Rx = rx_link.
Missing source rate: when a source file does not report its measured sampling rate, WiSenseHub records the chosen time grid as an assumption in the JSON metadata.
| Option | You can choose | What it changes |
|---|---|---|
--target-rate | Any positive Hz | Resamples time when the source rate is known. |
--duration | Any positive seconds | Overrides the automatic time window. |
--target-length | Any positive integer | Sets the exact steps per window; longer recordings create more windows. |
--interpolation | linear, nearest, or none | Chooses how time steps are resized. |
--layout | canonical or flat | Keeps [T, S, Tx, Rx] or flattens the signal axes. |
--subcarriers | Any positive integer | Selects or zero-pads S. Omit it to keep the original size. |
--tx-links | Any positive integer | Selects or zero-pads Tx. Omit it to keep the original size. |
--rx-links | Any positive integer | Selects or zero-pads Rx. Omit it to keep the original size. |
Prepared views are derived files under standardized/views/. Longer recordings become consecutive windows. Only the final remainder is padded. Time and dimension masks identify every padded value, and native standardized files stay unchanged.
DATA SPLITS
--setting does not change the signalIt only decides which samples belong to training, validation, and testing.
randomA repeatable hub-generated split. Use it for small demos when official split files are unavailable.
official or dataset-specific settingsReproduce a published protocol when the complete release includes the required subject, room, or ID information.
wisensehub prepare DATASET_ID \
--data-root data \
--setting random
The saved split is written to data/DATASET_ID/splits/random.json.
OUTPUT CONTRACT
Time length and native signal sizes can differ, but the axis names and provenance rules stay stable.
data/DATASET_ID/
├── original/
├── standardized/
│ ├── clip.npz
│ ├── clip.json
│ └── views/
├── splits/
├── reports/
└── prepare-manifest.jsonamplitude [T, S, Tx, Rx]
phase [T, S, Tx, Rx] optional
valid_mask [T]
subcarrier_mask [S]
tx_link_mask [Tx]
rx_link_mask [Rx]T is time, S is subcarrier, and Tx/Rx are antenna or device links.
valid_mask marks real time steps. subcarrier_mask, tx_link_mask, and rx_link_mask mark original signal positions. False means WiSenseHub added zero padding to meet a requested size.
USE THE RESULT
The split manifest points to standardized files, so model code does not need to understand the original dataset layout.
import json
from pathlib import Path
import numpy as np
root = Path("data/DATASET_ID")
split = json.loads((root / "splits/random.json").read_text())
sample_id = split["partitions"]["train"][0]
relative_path, separator, window = sample_id.partition("::")
path = root / relative_path
with np.load(path) as sample:
signal = sample["amplitude"]
mask = sample["valid_mask"]
dimension_masks = {
name: (sample[name].shape, int(sample[name].sum()))
for name in ("subcarrier_mask", "tx_link_mask", "rx_link_mask")
if name in sample
}
if separator:
signal = signal[int(window)]
mask = mask[int(window)]
print("shape:", signal.shape)
print("valid steps:", int(mask.sum()))
for name, (shape, real_count) in dimension_masks.items():
print(name, shape, "original positions:", real_count)