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