oscura 0.5.1__py3-none-any.whl → 0.7.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/edges.py +325 -65
- 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/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 +17 -102
- 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/{schemas → core/schemas}/device_mapping.json +2 -8
- oscura/{schemas → core/schemas}/packet_format.json +4 -24
- oscura/{schemas → core/schemas}/protocol_definition.json +2 -12
- 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 -8
- oscura/export/kaitai_struct.py +513 -0
- oscura/export/scapy_layer.py +801 -0
- 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/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 +183 -67
- 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/tss.py +456 -0
- oscura/loaders/vcd.py +215 -117
- oscura/loaders/wav.py +155 -68
- oscura/reporting/__init__.py +9 -0
- 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/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/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 +1 -1
- 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.7.0.dist-info/METADATA +661 -0
- oscura-0.7.0.dist-info/RECORD +591 -0
- 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/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 -291
- 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/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/workflow/__init__.py +0 -13
- oscura-0.5.1.dist-info/METADATA +0 -583
- oscura-0.5.1.dist-info/RECORD +0 -481
- /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/{schemas → core/schemas}/bus_configuration.json +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.1.dist-info → oscura-0.7.0.dist-info}/WHEEL +0 -0
- {oscura-0.5.1.dist-info → oscura-0.7.0.dist-info}/entry_points.txt +0 -0
- {oscura-0.5.1.dist-info → oscura-0.7.0.dist-info}/licenses/LICENSE +0 -0
oscura/exploratory/fuzzy.py
DELETED
|
@@ -1,513 +0,0 @@
|
|
|
1
|
-
"""Fuzzy matching for timing and pattern analysis.
|
|
2
|
-
|
|
3
|
-
This module provides fuzzy matching capabilities for tolerating
|
|
4
|
-
timing variations and pattern deviations in real-world signals.
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
Example:
|
|
8
|
-
>>> from oscura.exploratory.fuzzy import fuzzy_timing_match
|
|
9
|
-
>>> result = fuzzy_timing_match(edges, expected_period=1e-6, tolerance=0.1)
|
|
10
|
-
>>> print(f"Match confidence: {result.confidence:.1%}")
|
|
11
|
-
"""
|
|
12
|
-
|
|
13
|
-
from __future__ import annotations
|
|
14
|
-
|
|
15
|
-
from dataclasses import dataclass
|
|
16
|
-
from typing import TYPE_CHECKING, Any
|
|
17
|
-
|
|
18
|
-
import numpy as np
|
|
19
|
-
|
|
20
|
-
from oscura.core.types import WaveformTrace
|
|
21
|
-
|
|
22
|
-
if TYPE_CHECKING:
|
|
23
|
-
from numpy.typing import NDArray
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
@dataclass
|
|
27
|
-
class FuzzyTimingResult:
|
|
28
|
-
"""Result of fuzzy timing match.
|
|
29
|
-
|
|
30
|
-
Attributes:
|
|
31
|
-
match: True if timing matches within tolerance.
|
|
32
|
-
confidence: Match confidence (0.0 to 1.0).
|
|
33
|
-
period: Detected period.
|
|
34
|
-
deviation: Deviation from expected period.
|
|
35
|
-
jitter_rms: RMS timing jitter.
|
|
36
|
-
outlier_count: Number of timing outliers.
|
|
37
|
-
outlier_indices: Indices of outlier edges.
|
|
38
|
-
"""
|
|
39
|
-
|
|
40
|
-
match: bool
|
|
41
|
-
confidence: float
|
|
42
|
-
period: float
|
|
43
|
-
deviation: float
|
|
44
|
-
jitter_rms: float
|
|
45
|
-
outlier_count: int
|
|
46
|
-
outlier_indices: list[int]
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
def fuzzy_timing_match(
|
|
50
|
-
trace_or_edges: WaveformTrace | NDArray[np.float64],
|
|
51
|
-
*,
|
|
52
|
-
expected_period: float | None = None,
|
|
53
|
-
tolerance: float = 0.1,
|
|
54
|
-
sample_rate: float | None = None,
|
|
55
|
-
) -> FuzzyTimingResult:
|
|
56
|
-
"""Match timing with fuzzy tolerance.
|
|
57
|
-
|
|
58
|
-
Allows timing variations while still detecting protocol patterns.
|
|
59
|
-
Useful for signals with jitter or clock drift.
|
|
60
|
-
|
|
61
|
-
Args:
|
|
62
|
-
trace_or_edges: WaveformTrace or array of edge times.
|
|
63
|
-
expected_period: Expected period in seconds.
|
|
64
|
-
tolerance: Tolerance as fraction (0.1 = 10%).
|
|
65
|
-
sample_rate: Sample rate (required if trace provided).
|
|
66
|
-
|
|
67
|
-
Returns:
|
|
68
|
-
FuzzyTimingResult with match information.
|
|
69
|
-
|
|
70
|
-
Raises:
|
|
71
|
-
ValueError: If sample_rate is invalid when WaveformTrace provided.
|
|
72
|
-
|
|
73
|
-
Example:
|
|
74
|
-
>>> result = fuzzy_timing_match(trace, expected_period=1e-6, tolerance=0.1)
|
|
75
|
-
>>> print(f"Period match: {result.match}")
|
|
76
|
-
>>> print(f"Actual period: {result.period:.3e} s")
|
|
77
|
-
|
|
78
|
-
References:
|
|
79
|
-
FUZZY-001: Fuzzy Timing Tolerance
|
|
80
|
-
"""
|
|
81
|
-
# Extract edges if WaveformTrace provided
|
|
82
|
-
if isinstance(trace_or_edges, WaveformTrace):
|
|
83
|
-
data = trace_or_edges.data
|
|
84
|
-
sample_rate = sample_rate or trace_or_edges.metadata.sample_rate
|
|
85
|
-
|
|
86
|
-
if sample_rate is None or sample_rate <= 0:
|
|
87
|
-
raise ValueError("Valid sample_rate required for WaveformTrace")
|
|
88
|
-
|
|
89
|
-
# Threshold and find edges
|
|
90
|
-
v_min = np.percentile(data, 5)
|
|
91
|
-
v_max = np.percentile(data, 95)
|
|
92
|
-
threshold = (v_min + v_max) / 2
|
|
93
|
-
digital = data > threshold
|
|
94
|
-
|
|
95
|
-
# Find all transitions (rising and falling edges)
|
|
96
|
-
edge_samples = np.where(np.abs(np.diff(digital.astype(int))) > 0)[0]
|
|
97
|
-
edges = edge_samples / sample_rate
|
|
98
|
-
else:
|
|
99
|
-
edges = trace_or_edges
|
|
100
|
-
|
|
101
|
-
if len(edges) < 2:
|
|
102
|
-
return FuzzyTimingResult(
|
|
103
|
-
match=False,
|
|
104
|
-
confidence=0.0,
|
|
105
|
-
period=0.0,
|
|
106
|
-
deviation=1.0,
|
|
107
|
-
jitter_rms=0.0,
|
|
108
|
-
outlier_count=0,
|
|
109
|
-
outlier_indices=[],
|
|
110
|
-
)
|
|
111
|
-
|
|
112
|
-
# Calculate inter-edge intervals
|
|
113
|
-
intervals = np.diff(edges)
|
|
114
|
-
|
|
115
|
-
# Detect period (use median for robustness)
|
|
116
|
-
detected_period = np.median(intervals)
|
|
117
|
-
|
|
118
|
-
# Use expected period if provided, otherwise use detected
|
|
119
|
-
if expected_period is None:
|
|
120
|
-
expected_period = detected_period
|
|
121
|
-
|
|
122
|
-
# Calculate deviation
|
|
123
|
-
deviation = abs(detected_period - expected_period) / expected_period
|
|
124
|
-
|
|
125
|
-
# Determine match
|
|
126
|
-
match = bool(deviation <= tolerance)
|
|
127
|
-
|
|
128
|
-
# Calculate jitter
|
|
129
|
-
normalized_intervals = intervals / detected_period
|
|
130
|
-
jitter_rms = np.std(normalized_intervals - 1.0) * detected_period
|
|
131
|
-
|
|
132
|
-
# Find outliers
|
|
133
|
-
outlier_threshold = expected_period * tolerance * 3 # 3x tolerance
|
|
134
|
-
deviations = np.abs(intervals - expected_period)
|
|
135
|
-
outlier_mask = deviations > outlier_threshold
|
|
136
|
-
outlier_count = int(np.sum(outlier_mask))
|
|
137
|
-
outlier_indices = list(np.where(outlier_mask)[0])
|
|
138
|
-
|
|
139
|
-
# Calculate confidence
|
|
140
|
-
# Higher confidence for lower deviation and fewer outliers
|
|
141
|
-
confidence = max(0.0, 1.0 - deviation / tolerance)
|
|
142
|
-
confidence *= max(0.0, 1.0 - outlier_count / max(len(intervals), 1))
|
|
143
|
-
confidence = min(1.0, confidence)
|
|
144
|
-
|
|
145
|
-
return FuzzyTimingResult(
|
|
146
|
-
match=match,
|
|
147
|
-
confidence=confidence,
|
|
148
|
-
period=detected_period,
|
|
149
|
-
deviation=deviation,
|
|
150
|
-
jitter_rms=jitter_rms,
|
|
151
|
-
outlier_count=outlier_count,
|
|
152
|
-
outlier_indices=outlier_indices,
|
|
153
|
-
)
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
@dataclass
|
|
157
|
-
class FuzzyPatternResult:
|
|
158
|
-
"""Result of fuzzy pattern match.
|
|
159
|
-
|
|
160
|
-
Attributes:
|
|
161
|
-
matches: List of match locations with scores.
|
|
162
|
-
best_match_score: Score of best match.
|
|
163
|
-
total_matches: Total number of matches found.
|
|
164
|
-
pattern_variations: Common pattern variations found.
|
|
165
|
-
"""
|
|
166
|
-
|
|
167
|
-
matches: list[dict[str, Any]]
|
|
168
|
-
best_match_score: float
|
|
169
|
-
total_matches: int
|
|
170
|
-
pattern_variations: list[tuple[tuple[int, ...], int]]
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
def fuzzy_pattern_match(
|
|
174
|
-
trace: WaveformTrace,
|
|
175
|
-
pattern: list[int] | tuple[int, ...],
|
|
176
|
-
*,
|
|
177
|
-
max_errors: int = 1,
|
|
178
|
-
error_weight: float = 0.5,
|
|
179
|
-
) -> FuzzyPatternResult:
|
|
180
|
-
"""Match pattern with allowed bit errors.
|
|
181
|
-
|
|
182
|
-
Finds pattern occurrences allowing for bit errors, useful for
|
|
183
|
-
noisy signals or partial matches.
|
|
184
|
-
|
|
185
|
-
Args:
|
|
186
|
-
trace: Signal trace to search.
|
|
187
|
-
pattern: Bit pattern to find (list of 0s and 1s).
|
|
188
|
-
max_errors: Maximum allowed bit errors.
|
|
189
|
-
error_weight: Weight reduction per error.
|
|
190
|
-
|
|
191
|
-
Returns:
|
|
192
|
-
FuzzyPatternResult with match locations.
|
|
193
|
-
|
|
194
|
-
Example:
|
|
195
|
-
>>> result = fuzzy_pattern_match(trace, [0, 1, 0, 1, 0, 1], max_errors=1)
|
|
196
|
-
>>> print(f"Found {result.total_matches} matches")
|
|
197
|
-
>>> for match in result.matches[:5]:
|
|
198
|
-
... print(f" Position {match['position']}: score {match['score']:.2f}")
|
|
199
|
-
|
|
200
|
-
References:
|
|
201
|
-
FUZZY-002: Fuzzy Pattern Matching
|
|
202
|
-
"""
|
|
203
|
-
pattern = tuple(pattern)
|
|
204
|
-
pattern_len = len(pattern)
|
|
205
|
-
|
|
206
|
-
# Handle empty pattern
|
|
207
|
-
if pattern_len == 0:
|
|
208
|
-
return FuzzyPatternResult(
|
|
209
|
-
matches=[],
|
|
210
|
-
best_match_score=0.0,
|
|
211
|
-
total_matches=0,
|
|
212
|
-
pattern_variations=[],
|
|
213
|
-
)
|
|
214
|
-
|
|
215
|
-
# Convert trace to digital
|
|
216
|
-
data = trace.data
|
|
217
|
-
v_min = np.percentile(data, 5)
|
|
218
|
-
v_max = np.percentile(data, 95)
|
|
219
|
-
threshold = (v_min + v_max) / 2
|
|
220
|
-
digital = (data > threshold).astype(int)
|
|
221
|
-
|
|
222
|
-
# Find edges and sample at bit centers
|
|
223
|
-
edges = np.where(np.diff(digital) != 0)[0]
|
|
224
|
-
|
|
225
|
-
if len(edges) < 2:
|
|
226
|
-
return FuzzyPatternResult(
|
|
227
|
-
matches=[],
|
|
228
|
-
best_match_score=0.0,
|
|
229
|
-
total_matches=0,
|
|
230
|
-
pattern_variations=[],
|
|
231
|
-
)
|
|
232
|
-
|
|
233
|
-
# Estimate bit period from edges
|
|
234
|
-
# Note: This algorithm works best with signals that have frequent transitions.
|
|
235
|
-
# For patterns with long runs of identical bits, edge density may be insufficient
|
|
236
|
-
# for accurate bit recovery. Best suited for alternating patterns or noisy signals.
|
|
237
|
-
gaps = np.diff(edges)
|
|
238
|
-
|
|
239
|
-
# Use minimum gap as bit period estimate
|
|
240
|
-
# The smallest gap between edges is typically one bit period
|
|
241
|
-
estimated_bit_period = float(np.min(gaps))
|
|
242
|
-
|
|
243
|
-
# Sample bits at regular intervals
|
|
244
|
-
bits_list = []
|
|
245
|
-
sample_pos = edges[0] + estimated_bit_period / 2
|
|
246
|
-
|
|
247
|
-
while sample_pos < len(digital):
|
|
248
|
-
idx = int(sample_pos)
|
|
249
|
-
if idx < len(digital):
|
|
250
|
-
bits_list.append(digital[idx])
|
|
251
|
-
sample_pos += estimated_bit_period
|
|
252
|
-
|
|
253
|
-
bits = np.array(bits_list)
|
|
254
|
-
|
|
255
|
-
# Search for pattern with fuzzy matching
|
|
256
|
-
matches = []
|
|
257
|
-
variations: dict[tuple[int, ...], int] = {}
|
|
258
|
-
|
|
259
|
-
for i in range(len(bits) - pattern_len + 1):
|
|
260
|
-
window = tuple(bits[i : i + pattern_len])
|
|
261
|
-
|
|
262
|
-
# Count errors
|
|
263
|
-
errors = sum(1 for a, b in zip(window, pattern, strict=False) if a != b)
|
|
264
|
-
|
|
265
|
-
if errors <= max_errors:
|
|
266
|
-
score = 1.0 - errors * error_weight
|
|
267
|
-
matches.append(
|
|
268
|
-
{
|
|
269
|
-
"position": i,
|
|
270
|
-
"sample_position": int(edges[0] + i * estimated_bit_period),
|
|
271
|
-
"errors": errors,
|
|
272
|
-
"score": score,
|
|
273
|
-
"actual_pattern": window,
|
|
274
|
-
}
|
|
275
|
-
)
|
|
276
|
-
|
|
277
|
-
# Track variations
|
|
278
|
-
if window != pattern:
|
|
279
|
-
variations[window] = variations.get(window, 0) + 1
|
|
280
|
-
|
|
281
|
-
# Sort matches by score
|
|
282
|
-
matches.sort(key=lambda x: x["score"], reverse=True) # type: ignore[arg-type, return-value]
|
|
283
|
-
|
|
284
|
-
best_score = matches[0]["score"] if matches else 0.0
|
|
285
|
-
|
|
286
|
-
# Sort variations by frequency
|
|
287
|
-
variation_list = sorted(variations.items(), key=lambda x: x[1], reverse=True)
|
|
288
|
-
|
|
289
|
-
return FuzzyPatternResult(
|
|
290
|
-
matches=matches,
|
|
291
|
-
best_match_score=best_score, # type: ignore[arg-type]
|
|
292
|
-
total_matches=len(matches),
|
|
293
|
-
pattern_variations=variation_list[:10],
|
|
294
|
-
)
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
@dataclass
|
|
298
|
-
class FuzzyProtocolResult:
|
|
299
|
-
"""Result of fuzzy protocol detection.
|
|
300
|
-
|
|
301
|
-
Attributes:
|
|
302
|
-
detected_protocol: Most likely protocol.
|
|
303
|
-
confidence: Detection confidence.
|
|
304
|
-
alternatives: Alternative protocol candidates.
|
|
305
|
-
timing_score: Score based on timing match.
|
|
306
|
-
pattern_score: Score based on pattern match.
|
|
307
|
-
recommendations: Suggestions for improving detection.
|
|
308
|
-
"""
|
|
309
|
-
|
|
310
|
-
detected_protocol: str
|
|
311
|
-
confidence: float
|
|
312
|
-
alternatives: list[tuple[str, float]]
|
|
313
|
-
timing_score: float
|
|
314
|
-
pattern_score: float
|
|
315
|
-
recommendations: list[str]
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
# Protocol signatures for fuzzy matching
|
|
319
|
-
PROTOCOL_SIGNATURES = {
|
|
320
|
-
"UART": {
|
|
321
|
-
"start_bit": 0,
|
|
322
|
-
"stop_bits": 1,
|
|
323
|
-
"frame_size": [8, 9, 10, 11], # With start/stop
|
|
324
|
-
"typical_rates": [9600, 19200, 38400, 57600, 115200],
|
|
325
|
-
},
|
|
326
|
-
"I2C": {
|
|
327
|
-
"start_pattern": [1, 0], # SDA falls while SCL high
|
|
328
|
-
"stop_pattern": [0, 1], # SDA rises while SCL high
|
|
329
|
-
"ack_bit": 0,
|
|
330
|
-
"typical_rates": [100e3, 400e3, 1e6, 3.4e6],
|
|
331
|
-
},
|
|
332
|
-
"SPI": {
|
|
333
|
-
"idle_clock": [0, 1], # CPOL options
|
|
334
|
-
"clock_phase": [0, 1], # CPHA options
|
|
335
|
-
"frame_size": [8, 16],
|
|
336
|
-
"typical_rates": [1e6, 5e6, 10e6, 20e6, 40e6],
|
|
337
|
-
},
|
|
338
|
-
"CAN": {
|
|
339
|
-
"start_of_frame": 0,
|
|
340
|
-
"frame_patterns": ["standard", "extended"],
|
|
341
|
-
"typical_rates": [125e3, 250e3, 500e3, 1e6],
|
|
342
|
-
},
|
|
343
|
-
}
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
def fuzzy_protocol_detect(
|
|
347
|
-
trace: WaveformTrace,
|
|
348
|
-
*,
|
|
349
|
-
candidates: list[str] | None = None,
|
|
350
|
-
timing_tolerance: float = 0.15,
|
|
351
|
-
pattern_tolerance: int = 2,
|
|
352
|
-
) -> FuzzyProtocolResult:
|
|
353
|
-
"""Detect protocol with fuzzy matching.
|
|
354
|
-
|
|
355
|
-
Uses timing tolerance and pattern flexibility to identify
|
|
356
|
-
protocols even with non-ideal signals.
|
|
357
|
-
|
|
358
|
-
Args:
|
|
359
|
-
trace: Signal trace to analyze.
|
|
360
|
-
candidates: List of protocols to consider (None = all).
|
|
361
|
-
timing_tolerance: Timing tolerance as fraction.
|
|
362
|
-
pattern_tolerance: Maximum pattern bit errors.
|
|
363
|
-
|
|
364
|
-
Returns:
|
|
365
|
-
FuzzyProtocolResult with detection results.
|
|
366
|
-
|
|
367
|
-
Example:
|
|
368
|
-
>>> result = fuzzy_protocol_detect(trace)
|
|
369
|
-
>>> print(f"Detected: {result.detected_protocol}")
|
|
370
|
-
>>> print(f"Confidence: {result.confidence:.1%}")
|
|
371
|
-
|
|
372
|
-
References:
|
|
373
|
-
FUZZY-003: Fuzzy Protocol Detection
|
|
374
|
-
"""
|
|
375
|
-
data = trace.data
|
|
376
|
-
sample_rate = trace.metadata.sample_rate
|
|
377
|
-
|
|
378
|
-
if candidates is None:
|
|
379
|
-
candidates = list(PROTOCOL_SIGNATURES.keys())
|
|
380
|
-
|
|
381
|
-
# Analyze signal characteristics
|
|
382
|
-
v_min = np.percentile(data, 5)
|
|
383
|
-
v_max = np.percentile(data, 95)
|
|
384
|
-
threshold = (v_min + v_max) / 2
|
|
385
|
-
digital = data > threshold
|
|
386
|
-
|
|
387
|
-
edges = np.where(np.diff(digital.astype(int)) != 0)[0]
|
|
388
|
-
|
|
389
|
-
if len(edges) < 4:
|
|
390
|
-
return FuzzyProtocolResult(
|
|
391
|
-
detected_protocol="Unknown",
|
|
392
|
-
confidence=0.0,
|
|
393
|
-
alternatives=[],
|
|
394
|
-
timing_score=0.0,
|
|
395
|
-
pattern_score=0.0,
|
|
396
|
-
recommendations=["Insufficient edges for protocol detection"],
|
|
397
|
-
)
|
|
398
|
-
|
|
399
|
-
# Estimate bit rate
|
|
400
|
-
intervals = np.diff(edges)
|
|
401
|
-
median_interval = np.median(intervals)
|
|
402
|
-
estimated_bitrate = sample_rate / median_interval
|
|
403
|
-
|
|
404
|
-
# Score each protocol
|
|
405
|
-
scores: dict[str, dict[str, float]] = {}
|
|
406
|
-
|
|
407
|
-
for protocol in candidates:
|
|
408
|
-
if protocol not in PROTOCOL_SIGNATURES:
|
|
409
|
-
continue
|
|
410
|
-
|
|
411
|
-
sig = PROTOCOL_SIGNATURES[protocol]
|
|
412
|
-
timing_score = 0.0
|
|
413
|
-
pattern_score = 0.0
|
|
414
|
-
|
|
415
|
-
# Check timing against typical rates
|
|
416
|
-
if "typical_rates" in sig:
|
|
417
|
-
rates = sig["typical_rates"]
|
|
418
|
-
if hasattr(rates, "__iter__"):
|
|
419
|
-
for rate in rates:
|
|
420
|
-
if isinstance(rate, int | float):
|
|
421
|
-
ratio = estimated_bitrate / rate
|
|
422
|
-
if (1 - timing_tolerance) <= ratio <= (1 + timing_tolerance):
|
|
423
|
-
timing_score = max(timing_score, 1 - abs(1 - ratio) / timing_tolerance)
|
|
424
|
-
|
|
425
|
-
# Check patterns
|
|
426
|
-
if "start_pattern" in sig:
|
|
427
|
-
# Sample first few bits
|
|
428
|
-
bits = []
|
|
429
|
-
pos = edges[0] + median_interval / 2
|
|
430
|
-
for _ in range(4):
|
|
431
|
-
if pos < len(digital):
|
|
432
|
-
bits.append(int(digital[int(pos)]))
|
|
433
|
-
pos += median_interval
|
|
434
|
-
|
|
435
|
-
expected = sig["start_pattern"]
|
|
436
|
-
if len(bits) >= len(expected): # type: ignore[arg-type]
|
|
437
|
-
errors = sum(
|
|
438
|
-
1 # type: ignore[misc]
|
|
439
|
-
for a, b in zip(bits[: len(expected)], expected, strict=False) # type: ignore[call-overload, arg-type]
|
|
440
|
-
if a != b # type: ignore[misc, call-overload, arg-type]
|
|
441
|
-
)
|
|
442
|
-
if errors <= pattern_tolerance:
|
|
443
|
-
pattern_score = 1 - errors * 0.3
|
|
444
|
-
|
|
445
|
-
# Check frame size
|
|
446
|
-
if "frame_size" in sig:
|
|
447
|
-
# Estimate frame size from inter-frame gaps
|
|
448
|
-
gap_threshold = median_interval * 2
|
|
449
|
-
long_gaps = intervals[intervals > gap_threshold]
|
|
450
|
-
if len(long_gaps) > 0:
|
|
451
|
-
frame_samples = np.median(long_gaps)
|
|
452
|
-
frame_bits = frame_samples / median_interval
|
|
453
|
-
for valid_size in sig["frame_size"]: # type: ignore[attr-defined]
|
|
454
|
-
if abs(frame_bits - valid_size) < 1.5:
|
|
455
|
-
pattern_score = max(pattern_score, 0.7)
|
|
456
|
-
|
|
457
|
-
scores[protocol] = {
|
|
458
|
-
"timing": timing_score,
|
|
459
|
-
"pattern": pattern_score,
|
|
460
|
-
"total": timing_score * 0.5 + pattern_score * 0.5,
|
|
461
|
-
}
|
|
462
|
-
|
|
463
|
-
# Find best match
|
|
464
|
-
if not scores:
|
|
465
|
-
return FuzzyProtocolResult(
|
|
466
|
-
detected_protocol="Unknown",
|
|
467
|
-
confidence=0.0,
|
|
468
|
-
alternatives=[],
|
|
469
|
-
timing_score=0.0,
|
|
470
|
-
pattern_score=0.0,
|
|
471
|
-
recommendations=["No matching protocols found"],
|
|
472
|
-
)
|
|
473
|
-
|
|
474
|
-
sorted_protocols = sorted(scores.items(), key=lambda x: x[1]["total"], reverse=True)
|
|
475
|
-
best_protocol, best_scores = sorted_protocols[0]
|
|
476
|
-
|
|
477
|
-
confidence = best_scores["total"]
|
|
478
|
-
alternatives = [(p, s["total"]) for p, s in sorted_protocols[1:4] if s["total"] > 0.2]
|
|
479
|
-
|
|
480
|
-
# Generate recommendations
|
|
481
|
-
recommendations = []
|
|
482
|
-
|
|
483
|
-
if confidence < 0.5:
|
|
484
|
-
recommendations.append("Low confidence - verify with protocol-specific decoder")
|
|
485
|
-
|
|
486
|
-
if best_scores["timing"] > best_scores["pattern"]:
|
|
487
|
-
recommendations.append("Timing matched better than patterns - check signal quality")
|
|
488
|
-
|
|
489
|
-
if best_scores["pattern"] > best_scores["timing"]:
|
|
490
|
-
recommendations.append("Patterns matched but timing off - check clock accuracy")
|
|
491
|
-
|
|
492
|
-
if not alternatives:
|
|
493
|
-
recommendations.append("No alternative protocols detected")
|
|
494
|
-
|
|
495
|
-
return FuzzyProtocolResult(
|
|
496
|
-
detected_protocol=best_protocol,
|
|
497
|
-
confidence=confidence,
|
|
498
|
-
alternatives=alternatives,
|
|
499
|
-
timing_score=best_scores["timing"],
|
|
500
|
-
pattern_score=best_scores["pattern"],
|
|
501
|
-
recommendations=recommendations,
|
|
502
|
-
)
|
|
503
|
-
|
|
504
|
-
|
|
505
|
-
__all__ = [
|
|
506
|
-
"PROTOCOL_SIGNATURES",
|
|
507
|
-
"FuzzyPatternResult",
|
|
508
|
-
"FuzzyProtocolResult",
|
|
509
|
-
"FuzzyTimingResult",
|
|
510
|
-
"fuzzy_pattern_match",
|
|
511
|
-
"fuzzy_protocol_detect",
|
|
512
|
-
"fuzzy_timing_match",
|
|
513
|
-
]
|