rosotacom-dev 2.5.dev50__tar.gz → 2.5.dev52__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.
- {rosotacom_dev-2.5.dev50 → rosotacom_dev-2.5.dev52}/.github/workflows/nightly-e2e.yml +1 -0
- {rosotacom_dev-2.5.dev50 → rosotacom_dev-2.5.dev52}/PKG-INFO +8 -1
- {rosotacom_dev-2.5.dev50 → rosotacom_dev-2.5.dev52}/README.md +7 -0
- {rosotacom_dev-2.5.dev50 → rosotacom_dev-2.5.dev52}/docs/findings/README.md +5 -0
- {rosotacom_dev-2.5.dev50 → rosotacom_dev-2.5.dev52}/docs/findings/delay-alone-no-loss.md +8 -1
- rosotacom_dev-2.5.dev52/docs/findings/fastdds-datagram-cap-drops-large-samples.md +89 -0
- rosotacom_dev-2.5.dev52/docs/findings/graph-derived-types-block-a-non-graph-transport.md +79 -0
- {rosotacom_dev-2.5.dev50 → rosotacom_dev-2.5.dev52}/docs/findings/head-of-line-irregularity.md +11 -1
- rosotacom_dev-2.5.dev52/docs/findings/keyframe-loss-kills-the-next-deltas.md +69 -0
- rosotacom_dev-2.5.dev52/docs/findings/oversubscription-queues-not-losses.md +81 -0
- rosotacom_dev-2.5.dev52/docs/findings/repro/decode_chain_repro.py +126 -0
- rosotacom_dev-2.5.dev52/docs/findings/sender-rows-make-a-dead-link-look-loss-free.md +59 -0
- {rosotacom_dev-2.5.dev50 → rosotacom_dev-2.5.dev52}/session-configuration.md +87 -3
- {rosotacom_dev-2.5.dev50 → rosotacom_dev-2.5.dev52}/src/rosotacom/benchmark.py +12 -2
- {rosotacom_dev-2.5.dev50 → rosotacom_dev-2.5.dev52}/src/rosotacom/cli.py +89 -40
- {rosotacom_dev-2.5.dev50 → rosotacom_dev-2.5.dev52}/src/rosotacom/cli_benchmark.py +80 -28
- {rosotacom_dev-2.5.dev50 → rosotacom_dev-2.5.dev52}/src/rosotacom/resources/examples/sessions/4_comp_occ_grid_zen/session-definition.yaml +3 -0
- {rosotacom_dev-2.5.dev50 → rosotacom_dev-2.5.dev52}/src/rosotacom/resources/examples/sessions/6_sized_payload_zen/session-definition.yaml +3 -0
- rosotacom_dev-2.5.dev52/src/rosotacom/resources/ws/ota_configs/fastdds_easy_mode.xml.template +49 -0
- rosotacom_dev-2.5.dev52/src/rosotacom/resources/ws/ota_configs/fastdds_tuned.xml.template +154 -0
- {rosotacom_dev-2.5.dev50 → rosotacom_dev-2.5.dev52}/src/rosotacom/resources/ws/ros2src/com_py/com_py/base_bridge_relay.py +9 -2
- {rosotacom_dev-2.5.dev50 → rosotacom_dev-2.5.dev52}/src/rosotacom/resources/ws/ros2src/com_py/com_py/pub_sub_pair.py +19 -7
- {rosotacom_dev-2.5.dev50 → rosotacom_dev-2.5.dev52}/src/rosotacom/resources/ws/ros2src/com_py/com_py/qos.py +24 -0
- {rosotacom_dev-2.5.dev50 → rosotacom_dev-2.5.dev52}/src/rosotacom/resources/ws/session/content/base/session_plugin_base.yaml +21 -3
- {rosotacom_dev-2.5.dev50 → rosotacom_dev-2.5.dev52}/src/rosotacom/resources/ws/session/creation/generate_session_files.py +84 -7
- {rosotacom_dev-2.5.dev50 → rosotacom_dev-2.5.dev52}/src/rosotacom/transit.py +20 -2
- {rosotacom_dev-2.5.dev50 → rosotacom_dev-2.5.dev52}/src/rosotacom_dev.egg-info/PKG-INFO +8 -1
- {rosotacom_dev-2.5.dev50 → rosotacom_dev-2.5.dev52}/src/rosotacom_dev.egg-info/SOURCES.txt +11 -0
- {rosotacom_dev-2.5.dev50 → rosotacom_dev-2.5.dev52}/src/rosotacom_dev.egg-info/scm_file_list.json +11 -0
- rosotacom_dev-2.5.dev52/src/rosotacom_dev.egg-info/scm_version.json +8 -0
- {rosotacom_dev-2.5.dev50 → rosotacom_dev-2.5.dev52}/tests/contract/test_markdown_links.py +15 -1
- {rosotacom_dev-2.5.dev50 → rosotacom_dev-2.5.dev52}/tests/contract/test_packaged_resources.py +2 -0
- {rosotacom_dev-2.5.dev50 → rosotacom_dev-2.5.dev52}/tests/contract/test_security_maintenance_config.py +4 -0
- {rosotacom_dev-2.5.dev50 → rosotacom_dev-2.5.dev52}/tests/e2e/conftest.py +1 -0
- {rosotacom_dev-2.5.dev50 → rosotacom_dev-2.5.dev52}/tests/sessions/generate_rmw_matrix.py +7 -0
- rosotacom_dev-2.5.dev52/tests/sessions/rmw_matrix/1_heartbeat_fastdds-default/session-definition.yaml +22 -0
- {rosotacom_dev-2.5.dev50 → rosotacom_dev-2.5.dev52}/tests/unit/test_benchmark.py +15 -0
- {rosotacom_dev-2.5.dev50 → rosotacom_dev-2.5.dev52}/tests/unit/test_cli_benchmark.py +76 -0
- rosotacom_dev-2.5.dev52/tests/unit/test_ota_link_configs.py +288 -0
- rosotacom_dev-2.5.dev52/tests/unit/test_pub_sub_pair_types.py +144 -0
- {rosotacom_dev-2.5.dev50 → rosotacom_dev-2.5.dev52}/tests/unit/test_relay_optional_parameters.py +1 -1
- {rosotacom_dev-2.5.dev50 → rosotacom_dev-2.5.dev52}/tests/unit/test_transit.py +26 -2
- rosotacom_dev-2.5.dev50/src/rosotacom_dev.egg-info/scm_version.json +0 -8
- {rosotacom_dev-2.5.dev50 → rosotacom_dev-2.5.dev52}/.devcontainer/devcontainer.json.template +0 -0
- {rosotacom_dev-2.5.dev50 → rosotacom_dev-2.5.dev52}/.devcontainer/re_generate_devcontainer_json.py +0 -0
- {rosotacom_dev-2.5.dev50 → rosotacom_dev-2.5.dev52}/.github/CODEOWNERS +0 -0
- {rosotacom_dev-2.5.dev50 → rosotacom_dev-2.5.dev52}/.github/ISSUE_TEMPLATE/documentation-audit.md +0 -0
- {rosotacom_dev-2.5.dev50 → rosotacom_dev-2.5.dev52}/.github/ISSUE_TEMPLATE/implementation-cleanup.md +0 -0
- {rosotacom_dev-2.5.dev50 → rosotacom_dev-2.5.dev52}/.github/ISSUE_TEMPLATE/quality-workflow.md +0 -0
- {rosotacom_dev-2.5.dev50 → rosotacom_dev-2.5.dev52}/.github/ISSUE_TEMPLATE/redesign.md +0 -0
- {rosotacom_dev-2.5.dev50 → rosotacom_dev-2.5.dev52}/.github/ISSUE_TEMPLATE/release-process.md +0 -0
- {rosotacom_dev-2.5.dev50 → rosotacom_dev-2.5.dev52}/.github/ISSUE_TEMPLATE/repository-diagnosis.md +0 -0
- {rosotacom_dev-2.5.dev50 → rosotacom_dev-2.5.dev52}/.github/ISSUE_TEMPLATE/test-ci-audit.md +0 -0
- {rosotacom_dev-2.5.dev50 → rosotacom_dev-2.5.dev52}/.github/ISSUE_TEMPLATE/work-item.md +0 -0
- {rosotacom_dev-2.5.dev50 → rosotacom_dev-2.5.dev52}/.github/PULL_REQUEST_TEMPLATE.md +0 -0
- {rosotacom_dev-2.5.dev50 → rosotacom_dev-2.5.dev52}/.github/actions/setup-just/action.yml +0 -0
- {rosotacom_dev-2.5.dev50 → rosotacom_dev-2.5.dev52}/.github/dependabot.yml +0 -0
- {rosotacom_dev-2.5.dev50 → rosotacom_dev-2.5.dev52}/.github/workflows/benchmark-calibrate.yml +0 -0
- {rosotacom_dev-2.5.dev50 → rosotacom_dev-2.5.dev52}/.github/workflows/benchmark-gate.yml +0 -0
- {rosotacom_dev-2.5.dev50 → rosotacom_dev-2.5.dev52}/.github/workflows/dev-release.yml +0 -0
- {rosotacom_dev-2.5.dev50 → rosotacom_dev-2.5.dev52}/.github/workflows/image-scan.yml +0 -0
- {rosotacom_dev-2.5.dev50 → rosotacom_dev-2.5.dev52}/.github/workflows/pr-lightweight.yml +0 -0
- {rosotacom_dev-2.5.dev50 → rosotacom_dev-2.5.dev52}/.github/workflows/pr-merge-gate.yml +0 -0
- {rosotacom_dev-2.5.dev50 → rosotacom_dev-2.5.dev52}/.github/workflows/release.yml +0 -0
- {rosotacom_dev-2.5.dev50 → rosotacom_dev-2.5.dev52}/.pre-commit-config.yaml +0 -0
- {rosotacom_dev-2.5.dev50 → rosotacom_dev-2.5.dev52}/AGENTS.md +0 -0
- {rosotacom_dev-2.5.dev50 → rosotacom_dev-2.5.dev52}/CONCURRENCY.md +0 -0
- {rosotacom_dev-2.5.dev50 → rosotacom_dev-2.5.dev52}/CONTRIBUTING.md +0 -0
- {rosotacom_dev-2.5.dev50 → rosotacom_dev-2.5.dev52}/DEVELOPMENT_PRINCIPLES.md +0 -0
- {rosotacom_dev-2.5.dev50 → rosotacom_dev-2.5.dev52}/LICENSE.txt +0 -0
- {rosotacom_dev-2.5.dev50 → rosotacom_dev-2.5.dev52}/MANIFEST.in +0 -0
- {rosotacom_dev-2.5.dev50 → rosotacom_dev-2.5.dev52}/budgets.jsonl +0 -0
- {rosotacom_dev-2.5.dev50 → rosotacom_dev-2.5.dev52}/calibration/README.md +0 -0
- {rosotacom_dev-2.5.dev50 → rosotacom_dev-2.5.dev52}/calibration/capacity-size-tight-cyclone.json +0 -0
- {rosotacom_dev-2.5.dev50 → rosotacom_dev-2.5.dev52}/calibration/probe-loss-gop-tight-cyclone.json +0 -0
- {rosotacom_dev-2.5.dev50 → rosotacom_dev-2.5.dev52}/calibration/probe-loss-nominal-cyclone.json +0 -0
- {rosotacom_dev-2.5.dev50 → rosotacom_dev-2.5.dev52}/calibration/probe-loss-tight-cyclone.json +0 -0
- {rosotacom_dev-2.5.dev50 → rosotacom_dev-2.5.dev52}/calibration/probe-loss-tight-fastdds.json +0 -0
- {rosotacom_dev-2.5.dev50 → rosotacom_dev-2.5.dev52}/calibration/probe-loss-tight-zenoh.json +0 -0
- {rosotacom_dev-2.5.dev50 → rosotacom_dev-2.5.dev52}/codecov.yml +0 -0
- {rosotacom_dev-2.5.dev50 → rosotacom_dev-2.5.dev52}/deployment-configuration.md +0 -0
- {rosotacom_dev-2.5.dev50 → rosotacom_dev-2.5.dev52}/docs/benchmark-ab.md +0 -0
- {rosotacom_dev-2.5.dev50 → rosotacom_dev-2.5.dev52}/docs/benchmark-bag-as-load.md +0 -0
- {rosotacom_dev-2.5.dev50 → rosotacom_dev-2.5.dev52}/docs/ci.md +0 -0
- {rosotacom_dev-2.5.dev50 → rosotacom_dev-2.5.dev52}/docs/ffmpeg-keyframes.md +0 -0
- {rosotacom_dev-2.5.dev50 → rosotacom_dev-2.5.dev52}/docs/findings/cyclone-spdp-discovery-bursts.md +0 -0
- {rosotacom_dev-2.5.dev50 → rosotacom_dev-2.5.dev52}/docs/findings/jitter-loss-bandwidth-latency.md +0 -0
- {rosotacom_dev-2.5.dev50 → rosotacom_dev-2.5.dev52}/docs/findings/lighter-message-loss.md +0 -0
- {rosotacom_dev-2.5.dev50 → rosotacom_dev-2.5.dev52}/docs/findings/reorder-becomes-reader-loss.md +0 -0
- {rosotacom_dev-2.5.dev50 → rosotacom_dev-2.5.dev52}/docs/findings/repro/reorder_drop_repro.py +0 -0
- {rosotacom_dev-2.5.dev50 → rosotacom_dev-2.5.dev52}/docs/findings/zero-loss-boundaries-18kb-20hz.md +0 -0
- {rosotacom_dev-2.5.dev50 → rosotacom_dev-2.5.dev52}/docs/forensics-report.md +0 -0
- {rosotacom_dev-2.5.dev50 → rosotacom_dev-2.5.dev52}/docs/geo-link-map.md +0 -0
- {rosotacom_dev-2.5.dev50 → rosotacom_dev-2.5.dev52}/docs/github-repository-settings.md +0 -0
- {rosotacom_dev-2.5.dev50 → rosotacom_dev-2.5.dev52}/docs/link-bandwidth.md +0 -0
- {rosotacom_dev-2.5.dev50 → rosotacom_dev-2.5.dev52}/docs/link-trace.md +0 -0
- {rosotacom_dev-2.5.dev50 → rosotacom_dev-2.5.dev52}/docs/owner-runbook.md +0 -0
- {rosotacom_dev-2.5.dev50 → rosotacom_dev-2.5.dev52}/docs/performance-bands.md +0 -0
- {rosotacom_dev-2.5.dev50 → rosotacom_dev-2.5.dev52}/docs/playbooks/README.md +0 -0
- {rosotacom_dev-2.5.dev50 → rosotacom_dev-2.5.dev52}/docs/quality-model.md +0 -0
- {rosotacom_dev-2.5.dev50 → rosotacom_dev-2.5.dev52}/docs/release-notes/TEMPLATE.md +0 -0
- {rosotacom_dev-2.5.dev50 → rosotacom_dev-2.5.dev52}/docs/release-notes/v2.1.md +0 -0
- {rosotacom_dev-2.5.dev50 → rosotacom_dev-2.5.dev52}/docs/release-notes/v2.2.md +0 -0
- {rosotacom_dev-2.5.dev50 → rosotacom_dev-2.5.dev52}/docs/release-notes/v2.3.md +0 -0
- {rosotacom_dev-2.5.dev50 → rosotacom_dev-2.5.dev52}/docs/release-notes/v2.4.md +0 -0
- {rosotacom_dev-2.5.dev50 → rosotacom_dev-2.5.dev52}/docs/release.md +0 -0
- {rosotacom_dev-2.5.dev50 → rosotacom_dev-2.5.dev52}/docs/rfcs/0001-expectation-driven-test-suite.md +0 -0
- {rosotacom_dev-2.5.dev50 → rosotacom_dev-2.5.dev52}/docs/rfcs/0002-expectation-concepts.md +0 -0
- {rosotacom_dev-2.5.dev50 → rosotacom_dev-2.5.dev52}/docs/rfcs/0003-metric-backbone.md +0 -0
- {rosotacom_dev-2.5.dev50 → rosotacom_dev-2.5.dev52}/docs/rfcs/0004-network-profiles.md +0 -0
- {rosotacom_dev-2.5.dev50 → rosotacom_dev-2.5.dev52}/docs/rfcs/0005-benchmark-genres-and-ci.md +0 -0
- {rosotacom_dev-2.5.dev50 → rosotacom_dev-2.5.dev52}/docs/rfcs/0006-dynamic-qos.md +0 -0
- {rosotacom_dev-2.5.dev50 → rosotacom_dev-2.5.dev52}/docs/rfcs/0007-regression-gate.md +0 -0
- {rosotacom_dev-2.5.dev50 → rosotacom_dev-2.5.dev52}/docs/rfcs/README.md +0 -0
- {rosotacom_dev-2.5.dev50 → rosotacom_dev-2.5.dev52}/docs/testing.md +0 -0
- {rosotacom_dev-2.5.dev50 → rosotacom_dev-2.5.dev52}/docs/work-items.md +0 -0
- {rosotacom_dev-2.5.dev50 → rosotacom_dev-2.5.dev52}/install.sh +0 -0
- {rosotacom_dev-2.5.dev50 → rosotacom_dev-2.5.dev52}/justfile +0 -0
- {rosotacom_dev-2.5.dev50 → rosotacom_dev-2.5.dev52}/media/control_center.png +0 -0
- {rosotacom_dev-2.5.dev50 → rosotacom_dev-2.5.dev52}/media/echo_test.png +0 -0
- {rosotacom_dev-2.5.dev50 → rosotacom_dev-2.5.dev52}/media/forensics-report-example.png +0 -0
- {rosotacom_dev-2.5.dev50 → rosotacom_dev-2.5.dev52}/media/host.png +0 -0
- {rosotacom_dev-2.5.dev50 → rosotacom_dev-2.5.dev52}/media/vehicle.png +0 -0
- {rosotacom_dev-2.5.dev50 → rosotacom_dev-2.5.dev52}/pyproject.toml +0 -0
- {rosotacom_dev-2.5.dev50 → rosotacom_dev-2.5.dev52}/requirements.txt +0 -0
- {rosotacom_dev-2.5.dev50 → rosotacom_dev-2.5.dev52}/rosotacom.yaml +0 -0
- {rosotacom_dev-2.5.dev50 → rosotacom_dev-2.5.dev52}/setup.cfg +0 -0
- {rosotacom_dev-2.5.dev50 → rosotacom_dev-2.5.dev52}/src/rosotacom/__init__.py +0 -0
- {rosotacom_dev-2.5.dev50 → rosotacom_dev-2.5.dev52}/src/rosotacom/__main__.py +0 -0
- {rosotacom_dev-2.5.dev50 → rosotacom_dev-2.5.dev52}/src/rosotacom/anonymize.py +0 -0
- {rosotacom_dev-2.5.dev50 → rosotacom_dev-2.5.dev52}/src/rosotacom/bag_ground_truth.py +0 -0
- {rosotacom_dev-2.5.dev50 → rosotacom_dev-2.5.dev52}/src/rosotacom/benched_set.py +0 -0
- {rosotacom_dev-2.5.dev50 → rosotacom_dev-2.5.dev52}/src/rosotacom/benchmark_delta.py +0 -0
- {rosotacom_dev-2.5.dev50 → rosotacom_dev-2.5.dev52}/src/rosotacom/bundle_check.py +0 -0
- {rosotacom_dev-2.5.dev50 → rosotacom_dev-2.5.dev52}/src/rosotacom/deployment.py +0 -0
- {rosotacom_dev-2.5.dev50 → rosotacom_dev-2.5.dev52}/src/rosotacom/ffmpeg_packet.py +0 -0
- {rosotacom_dev-2.5.dev50 → rosotacom_dev-2.5.dev52}/src/rosotacom/forensics.py +0 -0
- {rosotacom_dev-2.5.dev50 → rosotacom_dev-2.5.dev52}/src/rosotacom/geomap.py +0 -0
- {rosotacom_dev-2.5.dev50 → rosotacom_dev-2.5.dev52}/src/rosotacom/image_cache.py +0 -0
- {rosotacom_dev-2.5.dev50 → rosotacom_dev-2.5.dev52}/src/rosotacom/netem_dist.py +0 -0
- {rosotacom_dev-2.5.dev50 → rosotacom_dev-2.5.dev52}/src/rosotacom/network_profiles.py +0 -0
- {rosotacom_dev-2.5.dev50 → rosotacom_dev-2.5.dev52}/src/rosotacom/network_shaper.py +0 -0
- {rosotacom_dev-2.5.dev50 → rosotacom_dev-2.5.dev52}/src/rosotacom/plots.py +0 -0
- {rosotacom_dev-2.5.dev50 → rosotacom_dev-2.5.dev52}/src/rosotacom/py.typed +0 -0
- {rosotacom_dev-2.5.dev50 → rosotacom_dev-2.5.dev52}/src/rosotacom/resources/benched-set.yaml +0 -0
- {rosotacom_dev-2.5.dev50 → rosotacom_dev-2.5.dev52}/src/rosotacom/resources/examples/.gitignore +0 -0
- {rosotacom_dev-2.5.dev50 → rosotacom_dev-2.5.dev52}/src/rosotacom/resources/examples/README.md +0 -0
- {rosotacom_dev-2.5.dev50 → rosotacom_dev-2.5.dev52}/src/rosotacom/resources/examples/__init__.py +0 -0
- {rosotacom_dev-2.5.dev50 → rosotacom_dev-2.5.dev52}/src/rosotacom/resources/examples/bags/8_drop_reference/metadata.yaml +0 -0
- {rosotacom_dev-2.5.dev50 → rosotacom_dev-2.5.dev52}/src/rosotacom/resources/examples/deployment.example.yaml +0 -0
- {rosotacom_dev-2.5.dev50 → rosotacom_dev-2.5.dev52}/src/rosotacom/resources/examples/dist/field-20260817.dist +0 -0
- {rosotacom_dev-2.5.dev50 → rosotacom_dev-2.5.dev52}/src/rosotacom/resources/examples/profiles.yaml +0 -0
- {rosotacom_dev-2.5.dev50 → rosotacom_dev-2.5.dev52}/src/rosotacom/resources/examples/ros2docker.json +0 -0
- {rosotacom_dev-2.5.dev50 → rosotacom_dev-2.5.dev52}/src/rosotacom/resources/examples/rosotacom.yaml +0 -0
- {rosotacom_dev-2.5.dev50 → rosotacom_dev-2.5.dev52}/src/rosotacom/resources/examples/scenarios/2_native_chatter/scenario-definition.yaml +0 -0
- {rosotacom_dev-2.5.dev50 → rosotacom_dev-2.5.dev52}/src/rosotacom/resources/examples/scripts/1_heartbeat/run_machine_a.sh +0 -0
- {rosotacom_dev-2.5.dev50 → rosotacom_dev-2.5.dev52}/src/rosotacom/resources/examples/scripts/1_heartbeat/run_machine_b.sh +0 -0
- {rosotacom_dev-2.5.dev50 → rosotacom_dev-2.5.dev52}/src/rosotacom/resources/examples/scripts/2_native_chatter/machine_a/external.ros2docker.json +0 -0
- {rosotacom_dev-2.5.dev50 → rosotacom_dev-2.5.dev52}/src/rosotacom/resources/examples/scripts/2_native_chatter/machine_a/run_communication.sh +0 -0
- {rosotacom_dev-2.5.dev50 → rosotacom_dev-2.5.dev52}/src/rosotacom/resources/examples/scripts/2_native_chatter/machine_a/run_external.py +0 -0
- {rosotacom_dev-2.5.dev50 → rosotacom_dev-2.5.dev52}/src/rosotacom/resources/examples/scripts/2_native_chatter/machine_a/wait_for_echo_topic.sh +0 -0
- {rosotacom_dev-2.5.dev50 → rosotacom_dev-2.5.dev52}/src/rosotacom/resources/examples/scripts/2_native_chatter/machine_b/external.ros2docker.json +0 -0
- {rosotacom_dev-2.5.dev50 → rosotacom_dev-2.5.dev52}/src/rosotacom/resources/examples/scripts/2_native_chatter/machine_b/run_communication.sh +0 -0
- {rosotacom_dev-2.5.dev50 → rosotacom_dev-2.5.dev52}/src/rosotacom/resources/examples/scripts/2_native_chatter/machine_b/run_external.py +0 -0
- {rosotacom_dev-2.5.dev50 → rosotacom_dev-2.5.dev52}/src/rosotacom/resources/examples/scripts/3_comp_occ_grid/machine_a/external.ros2docker.json +0 -0
- {rosotacom_dev-2.5.dev50 → rosotacom_dev-2.5.dev52}/src/rosotacom/resources/examples/scripts/3_comp_occ_grid/machine_a/external_session.yaml +0 -0
- {rosotacom_dev-2.5.dev50 → rosotacom_dev-2.5.dev52}/src/rosotacom/resources/examples/scripts/3_comp_occ_grid/machine_a/foxglove.json +0 -0
- {rosotacom_dev-2.5.dev50 → rosotacom_dev-2.5.dev52}/src/rosotacom/resources/examples/scripts/3_comp_occ_grid/machine_a/run_communication.sh +0 -0
- {rosotacom_dev-2.5.dev50 → rosotacom_dev-2.5.dev52}/src/rosotacom/resources/examples/scripts/3_comp_occ_grid/machine_a/run_external.py +0 -0
- {rosotacom_dev-2.5.dev50 → rosotacom_dev-2.5.dev52}/src/rosotacom/resources/examples/scripts/3_comp_occ_grid/machine_b/costmap_1second/costmap_1second_0.mcap +0 -0
- {rosotacom_dev-2.5.dev50 → rosotacom_dev-2.5.dev52}/src/rosotacom/resources/examples/scripts/3_comp_occ_grid/machine_b/costmap_1second/metadata.yaml +0 -0
- {rosotacom_dev-2.5.dev50 → rosotacom_dev-2.5.dev52}/src/rosotacom/resources/examples/scripts/3_comp_occ_grid/machine_b/external.ros2docker.json +0 -0
- {rosotacom_dev-2.5.dev50 → rosotacom_dev-2.5.dev52}/src/rosotacom/resources/examples/scripts/3_comp_occ_grid/machine_b/run_communication.sh +0 -0
- {rosotacom_dev-2.5.dev50 → rosotacom_dev-2.5.dev52}/src/rosotacom/resources/examples/scripts/3_comp_occ_grid/machine_b/run_external.py +0 -0
- {rosotacom_dev-2.5.dev50 → rosotacom_dev-2.5.dev52}/src/rosotacom/resources/examples/scripts/4_comp_occ_grid_zen/machine_a/external.ros2docker.json +0 -0
- {rosotacom_dev-2.5.dev50 → rosotacom_dev-2.5.dev52}/src/rosotacom/resources/examples/scripts/4_comp_occ_grid_zen/machine_a/external_session.yaml +0 -0
- {rosotacom_dev-2.5.dev50 → rosotacom_dev-2.5.dev52}/src/rosotacom/resources/examples/scripts/4_comp_occ_grid_zen/machine_a/foxglove.json +0 -0
- {rosotacom_dev-2.5.dev50 → rosotacom_dev-2.5.dev52}/src/rosotacom/resources/examples/scripts/4_comp_occ_grid_zen/machine_a/run_communication.sh +0 -0
- {rosotacom_dev-2.5.dev50 → rosotacom_dev-2.5.dev52}/src/rosotacom/resources/examples/scripts/4_comp_occ_grid_zen/machine_a/run_external.py +0 -0
- {rosotacom_dev-2.5.dev50 → rosotacom_dev-2.5.dev52}/src/rosotacom/resources/examples/scripts/4_comp_occ_grid_zen/machine_b/costmap_1second/costmap_1second_0.mcap +0 -0
- {rosotacom_dev-2.5.dev50 → rosotacom_dev-2.5.dev52}/src/rosotacom/resources/examples/scripts/4_comp_occ_grid_zen/machine_b/costmap_1second/metadata.yaml +0 -0
- {rosotacom_dev-2.5.dev50 → rosotacom_dev-2.5.dev52}/src/rosotacom/resources/examples/scripts/4_comp_occ_grid_zen/machine_b/external.ros2docker.json +0 -0
- {rosotacom_dev-2.5.dev50 → rosotacom_dev-2.5.dev52}/src/rosotacom/resources/examples/scripts/4_comp_occ_grid_zen/machine_b/run_communication.sh +0 -0
- {rosotacom_dev-2.5.dev50 → rosotacom_dev-2.5.dev52}/src/rosotacom/resources/examples/scripts/4_comp_occ_grid_zen/machine_b/run_external.py +0 -0
- {rosotacom_dev-2.5.dev50 → rosotacom_dev-2.5.dev52}/src/rosotacom/resources/examples/scripts/5_sized_payload/machine_a/external.ros2docker.json +0 -0
- {rosotacom_dev-2.5.dev50 → rosotacom_dev-2.5.dev52}/src/rosotacom/resources/examples/scripts/5_sized_payload/machine_a/external_session.yaml +0 -0
- {rosotacom_dev-2.5.dev50 → rosotacom_dev-2.5.dev52}/src/rosotacom/resources/examples/scripts/5_sized_payload/machine_a/run_communication.sh +0 -0
- {rosotacom_dev-2.5.dev50 → rosotacom_dev-2.5.dev52}/src/rosotacom/resources/examples/scripts/5_sized_payload/machine_a/run_external.py +0 -0
- {rosotacom_dev-2.5.dev50 → rosotacom_dev-2.5.dev52}/src/rosotacom/resources/examples/scripts/5_sized_payload/machine_b/external.ros2docker.json +0 -0
- {rosotacom_dev-2.5.dev50 → rosotacom_dev-2.5.dev52}/src/rosotacom/resources/examples/scripts/5_sized_payload/machine_b/external_session.yaml +0 -0
- {rosotacom_dev-2.5.dev50 → rosotacom_dev-2.5.dev52}/src/rosotacom/resources/examples/scripts/5_sized_payload/machine_b/run_communication.sh +0 -0
- {rosotacom_dev-2.5.dev50 → rosotacom_dev-2.5.dev52}/src/rosotacom/resources/examples/scripts/5_sized_payload/machine_b/run_external.py +0 -0
- {rosotacom_dev-2.5.dev50 → rosotacom_dev-2.5.dev52}/src/rosotacom/resources/examples/scripts/6_sized_payload_zen/machine_a/external.ros2docker.json +0 -0
- {rosotacom_dev-2.5.dev50 → rosotacom_dev-2.5.dev52}/src/rosotacom/resources/examples/scripts/6_sized_payload_zen/machine_a/external_session.yaml +0 -0
- {rosotacom_dev-2.5.dev50 → rosotacom_dev-2.5.dev52}/src/rosotacom/resources/examples/scripts/6_sized_payload_zen/machine_a/run_communication.sh +0 -0
- {rosotacom_dev-2.5.dev50 → rosotacom_dev-2.5.dev52}/src/rosotacom/resources/examples/scripts/6_sized_payload_zen/machine_a/run_external.py +0 -0
- {rosotacom_dev-2.5.dev50 → rosotacom_dev-2.5.dev52}/src/rosotacom/resources/examples/scripts/6_sized_payload_zen/machine_b/external.ros2docker.json +0 -0
- {rosotacom_dev-2.5.dev50 → rosotacom_dev-2.5.dev52}/src/rosotacom/resources/examples/scripts/6_sized_payload_zen/machine_b/external_session.yaml +0 -0
- {rosotacom_dev-2.5.dev50 → rosotacom_dev-2.5.dev52}/src/rosotacom/resources/examples/scripts/6_sized_payload_zen/machine_b/run_communication.sh +0 -0
- {rosotacom_dev-2.5.dev50 → rosotacom_dev-2.5.dev52}/src/rosotacom/resources/examples/scripts/6_sized_payload_zen/machine_b/run_external.py +0 -0
- {rosotacom_dev-2.5.dev50 → rosotacom_dev-2.5.dev52}/src/rosotacom/resources/examples/sessions/10_restamp/session-definition.yaml +0 -0
- {rosotacom_dev-2.5.dev50 → rosotacom_dev-2.5.dev52}/src/rosotacom/resources/examples/sessions/11_trickle/session-definition.yaml +0 -0
- {rosotacom_dev-2.5.dev50 → rosotacom_dev-2.5.dev52}/src/rosotacom/resources/examples/sessions/12_content_integrity/session-definition.yaml +0 -0
- {rosotacom_dev-2.5.dev50 → rosotacom_dev-2.5.dev52}/src/rosotacom/resources/examples/sessions/13_link_latency/session-definition.yaml +0 -0
- {rosotacom_dev-2.5.dev50 → rosotacom_dev-2.5.dev52}/src/rosotacom/resources/examples/sessions/14_remote_assist_anonymized/session-definition.yaml +0 -0
- {rosotacom_dev-2.5.dev50 → rosotacom_dev-2.5.dev52}/src/rosotacom/resources/examples/sessions/15_remote_assist_anonymized_costmap/session-definition.yaml +0 -0
- {rosotacom_dev-2.5.dev50 → rosotacom_dev-2.5.dev52}/src/rosotacom/resources/examples/sessions/16_remote_assist_anonymized_camera/session-definition.yaml +0 -0
- {rosotacom_dev-2.5.dev50 → rosotacom_dev-2.5.dev52}/src/rosotacom/resources/examples/sessions/17_synthetic_camera_quality/session-definition.yaml +0 -0
- {rosotacom_dev-2.5.dev50 → rosotacom_dev-2.5.dev52}/src/rosotacom/resources/examples/sessions/1_heartbeat/session-definition.yaml +0 -0
- {rosotacom_dev-2.5.dev50 → rosotacom_dev-2.5.dev52}/src/rosotacom/resources/examples/sessions/1_heartbeat_status/session-definition.yaml +0 -0
- {rosotacom_dev-2.5.dev50 → rosotacom_dev-2.5.dev52}/src/rosotacom/resources/examples/sessions/2_native_chatter/session-definition.yaml +0 -0
- {rosotacom_dev-2.5.dev50 → rosotacom_dev-2.5.dev52}/src/rosotacom/resources/examples/sessions/3_comp_occ_grid/session-definition.yaml +0 -0
- {rosotacom_dev-2.5.dev50 → rosotacom_dev-2.5.dev52}/src/rosotacom/resources/examples/sessions/5_sized_payload/session-definition.yaml +0 -0
- {rosotacom_dev-2.5.dev50 → rosotacom_dev-2.5.dev52}/src/rosotacom/resources/examples/sessions/7_latched_static/session-definition.yaml +0 -0
- {rosotacom_dev-2.5.dev50 → rosotacom_dev-2.5.dev52}/src/rosotacom/resources/examples/sessions/8_drop/session-definition.yaml +0 -0
- {rosotacom_dev-2.5.dev50 → rosotacom_dev-2.5.dev52}/src/rosotacom/resources/examples/sessions/9_throttle/session-definition.yaml +0 -0
- {rosotacom_dev-2.5.dev50 → rosotacom_dev-2.5.dev52}/src/rosotacom/resources/examples/sessions/bench_1_1_capacity/session-definition.yaml +0 -0
- {rosotacom_dev-2.5.dev50 → rosotacom_dev-2.5.dev52}/src/rosotacom/resources/examples/sessions/bench_1_2_load_sweep/session-definition.yaml +0 -0
- {rosotacom_dev-2.5.dev50 → rosotacom_dev-2.5.dev52}/src/rosotacom/resources/examples/sessions/bench_1_3_ramp/session-definition.yaml +0 -0
- {rosotacom_dev-2.5.dev50 → rosotacom_dev-2.5.dev52}/src/rosotacom/resources/examples/sessions/bench_1_4_recovery/session-definition.yaml +0 -0
- {rosotacom_dev-2.5.dev50 → rosotacom_dev-2.5.dev52}/src/rosotacom/resources/examples/sessions/bench_1_5_camera/session-definition.yaml +0 -0
- {rosotacom_dev-2.5.dev50 → rosotacom_dev-2.5.dev52}/src/rosotacom/resources/ros2docker.json.example +0 -0
- {rosotacom_dev-2.5.dev50 → rosotacom_dev-2.5.dev52}/src/rosotacom/resources/ws/.vscode/c_cpp_properties.json +0 -0
- {rosotacom_dev-2.5.dev50 → rosotacom_dev-2.5.dev52}/src/rosotacom/resources/ws/.vscode/settings.json +0 -0
- {rosotacom_dev-2.5.dev50 → rosotacom_dev-2.5.dev52}/src/rosotacom/resources/ws/ota_configs/archive/fastdds_easy_mode.xml.template +0 -0
- {rosotacom_dev-2.5.dev50 → rosotacom_dev-2.5.dev52}/src/rosotacom/resources/ws/ota_configs/archive/fastdds_initial_peer_list.xml.template +0 -0
- {rosotacom_dev-2.5.dev50 → rosotacom_dev-2.5.dev52}/src/rosotacom/resources/ws/ota_configs/archive/fastdds_v2.xml.template +0 -0
- {rosotacom_dev-2.5.dev50 → rosotacom_dev-2.5.dev52}/src/rosotacom/resources/ws/ota_configs/archive/fastdds_v3.xml.template +0 -0
- {rosotacom_dev-2.5.dev50 → rosotacom_dev-2.5.dev52}/src/rosotacom/resources/ws/ota_configs/archive/fastdds_v4.xml.template +0 -0
- {rosotacom_dev-2.5.dev50 → rosotacom_dev-2.5.dev52}/src/rosotacom/resources/ws/ota_configs/archive/fastdds_v5.xml.template +0 -0
- {rosotacom_dev-2.5.dev50 → rosotacom_dev-2.5.dev52}/src/rosotacom/resources/ws/ota_configs/create_zenoh_json5.py +0 -0
- {rosotacom_dev-2.5.dev50 → rosotacom_dev-2.5.dev52}/src/rosotacom/resources/ws/ota_configs/cyclonedds_local_participants.xml.template +0 -0
- {rosotacom_dev-2.5.dev50 → rosotacom_dev-2.5.dev52}/src/rosotacom/resources/ws/ota_configs/cyclonedds_minimal.xml.template +0 -0
- {rosotacom_dev-2.5.dev50 → rosotacom_dev-2.5.dev52}/src/rosotacom/resources/ws/ota_configs/cyclonedds_tuned.xml.template +0 -0
- {rosotacom_dev-2.5.dev50 → rosotacom_dev-2.5.dev52}/src/rosotacom/resources/ws/ota_configs/fastdds_unicast.xml.template +0 -0
- {rosotacom_dev-2.5.dev50 → rosotacom_dev-2.5.dev52}/src/rosotacom/resources/ws/ota_configs/get_ota_xml.py +0 -0
- {rosotacom_dev-2.5.dev50 → rosotacom_dev-2.5.dev52}/src/rosotacom/resources/ws/ota_configs/utils.py +0 -0
- {rosotacom_dev-2.5.dev50 → rosotacom_dev-2.5.dev52}/src/rosotacom/resources/ws/ota_configs/zenoh.json5.template +0 -0
- {rosotacom_dev-2.5.dev50 → rosotacom_dev-2.5.dev52}/src/rosotacom/resources/ws/ros2src/.gitkeep +0 -0
- {rosotacom_dev-2.5.dev50 → rosotacom_dev-2.5.dev52}/src/rosotacom/resources/ws/ros2src/com_cpp/CMakeLists.txt +0 -0
- {rosotacom_dev-2.5.dev50 → rosotacom_dev-2.5.dev52}/src/rosotacom/resources/ws/ros2src/com_cpp/COLCON_IGNORE +0 -0
- {rosotacom_dev-2.5.dev50 → rosotacom_dev-2.5.dev52}/src/rosotacom/resources/ws/ros2src/com_cpp/LICENSE.txt +0 -0
- {rosotacom_dev-2.5.dev50 → rosotacom_dev-2.5.dev52}/src/rosotacom/resources/ws/ros2src/com_cpp/package.xml +0 -0
- {rosotacom_dev-2.5.dev50 → rosotacom_dev-2.5.dev52}/src/rosotacom/resources/ws/ros2src/com_cpp/src/hz_monitor.cpp +0 -0
- {rosotacom_dev-2.5.dev50 → rosotacom_dev-2.5.dev52}/src/rosotacom/resources/ws/ros2src/com_cpp/src/universal_compressor.cpp +0 -0
- {rosotacom_dev-2.5.dev50 → rosotacom_dev-2.5.dev52}/src/rosotacom/resources/ws/ros2src/com_cpp/src/universal_decompressor.cpp +0 -0
- {rosotacom_dev-2.5.dev50 → rosotacom_dev-2.5.dev52}/src/rosotacom/resources/ws/ros2src/com_msgs/CMakeLists.txt +0 -0
- {rosotacom_dev-2.5.dev50 → rosotacom_dev-2.5.dev52}/src/rosotacom/resources/ws/ros2src/com_msgs/LICENSE.txt +0 -0
- {rosotacom_dev-2.5.dev50 → rosotacom_dev-2.5.dev52}/src/rosotacom/resources/ws/ros2src/com_msgs/msg/CompressedData.msg +0 -0
- {rosotacom_dev-2.5.dev50 → rosotacom_dev-2.5.dev52}/src/rosotacom/resources/ws/ros2src/com_msgs/msg/EchoHeartbeat.msg +0 -0
- {rosotacom_dev-2.5.dev50 → rosotacom_dev-2.5.dev52}/src/rosotacom/resources/ws/ros2src/com_msgs/msg/OtaStamped.msg +0 -0
- {rosotacom_dev-2.5.dev50 → rosotacom_dev-2.5.dev52}/src/rosotacom/resources/ws/ros2src/com_msgs/msg/SizedPayload.msg +0 -0
- {rosotacom_dev-2.5.dev50 → rosotacom_dev-2.5.dev52}/src/rosotacom/resources/ws/ros2src/com_msgs/package.xml +0 -0
- {rosotacom_dev-2.5.dev50 → rosotacom_dev-2.5.dev52}/src/rosotacom/resources/ws/ros2src/com_py/LICENSE.txt +0 -0
- {rosotacom_dev-2.5.dev50 → rosotacom_dev-2.5.dev52}/src/rosotacom/resources/ws/ros2src/com_py/com_py/__init__.py +0 -0
- {rosotacom_dev-2.5.dev50 → rosotacom_dev-2.5.dev52}/src/rosotacom/resources/ws/ros2src/com_py/com_py/bridge_in.py +0 -0
- {rosotacom_dev-2.5.dev50 → rosotacom_dev-2.5.dev52}/src/rosotacom/resources/ws/ros2src/com_py/com_py/bridge_out.py +0 -0
- {rosotacom_dev-2.5.dev50 → rosotacom_dev-2.5.dev52}/src/rosotacom/resources/ws/ros2src/com_py/com_py/ffmpeg_flags.py +0 -0
- {rosotacom_dev-2.5.dev50 → rosotacom_dev-2.5.dev52}/src/rosotacom/resources/ws/ros2src/com_py/com_py/heartbeat_echo.py +0 -0
- {rosotacom_dev-2.5.dev50 → rosotacom_dev-2.5.dev52}/src/rosotacom/resources/ws/ros2src/com_py/com_py/hz_monitor.py +0 -0
- {rosotacom_dev-2.5.dev50 → rosotacom_dev-2.5.dev52}/src/rosotacom/resources/ws/ros2src/com_py/com_py/image_bandwidth_reducer.py +0 -0
- {rosotacom_dev-2.5.dev50 → rosotacom_dev-2.5.dev52}/src/rosotacom/resources/ws/ros2src/com_py/com_py/image_pixel_capper.py +0 -0
- {rosotacom_dev-2.5.dev50 → rosotacom_dev-2.5.dev52}/src/rosotacom/resources/ws/ros2src/com_py/com_py/lanelet_saver.py +0 -0
- {rosotacom_dev-2.5.dev50 → rosotacom_dev-2.5.dev52}/src/rosotacom/resources/ws/ros2src/com_py/com_py/latch_relay.py +0 -0
- {rosotacom_dev-2.5.dev50 → rosotacom_dev-2.5.dev52}/src/rosotacom/resources/ws/ros2src/com_py/com_py/legacy/costmap_remapper.py +0 -0
- {rosotacom_dev-2.5.dev50 → rosotacom_dev-2.5.dev52}/src/rosotacom/resources/ws/ros2src/com_py/com_py/legacy/marker_remapper.py +0 -0
- {rosotacom_dev-2.5.dev50 → rosotacom_dev-2.5.dev52}/src/rosotacom/resources/ws/ros2src/com_py/com_py/legacy/speed_test.py +0 -0
- {rosotacom_dev-2.5.dev50 → rosotacom_dev-2.5.dev52}/src/rosotacom/resources/ws/ros2src/com_py/com_py/legacy/tf_remapper.py +0 -0
- {rosotacom_dev-2.5.dev50 → rosotacom_dev-2.5.dev52}/src/rosotacom/resources/ws/ros2src/com_py/com_py/legacy/traffic_monitor.py +0 -0
- {rosotacom_dev-2.5.dev50 → rosotacom_dev-2.5.dev52}/src/rosotacom/resources/ws/ros2src/com_py/com_py/legacy/wifi_network_analysis_1.py +0 -0
- {rosotacom_dev-2.5.dev50 → rosotacom_dev-2.5.dev52}/src/rosotacom/resources/ws/ros2src/com_py/com_py/link_bytes.py +0 -0
- {rosotacom_dev-2.5.dev50 → rosotacom_dev-2.5.dev52}/src/rosotacom/resources/ws/ros2src/com_py/com_py/link_trace.py +0 -0
- {rosotacom_dev-2.5.dev50 → rosotacom_dev-2.5.dev52}/src/rosotacom/resources/ws/ros2src/com_py/com_py/local_global_frame_bridge.py +0 -0
- {rosotacom_dev-2.5.dev50 → rosotacom_dev-2.5.dev52}/src/rosotacom/resources/ws/ros2src/com_py/com_py/localization_map_split_reduce_saver.py +0 -0
- {rosotacom_dev-2.5.dev50 → rosotacom_dev-2.5.dev52}/src/rosotacom/resources/ws/ros2src/com_py/com_py/msg_transform.py +0 -0
- {rosotacom_dev-2.5.dev50 → rosotacom_dev-2.5.dev52}/src/rosotacom/resources/ws/ros2src/com_py/com_py/pair_management.py +0 -0
- {rosotacom_dev-2.5.dev50 → rosotacom_dev-2.5.dev52}/src/rosotacom/resources/ws/ros2src/com_py/com_py/playout_pacer.py +0 -0
- {rosotacom_dev-2.5.dev50 → rosotacom_dev-2.5.dev52}/src/rosotacom/resources/ws/ros2src/com_py/com_py/playout_pacer_core.py +0 -0
- {rosotacom_dev-2.5.dev50 → rosotacom_dev-2.5.dev52}/src/rosotacom/resources/ws/ros2src/com_py/com_py/relay_in.py +0 -0
- {rosotacom_dev-2.5.dev50 → rosotacom_dev-2.5.dev52}/src/rosotacom/resources/ws/ros2src/com_py/com_py/relay_out.py +0 -0
- {rosotacom_dev-2.5.dev50 → rosotacom_dev-2.5.dev52}/src/rosotacom/resources/ws/ros2src/com_py/com_py/restamp.py +0 -0
- {rosotacom_dev-2.5.dev50 → rosotacom_dev-2.5.dev52}/src/rosotacom/resources/ws/ros2src/com_py/com_py/sized_publisher.py +0 -0
- {rosotacom_dev-2.5.dev50 → rosotacom_dev-2.5.dev52}/src/rosotacom/resources/ws/ros2src/com_py/com_py/stage_latency.py +0 -0
- {rosotacom_dev-2.5.dev50 → rosotacom_dev-2.5.dev52}/src/rosotacom/resources/ws/ros2src/com_py/com_py/stage_latency_core.py +0 -0
- {rosotacom_dev-2.5.dev50 → rosotacom_dev-2.5.dev52}/src/rosotacom/resources/ws/ros2src/com_py/com_py/status_overview.py +0 -0
- {rosotacom_dev-2.5.dev50 → rosotacom_dev-2.5.dev52}/src/rosotacom/resources/ws/ros2src/com_py/com_py/status_overview_core.py +0 -0
- {rosotacom_dev-2.5.dev50 → rosotacom_dev-2.5.dev52}/src/rosotacom/resources/ws/ros2src/com_py/com_py/tf_map_to_cart_saver.py +0 -0
- {rosotacom_dev-2.5.dev50 → rosotacom_dev-2.5.dev52}/src/rosotacom/resources/ws/ros2src/com_py/com_py/topic_monitor.py +0 -0
- {rosotacom_dev-2.5.dev50 → rosotacom_dev-2.5.dev52}/src/rosotacom/resources/ws/ros2src/com_py/com_py/topic_resolution.py +0 -0
- {rosotacom_dev-2.5.dev50 → rosotacom_dev-2.5.dev52}/src/rosotacom/resources/ws/ros2src/com_py/com_py/trickle.py +0 -0
- {rosotacom_dev-2.5.dev50 → rosotacom_dev-2.5.dev52}/src/rosotacom/resources/ws/ros2src/com_py/com_py/universal_compressor.py +0 -0
- {rosotacom_dev-2.5.dev50 → rosotacom_dev-2.5.dev52}/src/rosotacom/resources/ws/ros2src/com_py/com_py/universal_decompressor.py +0 -0
- {rosotacom_dev-2.5.dev50 → rosotacom_dev-2.5.dev52}/src/rosotacom/resources/ws/ros2src/com_py/com_py/universal_ota_unwrapper.py +0 -0
- {rosotacom_dev-2.5.dev50 → rosotacom_dev-2.5.dev52}/src/rosotacom/resources/ws/ros2src/com_py/com_py/universal_ota_wrapper.py +0 -0
- {rosotacom_dev-2.5.dev50 → rosotacom_dev-2.5.dev52}/src/rosotacom/resources/ws/ros2src/com_py/package.xml +0 -0
- {rosotacom_dev-2.5.dev50 → rosotacom_dev-2.5.dev52}/src/rosotacom/resources/ws/ros2src/com_py/resource/com_py +0 -0
- {rosotacom_dev-2.5.dev50 → rosotacom_dev-2.5.dev52}/src/rosotacom/resources/ws/ros2src/com_py/setup.cfg +0 -0
- {rosotacom_dev-2.5.dev50 → rosotacom_dev-2.5.dev52}/src/rosotacom/resources/ws/ros2src/com_py/setup.py +0 -0
- {rosotacom_dev-2.5.dev50 → rosotacom_dev-2.5.dev52}/src/rosotacom/resources/ws/ros2src/com_py/test/test_copyright.py +0 -0
- {rosotacom_dev-2.5.dev50 → rosotacom_dev-2.5.dev52}/src/rosotacom/resources/ws/ros2src/com_py/test/test_flake8.py +0 -0
- {rosotacom_dev-2.5.dev50 → rosotacom_dev-2.5.dev52}/src/rosotacom/resources/ws/ros2src/com_py/test/test_pep257.py +0 -0
- {rosotacom_dev-2.5.dev50 → rosotacom_dev-2.5.dev52}/src/rosotacom/resources/ws/session/content/network/up_connectivity.py +0 -0
- {rosotacom_dev-2.5.dev50 → rosotacom_dev-2.5.dev52}/src/rosotacom/resources/ws/session/content/network/wifi_network_analysis_2.py +0 -0
- {rosotacom_dev-2.5.dev50 → rosotacom_dev-2.5.dev52}/src/rosotacom/resources/ws/session/creation/anonymize_bag.py +0 -0
- {rosotacom_dev-2.5.dev50 → rosotacom_dev-2.5.dev52}/src/rosotacom/resources/ws/session/creation/catmux_log_setup.sh +0 -0
- {rosotacom_dev-2.5.dev50 → rosotacom_dev-2.5.dev52}/src/rosotacom/resources/ws/session/creation/create_session_yaml.py +0 -0
- {rosotacom_dev-2.5.dev50 → rosotacom_dev-2.5.dev52}/src/rosotacom/resources/ws/session/creation/run_session.py +0 -0
- {rosotacom_dev-2.5.dev50 → rosotacom_dev-2.5.dev52}/src/rosotacom/resources/ws/session/creation/run_session_in_external_terminal.py +0 -0
- {rosotacom_dev-2.5.dev50 → rosotacom_dev-2.5.dev52}/src/rosotacom/resources/ws/session/creation/strip_ansi.py +0 -0
- {rosotacom_dev-2.5.dev50 → rosotacom_dev-2.5.dev52}/src/rosotacom/status_eval.py +0 -0
- {rosotacom_dev-2.5.dev50 → rosotacom_dev-2.5.dev52}/src/rosotacom/stream_stats.py +0 -0
- {rosotacom_dev-2.5.dev50 → rosotacom_dev-2.5.dev52}/src/rosotacom/trace_profiles.py +0 -0
- {rosotacom_dev-2.5.dev50 → rosotacom_dev-2.5.dev52}/src/rosotacom/video_quality.py +0 -0
- {rosotacom_dev-2.5.dev50 → rosotacom_dev-2.5.dev52}/src/rosotacom_dev.egg-info/dependency_links.txt +0 -0
- {rosotacom_dev-2.5.dev50 → rosotacom_dev-2.5.dev52}/src/rosotacom_dev.egg-info/entry_points.txt +0 -0
- {rosotacom_dev-2.5.dev50 → rosotacom_dev-2.5.dev52}/src/rosotacom_dev.egg-info/requires.txt +0 -0
- {rosotacom_dev-2.5.dev50 → rosotacom_dev-2.5.dev52}/src/rosotacom_dev.egg-info/top_level.txt +0 -0
- {rosotacom_dev-2.5.dev50 → rosotacom_dev-2.5.dev52}/terminology.md +0 -0
- {rosotacom_dev-2.5.dev50 → rosotacom_dev-2.5.dev52}/tests/contract/test_benched_set_registry.py +0 -0
- {rosotacom_dev-2.5.dev50 → rosotacom_dev-2.5.dev52}/tests/contract/test_findings.py +0 -0
- {rosotacom_dev-2.5.dev50 → rosotacom_dev-2.5.dev52}/tests/contract/test_ota_source_bundle.py +0 -0
- {rosotacom_dev-2.5.dev50 → rosotacom_dev-2.5.dev52}/tests/contract/test_package_metadata.py +0 -0
- {rosotacom_dev-2.5.dev50 → rosotacom_dev-2.5.dev52}/tests/contract/test_profile_covers_generated_commands.py +0 -0
- {rosotacom_dev-2.5.dev50 → rosotacom_dev-2.5.dev52}/tests/contract/test_pytest_policy.py +0 -0
- {rosotacom_dev-2.5.dev50 → rosotacom_dev-2.5.dev52}/tests/contract/test_readme_examples.py +0 -0
- {rosotacom_dev-2.5.dev50 → rosotacom_dev-2.5.dev52}/tests/contract/test_session_configuration_doc.py +0 -0
- {rosotacom_dev-2.5.dev50 → rosotacom_dev-2.5.dev52}/tests/contract/test_session_template_substitution.py +0 -0
- {rosotacom_dev-2.5.dev50 → rosotacom_dev-2.5.dev52}/tests/contract/test_workflow_contracts.py +0 -0
- {rosotacom_dev-2.5.dev50 → rosotacom_dev-2.5.dev52}/tests/e2e/test_anonymize_e2e.py +0 -0
- {rosotacom_dev-2.5.dev50 → rosotacom_dev-2.5.dev52}/tests/e2e/test_benchmark_ab.py +0 -0
- {rosotacom_dev-2.5.dev50 → rosotacom_dev-2.5.dev52}/tests/e2e/test_benchmark_capacity.py +0 -0
- {rosotacom_dev-2.5.dev50 → rosotacom_dev-2.5.dev52}/tests/e2e/test_benchmark_replay.py +0 -0
- {rosotacom_dev-2.5.dev50 → rosotacom_dev-2.5.dev52}/tests/e2e/test_parallel_smoke.py +0 -0
- {rosotacom_dev-2.5.dev50 → rosotacom_dev-2.5.dev52}/tests/e2e/test_smoke.py +0 -0
- {rosotacom_dev-2.5.dev50 → rosotacom_dev-2.5.dev52}/tests/e2e/test_timeline_stepping.py +0 -0
- {rosotacom_dev-2.5.dev50 → rosotacom_dev-2.5.dev52}/tests/e2e/test_video_quality_e2e.py +0 -0
- {rosotacom_dev-2.5.dev50 → rosotacom_dev-2.5.dev52}/tests/sessions/rmw_matrix/1_heartbeat_cyclone-local_zenoh-ros2dds-ota/session-definition.yaml +0 -0
- {rosotacom_dev-2.5.dev50 → rosotacom_dev-2.5.dev52}/tests/sessions/rmw_matrix/1_heartbeat_cyclone-ota/session-definition.yaml +0 -0
- {rosotacom_dev-2.5.dev50 → rosotacom_dev-2.5.dev52}/tests/sessions/rmw_matrix/1_heartbeat_cyclone-ota-tuned/session-definition.yaml +0 -0
- {rosotacom_dev-2.5.dev50 → rosotacom_dev-2.5.dev52}/tests/sessions/rmw_matrix/1_heartbeat_fastdds/session-definition.yaml +0 -0
- {rosotacom_dev-2.5.dev50 → rosotacom_dev-2.5.dev52}/tests/sessions/rmw_matrix/1_heartbeat_zen-endpoints/session-definition.yaml +0 -0
- {rosotacom_dev-2.5.dev50 → rosotacom_dev-2.5.dev52}/tests/unit/test_anonymize.py +0 -0
- {rosotacom_dev-2.5.dev50 → rosotacom_dev-2.5.dev52}/tests/unit/test_bag_ground_truth.py +0 -0
- {rosotacom_dev-2.5.dev50 → rosotacom_dev-2.5.dev52}/tests/unit/test_benched_set.py +0 -0
- {rosotacom_dev-2.5.dev50 → rosotacom_dev-2.5.dev52}/tests/unit/test_benchmark_delta.py +0 -0
- {rosotacom_dev-2.5.dev50 → rosotacom_dev-2.5.dev52}/tests/unit/test_bundle_check.py +0 -0
- {rosotacom_dev-2.5.dev50 → rosotacom_dev-2.5.dev52}/tests/unit/test_catmux_pane_logging.py +0 -0
- {rosotacom_dev-2.5.dev50 → rosotacom_dev-2.5.dev52}/tests/unit/test_cli.py +0 -0
- {rosotacom_dev-2.5.dev50 → rosotacom_dev-2.5.dev52}/tests/unit/test_concurrency.py +0 -0
- {rosotacom_dev-2.5.dev50 → rosotacom_dev-2.5.dev52}/tests/unit/test_container_shaping.py +0 -0
- {rosotacom_dev-2.5.dev50 → rosotacom_dev-2.5.dev52}/tests/unit/test_deployment.py +0 -0
- {rosotacom_dev-2.5.dev50 → rosotacom_dev-2.5.dev52}/tests/unit/test_ffmpeg_flags.py +0 -0
- {rosotacom_dev-2.5.dev50 → rosotacom_dev-2.5.dev52}/tests/unit/test_ffmpeg_packet.py +0 -0
- {rosotacom_dev-2.5.dev50 → rosotacom_dev-2.5.dev52}/tests/unit/test_field_profiles.py +0 -0
- {rosotacom_dev-2.5.dev50 → rosotacom_dev-2.5.dev52}/tests/unit/test_forensics.py +0 -0
- {rosotacom_dev-2.5.dev50 → rosotacom_dev-2.5.dev52}/tests/unit/test_geomap.py +0 -0
- {rosotacom_dev-2.5.dev50 → rosotacom_dev-2.5.dev52}/tests/unit/test_image_cache.py +0 -0
- {rosotacom_dev-2.5.dev50 → rosotacom_dev-2.5.dev52}/tests/unit/test_link_bytes.py +0 -0
- {rosotacom_dev-2.5.dev50 → rosotacom_dev-2.5.dev52}/tests/unit/test_link_trace.py +0 -0
- {rosotacom_dev-2.5.dev50 → rosotacom_dev-2.5.dev52}/tests/unit/test_netem_dist.py +0 -0
- {rosotacom_dev-2.5.dev50 → rosotacom_dev-2.5.dev52}/tests/unit/test_network_profiles.py +0 -0
- {rosotacom_dev-2.5.dev50 → rosotacom_dev-2.5.dev52}/tests/unit/test_network_shaper.py +0 -0
- {rosotacom_dev-2.5.dev50 → rosotacom_dev-2.5.dev52}/tests/unit/test_ota_unwrapper.py +0 -0
- {rosotacom_dev-2.5.dev50 → rosotacom_dev-2.5.dev52}/tests/unit/test_ota_wrapper.py +0 -0
- {rosotacom_dev-2.5.dev50 → rosotacom_dev-2.5.dev52}/tests/unit/test_ota_xml.py +0 -0
- {rosotacom_dev-2.5.dev50 → rosotacom_dev-2.5.dev52}/tests/unit/test_playout_pacer.py +0 -0
- {rosotacom_dev-2.5.dev50 → rosotacom_dev-2.5.dev52}/tests/unit/test_plots.py +0 -0
- {rosotacom_dev-2.5.dev50 → rosotacom_dev-2.5.dev52}/tests/unit/test_qos_fallback_logging.py +0 -0
- {rosotacom_dev-2.5.dev50 → rosotacom_dev-2.5.dev52}/tests/unit/test_session_pipeline.py +0 -0
- {rosotacom_dev-2.5.dev50 → rosotacom_dev-2.5.dev52}/tests/unit/test_stage_latency.py +0 -0
- {rosotacom_dev-2.5.dev50 → rosotacom_dev-2.5.dev52}/tests/unit/test_status_eval.py +0 -0
- {rosotacom_dev-2.5.dev50 → rosotacom_dev-2.5.dev52}/tests/unit/test_status_overview.py +0 -0
- {rosotacom_dev-2.5.dev50 → rosotacom_dev-2.5.dev52}/tests/unit/test_status_overview_observer.py +0 -0
- {rosotacom_dev-2.5.dev50 → rosotacom_dev-2.5.dev52}/tests/unit/test_stream_stats.py +0 -0
- {rosotacom_dev-2.5.dev50 → rosotacom_dev-2.5.dev52}/tests/unit/test_trace_profiles.py +0 -0
- {rosotacom_dev-2.5.dev50 → rosotacom_dev-2.5.dev52}/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.
|
|
3
|
+
Version: 2.5.dev52
|
|
4
4
|
Summary: Host CLI for ROS OTA communication sessions.
|
|
5
5
|
Author: develNor
|
|
6
6
|
License-Expression: BSD-3-Clause
|
|
@@ -580,6 +580,13 @@ the ssh client's own argv apart, and a transport that promises no more than
|
|
|
580
580
|
`-t` and `-o BatchMode=yes` are dropped rather than forwarded. They are options
|
|
581
581
|
of one client, and a transport that is not ssh never agreed to understand them.
|
|
582
582
|
|
|
583
|
+
The same four options — `--peer-checkout`, `--peer-exec`, `--install-mode`,
|
|
584
|
+
`--install-pin` — are on every `ota-benchmark` genre, not just `ota-smoke`. A
|
|
585
|
+
measurement is the last thing that should require a shell on the peer: the
|
|
586
|
+
whole point of a narrower transport is that the runs which touch a machine
|
|
587
|
+
repeatedly can go through it, and a benchmark touches it far more often than a
|
|
588
|
+
smoke test does.
|
|
589
|
+
|
|
583
590
|
### Live status / debugging overview
|
|
584
591
|
|
|
585
592
|
Enable a continuously-updated, per-topic pipeline overview by setting
|
|
@@ -535,6 +535,13 @@ the ssh client's own argv apart, and a transport that promises no more than
|
|
|
535
535
|
`-t` and `-o BatchMode=yes` are dropped rather than forwarded. They are options
|
|
536
536
|
of one client, and a transport that is not ssh never agreed to understand them.
|
|
537
537
|
|
|
538
|
+
The same four options — `--peer-checkout`, `--peer-exec`, `--install-mode`,
|
|
539
|
+
`--install-pin` — are on every `ota-benchmark` genre, not just `ota-smoke`. A
|
|
540
|
+
measurement is the last thing that should require a shell on the peer: the
|
|
541
|
+
whole point of a narrower transport is that the runs which touch a machine
|
|
542
|
+
repeatedly can go through it, and a benchmark touches it far more often than a
|
|
543
|
+
smoke test does.
|
|
544
|
+
|
|
538
545
|
### Live status / debugging overview
|
|
539
546
|
|
|
540
547
|
Enable a continuously-updated, per-topic pipeline overview by setting
|
|
@@ -28,6 +28,11 @@ living verification hook.
|
|
|
28
28
|
|
|
29
29
|
## Findings
|
|
30
30
|
|
|
31
|
+
- [Sender-side transit rows made a dead link report zero loss](sender-rows-make-a-dead-link-look-loss-free.md) - `delivered = rows - lost` counted the sender's own `sent` rows as deliveries, so a topic the receiver never saw summarised as `delivered == expected` at 0.0% loss, with the publisher's timer reported as arrival spacing.
|
|
32
|
+
- [A bridge that learns types from the graph cannot use a transport that does not carry the graph](graph-derived-types-block-a-non-graph-transport.md) - over `zenoh_ros2dds` the payload topic never arrived in either run: the bridge routes a topic once a local reader exists and the reader waited for the bridge, leaving only `Pending=1` in a log.
|
|
33
|
+
- [A Fast DDS datagram cap stops every message larger than it](fastdds-datagram-cap-drops-large-samples.md) - copying CycloneDDS's 1200 B fragment size into the Fast DDS OTA profile makes every sample above the cap never arrive, at 1200 B and at 8192 B, sync and async — while the 84 B heartbeat keeps flowing at 10 Hz.
|
|
34
|
+
- [A lost keyframe silences exactly the next gop_size−2 deltas](keyframe-loss-kills-the-next-deltas.md) - libavcodec emits nothing for them, the last delta decodes with artifacts, the next keyframe restores clean decode; a lost delta never blocks decode — so the display hole is gop_size × frame period, deterministically.
|
|
35
|
+
- [Oversubscription queues — it does not lose until the queue itself dies](oversubscription-queues-not-losses.md) - 16% over an emulated cap climbed one-way delay 340 → 3250 ms across twelve bins with zero loss in every one; the losses arrived only when the queue died, all at once.
|
|
31
36
|
- [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.
|
|
32
37
|
- [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.
|
|
33
38
|
- [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.
|
|
@@ -54,13 +54,20 @@ rosotacom benchmark capacity --project src/rosotacom/resources/examples/rosotaco
|
|
|
54
54
|
Migrated summary: 400 expected, 400 delivered, 0 lost, max p95 latency
|
|
55
55
|
306.927 ms.
|
|
56
56
|
|
|
57
|
+
Field recurrence (2026-08-17 CCNG drive): the negative control held on a real
|
|
58
|
+
cellular link — five jam episodes ran one-way delay up to 401–459 ms with zero
|
|
59
|
+
losses, and the drive's soggy last minute held p90 ≈ 138 ms at 0.04 losses/s.
|
|
60
|
+
The positive counterpart (where the delay actually comes from when a link is
|
|
61
|
+
too slow rather than too far) is
|
|
62
|
+
[oversubscription-queues-not-losses.md](oversubscription-queues-not-losses.md).
|
|
63
|
+
|
|
57
64
|
Verification: manual: run the commands above from a source checkout with Docker
|
|
58
65
|
and `tc` privileges; this is a no-loss negative-control finding rather than a
|
|
59
66
|
must-fail boundary row, so it remains outside the RFC 0007 boundary gate.
|
|
60
67
|
|
|
61
68
|
## Status
|
|
62
69
|
|
|
63
|
-
confirmed, 2026-
|
|
70
|
+
confirmed, 2026-08-18.
|
|
64
71
|
|
|
65
72
|
## Publication notes
|
|
66
73
|
|
|
@@ -0,0 +1,89 @@
|
|
|
1
|
+
# A Fast DDS Datagram Cap Stops Every Message Larger Than It
|
|
2
|
+
|
|
3
|
+
## Claim
|
|
4
|
+
|
|
5
|
+
Setting `<maxMessageSize>` on a Fast DDS UDPv4 transport to a value below a
|
|
6
|
+
sample's serialized size makes that sample **never arrive** through the OTA
|
|
7
|
+
pipeline — not "arrive fragmented", not "arrive with loss". Messages that fit
|
|
8
|
+
inside the cap keep flowing at full rate on the same link, which is what makes
|
|
9
|
+
this dangerous: an 84 B heartbeat crossing at 10 Hz with 0.0% loss is a link
|
|
10
|
+
that looks alive from every direction except the one that matters.
|
|
11
|
+
|
|
12
|
+
The obvious reason to set it is symmetry with CycloneDDS, whose OTA profile
|
|
13
|
+
carries this link at `FragmentSize`/`MaxMessageSize` 1200 B and works. The
|
|
14
|
+
symmetry does not transfer: on ROS 2 Kilted with Fast DDS 3.2.4 the cap is the
|
|
15
|
+
single knob that breaks the link, at 1200 B and at 8192 B, with a synchronous
|
|
16
|
+
writer and with an asynchronous one.
|
|
17
|
+
|
|
18
|
+
## Setup
|
|
19
|
+
|
|
20
|
+
- Host pair / topology: one host, the packaged local smoke rig — two
|
|
21
|
+
communication containers on their own Docker network, split ROS domains
|
|
22
|
+
(peer 50, peer 51, OTA 52) with the stock `domain_bridge` between them. The
|
|
23
|
+
same failure was first seen across two machines over a VPN; one host
|
|
24
|
+
reproduces it, so nothing private is needed.
|
|
25
|
+
- Session: a two-peer session with a `com_msgs/msg/SizedPayload` stream at
|
|
26
|
+
12000 B and 10 Hz on the loaded direction plus the echo heartbeat, OTA QoS
|
|
27
|
+
best_effort / depth 1 / lifespan 0.7 (the deployed shape).
|
|
28
|
+
- rosotacom SHA: measured at `6e4b748`; re-runs record theirs.
|
|
29
|
+
- Profile: none for the bisect — the failure needs no impairment, which is what
|
|
30
|
+
makes it attributable to the configuration rather than to the link.
|
|
31
|
+
- Seed policy: deterministic (no netem, no random load), `n=1` per variant; the
|
|
32
|
+
effect is present/absent, not a distribution.
|
|
33
|
+
|
|
34
|
+
## Evidence
|
|
35
|
+
|
|
36
|
+
Evidence grade: single-host reproduction on packaged material, one variant per
|
|
37
|
+
run, read from the receiving peer's own `status.txt`.
|
|
38
|
+
|
|
39
|
+
Each knob of the tuned profile was added to the working `fastdds_unicast.xml`
|
|
40
|
+
one at a time (2026-08-19):
|
|
41
|
+
|
|
42
|
+
| added to fastdds_unicast.xml | 12 kB stream at the receiver |
|
|
43
|
+
|---|---|
|
|
44
|
+
| interface allowlist (127.0.0.1 + OTA address) | FLOWING 10.0 Hz, 0.0% loss |
|
|
45
|
+
| loopback locators (default / metatraffic / initial peers) | FLOWING 10.0 Hz |
|
|
46
|
+
| `maxInitialPeersRange` 10 | FLOWING 10.0 Hz |
|
|
47
|
+
| discovery lease / announcement | FLOWING 10.0 Hz |
|
|
48
|
+
| socket buffer sizes (1 MB / 4 MB) | FLOWING 10.0 Hz |
|
|
49
|
+
| `non_blocking_send` | FLOWING 10.0 Hz |
|
|
50
|
+
| `publishMode` ASYNCHRONOUS | FLOWING, degraded: 9.3 Hz at 285 ms |
|
|
51
|
+
| **`maxMessageSize` 1200** | **IDLE — nothing delivered** |
|
|
52
|
+
| **`maxMessageSize` 8192** | **IDLE — nothing delivered** |
|
|
53
|
+
| **`maxMessageSize` 1200 + ASYNCHRONOUS** | **IDLE — nothing delivered** |
|
|
54
|
+
|
|
55
|
+
The heartbeat crossed at 10 Hz with 0.0% loss in every one of those runs,
|
|
56
|
+
including the three that delivered no payload at all.
|
|
57
|
+
|
|
58
|
+
Two nearby observations, both of which cost time before the bisect narrowed it:
|
|
59
|
+
|
|
60
|
+
- Two Fast DDS participants in two containers, with the capped profile and
|
|
61
|
+
nothing else running, DO exchange 12 kB and 38 kB samples (57–59 of 60). The
|
|
62
|
+
cap only breaks the full pipeline, where the sample crosses a domain bridge
|
|
63
|
+
and an OTA bridge, so an isolated transport probe will report the cap as
|
|
64
|
+
harmless.
|
|
65
|
+
- Asynchronous publication does not rescue it, which rules out the documented
|
|
66
|
+
"a synchronous writer cannot fragment" explanation as the whole story.
|
|
67
|
+
|
|
68
|
+
Verification: manual: run the packaged smoke for a session whose OTA side names
|
|
69
|
+
`fastdds_tuned.xml`, then again for one that adds `<maxMessageSize>1200` to it,
|
|
70
|
+
and read `logs/<receiver>/status/status.txt`; the payload stage is FLOWING in
|
|
71
|
+
the first and IDLE in the second while the heartbeat is FLOWING in both. The
|
|
72
|
+
shipped profiles are pinned against the cap by
|
|
73
|
+
`tests/unit/test_ota_link_configs.py::test_only_cyclone_caps_the_datagram_and_fast_dds_must_not`.
|
|
74
|
+
|
|
75
|
+
## Status
|
|
76
|
+
|
|
77
|
+
confirmed, 2026-08-19.
|
|
78
|
+
|
|
79
|
+
## Publication notes
|
|
80
|
+
|
|
81
|
+
This is why "the OTA middleware is a configuration choice" needs measuring
|
|
82
|
+
rather than reasoning: the two profiles that look like translations of each
|
|
83
|
+
other are not, and the failure mode of the wrong translation is a link that
|
|
84
|
+
passes a heartbeat check. Consequence for the transport comparison: Fast DDS
|
|
85
|
+
lets IP fragment a large sample here where CycloneDDS sends RTPS fragments, so
|
|
86
|
+
the two differ in what a lost packet costs and in what the tunnel has to
|
|
87
|
+
reassemble — the measured OpenVPN barrier on the operator's path is around
|
|
88
|
+
110 kB per message. Report Fast DDS numbers with that difference stated, not as
|
|
89
|
+
a like-for-like transport swap.
|
|
@@ -0,0 +1,79 @@
|
|
|
1
|
+
# A Bridge That Learns Types From The Graph Cannot Use A Transport That Does Not Carry The Graph
|
|
2
|
+
|
|
3
|
+
## Claim
|
|
4
|
+
|
|
5
|
+
`PubSubPair` resolved a stage's message type from
|
|
6
|
+
`get_topic_names_and_types()`, so it could only create its endpoints once
|
|
7
|
+
somebody else had created the matching one. On a DDS OTA link that is invisible
|
|
8
|
+
— the remote writer is in the same domain, so the graph knows it. Over the
|
|
9
|
+
zenoh DDS bridge (`rmw.ota: zenoh_ros2dds`) it is a deadlock: the bridge
|
|
10
|
+
creates a subscriber route for a topic only once a **local DDS reader** exists,
|
|
11
|
+
and the reader was waiting for the bridge to route a publisher into the graph.
|
|
12
|
+
Neither ever happens, the payload topic never arrives, and the only trace is one
|
|
13
|
+
word in a log line — `Initialized 1 pair(s). Pending=1`.
|
|
14
|
+
|
|
15
|
+
The heartbeat escapes it because the receiving peer's bridge sees the remote
|
|
16
|
+
liveliness token for it early enough to create the route before the reader is
|
|
17
|
+
needed; the wrapped payload topic does not. So the observable is again a link
|
|
18
|
+
that carries the small topic and silently drops the big one.
|
|
19
|
+
|
|
20
|
+
## Setup
|
|
21
|
+
|
|
22
|
+
- Host pair / topology: two machines over a VPN, `rmw.ota: zenoh_ros2dds`
|
|
23
|
+
(local cyclone), split ROS domains, one 12 kB `SizedPayload` stream at 10 Hz
|
|
24
|
+
plus the echo heartbeat.
|
|
25
|
+
- rosotacom SHA: observed at `6e4b748` (before the fix), fixed in `48d84dc`.
|
|
26
|
+
- Profile: the drive-derived loss process; the effect is not a loss effect and
|
|
27
|
+
reproduced identically in both runs under it.
|
|
28
|
+
- Seed policy: not applicable — present/absent, `n=2` runs, both failing the
|
|
29
|
+
same way.
|
|
30
|
+
|
|
31
|
+
## Evidence
|
|
32
|
+
|
|
33
|
+
Evidence grade: two live two-machine runs, read from the receiving peer's node
|
|
34
|
+
logs and from the zenoh bridge's own routing log.
|
|
35
|
+
|
|
36
|
+
Receiving peer, `bridge_in` (2026-08-19):
|
|
37
|
+
|
|
38
|
+
```
|
|
39
|
+
[PubSubPair] Created: sub='/ota/b/heartbeat_b' ...
|
|
40
|
+
[PubSubPair] Created: sub='/ota/b/cam_like/ota_stamped' ...
|
|
41
|
+
[PubSubPair] Initialized: sub='/ota/b/heartbeat_b' type='com_msgs/msg/EchoHeartbeat'
|
|
42
|
+
[bridge_in] Initialized 1 pair(s). Pending=1
|
|
43
|
+
```
|
|
44
|
+
|
|
45
|
+
Receiving peer, `zenoh_bridge_ros2dds`, for the whole run:
|
|
46
|
+
|
|
47
|
+
```
|
|
48
|
+
routes_mgr: Route Subscriber (Zenoh:ota/b/heartbeat_b -> ROS:/ota/b/heartbeat_b) created
|
|
49
|
+
```
|
|
50
|
+
|
|
51
|
+
— and no such line for `ota/b/cam_like/ota_stamped`, while the sending peer had
|
|
52
|
+
created its publisher route for exactly that key. Delivered payload messages in
|
|
53
|
+
both runs: 0 of ~810 sent.
|
|
54
|
+
|
|
55
|
+
The fix removes the dependency rather than the symptom: every session now writes
|
|
56
|
+
`<peer>/topic_types.yaml` (`<stage topic>: <message type>` for every stage that
|
|
57
|
+
peer takes part in, derived from the same pipeline description the status node
|
|
58
|
+
uses), the bridge and relay nodes read it, and the graph is consulted only for
|
|
59
|
+
what the file does not name.
|
|
60
|
+
|
|
61
|
+
Verification: `python -m pytest -q tests/unit/test_pub_sub_pair_types.py` — a
|
|
62
|
+
pair with a declared type creates its endpoints against an empty graph, a pair
|
|
63
|
+
without one still reads the graph, and a pair with neither stays pending.
|
|
64
|
+
`tests/unit/test_ota_link_configs.py::test_every_peer_gets_the_stage_types_its_bridges_need`
|
|
65
|
+
pins that the file is generated for every peer of every session.
|
|
66
|
+
|
|
67
|
+
## Status
|
|
68
|
+
|
|
69
|
+
confirmed, 2026-08-19.
|
|
70
|
+
|
|
71
|
+
## Publication notes
|
|
72
|
+
|
|
73
|
+
Worth stating in any "the transport is a configuration choice" claim: a
|
|
74
|
+
pipeline can depend on a transport property nobody wrote down — here, that the
|
|
75
|
+
transport propagates the ROS graph and not only the data. DDS does, the zenoh
|
|
76
|
+
DDS bridge does not, and the dependency only shows up as a topic that never
|
|
77
|
+
arrives. The general lesson is that a contract known at generation time should
|
|
78
|
+
be handed to the runtime rather than rediscovered from the runtime, which is
|
|
79
|
+
also what makes bring-up order-independent.
|
{rosotacom_dev-2.5.dev50 → rosotacom_dev-2.5.dev52}/docs/findings/head-of-line-irregularity.md
RENAMED
|
@@ -45,13 +45,23 @@ The mechanism is visible only per message: the stream carrying alternating
|
|
|
45
45
|
heavy/light samples can lose more than the steady stream despite lower mean
|
|
46
46
|
payload bandwidth.
|
|
47
47
|
|
|
48
|
+
Field recurrence (2026-08-17 CCNG drive): the DELAY half of the mechanism
|
|
49
|
+
recurred and was measured per message — a small message with more than 30 KB
|
|
50
|
+
in flight ahead of it paid +17.4 ms p50 / +67 ms p90, a small message sent
|
|
51
|
+
within 40 ms after a video keyframe paid p50 42.1 vs 28.4 ms, and 18.4% of
|
|
52
|
+
cross-topic pairs sent within 2 ms flipped order after a >20 KB first message.
|
|
53
|
+
The LOSS half (delayed-then-overwritten) did not trigger there: it needs a
|
|
54
|
+
queue tight against the offered load, and that link had ~14 Mbit/s
|
|
55
|
+
serialization headroom at 2.5 Mbit/s offered. Both halves stay reproducible
|
|
56
|
+
with the tight profile above.
|
|
57
|
+
|
|
48
58
|
Verification: manual: run the two commands above from a source checkout with
|
|
49
59
|
Docker and `tc` privileges; automation waits for the RFC 0007 regression matrix
|
|
50
60
|
row for patterned loads because the public row registry does not exist yet.
|
|
51
61
|
|
|
52
62
|
## Status
|
|
53
63
|
|
|
54
|
-
confirmed, 2026-
|
|
64
|
+
confirmed, 2026-08-18.
|
|
55
65
|
|
|
56
66
|
## Publication notes
|
|
57
67
|
|
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
# A Lost Keyframe Silences Exactly The Next gop_size−2 Deltas
|
|
2
|
+
|
|
3
|
+
## Claim
|
|
4
|
+
|
|
5
|
+
In a best-effort libx264 stream (ABR, `tune: zerolatency`, fixed GOP), losing
|
|
6
|
+
a KEYFRAME makes exactly the next `gop_size − 2` arrived deltas undecodable —
|
|
7
|
+
libavcodec emits **no frame at all** for them — the last delta before the next
|
|
8
|
+
keyframe decodes with concealment artifacts, and the next keyframe restores
|
|
9
|
+
clean decode. Losing a DELTA never blocks decode: every following access unit
|
|
10
|
+
still produces a frame (artifacts until the next keyframe, never a gap). The
|
|
11
|
+
behaviour is deterministic — not timing, not decoder state noise — so the
|
|
12
|
+
display hole a keyframe loss tears is `gop_size × frame_period`, which is what
|
|
13
|
+
makes the GOP length the knob that decides whether one lost packet crosses an
|
|
14
|
+
operator-visible threshold.
|
|
15
|
+
|
|
16
|
+
## Setup
|
|
17
|
+
|
|
18
|
+
- Host pair / topology: single host, single process, below rosotacom at the
|
|
19
|
+
codec layer — encode with PyAV/libx264 exactly as the ffmpeg transport does,
|
|
20
|
+
delete single access units explicitly, feed the survivors to a fresh
|
|
21
|
+
libavcodec h264 decoder (what a best-effort receiver does after a loss).
|
|
22
|
+
- rosotacom SHA: measured at `73fa1de`; re-runs record theirs.
|
|
23
|
+
- Profile: none — no network is involved; the deletion IS the loss, which is
|
|
24
|
+
what makes the rule attributable to the codec rather than the transport.
|
|
25
|
+
- Seed policy: deterministic synthetic motion clip, `n=1` (the assertions are
|
|
26
|
+
exact-set comparisons, not statistics).
|
|
27
|
+
|
|
28
|
+
## Evidence
|
|
29
|
+
|
|
30
|
+
Evidence grade: scripted single-host reproduction, committed next to this file.
|
|
31
|
+
|
|
32
|
+
```bash
|
|
33
|
+
python3 docs/findings/repro/decode_chain_repro.py --gop 5 # needs: pip install av numpy
|
|
34
|
+
```
|
|
35
|
+
|
|
36
|
+
2026-08-18 runs (synthetic clip, libx264 1 Mbit/s ABR, zerolatency, 10 Hz):
|
|
37
|
+
|
|
38
|
+
| gop | deleted | silent AUs after it | first output after the gap |
|
|
39
|
+
|---|---|---|---|
|
|
40
|
+
| 5 | keyframe | exactly 3 (= gop−2) | last delta of the GOP, with artifacts |
|
|
41
|
+
| 3 | keyframe | exactly 1 (= gop−2) | last delta of the GOP, with artifacts |
|
|
42
|
+
| 2 | keyframe | none (= gop−2 = 0) | the following delta, with artifacts |
|
|
43
|
+
| any | one delta | none | every AU keeps decoding, artifacts to next KF |
|
|
44
|
+
|
|
45
|
+
The same rule was first measured on real field material (2026-08-17 CCNG
|
|
46
|
+
drive, camera FFMPEGPacket stream at gop 5: every lost keyframe silenced the
|
|
47
|
+
next 3 arrived deltas, ~400 ms holes at 10 Hz; a `refs=1` encode does NOT
|
|
48
|
+
shorten the chain — tested and rejected). The synthetic clip reproduces it
|
|
49
|
+
exactly, so the public repro carries the claim without any private bag.
|
|
50
|
+
|
|
51
|
+
Verification: manual: run the command above (per `--gop 5`, `--gop 3`,
|
|
52
|
+
`--gop 2`); the script asserts the exact silent set and exits non-zero on any
|
|
53
|
+
deviation.
|
|
54
|
+
|
|
55
|
+
## Status
|
|
56
|
+
|
|
57
|
+
confirmed, 2026-08-18.
|
|
58
|
+
|
|
59
|
+
## Publication notes
|
|
60
|
+
|
|
61
|
+
This is the quantitative bridge from "loss percent" to "operator-visible
|
|
62
|
+
hole": camera loss numbers mean nothing next to costmap loss numbers until
|
|
63
|
+
multiplied by the GOP geometry. It drove the 2026-08-17 field configuration
|
|
64
|
+
change gop 5 → 3 (hole ≤ 300 ms at equal measured bitrate). For papers: plot
|
|
65
|
+
hole duration vs GOP at fixed measured bitrate, and state that intra-refresh
|
|
66
|
+
trades the hole for a several-frame artifact window instead (no silent AUs,
|
|
67
|
+
lower clean-frame share). See also
|
|
68
|
+
[docs/ffmpeg-keyframes.md](../ffmpeg-keyframes.md) for reading keyframe flags
|
|
69
|
+
out of recorded transit records.
|
|
@@ -0,0 +1,81 @@
|
|
|
1
|
+
# Oversubscription Queues — It Does Not Lose Until The Queue Itself Dies
|
|
2
|
+
|
|
3
|
+
## Claim
|
|
4
|
+
|
|
5
|
+
Offered load slightly above a rate cap turns entirely into queueing delay, not
|
|
6
|
+
loss: one-way delay climbs by hundreds of milliseconds per second of
|
|
7
|
+
oversubscription — into the seconds — while per-bin loss stays exactly zero.
|
|
8
|
+
Loss appears only when the queue itself is destroyed (in emulation: a
|
|
9
|
+
shape-changing timeline step; in the field: a bearer/modem reset), and then it
|
|
10
|
+
is the whole backlog at once. Consequences: loss statistics alone certify a
|
|
11
|
+
badly broken link as healthy, delay-based failure precedes loss-based failure,
|
|
12
|
+
and a recovering link replays a burst of stale messages unless a lifespan
|
|
13
|
+
bounds them.
|
|
14
|
+
|
|
15
|
+
## Setup
|
|
16
|
+
|
|
17
|
+
- Host pair / topology: rosotacom local benchmark mode using the packaged
|
|
18
|
+
example project, synthetic `bench_1_1_capacity` session, one
|
|
19
|
+
`a->b:/bench_capacity` stream, uplink shaping only (tc inside the peer
|
|
20
|
+
containers' own netns — no host privileges).
|
|
21
|
+
- rosotacom SHA: measured at `73fa1de`; public re-runs record the current
|
|
22
|
+
checkout SHA in `result.json.context`.
|
|
23
|
+
- Profile: `field-20260817-case3-delay-jam` in
|
|
24
|
+
[`src/rosotacom/resources/examples/profiles.yaml`](../../src/rosotacom/resources/examples/profiles.yaml)
|
|
25
|
+
— 10 s clean, 15 s `rate: 900kbit` (the crunch), 20 s clean; fitted to the
|
|
26
|
+
2026-08-17 CCNG drive's case3 window (in-flight queueing to 1.4 s at almost
|
|
27
|
+
no loss).
|
|
28
|
+
- Seed policy: no stochastic netem elements (rate/delay only), `n=1`.
|
|
29
|
+
|
|
30
|
+
## Evidence
|
|
31
|
+
|
|
32
|
+
Evidence grade: packaged-profile benchmark run plus per-bin timeline.
|
|
33
|
+
|
|
34
|
+
```bash
|
|
35
|
+
rosotacom benchmark probe --project src/rosotacom/resources/examples/rosotacom.yaml --profile field-20260817-case3-delay-jam --size-pattern 1x13KB --rate-hz 10 --duration 45 --repeats 1 --no-plot
|
|
36
|
+
```
|
|
37
|
+
|
|
38
|
+
2026-08-18 run (offered 1.04 Mbit/s against the 15 s 900 kbit crunch — 16%
|
|
39
|
+
oversubscription): twelve consecutive 1 s bins during the crunch show p95
|
|
40
|
+
one-way latency climbing 340 → 601 → 880 → 1117 → 1420 → 1684 → 1944 → 2208 →
|
|
41
|
+
2469 → 2704 → 3009 → **3250 ms with `lost = 0` in every single bin** — the
|
|
42
|
+
queue (netem child, default limit 1000 packets) absorbs the entire excess.
|
|
43
|
+
All 27 lost messages (5.9% of the run) sit in the three bins at the
|
|
44
|
+
crunch→clean boundary, where the timeline step replaces a tbf+netem tree with
|
|
45
|
+
a netem-only tree and the queued ~2.7 s backlog dies with the old queue.
|
|
46
|
+
|
|
47
|
+
Two readings beyond the headline: (a) a loss-based health verdict over the
|
|
48
|
+
crunch window reads "0% loss" while the operator's picture is three seconds
|
|
49
|
+
old and aging — delay-based failure criteria fire long before loss-based
|
|
50
|
+
ones; (b) the backlog-drop at the boundary is the emulated twin of a bearer
|
|
51
|
+
reset, and it is also the caveat's edge: `tc qdisc replace` steps are seamless
|
|
52
|
+
only between same-shape trees — a step that adds or removes `rate` swaps the
|
|
53
|
+
tree and drops its queue.
|
|
54
|
+
|
|
55
|
+
Field recurrence (2026-08-17 CCNG drive): five jam episodes with one-way
|
|
56
|
+
delay 401–459 ms at zero losses; the case3 window queued to 1.4 s in-flight
|
|
57
|
+
with almost no loss; and the drive's last minute ran p90 ≈ 138 ms delay at
|
|
58
|
+
0.04 losses/s — the same regime at lower intensity. A 2026-08-18 container
|
|
59
|
+
replay of a 9% oversubscription reached 3.66 s delay at exactly 0% loss.
|
|
60
|
+
|
|
61
|
+
Verification: manual: run the command above from a source checkout with
|
|
62
|
+
Docker; read the per-bin `time-bins.jsonl` next to `result.json` — the crunch
|
|
63
|
+
bins must show monotonically climbing latency at zero loss, with the losses
|
|
64
|
+
(if any) confined to the boundary bins.
|
|
65
|
+
|
|
66
|
+
## Status
|
|
67
|
+
|
|
68
|
+
confirmed, 2026-08-18.
|
|
69
|
+
|
|
70
|
+
## Publication notes
|
|
71
|
+
|
|
72
|
+
This is the mechanism behind
|
|
73
|
+
[delay-alone-no-loss.md](delay-alone-no-loss.md)'s negative control seen from
|
|
74
|
+
the other side: delay alone does not lose, and oversubscription alone does not
|
|
75
|
+
lose either — it defers. Pair with
|
|
76
|
+
[reorder-becomes-reader-loss.md](reorder-becomes-reader-loss.md) for the two
|
|
77
|
+
ways "the network was fine, loss says so" misleads. For papers: plot per-bin
|
|
78
|
+
delay and loss on one timeline; the visual is a delay ramp under a flat zero
|
|
79
|
+
loss line, ending in a loss spike exactly when the queue dies. Lifespan QoS
|
|
80
|
+
(and its enforcement gap — writer-side only in CycloneDDS) decides what the
|
|
81
|
+
recovery burst does to the application.
|
|
@@ -0,0 +1,126 @@
|
|
|
1
|
+
#!/usr/bin/env python3
|
|
2
|
+
"""Reproduce the keyframe decode-chain rule of a best-effort libx264 stream.
|
|
3
|
+
|
|
4
|
+
Encodes a deterministic synthetic motion clip the way the ffmpeg transport
|
|
5
|
+
does (libx264, ABR, ``tune: zerolatency``, fixed GOP), then deletes single
|
|
6
|
+
access units and feeds the survivors to a fresh libavcodec h264 decoder --
|
|
7
|
+
which is exactly what a best-effort receiver does after a network loss.
|
|
8
|
+
|
|
9
|
+
Measured rule (docs/findings/keyframe-loss-kills-the-next-deltas.md):
|
|
10
|
+
|
|
11
|
+
* delete a KEYFRAME -> the next ``gop_size - 2`` arrived deltas produce NO
|
|
12
|
+
output at all, the last delta before the next keyframe decodes with
|
|
13
|
+
concealment artifacts, the next keyframe restores clean decode;
|
|
14
|
+
* delete a DELTA -> every following access unit still decodes (artifacts
|
|
15
|
+
until the next keyframe, never a gap).
|
|
16
|
+
|
|
17
|
+
Usage (needs ``pip install av numpy``; no network, no privileges)::
|
|
18
|
+
|
|
19
|
+
python3 docs/findings/repro/decode_chain_repro.py [--gop 5] [--frames 40]
|
|
20
|
+
|
|
21
|
+
Prints the per-AU decode table for both deletions and exits non-zero if the
|
|
22
|
+
rule above does not hold.
|
|
23
|
+
"""
|
|
24
|
+
|
|
25
|
+
from __future__ import annotations
|
|
26
|
+
|
|
27
|
+
import argparse
|
|
28
|
+
import sys
|
|
29
|
+
from fractions import Fraction
|
|
30
|
+
|
|
31
|
+
import av
|
|
32
|
+
import numpy as np
|
|
33
|
+
|
|
34
|
+
|
|
35
|
+
def synthetic_frames(count: int, width: int = 320, height: int = 240):
|
|
36
|
+
"""Deterministic motion: drifting background plus a moving block."""
|
|
37
|
+
for index in range(count):
|
|
38
|
+
image = np.full((height, width, 3), (index * 3) % 200, dtype=np.uint8)
|
|
39
|
+
x = (index * 7) % (width - 40)
|
|
40
|
+
image[60:180, x : x + 40] = 255
|
|
41
|
+
frame = av.VideoFrame.from_ndarray(image, format="rgb24")
|
|
42
|
+
yield frame.reformat(format="yuv420p")
|
|
43
|
+
|
|
44
|
+
|
|
45
|
+
def encode(gop: int, count: int) -> list[tuple[bytes, bool]]:
|
|
46
|
+
codec = av.CodecContext.create("libx264", "w")
|
|
47
|
+
codec.width, codec.height = 320, 240
|
|
48
|
+
codec.pix_fmt = "yuv420p"
|
|
49
|
+
codec.framerate = Fraction(10, 1)
|
|
50
|
+
codec.bit_rate = 1_000_000
|
|
51
|
+
codec.options = {"tune": "zerolatency", "g": str(gop)}
|
|
52
|
+
packets: list[tuple[bytes, bool]] = []
|
|
53
|
+
for frame in synthetic_frames(count):
|
|
54
|
+
for packet in codec.encode(frame):
|
|
55
|
+
packets.append((bytes(packet), bool(packet.is_keyframe)))
|
|
56
|
+
for packet in codec.encode(None):
|
|
57
|
+
packets.append((bytes(packet), bool(packet.is_keyframe)))
|
|
58
|
+
return packets
|
|
59
|
+
|
|
60
|
+
|
|
61
|
+
def decode_survivors(packets: list[tuple[bytes, bool]], deleted: set[int]) -> list[tuple[int, int]]:
|
|
62
|
+
"""Feed every surviving AU to a fresh decoder; return (au_index, frames_out)."""
|
|
63
|
+
decoder = av.CodecContext.create("h264", "r")
|
|
64
|
+
out: list[tuple[int, int]] = []
|
|
65
|
+
for index, (payload, _kf) in enumerate(packets):
|
|
66
|
+
if index in deleted:
|
|
67
|
+
continue
|
|
68
|
+
try:
|
|
69
|
+
frames = decoder.decode(av.Packet(payload))
|
|
70
|
+
except av.error.InvalidDataError:
|
|
71
|
+
frames = []
|
|
72
|
+
out.append((index, len(frames)))
|
|
73
|
+
return out
|
|
74
|
+
|
|
75
|
+
|
|
76
|
+
def main() -> int:
|
|
77
|
+
parser = argparse.ArgumentParser(description=__doc__)
|
|
78
|
+
parser.add_argument("--gop", type=int, default=5)
|
|
79
|
+
parser.add_argument("--frames", type=int, default=40)
|
|
80
|
+
args = parser.parse_args()
|
|
81
|
+
|
|
82
|
+
packets = encode(args.gop, args.frames)
|
|
83
|
+
kf_indices = [i for i, (_p, kf) in enumerate(packets) if kf]
|
|
84
|
+
if len(kf_indices) < 3:
|
|
85
|
+
print("clip too short to delete a mid-stream keyframe", file=sys.stderr)
|
|
86
|
+
return 2
|
|
87
|
+
|
|
88
|
+
baseline = decode_survivors(packets, set())
|
|
89
|
+
silent_baseline = [i for i, n in baseline if n == 0]
|
|
90
|
+
|
|
91
|
+
failures: list[str] = []
|
|
92
|
+
|
|
93
|
+
# --- delete the second keyframe -------------------------------------
|
|
94
|
+
kf = kf_indices[1]
|
|
95
|
+
next_kf = kf_indices[2]
|
|
96
|
+
rows = decode_survivors(packets, {kf})
|
|
97
|
+
silent = [i for i, n in rows if n == 0 and i not in silent_baseline]
|
|
98
|
+
deltas_in_gap = [i for i in range(kf + 1, next_kf)]
|
|
99
|
+
expected_silent = deltas_in_gap[: max(0, args.gop - 2)]
|
|
100
|
+
print(f"deleted keyframe AU {kf} (gop {args.gop}, next keyframe {next_kf})")
|
|
101
|
+
print(f" silent AUs: {silent}")
|
|
102
|
+
print(f" expected : {expected_silent} (the next gop_size-2 deltas)")
|
|
103
|
+
if silent != expected_silent:
|
|
104
|
+
failures.append(f"keyframe deletion: silent AUs {silent} != expected {expected_silent}")
|
|
105
|
+
resumed = [i for i, n in rows if i >= next_kf and n == 0]
|
|
106
|
+
if resumed:
|
|
107
|
+
failures.append(f"decode did not resume at the next keyframe: silent {resumed}")
|
|
108
|
+
|
|
109
|
+
# --- delete one delta ------------------------------------------------
|
|
110
|
+
delta = kf_indices[1] + 1
|
|
111
|
+
rows = decode_survivors(packets, {delta})
|
|
112
|
+
silent = [i for i, n in rows if n == 0 and i not in silent_baseline]
|
|
113
|
+
print(f"deleted delta AU {delta}")
|
|
114
|
+
print(f" silent AUs: {silent} (must be empty -- artifacts, never a gap)")
|
|
115
|
+
if silent:
|
|
116
|
+
failures.append(f"delta deletion silenced AUs {silent}; expected none")
|
|
117
|
+
|
|
118
|
+
if failures:
|
|
119
|
+
print("FINDING VIOLATED:", *failures, sep="\n ", file=sys.stderr)
|
|
120
|
+
return 1
|
|
121
|
+
print("ok: keyframe loss silences exactly the next gop_size-2 deltas; delta loss silences nothing")
|
|
122
|
+
return 0
|
|
123
|
+
|
|
124
|
+
|
|
125
|
+
if __name__ == "__main__":
|
|
126
|
+
sys.exit(main())
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
# Sender-Side Transit Rows Made A Dead Link Report Zero Loss
|
|
2
|
+
|
|
3
|
+
## Claim
|
|
4
|
+
|
|
5
|
+
`summarize_transit_records` counted `delivered = rows − lost`. Sender-side
|
|
6
|
+
transit rows carry the status `sent` and are written whether or not anything
|
|
7
|
+
arrives, so a topic that the receiving peer never saw at all summarised as
|
|
8
|
+
`delivered == expected`, `loss_pct 0.0` — with the sender's own publication
|
|
9
|
+
cadence reported as the arrival spacing. The most dangerous shape a wrong number
|
|
10
|
+
can take: a link that delivered nothing scored better than one that delivered
|
|
11
|
+
96% of its messages.
|
|
12
|
+
|
|
13
|
+
Delivery is a receiver fact. Rows still `sent` after the join are `unconfirmed`;
|
|
14
|
+
`loss_pct` is `None` when no receiver accounted for the topic at all; and an
|
|
15
|
+
oracle reads that `None` as the worst case rather than as zero.
|
|
16
|
+
|
|
17
|
+
## Setup
|
|
18
|
+
|
|
19
|
+
- Host pair / topology: not a transport property — the summary is a pure
|
|
20
|
+
function of collected records, so the reproduction is a unit test over
|
|
21
|
+
synthetic rows. It was found on a two-machine run whose OTA transport
|
|
22
|
+
delivered nothing.
|
|
23
|
+
- rosotacom SHA: present up to `6e4b748`, fixed in `730ddf5`.
|
|
24
|
+
- Profile: none — the defect is in the accounting, not in the link.
|
|
25
|
+
- Seed policy: not applicable, deterministic.
|
|
26
|
+
|
|
27
|
+
## Evidence
|
|
28
|
+
|
|
29
|
+
Evidence grade: unit-level, deterministic, plus the live run that exposed it.
|
|
30
|
+
|
|
31
|
+
Live (2026-08-19, `rmw.ota: zenoh_ros2dds` over two machines): the sending peer
|
|
32
|
+
wrote 816 `outbound`/`sent` rows, the receiving peer wrote 5 rows and no
|
|
33
|
+
delivery at all. The run's `result.json` reported `delivered 807, expected 807,
|
|
34
|
+
loss_pct 0.0` for that topic, with inter-arrival p05/p95 of 102/104 ms — the
|
|
35
|
+
publisher's own timer, not an arrival distribution. The receiver's `status.txt`
|
|
36
|
+
for the same run said `ota_recv ABSENT/GRAPH pub=0` and `com_in IDLE`.
|
|
37
|
+
|
|
38
|
+
The window mattered as much as the number: this only became reachable when
|
|
39
|
+
sender-side transit rows landed (issue #294). Before that, a topic with no
|
|
40
|
+
receiver rows had no rows at all and simply did not appear.
|
|
41
|
+
|
|
42
|
+
Verification: `python -m pytest -q tests/unit/test_transit.py -k sender_rows`
|
|
43
|
+
— sender rows alone report `delivered 0`, `unconfirmed 3` and `loss_pct None`,
|
|
44
|
+
and a mixed set counts only the receiver's verdicts.
|
|
45
|
+
`tests/unit/test_benchmark.py::test_an_unknown_loss_figure_is_read_as_the_worst_case`
|
|
46
|
+
pins that an oracle does not pass on the `None`.
|
|
47
|
+
|
|
48
|
+
## Status
|
|
49
|
+
|
|
50
|
+
confirmed, 2026-08-19.
|
|
51
|
+
|
|
52
|
+
## Publication notes
|
|
53
|
+
|
|
54
|
+
A measurement pipeline that gains a second observer gains a way to be
|
|
55
|
+
confidently wrong: the new rows were real, the join was correct, and the
|
|
56
|
+
aggregate stopped meaning what its name said. Worth citing whenever a loss
|
|
57
|
+
figure is quoted from an automated run — state which side observed it. The
|
|
58
|
+
general rule the fix encodes: an aggregate over rows from two observers must say
|
|
59
|
+
which observer each row came from before it counts anything.
|