Page 1 of 1

Electrocardiogram plot

Posted: Fri Jan 22, 2010 11:55 am
by 10551621
Hi All,
I'm trying to use a chart to plot ECG (cardiac) signals.
The signals are recorded during 24 hours with a sampling frequency of 1 kHz.
I have 12 lineseries. each series has its own YAxis. but they share the same XAxis.
At first I tried loading all the data into the chart and using chart pages to display only a part of the signal but it was too slow.
Then I switched to putting a Tscrollbar (TchartScrollbar is not usefull in my case because it scrolls an already filled chart).
At change of the scrollbar.position I clear the old series and i create new 12 lineseries and I fill them with segment of 10 seconds data:
Here is my code :

Code: Select all

		Screen->Cursor = crHourGlass;
	  int num_leads = ECG->getNumLeads() ;
	  int freq= ECG->getFreq();
	  long M= ECG->getNumSamp();
	  long K = M/num_leads;
	  ScrollBar2->Min = 0;
	  ScrollBar2->Max = floor(1.0*K/(UpDown1->Position*freq));
	  size_t num = freq * UpDown1->Position; // windows size
	  size_t start = ScrollBar2->Position*num; // Page number
	  double Dt = 1.0/freq;
	  double t =0.0;
	  char ld_name[64];
	  for (int j  = 0; j < num_leads; j++) { // plot lead by lead
			ECGPlot[j] = new TFastLineSeries(Chart1);
			ECGPlot[j]->ParentChart = Chart1;
			t=start*Dt;
			double *data = new double[num];
			int read =ECG->getData(j,start,num,data);
			// data table for lead j
			for (int i = 0; i < read ; i++)
			{
				t+=Dt;
				ECGPlot[j]->AddXY(t,data[i]);
			}
			delete [] data;
			sprintf(ld_name, "Lead-%s", ECG->LeadText(j));
			ECGPlot[j]->Title=ld_name;
		}
		Chart1->BottomAxis->SetMinMax(start*Dt, start*Dt+num*Dt);
		Chart1->BottomAxis->Increment=1.0;
		Chart1->CustomAxes->Clear();
	   int n = Chart1->SeriesCount();
	   int D = 100/n;
	   for (int i = 0; i < n; i++) {
		   TChartAxis *Ax = new TChartAxis(Chart1->CustomAxes);
		   Ax->Axis->Color = Chart1->Series[i]->Color;
		   Ax->StartPosition = i*D;
		   Ax->EndPosition = Min(i*D +D-2,100);
		   Ax->PositionPercent = 0;
		   Ax->LabelStyle =talNone;
		   Chart1->Series[i]->CustomVertAxis = Ax;
		   Chart1->Series[i]->Color= clNavy;
	   }
I have some questions about enhancing this code :
1- Can I make the 12 lineseries sharing the same XAxis data with repeating filling and allocating the Xvalues to the 12 series ?
2- I want to control the XAxis scale on the screen and on the printer. Doctors are used to display ECG data with a 25 mm/s.
How can I control the Xasis length to make it equal to 25*10 = 250 mm =25 cm on the screen and on the printer?

Regards

Re: Electrocardiogram plot

Posted: Mon Jan 25, 2010 12:38 pm
by yeray
Hi Yazou,
Yazou wrote:1- Can I make the 12 lineseries sharing the same XAxis data with repeating filling and allocating the Xvalues to the 12 series ?
I'm not sure to understand what are you exactly trying to do here. Could you please send us a picture showing what you would like to achieve or a simple example project we can run as-is to reproduce the issue here?
Yazou wrote:2- I want to control the XAxis scale on the screen and on the printer. Doctors are used to display ECG data with a 25 mm/s.
How can I control the Xasis length to make it equal to 25*10 = 250 mm =25 cm on the screen and on the printer?
I think you should be able to convert mm to pixels doing something similar to this (and maybe Printer.Handle [uses printers]).

Re: Electrocardiogram plot

