X-Git-Url: http://git.tdb.fi/?a=blobdiff_plain;f=dive.c;h=cb94e7925ce01695659ac1c5de33da192e77ef30;hb=93d07f631aec470fb9f920f66ccb03ce699942b5;hp=5ddb6bc8087faae233843dc46683382934fe6792;hpb=aab4d593bdbffef8442282318778a9833cbc7a43;p=ext%2Fsubsurface.git diff --git a/dive.c b/dive.c index 5ddb6bc..cb94e79 100644 --- a/dive.c +++ b/dive.c @@ -1,8 +1,170 @@ +/* dive.c */ +/* maintains the internal dive list structure */ #include #include #include "dive.h" +void add_event(struct dive *dive, int time, int type, int flags, int value, const char *name) +{ + struct event *ev, **p; + unsigned int size, len = strlen(name); + + size = sizeof(*ev) + len + 1; + ev = malloc(size); + if (!ev) + return; + memset(ev, 0, size); + memcpy(ev->name, name, len); + ev->time.seconds = time; + ev->type = type; + ev->flags = flags; + ev->value = value; + ev->next = NULL; + + p = &dive->events; + while (*p) + p = &(*p)->next; + *p = ev; + remember_event(name); +} + +int get_pressure_units(unsigned int mb, const char **units) +{ + int pressure; + const char* unit; + + switch (output_units.pressure) { + case PASCAL: + pressure = mb * 100; + unit = "pascal"; + break; + case BAR: + pressure = (mb + 500) / 1000; + unit = "bar"; + break; + case PSI: + pressure = mbar_to_PSI(mb); + unit = "psi"; + break; + } + if (units) + *units = unit; + return pressure; +} + +double get_temp_units(unsigned int mk, const char **units) +{ + double deg; + const char *unit; + + if (output_units.temperature == FAHRENHEIT) { + deg = mkelvin_to_F(mk); + unit = UTF8_DEGREE "F"; + } else { + deg = mkelvin_to_C(mk); + unit = UTF8_DEGREE "C"; + } + if (units) + *units = unit; + return deg; +} + +double get_volume_units(unsigned int ml, int *frac, const char **units) +{ + int decimals; + double vol; + const char *unit; + + switch (output_units.volume) { + case LITER: + vol = ml / 1000.0; + unit = "l"; + decimals = 1; + break; + case CUFT: + vol = ml_to_cuft(ml); + unit = "cuft"; + decimals = 2; + break; + } + if (frac) + *frac = decimals; + if (units) + *units = unit; + return vol; +} + +double get_depth_units(unsigned int mm, int *frac, const char **units) +{ + int decimals; + double d; + const char *unit; + + switch (output_units.length) { + case METERS: + d = mm / 1000.0; + unit = "m"; + decimals = d < 20; + break; + case FEET: + d = mm_to_feet(mm); + unit = "ft"; + decimals = 0; + break; + } + if (frac) + *frac = decimals; + if (units) + *units = unit; + return d; +} + +struct dive *alloc_dive(void) +{ + const int initial_samples = 5; + unsigned int size; + struct dive *dive; + + size = dive_size(initial_samples); + dive = malloc(size); + if (!dive) + exit(1); + memset(dive, 0, size); + dive->alloc_samples = initial_samples; + return dive; +} + +struct sample *prepare_sample(struct dive **divep) +{ + struct dive *dive = *divep; + if (dive) { + int nr = dive->samples; + int alloc_samples = dive->alloc_samples; + struct sample *sample; + if (nr >= alloc_samples) { + unsigned int size; + + alloc_samples = (alloc_samples * 3)/2 + 10; + size = dive_size(alloc_samples); + dive = realloc(dive, size); + if (!dive) + return NULL; + dive->alloc_samples = alloc_samples; + *divep = dive; + } + sample = dive->sample + nr; + memset(sample, 0, sizeof(*sample)); + return sample; + } + return NULL; +} + +void finish_sample(struct dive *dive, struct sample *sample) +{ + dive->samples++; +} + /* * So when we re-calculate maxdepth and meandepth, we will * not override the old numbers if they are close to the @@ -21,10 +183,70 @@ */ static void update_depth(depth_t *depth, int new) { - int old = depth->mm; + if (new) { + int old = depth->mm; - if (abs(old - new) > 1000) - depth->mm = new; + if (abs(old - new) > 1000) + depth->mm = new; + } +} + +static void update_duration(duration_t *duration, int new) +{ + if (new) + duration->seconds = new; +} + +static void update_temperature(temperature_t *temperature, int new) +{ + if (new) { + int old = temperature->mkelvin; + + if (abs(old - new) > 1000) + temperature->mkelvin = new; + } +} + +/* + * If you have more than 32 cylinders, you'd better have a 64-bit build. + * And if you have more than 64 cylinders, you need to use another tool, + * or fix this up to do something odd. + */ +static unsigned long fixup_pressure(struct dive *dive, struct sample *sample, unsigned long flags) +{ + unsigned long mask; + unsigned int pressure, index; + cylinder_t *cyl; + + pressure = sample->cylinderpressure.mbar; + if (!pressure) + return flags; + index = sample->cylinderindex; + if (index >= MAX_CYLINDERS) + return flags; + cyl = dive->cylinder + index; + if (!cyl->start.mbar) + cyl->start.mbar = pressure; + /* + * If we already have an end pressure, without + * ever having seen a sample for this cylinder, + * that means that somebody set the end pressure + * by hand + */ + mask = 1ul << index; + if (cyl->end.mbar) { + if (!(flags & mask)) + return flags; + } + flags |= mask; + + /* we need to handle the user entering beginning and end tank pressures + * - maybe even IF we have samples. But for now if we have air pressure + * data in the samples, we use that instead of the minimum + * if (!cyl->end.mbar || pressure < cyl->end.mbar) + */ + cyl->end.mbar = pressure; + return flags; } struct dive *fixup_dive(struct dive *dive) @@ -33,17 +255,15 @@ struct dive *fixup_dive(struct dive *dive) double depthtime = 0; int lasttime = 0; int start = -1, end = -1; - int startpress = 0, endpress = 0; int maxdepth = 0, mintemp = 0; int lastdepth = 0; int lasttemp = 0; - temperature_t *redundant_temp = NULL; + unsigned long flags = 0; for (i = 0; i < dive->samples; i++) { struct sample *sample = dive->sample + i; int time = sample->time.seconds; int depth = sample->depth.mm; - int press = sample->cylinderpressure.mbar; int temp = sample->temperature.mkelvin; if (lastdepth) @@ -55,26 +275,19 @@ struct dive *fixup_dive(struct dive *dive) if (depth > maxdepth) maxdepth = depth; } - if (press) { - endpress = press; - if (!startpress) - startpress = press; - } + + flags = fixup_pressure(dive, sample, flags); + if (temp) { /* * If we have consecutive identical * temperature readings, throw away - * the redundant ones. We care about - * the "edges" only. + * the redundant ones. */ - if (lasttemp == temp) { - if (redundant_temp) - redundant_temp->mkelvin = 0; - redundant_temp = &sample->temperature; - } else { - redundant_temp = NULL; + if (lasttemp == temp) + sample->temperature.mkelvin = 0; + else lasttemp = temp; - } if (!mintemp || temp < mintemp) mintemp = temp; @@ -85,46 +298,41 @@ struct dive *fixup_dive(struct dive *dive) } if (end < 0) return dive; - dive->duration.seconds = end - start; - if (start != end) - update_depth(&dive->meandepth, depthtime / (end - start)); - if (startpress) - dive->beginning_pressure.mbar = startpress; - if (endpress) - dive->end_pressure.mbar = endpress; - if (mintemp) - dive->watertemp.mkelvin = mintemp; - if (maxdepth) - update_depth(&dive->maxdepth, maxdepth); + update_duration(&dive->duration, end - start); + if (start != end) + depthtime /= (end - start); + + update_depth(&dive->meandepth, depthtime); + update_temperature(&dive->watertemp, mintemp); + update_depth(&dive->maxdepth, maxdepth); + + add_people(dive->buddy); + add_people(dive->divemaster); + add_location(dive->location); + for (i = 0; i < MAX_CYLINDERS; i++) { + cylinder_type_t *type = &dive->cylinder[i].type; + add_cylinder_description(type); + } return dive; } /* Don't pick a zero for MERGE_MIN() */ -#define MIN(a,b) ((a)<(b)?(a):(b)) -#define MAX(a,b) ((a)>(b)?(a):(b)) #define MERGE_MAX(res, a, b, n) res->n = MAX(a->n, b->n) #define MERGE_MIN(res, a, b, n) res->n = (a->n)?(b->n)?MIN(a->n, b->n):(a->n):(b->n) - -static int alloc_samples; +#define MERGE_TXT(res, a, b, n) res->n = merge_text(a->n, b->n) +#define MERGE_NONZERO(res, a, b, n) res->n = a->n ? a->n : b->n static struct dive *add_sample(struct sample *sample, int time, struct dive *dive) { - int nr = dive->samples; - struct sample *d; + struct sample *p = prepare_sample(&dive); - if (nr >= alloc_samples) { - alloc_samples = (alloc_samples + 64) * 3 / 2; - dive = realloc(dive, dive_size(alloc_samples)); - if (!dive) - return NULL; - } - dive->samples = nr+1; - d = dive->sample + nr; - - *d = *sample; - d->time.seconds = time; + if (!p) + return NULL; + *p = *sample; + p->time.seconds = time; + finish_sample(dive, p); return dive; } @@ -212,6 +420,59 @@ static char *merge_text(const char *a, const char *b) return res; } +#define SORT(a,b,field) \ + if (a->field != b->field) return a->field < b->field ? -1 : 1 + +static int sort_event(struct event *a, struct event *b) +{ + SORT(a,b,time.seconds); + SORT(a,b,type); + SORT(a,b,flags); + SORT(a,b,value); + return strcmp(a->name, b->name); +} + +static void merge_events(struct dive *res, struct dive *src1, struct dive *src2, int offset) +{ + struct event *a, *b; + struct event **p = &res->events; + + a = src1->events; + b = src2->events; + while (b) { + b->time.seconds += offset; + b = b->next; + } + b = src2->events; + + while (a || b) { + int s; + if (!b) { + *p = a; + break; + } + if (!a) { + *p = b; + break; + } + s = sort_event(a, b); + /* Pick b */ + if (s > 0) { + *p = b; + p = &b->next; + b = b->next; + continue; + } + /* Pick 'a' or neither */ + if (s < 0) { + *p = a; + p = &a->next; + } + a = a->next; + continue; + } +} + /* Pick whichever has any info (if either). Prefer 'a' */ static void merge_cylinder_type(cylinder_type_t *res, cylinder_type_t *a, cylinder_type_t *b) { @@ -220,7 +481,7 @@ static void merge_cylinder_type(cylinder_type_t *res, cylinder_type_t *a, cylind *res = *b; } -static void merge_cylinder_mix(gasmix_t *res, gasmix_t *a, gasmix_t *b) +static void merge_cylinder_mix(struct gasmix *res, struct gasmix *a, struct gasmix *b) { if (a->o2.permille) b = a; @@ -231,6 +492,8 @@ static void merge_cylinder_info(cylinder_t *res, cylinder_t *a, cylinder_t *b) { merge_cylinder_type(&res->type, &a->type, &b->type); merge_cylinder_mix(&res->gasmix, &a->gasmix, &b->gasmix); + MERGE_MAX(res, a, b, start.mbar); + MERGE_MIN(res, a, b, end.mbar); } /* @@ -243,28 +506,27 @@ struct dive *try_to_merge(struct dive *a, struct dive *b) int i; struct dive *res; - if (a->when != b->when) + if ((a->when >= b->when + 60) || (a->when <= b->when - 60)) return NULL; - alloc_samples = 5; - res = malloc(dive_size(alloc_samples)); - if (!res) - return NULL; - memset(res, 0, dive_size(alloc_samples)); + res = alloc_dive(); res->when = a->when; - res->location = merge_text(a->location, b->location); - res->notes = merge_text(a->notes, b->notes); + MERGE_NONZERO(res, a, b, latitude); + MERGE_NONZERO(res, a, b, longitude); + MERGE_TXT(res, a, b, location); + MERGE_TXT(res, a, b, notes); + MERGE_TXT(res, a, b, buddy); + MERGE_TXT(res, a, b, divemaster); + MERGE_MAX(res, a, b, number); MERGE_MAX(res, a, b, maxdepth.mm); res->meandepth.mm = 0; MERGE_MAX(res, a, b, duration.seconds); MERGE_MAX(res, a, b, surfacetime.seconds); MERGE_MAX(res, a, b, airtemp.mkelvin); MERGE_MIN(res, a, b, watertemp.mkelvin); - MERGE_MAX(res, a, b, beginning_pressure.mbar); - MERGE_MAX(res, a, b, end_pressure.mbar); for (i = 0; i < MAX_CYLINDERS; i++) merge_cylinder_info(res->cylinder+i, a->cylinder + i, b->cylinder + i); - + merge_events(res, a, b, 0); return merge_samples(res, a, b, 0); }