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,507 @@
|
|
|
1
|
+
"""Automatic executive report generation.
|
|
2
|
+
|
|
3
|
+
This module provides one-click generation of comprehensive analysis reports
|
|
4
|
+
in multiple formats (PDF, HTML, Markdown).
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
Example:
|
|
8
|
+
>>> from oscura.reporting import generate_report
|
|
9
|
+
>>> trace = load("capture.wfm")
|
|
10
|
+
>>> report = generate_report(trace)
|
|
11
|
+
>>> report.save_pdf("analysis_report.pdf")
|
|
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 pathlib import Path
|
|
22
|
+
from typing import TYPE_CHECKING, Any
|
|
23
|
+
|
|
24
|
+
import numpy as np
|
|
25
|
+
|
|
26
|
+
if TYPE_CHECKING:
|
|
27
|
+
from oscura.core.types import WaveformTrace
|
|
28
|
+
|
|
29
|
+
|
|
30
|
+
@dataclass
|
|
31
|
+
class ReportMetadata:
|
|
32
|
+
"""Report metadata.
|
|
33
|
+
|
|
34
|
+
Attributes:
|
|
35
|
+
title: Report title.
|
|
36
|
+
author: Report author.
|
|
37
|
+
date: Report date.
|
|
38
|
+
project: Project name.
|
|
39
|
+
tags: List of tags.
|
|
40
|
+
"""
|
|
41
|
+
|
|
42
|
+
title: str = "Signal Analysis Report"
|
|
43
|
+
author: str = "Oscura"
|
|
44
|
+
date: str = field(default_factory=lambda: datetime.now().strftime("%Y-%m-%d"))
|
|
45
|
+
project: str | None = None
|
|
46
|
+
tags: list[str] = field(default_factory=list)
|
|
47
|
+
|
|
48
|
+
|
|
49
|
+
@dataclass
|
|
50
|
+
class Report:
|
|
51
|
+
"""Executive analysis report.
|
|
52
|
+
|
|
53
|
+
Attributes:
|
|
54
|
+
sections: List of section names included.
|
|
55
|
+
plots: List of plot types included.
|
|
56
|
+
page_count: Estimated page count.
|
|
57
|
+
metadata: Report metadata.
|
|
58
|
+
content: Dictionary of section content.
|
|
59
|
+
output_path: Path to saved report.
|
|
60
|
+
file_size_mb: File size in MB (if saved).
|
|
61
|
+
"""
|
|
62
|
+
|
|
63
|
+
sections: list[str] = field(default_factory=list)
|
|
64
|
+
plots: list[str] = field(default_factory=list)
|
|
65
|
+
page_count: int = 0
|
|
66
|
+
metadata: ReportMetadata = field(default_factory=ReportMetadata)
|
|
67
|
+
content: dict[str, str] = field(default_factory=dict)
|
|
68
|
+
output_path: str | None = None
|
|
69
|
+
file_size_mb: float = 0.0
|
|
70
|
+
|
|
71
|
+
def save_pdf(self, path: str) -> None:
|
|
72
|
+
"""Save report as PDF.
|
|
73
|
+
|
|
74
|
+
Args:
|
|
75
|
+
path: Output file path.
|
|
76
|
+
|
|
77
|
+
Note:
|
|
78
|
+
This is a placeholder implementation. Full PDF generation
|
|
79
|
+
would require reportlab or similar library.
|
|
80
|
+
"""
|
|
81
|
+
self.output_path = path
|
|
82
|
+
# Placeholder: would generate actual PDF here
|
|
83
|
+
with open(path, "w") as f:
|
|
84
|
+
f.write("PDF Report - Placeholder\n")
|
|
85
|
+
f.write(f"Title: {self.metadata.title}\n")
|
|
86
|
+
f.write(f"Date: {self.metadata.date}\n\n")
|
|
87
|
+
|
|
88
|
+
for section in self.sections:
|
|
89
|
+
if section in self.content:
|
|
90
|
+
f.write(f"\n{section.upper()}\n")
|
|
91
|
+
f.write("=" * 60 + "\n")
|
|
92
|
+
f.write(self.content[section] + "\n")
|
|
93
|
+
|
|
94
|
+
# Estimate file size
|
|
95
|
+
self.file_size_mb = Path(path).stat().st_size / (1024 * 1024)
|
|
96
|
+
|
|
97
|
+
def save_html(self, path: str) -> None:
|
|
98
|
+
"""Save report as HTML.
|
|
99
|
+
|
|
100
|
+
Args:
|
|
101
|
+
path: Output file path.
|
|
102
|
+
"""
|
|
103
|
+
self.output_path = path
|
|
104
|
+
|
|
105
|
+
html_content = f"""<!DOCTYPE html>
|
|
106
|
+
<html>
|
|
107
|
+
<head>
|
|
108
|
+
<meta charset="UTF-8">
|
|
109
|
+
<title>{self.metadata.title}</title>
|
|
110
|
+
<style>
|
|
111
|
+
body {{
|
|
112
|
+
font-family: Arial, sans-serif;
|
|
113
|
+
max-width: 900px;
|
|
114
|
+
margin: 0 auto;
|
|
115
|
+
padding: 20px;
|
|
116
|
+
line-height: 1.6;
|
|
117
|
+
}}
|
|
118
|
+
h1 {{
|
|
119
|
+
color: #2c3e50;
|
|
120
|
+
border-bottom: 3px solid #3498db;
|
|
121
|
+
padding-bottom: 10px;
|
|
122
|
+
}}
|
|
123
|
+
h2 {{
|
|
124
|
+
color: #34495e;
|
|
125
|
+
margin-top: 30px;
|
|
126
|
+
border-bottom: 2px solid #ecf0f1;
|
|
127
|
+
padding-bottom: 5px;
|
|
128
|
+
}}
|
|
129
|
+
.metadata {{
|
|
130
|
+
background-color: #ecf0f1;
|
|
131
|
+
padding: 15px;
|
|
132
|
+
border-radius: 5px;
|
|
133
|
+
margin-bottom: 20px;
|
|
134
|
+
}}
|
|
135
|
+
.section {{
|
|
136
|
+
margin-bottom: 30px;
|
|
137
|
+
}}
|
|
138
|
+
.critical {{
|
|
139
|
+
color: #e74c3c;
|
|
140
|
+
font-weight: bold;
|
|
141
|
+
}}
|
|
142
|
+
.warning {{
|
|
143
|
+
color: #f39c12;
|
|
144
|
+
font-weight: bold;
|
|
145
|
+
}}
|
|
146
|
+
.info {{
|
|
147
|
+
color: #3498db;
|
|
148
|
+
}}
|
|
149
|
+
</style>
|
|
150
|
+
</head>
|
|
151
|
+
<body>
|
|
152
|
+
<h1>{self.metadata.title}</h1>
|
|
153
|
+
|
|
154
|
+
<div class="metadata">
|
|
155
|
+
<p><strong>Date:</strong> {self.metadata.date}</p>
|
|
156
|
+
<p><strong>Author:</strong> {self.metadata.author}</p>
|
|
157
|
+
"""
|
|
158
|
+
if self.metadata.project:
|
|
159
|
+
html_content += f" <p><strong>Project:</strong> {self.metadata.project}</p>\n"
|
|
160
|
+
|
|
161
|
+
if self.metadata.tags:
|
|
162
|
+
html_content += (
|
|
163
|
+
f" <p><strong>Tags:</strong> {', '.join(self.metadata.tags)}</p>\n"
|
|
164
|
+
)
|
|
165
|
+
|
|
166
|
+
html_content += " </div>\n\n"
|
|
167
|
+
|
|
168
|
+
for section in self.sections:
|
|
169
|
+
if section in self.content:
|
|
170
|
+
section_title = section.replace("_", " ").title()
|
|
171
|
+
html_content += f""" <div class="section">
|
|
172
|
+
<h2>{section_title}</h2>
|
|
173
|
+
<p>{self.content[section]}</p>
|
|
174
|
+
</div>
|
|
175
|
+
"""
|
|
176
|
+
html_content += """</body>
|
|
177
|
+
</html>"""
|
|
178
|
+
with open(path, "w") as f:
|
|
179
|
+
f.write(html_content)
|
|
180
|
+
|
|
181
|
+
self.file_size_mb = Path(path).stat().st_size / (1024 * 1024)
|
|
182
|
+
|
|
183
|
+
def save_markdown(self, path: str) -> None:
|
|
184
|
+
"""Save report as Markdown.
|
|
185
|
+
|
|
186
|
+
Args:
|
|
187
|
+
path: Output file path.
|
|
188
|
+
"""
|
|
189
|
+
self.output_path = path
|
|
190
|
+
|
|
191
|
+
md_content = f"# {self.metadata.title}\n\n"
|
|
192
|
+
md_content += f"**Date:** {self.metadata.date} \n"
|
|
193
|
+
md_content += f"**Author:** {self.metadata.author} \n"
|
|
194
|
+
|
|
195
|
+
if self.metadata.project:
|
|
196
|
+
md_content += f"**Project:** {self.metadata.project} \n"
|
|
197
|
+
|
|
198
|
+
if self.metadata.tags:
|
|
199
|
+
md_content += f"**Tags:** {', '.join(self.metadata.tags)} \n"
|
|
200
|
+
|
|
201
|
+
md_content += "\n---\n\n"
|
|
202
|
+
|
|
203
|
+
for section in self.sections:
|
|
204
|
+
if section in self.content:
|
|
205
|
+
section_title = section.replace("_", " ").title()
|
|
206
|
+
md_content += f"## {section_title}\n\n"
|
|
207
|
+
md_content += self.content[section] + "\n\n"
|
|
208
|
+
|
|
209
|
+
with open(path, "w") as f:
|
|
210
|
+
f.write(md_content)
|
|
211
|
+
|
|
212
|
+
self.file_size_mb = Path(path).stat().st_size / (1024 * 1024)
|
|
213
|
+
|
|
214
|
+
def add_section(
|
|
215
|
+
self,
|
|
216
|
+
title: str,
|
|
217
|
+
content: str,
|
|
218
|
+
position: int | None = None,
|
|
219
|
+
) -> None:
|
|
220
|
+
"""Add custom section to report.
|
|
221
|
+
|
|
222
|
+
Args:
|
|
223
|
+
title: Section title.
|
|
224
|
+
content: Section content.
|
|
225
|
+
position: Insert position (None = append).
|
|
226
|
+
"""
|
|
227
|
+
section_key = title.lower().replace(" ", "_")
|
|
228
|
+
|
|
229
|
+
if position is None:
|
|
230
|
+
self.sections.append(section_key)
|
|
231
|
+
else:
|
|
232
|
+
self.sections.insert(position, section_key)
|
|
233
|
+
|
|
234
|
+
self.content[section_key] = content
|
|
235
|
+
|
|
236
|
+
def include_plots(self, plot_types: list[str]) -> None:
|
|
237
|
+
"""Select which plots to include in report.
|
|
238
|
+
|
|
239
|
+
Args:
|
|
240
|
+
plot_types: List of plot type names.
|
|
241
|
+
"""
|
|
242
|
+
self.plots = plot_types
|
|
243
|
+
|
|
244
|
+
def set_metadata(
|
|
245
|
+
self,
|
|
246
|
+
title: str | None = None,
|
|
247
|
+
author: str | None = None,
|
|
248
|
+
date: str | None = None,
|
|
249
|
+
project: str | None = None,
|
|
250
|
+
tags: list[str] | None = None,
|
|
251
|
+
) -> None:
|
|
252
|
+
"""Set report metadata.
|
|
253
|
+
|
|
254
|
+
Args:
|
|
255
|
+
title: Report title.
|
|
256
|
+
author: Report author.
|
|
257
|
+
date: Report date.
|
|
258
|
+
project: Project name.
|
|
259
|
+
tags: List of tags.
|
|
260
|
+
"""
|
|
261
|
+
if title:
|
|
262
|
+
self.metadata.title = title
|
|
263
|
+
if author:
|
|
264
|
+
self.metadata.author = author
|
|
265
|
+
if date:
|
|
266
|
+
self.metadata.date = date
|
|
267
|
+
if project:
|
|
268
|
+
self.metadata.project = project
|
|
269
|
+
if tags:
|
|
270
|
+
self.metadata.tags = tags
|
|
271
|
+
|
|
272
|
+
|
|
273
|
+
def _generate_executive_summary(trace: WaveformTrace, context: dict) -> str: # type: ignore[type-arg]
|
|
274
|
+
"""Generate executive summary section.
|
|
275
|
+
|
|
276
|
+
Args:
|
|
277
|
+
trace: Waveform to analyze.
|
|
278
|
+
context: Analysis context.
|
|
279
|
+
|
|
280
|
+
Returns:
|
|
281
|
+
Executive summary text (≤200 words).
|
|
282
|
+
"""
|
|
283
|
+
sample_rate = trace.metadata.sample_rate
|
|
284
|
+
duration_ms = len(trace.data) / sample_rate * 1000
|
|
285
|
+
v_min = float(np.min(trace.data))
|
|
286
|
+
v_max = float(np.max(trace.data))
|
|
287
|
+
|
|
288
|
+
summary = "This report presents analysis of a signal capture taken at "
|
|
289
|
+
summary += f"{sample_rate / 1e6:.1f} MS/s sample rate over {duration_ms:.2f} milliseconds. "
|
|
290
|
+
summary += f"The signal ranges from {v_min:.3f}V to {v_max:.3f}V. "
|
|
291
|
+
|
|
292
|
+
# Add context-specific information
|
|
293
|
+
if "characterization" in context:
|
|
294
|
+
char = context["characterization"]
|
|
295
|
+
if hasattr(char, "signal_type"):
|
|
296
|
+
summary += f"The signal was identified as {char.signal_type}. "
|
|
297
|
+
|
|
298
|
+
if "quality" in context:
|
|
299
|
+
quality = context["quality"]
|
|
300
|
+
if hasattr(quality, "status"):
|
|
301
|
+
summary += f"Data quality assessment: {quality.status}. "
|
|
302
|
+
|
|
303
|
+
summary += "Detailed findings and recommendations are provided in the sections below."
|
|
304
|
+
|
|
305
|
+
return summary
|
|
306
|
+
|
|
307
|
+
|
|
308
|
+
def _generate_key_findings(trace: WaveformTrace, context: dict) -> str: # type: ignore[type-arg]
|
|
309
|
+
"""Generate key findings section.
|
|
310
|
+
|
|
311
|
+
Args:
|
|
312
|
+
trace: Waveform to analyze.
|
|
313
|
+
context: Analysis context.
|
|
314
|
+
|
|
315
|
+
Returns:
|
|
316
|
+
Key findings text.
|
|
317
|
+
"""
|
|
318
|
+
findings = []
|
|
319
|
+
|
|
320
|
+
# Basic signal characteristics
|
|
321
|
+
v_range = np.ptp(trace.data)
|
|
322
|
+
findings.append(f"Signal swing: {v_range:.3f}V")
|
|
323
|
+
|
|
324
|
+
# Add context-specific findings
|
|
325
|
+
if "anomalies" in context:
|
|
326
|
+
anomalies = context["anomalies"]
|
|
327
|
+
if hasattr(anomalies, "__len__"):
|
|
328
|
+
findings.append(f"Detected {len(anomalies)} anomalies in signal")
|
|
329
|
+
|
|
330
|
+
if "decode" in context:
|
|
331
|
+
decode = context["decode"]
|
|
332
|
+
if hasattr(decode, "data") and hasattr(decode.data, "__len__"):
|
|
333
|
+
findings.append(f"Successfully decoded {len(decode.data)} bytes")
|
|
334
|
+
|
|
335
|
+
# Format findings
|
|
336
|
+
findings_text = "Key findings from signal analysis:\n\n"
|
|
337
|
+
for i, finding in enumerate(findings, 1):
|
|
338
|
+
findings_text += f"{i}. {finding}\n"
|
|
339
|
+
|
|
340
|
+
return findings_text
|
|
341
|
+
|
|
342
|
+
|
|
343
|
+
def _generate_methodology(trace: WaveformTrace, context: dict[str, Any]) -> str:
|
|
344
|
+
"""Generate methodology section.
|
|
345
|
+
|
|
346
|
+
Args:
|
|
347
|
+
trace: Waveform to analyze.
|
|
348
|
+
context: Analysis context.
|
|
349
|
+
|
|
350
|
+
Returns:
|
|
351
|
+
Methodology description.
|
|
352
|
+
"""
|
|
353
|
+
methodology = "Analysis methodology:\n\n"
|
|
354
|
+
|
|
355
|
+
methodology += "Signal characterization: Automated signal type detection using "
|
|
356
|
+
methodology += "statistical analysis and pattern recognition algorithms.\n\n"
|
|
357
|
+
|
|
358
|
+
methodology += "Quality assessment: Signal-to-noise ratio, clipping detection, "
|
|
359
|
+
methodology += "and sample rate validation.\n\n"
|
|
360
|
+
|
|
361
|
+
if "anomalies" in context:
|
|
362
|
+
methodology += "Anomaly detection: Automated detection of glitches, dropouts, "
|
|
363
|
+
methodology += "noise spikes, and timing violations.\n\n"
|
|
364
|
+
|
|
365
|
+
if "decode" in context:
|
|
366
|
+
methodology += "Protocol decode: Automatic parameter detection and "
|
|
367
|
+
methodology += "frame extraction with confidence scoring.\n\n"
|
|
368
|
+
|
|
369
|
+
return methodology
|
|
370
|
+
|
|
371
|
+
|
|
372
|
+
def _generate_detailed_results(trace: WaveformTrace, context: dict[str, Any]) -> str:
|
|
373
|
+
"""Generate detailed results section.
|
|
374
|
+
|
|
375
|
+
Args:
|
|
376
|
+
trace: Waveform to analyze.
|
|
377
|
+
context: Analysis context.
|
|
378
|
+
|
|
379
|
+
Returns:
|
|
380
|
+
Detailed results text.
|
|
381
|
+
"""
|
|
382
|
+
results = "Detailed measurement results:\n\n"
|
|
383
|
+
|
|
384
|
+
# Basic statistics
|
|
385
|
+
data = trace.data.astype(np.float64)
|
|
386
|
+
results += f"Minimum voltage: {np.min(data):.6f}V\n"
|
|
387
|
+
results += f"Maximum voltage: {np.max(data):.6f}V\n"
|
|
388
|
+
results += f"Mean voltage: {np.mean(data):.6f}V\n"
|
|
389
|
+
results += f"Standard deviation: {np.std(data):.6f}V\n"
|
|
390
|
+
results += f"Peak-to-peak: {np.ptp(data):.6f}V\n\n"
|
|
391
|
+
|
|
392
|
+
# Sample info
|
|
393
|
+
results += f"Sample count: {len(data):,}\n"
|
|
394
|
+
results += f"Sample rate: {trace.metadata.sample_rate / 1e6:.3f} MS/s\n"
|
|
395
|
+
results += f"Duration: {len(data) / trace.metadata.sample_rate * 1000:.3f} ms\n\n"
|
|
396
|
+
|
|
397
|
+
return results
|
|
398
|
+
|
|
399
|
+
|
|
400
|
+
def generate_report(
|
|
401
|
+
trace: WaveformTrace,
|
|
402
|
+
*,
|
|
403
|
+
format: str = "pdf",
|
|
404
|
+
template: str | None = None,
|
|
405
|
+
context: dict[str, Any] | None = None,
|
|
406
|
+
options: dict[str, Any] | None = None,
|
|
407
|
+
) -> Report:
|
|
408
|
+
"""Generate comprehensive executive analysis report.
|
|
409
|
+
|
|
410
|
+
Creates a professional report with executive summary, key findings,
|
|
411
|
+
methodology, and detailed results. Auto-includes relevant plots.
|
|
412
|
+
|
|
413
|
+
Args:
|
|
414
|
+
trace: Waveform to analyze.
|
|
415
|
+
format: Output format ("pdf", "html", "markdown").
|
|
416
|
+
template: Optional template file path.
|
|
417
|
+
context: Pre-computed analysis results (characterization, anomalies, etc.).
|
|
418
|
+
options: Report customization options:
|
|
419
|
+
- select_sections: List of sections to include
|
|
420
|
+
- custom_header: Custom header text
|
|
421
|
+
- custom_footer: Custom footer text
|
|
422
|
+
- page_orientation: "portrait" or "landscape"
|
|
423
|
+
- include_raw_data: Include raw data table
|
|
424
|
+
- plot_dpi: Plot resolution (default 300)
|
|
425
|
+
|
|
426
|
+
Returns:
|
|
427
|
+
Report object with content and save methods.
|
|
428
|
+
|
|
429
|
+
Example:
|
|
430
|
+
>>> report = generate_report(trace)
|
|
431
|
+
>>> report.save_pdf("analysis.pdf")
|
|
432
|
+
>>> print(f"Generated {report.page_count} page report")
|
|
433
|
+
|
|
434
|
+
References:
|
|
435
|
+
DISC-005: Automatic Executive Report
|
|
436
|
+
"""
|
|
437
|
+
context = context or {}
|
|
438
|
+
options = options or {}
|
|
439
|
+
|
|
440
|
+
# Determine sections to include
|
|
441
|
+
default_sections = [
|
|
442
|
+
"executive_summary",
|
|
443
|
+
"key_findings",
|
|
444
|
+
"methodology",
|
|
445
|
+
"detailed_results",
|
|
446
|
+
]
|
|
447
|
+
|
|
448
|
+
sections = options.get("select_sections", default_sections)
|
|
449
|
+
|
|
450
|
+
# Generate content for each section
|
|
451
|
+
content = {}
|
|
452
|
+
|
|
453
|
+
if "executive_summary" in sections or "summary" in sections:
|
|
454
|
+
content["executive_summary"] = _generate_executive_summary(trace, context)
|
|
455
|
+
|
|
456
|
+
if "key_findings" in sections or "findings" in sections:
|
|
457
|
+
content["key_findings"] = _generate_key_findings(trace, context)
|
|
458
|
+
|
|
459
|
+
if "methodology" in sections:
|
|
460
|
+
content["methodology"] = _generate_methodology(trace, context)
|
|
461
|
+
|
|
462
|
+
if "detailed_results" in sections or "results" in sections:
|
|
463
|
+
content["detailed_results"] = _generate_detailed_results(trace, context)
|
|
464
|
+
|
|
465
|
+
if "recommendations" in sections:
|
|
466
|
+
content["recommendations"] = (
|
|
467
|
+
"Recommendations based on analysis:\n\n"
|
|
468
|
+
"1. Signal quality is acceptable for analysis\n"
|
|
469
|
+
"2. Consider additional captures for verification\n"
|
|
470
|
+
"3. Review anomalies if present\n"
|
|
471
|
+
)
|
|
472
|
+
|
|
473
|
+
# Determine plot types to include
|
|
474
|
+
plot_types = options.get("plot_types", [])
|
|
475
|
+
if not plot_types:
|
|
476
|
+
# Auto-select based on signal characteristics
|
|
477
|
+
plot_types = ["time_domain_waveform"]
|
|
478
|
+
|
|
479
|
+
# Add spectral if signal looks periodic
|
|
480
|
+
if len(trace.data) > 100:
|
|
481
|
+
plot_types.append("fft_spectrum")
|
|
482
|
+
|
|
483
|
+
# Estimate page count (rough estimate)
|
|
484
|
+
page_count = 1 # Title page
|
|
485
|
+
page_count += len(sections) # One page per section
|
|
486
|
+
page_count += (len(plot_types) + 1) // 2 # 2 plots per page
|
|
487
|
+
|
|
488
|
+
# Create report object
|
|
489
|
+
report = Report(
|
|
490
|
+
sections=list(sections),
|
|
491
|
+
plots=plot_types,
|
|
492
|
+
page_count=page_count,
|
|
493
|
+
content=content,
|
|
494
|
+
)
|
|
495
|
+
|
|
496
|
+
# Set custom metadata if provided
|
|
497
|
+
if "custom_header" in options:
|
|
498
|
+
report.metadata.title = options["custom_header"]
|
|
499
|
+
|
|
500
|
+
return report
|
|
501
|
+
|
|
502
|
+
|
|
503
|
+
__all__ = [
|
|
504
|
+
"Report",
|
|
505
|
+
"ReportMetadata",
|
|
506
|
+
"generate_report",
|
|
507
|
+
]
|