handoverlab 2.4.2__tar.gz → 2.6.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 (319) hide show
  1. handoverlab-2.6.0/.zenodo.json +39 -0
  2. {handoverlab-2.4.2 → handoverlab-2.6.0}/CHANGELOG.md +131 -1
  3. {handoverlab-2.4.2 → handoverlab-2.6.0}/CITATION.cff +9 -4
  4. {handoverlab-2.4.2 → handoverlab-2.6.0}/PKG-INFO +51 -13
  5. {handoverlab-2.4.2 → handoverlab-2.6.0}/README.md +50 -12
  6. {handoverlab-2.4.2 → handoverlab-2.6.0}/docs/calibration.md +208 -8
  7. {handoverlab-2.4.2 → handoverlab-2.6.0}/docs/comparison.md +9 -9
  8. {handoverlab-2.4.2 → handoverlab-2.6.0}/docs/guides/ai-mobility.md +9 -0
  9. {handoverlab-2.4.2 → handoverlab-2.6.0}/docs/guides/indoor-rural.md +5 -2
  10. {handoverlab-2.4.2 → handoverlab-2.6.0}/docs/guides/learning-rule.md +8 -0
  11. handoverlab-2.6.0/docs/guides/ns3-bridge.md +157 -0
  12. {handoverlab-2.4.2 → handoverlab-2.6.0}/docs/guides/upper-mid-band.md +4 -1
  13. handoverlab-2.6.0/docs/images/dashboard-charts.png +0 -0
  14. handoverlab-2.6.0/docs/images/dashboard.png +0 -0
  15. {handoverlab-2.4.2 → handoverlab-2.6.0}/docs/index.md +1 -1
  16. handoverlab-2.6.0/docs/reference/api/ns3bridge.md +28 -0
  17. {handoverlab-2.4.2 → handoverlab-2.6.0}/docs/reference/cli.md +83 -4
  18. {handoverlab-2.4.2 → handoverlab-2.6.0}/docs/scenarios/fr3-urban-15ghz.md +1 -1
  19. {handoverlab-2.4.2 → handoverlab-2.6.0}/docs/scenarios/highway-tr37885-30ghz.md +1 -1
  20. {handoverlab-2.4.2 → handoverlab-2.6.0}/docs/scenarios/highway-tr37885-4ghz.md +1 -1
  21. {handoverlab-2.4.2 → handoverlab-2.6.0}/docs/scenarios/hst-fr2-28ghz.md +1 -1
  22. {handoverlab-2.4.2 → handoverlab-2.6.0}/docs/scenarios/indoor-office-4ghz.md +1 -1
  23. {handoverlab-2.4.2 → handoverlab-2.6.0}/docs/scenarios/ns3-urban-3.5ghz.md +1 -1
  24. {handoverlab-2.4.2 → handoverlab-2.6.0}/docs/scenarios/open-area-10ghz.md +1 -1
  25. {handoverlab-2.4.2 → handoverlab-2.6.0}/docs/scenarios/rural-700mhz.md +1 -1
  26. {handoverlab-2.4.2 → handoverlab-2.6.0}/docs/scenarios/suburban-sma-7ghz.md +1 -1
  27. {handoverlab-2.4.2 → handoverlab-2.6.0}/docs/scenarios/urban-grid-10ghz.md +1 -1
  28. {handoverlab-2.4.2 → handoverlab-2.6.0}/docs/scenarios/urban-grid-28ghz-beams.md +1 -1
  29. {handoverlab-2.4.2 → handoverlab-2.6.0}/docs/scenarios/urban-macro-tr37885-4ghz.md +1 -1
  30. {handoverlab-2.4.2 → handoverlab-2.6.0}/docs/stability.md +1 -1
  31. {handoverlab-2.4.2 → handoverlab-2.6.0}/docs/standard-compliance.md +2 -1
  32. {handoverlab-2.4.2 → handoverlab-2.6.0}/docs/user_guide.md +18 -7
  33. {handoverlab-2.4.2 → handoverlab-2.6.0}/docs/verification.md +20 -2
  34. handoverlab-2.6.0/examples/ns3_study_v1.yaml +24 -0
  35. {handoverlab-2.4.2 → handoverlab-2.6.0}/mkdocs.yml +2 -0
  36. {handoverlab-2.4.2 → handoverlab-2.6.0}/pyproject.toml +1 -1
  37. {handoverlab-2.4.2 → handoverlab-2.6.0}/src/handoverlab/__init__.py +1 -1
  38. {handoverlab-2.4.2 → handoverlab-2.6.0}/src/handoverlab/cli.py +101 -2
  39. handoverlab-2.6.0/src/handoverlab/ns3bridge/__init__.py +28 -0
  40. handoverlab-2.6.0/src/handoverlab/ns3bridge/compare.py +172 -0
  41. handoverlab-2.6.0/src/handoverlab/ns3bridge/export.py +102 -0
  42. handoverlab-2.6.0/src/handoverlab/ns3bridge/results.py +116 -0
  43. handoverlab-2.6.0/src/handoverlab/ns3bridge/study.py +270 -0
  44. handoverlab-2.6.0/src/handoverlab/ns3bridge/templates.py +192 -0
  45. {handoverlab-2.4.2 → handoverlab-2.6.0}/src/handoverlab/scenarios/cards/fr3-urban-15ghz.yaml +1 -1
  46. {handoverlab-2.4.2 → handoverlab-2.6.0}/src/handoverlab/scenarios/cards/highway-tr37885-30ghz.yaml +1 -1
  47. {handoverlab-2.4.2 → handoverlab-2.6.0}/src/handoverlab/scenarios/cards/highway-tr37885-4ghz.yaml +1 -1
  48. {handoverlab-2.4.2 → handoverlab-2.6.0}/src/handoverlab/scenarios/cards/hst-fr2-28ghz.yaml +1 -1
  49. {handoverlab-2.4.2 → handoverlab-2.6.0}/src/handoverlab/scenarios/cards/indoor-office-4ghz.yaml +1 -1
  50. {handoverlab-2.4.2 → handoverlab-2.6.0}/src/handoverlab/scenarios/cards/ns3-urban-3.5ghz.yaml +1 -1
  51. {handoverlab-2.4.2 → handoverlab-2.6.0}/src/handoverlab/scenarios/cards/open-area-10ghz.yaml +1 -1
  52. {handoverlab-2.4.2 → handoverlab-2.6.0}/src/handoverlab/scenarios/cards/rural-700mhz.yaml +1 -1
  53. {handoverlab-2.4.2 → handoverlab-2.6.0}/src/handoverlab/scenarios/cards/suburban-sma-7ghz.yaml +1 -1
  54. {handoverlab-2.4.2 → handoverlab-2.6.0}/src/handoverlab/scenarios/cards/urban-grid-10ghz.yaml +1 -1
  55. {handoverlab-2.4.2 → handoverlab-2.6.0}/src/handoverlab/scenarios/cards/urban-grid-28ghz-beams.yaml +1 -1
  56. {handoverlab-2.4.2 → handoverlab-2.6.0}/src/handoverlab/scenarios/cards/urban-macro-tr37885-4ghz.yaml +1 -1
  57. {handoverlab-2.4.2 → handoverlab-2.6.0}/src/handoverlab/stability.py +3 -2
  58. {handoverlab-2.4.2 → handoverlab-2.6.0}/src/handoverlab/verify/__init__.py +6 -3
  59. {handoverlab-2.4.2 → handoverlab-2.6.0}/src/handoverlab/verify/ns3.py +47 -15
  60. {handoverlab-2.4.2 → handoverlab-2.6.0}/src/handoverlab.egg-info/PKG-INFO +51 -13
  61. {handoverlab-2.4.2 → handoverlab-2.6.0}/src/handoverlab.egg-info/SOURCES.txt +11 -0
  62. handoverlab-2.6.0/tests/data/ns3bridge/v1_runs.json +86 -0
  63. {handoverlab-2.4.2 → handoverlab-2.6.0}/tests/data/public_api.json +21 -0
  64. handoverlab-2.6.0/tests/test_ns3bridge.py +262 -0
  65. {handoverlab-2.4.2 → handoverlab-2.6.0}/tests/test_verify.py +42 -0
  66. handoverlab-2.4.2/.zenodo.json +0 -39
  67. handoverlab-2.4.2/docs/images/dashboard-charts.png +0 -0
  68. handoverlab-2.4.2/docs/images/dashboard.png +0 -0
  69. {handoverlab-2.4.2 → handoverlab-2.6.0}/CODE_OF_CONDUCT.md +0 -0
  70. {handoverlab-2.4.2 → handoverlab-2.6.0}/CONTRIBUTING.md +0 -0
  71. {handoverlab-2.4.2 → handoverlab-2.6.0}/LICENSE +0 -0
  72. {handoverlab-2.4.2 → handoverlab-2.6.0}/MANIFEST.in +0 -0
  73. {handoverlab-2.4.2 → handoverlab-2.6.0}/SECURITY.md +0 -0
  74. {handoverlab-2.4.2 → handoverlab-2.6.0}/docs/RELEASING.md +0 -0
  75. {handoverlab-2.4.2 → handoverlab-2.6.0}/docs/calibration-3gpp.md +0 -0
  76. {handoverlab-2.4.2 → handoverlab-2.6.0}/docs/guides/add-scenario.md +0 -0
  77. {handoverlab-2.4.2 → handoverlab-2.6.0}/docs/guides/beams-layers.md +0 -0
  78. {handoverlab-2.4.2 → handoverlab-2.6.0}/docs/guides/buffers.md +0 -0
  79. {handoverlab-2.4.2 → handoverlab-2.6.0}/docs/guides/daps-ltm.md +0 -0
  80. {handoverlab-2.4.2 → handoverlab-2.6.0}/docs/guides/dashboard.md +0 -0
  81. {handoverlab-2.4.2 → handoverlab-2.6.0}/docs/guides/dual-connectivity.md +0 -0
  82. {handoverlab-2.4.2 → handoverlab-2.6.0}/docs/guides/export-event-logs.md +0 -0
  83. {handoverlab-2.4.2 → handoverlab-2.6.0}/docs/guides/fading.md +0 -0
  84. {handoverlab-2.4.2 → handoverlab-2.6.0}/docs/guides/high-speed-train.md +0 -0
  85. {handoverlab-2.4.2 → handoverlab-2.6.0}/docs/guides/highway.md +0 -0
  86. {handoverlab-2.4.2 → handoverlab-2.6.0}/docs/guides/many-cores.md +0 -0
  87. {handoverlab-2.4.2 → handoverlab-2.6.0}/docs/guides/mro.md +0 -0
  88. {handoverlab-2.4.2 → handoverlab-2.6.0}/docs/guides/ntn.md +0 -0
  89. {handoverlab-2.4.2 → handoverlab-2.6.0}/docs/guides/radio-maps.md +0 -0
  90. {handoverlab-2.4.2 → handoverlab-2.6.0}/docs/guides/reproduce.md +0 -0
  91. {handoverlab-2.4.2 → handoverlab-2.6.0}/docs/guides/scheduler.md +0 -0
  92. {handoverlab-2.4.2 → handoverlab-2.6.0}/docs/guides/suburban-o2i.md +0 -0
  93. {handoverlab-2.4.2 → handoverlab-2.6.0}/docs/guides/ue-beams.md +0 -0
  94. {handoverlab-2.4.2 → handoverlab-2.6.0}/docs/images/ai-mobility.png +0 -0
  95. {handoverlab-2.4.2 → handoverlab-2.6.0}/docs/images/buffers.png +0 -0
  96. {handoverlab-2.4.2 → handoverlab-2.6.0}/docs/images/calibration_38901.png +0 -0
  97. {handoverlab-2.4.2 → handoverlab-2.6.0}/docs/images/calibration_7ghz.png +0 -0
  98. {handoverlab-2.4.2 → handoverlab-2.6.0}/docs/images/dashboard-compare.png +0 -0
  99. {handoverlab-2.4.2 → handoverlab-2.6.0}/docs/images/dashboard-where.png +0 -0
  100. {handoverlab-2.4.2 → handoverlab-2.6.0}/docs/images/dual-connectivity.png +0 -0
  101. {handoverlab-2.4.2 → handoverlab-2.6.0}/docs/images/fading.png +0 -0
  102. {handoverlab-2.4.2 → handoverlab-2.6.0}/docs/images/mro.png +0 -0
  103. {handoverlab-2.4.2 → handoverlab-2.6.0}/docs/images/new-scenarios.png +0 -0
  104. {handoverlab-2.4.2 → handoverlab-2.6.0}/docs/images/ntn-beams.png +0 -0
  105. {handoverlab-2.4.2 → handoverlab-2.6.0}/docs/images/procedures.png +0 -0
  106. {handoverlab-2.4.2 → handoverlab-2.6.0}/docs/images/radio-map.png +0 -0
  107. {handoverlab-2.4.2 → handoverlab-2.6.0}/docs/images/report-comparison.png +0 -0
  108. {handoverlab-2.4.2 → handoverlab-2.6.0}/docs/images/report-tuning.png +0 -0
  109. {handoverlab-2.4.2 → handoverlab-2.6.0}/docs/images/scheduler.png +0 -0
  110. {handoverlab-2.4.2 → handoverlab-2.6.0}/docs/images/ssb-beams.png +0 -0
  111. {handoverlab-2.4.2 → handoverlab-2.6.0}/docs/images/traffic.png +0 -0
  112. {handoverlab-2.4.2 → handoverlab-2.6.0}/docs/images/ue-beams.png +0 -0
  113. {handoverlab-2.4.2 → handoverlab-2.6.0}/docs/migration-2.0.md +0 -0
  114. {handoverlab-2.4.2 → handoverlab-2.6.0}/docs/performance.md +0 -0
  115. {handoverlab-2.4.2 → handoverlab-2.6.0}/docs/reference/api/config.md +0 -0
  116. {handoverlab-2.4.2 → handoverlab-2.6.0}/docs/reference/api/engine.md +0 -0
  117. {handoverlab-2.4.2 → handoverlab-2.6.0}/docs/reference/api/eventlog.md +0 -0
  118. {handoverlab-2.4.2 → handoverlab-2.6.0}/docs/reference/api/gui.md +0 -0
  119. {handoverlab-2.4.2 → handoverlab-2.6.0}/docs/reference/api/index.md +0 -0
  120. {handoverlab-2.4.2 → handoverlab-2.6.0}/docs/reference/api/mapfile.md +0 -0
  121. {handoverlab-2.4.2 → handoverlab-2.6.0}/docs/reference/api/ntn.md +0 -0
  122. {handoverlab-2.4.2 → handoverlab-2.6.0}/docs/reference/api/rules.md +0 -0
  123. {handoverlab-2.4.2 → handoverlab-2.6.0}/docs/reference/api/scenarios.md +0 -0
  124. {handoverlab-2.4.2 → handoverlab-2.6.0}/docs/reference/api/simulate.md +0 -0
  125. {handoverlab-2.4.2 → handoverlab-2.6.0}/docs/reference/api/stability.md +0 -0
  126. {handoverlab-2.4.2 → handoverlab-2.6.0}/docs/reference/api/stats.md +0 -0
  127. {handoverlab-2.4.2 → handoverlab-2.6.0}/docs/reference/api/studies.md +0 -0
  128. {handoverlab-2.4.2 → handoverlab-2.6.0}/docs/reference/api/testing.md +0 -0
  129. {handoverlab-2.4.2 → handoverlab-2.6.0}/docs/reference/api/verify.md +0 -0
  130. {handoverlab-2.4.2 → handoverlab-2.6.0}/docs/reference/metrics.md +0 -0
  131. {handoverlab-2.4.2 → handoverlab-2.6.0}/docs/reference-study.md +0 -0
  132. {handoverlab-2.4.2 → handoverlab-2.6.0}/docs/scenarios/dense-urban-2layer.md +0 -0
  133. {handoverlab-2.4.2 → handoverlab-2.6.0}/docs/scenarios/dense-urban-2layer.svg +0 -0
  134. {handoverlab-2.4.2 → handoverlab-2.6.0}/docs/scenarios/fr3-urban-15ghz.svg +0 -0
  135. {handoverlab-2.4.2 → handoverlab-2.6.0}/docs/scenarios/highway-tr37885-30ghz.svg +0 -0
  136. {handoverlab-2.4.2 → handoverlab-2.6.0}/docs/scenarios/highway-tr37885-4ghz.svg +0 -0
  137. {handoverlab-2.4.2 → handoverlab-2.6.0}/docs/scenarios/hst-fr2-28ghz.svg +0 -0
  138. {handoverlab-2.4.2 → handoverlab-2.6.0}/docs/scenarios/indoor-office-4ghz.svg +0 -0
  139. {handoverlab-2.4.2 → handoverlab-2.6.0}/docs/scenarios/ns3-urban-3.5ghz.svg +0 -0
  140. {handoverlab-2.4.2 → handoverlab-2.6.0}/docs/scenarios/ntn-leo600-2ghz.md +0 -0
  141. {handoverlab-2.4.2 → handoverlab-2.6.0}/docs/scenarios/ntn-leo600-2ghz.svg +0 -0
  142. {handoverlab-2.4.2 → handoverlab-2.6.0}/docs/scenarios/open-area-10ghz.svg +0 -0
  143. {handoverlab-2.4.2 → handoverlab-2.6.0}/docs/scenarios/rural-700mhz.svg +0 -0
  144. {handoverlab-2.4.2 → handoverlab-2.6.0}/docs/scenarios/smoke.md +0 -0
  145. {handoverlab-2.4.2 → handoverlab-2.6.0}/docs/scenarios/smoke.svg +0 -0
  146. {handoverlab-2.4.2 → handoverlab-2.6.0}/docs/scenarios/suburban-sma-7ghz.svg +0 -0
  147. {handoverlab-2.4.2 → handoverlab-2.6.0}/docs/scenarios/urban-grid-10ghz.svg +0 -0
  148. {handoverlab-2.4.2 → handoverlab-2.6.0}/docs/scenarios/urban-grid-28ghz-beams.svg +0 -0
  149. {handoverlab-2.4.2 → handoverlab-2.6.0}/docs/scenarios/urban-macro-tr37885-4ghz.svg +0 -0
  150. {handoverlab-2.4.2 → handoverlab-2.6.0}/docs/time-step.md +0 -0
  151. {handoverlab-2.4.2 → handoverlab-2.6.0}/docs/tutorials/first-run.md +0 -0
  152. {handoverlab-2.4.2 → handoverlab-2.6.0}/docs/tutorials/own-rule.md +0 -0
  153. {handoverlab-2.4.2 → handoverlab-2.6.0}/docs/tutorials/reading-the-report.md +0 -0
  154. {handoverlab-2.4.2 → handoverlab-2.6.0}/docs/tutorials/tuning-study.md +0 -0
  155. {handoverlab-2.4.2 → handoverlab-2.6.0}/examples/compare_a3_cho.py +0 -0
  156. {handoverlab-2.4.2 → handoverlab-2.6.0}/examples/open_area.toml +0 -0
  157. {handoverlab-2.4.2 → handoverlab-2.6.0}/examples/radio_map.py +0 -0
  158. {handoverlab-2.4.2 → handoverlab-2.6.0}/examples/smoke.yaml +0 -0
  159. {handoverlab-2.4.2 → handoverlab-2.6.0}/examples/speed_rule.py +0 -0
  160. {handoverlab-2.4.2 → handoverlab-2.6.0}/examples/street_grid.yaml +0 -0
  161. {handoverlab-2.4.2 → handoverlab-2.6.0}/examples/study_reference.yaml +0 -0
  162. {handoverlab-2.4.2 → handoverlab-2.6.0}/examples/study_reference_explore.yaml +0 -0
  163. {handoverlab-2.4.2 → handoverlab-2.6.0}/examples/study_smoke.yaml +0 -0
  164. {handoverlab-2.4.2 → handoverlab-2.6.0}/examples/traffic_models.py +0 -0
  165. {handoverlab-2.4.2 → handoverlab-2.6.0}/setup.cfg +0 -0
  166. {handoverlab-2.4.2 → handoverlab-2.6.0}/src/handoverlab/__main__.py +0 -0
  167. {handoverlab-2.4.2 → handoverlab-2.6.0}/src/handoverlab/channel/__init__.py +0 -0
  168. {handoverlab-2.4.2 → handoverlab-2.6.0}/src/handoverlab/channel/antenna.py +0 -0
  169. {handoverlab-2.4.2 → handoverlab-2.6.0}/src/handoverlab/channel/extended.py +0 -0
  170. {handoverlab-2.4.2 → handoverlab-2.6.0}/src/handoverlab/channel/fading.py +0 -0
  171. {handoverlab-2.4.2 → handoverlab-2.6.0}/src/handoverlab/channel/l2s.py +0 -0
  172. {handoverlab-2.4.2 → handoverlab-2.6.0}/src/handoverlab/channel/ntn.py +0 -0
  173. {handoverlab-2.4.2 → handoverlab-2.6.0}/src/handoverlab/channel/pathloss.py +0 -0
  174. {handoverlab-2.4.2 → handoverlab-2.6.0}/src/handoverlab/channel/radiomap.py +0 -0
  175. {handoverlab-2.4.2 → handoverlab-2.6.0}/src/handoverlab/channel/source.py +0 -0
  176. {handoverlab-2.4.2 → handoverlab-2.6.0}/src/handoverlab/config.py +0 -0
  177. {handoverlab-2.4.2 → handoverlab-2.6.0}/src/handoverlab/engine/__init__.py +0 -0
  178. {handoverlab-2.4.2 → handoverlab-2.6.0}/src/handoverlab/engine/_kernels.py +0 -0
  179. {handoverlab-2.4.2 → handoverlab-2.6.0}/src/handoverlab/engine/batch.py +0 -0
  180. {handoverlab-2.4.2 → handoverlab-2.6.0}/src/handoverlab/engine/buffers.py +0 -0
  181. {handoverlab-2.4.2 → handoverlab-2.6.0}/src/handoverlab/engine/core.py +0 -0
  182. {handoverlab-2.4.2 → handoverlab-2.6.0}/src/handoverlab/engine/dual.py +0 -0
  183. {handoverlab-2.4.2 → handoverlab-2.6.0}/src/handoverlab/engine/extended.py +0 -0
  184. {handoverlab-2.4.2 → handoverlab-2.6.0}/src/handoverlab/engine/invariants.py +0 -0
  185. {handoverlab-2.4.2 → handoverlab-2.6.0}/src/handoverlab/engine/metrics.py +0 -0
  186. {handoverlab-2.4.2 → handoverlab-2.6.0}/src/handoverlab/engine/procedures.py +0 -0
  187. {handoverlab-2.4.2 → handoverlab-2.6.0}/src/handoverlab/engine/scheduler.py +0 -0
  188. {handoverlab-2.4.2 → handoverlab-2.6.0}/src/handoverlab/eventlog.py +0 -0
  189. {handoverlab-2.4.2 → handoverlab-2.6.0}/src/handoverlab/gui/__init__.py +0 -0
  190. {handoverlab-2.4.2 → handoverlab-2.6.0}/src/handoverlab/gui/app.py +0 -0
  191. {handoverlab-2.4.2 → handoverlab-2.6.0}/src/handoverlab/gui/logic.py +0 -0
  192. {handoverlab-2.4.2 → handoverlab-2.6.0}/src/handoverlab/io.py +0 -0
  193. {handoverlab-2.4.2 → handoverlab-2.6.0}/src/handoverlab/layout.py +0 -0
  194. {handoverlab-2.4.2 → handoverlab-2.6.0}/src/handoverlab/linklevel.py +0 -0
  195. {handoverlab-2.4.2 → handoverlab-2.6.0}/src/handoverlab/mapfile.py +0 -0
  196. {handoverlab-2.4.2 → handoverlab-2.6.0}/src/handoverlab/measurement.py +0 -0
  197. {handoverlab-2.4.2 → handoverlab-2.6.0}/src/handoverlab/mobility.py +0 -0
  198. {handoverlab-2.4.2 → handoverlab-2.6.0}/src/handoverlab/py.typed +0 -0
  199. {handoverlab-2.4.2 → handoverlab-2.6.0}/src/handoverlab/requirements.py +0 -0
  200. {handoverlab-2.4.2 → handoverlab-2.6.0}/src/handoverlab/rules/__init__.py +0 -0
  201. {handoverlab-2.4.2 → handoverlab-2.6.0}/src/handoverlab/rules/base.py +0 -0
  202. {handoverlab-2.4.2 → handoverlab-2.6.0}/src/handoverlab/rules/builtin.py +0 -0
  203. {handoverlab-2.4.2 → handoverlab-2.6.0}/src/handoverlab/rules/dqn.py +0 -0
  204. {handoverlab-2.4.2 → handoverlab-2.6.0}/src/handoverlab/rules/mro.py +0 -0
  205. {handoverlab-2.4.2 → handoverlab-2.6.0}/src/handoverlab/rules/predict.py +0 -0
  206. {handoverlab-2.4.2 → handoverlab-2.6.0}/src/handoverlab/scenarios/__init__.py +0 -0
  207. {handoverlab-2.4.2 → handoverlab-2.6.0}/src/handoverlab/scenarios/cards/dense-urban-2layer.yaml +0 -0
  208. {handoverlab-2.4.2 → handoverlab-2.6.0}/src/handoverlab/scenarios/cards/ntn-leo600-2ghz.yaml +0 -0
  209. {handoverlab-2.4.2 → handoverlab-2.6.0}/src/handoverlab/scenarios/cards/smoke.yaml +0 -0
  210. {handoverlab-2.4.2 → handoverlab-2.6.0}/src/handoverlab/seeds.py +0 -0
  211. {handoverlab-2.4.2 → handoverlab-2.6.0}/src/handoverlab/simulate.py +0 -0
  212. {handoverlab-2.4.2 → handoverlab-2.6.0}/src/handoverlab/standard.py +0 -0
  213. {handoverlab-2.4.2 → handoverlab-2.6.0}/src/handoverlab/stats.py +0 -0
  214. {handoverlab-2.4.2 → handoverlab-2.6.0}/src/handoverlab/studies/__init__.py +0 -0
  215. {handoverlab-2.4.2 → handoverlab-2.6.0}/src/handoverlab/studies/config.py +0 -0
  216. {handoverlab-2.4.2 → handoverlab-2.6.0}/src/handoverlab/studies/report.py +0 -0
  217. {handoverlab-2.4.2 → handoverlab-2.6.0}/src/handoverlab/studies/runner.py +0 -0
  218. {handoverlab-2.4.2 → handoverlab-2.6.0}/src/handoverlab/studies/status.py +0 -0
  219. {handoverlab-2.4.2 → handoverlab-2.6.0}/src/handoverlab/studies/store.py +0 -0
  220. {handoverlab-2.4.2 → handoverlab-2.6.0}/src/handoverlab/studies/study.py +0 -0
  221. {handoverlab-2.4.2 → handoverlab-2.6.0}/src/handoverlab/studies/tuning.py +0 -0
  222. {handoverlab-2.4.2 → handoverlab-2.6.0}/src/handoverlab/testing.py +0 -0
  223. {handoverlab-2.4.2 → handoverlab-2.6.0}/src/handoverlab/traffic.py +0 -0
  224. {handoverlab-2.4.2 → handoverlab-2.6.0}/src/handoverlab/verify/aimobility.py +0 -0
  225. {handoverlab-2.4.2 → handoverlab-2.6.0}/src/handoverlab/verify/calibration.py +0 -0
  226. {handoverlab-2.4.2 → handoverlab-2.6.0}/src/handoverlab/verify/data/golden.json +0 -0
  227. {handoverlab-2.4.2 → handoverlab-2.6.0}/src/handoverlab/verify/data/golden_1x.json +0 -0
  228. {handoverlab-2.4.2 → handoverlab-2.6.0}/src/handoverlab/verify/data/ns3_urban_a3.json +0 -0
  229. {handoverlab-2.4.2 → handoverlab-2.6.0}/src/handoverlab/verify/data/r1_165974.csv +0 -0
  230. {handoverlab-2.4.2 → handoverlab-2.6.0}/src/handoverlab/verify/data/r1_2506406.csv +0 -0
  231. {handoverlab-2.4.2 → handoverlab-2.6.0}/src/handoverlab/verify/engine_checks.py +0 -0
  232. {handoverlab-2.4.2 → handoverlab-2.6.0}/src/handoverlab/verify/extended.py +0 -0
  233. {handoverlab-2.4.2 → handoverlab-2.6.0}/src/handoverlab/verify/fading.py +0 -0
  234. {handoverlab-2.4.2 → handoverlab-2.6.0}/src/handoverlab/verify/golden.py +0 -0
  235. {handoverlab-2.4.2 → handoverlab-2.6.0}/src/handoverlab/verify/highway.py +0 -0
  236. {handoverlab-2.4.2 → handoverlab-2.6.0}/src/handoverlab/verify/learning.py +0 -0
  237. {handoverlab-2.4.2 → handoverlab-2.6.0}/src/handoverlab/verify/ntn.py +0 -0
  238. {handoverlab-2.4.2 → handoverlab-2.6.0}/src/handoverlab/verify/radio.py +0 -0
  239. {handoverlab-2.4.2 → handoverlab-2.6.0}/src/handoverlab/verify/report.py +0 -0
  240. {handoverlab-2.4.2 → handoverlab-2.6.0}/src/handoverlab/verify/scenarios.py +0 -0
  241. {handoverlab-2.4.2 → handoverlab-2.6.0}/src/handoverlab/verify/stored.py +0 -0
  242. {handoverlab-2.4.2 → handoverlab-2.6.0}/src/handoverlab/verify/values.py +0 -0
  243. {handoverlab-2.4.2 → handoverlab-2.6.0}/src/handoverlab/world.py +0 -0
  244. {handoverlab-2.4.2 → handoverlab-2.6.0}/src/handoverlab.egg-info/dependency_links.txt +0 -0
  245. {handoverlab-2.4.2 → handoverlab-2.6.0}/src/handoverlab.egg-info/entry_points.txt +0 -0
  246. {handoverlab-2.4.2 → handoverlab-2.6.0}/src/handoverlab.egg-info/requires.txt +0 -0
  247. {handoverlab-2.4.2 → handoverlab-2.6.0}/src/handoverlab.egg-info/top_level.txt +0 -0
  248. {handoverlab-2.4.2 → handoverlab-2.6.0}/tests/conftest.py +0 -0
  249. {handoverlab-2.4.2 → handoverlab-2.6.0}/tests/data/bh6_reference.json +0 -0
  250. {handoverlab-2.4.2 → handoverlab-2.6.0}/tests/data/bh6_wide.json +0 -0
  251. {handoverlab-2.4.2 → handoverlab-2.6.0}/tests/data/make_bh6_reference.py +0 -0
  252. {handoverlab-2.4.2 → handoverlab-2.6.0}/tests/data/make_bh6_wide.py +0 -0
  253. {handoverlab-2.4.2 → handoverlab-2.6.0}/tests/helpers.py +0 -0
  254. {handoverlab-2.4.2 → handoverlab-2.6.0}/tests/test_ai_mobility.py +0 -0
  255. {handoverlab-2.4.2 → handoverlab-2.6.0}/tests/test_batch.py +0 -0
  256. {handoverlab-2.4.2 → handoverlab-2.6.0}/tests/test_beams_layers.py +0 -0
  257. {handoverlab-2.4.2 → handoverlab-2.6.0}/tests/test_buffers.py +0 -0
  258. {handoverlab-2.4.2 → handoverlab-2.6.0}/tests/test_calibration.py +0 -0
  259. {handoverlab-2.4.2 → handoverlab-2.6.0}/tests/test_channel_detail.py +0 -0
  260. {handoverlab-2.4.2 → handoverlab-2.6.0}/tests/test_cltm.py +0 -0
  261. {handoverlab-2.4.2 → handoverlab-2.6.0}/tests/test_config_cli.py +0 -0
  262. {handoverlab-2.4.2 → handoverlab-2.6.0}/tests/test_contract.py +0 -0
  263. {handoverlab-2.4.2 → handoverlab-2.6.0}/tests/test_determinism.py +0 -0
  264. {handoverlab-2.4.2 → handoverlab-2.6.0}/tests/test_dqn.py +0 -0
  265. {handoverlab-2.4.2 → handoverlab-2.6.0}/tests/test_engine_triggers.py +0 -0
  266. {handoverlab-2.4.2 → handoverlab-2.6.0}/tests/test_eventlog.py +0 -0
  267. {handoverlab-2.4.2 → handoverlab-2.6.0}/tests/test_fading.py +0 -0
  268. {handoverlab-2.4.2 → handoverlab-2.6.0}/tests/test_gui.py +0 -0
  269. {handoverlab-2.4.2 → handoverlab-2.6.0}/tests/test_highway.py +0 -0
  270. {handoverlab-2.4.2 → handoverlab-2.6.0}/tests/test_highway_tr37885.py +0 -0
  271. {handoverlab-2.4.2 → handoverlab-2.6.0}/tests/test_hst_dc.py +0 -0
  272. {handoverlab-2.4.2 → handoverlab-2.6.0}/tests/test_indoor_rural.py +0 -0
  273. {handoverlab-2.4.2 → handoverlab-2.6.0}/tests/test_invariants.py +0 -0
  274. {handoverlab-2.4.2 → handoverlab-2.6.0}/tests/test_levels_status_extend.py +0 -0
  275. {handoverlab-2.4.2 → handoverlab-2.6.0}/tests/test_lm_model.py +0 -0
  276. {handoverlab-2.4.2 → handoverlab-2.6.0}/tests/test_mapfile.py +0 -0
  277. {handoverlab-2.4.2 → handoverlab-2.6.0}/tests/test_measurement.py +0 -0
  278. {handoverlab-2.4.2 → handoverlab-2.6.0}/tests/test_mro.py +0 -0
  279. {handoverlab-2.4.2 → handoverlab-2.6.0}/tests/test_ntn.py +0 -0
  280. {handoverlab-2.4.2 → handoverlab-2.6.0}/tests/test_pathloss.py +0 -0
  281. {handoverlab-2.4.2 → handoverlab-2.6.0}/tests/test_protocol_3gpp.py +0 -0
  282. {handoverlab-2.4.2 → handoverlab-2.6.0}/tests/test_readme_boundaries.py +0 -0
  283. {handoverlab-2.4.2 → handoverlab-2.6.0}/tests/test_regression_bh6.py +0 -0
  284. {handoverlab-2.4.2 → handoverlab-2.6.0}/tests/test_regression_bh6_wide.py +0 -0
  285. {handoverlab-2.4.2 → handoverlab-2.6.0}/tests/test_rrc_detail.py +0 -0
  286. {handoverlab-2.4.2 → handoverlab-2.6.0}/tests/test_rules_rc2.py +0 -0
  287. {handoverlab-2.4.2 → handoverlab-2.6.0}/tests/test_scenarios.py +0 -0
  288. {handoverlab-2.4.2 → handoverlab-2.6.0}/tests/test_scenarios_230.py +0 -0
  289. {handoverlab-2.4.2 → handoverlab-2.6.0}/tests/test_sched_kernel.py +0 -0
  290. {handoverlab-2.4.2 → handoverlab-2.6.0}/tests/test_scheduler.py +0 -0
  291. {handoverlab-2.4.2 → handoverlab-2.6.0}/tests/test_signalling_energy.py +0 -0
  292. {handoverlab-2.4.2 → handoverlab-2.6.0}/tests/test_stability.py +0 -0
  293. {handoverlab-2.4.2 → handoverlab-2.6.0}/tests/test_stats.py +0 -0
  294. {handoverlab-2.4.2 → handoverlab-2.6.0}/tests/test_study.py +0 -0
  295. {handoverlab-2.4.2 → handoverlab-2.6.0}/tests/test_tools.py +0 -0
  296. {handoverlab-2.4.2 → handoverlab-2.6.0}/tests/test_traffic.py +0 -0
  297. {handoverlab-2.4.2 → handoverlab-2.6.0}/tests/test_tuning.py +0 -0
  298. {handoverlab-2.4.2 → handoverlab-2.6.0}/tests/test_ue_beams.py +0 -0
  299. {handoverlab-2.4.2 → handoverlab-2.6.0}/tests/test_v111.py +0 -0
  300. {handoverlab-2.4.2 → handoverlab-2.6.0}/tools/compare_fingerprints.py +0 -0
  301. {handoverlab-2.4.2 → handoverlab-2.6.0}/tools/make_ai_mobility_figure.py +0 -0
  302. {handoverlab-2.4.2 → handoverlab-2.6.0}/tools/make_beam_figure.py +0 -0
  303. {handoverlab-2.4.2 → handoverlab-2.6.0}/tools/make_buffers_figure.py +0 -0
  304. {handoverlab-2.4.2 → handoverlab-2.6.0}/tools/make_calibration_7ghz_figure.py +0 -0
  305. {handoverlab-2.4.2 → handoverlab-2.6.0}/tools/make_calibration_figure.py +0 -0
  306. {handoverlab-2.4.2 → handoverlab-2.6.0}/tools/make_dashboard_screenshots.py +0 -0
  307. {handoverlab-2.4.2 → handoverlab-2.6.0}/tools/make_dc_figure.py +0 -0
  308. {handoverlab-2.4.2 → handoverlab-2.6.0}/tools/make_fading_figure.py +0 -0
  309. {handoverlab-2.4.2 → handoverlab-2.6.0}/tools/make_mro_figure.py +0 -0
  310. {handoverlab-2.4.2 → handoverlab-2.6.0}/tools/make_ntn_figure.py +0 -0
  311. {handoverlab-2.4.2 → handoverlab-2.6.0}/tools/make_procedures_figure.py +0 -0
  312. {handoverlab-2.4.2 → handoverlab-2.6.0}/tools/make_radiomap_figure.py +0 -0
  313. {handoverlab-2.4.2 → handoverlab-2.6.0}/tools/make_readme_images.py +0 -0
  314. {handoverlab-2.4.2 → handoverlab-2.6.0}/tools/make_scenarios_figure.py +0 -0
  315. {handoverlab-2.4.2 → handoverlab-2.6.0}/tools/make_scheduler_figure.py +0 -0
  316. {handoverlab-2.4.2 → handoverlab-2.6.0}/tools/make_traffic_figure.py +0 -0
  317. {handoverlab-2.4.2 → handoverlab-2.6.0}/tools/make_ue_beams_figure.py +0 -0
  318. {handoverlab-2.4.2 → handoverlab-2.6.0}/tools/mkdocs_hooks.py +0 -0
  319. {handoverlab-2.4.2 → handoverlab-2.6.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,134 @@ All notable changes to this project are documented in this file. The format foll
7
7
 
8
8
  ## [Unreleased]
9
9
 
10
+ ## [2.6.0] - 2026-10-25
11
+
12
+ No change of defaults or results: every golden fingerprint is unchanged. This release corrects a wrong
13
+ claim of 2.5.0 about the failure level (see "Corrected").
14
+
15
+ ### Added
16
+
17
+ - A bridge to ns-3 / 5G-LENA (`handoverlab.ns3bridge`, `handoverlab ns3 export|import|compare`;
18
+ [guide](docs/guides/ns3-bridge.md)). An ns-3 study file (`examples/ns3_study_v1.yaml`) defines conditions
19
+ (site distance, speed, time-to-trigger, A3 offset, traffic, fading), users, run length and seeds once.
20
+ `export` writes a ready-to-run package of the program `benchmarks/ns3/handoverlab-ns3-a3.cc`: the program,
21
+ `build.sh`, `run.py` (standard library only, resumable, parallel, zips the results), `run.ps1` and
22
+ `manifest.json` (study hash, HandoverLab version, every ns-3 command line). `import` reads the run JSONs
23
+ into a reference file in the format of `handoverlab.verify.ns3.load_reference`. `compare` runs HandoverLab
24
+ on the same cells and seeds (ideal execution, the in-car loss of ns-3; `--comparison` adds the comparison
25
+ setting of 2.5.0) and applies the checks of the studies N3 and V1 (F1 level, F2 order, F3 handover rate,
26
+ F4 time-to-trigger effect) with the limits as options. Options the program cannot simulate (other cards,
27
+ beams, NTN, CHO, DAPS, LTM, other site distances, unknown keys) are refused, naming the option, before any
28
+ file is written.
29
+ - `handoverlab.ns3bridge` is part of the public surface (`tests/data/public_api.json`).
30
+
31
+ ### Validation
32
+
33
+ - Pre-registered acceptance of the bridge: exporting the V1 design gives the program options of the hand-made
34
+ V1 package run by run (40 command lines); importing the 40 V1 results and running `compare` with and
35
+ without the comparison setting gives the numbers of V1 (rows C and R) exactly, with
36
+ `handoverlab.run_length: card`, the HandoverLab run length registered for V1 (150 s with 60 s warm-up, so
37
+ 90 s measured against 120 s in ns-3). With the same run length on both sides (`run_length: study`, the
38
+ default; not registered) the comparison setting gives level 1.31, rank correlation 0.62 (below the limit
39
+ 0.8), handover-rate ratio 0.92 and the TTT sign in 4 of 4 pairs; the default link monitoring 0.32, 0.62,
40
+ 0.94 and 4 of 4. This difference revealed the error corrected below.
41
+
42
+ ### Corrected
43
+
44
+ - **The failure-level analyses of 2.5.0 compared unequal run lengths.** For the fresh ns-3 sets N3, V1 and
45
+ V3 the HandoverLab side ran the card's 150 s with 60 s warm-up (90 s measured), while ns-3 ran 180 s with
46
+ 60 s discarded (120 s measured); the level (F1) and order (F2) checks compare failure counts summed over
47
+ the cells, so the HandoverLab counts covered three quarters of the ns-3 time. A corrected analysis was
48
+ registered (amendment E1) before any corrected result was seen and applied to all three sets: equal run
49
+ lengths (180 s, 60 s discarded on both sides), same ns-3 runs, seeds, rows and criteria.
50
+ - Corrected results (registered analysis with the wrong run length in brackets). The ns-3 comparison
51
+ setting: N3 level 0.52 (0.47), fails; V1 level 1.31 (0.97) but rank correlation 0.62 (0.88), fails; V3
52
+ level 0.64 (0.44), fails. So it passes F1-F3 on none of the three sets. The defaults (default link
53
+ monitoring, ideal execution) fail level and rank on all three: level 0.18 / 0.32 / 0.17 (0.16 / 0.33 /
54
+ 0.095), rank 0.71 / 0.62 / 0.76. The handover-rate ratio (0.86-0.96) and the direction of the
55
+ time-to-trigger effect pass in every set and row. A rate-based level (failures per UE-minute, not affected
56
+ by the run length) equals the corrected count-based level in every set and row.
57
+ - Consequence: the 2.5.0 statement that the comparison setting passed the pre-registered checks on V1 is
58
+ withdrawn. The absolute failure level is **not calibrated**: the comparison setting gives 0.5-1.3 times
59
+ ns-3's failure level depending on the set, and its order of failures across cells is not
60
+ reliable. `COMPARISON_SETTING`, `comparison=True` and `verify --ns3-comparison` are kept (the stability
61
+ policy keeps them within 2.x) and documented as a setting that brings the level closer, not validated.
62
+ - Not affected: the comparisons of handover and unnecessary-handover rates (part A of the stored ns-3
63
+ reference, `handoverlab verify --ns3`, `verify.ns3.compare`) and N2, which use rates per UE-minute.
64
+ - V3 (registered 2026-10-25 before its runs: 40 fresh ns-3 runs, site distance 200 / 300 m, 10 / 28 m/s,
65
+ time-to-trigger 512 / 1024 ms, seeds 601-605, 1122 ns-3 failures): the comparison setting fails the level
66
+ in both analyses (0.44 registered, 0.64 corrected) with rank 0.87 / 0.88; the defaults 0.095 / 0.17. At
67
+ long time-to-trigger HandoverLab has fewer failures than ns-3, consistent with ns-3's slower out-of-sync
68
+ detection (0.65-0.83 s), which HandoverLab does not model.
69
+ - Planned, not in this release: an "ns-3-like" option (not a default) that imitates ns-3's slower
70
+ out-of-sync detection, built from the ns-3 data already seen and judged on a fresh set V4 (site distance
71
+ 200 / 400 m, 14 / 22 m/s, time-to-trigger 128 / 1280 ms, seeds 701-705), collected and kept unopened until
72
+ the option and its settings are frozen.
73
+ - Corrected everywhere the 2.5.0 claim appeared: README ("Checked against ns-3", the boundary table, What's
74
+ new), `docs/calibration.md` (the summary and the 2.5.0 study, with the registered numbers kept next to the
75
+ corrected ones, and new sections on V3 and the correction), `docs/comparison.md`, `docs/verification.md`,
76
+ `docs/standard-compliance.md`, the user guide, the guides, the 12 scenario cards (scenario pages
77
+ regenerated), the docstrings of `handoverlab.verify.ns3`, the note of `verify --ns3 --ns3-comparison`, and
78
+ the help of `verify --ns3-comparison` and `ns3 compare --comparison` (command-line reference regenerated).
79
+
80
+ ### Documentation
81
+
82
+ - Version 2.6.0 in the README, the user guide, the documentation home page and the citation metadata.
83
+ Dashboard screenshots retaken with this version.
84
+
85
+ ## [2.5.0] - 2026-10-25
86
+
87
+ No change of defaults or results: every golden fingerprint is unchanged.
88
+
89
+ ### Added
90
+
91
+ - The ns-3 comparison setting as code: `handoverlab.verify.ns3.COMPARISON_SETTING`
92
+ (`{"protocol.lm_load": 0.15, "protocol.exec_can_fail": False}`: interferer load 0.15 at link monitoring,
93
+ ideal execution), applied by `scenario_for(..., comparison=True)`, `compare(..., comparison=True)` and
94
+ `holdout(..., comparison=True)` (after the in-car loss, before `overrides`), by
95
+ `ns3_checks(..., comparison=True)` and `run_verify(..., ns3_comparison=True)` together with the in-car loss
96
+ of ns-3, and on the command line by `handoverlab verify --ns3 --ns3-comparison`. The summary of `compare`
97
+ records `car` and `comparison`. It is not a default.
98
+
99
+ ### Validation
100
+
101
+ - An independent validation study, registered before any of its runs, with one amendment fixed before the runs
102
+ of its second part; nothing tuned on its data (`docs/calibration.md`, "Independent validation study (2.5.0)").
103
+ - **V1, failure rate against 40 fresh ns-3 / 5G-LENA runs** (site distance 200 / 300 m, 10 / 28 m/s,
104
+ time-to-trigger 80 / 320 ms, seeds 401-405). The N3 candidate, not re-tuned (`lm_load` 0.15, ideal
105
+ execution, in-car loss): failure level 0.97 of ns-3 (limit 0.67-1.5), rank correlation 0.88 over the 8 cells,
106
+ handover-rate ratio 0.965, TTT direction in 4 of 4: all pass, so it becomes the documented ns-3 comparison
107
+ setting (corrected in 2.6.0: with equal run lengths it fails the rank on V1, 0.62, and is not validated).
108
+ The 2.4.2 link monitoring with ideal execution: level 0.33 (fails), rank 0.84, ratio 0.973. V1 has
109
+ time-to-trigger 80 and 320 ms only; on the earlier fresh set N3 the same setting failed the level (0.47): its
110
+ 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
111
+ registered criterion).
112
+ - **V2, generalisation of the adaptive and learning rules** (tuned and trained on `urban-grid-10ghz`, seeds
113
+ 101-104; tested on `ns3-urban-3.5ghz`, `urban-macro-tr37885-4ghz`, `highway-tr37885-4ghz` and
114
+ `fr3-urban-15ghz`, seeds 501-505, against tuned A3). `a3_mro` and `dqn` generalise (fewer unnecessary
115
+ handovers on all four test cards, no cost flag). `a3_bandit` meets the registered criteria only formally: it
116
+ has more unnecessary handovers than tuned A3 on every card (+1.644 per UE-minute on the training card) and
117
+ 1.65 to 4.15 times its ping-pong on three test cards. `a3_pred` does not generalise (the gain is lost on
118
+ `urban-macro-tr37885-4ghz`, and it cannot run at the 0.2 s measurement period of `ns3-urban-3.5ghz`). Limits
119
+ of the design: with 5 seeds the no-harm criterion cannot fail (smallest Wilcoxon p 0.0625), and
120
+ `fr3-urban-15ghz` is the least independent test card.
121
+
122
+ ### Documentation
123
+
124
+ - The failure level is now stated precisely everywhere it was called "not calibrated" (README, scenario cards,
125
+ `docs/comparison.md`, `docs/verification.md`, the user guide and the guides): the ns-3
126
+ comparison setting passed the pre-registered checks on one fresh set of ns-3 runs (V1, TTT 80 and 320 ms:
127
+ level 0.97, rank 0.88) (withdrawn in 2.6.0: wrong run length; see Corrected)
128
+ and failed the level on another (N3: 0.47, because of its TTT 640 ms cells); the
129
+ default link-monitoring model gives a third to a sixth of ns-3's level (0.33 on V1, 0.16 on N3) (corrected
130
+ in 2.6.0). Scenario pages regenerated.
131
+ - The learning-rule and AI/ML-mobility guides: the generalisation results of V2.
132
+ - README, `CITATION.cff` and `.zenodo.json` describe HandoverLab as a reproducible system-level simulator for
133
+ studying and comparing handover algorithms and procedures in 5G-Advanced, beyond-5G and some NTN scenarios,
134
+ with explicit models, optional features and a documented validation scope.
135
+ - Citation with the DOI of 2.4.2 (10.5281/zenodo.23589215).
136
+ - Command-line reference: `verify --ns3-comparison`. Dashboard screenshots retaken with this version.
137
+
10
138
  ## [2.4.2] - 2026-10-23
