rosotacom-dev 2.5.dev41__tar.gz → 2.5.dev47__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 (364) hide show
  1. {rosotacom_dev-2.5.dev41 → rosotacom_dev-2.5.dev47}/PKG-INFO +1 -1
  2. {rosotacom_dev-2.5.dev41 → rosotacom_dev-2.5.dev47}/docs/ffmpeg-keyframes.md +24 -5
  3. {rosotacom_dev-2.5.dev41 → rosotacom_dev-2.5.dev47}/docs/findings/README.md +1 -0
  4. rosotacom_dev-2.5.dev47/docs/findings/reorder-becomes-reader-loss.md +69 -0
  5. rosotacom_dev-2.5.dev47/docs/findings/repro/reorder_drop_repro.py +170 -0
  6. {rosotacom_dev-2.5.dev41 → rosotacom_dev-2.5.dev47}/docs/forensics-report.md +16 -0
  7. {rosotacom_dev-2.5.dev41 → rosotacom_dev-2.5.dev47}/docs/rfcs/0004-network-profiles.md +44 -7
  8. {rosotacom_dev-2.5.dev41 → rosotacom_dev-2.5.dev47}/session-configuration.md +21 -0
  9. {rosotacom_dev-2.5.dev41 → rosotacom_dev-2.5.dev47}/src/rosotacom/cli_benchmark.py +24 -0
  10. {rosotacom_dev-2.5.dev41 → rosotacom_dev-2.5.dev47}/src/rosotacom/forensics.py +143 -24
  11. rosotacom_dev-2.5.dev47/src/rosotacom/netem_dist.py +104 -0
  12. {rosotacom_dev-2.5.dev41 → rosotacom_dev-2.5.dev47}/src/rosotacom/network_profiles.py +162 -17
  13. rosotacom_dev-2.5.dev47/src/rosotacom/resources/examples/dist/field-20260817.dist +513 -0
  14. rosotacom_dev-2.5.dev47/src/rosotacom/resources/examples/profiles.yaml +171 -0
  15. rosotacom_dev-2.5.dev47/src/rosotacom/resources/ws/ros2src/com_py/com_py/ffmpeg_flags.py +58 -0
  16. rosotacom_dev-2.5.dev47/src/rosotacom/resources/ws/ros2src/com_py/com_py/playout_pacer.py +100 -0
  17. rosotacom_dev-2.5.dev47/src/rosotacom/resources/ws/ros2src/com_py/com_py/playout_pacer_core.py +83 -0
  18. {rosotacom_dev-2.5.dev41 → rosotacom_dev-2.5.dev47}/src/rosotacom/resources/ws/ros2src/com_py/com_py/status_overview.py +8 -0
  19. {rosotacom_dev-2.5.dev41 → rosotacom_dev-2.5.dev47}/src/rosotacom/resources/ws/ros2src/com_py/com_py/status_overview_core.py +35 -30
  20. {rosotacom_dev-2.5.dev41 → rosotacom_dev-2.5.dev47}/src/rosotacom/resources/ws/ros2src/com_py/setup.py +1 -0
  21. {rosotacom_dev-2.5.dev41 → rosotacom_dev-2.5.dev47}/src/rosotacom/stream_stats.py +8 -1
  22. {rosotacom_dev-2.5.dev41 → rosotacom_dev-2.5.dev47}/src/rosotacom_dev.egg-info/PKG-INFO +1 -1
  23. {rosotacom_dev-2.5.dev41 → rosotacom_dev-2.5.dev47}/src/rosotacom_dev.egg-info/SOURCES.txt +11 -0
  24. {rosotacom_dev-2.5.dev41 → rosotacom_dev-2.5.dev47}/src/rosotacom_dev.egg-info/scm_file_list.json +11 -0
  25. {rosotacom_dev-2.5.dev41 → rosotacom_dev-2.5.dev47}/src/rosotacom_dev.egg-info/scm_version.json +2 -2
  26. rosotacom_dev-2.5.dev47/tests/unit/test_ffmpeg_flags.py +130 -0
  27. rosotacom_dev-2.5.dev47/tests/unit/test_field_profiles.py +51 -0
  28. {rosotacom_dev-2.5.dev41 → rosotacom_dev-2.5.dev47}/tests/unit/test_forensics.py +153 -0
  29. rosotacom_dev-2.5.dev47/tests/unit/test_netem_dist.py +62 -0
  30. {rosotacom_dev-2.5.dev41 → rosotacom_dev-2.5.dev47}/tests/unit/test_network_profiles.py +87 -0
  31. rosotacom_dev-2.5.dev47/tests/unit/test_playout_pacer.py +81 -0
  32. {rosotacom_dev-2.5.dev41 → rosotacom_dev-2.5.dev47}/tests/unit/test_status_overview.py +27 -0
  33. {rosotacom_dev-2.5.dev41 → rosotacom_dev-2.5.dev47}/tests/unit/test_status_overview_observer.py +106 -5
  34. {rosotacom_dev-2.5.dev41 → rosotacom_dev-2.5.dev47}/tests/unit/test_stream_stats.py +12 -0
  35. rosotacom_dev-2.5.dev41/src/rosotacom/resources/examples/profiles.yaml +0 -85
  36. {rosotacom_dev-2.5.dev41 → rosotacom_dev-2.5.dev47}/.devcontainer/devcontainer.json.template +0 -0
  37. {rosotacom_dev-2.5.dev41 → rosotacom_dev-2.5.dev47}/.devcontainer/re_generate_devcontainer_json.py +0 -0
  38. {rosotacom_dev-2.5.dev41 → rosotacom_dev-2.5.dev47}/.github/CODEOWNERS +0 -0
  39. {rosotacom_dev-2.5.dev41 → rosotacom_dev-2.5.dev47}/.github/ISSUE_TEMPLATE/documentation-audit.md +0 -0
  40. {rosotacom_dev-2.5.dev41 → rosotacom_dev-2.5.dev47}/.github/ISSUE_TEMPLATE/implementation-cleanup.md +0 -0
  41. {rosotacom_dev-2.5.dev41 → rosotacom_dev-2.5.dev47}/.github/ISSUE_TEMPLATE/quality-workflow.md +0 -0
  42. {rosotacom_dev-2.5.dev41 → rosotacom_dev-2.5.dev47}/.github/ISSUE_TEMPLATE/redesign.md +0 -0
  43. {rosotacom_dev-2.5.dev41 → rosotacom_dev-2.5.dev47}/.github/ISSUE_TEMPLATE/release-process.md +0 -0
  44. {rosotacom_dev-2.5.dev41 → rosotacom_dev-2.5.dev47}/.github/ISSUE_TEMPLATE/repository-diagnosis.md +0 -0
  45. {rosotacom_dev-2.5.dev41 → rosotacom_dev-2.5.dev47}/.github/ISSUE_TEMPLATE/test-ci-audit.md +0 -0
  46. {rosotacom_dev-2.5.dev41 → rosotacom_dev-2.5.dev47}/.github/ISSUE_TEMPLATE/work-item.md +0 -0
  47. {rosotacom_dev-2.5.dev41 → rosotacom_dev-2.5.dev47}/.github/PULL_REQUEST_TEMPLATE.md +0 -0
  48. {rosotacom_dev-2.5.dev41 → rosotacom_dev-2.5.dev47}/.github/actions/setup-just/action.yml +0 -0
  49. {rosotacom_dev-2.5.dev41 → rosotacom_dev-2.5.dev47}/.github/dependabot.yml +0 -0
  50. {rosotacom_dev-2.5.dev41 → rosotacom_dev-2.5.dev47}/.github/workflows/benchmark-calibrate.yml +0 -0
  51. {rosotacom_dev-2.5.dev41 → rosotacom_dev-2.5.dev47}/.github/workflows/benchmark-gate.yml +0 -0
  52. {rosotacom_dev-2.5.dev41 → rosotacom_dev-2.5.dev47}/.github/workflows/dev-release.yml +0 -0
  53. {rosotacom_dev-2.5.dev41 → rosotacom_dev-2.5.dev47}/.github/workflows/image-scan.yml +0 -0
  54. {rosotacom_dev-2.5.dev41 → rosotacom_dev-2.5.dev47}/.github/workflows/nightly-e2e.yml +0 -0
  55. {rosotacom_dev-2.5.dev41 → rosotacom_dev-2.5.dev47}/.github/workflows/pr-lightweight.yml +0 -0
  56. {rosotacom_dev-2.5.dev41 → rosotacom_dev-2.5.dev47}/.github/workflows/pr-merge-gate.yml +0 -0
  57. {rosotacom_dev-2.5.dev41 → rosotacom_dev-2.5.dev47}/.github/workflows/release.yml +0 -0
  58. {rosotacom_dev-2.5.dev41 → rosotacom_dev-2.5.dev47}/.pre-commit-config.yaml +0 -0
  59. {rosotacom_dev-2.5.dev41 → rosotacom_dev-2.5.dev47}/AGENTS.md +0 -0
  60. {rosotacom_dev-2.5.dev41 → rosotacom_dev-2.5.dev47}/CONCURRENCY.md +0 -0
  61. {rosotacom_dev-2.5.dev41 → rosotacom_dev-2.5.dev47}/CONTRIBUTING.md +0 -0
  62. {rosotacom_dev-2.5.dev41 → rosotacom_dev-2.5.dev47}/DEVELOPMENT_PRINCIPLES.md +0 -0
  63. {rosotacom_dev-2.5.dev41 → rosotacom_dev-2.5.dev47}/LICENSE.txt +0 -0
  64. {rosotacom_dev-2.5.dev41 → rosotacom_dev-2.5.dev47}/MANIFEST.in +0 -0
  65. {rosotacom_dev-2.5.dev41 → rosotacom_dev-2.5.dev47}/README.md +0 -0
  66. {rosotacom_dev-2.5.dev41 → rosotacom_dev-2.5.dev47}/budgets.jsonl +0 -0
  67. {rosotacom_dev-2.5.dev41 → rosotacom_dev-2.5.dev47}/calibration/README.md +0 -0
  68. {rosotacom_dev-2.5.dev41 → rosotacom_dev-2.5.dev47}/calibration/capacity-size-tight-cyclone.json +0 -0
  69. {rosotacom_dev-2.5.dev41 → rosotacom_dev-2.5.dev47}/calibration/probe-loss-gop-tight-cyclone.json +0 -0
  70. {rosotacom_dev-2.5.dev41 → rosotacom_dev-2.5.dev47}/calibration/probe-loss-nominal-cyclone.json +0 -0
  71. {rosotacom_dev-2.5.dev41 → rosotacom_dev-2.5.dev47}/calibration/probe-loss-tight-cyclone.json +0 -0
  72. {rosotacom_dev-2.5.dev41 → rosotacom_dev-2.5.dev47}/calibration/probe-loss-tight-fastdds.json +0 -0
  73. {rosotacom_dev-2.5.dev41 → rosotacom_dev-2.5.dev47}/calibration/probe-loss-tight-zenoh.json +0 -0
  74. {rosotacom_dev-2.5.dev41 → rosotacom_dev-2.5.dev47}/codecov.yml +0 -0
  75. {rosotacom_dev-2.5.dev41 → rosotacom_dev-2.5.dev47}/deployment-configuration.md +0 -0
  76. {rosotacom_dev-2.5.dev41 → rosotacom_dev-2.5.dev47}/docs/benchmark-ab.md +0 -0
  77. {rosotacom_dev-2.5.dev41 → rosotacom_dev-2.5.dev47}/docs/benchmark-bag-as-load.md +0 -0
  78. {rosotacom_dev-2.5.dev41 → rosotacom_dev-2.5.dev47}/docs/ci.md +0 -0
  79. {rosotacom_dev-2.5.dev41 → rosotacom_dev-2.5.dev47}/docs/findings/cyclone-spdp-discovery-bursts.md +0 -0
  80. {rosotacom_dev-2.5.dev41 → rosotacom_dev-2.5.dev47}/docs/findings/delay-alone-no-loss.md +0 -0
  81. {rosotacom_dev-2.5.dev41 → rosotacom_dev-2.5.dev47}/docs/findings/head-of-line-irregularity.md +0 -0
  82. {rosotacom_dev-2.5.dev41 → rosotacom_dev-2.5.dev47}/docs/findings/jitter-loss-bandwidth-latency.md +0 -0
  83. {rosotacom_dev-2.5.dev41 → rosotacom_dev-2.5.dev47}/docs/findings/lighter-message-loss.md +0 -0
  84. {rosotacom_dev-2.5.dev41 → rosotacom_dev-2.5.dev47}/docs/findings/zero-loss-boundaries-18kb-20hz.md +0 -0
  85. {rosotacom_dev-2.5.dev41 → rosotacom_dev-2.5.dev47}/docs/geo-link-map.md +0 -0
  86. {rosotacom_dev-2.5.dev41 → rosotacom_dev-2.5.dev47}/docs/github-repository-settings.md +0 -0
  87. {rosotacom_dev-2.5.dev41 → rosotacom_dev-2.5.dev47}/docs/link-bandwidth.md +0 -0
  88. {rosotacom_dev-2.5.dev41 → rosotacom_dev-2.5.dev47}/docs/link-trace.md +0 -0
  89. {rosotacom_dev-2.5.dev41 → rosotacom_dev-2.5.dev47}/docs/owner-runbook.md +0 -0
  90. {rosotacom_dev-2.5.dev41 → rosotacom_dev-2.5.dev47}/docs/performance-bands.md +0 -0
  91. {rosotacom_dev-2.5.dev41 → rosotacom_dev-2.5.dev47}/docs/playbooks/README.md +0 -0
  92. {rosotacom_dev-2.5.dev41 → rosotacom_dev-2.5.dev47}/docs/quality-model.md +0 -0
  93. {rosotacom_dev-2.5.dev41 → rosotacom_dev-2.5.dev47}/docs/release-notes/TEMPLATE.md +0 -0
  94. {rosotacom_dev-2.5.dev41 → rosotacom_dev-2.5.dev47}/docs/release-notes/v2.1.md +0 -0
  95. {rosotacom_dev-2.5.dev41 → rosotacom_dev-2.5.dev47}/docs/release-notes/v2.2.md +0 -0
  96. {rosotacom_dev-2.5.dev41 → rosotacom_dev-2.5.dev47}/docs/release-notes/v2.3.md +0 -0
  97. {rosotacom_dev-2.5.dev41 → rosotacom_dev-2.5.dev47}/docs/release-notes/v2.4.md +0 -0
  98. {rosotacom_dev-2.5.dev41 → rosotacom_dev-2.5.dev47}/docs/release.md +0 -0
  99. {rosotacom_dev-2.5.dev41 → rosotacom_dev-2.5.dev47}/docs/rfcs/0001-expectation-driven-test-suite.md +0 -0
  100. {rosotacom_dev-2.5.dev41 → rosotacom_dev-2.5.dev47}/docs/rfcs/0002-expectation-concepts.md +0 -0
  101. {rosotacom_dev-2.5.dev41 → rosotacom_dev-2.5.dev47}/docs/rfcs/0003-metric-backbone.md +0 -0
  102. {rosotacom_dev-2.5.dev41 → rosotacom_dev-2.5.dev47}/docs/rfcs/0005-benchmark-genres-and-ci.md +0 -0
  103. {rosotacom_dev-2.5.dev41 → rosotacom_dev-2.5.dev47}/docs/rfcs/0006-dynamic-qos.md +0 -0
  104. {rosotacom_dev-2.5.dev41 → rosotacom_dev-2.5.dev47}/docs/rfcs/0007-regression-gate.md +0 -0
  105. {rosotacom_dev-2.5.dev41 → rosotacom_dev-2.5.dev47}/docs/rfcs/README.md +0 -0
  106. {rosotacom_dev-2.5.dev41 → rosotacom_dev-2.5.dev47}/docs/testing.md +0 -0
  107. {rosotacom_dev-2.5.dev41 → rosotacom_dev-2.5.dev47}/docs/work-items.md +0 -0
  108. {rosotacom_dev-2.5.dev41 → rosotacom_dev-2.5.dev47}/install.sh +0 -0
  109. {rosotacom_dev-2.5.dev41 → rosotacom_dev-2.5.dev47}/justfile +0 -0
  110. {rosotacom_dev-2.5.dev41 → rosotacom_dev-2.5.dev47}/media/control_center.png +0 -0
  111. {rosotacom_dev-2.5.dev41 → rosotacom_dev-2.5.dev47}/media/echo_test.png +0 -0
  112. {rosotacom_dev-2.5.dev41 → rosotacom_dev-2.5.dev47}/media/forensics-report-example.png +0 -0
  113. {rosotacom_dev-2.5.dev41 → rosotacom_dev-2.5.dev47}/media/host.png +0 -0
  114. {rosotacom_dev-2.5.dev41 → rosotacom_dev-2.5.dev47}/media/vehicle.png +0 -0
  115. {rosotacom_dev-2.5.dev41 → rosotacom_dev-2.5.dev47}/pyproject.toml +0 -0
  116. {rosotacom_dev-2.5.dev41 → rosotacom_dev-2.5.dev47}/requirements.txt +0 -0
  117. {rosotacom_dev-2.5.dev41 → rosotacom_dev-2.5.dev47}/rosotacom.yaml +0 -0
  118. {rosotacom_dev-2.5.dev41 → rosotacom_dev-2.5.dev47}/setup.cfg +0 -0
  119. {rosotacom_dev-2.5.dev41 → rosotacom_dev-2.5.dev47}/src/rosotacom/__init__.py +0 -0
  120. {rosotacom_dev-2.5.dev41 → rosotacom_dev-2.5.dev47}/src/rosotacom/__main__.py +0 -0
  121. {rosotacom_dev-2.5.dev41 → rosotacom_dev-2.5.dev47}/src/rosotacom/anonymize.py +0 -0
  122. {rosotacom_dev-2.5.dev41 → rosotacom_dev-2.5.dev47}/src/rosotacom/bag_ground_truth.py +0 -0
  123. {rosotacom_dev-2.5.dev41 → rosotacom_dev-2.5.dev47}/src/rosotacom/benched_set.py +0 -0
  124. {rosotacom_dev-2.5.dev41 → rosotacom_dev-2.5.dev47}/src/rosotacom/benchmark.py +0 -0
  125. {rosotacom_dev-2.5.dev41 → rosotacom_dev-2.5.dev47}/src/rosotacom/benchmark_delta.py +0 -0
  126. {rosotacom_dev-2.5.dev41 → rosotacom_dev-2.5.dev47}/src/rosotacom/bundle_check.py +0 -0
  127. {rosotacom_dev-2.5.dev41 → rosotacom_dev-2.5.dev47}/src/rosotacom/cli.py +0 -0
  128. {rosotacom_dev-2.5.dev41 → rosotacom_dev-2.5.dev47}/src/rosotacom/deployment.py +0 -0
  129. {rosotacom_dev-2.5.dev41 → rosotacom_dev-2.5.dev47}/src/rosotacom/ffmpeg_packet.py +0 -0
  130. {rosotacom_dev-2.5.dev41 → rosotacom_dev-2.5.dev47}/src/rosotacom/geomap.py +0 -0
  131. {rosotacom_dev-2.5.dev41 → rosotacom_dev-2.5.dev47}/src/rosotacom/image_cache.py +0 -0
  132. {rosotacom_dev-2.5.dev41 → rosotacom_dev-2.5.dev47}/src/rosotacom/network_shaper.py +0 -0
  133. {rosotacom_dev-2.5.dev41 → rosotacom_dev-2.5.dev47}/src/rosotacom/plots.py +0 -0
  134. {rosotacom_dev-2.5.dev41 → rosotacom_dev-2.5.dev47}/src/rosotacom/py.typed +0 -0
  135. {rosotacom_dev-2.5.dev41 → rosotacom_dev-2.5.dev47}/src/rosotacom/resources/benched-set.yaml +0 -0
  136. {rosotacom_dev-2.5.dev41 → rosotacom_dev-2.5.dev47}/src/rosotacom/resources/examples/.gitignore +0 -0
  137. {rosotacom_dev-2.5.dev41 → rosotacom_dev-2.5.dev47}/src/rosotacom/resources/examples/README.md +0 -0
  138. {rosotacom_dev-2.5.dev41 → rosotacom_dev-2.5.dev47}/src/rosotacom/resources/examples/__init__.py +0 -0
  139. {rosotacom_dev-2.5.dev41 → rosotacom_dev-2.5.dev47}/src/rosotacom/resources/examples/bags/8_drop_reference/metadata.yaml +0 -0
  140. {rosotacom_dev-2.5.dev41 → rosotacom_dev-2.5.dev47}/src/rosotacom/resources/examples/deployment.example.yaml +0 -0
  141. {rosotacom_dev-2.5.dev41 → rosotacom_dev-2.5.dev47}/src/rosotacom/resources/examples/ros2docker.json +0 -0
  142. {rosotacom_dev-2.5.dev41 → rosotacom_dev-2.5.dev47}/src/rosotacom/resources/examples/rosotacom.yaml +0 -0
  143. {rosotacom_dev-2.5.dev41 → rosotacom_dev-2.5.dev47}/src/rosotacom/resources/examples/scenarios/2_native_chatter/scenario-definition.yaml +0 -0
  144. {rosotacom_dev-2.5.dev41 → rosotacom_dev-2.5.dev47}/src/rosotacom/resources/examples/scripts/1_heartbeat/run_machine_a.sh +0 -0
  145. {rosotacom_dev-2.5.dev41 → rosotacom_dev-2.5.dev47}/src/rosotacom/resources/examples/scripts/1_heartbeat/run_machine_b.sh +0 -0
  146. {rosotacom_dev-2.5.dev41 → rosotacom_dev-2.5.dev47}/src/rosotacom/resources/examples/scripts/2_native_chatter/machine_a/external.ros2docker.json +0 -0
  147. {rosotacom_dev-2.5.dev41 → rosotacom_dev-2.5.dev47}/src/rosotacom/resources/examples/scripts/2_native_chatter/machine_a/run_communication.sh +0 -0
  148. {rosotacom_dev-2.5.dev41 → rosotacom_dev-2.5.dev47}/src/rosotacom/resources/examples/scripts/2_native_chatter/machine_a/run_external.py +0 -0
  149. {rosotacom_dev-2.5.dev41 → rosotacom_dev-2.5.dev47}/src/rosotacom/resources/examples/scripts/2_native_chatter/machine_a/wait_for_echo_topic.sh +0 -0
  150. {rosotacom_dev-2.5.dev41 → rosotacom_dev-2.5.dev47}/src/rosotacom/resources/examples/scripts/2_native_chatter/machine_b/external.ros2docker.json +0 -0
  151. {rosotacom_dev-2.5.dev41 → rosotacom_dev-2.5.dev47}/src/rosotacom/resources/examples/scripts/2_native_chatter/machine_b/run_communication.sh +0 -0
  152. {rosotacom_dev-2.5.dev41 → rosotacom_dev-2.5.dev47}/src/rosotacom/resources/examples/scripts/2_native_chatter/machine_b/run_external.py +0 -0
  153. {rosotacom_dev-2.5.dev41 → rosotacom_dev-2.5.dev47}/src/rosotacom/resources/examples/scripts/3_comp_occ_grid/machine_a/external.ros2docker.json +0 -0
  154. {rosotacom_dev-2.5.dev41 → rosotacom_dev-2.5.dev47}/src/rosotacom/resources/examples/scripts/3_comp_occ_grid/machine_a/external_session.yaml +0 -0
  155. {rosotacom_dev-2.5.dev41 → rosotacom_dev-2.5.dev47}/src/rosotacom/resources/examples/scripts/3_comp_occ_grid/machine_a/foxglove.json +0 -0
  156. {rosotacom_dev-2.5.dev41 → rosotacom_dev-2.5.dev47}/src/rosotacom/resources/examples/scripts/3_comp_occ_grid/machine_a/run_communication.sh +0 -0
  157. {rosotacom_dev-2.5.dev41 → rosotacom_dev-2.5.dev47}/src/rosotacom/resources/examples/scripts/3_comp_occ_grid/machine_a/run_external.py +0 -0
  158. {rosotacom_dev-2.5.dev41 → rosotacom_dev-2.5.dev47}/src/rosotacom/resources/examples/scripts/3_comp_occ_grid/machine_b/costmap_1second/costmap_1second_0.mcap +0 -0
  159. {rosotacom_dev-2.5.dev41 → rosotacom_dev-2.5.dev47}/src/rosotacom/resources/examples/scripts/3_comp_occ_grid/machine_b/costmap_1second/metadata.yaml +0 -0
  160. {rosotacom_dev-2.5.dev41 → rosotacom_dev-2.5.dev47}/src/rosotacom/resources/examples/scripts/3_comp_occ_grid/machine_b/external.ros2docker.json +0 -0
  161. {rosotacom_dev-2.5.dev41 → rosotacom_dev-2.5.dev47}/src/rosotacom/resources/examples/scripts/3_comp_occ_grid/machine_b/run_communication.sh +0 -0
  162. {rosotacom_dev-2.5.dev41 → rosotacom_dev-2.5.dev47}/src/rosotacom/resources/examples/scripts/3_comp_occ_grid/machine_b/run_external.py +0 -0
  163. {rosotacom_dev-2.5.dev41 → rosotacom_dev-2.5.dev47}/src/rosotacom/resources/examples/scripts/4_comp_occ_grid_zen/machine_a/external.ros2docker.json +0 -0
  164. {rosotacom_dev-2.5.dev41 → rosotacom_dev-2.5.dev47}/src/rosotacom/resources/examples/scripts/4_comp_occ_grid_zen/machine_a/external_session.yaml +0 -0
  165. {rosotacom_dev-2.5.dev41 → rosotacom_dev-2.5.dev47}/src/rosotacom/resources/examples/scripts/4_comp_occ_grid_zen/machine_a/foxglove.json +0 -0
  166. {rosotacom_dev-2.5.dev41 → rosotacom_dev-2.5.dev47}/src/rosotacom/resources/examples/scripts/4_comp_occ_grid_zen/machine_a/run_communication.sh +0 -0
  167. {rosotacom_dev-2.5.dev41 → rosotacom_dev-2.5.dev47}/src/rosotacom/resources/examples/scripts/4_comp_occ_grid_zen/machine_a/run_external.py +0 -0
  168. {rosotacom_dev-2.5.dev41 → rosotacom_dev-2.5.dev47}/src/rosotacom/resources/examples/scripts/4_comp_occ_grid_zen/machine_b/costmap_1second/costmap_1second_0.mcap +0 -0
  169. {rosotacom_dev-2.5.dev41 → rosotacom_dev-2.5.dev47}/src/rosotacom/resources/examples/scripts/4_comp_occ_grid_zen/machine_b/costmap_1second/metadata.yaml +0 -0
  170. {rosotacom_dev-2.5.dev41 → rosotacom_dev-2.5.dev47}/src/rosotacom/resources/examples/scripts/4_comp_occ_grid_zen/machine_b/external.ros2docker.json +0 -0
  171. {rosotacom_dev-2.5.dev41 → rosotacom_dev-2.5.dev47}/src/rosotacom/resources/examples/scripts/4_comp_occ_grid_zen/machine_b/run_communication.sh +0 -0
  172. {rosotacom_dev-2.5.dev41 → rosotacom_dev-2.5.dev47}/src/rosotacom/resources/examples/scripts/4_comp_occ_grid_zen/machine_b/run_external.py +0 -0
  173. {rosotacom_dev-2.5.dev41 → rosotacom_dev-2.5.dev47}/src/rosotacom/resources/examples/scripts/5_sized_payload/machine_a/external.ros2docker.json +0 -0
  174. {rosotacom_dev-2.5.dev41 → rosotacom_dev-2.5.dev47}/src/rosotacom/resources/examples/scripts/5_sized_payload/machine_a/external_session.yaml +0 -0
  175. {rosotacom_dev-2.5.dev41 → rosotacom_dev-2.5.dev47}/src/rosotacom/resources/examples/scripts/5_sized_payload/machine_a/run_communication.sh +0 -0
  176. {rosotacom_dev-2.5.dev41 → rosotacom_dev-2.5.dev47}/src/rosotacom/resources/examples/scripts/5_sized_payload/machine_a/run_external.py +0 -0
  177. {rosotacom_dev-2.5.dev41 → rosotacom_dev-2.5.dev47}/src/rosotacom/resources/examples/scripts/5_sized_payload/machine_b/external.ros2docker.json +0 -0
  178. {rosotacom_dev-2.5.dev41 → rosotacom_dev-2.5.dev47}/src/rosotacom/resources/examples/scripts/5_sized_payload/machine_b/external_session.yaml +0 -0
  179. {rosotacom_dev-2.5.dev41 → rosotacom_dev-2.5.dev47}/src/rosotacom/resources/examples/scripts/5_sized_payload/machine_b/run_communication.sh +0 -0
  180. {rosotacom_dev-2.5.dev41 → rosotacom_dev-2.5.dev47}/src/rosotacom/resources/examples/scripts/5_sized_payload/machine_b/run_external.py +0 -0
  181. {rosotacom_dev-2.5.dev41 → rosotacom_dev-2.5.dev47}/src/rosotacom/resources/examples/scripts/6_sized_payload_zen/machine_a/external.ros2docker.json +0 -0
  182. {rosotacom_dev-2.5.dev41 → rosotacom_dev-2.5.dev47}/src/rosotacom/resources/examples/scripts/6_sized_payload_zen/machine_a/external_session.yaml +0 -0
  183. {rosotacom_dev-2.5.dev41 → rosotacom_dev-2.5.dev47}/src/rosotacom/resources/examples/scripts/6_sized_payload_zen/machine_a/run_communication.sh +0 -0
  184. {rosotacom_dev-2.5.dev41 → rosotacom_dev-2.5.dev47}/src/rosotacom/resources/examples/scripts/6_sized_payload_zen/machine_a/run_external.py +0 -0
  185. {rosotacom_dev-2.5.dev41 → rosotacom_dev-2.5.dev47}/src/rosotacom/resources/examples/scripts/6_sized_payload_zen/machine_b/external.ros2docker.json +0 -0
  186. {rosotacom_dev-2.5.dev41 → rosotacom_dev-2.5.dev47}/src/rosotacom/resources/examples/scripts/6_sized_payload_zen/machine_b/external_session.yaml +0 -0
  187. {rosotacom_dev-2.5.dev41 → rosotacom_dev-2.5.dev47}/src/rosotacom/resources/examples/scripts/6_sized_payload_zen/machine_b/run_communication.sh +0 -0
  188. {rosotacom_dev-2.5.dev41 → rosotacom_dev-2.5.dev47}/src/rosotacom/resources/examples/scripts/6_sized_payload_zen/machine_b/run_external.py +0 -0
  189. {rosotacom_dev-2.5.dev41 → rosotacom_dev-2.5.dev47}/src/rosotacom/resources/examples/sessions/10_restamp/session-definition.yaml +0 -0
  190. {rosotacom_dev-2.5.dev41 → rosotacom_dev-2.5.dev47}/src/rosotacom/resources/examples/sessions/11_trickle/session-definition.yaml +0 -0
  191. {rosotacom_dev-2.5.dev41 → rosotacom_dev-2.5.dev47}/src/rosotacom/resources/examples/sessions/12_content_integrity/session-definition.yaml +0 -0
  192. {rosotacom_dev-2.5.dev41 → rosotacom_dev-2.5.dev47}/src/rosotacom/resources/examples/sessions/13_link_latency/session-definition.yaml +0 -0
  193. {rosotacom_dev-2.5.dev41 → rosotacom_dev-2.5.dev47}/src/rosotacom/resources/examples/sessions/14_remote_assist_anonymized/session-definition.yaml +0 -0
  194. {rosotacom_dev-2.5.dev41 → rosotacom_dev-2.5.dev47}/src/rosotacom/resources/examples/sessions/15_remote_assist_anonymized_costmap/session-definition.yaml +0 -0
  195. {rosotacom_dev-2.5.dev41 → rosotacom_dev-2.5.dev47}/src/rosotacom/resources/examples/sessions/16_remote_assist_anonymized_camera/session-definition.yaml +0 -0
  196. {rosotacom_dev-2.5.dev41 → rosotacom_dev-2.5.dev47}/src/rosotacom/resources/examples/sessions/17_synthetic_camera_quality/session-definition.yaml +0 -0
  197. {rosotacom_dev-2.5.dev41 → rosotacom_dev-2.5.dev47}/src/rosotacom/resources/examples/sessions/1_heartbeat/session-definition.yaml +0 -0
  198. {rosotacom_dev-2.5.dev41 → rosotacom_dev-2.5.dev47}/src/rosotacom/resources/examples/sessions/1_heartbeat_status/session-definition.yaml +0 -0
  199. {rosotacom_dev-2.5.dev41 → rosotacom_dev-2.5.dev47}/src/rosotacom/resources/examples/sessions/2_native_chatter/session-definition.yaml +0 -0
  200. {rosotacom_dev-2.5.dev41 → rosotacom_dev-2.5.dev47}/src/rosotacom/resources/examples/sessions/3_comp_occ_grid/session-definition.yaml +0 -0
  201. {rosotacom_dev-2.5.dev41 → rosotacom_dev-2.5.dev47}/src/rosotacom/resources/examples/sessions/4_comp_occ_grid_zen/session-definition.yaml +0 -0
  202. {rosotacom_dev-2.5.dev41 → rosotacom_dev-2.5.dev47}/src/rosotacom/resources/examples/sessions/5_sized_payload/session-definition.yaml +0 -0
  203. {rosotacom_dev-2.5.dev41 → rosotacom_dev-2.5.dev47}/src/rosotacom/resources/examples/sessions/6_sized_payload_zen/session-definition.yaml +0 -0
  204. {rosotacom_dev-2.5.dev41 → rosotacom_dev-2.5.dev47}/src/rosotacom/resources/examples/sessions/7_latched_static/session-definition.yaml +0 -0
  205. {rosotacom_dev-2.5.dev41 → rosotacom_dev-2.5.dev47}/src/rosotacom/resources/examples/sessions/8_drop/session-definition.yaml +0 -0
  206. {rosotacom_dev-2.5.dev41 → rosotacom_dev-2.5.dev47}/src/rosotacom/resources/examples/sessions/9_throttle/session-definition.yaml +0 -0
  207. {rosotacom_dev-2.5.dev41 → rosotacom_dev-2.5.dev47}/src/rosotacom/resources/examples/sessions/bench_1_1_capacity/session-definition.yaml +0 -0
  208. {rosotacom_dev-2.5.dev41 → rosotacom_dev-2.5.dev47}/src/rosotacom/resources/examples/sessions/bench_1_2_load_sweep/session-definition.yaml +0 -0
  209. {rosotacom_dev-2.5.dev41 → rosotacom_dev-2.5.dev47}/src/rosotacom/resources/examples/sessions/bench_1_3_ramp/session-definition.yaml +0 -0
  210. {rosotacom_dev-2.5.dev41 → rosotacom_dev-2.5.dev47}/src/rosotacom/resources/examples/sessions/bench_1_4_recovery/session-definition.yaml +0 -0
  211. {rosotacom_dev-2.5.dev41 → rosotacom_dev-2.5.dev47}/src/rosotacom/resources/examples/sessions/bench_1_5_camera/session-definition.yaml +0 -0
  212. {rosotacom_dev-2.5.dev41 → rosotacom_dev-2.5.dev47}/src/rosotacom/resources/ros2docker.json.example +0 -0
  213. {rosotacom_dev-2.5.dev41 → rosotacom_dev-2.5.dev47}/src/rosotacom/resources/ws/.vscode/c_cpp_properties.json +0 -0
  214. {rosotacom_dev-2.5.dev41 → rosotacom_dev-2.5.dev47}/src/rosotacom/resources/ws/.vscode/settings.json +0 -0
  215. {rosotacom_dev-2.5.dev41 → rosotacom_dev-2.5.dev47}/src/rosotacom/resources/ws/ota_configs/archive/fastdds_easy_mode.xml.template +0 -0
  216. {rosotacom_dev-2.5.dev41 → rosotacom_dev-2.5.dev47}/src/rosotacom/resources/ws/ota_configs/archive/fastdds_initial_peer_list.xml.template +0 -0
  217. {rosotacom_dev-2.5.dev41 → rosotacom_dev-2.5.dev47}/src/rosotacom/resources/ws/ota_configs/archive/fastdds_v2.xml.template +0 -0
  218. {rosotacom_dev-2.5.dev41 → rosotacom_dev-2.5.dev47}/src/rosotacom/resources/ws/ota_configs/archive/fastdds_v3.xml.template +0 -0
  219. {rosotacom_dev-2.5.dev41 → rosotacom_dev-2.5.dev47}/src/rosotacom/resources/ws/ota_configs/archive/fastdds_v4.xml.template +0 -0
  220. {rosotacom_dev-2.5.dev41 → rosotacom_dev-2.5.dev47}/src/rosotacom/resources/ws/ota_configs/archive/fastdds_v5.xml.template +0 -0
  221. {rosotacom_dev-2.5.dev41 → rosotacom_dev-2.5.dev47}/src/rosotacom/resources/ws/ota_configs/create_zenoh_json5.py +0 -0
  222. {rosotacom_dev-2.5.dev41 → rosotacom_dev-2.5.dev47}/src/rosotacom/resources/ws/ota_configs/cyclonedds_local_participants.xml.template +0 -0
  223. {rosotacom_dev-2.5.dev41 → rosotacom_dev-2.5.dev47}/src/rosotacom/resources/ws/ota_configs/cyclonedds_minimal.xml.template +0 -0
  224. {rosotacom_dev-2.5.dev41 → rosotacom_dev-2.5.dev47}/src/rosotacom/resources/ws/ota_configs/cyclonedds_tuned.xml.template +0 -0
  225. {rosotacom_dev-2.5.dev41 → rosotacom_dev-2.5.dev47}/src/rosotacom/resources/ws/ota_configs/fastdds_unicast.xml.template +0 -0
  226. {rosotacom_dev-2.5.dev41 → rosotacom_dev-2.5.dev47}/src/rosotacom/resources/ws/ota_configs/get_ota_xml.py +0 -0
  227. {rosotacom_dev-2.5.dev41 → rosotacom_dev-2.5.dev47}/src/rosotacom/resources/ws/ota_configs/utils.py +0 -0
  228. {rosotacom_dev-2.5.dev41 → rosotacom_dev-2.5.dev47}/src/rosotacom/resources/ws/ota_configs/zenoh.json5.template +0 -0
  229. {rosotacom_dev-2.5.dev41 → rosotacom_dev-2.5.dev47}/src/rosotacom/resources/ws/ros2src/.gitkeep +0 -0
  230. {rosotacom_dev-2.5.dev41 → rosotacom_dev-2.5.dev47}/src/rosotacom/resources/ws/ros2src/com_cpp/CMakeLists.txt +0 -0
  231. {rosotacom_dev-2.5.dev41 → rosotacom_dev-2.5.dev47}/src/rosotacom/resources/ws/ros2src/com_cpp/COLCON_IGNORE +0 -0
  232. {rosotacom_dev-2.5.dev41 → rosotacom_dev-2.5.dev47}/src/rosotacom/resources/ws/ros2src/com_cpp/LICENSE.txt +0 -0
  233. {rosotacom_dev-2.5.dev41 → rosotacom_dev-2.5.dev47}/src/rosotacom/resources/ws/ros2src/com_cpp/package.xml +0 -0
  234. {rosotacom_dev-2.5.dev41 → rosotacom_dev-2.5.dev47}/src/rosotacom/resources/ws/ros2src/com_cpp/src/hz_monitor.cpp +0 -0
  235. {rosotacom_dev-2.5.dev41 → rosotacom_dev-2.5.dev47}/src/rosotacom/resources/ws/ros2src/com_cpp/src/universal_compressor.cpp +0 -0
  236. {rosotacom_dev-2.5.dev41 → rosotacom_dev-2.5.dev47}/src/rosotacom/resources/ws/ros2src/com_cpp/src/universal_decompressor.cpp +0 -0
  237. {rosotacom_dev-2.5.dev41 → rosotacom_dev-2.5.dev47}/src/rosotacom/resources/ws/ros2src/com_msgs/CMakeLists.txt +0 -0
  238. {rosotacom_dev-2.5.dev41 → rosotacom_dev-2.5.dev47}/src/rosotacom/resources/ws/ros2src/com_msgs/LICENSE.txt +0 -0
  239. {rosotacom_dev-2.5.dev41 → rosotacom_dev-2.5.dev47}/src/rosotacom/resources/ws/ros2src/com_msgs/msg/CompressedData.msg +0 -0
  240. {rosotacom_dev-2.5.dev41 → rosotacom_dev-2.5.dev47}/src/rosotacom/resources/ws/ros2src/com_msgs/msg/EchoHeartbeat.msg +0 -0
  241. {rosotacom_dev-2.5.dev41 → rosotacom_dev-2.5.dev47}/src/rosotacom/resources/ws/ros2src/com_msgs/msg/OtaStamped.msg +0 -0
  242. {rosotacom_dev-2.5.dev41 → rosotacom_dev-2.5.dev47}/src/rosotacom/resources/ws/ros2src/com_msgs/msg/SizedPayload.msg +0 -0
  243. {rosotacom_dev-2.5.dev41 → rosotacom_dev-2.5.dev47}/src/rosotacom/resources/ws/ros2src/com_msgs/package.xml +0 -0
  244. {rosotacom_dev-2.5.dev41 → rosotacom_dev-2.5.dev47}/src/rosotacom/resources/ws/ros2src/com_py/LICENSE.txt +0 -0
  245. {rosotacom_dev-2.5.dev41 → rosotacom_dev-2.5.dev47}/src/rosotacom/resources/ws/ros2src/com_py/com_py/__init__.py +0 -0
  246. {rosotacom_dev-2.5.dev41 → rosotacom_dev-2.5.dev47}/src/rosotacom/resources/ws/ros2src/com_py/com_py/base_bridge_relay.py +0 -0
  247. {rosotacom_dev-2.5.dev41 → rosotacom_dev-2.5.dev47}/src/rosotacom/resources/ws/ros2src/com_py/com_py/bridge_in.py +0 -0
  248. {rosotacom_dev-2.5.dev41 → rosotacom_dev-2.5.dev47}/src/rosotacom/resources/ws/ros2src/com_py/com_py/bridge_out.py +0 -0
  249. {rosotacom_dev-2.5.dev41 → rosotacom_dev-2.5.dev47}/src/rosotacom/resources/ws/ros2src/com_py/com_py/heartbeat_echo.py +0 -0
  250. {rosotacom_dev-2.5.dev41 → rosotacom_dev-2.5.dev47}/src/rosotacom/resources/ws/ros2src/com_py/com_py/hz_monitor.py +0 -0
  251. {rosotacom_dev-2.5.dev41 → rosotacom_dev-2.5.dev47}/src/rosotacom/resources/ws/ros2src/com_py/com_py/image_bandwidth_reducer.py +0 -0
  252. {rosotacom_dev-2.5.dev41 → rosotacom_dev-2.5.dev47}/src/rosotacom/resources/ws/ros2src/com_py/com_py/image_pixel_capper.py +0 -0
  253. {rosotacom_dev-2.5.dev41 → rosotacom_dev-2.5.dev47}/src/rosotacom/resources/ws/ros2src/com_py/com_py/lanelet_saver.py +0 -0
  254. {rosotacom_dev-2.5.dev41 → rosotacom_dev-2.5.dev47}/src/rosotacom/resources/ws/ros2src/com_py/com_py/latch_relay.py +0 -0
  255. {rosotacom_dev-2.5.dev41 → rosotacom_dev-2.5.dev47}/src/rosotacom/resources/ws/ros2src/com_py/com_py/legacy/costmap_remapper.py +0 -0
  256. {rosotacom_dev-2.5.dev41 → rosotacom_dev-2.5.dev47}/src/rosotacom/resources/ws/ros2src/com_py/com_py/legacy/marker_remapper.py +0 -0
  257. {rosotacom_dev-2.5.dev41 → rosotacom_dev-2.5.dev47}/src/rosotacom/resources/ws/ros2src/com_py/com_py/legacy/speed_test.py +0 -0
  258. {rosotacom_dev-2.5.dev41 → rosotacom_dev-2.5.dev47}/src/rosotacom/resources/ws/ros2src/com_py/com_py/legacy/tf_remapper.py +0 -0
  259. {rosotacom_dev-2.5.dev41 → rosotacom_dev-2.5.dev47}/src/rosotacom/resources/ws/ros2src/com_py/com_py/legacy/traffic_monitor.py +0 -0
  260. {rosotacom_dev-2.5.dev41 → rosotacom_dev-2.5.dev47}/src/rosotacom/resources/ws/ros2src/com_py/com_py/legacy/wifi_network_analysis_1.py +0 -0
  261. {rosotacom_dev-2.5.dev41 → rosotacom_dev-2.5.dev47}/src/rosotacom/resources/ws/ros2src/com_py/com_py/link_bytes.py +0 -0
  262. {rosotacom_dev-2.5.dev41 → rosotacom_dev-2.5.dev47}/src/rosotacom/resources/ws/ros2src/com_py/com_py/link_trace.py +0 -0
  263. {rosotacom_dev-2.5.dev41 → rosotacom_dev-2.5.dev47}/src/rosotacom/resources/ws/ros2src/com_py/com_py/local_global_frame_bridge.py +0 -0
  264. {rosotacom_dev-2.5.dev41 → rosotacom_dev-2.5.dev47}/src/rosotacom/resources/ws/ros2src/com_py/com_py/localization_map_split_reduce_saver.py +0 -0
  265. {rosotacom_dev-2.5.dev41 → rosotacom_dev-2.5.dev47}/src/rosotacom/resources/ws/ros2src/com_py/com_py/msg_transform.py +0 -0
  266. {rosotacom_dev-2.5.dev41 → rosotacom_dev-2.5.dev47}/src/rosotacom/resources/ws/ros2src/com_py/com_py/pair_management.py +0 -0
  267. {rosotacom_dev-2.5.dev41 → rosotacom_dev-2.5.dev47}/src/rosotacom/resources/ws/ros2src/com_py/com_py/pub_sub_pair.py +0 -0
  268. {rosotacom_dev-2.5.dev41 → rosotacom_dev-2.5.dev47}/src/rosotacom/resources/ws/ros2src/com_py/com_py/qos.py +0 -0
  269. {rosotacom_dev-2.5.dev41 → rosotacom_dev-2.5.dev47}/src/rosotacom/resources/ws/ros2src/com_py/com_py/relay_in.py +0 -0
  270. {rosotacom_dev-2.5.dev41 → rosotacom_dev-2.5.dev47}/src/rosotacom/resources/ws/ros2src/com_py/com_py/relay_out.py +0 -0
  271. {rosotacom_dev-2.5.dev41 → rosotacom_dev-2.5.dev47}/src/rosotacom/resources/ws/ros2src/com_py/com_py/restamp.py +0 -0
  272. {rosotacom_dev-2.5.dev41 → rosotacom_dev-2.5.dev47}/src/rosotacom/resources/ws/ros2src/com_py/com_py/sized_publisher.py +0 -0
  273. {rosotacom_dev-2.5.dev41 → rosotacom_dev-2.5.dev47}/src/rosotacom/resources/ws/ros2src/com_py/com_py/stage_latency.py +0 -0
  274. {rosotacom_dev-2.5.dev41 → rosotacom_dev-2.5.dev47}/src/rosotacom/resources/ws/ros2src/com_py/com_py/stage_latency_core.py +0 -0
  275. {rosotacom_dev-2.5.dev41 → rosotacom_dev-2.5.dev47}/src/rosotacom/resources/ws/ros2src/com_py/com_py/tf_map_to_cart_saver.py +0 -0
  276. {rosotacom_dev-2.5.dev41 → rosotacom_dev-2.5.dev47}/src/rosotacom/resources/ws/ros2src/com_py/com_py/topic_monitor.py +0 -0
  277. {rosotacom_dev-2.5.dev41 → rosotacom_dev-2.5.dev47}/src/rosotacom/resources/ws/ros2src/com_py/com_py/topic_resolution.py +0 -0
  278. {rosotacom_dev-2.5.dev41 → rosotacom_dev-2.5.dev47}/src/rosotacom/resources/ws/ros2src/com_py/com_py/trickle.py +0 -0
  279. {rosotacom_dev-2.5.dev41 → rosotacom_dev-2.5.dev47}/src/rosotacom/resources/ws/ros2src/com_py/com_py/universal_compressor.py +0 -0
  280. {rosotacom_dev-2.5.dev41 → rosotacom_dev-2.5.dev47}/src/rosotacom/resources/ws/ros2src/com_py/com_py/universal_decompressor.py +0 -0
  281. {rosotacom_dev-2.5.dev41 → rosotacom_dev-2.5.dev47}/src/rosotacom/resources/ws/ros2src/com_py/com_py/universal_ota_unwrapper.py +0 -0
  282. {rosotacom_dev-2.5.dev41 → rosotacom_dev-2.5.dev47}/src/rosotacom/resources/ws/ros2src/com_py/com_py/universal_ota_wrapper.py +0 -0
  283. {rosotacom_dev-2.5.dev41 → rosotacom_dev-2.5.dev47}/src/rosotacom/resources/ws/ros2src/com_py/package.xml +0 -0
  284. {rosotacom_dev-2.5.dev41 → rosotacom_dev-2.5.dev47}/src/rosotacom/resources/ws/ros2src/com_py/resource/com_py +0 -0
  285. {rosotacom_dev-2.5.dev41 → rosotacom_dev-2.5.dev47}/src/rosotacom/resources/ws/ros2src/com_py/setup.cfg +0 -0
  286. {rosotacom_dev-2.5.dev41 → rosotacom_dev-2.5.dev47}/src/rosotacom/resources/ws/ros2src/com_py/test/test_copyright.py +0 -0
  287. {rosotacom_dev-2.5.dev41 → rosotacom_dev-2.5.dev47}/src/rosotacom/resources/ws/ros2src/com_py/test/test_flake8.py +0 -0
  288. {rosotacom_dev-2.5.dev41 → rosotacom_dev-2.5.dev47}/src/rosotacom/resources/ws/ros2src/com_py/test/test_pep257.py +0 -0
  289. {rosotacom_dev-2.5.dev41 → rosotacom_dev-2.5.dev47}/src/rosotacom/resources/ws/session/content/base/session_plugin_base.yaml +0 -0
  290. {rosotacom_dev-2.5.dev41 → rosotacom_dev-2.5.dev47}/src/rosotacom/resources/ws/session/content/network/up_connectivity.py +0 -0
  291. {rosotacom_dev-2.5.dev41 → rosotacom_dev-2.5.dev47}/src/rosotacom/resources/ws/session/content/network/wifi_network_analysis_2.py +0 -0
  292. {rosotacom_dev-2.5.dev41 → rosotacom_dev-2.5.dev47}/src/rosotacom/resources/ws/session/creation/anonymize_bag.py +0 -0
  293. {rosotacom_dev-2.5.dev41 → rosotacom_dev-2.5.dev47}/src/rosotacom/resources/ws/session/creation/catmux_log_setup.sh +0 -0
  294. {rosotacom_dev-2.5.dev41 → rosotacom_dev-2.5.dev47}/src/rosotacom/resources/ws/session/creation/create_session_yaml.py +0 -0
  295. {rosotacom_dev-2.5.dev41 → rosotacom_dev-2.5.dev47}/src/rosotacom/resources/ws/session/creation/generate_session_files.py +0 -0
  296. {rosotacom_dev-2.5.dev41 → rosotacom_dev-2.5.dev47}/src/rosotacom/resources/ws/session/creation/run_session.py +0 -0
  297. {rosotacom_dev-2.5.dev41 → rosotacom_dev-2.5.dev47}/src/rosotacom/resources/ws/session/creation/run_session_in_external_terminal.py +0 -0
  298. {rosotacom_dev-2.5.dev41 → rosotacom_dev-2.5.dev47}/src/rosotacom/resources/ws/session/creation/strip_ansi.py +0 -0
  299. {rosotacom_dev-2.5.dev41 → rosotacom_dev-2.5.dev47}/src/rosotacom/status_eval.py +0 -0
  300. {rosotacom_dev-2.5.dev41 → rosotacom_dev-2.5.dev47}/src/rosotacom/trace_profiles.py +0 -0
  301. {rosotacom_dev-2.5.dev41 → rosotacom_dev-2.5.dev47}/src/rosotacom/transit.py +0 -0
  302. {rosotacom_dev-2.5.dev41 → rosotacom_dev-2.5.dev47}/src/rosotacom/video_quality.py +0 -0
  303. {rosotacom_dev-2.5.dev41 → rosotacom_dev-2.5.dev47}/src/rosotacom_dev.egg-info/dependency_links.txt +0 -0
  304. {rosotacom_dev-2.5.dev41 → rosotacom_dev-2.5.dev47}/src/rosotacom_dev.egg-info/entry_points.txt +0 -0
  305. {rosotacom_dev-2.5.dev41 → rosotacom_dev-2.5.dev47}/src/rosotacom_dev.egg-info/requires.txt +0 -0
  306. {rosotacom_dev-2.5.dev41 → rosotacom_dev-2.5.dev47}/src/rosotacom_dev.egg-info/top_level.txt +0 -0
  307. {rosotacom_dev-2.5.dev41 → rosotacom_dev-2.5.dev47}/terminology.md +0 -0
  308. {rosotacom_dev-2.5.dev41 → rosotacom_dev-2.5.dev47}/tests/contract/test_benched_set_registry.py +0 -0
  309. {rosotacom_dev-2.5.dev41 → rosotacom_dev-2.5.dev47}/tests/contract/test_findings.py +0 -0
  310. {rosotacom_dev-2.5.dev41 → rosotacom_dev-2.5.dev47}/tests/contract/test_markdown_links.py +0 -0
  311. {rosotacom_dev-2.5.dev41 → rosotacom_dev-2.5.dev47}/tests/contract/test_ota_source_bundle.py +0 -0
  312. {rosotacom_dev-2.5.dev41 → rosotacom_dev-2.5.dev47}/tests/contract/test_package_metadata.py +0 -0
  313. {rosotacom_dev-2.5.dev41 → rosotacom_dev-2.5.dev47}/tests/contract/test_packaged_resources.py +0 -0
  314. {rosotacom_dev-2.5.dev41 → rosotacom_dev-2.5.dev47}/tests/contract/test_profile_covers_generated_commands.py +0 -0
  315. {rosotacom_dev-2.5.dev41 → rosotacom_dev-2.5.dev47}/tests/contract/test_pytest_policy.py +0 -0
  316. {rosotacom_dev-2.5.dev41 → rosotacom_dev-2.5.dev47}/tests/contract/test_readme_examples.py +0 -0
  317. {rosotacom_dev-2.5.dev41 → rosotacom_dev-2.5.dev47}/tests/contract/test_security_maintenance_config.py +0 -0
  318. {rosotacom_dev-2.5.dev41 → rosotacom_dev-2.5.dev47}/tests/contract/test_session_configuration_doc.py +0 -0
  319. {rosotacom_dev-2.5.dev41 → rosotacom_dev-2.5.dev47}/tests/contract/test_session_template_substitution.py +0 -0
  320. {rosotacom_dev-2.5.dev41 → rosotacom_dev-2.5.dev47}/tests/contract/test_workflow_contracts.py +0 -0
  321. {rosotacom_dev-2.5.dev41 → rosotacom_dev-2.5.dev47}/tests/e2e/conftest.py +0 -0
  322. {rosotacom_dev-2.5.dev41 → rosotacom_dev-2.5.dev47}/tests/e2e/test_anonymize_e2e.py +0 -0
  323. {rosotacom_dev-2.5.dev41 → rosotacom_dev-2.5.dev47}/tests/e2e/test_benchmark_ab.py +0 -0
  324. {rosotacom_dev-2.5.dev41 → rosotacom_dev-2.5.dev47}/tests/e2e/test_benchmark_capacity.py +0 -0
  325. {rosotacom_dev-2.5.dev41 → rosotacom_dev-2.5.dev47}/tests/e2e/test_benchmark_replay.py +0 -0
  326. {rosotacom_dev-2.5.dev41 → rosotacom_dev-2.5.dev47}/tests/e2e/test_parallel_smoke.py +0 -0
  327. {rosotacom_dev-2.5.dev41 → rosotacom_dev-2.5.dev47}/tests/e2e/test_smoke.py +0 -0
  328. {rosotacom_dev-2.5.dev41 → rosotacom_dev-2.5.dev47}/tests/e2e/test_timeline_stepping.py +0 -0
  329. {rosotacom_dev-2.5.dev41 → rosotacom_dev-2.5.dev47}/tests/e2e/test_video_quality_e2e.py +0 -0
  330. {rosotacom_dev-2.5.dev41 → rosotacom_dev-2.5.dev47}/tests/sessions/generate_rmw_matrix.py +0 -0
  331. {rosotacom_dev-2.5.dev41 → rosotacom_dev-2.5.dev47}/tests/sessions/rmw_matrix/1_heartbeat_cyclone-local_zenoh-ros2dds-ota/session-definition.yaml +0 -0
  332. {rosotacom_dev-2.5.dev41 → rosotacom_dev-2.5.dev47}/tests/sessions/rmw_matrix/1_heartbeat_cyclone-ota/session-definition.yaml +0 -0
  333. {rosotacom_dev-2.5.dev41 → rosotacom_dev-2.5.dev47}/tests/sessions/rmw_matrix/1_heartbeat_cyclone-ota-tuned/session-definition.yaml +0 -0
  334. {rosotacom_dev-2.5.dev41 → rosotacom_dev-2.5.dev47}/tests/sessions/rmw_matrix/1_heartbeat_fastdds/session-definition.yaml +0 -0
  335. {rosotacom_dev-2.5.dev41 → rosotacom_dev-2.5.dev47}/tests/sessions/rmw_matrix/1_heartbeat_zen-endpoints/session-definition.yaml +0 -0
  336. {rosotacom_dev-2.5.dev41 → rosotacom_dev-2.5.dev47}/tests/unit/test_anonymize.py +0 -0
  337. {rosotacom_dev-2.5.dev41 → rosotacom_dev-2.5.dev47}/tests/unit/test_bag_ground_truth.py +0 -0
  338. {rosotacom_dev-2.5.dev41 → rosotacom_dev-2.5.dev47}/tests/unit/test_benched_set.py +0 -0
  339. {rosotacom_dev-2.5.dev41 → rosotacom_dev-2.5.dev47}/tests/unit/test_benchmark.py +0 -0
  340. {rosotacom_dev-2.5.dev41 → rosotacom_dev-2.5.dev47}/tests/unit/test_benchmark_delta.py +0 -0
  341. {rosotacom_dev-2.5.dev41 → rosotacom_dev-2.5.dev47}/tests/unit/test_bundle_check.py +0 -0
  342. {rosotacom_dev-2.5.dev41 → rosotacom_dev-2.5.dev47}/tests/unit/test_catmux_pane_logging.py +0 -0
  343. {rosotacom_dev-2.5.dev41 → rosotacom_dev-2.5.dev47}/tests/unit/test_cli.py +0 -0
  344. {rosotacom_dev-2.5.dev41 → rosotacom_dev-2.5.dev47}/tests/unit/test_cli_benchmark.py +0 -0
  345. {rosotacom_dev-2.5.dev41 → rosotacom_dev-2.5.dev47}/tests/unit/test_concurrency.py +0 -0
  346. {rosotacom_dev-2.5.dev41 → rosotacom_dev-2.5.dev47}/tests/unit/test_deployment.py +0 -0
  347. {rosotacom_dev-2.5.dev41 → rosotacom_dev-2.5.dev47}/tests/unit/test_ffmpeg_packet.py +0 -0
  348. {rosotacom_dev-2.5.dev41 → rosotacom_dev-2.5.dev47}/tests/unit/test_geomap.py +0 -0
  349. {rosotacom_dev-2.5.dev41 → rosotacom_dev-2.5.dev47}/tests/unit/test_image_cache.py +0 -0
  350. {rosotacom_dev-2.5.dev41 → rosotacom_dev-2.5.dev47}/tests/unit/test_link_bytes.py +0 -0
  351. {rosotacom_dev-2.5.dev41 → rosotacom_dev-2.5.dev47}/tests/unit/test_link_trace.py +0 -0
  352. {rosotacom_dev-2.5.dev41 → rosotacom_dev-2.5.dev47}/tests/unit/test_network_shaper.py +0 -0
  353. {rosotacom_dev-2.5.dev41 → rosotacom_dev-2.5.dev47}/tests/unit/test_ota_unwrapper.py +0 -0
  354. {rosotacom_dev-2.5.dev41 → rosotacom_dev-2.5.dev47}/tests/unit/test_ota_wrapper.py +0 -0
  355. {rosotacom_dev-2.5.dev41 → rosotacom_dev-2.5.dev47}/tests/unit/test_ota_xml.py +0 -0
  356. {rosotacom_dev-2.5.dev41 → rosotacom_dev-2.5.dev47}/tests/unit/test_plots.py +0 -0
  357. {rosotacom_dev-2.5.dev41 → rosotacom_dev-2.5.dev47}/tests/unit/test_qos_fallback_logging.py +0 -0
  358. {rosotacom_dev-2.5.dev41 → rosotacom_dev-2.5.dev47}/tests/unit/test_relay_optional_parameters.py +0 -0
  359. {rosotacom_dev-2.5.dev41 → rosotacom_dev-2.5.dev47}/tests/unit/test_session_pipeline.py +0 -0
  360. {rosotacom_dev-2.5.dev41 → rosotacom_dev-2.5.dev47}/tests/unit/test_stage_latency.py +0 -0
  361. {rosotacom_dev-2.5.dev41 → rosotacom_dev-2.5.dev47}/tests/unit/test_status_eval.py +0 -0
  362. {rosotacom_dev-2.5.dev41 → rosotacom_dev-2.5.dev47}/tests/unit/test_trace_profiles.py +0 -0
  363. {rosotacom_dev-2.5.dev41 → rosotacom_dev-2.5.dev47}/tests/unit/test_transit.py +0 -0
  364. {rosotacom_dev-2.5.dev41 → rosotacom_dev-2.5.dev47}/tests/unit/test_video_quality.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: rosotacom-dev
