Drawing Medical Waveforms using a Windows Mobile 5.0 Device






4.86/5 (29 votes)
How to develop patient monitoring systems based on a WinMobile device
Introduction
Some time back, I developed a prototype real-time monitoring console for medical monitoring. This tip explains my work.
Definitions
- ECG means electrocardiograph, a graph that indicates electrical activity of heart tissues.
- SPO2 means Oxyhemoglobin saturation, or simply the percentage of Oxygen in blood. For a normal adult at rest, this could be around 95 to 99.
- Respiration means one inspiration and one expiration. For a normal adult at rest, this could be around 16 to 20 per minute.
Application and Scope
Biomedical monitors are used in critical care applications where patients are continuously monitored for any changes in the function of their vital organs. If such a monitor streams data to handheld devices like Windows Mobile phone, we can implement an anywhere, anytime monitoring device. This can help medical specialists to monitor any patient instantly, without leaving their seat.
The current demo takes values from static integer arrays. But, I will soon move it to Bluetooth to plot true real-time data. I have tested the code myself on an i-mate K-JAM device that runs Windows Mobile 5.0. However, this code should run on any PPC 2003 compatible device.
The application's working is quite simple, as described below:
public partial class Form1 : Form
{
//create variables for storing current and previous points
int x, p, q, r, y, px, pp, pq, pr, py = 0;
//create colored pens for drawing
Pen yelgpen = new Pen(Color.YellowGreen);
Pen redpen = new Pen(Color.Red);
Pen graypen = new Pen(Color.Gray);
Pen blackpen = new Pen(Color.Black);
//fill the points to be plotted in integer arrays
int[] ECGArray = new int[]
{ -5, -14, -10, -8, -9, -7, 0, 121, 108, -24, -34, -18, -19, -16, -19, -17,
-11, -11, -10, -1, 13, 33, 45, 47, 46, 37, 12, -12, -24, -23, -25, -21, -20,
-17, -19, -14, -12, -13, -12, -13, -8, -9, -5, -8, 10, 21, 19, 4, -10,
-11, -11, -12, -8, -12, 0, 121, 108, -24, -23, -18, -21, -16, -15, -14,
-10, -12, -7, 5, 26, 43, 46, 46, 41, 21, -1, -18, -24, -19, -22, -21,
-18, -18, -16, -13, -10, -12, -11, -8, -10, -8, -7, 0, 18, 24, 9, -7,
-13, -11, -9, -8, -10, 0, 121, 108, -24, -33, -17, -20, -19, -17, -14,
-13, -11, -8, 2, 19, 35, 47, 50, 43, 29, 7, -15, -29, -22, -25, -20,
-16, -19, -17, -16, -12, -11, -11, -10, -11, -9, -6, 0, 13, 23, 15, -2,
-11, -10, -10, -8, -7, -15, 0, 121, 108, -24, -20, -19, -20, -16, -12,
-14, -15, -11, -2, 12, 30, 41, 48, 44, 34, 16, -5, -21, -22, -21, -20,
-20, -13, -15, -15, -17, -14, -13, -9, -10, -10, -11, -8, 5, 23, 22, 4,
-9, -10, -12, -10, -10, -11, 0, 121, 108, -24, -28, -18, -19, -16, -16,
-16, -15, -13, -8, 4, 21, 39, 44, 46, 38, 45, 24, -1, -23, -28, -27, -25,
-20, -21, -18, -18, -14, -14};
int[] SPO2Array = new int[]
{ 10, 9, 9, 9, 9, 9, 9, 10, 12, 16, 20, 25, 31, 37, 41, 45, 47, 47,
46, 42, 38, 33, 27, 23, 20, 17, 15, 15, 16, 16, 18, 18, 19, 19, 18, 18, 17,
16, 15, 13, 13, 11, 11, 11, 10, 10, 9, 9, 9, 9, 9, 9, 10, 12, 16, 20, 25,
31, 37, 41, 45, 47, 47, 46, 42, 38, 33, 27, 23, 20, 17, 15, 15, 16, 16, 18,
18, 19, 19, 18, 18, 17, 16, 15, 13, 13, 11, 11, 11, 10, 10, 9, 9, 9, 9, 9,
9, 10, 12, 16, 20, 25, 31, 37, 41, 45, 47, 47, 46, 42, 38, 33, 27, 23, 20,
17, 15, 15, 16, 16, 18, 18, 19, 19, 18, 18, 17, 16, 15, 13, 13, 11, 11, 11,
10, 10, 9, 9, 9, 9, 9, 9, 10, 12, 16, 20, 25, 31, 37, 41, 45, 47, 47, 46,
42, 38, 33, 27, 23, 20, 17, 15, 15, 16, 16, 18, 18, 19, 19, 18, 18, 17, 16,
15, 13, 13, 11, 11, 11, 10, 10, 9, 9, 9, 9, 9, 9, 10, 12, 16, 20, 25, 31,
37, 41, 45, 47, 47, 46, 42, 38, 33, 27, 23, 20, 17, 15, 15, 16, 16, 18, 18,
19, 19, 18, 18, 17, 16, 15, 13, 13, 11, 11, 11, 10, 10, 10, 10 };
int[] respArray = new int[] { 50, 50, 50, 51, 51, 51, 51, 52, 52, 52, 52,
53, 53, 53, 54, 54, 54, 54, 55, 55, 55, 55, 55, 56, 56, 56, 56, 56,
56, 56, 56, 56, 56, 56, 56, 57, 57, 57, 57, 57, 57, 58, 58, 58, 58,
58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 57,
57, 57, 57, 57, 57, 57, 56, 56, 56, 56, 56, 56, 55, 55, 55, 55, 55,
54, 54, 54, 54, 54, 53, 53, 53, 52, 52, 51, 51, 51, 50, 50, 50, 49,
49, 49, 48, 48, 47, 47, 47, 46, 46, 46, 45, 45, 45, 45, 44, 44, 44,
43, 43, 42, 42, 41, 41, 41, 40, 40, 39, 39, 38, 38, 38, 37, 37, 37,
36, 36, 36, 36, 35, 35, 35, 35, 34, 34, 34, 34, 34, 33, 33, 33, 33,
33, 32, 32, 32, 32, 32, 32, 32, 32, 31, 31, 31, 31, 31, 31, 31, 31,
31, 31, 31, 31, 32, 32, 32, 32, 32, 32, 32, 33, 33, 33, 33, 33, 34,
34, 34, 35, 35, 35, 36, 36, 36, 37, 37, 37, 38, 38, 39, 39, 40, 40,
40, 41, 41, 42, 42, 42, 43, 43, 44, 44, 45, 45, 45, 45, 46, 46, 46,
46, 47, 47, 47, 47, 48, 48, 49, 49, 49, 49, 49, 49 };
Now, use a timer to get the graph moving:
private void timer1_Tick(object sender, EventArgs e)
{
//we are drawing on the form itself
Graphics g = this.CreateGraphics();
//traverse through the points one by one to get moving graphs
for (int i = 0; i < ECGArray.Length; i++)
{
//e.Graphics.DrawLine(pen, 10, 10, 100, 100);
if (x > this.Width - 10)
{
x = 0;
pp = 0;
pq = 0;
pr = 0;
px = 0;
}
x += 1;
//draw ECG
p = 70 - (ECGArray[i] / 6 + 50);
g.DrawLine(yelgpen, px, pp, x, p);
g.DrawLine(yelgpen, px, pp + 60, x, p + 60);
g.DrawLine(yelgpen, px, pp + 120, x, p + 120);
//draw SPO2
q = ((SPO2Array[i] * -1) / 2 + 200);
g.DrawLine(graypen, px, pq, x, q);
//draw respiration
r = (respArray[i] / 2 + 220);
g.DrawLine(redpen, px, pr, x, r);
//draw erasebar to erase previous content at current location
g.DrawRectangle(blackpen, 0, 0, x + 1, this.Height);
//store current point as previous point
pp = p;
pq = q;
pr = r;
px = x;
}
}
Do not forget to keep the timer enabled. I have set the timer interval to 1ms to get a good sweep speed.
History
- 14th February, 2022: Initial version