oscura 0.5.0__py3-none-any.whl → 0.6.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 +169 -167
- oscura/analyzers/__init__.py +3 -0
- oscura/analyzers/classification.py +659 -0
- oscura/analyzers/digital/__init__.py +0 -48
- oscura/analyzers/digital/edges.py +325 -65
- oscura/analyzers/digital/extraction.py +0 -195
- oscura/analyzers/digital/quality.py +293 -166
- oscura/analyzers/digital/timing.py +260 -115
- oscura/analyzers/digital/timing_numba.py +334 -0
- oscura/analyzers/entropy.py +605 -0
- oscura/analyzers/eye/diagram.py +176 -109
- oscura/analyzers/eye/metrics.py +5 -5
- oscura/analyzers/jitter/__init__.py +6 -4
- oscura/analyzers/jitter/ber.py +52 -52
- oscura/analyzers/jitter/classification.py +156 -0
- oscura/analyzers/jitter/decomposition.py +163 -113
- oscura/analyzers/jitter/spectrum.py +80 -64
- oscura/analyzers/ml/__init__.py +39 -0
- oscura/analyzers/ml/features.py +600 -0
- oscura/analyzers/ml/signal_classifier.py +604 -0
- oscura/analyzers/packet/daq.py +246 -158
- oscura/analyzers/packet/parser.py +12 -1
- oscura/analyzers/packet/payload.py +50 -2110
- oscura/analyzers/packet/payload_analysis.py +361 -181
- oscura/analyzers/packet/payload_patterns.py +133 -70
- oscura/analyzers/packet/stream.py +84 -23
- oscura/analyzers/patterns/__init__.py +26 -5
- oscura/analyzers/patterns/anomaly_detection.py +908 -0
- oscura/analyzers/patterns/clustering.py +169 -108
- oscura/analyzers/patterns/clustering_optimized.py +227 -0
- oscura/analyzers/patterns/discovery.py +1 -1
- oscura/analyzers/patterns/matching.py +581 -197
- oscura/analyzers/patterns/pattern_mining.py +778 -0
- oscura/analyzers/patterns/periodic.py +121 -38
- oscura/analyzers/patterns/sequences.py +175 -78
- oscura/analyzers/power/conduction.py +1 -1
- oscura/analyzers/power/soa.py +6 -6
- oscura/analyzers/power/switching.py +250 -110
- oscura/analyzers/protocol/__init__.py +17 -1
- oscura/analyzers/protocols/__init__.py +1 -22
- oscura/analyzers/protocols/base.py +6 -6
- oscura/analyzers/protocols/ble/__init__.py +38 -0
- oscura/analyzers/protocols/ble/analyzer.py +809 -0
- oscura/analyzers/protocols/ble/uuids.py +288 -0
- oscura/analyzers/protocols/can.py +257 -127
- oscura/analyzers/protocols/can_fd.py +107 -80
- oscura/analyzers/protocols/flexray.py +139 -80
- oscura/analyzers/protocols/hdlc.py +93 -58
- oscura/analyzers/protocols/i2c.py +247 -106
- oscura/analyzers/protocols/i2s.py +138 -86
- oscura/analyzers/protocols/industrial/__init__.py +40 -0
- oscura/analyzers/protocols/industrial/bacnet/__init__.py +33 -0
- oscura/analyzers/protocols/industrial/bacnet/analyzer.py +708 -0
- oscura/analyzers/protocols/industrial/bacnet/encoding.py +412 -0
- oscura/analyzers/protocols/industrial/bacnet/services.py +622 -0
- oscura/analyzers/protocols/industrial/ethercat/__init__.py +30 -0
- oscura/analyzers/protocols/industrial/ethercat/analyzer.py +474 -0
- oscura/analyzers/protocols/industrial/ethercat/mailbox.py +339 -0
- oscura/analyzers/protocols/industrial/ethercat/topology.py +166 -0
- oscura/analyzers/protocols/industrial/modbus/__init__.py +31 -0
- oscura/analyzers/protocols/industrial/modbus/analyzer.py +525 -0
- oscura/analyzers/protocols/industrial/modbus/crc.py +79 -0
- oscura/analyzers/protocols/industrial/modbus/functions.py +436 -0
- oscura/analyzers/protocols/industrial/opcua/__init__.py +21 -0
- oscura/analyzers/protocols/industrial/opcua/analyzer.py +552 -0
- oscura/analyzers/protocols/industrial/opcua/datatypes.py +446 -0
- oscura/analyzers/protocols/industrial/opcua/services.py +264 -0
- oscura/analyzers/protocols/industrial/profinet/__init__.py +23 -0
- oscura/analyzers/protocols/industrial/profinet/analyzer.py +441 -0
- oscura/analyzers/protocols/industrial/profinet/dcp.py +263 -0
- oscura/analyzers/protocols/industrial/profinet/ptcp.py +200 -0
- oscura/analyzers/protocols/jtag.py +180 -98
- oscura/analyzers/protocols/lin.py +219 -114
- oscura/analyzers/protocols/manchester.py +4 -4
- oscura/analyzers/protocols/onewire.py +253 -149
- oscura/analyzers/protocols/parallel_bus/__init__.py +20 -0
- oscura/analyzers/protocols/parallel_bus/centronics.py +92 -0
- oscura/analyzers/protocols/parallel_bus/gpib.py +137 -0
- oscura/analyzers/protocols/spi.py +192 -95
- oscura/analyzers/protocols/swd.py +321 -167
- oscura/analyzers/protocols/uart.py +267 -125
- oscura/analyzers/protocols/usb.py +235 -131
- oscura/analyzers/side_channel/power.py +17 -12
- oscura/analyzers/signal/__init__.py +15 -0
- oscura/analyzers/signal/timing_analysis.py +1086 -0
- oscura/analyzers/signal_integrity/__init__.py +4 -1
- oscura/analyzers/signal_integrity/sparams.py +2 -19
- oscura/analyzers/spectral/chunked.py +129 -60
- oscura/analyzers/spectral/chunked_fft.py +300 -94
- oscura/analyzers/spectral/chunked_wavelet.py +100 -80
- oscura/analyzers/statistical/checksum.py +376 -217
- oscura/analyzers/statistical/classification.py +229 -107
- oscura/analyzers/statistical/entropy.py +78 -53
- oscura/analyzers/statistics/correlation.py +407 -211
- oscura/analyzers/statistics/outliers.py +2 -2
- oscura/analyzers/statistics/streaming.py +30 -5
- oscura/analyzers/validation.py +216 -101
- oscura/analyzers/waveform/measurements.py +9 -0
- oscura/analyzers/waveform/measurements_with_uncertainty.py +31 -15
- oscura/analyzers/waveform/spectral.py +500 -228
- oscura/api/__init__.py +31 -5
- oscura/api/dsl/__init__.py +582 -0
- oscura/{dsl → api/dsl}/commands.py +43 -76
- oscura/{dsl → api/dsl}/interpreter.py +26 -51
- oscura/{dsl → api/dsl}/parser.py +107 -77
- oscura/{dsl → api/dsl}/repl.py +2 -2
- oscura/api/dsl.py +1 -1
- oscura/{integrations → api/integrations}/__init__.py +1 -1
- oscura/{integrations → api/integrations}/llm.py +201 -102
- oscura/api/operators.py +3 -3
- oscura/api/optimization.py +144 -30
- oscura/api/rest_server.py +921 -0
- oscura/api/server/__init__.py +17 -0
- oscura/api/server/dashboard.py +850 -0
- oscura/api/server/static/README.md +34 -0
- oscura/api/server/templates/base.html +181 -0
- oscura/api/server/templates/export.html +120 -0
- oscura/api/server/templates/home.html +284 -0
- oscura/api/server/templates/protocols.html +58 -0
- oscura/api/server/templates/reports.html +43 -0
- oscura/api/server/templates/session_detail.html +89 -0
- oscura/api/server/templates/sessions.html +83 -0
- oscura/api/server/templates/waveforms.html +73 -0
- oscura/automotive/__init__.py +8 -1
- oscura/automotive/can/__init__.py +10 -0
- oscura/automotive/can/checksum.py +3 -1
- oscura/automotive/can/dbc_generator.py +590 -0
- oscura/automotive/can/message_wrapper.py +121 -74
- oscura/automotive/can/patterns.py +98 -21
- oscura/automotive/can/session.py +292 -56
- oscura/automotive/can/state_machine.py +6 -3
- oscura/automotive/can/stimulus_response.py +97 -75
- oscura/automotive/dbc/__init__.py +10 -2
- oscura/automotive/dbc/generator.py +84 -56
- oscura/automotive/dbc/parser.py +6 -6
- oscura/automotive/dtc/data.json +2763 -0
- oscura/automotive/dtc/database.py +2 -2
- oscura/automotive/flexray/__init__.py +31 -0
- oscura/automotive/flexray/analyzer.py +504 -0
- oscura/automotive/flexray/crc.py +185 -0
- oscura/automotive/flexray/fibex.py +449 -0
- oscura/automotive/j1939/__init__.py +45 -8
- oscura/automotive/j1939/analyzer.py +605 -0
- oscura/automotive/j1939/spns.py +326 -0
- oscura/automotive/j1939/transport.py +306 -0
- oscura/automotive/lin/__init__.py +47 -0
- oscura/automotive/lin/analyzer.py +612 -0
- oscura/automotive/loaders/blf.py +13 -2
- oscura/automotive/loaders/csv_can.py +143 -72
- oscura/automotive/loaders/dispatcher.py +50 -2
- oscura/automotive/loaders/mdf.py +86 -45
- oscura/automotive/loaders/pcap.py +111 -61
- oscura/automotive/uds/__init__.py +4 -0
- oscura/automotive/uds/analyzer.py +725 -0
- oscura/automotive/uds/decoder.py +140 -58
- oscura/automotive/uds/models.py +7 -1
- oscura/automotive/visualization.py +1 -1
- oscura/cli/analyze.py +348 -0
- oscura/cli/batch.py +142 -122
- oscura/cli/benchmark.py +275 -0
- oscura/cli/characterize.py +137 -82
- oscura/cli/compare.py +224 -131
- oscura/cli/completion.py +250 -0
- oscura/cli/config_cmd.py +361 -0
- oscura/cli/decode.py +164 -87
- oscura/cli/export.py +286 -0
- oscura/cli/main.py +115 -31
- oscura/{onboarding → cli/onboarding}/__init__.py +3 -3
- oscura/{onboarding → cli/onboarding}/help.py +80 -58
- oscura/{onboarding → cli/onboarding}/tutorials.py +97 -72
- oscura/{onboarding → cli/onboarding}/wizard.py +55 -36
- oscura/cli/progress.py +147 -0
- oscura/cli/shell.py +157 -135
- oscura/cli/validate_cmd.py +204 -0
- oscura/cli/visualize.py +158 -0
- oscura/convenience.py +125 -79
- oscura/core/__init__.py +4 -2
- oscura/core/backend_selector.py +3 -3
- oscura/core/cache.py +126 -15
- oscura/core/cancellation.py +1 -1
- oscura/{config → core/config}/__init__.py +20 -11
- oscura/{config → core/config}/defaults.py +1 -1
- oscura/{config → core/config}/loader.py +7 -5
- oscura/{config → core/config}/memory.py +5 -5
- oscura/{config → core/config}/migration.py +1 -1
- oscura/{config → core/config}/pipeline.py +99 -23
- oscura/{config → core/config}/preferences.py +1 -1
- oscura/{config → core/config}/protocol.py +3 -3
- oscura/{config → core/config}/schema.py +426 -272
- oscura/{config → core/config}/settings.py +1 -1
- oscura/{config → core/config}/thresholds.py +195 -153
- oscura/core/correlation.py +5 -6
- oscura/core/cross_domain.py +0 -2
- oscura/core/debug.py +9 -5
- oscura/{extensibility → core/extensibility}/docs.py +158 -70
- oscura/{extensibility → core/extensibility}/extensions.py +160 -76
- oscura/{extensibility → core/extensibility}/logging.py +1 -1
- oscura/{extensibility → core/extensibility}/measurements.py +1 -1
- oscura/{extensibility → core/extensibility}/plugins.py +1 -1
- oscura/{extensibility → core/extensibility}/templates.py +73 -3
- oscura/{extensibility → core/extensibility}/validation.py +1 -1
- oscura/core/gpu_backend.py +11 -7
- oscura/core/log_query.py +101 -11
- oscura/core/logging.py +126 -54
- oscura/core/logging_advanced.py +5 -5
- oscura/core/memory_limits.py +108 -70
- oscura/core/memory_monitor.py +2 -2
- oscura/core/memory_progress.py +7 -7
- oscura/core/memory_warnings.py +1 -1
- oscura/core/numba_backend.py +13 -13
- oscura/{plugins → core/plugins}/__init__.py +9 -9
- oscura/{plugins → core/plugins}/base.py +7 -7
- oscura/{plugins → core/plugins}/cli.py +3 -3
- oscura/{plugins → core/plugins}/discovery.py +186 -106
- oscura/{plugins → core/plugins}/lifecycle.py +1 -1
- oscura/{plugins → core/plugins}/manager.py +7 -7
- oscura/{plugins → core/plugins}/registry.py +3 -3
- oscura/{plugins → core/plugins}/versioning.py +1 -1
- oscura/core/progress.py +16 -1
- oscura/core/provenance.py +8 -2
- oscura/{schemas → core/schemas}/__init__.py +2 -2
- oscura/core/schemas/bus_configuration.json +322 -0
- oscura/core/schemas/device_mapping.json +182 -0
- oscura/core/schemas/packet_format.json +418 -0
- oscura/core/schemas/protocol_definition.json +363 -0
- oscura/core/types.py +4 -0
- oscura/core/uncertainty.py +3 -3
- oscura/correlation/__init__.py +52 -0
- oscura/correlation/multi_protocol.py +811 -0
- oscura/discovery/auto_decoder.py +117 -35
- oscura/discovery/comparison.py +191 -86
- oscura/discovery/quality_validator.py +155 -68
- oscura/discovery/signal_detector.py +196 -79
- oscura/export/__init__.py +18 -20
- oscura/export/kaitai_struct.py +513 -0
- oscura/export/scapy_layer.py +801 -0
- oscura/export/wireshark/README.md +15 -15
- oscura/export/wireshark/generator.py +1 -1
- oscura/export/wireshark/templates/dissector.lua.j2 +2 -2
- oscura/export/wireshark_dissector.py +746 -0
- oscura/guidance/wizard.py +207 -111
- oscura/hardware/__init__.py +19 -0
- oscura/{acquisition → hardware/acquisition}/__init__.py +4 -4
- oscura/{acquisition → hardware/acquisition}/file.py +2 -2
- oscura/{acquisition → hardware/acquisition}/hardware.py +7 -7
- oscura/{acquisition → hardware/acquisition}/saleae.py +15 -12
- oscura/{acquisition → hardware/acquisition}/socketcan.py +1 -1
- oscura/{acquisition → hardware/acquisition}/streaming.py +2 -2
- oscura/{acquisition → hardware/acquisition}/synthetic.py +3 -3
- oscura/{acquisition → hardware/acquisition}/visa.py +33 -11
- oscura/hardware/firmware/__init__.py +29 -0
- oscura/hardware/firmware/pattern_recognition.py +874 -0
- oscura/hardware/hal_detector.py +736 -0
- oscura/hardware/security/__init__.py +37 -0
- oscura/hardware/security/side_channel_detector.py +1126 -0
- oscura/inference/__init__.py +4 -0
- oscura/inference/active_learning/README.md +7 -7
- oscura/inference/active_learning/observation_table.py +4 -1
- oscura/inference/alignment.py +216 -123
- oscura/inference/bayesian.py +113 -33
- oscura/inference/crc_reverse.py +101 -55
- oscura/inference/logic.py +6 -2
- oscura/inference/message_format.py +342 -183
- oscura/inference/protocol.py +95 -44
- oscura/inference/protocol_dsl.py +180 -82
- oscura/inference/signal_intelligence.py +1439 -706
- oscura/inference/spectral.py +99 -57
- oscura/inference/state_machine.py +810 -158
- oscura/inference/stream.py +270 -110
- oscura/iot/__init__.py +34 -0
- oscura/iot/coap/__init__.py +32 -0
- oscura/iot/coap/analyzer.py +668 -0
- oscura/iot/coap/options.py +212 -0
- oscura/iot/lorawan/__init__.py +21 -0
- oscura/iot/lorawan/crypto.py +206 -0
- oscura/iot/lorawan/decoder.py +801 -0
- oscura/iot/lorawan/mac_commands.py +341 -0
- oscura/iot/mqtt/__init__.py +27 -0
- oscura/iot/mqtt/analyzer.py +999 -0
- oscura/iot/mqtt/properties.py +315 -0
- oscura/iot/zigbee/__init__.py +31 -0
- oscura/iot/zigbee/analyzer.py +615 -0
- oscura/iot/zigbee/security.py +153 -0
- oscura/iot/zigbee/zcl.py +349 -0
- oscura/jupyter/display.py +125 -45
- oscura/{exploratory → jupyter/exploratory}/__init__.py +8 -8
- oscura/{exploratory → jupyter/exploratory}/error_recovery.py +298 -141
- oscura/jupyter/exploratory/fuzzy.py +746 -0
- oscura/{exploratory → jupyter/exploratory}/fuzzy_advanced.py +258 -100
- oscura/{exploratory → jupyter/exploratory}/legacy.py +464 -242
- oscura/{exploratory → jupyter/exploratory}/parse.py +167 -145
- oscura/{exploratory → jupyter/exploratory}/recovery.py +119 -87
- oscura/jupyter/exploratory/sync.py +612 -0
- oscura/{exploratory → jupyter/exploratory}/unknown.py +299 -176
- oscura/jupyter/magic.py +4 -4
- oscura/{ui → jupyter/ui}/__init__.py +2 -2
- oscura/{ui → jupyter/ui}/formatters.py +3 -3
- oscura/{ui → jupyter/ui}/progressive_display.py +153 -82
- oscura/loaders/__init__.py +171 -63
- oscura/loaders/binary.py +88 -1
- oscura/loaders/chipwhisperer.py +153 -137
- oscura/loaders/configurable.py +208 -86
- oscura/loaders/csv_loader.py +458 -215
- oscura/loaders/hdf5_loader.py +278 -119
- oscura/loaders/lazy.py +87 -54
- oscura/loaders/mmap_loader.py +1 -1
- oscura/loaders/numpy_loader.py +253 -116
- oscura/loaders/pcap.py +226 -151
- oscura/loaders/rigol.py +110 -49
- oscura/loaders/sigrok.py +201 -78
- oscura/loaders/tdms.py +81 -58
- oscura/loaders/tektronix.py +291 -174
- oscura/loaders/touchstone.py +182 -87
- oscura/loaders/vcd.py +215 -117
- oscura/loaders/wav.py +155 -68
- oscura/reporting/__init__.py +9 -7
- oscura/reporting/analyze.py +352 -146
- oscura/reporting/argument_preparer.py +69 -14
- oscura/reporting/auto_report.py +97 -61
- oscura/reporting/batch.py +131 -58
- oscura/reporting/chart_selection.py +57 -45
- oscura/reporting/comparison.py +63 -17
- oscura/reporting/content/executive.py +76 -24
- oscura/reporting/core_formats/multi_format.py +11 -8
- oscura/reporting/engine.py +312 -158
- oscura/reporting/enhanced_reports.py +949 -0
- oscura/reporting/export.py +86 -43
- oscura/reporting/formatting/numbers.py +69 -42
- oscura/reporting/html.py +139 -58
- oscura/reporting/index.py +137 -65
- oscura/reporting/output.py +158 -67
- oscura/reporting/pdf.py +67 -102
- oscura/reporting/plots.py +191 -112
- oscura/reporting/sections.py +88 -47
- oscura/reporting/standards.py +104 -61
- oscura/reporting/summary_generator.py +75 -55
- oscura/reporting/tables.py +138 -54
- oscura/reporting/templates/enhanced/protocol_re.html +525 -0
- oscura/reporting/templates/index.md +13 -13
- oscura/sessions/__init__.py +14 -23
- oscura/sessions/base.py +3 -3
- oscura/sessions/blackbox.py +106 -10
- oscura/sessions/generic.py +2 -2
- oscura/sessions/legacy.py +783 -0
- oscura/side_channel/__init__.py +63 -0
- oscura/side_channel/dpa.py +1025 -0
- oscura/utils/__init__.py +15 -1
- oscura/utils/autodetect.py +1 -5
- oscura/utils/bitwise.py +118 -0
- oscura/{builders → utils/builders}/__init__.py +1 -1
- oscura/{comparison → utils/comparison}/__init__.py +6 -6
- oscura/{comparison → utils/comparison}/compare.py +202 -101
- oscura/{comparison → utils/comparison}/golden.py +83 -63
- oscura/{comparison → utils/comparison}/limits.py +313 -89
- oscura/{comparison → utils/comparison}/mask.py +151 -45
- oscura/{comparison → utils/comparison}/trace_diff.py +1 -1
- oscura/{comparison → utils/comparison}/visualization.py +147 -89
- oscura/{component → utils/component}/__init__.py +3 -3
- oscura/{component → utils/component}/impedance.py +122 -58
- oscura/{component → utils/component}/reactive.py +165 -168
- oscura/{component → utils/component}/transmission_line.py +3 -3
- oscura/{filtering → utils/filtering}/__init__.py +6 -6
- oscura/{filtering → utils/filtering}/base.py +1 -1
- oscura/{filtering → utils/filtering}/convenience.py +2 -2
- oscura/{filtering → utils/filtering}/design.py +169 -93
- oscura/{filtering → utils/filtering}/filters.py +2 -2
- oscura/{filtering → utils/filtering}/introspection.py +2 -2
- oscura/utils/geometry.py +31 -0
- oscura/utils/imports.py +184 -0
- oscura/utils/lazy.py +1 -1
- oscura/{math → utils/math}/__init__.py +2 -2
- oscura/{math → utils/math}/arithmetic.py +114 -48
- oscura/{math → utils/math}/interpolation.py +139 -106
- oscura/utils/memory.py +129 -66
- oscura/utils/memory_advanced.py +92 -9
- oscura/utils/memory_extensions.py +10 -8
- oscura/{optimization → utils/optimization}/__init__.py +1 -1
- oscura/{optimization → utils/optimization}/search.py +2 -2
- oscura/utils/performance/__init__.py +58 -0
- oscura/utils/performance/caching.py +889 -0
- oscura/utils/performance/lsh_clustering.py +333 -0
- oscura/utils/performance/memory_optimizer.py +699 -0
- oscura/utils/performance/optimizations.py +675 -0
- oscura/utils/performance/parallel.py +654 -0
- oscura/utils/performance/profiling.py +661 -0
- oscura/{pipeline → utils/pipeline}/base.py +1 -1
- oscura/{pipeline → utils/pipeline}/composition.py +11 -3
- oscura/{pipeline → utils/pipeline}/parallel.py +3 -2
- oscura/{pipeline → utils/pipeline}/pipeline.py +1 -1
- oscura/{pipeline → utils/pipeline}/reverse_engineering.py +412 -221
- oscura/{search → utils/search}/__init__.py +3 -3
- oscura/{search → utils/search}/anomaly.py +188 -58
- oscura/utils/search/context.py +294 -0
- oscura/{search → utils/search}/pattern.py +138 -10
- oscura/utils/serial.py +51 -0
- oscura/utils/storage/__init__.py +61 -0
- oscura/utils/storage/database.py +1166 -0
- oscura/{streaming → utils/streaming}/chunked.py +302 -143
- oscura/{streaming → utils/streaming}/progressive.py +1 -1
- oscura/{streaming → utils/streaming}/realtime.py +3 -2
- oscura/{triggering → utils/triggering}/__init__.py +6 -6
- oscura/{triggering → utils/triggering}/base.py +6 -6
- oscura/{triggering → utils/triggering}/edge.py +2 -2
- oscura/{triggering → utils/triggering}/pattern.py +2 -2
- oscura/{triggering → utils/triggering}/pulse.py +115 -74
- oscura/{triggering → utils/triggering}/window.py +2 -2
- oscura/utils/validation.py +32 -0
- oscura/validation/__init__.py +121 -0
- oscura/{compliance → validation/compliance}/__init__.py +5 -5
- oscura/{compliance → validation/compliance}/advanced.py +5 -5
- oscura/{compliance → validation/compliance}/masks.py +1 -1
- oscura/{compliance → validation/compliance}/reporting.py +127 -53
- oscura/{compliance → validation/compliance}/testing.py +114 -52
- oscura/validation/compliance_tests.py +915 -0
- oscura/validation/fuzzer.py +990 -0
- oscura/validation/grammar_tests.py +596 -0
- oscura/validation/grammar_validator.py +904 -0
- oscura/validation/hil_testing.py +977 -0
- oscura/{quality → validation/quality}/__init__.py +4 -4
- oscura/{quality → validation/quality}/ensemble.py +251 -171
- oscura/{quality → validation/quality}/explainer.py +3 -3
- oscura/{quality → validation/quality}/scoring.py +1 -1
- oscura/{quality → validation/quality}/warnings.py +4 -4
- oscura/validation/regression_suite.py +808 -0
- oscura/validation/replay.py +788 -0
- oscura/{testing → validation/testing}/__init__.py +2 -2
- oscura/{testing → validation/testing}/synthetic.py +5 -5
- oscura/visualization/__init__.py +9 -0
- oscura/visualization/accessibility.py +1 -1
- oscura/visualization/annotations.py +64 -67
- oscura/visualization/colors.py +7 -7
- oscura/visualization/digital.py +180 -81
- oscura/visualization/eye.py +236 -85
- oscura/visualization/interactive.py +320 -143
- oscura/visualization/jitter.py +587 -247
- oscura/visualization/layout.py +169 -134
- oscura/visualization/optimization.py +103 -52
- oscura/visualization/palettes.py +1 -1
- oscura/visualization/power.py +427 -211
- oscura/visualization/power_extended.py +626 -297
- oscura/visualization/presets.py +2 -0
- oscura/visualization/protocols.py +495 -181
- oscura/visualization/render.py +79 -63
- oscura/visualization/reverse_engineering.py +171 -124
- oscura/visualization/signal_integrity.py +460 -279
- oscura/visualization/specialized.py +190 -100
- oscura/visualization/spectral.py +670 -255
- oscura/visualization/thumbnails.py +166 -137
- oscura/visualization/waveform.py +150 -63
- oscura/workflows/__init__.py +3 -0
- oscura/{batch → workflows/batch}/__init__.py +5 -5
- oscura/{batch → workflows/batch}/advanced.py +150 -75
- oscura/workflows/batch/aggregate.py +531 -0
- oscura/workflows/batch/analyze.py +236 -0
- oscura/{batch → workflows/batch}/logging.py +2 -2
- oscura/{batch → workflows/batch}/metrics.py +1 -1
- oscura/workflows/complete_re.py +1144 -0
- oscura/workflows/compliance.py +44 -54
- oscura/workflows/digital.py +197 -51
- oscura/workflows/legacy/__init__.py +12 -0
- oscura/{workflow → workflows/legacy}/dag.py +4 -1
- oscura/workflows/multi_trace.py +9 -9
- oscura/workflows/power.py +42 -62
- oscura/workflows/protocol.py +82 -49
- oscura/workflows/reverse_engineering.py +351 -150
- oscura/workflows/signal_integrity.py +157 -82
- oscura-0.6.0.dist-info/METADATA +643 -0
- oscura-0.6.0.dist-info/RECORD +590 -0
- oscura/analyzers/digital/ic_database.py +0 -498
- oscura/analyzers/digital/timing_paths.py +0 -339
- oscura/analyzers/digital/vintage.py +0 -377
- oscura/analyzers/digital/vintage_result.py +0 -148
- oscura/analyzers/protocols/parallel_bus.py +0 -449
- oscura/batch/aggregate.py +0 -300
- oscura/batch/analyze.py +0 -139
- oscura/dsl/__init__.py +0 -73
- oscura/exceptions.py +0 -59
- oscura/exploratory/fuzzy.py +0 -513
- oscura/exploratory/sync.py +0 -384
- oscura/export/wavedrom.py +0 -430
- oscura/exporters/__init__.py +0 -94
- oscura/exporters/csv.py +0 -303
- oscura/exporters/exporters.py +0 -44
- oscura/exporters/hdf5.py +0 -217
- oscura/exporters/html_export.py +0 -701
- oscura/exporters/json_export.py +0 -338
- oscura/exporters/markdown_export.py +0 -367
- oscura/exporters/matlab_export.py +0 -354
- oscura/exporters/npz_export.py +0 -219
- oscura/exporters/spice_export.py +0 -210
- oscura/exporters/vintage_logic_csv.py +0 -247
- oscura/reporting/vintage_logic_report.py +0 -523
- oscura/search/context.py +0 -149
- oscura/session/__init__.py +0 -34
- oscura/session/annotations.py +0 -289
- oscura/session/history.py +0 -313
- oscura/session/session.py +0 -520
- oscura/visualization/digital_advanced.py +0 -718
- oscura/visualization/figure_manager.py +0 -156
- oscura/workflow/__init__.py +0 -13
- oscura-0.5.0.dist-info/METADATA +0 -407
- oscura-0.5.0.dist-info/RECORD +0 -486
- /oscura/core/{config.py → config/legacy.py} +0 -0
- /oscura/{extensibility → core/extensibility}/__init__.py +0 -0
- /oscura/{extensibility → core/extensibility}/registry.py +0 -0
- /oscura/{plugins → core/plugins}/isolation.py +0 -0
- /oscura/{builders → utils/builders}/signal_builder.py +0 -0
- /oscura/{optimization → utils/optimization}/parallel.py +0 -0
- /oscura/{pipeline → utils/pipeline}/__init__.py +0 -0
- /oscura/{streaming → utils/streaming}/__init__.py +0 -0
- {oscura-0.5.0.dist-info → oscura-0.6.0.dist-info}/WHEEL +0 -0
- {oscura-0.5.0.dist-info → oscura-0.6.0.dist-info}/entry_points.txt +0 -0
- {oscura-0.5.0.dist-info → oscura-0.6.0.dist-info}/licenses/LICENSE +0 -0
|
@@ -47,165 +47,246 @@ class SwitchingEvent:
|
|
|
47
47
|
event_type: Literal["turn_on", "turn_off"]
|
|
48
48
|
|
|
49
49
|
|
|
50
|
-
def
|
|
50
|
+
def _prepare_switching_data(
|
|
51
51
|
voltage: WaveformTrace,
|
|
52
52
|
current: WaveformTrace,
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
i_threshold: float | None = None,
|
|
56
|
-
) -> dict[str, Any]:
|
|
57
|
-
"""Calculate switching losses for a power device.
|
|
58
|
-
|
|
59
|
-
Analyzes voltage and current waveforms to find switching transitions
|
|
60
|
-
and calculate turn-on and turn-off energy losses.
|
|
53
|
+
) -> tuple[NDArray[np.floating[Any]], NDArray[np.floating[Any]], NDArray[np.floating[Any]], float]:
|
|
54
|
+
"""Prepare synchronized voltage, current, and power arrays.
|
|
61
55
|
|
|
62
56
|
Args:
|
|
63
|
-
voltage: Drain-source
|
|
64
|
-
current: Drain
|
|
65
|
-
v_threshold: Voltage threshold for on/off detection.
|
|
66
|
-
If None, uses 10% of peak voltage.
|
|
67
|
-
i_threshold: Current threshold for on/off detection.
|
|
68
|
-
If None, uses 10% of peak current.
|
|
57
|
+
voltage: Drain-source voltage trace.
|
|
58
|
+
current: Drain current trace.
|
|
69
59
|
|
|
70
60
|
Returns:
|
|
71
|
-
|
|
72
|
-
- e_on: Turn-on energy per event (Joules)
|
|
73
|
-
- e_off: Turn-off energy per event (Joules)
|
|
74
|
-
- e_total: Total switching energy per cycle (Joules)
|
|
75
|
-
- p_sw: Switching power at estimated frequency (Watts)
|
|
76
|
-
- events: List of SwitchingEvent objects
|
|
77
|
-
- n_turn_on: Number of turn-on events
|
|
78
|
-
- n_turn_off: Number of turn-off events
|
|
79
|
-
|
|
80
|
-
Example:
|
|
81
|
-
>>> losses = switching_loss(v_ds, i_d)
|
|
82
|
-
>>> print(f"E_on: {losses['e_on']*1e6:.2f} uJ")
|
|
83
|
-
>>> print(f"E_off: {losses['e_off']*1e6:.2f} uJ")
|
|
84
|
-
>>> print(f"Switching power @ 100kHz: {losses['p_sw']*100e3:.2f} W")
|
|
85
|
-
|
|
86
|
-
References:
|
|
87
|
-
Infineon Application Note AN-9010
|
|
61
|
+
Tuple of (v_data, i_data, p_data, sample_period).
|
|
88
62
|
"""
|
|
89
|
-
# Calculate instantaneous power
|
|
90
63
|
power = instantaneous_power(voltage, current)
|
|
91
|
-
|
|
92
|
-
# Ensure i_data matches v_data length (handle mismatched array sizes)
|
|
93
64
|
min_len = min(len(voltage.data), len(current.data))
|
|
94
65
|
v_data = voltage.data[:min_len]
|
|
95
66
|
i_data = current.data[:min_len]
|
|
96
67
|
p_data = power.data[:min_len]
|
|
97
68
|
sample_period = power.metadata.time_base
|
|
69
|
+
return v_data, i_data, p_data, sample_period
|
|
70
|
+
|
|
71
|
+
|
|
72
|
+
def _compute_thresholds(
|
|
73
|
+
v_data: NDArray[np.floating[Any]],
|
|
74
|
+
i_data: NDArray[np.floating[Any]],
|
|
75
|
+
v_threshold: float | None,
|
|
76
|
+
i_threshold: float | None,
|
|
77
|
+
hysteresis_factor: float = 0.2,
|
|
78
|
+
) -> tuple[float, float, float, float, float, float]:
|
|
79
|
+
"""Compute voltage and current thresholds with hysteresis.
|
|
80
|
+
|
|
81
|
+
Args:
|
|
82
|
+
v_data: Voltage data array.
|
|
83
|
+
i_data: Current data array.
|
|
84
|
+
v_threshold: User-provided voltage threshold (or None for auto).
|
|
85
|
+
i_threshold: User-provided current threshold (or None for auto).
|
|
86
|
+
hysteresis_factor: Hysteresis band factor (default 20%).
|
|
98
87
|
|
|
99
|
-
|
|
88
|
+
Returns:
|
|
89
|
+
Tuple of (v_threshold, i_threshold, v_high, v_low, i_high, i_low).
|
|
90
|
+
"""
|
|
100
91
|
if v_threshold is None:
|
|
101
92
|
v_threshold = 0.1 * float(np.max(np.abs(v_data)))
|
|
102
93
|
if i_threshold is None:
|
|
103
94
|
i_threshold = 0.1 * float(np.max(np.abs(i_data)))
|
|
104
95
|
|
|
105
|
-
# Add hysteresis to prevent false transitions due to ringing (Schmitt trigger)
|
|
106
|
-
# Use 20% hysteresis band around thresholds
|
|
107
|
-
hysteresis_factor = 0.2
|
|
108
96
|
v_threshold_high = v_threshold * (1 + hysteresis_factor)
|
|
109
97
|
v_threshold_low = v_threshold * (1 - hysteresis_factor)
|
|
110
98
|
i_threshold_high = i_threshold * (1 + hysteresis_factor)
|
|
111
99
|
i_threshold_low = i_threshold * (1 - hysteresis_factor)
|
|
112
100
|
|
|
113
|
-
|
|
114
|
-
|
|
101
|
+
return (
|
|
102
|
+
v_threshold,
|
|
103
|
+
i_threshold,
|
|
104
|
+
v_threshold_high,
|
|
105
|
+
v_threshold_low,
|
|
106
|
+
i_threshold_high,
|
|
107
|
+
i_threshold_low,
|
|
108
|
+
)
|
|
109
|
+
|
|
110
|
+
|
|
111
|
+
def _build_device_state(
|
|
112
|
+
v_data: NDArray[np.floating[Any]],
|
|
113
|
+
i_data: NDArray[np.floating[Any]],
|
|
114
|
+
v_high: float,
|
|
115
|
+
v_low: float,
|
|
116
|
+
i_high: float,
|
|
117
|
+
i_low: float,
|
|
118
|
+
) -> NDArray[np.signedinteger[Any]]:
|
|
119
|
+
"""Build device state array using hysteresis comparator.
|
|
115
120
|
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
121
|
+
Args:
|
|
122
|
+
v_data: Voltage data array.
|
|
123
|
+
i_data: Current data array.
|
|
124
|
+
v_high: High voltage threshold.
|
|
125
|
+
v_low: Low voltage threshold.
|
|
126
|
+
i_high: High current threshold.
|
|
127
|
+
i_low: Low current threshold.
|
|
128
|
+
|
|
129
|
+
Returns:
|
|
130
|
+
State array: 0=unknown, 1=on (low V, high I), 2=off (high V, low I).
|
|
131
|
+
"""
|
|
132
|
+
min_len = len(v_data)
|
|
133
|
+
device_state = np.zeros(min_len, dtype=int)
|
|
134
|
+
current_state = 0
|
|
122
135
|
|
|
123
136
|
for i in range(min_len):
|
|
124
137
|
v = v_data[i]
|
|
125
138
|
i_val = i_data[i]
|
|
126
139
|
|
|
127
|
-
# Determine next state based on current state and measurements
|
|
128
140
|
if current_state == 1: # Currently ON
|
|
129
|
-
|
|
130
|
-
if v > v_threshold_high:
|
|
141
|
+
if v > v_high:
|
|
131
142
|
current_state = 2 # Transition to OFF
|
|
132
143
|
elif current_state == 2: # Currently OFF
|
|
133
|
-
|
|
134
|
-
if v < v_threshold_low and i_val > i_threshold_low:
|
|
144
|
+
if v < v_low and i_val > i_low:
|
|
135
145
|
current_state = 1 # Transition to ON
|
|
136
|
-
else: # Unknown state
|
|
137
|
-
if v <
|
|
146
|
+
else: # Unknown state
|
|
147
|
+
if v < v_low and i_val > i_high:
|
|
138
148
|
current_state = 1 # ON
|
|
139
|
-
elif v >
|
|
149
|
+
elif v > v_high and i_val < i_low:
|
|
140
150
|
current_state = 2 # OFF
|
|
141
151
|
|
|
142
152
|
device_state[i] = current_state
|
|
143
153
|
|
|
154
|
+
return device_state
|
|
155
|
+
|
|
156
|
+
|
|
157
|
+
def _find_transition_events(
|
|
158
|
+
device_state: NDArray[np.signedinteger[Any]],
|
|
159
|
+
p_data: NDArray[np.floating[Any]],
|
|
160
|
+
sample_period: float,
|
|
161
|
+
) -> list[SwitchingEvent]:
|
|
162
|
+
"""Find all switching events in device state trajectory.
|
|
163
|
+
|
|
164
|
+
Args:
|
|
165
|
+
device_state: State array (0=unknown, 1=on, 2=off).
|
|
166
|
+
p_data: Power data array.
|
|
167
|
+
sample_period: Sample period in seconds.
|
|
168
|
+
|
|
169
|
+
Returns:
|
|
170
|
+
List of switching events (turn-on and turn-off).
|
|
171
|
+
"""
|
|
172
|
+
from scipy.integrate import trapezoid
|
|
173
|
+
|
|
174
|
+
events: list[SwitchingEvent] = []
|
|
144
175
|
device_on = device_state == 1
|
|
145
176
|
device_off = device_state == 2
|
|
146
177
|
|
|
147
|
-
# Find transitions
|
|
148
178
|
i = 0
|
|
149
179
|
while i < len(device_on) - 1:
|
|
150
|
-
#
|
|
180
|
+
# Check for turn-on transition
|
|
151
181
|
if device_off[i] and not device_off[i + 1]:
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
end_idx += 1
|
|
157
|
-
|
|
158
|
-
if end_idx < len(device_on):
|
|
159
|
-
# Calculate transition energy (scipy for stable API)
|
|
160
|
-
from scipy.integrate import trapezoid
|
|
161
|
-
|
|
162
|
-
transition_power = p_data[start_idx : end_idx + 1]
|
|
163
|
-
e = float(trapezoid(transition_power, dx=sample_period))
|
|
164
|
-
peak_p = float(np.max(transition_power))
|
|
165
|
-
|
|
166
|
-
events.append(
|
|
167
|
-
SwitchingEvent(
|
|
168
|
-
start_time=start_idx * sample_period,
|
|
169
|
-
end_time=end_idx * sample_period,
|
|
170
|
-
duration=(end_idx - start_idx) * sample_period,
|
|
171
|
-
energy=e,
|
|
172
|
-
peak_power=peak_p,
|
|
173
|
-
event_type="turn_on",
|
|
174
|
-
)
|
|
175
|
-
)
|
|
176
|
-
i = end_idx
|
|
182
|
+
event = _create_turn_on_event(i, device_on, p_data, sample_period, trapezoid)
|
|
183
|
+
if event:
|
|
184
|
+
events.append(event)
|
|
185
|
+
i = int(event.end_time / sample_period)
|
|
177
186
|
continue
|
|
178
187
|
|
|
179
|
-
#
|
|
188
|
+
# Check for turn-off transition
|
|
180
189
|
if device_on[i] and not device_on[i + 1]:
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
if end_idx < len(device_off):
|
|
187
|
-
from scipy.integrate import trapezoid
|
|
188
|
-
|
|
189
|
-
transition_power = p_data[start_idx : end_idx + 1]
|
|
190
|
-
e = float(trapezoid(transition_power, dx=sample_period))
|
|
191
|
-
peak_p = float(np.max(transition_power))
|
|
192
|
-
|
|
193
|
-
events.append(
|
|
194
|
-
SwitchingEvent(
|
|
195
|
-
start_time=start_idx * sample_period,
|
|
196
|
-
end_time=end_idx * sample_period,
|
|
197
|
-
duration=(end_idx - start_idx) * sample_period,
|
|
198
|
-
energy=e,
|
|
199
|
-
peak_power=peak_p,
|
|
200
|
-
event_type="turn_off",
|
|
201
|
-
)
|
|
202
|
-
)
|
|
203
|
-
i = end_idx
|
|
190
|
+
event = _create_turn_off_event(i, device_off, p_data, sample_period, trapezoid)
|
|
191
|
+
if event:
|
|
192
|
+
events.append(event)
|
|
193
|
+
i = int(event.end_time / sample_period)
|
|
204
194
|
continue
|
|
205
195
|
|
|
206
196
|
i += 1
|
|
207
197
|
|
|
208
|
-
|
|
198
|
+
return events
|
|
199
|
+
|
|
200
|
+
|
|
201
|
+
def _create_turn_on_event(
|
|
202
|
+
start_idx: int,
|
|
203
|
+
device_on: NDArray[np.bool_],
|
|
204
|
+
p_data: NDArray[np.floating[Any]],
|
|
205
|
+
sample_period: float,
|
|
206
|
+
trapezoid: Any,
|
|
207
|
+
) -> SwitchingEvent | None:
|
|
208
|
+
"""Create turn-on event from transition indices.
|
|
209
|
+
|
|
210
|
+
Args:
|
|
211
|
+
start_idx: Start index of transition.
|
|
212
|
+
device_on: Boolean array indicating ON state.
|
|
213
|
+
p_data: Power data array.
|
|
214
|
+
sample_period: Sample period in seconds.
|
|
215
|
+
trapezoid: Integration function.
|
|
216
|
+
|
|
217
|
+
Returns:
|
|
218
|
+
SwitchingEvent or None if invalid transition.
|
|
219
|
+
"""
|
|
220
|
+
end_idx = start_idx + 1
|
|
221
|
+
while end_idx < len(device_on) and not device_on[end_idx]:
|
|
222
|
+
end_idx += 1
|
|
223
|
+
|
|
224
|
+
if end_idx >= len(device_on):
|
|
225
|
+
return None
|
|
226
|
+
|
|
227
|
+
transition_power = p_data[start_idx : end_idx + 1]
|
|
228
|
+
e = float(trapezoid(transition_power, dx=sample_period))
|
|
229
|
+
peak_p = float(np.max(transition_power))
|
|
230
|
+
|
|
231
|
+
return SwitchingEvent(
|
|
232
|
+
start_time=start_idx * sample_period,
|
|
233
|
+
end_time=end_idx * sample_period,
|
|
234
|
+
duration=(end_idx - start_idx) * sample_period,
|
|
235
|
+
energy=e,
|
|
236
|
+
peak_power=peak_p,
|
|
237
|
+
event_type="turn_on",
|
|
238
|
+
)
|
|
239
|
+
|
|
240
|
+
|
|
241
|
+
def _create_turn_off_event(
|
|
242
|
+
start_idx: int,
|
|
243
|
+
device_off: NDArray[np.bool_],
|
|
244
|
+
p_data: NDArray[np.floating[Any]],
|
|
245
|
+
sample_period: float,
|
|
246
|
+
trapezoid: Any,
|
|
247
|
+
) -> SwitchingEvent | None:
|
|
248
|
+
"""Create turn-off event from transition indices.
|
|
249
|
+
|
|
250
|
+
Args:
|
|
251
|
+
start_idx: Start index of transition.
|
|
252
|
+
device_off: Boolean array indicating OFF state.
|
|
253
|
+
p_data: Power data array.
|
|
254
|
+
sample_period: Sample period in seconds.
|
|
255
|
+
trapezoid: Integration function.
|
|
256
|
+
|
|
257
|
+
Returns:
|
|
258
|
+
SwitchingEvent or None if invalid transition.
|
|
259
|
+
"""
|
|
260
|
+
end_idx = start_idx + 1
|
|
261
|
+
while end_idx < len(device_off) and not device_off[end_idx]:
|
|
262
|
+
end_idx += 1
|
|
263
|
+
|
|
264
|
+
if end_idx >= len(device_off):
|
|
265
|
+
return None
|
|
266
|
+
|
|
267
|
+
transition_power = p_data[start_idx : end_idx + 1]
|
|
268
|
+
e = float(trapezoid(transition_power, dx=sample_period))
|
|
269
|
+
peak_p = float(np.max(transition_power))
|
|
270
|
+
|
|
271
|
+
return SwitchingEvent(
|
|
272
|
+
start_time=start_idx * sample_period,
|
|
273
|
+
end_time=end_idx * sample_period,
|
|
274
|
+
duration=(end_idx - start_idx) * sample_period,
|
|
275
|
+
energy=e,
|
|
276
|
+
peak_power=peak_p,
|
|
277
|
+
event_type="turn_off",
|
|
278
|
+
)
|
|
279
|
+
|
|
280
|
+
|
|
281
|
+
def _calculate_loss_metrics(events: list[SwitchingEvent]) -> dict[str, Any]:
|
|
282
|
+
"""Calculate energy and power metrics from switching events.
|
|
283
|
+
|
|
284
|
+
Args:
|
|
285
|
+
events: List of switching events.
|
|
286
|
+
|
|
287
|
+
Returns:
|
|
288
|
+
Dictionary with energy, frequency, and power metrics.
|
|
289
|
+
"""
|
|
209
290
|
turn_on_events = [e for e in events if e.event_type == "turn_on"]
|
|
210
291
|
turn_off_events = [e for e in events if e.event_type == "turn_off"]
|
|
211
292
|
|
|
@@ -213,10 +294,10 @@ def switching_loss(
|
|
|
213
294
|
e_off = float(np.mean([e.energy for e in turn_off_events])) if turn_off_events else 0.0
|
|
214
295
|
e_total = e_on + e_off
|
|
215
296
|
|
|
216
|
-
# Estimate switching frequency
|
|
297
|
+
# Estimate switching frequency
|
|
217
298
|
if len(events) >= 2:
|
|
218
299
|
event_times = [e.start_time for e in events]
|
|
219
|
-
avg_period = float(np.mean(np.diff(event_times))) * 2
|
|
300
|
+
avg_period = float(np.mean(np.diff(event_times))) * 2
|
|
220
301
|
f_sw = 1.0 / avg_period if avg_period > 0 else 0.0
|
|
221
302
|
else:
|
|
222
303
|
f_sw = 0.0
|
|
@@ -226,13 +307,61 @@ def switching_loss(
|
|
|
226
307
|
"e_off": e_off,
|
|
227
308
|
"e_total": e_total,
|
|
228
309
|
"f_sw": f_sw,
|
|
229
|
-
"p_sw": e_total * f_sw,
|
|
310
|
+
"p_sw": e_total * f_sw,
|
|
230
311
|
"events": events,
|
|
231
312
|
"n_turn_on": len(turn_on_events),
|
|
232
313
|
"n_turn_off": len(turn_off_events),
|
|
233
314
|
}
|
|
234
315
|
|
|
235
316
|
|
|
317
|
+
def switching_loss(
|
|
318
|
+
voltage: WaveformTrace,
|
|
319
|
+
current: WaveformTrace,
|
|
320
|
+
*,
|
|
321
|
+
v_threshold: float | None = None,
|
|
322
|
+
i_threshold: float | None = None,
|
|
323
|
+
) -> dict[str, Any]:
|
|
324
|
+
"""Calculate switching losses for a power device.
|
|
325
|
+
|
|
326
|
+
Analyzes voltage and current waveforms to find switching transitions
|
|
327
|
+
and calculate turn-on and turn-off energy losses.
|
|
328
|
+
|
|
329
|
+
Args:
|
|
330
|
+
voltage: Drain-source (or collector-emitter) voltage trace.
|
|
331
|
+
current: Drain (or collector) current trace.
|
|
332
|
+
v_threshold: Voltage threshold for on/off detection.
|
|
333
|
+
If None, uses 10% of peak voltage.
|
|
334
|
+
i_threshold: Current threshold for on/off detection.
|
|
335
|
+
If None, uses 10% of peak current.
|
|
336
|
+
|
|
337
|
+
Returns:
|
|
338
|
+
Dictionary with:
|
|
339
|
+
- e_on: Turn-on energy per event (Joules)
|
|
340
|
+
- e_off: Turn-off energy per event (Joules)
|
|
341
|
+
- e_total: Total switching energy per cycle (Joules)
|
|
342
|
+
- p_sw: Switching power at estimated frequency (Watts)
|
|
343
|
+
- events: List of SwitchingEvent objects
|
|
344
|
+
- n_turn_on: Number of turn-on events
|
|
345
|
+
- n_turn_off: Number of turn-off events
|
|
346
|
+
|
|
347
|
+
Example:
|
|
348
|
+
>>> losses = switching_loss(v_ds, i_d)
|
|
349
|
+
>>> print(f"E_on: {losses['e_on']*1e6:.2f} uJ")
|
|
350
|
+
>>> print(f"E_off: {losses['e_off']*1e6:.2f} uJ")
|
|
351
|
+
>>> print(f"Switching power @ 100kHz: {losses['p_sw']*100e3:.2f} W")
|
|
352
|
+
|
|
353
|
+
References:
|
|
354
|
+
Infineon Application Note AN-9010
|
|
355
|
+
"""
|
|
356
|
+
v_data, i_data, p_data, sample_period = _prepare_switching_data(voltage, current)
|
|
357
|
+
_, _, v_high, v_low, i_high, i_low = _compute_thresholds(
|
|
358
|
+
v_data, i_data, v_threshold, i_threshold
|
|
359
|
+
)
|
|
360
|
+
device_state = _build_device_state(v_data, i_data, v_high, v_low, i_high, i_low)
|
|
361
|
+
events = _find_transition_events(device_state, p_data, sample_period)
|
|
362
|
+
return _calculate_loss_metrics(events)
|
|
363
|
+
|
|
364
|
+
|
|
236
365
|
def switching_energy(
|
|
237
366
|
voltage: WaveformTrace,
|
|
238
367
|
current: WaveformTrace,
|
|
@@ -425,6 +554,17 @@ def switching_times(
|
|
|
425
554
|
def find_transition_time(
|
|
426
555
|
data: NDArray[np.floating[Any]], low: float, high: float, rising: bool
|
|
427
556
|
) -> float:
|
|
557
|
+
"""Find transition time between threshold levels.
|
|
558
|
+
|
|
559
|
+
Args:
|
|
560
|
+
data: Signal data array.
|
|
561
|
+
low: Lower threshold level (10% point).
|
|
562
|
+
high: Upper threshold level (90% point).
|
|
563
|
+
rising: True for rising transition, False for falling.
|
|
564
|
+
|
|
565
|
+
Returns:
|
|
566
|
+
Transition time in seconds, or np.nan if not found.
|
|
567
|
+
"""
|
|
428
568
|
if rising:
|
|
429
569
|
below_low = data < low
|
|
430
570
|
start_idx_arr = np.where(below_low[:-1] & ~below_low[1:])[0]
|
|
@@ -1,15 +1,31 @@
|
|
|
1
1
|
"""Protocol decoding module.
|
|
2
2
|
|
|
3
|
+
.. deprecated:: 0.6.0
|
|
4
|
+
This module (singular 'protocol') is deprecated in favor of 'protocols' (plural).
|
|
5
|
+
Use ``from oscura.analyzers.protocols import UARTDecoder`` instead.
|
|
6
|
+
This module will be removed in v1.0.0.
|
|
7
|
+
|
|
3
8
|
This module re-exports protocol decoders from the protocols package
|
|
4
9
|
for convenient access. Both import paths are equivalent:
|
|
5
10
|
|
|
6
|
-
from oscura.analyzers.protocol import UARTDecoder # singular (
|
|
11
|
+
from oscura.analyzers.protocol import UARTDecoder # singular (deprecated)
|
|
7
12
|
from oscura.analyzers.protocols import UARTDecoder # plural (recommended)
|
|
8
13
|
|
|
9
14
|
The plural form (protocols) is recommended as the canonical import path.
|
|
10
15
|
See IMPORT-PATHS.md in the repository root for detailed guidelines.
|
|
11
16
|
"""
|
|
12
17
|
|
|
18
|
+
import warnings
|
|
19
|
+
|
|
20
|
+
# Issue deprecation warning when this module is imported
|
|
21
|
+
warnings.warn(
|
|
22
|
+
"Importing from 'oscura.analyzers.protocol' (singular) is deprecated. "
|
|
23
|
+
"Use 'oscura.analyzers.protocols' (plural) instead. "
|
|
24
|
+
"This module will be removed in v1.0.0.",
|
|
25
|
+
DeprecationWarning,
|
|
26
|
+
stacklevel=2,
|
|
27
|
+
)
|
|
28
|
+
|
|
13
29
|
from oscura.analyzers.protocols import (
|
|
14
30
|
CAN_BITRATES,
|
|
15
31
|
CANFD_DLC_TO_LENGTH,
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
Provides protocol decoders for common serial and automotive protocols including
|
|
4
4
|
UART, SPI, I2C, CAN, LIN, FlexRay, JTAG, SWD, I2S, USB, HDLC, Manchester, CAN-FD,
|
|
5
|
-
1-Wire
|
|
5
|
+
and 1-Wire.
|
|
6
6
|
"""
|
|
7
7
|
|
|
8
8
|
from oscura.analyzers.protocols.base import (
|
|
@@ -60,18 +60,6 @@ from oscura.analyzers.protocols.onewire import (
|
|
|
60
60
|
OneWireTimings,
|
|
61
61
|
decode_onewire,
|
|
62
62
|
)
|
|
63
|
-
|
|
64
|
-
# Parallel bus protocols
|
|
65
|
-
from oscura.analyzers.protocols.parallel_bus import (
|
|
66
|
-
CentronicsFrame,
|
|
67
|
-
GPIBFrame,
|
|
68
|
-
GPIBMessageType,
|
|
69
|
-
ISACycleType,
|
|
70
|
-
ISATransaction,
|
|
71
|
-
decode_centronics,
|
|
72
|
-
decode_gpib,
|
|
73
|
-
decode_isa_bus,
|
|
74
|
-
)
|
|
75
63
|
from oscura.analyzers.protocols.spi import SPIDecoder, decode_spi
|
|
76
64
|
from oscura.analyzers.protocols.swd import SWDDecoder, SWDResponse, decode_swd
|
|
77
65
|
from oscura.analyzers.protocols.uart import UARTDecoder, decode_uart
|
|
@@ -103,16 +91,12 @@ __all__ = [
|
|
|
103
91
|
"CANFDFrameType",
|
|
104
92
|
"CANFrame",
|
|
105
93
|
"CANFrameType",
|
|
106
|
-
# Parallel bus protocols
|
|
107
|
-
"CentronicsFrame",
|
|
108
94
|
"ChannelDef",
|
|
109
95
|
"DecoderState",
|
|
110
96
|
# FlexRay (PRO-016)
|
|
111
97
|
"FlexRayDecoder",
|
|
112
98
|
"FlexRayFrame",
|
|
113
99
|
"FlexRaySegment",
|
|
114
|
-
"GPIBFrame",
|
|
115
|
-
"GPIBMessageType",
|
|
116
100
|
# HDLC (PRO-013)
|
|
117
101
|
"HDLCDecoder",
|
|
118
102
|
# I2C (PRO-004)
|
|
@@ -120,8 +104,6 @@ __all__ = [
|
|
|
120
104
|
# I2S (PRO-011)
|
|
121
105
|
"I2SDecoder",
|
|
122
106
|
"I2SMode",
|
|
123
|
-
"ISACycleType",
|
|
124
|
-
"ISATransaction",
|
|
125
107
|
# JTAG (PRO-009)
|
|
126
108
|
"JTAGDecoder",
|
|
127
109
|
# LIN (PRO-008)
|
|
@@ -153,13 +135,10 @@ __all__ = [
|
|
|
153
135
|
"USBSpeed",
|
|
154
136
|
"decode_can",
|
|
155
137
|
"decode_can_fd",
|
|
156
|
-
"decode_centronics",
|
|
157
138
|
"decode_flexray",
|
|
158
|
-
"decode_gpib",
|
|
159
139
|
"decode_hdlc",
|
|
160
140
|
"decode_i2c",
|
|
161
141
|
"decode_i2s",
|
|
162
|
-
"decode_isa_bus",
|
|
163
142
|
"decode_jtag",
|
|
164
143
|
"decode_lin",
|
|
165
144
|
"decode_manchester",
|
|
@@ -172,18 +172,18 @@ class ProtocolDecoder(ABC):
|
|
|
172
172
|
license: str = "MIT"
|
|
173
173
|
|
|
174
174
|
# Input/output types
|
|
175
|
-
inputs: list[str] = ["logic"]
|
|
176
|
-
outputs: list[str] = ["packets"]
|
|
175
|
+
inputs: list[str] = ["logic"]
|
|
176
|
+
outputs: list[str] = ["packets"]
|
|
177
177
|
|
|
178
178
|
# Channel definitions
|
|
179
|
-
channels: list[ChannelDef] = []
|
|
180
|
-
optional_channels: list[ChannelDef] = []
|
|
179
|
+
channels: list[ChannelDef] = []
|
|
180
|
+
optional_channels: list[ChannelDef] = []
|
|
181
181
|
|
|
182
182
|
# Options
|
|
183
|
-
options: list[OptionDef] = []
|
|
183
|
+
options: list[OptionDef] = []
|
|
184
184
|
|
|
185
185
|
# Annotation definitions (override in subclass)
|
|
186
|
-
annotations: list[tuple[str, str]] = []
|
|
186
|
+
annotations: list[tuple[str, str]] = []
|
|
187
187
|
|
|
188
188
|
def __init__(self, **options: Any) -> None:
|
|
189
189
|
"""Initialize decoder with options.
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
"""BLE (Bluetooth Low Energy) protocol analysis.
|
|
2
|
+
|
|
3
|
+
This module provides comprehensive BLE protocol decoding with GATT service
|
|
4
|
+
discovery, advertising packet parsing, and ATT operation decoding.
|
|
5
|
+
|
|
6
|
+
Example:
|
|
7
|
+
>>> from oscura.analyzers.protocols.ble import BLEAnalyzer
|
|
8
|
+
>>> analyzer = BLEAnalyzer()
|
|
9
|
+
>>> analyzer.add_packet(packet)
|
|
10
|
+
>>> services = analyzer.discover_services()
|
|
11
|
+
|
|
12
|
+
References:
|
|
13
|
+
Bluetooth Core Specification v5.4: https://www.bluetooth.com/specifications/specs/
|
|
14
|
+
"""
|
|
15
|
+
|
|
16
|
+
from oscura.analyzers.protocols.ble.analyzer import (
|
|
17
|
+
BLEAnalyzer,
|
|
18
|
+
BLEPacket,
|
|
19
|
+
GATTCharacteristic,
|
|
20
|
+
GATTDescriptor,
|
|
21
|
+
GATTService,
|
|
22
|
+
)
|
|
23
|
+
from oscura.analyzers.protocols.ble.uuids import (
|
|
24
|
+
STANDARD_CHARACTERISTICS,
|
|
25
|
+
STANDARD_DESCRIPTORS,
|
|
26
|
+
STANDARD_SERVICES,
|
|
27
|
+
)
|
|
28
|
+
|
|
29
|
+
__all__ = [
|
|
30
|
+
"STANDARD_CHARACTERISTICS",
|
|
31
|
+
"STANDARD_DESCRIPTORS",
|
|
32
|
+
"STANDARD_SERVICES",
|
|
33
|
+
"BLEAnalyzer",
|
|
34
|
+
"BLEPacket",
|
|
35
|
+
"GATTCharacteristic",
|
|
36
|
+
"GATTDescriptor",
|
|
37
|
+
"GATTService",
|
|
38
|
+
]
|