3
- Version: 2.5.dev41
3
+ Version: 2.5.dev47
4
4
  Summary: Host CLI for ROS OTA communication sessions.
5
5
  Author: develNor
6
6
  License-Expression: BSD-3-Clause
@@ -45,6 +45,18 @@ Grouping a stream into GOPs is then just splitting at keyframes. Note the
45
45
  encoder may emit keyframes *early* (scene cuts), so GOPs can be shorter than
46
46
  `gop_size` — assert spacing `<= gop_size`, not `== gop_size`.
47
47
 
48
+ ## Transit records carry the flag
49
+
50
+ The receiving status overview applies the same parse live: when an inbound
51
+ `OtaStamped` envelope's `msg_type` names an FFMPEGPacket, it walks the wrapped
52
+ CDR bytes to `flags` (`com_py/ffmpeg_flags.py`, a dependency-free mirror of the
53
+ host parser) and writes `keyframe: true/false` on every delivered RFC 0003
54
+ transit row in `events.jsonl`. Synthesized lost rows have no payload and carry
55
+ no such field; a payload the walk cannot parse simply omits it. Downstream,
56
+ `rosotacom report` annotates a stream from these real flags whenever they cover
57
+ more than 90% of its delivered rows (provenance `ffmpeg_flags (transit
58
+ records)`), and only recordings without the field fall back to size bimodality.
59
+
48
60
  ## Automated stream-stage stats
