Skip to main content

HandoverLab

DOI PyPI

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.0.0, the first stable release (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) is frozen and follows the stability policy: within 1.x it only grows, and anything removed is deprecated first.

The dashboard: users coloured by their serving cell

What 1.0.0 brings

1.0.0 is the first stable release: 1.0.0rc4 with the version and the release metadata set to 1.0.0, and HandoverLab is now installed from PyPI. Results, fingerprints, job IDs and the public surface are as in 1.0.0rc4. The release candidates before it added the following.

What 1.0.0rc4 changes

  • Checked 3GPP citations: every clause, table and value cited in the scenario cards and the documentation was checked against the specifications; wrong clause numbers were corrected (for example, the highway lanes, lane width and road length are in TR 37.885 Table A-1, the inter-site distance of 500 m in clause 6.1.3), and the 35 m site offset is marked as an assumption.
  • Documentation audit: stale statements, versions and option lists corrected; every example and code snippet checked.

Only text changed: results, fingerprints, job IDs and the public surface are as in 1.0.0rc3.

What 1.0.0rc3 adds

  • A highway scenario (highway-10ghz): a new layout kind highway (the straight multi-lane road of the TR 37.885 highway case, roadside sites at a configurable spacing and offset) and lane-keeping vehicles at 70-140 km/h (profiles hwy, hwy70, hwy140, or your own speed groups). See the how-to a highway.
  • Site lists from CSV files (layout.sites_file), for every layout kind.

All of it is additive: existing defaults, results, fingerprints and study job IDs are unchanged.

What 1.0.0rc2 adds

  • DAPS handover (Rel-16, rule daps): the source keeps serving during the execution, and a failed execution falls back to the source instead of a re-establishment.
  • L1/L2-triggered mobility (Rel-18, rule ltm): candidate cells configured in advance, cell switch decided on unfiltered L1 reports, a short RACH-less execution. Both come with base classes (DAPSRule, LTMRule) for your own rules and pass the rule contract kit.
  • A learning-based example rule (a3_bandit): A3 offsets learned per cell by an epsilon-greedy bandit from the outcome labels.
  • An upper mid-band scenario (fr3-urban-15ghz): the default grid at 15 GHz.
  • How-to guides: DAPS and LTM, a learning-based rule, the upper mid-band.

All of it is additive: existing defaults, results and fingerprints are unchanged.

What 1.0.0rc1 adds

  • A frozen public surface: configuration and result files carry schema: 1; a newer schema is refused with a clear message instead of being misread. The rule interface has a version, metric names are stable (renamed metrics keep their old name as a warning alias), and a deprecation helper warns before anything goes. A test compares the surface with a stored snapshot. See stability and the metric reference.
  • How-to guides: add a scenario, run on many cores, export event logs, reproduce and check results.
  • Quality gates in CI: a lint job (ruff), a minimum test coverage, and a nightly workflow that runs the long verification on Linux and Windows and compares the golden fingerprints of both systems.
  • Release plumbing: PyPI publishing with trusted publishing when a version tag is pushed, Zenodo metadata for an archived DOI, and the release steps in RELEASING.

What stage 4 adds

  • Dashboard (pip install "handoverlab[gui]", then handoverlab gui): a local page in the browser to pick a street grid or open area, speed profile, frequency, users and up to four rules with their parameters; a run button with progress; a map of the users moving on the layout, coloured by serving cell (time slider and play); charts of handovers, failures and rate; and downloads of the results (CSV, JSON, event logs), the figures and an experiment file that handoverlab run repeats exactly. It runs on your computer only; the core package does not depend on it. See the dashboard guide.
  • Tutorials and reference: your first run, writing your own rule, a fair tuning study, reading the report; an API reference generated from the docstrings and a command-line reference (mkdocs build, pip install "handoverlab[docs]"), and a workflow that builds the site and can publish it on GitHub Pages.
  • Event-log levels off, summary and full: full adds a trace of the procedure (reports, preparations, configurations, executions, attachments, failures) from which the serving cell of every user follows at any time. The level never changes a result.
  • handoverlab status STUDY: finished, failed and remaining jobs per stage, rule runs still to simulate and the time left, estimated from the study's own finished jobs.
  • Gate policy extend: a grid whose selected value sits on its edge is widened with the same spacing and every method is tuned again with the same budget, up to a cap; each extension is a dated amendment in the study directory and the report.
  • Failure-model calibration against ns-3 / 5G-LENA, from stored ns-3 outputs only: 96 % of the ns-3 failures are late handover commands. A Qout filter window of about 0.12 s (instead of 0.2 s) fits the mean failure rate of the 24 reference settings (0.35-0.41 against 0.43 per UE-minute, rank correlation 0.97) but fails every held-back set (three times too many failures in the second ns-3 data set, different responses to the preparation delay and the number of users). So no calibrated preset is offered, the default is unchanged, and failure rates stay relative quantities; see docs/calibration.md.

What stage 3 adds

  • handoverlab study study.yaml: one file describes conditions, tuning and test seeds, the rules with their tuning grids and the comparisons; one command runs everything.
  • Tuning on an equal budget: every rule evaluates the same number of settings (a random round, then refinement around the best) on seeds that never appear in a test. Selection rule: lowest unnecessary-handover rate within a failure budget set by a reference rule, with fixed tie-breaks.
  • Gates that fail loudly or warn before any test run: a selected setting on the edge of its grid, a failure budget that constrains nothing, unequal budgets, tuning seeds reused as test seeds, a flat optimum, a budget no setting meets.
  • Paired statistics (handoverlab.stats): paired differences, t and bootstrap intervals, exact or approximate Wilcoxon signed-rank tests, Holm correction.
  • Storage and runs: one atomic record per job, Parquet tables (CSV without pyarrow), content-hashed job IDs, resumption (finished jobs are skipped, stored rule runs reused), worker processes with --workers (default: leave four cores free).
  • handoverlab report DIR: Markdown and HTML with tables and figures.
  • Faster engines: the engines of all rules of a world share one step loop; with many rules per world (as in tuning) about half the engine time, with bit-identical results.

