handoverlab 2.4.2__tar.gz → 2.5.0__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (308) hide show
  1. handoverlab-2.5.0/.zenodo.json +39 -0
  2. {handoverlab-2.4.2 → handoverlab-2.5.0}/CHANGELOG.md +53 -1
  3. {handoverlab-2.4.2 → handoverlab-2.5.0}/CITATION.cff +9 -4
  4. {handoverlab-2.4.2 → handoverlab-2.5.0}/PKG-INFO +33 -13
  5. {handoverlab-2.4.2 → handoverlab-2.5.0}/README.md +32 -12
  6. {handoverlab-2.4.2 → handoverlab-2.5.0}/docs/calibration.md +119 -1
  7. {handoverlab-2.4.2 → handoverlab-2.5.0}/docs/comparison.md +9 -9
  8. {handoverlab-2.4.2 → handoverlab-2.5.0}/docs/guides/ai-mobility.md +9 -0
  9. {handoverlab-2.4.2 → handoverlab-2.5.0}/docs/guides/indoor-rural.md +6 -2
  10. {handoverlab-2.4.2 → handoverlab-2.5.0}/docs/guides/learning-rule.md +8 -0
  11. {handoverlab-2.4.2 → handoverlab-2.5.0}/docs/guides/upper-mid-band.md +4 -1
  12. handoverlab-2.5.0/docs/images/dashboard-charts.png +0 -0
  13. handoverlab-2.5.0/docs/images/dashboard.png +0 -0
  14. {handoverlab-2.4.2 → handoverlab-2.5.0}/docs/index.md +1 -1
  15. {handoverlab-2.4.2 → handoverlab-2.5.0}/docs/reference/cli.md +6 -2
  16. {handoverlab-2.4.2 → handoverlab-2.5.0}/docs/scenarios/fr3-urban-15ghz.md +1 -1
  17. {handoverlab-2.4.2 → handoverlab-2.5.0}/docs/scenarios/highway-tr37885-30ghz.md +1 -1
  18. {handoverlab-2.4.2 → handoverlab-2.5.0}/docs/scenarios/highway-tr37885-4ghz.md +1 -1
  19. {handoverlab-2.4.2 → handoverlab-2.5.0}/docs/scenarios/hst-fr2-28ghz.md +1 -1
  20. {handoverlab-2.4.2 → handoverlab-2.5.0}/docs/scenarios/indoor-office-4ghz.md +1 -1
  21. {handoverlab-2.4.2 → handoverlab-2.5.0}/docs/scenarios/ns3-urban-3.5ghz.md +1 -1
  22. {handoverlab-2.4.2 → handoverlab-2.5.0}/docs/scenarios/open-area-10ghz.md +1 -1
  23. {handoverlab-2.4.2 → handoverlab-2.5.0}/docs/scenarios/rural-700mhz.md +1 -1
  24. {handoverlab-2.4.2 → handoverlab-2.5.0}/docs/scenarios/suburban-sma-7ghz.md +1 -1
  25. {handoverlab-2.4.2 → handoverlab-2.5.0}/docs/scenarios/urban-grid-10ghz.md +1 -1
  26. {handoverlab-2.4.2 → handoverlab-2.5.0}/docs/scenarios/urban-grid-28ghz-beams.md +1 -1
  27. {handoverlab-2.4.2 → handoverlab-2.5.0}/docs/scenarios/urban-macro-tr37885-4ghz.md +1 -1
  28. {handoverlab-2.4.2 → handoverlab-2.5.0}/docs/standard-compliance.md +1 -1
  29. {handoverlab-2.4.2 → handoverlab-2.5.0}/docs/user_guide.md +11 -5
  30. {handoverlab-2.4.2 → handoverlab-2.5.0}/docs/verification.md +14 -2
  31. {handoverlab-2.4.2 → handoverlab-2.5.0}/pyproject.toml +1 -1
  32. {handoverlab-2.4.2 → handoverlab-2.5.0}/src/handoverlab/__init__.py +1 -1
  33. {handoverlab-2.4.2 → handoverlab-2.5.0}/src/handoverlab/cli.py +9 -2
  34. {handoverlab-2.4.2 → handoverlab-2.5.0}/src/handoverlab/scenarios/cards/fr3-urban-15ghz.yaml +1 -1
  35. {handoverlab-2.4.2 → handoverlab-2.5.0}/src/handoverlab/scenarios/cards/highway-tr37885-30ghz.yaml +1 -1
  36. {handoverlab-2.4.2 → handoverlab-2.5.0}/src/handoverlab/scenarios/cards/highway-tr37885-4ghz.yaml +1 -1
  37. {handoverlab-2.4.2 → handoverlab-2.5.0}/src/handoverlab/scenarios/cards/hst-fr2-28ghz.yaml +1 -1
  38. {handoverlab-2.4.2 → handoverlab-2.5.0}/src/handoverlab/scenarios/cards/indoor-office-4ghz.yaml +1 -1
  39. {handoverlab-2.4.2 → handoverlab-2.5.0}/src/handoverlab/scenarios/cards/ns3-urban-3.5ghz.yaml +1 -1
  40. {handoverlab-2.4.2 → handoverlab-2.5.0}/src/handoverlab/scenarios/cards/open-area-10ghz.yaml +1 -1
  41. {handoverlab-2.4.2 → handoverlab-2.5.0}/src/handoverlab/scenarios/cards/rural-700mhz.yaml +1 -1
  42. {handoverlab-2.4.2 → handoverlab-2.5.0}/src/handoverlab/scenarios/cards/suburban-sma-7ghz.yaml +1 -1
  43. {handoverlab-2.4.2 → handoverlab-2.5.0}/src/handoverlab/scenarios/cards/urban-grid-10ghz.yaml +1 -1
  44. {handoverlab-2.4.2 → handoverlab-2.5.0}/src/handoverlab/scenarios/cards/urban-grid-28ghz-beams.yaml +1 -1
  45. {handoverlab-2.4.2 → handoverlab-2.5.0}/src/handoverlab/scenarios/cards/urban-macro-tr37885-4ghz.yaml +1 -1
  46. {handoverlab-2.4.2 → handoverlab-2.5.0}/src/handoverlab/verify/__init__.py +6 -3
  47. {handoverlab-2.4.2 → handoverlab-2.5.0}/src/handoverlab/verify/ns3.py +44 -15
  48. {handoverlab-2.4.2 → handoverlab-2.5.0}/src/handoverlab.egg-info/PKG-INFO +33 -13
  49. {handoverlab-2.4.2 → handoverlab-2.5.0}/tests/test_verify.py +42 -0
  50. handoverlab-2.4.2/.zenodo.json +0 -39
  51. handoverlab-2.4.2/docs/images/dashboard-charts.png +0 -0
  52. handoverlab-2.4.2/docs/images/dashboard.png +0 -0
  53. {handoverlab-2.4.2 → handoverlab-2.5.0}/CODE_OF_CONDUCT.md +0 -0
  54. {handoverlab-2.4.2 → handoverlab-2.5.0}/CONTRIBUTING.md +0 -0
  55. {handoverlab-2.4.2 → handoverlab-2.5.0}/LICENSE +0 -0
  56. {handoverlab-2.4.2 → handoverlab-2.5.0}/MANIFEST.in +0 -0
  57. {handoverlab-2.4.2 → handoverlab-2.5.0}/SECURITY.md +0 -0
  58. {handoverlab-2.4.2 → handoverlab-2.5.0}/docs/RELEASING.md +0 -0
  59. {handoverlab-2.4.2 → handoverlab-2.5.0}/docs/calibration-3gpp.md +0 -0
  60. {handoverlab-2.4.2 → handoverlab-2.5.0}/docs/guides/add-scenario.md +0 -0
  61. {handoverlab-2.4.2 → handoverlab-2.5.0}/docs/guides/beams-layers.md +0 -0
  62. {handoverlab-2.4.2 → handoverlab-2.5.0}/docs/guides/buffers.md +0 -0
  63. {handoverlab-2.4.2 → handoverlab-2.5.0}/docs/guides/daps-ltm.md +0 -0
  64. {handoverlab-2.4.2 → handoverlab-2.5.0}/docs/guides/dashboard.md +0 -0
  65. {handoverlab-2.4.2 → handoverlab-2.5.0}/docs/guides/dual-connectivity.md +0 -0
  66. {handoverlab-2.4.2 → handoverlab-2.5.0}/docs/guides/export-event-logs.md +0 -0
  67. {handoverlab-2.4.2 → handoverlab-2.5.0}/docs/guides/fading.md +0 -0
  68. {handoverlab-2.4.2 → handoverlab-2.5.0}/docs/guides/high-speed-train.md +0 -0
  69. {handoverlab-2.4.2 → handoverlab-2.5.0}/docs/guides/highway.md +0 -0
  70. {handoverlab-2.4.2 → handoverlab-2.5.0}/docs/guides/many-cores.md +0 -0
  71. {handoverlab-2.4.2 → handoverlab-2.5.0}/docs/guides/mro.md +0 -0
  72. {handoverlab-2.4.2 → handoverlab-2.5.0}/docs/guides/ntn.md +0 -0
  73. {handoverlab-2.4.2 → handoverlab-2.5.0}/docs/guides/radio-maps.md +0 -0
  74. {handoverlab-2.4.2 → handoverlab-2.5.0}/docs/guides/reproduce.md +0 -0
  75. {handoverlab-2.4.2 → handoverlab-2.5.0}/docs/guides/scheduler.md +0 -0
  76. {handoverlab-2.4.2 → handoverlab-2.5.0}/docs/guides/suburban-o2i.md +0 -0
  77. {handoverlab-2.4.2 → handoverlab-2.5.0}/docs/guides/ue-beams.md +0 -0
  78. {handoverlab-2.4.2 → handoverlab-2.5.0}/docs/images/ai-mobility.png +0 -0
  79. {handoverlab-2.4.2 → handoverlab-2.5.0}/docs/images/buffers.png +0 -0
  80. {handoverlab-2.4.2 → handoverlab-2.5.0}/docs/images/calibration_38901.png +0 -0
  81. {handoverlab-2.4.2 → handoverlab-2.5.0}/docs/images/calibration_7ghz.png +0 -0
  82. {handoverlab-2.4.2 → handoverlab-2.5.0}/docs/images/dashboard-compare.png +0 -0
  83. {handoverlab-2.4.2 → handoverlab-2.5.0}/docs/images/dashboard-where.png +0 -0
  84. {handoverlab-2.4.2 → handoverlab-2.5.0}/docs/images/dual-connectivity.png +0 -0
  85. {handoverlab-2.4.2 → handoverlab-2.5.0}/docs/images/fading.png +0 -0
  86. {handoverlab-2.4.2 → handoverlab-2.5.0}/docs/images/mro.png +0 -0
  87. {handoverlab-2.4.2 → handoverlab-2.5.0}/docs/images/new-scenarios.png +0 -0
  88. {handoverlab-2.4.2 → handoverlab-2.5.0}/docs/images/ntn-beams.png +0 -0
  89. {handoverlab-2.4.2 → handoverlab-2.5.0}/docs/images/procedures.png +0 -0
  90. {handoverlab-2.4.2 → handoverlab-2.5.0}/docs/images/radio-map.png +0 -0
  91. {handoverlab-2.4.2 → handoverlab-2.5.0}/docs/images/report-comparison.png +0 -0
  92. {handoverlab-2.4.2 → handoverlab-2.5.0}/docs/images/report-tuning.png +0 -0
  93. {handoverlab-2.4.2 → handoverlab-2.5.0}/docs/images/scheduler.png +0 -0
  94. {handoverlab-2.4.2 → handoverlab-2.5.0}/docs/images/ssb-beams.png +0 -0
  95. {handoverlab-2.4.2 → handoverlab-2.5.0}/docs/images/traffic.png +0 -0
  96. {handoverlab-2.4.2 → handoverlab-2.5.0}/docs/images/ue-beams.png +0 -0
  97. {handoverlab-2.4.2 → handoverlab-2.5.0}/docs/migration-2.0.md +0 -0
  98. {handoverlab-2.4.2 → handoverlab-2.5.0}/docs/performance.md +0 -0
  99. {handoverlab-2.4.2 → handoverlab-2.5.0}/docs/reference/api/config.md +0 -0
  100. {handoverlab-2.4.2 → handoverlab-2.5.0}/docs/reference/api/engine.md +0 -0
  101. {handoverlab-2.4.2 → handoverlab-2.5.0}/docs/reference/api/eventlog.md +0 -0
  102. {handoverlab-2.4.2 → handoverlab-2.5.0}/docs/reference/api/gui.md +0 -0
  103. {handoverlab-2.4.2 → handoverlab-2.5.0}/docs/reference/api/index.md +0 -0
  104. {handoverlab-2.4.2 → handoverlab-2.5.0}/docs/reference/api/mapfile.md +0 -0
  105. {handoverlab-2.4.2 → handoverlab-2.5.0}/docs/reference/api/ntn.md +0 -0
  106. {handoverlab-2.4.2 → handoverlab-2.5.0}/docs/reference/api/rules.md +0 -0
  107. {handoverlab-2.4.2 → handoverlab-2.5.0}/docs/reference/api/scenarios.md +0 -0
  108. {handoverlab-2.4.2 → handoverlab-2.5.0}/docs/reference/api/simulate.md +0 -0
  109. {handoverlab-2.4.2 → handoverlab-2.5.0}/docs/reference/api/stability.md +0 -0
  110. {handoverlab-2.4.2 → handoverlab-2.5.0}/docs/reference/api/stats.md +0 -0
  111. {handoverlab-2.4.2 → handoverlab-2.5.0}/docs/reference/api/studies.md +0 -0
  112. {handoverlab-2.4.2 → handoverlab-2.5.0}/docs/reference/api/testing.md +0 -0
  113. {handoverlab-2.4.2 → handoverlab-2.5.0}/docs/reference/api/verify.md +0 -0
  114. {handoverlab-2.4.2 → handoverlab-2.5.0}/docs/reference/metrics.md +0 -0
  115. {handoverlab-2.4.2 → handoverlab-2.5.0}/docs/reference-study.md +0 -0
  116. {handoverlab-2.4.2 → handoverlab-2.5.0}/docs/scenarios/dense-urban-2layer.md +0 -0
  117. {handoverlab-2.4.2 → handoverlab-2.5.0}/docs/scenarios/dense-urban-2layer.svg +0 -0
  118. {handoverlab-2.4.2 → handoverlab-2.5.0}/docs/scenarios/fr3-urban-15ghz.svg +0 -0
  119. {handoverlab-2.4.2 → handoverlab-2.5.0}/docs/scenarios/highway-tr37885-30ghz.svg +0 -0
  120. {handoverlab-2.4.2 → handoverlab-2.5.0}/docs/scenarios/highway-tr37885-4ghz.svg +0 -0
  121. {handoverlab-2.4.2 → handoverlab-2.5.0}/docs/scenarios/hst-fr2-28ghz.svg +0 -0
  122. {handoverlab-2.4.2 → handoverlab-2.5.0}/docs/scenarios/indoor-office-4ghz.svg +0 -0
  123. {handoverlab-2.4.2 → handoverlab-2.5.0}/docs/scenarios/ns3-urban-3.5ghz.svg +0 -0
  124. {handoverlab-2.4.2 → handoverlab-2.5.0}/docs/scenarios/ntn-leo600-2ghz.md +0 -0
  125. {handoverlab-2.4.2 → handoverlab-2.5.0}/docs/scenarios/ntn-leo600-2ghz.svg +0 -0
  126. {handoverlab-2.4.2 → handoverlab-2.5.0}/docs/scenarios/open-area-10ghz.svg +0 -0
  127. {handoverlab-2.4.2 → handoverlab-2.5.0}/docs/scenarios/rural-700mhz.svg +0 -0
  128. {handoverlab-2.4.2 → handoverlab-2.5.0}/docs/scenarios/smoke.md +0 -0
  129. {handoverlab-2.4.2 → handoverlab-2.5.0}/docs/scenarios/smoke.svg +0 -0
  130. {handoverlab-2.4.2 → handoverlab-2.5.0}/docs/scenarios/suburban-sma-7ghz.svg +0 -0
  131. {handoverlab-2.4.2 → handoverlab-2.5.0}/docs/scenarios/urban-grid-10ghz.svg +0 -0
  132. {handoverlab-2.4.2 → handoverlab-2.5.0}/docs/scenarios/urban-grid-28ghz-beams.svg +0 -0
  133. {handoverlab-2.4.2 → handoverlab-2.5.0}/docs/scenarios/urban-macro-tr37885-4ghz.svg +0 -0
  134. {handoverlab-2.4.2 → handoverlab-2.5.0}/docs/stability.md +0 -0
  135. {handoverlab-2.4.2 → handoverlab-2.5.0}/docs/time-step.md +0 -0
  136. {handoverlab-2.4.2 → handoverlab-2.5.0}/docs/tutorials/first-run.md +0 -0
  137. {handoverlab-2.4.2 → handoverlab-2.5.0}/docs/tutorials/own-rule.md +0 -0
  138. {handoverlab-2.4.2 → handoverlab-2.5.0}/docs/tutorials/reading-the-report.md +0 -0
  139. {handoverlab-2.4.2 → handoverlab-2.5.0}/docs/tutorials/tuning-study.md +0 -0
  140. {handoverlab-2.4.2 → handoverlab-2.5.0}/examples/compare_a3_cho.py +0 -0
  141. {handoverlab-2.4.2 → handoverlab-2.5.0}/examples/open_area.toml +0 -0
  142. {handoverlab-2.4.2 → handoverlab-2.5.0}/examples/radio_map.py +0 -0
  143. {handoverlab-2.4.2 → handoverlab-2.5.0}/examples/smoke.yaml +0 -0
  144. {handoverlab-2.4.2 → handoverlab-2.5.0}/examples/speed_rule.py +0 -0
  145. {handoverlab-2.4.2 → handoverlab-2.5.0}/examples/street_grid.yaml +0 -0
  146. {handoverlab-2.4.2 → handoverlab-2.5.0}/examples/study_reference.yaml +0 -0
  147. {handoverlab-2.4.2 → handoverlab-2.5.0}/examples/study_reference_explore.yaml +0 -0
  148. {handoverlab-2.4.2 → handoverlab-2.5.0}/examples/study_smoke.yaml +0 -0
  149. {handoverlab-2.4.2 → handoverlab-2.5.0}/examples/traffic_models.py +0 -0
  150. {handoverlab-2.4.2 → handoverlab-2.5.0}/mkdocs.yml +0 -0
  151. {handoverlab-2.4.2 → handoverlab-2.5.0}/setup.cfg +0 -0
  152. {handoverlab-2.4.2 → handoverlab-2.5.0}/src/handoverlab/__main__.py +0 -0
  153. {handoverlab-2.4.2 → handoverlab-2.5.0}/src/handoverlab/channel/__init__.py +0 -0
  154. {handoverlab-2.4.2 → handoverlab-2.5.0}/src/handoverlab/channel/antenna.py +0 -0
  155. {handoverlab-2.4.2 → handoverlab-2.5.0}/src/handoverlab/channel/extended.py +0 -0
  156. {handoverlab-2.4.2 → handoverlab-2.5.0}/src/handoverlab/channel/fading.py +0 -0
  157. {handoverlab-2.4.2 → handoverlab-2.5.0}/src/handoverlab/channel/l2s.py +0 -0
  158. {handoverlab-2.4.2 → handoverlab-2.5.0}/src/handoverlab/channel/ntn.py +0 -0
  159. {handoverlab-2.4.2 → handoverlab-2.5.0}/src/handoverlab/channel/pathloss.py +0 -0
  160. {handoverlab-2.4.2 → handoverlab-2.5.0}/src/handoverlab/channel/radiomap.py +0 -0
  161. {handoverlab-2.4.2 → handoverlab-2.5.0}/src/handoverlab/channel/source.py +0 -0
  162. {handoverlab-2.4.2 → handoverlab-2.5.0}/src/handoverlab/config.py +0 -0
  163. {handoverlab-2.4.2 → handoverlab-2.5.0}/src/handoverlab/engine/__init__.py +0 -0
  164. {handoverlab-2.4.2 → handoverlab-2.5.0}/src/handoverlab/engine/_kernels.py +0 -0
  165. {handoverlab-2.4.2 → handoverlab-2.5.0}/src/handoverlab/engine/batch.py +0 -0
  166. {handoverlab-2.4.2 → handoverlab-2.5.0}/src/handoverlab/engine/buffers.py +0 -0
  167. {handoverlab-2.4.2 → handoverlab-2.5.0}/src/handoverlab/engine/core.py +0 -0
  168. {handoverlab-2.4.2 → handoverlab-2.5.0}/src/handoverlab/engine/dual.py +0 -0
  169. {handoverlab-2.4.2 → handoverlab-2.5.0}/src/handoverlab/engine/extended.py +0 -0
  170. {handoverlab-2.4.2 → handoverlab-2.5.0}/src/handoverlab/engine/invariants.py +0 -0
  171. {handoverlab-2.4.2 → handoverlab-2.5.0}/src/handoverlab/engine/metrics.py +0 -0
  172. {handoverlab-2.4.2 → handoverlab-2.5.0}/src/handoverlab/engine/procedures.py +0 -0
  173. {handoverlab-2.4.2 → handoverlab-2.5.0}/src/handoverlab/engine/scheduler.py +0 -0
  174. {handoverlab-2.4.2 → handoverlab-2.5.0}/src/handoverlab/eventlog.py +0 -0
  175. {handoverlab-2.4.2 → handoverlab-2.5.0}/src/handoverlab/gui/__init__.py +0 -0
  176. {handoverlab-2.4.2 → handoverlab-2.5.0}/src/handoverlab/gui/app.py +0 -0
  177. {handoverlab-2.4.2 → handoverlab-2.5.0}/src/handoverlab/gui/logic.py +0 -0
  178. {handoverlab-2.4.2 → handoverlab-2.5.0}/src/handoverlab/io.py +0 -0
  179. {handoverlab-2.4.2 → handoverlab-2.5.0}/src/handoverlab/layout.py +0 -0
  180. {handoverlab-2.4.2 → handoverlab-2.5.0}/src/handoverlab/linklevel.py +0 -0
  181. {handoverlab-2.4.2 → handoverlab-2.5.0}/src/handoverlab/mapfile.py +0 -0
  182. {handoverlab-2.4.2 → handoverlab-2.5.0}/src/handoverlab/measurement.py +0 -0
  183. {handoverlab-2.4.2 → handoverlab-2.5.0}/src/handoverlab/mobility.py +0 -0
  184. {handoverlab-2.4.2 → handoverlab-2.5.0}/src/handoverlab/py.typed +0 -0
  185. {handoverlab-2.4.2 → handoverlab-2.5.0}/src/handoverlab/requirements.py +0 -0
  186. {handoverlab-2.4.2 → handoverlab-2.5.0}/src/handoverlab/rules/__init__.py +0 -0
  187. {handoverlab-2.4.2 → handoverlab-2.5.0}/src/handoverlab/rules/base.py +0 -0
  188. {handoverlab-2.4.2 → handoverlab-2.5.0}/src/handoverlab/rules/builtin.py +0 -0
  189. {handoverlab-2.4.2 → handoverlab-2.5.0}/src/handoverlab/rules/dqn.py +0 -0
  190. {handoverlab-2.4.2 → handoverlab-2.5.0}/src/handoverlab/rules/mro.py +0 -0
  191. {handoverlab-2.4.2 → handoverlab-2.5.0}/src/handoverlab/rules/predict.py +0 -0
  192. {handoverlab-2.4.2 → handoverlab-2.5.0}/src/handoverlab/scenarios/__init__.py +0 -0
  193. {handoverlab-2.4.2 → handoverlab-2.5.0}/src/handoverlab/scenarios/cards/dense-urban-2layer.yaml +0 -0
  194. {handoverlab-2.4.2 → handoverlab-2.5.0}/src/handoverlab/scenarios/cards/ntn-leo600-2ghz.yaml +0 -0
  195. {handoverlab-2.4.2 → handoverlab-2.5.0}/src/handoverlab/scenarios/cards/smoke.yaml +0 -0
  196. {handoverlab-2.4.2 → handoverlab-2.5.0}/src/handoverlab/seeds.py +0 -0
  197. {handoverlab-2.4.2 → handoverlab-2.5.0}/src/handoverlab/simulate.py +0 -0
  198. {handoverlab-2.4.2 → handoverlab-2.5.0}/src/handoverlab/stability.py +0 -0
  199. {handoverlab-2.4.2 → handoverlab-2.5.0}/src/handoverlab/standard.py +0 -0
  200. {handoverlab-2.4.2 → handoverlab-2.5.0}/src/handoverlab/stats.py +0 -0
  201. {handoverlab-2.4.2 → handoverlab-2.5.0}/src/handoverlab/studies/__init__.py +0 -0
  202. {handoverlab-2.4.2 → handoverlab-2.5.0}/src/handoverlab/studies/config.py +0 -0
  203. {handoverlab-2.4.2 → handoverlab-2.5.0}/src/handoverlab/studies/report.py +0 -0
  204. {handoverlab-2.4.2 → handoverlab-2.5.0}/src/handoverlab/studies/runner.py +0 -0
  205. {handoverlab-2.4.2 → handoverlab-2.5.0}/src/handoverlab/studies/status.py +0 -0
  206. {handoverlab-2.4.2 → handoverlab-2.5.0}/src/handoverlab/studies/store.py +0 -0
  207. {handoverlab-2.4.2 → handoverlab-2.5.0}/src/handoverlab/studies/study.py +0 -0
  208. {handoverlab-2.4.2 → handoverlab-2.5.0}/src/handoverlab/studies/tuning.py +0 -0
  209. {handoverlab-2.4.2 → handoverlab-2.5.0}/src/handoverlab/testing.py +0 -0
  210. {handoverlab-2.4.2 → handoverlab-2.5.0}/src/handoverlab/traffic.py +0 -0
  211. {handoverlab-2.4.2 → handoverlab-2.5.0}/src/handoverlab/verify/aimobility.py +0 -0
  212. {handoverlab-2.4.2 → handoverlab-2.5.0}/src/handoverlab/verify/calibration.py +0 -0
  213. {handoverlab-2.4.2 → handoverlab-2.5.0}/src/handoverlab/verify/data/golden.json +0 -0
  214. {handoverlab-2.4.2 → handoverlab-2.5.0}/src/handoverlab/verify/data/golden_1x.json +0 -0
  215. {handoverlab-2.4.2 → handoverlab-2.5.0}/src/handoverlab/verify/data/ns3_urban_a3.json +0 -0
  216. {handoverlab-2.4.2 → handoverlab-2.5.0}/src/handoverlab/verify/data/r1_165974.csv +0 -0
  217. {handoverlab-2.4.2 → handoverlab-2.5.0}/src/handoverlab/verify/data/r1_2506406.csv +0 -0
  218. {handoverlab-2.4.2 → handoverlab-2.5.0}/src/handoverlab/verify/engine_checks.py +0 -0
  219. {handoverlab-2.4.2 → handoverlab-2.5.0}/src/handoverlab/verify/extended.py +0 -0
  220. {handoverlab-2.4.2 → handoverlab-2.5.0}/src/handoverlab/verify/fading.py +0 -0
  221. {handoverlab-2.4.2 → handoverlab-2.5.0}/src/handoverlab/verify/golden.py +0 -0
  222. {handoverlab-2.4.2 → handoverlab-2.5.0}/src/handoverlab/verify/highway.py +0 -0
  223. {handoverlab-2.4.2 → handoverlab-2.5.0}/src/handoverlab/verify/learning.py +0 -0
  224. {handoverlab-2.4.2 → handoverlab-2.5.0}/src/handoverlab/verify/ntn.py +0 -0
  225. {handoverlab-2.4.2 → handoverlab-2.5.0}/src/handoverlab/verify/radio.py +0 -0
  226. {handoverlab-2.4.2 → handoverlab-2.5.0}/src/handoverlab/verify/report.py +0 -0
  227. {handoverlab-2.4.2 → handoverlab-2.5.0}/src/handoverlab/verify/scenarios.py +0 -0
  228. {handoverlab-2.4.2 → handoverlab-2.5.0}/src/handoverlab/verify/stored.py +0 -0
  229. {handoverlab-2.4.2 → handoverlab-2.5.0}/src/handoverlab/verify/values.py +0 -0
  230. {handoverlab-2.4.2 → handoverlab-2.5.0}/src/handoverlab/world.py +0 -0
  231. {handoverlab-2.4.2 → handoverlab-2.5.0}/src/handoverlab.egg-info/SOURCES.txt +0 -0
  232. {handoverlab-2.4.2 → handoverlab-2.5.0}/src/handoverlab.egg-info/dependency_links.txt +0 -0
  233. {handoverlab-2.4.2 → handoverlab-2.5.0}/src/handoverlab.egg-info/entry_points.txt +0 -0
  234. {handoverlab-2.4.2 → handoverlab-2.5.0}/src/handoverlab.egg-info/requires.txt +0 -0
  235. {handoverlab-2.4.2 → handoverlab-2.5.0}/src/handoverlab.egg-info/top_level.txt +0 -0
  236. {handoverlab-2.4.2 → handoverlab-2.5.0}/tests/conftest.py +0 -0
  237. {handoverlab-2.4.2 → handoverlab-2.5.0}/tests/data/bh6_reference.json +0 -0
  238. {handoverlab-2.4.2 → handoverlab-2.5.0}/tests/data/bh6_wide.json +0 -0
  239. {handoverlab-2.4.2 → handoverlab-2.5.0}/tests/data/make_bh6_reference.py +0 -0
  240. {handoverlab-2.4.2 → handoverlab-2.5.0}/tests/data/make_bh6_wide.py +0 -0
  241. {handoverlab-2.4.2 → handoverlab-2.5.0}/tests/data/public_api.json +0 -0
  242. {handoverlab-2.4.2 → handoverlab-2.5.0}/tests/helpers.py +0 -0
  243. {handoverlab-2.4.2 → handoverlab-2.5.0}/tests/test_ai_mobility.py +0 -0
  244. {handoverlab-2.4.2 → handoverlab-2.5.0}/tests/test_batch.py +0 -0
  245. {handoverlab-2.4.2 → handoverlab-2.5.0}/tests/test_beams_layers.py +0 -0
  246. {handoverlab-2.4.2 → handoverlab-2.5.0}/tests/test_buffers.py +0 -0
  247. {handoverlab-2.4.2 → handoverlab-2.5.0}/tests/test_calibration.py +0 -0
  248. {handoverlab-2.4.2 → handoverlab-2.5.0}/tests/test_channel_detail.py +0 -0
  249. {handoverlab-2.4.2 → handoverlab-2.5.0}/tests/test_cltm.py +0 -0
  250. {handoverlab-2.4.2 → handoverlab-2.5.0}/tests/test_config_cli.py +0 -0
  251. {handoverlab-2.4.2 → handoverlab-2.5.0}/tests/test_contract.py +0 -0
  252. {handoverlab-2.4.2 → handoverlab-2.5.0}/tests/test_determinism.py +0 -0
  253. {handoverlab-2.4.2 → handoverlab-2.5.0}/tests/test_dqn.py +0 -0
  254. {handoverlab-2.4.2 → handoverlab-2.5.0}/tests/test_engine_triggers.py +0 -0
  255. {handoverlab-2.4.2 → handoverlab-2.5.0}/tests/test_eventlog.py +0 -0
  256. {handoverlab-2.4.2 → handoverlab-2.5.0}/tests/test_fading.py +0 -0
  257. {handoverlab-2.4.2 → handoverlab-2.5.0}/tests/test_gui.py +0 -0
  258. {handoverlab-2.4.2 → handoverlab-2.5.0}/tests/test_highway.py +0 -0
  259. {handoverlab-2.4.2 → handoverlab-2.5.0}/tests/test_highway_tr37885.py +0 -0
  260. {handoverlab-2.4.2 → handoverlab-2.5.0}/tests/test_hst_dc.py +0 -0
  261. {handoverlab-2.4.2 → handoverlab-2.5.0}/tests/test_indoor_rural.py +0 -0
  262. {handoverlab-2.4.2 → handoverlab-2.5.0}/tests/test_invariants.py +0 -0
  263. {handoverlab-2.4.2 → handoverlab-2.5.0}/tests/test_levels_status_extend.py +0 -0
  264. {handoverlab-2.4.2 → handoverlab-2.5.0}/tests/test_lm_model.py +0 -0
  265. {handoverlab-2.4.2 → handoverlab-2.5.0}/tests/test_mapfile.py +0 -0
  266. {handoverlab-2.4.2 → handoverlab-2.5.0}/tests/test_measurement.py +0 -0
  267. {handoverlab-2.4.2 → handoverlab-2.5.0}/tests/test_mro.py +0 -0
  268. {handoverlab-2.4.2 → handoverlab-2.5.0}/tests/test_ntn.py +0 -0
  269. {handoverlab-2.4.2 → handoverlab-2.5.0}/tests/test_pathloss.py +0 -0
  270. {handoverlab-2.4.2 → handoverlab-2.5.0}/tests/test_protocol_3gpp.py +0 -0
  271. {handoverlab-2.4.2 → handoverlab-2.5.0}/tests/test_readme_boundaries.py +0 -0
  272. {handoverlab-2.4.2 → handoverlab-2.5.0}/tests/test_regression_bh6.py +0 -0
  273. {handoverlab-2.4.2 → handoverlab-2.5.0}/tests/test_regression_bh6_wide.py +0 -0
  274. {handoverlab-2.4.2 → handoverlab-2.5.0}/tests/test_rrc_detail.py +0 -0
  275. {handoverlab-2.4.2 → handoverlab-2.5.0}/tests/test_rules_rc2.py +0 -0
  276. {handoverlab-2.4.2 → handoverlab-2.5.0}/tests/test_scenarios.py +0 -0
  277. {handoverlab-2.4.2 → handoverlab-2.5.0}/tests/test_scenarios_230.py +0 -0
  278. {handoverlab-2.4.2 → handoverlab-2.5.0}/tests/test_sched_kernel.py +0 -0
  279. {handoverlab-2.4.2 → handoverlab-2.5.0}/tests/test_scheduler.py +0 -0
  280. {handoverlab-2.4.2 → handoverlab-2.5.0}/tests/test_signalling_energy.py +0 -0
  281. {handoverlab-2.4.2 → handoverlab-2.5.0}/tests/test_stability.py +0 -0
  282. {handoverlab-2.4.2 → handoverlab-2.5.0}/tests/test_stats.py +0 -0
  283. {handoverlab-2.4.2 → handoverlab-2.5.0}/tests/test_study.py +0 -0
  284. {handoverlab-2.4.2 → handoverlab-2.5.0}/tests/test_tools.py +0 -0
  285. {handoverlab-2.4.2 → handoverlab-2.5.0}/tests/test_traffic.py +0 -0
  286. {handoverlab-2.4.2 → handoverlab-2.5.0}/tests/test_tuning.py +0 -0
  287. {handoverlab-2.4.2 → handoverlab-2.5.0}/tests/test_ue_beams.py +0 -0
  288. {handoverlab-2.4.2 → handoverlab-2.5.0}/tests/test_v111.py +0 -0
  289. {handoverlab-2.4.2 → handoverlab-2.5.0}/tools/compare_fingerprints.py +0 -0
  290. {handoverlab-2.4.2 → handoverlab-2.5.0}/tools/make_ai_mobility_figure.py +0 -0
  291. {handoverlab-2.4.2 → handoverlab-2.5.0}/tools/make_beam_figure.py +0 -0
  292. {handoverlab-2.4.2 → handoverlab-2.5.0}/tools/make_buffers_figure.py +0 -0
  293. {handoverlab-2.4.2 → handoverlab-2.5.0}/tools/make_calibration_7ghz_figure.py +0 -0
  294. {handoverlab-2.4.2 → handoverlab-2.5.0}/tools/make_calibration_figure.py +0 -0
  295. {handoverlab-2.4.2 → handoverlab-2.5.0}/tools/make_dashboard_screenshots.py +0 -0
  296. {handoverlab-2.4.2 → handoverlab-2.5.0}/tools/make_dc_figure.py +0 -0
  297. {handoverlab-2.4.2 → handoverlab-2.5.0}/tools/make_fading_figure.py +0 -0
  298. {handoverlab-2.4.2 → handoverlab-2.5.0}/tools/make_mro_figure.py +0 -0
  299. {handoverlab-2.4.2 → handoverlab-2.5.0}/tools/make_ntn_figure.py +0 -0
  300. {handoverlab-2.4.2 → handoverlab-2.5.0}/tools/make_procedures_figure.py +0 -0
  301. {handoverlab-2.4.2 → handoverlab-2.5.0}/tools/make_radiomap_figure.py +0 -0
  302. {handoverlab-2.4.2 → handoverlab-2.5.0}/tools/make_readme_images.py +0 -0
  303. {handoverlab-2.4.2 → handoverlab-2.5.0}/tools/make_scenarios_figure.py +0 -0
  304. {handoverlab-2.4.2 → handoverlab-2.5.0}/tools/make_scheduler_figure.py +0 -0
  305. {handoverlab-2.4.2 → handoverlab-2.5.0}/tools/make_traffic_figure.py +0 -0
  306. {handoverlab-2.4.2 → handoverlab-2.5.0}/tools/make_ue_beams_figure.py +0 -0
  307. {handoverlab-2.4.2 → handoverlab-2.5.0}/tools/mkdocs_hooks.py +0 -0
  308. {handoverlab-2.4.2 → handoverlab-2.5.0}/tools/release_check.py +0 -0
