Go to the first, previous, next, last section, table of contents.


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.

  1. Type in the first program from the previous chapter, compile it, and run it. Remember to set and export the environment variable DB (see section The Database Path). It is a good idea to include this step in your `.profile', `.cshrc', or `autoexec.bat'. As input, try record `100s', input annotator `atruth', and output annotator `normal'. The program should finish in five seconds or less. The annotations will have been written into a file called `normal.100s' (under MS-DOS, `100s.nor') in the current directory. Now type "rdann atruth 100s" and observe the output for a few seconds, then try "rdann normal 100s" and notice the difference.
  2. 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).
  3. 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:
    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
    
    For starters, convert a sample value of 280 into physical units.
  4. Convert 120 mmHg into adus.
  5. What are the maximum and minimum possible sample values in adu? in mmHg?
  6. How large is `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?
  7. 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?
  8. 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 K&R, pp. 114--115, if you don't know how to do this).
  9. 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.
  10. 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
    your-program record 10:0 10:10
    
    should skip the first ten minutes, then process the next ten seconds of signals from record.
  11. 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 `100'. Why doesn't it work?
     1  #include <ecg/db.h>
     2
     3  main()
     4  {
     5      DB_Annotation *annot;
     6      DB_Anninfo ai;
     7      int t;
     8
     9      ai.name = "atruth";
    10      ai.stat = READ;
    11      if (annopen(100, ai, 1)) {
    12          while (getann(1, annot)) {
    13              printf("%s\t(%d)\t%s\n", timstr(annot.time),
    14                     annot.time, mstimstr(annot.time - t));
    15              t = annot.time;
    16          }
    17      }
    18  }
    
    Extra credit: Without actually trying it out, what does it produce on the standard output?
  12. Using 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 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 isigsettime and 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 isigsettime equivalent that executes in an average of 100 msec or less?
  13. 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 putvec (see section Example 7: A General-Purpose FIR Filter, for a model program). Try it out on MIT-BIH record `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.
  14. 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 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.
  15. 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 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?
  16. (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 `200'. Read the documentation on the annotation comparator, `bxb', and figure out how to use it to compare the annotation file produced by your program against the reference annotator `atruth'. How does your detector compare to Example 10?
  17. If the previous exercise was too easy, modify your detector so that the annotations it generates match those in the `atruth' file. Copying the `atruth' file is not permitted. You may find this rather difficult. Good luck!


Go to the first, previous, next, last section, table of contents.



George B. Moody (george@hstbme.mit.edu)