# This is a shell archive.  Remove anything before this line,
# then unpack it by saving it in a file and typing "sh file".
#
# Wrapped by Dave Waller <dave@hpdstma> on Wed Dec 12 16:42:32 1990
#
# This archive contains:
#	Navy		Edge		Buhlmann	Huggins		
#	divecomp.c	makefile	sample_prof	rev_history	
#

LANG=""; export LANG
PATH=/bin:/usr/bin:$PATH; export PATH

echo x - Navy
cat >Navy <<'@EOF'
5	104
10	88
20	72
40	58
80	52
120	51
@EOF

chmod 644 Navy

echo x - Edge
cat >Edge <<'@EOF'
s:2.5
5 100
11 81.8
17 71.5
24 63.7
37 55.9
61 50.7
87 46.8
125 43
197 39.1
271 36.5
392 33.9
480 33.0
@EOF

chmod 644 Edge

echo x - Buhlmann
cat >Buhlmann <<'@EOF'
2.65	114
7.94	91
12.2	77
18.5	70
26.5	65
37	58
53	54
79	53
114	53
146	51
185	51
238	48
304	43
397	43
503	43
635	43
@EOF

chmod 644 Buhlmann

echo x - Huggins
cat >Huggins <<'@EOF'
5	102.0
10	85.0
20	67.5
40	54.5
80	47.5
120	43.0
@EOF

chmod 644 Huggins

