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,300 @@
|
|
|
1
|
+
"""State machine learning for CAN protocol reverse engineering.
|
|
2
|
+
|
|
3
|
+
This module integrates TraceKit's state machine inference capabilities with
|
|
4
|
+
CAN bus analysis to learn protocol state machines from message sequences.
|
|
5
|
+
|
|
6
|
+
Key capabilities:
|
|
7
|
+
- Learn state machines from CAN message sequences
|
|
8
|
+
- Extract sequences around trigger messages
|
|
9
|
+
- Map CAN message patterns to states
|
|
10
|
+
- Discover protocol initialization sequences
|
|
11
|
+
- Identify state-dependent message patterns
|
|
12
|
+
|
|
13
|
+
Use cases:
|
|
14
|
+
- Learn ignition sequence state machines (key off → acc → on → start)
|
|
15
|
+
- Discover ECU initialization sequences
|
|
16
|
+
- Identify state-dependent message patterns
|
|
17
|
+
- Find message dependencies and ordering constraints
|
|
18
|
+
"""
|
|
19
|
+
|
|
20
|
+
from __future__ import annotations
|
|
21
|
+
|
|
22
|
+
from dataclasses import dataclass
|
|
23
|
+
from typing import TYPE_CHECKING
|
|
24
|
+
|
|
25
|
+
from oscura.inference.state_machine import FiniteAutomaton, StateMachineInferrer
|
|
26
|
+
|
|
27
|
+
if TYPE_CHECKING:
|
|
28
|
+
from oscura.automotive.can.session import CANSession
|
|
29
|
+
|
|
30
|
+
__all__ = ["CANStateMachine", "SequenceExtraction"]
|
|
31
|
+
|
|
32
|
+
|
|
33
|
+
@dataclass
|
|
34
|
+
class SequenceExtraction:
|
|
35
|
+
"""A sequence of CAN messages extracted around a trigger.
|
|
36
|
+
|
|
37
|
+
Attributes:
|
|
38
|
+
trigger_id: CAN ID that triggered extraction.
|
|
39
|
+
trigger_timestamp: Timestamp of trigger message.
|
|
40
|
+
sequence: List of CAN IDs in the sequence.
|
|
41
|
+
timestamps: Corresponding timestamps for each message.
|
|
42
|
+
window_start: Start of time window.
|
|
43
|
+
window_end: End of time window.
|
|
44
|
+
"""
|
|
45
|
+
|
|
46
|
+
trigger_id: int
|
|
47
|
+
trigger_timestamp: float
|
|
48
|
+
sequence: list[int]
|
|
49
|
+
timestamps: list[float]
|
|
50
|
+
window_start: float
|
|
51
|
+
window_end: float
|
|
52
|
+
|
|
53
|
+
def to_symbol_sequence(self) -> list[str]:
|
|
54
|
+
"""Convert CAN IDs to symbol strings for state machine learning.
|
|
55
|
+
|
|
56
|
+
Returns:
|
|
57
|
+
List of symbols (CAN IDs as hex strings).
|
|
58
|
+
"""
|
|
59
|
+
return [f"0x{can_id:03X}" for can_id in self.sequence]
|
|
60
|
+
|
|
61
|
+
|
|
62
|
+
class CANStateMachine:
|
|
63
|
+
"""Learn CAN protocol state machines from message sequences.
|
|
64
|
+
|
|
65
|
+
This class wraps StateMachineInferrer for CAN-specific use cases,
|
|
66
|
+
handling the extraction of message sequences from CAN sessions and
|
|
67
|
+
converting them to the format needed for state machine learning.
|
|
68
|
+
|
|
69
|
+
The learned state machines reveal:
|
|
70
|
+
- Protocol initialization sequences
|
|
71
|
+
- State transitions triggered by specific messages
|
|
72
|
+
- Message ordering constraints
|
|
73
|
+
- State-dependent message patterns
|
|
74
|
+
|
|
75
|
+
Example - Learn ignition sequence:
|
|
76
|
+
>>> session = CANSession.from_log("ignition_cycles.blf")
|
|
77
|
+
>>> sm = CANStateMachine()
|
|
78
|
+
>>> # Use ignition-related CAN IDs as triggers
|
|
79
|
+
>>> automaton = sm.learn_from_session(
|
|
80
|
+
... session,
|
|
81
|
+
... trigger_ids=[0x280], # Engine status message
|
|
82
|
+
... context_window_ms=500
|
|
83
|
+
... )
|
|
84
|
+
>>> # Export for visualization
|
|
85
|
+
>>> print(automaton.to_dot())
|
|
86
|
+
|
|
87
|
+
Example - Discover initialization sequence:
|
|
88
|
+
>>> session = CANSession.from_log("ecu_startup.blf")
|
|
89
|
+
>>> sm = CANStateMachine()
|
|
90
|
+
>>> # Use diagnostic messages as triggers
|
|
91
|
+
>>> automaton = sm.learn_from_session(
|
|
92
|
+
... session,
|
|
93
|
+
... trigger_ids=[0x7E0, 0x7E8],
|
|
94
|
+
... context_window_ms=1000
|
|
95
|
+
... )
|
|
96
|
+
"""
|
|
97
|
+
|
|
98
|
+
def __init__(self) -> None:
|
|
99
|
+
"""Initialize CAN state machine learner."""
|
|
100
|
+
self._inferrer = StateMachineInferrer()
|
|
101
|
+
|
|
102
|
+
def learn_from_session(
|
|
103
|
+
self,
|
|
104
|
+
session: CANSession,
|
|
105
|
+
trigger_ids: list[int],
|
|
106
|
+
context_window_ms: float = 500,
|
|
107
|
+
min_sequence_length: int = 2,
|
|
108
|
+
) -> FiniteAutomaton:
|
|
109
|
+
"""Learn state machine from CAN session.
|
|
110
|
+
|
|
111
|
+
Extracts sequences of messages around trigger messages and learns
|
|
112
|
+
a finite automaton that captures the observed patterns.
|
|
113
|
+
|
|
114
|
+
Args:
|
|
115
|
+
session: CAN session with messages to analyze.
|
|
116
|
+
trigger_ids: CAN IDs that trigger sequence extraction.
|
|
117
|
+
context_window_ms: Time window (ms) before trigger to capture.
|
|
118
|
+
min_sequence_length: Minimum sequence length to include.
|
|
119
|
+
|
|
120
|
+
Returns:
|
|
121
|
+
Learned finite automaton representing state machine.
|
|
122
|
+
|
|
123
|
+
Raises:
|
|
124
|
+
ValueError: If no sequences could be extracted.
|
|
125
|
+
"""
|
|
126
|
+
# Extract sequences around triggers
|
|
127
|
+
extractions = self.extract_sequences(
|
|
128
|
+
session=session,
|
|
129
|
+
trigger_ids=trigger_ids,
|
|
130
|
+
context_window_ms=context_window_ms,
|
|
131
|
+
)
|
|
132
|
+
|
|
133
|
+
if not extractions:
|
|
134
|
+
raise ValueError(
|
|
135
|
+
f"No sequences found for trigger IDs {[f'0x{tid:03X}' for tid in trigger_ids]}"
|
|
136
|
+
)
|
|
137
|
+
|
|
138
|
+
# Convert to symbol sequences for learning
|
|
139
|
+
traces = []
|
|
140
|
+
for extraction in extractions:
|
|
141
|
+
symbol_seq = extraction.to_symbol_sequence()
|
|
142
|
+
if len(symbol_seq) >= min_sequence_length:
|
|
143
|
+
traces.append(symbol_seq)
|
|
144
|
+
|
|
145
|
+
if not traces:
|
|
146
|
+
raise ValueError(
|
|
147
|
+
f"No sequences with length >= {min_sequence_length} found. "
|
|
148
|
+
f"Try increasing context_window_ms or reducing min_sequence_length."
|
|
149
|
+
)
|
|
150
|
+
|
|
151
|
+
# Learn state machine using RPNI
|
|
152
|
+
automaton = self._inferrer.infer_rpni(positive_traces=traces)
|
|
153
|
+
|
|
154
|
+
return automaton
|
|
155
|
+
|
|
156
|
+
def extract_sequences(
|
|
157
|
+
self,
|
|
158
|
+
session: CANSession,
|
|
159
|
+
trigger_ids: list[int],
|
|
160
|
+
context_window_ms: float = 500,
|
|
161
|
+
) -> list[SequenceExtraction]:
|
|
162
|
+
"""Extract message sequences around trigger messages.
|
|
163
|
+
|
|
164
|
+
For each occurrence of a trigger ID, extract all messages within
|
|
165
|
+
the specified time window before the trigger.
|
|
166
|
+
|
|
167
|
+
Args:
|
|
168
|
+
session: CAN session to extract from.
|
|
169
|
+
trigger_ids: CAN IDs that trigger extraction.
|
|
170
|
+
context_window_ms: Time window (ms) before trigger.
|
|
171
|
+
|
|
172
|
+
Returns:
|
|
173
|
+
List of extracted sequences.
|
|
174
|
+
"""
|
|
175
|
+
context_window_s = context_window_ms / 1000.0
|
|
176
|
+
extractions = []
|
|
177
|
+
|
|
178
|
+
# Get all messages sorted by timestamp
|
|
179
|
+
all_messages = sorted(session._messages.messages, key=lambda m: m.timestamp)
|
|
180
|
+
|
|
181
|
+
# Find trigger messages
|
|
182
|
+
trigger_messages = [msg for msg in all_messages if msg.arbitration_id in trigger_ids]
|
|
183
|
+
|
|
184
|
+
# Extract context around each trigger
|
|
185
|
+
for trigger_msg in trigger_messages:
|
|
186
|
+
window_start = trigger_msg.timestamp - context_window_s
|
|
187
|
+
window_end = trigger_msg.timestamp
|
|
188
|
+
|
|
189
|
+
# Find messages in window
|
|
190
|
+
sequence_msgs = [
|
|
191
|
+
msg for msg in all_messages if window_start <= msg.timestamp <= window_end
|
|
192
|
+
]
|
|
193
|
+
|
|
194
|
+
if not sequence_msgs:
|
|
195
|
+
continue
|
|
196
|
+
|
|
197
|
+
# Create extraction
|
|
198
|
+
extraction = SequenceExtraction(
|
|
199
|
+
trigger_id=trigger_msg.arbitration_id,
|
|
200
|
+
trigger_timestamp=trigger_msg.timestamp,
|
|
201
|
+
sequence=[msg.arbitration_id for msg in sequence_msgs],
|
|
202
|
+
timestamps=[msg.timestamp for msg in sequence_msgs],
|
|
203
|
+
window_start=window_start,
|
|
204
|
+
window_end=window_end,
|
|
205
|
+
)
|
|
206
|
+
extractions.append(extraction)
|
|
207
|
+
|
|
208
|
+
return extractions
|
|
209
|
+
|
|
210
|
+
def learn_with_states(
|
|
211
|
+
self,
|
|
212
|
+
session: CANSession,
|
|
213
|
+
state_definitions: dict[str, list[int]],
|
|
214
|
+
context_window_ms: float = 500,
|
|
215
|
+
) -> FiniteAutomaton:
|
|
216
|
+
"""Learn state machine with predefined state labels.
|
|
217
|
+
|
|
218
|
+
Instead of using trigger IDs, this method allows you to define
|
|
219
|
+
states explicitly and learn transitions between them.
|
|
220
|
+
|
|
221
|
+
Args:
|
|
222
|
+
session: CAN session to analyze.
|
|
223
|
+
state_definitions: Mapping of state names to CAN IDs that
|
|
224
|
+
indicate that state (e.g., {"IGNITION_OFF": [0x123],
|
|
225
|
+
"IGNITION_ACC": [0x124], "IGNITION_ON": [0x125]}).
|
|
226
|
+
context_window_ms: Time window for state detection.
|
|
227
|
+
|
|
228
|
+
Returns:
|
|
229
|
+
Learned finite automaton with state labels.
|
|
230
|
+
|
|
231
|
+
Raises:
|
|
232
|
+
ValueError: If no state sequences could be extracted.
|
|
233
|
+
"""
|
|
234
|
+
context_window_s = context_window_ms / 1000.0
|
|
235
|
+
|
|
236
|
+
# Build reverse mapping: CAN ID -> state name
|
|
237
|
+
id_to_state: dict[int, str] = {}
|
|
238
|
+
for state_name, can_ids in state_definitions.items():
|
|
239
|
+
for can_id in can_ids:
|
|
240
|
+
id_to_state[can_id] = state_name
|
|
241
|
+
|
|
242
|
+
# Get all messages sorted by timestamp
|
|
243
|
+
all_messages = sorted(session._messages.messages, key=lambda m: m.timestamp)
|
|
244
|
+
|
|
245
|
+
# Detect state transitions
|
|
246
|
+
state_sequences = []
|
|
247
|
+
current_sequence = []
|
|
248
|
+
last_state_time = None
|
|
249
|
+
|
|
250
|
+
for msg in all_messages:
|
|
251
|
+
if msg.arbitration_id in id_to_state:
|
|
252
|
+
state_name = id_to_state[msg.arbitration_id]
|
|
253
|
+
|
|
254
|
+
# Check if this is part of current sequence
|
|
255
|
+
if last_state_time is None or (msg.timestamp - last_state_time) <= context_window_s:
|
|
256
|
+
current_sequence.append(state_name)
|
|
257
|
+
else:
|
|
258
|
+
# New sequence
|
|
259
|
+
if len(current_sequence) >= 2:
|
|
260
|
+
state_sequences.append(current_sequence)
|
|
261
|
+
current_sequence = [state_name]
|
|
262
|
+
|
|
263
|
+
last_state_time = msg.timestamp
|
|
264
|
+
|
|
265
|
+
# Add final sequence
|
|
266
|
+
if len(current_sequence) >= 2:
|
|
267
|
+
state_sequences.append(current_sequence)
|
|
268
|
+
|
|
269
|
+
if not state_sequences:
|
|
270
|
+
raise ValueError(
|
|
271
|
+
f"No state sequences found. Check state_definitions: {state_definitions}"
|
|
272
|
+
)
|
|
273
|
+
|
|
274
|
+
# Learn state machine
|
|
275
|
+
automaton = self._inferrer.infer_rpni(positive_traces=state_sequences)
|
|
276
|
+
|
|
277
|
+
return automaton
|
|
278
|
+
|
|
279
|
+
|
|
280
|
+
def learn_state_machine(
|
|
281
|
+
session: CANSession,
|
|
282
|
+
trigger_ids: list[int],
|
|
283
|
+
context_window_ms: float = 500,
|
|
284
|
+
) -> FiniteAutomaton:
|
|
285
|
+
"""Convenience function to learn state machine from session.
|
|
286
|
+
|
|
287
|
+
Args:
|
|
288
|
+
session: CAN session to analyze.
|
|
289
|
+
trigger_ids: CAN IDs that trigger sequence extraction.
|
|
290
|
+
context_window_ms: Time window (ms) before trigger.
|
|
291
|
+
|
|
292
|
+
Returns:
|
|
293
|
+
Learned finite automaton.
|
|
294
|
+
"""
|
|
295
|
+
learner = CANStateMachine()
|
|
296
|
+
return learner.learn_from_session(
|
|
297
|
+
session=session,
|
|
298
|
+
trigger_ids=trigger_ids,
|
|
299
|
+
context_window_ms=context_window_ms,
|
|
300
|
+
)
|