Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
25 commits
Select commit Hold shift + click to select a range
0eb7878
Initial plan
Copilot Nov 27, 2025
192ac45
Initial plan
Copilot Nov 27, 2025
bd1afb3
Add arithmetic.py with sum and mean processors
Copilot Nov 27, 2025
97c3559
Add sort_wf processor that returns a sorted array using numpy.sort
Copilot Nov 27, 2025
48b7617
Make sum and mean consistent (both return NaN for empty range) and us…
Copilot Nov 28, 2025
9a0fb33
Rename sort_wf to sort
Copilot Nov 28, 2025
42a086b
Merge pull request #2 from cVogl97/copilot/add-dspeed-processor-function
cVogl97 Nov 28, 2025
149dd96
Fix mean to divide by b-a as per original requirements
Copilot Nov 28, 2025
b66d479
updated docstring of time_point_thresh and added time_point_thresh_no…
cVogl97 Nov 28, 2025
2ac0d0f
Fix mean to divide by end-start+1 (number of elements)
Copilot Nov 28, 2025
c83a672
Merge pull request #4 from cVogl97/time_point_thresh
cVogl97 Nov 28, 2025
c83e511
Merge pull request #1 from cVogl97/copilot/add-arithmetics-file
cVogl97 Nov 28, 2025
79dc5cf
add time_point_thresh_nopol to init
cVogl97 Nov 28, 2025
4885af7
Initial plan
Copilot Nov 28, 2025
7544fa2
Add tests for time_point_thresh_nopol and differentiation tests
Copilot Nov 28, 2025
c248087
Clarified documentation
cVogl97 Dec 1, 2025
6c88f15
changed reported index when walking forward in time_point_thresh_nopol
cVogl97 Dec 1, 2025
99e295b
Adapt time_point_thresh_nopol tests for new behavior returning one in…
Copilot Dec 1, 2025
32fa530
Merge branch 'arithmetics' into copilot/add-tests-for-time-point-thresh
cVogl97 Dec 1, 2025
ae5aa51
Merge pull request #5 from cVogl97/copilot/add-tests-for-time-point-t…
cVogl97 Dec 1, 2025
4dca79b
simplified mean and sum processors such that they don't take start an…
cVogl97 Dec 2, 2025
3a5e551
Initial plan
Copilot Dec 2, 2025
03c6543
Update arithmetic tests and add mean_below_threshold processor
Copilot Dec 2, 2025
16e2f42
Apply black formatting
Copilot Dec 2, 2025
973e366
Merge pull request #6 from cVogl97/copilot/update-mean-sum-test-and-a…
cVogl97 Dec 2, 2025
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions src/dspeed/processors/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -64,6 +64,9 @@
# Mapping from function to name of module in which it is defined
# To add a new function to processors, it must be added here!
_modules = {
"mean": "arithmetic",
"mean_below_threshold": "arithmetic",
"sum": "arithmetic",
"bl_subtract": "bl_subtract",
"convolve_damped_oscillator": "convolutions",
"convolve_exp": "convolutions",
Expand Down Expand Up @@ -131,13 +134,15 @@
"saturation": "saturation",
"soft_pileup_corr": "soft_pileup_corr",
"soft_pileup_corr_bl": "soft_pileup_corr",
"sort": "sort",
"svm_predict": "svm",
"tf_model": "tf_model",
"time_over_threshold": "time_over_threshold",
"bi_level_zero_crossing_time_points": "time_point_thresh",
"interpolated_time_point_thresh": "time_point_thresh",
"multi_time_point_thresh": "time_point_thresh",
"time_point_thresh": "time_point_thresh",
"time_point_thresh_nopol": "time_point_thresh",
"asym_trap_filter": "trap_filters",
"trap_filter": "trap_filters",
"trap_norm": "trap_filters",
Expand Down
170 changes: 170 additions & 0 deletions src/dspeed/processors/arithmetic.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,170 @@
from __future__ import annotations

import numpy as np
from numba import guvectorize

from ..utils import numba_defaults_kwargs as nb_kwargs


@guvectorize(
[
"void(float32[:], float32[:])",
"void(float64[:], float64[:])",
],
"(n)->()",
**nb_kwargs,
)
def sum(w_in: np.ndarray, result: float) -> None:
"""Sum the waveform values from index a to b.

Parameters
----------
w_in
the input waveform
result
the sum of all values in w_in.

YAML Configuration Example
--------------------------

.. code-block:: yaml

wf_sum:
function: sum
module: dspeed.processors
args:
- waveform
- wf_sum
unit:
- ADC
"""
result[0] = np.nan

if np.isnan(w_in).any():
return

start = 0
end = len(w_in) - 1

if start < 0:
start = 0
if end > len(w_in) - 1:
end = len(w_in) - 1
if start > end:
return

total = 0.0
for i in range(start, end + 1):
total += w_in[i]

result[0] = total


@guvectorize(
[
"void(float32[:], float32[:])",
"void(float64[:], float64[:])",
],
"(n)->()",
**nb_kwargs,
)
def mean(w_in: np.ndarray, result: float) -> None:
"""Calculate the mean of waveform values.

Parameters
----------
w_in
the input waveform.
result
the mean of all values in w_in.

YAML Configuration Example
--------------------------

.. code-block:: yaml

wf_mean:
function: mean
module: dspeed.processors
args:
- waveform
- wf_mean
unit:
- ADC
"""
result[0] = np.nan

if np.isnan(w_in).any():
return

start = 0
end = len(w_in) - 1

if start < 0:
start = 0
if end > len(w_in) - 1:
end = len(w_in) - 1
if start > end:
return

total = 0.0
for i in range(start, end + 1):
total += w_in[i]

result[0] = total / (end - start + 1)


@guvectorize(
[
"void(float32[:], float32, float32[:])",
"void(float64[:], float64, float64[:])",
],
"(n),()->()",
**nb_kwargs,
)
def mean_below_threshold(w_in: np.ndarray, threshold: float, result: float) -> None:
"""Calculate the mean of waveform values that are below a threshold.

Parameters
----------
w_in
the input waveform.
threshold
the threshold value. Only waveform values below this threshold
are included in the mean calculation.
result
the mean of all values in w_in that are below the threshold.
Returns NaN if no values are below the threshold.

YAML Configuration Example
--------------------------

.. code-block:: yaml

wf_mean_below_threshold:
function: mean_below_threshold
module: dspeed.processors
args:
- waveform
- 100
- wf_mean_below_threshold
unit:
- ADC
"""
result[0] = np.nan

if np.isnan(w_in).any() or np.isnan(threshold):
return

total = 0.0
count = 0

for i in range(len(w_in)):
if w_in[i] < threshold:
total += w_in[i]
count += 1

if count == 0:
return

result[0] = total / count
41 changes: 41 additions & 0 deletions src/dspeed/processors/sort.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
from __future__ import annotations

import numpy as np
from numba import guvectorize

from ..utils import numba_defaults_kwargs as nb_kwargs


@guvectorize(
["void(float32[:], float32[:])", "void(float64[:], float64[:])"],
"(n)->(n)",
**nb_kwargs,
)
def sort(w_in: np.ndarray, w_out: np.ndarray) -> None:
"""Return a sorted array using :func:`numpy.sort`.

Parameters
----------
w_in
the input waveform.
w_out
the output sorted waveform.

YAML Configuration Example
--------------------------

.. code-block:: yaml

wf_sorted:
function: sort
module: dspeed.processors
args:
- waveform
- wf_sorted
"""
w_out[:] = np.nan

if np.isnan(w_in).any():
return

w_out[:] = np.sort(w_in)
83 changes: 81 additions & 2 deletions src/dspeed/processors/time_point_thresh.py
Original file line number Diff line number Diff line change
Expand Up @@ -18,8 +18,11 @@
def time_point_thresh(
w_in: np.ndarray, a_threshold: float, t_start: int, walk_forward: int, t_out: float
) -> None:
"""Find the index where the waveform value crosses the threshold, walking
either forward or backward from the starting index.
"""Find the index where the waveform value crosses above the threshold, walking
either forward or backward from the starting index. Find only crossings where the
waveform is rising through the threshold when moving forward in time (polarity check).
Return the waveform index just before the threshold crossing (i.e. below the threshold
when searching forward and above the threshold when searching backward).

Parameters
----------
Expand Down Expand Up @@ -81,6 +84,82 @@ def time_point_thresh(
return


@guvectorize(
[
"void(float32[:], float32, float32, float32, float32[:])",
"void(float64[:], float64, float64, float64, float64[:])",
],
"(n),(),(),()->()",
**nb_kwargs,
)
def time_point_thresh_nopol(
w_in: np.ndarray, a_threshold: float, t_start: int, walk_forward: int, t_out: float
) -> None:
"""Find the index where the waveform value crosses below a threshold, walking
either forward or backward from the starting index, without polarity check.
I.e., find the first crossing in the specified direction, regardless of whether the waveform is rising or falling.
Return the waveform index just above the threshold crossing.

Parameters
----------
w_in
the input waveform.
a_threshold
the threshold value.
t_start
the starting index.
walk_forward
the backward (``0``) or forward (``1``) search direction.
t_out
the index where the waveform value crosses the threshold.

YAML Configuration Example
--------------------------

.. code-block:: yaml

tp_0:
function: time_point_thresh
module: dspeed.processors
args:
- wf_atrap
- bl_std
- tp_start
- 0
- tp_0
unit: ns
"""
t_out[0] = np.nan

if (
np.isnan(w_in).any()
or np.isnan(a_threshold)
or np.isnan(t_start)
or np.isnan(walk_forward)
):
return

if np.floor(t_start) != t_start:
raise DSPFatal("The starting index must be an integer")

if np.floor(walk_forward) != walk_forward:
raise DSPFatal("The search direction must be an integer")

if int(t_start) < 0 or int(t_start) >= len(w_in):
raise DSPFatal("The starting index is out of range")

if int(walk_forward) == 1:
for i in range(int(t_start), len(w_in) - 2, 1):
if w_in[i + 1] <= a_threshold:
t_out[0] = i
return
else:
for i in range(int(t_start), 0, -1):
if w_in[i - 1] < a_threshold:
t_out[0] = i
return


@guvectorize(
[
"void(float32[:], float32, float32, int64, char, float32[:])",
Expand Down
Loading
Loading