@@ -0,0 +1,39 @@
1
+ {
2
+ "title": "HandoverLab: system-level handover simulator for 5G-Advanced and beyond-5G research",
3
+ "description": "HandoverLab is a reproducible system-level simulator for studying and comparing handover algorithms and procedures in 5G-Advanced, beyond-5G and some NTN scenarios, with explicit models, optional features and a documented validation scope. It is open source and simulates 3GPP mobility procedures (baseline handover with Event A3, Release-16 conditional and DAPS handover, Release-18 L1/L2-triggered mobility, conditional handover with the location events of Release 18, dual connectivity) for many users in dense 5G-Advanced and beyond-5G networks, on a street grid, in an open area, along a highway or a high-speed railway, in an indoor office, in suburban and rural macro cells or under the earth-moving beams of a LEO satellite. It provides a TR 38.901 received-power radio model (TR 38.811 for the satellite) with optional TDL fast fading, SSB and UE receive beams and several carriers, an optional slot scheduler with MCS, TBS and HARQ and user-plane buffers at handover, an L3-filtered measurement chain, a radio-link failure model, an outcome label for every handover attempt, a plug-in interface for handover rules (including a measurement-prediction rule after TR 38.744), equal-budget tuning studies with paired statistics, and verification against hand-computed values, golden event-log fingerprints, the 3GPP large-scale calibration and ns-3 / 5G-LENA.",
4
+ "upload_type": "software",
5
+ "access_right": "open",
6
+ "license": "MIT",
7
+ "creators": [
8
+ {
9
+ "name": "Jalili Sanjarani, Mostafa"
10
+ },
11
+ {
12
+ "name": "Elmi Sola, Yasser"
13
+ }
14
+ ],
15
+ "keywords": [
16
+ "handover",
17
+ "conditional handover",
18
+ "mobility",
19
+ "mobility robustness",
20
+ "radio-link failure",
21
+ "system-level simulation",
22
+ "5G",
23
+ "beyond 5G",
24
+ "3GPP",
25
+ "Python",
26
+ "dual connectivity",
27
+ "fast fading",
28
+ "beam management",
29
+ "NTN",
30
+ "AI/ML mobility"
31
+ ],
32
+ "related_identifiers": [
33
+ {
34
+ "identifier": "https://github.com/mjs19971997/handoverlab",
35
+ "relation": "isSupplementTo",
36
+ "scheme": "url"
37
+ }
38
+ ]
39
+ }
@@ -7,6 +7,57 @@ All notable changes to this project are documented in this file. The format foll
7
7
 