echo x - divecomp.c
cat >divecomp.c <<'@EOF'
/* $Header: divecomp.c,v 2.5 90/12/12 15:30:02 dave Exp $
   $Log:	divecomp.c,v $
 * Revision 2.5  90/12/12  15:30:02  15:30:02  dave (Dave Waller)
 * See file "rev_history" for information on new features.
 * 
 * Revision 2.4  90/11/27  15:30:03  15:30:03  dave (Dave Waller)
 * new features:
 * 
 * - tissue loading graph now compresses when tissue loading exceeds
 *   100%, to a scale of 0-200%. Rescales to 0-100% when all compartments
 *   are <= 100%.
 * 
 * - Compartments can now be displayed as absolute pressure in (units) sea
 *   water, instead of % loading. Default is loading graph, absolute
 *   pressure can be selected at runtime with -P option. While the program
 *   is running, user can toggle between the two modes by typing 't' (this
 *   only works while calculations are taking place, not while waiting for
 *   input). Scale along the top is calibrated in (units) sea water absolute.
 * 
 * - During calculation, depth can be adjusted up or down using the up and
 *   down arrow keys on the keyboard. For example, if the program is calculating
 *   a dive level of 60 feet for 50 minutes, the depth can be adjusted up or
 *   down by pressing the arrow keys. Adjustments are made in increments
 *   equal to the depth increment in the dive profile display.
 * 
 * - If compartments are overloaded, the program can "autodecompress" by
 *   entering a depth of 'd' in interactive mode. The program will then
 *   move up to the ceiling value and "ride" it until all the compartments
 *   are <=100% loading.
 * 
 * - When displaying compartment loading, pressure values are in (units) SWA
 *   instead of FSWA. (Of course, if the units are feet, then the values
 *   *are* FSWA).
 * 
 * - Stats line during overloading now displays a more meaningful message
 *   regarding ceiling and decompression time.
 * 
 * Revision 2.1  90/11/21  11:33:57  11:33:57  dave (Dave Waller)
 * Changed SUN ifdefs to be macro SIMPLE instead... Makefile has been
 * modified as well. Since Sun users can now compile with full SVID
 * curses functionality, it seemed unnecessary to remove the reverse
 * video bells and whistles for for them. However, this functionality
 * is retained as a "simple" version of the program for those that do not
 * have reverse video and underline capabilities on their terminals.
 * 
 * Revision 2.0  90/11/21  11:11:30  11:11:30  dave (Dave Waller)
 * Bug fixes:
 * 
 * - incompatible typecasting in several places made the program fail
 *   with certain model files.
 * 
 * - Logical error in the sample period determination algorithm.
 * 
 * Enhancements:
 * 
 * - added capability to display depth in arbitrary units. Default is
 *   feet. Units are specified in either a profile file on the first line
 *   or with the -u option. To use a different unit system, the user must
 *   supply the unit name and a unit conversion factor that represents
 *   units/ft. For example, to display in fathoms, the user would type
 * 
 *   divecomp -mEdge -ufathoms:0.1667
 * 
 *   There are 6 feet in a fathom, or 1/6=0.1667 fathoms per foot. For meters,
 *   the specification would be -umeters:0.3048, or -um:0.3, etc.
 * 
 *   The unit specification is written into an output profile, so that the
 *   correct units are used when using the profile. Units specified in a
 *   profile file override command line specification.
 * 
 * - added logging capability. A log file can be specified with the -l
 *   option, and the program will write the allowed nitrogen %age for
 *   each compartment at each sample period into the log file. A header
 *   containing information regarding the model, sample period, and depth
 *   unit used is initially written into the file before the simulation
 *   begins. To make a log of a computer run, type
 * 
 *   divecomp -mBuhlman -umeters:0.3 -llog.buhlman
 * 
 * 
 * Revision 1.9  90/11/20  11:24:48  11:24:48  dave (Dave Waller)
 * Bug fixes:
 *      - logical errors in reading sample period from specified sources
 *      - update_dive_profile() routine hada rounding error in the
 *        code that compresses the display.
 * 
 * Enhancements:
 * 
 *      - new profile file format. Profiles are now stored as depth-duration
 *        pairs, instead of discrete samples. This eliminates the need for
 *        sample period specification in the profile file, and makes the
 *        file more compact and easier to read. I have written a filter
 *        'cvtprof' to convert from the old format to the new format, so
 *        any existing profiles that people have can be easily converted.
 * 
 *        Profiles created with the -o option in interactive mode are written
 *        in the new format.
 * 
 * Revision 1.8  90/11/16  16:33:45  16:33:45  dave (Dave Waller)
 * Cleaned up rounding algorithm for depth profile; added round up to
 * compartment bar graph display, so that it reads 100% properly;
 * Fixed scale graphic at top of compartment bar graph (it was off
 * by one character position to the right, contributing tothe bars not
 * being at 100% visually when they actually were).
 * 
 * Revision 1.7  90/11/16  10:34:26  10:34:26  dave (Dave Waller)
 * Added ability to specify different sample periods. Sample period ('s' in
 * the source) is specified in the following manner (decreasing precedence):
 * 
 *            1) via command line option
 *               Example: divecomp -mEdge -omyprof -s2.5
 * 
 *               Units are in minutes
 * 
 *            2) From within a profile file. The first line can optionally
 *               specifiy a sample rate, if it has the form "s:<rate>".
 *               Example: A profile that contains 2 minute samples would
 *               look like
 * 
 *               s:2.0
 *               60
 *               60
 *               60
 *               60
 *               .
 *               .
 *               .
 * 
 *               (the ellipses [...] are not part of the file). 'divecomp'
 *               now automatically writes the sample period into every profile
 *               it generates via the -o option.
 * 
 *             3) From a model file. Format is the same as a profile file.
 *                The most basic command, "divecomp -mEdge", for instance,
 *                will cause the computer to use the proper sampling rate
 *                for the Edge. Also, any profiles generates with this model
 *                will have the correct sampling value when used in different
 *                models.
 * 
 *             4) Default: 1 minute.
 * 
 * Revision 1.6  90/11/14  17:36:52  17:36:52  dave (Dave Waller)
 * Added compile time checks to modify curses behavior to accomodate
 * incomplete curses library on Sun/OS. Controlling tissue is highlighted
 * with a preceding '*' instead of reverse video, deiling message does not
 * appear in reverse video when tissue M values are exceeded, and tissue
 * bar graph is composed of '#' characters instead of reverse video
 * spaces. Not quite as pretty as the full curses version, but that's what
 * you get if you're running on a Sun.
 * 
 * Original graphical functionality is maintained for other platforms.
 * 
 * Revision 1.5  90/11/14  13:58:43  13:58:43  dave (Dave Waller)
 * Added the following features:
 * 
 * - Decompression calculation and indication via ndl()
 * - ingas/outgas indicator nxt to each compartment number
 * - full "bottom timer" functions (i.e. dive #, bottom time, surface int)
 * 
 * Fixed the following bugs:
 * 
 * - would run forever if duration of 0 entered in interactive mode.
 *   Program now rejects such an entry, and asks for depth and duration
 *   again.
 * 
 * - Controlling tissue indication lagged one sample behind actual value;
 *   Fixed.
 * 
 * - Dive profile graph now rounds up to the nearest depth increment.
 * 
 * Revision 1.4  90/11/13  16:38:49  16:38:49  dave (Dave Waller)
 * Fixed up the comments.
 * 
 * Revision 1.3  90/11/13  15:11:48  15:11:48  dave (Dave Waller)
 * Initial checkin; started using RCS to keep track of revisions. This
 * revision also fixes a bug with the code that reads in half times from model
 * files -- they were stored as type 'int', which created problems for
 * fscanf if the half times were floats. Changed the array 'half[]' to
 * type 'float'.
 *  */

