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,417 @@
|
|
|
1
|
+
"""Protocol type auto-detection from signal characteristics.
|
|
2
|
+
|
|
3
|
+
This module analyzes edge timing, symbol rate, and idle levels to
|
|
4
|
+
automatically detect serial protocol types.
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
Example:
|
|
8
|
+
>>> import oscura as tk
|
|
9
|
+
>>> trace = tk.load('serial_data.wfm')
|
|
10
|
+
>>> result = tk.detect_protocol(trace)
|
|
11
|
+
>>> print(f"Protocol: {result['protocol']}")
|
|
12
|
+
>>> print(f"Confidence: {result['confidence']:.1%}")
|
|
13
|
+
|
|
14
|
+
References:
|
|
15
|
+
UART: TIA-232-F
|
|
16
|
+
I2C: NXP UM10204
|
|
17
|
+
SPI: Motorola SPI Block Guide
|
|
18
|
+
CAN: ISO 11898
|
|
19
|
+
"""
|
|
20
|
+
|
|
21
|
+
from __future__ import annotations
|
|
22
|
+
|
|
23
|
+
from collections.abc import Callable
|
|
24
|
+
from concurrent.futures import ThreadPoolExecutor, as_completed
|
|
25
|
+
from typing import TYPE_CHECKING, Any
|
|
26
|
+
|
|
27
|
+
import numpy as np
|
|
28
|
+
|
|
29
|
+
from oscura.core.exceptions import AnalysisError
|
|
30
|
+
|
|
31
|
+
if TYPE_CHECKING:
|
|
32
|
+
from oscura.core.types import WaveformTrace
|
|
33
|
+
|
|
34
|
+
|
|
35
|
+
def detect_protocol(
|
|
36
|
+
trace: WaveformTrace,
|
|
37
|
+
*,
|
|
38
|
+
min_confidence: float = 0.6,
|
|
39
|
+
return_candidates: bool = False,
|
|
40
|
+
parallel: bool = True,
|
|
41
|
+
) -> dict[str, Any]:
|
|
42
|
+
"""Auto-detect serial protocol type.
|
|
43
|
+
|
|
44
|
+
Analyzes signal characteristics to identify protocol:
|
|
45
|
+
- Edge timing and regularity
|
|
46
|
+
- Symbol rate detection
|
|
47
|
+
- Idle level analysis
|
|
48
|
+
- Transition patterns
|
|
49
|
+
|
|
50
|
+
Detects: UART, SPI, I2C, CAN, 1-Wire, Manchester
|
|
51
|
+
|
|
52
|
+
Args:
|
|
53
|
+
trace: Signal to analyze.
|
|
54
|
+
min_confidence: Minimum confidence threshold (0-1).
|
|
55
|
+
return_candidates: If True, return all candidate protocols.
|
|
56
|
+
parallel: If True, run protocol scoring in parallel (default True).
|
|
57
|
+
|
|
58
|
+
Returns:
|
|
59
|
+
Dictionary containing:
|
|
60
|
+
- protocol: Detected protocol name
|
|
61
|
+
- confidence: Detection confidence (0-1)
|
|
62
|
+
- config: Suggested decoder configuration dict
|
|
63
|
+
- characteristics: Dict with detected signal characteristics
|
|
64
|
+
- candidates: List of all candidates (if return_candidates=True)
|
|
65
|
+
|
|
66
|
+
Raises:
|
|
67
|
+
AnalysisError: If no protocol can be detected with sufficient confidence.
|
|
68
|
+
|
|
69
|
+
Example:
|
|
70
|
+
>>> trace = tk.load('unknown_serial.wfm')
|
|
71
|
+
>>> result = tk.detect_protocol(trace, return_candidates=True)
|
|
72
|
+
>>> print(f"Detected: {result['protocol']}")
|
|
73
|
+
>>> print(f"Baud rate: {result['config'].get('baud_rate', 'N/A')}")
|
|
74
|
+
>>> for candidate in result['candidates']:
|
|
75
|
+
... print(f" {candidate['protocol']}: {candidate['confidence']:.1%}")
|
|
76
|
+
|
|
77
|
+
References:
|
|
78
|
+
sigrok Protocol Decoder heuristics
|
|
79
|
+
UART: Asynchronous, idle high, start bit low
|
|
80
|
+
I2C: Clock + data, open-drain, pull-up
|
|
81
|
+
"""
|
|
82
|
+
# Analyze signal characteristics
|
|
83
|
+
characteristics = _analyze_signal_characteristics(trace)
|
|
84
|
+
|
|
85
|
+
# Define protocol detectors
|
|
86
|
+
protocol_detectors: list[tuple[str, Callable[[dict[str, Any]], float], dict[str, Any]]] = [
|
|
87
|
+
(
|
|
88
|
+
"UART",
|
|
89
|
+
_score_uart,
|
|
90
|
+
{
|
|
91
|
+
"baud_rate": characteristics.get("symbol_rate", 115200),
|
|
92
|
+
"data_bits": 8,
|
|
93
|
+
"parity": "none",
|
|
94
|
+
"stop_bits": 1,
|
|
95
|
+
},
|
|
96
|
+
),
|
|
97
|
+
(
|
|
98
|
+
"SPI",
|
|
99
|
+
_score_spi,
|
|
100
|
+
{
|
|
101
|
+
"clock_polarity": 0,
|
|
102
|
+
"clock_phase": 0,
|
|
103
|
+
"bit_order": "MSB",
|
|
104
|
+
},
|
|
105
|
+
),
|
|
106
|
+
(
|
|
107
|
+
"I2C",
|
|
108
|
+
_score_i2c,
|
|
109
|
+
{
|
|
110
|
+
"clock_rate": characteristics.get("symbol_rate", 100000),
|
|
111
|
+
"address_bits": 7,
|
|
112
|
+
},
|
|
113
|
+
),
|
|
114
|
+
(
|
|
115
|
+
"CAN",
|
|
116
|
+
_score_can,
|
|
117
|
+
{
|
|
118
|
+
"baud_rate": characteristics.get("symbol_rate", 500000),
|
|
119
|
+
"sample_point": 0.75,
|
|
120
|
+
},
|
|
121
|
+
),
|
|
122
|
+
]
|
|
123
|
+
|
|
124
|
+
# Score protocols (optionally in parallel)
|
|
125
|
+
candidates = []
|
|
126
|
+
|
|
127
|
+
if parallel:
|
|
128
|
+
# Run scoring in parallel using ThreadPoolExecutor
|
|
129
|
+
with ThreadPoolExecutor(max_workers=len(protocol_detectors)) as executor:
|
|
130
|
+
# Submit all scoring tasks
|
|
131
|
+
future_to_protocol = {
|
|
132
|
+
executor.submit(scorer, characteristics): (name, config)
|
|
133
|
+
for name, scorer, config in protocol_detectors
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
# Collect results
|
|
137
|
+
for future in as_completed(future_to_protocol):
|
|
138
|
+
name, config = future_to_protocol[future]
|
|
139
|
+
try:
|
|
140
|
+
score = future.result()
|
|
141
|
+
if score > 0:
|
|
142
|
+
candidates.append(
|
|
143
|
+
{
|
|
144
|
+
"protocol": name,
|
|
145
|
+
"confidence": score,
|
|
146
|
+
"config": config,
|
|
147
|
+
}
|
|
148
|
+
)
|
|
149
|
+
except Exception:
|
|
150
|
+
# If a scorer fails, skip that protocol
|
|
151
|
+
pass
|
|
152
|
+
else:
|
|
153
|
+
# Sequential scoring (original behavior)
|
|
154
|
+
for name, scorer, config in protocol_detectors:
|
|
155
|
+
score = scorer(characteristics)
|
|
156
|
+
if score > 0:
|
|
157
|
+
candidates.append(
|
|
158
|
+
{
|
|
159
|
+
"protocol": name,
|
|
160
|
+
"confidence": score,
|
|
161
|
+
"config": config,
|
|
162
|
+
}
|
|
163
|
+
)
|
|
164
|
+
|
|
165
|
+
# Sort by confidence
|
|
166
|
+
candidates.sort(key=lambda x: x["confidence"], reverse=True) # type: ignore[arg-type, return-value]
|
|
167
|
+
|
|
168
|
+
if not candidates:
|
|
169
|
+
raise AnalysisError(
|
|
170
|
+
"Could not detect protocol type. Signal may be analog or unsupported protocol."
|
|
171
|
+
)
|
|
172
|
+
|
|
173
|
+
# Primary detection
|
|
174
|
+
primary = candidates[0]
|
|
175
|
+
|
|
176
|
+
if primary["confidence"] < min_confidence: # type: ignore[operator]
|
|
177
|
+
raise AnalysisError(
|
|
178
|
+
f"Protocol detection confidence too low: {primary['confidence']:.1%} "
|
|
179
|
+
f"(minimum: {min_confidence:.1%}). Try specifying protocol manually."
|
|
180
|
+
)
|
|
181
|
+
|
|
182
|
+
# Build result
|
|
183
|
+
result = {
|
|
184
|
+
"protocol": primary["protocol"],
|
|
185
|
+
"confidence": primary["confidence"],
|
|
186
|
+
"config": primary["config"],
|
|
187
|
+
"characteristics": characteristics,
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
if return_candidates:
|
|
191
|
+
result["candidates"] = candidates
|
|
192
|
+
|
|
193
|
+
return result
|
|
194
|
+
|
|
195
|
+
|
|
196
|
+
def _analyze_signal_characteristics(trace: WaveformTrace) -> dict[str, Any]:
|
|
197
|
+
"""Analyze signal to extract protocol-relevant characteristics.
|
|
198
|
+
|
|
199
|
+
Args:
|
|
200
|
+
trace: Signal to analyze.
|
|
201
|
+
|
|
202
|
+
Returns:
|
|
203
|
+
Dictionary with characteristics.
|
|
204
|
+
"""
|
|
205
|
+
data = trace.data
|
|
206
|
+
sample_rate = trace.metadata.sample_rate
|
|
207
|
+
|
|
208
|
+
# Handle empty data
|
|
209
|
+
if len(data) == 0:
|
|
210
|
+
return {
|
|
211
|
+
"regularity": 0,
|
|
212
|
+
"symbol_rate": 0,
|
|
213
|
+
"idle_level": "low",
|
|
214
|
+
"duty_cycle": 0,
|
|
215
|
+
"transition_density": 0,
|
|
216
|
+
"edge_count": 0,
|
|
217
|
+
}
|
|
218
|
+
|
|
219
|
+
# Detect edges
|
|
220
|
+
threshold = (np.max(data) + np.min(data)) / 2
|
|
221
|
+
digital = data > threshold
|
|
222
|
+
edges = np.diff(digital.astype(int))
|
|
223
|
+
edge_indices = np.where(edges != 0)[0]
|
|
224
|
+
|
|
225
|
+
# Edge statistics
|
|
226
|
+
if len(edge_indices) > 1:
|
|
227
|
+
edge_times = edge_indices / sample_rate
|
|
228
|
+
edge_intervals = np.diff(edge_times)
|
|
229
|
+
|
|
230
|
+
# Detect if edges are regular (clock-like) or irregular
|
|
231
|
+
if len(edge_intervals) > 10:
|
|
232
|
+
interval_std = np.std(edge_intervals)
|
|
233
|
+
interval_mean = np.mean(edge_intervals)
|
|
234
|
+
regularity = 1.0 - min(1.0, interval_std / (interval_mean + 1e-12))
|
|
235
|
+
else:
|
|
236
|
+
regularity = 0.5
|
|
237
|
+
|
|
238
|
+
# Estimate symbol rate from edge intervals
|
|
239
|
+
# For clock-based: symbol_rate = 1 / (2 * edge_interval)
|
|
240
|
+
# For async: symbol_rate = 1 / min_interval
|
|
241
|
+
median_interval = np.median(edge_intervals)
|
|
242
|
+
symbol_rate = 1.0 / median_interval if median_interval > 0 else 0
|
|
243
|
+
elif len(edge_indices) == 1:
|
|
244
|
+
# With exactly 1 edge, regularity is indeterminate
|
|
245
|
+
regularity = 0.5
|
|
246
|
+
symbol_rate = 0
|
|
247
|
+
else:
|
|
248
|
+
# No edges: completely DC signal, no regularity
|
|
249
|
+
regularity = 0
|
|
250
|
+
symbol_rate = 0
|
|
251
|
+
|
|
252
|
+
# Idle level (high or low)
|
|
253
|
+
idle_level = "high" if np.mean(data) > threshold else "low"
|
|
254
|
+
|
|
255
|
+
# Duty cycle
|
|
256
|
+
duty_cycle = np.sum(digital) / len(digital)
|
|
257
|
+
|
|
258
|
+
# Transition density (edges per second)
|
|
259
|
+
duration = len(data) / sample_rate
|
|
260
|
+
transition_density = len(edge_indices) / duration if duration > 0 else 0
|
|
261
|
+
|
|
262
|
+
return {
|
|
263
|
+
"regularity": regularity,
|
|
264
|
+
"symbol_rate": symbol_rate,
|
|
265
|
+
"idle_level": idle_level,
|
|
266
|
+
"duty_cycle": duty_cycle,
|
|
267
|
+
"transition_density": transition_density,
|
|
268
|
+
"edge_count": len(edge_indices),
|
|
269
|
+
}
|
|
270
|
+
|
|
271
|
+
|
|
272
|
+
def _score_uart(characteristics: dict[str, Any]) -> float:
|
|
273
|
+
"""Score likelihood of UART protocol.
|
|
274
|
+
|
|
275
|
+
UART characteristics:
|
|
276
|
+
- Irregular edges (async)
|
|
277
|
+
- Idle high
|
|
278
|
+
- Low transition density
|
|
279
|
+
|
|
280
|
+
Args:
|
|
281
|
+
characteristics: Signal characteristics.
|
|
282
|
+
|
|
283
|
+
Returns:
|
|
284
|
+
Score from 0 to 1.
|
|
285
|
+
"""
|
|
286
|
+
score = 0.0
|
|
287
|
+
|
|
288
|
+
# UART is asynchronous - low regularity
|
|
289
|
+
regularity = characteristics["regularity"]
|
|
290
|
+
if regularity < 0.3:
|
|
291
|
+
score += 0.4
|
|
292
|
+
elif regularity < 0.5:
|
|
293
|
+
score += 0.2
|
|
294
|
+
|
|
295
|
+
# UART idles high
|
|
296
|
+
if characteristics["idle_level"] == "high":
|
|
297
|
+
score += 0.3
|
|
298
|
+
|
|
299
|
+
# UART has moderate transition density
|
|
300
|
+
density = characteristics["transition_density"]
|
|
301
|
+
if 1000 < density < 1e6: # Typical UART range
|
|
302
|
+
score += 0.3
|
|
303
|
+
|
|
304
|
+
# Cap at 0.99 to reflect inherent uncertainty
|
|
305
|
+
return min(0.99, score)
|
|
306
|
+
|
|
307
|
+
|
|
308
|
+
def _score_spi(characteristics: dict[str, Any]) -> float:
|
|
309
|
+
"""Score likelihood of SPI protocol.
|
|
310
|
+
|
|
311
|
+
SPI characteristics:
|
|
312
|
+
- Regular clock edges
|
|
313
|
+
- ~50% duty cycle
|
|
314
|
+
- High transition density
|
|
315
|
+
|
|
316
|
+
Args:
|
|
317
|
+
characteristics: Signal characteristics.
|
|
318
|
+
|
|
319
|
+
Returns:
|
|
320
|
+
Score from 0 to 1.
|
|
321
|
+
"""
|
|
322
|
+
score = 0.0
|
|
323
|
+
|
|
324
|
+
# SPI has regular clock - high regularity
|
|
325
|
+
regularity = characteristics["regularity"]
|
|
326
|
+
if regularity > 0.8:
|
|
327
|
+
score += 0.5
|
|
328
|
+
elif regularity > 0.6:
|
|
329
|
+
score += 0.3
|
|
330
|
+
|
|
331
|
+
# SPI clock typically ~50% duty cycle
|
|
332
|
+
duty_cycle = characteristics["duty_cycle"]
|
|
333
|
+
duty_error = abs(duty_cycle - 0.5)
|
|
334
|
+
if duty_error < 0.1:
|
|
335
|
+
score += 0.3
|
|
336
|
+
|
|
337
|
+
# SPI has high transition density
|
|
338
|
+
density = characteristics["transition_density"]
|
|
339
|
+
if density > 1e5: # High speed
|
|
340
|
+
score += 0.2
|
|
341
|
+
|
|
342
|
+
# Cap at 0.99 to reflect inherent uncertainty
|
|
343
|
+
return min(0.99, score)
|
|
344
|
+
|
|
345
|
+
|
|
346
|
+
def _score_i2c(characteristics: dict[str, Any]) -> float:
|
|
347
|
+
"""Score likelihood of I2C protocol.
|
|
348
|
+
|
|
349
|
+
I2C characteristics:
|
|
350
|
+
- Clock-like regularity
|
|
351
|
+
- Idle high (pull-up)
|
|
352
|
+
- Moderate transition density
|
|
353
|
+
|
|
354
|
+
Args:
|
|
355
|
+
characteristics: Signal characteristics.
|
|
356
|
+
|
|
357
|
+
Returns:
|
|
358
|
+
Score from 0 to 1.
|
|
359
|
+
"""
|
|
360
|
+
score = 0.0
|
|
361
|
+
|
|
362
|
+
# I2C clock has regularity
|
|
363
|
+
regularity = characteristics["regularity"]
|
|
364
|
+
if regularity > 0.6:
|
|
365
|
+
score += 0.4
|
|
366
|
+
|
|
367
|
+
# I2C idles high
|
|
368
|
+
if characteristics["idle_level"] == "high":
|
|
369
|
+
score += 0.3
|
|
370
|
+
|
|
371
|
+
# I2C has lower transition density than SPI
|
|
372
|
+
density = characteristics["transition_density"]
|
|
373
|
+
if 1e3 < density < 1e6:
|
|
374
|
+
score += 0.3
|
|
375
|
+
|
|
376
|
+
# Cap at 0.99 to reflect inherent uncertainty
|
|
377
|
+
return min(0.99, score)
|
|
378
|
+
|
|
379
|
+
|
|
380
|
+
def _score_can(characteristics: dict[str, Any]) -> float:
|
|
381
|
+
"""Score likelihood of CAN protocol.
|
|
382
|
+
|
|
383
|
+
CAN characteristics:
|
|
384
|
+
- Irregular edges (NRZ encoding with bit stuffing)
|
|
385
|
+
- Idle high (recessive)
|
|
386
|
+
- Moderate to high transition density
|
|
387
|
+
|
|
388
|
+
Args:
|
|
389
|
+
characteristics: Signal characteristics.
|
|
390
|
+
|
|
391
|
+
Returns:
|
|
392
|
+
Score from 0 to 1.
|
|
393
|
+
"""
|
|
394
|
+
score = 0.0
|
|
395
|
+
|
|
396
|
+
# CAN has some irregularity due to bit stuffing
|
|
397
|
+
regularity = characteristics["regularity"]
|
|
398
|
+
if 0.3 < regularity < 0.7:
|
|
399
|
+
score += 0.4
|
|
400
|
+
|
|
401
|
+
# CAN idles high (recessive state)
|
|
402
|
+
if characteristics["idle_level"] == "high":
|
|
403
|
+
score += 0.3
|
|
404
|
+
|
|
405
|
+
# CAN has specific baud rates (typically 125k, 250k, 500k, 1M)
|
|
406
|
+
symbol_rate = characteristics["symbol_rate"]
|
|
407
|
+
common_rates = [125000, 250000, 500000, 1000000]
|
|
408
|
+
for rate in common_rates:
|
|
409
|
+
if abs(symbol_rate - rate) / rate < 0.1: # Within 10%
|
|
410
|
+
score += 0.3
|
|
411
|
+
break
|
|
412
|
+
|
|
413
|
+
# Cap at 0.99 to reflect inherent uncertainty
|
|
414
|
+
return min(0.99, score)
|
|
415
|
+
|
|
416
|
+
|
|
417
|
+
__all__ = ["detect_protocol"]
|