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
@@ -59,7 +59,7 @@ def difference(
59
59
  trace2: WaveformTrace,
60
60
  *,
61
61
  normalize: bool = False,
62
- channel_name: str | None = None,
62
+ channel: str | None = None,
63
63
  ) -> WaveformTrace:
64
64
  """Compute difference between two traces.
65
65
 
@@ -70,7 +70,7 @@ def difference(
70
70
  trace1: First trace.
71
71
  trace2: Second trace.
72
72
  normalize: Normalize difference to percentage of reference range.
73
- channel_name: Name for the result trace.
73
+ channel: Name for the result trace.
74
74
 
75
75
  Returns:
76
76
  WaveformTrace containing the difference.
@@ -106,12 +106,10 @@ def difference(
106
106
 
107
107
  new_metadata = TraceMetadata(
108
108
  sample_rate=trace1.metadata.sample_rate,
109
- vertical_scale=None,
110
- vertical_offset=None,
111
- acquisition_time=trace1.metadata.acquisition_time,
112
- trigger_info=trace1.metadata.trigger_info,
113
- source_file=trace1.metadata.source_file,
114
- channel_name=channel_name or "difference",
109
+ vertical_scale=trace1.metadata.vertical_scale,
110
+ vertical_offset=trace1.metadata.vertical_offset,
111
+ channel=channel or "difference",
112
+ units=trace1.metadata.units,
115
113
  )
116
114
 
117
115
  return WaveformTrace(data=diff, metadata=new_metadata)
@@ -482,7 +480,7 @@ def compare_traces(
482
480
  match = _determine_match(method, max_diff, tolerance, tolerance_pct, data1, data2)
483
481
 
484
482
  diff_trace = (
485
- difference(trace1, trace2, channel_name="comparison_diff") if include_difference else None
483
+ difference(trace1, trace2, channel="comparison_diff") if include_difference else None
486
484
  )
487
485
  statistics = _compute_comparison_statistics(diff, violations, min_len, data1, data2)
488
486
 
@@ -235,7 +235,7 @@ def create_golden(
235
235
  description=description,
236
236
  metadata={
237
237
  "source_file": trace.metadata.source_file,
238
- "channel_name": trace.metadata.channel_name,
238
+ "channel": trace.metadata.channel,
239
239
  },
240
240
  )
241
241
 
@@ -496,7 +496,7 @@ def differentiate(
496
496
  if order < 1:
497
497
  raise AnalysisError(f"Derivative order must be positive, got {order}")
498
498
 
499
- sample_period = trace.metadata.time_base
499
+ sample_period = 1.0 / trace.metadata.sample_rate
500
500
  result = trace.data.copy()
501
501
 
502
502
  for _ in range(order):
@@ -530,7 +530,7 @@ def integrate(
530
530
  Example:
531
531
  >>> position = integrate(velocity_trace)
532
532
  """
533
- sample_period = trace.metadata.time_base
533
+ sample_period = 1.0 / trace.metadata.sample_rate
534
534
 
535
535
  if method == "cumtrapz":
536
536
  from scipy.integrate import cumulative_trapezoid
@@ -73,7 +73,7 @@ def add(
73
73
  trace1: WaveformTrace,
74
74
  trace2: TraceOrScalar,
75
75
  *,
76
- channel_name: str | None = None,
76
+ channel: str | None = None,
77
77
  ) -> WaveformTrace:
78
78
  """Add two traces or add a scalar to a trace.
79
79
 
@@ -83,7 +83,7 @@ def add(
83
83
  Args:
84
84
  trace1: First trace (base trace).
85
85
  trace2: Second trace or scalar value to add.
86
- channel_name: Name for the result trace (optional).
86
+ channel: Name for the result trace (optional).
87
87
 
88
88
  Returns:
89
89
  New WaveformTrace containing the sum.
@@ -121,10 +121,7 @@ def add(
121
121
  sample_rate=metadata.sample_rate,
122
122
  vertical_scale=metadata.vertical_scale,
123
123
  vertical_offset=metadata.vertical_offset,
124
- acquisition_time=metadata.acquisition_time,
125
- trigger_info=metadata.trigger_info,
126
- source_file=metadata.source_file,
127
- channel_name=channel_name or f"{metadata.channel_name or 'trace'}_sum",
124
+ channel=channel or f"{metadata.channel or 'trace'}_sum",
128
125
  )
129
126
 
130
127
  return WaveformTrace(data=result_data, metadata=new_metadata)
@@ -134,7 +131,7 @@ def subtract(
134
131
  trace1: WaveformTrace,
135
132
  trace2: TraceOrScalar,
136
133
  *,
137
- channel_name: str | None = None,
134
+ channel: str | None = None,
138
135
  ) -> WaveformTrace:
139
136
  """Subtract second trace from first trace or subtract a scalar.
140
137
 
