summaryrefslogtreecommitdiffstats
path: root/file.c
diff options
context:
space:
mode:
authorGravatar Linus Torvalds <torvalds@linux-foundation.org>2012-06-19 20:07:42 -0700
committerGravatar Linus Torvalds <torvalds@linux-foundation.org>2012-06-19 20:07:42 -0700
commitba31e37063308ab74b282db983557797d05f59d1 (patch)
tree2a8ce73074cba1019f4ea6481f0f549fd5c28430 /file.c
parent80b0c097334a13f9fd69ed87eceb3997a1084311 (diff)
downloadsubsurface-ba31e37063308ab74b282db983557797d05f59d1.tar.gz
cochran: add support for importing the exported CSV files
The Cochran Analyst software can export the basic dive information as CSV files (comma-separated values). Individual CSV files contain just one particular type of information: depth, temperature or cylinder pressure, which is rather inconvenient. However, the way subsurface works, you can just import these CSV files all as individual dives, and then subsurface will automatically merge the dives with the same date and time - and in the process it will also merge all the samples. So it turns out that we don't really need any special handling. You can literally just do subsurface <list-your-cochran-export-files-here> and you're all done. Of course, the CSV files really *are* pretty useless, since they don't contain all the nice information about where the dive took place etc. So you literally just get the dive profile. But that's better than getting nothing at all. I'd love to actually be able to parse the real native Cochran Analyst software CAN files, but in the meantime this is at least a starting point. And if I'm ever able to parse those nasty CAN-files, this makes comparisons with the exports much easier. Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
Diffstat (limited to 'file.c')
-rw-r--r--file.c127
1 files changed, 127 insertions, 0 deletions
diff --git a/file.c b/file.c
index aff1d51f2..3f06259fa 100644
--- a/file.c
+++ b/file.c
@@ -94,6 +94,125 @@ static int try_to_open_suunto(const char *filename, struct memblock *mem, GError
return success;
}
+static time_t parse_date(const char *date)
+{
+ int hour, min, sec;
+ struct tm tm;
+ char *p;
+
+ memset(&tm, 0, sizeof(tm));
+ tm.tm_mday = strtol(date, &p, 10);
+ if (tm.tm_mday < 1 || tm.tm_mday > 31)
+ return 0;
+ for (tm.tm_mon = 0; tm.tm_mon < 12; tm.tm_mon++) {
+ if (!memcmp(p, monthname(tm.tm_mon), 3))
+ break;
+ }
+ if (tm.tm_mon > 11)
+ return 0;
+ date = p+3;
+ tm.tm_year = strtol(date, &p, 10);
+ if (date == p)
+ return 0;
+ if (tm.tm_year < 70)
+ tm.tm_year += 2000;
+ if (tm.tm_year < 100)
+ tm.tm_year += 1900;
+ if (sscanf(p, "%d:%d:%d", &hour, &min, &sec) != 3)
+ return 0;
+ tm.tm_hour = hour;
+ tm.tm_min = min;
+ tm.tm_sec = sec;
+ return utc_mktime(&tm);
+}
+
+enum csv_format {
+ CSV_DEPTH, CSV_TEMP, CSV_PRESSURE
+};
+
+static void add_sample_data(struct sample *sample, enum csv_format type, double val)
+{
+ switch (type) {
+ case CSV_DEPTH:
+ sample->depth.mm = feet_to_mm(val);
+ break;
+ case CSV_TEMP:
+ sample->temperature.mkelvin = F_to_mkelvin(val);
+ break;
+ case CSV_PRESSURE:
+ sample->cylinderpressure.mbar = psi_to_mbar(val);
+ break;
+ }
+}
+
+/*
+ * Cochran comma-separated values: depth in feet, temperature in F, pressure in psi.
+ *
+ * They start with eight comma-separated fields like:
+ *
+ * filename: {C:\Analyst4\can\T036785.can},{C:\Analyst4\can\K031892.can}
+ * divenr: %d
+ * datetime: {03Sep11 16:37:22},{15Dec11 18:27:02}
+ * ??: 1
+ * serialnr??: {CCI134},{CCI207}
+ * computer??: {GeminiII},{CommanderIII}
+ * computer??: {GeminiII},{CommanderIII}
+ * ??: 1
+ *
+ * Followed by the data values (all comma-separated, all one long line).
+ */
+static int try_to_open_csv(const char *filename, struct memblock *mem, enum csv_format type)
+{
+ char *p = mem->buffer;
+ char *header[8];
+ int i, time;
+ time_t date;
+ struct dive *dive;
+
+ for (i = 0; i < 8; i++) {
+ header[i] = p;
+ p = strchr(p, ',');
+ if (!p)
+ return 0;
+ p++;
+ }
+
+ date = parse_date(header[2]);
+ if (!date)
+ return 0;
+
+ dive = alloc_dive();
+ dive->when = date;
+ dive->number = atoi(header[1]);
+
+ time = 0;
+ for (;;) {
+ char *end;
+ double val;
+ struct sample *sample;
+
+ errno = 0;
+ val = strtod(p,&end);
+ if (end == p)
+ break;
+ if (errno)
+ break;
+
+ sample = prepare_sample(&dive);
+ sample->time.seconds = time;
+ add_sample_data(sample, type, val);
+ finish_sample(dive);
+
+ time++;
+ dive->duration.seconds = time;
+ if (*end != ',')
+ break;
+ p = end+1;
+ }
+ record_dive(dive);
+ return 1;
+}
+
static int open_by_filename(const char *filename, const char *fmt, struct memblock *mem, GError **error)
{
/* Suunto Dive Manager files: SDE */
@@ -104,6 +223,14 @@ static int open_by_filename(const char *filename, const char *fmt, struct memblo
if (!strcasecmp(fmt, "CAN"))
return try_to_open_cochran(filename, mem, error);
+ /* Cochran export comma-separated-value files */
+ if (!strcasecmp(fmt, "DPT"))
+ return try_to_open_csv(filename, mem, CSV_DEPTH);
+ if (!strcasecmp(fmt, "TMP"))
+ return try_to_open_csv(filename, mem, CSV_TEMP);
+ if (!strcasecmp(fmt, "HP1"))
+ return try_to_open_csv(filename, mem, CSV_PRESSURE);
+
return 0;
}