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,429 @@
|
|
|
1
|
+
"""Recommendation engine for guided analysis workflow.
|
|
2
|
+
|
|
3
|
+
This module provides contextual "What should I look at next?" recommendations
|
|
4
|
+
based on current analysis state.
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
Example:
|
|
8
|
+
>>> from oscura.guidance import suggest_next_steps
|
|
9
|
+
>>> recommendations = suggest_next_steps(trace, current_state)
|
|
10
|
+
>>> for rec in recommendations:
|
|
11
|
+
... print(f"{rec.title}: {rec.explanation}")
|
|
12
|
+
|
|
13
|
+
References:
|
|
14
|
+
Oscura Auto-Discovery Specification
|
|
15
|
+
"""
|
|
16
|
+
|
|
17
|
+
from __future__ import annotations
|
|
18
|
+
|
|
19
|
+
from dataclasses import dataclass, field
|
|
20
|
+
from datetime import datetime
|
|
21
|
+
from typing import TYPE_CHECKING, Any
|
|
22
|
+
|
|
23
|
+
if TYPE_CHECKING:
|
|
24
|
+
from collections.abc import Callable
|
|
25
|
+
|
|
26
|
+
from oscura.core.types import WaveformTrace
|
|
27
|
+
|
|
28
|
+
|
|
29
|
+
@dataclass
|
|
30
|
+
class Recommendation:
|
|
31
|
+
"""Analysis step recommendation.
|
|
32
|
+
|
|
33
|
+
Attributes:
|
|
34
|
+
id: Unique recommendation ID.
|
|
35
|
+
title: Short title (≤50 chars).
|
|
36
|
+
explanation: Why this step is relevant (≤50 words).
|
|
37
|
+
rationale: Detailed reasoning.
|
|
38
|
+
priority: Priority score (0.0-1.0).
|
|
39
|
+
urgency: Urgency score (0.0-1.0).
|
|
40
|
+
ease: Ease of execution (0.0-1.0, higher = easier).
|
|
41
|
+
impact: Expected impact (0.0-1.0, higher = more valuable).
|
|
42
|
+
result_key: Key for storing result in state.
|
|
43
|
+
execute: Optional callable to execute this step.
|
|
44
|
+
impact_description: Description of expected impact.
|
|
45
|
+
"""
|
|
46
|
+
|
|
47
|
+
id: str
|
|
48
|
+
title: str
|
|
49
|
+
explanation: str
|
|
50
|
+
priority: float
|
|
51
|
+
urgency: float = 0.5
|
|
52
|
+
ease: float = 0.5
|
|
53
|
+
impact: float = 0.5
|
|
54
|
+
rationale: str = ""
|
|
55
|
+
result_key: str = ""
|
|
56
|
+
execute: Callable | None = None # type: ignore[type-arg]
|
|
57
|
+
impact_description: str = ""
|
|
58
|
+
|
|
59
|
+
|
|
60
|
+
@dataclass
|
|
61
|
+
class AnalysisHistory:
|
|
62
|
+
"""Track completed analysis steps.
|
|
63
|
+
|
|
64
|
+
Attributes:
|
|
65
|
+
steps_completed: List of completed step IDs.
|
|
66
|
+
step_timestamps: Timestamps for each step.
|
|
67
|
+
results: Stored results from each step.
|
|
68
|
+
"""
|
|
69
|
+
|
|
70
|
+
steps_completed: list[str] = field(default_factory=list)
|
|
71
|
+
step_timestamps: dict[str, datetime] = field(default_factory=dict)
|
|
72
|
+
results: dict[str, Any] = field(default_factory=dict)
|
|
73
|
+
|
|
74
|
+
def add_step(self, step_id: str, result: Any = None) -> None:
|
|
75
|
+
"""Record a completed step.
|
|
76
|
+
|
|
77
|
+
Args:
|
|
78
|
+
step_id: Step identifier.
|
|
79
|
+
result: Optional result from step.
|
|
80
|
+
"""
|
|
81
|
+
if step_id not in self.steps_completed:
|
|
82
|
+
self.steps_completed.append(step_id)
|
|
83
|
+
|
|
84
|
+
self.step_timestamps[step_id] = datetime.now()
|
|
85
|
+
|
|
86
|
+
if result is not None:
|
|
87
|
+
self.results[step_id] = result
|
|
88
|
+
|
|
89
|
+
def was_recent(self, step_id: str, seconds: float = 60.0) -> bool:
|
|
90
|
+
"""Check if step was completed recently.
|
|
91
|
+
|
|
92
|
+
Args:
|
|
93
|
+
step_id: Step identifier.
|
|
94
|
+
seconds: Time window in seconds.
|
|
95
|
+
|
|
96
|
+
Returns:
|
|
97
|
+
True if step was done within time window.
|
|
98
|
+
"""
|
|
99
|
+
if step_id not in self.step_timestamps:
|
|
100
|
+
return False
|
|
101
|
+
|
|
102
|
+
elapsed = datetime.now() - self.step_timestamps[step_id]
|
|
103
|
+
return elapsed.total_seconds() < seconds
|
|
104
|
+
|
|
105
|
+
|
|
106
|
+
def _calculate_priority(
|
|
107
|
+
urgency: float,
|
|
108
|
+
ease: float,
|
|
109
|
+
impact: float,
|
|
110
|
+
) -> float:
|
|
111
|
+
"""Calculate recommendation priority.
|
|
112
|
+
|
|
113
|
+
Uses weighted scoring: urgency (40%), ease (30%), impact (30%).
|
|
114
|
+
|
|
115
|
+
Args:
|
|
116
|
+
urgency: Urgency score (0.0-1.0).
|
|
117
|
+
ease: Ease score (0.0-1.0).
|
|
118
|
+
impact: Impact score (0.0-1.0).
|
|
119
|
+
|
|
120
|
+
Returns:
|
|
121
|
+
Priority score (0.0-1.0).
|
|
122
|
+
"""
|
|
123
|
+
priority = 0.4 * urgency + 0.3 * ease + 0.3 * impact
|
|
124
|
+
return round(priority, 2)
|
|
125
|
+
|
|
126
|
+
|
|
127
|
+
def _recommend_characterization(
|
|
128
|
+
trace: WaveformTrace,
|
|
129
|
+
state: dict, # type: ignore[type-arg]
|
|
130
|
+
history: AnalysisHistory,
|
|
131
|
+
) -> Recommendation | None:
|
|
132
|
+
"""Recommend signal characterization if not done.
|
|
133
|
+
|
|
134
|
+
Args:
|
|
135
|
+
trace: Waveform being analyzed.
|
|
136
|
+
state: Current analysis state.
|
|
137
|
+
history: Analysis history.
|
|
138
|
+
|
|
139
|
+
Returns:
|
|
140
|
+
Recommendation or None if already done.
|
|
141
|
+
"""
|
|
142
|
+
if "characterization" in state or history.was_recent("characterization"):
|
|
143
|
+
return None
|
|
144
|
+
|
|
145
|
+
return Recommendation(
|
|
146
|
+
id="characterization",
|
|
147
|
+
title="Characterize signal",
|
|
148
|
+
explanation="Identify what type of signal this is (UART, SPI, analog, etc.) to guide further analysis.",
|
|
149
|
+
urgency=0.95,
|
|
150
|
+
ease=0.90,
|
|
151
|
+
impact=0.95,
|
|
152
|
+
priority=_calculate_priority(0.95, 0.90, 0.95),
|
|
153
|
+
rationale="Signal characterization is the foundation for all other analysis",
|
|
154
|
+
result_key="characterization",
|
|
155
|
+
impact_description="Enables protocol-specific analysis and targeted measurements",
|
|
156
|
+
)
|
|
157
|
+
|
|
158
|
+
|
|
159
|
+
def _recommend_anomaly_check(
|
|
160
|
+
trace: WaveformTrace,
|
|
161
|
+
state: dict, # type: ignore[type-arg]
|
|
162
|
+
history: AnalysisHistory,
|
|
163
|
+
) -> Recommendation | None:
|
|
164
|
+
"""Recommend anomaly detection.
|
|
165
|
+
|
|
166
|
+
Args:
|
|
167
|
+
trace: Waveform being analyzed.
|
|
168
|
+
state: Current analysis state.
|
|
169
|
+
history: Analysis history.
|
|
170
|
+
|
|
171
|
+
Returns:
|
|
172
|
+
Recommendation or None if not applicable.
|
|
173
|
+
"""
|
|
174
|
+
if "anomalies" in state or history.was_recent("anomalies"):
|
|
175
|
+
return None
|
|
176
|
+
|
|
177
|
+
# Higher priority if characterization shows issues
|
|
178
|
+
urgency = 0.70
|
|
179
|
+
|
|
180
|
+
if "characterization" in state:
|
|
181
|
+
char = state["characterization"]
|
|
182
|
+
if hasattr(char, "confidence") and char.confidence < 0.8:
|
|
183
|
+
urgency = 0.85
|
|
184
|
+
|
|
185
|
+
if "quality" in state:
|
|
186
|
+
quality = state["quality"]
|
|
187
|
+
if hasattr(quality, "status") and quality.status in ["WARNING", "FAIL"]:
|
|
188
|
+
urgency = 0.90
|
|
189
|
+
|
|
190
|
+
return Recommendation(
|
|
191
|
+
id="anomaly_detection",
|
|
192
|
+
title="Check for anomalies",
|
|
193
|
+
explanation="Scan the signal for glitches, dropouts, noise spikes, and other problems that could affect data integrity.",
|
|
194
|
+
urgency=urgency,
|
|
195
|
+
ease=0.85,
|
|
196
|
+
impact=0.80,
|
|
197
|
+
priority=_calculate_priority(urgency, 0.85, 0.80),
|
|
198
|
+
rationale="Quality concerns detected - anomaly scan recommended",
|
|
199
|
+
result_key="anomalies",
|
|
200
|
+
impact_description="Identifies specific problem areas and their severity",
|
|
201
|
+
)
|
|
202
|
+
|
|
203
|
+
|
|
204
|
+
def _recommend_quality_assessment(
|
|
205
|
+
trace: WaveformTrace,
|
|
206
|
+
state: dict, # type: ignore[type-arg]
|
|
207
|
+
history: AnalysisHistory,
|
|
208
|
+
) -> Recommendation | None:
|
|
209
|
+
"""Recommend data quality assessment.
|
|
210
|
+
|
|
211
|
+
Args:
|
|
212
|
+
trace: Waveform being analyzed.
|
|
213
|
+
state: Current analysis state.
|
|
214
|
+
history: Analysis history.
|
|
215
|
+
|
|
216
|
+
Returns:
|
|
217
|
+
Recommendation or None if not applicable.
|
|
218
|
+
"""
|
|
219
|
+
if "quality" in state or history.was_recent("quality"):
|
|
220
|
+
return None
|
|
221
|
+
|
|
222
|
+
# Higher priority early in analysis
|
|
223
|
+
urgency = 0.75 if len(state) < 2 else 0.65
|
|
224
|
+
|
|
225
|
+
return Recommendation(
|
|
226
|
+
id="quality_assessment",
|
|
227
|
+
title="Assess data quality",
|
|
228
|
+
explanation="Verify that sample rate and resolution are adequate for reliable analysis of this signal.",
|
|
229
|
+
urgency=urgency,
|
|
230
|
+
ease=0.90,
|
|
231
|
+
impact=0.75,
|
|
232
|
+
priority=_calculate_priority(urgency, 0.90, 0.75),
|
|
233
|
+
rationale="Ensures captured data is suitable for intended analysis",
|
|
234
|
+
result_key="quality",
|
|
235
|
+
impact_description="Confirms data is good enough or identifies capture improvements needed",
|
|
236
|
+
)
|
|
237
|
+
|
|
238
|
+
|
|
239
|
+
def _recommend_protocol_decode(
|
|
240
|
+
trace: WaveformTrace,
|
|
241
|
+
state: dict, # type: ignore[type-arg]
|
|
242
|
+
history: AnalysisHistory,
|
|
243
|
+
) -> Recommendation | None:
|
|
244
|
+
"""Recommend protocol decoding.
|
|
245
|
+
|
|
246
|
+
Args:
|
|
247
|
+
trace: Waveform being analyzed.
|
|
248
|
+
state: Current analysis state.
|
|
249
|
+
history: Analysis history.
|
|
250
|
+
|
|
251
|
+
Returns:
|
|
252
|
+
Recommendation or None if not applicable.
|
|
253
|
+
"""
|
|
254
|
+
if "decode" in state or history.was_recent("decode"):
|
|
255
|
+
return None
|
|
256
|
+
|
|
257
|
+
# Only recommend if signal type is identified
|
|
258
|
+
if "characterization" not in state:
|
|
259
|
+
return None
|
|
260
|
+
|
|
261
|
+
char = state["characterization"]
|
|
262
|
+
|
|
263
|
+
# Check if it's a protocol signal
|
|
264
|
+
if hasattr(char, "signal_type"):
|
|
265
|
+
signal_type = char.signal_type.lower()
|
|
266
|
+
|
|
267
|
+
if any(proto in signal_type for proto in ["uart", "spi", "i2c", "can"]):
|
|
268
|
+
confidence = getattr(char, "confidence", 0.0)
|
|
269
|
+
|
|
270
|
+
if confidence >= 0.7:
|
|
271
|
+
urgency = 0.85
|
|
272
|
+
explanation = f"Signal identified as {char.signal_type} with high confidence. Decode to extract transmitted data."
|
|
273
|
+
else:
|
|
274
|
+
urgency = 0.60
|
|
275
|
+
explanation = f"Signal possibly {char.signal_type} but confidence is low. Decode may help verify."
|
|
276
|
+
|
|
277
|
+
return Recommendation(
|
|
278
|
+
id="protocol_decode",
|
|
279
|
+
title="Decode protocol data",
|
|
280
|
+
explanation=explanation,
|
|
281
|
+
urgency=urgency,
|
|
282
|
+
ease=0.80,
|
|
283
|
+
impact=0.90,
|
|
284
|
+
priority=_calculate_priority(urgency, 0.80, 0.90),
|
|
285
|
+
rationale=f"{char.signal_type} protocol detected",
|
|
286
|
+
result_key="decode",
|
|
287
|
+
impact_description="Extracts meaningful data from signal",
|
|
288
|
+
)
|
|
289
|
+
|
|
290
|
+
return None
|
|
291
|
+
|
|
292
|
+
|
|
293
|
+
def _recommend_spectral_analysis(
|
|
294
|
+
trace: WaveformTrace,
|
|
295
|
+
state: dict, # type: ignore[type-arg]
|
|
296
|
+
history: AnalysisHistory,
|
|
297
|
+
) -> Recommendation | None:
|
|
298
|
+
"""Recommend spectral analysis.
|
|
299
|
+
|
|
300
|
+
Args:
|
|
301
|
+
trace: Waveform being analyzed.
|
|
302
|
+
state: Current analysis state.
|
|
303
|
+
history: Analysis history.
|
|
304
|
+
|
|
305
|
+
Returns:
|
|
306
|
+
Recommendation or None if not applicable.
|
|
307
|
+
"""
|
|
308
|
+
if "spectral" in state or history.was_recent("spectral"):
|
|
309
|
+
return None
|
|
310
|
+
|
|
311
|
+
# Recommend for analog/periodic signals
|
|
312
|
+
if "characterization" in state:
|
|
313
|
+
char = state["characterization"]
|
|
314
|
+
|
|
315
|
+
if hasattr(char, "signal_type"):
|
|
316
|
+
signal_type = char.signal_type.lower()
|
|
317
|
+
|
|
318
|
+
if "analog" in signal_type or "periodic" in signal_type:
|
|
319
|
+
return Recommendation(
|
|
320
|
+
id="spectral_analysis",
|
|
321
|
+
title="Perform spectral analysis",
|
|
322
|
+
explanation="Analyze frequency content to identify dominant frequencies and harmonics in this analog signal.",
|
|
323
|
+
urgency=0.65,
|
|
324
|
+
ease=0.75,
|
|
325
|
+
impact=0.80,
|
|
326
|
+
priority=_calculate_priority(0.65, 0.75, 0.80),
|
|
327
|
+
rationale="Periodic/analog signal detected",
|
|
328
|
+
result_key="spectral",
|
|
329
|
+
impact_description="Reveals frequency components and signal purity",
|
|
330
|
+
)
|
|
331
|
+
|
|
332
|
+
return None
|
|
333
|
+
|
|
334
|
+
|
|
335
|
+
def suggest_next_steps(
|
|
336
|
+
trace: WaveformTrace,
|
|
337
|
+
*,
|
|
338
|
+
current_state: dict[str, Any] | None = None,
|
|
339
|
+
max_suggestions: int = 3,
|
|
340
|
+
include_rationale: bool = False,
|
|
341
|
+
) -> list[Recommendation]:
|
|
342
|
+
"""Suggest next analysis steps based on current state.
|
|
343
|
+
|
|
344
|
+
Provides contextual recommendations guiding users through the investigation
|
|
345
|
+
process without requiring expertise.
|
|
346
|
+
|
|
347
|
+
Args:
|
|
348
|
+
trace: Waveform being analyzed.
|
|
349
|
+
current_state: Current analysis state with completed steps and results.
|
|
350
|
+
max_suggestions: Maximum number of suggestions (default 3, range 2-5).
|
|
351
|
+
include_rationale: Include detailed rationale in recommendations.
|
|
352
|
+
|
|
353
|
+
Returns:
|
|
354
|
+
List of 2-5 recommended next steps, ranked by priority.
|
|
355
|
+
|
|
356
|
+
Example:
|
|
357
|
+
>>> trace = load("capture.wfm")
|
|
358
|
+
>>> state = {"characterization": char_result}
|
|
359
|
+
>>> recommendations = suggest_next_steps(trace, current_state=state)
|
|
360
|
+
>>> for rec in recommendations:
|
|
361
|
+
... print(f"{rec.priority:.2f}: {rec.title}")
|
|
362
|
+
|
|
363
|
+
References:
|
|
364
|
+
DISC-008: Recommendation Engine
|
|
365
|
+
"""
|
|
366
|
+
current_state = current_state or {}
|
|
367
|
+
|
|
368
|
+
# Extract or create analysis history
|
|
369
|
+
if "steps_completed" in current_state:
|
|
370
|
+
history = AnalysisHistory(
|
|
371
|
+
steps_completed=current_state["steps_completed"],
|
|
372
|
+
)
|
|
373
|
+
else:
|
|
374
|
+
history = AnalysisHistory()
|
|
375
|
+
|
|
376
|
+
# Generate candidate recommendations
|
|
377
|
+
candidates = []
|
|
378
|
+
|
|
379
|
+
# Try each recommendation generator
|
|
380
|
+
generators = [
|
|
381
|
+
_recommend_characterization,
|
|
382
|
+
_recommend_quality_assessment,
|
|
383
|
+
_recommend_anomaly_check,
|
|
384
|
+
_recommend_protocol_decode,
|
|
385
|
+
_recommend_spectral_analysis,
|
|
386
|
+
]
|
|
387
|
+
|
|
388
|
+
for generator in generators:
|
|
389
|
+
rec = generator(trace, current_state, history)
|
|
390
|
+
if rec is not None:
|
|
391
|
+
candidates.append(rec)
|
|
392
|
+
|
|
393
|
+
# If no specific recommendations, provide escape hatch
|
|
394
|
+
if not candidates:
|
|
395
|
+
candidates.append(
|
|
396
|
+
Recommendation(
|
|
397
|
+
id="basic_characterization",
|
|
398
|
+
title="Start with basic signal characterization",
|
|
399
|
+
explanation="Not sure where to start? Begin with automatic signal characterization to identify the signal type.",
|
|
400
|
+
urgency=0.50,
|
|
401
|
+
ease=0.95,
|
|
402
|
+
impact=0.85,
|
|
403
|
+
priority=_calculate_priority(0.50, 0.95, 0.85),
|
|
404
|
+
rationale="Default starting point when no analysis has been done",
|
|
405
|
+
result_key="characterization",
|
|
406
|
+
impact_description="Provides foundation for further analysis",
|
|
407
|
+
)
|
|
408
|
+
)
|
|
409
|
+
|
|
410
|
+
# Sort by priority (descending)
|
|
411
|
+
candidates.sort(key=lambda r: r.priority, reverse=True)
|
|
412
|
+
|
|
413
|
+
# Limit to max_suggestions
|
|
414
|
+
max_suggestions = max(2, min(5, max_suggestions))
|
|
415
|
+
recommendations = candidates[:max_suggestions]
|
|
416
|
+
|
|
417
|
+
# Remove rationale if not requested
|
|
418
|
+
if not include_rationale:
|
|
419
|
+
for rec in recommendations:
|
|
420
|
+
rec.rationale = ""
|
|
421
|
+
|
|
422
|
+
return recommendations
|
|
423
|
+
|
|
424
|
+
|
|
425
|
+
__all__ = [
|
|
426
|
+
"AnalysisHistory",
|
|
427
|
+
"Recommendation",
|
|
428
|
+
"suggest_next_steps",
|
|
429
|
+
]
|