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
oscura/convenience.py
ADDED
|
@@ -0,0 +1,457 @@
|
|
|
1
|
+
"""High-level convenience functions for common analysis tasks.
|
|
2
|
+
|
|
3
|
+
This module provides one-call solutions for common signal analysis tasks,
|
|
4
|
+
wrapping multiple lower-level functions into easy-to-use high-level APIs.
|
|
5
|
+
|
|
6
|
+
Example:
|
|
7
|
+
>>> import oscura as tk
|
|
8
|
+
>>> trace = tk.load("audio_capture.wfm")
|
|
9
|
+
>>>
|
|
10
|
+
>>> # One-call spectral analysis
|
|
11
|
+
>>> metrics = tk.quick_spectral(trace, fundamental=1000)
|
|
12
|
+
>>> print(f"THD: {metrics.thd_db:.1f} dB, SNR: {metrics.snr_db:.1f} dB")
|
|
13
|
+
>>>
|
|
14
|
+
>>> # Auto-detect and decode protocol
|
|
15
|
+
>>> result = tk.auto_decode(trace)
|
|
16
|
+
>>> print(f"Protocol: {result.protocol}, Frames: {len(result.frames)}")
|
|
17
|
+
>>>
|
|
18
|
+
>>> # Smart filtering
|
|
19
|
+
>>> clean = tk.smart_filter(trace, target="noise")
|
|
20
|
+
|
|
21
|
+
References:
|
|
22
|
+
- TraceKit API Design Guidelines
|
|
23
|
+
- IEEE 1241-2010 (ADC Characterization)
|
|
24
|
+
"""
|
|
25
|
+
|
|
26
|
+
from __future__ import annotations
|
|
27
|
+
|
|
28
|
+
from dataclasses import dataclass
|
|
29
|
+
from typing import TYPE_CHECKING, Any, Literal
|
|
30
|
+
|
|
31
|
+
import numpy as np
|
|
32
|
+
|
|
33
|
+
if TYPE_CHECKING:
|
|
34
|
+
from oscura.core.types import DigitalTrace, WaveformTrace
|
|
35
|
+
|
|
36
|
+
|
|
37
|
+
@dataclass
|
|
38
|
+
class SpectralMetrics:
|
|
39
|
+
"""Results from quick_spectral analysis.
|
|
40
|
+
|
|
41
|
+
Attributes:
|
|
42
|
+
thd_db: Total Harmonic Distortion in dB.
|
|
43
|
+
thd_percent: Total Harmonic Distortion as percentage.
|
|
44
|
+
snr_db: Signal-to-Noise Ratio in dB.
|
|
45
|
+
sinad_db: Signal-to-Noise and Distortion in dB.
|
|
46
|
+
enob: Effective Number of Bits.
|
|
47
|
+
sfdr_db: Spurious-Free Dynamic Range in dBc.
|
|
48
|
+
fundamental_freq: Detected fundamental frequency in Hz.
|
|
49
|
+
fundamental_mag_db: Fundamental magnitude in dB.
|
|
50
|
+
noise_floor_db: Estimated noise floor in dB.
|
|
51
|
+
"""
|
|
52
|
+
|
|
53
|
+
thd_db: float
|
|
54
|
+
thd_percent: float
|
|
55
|
+
snr_db: float
|
|
56
|
+
sinad_db: float
|
|
57
|
+
enob: float
|
|
58
|
+
sfdr_db: float
|
|
59
|
+
fundamental_freq: float
|
|
60
|
+
fundamental_mag_db: float
|
|
61
|
+
noise_floor_db: float
|
|
62
|
+
|
|
63
|
+
|
|
64
|
+
@dataclass
|
|
65
|
+
class DecodeResult:
|
|
66
|
+
"""Results from auto_decode analysis.
|
|
67
|
+
|
|
68
|
+
Attributes:
|
|
69
|
+
protocol: Detected protocol name.
|
|
70
|
+
frames: List of decoded protocol packets.
|
|
71
|
+
confidence: Detection confidence (0-1).
|
|
72
|
+
baud_rate: Detected baud/bit rate (if applicable).
|
|
73
|
+
config: Protocol configuration parameters.
|
|
74
|
+
errors: List of decoding errors.
|
|
75
|
+
statistics: Decoding statistics.
|
|
76
|
+
"""
|
|
77
|
+
|
|
78
|
+
protocol: str
|
|
79
|
+
frames: list[Any]
|
|
80
|
+
confidence: float
|
|
81
|
+
baud_rate: float | None
|
|
82
|
+
config: dict[str, Any]
|
|
83
|
+
errors: list[str]
|
|
84
|
+
statistics: dict[str, Any]
|
|
85
|
+
|
|
86
|
+
|
|
87
|
+
def quick_spectral(
|
|
88
|
+
trace: WaveformTrace,
|
|
89
|
+
fundamental: float | None = None,
|
|
90
|
+
n_harmonics: int = 10,
|
|
91
|
+
window: str = "hann",
|
|
92
|
+
) -> SpectralMetrics:
|
|
93
|
+
"""One-call spectral analysis with all common metrics.
|
|
94
|
+
|
|
95
|
+
Computes THD, SNR, SINAD, ENOB, and SFDR in a single call with
|
|
96
|
+
optimized caching for efficiency.
|
|
97
|
+
|
|
98
|
+
Args:
|
|
99
|
+
trace: Input waveform trace.
|
|
100
|
+
fundamental: Expected fundamental frequency in Hz.
|
|
101
|
+
If None, auto-detected from FFT peak.
|
|
102
|
+
n_harmonics: Number of harmonics to consider for THD (default 10).
|
|
103
|
+
window: Window function for FFT (default "hann").
|
|
104
|
+
|
|
105
|
+
Returns:
|
|
106
|
+
SpectralMetrics with all computed values.
|
|
107
|
+
|
|
108
|
+
Example:
|
|
109
|
+
>>> trace = tk.load("audio_1khz.wfm")
|
|
110
|
+
>>> metrics = tk.quick_spectral(trace, fundamental=1000)
|
|
111
|
+
>>> print(f"THD: {metrics.thd_db:.1f} dB")
|
|
112
|
+
>>> print(f"SNR: {metrics.snr_db:.1f} dB")
|
|
113
|
+
>>> print(f"ENOB: {metrics.enob:.1f} bits")
|
|
114
|
+
|
|
115
|
+
References:
|
|
116
|
+
IEEE 1241-2010 Section 4.1 (ADC Characterization)
|
|
117
|
+
"""
|
|
118
|
+
from oscura.analyzers.waveform.spectral import (
|
|
119
|
+
enob,
|
|
120
|
+
fft,
|
|
121
|
+
sfdr,
|
|
122
|
+
sinad,
|
|
123
|
+
snr,
|
|
124
|
+
thd,
|
|
125
|
+
)
|
|
126
|
+
|
|
127
|
+
# Compute FFT once
|
|
128
|
+
fft_result = fft(trace, window=window)
|
|
129
|
+
freq = fft_result[0]
|
|
130
|
+
mag_db = fft_result[1]
|
|
131
|
+
|
|
132
|
+
# Find fundamental if not specified
|
|
133
|
+
if fundamental is None:
|
|
134
|
+
# Skip DC bins, find peak
|
|
135
|
+
dc_bins = max(5, len(freq) // 1000)
|
|
136
|
+
peak_idx = dc_bins + int(np.argmax(mag_db[dc_bins : len(mag_db) // 2]))
|
|
137
|
+
fundamental = float(freq[peak_idx])
|
|
138
|
+
fundamental_mag = float(mag_db[peak_idx])
|
|
139
|
+
else:
|
|
140
|
+
# Find closest bin to specified fundamental
|
|
141
|
+
peak_idx = int(np.argmin(np.abs(freq - fundamental)))
|
|
142
|
+
fundamental_mag = float(mag_db[peak_idx])
|
|
143
|
+
|
|
144
|
+
# Estimate noise floor (median of lower half of spectrum, excluding DC and peaks)
|
|
145
|
+
noise_bins = mag_db[10 : len(mag_db) // 2]
|
|
146
|
+
noise_floor = float(np.median(noise_bins))
|
|
147
|
+
|
|
148
|
+
# Compute metrics (these functions don't accept 'fundamental' parameter)
|
|
149
|
+
thd_db_val = thd(trace, n_harmonics=n_harmonics, window=window)
|
|
150
|
+
thd_pct = 100 * 10 ** (thd_db_val / 20) if not np.isnan(thd_db_val) else np.nan
|
|
151
|
+
snr_db_val = snr(trace, n_harmonics=n_harmonics, window=window)
|
|
152
|
+
sinad_db_val = sinad(trace, window=window)
|
|
153
|
+
enob_val = enob(trace, window=window)
|
|
154
|
+
sfdr_db_val = sfdr(trace, window=window)
|
|
155
|
+
|
|
156
|
+
return SpectralMetrics(
|
|
157
|
+
thd_db=thd_db_val,
|
|
158
|
+
thd_percent=thd_pct,
|
|
159
|
+
snr_db=snr_db_val,
|
|
160
|
+
sinad_db=sinad_db_val,
|
|
161
|
+
enob=enob_val,
|
|
162
|
+
sfdr_db=sfdr_db_val,
|
|
163
|
+
fundamental_freq=fundamental,
|
|
164
|
+
fundamental_mag_db=fundamental_mag,
|
|
165
|
+
noise_floor_db=noise_floor,
|
|
166
|
+
)
|
|
167
|
+
|
|
168
|
+
|
|
169
|
+
def auto_decode(
|
|
170
|
+
trace: WaveformTrace | DigitalTrace,
|
|
171
|
+
protocol: str | None = None,
|
|
172
|
+
min_confidence: float = 0.5,
|
|
173
|
+
) -> DecodeResult:
|
|
174
|
+
"""Auto-detect protocol and decode frames in one call.
|
|
175
|
+
|
|
176
|
+
Automatically detects the protocol type (UART, SPI, I2C, CAN, etc.)
|
|
177
|
+
if not specified, then decodes all frames.
|
|
178
|
+
|
|
179
|
+
Args:
|
|
180
|
+
trace: Input trace (waveform or digital).
|
|
181
|
+
protocol: Force specific protocol (None for auto-detect).
|
|
182
|
+
min_confidence: Minimum confidence for auto-detection (0-1).
|
|
183
|
+
|
|
184
|
+
Returns:
|
|
185
|
+
DecodeResult with protocol name, decoded frames, and statistics.
|
|
186
|
+
|
|
187
|
+
Example:
|
|
188
|
+
>>> trace = tk.load("serial_capture.wfm")
|
|
189
|
+
>>> result = tk.auto_decode(trace)
|
|
190
|
+
>>> print(f"Protocol: {result.protocol}")
|
|
191
|
+
>>> print(f"Frames decoded: {len(result.frames)}")
|
|
192
|
+
>>> for frame in result.frames[:5]:
|
|
193
|
+
... print(f" {frame.data.hex()}")
|
|
194
|
+
|
|
195
|
+
References:
|
|
196
|
+
sigrok Protocol Decoder API
|
|
197
|
+
"""
|
|
198
|
+
from oscura.core.types import WaveformTrace
|
|
199
|
+
from oscura.inference.protocol import detect_protocol
|
|
200
|
+
|
|
201
|
+
# Convert to digital if needed
|
|
202
|
+
if isinstance(trace, WaveformTrace):
|
|
203
|
+
from oscura.analyzers.digital.extraction import to_digital
|
|
204
|
+
|
|
205
|
+
digital_trace = to_digital(trace, threshold="auto")
|
|
206
|
+
else:
|
|
207
|
+
digital_trace = trace
|
|
208
|
+
|
|
209
|
+
# Auto-detect protocol if not specified
|
|
210
|
+
if protocol is None or protocol.lower() == "auto":
|
|
211
|
+
# detect_protocol only accepts WaveformTrace, use original trace if it's waveform
|
|
212
|
+
if isinstance(trace, WaveformTrace):
|
|
213
|
+
detection = detect_protocol(
|
|
214
|
+
trace, min_confidence=min_confidence, return_candidates=True
|
|
215
|
+
)
|
|
216
|
+
else:
|
|
217
|
+
# For DigitalTrace, we can't auto-detect protocol, use default UART
|
|
218
|
+
detection = {"protocol": "UART", "config": {}, "confidence": 0.5}
|
|
219
|
+
protocol = detection.get("protocol", "unknown")
|
|
220
|
+
config = detection.get("config", {})
|
|
221
|
+
confidence = detection.get("confidence", 0.0)
|
|
222
|
+
else:
|
|
223
|
+
protocol = protocol.upper()
|
|
224
|
+
confidence = 1.0
|
|
225
|
+
config = _get_default_protocol_config(protocol)
|
|
226
|
+
|
|
227
|
+
# Decode based on protocol
|
|
228
|
+
frames: list[Any] = []
|
|
229
|
+
errors: list[str] = []
|
|
230
|
+
|
|
231
|
+
try:
|
|
232
|
+
if protocol == "UART":
|
|
233
|
+
from oscura.analyzers.protocols.uart import UARTDecoder
|
|
234
|
+
|
|
235
|
+
baud_rate = config.get("baud_rate", 115200)
|
|
236
|
+
decoder = UARTDecoder(
|
|
237
|
+
baudrate=baud_rate,
|
|
238
|
+
data_bits=config.get("data_bits", 8),
|
|
239
|
+
parity=config.get("parity", "none"),
|
|
240
|
+
stop_bits=config.get("stop_bits", 1),
|
|
241
|
+
)
|
|
242
|
+
frames = list(decoder.decode(digital_trace))
|
|
243
|
+
|
|
244
|
+
elif protocol == "SPI":
|
|
245
|
+
from oscura.analyzers.protocols.spi import SPIDecoder
|
|
246
|
+
|
|
247
|
+
spi_decoder = SPIDecoder(
|
|
248
|
+
cpol=config.get("clock_polarity", 0),
|
|
249
|
+
cpha=config.get("clock_phase", 0),
|
|
250
|
+
)
|
|
251
|
+
# Single channel decode - pass trace with channel mapping
|
|
252
|
+
frames = list(
|
|
253
|
+
spi_decoder.decode(digital_trace, clk=digital_trace.data, mosi=digital_trace.data)
|
|
254
|
+
)
|
|
255
|
+
|
|
256
|
+
elif protocol == "I2C":
|
|
257
|
+
from oscura.analyzers.protocols.i2c import I2CDecoder
|
|
258
|
+
|
|
259
|
+
i2c_decoder = I2CDecoder()
|
|
260
|
+
# Single channel - use SDA and create synthetic SCL
|
|
261
|
+
sda = digital_trace.data
|
|
262
|
+
edges = np.where(np.diff(sda.astype(int)) != 0)[0]
|
|
263
|
+
scl = np.ones_like(sda, dtype=bool)
|
|
264
|
+
for i, edge in enumerate(edges):
|
|
265
|
+
if i % 2 == 0 and edge + 10 < len(scl):
|
|
266
|
+
scl[edge : edge + 10] = False
|
|
267
|
+
frames = list(i2c_decoder.decode(digital_trace, scl=scl, sda=sda))
|
|
268
|
+
|
|
269
|
+
elif protocol == "CAN":
|
|
270
|
+
from oscura.analyzers.protocols.can import CANDecoder
|
|
271
|
+
|
|
272
|
+
can_decoder = CANDecoder(
|
|
273
|
+
bitrate=config.get("baud_rate", 500000),
|
|
274
|
+
sample_point=config.get("sample_point", 0.75),
|
|
275
|
+
)
|
|
276
|
+
frames = list(can_decoder.decode(digital_trace))
|
|
277
|
+
|
|
278
|
+
else:
|
|
279
|
+
errors.append(f"Unsupported protocol: {protocol}")
|
|
280
|
+
|
|
281
|
+
except Exception as e:
|
|
282
|
+
errors.append(f"Decoding error: {e!s}")
|
|
283
|
+
|
|
284
|
+
# Calculate statistics
|
|
285
|
+
error_frames = sum(1 for f in frames if hasattr(f, "errors") and f.errors)
|
|
286
|
+
statistics = {
|
|
287
|
+
"total_frames": len(frames),
|
|
288
|
+
"error_frames": error_frames,
|
|
289
|
+
"error_rate": error_frames / len(frames) if frames else 0,
|
|
290
|
+
}
|
|
291
|
+
|
|
292
|
+
return DecodeResult(
|
|
293
|
+
protocol=protocol,
|
|
294
|
+
frames=frames,
|
|
295
|
+
confidence=confidence,
|
|
296
|
+
baud_rate=config.get("baud_rate"),
|
|
297
|
+
config=config,
|
|
298
|
+
errors=errors,
|
|
299
|
+
statistics=statistics,
|
|
300
|
+
)
|
|
301
|
+
|
|
302
|
+
|
|
303
|
+
def smart_filter(
|
|
304
|
+
trace: WaveformTrace,
|
|
305
|
+
target: Literal["noise", "high_freq", "low_freq", "60hz_hum", "50hz_hum", "auto"] = "auto",
|
|
306
|
+
strength: float = 1.0,
|
|
307
|
+
) -> WaveformTrace:
|
|
308
|
+
"""Intelligently filter trace based on target.
|
|
309
|
+
|
|
310
|
+
Automatically selects appropriate filter type and parameters
|
|
311
|
+
based on the specified target or auto-detected noise characteristics.
|
|
312
|
+
|
|
313
|
+
Args:
|
|
314
|
+
trace: Input waveform trace.
|
|
315
|
+
target: What to filter:
|
|
316
|
+
- "noise": General noise reduction
|
|
317
|
+
- "high_freq": Remove high frequency components
|
|
318
|
+
- "low_freq": Remove DC and low frequency drift
|
|
319
|
+
- "60hz_hum": Remove 60 Hz power line interference
|
|
320
|
+
- "50hz_hum": Remove 50 Hz power line interference
|
|
321
|
+
- "auto": Auto-detect and filter dominant noise source
|
|
322
|
+
strength: Filter strength 0-1 (default 1.0 = full strength).
|
|
323
|
+
|
|
324
|
+
Returns:
|
|
325
|
+
Filtered WaveformTrace.
|
|
326
|
+
|
|
327
|
+
Example:
|
|
328
|
+
>>> noisy = tk.load("noisy_capture.wfm")
|
|
329
|
+
>>> clean = tk.smart_filter(noisy, target="noise")
|
|
330
|
+
>>> # Or auto-detect
|
|
331
|
+
>>> clean = tk.smart_filter(noisy, target="auto")
|
|
332
|
+
"""
|
|
333
|
+
from oscura.filtering.convenience import (
|
|
334
|
+
high_pass,
|
|
335
|
+
low_pass,
|
|
336
|
+
median_filter,
|
|
337
|
+
notch_filter,
|
|
338
|
+
)
|
|
339
|
+
|
|
340
|
+
sample_rate = trace.metadata.sample_rate
|
|
341
|
+
|
|
342
|
+
if target == "auto":
|
|
343
|
+
target = _detect_noise_type(trace)
|
|
344
|
+
|
|
345
|
+
if target == "noise":
|
|
346
|
+
# Use median filter for general noise reduction
|
|
347
|
+
kernel_size = int(3 + 4 * strength)
|
|
348
|
+
if kernel_size % 2 == 0:
|
|
349
|
+
kernel_size += 1
|
|
350
|
+
return median_filter(trace, kernel_size=kernel_size)
|
|
351
|
+
|
|
352
|
+
elif target == "high_freq":
|
|
353
|
+
# Low-pass filter
|
|
354
|
+
cutoff = sample_rate / 10 * (1 - 0.5 * strength)
|
|
355
|
+
return low_pass(trace, cutoff=cutoff)
|
|
356
|
+
|
|
357
|
+
elif target == "low_freq":
|
|
358
|
+
# High-pass filter
|
|
359
|
+
cutoff = 10 + 90 * strength # 10-100 Hz
|
|
360
|
+
return high_pass(trace, cutoff=cutoff)
|
|
361
|
+
|
|
362
|
+
elif target == "60hz_hum":
|
|
363
|
+
# 60 Hz notch filter with harmonics
|
|
364
|
+
result = trace
|
|
365
|
+
for harmonic in range(1, int(1 + 4 * strength)):
|
|
366
|
+
freq = 60 * harmonic
|
|
367
|
+
if freq < sample_rate / 2:
|
|
368
|
+
result = notch_filter(result, freq=freq, q_factor=30)
|
|
369
|
+
return result
|
|
370
|
+
|
|
371
|
+
elif target == "50hz_hum":
|
|
372
|
+
# 50 Hz notch filter with harmonics
|
|
373
|
+
result = trace
|
|
374
|
+
for harmonic in range(1, int(1 + 4 * strength)):
|
|
375
|
+
freq = 50 * harmonic
|
|
376
|
+
if freq < sample_rate / 2:
|
|
377
|
+
result = notch_filter(result, freq=freq, q_factor=30)
|
|
378
|
+
return result
|
|
379
|
+
|
|
380
|
+
else:
|
|
381
|
+
raise ValueError(f"Unknown filter target: {target}")
|
|
382
|
+
|
|
383
|
+
|
|
384
|
+
def _detect_noise_type(
|
|
385
|
+
trace: WaveformTrace,
|
|
386
|
+
) -> Literal["noise", "high_freq", "low_freq", "60hz_hum", "50hz_hum"]:
|
|
387
|
+
"""Auto-detect dominant noise type in trace.
|
|
388
|
+
|
|
389
|
+
Args:
|
|
390
|
+
trace: Input trace.
|
|
391
|
+
|
|
392
|
+
Returns:
|
|
393
|
+
Detected noise type string.
|
|
394
|
+
"""
|
|
395
|
+
from oscura.analyzers.waveform.spectral import fft
|
|
396
|
+
|
|
397
|
+
fft_result = fft(trace, window="hann")
|
|
398
|
+
freq = fft_result[0]
|
|
399
|
+
mag_db = fft_result[1]
|
|
400
|
+
|
|
401
|
+
# Check for 50/60 Hz peaks
|
|
402
|
+
idx_50 = int(np.argmin(np.abs(freq - 50)))
|
|
403
|
+
idx_60 = int(np.argmin(np.abs(freq - 60)))
|
|
404
|
+
|
|
405
|
+
# Get noise floor estimate
|
|
406
|
+
noise_floor = np.median(mag_db[10 : len(mag_db) // 4])
|
|
407
|
+
|
|
408
|
+
# Check for power line hum
|
|
409
|
+
if idx_60 < len(mag_db) and mag_db[idx_60] > noise_floor + 20:
|
|
410
|
+
return "60hz_hum"
|
|
411
|
+
if idx_50 < len(mag_db) and mag_db[idx_50] > noise_floor + 20:
|
|
412
|
+
return "50hz_hum"
|
|
413
|
+
|
|
414
|
+
# Check frequency distribution
|
|
415
|
+
low_power = np.mean(mag_db[1 : len(mag_db) // 10])
|
|
416
|
+
mid_power = np.mean(mag_db[len(mag_db) // 10 : len(mag_db) // 4])
|
|
417
|
+
high_power = np.mean(mag_db[len(mag_db) // 4 : len(mag_db) // 2])
|
|
418
|
+
|
|
419
|
+
if low_power > mid_power + 10:
|
|
420
|
+
return "low_freq"
|
|
421
|
+
if high_power > mid_power + 10:
|
|
422
|
+
return "high_freq"
|
|
423
|
+
|
|
424
|
+
return "noise"
|
|
425
|
+
|
|
426
|
+
|
|
427
|
+
def _get_default_protocol_config(protocol: str) -> dict[str, Any]:
|
|
428
|
+
"""Get default configuration for a protocol.
|
|
429
|
+
|
|
430
|
+
Args:
|
|
431
|
+
protocol: Protocol name.
|
|
432
|
+
|
|
433
|
+
Returns:
|
|
434
|
+
Default configuration dictionary.
|
|
435
|
+
"""
|
|
436
|
+
configs: dict[str, dict[str, Any]] = {
|
|
437
|
+
"UART": {
|
|
438
|
+
"baud_rate": 115200,
|
|
439
|
+
"data_bits": 8,
|
|
440
|
+
"parity": "none",
|
|
441
|
+
"stop_bits": 1,
|
|
442
|
+
},
|
|
443
|
+
"SPI": {
|
|
444
|
+
"clock_polarity": 0,
|
|
445
|
+
"clock_phase": 0,
|
|
446
|
+
"bit_order": "MSB",
|
|
447
|
+
},
|
|
448
|
+
"I2C": {
|
|
449
|
+
"clock_rate": 100000,
|
|
450
|
+
"address_bits": 7,
|
|
451
|
+
},
|
|
452
|
+
"CAN": {
|
|
453
|
+
"baud_rate": 500000,
|
|
454
|
+
"sample_point": 0.75,
|
|
455
|
+
},
|
|
456
|
+
}
|
|
457
|
+
return configs.get(protocol, {})
|