11
139
 
12
140
  No change of results.
@@ -1120,7 +1248,9 @@ First alpha release (stage 1: the core simulator).
1120
1248
  - Documentation: README, user guide, contribution guide, citation file; CI on Linux, Windows and
1121
1249
  macOS with Python 3.9-3.12.
1122
1250
 
1123
- [Unreleased]: https://github.com/mjs19971997/handoverlab/compare/v2.4.2...HEAD
1251
+ [Unreleased]: https://github.com/mjs19971997/handoverlab/compare/v2.6.0...HEAD
1252
+ [2.6.0]: https://github.com/mjs19971997/handoverlab/compare/v2.5.0...v2.6.0
1253
+ [2.5.0]: https://github.com/mjs19971997/handoverlab/compare/v2.4.2...v2.5.0
1124
1254
  [2.4.2]: https://github.com/mjs19971997/handoverlab/compare/v2.4.1...v2.4.2
1125
1255
  [2.4.1]: https://github.com/mjs19971997/handoverlab/compare/v2.4.0...v2.4.1
1126
1256
  [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.6.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.6.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.6.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 is not calibrated: with equal run lengths the default gives 0.17-0.32 of ns-3's and the ns-3 comparison setting 0.5-1.3 depending on the set, passing the pre-registered checks on none of three fresh ns-3 sets (corrected in 2.6.0): 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,19 @@ 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). **The absolute failure level is not
330
+ calibrated.** On three fresh, pre-registered sets of ns-3 runs (N3, V1, V3), compared over equal run
331
+ lengths, the default link-monitoring model gives 0.18, 0.32 and 0.17 of ns-3's failure level and fails
332
+ level and order on all three. The ns-3 comparison setting of 2.5.0
333
+ (`handoverlab.verify.ns3.COMPARISON_SETTING`: interferer load 0.15 at link monitoring and ideal execution,
334
+ with the in-car loss of ns-3; `handoverlab verify --ns3 --ns3-comparison`) brings the level closer but is
335
+ not validated: 0.52 (N3), 1.31 (V1, where the order of failures across cells fails, rank 0.62) and 0.64
336
+ (V3), so 0.5-1.3 times ns-3's level depending on the set, passing the checks on none of the
337
+ three sets. The handover-rate ratio (0.86-0.96) and the direction of the time-to-trigger effect agree in
338
+ every set. *Corrected in 2.6.0:* 2.5.0 reported that the setting passed on V1 (level 0.97, rank 0.88);
339
+ that analysis compared 90 s of HandoverLab with 120 s of ns-3. The setting is not a default, so
340
+ compare failure rates between rules within HandoverLab. The study and the causes of the differences are
341
+ 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
342
  than ns-3:
333
343
 
334
344
  - **In-car penetration loss.** TR 38.901 clause 7.4.3.2 gives it to a user inside a car. ns-3 adds
@@ -343,8 +353,28 @@ HandoverLab has had this as an option since 1.3.0 (`protocol.rlm_holdoff_s`), on
343
353
  covers baseline handover at 3.5 GHz only: 5G-LENA has no conditional handover, DAPS or
344
354
  L1/L2-triggered mobility.
345
355
 
356
+ To run your own study in both simulators, `handoverlab ns3 export|import|compare` writes the ns-3 package, reads its results back and applies the same checks ([guide](https://github.com/mjs19971997/handoverlab/blob/main/docs/guides/ns3-bridge.md)).
357
+
346
358
  ## What's new
347
359
 
360
+ - **2.6.0**: a bridge to ns-3 / 5G-LENA (`handoverlab ns3 export|import|compare`) that writes a ready-to-run
361
+ ns-3 package for a study, reads its results back and applies the checks of the ns-3 studies
362
+ ([guide](https://github.com/mjs19971997/handoverlab/blob/main/docs/guides/ns3-bridge.md)). It found an error in 2.5.0: the failure counts of
363
+ HandoverLab covered 90 s against 120 s of ns-3. Corrected, the ns-3 comparison setting passes the
364
+ pre-registered checks on none of the three fresh ns-3 sets (level 0.52, 1.31 with rank 0.62, 0.64): the
365
+ absolute failure level is not calibrated ([correction](https://github.com/mjs19971997/handoverlab/blob/main/docs/calibration.md#correction-in-260-equal-run-lengths)). Handover-rate comparisons were not
366
+ affected. No change of results.
367
+ - **2.5.0** (corrected in 2.6.0): an independent, pre-registered [validation study](https://github.com/mjs19971997/handoverlab/blob/main/docs/calibration.md#independent-validation-study-250). On 40
368
+ fresh ns-3 runs (V1, TTT 80 and 320 ms) the ns-3 comparison setting was reported to pass the
369
+ pre-registered checks (level 0.97, order 0.88); this compared 90 s of HandoverLab with 120 s of ns-3, and
370
+ with equal run lengths it fails the order on V1 (rank 0.62) and the level on N3 and V3 (0.52, 0.64), so it
371
+ is not validated. It is documented as code (`handoverlab.verify.ns3.COMPARISON_SETTING`,
372
+ `verify --ns3 --ns3-comparison`; not a default). Tuned on one card and tested on four unseen cards and
373
+ seeds, `a3_mro` and `dqn` kept fewer unnecessary handovers than tuned A3; `a3_bandit` met the criteria
374
+ only formally (more unnecessary handovers and ping-pong); `a3_pred` did not generalise. Limits of this
375
+ design: with 5 seeds the smallest two-sided Wilcoxon p is 0.0625, so the no-harm check could not fail, and
376
+ `fr3-urban-15ghz` is the training grid at another frequency, so it is the least independent test card
377
+ ([V2](https://github.com/mjs19971997/handoverlab/blob/main/docs/calibration.md#v2-do-the-adaptive-and-learning-rules-generalise)). No change of results.
348
378
  - **2.4.0**: UE receive beams (panels of TR 38.802, best beam or a sweep); a measurement-prediction rule after
349
379
  TR 38.744 (`a3_pred`: predicted Event A3 or measurement skipping, sample and hold or a trained ridge
350
380
  predictor); the slot scheduler as a compiled kernel (`run.backend: numba`, bit-identical; on the ns-3 card
@@ -569,9 +599,16 @@ the pages are plain Markdown in [docs/](https://github.com/mjs19971997/handoverl
569
599
  scheduler, the 7 GHz calibration and the comparison with other simulators.
570
600
  27. 2.4.1: pictures, citation and tutorials brought up to date; the example study
571
601
  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;
602
+ 28. 2.4.2: every option has a picture; the dashboard pictures show the current version;
573
603
  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
604
+ 29. 2.5.0: an independent, pre-registered validation study: the ns-3 comparison setting for the
605
+ failure level (its pass on V1 was corrected in 2.6.0: not validated), and the generalisation of the
606
+ adaptive and learning rules to unseen cards and seeds; no change of results.
607
+ 30. **2.6.0 (this release)**: the bridge to ns-3 / 5G-LENA (export, import, compare); the correction of the
608
+ 2.5.0 failure-level comparison (equal run lengths: the absolute failure level is not calibrated); no
609
+ change of results.
610
+ 31. Later (planned, not done), staying a handover simulator: an "ns-3-like" option imitating ns-3's slower
611
+ out-of-sync detection, judged on a fresh ns-3 set (V4) kept unopened until it is frozen; the effect of a handover on what the user
575
612
  sees (simple TCP and application models on top of the buffers), MIMO as an SINR gain, hundreds of
576
613
  users per run, and one scenario definition that also runs in ns-3; condEventT1 and earth-fixed beams.
577
614
 
@@ -581,9 +618,10 @@ If you use HandoverLab, please cite it as described in [CITATION.cff](https://gi
581
618
  it under *Cite this repository*). Every release is archived on Zenodo:
582
619
  [doi:10.5281/zenodo.23409131](https://doi.org/10.5281/zenodo.23409131) cites all versions (it resolves to
583
620
  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
621
+ the DOI of the latest version is listed; for example, version 2.4.2 is
622
+ [doi:10.5281/zenodo.23589215](https://doi.org/10.5281/zenodo.23589215), version 2.4.1
585
623
  [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;
624
+ [doi:10.5281/zenodo.23582521](https://doi.org/10.5281/zenodo.23582521). The BibTeX entry below cites 2.4.2;
587
625
  replace version and DOI with those of the version you used.
588
626
 
589
627
  ```bibtex
@@ -592,8 +630,8 @@ replace version and DOI with those of the version you used.
592
630
  title = {{HandoverLab}: system-level handover simulator for 5G-Advanced and beyond-5G research},
593
631
  year = {2026},
594
632
  publisher = {Zenodo},
595
- version = {2.4.1},
596
- doi = {10.5281/zenodo.23585308},
633
+ version = {2.4.2},
634
+ doi = {10.5281/zenodo.23589215},
597
635
  url = {https://github.com/mjs19971997/handoverlab}
598
636
  }
599
637
  ```
@@ -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.6.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 is not calibrated: with equal run lengths the default gives 0.17-0.32 of ns-3's and the ns-3 comparison setting 0.5-1.3 depending on the set, passing the pre-registered checks on none of three fresh ns-3 sets (corrected in 2.6.0): 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,19 @@ 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). **The absolute failure level is not
272
+ calibrated.** On three fresh, pre-registered sets of ns-3 runs (N3, V1, V3), compared over equal run
273
+ lengths, the default link-monitoring model gives 0.18, 0.32 and 0.17 of ns-3's failure level and fails
274
+ level and order on all three. The ns-3 comparison setting of 2.5.0
275
+ (`handoverlab.verify.ns3.COMPARISON_SETTING`: interferer load 0.15 at link monitoring and ideal execution,
276
+ with the in-car loss of ns-3; `handoverlab verify --ns3 --ns3-comparison`) brings the level closer but is
277
+ not validated: 0.52 (N3), 1.31 (V1, where the order of failures across cells fails, rank 0.62) and 0.64
278
+ (V3), so 0.5-1.3 times ns-3's level depending on the set, passing the checks on none of the
279
+ three sets. The handover-rate ratio (0.86-0.96) and the direction of the time-to-trigger effect agree in
280
+ every set. *Corrected in 2.6.0:* 2.5.0 reported that the setting passed on V1 (level 0.97, rank 0.88);
281
+ that analysis compared 90 s of HandoverLab with 120 s of ns-3. The setting is not a default, so
282
+ compare failure rates between rules within HandoverLab. The study and the causes of the differences are
283
+ 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
284
  than ns-3:
275
285
 
276
286
  - **In-car penetration loss.** TR 38.901 clause 7.4.3.2 gives it to a user inside a car. ns-3 adds
@@ -285,8 +295,28 @@ HandoverLab has had this as an option since 1.3.0 (`protocol.rlm_holdoff_s`), on
285
295
  covers baseline handover at 3.5 GHz only: 5G-LENA has no conditional handover, DAPS or
286
296
  L1/L2-triggered mobility.
287
297
 
298
+ To run your own study in both simulators, `handoverlab ns3 export|import|compare` writes the ns-3 package, reads its results back and applies the same checks ([guide](https://github.com/mjs19971997/handoverlab/blob/main/docs/guides/ns3-bridge.md)).
299
+
288
300
  ## What's new
289
301
 
302
+ - **2.6.0**: a bridge to ns-3 / 5G-LENA (`handoverlab ns3 export|import|compare`) that writes a ready-to-run
303
+ ns-3 package for a study, reads its results back and applies the checks of the ns-3 studies
304
+ ([guide](https://github.com/mjs19971997/handoverlab/blob/main/docs/guides/ns3-bridge.md)). It found an error in 2.5.0: the failure counts of
305
+ HandoverLab covered 90 s against 120 s of ns-3. Corrected, the ns-3 comparison setting passes the
306
+ pre-registered checks on none of the three fresh ns-3 sets (level 0.52, 1.31 with rank 0.62, 0.64): the
307
+ absolute failure level is not calibrated ([correction](https://github.com/mjs19971997/handoverlab/blob/main/docs/calibration.md#correction-in-260-equal-run-lengths)). Handover-rate comparisons were not
308
+ affected. No change of results.
309
+ - **2.5.0** (corrected in 2.6.0): an independent, pre-registered [validation study](https://github.com/mjs19971997/handoverlab/blob/main/docs/calibration.md#independent-validation-study-250). On 40
310
+ fresh ns-3 runs (V1, TTT 80 and 320 ms) the ns-3 comparison setting was reported to pass the
311
+ pre-registered checks (level 0.97, order 0.88); this compared 90 s of HandoverLab with 120 s of ns-3, and
312
+ with equal run lengths it fails the order on V1 (rank 0.62) and the level on N3 and V3 (0.52, 0.64), so it
313
+ is not validated. It is documented as code (`handoverlab.verify.ns3.COMPARISON_SETTING`,
314
+ `verify --ns3 --ns3-comparison`; not a default). Tuned on one card and tested on four unseen cards and
315
+ seeds, `a3_mro` and `dqn` kept fewer unnecessary handovers than tuned A3; `a3_bandit` met the criteria
316
+ only formally (more unnecessary handovers and ping-pong); `a3_pred` did not generalise. Limits of this
317
+ design: with 5 seeds the smallest two-sided Wilcoxon p is 0.0625, so the no-harm check could not fail, and
318
+ `fr3-urban-15ghz` is the training grid at another frequency, so it is the least independent test card
319
+ ([V2](https://github.com/mjs19971997/handoverlab/blob/main/docs/calibration.md#v2-do-the-adaptive-and-learning-rules-generalise)). No change of results.
290
320
  - **2.4.0**: UE receive beams (panels of TR 38.802, best beam or a sweep); a measurement-prediction rule after
291
321
  TR 38.744 (`a3_pred`: predicted Event A3 or measurement skipping, sample and hold or a trained ridge
292
322
  predictor); the slot scheduler as a compiled kernel (`run.backend: numba`, bit-identical; on the ns-3 card
@@ -511,9 +541,16 @@ the pages are plain Markdown in [docs/](https://github.com/mjs19971997/handoverl
511
541
  scheduler, the 7 GHz calibration and the comparison with other simulators.
512
542
  27. 2.4.1: pictures, citation and tutorials brought up to date; the example study
513
543
  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;
544
+ 28. 2.4.2: every option has a picture; the dashboard pictures show the current version;
515
545
  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
546
+ 29. 2.5.0: an independent, pre-registered validation study: the ns-3 comparison setting for the
547
+ failure level (its pass on V1 was corrected in 2.6.0: not validated), and the generalisation of the
548
+ adaptive and learning rules to unseen cards and seeds; no change of results.
549
+ 30. **2.6.0 (this release)**: the bridge to ns-3 / 5G-LENA (export, import, compare); the correction of the
550
+ 2.5.0 failure-level comparison (equal run lengths: the absolute failure level is not calibrated); no
551
+ change of results.
552
+ 31. Later (planned, not done), staying a handover simulator: an "ns-3-like" option imitating ns-3's slower
553
+ out-of-sync detection, judged on a fresh ns-3 set (V4) kept unopened until it is frozen; the effect of a handover on what the user
517
554
  sees (simple TCP and application models on top of the buffers), MIMO as an SINR gain, hundreds of
518
555
  users per run, and one scenario definition that also runs in ns-3; condEventT1 and earth-fixed beams.
519
556
 
@@ -523,9 +560,10 @@ If you use HandoverLab, please cite it as described in [CITATION.cff](https://gi
523
560
  it under *Cite this repository*). Every release is archived on Zenodo:
524
561
  [doi:10.5281/zenodo.23409131](https://doi.org/10.5281/zenodo.23409131) cites all versions (it resolves to
525
562
  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
563
+ the DOI of the latest version is listed; for example, version 2.4.2 is
564
+ [doi:10.5281/zenodo.23589215](https://doi.org/10.5281/zenodo.23589215), version 2.4.1
527
565
  [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;
566
+ [doi:10.5281/zenodo.23582521](https://doi.org/10.5281/zenodo.23582521). The BibTeX entry below cites 2.4.2;
529
567
  replace version and DOI with those of the version you used.
530
568
 
531
569
  ```bibtex
@@ -534,8 +572,8 @@ replace version and DOI with those of the version you used.
534
572
  title = {{HandoverLab}: system-level handover simulator for 5G-Advanced and beyond-5G research},
535
573
  year = {2026},
536
574
  publisher = {Zenodo},
537
- version = {2.4.1},
538
- doi = {10.5281/zenodo.23585308},
575
+ version = {2.4.2},
576
+ doi = {10.5281/zenodo.23589215},
539
577
  url = {https://github.com/mjs19971997/handoverlab}
540
578
  }
541
579
  ```