oscura 0.0.1__py3-none-any.whl → 0.1.0__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.
- oscura/__init__.py +813 -8
- oscura/__main__.py +392 -0
- oscura/analyzers/__init__.py +37 -0
- oscura/analyzers/digital/__init__.py +177 -0
- oscura/analyzers/digital/bus.py +691 -0
- oscura/analyzers/digital/clock.py +805 -0
- oscura/analyzers/digital/correlation.py +720 -0
- oscura/analyzers/digital/edges.py +632 -0
- oscura/analyzers/digital/extraction.py +413 -0
- oscura/analyzers/digital/quality.py +878 -0
- oscura/analyzers/digital/signal_quality.py +877 -0
- oscura/analyzers/digital/thresholds.py +708 -0
- oscura/analyzers/digital/timing.py +1104 -0
- oscura/analyzers/eye/__init__.py +46 -0
- oscura/analyzers/eye/diagram.py +434 -0
- oscura/analyzers/eye/metrics.py +555 -0
- oscura/analyzers/jitter/__init__.py +83 -0
- oscura/analyzers/jitter/ber.py +333 -0
- oscura/analyzers/jitter/decomposition.py +759 -0
- oscura/analyzers/jitter/measurements.py +413 -0
- oscura/analyzers/jitter/spectrum.py +220 -0
- oscura/analyzers/measurements.py +40 -0
- oscura/analyzers/packet/__init__.py +171 -0
- oscura/analyzers/packet/daq.py +1077 -0
- oscura/analyzers/packet/metrics.py +437 -0
- oscura/analyzers/packet/parser.py +327 -0
- oscura/analyzers/packet/payload.py +2156 -0
- oscura/analyzers/packet/payload_analysis.py +1312 -0
- oscura/analyzers/packet/payload_extraction.py +236 -0
- oscura/analyzers/packet/payload_patterns.py +670 -0
- oscura/analyzers/packet/stream.py +359 -0
- oscura/analyzers/patterns/__init__.py +266 -0
- oscura/analyzers/patterns/clustering.py +1036 -0
- oscura/analyzers/patterns/discovery.py +539 -0
- oscura/analyzers/patterns/learning.py +797 -0
- oscura/analyzers/patterns/matching.py +1091 -0
- oscura/analyzers/patterns/periodic.py +650 -0
- oscura/analyzers/patterns/sequences.py +767 -0
- oscura/analyzers/power/__init__.py +116 -0
- oscura/analyzers/power/ac_power.py +391 -0
- oscura/analyzers/power/basic.py +383 -0
- oscura/analyzers/power/conduction.py +314 -0
- oscura/analyzers/power/efficiency.py +297 -0
- oscura/analyzers/power/ripple.py +356 -0
- oscura/analyzers/power/soa.py +372 -0
- oscura/analyzers/power/switching.py +479 -0
- oscura/analyzers/protocol/__init__.py +150 -0
- oscura/analyzers/protocols/__init__.py +150 -0
- oscura/analyzers/protocols/base.py +500 -0
- oscura/analyzers/protocols/can.py +620 -0
- oscura/analyzers/protocols/can_fd.py +448 -0
- oscura/analyzers/protocols/flexray.py +405 -0
- oscura/analyzers/protocols/hdlc.py +399 -0
- oscura/analyzers/protocols/i2c.py +368 -0
- oscura/analyzers/protocols/i2s.py +296 -0
- oscura/analyzers/protocols/jtag.py +393 -0
- oscura/analyzers/protocols/lin.py +445 -0
- oscura/analyzers/protocols/manchester.py +333 -0
- oscura/analyzers/protocols/onewire.py +501 -0
- oscura/analyzers/protocols/spi.py +334 -0
- oscura/analyzers/protocols/swd.py +325 -0
- oscura/analyzers/protocols/uart.py +393 -0
- oscura/analyzers/protocols/usb.py +495 -0
- oscura/analyzers/signal_integrity/__init__.py +63 -0
- oscura/analyzers/signal_integrity/embedding.py +294 -0
- oscura/analyzers/signal_integrity/equalization.py +370 -0
- oscura/analyzers/signal_integrity/sparams.py +484 -0
- oscura/analyzers/spectral/__init__.py +53 -0
- oscura/analyzers/spectral/chunked.py +273 -0
- oscura/analyzers/spectral/chunked_fft.py +571 -0
- oscura/analyzers/spectral/chunked_wavelet.py +391 -0
- oscura/analyzers/spectral/fft.py +92 -0
- oscura/analyzers/statistical/__init__.py +250 -0
- oscura/analyzers/statistical/checksum.py +923 -0
- oscura/analyzers/statistical/chunked_corr.py +228 -0
- oscura/analyzers/statistical/classification.py +778 -0
- oscura/analyzers/statistical/entropy.py +1113 -0
- oscura/analyzers/statistical/ngrams.py +614 -0
- oscura/analyzers/statistics/__init__.py +119 -0
- oscura/analyzers/statistics/advanced.py +885 -0
- oscura/analyzers/statistics/basic.py +263 -0
- oscura/analyzers/statistics/correlation.py +630 -0
- oscura/analyzers/statistics/distribution.py +298 -0
- oscura/analyzers/statistics/outliers.py +463 -0
- oscura/analyzers/statistics/streaming.py +93 -0
- oscura/analyzers/statistics/trend.py +520 -0
- oscura/analyzers/validation.py +598 -0
- oscura/analyzers/waveform/__init__.py +36 -0
- oscura/analyzers/waveform/measurements.py +943 -0
- oscura/analyzers/waveform/measurements_with_uncertainty.py +371 -0
- oscura/analyzers/waveform/spectral.py +1689 -0
- oscura/analyzers/waveform/wavelets.py +298 -0
- oscura/api/__init__.py +62 -0
- oscura/api/dsl.py +538 -0
- oscura/api/fluent.py +571 -0
- oscura/api/operators.py +498 -0
- oscura/api/optimization.py +392 -0
- oscura/api/profiling.py +396 -0
- oscura/automotive/__init__.py +73 -0
- oscura/automotive/can/__init__.py +52 -0
- oscura/automotive/can/analysis.py +356 -0
- oscura/automotive/can/checksum.py +250 -0
- oscura/automotive/can/correlation.py +212 -0
- oscura/automotive/can/discovery.py +355 -0
- oscura/automotive/can/message_wrapper.py +375 -0
- oscura/automotive/can/models.py +385 -0
- oscura/automotive/can/patterns.py +381 -0
- oscura/automotive/can/session.py +452 -0
- oscura/automotive/can/state_machine.py +300 -0
- oscura/automotive/can/stimulus_response.py +461 -0
- oscura/automotive/dbc/__init__.py +15 -0
- oscura/automotive/dbc/generator.py +156 -0
- oscura/automotive/dbc/parser.py +146 -0
- oscura/automotive/dtc/__init__.py +30 -0
- oscura/automotive/dtc/database.py +3036 -0
- oscura/automotive/j1939/__init__.py +14 -0
- oscura/automotive/j1939/decoder.py +745 -0
- oscura/automotive/loaders/__init__.py +35 -0
- oscura/automotive/loaders/asc.py +98 -0
- oscura/automotive/loaders/blf.py +77 -0
- oscura/automotive/loaders/csv_can.py +136 -0
- oscura/automotive/loaders/dispatcher.py +136 -0
- oscura/automotive/loaders/mdf.py +331 -0
- oscura/automotive/loaders/pcap.py +132 -0
- oscura/automotive/obd/__init__.py +14 -0
- oscura/automotive/obd/decoder.py +707 -0
- oscura/automotive/uds/__init__.py +48 -0
- oscura/automotive/uds/decoder.py +265 -0
- oscura/automotive/uds/models.py +64 -0
- oscura/automotive/visualization.py +369 -0
- oscura/batch/__init__.py +55 -0
- oscura/batch/advanced.py +627 -0
- oscura/batch/aggregate.py +300 -0
- oscura/batch/analyze.py +139 -0
- oscura/batch/logging.py +487 -0
- oscura/batch/metrics.py +556 -0
- oscura/builders/__init__.py +41 -0
- oscura/builders/signal_builder.py +1131 -0
- oscura/cli/__init__.py +14 -0
- oscura/cli/batch.py +339 -0
- oscura/cli/characterize.py +273 -0
- oscura/cli/compare.py +775 -0
- oscura/cli/decode.py +551 -0
- oscura/cli/main.py +247 -0
- oscura/cli/shell.py +350 -0
- oscura/comparison/__init__.py +66 -0
- oscura/comparison/compare.py +397 -0
- oscura/comparison/golden.py +487 -0
- oscura/comparison/limits.py +391 -0
- oscura/comparison/mask.py +434 -0
- oscura/comparison/trace_diff.py +30 -0
- oscura/comparison/visualization.py +481 -0
- oscura/compliance/__init__.py +70 -0
- oscura/compliance/advanced.py +756 -0
- oscura/compliance/masks.py +363 -0
- oscura/compliance/reporting.py +483 -0
- oscura/compliance/testing.py +298 -0
- oscura/component/__init__.py +38 -0
- oscura/component/impedance.py +365 -0
- oscura/component/reactive.py +598 -0
- oscura/component/transmission_line.py +312 -0
- oscura/config/__init__.py +191 -0
- oscura/config/defaults.py +254 -0
- oscura/config/loader.py +348 -0
- oscura/config/memory.py +271 -0
- oscura/config/migration.py +458 -0
- oscura/config/pipeline.py +1077 -0
- oscura/config/preferences.py +530 -0
- oscura/config/protocol.py +875 -0
- oscura/config/schema.py +713 -0
- oscura/config/settings.py +420 -0
- oscura/config/thresholds.py +599 -0
- oscura/convenience.py +457 -0
- oscura/core/__init__.py +299 -0
- oscura/core/audit.py +457 -0
- oscura/core/backend_selector.py +405 -0
- oscura/core/cache.py +590 -0
- oscura/core/cancellation.py +439 -0
- oscura/core/confidence.py +225 -0
- oscura/core/config.py +506 -0
- oscura/core/correlation.py +216 -0
- oscura/core/cross_domain.py +422 -0
- oscura/core/debug.py +301 -0
- oscura/core/edge_cases.py +541 -0
- oscura/core/exceptions.py +535 -0
- oscura/core/gpu_backend.py +523 -0
- oscura/core/lazy.py +832 -0
- oscura/core/log_query.py +540 -0
- oscura/core/logging.py +931 -0
- oscura/core/logging_advanced.py +952 -0
- oscura/core/memoize.py +171 -0
- oscura/core/memory_check.py +274 -0
- oscura/core/memory_guard.py +290 -0
- oscura/core/memory_limits.py +336 -0
- oscura/core/memory_monitor.py +453 -0
- oscura/core/memory_progress.py +465 -0
- oscura/core/memory_warnings.py +315 -0
- oscura/core/numba_backend.py +362 -0
- oscura/core/performance.py +352 -0
- oscura/core/progress.py +524 -0
- oscura/core/provenance.py +358 -0
- oscura/core/results.py +331 -0
- oscura/core/types.py +504 -0
- oscura/core/uncertainty.py +383 -0
- oscura/discovery/__init__.py +52 -0
- oscura/discovery/anomaly_detector.py +672 -0
- oscura/discovery/auto_decoder.py +415 -0
- oscura/discovery/comparison.py +497 -0
- oscura/discovery/quality_validator.py +528 -0
- oscura/discovery/signal_detector.py +769 -0
- oscura/dsl/__init__.py +73 -0
- oscura/dsl/commands.py +246 -0
- oscura/dsl/interpreter.py +455 -0
- oscura/dsl/parser.py +689 -0
- oscura/dsl/repl.py +172 -0
- oscura/exceptions.py +59 -0
- oscura/exploratory/__init__.py +111 -0
- oscura/exploratory/error_recovery.py +642 -0
- oscura/exploratory/fuzzy.py +513 -0
- oscura/exploratory/fuzzy_advanced.py +786 -0
- oscura/exploratory/legacy.py +831 -0
- oscura/exploratory/parse.py +358 -0
- oscura/exploratory/recovery.py +275 -0
- oscura/exploratory/sync.py +382 -0
- oscura/exploratory/unknown.py +707 -0
- oscura/export/__init__.py +25 -0
- oscura/export/wireshark/README.md +265 -0
- oscura/export/wireshark/__init__.py +47 -0
- oscura/export/wireshark/generator.py +312 -0
- oscura/export/wireshark/lua_builder.py +159 -0
- oscura/export/wireshark/templates/dissector.lua.j2 +92 -0
- oscura/export/wireshark/type_mapping.py +165 -0
- oscura/export/wireshark/validator.py +105 -0
- oscura/exporters/__init__.py +94 -0
- oscura/exporters/csv.py +303 -0
- oscura/exporters/exporters.py +44 -0
- oscura/exporters/hdf5.py +219 -0
- oscura/exporters/html_export.py +701 -0
- oscura/exporters/json_export.py +291 -0
- oscura/exporters/markdown_export.py +367 -0
- oscura/exporters/matlab_export.py +354 -0
- oscura/exporters/npz_export.py +219 -0
- oscura/exporters/spice_export.py +210 -0
- oscura/extensibility/__init__.py +131 -0
- oscura/extensibility/docs.py +752 -0
- oscura/extensibility/extensions.py +1125 -0
- oscura/extensibility/logging.py +259 -0
- oscura/extensibility/measurements.py +485 -0
- oscura/extensibility/plugins.py +414 -0
- oscura/extensibility/registry.py +346 -0
- oscura/extensibility/templates.py +913 -0
- oscura/extensibility/validation.py +651 -0
- oscura/filtering/__init__.py +89 -0
- oscura/filtering/base.py +563 -0
- oscura/filtering/convenience.py +564 -0
- oscura/filtering/design.py +725 -0
- oscura/filtering/filters.py +32 -0
- oscura/filtering/introspection.py +605 -0
- oscura/guidance/__init__.py +24 -0
- oscura/guidance/recommender.py +429 -0
- oscura/guidance/wizard.py +518 -0
- oscura/inference/__init__.py +251 -0
- oscura/inference/active_learning/README.md +153 -0
- oscura/inference/active_learning/__init__.py +38 -0
- oscura/inference/active_learning/lstar.py +257 -0
- oscura/inference/active_learning/observation_table.py +230 -0
- oscura/inference/active_learning/oracle.py +78 -0
- oscura/inference/active_learning/teachers/__init__.py +15 -0
- oscura/inference/active_learning/teachers/simulator.py +192 -0
- oscura/inference/adaptive_tuning.py +453 -0
- oscura/inference/alignment.py +653 -0
- oscura/inference/bayesian.py +943 -0
- oscura/inference/binary.py +1016 -0
- oscura/inference/crc_reverse.py +711 -0
- oscura/inference/logic.py +288 -0
- oscura/inference/message_format.py +1305 -0
- oscura/inference/protocol.py +417 -0
- oscura/inference/protocol_dsl.py +1084 -0
- oscura/inference/protocol_library.py +1230 -0
- oscura/inference/sequences.py +809 -0
- oscura/inference/signal_intelligence.py +1509 -0
- oscura/inference/spectral.py +215 -0
- oscura/inference/state_machine.py +634 -0
- oscura/inference/stream.py +918 -0
- oscura/integrations/__init__.py +59 -0
- oscura/integrations/llm.py +1827 -0
- oscura/jupyter/__init__.py +32 -0
- oscura/jupyter/display.py +268 -0
- oscura/jupyter/magic.py +334 -0
- oscura/loaders/__init__.py +526 -0
- oscura/loaders/binary.py +69 -0
- oscura/loaders/configurable.py +1255 -0
- oscura/loaders/csv.py +26 -0
- oscura/loaders/csv_loader.py +473 -0
- oscura/loaders/hdf5.py +9 -0
- oscura/loaders/hdf5_loader.py +510 -0
- oscura/loaders/lazy.py +370 -0
- oscura/loaders/mmap_loader.py +583 -0
- oscura/loaders/numpy_loader.py +436 -0
- oscura/loaders/pcap.py +432 -0
- oscura/loaders/preprocessing.py +368 -0
- oscura/loaders/rigol.py +287 -0
- oscura/loaders/sigrok.py +321 -0
- oscura/loaders/tdms.py +367 -0
- oscura/loaders/tektronix.py +711 -0
- oscura/loaders/validation.py +584 -0
- oscura/loaders/vcd.py +464 -0
- oscura/loaders/wav.py +233 -0
- oscura/math/__init__.py +45 -0
- oscura/math/arithmetic.py +824 -0
- oscura/math/interpolation.py +413 -0
- oscura/onboarding/__init__.py +39 -0
- oscura/onboarding/help.py +498 -0
- oscura/onboarding/tutorials.py +405 -0
- oscura/onboarding/wizard.py +466 -0
- oscura/optimization/__init__.py +19 -0
- oscura/optimization/parallel.py +440 -0
- oscura/optimization/search.py +532 -0
- oscura/pipeline/__init__.py +43 -0
- oscura/pipeline/base.py +338 -0
- oscura/pipeline/composition.py +242 -0
- oscura/pipeline/parallel.py +448 -0
- oscura/pipeline/pipeline.py +375 -0
- oscura/pipeline/reverse_engineering.py +1119 -0
- oscura/plugins/__init__.py +122 -0
- oscura/plugins/base.py +272 -0
- oscura/plugins/cli.py +497 -0
- oscura/plugins/discovery.py +411 -0
- oscura/plugins/isolation.py +418 -0
- oscura/plugins/lifecycle.py +959 -0
- oscura/plugins/manager.py +493 -0
- oscura/plugins/registry.py +421 -0
- oscura/plugins/versioning.py +372 -0
- oscura/py.typed +0 -0
- oscura/quality/__init__.py +65 -0
- oscura/quality/ensemble.py +740 -0
- oscura/quality/explainer.py +338 -0
- oscura/quality/scoring.py +616 -0
- oscura/quality/warnings.py +456 -0
- oscura/reporting/__init__.py +248 -0
- oscura/reporting/advanced.py +1234 -0
- oscura/reporting/analyze.py +448 -0
- oscura/reporting/argument_preparer.py +596 -0
- oscura/reporting/auto_report.py +507 -0
- oscura/reporting/batch.py +615 -0
- oscura/reporting/chart_selection.py +223 -0
- oscura/reporting/comparison.py +330 -0
- oscura/reporting/config.py +615 -0
- oscura/reporting/content/__init__.py +39 -0
- oscura/reporting/content/executive.py +127 -0
- oscura/reporting/content/filtering.py +191 -0
- oscura/reporting/content/minimal.py +257 -0
- oscura/reporting/content/verbosity.py +162 -0
- oscura/reporting/core.py +508 -0
- oscura/reporting/core_formats/__init__.py +17 -0
- oscura/reporting/core_formats/multi_format.py +210 -0
- oscura/reporting/engine.py +836 -0
- oscura/reporting/export.py +366 -0
- oscura/reporting/formatting/__init__.py +129 -0
- oscura/reporting/formatting/emphasis.py +81 -0
- oscura/reporting/formatting/numbers.py +403 -0
- oscura/reporting/formatting/standards.py +55 -0
- oscura/reporting/formatting.py +466 -0
- oscura/reporting/html.py +578 -0
- oscura/reporting/index.py +590 -0
- oscura/reporting/multichannel.py +296 -0
- oscura/reporting/output.py +379 -0
- oscura/reporting/pdf.py +373 -0
- oscura/reporting/plots.py +731 -0
- oscura/reporting/pptx_export.py +360 -0
- oscura/reporting/renderers/__init__.py +11 -0
- oscura/reporting/renderers/pdf.py +94 -0
- oscura/reporting/sections.py +471 -0
- oscura/reporting/standards.py +680 -0
- oscura/reporting/summary_generator.py +368 -0
- oscura/reporting/tables.py +397 -0
- oscura/reporting/template_system.py +724 -0
- oscura/reporting/templates/__init__.py +15 -0
- oscura/reporting/templates/definition.py +205 -0
- oscura/reporting/templates/index.html +649 -0
- oscura/reporting/templates/index.md +173 -0
- oscura/schemas/__init__.py +158 -0
- oscura/schemas/bus_configuration.json +322 -0
- oscura/schemas/device_mapping.json +182 -0
- oscura/schemas/packet_format.json +418 -0
- oscura/schemas/protocol_definition.json +363 -0
- oscura/search/__init__.py +16 -0
- oscura/search/anomaly.py +292 -0
- oscura/search/context.py +149 -0
- oscura/search/pattern.py +160 -0
- oscura/session/__init__.py +34 -0
- oscura/session/annotations.py +289 -0
- oscura/session/history.py +313 -0
- oscura/session/session.py +445 -0
- oscura/streaming/__init__.py +43 -0
- oscura/streaming/chunked.py +611 -0
- oscura/streaming/progressive.py +393 -0
- oscura/streaming/realtime.py +622 -0
- oscura/testing/__init__.py +54 -0
- oscura/testing/synthetic.py +808 -0
- oscura/triggering/__init__.py +68 -0
- oscura/triggering/base.py +229 -0
- oscura/triggering/edge.py +353 -0
- oscura/triggering/pattern.py +344 -0
- oscura/triggering/pulse.py +581 -0
- oscura/triggering/window.py +453 -0
- oscura/ui/__init__.py +48 -0
- oscura/ui/formatters.py +526 -0
- oscura/ui/progressive_display.py +340 -0
- oscura/utils/__init__.py +99 -0
- oscura/utils/autodetect.py +338 -0
- oscura/utils/buffer.py +389 -0
- oscura/utils/lazy.py +407 -0
- oscura/utils/lazy_imports.py +147 -0
- oscura/utils/memory.py +836 -0
- oscura/utils/memory_advanced.py +1326 -0
- oscura/utils/memory_extensions.py +465 -0
- oscura/utils/progressive.py +352 -0
- oscura/utils/windowing.py +362 -0
- oscura/visualization/__init__.py +321 -0
- oscura/visualization/accessibility.py +526 -0
- oscura/visualization/annotations.py +374 -0
- oscura/visualization/axis_scaling.py +305 -0
- oscura/visualization/colors.py +453 -0
- oscura/visualization/digital.py +337 -0
- oscura/visualization/eye.py +420 -0
- oscura/visualization/histogram.py +281 -0
- oscura/visualization/interactive.py +858 -0
- oscura/visualization/jitter.py +702 -0
- oscura/visualization/keyboard.py +394 -0
- oscura/visualization/layout.py +365 -0
- oscura/visualization/optimization.py +1028 -0
- oscura/visualization/palettes.py +446 -0
- oscura/visualization/plot.py +92 -0
- oscura/visualization/power.py +290 -0
- oscura/visualization/power_extended.py +626 -0
- oscura/visualization/presets.py +467 -0
- oscura/visualization/protocols.py +932 -0
- oscura/visualization/render.py +207 -0
- oscura/visualization/rendering.py +444 -0
- oscura/visualization/reverse_engineering.py +791 -0
- oscura/visualization/signal_integrity.py +808 -0
- oscura/visualization/specialized.py +553 -0
- oscura/visualization/spectral.py +811 -0
- oscura/visualization/styles.py +381 -0
- oscura/visualization/thumbnails.py +311 -0
- oscura/visualization/time_axis.py +351 -0
- oscura/visualization/waveform.py +367 -0
- oscura/workflow/__init__.py +13 -0
- oscura/workflow/dag.py +377 -0
- oscura/workflows/__init__.py +58 -0
- oscura/workflows/compliance.py +280 -0
- oscura/workflows/digital.py +272 -0
- oscura/workflows/multi_trace.py +502 -0
- oscura/workflows/power.py +178 -0
- oscura/workflows/protocol.py +492 -0
- oscura/workflows/reverse_engineering.py +639 -0
- oscura/workflows/signal_integrity.py +227 -0
- oscura-0.1.0.dist-info/METADATA +300 -0
- oscura-0.1.0.dist-info/RECORD +463 -0
- oscura-0.1.0.dist-info/entry_points.txt +2 -0
- {oscura-0.0.1.dist-info → oscura-0.1.0.dist-info}/licenses/LICENSE +1 -1
- oscura-0.0.1.dist-info/METADATA +0 -63
- oscura-0.0.1.dist-info/RECORD +0 -5
- {oscura-0.0.1.dist-info → oscura-0.1.0.dist-info}/WHEEL +0 -0
|
@@ -0,0 +1,375 @@
|
|
|
1
|
+
"""CAN message wrapper for analysis and hypothesis testing.
|
|
2
|
+
|
|
3
|
+
This module provides the CANMessageWrapper class which wraps messages
|
|
4
|
+
with a specific CAN ID and provides analysis and hypothesis testing methods.
|
|
5
|
+
"""
|
|
6
|
+
|
|
7
|
+
from __future__ import annotations
|
|
8
|
+
|
|
9
|
+
from typing import TYPE_CHECKING, Literal
|
|
10
|
+
|
|
11
|
+
import numpy as np
|
|
12
|
+
|
|
13
|
+
from oscura.automotive.can.models import DecodedSignal, SignalDefinition
|
|
14
|
+
|
|
15
|
+
if TYPE_CHECKING:
|
|
16
|
+
from oscura.automotive.can.models import MessageAnalysis
|
|
17
|
+
from oscura.automotive.can.session import CANSession
|
|
18
|
+
|
|
19
|
+
__all__ = ["CANMessageWrapper", "HypothesisResult"]
|
|
20
|
+
|
|
21
|
+
|
|
22
|
+
class HypothesisResult:
|
|
23
|
+
"""Result of testing a signal hypothesis.
|
|
24
|
+
|
|
25
|
+
Attributes:
|
|
26
|
+
signal_name: Name of the tested signal.
|
|
27
|
+
definition: Signal definition tested.
|
|
28
|
+
values: Decoded values from all messages.
|
|
29
|
+
min_value: Minimum decoded value.
|
|
30
|
+
max_value: Maximum decoded value.
|
|
31
|
+
mean: Mean decoded value.
|
|
32
|
+
std: Standard deviation.
|
|
33
|
+
is_valid: Whether hypothesis appears valid.
|
|
34
|
+
confidence: Confidence score (0.0-1.0).
|
|
35
|
+
feedback: Human-readable feedback.
|
|
36
|
+
"""
|
|
37
|
+
|
|
38
|
+
def __init__(
|
|
39
|
+
self,
|
|
40
|
+
signal_name: str,
|
|
41
|
+
definition: SignalDefinition,
|
|
42
|
+
values: list[float],
|
|
43
|
+
min_value: float,
|
|
44
|
+
max_value: float,
|
|
45
|
+
mean: float,
|
|
46
|
+
std: float,
|
|
47
|
+
is_valid: bool,
|
|
48
|
+
confidence: float,
|
|
49
|
+
feedback: str,
|
|
50
|
+
):
|
|
51
|
+
"""Initialize hypothesis result."""
|
|
52
|
+
self.signal_name = signal_name
|
|
53
|
+
self.definition = definition
|
|
54
|
+
self.values = values
|
|
55
|
+
self.min_value = min_value
|
|
56
|
+
self.max_value = max_value
|
|
57
|
+
self.mean = mean
|
|
58
|
+
self.std = std
|
|
59
|
+
self.is_valid = is_valid
|
|
60
|
+
self.confidence = confidence
|
|
61
|
+
self.feedback = feedback
|
|
62
|
+
|
|
63
|
+
def __repr__(self) -> str:
|
|
64
|
+
"""Human-readable representation."""
|
|
65
|
+
status = "VALID" if self.is_valid else "INVALID"
|
|
66
|
+
return (
|
|
67
|
+
f"HypothesisResult({self.signal_name}: {status}, "
|
|
68
|
+
f"confidence={self.confidence:.2f}, "
|
|
69
|
+
f"range=[{self.min_value:.2f}, {self.max_value:.2f}])"
|
|
70
|
+
)
|
|
71
|
+
|
|
72
|
+
def summary(self) -> str:
|
|
73
|
+
"""Generate detailed summary."""
|
|
74
|
+
lines = [
|
|
75
|
+
f"=== Hypothesis Test: {self.signal_name} ===",
|
|
76
|
+
f"Status: {'VALID' if self.is_valid else 'INVALID'}",
|
|
77
|
+
f"Confidence: {self.confidence:.2f}",
|
|
78
|
+
"",
|
|
79
|
+
"Signal Definition:",
|
|
80
|
+
f" Start Byte: {self.definition.start_byte}",
|
|
81
|
+
f" Start Bit: {self.definition.start_bit}",
|
|
82
|
+
f" Length: {self.definition.length} bits",
|
|
83
|
+
f" Byte Order: {self.definition.byte_order}",
|
|
84
|
+
f" Value Type: {self.definition.value_type}",
|
|
85
|
+
f" Scale: {self.definition.scale}",
|
|
86
|
+
f" Offset: {self.definition.offset}",
|
|
87
|
+
"",
|
|
88
|
+
"Decoded Values:",
|
|
89
|
+
f" Min: {self.min_value:.2f} {self.definition.unit}",
|
|
90
|
+
f" Max: {self.max_value:.2f} {self.definition.unit}",
|
|
91
|
+
f" Mean: {self.mean:.2f} {self.definition.unit}",
|
|
92
|
+
f" Std Dev: {self.std:.2f} {self.definition.unit}",
|
|
93
|
+
f" Sample Count: {len(self.values)}",
|
|
94
|
+
"",
|
|
95
|
+
f"Feedback: {self.feedback}",
|
|
96
|
+
]
|
|
97
|
+
return "\n".join(lines)
|
|
98
|
+
|
|
99
|
+
|
|
100
|
+
class CANMessageWrapper:
|
|
101
|
+
"""Wrapper for analyzing messages with a specific CAN ID.
|
|
102
|
+
|
|
103
|
+
This class provides methods for analyzing and reverse engineering
|
|
104
|
+
a specific CAN message ID.
|
|
105
|
+
"""
|
|
106
|
+
|
|
107
|
+
def __init__(self, session: CANSession, arbitration_id: int):
|
|
108
|
+
"""Initialize message wrapper.
|
|
109
|
+
|
|
110
|
+
Args:
|
|
111
|
+
session: Parent CAN session.
|
|
112
|
+
arbitration_id: CAN ID to wrap.
|
|
113
|
+
"""
|
|
114
|
+
self._session = session
|
|
115
|
+
self._arbitration_id = arbitration_id
|
|
116
|
+
self._documented_signals: dict[str, SignalDefinition] = {}
|
|
117
|
+
|
|
118
|
+
@property
|
|
119
|
+
def arbitration_id(self) -> int:
|
|
120
|
+
"""Get CAN arbitration ID."""
|
|
121
|
+
return self._arbitration_id
|
|
122
|
+
|
|
123
|
+
def analyze(self, force_refresh: bool = False) -> MessageAnalysis:
|
|
124
|
+
"""Perform complete statistical analysis of this message.
|
|
125
|
+
|
|
126
|
+
Args:
|
|
127
|
+
force_refresh: Force re-analysis even if cached.
|
|
128
|
+
|
|
129
|
+
Returns:
|
|
130
|
+
MessageAnalysis with complete analysis results.
|
|
131
|
+
|
|
132
|
+
Example:
|
|
133
|
+
>>> msg = session.message(0x280)
|
|
134
|
+
>>> analysis = msg.analyze()
|
|
135
|
+
>>> print(analysis.summary())
|
|
136
|
+
"""
|
|
137
|
+
return self._session.analyze_message(self._arbitration_id, force_refresh=force_refresh)
|
|
138
|
+
|
|
139
|
+
def test_hypothesis(
|
|
140
|
+
self,
|
|
141
|
+
signal_name: str,
|
|
142
|
+
start_byte: int,
|
|
143
|
+
bit_length: int,
|
|
144
|
+
byte_order: Literal["big_endian", "little_endian"] = "big_endian",
|
|
145
|
+
value_type: Literal["unsigned", "signed", "float"] = "unsigned",
|
|
146
|
+
scale: float = 1.0,
|
|
147
|
+
offset: float = 0.0,
|
|
148
|
+
unit: str = "",
|
|
149
|
+
expected_min: float | None = None,
|
|
150
|
+
expected_max: float | None = None,
|
|
151
|
+
) -> HypothesisResult:
|
|
152
|
+
"""Test a hypothesis about signal encoding.
|
|
153
|
+
|
|
154
|
+
Args:
|
|
155
|
+
signal_name: Name for the signal.
|
|
156
|
+
start_byte: Starting byte position (0-7).
|
|
157
|
+
bit_length: Signal length in bits.
|
|
158
|
+
byte_order: Byte order ('big_endian' or 'little_endian').
|
|
159
|
+
value_type: Value type ('unsigned', 'signed', 'float').
|
|
160
|
+
scale: Scaling factor.
|
|
161
|
+
offset: Offset value.
|
|
162
|
+
unit: Physical unit.
|
|
163
|
+
expected_min: Expected minimum value (for validation).
|
|
164
|
+
expected_max: Expected maximum value (for validation).
|
|
165
|
+
|
|
166
|
+
Returns:
|
|
167
|
+
HypothesisResult with test results and feedback.
|
|
168
|
+
|
|
169
|
+
Example:
|
|
170
|
+
>>> msg = session.message(0x280)
|
|
171
|
+
>>> result = msg.test_hypothesis(
|
|
172
|
+
... signal_name="rpm",
|
|
173
|
+
... start_byte=2,
|
|
174
|
+
... bit_length=16,
|
|
175
|
+
... scale=0.25,
|
|
176
|
+
... unit="rpm",
|
|
177
|
+
... expected_min=0,
|
|
178
|
+
... expected_max=8000
|
|
179
|
+
... )
|
|
180
|
+
>>> print(result.summary())
|
|
181
|
+
"""
|
|
182
|
+
# Create signal definition
|
|
183
|
+
definition = SignalDefinition(
|
|
184
|
+
name=signal_name,
|
|
185
|
+
start_bit=start_byte * 8,
|
|
186
|
+
length=bit_length,
|
|
187
|
+
byte_order=byte_order,
|
|
188
|
+
value_type=value_type,
|
|
189
|
+
scale=scale,
|
|
190
|
+
offset=offset,
|
|
191
|
+
unit=unit,
|
|
192
|
+
)
|
|
193
|
+
|
|
194
|
+
# Get all messages with this ID
|
|
195
|
+
filtered = self._session._messages.filter_by_id(self._arbitration_id)
|
|
196
|
+
|
|
197
|
+
# Decode all values
|
|
198
|
+
decoded_values = []
|
|
199
|
+
for msg in filtered.messages:
|
|
200
|
+
try:
|
|
201
|
+
value = definition.decode(msg.data)
|
|
202
|
+
decoded_values.append(value)
|
|
203
|
+
except Exception:
|
|
204
|
+
# Skip messages that can't be decoded
|
|
205
|
+
pass
|
|
206
|
+
|
|
207
|
+
if not decoded_values:
|
|
208
|
+
return HypothesisResult(
|
|
209
|
+
signal_name=signal_name,
|
|
210
|
+
definition=definition,
|
|
211
|
+
values=[],
|
|
212
|
+
min_value=0.0,
|
|
213
|
+
max_value=0.0,
|
|
214
|
+
mean=0.0,
|
|
215
|
+
std=0.0,
|
|
216
|
+
is_valid=False,
|
|
217
|
+
confidence=0.0,
|
|
218
|
+
feedback="Failed to decode any messages with this definition",
|
|
219
|
+
)
|
|
220
|
+
|
|
221
|
+
# Calculate statistics
|
|
222
|
+
arr = np.array(decoded_values)
|
|
223
|
+
min_val = float(np.min(arr))
|
|
224
|
+
max_val = float(np.max(arr))
|
|
225
|
+
mean_val = float(np.mean(arr))
|
|
226
|
+
std_val = float(np.std(arr))
|
|
227
|
+
|
|
228
|
+
# Validate hypothesis
|
|
229
|
+
is_valid = True
|
|
230
|
+
confidence = 1.0
|
|
231
|
+
feedback_parts = []
|
|
232
|
+
|
|
233
|
+
# Check expected range if provided
|
|
234
|
+
if expected_min is not None and min_val < expected_min:
|
|
235
|
+
is_valid = False
|
|
236
|
+
confidence *= 0.5
|
|
237
|
+
feedback_parts.append(f"Min value {min_val:.2f} below expected {expected_min:.2f}")
|
|
238
|
+
|
|
239
|
+
if expected_max is not None and max_val > expected_max:
|
|
240
|
+
is_valid = False
|
|
241
|
+
confidence *= 0.5
|
|
242
|
+
feedback_parts.append(f"Max value {max_val:.2f} above expected {expected_max:.2f}")
|
|
243
|
+
|
|
244
|
+
# Check for reasonable value distribution
|
|
245
|
+
if std_val == 0:
|
|
246
|
+
confidence *= 0.7
|
|
247
|
+
feedback_parts.append("Warning: All values are identical - might be a constant field")
|
|
248
|
+
|
|
249
|
+
# Check for extremely large range (might indicate wrong scaling)
|
|
250
|
+
value_range = max_val - min_val
|
|
251
|
+
if value_range > 1e6:
|
|
252
|
+
confidence *= 0.6
|
|
253
|
+
feedback_parts.append("Warning: Very large value range - check scaling factor")
|
|
254
|
+
|
|
255
|
+
# Positive feedback
|
|
256
|
+
if is_valid and not feedback_parts:
|
|
257
|
+
feedback_parts.append(f"Values in expected range [{min_val:.2f}, {max_val:.2f}]")
|
|
258
|
+
if std_val > 0:
|
|
259
|
+
feedback_parts.append("Signal shows variation - likely represents real data")
|
|
260
|
+
|
|
261
|
+
feedback = "; ".join(feedback_parts) if feedback_parts else "Hypothesis test passed"
|
|
262
|
+
|
|
263
|
+
return HypothesisResult(
|
|
264
|
+
signal_name=signal_name,
|
|
265
|
+
definition=definition,
|
|
266
|
+
values=decoded_values,
|
|
267
|
+
min_value=min_val,
|
|
268
|
+
max_value=max_val,
|
|
269
|
+
mean=mean_val,
|
|
270
|
+
std=std_val,
|
|
271
|
+
is_valid=is_valid,
|
|
272
|
+
confidence=confidence,
|
|
273
|
+
feedback=feedback,
|
|
274
|
+
)
|
|
275
|
+
|
|
276
|
+
def document_signal(
|
|
277
|
+
self,
|
|
278
|
+
name: str,
|
|
279
|
+
start_bit: int,
|
|
280
|
+
length: int,
|
|
281
|
+
byte_order: Literal["big_endian", "little_endian"] = "big_endian",
|
|
282
|
+
value_type: Literal["unsigned", "signed", "float"] = "unsigned",
|
|
283
|
+
scale: float = 1.0,
|
|
284
|
+
offset: float = 0.0,
|
|
285
|
+
unit: str = "",
|
|
286
|
+
comment: str = "",
|
|
287
|
+
) -> None:
|
|
288
|
+
"""Document a confirmed signal definition.
|
|
289
|
+
|
|
290
|
+
Args:
|
|
291
|
+
name: Signal name.
|
|
292
|
+
start_bit: Starting bit position.
|
|
293
|
+
length: Signal length in bits.
|
|
294
|
+
byte_order: Byte order.
|
|
295
|
+
value_type: Value type.
|
|
296
|
+
scale: Scaling factor.
|
|
297
|
+
offset: Offset value.
|
|
298
|
+
unit: Physical unit.
|
|
299
|
+
comment: Description or notes.
|
|
300
|
+
|
|
301
|
+
Example:
|
|
302
|
+
>>> msg = session.message(0x280)
|
|
303
|
+
>>> msg.document_signal(
|
|
304
|
+
... name="rpm",
|
|
305
|
+
... start_bit=16,
|
|
306
|
+
... length=16,
|
|
307
|
+
... scale=0.25,
|
|
308
|
+
... unit="rpm",
|
|
309
|
+
... comment="Confirmed via OBD-II correlation"
|
|
310
|
+
... )
|
|
311
|
+
"""
|
|
312
|
+
definition = SignalDefinition(
|
|
313
|
+
name=name,
|
|
314
|
+
start_bit=start_bit,
|
|
315
|
+
length=length,
|
|
316
|
+
byte_order=byte_order,
|
|
317
|
+
value_type=value_type,
|
|
318
|
+
scale=scale,
|
|
319
|
+
offset=offset,
|
|
320
|
+
unit=unit,
|
|
321
|
+
comment=comment,
|
|
322
|
+
)
|
|
323
|
+
|
|
324
|
+
self._documented_signals[name] = definition
|
|
325
|
+
|
|
326
|
+
def get_documented_signals(self) -> dict[str, SignalDefinition]:
|
|
327
|
+
"""Get all documented signal definitions.
|
|
328
|
+
|
|
329
|
+
Returns:
|
|
330
|
+
Dictionary mapping signal names to definitions.
|
|
331
|
+
"""
|
|
332
|
+
return self._documented_signals.copy()
|
|
333
|
+
|
|
334
|
+
def decode_signals(self) -> list[DecodedSignal]:
|
|
335
|
+
"""Decode all documented signals from all messages.
|
|
336
|
+
|
|
337
|
+
Returns:
|
|
338
|
+
List of DecodedSignal objects, one per message per signal.
|
|
339
|
+
|
|
340
|
+
Example:
|
|
341
|
+
>>> msg = session.message(0x280)
|
|
342
|
+
>>> msg.document_signal("rpm", start_bit=16, length=16, scale=0.25, unit="rpm")
|
|
343
|
+
>>> decoded = msg.decode_signals()
|
|
344
|
+
>>> for sig in decoded[:5]: # First 5
|
|
345
|
+
... print(sig)
|
|
346
|
+
"""
|
|
347
|
+
decoded_signals = []
|
|
348
|
+
|
|
349
|
+
filtered = self._session._messages.filter_by_id(self._arbitration_id)
|
|
350
|
+
|
|
351
|
+
for msg in filtered.messages:
|
|
352
|
+
for sig_name, sig_def in self._documented_signals.items():
|
|
353
|
+
try:
|
|
354
|
+
value = sig_def.decode(msg.data)
|
|
355
|
+
raw_value = sig_def.extract_raw(msg.data)
|
|
356
|
+
|
|
357
|
+
decoded_sig = DecodedSignal(
|
|
358
|
+
name=sig_name,
|
|
359
|
+
value=value,
|
|
360
|
+
unit=sig_def.unit,
|
|
361
|
+
timestamp=msg.timestamp,
|
|
362
|
+
raw_value=raw_value,
|
|
363
|
+
definition=sig_def,
|
|
364
|
+
)
|
|
365
|
+
decoded_signals.append(decoded_sig)
|
|
366
|
+
|
|
367
|
+
except Exception:
|
|
368
|
+
# Skip messages that can't be decoded
|
|
369
|
+
pass
|
|
370
|
+
|
|
371
|
+
return decoded_signals
|
|
372
|
+
|
|
373
|
+
def __repr__(self) -> str:
|
|
374
|
+
"""Human-readable representation."""
|
|
375
|
+
return f"CANMessageWrapper(id=0x{self._arbitration_id:03X}, documented_signals={len(self._documented_signals)})"
|