From dc05f44dda54c4c033a91e626db914cf27eabdf7 Mon Sep 17 00:00:00 2001 From: Benjamin Moody Date: Tue, 2 Apr 2024 12:44:45 -0400 Subject: [PATCH 01/13] adc (inplace=True, expanded=False): refactor for clarity. --- wfdb/io/_signal.py | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/wfdb/io/_signal.py b/wfdb/io/_signal.py index a4ffbced..cb7770f6 100644 --- a/wfdb/io/_signal.py +++ b/wfdb/io/_signal.py @@ -556,12 +556,13 @@ def adc(self, expanded=False, inplace=False): self.e_d_signal = self.e_p_signal self.e_p_signal = None else: - nanlocs = np.isnan(self.p_signal) - np.multiply(self.p_signal, self.adc_gain, self.p_signal) - np.add(self.p_signal, self.baseline, self.p_signal) - np.round(self.p_signal, 0, self.p_signal) - self.p_signal = self.p_signal.astype(intdtype, copy=False) - self.d_signal = self.p_signal + p_signal = self.p_signal + nanlocs = np.isnan(p_signal) + np.multiply(p_signal, self.adc_gain, p_signal) + np.add(p_signal, self.baseline, p_signal) + np.round(p_signal, 0, p_signal) + d_signal = p_signal.astype(intdtype, copy=False) + self.d_signal = d_signal self.p_signal = None # Return the variable From 2eabaa51221600071b3d759c85161e2ace725546 Mon Sep 17 00:00:00 2001 From: Benjamin Moody Date: Tue, 2 Apr 2024 12:46:13 -0400 Subject: [PATCH 02/13] adc (inplace=False, expanded=False): refactor for clarity. --- wfdb/io/_signal.py | 13 ++++++------- 1 file changed, 6 insertions(+), 7 deletions(-) diff --git a/wfdb/io/_signal.py b/wfdb/io/_signal.py index cb7770f6..0d99fa4b 100644 --- a/wfdb/io/_signal.py +++ b/wfdb/io/_signal.py @@ -581,13 +581,12 @@ def adc(self, expanded=False, inplace=False): d_signal.append(ch_d_signal) else: - nanlocs = np.isnan(self.p_signal) - # Cannot cast dtype to int now because gain is float. - d_signal = self.p_signal.copy() - np.multiply(d_signal, self.adc_gain, d_signal) - np.add(d_signal, self.baseline, d_signal) - np.round(d_signal, 0, d_signal) - d_signal = d_signal.astype(intdtype, copy=False) + p_signal = self.p_signal.copy() + nanlocs = np.isnan(p_signal) + np.multiply(p_signal, self.adc_gain, p_signal) + np.add(p_signal, self.baseline, p_signal) + np.round(p_signal, 0, p_signal) + d_signal = p_signal.astype(intdtype, copy=False) if nanlocs.any(): for ch in range(d_signal.shape[1]): From 770423390c2f131b5d2ccf6a01cb0734499d89cf Mon Sep 17 00:00:00 2001 From: Benjamin Moody Date: Tue, 2 Apr 2024 12:48:34 -0400 Subject: [PATCH 03/13] adc (inplace=True, expanded=False): correctly handle NaNs. When converting physical to digital sample arrays, we must replace NaN values (which represent a missing sample) with the appropriate invalid-sample sentinel value. This is done correctly for normal uses of the package, but if the application directly invoked adc(inplace=True), NaNs would not have been handled (and were instead set to an implementation-defined value.) (Note that we don't use inplace=True internally because this overwrites the original floating-point array. Applications may want to use inplace=True to save memory, but this requires knowing that the original array is no longer needed.) --- wfdb/io/_signal.py | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/wfdb/io/_signal.py b/wfdb/io/_signal.py index 0d99fa4b..4662e3b9 100644 --- a/wfdb/io/_signal.py +++ b/wfdb/io/_signal.py @@ -562,6 +562,12 @@ def adc(self, expanded=False, inplace=False): np.add(p_signal, self.baseline, p_signal) np.round(p_signal, 0, p_signal) d_signal = p_signal.astype(intdtype, copy=False) + + if nanlocs.any(): + for ch in range(d_signal.shape[1]): + if nanlocs[:, ch].any(): + d_signal[nanlocs[:, ch], ch] = d_nans[ch] + self.d_signal = d_signal self.p_signal = None From d629897be8dc6b13e542a4ea5e95e6a0b0b03aad Mon Sep 17 00:00:00 2001 From: Benjamin Moody Date: Tue, 2 Apr 2024 12:53:09 -0400 Subject: [PATCH 04/13] adc (expanded=False): move shared logic to a function. --- wfdb/io/_signal.py | 41 +++++++++++++++++------------------------ 1 file changed, 17 insertions(+), 24 deletions(-) diff --git a/wfdb/io/_signal.py b/wfdb/io/_signal.py index 4662e3b9..8e2d123a 100644 --- a/wfdb/io/_signal.py +++ b/wfdb/io/_signal.py @@ -532,6 +532,21 @@ def adc(self, expanded=False, inplace=False): # To do: choose the minimum return res needed intdtype = "int64" + # Convert a 2D physical signal array to digital. Note that the + # input array is modified! + def adc_inplace_2d(p_signal): + nanlocs = np.isnan(p_signal) + np.multiply(p_signal, self.adc_gain, p_signal) + np.add(p_signal, self.baseline, p_signal) + np.round(p_signal, 0, p_signal) + d_signal = p_signal.astype(intdtype, copy=False) + + if nanlocs.any(): + for ch in range(d_signal.shape[1]): + if nanlocs[:, ch].any(): + d_signal[nanlocs[:, ch], ch] = d_nans[ch] + return d_signal + # Do inplace conversion and set relevant variables. if inplace: if expanded: @@ -556,19 +571,7 @@ def adc(self, expanded=False, inplace=False): self.e_d_signal = self.e_p_signal self.e_p_signal = None else: - p_signal = self.p_signal - nanlocs = np.isnan(p_signal) - np.multiply(p_signal, self.adc_gain, p_signal) - np.add(p_signal, self.baseline, p_signal) - np.round(p_signal, 0, p_signal) - d_signal = p_signal.astype(intdtype, copy=False) - - if nanlocs.any(): - for ch in range(d_signal.shape[1]): - if nanlocs[:, ch].any(): - d_signal[nanlocs[:, ch], ch] = d_nans[ch] - - self.d_signal = d_signal + self.d_signal = adc_inplace_2d(self.p_signal) self.p_signal = None # Return the variable @@ -587,17 +590,7 @@ def adc(self, expanded=False, inplace=False): d_signal.append(ch_d_signal) else: - p_signal = self.p_signal.copy() - nanlocs = np.isnan(p_signal) - np.multiply(p_signal, self.adc_gain, p_signal) - np.add(p_signal, self.baseline, p_signal) - np.round(p_signal, 0, p_signal) - d_signal = p_signal.astype(intdtype, copy=False) - - if nanlocs.any(): - for ch in range(d_signal.shape[1]): - if nanlocs[:, ch].any(): - d_signal[nanlocs[:, ch], ch] = d_nans[ch] + d_signal = adc_inplace_2d(self.p_signal.copy()) return d_signal From 7ae21d4c17a9c6e63bddfe1d7fef26f3a087f543 Mon Sep 17 00:00:00 2001 From: Benjamin Moody Date: Tue, 2 Apr 2024 13:02:01 -0400 Subject: [PATCH 05/13] adc (inplace=True, expanded=True): refactor for clarity. --- wfdb/io/_signal.py | 25 ++++++++----------------- 1 file changed, 8 insertions(+), 17 deletions(-) diff --git a/wfdb/io/_signal.py b/wfdb/io/_signal.py index 8e2d123a..8efa54fb 100644 --- a/wfdb/io/_signal.py +++ b/wfdb/io/_signal.py @@ -551,23 +551,14 @@ def adc_inplace_2d(p_signal): if inplace: if expanded: for ch in range(self.n_sig): - # NAN locations for the channel - ch_nanlocs = np.isnan(self.e_p_signal[ch]) - np.multiply( - self.e_p_signal[ch], - self.adc_gain[ch], - self.e_p_signal[ch], - ) - np.add( - self.e_p_signal[ch], - self.baseline[ch], - self.e_p_signal[ch], - ) - np.round(self.e_p_signal[ch], 0, self.e_p_signal[ch]) - self.e_p_signal[ch] = self.e_p_signal[ch].astype( - intdtype, copy=False - ) - self.e_p_signal[ch][ch_nanlocs] = d_nans[ch] + ch_p_signal = self.e_p_signal[ch] + ch_nanlocs = np.isnan(ch_p_signal) + np.multiply(ch_p_signal, self.adc_gain[ch], ch_p_signal) + np.add(ch_p_signal, self.baseline[ch], ch_p_signal) + np.round(ch_p_signal, 0, ch_p_signal) + ch_d_signal = ch_p_signal.astype(intdtype, copy=False) + ch_d_signal[ch_nanlocs] = d_nans[ch] + self.e_p_signal[ch] = ch_d_signal self.e_d_signal = self.e_p_signal self.e_p_signal = None else: From 89458613653d9d052ddd1bd4aea63f2572212377 Mon Sep 17 00:00:00 2001 From: Benjamin Moody Date: Tue, 2 Apr 2024 13:03:32 -0400 Subject: [PATCH 06/13] adc (inplace=False, expanded=True): refactor for clarity. --- wfdb/io/_signal.py | 13 ++++++------- 1 file changed, 6 insertions(+), 7 deletions(-) diff --git a/wfdb/io/_signal.py b/wfdb/io/_signal.py index 8efa54fb..e3b75637 100644 --- a/wfdb/io/_signal.py +++ b/wfdb/io/_signal.py @@ -570,13 +570,12 @@ def adc_inplace_2d(p_signal): if expanded: d_signal = [] for ch in range(self.n_sig): - # NAN locations for the channel - ch_nanlocs = np.isnan(self.e_p_signal[ch]) - ch_d_signal = self.e_p_signal[ch].copy() - np.multiply(ch_d_signal, self.adc_gain[ch], ch_d_signal) - np.add(ch_d_signal, self.baseline[ch], ch_d_signal) - np.round(ch_d_signal, 0, ch_d_signal) - ch_d_signal = ch_d_signal.astype(intdtype, copy=False) + ch_p_signal = self.e_p_signal[ch].copy() + ch_nanlocs = np.isnan(ch_p_signal) + np.multiply(ch_p_signal, self.adc_gain[ch], ch_p_signal) + np.add(ch_p_signal, self.baseline[ch], ch_p_signal) + np.round(ch_p_signal, 0, ch_p_signal) + ch_d_signal = ch_p_signal.astype(intdtype, copy=False) ch_d_signal[ch_nanlocs] = d_nans[ch] d_signal.append(ch_d_signal) From 235e1427c6298f3d13a613539d676f975aea2210 Mon Sep 17 00:00:00 2001 From: Benjamin Moody Date: Tue, 2 Apr 2024 13:16:59 -0400 Subject: [PATCH 07/13] adc (expanded=True): move shared logic to a function. --- wfdb/io/_signal.py | 37 +++++++++++++++++++++++-------------- 1 file changed, 23 insertions(+), 14 deletions(-) diff --git a/wfdb/io/_signal.py b/wfdb/io/_signal.py index e3b75637..40e5af79 100644 --- a/wfdb/io/_signal.py +++ b/wfdb/io/_signal.py @@ -532,6 +532,17 @@ def adc(self, expanded=False, inplace=False): # To do: choose the minimum return res needed intdtype = "int64" + # Convert a single physical channel to digital. Note that the + # input array is modified! + def adc_inplace_1d(ch_p_signal, adc_gain, baseline, d_nan): + ch_nanlocs = np.isnan(ch_p_signal) + np.multiply(ch_p_signal, adc_gain, ch_p_signal) + np.add(ch_p_signal, baseline, ch_p_signal) + np.round(ch_p_signal, 0, ch_p_signal) + ch_d_signal = ch_p_signal.astype(intdtype, copy=False) + ch_d_signal[ch_nanlocs] = d_nan + return ch_d_signal + # Convert a 2D physical signal array to digital. Note that the # input array is modified! def adc_inplace_2d(p_signal): @@ -551,13 +562,12 @@ def adc_inplace_2d(p_signal): if inplace: if expanded: for ch in range(self.n_sig): - ch_p_signal = self.e_p_signal[ch] - ch_nanlocs = np.isnan(ch_p_signal) - np.multiply(ch_p_signal, self.adc_gain[ch], ch_p_signal) - np.add(ch_p_signal, self.baseline[ch], ch_p_signal) - np.round(ch_p_signal, 0, ch_p_signal) - ch_d_signal = ch_p_signal.astype(intdtype, copy=False) - ch_d_signal[ch_nanlocs] = d_nans[ch] + ch_d_signal = adc_inplace_1d( + self.e_p_signal[ch], + self.adc_gain[ch], + self.baseline[ch], + d_nans[ch], + ) self.e_p_signal[ch] = ch_d_signal self.e_d_signal = self.e_p_signal self.e_p_signal = None @@ -570,13 +580,12 @@ def adc_inplace_2d(p_signal): if expanded: d_signal = [] for ch in range(self.n_sig): - ch_p_signal = self.e_p_signal[ch].copy() - ch_nanlocs = np.isnan(ch_p_signal) - np.multiply(ch_p_signal, self.adc_gain[ch], ch_p_signal) - np.add(ch_p_signal, self.baseline[ch], ch_p_signal) - np.round(ch_p_signal, 0, ch_p_signal) - ch_d_signal = ch_p_signal.astype(intdtype, copy=False) - ch_d_signal[ch_nanlocs] = d_nans[ch] + ch_d_signal = adc_inplace_1d( + self.e_p_signal[ch].copy(), + self.adc_gain[ch], + self.baseline[ch], + d_nans[ch], + ) d_signal.append(ch_d_signal) else: From 384b9873639f5ca2c73e3120044c5ce942027bf0 Mon Sep 17 00:00:00 2001 From: Benjamin Moody Date: Tue, 2 Apr 2024 14:02:18 -0400 Subject: [PATCH 08/13] adc (expanded=True): do not rely on n_sig. When converting physical to digital sample arrays, all the information we need is contained in self.e_p_signal, self.adc_gain, self.baseline, and self.fmt. We don't need to rely on self.n_sig here, and we don't use n_sig in the expanded=False case, so for consistency, don't use n_sig in the expanded=True case either. --- wfdb/io/_signal.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/wfdb/io/_signal.py b/wfdb/io/_signal.py index 40e5af79..44185105 100644 --- a/wfdb/io/_signal.py +++ b/wfdb/io/_signal.py @@ -561,9 +561,9 @@ def adc_inplace_2d(p_signal): # Do inplace conversion and set relevant variables. if inplace: if expanded: - for ch in range(self.n_sig): + for ch, ch_p_signal in enumerate(self.e_p_signal): ch_d_signal = adc_inplace_1d( - self.e_p_signal[ch], + ch_p_signal, self.adc_gain[ch], self.baseline[ch], d_nans[ch], @@ -579,9 +579,9 @@ def adc_inplace_2d(p_signal): else: if expanded: d_signal = [] - for ch in range(self.n_sig): + for ch, ch_p_signal in enumerate(self.e_p_signal): ch_d_signal = adc_inplace_1d( - self.e_p_signal[ch].copy(), + ch_p_signal.copy(), self.adc_gain[ch], self.baseline[ch], d_nans[ch], From 2b1d824746073c026371ea1a2c76ebddd68cf387 Mon Sep 17 00:00:00 2001 From: Benjamin Moody Date: Tue, 2 Apr 2024 14:06:32 -0400 Subject: [PATCH 09/13] adc: replace NaNs before converting array to integers. When converting physical to digital sample arrays, we must replace NaN values (which represent a missing sample) with the appropriate invalid-sample sentinel value. Attempting to convert a floating-point NaN to an integer, as was done here, is implementation-defined behavior (and is controlled, to an extent, by the global numpy configuration.) We don't want to be dependent on the hardware or the global numpy configuration, and for efficiency it's best to avoid triggering floating-point errors to begin with. So instead of converting the floating-point array to integers, and fixing up the integer array after the fact, we want to replace the floating-point values *first*, and then convert to integers. --- wfdb/io/_signal.py | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/wfdb/io/_signal.py b/wfdb/io/_signal.py index 44185105..96a4ec81 100644 --- a/wfdb/io/_signal.py +++ b/wfdb/io/_signal.py @@ -539,8 +539,8 @@ def adc_inplace_1d(ch_p_signal, adc_gain, baseline, d_nan): np.multiply(ch_p_signal, adc_gain, ch_p_signal) np.add(ch_p_signal, baseline, ch_p_signal) np.round(ch_p_signal, 0, ch_p_signal) + ch_p_signal[ch_nanlocs] = d_nan ch_d_signal = ch_p_signal.astype(intdtype, copy=False) - ch_d_signal[ch_nanlocs] = d_nan return ch_d_signal # Convert a 2D physical signal array to digital. Note that the @@ -550,12 +550,11 @@ def adc_inplace_2d(p_signal): np.multiply(p_signal, self.adc_gain, p_signal) np.add(p_signal, self.baseline, p_signal) np.round(p_signal, 0, p_signal) - d_signal = p_signal.astype(intdtype, copy=False) - if nanlocs.any(): - for ch in range(d_signal.shape[1]): + for ch in range(p_signal.shape[1]): if nanlocs[:, ch].any(): - d_signal[nanlocs[:, ch], ch] = d_nans[ch] + p_signal[nanlocs[:, ch], ch] = d_nans[ch] + d_signal = p_signal.astype(intdtype, copy=False) return d_signal # Do inplace conversion and set relevant variables. From a9011638ed15b53608eb7ec33c3fd1f100a8ad3e Mon Sep 17 00:00:00 2001 From: Benjamin Moody Date: Tue, 2 Apr 2024 14:29:00 -0400 Subject: [PATCH 10/13] adc: rename variables for clarity. --- wfdb/io/_signal.py | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/wfdb/io/_signal.py b/wfdb/io/_signal.py index 96a4ec81..67bb6d68 100644 --- a/wfdb/io/_signal.py +++ b/wfdb/io/_signal.py @@ -577,7 +577,7 @@ def adc_inplace_2d(p_signal): # Return the variable else: if expanded: - d_signal = [] + e_d_signal = [] for ch, ch_p_signal in enumerate(self.e_p_signal): ch_d_signal = adc_inplace_1d( ch_p_signal.copy(), @@ -585,12 +585,11 @@ def adc_inplace_2d(p_signal): self.baseline[ch], d_nans[ch], ) - d_signal.append(ch_d_signal) + e_d_signal.append(ch_d_signal) + return e_d_signal else: - d_signal = adc_inplace_2d(self.p_signal.copy()) - - return d_signal + return adc_inplace_2d(self.p_signal.copy()) def dac(self, expanded=False, return_res=64, inplace=False): """ From 5a05f7ae088f5da625911f0fb061327603da1598 Mon Sep 17 00:00:00 2001 From: Benjamin Moody Date: Tue, 2 Apr 2024 17:43:56 -0400 Subject: [PATCH 11/13] test_physical_conversion: make tests more stringent. - Test that Record.adc works when n_sig is not set. (Previously, this didn't work with expanded=True.) - Test that Record.adc handles NaN by mapping it to the correct invalid-sample value. (Previously, this didn't work with expanded=False and inplace=True.) Use multiple formats to test that this takes the format into account. Furthermore, the previous code relied on implementation-defined behavior to handle NaN, which normally results in a RuntimeWarning. Within the test suite, we set the numpy error handling mode to "raise", so such implementation-defined conversions actually result in a FloatingPointError. --- tests/test_record.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/tests/test_record.py b/tests/test_record.py index 8d09e39d..3459897b 100644 --- a/tests/test_record.py +++ b/tests/test_record.py @@ -1053,19 +1053,20 @@ def test_physical_conversion(self): adc_gain = [1.0, 1234.567, 765.4321] baseline = [10, 20, -30] d_signal = np.repeat(np.arange(-100, 100), 3).reshape(-1, 3) + d_signal[5:10, :] = [-32768, -2048, -128] e_d_signal = list(d_signal.transpose()) - fmt = ["16", "16", "16"] + fmt = ["16", "212", "80"] # Test adding or subtracting a small offset (0.01 ADU) to check # that we correctly round to the nearest integer for offset in (0, -0.01, 0.01): p_signal = (d_signal + offset - baseline) / adc_gain + p_signal[5:10, :] = np.nan e_p_signal = list(p_signal.transpose()) # Test converting p_signal to d_signal record = wfdb.Record( - n_sig=n_sig, p_signal=p_signal.copy(), adc_gain=adc_gain, baseline=baseline, @@ -1081,7 +1082,6 @@ def test_physical_conversion(self): # Test converting e_p_signal to e_d_signal record = wfdb.Record( - n_sig=n_sig, e_p_signal=[s.copy() for s in e_p_signal], adc_gain=adc_gain, baseline=baseline, @@ -1108,7 +1108,7 @@ def test_physical_conversion(self): p_signal=p_signal, adc_gain=adc_gain, baseline=baseline, - fmt=["16", "16", "16"], + fmt=fmt, write_dir=self.temp_path, ) record = wfdb.rdrecord( From e013d5b49620bfc02a5fc528947f90d1e5d5134e Mon Sep 17 00:00:00 2001 From: Benjamin Moody Date: Thu, 18 Apr 2024 14:23:45 -0400 Subject: [PATCH 12/13] adc: optimize replacement of NaNs. When converting physical to digital sample arrays, we must replace NaN values with the appropriate invalid-sample sentinel value. To do this, we need to call np.isnan and use the result as a mask to replace entries in the output array. (Although the function np.nan_to_num also exists, it's less efficient: it literally does just this, but also handles infinities.) What we don't need to do is to call any() to check whether there are any true entries - that just means we're iterating through the same array three times rather than once. Furthermore, np.copyto can broadcast d_nans across the rows of p_signal, so all the channels can be handled at once. Also use copyto in adc_inplace_1d for consistency. --- wfdb/io/_signal.py | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/wfdb/io/_signal.py b/wfdb/io/_signal.py index 67bb6d68..843aeaa1 100644 --- a/wfdb/io/_signal.py +++ b/wfdb/io/_signal.py @@ -539,7 +539,7 @@ def adc_inplace_1d(ch_p_signal, adc_gain, baseline, d_nan): np.multiply(ch_p_signal, adc_gain, ch_p_signal) np.add(ch_p_signal, baseline, ch_p_signal) np.round(ch_p_signal, 0, ch_p_signal) - ch_p_signal[ch_nanlocs] = d_nan + np.copyto(ch_p_signal, d_nan, where=ch_nanlocs) ch_d_signal = ch_p_signal.astype(intdtype, copy=False) return ch_d_signal @@ -550,10 +550,7 @@ def adc_inplace_2d(p_signal): np.multiply(p_signal, self.adc_gain, p_signal) np.add(p_signal, self.baseline, p_signal) np.round(p_signal, 0, p_signal) - if nanlocs.any(): - for ch in range(p_signal.shape[1]): - if nanlocs[:, ch].any(): - p_signal[nanlocs[:, ch], ch] = d_nans[ch] + np.copyto(p_signal, d_nans, where=nanlocs) d_signal = p_signal.astype(intdtype, copy=False) return d_signal From 5b564076d30b9e1b4e4503dde3eb641c83f99e6f Mon Sep 17 00:00:00 2001 From: Benjamin Moody Date: Thu, 18 Apr 2024 14:47:40 -0400 Subject: [PATCH 13/13] adc: combine shared logic into one inner function. --- wfdb/io/_signal.py | 41 ++++++++++++++++++++--------------------- 1 file changed, 20 insertions(+), 21 deletions(-) diff --git a/wfdb/io/_signal.py b/wfdb/io/_signal.py index 843aeaa1..68ca57e4 100644 --- a/wfdb/io/_signal.py +++ b/wfdb/io/_signal.py @@ -532,25 +532,14 @@ def adc(self, expanded=False, inplace=False): # To do: choose the minimum return res needed intdtype = "int64" - # Convert a single physical channel to digital. Note that the - # input array is modified! - def adc_inplace_1d(ch_p_signal, adc_gain, baseline, d_nan): - ch_nanlocs = np.isnan(ch_p_signal) - np.multiply(ch_p_signal, adc_gain, ch_p_signal) - np.add(ch_p_signal, baseline, ch_p_signal) - np.round(ch_p_signal, 0, ch_p_signal) - np.copyto(ch_p_signal, d_nan, where=ch_nanlocs) - ch_d_signal = ch_p_signal.astype(intdtype, copy=False) - return ch_d_signal - - # Convert a 2D physical signal array to digital. Note that the - # input array is modified! - def adc_inplace_2d(p_signal): + # Convert a physical (1D or 2D) signal array to digital. Note that + # the input array is modified! + def adc_inplace(p_signal, adc_gain, baseline, d_nan): nanlocs = np.isnan(p_signal) - np.multiply(p_signal, self.adc_gain, p_signal) - np.add(p_signal, self.baseline, p_signal) + np.multiply(p_signal, adc_gain, p_signal) + np.add(p_signal, baseline, p_signal) np.round(p_signal, 0, p_signal) - np.copyto(p_signal, d_nans, where=nanlocs) + np.copyto(p_signal, d_nan, where=nanlocs) d_signal = p_signal.astype(intdtype, copy=False) return d_signal @@ -558,7 +547,7 @@ def adc_inplace_2d(p_signal): if inplace: if expanded: for ch, ch_p_signal in enumerate(self.e_p_signal): - ch_d_signal = adc_inplace_1d( + ch_d_signal = adc_inplace( ch_p_signal, self.adc_gain[ch], self.baseline[ch], @@ -568,7 +557,12 @@ def adc_inplace_2d(p_signal): self.e_d_signal = self.e_p_signal self.e_p_signal = None else: - self.d_signal = adc_inplace_2d(self.p_signal) + self.d_signal = adc_inplace( + self.p_signal, + self.adc_gain, + self.baseline, + d_nans, + ) self.p_signal = None # Return the variable @@ -576,7 +570,7 @@ def adc_inplace_2d(p_signal): if expanded: e_d_signal = [] for ch, ch_p_signal in enumerate(self.e_p_signal): - ch_d_signal = adc_inplace_1d( + ch_d_signal = adc_inplace( ch_p_signal.copy(), self.adc_gain[ch], self.baseline[ch], @@ -586,7 +580,12 @@ def adc_inplace_2d(p_signal): return e_d_signal else: - return adc_inplace_2d(self.p_signal.copy()) + return adc_inplace( + self.p_signal.copy(), + self.adc_gain, + self.baseline, + d_nans, + ) def dac(self, expanded=False, return_res=64, inplace=False): """