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,453 @@
|
|
|
1
|
+
"""Window and zone triggering for Oscura.
|
|
2
|
+
|
|
3
|
+
Provides window triggering (signal inside/outside voltage window) and
|
|
4
|
+
zone triggering (signal enters/exits defined zones) for limit testing.
|
|
5
|
+
|
|
6
|
+
Example:
|
|
7
|
+
>>> from oscura.triggering.window import WindowTrigger, find_window_violations
|
|
8
|
+
>>> # Trigger when signal exits 0-3.3V window
|
|
9
|
+
>>> trigger = WindowTrigger(low_threshold=0, high_threshold=3.3, trigger_on="exit")
|
|
10
|
+
>>> violations = trigger.find_events(trace)
|
|
11
|
+
"""
|
|
12
|
+
|
|
13
|
+
from __future__ import annotations
|
|
14
|
+
|
|
15
|
+
from dataclasses import dataclass
|
|
16
|
+
from typing import TYPE_CHECKING, Literal
|
|
17
|
+
|
|
18
|
+
import numpy as np
|
|
19
|
+
|
|
20
|
+
from oscura.core.exceptions import AnalysisError
|
|
21
|
+
from oscura.triggering.base import (
|
|
22
|
+
Trigger,
|
|
23
|
+
TriggerEvent,
|
|
24
|
+
TriggerType,
|
|
25
|
+
)
|
|
26
|
+
|
|
27
|
+
if TYPE_CHECKING:
|
|
28
|
+
from oscura.core.types import WaveformTrace
|
|
29
|
+
|
|
30
|
+
|
|
31
|
+
@dataclass
|
|
32
|
+
class Zone:
|
|
33
|
+
"""Defines a voltage/time zone for triggering.
|
|
34
|
+
|
|
35
|
+
Attributes:
|
|
36
|
+
low: Lower voltage boundary.
|
|
37
|
+
high: Upper voltage boundary.
|
|
38
|
+
start_time: Start time boundary (None for no limit).
|
|
39
|
+
end_time: End time boundary (None for no limit).
|
|
40
|
+
name: Optional zone name for identification.
|
|
41
|
+
"""
|
|
42
|
+
|
|
43
|
+
low: float
|
|
44
|
+
high: float
|
|
45
|
+
start_time: float | None = None
|
|
46
|
+
end_time: float | None = None
|
|
47
|
+
name: str = ""
|
|
48
|
+
|
|
49
|
+
|
|
50
|
+
class WindowTrigger(Trigger):
|
|
51
|
+
"""Window trigger for detecting voltage limit violations.
|
|
52
|
+
|
|
53
|
+
Triggers when the signal enters or exits a voltage window defined
|
|
54
|
+
by low and high threshold levels.
|
|
55
|
+
|
|
56
|
+
Attributes:
|
|
57
|
+
low_threshold: Lower window boundary.
|
|
58
|
+
high_threshold: Upper window boundary.
|
|
59
|
+
trigger_on: When to trigger - "entry", "exit", or "both".
|
|
60
|
+
"""
|
|
61
|
+
|
|
62
|
+
def __init__(
|
|
63
|
+
self,
|
|
64
|
+
low_threshold: float,
|
|
65
|
+
high_threshold: float,
|
|
66
|
+
trigger_on: Literal["entry", "exit", "both"] = "exit",
|
|
67
|
+
) -> None:
|
|
68
|
+
"""Initialize window trigger.
|
|
69
|
+
|
|
70
|
+
Args:
|
|
71
|
+
low_threshold: Lower window boundary.
|
|
72
|
+
high_threshold: Upper window boundary.
|
|
73
|
+
trigger_on: "entry" triggers when entering window,
|
|
74
|
+
"exit" triggers when leaving window,
|
|
75
|
+
"both" triggers on either event.
|
|
76
|
+
|
|
77
|
+
Raises:
|
|
78
|
+
AnalysisError: If low_threshold is not less than high_threshold.
|
|
79
|
+
"""
|
|
80
|
+
if low_threshold >= high_threshold:
|
|
81
|
+
raise AnalysisError("low_threshold must be less than high_threshold")
|
|
82
|
+
|
|
83
|
+
self.low_threshold = low_threshold
|
|
84
|
+
self.high_threshold = high_threshold
|
|
85
|
+
self.trigger_on = trigger_on
|
|
86
|
+
|
|
87
|
+
def find_events(
|
|
88
|
+
self,
|
|
89
|
+
trace: WaveformTrace, # type: ignore[override]
|
|
90
|
+
) -> list[TriggerEvent]:
|
|
91
|
+
"""Find all window entry/exit events.
|
|
92
|
+
|
|
93
|
+
Args:
|
|
94
|
+
trace: Input waveform trace.
|
|
95
|
+
|
|
96
|
+
Returns:
|
|
97
|
+
List of trigger events for window crossings.
|
|
98
|
+
"""
|
|
99
|
+
data = trace.data
|
|
100
|
+
sample_period = trace.metadata.time_base
|
|
101
|
+
events: list[TriggerEvent] = []
|
|
102
|
+
|
|
103
|
+
# Determine if each sample is inside the window
|
|
104
|
+
inside = (data >= self.low_threshold) & (data <= self.high_threshold)
|
|
105
|
+
|
|
106
|
+
# Find transitions
|
|
107
|
+
for i in range(1, len(inside)):
|
|
108
|
+
if inside[i] and not inside[i - 1]:
|
|
109
|
+
# Entry event
|
|
110
|
+
if self.trigger_on in ("entry", "both"):
|
|
111
|
+
events.append(
|
|
112
|
+
TriggerEvent(
|
|
113
|
+
timestamp=i * sample_period,
|
|
114
|
+
sample_index=i,
|
|
115
|
+
event_type=TriggerType.WINDOW_ENTRY,
|
|
116
|
+
level=float(data[i]),
|
|
117
|
+
data={
|
|
118
|
+
"window": (self.low_threshold, self.high_threshold),
|
|
119
|
+
"direction": "entering",
|
|
120
|
+
},
|
|
121
|
+
)
|
|
122
|
+
)
|
|
123
|
+
|
|
124
|
+
elif not inside[i] and inside[i - 1]:
|
|
125
|
+
# Exit event
|
|
126
|
+
if self.trigger_on in ("exit", "both"):
|
|
127
|
+
# Determine which boundary was crossed
|
|
128
|
+
boundary = "high" if data[i] > self.high_threshold else "low"
|
|
129
|
+
events.append(
|
|
130
|
+
TriggerEvent(
|
|
131
|
+
timestamp=i * sample_period,
|
|
132
|
+
sample_index=i,
|
|
133
|
+
event_type=TriggerType.WINDOW_EXIT,
|
|
134
|
+
level=float(data[i]),
|
|
135
|
+
data={
|
|
136
|
+
"window": (self.low_threshold, self.high_threshold),
|
|
137
|
+
"direction": "exiting",
|
|
138
|
+
"boundary": boundary,
|
|
139
|
+
},
|
|
140
|
+
)
|
|
141
|
+
)
|
|
142
|
+
|
|
143
|
+
return events
|
|
144
|
+
|
|
145
|
+
|
|
146
|
+
class ZoneTrigger(Trigger):
|
|
147
|
+
"""Zone trigger for multiple defined voltage/time zones.
|
|
148
|
+
|
|
149
|
+
Triggers when signal enters any of the defined zones. Useful for
|
|
150
|
+
mask testing and compliance checking.
|
|
151
|
+
|
|
152
|
+
Attributes:
|
|
153
|
+
zones: List of Zone definitions.
|
|
154
|
+
trigger_on: When to trigger - "entry", "exit", or "violation".
|
|
155
|
+
"""
|
|
156
|
+
|
|
157
|
+
def __init__(
|
|
158
|
+
self,
|
|
159
|
+
zones: list[Zone],
|
|
160
|
+
trigger_on: Literal["entry", "exit", "violation"] = "violation",
|
|
161
|
+
) -> None:
|
|
162
|
+
"""Initialize zone trigger.
|
|
163
|
+
|
|
164
|
+
Args:
|
|
165
|
+
zones: List of zones to monitor.
|
|
166
|
+
trigger_on: "entry" for entering zones, "exit" for leaving,
|
|
167
|
+
"violation" is alias for "entry" (common use case).
|
|
168
|
+
"""
|
|
169
|
+
self.zones = zones
|
|
170
|
+
self.trigger_on = trigger_on
|
|
171
|
+
|
|
172
|
+
def find_events(
|
|
173
|
+
self,
|
|
174
|
+
trace: WaveformTrace, # type: ignore[override]
|
|
175
|
+
) -> list[TriggerEvent]:
|
|
176
|
+
"""Find all zone-related events.
|
|
177
|
+
|
|
178
|
+
Args:
|
|
179
|
+
trace: Input waveform trace.
|
|
180
|
+
|
|
181
|
+
Returns:
|
|
182
|
+
List of trigger events.
|
|
183
|
+
"""
|
|
184
|
+
data = trace.data
|
|
185
|
+
sample_period = trace.metadata.time_base
|
|
186
|
+
time_vector = np.arange(len(data)) * sample_period
|
|
187
|
+
events: list[TriggerEvent] = []
|
|
188
|
+
|
|
189
|
+
for zone in self.zones:
|
|
190
|
+
# Check time limits
|
|
191
|
+
if zone.start_time is not None:
|
|
192
|
+
time_mask = time_vector >= zone.start_time
|
|
193
|
+
else:
|
|
194
|
+
time_mask = np.ones(len(data), dtype=bool)
|
|
195
|
+
|
|
196
|
+
if zone.end_time is not None:
|
|
197
|
+
time_mask &= time_vector <= zone.end_time
|
|
198
|
+
|
|
199
|
+
# Check voltage limits
|
|
200
|
+
in_zone = (data >= zone.low) & (data <= zone.high) & time_mask
|
|
201
|
+
|
|
202
|
+
# Find transitions
|
|
203
|
+
for i in range(1, len(in_zone)):
|
|
204
|
+
if in_zone[i] and not in_zone[i - 1]:
|
|
205
|
+
# Entry event
|
|
206
|
+
if self.trigger_on in ("entry", "violation"):
|
|
207
|
+
events.append(
|
|
208
|
+
TriggerEvent(
|
|
209
|
+
timestamp=i * sample_period,
|
|
210
|
+
sample_index=i,
|
|
211
|
+
event_type=TriggerType.ZONE_VIOLATION,
|
|
212
|
+
level=float(data[i]),
|
|
213
|
+
data={
|
|
214
|
+
"zone_name": zone.name,
|
|
215
|
+
"zone_bounds": (zone.low, zone.high),
|
|
216
|
+
"direction": "entering",
|
|
217
|
+
},
|
|
218
|
+
)
|
|
219
|
+
)
|
|
220
|
+
|
|
221
|
+
elif not in_zone[i] and in_zone[i - 1]:
|
|
222
|
+
# Exit event
|
|
223
|
+
if self.trigger_on == "exit":
|
|
224
|
+
events.append(
|
|
225
|
+
TriggerEvent(
|
|
226
|
+
timestamp=i * sample_period,
|
|
227
|
+
sample_index=i,
|
|
228
|
+
event_type=TriggerType.ZONE_VIOLATION,
|
|
229
|
+
level=float(data[i]),
|
|
230
|
+
data={
|
|
231
|
+
"zone_name": zone.name,
|
|
232
|
+
"zone_bounds": (zone.low, zone.high),
|
|
233
|
+
"direction": "exiting",
|
|
234
|
+
},
|
|
235
|
+
)
|
|
236
|
+
)
|
|
237
|
+
|
|
238
|
+
# Sort by timestamp
|
|
239
|
+
events.sort(key=lambda e: e.timestamp)
|
|
240
|
+
return events
|
|
241
|
+
|
|
242
|
+
|
|
243
|
+
def find_window_violations(
|
|
244
|
+
trace: WaveformTrace,
|
|
245
|
+
low: float,
|
|
246
|
+
high: float,
|
|
247
|
+
) -> list[TriggerEvent]:
|
|
248
|
+
"""Find all window violations (signal outside limits).
|
|
249
|
+
|
|
250
|
+
Args:
|
|
251
|
+
trace: Input waveform trace.
|
|
252
|
+
low: Lower limit.
|
|
253
|
+
high: Upper limit.
|
|
254
|
+
|
|
255
|
+
Returns:
|
|
256
|
+
List of trigger events for each exit from the window.
|
|
257
|
+
|
|
258
|
+
Example:
|
|
259
|
+
>>> # Check if signal stays within 0-3.3V
|
|
260
|
+
>>> violations = find_window_violations(trace, low=0, high=3.3)
|
|
261
|
+
>>> if violations:
|
|
262
|
+
... print(f"Signal violated limits {len(violations)} times")
|
|
263
|
+
"""
|
|
264
|
+
trigger = WindowTrigger(
|
|
265
|
+
low_threshold=low,
|
|
266
|
+
high_threshold=high,
|
|
267
|
+
trigger_on="exit",
|
|
268
|
+
)
|
|
269
|
+
return trigger.find_events(trace)
|
|
270
|
+
|
|
271
|
+
|
|
272
|
+
def find_zone_events(
|
|
273
|
+
trace: WaveformTrace,
|
|
274
|
+
zones: list[tuple[float, float] | Zone],
|
|
275
|
+
) -> list[TriggerEvent]:
|
|
276
|
+
"""Find events where signal enters defined zones.
|
|
277
|
+
|
|
278
|
+
Args:
|
|
279
|
+
trace: Input waveform trace.
|
|
280
|
+
zones: List of zones as (low, high) tuples or Zone objects.
|
|
281
|
+
|
|
282
|
+
Returns:
|
|
283
|
+
List of trigger events.
|
|
284
|
+
|
|
285
|
+
Example:
|
|
286
|
+
>>> # Define forbidden zones
|
|
287
|
+
>>> zones = [
|
|
288
|
+
... (0.8, 1.2), # Metastable region around 1V
|
|
289
|
+
... (3.5, 5.0), # Overvoltage region
|
|
290
|
+
... ]
|
|
291
|
+
>>> events = find_zone_events(trace, zones)
|
|
292
|
+
"""
|
|
293
|
+
zone_objs: list[Zone] = []
|
|
294
|
+
for i, z in enumerate(zones):
|
|
295
|
+
if isinstance(z, Zone):
|
|
296
|
+
zone_objs.append(z)
|
|
297
|
+
else:
|
|
298
|
+
zone_objs.append(Zone(low=z[0], high=z[1], name=f"zone_{i}"))
|
|
299
|
+
|
|
300
|
+
trigger = ZoneTrigger(zones=zone_objs, trigger_on="violation")
|
|
301
|
+
return trigger.find_events(trace)
|
|
302
|
+
|
|
303
|
+
|
|
304
|
+
def check_limits(
|
|
305
|
+
trace: WaveformTrace,
|
|
306
|
+
low: float,
|
|
307
|
+
high: float,
|
|
308
|
+
) -> dict: # type: ignore[type-arg]
|
|
309
|
+
"""Check if trace stays within voltage limits.
|
|
310
|
+
|
|
311
|
+
Args:
|
|
312
|
+
trace: Input waveform trace.
|
|
313
|
+
low: Lower limit.
|
|
314
|
+
high: Upper limit.
|
|
315
|
+
|
|
316
|
+
Returns:
|
|
317
|
+
Dictionary with:
|
|
318
|
+
- passed: True if no violations
|
|
319
|
+
- violations: List of violation events
|
|
320
|
+
- min_value: Minimum value in trace
|
|
321
|
+
- max_value: Maximum value in trace
|
|
322
|
+
- time_in_spec: Percentage of time within limits
|
|
323
|
+
- time_out_of_spec: Percentage of time outside limits
|
|
324
|
+
|
|
325
|
+
Example:
|
|
326
|
+
>>> result = check_limits(trace, low=0, high=3.3)
|
|
327
|
+
>>> if result['passed']:
|
|
328
|
+
... print("Signal within limits")
|
|
329
|
+
>>> else:
|
|
330
|
+
... print(f"{result['time_out_of_spec']:.1f}% of time out of spec")
|
|
331
|
+
"""
|
|
332
|
+
violations = find_window_violations(trace, low, high)
|
|
333
|
+
|
|
334
|
+
data = trace.data
|
|
335
|
+
min_val = float(np.min(data))
|
|
336
|
+
max_val = float(np.max(data))
|
|
337
|
+
|
|
338
|
+
# Calculate time in/out of spec
|
|
339
|
+
in_spec = (data >= low) & (data <= high)
|
|
340
|
+
pct_in_spec = np.sum(in_spec) / len(data) * 100
|
|
341
|
+
pct_out_spec = 100 - pct_in_spec
|
|
342
|
+
|
|
343
|
+
return {
|
|
344
|
+
"passed": len(violations) == 0 and min_val >= low and max_val <= high,
|
|
345
|
+
"violations": violations,
|
|
346
|
+
"min_value": min_val,
|
|
347
|
+
"max_value": max_val,
|
|
348
|
+
"time_in_spec": pct_in_spec,
|
|
349
|
+
"time_out_of_spec": pct_out_spec,
|
|
350
|
+
}
|
|
351
|
+
|
|
352
|
+
|
|
353
|
+
class MaskTrigger(Trigger):
|
|
354
|
+
"""Mask trigger for eye diagram and waveform mask testing.
|
|
355
|
+
|
|
356
|
+
Tests waveform against a defined mask (polygonal region).
|
|
357
|
+
Triggers on any mask violation.
|
|
358
|
+
"""
|
|
359
|
+
|
|
360
|
+
def __init__(
|
|
361
|
+
self,
|
|
362
|
+
mask_points: list[tuple[float, float]],
|
|
363
|
+
mode: Literal["inside", "outside"] = "inside",
|
|
364
|
+
) -> None:
|
|
365
|
+
"""Initialize mask trigger.
|
|
366
|
+
|
|
367
|
+
Args:
|
|
368
|
+
mask_points: List of (time, voltage) points defining mask polygon.
|
|
369
|
+
mode: "inside" triggers when signal is inside mask,
|
|
370
|
+
"outside" triggers when signal is outside mask.
|
|
371
|
+
|
|
372
|
+
Raises:
|
|
373
|
+
AnalysisError: If mask has fewer than 3 points.
|
|
374
|
+
"""
|
|
375
|
+
if len(mask_points) < 3:
|
|
376
|
+
raise AnalysisError("Mask must have at least 3 points")
|
|
377
|
+
|
|
378
|
+
self.mask_points = mask_points
|
|
379
|
+
self.mode = mode
|
|
380
|
+
|
|
381
|
+
def find_events(
|
|
382
|
+
self,
|
|
383
|
+
trace: WaveformTrace, # type: ignore[override]
|
|
384
|
+
) -> list[TriggerEvent]:
|
|
385
|
+
"""Find mask violations.
|
|
386
|
+
|
|
387
|
+
Args:
|
|
388
|
+
trace: Input waveform trace.
|
|
389
|
+
|
|
390
|
+
Returns:
|
|
391
|
+
List of trigger events for mask violations.
|
|
392
|
+
"""
|
|
393
|
+
from matplotlib.path import Path
|
|
394
|
+
|
|
395
|
+
# Create polygon path
|
|
396
|
+
mask_path = Path(self.mask_points)
|
|
397
|
+
|
|
398
|
+
data = trace.data
|
|
399
|
+
sample_period = trace.metadata.time_base
|
|
400
|
+
time_vector = np.arange(len(data)) * sample_period
|
|
401
|
+
|
|
402
|
+
# Create points array for containment test
|
|
403
|
+
points = np.column_stack([time_vector, data])
|
|
404
|
+
|
|
405
|
+
# Check which points are inside the mask
|
|
406
|
+
inside = mask_path.contains_points(points)
|
|
407
|
+
|
|
408
|
+
events: list[TriggerEvent] = []
|
|
409
|
+
|
|
410
|
+
# Find violations based on mode
|
|
411
|
+
if self.mode == "inside":
|
|
412
|
+
# Trigger when inside mask (mask defines forbidden region)
|
|
413
|
+
violation_indices = np.where(inside)[0]
|
|
414
|
+
else:
|
|
415
|
+
# Trigger when outside mask (mask defines required region)
|
|
416
|
+
violation_indices = np.where(~inside)[0]
|
|
417
|
+
|
|
418
|
+
# Group consecutive violations into events
|
|
419
|
+
if len(violation_indices) > 0:
|
|
420
|
+
# Find starts of violation regions
|
|
421
|
+
starts = [violation_indices[0]]
|
|
422
|
+
for i in range(1, len(violation_indices)):
|
|
423
|
+
if violation_indices[i] != violation_indices[i - 1] + 1:
|
|
424
|
+
starts.append(violation_indices[i])
|
|
425
|
+
|
|
426
|
+
for start_idx in starts:
|
|
427
|
+
events.append(
|
|
428
|
+
TriggerEvent(
|
|
429
|
+
timestamp=start_idx * sample_period,
|
|
430
|
+
sample_index=int(start_idx),
|
|
431
|
+
event_type=TriggerType.ZONE_VIOLATION,
|
|
432
|
+
level=float(data[start_idx]),
|
|
433
|
+
data={
|
|
434
|
+
"mask_mode": self.mode,
|
|
435
|
+
"violation_type": "inside_forbidden"
|
|
436
|
+
if self.mode == "inside"
|
|
437
|
+
else "outside_required",
|
|
438
|
+
},
|
|
439
|
+
)
|
|
440
|
+
)
|
|
441
|
+
|
|
442
|
+
return events
|
|
443
|
+
|
|
444
|
+
|
|
445
|
+
__all__ = [
|
|
446
|
+
"MaskTrigger",
|
|
447
|
+
"WindowTrigger",
|
|
448
|
+
"Zone",
|
|
449
|
+
"ZoneTrigger",
|
|
450
|
+
"check_limits",
|
|
451
|
+
"find_window_violations",
|
|
452
|
+
"find_zone_events",
|
|
453
|
+
]
|
oscura/ui/__init__.py
ADDED
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
"""Oscura UI module.
|
|
2
|
+
|
|
3
|
+
Provides user interface patterns for progressive disclosure and formatting utilities.
|
|
4
|
+
"""
|
|
5
|
+
|
|
6
|
+
from oscura.ui.formatters import (
|
|
7
|
+
Color,
|
|
8
|
+
FormattedText,
|
|
9
|
+
TextAlignment,
|
|
10
|
+
align_text,
|
|
11
|
+
colorize,
|
|
12
|
+
format_code_block,
|
|
13
|
+
format_duration,
|
|
14
|
+
format_key_value_pairs,
|
|
15
|
+
format_list,
|
|
16
|
+
format_percentage,
|
|
17
|
+
format_size,
|
|
18
|
+
format_status,
|
|
19
|
+
format_table,
|
|
20
|
+
format_text,
|
|
21
|
+
truncate,
|
|
22
|
+
)
|
|
23
|
+
from oscura.ui.progressive_display import (
|
|
24
|
+
ProgressiveDisplay,
|
|
25
|
+
ProgressiveOutput,
|
|
26
|
+
Section,
|
|
27
|
+
)
|
|
28
|
+
|
|
29
|
+
__all__ = [
|
|
30
|
+
"Color",
|
|
31
|
+
"FormattedText",
|
|
32
|
+
"ProgressiveDisplay",
|
|
33
|
+
"ProgressiveOutput",
|
|
34
|
+
"Section",
|
|
35
|
+
"TextAlignment",
|
|
36
|
+
"align_text",
|
|
37
|
+
"colorize",
|
|
38
|
+
"format_code_block",
|
|
39
|
+
"format_duration",
|
|
40
|
+
"format_key_value_pairs",
|
|
41
|
+
"format_list",
|
|
42
|
+
"format_percentage",
|
|
43
|
+
"format_size",
|
|
44
|
+
"format_status",
|
|
45
|
+
"format_table",
|
|
46
|
+
"format_text",
|
|
47
|
+
"truncate",
|
|
48
|
+
]
|