Skip to content

About

Codebase for a transformer architecture for neural positioning.

Resources

Stars

1 star

Watchers

0 watching

Forks

Repository files navigation

Spatial and Temporal Generalization in Neural Positioning

This codebase implements CSI-based user equipment (UE) positioning with supervised training, often referred to as neural positioning.

This repository accompanies the paper [2] and implements the transformer variant of the neural positioning architectures compared therein (the MLP baseline from [2] is not included in this codebase but can be found in IIP-Group/neural-positioning). The implemented pipeline maps channel-state information (CSI) from multi-AP Wi-Fi and 5G NR testbeds directly to absolute 2D UE positions. It is evaluated across three real-world datasets under four complementary generalization scenarios: random partitioning (baseline), unseen spatial regions, unseen UE trajectories, and cross-week consistency.

Overview

The pipeline implemented in this repository trains a transformer network to predict absolute UE positions from CSI amplitude features. Features are extracted from the 5G uplink channel (PUSCH) for 5G CSI data, or from the Wi-Fi uplink channel for Wi-Fi CSI data. The pipeline uses:

  • CSI Features: At each sample instant $n$, the CSI tensor $\mathbf{H}_n \in \mathbb{C}^{B \times A \times W}$ is collected from $B$ APs each with $A$ antennas over $W$ subcarriers. Per-sample features are formed by taking element-wise absolute values and normalizing by the Frobenius norm, preserving inter-AP power ratios. Specifically:

    • CAEZ-5G (Outdoor and Hallway): The full 3 276 subcarriers are low-pass filtered and downsampled by a factor of 12, yielding 273 active subcarriers per antenna. The resulting CSI tensor of shape (N, B, A, W) is passed to the transformer after Frobenius normalization.
    • CAEZ-WIFI (Office): All 104 active subcarriers of the 40 MHz IEEE 802.11a Wi-Fi uplink are used. Per 50 ms window, mean |CSI| is taken over samples in each AP bin, then each window is Frobenius-normalised in feature_extraction_simple() (see main.py).
  • Neural Network Architecture: A transformer that processes one token per AP. Each AP $b$ contributes one token, formed by projecting the flattened $(A \times W)$-dimensional CSI slice through a two-layer MLP embedding with ReLU activations. A learned positional encoding is added per AP to inject AP identity. The token sequence is processed by two transformer encoder layers, each with multi-head self-attention and a feed-forward network (FFN). All encoder token outputs are concatenated and passed through a four-layer regression head with ReLU activations and a final sigmoid, producing normalized position estimates $\hat{\mathbf{x}}_n \in [0,1]^2$, which are rescaled to metres at evaluation time.

  • Training: The network is trained with MSE loss between predicted and ground-truth normalized positions. Position labels are normalized to $[0, 1]$ using per-axis minimum and maximum coordinates computed from the baseline training fold (the random training portion after spatial and temporal holdouts are removed); the same min/max mapping is applied to all evaluation sets. Training uses the AdamW optimizer with cosine annealing learning rate decay to a minimum of $10^{-6}$, Kaiming uniform weight initialization, and a weight decay of $10^{-4}$. In [2], the transformer is trained for 1000 epochs with batch size 32; in this repository, epochs and batch_size in the config dict at the top of main.py control these settings.

  • Dataset Split: A single model is trained once and evaluated under complementary scenarios. For 5G datasets, spatial and temporal holdouts are removed from the pool first, then a random train/test split forms the baseline. For Wi-Fi Office datasets, fixed 80/20 folds per week (A/B and C/D) are shared across all three dataset_type choices; see Usage for the fold table. Spatial and temporal holdouts are carved from the selected training fold only. For further information and to download the dataset files, visit https://caez.ethz.ch.

Versions tested

The following versions were used for development and testing:

  • Python 3.11.3
  • NumPy 1.24.3
  • PyTorch 2.0.1
  • SciPy 1.10.1
  • Matplotlib 3.7.1
  • pandas 1.5.3
  • h5py 3.7.0
  • tqdm 4.65.0