/* dive computer simulator, by Dave Waller (davew@hpdstma.hp.com)

========================================================================
Acknowlegments:

Much thanks to Eric Williams (sargon@portia.stanford.edu) for his
outstanding contribution to the ndl() routine to incorporate ceilings
and decompression times.  His support and timely reviews/bug reports
have been extremely helpful.  Thanks, Eric!
========================================================================

This program simulates a multi-compartment model dive computer, based
upon modified Haldanean tissue absoption models.  The number of
compartments and their corresponding constants are not built into the
program, but must be loaded at runtime, allowing the program to
simulate most dive computers on the market today.

Subsequent derivation of equations as found in the comments below were
done by myself, as I sit here at work, using only my memory;
therefore, this initial pass at the simulation may contain some errors
that I will have to fix later, after I have had time to review the
pertinent material at home.  Surprisingly enough, it seems to work
pretty accurately, based upon my experience with my ORCA Delpi.  Have
fun!

	   Tissue halftimes in general express the time it takes for a
	   tissue to either absorb or release nitrogen such that the
	   tissue is 50% equalized to the pressure differential between
	   the ambient nitrogen pressure and the tissue nitrogen
	   pressure. This uptake or outgassing obeys an asymptotic
	   exponential, namely if ambient pressure is PA, and initial tissue
	   pressure is PT0, then the tissue pressure as a function of
	   time is expressed as,

	   PT = PT0 + (PA - PT0)(1 - e^(-kt))

	   where k = -ln(0.5)/T     = 0.6931/T
		 	       half           half

	   This formula holds true for static conditions; i.e. PA does
	   not change. In reality, a diver will probably be continuously
	   changing depth, and therefore PA is changing also. A
	   reasonable approximation of continous PT values can be
	   calculated by sampling PA at discrete intervals, and applying
	   the above formula over the preceding sample interval, then
	   starting over with a new PA and PT0 for the next interval
	   based upon the calculation.

	   In this simulated computer, we assume datapoints are recorded
	   one per minute. Since the halftimes are expressed in minutes,
	   the above formula can be reduce to an adjustment at each
	   sample that consists of the following:

	   PTnew = PTlast + K(PA - PTlast)

	   where K = (1 - e^(-k*1)) = 1-e^(-k) = 1 - (1/e^k) =

				   1 - (0.5)^(1/T    )
						 half
	   
	   Note that the K value is dependant upon consistency between
	   the units of the compartment half time and the computer
	   sample period. Specifically, the above formula only applies
	   if the sample period is 1 unit of the half time unit (in this
	   case minutes). For an arbitrary sample period s, the formula
	   for the K value is

	   K = 1 - (0.5)^(s/T    )
			     half

	   The dive computer simulator calculates the K values on
	   startup, with the sample period 's' being taken from the
	   following sources, in order of decreasing priority:

	   1) a sample period specified on the command line with the
	      -s option

	   2) A sample period specified in a profile file. This must
	      take precedence over a sample period in a model file,
	      otherwise the number of samples in the file will not
	      match the dive profile in real time. In order to compare
	      different models, the same sampling period must be used
	      for a single profile.

	   3) A sample period specified in the model file

	   4) The default sample period of 1 second

	   */

char scale100[]=" 10| 20| 30| 40| 50| 60| 70| 80| 90|100|  % Allowed N2";
char scale200[]=" 20| 40| 60| 80|100|120|140|160|180|200|  % Allowed N2";

#include <stdio.h>
#include <curses.h>

#define NCOMP		24
#define ONE_ATM_FSW	33.0
#define PN2		0.79
#define ONE_ATM_FSW_N2	PN2 * ONE_ATM_FSW
#define LN_HALF		-0.6931
#define INF 		32000		/* for ndl() */
#define DELTAM		1.0		/* temporary fix */
#define SHOW_LOADING	0
#define SHOW_FSWA	1

/* Some obscure variable names and their meanings:

	bt	Bottom Time

	si	Surface Interval

	cf	Compression factor (for compressing profile display)

	di	Depth Increment (for profile display)

*/

int cf=1, duration=0, samples=0, n=0, controlling_tissue, lines=24,
	ceiling, dive=1, mode=SHOW_LOADING, logmode=SHOW_LOADING,
	decomode=0, width=80;

float bt=0.0, si=0.0, apn2=ONE_ATM_FSW_N2, half[NCOMP], ucf=1.0, di,
	depth, Kvalues[NCOMP], Mvalues[NCOMP], c[NCOMP], s=0.0,
	depth_record[8192];

double ceil(), floor(), pow(), exp(), log();
float loading();
char buf[128], unitstr[16], scale100FSWA[80], scale200FSWA[80];

char revision[]="$Header: divecomp.c,v 2.5 90/12/12 15:30:02 dave Exp $";

FILE *profile=NULL, *outfile=NULL, *logfile=NULL;

