]> git.tdb.fi Git - ext/subsurface.git/blob - dive.c
Remove redundant temperature readings
[ext/subsurface.git] / dive.c
1 #include <string.h>
2 #include <stdio.h>
3
4 #include "dive.h"
5
6 /*
7  * So when we re-calculate maxdepth and meandepth, we will
8  * not override the old numbers if they are close to the
9  * new ones.
10  *
11  * Why? Because a dive computer may well actually track the
12  * max depth and mean depth at finer granularity than the
13  * samples it stores. So it's possible that the max and mean
14  * have been reported more correctly originally.
15  *
16  * Only if the values calculated from the samples are clearly
17  * different do we override the normal depth values.
18  *
19  * This considers 1m to be "clearly different". That's
20  * a totally random number.
21  */
22 static void update_depth(depth_t *depth, int new)
23 {
24         int old = depth->mm;
25
26         if (abs(old - new) > 1000)
27                 depth->mm = new;
28 }
29
30 struct dive *fixup_dive(struct dive *dive)
31 {
32         int i;
33         double depthtime = 0;
34         int lasttime = 0;
35         int start = -1, end = -1;
36         int startpress = 0, endpress = 0;
37         int starttemp = 0, endtemp = 0;
38         int maxdepth = 0, mintemp = 0;
39         int lastdepth = 0;
40         int lasttemp = 0;
41         temperature_t *redundant_temp = NULL;
42
43         for (i = 0; i < dive->samples; i++) {
44                 struct sample *sample = dive->sample + i;
45                 int time = sample->time.seconds;
46                 int depth = sample->depth.mm;
47                 int press = sample->cylinderpressure.mbar;
48                 int temp = sample->temperature.mkelvin;
49
50                 if (lastdepth)
51                         end = time;
52
53                 if (depth) {
54                         if (start < 0)
55                                 start = lasttime;
56                         if (depth > maxdepth)
57                                 maxdepth = depth;
58                 }
59                 if (press) {
60                         endpress = press;
61                         if (!startpress)
62                                 startpress = press;
63                 }
64                 if (temp) {
65                         /*
66                          * If we have consecutive identical
67                          * temperature readings, throw away
68                          * the redundant ones. We care about
69                          * the "edges" only.
70                          */
71                         if (lasttemp == temp) {
72                                 if (redundant_temp)
73                                         redundant_temp->mkelvin = 0;
74                                 redundant_temp = &sample->temperature;
75                         } else {
76                                 redundant_temp = NULL;
77                                 lasttemp = temp;
78                         }
79
80                         endtemp = temp;
81                         if (!starttemp)
82                                 starttemp = temp;
83                         if (!mintemp || temp < mintemp)
84                                 mintemp = temp;
85                 }
86                 depthtime += (time - lasttime) * (lastdepth + depth) / 2;
87                 lastdepth = depth;
88                 lasttime = time;
89         }
90         if (end < 0)
91                 return dive;
92         dive->duration.seconds = end - start;
93         if (start != end)
94                 update_depth(&dive->meandepth, depthtime / (end - start));
95         if (startpress)
96                 dive->beginning_pressure.mbar = startpress;
97         if (endpress)
98                 dive->end_pressure.mbar = endpress;
99         if (mintemp)
100                 dive->watertemp.mkelvin = mintemp;
101
102         if (maxdepth)
103                 update_depth(&dive->maxdepth, maxdepth);
104
105         return dive;
106 }
107
108 /* Don't pick a zero for MERGE_MIN() */
109 #define MIN(a,b) ((a)<(b)?(a):(b))
110 #define MAX(a,b) ((a)>(b)?(a):(b))
111 #define MERGE_MAX(res, a, b, n) res->n = MAX(a->n, b->n)
112 #define MERGE_MIN(res, a, b, n) res->n = (a->n)?(b->n)?MIN(a->n, b->n):(a->n):(b->n)
113
114 static int alloc_samples;
115
116 static struct dive *add_sample(struct sample *sample, int time, struct dive *dive)
117 {
118         int nr = dive->samples;
119         struct sample *d;
120
121         if (nr >= alloc_samples) {
122                 alloc_samples = (alloc_samples + 64) * 3 / 2;
123                 dive = realloc(dive, dive_size(alloc_samples));
124                 if (!dive)
125                         return NULL;
126         }
127         dive->samples = nr+1;
128         d = dive->sample + nr;
129
130         *d = *sample;
131         d->time.seconds = time;
132         return dive;
133 }
134
135 /*
136  * Merge samples. Dive 'a' is "offset" seconds before Dive 'b'
137  */
138 static struct dive *merge_samples(struct dive *res, struct dive *a, struct dive *b, int offset)
139 {
140         int asamples = a->samples;
141         int bsamples = b->samples;
142         struct sample *as = a->sample;
143         struct sample *bs = b->sample;
144
145         for (;;) {
146                 int at, bt;
147                 struct sample sample;
148
149                 if (!res)
150                         return NULL;
151
152                 at = asamples ? as->time.seconds : -1;
153                 bt = bsamples ? bs->time.seconds + offset : -1;
154
155                 /* No samples? All done! */
156                 if (at < 0 && bt < 0)
157                         return fixup_dive(res);
158
159                 /* Only samples from a? */
160                 if (bt < 0) {
161 add_sample_a:
162                         res = add_sample(as, at, res);
163                         as++;
164                         asamples--;
165                         continue;
166                 }
167
168                 /* Only samples from b? */
169                 if (at < 0) {
170 add_sample_b:
171                         res = add_sample(bs, bt, res);
172                         bs++;
173                         bsamples--;
174                         continue;
175                 }
176
177                 if (at < bt)
178                         goto add_sample_a;
179                 if (at > bt)
180                         goto add_sample_b;
181
182                 /* same-time sample: add a merged sample. Take the non-zero ones */
183                 sample = *bs;
184                 if (as->depth.mm)
185                         sample.depth = as->depth;
186                 if (as->temperature.mkelvin)
187                         sample.temperature = as->temperature;
188                 if (as->cylinderpressure.mbar)
189                         sample.cylinderpressure = as->cylinderpressure;
190                 if (as->cylinderindex)
191                         sample.cylinderindex = as->cylinderindex;
192
193                 res = add_sample(&sample, at, res);
194
195                 as++;
196                 bs++;
197                 asamples--;
198                 bsamples--;
199         }
200 }
201
202 static char *merge_text(const char *a, const char *b)
203 {
204         char *res;
205
206         if (!a || !*a)
207                 return (char *)b;
208         if (!b || !*b)
209                 return (char *)a;
210         if (!strcmp(a,b))
211                 return (char *)a;
212         res = malloc(strlen(a) + strlen(b) + 9);
213         if (!res)
214                 return (char *)a;
215         sprintf(res, "(%s) or (%s)", a, b);
216         return res;
217 }
218
219 /* Pick whichever has any info (if either). Prefer 'a' */
220 static void merge_cylinder_type(cylinder_type_t *res, cylinder_type_t *a, cylinder_type_t *b)
221 {
222         if (a->size.mliter)
223                 b = a;
224         *res = *b;
225 }
226
227 static void merge_cylinder_mix(gasmix_t *res, gasmix_t *a, gasmix_t *b)
228 {
229         if (a->o2.permille)
230                 b = a;
231         *res = *b;
232 }
233
234 static void merge_cylinder_info(cylinder_t *res, cylinder_t *a, cylinder_t *b)
235 {
236         merge_cylinder_type(&res->type, &a->type, &b->type);
237         merge_cylinder_mix(&res->gasmix, &a->gasmix, &b->gasmix);
238 }
239
240 /*
241  * This could do a lot more merging. Right now it really only
242  * merges almost exact duplicates - something that happens easily
243  * with overlapping dive downloads.
244  */
245 struct dive *try_to_merge(struct dive *a, struct dive *b)
246 {
247         int i;
248         struct dive *res;
249
250         if (a->when != b->when)
251                 return NULL;
252
253         alloc_samples = 5;
254         res = malloc(dive_size(alloc_samples));
255         if (!res)
256                 return NULL;
257         memset(res, 0, dive_size(alloc_samples));
258
259         res->when = a->when;
260         res->name = merge_text(a->name, b->name);
261         res->location = merge_text(a->location, b->location);
262         res->notes = merge_text(a->notes, b->notes);
263         MERGE_MAX(res, a, b, maxdepth.mm);
264         res->meandepth.mm = 0;
265         MERGE_MAX(res, a, b, duration.seconds);
266         MERGE_MAX(res, a, b, surfacetime.seconds);
267         MERGE_MAX(res, a, b, airtemp.mkelvin);
268         MERGE_MIN(res, a, b, watertemp.mkelvin);
269         MERGE_MAX(res, a, b, beginning_pressure.mbar);
270         MERGE_MAX(res, a, b, end_pressure.mbar);
271         for (i = 0; i < MAX_CYLINDERS; i++)
272                 merge_cylinder_info(res->cylinder+i, a->cylinder + i, b->cylinder + i);
273
274         return merge_samples(res, a, b, 0);
275 }