oscura 0.0.1__py3-none-any.whl → 0.1.0__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- oscura/__init__.py +813 -8
- oscura/__main__.py +392 -0
- oscura/analyzers/__init__.py +37 -0
- oscura/analyzers/digital/__init__.py +177 -0
- oscura/analyzers/digital/bus.py +691 -0
- oscura/analyzers/digital/clock.py +805 -0
- oscura/analyzers/digital/correlation.py +720 -0
- oscura/analyzers/digital/edges.py +632 -0
- oscura/analyzers/digital/extraction.py +413 -0
- oscura/analyzers/digital/quality.py +878 -0
- oscura/analyzers/digital/signal_quality.py +877 -0
- oscura/analyzers/digital/thresholds.py +708 -0
- oscura/analyzers/digital/timing.py +1104 -0
- oscura/analyzers/eye/__init__.py +46 -0
- oscura/analyzers/eye/diagram.py +434 -0
- oscura/analyzers/eye/metrics.py +555 -0
- oscura/analyzers/jitter/__init__.py +83 -0
- oscura/analyzers/jitter/ber.py +333 -0
- oscura/analyzers/jitter/decomposition.py +759 -0
- oscura/analyzers/jitter/measurements.py +413 -0
- oscura/analyzers/jitter/spectrum.py +220 -0
- oscura/analyzers/measurements.py +40 -0
- oscura/analyzers/packet/__init__.py +171 -0
- oscura/analyzers/packet/daq.py +1077 -0
- oscura/analyzers/packet/metrics.py +437 -0
- oscura/analyzers/packet/parser.py +327 -0
- oscura/analyzers/packet/payload.py +2156 -0
- oscura/analyzers/packet/payload_analysis.py +1312 -0
- oscura/analyzers/packet/payload_extraction.py +236 -0
- oscura/analyzers/packet/payload_patterns.py +670 -0
- oscura/analyzers/packet/stream.py +359 -0
- oscura/analyzers/patterns/__init__.py +266 -0
- oscura/analyzers/patterns/clustering.py +1036 -0
- oscura/analyzers/patterns/discovery.py +539 -0
- oscura/analyzers/patterns/learning.py +797 -0
- oscura/analyzers/patterns/matching.py +1091 -0
- oscura/analyzers/patterns/periodic.py +650 -0
- oscura/analyzers/patterns/sequences.py +767 -0
- oscura/analyzers/power/__init__.py +116 -0
- oscura/analyzers/power/ac_power.py +391 -0
- oscura/analyzers/power/basic.py +383 -0
- oscura/analyzers/power/conduction.py +314 -0
- oscura/analyzers/power/efficiency.py +297 -0
- oscura/analyzers/power/ripple.py +356 -0
- oscura/analyzers/power/soa.py +372 -0
- oscura/analyzers/power/switching.py +479 -0
- oscura/analyzers/protocol/__init__.py +150 -0
- oscura/analyzers/protocols/__init__.py +150 -0
- oscura/analyzers/protocols/base.py +500 -0
- oscura/analyzers/protocols/can.py +620 -0
- oscura/analyzers/protocols/can_fd.py +448 -0
- oscura/analyzers/protocols/flexray.py +405 -0
- oscura/analyzers/protocols/hdlc.py +399 -0
- oscura/analyzers/protocols/i2c.py +368 -0
- oscura/analyzers/protocols/i2s.py +296 -0
- oscura/analyzers/protocols/jtag.py +393 -0
- oscura/analyzers/protocols/lin.py +445 -0
- oscura/analyzers/protocols/manchester.py +333 -0
- oscura/analyzers/protocols/onewire.py +501 -0
- oscura/analyzers/protocols/spi.py +334 -0
- oscura/analyzers/protocols/swd.py +325 -0
- oscura/analyzers/protocols/uart.py +393 -0
- oscura/analyzers/protocols/usb.py +495 -0
- oscura/analyzers/signal_integrity/__init__.py +63 -0
- oscura/analyzers/signal_integrity/embedding.py +294 -0
- oscura/analyzers/signal_integrity/equalization.py +370 -0
- oscura/analyzers/signal_integrity/sparams.py +484 -0
- oscura/analyzers/spectral/__init__.py +53 -0
- oscura/analyzers/spectral/chunked.py +273 -0
- oscura/analyzers/spectral/chunked_fft.py +571 -0
- oscura/analyzers/spectral/chunked_wavelet.py +391 -0
- oscura/analyzers/spectral/fft.py +92 -0
- oscura/analyzers/statistical/__init__.py +250 -0
- oscura/analyzers/statistical/checksum.py +923 -0
- oscura/analyzers/statistical/chunked_corr.py +228 -0
- oscura/analyzers/statistical/classification.py +778 -0
- oscura/analyzers/statistical/entropy.py +1113 -0
- oscura/analyzers/statistical/ngrams.py +614 -0
- oscura/analyzers/statistics/__init__.py +119 -0
- oscura/analyzers/statistics/advanced.py +885 -0
- oscura/analyzers/statistics/basic.py +263 -0
- oscura/analyzers/statistics/correlation.py +630 -0
- oscura/analyzers/statistics/distribution.py +298 -0
- oscura/analyzers/statistics/outliers.py +463 -0
- oscura/analyzers/statistics/streaming.py +93 -0
- oscura/analyzers/statistics/trend.py +520 -0
- oscura/analyzers/validation.py +598 -0
- oscura/analyzers/waveform/__init__.py +36 -0
- oscura/analyzers/waveform/measurements.py +943 -0
- oscura/analyzers/waveform/measurements_with_uncertainty.py +371 -0
- oscura/analyzers/waveform/spectral.py +1689 -0
- oscura/analyzers/waveform/wavelets.py +298 -0
- oscura/api/__init__.py +62 -0
- oscura/api/dsl.py +538 -0
- oscura/api/fluent.py +571 -0
- oscura/api/operators.py +498 -0
- oscura/api/optimization.py +392 -0
- oscura/api/profiling.py +396 -0
- oscura/automotive/__init__.py +73 -0
- oscura/automotive/can/__init__.py +52 -0
- oscura/automotive/can/analysis.py +356 -0
- oscura/automotive/can/checksum.py +250 -0
- oscura/automotive/can/correlation.py +212 -0
- oscura/automotive/can/discovery.py +355 -0
- oscura/automotive/can/message_wrapper.py +375 -0
- oscura/automotive/can/models.py +385 -0
- oscura/automotive/can/patterns.py +381 -0
- oscura/automotive/can/session.py +452 -0
- oscura/automotive/can/state_machine.py +300 -0
- oscura/automotive/can/stimulus_response.py +461 -0
- oscura/automotive/dbc/__init__.py +15 -0
- oscura/automotive/dbc/generator.py +156 -0
- oscura/automotive/dbc/parser.py +146 -0
- oscura/automotive/dtc/__init__.py +30 -0
- oscura/automotive/dtc/database.py +3036 -0
- oscura/automotive/j1939/__init__.py +14 -0
- oscura/automotive/j1939/decoder.py +745 -0
- oscura/automotive/loaders/__init__.py +35 -0
- oscura/automotive/loaders/asc.py +98 -0
- oscura/automotive/loaders/blf.py +77 -0
- oscura/automotive/loaders/csv_can.py +136 -0
- oscura/automotive/loaders/dispatcher.py +136 -0
- oscura/automotive/loaders/mdf.py +331 -0
- oscura/automotive/loaders/pcap.py +132 -0
- oscura/automotive/obd/__init__.py +14 -0
- oscura/automotive/obd/decoder.py +707 -0
- oscura/automotive/uds/__init__.py +48 -0
- oscura/automotive/uds/decoder.py +265 -0
- oscura/automotive/uds/models.py +64 -0
- oscura/automotive/visualization.py +369 -0
- oscura/batch/__init__.py +55 -0
- oscura/batch/advanced.py +627 -0
- oscura/batch/aggregate.py +300 -0
- oscura/batch/analyze.py +139 -0
- oscura/batch/logging.py +487 -0
- oscura/batch/metrics.py +556 -0
- oscura/builders/__init__.py +41 -0
- oscura/builders/signal_builder.py +1131 -0
- oscura/cli/__init__.py +14 -0
- oscura/cli/batch.py +339 -0
- oscura/cli/characterize.py +273 -0
- oscura/cli/compare.py +775 -0
- oscura/cli/decode.py +551 -0
- oscura/cli/main.py +247 -0
- oscura/cli/shell.py +350 -0
- oscura/comparison/__init__.py +66 -0
- oscura/comparison/compare.py +397 -0
- oscura/comparison/golden.py +487 -0
- oscura/comparison/limits.py +391 -0
- oscura/comparison/mask.py +434 -0
- oscura/comparison/trace_diff.py +30 -0
- oscura/comparison/visualization.py +481 -0
- oscura/compliance/__init__.py +70 -0
- oscura/compliance/advanced.py +756 -0
- oscura/compliance/masks.py +363 -0
- oscura/compliance/reporting.py +483 -0
- oscura/compliance/testing.py +298 -0
- oscura/component/__init__.py +38 -0
- oscura/component/impedance.py +365 -0
- oscura/component/reactive.py +598 -0
- oscura/component/transmission_line.py +312 -0
- oscura/config/__init__.py +191 -0
- oscura/config/defaults.py +254 -0
- oscura/config/loader.py +348 -0
- oscura/config/memory.py +271 -0
- oscura/config/migration.py +458 -0
- oscura/config/pipeline.py +1077 -0
- oscura/config/preferences.py +530 -0
- oscura/config/protocol.py +875 -0
- oscura/config/schema.py +713 -0
- oscura/config/settings.py +420 -0
- oscura/config/thresholds.py +599 -0
- oscura/convenience.py +457 -0
- oscura/core/__init__.py +299 -0
- oscura/core/audit.py +457 -0
- oscura/core/backend_selector.py +405 -0
- oscura/core/cache.py +590 -0
- oscura/core/cancellation.py +439 -0
- oscura/core/confidence.py +225 -0
- oscura/core/config.py +506 -0
- oscura/core/correlation.py +216 -0
- oscura/core/cross_domain.py +422 -0
- oscura/core/debug.py +301 -0
- oscura/core/edge_cases.py +541 -0
- oscura/core/exceptions.py +535 -0
- oscura/core/gpu_backend.py +523 -0
- oscura/core/lazy.py +832 -0
- oscura/core/log_query.py +540 -0
- oscura/core/logging.py +931 -0
- oscura/core/logging_advanced.py +952 -0
- oscura/core/memoize.py +171 -0
- oscura/core/memory_check.py +274 -0
- oscura/core/memory_guard.py +290 -0
- oscura/core/memory_limits.py +336 -0
- oscura/core/memory_monitor.py +453 -0
- oscura/core/memory_progress.py +465 -0
- oscura/core/memory_warnings.py +315 -0
- oscura/core/numba_backend.py +362 -0
- oscura/core/performance.py +352 -0
- oscura/core/progress.py +524 -0
- oscura/core/provenance.py +358 -0
- oscura/core/results.py +331 -0
- oscura/core/types.py +504 -0
- oscura/core/uncertainty.py +383 -0
- oscura/discovery/__init__.py +52 -0
- oscura/discovery/anomaly_detector.py +672 -0
- oscura/discovery/auto_decoder.py +415 -0
- oscura/discovery/comparison.py +497 -0
- oscura/discovery/quality_validator.py +528 -0
- oscura/discovery/signal_detector.py +769 -0
- oscura/dsl/__init__.py +73 -0
- oscura/dsl/commands.py +246 -0
- oscura/dsl/interpreter.py +455 -0
- oscura/dsl/parser.py +689 -0
- oscura/dsl/repl.py +172 -0
- oscura/exceptions.py +59 -0
- oscura/exploratory/__init__.py +111 -0
- oscura/exploratory/error_recovery.py +642 -0
- oscura/exploratory/fuzzy.py +513 -0
- oscura/exploratory/fuzzy_advanced.py +786 -0
- oscura/exploratory/legacy.py +831 -0
- oscura/exploratory/parse.py +358 -0
- oscura/exploratory/recovery.py +275 -0
- oscura/exploratory/sync.py +382 -0
- oscura/exploratory/unknown.py +707 -0
- oscura/export/__init__.py +25 -0
- oscura/export/wireshark/README.md +265 -0
- oscura/export/wireshark/__init__.py +47 -0
- oscura/export/wireshark/generator.py +312 -0
- oscura/export/wireshark/lua_builder.py +159 -0
- oscura/export/wireshark/templates/dissector.lua.j2 +92 -0
- oscura/export/wireshark/type_mapping.py +165 -0
- oscura/export/wireshark/validator.py +105 -0
- oscura/exporters/__init__.py +94 -0
- oscura/exporters/csv.py +303 -0
- oscura/exporters/exporters.py +44 -0
- oscura/exporters/hdf5.py +219 -0
- oscura/exporters/html_export.py +701 -0
- oscura/exporters/json_export.py +291 -0
- oscura/exporters/markdown_export.py +367 -0
- oscura/exporters/matlab_export.py +354 -0
- oscura/exporters/npz_export.py +219 -0
- oscura/exporters/spice_export.py +210 -0
- oscura/extensibility/__init__.py +131 -0
- oscura/extensibility/docs.py +752 -0
- oscura/extensibility/extensions.py +1125 -0
- oscura/extensibility/logging.py +259 -0
- oscura/extensibility/measurements.py +485 -0
- oscura/extensibility/plugins.py +414 -0
- oscura/extensibility/registry.py +346 -0
- oscura/extensibility/templates.py +913 -0
- oscura/extensibility/validation.py +651 -0
- oscura/filtering/__init__.py +89 -0
- oscura/filtering/base.py +563 -0
- oscura/filtering/convenience.py +564 -0
- oscura/filtering/design.py +725 -0
- oscura/filtering/filters.py +32 -0
- oscura/filtering/introspection.py +605 -0
- oscura/guidance/__init__.py +24 -0
- oscura/guidance/recommender.py +429 -0
- oscura/guidance/wizard.py +518 -0
- oscura/inference/__init__.py +251 -0
- oscura/inference/active_learning/README.md +153 -0
- oscura/inference/active_learning/__init__.py +38 -0
- oscura/inference/active_learning/lstar.py +257 -0
- oscura/inference/active_learning/observation_table.py +230 -0
- oscura/inference/active_learning/oracle.py +78 -0
- oscura/inference/active_learning/teachers/__init__.py +15 -0
- oscura/inference/active_learning/teachers/simulator.py +192 -0
- oscura/inference/adaptive_tuning.py +453 -0
- oscura/inference/alignment.py +653 -0
- oscura/inference/bayesian.py +943 -0
- oscura/inference/binary.py +1016 -0
- oscura/inference/crc_reverse.py +711 -0
- oscura/inference/logic.py +288 -0
- oscura/inference/message_format.py +1305 -0
- oscura/inference/protocol.py +417 -0
- oscura/inference/protocol_dsl.py +1084 -0
- oscura/inference/protocol_library.py +1230 -0
- oscura/inference/sequences.py +809 -0
- oscura/inference/signal_intelligence.py +1509 -0
- oscura/inference/spectral.py +215 -0
- oscura/inference/state_machine.py +634 -0
- oscura/inference/stream.py +918 -0
- oscura/integrations/__init__.py +59 -0
- oscura/integrations/llm.py +1827 -0
- oscura/jupyter/__init__.py +32 -0
- oscura/jupyter/display.py +268 -0
- oscura/jupyter/magic.py +334 -0
- oscura/loaders/__init__.py +526 -0
- oscura/loaders/binary.py +69 -0
- oscura/loaders/configurable.py +1255 -0
- oscura/loaders/csv.py +26 -0
- oscura/loaders/csv_loader.py +473 -0
- oscura/loaders/hdf5.py +9 -0
- oscura/loaders/hdf5_loader.py +510 -0
- oscura/loaders/lazy.py +370 -0
- oscura/loaders/mmap_loader.py +583 -0
- oscura/loaders/numpy_loader.py +436 -0
- oscura/loaders/pcap.py +432 -0
- oscura/loaders/preprocessing.py +368 -0
- oscura/loaders/rigol.py +287 -0
- oscura/loaders/sigrok.py +321 -0
- oscura/loaders/tdms.py +367 -0
- oscura/loaders/tektronix.py +711 -0
- oscura/loaders/validation.py +584 -0
- oscura/loaders/vcd.py +464 -0
- oscura/loaders/wav.py +233 -0
- oscura/math/__init__.py +45 -0
- oscura/math/arithmetic.py +824 -0
- oscura/math/interpolation.py +413 -0
- oscura/onboarding/__init__.py +39 -0
- oscura/onboarding/help.py +498 -0
- oscura/onboarding/tutorials.py +405 -0
- oscura/onboarding/wizard.py +466 -0
- oscura/optimization/__init__.py +19 -0
- oscura/optimization/parallel.py +440 -0
- oscura/optimization/search.py +532 -0
- oscura/pipeline/__init__.py +43 -0
- oscura/pipeline/base.py +338 -0
- oscura/pipeline/composition.py +242 -0
- oscura/pipeline/parallel.py +448 -0
- oscura/pipeline/pipeline.py +375 -0
- oscura/pipeline/reverse_engineering.py +1119 -0
- oscura/plugins/__init__.py +122 -0
- oscura/plugins/base.py +272 -0
- oscura/plugins/cli.py +497 -0
- oscura/plugins/discovery.py +411 -0
- oscura/plugins/isolation.py +418 -0
- oscura/plugins/lifecycle.py +959 -0
- oscura/plugins/manager.py +493 -0
- oscura/plugins/registry.py +421 -0
- oscura/plugins/versioning.py +372 -0
- oscura/py.typed +0 -0
- oscura/quality/__init__.py +65 -0
- oscura/quality/ensemble.py +740 -0
- oscura/quality/explainer.py +338 -0
- oscura/quality/scoring.py +616 -0
- oscura/quality/warnings.py +456 -0
- oscura/reporting/__init__.py +248 -0
- oscura/reporting/advanced.py +1234 -0
- oscura/reporting/analyze.py +448 -0
- oscura/reporting/argument_preparer.py +596 -0
- oscura/reporting/auto_report.py +507 -0
- oscura/reporting/batch.py +615 -0
- oscura/reporting/chart_selection.py +223 -0
- oscura/reporting/comparison.py +330 -0
- oscura/reporting/config.py +615 -0
- oscura/reporting/content/__init__.py +39 -0
- oscura/reporting/content/executive.py +127 -0
- oscura/reporting/content/filtering.py +191 -0
- oscura/reporting/content/minimal.py +257 -0
- oscura/reporting/content/verbosity.py +162 -0
- oscura/reporting/core.py +508 -0
- oscura/reporting/core_formats/__init__.py +17 -0
- oscura/reporting/core_formats/multi_format.py +210 -0
- oscura/reporting/engine.py +836 -0
- oscura/reporting/export.py +366 -0
- oscura/reporting/formatting/__init__.py +129 -0
- oscura/reporting/formatting/emphasis.py +81 -0
- oscura/reporting/formatting/numbers.py +403 -0
- oscura/reporting/formatting/standards.py +55 -0
- oscura/reporting/formatting.py +466 -0
- oscura/reporting/html.py +578 -0
- oscura/reporting/index.py +590 -0
- oscura/reporting/multichannel.py +296 -0
- oscura/reporting/output.py +379 -0
- oscura/reporting/pdf.py +373 -0
- oscura/reporting/plots.py +731 -0
- oscura/reporting/pptx_export.py +360 -0
- oscura/reporting/renderers/__init__.py +11 -0
- oscura/reporting/renderers/pdf.py +94 -0
- oscura/reporting/sections.py +471 -0
- oscura/reporting/standards.py +680 -0
- oscura/reporting/summary_generator.py +368 -0
- oscura/reporting/tables.py +397 -0
- oscura/reporting/template_system.py +724 -0
- oscura/reporting/templates/__init__.py +15 -0
- oscura/reporting/templates/definition.py +205 -0
- oscura/reporting/templates/index.html +649 -0
- oscura/reporting/templates/index.md +173 -0
- oscura/schemas/__init__.py +158 -0
- oscura/schemas/bus_configuration.json +322 -0
- oscura/schemas/device_mapping.json +182 -0
- oscura/schemas/packet_format.json +418 -0
- oscura/schemas/protocol_definition.json +363 -0
- oscura/search/__init__.py +16 -0
- oscura/search/anomaly.py +292 -0
- oscura/search/context.py +149 -0
- oscura/search/pattern.py +160 -0
- oscura/session/__init__.py +34 -0
- oscura/session/annotations.py +289 -0
- oscura/session/history.py +313 -0
- oscura/session/session.py +445 -0
- oscura/streaming/__init__.py +43 -0
- oscura/streaming/chunked.py +611 -0
- oscura/streaming/progressive.py +393 -0
- oscura/streaming/realtime.py +622 -0
- oscura/testing/__init__.py +54 -0
- oscura/testing/synthetic.py +808 -0
- oscura/triggering/__init__.py +68 -0
- oscura/triggering/base.py +229 -0
- oscura/triggering/edge.py +353 -0
- oscura/triggering/pattern.py +344 -0
- oscura/triggering/pulse.py +581 -0
- oscura/triggering/window.py +453 -0
- oscura/ui/__init__.py +48 -0
- oscura/ui/formatters.py +526 -0
- oscura/ui/progressive_display.py +340 -0
- oscura/utils/__init__.py +99 -0
- oscura/utils/autodetect.py +338 -0
- oscura/utils/buffer.py +389 -0
- oscura/utils/lazy.py +407 -0
- oscura/utils/lazy_imports.py +147 -0
- oscura/utils/memory.py +836 -0
- oscura/utils/memory_advanced.py +1326 -0
- oscura/utils/memory_extensions.py +465 -0
- oscura/utils/progressive.py +352 -0
- oscura/utils/windowing.py +362 -0
- oscura/visualization/__init__.py +321 -0
- oscura/visualization/accessibility.py +526 -0
- oscura/visualization/annotations.py +374 -0
- oscura/visualization/axis_scaling.py +305 -0
- oscura/visualization/colors.py +453 -0
- oscura/visualization/digital.py +337 -0
- oscura/visualization/eye.py +420 -0
- oscura/visualization/histogram.py +281 -0
- oscura/visualization/interactive.py +858 -0
- oscura/visualization/jitter.py +702 -0
- oscura/visualization/keyboard.py +394 -0
- oscura/visualization/layout.py +365 -0
- oscura/visualization/optimization.py +1028 -0
- oscura/visualization/palettes.py +446 -0
- oscura/visualization/plot.py +92 -0
- oscura/visualization/power.py +290 -0
- oscura/visualization/power_extended.py +626 -0
- oscura/visualization/presets.py +467 -0
- oscura/visualization/protocols.py +932 -0
- oscura/visualization/render.py +207 -0
- oscura/visualization/rendering.py +444 -0
- oscura/visualization/reverse_engineering.py +791 -0
- oscura/visualization/signal_integrity.py +808 -0
- oscura/visualization/specialized.py +553 -0
- oscura/visualization/spectral.py +811 -0
- oscura/visualization/styles.py +381 -0
- oscura/visualization/thumbnails.py +311 -0
- oscura/visualization/time_axis.py +351 -0
- oscura/visualization/waveform.py +367 -0
- oscura/workflow/__init__.py +13 -0
- oscura/workflow/dag.py +377 -0
- oscura/workflows/__init__.py +58 -0
- oscura/workflows/compliance.py +280 -0
- oscura/workflows/digital.py +272 -0
- oscura/workflows/multi_trace.py +502 -0
- oscura/workflows/power.py +178 -0
- oscura/workflows/protocol.py +492 -0
- oscura/workflows/reverse_engineering.py +639 -0
- oscura/workflows/signal_integrity.py +227 -0
- oscura-0.1.0.dist-info/METADATA +300 -0
- oscura-0.1.0.dist-info/RECORD +463 -0
- oscura-0.1.0.dist-info/entry_points.txt +2 -0
- {oscura-0.0.1.dist-info → oscura-0.1.0.dist-info}/licenses/LICENSE +1 -1
- oscura-0.0.1.dist-info/METADATA +0 -63
- oscura-0.0.1.dist-info/RECORD +0 -5
- {oscura-0.0.1.dist-info → oscura-0.1.0.dist-info}/WHEEL +0 -0
|
@@ -0,0 +1,453 @@
|
|
|
1
|
+
"""Adaptive parameter tuning for analysis functions.
|
|
2
|
+
|
|
3
|
+
Auto-configures analysis parameters based on signal characteristics,
|
|
4
|
+
reducing the need for manual parameter specification.
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
Example:
|
|
8
|
+
>>> import oscura as tk
|
|
9
|
+
>>> trace = tk.load('signal.wfm')
|
|
10
|
+
>>> tuner = tk.AdaptiveParameterTuner(trace.data, trace.metadata.sample_rate)
|
|
11
|
+
>>> params = tuner.get_spectral_params()
|
|
12
|
+
>>> print(f"NFFT: {params.get('nfft')}")
|
|
13
|
+
>>> print(f"Window: {params.get('window')}")
|
|
14
|
+
>>> print(f"Reasoning: {params.reasoning}")
|
|
15
|
+
|
|
16
|
+
References:
|
|
17
|
+
Harris, F. J. (1978): On the use of windows for harmonic analysis with DFT
|
|
18
|
+
Oppenheim, A. V. & Schafer, R. W. (2010): Discrete-Time Signal Processing
|
|
19
|
+
"""
|
|
20
|
+
|
|
21
|
+
from __future__ import annotations
|
|
22
|
+
|
|
23
|
+
import logging
|
|
24
|
+
from dataclasses import dataclass, field
|
|
25
|
+
from typing import TYPE_CHECKING, Any
|
|
26
|
+
|
|
27
|
+
import numpy as np
|
|
28
|
+
|
|
29
|
+
if TYPE_CHECKING:
|
|
30
|
+
from numpy.typing import NDArray
|
|
31
|
+
|
|
32
|
+
logger = logging.getLogger(__name__)
|
|
33
|
+
|
|
34
|
+
|
|
35
|
+
@dataclass
|
|
36
|
+
class TunedParameters:
|
|
37
|
+
"""Container for auto-tuned parameters.
|
|
38
|
+
|
|
39
|
+
Attributes:
|
|
40
|
+
parameters: Dictionary of parameter names to values.
|
|
41
|
+
confidence: Confidence in parameter tuning (0.0-1.0).
|
|
42
|
+
reasoning: Dictionary mapping parameter names to reasoning strings.
|
|
43
|
+
"""
|
|
44
|
+
|
|
45
|
+
parameters: dict[str, Any] = field(default_factory=dict)
|
|
46
|
+
confidence: float = 0.5
|
|
47
|
+
reasoning: dict[str, str] = field(default_factory=dict)
|
|
48
|
+
|
|
49
|
+
def get(self, key: str, default: Any = None) -> Any:
|
|
50
|
+
"""Get parameter value with default.
|
|
51
|
+
|
|
52
|
+
Args:
|
|
53
|
+
key: Parameter name.
|
|
54
|
+
default: Default value if parameter not found.
|
|
55
|
+
|
|
56
|
+
Returns:
|
|
57
|
+
Parameter value or default.
|
|
58
|
+
"""
|
|
59
|
+
return self.parameters.get(key, default)
|
|
60
|
+
|
|
61
|
+
|
|
62
|
+
class AdaptiveParameterTuner:
|
|
63
|
+
"""Auto-configure analysis parameters based on signal characteristics.
|
|
64
|
+
|
|
65
|
+
This class analyzes signal characteristics and provides intelligent
|
|
66
|
+
parameter suggestions for various analysis domains (spectral, digital,
|
|
67
|
+
timing, jitter, pattern recognition).
|
|
68
|
+
|
|
69
|
+
Attributes:
|
|
70
|
+
data: Input signal data array.
|
|
71
|
+
sample_rate: Sample rate in Hz.
|
|
72
|
+
signal_type: Optional signal type hint (digital, analog, etc.).
|
|
73
|
+
|
|
74
|
+
Example:
|
|
75
|
+
>>> tuner = AdaptiveParameterTuner(signal_data, sample_rate=1e6)
|
|
76
|
+
>>> spectral_params = tuner.get_spectral_params()
|
|
77
|
+
>>> print(spectral_params.parameters)
|
|
78
|
+
{'nfft': 8192, 'window': 'hann', 'overlap': 0.5}
|
|
79
|
+
"""
|
|
80
|
+
|
|
81
|
+
def __init__(
|
|
82
|
+
self,
|
|
83
|
+
data: NDArray[np.floating[Any]],
|
|
84
|
+
sample_rate: float = 1.0,
|
|
85
|
+
signal_type: str | None = None,
|
|
86
|
+
):
|
|
87
|
+
"""Initialize tuner with signal data.
|
|
88
|
+
|
|
89
|
+
Args:
|
|
90
|
+
data: Input signal data.
|
|
91
|
+
sample_rate: Sample rate in Hz.
|
|
92
|
+
signal_type: Optional signal type hint (digital, analog, etc.).
|
|
93
|
+
"""
|
|
94
|
+
self.data = data
|
|
95
|
+
self.sample_rate = sample_rate
|
|
96
|
+
self.signal_type = signal_type
|
|
97
|
+
|
|
98
|
+
# Pre-compute signal characteristics
|
|
99
|
+
self._characteristics = self._analyze_signal()
|
|
100
|
+
|
|
101
|
+
def _analyze_signal(self) -> dict[str, Any]:
|
|
102
|
+
"""Analyze signal characteristics for parameter tuning.
|
|
103
|
+
|
|
104
|
+
Returns:
|
|
105
|
+
Dictionary of signal characteristics including statistics,
|
|
106
|
+
noise estimates, frequency content, and signal type indicators.
|
|
107
|
+
"""
|
|
108
|
+
chars: dict[str, Any] = {}
|
|
109
|
+
|
|
110
|
+
try:
|
|
111
|
+
# Basic statistics
|
|
112
|
+
chars["mean"] = float(np.mean(self.data))
|
|
113
|
+
chars["std"] = float(np.std(self.data))
|
|
114
|
+
chars["min"] = float(np.min(self.data))
|
|
115
|
+
chars["max"] = float(np.max(self.data))
|
|
116
|
+
chars["range"] = chars["max"] - chars["min"]
|
|
117
|
+
chars["n_samples"] = len(self.data)
|
|
118
|
+
chars["duration"] = len(self.data) / self.sample_rate
|
|
119
|
+
|
|
120
|
+
# Detect if digital
|
|
121
|
+
unique_values = len(np.unique(np.round(self.data, decimals=2)))
|
|
122
|
+
chars["likely_digital"] = unique_values < 10
|
|
123
|
+
|
|
124
|
+
# Estimate dominant frequency
|
|
125
|
+
chars["dominant_freq"] = self._estimate_dominant_frequency()
|
|
126
|
+
|
|
127
|
+
# Estimate noise floor
|
|
128
|
+
median = np.median(self.data)
|
|
129
|
+
mad = np.median(np.abs(self.data - median)) * 1.4826
|
|
130
|
+
chars["noise_floor"] = float(mad)
|
|
131
|
+
|
|
132
|
+
# SNR estimate
|
|
133
|
+
signal_power = np.var(self.data)
|
|
134
|
+
noise_power = mad**2
|
|
135
|
+
if noise_power > 0:
|
|
136
|
+
chars["snr_db"] = float(10 * np.log10(signal_power / noise_power))
|
|
137
|
+
else:
|
|
138
|
+
chars["snr_db"] = 40.0
|
|
139
|
+
|
|
140
|
+
except Exception as e:
|
|
141
|
+
logger.debug(f"Error analyzing signal: {e}")
|
|
142
|
+
|
|
143
|
+
return chars
|
|
144
|
+
|
|
145
|
+
def _estimate_dominant_frequency(self) -> float | None:
|
|
146
|
+
"""Estimate dominant frequency using FFT.
|
|
147
|
+
|
|
148
|
+
Returns:
|
|
149
|
+
Dominant frequency in Hz, or None if not detectable.
|
|
150
|
+
"""
|
|
151
|
+
try:
|
|
152
|
+
data_ac = self.data - np.mean(self.data)
|
|
153
|
+
fft_result = np.fft.rfft(data_ac)
|
|
154
|
+
freqs = np.fft.rfftfreq(len(data_ac), d=1.0 / self.sample_rate)
|
|
155
|
+
magnitude = np.abs(fft_result[1:]) # Skip DC
|
|
156
|
+
|
|
157
|
+
if len(magnitude) > 0:
|
|
158
|
+
peak_idx = np.argmax(magnitude)
|
|
159
|
+
return float(freqs[1:][peak_idx])
|
|
160
|
+
except Exception:
|
|
161
|
+
pass
|
|
162
|
+
return None
|
|
163
|
+
|
|
164
|
+
def get_spectral_params(self) -> TunedParameters:
|
|
165
|
+
"""Get tuned parameters for spectral analysis.
|
|
166
|
+
|
|
167
|
+
Selects FFT size, window function, and overlap based on signal
|
|
168
|
+
characteristics and quality requirements.
|
|
169
|
+
|
|
170
|
+
Returns:
|
|
171
|
+
TunedParameters with spectral analysis configuration.
|
|
172
|
+
|
|
173
|
+
Example:
|
|
174
|
+
>>> params = tuner.get_spectral_params()
|
|
175
|
+
>>> print(f"NFFT: {params.get('nfft')}")
|
|
176
|
+
>>> print(f"Reasoning: {params.reasoning['nfft']}")
|
|
177
|
+
"""
|
|
178
|
+
params = {}
|
|
179
|
+
reasoning = {}
|
|
180
|
+
confidence = 0.8
|
|
181
|
+
|
|
182
|
+
n_samples = self._characteristics.get("n_samples", 1000)
|
|
183
|
+
|
|
184
|
+
# NFFT - power of 2, balancing resolution and computation
|
|
185
|
+
ideal_nfft = min(8192, max(256, 2 ** int(np.ceil(np.log2(n_samples / 4)))))
|
|
186
|
+
params["nfft"] = ideal_nfft
|
|
187
|
+
reasoning["nfft"] = f"Power of 2 for efficiency, ~{n_samples / ideal_nfft:.0f} averages"
|
|
188
|
+
|
|
189
|
+
# Window selection based on signal characteristics
|
|
190
|
+
snr = self._characteristics.get("snr_db", 20)
|
|
191
|
+
if snr < 15:
|
|
192
|
+
params["window"] = "blackman"
|
|
193
|
+
reasoning["window"] = "Low SNR - using Blackman for better noise rejection"
|
|
194
|
+
elif snr < 25:
|
|
195
|
+
params["window"] = "hann"
|
|
196
|
+
reasoning["window"] = "Moderate SNR - using Hann for balance"
|
|
197
|
+
else:
|
|
198
|
+
params["window"] = "hamming"
|
|
199
|
+
reasoning["window"] = "Good SNR - using Hamming for resolution"
|
|
200
|
+
|
|
201
|
+
# Overlap
|
|
202
|
+
params["overlap"] = 0.5
|
|
203
|
+
reasoning["overlap"] = "Standard 50% overlap for smooth averaging"
|
|
204
|
+
|
|
205
|
+
# Frequency range based on dominant frequency
|
|
206
|
+
dom_freq = self._characteristics.get("dominant_freq")
|
|
207
|
+
if dom_freq and dom_freq > 0:
|
|
208
|
+
params["freq_min"] = max(0, dom_freq / 10)
|
|
209
|
+
params["freq_max"] = min(self.sample_rate / 2, dom_freq * 5)
|
|
210
|
+
reasoning["freq_range"] = f"Based on dominant frequency {dom_freq:.1f} Hz"
|
|
211
|
+
|
|
212
|
+
return TunedParameters(parameters=params, confidence=confidence, reasoning=reasoning)
|
|
213
|
+
|
|
214
|
+
def get_digital_params(self) -> TunedParameters:
|
|
215
|
+
"""Get tuned parameters for digital signal analysis.
|
|
216
|
+
|
|
217
|
+
Determines threshold levels, edge detection sensitivity, and
|
|
218
|
+
baud rate hints based on signal characteristics.
|
|
219
|
+
|
|
220
|
+
Returns:
|
|
221
|
+
TunedParameters with digital analysis configuration.
|
|
222
|
+
|
|
223
|
+
Example:
|
|
224
|
+
>>> params = tuner.get_digital_params()
|
|
225
|
+
>>> print(f"Threshold: {params.get('threshold')}")
|
|
226
|
+
>>> print(f"Baud rate hint: {params.get('baud_rate_hint')}")
|
|
227
|
+
"""
|
|
228
|
+
params = {}
|
|
229
|
+
reasoning = {}
|
|
230
|
+
confidence = 0.7
|
|
231
|
+
|
|
232
|
+
chars = self._characteristics
|
|
233
|
+
|
|
234
|
+
# Threshold based on signal levels
|
|
235
|
+
if chars.get("likely_digital"):
|
|
236
|
+
mid = (chars["min"] + chars["max"]) / 2
|
|
237
|
+
params["threshold"] = mid
|
|
238
|
+
params["threshold_low"] = chars["min"] + 0.3 * chars["range"]
|
|
239
|
+
params["threshold_high"] = chars["max"] - 0.3 * chars["range"]
|
|
240
|
+
reasoning["threshold"] = (
|
|
241
|
+
f"Midpoint of signal range ({chars['min']:.2f} to {chars['max']:.2f})"
|
|
242
|
+
)
|
|
243
|
+
confidence = 0.85
|
|
244
|
+
else:
|
|
245
|
+
params["threshold"] = chars.get("mean", 0)
|
|
246
|
+
reasoning["threshold"] = "Using mean (signal may not be digital)"
|
|
247
|
+
confidence = 0.5
|
|
248
|
+
|
|
249
|
+
# Edge detection sensitivity based on noise
|
|
250
|
+
noise = chars.get("noise_floor", 0.1)
|
|
251
|
+
params["min_edge_separation"] = max(2, int(noise * 10))
|
|
252
|
+
reasoning["min_edge_separation"] = f"Based on noise floor {noise:.3f}"
|
|
253
|
+
|
|
254
|
+
# Baud rate hint from dominant frequency
|
|
255
|
+
dom_freq = chars.get("dominant_freq")
|
|
256
|
+
if dom_freq and dom_freq > 0:
|
|
257
|
+
# Common baud rates
|
|
258
|
+
common_bauds = [300, 1200, 2400, 4800, 9600, 19200, 38400, 57600, 115200]
|
|
259
|
+
closest_baud = min(common_bauds, key=lambda b: abs(b - dom_freq * 2))
|
|
260
|
+
params["baud_rate_hint"] = closest_baud
|
|
261
|
+
reasoning["baud_rate"] = f"Estimated from frequency {dom_freq:.0f} Hz"
|
|
262
|
+
|
|
263
|
+
return TunedParameters(parameters=params, confidence=confidence, reasoning=reasoning)
|
|
264
|
+
|
|
265
|
+
def get_timing_params(self) -> TunedParameters:
|
|
266
|
+
"""Get tuned parameters for timing analysis.
|
|
267
|
+
|
|
268
|
+
Configures time resolution, expected period, and edge timing
|
|
269
|
+
thresholds based on sample rate and signal characteristics.
|
|
270
|
+
|
|
271
|
+
Returns:
|
|
272
|
+
TunedParameters with timing analysis configuration.
|
|
273
|
+
|
|
274
|
+
Example:
|
|
275
|
+
>>> params = tuner.get_timing_params()
|
|
276
|
+
>>> print(f"Expected period: {params.get('expected_period')}")
|
|
277
|
+
>>> print(f"Tolerance: {params.get('period_tolerance')}")
|
|
278
|
+
"""
|
|
279
|
+
params = {}
|
|
280
|
+
reasoning = {}
|
|
281
|
+
confidence = 0.75
|
|
282
|
+
|
|
283
|
+
chars = self._characteristics
|
|
284
|
+
|
|
285
|
+
# Time resolution based on sample rate
|
|
286
|
+
params["time_resolution"] = 1.0 / self.sample_rate
|
|
287
|
+
reasoning["time_resolution"] = f"Based on sample rate {self.sample_rate:.0f} Hz"
|
|
288
|
+
|
|
289
|
+
# Expected period from dominant frequency
|
|
290
|
+
dom_freq = chars.get("dominant_freq")
|
|
291
|
+
if dom_freq and dom_freq > 0:
|
|
292
|
+
params["expected_period"] = 1.0 / dom_freq
|
|
293
|
+
params["period_tolerance"] = 0.2 / dom_freq # 20% tolerance
|
|
294
|
+
reasoning["period"] = f"From dominant frequency {dom_freq:.1f} Hz"
|
|
295
|
+
confidence = 0.85
|
|
296
|
+
|
|
297
|
+
# Edge timing thresholds
|
|
298
|
+
noise = chars.get("noise_floor", 0.1)
|
|
299
|
+
params["edge_threshold"] = noise * 3 # 3-sigma
|
|
300
|
+
reasoning["edge_threshold"] = f"3x noise floor ({noise:.3f})"
|
|
301
|
+
|
|
302
|
+
return TunedParameters(parameters=params, confidence=confidence, reasoning=reasoning)
|
|
303
|
+
|
|
304
|
+
def get_jitter_params(self) -> TunedParameters:
|
|
305
|
+
"""Get tuned parameters for jitter analysis.
|
|
306
|
+
|
|
307
|
+
Determines unit interval, histogram binning, and tolerance
|
|
308
|
+
parameters for jitter measurements.
|
|
309
|
+
|
|
310
|
+
Returns:
|
|
311
|
+
TunedParameters with jitter analysis configuration.
|
|
312
|
+
|
|
313
|
+
Example:
|
|
314
|
+
>>> params = tuner.get_jitter_params()
|
|
315
|
+
>>> print(f"Unit interval: {params.get('unit_interval')}")
|
|
316
|
+
>>> print(f"Histogram bins: {params.get('histogram_bins')}")
|
|
317
|
+
"""
|
|
318
|
+
params = {}
|
|
319
|
+
reasoning = {}
|
|
320
|
+
confidence = 0.7
|
|
321
|
+
|
|
322
|
+
chars = self._characteristics
|
|
323
|
+
|
|
324
|
+
# Unit interval from dominant frequency
|
|
325
|
+
dom_freq = chars.get("dominant_freq")
|
|
326
|
+
if dom_freq and dom_freq > 0:
|
|
327
|
+
ui = 1.0 / dom_freq
|
|
328
|
+
params["unit_interval"] = ui
|
|
329
|
+
params["ui_tolerance"] = ui * 0.1
|
|
330
|
+
reasoning["unit_interval"] = f"From dominant frequency {dom_freq:.1f} Hz"
|
|
331
|
+
confidence = 0.85
|
|
332
|
+
|
|
333
|
+
# Histogram bins based on data range and noise
|
|
334
|
+
snr = chars.get("snr_db", 20)
|
|
335
|
+
if snr > 30:
|
|
336
|
+
params["histogram_bins"] = 256
|
|
337
|
+
elif snr > 20:
|
|
338
|
+
params["histogram_bins"] = 128
|
|
339
|
+
else:
|
|
340
|
+
params["histogram_bins"] = 64
|
|
341
|
+
reasoning["histogram_bins"] = f"Based on SNR {snr:.0f} dB"
|
|
342
|
+
|
|
343
|
+
return TunedParameters(parameters=params, confidence=confidence, reasoning=reasoning)
|
|
344
|
+
|
|
345
|
+
def get_pattern_params(self) -> TunedParameters:
|
|
346
|
+
"""Get tuned parameters for pattern analysis.
|
|
347
|
+
|
|
348
|
+
Configures minimum pattern length and maximum distance for
|
|
349
|
+
fuzzy matching based on signal characteristics.
|
|
350
|
+
|
|
351
|
+
Returns:
|
|
352
|
+
TunedParameters with pattern analysis configuration.
|
|
353
|
+
|
|
354
|
+
Example:
|
|
355
|
+
>>> params = tuner.get_pattern_params()
|
|
356
|
+
>>> print(f"Min pattern length: {params.get('min_length')}")
|
|
357
|
+
>>> print(f"Max fuzzy distance: {params.get('max_distance')}")
|
|
358
|
+
"""
|
|
359
|
+
params = {}
|
|
360
|
+
reasoning = {}
|
|
361
|
+
confidence = 0.7
|
|
362
|
+
|
|
363
|
+
chars = self._characteristics
|
|
364
|
+
n_samples = chars.get("n_samples", 1000)
|
|
365
|
+
|
|
366
|
+
# Min pattern length based on signal characteristics
|
|
367
|
+
dom_freq = chars.get("dominant_freq")
|
|
368
|
+
if dom_freq and dom_freq > 0:
|
|
369
|
+
samples_per_period = self.sample_rate / dom_freq
|
|
370
|
+
params["min_length"] = max(3, int(samples_per_period / 4))
|
|
371
|
+
reasoning["min_length"] = (
|
|
372
|
+
f"Quarter of estimated period ({samples_per_period:.0f} samples)"
|
|
373
|
+
)
|
|
374
|
+
else:
|
|
375
|
+
params["min_length"] = max(3, n_samples // 100)
|
|
376
|
+
reasoning["min_length"] = "1% of signal length"
|
|
377
|
+
|
|
378
|
+
# Max distance for fuzzy matching based on noise
|
|
379
|
+
noise_ratio = chars.get("noise_floor", 0.1) / max(chars.get("range", 1), 0.001)
|
|
380
|
+
params["max_distance"] = max(1, int(noise_ratio * 10))
|
|
381
|
+
reasoning["max_distance"] = f"Based on noise ratio {noise_ratio:.2%}"
|
|
382
|
+
|
|
383
|
+
return TunedParameters(parameters=params, confidence=confidence, reasoning=reasoning)
|
|
384
|
+
|
|
385
|
+
def get_params_for_domain(self, domain: str) -> TunedParameters:
|
|
386
|
+
"""Get tuned parameters for a specific analysis domain.
|
|
387
|
+
|
|
388
|
+
Args:
|
|
389
|
+
domain: Analysis domain name (spectral, digital, timing, jitter, pattern).
|
|
390
|
+
|
|
391
|
+
Returns:
|
|
392
|
+
TunedParameters for the specified domain.
|
|
393
|
+
|
|
394
|
+
Example:
|
|
395
|
+
>>> params = tuner.get_params_for_domain("spectral")
|
|
396
|
+
>>> print(params.parameters)
|
|
397
|
+
{'nfft': 8192, 'window': 'hann', 'overlap': 0.5}
|
|
398
|
+
"""
|
|
399
|
+
domain_lower = domain.lower()
|
|
400
|
+
|
|
401
|
+
if "spectral" in domain_lower or "fft" in domain_lower:
|
|
402
|
+
return self.get_spectral_params()
|
|
403
|
+
elif "digital" in domain_lower:
|
|
404
|
+
return self.get_digital_params()
|
|
405
|
+
elif "timing" in domain_lower:
|
|
406
|
+
return self.get_timing_params()
|
|
407
|
+
elif "jitter" in domain_lower:
|
|
408
|
+
return self.get_jitter_params()
|
|
409
|
+
elif "pattern" in domain_lower:
|
|
410
|
+
return self.get_pattern_params()
|
|
411
|
+
else:
|
|
412
|
+
# Return basic params for unknown domains
|
|
413
|
+
return TunedParameters(
|
|
414
|
+
parameters={},
|
|
415
|
+
confidence=0.5,
|
|
416
|
+
reasoning={"note": "No domain-specific tuning available"},
|
|
417
|
+
)
|
|
418
|
+
|
|
419
|
+
|
|
420
|
+
def get_adaptive_parameters(
|
|
421
|
+
data: NDArray[np.floating[Any]],
|
|
422
|
+
sample_rate: float,
|
|
423
|
+
domain: str,
|
|
424
|
+
signal_type: str | None = None,
|
|
425
|
+
) -> TunedParameters:
|
|
426
|
+
"""Convenience function to get adaptive parameters.
|
|
427
|
+
|
|
428
|
+
This is a shortcut for creating an AdaptiveParameterTuner and
|
|
429
|
+
getting parameters for a specific domain.
|
|
430
|
+
|
|
431
|
+
Args:
|
|
432
|
+
data: Input signal data.
|
|
433
|
+
sample_rate: Sample rate in Hz.
|
|
434
|
+
domain: Analysis domain (spectral, digital, timing, jitter, pattern).
|
|
435
|
+
signal_type: Optional signal type hint.
|
|
436
|
+
|
|
437
|
+
Returns:
|
|
438
|
+
TunedParameters for the specified domain.
|
|
439
|
+
|
|
440
|
+
Example:
|
|
441
|
+
>>> params = get_adaptive_parameters(signal, 1e6, "spectral")
|
|
442
|
+
>>> print(f"Window: {params.get('window')}")
|
|
443
|
+
>>> print(f"Confidence: {params.confidence}")
|
|
444
|
+
"""
|
|
445
|
+
tuner = AdaptiveParameterTuner(data, sample_rate, signal_type)
|
|
446
|
+
return tuner.get_params_for_domain(domain)
|
|
447
|
+
|
|
448
|
+
|
|
449
|
+
__all__ = [
|
|
450
|
+
"AdaptiveParameterTuner",
|
|
451
|
+
"TunedParameters",
|
|
452
|
+
"get_adaptive_parameters",
|
|
453
|
+
]
|