This is wpg, produced by makeinfo version 4.0b from wpg.tex.


File: wpg,  Node: Header Files,  Next: Signal Files,  Prev: Database Files,  Up: Database Files

Header Files
------------

   "Header files" have names of the form `RECORD.hea', where RECORD is
the record name.  (MIT DB records are named 100-124 and 200-234 with
some numbers missing.  AHA DB records are named 1001-1010, 2001-2010,
3001-3010, 4001-4010, 5001-5010, 6001-6010, 7001-7010, and 8001-8010.
ESC DB records are named e0103-e1304, with many numbers missing.)
Header files are text files, with lines terminated by ASCII
carriage-return/line-feed pairs, created by `newheader', `setheader',
or `setmsheader', from which `isigopen', `osigopen', and `wfdbinit'
read the names of the signal files and their attributes as given in the
array of `WFDB_Siginfo' objects; `sampfreq' also reads a header file to
determine the sampling frequency used for a record.


File: wpg,  Node: Signal Files,  Next: Annotation Files,  Prev: Header Files,  Up: Database Files

Signal Files
------------

   "Signal files" usually have names of the form `RECORD.dat'.  (The
`.dat' suffix is conventional, but not required; any file name
acceptable to the operating system is permissible.)  Signal files are
binary, and usually contain either 16-bit amplitudes (format 16), pairs
of 12-bit amplitudes bit-packed into byte triplets (format 212), or
8-bit first differences (format 8).  (See `<wfdb/wfdb.h>' for
information about other formats that are supported.)  The functions
that read and write signal files perform appropriate transformations so
that the samples visible to the application program are always
amplitudes of type `int' (at least 16 bits), regardless of the signal
file format.


File: wpg,  Node: Annotation Files,  Next: Calibration Files,  Prev: Signal Files,  Up: Database Files

Annotation Files
----------------

   "Annotation files" have names of the form `RECORD.ANNOTATOR'.  Those
named `RECORD.atr' are reference annotation files (assumed to be
correct).  Annotation files are binary, and contain records of variable
length that average slightly over 16 bits per annotation.


File: wpg,  Node: Calibration Files,  Next: AHA Format Files,  Prev: Annotation Files,  Up: Database Files

Calibration Files
-----------------

   Unlike header, signal, and annotation files, "calibration files" are
not associated with individual records.  A calibration file is needed
only if you have records containing signals other than ECGs;  in this
case, it is likely that a single calibration file will be adequate for
use with all of your records.  Calibration files are text files, with
lines terminated by ASCII carriage-return/line-feed pairs, created by
`newcal', from which `calopen' reads the calibration list (*note
WFDB_Calinfo structures::).  The WFDB Software Package includes a
standard calibration file, `wfdbcal', in the `data' directory.


File: wpg,  Node: AHA Format Files,  Next: Standard I/O,  Prev: Calibration Files,  Up: Database Files

AHA Format Files
----------------

   The "AHA Format" was defined in 1980 for storage of database records
on 9-track digital tape.  Signal files in AHA format are in format 16,
with two signals multiplexed into one file (*note Multiplexed Signal
Files::), and may be read and written using `getvec' and `putvec'.
AHA-format annotation files are binary, and contain fixed-length
(16-byte) annotation records.  An annotation file in AHA format may be
read or written using `getann' or `putann', if the `stat' field of the
`WFDB_Anninfo' object is set to `WFDB_AHA_READ' or `WFDB_AHA_WRITE'
before opening the file.  `annopen' recognizes the format of input
annotation files automatically and prints a warning if the format does
not match what was expected on the basis of `stat'.  AHA format
annotation files may be converted to standard format without loss of
information, and doing so reduces the storage requirement by a factor
of eight.

   Yet another format has been used more recently for distribution of
AHA DB files on floppy diskettes and CD-ROMs.  This format is
compatible with neither the original AHA format nor with any of the
formats supported directly by the WFDB library.  Programs `a2m' and
`ad2m', supplied with the WFDB Software Package, can convert files in
this format (as well as those in the original AHA format) to the
standard formats.


File: wpg,  Node: Standard I/O,  Next: Multiplexed Signal Files,  Prev: AHA Format Files,  Up: Database Files

Using Standard I/O for Database Files
=====================================

   If `-' is supplied as a record name to any of the functions that
read or write header files, the `hea' file is taken to be the standard
input or output, as appropriate.  If the name of a signal file is
specified in the `hea' file (or in the array of `WFDB_Siginfo' objects
passed to `osigfopen') as `-', the standard input (output) is used by
`getvec' (`putvec').  If the name of an annotator is given in the array
of `WFDB_Anninfo' objects as `-', the standard input (output) is used
by `getann' (`putann').  If the name of a calibration file is given as
`-', the standard input (output) is used by `calopen' (`newcal').

   Under MS-DOS, these features may not always be usable, since the
standard input and output are usually opened in "text" mode (which is
unsuitable for binary database files).

   Although the WFDB library does not forbid the use of the standard