8
8
  ## [Unreleased]
9
9
 
10
+ ## [2.5.0] - 2026-10-25
11
+
12
+ No change of defaults or results: every golden fingerprint is unchanged.
13
+
14
+ ### Added
15
+
16
+ - The ns-3 comparison setting as code: `handoverlab.verify.ns3.COMPARISON_SETTING`
17
+ (`{"protocol.lm_load": 0.15, "protocol.exec_can_fail": False}`: interferer load 0.15 at link monitoring,
18
+ ideal execution), applied by `scenario_for(..., comparison=True)`, `compare(..., comparison=True)` and
19
+ `holdout(..., comparison=True)` (after the in-car loss, before `overrides`), by
20
+ `ns3_checks(..., comparison=True)` and `run_verify(..., ns3_comparison=True)` together with the in-car loss
21
+ of ns-3, and on the command line by `handoverlab verify --ns3 --ns3-comparison`. The summary of `compare`
22
+ records `car` and `comparison`. It is not a default.
23
+
24
+ ### Validation
25
+
26
+ - An independent validation study, registered before any of its runs, with one amendment fixed before the runs
27
+ of its second part; nothing tuned on its data (`docs/calibration.md`, "Independent validation study (2.5.0)").
28
+ - **V1, failure rate against 40 fresh ns-3 / 5G-LENA runs** (site distance 200 / 300 m, 10 / 28 m/s,
29
+ time-to-trigger 80 / 320 ms, seeds 401-405). The N3 candidate, not re-tuned (`lm_load` 0.15, ideal
30
+ execution, in-car loss): failure level 0.97 of ns-3 (limit 0.67-1.5), rank correlation 0.88 over the 8 cells,
31
+ handover-rate ratio 0.965, TTT direction in 4 of 4: all pass, so it becomes the documented ns-3 comparison
32
+ setting. The 2.4.2 link monitoring with ideal execution: level 0.33 (fails), rank 0.84, ratio 0.973. V1 has
33
+ time-to-trigger 80 and 320 ms only; on the earlier fresh set N3 the same setting failed the level (0.47): its
34
+ TTT 100 ms cells matched, its TTT 640 ms cells had about half of ns-3's failures (matching at 100 ms was not a
35
+ registered criterion).
36
+ - **V2, generalisation of the adaptive and learning rules** (tuned and trained on `urban-grid-10ghz`, seeds
37
+ 101-104; tested on `ns3-urban-3.5ghz`, `urban-macro-tr37885-4ghz`, `highway-tr37885-4ghz` and
38
+ `fr3-urban-15ghz`, seeds 501-505, against tuned A3). `a3_mro` and `dqn` generalise (fewer unnecessary
39
+ handovers on all four test cards, no cost flag). `a3_bandit` meets the registered criteria only formally: it
40
+ has more unnecessary handovers than tuned A3 on every card (+1.644 per UE-minute on the training card) and
41
+ 1.65 to 4.15 times its ping-pong on three test cards. `a3_pred` does not generalise (the gain is lost on
42
+ `urban-macro-tr37885-4ghz`, and it cannot run at the 0.2 s measurement period of `ns3-urban-3.5ghz`). Limits
43
+ of the design: with 5 seeds the no-harm criterion cannot fail (smallest Wilcoxon p 0.0625), and
44
+ `fr3-urban-15ghz` is the least independent test card.
45
+
46
+ ### Documentation
47
+
48
+ - The failure level is now stated precisely everywhere it was called "not calibrated" (README, scenario cards,
49
+ `docs/comparison.md`, `docs/verification.md`, the user guide and the guides): the ns-3
50
+ comparison setting passed the pre-registered checks on one fresh set of ns-3 runs (V1, TTT 80 and 320 ms:
51
+ level 0.97, rank 0.88) and failed the level on another (N3: 0.47, because of its TTT 640 ms cells); the
52
+ default link-monitoring model gives a third to a sixth of ns-3's level (0.33 on V1, 0.16 on N3). Scenario
53
+ pages regenerated.
54
+ - The learning-rule and AI/ML-mobility guides: the generalisation results of V2.
55
+ - README, `CITATION.cff` and `.zenodo.json` describe HandoverLab as a reproducible system-level simulator for
56
+ studying and comparing handover algorithms and procedures in 5G-Advanced, beyond-5G and some NTN scenarios,
57
+ with explicit models, optional features and a documented validation scope.
58
+ - Citation with the DOI of 2.4.2 (10.5281/zenodo.23589215).
59
+ - Command-line reference: `verify --ns3-comparison`. Dashboard screenshots retaken with this version.
60
+
10
61
  ## [2.4.2] - 2026-10-23
