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
@@ -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)