/* SPDX-License-Identifier: MIT-0 */ #include #include #include #include #include #include #include #include "deco.h" #include "opendeco-cli.h" #include "opendeco-conf.h" #include "output.h" #include "schedule.h" #include "units.h" #define MOD_OXY (abs_depth(xsw_to_bar(msw_or_fsw(6, 20)))) #define RMV_DIVE_DEFAULT 20 #define RMV_DECO_DEFAULT 15 #define SHOW_TRAVEL_DEFAULT 0 double RMV_DIVE = RMV_DIVE_DEFAULT; double RMV_DECO = RMV_DECO_DEFAULT; int SHOW_TRAVEL = SHOW_TRAVEL_DEFAULT; static struct gas_usage { const gas_t *gas; double usage; } gas_usage[10]; int register_gas_use(double depth, double time, const gas_t *gas, double rmv) { double usage = depth * time * rmv; for (int i = 0; i < 10; i++) { if (!gas_usage[i].gas) { gas_usage[i] = (struct gas_usage){ .gas = gas, .usage = usage, }; return 0; } else if (gas_usage[i].gas == gas) { gas_usage[i].usage += usage; return 0; } } return -ENOSPC; } void print_gas_use(void) { static char gasbuf[12]; wprintf(L"\nRMV dive: %.2f%lc/min\n", RMV_DIVE, LTR); wprintf(L"RMV deco: %.2f%lc/min\n\n", RMV_DECO, LTR); wprintf(L"Gas use:\n", RMV_DECO, LTR); for (int i = 0; i < 10; i++) { if (gas_usage[i].gas) { format_gas(gasbuf, len(gasbuf), gas_usage[i].gas); strcat(gasbuf, ":"); wprintf(L"%-12s%5i%lc\n", gasbuf, (int) ceil(gas_usage[i].usage), LTR); } } } void print_segment_callback_fn(const decostate_t *ds, segtype_t type, void *arg) { static double last_depth; static double last_runtime; wchar_t sign; double time_diff; /* first time initialization */ if (!last_depth) last_depth = SURFACE_PRESSURE; if (ds->depth < last_depth) sign = ASC; else if (ds->depth > last_depth) sign = DEC; else sign = LVL; time_diff = ds->runtime - last_runtime; if (SHOW_TRAVEL || type != SEG_TRAVEL) print_planline(sign, ds->depth, time_diff, ds->runtime, ds->gas); /* register gas use */ double avg_seg_depth = (ds->depth + last_depth) / 2; double rmv = type == SEG_DIVE ? RMV_DIVE : RMV_DECO; register_gas_use(avg_seg_depth, time_diff, ds->gas, rmv); last_depth = ds->depth; last_runtime = ds->runtime; } int parse_gasses(gas_t **gasses, char *str) { int nof_gasses = 0; *gasses = NULL; if (!str) return nof_gasses; char *token = NULL; for (;;) { if (!token) token = strtok(str, ","); else token = strtok(NULL, ","); if (!token) break; else nof_gasses += 1; *gasses = realloc(*gasses, nof_gasses * sizeof(gas_t)); if (*gasses == NULL) { wprintf(L"Cannot reserve space. Aborting!\n"); exit(EXIT_FAILURE); } if (scan_gas(&(*gasses)[nof_gasses - 1], token)) { wprintf(L"Invalid gas (%s). Aborting!\n", token); exit(EXIT_FAILURE); } } return nof_gasses; } void apply_arguments(struct arguments *arguments) { SURFACE_PRESSURE = arguments->SURFACE_PRESSURE; SWITCH_INTERMEDIATE = arguments->SWITCH_INTERMEDIATE; LAST_STOP_AT_SIX = arguments->LAST_STOP_AT_SIX; RMV_DIVE = arguments->RMV_DIVE; RMV_DECO = arguments->RMV_DECO; SHOW_TRAVEL = arguments->SHOW_TRAVEL; UNITS = arguments->UNITS; } int main(int argc, char *argv[]) { setlocale(LC_ALL, "en_US.utf8"); /* get conf and cli options */ char *gas_default = strdup("Air"); char *decogasses_default = strdup(""); if (!gas_default || !decogasses_default) { errno = ENOMEM; perror(__func__); exit(EXIT_FAILURE); } struct arguments arguments = { .depth = -1, .time = -1, .gas = gas_default, .gflow = 30, .gfhigh = 75, .decogasses = decogasses_default, .SURFACE_PRESSURE = SURFACE_PRESSURE_DEFAULT, .SWITCH_INTERMEDIATE = SWITCH_INTERMEDIATE_DEFAULT, .LAST_STOP_AT_SIX = LAST_STOP_AT_SIX_DEFAULT, .RMV_DIVE = RMV_DIVE_DEFAULT, .RMV_DECO = RMV_DECO_DEFAULT, .SHOW_TRAVEL = SHOW_TRAVEL_DEFAULT, .UNITS = UNITS_DEFAULT, }; opendeco_conf_parse("opendeco.toml", &arguments); opendeco_argp_parse(argc, argv, &arguments); /* apply global options */ apply_arguments(&arguments); /* setup */ decostate_t ds; decostate_t ds_p5; gas_t bottom_gas; gas_t *deco_gasses; double dec_per_min = xsw_to_bar(msw_or_fsw(9, 30)); int nof_gasses = parse_gasses(&deco_gasses, arguments.decogasses); init_decostate(&ds, arguments.gflow, arguments.gfhigh, xsw_to_bar(msw_or_fsw(3, 10))); if (scan_gas(&bottom_gas, arguments.gas)) { wprintf(L"Invalid gas (%s). Aborting!\n", arguments.gas); exit(EXIT_FAILURE); } /* override oxygen mod */ for (int i = 0; i < nof_gasses; i++) if (gas_o2(&deco_gasses[i]) == 100) deco_gasses[i].mod = MOD_OXY; /* simulate dive */ double descent_time = xsw_to_bar(arguments.depth) / dec_per_min; double bottom_time = max(1, arguments.time - descent_time); waypoint_t waypoints[] = { {.depth = abs_depth(xsw_to_bar(arguments.depth)), .time = descent_time, &bottom_gas}, {.depth = abs_depth(xsw_to_bar(arguments.depth)), .time = bottom_time, &bottom_gas}, }; waypoint_callback_t print_segment_callback = { .fn = &print_segment_callback_fn, .arg = NULL, }; print_planhead(); simulate_dive(&ds, waypoints, len(waypoints), &print_segment_callback); ds_p5 = ds; /* calculate deco */ decoinfo_t di = calc_deco(&ds, deco_gasses, nof_gasses, &print_segment_callback); /* calculate @+5 TTS */ add_segment_const(&ds_p5, ds_p5.depth, 5, ds_p5.gas); decoinfo_t di_p5 = calc_deco(&ds_p5, deco_gasses, nof_gasses, NULL); /* output deco info and disclaimer */ wprintf(L"\nNDL: %imin TTS: %imin TTS @+5: %imin\n", (int) floor(di.ndl), (int) ceil(di.tts), (int) ceil(di_p5.tts)); print_planfoot(&ds); print_gas_use(); print_disclaimer(); /* cleanup */ free(deco_gasses); free(arguments.gas); free(arguments.decogasses); return 0; }