49
61
 
50
62
  `rosotacom stream-stats` applies the same packet parser and GOP grouping to
@@ -69,8 +81,9 @@ Inputs are explicit and repeatable:
69
81
  rosbag2 storage backends fall back to `rosbag2_py` in a ROS environment.
70
82
  - `--events LABEL=PATH:/topic` reads delivered RFC 0003 transit rows from
71
83
  `events.jsonl`, using receiver-side `t_com_in` timestamps and `size_bytes`.
72
- Transit rows do not carry raw CDR flags, so GOP annotation uses the documented
73
- size-bimodality fallback when the keyframe share looks like a real GOP.
84
+ Rows carrying the per-message `keyframe` field annotate GOPs from the real
85
+ flags; older recordings without it use the documented size-bimodality
86
+ fallback when the keyframe share looks like a real GOP.
74
87
 
75
88
  The Markdown output starts with the comparison table:
76
89
 
@@ -105,9 +118,9 @@ averages 43,723 B, while positions 1-4 average 3,738 B, 4,512 B, 4,834 B, and
105
118
 
106
119
  ## Fallback: size bimodality
107
120
 
108
- If `flags` is unavailable (foreign recordings, or bags anonymized before
109
- flag preservation existed), the size distribution still separates the two
110
- classes: delta frames dominate the stream (a GOP of N contributes N−1), so
121
+ If `flags` is unavailable (foreign recordings, bags anonymized before flag
122
+ preservation existed, or `events.jsonl` written before transit rows carried
123
+ `keyframe`), the size distribution still separates the two classes: delta frames dominate the stream (a GOP of N contributes N−1), so
111
124
  the median frame size sits on the delta mode, while keyframes are typically