Other compatible versions of Python 3 and these packages should work as well.

Usage

All datasets are hosted on the CAEZ website. This repository adds two dataset-generation scripts for the newly released datasets introduced in [2]:

Dataset Generation script
CAEZ-5G-HALLWAY gen_dataset_hallway.py
CAEZ-WIFI-OFFICE-2WEEKS (week 1 and week 2) make_wifi_dataset_office2weeks.py

For both of these datasets you can either:

  1. Download the pre-generated .npz files from CAEZ and use them directly for training, or
  2. Download the raw measurement archives from CAEZ and run the matching script above to build the .npz yourself.

The published .npz files are produced with the default parameters baked into these scripts (no extra flags required for a matching rebuild). The scripts are updated versions of the older gen_dataset.py and make_wifi_dataset.py workflows in IIP-Group/neural-positioning; see Step 1 under each dataset below for inputs, commands, and output layout.

CAEZ-5G-OUTDOOR (used for evaluation in [2] but not generated here) is available as a preprocessed .npz on CAEZ, or can be built with gen_dataset.py from the IIP-Group/neural-positioning repository.

Paths for training: dataset generation does not read DATA_ROOT. Pass input and output locations on the command line when running the scripts. DATA_ROOT is only used by main.py to locate .npz files for training (Step 2).


5G Datasets: CAEZ-5G-OUTDOOR and CAEZ-5G-HALLWAY

The 5G NR datasets contain CSI measurements captured from four O-RUs, each equipped with four antennas. Data was recorded at 51 Hz, with 3,276 subcarriers that are subsequently downsampled by a factor of 12, resulting in 273 active subcarriers. CSI is extracted from the uplink PUSCH channel, spanning a 100 MHz bandwidth centered at 3.45 GHz.

Step 1: Dataset generation

CAEZ-5G-OUTDOOR: Use the processed .npz file from the CAEZ webiste (WorldViz ground truth and outdoor measurement metadata; no generation script in this repo) or generate as described above.

CAEZ-5G-HALLWAY: Unpack the raw archive and run gen_dataset_hallway.py on the PyAerial PUSCH CSI pickles and position CSV (gt-positions-chopper.csv with timestamp, position_x, position_y). Position timestamps (NTP) are shifted by 37 s by default to align with CSI timestamps (PTP) before interpolation. Output is a single *_with_pos.npz with keys H, noise_var, H_max_sc, timestamps, pos, and leap_second_offset. The O-RU positions are shipped with the archive as a .npz file and should be placed in the same location as the position CSV to be added to the output dataset.

python gen_dataset_hallway.py -data_path /path/to/csi \
  --pos_csv /path/to/gt-positions-chopper.csv \
  --output /path/to/hallway_with_pos.npz

Optional flags: --leap_offset (default 37.0), --output (auto-named if omitted). Defaults for -data_path and --pos_csv are ./csi and ./gt-positions-chopper.csv.

Step 2: Training and testing

Set the base directory where your .npz files live:

export DATA_ROOT=/path/to/your/data/

Expected layout under DATA_ROOT (default data_path / dataset entries in the DATASETS dict in main.py; edit those if your files differ):

caez_5g_outdoor/caez_5g_outdoor_full.npz
5g_hallway/5g_hallway_abs_before_downsampling_12_sorted_4_rx_ant_with_pos.npz

Place or copy the outdoor and/or hallway .npz files under DATA_ROOT using the layout above or edit DATASETS in main.py.

Set dataset_type to "5g_outdoor" or "5g_hallway" in the config dict at the top of main.py, then run:

python main.py

Wi-Fi Dataset: CAEZ-WIFI-OFFICE-2WEEKS

The Wi-Fi dataset features CSI data collected from 4 APs, each equipped with 4 antennas. The CSI was extracted from Wi-Fi frames spanning a 40 MHz bandwidth centered at 5.23 GHz and including 104 active subcarriers. Data was gathered across two measurement campaigns (week 1 and week 2), conducted about one week apart in the same environment.

Step 1: Dataset generation