main(argc,argv)
int argc;
char *argv[];
{	
	FILE *model=NULL, *fopen();
	int i, opt;
	char *getenv(), *envstr, *cptr, *strcpy(), *strchr(), mname[32];
	char *strcat();
	float get_sample(), dummy;
	double atof();
	extern char *optarg;

	strcpy(unitstr,"ft");

	/* find out how many lines display has */

	if ((envstr=getenv("LINES"))!=0) lines=atoi(envstr);
	if ((envstr=getenv("WIDTH"))!=0) width=atoi(envstr);

	/* process command line options */

	while ((opt=getopt(argc,argv,"p:m:o:s:u:l:P"))!=EOF) {
		switch(opt) {
			case 'p':
				if ((profile=fopen(optarg,"r"))==NULL) {
					perror(optarg);
					exit(1);
				}
				break;
			case 'm':
				if ((model=fopen(optarg,"r"))==NULL) {
					 perror(optarg);
					 exit(1);
				}
				strcpy(mname,optarg);
				break;
			case 'o':
				if ((outfile=fopen(optarg,"w"))==NULL) {
					perror(optarg);
					exit(1);
				}
				break;
			case 's':
				s = atof(optarg);
				break;
			case 'u':
				cptr = strchr(optarg,(int)':');
				*cptr = '\0';
				ucf = atof(cptr+1);
				strcpy(unitstr, optarg);
				break;
			case 'l':
				if ((logfile=fopen(optarg,"w"))==NULL) {
					perror(optarg);
					exit(1);
				}
				break;
			case 'P':
				mode=logmode=SHOW_FSWA;
				break;
				
		}
	}

	if (argc == 1) {
		fprintf(stderr, "Valid options are:\n");
		fprintf(stderr, " -m<fname>        Model file.  REQUIRED.\n");
		fprintf(stderr, " -p<fname>        Input profile filename\n");
		fprintf(stderr, " -o<fname>        Output filename for dive profile\n");
		fprintf(stderr, " -l<fname>        Output logfile filename.  logfile is depth and N2 vs time.\n");
		fprintf(stderr, " -s<float>        Sample period (in minutes)\n");
		fprintf(stderr, " -u<name>:<float> Depth units.  (new-unit)*<float> = 1 ft\n");
		fprintf(stderr, " -P               Display compartments as (units)SWA\n");
		exit(0);
	}

	if (model==NULL) {
		fprintf(stderr, "You must specify a model with the \"-m\" option\n");
		exit(0);
	} 

	/* check profile for depth unit specification */

	if (profile)
		if(fscanf(profile,"u:%[^:]:%f",unitstr,&ucf)==0)
			rewind(profile);

	/* check model file for sampling period */

	if (fscanf(model,"s:%f",(s==0?&s:&dummy))==0) rewind(model);

	if (s == 0.0) s = 1.0; /* default */

	/* set up the pressure scales */

	for (i=1; i<11; i++) {
		sprintf(buf,"%3d|",(int)(i*10*ucf));
		strcat(scale100FSWA,buf);
		sprintf(buf,"%3d|",(int)(i*20*ucf));
		strcat(scale200FSWA,buf);
	}

	sprintf(buf,"  %s sea water abs.", unitstr);
	strcat(scale100FSWA,buf);
	strcat(scale200FSWA,buf);

	/* read in the computer model */

	n = 0;
	while(fscanf(model, "%f %f", &half[n], &Mvalues[n]) != EOF) {
		Kvalues[n] = 1.0 - pow((double)0.5, (double)(s/half[n]));
		n++;
	}
	fclose(model);

	if (profile && outfile) {
		fprintf(stderr,"-o option can only be used in interactive mode.\n");
		exit(1);
	}

	/* put the depth unit into the outfile if there is one */

	if (outfile) fprintf(outfile,"u:%s:%.4f\n",unitstr,ucf);

	/* If logging, write the header into the logfile */

	if (logfile) {
		fprintf(logfile,"Model: %s   Sample period: %f min\n\n",
			mname, s);
		if (logmode==SHOW_LOADING)
			fprintf(logfile,"Depth\t%% Allowed Nitrogen\n(%s)\t",
				unitstr);
		else fprintf(logfile,"Depth\tCompartment pressures (%s sea water abs.)\n(%s)\t", unitstr, unitstr);
		for (i=0; i<n; i++) fprintf(logfile,"c%02d   ",i+1);
		fprintf(logfile,"\n");
	}

	/* determine the depth increment used in the profile display */

	for (di=ucf*5; ucf*200/di > lines-n-4; di+=ucf*5);

	/* initialize tissue N2 levels */

	for (i=0; i<NCOMP; i++) c[i] = ONE_ATM_FSW_N2;

	/* initialize the display */

	init_display();
	update_display();

	for (;;) {

		get_sample();

		/* update time values */

		if (depth > 3) {

			/* all depths >3ft are counted as bottom time */

			if ((si > 0.0) && (si < 5.0)) {

				/* We must account for brief periods of
				surfacing during the dive. The surface
				interval counter starts immediately upon
				ascending to depths shallower than 3
				feet, yet if the diver returns to depths
				deeper than 3 feet within 5 minutes, the
				time elapsed as surface interval must be
				added into the bottom time value, and
				the surface interval reset to 0 */

				bt += si + s;
				si = 0.0;
			}
			else if (si == 0.0) bt+=s; /* no intermediate
						   surface interval to
						   account for */

			else if (si > 5.0) {	   /* else the diver has
						   been on the surface
						   for more than 5
						   minutes, so we treat
						   this as a new dive
						   */
				dive++;
				bt=s;
				si=0.0;
			}
		}
		else si+=s; /* else the diver is still shallower than
			    3 feet; update the surface interval counter
			    */
		samples++;

		/* update ambient N2 pressure */

		apn2 = PN2*(depth+ONE_ATM_FSW);

		/* update tissue (compartment) N2 levels */

		update_tissues();

		/* check if there has been a character input */

		switch (getch()) {
			case 't':
				mode =
				   (mode==SHOW_LOADING?SHOW_FSWA:SHOW_LOADING);
				break;

			case 'q':
				decomode = 0; /* reset vars that cause */
				duration = 0; /* continuous calculation */
				break;

			case KEY_DOWN:
				depth += di;
				break;

			case KEY_UP:
				depth -= di;
				break;
		}

		/* repaint the display */

		update_display();
		update_dive_profile();
		refresh();

		/* if logging, write entry */

		if (logfile) {
			fprintf(logfile,"%.1f\t", ucf*depth);
			for (i=0; i<n; i++) {
				if (logmode == SHOW_LOADING)
					fprintf(logfile,"%-5d ",
						(int)(100*loading(i)));
				else fprintf(logfile,"%-5.1f ", c[i]);
			}
			fprintf(logfile,"\n");
		}
	}
}