112
125
  5–100× larger. `rosotacom.ffmpeg_packet.keyframes_by_size` marks frames
113
126
  above `3 × median` as keyframes. This misclassifies only when delta frames
@@ -119,6 +132,12 @@ scene at a very low bitrate) — prefer `flags` whenever present.
119
132
  - `tests/unit/test_ffmpeg_packet.py` — CDR parsing (alignment across
120
133
  odd-length strings), flag semantics, and the size fallback on a synthetic
121
134
  GOP pattern.
135
+ - `tests/unit/test_ffmpeg_flags.py` — the receiver-side keyframe-bit walk in
136
+ `com_py/ffmpeg_flags.py`: agreement with the host parser on hand-built
137
+ packets, and `None` (never an exception) on truncated or foreign bytes.
138
+ - `tests/unit/test_forensics.py` — `rosotacom report` prefers transit-row
139
+ `keyframe` flags over size bimodality, and falls back below the coverage
140
+ gate.
122
141
  - `tests/unit/test_stream_stats.py` — exact stream statistics, per-GOP-position
123
142
  tables from hand-built CDR packets, events-row source loading, comparison
124
143
  table shape, and CLI output writing.
@@ -28,6 +28,7 @@ living verification hook.
28
28
 
29
29
  ## Findings
30
30
 
