mirror of
https://github.com/subsurface/subsurface.git
synced 2024-11-30 22:20:21 +00:00
Merge branch 'add-info-stats-page' of git://github.com/dirkhh/subsurface
* 'add-info-stats-page' of git://github.com/dirkhh/subsurface: Add Info & Stats page to the notebook Even more places with pressure and volume conversions Further cleanup of pressure and volume conversions Use unit functions to get column headers, add unit function for pressure More consistency improvements Add new helper function to get temperature and unit
This commit is contained in:
commit
55352a051c
12 changed files with 429 additions and 88 deletions
5
Makefile
5
Makefile
|
@ -81,7 +81,7 @@ LIBS = $(LIBXML2) $(LIBGTK) $(LIBDIVECOMPUTER) -lpthread
|
||||||
|
|
||||||
OBJS = main.o dive.o profile.o info.o equipment.o divelist.o \
|
OBJS = main.o dive.o profile.o info.o equipment.o divelist.o \
|
||||||
parse-xml.o save-xml.o libdivecomputer.o print.o uemis.o \
|
parse-xml.o save-xml.o libdivecomputer.o print.o uemis.o \
|
||||||
gtk-gui.o $(RESFILE)
|
gtk-gui.o statistics.o $(RESFILE)
|
||||||
|
|
||||||
$(NAME): $(OBJS)
|
$(NAME): $(OBJS)
|
||||||
$(CC) $(LDFLAGS) -o $(NAME) $(OBJS) $(LIBS)
|
$(CC) $(LDFLAGS) -o $(NAME) $(OBJS) $(LIBS)
|
||||||
|
@ -136,6 +136,9 @@ info.o: info.c dive.h display.h display-gtk.h divelist.h
|
||||||
equipment.o: equipment.c dive.h display.h divelist.h
|
equipment.o: equipment.c dive.h display.h divelist.h
|
||||||
$(CC) $(CFLAGS) $(GTK2CFLAGS) $(GLIB2CFLAGS) -c equipment.c
|
$(CC) $(CFLAGS) $(GTK2CFLAGS) $(GLIB2CFLAGS) -c equipment.c
|
||||||
|
|
||||||
|
statistics.o: statistics.c dive.h display.h divelist.h
|
||||||
|
$(CC) $(CFLAGS) $(GTK2CFLAGS) $(GLIB2CFLAGS) -c statistics.c
|
||||||
|
|
||||||
divelist.o: divelist.c dive.h display.h divelist.h
|
divelist.o: divelist.c dive.h display.h divelist.h
|
||||||
$(CC) $(CFLAGS) $(GTK2CFLAGS) $(GLIB2CFLAGS) -c divelist.c
|
$(CC) $(CFLAGS) $(GTK2CFLAGS) $(GLIB2CFLAGS) -c divelist.c
|
||||||
|
|
||||||
|
|
|
@ -33,6 +33,7 @@ extern GtkWidget *dive_profile_widget(void);
|
||||||
extern GtkWidget *dive_info_frame(void);
|
extern GtkWidget *dive_info_frame(void);
|
||||||
extern GtkWidget *extended_dive_info_widget(void);
|
extern GtkWidget *extended_dive_info_widget(void);
|
||||||
extern GtkWidget *equipment_widget(void);
|
extern GtkWidget *equipment_widget(void);
|
||||||
|
extern GtkWidget *stats_widget(void);
|
||||||
|
|
||||||
extern GtkWidget *dive_list_create(void);
|
extern GtkWidget *dive_list_create(void);
|
||||||
|
|
||||||
|
|
66
dive.c
66
dive.c
|
@ -29,6 +29,72 @@ void add_event(struct dive *dive, int time, int type, int flags, int value, cons
|
||||||
remember_event(name);
|
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)
|
double get_depth_units(unsigned int mm, int *frac, const char **units)
|
||||||
{
|
{
|
||||||
int decimals;
|
int decimals;
|
||||||
|
|
32
dive.h
32
dive.h
|
@ -86,7 +86,20 @@ typedef struct {
|
||||||
pressure_t start, end;
|
pressure_t start, end;
|
||||||
} cylinder_t;
|
} cylinder_t;
|
||||||
|
|
||||||
|
extern int get_pressure_units(unsigned int mb, const char **units);
|
||||||
extern double get_depth_units(unsigned int mm, int *frac, const char **units);
|
extern double get_depth_units(unsigned int mm, int *frac, const char **units);
|
||||||
|
extern double get_volume_units(unsigned int mm, int *frac, const char **units);
|
||||||
|
extern double get_temp_units(unsigned int mm, const char **units);
|
||||||
|
|
||||||
|
static inline double ml_to_cuft(int ml)
|
||||||
|
{
|
||||||
|
return ml / 28316.8466;
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline double cuft_to_l(double cuft)
|
||||||
|
{
|
||||||
|
return cuft * 28.3168466;
|
||||||
|
}
|
||||||
|
|
||||||
static inline double mm_to_feet(int mm)
|
static inline double mm_to_feet(int mm)
|
||||||
{
|
{
|
||||||
|
@ -129,16 +142,31 @@ static inline int to_K(temperature_t temp)
|
||||||
return (temp.mkelvin + 499)/1000;
|
return (temp.mkelvin + 499)/1000;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static inline double psi_to_bar(double psi)
|
||||||
|
{
|
||||||
|
return psi / 14.5037738;
|
||||||
|
}
|
||||||
static inline int to_PSI(pressure_t pressure)
|
static inline int to_PSI(pressure_t pressure)
|
||||||
{
|
{
|
||||||
return pressure.mbar * 0.0145037738 + 0.5;
|
return pressure.mbar * 0.0145037738 + 0.5;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static inline double bar_to_atm(double bar)
|
||||||
|
{
|
||||||
|
return bar / 1.01325;
|
||||||
|
}
|
||||||
|
|
||||||
static inline double to_ATM(pressure_t pressure)
|
static inline double to_ATM(pressure_t pressure)
|
||||||
{
|
{
|
||||||
return pressure.mbar / 1013.25;
|
return pressure.mbar / 1013.25;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static inline int mbar_to_PSI(int mbar)
|
||||||
|
{
|
||||||
|
pressure_t p = {mbar};
|
||||||
|
return to_PSI(p);
|
||||||
|
}
|
||||||
|
|
||||||
struct sample {
|
struct sample {
|
||||||
duration_t time;
|
duration_t time;
|
||||||
depth_t depth;
|
depth_t depth;
|
||||||
|
@ -176,7 +204,7 @@ struct dive {
|
||||||
depth_t visibility;
|
depth_t visibility;
|
||||||
temperature_t airtemp, watertemp;
|
temperature_t airtemp, watertemp;
|
||||||
cylinder_t cylinder[MAX_CYLINDERS];
|
cylinder_t cylinder[MAX_CYLINDERS];
|
||||||
int otu;
|
int sac, otu;
|
||||||
struct event *events;
|
struct event *events;
|
||||||
int samples, alloc_samples;
|
int samples, alloc_samples;
|
||||||
struct sample sample[];
|
struct sample sample[];
|
||||||
|
@ -231,6 +259,8 @@ extern void flush_dive_info_changes(struct dive *);
|
||||||
extern void show_dive_equipment(struct dive *);
|
extern void show_dive_equipment(struct dive *);
|
||||||
extern void flush_dive_equipment_changes(struct dive *);
|
extern void flush_dive_equipment_changes(struct dive *);
|
||||||
|
|
||||||
|
extern void show_dive_stats(struct dive *);
|
||||||
|
|
||||||
extern void update_dive(struct dive *new_dive);
|
extern void update_dive(struct dive *new_dive);
|
||||||
extern void save_dives(const char *filename);
|
extern void save_dives(const char *filename);
|
||||||
|
|
||||||
|
|
40
divelist.c
40
divelist.c
|
@ -223,7 +223,6 @@ static void sac_data_func(GtkTreeViewColumn *col,
|
||||||
gpointer data)
|
gpointer data)
|
||||||
{
|
{
|
||||||
int value;
|
int value;
|
||||||
const double liters_per_cuft = 28.317;
|
|
||||||
const char *fmt;
|
const char *fmt;
|
||||||
char buffer[16];
|
char buffer[16];
|
||||||
double sac;
|
double sac;
|
||||||
|
@ -242,7 +241,7 @@ static void sac_data_func(GtkTreeViewColumn *col,
|
||||||
break;
|
break;
|
||||||
case CUFT:
|
case CUFT:
|
||||||
fmt = "%4.2f";
|
fmt = "%4.2f";
|
||||||
sac /= liters_per_cuft;
|
sac = ml_to_cuft(sac * 1000);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
snprintf(buffer, sizeof(buffer), fmt, sac);
|
snprintf(buffer, sizeof(buffer), fmt, sac);
|
||||||
|
@ -307,7 +306,7 @@ static double calculate_airuse(struct dive *dive)
|
||||||
if (!size)
|
if (!size)
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
kilo_atm = (cyl->start.mbar - cyl->end.mbar) / 1013250.0;
|
kilo_atm = (to_ATM(cyl->start) - to_ATM(cyl->end)) / 1000.0;
|
||||||
|
|
||||||
/* Liters of air at 1 atm == milliliters at 1k atm*/
|
/* Liters of air at 1 atm == milliliters at 1k atm*/
|
||||||
airuse += kilo_atm * size;
|
airuse += kilo_atm * size;
|
||||||
|
@ -315,23 +314,22 @@ static double calculate_airuse(struct dive *dive)
|
||||||
return airuse;
|
return airuse;
|
||||||
}
|
}
|
||||||
|
|
||||||
static void get_sac(struct dive *dive, int *val)
|
static int calculate_sac(struct dive *dive)
|
||||||
{
|
{
|
||||||
double airuse, pressure, sac;
|
double airuse, pressure, sac;
|
||||||
|
|
||||||
*val = 0;
|
|
||||||
airuse = calculate_airuse(dive);
|
airuse = calculate_airuse(dive);
|
||||||
if (!airuse)
|
if (!airuse)
|
||||||
return;
|
return 0;
|
||||||
if (!dive->duration.seconds)
|
if (!dive->duration.seconds)
|
||||||
return;
|
return 0;
|
||||||
|
|
||||||
/* Mean pressure in atm: 1 atm per 10m */
|
/* Mean pressure in atm: 1 atm per 10m */
|
||||||
pressure = 1 + (dive->meandepth.mm / 10000.0);
|
pressure = 1 + (dive->meandepth.mm / 10000.0);
|
||||||
sac = airuse / pressure * 60 / dive->duration.seconds;
|
sac = airuse / pressure * 60 / dive->duration.seconds;
|
||||||
|
|
||||||
/* milliliters per minute.. */
|
/* milliliters per minute.. */
|
||||||
*val = sac * 1000;
|
return sac * 1000;
|
||||||
}
|
}
|
||||||
|
|
||||||
static void get_string(char **str, const char *s)
|
static void get_string(char **str, const char *s)
|
||||||
|
@ -364,12 +362,10 @@ static void fill_one_dive(struct dive *dive,
|
||||||
GtkTreeModel *model,
|
GtkTreeModel *model,
|
||||||
GtkTreeIter *iter)
|
GtkTreeIter *iter)
|
||||||
{
|
{
|
||||||
int sac;
|
|
||||||
char *location, *cylinder;
|
char *location, *cylinder;
|
||||||
|
|
||||||
get_cylinder(dive, &cylinder);
|
get_cylinder(dive, &cylinder);
|
||||||
get_location(dive, &location);
|
get_location(dive, &location);
|
||||||
get_sac(dive, &sac);
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* We only set the fields that changed: the strings.
|
* We only set the fields that changed: the strings.
|
||||||
|
@ -379,7 +375,7 @@ static void fill_one_dive(struct dive *dive,
|
||||||
DIVE_NR, dive->number,
|
DIVE_NR, dive->number,
|
||||||
DIVE_LOCATION, location,
|
DIVE_LOCATION, location,
|
||||||
DIVE_CYLINDER, cylinder,
|
DIVE_CYLINDER, cylinder,
|
||||||
DIVE_SAC, sac,
|
DIVE_SAC, dive->sac,
|
||||||
DIVE_OTU, dive->otu,
|
DIVE_OTU, dive->otu,
|
||||||
-1);
|
-1);
|
||||||
}
|
}
|
||||||
|
@ -423,27 +419,10 @@ void update_dive_list_units(void)
|
||||||
const char *unit;
|
const char *unit;
|
||||||
GtkTreeModel *model = GTK_TREE_MODEL(dive_list.model);
|
GtkTreeModel *model = GTK_TREE_MODEL(dive_list.model);
|
||||||
|
|
||||||
switch (output_units.length) {
|
(void) get_depth_units(0, NULL, &unit);
|
||||||
case METERS:
|
|
||||||
unit = "m";
|
|
||||||
break;
|
|
||||||
case FEET:
|
|
||||||
unit = "ft";
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
gtk_tree_view_column_set_title(dive_list.depth, unit);
|
gtk_tree_view_column_set_title(dive_list.depth, unit);
|
||||||
|
|
||||||
switch (output_units.temperature) {
|
(void) get_temp_units(0, &unit);
|
||||||
case CELSIUS:
|
|
||||||
unit = UTF8_DEGREE "C";
|
|
||||||
break;
|
|
||||||
case FAHRENHEIT:
|
|
||||||
unit = UTF8_DEGREE "F";
|
|
||||||
break;
|
|
||||||
case KELVIN:
|
|
||||||
unit = "Kelvin";
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
gtk_tree_view_column_set_title(dive_list.temperature, unit);
|
gtk_tree_view_column_set_title(dive_list.temperature, unit);
|
||||||
|
|
||||||
gtk_tree_model_foreach(model, set_one_dive, NULL);
|
gtk_tree_model_foreach(model, set_one_dive, NULL);
|
||||||
|
@ -471,6 +450,7 @@ static void fill_dive_list(void)
|
||||||
struct dive *dive = dive_table.dives[i];
|
struct dive *dive = dive_table.dives[i];
|
||||||
|
|
||||||
dive->otu = calculate_otu(dive);
|
dive->otu = calculate_otu(dive);
|
||||||
|
dive->sac = calculate_sac(dive);
|
||||||
gtk_list_store_append(store, &iter);
|
gtk_list_store_append(store, &iter);
|
||||||
gtk_list_store_set(store, &iter,
|
gtk_list_store_set(store, &iter,
|
||||||
DIVE_INDEX, i,
|
DIVE_INDEX, i,
|
||||||
|
|
44
equipment.c
44
equipment.c
|
@ -49,18 +49,19 @@ struct cylinder_widget {
|
||||||
GtkWidget *o2, *gasmix_button;
|
GtkWidget *o2, *gasmix_button;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/* we want bar - so let's not use our unit functions */
|
||||||
static int convert_pressure(int mbar, double *p)
|
static int convert_pressure(int mbar, double *p)
|
||||||
{
|
{
|
||||||
int decimals = 1;
|
int decimals = 1;
|
||||||
double pressure;
|
double pressure;
|
||||||
|
|
||||||
pressure = mbar / 1000.0;
|
if (output_units.pressure == PSI) {
|
||||||
if (mbar) {
|
pressure = mbar_to_PSI(mbar);
|
||||||
if (output_units.pressure == PSI) {
|
decimals = 0;
|
||||||
pressure *= 14.5037738; /* Bar to PSI */
|
} else {
|
||||||
decimals = 0;
|
pressure = mbar / 1000.0;
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
*p = pressure;
|
*p = pressure;
|
||||||
return decimals;
|
return decimals;
|
||||||
}
|
}
|
||||||
|
@ -70,17 +71,18 @@ static int convert_volume_pressure(int ml, int mbar, double *v, double *p)
|
||||||
int decimals = 1;
|
int decimals = 1;
|
||||||
double volume, pressure;
|
double volume, pressure;
|
||||||
|
|
||||||
volume = ml / 1000.0;
|
|
||||||
pressure = mbar / 1000.0;
|
|
||||||
if (mbar) {
|
if (mbar) {
|
||||||
if (output_units.volume == CUFT) {
|
if (output_units.volume == CUFT) {
|
||||||
volume /= 28.3168466; /* Liters to cuft */
|
volume = ml_to_cuft(ml);
|
||||||
volume *= pressure / 1.01325;
|
volume *= bar_to_atm(mbar / 1000.0);
|
||||||
}
|
} else
|
||||||
|
volume = ml / 1000.0;
|
||||||
|
|
||||||
if (output_units.pressure == PSI) {
|
if (output_units.pressure == PSI) {
|
||||||
pressure *= 14.5037738; /* Bar to PSI */
|
pressure = mbar_to_PSI(mbar);
|
||||||
decimals = 0;
|
decimals = 0;
|
||||||
}
|
} else
|
||||||
|
pressure = mbar / 1000.0;
|
||||||
}
|
}
|
||||||
*v = volume;
|
*v = volume;
|
||||||
*p = pressure;
|
*p = pressure;
|
||||||
|
@ -352,14 +354,14 @@ static void fill_cylinder_info(struct cylinder_widget *cylinder, cylinder_t *cyl
|
||||||
int mbar, ml;
|
int mbar, ml;
|
||||||
|
|
||||||
if (output_units.pressure == PSI) {
|
if (output_units.pressure == PSI) {
|
||||||
pressure /= 14.5037738;
|
pressure = psi_to_bar(pressure);
|
||||||
start /= 14.5037738;
|
start = psi_to_bar(start);
|
||||||
end /= 14.5037738;
|
end = psi_to_bar(end);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (pressure && output_units.volume == CUFT) {
|
if (pressure && output_units.volume == CUFT) {
|
||||||
volume *= 28.3168466; /* CUFT to liter */
|
volume = cuft_to_l(volume);
|
||||||
volume /= pressure / 1.01325;
|
volume /= bar_to_atm(pressure);
|
||||||
}
|
}
|
||||||
|
|
||||||
ml = volume * 1000 + 0.5;
|
ml = volume * 1000 + 0.5;
|
||||||
|
@ -461,9 +463,9 @@ static void fill_tank_list(GtkListStore *store)
|
||||||
|
|
||||||
/* Is it in cuft and psi? */
|
/* Is it in cuft and psi? */
|
||||||
if (psi) {
|
if (psi) {
|
||||||
double bar = 0.0689475729 * psi;
|
double bar = psi_to_bar(psi);
|
||||||
double airvolume = 28316.8466 * size;
|
double airvolume = cuft_to_l(size) * 1000.0;
|
||||||
double atm = bar / 1.01325;
|
double atm = bar_to_atm(bar);
|
||||||
|
|
||||||
ml = airvolume / atm + 0.5;
|
ml = airvolume / atm + 0.5;
|
||||||
mbar = bar*1000 + 0.5;
|
mbar = bar*1000 + 0.5;
|
||||||
|
|
|
@ -746,6 +746,7 @@ void init_ui(int *argcp, char ***argvp)
|
||||||
GtkWidget *dive_info;
|
GtkWidget *dive_info;
|
||||||
GtkWidget *dive_list;
|
GtkWidget *dive_list;
|
||||||
GtkWidget *equipment;
|
GtkWidget *equipment;
|
||||||
|
GtkWidget *stats;
|
||||||
GtkWidget *menubar;
|
GtkWidget *menubar;
|
||||||
GtkWidget *vbox;
|
GtkWidget *vbox;
|
||||||
GdkScreen *screen;
|
GdkScreen *screen;
|
||||||
|
@ -875,6 +876,10 @@ void init_ui(int *argcp, char ***argvp)
|
||||||
equipment = equipment_widget();
|
equipment = equipment_widget();
|
||||||
gtk_notebook_append_page(GTK_NOTEBOOK(notebook), equipment, gtk_label_new("Equipment"));
|
gtk_notebook_append_page(GTK_NOTEBOOK(notebook), equipment, gtk_label_new("Equipment"));
|
||||||
|
|
||||||
|
/* Frame for dive statistics */
|
||||||
|
stats = stats_widget();
|
||||||
|
gtk_notebook_append_page(GTK_NOTEBOOK(notebook), stats, gtk_label_new("Info & Stats"));
|
||||||
|
|
||||||
gtk_widget_set_app_paintable(win, TRUE);
|
gtk_widget_set_app_paintable(win, TRUE);
|
||||||
gtk_widget_show_all(win);
|
gtk_widget_show_all(win);
|
||||||
|
|
||||||
|
|
1
main.c
1
main.c
|
@ -193,6 +193,7 @@ void update_dive(struct dive *new_dive)
|
||||||
if (new_dive) {
|
if (new_dive) {
|
||||||
show_dive_info(new_dive);
|
show_dive_info(new_dive);
|
||||||
show_dive_equipment(new_dive);
|
show_dive_equipment(new_dive);
|
||||||
|
show_dive_stats(new_dive);
|
||||||
}
|
}
|
||||||
buffered_dive = new_dive;
|
buffered_dive = new_dive;
|
||||||
}
|
}
|
||||||
|
|
|
@ -425,7 +425,7 @@ static void water_pressure(char *buffer, void *_depth)
|
||||||
if (!val.fp)
|
if (!val.fp)
|
||||||
break;
|
break;
|
||||||
/* cbar to atm */
|
/* cbar to atm */
|
||||||
atm = (val.fp / 100) / 1.01325;
|
atm = bar_to_atm(val.fp * 10);
|
||||||
/*
|
/*
|
||||||
* atm to cm. Why not mm? The precision just isn't
|
* atm to cm. Why not mm? The precision just isn't
|
||||||
* there.
|
* there.
|
||||||
|
@ -1122,9 +1122,9 @@ static void match_standard_cylinder(cylinder_type_t *type)
|
||||||
if (type->description)
|
if (type->description)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
cuft = type->size.mliter / 28317.0;
|
cuft = ml_to_cuft(type->size.mliter);
|
||||||
cuft *= to_ATM(type->workingpressure);
|
cuft *= to_ATM(type->workingpressure);
|
||||||
psi = type->workingpressure.mbar / 68.95;
|
psi = to_PSI(type->workingpressure);
|
||||||
|
|
||||||
switch (psi) {
|
switch (psi) {
|
||||||
case 2300 ... 2500: /* 2400 psi: LP tank */
|
case 2300 ... 2500: /* 2400 psi: LP tank */
|
||||||
|
@ -1177,7 +1177,8 @@ static void sanitize_cylinder_type(cylinder_type_t *type)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
if (input_units.volume == CUFT || import_source == SUUNTO) {
|
if (input_units.volume == CUFT || import_source == SUUNTO) {
|
||||||
volume_of_air = type->size.mliter * 28.317; /* milli-cu ft to milliliter */
|
/* confusing - we don't really start from ml but millicuft !*/
|
||||||
|
volume_of_air = cuft_to_l(type->size.mliter);
|
||||||
atm = to_ATM(type->workingpressure); /* working pressure in atm */
|
atm = to_ATM(type->workingpressure); /* working pressure in atm */
|
||||||
volume = volume_of_air / atm; /* milliliters at 1 atm: "true size" */
|
volume = volume_of_air / atm; /* milliliters at 1 atm: "true size" */
|
||||||
type->size.mliter = volume + 0.5;
|
type->size.mliter = volume + 0.5;
|
||||||
|
|
35
profile.c
35
profile.c
|
@ -485,19 +485,13 @@ static int setup_temperature_limits(struct graphics_context *gc, struct plot_inf
|
||||||
|
|
||||||
static void plot_single_temp_text(struct graphics_context *gc, int sec, int mkelvin)
|
static void plot_single_temp_text(struct graphics_context *gc, int sec, int mkelvin)
|
||||||
{
|
{
|
||||||
int deg;
|
double deg;
|
||||||
const char *unit;
|
const char *unit;
|
||||||
static const text_render_options_t tro = {12, 0.2, 0.2, 1.0, LEFT, TOP};
|
static const text_render_options_t tro = {12, 0.2, 0.2, 1.0, LEFT, TOP};
|
||||||
temperature_t temperature = { mkelvin };
|
|
||||||
|
|
||||||
if (output_units.temperature == FAHRENHEIT) {
|
deg = get_temp_units(mkelvin, &unit);
|
||||||
deg = to_F(temperature);
|
|
||||||
unit = UTF8_DEGREE "F";
|
plot_text(gc, &tro, sec, mkelvin, "%d%s", (int)(deg + 0.5), unit);
|
||||||
} else {
|
|
||||||
deg = to_C(temperature);
|
|
||||||
unit = UTF8_DEGREE "C";
|
|
||||||
}
|
|
||||||
plot_text(gc, &tro, sec, temperature.mkelvin, "%d%s", deg, unit);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static void plot_temperature_text(struct graphics_context *gc, struct plot_info *pi)
|
static void plot_temperature_text(struct graphics_context *gc, struct plot_info *pi)
|
||||||
|
@ -617,32 +611,13 @@ static void plot_cylinder_pressure(struct graphics_context *gc, struct plot_info
|
||||||
plot_pressure_helper(gc, pi, INTERPOLATED_PR);
|
plot_pressure_helper(gc, pi, INTERPOLATED_PR);
|
||||||
}
|
}
|
||||||
|
|
||||||
static int mbar_to_PSI(int mbar)
|
|
||||||
{
|
|
||||||
pressure_t p = {mbar};
|
|
||||||
return to_PSI(p);
|
|
||||||
}
|
|
||||||
|
|
||||||
static void plot_pressure_value(struct graphics_context *gc, int mbar, int sec,
|
static void plot_pressure_value(struct graphics_context *gc, int mbar, int sec,
|
||||||
int xalign, int yalign)
|
int xalign, int yalign)
|
||||||
{
|
{
|
||||||
int pressure;
|
int pressure;
|
||||||
const char *unit;
|
const char *unit;
|
||||||
|
|
||||||
switch (output_units.pressure) {
|
pressure = get_pressure_units(mbar, &unit);
|
||||||
case PASCAL:
|
|
||||||
pressure = mbar * 100;
|
|
||||||
unit = "pascal";
|
|
||||||
break;
|
|
||||||
case BAR:
|
|
||||||
pressure = (mbar + 500) / 1000;
|
|
||||||
unit = "bar";
|
|
||||||
break;
|
|
||||||
case PSI:
|
|
||||||
pressure = mbar_to_PSI(mbar);
|
|
||||||
unit = "psi";
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
text_render_options_t tro = {10, 0.2, 1.0, 0.2, xalign, yalign};
|
text_render_options_t tro = {10, 0.2, 1.0, 0.2, xalign, yalign};
|
||||||
plot_text(gc, &tro, sec, mbar, "%d %s", pressure, unit);
|
plot_text(gc, &tro, sec, mbar, "%d %s", pressure, unit);
|
||||||
}
|
}
|
||||||
|
|
277
statistics.c
Normal file
277
statistics.c
Normal file
|
@ -0,0 +1,277 @@
|
||||||
|
/* statistics.c */
|
||||||
|
/* creates the UI for the Info & Stats page -
|
||||||
|
* controlled through the following interfaces:
|
||||||
|
*
|
||||||
|
* void show_dive_stats(struct dive *dive)
|
||||||
|
* void flush_dive_stats_changes(struct dive *dive)
|
||||||
|
*
|
||||||
|
* called from gtk-ui:
|
||||||
|
* GtkWidget *stats_widget(void)
|
||||||
|
*/
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <string.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <stdarg.h>
|
||||||
|
#include <time.h>
|
||||||
|
|
||||||
|
#include "dive.h"
|
||||||
|
#include "display.h"
|
||||||
|
#include "display-gtk.h"
|
||||||
|
#include "divelist.h"
|
||||||
|
|
||||||
|
typedef struct {
|
||||||
|
GtkWidget *date,
|
||||||
|
*dive_time,
|
||||||
|
*surf_intv,
|
||||||
|
*max_depth,
|
||||||
|
*avg_depth,
|
||||||
|
*water_temp,
|
||||||
|
*sac,
|
||||||
|
*otu,
|
||||||
|
*o2he,
|
||||||
|
*gas_used,
|
||||||
|
*total_time,
|
||||||
|
*avg_time,
|
||||||
|
*max_overall_depth,
|
||||||
|
*avg_overall_depth,
|
||||||
|
*min_sac,
|
||||||
|
*avg_sac,
|
||||||
|
*max_sac;
|
||||||
|
} info_stat_widget_t;
|
||||||
|
|
||||||
|
static info_stat_widget_t info_stat_w;
|
||||||
|
|
||||||
|
typedef struct {
|
||||||
|
duration_t total_time;
|
||||||
|
/* avg_time is simply total_time / nr -- let's not keep this */
|
||||||
|
depth_t max_depth;
|
||||||
|
depth_t avg_depth;
|
||||||
|
volume_t max_sac;
|
||||||
|
volume_t min_sac;
|
||||||
|
volume_t avg_sac;
|
||||||
|
} info_stat_t;
|
||||||
|
|
||||||
|
static info_stat_t info_stat;
|
||||||
|
|
||||||
|
static void process_all_dives(struct dive *dive, struct dive **prev_dive)
|
||||||
|
{
|
||||||
|
int idx;
|
||||||
|
struct dive *dp;
|
||||||
|
int old_tt, sac_time = 0;
|
||||||
|
|
||||||
|
*prev_dive = NULL;
|
||||||
|
memset(&info_stat, 0, sizeof(info_stat));
|
||||||
|
/* this relies on the fact that the dives in the dive_table
|
||||||
|
* are in chronological order */
|
||||||
|
for (idx = 0; idx < dive_table.nr; idx++) {
|
||||||
|
dp = dive_table.dives[idx];
|
||||||
|
if (dp->when == dive->when) {
|
||||||
|
/* that's the one we are showing */
|
||||||
|
if (idx > 0)
|
||||||
|
*prev_dive = dive_table.dives[idx-1];
|
||||||
|
}
|
||||||
|
old_tt = info_stat.total_time.seconds;
|
||||||
|
info_stat.total_time.seconds += dp->duration.seconds;
|
||||||
|
if (dp->maxdepth.mm > info_stat.max_depth.mm)
|
||||||
|
info_stat.max_depth.mm = dp->maxdepth.mm;
|
||||||
|
info_stat.avg_depth.mm = (1.0 * old_tt * info_stat.avg_depth.mm +
|
||||||
|
dp->duration.seconds * dp->meandepth.mm) / info_stat.total_time.seconds;
|
||||||
|
if (dp->sac > 0) {
|
||||||
|
int old_sac_time = sac_time;
|
||||||
|
sac_time += dp->duration.seconds;
|
||||||
|
info_stat.avg_sac.mliter = (1.0 * old_sac_time * info_stat.avg_sac.mliter +
|
||||||
|
dp->duration.seconds * dp->sac) / sac_time ;
|
||||||
|
if (dp->sac > info_stat.max_sac.mliter)
|
||||||
|
info_stat.max_sac.mliter = dp->sac;
|
||||||
|
if (info_stat.min_sac.mliter == 0 || dp->sac < info_stat.max_sac.mliter)
|
||||||
|
info_stat.min_sac.mliter = dp->sac;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static void set_label(GtkWidget *w, const char *fmt, ...)
|
||||||
|
{
|
||||||
|
char buf[80];
|
||||||
|
va_list args;
|
||||||
|
|
||||||
|
va_start(args, fmt);
|
||||||
|
vsnprintf(buf, sizeof(buf), fmt, args);
|
||||||
|
va_end(args);
|
||||||
|
gtk_label_set_text(GTK_LABEL(w), buf);
|
||||||
|
}
|
||||||
|
|
||||||
|
static char * get_time_string(int seconds, int maxdays)
|
||||||
|
{
|
||||||
|
static char buf[80];
|
||||||
|
if (maxdays && seconds > 3600 * 24 * maxdays)
|
||||||
|
snprintf(buf, sizeof(buf), "more than %d days", maxdays);
|
||||||
|
else {
|
||||||
|
int days = seconds / 3600 / 24;
|
||||||
|
int hours = (seconds - days * 3600 * 24) / 3600;
|
||||||
|
int minutes = (seconds - days * 3600 * 24 - hours * 3600) / 60;
|
||||||
|
if (days > 0)
|
||||||
|
snprintf(buf, sizeof(buf), "%dd %dh %dmin", days, hours, minutes);
|
||||||
|
else
|
||||||
|
snprintf(buf, sizeof(buf), "%dh %dmin", hours, minutes);
|
||||||
|
}
|
||||||
|
return buf;
|
||||||
|
}
|
||||||
|
|
||||||
|
void show_dive_stats(struct dive *dive)
|
||||||
|
{
|
||||||
|
char buf[80];
|
||||||
|
double value;
|
||||||
|
int decimals;
|
||||||
|
const char *unit;
|
||||||
|
int idx, offset, gas_used;
|
||||||
|
struct dive *prev_dive;
|
||||||
|
|
||||||
|
process_all_dives(dive, &prev_dive);
|
||||||
|
|
||||||
|
strftime(buf, 80, "%a, %b %d, %Y, %k:%M", gmtime(&dive->when));
|
||||||
|
set_label(info_stat_w.date, buf);
|
||||||
|
set_label(info_stat_w.dive_time, "%d min", (dive->duration.seconds + 30) / 60);
|
||||||
|
if (prev_dive)
|
||||||
|
set_label(info_stat_w.surf_intv, get_time_string(dive->when - prev_dive->when, 4));
|
||||||
|
else
|
||||||
|
set_label(info_stat_w.surf_intv, "unknown");
|
||||||
|
value = get_depth_units(dive->maxdepth.mm, &decimals, &unit);
|
||||||
|
set_label(info_stat_w.max_depth, "%.*f %s", decimals, value, unit);
|
||||||
|
value = get_depth_units(dive->meandepth.mm, &decimals, &unit);
|
||||||
|
set_label(info_stat_w.avg_depth, "%.*f %s", decimals, value, unit);
|
||||||
|
value = get_temp_units(dive->watertemp.mkelvin, &unit);
|
||||||
|
set_label(info_stat_w.water_temp, "%.1f %s", value, unit);
|
||||||
|
value = get_volume_units(dive->sac, &decimals, &unit);
|
||||||
|
if (value > 0) {
|
||||||
|
set_label(info_stat_w.sac, "%.*f %s/min", decimals, value, unit);
|
||||||
|
} else
|
||||||
|
set_label(info_stat_w.sac, "");
|
||||||
|
set_label(info_stat_w.otu, "%d", dive->otu);
|
||||||
|
offset = 0;
|
||||||
|
gas_used = 0;
|
||||||
|
buf[0] = '\0';
|
||||||
|
/* for the O2/He readings just create a list of them */
|
||||||
|
for (idx = 0; idx < MAX_CYLINDERS; idx++) {
|
||||||
|
cylinder_t *cyl = &dive->cylinder[idx];
|
||||||
|
/* we assume that every valid cylinder has either a working pressure
|
||||||
|
* or a size; but for good measure let's also accept cylinders with
|
||||||
|
* a starting or ending pressure*/
|
||||||
|
if (cyl->type.workingpressure.mbar || cyl->type.size.mliter ||
|
||||||
|
cyl->start.mbar || cyl->end.mbar) {
|
||||||
|
/* 0% O2 strangely means air, so 21% - I don't like that at all */
|
||||||
|
int o2 = cyl->gasmix.o2.permille ? : 209;
|
||||||
|
if (offset > 0) {
|
||||||
|
snprintf(buf+offset, 80-offset, ", ");
|
||||||
|
offset += 2;
|
||||||
|
}
|
||||||
|
snprintf(buf+offset, 80-offset, "%d/%d", (o2 + 5) / 10,
|
||||||
|
(cyl->gasmix.he.permille + 5) / 10);
|
||||||
|
offset = strlen(buf);
|
||||||
|
}
|
||||||
|
/* and if we have size, start and end pressure, we can
|
||||||
|
* calculate the total gas used */
|
||||||
|
if (cyl->type.size.mliter && cyl->start.mbar && cyl->end.mbar)
|
||||||
|
gas_used += cyl->type.size.mliter / 1000.0 *
|
||||||
|
(cyl->start.mbar - cyl->end.mbar);
|
||||||
|
}
|
||||||
|
if (offset)
|
||||||
|
set_label(info_stat_w.o2he, buf);
|
||||||
|
if (gas_used) {
|
||||||
|
value = get_volume_units(gas_used, &decimals, &unit);
|
||||||
|
set_label(info_stat_w.gas_used, "%.*f %s", decimals, value, unit);
|
||||||
|
} else
|
||||||
|
set_label(info_stat_w.gas_used, "");
|
||||||
|
/* and now do the statistics */
|
||||||
|
set_label(info_stat_w.total_time, get_time_string(info_stat.total_time.seconds, 0));
|
||||||
|
set_label(info_stat_w.avg_time, get_time_string(info_stat.total_time.seconds / dive_table.nr, 0));
|
||||||
|
value = get_depth_units(info_stat.max_depth.mm, &decimals, &unit);
|
||||||
|
set_label(info_stat_w.max_overall_depth, "%.*f %s", decimals, value, unit);
|
||||||
|
value = get_depth_units(info_stat.avg_depth.mm, &decimals, &unit);
|
||||||
|
set_label(info_stat_w.avg_overall_depth, "%.*f %s", decimals, value, unit);
|
||||||
|
value = get_volume_units(info_stat.max_sac.mliter, &decimals, &unit);
|
||||||
|
set_label(info_stat_w.max_sac, "%.*f %s/min", decimals, value, unit);
|
||||||
|
value = get_volume_units(info_stat.min_sac.mliter, &decimals, &unit);
|
||||||
|
set_label(info_stat_w.min_sac, "%.*f %s/min", decimals, value, unit);
|
||||||
|
value = get_volume_units(info_stat.avg_sac.mliter, &decimals, &unit);
|
||||||
|
set_label(info_stat_w.avg_sac, "%.*f %s/min", decimals, value, unit);
|
||||||
|
}
|
||||||
|
|
||||||
|
void flush_dive_stats_changes(struct dive *dive)
|
||||||
|
{
|
||||||
|
/* We do nothing: we require the "Ok" button press */
|
||||||
|
}
|
||||||
|
|
||||||
|
static GtkWidget *new_info_label_in_frame(GtkWidget *box, const char *label)
|
||||||
|
{
|
||||||
|
GtkWidget *label_widget;
|
||||||
|
GtkWidget *frame;
|
||||||
|
|
||||||
|
frame = gtk_frame_new(label);
|
||||||
|
label_widget = gtk_label_new(NULL);
|
||||||
|
gtk_box_pack_start(GTK_BOX(box), frame, TRUE, TRUE, 3);
|
||||||
|
gtk_container_add(GTK_CONTAINER(frame), label_widget);
|
||||||
|
|
||||||
|
return label_widget;
|
||||||
|
}
|
||||||
|
|
||||||
|
GtkWidget *stats_widget(void)
|
||||||
|
{
|
||||||
|
|
||||||
|
GtkWidget *vbox, *hbox, *infoframe, *statsframe, *framebox;
|
||||||
|
|
||||||
|
vbox = gtk_vbox_new(FALSE, 3);
|
||||||
|
|
||||||
|
infoframe = gtk_frame_new("Dive Info");
|
||||||
|
gtk_box_pack_start(GTK_BOX(vbox), infoframe, TRUE, FALSE, 3);
|
||||||
|
framebox = gtk_vbox_new(FALSE, 3);
|
||||||
|
gtk_container_add(GTK_CONTAINER(infoframe), framebox);
|
||||||
|
|
||||||
|
/* first row */
|
||||||
|
hbox = gtk_hbox_new(FALSE, 3);
|
||||||
|
gtk_box_pack_start(GTK_BOX(framebox), hbox, TRUE, FALSE, 3);
|
||||||
|
|
||||||
|
info_stat_w.date = new_info_label_in_frame(hbox, "Date");
|
||||||
|
info_stat_w.dive_time = new_info_label_in_frame(hbox, "Dive Time");
|
||||||
|
info_stat_w.surf_intv = new_info_label_in_frame(hbox, "Surf Intv");
|
||||||
|
|
||||||
|
/* second row */
|
||||||
|
hbox = gtk_hbox_new(FALSE, 3);
|
||||||
|
gtk_box_pack_start(GTK_BOX(framebox), hbox, TRUE, FALSE, 3);
|
||||||
|
|
||||||
|
info_stat_w.max_depth = new_info_label_in_frame(hbox, "Max Depth");
|
||||||
|
info_stat_w.avg_depth = new_info_label_in_frame(hbox, "Avg Depth");
|
||||||
|
info_stat_w.water_temp = new_info_label_in_frame(hbox, "Water Temp");
|
||||||
|
|
||||||
|
/* third row */
|
||||||
|
hbox = gtk_hbox_new(FALSE, 3);
|
||||||
|
gtk_box_pack_start(GTK_BOX(framebox), hbox, TRUE, FALSE, 3);
|
||||||
|
|
||||||
|
info_stat_w.sac = new_info_label_in_frame(hbox, "SAC");
|
||||||
|
info_stat_w.otu = new_info_label_in_frame(hbox, "OTU");
|
||||||
|
info_stat_w.o2he = new_info_label_in_frame(hbox, "O" UTF8_SUBSCRIPT_2 " / He");
|
||||||
|
info_stat_w.gas_used = new_info_label_in_frame(hbox, "Gas Used");
|
||||||
|
|
||||||
|
statsframe = gtk_frame_new("Statistics");
|
||||||
|
gtk_box_pack_start(GTK_BOX(vbox), statsframe, TRUE, FALSE, 3);
|
||||||
|
framebox = gtk_vbox_new(FALSE, 3);
|
||||||
|
gtk_container_add(GTK_CONTAINER(statsframe), framebox);
|
||||||
|
|
||||||
|
/* first row */
|
||||||
|
hbox = gtk_hbox_new(FALSE, 3);
|
||||||
|
gtk_box_pack_start(GTK_BOX(framebox), hbox, TRUE, FALSE, 3);
|
||||||
|
|
||||||
|
info_stat_w.total_time = new_info_label_in_frame(hbox, "Total Time");
|
||||||
|
info_stat_w.avg_time = new_info_label_in_frame(hbox, "Avg Time");
|
||||||
|
info_stat_w.max_overall_depth = new_info_label_in_frame(hbox, "Max Depth");
|
||||||
|
info_stat_w.avg_overall_depth = new_info_label_in_frame(hbox, "Avg Depth");
|
||||||
|
|
||||||
|
/* second row */
|
||||||
|
hbox = gtk_hbox_new(FALSE, 3);
|
||||||
|
gtk_box_pack_start(GTK_BOX(framebox), hbox, TRUE, FALSE, 3);
|
||||||
|
|
||||||
|
info_stat_w.max_sac = new_info_label_in_frame(hbox, "Max SAC");
|
||||||
|
info_stat_w.min_sac = new_info_label_in_frame(hbox, "Min SAC");
|
||||||
|
info_stat_w.avg_sac = new_info_label_in_frame(hbox, "Avg SAC");
|
||||||
|
|
||||||
|
return vbox;
|
||||||
|
}
|
2
uemis.c
2
uemis.c
|
@ -86,7 +86,7 @@ static int pressure_to_depth(uint16_t value)
|
||||||
{
|
{
|
||||||
double atm, cm;
|
double atm, cm;
|
||||||
|
|
||||||
atm = (value / 100.0) / 1.01325;
|
atm = bar_to_atm(value / 100.0);
|
||||||
cm = 100 * atm + 0.5;
|
cm = 100 * atm + 0.5;
|
||||||
return( (cm > 0) ? 10 * (long)cm : 0);
|
return( (cm > 0) ? 10 * (long)cm : 0);
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue