Plot of the EKG signals

For some reasons, I had to do an ECG Holter test. This test involves monitoring the heart rate for an extended period of time. In my case, 24 hours. The test is performed using a special apparatus, which measures the potential appearing on the surface of the skin, produced by the working heart.

The test itself does not provide much information because in its raw form it is incomprehensible to non-cardiologists.

The center where I had the test, performs a description of the recording by a cardiologist in the price of the service. The problem is, unfortunately, that the description of the examination is only a fragment of the entire recording. That’s why I asked the center to give me access to the whole recording in order to get a second opinion from another doctor. I was very surprised by the response: “This is not possible. The data is encrypted. Only we (the center’s staff) have the appropriate software that can open this examination.”

Well… I was forced to “hack” the device. I read the terms and conditions for renting the device and apart from the clause prohibiting destruction by immersion I found nothing else.

The recorder was made by MEDEA and is called SILICONBEAT 3. The device looks like a small remote control with one button. Sticking out of the casing are four cables that are electrodes clipped to the patient. On the side of the device there is a rubber cap that covers the USB port. Plot of the EKG signals After connecting the device to the computer it was detected as FLASH memory. Upon opening it, two files were displayed: INFO.TXT and 00_01_01-00_05_35.hol.

The INFO.TXT file contains information on the device status and examination settings like model, version number, battery voltage and so on.

REJESTRATOR HOLTEROWSKI SiliconBeat 3 Blue
Model:                   RCH8
Wersja:                  3
Wersja oprogramowania:   6.3
Numer seryjny            510
Tryb pracy:              24godz. 3 kanały
Automatyczny stop:       załšczony
Licznik zapisów:         80


--------------- ZAPIS BIEŻĽCY -------------------------------
NAPIĘCIE BATERII:      3,063V
START REJESTRACJI:     00:05:35 01/01/2000
STOP REJESTRACJI:      00:03:55 02/01/2000
NAPIĘCIE BATERII:      2.792V
PRZYCZYNA ZAKOŃCZENIA: NAND_ FLASH ZAPEŁNIONY
zapisano:              23godz. 58min.
liczba markerów:       0

The second file contains the captured data. After opening it with xxd is saw the following output:

00000000: 003c 4e4f 5759 205a 4150 4953 3e20 2020  .<NOWY ZAPIS>
00000010: 2020 2020 2020 2020 2020 2020 2020 2020
00000020: 2020 2020 2020 2020 2020 2020 2020 2020
00000030: 2020 2020 2020 2020 2020 2020 2020 2020
...
00000800: 4000 0300 6400 0c00 0000 ff00 950d 0000  @...d...........
00000810: 6300 fe01 0335 0500 0101 d007 00d8 0100  c....5..........
00000820: ffff ffff ffff ffff ffff ffff ffff ffff  ................
00000830: ffff ffff ffff ffff ffff ffff ffff ffff  ................
...
00001000: 5245 4a45 5354 5241 544f 5220 484f 4c54  REJESTRATOR HOLT
00001010: 4552 4f57 534b 4920 5369 6c69 636f 6e42  EROWSKI SiliconB
00001020: 6561 7420 3320 426c 7565 200d 0a4d 6f64  eat 3 Blue ..Mod
00001030: 656c 3a20 2020 2020 2020 2020 2020 2020  el:             
00001040: 2020 2020 2020 5243 4838 0d0a 5765 7273        RCH8..Wers
00001050: 6a61 3a20 2020 2020 2020 2020 2020 2020  ja:             
00001060: 2020 2020 2033 0d0a 5765 7273 6a61 206f       3..Wersja o
00001070: 7072 6f67 7261 6d6f 7761 6e69 613a 2020  programowania:  
00001080: 2036 2e33 0d0a 4e75 6d65 7220 7365 7279   6.3..Numer sery
00001090: 6a6e 7920 2020 2020 2020 2020 2020 2035  jny            5
000010a0: 3130 0d0a 5472 7962 2070 7261 6379 3a20  10..Tryb pracy:
000010b0: 2020 2020 2020 2020 2020 2020 2032 3467               24g
000010c0: 6f64 7a2e 2033 206b 616e 61b3 790d 0a41  odz. 3 kana.y..A
000010d0: 7574 6f6d 6174 7963 7a6e 7920 7374 6f70  utomatyczny stop
000010e0: 3a20 2020 2020 2020 7a61 b3b9 637a 6f6e  :       za..czon
000010f0: 790d 0a4c 6963 7a6e 696b 207a 6170 6973  y..Licznik zapis
00001100: f377 3a20 2020 2020 2020 2020 3830 0d0a  .w:         80..
00001110: 0d0a 0d0a 2d2d 2d2d 2d2d 2d2d 2d2d 2d2d  ....------------
00001120: 2d2d 2d20 5a41 5049 5320 4249 45af a543  --- ZAPIS BIE..C
00001130: 5920 2d2d 2d2d 2d2d 2d2d 2d2d 2d2d 2d2d  Y --------------
00001140: 2d2d 2d2d 2d2d 2d2d 2d2d 2d2d 2d2d 2d2d  ----------------
00001150: 2d0d 0a4e 4150 49ca 4349 4520 4241 5445  -..NAPI.CIE BATE
00001160: 5249 493a 2020 2020 2020 332c 3036 3356  RII:      3,063V
00001170: 200d 0a53 5441 5254 2052 454a 4553 5452   ..START REJESTR
00001180: 4143 4a49 3a20 2020 2020 3030 3a30 353a  ACJI:     00:05:
00001190: 3335 2030 312f 3031 2f32 3030 300d 0a53  35 01/01/2000..S
000011a0: 544f 5020 5245 4a45 5354 5241 434a 493a  TOP REJESTRACJI:
000011b0: 2020 2020 2020 3030 3a30 333a 3535 2030        00:03:55 0
000011c0: 322f 3031 2f32 3030 300d 0a4e 4150 49ca  2/01/2000..NAPI.
000011d0: 4349 4520 4241 5445 5249 493a 2020 2020  CIE BATERII:    
000011e0: 2020 322e 3739 3256 0d0a 5052 5a59 435a    2.792V..PRZYCZ
000011f0: 594e 4120 5a41 4b4f d143 5a45 4e49 413a  YNA ZAKO.CZENIA:
00001200: 204e 414e 445f 2046 4c41 5348 205a 4150   NAND_ FLASH ZAP
00001210: 45a3 4e49 4f4e 590d 0a7a 6170 6973 616e  E.NIONY..zapisan
00001220: 6f3a 2020 2020 2020 2020 2020 2020 2020  o:              
00001230: 3233 676f 647a 2e20 3538 6d69 6e2e 200d  23godz. 58min. .
00001240: 0a6c 6963 7a62 6120 6d61 726b 6572 f377  .liczba marker.w
00001250: 3a20 2020 2020 2020 300d 0a0d 0a20 2020  :       0....   
00001260: 2020 2020 2020 2020 2020 2020 2020 2020                  
00001270: 2020 2020 2020 2020 2020 2020 2020 2020
...
00001800: ffff ffff ffff ffff ffff ffff ffff ffff  ................
00001810: ffff ffff ffff ffff ffff ffff ffff ffff  ................
...
0001d800: 000c 9c07 bd07 e007 8307 a107 f807 8207  ................
0001d810: 8807 2608 2c08 0408 5a08 c909 6f09 4208  ..&.,...Z...o.B.
0001d820: 3c0b fa0a f507 ae0a b90a b207 c908 1709  <...............
...
0317d7e0: bc07 ae07 0408 ae07 a907 ed07 b007 c307  ................
0317d7f0: e807 b107 c907 e207 ab07 c907 e007 9d07  ................
0317d800: ffff