31
+ - [Best-effort reordering becomes reader-side loss, independent of history depth](reorder-becomes-reader-loss.md) - under pure delay jitter a Cyclone best-effort reader discards every overtaken sample (57% at 100 Hz under 50+-45 ms), delivery stays strictly monotonic, and depth 50 loses the same as depth 1.
31
32
  - [Jitter causes loss while bandwidth shortage builds latency](jitter-loss-bandwidth-latency.md) - 18 KB at 20 Hz separates jitter-induced loss from bandwidth-induced queueing.
32
33
  - [Lighter alternating messages can lose more than steady messages](lighter-message-loss.md) - a 1x18KB+1x0KB pattern lost under a tight emulated profile while the steady 18 KB comparison did not.
33
34
  - [Delay alone was not the loss boundary for the 18 KB at 20 Hz probe](delay-alone-no-loss.md) - pure delay profiles showed no material loss, and the 300 ms delay-only probe delivered all messages.
@@ -0,0 +1,69 @@
1
+ # Best-Effort Reordering Becomes Reader-Side Loss, Independent Of History Depth
2
+
3
+ ## Claim
4
+
5
+ Under delay jitter large enough to reorder packets — with ZERO configured
6
+ loss — a CycloneDDS best-effort reader discards every overtaken sample to
7
+ preserve per-writer order: delivery is strictly monotonic, the discarded
8
+ samples count as loss, and subscription history depth does not recover any of
9
+ it (the drop happens in the reader proxy, before history). Variance is
10
+ therefore a loss mechanism of its own, and it is the one QoS depth cannot
11
+ help against — unlike bundled-publication overwrite, where depth does help.
12
+
13
+ ## Setup
14
+
15
+ - Host pair / topology: two Docker containers on one machine (the repository's
16
+ communication image; any image with `rclpy`, `rmw_cyclonedds_cpp` and `tc`
17
+ works), unicast Cyclone peers (`AllowMulticast=false`, static IPs), one
18
+ `std_msgs/Int64` stream at 100 Hz, publisher history depth 10, best-effort;
19
+ the receiver subscribes the SAME topic twice: depth 1 and depth 50. netem on
20
+ the publisher's egress only, delay+jitter, no loss, no rate limit.
21
+ - rosotacom SHA: measured at `3227daf`; re-runs record theirs.
22
+ - Profile: inline netem args (`delay 50ms 45ms` and `delay 50ms 20ms`), not a
23
+ packaged profile — the experiment is below rosotacom, at the RMW layer.
24
+ - Seed policy: seedless single runs (`n=1` per jitter level); the effect size
25
+ (tens of percent) dwarfs run-to-run noise.
26
+
27
+ ## Evidence
28
+
29
+ Evidence grade: scripted container reproduction, committed next to this file.
30
+
31
+ ```bash
32
+ python3 docs/findings/repro/reorder_drop_repro.py orchestrate --rate 100 --duration 40
33
+ ```
34
+
35
+ 2026-08-18 run (4000 messages sent per scenario, zero configured loss):
36
+
37
+ | netem | depth 1 delivered | depth 50 delivered | inversions | loss of span |
38
+ |---|---|---|---|---|
39
+ | `delay 50ms 45ms` | 1709 | 1727 | 0 | **57%** |
40
+ | `delay 50ms 20ms` | 2626 | 2659 | 0 | **34%** |
41
+
42
+ Three readings, one per column group: delivery is strictly monotonic (zero
43
+ inversions in every subscription), depth 50 loses the same as depth 1 (the
44
+ 0.5–1 pp difference is executor noise, not recovery), and the loss tracks the
45
+ overtake probability — at 100 Hz the inter-message gap is 10 ms, so jitter
46
+ ±45 ms makes "the next message arrives first" the common case.
47
+
48
+ Field context (2026-08-17 CCNG drive): the operational 10 Hz streams have a
49
+ 100 ms gap against ~18 ms steady jitter, so this channel is minor there
50
+ (neighbour-delay overtake proxy: 9.6% around steady losses vs 6.0% baseline).
51
+ It grows quadratically relevant as rates rise or jitter grows — any stream
52
+ whose inter-message gap approaches the link's jitter pays it.
53
+
54
+ Verification: manual: run the command above from a source checkout with
55
+ Docker (build the image via the packaged `ros2docker.json.example` or pass
56
+ `--image`); the script prints and writes `out/reorder_results.json`.
57
+
58
+ ## Status
59
+
60
+ confirmed, 2026-08-18.
61
+
62
+ ## Publication notes
63
+
64
+ This is the mechanism behind the jitter side of
65
+ [jitter-loss-bandwidth-latency.md](jitter-loss-bandwidth-latency.md): the
66
+ jitter boundary is not radio noise but the reader's monotonicity rule. For
67
+ papers: plot delivered share vs jitter/gap ratio, and state explicitly that
68
+ history depth is not a mitigation here — pacing (larger gaps), FEC, or
69
+ reliable QoS are.
@@ -0,0 +1,170 @@
1
+ #!/usr/bin/env python3
2
+ """Does jitter-induced reordering become LOSS at a CycloneDDS best-effort
3
+ reader, and does history depth help?
4
+
5
+ Two containers (rcd image, cyclone RMW). Publisher shapes its egress with
6
+ pure delay+jitter (NO configured loss, no rate limit). Receiver subscribes
7
+ the same topic twice: depth 1 and depth 50.
8
+
9
+ Discriminates two mechanisms:
10
+ - loss seen by depth-50 sub = reader-proxy drop of overtaken samples
11
+ (depth cannot help) (or nothing, if cyclone delivers OOO)
12
+ - extra loss of depth-1 sub = history overwrite from arrival bunching
13
+ (depth DOES help)
14
+ - delivered inversions = out-of-order delivery (would mean no drop)
15
+
16
+ Modes: orchestrate (host) | pub | sub (in container).
17
+ """
18
+
19
+ import argparse
20
+ import json
21
+ import subprocess
22
+ import sys
23
+ import time
24
+ from pathlib import Path
25
+
26
+ IMG = "ros-communication:latest" # any image with rclpy + rmw_cyclonedds + tc (--image overrides)
27
+ NET = "claude_reorder_net"
28
+ TOPIC = "/reorder_probe"
29
+
30
+
31
+ def mode_pub(args):
32
+ import rclpy
33
+ from rclpy.node import Node
34
+ from rclpy.qos import HistoryPolicy, QoSProfile, ReliabilityPolicy
35
+ from std_msgs.msg import Int64
36
+
37
+ rclpy.init()
38
+ node = Node("reorder_pub")
39
+ qos = QoSProfile(reliability=ReliabilityPolicy.BEST_EFFORT, history=HistoryPolicy.KEEP_LAST, depth=10)
40
+ pub = node.create_publisher(Int64, TOPIC, qos)
41
+ time.sleep(5.0) # discovery
42
+ n = int(args.rate * args.duration)
43
+ period = 1.0 / args.rate
44
+ t0 = time.monotonic()
45
+ for i in range(n):
46
+ while time.monotonic() < t0 + i * period:
47
+ time.sleep(0.0005)
48
+ m = Int64()
49
+ m.data = i
50
+ pub.publish(m)
51
+ time.sleep(3.0)
52
+ print(f"published {n}")
53
+ node.destroy_node()
54
+ rclpy.shutdown()
55
+
56
+
57
+ def mode_sub(args):
58
+ import rclpy
59
+ from rclpy.node import Node
60
+ from rclpy.qos import HistoryPolicy, QoSProfile, ReliabilityPolicy
61
+ from std_msgs.msg import Int64
62
+
63
+ rclpy.init()
64
+ node = Node("reorder_sub")
65
+ got = {1: [], 50: []}
66
+
67
+ def make_cb(depth):
68
+ def cb(msg):
69
+ got[depth].append(msg.data)
70
+
71
+ return cb
72
+
73
+ subs = []
74
+ for depth in (1, 50):
75
+ qos = QoSProfile(reliability=ReliabilityPolicy.BEST_EFFORT, history=HistoryPolicy.KEEP_LAST, depth=depth)
76
+ subs.append(node.create_subscription(Int64, TOPIC, make_cb(depth), qos))
77
+ t_end = time.monotonic() + args.duration + 20
78
+ while time.monotonic() < t_end:
79
+ rclpy.spin_once(node, timeout_sec=0.05)
80
+ out = {}
81
+ for depth, seqs in got.items():
82
+ inv = sum(1 for a, b in zip(seqs, seqs[1:], strict=False) if b < a)
83
+ out[f"depth{depth}"] = {
84
+ "received": len(seqs),
85
+ "unique": len(set(seqs)),
86
+ "inversions": inv,
87
+ "first": seqs[0] if seqs else None,
88
+ "last": max(seqs) if seqs else None,
89
+ }
90
+ Path(args.out).write_text(json.dumps(out, indent=2))
91
+ print(json.dumps(out))
92
+ node.destroy_node()
93
+ rclpy.shutdown()
94
+
95
+
96
+ def run(cmd):
97
+ r = subprocess.run(cmd, shell=True, capture_output=True, text=True)
98
+ if r.returncode != 0:
99
+ raise RuntimeError(f"{cmd}\n{r.stderr or r.stdout}")
100
+ return r.stdout
101
+
102
+
103
+ def mode_orchestrate(args):
104
+ global IMG
105
+ IMG = args.image
106
+ me = Path(__file__).resolve()
107
+ outdir = me.parent / "out"
108
+ scenarios = [
109
+ ("jitter45", "tc qdisc replace dev eth0 root netem delay 50ms 45ms"),
110
+ ("jitter20", "tc qdisc replace dev eth0 root netem delay 50ms 20ms"),
111
+ ]
112
+ ip_sub, ip_pub = "10.97.0.2", "10.97.0.3"
113
+ ddsuri = (
114
+ "<CycloneDDS><Domain><General><AllowMulticast>false</AllowMulticast>"
115
+ '</General><Discovery><Peers><Peer address=\\"' + ip_sub + '\\"/>'
116
+ '<Peer address=\\"' + ip_pub + '\\"/></Peers>'
117
+ "<ParticipantIndex>auto</ParticipantIndex></Discovery></Domain></CycloneDDS>"
118
+ )
119
+ subprocess.run(f"docker network rm {NET}", shell=True, capture_output=True)
120
+ run(f"docker network create --subnet 10.97.0.0/24 {NET}")
121
+ results = {}
122
+ try:
123
+ for name, netem in scenarios:
124
+ subprocess.run("docker rm -f claude_reorder_pub claude_reorder_sub", shell=True, capture_output=True)
125
+ env = f'-e RMW_IMPLEMENTATION=rmw_cyclonedds_cpp -e ROS_DOMAIN_ID=77 -e CYCLONEDDS_URI="{ddsuri}"'
126
+ run(
127
+ f"docker run -d --name claude_reorder_sub --network {NET} --ip {ip_sub} {env} "
128
+ f"-v {me.parent}:/w -v {outdir}:/out {IMG} bash -lc "
129
+ f"'python3 /w/{me.name} sub --duration {args.duration} "
130
+ f"--out /out/reorder_{name}.json'"
131
+ )
132
+ time.sleep(2)
133
+ run(
134
+ f"docker run -d --name claude_reorder_pub --network {NET} --ip {ip_pub} "
135
+ f"--cap-add NET_ADMIN {env} -v {me.parent}:/w {IMG} bash -lc "
136
+ f"'{netem} && python3 /w/{me.name} pub --rate {args.rate} "
137
+ f"--duration {args.duration}'"
138
+ )
139
+ run("docker wait claude_reorder_pub")
140
+ run("docker wait claude_reorder_sub")
141
+ data = json.loads((outdir / f"reorder_{name}.json").read_text())
142
+ sent = int(args.rate * args.duration)
143
+ for d in ("depth1", "depth50"):
144
+ span = (data[d]["last"] or 0) - (data[d]["first"] or 0) + 1
145
+ data[d]["loss_pct_of_span"] = round(100 * (1 - data[d]["unique"] / max(span, 1)), 2)
146
+ data["sent"] = sent
147
+ results[name] = data
148
+ print(f"== {name}: {json.dumps(data)}")
149
+ finally:
150
+ subprocess.run("docker rm -f claude_reorder_pub claude_reorder_sub", shell=True, capture_output=True)
151
+ subprocess.run(f"docker network rm {NET}", shell=True, capture_output=True)
152
+ (outdir / "reorder_results.json").write_text(json.dumps(results, indent=2))
153
+
154
+
155
+ def main():
156
+ ap = argparse.ArgumentParser()
157
+ sub = ap.add_subparsers(dest="mode", required=True)
158
+ for m in ("orchestrate", "pub", "sub"):
159
+ p = sub.add_parser(m)
160
+ p.add_argument("--rate", type=float, default=100.0)
161
+ p.add_argument("--duration", type=float, default=40.0)
162
+ p.add_argument("--image", default=IMG)
163
+ if m == "sub":
164
+ p.add_argument("--out", required=True)
165
+ args = ap.parse_args()
166
+ {"pub": mode_pub, "sub": mode_sub, "orchestrate": mode_orchestrate}[args.mode](args)
167
+
168
+
169
+ if __name__ == "__main__":
170
+ sys.exit(main() or 0)
@@ -77,6 +77,22 @@ missing tail after the last record is not judged either — without a sender-sid
77
77
  end marker it is indistinguishable from a normal shutdown (RFC 0003, honest
78
78
  limits).
