oscura 0.7.0__py3-none-any.whl → 0.10.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 +19 -19
- oscura/analyzers/__init__.py +2 -0
- oscura/analyzers/digital/extraction.py +2 -3
- oscura/analyzers/digital/quality.py +1 -1
- oscura/analyzers/digital/timing.py +1 -1
- oscura/analyzers/eye/__init__.py +5 -1
- oscura/analyzers/eye/generation.py +501 -0
- oscura/analyzers/jitter/__init__.py +6 -6
- oscura/analyzers/jitter/timing.py +419 -0
- oscura/analyzers/patterns/__init__.py +94 -0
- oscura/analyzers/patterns/reverse_engineering.py +991 -0
- oscura/analyzers/power/__init__.py +35 -12
- oscura/analyzers/power/basic.py +3 -3
- oscura/analyzers/power/soa.py +1 -1
- oscura/analyzers/power/switching.py +3 -3
- oscura/analyzers/signal_classification.py +529 -0
- oscura/analyzers/signal_integrity/sparams.py +3 -3
- oscura/analyzers/statistics/__init__.py +4 -0
- oscura/analyzers/statistics/basic.py +152 -0
- oscura/analyzers/statistics/correlation.py +47 -6
- oscura/analyzers/validation.py +1 -1
- oscura/analyzers/waveform/__init__.py +2 -0
- oscura/analyzers/waveform/measurements.py +329 -163
- oscura/analyzers/waveform/measurements_with_uncertainty.py +91 -35
- oscura/analyzers/waveform/spectral.py +498 -54
- oscura/api/dsl/commands.py +15 -6
- oscura/api/server/templates/base.html +137 -146
- oscura/api/server/templates/export.html +84 -110
- oscura/api/server/templates/home.html +248 -267
- oscura/api/server/templates/protocols.html +44 -48
- oscura/api/server/templates/reports.html +27 -35
- oscura/api/server/templates/session_detail.html +68 -78
- oscura/api/server/templates/sessions.html +62 -72
- oscura/api/server/templates/waveforms.html +54 -64
- oscura/automotive/__init__.py +1 -1
- oscura/automotive/can/session.py +1 -1
- oscura/automotive/dbc/generator.py +638 -23
- oscura/automotive/dtc/data.json +102 -17
- oscura/automotive/uds/decoder.py +99 -6
- oscura/cli/analyze.py +8 -2
- oscura/cli/batch.py +36 -5
- oscura/cli/characterize.py +18 -4
- oscura/cli/export.py +47 -5
- oscura/cli/main.py +2 -0
- oscura/cli/onboarding/wizard.py +10 -6
- oscura/cli/pipeline.py +585 -0
- oscura/cli/visualize.py +6 -4
- oscura/convenience.py +400 -32
- oscura/core/config/loader.py +0 -1
- oscura/core/measurement_result.py +286 -0
- oscura/core/progress.py +1 -1
- oscura/core/schemas/device_mapping.json +8 -2
- oscura/core/schemas/packet_format.json +24 -4
- oscura/core/schemas/protocol_definition.json +12 -2
- oscura/core/types.py +300 -199
- oscura/correlation/multi_protocol.py +1 -1
- oscura/export/legacy/__init__.py +11 -0
- oscura/export/legacy/wav.py +75 -0
- oscura/exporters/__init__.py +19 -0
- oscura/exporters/wireshark.py +809 -0
- oscura/hardware/acquisition/file.py +5 -19
- oscura/hardware/acquisition/saleae.py +10 -10
- oscura/hardware/acquisition/socketcan.py +4 -6
- oscura/hardware/acquisition/synthetic.py +1 -5
- oscura/hardware/acquisition/visa.py +6 -6
- oscura/hardware/security/side_channel_detector.py +5 -508
- oscura/inference/message_format.py +686 -1
- oscura/jupyter/display.py +2 -2
- oscura/jupyter/magic.py +3 -3
- oscura/loaders/__init__.py +17 -12
- oscura/loaders/binary.py +1 -1
- oscura/loaders/chipwhisperer.py +1 -2
- oscura/loaders/configurable.py +1 -1
- oscura/loaders/csv_loader.py +2 -2
- oscura/loaders/hdf5_loader.py +1 -1
- oscura/loaders/lazy.py +6 -1
- oscura/loaders/mmap_loader.py +0 -1
- oscura/loaders/numpy_loader.py +8 -7
- oscura/loaders/preprocessing.py +3 -5
- oscura/loaders/rigol.py +21 -7
- oscura/loaders/sigrok.py +2 -5
- oscura/loaders/tdms.py +3 -2
- oscura/loaders/tektronix.py +38 -32
- oscura/loaders/tss.py +20 -27
- oscura/loaders/vcd.py +13 -8
- oscura/loaders/wav.py +1 -6
- oscura/pipeline/__init__.py +76 -0
- oscura/pipeline/handlers/__init__.py +165 -0
- oscura/pipeline/handlers/analyzers.py +1045 -0
- oscura/pipeline/handlers/decoders.py +899 -0
- oscura/pipeline/handlers/exporters.py +1103 -0
- oscura/pipeline/handlers/filters.py +891 -0
- oscura/pipeline/handlers/loaders.py +640 -0
- oscura/pipeline/handlers/transforms.py +768 -0
- oscura/reporting/__init__.py +88 -1
- oscura/reporting/automation.py +348 -0
- oscura/reporting/citations.py +374 -0
- oscura/reporting/core.py +54 -0
- oscura/reporting/formatting/__init__.py +11 -0
- oscura/reporting/formatting/measurements.py +320 -0
- oscura/reporting/html.py +57 -0
- oscura/reporting/interpretation.py +431 -0
- oscura/reporting/summary.py +329 -0
- oscura/reporting/templates/enhanced/protocol_re.html +504 -503
- oscura/reporting/visualization.py +542 -0
- oscura/side_channel/__init__.py +38 -57
- oscura/utils/builders/signal_builder.py +5 -5
- oscura/utils/comparison/compare.py +7 -9
- oscura/utils/comparison/golden.py +1 -1
- oscura/utils/filtering/convenience.py +2 -2
- oscura/utils/math/arithmetic.py +38 -62
- oscura/utils/math/interpolation.py +20 -20
- oscura/utils/pipeline/__init__.py +4 -17
- oscura/utils/progressive.py +1 -4
- oscura/utils/triggering/edge.py +1 -1
- oscura/utils/triggering/pattern.py +2 -2
- oscura/utils/triggering/pulse.py +2 -2
- oscura/utils/triggering/window.py +3 -3
- oscura/validation/hil_testing.py +11 -11
- oscura/visualization/__init__.py +47 -284
- oscura/visualization/batch.py +160 -0
- oscura/visualization/plot.py +542 -53
- oscura/visualization/styles.py +184 -318
- oscura/workflows/__init__.py +2 -0
- oscura/workflows/batch/advanced.py +1 -1
- oscura/workflows/batch/aggregate.py +7 -8
- oscura/workflows/complete_re.py +251 -23
- oscura/workflows/digital.py +27 -4
- oscura/workflows/multi_trace.py +136 -17
- oscura/workflows/waveform.py +788 -0
- {oscura-0.7.0.dist-info → oscura-0.10.0.dist-info}/METADATA +59 -79
- {oscura-0.7.0.dist-info → oscura-0.10.0.dist-info}/RECORD +135 -149
- oscura/side_channel/dpa.py +0 -1025
- oscura/utils/optimization/__init__.py +0 -19
- oscura/utils/optimization/parallel.py +0 -443
- oscura/utils/optimization/search.py +0 -532
- oscura/utils/pipeline/base.py +0 -338
- oscura/utils/pipeline/composition.py +0 -248
- oscura/utils/pipeline/parallel.py +0 -449
- oscura/utils/pipeline/pipeline.py +0 -375
- oscura/utils/search/__init__.py +0 -16
- oscura/utils/search/anomaly.py +0 -424
- oscura/utils/search/context.py +0 -294
- oscura/utils/search/pattern.py +0 -288
- oscura/utils/storage/__init__.py +0 -61
- oscura/utils/storage/database.py +0 -1166
- oscura/visualization/accessibility.py +0 -526
- oscura/visualization/annotations.py +0 -371
- oscura/visualization/axis_scaling.py +0 -305
- oscura/visualization/colors.py +0 -451
- oscura/visualization/digital.py +0 -436
- oscura/visualization/eye.py +0 -571
- oscura/visualization/histogram.py +0 -281
- oscura/visualization/interactive.py +0 -1035
- oscura/visualization/jitter.py +0 -1042
- oscura/visualization/keyboard.py +0 -394
- oscura/visualization/layout.py +0 -400
- oscura/visualization/optimization.py +0 -1079
- oscura/visualization/palettes.py +0 -446
- oscura/visualization/power.py +0 -508
- oscura/visualization/power_extended.py +0 -955
- oscura/visualization/presets.py +0 -469
- oscura/visualization/protocols.py +0 -1246
- oscura/visualization/render.py +0 -223
- oscura/visualization/rendering.py +0 -444
- oscura/visualization/reverse_engineering.py +0 -838
- oscura/visualization/signal_integrity.py +0 -989
- oscura/visualization/specialized.py +0 -643
- oscura/visualization/spectral.py +0 -1226
- oscura/visualization/thumbnails.py +0 -340
- oscura/visualization/time_axis.py +0 -351
- oscura/visualization/waveform.py +0 -454
- {oscura-0.7.0.dist-info → oscura-0.10.0.dist-info}/WHEEL +0 -0
- {oscura-0.7.0.dist-info → oscura-0.10.0.dist-info}/entry_points.txt +0 -0
- {oscura-0.7.0.dist-info → oscura-0.10.0.dist-info}/licenses/LICENSE +0 -0
|
@@ -97,7 +97,7 @@ class WindowTrigger(Trigger):
|
|
|
97
97
|
List of trigger events for window crossings.
|
|
98
98
|
"""
|
|
99
99
|
data = trace.data
|
|
100
|
-
sample_period = trace.metadata.
|
|
100
|
+
sample_period = 1.0 / trace.metadata.sample_rate
|
|
101
101
|
events: list[TriggerEvent] = []
|
|
102
102
|
|
|
103
103
|
# Determine if each sample is inside the window
|
|
@@ -182,7 +182,7 @@ class ZoneTrigger(Trigger):
|
|
|
182
182
|
List of trigger events.
|
|
183
183
|
"""
|
|
184
184
|
data = trace.data
|
|
185
|
-
sample_period = trace.metadata.
|
|
185
|
+
sample_period = 1.0 / trace.metadata.sample_rate
|
|
186
186
|
time_vector = np.arange(len(data)) * sample_period
|
|
187
187
|
events: list[TriggerEvent] = []
|
|
188
188
|
|
|
@@ -396,7 +396,7 @@ class MaskTrigger(Trigger):
|
|
|
396
396
|
mask_path = Path(self.mask_points)
|
|
397
397
|
|
|
398
398
|
data = trace.data
|
|
399
|
-
sample_period = trace.metadata.
|
|
399
|
+
sample_period = 1.0 / trace.metadata.sample_rate
|
|
400
400
|
time_vector = np.arange(len(data)) * sample_period
|
|
401
401
|
|
|
402
402
|
# Create points array for containment test
|
oscura/validation/hil_testing.py
CHANGED
|
@@ -35,7 +35,7 @@ from enum import Enum
|
|
|
35
35
|
from typing import Any, Protocol
|
|
36
36
|
|
|
37
37
|
try:
|
|
38
|
-
import can
|
|
38
|
+
import can
|
|
39
39
|
except ImportError:
|
|
40
40
|
can = None # type: ignore[assignment]
|
|
41
41
|
|
|
@@ -46,18 +46,18 @@ except ImportError:
|
|
|
46
46
|
# Create module structure for test patching even when pyusb unavailable
|
|
47
47
|
import types
|
|
48
48
|
|
|
49
|
-
usb = types.ModuleType("usb")
|
|
49
|
+
usb = types.ModuleType("usb")
|
|
50
50
|
usb.core = None # type: ignore[attr-defined]
|
|
51
51
|
|
|
52
52
|
try:
|
|
53
53
|
import spidev # type: ignore[import-not-found]
|
|
54
54
|
except ImportError:
|
|
55
|
-
spidev = None
|
|
55
|
+
spidev = None
|
|
56
56
|
|
|
57
57
|
try:
|
|
58
58
|
from smbus2 import SMBus # type: ignore[import-not-found]
|
|
59
59
|
except ImportError:
|
|
60
|
-
SMBus = None
|
|
60
|
+
SMBus = None
|
|
61
61
|
|
|
62
62
|
try:
|
|
63
63
|
import RPi.GPIO as GPIO # type: ignore[import-untyped]
|
|
@@ -65,23 +65,23 @@ except ImportError:
|
|
|
65
65
|
try:
|
|
66
66
|
import gpiod # type: ignore[import-not-found]
|
|
67
67
|
|
|
68
|
-
GPIO = None
|
|
68
|
+
GPIO = None
|
|
69
69
|
except ImportError:
|
|
70
|
-
GPIO = None
|
|
71
|
-
gpiod = None
|
|
70
|
+
GPIO = None
|
|
71
|
+
gpiod = None
|
|
72
72
|
|
|
73
73
|
try:
|
|
74
74
|
from scapy.all import IP, UDP, Packet, wrpcap # type: ignore[attr-defined]
|
|
75
75
|
except ImportError:
|
|
76
|
-
IP = None
|
|
77
|
-
UDP = None
|
|
76
|
+
IP = None
|
|
77
|
+
UDP = None
|
|
78
78
|
Packet = None # type: ignore[assignment,misc]
|
|
79
79
|
wrpcap = None # type: ignore[assignment]
|
|
80
80
|
|
|
81
81
|
try:
|
|
82
82
|
import serial # type: ignore[import-untyped]
|
|
83
83
|
except ImportError:
|
|
84
|
-
serial = None
|
|
84
|
+
serial = None
|
|
85
85
|
|
|
86
86
|
from oscura.utils.serial import connect_serial_port
|
|
87
87
|
|
|
@@ -960,7 +960,7 @@ class HILTester:
|
|
|
960
960
|
# Create UDP packet for received data if present
|
|
961
961
|
if recv_data:
|
|
962
962
|
pkt = IP(src="192.168.1.1") / UDP(sport=12345) / bytes(recv_data)
|
|
963
|
-
pkt.time = timestamp + 0.001
|
|
963
|
+
pkt.time = timestamp + 0.001
|
|
964
964
|
packets.append(pkt)
|
|
965
965
|
|
|
966
966
|
if packets:
|
oscura/visualization/__init__.py
CHANGED
|
@@ -1,330 +1,93 @@
|
|
|
1
1
|
"""Visualization module for Oscura.
|
|
2
2
|
|
|
3
|
-
Provides plotting functions for waveforms, spectra,
|
|
4
|
-
|
|
3
|
+
Provides simple matplotlib-based plotting functions for waveforms, spectra,
|
|
4
|
+
and digital signals. All functions accept and return matplotlib axes for
|
|
5
|
+
composability.
|
|
5
6
|
|
|
6
7
|
**Requires matplotlib:**
|
|
7
|
-
|
|
8
|
-
pip install oscura[visualization] # Just visualization
|
|
9
|
-
pip install oscura[standard] # Recommended
|
|
10
|
-
pip install oscura[all] # Everything
|
|
8
|
+
Install with: pip install oscura[visualization]
|
|
11
9
|
|
|
12
10
|
Example:
|
|
13
|
-
>>> from oscura.visualization import plot_waveform, plot_spectrum
|
|
14
|
-
>>> from oscura.visualization import plot_timing, plot_eye
|
|
15
|
-
>>> from oscura.visualization import plot_bode, plot_histogram
|
|
16
|
-
>>> from oscura.visualization import apply_style_preset
|
|
17
11
|
>>> import matplotlib.pyplot as plt
|
|
18
|
-
>>>
|
|
19
|
-
|
|
20
|
-
|
|
12
|
+
>>> import oscura as osc
|
|
13
|
+
>>> from oscura.visualization import plot_waveform, plot_fft
|
|
14
|
+
>>>
|
|
15
|
+
>>> trace = osc.load("signal.wfm")
|
|
16
|
+
>>> fig, (ax1, ax2) = plt.subplots(2, 1)
|
|
17
|
+
>>> plot_waveform(ax1, trace)
|
|
18
|
+
>>> plot_fft(ax2, trace)
|
|
19
|
+
>>> plt.savefig("analysis.png")
|
|
21
20
|
"""
|
|
22
21
|
|
|
23
|
-
#
|
|
24
|
-
#
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
from oscura.visualization import plot
|
|
29
|
-
from oscura.visualization.accessibility import (
|
|
30
|
-
FAIL_SYMBOL,
|
|
31
|
-
LINE_STYLES,
|
|
32
|
-
PASS_SYMBOL,
|
|
33
|
-
KeyboardHandler,
|
|
34
|
-
add_plot_aria_attributes,
|
|
35
|
-
format_pass_fail,
|
|
36
|
-
generate_alt_text,
|
|
37
|
-
get_colorblind_palette,
|
|
38
|
-
get_multi_line_styles,
|
|
39
|
-
)
|
|
40
|
-
|
|
41
|
-
# Phase 30 enhancements
|
|
42
|
-
from oscura.visualization.annotations import (
|
|
43
|
-
Annotation as EnhancedAnnotation,
|
|
44
|
-
)
|
|
45
|
-
from oscura.visualization.annotations import (
|
|
46
|
-
PlacedAnnotation as EnhancedPlacedAnnotation,
|
|
47
|
-
)
|
|
48
|
-
from oscura.visualization.annotations import (
|
|
49
|
-
create_priority_annotation,
|
|
50
|
-
filter_by_zoom_level,
|
|
51
|
-
place_annotations,
|
|
52
|
-
)
|
|
53
|
-
from oscura.visualization.axis_scaling import (
|
|
54
|
-
calculate_axis_limits,
|
|
55
|
-
calculate_multi_channel_limits,
|
|
56
|
-
suggest_tick_spacing,
|
|
57
|
-
)
|
|
58
|
-
from oscura.visualization.colors import (
|
|
59
|
-
COLORBLIND_SAFE_QUALITATIVE,
|
|
60
|
-
DIVERGING_COOLWARM,
|
|
61
|
-
SEQUENTIAL_VIRIDIS,
|
|
62
|
-
select_optimal_palette,
|
|
63
|
-
)
|
|
64
|
-
from oscura.visualization.digital import (
|
|
65
|
-
plot_logic_analyzer,
|
|
66
|
-
plot_timing,
|
|
67
|
-
)
|
|
68
|
-
from oscura.visualization.eye import (
|
|
22
|
+
# Import core plotting functions
|
|
23
|
+
# Import batch utilities
|
|
24
|
+
from oscura.visualization import batch
|
|
25
|
+
from oscura.visualization.batch import fig_to_base64, generate_all_plots
|
|
26
|
+
from oscura.visualization.plot import (
|
|
69
27
|
plot_bathtub,
|
|
70
28
|
plot_eye,
|
|
71
|
-
|
|
72
|
-
from oscura.visualization.histogram import (
|
|
73
|
-
calculate_bin_edges,
|
|
74
|
-
calculate_optimal_bins,
|
|
75
|
-
)
|
|
76
|
-
from oscura.visualization.interactive import (
|
|
77
|
-
CursorMeasurement,
|
|
78
|
-
ZoomState,
|
|
79
|
-
add_measurement_cursors,
|
|
80
|
-
enable_zoom_pan,
|
|
81
|
-
plot_bode,
|
|
29
|
+
plot_fft,
|
|
82
30
|
plot_histogram,
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
plot_with_cursors,
|
|
86
|
-
)
|
|
87
|
-
from oscura.visualization.layout import (
|
|
88
|
-
Annotation,
|
|
89
|
-
ChannelLayout,
|
|
90
|
-
PlacedAnnotation,
|
|
91
|
-
layout_stacked_channels,
|
|
92
|
-
optimize_annotation_placement,
|
|
93
|
-
)
|
|
94
|
-
from oscura.visualization.optimization import (
|
|
95
|
-
InterestingRegion,
|
|
96
|
-
calculate_grid_spacing,
|
|
97
|
-
calculate_optimal_x_window,
|
|
98
|
-
calculate_optimal_y_range,
|
|
99
|
-
decimate_for_display,
|
|
100
|
-
detect_interesting_regions,
|
|
101
|
-
optimize_db_range,
|
|
102
|
-
)
|
|
103
|
-
from oscura.visualization.presets import (
|
|
104
|
-
DARK_THEME_PRESET,
|
|
105
|
-
IEEE_DOUBLE_COLUMN_PRESET,
|
|
106
|
-
IEEE_PUBLICATION_PRESET,
|
|
107
|
-
VisualizationPreset,
|
|
108
|
-
apply_preset,
|
|
109
|
-
get_preset_colors,
|
|
110
|
-
)
|
|
111
|
-
from oscura.visualization.presets import (
|
|
112
|
-
create_custom_preset as create_custom_visualization_preset,
|
|
113
|
-
)
|
|
114
|
-
from oscura.visualization.presets import (
|
|
115
|
-
list_presets as list_visualization_presets,
|
|
116
|
-
)
|
|
117
|
-
from oscura.visualization.protocols import (
|
|
118
|
-
plot_can_decode,
|
|
119
|
-
plot_i2c_decode,
|
|
31
|
+
plot_logic_analyzer,
|
|
32
|
+
plot_multi_channel,
|
|
120
33
|
plot_protocol_decode,
|
|
121
|
-
plot_spi_decode,
|
|
122
|
-
plot_uart_decode,
|
|
123
|
-
)
|
|
124
|
-
from oscura.visualization.render import (
|
|
125
|
-
RenderPreset,
|
|
126
|
-
apply_rendering_config,
|
|
127
|
-
configure_dpi_rendering,
|
|
128
|
-
)
|
|
129
|
-
from oscura.visualization.rendering import (
|
|
130
|
-
StreamingRenderer,
|
|
131
|
-
downsample_for_memory,
|
|
132
|
-
estimate_memory_usage,
|
|
133
|
-
progressive_render,
|
|
134
|
-
render_with_lod,
|
|
135
|
-
)
|
|
136
|
-
|
|
137
|
-
# Reverse Engineering Visualizations (HIGH-2)
|
|
138
|
-
from oscura.visualization.reverse_engineering import (
|
|
139
|
-
plot_crc_parameters,
|
|
140
|
-
plot_field_confidence_heatmap,
|
|
141
|
-
plot_message_field_layout,
|
|
142
|
-
plot_message_type_distribution,
|
|
143
|
-
plot_pipeline_timing,
|
|
144
|
-
plot_protocol_candidates,
|
|
145
|
-
plot_re_summary,
|
|
146
|
-
)
|
|
147
|
-
from oscura.visualization.specialized import (
|
|
148
|
-
ProtocolSignal,
|
|
149
|
-
StateTransition,
|
|
150
|
-
plot_protocol_timing,
|
|
151
|
-
plot_state_machine,
|
|
152
|
-
)
|
|
153
|
-
from oscura.visualization.spectral import (
|
|
154
|
-
plot_fft,
|
|
155
34
|
plot_psd,
|
|
156
35
|
plot_quality_summary,
|
|
157
36
|
plot_spectrogram,
|
|
158
37
|
plot_spectrum,
|
|
38
|
+
plot_state_machine,
|
|
159
39
|
plot_thd_bars,
|
|
160
|
-
|
|
161
|
-
from oscura.visualization.styles import (
|
|
162
|
-
PRESENTATION_PRESET,
|
|
163
|
-
PRINT_PRESET,
|
|
164
|
-
PUBLICATION_PRESET,
|
|
165
|
-
SCREEN_PRESET,
|
|
166
|
-
StylePreset,
|
|
167
|
-
apply_style_preset,
|
|
168
|
-
create_custom_preset,
|
|
169
|
-
)
|
|
170
|
-
from oscura.visualization.thumbnails import (
|
|
171
|
-
render_thumbnail,
|
|
172
|
-
render_thumbnail_multichannel,
|
|
173
|
-
)
|
|
174
|
-
from oscura.visualization.time_axis import (
|
|
175
|
-
TimeUnit,
|
|
176
|
-
calculate_major_ticks,
|
|
177
|
-
convert_time_values,
|
|
178
|
-
create_relative_time,
|
|
179
|
-
format_cursor_readout,
|
|
180
|
-
format_time_labels,
|
|
181
|
-
select_time_unit,
|
|
182
|
-
)
|
|
183
|
-
from oscura.visualization.waveform import (
|
|
184
|
-
plot_multi_channel,
|
|
40
|
+
plot_timing,
|
|
185
41
|
plot_waveform,
|
|
186
42
|
plot_xy,
|
|
187
43
|
)
|
|
188
44
|
|
|
45
|
+
# Import styling utilities
|
|
46
|
+
from oscura.visualization.styles import (
|
|
47
|
+
COLORBLIND_PALETTE,
|
|
48
|
+
FAIL_SYMBOL,
|
|
49
|
+
IEEE_STYLE,
|
|
50
|
+
LINE_STYLES,
|
|
51
|
+
PASS_SYMBOL,
|
|
52
|
+
apply_ieee_style,
|
|
53
|
+
apply_presentation_style,
|
|
54
|
+
format_pass_fail,
|
|
55
|
+
get_colorblind_cmap,
|
|
56
|
+
get_colorblind_palette,
|
|
57
|
+
get_line_styles,
|
|
58
|
+
get_multi_line_styles,
|
|
59
|
+
)
|
|
60
|
+
|
|
189
61
|
__all__ = [
|
|
190
|
-
"
|
|
191
|
-
"DARK_THEME_PRESET",
|
|
192
|
-
"DIVERGING_COOLWARM",
|
|
62
|
+
"COLORBLIND_PALETTE",
|
|
193
63
|
"FAIL_SYMBOL",
|
|
194
|
-
"
|
|
195
|
-
"IEEE_PUBLICATION_PRESET",
|
|
64
|
+
"IEEE_STYLE",
|
|
196
65
|
"LINE_STYLES",
|
|
197
66
|
"PASS_SYMBOL",
|
|
198
|
-
"
|
|
199
|
-
"
|
|
200
|
-
"
|
|
201
|
-
"
|
|
202
|
-
"SEQUENTIAL_VIRIDIS",
|
|
203
|
-
"Annotation",
|
|
204
|
-
# Layout functions (VIS-015, VIS-016)
|
|
205
|
-
"ChannelLayout",
|
|
206
|
-
# Interactive (VIS-008)
|
|
207
|
-
"CursorMeasurement",
|
|
208
|
-
# Phase 30: Enhanced modules
|
|
209
|
-
"EnhancedAnnotation",
|
|
210
|
-
"EnhancedPlacedAnnotation",
|
|
211
|
-
"InterestingRegion",
|
|
212
|
-
"KeyboardHandler",
|
|
213
|
-
"PlacedAnnotation",
|
|
214
|
-
"ProtocolSignal",
|
|
215
|
-
"RenderPreset",
|
|
216
|
-
"StateTransition",
|
|
217
|
-
"StreamingRenderer",
|
|
218
|
-
"StylePreset",
|
|
219
|
-
"TimeUnit",
|
|
220
|
-
"VisualizationPreset",
|
|
221
|
-
# Interactive (VIS-007)
|
|
222
|
-
"ZoomState",
|
|
223
|
-
# Interactive (VIS-008)
|
|
224
|
-
"add_measurement_cursors",
|
|
225
|
-
"add_plot_aria_attributes",
|
|
226
|
-
"apply_preset",
|
|
227
|
-
"apply_rendering_config",
|
|
228
|
-
# Styles
|
|
229
|
-
"apply_style_preset",
|
|
230
|
-
"calculate_axis_limits",
|
|
231
|
-
"calculate_bin_edges",
|
|
232
|
-
"calculate_grid_spacing",
|
|
233
|
-
"calculate_major_ticks",
|
|
234
|
-
"calculate_multi_channel_limits",
|
|
235
|
-
# Histogram
|
|
236
|
-
"calculate_optimal_bins",
|
|
237
|
-
"calculate_optimal_x_window",
|
|
238
|
-
# Optimization functions (VIS-013, VIS-014, VIS-019, VIS-020, VIS-022)
|
|
239
|
-
"calculate_optimal_y_range",
|
|
240
|
-
# Rendering functions (VIS-017)
|
|
241
|
-
"configure_dpi_rendering",
|
|
242
|
-
"convert_time_values",
|
|
243
|
-
"create_custom_preset",
|
|
244
|
-
"create_custom_visualization_preset",
|
|
245
|
-
"create_priority_annotation",
|
|
246
|
-
"create_relative_time",
|
|
247
|
-
"decimate_for_display",
|
|
248
|
-
"detect_interesting_regions",
|
|
249
|
-
"downsample_for_memory",
|
|
250
|
-
# Interactive (VIS-007)
|
|
251
|
-
"enable_zoom_pan",
|
|
252
|
-
"estimate_memory_usage",
|
|
253
|
-
"filter_by_zoom_level",
|
|
254
|
-
"format_cursor_readout",
|
|
67
|
+
"apply_ieee_style",
|
|
68
|
+
"apply_presentation_style",
|
|
69
|
+
"batch",
|
|
70
|
+
"fig_to_base64",
|
|
255
71
|
"format_pass_fail",
|
|
256
|
-
"
|
|
257
|
-
"
|
|
258
|
-
# Accessibility (ACC-001, ACC-002, ACC-003)
|
|
72
|
+
"generate_all_plots",
|
|
73
|
+
"get_colorblind_cmap",
|
|
259
74
|
"get_colorblind_palette",
|
|
75
|
+
"get_line_styles",
|
|
260
76
|
"get_multi_line_styles",
|
|
261
|
-
"get_preset_colors",
|
|
262
|
-
"layout_stacked_channels",
|
|
263
|
-
"list_visualization_presets",
|
|
264
|
-
"optimize_annotation_placement",
|
|
265
|
-
"optimize_db_range",
|
|
266
|
-
"place_annotations",
|
|
267
|
-
"plot",
|
|
268
|
-
# Eye diagram (VIS-006)
|
|
269
77
|
"plot_bathtub",
|
|
270
|
-
# Interactive (VIS-010)
|
|
271
|
-
"plot_bode",
|
|
272
|
-
# Protocol decode visualization (VIS-030)
|
|
273
|
-
"plot_can_decode",
|
|
274
|
-
# Reverse Engineering Visualization (HIGH-2)
|
|
275
|
-
"plot_crc_parameters",
|
|
276
78
|
"plot_eye",
|
|
277
|
-
# Spectral plotting
|
|
278
79
|
"plot_fft",
|
|
279
|
-
# Reverse Engineering Visualization (HIGH-2)
|
|
280
|
-
"plot_field_confidence_heatmap",
|
|
281
|
-
# Interactive (VIS-012)
|
|
282
80
|
"plot_histogram",
|
|
283
|
-
# Protocol decode visualization (VIS-030)
|
|
284
|
-
"plot_i2c_decode",
|
|
285
|
-
# Digital visualization (VIS-005)
|
|
286
81
|
"plot_logic_analyzer",
|
|
287
|
-
# Reverse Engineering Visualization (HIGH-2)
|
|
288
|
-
"plot_message_field_layout",
|
|
289
|
-
"plot_message_type_distribution",
|
|
290
82
|
"plot_multi_channel",
|
|
291
|
-
# Interactive (VIS-009)
|
|
292
|
-
"plot_phase",
|
|
293
|
-
# Reverse Engineering Visualization (HIGH-2)
|
|
294
|
-
"plot_pipeline_timing",
|
|
295
|
-
# Protocol decode visualization (VIS-030)
|
|
296
|
-
"plot_protocol_candidates",
|
|
297
83
|
"plot_protocol_decode",
|
|
298
|
-
"plot_protocol_timing",
|
|
299
84
|
"plot_psd",
|
|
300
|
-
# Signal quality summary (IEEE 1241-2010)
|
|
301
85
|
"plot_quality_summary",
|
|
302
|
-
# Reverse Engineering Visualization (HIGH-2)
|
|
303
|
-
"plot_re_summary",
|
|
304
86
|
"plot_spectrogram",
|
|
305
87
|
"plot_spectrum",
|
|
306
|
-
# Protocol decode visualization (VIS-030)
|
|
307
|
-
"plot_spi_decode",
|
|
308
88
|
"plot_state_machine",
|
|
309
|
-
# THD harmonic bar chart
|
|
310
89
|
"plot_thd_bars",
|
|
311
90
|
"plot_timing",
|
|
312
|
-
# Protocol decode visualization (VIS-030)
|
|
313
|
-
"plot_uart_decode",
|
|
314
|
-
# Interactive (VIS-011)
|
|
315
|
-
"plot_waterfall",
|
|
316
|
-
# Waveform plotting
|
|
317
91
|
"plot_waveform",
|
|
318
|
-
# Interactive (VIS-008)
|
|
319
|
-
"plot_with_cursors",
|
|
320
92
|
"plot_xy",
|
|
321
|
-
"progressive_render",
|
|
322
|
-
# Thumbnails
|
|
323
|
-
"render_thumbnail",
|
|
324
|
-
"render_thumbnail_multichannel",
|
|
325
|
-
"render_with_lod",
|
|
326
|
-
# Colors
|
|
327
|
-
"select_optimal_palette",
|
|
328
|
-
"select_time_unit",
|
|
329
|
-
"suggest_tick_spacing",
|
|
330
93
|
]
|
|
@@ -0,0 +1,160 @@
|
|
|
1
|
+
"""Batch plot generation for comprehensive signal visualization.
|
|
2
|
+
|
|
3
|
+
Generate multiple related plots from signal traces in a single operation,
|
|
4
|
+
useful for comprehensive analysis reports.
|
|
5
|
+
|
|
6
|
+
Example:
|
|
7
|
+
>>> from oscura.visualization import batch
|
|
8
|
+
>>> trace = osc.load("signal.wfm")
|
|
9
|
+
>>> plots = batch.generate_all_plots(trace)
|
|
10
|
+
>>> # Returns: {"waveform": <base64>, "fft": <base64>, ...}
|
|
11
|
+
"""
|
|
12
|
+
|
|
13
|
+
from __future__ import annotations
|
|
14
|
+
|
|
15
|
+
import base64
|
|
16
|
+
from io import BytesIO
|
|
17
|
+
from typing import TYPE_CHECKING
|
|
18
|
+
|
|
19
|
+
import matplotlib
|
|
20
|
+
|
|
21
|
+
matplotlib.use("Agg") # Non-interactive backend
|
|
22
|
+
import matplotlib.pyplot as plt
|
|
23
|
+
|
|
24
|
+
from oscura.core.types import DigitalTrace, WaveformTrace
|
|
25
|
+
|
|
26
|
+
if TYPE_CHECKING:
|
|
27
|
+
from matplotlib.figure import Figure
|
|
28
|
+
|
|
29
|
+
# Plot configuration
|
|
30
|
+
PLOT_DPI = 150
|
|
31
|
+
FIGURE_SIZE = (10, 6)
|
|
32
|
+
|
|
33
|
+
|
|
34
|
+
def fig_to_base64(fig: Figure, *, dpi: int = PLOT_DPI) -> str:
|
|
35
|
+
"""Convert matplotlib figure to base64-encoded PNG.
|
|
36
|
+
|
|
37
|
+
Args:
|
|
38
|
+
fig: Matplotlib figure object.
|
|
39
|
+
dpi: Resolution in dots per inch.
|
|
40
|
+
|
|
41
|
+
Returns:
|
|
42
|
+
Base64-encoded PNG image string with data URI prefix.
|
|
43
|
+
|
|
44
|
+
Example:
|
|
45
|
+
>>> fig, ax = plt.subplots()
|
|
46
|
+
>>> ax.plot([1, 2, 3])
|
|
47
|
+
>>> b64 = fig_to_base64(fig)
|
|
48
|
+
"""
|
|
49
|
+
buf = BytesIO()
|
|
50
|
+
fig.savefig(buf, format="png", dpi=dpi, bbox_inches="tight", facecolor="white")
|
|
51
|
+
buf.seek(0)
|
|
52
|
+
img_base64 = base64.b64encode(buf.read()).decode("utf-8")
|
|
53
|
+
plt.close(fig)
|
|
54
|
+
return f"data:image/png;base64,{img_base64}"
|
|
55
|
+
|
|
56
|
+
|
|
57
|
+
def generate_all_plots(
|
|
58
|
+
trace: WaveformTrace | DigitalTrace,
|
|
59
|
+
*,
|
|
60
|
+
verbose: bool = True,
|
|
61
|
+
) -> dict[str, str]:
|
|
62
|
+
"""Generate all applicable plots for a signal trace.
|
|
63
|
+
|
|
64
|
+
Automatically detects signal type and generates appropriate plots:
|
|
65
|
+
- Analog: waveform, FFT, histogram, spectrogram
|
|
66
|
+
- Digital: waveform, timing diagram
|
|
67
|
+
|
|
68
|
+
Args:
|
|
69
|
+
trace: WaveformTrace or DigitalTrace to visualize.
|
|
70
|
+
verbose: Print progress messages.
|
|
71
|
+
|
|
72
|
+
Returns:
|
|
73
|
+
Dictionary mapping plot names to base64 image strings.
|
|
74
|
+
|
|
75
|
+
Example:
|
|
76
|
+
>>> trace = osc.load("signal.wfm")
|
|
77
|
+
>>> plots = generate_all_plots(trace)
|
|
78
|
+
>>> len(plots) # 4-5 plots for analog signal
|
|
79
|
+
4
|
|
80
|
+
"""
|
|
81
|
+
from oscura.visualization.plot import (
|
|
82
|
+
plot_fft,
|
|
83
|
+
plot_histogram,
|
|
84
|
+
plot_spectrogram,
|
|
85
|
+
plot_timing,
|
|
86
|
+
plot_waveform,
|
|
87
|
+
)
|
|
88
|
+
|
|
89
|
+
plots = {}
|
|
90
|
+
|
|
91
|
+
# Always generate waveform (handle both analog and digital)
|
|
92
|
+
try:
|
|
93
|
+
fig, ax = plt.subplots(figsize=FIGURE_SIZE)
|
|
94
|
+
if isinstance(trace, WaveformTrace):
|
|
95
|
+
plot_waveform(ax, trace)
|
|
96
|
+
else:
|
|
97
|
+
# Digital trace
|
|
98
|
+
plot_timing(ax, trace)
|
|
99
|
+
plots["waveform"] = fig_to_base64(fig)
|
|
100
|
+
if verbose:
|
|
101
|
+
print(" ✓ Generated waveform plot")
|
|
102
|
+
except Exception as e:
|
|
103
|
+
if verbose:
|
|
104
|
+
print(f" ⚠ Waveform plot failed: {e}")
|
|
105
|
+
|
|
106
|
+
if isinstance(trace, WaveformTrace):
|
|
107
|
+
# FFT spectrum
|
|
108
|
+
try:
|
|
109
|
+
fig, ax = plt.subplots(figsize=FIGURE_SIZE)
|
|
110
|
+
plot_fft(ax, trace)
|
|
111
|
+
plots["fft"] = fig_to_base64(fig)
|
|
112
|
+
if verbose:
|
|
113
|
+
print(" ✓ Generated FFT spectrum")
|
|
114
|
+
except Exception as e:
|
|
115
|
+
if verbose:
|
|
116
|
+
print(f" ⚠ FFT plot failed: {e}")
|
|
117
|
+
|
|
118
|
+
# Histogram
|
|
119
|
+
try:
|
|
120
|
+
fig, ax = plt.subplots(figsize=FIGURE_SIZE)
|
|
121
|
+
plot_histogram(ax, trace.data)
|
|
122
|
+
plots["histogram"] = fig_to_base64(fig)
|
|
123
|
+
if verbose:
|
|
124
|
+
print(" ✓ Generated histogram")
|
|
125
|
+
except Exception as e:
|
|
126
|
+
if verbose:
|
|
127
|
+
print(f" ⚠ Histogram plot failed: {e}")
|
|
128
|
+
|
|
129
|
+
# Spectrogram
|
|
130
|
+
try:
|
|
131
|
+
fig, ax = plt.subplots(figsize=FIGURE_SIZE)
|
|
132
|
+
plot_spectrogram(ax, trace)
|
|
133
|
+
plots["spectrogram"] = fig_to_base64(fig)
|
|
134
|
+
if verbose:
|
|
135
|
+
print(" ✓ Generated spectrogram")
|
|
136
|
+
except Exception as e:
|
|
137
|
+
if verbose:
|
|
138
|
+
print(f" ⚠ Spectrogram plot failed: {e}")
|
|
139
|
+
|
|
140
|
+
elif isinstance(trace, DigitalTrace):
|
|
141
|
+
# Timing diagram
|
|
142
|
+
try:
|
|
143
|
+
fig, ax = plt.subplots(figsize=FIGURE_SIZE)
|
|
144
|
+
plot_timing(ax, trace)
|
|
145
|
+
plots["timing"] = fig_to_base64(fig)
|
|
146
|
+
if verbose:
|
|
147
|
+
print(" ✓ Generated timing diagram")
|
|
148
|
+
except Exception as e:
|
|
149
|
+
if verbose:
|
|
150
|
+
print(f" ⚠ Timing plot failed: {e}")
|
|
151
|
+
|
|
152
|
+
return plots
|
|
153
|
+
|
|
154
|
+
|
|
155
|
+
__all__ = [
|
|
156
|
+
"FIGURE_SIZE",
|
|
157
|
+
"PLOT_DPI",
|
|
158
|
+
"fig_to_base64",
|
|
159
|
+
"generate_all_plots",
|
|
160
|
+
]
|