diff -Nru openambit-0.2+20140602/debian/changelog openambit-0.3/debian/changelog --- openambit-0.2+20140602/debian/changelog 2014-07-18 05:18:32.000000000 +0000 +++ openambit-0.3/debian/changelog 2014-09-16 20:27:30.000000000 +0000 @@ -1,3 +1,9 @@ +openambit (0.3-1) unstable; urgency=low + + * New upstream release + + -- Christian Perrier Tue, 16 Sep 2014 22:27:30 +0200 + openambit (0.2+20140602-1) unstable; urgency=low * Upstream git snapshot (OpenAmbit should work with latest firmware diff -Nru openambit-0.2+20140602/src/libambit/CMakeLists.txt openambit-0.3/src/libambit/CMakeLists.txt --- openambit-0.2+20140602/src/libambit/CMakeLists.txt 2014-07-17 20:14:24.000000000 +0000 +++ openambit-0.3/src/libambit/CMakeLists.txt 2014-09-15 21:24:24.000000000 +0000 @@ -38,7 +38,7 @@ m ) -set_target_properties(ambit PROPERTIES VERSION 0.2.0 SOVERSION 0) +set_target_properties(ambit PROPERTIES VERSION 0.3.0 SOVERSION 0) include_directories( hidapi diff -Nru openambit-0.2+20140602/src/libambit/debian/changelog openambit-0.3/src/libambit/debian/changelog --- openambit-0.2+20140602/src/libambit/debian/changelog 2014-07-17 20:14:24.000000000 +0000 +++ openambit-0.3/src/libambit/debian/changelog 2014-09-15 21:24:24.000000000 +0000 @@ -1,3 +1,9 @@ +libambit (0.3-1) trusty; urgency=medium + + * New upstream release including support for Ambit 2R + + -- Emil Ljungdahl Mon, 15 Sep 2014 22:30:01 +0200 + libambit (0.2-1) saucy; urgency=low * Initial release diff -Nru openambit-0.2+20140602/src/libambit/debian/control openambit-0.3/src/libambit/debian/control --- openambit-0.2+20140602/src/libambit/debian/control 2014-07-17 20:14:24.000000000 +0000 +++ openambit-0.3/src/libambit/debian/control 2014-09-15 21:24:24.000000000 +0000 @@ -11,7 +11,7 @@ Package: libambit-dev Section: libdevel Architecture: any -Depends: libambit0 (= ${binary:Version}) +Depends: libambit0 (= ${binary:Version}), ${misc:Depends} Description: Communication library for Suunto Ambit series Libambit is the driver library to communicate with the Suunto Ambit series of sport watches. This is the -dev version of the package container @@ -20,7 +20,7 @@ Package: libambit0 Section: libs Architecture: any -Depends: libudev1, libusb-1.0-0, ${shlibs:Depends}, ${misc:Pre-Depends}, ${misc:Depends} +Depends: ${shlibs:Depends}, ${misc:Pre-Depends}, ${misc:Depends} Description: Communication library for Suunto Ambit series Libambit is the driver library to communicate with the Suunto Ambit series of sport watches. diff -Nru openambit-0.2+20140602/src/libambit/libambit.c openambit-0.3/src/libambit/libambit.c --- openambit-0.2+20140602/src/libambit/libambit.c 2014-07-17 20:14:24.000000000 +0000 +++ openambit-0.3/src/libambit/libambit.c 2014-09-15 21:24:24.000000000 +0000 @@ -59,9 +59,11 @@ * Static variables */ static ambit_known_device_t known_devices[] = { + { SUUNTO_USB_VENDOR_ID, 0x001c, "Finch", {0x00,0x00,0x00,0x00}, "Suunto Ambit3 Sport", false, 0x0400 }, + { SUUNTO_USB_VENDOR_ID, 0x001b, "Emu", {0x00,0x00,0x00,0x00}, "Suunto Ambit3 Peak", false, 0x0400 }, + { SUUNTO_USB_VENDOR_ID, 0x001d, "Greentit", {0x00,0x00,0x00,0x00}, "Suunto Ambit2 R", true, 0x0400 }, { SUUNTO_USB_VENDOR_ID, 0x001a, "Colibri", {0x01,0x01,0x02,0x00}, "Suunto Ambit2 S", true, 0x0400 }, { SUUNTO_USB_VENDOR_ID, 0x0019, "Duck", {0x01,0x01,0x02,0x00}, "Suunto Ambit2", true, 0x0400 }, - { SUUNTO_USB_VENDOR_ID, 0x001d, "Greentit", {0x00,0x00,0x00,0x00}, "Suunto Ambit2 R", true, 0x0400 }, { SUUNTO_USB_VENDOR_ID, 0x001a, "Colibri", {0x00,0x02,0x03,0x00}, "Suunto Ambit2 S", false, 0x0400 }, { SUUNTO_USB_VENDOR_ID, 0x0019, "Duck", {0x00,0x02,0x03,0x00}, "Suunto Ambit2", false, 0x0400 }, { SUUNTO_USB_VENDOR_ID, 0x001a, "Colibri", {0x00,0x02,0x02,0x00}, "Suunto Ambit2 S (up to 0.2.2)", false, 0x0200 }, @@ -220,8 +222,8 @@ int libambit_date_time_set(ambit_object_t *object, struct tm *tm) { - uint8_t date_data[8]; - uint8_t time_data[8] = { 0x09, 0x00, 0x01, 0x00 }; + uint8_t date_data[8] = { 0x00, 0x00, 0x00, 0x00, 0x28, 0x00, 0x00, 0x00 }; + uint8_t time_data[8]; int ret = -1; LOG_INFO("Writing date and time to clock"); @@ -230,9 +232,12 @@ *(uint16_t*)(&date_data[0]) = htole16(1900 + tm->tm_year); date_data[2] = 1 + tm->tm_mon; date_data[3] = tm->tm_mday; - memset(&date_data[4], 0, 4); // ????? Unknown data + // byte[4-7] unknown (but set to 0x28000000 in moveslink) - // Set time + // Set time (+date) + *(uint16_t*)(&time_data[0]) = htole16(1900 + tm->tm_year); + time_data[2] = 1 + tm->tm_mon; + time_data[3] = tm->tm_mday; time_data[4] = tm->tm_hour; time_data[5] = tm->tm_min; *(uint16_t*)(&time_data[6]) = htole16(1000*tm->tm_sec); diff -Nru openambit-0.2+20140602/src/libambit/libambit.h openambit-0.3/src/libambit/libambit.h --- openambit-0.2+20140602/src/libambit/libambit.h 2014-07-17 20:14:24.000000000 +0000 +++ openambit-0.3/src/libambit/libambit.h 2014-09-15 21:24:24.000000000 +0000 @@ -310,7 +310,7 @@ uint32_t heartrate_min_time; /* ms */ uint8_t peak_training_effect; /* effect scale 0.1 */ uint8_t activity_type; - char activity_name[16+1]; + char activity_name[16+1]; /* name of activity in ISO 8859-1 */ int16_t temperature_max; /* degree celsius scale 0.1 */ int16_t temperature_min; /* degree celsius scale 0.1 */ uint32_t temperature_max_time; /* ms */ diff -Nru openambit-0.2+20140602/src/openambit/CMakeLists.txt openambit-0.3/src/openambit/CMakeLists.txt --- openambit-0.2+20140602/src/openambit/CMakeLists.txt 2014-07-17 20:14:24.000000000 +0000 +++ openambit-0.3/src/openambit/CMakeLists.txt 2014-09-15 21:24:24.000000000 +0000 @@ -1,7 +1,7 @@ cmake_minimum_required(VERSION 2.8.5) project (OPENAMBIT) -set (OPENAMBIT_VERSION HEAD) +set (OPENAMBIT_VERSION 0.3) # Where to lookup modules set(CMAKE_MODULE_PATH "${OPENAMBIT_SOURCE_DIR}/cmake") @@ -21,8 +21,6 @@ ${CMAKE_SOURCE_DIR} ${CMAKE_CURRENT_BINARY_DIR} ${QT_QTCORE_INCLUDE_DIR} ${QT_QTGUI_INCLUDE_DIR} - ${QT_QTCORE_INCLUDE_DIR} - ${QT_QTGUI_INCLUDE_DIR} ${QT_QTNETWORK_INCLUDE_DIR} ${LIBAMBIT_INCLUDE_DIR} ) @@ -86,7 +84,7 @@ add_executable ( openambit ${openambit_SRCS} ${UIS} ${RSCS} ${TRS} ${MOCS} ) -target_link_libraries ( openambit ${QT_QTCORE_LIBRARY} ${QT_QTGUI_LIBRARY} ${QT_QTCORE_LIBRARY} ${QT_QTGUI_LIBRARY} ${QT_QTNETWORK_LIBRARY} ${LIBAMBIT_LIBS} ${UDEV_LIBS} ${ZLIB_LIBRARY} ${QJSON_LIBRARIES} ) +target_link_libraries ( openambit ${QT_QTCORE_LIBRARY} ${QT_QTGUI_LIBRARY} ${QT_QTNETWORK_LIBRARY} ${LIBAMBIT_LIBS} ${UDEV_LIBS} ${ZLIB_LIBRARY} ${QJSON_LIBRARIES} ) install ( TARGETS openambit DESTINATION ${CMAKE_INSTALL_BINDIR} ) install ( FILES ${OPENAMBIT_SOURCE_DIR}/deployment/99-suunto-ambit.rules diff -Nru openambit-0.2+20140602/src/openambit/debian/changelog openambit-0.3/src/openambit/debian/changelog --- openambit-0.2+20140602/src/openambit/debian/changelog 2014-07-17 20:14:24.000000000 +0000 +++ openambit-0.3/src/openambit/debian/changelog 2014-09-15 21:24:24.000000000 +0000 @@ -1,3 +1,9 @@ +openambit (0.3-1) trusty; urgency=medium + + * New upstream release + + -- Emil Ljungdahl Mon, 15 Sep 2014 23:10:37 +0200 + openambit (0.2-1) saucy; urgency=low * Initial release diff -Nru openambit-0.2+20140602/src/openambit/debian/control openambit-0.3/src/openambit/debian/control --- openambit-0.2+20140602/src/openambit/debian/control 2014-07-17 20:14:24.000000000 +0000 +++ openambit-0.3/src/openambit/debian/control 2014-09-15 21:24:24.000000000 +0000 @@ -2,7 +2,7 @@ Priority: extra Maintainer: Emil Ljungdahl Build-Depends: debhelper (>= 9), cmake (>= 2.8.5), libudev-dev, libqjson-dev, zlib1g-dev, - libambit-dev (= 0.2-1), libqt4-dev + libambit-dev (= 0.3-1), libqt4-dev Standards-Version: 3.9.4 Section: utils Homepage: http://openambit.org/ @@ -11,8 +11,7 @@ Package: openambit Architecture: any -Depends: libudev1, libqjson0, zlib1g, libambit0 (= 0.2-1), - libqt4-core, libqt4-gui, libqt4-network, ${shlibs:Depends}, ${misc:Depends} +Depends: libqt4-core, libqt4-gui, libqt4-network, ${shlibs:Depends}, ${misc:Depends} Description: Synchronize your Suunto Ambit series device Openambit is GUI application for synchronizing the Suunto Ambit series of devices with your PC and Suuntos cloudservice movescount.com diff -Nru openambit-0.2+20140602/src/openambit/deployment/99-suunto-ambit.rules openambit-0.3/src/openambit/deployment/99-suunto-ambit.rules --- openambit-0.2+20140602/src/openambit/deployment/99-suunto-ambit.rules 2014-07-17 20:14:24.000000000 +0000 +++ openambit-0.3/src/openambit/deployment/99-suunto-ambit.rules 2014-09-15 21:24:24.000000000 +0000 @@ -12,5 +12,11 @@ # Colibri (a.k.a Suunto Ambit2 S) SUBSYSTEMS=="usb", ATTRS{idVendor}=="1493", ATTRS{idProduct}=="001a", MODE="0666" +# Emu (a.k.a Suunto Ambit3 Peak) +SUBSYSTEMS=="usb", ATTRS{idVendor}=="1493", ATTRS{idProduct}=="001b", MODE="0666" + +# Finch (a.k.a Suunto Ambit3 Sport) +SUBSYSTEMS=="usb", ATTRS{idVendor}=="1493", ATTRS{idProduct}=="001c", MODE="0666" + # Greentit (a.k.a Suunto Ambit2 R) SUBSYSTEMS=="usb", ATTRS{idVendor}=="1493", ATTRS{idProduct}=="001d", MODE="0666" diff -Nru openambit-0.2+20140602/src/openambit/deployment/openambit.desktop openambit-0.3/src/openambit/deployment/openambit.desktop --- openambit-0.2+20140602/src/openambit/deployment/openambit.desktop 2014-07-17 20:14:24.000000000 +0000 +++ openambit-0.3/src/openambit/deployment/openambit.desktop 2014-09-15 21:24:24.000000000 +0000 @@ -1,5 +1,5 @@ [Desktop Entry] -Version=HEAD +Version=0.3 Type=Application Name=Openambit Comment=Open source synchronization for Suunto Ambit series diff -Nru openambit-0.2+20140602/src/openambit/logentry.cpp openambit-0.3/src/openambit/logentry.cpp --- openambit-0.2+20140602/src/openambit/logentry.cpp 2014-07-17 20:14:24.000000000 +0000 +++ openambit-0.3/src/openambit/logentry.cpp 2014-09-15 21:24:24.000000000 +0000 @@ -147,7 +147,7 @@ QString LogEntry::toHtml(){ QString log_html; - log_html += "

" + QString(this->logEntry->header.activity_name) + "

"; + log_html += "

" + QString::fromLatin1(this->logEntry->header.activity_name) + "

"; if (this->isUploaded()){ log_html += "see on movescount.com"; } diff -Nru openambit-0.2+20140602/src/openambit/logstore.cpp openambit-0.3/src/openambit/logstore.cpp --- openambit-0.2+20140602/src/openambit/logstore.cpp 2014-07-17 20:14:24.000000000 +0000 +++ openambit-0.3/src/openambit/logstore.cpp 2014-09-15 21:24:24.000000000 +0000 @@ -79,6 +79,7 @@ } sample_lap_event_type_t; static sample_lap_event_type_t sampleLapEventTypeNames[] = { + { 0x00, "Autolap" }, { 0x01, "Manual" }, { 0x14, "High Interval" }, { 0x15, "Low Interval" }, @@ -207,7 +208,7 @@ XMLReader reader(retEntry); if (!reader.read(&logfile)) { QString error = reader.errorString(); - qDebug() << error; + qDebug() << "Failed to read " << path << ": " << error; delete retEntry; retEntry = NULL; } @@ -626,7 +627,7 @@ logEntry->logEntry->header.activity_type = xml.readElementText().toUInt(); } else if (xml.name() == "Activity") { - QByteArray ba = xml.readElementText().toLocal8Bit(); + QByteArray ba = xml.readElementText().toLatin1(); const char *c_str = ba.data(); strncpy(logEntry->logEntry->header.activity_name, c_str, 16); logEntry->logEntry->header.activity_name[16] = 0; @@ -1371,7 +1372,7 @@ xml.writeEndElement(); xml.writeTextElement("PeakTrainingEffect", QString("%1").arg(logEntry->header.peak_training_effect)); xml.writeTextElement("ActivityType", QString("%1").arg(logEntry->header.activity_type)); - xml.writeTextElement("Activity", QString(logEntry->header.activity_name)); + xml.writeTextElement("Activity", QString::fromLatin1(logEntry->header.activity_name)); xml.writeStartElement("Temperature"); xml.writeTextElement("Max", QString("%1").arg(logEntry->header.temperature_max)); xml.writeTextElement("Min", QString("%1").arg(logEntry->header.temperature_min)); diff -Nru openambit-0.2+20140602/src/openambit/mainwindow.cpp openambit-0.3/src/openambit/mainwindow.cpp --- openambit-0.2+20140602/src/openambit/mainwindow.cpp 2014-07-17 20:14:24.000000000 +0000 +++ openambit-0.3/src/openambit/mainwindow.cpp 2014-09-15 21:24:24.000000000 +0000 @@ -119,9 +119,12 @@ { deviceWorkerThread.quit(); deviceWorkerThread.wait(); - delete deviceManager; + if (movesCount != NULL) { + movesCount->exit(); + } + delete trayIcon; delete trayIconMinimizeRestoreAction; delete trayIconSyncAction; @@ -382,7 +385,9 @@ if (current != NULL) { logEntry = logStore.read(current->data(Qt::UserRole).toString()); - ui->logDetail->setHtml(logEntry->toHtml()); + if (logEntry != NULL) { + ui->logDetail->setHtml(logEntry->toHtml()); + } delete logEntry; } diff -Nru openambit-0.2+20140602/src/openambit/movescount/CMakeLists.txt openambit-0.3/src/openambit/movescount/CMakeLists.txt --- openambit-0.2+20140602/src/openambit/movescount/CMakeLists.txt 2014-07-17 20:14:24.000000000 +0000 +++ openambit-0.3/src/openambit/movescount/CMakeLists.txt 2014-09-15 21:24:24.000000000 +0000 @@ -4,6 +4,7 @@ ${CMAKE_CURRENT_SOURCE_DIR}/movescountjson.cpp ${CMAKE_CURRENT_SOURCE_DIR}/movescountlogdirentry.cpp ${CMAKE_CURRENT_SOURCE_DIR}/movescountxml.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/movescountlogchecker.cpp PARENT_SCOPE ) @@ -13,5 +14,6 @@ ${CMAKE_CURRENT_SOURCE_DIR}/movescountjson.h ${CMAKE_CURRENT_SOURCE_DIR}/movescountlogdirentry.h ${CMAKE_CURRENT_SOURCE_DIR}/movescountxml.h + ${CMAKE_CURRENT_SOURCE_DIR}/movescountlogchecker.h PARENT_SCOPE ) diff -Nru openambit-0.2+20140602/src/openambit/movescount/movescount.cpp openambit-0.3/src/openambit/movescount/movescount.cpp --- openambit-0.2+20140602/src/openambit/movescount/movescount.cpp 2014-07-17 20:14:24.000000000 +0000 +++ openambit-0.3/src/openambit/movescount/movescount.cpp 2014-09-15 21:24:24.000000000 +0000 @@ -47,6 +47,24 @@ return m_Instance; } +void MovesCount::exit() +{ + static QMutex mutex; + + exiting = true; + + mutex.lock(); + if (m_Instance) { + workerThread.quit(); + workerThread.wait(); + + delete logChecker; + + m_Instance = NULL; + } + mutex.unlock(); +} + void MovesCount::setBaseAddress(QString baseAddress) { this->baseAddress = baseAddress; @@ -97,6 +115,134 @@ int MovesCount::getOrbitalData(u_int8_t **data) { int ret = -1; + + if (&workerThread == QThread::currentThread()) { + ret = getOrbitalDataInThread(data); + } + else { + QMetaObject::invokeMethod(this, "getOrbitalDataInThread", Qt::BlockingQueuedConnection, + Q_RETURN_ARG(int, ret), + Q_ARG(u_int8_t **, data)); + } + + return ret; +} + +int MovesCount::getPersonalSettings(ambit_personal_settings_t *settings) +{ + int ret = -1; + + if (&workerThread == QThread::currentThread()) { + ret = getPersonalSettingsInThread(settings); + } + else { + QMetaObject::invokeMethod(this, "getPersonalSettingsInThread", Qt::BlockingQueuedConnection, + Q_RETURN_ARG(int, ret), + Q_ARG(ambit_personal_settings_t *, settings)); + } + + return ret; +} + +void MovesCount::getDeviceSettings() +{ + QMetaObject::invokeMethod(this, "getDeviceSettingsInThread", Qt::AutoConnection); +} + +QList MovesCount::getMovescountEntries(QDate startTime, QDate endTime) +{ + QList retList; + + if (&workerThread == QThread::currentThread()) { + retList = getMovescountEntriesInThread(startTime, endTime); + } + else { + QMetaObject::invokeMethod(this, "getMovescountEntriesInThread", Qt::BlockingQueuedConnection, + Q_RETURN_ARG(QList, retList), + Q_ARG(QDate, startTime), + Q_ARG(QDate, endTime)); + } + + return retList; +} + +void MovesCount::checkAuthorization() +{ + QMetaObject::invokeMethod(this, "checkAuthorizationInThread", Qt::AutoConnection); +} + +void MovesCount::checkLatestFirmwareVersion() +{ + QMetaObject::invokeMethod(this, "checkLatestFirmwareVersionInThread", Qt::AutoConnection); +} + +void MovesCount::writePersonalSettings(ambit_personal_settings_t *settings) +{ + if (&workerThread == QThread::currentThread()) { + writePersonalSettingsInThread(settings); + } + else { + QMetaObject::invokeMethod(this, "writePersonalSettingsInThread", Qt::BlockingQueuedConnection, + Q_ARG(ambit_personal_settings_t *, settings)); + } +} + +void MovesCount::writeLog(LogEntry *logEntry) +{ + if (&workerThread == QThread::currentThread()) { + writeLogInThread(logEntry); + } + else { + QMetaObject::invokeMethod(this, "writeLogInThread", Qt::BlockingQueuedConnection, + Q_ARG(LogEntry *, logEntry)); + } +} + +void MovesCount::authCheckFinished() +{ + if (authCheckReply != NULL) { + checkReplyAuthorization(authCheckReply); + authCheckReply->deleteLater(); + authCheckReply = NULL; + } +} + +void MovesCount::firmwareReplyFinished() +{ + u_int8_t fw_version[4]; + + if (firmwareCheckReply != NULL) { + if (firmwareCheckReply->error() == QNetworkReply::NoError) { + QByteArray data = firmwareCheckReply->readAll(); + if (jsonParser.parseFirmwareVersionReply(data, fw_version) == 0) { + if (fw_version[0] > device_info.fw_version[0] || + (fw_version[0] == device_info.fw_version[0] && (fw_version[1] > device_info.fw_version[1] || + (fw_version[1] == device_info.fw_version[1] && ((fw_version[2] | (fw_version[3] << 8)) > (device_info.fw_version[2] | (device_info.fw_version[3] << 8))))))) { + emit newerFirmwareExists(QByteArray((const char*)fw_version, 4)); + } + } + } + + firmwareCheckReply->deleteLater(); + firmwareCheckReply = NULL; + } +} + +void MovesCount::recheckAuthorization() +{ + getDeviceSettings(); +} + +void MovesCount::handleAuthorizationSignal(bool authorized) +{ + if (authorized) { + logChecker->run(); + } +} + +int MovesCount::getOrbitalDataInThread(u_int8_t **data) +{ + int ret = -1; QNetworkReply *reply; reply = syncGET("/devices/gpsorbit/binary", "", false); @@ -118,13 +264,13 @@ return ret; } -int MovesCount::getPersonalSettings(ambit_personal_settings_t *settings) +int MovesCount::getPersonalSettingsInThread(ambit_personal_settings_t *settings) { Q_UNUSED(settings); return 0; } -int MovesCount::getDeviceSettings() +void MovesCount::getDeviceSettingsInThread() { QNetworkReply *reply; @@ -132,14 +278,10 @@ if (checkReplyAuthorization(reply)) { QByteArray _data = reply->readAll(); - - return 0; } - - return -1; } -QList MovesCount::getMovescountEntries(QDate startTime, QDate endTime) +QList MovesCount::getMovescountEntriesInThread(QDate startTime, QDate endTime) { QNetworkReply *reply; QList retList; @@ -158,7 +300,7 @@ return retList; } -void MovesCount::checkAuthorization() +void MovesCount::checkAuthorizationInThread() { if (authCheckReply == NULL) { authCheckReply = asyncGET("/members/private", "", true); @@ -166,7 +308,7 @@ } } -void MovesCount::checkLatestFirmwareVersion() +void MovesCount::checkLatestFirmwareVersionInThread() { if (firmwareCheckReply == NULL) { firmwareCheckReply = asyncGET("/devices/" + QString("%1/%2.%3.%4") @@ -178,12 +320,12 @@ } } -void MovesCount::writePersonalSettings(ambit_personal_settings_t *settings) +void MovesCount::writePersonalSettingsInThread(ambit_personal_settings_t *settings) { Q_UNUSED(settings); } -void MovesCount::writeLog(LogEntry *logEntry) +void MovesCount::writeLogInThread(LogEntry *logEntry) { QByteArray output; QNetworkReply *reply; @@ -204,53 +346,13 @@ } } -void MovesCount::authCheckFinished() -{ - if (authCheckReply != NULL) { - checkReplyAuthorization(authCheckReply); - authCheckReply->deleteLater(); - authCheckReply = NULL; - } -} - -void MovesCount::firmwareReplyFinished() -{ - u_int8_t fw_version[4]; - - if (firmwareCheckReply != NULL) { - if (firmwareCheckReply->error() == QNetworkReply::NoError) { - QByteArray data = firmwareCheckReply->readAll(); - if (jsonParser.parseFirmwareVersionReply(data, fw_version) == 0) { - if (fw_version[0] > device_info.fw_version[0] || - (fw_version[0] == device_info.fw_version[0] && (fw_version[1] > device_info.fw_version[1] || - (fw_version[1] == device_info.fw_version[1] && ((fw_version[2] | (fw_version[3] << 8)) > (device_info.fw_version[2] | (device_info.fw_version[3] << 8))))))) { - emit newerFirmwareExists(QByteArray((const char*)fw_version, 4)); - } - } - } - - firmwareCheckReply->deleteLater(); - firmwareCheckReply = NULL; - } -} - -void MovesCount::recheckAuthorization() -{ - getDeviceSettings(); -} - -void MovesCount::handleAuthorizationSignal(bool authorized) -{ - if (authorized) { - checkUploadedLogs(); - } -} - MovesCount::MovesCount() : - authorized(false), uploadedCheckRunning(false), firmwareCheckReply(NULL), authCheckReply(NULL) + exiting(false), authorized(false), firmwareCheckReply(NULL), authCheckReply(NULL) { this->manager = new QNetworkAccessManager(this); + this->logChecker = new MovesCountLogChecker(); + this->moveToThread(&workerThread); workerThread.start(); @@ -278,58 +380,6 @@ return authorized; } -void MovesCount::checkUploadedLogs() -{ - QDateTime firstUnknown = QDateTime::currentDateTime(); - QDateTime lastUnknown = QDateTime::fromTime_t(0); - QList missingEntries; - - if (!uploadedCheckRunning) { - uploadedCheckRunning = true; - - QList entries = logStore.dir(); - foreach(LogStore::LogDirEntry entry, entries) { - LogEntry *logEntry = logStore.read(entry); - if (logEntry->movescountId.length() == 0) { - missingEntries.append(logEntry); - if (logEntry->time < firstUnknown) { - firstUnknown = logEntry->time; - } - if (logEntry->time > lastUnknown) { - lastUnknown = logEntry->time; - } - } - else { - delete logEntry; - } - } - - if (missingEntries.count() > 0) { - QList movescountEntries = getMovescountEntries(firstUnknown.date(), lastUnknown.date()); - foreach(MovesCountLogDirEntry entry, movescountEntries) { - foreach(LogEntry *logEntry, missingEntries) { - if (entry.time == logEntry->time) { - missingEntries.removeOne(logEntry); - logStore.storeMovescountId(logEntry->device, logEntry->time, entry.moveId); - delete logEntry; - break; - } - } - } - - // Delete remaining entries - while (missingEntries.count() > 0) { - LogEntry *logEntry = missingEntries.first(); - writeLog(logEntry); - missingEntries.removeOne(logEntry); - delete logEntry; - } - } - - uploadedCheckRunning = false; - } -} - QNetworkReply *MovesCount::asyncGET(QString path, QString additionalHeaders, bool auth) { QNetworkRequest req; diff -Nru openambit-0.2+20140602/src/openambit/movescount/movescount.h openambit-0.3/src/openambit/movescount/movescount.h --- openambit-0.2+20140602/src/openambit/movescount/movescount.h 2014-07-17 20:14:24.000000000 +0000 +++ openambit-0.3/src/openambit/movescount/movescount.h 2014-09-15 21:24:24.000000000 +0000 @@ -35,12 +35,14 @@ #include "logstore.h" #include "movescountjson.h" #include "movescountlogdirentry.h" +#include "movescountlogchecker.h" class MovesCount : public QObject { Q_OBJECT public: static MovesCount* instance(); + void exit(); void setBaseAddress(QString baseAddress); void setAppkey(QString appkey); @@ -52,31 +54,40 @@ bool isAuthorized(); int getOrbitalData(u_int8_t **data); int getPersonalSettings(ambit_personal_settings_t *settings); - int getDeviceSettings(); + void getDeviceSettings(); QList getMovescountEntries(QDate startTime, QDate endTime); -signals: - void newerFirmwareExists(QByteArray fw_version); - void movesCountAuth(bool authorized); - void logMoveID(QString device, QDateTime time, QString moveID); - -public slots: + void checkAuthorization(); void checkLatestFirmwareVersion(); void writePersonalSettings(ambit_personal_settings_t *settings); void writeLog(LogEntry *logEntry); +signals: + void newerFirmwareExists(QByteArray fw_version); + void movesCountAuth(bool authorized); + void logMoveID(QString device, QDateTime time, QString moveID); + private slots: void authCheckFinished(); void firmwareReplyFinished(); void recheckAuthorization(); void handleAuthorizationSignal(bool authorized); + int getOrbitalDataInThread(u_int8_t **data); + int getPersonalSettingsInThread(ambit_personal_settings_t *settings); + void getDeviceSettingsInThread(); + QList getMovescountEntriesInThread(QDate startTime, QDate endTime); + + void checkAuthorizationInThread(); + void checkLatestFirmwareVersionInThread(); + void writePersonalSettingsInThread(ambit_personal_settings_t *settings); + void writeLogInThread(LogEntry *logEntry); + private: MovesCount(); ~MovesCount(); bool checkReplyAuthorization(QNetworkReply *reply); - void checkUploadedLogs(); QNetworkReply *asyncGET(QString path, QString additionalHeaders, bool auth); QNetworkReply *syncGET(QString path, QString additionalHeaders, bool auth); @@ -84,8 +95,8 @@ QNetworkReply *asyncPOST(QString path, QString additionalHeaders, QByteArray &postData, bool auth); QNetworkReply *syncPOST(QString path, QString additionalHeaders, QByteArray &postData, bool auth); + bool exiting; bool authorized; - bool uploadedCheckRunning; QString baseAddress; QString appkey; @@ -103,6 +114,8 @@ LogStore logStore; + MovesCountLogChecker *logChecker; + QThread workerThread; }; diff -Nru openambit-0.2+20140602/src/openambit/movescount/movescountjson.cpp openambit-0.3/src/openambit/movescount/movescountjson.cpp --- openambit-0.2+20140602/src/openambit/movescount/movescountjson.cpp 2014-07-17 20:14:24.000000000 +0000 +++ openambit-0.3/src/openambit/movescount/movescountjson.cpp 2014-09-15 21:24:24.000000000 +0000 @@ -175,6 +175,14 @@ } case ambit_log_sample_type_lapinfo: switch (sample->u.lapinfo.event_type) { + case 0x00: /* autolap = 5 */ + { + QVariantMap tmpMap; + tmpMap.insert("LocalTime", dateTimeString(localBaseTime.addMSecs(sample->time))); + tmpMap.insert("Type", 5); + marksContent.append(tmpMap); + break; + } case 0x01: /* manual = 0 */ case 0x16: /* interval = 0 */ { diff -Nru openambit-0.2+20140602/src/openambit/movescount/movescountlogchecker.cpp openambit-0.3/src/openambit/movescount/movescountlogchecker.cpp --- openambit-0.2+20140602/src/openambit/movescount/movescountlogchecker.cpp 1970-01-01 00:00:00.000000000 +0000 +++ openambit-0.3/src/openambit/movescount/movescountlogchecker.cpp 2014-09-15 21:24:24.000000000 +0000 @@ -0,0 +1,107 @@ +#include "movescountlogchecker.h" +#include "movescount.h" + +MovesCountLogChecker::MovesCountLogChecker(QObject *parent) : + QObject(parent), running(false), cancelRun(false) +{ + this->moveToThread(&workerThread); + workerThread.start(); +} + +MovesCountLogChecker::~MovesCountLogChecker() +{ + cancel(); + workerThread.exit(); + workerThread.wait(); +} + +void MovesCountLogChecker::run() +{ + if (!running) { + QMetaObject::invokeMethod(this, "checkUploadedLogs", Qt::AutoConnection); + } +} + +bool MovesCountLogChecker::isRunning() +{ + return running; +} + +void MovesCountLogChecker::cancel() +{ + cancelRun = true; +} + +void MovesCountLogChecker::checkUploadedLogs() +{ + QDateTime firstUnknown = QDateTime::currentDateTime(); + QDateTime lastUnknown = QDateTime::fromTime_t(0); + QList missingEntries; + MovesCount *movescount = MovesCount::instance(); + + running = true; + + QList entries = logStore.dir(); + foreach(LogStore::LogDirEntry entry, entries) { + // This is a long operation, exit if application want to quit + if (cancelRun) { + cancelRun = false; + return; + } + LogEntry *logEntry = logStore.read(entry); + if (logEntry != NULL) { + if (logEntry->movescountId.length() == 0) { + missingEntries.append(logEntry); + if (logEntry->time < firstUnknown) { + firstUnknown = logEntry->time; + } + if (logEntry->time > lastUnknown) { + lastUnknown = logEntry->time; + } + } + else { + delete logEntry; + } + } + } + + if (missingEntries.count() > 0) { + // This is a long operation, exit if application want to quit + if (cancelRun) { + cancelRun = false; + return; + } + QList movescountEntries = movescount->getMovescountEntries(firstUnknown.date(), lastUnknown.date()); + foreach(MovesCountLogDirEntry entry, movescountEntries) { + // This is a long operation, exit if application want to quit + if (cancelRun) { + cancelRun = false; + return; + } + foreach(LogEntry *logEntry, missingEntries) { + if (entry.time == logEntry->time) { + missingEntries.removeOne(logEntry); + logStore.storeMovescountId(logEntry->device, logEntry->time, entry.moveId); + delete logEntry; + break; + } + } + } + + // Delete remaining entries + while (missingEntries.count() > 0) { + // This is a long operation, exit if application want to quit + if (cancelRun) { + cancelRun = false; + return; + } + LogEntry *logEntry = missingEntries.first(); + movescount->writeLog(logEntry); + missingEntries.removeOne(logEntry); + delete logEntry; + } + } + + cancelRun = false; + running = false; +} diff -Nru openambit-0.2+20140602/src/openambit/movescount/movescountlogchecker.h openambit-0.3/src/openambit/movescount/movescountlogchecker.h --- openambit-0.2+20140602/src/openambit/movescount/movescountlogchecker.h 1970-01-01 00:00:00.000000000 +0000 +++ openambit-0.3/src/openambit/movescount/movescountlogchecker.h 2014-09-15 21:24:24.000000000 +0000 @@ -0,0 +1,32 @@ +#ifndef MOVESCOUNTLOGCHECKER_H +#define MOVESCOUNTLOGCHECKER_H + +#include +#include + +#include + +#include "logentry.h" +#include "logstore.h" + +class MovesCountLogChecker : public QObject +{ + Q_OBJECT +public: + explicit MovesCountLogChecker(QObject *parent = 0); + ~MovesCountLogChecker(); + void run(); + bool isRunning(); + void cancel(); +private slots: + void checkUploadedLogs(); + +private: + bool running; + bool cancelRun; + + LogStore logStore; + QThread workerThread; +}; + +#endif // MOVESCOUNTLOGCHECKER_H diff -Nru openambit-0.2+20140602/src/openambit/movescount/movescountxml.cpp openambit-0.3/src/openambit/movescount/movescountxml.cpp --- openambit-0.2+20140602/src/openambit/movescount/movescountxml.cpp 2014-07-17 20:14:24.000000000 +0000 +++ openambit-0.3/src/openambit/movescount/movescountxml.cpp 2014-09-15 21:24:24.000000000 +0000 @@ -42,6 +42,7 @@ }; static typename_lookup_entry_t sampleLapEventTypeNames[] = { + { 0x00, "Distance" }, { 0x01, "Manual" }, { 0x14, "High Interval" }, { 0x15, "Low Interval" }, @@ -240,7 +241,7 @@ xml.writeTextElement("PeakTrainingEffect", QString::number((double)logEntry->logEntry->header.peak_training_effect/10.0, 'g', 16)); } xml.writeTextElement("ActivityType", QString("%1").arg(logEntry->logEntry->header.activity_type)); - xml.writeTextElement("Activity", QString(logEntry->logEntry->header.activity_name)); + xml.writeTextElement("Activity", QString::fromLatin1(logEntry->logEntry->header.activity_name)); xml.writeStartElement("Temperature"); xml.writeTextElement("Max", QString::number((double)logEntry->logEntry->header.temperature_max/10.0 + 273.15, 'g', 16)); xml.writeTextElement("Min", QString::number((double)logEntry->logEntry->header.temperature_min/10.0 + 273.15, 'g', 16)); diff -Nru openambit-0.2+20140602/tools/openambit2gpx.py openambit-0.3/tools/openambit2gpx.py --- openambit-0.2+20140602/tools/openambit2gpx.py 1970-01-01 00:00:00.000000000 +0000 +++ openambit-0.3/tools/openambit2gpx.py 2014-09-15 21:24:24.000000000 +0000 @@ -0,0 +1,263 @@ +#!/usr/bin/python + +""" concerts the *.log files produced by openambit in ~/.openambit/ to standard gpx format. +usage: ./openambit2gpx.py inputfile outputFile +""" + +#from lxml import etree # does not allow namespace prefixes which are required for gpx extensions; everything else in this script would work otherwise with lxml +import xml.etree.ElementTree as etree +import math +import sys + +############################## +## getting input parameters ## +############################## + +fileIn=sys.argv[1] +fileOut=sys.argv[2] + + +########################################### +## setting variables up, starting output ## +########################################### + +fOut=open(fileOut, 'w') + +fOut.write("\n\n") +fOut.write('') +fOut.write(" \n") +fOut.write(" \n") + +rootIn=etree.parse(fileIn) + +latLast=None +lonLast=None +timeLast=None +altitudeLast=None +hrLast=None +cadenceLast=None +speedLast=None +tempLast=None +airpressureLast=None +latLatest=None +lonLatest=None +timeGPSLatest=None + +lapCount=0 +lapArray=[0] +maxLap=0 + +def utcSplitConvSeconds(utcTime): + """ Splits the UTC time code YYYY-MM-DDTHH:MM:SS.SSSZ, keeps only the time part and converts it into seconds. + """ + + import math + tmpTime=utcTime.split("T")[1].split("Z")[0].split(":") + tmpDay=int(utcTime.split("T")[0].split("-")[2]) + secs=float(tmpDay)*24*3600 + float(tmpTime[0])*3600 + float(tmpTime[1])*60 + float(tmpTime[2]) + + return secs + +def timeDiff(utcTime1,utcTime2): + """ Computes the difference, in seconds, between an earlier (utcTime1) and a later date (utcTime2). Only safe for dates within the same month or less than 2 days apart if on the boundary of a month. + """ + + secs1=utcSplitConvSeconds(utcTime1) + secs2=utcSplitConvSeconds(utcTime2) + + if int(utcTime2.split("T")[0].split("-")[2])==1: + secs1-=float(utcTime2.split("T")[0].split("-")[2])*24*3600 # if second date is on a first of a month, then the previous day gets reset to day 0 of the same month + + return secs2-secs1 + + +########################### +## getting activity data ## +########################### + +for element in rootIn.iterfind("Log/Samples/Sample"): + trk=etree.Element("trkpt") + + lat=element.findtext("Latitude") + lon=element.findtext("Longitude") + time=element.findtext("UTC") + + altitude=element.findtext("Altitude") if element.findtext("Altitude")!=None else altitudeLast + hr=element.findtext("HR") if element.findtext("HR")!=None else hrLast + cadence=element.findtext("Cadence") if element.findtext("cadence")!=None else cadenceLast + speed=element.findtext("Speed") if element.findtext("Speed")!=None else speedLast + temp=str(float(element.findtext("Temperature"))/10) if element.findtext("Temperature")!=None else tempLast + airpressure=element.findtext("SeaLevelPressure") if element.findtext("SeaLevelPressure")!=None else airpressureLast + + sampType=element.findtext("Type") + if sampType=="lap-info": + lapType=element.findtext("Lap/Type") + lapDate=element.findtext("Lap/DateTime") + lapDuration=element.findtext("Lap/Duration") + lapDistance=element.findtext("Lap/Distance") + lapUtc=element.findtext("UTC") + lapPreviousLat=latLatest + lapPreviousLon=lonLatest + lapPreviousTime=timeGPSLatest + lapCheck=1 + + if lapCount==0: + lapArray[0]=[lapType,lapDate,lapDuration,lapDistance,lapUtc,lapPreviousLat,lapPreviousLon,lapPreviousTime,0,0,0] + else: + lapArray.append([lapType,lapDate,lapDuration,lapDistance,lapUtc,lapPreviousLat,lapPreviousLon,lapPreviousTime,0,0,0]) + lapCount+=1 + + maxLap=lapCount-1 + + if lat!=None and lon!=None: + lat=float(lat)/10000000 + lon=float(lon)/10000000 + + trk.set("lat",str(lat)) + trk.set("lon",str(lon)) + + latLatest=str(lat) + lonLatest=str(lon) + timeGPSLatest=time + + if lapCheck==1: + lapArray[lapCount-1][8]=latLatest + lapArray[lapCount-1][9]=lonLatest + lapArray[lapCount-1][10]=timeGPSLatest + lapCheck=0 + + if altitude!=None: + etree.SubElement(trk,"ele").text=altitude + elif altitudeLast!=None: + etree.SubElement(trk,"ele").text=altitudeLast + + if time!=None: + etree.SubElement(trk,"time").text=time + elif timeLast!=None: + etree.SubElement(trk,"time").text=timeLast + + if hr!=None or cadence!=None or speed!=None or temp!=None or airpressure!=None: + extGpx=etree.SubElement(trk,"extensions") + if hr!=None: etree.SubElement(extGpx,"gpxdata:hr").text=hr + if cadence!=None: etree.SubElement(extGpx,"gpxdata:cadence").text=cadence + if temp!=None: etree.SubElement(extGpx,"gpxdata:atemp").text=temp + if speed!=None: etree.SubElement(extGpx,"gpxdata:speed").text=speed + if airpressure!=None: etree.SubElement(extGpx,"gpxdata:SeaLevelPressure").text=airpressure + + fOut.write(" "+etree.tostring(trk)+"\n") + + latLast=lat + lonLast=lon + timeLast=time + altitudeLast=altitude + hrLast=hr + cadenceLast=cadence + speedLast=speed + tempLast=temp + airpressureLast=airpressure + + lat=None + lon=None + time=None + altitude=None + hr=None + cadence=None + speed=None + temp=None + airpressure=None + +fOut.write(" \n") +fOut.write(" \n") + + +############################# +## getting lap information ## +############################# + +lapCount=0 +previousEndTime=0 + +fOut.write(" \n") + +for i in range(0,len(lapArray)): + if lapArray[i][0]=='Manual': + lap=etree.Element("gpxdata:lap") + lap.set("xmlns","http://www.cluetrust.com/XML/GPXDATA/1/0") + + startTime=lapArray[0][4] if lapCount==0 else previousEndTime + previousEndTime=lapArray[i][4] + + etree.SubElement(lap,'index').text=str(lapCount) + etree.SubElement(lap,'startTime').text=startTime + etree.SubElement(lap,'elapsedTime').text=str(float(lapArray[i][2])/1000) + etree.SubElement(lap,'distance').text=lapArray[i][3] + + latInterPolSP=lapArray[0][8] if lapCount==0 else previousLatEP + lonInterPolSP=lapArray[0][9] if lapCount==0 else previousLonEP + + if i==maxLap: + latInterPolEP=lapArray[i][5] + else: + t=lapArray[i][4] + t1=lapArray[i][7] + t2=lapArray[i][10] + lat1=float(lapArray[i][5]) + lat2=float(lapArray[i][8]) + latInterPolEP=str( ((lat2-lat1)/timeDiff(t1,t2))*timeDiff(t1,t) + lat1 ) + if i==maxLap: + lonInterPolEP=lapArray[i][6] + else: + t=lapArray[i][4] + t1=lapArray[i][7] + t2=lapArray[i][10] + lon1=float(lapArray[i][6]) + lon2=float(lapArray[i][9]) + lonInterPolEP=str( ((lon2-lon1)/timeDiff(t1,t2))*timeDiff(t1,t) + lon1 ) + previousLatEP=latInterPolEP + previousLonEP=lonInterPolEP + SP=etree.SubElement(lap,'startPoint') + SP.text=' ' + SP.set('lat',str(latInterPolSP)) + SP.set('lon',str(lonInterPolSP)) + EP=etree.SubElement(lap,'endPoint') + EP.text=' ' + EP.set('lat',str(latInterPolEP)) + EP.set('lon',str(lonInterPolEP)) + + etree.SubElement(lap,'intensity').text='active' + trigger=etree.SubElement(lap,'trigger') + trigger.text=' ' + trigger.set('kind','manual') + + #the elements below need to be added for the gpx file to be understood; data all set to 0 + etree.SubElement(lap,'calories').text='0' + sumHrAvg=etree.SubElement(lap,'summary') + sumHrAvg.text='0' + sumHrAvg.set('kind','avg') + sumHrAvg.set('name','hr') + sumHrMax=etree.SubElement(lap,'summary') + sumHrMax.text='0' + sumHrMax.set('kind','max') + sumHrMax.set('name','hr') + sumCadAvg=etree.SubElement(lap,'summary') + sumCadAvg.text='0' + sumCadAvg.set('kind','avg') + sumCadAvg.set('name','cadence') + sumSpeedMax=etree.SubElement(lap,'summary') + sumSpeedMax.text='0' + sumSpeedMax.set('kind','max') + sumSpeedMax.set('name','speed') + + lapCount+=1 + + fOut.write(" "+etree.tostring(lap)+"\n") + +fOut.write(" \n") + + +######################### +## closing output file ## +######################### + +fOut.write("\n") +fOut.close() diff -Nru openambit-0.2+20140602/wireshark_dissector/ambit-dissector.c openambit-0.3/wireshark_dissector/ambit-dissector.c --- openambit-0.2+20140602/wireshark_dissector/ambit-dissector.c 2014-07-17 20:14:24.000000000 +0000 +++ openambit-0.3/wireshark_dissector/ambit-dissector.c 2014-09-15 21:24:24.000000000 +0000 @@ -14,9 +14,16 @@ guint32 frame_total; guint32 size; unsigned char *data; - guint32 log_entry_size; - unsigned char *log_entry; - guint32 log_start_frame; + struct { + guint32 entry_size; + unsigned char *entry; + guint32 start_frame; + } log; + struct { + guint32 entry_size; + unsigned char *entry; + guint32 start_frame; + } log_header; } ambit_reassembly_entry_t; typedef struct ambit_protocol_type { @@ -50,6 +57,16 @@ static gint dissect_ambit_log_data_reply(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, void *data _U_); static gint dissect_ambit_log_data_content(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, void *data _U_, guint32 offset, guint32 length); static gint dissect_ambit_log_data_sample(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, void *data _U_, guint32 offset, guint32 length, guint32 *sampleno, guint32 *periodic_sample_specifier); +static gint dissect_ambit_lock_status_get(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, void *data _U_); +static gint dissect_ambit_lock_status_reply(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, void *data _U_); +static gint dissect_ambit_lock_set(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, void *data _U_); +static gint dissect_ambit_lock_set_reply(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, void *data _U_); + +static gint dissect_ambit3_settings_get(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, void *data _U_); +static gint dissect_ambit3_settings_reply(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, void *data _U_); +static gint dissect_ambit3_log_headers_get(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, void *data _U_); +static gint dissect_ambit3_log_headers_reply(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, void *data _U_); +static gint dissect_ambit3_log_headers_content(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, void *data _U_, guint32 offset, guint32 length); /* protocols and header fields */ #define D_AMBIT_USBID 0x3f @@ -62,6 +79,7 @@ static int hf_ambit_msgseg = -1; static int hf_ambit_msgheaderchksum = -1; static int hf_ambit_requestcmd = -1; +static int hf_ambit_pktformat = -1; static int hf_ambit_pktseqno = -1; static int hf_ambit_pktlen = -1; static int hf_ambit_payloadchksum = -1; @@ -145,10 +163,12 @@ static int hf_ambit_log_header_battery_start = -1; static int hf_ambit_log_header_battery_stop = -1; static int hf_ambit_log_header_distance_before_calib = -1; +static int hf_ambit_log_header_synced = -1; static int hf_ambit_log_header_more = -1; static int hf_ambit_log_count = -1; +static int hf_ambit_unsynced_log_count = -1; static int hf_ambit_log_data_addr_frame_ref = -1; static int hf_ambit_log_data_address = -1; @@ -225,6 +245,8 @@ static gint ett_ambit_log_data = -1; static gint ett_ambit_log_samples = -1; static gint ett_ambit_log_sample = -1; +static gint ett_ambit3_log_headers = -1; +static gint ett_ambit3_log_header = -1; static ambit_reassembly_entry_t *reassembly_entries = NULL; static guint32 reassembly_entries_alloc = 0; @@ -309,6 +331,16 @@ { 0x0b0b0a00, "Log header reply", dissect_ambit_log_header_reply }, { 0x0b170500, "Get log data", dissect_ambit_log_data_get }, { 0x0b170a00, "Log data reply", dissect_ambit_log_data_reply }, + { 0x0b190500, "Get lock status", dissect_ambit_lock_status_get }, + { 0x0b190a00, "Lock status reply", dissect_ambit_lock_status_reply }, + { 0x0b1a0500, "Lock set", dissect_ambit_lock_set }, + { 0x0b1a0a00, "Lock set reply", dissect_ambit_lock_set_reply }, + { 0x11000500, "Ambit3 - Get settings", dissect_ambit3_settings_get }, + { 0x11000a00, "Ambit3 - Settings reply", dissect_ambit3_settings_reply }, + { 0x11010500, "Ambit3 - Write settings", dissect_ambit3_settings_reply }, + { 0x11010a00, "Ambit3 - Settings write reply", dissect_ambit3_settings_get }, + { 0x12000500, "Ambit3 - Get log headers", dissect_ambit3_log_headers_get }, + { 0x12000a00, "Ambit3 - Log headers reply", dissect_ambit3_log_headers_reply }, { 0, NULL, NULL } }; @@ -353,10 +385,13 @@ static gint dissect_ambit_time_write(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, void *data _U_) { gint offset = 0; + guint16 year = tvb_get_letohs(tvb, 0); + guint8 month = tvb_get_guint8(tvb, 2); + guint8 day = tvb_get_guint8(tvb, 3); guint8 hour = tvb_get_guint8(tvb, 4); guint8 minute = tvb_get_guint8(tvb, 5); guint16 seconds = tvb_get_letohs(tvb, 6) / 1000; - dissect_ambit_add_unknown(tvb, pinfo, tree, offset, 4); + proto_tree_add_string_format_value(tree, hf_ambit_date, tvb, offset, 4, "Date", "%04d-%02d-%02d", year, month, day); offset += 4; proto_tree_add_string_format_value(tree, hf_ambit_time, tvb, offset, 4, "Time", "%02d:%02d:%02d", hour, minute, seconds); offset += 4; @@ -968,7 +1003,7 @@ if (offset + 4 >= length) return offset; proto_tree_add_item(tree, hf_ambit_log_header_distance_before_calib, tvb, offset, 4, ENC_LITTLE_ENDIAN); offset += 4; - if (header_1_len == 913) { /* Long header */ + if (header_1_len >= 913) { /* Long header */ if (offset + 24 >= length) return offset; dissect_ambit_add_unknown(tvb, pinfo, tree, offset, 24); offset += 24; @@ -1477,6 +1512,149 @@ return 0; } +static gint dissect_ambit_lock_status_get(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, void *data _U_) +{ +} + +static gint dissect_ambit_lock_status_reply(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, void *data _U_) +{ +} + +static gint dissect_ambit_lock_set(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, void *data _U_) +{ +} + +static gint dissect_ambit_lock_set_reply(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, void *data _U_) +{ +} + +static gint dissect_ambit3_settings_get(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, void *data _U_) +{ +} + +static gint dissect_ambit3_settings_reply(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, void *data _U_) +{ +} + +static gint dissect_ambit3_log_headers_get(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, void *data _U_) +{ + dissect_ambit_add_unknown(tvb, pinfo, tree, 0, 18); +} + +static gint dissect_ambit3_log_headers_reply(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, void *data _U_) +{ + dissect_ambit_add_unknown(tvb, pinfo, tree, 0, 6); +} + +static gint dissect_ambit3_log_headers_content(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, void *data _U_, guint32 offset, guint32 length) +{ + gint namelen = 0; + guint16 log_count = 0; + guint16 log_cntr = 0; + gint header_len = 0; + proto_item *logs_ti = NULL; + proto_tree *logs_tree = NULL; + proto_item *log_ti = NULL; + proto_tree *log_tree = NULL; + dissect_ambit_add_unknown(tvb, pinfo, tree, offset, 10); + offset += 10; + log_count = tvb_get_letohs(tvb, offset); + proto_tree_add_item(tree, hf_ambit_log_count, tvb, offset, 2, ENC_LITTLE_ENDIAN); + offset += 2; + dissect_ambit_add_unknown(tvb, pinfo, tree, offset, 2); + offset += 2; + proto_tree_add_item(tree, hf_ambit_unsynced_log_count, tvb, offset, 2, ENC_LITTLE_ENDIAN); + offset += 2; + logs_ti = proto_tree_add_text(tree, tvb, 0, 0, "Logs"); + logs_tree = proto_item_add_subtree(logs_ti, ett_ambit3_log_headers); + while (offset + 2 < length) { + header_len = tvb_get_guint8(tvb, offset+1); + log_cntr++; + log_ti = proto_tree_add_text(logs_tree, tvb, offset, header_len + 2, "Header #%u", log_cntr); + log_tree = proto_item_add_subtree(log_ti, ett_ambit3_log_header); + dissect_ambit_add_unknown(tvb, pinfo, log_tree, offset, 1); + offset += 1; + proto_tree_add_item(log_tree, hf_ambit_log_header_length, tvb, offset, 1, ENC_LITTLE_ENDIAN); + offset += 1; + if (offset + header_len <= length) { + proto_tree_add_item(log_tree, hf_ambit_time, tvb, offset, 20, ENC_LITTLE_ENDIAN); + offset += 20; + proto_tree_add_item(log_tree, hf_ambit_log_header_synced, tvb, offset, 1, ENC_LITTLE_ENDIAN); + offset += 1; + proto_tree_add_item(log_tree, hf_ambit_log_data_address, tvb, offset, 4, ENC_LITTLE_ENDIAN); + offset += 4; + proto_tree_add_item(log_tree, hf_ambit_log_data_next_addr, tvb, offset, 4, ENC_LITTLE_ENDIAN); + offset += 4; + dissect_ambit_add_unknown(tvb, pinfo, log_tree, offset, 8); + offset += 8; + proto_tree_add_item(log_tree, hf_ambit_log_header_hr_min, tvb, offset, 1, ENC_LITTLE_ENDIAN); + offset += 1; + proto_tree_add_item(log_tree, hf_ambit_log_header_hr_avg, tvb, offset, 1, ENC_LITTLE_ENDIAN); + offset += 1; + proto_tree_add_item(log_tree, hf_ambit_log_header_hr_max, tvb, offset, 1, ENC_LITTLE_ENDIAN); + offset += 1; + proto_tree_add_item(log_tree, hf_ambit_log_header_hr_min_time, tvb, offset, 4, ENC_LITTLE_ENDIAN); + offset += 4; + proto_tree_add_item(log_tree, hf_ambit_log_header_hr_max_time, tvb, offset, 4, ENC_LITTLE_ENDIAN); + offset += 4; + dissect_ambit_add_unknown(tvb, pinfo, log_tree, offset, 2); + offset += 2; + proto_tree_add_item(log_tree, hf_ambit_log_header_temp_min_time, tvb, offset, 4, ENC_LITTLE_ENDIAN); + offset += 4; + proto_tree_add_item(log_tree, hf_ambit_log_header_temp_max_time, tvb, offset, 4, ENC_LITTLE_ENDIAN); + offset += 4; + proto_tree_add_item(log_tree, hf_ambit_log_header_altitude_min, tvb, offset, 2, ENC_LITTLE_ENDIAN); + offset += 2; + proto_tree_add_item(log_tree, hf_ambit_log_header_altitude_max, tvb, offset, 2, ENC_LITTLE_ENDIAN); + offset += 2; + proto_tree_add_item(log_tree, hf_ambit_log_header_alt_min_time, tvb, offset, 4, ENC_LITTLE_ENDIAN); + offset += 4; + proto_tree_add_item(log_tree, hf_ambit_log_header_alt_max_time, tvb, offset, 4, ENC_LITTLE_ENDIAN); + offset += 4; + proto_tree_add_item(log_tree, hf_ambit_log_header_cadence_avg, tvb, offset, 1, ENC_LITTLE_ENDIAN); + offset += 1; + proto_tree_add_item(log_tree, hf_ambit_log_header_cadence_max, tvb, offset, 1, ENC_LITTLE_ENDIAN); + offset += 1; + proto_tree_add_item(log_tree, hf_ambit_log_header_cadence_max_time, tvb, offset, 4, ENC_LITTLE_ENDIAN); + offset += 4; + proto_tree_add_item(log_tree, hf_ambit_log_header_avg_speed, tvb, offset, 2, ENC_LITTLE_ENDIAN); + offset += 2; + proto_tree_add_item(log_tree, hf_ambit_log_header_max_speed, tvb, offset, 2, ENC_LITTLE_ENDIAN); + offset += 2; + proto_tree_add_item(log_tree, hf_ambit_log_header_speed_max_time, tvb, offset, 4, ENC_LITTLE_ENDIAN); + offset += 4; + dissect_ambit_add_unknown(tvb, pinfo, log_tree, offset, 4); + offset += 4; + proto_tree_add_item(log_tree, hf_ambit_log_header_duration, tvb, offset, 4, ENC_LITTLE_ENDIAN); + offset += 4; + proto_tree_add_item(log_tree, hf_ambit_log_header_ascent, tvb, offset, 2, ENC_LITTLE_ENDIAN); + offset += 2; + proto_tree_add_item(log_tree, hf_ambit_log_header_descent, tvb, offset, 2, ENC_LITTLE_ENDIAN); + offset += 2; + proto_tree_add_item(log_tree, hf_ambit_log_header_ascent_time, tvb, offset, 4, ENC_LITTLE_ENDIAN); + offset += 4; + proto_tree_add_item(log_tree, hf_ambit_log_header_descent_time, tvb, offset, 4, ENC_LITTLE_ENDIAN); + offset += 4; + proto_tree_add_item(log_tree, hf_ambit_log_header_recovery, tvb, offset, 2, ENC_LITTLE_ENDIAN); + offset += 2; + proto_tree_add_item(log_tree, hf_ambit_log_header_peak_effect, tvb, offset, 1, ENC_LITTLE_ENDIAN); + offset += 1; + namelen = 0; + while (tvb_get_guint8(tvb, offset+namelen) != 0) { + namelen++; + } + proto_tree_add_item(log_tree, hf_ambit_log_header_activity_name, tvb, offset, namelen+1, ENC_LITTLE_ENDIAN); + offset += namelen + 1; + proto_tree_add_item(log_tree, hf_ambit_log_header_distance, tvb, offset, 4, ENC_LITTLE_ENDIAN); + offset += 4; + proto_tree_add_item(log_tree, hf_ambit_log_header_energy, tvb, offset, 2, ENC_LITTLE_ENDIAN); + offset += 2; + dissect_ambit_add_unknown(tvb, pinfo, log_tree, offset, 26); + offset += 26; + } + } +} + static gint dissect_ambit(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, void *data _U_) { @@ -1488,7 +1666,7 @@ guint16 msg_count = tvb_get_letohs(tvb, 4); guint32 command = tvb_get_ntohl(tvb, 8); guint32 pkt_len = tvb_get_letohl(tvb, 16); - tvbuff_t *new_tvb = NULL, *next_tvb = NULL, *log_tvb = NULL; + tvbuff_t *new_tvb = NULL, *next_tvb = NULL, *log_tvb = NULL, *log_header_tvb = NULL; static guint32 fragments_start_frame; static guint16 fragments_offset; static guint16 fragments_data_len; @@ -1500,6 +1678,9 @@ static guint32 log_after_end_of_use = 0xffffffff; static guint32 log_last_known_address; + static guint32 current_log_header_start_frame = 0xffffffff; + static guint32 current_log_header_end_found = 0; + if (usbid == D_AMBIT_USBID) { if (msg_part == 0x5d) { data_len = data_header_len - 12; @@ -1581,13 +1762,13 @@ if (b == 'M' && c == 'E' && d == 'M') { if (current_log_start_frame != 0xffffffff) { - reassembly_entries[current_log_start_frame].log_entry = g_realloc(reassembly_entries[current_log_start_frame].log_entry, reassembly_entries[current_log_start_frame].log_entry_size + i); - tvb_memcpy(tvb, &reassembly_entries[current_log_start_frame].log_entry[reassembly_entries[current_log_start_frame].log_entry_size], data_offset, i); - reassembly_entries[current_log_start_frame].log_entry_size += i; + reassembly_entries[current_log_start_frame].log.entry = g_realloc(reassembly_entries[current_log_start_frame].log.entry, reassembly_entries[current_log_start_frame].log.entry_size + i); + tvb_memcpy(tvb, &reassembly_entries[current_log_start_frame].log.entry[reassembly_entries[current_log_start_frame].log.entry_size], data_offset, i); + reassembly_entries[current_log_start_frame].log.entry_size += i; } - reassembly_entries[pinfo->fd->num].log_entry = (unsigned char*)g_malloc(data_len-i); - tvb_memcpy(tvb, reassembly_entries[pinfo->fd->num].log_entry, data_offset+i, data_len-i); - reassembly_entries[pinfo->fd->num].log_entry_size = data_len-i; + reassembly_entries[pinfo->fd->num].log.entry = (unsigned char*)g_malloc(data_len-i); + tvb_memcpy(tvb, reassembly_entries[pinfo->fd->num].log.entry, data_offset+i, data_len-i); + reassembly_entries[pinfo->fd->num].log.entry_size = data_len-i; current_log_start_frame = pinfo->fd->num; break; } @@ -1596,22 +1777,68 @@ if (i == data_len && current_log_start_frame != 0xffffffff) { // No new PMEM found - reassembly_entries[current_log_start_frame].log_entry = g_realloc(reassembly_entries[current_log_start_frame].log_entry, reassembly_entries[current_log_start_frame].log_entry_size + data_len); - tvb_memcpy(tvb, &reassembly_entries[current_log_start_frame].log_entry[reassembly_entries[current_log_start_frame].log_entry_size], data_offset, data_len); + reassembly_entries[current_log_start_frame].log.entry = g_realloc(reassembly_entries[current_log_start_frame].log.entry, reassembly_entries[current_log_start_frame].log.entry_size + data_len); + tvb_memcpy(tvb, &reassembly_entries[current_log_start_frame].log.entry[reassembly_entries[current_log_start_frame].log.entry_size], data_offset, data_len); - reassembly_entries[current_log_start_frame].log_entry_size += data_len; + reassembly_entries[current_log_start_frame].log.entry_size += data_len; } // Increment address log_last_known_address += data_len; - reassembly_entries[pinfo->fd->num].log_start_frame = current_log_start_frame; + reassembly_entries[pinfo->fd->num].log.start_frame = current_log_start_frame; if (log_last_known_address >= log_after_end_of_use) { // If we are after end of file, reset log entry! current_log_start_frame = 0xffffffff; } } + + // Handle dissection of Ambit3 log headers + if (command == 0x12000a00) { + if (msg_part == 0x5d) { + // Check if this is the first packet of the last header chunk + if (tvb_get_guint8(tvb, data_offset) == 0x00 && + tvb_get_guint8(tvb, data_offset+1) == 0x00 && + tvb_get_guint8(tvb, data_offset+2) == 0x00 && + tvb_get_guint8(tvb, data_offset+3) == 0x00 && + tvb_get_guint8(tvb, data_offset+4) == 0x01 && + tvb_get_guint8(tvb, data_offset+5) == 0x00) { + current_log_header_end_found = 1; + } + + // Adjust data pointers for extra address and length fields + data_offset += 6; + data_len -= 6; + } + + // Look for start of entry (at SBEM0102) + if (tvb_get_guint8(tvb, data_offset) == 'S' && + tvb_get_guint8(tvb, data_offset+1) == 'B' && + tvb_get_guint8(tvb, data_offset+2) == 'E' && + tvb_get_guint8(tvb, data_offset+3) == 'M') { + reassembly_entries[pinfo->fd->num].log_header.entry = (unsigned char*)g_malloc(data_len); + tvb_memcpy(tvb, reassembly_entries[pinfo->fd->num].log_header.entry, data_offset, data_len); + reassembly_entries[pinfo->fd->num].log_header.entry_size = data_len; + current_log_header_start_frame = pinfo->fd->num; + } + else if (current_log_header_start_frame != 0xffffffff) { + // Append to current entry + reassembly_entries[current_log_header_start_frame].log_header.entry = g_realloc(reassembly_entries[current_log_header_start_frame].log_header.entry, reassembly_entries[current_log_header_start_frame].log_header.entry_size + data_len); + tvb_memcpy(tvb, &reassembly_entries[current_log_header_start_frame].log_header.entry[reassembly_entries[current_log_header_start_frame].log_header.entry_size], data_offset, data_len); + + reassembly_entries[current_log_header_start_frame].log_header.entry_size += data_len; + } + + reassembly_entries[pinfo->fd->num].log_header.start_frame = current_log_header_start_frame; + + // If we have reached last packet in last chunk, reset "pointers" + if (reassembly_entries[pinfo->fd->num].frame_index + 1 == reassembly_entries[pinfo->fd->num].frame_total && + current_log_header_end_found == 1) { + current_log_header_start_frame = 0xffffffff; + current_log_header_end_found = 0; + } + } } if (tree) { /* we are being asked for details */ @@ -1645,7 +1872,7 @@ if (msg_part == 0x5d) { proto_tree_add_item(ambit_tree, hf_ambit_requestcmd, tvb, offset, 4, ENC_BIG_ENDIAN); offset += 4; - dissect_ambit_add_unknown (tvb, pinfo, ambit_tree, offset, 2); + proto_tree_add_item(ambit_tree, hf_ambit_pktformat, tvb, offset, 2, ENC_LITTLE_ENDIAN); offset += 2; proto_tree_add_item(ambit_tree, hf_ambit_pktseqno, tvb, offset, 2, ENC_LITTLE_ENDIAN); offset += 2; @@ -1666,11 +1893,17 @@ col_add_fstr(pinfo->cinfo, COL_INFO, " (#%u of #%u) Reassembled", reassembly_entries[pinfo->fd->num].frame_index + 1, reassembly_entries[pinfo->fd->num].frame_total); - if (reassembly_entries[pinfo->fd->num].log_start_frame != 0xffffffff && - reassembly_entries[reassembly_entries[pinfo->fd->num].log_start_frame].log_entry != NULL) { - log_tvb = tvb_new_real_data(reassembly_entries[reassembly_entries[pinfo->fd->num].log_start_frame].log_entry, reassembly_entries[reassembly_entries[pinfo->fd->num].log_start_frame].log_entry_size, reassembly_entries[reassembly_entries[pinfo->fd->num].log_start_frame].log_entry_size); + if (reassembly_entries[pinfo->fd->num].log.start_frame != 0xffffffff && + reassembly_entries[reassembly_entries[pinfo->fd->num].log.start_frame].log.entry != NULL) { + log_tvb = tvb_new_real_data(reassembly_entries[reassembly_entries[pinfo->fd->num].log.start_frame].log.entry, reassembly_entries[reassembly_entries[pinfo->fd->num].log.start_frame].log.entry_size, reassembly_entries[reassembly_entries[pinfo->fd->num].log.start_frame].log.entry_size); add_new_data_source(pinfo, log_tvb, "Log"); } + + if (reassembly_entries[pinfo->fd->num].log_header.start_frame != 0xffffffff && + reassembly_entries[reassembly_entries[pinfo->fd->num].log_header.start_frame].log_header.entry != NULL) { + log_header_tvb = tvb_new_real_data(reassembly_entries[reassembly_entries[pinfo->fd->num].log_header.start_frame].log_header.entry, reassembly_entries[reassembly_entries[pinfo->fd->num].log_header.start_frame].log_header.entry_size, reassembly_entries[reassembly_entries[pinfo->fd->num].log_header.start_frame].log_header.entry_size); + add_new_data_source(pinfo, log_header_tvb, "Log header"); + } } else { col_add_fstr(pinfo->cinfo, COL_INFO, " (#%u of #%u)", reassembly_entries[pinfo->fd->num].frame_index + 1, reassembly_entries[pinfo->fd->num].frame_total); @@ -1698,7 +1931,13 @@ if (log_tvb != NULL) { data_ti = proto_tree_add_text(ambit_tree, new_tvb, 0, pkt_len, "Full log entry"); data_tree = proto_item_add_subtree(data_ti, ett_ambit_log_data); - dissect_ambit_log_data_content(log_tvb, pinfo, data_tree, data, 0, reassembly_entries[reassembly_entries[pinfo->fd->num].log_start_frame].log_entry_size); + dissect_ambit_log_data_content(log_tvb, pinfo, data_tree, data, 0, reassembly_entries[reassembly_entries[pinfo->fd->num].log.start_frame].log.entry_size); + } + + if (log_header_tvb != NULL) { + data_ti = proto_tree_add_text(ambit_tree, new_tvb, 0, pkt_len, "Full log headers"); + data_tree = proto_item_add_subtree(data_ti, ett_ambit_log_data); + dissect_ambit3_log_headers_content(log_header_tvb, pinfo, data_tree, data, 0, reassembly_entries[reassembly_entries[pinfo->fd->num].log_header.start_frame].log_header.entry_size); } offset += data_len; @@ -1738,6 +1977,8 @@ { "msg header checksum", "ambit.msgheaderchksum", FT_UINT16, BASE_HEX, NULL, 0x0, NULL, HFILL } }, { &hf_ambit_requestcmd, { "Command", "ambit.command", FT_UINT32, BASE_HEX, NULL, 0x0, NULL, HFILL } }, + { &hf_ambit_pktformat, + { "Packet format", "ambit.pktformat", FT_UINT16, BASE_DEC, NULL, 0x0, NULL, HFILL } }, { &hf_ambit_pktseqno, { "Packet sequence no", "ambit.pktseqno", FT_UINT16, BASE_DEC, NULL, 0x0, NULL, HFILL } }, { &hf_ambit_pktlen, @@ -1897,12 +2138,16 @@ { "Battery at end", "ambit.log_header.battery_stop", FT_UINT8, BASE_DEC, NULL, 0x0,NULL, HFILL } }, { &hf_ambit_log_header_distance_before_calib, { "Distance before calibration", "ambit.log_header.distance_before_calib", FT_UINT32, BASE_DEC, NULL, 0x0,NULL, HFILL } }, + { &hf_ambit_log_header_synced, + { "Syncronized", "ambit.log_header.synced", FT_UINT8, BASE_DEC, NULL, 0x0,NULL, HFILL } }, { &hf_ambit_log_header_more, { "More values", "ambit.log_header.more", FT_UINT32, BASE_HEX, VALS(log_header_more_vals), 0, NULL, HFILL } }, { &hf_ambit_log_count, { "Log count", "ambit.log.count", FT_UINT16, BASE_DEC, NULL, 0x0,NULL, HFILL } }, + { &hf_ambit_unsynced_log_count, + { "Not synced log count", "ambit.log.unsynced.count", FT_UINT16, BASE_DEC, NULL, 0x0,NULL, HFILL } }, { &hf_ambit_log_data_addr_frame_ref, { "In frame", "ambit.log_data.inframe", FT_FRAMENUM, BASE_NONE, NULL, 0, NULL, HFILL } }, @@ -2043,6 +2288,8 @@ &ett_ambit_log_data, &ett_ambit_log_samples, &ett_ambit_log_sample, + &ett_ambit3_log_headers, + &ett_ambit3_log_header }; proto_ambit = proto_register_protocol (