79
79
 
80
+ A receiver-only view also cannot tell a broken link from a source that simply
81
+ stopped publishing — on the 2026-08-17 CCNG drive, "0 Hz collapses" on the
82
+ planner visualization were the source pausing during manual interventions while
83
+ the link delivered everything offered. When the instance also contains the
84
+ sending peer's own transit records for a stream (`direction: outbound`, stage
85
+ `com_out`), the report bins their `t_wrap` stamps into a per-bin **offered**
86
+ rate on the same publish timeline and each collapse event names its culprit in
87
+ `details.cause`: `source` when every collapsed bin was offered below the same
88
+ threshold — the collapse is fully explained by the source, and the markdown
89
+ line reads "source paused" (0 Hz offered) or "source slowed"; `link` when at
90
+ least one collapsed bin was offered at/above threshold and the link still
91
+ delivered below it; `unknown` without sender-side records. `min_offered_hz` /
92
+ `max_offered_hz` accompany the verdict. Sender-side rows feed only the offered
93
+ rate — they never enter the delivered view, where the cross-peer join would
94
+ upgrade receiver-inferred lost rows to delivered.
95
+
80
96
  Events across streams and kinds are grouped into **incidents** when their
81
97
  windows touch (merge gap defaults to one bin): one degradation moment, several
