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
|
@@ -180,94 +180,28 @@ class CANMessageWrapper:
|
|
|
180
180
|
>>> print(result.summary())
|
|
181
181
|
"""
|
|
182
182
|
# Create signal definition
|
|
183
|
-
definition =
|
|
184
|
-
|
|
185
|
-
start_bit=start_byte * 8,
|
|
186
|
-
length=bit_length,
|
|
187
|
-
byte_order=byte_order,
|
|
188
|
-
value_type=value_type,
|
|
189
|
-
scale=scale,
|
|
190
|
-
offset=offset,
|
|
191
|
-
unit=unit,
|
|
183
|
+
definition = _create_signal_definition(
|
|
184
|
+
signal_name, start_byte, bit_length, byte_order, value_type, scale, offset, unit
|
|
192
185
|
)
|
|
193
186
|
|
|
194
|
-
# Get all messages with this ID
|
|
195
|
-
filtered = self._session._messages.filter_by_id(self._arbitration_id)
|
|
196
|
-
|
|
197
187
|
# Decode all values
|
|
198
|
-
decoded_values =
|
|
199
|
-
for msg in filtered.messages:
|
|
200
|
-
try:
|
|
201
|
-
value = definition.decode(msg.data)
|
|
202
|
-
decoded_values.append(value)
|
|
203
|
-
except Exception:
|
|
204
|
-
# Skip messages that can't be decoded
|
|
205
|
-
pass
|
|
188
|
+
decoded_values = _decode_hypothesis_values(self._session, self._arbitration_id, definition)
|
|
206
189
|
|
|
190
|
+
# Handle no decoded values
|
|
207
191
|
if not decoded_values:
|
|
208
|
-
return
|
|
209
|
-
signal_name=signal_name,
|
|
210
|
-
definition=definition,
|
|
211
|
-
values=[],
|
|
212
|
-
min_value=0.0,
|
|
213
|
-
max_value=0.0,
|
|
214
|
-
mean=0.0,
|
|
215
|
-
std=0.0,
|
|
216
|
-
is_valid=False,
|
|
217
|
-
confidence=0.0,
|
|
218
|
-
feedback="Failed to decode any messages with this definition",
|
|
219
|
-
)
|
|
192
|
+
return _create_failed_hypothesis_result(signal_name, definition)
|
|
220
193
|
|
|
221
194
|
# Calculate statistics
|
|
222
|
-
|
|
223
|
-
min_val = float(np.min(arr))
|
|
224
|
-
max_val = float(np.max(arr))
|
|
225
|
-
mean_val = float(np.mean(arr))
|
|
226
|
-
std_val = float(np.std(arr))
|
|
195
|
+
stats = _calculate_hypothesis_statistics(decoded_values)
|
|
227
196
|
|
|
228
197
|
# Validate hypothesis
|
|
229
|
-
is_valid =
|
|
230
|
-
confidence = 1.0
|
|
231
|
-
feedback_parts = []
|
|
232
|
-
|
|
233
|
-
# Check expected range if provided
|
|
234
|
-
if expected_min is not None and min_val < expected_min:
|
|
235
|
-
is_valid = False
|
|
236
|
-
confidence *= 0.5
|
|
237
|
-
feedback_parts.append(f"Min value {min_val:.2f} below expected {expected_min:.2f}")
|
|
238
|
-
|
|
239
|
-
if expected_max is not None and max_val > expected_max:
|
|
240
|
-
is_valid = False
|
|
241
|
-
confidence *= 0.5
|
|
242
|
-
feedback_parts.append(f"Max value {max_val:.2f} above expected {expected_max:.2f}")
|
|
243
|
-
|
|
244
|
-
# Check for reasonable value distribution
|
|
245
|
-
if std_val == 0:
|
|
246
|
-
confidence *= 0.7
|
|
247
|
-
feedback_parts.append("Warning: All values are identical - might be a constant field")
|
|
248
|
-
|
|
249
|
-
# Check for extremely large range (might indicate wrong scaling)
|
|
250
|
-
value_range = max_val - min_val
|
|
251
|
-
if value_range > 1e6:
|
|
252
|
-
confidence *= 0.6
|
|
253
|
-
feedback_parts.append("Warning: Very large value range - check scaling factor")
|
|
254
|
-
|
|
255
|
-
# Positive feedback
|
|
256
|
-
if is_valid and not feedback_parts:
|
|
257
|
-
feedback_parts.append(f"Values in expected range [{min_val:.2f}, {max_val:.2f}]")
|
|
258
|
-
if std_val > 0:
|
|
259
|
-
feedback_parts.append("Signal shows variation - likely represents real data")
|
|
260
|
-
|
|
261
|
-
feedback = "; ".join(feedback_parts) if feedback_parts else "Hypothesis test passed"
|
|
198
|
+
is_valid, confidence, feedback = _validate_hypothesis(stats, expected_min, expected_max)
|
|
262
199
|
|
|
263
200
|
return HypothesisResult(
|
|
264
201
|
signal_name=signal_name,
|
|
265
202
|
definition=definition,
|
|
266
203
|
values=decoded_values,
|
|
267
|
-
|
|
268
|
-
max_value=max_val,
|
|
269
|
-
mean=mean_val,
|
|
270
|
-
std=std_val,
|
|
204
|
+
**stats,
|
|
271
205
|
is_valid=is_valid,
|
|
272
206
|
confidence=confidence,
|
|
273
207
|
feedback=feedback,
|
|
@@ -373,3 +307,116 @@ class CANMessageWrapper:
|
|
|
373
307
|
def __repr__(self) -> str:
|
|
374
308
|
"""Human-readable representation."""
|
|
375
309
|
return f"CANMessageWrapper(id=0x{self._arbitration_id:03X}, documented_signals={len(self._documented_signals)})"
|
|
310
|
+
|
|
311
|
+
|
|
312
|
+
def _create_signal_definition(
|
|
313
|
+
signal_name: str,
|
|
314
|
+
start_byte: int,
|
|
315
|
+
bit_length: int,
|
|
316
|
+
byte_order: Literal["big_endian", "little_endian"],
|
|
317
|
+
value_type: Literal["unsigned", "signed", "float"],
|
|
318
|
+
scale: float,
|
|
319
|
+
offset: float,
|
|
320
|
+
unit: str,
|
|
321
|
+
) -> SignalDefinition:
|
|
322
|
+
"""Create signal definition from parameters."""
|
|
323
|
+
return SignalDefinition(
|
|
324
|
+
name=signal_name,
|
|
325
|
+
start_bit=start_byte * 8,
|
|
326
|
+
length=bit_length,
|
|
327
|
+
byte_order=byte_order,
|
|
328
|
+
value_type=value_type,
|
|
329
|
+
scale=scale,
|
|
330
|
+
offset=offset,
|
|
331
|
+
unit=unit,
|
|
332
|
+
)
|
|
333
|
+
|
|
334
|
+
|
|
335
|
+
def _decode_hypothesis_values(
|
|
336
|
+
session: CANSession, arbitration_id: int, definition: SignalDefinition
|
|
337
|
+
) -> list[float]:
|
|
338
|
+
"""Decode all values using signal definition."""
|
|
339
|
+
|
|
340
|
+
filtered = session._messages.filter_by_id(arbitration_id)
|
|
341
|
+
decoded_values = []
|
|
342
|
+
for msg in filtered.messages:
|
|
343
|
+
try:
|
|
344
|
+
value = definition.decode(msg.data)
|
|
345
|
+
decoded_values.append(value)
|
|
346
|
+
except Exception:
|
|
347
|
+
pass
|
|
348
|
+
return decoded_values
|
|
349
|
+
|
|
350
|
+
|
|
351
|
+
def _create_failed_hypothesis_result(
|
|
352
|
+
signal_name: str, definition: SignalDefinition
|
|
353
|
+
) -> HypothesisResult:
|
|
354
|
+
"""Create result for failed decoding."""
|
|
355
|
+
return HypothesisResult(
|
|
356
|
+
signal_name=signal_name,
|
|
357
|
+
definition=definition,
|
|
358
|
+
values=[],
|
|
359
|
+
min_value=0.0,
|
|
360
|
+
max_value=0.0,
|
|
361
|
+
mean=0.0,
|
|
362
|
+
std=0.0,
|
|
363
|
+
is_valid=False,
|
|
364
|
+
confidence=0.0,
|
|
365
|
+
feedback="Failed to decode any messages with this definition",
|
|
366
|
+
)
|
|
367
|
+
|
|
368
|
+
|
|
369
|
+
def _calculate_hypothesis_statistics(decoded_values: list[float]) -> dict[str, float]:
|
|
370
|
+
"""Calculate statistics for decoded values."""
|
|
371
|
+
arr = np.array(decoded_values)
|
|
372
|
+
return {
|
|
373
|
+
"min_value": float(np.min(arr)),
|
|
374
|
+
"max_value": float(np.max(arr)),
|
|
375
|
+
"mean": float(np.mean(arr)),
|
|
376
|
+
"std": float(np.std(arr)),
|
|
377
|
+
}
|
|
378
|
+
|
|
379
|
+
|
|
380
|
+
def _validate_hypothesis(
|
|
381
|
+
stats: dict[str, float], expected_min: float | None, expected_max: float | None
|
|
382
|
+
) -> tuple[bool, float, str]:
|
|
383
|
+
"""Validate hypothesis and generate feedback."""
|
|
384
|
+
is_valid = True
|
|
385
|
+
confidence = 1.0
|
|
386
|
+
feedback_parts = []
|
|
387
|
+
|
|
388
|
+
# Check expected range
|
|
389
|
+
if expected_min is not None and stats["min_value"] < expected_min:
|
|
390
|
+
is_valid = False
|
|
391
|
+
confidence *= 0.5
|
|
392
|
+
feedback_parts.append(
|
|
393
|
+
f"Min value {stats['min_value']:.2f} below expected {expected_min:.2f}"
|
|
394
|
+
)
|
|
395
|
+
|
|
396
|
+
if expected_max is not None and stats["max_value"] > expected_max:
|
|
397
|
+
is_valid = False
|
|
398
|
+
confidence *= 0.5
|
|
399
|
+
feedback_parts.append(
|
|
400
|
+
f"Max value {stats['max_value']:.2f} above expected {expected_max:.2f}"
|
|
401
|
+
)
|
|
402
|
+
|
|
403
|
+
# Check value distribution
|
|
404
|
+
if stats["std"] == 0:
|
|
405
|
+
confidence *= 0.7
|
|
406
|
+
feedback_parts.append("Warning: All values are identical - might be a constant field")
|
|
407
|
+
|
|
408
|
+
value_range = stats["max_value"] - stats["min_value"]
|
|
409
|
+
if value_range > 1e6:
|
|
410
|
+
confidence *= 0.6
|
|
411
|
+
feedback_parts.append("Warning: Very large value range - check scaling factor")
|
|
412
|
+
|
|
413
|
+
# Positive feedback
|
|
414
|
+
if is_valid and not feedback_parts:
|
|
415
|
+
feedback_parts.append(
|
|
416
|
+
f"Values in expected range [{stats['min_value']:.2f}, {stats['max_value']:.2f}]"
|
|
417
|
+
)
|
|
418
|
+
if stats["std"] > 0:
|
|
419
|
+
feedback_parts.append("Signal shows variation - likely represents real data")
|
|
420
|
+
|
|
421
|
+
feedback = "; ".join(feedback_parts) if feedback_parts else "Hypothesis test passed"
|
|
422
|
+
return is_valid, confidence, feedback
|
|
@@ -8,7 +8,7 @@ from __future__ import annotations
|
|
|
8
8
|
|
|
9
9
|
from collections import defaultdict
|
|
10
10
|
from dataclasses import dataclass
|
|
11
|
-
from typing import TYPE_CHECKING
|
|
11
|
+
from typing import TYPE_CHECKING, Any
|
|
12
12
|
|
|
13
13
|
import numpy as np
|
|
14
14
|
|
|
@@ -233,7 +233,13 @@ class PatternAnalyzer:
|
|
|
233
233
|
Raises:
|
|
234
234
|
ValueError: If max_sequence_length is invalid (<2 or >10) or
|
|
235
235
|
min_support is not in range [0.0, 1.0].
|
|
236
|
+
|
|
237
|
+
Example:
|
|
238
|
+
>>> from oscura.automotive.can import CANSession
|
|
239
|
+
>>> session = CANSession()
|
|
240
|
+
>>> sequences = PatternAnalyzer.find_message_sequences(session, max_sequence_length=3)
|
|
236
241
|
"""
|
|
242
|
+
# Validate parameters
|
|
237
243
|
if max_sequence_length < 2:
|
|
238
244
|
raise ValueError("max_sequence_length must be at least 2")
|
|
239
245
|
if max_sequence_length > 10:
|
|
@@ -242,25 +248,61 @@ class PatternAnalyzer:
|
|
|
242
248
|
raise ValueError("min_support must be between 0.0 and 1.0")
|
|
243
249
|
|
|
244
250
|
time_window_s = time_window_ms / 1000.0
|
|
245
|
-
|
|
246
|
-
# Get all messages sorted by timestamp
|
|
247
251
|
all_messages = sorted(session._messages.messages, key=lambda m: m.timestamp)
|
|
248
252
|
|
|
249
253
|
if not all_messages:
|
|
250
254
|
return []
|
|
251
255
|
|
|
252
|
-
#
|
|
256
|
+
# Calculate maximum message frequency for support calculation
|
|
257
|
+
max_count = PatternAnalyzer._calculate_max_message_frequency(all_messages)
|
|
258
|
+
|
|
259
|
+
# Mine sequences from message stream
|
|
260
|
+
sequences = PatternAnalyzer._mine_sequences(
|
|
261
|
+
all_messages, max_sequence_length, time_window_s
|
|
262
|
+
)
|
|
263
|
+
|
|
264
|
+
# Build and filter result objects
|
|
265
|
+
result = PatternAnalyzer._build_sequence_results(sequences, max_count, min_support)
|
|
266
|
+
|
|
267
|
+
# Sort by support (descending), then by occurrences
|
|
268
|
+
result.sort(key=lambda s: (s.support, s.occurrences), reverse=True)
|
|
269
|
+
|
|
270
|
+
return result
|
|
271
|
+
|
|
272
|
+
@staticmethod
|
|
273
|
+
def _calculate_max_message_frequency(messages: list[Any]) -> int:
|
|
274
|
+
"""Calculate maximum message frequency for support calculation.
|
|
275
|
+
|
|
276
|
+
Args:
|
|
277
|
+
messages: List of CAN messages.
|
|
278
|
+
|
|
279
|
+
Returns:
|
|
280
|
+
Maximum frequency of any message ID.
|
|
281
|
+
"""
|
|
253
282
|
id_counts: dict[int, int] = defaultdict(int)
|
|
254
|
-
for msg in
|
|
283
|
+
for msg in messages:
|
|
255
284
|
id_counts[msg.arbitration_id] += 1
|
|
256
|
-
|
|
285
|
+
return max(id_counts.values()) if id_counts else 1
|
|
257
286
|
|
|
258
|
-
|
|
287
|
+
@staticmethod
|
|
288
|
+
def _mine_sequences(
|
|
289
|
+
all_messages: list[Any],
|
|
290
|
+
max_sequence_length: int,
|
|
291
|
+
time_window_s: float,
|
|
292
|
+
) -> dict[tuple[int, ...], list[list[float]]]:
|
|
293
|
+
"""Mine message sequences from CAN message stream.
|
|
294
|
+
|
|
295
|
+
Args:
|
|
296
|
+
all_messages: Sorted list of CAN messages.
|
|
297
|
+
max_sequence_length: Maximum sequence length.
|
|
298
|
+
time_window_s: Time window in seconds.
|
|
299
|
+
|
|
300
|
+
Returns:
|
|
301
|
+
Dictionary mapping sequence IDs to timing lists.
|
|
302
|
+
"""
|
|
259
303
|
sequences: dict[tuple[int, ...], list[list[float]]] = defaultdict(list)
|
|
260
304
|
|
|
261
|
-
# Start with 2-message sequences
|
|
262
305
|
for i, msg_a in enumerate(all_messages):
|
|
263
|
-
# Look forward in time window
|
|
264
306
|
sequence_start = msg_a.timestamp
|
|
265
307
|
current_sequence = [msg_a.arbitration_id]
|
|
266
308
|
timing = []
|
|
@@ -276,6 +318,8 @@ class PatternAnalyzer:
|
|
|
276
318
|
# Record sequences of desired length
|
|
277
319
|
if 2 <= len(current_sequence) <= max_sequence_length:
|
|
278
320
|
seq_key = tuple(current_sequence)
|
|
321
|
+
# NECESSARY COPY: timing list reused across iterations.
|
|
322
|
+
# Without .copy(), all sequences would reference final state.
|
|
279
323
|
sequences[seq_key].append(timing.copy())
|
|
280
324
|
|
|
281
325
|
# Update for next iteration
|
|
@@ -283,7 +327,24 @@ class PatternAnalyzer:
|
|
|
283
327
|
if len(current_sequence) >= max_sequence_length:
|
|
284
328
|
break
|
|
285
329
|
|
|
286
|
-
|
|
330
|
+
return sequences
|
|
331
|
+
|
|
332
|
+
@staticmethod
|
|
333
|
+
def _build_sequence_results(
|
|
334
|
+
sequences: dict[tuple[int, ...], list[list[float]]],
|
|
335
|
+
max_count: int,
|
|
336
|
+
min_support: float,
|
|
337
|
+
) -> list[MessageSequence]:
|
|
338
|
+
"""Build MessageSequence objects from mined sequences.
|
|
339
|
+
|
|
340
|
+
Args:
|
|
341
|
+
sequences: Dictionary mapping sequence IDs to timing lists.
|
|
342
|
+
max_count: Maximum message frequency (for support calculation).
|
|
343
|
+
min_support: Minimum support threshold.
|
|
344
|
+
|
|
345
|
+
Returns:
|
|
346
|
+
List of MessageSequence objects that meet support threshold.
|
|
347
|
+
"""
|
|
287
348
|
result = []
|
|
288
349
|
for seq_ids, timing_lists in sequences.items():
|
|
289
350
|
occurrences = len(timing_lists)
|
|
@@ -291,14 +352,7 @@ class PatternAnalyzer:
|
|
|
291
352
|
|
|
292
353
|
if support >= min_support:
|
|
293
354
|
# Calculate average timing between consecutive messages
|
|
294
|
-
avg_timing =
|
|
295
|
-
if timing_lists:
|
|
296
|
-
# timing_lists[i] has len(seq_ids) - 1 elements
|
|
297
|
-
num_gaps = len(seq_ids) - 1
|
|
298
|
-
for gap_idx in range(num_gaps):
|
|
299
|
-
gap_times = [t[gap_idx] for t in timing_lists if gap_idx < len(t)]
|
|
300
|
-
if gap_times:
|
|
301
|
-
avg_timing.append(float(np.mean(gap_times)))
|
|
355
|
+
avg_timing = PatternAnalyzer._calculate_average_timing(seq_ids, timing_lists)
|
|
302
356
|
|
|
303
357
|
result.append(
|
|
304
358
|
MessageSequence(
|
|
@@ -309,11 +363,34 @@ class PatternAnalyzer:
|
|
|
309
363
|
)
|
|
310
364
|
)
|
|
311
365
|
|
|
312
|
-
# Sort by support (descending), then by occurrences
|
|
313
|
-
result.sort(key=lambda s: (s.support, s.occurrences), reverse=True)
|
|
314
|
-
|
|
315
366
|
return result
|
|
316
367
|
|
|
368
|
+
@staticmethod
|
|
369
|
+
def _calculate_average_timing(
|
|
370
|
+
seq_ids: tuple[int, ...],
|
|
371
|
+
timing_lists: list[list[float]],
|
|
372
|
+
) -> list[float]:
|
|
373
|
+
"""Calculate average timing between consecutive messages in sequence.
|
|
374
|
+
|
|
375
|
+
Args:
|
|
376
|
+
seq_ids: Sequence of message IDs.
|
|
377
|
+
timing_lists: List of timing lists for each occurrence.
|
|
378
|
+
|
|
379
|
+
Returns:
|
|
380
|
+
List of average timings for each gap in sequence.
|
|
381
|
+
"""
|
|
382
|
+
avg_timing: list[float] = []
|
|
383
|
+
if not timing_lists:
|
|
384
|
+
return avg_timing
|
|
385
|
+
|
|
386
|
+
num_gaps = len(seq_ids) - 1
|
|
387
|
+
for gap_idx in range(num_gaps):
|
|
388
|
+
gap_times = [t[gap_idx] for t in timing_lists if gap_idx < len(t)]
|
|
389
|
+
if gap_times:
|
|
390
|
+
avg_timing.append(float(np.mean(gap_times)))
|
|
391
|
+
|
|
392
|
+
return avg_timing
|
|
393
|
+
|
|
317
394
|
@staticmethod
|
|
318
395
|
def find_temporal_correlations(
|
|
319
396
|
session: CANSession,
|