oscura 0.0.1__py3-none-any.whl → 0.1.1__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- 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.1.dist-info/METADATA +300 -0
- oscura-0.1.1.dist-info/RECORD +463 -0
- oscura-0.1.1.dist-info/entry_points.txt +2 -0
- {oscura-0.0.1.dist-info → oscura-0.1.1.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.1.dist-info}/WHEEL +0 -0
|
@@ -0,0 +1,337 @@
|
|
|
1
|
+
"""Digital timing diagram visualization.
|
|
2
|
+
|
|
3
|
+
This module provides timing diagrams for digital signals with
|
|
4
|
+
protocol decode overlay support.
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
Example:
|
|
8
|
+
>>> from oscura.visualization.digital import plot_timing
|
|
9
|
+
>>> fig = plot_timing([clk, data, cs], names=["CLK", "DATA", "CS"])
|
|
10
|
+
>>> plt.show()
|
|
11
|
+
|
|
12
|
+
References:
|
|
13
|
+
matplotlib best practices for digital waveform visualization
|
|
14
|
+
"""
|
|
15
|
+
|
|
16
|
+
from __future__ import annotations
|
|
17
|
+
|
|
18
|
+
from typing import TYPE_CHECKING
|
|
19
|
+
|
|
20
|
+
import numpy as np
|
|
21
|
+
|
|
22
|
+
try:
|
|
23
|
+
import matplotlib.pyplot as plt
|
|
24
|
+
from matplotlib.patches import Rectangle
|
|
25
|
+
|
|
26
|
+
HAS_MATPLOTLIB = True
|
|
27
|
+
except ImportError:
|
|
28
|
+
HAS_MATPLOTLIB = False
|
|
29
|
+
|
|
30
|
+
from oscura.core.types import DigitalTrace, WaveformTrace
|
|
31
|
+
|
|
32
|
+
if TYPE_CHECKING:
|
|
33
|
+
from collections.abc import Sequence
|
|
34
|
+
|
|
35
|
+
from matplotlib.axes import Axes
|
|
36
|
+
from matplotlib.figure import Figure
|
|
37
|
+
|
|
38
|
+
from oscura.analyzers.protocols.base import Annotation
|
|
39
|
+
|
|
40
|
+
|
|
41
|
+
def plot_timing(
|
|
42
|
+
traces: Sequence[WaveformTrace | DigitalTrace],
|
|
43
|
+
*,
|
|
44
|
+
names: list[str] | None = None,
|
|
45
|
+
annotations: list[list[Annotation]] | None = None,
|
|
46
|
+
time_unit: str = "auto",
|
|
47
|
+
show_grid: bool = True,
|
|
48
|
+
figsize: tuple[float, float] | None = None,
|
|
49
|
+
title: str | None = None,
|
|
50
|
+
time_range: tuple[float, float] | None = None,
|
|
51
|
+
threshold: float | str = "auto",
|
|
52
|
+
) -> Figure:
|
|
53
|
+
"""Plot digital timing diagram with protocol decode overlay.
|
|
54
|
+
|
|
55
|
+
Creates a stacked timing diagram showing digital waveforms with
|
|
56
|
+
timing information and optional protocol decode annotations.
|
|
57
|
+
|
|
58
|
+
Args:
|
|
59
|
+
traces: List of traces to plot (analog or digital).
|
|
60
|
+
names: Channel names for labels. If None, uses CH1, CH2, etc.
|
|
61
|
+
annotations: List of protocol annotations per channel (optional).
|
|
62
|
+
time_unit: Time unit ("s", "ms", "us", "ns", "auto").
|
|
63
|
+
show_grid: Show vertical grid lines at time intervals.
|
|
64
|
+
figsize: Figure size (width, height) in inches.
|
|
65
|
+
title: Overall figure title.
|
|
66
|
+
time_range: Optional (start, end) time range to display in seconds.
|
|
67
|
+
threshold: Threshold for analog-to-digital conversion ("auto" or float).
|
|
68
|
+
|
|
69
|
+
Returns:
|
|
70
|
+
Matplotlib Figure object.
|
|
71
|
+
|
|
72
|
+
Raises:
|
|
73
|
+
ImportError: If matplotlib is not available.
|
|
74
|
+
ValueError: If traces list is empty.
|
|
75
|
+
|
|
76
|
+
Example:
|
|
77
|
+
>>> fig = plot_timing(
|
|
78
|
+
... [clk_trace, data_trace, cs_trace],
|
|
79
|
+
... names=["CLK", "DATA", "CS"],
|
|
80
|
+
... annotations=[[], uart_annotations, []]
|
|
81
|
+
... )
|
|
82
|
+
>>> plt.savefig("timing.png")
|
|
83
|
+
|
|
84
|
+
References:
|
|
85
|
+
IEEE 181-2011: Standard for Transitional Waveform Definitions
|
|
86
|
+
"""
|
|
87
|
+
if not HAS_MATPLOTLIB:
|
|
88
|
+
raise ImportError("matplotlib is required for visualization")
|
|
89
|
+
|
|
90
|
+
if len(traces) == 0:
|
|
91
|
+
raise ValueError("traces list cannot be empty")
|
|
92
|
+
|
|
93
|
+
n_channels = len(traces)
|
|
94
|
+
|
|
95
|
+
if names is None:
|
|
96
|
+
names = [f"CH{i + 1}" for i in range(n_channels)]
|
|
97
|
+
|
|
98
|
+
if len(names) != n_channels:
|
|
99
|
+
raise ValueError(f"names length ({len(names)}) must match traces ({n_channels})")
|
|
100
|
+
|
|
101
|
+
if figsize is None:
|
|
102
|
+
figsize = (12, 1.5 * n_channels)
|
|
103
|
+
|
|
104
|
+
# Convert analog traces to digital
|
|
105
|
+
from oscura.analyzers.digital.extraction import to_digital
|
|
106
|
+
|
|
107
|
+
digital_traces: list[DigitalTrace] = []
|
|
108
|
+
for trace in traces:
|
|
109
|
+
if isinstance(trace, WaveformTrace):
|
|
110
|
+
digital_traces.append(to_digital(trace, threshold=threshold)) # type: ignore[arg-type]
|
|
111
|
+
else:
|
|
112
|
+
digital_traces.append(trace)
|
|
113
|
+
|
|
114
|
+
# Auto-select time unit from first trace
|
|
115
|
+
if time_unit == "auto" and len(digital_traces) > 0:
|
|
116
|
+
ref_trace = digital_traces[0]
|
|
117
|
+
duration = len(ref_trace.data) * ref_trace.metadata.time_base
|
|
118
|
+
if duration < 1e-6:
|
|
119
|
+
time_unit = "ns"
|
|
120
|
+
elif duration < 1e-3:
|
|
121
|
+
time_unit = "us"
|
|
122
|
+
elif duration < 1:
|
|
123
|
+
time_unit = "ms"
|
|
124
|
+
else:
|
|
125
|
+
time_unit = "s"
|
|
126
|
+
|
|
127
|
+
time_multipliers = {"s": 1.0, "ms": 1e3, "us": 1e6, "ns": 1e9}
|
|
128
|
+
multiplier = time_multipliers.get(time_unit, 1.0)
|
|
129
|
+
|
|
130
|
+
# Create figure
|
|
131
|
+
fig, axes = plt.subplots(
|
|
132
|
+
n_channels,
|
|
133
|
+
1,
|
|
134
|
+
figsize=figsize,
|
|
135
|
+
sharex=True,
|
|
136
|
+
)
|
|
137
|
+
|
|
138
|
+
if n_channels == 1:
|
|
139
|
+
axes = [axes]
|
|
140
|
+
|
|
141
|
+
# Determine time range
|
|
142
|
+
if time_range is not None:
|
|
143
|
+
start_time, end_time = time_range
|
|
144
|
+
else:
|
|
145
|
+
start_time = 0.0
|
|
146
|
+
end_time = max(trace.duration for trace in digital_traces if len(trace.data) > 0)
|
|
147
|
+
|
|
148
|
+
for i, (trace, name, ax) in enumerate(zip(digital_traces, names, axes, strict=False)):
|
|
149
|
+
time = trace.time_vector * multiplier
|
|
150
|
+
|
|
151
|
+
# Filter to time range
|
|
152
|
+
if time_range is not None:
|
|
153
|
+
start_idx = int(np.searchsorted(trace.time_vector, start_time))
|
|
154
|
+
end_idx = int(np.searchsorted(trace.time_vector, end_time))
|
|
155
|
+
time = time[start_idx:end_idx]
|
|
156
|
+
data_slice = trace.data[start_idx:end_idx]
|
|
157
|
+
else:
|
|
158
|
+
data_slice = trace.data
|
|
159
|
+
|
|
160
|
+
# Plot digital waveform as step function
|
|
161
|
+
ax.step(
|
|
162
|
+
time,
|
|
163
|
+
data_slice.astype(int),
|
|
164
|
+
where="post",
|
|
165
|
+
color=f"C{i}",
|
|
166
|
+
linewidth=1.5,
|
|
167
|
+
)
|
|
168
|
+
|
|
169
|
+
# Set up digital signal display
|
|
170
|
+
ax.set_ylim(-0.2, 1.2)
|
|
171
|
+
ax.set_yticks([0, 1])
|
|
172
|
+
ax.set_yticklabels(["0", "1"])
|
|
173
|
+
ax.set_ylabel(name, rotation=0, ha="right", va="center", fontweight="bold")
|
|
174
|
+
|
|
175
|
+
if show_grid:
|
|
176
|
+
ax.grid(True, alpha=0.2, axis="x")
|
|
177
|
+
|
|
178
|
+
# Add protocol annotations if provided
|
|
179
|
+
if annotations is not None and i < len(annotations) and annotations[i]:
|
|
180
|
+
_add_protocol_annotations(ax, annotations[i], multiplier, time_unit)
|
|
181
|
+
|
|
182
|
+
# Remove x-axis labels except for bottom plot
|
|
183
|
+
if i < n_channels - 1:
|
|
184
|
+
ax.set_xticklabels([])
|
|
185
|
+
|
|
186
|
+
# Set x-label on bottom plot
|
|
187
|
+
axes[-1].set_xlabel(f"Time ({time_unit})")
|
|
188
|
+
|
|
189
|
+
if title:
|
|
190
|
+
fig.suptitle(title, fontsize=14, fontweight="bold")
|
|
191
|
+
|
|
192
|
+
fig.tight_layout()
|
|
193
|
+
return fig
|
|
194
|
+
|
|
195
|
+
|
|
196
|
+
def _add_protocol_annotations(
|
|
197
|
+
ax: Axes,
|
|
198
|
+
annotations: list[Annotation],
|
|
199
|
+
multiplier: float,
|
|
200
|
+
time_unit: str,
|
|
201
|
+
) -> None:
|
|
202
|
+
"""Add protocol decode annotations to timing diagram.
|
|
203
|
+
|
|
204
|
+
Args:
|
|
205
|
+
ax: Matplotlib axes to annotate.
|
|
206
|
+
annotations: List of protocol annotations.
|
|
207
|
+
multiplier: Time unit multiplier for display.
|
|
208
|
+
time_unit: Time unit string.
|
|
209
|
+
"""
|
|
210
|
+
for ann in annotations:
|
|
211
|
+
# Get annotation time range
|
|
212
|
+
start_time = ann.start_sample * multiplier if hasattr(ann, "start_sample") else 0
|
|
213
|
+
end_time = ann.end_sample * multiplier if hasattr(ann, "end_sample") else start_time
|
|
214
|
+
|
|
215
|
+
# Get annotation text and level
|
|
216
|
+
if hasattr(ann, "data"):
|
|
217
|
+
text = str(ann.data)
|
|
218
|
+
elif hasattr(ann, "value"):
|
|
219
|
+
text = str(ann.value)
|
|
220
|
+
else:
|
|
221
|
+
text = str(ann)
|
|
222
|
+
|
|
223
|
+
# Determine annotation color based on type/level
|
|
224
|
+
color = "lightblue"
|
|
225
|
+
if hasattr(ann, "level"):
|
|
226
|
+
level_str = str(ann.level).lower()
|
|
227
|
+
if "error" in level_str or "warn" in level_str:
|
|
228
|
+
color = "lightcoral"
|
|
229
|
+
elif "data" in level_str or "byte" in level_str:
|
|
230
|
+
color = "lightgreen"
|
|
231
|
+
elif "start" in level_str or "stop" in level_str:
|
|
232
|
+
color = "lightyellow"
|
|
233
|
+
|
|
234
|
+
# Draw annotation box
|
|
235
|
+
width = end_time - start_time if end_time > start_time else multiplier * 10
|
|
236
|
+
rect = Rectangle(
|
|
237
|
+
(start_time, 1.05),
|
|
238
|
+
width,
|
|
239
|
+
0.15,
|
|
240
|
+
facecolor=color,
|
|
241
|
+
edgecolor="black",
|
|
242
|
+
linewidth=0.5,
|
|
243
|
+
alpha=0.7,
|
|
244
|
+
)
|
|
245
|
+
ax.add_patch(rect)
|
|
246
|
+
|
|
247
|
+
# Add text label
|
|
248
|
+
mid_time = start_time + width / 2
|
|
249
|
+
ax.text(
|
|
250
|
+
mid_time,
|
|
251
|
+
1.125,
|
|
252
|
+
text,
|
|
253
|
+
ha="center",
|
|
254
|
+
va="center",
|
|
255
|
+
fontsize=7,
|
|
256
|
+
fontfamily="monospace",
|
|
257
|
+
)
|
|
258
|
+
|
|
259
|
+
|
|
260
|
+
def plot_logic_analyzer(
|
|
261
|
+
traces: Sequence[DigitalTrace],
|
|
262
|
+
*,
|
|
263
|
+
names: list[str] | None = None,
|
|
264
|
+
bus_groups: dict[str, list[int]] | None = None,
|
|
265
|
+
time_unit: str = "auto",
|
|
266
|
+
show_grid: bool = True,
|
|
267
|
+
figsize: tuple[float, float] | None = None,
|
|
268
|
+
title: str | None = None,
|
|
269
|
+
) -> Figure:
|
|
270
|
+
"""Plot logic analyzer style multi-channel display with bus grouping.
|
|
271
|
+
|
|
272
|
+
Creates a timing diagram optimized for logic analyzer visualization
|
|
273
|
+
with support for bus grouping (showing multi-bit buses as hex values).
|
|
274
|
+
|
|
275
|
+
Args:
|
|
276
|
+
traces: List of digital traces.
|
|
277
|
+
names: Channel names.
|
|
278
|
+
bus_groups: Dictionary mapping bus names to channel indices.
|
|
279
|
+
Example: {"DATA": [0, 1, 2, 3], "ADDR": [4, 5, 6, 7]}
|
|
280
|
+
time_unit: Time unit for display.
|
|
281
|
+
show_grid: Show vertical grid lines.
|
|
282
|
+
figsize: Figure size.
|
|
283
|
+
title: Plot title.
|
|
284
|
+
|
|
285
|
+
Returns:
|
|
286
|
+
Matplotlib Figure object.
|
|
287
|
+
|
|
288
|
+
Raises:
|
|
289
|
+
ImportError: If matplotlib is not available.
|
|
290
|
+
ValueError: If traces list is empty.
|
|
291
|
+
|
|
292
|
+
Example:
|
|
293
|
+
>>> fig = plot_logic_analyzer(
|
|
294
|
+
... traces,
|
|
295
|
+
... names=[f"D{i}" for i in range(8)],
|
|
296
|
+
... bus_groups={"DATA": [0, 1, 2, 3, 4, 5, 6, 7]}
|
|
297
|
+
... )
|
|
298
|
+
|
|
299
|
+
References:
|
|
300
|
+
Logic analyzer display conventions
|
|
301
|
+
"""
|
|
302
|
+
if not HAS_MATPLOTLIB:
|
|
303
|
+
raise ImportError("matplotlib is required for visualization")
|
|
304
|
+
|
|
305
|
+
if len(traces) == 0:
|
|
306
|
+
raise ValueError("traces list cannot be empty")
|
|
307
|
+
|
|
308
|
+
# Convert to list for plot_timing
|
|
309
|
+
traces_list: list[WaveformTrace | DigitalTrace] = list(traces)
|
|
310
|
+
|
|
311
|
+
# If no bus groups, just use regular timing diagram
|
|
312
|
+
if bus_groups is None:
|
|
313
|
+
return plot_timing(
|
|
314
|
+
traces_list,
|
|
315
|
+
names=names,
|
|
316
|
+
time_unit=time_unit,
|
|
317
|
+
show_grid=show_grid,
|
|
318
|
+
figsize=figsize,
|
|
319
|
+
title=title,
|
|
320
|
+
)
|
|
321
|
+
|
|
322
|
+
# Implementation for bus grouping would go here
|
|
323
|
+
# For MVP, delegate to plot_timing
|
|
324
|
+
return plot_timing(
|
|
325
|
+
traces_list,
|
|
326
|
+
names=names,
|
|
327
|
+
time_unit=time_unit,
|
|
328
|
+
show_grid=show_grid,
|
|
329
|
+
figsize=figsize,
|
|
330
|
+
title=title,
|
|
331
|
+
)
|
|
332
|
+
|
|
333
|
+
|
|
334
|
+
__all__ = [
|
|
335
|
+
"plot_logic_analyzer",
|
|
336
|
+
"plot_timing",
|
|
337
|
+
]
|