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,448 @@
|
|
|
1
|
+
"""Comprehensive analysis report system main entry point.
|
|
2
|
+
|
|
3
|
+
This module provides the primary `analyze()` function for running
|
|
4
|
+
comprehensive analysis on any supported input data type.
|
|
5
|
+
"""
|
|
6
|
+
|
|
7
|
+
from __future__ import annotations
|
|
8
|
+
|
|
9
|
+
import logging
|
|
10
|
+
import time
|
|
11
|
+
from collections.abc import Callable
|
|
12
|
+
from datetime import datetime
|
|
13
|
+
from pathlib import Path
|
|
14
|
+
from typing import TYPE_CHECKING, Any
|
|
15
|
+
|
|
16
|
+
from oscura.reporting.config import (
|
|
17
|
+
AnalysisConfig,
|
|
18
|
+
AnalysisDomain,
|
|
19
|
+
AnalysisError,
|
|
20
|
+
AnalysisResult,
|
|
21
|
+
InputType,
|
|
22
|
+
ProgressInfo,
|
|
23
|
+
get_available_analyses,
|
|
24
|
+
)
|
|
25
|
+
from oscura.reporting.output import OutputManager
|
|
26
|
+
|
|
27
|
+
if TYPE_CHECKING:
|
|
28
|
+
from oscura.core.types import Trace
|
|
29
|
+
|
|
30
|
+
logger = logging.getLogger(__name__)
|
|
31
|
+
|
|
32
|
+
|
|
33
|
+
class UnsupportedFormatError(Exception):
|
|
34
|
+
"""Raised when input file format is not recognized."""
|
|
35
|
+
|
|
36
|
+
|
|
37
|
+
def analyze(
|
|
38
|
+
input_path: str | Path | None = None,
|
|
39
|
+
data: Trace | bytes | list[Any] | None = None,
|
|
40
|
+
*,
|
|
41
|
+
output_dir: str | Path | None = None,
|
|
42
|
+
config: AnalysisConfig | None = None,
|
|
43
|
+
progress_callback: Callable[[ProgressInfo], None] | None = None,
|
|
44
|
+
) -> AnalysisResult:
|
|
45
|
+
"""Run comprehensive analysis on data.
|
|
46
|
+
|
|
47
|
+
Provide EITHER input_path (file) OR data (in-memory), not both.
|
|
48
|
+
|
|
49
|
+
Args:
|
|
50
|
+
input_path: Path to input data file (any supported format).
|
|
51
|
+
data: In-memory data (Trace, bytes, list of packets).
|
|
52
|
+
output_dir: Base directory for output. Default: input file's directory
|
|
53
|
+
or current directory for in-memory data.
|
|
54
|
+
config: Analysis configuration. Default: analyze all applicable domains.
|
|
55
|
+
progress_callback: Called with progress updates during analysis.
|
|
56
|
+
|
|
57
|
+
Returns:
|
|
58
|
+
AnalysisResult with paths to all outputs and summary statistics.
|
|
59
|
+
|
|
60
|
+
Raises:
|
|
61
|
+
FileNotFoundError: If the input file does not exist.
|
|
62
|
+
ValueError: If neither or both input_path and data are provided.
|
|
63
|
+
|
|
64
|
+
Examples:
|
|
65
|
+
# From file
|
|
66
|
+
result = analyze("capture.wfm")
|
|
67
|
+
print(result.output_dir) # 20260101_120000_capture_analysis/
|
|
68
|
+
|
|
69
|
+
# From in-memory data
|
|
70
|
+
result = analyze(data=my_waveform_trace, output_dir="/reports")
|
|
71
|
+
|
|
72
|
+
# With configuration
|
|
73
|
+
config = AnalysisConfig(domains=[AnalysisDomain.SPECTRAL])
|
|
74
|
+
result = analyze("capture.wfm", config=config)
|
|
75
|
+
|
|
76
|
+
# With progress callback
|
|
77
|
+
def on_progress(info):
|
|
78
|
+
print(f"{info.domain}: {info.percent}%")
|
|
79
|
+
result = analyze("capture.wfm", progress_callback=on_progress)
|
|
80
|
+
"""
|
|
81
|
+
# Validate inputs
|
|
82
|
+
if input_path is None and data is None:
|
|
83
|
+
raise ValueError("Either input_path or data must be provided")
|
|
84
|
+
if input_path is not None and data is not None:
|
|
85
|
+
raise ValueError("Provide input_path OR data, not both")
|
|
86
|
+
|
|
87
|
+
# Use default config if not provided
|
|
88
|
+
if config is None:
|
|
89
|
+
config = AnalysisConfig()
|
|
90
|
+
|
|
91
|
+
# Track timing
|
|
92
|
+
start_time = time.time()
|
|
93
|
+
|
|
94
|
+
# Determine input name and type
|
|
95
|
+
if input_path is not None:
|
|
96
|
+
input_path = Path(input_path)
|
|
97
|
+
if not input_path.exists():
|
|
98
|
+
raise FileNotFoundError(f"Input file not found: {input_path}")
|
|
99
|
+
input_name = input_path.stem
|
|
100
|
+
input_type = _detect_input_type_from_file(input_path)
|
|
101
|
+
loaded_data = _load_input_file(input_path, input_type)
|
|
102
|
+
else:
|
|
103
|
+
input_name = "memory_data"
|
|
104
|
+
input_type = _detect_input_type_from_data(data)
|
|
105
|
+
loaded_data = data
|
|
106
|
+
|
|
107
|
+
# Determine output directory
|
|
108
|
+
if output_dir is None:
|
|
109
|
+
if input_path is not None:
|
|
110
|
+
base_dir = input_path.parent
|
|
111
|
+
else:
|
|
112
|
+
base_dir = Path.cwd()
|
|
113
|
+
else:
|
|
114
|
+
base_dir = Path(output_dir)
|
|
115
|
+
|
|
116
|
+
# Create output manager with timestamp
|
|
117
|
+
timestamp = datetime.now()
|
|
118
|
+
output_manager = OutputManager(base_dir, input_name, timestamp)
|
|
119
|
+
output_manager.create()
|
|
120
|
+
|
|
121
|
+
# Report progress: starting
|
|
122
|
+
_report_progress(
|
|
123
|
+
progress_callback,
|
|
124
|
+
phase="initializing",
|
|
125
|
+
domain=None,
|
|
126
|
+
function=None,
|
|
127
|
+
percent=0.0,
|
|
128
|
+
message="Initializing analysis",
|
|
129
|
+
elapsed=time.time() - start_time,
|
|
130
|
+
)
|
|
131
|
+
|
|
132
|
+
# Determine applicable domains
|
|
133
|
+
applicable_domains = get_available_analyses(input_type)
|
|
134
|
+
enabled_domains = [d for d in applicable_domains if config.is_domain_enabled(d)]
|
|
135
|
+
|
|
136
|
+
logger.info(f"Running analysis on {input_name} ({input_type.value})")
|
|
137
|
+
logger.info(f"Enabled domains: {[d.value for d in enabled_domains]}")
|
|
138
|
+
|
|
139
|
+
# Execute analysis engine
|
|
140
|
+
from oscura.reporting.engine import AnalysisEngine
|
|
141
|
+
|
|
142
|
+
engine = AnalysisEngine(config)
|
|
143
|
+
engine_result = engine.run(
|
|
144
|
+
input_path=input_path,
|
|
145
|
+
data=loaded_data,
|
|
146
|
+
progress_callback=progress_callback,
|
|
147
|
+
)
|
|
148
|
+
|
|
149
|
+
# Generate plots
|
|
150
|
+
plot_paths: list[Path] = []
|
|
151
|
+
if config.generate_plots:
|
|
152
|
+
_report_progress(
|
|
153
|
+
progress_callback,
|
|
154
|
+
phase="plotting",
|
|
155
|
+
domain=None,
|
|
156
|
+
function=None,
|
|
157
|
+
percent=70.0,
|
|
158
|
+
message="Generating visualizations",
|
|
159
|
+
elapsed=time.time() - start_time,
|
|
160
|
+
)
|
|
161
|
+
|
|
162
|
+
from oscura.reporting.plots import PlotGenerator
|
|
163
|
+
|
|
164
|
+
plot_gen = PlotGenerator(config)
|
|
165
|
+
for domain, results in engine_result["results"].items():
|
|
166
|
+
domain_plots = plot_gen.generate_plots(domain, results, output_manager)
|
|
167
|
+
plot_paths.extend(domain_plots)
|
|
168
|
+
|
|
169
|
+
# Save data outputs
|
|
170
|
+
_report_progress(
|
|
171
|
+
progress_callback,
|
|
172
|
+
phase="saving",
|
|
173
|
+
domain=None,
|
|
174
|
+
function=None,
|
|
175
|
+
percent=85.0,
|
|
176
|
+
message="Saving analysis results",
|
|
177
|
+
elapsed=time.time() - start_time,
|
|
178
|
+
)
|
|
179
|
+
|
|
180
|
+
# Save summary data
|
|
181
|
+
summary_data = {
|
|
182
|
+
"input": {
|
|
183
|
+
"name": input_name,
|
|
184
|
+
"type": input_type.value,
|
|
185
|
+
"path": str(input_path) if input_path else None,
|
|
186
|
+
},
|
|
187
|
+
"timestamp": timestamp.isoformat(),
|
|
188
|
+
"duration_seconds": time.time() - start_time,
|
|
189
|
+
"stats": engine_result["stats"],
|
|
190
|
+
"domains": {d.value: r for d, r in engine_result["results"].items()},
|
|
191
|
+
}
|
|
192
|
+
|
|
193
|
+
summary_json = output_manager.save_json("summary", summary_data)
|
|
194
|
+
summary_yaml = None
|
|
195
|
+
if "yaml" in config.output_formats:
|
|
196
|
+
summary_yaml = output_manager.save_yaml("summary", summary_data)
|
|
197
|
+
|
|
198
|
+
# Save metadata
|
|
199
|
+
metadata = {
|
|
200
|
+
"oscura_version": _get_version(),
|
|
201
|
+
"analysis_version": "2.0",
|
|
202
|
+
"timestamp": timestamp.isoformat(),
|
|
203
|
+
"input_file": str(input_path) if input_path else None,
|
|
204
|
+
"input_type": input_type.value,
|
|
205
|
+
"duration_seconds": time.time() - start_time,
|
|
206
|
+
"total_analyses": engine_result["stats"]["total_analyses"],
|
|
207
|
+
"successful": engine_result["stats"]["successful_analyses"],
|
|
208
|
+
"failed": engine_result["stats"]["failed_analyses"],
|
|
209
|
+
"skipped": engine_result["stats"].get("skipped_analyses", 0),
|
|
210
|
+
}
|
|
211
|
+
metadata_json = output_manager.save_json("metadata", metadata)
|
|
212
|
+
|
|
213
|
+
# Save configuration
|
|
214
|
+
config_data = {
|
|
215
|
+
"domains": [d.value for d in enabled_domains],
|
|
216
|
+
"generate_plots": config.generate_plots,
|
|
217
|
+
"plot_format": config.plot_format,
|
|
218
|
+
"plot_dpi": config.plot_dpi,
|
|
219
|
+
"output_formats": config.output_formats,
|
|
220
|
+
"index_formats": config.index_formats,
|
|
221
|
+
}
|
|
222
|
+
config_yaml = output_manager.save_yaml("config", config_data)
|
|
223
|
+
|
|
224
|
+
# Save domain results
|
|
225
|
+
domain_dirs: dict[AnalysisDomain, Path] = {}
|
|
226
|
+
for domain, results in engine_result["results"].items():
|
|
227
|
+
domain_dir = output_manager.create_domain_dir(domain)
|
|
228
|
+
domain_dirs[domain] = domain_dir
|
|
229
|
+
output_manager.save_json("results", results, subdir=domain.value)
|
|
230
|
+
|
|
231
|
+
# Save errors if any
|
|
232
|
+
error_log: Path | None = None
|
|
233
|
+
errors: list[AnalysisError] = engine_result["errors"]
|
|
234
|
+
if errors:
|
|
235
|
+
error_list = [
|
|
236
|
+
{
|
|
237
|
+
"domain": e.domain.value,
|
|
238
|
+
"function": e.function,
|
|
239
|
+
"error_type": e.error_type,
|
|
240
|
+
"error_message": e.error_message,
|
|
241
|
+
"duration_ms": e.duration_ms,
|
|
242
|
+
}
|
|
243
|
+
for e in errors
|
|
244
|
+
]
|
|
245
|
+
error_data = {"errors": error_list, "count": len(error_list)}
|
|
246
|
+
error_log = output_manager.save_json("failed_analyses", error_data, subdir="errors")
|
|
247
|
+
|
|
248
|
+
# Build AnalysisResult for index generation
|
|
249
|
+
partial_result = AnalysisResult(
|
|
250
|
+
output_dir=output_manager.root,
|
|
251
|
+
index_html=None,
|
|
252
|
+
index_md=None,
|
|
253
|
+
index_pdf=None,
|
|
254
|
+
summary_json=summary_json,
|
|
255
|
+
summary_yaml=summary_yaml,
|
|
256
|
+
metadata_json=metadata_json,
|
|
257
|
+
config_yaml=config_yaml,
|
|
258
|
+
domain_dirs=domain_dirs,
|
|
259
|
+
plot_paths=plot_paths,
|
|
260
|
+
error_log=error_log,
|
|
261
|
+
input_file=str(input_path) if input_path else None,
|
|
262
|
+
input_type=input_type,
|
|
263
|
+
total_analyses=engine_result["stats"]["total_analyses"],
|
|
264
|
+
successful_analyses=engine_result["stats"]["successful_analyses"],
|
|
265
|
+
failed_analyses=engine_result["stats"]["failed_analyses"],
|
|
266
|
+
skipped_analyses=engine_result["stats"].get("skipped_analyses", 0),
|
|
267
|
+
duration_seconds=time.time() - start_time,
|
|
268
|
+
domain_summaries=engine_result["results"],
|
|
269
|
+
errors=errors,
|
|
270
|
+
)
|
|
271
|
+
|
|
272
|
+
# Generate index files
|
|
273
|
+
_report_progress(
|
|
274
|
+
progress_callback,
|
|
275
|
+
phase="indexing",
|
|
276
|
+
domain=None,
|
|
277
|
+
function=None,
|
|
278
|
+
percent=95.0,
|
|
279
|
+
message="Generating index files",
|
|
280
|
+
elapsed=time.time() - start_time,
|
|
281
|
+
)
|
|
282
|
+
|
|
283
|
+
from oscura.reporting.index import IndexGenerator
|
|
284
|
+
|
|
285
|
+
index_gen = IndexGenerator(output_manager)
|
|
286
|
+
index_paths = index_gen.generate(partial_result, config.index_formats)
|
|
287
|
+
|
|
288
|
+
# Complete result
|
|
289
|
+
result = AnalysisResult(
|
|
290
|
+
output_dir=output_manager.root,
|
|
291
|
+
index_html=index_paths.get("html"),
|
|
292
|
+
index_md=index_paths.get("md"),
|
|
293
|
+
index_pdf=index_paths.get("pdf"),
|
|
294
|
+
summary_json=summary_json,
|
|
295
|
+
summary_yaml=summary_yaml,
|
|
296
|
+
metadata_json=metadata_json,
|
|
297
|
+
config_yaml=config_yaml,
|
|
298
|
+
domain_dirs=domain_dirs,
|
|
299
|
+
plot_paths=plot_paths,
|
|
300
|
+
error_log=error_log,
|
|
301
|
+
input_file=str(input_path) if input_path else None,
|
|
302
|
+
input_type=input_type,
|
|
303
|
+
total_analyses=engine_result["stats"]["total_analyses"],
|
|
304
|
+
successful_analyses=engine_result["stats"]["successful_analyses"],
|
|
305
|
+
failed_analyses=engine_result["stats"]["failed_analyses"],
|
|
306
|
+
skipped_analyses=engine_result["stats"].get("skipped_analyses", 0),
|
|
307
|
+
duration_seconds=time.time() - start_time,
|
|
308
|
+
domain_summaries=engine_result["results"],
|
|
309
|
+
errors=errors,
|
|
310
|
+
)
|
|
311
|
+
|
|
312
|
+
# Report completion
|
|
313
|
+
_report_progress(
|
|
314
|
+
progress_callback,
|
|
315
|
+
phase="complete",
|
|
316
|
+
domain=None,
|
|
317
|
+
function=None,
|
|
318
|
+
percent=100.0,
|
|
319
|
+
message=f"Analysis complete: {result.successful_analyses}/{result.total_analyses} successful",
|
|
320
|
+
elapsed=time.time() - start_time,
|
|
321
|
+
)
|
|
322
|
+
|
|
323
|
+
logger.info(f"Analysis complete. Output: {result.output_dir}")
|
|
324
|
+
return result
|
|
325
|
+
|
|
326
|
+
|
|
327
|
+
def _detect_input_type_from_file(path: Path) -> InputType:
|
|
328
|
+
"""Detect input type from file extension."""
|
|
329
|
+
suffix = path.suffix.lower()
|
|
330
|
+
|
|
331
|
+
waveform_extensions = {".wfm", ".csv", ".npz", ".hdf5", ".h5", ".wav", ".tdms"}
|
|
332
|
+
digital_extensions = {".vcd", ".sr"}
|
|
333
|
+
binary_extensions = {".bin", ".raw"}
|
|
334
|
+
pcap_extensions = {".pcap", ".pcapng"}
|
|
335
|
+
sparams_extensions = {".s1p", ".s2p", ".s3p", ".s4p", ".s5p", ".s6p", ".s7p", ".s8p"}
|
|
336
|
+
|
|
337
|
+
if suffix in waveform_extensions:
|
|
338
|
+
return InputType.WAVEFORM
|
|
339
|
+
elif suffix in digital_extensions:
|
|
340
|
+
return InputType.DIGITAL
|
|
341
|
+
elif suffix in binary_extensions:
|
|
342
|
+
return InputType.BINARY
|
|
343
|
+
elif suffix in pcap_extensions:
|
|
344
|
+
return InputType.PCAP
|
|
345
|
+
elif suffix in sparams_extensions:
|
|
346
|
+
return InputType.SPARAMS
|
|
347
|
+
else:
|
|
348
|
+
raise UnsupportedFormatError(f"Unsupported file format: {suffix}")
|
|
349
|
+
|
|
350
|
+
|
|
351
|
+
def _detect_input_type_from_data(data: Any) -> InputType:
|
|
352
|
+
"""Detect input type from in-memory data."""
|
|
353
|
+
# Check for Trace object (time + voltage = waveform)
|
|
354
|
+
# Check this BEFORE SParameterData to avoid MagicMock false positives
|
|
355
|
+
if hasattr(data, "time") and hasattr(data, "voltage"):
|
|
356
|
+
# Verify these are not just mock/placeholder attributes
|
|
357
|
+
try:
|
|
358
|
+
_ = data.time
|
|
359
|
+
_ = data.voltage
|
|
360
|
+
return InputType.WAVEFORM
|
|
361
|
+
except (AttributeError, TypeError):
|
|
362
|
+
pass
|
|
363
|
+
|
|
364
|
+
# Check for SParameterData
|
|
365
|
+
if hasattr(data, "s_matrix") and hasattr(data, "frequencies"):
|
|
366
|
+
return InputType.SPARAMS
|
|
367
|
+
|
|
368
|
+
# Check for bytes
|
|
369
|
+
if isinstance(data, bytes | bytearray):
|
|
370
|
+
return InputType.BINARY
|
|
371
|
+
|
|
372
|
+
# Check for list of packets
|
|
373
|
+
if isinstance(data, list) and len(data) > 0:
|
|
374
|
+
first = data[0]
|
|
375
|
+
if hasattr(first, "timestamp") or isinstance(first, dict):
|
|
376
|
+
return InputType.PACKETS
|
|
377
|
+
|
|
378
|
+
# Default to waveform
|
|
379
|
+
return InputType.WAVEFORM
|
|
380
|
+
|
|
381
|
+
|
|
382
|
+
def _load_input_file(path: Path, input_type: InputType) -> Any:
|
|
383
|
+
"""Load input file based on type."""
|
|
384
|
+
try:
|
|
385
|
+
from oscura.loaders import load
|
|
386
|
+
|
|
387
|
+
if input_type == InputType.WAVEFORM:
|
|
388
|
+
return load(path)
|
|
389
|
+
elif input_type == InputType.DIGITAL:
|
|
390
|
+
# Use VCD/SR loader
|
|
391
|
+
from oscura.loaders.vcd import load_vcd
|
|
392
|
+
|
|
393
|
+
return load_vcd(path)
|
|
394
|
+
elif input_type == InputType.BINARY:
|
|
395
|
+
return path.read_bytes()
|
|
396
|
+
elif input_type == InputType.PCAP:
|
|
397
|
+
from oscura.loaders.pcap import load_pcap
|
|
398
|
+
|
|
399
|
+
return load_pcap(path)
|
|
400
|
+
elif input_type == InputType.SPARAMS:
|
|
401
|
+
from oscura.analyzers.signal_integrity.sparams import load_touchstone
|
|
402
|
+
|
|
403
|
+
return load_touchstone(path)
|
|
404
|
+
else:
|
|
405
|
+
return load(path)
|
|
406
|
+
except ImportError as e:
|
|
407
|
+
logger.warning(f"Loader not available: {e}")
|
|
408
|
+
# Fall back to raw bytes
|
|
409
|
+
return path.read_bytes()
|
|
410
|
+
|
|
411
|
+
|
|
412
|
+
def _report_progress(
|
|
413
|
+
callback: Callable[[ProgressInfo], None] | None,
|
|
414
|
+
phase: str,
|
|
415
|
+
domain: AnalysisDomain | None,
|
|
416
|
+
function: str | None,
|
|
417
|
+
percent: float,
|
|
418
|
+
message: str,
|
|
419
|
+
elapsed: float,
|
|
420
|
+
) -> None:
|
|
421
|
+
"""Report progress to callback if provided."""
|
|
422
|
+
if callback is not None:
|
|
423
|
+
info = ProgressInfo(
|
|
424
|
+
phase=phase,
|
|
425
|
+
domain=domain,
|
|
426
|
+
function=function,
|
|
427
|
+
percent=percent,
|
|
428
|
+
message=message,
|
|
429
|
+
elapsed_seconds=elapsed,
|
|
430
|
+
estimated_remaining_seconds=None,
|
|
431
|
+
)
|
|
432
|
+
callback(info)
|
|
433
|
+
|
|
434
|
+
|
|
435
|
+
def _get_version() -> str:
|
|
436
|
+
"""Get Oscura version."""
|
|
437
|
+
try:
|
|
438
|
+
from oscura import __version__
|
|
439
|
+
|
|
440
|
+
return __version__
|
|
441
|
+
except ImportError:
|
|
442
|
+
return "unknown"
|
|
443
|
+
|
|
444
|
+
|
|
445
|
+
__all__ = [
|
|
446
|
+
"UnsupportedFormatError",
|
|
447
|
+
"analyze",
|
|
448
|
+
]
|