Everything below the address 0001d800 seemed to be the recorded data. Using string on the file spat out this aligned output:

	0	$
M	y
_	F	W
	-	3
2	g
~	e
	Y	:
a	&	E
h			:
!	n
K	k

Experience tells me that these are simply sequential 16-bit readings from the ADC. I read it in octave with fid = fopen(f_name); f = fread(fid, Inf, "uint16"); and plotted some data from the middle. Plot of the EKG signals Almost ready. The graph is a little chopped up because the recorder measures three channels in series. We have to split those channels into three separate waveforms. Additionally, I removed the baseline by subtracting the signals passed through a low-pass filter. And done. I can now do anything with this data. E.g. share it in any format with other doctors or show the study during a consultation. Plot of the EKG signals

The code snippet for the whole analysis (plus peak detection and heart rate plotting) is posted below.

[expand]

clc;
clear;
pkg load signal;

function retval = remove_baseline(vector)
  cutoff = 300;
  order = 5;
  [b,a] = butter(order, 1/cutoff);
  retval = round(filter(b, a, vector));
endfunction

function retval = clip_vector(vector)
  clipping_factor = 10;
  vector(vector > mean(vector)+clipping_factor*std(vector)) = mean(vector)+clipping_factor*std(vector);
  vector(vector < mean(vector)-clipping_factor*std(vector)) = mean(vector)-clipping_factor*std(vector);
  retval = vector;
endfunction

f_name = "00_01_01-00_05_35.hol";

sample_clock = 200/4; %[Hz]

fid = fopen(f_name);
f = fread(fid, Inf, "uint16");

data_start_addr_bytes = 120833;
data_start = round(data_start_addr_bytes / 2);

ch0 = f(data_start + 0:3:end);
ch1 = f(data_start + 1:3:end);
ch2 = f(data_start + 2:3:end);

# Remove baseline
ch0 = ch0 - remove_baseline(ch0);
ch1 = ch1 - remove_baseline(ch1);
ch2 = ch2 - remove_baseline(ch2);

# Clip noise
ch0 = clip_vector(ch0);
ch1 = clip_vector(ch1);
ch2 = clip_vector(ch2);

# Find heart rate
p_ch2 = ch2;
p_ch2(p_ch2<0) = 0;
peaks_pos = [];
tmp = [];
part_size = 100000;
for i = 1:(length(p_ch2) / part_size)
  [~,tmp] = findpeaks(p_ch2((1 + part_size*(i-1)) : (part_size*(i))),'MinPeakDistance',10,'MinPeakHeight',300);
  peaks_pos = [peaks_pos; tmp + part_size*(i-1)];
endfor

plot(1:length(ch0),ch0,
     1:length(ch0),ch1,
     1:length(ch0),ch2,
     peaks_pos(2:end), 60./(diff(peaks_pos)./sample_clock));

[/expand]


Summary

  • The data was not encrypted.
  • The staff at the front desk is lying. ;D
  • Not sharing the entire medical imaging record is literally theft.
  • The date on the device is a little off.
  • MEDEA praises that the device has a 24 bit ADC but the data is saved in 16 bits.