11
62
 
12
63
  No change of results.
@@ -1120,7 +1171,8 @@ First alpha release (stage 1: the core simulator).
1120
1171
  - Documentation: README, user guide, contribution guide, citation file; CI on Linux, Windows and
1121
1172
  macOS with Python 3.9-3.12.
1122
1173
 
1123
- [Unreleased]: https://github.com/mjs19971997/handoverlab/compare/v2.4.2...HEAD
1174
+ [Unreleased]: https://github.com/mjs19971997/handoverlab/compare/v2.5.0...HEAD
1175
+ [2.5.0]: https://github.com/mjs19971997/handoverlab/compare/v2.4.2...v2.5.0
1124
1176
  [2.4.2]: https://github.com/mjs19971997/handoverlab/compare/v2.4.1...v2.4.2
1125
1177
  [2.4.1]: https://github.com/mjs19971997/handoverlab/compare/v2.4.0...v2.4.1
1126
1178
  [2.4.0]: https://github.com/mjs19971997/handoverlab/compare/v2.3.0...v2.4.0
@@ -2,8 +2,10 @@ cff-version: 1.2.0
2
2
  message: "If you use this software, please cite it as below."
3
3
  title: "HandoverLab: system-level handover simulator for 5G-Advanced and beyond-5G research"
4
4
  abstract: >-
