summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorGravatar Robert C. Helling <helling@atdotde.de>2019-03-30 20:59:28 +0100
committerGravatar Dirk Hohndel <dirk@hohndel.org>2019-04-04 09:33:56 -0700
commit8c9e5becb20d2d8c8aa1f7108775cc520b08fae1 (patch)
treecc7bdbc918716ba4d7cf9d39acebce1ecc1cbc31
parenta89b36c661662346c6c8092bdd2cefebf8f58c8a (diff)
downloadsubsurface-8c9e5becb20d2d8c8aa1f7108775cc520b08fae1.tar.gz
Export profile data
This introduces a csv file that contains the data from the structs defined in profile.c, in particular all deco information computed for the dive profle (including NDL, TTS, ceilings, surface GFs etc). Signed-off-by: Robert C. Helling <helling@atdotde.de>
-rw-r--r--CHANGELOG.md1
-rw-r--r--core/CMakeLists.txt1
-rw-r--r--core/save-profiledata.c197
-rw-r--r--core/save-profiledata.h16
-rw-r--r--desktop-widgets/divelogexportdialog.cpp7
-rw-r--r--desktop-widgets/divelogexportdialog.ui12
6 files changed, 233 insertions, 1 deletions
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 2da845e5a..6cd5076d5 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -1,4 +1,5 @@
- Import: Initial support for importing Mares log software
+- Export option for profile data
- Planner: Allow for a final segment at the surface to display further desaturation
- Desktop: Add stats by depth and temperature ranges to yearly stats [#1996]
- Desktop: make sure cloud storage email addresses are lower case only
diff --git a/core/CMakeLists.txt b/core/CMakeLists.txt
index 004f3693b..c55b5627d 100644
--- a/core/CMakeLists.txt
+++ b/core/CMakeLists.txt
@@ -128,6 +128,7 @@ set(SUBSURFACE_CORE_LIB_SRCS
save-git.c
save-html.c
save-html.h
+ save-profiledata.c
save-xml.c
sha1.c
sha1.h
diff --git a/core/save-profiledata.c b/core/save-profiledata.c
new file mode 100644
index 000000000..603045fe0
--- /dev/null
+++ b/core/save-profiledata.c
@@ -0,0 +1,197 @@
+#include "core/profile.h"
+#include "core/profile.h"
+#include "core/dive.h"
+#include "core/display.h"
+#include "core/membuffer.h"
+#include "core/subsurface-string.h"
+#include "core/save-profiledata.h"
+
+static void put_int(struct membuffer *b, int val)
+{
+ put_format(b, "\"%d\", ", val);
+}
+
+static void put_csv_string(struct membuffer *b, const char *val)
+{
+ put_format(b, "\"%s\", ", val);
+}
+
+static void put_double(struct membuffer *b, double val)
+{
+ put_format(b, "\"%f\" ", val);
+}
+
+static void put_pd(struct membuffer *b, struct plot_data *entry)
+{
+ if (!entry)
+ return;
+
+ put_int(b, entry->in_deco);
+ put_int(b, entry->sec);
+ for (int c = 0; c < MAX_CYLINDERS; c++) {
+ put_int(b, entry->pressure[c][0]);
+ put_int(b, entry->pressure[c][1]);
+ }
+ put_int(b, entry->temperature);
+ put_int(b, entry->depth);
+ put_int(b, entry->ceiling);
+ for (int i = 0; i < 16; i++)
+ put_int(b, entry->ceilings[i]);
+ for (int i = 0; i < 16; i++)
+ put_int(b, entry->percentages[i]);
+ put_int(b, entry->ndl);
+ put_int(b, entry->tts);
+ put_int(b, entry->rbt);
+ put_int(b, entry->stoptime);
+ put_int(b, entry->stopdepth);
+ put_int(b, entry->cns);
+ put_int(b, entry->smoothed);
+ put_int(b, entry->sac);
+ put_int(b, entry->running_sum);
+ put_double(b, entry->pressures.o2);
+ put_double(b, entry->pressures.n2);
+ put_double(b, entry->pressures.he);
+ put_int(b, entry->o2pressure.mbar);
+ put_int(b, entry->o2sensor[0].mbar);
+ put_int(b, entry->o2sensor[1].mbar);
+ put_int(b, entry->o2sensor[2].mbar);
+ put_int(b, entry->o2setpoint.mbar);
+ put_int(b, entry->scr_OC_pO2.mbar);
+ put_double(b, entry->mod);
+ put_double(b, entry->ead);
+ put_double(b, entry->end);
+ put_double(b, entry->eadd);
+ switch (entry->velocity) {
+ case STABLE:
+ put_csv_string(b, "STABLE");
+ break;
+ case SLOW:
+ put_csv_string(b, "SLOW");
+ break;
+ case MODERATE:
+ put_csv_string(b, "MODERATE");
+ break;
+ case FAST:
+ put_csv_string(b, "FAST");
+ break;
+ case CRAZY:
+ put_csv_string(b, "CRAZY");
+ break;
+ }
+ put_int(b, entry->speed);
+ put_int(b, entry->in_deco_calc);
+ put_int(b, entry->ndl_calc);
+ put_int(b, entry->tts_calc);
+ put_int(b, entry->stoptime_calc);
+ put_int(b, entry->stopdepth_calc);
+ put_int(b, entry->pressure_time);
+ put_int(b, entry->heartbeat);
+ put_int(b, entry->bearing);
+ put_double(b, entry->ambpressure);
+ put_double(b, entry->gfline);
+ put_double(b, entry->surface_gf);
+ put_double(b, entry->density);
+ put_int(b, entry->icd_warning ? 1 : 0);
+}
+
+static void put_headers(struct membuffer *b)
+{
+ put_csv_string(b, "in_deco");
+ put_csv_string(b, "sec");
+ for (int c = 0; c < MAX_CYLINDERS; c++) {
+ put_format(b, "\"pressure_%d_cylinder\", ", c);
+ put_format(b, "\"pressure_%d_interpolated\", ", c);
+ }
+ put_csv_string(b, "temperature");
+ put_csv_string(b, "depth");
+ put_csv_string(b, "ceiling");
+ for (int i = 0; i < 16; i++)
+ put_format(b, "\"ceiling_%d\", ", i);
+ for (int i = 0; i < 16; i++)
+ put_format(b, "\"percentage_%d\", ", i);
+ put_csv_string(b, "ndl");
+ put_csv_string(b, "tts");
+ put_csv_string(b, "rbt");
+ put_csv_string(b, "stoptime");
+ put_csv_string(b, "stopdepth");
+ put_csv_string(b, "cns");
+ put_csv_string(b, "smoothed");
+ put_csv_string(b, "sac");
+ put_csv_string(b, "running_sum");
+ put_csv_string(b, "pressureo2");
+ put_csv_string(b, "pressuren2");
+ put_csv_string(b, "pressurehe");
+ put_csv_string(b, "o2pressure");
+ put_csv_string(b, "o2sensor0");
+ put_csv_string(b, "o2sensor1");
+ put_csv_string(b, "o2sensor2");
+ put_csv_string(b, "o2setpoint");
+ put_csv_string(b, "scr_oc_po2");
+ put_csv_string(b, "mod");
+ put_csv_string(b, "ead");
+ put_csv_string(b, "end");
+ put_csv_string(b, "eadd");
+ put_csv_string(b, "velocity");
+ put_csv_string(b, "speed");
+ put_csv_string(b, "in_deco_calc");
+ put_csv_string(b, "ndl_calc");
+ put_csv_string(b, "tts_calc");
+ put_csv_string(b, "stoptime_calc");
+ put_csv_string(b, "stopdepth_calc");
+ put_csv_string(b, "pressure_time");
+ put_csv_string(b, "heartbeat");
+ put_csv_string(b, "bearing");
+ put_csv_string(b, "ambpressure");
+ put_csv_string(b, "gfline");
+ put_csv_string(b, "surface_gf");
+ put_csv_string(b, "density");
+ put_csv_string(b, "icd_warning");
+}
+
+static void save_profiles_buffer(struct membuffer *b, bool select_only)
+{
+ int i;
+ struct dive *dive;
+ struct plot_info pi;
+ struct deco_state *planner_deco_state = NULL;
+
+ for_each_dive(i, dive) {
+ if (select_only && !dive->selected)
+ continue;
+ pi = calculate_max_limits_new(dive, &dive->dc);
+ create_plot_info_new(dive, &dive->dc, &pi, false, planner_deco_state);
+ put_headers(b);
+ put_format(b, "\n");
+
+ for (int i = 0; i < pi.nr; i++) {
+ put_pd(b, &pi.entry[i]);
+ put_format(b, "\n");
+ }
+ put_format(b, "\n");
+ }
+}
+
+int save_profiledata(const char *filename, const bool select_only)
+{
+ struct membuffer buf = { 0 };
+ FILE *f;
+ int error = 0;
+
+ save_profiles_buffer(&buf, select_only);
+
+ if (same_string(filename, "-")) {
+ f = stdout;
+ } else {
+ error = -1;
+ f = subsurface_fopen(filename, "w");
+ }
+ if (f) {
+ flush_buffer(&buf, f);
+ error = fclose(f);
+ }
+ if (error)
+ report_error("Save failed (%s)", strerror(error));
+
+ free_buffer(&buf);
+ return error;
+}
diff --git a/core/save-profiledata.h b/core/save-profiledata.h
new file mode 100644
index 000000000..23e833a27
--- /dev/null
+++ b/core/save-profiledata.h
@@ -0,0 +1,16 @@
+// SPDX-License-Identifier: GPL-2.0
+#ifndef SAVE_PROFILE_DATA_H
+#define SAVE_PROFILE_DATA_H
+
+#include "dive.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+int save_profiledata(const char *filename, bool selected_only);
+
+#ifdef __cplusplus
+}
+#endif
+#endif // SAVE_PROFILE_DATA_H
diff --git a/desktop-widgets/divelogexportdialog.cpp b/desktop-widgets/divelogexportdialog.cpp
index 3e712fc5d..216a296eb 100644
--- a/desktop-widgets/divelogexportdialog.cpp
+++ b/desktop-widgets/divelogexportdialog.cpp
@@ -15,6 +15,7 @@
#include "core/settings/qPrefDisplay.h"
#include "desktop-widgets/mainwindow.h"
#include "profile-widget/profilewidget2.h"
+#include "core/save-profiledata.h"
#include "core/dive.h" // Allows access to helper functions in TeX export.
// Retrieves the current unit settings defined in the Subsurface preferences.
@@ -98,6 +99,8 @@ void DiveLogExportDialog::showExplanation()
ui->description->setText(tr("Write dive as LaTeX macros to file."));
} else if (ui->exportProfile->isChecked()) {
ui->description->setText(tr("Write the profile image as PNG file."));
+ } else if (ui->exportProfileData->isChecked()) {
+ ui->description->setText(tr("Write profile data to a CSV file."));
}
}
@@ -175,6 +178,10 @@ void DiveLogExportDialog::on_buttonBox_accepted()
filename = QFileDialog::getSaveFileName(this, tr("Save profile image"), lastDir);
if (!filename.isNull() && !filename.isEmpty())
exportProfile(qPrintable(filename), ui->exportSelected->isChecked());
+ } else if (ui->exportProfileData->isChecked()) {
+ filename = QFileDialog::getSaveFileName(this, tr("Save profile data"), lastDir);
+ if (!filename.isNull() && !filename.isEmpty())
+ save_profiledata(qPrintable(filename), ui->exportSelected->isChecked());
}
break;
case 1:
diff --git a/desktop-widgets/divelogexportdialog.ui b/desktop-widgets/divelogexportdialog.ui
index 9080c647f..07b06378b 100644
--- a/desktop-widgets/divelogexportdialog.ui
+++ b/desktop-widgets/divelogexportdialog.ui
@@ -7,7 +7,7 @@
<x>0</x>
<y>0</y>
<width>507</width>
- <height>398</height>
+ <height>423</height>
</rect>
</property>
<property name="windowTitle">
@@ -231,6 +231,16 @@
</attribute>
</widget>
</item>
+ <item>
+ <widget class="QRadioButton" name="exportProfileData">
+ <property name="text">
+ <string>Profile Data CSV</string>
+ </property>
+ <attribute name="buttonGroup">
+ <string notr="true">exportGroup</string>
+ </attribute>
+ </widget>
+ </item>
</layout>
</widget>
</item>