home *** CD-ROM | disk | FTP | other *** search
Text File | 1990-12-16 | 28.3 KB | 1,073 lines |
- /* $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));
- }
-