/* file: refhr.c G. Moody 20 March 1992
Last revised: 7 May 1999
-------------------------------------------------------------------------------
refhr: Sample program for generating heart rate measurement annotation file
Copyright (C) 1999 George B. Moody
This program is free software; you can redistribute it and/or modify it under
the terms of the GNU General Public License as published by the Free Software
Foundation; either version 2 of the License, or (at your option) any later
version.
This program is distributed in the hope that it will be useful, but WITHOUT ANY
WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
PARTICULAR PURPOSE. See the GNU General Public License for more details.
You should have received a copy of the GNU General Public License along with
this program; if not, see .
You may contact the author by e-mail (wfdb@physionet.org) or postal mail
(MIT Room E25-505A, Cambridge, MA 02139 USA). For updates to this software,
please visit PhysioNet (http://www.physionet.org/).
_______________________________________________________________________________
This program copies an annotation file, inserting MEASURE annotations
containing two illustrative types of heart rate measurements into the output.
Any MEASURE annotations in the input file are not copied. The output of this
program is suitable for input to `mxm'.
*/
#include
#include
#include
main(argc, argv)
int argc;
char *argv[];
{
char hr_string[20]; /* temporary storage for heart rate measurements */
double sps; /* sampling frequency (samples per second) */
int i;
static char *record;
static WFDB_Anninfo an[2];
static WFDB_Annotation annot, hr_annot;
for (i = 1; i < argc; i++) { /* read and interpret command arguments */
if (*argv[i] == '-') switch (*(argv[i]+1)) {
case 'a': /* annotator names follow */
if (++i >= argc-1) {
(void)fprintf(stderr,
"%s: input and output annotator names must follow -a\n",
argv[0]);
exit(1);
}
an[0].name = argv[i]; an[0].stat = WFDB_READ;
an[1].name = argv[++i]; an[1].stat = WFDB_WRITE;
break;
case 'r': /* record name follows */
if (++i >= argc) {
(void)fprintf(stderr,
"%s: record name must follow -r\n", argv[0]);
exit(1);
}
record = argv[i];
break;
default:
(void)fprintf(stderr,
"%s: unrecognized option %s\n", argv[0], argv[i]);
exit(1);
}
else {
(void)fprintf(stderr,
"%s: unrecognized argument %s\n",argv[0],argv[i]);
exit(1);
}
}
if (record == NULL || an[0].name == NULL) {
(void)fprintf(stderr,
"usage: %s -r record -a input-annotator output-annotator\n", argv[0]);
exit(1);
}
if ((sps = sampfreq(record)) <= 0) {
(void)fprintf(stderr,
"%s: (warning) %g Hz sampling frequency assumed\n",
argv[0], WFDB_DEFFREQ);
sps = WFDB_DEFFREQ;
}
/* Open the input and output annotation files. */
if (annopen(record, an, 2) < 0)
exit(2);
/* Initialize the constant fields of the annotation structure which is
to be used for heart rate measurements. */
hr_annot.anntyp = MEASURE;
hr_annot.aux = hr_string;
/* Read an annotation on each iteration. */
while (getann(0, &annot) == 0) {
/* Copy the annotation to the output file, unless it's a MEASURE
annotation (these get filtered out, so that they won't be
mistaken for MEASURE annotations generated by this program) */
if (annot.anntyp != MEASURE)
(void)putann(0, &annot);
/* Update heart rate measurements only if this annotation is a beat
label. */
if (isqrs(annot.anntyp)) {
double heart_rate;
static int a1; /* anntyp of previous beat label */
static long t1, t2, t3; /* times of 3 previous beat labels */
static long nn0, nn1, nn2, nn3, nn4, nn5;
/* current and previous 5 N-N intervals */
/* This sample program calculates two simple heart rate
measurements for illustrative purposes. First is a
measurement based on the last three R-R intervals, valid only
after the fourth beat. */
if (t3 > 0L) {
heart_rate = 180.0*sps/(annot.time - t3);
/* Convert the measurement to a string. */
sprintf(hr_string+1, "%g", heart_rate);
/* The first byte of the `aux' field must be a byte count. */
hr_string[0] = strlen(hr_string+1);
/* The measurement annotation can't be attached to the same
sample as the current beat annotation, so attach it to
the next sample. */
hr_annot.time = annot.time + 1;
/* The `subtyp' field specifies the measurement type. Here
the 3-beat average is assigned type 0. */
hr_annot.subtyp = 0;
/* Write the measurement annotation. */
(void)putann(0, &hr_annot);
}
t3 = t2; t2 = t1; t1 = annot.time;
/* The second measurement is based on the last six normal R-R
intervals, valid only after six normal R-R intervals have
been observed, and updated only if the current and previous
beats are both normal. */
if (map2(annot.anntyp) == NORMAL && a1 == NORMAL) {
nn0 = t1 - t2;
if (nn5 > 0L) {
heart_rate = 360.0*sps/(nn5 + nn4 + nn3 + nn2 + nn1 + nn0);
sprintf(hr_string+1, "%g", heart_rate);
hr_string[0] = strlen(hr_string+1);
/* This measurement annotation will need to be written
two sample intervals after the beat annotation, so
that it doesn't coincide with the first measurement. */
hr_annot.time = annot.time + 2;
/* The 6 N-N interval-based average is assigned type 1. */
hr_annot.subtyp = 1;
(void)putann(0, &hr_annot);
}
nn5 = nn4; nn4 = nn3; nn3 = nn2; nn2 = nn1; nn1 = nn0;
}
a1 = map2(annot.anntyp);
}
/* Note: we might run into trouble if another input annotation
follows a beat annotation within two sample intervals. In this
case, putann() will complain that annotations were not supplied
in time order. The measurement annotations will have been
written properly, but the offending input annotation will not
have been written. This is harmless if we're only using the
output file as input for `mxm', since `mxm' ignores everything
but the measurement annotations anyway. */
}
wfdbquit(); /* close input files */
exit(0); /*NOTREACHED*/
}