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.
Files changed (175) hide show
  1. oscura/__init__.py +19 -19
  2. oscura/analyzers/__init__.py +2 -0
  3. oscura/analyzers/digital/extraction.py +2 -3
  4. oscura/analyzers/digital/quality.py +1 -1
  5. oscura/analyzers/digital/timing.py +1 -1
  6. oscura/analyzers/eye/__init__.py +5 -1
  7. oscura/analyzers/eye/generation.py +501 -0
  8. oscura/analyzers/jitter/__init__.py +6 -6
  9. oscura/analyzers/jitter/timing.py +419 -0
  10. oscura/analyzers/patterns/__init__.py +94 -0
  11. oscura/analyzers/patterns/reverse_engineering.py +991 -0
  12. oscura/analyzers/power/__init__.py +35 -12
  13. oscura/analyzers/power/basic.py +3 -3
  14. oscura/analyzers/power/soa.py +1 -1
  15. oscura/analyzers/power/switching.py +3 -3
  16. oscura/analyzers/signal_classification.py +529 -0
  17. oscura/analyzers/signal_integrity/sparams.py +3 -3
  18. oscura/analyzers/statistics/__init__.py +4 -0
  19. oscura/analyzers/statistics/basic.py +152 -0
  20. oscura/analyzers/statistics/correlation.py +47 -6
  21. oscura/analyzers/validation.py +1 -1
  22. oscura/analyzers/waveform/__init__.py +2 -0
  23. oscura/analyzers/waveform/measurements.py +329 -163
  24. oscura/analyzers/waveform/measurements_with_uncertainty.py +91 -35
  25. oscura/analyzers/waveform/spectral.py +498 -54
  26. oscura/api/dsl/commands.py +15 -6
  27. oscura/api/server/templates/base.html +137 -146
  28. oscura/api/server/templates/export.html +84 -110
  29. oscura/api/server/templates/home.html +248 -267
  30. oscura/api/server/templates/protocols.html +44 -48
  31. oscura/api/server/templates/reports.html +27 -35
  32. oscura/api/server/templates/session_detail.html +68 -78
  33. oscura/api/server/templates/sessions.html +62 -72
  34. oscura/api/server/templates/waveforms.html +54 -64
  35. oscura/automotive/__init__.py +1 -1
  36. oscura/automotive/can/session.py +1 -1
  37. oscura/automotive/dbc/generator.py +638 -23
  38. oscura/automotive/dtc/data.json +102 -17
  39. oscura/automotive/uds/decoder.py +99 -6
  40. oscura/cli/analyze.py +8 -2
  41. oscura/cli/batch.py +36 -5
  42. oscura/cli/characterize.py +18 -4
  43. oscura/cli/export.py +47 -5
  44. oscura/cli/main.py +2 -0
  45. oscura/cli/onboarding/wizard.py +10 -6
  46. oscura/cli/pipeline.py +585 -0
  47. oscura/cli/visualize.py +6 -4
  48. oscura/convenience.py +400 -32
  49. oscura/core/config/loader.py +0 -1
  50. oscura/core/measurement_result.py +286 -0
  51. oscura/core/progress.py +1 -1
  52. oscura/core/schemas/device_mapping.json +8 -2
  53. oscura/core/schemas/packet_format.json +24 -4
  54. oscura/core/schemas/protocol_definition.json +12 -2
  55. oscura/core/types.py +300 -199
  56. oscura/correlation/multi_protocol.py +1 -1
  57. oscura/export/legacy/__init__.py +11 -0
  58. oscura/export/legacy/wav.py +75 -0
  59. oscura/exporters/__init__.py +19 -0
  60. oscura/exporters/wireshark.py +809 -0
  61. oscura/hardware/acquisition/file.py +5 -19
  62. oscura/hardware/acquisition/saleae.py +10 -10
  63. oscura/hardware/acquisition/socketcan.py +4 -6
  64. oscura/hardware/acquisition/synthetic.py +1 -5
  65. oscura/hardware/acquisition/visa.py +6 -6
  66. oscura/hardware/security/side_channel_detector.py +5 -508
  67. oscura/inference/message_format.py +686 -1
  68. oscura/jupyter/display.py +2 -2
  69. oscura/jupyter/magic.py +3 -3
  70. oscura/loaders/__init__.py +17 -12
  71. oscura/loaders/binary.py +1 -1
  72. oscura/loaders/chipwhisperer.py +1 -2
  73. oscura/loaders/configurable.py +1 -1
  74. oscura/loaders/csv_loader.py +2 -2
  75. oscura/loaders/hdf5_loader.py +1 -1
  76. oscura/loaders/lazy.py +6 -1
  77. oscura/loaders/mmap_loader.py +0 -1
  78. oscura/loaders/numpy_loader.py +8 -7
  79. oscura/loaders/preprocessing.py +3 -5
  80. oscura/loaders/rigol.py +21 -7
  81. oscura/loaders/sigrok.py +2 -5
  82. oscura/loaders/tdms.py +3 -2
  83. oscura/loaders/tektronix.py +38 -32
  84. oscura/loaders/tss.py +20 -27
  85. oscura/loaders/vcd.py +13 -8
  86. oscura/loaders/wav.py +1 -6
  87. oscura/pipeline/__init__.py +76 -0
  88. oscura/pipeline/handlers/__init__.py +165 -0
  89. oscura/pipeline/handlers/analyzers.py +1045 -0
  90. oscura/pipeline/handlers/decoders.py +899 -0
  91. oscura/pipeline/handlers/exporters.py +1103 -0
  92. oscura/pipeline/handlers/filters.py +891 -0
  93. oscura/pipeline/handlers/loaders.py +640 -0
  94. oscura/pipeline/handlers/transforms.py +768 -0
  95. oscura/reporting/__init__.py +88 -1
  96. oscura/reporting/automation.py +348 -0
  97. oscura/reporting/citations.py +374 -0
  98. oscura/reporting/core.py +54 -0
  99. oscura/reporting/formatting/__init__.py +11 -0
  100. oscura/reporting/formatting/measurements.py +320 -0
  101. oscura/reporting/html.py +57 -0
  102. oscura/reporting/interpretation.py +431 -0
  103. oscura/reporting/summary.py +329 -0
  104. oscura/reporting/templates/enhanced/protocol_re.html +504 -503
  105. oscura/reporting/visualization.py +542 -0
  106. oscura/side_channel/__init__.py +38 -57
  107. oscura/utils/builders/signal_builder.py +5 -5
  108. oscura/utils/comparison/compare.py +7 -9
  109. oscura/utils/comparison/golden.py +1 -1
  110. oscura/utils/filtering/convenience.py +2 -2
  111. oscura/utils/math/arithmetic.py +38 -62
  112. oscura/utils/math/interpolation.py +20 -20
  113. oscura/utils/pipeline/__init__.py +4 -17
  114. oscura/utils/progressive.py +1 -4
  115. oscura/utils/triggering/edge.py +1 -1
  116. oscura/utils/triggering/pattern.py +2 -2
  117. oscura/utils/triggering/pulse.py +2 -2
  118. oscura/utils/triggering/window.py +3 -3
  119. oscura/validation/hil_testing.py +11 -11
  120. oscura/visualization/__init__.py +47 -284
  121. oscura/visualization/batch.py +160 -0
  122. oscura/visualization/plot.py +542 -53
  123. oscura/visualization/styles.py +184 -318
  124. oscura/workflows/__init__.py +2 -0
  125. oscura/workflows/batch/advanced.py +1 -1
  126. oscura/workflows/batch/aggregate.py +7 -8
  127. oscura/workflows/complete_re.py +251 -23
  128. oscura/workflows/digital.py +27 -4
  129. oscura/workflows/multi_trace.py +136 -17
  130. oscura/workflows/waveform.py +788 -0
  131. {oscura-0.7.0.dist-info → oscura-0.10.0.dist-info}/METADATA +59 -79
  132. {oscura-0.7.0.dist-info → oscura-0.10.0.dist-info}/RECORD +135 -149
  133. oscura/side_channel/dpa.py +0 -1025
  134. oscura/utils/optimization/__init__.py +0 -19
  135. oscura/utils/optimization/parallel.py +0 -443
  136. oscura/utils/optimization/search.py +0 -532
  137. oscura/utils/pipeline/base.py +0 -338
  138. oscura/utils/pipeline/composition.py +0 -248
  139. oscura/utils/pipeline/parallel.py +0 -449
  140. oscura/utils/pipeline/pipeline.py +0 -375
  141. oscura/utils/search/__init__.py +0 -16
  142. oscura/utils/search/anomaly.py +0 -424
  143. oscura/utils/search/context.py +0 -294
  144. oscura/utils/search/pattern.py +0 -288
  145. oscura/utils/storage/__init__.py +0 -61
  146. oscura/utils/storage/database.py +0 -1166
  147. oscura/visualization/accessibility.py +0 -526
  148. oscura/visualization/annotations.py +0 -371
  149. oscura/visualization/axis_scaling.py +0 -305
  150. oscura/visualization/colors.py +0 -451
  151. oscura/visualization/digital.py +0 -436
  152. oscura/visualization/eye.py +0 -571
  153. oscura/visualization/histogram.py +0 -281
  154. oscura/visualization/interactive.py +0 -1035
  155. oscura/visualization/jitter.py +0 -1042
  156. oscura/visualization/keyboard.py +0 -394
  157. oscura/visualization/layout.py +0 -400
  158. oscura/visualization/optimization.py +0 -1079
  159. oscura/visualization/palettes.py +0 -446
  160. oscura/visualization/power.py +0 -508
  161. oscura/visualization/power_extended.py +0 -955
  162. oscura/visualization/presets.py +0 -469
  163. oscura/visualization/protocols.py +0 -1246
  164. oscura/visualization/render.py +0 -223
  165. oscura/visualization/rendering.py +0 -444
  166. oscura/visualization/reverse_engineering.py +0 -838
  167. oscura/visualization/signal_integrity.py +0 -989
  168. oscura/visualization/specialized.py +0 -643
  169. oscura/visualization/spectral.py +0 -1226
  170. oscura/visualization/thumbnails.py +0 -340
  171. oscura/visualization/time_axis.py +0 -351
  172. oscura/visualization/waveform.py +0 -454
  173. {oscura-0.7.0.dist-info → oscura-0.10.0.dist-info}/WHEEL +0 -0
  174. {oscura-0.7.0.dist-info → oscura-0.10.0.dist-info}/entry_points.txt +0 -0
  175. {oscura-0.7.0.dist-info → oscura-0.10.0.dist-info}/licenses/LICENSE +0 -0
