\input texinfo	@c -*-texinfo-*-
@comment %**start of header (This is for running Texinfo on a region.)
@setfilename dbpg
@settitle ECG Database Programmer's Guide
@setchapternewpage odd
@comment %**end of header (This is for running Texinfo on a region.)

@titlepage
@sp 5
@center @titlefont{ECG Database Programmer's Guide}
@sp 4
@center Ninth Edition (revised and with additions for DB library version 9.7)
@center @today
@sp 5
@center George B. Moody
@sp 1
@center Harvard-MIT Division of Health Sciences and Technology
@center Biomedical Engineering Center
@page
@vskip 0pt plus 1filll
Copyright @copyright{} 1989 -- 1997 Massachusetts Institute of Technology.
@sp 2
For information on obtaining the most recent versions of the software
described in this guide, visit @code{http://ecg.mit.edu}, or write to:

@display
MIT-BIH Database Distribution
Massachusetts Institute of Technology
77 Massachusetts Avenue, Room 20A-113
Cambridge, MA 02139
USA
@end display

An HTML version of this guide is available;  point your web browser to
@code{http://ecg.mit.edu/dbpg/} to view it.
Additional printed copies of this guide are available for US$10 each
(including shipping by surface mail) by writing to the address above.
Please make checks payable to ``Beth Israel Hospital Biomedical
Engineering Division.''

@sp 2
Permission is granted to make and distribute verbatim copies of this
guide provided that the copyright notice and this permission notice are
preserved on all copies.

Permission is granted to copy and distribute modified versions of this
guide under the conditions for verbatim copying and under the conditions
that follow in this paragraph.  Each copy of the resulting derived work
must contain a notice that it is a modified version of this guide.  The
notice must state which edition of this guide was the source for the derived
work, and it must credit the authors of this guide and of the
modifications.  The entire resulting derived work must be distributed
under the terms of a permission notice identical to this one.

Permission is granted to copy and distribute translations of this guide
into another language, under the above conditions for modified versions.

The author would appreciate receiving copies of any modified or
translated versions of this guide for reference purposes.
@end titlepage

@node Top, Overview, (dir), (dir)

@ifinfo
This guide documents the ECG database interface library (the DB
library).  This file contains the text of the Ninth Edition of
the @cite{ECG Database Programmer's Guide} (July, 1995), with
additional material for release 9.6.0 of the DB library.
@end ifinfo

@menu
* Overview::			What is the DB library?
* Recent changes::              New material, not included in the most
                                recent printed edition of this guide.
* Usage::			How to compile and run a program that uses
				the DB library.
* Functions::			Call and return syntax of each function, with
				descriptions and program examples.
* Data Types::		        Annotator and signal information structures,
				and annotation structures.
* Annotation Codes::    	Table of codes, descriptions of mapping macros.
* Database Files::		A description of the standard file types, and
				notes about reading from nonstandard sources.
* Examples::			Annotated example programs.
* Exercises::			Test yourself.
* Glossary::			So you think you know what `time' is, eh?
				A guide for the perplexed.
* Installation::		Notes on installing the DB software package.
* DB Applications::		Brief descriptions of the DB application
				programs provided with the DB software package.
* Extensions::			Notes on extending the capabilities of the
				DB library.
* Sources::			Where to get the DB software package, databases
				of ECGs and other signals, and related items.
Indices

* Concept Index::		An item for each concept.
* Function and Macro Index::	An item for each DB library function and macro.

* Copying::                     You can make copies of this guide.  Here are
                                the terms for doing so.
@end menu

@node     Overview, Recent changes, Top, Top
@comment  node-name,  next,  previous,  up
@unnumbered Preface

@cindex DB library
@cindex MIT DB
@cindex AHA DB

This guide documents the ECG database interface library (the @dfn{DB
library}), a package of C-callable functions that provide clean and
uniform access to digitized, annotated signals stored in a variety of
formats.  These functions were designed for use with the MIT-BIH
Arrhythmia Database (@dfn{MIT DB}) and the AHA Database for the
Evaluation of Ventricular Arrhythmia Detectors (@dfn{AHA DB}).  In
February 1990, the predefined annotation set was expanded to accommodate
the needs of the European ST-T Database (@dfn{ESC DB}).  The DB library
is sufficiently general, however, to be useful for dealing with any
similar collection of digitized signals, which may or may not be
annotated.  The DB library has evolved to support the development of
several databases that include signals such as blood pressure,
respiration, oxygen saturation, EEG, as well as ECGs.  Among these
multi-parameter databases are the MIT-BIH Polysomnographic Database, the
MGH/Marquette Foundation Waveform Database, and the not yet completed
MIMIC Database.  Thus the DB library is considerably more than an
@emph{ECG} database interface.

This guide describes how to write C-language programs that use databases
of ECGs and other signals.  A standard set of such programs is included
in the DB Software Package, and is described in the @cite{ECG Database
Applications Guide}; other documents describe the databases themselves,
and existing programs that use them (@pxref{Sources}, for information
about obtaining these and related items).

There are a few important concepts that should be well understood before
going further.  These concepts include @dfn{records}; @dfn{signals},
@dfn{samples}, and @dfn{time}; and @dfn{annotations}.

@menu
* Concepts 1::			Records (``tapes'') and record names.
* Concepts 2::			Signals, samples, and time.
* Concepts 3::			Annotations and annotation files.

@emph{If this is your first exposure to the DB library,
study the three nodes above before going on.}

* Applications::		Examples of programs based on the DB library.
* Guide::			What's in this guide, and where.
                		What you need to know to get started.
				Acknowledgments, where to send your
				comments, and how to get your very own
				printed copy of this guide.
@end menu

@node     Concepts 1, Concepts 2, Overview, Overview
@unnumberedsec Records
@cindex record
@cindex record name
@cindex tape

The databases for which the DB library was designed consist of a small
number of ECG @dfn{records}, each of which is quite large (typically a
megabyte or more).  Database records usually originate as multi-channel
analog tape recordings that have been digitized and stored as disk
files.  For this historical reason, they are often referred to as
@dfn{tapes}.  Each record contains a continuous recording from a single
subject.  A typical application program accesses only a single record,
and most (if not all) of the access within the record is sequential.
Much less frequently, it may be of interest to compare the contents of
several records, or to select sets of records.  These databases are
therefore qualitatively different from those for which conventional
database management software is written.

@cindex MS-DOS file names
@cindex UNIX file names
Records are identified by @dfn{record names}, which are three-digit
numbers for MIT DB records, four-digit numbers for AHA DB records, and
four-digit numbers prefixed by @samp{e} for ESC DB records.  You may
create database records with names containing letters, digits, and
underscores.  Case is significant in record names that contain letters,
even in environments such as MS-DOS for which case translation is
normally performed by the operating system on file names; thus
@samp{e0104} is the name of a record found in the ESC DB, whereas
@samp{E0104} is not.  A record is comprised of several files, which
contain signals, annotations, and specifications of signal attributes;
each file belonging to a given record normally includes the record name
as part of its name.  (On writable UNIX and Macintosh file systems, the
record name is usually the last part of the file name; the opposite
convention is used on CD-ROM and MS-DOS file systems, because of their
restrictions on the length of file name suffixes.)  A record is an
extensible collection of files, which need not all be located in the
same directory, or even on the same physical device.  Thus it is
possible, for example, to create on a magnetic disk a file of your own
annotations for a record distributed on a CD-ROM, and to treat your file
as part of the record.

@node     Concepts 2, Concepts 3, Concepts 1, Overview
@unnumberedsec Signals, Samples, and Time
@cindex adu
@cindex gain
@cindex physical units
@cindex sample
@cindex sampling frequency
@cindex sample interval
@cindex scales (time and amplitude)
@cindex signal
@cindex units (ADC)
@cindex units (physical)

Signals are commonly understood to be functions of time obtained by
observation of physical variables.  In this guide, a @dfn{signal} is
defined more restrictively as a finite sequence of integer
@dfn{samples}, usually obtained by digitizing a continuous observed
function of time at a fixed @dfn{sampling frequency} expressed in Hz
(samples per second).  The time interval between any pair of adjacent
samples in a given signal is a @dfn{sample interval}; all sample
intervals for a given signal are equal.  The integer value of each
sample is usually interpreted as a voltage, and the units are called
analog-to-digital converter units, or @dfn{adu}.  The @dfn{gain} defined
for each signal specifies how many adus correspond to one @dfn{physical
unit} (usually one millivolt, the nominal amplitude of a normal QRS
complex on a body-surface lead roughly parallel to the mean cardiac
electrical axis).  All signals in a given record are usually sampled at
the same frequency, but not necessarily at the same gain
(@pxref{Multi-Frequency Records}, for exceptions to
this rule).  MIT DB records are sampled at 360 Hz; AHA and ESC DB
records are sampled at 250 Hz.

@cindex sample number
@cindex time
The @dfn{sample number} is an attribute of a sample, defined as the
number of samples of the same signal that precede it;  thus the sample
number of the first sample in each signal is zero.  Within this guide,
the units of @dfn{time} are sample intervals;  hence the ``time'' of a
sample is synonymous with its sample number.

Samples having the same sample number in different signals of the same
record are treated as simultaneous.  In truth, they are usually not
@emph{precisely} simultaneous, since most multi-channel digitizers
sample signals in ``round-robin'' fashion.  If this subtlety makes a
difference to you, you should be prepared to compensate for inter-signal
sampling skew in your programs.

@node     Concepts 3, Applications, Concepts 2, Overview
@unnumberedsec Annotations
@cindex annotation
@cindex annotator
@cindex @code{atruth}
@cindex beat label
@cindex label (beat)
@cindex QRS label
@cindex reference annotations

MIT DB records are each 30 minutes in duration, and are @dfn{annotated}
throughout; by this we mean that each beat (QRS complex) is described by
a label called an @dfn{annotation}.  Typically an @dfn{annotation file}
for an MIT DB record contains about 2000 beat annotations, and smaller
numbers of rhythm and signal quality annotations.  AHA DB records are
either 35 minutes or 3 hours in duration, and only the last 30 minutes
of each record are annotated.  ESC DB records are each 2 hours long, and
are annotated throughout.  The ``time'' of an annotation is simply the
sample number of the sample with which the annotation is associated.
Annotations may be associated with a single signal, if desired.  Like
samples in signals, annotations are kept in time and signal order in
annotation files (but @pxref{Annotation Order} for exceptions to this
rule).  No more than one annotation in a given annotation
file may be associated with any given sample of any given signal.  There
may be many annotation files associated with the same record, however;
they are distinguished by @dfn{annotator names}.  The annotator name
@file{atruth} (under MS-DOS, @file{atr}) is reserved to identify
@dfn{reference annotation files} supplied by the developers of the
databases to document correct beat labels.  You may use other annotator
names (which may contain letters, digits and underscores, as for record
names) to identify annotation files that you create.  You may wish to
adopt the convention that the annotator name is the name of the file's
creator (a program or a person).

Annotations are visible to the DB library user as C structures, the
fields of which specify time, beat type, and several user-definable
variables.  The DB library performs efficient conversions between these
structures and a compact bit-packed representation used for storage of
annotations in annotation files.

@node     Applications, Guide, Concepts 3, Overview
@unnumberedsec Applications

Some typical uses of the DB library are these:

@cindex annotation editor
@cindex waveform editor
@itemize @bullet
@item
A @emph{waveform editor}, such as @code{wave} (@pxref{Sources}), reads the
digitized signals of a database record and displays them with
annotations superimposed on the waveforms.  Such a program allows the
user to select any portion of the signals for display at various scales,
and to add, delete, or correct annotations.

@cindex digital filter
@cindex filter (digital)
@item
@emph{Signal processing programs} (e.g., @pxref{Example 7}) apply digital
filters to the signals of a database record and then record the filtered
signals as a new record.  Similar programs perform sampling frequency
conversion.

@item
@emph{Analysis programs} (e.g., @pxref{Example 10}) read the digitized
signals, analyze them, and then record their own annotations.

@cindex annotation comparator
@cindex comparator (annotation)
@item
An @emph{annotation comparator}, such as @code{bxb}
(@pxref{DB Applications}), reads two or more sets of annotations
corresponding to a given record, and tabulates discrepancies between
them.  If the reference annotations supplied with the database are
compared in this way with annotations produced using an analysis
program, this comparison is a means of establishing the accuracy of the
analysis program's output.
@end itemize

The DB library provides the means for programs such as those described
above to select a database record, read and write signals, read and
write annotations, jump to arbitrary points in the record, and determine
attributes of the signals such as the sampling frequency.  The library
also provides a variety of other more specialized services for programs
that need them.  The library defines an interface between programs and
the database that is sufficiently powerful, general, and efficient to
eliminate the need for @emph{ad hoc} user-written database I/O.

@node     Guide, , Applications, Overview
@unnumberedsec About this Guide

You should have a good grasp of the C language in order to make the best
use of this guide.  If ANSI C prototypes, used here to document the DB
library functions, are unfamiliar to you, see pp. 217--218 in the second
edition of @cite{The C Programming Language} by Kernighan and Ritchie,
Prentice Hall, 1988.  (This is the famous @cite{K&R}; all @cite{K&R}
references in this guide include page numbers for the second edition.
Newcomers to C should have a copy for ready reference while reading this
guide.)  It may also be helpful to have a copy of a database
directory, such as the @cite{MIT-BIH Arrhythmia Database Directory}.
If the entire DB Software Package is available, the @cite{ECG Database
Applications Guide} will be useful as a reference for existing DB
library-based applications;  if you have only a CD-ROM with MS-DOS
binaries, refer to @file{bin.doc} (in the @file{bin} directory of your
CD-ROM) for information on the available applications.

You should have access to a computer that has the DB library and at
least one or two database records on-line.  (If you are installing the
DB library on a new computer for the first time, please read the
installation notes supplied with the DB library first, or
@pxref{Installation, , Installing the DB Software Package}, then return
here.)  You should know how to create a C source file using your
favorite editor, and you should know how to compile it and how to run
the resulting executable program.

Resist all temptation to plunge into the esoteric details of file
formats.  (Those who find such details irresistible will find them in
Section 5 of the @cite{ECG Database Applications Guide}; note, however,
that support for new file formats is added to the DB library from time
to time, so that the information you find there may be incomplete.)  The
DB library provides an efficient means of reading and writing files in
many formats; it is not a trivial task to duplicate it, and time spent
doing so is time that could be spent doing something useful, enjoyable,
or possibly both.  If you really think you need to understand the file
formats in order to translate them into whatever the ECGWhizz Model 666
needs, consider instead writing a format translator using the DB library
to read the files; then you will at least have a program that requires
only recompilation with a new version of the DB library when file
formats change.  @emph{In extremis}, use @file{rdann} and @file{rdsamp} ---
MS-DOS executables of which are included on all CD-ROM databases of ECGs ---
to translate files into text format.

Chapter 1 of this guide begins with a simple example program that reads
a few samples from a database record.  This example should help you
understand the mechanics of compiling and using a program that does
something with an ECG database.  Chapter 2 introduces the library
functions themselves, with a number of brief examples; you may wish to
skim through this material on a first reading to get acquainted with
what is available, and then refer to it as needed while writing your
programs.  Data structures for annotations and for signal and annotator
attributes are described in chapter 3.  Chapter 4 contains a table of
annotation types and descriptions of several annotation-mapping macros.
Database files and related topics are discussed in chapter 5, which can
be skipped on a first reading.  Chapter 6 contains additional example
programs that illuminate a few of the darker corners of the DB library.
The glossary defines the ordinary-sounding words such as @dfn{signal}
that have specialized meanings in this guide; such words are
@dfn{emphasized} in their first appearances in order to warn you that
you should look them up in the glossary on a first reading
(@pxref{Glossary}).

If the DB library has not yet been installed on your system,
@pxref{Installation, , Installing the DB Software Package}.
Another appendix (@pxref{DB Applications}) includes brief
descriptions of the application programs that are distributed with the
DB library as part of the DB software package.

@cindex MS-DOS file names
@cindex operating systems (supported)
The DB library has been written with portability in mind.  It runs on a
wide variety of machines and operating systems, including UNIX (BSD 4.x,
System V, SunOS, Solaris, HP-UX, OSF/1, Version 7, XENIX, VENIX, ULTRIX,
Linux, AIX, AUX, SCO, Coherent, and more), MS-DOS, MS-Windows, VMS, and
the Macintosh OS.  This guide was written for UNIX users (with notes for
MS-DOS users where differences exist), but others should find only minor
differences.  One obvious difference between MS-DOS and other versions
of the DB library is the file-naming convention: since record names can
be longer than the three characters permitted in MS-DOS file name
``extensions'', MS-DOS names for DB files @emph{begin} with the record
name and @emph{end} with a file type specifier (@file{hea} instead of
@file{header}, @file{atr} instead of @file{atruth}, etc.), rather than
the other way around.  This difference is not particularly significant,
however, since DB file names @emph{per se} are never needed as DB
library function arguments.  So that database files on CD-ROMs may be
read on all supported systems, the MS-DOS convention for naming DB files
is recognized in addition to the ``native'' convention under UNIX, VMS,
and on the Macintosh.

Another appendix discusses porting the DB library to new machines or
operating systems, and includes notes on adding support for new file
formats, annotation codes, and other enhancements (@pxref{Extensions}).
At the end of the guide is a list of sources for databases and other
materials that may be useful to readers (@pxref{Sources}).

Many friends have contributed to the development of the DB library.  Thanks to
Paul Albrecht, Ted Baker, Phil Devlin, Scott Greenwald, David Israel, Roger
Mark, Joe Mietus, Warren Muldrow, and especially to Paul Schluter, whose
elegant 8080 assembly language functions inspired these (long live
@code{getann}!).  Pat Hamilton and Bob Farrell contributed ports, to the
Macintosh and the MS 32-bit Windows environments, respectively.  Thanks also
to the many readers of earlier versions of this guide; if this edition answers
your questions, it is because someone else has already asked them, and hounded
the author until he produced comprehensible answers.

The first edition of this guide was written as a tutorial for MIT
students using the ECG databases for a variety of signal-processing and
analysis projects.  The guide, and the DB library itself, have been
extensively revised since they first appeared in 1981.  Your comments
and suggestions are welcome.  Please send them to:

@display
George B. Moody
MIT Room 20A-113
Cambridge, MA 02139
USA

(e-mail: george@@hstbme.mit.edu)
@end display

@iftex
@cindex Emacs Info
@cindex GNU emacs
@cindex Info (GNU emacs)
If you use the GNU @code{emacs} editor, you can peruse a hypertext
version of this guide using @code{info} if it has been installed on your
system; among its many other features, @code{emacs} makes it easy to
copy code from the examples into your own programs.  Installation
instructions are included in the DB Software Package; type @kbd{C-h i}
within GNU @code{emacs} to start up @code{info} (@pxref{Sources}, for
information about obtaining GNU @code{emacs}).
@end iftex

An HTML version of this guide, suitable for viewing using any web browser,
is included with the DB Software Package.  The latest version may always be
viewed at @code{http://ecg.mit.edu} using your web browser.

@ifinfo
You can format and print copies of this guide using TeX if you have it (see
@file{makefile} in the @file{doc} directory of the library distribution for
instructions on doing so).  You may obtain a preformatted version suitable for
output on any PostScript device from @code{http://ecg.mit.edu}, or you can
obtain nicely printed copies for $10 each from the author (address above;
please make checks payable to ``Beth Israel Hospital Biomedical Engineering
Division'').
@end ifinfo

@node     Recent changes, Usage, Overview, Top
@comment  node-name,  next,  previous,  up
@unnumberedsec Recent changes

This node contains a brief summary of changes to the DB library and to this
guide since the first printing of the ninth edition of this guide in July,
1995.  See @file{NEWS.TXT} and @file{NEWNEWS.TXT}, in the top-level
directory of the DB Software Package distribution, for details on these
changes, and for information on any more recent changes that may not be
described here.

@unnumberedsubsec Changes in version 9.7

Support for compiling the DB library as a 32-bit MS Windows DLL has been
added (thanks to Bob Farrell).  Define the symbols @code{_WIN32} and 
@code{_WINDLL} when building the DLL, and define the symbol @code{_WIN32}
when compiling an application that uses the DLL.

The string @samp{%r} is replaced by the record name wherever it appears in the
DB path (@pxref{DB path}.  The string @samp{%@var{N}r}, where @var{N} is
a non-zero digit, is replaced by the first @var{N} characters of the
record name.  When followed by any other character, @samp{%} is an
`escape';  thus, for example, to insert a literal @samp{%} in the DB
path, use @samp{%%}.

It is now possible to write annotations out-of-order using @code{putann}
(@pxref{Annotation Order}).

@unnumberedsubsec Changes in version 9.6.2

Version 5.0 and later versions of Apple's ISO 9660 CD-ROM driver for the
Macintosh handle version number suffixes more sensibly than do earlier
versions.  When compiling the DB library on the Macintosh, the new driver
behavior is assumed by default;  if you are still using a pre-5.0 version
of this driver, define the symbol FIXISOCD when compiling the DB library.

@unnumberedsubsec Changes in version 9.6.1

A new annotation type (@code{LINK}) has been defined
(@pxref{Annotation Codes}).

@unnumberedsubsec Changes in version 9.6

A new function was added to the DB library (@pxref{getspf}).

A number of changes in the implementation of existing DB library functions now
make use of high-resolution mode (@pxref{Multi-Frequency Records}) almost
completely transparent to DB applications.  The recommended practice is to call
@code{setgvmode(DB_HIGHRES)} @emph{before} calling @code{annopen},
@code{dbinit}, @code{getvec}, @code{sampfreq}, @code{strtim}, or @code{timstr}.
By doing so, all @code{DB_Time} data visible to the application are in units of
the high-resolution sampling intervals(@pxref{setgvmode}).

Note that the resolution of annotation times in annotation files created while
in high-resolution mode remains that of the low-resolution sampling intervals.
This may change in a future version of the DB library.

@unnumberedsubsec Changes in version 9.5

The number of signals that may be read simultaneously, @code{DB_MAXSIG},
was increased from 16 to 32.

@unnumberedsubsec Changes in version 9.4.2

Support for use of the DB library with Fortran programs was added
(@pxref{other languages}).

@unnumberedsubsec Changes in version 9.4.1

In release 9.4.0 only, DB library function @code{setheader} did not properly
interpret the skew setting made by @code{dbsetskew} in the case of an
oversampled signal in a multi-frequency record.  This has been corrected.

@unnumberedsubsec Changes in version 9.4

Four new functions were added to the DB library to simplify management of
records with signal files containing prologs or intersignal skews.

@pxref{dbgetskew}

@pxref{dbsetskew}

@pxref{dbgetstart}

@pxref{dbsetstart}

Also, @pxref{Glossary}, for definitions of @dfn{prolog} and @dfn{skew}.

@code{newheader} and @code{setheader} now write header files containing skew
and byte offset fields if you have previously set non-zero values for these
parameters using @code{dbsetskew} or @code{dbsetstart}.

If @code{dberror} is invoked before any errors have occurred, the character
string it returns now contains the DB library version number.  Versions earlier
than 9.4 returned an empty string in this case.

@node     Usage, Functions, Recent changes, Top
@chapter Using the DB Library

This chapter gives a brief overview of the steps needed to compile,
load, and run a program that uses the DB library.  It assumes that you
are able to log onto a UNIX-based computer on which the DB Software
Package has been installed
(@pxref{Installation, , Installing the DB Software Package}),
and that you know how to create a source file using a text editor such
as @code{emacs} or @code{vi}.  If you are using an MS-DOS system, there
are a few differences noted below.


@menu
* print samples::		A trivial example program.
* compiling::			Compiling a C program with the DB library.
* other languages::             Using the DB library with C++ or Fortran.
* DB path::			The database path (environment variable DB).
* running example::		Running the example program.
* name restrictions::		Names to avoid when writing your programs.
* exercises 1::			A few questions.
@end menu

@node     print samples, compiling, Usage, Usage
@section A Trivial Example Program

Suppose we wish to print the first ten samples of record @file{100s}.
(Record @file{100s} is the first minute of MIT-BIH Arrhythmia Database
record @file{100}, supplied as a sample with all source distributions of the
DB Software Package.  If you have only the MS-DOS binary version of the
DB library on a CD-ROM, substitute the name of one of the records on
your CD-ROM for @file{100s} in the program below.)  We might begin by
creating a source file called @file{psamples.c} that contains:

@example
#include <ecg/db.h>

main()
@{
    int i;
    DB_Sample v[2];
    DB_Siginfo s[2];

    if (isigopen("100s", s, 2) < 1)
        exit(1);
    for (i = 0; i < 10; i++) @{
        if (getvec(v) < 0)
            break;
        printf("%d\t%d\n", v[0], v[1]);
    @}
    exit(0);
@}
@end example

All programs that use the DB library @emph{must} include the
statement

@example
#include <ecg/db.h>
@end example

@noindent
which defines function interfaces, data types (such as the
@code{DB_Sample} and @code{DB_Siginfo} types used in this example), and
a few useful constants.  (Most MS-DOS C compilers accept @samp{/} as a
directory separator.  If you prefer to use the non-portable @samp{\}
under MS-DOS, remember to quote it: @samp{#include <ecg\\db.h>}.)

The functions used in the example are described in detail in the next
chapter, and the data types are described in the following chapter
(@pxref{Data Types}).  For now, note that @code{isigopen} prepares a
record to be read by @code{getvec}, which reads a sample from each of
the two signals each time it is called.

@node     compiling, other languages, print samples, Usage
@comment  node-name,  next,  previous,  up
@section Compiling a Program with the DB Library
@cindex DB library (compiling with)
@cindex compiling
@cindex loader options

To compile the example program on a UNIX system, we can say:

@example
cc -o psamples psamples.c -ldb
@end example

@noindent
to produce an executable program called @code{psamples}.  You may use any
other compiler options you choose, but the @samp{-ldb} option must
appear in the @code{cc} command line following any and all source
(@file{*.c}) and object (@file{*.o}) file names, in order to instruct
the loader to search the DB library for any functions that the program
needs (in this case, @code{isigopen} and @code{getvec}).  Some programs
will need additional libraries, and the corresponding @samp{-l} options
can usually be given before or after the @samp{-ldb} option.

Each MS-DOS C or C++ compiler has its own idiosyncratic syntax, so no general
rule can be given for MS-DOS.  With Microsoft C/C++, use:

@example
cl psamples.c -link db
@end example

@noindent
With Borland C/C++, use:

@example
bcc -L@var{LIBDIR} psamples.c db.lib
@end example

@noindent
where @var{LIBDIR} is the directory in which @samp{db.lib} (the DB
library) has been installed.  (Substitute @samp{tcc} for @samp{bcc} if
you are using Turbo C or C++.)  See your compiler manual for further
information.

@node     other languages, DB path, compiling, Usage
@comment  node-name,  next,  previous,  up
@unnumberedsubsec Using the DB library with other languages
@cindex C++ bindings

If you prefer to write your applications in C++, you may do so, but note
that the DB library is written in C.  (Most C++ compilers can be run in
ANSI/ISO C compatibility mode in order to compile the DB library
itself.)  Each C++ source file that uses DB library functions must
include @file{<ecg/db.h>}, in order to instruct your compiler to use C
conventions for argument passing and to use unmangled names for the DB
library functions.  In order for this to work, your C++ compiler should
predefine @samp{__cplusplus} or @samp{c_plusplus}; if it predefines
neither of these symbols, modify @file{<ecg/db.h>} so that the symbols
@samp{db_CPP} and @samp{db_PROTO} are defined at the top of the file.
Compile and link your program using whatever standard methods are
supported by your compiler for linking C++ programs with C libraries.
See your compiler manual for further information.

@cindex Fortran bindings
@cindex wrappers for Fortran
A set of wrapper functions is also available for those who wish to use
the DB library together with applications written in Fortran.  These
functions provide a thin `wrapper' around the DB library functions, by
accepting Fortran-compatible arguments (there are no structures, and all
arguments are passed by reference rather than by value).  You will still
need a C or C++ compiler to compile the DB library and the wrapper
functions themselves.  The Fortran wrapper functions are not discussed
in this guide; for further information, refer to @file{fortran/readme.doc}
in the DB Software Package (included in version 9.4 and later versions).

@node     DB path, running example, other languages, Usage
@comment  node-name,  next,  previous,  up
@section The Database Path
@cindex database path (setting)
@cindex path (database)
@cindex directories for DB files
@cindex DB files (finding)
@cindex finding DB files
@cindex @code{DB} (environment variable)

When DB applications @emph{read} database files, they must be able to
find them in various directories that may vary from system to system.
The DB library refers to a character string that consists of an ordered
list of directories to be searched @emph{for input files}.  This string
is called the @dfn{database path}, or the @dfn{DB path}.  Under UNIX and
VMS, the DB path is a colon-separated list of prefixes, in the format
used for the Bourne shell's @code{PATH} variable.  Under MS-DOS and on
the Macintosh, the DB path is in the format used for the MS-DOS
@code{PATH} variable, with semicolons used to separate prefixes (colons
retain their customary meanings, as drive letter suffixes under MS-DOS,
or as directory separators on the Macintosh).  When a DB file must be
opened for input, the DB library attempts to locate it by attaching each
of the components of the DB path (one at a time) as a prefix to the file
name.  If two or more matching files exist in different directories in
the DB path, the DB library opens only the file that resides in the
first of these directories named in the DB path.  Any other matching
files are effectively invisible to DB applications unless the DB path is
rearranged.

When DB applications @emph{write} database files, these files are
generally written to the current directory.  (As an example, an
application that analyzes one or more signals in a record may record its
findings in an annotation file in the current directory.)

Note particularly that the current directory is @emph{not} necessarily
part of the DB path.  In order to be sure that your DB applications can
read any DB files that you have previously written in the current
directory, be sure that your DB path includes an empty (null) component,
which corresponds to the current directory.  In most cases, this null
component should be the first in the DB path.  Thus, if you write into
the current directory a modified version of an existing DB file, any
later actions that would read this file will read your modified version
rather than the original.

@cindex database path (default)

A default DB path may be specified at the time the DB library is
compiled, by defining a value for the symbol @code{DEFDBP} in
@file{dblib.h}.  On most systems, the environment variable @code{DB}, if
set, specifies the value of the DB path, and overrides the default value
if any.  On the Macintosh, for which the concept of environment
variables is foreign, the DB path may be set only by using
@code{DEFDBP}.

@cindex indirect DB path
@cindex file containing DB path
@cindex database path file (indirect)

If the DB library finds that the value assigned to the DB path is of the
form @samp{@@@var{file}}, it replaces that value with the contents of
the specified @var{file}.  (This feature was first introduced in DB
library version 8.0.)  Indirect DB path files may be nested up to ten
levels (this arbitrary limit is imposed to avoid infinite recursion if
the contents of the indirect file are incorrect).  This method of
indirect assignment is useful on the Macintosh, where recompilation of
the DB library would otherwise be necessary in order to change the DB
path.  It may also be useful under MS-DOS to reduce the need for
environment space, or if the length of the command needed to set the
@code{DB} environment variable would otherwise approach or exceed the
128-byte limit for MS-DOS commands.

If a DB header file (@pxref{Database Files}) specifies that a signal
file is to be found in a directory that is not already in the DB path,
that directory is appended to the end of the DB path; in this case, if
the DB path is not set, it is created with an initial null component
followed by the directory that contains the signal file.  (This feature
was first introduced in DB library version 6.2.)

The string @samp{%r} is replaced by the current record name wherever it appears
in the DB path;  @samp{%@var{N}r} is replaced by the first @var{N}
digits of the record name, if @var{N} is a non-zero digit.  For example,
if (under UNIX) the DB path is @samp{:/cdrom/mimicdb/%3r:/cdrom/mitdb},
a request to read a file associated with record 055n will cause the DB
library to look first in the current directory (since the DB path begins
with an empty component), then in @samp{/cdrom/mimicdb/055}, and then in
@samp{/cdrom/mitdb}.  If @samp{%} is followed by any character other than
@samp{r} or a non-zero digit followed by @samp{r}), that character is used
as is in the DB path;  thus a literal @samp{%} can be included in the DB
path by `escaping' it as @samp{%%}.  (Substitutions of @samp{%}-strings
in the DB path were first introduced in DB library version 9.7.)

Before the example program can be run successfully, @code{DB} must be
set appropriately and placed in the environment where it can be examined
by the running program.  If you use the Bourne shell (@code{sh}), the
GNU Bourne Again shell (@code{bash}), or the Korn shell (@code{ksh}), do
so by typing

@example
. setdb
@end example

@noindent
@cindex @code{.profile}
@cindex @code{setdb}
(or by placing this command in your @file{.profile}, which will cause
@code{DB} to be set automatically whenever you log in).  If you use the
C-shell (@code{csh}) or a variant of it (such as @code{ecsh} or @code{tcsh}),
type

@example
source /usr/local/bin/cshsetdb
@end example

@noindent
@cindex @code{.cshrc}
@cindex @code{cshsetdb}
(or put this command in your @file{.cshrc}).  These shell scripts should
be available on any system on which the DB library itself is available,
although they may not be in @file{/usr/local/bin} as shown above.  If
you cannot find the script you need, consult an expert (such as the
person who installed the DB library on your system).  Note that the
default value for @code{DB} obtained in this way begins with the current
directory, and includes all standard directories in which database
records are kept.  Thus, if you create your own database records or
files, you may keep them in your own directory without special
arrangements.

Under MS-DOS, you may set @code{DB} using the batch file
@file{dossetdb.bat} (created during installation of the DB Software
Package), or you may include the contents of that file in your
@file{autoexec.bat} file, which will cause @code{DB} to be set automatically
whenever your system is rebooted.  Since environment space under
MS-DOS is an operating system resource and is often in very short
supply, you may need to allocate additional environment space within
your @file{config.sys} file, with a line of the form

@example
shell=c:\command.com /p /e:512
@end example

@noindent
See your MS-DOS manual for further information.

@cindex calibration file
@cindex @code{DBCAL} (environment variable)
These shell scripts usually set the @code{DBCAL} environment variable,
which is important if you make use of records that contain signals other
than ECGs.  @code{DBCAL} names a @dfn{calibration file} located in one
of the directories named by @code{DB}.  (Beginning with DB library
version 8.3, the symbol @code{DEFDBC} may be defined in @file{dblib.h}
to specify the name of a default calibration file, to be used by the DB
library if @code{DBCAL} has not been set.)  Each signal type may be
represented by an entry in the calibration file.  Entries specify the
characteristics of any calibration pulses that may be present, and
customary scales for plotting the signals.

@node     running example, name restrictions, DB path, Usage
@section Running the Example Program

If @code{DB} is properly set, MIT DB record @file{100s} is on-line and
readable, and the example program was compiled correctly, it can be run
by typing

@example
psamples
@end example

@noindent
and its output will appear as:

@example
995     1011
995     1011
995     1011
995     1011
995     1011
995     1011
995     1011
995     1011
1000    1008
997     1008
@end example

The left column contains samples from signal 0, and the right column
contains those from signal 1.

@node     name restrictions, exercises 1, running example, Usage
@section A Note on Identifiers
@cindex function name restrictions
@cindex external identifiers (restrictions)
@cindex restrictions on function and variable names
@cindex variable name restrictions

External identifiers that begin with the underscore (@samp{_}) character
are reserved under the rules of ANSI C to the compiler and libraries.
In order to make the DB library as portable as possible, its own external
identifiers do not begin with underscores (since otherwise they might
conflict with external identifiers used by a standard library).

External identifiers beginning with @samp{db_} are reserved for the use
of the DB library.  These names are used for functions, global variables
that are intended for the private use of the DB library; your programs
should not need to use them.  You should avoid defining functions or
global variables with such names in your programs.

External identifiers beginning with @samp{DB_} are used for constants
and data types defined within @file{<ecg/db.h>}.  Use these identifiers
as needed in your programs, but avoid redefining them.

@node     exercises 1, , name restrictions, Usage
@section Exercises

These exercises should require only a few minutes.  If you work through
them, you will have an opportunity to become acquainted with a few of
the most common errors in using the DB library.

@enumerate
@item
Find out where database records are kept on your system.  What records
are available?

@item
Run the example program in this chapter @emph{without} setting the DB
path.  Try doing so from your home directory, and from the directory
where record @file{100s} is kept.  Can you explain the results?

@item
What happens when you compile the example program as shown, but with the
@code{#include} statement omitted?  with the @samp{-ldb} (@samp{-link db},
etc.) omitted?

@item
What is the type of the argument to @code{getvec}?  Why can't @code{getvec}
simply return the value it reads, as in @samp{v = getvec()}?
@end enumerate

@node     Functions, Data Types, Usage, Top
@chapter DB Library Functions
@cindex DB library functions
@cindex library functions
@cindex functions in the DB library

This chapter describes the functions that are available to programs
compiled with the @samp{-ldb} option.  The functions are introduced in
several groups, with examples to illustrate their usage.

@menu
* introduction to functions::	General notes on functions.
				This node discusses arguments, return codes,
				and the organization of this section of
				the guide.

The remainder of the nodes in this section describe functions for:
				
* selecting::			Selecting database records (opening files).
* signal and annotation I/O::	Reading and writing signals and annotations.
* non-sequential::		Non-sequential access to DB files.
* conversion::			Time and other conversion functions.
* calibration::			Calibrating signals.
* miscellaneous functions::	Attribute-reading and other functions.
@end menu

@node     introduction to functions, selecting, Functions, Functions
@unnumberedsec About these functions

@cindex arguments
@cindex function arguments
@cindex pointer arguments
Each function description begins with an ANSI C function prototype, which
specifies the types of any arguments as well as the type of the quantity
returned by the function (see @cite{K&R}, pp. 217--218).  Note that many
of these functions take pointer arguments.  These can be traps for
newcomers to C.  Study the examples carefully!  Often a function will
return information to the caller in a variable or structure to which the
pointer argument points.  @strong{It is necessary in such cases for the
caller to allocate storage for the variables or structures and to
initialize the pointers so that they point to the allocated storage.  If
you fail to do so, the compiler probably will not warn you of your
error; instead your program will fail mysteriously, probably with a core
dump and an ``illegal memory reference'' error message.}

@cindex errors
@cindex function return codes
@cindex return codes
With few exceptions, DB library functions return integers that indicate
success or failure.  The tables that follow the function prototypes
list the possible returns and their meanings.  By convention, a
return code of
@iftex
@minus{}1
@end iftex
@ifinfo
-1
@end ifinfo
indicates end-of-file on input files, and no
error message is printed.  Other negative return codes signify other
types of errors, and are usually accompanied by descriptive messages on
the standard error output (but
@iftex
@pxref{dbquiet and dbverbose, , @code{dbquiet} and @code{dbverbose}}).
@end iftex
@ifinfo
@pxref{dbquiet and dbverbose}).
@end ifinfo
Zero may indicate success or failure, depending on context (see the
descriptions of the individual functions below).  Positive codes
(returned by only a few functions) always indicate success.

A comprehensive discussion of database files appears later in this guide
(@pxref{Database Files}).  Most readers should not need to learn about
the gruesome details of how the data are actually stored.  You should
know, however, that there are files that contain digitized signals,
other files that contain annotations, and still others (called
@file{header} files) that describe attributes of the signals such as
sampling frequency.  The database path lists directories in which
database files are found; the DB library functions can find them given
only the record (and annotator) names, provided that @code{DB} has been
properly set (@pxref{DB path}).  DB library functions responsible for
opening signal files find them by reading the @file{header} file (which
contains their names) first.

The first part of this chapter describes functions that extract
information from @file{header} files in order to gain access to signal
and annotation files.  The following two sections describe functions
that deal with signal and annotation files.  Many readers will not need
to go any further; the remaining sections deal with special-purpose
functions that exist to serve unusual applications.

@node selecting,signal and annotation I/O,introduction to functions,Functions
@section Selecting Database Records
@cindex selecting database records
@cindex opening database files
@cindex initialization

@menu
* annopen::		Opening input and output annotation files.
* isigopen::		Opening input signal files.
* osigopen::		Opening output signal files.
* osigfopen::		Opening output signal files by name.
* dbinit::		@code{annopen} and @code{isigopen} in one function.
@end menu

@c @group
@node     annopen, isigopen, selecting, selecting
@unnumberedsubsec annopen
@findex annopen
@cindex annotation files (opening)
@cindex creating annotation files
@cindex opening annotation files

@example
int annopen(char *@var{record}, DB_Anninfo *@var{aiarray}, unsigned int @var{nann})
@end example
@noindent
@strong{Return:}
@table @asis
@item @t{ 0}
Success
@item @t{-3}
Failure: unable to open input annotation file
@item @t{-4}
Failure: unable to open output annotation file
@item @t{-5}
Failure: illegal @code{stat} (in @var{aiarray}) specified for annotation file
@end table
@c @end group

@noindent
This function opens input and output annotation files for a selected
record.  If @var{record} begins with @samp{+}, previously opened
annotation files are left open, and the record name is taken to be the
remainder of @var{record} after discarding the @samp{+}.  Otherwise,
@code{annopen} closes any previously opened annotation files, and takes
all of @var{record} as the record name.  @var{aiarray} is a pointer to
an array of @code{DB_Anninfo} structures
@iftex
(@pxref{DB_Anninfo structures, , Annotator Information Structures}),
@end iftex
@ifinfo
(@pxref{DB_Anninfo structures}),
@end ifinfo
one for each annotator to be opened.  @var{nann} is the number of
@code{DB_Anninfo} structures in @var{aiarray}; @var{nann} must be no
greater than twice @code{DB_MAXANN} (a constant defined in
@file{<ecg/db.h>}), and no more than @code{DB_MAXANN} input and
@code{DB_MAXANN} output annotators may be open at once.  The caller must
fill in the @code{DB_Anninfo} structures to specify the names of the
annotators, and to indicate which annotators are to be read, and which
are to be written.  Input and output annotators may be listed in any
order in @var{aiarray}.  @dfn{Annotator numbers} (for both input and
output annotators) are assigned in the order in which the annotators
appear in @var{aiarray}.  For example, this code fragment

@example
@dots{}
char *record = "100s";
DB_Anninfo a[3];

a[0].name = "a"; a[0].stat = READ;
a[1].name = "b"; a[1].stat = WRITE;
a[2].name = "c"; a[2].stat = READ;
if (annopen(record, a, 3) < 0)
@dots{}
@end example

@noindent
attempts to open three annotation files for record @file{100s}.
Annotator @file{a} becomes input annotator 0, @file{b} becomes output
annotator 0, and @file{c} becomes input annotator 1.  Thus
@code{getann(1, &annot)}
@iftex
(@pxref{getann, , @code{getann}})
@end iftex
@ifinfo
(@pxref{getann})
@end ifinfo
will read an annotation from annotator @file{c}, and
@code{putann(0, &annot)} will write an annotation for annotator @file{b}.
Input annotation files will be found if they are located in any of the
directories specified by @code{DB} (@pxref{DB path}); output annotators
are created in the current directory (but note that, under UNIX at
least, it is possible to specify annotator names such as @file{/here} or
@file{zzz/there} or even @file{../somewhere/else}; @pxref{Annotation
Files}, for details of how file names are constructed from annotator and
record names).  Several of the example programs in chapter 6 illustrate
the use of @code{annopen}; for example, @pxref{Example 1}.

As a special case, if @var{nann} is 0, @var{aiarray} can be @code{NULL}.
This can be useful to force open annotation files to be closed without
closing open signal files.

@c @group
@node     isigopen, osigopen, annopen, selecting
@unnumberedsubsec isigopen
@findex isigopen
@cindex signal files (opening)
@cindex opening signal files

@example
int isigopen(char *@var{record}, DB_Siginfo *@var{siarray}, int @var{nsig})
@end example
@noindent
@strong{Return:}
@table @asis
@item @t{>0}
Success: the returned value is the number of input signals (i.e., the
number of valid entries in @var{siarray})
@item @t{ 0}
Failure: no input signals available
@item @t{-1}
Failure: unable to read @file{header} file (probably incorrect record
name)
@item @t{-2}
Failure: incorrect @file{header} file format
@end table
@c @end group

@noindent
This function opens input signal files for a selected record.  If
@var{record} begins with @samp{+}, previously opened input signal files
are left open, and the record name is taken to be the remainder of
@var{record} after discarding the @samp{+}.  Otherwise, @code{isigopen}
closes any previously opened input signal files, and takes all of
@var{record} as the record name.  If the record name is @samp{-},
@code{isigopen} reads the standard input rather than a @file{header}
file.  @var{Siarray} is a pointer to an array of @code{DB_Siginfo}
structures
@iftex
(@pxref{DB_Siginfo structures, , Signal Information Structures}),
@end iftex
@ifinfo
(@pxref{DB_Siginfo structures}),
@end ifinfo
one for each signal to be opened.  The number of
@code{DB_Siginfo} structures in @var{siarray}, @var{nsig}, need never be
greater than @code{DB_MAXSIG} (a constant defined in @file{<ecg/db.h>});
@code{isigopen} normally returns the number of input signals it actually
opened, which may be less than @var{nsig} but is never greater than
@var{nsig}.  The caller must allocate storage for the @code{DB_Siginfo}
structures; @code{isigopen} will fill them in with information about the
signals.  @dfn{Signal numbers} are assigned in the order in which
signals are specified in the @file{header} file for the record; on
return from @code{isigopen}, information for signal @var{i} will be
found in @var{siarray[i]}.  For example, we can read the @code{gain}
attributes of each signal in record @file{100s} like this:

@c @group
@example
@dots{}
static DB_Siginfo s[DB_MAXSIG];
int i, nsig;

nsig = isigopen("100s", s, DB_MAXSIG);
for (i = 0; i < nsig; i++)
    printf("signal %d gain = %g\n", i, s[i].gain);
@dots{}
@end example
@c @end group

@noindent
Notice that the @code{for} loop is never executed if @code{isigopen}
encounters an error, since @code{nsig} is less than 1 in such cases.
The array of @code{DB_Siginfo} structures is declared @code{static}, a
practice that is recommended for all arrays and data structures of any
substantial size, to avoid run-time stack overflow.

An error message is produced if @code{isigopen} is unable to open
@emph{any} of the signals listed in the @file{header} file.  It is not
considered an error if only some of the signals can be opened, however.
A signal will not be opened if its signal file is unreadable, if an
input buffer cannot be allocated for it, or if opening all of the
signals in its group would exceed the limits defined by @var{nsig} or
@code{DB_MAXSIG}.  (On existing CD-ROMs containing database records, there is
only one signal group for each record; as a consequence, @code{isigopen}
fails if @var{nsig} is less than the number of available signals while
attempting to read a CD-ROM record.)  If necessary, the caller can
inspect the file names and signal descriptions in @var{siarray} to
determine which signals were opened; 
@iftex
@pxref{DB_Siginfo structures, , Signal Information Structures}.
@end iftex
@ifinfo
@pxref{DB_Siginfo structures}.
@end ifinfo
Several of the example programs in chapter 6
illustrate the use of @code{isigopen}; for example, @pxref{Example
5}.

If @var{nsig} is not positive, @code{isigopen} fills in up to
@iftex
@minus{}@var{nsig}
@end iftex
@ifinfo
-@var{nsig}
@end ifinfo
members of @var{siarray}, based on information from the @file{header} file
for @var{record}, but @emph{no signals are actually opened}.  The value
returned in this case is the number of signals named in the @file{header} file.
Note, however, that there is no guarantee that all (or indeed any) of the
signals named in the @file{header} file are available to be opened.
The features described in this paragraph were first introduced in
version 4.4 of the DB library.

As a special case, if @var{nsig} is 0, @var{siarray} can be @code{NULL}.
This can be useful to force open input signal files to be closed without
closing open annotation or output signal files.

@c @group
@node     osigopen, osigfopen, isigopen, selecting
@unnumberedsubsec osigopen
@findex osigopen
@cindex signal files (creating)
@cindex creating signal files

@example
int osigopen(char *@var{record}, DB_Siginfo *@var{siarray}, unsigned int @var{nsig})
@end example
@noindent
@strong{Return:}
@table @asis
@item @t{>0}
Success: the returned value is the number of output signals; this number
should match @var{nsig}
@item @t{-1}
Failure: unable to read @file{header} file
@item @t{-2}
Failure: incorrect @file{header} file format
@item @t{-3}
Failure: unable to open output signal(s)
@end table
@c @end group

@noindent
This function opens output signal files.  Use it only if signals are to
be @emph{written} using @code{putvec}.  The signal specifications,
including the file names, are read from the @file{header} file for a
specified record.  Unmodified MIT or AHA database @file{header} files
cannot be used, since @code{osigopen} would attempt to overwrite the
(write-protected) signal files named within.  If @var{record} begins
with @samp{+}, previously opened output signal files are left open, and
the record name is taken to be the remainder of @var{record} after
discarding the @samp{+}.  Otherwise, @code{osigopen} closes any
previously opened output signal files, and takes all of @var{record} as
the record name.  If the record name is @samp{-}, @code{osigopen} reads
the standard input rather than a @file{header} file.  @var{siarray} is a
pointer to an uninitialized array of @code{DB_Siginfo} structures;
@var{siarray} must contain at least @var{nsig} members.  The caller must
allocate storage for the @code{DB_Siginfo} structures.  On return,
@code{osigopen} will have filled in the @code{DB_Siginfo} structures with
the signal specifications.

No more than @var{nsig} (additional) output signals will be opened by
@code{osigopen}, even if the @file{header} contains specifications for
more than @var{nsig} signals.  For example, this code fragment

@example
@dots{}
DB_Siginfo s[2];
int i, nsig;

nsig = osigopen("8l", s, 2);
for (i = 0; i < nsig; i++)
    printf("signal %d will be written into `%s'\n", i, s[i].fname);
@dots{}
@end example

@noindent
creates 2 output signals named @file{data0} and @file{data1}
(@pxref{Piped and Local Records}).  @xref{Example 6}, and @pxref{Example 7},
for illustrations of the use of @code{osigopen}.

As a special case, if @var{nsig} is 0, @var{siarray} can be @code{NULL}.
This can be useful to force open output signal files to be closed
without closing open annotation or input signal files.

@c @group
@node     osigfopen, dbinit, osigopen, selecting
@unnumberedsubsec osigfopen
@findex osigfopen
@cindex signal files (creating)
@cindex creating signal files

@example
int osigfopen(DB_Siginfo *@var{siarray}, unsigned int @var{nsig})
@end example
@noindent
@strong{Return:}
@table @asis
@item @t{>0}
Success: the returned value is the number of output signals;  this number
should match @var{nsig}
@item @t{-2}
Failure: error in signal specification (@code{fname} or @code{desc} too
long, illegal @code{fmt} or @code{bsize}, or incorrect signal group
assignment)
@item @t{-3}
Failure: unable to open output signal(s)
@end table
@c @end group

@noindent
This function opens output signals as does @code{osigopen}, but the
signal specifications, including the signal file names, are supplied by
the caller to @code{osigfopen}, rather than read from a @file{header}
file as in @code{osigopen}.  Any previously open output signals are
closed by @code{osigfopen}.  @var{siarray} is a pointer to an array of
@code{DB_Siginfo} structures
@iftex
(@pxref{DB_Siginfo structures, , Signal Information Structures}),
@end iftex
@ifinfo
(@pxref{DB_Siginfo structures}),
@end ifinfo
one for each signal to be opened.  @var{nsig} is the number of
@code{DB_Siginfo} structures in @var{siarray}; @var{nsig} must be no
greater than @code{DB_MAXSIG} (a constant defined in @file{<ecg/db.h>}).

Before invoking @code{osigfopen}, the caller must fill in the fields of
the @code{DB_Siginfo} structures in @var{siarray} (@pxref{Data Types};
the @code{initval}, @code{nsamp}, and @code{cksum} fields may be left
uninitialized, however).  To make a multiplexed signal file, specify the
same @code{fname} and @code{group} for each signal to be included
(@pxref{Multiplexed Signal Files}).  For ordinary (non-multiplexed)
signal files, specify a unique @code{fname} and @code{group} for each
signal.  @xref{Example 8}, for an illustration of the use of
@code{osigfopen}.

As a special case, if @var{nsig} is 0, @var{siarray} can be @code{NULL}.
This can be useful to force open output signal files to be closed
without closing open annotation or input signal files.

@c @group
@node     dbinit, , osigfopen, selecting    
@unnumberedsubsec dbinit
@findex dbinit

@example
int dbinit(char *@var{record}, DB_Anninfo *@var{aiarray}, unsigned int @var{nann},
           DB_Siginfo *@var{siarray}, unsigned int @var{nsig})
@end example
@noindent
@strong{Return:}
@table @asis
@item @t{>0}
Success: the returned value is the number of input signals (i.e., the
number of valid entries in @var{siarray})
@item @t{ 0}
Annotation files opened successfully, input signals unavailable
(not an error for programs that don't need them; no error
message is printed if @var{nsig} is 0)
@item @t{-1}
Failure: unable to read @file{header} file (probably incorrect record
name)
@item @t{-2}
Failure: incorrect @file{header} file format
@item @t{-3}
Failure: unable to open input annotation file
@item @t{-4}
Failure: unable to open output annotation file
@item @t{-5}
Failure: illegal @code{stat} (in @var{aiarray}) specified for annotation file
@iftex
(@pxref{DB_Anninfo structures, , Annotator Information Structures})
@end iftex
@ifinfo
(@pxref{DB_Anninfo structures})
@end ifinfo
@end table
@c @end group

@noindent
This function opens database files other than output signal
files for a selected record.  The code

@example
n = dbinit(record, a, na, s, ns);
@end example

@noindent
is exactly equivalent to

@example
n = annopen(record, a, na);
if (n == 0)
    n = isigopen(record, s, ns);
@end example

@noindent
@xref{Example 9}, for an illustration of the use of @code{dbinit}.
@iftex
@xref{osigopen, , @code{osigopen}}, and @pxref{osigfopen, , @code{osigfopen}},
@end iftex
@ifinfo
@xref{osigopen}, and @pxref{osigfopen},
@end ifinfo
for methods of opening output signal files.

@page
@node     signal and annotation I/O, non-sequential, selecting, Functions
@section Reading and Writing Signals and Annotations
@cindex signal I/O

@menu
* getvec::		Reading input signals.
* getframe::            Reading input signals from multifrequency records.
* putvec::		Writing output signals.
* getann::		Reading annotations.
* ungetann::		Pushing an annotation onto an input stream.
* putann::		Writing annotations.
@end menu

@c @group
@node    getvec, getframe, signal and annotation I/O, signal and annotation I/O
@unnumberedsubsec getvec
@findex getvec
@cindex reading signals
@cindex signals (reading)

@example
int getvec(DB_Sample *@var{vector})
@end example
@noindent
@strong{Return:}
@table @asis
@item @t{>0}
Success;  the returned value is the number of input signals (the number of
valid entries in @var{vector})
@item @t{-1}
End of data (contents of @var{vector} not valid)
@item @t{-3}
Failure: unexpected physical end of file
@item @t{-4}
Failure: checksum error (detected only at end of file)
@end table
@c @end group

@noindent
This function reads a sample from each input signal.  The caller should
allocate storage for an array of DB_Samples (integers) and pass a
pointer to this array to @code{getvec}.  (The length of the array must
be no less than the number of input signals, as obtained from
@code{isigopen} or @code{dbinit}; this number will never exceed
@code{DB_MAXSIG}, defined in @file{<ecg/db.h>}.)  On return,
@var{vector[i]} contains the next sample from signal @var{i}.  For
example, this modified version of the example from chapter 1 reads and
prints the first ten samples of each available input signal:

@example
#include <ecg/db.h>

main()
@{
    int i, j, nsig;
    DB_Sample v[DB_MAXSIG];
    static DB_Siginfo s[DB_MAXSIG];

    nsig = isigopen("100s", s, DB_MAXSIG);
    if (nsig < 1)
        exit(1);
    for (i = 0; i < 10; i++) @{
        if (getvec(v) < 0)
            break;
        for (j = 0; j < nsig; j++)
            printf("%8d", v[j]);
        printf("\n");
    @}
    exit(0);
@}
@end example

Notice how the value returned by @code{isigopen} is used to determine
how many input signals there are.  The array of @code{DB_Siginfo}
structures is declared @code{static}, a practice that is recommended
for all arrays and data structures of any substantial size, to avoid
run-time stack overflow.  Several of the example programs in chapter 6
illustrate the use of @code{getvec}; for example, @pxref{Example
6}.

@c @group
@node    getframe, putvec, getvec, signal and annotation I/O
@unnumberedsubsec getframe
@findex getframe (9.0)
@cindex reading signals
@cindex signals (reading)
@cindex frames (reading)
@cindex multifrequency records

@example
int getframe(DB_Sample *@var{vector})
@end example
@noindent
@strong{Return:}
@table @asis
@item @t{>0}
Success;  the returned value is the number of input signals
@item @t{-1}
End of data (contents of @var{vector} not valid)
@item @t{-3}
Failure: unexpected physical end of file
@item @t{-4}
Failure: checksum error (detected only at end of file)
@end table
@c @end group

@noindent
This function reads a vector of samples, including at least one sample
from each open input signal.  If all signals are sampled at the same
frequency, only one sample is read from each signal.  Otherwise, signals
sampled at multiples of the frame frequency are represented by two or
more consecutive elements of the returned @var{vector}.  For example,
if the frame frequency is 125 Hz, signal 0 is sampled at 500 Hz, and the
remaining 3 signals are sampled at 125 Hz each, then the returned
@var{vector} has 7 valid components: the first 4 are samples of signal
0, and the remaining 3 are samples of signals 1, 2, and 3.  The caller
should allocate storage for an array of DB_Samples (integers) and pass a
pointer to this array to @code{getframe}.  The length of @var{vector}
must be determined by summing the values of the @code{spf} (samples per
frame) fields in the @code{DB_Siginfo} structures associated with the
input signals
@iftex
(@pxref{isigopen, , @code{isigopen}}).
@end iftex
@ifinfo
(@pxref{isigopen}).
@end ifinfo

@c @group
@node     putvec, getann, getframe, signal and annotation I/O
@unnumberedsubsec putvec
@findex putvec
@cindex signals (writing)
@cindex writing signals

@example
int putvec(DB_Sample *@var{vector})
@end example
@noindent
@strong{Return:}
@table @asis
@item @t{>0}
Success: the returned value is the number of output signals (the number
of entries in @var{vector} that were written)
@item @t{ 0}
Slew rate too high for one or more signals (difference format only; the
DC level(s) will be corrected as soon as the slew rate permits)
@item @t{-1}
Failure: write error
@end table
@c @end group

@noindent
This function writes a sample to each input signal.  The caller should
fill an array of DB_Samples with the samples and pass a pointer to this
array to @code{putvec}.  (The length of the array must be no less than
the number of output signals, as given to @code{osigfopen} or
@code{osigopen}; this number must never exceed @code{DB_MAXSIG}, defined in
@file{<ecg/db.h>}.)  On entry, @var{vector[i]} contains the next sample
from signal @var{i}.  For example, this modified version of the previous
example
@iftex
(@pxref{getvec, , @code{getvec}})
@end iftex
@ifinfo
(@pxref{getvec})
@end ifinfo
copies the first ten samples of each available input signal:

@example
#include <ecg/db.h>

main()
@{
    int i, nsig;
    DB_Sample v[DB_MAXSIG];
    static DB_Siginfo s[DB_MAXSIG];

    if ((nsig = isigopen("100s", s, DB_MAXSIG)) < 1 ||
        osigopen("8l", s, nsig) < 1)
        exit(1);
    for (i = 0; i < 10; i++)
        if (getvec(v) < 0 || putvec(v) < 0)
            break;
    dbquit();
    exit(0);
@}
@end example

All programs that write signals or annotations @emph{must} invoke
@code{dbquit} to close the output files properly
@iftex
(@pxref{dbquit, , @code{dbquit}}).
@end iftex
@ifinfo
(@pxref{dbquit}).
@end ifinfo
This example uses record @file{8l} (@pxref{Piped and Local Records}) for
the output signal specifications; the output signal files will be named
@file{data0} and @file{data1} in the current directory.  The array of
@code{DB_Siginfo} structures is declared @code{static}, a practice that is
recommended for all arrays and data structures of any substantial size,
to avoid run-time stack overflow.  Several of the example programs in
chapter 6 illustrate the use of @code{putvec}; for example,
@pxref{Example 6}.

@cindex annotation I/O
@c @group
@node     getann, ungetann, putvec, signal and annotation I/O
@unnumberedsubsec getann
@findex getann
@cindex annotations (reading)
@cindex reading annotations

@example
int getann(DB_Annotator @var{an}, DB_Annotation *@var{annot})
@end example
@noindent
@strong{Return:}
@table @asis
@item @t{ 0}
Success
@item @t{-1}
End of file (@var{*annot} is not valid)
@item @t{-2}
Failure: incorrect annotator number specified
@item @t{-3}
Failure: unexpected physical end of file
@end table
@c @end group

@noindent
This function reads the next annotation from the input annotator
specified by @var{an} into the annotation structure
@iftex
(@pxref{DB_Annotation structures, , Annotation Structures})
@end iftex
@ifinfo
(@pxref{DB_Annotation structures})
@end ifinfo
pointed to by @var{annot}.  The caller must allocate storage for the
annotation structure.  Input annotators are numbered 0, 1, 2, etc.  This
short program uses @code{getann} to read the contents of the reference
(@file{atruth}) annotation file for record @file{100s}:

@example
#include <ecg/db.h>

main()
@{
    DB_Anninfo a;
    DB_Annotation annot;

    a.name = "atruth"; a.stat = READ;
    if (annopen("100s", &a, 1) < 0)
        exit(1);
    while (getann(0, &annot) == 0)
        printf("%s %s\n", mstimstr(annot.time), annstr(annot.anntyp);
    exit(0);
@}
@end example

@iftex
@xref{DB_Anninfo structures, , Annotator Information Structures},
@end iftex
@ifinfo
@xref{DB_Anninfo structures},
@end ifinfo
for information on the contents of the @code{DB_Anninfo} structure,
and
@iftex
@pxref{timstr and strtim, , @code{mstimstr}}, and
@pxref{annstr and strann, , @code{annstr}},
@end iftex
@ifinfo
@pxref{timstr and strtim}, and @pxref{annstr and strann},
@end ifinfo
for details of the functions used to print portions of the annotations
read by @code{getann} in this example.

@c @group
@node     ungetann, putann, getann, signal and annotation I/O
@unnumberedsubsec ungetann
@findex ungetann (5.3)
@cindex annotations (reading)
@cindex reading annotations
@cindex unreading annotations

@example
int ungetann(DB_Annotator @var{an}, DB_Annotation *@var{annot})
@end example
@noindent
@strong{Return:}
@table @asis
@item @t{ 0}
Success
@item @t{-1}
Failure: push-back buffer full (@code{*@var{annot}} was not pushed back)
@item @t{-2}
Failure: incorrect annotator number specified
@end table
@c @end group

@noindent
This function arranges for the annotation structure pointed to by
@var{annot} to be the next one read by @code{getann} from input
annotator @var{an}.  The pushed-back annotation need not necessarily be
one originally read by @code{getann}.  No more than one annotation may
be pushed back at a time for each input annotator.  (This function was
first introduced in DB library version 5.3.)

@c @group
@node     putann, , ungetann, signal and annotation I/O
@unnumberedsubsec putann
@findex putann
@cindex annotations (writing)
@cindex writing annotations

@example
int putann(DB_Annotator @var{an}, DB_Annotation *@var{annot})
@end example
@noindent
@strong{Return:}
@table @asis
@item @t{ 0}
Success
@item @t{-1}
Failure: write error
@item @t{-2}
Failure: incorrect annotator number specified
@end table
@c @end group

@noindent
This function writes the next annotation for the output annotator specified by
@var{an} from the annotation structure pointed to by @var{annot}.  Output
annotators are numbered 0, 1, 2, etc.  The caller must fill in all fields of
the annotation structure.  Using version 9.7 and later versions of the DB
library, annotations may be written in any order (@pxref{Annotation Order}).
Earlier versions require that annotations be supplied to @code{putann} in
canonical order, and return an error code of @t{-3} if an out-of-order
annotation is supplied.  All programs that write signals or annotations
@emph{must} invoke @code{dbquit} to close the output files properly
@iftex
(@pxref{dbquit, , @code{dbquit}}).
@end iftex
@ifinfo
(@pxref{dbquit}).
@end ifinfo
Several of the example programs in chapter 6 illustrate the use of
@code{putann}; for example, @pxref{Example 1}.

@page
@node     non-sequential, conversion, signal and annotation I/O, Functions
@section Non-Sequential Access to DB Files
@cindex random access
@cindex non-sequential access
@cindex skipping through DB files

The next three functions permit random access to signal and annotation
files.  It is not possible, however, to skip backwards on piped
input.

@menu
* isigsettime::			Setting time of next samples read.
* isgsettime::			As above, but for one signal group only.
* iannsettime::			Setting time of next annotations read.
@end menu

@c @group
@node     isigsettime, isgsettime, non-sequential, non-sequential
@unnumberedsubsec isigsettime
@findex isigsettime
@cindex signals (non-sequential access)

@example
int isigsettime(DB_Time @var{t})
@end example
@noindent
@strong{Return:}
@table @asis
@item @t{ 0}
Success
@item @t{-1}
Failure: EOF reached or improper seek
@end table
@c @end group

@noindent
This function resets the input signal file pointers so that the next
samples returned from @code{getvec} will be those with sample number
|@var{t}|.  Only the magnitude of @var{t} is significant, not its sign;
hence values returned by @code{strtim} can always be used safely as
arguments to @code{isigsettime}
@iftex
(@pxref{timstr and strtim, , @code{timstr} and @code{strtim}}).
@end iftex
@ifinfo
(@pxref{timstr and strtim}).
@end ifinfo
This function will fail if a pipe is used for input and |@var{t}| is less
than the current sample number.  @xref{Example 7}, and @pxref{Example 9},
for illustrations of the use of @code{isigsettime}.

@c @group
@node     isgsettime, iannsettime, isigsettime, non-sequential
@unnumberedsubsec isgsettime
@findex isgsettime
@cindex signals (non-sequential access)

@example
int isgsettime(DB_Group @var{sgroup}, DB_Time @var{t})
@end example
@noindent
@strong{Return:}
@table @asis
@item @t{ 0}
Success
@item @t{-1}
Failure: EOF reached or improper seek
@item @t{-2}
Failure: incorrect signal group number specified
@end table
@c @end group

@noindent
This function does the job of @code{isigsettime}, but only for the signal
group specified by @var{sgroup}.  This function may be of use if more than
one record is open simultaneously (@pxref{Multiple Record Access}).

@c @group
@node     iannsettime, , isgsettime, non-sequential
@unnumberedsubsec iannsettime
@findex iannsettime
@cindex annotations (non-sequential access)

@example
int iannsettime(DB_Time @var{t})
@end example
@noindent
@strong{Return:}
@table @asis
@item @t{ 0}
Success
@item @t{-1}
Failure: EOF reached or improper seek
@item @t{-3}
Failure: unexpected physical end of file
@end table
@c @end group

@noindent
This function resets the input annotation file pointers so that the next
annotation read by @code{getann} from each input annotation file will be
the first occurring on or after sample number |@var{t}| in that file.
Only the magnitude of @var{t} is significant, not its sign; hence values
returned by @code{strtim} can always be used safely as arguments to
@code{iannsettime}
@iftex
(@pxref{timstr and strtim, , @code{timstr} and @code{strtim}}).
@end iftex
@ifinfo
(@pxref{timstr and strtim}).
@end ifinfo
This function will fail if a pipe is used for input and |@var{t}| is less
than the time of the most recent annotation read from the pipe.
@xref{Example 9}, for an illustration of the use of @code{iannsettime}.

The version of @code{iannsettime} that is included on the first edition
of the MIT-BIH Arrhythmia Database CD-ROM contains a bug that can
occasionally cause incorrect results.  To avoid this problem when using
that version of the library, make at least one call to @code{getann}
before using @code{iannsettime}.  The bug was corrected in version 4.0
of the DB library.

@page
@node     conversion, calibration, non-sequential, Functions
@section Conversion Functions
@cindex conversion functions

Functions in this section perform various useful conversions:  between
annotation codes and printable strings, between times in sample intervals
and printable strings, between Julian dates and printable strings, and between
ADC units and physical units.

@menu
* annstr and strann::		annotation code <-> string
* timstr and strtim::		time in sample intervals <-> HH:MM:SS string
* datstr and strdat::		Julian date <-> DD/MM/YYYY string
* aduphys and physadu::		ADC units <-> physical units
@end menu

@c @group
@node     annstr and strann, timstr and strtim, conversion, conversion
@unnumberedsubsec annstr, anndesc, and ecgstr
@findex ecgstr
@findex annstr (5.3)
@findex anndesc (5.3)
@cindex annotation code (conversion to and from string)
@cindex string (conversion to and from annotation code)
@cindex mnemonic (annotation)
@cindex conversion between annotation code and string

@example
char *annstr(int @var{code})
char *anndesc(int @var{code})
char *ecgstr(int @var{code})
@end example
@noindent
@strong{Return:}
@table @asis
@item @t{(char *)}
pointer to a printable string that describes the code, or @code{NULL}
@end table
@c @end group

@cindex modification label
@noindent
These functions translate the annotation code specified by their argument
into a string (@pxref{Annotation Codes}).  Illegal or undefined codes
are translated by @code{annstr} and @code{ecgstr} into decimal numerals
surrounded by brackets (e.g., @samp{[55]}); @code{anndesc} returns @code{NULL}
in such cases.  The strings returned by @code{annstr} are mnemonics
(usually only one character), which may be modified either by @code{setannstr}
or by the presence of @dfn{modification labels} in an input annotation file
@iftex
(@pxref{annstr and strann, , @code{setannstr}}).
@end iftex
@ifinfo
(@pxref{annstr and strann}).
@end ifinfo
The strings returned by @code{anndesc} are brief descriptive strings,
usually those given in the table of annotation codes
(@pxref{Annotation Codes}).  The strings returned by
@code{ecgstr} are usually the same as those returned by @code{annstr},
but they can be modified only by @code{setecgstr}, and not by the
presence of modification labels as for @code{annstr}.  The intent is
that @code{ecgstr} should be used rather than @code{annstr} only when
it is necessary that a fixed set of mnemonics be used, independent of
any modification labels.

Here is a little program that prints a table of the codes, mnemonic
strings, and descriptions:

@example
#include <stdio.h>
#include <ecg/db.h>
#include <ecg/ecgcodes.h>

main()
@{
    int i;

    printf("Code\tMnemonic\tDescription\n");
    for (i = 1; i <= ACMAX; i++) @{
        printf("%3d\t%s", i, annstr(i));
        if (anndesc(i) != NULL)
            printf("\t\t%s", anndesc(i));
        printf("\n");
    @}
@}
@end example

@code{ACMAX} is defined in @file{<ecg/ecgcodes.h>}.  The range from 1
through @code{ACMAX} includes all legal annotation codes; if you run
this program, you will find some undefined but legal annotation codes in
this range. @xref{Example 3}, for another illustration of the use of
@code{annstr}.  (@code{annstr} and @code{anndesc} were first introduced
in DB library version 5.3.)

@c @group
@unnumberedsubsec strann and strecg
@findex strecg
@findex strann (5.3)
@cindex annotation code (conversion to and from string)
@cindex string (conversion to and from annotation code)
@cindex mnemonic (annotation)
@cindex conversion between annotation code and string

@example
int strann(char *@var{string})
int strecg(char *@var{string})
@end example
@noindent
@strong{Return:}
@table @asis
@item @t{(int)}
annotation code
@end table
@c @end group

@noindent
These functions translate the null-terminated ASCII character strings to
which their arguments point into annotation codes.  Illegal strings are
translated into @code{NOTQRS}.  Input strings for @code{strann} and
@code{strecg} should match those returned by @code{annstr} and
@code{ecgstr} respectively.  @xref{Example 9}, for an illustration of the
use of @code{strann}. (@code{strann} was first introduced in DB library
version 5.3.)

@c @group
@unnumberedsubsec setannstr, setanndesc, and setecgstr
@findex setannstr (5.3)
@findex setanndesc (5.3)
@findex setecgstr
@cindex annotation code strings (setting)
@cindex changing annotation code strings
@cindex setting annotation code strings

@example
int setannstr(int @var{code}, char *@var{string})
int setanndesc(int@var{code}, char *@var{string})
int setecgstr(int @var{code}, char *@var{string})
@end example
@noindent
@strong{Return:}
@table @asis
@item @t{ 0}
Success
@item @t{-1}
Failure: illegal @code{code}
@end table
@c @end group

@noindent
These functions modify translation tables used by functions that
convert between annotation codes and strings.  @code{setannstr} modifies
the table shared by @code{annstr} and @code{strann}; @code{setanndesc}
modifies the table used by @code{anndesc}; and @code{setecgstr} modifies
the table shared by @code{ecgstr} and @code{strecg}.  They may be used
to redefine strings for defined annotation codes as well as to define
strings for undefined annotation codes.  For example,
@code{setannstr(NORMAL, "\\267")} redefines the string for normal beats
as a PostScript bullet, @samp{@bullet{}} (@code{NORMAL} is defined in
@file{<ecg/ecgcodes.h>}).  These functions do not copy their string
arguments, which must therefore be kept valid by the caller.

@cindex modification label
An important difference between @code{setannstr} (or @code{setanndesc})
and @code{setecgstr} is that @code{annopen} and @code{dbinit} insert
modification labels in any output annotation files that are created
@emph{after} invoking @code{setannstr} or @code{setanndesc};
@code{setecgstr} does not have this side effect.  By using
@code{setannstr} before @code{annopen}, a DB application may create
annotation files with self-contained code tables, which can be read
properly by other DB applications without the need to inform them
explicitly about non-standard codes.  For this scheme to work as
intended, all custom code mnemonics and descriptions must be defined
before the output annotation files are opened.

By passing a negative value as @var{code} to @code{setannstr} or
@code{setanndesc}, the translation for
@iftex
@minus{}@var{code}
@end iftex
@ifinfo
-@var{code}
@end ifinfo
can be modified without triggering the generation of a modification label.
This feature can be useful for programs that use alternate sets of
mnemonics or descriptions for speakers of different languages.

Note that it is possible, though not desirable, to define identical
strings for two or more codes; the behavior of @code{strann} and
@code{strecg} in such cases is implementation-dependent.
(@code{setannstr} and @code{setanndesc} were first introduced in DB
library version 5.3.)

@node     timstr and strtim, datstr and strdat, annstr and strann, conversion

@iftex
@sp 2
@end iftex

The next three functions convert between ``standard time format''
strings and times in units of sample intervals.  Normally they should be
invoked after @code{isigopen}, @code{dbinit}, or @code{sampfreq}, any of
which will determine the duration of a sample interval and the base time
from a @file{header} file, or after defining these quantities using
@code{setsampfreq} and @code{setbasetime}.  If this is not done, or if
these time-conversion functions are used after @code{dbquit}, they will
perform conversions in units of seconds (i.e., the sample interval is
taken to be one second in such cases).

@c @group
@unnumberedsubsec [ms]timstr
@findex timstr
@findex mstimstr
@cindex time (conversion to and from string)
@cindex string (conversion to and from time)
@cindex elapsed time
@cindex conversion between time and string

@example
char *timstr(DB_Time @var{t})
char *mstimstr(DB_Time @var{t})
@end example
@noindent
@strong{Return:}
@table @asis
@item @t{(char *)}
pointer to a string that represents the time
@end table
@c @end group

@noindent
These functions convert times or time intervals into null-terminated
ASCII strings.  If the argument, @var{t}, is greater than zero, it is
treated as a time interval, and converted directly into @var{HH:MM:SS}
format by @code{timstr}, or to @var{HH:MM:SS.SSS} format by
@code{mstimstr}, with leading zero digits and colons suppressed.  If
@var{t} is zero or negative, it is taken to represent negated elapsed
time from the beginning of the record, and it is converted to a time of
day using the base time for the record as indicated by the @file{header}
file or the caller
@iftex
(@pxref{setbasetime, , @code{setbasetime}});
@end iftex
@ifinfo
(@pxref{setbasetime});
@end ifinfo
in this case, if the base time is defined, the string will contain all
digits even if there are leading zeroes, it will include the date if a
base date is defined, and it will be marked as a time of day by being
bracketed (e.g., @samp{[08:45:00 23/04/1989]}).  The result of the
conversion is truncated to a multiple of a second by @code{timstr}, or
to a multiple of a millisecond by @code{mstimstr}.  Note in each case
that the returned pointer addresses static data (shared by @code{timstr}
and @code{mstimstr}), the contents of which are overwritten by
subsequent calls.  @xref{Example 3}, for an illustration of the use of
@code{mstimstr}; also @pxref{Example 5}, for an example of the use of
@code{timstr}.

@c @group
@unnumberedsubsec strtim
@findex strtim
@cindex time (conversion to and from string)
@cindex string (conversion to and from time)
@cindex elapsed time
@cindex conversion between time and string

@example
DB_Time strtim(char *@var{string})
@end example
@noindent
@strong{Return:}
@table @asis
@item @t{(DB_Time) >0}
number of sample intervals corresponding to the argument interpreted as
a time interval
@item @t{(DB_Time) <0}
(negated) elapsed time in sample intervals from the beginning of the record,
corresponding to the argument interpreted as a time of day
@item @t{(DB_Time)  0}
a legal return if the argument matches the base time;  otherwise an error
return indicating an incorrectly formatted argument
@end table
@c @end group

@noindent
@cindex standard time format (examples)
This function converts an ASCII string in @dfn{standard time format} to a time
in units of sample intervals.  Examples of standard time format:
@table @code
@item 2:14.875
2 minutes + 14.875 seconds
@item [13:6:0]
13:06 (1:06 PM)
@item [8:0:0 1]
8 AM on the day following the base date
@item [12:0:0 1/3/1992]
noon on 1 March 1992
@item 143
143 seconds (2 minutes + 23 seconds)
@item 4:02:01
4 hours + 2 minutes + 1 second
@item s12345
12345 sample intervals
@item c350.5
counter value 350.5
@item e
time of the end of the record (if defined)
@item i
time of the next sample in input signal 0
@item o
(the letter `o') time of the next sample in output signal 0
@end table

If the argument is bracketed (as in the second, third, and fourth examples), it
is taken as a time of day, and @code{strtim} uses the base time defined
by the @file{header} file or by the caller
@iftex
(@pxref{setbasetime, , @code{setbasetime}});
@end iftex
@ifinfo
(@pxref{setbasetime});
@end ifinfo
in this case, the value returned is zero or negative (and can be
converted into elapsed time from the beginning of the record by simply
negating it).  If the argument is not bracketed, it is taken as a time
interval, and converted directly into a positive number of sample
intervals.  These notations match those used by @code{timstr} and
@code{mstimstr}, which are (approximately) inverse functions of
@code{strtim}; in fact, for MIT DB and AHA DB records (and any others
with sampling frequencies below 1 KHz), @code{strtim(mstimstr(@var{t}))}
= @var{t}, for any @var{t}.  The @samp{s}-format (as in the seventh
example above) is provided to allow ``conversion'' of time intervals
already expressed in sample intervals.  The similar @samp{c}-format
converts counter values
@iftex
(@pxref{counter conversion, , @code{getcfreq}})
@end iftex
@ifinfo
(@pxref{counter conversion})
@end ifinfo
into sample intervals.  The length of the record in
sample intervals can be obtained using @code{strtim("e")}, which
evaluates to zero if this quantity is undefined.  The sample number of
the next sample to be read or written can be determined using
@code{strtim("i")} or @code{strtim("o")}.  If the argument string is
incorrectly formatted, @code{strtim} returns zero (indistinguishable
from a correct input that evokes a zero output); this may be considered
a feature.  Several of the programs in chapter 6 illustrate the use of
@code{strtim} (for example, @pxref{Example 7}).

@node datstr and strdat, aduphys and physadu, timstr and strtim, conversion

The next two functions convert between Julian dates and ASCII strings.
Julian dates as defined by astronomers begin at noon GMT; these begin at
midnight local time.

@c @group
@unnumberedsubsec datstr
@findex datstr
@cindex date (conversion to and from string)
@cindex Julian date (conversion to and from string)
@cindex string (conversion to and from Julian date)
@cindex conversion between Julian date and string

@example
char *datstr(DB_Date @var{date})
@end example
@noindent
@strong{Return:}
@table @asis
@item @t{(char *)}
pointer to a string that represents the date
@end table
@c @end group

@noindent
This function converts the Julian date represented by @var{date} into
an ASCII string in the form @var{DD/MM/YYYY}.

@c @group
@unnumberedsubsec strdat
@findex strdat
@cindex date (conversion to and from string)
@cindex Julian date (conversion to and from string)
@cindex string (conversion to and from Julian date)
@cindex conversion between Julian date and string

@example
DB_Date strdat(char *@var{string})
@end example
@noindent
@strong{Return:}
@table @asis
@item @t{(DB_Date)}
Julian date corresponding to the argument
@end table
@c @end group

@noindent
This function converts @var{string} into a Julian date.  The argument
should be in the format used by @code{datstr}; if @var{string} is
improperly formatted, @code{strdat} returns zero.  Note that dates such
as @samp{15/3/89} refer to the first century A.D., not the twentieth.
For example, the interval in days between the events commemorated by the
French and American national holidays is @code{strdat("14/7/1789")} --
@code{strdat("4/7/1776")}.

@node aduphys and physadu, , datstr and strdat, conversion

The next four functions convert between analog-to-digital converter
(ADC) units and physical units, using as a conversion factor the gain
for the specified input signal.  The first two (@code{aduphys} and
@code{physadu}) are general-purpose functions that convert absolute
levels (i.e., they account for non-zero @code{baseline} values); the
last two (@code{adumuv} and @code{muvadu}) are for use with
millivolt-dimensioned signals only, and convert potential differences
(i.e., @code{adumuv(@var{s}, 0)} = @code{muvadu(@var{s}, 0)} = 0 for all
@var{s}, irrespective of the @code{baseline} values specified in the
header file).  Normally, these functions should be invoked after
@code{isigopen} or @code{dbinit}, either of which will determine the
gain from the @file{header} file.  If this is not done, or if the
@file{header} file indicates that the gain is uncalibrated, or if the
specified input signal is not currently open, a gain of @code{DEFGAIN}
(defined in @file{<ecg/db.h>}) ADC units per millivolt, and a baseline
of zero, are assumed.  If the physical units
@iftex
(@pxref{DB_Siginfo structures, , Signal Information Structures})
@end iftex
@ifinfo
(@pxref{DB_Siginfo structures})
@end ifinfo
are not millivolts, @code{adumuv} and @code{muvadu} convert to and from
thousandths of the defined physical units.  Note that @code{adumuv} and
@code{muvadu} deal exclusively with integers, but @code{aduphys} returns
and @code{physadu} accepts double-precision floating point physical
values.

@c @group
@unnumberedsubsec aduphys
@findex aduphys (6.0)
@cindex adu (conversion to and from physical units)
@cindex physical units (conversion to and from adus)
@cindex conversion between adus and physical units

@example
double aduphys(DB_Signal @var{s}, DB_Sample @var{a})
@end example
@noindent
@strong{Return:}
@table @asis
@item @t{(double)}
physical value corresponding to a sample value of @var{a} ADC units
@end table
@c @end group

@noindent
This function converts the sample value @var{a} from ADC units to
physical units, based on the @code{gain} and @code{baseline} for input
signal @var{s}.  (@code{aduphys} was first introduced in DB library
version 6.0.)

@c @group
@unnumberedsubsec physadu
@findex physadu (6.0)
@cindex adu (conversion to and from physical units)
@cindex physical units (conversion to and from adus)
@cindex conversion between adus and physical units

@example
DB_Sample physadu(DB_Signal @var{s}, double @var{v})
@end example
@noindent
@strong{Return:}
@table @asis
@item @t{(DB_Sample)}
sample value, in ADC units, corresponding to @var{v}, in physical units
@end table
@c @end group

@noindent
This function converts the value @var{v} from physical units to ADC
units, based on the @code{gain} and @code{baseline} for input signal
@var{s}.  (@code{physadu} was first introduced in DB library version
6.0.)

@c @group
@unnumberedsubsec adumuv
@findex adumuv
@cindex adu (conversion to and from voltage)
@cindex voltage (conversion to and from adus)
@cindex conversion between adus and voltage

@example
int adumuv(DB_Signal @var{s}, DB_Sample @var{a})
@end example
@noindent
@strong{Return:}
@table @asis
@item @t{(int)}
number of microvolts corresponding to @var{a} ADC units
@end table
@c @end group

@noindent
This function converts the potential difference @var{a} from ADC units to
microvolts, based on the @code{gain} for input signal @var{s}.

@c @group
@unnumberedsubsec muvadu
@findex muvadu
@cindex adu (conversion to and from voltage)
@cindex voltage (conversion to and from adus)
@cindex conversion between adus and voltage

@example
DB_Sample muvadu(DB_Signal @var{s}, int @var{v})
@end example
@noindent
@strong{Return:}
@table @asis
@item @t{(int)}
number of ADC units corresponding to @var{v} microvolts
@end table
@c @end group

@noindent
This function converts the potential difference @var{v} from microvolts
to ADC units, based on the @code{gain} for input signal @var{s}.

@page
@node     calibration, miscellaneous functions, conversion, Functions
@section Calibration Functions
@cindex calibration functions

@cindex calibration list
Functions in this section are used to determine specifications for
calibration pulses and customary scales for plotting signals.  All of
them make use of the @dfn{calibration list}, which is maintained in
memory and which contains entries for various types of signals.

@menu
* calopen::			read a calibration file into list
* getcal::			retrieve calibration data from list
* putcal::			append calibration data to list
* newcal::			write calibration list to a file
* flushcal::			discard contents of calibration list
@end menu

@c @group
@node   calopen, getcal, calibration, calibration
@unnumberedsubsec calopen
@findex calopen (6.0)
@cindex calibration file (reading)
@cindex reading calibration files

@example
int calopen(char *@var{file})
@end example
@noindent
@strong{Return:}
@table @asis
@item @t{ 0}
Success
@item @t{-1}
Failure: insufficient memory for calibration list
@item @t{-2}
Failure: unable to open calibration file
@end table
@c @end group

@noindent
This function reads the specified calibration @var{file} (which must be
located in one of the directories specified by @code{DB},
@pxref{DB path}) into the calibration list.  If @var{file} is
@code{NULL}, the file named by @code{DBCAL} is read.  Normally, the
current contents of the calibration list are discarded before reading
the calibration file; if @var{file} begins with @samp{+}, however, the
@samp{+} is stripped from the file name and the contents of the file are
appended to the current calibration list.  If @var{file} is @samp{-},
@code{calopen} reads the standard input rather than a calibration file.
(This function was first introduced in DB library version 6.0.)

@c @group
@node   getcal, putcal, calopen, calibration
@unnumberedsubsec getcal
@findex getcal (6.0)
@cindex calibration (retrieving)
@cindex retrieving calibration data

@example
int getcal(char *@var{desc}, char *@var{units}, DB_Calinfo *@var{cal})
@end example
@noindent
@strong{Return:}
@table @asis
@item @t{ 0}
Success;  @code{*@var{cal}} contains the requested data
@item @t{-1}
Failure: no match found
@end table
@c @end group

@noindent
This function attempts to find calibration data for signals of type
@var{desc}, having physical units as given by @var{units}.  If
successful, it fills in the contents of the @code{DB_Calinfo} structure
@iftex
(@pxref{DB_Calinfo structures, , Calibration Information Structures})
@end iftex
@ifinfo
(@pxref{DB_Calinfo structures})
@end ifinfo
pointed to by @var{cal}.  The caller must allocate storage for the
@code{DB_Calinfo} structure, and must not modify the contents of the
strings addressed by the @code{sigtype} and @code{units} fields of the
@code{DB_Calinfo} structure after @code{getcal} returns.  @code{getcal}
returns data from the first entry in the calibration list that contains
a @code{sigtype} field that is either an exact match or a prefix of
@var{desc}, and a @code{units} field that is an exact match of
@var{units}; if either @var{desc} or @var{units} is @code{NULL},
however, it is ignored for the purpose of finding a match.
@code{getcal} cannot succeed unless the calibration list has been
initialized by a previous invocation of @code{calopen} or @code{putcal}.
(This function was first introduced in DB library version 6.0.)

@c @group
@node    putcal, newcal, getcal, calibration
@unnumberedsubsec putcal
@findex putcal (6.0)
@cindex calibration (storing)
@cindex storing calibration data

@example
int putcal(DB_Calinfo *@var{cal})
@end example
@noindent
@strong{Return:}
@table @asis
@item @t{ 0}
Success
@item @t{-1}
Failure: insufficient memory
@end table
@c @end group

@noindent
This function adds the @code{DB_Calinfo} structure pointed to by @var{cal}
to the end of the calibration list.  (This function was first introduced
in DB library version 6.0.)

@c @group
@node    newcal, flushcal, putcal, calibration
@unnumberedsubsec newcal
@findex newcal (6.0)
@cindex calibration list (writing)
@cindex writing calibration list

@example
int newcal(char *@var{file})
@end example
@noindent
@strong{Return:}
@table @asis
@item @t{ 0}
Success
@item @t{-1}
Failure: unable to open @var{file}
@end table
@c @end group

@noindent
This function creates a new calibration @var{file} (in the current
directory) containing the contents of the calibration list (which is not
modified).  @var{file} must satisfy the standard conditions for a DB
file name, i.e., it may contain letters, digits, or underscores.  (This
function was first introduced in DB library version 6.0.)

@c @group
@node    flushcal, , newcal, calibration
@unnumberedsubsec flushcal
@findex flushcal (6.0)
@cindex calibration list (discarding)
@cindex discarding calibration list
@cindex emptying calibration list
@cindex flushing calibration list

@example
void flushcal()
@end example
@c @end group

@noindent
This function discards the current calibration list and returns the
memory that it occupied to the heap.  Note that @code{dbquit} does
@emph{not} perform the function of @code{flushcal}.  (This function was
first introduced in DB library version 6.0.)

@page
@node    miscellaneous functions, , calibration, Functions
@section Miscellaneous DB Functions

@menu
* newheader::			Creating a @file{header} file for a new DB
				record.
* setheader::			Creating or changing a @file{header} file.
* setmsheader::                 Creating a @file{header} for a multi-segment
                                record.
* dbquit::			Closing DB files.
* iannclose and oannclose::     Closing annotation files.
* dbquiet and dbverbose::	Suppressing error messages from the DB library.
* dberror::			Retrieving error messages from the DB library.
* sampfreq::			Reading the sampling frequency of a DB record.
* setsampfreq::			Setting the sampling frequency.
* setbasetime::			Setting the base time.
* setgvmode::                   Setting the resolution for a multifrequency
                                record.
* getspf::                      Determining the number of samples per frame.
* counter conversion::		Functions for reading and setting counter
				conversion parameters.
* setdb::			Dynamically changing the database path.
* getdb::			Reading the database path.
* dbfile::			Obtaining the pathname of a DB file.
* dbflush::			Flushing buffered output annotations and
				samples.
* getinfo::			Reading info strings from a @file{header} file.
* putinfo::			Writing info strings into a @file{header} file.
* setibsize::			Setting the default input buffer size.
* setobsize::			Setting the default output buffer size.
* dbgetskew::                   Reading intersignal skew.
* dbsetskew::                   Recording intersignal skew.
* dbgetstart::                  Reading the prolog size in a signal file.
* dbsetstart::                  Recording the prolog size in a signal file.

@end menu

@c @group
@node     newheader, setheader, miscellaneous functions, miscellaneous functions
@unnumberedsubsec newheader
@findex newheader
@cindex @code{header} files (creating)
@cindex creating @file{header} files

@example
int newheader(char *@var{record})
@end example
@noindent
@strong{Return:}
@table @asis
@item @t{ 0}
Success
@item @t{-1}
Failure: unable to create @file{header} file
@end table
@c @end group

@cindex record names (restrictions)
@noindent
This function creates a @file{header} file (in the current directory).
Use @code{newheader} just after you have finished writing the signal
files, but before calling @code{dbquit}.  If @var{record} begins with
@samp{+}, the @samp{+} is discarded and the remainder of @var{record} is
taken as the record name.  Otherwise, all of @var{record} is taken to be
the record name.  If the record name is @samp{-}, the @file{header} file
is written to the standard output.  Record names may include letters in
lower or upper case, digits, and underscores (@samp{_}); they may not
include any other characters.  If @var{record} does not conform to these
requirements, @code{newheader} will return
@iftex
@minus{}1;
@end iftex
@ifinfo
-1;
@end ifinfo
@pxref{Example 8},
for an illustration of the use of @code{newheader} to check the validity
of a record name.  For compatibility with the widest range of operating
systems, keep record names short (6 characters or less) and avoid those
that are distinguished by case alone.  To avoid confusion with MIT DB
and AHA DB records, do not use three- or four-digit record names.

@c @group
@node     setheader, setmsheader, newheader, miscellaneous functions
@unnumberedsubsec setheader
@findex setheader (5.0)
@cindex @code{header} files (creating)
@cindex @code{header} files (modifying)
@cindex creating @file{header} files
@cindex modifying @file{header} files

@example
int setheader(char *@var{record}, DB_Siginfo *@var{siarray}, unsigned int @var{nsig})
@end example
@noindent
@strong{Return:}
@table @asis
@item @t{ 0}
Success
@item @t{-1}
Failure: unable to create @file{header} file
@end table
@c @end group

@noindent
This function creates or recreates a @file{header} file (in the current
directory) for the specified @var{record}, based on the contents of the
first @var{nsig} members of @var{siarray}.  The preferred way to create
a header file for a new record is using @code{newheader}, which records
signal checksum and length variables maintained by @code{putvec}.  The
intended use of @code{setheader} is for editing @file{header} files,
e.g., to change recorded signal gains from a calibration program, or to add
signal descriptions.  In the following code fragment, the @file{header}
file for record @file{old} is used to create a @file{header} file for
record @file{new}:

@example
@dots{}
int nsig, status;
DB_Siginfo s[DB_MAXSIG];

nsig = isigopen("old", s, -DB_MAXSIG);
if (nsig > 0) @{
    s[0].gain = 100.0;
    status = setheader("new", s, (unsigned int)nsig);
@}
@dots{}
@end example

@noindent
The @file{header} file for record @file{new} will contain the same signal
information as that for record @file{old}, except that the @code{gain}
for signal 0 will have been changed as shown.  Any ``info'' strings in
the @file{header} file for record @file{old} must be copied explicitly;
@iftex
@pxref{getinfo, , @code{getinfo}}, and @pxref{putinfo, , @code{putinfo}}.
@end iftex
@ifinfo
@pxref{getinfo}, and @pxref{putinfo}.
@end ifinfo
(This function was first
introduced in DB library version 5.0.)

@c @group
@node     setmsheader, dbquit, setheader, miscellaneous functions
@unnumberedsubsec setmsheader
@findex setmsheader (9.1)
@cindex @code{header} files (creating)
@cindex multi-segment records (creating)
@cindex creating @file{header} files

@example
int setmsheader(char *@var{record}, char *@var{snarray}[], unsigned int @var{nsegments})
@end example
@noindent
@strong{Return:}
@table @asis
@item @t{ 0}
Success
@item @t{-1}
Failure: illegal record name, or no segments specified, or header not writable
@item @t{-2}
Failure: segment name too long, or insufficient memory
@item @t{-3}
Failure: attempt to nest multi-segment records, or unreadable segment
@file{header}
@item @t{-4}
Failure: segment length unspecified, or numbers of signals or sampling
frequencies don't match between segments
@end table
@c @end group

@noindent
This function creates a @file{header} file (in the current directory)
for a multi-segment @var{record} (@pxref{Multi-Segment Records}.
@var{snarray} contains the names of the segments, each of which must be
an existing (single-segment) record; @var{nsegments} specifies the
number of segments in @var{snarray}.  Once a @file{header} has been
created by @code{setmsheader}, any DB application can read the
concatenated signal files of the constituent segment simply by opening
the multi-segment record (using @code{isigopen} or @code{dbinit}).  Note
that the signal files themselves are not modified in any way, nor are
they copied; rather, the other DB library functions that read signals
(@code{getvec}, @code{getframe}, @code{isigsettime}, and
@code{isgsettime}) automatically switch among the signal files of the
segments as required.  For an example of the use of @code{setmsheader},
see @file{app/dbcollate.c} (@file{app/dbcoll8.c} under MS-DOS) in the DB
Software Package.  (This function was first introduced in DB library
version 9.1.)

@c @group
@node     dbquit, iannclose and oannclose, setmsheader, miscellaneous functions
@unnumberedsubsec dbquit
@findex dbquit
@cindex closing DB files
@cindex flushing DB I/O
@cindex I/O (completing)

@example
void dbquit(void)
@end example
@c @end group

@noindent
This function closes all open database files.  It also resets the following:

@itemize @bullet
@item
the factor used for converting between samples and seconds (reset to 1),
and the base time (reset to 0, i.e., midnight);
@iftex
@pxref{timstr and strtim, , @code{timstr} and @code{strtim}}
@end iftex
@ifinfo
(@pxref{timstr and strtim})
@end ifinfo
@item
the parameters used for converting between adus and physical units (reset to
@code{DEFGAIN} adu/mV, a quantity defined in @file{<ecg/db.h>});
@iftex
@pxref{aduphys and physadu, , @code{aduphys} and @code{physadu}}
@end iftex
@ifinfo
(@pxref{aduphys and physadu})
@end ifinfo
@item
internal variables used to determine output signal specifications;
@iftex
@pxref{newheader, , @code{newheader}}.
@end iftex
@ifinfo
@pxref{newheader}.
@end ifinfo
@end itemize

If any annotations have been written out-of-order (@pxref{Annotation Order}),
this function attempts to run @file{sortann} (see the @cite{ECG Database
Applications Guide}) as a subprocess to restore the annotations to canonical
order.  If this cannot be done, it prints a warning message indicating that the
annotations are not in order, and providing instructions for putting them in
order.

Programs that do not write annotations or signals need not use
@code{dbquit}.

@c @group
@node     iannclose and oannclose, dbquiet and dbverbose, dbquit, miscellaneous functions
@unnumberedsubsec iannclose
@findex iannclose (9.1)
@cindex closing annotation files

@example
@code{void iannclose(void)}
@end example
@c @end group

@noindent
This function closes all input annotation files.  It was first
introduced in DB library version 9.1.

@c @group
@unnumberedsubsec oannclose
@findex oannclose (9.1)

@example
@code{void oannclose(void)}
@end example
@c @end group

@noindent
This function closes all output annotation files.  It was first
introduced in DB library version 9.1.

If any annotations have been written out-of-order (@pxref{Annotation Order}),
this function attempts to run @file{sortann} (see the @cite{ECG Database
Applications Guide}) as a subprocess to restore the annotations to canonical
order.  If this cannot be done, it prints a warning message indicating that the
annotations are not in order, and providing instructions for putting them in
order.

@c @group
@node     dbquiet and dbverbose, dberror, iannclose and oannclose, miscellaneous functions
@unnumberedsubsec dbquiet
@findex dbquiet
@cindex error suppression
@cindex suppressing errors

@example
@code{void dbquiet(void)}
@end example
@c @end group

@noindent
This function suppresses error reporting on the standard error output
from the DB library functions.

@c @group
@unnumberedsubsec dbverbose
@findex dbverbose (4.0)

@example
@code{void dbverbose(void)}
@end example
@c @end group

@noindent
This function can be used to restore normal error reporting after using
@code{dbquiet}.  (This function was first introduced in DB library
version 4.0.)

@c @group
@node     dberror, sampfreq, dbquiet and dbverbose, miscellaneous functions
@unnumberedsubsec dberror
@findex dberror (4.5)

@example
@code{char *dberror(void)}
@end example
@noindent
@strong{Return:}
@table @asis
@item @t{(char *)}
pointer to error string
@end table
@c @end group

@noindent
This function returns a pointer to a string containing the text of the
most recent DB library error message (or to a string containing the DB
library version number, if there have been no errors).  Function
@code{dberror} is primarily intended for use in applications for which the
standard error output is unavailable or inadequate, such as in X Window
System applications.  (Note that this function may be unnecessary for
MS-Windows applications, since the MS-Windows version of the DB library
generates a message box for error messages, unless @code{dbquiet} has
been used to silence them.)  This function was first introduced in DB
library version 4.5.  Versions earlier than 9.4 return an empty string
rather than the library version number if there have been no errors.

@c @group
@node     sampfreq, setsampfreq, dberror, miscellaneous functions
@unnumberedsubsec sampfreq
@findex sampfreq

@example
DB_Frequency sampfreq(char *@var{record})
@end example
@noindent
@strong{Return:}
@table @asis
@item @t{(DB_Frequency)>0.}
Success: the returned value is the sampling frequency in Hz
@item @t{(DB_Frequency)-1.}
Failure: unable to read @file{header} file
@item @t{(DB_Frequency)-2.}
Failure: incorrect @file{header} file format
@end table
@c @end group

@noindent
This function determines the sampling frequency (in Hz) for the record
specified by its argument.  If its argument is NULL, @code{sampfreq}
returns the currently defined sampling frequency, if any.  It also sets
the internal variables used by the time-conversion functions
@iftex
(@pxref{timstr and strtim, , @code{timstr} and @code{strtim}})
@end iftex
@ifinfo
(@pxref{timstr and strtim})
@end ifinfo
for converting between sample intervals and seconds.  @xref{Example 3},
for an illustration of the use of @code{sampfreq}.  Note that the value
returned by @code{sampfreq} for a multifrequency record depends on the
current @code{getvec} mode
@iftex
(@pxref{setgvmode, , @code{setgvmode}}).
@end iftex
@ifinfo
(@pxref{setgvmode}).
@end ifinfo

@c @group
@node     setsampfreq, setbasetime, sampfreq, miscellaneous functions
@unnumberedsubsec setsampfreq
@findex setsampfreq
@cindex sampling frequency (changing)
@cindex changing sampling frequency

@example
int setsampfreq(DB_Frequency @var{freq})
@end example
@noindent
@strong{Return:}
@table @asis
@item @t{ 0}
Success
@item @t{-1}
Failure:  illegal sampling frequency specified (@var{freq} must not be
negative)
@end table
@c @end group

@noindent
This function sets the sampling frequency used by the time-conversion
functions
@iftex
(@pxref{timstr and strtim, , @code{timstr} and @code{strtim}}).
@end iftex
@ifinfo
(@pxref{timstr and strtim}).
@end ifinfo
Use @code{setsampfreq} before creating a new @file{header} file
@iftex
(@pxref{newheader, , @code{newheader}}).
@end iftex
@ifinfo
(@pxref{newheader}).
@end ifinfo
@xref{Example 8}, for an illustration of the use of @code{setsampfreq}.

@c @group
@node     setbasetime, setgvmode, setsampfreq, miscellaneous functions
@unnumberedsubsec setbasetime
@findex setbasetime
@cindex current time
@cindex time of day (setting)
@cindex base time (setting)

@example
int setbasetime(char *@var{string})
@end example
@noindent
@strong{Return:}
@table @asis
@item @t{ 0}
Success
@item @t{-1}
Failure: incorrect string format
@end table
@c @end group

@noindent
This function sets the base time used by the time-conversion functions
@code{timstr} and @code{strtim}.  Its argument is a null-terminated
ASCII string in @var{HH:MM:SS} format.  An optional base date in
@var{dd/mm/yyyy} format can follow the time in @var{string}; if present,
the date should be separated from the time by a space or tab character.  If
@var{string} is empty or @code{NULL}, the current date and time are read
from the system clock.  Use @code{setbasetime} after defining the sampling
frequency and before creating a @file{header} file
@iftex
(@pxref{newheader, , @code{newheader}}).
@end iftex
@ifinfo
(@pxref{newheader}).
@end ifinfo
@xref{Example 8}, for an illustration of the use of @code{setbasetime}.

@c @group
@node     setgvmode, getspf, setbasetime, miscellaneous functions
@unnumberedsubsec setgvmode
@findex setgvmode (9.0)
@cindex interpolation
@cindex decimation
@cindex resampling
@cindex multi-frequency records (reading)

@example
void setgvmode(int *@var{mode})
@end example
@noindent
@c @end group

@noindent
This function sets the mode used by @code{getvec} when reading a
multi-frequency record (@pxref{Multi-Frequency Records}).  If @var{mode} is
@code{DB_LOWRES}, @code{getvec} decimates any signals sampled at multiples of
the frame rate, so that one sample is returned per signal per frame (i.e., the
oversampled signals are resampled by simple averaging of the samples for each
signal within each frame).  If @var{mode} is @code{DB_HIGHRES}, each sample of
any oversampled signal is returned by successive invocations of @code{getvec},
and each sample of any signal sampled at a lower frequency is returned by two
or more successive invocations of @code{getvec} (i.e., the less frequently
sampled signals are resampled using zero-order interpolation).  @code{getvec}
operates in @code{DB_LOWRES} mode by default.  @code{DB_LOWRES} and
@code{DB_HIGHRES} are defined in @file{<ecg/db.h>}.

In DB library version 9.6 and later versions, @code{setgvmode} also affects
how annotations are read and written.  If @code{setgvmode(DB_HIGHRES)} is
invoked @emph{before} using @code{annopen}, @code{dbinit}, @code{getvec},
@code{sampfreq}, @code{strtim}, or @code{timstr}, then all @code{DB_Time}
data (including the @code{time} attributes of annotations read by @code{getann}
or written by @code{putann}) visible to the application are in units of the
high-resolution sampling intervals.  (Otherwise, @code{DB_Time} data are in
units of frame intervals.)

@c @group
@node     getspf, counter conversion, setgvmode, miscellaneous functions
@unnumberedsubsec getspf
@findex getspf (9.6)
@cindex interpolation
@cindex decimation
@cindex resampling
@cindex multi-frequency records (reading)

@example
int getspf(void)
@end example
@noindent
@strong{Return:}
@table @asis
@item @t{(int)}
the number of samples per signal per frame
@end table
@c @end group

@noindent
Unless the application has used @code{setgvmode(DB_HIGHRES)} and has
then opened a multi-frequency record, this function returns 1.  For the
case of a multi-frequency record being read in high resolution mode,
however, @code{getspf} returns the number of samples per signal per
frame (hence @code{sampfreq(NULL)/getspf()} is the number of frames
per second).

@c @group
@node     counter conversion, setdb, getspf, miscellaneous functions
@cindex base counter value
@cindex counter (base)
@cindex counter frequency
@cindex counter value
@cindex frequency (counter)
@cindex tape counter

Database records are sometimes obtained from analog tapes for which a
tape counter is available.  Since many analog tape recorders lack
elapsed time indicators, it is often useful to identify events in the
analog tape using counter values.  A similar situation may arise if a
chart recording or other hard copy with numbered pages is to be compared
with a database record.  To simplify cross-referencing between the
analog tape or chart and the digital database record, the DB library
supports conversion of counter values (or page numbers) to time.  For this
to be possible, the counter must be linear (i.e., it must change at the
same rate throughout the tape; this is not true of those that count the
number of revolutions of the supply or take-up reel), and the base
counter value (the counter value or page number corresponding to sample
0) and the counter frequency (the difference between counter values
separated by a one-second interval, or the reciprocal of the number of
seconds per page) must be defined.  The following four functions,
first introduced in DB library version 5.2, are used to obtain or set
the values of these parameters.

@unnumberedsubsec getcfreq
@findex getcfreq (5.2)
@example
DB_Frequency getcfreq(void)
@end example
@noindent
@strong{Return:}
@table @asis
@item @t{(DB_Frequency)}
the counter frequency in Hz
@end table
@c @end group

@noindent
This function returns the currently-defined counter frequency.  The
counter frequency is set by the functions that read @file{header}
files, or by @code{setcfreq}.  If the counter frequency has not been
defined explicitly, @code{getcfreq} returns the sampling frequency.

@c @group
@unnumberedsubsec setcfreq
@findex setcfreq (5.2)
@example
void setcfreq(DB_Frequency @var{freq})
@end example
@c @end group

@noindent
This function sets the counter frequency.  Use @code{setcfreq}
before creating a @file{header} file
@iftex
(@pxref{newheader, , @code{newheader}}).
@end iftex
@ifinfo
(@pxref{newheader}).
@end ifinfo
The effect of @code{setcfreq} is nullified by later invoking any of the
functions that read @file{header} files.  If @var{freq} is zero or
negative, the counter frequency is treated as equivalent to the sampling
frequency.

@c @group
@unnumberedsubsec getbasecount
@findex getbasecount (5.2)
@example
double getbasecount(void)
@end example
@noindent
@strong{Return:}
@table @asis
@item @t{(double)}
base counter value
@end table
@c @end group

@noindent
This function returns the base counter value, which is set by the
functions that read @file{header} files, or by @code{setbasecount}.
If the base counter value has not been set explicitly,
@code{getbasecount} returns zero.

@c @group
@unnumberedsubsec setbasecount
@findex setbasecount (5.2)
@example
void setbasecount(double @var{count})
@end example
@c @end group

@noindent
This function sets the base counter value.  Use @code{setbasecount}
before creating a @file{header} file
@iftex
(@pxref{newheader, , @code{newheader}}).
@end iftex
@ifinfo
(@pxref{newheader}).
@end ifinfo
The effect of @code{setbasecount} is nullified by later invoking any of the
functions that read @file{header} files.

@c @group
@node     setdb, getdb, counter conversion, miscellaneous functions
@unnumberedsubsec setdb
@findex setdb
@cindex database path (changing)
@cindex changing the DB path

@example
void setdb(char *@var{string})
@end example
@c @end group

@noindent
This function may be used to set or change the database path (@pxref{DB
path}) within a running program.  The argument points to a
null-terminated string that specifies the desired database path (but see
the next paragraph for an exception).  The
string contains directory names separated by colons (@samp{:}) under
UNIX or by semicolons (@samp{;}) under MS-DOS.  An empty component,
indicated by an initial or terminal separator, or by two consecutive
separators, will be understood to specify the current directory.  If the
string is empty or @code{NULL}, the database path is limited to the
current directory.

@cindex indirect DB path
@cindex file containing DB path
@cindex database path file (indirect)

If @var{string} begins with @samp{@@}, the remaining characters of
@var{string} are taken as the name of a file from which the DB path is
to be read.  This file may contain either the DB path, as described in
the previous paragraph, or another indirect DB path specification.
Indirect DB path specifications may be nested no more than ten levels
deep (an arbitrary limit imposed to avoid infinite recursion).
Evaluation of indirect DB paths is deferred until @code{getdb} is
invoked, either explicitly or by the DB library while attempting to open
an input file (e.g., using @code{annopen} or @code{isigopen}).  (The
features described in this paragraph were first introduced in DB library
version 8.0.)

The @code{setdb} function does not copy the string itself, which must
therefore be kept valid by the caller, nor does it modify the environment
variable @code{DB}, which will be inherited by any processes spawned
from the caller.  For portability, as well as efficiency, it is better
to use @code{setdb} than to manipulate @code{DB} directly (via
@code{putenv}, for example); furthermore, doing the latter is
ineffective (within the current process) once the first DB file has been
opened.
@iftex
@xref{getdb, , @code{getdb}},
@end iftex
@ifinfo
@xref{getdb},
@end ifinfo
for an example of the use of @code{setdb}.

@c @group
@node     getdb, dbfile, setdb, miscellaneous functions
@unnumberedsubsec getdb
@findex getdb
@cindex database path (reading)
@cindex reading the DB path

@example
char *getdb(void)
@end example
@noindent
@strong{Return:}
@table @asis
@item @t{(char *)}
pointer to the database path string
@end table
@c @end group

@noindent
This function returns the current database path.  For example, this code
fragment

@example
@dots{}
char *oldp, *newp;

oldp = getdb();
if (newp = malloc(strlen("/usr/mydb:") + strlen(oldp) + 1)) @{
    sprintf(newp, "/usr/mydb:%s", oldp);
    setdb(newp);
@}
@dots{}
@end example

@noindent
adds the directory @samp{/usr/mydb} to the beginning of the database
path.  (To perform a similar operation under MS-DOS, replace the colons
by semicolons.  The standard @samp{/} directory separator can be used,
even under MS-DOS; if you elect to use the alternate @samp{\}, remember to
quote it within a C string as @samp{\\}.)

@c @group
@node     dbfile, dbflush, getdb, miscellaneous functions
@unnumberedsubsec dbfile
@findex dbfile (4.3)
@cindex filenames of DB files (obtaining)
@cindex pathnames of DB files (obtaining)

@example
char *dbfile(char *@var{type}, char *@var{record})
@end example
@noindent
@strong{Return:}
@table @asis
@item @t{(char *)}
pointer to a filename, or @code{NULL}
@end table
@c @end group

@noindent
This function attempts to locate an existing DB file by searching the database
path (@pxref{DB path}).  Normally, the file is specified by its @var{type}
(e.g., @file{header}, or an annotator name such as @file{atruth}) and by the
@var{record} to which it belongs.  A file that does not include a record name
as part of its name can be found by @code{dbfile} if the name is passed in the
@var{type} variable and @var{record} is @code{NULL}.  The string returned by
@code{dbfile} includes the appropriate component of the database path;  since
the database path may include empty or non-absolute components, the string is
not necessarily an absolute pathname.  If the file cannot be found,
@code{dbfile} returns @code{NULL}.  (This function was first introduced
in DB library version 4.3.)

@c @group
@node     dbflush, getinfo, dbfile, miscellaneous functions
@unnumberedsubsec dbflush
@findex dbflush
@cindex flushing output annotations and samples

@example
void dbflush(void)
@end example
@c @end group

@noindent
This function brings database output files up-to-date by forcing any
output annotations or samples that are buffered to be written to the
output files.

@c @group
@node     getinfo, putinfo, dbflush, miscellaneous functions
@unnumberedsubsec getinfo
@findex getinfo (4.0)
@cindex @code{header} info (reading) 
@cindex info (in @code{header} files)

@example
char *getinfo(char *@var{record})
@end example
@noindent
@strong{Return:}
@table @asis
@item @t{(char *)}
pointer to an ``info'' string, or @code{NULL}
@end table
@c @end group

@noindent
This function reads an ``info'' string from the @file{header} file for
the specified @var{record}.  Info strings are null-terminated and do not
contain newline characters.  The @file{header} files of some records may
contain no info strings;  others may contain more than one info string.
To read additional info strings after the first, use @code{getinfo(NULL)}.
For example, the following code fragment may be used to read and print
all of the info for record @file{100s}:

@example
@dots{}
char *info;

if (info = getinfo("100s"))
    do @{
        puts(info);
    @} while (info = getinfo(NULL));
@dots{}
@end example

(This function was first introduced in DB library version 4.0.)

@c @group
@node     putinfo, setibsize, getinfo, miscellaneous functions
@unnumberedsubsec putinfo
@findex putinfo (4.0)
@cindex @code{header} info (writing)
@cindex info (in @code{header} files)

@example
int putinfo(char *@var{s})
@end example
@noindent
@strong{Return:}
@table @asis
@item @t{ 0}
Success
@item @t{-1}
Failure: @code{header} not initialized
@end table
@c @end group

@noindent
This function writes @var{s} as an ``info'' string into the
@file{header} file that was created by the most recent invocation of
@code{newheader}.  The string argument, @var{s}, must be null-terminated
and should not contain newline characters.  No more than 254 characters
may be written in a single invocation of @code{putinfo}.  Two or more
info strings may be written to the same @code{header} by successive
invocations of @code{putinfo}.  Note that @code{newheader} or
@code{setheader} must be used before @code{putinfo}.  (This function was
first introduced in DB library version 4.0.)

@c @group
@node     setibsize, setobsize, putinfo, miscellaneous functions
@unnumberedsubsec setibsize
@findex setibsize (5.0)
@cindex buffer size (setting)
@cindex input buffer size
@cindex @code{getvec} buffer size

@example
int setibsize(int @var{size})
@end example
@noindent
@strong{Return:}
@table @asis
@item @t{>0}
Success: the returned value is the new input buffer size in bytes
@item @t{-1}
Failure: buffer size could not be changed
@item @t{-2}
Failure: illegal value for @var{size}
@end table
@c @end group

@noindent
This function can be used to change the default size of the input
buffers allocated by @code{getvec}.  It cannot be used while input
signals are open (i.e., after invoking @code{isigopen} or @code{dbinit}
and before invoking @code{dbquit}).  If @var{size} is positive, the
default input buffers will be @var{size} bytes;  if @var{size} is zero,
the system default buffer size (@code{BUFSIZ}) is used.  Note that
the default buffer size has no effect on reading signals for which
an explicit buffer size is given in the @file{header} file, i.e.,
those for which the @code{bsize} field of the @code{DB_Siginfo} structure
@iftex
(@pxref{DB_Siginfo structures, , Signal Information Structures})
@end iftex
@ifinfo
(@pxref{DB_Siginfo structures})
@end ifinfo
is non-zero.
(This function was first introduced in DB library version 5.0.)

@c @group
@node     setobsize, dbgetskew, setibsize, miscellaneous functions
@unnumberedsubsec setobsize
@findex setobsize (5.0)
@cindex buffer size (setting)
@cindex output buffer size
@cindex @code{putvec} buffer size

@example
int setobsize(int @var{size})
@end example
@noindent
@strong{Return:}
@table @asis
@item @t{>0}
Success: the returned value is the new output buffer size in bytes
@item @t{-1}
Failure: buffer size could not be changed
@item @t{-2}
Failure: illegal value for @var{size}
@end table
@c @end group

@noindent
This function can be used to change the default size of the output
buffers allocated by @code{putvec}.  It cannot be used while output
signals are open (i.e., after invoking @code{osigopen} or
@code{osigfopen} and before invoking @code{dbquit}).  If @var{size} is
positive, the default output buffers will be @var{size} bytes; if
@var{size} is zero, the system default buffer size (@code{BUFSIZ}) is
used.  Note that the default buffer size has no effect on writing
signals for which an explicit buffer size is given in the @file{header}
file read by @code{osigopen}, or in the @code{bsize} field of the
@code{DB_Siginfo} structure
@iftex
(@pxref{DB_Siginfo structures, , Signal Information Structures})
@end iftex
@ifinfo
(@pxref{DB_Siginfo structures})
@end ifinfo
passed to @code{osigfopen}.
(This function was first introduced in DB library version 5.0.)

@c @group
@node     dbgetskew, dbsetskew, setobsize, miscellaneous functions
@unnumberedsubsec dbgetskew
@findex dbgetskew (9.4)
@cindex intersignal skew
@cindex skew

@example
int dbgetskew(DB_Signal @var{s})
@end example
@noindent
@strong{Return:}
@table @asis
@item @t{(int)}
the skew (in frames) for input signal @var{s}
@end table
@c @end group

@noindent
This function returns the @dfn{skew} (as recorded in the @file{header}
file, but in frame intervals rather than in sample intervals) of the
specified input signal, or 0 if @var{s} is not a valid input signal
number.  Since sample vectors returned by @code{getvec} or
@code{getframe} are already corrected for skew, @code{dbgetskew} is
useful primarily for programs that need to rewrite existing
@file{header} files, where it is necessary to preserve the previously
recorded skews.  The following code fragment demonstrates how this can
be done:

@example
char *record;
int nsig;
DB_Signal s;
static DB_Siginfo siarray[DB_MAXSIG];

@dots{}

if ((nsig = isigopen(record, siarray, DB_MAXSIG)) < 1)
    exit(1);
for (s = 0; s < nsig; s++) @{
    dbsetskew(s, dbgetskew(s));
    dbsetstart(s, dbgetstart(s));
@}
setheader(record, siarray, (unsigned)nsig);
@end example

@noindent
Note that this function does not @emph{determine} the skew between
signals;  the problem of doing so is not possible to solve in the
general case.  @code{dbgetskew} merely reports what has previously been
determined by other means and recorded in the @file{header} file for the input
record.  (This function was first introduced in DB library version 9.4.)

@c @group
@node     dbsetskew, dbgetstart, dbgetskew, miscellaneous functions
@unnumberedsubsec dbsetskew
@findex dbsetskew (9.4)
@cindex intersignal skew
@cindex skew

@example
void dbsetskew(DB_Signal @var{s}, int @var{skew})
@end example
@c @end group

@noindent
This function sets the specified @var{skew} (in frames) to be recorded
by @code{newheader} or @code{setheader} for signal @var{s}.  For an
example of the use of @code{dbsetskew}, @pxref{dbgetskew}.  Note that
@code{dbsetskew} has no effect on the skew correction performed by
@code{getframe} (or @code{getvec}), which is determined solely by the
skews that were recorded in the @file{header} file at the time the input
signals were opened.  (This function was first introduced in DB library
version 9.4.)

@c @group
@node     dbgetstart, dbsetstart, dbsetskew, miscellaneous functions
@unnumberedsubsec dbgetstart
@findex dbgetstart (9.4)
@cindex byte offset
@cindex cruft (in signal files)
@cindex prolog (in signal files)
@cindex start of sample data

@example
long dbgetstart(DB_Signal @var{s})
@end example
@noindent
@strong{Return:}
@table @asis
@item @t{(long)}
the length of the prolog of the file that contains input signal @var{s}
@end table
@c @end group

@noindent
This function returns the number of bytes in the @dfn{prolog} of the
signal file that contains the specified input signal, as recorded in the
@file{header} file.  Note that @code{dbgetstart} does not
@emph{determine} the length of the prolog by inspection of the signal
file; it merely reports what has been determined by other means and
recorded in the @file{header} file.  Since the prolog is not readable
using the DB library, and since functions such as @code{isigopen} and
@code{isigsettime} take the prolog into account when calculating byte
offsets for @code{getframe} and @code{getvec}, @code{dbgetstart} is
useful primarily for programs that need to rewrite existing
@file{header} files, where it is necessary to preserve the previously
recorded byte offsets.  For an example of how this can be done,
@pxref{dbgetskew}.  (This function was first introduced in DB library
version 9.4.)

@c @group
@node     dbsetstart, , dbgetstart, miscellaneous functions
@unnumberedsubsec dbsetstart
@findex dbsetstart (9.4)
@cindex byte offset
@cindex cruft (in signal files)
@cindex prolog (in signal files)
@cindex start of sample data

@example
void dbsetstart(DB_Signal @var{s}, long @var{bytes})
@end example
@c @end group

@noindent
This function sets the specified prolog length (@var{bytes}) to be
recorded by @code{newheader} or @code{setheader} for signal @var{s}.
For an example of the use of @code{dbsetstart}, @pxref{dbgetskew}.  Note
that @code{dbsetstart} has no effect on the calculations of byte offsets
within signal files as performed by @code{isigsettime}, which are
determined solely by the contents of the @file{header} file at the time
the signals were opened.  (This function was first introduced in DB library
version 9.4.)


@node     Data Types, Annotation Codes, Functions, Top
@chapter Data Types

@noindent
@emph{Simple data types} used by the DB library are defined in
@file{<ecg/db.h>}.  These include:

@table @code
@item DB_Sample
@cindex @code{DB_Sample} (defined)
a signed integer type (at least 16 bits) used to represent sample
values, in units of adus.

@item DB_Time
@cindex @code{DB_Time} (defined)
a signed integer type (at least 32 bits) used to represent times and
time intervals, in units of sample intervals.  Only the magnitude is
significant;  the sign of a @code{DB_Time} variable indicates how it
is to be printed by @code{timstr} or @code{mstimstr}.

@item DB_Date
@cindex @code{DB_Date} (defined)
a signed integer type (at least 32 bits) used to represent Julian dates,
in units of days.

@item DB_Frequency
@cindex @code{DB_Frequency} (defined)
a floating point type used to represent sampling and counter
frequencies, in units of Hz.

@item DB_Gain
@cindex @code{DB_Gain} (defined)
a floating point type used to represent signal gains, in units of adus
per physical unit.

@item DB_Group
@cindex @code{DB_Group} (defined)
an unsigned integer type used to represent signal group numbers.

@item DB_Signal
@cindex @code{DB_Signal} (defined)
an unsigned integer type used to represent signal numbers.

@item DB_Annotator
@cindex @code{DB_Annotator} (defined)
an unsigned integer type used to represent annotator numbers.

@end table

@noindent
@emph{Composite data types} used by the DB library are also defined in
@file{<ecg/db.h>}.  These types, described in detail in the following
sections, include:

@table @code
@item DB_Siginfo
an object containing the name and global attributes of a given signal.

@item DB_Calinfo
an object containing calibration specifications for signals of a given
type.

@item DB_Anninfo
an object containing the name and attributes of a given annotator.

@item DB_Annotation
an object describing one or more attributes of one or more signals at a
given time.

@end table

Until DB library version 8.2, these types were known as @code{struct
siginfo}, @code{struct calinfo}, @code{struct anninfo}, and @code{struct
ann} respectively.  Except under Solaris (which has its own definition
for @code{siginfo}), these older names are still defined in
@file{<ecg/db.h>}, but their use is discouraged.

@menu
* DB_Siginfo structures::		Signal names and attributes.
* DB_Calinfo structures::		Signal calibration specifications.
* DB_Anninfo structures::		Annotator names and file types.
* DB_Annotation structures::		Annotation contents.
@end menu

@node DB_Siginfo structures, DB_Calinfo structures, Data Types, Data Types
@section Signal Information Structures
@cindex @code{DB_Siginfo} structure (defined)
@cindex signal information structure
@cindex information structure (signal)
@cindex structure (signal information)
@cindex attributes of signals (global)

The @var{siarray} argument for @code{isigopen}, @code{osigopen},
@code{dbinit}, and @code{osigfopen} is a pointer to an array of
objects of type @code{DB_Siginfo}.  The first three of these functions
fill in the @code{DB_Siginfo} objects to which @var{siarray} points, but
the caller must supply initialized @code{DB_Siginfo} objects to
@code{osigfopen}.  Each object specifies the attributes of a
signal:

@table @code
@item char *fname
@cindex signal file name
a pointer to a null-terminated string that names the file in which
samples of the associated signal are stored.  Input signal files are
found by prefixing @code{fname} with each of the components of the
database path in turn (@pxref{DB path}).  @code{fname} may include
relative or absolute path specifications if necessary; the use of an
absolute pathname, combined with an initial null component in @code{DB},
reduces the time needed to find the signal file to a minimum.  If
@code{fname} is @samp{-}, it refers to the standard input or
output.

@item char *desc
@cindex signal file description
a pointer to a null-terminated string without embedded newlines (e.g.,
@samp{ECG lead V1} or @samp{trans-thoracic impedance}).  The length of
the @code{desc} string is restricted to a maximum of @code{DB_MAXDSL}
(defined in @file{<ecg/db.h>}) characters, not including the null.

@item char *units
@cindex physical units
@cindex units (physical)
@cindex scale (amplitude)
a pointer to a null-terminated string without embedded whitespace.  The
string specifies the physical units of the signal; if NULL, the units
are assumed to be millivolts.  The length of the @code{units} string is
restricted to a maximum of @code{DB_MAXUSL} (defined in
@file{<ecg/db.h>}) characters (not including the null).

@item DB_Gain gain
@cindex gain
@cindex adu
@cindex units (ADC)
@cindex scale (amplitude)
the number of analog-to-digital converter units (adus) per physical unit
(see previous item) relative to the original analog signal; for an ECG,
this is roughly equal to the amplitude of a normal QRS complex.  If
@code{gain} is zero, no amplitude calibration is available; in this
case, a @code{gain} of @code{DEFGAIN} (defined in @file{<ecg/db.h>}) may
be assumed.

@item DB_Sample initval
@cindex initial value of signal
the initial value of the associated signal (i.e., the value of sample
number 0).

@item DB_Group group
@cindex signal group
@cindex multiplexed signal file
the signal group number.  All signals in a given group are stored in the
same file.  If there are two or more signals in a group, the file is
called a @dfn{multiplexed signal file}.  Group numbers begin at 0;
arrays of @code{DB_Siginfo} structures are always kept ordered with respect
to the group number, so that signals belonging to the same group are
described by consecutive entries in @var{siarray}.

@item int fmt
@cindex signal file format
@cindex format (signal file)
@cindex difference format
@cindex first difference
the signal storage format.  The most commonly-used formats are format 8
(8-bit first differences), format 16 (16-bit amplitudes), and format 212
(pairs of 12-bit amplitudes bit-packed into byte triplets).  See
@file{<ecg/db.h>} for a complete list of supported formats.  All signals
belonging to the same group must be stored in the same format.

@item int spf
@cindex multi-frequency records
@cindex frame rate
@cindex oversampled signals
@cindex signals (oversampled)
@cindex samples per frame
the number of samples per frame.  This is 1, for all except oversampled
signals in multi-frequency records, for which @code{spf} may be between
2 and @code{DB_MAXSPF} (defined in @file{<ecg/db.h>}).  Note that
non-integer values are not permitted (thus the frame rate must be chosen
such that all sampling frequencies used in the record are integer
multiples of the frame rate).

@item int bsize
@cindex block size
the block size, in bytes.  For signal files that reside on UNIX character
device special files (or their equivalents), the @code{bsize} field
indicates how many bytes must be read or written at a time
(@pxref{Special Files}).  For ordinary disk files, @code{bsize} is zero.
All signals belonging to a given group have the same
@code{bsize}.

@item int adcres
@cindex resolution
@cindex ADC resolution
the ADC resolution in bits.  Typical ADCs have resolutions between 8 and 16
bits inclusive.

@item int adczero
@cindex ADC zero
the ADC output given an input that falls exactly at the center of the
ADC range (normally 0 VDC).  Bipolar ADCs produce two's complement
output; for these, @code{adczero} is usually zero.  For the MIT DB,
however, an offset binary ADC was used, and @code{adczero} was 1024.

@item int baseline
@cindex baseline
@cindex isoelectric level
@cindex physical zero level
the value of ADC output that would map to 0 physical units input.
The value of @code{adczero} is not synonymous with that of
@code{baseline} (the isoelectric or physical zero level of the signal);  the
@code{baseline} is a characteristic of the @emph{signal}, while
@code{adczero} is a characteristic of the @emph{digitizer}.  The value
of @code{baseline} need not necessarily lie within the output range of
the ADC;  for example, if the @code{units} are @samp{degrees_Kelvin},
and the ADC range is 200--300 degrees Kelvin, @code{baseline} corresponds to 
absolute zero, and lies well outside the range of values actually produced
by the ADC.

@item long nsamp
@cindex length of signal file
@cindex duration of signal file
@cindex signal file length
the number of samples in the signal.  All signals in a given record
must have the same number of samples.  If @code{nsamp} is zero, the
number of samples is unspecified, and the @code{cksum} (see the next item)
is not used;  this is useful for specifying signals that are obtained
from pipes, for which the length may not be known.

@item int cksum
@cindex checksum of signal file
@cindex signal file checksum
a 16-bit checksum of all samples.  This field is not usually accessed by
application programs; @code{newheader} records checksums calculated by
@code{putvec} when it creates a new @file{header} file, and
@code{getvec} compares checksums that it calculates against @code{cksum}
at the end of the record, provided that the entire record was read
through without skipping samples.
@end table

@cindex signal number
The number of @code{DB_Siginfo} structures in @var{siarray} is given by
the @var{nsig} argument of the functions that open signal files.  Input
and output signal numbers are assigned beginning with 0 in the order in
which the signals are given in @var{siarray}.  Note that input signal 0
and output signal 0 are distinct.  Input signal numbers are supplied to
@code{aduphys}, @code{physadu}, @code{adumuv}, and @code{muvadu} in
their first arguments.  There may be no more than @code{DB_MAXSIG}
(defined in @file{<ecg/db.h>}) input signals and @code{DB_MAXSIG} output
signals open at once.  @xref{Example 5}, for an illustration of how to
read signal specifications from @code{DB_Siginfo} structures.

@node DB_Calinfo structures, DB_Anninfo structures, DB_Siginfo structures, Data Types
@section Calibration Information Structures
@cindex @code{DB_Calinfo} structure (defined)
@cindex calibration information structure

The @var{cal} argument for @code{getcal} and @code{putcal} is a pointer
to an object of type @code{DB_Calinfo}.  A @code{DB_Calinfo} object
contains information about signals of a specified type:

@table @code
@item char *sigtype
@cindex signal type
a pointer to a null-terminated string without embedded tabs or newlines.
This field describes the type(s) of signals to which the calibration
specifications apply.  Usually, @code{sigtype} is an exact match to
(or a prefix of) the @code{desc} field of the @code{DB_Siginfo} object
that describes a matching signal.

@item char *units
@cindex physical units
@cindex units (physical)
a pointer to a null-terminated string without embedded whitespace.
This field specifies the physical units of signals to which the
calibration specifications apply.  Usually, the @code{units} field of
a @code{DB_Calinfo} structure must exactly match the @code{units} field of
the @code{DB_Siginfo} structure that describes a matching signal.

@item double scale
@cindex scale (amplitude)
@cindex plotting scale
@cindex display scale
the customary plotting scale, in physical units per centimeter.  DB
applications that produce graphical output may use @code{scale} as a
default.  Except in unusual circumstances, signals of different types
should be plotted at equal multiples of their respective @code{scale}s.

@item double low
@itemx double high
@cindex calibration pulse limits
@cindex pulse limits (calibration)
values (in physical units) corresponding to the low and high levels of
a calibration pulse.  If the signal is AC-coupled (see below),
@code{low} is zero, and @code{high} is the pulse amplitude.

@item int caltype
@cindex AC-coupled signals
@cindex DC-coupled signals
@cindex calibration pulse shape
@cindex pulse shape (calibration)
a small integer that specifies the shape of the calibration pulse (see
@file{<ecg/db.h>} for definitions).  @code{caltype} is even if signals of
the corresponding @code{sigtype} are AC-coupled, and odd if they are
DC-coupled.
@end table

@cindex calibration list
The calibration list is a memory-resident linked list of @code{DB_Calinfo}
structures.  It is accessible only via @code{calopen}, @code{getcal},
@code{putcal}, @code{newcal}, and @code{flushcal}.

@node DB_Anninfo structures, DB_Annotation structures, DB_Calinfo structures, Data Types
@section Annotator Information Structures
@cindex @code{DB_Anninfo} structure (defined)
@cindex annotator information structure
@cindex annotator name
@cindex attributes of annotators
@cindex AHA-format annotation file
@cindex MIT-format annotation file
@cindex standard annotation file
@cindex format (annotation file)
@cindex MS-DOS file names
@cindex information structure (annotator)
@cindex structure (annotator information)

The @var{aiarray} argument for @code{annopen} and @code{dbinit} is a
pointer to an array of objects of type @code{DB_Anninfo}.  Each member
of the array contains information provided to @code{annopen} and
@code{dbinit} about an annotation file associated with the
record:

@table @code
@item char *name
@cindex annotator name
the annotator name.  The name @file{atruth} is reserved for a
@dfn{reference annotation file} supplied by the creator of the database
record to document its contents as accurately and thoroughly as
possible.  You may use other annotator names to identify annotation
files that you create; unless there are compelling reasons not to do so,
follow the convention that the annotator name is the name of the file's
creator (a program or a person).  To avoid confusion, do not use
@samp{dat}, @samp{data@var{n}}, @samp{d@var{n}}, or @samp{header} (all
of which are commonly used as parts of DB file names) as annotator
names.  The special name @samp{-} refers to the standard input or
output.  Other annotator names may contain upper- or lower-case letters,
digits, and underscores.  Annotation files are normally created in the
current directory and found in any of the directories in the database
path (@pxref{DB path}).  Under UNIX, it is possible to create or find
annotation files in any accessible directory by specifying annotator
names that begin with absolute or relative directory pathnames (note
that such prefixes are exempt from the restriction on legal characters
in annotator names).  Under MS-DOS, this is not possible; furthermore,
note that the only the first three characters of the annotator name are
used to construct the MS-DOS file name, so be careful to keep annotator
names distinct from each other and from commonly used MS-DOS file name
``extensions''.

@item int stat
@cindex AHA_READ
@cindex AHA_WRITE
@cindex READ
@cindex WRITE
the file type/access code.  Usually, @code{stat} is either @code{READ}
or @code{WRITE}, to specify standard (``MIT format'') annotation files
to be read by @code{getann} or to be written by @code{putann}.  Both MIT
DB and AHA DB annotation files are kept on-line in MIT format.  The
symbols @code{READ} and @code{WRITE} are defined in @file{<ecg/db.h>}.
An AHA-format annotation file can be read by @code{getann} or written by
@code{putann} if the @code{stat} field is set to @code{AHA_READ} or
@code{AHA_WRITE} before calling @code{annopen} or @code{dbinit}
(@pxref{Example 2}).  Other formats may be supported via a similar
mechanism; consult @file{<ecg/db.h>} for more information.
@end table

@cindex annotator number
The number of @code{DB_Anninfo} objects in @var{aiarray} is given by
the @var{nann} argument of @code{annopen} and @code{dbinit}.  The
annotation-reading function, @code{getann}, knows the annotators by
number only; @code{annopen} and @code{dbinit} assign input annotator
numbers beginning with 0 in the order in which they are given in the
array of @code{DB_Anninfo} objects.  Output annotator numbers used by
@code{putann} also start at 0; note that input annotator 0 and output
annotator 0 are distinct.  Annotator numbers are supplied to
@code{getann} and @code{putann} in their first arguments.  There may be
no more than @code{DB_MAXANN} (defined in @file{<ecg/db.h>}) input
annotators and @code{DB_MAXANN} output annotators open at once.
@iftex
@xref{annopen, , @code{annopen}},
@end iftex
@ifinfo
@xref{annopen},
@end ifinfo
for an example of how to set the contents of an array of @code{DB_Anninfo}
objects.

@node DB_Annotation structures, , DB_Anninfo structures, Data Types
@section Annotation Structures
@cindex @code{DB_Annotation} structure (defined)
@cindex annotation structure
@cindex structure (annotation)
@cindex attributes of signals (local)

The @var{annot} argument of @code{getann} and @code{putann} is an
object of type @code{DB_Annotation} containing these fields:

@table @code
@item long time
@cindex annotation time
@cindex time of annotation
@cindex reference point (on QRS)
time of the annotation, in samples from the beginning of the record.
The times of beat annotations in the @file{atruth} files for the MIT DB
generally coincide with the R-wave peak in signal 0; for the AHA DB,
they generally coincide with the PQ-junction.

@item char anntyp
@cindex annotation code field
@cindex annotation type
@cindex type (annotation)
annotation code; an integer between 1 and @code{ACMAX} (defined in
@file{<ecg/ecgcodes.h>}). @xref{Annotation Codes}, for a list of
legal annotation codes.

@item signed char subtyp
@itemx signed char chan
@itemx signed char num
@cindex annotation subtype
@cindex subtype (annotation)
@cindex user-defined fields in annotation
@cindex signal (associating annotation with)
numbers between
@iftex
@minus{}128
@end iftex
@ifinfo
-128
@end ifinfo
and 127.  In MIT DB @file{atruth} files, the
@code{subtyp} field is used with noise and artifact annotations to
indicate which signals are affected (@pxref{Annotation Codes}).
The @code{chan} field is intended to indicate the signal to which the
annotation is attached.  More than one annotation may be written with
the same @code{time} if the @code{chan} fields are distinct and in
ascending order.  The semantics of the @code{chan} field are
unspecified, however; users may assign any desired meaning, which need
not have anything to do with signal numbers.  In user-created annotation
files, these fields can be used to store arbitrary small integers.  The
@code{subtyp} field requires no space in a standard annotation file
unless it is non-zero; the @code{chan} and @code{num} fields require no
space unless they have changed since the previous annotation.

@item char *aux
@cindex annotation @code{aux} string
@cindex @code{aux} string (annotation)
a free text string.  The first byte is interpreted as an @code{unsigned
char} that specifies the number of bytes that follow (up to 255).  In
MIT DB @file{atruth} files, the @code{aux} field is used with rhythm
change annotations to specify the new rhythm, and with comment
annotations to store the text of the comment
@iftex
(@pxref{Annotation Codes}).
@end iftex
The string can contain arbitrary binary
data, including embedded nulls.  It is unwise to store anything but ASCII
strings, however, if the annotation file may be transported to a system with
a different architecture (e.g., on which multiple-byte quantities may have
different sizes or byte layouts).  The @code{aux} field requires no
space in a standard annotation file if it is @code{NULL}.  Note that
conversion of annotation files to other formats may entail truncation or
loss of the @code{aux} string.  Note also that the @code{aux} pointer
returned by @code{getann} points to a small static buffer (separately
allocated for each input annotator beginning with DB library version
9.4) that may be overwritten by subsequent calls.
@end table

@xref{Example 3}, for a short program that examines the contents of a
@code{DB_Annotation}.


@node     Annotation Codes, Database Files, Data Types, Top
@chapter Annotation Codes

@menu
* Mapping macros::		Macros for mapping annotation codes.
@end menu

@cindex ECG annotation code
@cindex annotation code
@cindex code (annotation)

Application programs that deal with annotations should include the
line

@example
#include <ecg/ecgcodes.h>
@end example

@noindent
which provides the symbolic definitions of annotation codes given in the
first column of the table below.  (The second column of the table shows
the strings returned by @code{annstr} and @code{ecgstr}.)

@display
@emph{Beat annotation codes:}
@t{NORMAL   N  } Normal beat
@t{LBBB     L  } Left bundle branch block beat
@t{RBBB     R  } Right bundle branch block beat
@t{BBB      B  } Bundle branch block beat (unspecified)
@t{APC      A  } Atrial premature beat
@t{ABERR    a  } Aberrated atrial premature beat
@t{NPC      J  } Nodal (junctional) premature beat
@t{SVPB     S  } Supraventricular premature or ectopic beat (atrial or nodal)
@t{PVC      V  } Premature ventricular contraction
@t{RONT     r  } R-on-T premature ventricular contraction
@t{FUSION   F  } Fusion of ventricular and normal beat
@t{AESC     e  } Atrial escape beat
@t{NESC     j  } Nodal (junctional) escape beat
@t{SVESC    n  } Supraventricular escape beat (atrial or nodal) [1]
@t{VESC     E  } Ventricular escape beat
@t{PACE     P  } Paced beat
@t{PFUS     f  } Fusion of paced and normal beat
@t{UNKNOWN  Q  } Unclassifiable beat
@t{LEARN    ?  } Beat not classified during learning

@emph{Non-beat annotation codes:}
@t{VFON     [  } Start of ventricular flutter/fibrillation
@t{FLWAV    !  } Ventricular flutter wave
@t{VFOFF    ]  } End of ventricular flutter/fibrillation
@t{NAPC     x  } Non-conducted P-wave (blocked APC) [4]
@t{WFON     (  } Waveform onset [4]
@t{WFOFF    )  } Waveform end [4]
@t{PWAVE    p  } Peak of P-wave [4]
@t{TWAVE    t  } Peak of T-wave [4]
@t{UWAVE    u  } Peak of U-wave [4]
@t{PQ       `  } PQ junction
@t{JPT      '  } J-point
@t{PACESP   ^  } (Non-captured) pacemaker artifact
@t{ARFCT    |  } Isolated QRS-like artifact [2]
@t{NOISE    ~  } Change in signal quality [2]
@t{RHYTHM   +  } Rhythm change [3]
@t{STCH     s  } ST segment change [1,3]
@t{TCH      T  } T-wave change [1,3,4]
@t{SYSTOLE  *  } Systole [1]
@t{DIASTOLE D  } Diastole [1]
@t{MEASURE  =  } Measurement annotation [1,3]
@t{NOTE     "  } Comment annotation [3]
@t{LINK     @@  } Link to external data [5]
@end display

@c @group
@noindent
@strong{Notes:}
@enumerate
@item
Codes @code{SVESC}, @code{STCH}, and @code{TCH} were first introduced
in DB library version 4.0.  Codes @code{SYSTOLE}, @code{DIASTOLE}, and
@code{MEASURE} were first introduced in DB library version 7.0.

@cindex @code{subtyp} (in @code{NOISE} annotation)
@cindex noisy signals (annotating)
@item
In MIT and ESC DB @file{atruth} files, each non-zero bit in the @code{subtyp}
field indicates that the corresponding signal contains noise (the least
significant bit corresponds to signal 0).

@cindex annotation @code{aux} string
@cindex @code{aux} string (annotation)
@item
The @code{aux} field contains an ASCII string (with prefixed byte count)
describing the rhythm, ST segment, T-wave change, measurement, or the
nature of the comment.  By convention, the character that follows the
byte count in the @code{aux} field of a @code{RHYTHM} annotation is @samp{(}.
See the @cite{MIT-BIH Arrhythmia Database Directory} for a list of rhythm
annotation strings.

@item
Codes @code{WFON}, @code{WFOFF}, @code{PWAVE}, @code{TWAVE}, and
@code{UWAVE} were first introduced in DB library version 8.3.  The
@samp{p} mnemonic now assigned to @code{PWAVE} was formerly assigned to
@code{NAPC}, and the @samp{t} mnemonic now assigned to @code{TWAVE} was
formerly assigned to @code{TCH}.  The obsolete codes @code{PQ}
(designating the PQ junction) and @code{JPT} (designating the J-point)
are still defined in @file{<ecg/ecgcodes.h>}, but are identical to
@code{WFON} and @code{WFOFF} respectively.

@item
The @code{LINK} code was first introduced in DB library version 9.6.  The
@code{aux} field of a @code{LINK} annotation contains a URL (a uniform
resource locator, in the form @file{http://machine.name/some/data},
suitable for passing to a Web browser such as Netscape or Mosaic).  @code{LINK}
annotations may be used to associate extended text, images, or other data with
an annotation file.  If the @code{aux} field contains any whitespace, text
following the first whitespace is taken as descriptive text to be displayed by
a DB browser such as @code{WAVE}.

@end enumerate
@c @end group

@cindex @code{NOTQRS} (annotation code)
The annotation codes in the table above are the predefined values of the
@code{anntyp} field in a @code{DB_Annotation}.  Other values in the range
of 1 to @code{ACMAX} (defined in @file{<ecg/ecgcodes.h>}) are legal but
do not have preassigned meanings.  The constant @code{NOTQRS}, also defined in
@file{<ecg/ecgcodes.h>}, is not a legal value for @code{anntyp}, but is
a possible output of the macros discussed below.

@node    Mapping macros, , Annotation Codes, Annotation Codes
@section Macros for Mapping Annotation Codes
@cindex macros
@cindex annotation code mapping
@cindex mapping annotation codes

Application programs that use the macros described in this section
should include the line
@example
#include <ecg/ecgmap.h>
@end example

@noindent
which will make their definitions, and those in @file{<ecg/ecgcodes.h>},
available.

@table @code
@findex isann
@cindex annotation code (legal)
@cindex legal annotation code
@item isann(@var{c})
true (1) if @var{c} is a legal annotation code, false (0)
otherwise

@cindex QRS annotation code
@findex isqrs
@item isqrs(@var{c})
true (1) if @var{c} denotes a QRS complex, false (0) otherwise

@findex map1
@item map1(@var{c})
maps @var{c} into one of the set @{@code{NOTQRS}, @code{NORMAL},
@code{PVC}, @code{FUSION}, @code{LEARN}@}

@findex map2
@item map2(@var{c})
maps @var{c} into one of the set @{@code{NOTQRS}, @code{NORMAL},
@code{SVPB}, @code{PVC}, @code{FUSION}, @code{LEARN}@}

@findex annpos (6.0)
@item annpos(@var{c})
maps @var{c} into one of the set @{@code{APUNDEF}, @code{APSTD},
@code{APHIGH}, @code{APLOW}, @code{APATT}, @code{APAHIGH},
@code{APALOW}@} (see @file{<ecg/ecgmap.h>} for definitions of these
symbols;  this macro was first introduced in DB library version 6.0)
@end table

@findex setisqrs (6.0)
@findex setmap1 (6.0)
@findex setmap2 (6.0)
@findex setannpos (6.0)
If you define your own annotation codes, you may wish to modify the
tables used by the macros above.  The file @file{<ecg/ecgmap.h>} also defines
@code{setisqrs(@var{c}, @var{x})}, @code{setmap1(@var{c}, @var{x})},
@code{setmap2(@var{c}, @var{x})}, and @code{setannpos(@var{c}, @var{x})}
for this purpose.  In each case, @var{x} is the value to be returned
when the corresponding mapping macro is invoked with an argument of
@var{c}.  (These macros were first introduced in DB library version
6.0.)

The macros below convert between AHA and MIT annotation codes;  they are
also defined in @file{<ecg/ecgmap.h>}.

@findex ammap
@findex mamap
@cindex AHA annotation code
@table @code
@item ammap(@var{a})
maps @var{a} (an AHA annotation code) into an MIT annotation code
(one of the set @{@code{NORMAL}, @code{PVC}, @code{FUSION}, @code{RONT},
@code{VESC}, @code{PACE}, @code{UNKNOWN}, @code{VFON}, @code{VFOFF},
@code{NOISE}, @code{NOTE}@}), or @code{NOTQRS}

@item mamap(@var{c}, @var{s})
maps @var{c} (an MIT annotation code) into an AHA annotation code
(one of the set @{@samp{N}, @samp{V}, @samp{F}, @samp{R}, @samp{E},
@samp{P}, @samp{Q}, @samp{[}, @samp{]}, @samp{U}, @samp{O}@}); @var{s}
is the MIT annotation @code{subtyp} (significant only if @var{c} is
@code{NOISE})
@end table

@node     Database Files, Examples, Annotation Codes, Top
@chapter Database Files

The DB library has been constructed to provide a standard interface
between the database files and application programs.  Alternate means of
access to database files is strongly discouraged, since file formats may
change.  Database files are located in the directories specified by
@code{DB} (@pxref{DB path}).

Recall that a DB record is not a file; rather, it is an extensible
@emph{collection} of database files (@pxref{Concepts 1, , Records}).
Thus, for example, record 100 of the MIT-BIH Arrhythmia Database
consists of the files named @file{100.hea}, @file{100.dat}, and
@file{100.atr} in the @file{mitdb} directory of the MIT-BIH Arrhythmia
Database CD-ROM, together with any additional files in other directories
that you may have associated with record 100 (such as your own
annotation file).  All files associated with a given record include the
record name in their file names, either as the first part of the name
(the default on writable UNIX and Macintosh file systems) or as the
suffix or extension (on CD-ROM and MS-DOS file systems).  Applications
that use the DB library under UNIX or the Macintosh OS can locate files
named using either of these conventions.  No explicit action (other than
choosing the file name, and locating the file in the DB path) is needed
in order to associate a new file with an existing DB record.

To find the location of a database file easily, you can use @file{dbwhich},
an application included with the DB Software Package.  Type
@code{dbwhich} for brief instructions on its use, or see the @cite{ECG
Database Applications Guide}.

@section File Types
@cindex file types

There are four types of files supported by the DB library:

@menu
* Header Files::		contain signal file names and attributes.
* Signal Files::		contain signals.
* Annotation Files::		contain annotations.
* Calibration Files::		contain signal calibration specifications.

The other topics in this section deal with special types of database access:

* AHA Format Files::		not used for on-line DB records.
* Standard I/O::		pipes and I/O redirection for DB files.
* Multiplexed Signal Files::	signal groups.
* Multi-Frequency Records::     signals sampled at different frequencies within
                                 within a given record.
* Multi-Segment Records::       concatenated records.
* Multiple Record Access::	how to have more than one record open.
* Special Files::		signal I/O using block and character devices.
* Piped and Local Records::	records for use with user-created signals.
* Annotation Order::            the canonical order for annotations:
                                 how and why to break the rules, and how
                                 to deal with the consequences of doing so.
@end menu

@node     Header Files, Signal Files, Database Files, Database Files
@unnumberedsubsec Header Files
@cindex @code{header} file

@dfn{Header files} have names of the form @file{header.@var{record}} on
writable UNIX and Macintosh file systems, where @var{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.)  On CD-ROM and MS-DOS file systems, because of restrictions on
the length of extensions, header files have names of the form
@file{@var{record}.HEA}.  Header files are text files, with lines
terminated by ASCII carriage-return/line-feed pairs, created by
@code{newheader}, @code{setheader}, or @code{setmsheader}, from which
@code{isigopen}, @code{osigopen}, and @code{dbinit} read the names of
the signal files and their attributes as given in the array of
@code{DB_Siginfo} objects; @code{sampfreq} also reads a @file{header}
file to determine the sampling frequency used for a record.

@node     Signal Files, Annotation Files, Header Files, Database Files
@unnumberedsubsec Signal Files
@cindex signal file

@dfn{Signal files} usually have names of the form
@file{@var{record}.dat}.  (The @code{.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 @file{<ecg/db.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 @code{int} (at least
16 bits), regardless of the signal file format.

@node     Annotation Files, Calibration Files, Signal Files, Database Files
@unnumberedsubsec Annotation Files
@cindex annotation file

@dfn{Annotation files} have names of the form
@file{@var{annotator}.@var{record}} on writable UNIX and Macintosh file
systems (on CD-ROM and MS-DOS file systems,
@file{@var{record}.@var{annotator}}).  Those named
@file{atruth.@var{record}} (@file{@var{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.

@node     Calibration Files, AHA Format Files, Annotation Files, Database Files
@unnumberedsubsec Calibration Files
@cindex calibration file

Unlike header, signal, and annotation files, @dfn{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
@code{newcal}, from which @code{calopen} reads the calibration list
@iftex
(@pxref{DB_Calinfo structures, , Calibration Information Structures}).
@end iftex
@ifinfo
(@pxref{DB_Calinfo structures}).
@end ifinfo
The DB Software Package includes a standard calibration file,
@file{dbcal}, in the @file{udb} directory.

@node     AHA Format Files, Standard I/O, Calibration Files, Database Files
@unnumberedsubsec AHA Format Files
@cindex AHA format

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 (@pxref{Multiplexed Signal Files}),
and may be read and written using @code{getvec} and @code{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 @code{getann} or @code{putann}, if the @code{stat}
field of the @code{DB_Anninfo} object is set to @code{AHA_READ} or
@code{AHA_WRITE} before opening the file.  @code{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 @code{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.  This format is compatible with neither
the original AHA format nor with any of the formats supported directly
by the DB library.  Programs @file{a2m} and @file{ad2m}, supplied with
the DB Software Package, can convert files in this format (as well as
those in the original AHA format) to the standard formats.

@node Standard I/O, Multiplexed Signal Files, AHA Format Files, Database Files
@section Using Standard I/O for Database Files
@cindex standard I/O (as DB files)
@cindex pipes (as DB files)

If @samp{-} is supplied as a record name to any of the functions that
read or write @file{header} files, the @file{header} file is taken to be
the standard input or output, as appropriate.  If the name of a signal
file is specified in the @file{header} file (or in the array of
@code{DB_Siginfo} objects passed to @code{osigfopen}) as @samp{-}, the
standard input (output) is used by @code{getvec} (@code{putvec}).  If
the name of an annotator is given in the array of @code{DB_Anninfo} objects
as @samp{-}, the standard input (output) is used by @code{getann}
(@code{putann}).  If the name of a calibration file is given as
@samp{-}, the standard input (output) is used by @code{calopen}
(@code{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 DB 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.

@node     Multiplexed Signal Files, Multi-Frequency Records, Standard I/O, Database Files
@section Multiplexed Signal Files
@cindex multiplexed signal file
@cindex signal group

Multiplexed signal files may be identified by examining the @code{group}
fields of the array of @code{DB_Siginfo} objects returned by
@code{isigopen} or @code{dbinit}.  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 @var{n} signals
in the file, then each group of @var{n} successive samples in that file
contains a sample from each signal, always in the same order
(but @pxref{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
are multiplexed unless the record contains only one signal.

@node     Multi-Frequency Records, Multi-Segment Records, Multiplexed Signal Files, Database Files
@section Multi-Frequency Records
@cindex multi-frequency records
@cindex sampling frequency
@cindex frame (of samples)
@cindex sample frame
@cindex frequency multiplier

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
DB 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 @dfn{frame} of
samples contains one @emph{or more} samples from each signal.  The
@dfn{frame rate} (base sampling frequency) of the record, as recorded in the
header file and as normally returned by @code{sampfreq}, is defined as
the number of frames per second.  Signals sampled at multiples of the
frame rate are referred to as @dfn{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
@code{spf} field of the @code{DB_Siginfo} structure for the signal.

A frame can be read as it was written
@iftex
(@pxref{getframe, , @code{getframe}})
@end iftex
@ifinfo
(@pxref{getframe})
@end ifinfo
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 @code{getvec} interface, which returns
(as always) one sample per signal on each invocation.  By default,
@code{getvec} reads multi-frequency records in @dfn{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 @code{setgvmode} can be used to select @dfn{high-resolution} mode,
in which @code{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 @code{getvec}.  In this mode, @code{sampfreq} returns the number of samples
per signal returned by @code{getvec} per second of the record.  Furthermore
(when using DB library version 9.6 and later versions), all time quantities
passed to and from the DB library functions are understood to be in units of
these shorter sampling intervals; thus, for example, @code{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 @code{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, with only a one-line change in the source.

@node     Multi-Segment Records, Multiple Record Access, Multi-Frequency Records, Database Files
@section Multi-Segment Records
@cindex multi-segment records
@cindex concatenating records
@cindex nested records

A multi-segment record consists of two or more concatenated segments.
Each segment is an ordinary DB 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 DB 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 @code{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 DB 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 @emph{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 DB application, reading a multi-segment record is
exactly like reading an ordinary record; specifically,
@code{isigsettime} works as expected, permitting jumps forward and
backward between as well as within segments.  Version 9.1 of the DB
library is the first to support reading and writing multi-segment
records.

@node     Multiple Record Access, Special Files, Multi-Segment Records, Database Files
@section Simultaneous Access to Multiple Records
@cindex simultaneous records
@cindex access to multiple records
@cindex multiple record access

Selection functions that accept @var{record} arguments (@code{annopen},
@code{isigopen}, @code{osigopen}, and @code{dbinit}) normally close any
active database files of the types with which each deals before
proceeding.  The argument @code{+}@var{record} is synonymous with
@var{record}, but has the effect of causing these functions to leave any
active files open.  (For convenience, the other functions that accept
@var{record} arguments --- @code{sampfreq}, @code{newheader}, and
@code{setheader} --- also treat @var{record} and @code{+}@var{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 @code{dbquiet} was invoked).  The
time-conversion functions
@iftex
(@pxref{timstr and strtim, , @code{timstr} and @code{strtim}})
@end iftex
@ifinfo
(@pxref{timstr and strtim})
@end ifinfo
will continue to use the sampling frequency and base
time defined for the first record that was opened, unless these
attributes are reset by @code{sampfreq}, @code{setsampfreq}, or
@code{setbasetime}.

Function @code{calopen} uses the @samp{+} 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 @samp{+}.

@node     Special Files, Piped and Local Records, Multiple Record Access, Database Files
@section Signals That Are Not Stored in Disk Files
@cindex character devices (as signal files)
@cindex nine-track tape
@cindex reading 9-track tape
@cindex signal file (on tape)
@cindex special files (as signal files)
@cindex UNIX character devices (as signal files)
@cindex writing 9-track tape

The @code{fname} component of a @code{DB_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 @file{/dev/rmt0} (the raw tape drive).  If I/O must be performed
in fixed-size blocks (such as for UNIX character devices), the
@code{bsize} component of the @code{DB_Siginfo} object must contain the
appropriate block size in bytes.  In such cases, the DB library must
obtain (using @code{malloc} (see @cite{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, @code{isigopen} or @code{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'' (@pxref{Piped and Local
Records}) instead.  The usual method is to read or write the signal file
using a utility such as UNIX's @code{dd} and to pipe the data to or from
the application program.  Although this approach is flexible, there are
a few drawbacks:

@enumerate
@item
While reading piped input, the standard input cannot be used for other
purposes by the application program.  Interactive programs can avoid
problems by opening @file{/dev/tty} for I/O, however.

@item
Programs that use @code{isigsettime} or @code{isgsettime} cannot perform
backward skips on piped input, and forward skips can be quite slow.

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

Several special-purpose @file{header} files allow application programs
to read data directly from 9-track tape.  The names of most of these
records (@file{512}, @file{1024}, @file{4096}, @file{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 @file{/dev/rmt0}.  Record @file{b} uses 8-bit difference
format, 6144 bytes/block, and is otherwise similar to the others.
Records @file{ahatape} and @file{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
@file{/dev/nrmt0} (the non-rewinding raw tape drive).  If the tape
density is encoded into the tape drive name on your system, additional
@file{header} files may be needed.

@node     Piped and Local Records, Annotation Order, Special Files, Database Files
@section Piped and Local Records
@cindex piped record
@cindex record (piped)
@cindex signal file (piped)
@cindex standard I/O (as DB files)

@dfn{Piped record} @file{header} files allow application programs to
read signals from the standard input, or write them to the standard
output.  Record @file{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 @file{16} specifies 16-bit format and a 12-bit ADC, and is
otherwise identical to record @file{8}.  ADCs from several manufacturers
can produce output in the format specified by record @file{16}; thus
such output can be piped directly into an application program using
record @file{16}.  Signal files in AHA format also match these
specifications.  Piped records for reading or writing other numbers of
signals also exist; they are named @file{8x@var{n}} and
@file{16x@var{n}}, where @var{n} is the number of signals (@var{n} = 1,
2, @dots{}, @code{DB_MAXSIG}). 

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

@node     Annotation Order, , Piped and Local Records, Database Files
@section Annotation Order
@cindex annotation order
@cindex canonical order of annotations
@cindex order of annotations

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

@cindex location (of annotation)
@cindex annotation location
@cindex virtual array of annotations
The combination of the @code{time} and @code{chan} fields of an
annotation defines a unique @dfn{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 DB library earlier than version 9.7.  In
these versions of the DB library, @code{putann} required that
annotations be written in canonical order, and refused to write any
out-of-order annotations supplied to it.

@cindex @code{sortann}
Current versions of the DB library do not impose this requirement.  In version
9.7 and later versions, @code{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 @code{dbquit} or @code{oannclose}.  This is accomplished
by running @file{sortann} (see the @cite{ECG Database Applications
Guide}) as a separate process using the ANSI C @code{system} function.  If this
function is not available, or if @file{sortann} cannot be run, @code{dbquit}
(or @code{oannclose}) emits a warning message describing how to post-process
the annotations to put them into canonical order.

@cindex changing an annotation
@cindex deleting an annotation
@cindex annotation (changing or deleting)
Although it is possible using current versions of the DB library to write two
or more annotations to the same location, @emph{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 @code{anntyp}, @code{subtyp}, @code{num}, or @code{aux} fields
of a previously-written annotation simply by writing another annotation to the
same location (i.e, with the same @code{time} and @code{chan} fields).  As a
special case, an application may @emph{delete} a previously-written annotation
by writing a @code{NOTQRS} annotation to the same location.  To move an
annotation to a different location (i.e., to change its @code{time} or
@code{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 @code{putann}.

@cindex unsorted annotation files
In unusual circumstances, an unsorted annotation file may be useful (for
example, as an aid for debugging the application that produced it; @file{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
@code{system} function may be a security problem, and you may wish to avoid
automatic sorting of annotations for this reason.  Set the environment variable
@code{DBNOSORT} (to any value) at run time, or define the symbol
@code{DBNOSORT} when compiling the DB library, if you wish to suppress
automatic annotation sorting by @code{dbquit} and @code{oannclose}.

@node Examples, Exercises, Database Files, Top
@chapter Programming Examples
@cindex examples
@cindex programming examples
The programs in this chapter are useful as models for a variety of
applications that use the DB 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
@example
cc @var{file.c} -ldb
@end example
@noindent
where @var{file.c} is the name of the file containing the source;
@pxref{Usage, , Using the DB Library}, for further information.
The sources for these examples are included in the DB Software Package,
within the @file{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 DB record.
* Example 9::			A signal averager.
* Example 10::			A QRS detector.
@end menu

@node     Example 1, Example 2, Examples, Examples
@unnumberedsec Example 1:  An Annotation Filter

The following program copies an annotation file, changing all QRS annotations
to @code{NORMAL} and deleting all non-QRS annotations.

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

@c @group
@noindent
@strong{Notes:}

@table @emph
@item Line 2:
All programs that use the DB library must include
@file{<ecg/db.h>}.

@item Line 3:
The @code{#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 @file{<ecg/ecgcodes.h>}, one of which
will be needed in line 22.

@item Line 7:
Since there will be two annotators (one each for input and output),
the array of @code{DB_Anninfo} objects has two members.

@item Line 9:
This structure will be filled in by @code{getann}, modified, and passed
to @code{putann} for output.

@item 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 @code{fgets}
copies into the string) with a null.  String arguments to DB library
functions should not include newline characters.

@item Lines 17--18:
Pointers to the character arrays (strings) containing the annotator
names are filled into the @code{name} fields of the array of
@code{DB_Anninfo} objects. Note that the @code{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
@cite{K&R}, pp.  97--100.  The input annotator is to be read, the output
annotator is to be written.  @code{READ} and @code{WRITE} are defined in
@file{<ecg/db.h>}.

@item Line 19:
Note that the first and second arguments of @code{annopen} are
the names of the respective arrays; thus @code{annopen} receives pointers
rather than values in its argument list.

@item Line 20:
An annotation is read from annotator 0 into @code{annot}.
The @samp{&} is necessary since @code{getann} requires a pointer to
the structure in order to be able to modify its contents.  When
@code{getann} returns a negative value, no more annotations remain to be
read and the loop ends.

@item Line 21:
The macro @code{isqrs} is defined in @file{<ecg/ecgmap.h>};
@code{isqrs(@var{x})} is true if @var{x} is an annotation code that
denotes a QRS complex, false if @var{x} is not a QRS annotation
code.

@item Line 22:
@code{NORMAL} is defined in @file{<ecg/ecgcodes.h>}.

@item Line 23:
The call to @code{putann} now writes the modified annotation in the
output annotator 0 file.  As for @code{getann}, a pointer to
@code{annot} must be passed using the @samp{&} operator.

@item Line 25:
All files are closed prior to exiting.  This is mandatory since the
program creates an output file with @code{putann}.
@end table
@c @end group

@node     Example 2, Example 3, Example 1, Examples
@unnumberedsec Example 2: An Annotation Translator

This program translates the @file{atruth} annotations for the record
named in its argument into an AHA-format annotation file with the
annotator name @file{aha}.

@example
 @i{1}  #include <stdio.h>
 @i{2}  #include <ecg/db.h>
 @i{3}
 @i{4}  main(argc, argv)
 @i{5}  int argc;
 @i{6}  char *argv[];
 @i{7}  @{
 @i{8}      DB_Anninfo an[2];
 @i{9}      DB_Annotation annot;
@i{10}
@i{11}      if (argc < 2) @{
@i{12}          fprintf(stderr, "usage: %s record\n", argv[0]);
@i{13}          exit(1);
@i{14}      @}
@i{15}      an[0].name = "atruth"; an[0].stat = READ;
@i{16}      an[1].name = "aha";    an[1].stat = AHA_WRITE;
@i{17}      if (annopen(argv[1], an, 2) < 0) exit(2);
@i{18}      while (getann(0, &annot) == 0 && putann(0, &annot) == 0)
@i{19}          ;
@i{20}      dbquit();
@i{21}      exit(0);
@i{22}  @}
@end example

@noindent
@strong{Notes:}

@table @emph
@item Lines 4--6:
If this doesn't look familiar, see @cite{K&R}, pp. 114--115.

@item Lines 11--14:
This is the standard idiom for producing those cryptic error messages
for which UNIX programs are notorious; @code{argv[0]} is the name by
which the program was invoked.

@item Lines 15--16:
These lines set up the annotator information.  Input annotator 0 is the
@file{atruth} annotation file, and output annotator 0 will be written
in AHA format.

@item Line 17:
If we can't read the input or write the output, quit with an error
message from @code{annopen}.

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

@item 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.
@end table

@node     Example 3, Example 4, Example 2, Examples
@unnumberedsec 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.

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

@noindent
@strong{Notes:}
@table @emph
@item Line 16:
The invocation of @code{sampfreq} here sets the internal variables needed
by @code{timstr} below.

@item Line 20:
This line gives the annotation time as a time of day.  If the base time
is omitted in the @file{header} file, or if we used
@code{timstr(annot.time)} instead, we would obtain the elapsed time from
the beginning of the record.

@item Lines 24--25:
This expression evaluates to an empty string unless the @code{aux}
string is non-empty.  It makes the assumption that @code{aux} is a
printable ASCII string; the printable part follows the length
byte.
@end table

@node     Example 4, Example 5, Example 3, Examples
@unnumberedsec 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.

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

@c @group
@noindent
@strong{Notes:}
@table @emph
@item Lines 21--25:
Here we allocate storage for the histogram.  The value returned by
@code{sampfreq}, if positive, specifies the number of sample intervals
per second; we will allocate 3 seconds' worth of bins, initialized to
zero.  See @cite{K&R}, page 167, for a description of
@code{calloc}.

@item Lines 26--28:
This code sets @code{t} to the time of the first annotated beat in the
record.

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

@item 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
@code{rr} starts at 0 rather than 1 in line 35?)
@end table
@c @end group

@node     Example 5, Example 6, Example 4, Examples
@unnumberedsec Example 5: Reading Signal Specifications

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

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

@noindent
@strong{Notes:}

@table @emph
@item Line 8:
There might be as many as @code{DB_MAXSIG} signals, so we allocate enough
@code{DB_Siginfo} objects to accommodate them all.

@item Line 15:
The command-line argument, @code{argv[1]}, is the record name.  The
number of readable signals is @code{nsig}.  This program will give
specifications for each of them, but not for any signals that are named
in the @file{header} file but are not readable.  If @code{nsig} < 1,
@code{isigopen} will print an error message; in this case the program
can't do anything useful, so it exits.

@item Line 18:
Invoking @code{timstr} with an argument of zero (here written @samp{0L}
to emphasize to the compiler that the argument is a @code{long} integer)
will obtain the starting time of the record.  If no starting time is
defined, @code{timstr} will return ``@code{0:00:00}''.

@item Lines 25--28:
Notice how a zero value for @code{gain} is interpreted.

@item Line 29:
If the @code{units} field is NULL, the physical units are assumed to be
millivolts (``mV'').

@item Lines 32--34:
If @code{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
@code{bsize}.

@item Lines 37--42:
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
@code{timstr} in line 39 is positive, it is interpreted as a time
interval.  The checksum is defined only if the record length is
defined.
@end table

@node     Example 6, Example 7, Example 5, Examples
@unnumberedsec Example 6: A Differentiator

@cindex digital filter
@cindex filter (digital)
The program below inverts and differentiates the signals read by
@code{getvec} and writes the results with @code{putvec}.  The output is
readable as record @file{dif}.  A wide variety of simple digital filters
can be modelled on this example; @pxref{Example 7}, for a more general
approach.

@example
 @i{1}  #include <stdio.h>
 @i{2}  #include <ecg/db.h>
 @i{3}
 @i{4}  main(argc, argv)
 @i{5}  int argc;
 @i{6}  char *argv[];
 @i{7}  @{
 @i{8}      static DB_Siginfo s[DB_MAXSIG];
 @i{9}      int i, nsig, nsamp = 1000;
@i{10}      DB_Sample vin[DB_MAXSIG], vout[DB_MAXSIG];
@i{11}
@i{12}      if (argc < 2) @{
@i{13}          fprintf(stderr, "usage: %s record\n", argv[0]); exit(1);
@i{14}      @}
@i{15}      if ((nsig = isigopen(argv[1], s, DB_MAXSIG)) <= 0) exit(2);
@i{16}      if (osigopen("8l", s, nsig) <= 0) exit(3);
@i{17}      while (nsamp-- > 0 && getvec(vin) > 0) @{
@i{18}          for (i = 0; i < nsig; i++)
@i{19}              vout[i] -= vin[i];
@i{20}          if (putvec(vout) < 0) break;
@i{21}          for (i = 0; i < nsig; i++)
@i{22}              vout[i] = vin[i];
@i{23}      @}
@i{24}      (void)newheader("dif");
@i{25}      dbquit();
@i{26}      exit(0);
@i{27}  @}
@end example

@noindent
@strong{Notes:}

@table @emph
@item Line 16:
Here we attempt to open as many output signals as there are input
signals; if we cannot do so, the program exits after @code{osigopen}
prints an error message.

@item Line 17:
The main loop of the program begins here.  If 1000 samples can be read
from each signal, the loop will end normally; if @code{getvec} fails
before 1000 samples have been read, the loop ends prematurely.

@item Lines 18--19:
For each signal, we compute the negated first difference by subtracting
the new sample from the previous sample.

@item Line 20:
One sample of each output signal is written here.

@item Lines 21--22:
The new input samples are copied into the output sample vector in preparation
for the next iteration.

@item Line 24:
This step is optional.  It creates a @file{header} file for a new record
to be called @file{dif}, which we can then open with another program if
we want to read the signals that this program has written.  Since the
@var{record} argument for @code{osigopen} was @file{8l}, we can also
read these files using record @file{8l}; one reason for making a new
@file{header} file here is that the @file{header} file for @file{8l} may
not necessarily indicate the proper sampling frequency for these
signals.

@item Line 25:
Since the program writes output signals, it must invoke @code{dbquit} to
close the files properly.
@end table

@node     Example 7, Example 8, Example 6, Examples
@unnumberedsec Example 7:  A General-Purpose FIR Filter

@cindex digital filter
@cindex filter (digital)
This program illustrates a useful technique for obtaining something
close to random access to signals, a technique that is particularly
useful for implementing digital filters.  The first argument is the
record name, the second and third arguments are the start time and the
duration of the segment to be filtered, and the rest of the arguments
are finite-impulse-response (FIR) filter coefficients.  For example, if
this program were compiled into an executable program called
@samp{filter}, it might be used by
@example
filter 100 5:0 20 .2 .2 .2 .2 .2
@end example
@noindent
which would apply a five-point moving average (rectangular window) filter
to 20 seconds of record @file{100}, beginning 5 minutes into the record.
The output of the program is readable as record @file{out}, for which a
@file{header} file is created in the current directory.

@example
 @i{1}  #include <stdio.h>
 @i{2}  #include <ecg/db.h>
 @i{3}  #define BUFLN 512
 @i{4}  int sample_ok = 1;
 @i{5}
 @i{6}  DB_Sample sample(s, t)
 @i{7}  DB_Signal s;
 @i{8}  DB_Time t;
 @i{9}  @{
@i{10}      static DB_Sample sbuf[BUFLN][DB_MAXSIG];
@i{11}      static DB_Time tt = -1L;
@i{12}
@i{13}      if (t <= tt - BUFLN)
@i{14}          fprintf(stderr, "sample: buffer too short\n");
@i{15}      while (t > tt)
@i{16}          if (getvec(sbuf[(++tt)&(BUFLN-1)]) < 0) sample_ok = 0;
@i{17}      return (sbuf[t&(BUFLN-1)][s]);
@i{18}  @}
@i{19}
@i{20}  main(argc, argv)
@i{21}  int argc;
@i{22}  char *argv[];
@i{23}  @{
@i{24}      double *c, one = 1.0, vv, atof();
@i{25}      int i, j, nc = argc - 4, nsig, v[DB_MAXSIG];
@i{26}      long nsamp, t;
@i{27}      static DB_Siginfo s[DB_MAXSIG];
@i{28}
@i{29}      if (argc < 4) @{
@i{30}          fprintf(stderr,
@i{31}            "usage: %s record start duration [ coefficients ... ]\n",
@i{32}                  argv[0]);
@i{33}          exit(1);
@i{34}      @}
@i{35}      if (nc < 1) @{
@i{36}          nc = 1; c = &one;
@i{37}      @}
@i{38}      else if (nc >= BUFLN ||
@i{39}               (c = (double *)calloc(nc, sizeof(double))) == NULL) @{
@i{40}          fprintf(stderr, "%s: too many coefficients\n", argv[0]);
@i{41}          exit(2);
@i{42}      @}
@i{43}      for (i = 0; i < nc; i++)
@i{44}          c[i] = atof(argv[i+4]);
@i{45}      if ((nsig = isigopen(argv[1], s, DB_MAXSIG)) < 1)
@i{46}          exit(3);
@i{47}      if (isigsettime(strtim(argv[2])) < 0)
@i{48}          exit(4);
@i{49}      if ((nsamp = strtim(argv[3])) < 1) @{
@i{50}          fprintf(stderr, "%s: inappropriate value for duration\n",
@i{51}                  argv[0]);
@i{52}          exit(5);
@i{53}      @}
@i{54}      if (osigopen("16l", s, nsig) != nsig)
@i{55}          exit(6);
@i{56}
@i{57}      for (t = 0; t < nsamp && sample_ok; t++) @{
@i{58}          for (j = 0; j < nsig; j++) @{
@i{59}              for (i = 0, vv = 0.; i < nc; i++)
@i{60}                  if (c[i] != 0.) vv += c[i]*sample(j, t+i);
@i{61}              v[j] = (int)vv;
@i{62}          @}
@i{63}          if (putvec(v) < 0) break;
@i{64}      @}
@i{65}
@i{66}      (void)newheader("out");
@i{67}      dbquit();
@i{68}      exit(0);
@i{69}  @}
@end example

@noindent
@strong{Notes:}

@table @emph
@item Line 4:
@code{BUFLN} must be a power of 2 (why? see lines 16 and 17), and it
should be larger than the length of the filter (i.e., the caller should
not look back further than @code{BUFLN-1} samples into the past,
relative to the most recent sample that has been read).

@item Lines 6--18:
This function supplies input samples to the @code{main} routine as
needed, and frees the @code{main} routine of the need to read them in
strict time order.  The @code{sample} function returns the sample from
signal @code{s} with adjusted sample number @code{t} (i.e., relative to
the beginning of the segment to be processed), either by retrieving it
from a circular buffer of samples recently read, or by reading it using
@code{getvec}.  The ugly-looking @code{getvec} argument is simply the
next slot in the circular buffer; note that @code{tt} is an internal
``clock'' for @code{sample}, which records the adjusted sample number of
the most recently read sample.  In this program, the test in line 13 is
redundant (why?)  and might be removed for efficiency's sake.

@item Lines 35--37:
If no coefficients are provided on the command line, the program will
simply copy the selected segment of the input signals.

@item Lines 38--44:
If there are more coefficients than there are samples in the circular
buffer, or if memory cannot be allocated for the coefficient vector, the
program cannot work properly, so it exits with an error message.  In
lines 43 and 44, the ASCII strings that represent the coefficients are
converted to @code{double} format and stored in the coefficient
vector.

@item Lines 45--48:
The record name is @code{argv[1]}, and the start time is @code{argv[2]};
if the record can't be opened, or the start time is inappropriate, the
program exits.

@item Lines 49--53:
The @var{duration} argument should be a time interval in @var{HH:MM:SS}
format; @code{strtim} converts it to the appropriate number of
samples.

@item Lines 54--55:
The output signals will be written to files in the current directory
according to the specifications for record @file{16l}
(@pxref{Piped and Local Records}).  If we can't write as many output
signals as there are input signals, the program exits.

@item Lines 57--64:
Here's where the work is done.  The outer loop is executed once per
sample vector, the middle loop once per signal, and the inner loop
once per coefficient.  In line 60, we retrieve an input sample,
multiply it by a filter coefficient, and add it to a running sum.
The sum is initialized to zero in line 59 before we begin, and is
converted to an @code{int} in line 61 when we are finished.  Once
an entire sample vector has been filtered, it is written out in line 63.
The entire process is repeated up to @code{nsamp} times, or until
we run out of input samples.

@item Line 66:
The program creates a @file{header} file for record @file{out}, using
the signal specifications from record @file{16l} and the sampling
frequency from the input record.
@end table

@node     Example 8, Example 9, Example 7, Examples
@unnumberedsec Example 8:  Creating a New Database Record
@cindex creating a record
@cindex records (creating)

This program creates a new record from scratch.  It asks the user for
information about the signals to be sampled, then records them, and
finally creates a @file{header} file for the new record.  Details of
data acquisition are hardware-dependent and are not shown here. 

@example
 @i{1}  #include <stdio.h>
 @i{2}  #include <ecg/db.h>
 @i{3}
 @i{4}  main()
 @i{5}  @{
 @i{6}      char answer[32], record[8], directory[32];
 @i{7}      int i, nsig = 0, v[DB_MAXSIG];
 @i{8}      long nsamp, t;
 @i{9}      double freq = 0.;
@i{10}      static char filename[DB_MAXSIG][32],
@i{11}          description[DB_MAXSIG][32], units[DB_MAXISIG][22];
@i{12}      static DB_Siginfo s[DB_MAXSIG];
@i{13}
@i{14}      do @{
@i{15}          printf("Choose a record name [up to 6 characters]: ");
@i{16}          fgets(record, 8, stdin); record[strlen(record)-1] = '\0';
@i{17}      @} while (newheader(record) < 0);
@i{18}      do @{
@i{19}          printf("Number of signals to be recorded [1-%d]: ",
@i{20}                 DB_MAXSIG);
@i{21}          fgets(answer, 32, stdin); sscanf(answer, "%d", &nsig);
@i{22}      @} while (nsig < 1 || nsig > DB_MAXSIG);
@i{23}      do @{
@i{24}          printf("Sampling frequency [Hz per signal, > 0]: ");
@i{25}          fgets(answer, 32, stdin); sscanf(answer, "%lf", &freq);
@i{26}      @} while (setsampfreq(freq) < 0);
@i{27}      do @{
@i{28}          printf("Length of record (H:M:S): ");
@i{29}          fgets(answer, 32, stdin);
@i{30}      @} while ((nsamp = strtim(answer)) < 1L);
@i{31}      printf("Directory for signal files [up to 30 characters]: ");
@i{32}      fgets(directory, 32, stdin);
@i{33}      directory[strlen(directory)-1] = '\0';
@i{34}      printf("Save signals in difference format? [y/n]: ");
@i{35}      fgets(answer, 32, stdin);
@i{36}      s[0].fmt = (answer[0] == 'y') ? 8 : 16;
@i{37}      printf("Save all signals in one file? [y/n]: ");
@i{38}      fgets(answer, 32, stdin);
@i{39}      if (answer[0] == 'y') @{
@i{40}          sprintf(filename[0], "%s/d.%s", directory, record);
@i{41}          for (i = 0; i < nsig; i++) @{
@i{42}               s[i].fname = filename[0];
@i{43}               s[i].group = 0;
@i{44}          @}
@i{45}      @}
@i{46}      else @{
@i{47}          for (i = 0; i < nsig; i++) @{
@i{48}               sprintf(filename[i], "%s/d%d.%s", directory,i,record);
@i{49}               s[i].fname = filename[i];
@i{50}               s[i].group = i;
@i{51}          @}
@i{52}      @}
@i{53}      for (i = 0; i < nsig; i++) @{
@i{54}          s[i].fmt = s[0].fmt; s[i].bsize = 0;
@i{55}          printf("Signal %d description [up to 30 characters]: ", i);
@i{56}          fgets(description[i], 32, stdin);
@i{57}          description[i][strlen(description[i])-1] = '\0';
@i{58}          s[i].desc = description[i];
@i{59}          printf("Signal %d units [up to 20 characters]: ", i);
@i{60}          fgets(units[i], 22, stdin);
@i{61}          units[i][strlen(units[i])-1] = '\0';
@i{62}          s[i].units = (*units[i]) ? units[i] : "mV";
@i{63}          do @{
@i{64}              printf(" Signal %d gain [adu/%s]: ", i, s[i].units);
@i{65}              fgets(answer, 32, stdin);
@i{66}              sscanf(answer, "%lf", &s[i].gain);
@i{67}          @} while (s[i].gain < 0.);
@i{68}          do @{
@i{69}              printf(" Signal %d ADC resolution in bits [8-16]: ", i);
@i{70}              fgets(answer, 32, stdin);
@i{71}              sscanf(answer, "%d", &s[i].adcres);
@i{72}          @} while (s[i].adcres < 8 || s[i].adcres > 16);
@i{73}          printf(" Signal %d ADC zero level [adu]: ", i);
@i{74}          fgets(answer, 32, stdin);
@i{75}          sscanf(answer, "%d", &s[i].adczero);
@i{76}      @}
@i{77}      if (osigfopen(s, nsig) < nsig) exit(1);
@i{78}      printf("To begin sampling, press RETURN;  to specify a\n");
@i{79}      printf(" start time other than the current time, enter\n");
@i{80}      printf(" it in H:M:S format before pressing RETURN: ");
@i{81}      fgets(answer, 32, stdin); answer[strlen(answer)-1] = '\0';
@i{82}      setbasetime(answer);
@i{83}
@i{84}      @var{initialize ADC here}
@i{85}
@i{86}      for (t = 0; t < nsamp; t++) @{
@i{87}          for (i = 0; i < nsig; i++)
@i{88}              v[i] = @var{sample from ADC channel i}
@i{89}          if (putvec(v) < 0) break;
@i{90}      @}
@i{91}
@i{92}      @var{stop ADC here}
@i{93}
@i{94}      (void)newheader(record);
@i{95}      dbquit();
@i{96}      exit(0);
@i{97}  @}
@end example

@noindent
@strong{Notes:}
@table @emph
@item Lines 14--17:
This code uses @code{newheader} to determine if a legal record name was
entered (since we don't want to digitize the signals and then find out
that we can't create the @file{header} file).  The @file{header} file
created in line 17 will be overwritten in line 94.

@item Lines 39--45:
This code generates a file name and initializes the @code{fname} and
@code{group} fields of the array of @code{DB_Siginfo} objects so that all
signals will be saved in one file.

@item Lines 46--52:
This code generates unique file names and groups for each signal.

@item Lines 53--76:
Here, information specific to individual signals is gathered.

@item Line 77:
If the signal files can't be created, this program can do nothing else
useful, so it quits with an error message from @code{osigfopen}.

@item Lines 78--82:
Just before sampling begins, we set the base time.  Note that an empty
string argument for @code{setbasetime} gives us the current time read from
the system clock.

@item Line 84:
What goes here will be hardware dependent.  Typically it is necessary to
set up a timer for the ADC, allocate DMA buffers, specify interrupt vectors,
and initiate the first conversion(s).  This program might also be used to
create a database record from prerecorded data in a non-supported format;
in this case, we might simply open the file containing the prerecorded data
here.

@item Lines 86--90:
Here is where the samples are acquired (using hardware-dependent code
not shown here) and recorded (using @code{putvec}).  At high sampling
frequencies, it is critical to make this code as fast as possible.  It
could be made faster by judicious use of @code{register} and pointer
variables if necessary.  In an extreme case the entire loop, possibly
including @code{putvec} itself, can be written in assembly language;
since it is only a small fraction of the entire program, doing so is
within reason.

@item Line 92:
This final piece of hardware-dependent code typically clears the ADC
control register, stops the timer, and frees any system resources such as
DMA channels or interrupts.

@item Line 94:
All of the information needed to generate the @file{header} file has been
stored in DB library internal data structures by @code{osigfopen} and 
@code{putvec};  we call @code{newheader} here (before @code{dbquit}) to
create the new @file{header} file.

@item Line 95:
It is still necessary to use @code{dbquit} to close the signal file(s),
even after calling @code{newheader}.  (In fact, it would be possible,
though not likely to be useful, to record more samples and to generate
another @file{header} file before calling @code{dbquit}.)
@end table

@node     Example 9, Example 10, Example 8, Examples
@unnumberedsec Example 9: A Signal Averager

The following program is considerably more complex than the previous examples
in this chapter.  It reads an annotation file (for which the annotator name
is specified in its first argument, and the record name in the second
argument) and selects beats of a specified type to be averaged.  The
program selects segments of the signals that are within 50 milliseconds of the
time of the specified beat annotations, subtracts a baseline estimate
from each sample, and calculates an average waveform (by default, the
average normal QRS complex).

@example
 @i{1}  #include <stdio.h>
 @i{2}  #include <ecg/db.h>
 @i{3}  #include <ecg/ecgmap.h>
 @i{4}
 @i{5}  main(argc, argv)
 @i{6}  int argc;
 @i{7}  char *argv[];
 @i{8}  @{
 @i{9}      int btype, i, j, nbeats = 0, nsig, hwindow, window;
@i{10}      DB_Sample v[DB_MAXSIG], vb[DB_MAXSIG];
@i{11}      long stoptime = 0L, *sum[DB_MAXSIG];
@i{12}      DB_Anninfo a;
@i{13}      DB_Annotation annot;
@i{14}      static DB_Siginfo s[DB_MAXSIG];
@i{15}      void *calloc();
@i{16}
@i{17}      if (argc < 3) @{
@i{18}          fprintf(stderr,
@i{19}                  "usage: %s annotator record [beat-type from to]\n",
@i{20}                  argv[0]);
@i{21}          exit(1);
@i{22}      @}
@i{23}      a.name = argv[1]; a.stat = READ;
@i{24}      if ((nsig = dbinit(argv[2], &a, 1, s, DB_MAXSIG)) < 1) exit(2);
@i{25}      hwindow = strtim(".05"); window = 2*hwindow + 1;
@i{26}      btype = (argc > 3) ? strann(argv[3]) : NORMAL;
@i{27}      if (argc > 4) iannsettime(strtim(argv[4]));
@i{28}      if (argc > 5) @{
@i{29}          if ((stoptime = strtim(argv[5])) < 0L)
@i{30}              stoptime = -stoptime;
@i{31}          if (s[0].nsamp > 0L && stoptime > s[0].nsamp)
@i{32}              stoptime = s[0].nsamp;
@i{33}      @}
@i{34}      else stoptime = s[0].nsamp;
@i{35}      if (stoptime > 0L) stoptime -= hwindow;
@i{36}      for (i = 0; i < nsig; i++)
@i{37}          if ((sum[i]=(long *)calloc(window,sizeof(long))) == NULL) @{
@i{38}              fprintf(stderr, "%s: insufficient memory\n", argv[0]);
@i{39}              exit(3);
@i{40}          @}
@i{41}      while (getann(0, &annot) == 0 && annot.time < hwindow)
@i{42}          ;
@i{43}      do @{
@i{44}          if (annot.anntyp != btype) continue;
@i{45}          isigsettime(annot.time - hwindow - 1);
@i{46}          (void)getvec(vb);
@i{47}          for (j = 0; j < window && getvec(v) > 0; j++)
@i{48}              for (i = 0; i < nsig; i++)
@i{49}                  sum[i][j] += v[i] - vb[i];
@i{50}          nbeats++;
@i{51}      @} while (getann(0, &annot) == 0 &&
@i{52}               (stoptime == 0L || annot.time < stoptime));
@i{53}      if (nbeats < 1) @{
@i{54}          fprintf(stderr, "%s: no `%s' beats found\n",
@i{55}                  argv[0], annstr(btype));
@i{56}          exit(4);
@i{57}      @}
@i{58}      printf("Average of %d `%s' beats:\n", nbeats, annstr(btype));
@i{59}      for (j = 0; j < window; j++)
@i{60}          for (i = 0; i < nsig; i++)
@i{61}              printf("%g%c", (double)sum[i][j]/nbeats, 
@i{62}                     (i == nsig-1) ? '\n' : '\t');
@i{63}      exit(0);
@i{64}  @}
@end example

@noindent
@strong{Notes:}

@table @emph
@item Line 25:
The ``half-window'' is 50 milliseconds wide, and the ``window'' (the
duration of a segment to be entered into the average) is one sample more
than twice that amount (i.e., 50 milliseconds to either side of the
fiducial point defined by the annotation).

@item Line 26:
If a third argument is present on the command line, it is taken as an
annotation code mnemonic for the desired beat type;  otherwise, the
program will average @code{NORMAL} QRS complexes.

@item Line 27:
If a fourth argument is present on the command line, it is taken as the
start time; we arrange for the first annotation to be read by
@code{getann} to be the first annotation that occurs after the chosen
start time.

@item Lines 28--35:
This code similarly determines when the averaging should stop.  Unless
no stop time was specified on the command line and the signal length is
not defined in the @file{header} file for the record, @code{stoptime}
will have a positive value in line 35, which makes a tiny adjustment so
that if a beat annotation occurs within 50 milliseconds of the end of the
averaging period, the beat will not be included in the average.

@item Lines 36--40:
Here we allocate memory for the @code{sum} vectors that will be used to
store the running totals.  See @cite{K&R}, page 167, for a description
of @code{calloc}.

@item Lines 41-42:
This code addresses the (admittedly unlikely) prospect that the first
annotation(s) might occur within the first 50 milliseconds of the
record; any such annotations will be excluded from the average.

@item Lines 43--52:
Here we read annotations (the first is already in @code{annot} when we
enter the loop, and subsequent annotations are read in line 51); select
the desired ones (line 44); skip to the correct spot in the signals (line
45; the sample selected there is the one just before the beginning of
the window); read a sample from each signal (line 46) into the @code{vb}
vector, which will be used as a crude baseline estimate; read
@code{window} samples from each signal (line 47), subtracting the
baseline from each and adding the result into the running totals; update
a beat counter (line 50); and check for loop termination conditions
(line 52).

@item Lines 53--62:
This is the output section.  If no beats of type @code{btype} were
found, obviously no average can be printed; note that the message goes
to the standard error output, so the user will notice it even if the
standard output has been redirected to a file.  In the usual case, the
averages are printed out as a table, with a column allocated to each
signal.  Note the cast in line 61 (necessary to preserve precision), and
the trick used in line 62 to print a tab after each column but the last
in each line.
@end table

@node     Example 10, , Example 9, Examples
@unnumberedsec Example 10: A QRS Detector

@cindex QRS detector
@cindex detector (QRS)
@cindex digital filter
@cindex filter (digital)
This program reads a single ECG signal, attempts to detect QRS complexes,
and records their locations in an annotation file.  The detector algorithm
is based on a Pascal program written by W.A.H. Engelse and C. Zeelenberg,
``A single scan algorithm for QRS-detection and feature extraction'',
@cite{Computers in Cardiology} @b{6}:37-42 (1979).

@example
 @i{1}  #include <stdio.h>
 @i{2}  #include <ecg/db.h>
 @i{3}  #include <ecg/ecgcodes.h>
 @i{4}  
 @i{5}  #define abs(A)	((A) >= 0 ? (A) : -(A))
 @i{6}  
 @i{7}  main(argc, argv)
 @i{8}  int argc;
 @i{9}  char *argv[];
@i{10}  @{
@i{11}      int filter, time=0, slopecrit, sign, maxslope=0, nslope=0,
@i{12}          qtime, maxtime, t0, t1, t2, t3, t4, t5, t6, t7, t8, t9,
@i{13}          ms160, ms200, s2, scmax, scmin = 0, v[DB_MAXSIG];
@i{14}      DB_Anninfo a;
@i{15}      DB_Annotation annot;
@i{16}      static DB_Siginfo s[DB_MAXSIG];
@i{17}  
@i{18}      if (argc < 2) @{
@i{19}          fprintf(stderr, "usage: %s record [threshold]\n", argv[0]);
@i{20}          exit(1);
@i{21}      @}
@i{22}      a.name = argv[0]; a.stat = WRITE;
@i{23}      if (dbinit(argv[1], &a, 1, s, DB_MAXSIG) < 1) exit(2);
@i{24}      if (sampfreq(NULL) != 250.)
@i{25}          fprintf(stderr, "warning: %s is designed for 250 Hz input\n",
@i{26}                  argv[0]);
@i{27}      if (argc > 2) scmin = muvadu(0, atoi(argv[2]));
@i{28}      if (scmin < 1) scmin = muvadu(0, 1000);
@i{29}      slopecrit = scmax = 10 * scmin;
@i{30}      ms160 = strtim("0.16"); ms200 = strtim("0.2"); s2 = strtim("2");
@i{31}      annot.subtyp = annot.chan = annot.num = 0; annot.aux = NULL;
@i{32}      (void)getvec(v);
@i{33}      t9 = t8 = t7 = t6 = t5 = t4 = t3 = t2 = t1 = v[0];
@i{34}  
@i{35}      do @{
@i{36}          filter = (t0 = v[0]) + 4*t1 + 6*t2 + 4*t3 + t4
@i{37}                  - t5         - 4*t6 - 6*t7 - 4*t8 - t9;
@i{38}          if (time % s2 == 0) @{
@i{39}              if (nslope == 0) @{
@i{40}                  slopecrit -= slopecrit >> 4;
@i{41}                  if (slopecrit < scmin) slopecrit = scmin;
@i{42}              @}
@i{43}              else if (nslope >= 5) @{
@i{44}                  slopecrit += slopecrit >> 4;
@i{45}                  if (slopecrit > scmax) slopecrit = scmax;
@i{46}              @}
@i{47}          @}
@i{48}          if (nslope == 0 && abs(filter) > slopecrit) @{
@i{49}              nslope = 1; maxtime = ms160;
@i{50}              sign = (filter > 0) ? 1 : -1;
@i{51}              qtime = time;
@i{52}          @}
@i{53}          if (nslope != 0) @{
@i{54}              if (filter * sign < -slopecrit) @{
@i{55}                  sign = -sign;
@i{56}                  maxtime = (++nslope > 4) ? ms200 : ms160;
@i{57}              @}
@i{58}              else if (filter * sign > slopecrit &&
@i{59}                       abs(filter) > maxslope)
@i{60}                  maxslope = abs(filter);
@i{61}              if (maxtime-- < 0) @{
@i{62}                  if (2 <= nslope && nslope <= 4) @{
@i{63}                      slopecrit += ((maxslope>>2) - slopecrit) >> 3;
@i{64}                      if (slopecrit < scmin) slopecrit = scmin;
@i{65}                      else if (slopecrit > scmax) slopecrit = scmax;
@i{66}                      annot.time = strtim("i") - (time - qtime) - 4;
@i{67}                      annot.anntyp = NORMAL; (void)putann(0, &annot);
@i{68}                      time = 0;
@i{69}                  @}
@i{70}                  else if (nslope >= 5) @{
@i{71}                      annot.time = strtim("i") - (time - qtime) - 4;
@i{72}                      annot.anntyp = ARFCT; (void)putann(0, &annot);
@i{73}                  @}
@i{74}                  nslope = 0;
@i{75}              @}
@i{76}          @}
@i{77}          t9 = t8; t8 = t7; t7 = t6; t6 = t5; t5 = t4;
@i{78}          t4 = t3; t3 = t2; t2 = t1; t1 = t0; time++;
@i{79}      @} while (getvec(v) > 0);
@i{80}  
@i{81}      dbquit();
@i{82}      exit(0);
@i{83}  @}
@end example

@noindent
@strong{Notes:}

@table @emph
@item Line 5:
A macro that evaluates to the absolute value of its argument.

@item Lines 11--12:
The names of these variables match those in the original Pascal program.

@item Lines 24--26:
Most of this program is independent of sampling frequency, but the filter
(lines 36--37) and the threshold are as specified by the authors
of the original program for human ECGs sampled at 250 Hz (e.g., the AHA DB).
The program will work for MIT DB records sampled at 360 Hz, but better
results can be obtained if the filter is optimized for the correct sampling
frequency.

@item Lines 27--29:
The threshold is actually a slope criterion (with units of amplitude/time);
these lines normalize the threshold with respect to the signal gain.
The default value is used unless the user supplies an acceptable
alternative.  The variables @code{scmin} and @code{scmax} are lower and
upper bounds for the adaptive threshold @code{slopecrit}.

@item Lines 32--33:
Here we read the first sample and copy it into the variables that will
be used to store the ten most recent samples.

@cindex digital filter
@cindex filter (digital)
@item Lines 36--37:
This FIR filter differentiates and low-pass filters the input signal.

@item Lines 38--47:
Here we adjust the threshold if more than two seconds have elapsed since
a QRS was detected.  In line 40, @code{slopecrit} is set to 15/16 of its
previous value if no slopes have been found;  in line 45, it is set to
17/16 of its previous value if 5 or more slopes were found (suggesting
the presence of noise).

@item Lines 48--52:
If the condition in line 48 is satisfied, we may have found the beginning
of a QRS complex.  We record that a slope has been found, set the timer
@code{maxtime} to 160 msec, and save the sign of the slope and the current
time relative to the previous beat.

@item Lines 53--76:
This code is executed once we have found a slope.  Each time the filter
output crosses the threshold, we record another slope and begin looking
for a threshold crossing of the opposite sign (lines 54--57), which must
occur within a specified time.  We record the maximum absolute value of
the filter in @code{maxslope} (lines 58--60) for eventual use in updating
the threshold (lines 63--65).  Once a sufficient interval has elapsed
following the last threshold crossing (line 61), if there were between
2 and 4 slopes, we have (apparently) found a QRS complex, and the program
records a @code{NORMAL} annotation (lines 66--67).  If there were 5 or more
slopes, the program records an artifact annotation (lines 71--72).  If
only 1 slope was found, it is assumed to represent a baseline shift and
no output is produced.

@item Lines 77--79:
At the end of the loop, the samples are shifted through the @code{t@var{n}}
variables and another sample is read.
@end table

@node     Exercises, Glossary, Examples, Top
@unnumbered Exercises

These exercises are based on the material in the previous chapters.
Answers to some of them are at the back of the book, but try to work
through them first.

@enumerate
@item
Type in the first program from the previous chapter, compile it, and run
it.  Remember to set and export the environment variable @code{DB}
(@pxref{DB path}).  It is a good idea to include this step in your
@file{.profile}, @file{.cshrc}, or @file{autoexec.bat}.  As input, try
record @file{100s}, input annotator @file{atruth}, and output annotator
@file{normal}.  The program should finish in five seconds or less.
The annotations will have been written into a file called
@file{normal.100s} (under MS-DOS, @file{100s.nor}) in the current
directory.  Now type ``@code{rdann atruth 100s}'' and observe the output
for a few seconds, then try ``@code{rdann normal 100s}'' and notice the
difference.

@item
Modify the program from the previous exercise so that the non-QRS
annotations are put into a second output annotation file.  Remember that
you will need three annotation files in all (one input and two
output).

@item
The next five short exercises are to be worked out on paper, although you
may wish to check your work on the computer.  All of them assume that
we are given a signal sampled at 100 Hz with the following
specifications:
@example
fname = "signal.dat"
desc = "BP"
units = "mmHg"
gain = 10
initval = 80
group = 0
fmt = 212
spf = 1
bsize = 0
adcres = 12
adczero = 0
baseline = -300
nsamp = 1000000
cksum = 3109
@end example
For starters, convert a sample value of 280 into physical units.

@item
Convert 120 mmHg into adus.

@item
What are the maximum and minimum possible sample values in adu?  in
mmHg?

@item
How large is @file{signal.dat}, in bytes?  How much space could we save
if we converted it to format 8 (eight-bit first-differences)?  What is
the maximum slew rate (in mmHg/second) that we can represent in that
format?

@item
Oops!  We have just discovered that the maximum slew rate in our signal
is 1500 mmHg/sec.  Is there any way to store it at full precision in one
of the supported formats, that saves space compared to its present
format?

@item
Figure out how to plot or display the first 1000 points from signal 0 of
a record in amplitude vs. time format.  You may wish to begin with the
example program from the first chapter.  Arrange for the record name to
be read from the command line (see @cite{K&R}, pp. 114--115, if you
don't know how to do this).

@item
Try plotting VCGs by modifying the program from the previous exercise to
plot pairs of samples from each of two signals rather than sample
number/value pairs.

@item
Modify the program from the previous exercise, or Example 2 from the
previous chapter, so that you can specify a segment of the record to be
processed with start and end times.  For example, the command
@example
@var{your-program record} 10:0 10:10
@end example
should skip the first ten minutes, then process the next ten seconds of
signals from @var{record}.

@item
Wesley Q. Phortran, IV, wrote this program to print beat times (in
minutes and seconds) and R-R intervals for the reference annotation file
of record @file{100}.  Why doesn't it work?
@example
 @i{1}  #include <ecg/db.h>
 @i{2}
 @i{3}  main()
 @i{4}  @{
 @i{5}      DB_Annotation *annot;
 @i{6}      DB_Anninfo ai;
 @i{7}      int t;
 @i{8}
 @i{9}      ai.name = "atruth";
@i{10}      ai.stat = READ;
@i{11}      if (annopen(100, ai, 1)) @{
@i{12}          while (getann(1, annot)) @{
@i{13}              printf("%s\t(%d)\t%s\n", timstr(annot.time),
@i{14}                     annot.time, mstimstr(annot.time - t));
@i{15}              t = annot.time;
@i{16}          @}
@i{17}      @}
@i{18}  @}
@end example
Extra credit: Without actually trying it out, what @emph{does} it
produce on the standard output?

@item
Using @code{isigsettime} on a format 8 signal introduces a random offset
into the signal, since the contents of a format 8 signal file are first
differences rather than amplitudes.  For an AC-coupled signal such as an
ECG, this is usually inconsequential, but a DC-coupled signal such as a
blood pressure signal is usually useful only if absolute levels are
known.  If we store such a signal in format 8, we must read it
sequentially from the beginning in order to get correct sample values.
If we intend to do a lot of non-sequential processing of such a signal,
it may be worthwhile to build a table containing the correct sample
values at periodic intervals; then we can use @code{isigsettime} to
skip to a sample in the table, and read forward sequentially from that
point.  Write a program to build such a table, and wrappers for
@code{isigsettime} and @code{getvec} to give random access to format 8
signal files without introducing offset errors.  On your system, how
many sample intervals should be allowed between table entries in order
to obtain an @code{isigsettime} equivalent that executes in an average
of 100 msec or less?

@item
This exercise assumes that you have access to the MIT-BIH Arrhythmia
Database.  Since the 360 Hz sampling frequency used in that database is
an integer multiple of 60 Hz, it is quite easy to design a 60 Hz notch
filter that can be applied to the database.  Write a program that
filters two input signals and writes out the filtered data using
@code{putvec} (@pxref{Example 7}, for a model program).  Try it out on
MIT-BIH record @file{122}.  Caution: signal files are quite large.  In
order to avoid using too much disk space for this exercise, limit the
program to writing 10 seconds' worth of samples.  Use your programs from
the previous exercises to display your output.

@item
If you used Example 7 as a model in the previous exercise, you may have
noticed that it is quite slow.  Make it faster by arranging for
@code{sample} to return a pointer to a vector of samples from all
signals (thereby reducing the number of function calls).  Speed it up
further by defining a macro that calls the function only if the proper
sample vector is not already in the circular buffer; otherwise the macro
should evaluate to a pointer to the correct sample vector.

@item
Prof. Nottin Ventedhier says, ``Real programmers don't use inefficient
library I/O routines --- they write their own, in assembly language.''
Implement a version of the QRS detector in Example 10 @emph{without}
using the DB library.  (To keep it simple, assume that only one input
format --- of your choice --- needs to be supported.)  How much faster
than the original is your version?

@item
(Non-trivial) Write a QRS detector that is independent of sampling
frequency.  Some useful constants (for adult human ECGs): average normal
QRS duration = 80 milliseconds, average QRS amplitude = 1 millivolt,
average R-R interval = 1 second; assume that upper and lower limits for
these quantities are within a factor of 3 of the average values.  If you
have access to the MIT-BIH Arrhythmia Database, run your detector on
record @file{200}.  Read the documentation on the annotation
comparator, @file{bxb}, and figure out how to use it to compare the
annotation file produced by your program against the reference annotator
@file{atruth}.  How does your detector compare to Example 10?

@item
If the previous exercise was too easy, modify your detector so that the
annotations it generates match those in the @file{atruth} file.  Copying
the @file{atruth} file is not permitted.  You may find this rather
difficult.  Good luck!
@end enumerate

@node     Glossary, Installation, Exercises, Top
@appendix Glossary

@table @emph
@item AC-coupled signal
@cindex AC-coupled signal (defined)
A signal, such as an ECG, for which only variations in level, rather
than absolute levels, are significant.  Such signals are usually passed
through high-pass filters before they are digitized, in order to remove
any DC component (baseline offset), so that the gain can be chosen
optimally for the range of variation in the signal.

@item ADC
@cindex ADC (defined)
Analog-to-digital converter.

@item ADC resolution
@cindex ADC resolution (defined)
@cindex resolution
The number of significant bits per sample.  Typical ADCs yield between 8
and 16 bits of resolution.

@item ADC zero
@cindex ADC zero (defined)
The value produced by the ADC given a 0 volt input.  For bipolar ADCs,
this value is usually 0, but for the unipolar (offset binary) converter
used for the MIT DB, the ADC zero was 1024.

@item adu
@cindex adu (defined)
The unit of amplitude for samples.

@item AHA DB
@cindex AHA DB (defined)
The American Heart Association Database for the Evaluation of Ventricular
Arrhythmia Detectors, consisting of 80 records identified by four-digit
record names.

@item AHA format
@cindex AHA format (defined)
The format used for interchange of AHA DB and MIT DB records on 9-track
tape between institutions, not used for on-line files because it is
relatively wasteful of storage space compared to MIT format
(q.v.).

@item Annotation
@cindex annotation (defined)
A label, associated with a particular sample, which describes a feature
of the signal at that time.  Most annotations are QRS annotations and
indicate the QRS type (normal, PVC, SVPB, etc.).  Annotations are
written by @code{putann} and read by @code{getann}.

@item Annotation code
@cindex annotation code (defined)
An integer in the range of 1 to @code{ACMAX} (a constant defined in
@file{<ecg/ecgcodes.h>}) inclusive, which denotes an event type.

@item Annotation file
@cindex annotation file (defined)
A set of annotations in time order.

@item Annotator name
@cindex annotator name (defined)
A name associated with an annotation file.  On writable UNIX and
Macintosh file systems, the annotation file name is constructed from the
annotator name by appending a @samp{.} and the record name.  On CD-ROMs
and MS-DOS file systems, the annotator name is restricted to three
characters, and the annotation file name is constructed from the record
name by appending a @samp{.} and the annotator name.  UNIX and Macintosh
versions of the DB library can locate and read annotation files named
using either convention.

@item Annotator [number]
@cindex annotator number (defined)
An integer by which an annotation file, once opened, is known.  Input
annotators and output annotators each have their own series of annotator
numbers assigned in serial order beginning with 0.  Since at most
@code{DB_MAXANN} (defined in @file{<ecg/db.h>}) input or output
annotation files may be open at once, the largest valid annotator number
is @code{DB_MAXANN - 1}.

@item Application program
In this guide, a program that uses the DB library to do
something.

@item @file{atruth}
@cindex @code{atruth} (defined)
The annotator name for the reference annotation files.  On CD-ROMs and
MS-DOS file systems, @file{atruth} is truncated within the names of the
reference annotation files to @file{atr};  @code{annopen} accepts either
@file{atruth} or @file{atr} as the annotator name for these files.

@item Base counter value
@cindex base counter value (defined)
@cindex counter (base)
The counter value (q.v.) that corresponds to sample 0.  The base
counter value is read by @code{getbasecount}, and set by @code{setbasecount}
(or by any of the functions that read @file{header} files).  If not
defined explicitly, the base counter value is taken to be 0.

@item Base time
@cindex base time (defined)
The time of day that corresponds to sample 0 in a given record.  For
MIT, AHA, and ESC DB records, the base time was not recorded and is taken to
be 0:0:0 (midnight).

@item Baseline [amplitude]
@cindex baseline amplitude (defined)
The sample value that corresponds to the baseline (isoelectric level or
physical zero level) in the signal.  This quantity may drift during the
record for a variety of reasons, in which case the @code{baseline} field
of the @code{DB_Siginfo} object that describes the signal is only an
approximation.  The baseline is @emph{not} the same as the ADC zero
(q.v.), which is a fixed characteristic of the digitizer.

@item Calibration file
@cindex calibration file (defined)
A file containing data used to build a calibration list (q.v.).

@item Calibration list
@cindex calibration list (defined)
A memory-resident linked list of @code{DB_Calinfo} objects
@iftex
(@pxref{DB_Calinfo structures, , Calibration Information Structures}).
@end iftex
@ifinfo
(@pxref{DB_Calinfo structures}).
@end ifinfo
Each such structure specifies the size and type of the calibration pulse,
and the customary plotting scale, for a particular type of signal.

@item CD-ROM
@cindex CD-ROM (defined)
A read-only medium used for distribution of the MIT-BIH and ESC
databases, among others.  CD-ROMs are physically identical in appearance
to audio compact disks.  CD-ROM readers are available for most
microcomputers and for many larger systems.

@item Closing [a record]
The process of completing I/O associated with a record.

@item Counter frequency
@cindex counter frequency (defined)
@cindex frequency (counter)
The difference between counter values (q.v.) that are separated
by an interval of one second.  The counter frequency is constant
throughout any given record.  It may be undefined, in which case it is
treated as equivalent to the sampling frequency (q.v.) by the DB
library.  The counter frequency is read by @code{getcfreq},
and set by @code{setcfreq} (or by any of the functions that read
@file{header} files).

@item Counter value
@cindex counter value (defined)
@cindex tape counter
A number that serves as a time reference, in a record for which a
counter frequency is defined.  A counter value may be converted to the
time in seconds from the beginning of the record by subtracting the base
counter value (q.v.) and dividing the remainder by the counter
frequency.  The units of @samp{c}-prefixed @code{strtim} arguments are
counter values.

@item Database files
Those files (annotation files, @file{header} files, signal files, and
calibration files) that are accessed via the DB library.

@item Database path
@cindex database path (defined)
The names of the directories in which @file{header}, annotation, and
calibration files are kept.  The environment variable @code{DB} must be
set by the user and exported accordingly.

@item DB library
A set of functions (subroutines), able to read and write database files,
callable by C and C++ programs, and described in this guide.

@item DC-coupled signal
@cindex DC-coupled signal (defined)
A signal, such as a blood pressure signal, for which absolute levels are
significant.  Such signals must be digitized without being passed
through high-pass filters, in order to preserve absolute levels.

@item ESC DB
@cindex ESC DB (defined)
The European ST-T Database, consisting of 90 records identified by
@samp{e}-prefixed four-digit record names.

@item Frame
@cindex frame (defined)
A set of samples, containing all samples that occur within a given
frame interval.  For an ordinary record, a frame contains exactly one
sample of each signal;  for a multi-frequency record, a frame contains
@emph{at least} one sample of each signal, and more than one sample of
each oversampled signal (q.v.).

@item Frame interval
@cindex frame interval (defined)
A time interval during which at least one sample exists for each signal.
For an ordinary record, the frame interval and the sampling interval are
identical.  For a multi-frequency record, the frame interval is chosen
to be an integer multiple of each sampling frequency used.

@item Frame rate
@cindex frame rate (defined)
The basic sampling frequency defined for a multi-frequency record;  the
reciprocal of the frame interval.  The frame rate is usually the lowest
sampling frequency used for any signal included in the record.

@item Gain
@cindex gain (defined)
In this context, the number of adus (q.v.) per physical unit, referred to the
original analog signal.  Gain in this sense is directly proportional to the
degree of amplification (the usual meaning of the word) of the analog
signal prior to digitization.  Gain may vary between signals in a
record.

@item @file{header} file
@cindex @code{header} file (defined)
A file accessible via the DB library that describes the signal files
associated with a given database record.  On writable UNIX and Macintosh
file systems, @file{header} files have names of the form
`@code{header.}@var{record}', where @var{record} is the record name
(q.v.).  On CD-ROMs and MS-DOS file systems, @file{header} files have
names of the form `@code{@var{record}.hea}'.  UNIX and Macintosh
versions of the DB library can locate and read @file{header} files named
using either convention.

@item High-resolution mode
@cindex high-resolution mode (defined)
An alternative mode for reading a multi-frequency record using
@code{getvec}, that can be selected using @code{setgvmode}.  In
high-resolution mode, @code{getvec} replicates samples of signals
digitized at less than the maximum sampling frequency, so that each
sample of any oversampled signals appear in at least one sample vector.

@item Info string
@cindex info string (defined)
Free text within a @file{header} file.  Info strings can be read using
@code{getinfo} and written using @code{putinfo}.

@item Local record
@cindex local record (defined)
A record for which the signal files reside in the current directory,
typically used for user-created signals.  Records @file{8l} and
@file{16l} are local records.

@item Low-resolution mode
@cindex low-resolution mode (defined)
The default mode for reading a multi-frequency record using
@code{getvec}.  In low-resolution mode, @code{getvec} returns one
sample per signal per frame, by decimating any oversampled signals
to the frame rate.

@item MIT DB
@cindex MIT DB (defined)
The Massachusetts Institute of Technology--Beth Israel Hospital
Arrhythmia Database, consisting of 48 records identified by three-digit
record names.

@item MIT format
@cindex MIT format (defined)
The standard format for storage of DB records on CD-ROMs, used on the
MIT, ESC, and MGH DB CD-ROMs, among others.

@item Modification label
@cindex modification label (defined)
An ``invisible'' annotation at the beginning of an annotation file.  A
modification label defines an annotation mnemonic and a corresponding
description.  When @code{annopen} (or @code{dbinit}) opens an annotation
file that contains modification labels, it automatically calls
@code{setannstr} and @code{setanndesc} to add the mnemonics and
descriptions to the translation tables used by @code{annstr},
@code{strann}, and @code{anndesc}.  When @code{annopen} (or
@code{dbinit}) creates an annotation file, it automatically generates
modification labels, for each annotation code that has been (re)defined
using @code{setannstr} or @code{setanndesc}.  For this reason, you
should normally make all of your calls to @code{setannstr} and
@code{setanndesc} @emph{before} calling @code{annopen} or @code{dbinit}.
(An exception is if you are simply @emph{translating} mnemonics and
descriptions into another language, rather than @emph{redefining} them.)
Version 5.3 and later versions of the DB library support reading and
writing modification labels;  earlier versions read modification labels
as @code{NOTE} annotations.

@item Multi-frequency record
@cindex multi-frequency record (defined)
A record containing signals sampled at two or more sampling frequencies.
Version 9.0 and later versions of the DB library support reading and
writing multi-frequency records.

@item Multi-segment record
@cindex multi-segment record (defined)
A composite record that is the concatenation of two or more ordinary
(single-segment) records.  Multi-segment records do not have their own
signal files (the signal files of their constituent segments are read
when it is necessary to read signals of multi-segment records), but they
have their own @file{header} files (created using @code{setmsheader}),
and may have their own annotation files as well (annotation files for
the constituent segments of a multi-segment record are @emph{not}
concatenated automatically when the record is read).  The DB Software
Package includes @code{dbcollate} (under MS-DOS, @code{dbcoll8}), an
application that can create multi-segment records from sets of
single-segment records.  Version 9.1 and later versions of the DB
library support reading and writing multi-segment records.

@item Multiplexed signal file
@cindex multiplexed signal file (defined)
A set of vectors in time order, each consisting of two or more integer
samples, thus representing an equal number of signals.

@item 9-track tape
@cindex nine-track tape (defined)
A medium used for archival storage of DB records, which was once nearly
universally available on minicomputers and larger systems.  The
important parameters are tape density (typically 800 or 1600 bpi) and
block size (typically some multiple of 512 bytes).  Higher tape density
and larger block size permit more data to be stored on a tape.

@item Opening [a database record or a file]
The process of making a database record or a file accessible, if
necessary by creating it.

@item Oversampled signal
@cindex oversampled signal (defined)
In a multi-frequency record, any signal recorded at a sampling frequency
greater than the frame rate (q.v.).

@item Physical unit
@cindex physical unit (defined)
The natural unit of measurement of the original analog signal (e.g.,
millivolts, liters per second, degrees).  To convert samples into
physical units, subtract the ADC zero and divide the remainder by the
gain.

@item Physical zero
@cindex physical zero (defined)
The level (in physical units) that corresponds to the baseline (in adu),
normally zero physical units.  For example, physical zero for a pressure
signal with units of mmHg is 0 mmHg.

@item Piped record
@cindex piped record (defined)
A database record for which a signal file is designated as @file{-},
signifying that it is to be read from the standard input or written to
the standard output.  Records @file{8} and @file{16} are piped
records.

@item Prolog
@cindex byte offset
@cindex cruft (in signal files)
@cindex prolog (in signal files)
@cindex start of sample data
Extraneous bytes at the beginning of a signal file that are not to be
read as samples.  Signal files created using the DB library do not
contain prologs, but signal files created using other means may contain
prologs.  To read such a signal file using the DB library, provided that
the sample data is in a supported format, it is sufficient to record the
length of the prolog (in bytes) in the appropriate locations in a
@file{header} file that names the signal file.  If you need to create
such a @file{header} file, refer to the description of the byte offset
field in @cite{header(5)} (the specification of the @file{header} file
format in the @cite{ECG Database Applications Guide}, or
@pxref{dbsetstart}.

@item Record
@cindex record (defined)
An extensible set of files that may include signal files, annotation
files, and a @file{header} file, all of which are associated with the
same original signals.  Only the @file{header} file is mandatory.
Although records are sometimes called tapes for historical reasons,
records are now more commonly maintained on optical or magnetic disks
than on tape.

@item Record name
@cindex record name (defined)
A character string that identifies a database record.  Record names of
MIT DB records are 3-digit numerals, those of AHA DB records are 4-digit
numerals, and those of ESC DB records are 4-digit numerals with a
prefixed @samp{e}.  Record names may contain up to @code{DB_MAXRNL}
(defined in @file{<ecg/db.h>}) characters, including any combination of
letters, digits, and underscores.  Case (the difference between @samp{e}
and @samp{E}, for example) is significant in record names, even under
operating systems such as MS-DOS that do not treat case as significant
in file names.

@item Reference annotation file
@cindex reference annotation file (defined)
An annotation file supplied by the creator of a record to document its
contents as accurately and thoroughly as possible.  The annotator name
@file{atruth} is reserved for reference annotation files.

@item Sample
@cindex sample (defined)
An integer (of at least 16 bits) that corresponds to a voltage measured
at a given instant by an analog-to-digital converter.  Samples are
written by @code{putvec} and read by @code{getvec}.

@item Sample interval
@cindex sample interval (defined)
The unit of time;  the interval between consecutive samples of a given
signal.

@item Sample number
@cindex sample number (defined)
An attribute of a sample defined as the number of samples of the same
signal that precede it; thus the first sample of any signal has sample
number 0.  Sample numbers are long integers (32 bits).  Samples that
have the same sample number in different signals of a given record may
be treated as having been observed simultaneously.

@item Sampling frequency
@cindex sampling frequency (defined)
The number of samples of a given signal that represent one second of
the original analog signal.  The sampling frequency is constant
throughout a signal file, and is the same for all signals in a given
record.

@item Signal
@cindex signal (defined)
A continuously varying function of time that is approximated by
discrete samples.

@item Signal file
@cindex signal file (defined)
A set of samples in time order, which represent a signal or signal
group.

@item Signal group
@cindex signal group (defined)
A set of signals that are multiplexed together and stored in the same
file.  It is possible to reset input pointers for all signals in a
given signal group
@iftex
(@pxref{isgsettime, , @code{isgsettime}}),
@end iftex
@ifinfo
(@pxref{isgsettime}),
@end ifinfo
but not independently for individual signals within a signal group.

@item Signal group number
@cindex signal group number (defined)
A number by which a signal file, once opened, is known.

@item Signal number
@cindex signal number (defined)
An integer by which a signal, once opened, is known.  Input and output
signals each have their own series of signal numbers assigned in serial
order beginning with 0.  Since at most @code{DB_MAXSIG} (defined in
@file{<ecg/db.h>}) input or output signals may be open at once, the
largest valid signal number is @code{DB_MAXSIG - 1}.

@item Skew
@cindex skew
@cindex intersignal skew
The time difference between samples having the same sample number but
belonging to different signals.  Ideally the skew is zero (or less than
one sample interval), but in some cases this is not so. For example, if
the signals were originally recorded on multitrack analog tape, very
small differences in the azimuth of the recording and playback heads may
result in measurable skew among signals.  If the skew can be measured
(for example, by reference to features of two signals with a known time
difference), it can be recorded in the @file{header} file for a record;
once this has been done, @code{getvec} and @code{getframe} correct for
skew automatically.  If you need to correct for skew, see
@cite{skewedit(1)} and @cite{header(1)} (in the @cite{ECG Database
Applications Guide}), or @pxref{dbsetskew}.  Prospectively, if you
anticipate that skew may be a problem, it is a good idea to apply an
easily identifiable synchronization pulse to all your inputs
simultaneously while recording;  you can then locate this pulse in
each digitized signal and use these measurements to correct for skew.

@item Standard time format
@cindex standard time format (defined)
Any string format legal as an argument for @code{strtim}
@iftex
(@pxref{timstr and strtim, , @code{timstr} and @code{strtim}}).
@end iftex
@ifinfo
(@pxref{timstr and strtim}).
@end ifinfo

@item Tape
@cindex tape (defined)
A database record.

@item Time
@cindex time (defined)
In this guide, synonymous with sample number (q.v.).  Thus the ``time of
an annotation'' is the sample number of the sample to which the
annotation ``points''.

@end table

@node     Installation, DB Applications, Glossary, Top
@appendix Installing the DB Software Package

@menu
* Distribution::	How to obtain the latest version of the DB
			Software Package.
* UNIX installation::	Installing the DB Software Package on UNIX and
			similar systems.
* MS-DOS installation::	Installing the DB Software Package on MS-DOS PCs.
* Other installation::	Installing the DB Software Package on other systems.
@end menu

@node Distribution, UNIX installation, Installation, Installation
@unnumberedsec How to obtain the DB Software Package

This appendix briefly describes how to install the DB Software Package
on a new system.  The package includes C-language sources for the DB
library and for a variety of applications (@pxref{DB Applications}),
@TeX{} source for this manual, @code{troff} source for UNIX @code{man}
pages for the package, and a one-minute sample record (@samp{100s}).
Except for UNIX-specific and MS-DOS-specific visualization programs and
an MS-DOS-specific digitization/replay program, all of the software is
portable between UNIX and MS-DOS, and is usable under VMS and on the
Macintosh with only minor modifications.

The package is included on the second edition of the MIT-BIH Arrhythmia
Database CD-ROM (which also includes a version of @emph{WAVE} for Sun
SPARCstations), and is also available separately on 1.44 Mb 3.5" floppy
diskettes.  For information on obtaining the latest version of the DB
Software Package, @pxref{Sources}.

The DB library is provided in precompiled form for MS-DOS users,
together with the `include' (@samp{.h}) files needed to use it, on several
other CD-ROMs that contain databases of ECGs and other signals, including the
first edition of the MIT-BIH Arrhythmia Database CD-ROM, both editions
of the European ST-T Database CD-ROM and the MIT-BIH Polysomnographic
Database CD-ROM, and on the MGH/Marquette Foundation Waveform Database
CD-ROMs (@pxref{Sources}).  These CD-ROMs do @emph{not} include
the DB applications included in the DB Software Package.  To use the
precompiled DB library, you must compile and link your own programs with
it using the Microsoft C compiler (or another compiler/linker that is
compatible with Microsoft @file{.lib} libraries).
If you have only a CD-ROM with MS-DOS binaries, and have not obtained
the DB Software Package, see the files @file{readme.doc} (in the root
directory of the CD-ROM) and @file{lib.doc} (in the @file{lib}
directory) for information about using the precompiled DB library, and
check @file{lib\ecg\db.h} to see which DB library version is on your
CD-ROM.

On more recent (1995 and later) CD-ROMs, the DB Software Package is provided
in source form within @file{software/db}, in precompiled binary form for
MS-DOS within @file{software/msdos}, and as @code{tar} archives (named
@code{software/}@emph{xxx}@code{.tar}) containing precompiled binaries
for several versions of UNIX.  The DB Browser for MS Windows
(@file{wview}) is provided on these CD-ROMs in binary form only, within
@code{software/msdos}.  @emph{WAVE} is also provided on these CD-ROMs in
binary form only, within the @code{tar} archives for those versions of
UNIX under which @emph{WAVE} runs (as of mid-1995, these included SunOS
4.x, SPARC Solaris 2.x, and Linux).  If you are installing the DB
Software Package from one of these CD-ROMs, first review the general
notes in @file{software/README.TXT}, then follow the specific
instructions for your operating system found in:
@itemize @bullet
@item
@file{software/MAC.TXT}, for the Macintosh

@item
@file{software/MSDOS.TXT}, for MS-DOS and MS Windows

@item
@file{software/UNIX.TXT}, for all versions of UNIX (Linux users, also
see @file{software/LINUX.TXT})
@end itemize

The remainder of this appendix refers only to the diskette and the
August 1992 CD-ROM distributions.

@node UNIX installation, MS-DOS installation, Distribution, Installation
@unnumberedsec UNIX

Select an existing directory in a writable file system for the
installation;  @file{/usr/local/src} is a good choice in most cases.
Make sure that at least 6 megabytes are available (most of this space
can be recovered after the installation is complete).

If you are installing the software from a CD-ROM, copy the contents of the 
@file{src/db} directory to your writable file system.  One way to do this is
to change to the @file{src} directory on the CD-ROM and then to type:

@example
tar cfv - db | ( cd /usr/local/src; tar xfv - )
@end example

To save space, you may prefer to make symbolic links (if your system supports
them) from the CD-ROM files to @file{/usr/local/src}.

If you have obtained the DB Software Package on a diskette, copy the
file @file{db-tar.z} from the diskette to your writable file system.
(Note that @file{db-tar.z} is a binary file;  do not remove carriage return
characters from it as you would normally do if copying an MS-DOS text file.)
Now type:

@example
uncompress <db-tar.z | tar xfv -
@end example

@noindent
This command unpacks the archive into a directory called @file{db}.  You may
remove @file{db-tar.z} from your hard disk now if you wish.  (If you don't
have @file{uncompress} or @file{tar}, unpack the archive on a PC using the
MS-DOS installation procedure below;  you will be given an opportunity to stop
once the archive has been unpacked.  Copy the entire contents of the @file{db}
directory to your UNIX file system, treating all files as binary files.)

Change to the @file{db} directory on your hard disk:

@example
cd db
@end example

Type the following command to rename some of the files for UNIX:

@example
sh setunix
@end example

Follow the instructions in file @file{UNIX.TXT}, within the @file{db}
directory, to complete the installation.  (In most cases, this requires
only reading @file{Makefile}, changing the site-dependent variables defined
there as appropriate for your system, then typing @samp{make}.  Depending on
the speed of your system and of your C compiler, @samp{make} will generally
require between 5 and 15 minutes.)

@node MS-DOS installation, Other installation, UNIX installation, Installation
@unnumberedsec MS-DOS

Install your C compiler if you have not already done so, and make sure that
your hard disk has at least 6 megabytes of free space remaining.  (Most of
this space can be reclaimed after the installation is complete.)

If you have Microsoft or Turbo C or C++, and a Microstar Laboratories DAP 1200-
or 2400-series analog interface board, you can instruct the @file{install}
procedure to compile @file{sample} (a program for creating database records
from analog signals, and for replaying them in analog form).  To do so
successfully, you must first have installed the Microstar @file{#include} files
and DAP interface library on your system.  Specifically, files @file{c_lib.c},
@file{clock.h}, and @file{ioutil.h} must be installed in your @file{include}
directory, and file @file{cdapl.lib} must be installed in a directory in which
libraries are found by your linker.  (If you are using Microsoft C, copy
@file{cdapl5.lib} from the Microstar distribution diskettes into your library
directory, and rename it @file{cdapl.lib}.)

If you are installing the software from a CD-ROM, load the CD-ROM and
type:

@example
@var{d}:\src\install
@end example

@noindent
(replacing @var{d} by the drive letter of your CD-ROM drive).

If you are installing the software from a diskette, load the diskette into the
@code{a} drive and type:

@example
a:install
@end example

@noindent
(or @code{b:install} if you must use the @code{b} drive).

Follow the instructions that are presented on-screen to complete the
installation.  Depending on the speed of your system and of your C compiler,
the automatic portion of the installation will generally require from 5
minutes to an hour once you have selected the configuration.

@node Other installation, , MS-DOS installation, Installation
@unnumberedsec Other systems

If you are installing the software from a CD-ROM, copy the contents of
the @file{src/db} directory to your hard disk.  If you are installing
the software from a diskette, and your system does not have
UNIX-compatible @file{tar} and @file{compress} utilities, unpack the
archive on a PC and copy the entire contents of the @file{db} directory
to your system.  Follow the MS-DOS installation procedure as above; you
will be given an opportunity to stop once the archive has been unpacked.
Note that the unpacked text files are in UNIX format (i.e., lines are
terminated by ASCII line-feed characters only).  If your system expects
text files in MS-DOS format (with both a carriage return and a line-feed
at the end of each line; VMS is one such system), let the MS-DOS
installation procedure continue one step further, until it announces
that it has reformatted the text files for MS-DOS.  If your system is a
Macintosh (which expects that lines are terminated by carriage returns
only), you will have to reformat the text files yourself, which may be
done on the PC if you have third-party software to do so, or on the
Macintosh otherwise.  Additional notes for Macintosh users may be found
in file @file{MAC} (within the @file{db} directory).

The DB Software Package is written in highly portable C, and (with the
exception of a few MS-DOS or UNIX-specific display or data-acquisition
programs) should be easy to compile with any K&R or ANSI C compiler.
The UNIX and MS-DOS @file{make} description files (@file{makefile.unx}
and @file{makefile.dos} in @file{db} and in each of its subdirectories)
should get you started.

@node     DB Applications, Extensions, Installation, Top
@appendix DB Application Programs

This appendix briefly describes the application programs that are
included with the DB Software Package.  Except where noted otherwise,
these applications are usable on all systems for which the DB library is
available.  For details on using these programs, refer to the @cite{ECG
Database Applications Guide}.  (On UNIX systems, the contents of the
@cite{Applications Guide} may also be available as on-line @code{man}
pages.)

@menu
* Using::		Notes on using these programs.
* Annotation I/O::	Programs that read, write, summarize, and
			otherwise process annotation files.
* Evaluation::		Programs for evaluating the performance of ECG
			analysis programs.
* Signal processing::	Programs that read, excerpt, reformat, resample,
			filter, combine, analyze, acquire, and replay signals.
* Graphics::		Programs for viewing or plotting signals and
			annotations.
@end menu

@node     Using, Annotation I/O, DB Applications, DB Applications
@unnumberedsec How to use these programs

These programs are kept in directories that vary from system to system;
they may not be in the default search path.  If you cannot find them,
consult an expert (such as the person who installed the DB library on
your system).  If you use these programs often, you may wish to include
the directory in which they are kept in your search path.

To use any of these programs, you will need to set the database path first
(@pxref{DB path}).  Programs that accept @emph{time} arguments or commands
(usually shown as @var{from} and @var{to} below) use @code{strtim} to convert
these strings into sample intervals; hence they accept any of the varieties of
standard time format described earlier
@iftex
(@pxref{timstr and strtim, , @code{timstr} and @code{strtim}}).
@end iftex
@ifinfo
(@pxref{timstr and strtim}).
@end ifinfo
Programs that accept annotation mnemonics as arguments or commands
(usually shown as @var{code} below) use @code{strann} to interpret them;
for a list of legal mnemonics, @pxref{Annotation Codes}.  Where record
or annotator names are required as command arguments, they are indicated
below as @var{record} or @var{annotator}.

In the remainder of this appendix, you will find usage examples and
capsule descriptions of the standard DB application programs.  The
square brackets (`@var{[ ]}') in some of the usage examples surround
arguments that may be omitted;  the brackets themselves are not
to be included in the command line.  Where an ellipsis (`@var{...}')
appears, it indicates that the previous argument may be repeated.
If invoked without any arguments, or with a @samp{-h} (help) option, most of
these programs print a brief synopsis of how they are used.

@node     Annotation I/O, Evaluation, Using, DB Applications
@unnumberedsec Annotation File Processing

@example
rdann -a @var{annotator} -r @var{record [} -f @var{from} -t @var{to} -p @var{type ... ]}
wrann -a @var{annotator} -r @var{record}
sumann -a @var{annotator} -r @var{record}
tach -a @var{annotator} -r @var{record [ options ... ]}
@end example

The program @file{rdann} is an annotation printer similar to the one
shown in chapter 6 (@pxref{Example 3}).  The optional @var{from} and
@var{to} arguments (in standard time format) specify a portion of the
annotation file to be printed, and one or more @var{type} arguments
(annotation mnemonics) can be given to restrict the output to
annotations that are of the specified type(s).

The output of @file{rdann} can be converted back into an annotation file
by providing it as the standard input of @file{wrann}.  This can be
useful for editing annotation files in some cases;  they can be
converted to ASCII format by @file{rdann}, edited using any text editor,
and converted back into annotation files by @file{wrann}.

A summary of the contents of an annotation file can be obtained using
@file{sumann}.  The summary includes the number of annotations of each
type, and the duration and number of episodes of each rhythm and signal
quality.

@file{tach} generates a uniformly sampled, smoothed, instantaneous heart
rate sequence from an annotation file.

@node     Evaluation, Signal processing, Annotation I/O, DB Applications
@unnumberedsec Evaluation of ECG Analyzers

@cindex annotation comparator
@cindex comparator (annotation)
@example
bxb -r @var{record} -a @var{reference-annotator test-annotator [ options ... ]}
rxr -r @var{record} -a @var{reference-annotator test-annotator [ options ... ]}
mxm -r @var{record} -a @var{reference-annotator test-annotator [ options ... ]}
epic -r @var{record} -a @var{reference-annotator test-annotator [ options ... ]}
sumstats @var{file}
plotstm @var{file}
ecgeval
nst @var{[ options ... ]}
@end example

The motivation for developing the MIT and AHA databases was to provide
material for evaluating the accuracy of arrhythmia detectors,
particularly with respect to ventricular arrhythmias.  Between 1984 and
1987, the Association for the Advancement of Medical Instrumentation
(AAMI) sponsored the development of a recommended practice (designated
ECAR) for using the databases for this purpose.  The aim of ECAR is to
specify the evaluation methodology in sufficient detail to permit
reproducible testing, and to encourage informed comparisons of the
performance of ventricular arrhythmia detectors in the analysis of these
standard test recordings.  More recently, the AAMI has developed, and
ANSI has adopted, a standard (designated EC38) for ambulatory
electrocardiographs.  EC38 specifies standard protocols for evaluating
the automated analysis algorithms that are included in many such
devices.  These protocols include those developed for the earlier
recommended practice, and extend them to evaluation of supraventricular
arrhythmia and ischemia detection.  EC38 specifies the use of
@file{bxb}, @file{rxr}, @file{mxm}, and @file{epic} to perform
evaluations, and further specifies the use of the MIT DB (as well as two
other databases included on the MIT-BIH Arrhythmia Database CD-ROM), the
AHA DB, and (for devices that perform analysis of the ST segment) the
ESC DB.  If you are interested in this subject, obtain copies of the
@cite{American National Standard for Ambulatory Electrocardiographs}
(ANSI/AAMI EC38--1994) and @cite{Testing and Reporting Performance
Results of Ventricular Arrhythmia Detection Algorithms} (AAMI
ECAR--1987; @pxref{Sources}).

@cindex QRS detector
@cindex detector (QRS)
To evaluate an arrhythmia detector using this software, obtain for each
DB record to be used in the test an annotation file containing the
detector's analysis of each beat.  These are referred to as the `test'
annotation files (or the `algorithm' annotation files, in EC38 and ECAR).
The placement of the beat annotations must match those in the reference
annotations within 150 msec; thus it is not necessary to place
annotations precisely at the PQ junction (as in the AHA DB reference
annotations) or on the major local extremum (as in the MIT DB reference
annotations).  If the detector is capable of shut-down (i.e., if it
inhibits its QRS detection function during periods that it judges are
unreadable), the test annotation files should include a @code{NOISE}
annotation with @code{subtyp = -1} at the beginning of each period of
shut-down, and a @code{NOISE} annotation with any other @code{subtyp}
at the end of each such period.  (If the record ends while the detector
is shut down, the annotation file should include a final `end of shut-down'
annotation as above to permit correct shut-down accounting.)  If the
detector is capable of ventricular fibrillation detection, the test
annotation files should also include @code{VFON} and @code{VFOFF}
annotations; it is not necessary to mark flutter waves (use @code{FLWAV}
annotations to do so if desired).  See the @code{man} page for @file{epic},
in the @cite{ECG Database Applications Guide}, for information on marking
atrial fibrillation, ischemic ST episodes, and ST deviation measurements
in test annotation files.  Any annotations that appear in the
first five minutes of an annotation file are treated as belonging to the
detector's learning period, and are not used in the evaluation.  The
evaluation software examines such annotations only to determine the
detector's state (normal, shut down, or in VF) at the beginning of the
test period.

@cindex annotation comparator
@cindex comparator (annotation)
Program @file{bxb} implements the beat-by-beat comparison algorithm described
in ECAR (section 4.3) and in EC38 (section 4.2.14.2.2).  By default, the output is in a self-explanatory
matrix format.  The @samp{-L} option, which must be followed by two file names,
specifies that the output of @file{bxb} should be written in line format, for
further processing by @file{sumstats}.  The line-format output includes column
headings only if the output file must be created from scratch.  In this way,
@file{bxb} can be used repeatedly to build up a line-format tables for multiple
records.  Among the other options is @samp{-o}, which causes @file{bxb} to
generate an output annotation file (with annotator name @file{bxb}) indicating
agreements and discrepancies between the input annotators.

@file{rxr} can be used to performed the run-by-run comparison described
in ECAR (section 5.3) and in EC38 (section 4.2.14.2.4).  @file{mxm}
compares heart rate, HRV, or other measurements, as described in EC38
(section 4.2.14.2.3).  @file{epic} evaluates VF and AF detection, and ST
analysis, as described in EC38 (sections 4.2.14.2.5 and 4.2.14.2.6).
These programs also accept a @samp{-L} option to produce line-format
output as for @file{bxb}.

@file{sumstats} derives the record-by-record, episode-by-episode, and
aggregate performance statistics described in ECAR (sections 4.6.1 and
5.6) and EC38 (section 3.2.14.3) from line-format output files produced
by @file{bxb}, @file{rxr}, @file{mxm}, and @file{epic}.  The input file
must include the column headings so that @file{sumstats} can recognize
the file type.  The output includes a copy of the input, with aggregate
statistics appended at the end.  @file{plotstm} generates a PostScript
scatter plot of ST measurement comparisons gathered by @file{epic}, as
described in EC38 (section A.3.2.14.2.6.1).

The easiest way to use these programs is to run @file{ecgeval}, which
generates a script (batch) file to run @file{bxb}, @file{rxr}, etc., for
each record in a database.  See @cite{Evaluating ECG Analyzers} (in the
@cite{ECG Database Applications Guide}) for details.

@cindex noise stress test
By adding noise to annotated ECG records, the noise tolerance of an
arrhythmia detector can be measured.  This idea was described by the
author, along with W.K. Muldrow and R.G. Mark, in ``A noise stress test
for arrhythmia detectors'', @cite{Computers in Cardiology}
@strong{11}:381-384 (1984).  Program @file{nst} adds calibrated amounts
of noise to ECGs (or other signals), generating an output record in DB
format.  @file{nst} was used to generate the graded series of noisy ECG
records in the @file{nstdb} directory of the MIT-BIH Arrhythmia Database
CD-ROM.  These records are among those specified as standard test
material by EC38 (section 3.2.14.2).

@node     Signal processing, Graphics, Evaluation, DB Applications
@unnumberedsec Signal Processing Applications

@example
rdsamp -r @var{record [ options ... ]}
wrsamp -r @var{record [ options ... ]}
snip -i @var{input-record} -n @var{new-record [ options ... ]}
xform -i @var{input-record [ options ... ]}
fir @var{[ options ... ]} -c @var{coefficient ...}
sigamp -r @var{record [ options ...]}
sqrs -r @var{record [ options ... ]}
sample @var{[ options ... ]}
calibrate -r @var{record [ options ... ]}
@end example

@file{rdsamp} prints samples from the specified record;  @samp{-f} and
@samp{-t} options may be used to specify a range of sample numbers, and
a subset of signal numbers may be selected using the @samp{-s}
option.  The output of @file{rdsamp}, or any similar text, can be
converted into a DB record using @file{wrsamp}.

To copy an excerpt of a longer record, use @file{snip}, which creates new
@file{header} and signal files for @var{new-record} in the current directory.
The beginning and end of the excerpt are specified using @samp{-f} and
@samp{-t} options as for @file{rdsamp}.  Annotator names may follow a @samp{-a}
option; in this case excerpts from the specified annotation files are copied as
well (the annotations are appropriately time-shifted).

@file{xform} is a more general version of @file{snip}; its main uses are for
reformatting, rescaling, and sampling rate conversion.  You may create a
@file{header} file specifying the desired format, sampling frequency, ADC zero
levels, signal gains, etc., and supply it to @file{xform} using the @samp{-o}
option; if you do not do so, @file{xform} obtains the required information
interactively.  @file{xform} accepts all of the options used by @file{snip},
as well as several others.

@cindex digital filter
@cindex filter (digital)
Program @file{fir} is a general-purpose FIR filter for DB records,
similar to the one discussed in chapter 6 (@pxref{Example 7}).

@file{sigamp} measures signal amplitudes (either baseline-corrected RMS
amplitudes or peak-to-peak amplitudes);  it may be useful for calibrating
signals (together with @file{calibrate}) or for determining signal gains
for @file{nst}.

@cindex QRS detector
@cindex detector (QRS)
@file{sqrs} is a slightly modified version of the QRS detector
discussed in chapter 6 (@pxref{Example 10}).  Options allow
specification of the signal and interval to be analyzed and the
detection threshold.

Program @file{sample} is an MS-DOS application that uses a Microstar
Laboratories DAP 1200- or 2400-series ISA (AT bus) analog interface
board (@pxref{Sources}) to generate database records from analog
signals, or to generate analog signals from database records.  If you
wish to use other hardware for these purposes, refer to chapter 6
(@pxref{Example 8}) and to the source for @file{sample} as
models.

If you create your own database records using @file{sample} or other
means, program @file{calibrate} may be useful for determining signal
gains and offsets if your signals include standard calibration pulses or
identifiable signal levels. @file{calibrate} incorporates two
independent algorithms for measuring calibration pulses; it rewrites
@file{header} files based on its measurements.

@node     Graphics, , Signal processing, DB Applications
@unnumberedsec Graphical Applications

@example
wave -r @var{record [ }-a @var{annotator ]}
dbtool -r @var{record [ }-a @var{annotator ]}
dbplot -t@var{record [ }-a@var{annotator options ... ]}
view @var{record annotator}
wview @var{record annotator}
pschart @var{[ [ options ... ] script ... ]}
psfd @var{[ [ options ... ] script ... ]}
@end example

@cindex ECG waveform editor
@cindex waveform editor
@file{wave} is an X Window System client application for viewing
and editing DB records.  (@file{wave} is not included in the DB software
package, but is available separately; @pxref{Sources}.)  @file{wave} can be
run on UNIX systems, and can be accessed remotely using networked PCs or
other systems for which X11 servers are available.  Run @file{wave}
without any arguments to obtain instructions for printing its on-line
manual.

@file{dbtool} is a SunView application for viewing the specified record.
@file{dbtool} can be run on Sun workstations only.  It is very similar to
@file{wave}, but @file{dbtool} lacks annotation editing capabilities.

If your system supports the UNIX @file{plot} utility, you can pipe the
output of @file{dbplot} into @file{plot} in order to view DB records
with annotations interactively on a graphics terminal.  @file{dbplot}
has many options, which can be given as arguments or interactively.

@file{view}, included on the MIT-BIH Arrhythmia Database and European ST-T
Database CD-ROMs, among others, is an MS-DOS application for viewing DB records
on CGA, EGA, VGA, SVGA, XGA, or Hercules graphics-capable PCs.  See
@file{bin.doc} in the @file{bin} directory of the CD-ROM for more
information.

@file{wview}, included on recent (1995 and later) CD-ROMs and also
available separately (@pxref{Sources}; note that @file{wview} is not
part of the DB Software Package), is an MS-Windows application for
viewing DB records.  It has most of the display capabilities of
@file{wave}, but lacks support for annotation editing.

@file{pschart} and @file{psfd} produce annotated ``chart recordings''
and ``full-disclosure'' plots that can be printed on PostScript devices.
These programs were used to prepare the @cite{MIT-BIH Arrhythmia
Database Directory} and the @cite{European ST-T Database
Directory}.

@node     Extensions, Sources, DB Applications, Top
@appendix Extensions

This section may be helpful if you wish to extend the capabilities of
the DB library, or if you wish to port it to another environment.  In
order to make use of the information in this section, you should have
the DB library sources (@pxref{Sources}).  The sources are distributed
among four `include' (@file{.h}) files and five @file{.c} files:

@display
@t{db.h        } Constant and structure definitions, and function prototypes
@t{ecgcodes.h  } Annotation codes
@t{ecgmap.h    } Annotation code mapping macros
@t{dblib.h     } External definitions for private DB library functions

@t{dbinit.c    } Functions @code{dbinit}, @code{dbquit}, and @code{dbflush}
@t{signal.c    } Functions for signals
@t{calib.c     } Functions for signal calibration
@t{annot.c     } Functions for annotations
@t{dbio.c      } Low-level I/O and operating system-dependent functions
@end display

The first three of these files are the standard `include' files that
are usually obtained by @samp{#include <ecg/@var{file}.h>} statements.
When modifying the DB library, however, make any necessary changes in
the copies of these files that are kept in the library source
directory.  Install the modified versions of the @file{.h} files in the
system's @file{include} directory after installing the modified DB
library.

If you wish to increase the number of simultaneously accessible signals or
annotators, simply change @code{DB_MAXSIG} or @code{DB_MAXANN} in @file{db.h},
and recompile the DB library and your applications.  Your operating system's
limit on the number of simultaneously open files may require you to use
multiplexed signal files if you increase @code{DB_MAXSIG} substantially.

The cleanest mechanism for adding additional fields to @file{header} files is
to include them in `info' strings
@iftex
(@pxref{getinfo, , @code{getinfo}}),
@end iftex
@ifinfo
(@pxref{getinfo}),
@end ifinfo
rather than by modifying the code that reads and writes @file{header}
files (in @file{signal.c}).

A common problem is the need to import signal files generated by other
software.  Often this problem can be solved by writing a format
conversion program that uses input functions provided with the other
software to read the signal files, and @code{putvec} to write them
in one of the formats supported by the DB library.  This solution is
unlikely to be satisfactory if you have many large signal files to
import, however, and you may wish to arrange for @code{getvec} to read
the imported files directly.  This may be done by defining a new signal
file format, as outlined below.

To define a new format for signal files, choose a numeric code to
represent your format.  (Values between 900 and 999 are reserved for
user-defined signal file format codes.)  In @file{db.h}, add your format
code to @code{FMT_LIST} and increment @code{NFMTS}.  In @file{signal.c},
define functions (macros if possible for efficiency) for reading and
writing single samples; these should be named @code{r@var{nnn}} and
@code{w@var{nnn}}, where @var{nnn} is your format code.  Follow the
examples in @file{signal.c}; it will almost certainly be easier to make
use of the existing macros @code{r8} and @code{w8} than to begin from
scratch.  Add additional @code{case} statements in @code{getvec} and
@code{putvec}, again following the existing models.  You will also need
to add a @code{case} in @code{isgsettime}, including a formula to
determine the number of bytes needed per sample, given the number of
signals multiplexed.  (All currently-defined formats use fixed-length
encoding.  If you wish to implement variable-length encoding, it may be
easiest to implement an indexed-search method for @code{isgsettime} in
such cases.)  If the ADC resolution exceeds the number of bits in a C
@code{int} on your system, change the @code{typedef} for @samp{Sample}
in @file{<ecg/db.h>} as necessary;  be aware that this change is likely
to require additional changes to application programs (use @file{lint}
or an ANSI C compiler to check your code).

Although the DB library generally assumes that signal files are ``pure'',
it is possible to read imported signal files that contain prologs
(data that precede the first sample).  To do so, you must construct a
@file{header} file in which the @code{format} fields encode the length
of the prolog in bytes.  For example, a signal file with a 512-byte
prolog followed by format 16 samples would be specified using @samp{16+512}
in the @code{format} field or fields (if the file contains more than one
signal, the @code{format} fields for all signals in the file must be
identical).  Note that this facility is provided only for signal file
import;  the DB library is not equipped to create signal files with
embedded prologs.

In a similar fashion, though with substantially more effort in most
cases, you may define a new format for annotation files.  Add additional
@code{stat} values for reading and writing to the list in @file{db.h}.
In @file{annot.c}, add additional @code{case} statements and code to
@code{annopen}, @code{getann}, @code{putann}, and @code{db_anclose}.  If
you are designing a new format, you may wish to specify a `magic number'
with which your files will begin, to allow @code{annopen} to recognize
the format automatically; a good choice of such a number is one in which
the first byte is non-zero (to distinguish it from AHA format files) and
the high six bits of the second byte are zero (to distinguish it from MIT
format files).

Some users may wish to define additional annotation codes.  An easy and
portable way to accomplish this is to use @code{setannstr} and
@code{setanndesc} within programs that create your annotation files,
before opening them using @code{annopen} (or @code{dbinit}).  Annotation
files created in this way contain modification labels at the beginning
that document the non-standard code definitions, and that permit them to
be read properly by standard DB applications.  Another solution is to
modify the DB library.  This method has the disadvantage that all of
your applications that read annotation files must be recompiled, and
they may no longer read standard annotation files properly.  If despite
this disadvantage you prefer to modify the DB library, begin by defining
symbolic names and numeric values for your new codes in
@file{ecgcodes.h}.  (Values between 42 and 49 are reserved for
user-defined annotation codes.  Unused values less than 42 may be
assigned in future versions of the DB library, and values greater than
49 are reserved to indicate the presence of optional fields such as
@code{subtyp}.)  Next, decide how the new codes are to be mapped by
@code{isqrs}, @code{map1}, @code{map2}, @code{mamap}, and @code{annpos},
and set the appropriate entries in each of the code map arrays in
@file{ecgmap.h}.  Finally, add mnemonic and descriptive strings for the
new codes in the @code{cstring}, @code{astring}, and @code{tstring}
arrays in @file{annot.c}.

The modular design of the library makes it fairly easy to remove
unneeded functionality in order to conserve memory for special
applications.  The @file{calib.c} package is not referenced by any other
DB library modules.  For signal processing applications that do not involve
ECGs, the entire @file{annot.c} package may be removed (with trivial
modifications to the functions in @file{dbinit.c}).  If you wish to add
functions to the library, you will find that it will be easier to
maintain your modified version and to merge updates if you preserve the
existing arrangement of functions, which requires no global variables.
Rather than defining global variables, consider implementing query
functions (global-scope functions that read or write local
variables).  If you wish to define new types of binary files, consider
using the low-level I/O routines in @file{dbio.c} for reading and writing
them in a machine-independent format.

Porting the DB library to another environment is a straightforward
operation if an ANSI C compiler is available in the target environment.
Since all direct access to database files is performed using the
(private) function @code{db_open}, it is possible to include file name
translation in that function if needed, to accommodate file naming
schemes that may be imposed by the operating system or other
requirements.  If the notion of environment variables is foreign to the
target environment, @code{getdb} can be modified to read the DB path
from a file.  You may wish to modify the private function
@code{db_error} (which is responsible for all error reporting from DB
library functions) if the `standard error output' is unavailable or
inadequate for use in the target environment.  All of these functions are
contained within @file{dbio.c}; it is unlikely that any other code will
require changes for a port.

If you encounter errors while compiling @file{signal.c}, you may wish to
try using the functions provided in that file as alternatives to the
standard macros @code{r16} and @code{w16}; the fully-expanded versions
of these macros are quite complex and are known to cause difficulty for
at least one C compiler.  (Define the symbol @samp{BROKEN_CC} while compiling
@file{signal.c} in order to obtain the function versions of @code{r16}
and @code{w16}.)  While compiling @file{signal.c}, it may be necessary
to disable code optimization for some C compilers.

@node     Sources, Answers, Extensions, Top
@appendix Sources

This section is a compendium of sources for databases and related
materials that may be useful to readers of this guide.  If you have
installed the DB Software Package, you may find an updated version of
this list in @file{SOURCES.TXT}, in the @file{db} directory that was
created during the installation.  Please send any corrections to the
author (first address below).

@table @emph
@cindex CD-ROM
@cindex MIT DB
@cindex waveform editor
@item ECG Database Programmer's Guide (this guide)
@itemx ECG Database Applications Guide
@itemx WAVE User's Guide
@itemx MIT-BIH Arrhythmia Database CD-ROM
@itemx MIT-BIH Arrhythmia Database Directory
@itemx MIT-BIH Polysomnographic Database CD-ROM
@itemx MIT-BIH Polysomnographic Database Directory
@itemx Samples of Physiologic Databases CD-ROM
@itemx Software for Physiologic Databases with Samples CD-ROM
@itemx MIMIC Database CD-ROMs
@display
MIT-BIH Database Distribution
MIT Room 20A-113
77 Massachusetts Ave.
Cambridge, MA 02139 USA

e-mail:	@code{dbdist@@hstbme.mit.edu}, @code{george@@hstbme.mit.edu}
WWW: http://ecg.mit.edu
telephone: +1 617 253 7424
telefax: +1 617 253 2514
@end display

@cindex CD-ROM
@cindex ESC DB
@item European ST-T Database CD-ROM
@itemx European ST-T Database Directory
@itemx VALE Database Directory
@display
National Research Council (CNR) Institute of Clinical Physiology
Computer Laboratory
via Trieste, 41
56100 PISA, Italy

e-mail:	@code{taddei@@anemone.ifc.pi.cnr.it}
telephone: +39 50 502771
telefax: +39 50 589038
@end display

@cindex AHA DB
@item AHA Database for Evaluation of Ventricular Arrhythmia Detectors
@display
ECRI
5200 Butler Pike
Plymouth Meeting, PA 19462 USA

e-mail: @code{ecri@@shrsys.hslc.org}
telephone: +1 215 825 6000
@end display

@cindex CD-ROM
@cindex MGH DB
@item MGH/Marquette Foundation Waveform Database CD-ROMs
@display
Anaesthesia/Bioengineering Unit
Massachusetts General Hospital
Fruit St.
Boston, MA 02114 USA

e-mail: @code{cooper@@etherdome.mgh.harvard.edu}
telephone: +1 617 726 8824
@end display

This is a large database of multi-channel recordings (3 ECG leads, radial
arterial, pulmonary arterial, and central venous pressure, respiration,
and CO2), which has been issued on 10 CD-ROMs.

@item American National Standard for Ambulatory Electrocardiographs (ANSI/AAMI EC38--1994)
@itemx AAMI Recommended Practice for Testing and Reporting Performance Results of Ventricular
@itemx @ @ @ Arrhythmia Detection Algorithms (AAMI ECAR--1987)
@display
Association for the Advancement of Medical Instrumentation
3330 Washington Boulevard, Suite 400
Arlington, VA 22201 USA

telephone: +1 703 525 4890
telefax: +1 703 276 0793
@end display

@item Computers in Cardiology
@display
WWW: @code{http://www.cinc.org/}
@end display

CIC is the major scientific meeting at which current research in ECG signal
processing and modelling is discussed;  the proceedings of the conference are
probably the single best source of information in print about these topics.
CIC conferences have taken place annually since 1974, usually in September;
in even-numbered years, they are convened in North America, and in Europe in
odd-numbered years.  The deadline for submission of abstracts is 1 May each
year.  Proceedings of the conferences are published by the IEEE Computer
Society Press, and usually appear about 3 months after the date of the
conference.  CIC will be in Lund (Sweden) in 1997.

@item Proceedings of Computers in Cardiology (ISSN 0276-6574)

@display
IEEE Customer Service
445 Hoes Lane
P.O. Box 1331
Piscataway, NJ 08855-1331 USA

e-mail: @code{customer.service@@ieee.org}
WWW: @code{http://www.ieee.org/bkstr.html}
telephone: 1 800 678 IEEE (USA and Canada) or +1 908 981 0060
telefax: +1 908 981 9667
@end display

@cindex GNU emacs
@item GNU emacs
@itemx gcc (the GNU portable C/C++ compiler)
@itemx ghostscript
@itemx GNU tar
@itemx GNU gzip (free and improved replacement for `compress')
@itemx Larry Wall's `patch' program, with GNU revisions
@itemx GNU groff, gtbl, and related text formatting utilities
@itemx GNU info and makeinfo (standalone hypertext browser and formatter)

@display
Free Software Foundation
675 Massachusetts Ave.
Cambridge, MA 02139 USA

e-mail: @code{gnu@@prep.ai.mit.edu}
WWW: @code{http://www.gnu.ai.mit.edu}
telephone: +1 617 876 3296
@end display
GNU software is available on CD-ROM or tape from the address above, and
is also freely available by anonymous FTP from:
@display
prep.ai.mit.edu
wuarchive.wustl.edu
ftp.cs.columbia.edu
gatekeeper.dec.com
labrea.stanford.edu
ftp.uu.net
ftp.cs.ubc.ca
ftp.unicamp.br
archie.au
ftp.technion.ac.il
ftp.sun.ac.za
ftp.informatik.tu-muenchen.de
ftp.sunet.se
hp4nl.nluug.nl
ftp.funet.fi
ftp.denet.dk
ugle.unit.no
ftp.eunet.ch
irisa.irisa.fr
ftp.ieunet.ie
archive.eu.net
cair.kaist.ac.kr
ftp.nectec.or.th
utsun.s.u-tokyo.ac.jp
ftp.cs.titech.ac.jp
@end display
and many other archive sites.  Please support the FSF with a donation if
you use GNU software.

@item @TeX{} for UNIX systems

This software is available by anonymous FTP from CTAN (Comprehensive TeX
Archive Network) mirrors, including ftp.shsu.edu, ftp.tex.ac.uk, and
ftp.uni-stuttgart.de.  Many of the sources of GNU software (above) also
make @TeX{}, etc. available.  CTAN is indexed on the World Wide Web (one such
index is @file{http://jasper.ora.com/ctan.html}).

The UNIX @TeX{} distribution is also distributed on CD-ROM and in other tape
formats by the Free Software Foundation (address above) and others.  It is
also included with most Linux distributions (see below).

Several commercial implementations of @TeX{} for MS-DOS are widely available.

@item General information on @TeX{}
@display
TeX Users Group
1850 Union St., #1637
San Francisco, CA 94123 USA

WWW: @code{http://www.tug.org}
e-mail: @code{tug@@tug.org}
telephone: +1 415 982 8449
telefax: +1 415 982 8559
@end display

@item X11R6 (the X Window System, Version 11, Release 6)
@itemx XView
@display
The Open Group
11 Cambridge Center
Cambridge, MA 02142 USA

WWW: @code{http://www.x.org/}
@end display

These packages are freely available by anonymous FTP from
ftp.x.org and other archive sites.  X11R6 sources are also available
on tape from the Free Software Foundation (address above).

@item Linux

Linux is a POSIX-compliant reimplementation of the UNIX operating system,
written by Linus Torvalds and a cast of thousands.  It runs on Intel 386,
486, and Pentium PCs, among others.  For information about Linux, visit
the web site of the Linux Documentation Project:
@display
@code{WWW: http://sunsite.unc.edu/linux/}
@end display

Linux is freely available by anonymous FTP in source and binary form from many
sites, including:
@display
tsx-11.mit.edu
sunsite.unc.edu
ftp.funet.fi
net.tamu.edu
ftp.mcc.ac.uk
src.doc.ic.ac.uk
fgb1.fgb.mw.tu-muenchen.de
ftp.informatik.tu-muenchen.de
ftp.dfv.rwth-aachen.de
ftp.informatik.rwth-aachen.de
ftp.Germany.EU.net
ftp.ibp.fr
kirk.bond.edu.au
ftp.uu.net
wuarchive.wustl.edu
ftp.win.tue.nl
ftp.stack.urc.tue.nl
srawgw.sra.co.jp
cair.kaist.ac.kr
ftp.denet.dk
NCTUCCCA.edu.tw
nic.switch.ch
monu1.monash.edu.au
cnuce_arch.cnr.it
@end display

There are also many low-cost (typically US$10 to US$30) distributions of
Linux on CD-ROMs widely available.  Among the more popular are:

@display
Red Hat Linux
3203 Yorktown Ave., Suite 123
Durham, NC 27713 USA

e-mail: @code{sales@@redhat.com}
WWW: @code{http://www.redhat.com/}
telephone: 1 800 546 7274 (USA and Canada) or +1 919 572 6500
telefax: +1 919 572 6726


InfoMagic
11950 N. Hwy 89
Flagstaff, AZ 86004 USA

e-mail: @code{questions@@infomagic.com}
WWW: @code{http://www.infomagic.com/}
telephone: 1 800 800 6613 (USA and Canada) or +1 520 526 9565
telefax: +1 520 526 9573


Walnut Creek CD-ROM
4041 Pike Lane, Suite E
Concord, CA 94520 USA

e-mail: @code{info@@cdrom.com}
WWW: @code{http://www.cdrom.com/}
telephone: 1 800 786 9907 (USA and Canada) or +1 510 674 0783
telefax: +1 510 674 0821


Caldera, Inc.
633 South 550 East
Provo, UT 84606 USA

e-mail: @code{info@@caldera.com}
WWW: @code{http://www.caldera.com/}
telephone: +1 801 377 7687
telefax: +1 801 377 8752


S.u.S.E. GmbH
e-mail: @code{kfr@@suse.de}
WWW: @code{http://www.suse.de/}
@end display

See ads in Byte, Dr. Dobb's Journal, and similar magazines for other sources.

@item Borland C++
@itemx Turbo C/C++
@display
Borland International, Inc.
1800 Green Hills Road
P.O. Box 660001
Scotts Valley, CA 95067 USA

WWW: @code{http://www.borland.com/}
telephone: +1 408 438 5300
@end display

@item Microsoft C/C++
@itemx MS-DOS with Microsoft CD-ROM extensions
@display
Microsoft Corporation
One Microsoft Way
Redmond, WA 98052 USA

WWW: @code{http://www.microsoft.com/}
telephone: +1 206 936 8661
@end display

Versions of MS-DOS earlier than 6.0 did not include `Microsoft CD-ROM
extensions' (@file{mscdex.exe}, an MS-DOS TSR that intercepts read
requests directed to a CD-ROM drive, thus making an ISO 9660 or High
Sierra file system appear to be a read-only MS-DOS file system).  If you
have an earlier version of MS-DOS, either upgrade to MS-DOS 6.x or
later, or obtain @file{mscdex.exe} from your CD-ROM drive vendor.  Be
sure to specify which version of MS-DOS you are using, since newer
versions of MS-DOS are not compatible with older versions of
@file{mscdex.exe} (MS-DOS 3.x requires @file{mscdex} 2.00 or later,
MS-DOS 4.x requires @file{mscdex} 2.10 or later, and MS-DOS 5.0 requires
@file{mscdex} 2.20 or later).  Microsoft Windows 95 (Chicago) does not
require @file{mscdex}, since it incorporates the functions of
@file{mscdex} into the operating system.

@cindex CD-ROM
@item CD-ROM drives
These are now available from most PC and UNIX workstation vendors.  For
the CD-ROMs listed above, the only requirement is that the drive and the
software supplied with it must be compatible with your operating system and
with ISO 9660 format CD-ROMs.  All currently available drives
for PCs and UNIX workstations meet this requirement.  High-speed drives
(4x, 6x, and faster) are now cheaper than single-speed drives were three years
ago; for use with signal databases, these high-speed drives are well
worth the small incremental cost.

For users of UNIX versions that date from 1993 and earlier, it may be
worthwhile to upgrade to newer software that supports Rock Ridge extensions
to ISO 9660 format.  Our recent (1995 and later) CD-ROMs are written in this
format, which appears identical to ISO 9660 format when read on a PC or an
older UNIX system, but supports full UNIX file naming semantics on Rock Ridge
aware systems.

@item Microstar DAP 1200 and 2400 analog interface boards for PCs
@display
Microstar Laboratories
2265 116th Avenue N.E.
Bellevue, WA 98004 USA

telephone: +1 206 453 2345
telefax: +1 206 453 3199
WWW: http://www.mstarlabs.com/mstarlabs/
@end display

@cindex Web browser
@item Web browsers

The most popular Web browsers may be downloaded by anonymous FTP.

@display
Netscape
FTP: @code{ftp.netscape.com}
WWW: @code{http://www.netscape.com}

Mosaic
FTP: @code{ftp.ncsa.uiuc.edu}
WWW: @code{http://www.ncsa.uiuc.edu/SDG/Software}
@end display

NCSA Mosaic is available in both source and precompiled form, and is freely
usable by anyone.  Netscape is available in precompiled form only, and is
free for academic use or for limited-period evaluations;  information about
commercial and other non-academic licensing is available on Netscape's Web
site.
@end table

@node     Answers, Concept Index, Sources, Top
@comment This node isn't in the top-level menu;  why make it too easy?
@unnumbered Answers to Selected Exercises

@table @asis
@item 3.
@iftex
280 adu = (280 adu @minus{}(@minus{}300 adu)) / 10 adu/mmHg = 58 mmHg.
@end iftex
@ifinfo
280 adu = (280 adu - (-300 adu)) / 10 adu/mmHg = 58 mmHg.
@end ifinfo

@item 4.
@iftex
120 mmHg = 120 mmHg * 10 adu/mmHg + ( @minus{}300 adu) = 900 adu.
@end iftex
@ifinfo
120 mmHg = 120 mmHg * 10 adu/mmHg + (- 300 adu) = 900 adu.
@end ifinfo

@item 5.
The range of sample values is
@iftex
@minus{}2048 to +2047 adu, or @minus{}174.8
@end iftex
@ifinfo
-2048 to +2047 adu, or -174.8
@end ifinfo
to +234.7 mmHg.

@item 6.
We don't know how big @file{signal.dat} is, because we don't know how
many other signals are multiplexed with the @code{BP} signal.  If there
are no others, @file{signal.dat} is 1,500,000 bytes (@code{nsamp} * 1.5
bytes/sample).  One-third of the space occupied by @file{signal.dat} could be
saved if it were converted to format 8.  The maximum slew rate
representable in format 8 is 127 adu/sample interval * 100 sample
intervals/sec / 10 adu/mmHg = 1270 mmHg/sec.

@item 7.
One way to save a little space is to resample the signal at 120 Hz, and
then change to format 8 (maximum slew rate = 1524 mmHg/sec).  This can be
done using @file{xform};  it reduces the storage requirement by
one-fifth.

@item 8.
On a UNIX system that supports the @samp{graph} and @samp{plot}
commands, a simple solution is to write the sample numbers and values on
the standard output in two-column ASCII format.  The plotting is then
performed by the pipeline:
@example
@var{your-program} | graph | plot
@end example

@item 11.
Line 5 allocates storage for a pointer; in line 12, @code{getann} needs
storage for the annotation structure.  @samp{t} is declared as an
@code{int} in line 7, which works on a 32-bit machine, but probably not
on a 16-bit machine.  Line 11 contains three errors (did you find all of
them?): the first argument to @code{annopen} should be a character
string, not an integer; the second argument should be a @emph{pointer}
to a @code{DB_Anninfo} object, not the object itself; and (as written)
the @samp{if} condition is satisfied only if @code{annopen} fails (it
returns zero if successful).  Line 12 also contains three errors: the
first argument to @code{getann} is the annotator number, but the first
(in this case, the only) input annotator is @code{0}, not @code{1}; the
second argument to @code{getann} should be, but is not, a pointer to an
allocated @code{DB_Annotation} structure; and the @samp{while} loop
terminates only if @code{getann} @emph{succeeds}.  There are two errors
in lines 13 and 14: @samp{annot.time} is a long integer (unless
@code{long} is equivalent to @code{int} on your machine, @samp{%d} is an
incorrect specification for printing it); and the functions
@code{timstr} and @code{mstimstr} return pointers to static storage that
is overwritten by each call.  If the other errors are fixed, the
@code{printf} statement will print the same string twice (which one
depends on the order of evaluation of function arguments, which may vary
between compilers).  Having fixed all of these errors, the output is
still incorrect, since @code{getann} returns rhythm and signal quality
annotations as well as beat labels (only the latter should be used for
calculating R-R intervals), and @samp{t} is not initialized, which makes
the first interval wrong in any case.  As for the extra credit question,
the program probably produces nothing at all on its standard output!
If, by some miracle, @code{annopen} succeeds, it returns zero, and the
body of the @samp{if} is never executed.  If @code{annopen} simply
fails, perhaps because the input annotation file can't be opened,
@code{getann} also fails, and the program probably dumps core with an
illegal memory reference in the @code{printf} statement, since
@code{annot} hasn't been initialized.  More likely, the program will
dump core in @code{annopen}, attempting to reference memory location
100.

@end table

@node     Concept Index, Function and Macro Index, Answers, Top
@unnumbered Concept Index

@printindex cp

@node     Function and Macro Index, Copying, Concept Index, Top
@unnumbered Function and Macro Index

For a number of entries below, the function name is followed by the version
number of the DB library in which the function first appeared.
Functions for which no such number appears have been present in all
numbered versions of the DB library.


@printindex fn

@node     Copying, , Function and Macro Index, Top
@ifinfo
ECG Database Programmer's Guide

Ninth Edition (revised for DB library version 9.4)

George B. Moody

Copyright (C) 1989 -- 1995 Massachusetts Institute of Technology.

Permission is granted to make and distribute verbatim copies of this
guide provided that the copyright notice and this permission notice are
preserved on all copies.

@ignore
Permission is granted to process this file through TeX and to print the
results, provided that the printed document carries a copying permission
notice identical to this one except for the removal of this paragraph
(this paragraph not being relevant to the printed document).

@end ignore
Permission is granted to copy and distribute modified versions of this
guide under the conditions for verbatim copying and under the conditions
that follow in this paragraph.  Each copy of the resulting derived work
must contain a notice that it is a modified version of this guide.  The
notice must state which edition of this guide was the source for the derived
work, and it must credit the authors of this guide and of the
modifications.  The entire resulting derived work must be distributed
under the terms of a permission notice identical to this one.

Permission is granted to copy and distribute translations of this guide
into another language, under the above conditions for modified versions.

The author would appreciate receiving copies of any modified or
translated versions of this guide for reference purposes.
@end ifinfo

@contents

@bye
