oscura 0.8.0__py3-none-any.whl → 0.11.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 (161) hide show
  1. oscura/__init__.py +19 -19
  2. oscura/__main__.py +4 -0
  3. oscura/analyzers/__init__.py +2 -0
  4. oscura/analyzers/digital/extraction.py +2 -3
  5. oscura/analyzers/digital/quality.py +1 -1
  6. oscura/analyzers/digital/timing.py +1 -1
  7. oscura/analyzers/ml/signal_classifier.py +6 -0
  8. oscura/analyzers/patterns/__init__.py +66 -0
  9. oscura/analyzers/power/basic.py +3 -3
  10. oscura/analyzers/power/soa.py +1 -1
  11. oscura/analyzers/power/switching.py +3 -3
  12. oscura/analyzers/signal_classification.py +529 -0
  13. oscura/analyzers/signal_integrity/sparams.py +3 -3
  14. oscura/analyzers/statistics/basic.py +10 -7
  15. oscura/analyzers/validation.py +1 -1
  16. oscura/analyzers/waveform/measurements.py +200 -156
  17. oscura/analyzers/waveform/measurements_with_uncertainty.py +91 -35
  18. oscura/analyzers/waveform/spectral.py +182 -84
  19. oscura/api/dsl/commands.py +15 -6
  20. oscura/api/server/templates/base.html +137 -146
  21. oscura/api/server/templates/export.html +84 -110
  22. oscura/api/server/templates/home.html +248 -267
  23. oscura/api/server/templates/protocols.html +44 -48
  24. oscura/api/server/templates/reports.html +27 -35
  25. oscura/api/server/templates/session_detail.html +68 -78
  26. oscura/api/server/templates/sessions.html +62 -72
  27. oscura/api/server/templates/waveforms.html +54 -64
  28. oscura/automotive/__init__.py +1 -1
  29. oscura/automotive/can/session.py +1 -1
  30. oscura/automotive/dbc/generator.py +638 -23
  31. oscura/automotive/dtc/data.json +17 -102
  32. oscura/automotive/flexray/fibex.py +9 -1
  33. oscura/automotive/uds/decoder.py +99 -6
  34. oscura/cli/analyze.py +8 -2
  35. oscura/cli/batch.py +36 -5
  36. oscura/cli/characterize.py +18 -4
  37. oscura/cli/export.py +47 -5
  38. oscura/cli/main.py +2 -0
  39. oscura/cli/onboarding/wizard.py +10 -6
  40. oscura/cli/pipeline.py +585 -0
  41. oscura/cli/visualize.py +6 -4
  42. oscura/convenience.py +400 -32
  43. oscura/core/measurement_result.py +286 -0
  44. oscura/core/progress.py +1 -1
  45. oscura/core/schemas/device_mapping.json +2 -8
  46. oscura/core/schemas/packet_format.json +4 -24
  47. oscura/core/schemas/protocol_definition.json +2 -12
  48. oscura/core/types.py +232 -239
  49. oscura/correlation/multi_protocol.py +1 -1
  50. oscura/export/legacy/__init__.py +11 -0
  51. oscura/export/legacy/wav.py +75 -0
  52. oscura/exporters/__init__.py +19 -0
  53. oscura/exporters/wireshark.py +809 -0
  54. oscura/hardware/acquisition/file.py +5 -19
  55. oscura/hardware/acquisition/saleae.py +10 -10
  56. oscura/hardware/acquisition/socketcan.py +4 -6
  57. oscura/hardware/acquisition/synthetic.py +1 -5
  58. oscura/hardware/acquisition/visa.py +6 -6
  59. oscura/hardware/security/side_channel_detector.py +5 -508
  60. oscura/inference/message_format.py +686 -1
  61. oscura/jupyter/display.py +2 -2
  62. oscura/jupyter/magic.py +3 -3
  63. oscura/loaders/__init__.py +17 -12
  64. oscura/loaders/binary.py +1 -1
  65. oscura/loaders/chipwhisperer.py +1 -2
  66. oscura/loaders/configurable.py +1 -1
  67. oscura/loaders/csv_loader.py +2 -2
  68. oscura/loaders/hdf5_loader.py +1 -1
  69. oscura/loaders/lazy.py +6 -1
  70. oscura/loaders/mmap_loader.py +0 -1
  71. oscura/loaders/numpy_loader.py +8 -7
  72. oscura/loaders/preprocessing.py +3 -5
  73. oscura/loaders/rigol.py +21 -7
  74. oscura/loaders/sigrok.py +2 -5
  75. oscura/loaders/tdms.py +3 -2
  76. oscura/loaders/tektronix.py +38 -32
  77. oscura/loaders/tss.py +20 -27
  78. oscura/loaders/validation.py +17 -10
  79. oscura/loaders/vcd.py +13 -8
  80. oscura/loaders/wav.py +1 -6
  81. oscura/pipeline/__init__.py +76 -0
  82. oscura/pipeline/handlers/__init__.py +165 -0
  83. oscura/pipeline/handlers/analyzers.py +1045 -0
  84. oscura/pipeline/handlers/decoders.py +899 -0
  85. oscura/pipeline/handlers/exporters.py +1103 -0
  86. oscura/pipeline/handlers/filters.py +891 -0
  87. oscura/pipeline/handlers/loaders.py +640 -0
  88. oscura/pipeline/handlers/transforms.py +768 -0
  89. oscura/reporting/formatting/measurements.py +55 -14
  90. oscura/reporting/templates/enhanced/protocol_re.html +504 -503
  91. oscura/sessions/legacy.py +49 -1
  92. oscura/side_channel/__init__.py +38 -57
  93. oscura/utils/builders/signal_builder.py +5 -5
  94. oscura/utils/comparison/compare.py +7 -9
  95. oscura/utils/comparison/golden.py +1 -1
  96. oscura/utils/filtering/convenience.py +2 -2
  97. oscura/utils/math/arithmetic.py +38 -62
  98. oscura/utils/math/interpolation.py +20 -20
  99. oscura/utils/pipeline/__init__.py +4 -17
  100. oscura/utils/progressive.py +1 -4
  101. oscura/utils/triggering/edge.py +1 -1
  102. oscura/utils/triggering/pattern.py +2 -2
  103. oscura/utils/triggering/pulse.py +2 -2
  104. oscura/utils/triggering/window.py +3 -3
  105. oscura/validation/hil_testing.py +11 -11
  106. oscura/visualization/__init__.py +46 -284
  107. oscura/visualization/batch.py +72 -433
  108. oscura/visualization/plot.py +542 -53
  109. oscura/visualization/styles.py +184 -318
  110. oscura/workflows/batch/advanced.py +1 -1
  111. oscura/workflows/batch/aggregate.py +12 -9
  112. oscura/workflows/complete_re.py +251 -23
  113. oscura/workflows/digital.py +27 -4
  114. oscura/workflows/multi_trace.py +136 -17
  115. oscura/workflows/waveform.py +11 -6
  116. oscura-0.11.0.dist-info/METADATA +460 -0
  117. {oscura-0.8.0.dist-info → oscura-0.11.0.dist-info}/RECORD +120 -145
  118. oscura/side_channel/dpa.py +0 -1025
  119. oscura/utils/optimization/__init__.py +0 -19
  120. oscura/utils/optimization/parallel.py +0 -443
  121. oscura/utils/optimization/search.py +0 -532
  122. oscura/utils/pipeline/base.py +0 -338
  123. oscura/utils/pipeline/composition.py +0 -248
  124. oscura/utils/pipeline/parallel.py +0 -449
  125. oscura/utils/pipeline/pipeline.py +0 -375
  126. oscura/utils/search/__init__.py +0 -16
  127. oscura/utils/search/anomaly.py +0 -424
  128. oscura/utils/search/context.py +0 -294
  129. oscura/utils/search/pattern.py +0 -288
  130. oscura/utils/storage/__init__.py +0 -61
  131. oscura/utils/storage/database.py +0 -1166
  132. oscura/visualization/accessibility.py +0 -526
  133. oscura/visualization/annotations.py +0 -371
  134. oscura/visualization/axis_scaling.py +0 -305
  135. oscura/visualization/colors.py +0 -451
  136. oscura/visualization/digital.py +0 -436
  137. oscura/visualization/eye.py +0 -571
  138. oscura/visualization/histogram.py +0 -281
  139. oscura/visualization/interactive.py +0 -1035
  140. oscura/visualization/jitter.py +0 -1042
  141. oscura/visualization/keyboard.py +0 -394
  142. oscura/visualization/layout.py +0 -400
  143. oscura/visualization/optimization.py +0 -1079
  144. oscura/visualization/palettes.py +0 -446
  145. oscura/visualization/power.py +0 -508
  146. oscura/visualization/power_extended.py +0 -955
  147. oscura/visualization/presets.py +0 -469
  148. oscura/visualization/protocols.py +0 -1246
  149. oscura/visualization/render.py +0 -223
  150. oscura/visualization/rendering.py +0 -444
  151. oscura/visualization/reverse_engineering.py +0 -838
  152. oscura/visualization/signal_integrity.py +0 -989
  153. oscura/visualization/specialized.py +0 -643
  154. oscura/visualization/spectral.py +0 -1226
  155. oscura/visualization/thumbnails.py +0 -340
  156. oscura/visualization/time_axis.py +0 -351
  157. oscura/visualization/waveform.py +0 -454
  158. oscura-0.8.0.dist-info/METADATA +0 -661
  159. {oscura-0.8.0.dist-info → oscura-0.11.0.dist-info}/WHEEL +0 -0
  160. {oscura-0.8.0.dist-info → oscura-0.11.0.dist-info}/entry_points.txt +0 -0
  161. {oscura-0.8.0.dist-info → oscura-0.11.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
- ]