Skip to content

Commit

Permalink
started adding support for DM5
Browse files Browse the repository at this point in the history
  • Loading branch information
nrado authored and nrado committed Jan 27, 2023
1 parent c4537a9 commit a03ca4d
Show file tree
Hide file tree
Showing 4 changed files with 123 additions and 15 deletions.
9 changes: 6 additions & 3 deletions .classpath
Original file line number Diff line number Diff line change
@@ -1,12 +1,15 @@
<?xml version="1.0" encoding="UTF-8"?>
<classpath>
<classpathentry excluding="src/" kind="src" path=""/>
<classpathentry kind="src" path="src"/>
<classpathentry kind="lib" path="C:/Users/nrado/Downloads/FitSDKRelease_21.94.00/java/fit.jar">
<classpathentry exported="true" kind="lib" path="C:/Users/nrado/Downloads/FitSDKRelease_21.94.00/java/fit.jar">
<attributes>
<attribute name="javadoc_location" value="file:/C:/Users/nrado/Downloads/FitSDKRelease_21.94.00/java/doc/"/>
</attributes>
</classpathentry>
<classpathentry kind="con" path="org.eclipse.jdt.launching.JRE_CONTAINER"/>
<classpathentry kind="con" path="org.eclipse.jdt.launching.JRE_CONTAINER">
<attributes>
<attribute name="module" value="true"/>
</attributes>
</classpathentry>
<classpathentry kind="output" path="bin"/>
</classpath>
68 changes: 68 additions & 0 deletions pom.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0
http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>

<groupId>com.github.nradov</groupId>
<artifactId>SdeToFit</artifactId>
<version>1.0-SNAPSHOT</version>
<packaging>jar</packaging>
<name>SdeToFit</name>
<description>Java application to convert Suunto dive log files to ANT+ Flexible and Interoperable format for import into Garmin Connect</description>
<url>https://github.com/nradov/SdeToFit</url>
<inceptionYear>2022</inceptionYear>
<licenses>
<license>
<name>GNU Lesser General Public License v2.1</name>
<url>https://www.gnu.org/licenses/old-licenses/lgpl-2.1.html</url>
</license>
</licenses>
<issueManagement>
<system>GitHub</system>
<url>https://github.com/nradov/SdeToFit/issues</url>
</issueManagement>
<properties>
<maven.compiler.source>19</maven.compiler.source>
<maven.compiler.target>19</maven.compiler.target>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
</properties>

<build>
<sourceDirectory>src</sourceDirectory>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-site-plugin</artifactId>
<version>3.7</version>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-project-info-reports-plugin</artifactId>
<version>2.9</version>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-jar-plugin</artifactId>
<version>3.3.0</version>
<configuration>
<archive>
<manifest>
<mainClass>com.github.nradov.sdetofit.SdeToFit</mainClass>
</manifest>
</archive>
</configuration>
</plugin>
</plugins>
</build>

<dependencies>
<dependency>
<!-- https://developer.garmin.com/fit/download/ -->
<groupId>com.garmin</groupId>
<artifactId>fit</artifactId>
<version>21.94.00</version>
<type>jar</type>
<scope>system</scope>
<systemPath>${env.USERPROFILE}\Downloads\FitSDKRelease_21.94.00\java\fit.jar</systemPath>
</dependency>
</dependencies>
</project>
6 changes: 6 additions & 0 deletions src/com/github/nradov/sdetofit/DivesSourceFactory.java
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@
import org.xml.sax.SAXException;

import com.github.nradov.sdetofit.suunto.SuuntoSde;
import com.github.nradov.sdetofit.suunto.SuuntoXml;

/**
* Utility class for creating {@link DivesSource} objects.
Expand All @@ -30,6 +31,9 @@ private static final class FileExtension {
/** Suunto dive export. */
final static String SUUNTO_DIVE_EXPORT = ".sde";

/** XML file. */
final static String XML = ".xml";

}