input or output for more than one function (e.g., as both a signal file
and an annotation file), such use is in general a gross error that is
likely to lead to unintended results.


File: wpg,  Node: Multiplexed Signal Files,  Next: Multi-Frequency Records,  Prev: Standard I/O,  Up: Database Files

Multiplexed Signal Files
========================

   Multiplexed signal files may be identified by examining the `group'
fields of the array of `WFDB_Siginfo' objects returned by `isigopen' or
`wfdbinit'.  Signals belonging to the same group are multiplexed
together in the same file.  If all signals in a given signal file have
been sampled at the same frequency, and there are N signals in the
file, then each group of N successive samples in that file contains a
sample from each signal, always in the same order (but *note
Multi-Frequency Records::).

   Multiplexed signal files can be useful if the storage device is
sequential-access only (e.g., 9-track tape), if the storage device has
lengthy seek times (e.g., optical disk), if many signals must be
recorded and Unix's per-process limit on open files would otherwise be
exceeded, or if very high speed is required while the file is being
created (because of sampling constraints).  CD-ROM signal files, and
those available from PhysioNet, are multiplexed unless the record
contains only one signal.


File: wpg,  Node: Multi-Frequency Records,  Next: Multi-Segment Records,  Prev: Multiplexed Signal Files,  Up: Database Files

Multi-Frequency Records
=======================

   When signals of different types are recorded simultaneously and for
lengthy periods, it may be appropriate to choose different sampling
frequencies in order to reduce the storage requirements for signals of
limited bandwidth.  The support for multi-frequency records provided in
WFDB library version 9.0 (and later versions) allows application
programs to read and write records containing signals digitized at
multiple sampling frequencies.  In a multi-frequency record, a "frame"
of samples contains one _or more_ samples from each signal.  The "frame
rate" (base sampling frequency) of the record, as recorded in the
header file and as normally returned by `sampfreq', is defined as the
number of frames per second.  Signals sampled at multiples of the frame
rate are referred to as "oversampled signals".  For each signal, a
frequency multiplier specifies how many samples are included in each
frame.  The frequency multiplier (1 by default) is an integer, encoded
within the format field in the header file, and specified in the `spf'
field of the `WFDB_Siginfo' structure for the signal.

   A frame can be read as it was written (*note getframe::) by an
application that has been written to make use of multi-frequency
records.  Applications that are not "multi-frequency aware" can still
read signals using the standard `getvec' interface, which returns (as
always) one sample per signal on each invocation.  By default, `getvec'
reads multi-frequency records in "low-resolution" mode.  In this mode,
each oversampled signal is resampled at the frame rate by averaging all
of its samples in each frame.

   The function `setgvmode' can be used to select "high-resolution"
mode, in which `getvec' replicates samples of signals digitized at less
than the maximum sampling frequency (i.e., using zero-order
interpolation) so that each sample of an oversampled signal appears in
at least one sample vector returned by `getvec'.  In this mode,
`sampfreq' returns the number of samples per signal returned by
`getvec' per second of the record.  Furthermore (when using WFDB
library version 9.6 and later versions), all time quantities passed to
and from the WFDB library functions are understood to be in units of
these shorter sampling intervals; thus, for example, `getann' converts
times in frame numbers (as recorded in annotation files) into times in
sample numbers before filling in the caller's annotation structure, and
`putann' converts times in sample numbers into times in frame numbers
before writing annotations into annotation files.  This permits
applications that are not "multi-frequency aware" to read
multi-frequency records with the highest possible resolution.

   The operating mode used by `getvec', if not specified by an explicit
call to `setgvmode', is determined by the value of the environment
variable `WFDBGVMODE' if it is set, and otherwise by the value of
`DEFWFDBGVMODE' in `wfdblib.h' at the time the library was compiled..
In either case, a value of 0 selects low-resolution mode, and any other
value selects high-resolution mode.


File: wpg,  Node: Multi-Segment Records,  Next: Multiple Record Access,  Prev: Multi-Frequency Records,  Up: Database Files

Multi-Segment Records
=====================

   A multi-segment record consists of two or more concatenated segments.