Run make_wifi_dataset_office2weeks.py once per campaign (week 1 and week 2) on the unpacked measurement folder (ap1/, ap2/, … with HDF5 takes, plus gt-positions-chopper.csv; optional ap_pos.npz with key ap_pos). The ground truth positions are read from the CSV file. CSI is grouped into 50 ms windows (--window-len, default 0.05); positions are interpolated to each window with gap-limited validity (--max-gt-gap, default 0.5 s). Windows with non-finite pos are removed before save.

python make_wifi_dataset_office2weeks.py /path/to/caez-wifi-office-week1 \
  --output /path/to/caez-wifi-office-week1.npz

python make_wifi_dataset_office2weeks.py /path/to/caez-wifi-office-week2 \
  --output /path/to/caez-wifi-office-week2.npz

The output .npz stores ragged per-window bins (csi_bins, timestamp_bins, snr_bins, samples_per_bin) and pos, timestamps, window_len, grid_start_timestamp, ap_numbers, and related metadata—not the dense H layout of the legacy make_wifi_dataset.py.

Step 2: Training and testing

Set the base directory where your .npz files live:

export DATA_ROOT=/path/to/your/data/

Expected layout under DATA_ROOT (default data_path / dataset entries in the DATASETS dict in main.py; edit those if your files differ):

caez-wifi-office-week1/caez-wifi-office-week1.npz
caez-wifi-office-week2/caez-wifi-office-week2.npz

Place or copy the week 1 and week 2 .npz files under DATA_ROOT using the layout above or edit DATASETS in main.py.

Set dataset_type in the config dict at the top of main.py.

Both week-1 and week-2 .npz files are always loaded. A fixed 80/20 split (seed 0) is applied independently to each week:

Fold Week Role
A Week 1 80% train
B Week 1 20% test
C Week 2 80% train
D Week 2 20% test

These four folds are reused for every Wi-Fi dataset_type; which folds are used for training, baseline testing, and cross-week testing is listed in the table under Cross-Week Consistency in Generalization Scenarios.

Spatial and temporal holdouts (if enabled) are removed from the training fold only before fitting; baseline and cross-week test folds B/D are left intact.

Then run:

python main.py

Generalization Scenarios

Four evaluation scenarios are implemented and can be toggled in the config dict. They correspond to the testing scenarios described in [2].

All scenarios share a single trained model. Spatial and temporal holdout samples are removed from the training pool/fold so they are never seen during training. For 5G, holdouts precede a random 80/20 baseline split. For Wi-Fi, the fixed folds A/B/C/D are defined first; holdouts are then applied only to the selected training fold.

Random Partitioning (Baseline)

The de facto standard evaluation scenario used in the literature. For 5G, after spatial and temporal holdouts are removed, the remaining pool is randomly partitioned into 80% training and 20% test. For Wi-Fi, the baseline test set is the fixed fold B, D, or B ∪ D depending on dataset_type (see the Cross-Week Consistency table below). This serves as the reference against which other generalization results are compared.

Unseen Regions — Spatial Validation (enable_spatial_val)

Holds out all samples whose ground-truth position falls within multiple fixed circles of radius circle_radius. The withheld samples are never seen during training and are used exclusively for evaluation. Circle placement depends on the used dataset. All use fixed circles defined in _CUSTOM_CIRCLE_CENTERS in create_validation_dataset.py.

Unseen Trajectory — Temporal Validation (enable_temporal_val)

Holds out a contiguous block of temporal_val_ratio × N samples centred on the recording (or on the week-1 portion of the training fold for wifi_office_both). The withheld samples are never seen during training and are used exclusively for evaluation.

Approximately 1% of the (training) sequence, corresponding to roughly 1 minute of CSI acquisition, is withheld. This yields unseen trajectory lengths of 21 m (5G Hallway), 41 m (5G Outdoor), 13 m (Wi-Fi Office Week 1), and 8 m (Wi-Fi Office Week 2).

Cross-Week Consistency (Wi-Fi office datasets only)

Uses the fixed folds A/B/C/D so that single-week and joint-training runs are comparable (same train and test subsets where they overlap).

