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
@@ -165,19 +165,12 @@ class FileSource:
165
165
  from oscura.core.types import IQTrace
166
166
 
167
167
  # Get sample count based on trace type
168
- if isinstance(trace, IQTrace):
169
- n_samples = len(trace.i_data)
170
- else:
171
- n_samples = len(trace.data)
168
+ n_samples = len(trace.data)
172
169
 
173
170
  for start in range(0, n_samples, chunk_size):
174
171
  end = min(start + chunk_size, n_samples)
175
172
  # Create chunk trace with sliced data
176
- if isinstance(trace, IQTrace):
177
- # IQTrace doesn't have .data attribute
178
- chunk_data = None # Will be handled separately below
179
- else:
180
- chunk_data = trace.data[start:end]
173
+ chunk_data = trace.data[start:end]
181
174
 
182
175
  # Import here to avoid circular dependency
183
176
  from oscura.core.types import (
@@ -191,11 +184,7 @@ class FileSource:
191
184
  sample_rate=trace.metadata.sample_rate,
192
185
  vertical_scale=trace.metadata.vertical_scale,
193
186
  vertical_offset=trace.metadata.vertical_offset,
194
- acquisition_time=trace.metadata.acquisition_time,
195
- trigger_info=trace.metadata.trigger_info,
196
- source_file=str(self.path),
197
- channel_name=trace.metadata.channel_name,
198
- calibration_info=trace.metadata.calibration_info,
187
+ channel=trace.metadata.channel,
199
188
  )
200
189
 
201
190
  if isinstance(trace, WaveformTrace):
@@ -206,12 +195,9 @@ class FileSource:
206
195
  metadata=chunk_metadata,
207
196
  )
208
197
  elif isinstance(trace, IQTrace):
209
- # Handle I/Q separately
210
- chunk_i = trace.i_data[start:end]
211
- chunk_q = trace.q_data[start:end]
198
+ # Handle I/Q complex data
212
199
  yield IQTrace(
213
- i_data=chunk_i,
214
- q_data=chunk_q,
200
+ data=chunk_data, # type: ignore[arg-type]
215
201
  metadata=chunk_metadata,
216
202
  )
217
203
 
@@ -50,7 +50,7 @@ from typing import TYPE_CHECKING, Any
50
50
  try:
51
51
  import saleae
52
52
  except ImportError:
53
- saleae = None # type: ignore[assignment]
53
+ saleae = None
54
54
 
55
55
  if TYPE_CHECKING:
56
56
  from oscura.core.types import Trace
@@ -206,7 +206,7 @@ class SaleaeSource:
206
206
 
207
207
  from oscura.core.types import DigitalTrace, TraceMetadata, WaveformTrace
208
208
 
209
- acquisition_start = datetime.now()
209
+ acquisition_time = datetime.now()
210
210
 
211
211
  # Start capture
212
212
  self.saleae.capture_start()
@@ -223,11 +223,15 @@ class SaleaeSource:
223
223
 
224
224
  n_samples = int(self.sample_rate * self.duration)
225
225
 
226
+ # Format channel list for display
227
+ channel_list = self.digital_channels or self.analog_channels
228
+ source_file = f"saleae://{self.device_id}" if self.device_id else "saleae://unknown"
229
+
226
230
  metadata = TraceMetadata(
227
231
  sample_rate=self.sample_rate,
228
- acquisition_time=acquisition_start,
229
- source_file=f"saleae://{self.device_id or 'auto'}",
230
- channel_name=f"Saleae Ch{self.digital_channels or self.analog_channels}",
232
+ channel=f"Saleae Ch{channel_list}",
233
+ source_file=source_file,
234
+ acquisition_time=acquisition_time,
231
235
  )
232
236
 
233
237
  if self.digital_channels:
@@ -289,11 +293,7 @@ class SaleaeSource:
289
293
  sample_rate=full_trace.metadata.sample_rate,
290
294
  vertical_scale=full_trace.metadata.vertical_scale,
291
295
  vertical_offset=full_trace.metadata.vertical_offset,