Posted: Mon Jan 25, 2010 3:36 pm
by 10551621
Hello,
1-As you can see in the attached image I have 8 lineseries sharing the same X axis data.
My data is saved internally as integer (16 bits) without the x axis values. I need to know only the sampling frequency of the data. If I have 24 hours ECG recording with 1000 values/s. This gives 1000*3600*24*(2 bytes) = 172.8 MB for one ECG channel and about 1.4 GB for 8 channel.
If i want to add theses data to a chart then for each value we have (X,Y) on 2 doubles (2x8 bytes = 16 Bytes). This gives 1.38 GB by channel and about 11 GB for 8 channels.
My idea is to to make the 8 lineseries sharing the same Xvalues in order to reduce the memory usage of the chart. Is this possible ?

2- regarding printing with a user defined scale. Is there any example in the demo showing this possibility? I know how to compute the printer pixelperinches informations. But how can I pass these informatiion to the chart ?

best regards,
Yazou

Re: Electrocardiogram plot

Posted: Thu Jan 28, 2010 10:08 am
by yeray
Hi Yazou,

Excuse us for the delayed reply.
Yazou wrote:1-As you can see in the attached image I have 8 lineseries sharing the same X axis data.
My data is saved internally as integer (16 bits) without the x axis values. I need to know only the sampling frequency of the data. If I have 24 hours ECG recording with 1000 values/s. This gives 1000*3600*24*(2 bytes) = 172.8 MB for one ECG channel and about 1.4 GB for 8 channel.
If i want to add theses data to a chart then for each value we have (X,Y) on 2 doubles (2x8 bytes = 16 Bytes). This gives 1.38 GB by channel and about 11 GB for 8 channels.
My idea is to to make the 8 lineseries sharing the same Xvalues in order to reduce the memory usage of the chart. Is this possible ?
First of all, I recommend you to try to implement as tips as possible from the ones explained in the Real-time Charting article here. In particular you may be interested in the arrays assignment to series' valuelists.

Another suggestion could be using singles instead of doubles as is explained here.

Here is a test I've made that could help you:

Code: Select all

uses series;

procedure TForm1.FormCreate(Sender: TObject);
var i, j, nSeries, nPoints: Integer;
    XValues: array of Single;
    YValues: array of array of Single;
begin
  Chart1.View3D:=false;

  nSeries:=20;
  nPoints:=100000;

  SetLength(XValues,nPoints);
  SetLength(YValues,nSeries);
  for i:=0 to nSeries-1 do
  begin
    SetLength(YValues[i],nPoints);
    YValues[i][0]:=random*100+1000;
  end;
  XValues[0]:=0;
  for j:=Low(XValues)+1 to High(XValues) do
  begin
    XValues[j]:=j*5;
    for i:=Low(YValues) to High(YValues) do
      YValues[i][j]:=YValues[i][j-1] + random*10-5;
  end;


  //option 1: AddXY
  //35 Mb with 20 series and 100.000 pts per series
  //19 Mb with the same data and TEEVALUESINGLE and arrays of singles instead of arrays of doubles
{  for i:=0 to nSeries-1 do
  begin
    Chart1.AddSeries(TLineSeries.Create(self));
    Chart1[i].BeginUpdate;
    for j:=0 to nPoints-1 do
      Chart1[i].AddXY(XValues[j],YValues[i][j]);
    Chart1[i].EndUpdate;
  end;}


  //option 2: Assign Arrays
  //19 Mb with 20 series and 100.000 pts per series
  //11 Mb with the same data and TEEVALUESINGLE and arrays of singles instead of arrays of doubles
  for i:=0 to nSeries-1 do
  begin
    Chart1.AddSeries(TLineSeries.Create(self));
    With Chart1[i].XValues do
    begin
      Value:=TChartValues(XValues);
      Count:=Length(XValues);
      Modified:=True;
    end;
    With Chart1[i].YValues do
    begin
      Value:=TChartValues(YValues[i]);
      Count:=Length(YValues[i]);
      Modified:=True;
    end;
  end;

  Chart1.Axes.Left.Automatic:=true;
end;
Another option could be creating a custom series class that could have a valuelist for each channel and only one more for the X values.
Yazou wrote:2- regarding printing with a user defined scale. Is there any example in the demo showing this possibility? I know how to compute the printer pixelperinches informations. But how can I pass these informatiion to the chart ?
I'd recommend you to take a look at the printing better article here