Skip to content

Commit f07bfff

Browse files
authored
Merge pull request MIT-LCP#419 from MIT-LCP/adc-fixes
Round sample values in physical-to-digital conversion
2 parents b1c3284 + 5caa60b commit f07bfff

File tree

2 files changed

+91
-1
lines changed

2 files changed

+91
-1
lines changed

tests/test_record.py

Lines changed: 84 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -971,6 +971,90 @@ def test_infer_sig_len(self):
971971

972972
assert record_2.__eq__(record)
973973

974+
def test_physical_conversion(self):
975+
n_sig = 3
976+
adc_gain = [1.0, 1234.567, 765.4321]
977+
baseline = [10, 20, -30]
978+
d_signal = np.repeat(np.arange(-100, 100), 3).reshape(-1, 3)
979+
e_d_signal = list(d_signal.transpose())
980+
fmt = ["16", "16", "16"]
981+
982+
# Test adding or subtracting a small offset (0.01 ADU) to check
983+
# that we correctly round to the nearest integer
984+
for offset in (0, -0.01, 0.01):
985+
p_signal = (d_signal + offset - baseline) / adc_gain
986+
e_p_signal = list(p_signal.transpose())
987+
988+
# Test converting p_signal to d_signal
989+
990+
record = wfdb.Record(
991+
n_sig=n_sig,
992+
p_signal=p_signal.copy(),
993+
adc_gain=adc_gain,
994+
baseline=baseline,
995+
fmt=fmt,
996+
)
997+
998+
d_signal_converted = record.adc(expanded=False, inplace=False)
999+
np.testing.assert_array_equal(d_signal_converted, d_signal)
1000+
1001+
record.adc(expanded=False, inplace=True)
1002+
np.testing.assert_array_equal(record.d_signal, d_signal)
1003+
1004+
# Test converting e_p_signal to e_d_signal
1005+
1006+
record = wfdb.Record(
1007+
n_sig=n_sig,
1008+
e_p_signal=[s.copy() for s in e_p_signal],
1009+
adc_gain=adc_gain,
1010+
baseline=baseline,
1011+
fmt=fmt,
1012+
)
1013+
1014+
e_d_signal_converted = record.adc(expanded=True, inplace=False)
1015+
self.assertEqual(len(e_d_signal_converted), n_sig)
1016+
for x, y in zip(e_d_signal_converted, e_d_signal):
1017+
np.testing.assert_array_equal(x, y)
1018+
1019+
record.adc(expanded=True, inplace=True)
1020+
self.assertEqual(len(record.e_d_signal), n_sig)
1021+
for x, y in zip(record.e_d_signal, e_d_signal):
1022+
np.testing.assert_array_equal(x, y)
1023+
1024+
# Test automatic conversion using wfdb.wrsamp()
1025+
1026+
wfdb.wrsamp(
1027+
"test_physical_conversion",
1028+
fs=1000,
1029+
sig_name=["X", "Y", "Z"],
1030+
units=["mV", "mV", "mV"],
1031+
p_signal=p_signal,
1032+
adc_gain=adc_gain,
1033+
baseline=baseline,
1034+
fmt=["16", "16", "16"],
1035+
)
1036+
record = wfdb.rdrecord("test_physical_conversion", physical=False)
1037+
np.testing.assert_array_equal(record.d_signal, d_signal)
1038+
1039+
record = wfdb.rdrecord("test_physical_conversion", physical=True)
1040+
for ch, gain in enumerate(adc_gain):
1041+
np.testing.assert_allclose(
1042+
record.p_signal[:, ch],
1043+
p_signal[:, ch],
1044+
rtol=0.0000001,
1045+
atol=(0.05 / gain),
1046+
)
1047+
1048+
@classmethod
1049+
def tearDownClass(cls):
1050+
writefiles = [
1051+
"test_physical_conversion.dat",
1052+
"test_physical_conversion.hea",
1053+
]
1054+
for file in writefiles:
1055+
if os.path.isfile(file):
1056+
os.remove(file)
1057+
9741058

9751059
class TestDownload(unittest.TestCase):
9761060
# Test that we can download records with no "dat" file

wfdb/io/_signal.py

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -544,8 +544,11 @@ def adc(self, expanded=False, inplace=False):
544544
self.e_p_signal[ch],
545545
)
546546
np.add(
547-
e_p_signal[ch], self.baseline[ch], self.e_p_signal[ch]
547+
self.e_p_signal[ch],
548+
self.baseline[ch],
549+
self.e_p_signal[ch],
548550
)
551+
np.round(self.e_p_signal[ch], 0, self.e_p_signal[ch])
549552
self.e_p_signal[ch] = self.e_p_signal[ch].astype(
550553
intdtype, copy=False
551554
)
@@ -556,6 +559,7 @@ def adc(self, expanded=False, inplace=False):
556559
nanlocs = np.isnan(self.p_signal)
557560
np.multiply(self.p_signal, self.adc_gain, self.p_signal)
558561
np.add(self.p_signal, self.baseline, self.p_signal)
562+
np.round(self.p_signal, 0, self.p_signal)
559563
self.p_signal = self.p_signal.astype(intdtype, copy=False)
560564
self.d_signal = self.p_signal
561565
self.p_signal = None
@@ -570,6 +574,7 @@ def adc(self, expanded=False, inplace=False):
570574
ch_d_signal = self.e_p_signal[ch].copy()
571575
np.multiply(ch_d_signal, self.adc_gain[ch], ch_d_signal)
572576
np.add(ch_d_signal, self.baseline[ch], ch_d_signal)
577+
np.round(ch_d_signal, 0, ch_d_signal)
573578
ch_d_signal = ch_d_signal.astype(intdtype, copy=False)
574579
ch_d_signal[ch_nanlocs] = d_nans[ch]
575580
d_signal.append(ch_d_signal)
@@ -580,6 +585,7 @@ def adc(self, expanded=False, inplace=False):
580585
d_signal = self.p_signal.copy()
581586
np.multiply(d_signal, self.adc_gain, d_signal)
582587
np.add(d_signal, self.baseline, d_signal)
588+
np.round(d_signal, 0, d_signal)
583589
d_signal = d_signal.astype(intdtype, copy=False)
584590

585591
if nanlocs.any():

0 commit comments

Comments
 (0)