project-ghost 0.1.1__py3-none-any.whl
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.
- project_ghost/__init__.py +10 -0
- project_ghost/actuators/__init__.py +4 -0
- project_ghost/analysis/__init__.py +233 -0
- project_ghost/analysis/belief_consistency.py +318 -0
- project_ghost/analysis/belief_traceability.py +370 -0
- project_ghost/analysis/calibration.py +425 -0
- project_ghost/analysis/comparison.py +702 -0
- project_ghost/analysis/decision_trace.py +534 -0
- project_ghost/analysis/models.py +80 -0
- project_ghost/analysis/report.py +69 -0
- project_ghost/analysis/self_assessment.py +345 -0
- project_ghost/analysis/summary.py +203 -0
- project_ghost/app/__init__.py +25 -0
- project_ghost/app/main.py +1802 -0
- project_ghost/cli.py +1227 -0
- project_ghost/core/__init__.py +15 -0
- project_ghost/core/actuation/__init__.py +43 -0
- project_ghost/core/actuation/attitude_hold_policy.py +152 -0
- project_ghost/core/actuation/orchestration.py +37 -0
- project_ghost/core/actuation/protocols.py +68 -0
- project_ghost/core/actuation/reference_policy.py +80 -0
- project_ghost/core/actuation/sinks.py +53 -0
- project_ghost/core/actuation/types.py +125 -0
- project_ghost/core/clock/__init__.py +53 -0
- project_ghost/core/clock/error_sink.py +78 -0
- project_ghost/core/clock/random_source.py +109 -0
- project_ghost/core/clock/sim_clock.py +225 -0
- project_ghost/core/clock/types.py +101 -0
- project_ghost/core/decisions/__init__.py +59 -0
- project_ghost/core/decisions/orchestration.py +108 -0
- project_ghost/core/decisions/protocols.py +67 -0
- project_ghost/core/decisions/reference_policy.py +110 -0
- project_ghost/core/decisions/sinks.py +66 -0
- project_ghost/core/decisions/types.py +286 -0
- project_ghost/core/feedback/__init__.py +44 -0
- project_ghost/core/feedback/orchestration.py +107 -0
- project_ghost/core/feedback/protocols.py +52 -0
- project_ghost/core/feedback/reference_policy.py +142 -0
- project_ghost/core/feedback/types.py +237 -0
- project_ghost/core/fusion/__init__.py +44 -0
- project_ghost/core/fusion/orchestration.py +28 -0
- project_ghost/core/fusion/protocols.py +46 -0
- project_ghost/core/fusion/reference_policy.py +189 -0
- project_ghost/core/fusion/sinks.py +39 -0
- project_ghost/core/fusion/types.py +191 -0
- project_ghost/core/prediction/__init__.py +56 -0
- project_ghost/core/prediction/divergence.py +345 -0
- project_ghost/core/prediction/orchestration.py +40 -0
- project_ghost/core/prediction/protocols.py +74 -0
- project_ghost/core/prediction/reference_predictor.py +134 -0
- project_ghost/core/prediction/sinks.py +53 -0
- project_ghost/core/prediction/types.py +205 -0
- project_ghost/core/uncertainty/__init__.py +94 -0
- project_ghost/core/uncertainty/composition.py +110 -0
- project_ghost/core/uncertainty/estimate.py +227 -0
- project_ghost/core/uncertainty/inflation.py +136 -0
- project_ghost/core/uncertainty/mode_detector.py +411 -0
- project_ghost/core/uncertainty/mode_events.py +123 -0
- project_ghost/core/uncertainty/sealing.py +116 -0
- project_ghost/core/uncertainty/self_assessment.py +469 -0
- project_ghost/core/uncertainty/types.py +182 -0
- project_ghost/estimation/__init__.py +30 -0
- project_ghost/estimation/config.py +139 -0
- project_ghost/estimation/noisy_gt.py +267 -0
- project_ghost/events/__init__.py +56 -0
- project_ghost/events/adapters.py +100 -0
- project_ghost/events/bus.py +269 -0
- project_ghost/events/types.py +136 -0
- project_ghost/examples/__init__.py +24 -0
- project_ghost/examples/closed_loop_smoke.py +397 -0
- project_ghost/examples/replay_verification.py +377 -0
- project_ghost/hal/__init__.py +39 -0
- project_ghost/hal/messages/__init__.py +88 -0
- project_ghost/hal/messages/actuators.py +506 -0
- project_ghost/hal/messages/runtime.py +274 -0
- project_ghost/hal/messages/sensors.py +486 -0
- project_ghost/hal/protocols.py +158 -0
- project_ghost/properties/__init__.py +79 -0
- project_ghost/properties/baud.py +468 -0
- project_ghost/properties/erur.py +410 -0
- project_ghost/properties/fpb.py +342 -0
- project_ghost/properties/md.py +248 -0
- project_ghost/properties/rlb.py +285 -0
- project_ghost/py.typed +0 -0
- project_ghost/sensors/__init__.py +4 -0
- project_ghost/simulation/__init__.py +8 -0
- project_ghost/state/__init__.py +56 -0
- project_ghost/state/aggregator.py +149 -0
- project_ghost/state/messages.py +466 -0
- project_ghost/state/transforms.py +230 -0
- project_ghost/telemetry/__init__.py +110 -0
- project_ghost/telemetry/adapters.py +565 -0
- project_ghost/telemetry/channels.py +110 -0
- project_ghost/telemetry/mcap_sink.py +169 -0
- project_ghost/telemetry/replay.py +334 -0
- project_ghost/telemetry/serialization.py +273 -0
- project_ghost/telemetry/sink.py +96 -0
- project_ghost/traceability/__init__.py +50 -0
- project_ghost/traceability/analysis.py +279 -0
- project_ghost/traceability/models.py +101 -0
- project_ghost-0.1.1.dist-info/METADATA +262 -0
- project_ghost-0.1.1.dist-info/RECORD +105 -0
- project_ghost-0.1.1.dist-info/WHEEL +4 -0
- project_ghost-0.1.1.dist-info/entry_points.txt +3 -0
- project_ghost-0.1.1.dist-info/licenses/LICENSE +201 -0
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
"""Project Ghost — autonomous GPS-denied drone navigation.
|
|
2
|
+
|
|
3
|
+
This package is intentionally empty in Fase 0. Module implementations land
|
|
4
|
+
in Fase 1 according to `docs/roadmaps/phase1.md`.
|
|
5
|
+
"""
|
|
6
|
+
|
|
7
|
+
from __future__ import annotations
|
|
8
|
+
|
|
9
|
+
__version__ = "0.0.1"
|
|
10
|
+
__all__ = ["__version__"]
|
|
@@ -0,0 +1,233 @@
|
|
|
1
|
+
"""`analysis` — derived run analysis artifacts.
|
|
2
|
+
|
|
3
|
+
T5 / ADR-0013 (`RunSummary`), ADR-0016 (`BeliefTraceabilityReport`),
|
|
4
|
+
ADR-0017 (`BeliefConsistencySummary`), ADR-0018
|
|
5
|
+
(`RunManifest` + `ComparativeBeliefReport`), ADR-0019
|
|
6
|
+
(`BeliefCalibrationReport`).
|
|
7
|
+
|
|
8
|
+
Offline only. Deterministic. JSON-only output. No databases, dashboards,
|
|
9
|
+
services, threads, async, ML, or anomaly detection.
|
|
10
|
+
|
|
11
|
+
Public API:
|
|
12
|
+
|
|
13
|
+
- ``RunSummary`` (frozen dataclass): the derived artifact (T5).
|
|
14
|
+
- ``build_run_summary(*, run_id, reader, final_state) -> RunSummary``.
|
|
15
|
+
- ``generate_run_report(summary, output_path)``: writes a JSON sidecar.
|
|
16
|
+
- ``encode_report_to_bytes(summary) -> bytes``: pure encoder.
|
|
17
|
+
- ``SUMMARY_SCHEMA_VERSION`` / ``REPORT_SCHEMA_VERSION``: versioned
|
|
18
|
+
contracts.
|
|
19
|
+
- ``BeliefTraceRecord`` / ``BeliefTraceabilityReport`` (frozen
|
|
20
|
+
dataclasses): per-sample and aggregated artifact for the
|
|
21
|
+
truth/belief comparison (ADR-0016).
|
|
22
|
+
- ``build_traceability_report(*, truth, belief)``: pure aligner.
|
|
23
|
+
- ``compute_position_error`` / ``compute_orientation_error``: pure
|
|
24
|
+
helpers.
|
|
25
|
+
- ``encode_belief_report_to_bytes`` / ``generate_belief_report``:
|
|
26
|
+
canonical JSON encoder + file writer for ADR-0016.
|
|
27
|
+
- ``BELIEF_TRACEABILITY_ANALYSIS_VERSION`` /
|
|
28
|
+
``BELIEF_TRACEABILITY_REPORT_SCHEMA_VERSION``: versioned contracts.
|
|
29
|
+
- ``BeliefConsistencySummary`` (frozen dataclass): descriptive
|
|
30
|
+
statistics over a ``BeliefTraceabilityReport`` (ADR-0017).
|
|
31
|
+
- ``summarize_belief_consistency(report)``: pure aggregator.
|
|
32
|
+
- ``decode_belief_report_from_json(data)``: companion deserializer for
|
|
33
|
+
the ADR-0016 JSON envelope.
|
|
34
|
+
- ``encode_consistency_summary_to_bytes`` /
|
|
35
|
+
``generate_consistency_report``: canonical JSON encoder + file writer
|
|
36
|
+
for ADR-0017.
|
|
37
|
+
- ``BELIEF_CONSISTENCY_ANALYSIS_VERSION`` /
|
|
38
|
+
``BELIEF_CONSISTENCY_REPORT_SCHEMA_VERSION``: versioned contracts.
|
|
39
|
+
- ``ManifestArtifact`` / ``RunManifest`` (frozen dataclasses):
|
|
40
|
+
content-addressed provenance for a run (ADR-0018).
|
|
41
|
+
- ``LabeledSummary`` / ``MetricDelta`` /
|
|
42
|
+
``ComparativeBeliefReport`` (frozen dataclasses): N-way structured
|
|
43
|
+
deltas between summaries (ADR-0018).
|
|
44
|
+
- ``build_run_manifest`` / ``verify_run_manifest`` /
|
|
45
|
+
``build_comparative_report``: pure functions for provenance and
|
|
46
|
+
comparison.
|
|
47
|
+
- ``decode_consistency_summary_from_json`` /
|
|
48
|
+
``decode_run_manifest_from_json`` /
|
|
49
|
+
``decode_comparative_report_from_json``: companion decoders.
|
|
50
|
+
- ``encode_run_manifest_to_bytes`` /
|
|
51
|
+
``encode_comparative_report_to_bytes`` /
|
|
52
|
+
``generate_run_manifest`` / ``generate_comparative_report``:
|
|
53
|
+
canonical JSON encoders + file writers for ADR-0018.
|
|
54
|
+
- ``BELIEF_COMPARISON_ANALYSIS_VERSION`` /
|
|
55
|
+
``BELIEF_COMPARISON_REPORT_SCHEMA_VERSION`` /
|
|
56
|
+
``RUN_MANIFEST_SCHEMA_VERSION``: versioned contracts.
|
|
57
|
+
- ``BeliefCalibrationRecord`` / ``BeliefCalibrationReport`` (frozen
|
|
58
|
+
dataclasses): per-record + aggregate audit of declared-vs-empirical
|
|
59
|
+
uncertainty ratios (ADR-0019). Observational, no verdicts.
|
|
60
|
+
- ``analyze_belief_calibration(report, *, source_belief_report_sha256)``:
|
|
61
|
+
pure auditor over the ADR-0016 traceability report.
|
|
62
|
+
- ``decode_calibration_report_from_json`` /
|
|
63
|
+
``encode_calibration_report_to_bytes`` /
|
|
64
|
+
``generate_calibration_report``: canonical JSON IO for ADR-0019.
|
|
65
|
+
- ``BELIEF_CALIBRATION_ANALYSIS_VERSION`` /
|
|
66
|
+
``BELIEF_CALIBRATION_REPORT_SCHEMA_VERSION``: versioned contracts.
|
|
67
|
+
|
|
68
|
+
CLI: six subcommands live in ``project_ghost.cli``:
|
|
69
|
+
|
|
70
|
+
- ``ghost analyze-run --mcap PATH --state PATH --output PATH``
|
|
71
|
+
- ``ghost analyze-belief --truth-mcap PATH --belief-mcap PATH
|
|
72
|
+
[--output PATH]``
|
|
73
|
+
- ``ghost summarize-belief --report PATH [--output PATH]``
|
|
74
|
+
- ``ghost build-manifest --run-id ID [--config-json PATH]
|
|
75
|
+
[--config-kv KEY=VALUE ...] [--input PATH=KIND ...]
|
|
76
|
+
[--output-artifact PATH=KIND ...] [--output PATH]``
|
|
77
|
+
- ``ghost compare-belief --summary LABEL=PATH ...
|
|
78
|
+
[--manifest LABEL=PATH ...] [--output PATH]``
|
|
79
|
+
- ``ghost analyze-calibration --belief-report PATH [--output PATH]``
|
|
80
|
+
"""
|
|
81
|
+
|
|
82
|
+
from __future__ import annotations
|
|
83
|
+
|
|
84
|
+
from .belief_consistency import (
|
|
85
|
+
BELIEF_CONSISTENCY_ANALYSIS_VERSION,
|
|
86
|
+
BELIEF_CONSISTENCY_REPORT_SCHEMA_VERSION,
|
|
87
|
+
BeliefConsistencySummary,
|
|
88
|
+
decode_belief_report_from_json,
|
|
89
|
+
encode_consistency_summary_to_bytes,
|
|
90
|
+
generate_consistency_report,
|
|
91
|
+
summarize_belief_consistency,
|
|
92
|
+
)
|
|
93
|
+
from .belief_traceability import (
|
|
94
|
+
BELIEF_TRACEABILITY_ANALYSIS_VERSION,
|
|
95
|
+
BELIEF_TRACEABILITY_REPORT_SCHEMA_VERSION,
|
|
96
|
+
BeliefTraceabilityReport,
|
|
97
|
+
BeliefTraceRecord,
|
|
98
|
+
build_traceability_report,
|
|
99
|
+
compute_orientation_error,
|
|
100
|
+
compute_position_error,
|
|
101
|
+
encode_belief_report_to_bytes,
|
|
102
|
+
generate_belief_report,
|
|
103
|
+
)
|
|
104
|
+
from .calibration import (
|
|
105
|
+
BELIEF_CALIBRATION_ANALYSIS_VERSION,
|
|
106
|
+
BELIEF_CALIBRATION_REPORT_SCHEMA_VERSION,
|
|
107
|
+
BeliefCalibrationRecord,
|
|
108
|
+
BeliefCalibrationReport,
|
|
109
|
+
analyze_belief_calibration,
|
|
110
|
+
decode_calibration_report_from_json,
|
|
111
|
+
encode_calibration_report_to_bytes,
|
|
112
|
+
generate_calibration_report,
|
|
113
|
+
)
|
|
114
|
+
from .comparison import (
|
|
115
|
+
BELIEF_COMPARISON_ANALYSIS_VERSION,
|
|
116
|
+
BELIEF_COMPARISON_REPORT_SCHEMA_VERSION,
|
|
117
|
+
RUN_MANIFEST_SCHEMA_VERSION,
|
|
118
|
+
ComparativeBeliefReport,
|
|
119
|
+
LabeledSummary,
|
|
120
|
+
ManifestArtifact,
|
|
121
|
+
MetricDelta,
|
|
122
|
+
RunManifest,
|
|
123
|
+
build_comparative_report,
|
|
124
|
+
build_run_manifest,
|
|
125
|
+
decode_comparative_report_from_json,
|
|
126
|
+
decode_consistency_summary_from_json,
|
|
127
|
+
decode_run_manifest_from_json,
|
|
128
|
+
encode_comparative_report_to_bytes,
|
|
129
|
+
encode_run_manifest_to_bytes,
|
|
130
|
+
generate_comparative_report,
|
|
131
|
+
generate_run_manifest,
|
|
132
|
+
verify_run_manifest,
|
|
133
|
+
)
|
|
134
|
+
from .decision_trace import (
|
|
135
|
+
DECISION_TRACE_ANALYSIS_VERSION,
|
|
136
|
+
DECISION_TRACE_REPORT_SCHEMA_VERSION,
|
|
137
|
+
ChainStatus,
|
|
138
|
+
DecisionTraceRecord,
|
|
139
|
+
DecisionTraceReport,
|
|
140
|
+
build_decision_trace_report,
|
|
141
|
+
decode_decision_trace_report_from_json,
|
|
142
|
+
encode_decision_trace_report_to_bytes,
|
|
143
|
+
generate_decision_trace_report,
|
|
144
|
+
verify_decision_chain,
|
|
145
|
+
)
|
|
146
|
+
from .models import SUMMARY_SCHEMA_VERSION, RunSummary
|
|
147
|
+
from .report import (
|
|
148
|
+
REPORT_SCHEMA_VERSION,
|
|
149
|
+
encode_report_to_bytes,
|
|
150
|
+
generate_run_report,
|
|
151
|
+
)
|
|
152
|
+
from .self_assessment import (
|
|
153
|
+
SELF_ASSESSMENT_SUMMARY_ANALYSIS_VERSION,
|
|
154
|
+
SELF_ASSESSMENT_SUMMARY_SCHEMA_VERSION,
|
|
155
|
+
LevelCounts,
|
|
156
|
+
SelfAssessmentSummary,
|
|
157
|
+
decode_self_assessment_summary_from_json,
|
|
158
|
+
encode_self_assessment_summary_to_bytes,
|
|
159
|
+
generate_self_assessment_summary,
|
|
160
|
+
read_self_assessments_from_mcap,
|
|
161
|
+
summarize_self_assessments,
|
|
162
|
+
)
|
|
163
|
+
from .summary import build_run_summary
|
|
164
|
+
|
|
165
|
+
__all__ = [
|
|
166
|
+
"BELIEF_CALIBRATION_ANALYSIS_VERSION",
|
|
167
|
+
"BELIEF_CALIBRATION_REPORT_SCHEMA_VERSION",
|
|
168
|
+
"BELIEF_COMPARISON_ANALYSIS_VERSION",
|
|
169
|
+
"BELIEF_COMPARISON_REPORT_SCHEMA_VERSION",
|
|
170
|
+
"BELIEF_CONSISTENCY_ANALYSIS_VERSION",
|
|
171
|
+
"BELIEF_CONSISTENCY_REPORT_SCHEMA_VERSION",
|
|
172
|
+
"BELIEF_TRACEABILITY_ANALYSIS_VERSION",
|
|
173
|
+
"BELIEF_TRACEABILITY_REPORT_SCHEMA_VERSION",
|
|
174
|
+
"DECISION_TRACE_ANALYSIS_VERSION",
|
|
175
|
+
"DECISION_TRACE_REPORT_SCHEMA_VERSION",
|
|
176
|
+
"REPORT_SCHEMA_VERSION",
|
|
177
|
+
"RUN_MANIFEST_SCHEMA_VERSION",
|
|
178
|
+
"SELF_ASSESSMENT_SUMMARY_ANALYSIS_VERSION",
|
|
179
|
+
"SELF_ASSESSMENT_SUMMARY_SCHEMA_VERSION",
|
|
180
|
+
"SUMMARY_SCHEMA_VERSION",
|
|
181
|
+
"BeliefCalibrationRecord",
|
|
182
|
+
"BeliefCalibrationReport",
|
|
183
|
+
"BeliefConsistencySummary",
|
|
184
|
+
"BeliefTraceRecord",
|
|
185
|
+
"BeliefTraceabilityReport",
|
|
186
|
+
"ChainStatus",
|
|
187
|
+
"ComparativeBeliefReport",
|
|
188
|
+
"DecisionTraceRecord",
|
|
189
|
+
"DecisionTraceReport",
|
|
190
|
+
"LabeledSummary",
|
|
191
|
+
"LevelCounts",
|
|
192
|
+
"ManifestArtifact",
|
|
193
|
+
"MetricDelta",
|
|
194
|
+
"RunManifest",
|
|
195
|
+
"RunSummary",
|
|
196
|
+
"SelfAssessmentSummary",
|
|
197
|
+
"analyze_belief_calibration",
|
|
198
|
+
"build_comparative_report",
|
|
199
|
+
"build_decision_trace_report",
|
|
200
|
+
"build_run_manifest",
|
|
201
|
+
"build_run_summary",
|
|
202
|
+
"build_traceability_report",
|
|
203
|
+
"compute_orientation_error",
|
|
204
|
+
"compute_position_error",
|
|
205
|
+
"decode_belief_report_from_json",
|
|
206
|
+
"decode_calibration_report_from_json",
|
|
207
|
+
"decode_comparative_report_from_json",
|
|
208
|
+
"decode_consistency_summary_from_json",
|
|
209
|
+
"decode_decision_trace_report_from_json",
|
|
210
|
+
"decode_run_manifest_from_json",
|
|
211
|
+
"decode_self_assessment_summary_from_json",
|
|
212
|
+
"encode_belief_report_to_bytes",
|
|
213
|
+
"encode_calibration_report_to_bytes",
|
|
214
|
+
"encode_comparative_report_to_bytes",
|
|
215
|
+
"encode_consistency_summary_to_bytes",
|
|
216
|
+
"encode_decision_trace_report_to_bytes",
|
|
217
|
+
"encode_report_to_bytes",
|
|
218
|
+
"encode_run_manifest_to_bytes",
|
|
219
|
+
"encode_self_assessment_summary_to_bytes",
|
|
220
|
+
"generate_belief_report",
|
|
221
|
+
"generate_calibration_report",
|
|
222
|
+
"generate_comparative_report",
|
|
223
|
+
"generate_consistency_report",
|
|
224
|
+
"generate_decision_trace_report",
|
|
225
|
+
"generate_run_manifest",
|
|
226
|
+
"generate_run_report",
|
|
227
|
+
"generate_self_assessment_summary",
|
|
228
|
+
"read_self_assessments_from_mcap",
|
|
229
|
+
"summarize_belief_consistency",
|
|
230
|
+
"summarize_self_assessments",
|
|
231
|
+
"verify_decision_chain",
|
|
232
|
+
"verify_run_manifest",
|
|
233
|
+
]
|
|
@@ -0,0 +1,318 @@
|
|
|
1
|
+
"""Belief consistency analysis (ADR-0017).
|
|
2
|
+
|
|
3
|
+
Pure, deterministic, descriptive. Ingests a
|
|
4
|
+
``BeliefTraceabilityReport`` (ADR-0016) and emits a frozen
|
|
5
|
+
``BeliefConsistencySummary`` with min/max/mean over the report's
|
|
6
|
+
records, plus the timestamp range and finite-metric sub-counts.
|
|
7
|
+
|
|
8
|
+
**Honest framing.** This module produces descriptive statistics over
|
|
9
|
+
already-observed paired samples. It does NOT:
|
|
10
|
+
|
|
11
|
+
- evaluate the belief,
|
|
12
|
+
- compute consistency tests (NEES / NIS / Mahalanobis),
|
|
13
|
+
- classify records,
|
|
14
|
+
- detect anomalies,
|
|
15
|
+
- recommend corrective action,
|
|
16
|
+
- cross-correlate fields.
|
|
17
|
+
|
|
18
|
+
Operators read the numbers and form their own hypotheses. The system
|
|
19
|
+
explicitly refuses to do so.
|
|
20
|
+
|
|
21
|
+
Encoding posture: ``sort_keys=True``, ``indent=2``,
|
|
22
|
+
``ensure_ascii=False``, trailing newline, UTF-8. Byte determinism
|
|
23
|
+
within a fixed ``(CPython, numpy)`` tuple.
|
|
24
|
+
"""
|
|
25
|
+
|
|
26
|
+
from __future__ import annotations
|
|
27
|
+
|
|
28
|
+
import dataclasses
|
|
29
|
+
import json
|
|
30
|
+
from collections.abc import Mapping
|
|
31
|
+
from dataclasses import dataclass
|
|
32
|
+
from typing import TYPE_CHECKING, Any
|
|
33
|
+
|
|
34
|
+
from project_ghost.telemetry import from_json_dict
|
|
35
|
+
|
|
36
|
+
from .belief_traceability import (
|
|
37
|
+
BELIEF_TRACEABILITY_REPORT_SCHEMA_VERSION,
|
|
38
|
+
BeliefTraceabilityReport,
|
|
39
|
+
)
|
|
40
|
+
|
|
41
|
+
if TYPE_CHECKING:
|
|
42
|
+
from pathlib import Path
|
|
43
|
+
|
|
44
|
+
|
|
45
|
+
BELIEF_CONSISTENCY_ANALYSIS_VERSION: int = 1
|
|
46
|
+
BELIEF_CONSISTENCY_REPORT_SCHEMA_VERSION: str = "1"
|
|
47
|
+
|
|
48
|
+
|
|
49
|
+
# ---------------------------------------------------------------------------
|
|
50
|
+
# Summary dataclass
|
|
51
|
+
# ---------------------------------------------------------------------------
|
|
52
|
+
|
|
53
|
+
|
|
54
|
+
@dataclass(frozen=True)
|
|
55
|
+
class BeliefConsistencySummary:
|
|
56
|
+
"""Descriptive statistics over a `BeliefTraceabilityReport`.
|
|
57
|
+
|
|
58
|
+
Aggregation rules (frozen per ADR-0017):
|
|
59
|
+
|
|
60
|
+
- Counts: ``total_samples`` is ``len(report.records)``;
|
|
61
|
+
``samples_with_covariance`` / ``samples_without_covariance`` are
|
|
62
|
+
pass-through from the input report.
|
|
63
|
+
- Timestamps: ``first = min(record.timestamp_ns)``,
|
|
64
|
+
``last = max(...)``, ``span = last - first``. All three are
|
|
65
|
+
``None`` iff ``total_samples == 0``.
|
|
66
|
+
- Position / orientation error: ``min``, ``max``, ``mean`` over
|
|
67
|
+
**all** records. Empty input collapses to ``0.0`` (consistent
|
|
68
|
+
with ADR-0016 convention).
|
|
69
|
+
- Covariance trace / condition number: ``min``, ``max``, ``mean``
|
|
70
|
+
over records whose corresponding field is **not** ``None``. If
|
|
71
|
+
no such record exists, all three are ``None``.
|
|
72
|
+
- ``samples_with_finite_trace`` /
|
|
73
|
+
``samples_with_finite_condition_number`` count only records
|
|
74
|
+
whose computed metric was finite (i.e., not collapsed to
|
|
75
|
+
``None`` by ADR-0016).
|
|
76
|
+
|
|
77
|
+
The summary intentionally does **not** carry the records
|
|
78
|
+
themselves; the source ``BeliefTraceabilityReport`` is the
|
|
79
|
+
authoritative store for per-sample data.
|
|
80
|
+
"""
|
|
81
|
+
|
|
82
|
+
total_samples: int
|
|
83
|
+
samples_with_covariance: int
|
|
84
|
+
samples_without_covariance: int
|
|
85
|
+
|
|
86
|
+
timestamp_first_ns: int | None
|
|
87
|
+
timestamp_last_ns: int | None
|
|
88
|
+
timestamp_span_ns: int | None
|
|
89
|
+
|
|
90
|
+
position_error_min_m: float
|
|
91
|
+
position_error_max_m: float
|
|
92
|
+
position_error_mean_m: float
|
|
93
|
+
|
|
94
|
+
orientation_error_min_rad: float
|
|
95
|
+
orientation_error_max_rad: float
|
|
96
|
+
orientation_error_mean_rad: float
|
|
97
|
+
|
|
98
|
+
covariance_trace_min: float | None
|
|
99
|
+
covariance_trace_max: float | None
|
|
100
|
+
covariance_trace_mean: float | None
|
|
101
|
+
|
|
102
|
+
covariance_condition_number_min: float | None
|
|
103
|
+
covariance_condition_number_max: float | None
|
|
104
|
+
covariance_condition_number_mean: float | None
|
|
105
|
+
|
|
106
|
+
samples_with_finite_trace: int
|
|
107
|
+
samples_with_finite_condition_number: int
|
|
108
|
+
|
|
109
|
+
analysis_version: int = BELIEF_CONSISTENCY_ANALYSIS_VERSION
|
|
110
|
+
|
|
111
|
+
|
|
112
|
+
# ---------------------------------------------------------------------------
|
|
113
|
+
# Summarizer
|
|
114
|
+
# ---------------------------------------------------------------------------
|
|
115
|
+
|
|
116
|
+
|
|
117
|
+
def summarize_belief_consistency(
|
|
118
|
+
report: BeliefTraceabilityReport,
|
|
119
|
+
) -> BeliefConsistencySummary:
|
|
120
|
+
"""Aggregate descriptive statistics over a traceability report.
|
|
121
|
+
|
|
122
|
+
Pure function: reads no clock, performs no I/O, holds no
|
|
123
|
+
thread-local state, uses no random. Single forward pass over
|
|
124
|
+
``report.records``.
|
|
125
|
+
"""
|
|
126
|
+
records = report.records
|
|
127
|
+
n = len(records)
|
|
128
|
+
|
|
129
|
+
if n == 0:
|
|
130
|
+
timestamp_first: int | None = None
|
|
131
|
+
timestamp_last: int | None = None
|
|
132
|
+
timestamp_span: int | None = None
|
|
133
|
+
pos_min = pos_max = pos_mean = 0.0
|
|
134
|
+
ori_min = ori_max = ori_mean = 0.0
|
|
135
|
+
else:
|
|
136
|
+
timestamps = [r.timestamp_ns for r in records]
|
|
137
|
+
timestamp_first = min(timestamps)
|
|
138
|
+
timestamp_last = max(timestamps)
|
|
139
|
+
timestamp_span = timestamp_last - timestamp_first
|
|
140
|
+
|
|
141
|
+
pos_errors = [r.position_error_norm_m for r in records]
|
|
142
|
+
ori_errors = [r.orientation_error_rad for r in records]
|
|
143
|
+
pos_min = min(pos_errors)
|
|
144
|
+
pos_max = max(pos_errors)
|
|
145
|
+
pos_mean = sum(pos_errors) / n
|
|
146
|
+
ori_min = min(ori_errors)
|
|
147
|
+
ori_max = max(ori_errors)
|
|
148
|
+
ori_mean = sum(ori_errors) / n
|
|
149
|
+
|
|
150
|
+
# Covariance trace: include only records whose computed trace was
|
|
151
|
+
# finite (ADR-0016 collapses non-finite metrics to None).
|
|
152
|
+
traces: list[float] = [
|
|
153
|
+
r.covariance_trace
|
|
154
|
+
for r in records
|
|
155
|
+
if r.covariance_trace is not None
|
|
156
|
+
]
|
|
157
|
+
if traces:
|
|
158
|
+
cov_trace_min: float | None = min(traces)
|
|
159
|
+
cov_trace_max: float | None = max(traces)
|
|
160
|
+
cov_trace_mean: float | None = sum(traces) / len(traces)
|
|
161
|
+
else:
|
|
162
|
+
cov_trace_min = None
|
|
163
|
+
cov_trace_max = None
|
|
164
|
+
cov_trace_mean = None
|
|
165
|
+
|
|
166
|
+
conds: list[float] = [
|
|
167
|
+
r.covariance_condition_number
|
|
168
|
+
for r in records
|
|
169
|
+
if r.covariance_condition_number is not None
|
|
170
|
+
]
|
|
171
|
+
if conds:
|
|
172
|
+
cov_cond_min: float | None = min(conds)
|
|
173
|
+
cov_cond_max: float | None = max(conds)
|
|
174
|
+
cov_cond_mean: float | None = sum(conds) / len(conds)
|
|
175
|
+
else:
|
|
176
|
+
cov_cond_min = None
|
|
177
|
+
cov_cond_max = None
|
|
178
|
+
cov_cond_mean = None
|
|
179
|
+
|
|
180
|
+
return BeliefConsistencySummary(
|
|
181
|
+
total_samples=n,
|
|
182
|
+
samples_with_covariance=report.samples_with_covariance,
|
|
183
|
+
samples_without_covariance=report.samples_without_covariance,
|
|
184
|
+
timestamp_first_ns=timestamp_first,
|
|
185
|
+
timestamp_last_ns=timestamp_last,
|
|
186
|
+
timestamp_span_ns=timestamp_span,
|
|
187
|
+
position_error_min_m=pos_min,
|
|
188
|
+
position_error_max_m=pos_max,
|
|
189
|
+
position_error_mean_m=pos_mean,
|
|
190
|
+
orientation_error_min_rad=ori_min,
|
|
191
|
+
orientation_error_max_rad=ori_max,
|
|
192
|
+
orientation_error_mean_rad=ori_mean,
|
|
193
|
+
covariance_trace_min=cov_trace_min,
|
|
194
|
+
covariance_trace_max=cov_trace_max,
|
|
195
|
+
covariance_trace_mean=cov_trace_mean,
|
|
196
|
+
covariance_condition_number_min=cov_cond_min,
|
|
197
|
+
covariance_condition_number_max=cov_cond_max,
|
|
198
|
+
covariance_condition_number_mean=cov_cond_mean,
|
|
199
|
+
samples_with_finite_trace=len(traces),
|
|
200
|
+
samples_with_finite_condition_number=len(conds),
|
|
201
|
+
)
|
|
202
|
+
|
|
203
|
+
|
|
204
|
+
# ---------------------------------------------------------------------------
|
|
205
|
+
# Decoder for the ADR-0016 JSON envelope
|
|
206
|
+
# ---------------------------------------------------------------------------
|
|
207
|
+
|
|
208
|
+
|
|
209
|
+
def decode_belief_report_from_json(
|
|
210
|
+
data: Mapping[str, Any],
|
|
211
|
+
) -> BeliefTraceabilityReport:
|
|
212
|
+
"""Reconstruct a ``BeliefTraceabilityReport`` from canonical JSON.
|
|
213
|
+
|
|
214
|
+
Expects the structure produced by
|
|
215
|
+
``encode_belief_report_to_bytes`` (ADR-0016)::
|
|
216
|
+
|
|
217
|
+
{
|
|
218
|
+
"schema_version": "1",
|
|
219
|
+
"report": { ...fields... }
|
|
220
|
+
}
|
|
221
|
+
|
|
222
|
+
Validates ``schema_version`` against the literal
|
|
223
|
+
``BELIEF_TRACEABILITY_REPORT_SCHEMA_VERSION``. Reconstruction goes
|
|
224
|
+
through ``telemetry.from_json_dict``, so each nested dataclass's
|
|
225
|
+
``__post_init__`` re-runs — bad data fails loudly.
|
|
226
|
+
"""
|
|
227
|
+
if not isinstance(data, Mapping):
|
|
228
|
+
raise TypeError(
|
|
229
|
+
f"decode_belief_report_from_json: expected mapping; got "
|
|
230
|
+
f"{type(data).__name__}"
|
|
231
|
+
)
|
|
232
|
+
if "schema_version" not in data:
|
|
233
|
+
raise ValueError(
|
|
234
|
+
"decode_belief_report_from_json: missing 'schema_version'"
|
|
235
|
+
)
|
|
236
|
+
schema_version = data["schema_version"]
|
|
237
|
+
if schema_version != BELIEF_TRACEABILITY_REPORT_SCHEMA_VERSION:
|
|
238
|
+
raise ValueError(
|
|
239
|
+
f"decode_belief_report_from_json: incompatible "
|
|
240
|
+
f"schema_version {schema_version!r}; expected "
|
|
241
|
+
f"{BELIEF_TRACEABILITY_REPORT_SCHEMA_VERSION!r}"
|
|
242
|
+
)
|
|
243
|
+
if "report" not in data:
|
|
244
|
+
raise ValueError(
|
|
245
|
+
"decode_belief_report_from_json: missing 'report' field"
|
|
246
|
+
)
|
|
247
|
+
report_dict = data["report"]
|
|
248
|
+
if not isinstance(report_dict, Mapping):
|
|
249
|
+
raise TypeError(
|
|
250
|
+
f"decode_belief_report_from_json: 'report' must be a mapping; "
|
|
251
|
+
f"got {type(report_dict).__name__}"
|
|
252
|
+
)
|
|
253
|
+
decoded = from_json_dict(BeliefTraceabilityReport, report_dict)
|
|
254
|
+
if not isinstance(decoded, BeliefTraceabilityReport): # pragma: no cover
|
|
255
|
+
raise TypeError(
|
|
256
|
+
"decode_belief_report_from_json: decoded object is not a "
|
|
257
|
+
"BeliefTraceabilityReport"
|
|
258
|
+
)
|
|
259
|
+
return decoded
|
|
260
|
+
|
|
261
|
+
|
|
262
|
+
# ---------------------------------------------------------------------------
|
|
263
|
+
# JSON encoder + file writer
|
|
264
|
+
# ---------------------------------------------------------------------------
|
|
265
|
+
|
|
266
|
+
|
|
267
|
+
def encode_consistency_summary_to_bytes(
|
|
268
|
+
summary: BeliefConsistencySummary,
|
|
269
|
+
) -> bytes:
|
|
270
|
+
"""Encode ``summary`` to deterministic UTF-8 JSON bytes.
|
|
271
|
+
|
|
272
|
+
Output structure::
|
|
273
|
+
|
|
274
|
+
{
|
|
275
|
+
"schema_version": "1",
|
|
276
|
+
"summary": { ...alphabetically sorted fields... }
|
|
277
|
+
}
|
|
278
|
+
|
|
279
|
+
Encoding rules (frozen):
|
|
280
|
+
|
|
281
|
+
- ``sort_keys=True``
|
|
282
|
+
- ``indent=2``
|
|
283
|
+
- ``ensure_ascii=False``
|
|
284
|
+
- trailing newline
|
|
285
|
+
- UTF-8
|
|
286
|
+
"""
|
|
287
|
+
document = {
|
|
288
|
+
"schema_version": BELIEF_CONSISTENCY_REPORT_SCHEMA_VERSION,
|
|
289
|
+
"summary": dataclasses.asdict(summary),
|
|
290
|
+
}
|
|
291
|
+
serialized = json.dumps(
|
|
292
|
+
document,
|
|
293
|
+
sort_keys=True,
|
|
294
|
+
indent=2,
|
|
295
|
+
ensure_ascii=False,
|
|
296
|
+
)
|
|
297
|
+
return (serialized + "\n").encode("utf-8")
|
|
298
|
+
|
|
299
|
+
|
|
300
|
+
def generate_consistency_report(
|
|
301
|
+
summary: BeliefConsistencySummary, output_path: Path
|
|
302
|
+
) -> None:
|
|
303
|
+
"""Write ``summary`` as canonical JSON to ``output_path``.
|
|
304
|
+
|
|
305
|
+
Overwrites if the file exists. Does not invent parent directories.
|
|
306
|
+
"""
|
|
307
|
+
output_path.write_bytes(encode_consistency_summary_to_bytes(summary))
|
|
308
|
+
|
|
309
|
+
|
|
310
|
+
__all__ = [
|
|
311
|
+
"BELIEF_CONSISTENCY_ANALYSIS_VERSION",
|
|
312
|
+
"BELIEF_CONSISTENCY_REPORT_SCHEMA_VERSION",
|
|
313
|
+
"BeliefConsistencySummary",
|
|
314
|
+
"decode_belief_report_from_json",
|
|
315
|
+
"encode_consistency_summary_to_bytes",
|
|
316
|
+
"generate_consistency_report",
|
|
317
|
+
"summarize_belief_consistency",
|
|
318
|
+
]
|