Each segment is an ordinary WFDB record, with its own header file and
signal file(s).  In any given multi-segment record, all signals must
appear in the same order within each segment (signals may not be
omitted), and the sampling frequency of any given signal must be the
same in each segment.  Segments of multi-segment records must be
ordinary records (it is not permitted to nest one multi-segment record
within another, for example), and the length of each segment must be
specified (the WFDB library does not impose this requirement on ordinary
records that are not part of a multi-segment record).  There are no
other restrictions on segments; specifically, it is permitted to mix
segments with different storage formats, and for any segment to appear
more than once.  A special header file (created either manually or by
using `setmsheader') specifies the record name for each segment in a
multi-segment record.  Once this special header exists, the
multi-segment record can be read by any WFDB application.  Note that
only the signal files of the segments are "linked" by the multi-segment
record's header; annotation files associated with the individual
segments are _not_ readable as part of the multi-segment record
(although an annotation file associated directly with the multi-segment
record can be created and read just as for an ordinary record).  From
the point of view of a WFDB application, reading a multi-segment record
is exactly like reading an ordinary record; specifically, `isigsettime'
works as expected, permitting jumps forward and backward between as
well as within segments.  Version 9.1 of the WFDB library is the first
to support reading and writing multi-segment records.


File: wpg,  Node: Multiple Record Access,  Next: Special Files,  Prev: Multi-Segment Records,  Up: Database Files

Simultaneous Access to Multiple Records
=======================================

   Selection functions that accept RECORD arguments (`annopen',
`isigopen', `osigopen', and `wfdbinit') normally close any active
database files of the types with which each deals before proceeding.
The argument `+'RECORD is synonymous with RECORD, but has the effect of
causing these functions to leave any active files open.  (For
convenience, the other functions that accept RECORD arguments --
`sampfreq', `newheader', and `setheader' -- also treat RECORD and
`+'RECORD as synonymous, but without any noticeable effect.)  The
restrictions on the total numbers of signals and annotation files still
apply.

   If the sampling frequencies or lengths of the records do not match,
a warning message will be produced (unless `wfdbquiet' was invoked).
The time-conversion functions (*note timstr and strtim::) will continue
to use the sampling frequency and base time defined for the first
record that was opened, unless these attributes are reset by
`sampfreq', `setsampfreq', or `setbasetime'.

   Function `calopen' uses the `+' convention for calibration file
names.  Although it normally creates the calibration list from scratch
each time it is called, it retains the current calibration list if the
calibration file name is prefixed by `+'.


File: wpg,  Node: Special Files,  Next: Piped and Local Records,  Prev: Multiple Record Access,  Up: Database Files

Signals That Are Not Stored in Disk Files
=========================================

   The `fname' component of a `WFDB_Siginfo' object can be any string
acceptable as a file name to your operating system.  Under Unix, for
example, signals can be read from (or written to) `special' files such
as `/dev/rmt0' (the raw tape drive).  If I/O must be performed in
fixed-size blocks (such as for Unix character devices), the `bsize'
component of the `WFDB_Siginfo' object must contain the appropriate
block size in bytes.  In such cases, the WFDB library must obtain
(using `malloc' (see `K&R', page 167) an amount of memory equal to the
size of one block when the signal file is first opened.  For large
programs running on 16-bit machines, this can cause problems if signal
files with large block sizes are read.  (In such cases, `isigopen' or
`osigopen' will not open the signal file if there is not enough memory
to allocate a buffer.)  Under Unix, if this problem occurs, use the
"piped records" (*note Piped and Local Records::) instead.  The usual
method is to read or write the signal file using a utility such as
Unix's `dd' and to pipe the data to or from the application program.
Although this approach is flexible, there are a few drawbacks:

  1. While reading piped input, the standard input cannot be used for
     other purposes by the application program.  Interactive programs
     can avoid problems by opening `/dev/tty' for I/O, however.

  2. Programs that use `isigsettime' or `isgsettime' cannot perform
     backward skips on piped input, and forward skips can be quite slow.

  3. Additional system resources (computation time, process slots, and
     memory) are needed when using pipes, in comparison with the usual
     method of operation.

   Several special-purpose header files allow application programs to
read data directly from 9-track tape.  When the WFDB Software Package
is installed, these files are copied into the `tape' subdirectory of
the system-wide database directory.  The record names associated with
most of these header files (`tape/512', `tape/1024', `tape/4096',
`tape/10240') specify the block size in bytes.  These use 16-bit
format, 250 Hz samples, 12 bit ADC with zero ADC offset, two signals
multiplexed into one, and data to be read from `/dev/rmt0'.  Record
`tape/6144d' uses 8-bit difference format, 6144 bytes/block, and is
otherwise similar to the others.  Records `tape/ahatape' and
`tape/mittape' can be used to read or write an AHA-format signal file
on a 9-track tape that has been positioned to the beginning of the
correct file; the signal file for these is `/dev/nrmt0' (the
non-rewinding raw tape drive).  If the tape density is encoded into the
tape drive name on your system, additional header files may be needed.


File: wpg,  Node: Piped and Local Records,  Next: NETFILES,  Prev: Special Files,  Up: Database Files

Piped and Local Records
=======================

   "Piped record" header files allow application programs to read
signals from the standard input, or write them to the standard output.
Record `8' specifies 8-bit format, a 10-bit ADC, zero ADC offset, and
two signals sampled at 250 Hz, both of which are to be acquired from
the standard input, or written to the standard output.  Record `16'
specifies 16-bit format and a 12-bit ADC, and is otherwise identical to
record `8'.  ADCs from several manufacturers can produce output in the
format specified by record `16'; thus such output can be piped directly
into an application program using record `16'.  Signal files in AHA
format also match these specifications.  Piped records for reading or
writing other numbers of signals are provided in the `pipe'
subdirectory of the system-wide database directory; they are named
`pipe/8xN' and `pipe/16xN', where N is the number of signals (N = 1, 2,
..., 16; piped record header files can be created with larger numbers
of signals (use the existing files as a model).

   Application programs may also read or write signal files in the
current directory using "local record" header files.  Record `16l'
("one-six-ell") specifies up to sixteen format 16 files, and record
`8l' ("eight-ell") specifies up to sixteen format 8 files, named
`data0', `data1', `data2', ..., `dataN' in the current directory.  When
opened using `isigopen' or `wfdbinit', these signal files will be
readable by `getvec' as signals 0, 1, 2, ... 16 respectively.  These
files should be created by the user, with the use of `putvec'.  It is
necessary to create only as many signal files as will be used; if, for
example, only one signal is needed, only `data0' need be created.


File: wpg,  Node: NETFILES,  Next: Annotation Order,  Prev: Piped and Local Records,  Up: Database Files

NETFILES
========

   If the symbol `WFDB_NETFILES' is defined at the time the WFDB
library is compiled, then input files located on remote web (HTTP) and
FTP servers can be read directly.  This capability is implemented using
the World Wide Web Consortium's `libwww' library (which is available on
many of the platforms supported by the WFDB library).  NETFILES
support, if available, is transparent to WFDB applications.  To make
use of this feature, simply link to the NETFILES-enabled WFDB library
(the necessary `libwww' functions will be loaded automatically), and
incorporate one or more URL prefixes in the WFDB path.

   In current versions of the WFDB library, the default WFDB path
(defined in the WFDB library source file `wfdblib.h', and used as the
WFDB path if the WFDB environment variable is undefined) is `.
/usr/database http://www.physionet.org/physiobank/database'.  (The
second component,, after the `.' that specifies the current
directory,may vary, depending on your platform and the choices made
during installation.)  The URL prefix (the third component) points to
PhysioBank, an on-line archive for a wide variety of standard databases
of physiologic signals.  The databases are kept in subdirectories of
`http://www.physionet.org/physiobank/database'.  For example, the
MIT-BIH Polysomnographic Database is kept in
`http://www.physionet.org/physiobank/database/slpdb', so it is possible
to read record `slp37' of that database directly from PhysioBank by
passing `slpdb/slp37' as the RECORD argument to `wfdbinit' (or
`isigopen', `annopen', etc.).

   Current implementations of `libwww' permit input from `http://' URLs
in much the same way that local files are read, provided that the
remote web server supports HTTP 1.1 range requests (most, including
PhysioNet's, do).  This means that it is not necessary to download an
entire file in order to examine part of it, and you may notice little
or no speed difference between local file and network file input for
many applications.  If the remote server does not support range
requests, however, or if input is from an `ftp://' URL, the current
implementation downloads the entire file to memory, so you may notice a
significant startup delay if the file is long and your network
connection is slow, or if the file does not fit into physical memory.

   Currently, NETFILES support is limited to input files; as always,
any output files created by the WFDB library are written into the
current directory, unless the record name contains local path
information.

   NETFILES support was introduced in WFDB library version 10.0.1.


File: wpg,  Node: Annotation Order,  Prev: NETFILES,  Up: Database Files

Annotation Order
================

   WFDB applications may generally assume (and most of them do assume)
that all annotations in any given annotation file are in "canonical
order".  Successful use of `iannsettime' requires that this assumption
be correct.  Early versions of the WFDB library (before version 6.2)
defined canonical order as time order.  More recent versions of the
WFDB library define canonical order as `time' and `chan' order (thus
annotations are arranged first in `time' order, and any simultaneous
annotations are arranged according to the value of their `chan' fields,
from smallest to largest).

   The combination of the `time' and `chan' fields of an annotation
defines a unique "location" in a virtual array of annotations which an
annotation file represents.  No two annotations may occupy the same
location in this virtual array.  This restriction was enforced by
versions of the WFDB library earlier than version 9.7.  In these
versions of the WFDB library, `putann' required that annotations be
written in canonical order, and refused to write any out-of-order
annotations supplied to it.

   Current versions of the WFDB library do not impose this requirement.
In version 9.7 and later versions, `putann' accepts and records
out-of-order annotations and multiple annotations that occupy the same
location.  If any such annotations have been written, the completed
annotation file is rewritten in canonical order by `wfdbquit' or
`oannclose'.  This is accomplished by running `sortann' (see the `WFDB
Applications Guide') as a separate process using the ANSI C `system'
function.  If this function is not available, or if `sortann' cannot be
run, `wfdbquit' (or `oannclose') emits a warning message describing how
to post-process the annotations to put them into canonical order.

   Although it is possible using current versions of the WFDB library
to write two or more annotations to the same location, _only the last
annotation written to any given location is retained_ in the
canonically-ordered annotation file.  Thus that an application that
generates an annotation file can change the `anntyp', `subtyp', `num',
or `aux' fields of a previously-written annotation simply by writing
another annotation to the same location (i.e, with the same `time' and
`chan' fields).  As a special case, an application may _delete_ a
previously-written annotation by writing a `NOTQRS' annotation to the
same location.  To move an annotation to a different location (i.e., to
change its `time' or `chan' fields), it is necessary to delete it from
the original location, and then to insert it at the desired location,
using two separate invocations of `putann'.

   In unusual circumstances, an unsorted annotation file may be useful
(for example, as an aid for debugging the application that produced it;
`rdann' can be used to list all of the annotations in such a file, in
the order in which they were written).  In some environments, the use
of the ANSI C `system' function may be a security problem, and you may
wish to avoid automatic sorting of annotations for this reason.  Set
the environment variable `WFDBANNSORT' to 0 at run time, or define the
symbol `DEFWFDBANNSORT' as 0 when compiling the WFDB library, if you
wish to suppress automatic annotation sorting by `wfdbquit' and
`oannclose'.


File: wpg,  Node: Examples,  Next: Exercises,  Prev: Database Files,  Up: Top

Programming Examples
********************

   The programs in this chapter are useful as models for a variety of
applications that use the WFDB library.  The line numbers are for
reference only; they are not part of the programs.  Any of these
examples can be compiled (under Unix) using a command of the form
     cc FILE.C -lwfdb

where FILE.C is the name of the file containing the source; *note Using
the WFDB Library: Usage., for further information.  The sources for
these examples are included in the WFDB Software Package, within the
`examples' directory.

* Menu:

* Example 1::			An annotation filter.
* Example 2::			An MIT-to-AHA format annotation translator.
* Example 3::			An annotation printer.
* Example 4::			Generating an R-R interval histogram.
* Example 5::			A program that prints signal specifications.
* Example 6::			A differentiator.
* Example 7::			A general-purpose FIR filter.
* Example 8::			Creating a new WFDB record.
* Example 9::			A signal averager.
* Example 10::			A QRS detector.


File: wpg,  Node: Example 1,  Next: Example 2,  Prev: Examples,  Up: Examples

Example 1:  An Annotation Filter
================================

   The following program copies an annotation file, changing all QRS
annotations to `NORMAL' and deleting all non-QRS annotations.

      1  #include <stdio.h>
      2  #include <wfdb/wfdb.h>
      3  #include <wfdb/ecgmap.h>
      4
      5  main()
      6  {
      7      WFDB_Anninfo an[2];
      8      char record[8], iann[10], oann[10];
      9      WFDB_Annotation annot;
     10
     11      printf("Type record name: ");
     12      fgets(record, 8, stdin); record[strlen(record)-1] = '\0';
     13      printf("Type input annotator name: ");
     14      fgets(iann, 10, stdin); iann[strlen(iann)-1] = '\0';
     15      printf("Type output annotator name: ");
     16      fgets(oann, 10, stdin); oann[strlen(oann)-1] = '\0';
     17      an[0].name = iann; an[0].stat = WFDB_READ;
     18      an[1].name = oann; an[1].stat = WFDB_WRITE;
     19      if (annopen(record, an, 2) < 0) exit(1);
     20      while (getann(0, &annot) == 0)
     21          if (isqrs(annot.anntyp)) {
     22              annot.anntyp = NORMAL;
     23              if (putann(0, &annot) < 0) break;
     24          }
     25      wfdbquit();
     26  }

*Notes:*

_Line 2:_
     All programs that use the WFDB library must include
     `<wfdb/wfdb.h>'.

_Line 3:_
     The `#include' statement makes available not only the mapping
     macros, one of which will be used in line 21, but also the
     annotation code symbols in `<wfdb/ecgcodes.h>', one of which will
     be needed in line 22.

_Line 7:_
     Since there will be two annotators (one each for input and output),
     the array of `WFDB_Anninfo' objects has two members.

_Line 9:_
     This structure will be filled in by `getann', modified, and passed
     to `putann' for output.

_Lines 11-16:_
     The record name and the annotator names are filled into the
     character arrays.  The code in lines 12, 14, and 16 illustrates a
     C idiom for reading a string of limited length; the second
     statement in each of these lines replaces the trailing newline
     character (which `fgets' copies into the string) with a null.
     String arguments to WFDB library functions should not include
     newline characters.

_Lines 17-18:_
     Pointers to the character arrays (strings) containing the annotator
     names are filled into the `name' fields of the array of
     `WFDB_Anninfo' objects. Note that the `name' fields are only
     pointers and do not contain storage for the strings themselves.
     If this is not clear to you, review the discussion of pointers and
     arrays in `K&R', pp.  97-100.  The input annotator is to be read,
     the output annotator is to be written.  `WFDB_READ' and
     `WFDB_WRITE' are defined in `<wfdb/wfdb.h>'.

_Line 19:_
     Note that the first and second arguments of `annopen' are the
     names of the respective arrays; thus `annopen' receives pointers
     rather than values in its argument list.

_Line 20:_
     An annotation is read from annotator 0 into `annot'.  The `&' is
     necessary since `getann' requires a pointer to the structure in
     order to be able to modify its contents.  When `getann' returns a
     negative value, no more annotations remain to be read and the loop
     ends.

_Line 21:_
     The macro `isqrs' is defined in `<wfdb/ecgmap.h>'; `isqrs(X)' is
     true if X is an annotation code that denotes a QRS complex, false
     if X is not a QRS annotation code.

_Line 22:_
     `NORMAL' is defined in `<wfdb/ecgcodes.h>'.

_Line 23:_
     The call to `putann' now writes the modified annotation in the
     output annotator 0 file.  As for `getann', a pointer to `annot'
     must be passed using the `&' operator.

_Line 25:_
     All files are closed prior to exiting.  This is mandatory since the
     program creates an output file with `putann'.


File: wpg,  Node: Example 2,  Next: Example 3,  Prev: Example 1,  Up: Examples

Example 2: An Annotation Translator
===================================

   This program translates the `atr' annotations for the record named
in its argument into an AHA-format annotation file with the annotator
name `aha'.

      1  #include <stdio.h>
      2  #include <wfdb/wfdb.h>
      3
      4  main(argc, argv)
      5  int argc;
      6  char *argv[];
      7  {
      8      WFDB_Anninfo an[2];
      9      WFDB_Annotation annot;
     10
     11      if (argc < 2) {
     12          fprintf(stderr, "usage: %s record\n", argv[0]);
     13          exit(1);
     14      }
     15      an[0].name = "atr"; an[0].stat = WFDB_READ;
     16      an[1].name = "aha"; an[1].stat = WFDB_AHA_WRITE;
     17      if (annopen(argv[1], an, 2) < 0) exit(2);
     18      while (getann(0, &annot) == 0 && putann(0, &annot) == 0)
     19          ;
     20      wfdbquit();
     21      exit(0);
     22  }

*Notes:*

_Lines 4-6:_
     If this doesn't look familiar, see `K&R', pp. 114-115.

_Lines 11-14:_
     This is the standard idiom for producing those cryptic error
     messages for which Unix programs are notorious; `argv[0]' is the
     name by which the program was invoked.

_Lines 15-16:_
     These lines set up the annotator information.  Input annotator 0
     is the `atr' annotation file, and output annotator 0 will be
     written in AHA format.

_Line 17:_
     If we can't read the input or write the output, quit with an error
     message from `annopen'.

_Line 18:_
     Here's where the work is done.  The format translation is handled
     entirely by `getann' and `putann'.  The loop ends normally when
     `getann' reaches the end of the input file, or prematurely if
     there is a read or write error.

_Line 21:_
     Since we have carefully defined non-zero exit codes for the various
     errors that this program might encounter, we also define this
     successful exit here.  If this program is run as part of a Unix
     shell script, the exit codes are accessible to the shell, which
     can determine what to do next as a result.  If this line were
     omitted (as in example 1), the exit code would be undefined.


File: wpg,  Node: Example 3,  Next: Example 4,  Prev: Example 2,  Up: Examples

Example 3: An Annotation Printer
================================

   This program prints annotations in readable form.  Its first
argument is an annotator name, and its second argument is a record name.

      1  #include <stdio.h>
      2  #include <wfdb/wfdb.h>
      3
      4  main(argc, argv)
      5  int argc;
      6  char *argv[];
      7  {
      8      WFDB_Anninfo a;
      9      WFDB_Annotation annot;
     10
     11      if (argc < 3) {
     12          fprintf(stderr, "usage: %s annotator record\n", argv[0]);
     13          exit(1);
     14      }
     15      a.name = argv[1]; a.stat = WFDB_READ;
     16      (void)sampfreq(argv[2]);
     17      if (annopen(argv[2], &a, 1) < 0) exit(2);
     18      while (getann(0, &annot) == 0)
     19          printf("%s (%ld) %s %d %d %d %s\n",
     20                 timstr(-(annot.time)),
     21                 annot.time,
     22                 annstr(annot.anntyp),
     23                 annot.subtyp, annot.chan, annot.num,
     24                 (annot.aux != NULL && *annot.aux > 0) ?
     25                  annot.aux+1 : "");
     26      exit(0);
     27  }

*Notes:*
_Line 16:_
     The invocation of `sampfreq' here sets the internal variables
     needed by `timstr' below.

_Line 20:_
     This line gives the annotation time as a time of day.  If the base
     time is omitted in the header file, or if we used
     `timstr(annot.time)' instead, we would obtain the elapsed time from
     the beginning of the record.

_Lines 24-25:_
     This expression evaluates to an empty string unless the `aux'
     string is non-empty.  It makes the assumption that `aux' is a
     printable ASCII string; the printable part follows the length byte.


File: wpg,  Node: Example 4,  Next: Example 5,  Prev: Example 3,  Up: Examples

Example 4: Generating an R-R Interval Histogram
===============================================

   This program reads an annotation file, determines the intervals
between beat annotations (assumed to be the R-R intervals), and
accumulates a histogram of them.

      1  #include <stdio.h>
      2  #include <wfdb/wfdb.h>
      3  #include <wfdb/ecgmap.h>
      4
      5  main(argc, argv)
      6  int argc;
      7  char *argv[];
      8  {
      9      int rr, *rrhist, rrmax;
     10      long t;
     11      WFDB_Anninfo a;
     12      WFDB_Annotation annot;
     13      void *calloc();
     14
     15      if (argc < 3) {
     16          fprintf(stderr, "usage: %s annotator record\n", argv[0]);
     17          exit(1);
     18      }
     19      a.name = argv[1]; a.stat = WFDB_READ;
     20      if (annopen(argv[2], &a, 1) < 0) exit(2);
     21      if ((rrmax = (int)(3*sampfreq(argv[2]))) <= 0) exit(3);
     22      if ((rrhist = (int *)calloc(rrmax+1, sizeof(int))) == NULL) {
     23          fprintf(stderr, "%s: insufficient memory\n", argv[0]);
     24          exit(4);
     25      }
     26      while (getann(0, &annot) == 0 && !isqrs(annot.anntyp))
     27          ;
     28      t = annot.time;
     29      while (getann(0, &annot) == 0)
     30          if (isqrs(annot.anntyp)) {
     31              if ((rr = annot.time - t) > rrmax) rr = rrmax;
     32              rrhist[rr]++;
     33              t = annot.time;
     34          }
     35      for (rr = 1; rr < rrmax; rr++)
     36          printf("%4d %s\n", rrhist[rr], mstimstr((long)rr));
     37      printf("%4d %s (or longer)\n", rrhist[rr], mstimstr((long)rr));
     38      exit(0);
     39  }

*Notes:*
_Lines 21-25:_
     Here we allocate storage for the histogram.  The value returned by
     `sampfreq', if positive, specifies the number of sample intervals
     per second; we will allocate 3 seconds' worth of bins, initialized
     to zero.  See `K&R', page 167, for a description of `calloc'.

_Lines 26-28:_
     This code sets `t' to the time of the first annotated beat in the
     record.

_Lines 29-34:_
     Here we read the remainder of the annotations, skipping any
     non-beat annotations.  The difference between the values of
     `annot.time' for consecutive beat annotations defines an R-R
     interval (`rr').  Each possible value of `rr' up to `rrmax' is
     assigned a bin in `rrhist'.  Intervals longer than 3 seconds
     (`rrmax') are counted in the bin corresponding to `rr' = `rrmax'.

_Lines 35-37:_
     The histogram is printed as a two-column table, with the number of
     intervals in the first column and the length of the interval (with
     millisecond resolution) in the second column. (What happens if
     `rr' starts at 0 rather than 1 in line 35?)


File: wpg,  Node: Example 5,  Next: Example 6,  Prev: Example 4,  Up: Examples

Example 5: Reading Signal Specifications
========================================

   This program reads the signal specifications of the record named as
its argument:

      1  #include <stdio.h>
      2  #include <wfdb/wfdb.h>
      3
      4  main(argc, argv)
      5  int argc;
      6  char *argv[];
      7  {
      8      WFDB_Siginfo *s;
      9      int i, nsig;
     10
     11      if (argc < 2) {
     12          fprintf(stderr, "usage: %s record\n", argv[0]);
     13          exit(1);
     14      }
     15      nsig = isigopen(argv[1], NULL, 0);
     16      if (nsig < 1) exit(2);
     17      s = (WFDB_Siginfo *)malloc(nsig * sizeof(WFDB_Siginfo));
     18      if (s == NULL) {
     19          fprintf(stderr, "insufficient memory\n");
     20	        exit(3);
     21      }
     22      if (isigopen(argv[1], s, nsig) != nsig) exit(2);
     23      printf("Record %s\n", argv[1]);
     24      printf("Starting time: %s\n", timstr(0L));
     25      printf("Sampling frequency: %g Hz\n", sampfreq(argv[1]));
     26      printf("%d signals\n", nsig);
     27      for (i = 0; i < nsig; i++) {
     28          printf("Group %d, Signal %d:\n", s[i].group, i);
     29          printf(" File: %s\n", s[i].fname);
     30          printf(" Description: %s\n", s[i].desc);
     31          printf(" Gain: ");
     32          if (s[i].gain == 0.)
     33              printf("uncalibrated; assume %g", WFDB_DEFGAIN);
     34          else printf("%g", s[i].gain);
     35          printf(" adu/%s\n", s[i].units ? s[i].units : "mV");
     36          printf(" Initial value: %d\n", s[i].initval);
     37          printf(" Storage format: %d\n", s[i].fmt);
     38          printf(" I/O: ");
     39          if (s[i].bsize == 0) printf("can be unbuffered\n");
     40          else printf("%d-byte blocks\n", s[i].bsize);
     41          printf(" ADC resolution: %d bits\n", s[i].adcres);
     42          printf(" ADC zero: %d\n", s[i].adczero);
     43          if (s[i].nsamp > 0L) {
     44              printf(" Length: %s (%ld sample intervals)\n",
     45                     timstr(s[i].nsamp), s[i].nsamp);
     46              printf(" Checksum: %d\n", s[i].cksum);
     47          }
     48          else printf(" Length undefined\n");
     49      }
     50      exit(0);
     51  }

*Notes:*

_Line 15:_
     The command-line argument, `argv[1]', is the record name.  The
     number of signals listed in the header file for the record is
     returned by `isigopen' as `nsig'.  If `nsig' < 1, `isigopen' will
     print an error message; in this case the program can't do anything
     useful, so it exits.

_Line 17:_
     We allocate `nsig' signal information (`WFDB_Siginfo') objects.

_Line 22:_
     On the second invocation of `isigopen', we pass the pointer to the
     signal information objects and the number of signals we expect to
     open.  `isigopen' returns the number of signals it is able to
     open;  if any of those named in the header file are unreadable,
     the return value will not match `nsig', and the program exits.

_Line 24:_
     Invoking `timstr' with an argument of zero (here written `0L' to
     emphasize to the compiler that the argument is a `long' integer)
     will obtain the starting time of the record.  If no starting time
     is defined, `timstr' will return "`0:00:00'".

_Lines 31-34:_
     Notice how a zero value for `gain' is interpreted.

_Line 35:_
     If the `units' field is NULL, the physical units are assumed to be
     millivolts ("mV").

_Lines 38-40:_
     If `bsize' is zero, I/O can be performed in blocks of any
     reasonable size;  otherwise it must be performed in blocks of
     exactly the specified `bsize'.

_Lines 43-48:_
     If the length of the record is defined, it is printed in both
     hours, minutes, and seconds, and in sample intervals.  Since the
     argument of `timstr' in line 39 is positive, it is interpreted as
     a time interval.  The checksum is defined only if the record
     length is defined.


File: wpg,  Node: Example 6,  Next: Example 7,  Prev: Example 5,  Up: Examples

Example 6: A Differentiator
===========================

   The program below inverts and differentiates the signals read by
`getvec' and writes the results with `putvec'.  The output is readable
as record `dif'.  A wide variety of simple digital filters can be
modelled on this example; *note Example 7::, for a more general
approach.

      1  #include <stdio.h>
      2  #include <wfdb/wfdb.h>
      3
      4  main(argc, argv)
      5  int argc;
      6  char *argv[];
      7  {
      8      WFDB_Siginfo *s;
      9      int i, nsig, nsamp=1000;
     10      WFDB_Sample *vin, *vout;
     11
     12      if (argc < 2) {
     13          fprintf(stderr, "usage: %s record\n", argv[0]); exit(1);
     14      }
     15      if ((nsig = isigopen(argv[1], NULL, 0)) <= 0) exit(2);
     16      s = (WFDB_Siginfo *)malloc(nsig * sizeof(WFDB_Siginfo));
     17      vin = (WFDB_Sample *)malloc(nsig * sizeof(WFDB_Sample));
     18      vout = (WFDB_Sample *)malloc(nsig * sizeof(WFDB_Sample));
     19      if (s == NULL || vin == NULL || vout == NULL) {
     20          fprintf(stderr, "insufficient memory\n");
     21          exit(3);
     22      }
     23      if (isigopen(argv[1], s, nsig) != nsig) exit(2);
     24      if (osigopen("8l", s, nsig) <= 0) exit(3);
     25      while (nsamp-- > 0 && getvec(vin) > 0) {
     26          for (i = 0; i < nsig; i++)
     27              vout[i] -= vin[i];
     28          if (putvec(vout) < 0) break;
     29          for (i = 0; i < nsig; i++)
     30              vout[i] = vin[i];
     31      }
     32      (void)newheader("dif");
     33      wfdbquit();
     34      exit(0);
     35  }

*Notes:*

_Line 24:_
     Here we attempt to open as many output signals as there are input
     signals; if we cannot do so, the program exits after `osigopen'
     prints an error message.

_Line 25:_
     The main loop of the program begins here.  If 1000 samples can be
     read from each signal, the loop will end normally; if `getvec'
     fails before 1000 samples have been read, the loop ends
     prematurely.

_Lines 26-27:_
     For each signal, we compute the negated first difference by
     subtracting the new sample from the previous sample.

_Line 28:_
     One sample of each output signal is written here.

_Lines 29-30:_
     The new input samples are copied into the output sample vector in
     preparation for the next iteration.

_Line 32:_
     This step is optional.  It creates a header file for a new record
     to be called `dif', which we can then open with another program if
     we want to read the signals that this program has written.  Since
     the RECORD argument for `osigopen' was `8l', we can also read
     these files using record `8l'; one reason for making a new `hea'
     file here is that the `hea' file for `8l' may not necessarily
     indicate the proper sampling frequency for these signals.

_Line 33:_
     Since the program writes output signals, it must invoke `wfdbquit'
     to close the files properly.

