aboutsummaryrefslogtreecommitdiffstatshomepage
path: root/src/deco.c
blob: 51cdf0e77dfc8953d502c267de35317af2081cb8 (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
/* SPDX-License-Identifier: MIT-0 */

#include <assert.h>
#include <math.h>
#include <stdbool.h>

#include "deco.h"

#define RND(x) (round((x) *10000) / 10000)

enum ALGO ALGO_VER = ALGO_VER_DEFAULT;
double SURFACE_PRESSURE = SURFACE_PRESSURE_DEFAULT;
double P_WV = P_WV_DEFAULT;

double PO2_MAX = PO2_MAX_DEFAULT;
double END_MAX = END_MAX_DEFAULT;

int LAST_STOP_AT_SIX = LAST_STOP_AT_SIX_DEFAULT;

typedef struct zhl_n2_t {
    double t;
    double a[3];
    double b;
} zhl_n2_t;

typedef struct zhl_he_t {
    double t;
    double a;
    double b;
} zhl_he_t;

const zhl_n2_t ZHL16N[] = {
    {.t = 5.0,   .a = {1.1696, 1.1696, 1.1696}, .b = 0.5578},
    {.t = 8.0,   .a = {1.0000, 1.0000, 1.0000}, .b = 0.6514},
    {.t = 12.5,  .a = {0.8618, 0.8618, 0.8618}, .b = 0.7222},
    {.t = 18.5,  .a = {0.7562, 0.7562, 0.7562}, .b = 0.7825},
    {.t = 27.0,  .a = {0.6667, 0.6667, 0.6200}, .b = 0.8126},
    {.t = 38.3,  .a = {0.5933, 0.5600, 0.5043}, .b = 0.8434},
    {.t = 54.3,  .a = {0.5282, 0.4947, 0.4410}, .b = 0.8693},
    {.t = 77.0,  .a = {0.4701, 0.4500, 0.4000}, .b = 0.8910},
    {.t = 109.0, .a = {0.4187, 0.4187, 0.3750}, .b = 0.9092},
    {.t = 146.0, .a = {0.3798, 0.3798, 0.3500}, .b = 0.9222},
    {.t = 187.0, .a = {0.3497, 0.3497, 0.3295}, .b = 0.9319},
    {.t = 239.0, .a = {0.3223, 0.3223, 0.3065}, .b = 0.9403},
    {.t = 305.0, .a = {0.2971, 0.2850, 0.2835}, .b = 0.9477},
    {.t = 390.0, .a = {0.2737, 0.2737, 0.2610}, .b = 0.9544},
    {.t = 498.0, .a = {0.2523, 0.2523, 0.2480}, .b = 0.9602},
    {.t = 635.0, .a = {0.2327, 0.2327, 0.2327}, .b = 0.9653},
};

const zhl_he_t ZHL16He[] = {
    {.t = 1.88,   .a = 1.6189, .b = 0.4770},
    {.t = 3.02,   .a = 1.3830, .b = 0.5747},
    {.t = 4.72,   .a = 1.1919, .b = 0.6527},
    {.t = 6.99,   .a = 1.0458, .b = 0.7223},
    {.t = 10.21,  .a = 0.9220, .b = 0.7582},
    {.t = 14.48,  .a = 0.8205, .b = 0.7957},
    {.t = 20.53,  .a = 0.7305, .b = 0.8279},
    {.t = 29.11,  .a = 0.6502, .b = 0.8553},
    {.t = 41.20,  .a = 0.5950, .b = 0.8757},
    {.t = 55.19,  .a = 0.5545, .b = 0.8903},
    {.t = 70.69,  .a = 0.5333, .b = 0.8997},
    {.t = 90.34,  .a = 0.5189, .b = 0.9073},
    {.t = 115.29, .a = 0.5181, .b = 0.9122},
    {.t = 147.42, .a = 0.5176, .b = 0.9171},
    {.t = 188.24, .a = 0.5172, .b = 0.9217},
    {.t = 240.03, .a = 0.5119, .b = 0.9267},
};

double bar_to_msw(const double bar)
{
    return bar * 10;
}

double msw_to_bar(const double msw)
{
    return msw / 10;
}

double abs_depth(const double gd)
{
    return gd + SURFACE_PRESSURE;
}

double gauge_depth(const double ad)
{
    return ad - SURFACE_PRESSURE;
}

gas_t gas_new(const unsigned char o2, const unsigned char he, double mod)
{
    assert(o2 + he <= 100);

    if (mod == MOD_AUTO) {
        double mod_po2 = PO2_MAX / (o2 / 100.0);
        double mod_end = END_MAX / (1 - he / 100.0);

        mod = min(mod_po2, mod_end);
    }

    return (gas_t){.o2 = o2, .he = he, .n2 = 100 - o2 - he, .mod = mod};
}

int gas_equal(const gas_t *g1, const gas_t *g2)
{
    return g1->o2 == g2->o2 && g1->he == g2->he && g1->mod == g2->mod;
}

unsigned char gas_o2(const gas_t *gas)
{
    return gas->o2;
}

unsigned char gas_he(const gas_t *gas)
{
    return gas->he;
}

unsigned char gas_n2(const gas_t *gas)
{
    return gas->n2;
}

double gas_mod(const gas_t *gas)
{
    return gas->mod;
}

double add_segment_ascdec(decostate_t *ds, const double dstart, const double dend, const double time, const gas_t *gas)
{
    assert(time > 0);

    const double rate = (dend - dstart) / time;

    for (int i = 0; i < 16; i++) {
        double pio = gas_he(gas) / 100.0 * (dstart - P_WV);
        double po = ds->phe[i];
        double r = gas_he(gas) / 100.0 * rate;
        double k = log(2) / ZHL16He[i].t;
        double t = time;

        ds->phe[i] = pio + r * (t - 1 / k) - (pio - po - (r / k)) * exp(-k * t);
    }

    for (int i = 0; i < 16; i++) {
        double pio = gas_n2(gas) / 100.0 * (dstart - P_WV);
        double po = ds->pn2[i];
        double r = gas_n2(gas) / 100.0 * rate;
        double k = log(2) / ZHL16N[i].t;
        double t = time;

        ds->pn2[i] = pio + r * (t - 1 / k) - (pio - po - (r / k)) * exp(-k * t);
    }

    /* TODO add CNS */
    /* TODO add OTU */

    if (dend > ds->max_depth)
        ds->max_depth = dend;

    return time;
}

double add_segment_const(decostate_t *ds, const double depth, const double time, const gas_t *gas)
{
    assert(time > 0);

    for (int i = 0; i < 16; i++) {
        double pio = gas_he(gas) / 100.0 * (depth - P_WV);
        double po = ds->phe[i];
        double k = log(2) / ZHL16He[i].t;
        double t = time;

        ds->phe[i] = po + (pio - po) * (1 - exp(-k * t));
    }

    for (int i = 0; i < 16; i++) {
        double pio = gas_n2(gas) / 100.0 * (depth - P_WV);
        double po = ds->pn2[i];
        double k = log(2) / ZHL16N[i].t;
        double t = time;

        ds->pn2[i] = po + (pio - po) * (1 - exp(-k * t));
    }

    /* TODO add CNS */
    /* TODO add OTU */

    if (depth > ds->max_depth)
        ds->max_depth = depth;

    return time;
}

double get_gf(const decostate_t *ds, const double depth)
{
    const unsigned char lo = ds->gflo;
    const unsigned char hi = ds->gfhi;

    if (ds->firststop == -1)
        return lo;

    if (depth <= SURFACE_PRESSURE)
        return hi;

    if (depth >= ds->firststop)
        return lo;

    /* interpolate lo and hi between first stop and last stop */
    return hi - (hi - lo) * gauge_depth(depth) / gauge_depth(ds->firststop);
}

double ceiling(const decostate_t *ds, double gf)
{
    double c = 0;
    gf /= 100;

    for (int i = 0; i < 16; i++) {
        /* n2 a and b values */
        double an = ZHL16N[i].a[ALGO_VER];
        double bn = ZHL16N[i].b;

        /* he a and b values */
        double ah = ZHL16He[i].a;
        double bh = ZHL16He[i].b;

        /* scale n2 and he values for a and b proportional to their pressure */
        double pn2 = ds->pn2[i];
        double phe = ds->phe[i];

        double a = ((an * pn2) + (ah * phe)) / (pn2 + phe);
        double b = ((bn * pn2) + (bh * phe)) / (pn2 + phe);

        /* update ceiling */
        c = max(c, ((pn2 + phe) - (a * gf)) / (gf / b + 1 - gf));
    }

    return c;
}

double gf99(const decostate_t *ds, double depth)
{
    double gf = 0;

    for (int i = 0; i < 16; i++) {
        /* n2 a and b values */
        double an = ZHL16N[i].a[ALGO_VER];
        double bn = ZHL16N[i].b;

        /* he a and b values */
        double ah = ZHL16He[i].a;
        double bh = ZHL16He[i].b;

        /* scale n2 and he values for a and b proportional to their pressure */
        double pn2 = ds->pn2[i];
        double phe = ds->phe[i];

        double a = ((an * pn2) + (ah * phe)) / (pn2 + phe);
        double b = ((bn * pn2) + (bh * phe)) / (pn2 + phe);

        /* update gf99 */
        gf = max(gf, (pn2 + phe - depth) / (a + depth / b - depth));
    }

    return gf * 100;
}

void init_tissues(decostate_t *ds)
{
    const double pn2 = 0.79 * (SURFACE_PRESSURE - P_WV);
    const double phe = 0.00 * (SURFACE_PRESSURE - P_WV);

    for (int i = 0; i < 16; i++)
        ds->pn2[i] = pn2;

    for (int i = 0; i < 16; i++)
        ds->phe[i] = phe;
}

void init_decostate(decostate_t *ds, const unsigned char gflo, const unsigned char gfhi, const double ceil_multiple)
{
    assert(gflo <= gfhi);

    init_tissues(ds);

    ds->gflo = gflo;
    ds->gfhi = gfhi;
    ds->firststop = -1;
    ds->max_depth = 0;
    ds->ceil_multiple = ceil_multiple;
}

double ppO2(double depth, const gas_t *gas)
{
    return gas_o2(gas) / 100.0 * depth;
}

double end(double depth, const gas_t *gas)
{
    return (gas_o2(gas) + gas_n2(gas)) / 100.0 * depth;
}

double ead(double depth, const gas_t *gas)
{
    return depth * gas_n2(gas) / 79.0;
}