update_display()
{
	/* update the stats line first. Since the controlling tissue is
	found in the ndl() routine which is called by the update_stats()
	routine, it must be called before the tissue graph is repainted.
	*/

	update_stats();
	update_ndl_limits();

	if (mode == SHOW_LOADING) display_loading();
	else display_fswa();
}

update_tissues()
{
	int i;

	for (i=0; i<n; i++) /* n == number of compartments */
		c[i] = c[i] + (apn2 - c[i])*Kvalues[i];
}

update_stats()
{
	int limit;
	float time_to_surface();
	char *showtime(), buf1[16], buf2[16], buf3[16];

	move(n+2,15);
	clrtoeol();
	limit = ndl(apn2);

	sprintf(buf, "Dive: %d  Depth: %.1f %s  BT: %s",
		dive, ucf*depth, unitstr,  showtime(buf1,bt));
	addstr(buf);
	move(n+3,15);
	clrtoeol();
	if (limit >= 0) {
		sprintf(buf,"NDL: %s  Sfc Int: %s",
			showtime(buf2,(float)limit), showtime(buf3,si));
		addstr(buf);
	}
	else {
		attron(A_REVERSE);
		sprintf(buf,"Ceiling: %.1f %s", ucf*ceiling,
			unitstr);
		addstr(buf);
		sprintf(buf," Time to surface: %s",
			showtime(buf1,time_to_surface()));
		addstr(buf);
		attroff(A_REVERSE);
	}
}

update_dive_profile()
{
	int i, j, x, y;

	/* update the dive profile display */

	if (samples/cf > 60) {

		/* erase and compress display */

		for (i=n+5; i<lines; i++) {
			move(i,10);
			clrtoeol();
		}

		cf *=2;
		for (i=1; i<samples; i+=cf) {
			x = 10+i/cf;
			y = n+5+(int)ceil((double)ucf*depth_record[i]/di);
			move((y>=lines?lines-1:y),x);
			addch((y>=lines?'v':'*'));
		}
	}

	x = 10+samples/cf;
	y = n+5+ceil((double)ucf*depth/di);
	move((y>=lines?lines-1:y),x);
	addch((y>=lines?'v':'*'));

	depth_record[samples] = depth;
}

update_ndl_limits()
{
	int i;
	int t;
	float d;
	float p;
	char buf[10];

	for (i=n+5; i<lines; i++) {
		d = (i-n-5)*di/ucf;		/* depth in feet */
		p = 0.79 * (d + 33.0);		/* pn2 at depth  */
		t = ndl(p);			/* compute limits */
		if (t<0) t=0;

		move(i,0);
		if (t >= INF) {
			addstr("INF");
		} else if (t >= 1000) {
			addstr("***");
		} else if (t>0) {
			sprintf(buf,"%3d",t);
			addstr(buf);
		} else addstr("   ");
	}
	t = ndl(apn2);    /* leave all the globals at the right values */
}