See the user guide, docs/performance.md and the validation section of docs/verification.md.

What stage 2 adds

  • handoverlab verify: path loss and LOS probability against hand-computed TR 38.901 values, radio statistics (shadowing standard deviation and correlation, LOS share, time variation, L1 error), invariants and conservation of counts, determinism, equivalences (CHO with immediate execution = A3), golden event-log fingerprints; a pass/fail report and a JSON copy.
  • Debug mode (debug=True, run --debug, HANDOVERLAB_DEBUG=1): invariant checks at every step and a read-only observation for rules; results unchanged.
  • Event-log fingerprints: the SHA-256 of every run's event log is stored with its metrics; verify --results recomputes stored runs and compares them.
  • Event logs as tables: Parquet (with pyarrow) or CSV, typed columns, read back with read_events.
  • Contract test kit for rule plug-ins: handoverlab.testing.check_rule.
  • Scenario cards for four built-in scenarios (smoke, urban-grid-10ghz, open-area-10ghz, ns3-urban-3.5ghz), usable by name in configuration files.
  • Wider regression against the bh6 prototype: ten cases, 36 runs, identical metrics and event logs.
  • Reference comparison with ns-3 / 5G-LENA over 24 A3 settings: handover and unnecessary-handover rates within the limits (median ratios 0.95 and 1.04, rank correlation 0.99); failure rates differ (0.13 against 0.43 per UE-minute), as documented in the validation section.
  • Faster world build: about a third less time for the radio map, with bit-identical results.

See docs/verification.md and docs/performance.md.

What stage 1 contains

  • Scenarios: the TR 37.885 urban street grid (configurable number of blocks, default 3 x 3, the minimum area of TR 37.885 Table A-1; 433 m x 250 m pitch, 20 m streets) with sites on the pavement, and an open area of the same size with a hexagonal site grid; explicit site lists.
  • 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 shared by all users ("place-tied blockage"), optionally partly moved to a process along each user's path; a time-varying component; L1 measurement error; beam gain and load-coupled interference.
  • Mobility: street-following users with turn probabilities, random waypoints in the open area, pedestrian and vehicular speed profiles or your own speed groups.
  • 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) and Rel-16 conditional handover (preparation of up to 8 candidates, per-candidate execution offset and time-to-trigger, release, recovery into a prepared cell), behind a small rule interface.
  • 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).
  • Metrics: outcome of every attempt (useful, unnecessary, too early, wrong cell, too late, censored), failures by cause, ping-pong, preparation overhead, interruption, delivered rate.
  • Reproducibility: one world per seed shared by all compared rules (paired comparison); the engine draws no random numbers.

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:

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

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:

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

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

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:

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

Example

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"])

The full script is examples/compare_a3_cho.py. Writing your own handover rule takes a few lines; see the user guide.

Documentation

The documentation is a MkDocs site (python -m pip install -e ".[docs]", then mkdocs serve); the pages are plain Markdown in docs/ and readable on GitHub as well.

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 (this release): the first stable release, published on PyPI.
  10. 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 (GitHub shows it under Cite this repository). Version 1.0.0 is archived on Zenodo: doi:10.5281/zenodo.23409132.

Licence

MIT. See LICENSE.

Authors

Mostafa Jalili Sanjarani and Yasser Elmi Sola

Metadata

Release files for handoverlab 1.0.1

For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.

Source distribution (sdist)

Source distribution for handoverlab 1.0.1
File Size Uploaded Provenance
handoverlab-1.0.1.tar.gz 601.7 kB With provenance Details

Built distribution (wheel)

Table of built distributions (wheels) for handoverlab 1.0.1
File Interpreter ABI Platform
handoverlab-1.0.1-py3-none-any.whl Python 3 none any Details

Total release size: 778.4 kB

Release files / handoverlab-1.0.1.tar.gz

Download URL handoverlab-1.0.1.tar.gz
Size 601.7 kB
Tags Source
SHA-256 checksum
How to use checksums
cbee832b3ee29ed5bb7b5377bbee1427e4ce56569a877690e29a2439d5b45ee4
BLAKE2b-256 checksum
How to use checksums
fb496a6bd63ed16b9114d68ac70f9696f8b2b94bb2081eb73e5f735f31bb8152
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/7.0.0 CPython/3.13.14

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Oct 16, 2026.

Transparency log

Release files / handoverlab-1.0.1-py3-none-any.whl

Download URL handoverlab-1.0.1-py3-none-any.whl
Size 176.6 kB
Tags Python 3
SHA-256 checksum
How to use checksums
a307ffedc755bf7dfd374ec0929079c050592c25bb52d6b561aee1346283abc5
BLAKE2b-256 checksum
How to use checksums
7b19db1d35c6af775d692c706ed7fda6503aa3e0cbfe470e9146df4b45106177
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/7.0.0 CPython/3.13.14

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Oct 16, 2026.

Transparency log

Security for handoverlab 1.0.1

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

Full provenance

All 2 files in this release have been uploaded with provenance metadata.

Select from 2 files

Attestation for handoverlab-1.0.1.tar.gz

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Oct 16, 2026.

Transparency log

A note on package security

PyPI is a repository for distributing packages, not a service for auditing them. While third-party security researchers and our staff do their best to detect and remove malicious packages, PyPI cannot guarantee the quality, security or suitability of any given package.

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page