Skip to content

Commit 32265d6

Browse files
committed
add ecg grids
1 parent 6722045 commit 32265d6

File tree

3 files changed

+499
-2
lines changed

3 files changed

+499
-2
lines changed

devtests.ipynb

Lines changed: 399 additions & 1 deletion
Large diffs are not rendered by default.

wfdb/_headers.py

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -582,4 +582,41 @@ def __init__(self, allowedtypes, delimiter, dependency, write_req, read_def, wri
582582

583583

584584

585+
# For storing WFDB Signal definitions.
586+
587+
# SignalType class with all its parameters
588+
class SignalType():
589+
def __init__(self, description, measurement=None, default_display=None, signalnames=None):
590+
self.description = description
591+
self.unitscale = unitscale
592+
# Tuple pair (a, b). The plot displays range a, of unit b.
593+
self.default_display = default_display
594+
self.signalnames = signalnames
595+
596+
unitscale = {
597+
'Voltage': ['pV', 'nV', 'uV', 'mV', 'V', 'kV'],
598+
'Temperature': ['C'],
599+
'Pressure': ['mmHg'],
600+
}
601+
602+
# All signal types
603+
signaltypes = {
604+
'BP': SignalType('Blood Pressure', 'Pressure'),
605+
'CO2': SignalType('Carbon Dioxide'),
606+
'CO': SignalType('Carbon Monoxide'),
607+
'ECG': SignalType('Electrocardiogram'),
608+
'EEG': SignalType('Electroencephalogram'),
609+
'EMG': SignalType('Electromyograph'),
610+
'EOG': SignalType('Electrooculograph'),
611+
'HR': SignalType('Heart Rate'),
612+
'MMG': SignalType('Magnetomyograph'),
613+
'O2': SignalType('Oxygen'),
614+
'PLETH': SignalType('Plethysmograph'),
615+
'RESP': SignalType('Respiration'),
616+
'SCG': SignalType('Seismocardiogram'),
617+
'STAT': SignalType('Status'), # small integers indicating status
618+
'ST': SignalType('ECG ST Segment'),
619+
'TEMP': SignalType('Temperature'),
620+
}
621+
585622

wfdb/plots.py

Lines changed: 63 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,13 @@
11
import numpy as np
22
import matplotlib.pyplot as plt
3+
import math
34
from . import records
45
from . import _headers
56
from . import annotations
67

78
# Plot a WFDB Record's signals
89
# Optionally, overlay annotation locations
9-
def plotrec(record=None, title = None, annotation = None, annch = [0], timeunits='samples', returnfig = False):
10+
def plotrec(record=None, title = None, annotation = None, annch = [0], timeunits='samples', returnfig = False, ecggrids=False):
1011
""" Subplot and label each channel of a WFDB Record.
1112
Optionally, subplot annotation locations over selected channels.
1213
@@ -70,12 +71,65 @@ def plotrec(record=None, title = None, annotation = None, annch = [0], timeunits
7071
unitlabel='NU'
7172
plt.ylabel(chanlabel+"/"+unitlabel)
7273

74+
# Show standard ecg grids if specified.
75+
if ecggrids:
76+
major_ticks_x, minor_ticks_x, major_ticks_y, minor_ticks_y = calc_ecg_grids(
77+
record.p_signals[:,ch], record.units[ch], record.fs, t, timeunits)
78+
ax.set_xticks(major_ticks_x)
79+
ax.set_xticks(minor_ticks_x, minor=True)
80+
ax.set_yticks(major_ticks_y)
81+
ax.set_yticks(minor_ticks_y, minor=True)
82+
ax.grid(which='both')
83+
7384
plt.show(fig)
7485

7586
# Return the figure if requested
7687
if returnfig:
7788
return fig
7889

90+
# Calculate tick intervals for ecg grids
91+
def calc_ecg_grids(signal, units, fs, t, timeunits):
92+
93+
# 5mm 0.2s major grids, 0.04s minor grids
94+
# 0.5mV major grids, 0.125 minor grids
95+
# 10 mm is equal to 1mV in voltage.
96+
97+
# Get the grid interval of the x axis
98+
if timeunits == 'samples':
99+
majorx = 0.2*fs
100+
minorx = 0.04*fs
101+
elif timeunits == 'seconds':
102+
majorx = 0.2
103+
minorx = 0.04
104+
elif timeunits == 'minutes':
105+
majorx = 0.2/60
106+
minorx = 0.04/60
107+
elif timeunits == 'hours':
108+
majorx = 0.2/3600
109+
minorx = 0.04/3600
110+
111+
# Get the grid interval of the y axis
112+
if units.lower()=='uv':
113+
majory = 500
114+
minory = 125
115+
elif units.lower()=='mv':
116+
majory = 0.5
117+
minory = 0.125
118+
elif units.lower()=='v':
119+
majory = 0.0005
120+
minory = 0.000125
121+
else:
122+
raise ValueError('Signal units must be uV, mV, or V to plot the ECG grid.')
123+
124+
125+
major_ticks_x = np.arange(0, upround(max(t), majorx), majorx)
126+
minor_ticks_x = np.arange(0, upround(max(t), majorx), minorx)
127+
128+
major_ticks_y = np.arange(downround(min(signal), majory), upround(max(signal), majory), majory)
129+
minor_ticks_y = np.arange(downround(min(signal), majory), upround(max(signal), majory), minory)
130+
131+
return (major_ticks_x, minor_ticks_x, major_ticks_y, minor_ticks_y)
132+
79133
# Check the validity of items used to make the plot
80134
# Return the x axis time values to plot for the record (and annotation if any)
81135
def checkplotitems(record, title, annotation, annch, timeunits):
@@ -233,3 +287,11 @@ def checkannplotitems(annotation, title, timeunits):
233287
raise TypeError("The 'title' field must be a string")
234288

235289
return plotvals
290+
291+
# Round down to nearest <base>
292+
def downround(x, base):
293+
return base * round(float(x)/base)
294+
295+
# Round up to nearest <base>
296+
def upround(x, base):
297+
return base * math.ceil(float(x)/base)

0 commit comments

Comments
 (0)