display_loading()
{
	int i, j, k, temp, over;
	/* if overpressure on any tissue, change the scale to 0 - 200% */

	for (over=i=0; i<n; i++) if (c[i] > Mvalues[i]) over=1;
	move(0,10);
	clrtoeol();
	if (over) addstr(scale200);
	else addstr(scale100);

	/* display tissue saturation */

	for (i=0; i<n; i++) { /* n == number of compartments */

		/* display the compartment number, highlighting if it
		is the controlling tissue. */

#ifdef SIMPLE
		move(1+i,6);
		if (i==controlling_tissue) sprintf(buf,"*%d",i+1);
		else sprintf(buf," %d",i+1);
#else
		move(1+i,7);
		if (i==controlling_tissue) attron(A_REVERSE);
		sprintf(buf,"%d",i+1);
#endif
		addstr(buf);
#ifndef SIMPLE
		attroff(A_REVERSE);
#endif

		/* display the ingas/outgas indicator for the
		compartment. Ingas/outgas indicator is adjusted to 3 decimal
		place accuracy. */

		move(1+i,9);
		temp = (apn2 - c[i]) * 1000.0;
		if (temp > 0) addch('>');
		if (temp == 0) addch(' ');
		if (temp < 0) addch('<');

		/* clear the bar before displaying the new value */

		move(1+i,10);
		clrtoeol();

		k = ceil(floor((double)(1000.0 * loading(i))) * (over==0?0.04:0.02));

#ifndef SIMPLE
		attron(A_REVERSE | A_UNDERLINE);
#endif
		sprintf(buf," %4.2f",ucf*c[i]);
		for (j=0; j<k; j++) {
			if (j==(width-11-strlen(buf))) {
				addch('>');
				break;
			}	
#ifdef SIMPLE
			if (j<(over==0?40:20)) addch('#');
#else
			if (j<(over==0?40:20)) addch(' ');
#endif
			else addch('*');
		}
#ifndef SIMPLE
		attroff(A_REVERSE | A_UNDERLINE);
#endif
		addstr(buf);
	}
}


display_fswa()
{
	int i, j, k, temp, over, Mmarker;
	/* if overpressure on any tissue, change the scale to 0 - 200% */

	for (over=i=0; i<n; i++) if (c[i] > 100.0) over=1;
	move(0,10);
	clrtoeol();
	if (over) addstr(scale200FSWA);
	else addstr(scale100FSWA);

	/* display compartment N2 levels in FSWA */

	for (i=0; i<n; i++) { /* n == number of compartments */

		/* display the compartment number, highlighting if it
		is the controlling tissue. */

#ifdef SIMPLE
		move(1+i,6);
		if (i==controlling_tissue) sprintf(buf,"*%d",i+1);
		else sprintf(buf," %d",i+1);
#else
		move(1+i,7);
		if (i==controlling_tissue) attron(A_REVERSE);
		sprintf(buf,"%d",i+1);
#endif
		addstr(buf);
#ifndef SIMPLE
		attroff(A_REVERSE);
#endif

		/* display the ingas/outgas indicator for the
		compartment. Ingas/outgas indicator is adjusted to 3 decimal
		place accuracy. */

		move(1+i,9);
		temp = (apn2 - c[i]) * 1000.0;
		if (temp > 0) addch('>');
		if (temp == 0) addch(' ');
		if (temp < 0) addch('<');

		/* clear the bar before displaying the new value */

		move(1+i,10);
		clrtoeol();

		Mmarker = ceil((double)(Mvalues[i]*(over==0?0.4:0.2)));
		k = ceil((double)(c[i]*(over==0?0.4:0.2)));
			

#ifndef SIMPLE
		attron(A_REVERSE | A_UNDERLINE);
#endif
		for (j=0; j<k; j++) {
			if (j==(width-11)) {
				addch('>');
				break;
			}	
#ifdef SIMPLE
			if (j<Mmarker) addch('#');
#else
			if (j<Mmarker) addch(' ');
#endif
			else addch('*');
		}
#ifndef SIMPLE
		attroff(A_REVERSE | A_UNDERLINE);
#endif
	}
}

init_display()
{
	int i;
	initscr();

	/* set nodelay mode for input processing */

	nodelay(stdscr,TRUE);
	keypad(stdscr,TRUE);

	/* print the saturation %age scale */

	move(0,10);
	if (mode==SHOW_LOADING) addstr(scale100);
	else addstr(scale100FSWA);

	/* label the Dive profile graph */

	move(n+3,0);
	addstr("NDL  Depth");
	move(n+4,0);
	addstr("----------");

	/* create the dive profile display */

	for (i=n+5; i<lines; i++) {
		move(i,4);
		sprintf(buf,"%5.1f|",(i-n-5)*di);
		addstr(buf);
	}
}