82
98
  lenses on it.
@@ -91,11 +91,24 @@ profiles:
91
91
  - { for: 40s, uplink: { rate: 2.5mbit, delay: 90ms, loss: 1% } }
92
92
  ```
93
93
 
94
- - **static** — constant `{rate, delay, jitter, distribution, loss,
95
- loss_correlation, reorder, duplicate, seed}` per direction. `loss_correlation`
96
- (netem state/`gemodel`) makes loss bursty rather than independent — closer to
97
- real radio loss. `seed` maps to `tc netem seed SEED` when supported, making the
98
- generated random delay/loss draw replayable.
94
+ - **static** — constant `{rate, delay, jitter, distribution, distribution_file,
95
+ loss, loss_correlation, loss_gemodel, reorder, duplicate, seed}` per
96
+ direction. Two loss models, mutually exclusive per direction:
97
+ `loss` (+ optional `loss_correlation`) is netem's independent/correlated
98
+ per-packet loss; `loss_gemodel: {p, r, loss_bad, loss_good}` is the
99
+ Gilbert–Elliott state model (`tc netem loss gemodel`), the vocabulary a real
100
+ cellular link needs — rare transitions into a short intense bad state
101
+ (`p`/`r` per packet) with distinct loss probabilities inside (`loss_bad`)
102
+ and outside (`loss_good`) of it. The 2026-08-17 CCNG field fit is the
103
+ reference: `p 0.12%, r 2.9%, loss_bad 2.7%, loss_good 0.098%` reproduces
104
+ 113 ms bad states every ~2.8 s. `distribution` accepts the iproute2
105
+ builtins (`normal|pareto|paretonormal`) or a custom table name whose
106
+ `distribution_file` (a `.dist` built by `python -m rosotacom.netem_dist`
107
+ from measured delay samples) the runner installs into the shaping netns as
108
+ `/usr/lib/tc/<name>.dist` — because a field link's delay is
109
+ lognormal-tailed and `normal` under-tails p99 by ~2x. `seed` maps to
110
+ `tc netem seed SEED` when supported, making the generated random
111
+ delay/loss draw replayable.
99
112
  - **timeline** — an ordered list of `{ for: <dur>, <static-params> | outage }`
100
113
  segments. A recovery benchmark (RFC 0005) *is* a timeline profile; `outage` is
101
114
  the step whose recovery is measured.
@@ -189,8 +202,11 @@ RFC 0002's `calibrate` / `--suggest` machinery, **per profile**:
189
202
  the fluctuation `netem` does not reproduce by default. Emulation buys
190
203
  **reproducibility, not truth** — rung 4 (real cellular) calibrates the
191
204
  *shape* of the emulated profile; the profile then gives repeatable gating.
192
- - **Bursty loss needs the state model.** Default `netem loss` is independent; use
193
- `loss_correlation` / `gemodel` to approximate real burst loss.
205
+ - **Bursty loss needs the state model.** Default `netem loss` is independent; the
206
+ 2-arg `loss p% corr%` cannot express short-bad/long-good either. `loss_gemodel`
207
+ carries the Gilbert–Elliott parameters directly (see the schema section); fit
208
+ `p`/`r` from the measured bad/good run lengths (per packet) and the two loss
209
+ probabilities from the loss rate inside/outside bad states.
194
210
  - **Outage semantics are a choice.** `netem loss 100%` keeps the interface up (DDS
195
211
  endpoints survive, recovery is "catch up") vs link-down (forces RMW
196
212
  re-discovery, a harsher recovery). They test different recoveries — see Open
@@ -215,6 +231,12 @@ risk — a stuck `qdisc` silently corrupts every later result on the machine.
215
231
  `profiles.yaml` referenced from `rosotacom.yaml`), and resolve selection via
216
232
  `--profile <name>` / `shared.profile` / `none` (`resolve_profile_selection`,
217
233
  `cli._resolve_active_profile`).
234
+ - [x] Extend the loss/delay vocabulary to what field links actually need
235
+ (issue #278): `loss_gemodel` (Gilbert–Elliott state loss, exclusive with
236
+ `loss`) and custom delay-distribution tables (`distribution` beyond the
237
+ builtins + `distribution_file`, generated from measured samples by
238
+ `rosotacom.netem_dist`, installed into the shaping netns by the lab runner
239
+ before arming).
218
240
  - [x] Arm a named static profile on the OTA-interface egress with fail-safe
219
241
  teardown (revert on stop, on error, and via a safety max-duration watchdog);
220
242
  target the data interface only, never the SSH/control interface
@@ -268,6 +290,21 @@ off during implementation). Notes whether automation is feasible; privileged
268
290
  for a given profile/direction (rate/delay/jitter/loss/correlation → `tbf`+`netem`
269
291
  string), without touching a real interface (`test_shaping_commands_*`,
270
292
  `test_netem_arg_ordering_is_valid`). Done.
293
+ - [x] **Gilbert–Elliott loss (`loss_gemodel`)** — host unit tests on the schema
294
+ (required/unknown keys, exclusivity against `loss`) and on the argv (all four
295
+ probabilities always emitted): `test_gemodel_parse_and_exclusivity`,
296
+ `test_gemodel_argv_emits_all_four_probabilities`. The *statistical* fidelity of
297
+ the emitted process (bad-run length, in-state loss rates) stays a bench check
298
+ against a field fit — the 2026-08-17 CCNG fragment replay is the reference.
299
+ - [x] **Custom delay-distribution tables** — host unit tests: a non-builtin
300
+ `distribution` requires `distribution_file` and resolves relative to the
301
+ profiles file (`test_custom_distribution_requires_a_file_and_builtin_does_not`,
302
+ `test_load_profiles_file_resolves_distribution_files_and_lists_installs`);
303
+ table generation is normalized, monotonic, tail-preserving
304
+ (`test_netem_dist.py`). The lab runner installs required tables into the
305
+ shaping netns before arming (`required_dist_installs` consumed in
306
+ `cli_benchmark`); that a netem referencing the installed name actually loads
307
+ it is part of the same bench check as arming itself.
271
308
  - [~] **Fail-safe teardown** (revert on stop, on error, on safety max-duration; the
272
309
  idempotent `tc qdisc del … root` always runs; data-interface-only, never the
273
310
  SSH/control interface) — host unit test on the teardown/targeting logic done
@@ -568,6 +568,27 @@ number being reported.
568
568
  - `bit_rate` (int)
569
569
  - `encoder_av_options` (string)
570
570
 
571
+ #### Receiver-side playout pacing (manual node)
572
+
573
+ A receiver that hands every packet to the decoder the moment it arrives turns
574
+ network delay jitter into display stutter. `com_py playout_pacer` re-times a
575
+ received packet stream to `stamp + budget` (budget adaptive above the fastest
576
+ observed path, clock-offset-proof; late packets pass through immediately, order
577
+ always preserved — the decoder chain sees every packet):
578
+
579
+ ```bash
580
+ ros2 run com_py playout_pacer --ros-args \
581
+ -p topic:=/cam/image/compressed/drop1of2/ffmpeg \
582
+ -p target_ms:=350.0 -p adaptive:=true
583
+ ```
584
+
585
+ It republishes on `<topic>/paced` plus `.../paced/budget_ms` and
586
+ `.../paced/queue_depth` debug topics; point the reverse republish (or any
587
+ decoder) at the paced name. On the 2026-08-17 CCNG field trace this removes
588
+ ~95 % of delay-caused >200 ms display gaps at ~100 ms median added age
589
+ (adaptive mode). Declarative wiring as a `transport.playout` block is tracked
590
+ in issue #284.
591
+
571
592
  `type: foxglove` supports (CompressedVideo):
572
593
  - `gop_size` (int)
573
594
  - `bit_rate` (int)
@@ -5514,6 +5514,30 @@ def _make_live_run_point(args: argparse.Namespace, session_name: str) -> RunPoin
5514
5514
 
5515
5515
  # 3. Arm the network shapers (after discovery is complete)
5516
5516
  if profile_obj is not None:
5517
+ from .network_profiles import required_dist_installs
5518
+
5519
+ dist_installs = required_dist_installs(profile_obj)
5520
+ if dist_installs:
5521
+ # Custom delay-distribution tables must exist inside the
5522
+ # shaping netns before netem references them by name.
5523
+ for dist_container in (a_container, b_container):
5524
+ if not dist_container:
5525
+ continue
5526
+ for table_name, file_path in dist_installs:
5527
+ dist_target = f"/usr/lib/tc/{table_name}.dist"
5528
+ for install_cmd in (
5529
+ ["docker", "exec", "-u", "root", dist_container, "mkdir", "-p", "/usr/lib/tc"],
5530
+ ["docker", "cp", file_path, f"{dist_container}:{dist_target}"],
5531
+ ):
5532
+ res = subprocess.run(install_cmd, capture_output=True, text=True, check=False)
5533
+ if res.returncode != 0:
5534
+ raise RuntimeError(
5535
+ f"failed to install netem distribution table {table_name!r} in "
5536
+ f"{dist_container}: {' '.join(install_cmd)} -> {res.stderr or res.stdout}"
5537
+ )
5538
+ print(
5539
+ f" netem distribution {table_name!r} installed in {dist_container}: {dist_target}"
5540
+ )
5517
5541
  if profile_obj.is_timeline:
5518
5542
  # Peer A shapes uplink (egress to B)
5519
5543
  shaper_a = ProfileShaper("eth0", make_container_runner(a_container))