Metadata-Version: 2.4
Name: handoverlab
Version: 1.5.0
Summary: System-level simulator of handover and mobility procedures in dense 5G and beyond-5G networks
Author: Mostafa Jalili Sanjarani, Yasser Elmi Sola
License-Expression: MIT
Project-URL: Homepage, https://github.com/mjs19971997/handoverlab
Project-URL: Issues, https://github.com/mjs19971997/handoverlab/issues
Project-URL: Documentation, https://mjs19971997.github.io/handoverlab/
Project-URL: Changelog, https://github.com/mjs19971997/handoverlab/blob/main/CHANGELOG.md
Keywords: handover,conditional handover,mobility,system-level simulation,5G,3GPP
Classifier: Development Status :: 5 - Production/Stable
Classifier: Intended Audience :: Science/Research
Classifier: Operating System :: OS Independent
Classifier: Programming Language :: Python :: 3
Classifier: Programming Language :: Python :: 3 :: Only
Classifier: Programming Language :: Python :: 3.9
Classifier: Programming Language :: Python :: 3.10
Classifier: Programming Language :: Python :: 3.11
Classifier: Programming Language :: Python :: 3.12
Classifier: Topic :: Scientific/Engineering
Requires-Python: >=3.9
Description-Content-Type: text/markdown
License-File: LICENSE
Requires-Dist: numpy>=1.21
Requires-Dist: scipy>=1.7
Requires-Dist: pyyaml>=5.4
Requires-Dist: tomli>=1.1; python_version < "3.11"
Provides-Extra: test
Requires-Dist: pytest>=7; extra == "test"
Provides-Extra: cov
Requires-Dist: pytest>=7; extra == "cov"
Requires-Dist: pytest-cov>=4; extra == "cov"
Provides-Extra: lint
Requires-Dist: ruff>=0.6; extra == "lint"
Provides-Extra: parquet
Requires-Dist: pyarrow>=8; extra == "parquet"
Provides-Extra: report
Requires-Dist: matplotlib>=3.3; extra == "report"
Provides-Extra: gui
Requires-Dist: streamlit>=1.37; extra == "gui"
Requires-Dist: matplotlib>=3.3; extra == "gui"
Provides-Extra: docs
Requires-Dist: mkdocs>=1.6; extra == "docs"
Requires-Dist: mkdocstrings[python]>=0.26; extra == "docs"
Provides-Extra: dev
Requires-Dist: pytest>=7; extra == "dev"
Requires-Dist: pytest-cov>=4; extra == "dev"
Requires-Dist: ruff>=0.6; extra == "dev"
Requires-Dist: pyarrow>=8; extra == "dev"
Requires-Dist: matplotlib>=3.3; extra == "dev"
Requires-Dist: streamlit>=1.37; extra == "dev"
Dynamic: license-file

# HandoverLab

