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,369 @@
|
|
|
1
|
+
"""Automotive CAN bus visualization utilities.
|
|
2
|
+
|
|
3
|
+
This module provides visualization functions for CAN bus data, including
|
|
4
|
+
message timelines, signal plots, frequency analysis, and bus utilization.
|
|
5
|
+
"""
|
|
6
|
+
|
|
7
|
+
from __future__ import annotations
|
|
8
|
+
|
|
9
|
+
from collections import Counter
|
|
10
|
+
from typing import TYPE_CHECKING, Any
|
|
11
|
+
|
|
12
|
+
import matplotlib.pyplot as plt
|
|
13
|
+
import numpy as np
|
|
14
|
+
|
|
15
|
+
if TYPE_CHECKING:
|
|
16
|
+
from oscura.automotive.can.models import CANMessage, CANMessageList
|
|
17
|
+
|
|
18
|
+
__all__ = [
|
|
19
|
+
"plot_bus_timeline",
|
|
20
|
+
"plot_bus_utilization",
|
|
21
|
+
"plot_message_distribution",
|
|
22
|
+
"plot_message_frequency",
|
|
23
|
+
"plot_signal_timeline",
|
|
24
|
+
]
|
|
25
|
+
|
|
26
|
+
|
|
27
|
+
def plot_bus_timeline(
|
|
28
|
+
messages: CANMessageList,
|
|
29
|
+
*,
|
|
30
|
+
max_messages: int = 1000,
|
|
31
|
+
figsize: tuple[float, float] = (12, 6),
|
|
32
|
+
) -> Any:
|
|
33
|
+
"""Plot CAN bus message timeline.
|
|
34
|
+
|
|
35
|
+
Creates a scatter plot showing when each CAN ID appeared on the bus.
|
|
36
|
+
Useful for understanding bus activity patterns and message timing.
|
|
37
|
+
|
|
38
|
+
Args:
|
|
39
|
+
messages: List of CAN messages to plot.
|
|
40
|
+
max_messages: Maximum number of messages to plot (for performance).
|
|
41
|
+
figsize: Figure size (width, height) in inches.
|
|
42
|
+
|
|
43
|
+
Returns:
|
|
44
|
+
Matplotlib figure object.
|
|
45
|
+
|
|
46
|
+
Raises:
|
|
47
|
+
ValueError: If messages list is empty.
|
|
48
|
+
|
|
49
|
+
Example:
|
|
50
|
+
>>> from oscura.automotive.loaders import load_automotive_log
|
|
51
|
+
>>> messages = load_automotive_log("capture.blf")
|
|
52
|
+
>>> plot_bus_timeline(messages)
|
|
53
|
+
"""
|
|
54
|
+
if len(messages) == 0:
|
|
55
|
+
raise ValueError("No messages to plot")
|
|
56
|
+
|
|
57
|
+
# Limit messages for performance
|
|
58
|
+
plot_messages = messages[:max_messages]
|
|
59
|
+
|
|
60
|
+
# Ensure plot_messages is a list for iteration
|
|
61
|
+
if isinstance(plot_messages, CANMessage):
|
|
62
|
+
# Single message returned from indexing
|
|
63
|
+
msg_list: list[CANMessage] = [plot_messages]
|
|
64
|
+
else:
|
|
65
|
+
# Must be list[CANMessage] from slice
|
|
66
|
+
msg_list = plot_messages # type: ignore[assignment]
|
|
67
|
+
|
|
68
|
+
# Extract timestamps and IDs
|
|
69
|
+
timestamps = [_get_timestamp(msg) for msg in msg_list]
|
|
70
|
+
arb_ids = [_get_arbitration_id(msg) for msg in msg_list]
|
|
71
|
+
|
|
72
|
+
# Create plot
|
|
73
|
+
fig, ax = plt.subplots(figsize=figsize)
|
|
74
|
+
|
|
75
|
+
ax.scatter(timestamps, arb_ids, alpha=0.5, s=2)
|
|
76
|
+
ax.set_xlabel("Time (s)")
|
|
77
|
+
ax.set_ylabel("CAN Arbitration ID")
|
|
78
|
+
ax.set_title(f"CAN Bus Timeline ({len(msg_list)} messages)")
|
|
79
|
+
ax.grid(True, alpha=0.3)
|
|
80
|
+
|
|
81
|
+
# Format y-axis as hex
|
|
82
|
+
ax.yaxis.set_major_formatter(plt.FuncFormatter(lambda x, _: f"0x{int(x):03X}"))
|
|
83
|
+
|
|
84
|
+
plt.tight_layout()
|
|
85
|
+
return fig
|
|
86
|
+
|
|
87
|
+
|
|
88
|
+
def plot_message_frequency(
|
|
89
|
+
messages: CANMessageList,
|
|
90
|
+
*,
|
|
91
|
+
top_n: int = 20,
|
|
92
|
+
figsize: tuple[float, float] = (10, 6),
|
|
93
|
+
) -> Any:
|
|
94
|
+
"""Plot CAN message frequency distribution.
|
|
95
|
+
|
|
96
|
+
Creates a bar chart showing the most frequent CAN IDs.
|
|
97
|
+
|
|
98
|
+
Args:
|
|
99
|
+
messages: List of CAN messages to analyze.
|
|
100
|
+
top_n: Number of top IDs to display.
|
|
101
|
+
figsize: Figure size (width, height) in inches.
|
|
102
|
+
|
|
103
|
+
Returns:
|
|
104
|
+
Matplotlib figure object.
|
|
105
|
+
|
|
106
|
+
Raises:
|
|
107
|
+
ValueError: If messages list is empty.
|
|
108
|
+
|
|
109
|
+
Example:
|
|
110
|
+
>>> plot_message_frequency(messages, top_n=15)
|
|
111
|
+
"""
|
|
112
|
+
if len(messages) == 0:
|
|
113
|
+
raise ValueError("No messages to plot")
|
|
114
|
+
|
|
115
|
+
# Count message occurrences
|
|
116
|
+
id_counts = Counter(_get_arbitration_id(msg) for msg in messages)
|
|
117
|
+
most_common = id_counts.most_common(top_n)
|
|
118
|
+
|
|
119
|
+
ids = [f"0x{id_:03X}" for id_, _ in most_common]
|
|
120
|
+
counts = [count for _, count in most_common]
|
|
121
|
+
|
|
122
|
+
# Create plot
|
|
123
|
+
fig, ax = plt.subplots(figsize=figsize)
|
|
124
|
+
|
|
125
|
+
bars = ax.bar(range(len(ids)), counts)
|
|
126
|
+
ax.set_xlabel("CAN ID")
|
|
127
|
+
ax.set_ylabel("Message Count")
|
|
128
|
+
ax.set_title(f"Top {top_n} Most Frequent CAN IDs")
|
|
129
|
+
ax.set_xticks(range(len(ids)))
|
|
130
|
+
ax.set_xticklabels(ids, rotation=45, ha="right")
|
|
131
|
+
ax.grid(True, axis="y", alpha=0.3)
|
|
132
|
+
|
|
133
|
+
# Color bars by frequency
|
|
134
|
+
max_count = max(counts)
|
|
135
|
+
for bar, count in zip(bars, counts, strict=False):
|
|
136
|
+
bar.set_color(plt.cm.viridis(count / max_count))
|
|
137
|
+
|
|
138
|
+
plt.tight_layout()
|
|
139
|
+
return fig
|
|
140
|
+
|
|
141
|
+
|
|
142
|
+
def plot_signal_timeline(
|
|
143
|
+
messages: CANMessageList,
|
|
144
|
+
arb_id: int,
|
|
145
|
+
byte_index: int,
|
|
146
|
+
*,
|
|
147
|
+
figsize: tuple[float, float] = (12, 4),
|
|
148
|
+
) -> Any:
|
|
149
|
+
"""Plot timeline of a specific byte in CAN messages.
|
|
150
|
+
|
|
151
|
+
Useful for visualizing how a particular signal changes over time.
|
|
152
|
+
|
|
153
|
+
Args:
|
|
154
|
+
messages: List of CAN messages.
|
|
155
|
+
arb_id: CAN arbitration ID to filter.
|
|
156
|
+
byte_index: Index of byte to plot (0-7).
|
|
157
|
+
figsize: Figure size (width, height) in inches.
|
|
158
|
+
|
|
159
|
+
Returns:
|
|
160
|
+
Matplotlib figure object.
|
|
161
|
+
|
|
162
|
+
Raises:
|
|
163
|
+
ValueError: If no messages found with specified ID or byte index.
|
|
164
|
+
|
|
165
|
+
Example:
|
|
166
|
+
>>> # Plot byte 0 of CAN ID 0x123
|
|
167
|
+
>>> plot_signal_timeline(messages, 0x123, 0)
|
|
168
|
+
"""
|
|
169
|
+
# Filter messages by ID
|
|
170
|
+
filtered = [msg for msg in messages if _get_arbitration_id(msg) == arb_id]
|
|
171
|
+
|
|
172
|
+
if len(filtered) == 0:
|
|
173
|
+
raise ValueError(f"No messages found with ID 0x{arb_id:03X}")
|
|
174
|
+
|
|
175
|
+
# Extract timestamps and byte values
|
|
176
|
+
timestamps = []
|
|
177
|
+
values = []
|
|
178
|
+
|
|
179
|
+
for msg in filtered:
|
|
180
|
+
msg_data = _get_data(msg)
|
|
181
|
+
if len(msg_data) > byte_index:
|
|
182
|
+
timestamps.append(_get_timestamp(msg))
|
|
183
|
+
values.append(msg_data[byte_index])
|
|
184
|
+
|
|
185
|
+
if len(values) == 0:
|
|
186
|
+
raise ValueError(f"No messages with ID 0x{arb_id:03X} have byte at index {byte_index}")
|
|
187
|
+
|
|
188
|
+
# Create plot
|
|
189
|
+
fig, ax = plt.subplots(figsize=figsize)
|
|
190
|
+
|
|
191
|
+
ax.plot(timestamps, values, linewidth=1, marker=".", markersize=3)
|
|
192
|
+
ax.set_xlabel("Time (s)")
|
|
193
|
+
ax.set_ylabel(f"Byte {byte_index} Value")
|
|
194
|
+
ax.set_title(f"CAN ID 0x{arb_id:03X} - Byte {byte_index} Timeline")
|
|
195
|
+
ax.grid(True, alpha=0.3)
|
|
196
|
+
|
|
197
|
+
plt.tight_layout()
|
|
198
|
+
return fig
|
|
199
|
+
|
|
200
|
+
|
|
201
|
+
def plot_bus_utilization(
|
|
202
|
+
messages: CANMessageList,
|
|
203
|
+
*,
|
|
204
|
+
window_size: float = 1.0,
|
|
205
|
+
figsize: tuple[float, float] = (12, 5),
|
|
206
|
+
) -> Any:
|
|
207
|
+
"""Plot CAN bus utilization over time.
|
|
208
|
+
|
|
209
|
+
Calculates and plots message rate in messages/second over time windows.
|
|
210
|
+
|
|
211
|
+
Args:
|
|
212
|
+
messages: List of CAN messages.
|
|
213
|
+
window_size: Time window size in seconds for calculating rate.
|
|
214
|
+
figsize: Figure size (width, height) in inches.
|
|
215
|
+
|
|
216
|
+
Returns:
|
|
217
|
+
Matplotlib figure object.
|
|
218
|
+
|
|
219
|
+
Raises:
|
|
220
|
+
ValueError: If messages list is empty or all messages have same timestamp.
|
|
221
|
+
|
|
222
|
+
Example:
|
|
223
|
+
>>> # Plot bus utilization with 0.5s windows
|
|
224
|
+
>>> plot_bus_utilization(messages, window_size=0.5)
|
|
225
|
+
"""
|
|
226
|
+
if len(messages) == 0:
|
|
227
|
+
raise ValueError("No messages to plot")
|
|
228
|
+
|
|
229
|
+
# Get time range - use iteration to handle type union
|
|
230
|
+
first_msg: CANMessage = next(iter(messages))
|
|
231
|
+
last_msg: CANMessage = messages.messages[-1] # Access internal list directly
|
|
232
|
+
start_time = _get_timestamp(first_msg)
|
|
233
|
+
end_time = _get_timestamp(last_msg)
|
|
234
|
+
duration = end_time - start_time
|
|
235
|
+
|
|
236
|
+
if duration == 0:
|
|
237
|
+
raise ValueError("All messages have the same timestamp")
|
|
238
|
+
|
|
239
|
+
# Create time bins
|
|
240
|
+
num_bins = int(duration / window_size) + 1
|
|
241
|
+
bins = np.linspace(start_time, end_time, num_bins)
|
|
242
|
+
bin_centers = (bins[:-1] + bins[1:]) / 2
|
|
243
|
+
|
|
244
|
+
# Count messages per bin
|
|
245
|
+
timestamps = np.array([_get_timestamp(msg) for msg in messages])
|
|
246
|
+
counts, _ = np.histogram(timestamps, bins=bins)
|
|
247
|
+
|
|
248
|
+
# Convert to messages/second
|
|
249
|
+
rates = counts / window_size
|
|
250
|
+
|
|
251
|
+
# Create plot
|
|
252
|
+
fig, ax = plt.subplots(figsize=figsize)
|
|
253
|
+
|
|
254
|
+
ax.plot(bin_centers, rates, linewidth=1.5)
|
|
255
|
+
ax.fill_between(bin_centers, rates, alpha=0.3)
|
|
256
|
+
ax.set_xlabel("Time (s)")
|
|
257
|
+
ax.set_ylabel("Message Rate (msg/s)")
|
|
258
|
+
ax.set_title(f"CAN Bus Utilization (window={window_size}s)")
|
|
259
|
+
ax.grid(True, alpha=0.3)
|
|
260
|
+
|
|
261
|
+
# Add statistics
|
|
262
|
+
mean_rate = np.mean(rates)
|
|
263
|
+
ax.axhline(
|
|
264
|
+
mean_rate, color="r", linestyle="--", alpha=0.5, label=f"Mean: {mean_rate:.1f} msg/s"
|
|
265
|
+
)
|
|
266
|
+
ax.legend()
|
|
267
|
+
|
|
268
|
+
plt.tight_layout()
|
|
269
|
+
return fig
|
|
270
|
+
|
|
271
|
+
|
|
272
|
+
def plot_message_distribution(
|
|
273
|
+
messages: CANMessageList,
|
|
274
|
+
*,
|
|
275
|
+
figsize: tuple[float, float] = (10, 8),
|
|
276
|
+
) -> Any:
|
|
277
|
+
"""Plot distribution of CAN message properties.
|
|
278
|
+
|
|
279
|
+
Creates a multi-panel plot showing:
|
|
280
|
+
- DLC (data length) distribution
|
|
281
|
+
- Standard vs Extended ID ratio
|
|
282
|
+
- Message timing histogram
|
|
283
|
+
|
|
284
|
+
Args:
|
|
285
|
+
messages: List of CAN messages.
|
|
286
|
+
figsize: Figure size (width, height) in inches.
|
|
287
|
+
|
|
288
|
+
Returns:
|
|
289
|
+
Matplotlib figure object.
|
|
290
|
+
|
|
291
|
+
Raises:
|
|
292
|
+
ValueError: If messages list is empty.
|
|
293
|
+
|
|
294
|
+
Example:
|
|
295
|
+
>>> plot_message_distribution(messages)
|
|
296
|
+
"""
|
|
297
|
+
if len(messages) == 0:
|
|
298
|
+
raise ValueError("No messages to plot")
|
|
299
|
+
|
|
300
|
+
# Create subplots
|
|
301
|
+
fig, (ax1, ax2, ax3) = plt.subplots(3, 1, figsize=figsize)
|
|
302
|
+
|
|
303
|
+
# 1. DLC distribution
|
|
304
|
+
dlcs = [_get_dlc(msg) for msg in messages]
|
|
305
|
+
dlc_counts = Counter(dlcs)
|
|
306
|
+
dlc_values = sorted(dlc_counts.keys())
|
|
307
|
+
dlc_counts_list = [dlc_counts[d] for d in dlc_values]
|
|
308
|
+
|
|
309
|
+
ax1.bar(dlc_values, dlc_counts_list, color="steelblue")
|
|
310
|
+
ax1.set_xlabel("Data Length Code (DLC)")
|
|
311
|
+
ax1.set_ylabel("Count")
|
|
312
|
+
ax1.set_title("Message DLC Distribution")
|
|
313
|
+
ax1.set_xticks(range(9))
|
|
314
|
+
ax1.grid(True, axis="y", alpha=0.3)
|
|
315
|
+
|
|
316
|
+
# 2. ID type distribution
|
|
317
|
+
std_count = sum(1 for msg in messages if not _is_extended(msg))
|
|
318
|
+
ext_count = sum(1 for msg in messages if _is_extended(msg))
|
|
319
|
+
|
|
320
|
+
ax2.bar(
|
|
321
|
+
["Standard (11-bit)", "Extended (29-bit)"],
|
|
322
|
+
[std_count, ext_count],
|
|
323
|
+
color=["green", "orange"],
|
|
324
|
+
)
|
|
325
|
+
ax2.set_ylabel("Count")
|
|
326
|
+
ax2.set_title("ID Type Distribution")
|
|
327
|
+
ax2.grid(True, axis="y", alpha=0.3)
|
|
328
|
+
|
|
329
|
+
# 3. Inter-message timing histogram
|
|
330
|
+
if len(messages) > 1:
|
|
331
|
+
timestamps = [_get_timestamp(msg) for msg in messages]
|
|
332
|
+
intervals = np.diff(timestamps)
|
|
333
|
+
# Filter out very large gaps (likely recording breaks)
|
|
334
|
+
intervals = intervals[intervals < np.percentile(intervals, 99)]
|
|
335
|
+
|
|
336
|
+
ax3.hist(intervals * 1000, bins=50, color="purple", alpha=0.7) # Convert to ms
|
|
337
|
+
ax3.set_xlabel("Inter-message Time (ms)")
|
|
338
|
+
ax3.set_ylabel("Count")
|
|
339
|
+
ax3.set_title("Message Timing Distribution")
|
|
340
|
+
ax3.grid(True, axis="y", alpha=0.3)
|
|
341
|
+
|
|
342
|
+
plt.tight_layout()
|
|
343
|
+
return fig
|
|
344
|
+
|
|
345
|
+
|
|
346
|
+
# Helper functions to access CANMessage attributes safely
|
|
347
|
+
def _get_timestamp(msg: CANMessage) -> float:
|
|
348
|
+
"""Get timestamp from CANMessage."""
|
|
349
|
+
return float(msg.timestamp)
|
|
350
|
+
|
|
351
|
+
|
|
352
|
+
def _get_arbitration_id(msg: CANMessage) -> int:
|
|
353
|
+
"""Get arbitration_id from CANMessage."""
|
|
354
|
+
return int(msg.arbitration_id)
|
|
355
|
+
|
|
356
|
+
|
|
357
|
+
def _get_data(msg: CANMessage) -> bytes:
|
|
358
|
+
"""Get data from CANMessage."""
|
|
359
|
+
return bytes(msg.data)
|
|
360
|
+
|
|
361
|
+
|
|
362
|
+
def _get_dlc(msg: CANMessage) -> int:
|
|
363
|
+
"""Get DLC from CANMessage."""
|
|
364
|
+
return int(msg.dlc)
|
|
365
|
+
|
|
366
|
+
|
|
367
|
+
def _is_extended(msg: CANMessage) -> bool:
|
|
368
|
+
"""Check if CANMessage has extended ID."""
|
|
369
|
+
return bool(msg.is_extended)
|
oscura/batch/__init__.py
ADDED
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
"""Batch processing functionality for TraceKit.
|
|
2
|
+
|
|
3
|
+
|
|
4
|
+
This module enables efficient batch analysis of multiple signal files
|
|
5
|
+
with parallel execution support and comprehensive result aggregation.
|
|
6
|
+
"""
|
|
7
|
+
|
|
8
|
+
from oscura.batch.advanced import (
|
|
9
|
+
AdvancedBatchProcessor,
|
|
10
|
+
BatchCheckpoint,
|
|
11
|
+
BatchConfig,
|
|
12
|
+
FileResult,
|
|
13
|
+
resume_batch,
|
|
14
|
+
)
|
|
15
|
+
from oscura.batch.aggregate import aggregate_results
|
|
16
|
+
from oscura.batch.analyze import batch_analyze
|
|
17
|
+
from oscura.batch.logging import (
|
|
18
|
+
BatchLogger,
|
|
19
|
+
BatchSummary,
|
|
20
|
+
FileLogEntry,
|
|
21
|
+
FileLogger,
|
|
22
|
+
aggregate_batch_logs,
|
|
23
|
+
)
|
|
24
|
+
from oscura.batch.metrics import (
|
|
25
|
+
BatchMetrics,
|
|
26
|
+
BatchMetricsSummary,
|
|
27
|
+
ErrorBreakdown,
|
|
28
|
+
FileMetrics,
|
|
29
|
+
ThroughputStats,
|
|
30
|
+
TimingStats,
|
|
31
|
+
get_batch_stats,
|
|
32
|
+
)
|
|
33
|
+
|
|
34
|
+
__all__ = [
|
|
35
|
+
# Advanced batch processing (API-012)
|
|
36
|
+
"AdvancedBatchProcessor",
|
|
37
|
+
"BatchCheckpoint",
|
|
38
|
+
"BatchConfig",
|
|
39
|
+
"BatchLogger",
|
|
40
|
+
"BatchMetrics",
|
|
41
|
+
"BatchMetricsSummary",
|
|
42
|
+
"BatchSummary",
|
|
43
|
+
"ErrorBreakdown",
|
|
44
|
+
"FileLogEntry",
|
|
45
|
+
"FileLogger",
|
|
46
|
+
"FileMetrics",
|
|
47
|
+
"FileResult",
|
|
48
|
+
"ThroughputStats",
|
|
49
|
+
"TimingStats",
|
|
50
|
+
"aggregate_batch_logs",
|
|
51
|
+
"aggregate_results",
|
|
52
|
+
"batch_analyze",
|
|
53
|
+
"get_batch_stats",
|
|
54
|
+
"resume_batch",
|
|
55
|
+
]
|