From 8c5571544169a4095ceebf5f6d6b5b9caf09c59b Mon Sep 17 00:00:00 2001 From: Tim Segers Date: Mon, 10 Oct 2022 18:37:28 +0200 Subject: Move sources into src --- src/opendeco.c | 208 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 208 insertions(+) create mode 100644 src/opendeco.c (limited to 'src/opendeco.c') diff --git a/src/opendeco.c b/src/opendeco.c new file mode 100644 index 0000000..9231ab3 --- /dev/null +++ b/src/opendeco.c @@ -0,0 +1,208 @@ +/* SPDX-License-Identifier: MIT-0 */ + +#include +#include +#include +#include +#include +#include + +#include "deco.h" +#include "schedule.h" +#include "output.h" + +#define MOD_OXY (abs_depth(msw_to_bar(6))) + +#ifndef VERSION +#define VERSION "unknown version" +#endif + +/* argp settings */ +static char args_doc[] = ""; +static char doc[] = "Implementation of Buhlmann ZH-L16 with Gradient Factors:" + "\vExamples:\n\n" + "\t./opendeco -d 18 -t 60 -g Air\n" + "\t./opendeco -d 30 -t 60 -g EAN32\n" + "\t./opendeco -d 40 -t 120 -g 21/35 -l 20 -h 80 --decogasses Oxygen,EAN50\n"; +const char *argp_program_bug_address = "<~tsegers/opendeco@lists.sr.ht> or https://todo.sr.ht/~tsegers/opendeco"; +const char *argp_program_version = "opendeco " VERSION; + +static struct argp_option options[] = { + {"depth", 'd', "NUMBER", 0, "Set the depth of the dive in meters", 0}, + {"time", 't', "NUMBER", 0, "Set the time of the dive in minutes", 1}, + {"gas", 'g', "STRING", 0, "Set the bottom gas used during the dive, defaults to Air", 2}, + {"gflow", 'l', "NUMBER", 0, "Set the gradient factor at the first stop, defaults to 30", 3}, + {"gfhigh", 'h', "NUMBER", 0, "Set the gradient factor at the surface, defaults to 75", 4}, + {"decogasses", 'G', "LIST", 0, "Set the gasses available for deco", 5}, + {0, 0, 0, 0, 0, 0} +}; + +struct arguments { + double depth; + double time; + char *gas; + int gflow; + int gfhigh; + char *decogasses; +}; + +static error_t parse_opt(int key, char *arg, struct argp_state *state) +{ + struct arguments *arguments = state->input; + + switch (key) { + case 'd': + arguments->depth = arg ? atof(arg) : -1; + break; + case 't': + arguments->time = arg ? atof(arg) : -1; + break; + case 'g': + arguments->gas = arg; + break; + case 'G': + arguments->decogasses = arg; + break; + case 'l': + arguments->gflow = arg ? atoi(arg) : 100; + break; + case 'h': + arguments->gfhigh = arg ? atoi(arg) : 100; + break; + case ARGP_KEY_END: + if (arguments->depth < 0 || arguments->time < 0) { + argp_state_help(state, stderr, ARGP_HELP_USAGE); + argp_failure(state, 1, 0, "Options -d and -t are required. See --help for more information"); + exit(ARGP_ERR_UNKNOWN); + } + default: + return ARGP_ERR_UNKNOWN; + } + + return 0; +} + +static struct argp argp = {options, parse_opt, args_doc, doc, 0, 0, 0}; + +void print_segment_callback(const decostate_t *ds, const waypoint_t wp, segtype_t type) +{ + static double last_depth; + static double runtime; + + wchar_t sign; + + runtime += wp.time; + + if (wp.depth < last_depth) + sign = ASC; + else if (wp.depth > last_depth) + sign = DEC; + else + sign = LVL; + + if (type != SEG_TRAVEL) + print_planline(sign, wp.depth, wp.time, runtime, wp.gas); + + last_depth = wp.depth; +} + +int parse_gasses(gas_t **gasses, char *str) +{ + if (!str) { + *gasses = NULL; + return 0; + } + + /* count number of gasses in string */ + int nof_gasses = 1; + + for (int c = 0; str[c]; c++) + if (str[c] == ',') + nof_gasses++; + + /* allocate gas array */ + gas_t *deco_gasses = malloc(nof_gasses * sizeof(gas_t)); + + /* fill gas array */ + char *gas_str = NULL; + int gas_idx = 0; + + while (1) { + if (!gas_str) + gas_str = strtok(str, ","); + else + gas_str = strtok(NULL, ","); + + if (!gas_str) + break; + + scan_gas(&deco_gasses[gas_idx], gas_str); + gas_idx++; + } + + *gasses = deco_gasses; + return nof_gasses; +} + +int main(int argc, char *argv[]) +{ + setlocale(LC_ALL, "en_US.utf8"); + + /* argp */ + struct arguments arguments; + + arguments.depth = -1; + arguments.time = -1; + arguments.gas = "Air"; + arguments.gflow = 30; + arguments.gfhigh = 75; + arguments.decogasses = ""; + + argp_parse(&argp, argc, argv, 0, 0, &arguments); + + /* setup */ + decostate_t ds; + init_decostate(&ds, arguments.gflow, arguments.gfhigh, msw_to_bar(3)); + double dec_per_min = msw_to_bar(9); + + gas_t bottom_gas; + scan_gas(&bottom_gas, arguments.gas); + + gas_t *deco_gasses; + int nof_gasses = parse_gasses(&deco_gasses, arguments.decogasses); + + /* 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 = msw_to_bar(arguments.depth) / dec_per_min; + double bottom_time = max(1, arguments.time - descent_time); + + waypoint_t waypoints[] = { + {.depth = abs_depth(msw_to_bar(arguments.depth)), .time = descent_time, &bottom_gas}, + {.depth = abs_depth(msw_to_bar(arguments.depth)), .time = bottom_time, &bottom_gas}, + }; + + print_planhead(); + simulate_dive(&ds, waypoints, len(waypoints), &print_segment_callback); + + /* generate deco schedule */ + double depth = waypoints[len(waypoints) - 1].depth; + const gas_t *gas = waypoints[len(waypoints) - 1].gas; + + /* determine @+5 TTS */ + decostate_t ds_ = ds; + add_segment_const(&ds_, depth, 5, gas); + decoinfo_t di_plus5 = calc_deco(&ds_, depth, gas, deco_gasses, nof_gasses, NULL); + + /* print actual deco schedule */ + decoinfo_t di = calc_deco(&ds, depth, gas, deco_gasses, nof_gasses, &print_segment_callback); + + /* output deco info and disclaimer */ + wprintf(L"\nNDL: %i TTS: %i TTS @+5: %i\n", (int) floor(di.ndl), (int) ceil(di.tts), (int) ceil(di_plus5.tts)); + print_planfoot(&ds); + + return 0; +} -- cgit v1.2.3-70-g09d2