dataset_type Train Baseline test Cross-week output
wifi_office_week1 A B → train_test.csv D → cross_week_validation.csv
wifi_office_week2 C D → train_test.csv B → cross_week_validation.csv
wifi_office_both A ∪ C B ∪ D → train_test.csv B → cross_week_validation_week1.csv, D → cross_week_validation_week2.csv

Note on [2], Fig. cross-week bar plot: Earlier code scored full weeks after joint training (training/test overlap) and/or used protocol variants that are not identical to this fold design. Regenerate that figure from runs that use the A/B/C/D table above.


Configuration

All options are controlled through the config dict at the top of main.py. Training loads .npz files from DATASETS, which builds paths as data_path + dataset. By default, data_path is a subdirectory of DATA_ROOT (set via environment variable or the default in main.py). Edit DATASETS if your files use different folder or file names.

Key Description
dataset_type One of 5g_outdoor, 5g_hallway, wifi_office_week1, wifi_office_week2, wifi_office_both
results_folder Sub-folder under results/ where outputs are saved. Default: <dataset_type>_<YYMMDD_HHMM> (set to a string to override)
epochs Number of training epochs (1000 in [2])
learning_rate Initial learning rate for AdamW
batch_size Mini-batch size (32 in [2])
d_model Transformer embedding dimension $d$
num_heads Number of self-attention heads
num_layers Number of transformer encoder layers
d_ff FFN hidden dimension $d_\text{ff}$
dropout Dropout rate in embedding, encoder, and head
weight_decay AdamW weight decay
enable_spatial_val Whether to run the unseen-regions spatial validation
enable_temporal_val Whether to run the unseen-trajectory temporal validation
circle_radius Circle radius in metres for spatial splits

Output

All outputs are saved to results/<results_folder>/:

  • train_test.csv, spatial_validation.csv, temporal_validation.csv, cross_week_validation.csv, cross_week_validation_week1.csv, cross_week_validation_week2.csv: per-sample predicted and ground-truth positions with Euclidean error in metres. For Wi-Fi, see the fold table under Usage for which CSV maps to folds B, D, or B ∪ D.
  • cdf.png: CDF of positioning error for all enabled splits.
  • loss_curve.png: per-epoch train and validation MSE loss (linear scale).
  • loss_curve_log.png: per-epoch train and validation MSE loss (log scale).
  • position_error_map_<split>.png: three-panel figure per split showing ground-truth positions, predicted positions, and an error heatmap coloured by positioning error.
  • config.csv: full configuration and model parameter count.

File Structure

  • main.py: Main training, evaluation, and plotting script.
  • gen_dataset_hallway.py: CAEZ-5G-HALLWAY dataset generation (CSI pickles + CSV positions).
  • make_wifi_dataset_office2weeks.py: CAEZ-WIFI-OFFICE week 1/2 dataset generation (HDF5 takes + chopper GT).
  • NN_models.py: CSITransformerNet model definition and TransformerModel trainer wrapper.
  • create_dataset.py: Dataset classes for PyTorch.
  • create_validation_dataset.py: Functions implementing the generalization scenarios from [2].
  • feature_extraction.py: CSI feature extraction functions
  • param_config.py: Configuration parameters and plotting utilities

Citation

If you use this code (or parts of it), you must cite reference [2].

License

Code is licensed under a Creative Commons Attribution-ShareAlike 4.0 International License.

References

[1] R. Wiesmayr, F. Zumegen, S. Taner, C. Dick, and C. Studer, "CSI-based user positioning, channel charting, and device classification with an NVIDIA 5G testbed," in Asilomar Conf. Signals, Syst., Comput., Oct. 2025.

[2] T.-Y. Müller, F. Zumegen, R. Wiesmayr, and C. Studer, "Spatial and Temporal Generalization of CSI-based Neural Positioning," Jun. 2026, arXiv preprint https://arxiv.org/abs/2606.18150

About

Codebase for a transformer architecture for neural positioning.

Resources

Stars

1 star

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages