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,466 @@
|
|
|
1
|
+
"""Interactive analysis wizard for guided signal analysis.
|
|
2
|
+
|
|
3
|
+
This module provides step-by-step guided analysis wizards that help
|
|
4
|
+
non-expert users analyze their signals with intelligent recommendations.
|
|
5
|
+
|
|
6
|
+
- Step-by-step guidance
|
|
7
|
+
- Intelligent defaults
|
|
8
|
+
- Context-aware recommendations
|
|
9
|
+
- Result interpretation
|
|
10
|
+
|
|
11
|
+
Example:
|
|
12
|
+
>>> from oscura.onboarding import run_wizard
|
|
13
|
+
>>> run_wizard(trace)
|
|
14
|
+
Analysis Wizard
|
|
15
|
+
Step 1: What type of signal is this?
|
|
16
|
+
...
|
|
17
|
+
"""
|
|
18
|
+
|
|
19
|
+
from __future__ import annotations
|
|
20
|
+
|
|
21
|
+
from dataclasses import dataclass, field
|
|
22
|
+
from enum import Enum
|
|
23
|
+
from typing import TYPE_CHECKING, Any
|
|
24
|
+
|
|
25
|
+
if TYPE_CHECKING:
|
|
26
|
+
from collections.abc import Callable
|
|
27
|
+
|
|
28
|
+
|
|
29
|
+
class WizardAction(Enum):
|
|
30
|
+
"""Actions the wizard can perform."""
|
|
31
|
+
|
|
32
|
+
MEASURE = "measure"
|
|
33
|
+
CHARACTERIZE = "characterize"
|
|
34
|
+
DECODE = "decode"
|
|
35
|
+
FILTER = "filter"
|
|
36
|
+
SPECTRAL = "spectral"
|
|
37
|
+
COMPARE = "compare"
|
|
38
|
+
|
|
39
|
+
|
|
40
|
+
@dataclass
|
|
41
|
+
class WizardStep:
|
|
42
|
+
"""A step in the analysis wizard.
|
|
43
|
+
|
|
44
|
+
Attributes:
|
|
45
|
+
title: Step title
|
|
46
|
+
question: Question to ask user
|
|
47
|
+
options: Available options
|
|
48
|
+
action: Action to perform based on choice
|
|
49
|
+
help_text: Additional help for this step
|
|
50
|
+
"""
|
|
51
|
+
|
|
52
|
+
title: str
|
|
53
|
+
question: str
|
|
54
|
+
options: list[str]
|
|
55
|
+
action: Callable[[int], None] | None = None
|
|
56
|
+
help_text: str = ""
|
|
57
|
+
skip_condition: Callable[[Any], bool] | None = None
|
|
58
|
+
|
|
59
|
+
|
|
60
|
+
@dataclass
|
|
61
|
+
class WizardResult:
|
|
62
|
+
"""Result from wizard analysis.
|
|
63
|
+
|
|
64
|
+
Attributes:
|
|
65
|
+
steps_completed: Number of steps completed
|
|
66
|
+
measurements: Collected measurements
|
|
67
|
+
recommendations: Analysis recommendations
|
|
68
|
+
summary: Human-readable summary
|
|
69
|
+
"""
|
|
70
|
+
|
|
71
|
+
steps_completed: int = 0
|
|
72
|
+
measurements: dict[str, Any] = field(default_factory=dict)
|
|
73
|
+
recommendations: list[str] = field(default_factory=list)
|
|
74
|
+
summary: str = ""
|
|
75
|
+
|
|
76
|
+
|
|
77
|
+
class AnalysisWizard:
|
|
78
|
+
"""Interactive analysis wizard.
|
|
79
|
+
|
|
80
|
+
Guides users through signal analysis with intelligent
|
|
81
|
+
recommendations and plain English explanations.
|
|
82
|
+
"""
|
|
83
|
+
|
|
84
|
+
def __init__(self, trace: Any) -> None:
|
|
85
|
+
"""Initialize wizard with a trace.
|
|
86
|
+
|
|
87
|
+
Args:
|
|
88
|
+
trace: WaveformTrace or DigitalTrace to analyze
|
|
89
|
+
"""
|
|
90
|
+
self.trace = trace
|
|
91
|
+
self.result = WizardResult()
|
|
92
|
+
self.steps: list[WizardStep] = self._build_steps()
|
|
93
|
+
self.current_step = 0
|
|
94
|
+
|
|
95
|
+
def _build_steps(self) -> list[WizardStep]:
|
|
96
|
+
"""Build the wizard steps based on trace characteristics."""
|
|
97
|
+
steps = [
|
|
98
|
+
WizardStep(
|
|
99
|
+
title="Signal Type Detection",
|
|
100
|
+
question="What type of analysis do you want to perform?",
|
|
101
|
+
options=[
|
|
102
|
+
"Auto-detect (let Oscura figure it out)",
|
|
103
|
+
"Digital signal analysis",
|
|
104
|
+
"Analog/waveform analysis",
|
|
105
|
+
"Protocol decoding",
|
|
106
|
+
"Power analysis",
|
|
107
|
+
],
|
|
108
|
+
action=self._handle_signal_type,
|
|
109
|
+
help_text="Not sure? Choose 'Auto-detect' and we'll analyze your signal.",
|
|
110
|
+
),
|
|
111
|
+
WizardStep(
|
|
112
|
+
title="Basic Measurements",
|
|
113
|
+
question="Would you like to run basic measurements?",
|
|
114
|
+
options=[
|
|
115
|
+
"Yes, run all standard measurements",
|
|
116
|
+
"Yes, but only timing measurements",
|
|
117
|
+
"Yes, but only amplitude measurements",
|
|
118
|
+
"No, skip this step",
|
|
119
|
+
],
|
|
120
|
+
action=self._handle_measurements,
|
|
121
|
+
help_text="Basic measurements give you an overview of your signal.",
|
|
122
|
+
),
|
|
123
|
+
WizardStep(
|
|
124
|
+
title="Spectral Analysis",
|
|
125
|
+
question="Would you like to analyze the frequency content?",
|
|
126
|
+
options=[
|
|
127
|
+
"Yes, compute FFT spectrum",
|
|
128
|
+
"Yes, compute power spectral density",
|
|
129
|
+
"Yes, both FFT and PSD",
|
|
130
|
+
"No, skip spectral analysis",
|
|
131
|
+
],
|
|
132
|
+
action=self._handle_spectral,
|
|
133
|
+
help_text="Spectral analysis shows what frequencies are in your signal.",
|
|
134
|
+
),
|
|
135
|
+
WizardStep(
|
|
136
|
+
title="Signal Quality",
|
|
137
|
+
question="Would you like to assess signal quality?",
|
|
138
|
+
options=[
|
|
139
|
+
"Yes, measure THD and SNR",
|
|
140
|
+
"Yes, check for anomalies",
|
|
141
|
+
"Yes, full quality assessment",
|
|
142
|
+
"No, skip quality check",
|
|
143
|
+
],
|
|
144
|
+
action=self._handle_quality,
|
|
145
|
+
help_text="Quality metrics help identify issues with your signal.",
|
|
146
|
+
),
|
|
147
|
+
]
|
|
148
|
+
return steps
|
|
149
|
+
|
|
150
|
+
def run(self, interactive: bool = True) -> WizardResult:
|
|
151
|
+
"""Run the analysis wizard.
|
|
152
|
+
|
|
153
|
+
Args:
|
|
154
|
+
interactive: If True, prompt for user input
|
|
155
|
+
|
|
156
|
+
Returns:
|
|
157
|
+
WizardResult with all collected data
|
|
158
|
+
"""
|
|
159
|
+
print("\n" + "=" * 60)
|
|
160
|
+
print("Oscura Analysis Wizard")
|
|
161
|
+
print("=" * 60)
|
|
162
|
+
print("Let's analyze your signal step by step.\n")
|
|
163
|
+
|
|
164
|
+
# Show trace summary
|
|
165
|
+
self._show_trace_summary()
|
|
166
|
+
|
|
167
|
+
for i, step in enumerate(self.steps):
|
|
168
|
+
# Check skip condition
|
|
169
|
+
if step.skip_condition and step.skip_condition(self.result):
|
|
170
|
+
continue
|
|
171
|
+
|
|
172
|
+
print(f"\n{'=' * 60}")
|
|
173
|
+
print(f"Step {i + 1}/{len(self.steps)}: {step.title}")
|
|
174
|
+
print("=" * 60)
|
|
175
|
+
|
|
176
|
+
if step.help_text:
|
|
177
|
+
print(f"Tip: {step.help_text}\n")
|
|
178
|
+
|
|
179
|
+
print(step.question)
|
|
180
|
+
for j, option in enumerate(step.options, 1):
|
|
181
|
+
print(f" {j}. {option}")
|
|
182
|
+
|
|
183
|
+
if interactive:
|
|
184
|
+
choice = self._get_user_choice(len(step.options))
|
|
185
|
+
else:
|
|
186
|
+
choice = 1 # Auto-select first option
|
|
187
|
+
|
|
188
|
+
if step.action:
|
|
189
|
+
step.action(choice)
|
|
190
|
+
|
|
191
|
+
self.result.steps_completed = i + 1
|
|
192
|
+
|
|
193
|
+
# Generate summary
|
|
194
|
+
self._generate_summary()
|
|
195
|
+
|
|
196
|
+
print("\n" + "=" * 60)
|
|
197
|
+
print("Analysis Complete!")
|
|
198
|
+
print("=" * 60)
|
|
199
|
+
print(self.result.summary)
|
|
200
|
+
|
|
201
|
+
if self.result.recommendations:
|
|
202
|
+
print("\nRecommendations:")
|
|
203
|
+
for rec in self.result.recommendations:
|
|
204
|
+
print(f" - {rec}")
|
|
205
|
+
|
|
206
|
+
return self.result
|
|
207
|
+
|
|
208
|
+
def _show_trace_summary(self) -> None:
|
|
209
|
+
"""Show a summary of the loaded trace."""
|
|
210
|
+
trace = self.trace
|
|
211
|
+
print("Loaded trace summary:")
|
|
212
|
+
|
|
213
|
+
if hasattr(trace, "data"):
|
|
214
|
+
print(f" Samples: {len(trace.data):,}")
|
|
215
|
+
|
|
216
|
+
if hasattr(trace, "metadata"):
|
|
217
|
+
meta = trace.metadata
|
|
218
|
+
if hasattr(meta, "sample_rate") and meta.sample_rate:
|
|
219
|
+
rate = meta.sample_rate
|
|
220
|
+
if rate >= 1e9:
|
|
221
|
+
print(f" Sample rate: {rate / 1e9:.3f} GSa/s")
|
|
222
|
+
elif rate >= 1e6:
|
|
223
|
+
print(f" Sample rate: {rate / 1e6:.3f} MSa/s")
|
|
224
|
+
else:
|
|
225
|
+
print(f" Sample rate: {rate / 1e3:.3f} kSa/s")
|
|
226
|
+
|
|
227
|
+
if hasattr(meta, "channel_name") and meta.channel_name:
|
|
228
|
+
print(f" Channel: {meta.channel_name}")
|
|
229
|
+
|
|
230
|
+
if hasattr(trace, "data"):
|
|
231
|
+
import numpy as np
|
|
232
|
+
|
|
233
|
+
data = trace.data
|
|
234
|
+
print(f" Value range: {np.min(data):.4g} to {np.max(data):.4g}")
|
|
235
|
+
|
|
236
|
+
def _get_user_choice(self, max_options: int) -> int:
|
|
237
|
+
"""Get user's choice with validation."""
|
|
238
|
+
while True:
|
|
239
|
+
try:
|
|
240
|
+
choice_str = input(f"\nEnter choice (1-{max_options}): ")
|
|
241
|
+
choice = int(choice_str)
|
|
242
|
+
if 1 <= choice <= max_options:
|
|
243
|
+
return choice
|
|
244
|
+
print(f"Please enter a number between 1 and {max_options}")
|
|
245
|
+
except ValueError:
|
|
246
|
+
print("Please enter a valid number")
|
|
247
|
+
|
|
248
|
+
def _handle_signal_type(self, choice: int) -> None:
|
|
249
|
+
"""Handle signal type selection."""
|
|
250
|
+
if choice == 1: # Auto-detect
|
|
251
|
+
print("\nAuto-detecting signal type...")
|
|
252
|
+
try:
|
|
253
|
+
from oscura.discovery import characterize_signal
|
|
254
|
+
|
|
255
|
+
result = characterize_signal(self.trace)
|
|
256
|
+
self.result.measurements["signal_type"] = result.signal_type
|
|
257
|
+
self.result.measurements["signal_confidence"] = result.confidence
|
|
258
|
+
print(f"Detected: {result.signal_type} (confidence: {result.confidence:.0%})")
|
|
259
|
+
|
|
260
|
+
if result.confidence < 0.8:
|
|
261
|
+
self.result.recommendations.append(
|
|
262
|
+
f"Signal type detection has low confidence. "
|
|
263
|
+
f"Consider alternatives: {[a.signal_type for a in result.alternatives[:2]]}" # type: ignore[attr-defined]
|
|
264
|
+
)
|
|
265
|
+
except Exception as e:
|
|
266
|
+
print(f"Auto-detection failed: {e}")
|
|
267
|
+
self.result.measurements["signal_type"] = "unknown"
|
|
268
|
+
|
|
269
|
+
elif choice == 2: # Digital
|
|
270
|
+
self.result.measurements["signal_type"] = "digital"
|
|
271
|
+
print("\nDigital analysis mode selected.")
|
|
272
|
+
|
|
273
|
+
elif choice == 3: # Analog
|
|
274
|
+
self.result.measurements["signal_type"] = "analog"
|
|
275
|
+
print("\nAnalog/waveform analysis mode selected.")
|
|
276
|
+
|
|
277
|
+
elif choice == 4: # Protocol
|
|
278
|
+
self.result.measurements["signal_type"] = "protocol"
|
|
279
|
+
print("\nProtocol decoding mode selected.")
|
|
280
|
+
self.result.recommendations.append(
|
|
281
|
+
"For protocol decoding, try: decode_uart(), decode_spi(), decode_i2c()"
|
|
282
|
+
)
|
|
283
|
+
|
|
284
|
+
elif choice == 5: # Power
|
|
285
|
+
self.result.measurements["signal_type"] = "power"
|
|
286
|
+
print("\nPower analysis mode selected.")
|
|
287
|
+
|
|
288
|
+
def _handle_measurements(self, choice: int) -> None:
|
|
289
|
+
"""Handle measurement selection."""
|
|
290
|
+
import oscura as osc
|
|
291
|
+
|
|
292
|
+
if choice == 4: # Skip
|
|
293
|
+
print("\nSkipping measurements.")
|
|
294
|
+
return
|
|
295
|
+
|
|
296
|
+
print("\nRunning measurements...")
|
|
297
|
+
|
|
298
|
+
try:
|
|
299
|
+
if choice == 1: # All
|
|
300
|
+
results = osc.measure(self.trace)
|
|
301
|
+
elif choice == 2: # Timing only
|
|
302
|
+
results = {
|
|
303
|
+
"rise_time": osc.rise_time(self.trace),
|
|
304
|
+
"fall_time": osc.fall_time(self.trace),
|
|
305
|
+
"frequency": osc.frequency(self.trace),
|
|
306
|
+
"period": osc.period(self.trace),
|
|
307
|
+
"duty_cycle": osc.duty_cycle(self.trace),
|
|
308
|
+
}
|
|
309
|
+
elif choice == 3: # Amplitude only
|
|
310
|
+
results = {
|
|
311
|
+
"amplitude": osc.amplitude(self.trace),
|
|
312
|
+
"rms": osc.rms(self.trace),
|
|
313
|
+
"mean": osc.mean(self.trace),
|
|
314
|
+
"overshoot": osc.overshoot(self.trace),
|
|
315
|
+
"undershoot": osc.undershoot(self.trace),
|
|
316
|
+
}
|
|
317
|
+
|
|
318
|
+
self.result.measurements.update(results)
|
|
319
|
+
|
|
320
|
+
print("\nMeasurement results:")
|
|
321
|
+
for name, value in results.items():
|
|
322
|
+
if isinstance(value, float):
|
|
323
|
+
print(f" {name}: {value:.4g}")
|
|
324
|
+
else:
|
|
325
|
+
print(f" {name}: {value}")
|
|
326
|
+
|
|
327
|
+
except Exception as e:
|
|
328
|
+
print(f"Measurement error: {e}")
|
|
329
|
+
|
|
330
|
+
def _handle_spectral(self, choice: int) -> None:
|
|
331
|
+
"""Handle spectral analysis selection."""
|
|
332
|
+
import numpy as np
|
|
333
|
+
|
|
334
|
+
import oscura as osc
|
|
335
|
+
|
|
336
|
+
if choice == 4: # Skip
|
|
337
|
+
print("\nSkipping spectral analysis.")
|
|
338
|
+
return
|
|
339
|
+
|
|
340
|
+
print("\nRunning spectral analysis...")
|
|
341
|
+
|
|
342
|
+
try:
|
|
343
|
+
if choice in (1, 3): # FFT
|
|
344
|
+
freq, mag = osc.fft(self.trace) # type: ignore[misc]
|
|
345
|
+
peak_idx = np.argmax(mag)
|
|
346
|
+
self.result.measurements["fft_peak_freq"] = freq[peak_idx]
|
|
347
|
+
self.result.measurements["fft_peak_mag"] = mag[peak_idx]
|
|
348
|
+
print(f" FFT peak: {freq[peak_idx] / 1e6:.3f} MHz at {mag[peak_idx]:.1f} dB")
|
|
349
|
+
|
|
350
|
+
if choice in (2, 3): # PSD
|
|
351
|
+
freq, _psd_vals = osc.psd(self.trace)
|
|
352
|
+
self.result.measurements["psd_computed"] = True
|
|
353
|
+
print(f" PSD computed over {len(freq)} frequency bins")
|
|
354
|
+
|
|
355
|
+
except Exception as e:
|
|
356
|
+
print(f"Spectral analysis error: {e}")
|
|
357
|
+
|
|
358
|
+
def _handle_quality(self, choice: int) -> None:
|
|
359
|
+
"""Handle quality assessment selection."""
|
|
360
|
+
import oscura as osc
|
|
361
|
+
|
|
362
|
+
if choice == 4: # Skip
|
|
363
|
+
print("\nSkipping quality assessment.")
|
|
364
|
+
return
|
|
365
|
+
|
|
366
|
+
print("\nAssessing signal quality...")
|
|
367
|
+
|
|
368
|
+
try:
|
|
369
|
+
if choice in (1, 3): # THD and SNR
|
|
370
|
+
thd_val = osc.thd(self.trace)
|
|
371
|
+
snr_val = osc.snr(self.trace)
|
|
372
|
+
self.result.measurements["thd"] = thd_val
|
|
373
|
+
self.result.measurements["snr"] = snr_val
|
|
374
|
+
print(f" THD: {thd_val:.1f} dB")
|
|
375
|
+
print(f" SNR: {snr_val:.1f} dB")
|
|
376
|
+
|
|
377
|
+
# Recommendations based on quality
|
|
378
|
+
if thd_val > -40:
|
|
379
|
+
self.result.recommendations.append(
|
|
380
|
+
f"THD is {thd_val:.1f} dB - consider filtering to reduce distortion"
|
|
381
|
+
)
|
|
382
|
+
if snr_val < 40:
|
|
383
|
+
self.result.recommendations.append(
|
|
384
|
+
f"SNR is {snr_val:.1f} dB - signal is noisy, try averaging or filtering"
|
|
385
|
+
)
|
|
386
|
+
|
|
387
|
+
if choice in (2, 3): # Anomalies
|
|
388
|
+
from oscura.discovery import find_anomalies
|
|
389
|
+
|
|
390
|
+
anomalies = find_anomalies(self.trace)
|
|
391
|
+
self.result.measurements["anomaly_count"] = len(anomalies)
|
|
392
|
+
print(f" Found {len(anomalies)} anomalies")
|
|
393
|
+
|
|
394
|
+
if anomalies:
|
|
395
|
+
self.result.recommendations.append(
|
|
396
|
+
f"Found {len(anomalies)} anomalies - review the anomaly list for issues"
|
|
397
|
+
)
|
|
398
|
+
|
|
399
|
+
except Exception as e:
|
|
400
|
+
print(f"Quality assessment error: {e}")
|
|
401
|
+
|
|
402
|
+
def _generate_summary(self) -> None:
|
|
403
|
+
"""Generate a human-readable summary of the analysis."""
|
|
404
|
+
lines = ["Analysis Summary:"]
|
|
405
|
+
|
|
406
|
+
if "signal_type" in self.result.measurements:
|
|
407
|
+
lines.append(f" Signal type: {self.result.measurements['signal_type']}")
|
|
408
|
+
|
|
409
|
+
if "frequency" in self.result.measurements:
|
|
410
|
+
freq = self.result.measurements["frequency"]
|
|
411
|
+
# Handle dict format from measure()
|
|
412
|
+
if isinstance(freq, dict):
|
|
413
|
+
freq = freq.get("value", 0)
|
|
414
|
+
|
|
415
|
+
if freq >= 1e6:
|
|
416
|
+
lines.append(f" Frequency: {freq / 1e6:.3f} MHz")
|
|
417
|
+
elif freq >= 1e3:
|
|
418
|
+
lines.append(f" Frequency: {freq / 1e3:.3f} kHz")
|
|
419
|
+
else:
|
|
420
|
+
lines.append(f" Frequency: {freq:.1f} Hz")
|
|
421
|
+
|
|
422
|
+
if "rise_time" in self.result.measurements:
|
|
423
|
+
rt = self.result.measurements["rise_time"]
|
|
424
|
+
# Handle dict format from measure()
|
|
425
|
+
if isinstance(rt, dict):
|
|
426
|
+
rt = rt.get("value", 0)
|
|
427
|
+
|
|
428
|
+
lines.append(f" Rise time: {rt * 1e9:.2f} ns")
|
|
429
|
+
|
|
430
|
+
if "thd" in self.result.measurements:
|
|
431
|
+
thd = self.result.measurements["thd"]
|
|
432
|
+
# Handle dict format
|
|
433
|
+
if isinstance(thd, dict):
|
|
434
|
+
thd = thd.get("value", 0)
|
|
435
|
+
lines.append(f" THD: {thd:.1f} dB")
|
|
436
|
+
|
|
437
|
+
if "snr" in self.result.measurements:
|
|
438
|
+
snr = self.result.measurements["snr"]
|
|
439
|
+
# Handle dict format
|
|
440
|
+
if isinstance(snr, dict):
|
|
441
|
+
snr = snr.get("value", 0)
|
|
442
|
+
lines.append(f" SNR: {snr:.1f} dB")
|
|
443
|
+
|
|
444
|
+
self.result.summary = "\n".join(lines)
|
|
445
|
+
|
|
446
|
+
|
|
447
|
+
def run_wizard(trace: Any, interactive: bool = True) -> WizardResult:
|
|
448
|
+
"""Run the analysis wizard on a trace.
|
|
449
|
+
|
|
450
|
+
This is the main entry point for guided analysis.
|
|
451
|
+
|
|
452
|
+
Args:
|
|
453
|
+
trace: WaveformTrace or DigitalTrace to analyze
|
|
454
|
+
interactive: If True, prompt for user input
|
|
455
|
+
|
|
456
|
+
Returns:
|
|
457
|
+
WizardResult with measurements, recommendations, and summary
|
|
458
|
+
|
|
459
|
+
Example:
|
|
460
|
+
>>> import oscura as osc
|
|
461
|
+
>>> from oscura.onboarding import run_wizard
|
|
462
|
+
>>> trace = osc.load("signal.csv")
|
|
463
|
+
>>> result = run_wizard(trace)
|
|
464
|
+
"""
|
|
465
|
+
wizard = AnalysisWizard(trace)
|
|
466
|
+
return wizard.run(interactive=interactive)
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
"""Parameter optimization and search algorithms.
|
|
2
|
+
|
|
3
|
+
This module provides grid search and randomized search for finding optimal
|
|
4
|
+
analysis parameters.
|
|
5
|
+
"""
|
|
6
|
+
|
|
7
|
+
from oscura.optimization.search import (
|
|
8
|
+
GridSearchCV,
|
|
9
|
+
RandomizedSearchCV,
|
|
10
|
+
ScoringFunction,
|
|
11
|
+
SearchResult,
|
|
12
|
+
)
|
|
13
|
+
|
|
14
|
+
__all__ = [
|
|
15
|
+
"GridSearchCV",
|
|
16
|
+
"RandomizedSearchCV",
|
|
17
|
+
"ScoringFunction",
|
|
18
|
+
"SearchResult",
|
|
19
|
+
]
|