292
- acquisition_time=full_trace.metadata.acquisition_time,
293
- trigger_info=full_trace.metadata.trigger_info,
294
- source_file=full_trace.metadata.source_file,
295
- channel_name=full_trace.metadata.channel_name,
296
- calibration_info=full_trace.metadata.calibration_info,
296
+ channel=full_trace.metadata.channel,
297
297
  )
298
298
 
299
299
  if isinstance(full_trace, DigitalTrace):
@@ -172,8 +172,7 @@ class SocketCANSource:
172
172
  metadata = TraceMetadata(
173
173
  sample_rate=1.0, # Placeholder
174
174
  acquisition_time=acquisition_start,
175
- source_file=f"socketcan://{self.interface}",
176
- channel_name=f"CAN {self.interface}",
175
+ channel=f"CAN {self.interface}",
177
176
  )
178
177
  return DigitalTrace(data=np.array([], dtype=np.uint8), metadata=metadata)
179
178
 
@@ -189,7 +188,7 @@ class SocketCANSource:
189
188
  sample_rate=effective_rate,
190
189
  acquisition_time=acquisition_start,
191
190
  source_file=f"socketcan://{self.interface}",
192
- channel_name=f"CAN {self.interface}",
191
+ channel=f"CAN {self.interface}",
193
192
  )
194
193
 
195
194
  # Store CAN IDs as digital data
@@ -249,7 +248,7 @@ class SocketCANSource:
249
248
  sample_rate=effective_rate,
250
249
  acquisition_time=acquisition_start,
251
250
  source_file=f"socketcan://{self.interface}",
252
- channel_name=f"CAN {self.interface}",
251
+ channel=f"CAN {self.interface}",
253
252
  )
254
253
 
255
254
  data_bytes = can_ids.view(np.uint8)
@@ -268,8 +267,7 @@ class SocketCANSource:
268
267
  metadata = TraceMetadata(
269
268
  sample_rate=effective_rate,
270
269
  acquisition_time=acquisition_start,
271
- source_file=f"socketcan://{self.interface}",
272
- channel_name=f"CAN {self.interface}",
270
+ channel=f"CAN {self.interface}",
273
271
  )
274
272
 
275
273
  data_bytes = can_ids.view(np.uint8)
@@ -176,11 +176,7 @@ class SyntheticSource:
176
176
  sample_rate=trace.metadata.sample_rate,
177
177
  vertical_scale=trace.metadata.vertical_scale,
178
178
  vertical_offset=trace.metadata.vertical_offset,
179
- acquisition_time=trace.metadata.acquisition_time,
180
- trigger_info=trace.metadata.trigger_info,
181
- source_file=trace.metadata.source_file,
182
- channel_name=trace.metadata.channel_name,
183
- calibration_info=trace.metadata.calibration_info,
179
+ channel=trace.metadata.channel,
184
180
  )
185
181
 
186
182
  if isinstance(trace, DigitalTrace):
@@ -256,7 +256,7 @@ class VISASource:
256
256
  if channel is None:
257
257
  channel = self.channels[0]
258
258
 
259
- acquisition_start = datetime.now()
259
+ acquisition_time = datetime.now()
260
260
 
261
261
  try:
262
262
  # Single acquisition
@@ -296,7 +296,7 @@ class VISASource:
296
296
  except Exception:
297
297
  idn = "Unknown Instrument"
298
298
 
299
- calibration_info = CalibrationInfo(
299
+ calibration = CalibrationInfo(
300
300
  instrument=idn,
301
301
  coupling="DC", # Default
302
302
  vertical_resolution=8, # Typical for oscilloscopes
@@ -305,10 +305,10 @@ class VISASource:
305
305
  metadata = TraceMetadata(
306
306
  sample_rate=sample_rate,
307
307
  vertical_scale=self.vertical_scale,
308
- acquisition_time=acquisition_start,
309
- source_file=f"visa://{self.resource}",
310
- channel_name=f"CH{channel}",
311
- calibration_info=calibration_info,
308
+ channel=f"CH{channel}",
309
+ source_file=f"visa://{self.resource}" if self.resource else "visa://unknown",
310
+ acquisition_time=acquisition_time,
311
+ calibration=calibration,
312
312
  )
313
313
 
314
314
  return WaveformTrace(data=data, metadata=metadata)