@@ -1,281 +0,0 @@
1
- """Histogram utilities with automatic bin optimization.
2
-
3
- This module provides intelligent histogram bin calculation using
4
- established statistical rules.
5
-
6
-
7
- Example:
8
- >>> from oscura.visualization.histogram import calculate_optimal_bins
9
- >>> bins = calculate_optimal_bins(data, method="freedman-diaconis")
10
-
11
- References:
12
- Sturges' rule (1926)
13
- Freedman-Diaconis rule (1981)
14
- Scott's rule (1979)
15
- """
16
-
17
- from __future__ import annotations
18
-
19
- from typing import TYPE_CHECKING, Literal
20
-
21
- import numpy as np
22
-
23
- if TYPE_CHECKING:
24
- from numpy.typing import NDArray
25
-
26
-
27
- def calculate_optimal_bins(
28
- data: NDArray[np.float64],
29
- *,
30
- method: Literal["auto", "sturges", "freedman-diaconis", "scott"] = "auto",
31
- min_bins: int = 5,
32
- max_bins: int = 200,
33
- ) -> int:
34
- """Calculate optimal histogram bin count using statistical rules.
35
-
36
- : Automatically calculate optimal histogram bin count
37
- using Sturges, Freedman-Diaconis, or Scott's rule.
38
-
39
- Args:
40
- data: Input data array
41
- method: Binning method to use
42
- - "auto": Auto-select based on data characteristics
43
- - "sturges": Sturges' rule (good for normal distributions)
44
- - "freedman-diaconis": Freedman-Diaconis rule (robust to outliers)
45
- - "scott": Scott's rule (good for smooth distributions)
46
- min_bins: Minimum number of bins (default: 5)
47
- max_bins: Maximum number of bins (default: 200)
48
-
49
- Returns:
50
- Optimal number of bins (clamped to [min_bins, max_bins])
51
-
52
- Raises:
53
- ValueError: If data is empty or invalid
54
-
55
- Example:
56
- >>> data = np.random.randn(1000)
57
- >>> bins = calculate_optimal_bins(data, method="freedman-diaconis")
58
- >>> hist, edges = np.histogram(data, bins=bins)
59
-
60
- >>> # Auto-select method
61
- >>> bins = calculate_optimal_bins(data, method="auto")
62
-
63
- References:
64
- VIS-025: Histogram Bin Optimization
65
- Sturges (1926): k = ceil(log2(n) + 1)
66
- Freedman-Diaconis (1981): h = 2 * IQR * n^(-1/3)
67
- Scott (1979): h = 3.5 * std * n^(-1/3)
68
- """
69
- if len(data) == 0:
70
- raise ValueError("Data array cannot be empty")
71
- if min_bins < 1:
72
- raise ValueError("min_bins must be >= 1")
73
- if max_bins < min_bins:
74
- raise ValueError("max_bins must be >= min_bins")
75
-
76
- # Remove NaN values
77
- clean_data = data[~np.isnan(data)]
78
-
79
- if len(clean_data) < 2:
80
- return min_bins
81
-
82
- # Auto-select method based on data characteristics
83
- if method == "auto":
84
- method = _auto_select_method(clean_data)
85
-
86
- # Calculate bins using selected method
87
- if method == "sturges":
88
- bins = _sturges_bins(clean_data)
89
- elif method == "freedman-diaconis":
90
- bins = _freedman_diaconis_bins(clean_data)
91
- elif method == "scott":
92
- bins = _scott_bins(clean_data)
93
- else:
94
- raise ValueError(f"Unknown method: {method}")
95
-
96
- # Clamp to valid range
97
- bins = max(min_bins, min(max_bins, bins))
98
-
99
- return bins
100
-
101
-
102
- def calculate_bin_edges(
103
- data: NDArray[np.float64],
104
- n_bins: int,
105
- ) -> NDArray[np.float64]:
106
- """Calculate histogram bin edges for given bin count.
107
-
108
- Args:
109
- data: Input data array
110
- n_bins: Number of bins
111
-
112
- Returns:
113
- Array of bin edges (length n_bins + 1)
114
-
115
- Raises:
116
- ValueError: If data is empty or n_bins < 1.
117
-
118
- Example:
119
- >>> data = np.random.randn(1000)
120
- >>> n_bins = calculate_optimal_bins(data)
121
- >>> edges = calculate_bin_edges(data, n_bins)
122
- """
123
- if len(data) == 0:
124
- raise ValueError("Data array cannot be empty")
125
- if n_bins < 1:
126
- raise ValueError("n_bins must be >= 1")
127
-
128
- # Remove NaN values
129
- clean_data = data[~np.isnan(data)]
130
-
131
- if len(clean_data) == 0:
132
- return np.array([0.0, 1.0])
133
-
134
- # Calculate edges
135
- data_min = np.min(clean_data)
136
- data_max = np.max(clean_data)
137
-
138
- # Handle single-value data
139
- if data_min == data_max:
140
- return np.array([data_min - 0.5, data_max + 0.5])
141
-
142
- edges: NDArray[np.float64] = np.linspace(data_min, data_max, n_bins + 1)
143
- return edges
144
-
145
-
146
- def _sturges_bins(data: NDArray[np.float64]) -> int:
147
- """Calculate bins using Sturges' rule.
148
-
149
- Sturges' rule: k = ceil(log2(n) + 1)
150
-
151
- Good for: Normal distributions, small to moderate sample sizes
152
-
153
- Args:
154
- data: Input data
155
-
156
- Returns:
157
- Number of bins
158
- """
159
- n = len(data)
160
- bins = int(np.ceil(np.log2(n) + 1))
161
- return bins
162
-
163
-
164
- def _freedman_diaconis_bins(data: NDArray[np.float64]) -> int:
165
- """Calculate bins using Freedman-Diaconis rule.
166
-
167
- Freedman-Diaconis rule: h = 2 * IQR(x) / n^(1/3)
168
- where h is bin width and IQR is interquartile range
169
-
170
- Good for: Robust estimation, data with outliers
171
-
172
- Args:
173
- data: Input data
174
-
175
- Returns:
176
- Number of bins
177
- """
178
- n = len(data)
179
-
180
- # Calculate IQR
181
- q75, q25 = np.percentile(data, [75, 25])
182
- iqr = q75 - q25
183
-
184
- if iqr == 0:
185
- # Fall back to Sturges if IQR is zero
186
- return _sturges_bins(data)
187
-
188
- # Calculate bin width
189
- bin_width = 2.0 * iqr / (n ** (1.0 / 3.0))
190
-
191
- # Calculate number of bins
192
- data_range = np.ptp(data) # peak-to-peak (max - min)
193
-
194
- if bin_width == 0:
195
- return _sturges_bins(data)
196
-
197
- bins = int(np.ceil(data_range / bin_width))
198
-
199
- return max(1, bins)
200
-
201
-
202
- def _scott_bins(data: NDArray[np.float64]) -> int:
203
- """Calculate bins using Scott's rule.
204
-
205
- Scott's rule: h = 3.5 * std(x) / n^(1/3)
206
- where h is bin width
207
-
208
- Good for: Smooth distributions, normally distributed data
209
-
210
- Args:
211
- data: Input data
212
-
213
- Returns:
214
- Number of bins
215
- """
216
- n = len(data)
217
-
218
- # Calculate standard deviation
219
- std = np.std(data)
220
-
221
- if std == 0:
222
- # Fall back to Sturges if std is zero
223
- return _sturges_bins(data)
224
-
225
- # Calculate bin width
226
- bin_width = 3.5 * std / (n ** (1.0 / 3.0))
227
-
228
- # Calculate number of bins
229
- data_range = np.ptp(data)
230
-
231
- if bin_width == 0:
232
- return _sturges_bins(data)
233
-
234
- bins = int(np.ceil(data_range / bin_width))
235
-
236
- return max(1, bins)
237
-
238
-
239
- def _auto_select_method(
240
- data: NDArray[np.float64],
241
- ) -> Literal["sturges", "freedman-diaconis", "scott"]:
242
- """Auto-select binning method based on data characteristics.
243
-
244
- Selection criteria:
245
- - Use Sturges for small samples (n < 100)
246
- - Use Freedman-Diaconis for data with outliers (high skewness)
247
- - Use Scott for smooth, normal-like distributions
248
-
249
- Args:
250
- data: Input data
251
-
252
- Returns:
253
- Selected method name
254
- """
255
- n = len(data)
256
-
257
- # Small samples: use Sturges
258
- if n < 100:
259
- return "sturges"
260
-
261
- # Calculate skewness to detect outliers
262
- mean = np.mean(data)
263
- std = np.std(data)
264
-
265
- if std == 0:
266
- return "sturges"
267
-
268
- skewness = np.mean(((data - mean) / std) ** 3)
269
-
270
- # High skewness indicates outliers: use Freedman-Diaconis (robust)
271
- if abs(skewness) > 0.5:
272
- return "freedman-diaconis"
273
-
274
- # Normal-like distribution: use Scott
275
- return "scott"
276
-
277
-
278
- __all__ = [
279
- "calculate_bin_edges",
280
- "calculate_optimal_bins",
281
- ]