float get_sample()
{
	float target_depth;
	double atof();
	while (duration == 0) {
		if (!profile) {

			/* check for autodecompression mode */

			if (decomode) {
				if (ceiling == 0) decomode = 0;
				else {
					target_depth =
						ceil((double)ceiling/5.0)*5.0;
					if (target_depth - depth > 30)
						depth -= 30;
					else depth = target_depth;
					duration = 1;
				}	
				continue;
			}

			/* read depth and duration from user */

			nodelay(stdscr,FALSE);
			move(n+4,15);
			clrtoeol();
			addstr("Enter Depth ('q' to quit): ");
			refresh();
			getstr(buf);
			if (buf[0]=='q') {
				nodelay(stdscr,FALSE);
				endwin();
				exit(0);
			}

			/* 'd' indicates autodecompression */

			if (buf[0]=='d') {
				decomode = 1;
				move(n+4,15);
				clrtoeol();
				attron(A_REVERSE);
				addstr("*** autodecompressing ***");
				attroff(A_REVERSE);
				continue;
			}

			depth = atof(buf);
			move(n+4,15);
			clrtoeol();
			addstr("Enter Duration (min): ");
			refresh();
			getstr(buf);
			duration = atof(buf) / s;
			move(n+4,15);
			clrtoeol();
			if (outfile && duration)
				fprintf(outfile,"%.1f %d\n",depth,
					(int)(duration*s));
		}

		/* else get depth and duration from profile */

		else if (fscanf(profile,"%f %d",&depth, &duration)==EOF) {
			endwin();
			exit(0);
		}
		else duration /= s;
		depth /= ucf;
	}
	duration--;
	nodelay(stdscr,TRUE);
	return(depth);
}

float time_to_surface()
{
	int i, j;
	float time=0, ctemp[NCOMP], stop, next, pn2_stop, pn2_next,
	      target, temp, dt;

	/* make a copy of the compartment N2 partial pressure values */

	for (i=0; i<n; i++) ctemp[i] = c[i];
	
	/* set up first stop data */

	stop = ceil((double)(ceiling/5.0))*5.0;
	pn2_stop = PN2*(stop+ONE_ATM_FSW);

	/* for each stop, calculate the time to get to it from the
	previous stop */

	for (next=stop-5.0; next>=0.0; next-=5.0) {
		pn2_next = PN2*(next+ONE_ATM_FSW);
		for (i=0, temp=0; i<n; i++) {
			/* determine the time to decompress compartment
			to target N2 pressure. Target pressure is the
			value at which the compartment yields a ceiling
			equal to the next stop. Time is then calculated
			using the exponential decay formula with the
			current stop as the ambient pressure */

			target = next * DELTAM + Mvalues[i];
			if ((target < ctemp[i])&&(pn2_stop < ctemp[i])) {
				dt = half[i] / LN_HALF *
					log((double)(1.0 -
					(target - ctemp[i]) /
					(pn2_stop - ctemp[i])));
				if (dt > temp) temp = dt;
			}

		}

		/* convert dt to a number of samples */

		dt = ceil((double)(temp/s));

		/* adjust compartment values for next calculation */

		for (i=0; i<n; i++)
			for (j=0; j<dt; j++) ctemp[i] = ctemp[i] +
				(pn2_stop - ctemp[i]) * Kvalues[i];
		time += dt*s;
		stop = next;
		pn2_stop = PN2*(stop+ONE_ATM_FSW);
	}
	return(time);
}

ndl(pressure)
float pressure;
{
	int i, limit, dlimit;
	int dcontrol;
	int safe, outgas, decom;
	int t;
	double time_fn();

	limit = INF;
	dlimit = 0;
	dcontrol = -1;
	ceiling = 0;
	for (i=0; i<n; i++) {
		safe = (pressure < Mvalues[i]);
		outgas = (pressure < c[i]);
		decom = (Mvalues[i] < c[i]);
		if (decom) {
			/* compute ceiling */
			t = ceil((c[i] - Mvalues[i])/DELTAM);
			if (t > ceiling) ceiling = t;

			if ((outgas) && (safe)) {
				/* how long to decompress */
				t = - ceil(time_fn(pressure,i));
 				if (t < dlimit) { 
					dlimit = t;
					dcontrol = i;
				}
			} else {
				/* compartment will not decompress */
				dlimit = -INF;
				dcontrol = i;
			}
	        } else if ((!safe) && (!outgas)) {
 			/* compute no-decompress time */

			if (pressure == Mvalues[i]) {
				t = INF;
			} else if (pressure == c[i]) {
				t = INF;
			} else if (c[i] == Mvalues[i]) {
				t = 0;
			} else {
				t = floor(time_fn(pressure,i));
			}

			if (t < limit) {
				limit = t;
				controlling_tissue = i;
			}
		}
	}
	
	/* if any tissues are over their m-value than they will control */
	if (dcontrol >=0) {
		controlling_tissue = dcontrol;
	        limit = dlimit;
	}
	      
	return(limit);
}