5
- HandoverLab is an open-source, system-level simulator of 3GPP mobility procedures (baseline
6
- handover with Event A3, Release-16 conditional and DAPS handover, Release-18 L1/L2-triggered
5
+ HandoverLab is a reproducible system-level simulator for studying and comparing handover
6
+ algorithms and procedures in 5G-Advanced, beyond-5G and some NTN scenarios, with explicit models,
7
+ optional features and a documented validation scope. It is open source and simulates 3GPP
8
+ mobility procedures (baseline handover with Event A3, Release-16 conditional and DAPS handover, Release-18 L1/L2-triggered
7
9
  mobility, conditional handover with the location events of Release 18) for many users in dense
8
10
  5G and beyond-5G networks, on a street grid, in an open area, along a highway, indoors, in rural
9
11
  cells, along a high-speed railway or under the earth-moving beams of a LEO satellite. It provides a
@@ -14,7 +16,7 @@ abstract: >-
14
16
  handover rules (including a measurement-prediction rule after TR 38.744), equal-budget tuning
15
17
  studies with paired statistics, and verification against hand-computed values, golden event-log
16
18
  fingerprints, the 3GPP large-scale calibration and ns-3 / 5G-LENA.
17
- version: "2.4.2"
19
+ version: "2.5.0"
18
20
  doi: 10.5281/zenodo.23409131
19
21
  identifiers:
20
22
  - type: doi
@@ -32,7 +34,10 @@ identifiers:
32
34
  - type: doi
33
35
  value: 10.5281/zenodo.23585308
34
36
  description: "Version 2.4.1 on Zenodo"
35
- date-released: 2026-10-23
37
+ - type: doi
38
+ value: 10.5281/zenodo.23589215
39
+ description: "Version 2.4.2 on Zenodo"
40
+ date-released: 2026-10-25
36
41
  license: MIT
37
42
  repository-code: "https://github.com/mjs19971997/handoverlab"
38
43
  url: "https://mjs19971997.github.io/handoverlab/"
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: handoverlab
3
- Version: 2.4.2
3
+ Version: 2.5.0
4
4
  Summary: System-level handover simulator for 5G-Advanced and beyond-5G research
5
5
  Author: Mostafa Jalili Sanjarani, Yasser Elmi Sola
6
6
  License-Expression: MIT
@@ -60,7 +60,7 @@ Dynamic: license-file
60
60
 
61
61
  [![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/)
62
62
 
63
- A system-level handover simulator for 5G-Advanced and beyond-5G research, written in Python.
63
+ HandoverLab is a reproducible system-level simulator for studying and comparing handover algorithms and procedures in 5G-Advanced, beyond-5G and some NTN scenarios, with explicit models, optional features and a documented validation scope. It is written in Python.
64
64
 
65
65
  HandoverLab simulates 3GPP mobility procedures (baseline handover with Event A3, Release-16
66
66
  conditional handover and DAPS handover, Release-18 L1/L2-triggered mobility, conditional handover with
@@ -70,7 +70,7 @@ received-power-level radio model from TR 38.901 (TR 38.811 for the satellite) (o
70
70
  and several carriers), an L3-filtered measurement chain, a radio-link failure model, and an
71
71
  outcome label for every handover attempt.
72
72
 
73
- > **Status: 2.4.2** (`python -m pip install handoverlab`). The public surface (configuration and
73
+ > **Status: 2.5.0** (`python -m pip install handoverlab`). The public surface (configuration and
74
74
  > result formats with a schema version, the rule interface, metric names, commands and the
75
75
  > documented Python names) follows the [stability policy](https://github.com/mjs19971997/handoverlab/blob/main/docs/stability.md): within 2.x it only grows,
76
76
  > and anything removed is deprecated first. 2.0.0 makes the 3GPP values the defaults (the protocol profile,
@@ -257,7 +257,7 @@ the clause-by-clause detail is in [standard compliance](https://github.com/mjs19
257
257
  | Traffic and load | constant rates per 5QI, full buffer, periodic, FTP model 3; cell load drives interference; options: a slot scheduler (`protocol.scheduler`: round robin or proportional fair) with the MCS, TBS and HARQ of TS 38.214 (since 2.2.0), and user-plane buffers at handover (`protocol.buffers`: simplified PDCP / RLC in AM or UM with Xn data forwarding, since 2.3.0) | by default the rate from the SINR (Shannon-type mapping) without a scheduler; with the scheduler the channel is held for the 10 ms step and the MCS is chosen from the SINR of one measurement interval earlier (no CSI reports); buffers: downlink only, one bearer per user, the interruption quantised to the measurement interval; both options need the base engine (no beams, layers, NTN or TS 38.133 periods) | uplink data, CSI-based link adaptation, PDCP reordering and status reports at the UE, TCP and applications |
258
258
  | Measurements | L1 samples with error, TS 38.331 L3 filter, TS 38.133 measurement periods and gaps (option) | wideband RSRP; SS-RSRP per resource element with beams/carriers | RSRQ, CSI reporting |
259
259
  | Procedures | baseline (A3), CHO, DAPS, LTM, conditional LTM, CHO with D1/D2 for satellites, A3/A5 between carriers; rules `a3`, `a3a5`, `a3_mro`, `a3_bandit`, `dqn`, `a3_pred`, `cho`, `cho_d2`, `daps`, `ltm`, `cltm` | fixed preparation, command and execution delays; re-establishment always succeeds after a fixed outage | condEventT1, RACH-less NTN handover, Xn signalling in detail |
260
- | Failure model | Qout/Qin with windows, N310/N311/T310, T304, command received during T310, re-establishment, link-monitoring restart | Qout/Qin as SINR thresholds (TR 36.839 values) instead of the BLER of a hypothetical PDCCH | BLER-based link monitoring; absolute failure rates are not calibrated: compare rules with each other |
260
+ | Failure model | Qout/Qin with windows, N310/N311/T310, T304, command received during T310, re-establishment, link-monitoring restart | Qout/Qin as SINR thresholds (TR 36.839 values) instead of the BLER of a hypothetical PDCCH | BLER-based link monitoring; the absolute failure level: the ns-3 comparison setting passed the pre-registered checks on one fresh ns-3 set (TTT 80 and 320 ms) and failed the level on another (TTT 640 ms cells); the default gives a third to a sixth of ns-3's: compare rules with each other |
261
261
  | Time | 10 ms step by default (`protocol.step_s`); the effect of the step is measured in [time step](https://github.com/mjs19971997/handoverlab/blob/main/docs/time-step.md) | events rounded to the step | sub-slot timing |
262
262
 
263
263
  Built-in cards: `smoke`, `urban-grid-10ghz`, `fr3-urban-15ghz`, `urban-grid-28ghz-beams`, `dense-urban-2layer`,
@@ -326,9 +326,15 @@ The same 24 Event-A3 settings were simulated in HandoverLab and in the packet-le
326
326
  ns-3 with 5G-LENA. Handover and unnecessary-handover rates agree (median ratios 0.95 and 1.06,
327
327
  rank correlation 0.99; `handoverlab verify --ns3`, version 2.1.0). For failure rates, with the in-car loss of ns-3 and the restart of the link
328
328
  monitoring switched on, the order of settings and conditions agrees too, also on ns-3 results
329
- that were held back from any tuning (rank correlation 0.82-0.91). The absolute values
330
- differ, so compare failure rates between rules within HandoverLab. The causes are traced in
331
- [docs/calibration.md](https://github.com/mjs19971997/handoverlab/blob/main/docs/calibration.md). On two of them HandoverLab follows the 3GPP texts more closely
329
+ that were held back from any tuning (rank correlation 0.82-0.91). For the failure level,
330
+ the ns-3 comparison setting of 2.5.0 (`handoverlab.verify.ns3.COMPARISON_SETTING`: interferer load
331
+ 0.15 at link monitoring and ideal execution, with the in-car loss of ns-3;
332
+ `handoverlab verify --ns3 --ns3-comparison`) passed the pre-registered checks on one fresh set of ns-3
333
+ runs (V1, TTT 80 and 320 ms: level 0.97 of ns-3, rank 0.88) and failed the level on another (N3: 0.47,
334
+ because of its TTT 640 ms cells). The default link-monitoring model gives a third to a sixth of ns-3's
335
+ level (0.33 on V1, 0.16 on N3). The setting is not a default, so
336
+ compare failure rates between rules within HandoverLab. The study and the causes of the differences are
337
+ in [docs/calibration.md](https://github.com/mjs19971997/handoverlab/blob/main/docs/calibration.md). On two of the causes HandoverLab follows the 3GPP texts more closely
332
338
  than ns-3:
333
339
 
334
340
  - **In-car penetration loss.** TR 38.901 clause 7.4.3.2 gives it to a user inside a car. ns-3 adds
@@ -345,6 +351,16 @@ L1/L2-triggered mobility.
345
351
 
346
352
  ## What's new
347
353
 
354
+ - **2.5.0**: an independent, pre-registered [validation study](https://github.com/mjs19971997/handoverlab/blob/main/docs/calibration.md#independent-validation-study-250). On 40
355
+ fresh ns-3 runs (V1, TTT 80 and 320 ms) the ns-3 comparison setting passed the pre-registered checks
356
+ (level 0.97, order 0.88); on the earlier fresh set N3 it failed the level (0.47, because of its TTT 640 ms
357
+ cells). It is now documented as code (`handoverlab.verify.ns3.COMPARISON_SETTING`,
358
+ `verify --ns3 --ns3-comparison`; not a default). Tuned on one card and tested on four unseen cards and
359
+ seeds, `a3_mro` and `dqn` kept fewer unnecessary handovers than tuned A3; `a3_bandit` met the criteria
360
+ only formally (more unnecessary handovers and ping-pong); `a3_pred` did not generalise. Limits of this
361
+ design: with 5 seeds the smallest two-sided Wilcoxon p is 0.0625, so the no-harm check could not fail, and
362
+ `fr3-urban-15ghz` is the training grid at another frequency, so it is the least independent test card
363
+ ([V2](https://github.com/mjs19971997/handoverlab/blob/main/docs/calibration.md#v2-do-the-adaptive-and-learning-rules-generalise)). No change of results.
348
364
  - **2.4.0**: UE receive beams (panels of TR 38.802, best beam or a sweep); a measurement-prediction rule after
349
365
  TR 38.744 (`a3_pred`: predicted Event A3 or measurement skipping, sample and hold or a trained ridge
350
366
  predictor); the slot scheduler as a compiled kernel (`run.backend: numba`, bit-identical; on the ns-3 card
@@ -569,9 +585,12 @@ the pages are plain Markdown in [docs/](https://github.com/mjs19971997/handoverl
569
585
  scheduler, the 7 GHz calibration and the comparison with other simulators.
570
586
  27. 2.4.1: pictures, citation and tutorials brought up to date; the example study
571
587
  completes again; no change of results.
572
- 28. **2.4.2 (this release)**: every option has a picture; the dashboard pictures show the current version;
588
+ 28. 2.4.2: every option has a picture; the dashboard pictures show the current version;
573
589
  guide and tutorial examples rerun; facts made consistent; no change of results.
574
- 29. Later (planned, not done), staying a handover simulator: the effect of a handover on what the user
590
+ 29. **2.5.0 (this release)**: an independent, pre-registered validation study: the ns-3 comparison setting
591
+ for the failure level (passed on one fresh set with TTT 80 and 320 ms, failed the level on N3), and the generalisation of the adaptive and learning rules to
592
+ unseen cards and seeds; no change of results.
593
+ 30. Later (planned, not done), staying a handover simulator: the effect of a handover on what the user
575
594
  sees (simple TCP and application models on top of the buffers), MIMO as an SINR gain, hundreds of
576
595
  users per run, and one scenario definition that also runs in ns-3; condEventT1 and earth-fixed beams.
577
596
 
@@ -581,9 +600,10 @@ If you use HandoverLab, please cite it as described in [CITATION.cff](https://gi
581
600
  it under *Cite this repository*). Every release is archived on Zenodo:
582
601
  [doi:10.5281/zenodo.23409131](https://doi.org/10.5281/zenodo.23409131) cites all versions (it resolves to
583
602
  the latest). To cite the exact version you used, take its DOI from the version list on that page, where
584
- the DOI of the latest version is listed; for example, version 2.4.1 is
603
+ the DOI of the latest version is listed; for example, version 2.4.2 is
604
+ [doi:10.5281/zenodo.23589215](https://doi.org/10.5281/zenodo.23589215), version 2.4.1
585
605
  [doi:10.5281/zenodo.23585308](https://doi.org/10.5281/zenodo.23585308) and version 2.4.0
586
- [doi:10.5281/zenodo.23582521](https://doi.org/10.5281/zenodo.23582521). The BibTeX entry below cites 2.4.1;
606
+ [doi:10.5281/zenodo.23582521](https://doi.org/10.5281/zenodo.23582521). The BibTeX entry below cites 2.4.2;
587
607
  replace version and DOI with those of the version you used.
588
608
 
589
609
  ```bibtex
@@ -592,8 +612,8 @@ replace version and DOI with those of the version you used.
592
612
  title = {{HandoverLab}: system-level handover simulator for 5G-Advanced and beyond-5G research},
593
613
  year = {2026},
594
614
  publisher = {Zenodo},
595
- version = {2.4.1},
596
- doi = {10.5281/zenodo.23585308},
615
+ version = {2.4.2},
616
+ doi = {10.5281/zenodo.23589215},
597
617
  url = {https://github.com/mjs19971997/handoverlab}
598
618
  }
599
619
  ```
@@ -2,7 +2,7 @@
2
2
 
3
3
  [![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/)
4
4
 
5
- A system-level handover simulator for 5G-Advanced and beyond-5G research, written in Python.
5
+ HandoverLab is a reproducible system-level simulator for studying and comparing handover algorithms and procedures in 5G-Advanced, beyond-5G and some NTN scenarios, with explicit models, optional features and a documented validation scope. It is written in Python.
6
6
 
7
7
  HandoverLab simulates 3GPP mobility procedures (baseline handover with Event A3, Release-16
8
8
  conditional handover and DAPS handover, Release-18 L1/L2-triggered mobility, conditional handover with
@@ -12,7 +12,7 @@ received-power-level radio model from TR 38.901 (TR 38.811 for the satellite) (o
12
12
  and several carriers), an L3-filtered measurement chain, a radio-link failure model, and an
13
13
  outcome label for every handover attempt.
14
14
 
15
- > **Status: 2.4.2** (`python -m pip install handoverlab`). The public surface (configuration and
15
+ > **Status: 2.5.0** (`python -m pip install handoverlab`). The public surface (configuration and
16
16
  > result formats with a schema version, the rule interface, metric names, commands and the
17
17
  > documented Python names) follows the [stability policy](https://github.com/mjs19971997/handoverlab/blob/main/docs/stability.md): within 2.x it only grows,
18
18
  > and anything removed is deprecated first. 2.0.0 makes the 3GPP values the defaults (the protocol profile,
@@ -199,7 +199,7 @@ the clause-by-clause detail is in [standard compliance](https://github.com/mjs19
199
199
  | Traffic and load | constant rates per 5QI, full buffer, periodic, FTP model 3; cell load drives interference; options: a slot scheduler (`protocol.scheduler`: round robin or proportional fair) with the MCS, TBS and HARQ of TS 38.214 (since 2.2.0), and user-plane buffers at handover (`protocol.buffers`: simplified PDCP / RLC in AM or UM with Xn data forwarding, since 2.3.0) | by default the rate from the SINR (Shannon-type mapping) without a scheduler; with the scheduler the channel is held for the 10 ms step and the MCS is chosen from the SINR of one measurement interval earlier (no CSI reports); buffers: downlink only, one bearer per user, the interruption quantised to the measurement interval; both options need the base engine (no beams, layers, NTN or TS 38.133 periods) | uplink data, CSI-based link adaptation, PDCP reordering and status reports at the UE, TCP and applications |
200
200
  | Measurements | L1 samples with error, TS 38.331 L3 filter, TS 38.133 measurement periods and gaps (option) | wideband RSRP; SS-RSRP per resource element with beams/carriers | RSRQ, CSI reporting |
201
201
  | Procedures | baseline (A3), CHO, DAPS, LTM, conditional LTM, CHO with D1/D2 for satellites, A3/A5 between carriers; rules `a3`, `a3a5`, `a3_mro`, `a3_bandit`, `dqn`, `a3_pred`, `cho`, `cho_d2`, `daps`, `ltm`, `cltm` | fixed preparation, command and execution delays; re-establishment always succeeds after a fixed outage | condEventT1, RACH-less NTN handover, Xn signalling in detail |
202
- | Failure model | Qout/Qin with windows, N310/N311/T310, T304, command received during T310, re-establishment, link-monitoring restart | Qout/Qin as SINR thresholds (TR 36.839 values) instead of the BLER of a hypothetical PDCCH | BLER-based link monitoring; absolute failure rates are not calibrated: compare rules with each other |
202
+ | Failure model | Qout/Qin with windows, N310/N311/T310, T304, command received during T310, re-establishment, link-monitoring restart | Qout/Qin as SINR thresholds (TR 36.839 values) instead of the BLER of a hypothetical PDCCH | BLER-based link monitoring; the absolute failure level: the ns-3 comparison setting passed the pre-registered checks on one fresh ns-3 set (TTT 80 and 320 ms) and failed the level on another (TTT 640 ms cells); the default gives a third to a sixth of ns-3's: compare rules with each other |
203
203
  | Time | 10 ms step by default (`protocol.step_s`); the effect of the step is measured in [time step](https://github.com/mjs19971997/handoverlab/blob/main/docs/time-step.md) | events rounded to the step | sub-slot timing |
204
204
 
205
205
  Built-in cards: `smoke`, `urban-grid-10ghz`, `fr3-urban-15ghz`, `urban-grid-28ghz-beams`, `dense-urban-2layer`,
@@ -268,9 +268,15 @@ The same 24 Event-A3 settings were simulated in HandoverLab and in the packet-le
268
268
  ns-3 with 5G-LENA. Handover and unnecessary-handover rates agree (median ratios 0.95 and 1.06,
269
269
  rank correlation 0.99; `handoverlab verify --ns3`, version 2.1.0). For failure rates, with the in-car loss of ns-3 and the restart of the link
270
270
  monitoring switched on, the order of settings and conditions agrees too, also on ns-3 results
271
- that were held back from any tuning (rank correlation 0.82-0.91). The absolute values
272
- differ, so compare failure rates between rules within HandoverLab. The causes are traced in
273
- [docs/calibration.md](https://github.com/mjs19971997/handoverlab/blob/main/docs/calibration.md). On two of them HandoverLab follows the 3GPP texts more closely
271
+ that were held back from any tuning (rank correlation 0.82-0.91). For the failure level,
272
+ the ns-3 comparison setting of 2.5.0 (`handoverlab.verify.ns3.COMPARISON_SETTING`: interferer load
273
+ 0.15 at link monitoring and ideal execution, with the in-car loss of ns-3;
274
+ `handoverlab verify --ns3 --ns3-comparison`) passed the pre-registered checks on one fresh set of ns-3
275
+ runs (V1, TTT 80 and 320 ms: level 0.97 of ns-3, rank 0.88) and failed the level on another (N3: 0.47,
276
+ because of its TTT 640 ms cells). The default link-monitoring model gives a third to a sixth of ns-3's
277
+ level (0.33 on V1, 0.16 on N3). The setting is not a default, so
278
+ compare failure rates between rules within HandoverLab. The study and the causes of the differences are
279
+ in [docs/calibration.md](https://github.com/mjs19971997/handoverlab/blob/main/docs/calibration.md). On two of the causes HandoverLab follows the 3GPP texts more closely
274
280
  than ns-3:
275
281
 
276
282
  - **In-car penetration loss.** TR 38.901 clause 7.4.3.2 gives it to a user inside a car. ns-3 adds
@@ -287,6 +293,16 @@ L1/L2-triggered mobility.
287
293
 
288
294
  ## What's new
289
295
 
296
+ - **2.5.0**: an independent, pre-registered [validation study](https://github.com/mjs19971997/handoverlab/blob/main/docs/calibration.md#independent-validation-study-250). On 40
297
+ fresh ns-3 runs (V1, TTT 80 and 320 ms) the ns-3 comparison setting passed the pre-registered checks
298
+ (level 0.97, order 0.88); on the earlier fresh set N3 it failed the level (0.47, because of its TTT 640 ms
299
+ cells). It is now documented as code (`handoverlab.verify.ns3.COMPARISON_SETTING`,
300
+ `verify --ns3 --ns3-comparison`; not a default). Tuned on one card and tested on four unseen cards and
301
+ seeds, `a3_mro` and `dqn` kept fewer unnecessary handovers than tuned A3; `a3_bandit` met the criteria
302
+ only formally (more unnecessary handovers and ping-pong); `a3_pred` did not generalise. Limits of this
303
+ design: with 5 seeds the smallest two-sided Wilcoxon p is 0.0625, so the no-harm check could not fail, and
304
+ `fr3-urban-15ghz` is the training grid at another frequency, so it is the least independent test card
305
+ ([V2](https://github.com/mjs19971997/handoverlab/blob/main/docs/calibration.md#v2-do-the-adaptive-and-learning-rules-generalise)). No change of results.
290
306
  - **2.4.0**: UE receive beams (panels of TR 38.802, best beam or a sweep); a measurement-prediction rule after
291
307
  TR 38.744 (`a3_pred`: predicted Event A3 or measurement skipping, sample and hold or a trained ridge
292
308
  predictor); the slot scheduler as a compiled kernel (`run.backend: numba`, bit-identical; on the ns-3 card
@@ -511,9 +527,12 @@ the pages are plain Markdown in [docs/](https://github.com/mjs19971997/handoverl
511
527
  scheduler, the 7 GHz calibration and the comparison with other simulators.
512
528
  27. 2.4.1: pictures, citation and tutorials brought up to date; the example study
513
529
  completes again; no change of results.
514
- 28. **2.4.2 (this release)**: every option has a picture; the dashboard pictures show the current version;
530
+ 28. 2.4.2: every option has a picture; the dashboard pictures show the current version;
515
531
  guide and tutorial examples rerun; facts made consistent; no change of results.
516
- 29. Later (planned, not done), staying a handover simulator: the effect of a handover on what the user
532
+ 29. **2.5.0 (this release)**: an independent, pre-registered validation study: the ns-3 comparison setting
533
+ for the failure level (passed on one fresh set with TTT 80 and 320 ms, failed the level on N3), and the generalisation of the adaptive and learning rules to
534
+ unseen cards and seeds; no change of results.
535
+ 30. Later (planned, not done), staying a handover simulator: the effect of a handover on what the user
517
536
  sees (simple TCP and application models on top of the buffers), MIMO as an SINR gain, hundreds of
518
537
  users per run, and one scenario definition that also runs in ns-3; condEventT1 and earth-fixed beams.
519
538
 
@@ -523,9 +542,10 @@ If you use HandoverLab, please cite it as described in [CITATION.cff](https://gi
523
542
  it under *Cite this repository*). Every release is archived on Zenodo:
524
543
  [doi:10.5281/zenodo.23409131](https://doi.org/10.5281/zenodo.23409131) cites all versions (it resolves to
525
544
  the latest). To cite the exact version you used, take its DOI from the version list on that page, where
526
- the DOI of the latest version is listed; for example, version 2.4.1 is
545
+ the DOI of the latest version is listed; for example, version 2.4.2 is
546
+ [doi:10.5281/zenodo.23589215](https://doi.org/10.5281/zenodo.23589215), version 2.4.1
527
547
  [doi:10.5281/zenodo.23585308](https://doi.org/10.5281/zenodo.23585308) and version 2.4.0
528
- [doi:10.5281/zenodo.23582521](https://doi.org/10.5281/zenodo.23582521). The BibTeX entry below cites 2.4.1;
548
+ [doi:10.5281/zenodo.23582521](https://doi.org/10.5281/zenodo.23582521). The BibTeX entry below cites 2.4.2;
529
549
  replace version and DOI with those of the version you used.
530
550
 
531
551
  ```bibtex
@@ -534,8 +554,8 @@ replace version and DOI with those of the version you used.
534
554
  title = {{HandoverLab}: system-level handover simulator for 5G-Advanced and beyond-5G research},
535
555
  year = {2026},
536
556
  publisher = {Zenodo},
537
- version = {2.4.1},
538
- doi = {10.5281/zenodo.23585308},
557
+ version = {2.4.2},
558
+ doi = {10.5281/zenodo.23589215},
539
559
  url = {https://github.com/mjs19971997/handoverlab}
540
560
  }
541
561
  ```
@@ -4,6 +4,14 @@ The radio model itself is calibrated against 3GPP (TR 38.901 clause 7.8.1, the 2
4
4
  R1-165974); see [radio calibration against 3GPP](calibration-3gpp.md). This page is about the
5
5
  failure model, which no 3GPP calibration covers.
6
6
 
7
+ **Where the failure level stands (2.5.0).** The *ns-3 comparison setting*
8
+ (`handoverlab.verify.ns3.COMPARISON_SETTING`: interferer load 0.15 at link monitoring, ideal execution,
9
+ with the in-car loss of ns-3) passed the pre-registered checks against ns-3 / 5G-LENA on one fresh set
10
+ of ns-3 runs (V1, TTT 80 and 320 ms: level 0.97, rank 0.88) and failed the level on another (N3: 0.47,
11
+ because of its TTT 640 ms cells). The default link-monitoring model gives a third to a sixth of ns-3's
12
+ level (0.33 on V1, 0.16 on N3). The setting is not a default. See the [independent validation study (2.5.0)](#independent-validation-study-250) at the
13
+ end of this page, which also tests whether the adaptive and learning rules generalise.
14
+
7
15
  **Fresh hold-out D (1.9.0).** 48 new ns-3 runs on conditions never used before (12 and 20 m/s)
8
16
  were compared with the 1.3.0 configuration fixed in advance: handover and unnecessary-handover
9
17
  rates agree (median ratios 0.93 and 0.95, ranks 0.97), and the failure level agrees too (ratio
@@ -53,7 +61,9 @@ traffic), with limits fixed before the runs:
53
61
  At TTT 100 ms the fixed load gives ns-3's failure counts (17 against 14, 11 against 10); at
54
62
  TTT 640 ms it gives about half of them (84 against 180, 68 against 193), the known effect of ns-3's
55
63
  slow out-of-sync detection (below). As fixed in advance, the setting is not adopted because the
56
- level check fails; it is reported here, and no further tuning was done on these runs.
64
+ level check fails; it is reported here, and no further tuning was done on these runs. It was
65
+ adopted as the comparison setting in 2.5.0 after V1, see
66
+ [below](#independent-validation-study-250).
57
67
 
58
68
  **Summary (1.5.0).** Handover and unnecessary-handover rates of HandoverLab agree with ns-3 /
59
69
  5G-LENA. For failure rates, the order of settings and conditions agrees on part A and on both
@@ -381,3 +391,111 @@ time should follow the control traffic of each cell.
381
391
 
382
392
  Reproduce with `compare(seeds=[500, 501], overrides=...)` and
383
393
  `holdout(seeds=[500, 501, 502, 503], overrides=...)` from `handoverlab.verify.ns3`.
394
+
395
+ ## Independent validation study (2.5.0)
396
+
397
+ A study in two parts was registered on 2026-10-24, before any of its runs, with its conditions, seeds,
398
+ criteria and decisions fixed; one amendment (2026-10-25, before any run of part V2) fixed the tuning
399
+ grids and the test procedure without changing a criterion, card, seed or selection rule. Nothing was
400
+ tuned on the study's data, and every result is reported as it came. Code: HandoverLab 2.4.2 (results
401
+ identical to 2.4.0).
402
+
403
+ ### V1: failure rate against fresh ns-3 runs
404
+
405
+ **Runs.** 40 new ns-3 / 5G-LENA runs (`benchmarks/ns3/handoverlab-ns3-a3.cc`, ns-3.48 with the
406
+ documented upstream commits, 5G-LENA v5.1 with the one documented local change, see
407
+ `benchmarks/ns3/README.md`) on conditions never used before: urban, 20 users, 180 s (60 s
408
+ discarded), light traffic, A3 3 dB, site distance 200 and 300 m, 10 and 28 m/s (both inside the
409
+ 30-120 km/h range of the ns-3 in-car loss), time-to-trigger 80 and 320 ms, seeds 401-405: 8 cells of
410
+ 5 runs. HandoverLab ran `verify.ns3.scenario_for(isd, v, car=True)` with seeds 401-405 in two rows
411
+ fixed in advance:
412
+
413
+ - **R** (reference): the 2.4.2 defaults with `protocol.exec_can_fail: false` (ns-3 has ideal execution);
414
+ - **C** (the N3 candidate above, not re-tuned): R with `protocol.lm_load: 0.15`.
415
+
416
+ | Row | F1 level, sum HandoverLab / sum ns-3 (0.67-1.5) | F2 order, Spearman over the 8 cells (at least 0.8) | F3 handover rate, median ratio (0.8-1.25) | F4 TTT direction (3 of 4) |
417
+ |---|---|---|---|---|
418
+ | C: `lm_load` 0.15, ideal execution | 0.97, passes | 0.88, passes | 0.965, passes | 4, passes |
419
+ | R: 2.4.2 link monitoring, ideal execution | 0.33, fails | 0.84, passes | 0.973, passes | 4, passes |
420
+
421
+ Failures summed over the 5 seeds per cell (site distance / speed / time-to-trigger):
422
+
423
+ | Cell | 200 m, 10 m/s, 80 ms | 200, 10, 320 | 200, 28, 80 | 200, 28, 320 | 300, 10, 80 | 300, 10, 320 | 300, 28, 80 | 300, 28, 320 | Total |
424
+ |---|---|---|---|---|---|---|---|---|---|
425
+ | ns-3 | 16 | 29 | 7 | 47 | 3 | 15 | 4 | 12 | 133 |
426
+ | HandoverLab, row C | 8 | 46 | 7 | 33 | 5 | 16 | 3 | 11 | 129 |
427
+ | HandoverLab, row R | 6 | 12 | 2 | 13 | 3 | 6 | 0 | 2 | 44 |
428
+
429
+ **Decision, as registered.** Row C passes F1-F3 on V1, the fresh set after N3 (on which it had failed F1), so it is the
430
+ documented **ns-3 comparison setting** of 2.5.0: `handoverlab.verify.ns3.COMPARISON_SETTING`
431
+ (`{"protocol.lm_load": 0.15, "protocol.exec_can_fail": False}`), used with the in-car loss of ns-3
432
+ (`scenario_for(isd, v, car=True, comparison=True)`, `compare(..., car=True, comparison=True)`, or
433
+ `handoverlab verify --ns3 --ns3-comparison`, which adds the in-car loss itself). It is not a default:
434
+ every default, golden fingerprint and stored result is unchanged.
435
+
436
+ **Validity range.** The setting passed the pre-registered checks on one fresh set only (V1,
437
+ time-to-trigger 80 and 320 ms). On N3 the same row failed the level (0.47) because of the TTT 640 ms
438
+ cells, where it gives about half of ns-3's failures (84 against 180, 68 against 193, 30 against 46,
439
+ 36 against 80), the slow out-of-sync detection of ns-3 described in the version 1.4.0 section; its
440
+ TTT 100 ms cells matched (17 against 14, 11 against 10, 1 against 3, 3 against 1), but matching at
441
+ 100 ms was not a registered criterion. Both results stand side by side. The default link-monitoring
442
+ model (row R) gives a third to a sixth of ns-3's failure level (0.33 here, 0.16 on N3), with the order
443
+ of the cells kept here (0.84) but not on N3 (0.78).
444
+
445
+ A run of 5G-LENA without the local change is not part of V1: it aborts the run instead of producing
446
+ data, and how often it would do so cannot be measured in the optimised build (logging compiled out).
447
+
448
+ ### V2: do the adaptive and learning rules generalise?
449
+
450
+ **Design.** Rules `a3_mro`, `a3_bandit`, `a3_pred` (ridge predictor, case A) and `dqn`, against
451
+ A3-ref (3 dB, 160 ms) and A3-tuned. Tuning and training on `urban-grid-10ghz`, seeds 101-104 only:
452
+ 8 evaluated settings per rule from its grid (5 drawn at random, 3 around the best), the lowest
453
+ `unnecessary_pm` among settings with `failures_pm` at most that of A3-ref on the same runs (budget
454
+ 0.0638 per UE-minute), ties within 5 % broken by fewer RRC messages. Settings and weights were frozen
455
+ before any test run. Test cards never used for tuning: `ns3-urban-3.5ghz`, `urban-macro-tr37885-4ghz`,
456
+ `highway-tr37885-4ghz`, `fr3-urban-15ghz`, with test seeds 501-505 and each card's default length and
457
+ users; also the training card with the test seeds. Criteria, paired by card and seed against
458
+ A3-tuned (two-sided Wilcoxon, Holm over the 4 test cards):
459
+
460
+ - **G-A (no harm):** failures not significantly higher than A3-tuned's on at least 3 of the 4 test cards;
461
+ - **G-B (gain kept):** the sign of the mean difference in `unnecessary_pm` on the training card's test
462
+ seeds is the same on at least 3 of the 4 test cards;
463
+ - cost flags, no limit: attempts, ping-pong or RRC messages above 1.5 times A3-tuned's on a test card.
464
+
465
+ A rule generalises if it passes G-A and G-B.
466
+
467
+ **Selected settings.** A3-tuned 4 dB / 160 ms; `a3_mro` offset 6 dB, 160 ms, step 2 dB, window 5 s;
468
+ `a3_bandit` arms 1-5 dB, 40 ms, epoch 5, `w_failure` 2; `a3_pred` offset 6 dB, 80 ms, prediction window
469
+ 0.4 s; `dqn` offsets 0-6 dB, 160 ms, `w_failure` 1.
470
+
471
+ Difference to A3-tuned in unnecessary handovers per UE-minute (rule minus A3-tuned, mean over seeds
472
+ 501-505):
473
+
474
+ | Rule | `urban-grid-10ghz` (training card) | `ns3-urban-3.5ghz` | `urban-macro-tr37885-4ghz` | `highway-tr37885-4ghz` | `fr3-urban-15ghz` | G-A | G-B | Verdict |
475
+ |---|---|---|---|---|---|---|---|---|
476
+ | `a3_mro` | -0.780 | -1.120 | -4.793 | -0.637 | -0.798 | 4 of 4 | 4 of 4 | generalises |
477
+ | `dqn` | -0.357 | -0.520 | -1.408 | -0.382 | -0.363 | 4 of 4 | 4 of 4 | generalises |
478
+ | `a3_bandit` | +1.644 | +0.033 | +4.069 | +0.537 | +1.617 | 4 of 4 | 4 of 4 | meets the criteria, but worse (below) |
479
+ | `a3_pred` | -0.288 | not run | +0.946 | -0.507 | -0.312 | 3 of 4 | 2 of 4 | does not generalise |
480
+
481
+ - **`a3_mro` and `dqn` generalise.** Both keep fewer unnecessary handovers than A3-tuned on all four
482
+ test cards (every seed in the same direction; Wilcoxon p 0.0625, Holm 0.25), with no cost flag. Their
483
+ failure differences are small and of both signs (`a3_mro` -0.307 on `urban-macro-tr37885-4ghz`,
484
+ +0.021 on `fr3-urban-15ghz`; `dqn` +0.020 and +0.015 per UE-minute), none significant.
485
+ - **`a3_bandit` is worse in unnecessary handovers and ping-pong, although it formally meets the
486
+ registered criteria.** G-B checks only that the sign stays the same, and its sign is +: the setting
487
+ chosen within the failure budget already had more unnecessary handovers than A3-tuned on the training
488
+ card (+1.644 per UE-minute), and that loss stays on every test card. It has fewer failures (0.782
489
+ against 1.759 per UE-minute on `urban-macro-tr37885-4ghz`) and pays with 1.65, 4.15 and 2.80 times
490
+ A3-tuned's ping-pong on `urban-macro-tr37885-4ghz`, `highway-tr37885-4ghz` and `fr3-urban-15ghz`.
491
+ Its G-B pass is a consistent loss, not a kept gain.
492
+ - **`a3_pred` does not generalise.** It loses the sign on `urban-macro-tr37885-4ghz` (+0.946
493
+ unnecessary handovers per UE-minute, while failures fall from 1.759 to 0.384), and it cannot run on
494
+ `ns3-urban-3.5ghz`: that card measures every 0.2 s, the predictor was trained at the 0.04 s of the
495
+ training card, and the rule refuses a predictor trained with another measurement period (counted as
496
+ not passed, as registered).
497
+
498
+ **Limits of the design.** With 5 paired seeds the smallest two-sided exact Wilcoxon p is 0.0625, so no
499
+ card can show significant harm and G-A cannot fail; read the failure differences themselves.
500
+ `fr3-urban-15ghz` has the same street grid and users as the training card on another carrier and gives
501
+ nearly the same numbers, so it is the least independent of the four test cards.
@@ -1,6 +1,6 @@
1
1
  # Comparison with other simulators
2
2
 
3
- This page sets HandoverLab 2.4.0 next to other open or widely used 5G simulators and emulators, on the points
3
+ This page sets HandoverLab 2.5.0 next to other open or widely used 5G simulators and emulators, on the points
4
4
  that matter for handover and mobility studies. It is meant to help you pick the right tool for a question, not
5
5
  to rank tools: most of them were built for other questions, and several are far more detailed than HandoverLab
6
6
  in layers that HandoverLab abstracts.
@@ -9,13 +9,13 @@ in layers that HandoverLab abstracts.
9
9
  repository (README, docs, source tree, release notes) or its peer-reviewed paper; the source labels in brackets
10
10
  point to the [list of sources](#sources), all accessed on 2026-10-22. **"not stated"** means the fact was not found
11
11
  in those sources; it does not prove that the feature is absent, and a later release may add it. Corrections are
12
- welcome as issues. The HandoverLab column follows the code and the pages of this site for version 2.4.0.
12
+ welcome as issues. The HandoverLab column follows the code and the pages of this site for version 2.5.0.
13
13
 
14
14
  Tools compared (latest release found on 2026-10-22):
15
15
 
16
16
  | Tool | Kind | Latest release | Licence | Language |
17
17
  |---|---|---|---|---|
18
- | HandoverLab | system-level, procedure-centred, time-stepped | 2.4.0 (2026-10-22) | MIT | Python (optional Numba kernel) |
18
+ | HandoverLab | system-level, procedure-centred, time-stepped | 2.5.0 (2026-10-25) | MIT | Python (optional Numba kernel) |
19
19
  | ns-3 with 5G-LENA (`nr` module) | packet-level, end-to-end, discrete-event | v5.1 (2026-08-06, ns-3.48) [L5] | GPLv2 [L6] | C++ [L6] |
20
20
  | ns-3 mmWave module (NYU / UniPD) | packet-level, end-to-end, discrete-event | v8.0 (2024-06-26, ns-3.42) [M2] | GPL-2.0 [M1] | C++ [M1] |
21
21
  | Simu5G (OMNeT++ / INET) | packet-level, end-to-end, discrete-event; real-time emulation | 1.8.0 (2026-10-07) [S2] | LGPL-3.0 [S7] | C++ and NED [S1] |
@@ -28,7 +28,7 @@ Tools compared (latest release found on 2026-10-22):
28
28
 
29
29
  | Tool | Event A3 / RRC handover | CHO | DAPS | LTM | Radio-link failure | Dual connectivity |
30
30
  |---|---|---|---|---|---|---|
31
- | HandoverLab 2.4.0 | yes: report, preparation, command, execution; A5 between carriers; a rule on predicted measurements after TR 38.744 ([AI/ML mobility](guides/ai-mobility.md)) | yes (Rel-16, up to 8 candidates; D1/D2 for NTN) | yes | yes, and conditional LTM | Qout/Qin as SINR thresholds with windows (or the MIESM effective SINR of the hypothetical PDCCH with fading), N310/N311/T310, T304, command during T310, re-establishment after a fixed outage; absolute failure rates are not calibrated | NR-DC as an option: SN addition, PSCell change, SN release, SCG failure; no EN-DC |
31
+ | HandoverLab 2.5.0 | yes: report, preparation, command, execution; A5 between carriers; a rule on predicted measurements after TR 38.744 ([AI/ML mobility](guides/ai-mobility.md)) | yes (Rel-16, up to 8 candidates; D1/D2 for NTN) | yes | yes, and conditional LTM | Qout/Qin as SINR thresholds with windows (or the MIESM effective SINR of the hypothetical PDCCH with fading), N310/N311/T310, T304, command during T310, re-establishment after a fixed outage; absolute failure rates are not calibrated | NR-DC as an option: SN addition, PSCell change, SN release, SCG failure; no EN-DC |
32
32
  | ns-3 5G-LENA v5.1 | yes: X2-based, intra- and inter-frequency, A3 and A2-A4 algorithms, CIO; TTT scaling and ping-pong guards in v5.1 [L2, L3, L5] | not stated | not stated | not stated | Qout/Qin from the hypothetical-PDCCH BLER, N310/N311/T310, re-establishment; optional TR 36.839 handover-failure model (v5.1) [L2, L4, L5] | no: "out of scope and is future work" [L3] |
33
33
  | ns-3 mmWave v8.0 | mmWave secondary-cell switch coordinated by the LTE eNB (SINR difference with fixed or dynamic TTT), not an A3 configuration as such [M1, M4, M5] | not stated | not stated | not stated | mmWave outage by an SINR threshold; T310/N310: not stated [M4] | LTE-mmWave DC (LTE master, mmWave secondary) [M1, M5]; NR-DC: not stated |
34
34
  | Simu5G 1.8.0 | yes: Xn-prepared handover on a measurement report with the best cell, hysteresis on RSSI; A3 with TTT: not stated [S3, S4] | not stated | not stated | not stated | RLF when an RLC AM PDU exceeds its retransmission limit, RRC re-establishment (T311 = 0 disables it by default); T310/N310: not stated [S2] | EN-DC and NE-DC with split bearers [S1, S6]; NR-DC: not stated |
@@ -41,7 +41,7 @@ Tools compared (latest release found on 2026-10-22):
41
41
 
42
42
  | Tool | NTN | Beams | Fast fading and link-to-system mapping | Scheduler |
43
43
  |---|---|---|---|---|
44
- | HandoverLab 2.4.0 | one LEO satellite with earth-moving beams (TR 38.811, TR 38.821), CHO with D2; no several satellites, Doppler or earth-fixed beams ([NTN guide](guides/ntn.md)) | SSB beams per sector from the TR 38.901 element and DFT beams, cell quality from beams, beam failure detection and recovery; UE receive beams optional (1, 2 or 4 panels, [UE beams](guides/ue-beams.md)), isotropic UE by default; no MIMO ([beams](guides/beams-layers.md)) | off by default (large-scale channel with a slow time-varying part); option: TDL-A to TDL-E with MIESM for link monitoring and a capacity-equivalent SINR for the rate; no CDL ([fading](guides/fading.md)) | by default a rate model without a scheduler; option: slot scheduler RR or PF with MCS, TBS and HARQ (base engine only; with `run.backend: numba` bit-identical and, on the ns-3 card, 42 (`rr`) and 93 (`pf`) times faster than its NumPy loop) ([scheduler](guides/scheduler.md)) |
44
+ | HandoverLab 2.5.0 | one LEO satellite with earth-moving beams (TR 38.811, TR 38.821), CHO with D2; no several satellites, Doppler or earth-fixed beams ([NTN guide](guides/ntn.md)) | SSB beams per sector from the TR 38.901 element and DFT beams, cell quality from beams, beam failure detection and recovery; UE receive beams optional (1, 2 or 4 panels, [UE beams](guides/ue-beams.md)), isotropic UE by default; no MIMO ([beams](guides/beams-layers.md)) | off by default (large-scale channel with a slow time-varying part); option: TDL-A to TDL-E with MIESM for link monitoring and a capacity-equivalent SINR for the rate; no CDL ([fading](guides/fading.md)) | by default a rate model without a scheduler; option: slot scheduler RR or PF with MCS, TBS and HARQ (base engine only; with `run.backend: numba` bit-identical and, on the ns-3 card, 42 (`rr`) and 93 (`pf`) times faster than its NumPy loop) ([scheduler](guides/scheduler.md)) |
45
45
  | ns-3 5G-LENA v5.1 | NTN channel (TR 38.811, from ns-3 mainline [L9]) and a LEO mobility example [L1, L2] | ideal and SRS-based realistic beamforming; beam management "Simplified model; no SSB blocks" [L1]; quasi-omni beams in the RRC / handover context [L3] | TR 38.901 with fast fading; EESM-based PHY abstraction with SINR-BLER tables [L1, L8] | PF, RR, MR, QoS-aware, RL-based; OFDMA and TDMA [L1] |
46
46
  | ns-3 mmWave v8.0 | TR 38.811 channel inherited from ns-3.42; no NTN protocol features stated [M2] | SVD- and DFT-based beamforming; NR beam tracking listed as future work [M1, M3] | TR 38.901 channel, ray-tracing and measured traces; EESM error model from 5G-LENA [M1, M3] | flexible-TTI RR, PF, max-rate, max-weight [M1] |
47
47
  | Simu5G 1.8.0 | not in the official release; separate third-party work exists [S10] | not stated; antenna-gain link budget only [S1] | path loss, LOS and shadowing of TR 36.814 / 36.873 / 38.901 with Jakes or Rayleigh fading; reception from SINR and BLER curves [S5, S6] | MAX C/I, PF, DRR, QoS-aware PF [S1] |
@@ -54,7 +54,7 @@ Tools compared (latest release found on 2026-10-22):
54
54
 
55
55
  | Tool | Speed and scale | Validation against 3GPP |
56
56
  |---|---|---|
57
- | HandoverLab 2.4.0 | one process: 2 rules, 40 users, 76 cells, 60 s simulated in 10-15 s on a modest virtual machine (1.11.0 timings, [performance](performance.md)); many rules share one world; no published figures for thousands of nodes | large-scale calibration of TR 38.901 clause 7.8.1 against the 21 companies of R1-165974 (325 of 342 percentiles inside the range) and against R1-2506406 (at 7 GHz all 228 percentiles of UMa, UMi and SMa inside the company range, largest distance from the company mean 1.69-2.43 dB; with the two 6 GHz reruns 342 percentiles, 1.65-2.88 dB, [details](calibration-3gpp.md)); handover counts against ns-3 / 5G-LENA; failure levels differ from ns-3 ([calibration](calibration.md)); clause-by-clause [standard compliance](standard-compliance.md) |
57
+ | HandoverLab 2.5.0 | one process: 2 rules, 40 users, 76 cells, 60 s simulated in 10-15 s on a modest virtual machine (1.11.0 timings, [performance](performance.md)); many rules share one world; no published figures for thousands of nodes | large-scale calibration of TR 38.901 clause 7.8.1 against the 21 companies of R1-165974 (325 of 342 percentiles inside the range) and against R1-2506406 (at 7 GHz all 228 percentiles of UMa, UMi and SMa inside the company range, largest distance from the company mean 1.69-2.43 dB; with the two 6 GHz reruns 342 percentiles, 1.65-2.88 dB, [details](calibration-3gpp.md)); handover counts against ns-3 / 5G-LENA; failure level: the defaults give a third to a sixth of ns-3's, the comparison setting passed one fresh pre-registered set (TTT 80 and 320 ms) and failed another (TTT 640 ms) ([calibration](calibration.md)); clause-by-clause [standard compliance](standard-compliance.md) |
58
58
  | ns-3 5G-LENA v5.1 | runtime numbers: not stated for the module; a MIMO channel speed-up of up to 16 times is reported in a WNS3 paper [L11]; the calibration scenario has 12 gNBs and 120 UEs [L10] | calibrated in 3GPP outdoor reference scenarios (SIMPAT 2022) [L7]; calibration examples for RP-180524 [L2]; mobility checked against TR 36.839 (v5.1) [L5] |
59
59
  | ns-3 mmWave v8.0 | not stated | not stated (examples "verify its correct functioning" [M6]) |
60
60
  | Simu5G 1.8.0 | background cells and UEs for large scenarios; real-time emulation of "hundreds of simulated users and tens of cells" on one desktop [S1, S9] | PHY abstraction validated in 3GPP-based scenarios [S8]; fingerprint regression tests [S1] |
@@ -77,8 +77,8 @@ Tools compared (latest release found on 2026-10-22):
77
77
  - **Scale.** The Vienna simulator reports thousands of nodes [V1]; Simu5G emulates tens of cells in real time
78
78
  [S9]. HandoverLab runs in one Python process per world and is sized for tens of cells and users per run.
79
79
  - **Packet-level handover against the 3GPP mobility study.** 5G-LENA v5.1 can be checked against the TR 36.839
80
- handover-failure curves [L5], which matters because HandoverLab's absolute failure rates are not calibrated
81
- ([calibration](calibration.md)).
80
+ handover-failure curves [L5], which matters because HandoverLab's absolute failure level is checked against ns-3 / 5G-LENA only: the ns-3 comparison setting (`handoverlab.verify.ns3.COMPARISON_SETTING`, with the in-car loss of ns-3) passed the pre-registered checks on one fresh set of ns-3 runs (V1, TTT 80 and 320 ms: level 0.97, rank 0.88) and failed the level on another (N3: 0.47, because of its TTT 640 ms cells); the default link-monitoring model gives a third to a sixth of ns-3's level (0.33 on V1, 0.16 on N3)
81
+ ([calibration](calibration.md#independent-validation-study-250)).
82
82
 
83
83
  ## Where HandoverLab differs
84
84
 
@@ -95,7 +95,7 @@ Tools compared (latest release found on 2026-10-22):
95
95
 
96
96
  All accessed on 2026-10-22.
97
97
 
98
- **HandoverLab:** the pages linked above and `CHANGELOG.md` of 2.4.0.
98
+ **HandoverLab:** the pages linked above and `CHANGELOG.md` of 2.5.0.
99
99
 
100
100
  **ns-3 5G-LENA**
101
101
 
@@ -162,6 +162,15 @@ almost all of the gain comes from not waiting for the time-to-trigger: sample an
162
162
  passed only by 0.0002 attempts per UE per second), and both make 55 % more attempts and three times the
163
163
  ping-pongs. The TR tables report no ping-pong rate; read the gain together with that cost.
164
164
 
165
+ **Generalisation (2.5.0).** Tuned and trained on `urban-grid-10ghz` (ridge, case A) and tested on four other
166
+ cards with unseen seeds in a pre-registered study ([V2](../calibration.md#v2-do-the-adaptive-and-learning-rules-generalise)), `a3_pred` did not generalise: on
167
+ `urban-macro-tr37885-4ghz` it had more unnecessary handovers than tuned A3 (+0.95 per UE-minute) while its
168
+ failures fell (0.38 against 1.76), and it cannot run on `ns3-urban-3.5ghz`, whose 0.2 s measurement period
169
+ differs from the 0.04 s the predictor was trained at (the rule refuses such a predictor). Limits of the design:
170
+ with 5 seeds the smallest two-sided Wilcoxon p is 0.0625, so the no-harm check could not fail, and
171
+ `fr3-urban-15ghz` is the training grid at another frequency, so it is the least independent test card
172
+ ([V2](../calibration.md#v2-do-the-adaptive-and-learning-rules-generalise)).
173
+
165
174
  The runner and the full numbers are kept outside the package; `handoverlab verify --only learning` repeats a
166
175
  short check of the mechanics (P1-P4) on the `smoke` card.
167
176
 
@@ -92,8 +92,12 @@ Means over three seeds per user-minute, with the cards as they are and with the
92
92
  about 15 dB (InH at 20 m) when its state changes. In rural cells these jumps raise the failures from
93
93
  0.07 to 0.18 per user-minute and the handovers by about a fifth (13.07 to 15.62); indoors there are
94
94
  no failures with either state, and the hard switch adds only a few handovers (3.81 to 3.95).
95
- - The failure model is not calibrated in absolute terms in any scenario; read failure rates
96
- relative to each other.
95
+ - The absolute failure level was checked against ns-3 only on `ns3-urban-3.5ghz`, with the ns-3
96
+ comparison setting (it passed on one fresh set with TTT 80 and 320 ms and failed the level on another
97
+ with TTT 640 ms cells; [calibration](../calibration.md#independent-validation-study-250)), not in these
98
+ scenarios; the default link-monitoring model gives a third to a sixth of ns-3's level (0.33 on V1, 0.16
99
+ on N3). Read
100
+ failure rates relative to each other.
97
101
 
98
102
  ## 4. Change them
99
103