Skip to content

Commit

Permalink
Fill pulse traces with NaN/-1 instead of throwing an exception when t…
Browse files Browse the repository at this point in the history
…rain trace is too short
  • Loading branch information
philsmt committed Sep 26, 2024
1 parent 049efdf commit 8c63960
Show file tree
Hide file tree
Showing 3 changed files with 26 additions and 4 deletions.
1 change: 1 addition & 0 deletions docs/changelog.md
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@ Changed:
use by default (!221).
- [`fit_gaussian()`][extra.utils.fit_gaussian] has a new `A_sign` parameter to
specify the expected orientation of the peak (!222).
- [`AdqRawChannel.pulse_data()`][extra.components.AdqRawChannel.pulse_data] no longer throws an exception if the pulse pattern refers to data beyond the acquired traces, but instead fills this with NaN or -1 depending on data type (!245).

## [2024.1.2]
Added:
Expand Down
25 changes: 21 additions & 4 deletions src/extra/components/_adq.pyx
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
from cython cimport numeric
from libc.limits cimport INT_MAX
from libc.string cimport memcpy
from numpy.math cimport NAN


def _reshape_flat_pulses(
Expand All @@ -15,12 +16,18 @@ def _reshape_flat_pulses(
if pulse_ids.shape[0] > out.shape[0]:
raise ValueError('pulse output buffer smaller than pulse count')

cdef int start, end, i, j = -1, \
cdef int start, end, i, j = -1, k, \
cur_pid, prev_pid = INT_MAX, pid_offset = 0, \
num_trains = traces.shape[0], trace_len = traces.shape[1]

cdef size_t bytes_per_pulse = samples_per_pulse * sizeof(traces[0, 0])

# Prepare the placeholder value for float and int types, using the
# fact that NAN != NAN for the true float value while becoming equal
# after integer conversion.
cdef numeric placeholder_val = -1 if <numeric>NAN == <numeric>NAN \
else <numeric>NAN

for i in range(pulse_ids.shape[0]):
cur_pid = pulse_ids[i]

Expand All @@ -37,6 +44,16 @@ def _reshape_flat_pulses(
end = start + samples_per_pulse

if end > trace_len:
raise ValueError(f'trace too short for pulse at {start}:{end}')

memcpy(&out[i, 0], &traces[j, start], bytes_per_pulse)
# The end of this pulse is beyond the trace length, try to
# copy what remains and fill with placeholder as needed.

if start < trace_len:
# There is still some data to copy into this pulse.
memcpy(&out[i, 0], &traces[j, start],
(trace_len - start) * sizeof(traces[0, 0]))

# Fill in the rest of this pulse with the placeholder value.
for k in range(max(trace_len - start, 0), samples_per_pulse):
out[i, k] = placeholder_val
else:
memcpy(&out[i, 0], &traces[j, start], bytes_per_pulse)
4 changes: 4 additions & 0 deletions src/extra/components/adq.py
Original file line number Diff line number Diff line change
Expand Up @@ -1046,6 +1046,10 @@ def pulse_data(self, labelled=True, pulse_dim='pulseId', train_roi=(),
This process depends on the `first_pulse_offset` and potentially
`single_pulse_length` the component was initialized with.
If the pulse information refers to data beyond the acquired
traces, it is filled by np.nan for floating data types or
-1 for integer types.
Args:
labelled (bool, optional): Whether data is returned as a
labelled xarray (default) or unlabelled ndarray.
Expand Down

0 comments on commit 8c63960

Please sign in to comment.