Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fill pulse traces with NaN/-1 if data is too short #245

Open
wants to merge 1 commit into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
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
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:
Comment on lines 44 to 46
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
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)
Comment on lines +47 to +59
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looks it possible to make a bit simple:

Suggested change
# 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)
# The end of this pulse is beyond the trace length, try to
# copy what remains and fill with placeholder as needed.
samples_to_copy = min(max(trace_len - start, 0), samples_per_pulse)
# copy if data available
memcpy(&out[i, 0], &traces[j, start], samples_to_copy * sizeof(traces[0, 0]))
# Fill in the rest of this pulse with the placeholder value.
for k in range(samples_to_copy, samples_per_pulse):
out[i, k] = placeholder_val

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