]> git.tdb.fi Git - ext/subsurface.git/blob - dive.c
Add helper function for doing depth unit calculations
[ext/subsurface.git] / dive.c
1 /* dive.c */
2 /* maintains the internal dive list structure */
3 #include <string.h>
4 #include <stdio.h>
5
6 #include "dive.h"
7
8 double get_depth_units(unsigned int mm, int *frac, const char **units)
9 {
10         int decimals;
11         double d;
12         const char *unit;
13
14         switch (output_units.length) {
15         case METERS:
16                 d = mm / 1000.0;
17                 unit = "m";
18                 decimals = d < 20;
19                 break;
20         case FEET:
21                 d = mm_to_feet(mm);
22                 unit = "ft";
23                 decimals = 0;
24                 break;
25         }
26         if (frac)
27                 *frac = decimals;
28         if (units)
29                 *units = unit;
30         return d;
31 }
32
33 struct dive *alloc_dive(void)
34 {
35         const int initial_samples = 5;
36         unsigned int size;
37         struct dive *dive;
38
39         size = dive_size(initial_samples);
40         dive = malloc(size);
41         if (!dive)
42                 exit(1);
43         memset(dive, 0, size);
44         dive->alloc_samples = initial_samples;
45         return dive;
46 }
47
48 struct sample *prepare_sample(struct dive **divep)
49 {
50         struct dive *dive = *divep;
51         if (dive) {
52                 int nr = dive->samples;
53                 int alloc_samples = dive->alloc_samples;
54                 struct sample *sample;
55                 if (nr >= alloc_samples) {
56                         unsigned int size;
57
58                         alloc_samples = (alloc_samples * 3)/2 + 10;
59                         size = dive_size(alloc_samples);
60                         dive = realloc(dive, size);
61                         if (!dive)
62                                 return NULL;
63                         dive->alloc_samples = alloc_samples;
64                         *divep = dive;
65                 }
66                 sample = dive->sample + nr;
67                 memset(sample, 0, sizeof(*sample));
68                 return sample;
69         }
70         return NULL;
71 }
72
73 void finish_sample(struct dive *dive, struct sample *sample)
74 {
75         dive->samples++;
76 }
77
78 /*
79  * So when we re-calculate maxdepth and meandepth, we will
80  * not override the old numbers if they are close to the
81  * new ones.
82  *
83  * Why? Because a dive computer may well actually track the
84  * max depth and mean depth at finer granularity than the
85  * samples it stores. So it's possible that the max and mean
86  * have been reported more correctly originally.
87  *
88  * Only if the values calculated from the samples are clearly
89  * different do we override the normal depth values.
90  *
91  * This considers 1m to be "clearly different". That's
92  * a totally random number.
93  */
94 static void update_depth(depth_t *depth, int new)
95 {
96         if (new) {
97                 int old = depth->mm;
98
99                 if (abs(old - new) > 1000)
100                         depth->mm = new;
101         }
102 }
103
104 static void update_duration(duration_t *duration, int new)
105 {
106         if (new)
107                 duration->seconds = new;
108 }
109
110 static void update_temperature(temperature_t *temperature, int new)
111 {
112         if (new) {
113                 int old = temperature->mkelvin;
114
115                 if (abs(old - new) > 1000)
116                         temperature->mkelvin = new;
117         }
118 }
119
120 static void fixup_pressure(struct dive *dive, struct sample *sample)
121 {
122         unsigned int pressure, index;
123         cylinder_t *cyl;
124
125         pressure = sample->cylinderpressure.mbar;
126         if (!pressure)
127                 return;
128         index = sample->cylinderindex;
129         if (index >= MAX_CYLINDERS)
130                 return;
131         cyl = dive->cylinder + index;
132         if (!cyl->start.mbar)
133                 cyl->start.mbar = pressure;
134         if (!cyl->end.mbar || pressure < cyl->end.mbar)
135                 cyl->end.mbar = pressure;
136 }
137
138 struct dive *fixup_dive(struct dive *dive)
139 {
140         int i;
141         double depthtime = 0;
142         int lasttime = 0;
143         int start = -1, end = -1;
144         int maxdepth = 0, mintemp = 0;
145         int lastdepth = 0;
146         int lasttemp = 0;
147
148         for (i = 0; i < dive->samples; i++) {
149                 struct sample *sample = dive->sample + i;
150                 int time = sample->time.seconds;
151                 int depth = sample->depth.mm;
152                 int temp = sample->temperature.mkelvin;
153
154                 if (lastdepth)
155                         end = time;
156
157                 if (depth) {
158                         if (start < 0)
159                                 start = lasttime;
160                         if (depth > maxdepth)
161                                 maxdepth = depth;
162                 }
163
164                 fixup_pressure(dive, sample);
165
166                 if (temp) {
167                         /*
168                          * If we have consecutive identical
169                          * temperature readings, throw away
170                          * the redundant ones.
171                          */
172                         if (lasttemp == temp)
173                                 sample->temperature.mkelvin = 0;
174                         else
175                                 lasttemp = temp;
176
177                         if (!mintemp || temp < mintemp)
178                                 mintemp = temp;
179                 }
180                 depthtime += (time - lasttime) * (lastdepth + depth) / 2;
181                 lastdepth = depth;
182                 lasttime = time;
183         }
184         if (end < 0)
185                 return dive;
186
187         update_duration(&dive->duration, end - start);
188         if (start != end)
189                 depthtime /= (end - start);
190
191         update_depth(&dive->meandepth, depthtime);
192         update_temperature(&dive->watertemp, mintemp);
193         update_depth(&dive->maxdepth, maxdepth);
194
195         return dive;
196 }
197
198 /* Don't pick a zero for MERGE_MIN() */
199 #define MERGE_MAX(res, a, b, n) res->n = MAX(a->n, b->n)
200 #define MERGE_MIN(res, a, b, n) res->n = (a->n)?(b->n)?MIN(a->n, b->n):(a->n):(b->n)
201
202 static struct dive *add_sample(struct sample *sample, int time, struct dive *dive)
203 {
204         struct sample *p = prepare_sample(&dive);
205
206         if (!p)
207                 return NULL;
208         *p = *sample;
209         p->time.seconds = time;
210         finish_sample(dive, p);
211         return dive;
212 }
213
214 /*
215  * Merge samples. Dive 'a' is "offset" seconds before Dive 'b'
216  */
217 static struct dive *merge_samples(struct dive *res, struct dive *a, struct dive *b, int offset)
218 {
219         int asamples = a->samples;
220         int bsamples = b->samples;
221         struct sample *as = a->sample;
222         struct sample *bs = b->sample;
223
224         for (;;) {
225                 int at, bt;
226                 struct sample sample;
227
228                 if (!res)
229                         return NULL;
230
231                 at = asamples ? as->time.seconds : -1;
232                 bt = bsamples ? bs->time.seconds + offset : -1;
233
234                 /* No samples? All done! */
235                 if (at < 0 && bt < 0)
236                         return fixup_dive(res);
237
238                 /* Only samples from a? */
239                 if (bt < 0) {
240 add_sample_a:
241                         res = add_sample(as, at, res);
242                         as++;
243                         asamples--;
244                         continue;
245                 }
246
247                 /* Only samples from b? */
248                 if (at < 0) {
249 add_sample_b:
250                         res = add_sample(bs, bt, res);
251                         bs++;
252                         bsamples--;
253                         continue;
254                 }
255
256                 if (at < bt)
257                         goto add_sample_a;
258                 if (at > bt)
259                         goto add_sample_b;
260
261                 /* same-time sample: add a merged sample. Take the non-zero ones */
262                 sample = *bs;
263                 if (as->depth.mm)
264                         sample.depth = as->depth;
265                 if (as->temperature.mkelvin)
266                         sample.temperature = as->temperature;
267                 if (as->cylinderpressure.mbar)
268                         sample.cylinderpressure = as->cylinderpressure;
269                 if (as->cylinderindex)
270                         sample.cylinderindex = as->cylinderindex;
271
272                 res = add_sample(&sample, at, res);
273
274                 as++;
275                 bs++;
276                 asamples--;
277                 bsamples--;
278         }
279 }
280
281 static char *merge_text(const char *a, const char *b)
282 {
283         char *res;
284
285         if (!a || !*a)
286                 return (char *)b;
287         if (!b || !*b)
288                 return (char *)a;
289         if (!strcmp(a,b))
290                 return (char *)a;
291         res = malloc(strlen(a) + strlen(b) + 9);
292         if (!res)
293                 return (char *)a;
294         sprintf(res, "(%s) or (%s)", a, b);
295         return res;
296 }
297
298 /* Pick whichever has any info (if either). Prefer 'a' */
299 static void merge_cylinder_type(cylinder_type_t *res, cylinder_type_t *a, cylinder_type_t *b)
300 {
301         if (a->size.mliter)
302                 b = a;
303         *res = *b;
304 }
305
306 static void merge_cylinder_mix(struct gasmix *res, struct gasmix *a, struct gasmix *b)
307 {
308         if (a->o2.permille)
309                 b = a;
310         *res = *b;
311 }
312
313 static void merge_cylinder_info(cylinder_t *res, cylinder_t *a, cylinder_t *b)
314 {
315         merge_cylinder_type(&res->type, &a->type, &b->type);
316         merge_cylinder_mix(&res->gasmix, &a->gasmix, &b->gasmix);
317         MERGE_MAX(res, a, b, start.mbar);
318         MERGE_MIN(res, a, b, end.mbar);
319 }
320
321 /*
322  * This could do a lot more merging. Right now it really only
323  * merges almost exact duplicates - something that happens easily
324  * with overlapping dive downloads.
325  */
326 struct dive *try_to_merge(struct dive *a, struct dive *b)
327 {
328         int i;
329         struct dive *res;
330
331         if (a->when != b->when)
332                 return NULL;
333
334         res = alloc_dive();
335
336         res->when = a->when;
337         res->location = merge_text(a->location, b->location);
338         res->notes = merge_text(a->notes, b->notes);
339         MERGE_MAX(res, a, b, number);
340         MERGE_MAX(res, a, b, maxdepth.mm);
341         res->meandepth.mm = 0;
342         MERGE_MAX(res, a, b, duration.seconds);
343         MERGE_MAX(res, a, b, surfacetime.seconds);
344         MERGE_MAX(res, a, b, airtemp.mkelvin);
345         MERGE_MIN(res, a, b, watertemp.mkelvin);
346         for (i = 0; i < MAX_CYLINDERS; i++)
347                 merge_cylinder_info(res->cylinder+i, a->cylinder + i, b->cylinder + i);
348
349         return merge_samples(res, a, b, 0);
350 }