[![DOI](https://zenodo.org/badge/DOI/10.5281/zenodo.23409131.svg)](https://doi.org/10.5281/zenodo.23409131) [![PyPI](https://img.shields.io/pypi/v/handoverlab.svg)](https://pypi.org/project/handoverlab/)

A system-level simulator for studying handover in dense 5G and beyond-5G networks, written in Python.

HandoverLab simulates 3GPP mobility procedures (baseline handover with Event A3, Release-16
conditional handover and DAPS handover, Release-18 L1/L2-triggered mobility) for many users moving through a street grid, an open area or along a highway, with a
received-power-level radio model from TR 38.901, an L3-filtered measurement chain, a radio-link
failure model, and an outcome label for every handover attempt.

> **Status: 1.5.0** (`python -m pip install handoverlab`). The public surface (configuration and
> result formats with a schema version, the rule interface, metric names, commands and the
> documented Python names) follows the [stability policy](https://github.com/mjs19971997/handoverlab/blob/main/docs/stability.md): within 1.x it only grows,
> and anything removed is deprecated first. New models are options, so the results of earlier
> configurations do not change.

## See it

**Run and watch.** `handoverlab gui` opens a dashboard in the browser, on your computer only. Choose a
layout, users and rules, press Run, and follow the users. Each user is coloured by its serving cell:

![The dashboard map: users on the street grid, each joined to its serving site](https://raw.githubusercontent.com/mjs19971997/handoverlab/main/docs/images/dashboard.png)

**See where things happen.** The tab *Where* marks every handover, unnecessary handover and
failure at the place where it happened:

![Places of handovers and unnecessary handovers on the street grid](https://raw.githubusercontent.com/mjs19971997/handoverlab/main/docs/images/dashboard-where.png)

**Compare rules fairly.** All rules run on the same worlds, one world per seed. The tab *Compare*
shows each mean with its 95 % confidence interval over the seeds, and the paired difference to the
first rule:

![Handovers, unnecessary handovers, failures and rate of A3 and conditional handover, with confidence intervals](https://raw.githubusercontent.com/mjs19971997/handoverlab/main/docs/images/dashboard-compare.png)

**Run whole studies.** `handoverlab study` tunes every rule, checks gates, runs paired tests with
multiple-comparison correction and writes an HTML report:

![Study report: paired differences to a reference rule with bootstrap intervals](https://raw.githubusercontent.com/mjs19971997/handoverlab/main/docs/images/report-comparison.png)

## What it models

- **Scenarios**: the TR 37.885 urban street grid (configurable number of blocks; 433 m x 250 m
  pitch, 20 m streets) with sites on the pavement, an open area with a hexagonal site grid, a
  highway, explicit site lists, and built-in scenario cards (`handoverlab scenarios`).
- **Channel**: TR 38.901 UMi street canyon and UMa path loss (LOS/NLOS); line of sight from the
  building geometry plus the spatially consistent LOS state; spatially correlated shadowing; a
  time-varying component; L1 measurement error; beam gain and load-coupled interference;
  optionally a penetration loss per user (in-car loss of TR 38.901).
- **Mobility**: street-following users with turn probabilities, random waypoints in the open area,
  pedestrian and vehicular speed profiles or your own speed groups.
- **Traffic**: constant service rates per 5QI class, or a traffic model per user: full buffer,
  periodic packets (as VoIP) or FTP model 3 of TR 36.814. The load of every cell, and so the
  interference, follows the traffic of its users.
- **Measurements**: L1 samples, the TS 38.331 L3 filter, Event A3 with time-to-trigger, periodic
  reporting for conditional handover.
- **Handover procedures**: baseline handover (report, preparation, command, execution), Rel-16
  conditional handover (up to 8 prepared candidates) and DAPS handover, Rel-18 L1/L2-triggered
  mobility, behind a small rule interface. Rules that learn from outcomes are supported.
- **Failure model**: Qout/Qin with windows, N310/N311/T310, lost commands and configurations,
  execution failures, re-establishment (TR 36.839 / TR 36.842 values by default), with options for
  the link monitoring (own interferer load, restart on a new cell, ideal random access).
- **Metrics**: outcome of every attempt (useful, unnecessary, too early, wrong cell, too late,
  censored), failures by cause, ping-pong, preparation overhead, interruption, delivered rate,
  signalling (RRC messages, commands), L1 measurements of the device and network energy (EARTH
  model).
- **Reproducibility**: one world per seed shared by all compared rules (paired comparison); the
  engine draws no random numbers; golden event-log fingerprints guard every change.

## Built for beyond-5G

HandoverLab targets the questions of dense beyond-5G networks: small cells 50-300 m apart, the
upper mid-band (FR3, 7-24 GHz; the scenario `fr3-urban-15ghz`, 10 GHz by default), and the
mobility procedures beyond baseline handover: conditional handover, DAPS handover and
L1/L2-triggered mobility of Release 18, next to rules that learn from handover outcomes. The cost
of each procedure (signalling, prepared cells, interruption, energy) is measured next to its
benefit.

## Checked against ns-3

The same 24 Event-A3 settings were simulated in HandoverLab and in the packet-level simulator
ns-3 with 5G-LENA. Handover and unnecessary-handover rates agree (median ratio 0.92, rank
correlation 0.99). For failure rates, with the in-car loss of ns-3 and the restart of the link
monitoring switched on, the order of settings and conditions agrees too, also on ns-3 results
that were held back from any tuning (rank correlation 0.82-0.91). The absolute values
differ, so compare failure rates between rules within HandoverLab. The causes are traced in
[docs/calibration.md](https://github.com/mjs19971997/handoverlab/blob/main/docs/calibration.md). On two of them HandoverLab follows the 3GPP texts more closely
than ns-3:

- **In-car penetration loss.** TR 38.901 clause 7.4.3.2 gives it to a user inside a car. ns-3 adds
  it only while a user moves at 30-120 km/h and drops it above 120 km/h. HandoverLab applies it
  per user when configured (`radio.penetration_db`), at any speed.
- **Time to detect a radio problem.** TS 38.133 clause 8.1 evaluates out-of-sync over about
  200 ms. ns-3 counts control receptions instead of time, and at light traffic this took 0.65 to
  0.83 s in the reference runs. HandoverLab evaluates over time (`protocol.qout_window_s`, 0.2 s).

On a third point ns-3 was closer to the 3GPP texts: link monitoring restarts on a newly joined cell.
HandoverLab has had this as an option since 1.3.0 (`protocol.rlm_holdoff_s`). The comparison
covers baseline handover at 3.5 GHz only: 5G-LENA has no conditional handover, DAPS or
L1/L2-triggered mobility.

## What's new

- **1.5.0**: signalling and energy metrics (RRC messages, handover commands, L1 measurements,
  site power and energy per bit); why failure rates differ from ns-3, stated in the README.
- **1.4.0**: traffic models per user (`traffic.model`: full buffer, periodic, FTP model 3); a
  dashboard with confidence intervals, a map of where handovers and failures happen, and the status
  of a running study; the ns-3 link-monitoring finding in the calibration report.
- **1.3.0**: optional restart of the link monitoring on a newly joined cell (TS 38.133).
- **1.2.0**: optional penetration loss per user; the ns-3 comparison with the in-car loss of ns-3.
- **1.1.0 / 1.1.1**: optional link-monitoring model; held-back ns-3 results; issue forms, CI on
  Windows for every test.

Earlier versions and every detail: the [changelog](https://github.com/mjs19971997/handoverlab/blob/main/CHANGELOG.md).

## Install

HandoverLab needs Python 3.9 or later. NumPy, SciPy and PyYAML are installed with it; matplotlib
(reports, extra `report`), pyarrow (Parquet tables, extra `parquet`), Streamlit (the dashboard,
extra `gui`) and MkDocs (the documentation site, extra `docs`) are optional.

From PyPI:

```bash
python -m pip install handoverlab                  # the core package
python -m pip install "handoverlab[report,gui]"    # with reports and the dashboard
handoverlab verify --only pathloss fingerprints    # optional: a quick check of the installation
```

From source (for development, the tests and the examples):

```bash
git clone https://github.com/mjs19971997/handoverlab.git
cd handoverlab
python -m pip install -e ".[test,report]"    # add gui for the dashboard: ".[test,report,gui]"
python -m pytest            # optional: run the tests (a few minutes)
handoverlab verify          # optional: verification report (about two minutes)
```

## Quick start

Write an example configuration and run it:

```bash
handoverlab init config.yaml
handoverlab run config.yaml -o results/metrics.csv
```

`run` writes one row per seed and rule with all metrics and the fingerprint of the event log, to
CSV or (with a `.json` output name) to JSON together with the resolved scenario and the package
versions. `handoverlab rules` lists the available rules and `handoverlab scenarios` the built-in
scenarios (`scenario: smoke` in a configuration uses one). Configurations can be YAML, TOML or JSON;
see [examples/](https://github.com/mjs19971997/handoverlab/tree/main/examples).

A whole study, with tuning, gates, paired tests and a report (a few minutes):

```bash
handoverlab study examples/study_smoke.yaml --workers 2 --report
```

The report lands in `results/study-smoke/report/` (`report.html`, `report.md`, figures);
re-running the command skips everything that is already done, and `handoverlab status
results/study-smoke` shows what is left and how long it will take.

Or explore in the browser:

```bash
python -m pip install "handoverlab[gui]"
handoverlab gui                      # http://localhost:8501, on your computer only
```

## Example

```python
import handoverlab as hl

scenario = hl.Scenario.from_dict({
    "layout": {"kind": "street_grid", "blocks": [3, 3], "site_spacing_m": 150.0},
    "mobility": {"users": 20, "profile": "veh16"},
    "run": {"duration_s": 60.0, "warmup_s": 10.0},
})
rules = [
    {"rule": "a3", "name": "A3", "off_db": 3.0, "ttt_s": 0.16},
    {"rule": "cho", "name": "CHO", "o_prep_db": -2.0, "o_exec_db": 3.0, "ttt_exec_s": 0.08},
]
for r in hl.simulate(scenario, rules, seeds=[1]):      # both rules run on the same world
    print(r.rule, r.metrics["handovers_pm"], r.metrics["unnecessary_pm"], r.metrics["failures_pm"])
```

Sample output of [examples/traffic_models.py](https://github.com/mjs19971997/handoverlab/blob/main/examples/traffic_models.py), the same rule under the four
traffic models (one seed, 30 users, 60 s on the urban grid at 10 GHz):

```text
model        cell load  rate [Mbit/s]  delivered  SINR [dB]  failures/UE-min
fixed             0.28          14.25       0.98       24.8             0.00
full_buffer       0.51         707.08       0.48       22.4             0.04
periodic          0.28           0.04       0.96       24.9             0.00
ftp3              0.28           3.93       0.93       24.9             0.00
```

Busier cells interfere more: under full buffer the SINR drops by more than 2 dB and failures
appear.

The full script is [examples/compare_a3_cho.py](https://github.com/mjs19971997/handoverlab/blob/main/examples/compare_a3_cho.py). Writing your own
handover rule takes a few lines; see the [user guide](https://github.com/mjs19971997/handoverlab/blob/main/docs/user_guide.md).

## Documentation

The documentation site is at **<https://mjs19971997.github.io/handoverlab/>**. It is a MkDocs site (`python -m pip install -e ".[docs]"`, then `mkdocs serve`);
the pages are plain Markdown in [docs/](https://github.com/mjs19971997/handoverlab/tree/main/docs) and readable on GitHub as well.

- Tutorials: [your first run](https://github.com/mjs19971997/handoverlab/blob/main/docs/tutorials/first-run.md),
  [writing your own rule](https://github.com/mjs19971997/handoverlab/blob/main/docs/tutorials/own-rule.md),
  [a fair tuning study](https://github.com/mjs19971997/handoverlab/blob/main/docs/tutorials/tuning-study.md),
  [reading the report](https://github.com/mjs19971997/handoverlab/blob/main/docs/tutorials/reading-the-report.md).
- [User guide](https://github.com/mjs19971997/handoverlab/blob/main/docs/user_guide.md): concepts, scenarios, configuration keys, command line, Python
  API, rule plug-ins and their contract checks, event logs and their levels, verification, metrics,
  studies (tuning, gates, storage, workers, status, reports), paired statistics, limitations.
- How-to guides: [dashboard](https://github.com/mjs19971997/handoverlab/blob/main/docs/guides/dashboard.md), [add a scenario](https://github.com/mjs19971997/handoverlab/blob/main/docs/guides/add-scenario.md),
  [run on many cores](https://github.com/mjs19971997/handoverlab/blob/main/docs/guides/many-cores.md), [export event logs](https://github.com/mjs19971997/handoverlab/blob/main/docs/guides/export-event-logs.md),
  [reproduce and check results](https://github.com/mjs19971997/handoverlab/blob/main/docs/guides/reproduce.md), [DAPS and LTM](https://github.com/mjs19971997/handoverlab/blob/main/docs/guides/daps-ltm.md),
  [a learning-based rule](https://github.com/mjs19971997/handoverlab/blob/main/docs/guides/learning-rule.md), [the upper mid-band](https://github.com/mjs19971997/handoverlab/blob/main/docs/guides/upper-mid-band.md),
  [a highway](https://github.com/mjs19971997/handoverlab/blob/main/docs/guides/highway.md).
- Reference: [command line](https://github.com/mjs19971997/handoverlab/blob/main/docs/reference/cli.md), [metric names](https://github.com/mjs19971997/handoverlab/blob/main/docs/reference/metrics.md) and the
  API (generated from the docstrings, in the site); [stability policy](https://github.com/mjs19971997/handoverlab/blob/main/docs/stability.md).
- [Scenario cards](https://github.com/mjs19971997/handoverlab/tree/main/docs/scenarios): the built-in scenarios with the source of every parameter.
- [Verification](https://github.com/mjs19971997/handoverlab/blob/main/docs/verification.md), [failure-model calibration](https://github.com/mjs19971997/handoverlab/blob/main/docs/calibration.md) and
  [performance](https://github.com/mjs19971997/handoverlab/blob/main/docs/performance.md).
- [Changelog](https://github.com/mjs19971997/handoverlab/blob/main/CHANGELOG.md), [contribution guide](https://github.com/mjs19971997/handoverlab/blob/main/CONTRIBUTING.md) and [releasing](https://github.com/mjs19971997/handoverlab/blob/main/docs/RELEASING.md).

## Roadmap

1. Stage 1 (0.1.0a1): core simulator, configuration, rule interface, command line, tests.
2. Stage 2 (0.2.0a1): verification in depth (invariant checks, golden event-log fingerprints, radio
   statistics, rule contract test kit, wider bh6 regression, ns-3 reference case), event logs as
   tables, the `verify` command, scenario cards, a faster world build.
3. Stage 3 (0.3.0a1): studies: equal-budget tuning with automatic gates, the selection rule, paired
   statistics, job records and Parquet tables, resumable studies with reused runs, worker
   processes, `study` and `report` commands, batched engines.
4. Stage 4 (0.4.0a1): browser dashboard (extra `gui`), tutorials, API and command-line
   reference with a documentation site, event-log levels, `status` with the time left, the
   grid-edge policy `extend`, failure-model calibration against ns-3 / 5G-LENA.
5. Stage 5 (1.0.0rc1): the public interfaces frozen and documented with a
   schema version (configuration, results, rule interface, metric names) and a stability policy;
   how-to guides; a lint job and a coverage threshold in CI; a nightly comparison of fingerprints
   between Linux and Windows; release plumbing for PyPI and Zenodo.
6. Stage 6 (1.0.0rc2): DAPS handover, L1/L2-triggered mobility, a learning-based
   example rule and an upper mid-band scenario, all additive.
7. Stage 7 (1.0.0rc3): a highway layout with lane-keeping vehicular mobility and its scenario
   card, site lists from CSV files, all additive.
8. Stage 8 (1.0.0rc4): the 3GPP citations and the documentation checked and
   corrected; no change of results.
9. 1.0.0: the first stable release, published on PyPI.
10. 1.0.1: the dashboard image shows on the PyPI project page; no change of results.
11. 1.0.2: README links work on PyPI, the documentation site is linked, text corrections; no change
    of results.
12. 1.1.0-1.3.0: optional link-monitoring and penetration-loss models traced against ns-3; issue
    forms and CI for every test on Windows.
13. 1.4.0: traffic models per user, a richer dashboard, pictures in the README.
14. **1.5.0 (this release)**: signalling and energy metrics.
15. Later (planned, not done): conditional LTM, optional compiled kernels for the step loop, a TR 38.901
    calibration scenario.

## Citation

If you use HandoverLab, please cite it as described in [CITATION.cff](https://github.com/mjs19971997/handoverlab/blob/main/CITATION.cff) (GitHub shows
it under *Cite this repository*). The releases are archived on Zenodo:
[doi:10.5281/zenodo.23409131](https://doi.org/10.5281/zenodo.23409131) cites all versions (it resolves to
the latest); version 1.0.0 is [doi:10.5281/zenodo.23409132](https://doi.org/10.5281/zenodo.23409132)
and version 1.0.1 [doi:10.5281/zenodo.23409402](https://doi.org/10.5281/zenodo.23409402).

## Licence

MIT. See [LICENSE](https://github.com/mjs19971997/handoverlab/blob/main/LICENSE).

## Authors

Mostafa Jalili Sanjarani and Yasser Elmi Sola
