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,280 @@
|
|
|
1
|
+
"""EMC/EMI compliance testing workflow.
|
|
2
|
+
|
|
3
|
+
This module implements spectral compliance testing against regulatory limits.
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
Example:
|
|
7
|
+
>>> import oscura as tk
|
|
8
|
+
>>> trace = tk.load('emissions.wfm')
|
|
9
|
+
>>> result = tk.emc_compliance_test(trace, standard='FCC_Part15_ClassB')
|
|
10
|
+
>>> print(f"Status: {result['status']}")
|
|
11
|
+
>>> print(f"Violations: {len(result['violations'])}")
|
|
12
|
+
|
|
13
|
+
References:
|
|
14
|
+
FCC Part 15: Radio Frequency Devices
|
|
15
|
+
CISPR 22/32: Information Technology Equipment
|
|
16
|
+
MIL-STD-461: EMI/EMC Requirements
|
|
17
|
+
"""
|
|
18
|
+
|
|
19
|
+
from __future__ import annotations
|
|
20
|
+
|
|
21
|
+
from typing import TYPE_CHECKING, Any, Literal
|
|
22
|
+
|
|
23
|
+
import numpy as np
|
|
24
|
+
|
|
25
|
+
from oscura.core.exceptions import AnalysisError
|
|
26
|
+
|
|
27
|
+
if TYPE_CHECKING:
|
|
28
|
+
from oscura.core.types import WaveformTrace
|
|
29
|
+
|
|
30
|
+
|
|
31
|
+
def emc_compliance_test(
|
|
32
|
+
trace: WaveformTrace,
|
|
33
|
+
*,
|
|
34
|
+
standard: str = "FCC_Part15_ClassB",
|
|
35
|
+
frequency_range: tuple[float, float] | None = None,
|
|
36
|
+
detector: Literal["peak", "quasi-peak", "average"] = "peak",
|
|
37
|
+
report: str | None = None,
|
|
38
|
+
) -> dict[str, Any]:
|
|
39
|
+
"""EMC/EMI compliance testing against regulatory limits.
|
|
40
|
+
|
|
41
|
+
Performs spectral compliance testing:
|
|
42
|
+
- Computes spectrum (FFT or welch)
|
|
43
|
+
- Loads regulatory limit mask
|
|
44
|
+
- Overlays limit lines on spectrum
|
|
45
|
+
- Identifies violations
|
|
46
|
+
- Generates compliance report
|
|
47
|
+
|
|
48
|
+
Args:
|
|
49
|
+
trace: Signal to test for emissions.
|
|
50
|
+
standard: Regulatory standard to test against:
|
|
51
|
+
'FCC_Part15_ClassA', 'FCC_Part15_ClassB',
|
|
52
|
+
'CE_CISPR22_ClassA', 'CE_CISPR22_ClassB',
|
|
53
|
+
'CE_CISPR32_ClassA', 'CE_CISPR32_ClassB',
|
|
54
|
+
'MIL_STD_461G_CE102', 'MIL_STD_461G_RE102'
|
|
55
|
+
frequency_range: Optional frequency range (f_min, f_max) in Hz.
|
|
56
|
+
detector: Detector type ('peak', 'quasi-peak', 'average').
|
|
57
|
+
report: Optional path to save HTML compliance report.
|
|
58
|
+
|
|
59
|
+
Returns:
|
|
60
|
+
Dictionary containing:
|
|
61
|
+
- status: 'PASS' or 'FAIL'
|
|
62
|
+
- standard: Standard tested against
|
|
63
|
+
- violations: List of frequency violations
|
|
64
|
+
- margin_to_limit: Minimum margin in dB (negative if failing)
|
|
65
|
+
- worst_frequency: Frequency with worst margin
|
|
66
|
+
- worst_margin: Worst margin value in dB
|
|
67
|
+
- spectrum_freq: Frequency array for spectrum
|
|
68
|
+
- spectrum_mag: Magnitude array for spectrum (dBµV or dBm)
|
|
69
|
+
- limit_freq: Frequency array for limit mask
|
|
70
|
+
- limit_mag: Magnitude array for limit mask
|
|
71
|
+
|
|
72
|
+
Returns:
|
|
73
|
+
Dictionary containing:
|
|
74
|
+
- status: 'PASS' or 'FAIL'
|
|
75
|
+
- standard: Standard tested against
|
|
76
|
+
- violations: List of frequency violations
|
|
77
|
+
- margin_to_limit: Minimum margin in dB (negative if failing)
|
|
78
|
+
- worst_frequency: Frequency with worst margin
|
|
79
|
+
- worst_margin: Worst margin value in dB
|
|
80
|
+
- spectrum_freq: Frequency array for spectrum
|
|
81
|
+
- spectrum_mag: Magnitude array for spectrum (dBµV or dBm)
|
|
82
|
+
- limit_freq: Frequency array for limit mask
|
|
83
|
+
- limit_mag: Magnitude array for limit mask
|
|
84
|
+
|
|
85
|
+
Example:
|
|
86
|
+
>>> trace = tk.load('radiated_emissions.wfm')
|
|
87
|
+
>>> result = tk.emc_compliance_test(trace, standard='FCC_Part15_ClassB')
|
|
88
|
+
>>> print(f"Compliance: {result['status']}")
|
|
89
|
+
>>> print(f"Margin: {result['margin_to_limit']:.1f} dB")
|
|
90
|
+
>>> if result['violations']:
|
|
91
|
+
... print(f"Violations at: {[v['frequency']/1e6 for v in result['violations']]} MHz")
|
|
92
|
+
|
|
93
|
+
References:
|
|
94
|
+
FCC Part 15 Subpart B (Unintentional Radiators)
|
|
95
|
+
CISPR 22/32 (Information Technology Equipment EMC)
|
|
96
|
+
MIL-STD-461G (Military EMC Requirements)
|
|
97
|
+
"""
|
|
98
|
+
# Import spectral analysis
|
|
99
|
+
from oscura.analyzers.waveform.spectral import fft
|
|
100
|
+
|
|
101
|
+
# Calculate spectrum
|
|
102
|
+
freq, mag_db = fft(trace) # type: ignore[misc]
|
|
103
|
+
# Note: fft() returns magnitude_db (already in dB relative to 1V)
|
|
104
|
+
|
|
105
|
+
# Convert to dBµV (typical EMC unit)
|
|
106
|
+
# mag_db is in dBV, convert to dBµV: dBµV = dBV + 120
|
|
107
|
+
# (since 1µV = 1e-6 V, and 20*log10(1e6) = 120 dB)
|
|
108
|
+
spectrum_dbuv = mag_db + 120
|
|
109
|
+
|
|
110
|
+
# Load limit mask for standard
|
|
111
|
+
limit_freq, limit_mag = _load_emc_mask(standard)
|
|
112
|
+
|
|
113
|
+
# Apply frequency range if specified
|
|
114
|
+
if frequency_range is not None:
|
|
115
|
+
f_min, f_max = frequency_range
|
|
116
|
+
mask = (freq >= f_min) & (freq <= f_max)
|
|
117
|
+
freq = freq[mask]
|
|
118
|
+
spectrum_dbuv = spectrum_dbuv[mask]
|
|
119
|
+
|
|
120
|
+
# Interpolate limit to spectrum frequencies
|
|
121
|
+
limit_interp = np.interp(freq, limit_freq, limit_mag)
|
|
122
|
+
|
|
123
|
+
# Find violations (spectrum exceeds limit)
|
|
124
|
+
margin = limit_interp - spectrum_dbuv
|
|
125
|
+
violations_mask = margin < 0
|
|
126
|
+
|
|
127
|
+
# Build violations list
|
|
128
|
+
violations = []
|
|
129
|
+
if np.any(violations_mask):
|
|
130
|
+
violation_indices = np.where(violations_mask)[0]
|
|
131
|
+
for idx in violation_indices:
|
|
132
|
+
violations.append(
|
|
133
|
+
{
|
|
134
|
+
"frequency": freq[idx],
|
|
135
|
+
"measured_dbuv": spectrum_dbuv[idx],
|
|
136
|
+
"limit_dbuv": limit_interp[idx],
|
|
137
|
+
"excess_db": -margin[idx], # Positive value for excess
|
|
138
|
+
}
|
|
139
|
+
)
|
|
140
|
+
|
|
141
|
+
# Overall status
|
|
142
|
+
status = "FAIL" if violations else "PASS"
|
|
143
|
+
|
|
144
|
+
# Margin analysis
|
|
145
|
+
margin_to_limit = np.min(margin)
|
|
146
|
+
worst_idx = np.argmin(margin)
|
|
147
|
+
worst_frequency = freq[worst_idx]
|
|
148
|
+
worst_margin = margin[worst_idx]
|
|
149
|
+
|
|
150
|
+
result = {
|
|
151
|
+
"status": status,
|
|
152
|
+
"standard": standard,
|
|
153
|
+
"violations": violations,
|
|
154
|
+
"margin_to_limit": margin_to_limit,
|
|
155
|
+
"worst_frequency": worst_frequency,
|
|
156
|
+
"worst_margin": worst_margin,
|
|
157
|
+
"spectrum_freq": freq,
|
|
158
|
+
"spectrum_mag": spectrum_dbuv,
|
|
159
|
+
"limit_freq": limit_freq,
|
|
160
|
+
"limit_mag": limit_mag,
|
|
161
|
+
"detector": detector,
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
# Generate report if requested
|
|
165
|
+
if report is not None:
|
|
166
|
+
_generate_compliance_report(result, report)
|
|
167
|
+
|
|
168
|
+
return result
|
|
169
|
+
|
|
170
|
+
|
|
171
|
+
def _load_emc_mask(
|
|
172
|
+
standard: str,
|
|
173
|
+
) -> tuple[np.ndarray[Any, np.dtype[np.float64]], np.ndarray[Any, np.dtype[np.float64]]]:
|
|
174
|
+
"""Load EMC limit mask for a standard.
|
|
175
|
+
|
|
176
|
+
Args:
|
|
177
|
+
standard: Standard name.
|
|
178
|
+
|
|
179
|
+
Returns:
|
|
180
|
+
Tuple of (frequency array, limit array in dBµV).
|
|
181
|
+
|
|
182
|
+
Raises:
|
|
183
|
+
AnalysisError: If unknown EMC standard.
|
|
184
|
+
"""
|
|
185
|
+
# Simplified mask data - real implementation would load from data files
|
|
186
|
+
masks = {
|
|
187
|
+
"FCC_Part15_ClassB": {
|
|
188
|
+
# Frequencies in MHz, limits in dBµV at 3m
|
|
189
|
+
"freq": np.array([0.15, 0.5, 5.0, 30.0, 88.0, 216.0, 1000.0]) * 1e6,
|
|
190
|
+
"limit": np.array([60, 60, 56, 46, 46, 46, 46]), # dBµV/m
|
|
191
|
+
},
|
|
192
|
+
"FCC_Part15_ClassA": {
|
|
193
|
+
"freq": np.array([0.15, 0.5, 5.0, 30.0, 88.0, 216.0, 1000.0]) * 1e6,
|
|
194
|
+
"limit": np.array([70, 70, 66, 56, 56, 56, 56]),
|
|
195
|
+
},
|
|
196
|
+
"CE_CISPR22_ClassB": {
|
|
197
|
+
"freq": np.array([0.15, 0.5, 5.0, 30.0, 230.0, 1000.0]) * 1e6,
|
|
198
|
+
"limit": np.array([66, 56, 56, 47, 47, 47]),
|
|
199
|
+
},
|
|
200
|
+
"CE_CISPR22_ClassA": {
|
|
201
|
+
"freq": np.array([0.15, 0.5, 5.0, 30.0, 230.0, 1000.0]) * 1e6,
|
|
202
|
+
"limit": np.array([79, 73, 73, 60, 60, 60]),
|
|
203
|
+
},
|
|
204
|
+
"CE_CISPR32_ClassB": {
|
|
205
|
+
"freq": np.array([0.15, 0.5, 5.0, 30.0, 230.0, 1000.0]) * 1e6,
|
|
206
|
+
"limit": np.array([66, 56, 56, 47, 47, 47]),
|
|
207
|
+
},
|
|
208
|
+
"CE_CISPR32_ClassA": {
|
|
209
|
+
"freq": np.array([0.15, 0.5, 5.0, 30.0, 230.0, 1000.0]) * 1e6,
|
|
210
|
+
"limit": np.array([79, 73, 73, 60, 60, 60]),
|
|
211
|
+
},
|
|
212
|
+
"MIL_STD_461G_CE102": {
|
|
213
|
+
"freq": np.array([0.01, 0.15, 10.0, 50.0]) * 1e6,
|
|
214
|
+
"limit": np.array([90, 80, 80, 80]),
|
|
215
|
+
},
|
|
216
|
+
"MIL_STD_461G_RE102": {
|
|
217
|
+
"freq": np.array([2, 30, 200, 1000, 18000]) * 1e6,
|
|
218
|
+
"limit": np.array([54, 54, 34, 34, 34]),
|
|
219
|
+
},
|
|
220
|
+
}
|
|
221
|
+
|
|
222
|
+
if standard not in masks:
|
|
223
|
+
raise AnalysisError(f"Unknown EMC standard: {standard}")
|
|
224
|
+
|
|
225
|
+
mask_data = masks[standard]
|
|
226
|
+
return mask_data["freq"], mask_data["limit"]
|
|
227
|
+
|
|
228
|
+
|
|
229
|
+
def _generate_compliance_report(result: dict[str, Any], output_path: str) -> None:
|
|
230
|
+
"""Generate HTML compliance report.
|
|
231
|
+
|
|
232
|
+
Args:
|
|
233
|
+
result: Compliance test result dictionary.
|
|
234
|
+
output_path: Path to save HTML report.
|
|
235
|
+
"""
|
|
236
|
+
status_color = "green" if result["status"] == "PASS" else "red"
|
|
237
|
+
|
|
238
|
+
html = f"""
|
|
239
|
+
<html>
|
|
240
|
+
<head><title>EMC Compliance Report</title></head>
|
|
241
|
+
<body>
|
|
242
|
+
<h1>EMC Compliance Test Report</h1>
|
|
243
|
+
<h2>Standard: {result["standard"]}</h2>
|
|
244
|
+
<h2 style="color: {status_color}">Status: {result["status"]}</h2>
|
|
245
|
+
|
|
246
|
+
<h3>Summary</h3>
|
|
247
|
+
<table>
|
|
248
|
+
<tr><th>Parameter</th><th>Value</th></tr>
|
|
249
|
+
<tr><td>Margin to Limit</td><td>{result["margin_to_limit"]:.2f} dB</td></tr>
|
|
250
|
+
<tr><td>Worst Frequency</td><td>{result["worst_frequency"] / 1e6:.2f} MHz</td></tr>
|
|
251
|
+
<tr><td>Worst Margin</td><td>{result["worst_margin"]:.2f} dB</td></tr>
|
|
252
|
+
<tr><td>Violations</td><td>{len(result["violations"])}</td></tr>
|
|
253
|
+
</table>
|
|
254
|
+
"""
|
|
255
|
+
if result["violations"]:
|
|
256
|
+
html += """
|
|
257
|
+
<h3>Violations</h3>
|
|
258
|
+
<table>
|
|
259
|
+
<tr><th>Frequency (MHz)</th><th>Measured (dBµV)</th><th>Limit (dBµV)</th><th>Excess (dB)</th></tr>
|
|
260
|
+
"""
|
|
261
|
+
for v in result["violations"]:
|
|
262
|
+
html += f"""
|
|
263
|
+
<tr>
|
|
264
|
+
<td>{v["frequency"] / 1e6:.2f}</td>
|
|
265
|
+
<td>{v["measured_dbuv"]:.2f}</td>
|
|
266
|
+
<td>{v["limit_dbuv"]:.2f}</td>
|
|
267
|
+
<td>{v["excess_db"]:.2f}</td>
|
|
268
|
+
</tr>
|
|
269
|
+
"""
|
|
270
|
+
html += "</table>"
|
|
271
|
+
|
|
272
|
+
html += """
|
|
273
|
+
</body>
|
|
274
|
+
</html>
|
|
275
|
+
"""
|
|
276
|
+
with open(output_path, "w") as f:
|
|
277
|
+
f.write(html)
|
|
278
|
+
|
|
279
|
+
|
|
280
|
+
__all__ = ["emc_compliance_test"]
|
|
@@ -0,0 +1,272 @@
|
|
|
1
|
+
"""Digital buffer characterization workflow.
|
|
2
|
+
|
|
3
|
+
This module implements complete TTL/CMOS buffer characterization in a single
|
|
4
|
+
function call, with automatic logic family detection.
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
Example:
|
|
8
|
+
>>> import oscura as tk
|
|
9
|
+
>>> trace = tk.load('74hc04_output.wfm')
|
|
10
|
+
>>> result = tk.characterize_buffer(trace)
|
|
11
|
+
>>> print(f"Logic Family: {result['logic_family']}")
|
|
12
|
+
>>> print(f"Rise Time: {result['rise_time']:.2f} ns")
|
|
13
|
+
>>> print(f"Status: {result['status']}")
|
|
14
|
+
|
|
15
|
+
References:
|
|
16
|
+
IEEE 181-2011: Standard for Transitional Waveform Definitions
|
|
17
|
+
JEDEC Standard No. 65B: High-Speed Interface Timing
|
|
18
|
+
"""
|
|
19
|
+
|
|
20
|
+
from __future__ import annotations
|
|
21
|
+
|
|
22
|
+
from typing import TYPE_CHECKING, Any
|
|
23
|
+
|
|
24
|
+
import numpy as np
|
|
25
|
+
|
|
26
|
+
from oscura.core.exceptions import AnalysisError
|
|
27
|
+
|
|
28
|
+
if TYPE_CHECKING:
|
|
29
|
+
from oscura.core.types import WaveformTrace
|
|
30
|
+
|
|
31
|
+
|
|
32
|
+
def characterize_buffer(
|
|
33
|
+
trace: WaveformTrace,
|
|
34
|
+
*,
|
|
35
|
+
reference_trace: WaveformTrace | None = None,
|
|
36
|
+
logic_family: str | None = None,
|
|
37
|
+
thresholds: dict[str, float] | None = None,
|
|
38
|
+
report: str | None = None,
|
|
39
|
+
) -> dict[str, Any]:
|
|
40
|
+
"""Characterize digital buffer timing and quality.
|
|
41
|
+
|
|
42
|
+
One-call characterization of digital buffer including:
|
|
43
|
+
- Automatic logic family detection (if not specified)
|
|
44
|
+
- Rise/fall time measurements
|
|
45
|
+
- Propagation delay (if reference provided)
|
|
46
|
+
- Overshoot/undershoot analysis
|
|
47
|
+
- Noise margin calculation
|
|
48
|
+
- Pass/fail against logic family specifications
|
|
49
|
+
|
|
50
|
+
Args:
|
|
51
|
+
trace: Output signal to characterize.
|
|
52
|
+
reference_trace: Optional reference (input) signal for propagation delay.
|
|
53
|
+
logic_family: Logic family override (e.g., 'TTL', 'CMOS_3V3', 'CMOS_5V').
|
|
54
|
+
If None, auto-detected from signal levels.
|
|
55
|
+
thresholds: Optional dict of custom pass/fail thresholds
|
|
56
|
+
(e.g., {'rise_time': 10e-9} for 10 ns max).
|
|
57
|
+
report: Optional path to save HTML report.
|
|
58
|
+
|
|
59
|
+
Returns:
|
|
60
|
+
Dictionary containing:
|
|
61
|
+
- logic_family: Detected or specified logic family
|
|
62
|
+
- rise_time: 10%-90% rise time in seconds
|
|
63
|
+
- fall_time: 10%-90% fall time in seconds
|
|
64
|
+
- propagation_delay: Delay from reference (if provided), in seconds
|
|
65
|
+
- overshoot: Peak overshoot voltage
|
|
66
|
+
- overshoot_percent: Overshoot as percentage of swing
|
|
67
|
+
- undershoot: Peak undershoot voltage
|
|
68
|
+
- undershoot_percent: Undershoot as percentage of swing
|
|
69
|
+
- noise_margin_high: High-level noise margin in volts
|
|
70
|
+
- noise_margin_low: Low-level noise margin in volts
|
|
71
|
+
- status: 'PASS' or 'FAIL' based on logic family specs
|
|
72
|
+
- reference_comparison: Dict with timing drift if reference provided
|
|
73
|
+
- confidence: Confidence score for logic family detection (0-1)
|
|
74
|
+
|
|
75
|
+
Raises:
|
|
76
|
+
AnalysisError: If trace has insufficient transitions for analysis.
|
|
77
|
+
|
|
78
|
+
Example:
|
|
79
|
+
>>> trace = tk.load('74hc04_output.wfm')
|
|
80
|
+
>>> result = tk.characterize_buffer(trace, logic_family='CMOS_3V3')
|
|
81
|
+
>>> print(f"Rise Time: {result['rise_time']*1e9:.2f} ns")
|
|
82
|
+
>>> print(f"Status: {result['status']}")
|
|
83
|
+
|
|
84
|
+
References:
|
|
85
|
+
IEEE 181-2011 Section 5.2 (Edge timing)
|
|
86
|
+
JEDEC Standard No. 65B (Logic family specifications)
|
|
87
|
+
"""
|
|
88
|
+
# Import here to avoid circular dependencies
|
|
89
|
+
from oscura.analyzers.waveform.measurements import (
|
|
90
|
+
fall_time,
|
|
91
|
+
overshoot,
|
|
92
|
+
rise_time,
|
|
93
|
+
undershoot,
|
|
94
|
+
)
|
|
95
|
+
from oscura.inference.logic import detect_logic_family
|
|
96
|
+
|
|
97
|
+
# Auto-detect logic family if not specified
|
|
98
|
+
if logic_family is None:
|
|
99
|
+
detection = detect_logic_family(trace)
|
|
100
|
+
logic_family = detection["primary"]["name"]
|
|
101
|
+
confidence = detection["primary"]["confidence"]
|
|
102
|
+
voh = detection["primary"]["voh"]
|
|
103
|
+
vol = detection["primary"]["vol"]
|
|
104
|
+
else:
|
|
105
|
+
confidence = 1.0
|
|
106
|
+
# Measure VOH/VOL from trace
|
|
107
|
+
voh = np.percentile(trace.data, 95)
|
|
108
|
+
vol = np.percentile(trace.data, 5)
|
|
109
|
+
|
|
110
|
+
# Measure timing parameters
|
|
111
|
+
try:
|
|
112
|
+
t_rise = rise_time(trace)
|
|
113
|
+
t_fall = fall_time(trace)
|
|
114
|
+
except Exception as e:
|
|
115
|
+
raise AnalysisError(f"Failed to measure rise/fall time: {e}") from e
|
|
116
|
+
|
|
117
|
+
# Measure overshoot/undershoot
|
|
118
|
+
v_overshoot = overshoot(trace)
|
|
119
|
+
v_undershoot = undershoot(trace)
|
|
120
|
+
|
|
121
|
+
# Calculate percentages
|
|
122
|
+
swing = voh - vol
|
|
123
|
+
if swing > 0:
|
|
124
|
+
overshoot_pct = (v_overshoot / swing) * 100.0
|
|
125
|
+
undershoot_pct = (v_undershoot / swing) * 100.0
|
|
126
|
+
else:
|
|
127
|
+
overshoot_pct = 0.0
|
|
128
|
+
undershoot_pct = 0.0
|
|
129
|
+
|
|
130
|
+
# Calculate noise margins (simplified - uses typical values)
|
|
131
|
+
# In a real implementation, these would come from LOGIC_FAMILIES constants
|
|
132
|
+
logic_specs = _get_logic_specs(logic_family)
|
|
133
|
+
noise_margin_high = voh - logic_specs["vih"]
|
|
134
|
+
noise_margin_low = logic_specs["vil"] - vol
|
|
135
|
+
|
|
136
|
+
# Propagation delay if reference provided
|
|
137
|
+
propagation_delay = None
|
|
138
|
+
timing_drift = None
|
|
139
|
+
if reference_trace is not None:
|
|
140
|
+
try:
|
|
141
|
+
from oscura.analyzers.digital.timing import (
|
|
142
|
+
propagation_delay as prop_delay,
|
|
143
|
+
)
|
|
144
|
+
|
|
145
|
+
propagation_delay = prop_delay(reference_trace, trace)
|
|
146
|
+
except Exception:
|
|
147
|
+
# If propagation delay measurement fails, set to None
|
|
148
|
+
propagation_delay = None
|
|
149
|
+
|
|
150
|
+
# Apply thresholds and determine pass/fail
|
|
151
|
+
status = "PASS"
|
|
152
|
+
if thresholds is not None:
|
|
153
|
+
if "rise_time" in thresholds and t_rise > thresholds["rise_time"]:
|
|
154
|
+
status = "FAIL"
|
|
155
|
+
if "fall_time" in thresholds and t_fall > thresholds["fall_time"]:
|
|
156
|
+
status = "FAIL"
|
|
157
|
+
if "overshoot_percent" in thresholds and overshoot_pct > thresholds["overshoot_percent"]:
|
|
158
|
+
status = "FAIL"
|
|
159
|
+
else:
|
|
160
|
+
# Use logic family defaults
|
|
161
|
+
if t_rise > logic_specs.get("max_rise_time", float("inf")):
|
|
162
|
+
status = "FAIL"
|
|
163
|
+
if t_fall > logic_specs.get("max_fall_time", float("inf")):
|
|
164
|
+
status = "FAIL"
|
|
165
|
+
|
|
166
|
+
# Build result dictionary
|
|
167
|
+
result = {
|
|
168
|
+
"logic_family": logic_family,
|
|
169
|
+
"confidence": confidence,
|
|
170
|
+
"rise_time": t_rise,
|
|
171
|
+
"fall_time": t_fall,
|
|
172
|
+
"propagation_delay": propagation_delay,
|
|
173
|
+
"overshoot": v_overshoot,
|
|
174
|
+
"overshoot_percent": overshoot_pct,
|
|
175
|
+
"undershoot": v_undershoot,
|
|
176
|
+
"undershoot_percent": undershoot_pct,
|
|
177
|
+
"noise_margin_high": noise_margin_high,
|
|
178
|
+
"noise_margin_low": noise_margin_low,
|
|
179
|
+
"voh": voh,
|
|
180
|
+
"vol": vol,
|
|
181
|
+
"status": status,
|
|
182
|
+
"reference_comparison": None,
|
|
183
|
+
}
|
|
184
|
+
|
|
185
|
+
if reference_trace is not None and propagation_delay is not None:
|
|
186
|
+
result["reference_comparison"] = {
|
|
187
|
+
"propagation_delay": propagation_delay,
|
|
188
|
+
"timing_drift": timing_drift,
|
|
189
|
+
}
|
|
190
|
+
|
|
191
|
+
# Generate report if requested
|
|
192
|
+
if report is not None:
|
|
193
|
+
_generate_buffer_report(result, report)
|
|
194
|
+
|
|
195
|
+
return result
|
|
196
|
+
|
|
197
|
+
|
|
198
|
+
def _get_logic_specs(family: str) -> dict[str, float]:
|
|
199
|
+
"""Get specifications for a logic family.
|
|
200
|
+
|
|
201
|
+
Args:
|
|
202
|
+
family: Logic family name.
|
|
203
|
+
|
|
204
|
+
Returns:
|
|
205
|
+
Dict with VIH, VIL, and timing specs.
|
|
206
|
+
"""
|
|
207
|
+
specs = {
|
|
208
|
+
"TTL": {
|
|
209
|
+
"vih": 2.0,
|
|
210
|
+
"vil": 0.8,
|
|
211
|
+
"max_rise_time": 10e-9,
|
|
212
|
+
"max_fall_time": 10e-9,
|
|
213
|
+
},
|
|
214
|
+
"CMOS_5V": {
|
|
215
|
+
"vih": 3.5,
|
|
216
|
+
"vil": 1.5,
|
|
217
|
+
"max_rise_time": 15e-9,
|
|
218
|
+
"max_fall_time": 15e-9,
|
|
219
|
+
},
|
|
220
|
+
"CMOS_3V3": {
|
|
221
|
+
"vih": 2.0,
|
|
222
|
+
"vil": 0.8,
|
|
223
|
+
"max_rise_time": 5e-9,
|
|
224
|
+
"max_fall_time": 5e-9,
|
|
225
|
+
},
|
|
226
|
+
"LVTTL": {
|
|
227
|
+
"vih": 2.0,
|
|
228
|
+
"vil": 0.8,
|
|
229
|
+
"max_rise_time": 3e-9,
|
|
230
|
+
"max_fall_time": 3e-9,
|
|
231
|
+
},
|
|
232
|
+
"LVCMOS": {
|
|
233
|
+
"vih": 1.7,
|
|
234
|
+
"vil": 0.7,
|
|
235
|
+
"max_rise_time": 2e-9,
|
|
236
|
+
"max_fall_time": 2e-9,
|
|
237
|
+
},
|
|
238
|
+
}
|
|
239
|
+
return specs.get(family, specs["CMOS_3V3"])
|
|
240
|
+
|
|
241
|
+
|
|
242
|
+
def _generate_buffer_report(result: dict[str, Any], output_path: str) -> None:
|
|
243
|
+
"""Generate HTML report for buffer characterization.
|
|
244
|
+
|
|
245
|
+
Args:
|
|
246
|
+
result: Characterization result dictionary.
|
|
247
|
+
output_path: Path to save HTML report.
|
|
248
|
+
"""
|
|
249
|
+
# Simplified report generation - in real implementation would use
|
|
250
|
+
# oscura.reporting module
|
|
251
|
+
html = f"""
|
|
252
|
+
<html>
|
|
253
|
+
<head><title>Buffer Characterization Report</title></head>
|
|
254
|
+
<body>
|
|
255
|
+
<h1>Buffer Characterization Report</h1>
|
|
256
|
+
<h2>Logic Family: {result["logic_family"]} (confidence: {result["confidence"]:.1%})</h2>
|
|
257
|
+
<table>
|
|
258
|
+
<tr><th>Parameter</th><th>Value</th><th>Units</th></tr>
|
|
259
|
+
<tr><td>Rise Time</td><td>{result["rise_time"] * 1e9:.2f}</td><td>ns</td></tr>
|
|
260
|
+
<tr><td>Fall Time</td><td>{result["fall_time"] * 1e9:.2f}</td><td>ns</td></tr>
|
|
261
|
+
<tr><td>Overshoot</td><td>{result["overshoot_percent"]:.1f}</td><td>%</td></tr>
|
|
262
|
+
<tr><td>Undershoot</td><td>{result["undershoot_percent"]:.1f}</td><td>%</td></tr>
|
|
263
|
+
<tr><td>Status</td><td><b>{result["status"]}</b></td><td></td></tr>
|
|
264
|
+
</table>
|
|
265
|
+
</body>
|
|
266
|
+
</html>
|
|
267
|
+
"""
|
|
268
|
+
with open(output_path, "w") as f:
|
|
269
|
+
f.write(html)
|
|
270
|
+
|
|
271
|
+
|
|
272
|
+
__all__ = ["characterize_buffer"]
|