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,333 @@
|
|
|
1
|
+
"""BER-related jitter analysis functions.
|
|
2
|
+
|
|
3
|
+
This module provides total jitter at BER calculations, bathtub curve
|
|
4
|
+
generation, and eye opening measurements using the dual-Dirac model.
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
Example:
|
|
8
|
+
>>> from oscura.analyzers.jitter.ber import tj_at_ber, bathtub_curve
|
|
9
|
+
>>> tj = tj_at_ber(rj_rms=1e-12, dj_pp=10e-12, ber=1e-12)
|
|
10
|
+
>>> positions, ber_values = bathtub_curve(tie_data, unit_interval=1e-9)
|
|
11
|
+
|
|
12
|
+
References:
|
|
13
|
+
IEEE 2414-2020: Standard for Jitter and Phase Noise
|
|
14
|
+
"""
|
|
15
|
+
|
|
16
|
+
from __future__ import annotations
|
|
17
|
+
|
|
18
|
+
from dataclasses import dataclass
|
|
19
|
+
from typing import TYPE_CHECKING
|
|
20
|
+
|
|
21
|
+
import numpy as np
|
|
22
|
+
from scipy import special
|
|
23
|
+
|
|
24
|
+
if TYPE_CHECKING:
|
|
25
|
+
from numpy.typing import NDArray
|
|
26
|
+
|
|
27
|
+
|
|
28
|
+
@dataclass
|
|
29
|
+
class BathtubCurveResult:
|
|
30
|
+
"""Result of bathtub curve generation.
|
|
31
|
+
|
|
32
|
+
Attributes:
|
|
33
|
+
positions: Sampling positions in UI (0.0 to 1.0).
|
|
34
|
+
ber_left: BER values for left side of eye.
|
|
35
|
+
ber_right: BER values for right side of eye.
|
|
36
|
+
ber_total: Combined BER (left + right).
|
|
37
|
+
eye_opening: Eye opening in UI at specified target BER.
|
|
38
|
+
target_ber: Target BER for eye opening calculation.
|
|
39
|
+
unit_interval: Unit interval in seconds.
|
|
40
|
+
"""
|
|
41
|
+
|
|
42
|
+
positions: NDArray[np.float64]
|
|
43
|
+
ber_left: NDArray[np.float64]
|
|
44
|
+
ber_right: NDArray[np.float64]
|
|
45
|
+
ber_total: NDArray[np.float64]
|
|
46
|
+
eye_opening: float
|
|
47
|
+
target_ber: float
|
|
48
|
+
unit_interval: float
|
|
49
|
+
|
|
50
|
+
|
|
51
|
+
def q_factor_from_ber(ber: float) -> float:
|
|
52
|
+
"""Calculate Q-factor from target BER.
|
|
53
|
+
|
|
54
|
+
The Q-factor is the number of standard deviations from the mean
|
|
55
|
+
for a Gaussian distribution to achieve the target BER.
|
|
56
|
+
|
|
57
|
+
Args:
|
|
58
|
+
ber: Bit error rate (e.g., 1e-12).
|
|
59
|
+
|
|
60
|
+
Returns:
|
|
61
|
+
Q-factor value.
|
|
62
|
+
|
|
63
|
+
Example:
|
|
64
|
+
>>> q = q_factor_from_ber(1e-12)
|
|
65
|
+
>>> print(f"Q(1e-12) = {q:.3f}") # ~7.034
|
|
66
|
+
|
|
67
|
+
References:
|
|
68
|
+
IEEE 2414-2020: Q = sqrt(2) * erfc_inv(2 * BER)
|
|
69
|
+
"""
|
|
70
|
+
if ber <= 0 or ber >= 0.5:
|
|
71
|
+
return np.nan # type: ignore[no-any-return]
|
|
72
|
+
|
|
73
|
+
# BER = 0.5 * erfc(Q / sqrt(2))
|
|
74
|
+
# erfc(Q / sqrt(2)) = 2 * BER
|
|
75
|
+
# Q / sqrt(2) = erfc_inv(2 * BER)
|
|
76
|
+
# Q = sqrt(2) * erfc_inv(2 * BER)
|
|
77
|
+
|
|
78
|
+
q = np.sqrt(2) * special.erfcinv(2 * ber)
|
|
79
|
+
return float(q)
|
|
80
|
+
|
|
81
|
+
|
|
82
|
+
def ber_from_q_factor(q: float) -> float:
|
|
83
|
+
"""Calculate BER from Q-factor.
|
|
84
|
+
|
|
85
|
+
Args:
|
|
86
|
+
q: Q-factor value.
|
|
87
|
+
|
|
88
|
+
Returns:
|
|
89
|
+
Bit error rate.
|
|
90
|
+
|
|
91
|
+
Example:
|
|
92
|
+
>>> ber = ber_from_q_factor(7.034)
|
|
93
|
+
>>> print(f"BER = {ber:.2e}") # ~1e-12
|
|
94
|
+
"""
|
|
95
|
+
if q <= 0:
|
|
96
|
+
return 0.5
|
|
97
|
+
|
|
98
|
+
# BER = 0.5 * erfc(Q / sqrt(2))
|
|
99
|
+
ber = 0.5 * special.erfc(q / np.sqrt(2))
|
|
100
|
+
return float(ber)
|
|
101
|
+
|
|
102
|
+
|
|
103
|
+
def tj_at_ber(
|
|
104
|
+
rj_rms: float,
|
|
105
|
+
dj_pp: float,
|
|
106
|
+
ber: float = 1e-12,
|
|
107
|
+
) -> float:
|
|
108
|
+
"""Calculate total jitter at specified BER using dual-Dirac model.
|
|
109
|
+
|
|
110
|
+
The dual-Dirac model combines random and deterministic jitter:
|
|
111
|
+
TJ(BER) = 2 * Q(BER) * RJ_rms + DJ_pp
|
|
112
|
+
|
|
113
|
+
Common Q values:
|
|
114
|
+
- Q(1e-12) = 7.034
|
|
115
|
+
- Q(1e-15) = 7.941
|
|
116
|
+
|
|
117
|
+
Args:
|
|
118
|
+
rj_rms: Random jitter RMS in seconds.
|
|
119
|
+
dj_pp: Deterministic jitter peak-to-peak in seconds.
|
|
120
|
+
ber: Target bit error rate (default 1e-12).
|
|
121
|
+
|
|
122
|
+
Returns:
|
|
123
|
+
Total jitter in seconds at specified BER.
|
|
124
|
+
|
|
125
|
+
Raises:
|
|
126
|
+
ValueError: If rj_rms is negative.
|
|
127
|
+
|
|
128
|
+
Example:
|
|
129
|
+
>>> tj = tj_at_ber(rj_rms=1e-12, dj_pp=10e-12, ber=1e-12)
|
|
130
|
+
>>> print(f"TJ@1e-12: {tj * 1e12:.2f} ps")
|
|
131
|
+
|
|
132
|
+
References:
|
|
133
|
+
IEEE 2414-2020 Section 6.6
|
|
134
|
+
"""
|
|
135
|
+
if rj_rms < 0:
|
|
136
|
+
raise ValueError("RJ must be non-negative")
|
|
137
|
+
|
|
138
|
+
if dj_pp < 0:
|
|
139
|
+
raise ValueError("DJ must be non-negative")
|
|
140
|
+
|
|
141
|
+
q = q_factor_from_ber(ber)
|
|
142
|
+
|
|
143
|
+
if np.isnan(q):
|
|
144
|
+
return np.nan # type: ignore[no-any-return]
|
|
145
|
+
|
|
146
|
+
# TJ = 2 * Q * RJ_rms + DJ_pp
|
|
147
|
+
tj = 2 * q * rj_rms + dj_pp
|
|
148
|
+
|
|
149
|
+
return tj
|
|
150
|
+
|
|
151
|
+
|
|
152
|
+
def bathtub_curve(
|
|
153
|
+
tie_data: NDArray[np.float64],
|
|
154
|
+
unit_interval: float,
|
|
155
|
+
*,
|
|
156
|
+
n_points: int = 100,
|
|
157
|
+
target_ber: float = 1e-12,
|
|
158
|
+
rj_rms: float | None = None,
|
|
159
|
+
dj_delta: float | None = None,
|
|
160
|
+
) -> BathtubCurveResult:
|
|
161
|
+
"""Generate bathtub curve showing BER vs. sampling position.
|
|
162
|
+
|
|
163
|
+
The bathtub curve shows how bit error rate varies across the
|
|
164
|
+
unit interval, with low BER in the center (eye opening) and
|
|
165
|
+
high BER near the edges.
|
|
166
|
+
|
|
167
|
+
Args:
|
|
168
|
+
tie_data: Time Interval Error data in seconds.
|
|
169
|
+
unit_interval: Unit interval (bit period) in seconds.
|
|
170
|
+
n_points: Number of points in the curve.
|
|
171
|
+
target_ber: Target BER for eye opening calculation.
|
|
172
|
+
rj_rms: Pre-computed RJ (extracted from data if None).
|
|
173
|
+
dj_delta: Pre-computed DJ delta (extracted from data if None).
|
|
174
|
+
|
|
175
|
+
Returns:
|
|
176
|
+
BathtubCurveResult with position and BER arrays.
|
|
177
|
+
|
|
178
|
+
Example:
|
|
179
|
+
>>> result = bathtub_curve(tie_data, unit_interval=1e-9)
|
|
180
|
+
>>> print(f"Eye opening: {result.eye_opening:.3f} UI at BER=1e-12")
|
|
181
|
+
|
|
182
|
+
References:
|
|
183
|
+
IEEE 2414-2020 Section 6.7
|
|
184
|
+
"""
|
|
185
|
+
from oscura.analyzers.jitter.decomposition import extract_dj, extract_rj
|
|
186
|
+
|
|
187
|
+
valid_data = tie_data[~np.isnan(tie_data)]
|
|
188
|
+
|
|
189
|
+
# Normalize TIE to UI
|
|
190
|
+
valid_data / unit_interval
|
|
191
|
+
|
|
192
|
+
# Extract jitter components if not provided
|
|
193
|
+
if rj_rms is None or dj_delta is None:
|
|
194
|
+
try:
|
|
195
|
+
rj_result = extract_rj(valid_data, min_samples=100)
|
|
196
|
+
rj_rms = rj_result.rj_rms
|
|
197
|
+
except Exception:
|
|
198
|
+
rj_rms = np.std(valid_data)
|
|
199
|
+
|
|
200
|
+
try:
|
|
201
|
+
dj_result = extract_dj(valid_data, min_samples=100)
|
|
202
|
+
dj_delta = dj_result.dj_delta
|
|
203
|
+
except Exception:
|
|
204
|
+
dj_delta = 0.0
|
|
205
|
+
|
|
206
|
+
# Convert to UI
|
|
207
|
+
sigma_ui = rj_rms / unit_interval
|
|
208
|
+
delta_ui = dj_delta / unit_interval
|
|
209
|
+
|
|
210
|
+
# Generate sampling positions (0 to 1 UI)
|
|
211
|
+
positions = np.linspace(0, 1, n_points)
|
|
212
|
+
|
|
213
|
+
# Calculate BER at each position using dual-Dirac model
|
|
214
|
+
# Left side: probability of sampling a '1' when '0' is sent
|
|
215
|
+
# Right side: probability of sampling a '0' when '1' is sent
|
|
216
|
+
|
|
217
|
+
# For a dual-Dirac distribution centered at 0.5 UI:
|
|
218
|
+
# Left Dirac at 0.5 - delta, Right Dirac at 0.5 + delta
|
|
219
|
+
|
|
220
|
+
ber_left = np.zeros(n_points)
|
|
221
|
+
ber_right = np.zeros(n_points)
|
|
222
|
+
|
|
223
|
+
for i, pos in enumerate(positions):
|
|
224
|
+
# Left BER: Q-function from left edge
|
|
225
|
+
if sigma_ui > 0:
|
|
226
|
+
# Distance from left edge to sampling point
|
|
227
|
+
q_left = (pos - delta_ui) / sigma_ui
|
|
228
|
+
ber_left[i] = 0.5 * special.erfc(q_left / np.sqrt(2))
|
|
229
|
+
|
|
230
|
+
# Distance from right edge to sampling point
|
|
231
|
+
q_right = (1 - pos - delta_ui) / sigma_ui
|
|
232
|
+
ber_right[i] = 0.5 * special.erfc(q_right / np.sqrt(2))
|
|
233
|
+
else:
|
|
234
|
+
# No random jitter - step function
|
|
235
|
+
ber_left[i] = 0.5 if pos <= delta_ui else 0
|
|
236
|
+
ber_right[i] = 0.5 if pos >= (1 - delta_ui) else 0
|
|
237
|
+
|
|
238
|
+
# Total BER is sum of left and right
|
|
239
|
+
ber_total = ber_left + ber_right
|
|
240
|
+
|
|
241
|
+
# Clip to valid range
|
|
242
|
+
ber_total = np.clip(ber_total, 1e-18, 0.5)
|
|
243
|
+
ber_left = np.clip(ber_left, 1e-18, 0.5)
|
|
244
|
+
ber_right = np.clip(ber_right, 1e-18, 0.5)
|
|
245
|
+
|
|
246
|
+
# Calculate eye opening at target BER
|
|
247
|
+
eye_opening = _calculate_eye_opening(positions, ber_total, target_ber)
|
|
248
|
+
|
|
249
|
+
return BathtubCurveResult(
|
|
250
|
+
positions=positions,
|
|
251
|
+
ber_left=ber_left,
|
|
252
|
+
ber_right=ber_right,
|
|
253
|
+
ber_total=ber_total,
|
|
254
|
+
eye_opening=eye_opening,
|
|
255
|
+
target_ber=target_ber,
|
|
256
|
+
unit_interval=unit_interval,
|
|
257
|
+
)
|
|
258
|
+
|
|
259
|
+
|
|
260
|
+
def _calculate_eye_opening(
|
|
261
|
+
positions: NDArray[np.float64],
|
|
262
|
+
ber: NDArray[np.float64],
|
|
263
|
+
target_ber: float,
|
|
264
|
+
) -> float:
|
|
265
|
+
"""Calculate eye opening at target BER.
|
|
266
|
+
|
|
267
|
+
Args:
|
|
268
|
+
positions: Sampling positions in UI.
|
|
269
|
+
ber: BER values at each position.
|
|
270
|
+
target_ber: Target BER for eye opening.
|
|
271
|
+
|
|
272
|
+
Returns:
|
|
273
|
+
Eye opening in UI.
|
|
274
|
+
"""
|
|
275
|
+
# Find positions where BER <= target_ber
|
|
276
|
+
valid_positions = positions[ber <= target_ber]
|
|
277
|
+
|
|
278
|
+
if len(valid_positions) == 0:
|
|
279
|
+
return 0.0
|
|
280
|
+
|
|
281
|
+
# Eye opening is the range of valid positions
|
|
282
|
+
eye_opening = float(np.max(valid_positions) - np.min(valid_positions))
|
|
283
|
+
|
|
284
|
+
return eye_opening
|
|
285
|
+
|
|
286
|
+
|
|
287
|
+
def eye_opening_at_ber(
|
|
288
|
+
rj_rms: float,
|
|
289
|
+
dj_pp: float,
|
|
290
|
+
unit_interval: float,
|
|
291
|
+
target_ber: float = 1e-12,
|
|
292
|
+
) -> float:
|
|
293
|
+
"""Calculate horizontal eye opening at target BER.
|
|
294
|
+
|
|
295
|
+
Uses the dual-Dirac model to calculate the eye opening width
|
|
296
|
+
at a specified BER level.
|
|
297
|
+
|
|
298
|
+
Args:
|
|
299
|
+
rj_rms: Random jitter RMS in seconds.
|
|
300
|
+
dj_pp: Deterministic jitter peak-to-peak in seconds.
|
|
301
|
+
unit_interval: Unit interval in seconds.
|
|
302
|
+
target_ber: Target BER level.
|
|
303
|
+
|
|
304
|
+
Returns:
|
|
305
|
+
Eye opening in UI (0.0 to 1.0).
|
|
306
|
+
|
|
307
|
+
Example:
|
|
308
|
+
>>> opening = eye_opening_at_ber(1e-12, 10e-12, 100e-12, 1e-12)
|
|
309
|
+
>>> print(f"Eye opening: {opening:.3f} UI")
|
|
310
|
+
"""
|
|
311
|
+
# Total jitter at BER
|
|
312
|
+
tj = tj_at_ber(rj_rms, dj_pp, target_ber)
|
|
313
|
+
|
|
314
|
+
# Eye opening = UI - TJ
|
|
315
|
+
opening_seconds = unit_interval - tj
|
|
316
|
+
|
|
317
|
+
if opening_seconds <= 0:
|
|
318
|
+
return 0.0
|
|
319
|
+
|
|
320
|
+
# Convert to UI
|
|
321
|
+
opening_ui = opening_seconds / unit_interval
|
|
322
|
+
|
|
323
|
+
return max(0.0, min(1.0, opening_ui))
|
|
324
|
+
|
|
325
|
+
|
|
326
|
+
__all__ = [
|
|
327
|
+
"BathtubCurveResult",
|
|
328
|
+
"bathtub_curve",
|
|
329
|
+
"ber_from_q_factor",
|
|
330
|
+
"eye_opening_at_ber",
|
|
331
|
+
"q_factor_from_ber",
|
|
332
|
+
"tj_at_ber",
|
|
333
|
+
]
|