From f6e5179218b00d79669da2bcb5f158a115f82eda Mon Sep 17 00:00:00 2001 From: Benjamin Moody Date: Thu, 23 Sep 2021 16:55:00 -0400 Subject: [PATCH 1/8] Add .gitattributes file. Mark Python source files as "diff=python" to show the correct function names, and mark various types of binary files as binary to avoid attempting to diff/merge them. --- .gitattributes | 14 ++++++++++++++ 1 file changed, 14 insertions(+) create mode 100644 .gitattributes diff --git a/.gitattributes b/.gitattributes new file mode 100644 index 00000000..22ece28c --- /dev/null +++ b/.gitattributes @@ -0,0 +1,14 @@ +*.py diff=python + +*.anI binary +*.atr binary +*.d[0-9] binary +*.dat binary +*.edf binary +*.gz binary +*.mat binary +*.qrs binary +*.wabp binary +*.wav binary +*.wqrs binary +*.xyz binary From f2b8ab6de5c30409566e49885729064faa94d43a Mon Sep 17 00:00:00 2001 From: Benjamin Moody Date: Thu, 23 Sep 2021 13:18:50 -0400 Subject: [PATCH 2/8] _rd_segment: reformat for readability/maintainability. In each branch, the calls to _rd_dat_signals with and without 'no_file=True, sig_data=sig_data' were otherwise identical. sig_data is ignored if no_file is false, so this is equivalent to simply passing 'no_file=no_file, sig_data=sig_data'. Furthermore, since _rd_dat_signals takes a huge number of arguments, convert all of them to keyword style to avoid confusion. --- wfdb/io/_signal.py | 52 +++++++++++++++++++++++++++------------------- 1 file changed, 31 insertions(+), 21 deletions(-) diff --git a/wfdb/io/_signal.py b/wfdb/io/_signal.py index 43e587c9..a09d4368 100644 --- a/wfdb/io/_signal.py +++ b/wfdb/io/_signal.py @@ -997,17 +997,22 @@ def _rd_segment(file_name, dir_name, pn_dir, fmt, n_sig, sig_len, byte_offset, # Read each wanted dat file and store signals for fn in w_file_name: - if no_file: - signals[:, out_dat_channel[fn]] = _rd_dat_signals(fn, dir_name, - pn_dir, w_fmt[fn], len(datchannel[fn]), sig_len, - w_byte_offset[fn], w_samps_per_frame[fn], w_skew[fn], - sampfrom, sampto, smooth_frames, no_file=True, - sig_data=sig_data)[:, r_w_channel[fn]] - else: - signals[:, out_dat_channel[fn]] = _rd_dat_signals(fn, dir_name, - pn_dir, w_fmt[fn], len(datchannel[fn]), sig_len, - w_byte_offset[fn], w_samps_per_frame[fn], w_skew[fn], - sampfrom, sampto, smooth_frames)[:, r_w_channel[fn]] + datsignals = _rd_dat_signals( + file_name=fn, + dir_name=dir_name, + pn_dir=pn_dir, + fmt=w_fmt[fn], + n_sig=len(datchannel[fn]), + sig_len=sig_len, + byte_offset=w_byte_offset[fn], + samps_per_frame=w_samps_per_frame[fn], + skew=w_skew[fn], + sampfrom=sampfrom, + sampto=sampto, + smooth_frames=smooth_frames, + no_file=no_file, + sig_data=sig_data) + signals[:, out_dat_channel[fn]] = datsignals[:, r_w_channel[fn]] # Return each sample in signals with multiple samples/frame, without smoothing. # Return a list of numpy arrays for each signal. @@ -1016,16 +1021,21 @@ def _rd_segment(file_name, dir_name, pn_dir, fmt, n_sig, sig_len, byte_offset, for fn in w_file_name: # Get the list of all signals contained in the dat file - if no_file: - datsignals = _rd_dat_signals(fn, dir_name, pn_dir, w_fmt[fn], - len(datchannel[fn]), sig_len, w_byte_offset[fn], - w_samps_per_frame[fn], w_skew[fn], sampfrom, sampto, - smooth_frames, no_file=True, sig_data=sig_data) - else: - datsignals = _rd_dat_signals(fn, dir_name, pn_dir, w_fmt[fn], - len(datchannel[fn]), sig_len, w_byte_offset[fn], - w_samps_per_frame[fn], w_skew[fn], sampfrom, sampto, - smooth_frames) + datsignals = _rd_dat_signals( + file_name=fn, + dir_name=dir_name, + pn_dir=pn_dir, + fmt=w_fmt[fn], + n_sig=len(datchannel[fn]), + sig_len=sig_len, + byte_offset=w_byte_offset[fn], + samps_per_frame=w_samps_per_frame[fn], + skew=w_skew[fn], + sampfrom=sampfrom, + sampto=sampto, + smooth_frames=smooth_frames, + no_file=no_file, + sig_data=sig_data) # Copy over the wanted signals for cn in range(len(out_dat_channel[fn])): From d2406cfadc042cf3b099e3de1abf6ee975207a62 Mon Sep 17 00:00:00 2001 From: Benjamin Moody Date: Thu, 23 Sep 2021 13:25:24 -0400 Subject: [PATCH 3/8] rdrecord: reformat for readability/maintainability. All four of the calls to _rd_segment were identical apart from the 'no_file' and 'sig_data' arguments. Rearrange the code so there is only one call to _rd_segment, to avoid redundancy. Furthermore, since _rd_segment takes a huge number of arguments, convert all of them to keyword style to avoid confusion. --- wfdb/io/record.py | 83 +++++++++++++++-------------------------------- 1 file changed, 27 insertions(+), 56 deletions(-) diff --git a/wfdb/io/record.py b/wfdb/io/record.py index a53ad4fa..c2c90c43 100644 --- a/wfdb/io/record.py +++ b/wfdb/io/record.py @@ -3507,39 +3507,37 @@ def rdrecord(record_name, sampfrom=0, sampto=None, channels=None, # A single segment record elif isinstance(record, Record): + if record_name.endswith('.edf') or record_name.endswith('.wav'): + no_file = True + sig_data = record.d_signal + else: + no_file = False + sig_data = None + + signals = _signal._rd_segment( + file_name=record.file_name, + dir_name=dir_name, + pn_dir=pn_dir, + fmt=record.fmt, + n_sig=record.n_sig, + sig_len=record.sig_len, + byte_offset=record.byte_offset, + samps_per_frame=record.samps_per_frame, + skew=record.skew, + sampfrom=sampfrom, + sampto=sampto, + channels=channels, + smooth_frames=smooth_frames, + ignore_skew=ignore_skew, + no_file=no_file, + sig_data=sig_data, + return_res=return_res) # Only 1 sample/frame, or frames are smoothed. Return uniform numpy array if smooth_frames or max([record.samps_per_frame[c] for c in channels]) == 1: # Read signals from the associated dat files that contain # wanted channels - if record_name.endswith('.edf') or record_name.endswith('.wav'): - record.d_signal = _signal._rd_segment(record.file_name, - dir_name, pn_dir, - record.fmt, - record.n_sig, - record.sig_len, - record.byte_offset, - record.samps_per_frame, - record.skew, sampfrom, - sampto, channels, - smooth_frames, - ignore_skew, - no_file=True, - sig_data=record.d_signal, - return_res=return_res) - else: - record.d_signal = _signal._rd_segment(record.file_name, - dir_name, pn_dir, - record.fmt, - record.n_sig, - record.sig_len, - record.byte_offset, - record.samps_per_frame, - record.skew, sampfrom, - sampto, channels, - smooth_frames, - ignore_skew, - return_res=return_res) + record.d_signal = signals # Arrange/edit the object fields to reflect user channel # and/or signal range input @@ -3552,34 +3550,7 @@ def rdrecord(record_name, sampfrom=0, sampto=None, channels=None, # Return each sample of the signals with multiple samples per frame else: - if record_name.endswith('.edf') or record_name.endswith('.wav'): - record.e_d_signal = _signal._rd_segment(record.file_name, - dir_name, pn_dir, - record.fmt, - record.n_sig, - record.sig_len, - record.byte_offset, - record.samps_per_frame, - record.skew, sampfrom, - sampto, channels, - smooth_frames, - ignore_skew, - no_file=True, - sig_data=record.d_signal, - return_res=return_res) - else: - record.e_d_signal = _signal._rd_segment(record.file_name, - dir_name, pn_dir, - record.fmt, - record.n_sig, - record.sig_len, - record.byte_offset, - record.samps_per_frame, - record.skew, sampfrom, - sampto, channels, - smooth_frames, - ignore_skew, - return_res=return_res) + record.e_d_signal = signals # Arrange/edit the object fields to reflect user channel # and/or signal range input From 4d18c53a7f81606344f37dec58a588ebbc885488 Mon Sep 17 00:00:00 2001 From: Benjamin Moody Date: Thu, 23 Sep 2021 13:40:39 -0400 Subject: [PATCH 4/8] _rd_segment: add init_value argument. This should be a list containing the initial sample value for each signal; this is required in order to correctly read format-8 dat files. --- wfdb/io/_signal.py | 4 +++- wfdb/io/record.py | 1 + 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/wfdb/io/_signal.py b/wfdb/io/_signal.py index a09d4368..69052fb5 100644 --- a/wfdb/io/_signal.py +++ b/wfdb/io/_signal.py @@ -865,7 +865,7 @@ def smooth_frames(self, sigtype='physical'): def _rd_segment(file_name, dir_name, pn_dir, fmt, n_sig, sig_len, byte_offset, - samps_per_frame, skew, sampfrom, sampto, channels, + samps_per_frame, skew, init_value, sampfrom, sampto, channels, smooth_frames, ignore_skew, no_file=False, sig_data=None, return_res=64): """ Read the digital samples from a single segment record's associated @@ -893,6 +893,8 @@ def _rd_segment(file_name, dir_name, pn_dir, fmt, n_sig, sig_len, byte_offset, The samples/frame for each signal of the dat file. skew : list The skew for the signals of the dat file. + init_value : list + The initial value for each signal of the dat file. sampfrom : int The starting sample number to be read from the signals. sampto : int diff --git a/wfdb/io/record.py b/wfdb/io/record.py index c2c90c43..077c03b5 100644 --- a/wfdb/io/record.py +++ b/wfdb/io/record.py @@ -3524,6 +3524,7 @@ def rdrecord(record_name, sampfrom=0, sampto=None, channels=None, byte_offset=record.byte_offset, samps_per_frame=record.samps_per_frame, skew=record.skew, + init_value=record.init_value, sampfrom=sampfrom, sampto=sampto, channels=channels, From ce1834da8aacbc72dc1f4e57057c73e386ca8adf Mon Sep 17 00:00:00 2001 From: Benjamin Moody Date: Thu, 23 Sep 2021 13:41:57 -0400 Subject: [PATCH 5/8] _rd_dat_signals: add init_value argument. This should be a list containing the initial sample value for each signal; this is required in order to correctly read format-8 dat files. --- wfdb/io/_signal.py | 14 ++++++++++++-- 1 file changed, 12 insertions(+), 2 deletions(-) diff --git a/wfdb/io/_signal.py b/wfdb/io/_signal.py index 69052fb5..95ed304b 100644 --- a/wfdb/io/_signal.py +++ b/wfdb/io/_signal.py @@ -941,6 +941,7 @@ def _rd_segment(file_name, dir_name, pn_dir, fmt, n_sig, sig_len, byte_offset, byte_offset = byte_offset[:] samps_per_frame = samps_per_frame[:] skew = skew[:] + init_value = init_value[:] # Set defaults for empty fields for i in range(n_sig): @@ -950,6 +951,8 @@ def _rd_segment(file_name, dir_name, pn_dir, fmt, n_sig, sig_len, byte_offset, samps_per_frame[i] = 1 if skew[i] == None: skew[i] = 0 + if init_value[i] == None: + init_value[i] = 0 # If skew is to be ignored, set all to 0 if ignore_skew: @@ -966,6 +969,7 @@ def _rd_segment(file_name, dir_name, pn_dir, fmt, n_sig, sig_len, byte_offset, w_byte_offset = {} # one scalar per dat file w_samps_per_frame = {} # one list per dat file w_skew = {} # one list per dat file + w_init_value = {} # one list per dat file w_channel = {} # one list per dat file for fn in file_name: @@ -979,6 +983,7 @@ def _rd_segment(file_name, dir_name, pn_dir, fmt, n_sig, sig_len, byte_offset, w_byte_offset[fn] = byte_offset[datchannel[fn][0]] w_samps_per_frame[fn] = [samps_per_frame[c] for c in datchannel[fn]] w_skew[fn] = [skew[c] for c in datchannel[fn]] + w_init_value[fn] = [init_value[c] for c in datchannel[fn]] w_channel[fn] = idc # Wanted dat channels, relative to the dat file itself @@ -1009,6 +1014,7 @@ def _rd_segment(file_name, dir_name, pn_dir, fmt, n_sig, sig_len, byte_offset, byte_offset=w_byte_offset[fn], samps_per_frame=w_samps_per_frame[fn], skew=w_skew[fn], + init_value=w_init_value[fn], sampfrom=sampfrom, sampto=sampto, smooth_frames=smooth_frames, @@ -1033,6 +1039,7 @@ def _rd_segment(file_name, dir_name, pn_dir, fmt, n_sig, sig_len, byte_offset, byte_offset=w_byte_offset[fn], samps_per_frame=w_samps_per_frame[fn], skew=w_skew[fn], + init_value=w_init_value[fn], sampfrom=sampfrom, sampto=sampto, smooth_frames=smooth_frames, @@ -1047,8 +1054,9 @@ def _rd_segment(file_name, dir_name, pn_dir, fmt, n_sig, sig_len, byte_offset, def _rd_dat_signals(file_name, dir_name, pn_dir, fmt, n_sig, sig_len, - byte_offset, samps_per_frame, skew, sampfrom, sampto, - smooth_frames, no_file=False, sig_data=None): + byte_offset, samps_per_frame, skew, init_value, + sampfrom, sampto, smooth_frames, + no_file=False, sig_data=None): """ Read all signals from a WFDB dat file. @@ -1074,6 +1082,8 @@ def _rd_dat_signals(file_name, dir_name, pn_dir, fmt, n_sig, sig_len, The samples/frame for each signal of the dat file. skew : list The skew for the signals of the dat file. + init_value : list + The initial value for each signal of the dat file. sampfrom : int The starting sample number to be read from the signals. sampto : int From 6bede91f2fbea67fa84bb23dd5fa7d25e6a186ab Mon Sep 17 00:00:00 2001 From: Benjamin Moody Date: Thu, 23 Sep 2021 13:47:03 -0400 Subject: [PATCH 6/8] _rd_dat_signals: fix conversion of format 8. In signal format 8, each sample is stored as an 8-bit signed difference from the previous sample. This means that after reading the raw byte values, they must be translated to absolute sample values by calling cumsum() and adding the initial value. --- wfdb/io/_signal.py | 26 ++++++++++++++++++++++++++ 1 file changed, 26 insertions(+) diff --git a/wfdb/io/_signal.py b/wfdb/io/_signal.py index 95ed304b..9fb55c88 100644 --- a/wfdb/io/_signal.py +++ b/wfdb/io/_signal.py @@ -1182,6 +1182,32 @@ def _rd_dat_signals(file_name, dir_name, pn_dir, fmt, n_sig, sig_len, elif fmt == '160': sig_data = (sig_data.astype('int32') - 32768).astype('int16') + # For format 8, convert sample differences to absolute samples. Note + # that if sampfrom is not 0, the results will be wrong, since we can't + # know the starting value without reading the entire record from the + # beginning - an inherent limitation of the format, and the use of + # format 8 is discouraged for this reason! However, the following is + # consistent with the behavior of the WFDB library: the initial value + # specified by the header file is used as the starting sample value, + # regardless of where in the record we begin reading. Therefore, the + # following should give the same results as rdsamp. + if fmt == '8': + dif_frames = sig_data.reshape(-1, tsamps_per_frame) + abs_frames = np.empty(dif_frames.shape, dtype='int32') + ch_start = 0 + for ch in range(n_sig): + ch_end = ch_start + samps_per_frame[ch] + # Extract sample differences as a 2D array + ch_dif_signal = dif_frames[:, ch_start:ch_end] + # Convert to a 1D array of absolute samples + ch_abs_signal = ch_dif_signal.cumsum(dtype=abs_frames.dtype) + ch_abs_signal += init_value[ch] + # Transfer to the output array + ch_abs_signal = ch_abs_signal.reshape(ch_dif_signal.shape) + abs_frames[:, ch_start:ch_end] = ch_abs_signal + ch_start = ch_end + sig_data = abs_frames.reshape(-1) + # At this point, dtype of sig_data is the minimum integer format # required for storing the final digital samples. From 8b3d3077e172561fd71f290e78d43e0ef1276713 Mon Sep 17 00:00:00 2001 From: Benjamin Moody Date: Thu, 23 Sep 2021 15:52:56 -0400 Subject: [PATCH 7/8] _blocks_to_samples: handle partial blocks in formats 310/311. In formats 310 and 311, each block of three samples is written as four bytes. _rd_dat_signals will retrieve the minimum range of bytes (as determined by _dat_read_params and _required_byte_num) that are needed in order to decode the desired samples; thus, the data passed to _blocks_to_samples may include an incomplete block at the end. The previous implementation of _blocks_to_samples was meant to pad the input data to a multiple of four bytes. However, this logic was wrong: added_samps was always set to zero, so the intended extra bytes were not appended, and (if the lack of extra bytes didn't cause an error) the wrong number of samples was returned to the caller. In fact, the subsequent statements for decoding blocks into samples already worked correctly for an unpadded input array (since each input slice is correctly truncated to the length of the output slice.) So remove the padding logic entirely. --- wfdb/io/_signal.py | 26 -------------------------- 1 file changed, 26 deletions(-) diff --git a/wfdb/io/_signal.py b/wfdb/io/_signal.py index 9fb55c88..a7f124fb 100644 --- a/wfdb/io/_signal.py +++ b/wfdb/io/_signal.py @@ -1520,14 +1520,6 @@ def _blocks_to_samples(sig_data, n_samp, fmt): sig[sig > 2047] -= 4096 elif fmt == '310': - # Easier to process when dealing with whole blocks - if n_samp % 3: - n_samp = upround(n_samp,3) - added_samps = n_samp % 3 - sig_data = np.append(sig_data, np.zeros(added_samps, dtype='uint8')) - else: - added_samps = 0 - sig_data = sig_data.astype('int16') sig = np.zeros(n_samp, dtype='int16') @@ -1539,24 +1531,11 @@ def _blocks_to_samples(sig_data, n_samp, fmt): # Third signal is 5 msb of second byte and 5 msb of forth byte sig[2::3] = np.bitwise_and((sig_data[1::4] >> 3), 0x1f)[0:len(sig[2::3])] + 32 * np.bitwise_and(sig_data[3::4] >> 3, 0x1f)[0:len(sig[2::3])] - # Remove trailing samples read within the byte block if - # originally not 3n sampled - if added_samps: - sig = sig[:-added_samps] - # Loaded values as un_signed. Convert to 2's complement form: # values > 2^9-1 are negative. sig[sig > 511] -= 1024 elif fmt == '311': - # Easier to process when dealing with whole blocks - if n_samp % 3: - n_samp = upround(n_samp,3) - added_samps = n_samp % 3 - sig_data = np.append(sig_data, np.zeros(added_samps, dtype='uint8')) - else: - added_samps = 0 - sig_data = sig_data.astype('int16') sig = np.zeros(n_samp, dtype='int16') @@ -1568,11 +1547,6 @@ def _blocks_to_samples(sig_data, n_samp, fmt): # Third sample is 4 msb of third byte and 6 msb of forth byte sig[2::3] = (sig_data[2::4] >> 4)[0:len(sig[2::3])] + 16 * np.bitwise_and(sig_data[3::4], 0x7f)[0:len(sig[2::3])] - # Remove trailing samples read within the byte block if - # originally not 3n sampled - if added_samps: - sig = sig[:-added_samps] - # Loaded values as un_signed. Convert to 2's complement form. # Values > 2^9-1 are negative. sig[sig > 511] -= 1024 From 14048ddc077f322171b4139b498c9dcc82e7cc41 Mon Sep 17 00:00:00 2001 From: Benjamin Moody Date: Thu, 23 Sep 2021 16:14:50 -0400 Subject: [PATCH 8/8] Add a test case for all WFDB binary signal formats. The record "binformats" contains one signal in each of the ten WFDB binary formats (8, 16, 61, 80, 160, 212, 310, 311, 24, and 32.) In this record, sample j of signal i is equal to: (i + 16843019 * j) % ((1 << adcres) - 1) + 1 - (1 << (adcres - 1))) Note that the length of the record is 499 samples, so each of the bit-packed data files ends with an incomplete data block. Use this record to test that it is possible to read all of the formats correctly, including when we skip one or two samples from the start and/or end of the record. (Skipping samples is expected to give incorrect results for format 8, so that signal is not required to match.) We do not test writing, since not all formats are currently supported by wr_dat_file. --- sample-data/binformats.d0 | Bin 0 -> 499 bytes sample-data/binformats.d1 | Bin 0 -> 998 bytes sample-data/binformats.d2 | Bin 0 -> 998 bytes sample-data/binformats.d3 | 3 +++ sample-data/binformats.d4 | Bin 0 -> 998 bytes sample-data/binformats.d5 | Bin 0 -> 749 bytes sample-data/binformats.d6 | Bin 0 -> 666 bytes sample-data/binformats.d7 | Bin 0 -> 666 bytes sample-data/binformats.d8 | Bin 0 -> 1497 bytes sample-data/binformats.d9 | Bin 0 -> 1996 bytes sample-data/binformats.hea | 11 +++++++++++ tests/target-output/record-1f.gz | Bin 0 -> 15914 bytes tests/test_record.py | 29 +++++++++++++++++++++++++++++ 13 files changed, 43 insertions(+) create mode 100644 sample-data/binformats.d0 create mode 100644 sample-data/binformats.d1 create mode 100644 sample-data/binformats.d2 create mode 100644 sample-data/binformats.d3 create mode 100644 sample-data/binformats.d4 create mode 100644 sample-data/binformats.d5 create mode 100644 sample-data/binformats.d6 create mode 100644 sample-data/binformats.d7 create mode 100644 sample-data/binformats.d8 create mode 100644 sample-data/binformats.d9 create mode 100644 sample-data/binformats.hea create mode 100644 tests/target-output/record-1f.gz diff --git a/sample-data/binformats.d0 b/sample-data/binformats.d0 new file mode 100644 index 0000000000000000000000000000000000000000..51bd9887cc1e40f69e47997ab1482afc4e37e689 GIT binary patch literal 499 zcmZRmM*$5$pbQrQ@fsSuV0;L7EtCu4HK0nuxKPPVh%A)Hk5wmB(uPt=YFR=pbLyc6 Oq52EzO=?*}jyV9+E%I&v literal 0 HcmV?d00001 diff --git a/sample-data/binformats.d1 b/sample-data/binformats.d1 new file mode 100644 index 0000000000000000000000000000000000000000..d4e87af08d9fdbbdea5b057b5bbdb720d0ce57c2 GIT binary patch literal 998 zcmV`EX!H&b4#iqxy$-c|V&DPK2(eBgw)dtuS+8*30-ZtPu z;!fmR=4R+}>VE8s?w0VP@~-r{_Qv?q`riEN{`LR@0}cfm2PO$J3qB1>4^|Ok6K)lH z7ls*<8=f7iAGRUFBhDq-C*~>gEB-ACFcvZ*G%hweI7T{BJYGI(Kz2fcM2<$9NTy1% zOukOaP}Wl7RPI*#SO!}XT^?U5VK!qzWlm>WX=ZD3ZGLZxah7wUb*^{1dB%IvecpfT zf%k(0h7X7viYJUSjz5q~l2?>tmT#DQnunZ|o}ZwrqPL{Orq8I`s^_fpuK%zLvlq1^ zw=cOnyGOlKzhA*?!*|7l$B)UI%csq=&%e>j)7RDG*YDZ;+Xvng;2+{E}T$C@PG1)^q2Od_^vNPNz__QomHo zR@YeLTJK!@UI$?lV;^NJXE$j>Yfo)kZ)b6HbANSNxB~?ojYt@@VvQ z_JH_|`k4Hr{;&YN0>}i^2H*(n3iu2J4-gR?6DSol7eEeJQzMCKrli)L`X(dNMK5AOn6R&P>@obRH#<8SioA$T-aXZ zVDMu6WC&*zX&`GXZ8&d4aZqzyb!c~Vd4PM2eVBixfw6`z}qH&~srirMPs-di{uDP(qveC5Nw&}R_x&gfnzZt(~;Gk*QwdH+riz<-`U~i>=(g@Hz5D^ilR*_-Xoe{DJZy|9lb2)WIcTss=dsR*DDF6Tf literal 0 HcmV?d00001 diff --git a/sample-data/binformats.d2 b/sample-data/binformats.d2 new file mode 100644 index 0000000000000000000000000000000000000000..fc10891e2b3148be096db2a8a4a9ae22c7339ca1 GIT binary patch literal 998 zcmVj3uy0|gHU8wn>1GYvlvOA%KSV-;@~ zdl`otlO3NQt0A`|!zIrr+bQQO^DX}{3o;ipBQ`HMJ32=^Q$AlnYeIKKgGP@?n@Xol zvrfNI%Tm`=<5urj`&$QH6JH-;D`PihLuXHETWe=+b8mlfi*uKCqj#@)yL-od(|_NA z?1T7*1c(rd9E>QAG>|}&Oq5ubWSDT8e4L1$l%SxZtfaW6#Hi4!+^p!X^sxZ547C`y zB)KrVJiSQ2RKZ}wY{huTgvpT0oXx1uw9&xR%+=V}<0J=1Pc%i91kcFG!sA-Ocz)gWE*fDd>@D* zlp~-etS7iB#4FG(+%M=c^fUlA3^*7%Bs?%aJU~c7R77A#Y)E)YgiMf5oKUDzv{b-W z%vji3wOYYk&0g7H zA}FEpIt-MRQSgU3Y1Db$fw*jenVerh~GDzK6<*){Eke?vMJC29y$( z9+)bcHk?A9PM}(%W~6eaeyED7maL+#uCThY#F|{^|LZhv-;IWes*8FTbp~ '5CQ_m{$2@N\jx!/=KYgu,:HVdr )7ESao} +&4BP^lz#1?M[iw .LZhv-;IWes*8FTbp~ '5CQ_m{$2@N\jx!/=KYgu,:HVdr )7ESao} +&4BP^lz#1?M[ \ No newline at end of file diff --git a/sample-data/binformats.d4 b/sample-data/binformats.d4 new file mode 100644 index 0000000000000000000000000000000000000000..db8f5b004e54184fb409a9d337b13b3646d97685 GIT binary patch literal 998 zcmV3|S6k5OETH6p0p<7@-=i9JwCFAkiY-B1peA?Sb!&@tO0b^|AN8`N{j${o()a z0r~?51`-G!3MvdX4nh!45?T~y7IGMV8j2j29-<(wBDy5TCekS0D(WouE&?$QGa5A} zH!?XsJ4!uPKVm^{LwZGqN0LdNOR7z_Pr^~oQ`%MLSMpi@TMAwlU?O5JWIAR>Xi{ok zY-(!OXwi>u5x-z^zzDmGW!eYd3#(Kzx%96~U&Z^M1(!$iv z*4o(S+Vb4~-U{Is<09oQ=Q`;|>r(Ar?`rXO^Mdt`_nP^p`?CGM|H}c_1LFnn2m1*J z3=<9?5GxWl6hjtI7+V@=9CIFjAd4cGB%>y;D7z}hEYmLEFzYh+Gy^veIU74CJu^Q) zK}$ndMPo;ANqb9&O_NWbQL9t8Rl`@$S=(FZUGrc6VGCpzW+P}XYCCL4Zc}hya%*&V zc7u42dYgQwezSnTg3E;0hU19uiu;TQj}ws}lPi@smqVFPn_Hb{pL3yqql=}Nr=zK_ ztGlhouhX&Lv+K3@w*$Hlyc@nJz%#->#7o9k$YaWH%zMs<(38@i)T`FF*u&b-+}qyg z;Pc}D35 z7AzPz8blmW9$X-3B6K8xCX6VUDx@s1F1#?vGSoESHtaa~Is`osKO8|QLo`J|M@&gr zOJq%OPkd2`QkRD}?cc12>Ceww^)lm4&E5a$C!A3J4NbBBa{I5X|U81R!NRSci3ysjbP&1S8mTr?%WjJ8ros>2`Y{$VR8DGkhNGCj+q(A??Jr9YXM-W1yF=IWbq_3< zNMyQzoZPt7{_qSTC`3bGd}Wxlhtci12_@V?UH*BM5V6fN=>=sWJ%?I$sgkbC%I5t7 z9uPWKGIEYos>XKW|EL=`%TsUdi>D96-y!=KR5MR@YlxVnz`ES{>=ZB$OlTp5phLXa zW%Lo4ElIj%f!v+B{?+jy4JkuKVP$=phqltG?h4!{LjGQQ5SFqs&gx_aB8NX)sdtmh zugmS{0}vlOGFNj{kE?da@(x2PMrC4thnu&l)9>30C;mfU5PO$0v(HrP2Ztm; zsa$x=l(6m0=pO_iGCWvRbdYwe$(ZE<%N;rGRdElEsUgMTL;V?cHBy*vin^x4+}`>Q z7BV4DYD0#iWxm>n_Y=A=OWbFJ{-3)L*YiRTD`iJxhk%@^xYWz=4E`uY5MX>Vn6y;T z?1l*?sX<-Kd6n(4%@64XG9f)wS#@@iu9(W^$^stkI#v&Ijv=bXL*o8*8aJ3zZ@P=8 f+{54g`xhZIPeW^mWuw1`-1xZ^Fx*UN{)C_aZ6b8! literal 0 HcmV?d00001 diff --git a/sample-data/binformats.d6 b/sample-data/binformats.d6 new file mode 100644 index 0000000000000000000000000000000000000000..29232e669863d2a3f7a39655cd2ef009c208d909 GIT binary patch literal 666 zcmV;L0%iRU?8GiIeCRe-3=BVdoG?G0EL2Crym&|EOq@>&-M~*SZRA%{{RmKYjVxH2 z9Z+Dtt#n}GJ(y?)&b(+UUf^&}?gVjiekggC7EFPpxG1SvS z!T8f=Q6Aii;X>T5ac1Pw0gCMQk*@3-BGU9evi9_1LK^*s(mwsFVqyi(@`eTSf~pM` z6V4Snqw*DAGZr3#!#Wv^GDaL|RA4)P6_o zWTHeNO zzIK7+PMV1b-@b_~Z{m?q{|1tDk1Cp&A5NmYuX3W`KbEQl$hxX1Sl+Ts=mN8FcqY4) z2uj1am~O+|D3Z$nxwgwBN!rs$+5XdPX(Zf``AFQfiEQN98IbJ!skH1MI@t6?%KY?b zS|I(5>O}ppdT0gI3XBc-ny?KVE7TQ0yZ9AkOB^1E+dv+yYh)(S`-mp=i>xjh9MCpC ztn@ZwJQzQQ%sfA;Twq7e?1V@1e5g+r4a`?Mo$yy)EfiRSy*OB=OW%Hj_hz+9uRqct~7a~Mp%Ky)_j5LW}t}<_r!@Zhv<=37Yv$ur!bnHH&mj+$9SUV zSDdN};J~UbaOAR500_HwkSx2JAW*};uyn)XK$y!0(Y(tlVc^qF@dng#fhydV5l-Z~ zp>pKjF_!EC!n*7xQr`4R;sW(h}bvS(D*p&7#TYJU^zPusaZTA@p(NmIhsC1fx16d$=U#D5&HppSsMe4 zp*sYg>01S`F?2S= zO5YNRlK&H%+8-6IA}|)eYET!^vTzvV`j8p*La`bLi_sey)A1ZD8xkHpV=^C4t70Hw z^MWCBJEJ0ngTo`2%i|=e69gu^TO=pVqeLj*>treMGlePxc%>^9z{M;l2<9z0PzEnc zm?kh?;6^cTC}%Q(aECLKxTiFw1jseFOz1Yrln6N6+$cHiBuP5|Y-u|YwTV0<{i!`Q zMan)$jp{#H)d~S?9V-HTWlIB(t!o6K^@|0wJ*)=Bh0F)n&Fl#26%7jeT`da^rcDeW z?rjY+HjWNNey$Hz#?BCE4(}0qRu2=5o-Y)h=1&!{E?^eIc7PYuzMvT92EiHlPT?8~ zmjN6c-yt0?Cqf=TZ(<)(w~`=a|FR)=N7EvRkMkp%*ApeIA2cSuXH+N9uXHHl_mnC0 zKeZ|bgw-n;%=IiP6c#Q#TsALGq*gFs>~=A7G?y}je77@}#Md;a5coB^SQt0Vpg1_* z=vX=MFnKxxd6_#D!MQvp3EDk4QTjegnHm6G;W`0sDO&=7aeD)jxtj!}0lWpa0bIZg AH2?qr literal 0 HcmV?d00001 diff --git a/sample-data/binformats.d8 b/sample-data/binformats.d8 new file mode 100644 index 0000000000000000000000000000000000000000..63d750d46ba0dd843ab2a1404c713e0b44be67a8 GIT binary patch literal 1497 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 literal 0 HcmV?d00001 diff --git a/sample-data/binformats.hea b/sample-data/binformats.hea new file mode 100644 index 00000000..66a498ec --- /dev/null +++ b/sample-data/binformats.hea @@ -0,0 +1,11 @@ +binformats 10 200 499 +binformats.d0 8 200/mV 12 0 -2047 -31143 0 sig 0, fmt 8 +binformats.d1 16 200/mV 16 0 -32766 -750 0 sig 1, fmt 16 +binformats.d2 61 200/mV 16 0 -32765 -251 0 sig 2, fmt 61 +binformats.d3 80 200/mV 8 0 -124 -517 0 sig 3, fmt 80 +binformats.d4 160 200/mV 16 0 -32763 747 0 sig 4, fmt 160 +binformats.d5 212 200/mV 12 0 -2042 -6824 0 sig 5, fmt 212 +binformats.d6 310 200/mV 10 0 -505 -1621 0 sig 6, fmt 310 +binformats.d7 311 200/mV 10 0 -504 -2145 0 sig 7, fmt 311 +binformats.d8 24 200/mV 24 0 -8388599 11715 0 sig 8, fmt 24 +binformats.d9 32 200/mV 32 0 -2147483638 19035 0 sig 9, fmt 32 diff --git a/tests/target-output/record-1f.gz b/tests/target-output/record-1f.gz new file mode 100644 index 0000000000000000000000000000000000000000..674baad3f959e9792101e1ed2762d43197ef9bc3 GIT binary patch literal 15914 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 record-1f.gz + """ + record = wfdb.rdrecord('sample-data/binformats', physical=False) + sig_target = np.genfromtxt('tests/target-output/record-1f.gz') + + for n, name in enumerate(record.sig_name): + np.testing.assert_array_equal( + record.d_signal[:, n], + sig_target[:, n], + "Mismatch in %s" % name) + + for sampfrom in range(0, 3): + for sampto in range(record.sig_len - 3, record.sig_len): + record_2 = wfdb.rdrecord('sample-data/binformats', + physical=False, + sampfrom=sampfrom, sampto=sampto) + for n, name in enumerate(record.sig_name): + if record.fmt[n] != '8': + np.testing.assert_array_equal( + record_2.d_signal[:, n], + sig_target[sampfrom:sampto, n], + "Mismatch in %s" % name) + # ------------------ 2. Special format records ------------------ # def test_2a(self):