/**
Expand All @@ -53,6 +57,8 @@ public static DivesSource create(final Path file)
final var lowerCaseFile = file.toString().toLowerCase(Locale.US);
if (lowerCaseFile.endsWith(FileExtension.SUUNTO_DIVE_EXPORT)) {
return new SuuntoSde(file);
} else if (lowerCaseFile.endsWith(FileExtension.XML)) {
return new SuuntoXml(file);
}
// TODO: add support for other file formats

Expand Down
55 changes: 43 additions & 12 deletions src/com/github/nradov/sdetofit/suunto/SuuntoXml.java
Original file line number Diff line number Diff line change
@@ -1,12 +1,16 @@
package com.github.nradov.sdetofit.suunto;

import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.nio.file.Path;
import java.util.ArrayList;
import java.util.Calendar;
import java.util.Collections;
import java.util.GregorianCalendar;
import java.util.List;
import java.util.NavigableSet;
import java.util.TreeSet;

import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
Expand All @@ -20,16 +24,17 @@
import com.garmin.fit.DateTime;
import com.garmin.fit.Manufacturer;
import com.github.nradov.sdetofit.Dive;
import com.github.nradov.sdetofit.DivesSource;
import com.github.nradov.sdetofit.Record;
import com.github.nradov.sdetofit.SdeToFit;

/**
* <p>
* Single dive profile in Suunto XML format. The format is simple with some
* basic header information at the top followed by a series of data point
* elements each containing a time, depth, and temperature (actual temperature
* readings are only present in a subset of points).
* </p>
* Single dive profile in Suunto XML format. The format is not documented so I
* had to reverse engineer it. This class may not work correctly for some
* versions of Suunto Dive Manager. The format is simple with some basic header
* information at the top followed by a series of data point elements each
* containing a time, depth, and temperature (actual temperature readings are
* only present in a subset of points).
*
* <p>
* <strong>Note:</strong> This class is intended to process input from trusted
Expand All @@ -38,7 +43,7 @@
*
* @author Nick Radov
*/
public class SuuntoXml implements Dive {
public class SuuntoXml implements Dive, DivesSource {

private final DateTime start, end;
private final String productName;
Expand All @@ -49,13 +54,25 @@ public class SuuntoXml implements Dive {

private final Element suunto;

public SuuntoXml(final InputStream is)
throws ParserConfigurationException, SAXException, IOException {
/* XML document root element name. */
private static final String DOCUMENT_ELEMENT_NAME = "SUUNTO";

public SuuntoXml(final Path file) throws ParserConfigurationException, SAXException, IOException {
this(new FileInputStream(file.toFile()));
}

public SuuntoXml(final InputStream is) throws ParserConfigurationException, SAXException, IOException {
final DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance();
final DocumentBuilder db = dbf.newDocumentBuilder();
final Document doc = db.parse(is);
suunto = doc.getDocumentElement();

if (!DOCUMENT_ELEMENT_NAME.equals(suunto.getLocalName())) {
throw new IllegalArgumentException(
"Document element name is " + suunto.getLocalName() + " instead of " + DOCUMENT_ELEMENT_NAME);
}

final int sampleCnt = Integer.valueOf(suunto.getElementsByTagName("SAMPLECNT").item(0).getTextContent());
final String date = suunto.getElementsByTagName("DATE").item(0).getTextContent();
final int dayOfMonth = Integer.valueOf(date.substring(0, 2));
final int month = Integer.valueOf(date.substring(3, 5)) - 1;
Expand All @@ -64,19 +81,28 @@ public SuuntoXml(final InputStream is)
final int hourOfDay = Integer.valueOf(time.substring(0, 2));
final int minute = Integer.valueOf(time.substring(3, 5));
final int second = Integer.valueOf(time.substring(6));
final int sampleInterval = Integer.valueOf(suunto.getElementsByTagName("SAMPLEINTERVAL").item(0).getTextContent());
final Calendar startCalendar = new GregorianCalendar(year, month, dayOfMonth, hourOfDay, minute, second);
start = new DateTime((startCalendar.getTimeInMillis() - SdeToFit.OFFSET_MS) / 1000);
final int diveTimeSec = Integer.valueOf(suunto.getElementsByTagName("DIVETIMESEC").item(0).getTextContent());
final var diveTimeSecText = suunto.getElementsByTagName("DIVETIMESEC").item(0).getTextContent().trim();
final int diveTimeSec;
// the DIVETIMESEC element may or may not be populated depending on the SDM
// version
if (diveTimeSecText.length() > 0) {
diveTimeSec = Integer.valueOf(diveTimeSecText);
} else {
diveTimeSec = sampleCnt * sampleInterval;
}
end = new DateTime(((startCalendar.getTimeInMillis() - SdeToFit.OFFSET_MS) / 1000) + diveTimeSec);

this.maxDepth = Float.parseFloat(suunto.getElementsByTagName("MAXDEPTH").item(0).getTextContent());
this.meanDepth = Float.parseFloat(suunto.getElementsByTagName("MEANDEPTH").item(0).getTextContent());

// the LOGTITLE element content is formatted like "367. 2019-11-16 11:11:00"
// where the first number is the dive number
final var logTitle = suunto.getElementsByTagName("LOGTITLE").item(0).getTextContent();
this.diveNumber = Long.parseLong(logTitle.substring(0, logTitle.indexOf('.')));

this.productName = suunto.getElementsByTagName("DEVICEMODEL").item(0).getTextContent();
this.serialNumber = Long.valueOf(suunto.getElementsByTagName("WRISTOPID").item(0).getTextContent());
this.waterTemperatureMaxDepth = Byte
Expand Down Expand Up @@ -165,4 +191,9 @@ public long getDiveNumber() {
return diveNumber;
}

@Override
public NavigableSet<Dive> getDives() {
return new TreeSet<Dive>(Collections.singleton(this));
}

}

0 comments on commit a03ca4d

Please sign in to comment.