]> git.tdb.fi Git - ext/subsurface.git/commitdiff
First pass to parse uemis Zurich '.SDA' files
authorDirk Hohndel <dirk@hohndel.org>
Thu, 15 Sep 2011 04:00:49 +0000 (21:00 -0700)
committerLinus Torvalds <torvalds@linux-foundation.org>
Thu, 15 Sep 2011 15:52:55 +0000 (08:52 -0700)
This is missing a ton of the information in the .SDA files It only
parses the divelog.SDA file, not the dive.SDA file It ignores the
information on the gas(es) used and all the data on the tanks.

It still draws some strange artefacts at the end of the dive

But it correctly hooks into the import dialogue, it gives you a file
select box (somewhere, I'm sure, a gtk developer cries quietly) and then
parses enough of this file to serve as a proof of concept.

Signed-off-by: Dirk Hohndel <dirk@hohndel.org>
Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
Makefile
libdivecomputer.c
uemis.c [new file with mode: 0644]
uemis.h [new file with mode: 0644]

index 858f4a8683321b4411dd86810ce7d6a9c627b704..c78b806bb259968493b23b9e7048daa4e6f60ba7 100644 (file)
--- a/Makefile
+++ b/Makefile
@@ -6,7 +6,7 @@ LIBDIVECOMPUTERINCLUDES = $(LIBDIVECOMPUTERDIR)/include/libdivecomputer
 LIBDIVECOMPUTERARCHIVE = $(LIBDIVECOMPUTERDIR)/lib/libdivecomputer.a
 
 OBJS = main.o dive.o profile.o info.o equipment.o divelist.o \
-       parse-xml.o save-xml.o libdivecomputer.o print.o
+       parse-xml.o save-xml.o libdivecomputer.o print.o uemis.o
 
 divelog: $(OBJS)
        $(CC) $(LDFLAGS) -o divelog $(OBJS) \
@@ -46,3 +46,6 @@ libdivecomputer.o: libdivecomputer.c dive.h display.h
        $(CC) $(CFLAGS) `pkg-config --cflags gtk+-2.0 glib-2.0` \
                        -I$(LIBDIVECOMPUTERINCLUDES) \
                        -c libdivecomputer.c
+
+uemis.o: uemis.c uemis.h
+       $(CC) $(CFLAGS) `pkg-config --cflags gtk+-2.0 glib-2.0` -c uemis.c
index 06ab066392d6462f5d44941a3d729460b6941d4e..2b62fa3652b58e556684ca87df48f7364573a6a6 100644 (file)
@@ -18,6 +18,9 @@
 #include <atomics.h>
 #include <utils.h>
 
+/* handling uemis Zurich SDA files */
+#include "uemis.h"
+
 /*
  * I'd love to do a while-loop here for pending events, but
  * that seems to screw up with the dive computer reading timing.
@@ -437,6 +440,10 @@ static void do_import(device_data_t *data)
        device_t *device = NULL;
        device_status_t rc;
 
+       if (data->type == DEVICE_TYPE_UEMIS) {
+               return uemis_import();
+       }
+
        rc = device_open(data->devname, data->type, &device);
        if (rc != DEVICE_STATUS_SUCCESS) {
                error("Unable to open %s (%s)", data->name, data->devname);
@@ -502,6 +509,7 @@ struct device_list {
        { "Cressi Edy",         DEVICE_TYPE_CRESSI_EDY },
        { "Zeagle N2iTiON 3",   DEVICE_TYPE_ZEAGLE_N2ITION3 },
        { "Atomics Cobalt",     DEVICE_TYPE_ATOMICS_COBALT },
+       { "Uemis Zurich SDA",   DEVICE_TYPE_UEMIS },
        { NULL }
 };
 
diff --git a/uemis.c b/uemis.c
new file mode 100644 (file)
index 0000000..6354135
--- /dev/null
+++ b/uemis.c
@@ -0,0 +1,275 @@
+/*
+ * uemis.c
+ *
+ * UEMIS SDA file importer
+ * AUTHOR:  Dirk Hohndel - Copyright 2011
+ *
+ * Licensed under the MIT license.
+ */
+
+#include <unistd.h>
+#include <stdio.h>
+#include <string.h>
+#include <stdint.h>
+#include <stdlib.h>
+#define __USE_XOPEN
+#include <time.h>
+#include <regex.h>
+
+#include <gtk/gtk.h>
+
+#include "dive.h"
+#include "display.h"
+#include "uemis.h"
+
+/*
+ * following code is based on code found in at base64.sourceforge.net/b64.c
+ * AUTHOR:         Bob Trower 08/04/01
+ * COPYRIGHT:      Copyright (c) Trantor Standard Systems Inc., 2001
+ * NOTE:           This source code may be used as you wish, subject to
+ *                 the MIT license.
+ */
+/*
+ * Translation Table to decode (created by Bob Trower)
+ */
+static const char cd64[]="|$$$}rstuvwxyz{$$$$$$$>?@ABCDEFGHIJKLMNOPQRSTUVW$$$$$$XYZ[\\]^_`abcdefghijklmnopq";
+
+/*
+ * decodeblock -- decode 4 '6-bit' characters into 3 8-bit binary bytes
+ */
+static void decodeblock( unsigned char in[4], unsigned char out[3] ) {
+       out[ 0 ] = (unsigned char ) (in[0] << 2 | in[1] >> 4);
+       out[ 1 ] = (unsigned char ) (in[1] << 4 | in[2] >> 2);
+       out[ 2 ] = (unsigned char ) (((in[2] << 6) & 0xc0) | in[3]);
+}
+
+/*
+ * decode a base64 encoded stream discarding padding, line breaks and noise
+ */
+static void decode( uint8_t *inbuf, uint8_t *outbuf, int inbuf_len ) {
+       uint8_t in[4], out[3], v;
+       int i,len,indx_in=0,indx_out=0;
+
+       while (indx_in < inbuf_len) {
+               for (len = 0, i = 0; i < 4 && (indx_in < inbuf_len); i++ ) {
+                       v = 0;
+                       while ((indx_in < inbuf_len) && v == 0) {
+                               v = inbuf[indx_in++];
+                               v = ((v < 43 || v > 122) ? 0 : cd64[ v - 43 ]);
+                               if (v)
+                                       v = ((v == '$') ? 0 : v - 61);
+                       }
+                       if (indx_in < inbuf_len) {
+                               len++;
+                               if (v)
+                                       in[i] = (v - 1);
+                       }
+                       else
+                               in[i] = 0;
+               }
+               if( len ) {
+                       decodeblock( in, out );
+                       for( i = 0; i < len - 1; i++ )
+                               outbuf[indx_out++] = out[i];
+               }
+       }
+}
+/* end code from Bob Trower */
+
+/* small helper functions */
+/* simpleregex allocates (and reallocates) the found buffer
+ * don't forget to free it when you are done
+ */
+static int simpleregex(char *buffer, char *regex, char **found) {
+       int status;
+       regex_t re;
+       regmatch_t match[5];
+
+       if (regcomp(&re, regex, 0) !=0) {
+               fprintf(stderr,"internal error, regex failed!\n");
+               exit(1);
+       }
+       status = regexec(&re,buffer,5,match,0);
+       if(status == 0) {
+               *found = realloc(*found,match[1].rm_eo-match[1].rm_so + 1);
+               strncpy(*found,buffer+match[1].rm_so,match[1].rm_eo-match[1].rm_so);
+               (*found)[match[1].rm_eo-match[1].rm_so] = '\0';
+       }
+       return(status == 0);
+}
+
+/* read in line of arbitrary length (important for SDA files that can
+ * have lines that are tens of kB long
+ * don't forget to free it when you are done
+ */
+#define MYGETL_INCR 1024
+static char * mygetline(FILE * f) {
+       size_t size = 0;
+       size_t len  = 0;
+       char * buf  = NULL;
+
+       do {
+               size += MYGETL_INCR;
+               if ((buf = realloc(buf,size)) == NULL)
+                       break;
+               fgets(buf+len,MYGETL_INCR,f);
+               len = strlen(buf);
+       } while (!feof(f) && buf[len-1]!='\n');
+       return buf;
+}
+
+/* text matching, used to build very poor man's XML parser */
+int matchit(FILE *infd, char *regex, char *typeregex, char **found) {
+       char *buffer;
+
+       while (!feof(infd)) {
+               buffer = mygetline(infd);
+               if (buffer && simpleregex(buffer,regex,found)) {
+                       buffer = mygetline(infd);
+                       if (buffer && simpleregex(buffer,typeregex,found)) {
+                               return 1;
+                       }
+               }
+       }
+       return 0;
+}
+
+
+/*
+ * convert the base64 data blog
+ */
+int uemis_convert_base64(char *base64, uint8_t **data) {
+       int len,datalen;
+
+       len = strlen(base64);
+       datalen = (len/4 + 1)*3;
+       if (datalen < 0x123+0x25) {
+               /* less than header + 1 sample??? */
+               fprintf(stderr,"suspiciously short data block\n");
+       }
+       *data = malloc(datalen);
+       if (! *data) {
+               datalen = 0;
+               fprintf(stderr,"Out of memory\n");
+               goto bail;
+       }
+       decode(base64, *data, len);
+
+       if (memcmp(data,"Dive\01\00\00",7))
+               fprintf(stderr,"Missing Dive100 header\n");
+
+bail:
+       return datalen;
+}
+
+/*
+ * parse uemis base64 data blob into struct dive
+ */
+static void parse_divelog_binary(char *base64, struct dive **divep) {
+       int datalen;
+       int i;
+       uint8_t *data;
+       struct sample *sample;
+
+       datalen = uemis_convert_base64(base64, &data);
+
+       /* first byte of divelog data is at offset 0x123 */
+       i = 0x123;
+       while ((i < datalen) && (*(uint16_t *)(data+i))) {
+               /* it seems that a dive_time of 0 indicates the end of the valid readings */
+               sample = prepare_sample(divep);
+               sample->time.seconds = *(uint16_t *)(data+i);
+               sample->depth.mm = (*(uint16_t *)(data+i+2) - 100) / 0.101428571 + 0.5;
+               sample->temperature.mkelvin = (*(uint16_t *)(data+i+4) * 100) + 273150;
+               sample->cylinderpressure.mbar= *(uint16_t *)(data+i+23) * 10;
+               sample->cylinderindex = *(uint8_t *)(data+i+22);
+               finish_sample(*divep, sample);
+               i += 0x25;
+       }
+       return;
+}
+
+void
+parse_uemis_file(char *divelogfilename,GError **error) {
+       char *found=NULL;
+       struct tm tm;
+       struct dive *dive;
+
+       FILE *divelogfile = fopen(divelogfilename,"r");
+
+       dive = alloc_dive();
+
+       if (! matchit(divelogfile,"val key=\"date\"","<ts>\\([^<]*\\)</ts>",&found)) {
+               /* some error handling */
+               goto bail;
+       }
+       strptime(found, "%Y-%m-%dT%H:%M:%S", &tm);
+       dive->when = utc_mktime(&tm);
+       if (! matchit(divelogfile,"<val key=\"duration\">",
+                       "<float>\\([0-9.]*\\)</float>", &found)) {
+               /* some error handling */
+               goto bail;
+       }
+       dive->duration.seconds = 60.0 * atof(found);
+
+       if (! matchit(divelogfile,"<val key=\"depth\">",
+                       "<int>\\([0-9.]*\\)</int>", &found)) {
+               /* some error handling */
+               goto bail;
+       }
+       dive->maxdepth.mm = atof(found) / 0.10143 + 0.5;
+
+       if (! matchit(divelogfile,"<val key=\"file_content\">",
+                       ">\\([a-zA-Z0-9+/]*\\)<", &found)) {
+               /* some error handling */
+               goto bail;
+       }
+       parse_divelog_binary(found,&dive);
+       record_dive(dive);
+bail:
+       if (found)
+               free(found);
+}
+
+/*
+ * parse the two files extracted from the SDA
+ */
+void
+uemis_import() {
+       GtkWidget *dialog;
+       GtkFileFilter *filter = gtk_file_filter_new ();
+       gtk_file_filter_add_pattern (filter, "*.SDA");
+       gtk_file_filter_set_name(filter, "uemis Zurich SDA files");
+       dialog = gtk_file_chooser_dialog_new("Open File",
+                                       GTK_WINDOW(main_window),
+                                       GTK_FILE_CHOOSER_ACTION_OPEN,
+                                       GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL,
+                                       GTK_STOCK_OPEN, GTK_RESPONSE_ACCEPT,
+                                       NULL);
+       gtk_file_chooser_set_select_multiple(GTK_FILE_CHOOSER(dialog), TRUE);
+       gtk_file_chooser_add_filter(GTK_FILE_CHOOSER(dialog),filter);
+
+       if (gtk_dialog_run(GTK_DIALOG(dialog)) == GTK_RESPONSE_ACCEPT) {
+               GSList *filenames;
+               char *filename;
+               filenames = gtk_file_chooser_get_filenames(GTK_FILE_CHOOSER(dialog));
+
+               GError *error = NULL;
+               while(filenames != NULL) {
+                       filename = (char *)filenames->data;
+                       parse_uemis_file(filename, &error);
+                       if (error != NULL)
+                       {
+                               report_error(error);
+                               g_error_free(error);
+                               error = NULL;
+                       }
+
+                       g_free(filename);
+                       filenames = g_slist_next(filenames);
+               }
+               g_slist_free(filenames);
+               report_dives();
+       }
+       gtk_widget_destroy(dialog);
+}
diff --git a/uemis.h b/uemis.h
new file mode 100644 (file)
index 0000000..1b6c026
--- /dev/null
+++ b/uemis.h
@@ -0,0 +1,15 @@
+#ifndef UEMIS_H
+#define UEMIS_H
+
+/*
+ * defines and prototypes for the uemis Zurich SDA file parser
+ * we add this to the list of dive computers that is supported
+ * in libdivecomputer by using a negative value for the type enum
+ */
+#define DEVICE_TYPE_UEMIS (-1)
+
+void uemis_import();
+
+extern GtkWidget *main_window;
+
+#endif /* DIVE_H */