@@ -144,7 +141,7 @@ def subtract(
144
141
  Args:
145
142
  trace1: Trace to subtract from.
146
143
  trace2: Trace or scalar to subtract.
147
- channel_name: Name for the result trace (optional).
144
+ channel: Name for the result trace (optional).
148
145
 
149
146
  Returns:
150
147
  New WaveformTrace containing the difference.
@@ -178,10 +175,7 @@ def subtract(
178
175
  sample_rate=metadata.sample_rate,
179
176
  vertical_scale=metadata.vertical_scale,
180
177
  vertical_offset=metadata.vertical_offset,
181
- acquisition_time=metadata.acquisition_time,
182
- trigger_info=metadata.trigger_info,
183
- source_file=metadata.source_file,
184
- channel_name=channel_name or f"{metadata.channel_name or 'trace'}_diff",
178
+ channel=channel or f"{metadata.channel or 'trace'}_diff",
185
179
  )
186
180
 
187
181
  return WaveformTrace(data=result_data, metadata=new_metadata)
@@ -191,7 +185,7 @@ def multiply(
191
185
  trace1: WaveformTrace,
192
186
  trace2: TraceOrScalar,
193
187
  *,
194
- channel_name: str | None = None,
188
+ channel: str | None = None,
195
189
  ) -> WaveformTrace:
196
190
  """Multiply two traces or multiply trace by a scalar.
197
191
 
@@ -201,7 +195,7 @@ def multiply(
201
195
  Args:
202
196
  trace1: First trace.
203
197
  trace2: Second trace or scalar multiplier.
204
- channel_name: Name for the result trace (optional).
198
+ channel: Name for the result trace (optional).
205
199
 
206
200
  Returns:
207
201
  New WaveformTrace containing the product.
@@ -235,10 +229,7 @@ def multiply(
235
229
  sample_rate=metadata.sample_rate,
236
230
  vertical_scale=metadata.vertical_scale,
237
231
  vertical_offset=metadata.vertical_offset,
238
- acquisition_time=metadata.acquisition_time,
239
- trigger_info=metadata.trigger_info,
240
- source_file=metadata.source_file,
241
- channel_name=channel_name or f"{metadata.channel_name or 'trace'}_mult",
232
+ channel=channel or f"{metadata.channel or 'trace'}_mult",
242
233
  )
243
234
 
244
235
  return WaveformTrace(data=result_data, metadata=new_metadata)
@@ -248,7 +239,7 @@ def divide(
248
239
  trace1: WaveformTrace,
249
240
  trace2: TraceOrScalar,
250
241
  *,
251
- channel_name: str | None = None,
242
+ channel: str | None = None,
252
243
  fill_value: float = np.nan,
253
244
  ) -> WaveformTrace:
254
245
  """Divide first trace by second trace or by a scalar.
@@ -259,7 +250,7 @@ def divide(
259
250
  Args:
260
251
  trace1: Numerator trace.
261
252
  trace2: Denominator trace or scalar.
262
- channel_name: Name for the result trace (optional).
253
+ channel: Name for the result trace (optional).
263
254
  fill_value: Value to use for division by zero (default NaN).
264
255
 
265
256
  Returns:
@@ -301,10 +292,7 @@ def divide(
301
292
  sample_rate=metadata.sample_rate,
302
293
  vertical_scale=metadata.vertical_scale,
303
294
  vertical_offset=metadata.vertical_offset,
304
- acquisition_time=metadata.acquisition_time,
305
- trigger_info=metadata.trigger_info,
306
- source_file=metadata.source_file,
307
- channel_name=channel_name or f"{metadata.channel_name or 'trace'}_div",
295
+ channel=channel or f"{metadata.channel or 'trace'}_div",
308
296
  )
309
297
 
310
298
  return WaveformTrace(data=result_data, metadata=new_metadata)
@@ -314,7 +302,7 @@ def scale(
314
302
  trace: WaveformTrace,
315
303
  factor: float,
316
304
  *,
317
- channel_name: str | None = None,
305
+ channel: str | None = None,
318
306
  ) -> WaveformTrace:
319
307
  """Scale trace by a constant factor.
320
308
 
@@ -324,7 +312,7 @@ def scale(
324
312
  Args:
325
313
  trace: Input trace.
326
314
  factor: Scale factor to apply.
327
- channel_name: Name for the result trace (optional).
315
+ channel: Name for the result trace (optional).
328
316
 
329
317
  Returns:
330
318
  Scaled WaveformTrace.
@@ -336,7 +324,7 @@ def scale(
336
324
  return multiply(
337
325
  trace,
338
326
  factor,
339
- channel_name=channel_name or f"{trace.metadata.channel_name or 'trace'}_scaled",
327
+ channel=channel or f"{trace.metadata.channel or 'trace'}_scaled",
340
328
  )
341
329
 
342
330
 
@@ -344,7 +332,7 @@ def offset(
344
332
  trace: WaveformTrace,
345
333
  value: float,
346
334
  *,
347
- channel_name: str | None = None,
335
+ channel: str | None = None,
348
336
  ) -> WaveformTrace:
349
337
  """Add a constant offset to trace.
350
338
 
@@ -353,7 +341,7 @@ def offset(
353
341
  Args:
354
342
  trace: Input trace.
355
343
  value: Offset value to add.
356
- channel_name: Name for the result trace (optional).
344
+ channel: Name for the result trace (optional).
357
345
 
358
346
  Returns:
359
347
  Offset WaveformTrace.
@@ -364,14 +352,14 @@ def offset(
364
352
  return add(
365
353
  trace,
366
354
  value,
367
- channel_name=channel_name or f"{trace.metadata.channel_name or 'trace'}_offset",
355
+ channel=channel or f"{trace.metadata.channel or 'trace'}_offset",
368
356
  )
369
357
 
370
358
 
371
359
  def invert(
372
360
  trace: WaveformTrace,
373
361
  *,
374
- channel_name: str | None = None,
362
+ channel: str | None = None,
375
363
  ) -> WaveformTrace:
376
364
  """Invert trace polarity (multiply by -1).
377
365
 
@@ -379,7 +367,7 @@ def invert(
379
367
 
380
368
  Args:
381
369
  trace: Input trace.
382
- channel_name: Name for the result trace (optional).
370
+ channel: Name for the result trace (optional).
383
371
 
384
372
  Returns:
385
373
  Inverted WaveformTrace.
@@ -390,14 +378,14 @@ def invert(
390
378
  return scale(
391
379
  trace,
392
380
  -1.0,
393
- channel_name=channel_name or f"{trace.metadata.channel_name or 'trace'}_inverted",
381
+ channel=channel or f"{trace.metadata.channel or 'trace'}_inverted",
394
382
  )
395
383
 
396
384
 
397
385
  def absolute(
398
386
  trace: WaveformTrace,
399
387
  *,
400
- channel_name: str | None = None,
388
+ channel: str | None = None,
401
389
  ) -> WaveformTrace:
402
390
  """Compute absolute value of trace.
403
391
 
@@ -405,7 +393,7 @@ def absolute(
405
393
 
406
394
  Args:
407
395
  trace: Input trace.
408
- channel_name: Name for the result trace (optional).
396
+ channel: Name for the result trace (optional).
409
397
 
410
398
  Returns:
411
399
  WaveformTrace with absolute values.
@@ -419,10 +407,7 @@ def absolute(
419
407
  sample_rate=trace.metadata.sample_rate,
420
408
  vertical_scale=trace.metadata.vertical_scale,
421
409
  vertical_offset=trace.metadata.vertical_offset,
422
- acquisition_time=trace.metadata.acquisition_time,
423
- trigger_info=trace.metadata.trigger_info,
424
- source_file=trace.metadata.source_file,
425
- channel_name=channel_name or f"{trace.metadata.channel_name or 'trace'}_abs",
410
+ channel=channel or f"{trace.metadata.channel or 'trace'}_abs",
426
411
  )
427
412
 
428
413
  return WaveformTrace(data=result_data, metadata=new_metadata)
@@ -433,7 +418,7 @@ def differentiate(
433
418
  *,
434
419
  order: int = 1,
435
420
  method: str = "central",
436
- channel_name: str | None = None,
421
+ channel: str | None = None,
437
422
  ) -> WaveformTrace:
438
423
  """Compute numerical derivative of trace.
439
424
 
@@ -447,7 +432,7 @@ def differentiate(
447
432
  - "central": Central difference (default, most accurate)
448
433
  - "forward": Forward difference
449
434
  - "backward": Backward difference
450
- channel_name: Name for the result trace (optional).
435
+ channel: Name for the result trace (optional).
451
436
 
452
437
  Returns:
453
438
  Differentiated WaveformTrace in V/s.
@@ -467,7 +452,7 @@ def differentiate(
467
452
  raise ValueError(f"Order must be positive, got {order}")
468
453
 
469
454
  data = trace.data.astype(np.float64)
470
- dt = trace.metadata.time_base
455
+ dt = 1.0 / trace.metadata.sample_rate
471
456
 
472
457
  if len(data) < order + 1:
473
458
  raise InsufficientDataError(
@@ -500,10 +485,7 @@ def differentiate(
500
485
  sample_rate=trace.metadata.sample_rate,
501
486
  vertical_scale=None, # Units changed
502
487
  vertical_offset=None,
503
- acquisition_time=trace.metadata.acquisition_time,
504
- trigger_info=trace.metadata.trigger_info,
505
- source_file=trace.metadata.source_file,
506
- channel_name=channel_name or f"{trace.metadata.channel_name or 'trace'}_d{order}",
488
+ channel=channel or f"{trace.metadata.channel or 'trace'}_d{order}",
507
489
  )
508
490
 
509
491
  return WaveformTrace(data=result, metadata=new_metadata)
@@ -514,7 +496,7 @@ def integrate(
514
496
  *,
515
497
  method: str = "trapezoid",
516
498
  initial: float = 0.0,
517
- channel_name: str | None = None,
499
+ channel: str | None = None,
518
500
  ) -> WaveformTrace:
519
501
  """Compute numerical integral of trace.
520
502
 
@@ -528,7 +510,7 @@ def integrate(
528
510
  - "simpson": Simpson's rule (requires odd number of points)
529
511
  - "cumsum": Simple cumulative sum
530
512
  initial: Initial value for cumulative integral (default 0).
531
- channel_name: Name for the result trace (optional).
513
+ channel: Name for the result trace (optional).
532
514
 
533
515
  Returns:
534
516
  Integrated WaveformTrace in V*s.
@@ -545,7 +527,7 @@ def integrate(
545
527
  ARITH-006
546
528
  """
547
529
  data = trace.data.astype(np.float64)
548
- dt = trace.metadata.time_base
530
+ dt = 1.0 / trace.metadata.sample_rate
549
531
 
550
532
  if len(data) < 2:
551
533
  raise InsufficientDataError(
@@ -572,10 +554,7 @@ def integrate(
572
554
  sample_rate=trace.metadata.sample_rate,
573
555
  vertical_scale=None, # Units changed
574
556
  vertical_offset=None,
575
- acquisition_time=trace.metadata.acquisition_time,
576
- trigger_info=trace.metadata.trigger_info,
577
- source_file=trace.metadata.source_file,
578
- channel_name=channel_name or f"{trace.metadata.channel_name or 'trace'}_integral",
557
+ channel=channel or f"{trace.metadata.channel or 'trace'}_integral",
579
558
  )
580
559
 
581
560
  return WaveformTrace(data=result, metadata=new_metadata)
@@ -818,14 +797,14 @@ def _ensure_array_result(result: Any, expected_len: int) -> NDArray[np.float64]:
818
797
 
819
798
 
820
799
  def _build_expression_metadata(
821
- ref_trace: WaveformTrace, expression: str, channel_name: str | None
800
+ ref_trace: WaveformTrace, expression: str, channel: str | None
822
801
  ) -> TraceMetadata:
823
802
  """Build metadata for expression result trace.
824
803
 
825
804
  Args:
826
805
  ref_trace: Reference trace for metadata.
827
806
  expression: Expression string (for default naming).
828
- channel_name: Optional channel name override.
807
+ channel: Optional channel name override.
829
808
 
830
809
  Returns:
831
810
  Metadata for result trace.
@@ -834,10 +813,7 @@ def _build_expression_metadata(
834
813
  sample_rate=ref_trace.metadata.sample_rate,
835
814
  vertical_scale=None,
836
815
  vertical_offset=None,
837
- acquisition_time=ref_trace.metadata.acquisition_time,
838
- trigger_info=ref_trace.metadata.trigger_info,
839
- source_file=ref_trace.metadata.source_file,
840
- channel_name=channel_name or f"expr({expression[:20]})",
816
+ channel=channel or f"expr({expression[:20]})",
841
817
  )
842
818
 
843
819
 
@@ -845,7 +821,7 @@ def math_expression(
845
821
  expression: str,
846
822
  traces: dict[str, WaveformTrace],
847
823
  *,
848
- channel_name: str | None = None,
824
+ channel: str | None = None,
849
825
  ) -> WaveformTrace:
850
826
  """Evaluate a mathematical expression on traces.
851
827
 
@@ -855,7 +831,7 @@ def math_expression(
855
831
  Args:
856
832
  expression: Math expression (e.g., "CH1 + CH2", "abs(CH1 - CH2)").
857
833
  traces: Dictionary mapping variable names to traces.
858
- channel_name: Name for the result trace (optional).
834
+ channel: Name for the result trace (optional).
859
835
 
860
836
  Returns:
861
837
  Result WaveformTrace.
@@ -884,5 +860,5 @@ def math_expression(
884
860
  result = _evaluate_expression(expression, safe_namespace)
885
861
  result = _ensure_array_result(result, len(ref_trace.data))
886
862
 
887
- metadata = _build_expression_metadata(ref_trace, expression, channel_name)
863
+ metadata = _build_expression_metadata(ref_trace, expression, channel)
888
864
  return WaveformTrace(data=result.astype(np.float64), metadata=metadata)
@@ -35,7 +35,7 @@ def interpolate(
35
35
  *,
36
36
  method: Literal["linear", "cubic", "nearest", "zero"] = "linear",
37
37
  fill_value: float | tuple[float, float] = np.nan,
38
- channel_name: str | None = None,
38
+ channel: str | None = None,
39
39
  ) -> WaveformTrace:
40
40
  """Interpolate trace to new time points.
41
41
 
@@ -47,7 +47,7 @@ def interpolate(
47
47
  new_time: New time points in seconds.
48
48
  method: Interpolation method ("linear", "cubic", "nearest", "zero").
49
49
  fill_value: Value for points outside original range.
50
- channel_name: Name for the result trace (optional).
50
+ channel: Name for the result trace (optional).
51
51
 
52
52
  Returns:
53
53
  Interpolated WaveformTrace at new time points.
@@ -70,13 +70,13 @@ def interpolate(
70
70
 
71
71
  # Create interpolator and interpolate
72
72
  interp_func = _create_interpolator(
73
- trace.time_vector, trace.data.astype(np.float64), method, fill_value
73
+ trace.time, trace.data.astype(np.float64), method, fill_value
74
74
  )
75
75
  result_data = interp_func(new_time)
76
76
 
77
77
  # Build result trace
78
78
  new_sample_rate = _calculate_new_sample_rate(new_time, trace.metadata.sample_rate)
79
- new_metadata = _create_interpolated_metadata(trace, new_sample_rate, channel_name)
79
+ new_metadata = _create_interpolated_metadata(trace, new_sample_rate, channel)
80
80
 
81
81
  return WaveformTrace(data=result_data.astype(np.float64), metadata=new_metadata)
82
82
 
@@ -107,7 +107,7 @@ def _calculate_new_sample_rate(new_time: NDArray[np.float64], original_sample_ra
107
107
 
108
108
 
109
109
  def _create_interpolated_metadata(
110
- trace: WaveformTrace, new_sample_rate: float, channel_name: str | None
110
+ trace: WaveformTrace, new_sample_rate: float, channel: str | None
111
111
  ) -> TraceMetadata:
112
112
  """Create metadata for interpolated trace."""
113
113
  return TraceMetadata(
@@ -117,7 +117,7 @@ def _create_interpolated_metadata(
117
117
  acquisition_time=trace.metadata.acquisition_time,
118
118
  trigger_info=trace.metadata.trigger_info,
119
119
  source_file=trace.metadata.source_file,
120
- channel_name=channel_name or f"{trace.metadata.channel_name or 'trace'}_interp",
120
+ channel=channel or f"{trace.metadata.channel or 'trace'}_interp",
121
121
  )
122
122
 
123
123
 
@@ -214,7 +214,7 @@ def resample(
214
214
  num_samples: int | None = None,
215
215
  method: Literal["fft", "polyphase", "interp"] = "fft",
216
216
  anti_alias: bool = True,
217
- channel_name: str | None = None,
217
+ channel: str | None = None,
218
218
  ) -> WaveformTrace:
219
219
  """Resample trace to new sample rate or number of samples.
220
220
 
@@ -232,7 +232,7 @@ def resample(
232
232
  - "polyphase": Polyphase filter resampling (efficient)
233
233
  - "interp": Linear interpolation (fastest)
234
234
  anti_alias: Apply anti-aliasing filter before downsampling.
235
- channel_name: Name for the result trace (optional).
235
+ channel: Name for the result trace (optional).
236
236
 
237
237
  Returns:
238
238
  Resampled WaveformTrace.
@@ -293,7 +293,7 @@ def resample(
293
293
  acquisition_time=trace.metadata.acquisition_time,
294
294
  trigger_info=trace.metadata.trigger_info,
295
295
  source_file=trace.metadata.source_file,
296
- channel_name=channel_name or f"{trace.metadata.channel_name or 'trace'}_resampled",
296
+ channel=channel or f"{trace.metadata.channel or 'trace'}_resampled",
297
297
  )
298
298
 
299
299
  return WaveformTrace(data=result_data.astype(np.float64), metadata=new_metadata)
@@ -305,7 +305,7 @@ def align_traces(
305
305
  *,
306
306
  method: Literal["interpolate", "resample"] = "interpolate",
307
307
  reference: Literal["first", "second", "higher"] = "higher",
308
- channel_names: tuple[str | None, str | None] | None = None,
308
+ channels: tuple[str | None, str | None] | None = None,
309
309
  ) -> tuple[WaveformTrace, WaveformTrace]:
310
310
  """Align two traces to have the same sample rate and length.
311
311
 
@@ -322,7 +322,7 @@ def align_traces(
322
322
  - "first": Use trace1's sample rate
323
323
  - "second": Use trace2's sample rate
324
324
  - "higher": Use the higher sample rate (default)
325
- channel_names: Optional names for the aligned traces.
325
+ channels: Optional names for the aligned traces.
326
326
 
327
327
  Returns:
328
328
  Tuple of (aligned_trace1, aligned_trace2) with matching parameters.
@@ -353,17 +353,17 @@ def align_traces(
353
353
  # Create common time vector
354
354
  common_time = np.arange(num_samples) / target_rate
355
355
 
356
- name1 = channel_names[0] if channel_names else None
357
- name2 = channel_names[1] if channel_names else None
356
+ name1 = channels[0] if channels else None
357
+ name2 = channels[1] if channels else None
358
358
 
359
359
  if method == "interpolate":
360
360
  # Interpolate both traces to common time points
361
- aligned1 = interpolate(trace1, common_time, channel_name=name1)
362
- aligned2 = interpolate(trace2, common_time, channel_name=name2)
361
+ aligned1 = interpolate(trace1, common_time, channel=name1)
362
+ aligned2 = interpolate(trace2, common_time, channel=name2)
363
363
  else: # "resample"
364
364
  # Resample both to common rate
365
- aligned1 = resample(trace1, num_samples=num_samples, channel_name=name1)
366
- aligned2 = resample(trace2, num_samples=num_samples, channel_name=name2)
365
+ aligned1 = resample(trace1, num_samples=num_samples, channel=name1)
366
+ aligned2 = resample(trace2, num_samples=num_samples, channel=name2)
367
367
 
368
368
  return aligned1, aligned2
369
369
 
@@ -374,7 +374,7 @@ def downsample(
374
374
  *,
375
375
  anti_alias: bool = True,
376
376
  method: Literal["decimate", "average", "max", "min"] = "decimate",
377
- channel_name: str | None = None,
377
+ channel: str | None = None,
378
378
  ) -> WaveformTrace:
379
379
  """Downsample trace by an integer factor.
380
380
 
@@ -390,7 +390,7 @@ def downsample(
390
390
  - "average": Average every N samples
391
391
  - "max": Maximum of every N samples
392
392
  - "min": Minimum of every N samples
393
- channel_name: Name for the result trace (optional).
393
+ channel: Name for the result trace (optional).
394
394
 
395
395
  Returns:
396
396
  Downsampled WaveformTrace.
@@ -440,7 +440,7 @@ def downsample(
440
440
  acquisition_time=trace.metadata.acquisition_time,
441
441
  trigger_info=trace.metadata.trigger_info,
442
442
  source_file=trace.metadata.source_file,
443
- channel_name=channel_name or f"{trace.metadata.channel_name or 'trace'}_ds{factor}",
443
+ channel=channel or f"{trace.metadata.channel or 'trace'}_ds{factor}",
444
444
  )
445
445
 
446
446
  return WaveformTrace(data=result_data, metadata=new_metadata)
@@ -1,15 +1,12 @@
1
- """Pipeline architecture for composable trace transformations.
1
+ """Reverse engineering pipeline for protocol analysis.
2
2
 
3
- This package provides pipeline composition, functional operators, and
4
- base classes for building reusable trace processing workflows.
3
+ This package provides the REPipeline for automated protocol reverse engineering
4
+ from captured traces, including flow extraction, payload analysis, field inference,
5
+ and protocol structure discovery.
5
6
 
6
7
  - RE-INT-001: RE Pipeline Integration
7
8
  """
8
9
 
9
- from .base import TraceTransformer
10
- from .composition import Composable, compose, curry, make_composable, pipe
11
- from .pipeline import Pipeline
12
-
13
10
  # RE-INT-001: Reverse Engineering Pipeline
14
11
  from .reverse_engineering import (
15
12
  FlowInfo,
@@ -22,22 +19,12 @@ from .reverse_engineering import (
22
19
  )
23
20
 
24
21
  __all__ = [
25
- "Composable",
26
22
  # RE-INT-001: Reverse Engineering Pipeline
27
23
  "FlowInfo",
28
24
  "MessageTypeInfo",
29
- # Pipeline
30
- "Pipeline",
31
25
  "ProtocolCandidate",
32
26
  "REAnalysisResult",
33
27
  "REPipeline",
34
28
  "StageResult",
35
- # Base classes
36
- "TraceTransformer",
37
29
  "analyze",
38
- # Composition
39
- "compose",
40
- "curry",
41
- "make_composable",
42
- "pipe",
43
30
  ]
@@ -242,10 +242,7 @@ def analyze_roi(
242
242
  sample_rate=trace.metadata.sample_rate,
243
243
  vertical_scale=trace.metadata.vertical_scale,
244
244
  vertical_offset=trace.metadata.vertical_offset,
245
- acquisition_time=trace.metadata.acquisition_time,
246
- trigger_info=trace.metadata.trigger_info,
247
- source_file=trace.metadata.source_file,
248
- channel_name=getattr(trace.metadata, "channel_name", None),
245
+ channel=trace.metadata.channel,
249
246
  ),
250
247
  )
251
248
 
@@ -78,7 +78,7 @@ class EdgeTrigger(Trigger):
78
78
  else:
79
79
  data = trace.data
80
80
 
81
- sample_period = trace.metadata.time_base
81
+ sample_period = 1.0 / trace.metadata.sample_rate
82
82
  events: list[TriggerEvent] = []
83
83
 
84
84
  if self.hysteresis > 0:
@@ -91,7 +91,7 @@ class PatternTrigger(Trigger):
91
91
  level = self._get_level(trace)
92
92
  digital = trace.data >= level
93
93
 
94
- sample_period = trace.metadata.time_base
94
+ sample_period = 1.0 / trace.metadata.sample_rate
95
95
  events: list[TriggerEvent] = []
96
96
 
97
97
  if self.match_type == "sequence":
@@ -240,7 +240,7 @@ class MultiChannelPatternTrigger(Trigger):
240
240
  digitals.append(trace.data >= level)
241
241
 
242
242
  # Find samples where all channels match pattern
243
- sample_period = traces[0].metadata.time_base
243
+ sample_period = 1.0 / traces[0].metadata.sample_rate
244
244
  n_samples = min(len(d) for d in digitals)
245
245
  events: list[TriggerEvent] = []
246
246
 
@@ -142,7 +142,7 @@ class PulseWidthTrigger(Trigger):
142
142
  data = trace.data
143
143
  level = self.level
144
144
 
145
- sample_period = trace.metadata.time_base
145
+ sample_period = 1.0 / trace.metadata.sample_rate
146
146
  pulses: list[PulseInfo] = []
147
147
 
148
148
  # Find all threshold crossings
@@ -419,7 +419,7 @@ class RuntTrigger(Trigger):
419
419
  return []
420
420
 
421
421
  data = trace.data
422
- sample_period = trace.metadata.time_base
422
+ sample_period = 1.0 / trace.metadata.sample_rate
423
423
  events: list[TriggerEvent] = []
424
424
 
425
425
  zones = np.array([self._get_zone(v) for v in data])