char *showtime(tmpbuf,min)
char *tmpbuf;
float min;
{
	char *strcpy();
	if ((int)min >= INF) return(strcpy(tmpbuf,"INF"));
	if ((int)min > 60) sprintf(tmpbuf,"%d h %d m", (int)min/60, 
		(int)min%60);
	else sprintf(tmpbuf,"%d min", (int)min);
	return(tmpbuf);
}

double time_fn(pressure, i)
float pressure;
int i;
{
	double temp;

	temp = (pressure-Mvalues[i])/(pressure-c[i]);
	return(half[i]/LN_HALF * log(temp));
}

float loading(index)
int index;
{
	return((c[index]-ONE_ATM_FSW_N2)/(Mvalues[index]-ONE_ATM_FSW_N2));
}
@EOF

chmod 644 divecomp.c

echo x - makefile
cat >makefile <<'@EOF'

divecomp: divecomp.c
	cc -o divecomp divecomp.c -lm -lcurses

sun_divecomp: divecomp.c
	/usr/5bin/cc -o divecomp divecomp.c -lm -lcurses

simple:
	cc -DSIMPLE -o divecomp divecomp.c -lm -lcurses

sun_simple:
	/usr/5bin/cc -DSIMPLE -o divecomp divecomp.c -lm -lcurses

cvtprof: cvtprof.c
	cc -o cvtprof cvtprof.c
@EOF

chmod 644 makefile

echo x - sample_prof
cat >sample_prof <<'@EOF'
u:ft:1.0
110 10
90 20
60 10
30 5
20 5
10 5
0 60
40 30
30 10
@EOF

chmod 644 sample_prof

echo x - rev_history
cat >rev_history <<'@EOF'
Changes as of revision 2.5:

New features (lots!):

Now checks for "LINES" or "WIDTH" environment variable set, truncating the
compartment bar graph appropriately (for those of you who simulate DEEP
dives :-))

New option, -P, displays compartments as absolute pressure instead of
tissue loading %age. The pressure is in <units> sea water absolute, where
<units> is whatever units have been specified with the -u option. Default
is feet. -P also causes the compartment values logged to an optional log
file (specified with -l) to be in absolute pressure instead of loading
%age.

Graph can be toggled between loading display and absolute pressure
display by pressing the 't' key WHILE THE PROGRAM IS CALCULATING, not
while it is wating for input. For example, if you are running interactive
and you enter a depth of 100 ft for 20 minutes, 't' can be used while
the 20 minutes are elapsing to toggle the screen. 't' does not affect

initial -P option.

Changed the transition depth between bottom time and surface interval
counting to 3 feet from 5 feet. This was necessary for decompression
stuff (more later).

'q' can be pressed during calculations to abort the calculated run.
For instance, when calculating a long decompression in autodecompression
mode (more on this later), you can press 'q' to return to the input
prompt. Similarly when calculating a long depth/duration combination
entered from the keyboard.

No Decompression Limits are now displayed and continuously updated along
side the dive profile for each depth displayed.

When NDLs are exceeded, the stats line now displays a ceiling, and a
time to surface assuming proper following of the ceiling in 5 foot stops.
For example, after 20 minutes at 200 feet, the stats line might display
"Ceiling: 75.0 ft Time to surface: 46 min", indicating that the diver
should not go shallower than 75 feet. If the diver makes decompression
stops every 5 feet (stopping at each stop until the ceiling goes up to
the next 5' stop), it will take 46 minutes to make it safely to the surface.

Depths deeper than the deepest depth in the dive profile display now
show a 'v' in the deepest depth rather than not being displayed at all.

Bar graph now automatically reverts to a 2:1 scale in both loading and
absolute display modes. For instance, if any compartment exceeds 100%
loading, the graph will rescale to 0-200% (similarly for absolute
pressure display).

Autodecompression: When NDLs have been exceeded, the diver can
autodecompress by entering 'd' at the depth prompt. This will cause the
program to advance to the nearest 5' deeper than the indocated ceiling,
and follow the ceiling up in 5' increments. This will produce a profile
exactly equal to that used in calculating the 'time to surface' value.

If available on your keyboard, the up and down arrow keys can now be
used to change depth by one depth increment (as shown in the profile
display) with each press during calculation. This is mainly useful while
plotting a portion of a profile (i.e. 60' for 50 minutes), and you want
to create a more realistic profile by moving up or down while
calculating.
@EOF

chmod 644 rev_history

exit 0
