summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorRALOVICH, Kristóf <tade60@freemail.hu>2013-03-02 17:00:45 +0100
committerRALOVICH, Kristóf <tade60@freemail.hu>2013-03-02 17:00:45 +0100
commit09b051c37f13ffa3941bac68272b0814b40707a9 (patch)
treea9de636c8267604091014829ef3858df6a1dfbe4
parent984592513069187d2acb1b071b96996b74b1953e (diff)
import sources
-rw-r--r--.gitignore2
-rw-r--r--src/AntChannel.cpp297
-rw-r--r--src/AntChannel.hpp103
-rw-r--r--src/AntFr310XT.cpp564
-rw-r--r--src/AntFr310XT.hpp81
-rw-r--r--src/AntMessage.cpp761
-rw-r--r--src/AntMessage.hpp586
-rw-r--r--src/AntMessenger.cpp1204
-rw-r--r--src/AntMessenger.hpp176
-rw-r--r--src/CMakeLists.txt135
-rw-r--r--src/FIT.cpp1221
-rw-r--r--src/FIT.hpp297
-rw-r--r--src/GPX.cpp221
-rw-r--r--src/GPX.hpp127
-rw-r--r--src/GarminConvert.cpp162
-rw-r--r--src/GarminConvert.hpp58
-rw-r--r--src/Serial.hpp32
-rw-r--r--src/SerialTty.cpp313
-rw-r--r--src/SerialTty.hpp58
-rw-r--r--src/SerialUsb.cpp594
-rw-r--r--src/SerialUsb.hpp49
-rw-r--r--src/SmartPtrFwd.hpp57
-rw-r--r--src/antdefs.hpp323
-rw-r--r--src/antpm-downloader.cpp192
-rw-r--r--src/antpm-fit2gpx.cpp120
-rw-r--r--src/antpm-usbmon2ant.cpp222
-rw-r--r--src/cmake/FindLIBUSB.cmake61
-rw-r--r--src/cmake/Findlibusb-1.0.cmake98
-rw-r--r--src/common.cpp241
-rw-r--r--src/common.hpp82
-rw-r--r--src/lqueue.hpp236
-rw-r--r--src/stdintfwd.hpp23
-rw-r--r--src/w_inttypes.h305
-rw-r--r--src/w_stdint.h248
34 files changed, 9249 insertions, 0 deletions
diff --git a/.gitignore b/.gitignore
new file mode 100644
index 0000000..1677b58
--- /dev/null
+++ b/.gitignore
@@ -0,0 +1,2 @@
+build*
+*~
diff --git a/src/AntChannel.cpp b/src/AntChannel.cpp
new file mode 100644
index 0000000..51c6eb8
--- /dev/null
+++ b/src/AntChannel.cpp
@@ -0,0 +1,297 @@
+// -*- mode: c++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2; coding: utf-8-unix -*-
+// ***** BEGIN LICENSE BLOCK *****
+////////////////////////////////////////////////////////////////////
+// //
+// Copyright (c) 2011-2013 RALOVICH, Kristóf //
+// //
+// This program is free software; you can redistribute it and/or //
+// modify it under the terms of the GNU General Public License //
+// version 2 as published by the Free Software Foundation. //
+// //
+////////////////////////////////////////////////////////////////////
+// ***** END LICENSE BLOCK *****
+
+
+#include "AntChannel.hpp"
+
+
+
+
+
+
+void
+AntChannel::addMsgListener2(AntListenerBase* lb)
+{
+ boost::unique_lock<boost::mutex> lock(m_mtxListeners);
+ listeners.push_back(lb);
+}
+
+void
+AntChannel::rmMsgListener2(AntListenerBase* lb)
+{
+ boost::unique_lock<boost::mutex> lock(m_mtxListeners);
+ listeners.remove(lb);
+}
+
+void
+AntChannel::onMsg(AntMessage &m)
+{
+ boost::unique_lock<boost::mutex> lock(m_mtxListeners);
+ for(std::list<AntListenerBase*>::iterator i = listeners.begin(); i != listeners.end(); i++)
+ {
+ (*i)->onMsg(m);
+ }
+}
+
+
+
+
+
+
+AntListenerBase::AntListenerBase(AntChannel& o)
+ : owner(o)
+{
+ owner.addMsgListener2(this);
+}
+
+
+AntListenerBase::~AntListenerBase()
+{
+ owner.rmMsgListener2(this);
+}
+
+
+void
+AntListenerBase::onMsg(AntMessage& m)
+{
+ boost::unique_lock<boost::mutex> lock(m_mtxResp);
+ if(match(m))
+ {
+ //assert(!m_msgResp); //this assert won't always work: e.g. if the waitForMsg() already concluded, there is noone to reset this pointer
+ m_msgResp.reset(new AntMessage(m));
+ m_cndResp.notify_all();
+ }
+}
+
+bool
+AntListenerBase::waitForMsg(AntMessage* m, const size_t timeout_ms)
+{
+ boost::unique_lock<boost::mutex> lock(m_mtxResp);
+ if(m_msgResp)
+ {
+ if(m) *m = *m_msgResp; // it had already arrived
+ m_msgResp.reset();
+ return true;
+ }
+ if(!m_cndResp.timed_wait(lock, boost::posix_time::milliseconds(timeout_ms)))
+ {
+ return false;
+ }
+ assert(m_msgResp);
+ if(m) *m=*m_msgResp;//copy
+ m_msgResp.reset();
+ return true;
+}
+
+
+
+
+
+
+
+
+bool
+AntEvListener::match(AntMessage& other) const
+{
+ return other.getMsgId()==MESG_RESPONSE_EVENT_ID
+ && owner.chan == other.getPayloadRef()[0]
+ && other.getPayloadRef()[1]==MESG_EVENT_ID;
+}
+// whether there was a response before timeout
+bool
+AntEvListener::waitForEvent(uint8_t& msgCode, const size_t timeout_ms)
+{
+ AntMessage resp;
+ if(!waitForMsg(&resp, timeout_ms))
+ return false;
+ msgCode = resp.getPayloadRef()[2];
+ return true;
+}
+
+
+
+
+
+
+
+
+bool
+AntRespListener::match(AntMessage& other) const
+{
+ return other.getMsgId()==MESG_RESPONSE_EVENT_ID
+ && owner.chan == other.getPayloadRef()[0]
+ && other.getPayloadRef()[1]==msgId;
+}
+
+// whether there was a response before timeout
+bool
+AntRespListener::waitForResponse(uint8_t& respVal, const size_t timeout_ms)
+{
+ AntMessage resp;
+ if(!waitForMsg(&resp, timeout_ms))
+ return false;
+ respVal = resp.getPayloadRef()[2];
+ return true;
+}
+
+
+
+
+
+
+
+
+bool
+AntReqListener::match(AntMessage& other) const
+{
+ bool matched = other.getMsgId()==msgId
+ && other.getPayloadRef()[0]==chan;
+ //printf("matched=%d\n", int(matched));fflush(stdout);
+ return matched;
+}
+
+
+
+
+
+
+bool
+AntBCastListener::match(AntMessage& other) const
+{
+ return other.getMsgId()==MESG_BROADCAST_DATA_ID
+ && other.getPayloadRef()[0]==owner.chan
+ && other.getPayloadRef()[1]==first;
+}
+
+bool
+AntBCastListener::waitForBCast(AntMessage& bcast, const size_t timeout_ms)
+{
+ return waitForMsg(&bcast, timeout_ms);
+}
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+void
+AntBurstListener::onMsg(AntMessage& m)
+ {
+ boost::unique_lock<boost::mutex> lock(m_mtxResp);
+ if(match(m))
+ {
+ bursts.push_back(m);
+ m_cndResp.notify_all();
+ }
+ }
+
+
+bool
+AntBurstListener::match(AntMessage& other) const
+ {
+ const M_ANT_Burst* burst(reinterpret_cast<const M_ANT_Burst*>(other.getPayloadRef()));
+ return other.getMsgId()==MESG_BURST_DATA_ID
+ && burst->chan==owner.chan;
+ }
+
+
+bool
+AntBurstListener::waitForBursts(std::list<AntMessage>& bs, const size_t timeout_ms)
+ {
+ bs.clear();
+ boost::unique_lock<boost::mutex> lock(m_mtxResp);
+ if(!bursts.empty()) // some had already arrived
+ {
+ std::swap(bs, bursts);
+ return true;
+ }
+ // TODO: handle arrival of event:EVENT_RX_FAIL
+ if(!m_cndResp.timed_wait(lock, boost::posix_time::milliseconds(timeout_ms)))
+ return false;
+ assert(!bursts.empty());
+ std::swap(bs, bursts);
+ return true;
+ }
+
+bool
+AntBurstListener::collectBurst(std::vector<uint8_t>& burstData, const size_t timeout_ms)
+ {
+ boost::posix_time::ptime start = boost::posix_time::microsec_clock::local_time();
+ boost::posix_time::ptime end = start+boost::posix_time::milliseconds(timeout_ms);
+
+ boost::posix_time::ptime after=start;
+ boost::posix_time::time_duration remaining = end-after;
+ uchar expectedSeq=0;
+ bool found=false;
+ bool lastFound=false;
+ while(remaining>boost::posix_time::time_duration(boost::posix_time::milliseconds(0)))
+ {
+ std::list<AntMessage> msgs;
+ size_t left_ms=static_cast<size_t>(remaining.total_milliseconds());
+ //printf("msg wait ms=%d\n", int(left_ms));
+ CHECK_RETURN_FALSE(waitForBursts(msgs, left_ms));
+ after = boost::posix_time::microsec_clock::local_time();
+ remaining = end-after;
+ for(std::list<AntMessage>::iterator i = msgs.begin(); i != msgs.end(); i++)
+ {
+ AntMessage& repl(*i);
+ CHECK_RETURN_FALSE(repl.getLenPayload()==9);
+ const M_ANT_Burst* burst(reinterpret_cast<const M_ANT_Burst*>(repl.getPayloadRef()));
+ //if(burst->chan != chan) // this check is a duplicate
+ //{
+ // //++i;j++;
+ // continue;
+ //}
+ //LOG_VAR2((int)burst->seq, (int)expectedSeq);
+ CHECK_RETURN_FALSE(burst->seq == expectedSeq);
+ ++expectedSeq;
+ if(expectedSeq == 4)
+ expectedSeq = 1;
+ found = true;
+ std::vector<uchar> crtBurst(repl.getPayload());
+ //std::string desc=burst->toString();
+ //printf("reading %d bytes of BURST:%s\n", int(crtBurst.size()), desc.c_str());
+ burstData.insert(burstData.end(), crtBurst.begin()+1,crtBurst.end());
+ assert(burstData.size()%8==0);
+ lastFound = burst->isLast();
+ //i=m_rpackQueue.erase(i);j++;
+ //if(burst->isLast())
+ //{
+ // lastFound = true;
+ // //break;
+ //}
+ //LOG_VAR(lastFound);
+ }
+ if(lastFound)
+ {
+ //LOG_VAR(lastFound);
+ break;
+ }
+ }
+ if(!found || !lastFound)
+ {
+ printf("E: couldn't reconstruct burst data transmission before timeout\n"); fflush(stdout);
+ return false;
+ }
+ printf("ok: collectBurst: %d bytes\n", int(burstData.size()));
+ return true;
+ }
diff --git a/src/AntChannel.hpp b/src/AntChannel.hpp
new file mode 100644
index 0000000..948da57
--- /dev/null
+++ b/src/AntChannel.hpp
@@ -0,0 +1,103 @@
+// -*- mode: c++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2; coding: utf-8-unix -*-
+// ***** BEGIN LICENSE BLOCK *****
+////////////////////////////////////////////////////////////////////
+// Copyright (c) 2011-2013 RALOVICH, Kristóf //
+// //
+// This program is free software; you can redistribute it and/or //
+// modify it under the terms of the GNU General Public License //
+// version 2 as published by the Free Software Foundation. //
+// //
+////////////////////////////////////////////////////////////////////
+// ***** END LICENSE BLOCK *****
+#pragma once
+
+#include "AntMessage.hpp"
+#include <boost/thread.hpp>
+
+
+struct AntChannel;
+
+struct AntListenerBase
+{
+protected:
+ boost::mutex m_mtxResp;
+ boost::condition_variable m_cndResp;
+ boost::scoped_ptr<AntMessage> m_msgResp;
+ AntChannel& owner;
+
+public:
+ AntListenerBase(AntChannel& o);
+ virtual ~AntListenerBase();
+ virtual void onMsg(AntMessage& m);
+protected:
+ virtual bool match(AntMessage& other) const = 0;
+public:
+ // whether there was a response before timeout
+ virtual bool waitForMsg(AntMessage* m, const size_t timeout_ms);
+};
+
+struct AntChannel
+{
+ uchar chan;
+protected:
+ boost::mutex m_mtxListeners;
+ std::list<AntListenerBase*> listeners;
+public:
+ void addMsgListener2(AntListenerBase* lb);
+ void rmMsgListener2(AntListenerBase* lb);
+ void onMsg(AntMessage &m);
+};
+
+
+struct AntEvListener : public AntListenerBase
+{
+ AntEvListener(AntChannel& o) : AntListenerBase(o) {}
+ virtual ~AntEvListener() {}
+ virtual bool match(AntMessage& other) const;
+ // whether there was a response before timeout
+ bool waitForEvent(uint8_t& msgCode, const size_t timeout_ms);
+};
+
+struct AntRespListener : public AntListenerBase
+{
+ uint8_t msgId;//
+
+ AntRespListener(AntChannel& o, const uint8_t msgId_) : AntListenerBase(o), msgId(msgId_) {}
+ virtual ~AntRespListener() {}
+ virtual bool match(AntMessage& other) const;
+ // whether there was a response before timeout
+ bool waitForResponse(uint8_t& respVal, const size_t timeout_ms);
+};
+
+struct AntReqListener : public AntListenerBase
+{
+ uint8_t msgId;
+ uint8_t chan;
+
+ AntReqListener(AntChannel& o, uint8_t m, uint8_t c) : AntListenerBase(o), msgId(m), chan(c) {}
+ virtual ~AntReqListener() {}
+ virtual bool match(AntMessage& other) const;
+};
+
+struct AntBCastListener : public AntListenerBase
+{
+ uint8_t first;//
+
+ AntBCastListener(AntChannel& o, uint8_t f) : AntListenerBase(o), first(f) {}
+ virtual ~AntBCastListener() {}
+ virtual bool match(AntMessage& other) const;
+ bool waitForBCast(AntMessage& bcast, const size_t timeout_ms);
+};
+
+struct AntBurstListener : public AntListenerBase
+{
+ std::list<AntMessage> bursts;
+
+ AntBurstListener(AntChannel& o) : AntListenerBase(o) {}
+ virtual ~AntBurstListener() {}
+ virtual void onMsg(AntMessage& m);
+ virtual bool match(AntMessage& other) const;
+ virtual bool waitForBursts(std::list<AntMessage>& bs, const size_t timeout_ms);
+ bool collectBurst(std::vector<uint8_t>& burstData, const size_t timeout_ms);
+};
+
diff --git a/src/AntFr310XT.cpp b/src/AntFr310XT.cpp
new file mode 100644
index 0000000..89b2b5a
--- /dev/null
+++ b/src/AntFr310XT.cpp
@@ -0,0 +1,564 @@
+// -*- mode: c++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2; coding: utf-8-unix -*-
+// ***** BEGIN LICENSE BLOCK *****
+////////////////////////////////////////////////////////////////////
+// Copyright (c) 2011-2013 RALOVICH, Kristóf //
+// //
+// This program is free software; you can redistribute it and/or //
+// modify it under the terms of the GNU General Public License //
+// version 2 as published by the Free Software Foundation. //
+// //
+////////////////////////////////////////////////////////////////////
+// ***** END LICENSE BLOCK *****
+
+#include "AntFr310XT.hpp"
+#include "SerialTty.hpp"
+#include "SerialUsb.hpp"
+#include "antdefs.hpp"
+#include "common.hpp"
+#include <stdio.h>
+#include <string.h>
+#include <stdlib.h>
+
+#include <functional>
+#include <algorithm>
+#include <vector>
+#include <string>
+#include <boost/thread/thread_time.hpp>
+#include <iostream>
+#include "stdintfwd.hpp"
+
+
+//#include <boost/property_tree/ptree.hpp>
+//#include <boost/property_tree/ini_parser.hpp>
+
+const uchar net = 0x00;
+const uchar chan = 0x00;
+//const uint hostSN = 0x7c9101e0; // from Garmin ANT+ Agent
+//const uint64_t pairedKey = 0xd273f79a6f166fa5;
+const uint hostSN = 0x00000000;//0xbcdef012;
+//const uint64_t pairedKey = 0xa56f166f9af773d2;
+const ushort msgPeriod = 0x1000;
+const uchar chanSearchTimeout = 0xff;
+const uchar rfFreq = 0x32;
+const ushort waveform = 0x5300;
+
+const uchar fsFreq = 0x46; // other values seen: 0x46 0x50 0x0f
+const uchar beaconPer = 0x04;
+const uchar fsSearchTimeout = 0x03;
+
+uint maxFileDownloads = 1000;
+
+
+
+
+struct AntFr310XT2_EventLoop
+{
+ void operator() (AntFr310XT2* arg)
+ {
+ //printf("msgFunc, arg: %p\n", arg); fflush(stdout);
+ if(!arg)
+ {
+ rv=0;
+ return;
+ }
+ AntFr310XT2* This = reinterpret_cast<AntFr310XT2*>(arg);
+ //printf("msgFunc, This: %p\n", This); fflush(stdout);
+ rv = This->th_eventLoop();
+ }
+ void* rv;
+};
+
+AntFr310XT2::AntFr310XT2(bool eventLoopInBgTh)
+ : m_serial(new ANTPM_SERIAL_IMPL())
+ , m_antMessenger(new AntMessenger(eventLoopInBgTh))
+ , doPairing(false)
+ , aplc(getConfigFolder()+std::string("libantpm_")+getDateString()+".antparse.txt")
+ , clientSN(0)
+ , pairedKey(0)
+ , m_eventLoopInBgTh(eventLoopInBgTh)
+ , mode(MD_DOWNLOAD_ALL)
+{
+ //boost::property_tree::ptree pt;
+ //std::string confFile = getConfigFileName();
+ //boost::property_tree::ini_parser::read_ini(confFile.c_str(), pt);
+ //std::cout << pt.get<uint>("libantpm.MaxFileDownloads") << std::endl;
+ //std::cout << pt.get<std::string>("Section1.Value2") << std::endl;
+ //maxFileDownloads = pt.get<uint>("libantpm.MaxFileDownloads");
+
+ m_antMessenger->setHandler(m_serial.get());
+ m_antMessenger->setCallback(this);
+ state = ST_ANTFS_0;
+ m_eventThKill=0;
+
+ AntFr310XT2_EventLoop eventTh;
+ eventTh.rv=0;
+ m_eventTh = boost::thread(eventTh, this);
+
+}
+
+
+AntFr310XT2::~AntFr310XT2()
+{
+ m_antMessenger->setCallback(0);
+ //m_antMessenger->setHandler(0);
+
+ m_eventThKill=1;
+ m_eventTh.join();
+ state = ST_ANTFS_0;
+
+ m_antMessenger.reset();
+ m_serial.reset();
+ fprintf(loggerc(), "%s\n", __FUNCTION__);
+}
+
+
+void
+AntFr310XT2::setModeDownloadAll()
+{
+ mode = MD_DOWNLOAD_ALL;
+ LOG_VAR2(mode, ModeOfOperation2Str(mode));
+}
+
+void
+AntFr310XT2::setModeDownloadSingleFile( const uint16_t fileIdx )
+{
+ mode = MD_DOWNLOAD_SINGLE_FILE;
+ singleFileIdx = fileIdx;
+ LOG_VAR2(mode, ModeOfOperation2Str(mode));
+}
+
+void
+AntFr310XT2::setModeDirectoryListing()
+{
+ mode = MD_DIRECTORY_LISTING;
+ LOG_VAR2(mode, ModeOfOperation2Str(mode));
+}
+
+void
+AntFr310XT2::setModeEraseSingleFile(const uint16_t fileIdx)
+{
+ mode = MD_ERASE_SINGLE_FILE;
+ singleFileIdx = fileIdx;
+ LOG_VAR2(mode, ModeOfOperation2Str(mode));
+}
+
+
+void
+AntFr310XT2::setModeEraseAllActivities()
+{
+ mode = MD_ERASE_ALL_ACTIVITIES;
+ LOG_VAR2(mode, ModeOfOperation2Str(mode));
+}
+
+
+void
+AntFr310XT2::onAntReceived(const AntMessage m)
+{
+ postEvent(m);
+}
+
+void
+AntFr310XT2::onAntSent(const AntMessage m)
+{
+}
+
+
+
+void
+AntFr310XT2::start()
+{
+ CHECK_RETURN(m_serial->open());
+
+ folder = getConfigFolder() + "/" + getDateString() + "/";
+ CHECK_RETURN(mkDir(folder.c_str()));
+
+ //m_antMessenger->addListener(boost::bind(&AntFr310XT2::listenerFunc2, this, _1));
+
+ //m_antMessenger->setCallback(&aplc);
+
+ changeState(ST_ANTFS_START0);
+
+ if(!m_eventLoopInBgTh)
+ m_antMessenger->eventLoop();
+}
+
+void AntFr310XT2::stop()
+{
+ m_eventThKill = 1;
+ m_antMessenger->kill();
+ if(m_serial->isOpen())
+ {
+ if(state>ST_ANTFS_LINKING)
+ m_antMessenger->ANTFS_Disconnect(chan);
+ m_antMessenger->ANT_CloseChannel(chan);
+ m_antMessenger->ANT_ResetSystem();
+ }
+ m_serial->close();
+ changeState(ST_ANTFS_LAST);
+}
+
+const int
+AntFr310XT2::getSMState() const
+{
+ return state;
+}
+
+const char*
+AntFr310XT2::getSMStateStr() const
+{
+ return StateFSWork2Str(state);
+}
+
+void
+AntFr310XT2::postEvent(const AntMessage& m)
+{
+ m_evQue.push(m);
+}
+
+
+
+void*
+AntFr310XT2::th_eventLoop()
+{
+ for(;;)
+ {
+ if(m_eventThKill)
+ break;
+ //if(m_received)
+ //{
+ // m_received = 0;
+ // handleEvents();
+ //}
+ //else
+ //{
+ // sleepms(2);
+ //}
+ if(handleEvents())
+ {
+ //sleepms(2);
+ }
+ else
+ {
+ changeState(ST_ANTFS_BAD);
+ sleepms(1000);
+ }
+ }
+ return 0;
+}
+
+
+bool
+AntFr310XT2::handleEvents()
+{
+ //return true;
+
+ while(!m_evQue.empty())
+ {
+ AntMessage m;
+ m_evQue.pop(m);
+ if(m.getMsgId()==MESG_RESPONSE_EVENT_ID)
+ {
+ uint8_t chan=m.getPayloadRef()[0];
+ uint8_t msgId=m.getPayloadRef()[1];
+ if(msgId==MESG_EVENT_ID)
+ {
+ uint8_t msgCode = m.getPayloadRef()[2];
+ if(msgCode==EVENT_RX_SEARCH_TIMEOUT) // handle RX_SEARCH_TIMEOUT
+ {
+ changeState(ST_ANTFS_BAD);
+ }
+ }
+ }
+ }
+
+ // new state machine
+ if(state==ST_ANTFS_RESTART)
+ {
+ m_evQue.clear();
+ //m_antMessenger->clearRxQueue();
+ m_antMessenger->ANT_ResetSystem();
+ m_antMessenger->ANT_ResetSystem();
+ changeState(ST_ANTFS_START0);
+ }
+ if(state==ST_ANTFS_START0)
+ {
+ CHECK_RETURN_FALSE(m_antMessenger->ANT_ResetSystem());
+
+ //CHECK_RETURN_FALSE(m_antMessenger->ANT_RequestMessage(chan, MESG_CHANNEL_STATUS_ID));
+
+ CHECK_RETURN_FALSE(m_antMessenger->ANT_SetNetworkKey(net, ANTP_NETKEY));
+
+ CHECK_RETURN_FALSE(m_antMessenger->ANT_AssignChannel(chan,0,net));
+
+ CHECK_RETURN_FALSE(m_antMessenger->ANT_SetChannelMessagePeriod(chan, msgPeriod));
+
+ CHECK_RETURN_FALSE(m_antMessenger->ANT_SetChannelSearchTimeout(chan, chanSearchTimeout));
+ CHECK_RETURN_FALSE(m_antMessenger->ANT_SetChannelRadioFreq(chan, rfFreq));
+ CHECK_RETURN_FALSE(m_antMessenger->ANT_SetSearchWaveform(chan, waveform));
+ CHECK_RETURN_FALSE(m_antMessenger->ANT_SetChannelId(chan, 0, 0x01, 0x05));
+ CHECK_RETURN_FALSE(m_antMessenger->ANT_OpenChannel(chan));
+ CHECK_RETURN_FALSE(m_antMessenger->ANT_RequestMessage(chan, MESG_CHANNEL_STATUS_ID));
+
+ changeState(ST_ANTFS_LINKING);
+ }
+ else if(state==ST_ANTFS_LINKING)
+ {
+ AntMessage m;
+ //CHECK_RETURN_FALSE(m_antMessenger->waitForBroadcastDataAvail(chan, &m, 20000));//link beacon
+ CHECK_RETURN_FALSE(m_antMessenger->waitForBroadcast(chan, &m, 20000));//link beacon
+ M_ANTFS_Beacon* beacon(reinterpret_cast<M_ANTFS_Beacon*>(&m.getPayloadRef()[1]));
+ // TODO:handle case of no available data
+ if(!beacon->dataAvail)
+ {
+ changeState(ST_ANTFS_NODATA);
+ logger() << "\n\nNo data available from client!\n\n\n";
+ return true;
+ }
+
+ CHECK_RETURN_FALSE(m_antMessenger->ANT_RequestMessage(chan, MESG_CHANNEL_ID_ID));
+
+ CHECK_RETURN_FALSE(m_antMessenger->ANTFS_Link(chan, fsFreq, beaconPer, hostSN));
+
+ changeState(ST_ANTFS_AUTH0_SN);
+ }
+ else if(state == ST_ANTFS_BAD)
+ {
+ m_antMessenger->ANT_CloseChannel(chan);
+ sleepms(800);
+ changeState(ST_ANTFS_RESTART);
+ return true;
+ }
+ else if(state == ST_ANTFS_AUTH0_SN)
+ {
+ CHECK_RETURN_FALSE(m_antMessenger->ANT_SetChannelMessagePeriod(chan, msgPeriod));
+ CHECK_RETURN_FALSE(m_antMessenger->ANT_SetChannelSearchTimeout(chan, fsSearchTimeout));
+ CHECK_RETURN_FALSE(m_antMessenger->ANT_SetChannelRadioFreq(chan, fsFreq));
+
+ CHECK_RETURN_FALSE(m_antMessenger->waitForBroadcast(chan));//auth beacon
+
+ // R 105.996 40 MESG_RESPONSE_EVENT_ID chan=0x00 mId=MESG_EVENT_ID mCode=EVENT_RX_FAIL
+ //CHECK_RETURN_FALSE(m_antMessenger->waitForRxFail(chan));
+
+ //CHECK_RETURN_FALSE_LOG_OK(m_antMessenger->waitForBroadcast(chan));
+
+ CHECK_RETURN_FALSE_LOG_OK(m_antMessenger->ANTFS_RequestClientDeviceSerialNumber(chan, hostSN, clientSN, clientDevName));
+
+ logger() << "\n\nFound client \"" << clientDevName << "\" SN=0x" << toString<uint>(clientSN,8,'0') << " SN=" << clientSN << "\n\n\n";
+
+ readUInt64(clientSN, pairedKey);
+
+ LOG_VAR3(doPairing, toString<uint64_t>(pairedKey,16,'0'), clientSN);
+ if(doPairing || pairedKey==0)
+ changeState(ST_ANTFS_AUTH1_PAIR);
+ else
+ changeState(ST_ANTFS_AUTH1_PASS);
+ }
+ else if(state == ST_ANTFS_AUTH1_PAIR)
+ {
+ const std::string hostName("libantpm");
+ uint dummy;
+ if(!m_antMessenger->ANTFS_Pairing(chan, hostSN, hostName, dummy, pairedKey))
+ {
+ changeState(ST_ANTFS_LAST);
+ return true;
+ }
+ writeUInt64(clientSN, pairedKey);
+
+ CHECK_RETURN_FALSE_LOG_OK(m_antMessenger->ANT_RequestMessage(chan, MESG_CHANNEL_STATUS_ID));
+
+ //changeState(ST_ANTFS_LAST);
+ changeState(ST_ANTFS_AUTH1_PASS);
+ }
+ else if(state == ST_ANTFS_AUTH1_PAIR)
+ {
+ //FIXME:
+ //m_antMessenger->ANTFS_Pairing(chan, hostSN, );
+ }
+ else if(state == ST_ANTFS_AUTH1_PASS)
+ {
+ CHECK_RETURN_FALSE_LOG_OK(m_antMessenger->ANT_RequestMessage(chan, MESG_CHANNEL_STATUS_ID));
+
+ if(!m_antMessenger->ANTFS_Authenticate(chan, hostSN, pairedKey))
+ {
+ changeState(ST_ANTFS_RESTART);
+ return true;
+ }
+
+ logger() << "\n\nClient authenticated successfully!\n\n\n";
+
+ // channel status <>
+ CHECK_RETURN_FALSE_LOG_OK(m_antMessenger->ANT_RequestMessage(chan, MESG_CHANNEL_STATUS_ID));
+
+ if(mode==MD_DOWNLOAD_ALL || mode==MD_DIRECTORY_LISTING)
+ changeState(ST_ANTFS_DL_DIRECTORY);
+ else if(mode==MD_DOWNLOAD_SINGLE_FILE)
+ changeState(ST_ANTFS_DL_SINGLE_FILE);
+ else if(mode==MD_ERASE_SINGLE_FILE)
+ changeState(ST_ANTFS_ERASE_SINGLE_FILE);
+ else
+ changeState(ST_ANTFS_LAST);
+ }
+ else if(state == ST_ANTFS_DL_DIRECTORY)
+ {
+ //ANTFS_Upload(); //command pipe
+ //ANTFS_UploadData();
+
+ std::vector<uchar> dir;
+ if(!m_antMessenger->ANTFS_Download(chan, 0x0000, dir))
+ {
+ changeState(ST_ANTFS_RESTART);
+ return true;
+ }
+ logger() << "\n\nDownloaded directory file idx=0x0000\n\n\n";
+
+ AntFsFile file0;
+ file0.bytes=dir;
+ LOG_VAR(file0.checkCrc());
+ file0.saveToFile((folder+"0000.fit").c_str());
+
+ CHECK_RETURN_FALSE(fit.parseZeroFile(dir, zfc));
+ LOG_VAR(zfc.activityFiles.size());
+ LOG_VAR(zfc.courseFiles.size());
+ LOG_VAR(zfc.waypointsFiles.size());
+
+ // TODO: read bcast here?
+
+ // channel status <>
+ //CHECK_RETURN_FALSE_LOG_OK(ANT_RequestMessage(chan, MESG_CHANNEL_STATUS_ID));
+ if(mode==MD_DIRECTORY_LISTING)
+ changeState(ST_ANTFS_LAST);
+ else
+ changeState(ST_ANTFS_DL_FILES);
+ }
+ else if(state==ST_ANTFS_DL_FILES)
+ {
+ // dl waypoint files
+ // dl activity files
+ // dl course files
+
+ uint fileCnt=0;
+ for(size_t i=0; i<zfc.waypointsFiles.size() && fileCnt<maxFileDownloads; i++, fileCnt++)
+ {
+ LOG_VAR3(fileCnt, maxFileDownloads, zfc.waypointsFiles.size());
+ ushort fileIdx = zfc.waypointsFiles[i];
+ logger() << "# Transfer waypoints file 0x" << hex << fileIdx << "\n";
+
+ std::vector<uchar> data;
+ if(!m_antMessenger->ANTFS_Download(chan, fileIdx, data))
+ {
+ changeState(ST_ANTFS_LAST);
+ return true;
+ }
+ logger() << "\n\nDownloaded file idx=" << toString<ushort>(fileIdx,4,'0') << "\n\n\n";
+ AntFsFile file0; file0.bytes=data; file0.saveToFile((folder+toString(fileIdx, 4, '0')+".fit").c_str());
+ LOG_VAR(file0.checkCrc());
+
+ fit.parse(data, gpx);
+ }
+
+ for (size_t i=0; i<zfc.activityFiles.size() && fileCnt<maxFileDownloads; i++, fileCnt++)
+ {
+ LOG_VAR3(fileCnt, maxFileDownloads, zfc.activityFiles.size());
+ ushort fileIdx = zfc.activityFiles[i];
+ logger() << "# Transfer activity file 0x" << hex << fileIdx << "\n";
+
+ std::vector<uchar> data;
+ if(!m_antMessenger->ANTFS_Download(chan, fileIdx, data))
+ {
+ changeState(ST_ANTFS_LAST);
+ return true;
+ }
+ logger() << "\n\nDownloaded file idx=" << toString<ushort>(fileIdx,4,'0') << "\n\n\n";
+ AntFsFile file0; file0.bytes=data; file0.saveToFile((folder+toString(fileIdx, 4, '0')+".fit").c_str());
+
+ fit.parse(data, gpx);
+ }
+
+ for (size_t i=0; i<zfc.courseFiles.size() && fileCnt<maxFileDownloads; i++, fileCnt++)
+ {
+ LOG_VAR3(fileCnt, maxFileDownloads, zfc.courseFiles.size());
+ ushort fileIdx = zfc.courseFiles[i];
+ logger() << "# Transfer course file 0x" << hex << fileIdx << "\n";
+
+ std::vector<uchar> data;
+ if(!m_antMessenger->ANTFS_Download(chan, fileIdx, data))
+ {
+ changeState(ST_ANTFS_LAST);
+ return true;
+ }
+ logger() << "\n\nDownloaded file idx=" << toString<ushort>(fileIdx,4,'0') << "\n\n\n";
+ AntFsFile file0; file0.bytes=data; file0.saveToFile((folder+toString(fileIdx, 4, '0')+".fit").c_str());
+
+ fit.parse(data, gpx);
+ }
+
+ std::string gpxFile=folder+"libantpm.gpx";
+ logger() << "# Writing output to '" << gpxFile << "'\n";
+ gpx.writeToFile(gpxFile);
+
+ changeState(ST_ANTFS_LAST);
+ }
+ else if(state==ST_ANTFS_DL_SINGLE_FILE)
+ {
+ logger() << "# Transfer of file 0x" << hex << singleFileIdx << dec << "\n";
+
+ std::vector<uchar> data;
+ if(!m_antMessenger->ANTFS_Download(chan, singleFileIdx, data))
+ {
+ changeState(ST_ANTFS_LAST);
+ return true;
+ }
+ logger() << "\n\nDownloaded file idx=" << toString<ushort>(singleFileIdx,4,'0') << "\n\n\n";
+ AntFsFile file0; file0.bytes=data; file0.saveToFile((folder+toString(singleFileIdx, 4, '0')+".fit").c_str());//...might not be a fit file
+ LOG_VAR(file0.checkCrc());
+
+ fit.parse(data, gpx);
+
+ std::string gpxFile=folder+"libantpm.gpx";
+ logger() << "# Writing output to '" << gpxFile << "'\n";
+ gpx.writeToFile(gpxFile);
+
+ changeState(ST_ANTFS_LAST);
+ }
+ else if(state==ST_ANTFS_ERASE_SINGLE_FILE)
+ {
+ CHECK_RETURN_FALSE_LOG_OK(m_antMessenger->ANTFS_Erase(chan, singleFileIdx));
+
+ logger() << "\n\nErased file idx=" << toString<ushort>(singleFileIdx,4,'0') << "\n\n\n";
+
+ changeState(ST_ANTFS_LAST);
+ }
+ else if(state==ST_ANTFS_NODATA)
+ {
+ changeState(ST_ANTFS_LAST);
+ }
+ else if(state==ST_ANTFS_LAST)
+ {
+ stop();
+ }
+
+ return true;
+}
+
+
+int
+AntFr310XT2::changeState(const int newState)
+{
+ int oldState = this->state;
+ this->state = newState;
+ logger() << "\nSTATE: " << std::dec << oldState << " => " << newState
+ << "\t " << StateFSWork2Str(oldState) << " => " << StateFSWork2Str(newState)
+ << "\n\n";
+ return oldState;
+}
+
+
+AntFr310XT2::StateANTFS
+AntFr310XT2::changeFSState(const AntFr310XT2::StateANTFS newState)
+{
+ StateANTFS oldState = this->clientState;
+ this->clientState = newState;
+ logger() << "\nFS: " << std::dec << oldState << " => " << newState << "\n\n";
+ return oldState;
+}
+
diff --git a/src/AntFr310XT.hpp b/src/AntFr310XT.hpp
new file mode 100644
index 0000000..9f7376a
--- /dev/null
+++ b/src/AntFr310XT.hpp
@@ -0,0 +1,81 @@
+// -*- mode: c++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2; coding: utf-8-unix -*-
+// ***** BEGIN LICENSE BLOCK *****
+////////////////////////////////////////////////////////////////////
+// Copyright (c) 2011-2013 RALOVICH, Kristóf //
+// //
+// This program is free software; you can redistribute it and/or //
+// modify it under the terms of the GNU General Public License //
+// version 2 as published by the Free Software Foundation. //
+// //
+////////////////////////////////////////////////////////////////////
+// ***** END LICENSE BLOCK *****
+#pragma once
+
+#include "AntMessenger.hpp"
+#include <queue>
+#include "antdefs.hpp"
+#include <list>
+#include <memory>
+#include <boost/thread.hpp>
+#include "FIT.hpp"
+
+
+struct AntFr310XT2_EventLoop;
+// State-machine for ANT+ communication with Forerunner 310XT.
+class AntFr310XT2: public AntCallback
+{
+public:
+ AntFr310XT2(bool eventLoopInBgTh = true);
+ virtual ~AntFr310XT2();
+
+ void setModeForcePairing() { doPairing=true; }
+ void setModeDownloadAll();
+ void setModeDownloadSingleFile(const uint16_t fileIdx);
+ void setModeDirectoryListing();
+ void setModeEraseSingleFile(const uint16_t fileIdx);
+ void setModeEraseAllActivities();
+
+ virtual void onAntReceived(const AntMessage m);
+ virtual void onAntSent(const AntMessage m);
+
+ void start();
+ void stop();
+
+ const int getSMState() const;
+ const char* getSMStateStr() const;
+ AntMessenger* getMessenger() { return m_antMessenger.get(); }
+
+ void postEvent(const AntMessage& m);
+
+protected:
+ boost::scoped_ptr<Serial> m_serial;
+ boost::scoped_ptr<AntMessenger> m_antMessenger;
+ typedef enum { LINK,AUTHENTICATION,TRANSPORT,BUSY} StateANTFS;
+ StateANTFS clientState;
+ int state;
+ volatile int m_eventThKill;
+ boost::thread m_eventTh;
+ lqueue4<AntMessage> m_evQue;
+ AntParsedLoggerCallback aplc;
+
+ FIT fit;
+ ZeroFileContent zfc;
+ GPX gpx;
+
+ uint clientSN;
+ std::string clientDevName;
+ uint64_t pairedKey;
+
+ bool m_eventLoopInBgTh;
+
+ bool doPairing;
+ std::string folder;
+ int mode;
+ uint16_t singleFileIdx;
+private:
+ friend struct AntFr310XT2_EventLoop;
+ void* th_eventLoop();
+ bool handleEvents();
+ int changeState(const int newState);
+ StateANTFS changeFSState(const StateANTFS newState);
+};
diff --git a/src/AntMessage.cpp b/src/AntMessage.cpp
new file mode 100644
index 0000000..4e6c261
--- /dev/null
+++ b/src/AntMessage.cpp
@@ -0,0 +1,761 @@
+// -*- mode: c++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2; coding: utf-8-unix -*-
+// ***** BEGIN LICENSE BLOCK *****
+////////////////////////////////////////////////////////////////////
+// Copyright (c) 2011-2013 RALOVICH, Kristóf //
+// //
+// This program is free software; you can redistribute it and/or //
+// modify it under the terms of the GNU General Public License //
+// version 2 as published by the Free Software Foundation. //
+// //
+////////////////////////////////////////////////////////////////////
+// ***** END LICENSE BLOCK *****
+
+
+#include "AntMessage.hpp"
+#include "common.hpp"
+#include <cstdio>
+#include <sstream>
+#include <cstdlib>
+#include <iomanip> // setw
+#include <cstring> // strlen
+#include <cassert> // assert
+#include <fstream>
+#include <queue>
+#include <boost/date_time/time_duration.hpp>
+#include <boost/date_time.hpp>
+
+
+using namespace std;
+
+
+std::string
+antFSCommand2Str(uchar cmd)
+{
+ size_t i=0;
+ for(; antFSCommandNames[i].i!=-1; i++)
+ {
+ if(antFSCommandNames[i].i==int(cmd))
+ {
+ break;
+ }
+ }
+ if(antFSCommandNames[i].i==-1)
+ return std::string("UNKNOWN_0x") + toString<int>(cmd, 2, '0');
+ else
+ return std::string(antFSCommandNames[i].s);
+}
+
+
+
+std::string
+antFSResponse2Str(uchar resp)
+{
+ size_t i=0;
+ for(; antFSResponseNames[i].i!=-1; i++)
+ {
+ if(antFSResponseNames[i].i==int(resp))
+ {
+ break;
+ }
+ }
+ if(antFSResponseNames[i].i==-1)
+ return std::string("UNKNOWN_0x") + toString<int>(resp, 2, '0');
+ else
+ return std::string(antFSResponseNames[i].s);
+}
+
+bool
+isAntFSCommandOrResponse(const uchar command, bool& isCommand)
+{
+ if(command==ANTFS_CmdLink
+ || command==ANTFS_CmdDisconnect
+ || command==ANTFS_CmdAuthenticate
+ || command==ANTFS_CmdPing
+ || command==ANTFS_ReqDownload
+ || command==ANTFS_ReqUpload
+ || command==ANTFS_ReqErase
+ || command==ANTFS_UploadData )
+ {
+ isCommand=true;
+ return true;
+ }
+ else if (command==ANTFS_RespAuthenticate
+ || command==ANTFS_RespDownload
+ || command==ANTFS_RespUpload
+ || command==ANTFS_RespErase
+ || command==ANTFS_RespUploadData
+ )
+ {
+ isCommand=false;
+ return true;
+ }
+ else
+ {
+ return false;
+ }
+}
+
+
+bool AntMessage::lookupInVector = false;
+
+bool AntMessage::vrfChkSum() const
+{
+ if(bytes.size()<5)
+ {
+ printf("E: %d bytes\n", (int)bytes.size());
+ return false;
+ }
+ if(bytes.size()<getLenPacket())
+ {
+ printf("E: %d bytes, %d payload\n", (int)bytes.size(), (int)getLenPacket());
+ return false;
+ }
+ size_t chkIdx=getLenPayload()+3; // index of checksum byte, not necessary the last one
+ uchar chk = getCheckSum();
+ bool match= (chk == bytes[chkIdx]);
+ if(!match) printf("E: checksum mismatch\n");
+ return match;
+}
+
+std::string
+AntMessage::msgId2Str(uchar msgId)
+{
+ size_t i=0;
+ for(; msgNames[i].i!=-1; i++)
+ {
+ if(msgNames[i].i==int(msgId))
+ {
+ break;
+ }
+ }
+ if(msgNames[i].i==-1)
+ return std::string("UNKNOWN_0x") + toString<int>(msgId, 2, '0');
+ else
+ return std::string(msgNames[i].s);
+}
+
+std::string AntMessage::msgCode2Str(uchar msgCode)
+{
+ size_t i=0;
+ for(; responseNames[i].i!=-1; i++)
+ {
+ if(responseNames[i].i==int(msgCode))
+ {
+ break;
+ }
+ }
+ if(responseNames[i].i==-1)
+ return std::string("UNKNOWN_0x") + toString<int>(msgCode, 2, '0');
+ else
+ return std::string(responseNames[i].s);
+}
+
+
+const std::string
+hexa8(const int off, const std::vector<uchar> bytes)
+{
+ assert(bytes.size()>=size_t(off+8));
+ std::string str;
+
+ str += " ";
+ for(int i=off; i<off+8; i++)
+ {
+ str += toString<int>(bytes[i], 2, '0');
+ }
+ str += " ";
+ for(int i=off; i<off+8; i++)
+ {
+ str += isprint(bytes[i])?bytes[i]:'.';
+ }
+ return str;
+}
+
+
+const std::string AntMessage::str() const
+{
+ if(bytes.empty())
+ return "? ???";
+
+ std::string str=sent?"S ":"R ";
+
+
+ str += toStringDec<size_t>(idx, 4, ' ') + " ";
+
+ str += str2();
+
+ return str;
+}
+
+
+const std::string
+AntMessage::str2() const
+{
+ if(bytes.empty())
+ return " EMPTY";
+
+ std::stringstream sstr;
+ std::string str;
+
+ //sstr << /*"0x" +*/ toString<int>(getMsgId(), 2, ' ') << " ";
+ sstr << msgId2Str(getMsgId());
+
+ if(getMsgId()==MESG_RESPONSE_EVENT_ID)
+ {
+ sstr << " chan=0x" + toString<int>(getPayload()[0], 2, '0');
+ sstr << " mId=" + msgId2Str(getPayload()[1]);
+ sstr << " mCode=" + msgCode2Str(getPayload()[2]);
+ }
+ else if(getMsgId()==MESG_ASSIGN_CHANNEL_ID)
+ {
+ sstr << " chan=0x" + toString<int>(getPayload()[0], 2, '0');
+ sstr << " type=0x" + toString<int>(getPayload()[1], 2, '0');
+ sstr << " net=0x" + toString<int>(getPayload()[2], 2, '0');
+ if(getLenPayload()>3)
+ {
+ sstr << " ext=0x" + toString<int>(getPayload()[3], 2, '0');
+ }
+ }
+ else if(getMsgId()==MESG_CHANNEL_MESG_PERIOD_ID)
+ {
+ sstr << " chan=0x" + toString<int>(getPayloadRef()[0], 2, '0');
+ const ushort* mesgPeriod = reinterpret_cast<const ushort*>(&getPayloadRef()[1]);
+ sstr << " mesgPeriod=0x" << toString<int>(*mesgPeriod, 4, '0');
+ }
+ else if(getMsgId()==MESG_CHANNEL_SEARCH_TIMEOUT_ID)
+ {
+ sstr << " chan=0x" + toString<int>(getPayloadRef()[0], 2, '0');
+ sstr << " searchTimeout=0x" << toString<int>(getPayloadRef()[1], 2, '0');
+ }
+ else if(getMsgId()==MESG_CHANNEL_RADIO_FREQ_ID)
+ {
+ sstr << " chan=0x" + toString<int>(getPayloadRef()[0], 2, '0');
+ sstr << " rfFreq=0x" << toString<int>(getPayloadRef()[1], 2, '0');
+ }
+ else if(getMsgId()==MESG_NETWORK_KEY_ID)
+ {
+ sstr << " net=0x" + toString<int>(getPayloadRef()[0], 2, '0');
+ sstr << " key=0x" + hexa8(1, getPayload());
+ }
+ else if(getMsgId()==MESG_SEARCH_WAVEFORM_ID)
+ {
+ sstr << " chan=0x" + toString<int>(getPayloadRef()[0], 2, '0');
+ sstr << " wave=0x" << toString<int>(ushort(getPayloadRef()[1]*256+getPayloadRef()[2]), 4, '0');
+ }
+ else if(getMsgId()==MESG_OPEN_CHANNEL_ID)
+ {
+ sstr << " chan=0x" + toString<int>(getPayloadRef()[0], 2, '0');
+ }
+ else if(getMsgId()==MESG_REQUEST_ID)
+ {
+ sstr << " chan=0x" + toString<int>(getPayload()[0], 2, '0');
+ sstr << " reqMsgId=" + msgId2Str(getPayload()[1]);
+ }
+ else if(getMsgId()==MESG_BROADCAST_DATA_ID)
+ {
+ int len = getLenPayload();
+ vector<uchar> payl=getPayload();
+ sstr << " chan=0x" + toString<int>(getPayload()[0], 2, '0');
+ sstr << antfs2Str();
+ }
+ else if(getMsgId()==MESG_ACKNOWLEDGED_DATA_ID)
+ {
+ sstr << " chan=" + toString<int>(getPayload()[0], 2, '0');
+ sstr << antfs2Str();
+ }
+ else if(getMsgId()==MESG_BURST_DATA_ID)
+ {
+ assert(getLenPayload()==9);
+ for (size_t i=0;i<9;i++)
+ {
+ vector<uchar> payl=getPayload();
+ const uchar* p=getPayloadRef();
+ assert(payl[i]==p[i]);
+ }
+ const M_ANT_Burst* burst(reinterpret_cast<const M_ANT_Burst*>(getPayloadRef()));
+ //sstr << " chan=0x" << toString<int>(burst->chan,2,'0') << ", seq=" << toStringDec<int>(burst->seq,1,' ') << ", last=" << (burst->last?"yes":"no ");
+ sstr << burst->toString();
+ sstr << antfs2Str();
+ }
+ else if(getMsgId()==MESG_CHANNEL_ID_ID)
+ {
+ struct M_ANT_Channel_Id
+ {
+ uchar chan;
+ ushort devNum;
+ uchar devTypeId;
+ uchar manId;
+ const std::string toString() const
+ {
+ std::stringstream sstr;
+ sstr
+ << " chan=0x" << ::toString<int>(chan,2,'0') << ", devNum=0x" << ::toString<int>(devNum,4,'0')
+ << ", typ=0x" << ::toString<int>(devTypeId,2,'0') << ", man=0x" << ::toString<int>(manId,2,'0');
+ return sstr.str();
+ }
+ };
+ const M_ANT_Channel_Id* mesg(reinterpret_cast<const M_ANT_Channel_Id*>(getPayloadRef()));
+ sstr << mesg->toString();
+ }
+ else if(getMsgId()==MESG_CHANNEL_STATUS_ID)
+ {
+ enum {
+ UnAssigned = 0,
+ Assigned = 1,
+ Searching = 2,
+ Tracking = 3
+ } ;
+ sstr << " chan=" + toString<int>(getPayload()[0], 2, '0');
+ uchar chanSt=getPayloadRef()[1]&0x03;
+ struct {
+ const char* szChanSt(uchar chanSt)
+ {
+ if(chanSt==UnAssigned) return "chanSt=UnAssigned";
+ else if(chanSt==Assigned) return "chanSt=Assigned";
+ else if(chanSt==Searching) return "chanSt=Searching";
+ else if(chanSt==Tracking) return "chanSt=Tracking";
+ else return "chanSt=??";
+ } } CHST;
+ sstr << " " << CHST.szChanSt(chanSt);
+ }
+
+ return sstr.str();
+}
+
+
+const string AntMessage::strDt(const double &dt) const
+{
+ if(bytes.empty())
+ return "? ???";
+
+ std::string str=sent?"S ":"R ";
+
+
+ str += toStringDec<double>(dt, 7, ' ') + " ";
+
+ str += str2();
+
+ return str;
+}
+
+
+const std::string
+AntMessage::strExt() const
+{
+ return dump() + str2();
+}
+
+const std::string
+AntMessage::antfs2Str() const
+{
+ bool isCommand=false;
+ bool valid = isAntFSCommandOrResponse(getPayload()[2], isCommand);
+ std::stringstream sstr;
+ if(getPayload()[1]==ANTFS_BeaconId)
+ {
+ const M_ANTFS_Beacon* beacon(reinterpret_cast<const M_ANTFS_Beacon*>(&getPayloadRef()[1]));
+ sstr << beacon->toString();// << hexa8(1, getPayload());
+ }
+ else if(getPayload()[1]==ANTFS_CommandResponseId && isCommand && valid)
+ {
+ const M_ANTFS_Command* cmdResp(reinterpret_cast<const M_ANTFS_Command*>(&getPayloadRef()[1]));
+ sstr << cmdResp->toString();// << hexa8(1, getPayload());
+ }
+ else if(getPayload()[1]==ANTFS_CommandResponseId && !isCommand && valid)
+ {
+ const M_ANTFS_Response* cmdResp(reinterpret_cast<const M_ANTFS_Response*>(&getPayloadRef()[1]));
+ //if(lookupInVector && cmdResp->response==ANTFS_ReqDownload)
+ sstr << cmdResp->toString();// << hexa8(1, getPayload());
+ }
+ else
+ {
+ sstr << hexa8(1, getPayload());
+ }
+ return sstr.str();
+}
+
+const std::string
+AntMessage::dump() const
+{
+ assert(bytes.size()>=5);
+ assert(bytes.size()<=13);
+ std::stringstream ss;
+ ss << (sent?"S":"R") << "[" << std::setw(2) << bytes.size() << "] ";
+ for(size_t i=0; i<bytes.size(); i++)
+ {
+ ss << std::setw(2) << std::setfill('0') << std::hex << int(bytes[i]);
+ if(i+1!=bytes.size()) ss << "_"; else ss << " ";
+ }
+ for(size_t i=bytes.size(); i<13; i++)
+ {
+ ss << " ";
+ }
+ return ss.str();
+}
+
+
+
+const std::string
+AntMessage::dumpDumb() const
+{
+ std::stringstream ss;
+ ss << " " << "[" << std::setw(2) << bytes.size() << "] ";
+ for(size_t i=0; i<bytes.size(); i++)
+ {
+ ss << std::setw(2) << std::setfill('0') << std::hex << int(bytes[i]);
+ if(i+1!=bytes.size()) ss << "_"; else ss << " ";
+ }
+ for(size_t i=bytes.size(); i<13; i++)
+ {
+ ss << " ";
+ }
+ return ss.str();
+}
+
+
+bool AntMessage::interpret()
+{
+ if(bytes.size()<5)
+ {
+ //printf("not enough bytes in raw stream: %s\n", dumpDumb().c_str());
+ return false;
+ }
+ if(getSync() != MESG_TX_SYNC)
+ {
+ printf("no TX_SYNC byte: %s", dumpDumb().c_str());
+ return false;
+ }
+ //if(getMsgId()==0xE2 || getMsgId()==0xE1 || getMsgId()==0xE0)
+ //{
+ // if(bytes.size()<static_cast<size_t>(getLenPayload()+5))
+ // return false;
+ // abort();// FIXME: extended message
+ // if(!vrfChkSum())
+ // return false;
+ //}
+ //else
+ {
+ const size_t minLen=static_cast<size_t>(getLenPayload()+4);
+ if(bytes.size()<minLen)
+ {
+ //printf("not enough bytes to cover payload: %s", dump().c_str());
+ return false;
+ }
+ if(!vrfChkSum())
+ {
+ printf("checksum verification failed: %s", dump().c_str());
+ return false;
+ }
+ // TODO: handle case where multiple messages are concatenated in this->bytes
+ ptrdiff_t residue = bytes.end()-bytes.begin()+getLenPacket();
+ //fprintf(loggerc(), "%d bytes for next decode\n", (int)residue);
+ bytes.erase(bytes.begin()+getLenPacket(), bytes.end());
+ }
+ return true;
+}
+
+// interpret byte stream, assemble packets and store results in \a messages
+bool
+AntMessage::interpret2(std::list<uchar>& q, std::vector<AntMessage>& messages)
+{
+ if(q.empty())
+ return false;
+
+ while(!q.empty())
+ {
+ if(q.size()==2
+ && *(q.begin())==0x00
+ && *(q.begin()++)==0x00)
+ {
+ // the usbmon logs seem to suffix sent messages with two zeroes...
+ q.clear();
+ return true;
+ }
+
+ AntMessage m;
+ m.sent = false;
+ m.bytes.resize(q.size());
+ std::copy(q.begin(), q.end(), m.bytes.begin());
+
+ bool ok = m.interpret();
+ if(!ok)
+ {
+ printf("interpret failed!\n"); fflush(stdout);
+ return false;
+ }
+ for(size_t i=0; i<m.getLenPacket(); i++)
+ {
+ q.pop_front();
+ }
+ //m.idx = packetIdx++;
+ messages.push_back(m);
+ }
+
+ return true;
+}
+
+size_t AntMessage::getLenPacket() const
+{
+ return getLenPayload()+4;
+}
+
+size_t AntMessage::getLenRaw() const
+{
+ return bytes.size();
+}
+
+
+bool
+AntMessage::getChannelNumber(uchar &chan)
+{
+ //if(getMsgId()==MESG_RESPONSE_EVENT_ID)
+ // ;
+ if(getMsgId()==MESG_BURST_DATA_ID && getLenPayload()==9)
+ {
+ chan = getPayload()[0];
+ return true;
+ }
+ return false;
+}
+
+
+bool AntMessage::fromStringOfBytes(const char *s)
+{
+ return stringToBytes(s, this->bytes);
+}
+
+
+template <class Container>
+bool
+AntMessage::stringToBytes(const char *s, Container& bytes)
+{
+ // e.g. "a409502022008004310000b0fa"
+ size_t chars=strlen(s);
+ if(chars%2)
+ return false;
+ bytes.clear();
+ for(size_t i=0; i<chars/2;i++)
+ {
+ char xx[3]={s[i*2], s[i*2+1], 0x00};
+ bytes.push_back(uchar(strtoul(xx, NULL, 16)));
+ }
+ assert(bytes.size()*2==chars);
+ return true;
+}
+
+
+bool AntMessage::assemble(unsigned char mesg, const unsigned char *inbuf, unsigned char len)
+{
+ bytes.clear();
+ bytes.resize(len+4);
+ bytes[0] = MESG_TX_SYNC;
+ bytes[1] = len;
+ bytes[2] = mesg;
+ for(size_t i=0; i< len; i++)
+ bytes[3+i] = inbuf[i];
+ unsigned char chk = getCheckSum();
+ bytes[len+3] = chk;
+ return true;
+}
+
+
+template <class Container>
+bool
+AntMessage::saveAsUsbMon(const char* fileName, const Container& messages)
+{
+ std::ofstream of(fileName);
+ CHECK_RETURN_FALSE(of.is_open());
+
+ return saveAsUsbMon(of, messages);
+}
+
+
+template <class Container>
+bool
+AntMessage::saveAsUsbMon(std::ostream& os, const Container& messages)
+{
+ CHECK_RETURN_FALSE(os.good());
+ // dd65f0e8 4128379752 S Bo:1:005:2 -115 31 = 55534243 5e000000 00000000 00000600 00000000 00000000 00000000 000000
+ // dd65f0e8 4128379808 C Bo:1:005:2 0 31 >
+ for(typename Container::const_iterator i=messages.begin(); i!= messages.end(); i++)
+ {
+#define USB_SENT_FIXUP
+ size_t bytes = i->getLenPacket();
+ os << "ffff880064cd3f00 " << "3647721914 " /*<< i->timestamp*/ << ((i->sent?"S ":"C ")) << (i->sent?"Bo":"Bi") << ":3:002:1 " << ((i->sent?"-115 ":"0 "));
+#ifdef USB_SENT_FIXUP
+ if(i->sent)
+ os << bytes+2;
+ else
+ os << bytes;
+#else
+ os << bytes;
+#endif
+ os << " = ";
+ for (size_t j=0; j<bytes; j++)
+ {
+ uchar c=i->getBytes()[j];
+ os << toString(int(c), 2, '0');
+ if((j+1)%4==0 && j+1!=bytes)
+ os<< " ";
+ }
+#ifdef USB_SENT_FIXUP
+ if(i->sent) os<<"0000";
+#endif
+ os<<"\n";
+ }
+ return true;
+}
+
+
+template <class Container>
+bool
+AntMessage::saveAsAntParse(const char* fileName, const Container& messages)
+{
+ std::ofstream of(fileName, ios_base::out | ios_base::app );
+ CHECK_RETURN_FALSE(of.is_open());
+
+ return saveAsAntParse(of, messages);
+}
+
+
+template <class Container>
+bool
+AntMessage::saveAsAntParse(std::ostream& os, const Container& messages)
+{
+ CHECK_RETURN_FALSE(os.good());
+
+ boost::system_time t0;
+ for(typename Container::const_iterator i=messages.begin(); i!= messages.end(); i++)
+ {
+ if(i==messages.begin())
+ t0=i->timestamp;
+ boost::system_time t1 = i->timestamp;
+ boost::posix_time::time_duration td = t1 - t0;
+ t0 = t1;
+ double dt = double(td.total_microseconds())*0.001;
+ os << i->strDt(dt) << "\n";
+ }
+ return true;
+}
+
+
+uchar
+AntMessage::getCheckSum() const
+{
+ unsigned char chk = bytes[0];
+ for(size_t i=1; i< getLenPacket()-1; i++)
+ chk ^= bytes[i];
+ return chk;
+}
+
+
+std::vector<uchar>
+AntMessage::getPayload() const
+{
+ std::vector<uchar> pl;
+ pl.resize(getLenPayload());
+ std::copy(bytes.begin()+3, bytes.begin()+3+getLenPayload(), pl.begin());
+ return pl;
+}
+
+
+uchar*
+AntMessage::getPayloadRef()
+{
+ return &bytes[3];
+}
+
+const uchar*
+AntMessage::getPayloadRef() const
+{
+ return &bytes[3];
+}
+
+
+uchar*
+AntMessage::getBytes()
+{
+ return &bytes[0];
+}
+
+const uchar*
+AntMessage::getBytes() const
+{
+ return &bytes[0];
+}
+
+bool
+AntFsFile::checkCrc(const ushort seed) const
+{
+ if(bytes.size()<24)
+ return false;
+ ushort crc=seed;
+ for (size_t i=16;i+8<bytes.size(); i++)
+ {
+ crc = crc16byte(crc, bytes[i]);
+ }
+ const ushort* tail = reinterpret_cast<const ushort*>(&bytes[bytes.size()-1-2]);
+ return *tail == crc;
+}
+
+ushort
+AntFsFile::crc16Calc(const ushort seed) const
+{
+ ushort crc=seed;
+ for (size_t i=0;i<bytes.size(); i++)
+ {
+ crc = crc16byte(crc, bytes[i]);
+ }
+ return crc;
+}
+
+ushort
+AntFsFile::crc16byte(const ushort crc0, uchar byte) const
+{
+ static const ushort crc_table[16] =
+ {
+ 0x0000, 0xCC01, 0xD801, 0x1400, 0xF001, 0x3C00, 0x2800, 0xE401,
+ 0xA001, 0x6C00, 0x7800, 0xB401, 0x5000, 0x9C01, 0x8801, 0x4400
+ };
+
+ ushort tmp = crc_table[crc0 & 0xF];
+ ushort crc = (crc0 >> 4) & 0x0FFF;
+ crc = crc ^ tmp ^ crc_table[byte & 0xF];
+
+ tmp = crc_table[crc & 0xF];
+ crc = (crc >> 4) & 0x0FFF;
+ crc = crc ^ tmp ^ crc_table[(byte >> 4) & 0xF];
+
+ return crc;
+}
+
+
+bool
+AntFsFile::saveToFile(const char* fileName /* = "antfs.bin" */)
+{
+ logger() << "Saving '" << fileName << "'...\n";
+ if(bytes.empty()) return false;
+ FILE *f=fopen(fileName, "wb");
+ if(!f) return false;
+ if(bytes.size() != (size_t)fwrite(&bytes[0], bytes.size(), 1 , f)) { fclose(f); return false; }
+ fclose(f);
+ return true;
+}
+
+
+// explicit template instantiations
+
+template bool AntMessage::stringToBytes(const char *s, std::list<uchar>& bytes);
+template bool AntMessage::stringToBytes(const char *s, std::vector<uchar>& bytes);
+template bool AntMessage::saveAsUsbMon<std::list<AntMessage> >(const char* fileName, const std::list<AntMessage>& messages);
+//template bool AntMessage::saveAsUsbMon<std::queue<AntMessage> >(const char* fileName, const std::queue<AntMessage>& messages);
+template bool AntMessage::saveAsUsbMon<std::vector<AntMessage> >(const char* fileName, const std::vector<AntMessage>& messages);
+template bool AntMessage::saveAsUsbMon<std::list<AntMessage> >(std::ostream& os, const std::list<AntMessage>& messages);
+template bool AntMessage::saveAsUsbMon<std::vector<AntMessage> >(std::ostream& os, const std::vector<AntMessage>& messages);
+
+template bool AntMessage::saveAsAntParse<std::list<AntMessage> >(const char* fileName, const std::list<AntMessage>& messages);
+//template bool AntMessage::saveAsAntParse<std::queue<AntMessage> >(const char* fileName, const std::queue<AntMessage>& messages);
+template bool AntMessage::saveAsAntParse<std::vector<AntMessage> >(const char* fileName, const std::vector<AntMessage>& messages);
+
+template bool AntMessage::saveAsAntParse<std::list<AntMessage> >(std::ostream& os, const std::list<AntMessage>& messages);
+//template bool AntMessage::saveAsAntParse<std::queue<AntMessage> >(std::ostream& os, const std::queue<AntMessage>& messages);
+template bool AntMessage::saveAsAntParse<std::vector<AntMessage> >(std::ostream& os, const std::vector<AntMessage>& messages);
diff --git a/src/AntMessage.hpp b/src/AntMessage.hpp
new file mode 100644
index 0000000..4f88271
--- /dev/null
+++ b/src/AntMessage.hpp
@@ -0,0 +1,586 @@
+// -*- mode: c++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2; coding: utf-8-unix -*-
+// ***** BEGIN LICENSE BLOCK *****
+////////////////////////////////////////////////////////////////////
+// Copyright (c) 2011-2013 RALOVICH, Kristóf //
+// //
+// This program is free software; you can redistribute it and/or //
+// modify it under the terms of the GNU General Public License //
+// version 2 as published by the Free Software Foundation. //
+// //
+////////////////////////////////////////////////////////////////////
+// ***** END LICENSE BLOCK *****
+#pragma once
+
+#include "antdefs.hpp"
+#include <vector>
+#include <list>
+#include <string>
+#include "common.hpp"
+#include <cassert>
+#include <sstream>
+#include <boost/static_assert.hpp>
+#include <boost/thread/thread_time.hpp>
+
+
+std::string antFSCommand2Str(uchar cmd);
+std::string antFSResponse2Str(uchar resp);
+bool isAntFSCommandOrResponse(const uchar command, bool& isCommand);
+
+#pragma pack(push,1)
+struct M_ANTFS_Beacon
+{
+ uchar beaconId;//0x43
+ union
+ {
+ struct
+ {
+ uchar beaconChannelPeriod:3;
+ uchar pairingEnabled:1;
+ uchar uploadEnabled:1;
+ uchar dataAvail:1;
+ uchar reserved0:2;
+ };
+ uchar status1;
+ };
+ union
+ {
+ struct
+ {
+ uchar clientDeviceState:4;
+ uchar reserved1:4;
+ };
+ uchar status2;
+ };
+ uchar authType;
+ union
+ {
+ struct
+ {
+ ushort dev;
+ ushort manuf;
+ };
+ struct
+ {
+ uint sn;
+ };
+ uchar sn4[4];
+ };
+
+
+ const char* szBeaconChannelPeriod() const
+ {
+ if(beaconChannelPeriod==0x0) return "Beacon=0.5Hz";
+ else if(beaconChannelPeriod==0x1) return "Beacon=1Hz";
+ else if(beaconChannelPeriod==0x2) return "Beacon=2Hz";
+ else if(beaconChannelPeriod==0x3) return "Beacon=4Hz";
+ else if(beaconChannelPeriod==0x4) return "Beacon=8Hz";
+ else if(beaconChannelPeriod==0x7) return "Beacon=MatchEstablishedChannelPeriod";
+ else return "Beacon=??";
+ }
+ const char* szPairingEnabled() const
+ {
+ return pairingEnabled ? "pairing=enabled" : "pairing=disabled";
+ }
+ const char* szUploadEnabled() const
+ {
+ return uploadEnabled ? "upload=enabled" : "upload=disabled";
+ }
+ const char* szClientDeviceState() const
+ {
+ if(clientDeviceState==0x00) return "State=Link";
+ else if(clientDeviceState==0x01) return "State=Authentication";
+ else if(clientDeviceState==0x02) return "State=Transport";
+ else if(clientDeviceState==0x03) return "State=Busy";
+ else return "State=??";
+ }
+ const char* szDataAvail() const
+ {
+ return dataAvail ? "dataAvail=yes" : "dataAvail=no";
+ }
+ const char* szAuthType() const
+ {
+ if(authType==0x0) return "Auth=Passthrough";
+ else if(authType==0x1) return "Auth=NA";
+ else if(authType==0x2) return "Auth=PairingOnly";
+ else if(authType==0x3) return "Auth=PasskeyAndPairingOnly";
+ else return "Auth=??";
+ }
+ const std::string strDeviceDescriptor() const
+ {
+ return std::string("dev=0x") + ::toString(this->dev, 4, '0') + std::string("manuf=0x") + ::toString(this->manuf, 4, '0');
+ }
+ const std::string strDeviceSerial() const
+ {
+ return std::string("SN=0x") + ::toString(this->sn, 8, '0');
+ }
+ void getDeviceDescriptor(ushort& dev, ushort& manuf) const
+ {
+ dev=this->dev;
+ manuf=this->manuf;
+ }
+ uint getDeviceSerial() const
+ {
+ return this->sn;
+ }
+ const std::string toString() const
+ {
+ assert(beaconId==0x43);
+ std::stringstream sstr;
+ sstr << " ANTFS_BEACON(0x" << ::toString(unsigned(beaconId), 2, '0') << ") "
+ << this->szBeaconChannelPeriod()
+ << ", " << this->szPairingEnabled()
+ << ", " << this->szUploadEnabled()
+ << ", " << this->szDataAvail()
+ << ", " << this->szClientDeviceState()
+ << ", " << this->szAuthType();
+ return sstr.str();
+ }
+};
+#pragma pack(pop)
+BOOST_STATIC_ASSERT(sizeof(M_ANTFS_Beacon)==8);
+
+
+#pragma pack(push,1)
+struct M_ANTFS_Command
+{
+ uchar commandId;//0x44
+ uchar command;
+ enum
+ {
+ ReturnToLinkLayer=0,
+ ReturnToBroadcastMode=1
+ };
+ enum
+ {
+ ProceedToTransport=0,
+ RequestClientDeviceSerialNumber=1,
+ RequestPairing=2,
+ RequestPasskeyExchange=3
+ };
+
+ union Detail1
+ {
+ struct Link
+ {
+ uchar chanFreq;
+ uchar chanPeriod;
+ union
+ {
+ struct
+ {
+ uint sn;
+ };
+ uchar sn4[4];
+ };
+ const std::string toString() const
+ {
+ std::stringstream sstr;
+ sstr << "freq=0x" << ::toString(unsigned(chanFreq), 2, '0') << ", period=0x" << ::toString(unsigned(chanPeriod), 2, '0') << ", SNhost=0x" << ::toString(sn, 8, '0');
+ return sstr.str();
+ }
+ } link;
+ struct Disconnect
+ {
+ uchar cmdType;
+ const char* szCmdType() const
+ {
+ if(cmdType==ReturnToLinkLayer) return "type=ReturnToLinkLayer";
+ else if(cmdType==ReturnToBroadcastMode) return "type=ReturnToBroadcastMode";
+ else return "type=??";
+ }
+ const std::string toString() const
+ {
+ std::stringstream sstr;
+ sstr << szCmdType();
+ return sstr.str();
+ }
+ } disconnect;
+ struct Authenticate
+ {
+ uchar cmdType;
+ uchar authStrLen;
+ union
+ {
+ struct
+ {
+ uint sn;
+ };
+ uchar sn4[4];
+ };
+ // TODO:extra bytes ....
+ const char* szCmdType() const
+ {
+ if(cmdType==ProceedToTransport) return "type=ProceedToTransport(pass-through)";
+ else if(cmdType==RequestClientDeviceSerialNumber) return "type=RequestClientDeviceSerialNumber";
+ else if(cmdType==RequestPairing) return "type=RequestPairing";
+ else if(cmdType==RequestPasskeyExchange) return "type=RequestPasskeyExchange";
+ else return "type=??";
+ }
+ const std::string toString() const
+ {
+ std::stringstream sstr;
+ sstr << szCmdType() << ", authStrLen=" << int(authStrLen) << ", SNhost=0x" << ::toString(sn, 8, '0');
+ return sstr.str();
+ }
+ } authenticate;
+ // ping
+ struct DownloadRequest
+ {
+ ushort dataFileIdx;
+ uint dataOffset;
+ // ...
+ const std::string toString() const
+ {
+ std::stringstream sstr;
+ sstr << "file=0x" << ::toString(dataFileIdx, 4, '0') << ", dataOffset=0x" << ::toString(dataOffset, 8, '0');
+ return sstr.str();
+ }
+ } downloadRequest;
+ struct UploadRequest
+ {
+ ushort dataFileIdx;
+ uint maxSize;
+ //uint dataOffset; //TODO: in next burst packet
+ const std::string toString() const
+ {
+ std::stringstream sstr;
+ sstr << "file=0x" << ::toString(dataFileIdx, 4, '0') << ", maxSize=0x" << ::toString(maxSize, 8, '0');
+ return sstr.str();
+ }
+ } uploadRequest;
+ struct EraseRequest
+ {
+ ushort dataFileIdx;
+ // ...
+ const std::string toString() const
+ {
+ std::stringstream sstr;
+ sstr << "dataFileIdx=0x" << ::toString(dataFileIdx, 4, '0');
+ return sstr.str();
+ }
+ } eraseRequest;
+ struct UploadData
+ {
+ ushort crcSeed;
+ uint dataOffset;
+ // TODO: more burst packets
+ // uchar unused6[6];
+ // ushort crc;
+ const std::string toString() const
+ {
+ std::stringstream sstr;
+ sstr << "crcSeed=0x" << ::toString(crcSeed, 4, '0') << ", dataOffset=0x" << ::toString(dataOffset, 8, '0');
+ return sstr.str();
+ }
+ } uploadData;
+ } detail;
+ const std::string toString() const
+ {
+ assert(commandId==0x44);
+ std::stringstream sstr;
+ sstr << " ANTFS_CMD(0x" << ::toString(unsigned(commandId),2,'0') << ") "
+ << antFSCommand2Str(command);
+ if(command==ANTFS_CmdLink) sstr << " " << detail.link.toString();
+ else if(command==ANTFS_CmdDisconnect) sstr << " " << detail.disconnect.toString();
+ else if(command==ANTFS_CmdAuthenticate) sstr << " " << detail.authenticate.toString();
+ else if(command==ANTFS_ReqDownload) sstr << " " << detail.downloadRequest.toString();
+ else if(command==ANTFS_ReqUpload) sstr << " " << detail.uploadRequest.toString();
+ else if(command==ANTFS_ReqErase) sstr << " " << detail.eraseRequest.toString();
+ else if(command==ANTFS_UploadData) sstr << " " << detail.uploadData.toString();
+ return sstr.str();
+ }
+};
+struct M_ANTFS_Command_Authenticate : public M_ANTFS_Command
+{
+ uint64_t key;
+ uint64_t footer;
+};
+struct M_ANTFS_Command_Download : public M_ANTFS_Command
+{
+ uchar zero;
+ uchar initReq;
+ ushort crcSeed;
+ uint maxBlockSize;
+};
+struct M_ANTFS_Command_Pairing : public M_ANTFS_Command
+{
+ uchar name[8]; // seems like 310XT can display properly up to 8 chars during the pairing screen
+};
+#pragma pack(pop)
+BOOST_STATIC_ASSERT(sizeof(M_ANTFS_Command)==8);
+BOOST_STATIC_ASSERT(sizeof(M_ANTFS_Command_Authenticate)==24);
+BOOST_STATIC_ASSERT(sizeof(M_ANTFS_Command_Download)==16);
+BOOST_STATIC_ASSERT(sizeof(M_ANTFS_Command_Pairing)==16);
+
+#pragma pack(push,1)
+struct M_ANTFS_Response
+{
+ uchar responseId;//0x44
+ uchar response;
+ enum {
+ DownloadRequestOK=0,
+ DataDoesNotExist=1,
+ DataExistsButIsNotDownloadable=2,
+ NotReadyToDownload=3,
+ DownloadRequestInvalid=4,
+ CRCIncorrect=5
+ };
+ enum {
+ UploadRequestOK=0,
+ DataFileIndexDoesNotExist=1,
+ DataFileIndexExistsButIsNotWriteable=2,
+ NotEnoughSpaceToCompleteWrite=3,
+ UploadRequestInvalid=4,
+ NotReadyToUpload=5
+ };
+ enum {
+ DataUploadSuccessfulOK=0,
+ DataUploadFailed=1
+ };
+ union Detail
+ {
+ struct AuthenticateResponse
+ {
+ uchar respType;
+ uchar authStrLen;
+ union
+ {
+ struct
+ {
+ uint sn;
+ };
+ uchar sn4[4];
+ };
+ // TODO:extra bytes in following burst message
+ const char* szRespType() const
+ {
+ if(respType==0) return "resp=SN";
+ else if(respType==1) return "resp=accept";
+ else if (respType==2) return "resp=reject";
+ else return "resp=??";
+ }
+ const std::string toString() const
+ {
+ std::stringstream sstr;
+ sstr << szRespType() << ", authStrLen=" << int(authStrLen) << ", SNclient=0x" << ::toString(sn, 8, '0');
+ return sstr.str();
+ }
+ } authenticateResponse;
+ // ping
+ struct DownloadRequestResponse
+ {
+ uchar responseVal;
+ uchar reserved;
+ uint remainingBytes;
+ // ...
+ const char* szResponseVal() const
+ {
+ if(responseVal==DownloadRequestOK) return "resp=DownloadRequestOK";
+ else if(responseVal==DataDoesNotExist) return "resp=DataDoesNotExist";
+ else if(responseVal==DataExistsButIsNotDownloadable) return "resp=DataExistsButIsNotDownloadable";
+ else if(responseVal==NotReadyToDownload) return "resp=NotReadyToDownload";
+ else if(responseVal==DownloadRequestInvalid) return "resp=DownloadRequestInvalid";
+ else if(responseVal==CRCIncorrect) return "resp=CRCIncorrect";
+ return "resp=??";
+ }
+ const std::string toString() const
+ {
+ std::stringstream sstr;
+ sstr << szResponseVal() << ", remainingBytes=0x" << ::toString(remainingBytes, 8, '0');
+ return sstr.str();
+ }
+ } downloadRequestResponse;
+ struct UploadRequestResponse
+ {
+ uchar responseVal;
+ uchar unused1;
+ uint lastDataOffset;
+ // TODO: 4 packets in total
+ const char* szResponseVal() const
+ {
+ if(responseVal==UploadRequestOK) return "resp=UploadRequestOK";
+ else if(responseVal==DataFileIndexDoesNotExist) return "resp=DataFileIndexDoesNotExist";
+ else if(responseVal==DataFileIndexExistsButIsNotWriteable) return "resp=DataFileIndexExistsButIsNotWriteable";
+ else if(responseVal==NotEnoughSpaceToCompleteWrite) return "resp=NotEnoughSpaceToCompleteWrite";
+ else if(responseVal==UploadRequestInvalid) return "resp=UploadRequestInvalid";
+ else if(responseVal==NotReadyToUpload) return "resp=NotReadyToUpload";
+ return "resp=??";
+ }
+ const std::string toString() const
+ {
+ std::stringstream sstr;
+ sstr << szResponseVal() << ", lastDataOffset=0x" << ::toString(lastDataOffset, 8, '0');
+ return sstr.str();
+ }
+ } uploadRequestResponse;
+ struct EraseRequestResponse
+ {
+ uchar responseVal;
+ const char* szResponseVal() const
+ {
+ if(responseVal==0) return "resp=EraseSuccessful";
+ else if(responseVal==1) return "resp=EraseFailed";
+ else if(responseVal==2) return "resp=NotReady";
+ return "resp=??";
+ }
+ const std::string toString() const
+ {
+ std::stringstream sstr;
+ sstr << szResponseVal() << " 0x" << ::toString<int>(int(responseVal),2,'0');
+ return sstr.str();
+ }
+ } eraseRequestResponse;
+ struct UploadDataResponse
+ {
+ // TODO: first is a beacon
+ uchar responseVal;
+ const char* szResponseVal() const
+ {
+ if(responseVal==DataUploadSuccessfulOK) return "resp=DataUploadSuccessfulOK";
+ else if(responseVal==DataUploadFailed) return "resp=DataUploadFailed";
+ return "resp=??";
+ }
+ const std::string toString() const
+ {
+ std::stringstream sstr;
+ sstr << szResponseVal();
+ return sstr.str();
+ }
+ } uploadDataResponse;
+ } detail;
+ const std::string toString() const
+ {
+ assert(responseId==0x44);
+ std::stringstream sstr;
+ sstr << " ANTFS_RESP(0x" << ::toString(unsigned(responseId),2,'0') << ") "
+ << antFSResponse2Str(response);
+ if(response==ANTFS_RespAuthenticate) sstr << " " << detail.authenticateResponse.toString();
+ else if(response==ANTFS_RespDownload) sstr << " " << detail.downloadRequestResponse.toString();
+ else if(response==ANTFS_RespUpload) sstr << " " << detail.uploadRequestResponse.toString();
+ else if(response==ANTFS_RespErase) sstr << " " << detail.eraseRequestResponse.toString();
+ else if(response==ANTFS_RespUploadData) sstr << " " << detail.uploadDataResponse.toString();
+ return sstr.str();
+ }
+};
+struct M_ANTFS_Response_Download : public M_ANTFS_Response
+{
+ uint dataOff;
+ uint fileSize;
+};
+struct M_ANTFS_Response_Download_Footer
+{
+ uchar reserved[6];
+ ushort crc;
+};
+struct M_ANTFS_Response_Pairing : public M_ANTFS_Response
+{
+ uint64_t pairedKey;
+};
+#pragma pack(pop)
+BOOST_STATIC_ASSERT(sizeof(M_ANTFS_Response)==8);
+BOOST_STATIC_ASSERT(sizeof(M_ANTFS_Response_Download)==16);
+BOOST_STATIC_ASSERT(sizeof(M_ANTFS_Response_Download_Footer)==8);
+BOOST_STATIC_ASSERT(sizeof(M_ANTFS_Response_Pairing)==16);
+
+#pragma pack(push,1)
+struct M_ANT_Burst
+{
+ union {
+ struct {
+ uchar chan:5;
+ uchar seq:2;
+ uchar last:1;
+ };
+ uchar seqchan;
+ };
+ uchar data8[8];
+ const std::string toString() const
+ {
+ std::stringstream sstr;
+ sstr << " chan=0x" << ::toString<int>(chan,2,'0') << ", seq=" << ::toStringDec<int>(seq,1,' ') << ", last=" << (isLast()?"yes":"no ");
+ return sstr.str();
+ }
+ bool isLast() const
+ {
+ bool isLast = ((last!=0x00)?true:false);
+ bool isLast2 = (((seqchan&0x80)==0x80)?true:false);
+ assert(isLast==isLast2);
+
+ return isLast;
+ }
+};
+#pragma pack(pop)
+BOOST_STATIC_ASSERT(sizeof(M_ANT_Burst)==9);
+
+
+
+//
+struct AntMessage{
+ bool sent; // sent or received
+ boost::system_time timestamp;
+ size_t idx; //
+ std::vector<unsigned char> bytes; // the raw message bytes
+
+ static bool lookupInVector;
+
+public:
+ AntMessage() {}
+ AntMessage(uchar mesg, uchar data1) {uchar buf[1] = {data1}; if(!assemble(mesg, buf, sizeof(buf))) throw 0; }
+ AntMessage(uchar mesg, uchar data1, uchar data2) {uchar buf[2] = {data1, data2 }; if(!assemble(mesg, buf, sizeof(buf))) throw 0; }
+ AntMessage(uchar mesg, uchar data1, uchar data2, uchar data3) {uchar buf[3] = {data1, data2, data3 }; if(!assemble(mesg, buf, sizeof(buf))) throw 0; }
+
+ bool vrfChkSum() const;
+ static std::string msgId2Str(uchar msgId);
+ static std::string msgCode2Str(uchar msgCode);
+ const std::string str() const;
+ const std::string str2() const;
+ const std::string strDt(const double& dt) const;
+ const std::string strExt() const;
+ const std::string antfs2Str() const;
+ const std::string dump() const;
+ const std::string dumpDumb() const;
+
+ // receiving
+ bool interpret();
+ static bool interpret2(std::list<uchar>& q, std::vector<AntMessage>& messages);
+ size_t getLenPacket() const;
+ size_t getLenRaw() const;
+ bool getChannelNumber(uchar& chan);
+ bool fromStringOfBytes(const char* s);
+ template <class Container>
+ static bool stringToBytes(const char* s, Container& bytes);
+
+ // sending
+ bool assemble(unsigned char mesg, const unsigned char *inbuf, unsigned char len);
+
+ template <class Container>
+ static bool saveAsUsbMon(const char* fileName, const Container& messages);
+ template <class Container>
+ static bool saveAsUsbMon(std::ostream& os, const Container& messages);
+ template <class Container>
+ static bool saveAsAntParse(const char* fileName, const Container& messages);
+ template <class Container>
+ static bool saveAsAntParse(std::ostream& os, const Container& messages);
+
+//private:
+ uchar getSync() const {return bytes[0]; }
+ uchar getLenPayload() const { return bytes[1]; }
+ uchar getMsgId() const { return bytes[2]; }
+ uchar getCheckSum() const;
+ std::vector<uchar> getPayload() const;
+ uchar* getPayloadRef();
+ const uchar* getPayloadRef() const;
+ uchar* getBytes();
+ const uchar* getBytes() const;
+};
+
+
+struct AntFsFile
+{
+ std::vector<uchar> bytes;
+ bool checkCrc(const ushort seed = 0x0000) const;
+ ushort crc16Calc(const ushort seed = 0x0000) const;
+ ushort crc16byte(const ushort crc, uchar byte) const;
+ bool saveToFile(const char* fileName = "antfs.bin");
+};
+
diff --git a/src/AntMessenger.cpp b/src/AntMessenger.cpp
new file mode 100644
index 0000000..ac33ba7
--- /dev/null
+++ b/src/AntMessenger.cpp
@@ -0,0 +1,1204 @@
+// -*- mode: c++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2; coding: utf-8-unix -*-
+// ***** BEGIN LICENSE BLOCK *****
+////////////////////////////////////////////////////////////////////
+// Copyright (c) 2011-2013 RALOVICH, Kristóf //
+// //
+// This program is free software; you can redistribute it and/or //
+// modify it under the terms of the GNU General Public License //
+// version 2 as published by the Free Software Foundation. //
+// //
+////////////////////////////////////////////////////////////////////
+// ***** END LICENSE BLOCK *****
+
+#include "AntMessenger.hpp"
+#include "antdefs.hpp"
+#include <stdio.h>
+#include <string.h>
+#include <stdlib.h>
+
+#include <functional>
+#include <algorithm>
+#include <vector>
+#include <string>
+#include <iostream>
+#include "common.hpp"
+#include <boost/thread/thread_time.hpp>
+#include <boost/foreach.hpp>
+
+
+
+
+
+
+// runs in other thread
+struct AntMessenger_Recevier
+{
+ void operator() (AntMessenger* arg)
+ {
+ //printf("msgFunc, arg: %p\n", arg); fflush(stdout);
+ if(!arg)
+ {
+ rv=0;
+ return;
+ }
+ AntMessenger* This = reinterpret_cast<AntMessenger*>(arg);
+ //printf("msgFunc, This: %p\n", This); fflush(stdout);
+ rv = This->th_messageHandler();
+ }
+ void* rv;
+};
+
+
+AntMessenger::AntMessenger(bool eventLoopInBgTh)
+ : m_io(0)
+ , m_cb(0)
+ , m_packerThKill(0)
+ , m_rpackQueue2(eventLoopInBgTh)
+{
+ m_packerThKill = 0;
+ AntMessenger_Recevier msgTh;
+ msgTh.rv=0;
+ m_packerTh = boost::thread(msgTh, this);
+
+ m_rpackQueue2.setOnDataArrivedCallback(std::bind1st(std::mem_fun(&AntMessenger::onMessage), this));
+
+ for(int i=0; i < ANTPM_MAX_CHANNELS; i++)
+ chs[i].chan = i;
+
+ packetIdx=0;
+}
+
+AntMessenger::~AntMessenger()
+{
+ m_packerThKill = 1;
+ m_packerTh.join();
+ m_io=0;
+ m_cb=0;
+}
+
+size_t AntMessenger::getQueueLength() const
+{
+ return m_rpackQueue2.size();
+}
+
+std::list<AntMessage> AntMessenger::getQueue()
+{
+ return m_rpackQueue2.getListCopy();
+}
+
+
+bool
+AntMessenger::ANT_ResetSystem()
+{
+ uchar filler = 0;
+ bool rv = writeMessage(MESG_SYSTEM_RESET_ID, &filler, 1);
+ sleepms(1000);
+ return rv;
+}
+
+
+bool
+AntMessenger::ANT_SetNetworkKey(const unsigned char net, const unsigned char key[8])
+{
+ uchar buf[9];
+
+ buf[0] = net;
+ memcpy(buf+1, key, 8);
+ bool rv = sendCommand(MESG_NETWORK_KEY_ID, buf, sizeof(buf), 2000);
+ return rv;
+}
+
+bool AntMessenger::ANT_AssignChannel(uchar chan, uchar chtype, uchar net)
+{
+ AntMessage m(MESG_ASSIGN_CHANNEL_ID, chan, chtype, net);
+ return sendCommand(m, 2000);
+}
+
+bool AntMessenger::ANT_SetChannelMessagePeriod(uchar chan, ushort msgPeriod)
+{
+ AntMessage m(MESG_CHANNEL_MESG_PERIOD_ID, chan, msgPeriod%256, msgPeriod/256);
+ return sendCommand(m, 2000);
+}
+
+bool AntMessenger::ANT_SetChannelSearchTimeout(uchar chan, uchar searchTimeout)
+{
+ AntMessage m(MESG_CHANNEL_SEARCH_TIMEOUT_ID, chan, searchTimeout);
+ return sendCommand(m, 2000);
+}
+
+bool AntMessenger::ANT_SetChannelRadioFreq(uchar chan, uchar freq)
+{
+ AntMessage m(MESG_CHANNEL_RADIO_FREQ_ID, chan, freq);
+ return sendCommand(m, 2000);
+}
+
+bool AntMessenger::ANT_SetSearchWaveform(uchar chan, ushort wave)
+{
+ AntMessage m(MESG_SEARCH_WAVEFORM_ID, chan, wave/256, wave%256);
+ return sendCommand(m, 2000);
+}
+
+bool AntMessenger::ANT_SetChannelId(uchar chan, ushort devNum, uchar devId, uchar transType)
+{
+ uchar buf[5];
+
+ buf[0] = chan;
+ buf[1] = devNum/256;
+ buf[2] = devNum%256;
+ buf[3] = devId;
+ buf[4] = transType;
+ bool rv = sendCommand(MESG_CHANNEL_ID_ID, buf, sizeof(buf), 2000);
+ return rv;
+}
+
+
+bool
+AntMessenger::ANT_OpenChannel(uchar chan)
+{
+ AntMessage m(MESG_OPEN_CHANNEL_ID, chan);
+ return sendCommand(m, 2000);
+}
+
+
+bool
+AntMessenger::ANT_CloseChannel(uchar chan, const size_t timeout_ms)
+{
+ AntMessage m(MESG_CLOSE_CHANNEL_ID, chan);
+ bool rv = sendCommand(m, timeout_ms/2);
+ // TODO: read R[ 7] a4_03_40_00_01_07_e1 MESG_RESPONSE_EVENT_ID chan=0x00 mId=MESG_EVENT_ID mCode=EVENT_CHANNEL_CLOSED
+
+ AntChannel& pc = chs[chan];
+ AntEvListener el(pc);
+ //pc.addEvListener(&el);
+
+ uint8_t msgCode;
+ rv = rv && el.waitForEvent(msgCode, timeout_ms/2);
+ rv = rv && (msgCode==EVENT_CHANNEL_CLOSED);
+
+ //pc.rmEvListener(&el);
+
+ return rv;
+}
+
+
+bool
+AntMessenger::ANT_RequestMessage(uchar chan, uchar reqMsgId)
+{
+ AntMessage response;
+ return sendRequest(reqMsgId, chan, &response, 2000);
+}
+
+bool AntMessenger::ANT_GetChannelId(uchar chan, ushort *devNum, uchar *devId, uchar *transType, size_t timeout_ms)
+{
+ AntMessage response;
+ if(!sendRequest(MESG_CHANNEL_ID_ID, chan, &response, timeout_ms))
+ return false;
+ if(response.getLenRaw()<1 || response.getLenPayload()!=5)
+ return false;
+ if(devNum)
+ *devNum = (ushort)response.getPayload()[1] | (ushort)response.getPayload()[2]*256;
+ if(devId)
+ *devId = response.getPayload()[3];
+ if(transType)
+ *transType = response.getPayload()[4];
+ return true;
+}
+
+
+bool
+AntMessenger::ANT_SendAcknowledgedData(const uchar chan, const uchar data[8], const size_t timeout_ms)
+{
+ return sendAckData(chan, data, timeout_ms);
+}
+
+bool AntMessenger::ANT_SendBurstData(const uchar seqchan, const uchar data[8])
+{
+ uchar buf[9];
+ buf[0] = seqchan;
+ memcpy(buf+1, data, 8);
+
+ AntMessage m;
+ if(!m.assemble(MESG_BURST_DATA_ID, buf, sizeof(buf)))
+ return false;
+
+ return writeMessage(m);
+}
+
+
+bool
+AntMessenger::ANT_SendBurstData2(const uchar chan, const uchar* data, const size_t len)
+{
+ CHECK_RETURN_FALSE(len>0);
+ CHECK_RETURN_FALSE(len % 8 == 0);
+
+ std::vector<uchar> vdata(len);
+ for (size_t i=0; i<len; i++)
+ {
+ vdata[i]=data[i];
+ }
+
+ return ANT_SendBurstData2(chan, vdata);
+}
+
+
+bool
+AntMessenger::ANT_SendBurstData2(const uchar chan, const std::vector<uchar>& data)
+{
+ CHECK_RETURN_FALSE(!data.empty());
+ CHECK_RETURN_FALSE(data.size() % 8 == 0);
+
+ M_ANT_Burst burst;
+ uchar seq=0;
+ for(size_t start=0; start<data.size(); start += 8)
+ {
+ burst.chan = chan;
+ burst.seq = seq;
+ burst.last = (start+8==data.size());
+ //LOG_VAR(burst.toString());
+ CHECK_RETURN_FALSE(ANT_SendBurstData(burst.seqchan, &data[start]));
+ if(start==0 && data.size()>8)
+ {
+ //LOG_VAR(waitForBroadcast(chan,0,800));
+ CHECK_RETURN_FALSE(waitForBroadcast(chan,0,800));
+ //sleepms(10);
+ }
+ ++seq;
+ if(seq==4)
+ seq=1;
+ }
+
+ return true;
+}
+
+
+bool
+AntMessenger::ANTFS_Link( const uchar chan, const uchar freq, const uchar beaconPer, const uint hostSN )
+{
+ //S 3.536 4f MESG_ACKNOWLEDGED_DATA_ID chan=00 ANTFS_CMD(0x44) ANTFS_CmdLink freq=0f, period=04, SN=0x7c9101e0
+ //R 113.467 4e MESG_BROADCAST_DATA_ID chan=00 ANTFS_BEACON(0x43) Beacon=8Hz, pairing=enabled, upload=disabled, dataAvail=yes, State=Link, Auth=PasskeyAndPairingOnly
+ //R 1.975 40 MESG_RESPONSE_EVENT_ID chan=00 mId=UNKNOWN_0x01 mCode=EVENT_TRANSFER_TX_COMPLETED
+
+
+
+ bool success=false;
+
+ for (int i=0; i < ANTPM_RETRIES; i++)
+ {
+ M_ANTFS_Command cmd;
+ cmd.commandId = ANTFS_CommandResponseId;
+ cmd.command = ANTFS_CmdLink;
+ cmd.detail.link.chanFreq = freq;
+ cmd.detail.link.chanPeriod = beaconPer;
+ cmd.detail.link.sn = hostSN;
+
+ if(!ANT_SendAcknowledgedData(chan, reinterpret_cast<uchar*>(&cmd), 2000))
+ continue;
+
+ success=true;
+ break;
+ }
+
+ return success;
+}
+
+
+bool
+AntMessenger::ANTFS_Disconnect(const uchar chan)
+{
+ M_ANTFS_Command cmd;
+ cmd.commandId = ANTFS_CommandResponseId;
+ cmd.command = ANTFS_CmdDisconnect;
+ cmd.detail.disconnect.cmdType = M_ANTFS_Command::ReturnToLinkLayer;
+
+ /*CHECK_RETURN_FALSE_LOG_OK(*/ANT_SendAcknowledgedData(chan, reinterpret_cast<uchar*>(&cmd), 400)/*)*/;
+
+ return true;
+}
+
+
+bool
+AntMessenger::ANTFS_Pairing(const uchar chan, const uint hostSN, const std::string& name1, uint& unitId, uint64_t& key)
+{
+ std::string name=(name1.length()>8?name1.substr(0, 8):name1);
+ while(name.length()<8) { name.push_back(' '); }
+ assert(name.length()==8);
+ M_ANTFS_Command_Pairing cmd;
+ cmd.commandId = ANTFS_CommandResponseId;
+ cmd.command = ANTFS_CmdAuthenticate;
+ cmd.detail.authenticate.cmdType = M_ANTFS_Command::RequestPairing;
+ cmd.detail.authenticate.authStrLen = 8;
+ cmd.detail.authenticate.sn = hostSN;
+ memcpy(cmd.name, &name[0], 8);
+
+ AntChannel& pc(chs[chan]);
+ AntBurstListener bl(pc);
+ //pc.addMsgListener(&bl);
+
+ bool sentPairing = false;
+ for(int i = 0; i < ANTPM_RETRIES; i++)
+ {
+ sentPairing = false;
+
+ LOG_VAR(waitForBroadcast(chan));
+
+ //CHECK_RETURN_FALSE_LOG_OK(collectBroadcasts(chan));
+ sentPairing = ANT_SendBurstData2(chan, reinterpret_cast<uchar*>(&cmd), sizeof(cmd));
+
+ // TODO: read bcast here?
+ //AntMessage reply0;
+ //waitForMessage(MESG_RESPONSE_EVENT_ID, &reply0, 2000);
+
+ AntChannel& pc(chs[chan]);
+ AntEvListener el(pc);
+ //pc.addEvListener(&el);
+
+ uint8_t responseVal;
+ sentPairing = sentPairing && el.waitForEvent(responseVal, 800);
+ //pc.rmEvListener(&el);
+ sentPairing = sentPairing && (responseVal==EVENT_TRANSFER_TX_COMPLETED);
+
+ if(sentPairing)
+ break;
+ else
+ sleepms(ANTPM_RETRY_MS);
+ }
+ CHECK_RETURN_FALSE_LOG_OK(sentPairing);
+
+ // FIXME: look for, and handle event:EVENT_RX_FAIL; event:EVENT_TRANSFER_RX_FAILED
+
+ // ANTFS_RespAuthenticate
+ std::vector<uchar> burstData;
+ bool rv = bl.collectBurst(burstData, 30*1000); // 30s to allow user confirmation
+ //pc.rmMsgListener(&bl);
+
+ CHECK_RETURN_FALSE_LOG_OK(burstData.size()==3*8);
+ const M_ANTFS_Response_Pairing* resp(reinterpret_cast<const M_ANTFS_Response_Pairing*>(&burstData[8]));
+ CHECK_RETURN_FALSE_LOG_OK(resp->responseId==ANTFS_CommandResponseId);
+ CHECK_RETURN_FALSE_LOG_OK(resp->response==ANTFS_RespAuthenticate);
+ CHECK_RETURN_FALSE_LOG_OK(resp->detail.authenticateResponse.respType==1); // accept
+ LOG_VAR((int)resp->detail.authenticateResponse.authStrLen);
+ CHECK_RETURN_FALSE(resp->detail.authenticateResponse.authStrLen==8);
+
+ unitId = resp->detail.authenticateResponse.sn;
+ key = resp->pairedKey;
+
+ //printf("unitId=%d, key=0x016x\n", unitId, key); fflush(stdout);
+
+ // TODO: read bcast here?
+
+
+ return true;
+}
+
+
+bool
+AntMessenger::ANTFS_Authenticate(const uchar chan, const uint hostSN, const uint64_t pairedKey)
+{
+ //S 2.099 50 MESG_BURST_DATA_ID chan=0x00, seq=0, last=no ANTFS_CMD(0x44) ANTFS_CmdAuthenticate type=RequestPasskeyExchange, authStrLen=8, SN=0x7c9101e0
+ //R 100.875 4e MESG_BROADCAST_DATA_ID chan=00 ANTFS_BEACON(0x43) Beacon=8Hz, pairing=enabled, upload=disabled, dataAvail=yes, State=Authentication, Auth=PasskeyAndPairingOnly
+ //S 1.977 50 MESG_BURST_DATA_ID chan=0x00, seq=1, last=no d273f79a6f166fa5 .s..o.o.
+ //S 3.035 50 MESG_BURST_DATA_ID chan=0x00, seq=2, last=yes 0000000000000000 ........
+ //R 9.986 40 MESG_RESPONSE_EVENT_ID chan=00 mId=MESG_EVENT_ID mCode=EVENT_TRANSFER_TX_COMPLETED
+ //R 110.037 50 MESG_BURST_DATA_ID chan=0x00, seq=0, last=no ANTFS_BEACON(0x43) Beacon=8Hz, pairing=enabled, upload=disabled, dataAvail=yes, State=Authentication, Auth=PasskeyAndPairingOnly
+ //R 3.99 50 MESG_BURST_DATA_ID chan=0x00, seq=1, last=yes ANTFS_RESP(0x44) ANTFS_RespAuthenticate resp=accept, authStrLen=0, SN=0x00000000
+ //R 120.998 4e MESG_BROADCAST_DATA_ID chan=00 ANTFS_BEACON(0x43) Beacon=8Hz, pairing=enabled, upload=disabled, dataAvail=yes, State=Transport, Auth=PasskeyAndPairingOnly
+ //S 96.065 4d MESG_REQUEST_ID chan=00 reqMsgId=MESG_CHANNEL_STATUS_ID
+ //R 3.91 52 MESG_CHANNEL_STATUS_ID chan=00 chanSt=Tracking
+ M_ANTFS_Command_Authenticate cmd;
+ cmd.commandId = ANTFS_CommandResponseId;
+ cmd.command = ANTFS_CmdAuthenticate;
+ cmd.detail.authenticate.cmdType = M_ANTFS_Command::RequestPasskeyExchange;
+ cmd.detail.authenticate.authStrLen = 8;
+ cmd.detail.authenticate.sn = hostSN;
+ cmd.key = pairedKey;
+ cmd.footer = 0x0000000000000000;
+
+ //CHECK_RETURN_FALSE_LOG_OK(waitForBroadcast(chan));
+
+ // TODO: start listening for burst here!
+
+ bool sentReqAuth = false;
+ for(int i = 0; i < ANTPM_RETRIES; i++)
+ {
+ sentReqAuth = false;
+
+ LOG_VAR(waitForBroadcast(chan));
+
+ //CHECK_RETURN_FALSE_LOG_OK(collectBroadcasts(chan));
+ sentReqAuth = ANT_SendBurstData2(chan, reinterpret_cast<uchar*>(&cmd), sizeof(cmd));
+
+ // TODO: read bcast here?
+ //AntMessage reply0;
+ //waitForMessage(MESG_RESPONSE_EVENT_ID, &reply0, 2000);
+
+ AntChannel& pc(chs[chan]);
+ AntEvListener el(pc);
+ //pc.addEvListener(&el);
+
+ uint8_t responseVal;
+ sentReqAuth = sentReqAuth && el.waitForEvent(responseVal, 800);
+ //pc.rmEvListener(&el);
+ sentReqAuth = sentReqAuth && (responseVal==EVENT_TRANSFER_TX_COMPLETED);
+
+ if(sentReqAuth)
+ break;
+ else
+ sleepms(ANTPM_RETRY_MS);
+ }
+ CHECK_RETURN_FALSE_LOG_OK(sentReqAuth);
+
+
+
+ // ANTFS_RespAuthenticate
+ std::vector<uchar> burstData;
+ CHECK_RETURN_FALSE_LOG_OK(waitForBurst(chan, burstData, 10*1000));
+ CHECK_RETURN_FALSE_LOG_OK(burstData.size()==2*8);
+ const M_ANTFS_Response* resp(reinterpret_cast<const M_ANTFS_Response*>(&burstData[8]));
+ CHECK_RETURN_FALSE_LOG_OK(resp->responseId==ANTFS_CommandResponseId);
+ CHECK_RETURN_FALSE_LOG_OK(resp->response==ANTFS_RespAuthenticate);
+ CHECK_RETURN_FALSE_LOG_OK(resp->detail.authenticateResponse.respType==1); // accept
+
+ // TODO: read bcast here?
+
+ return true;
+}
+
+
+
+bool
+AntMessenger::ANTFS_Download( const uchar chan, const ushort file, std::vector<uchar>& data )
+{
+ //S 1.221 50 MESG_BURST_DATA_ID chan=0x00, seq=0, last=no ANTFS_CMD(0x44) ANTFS_ReqDownload file=0x0000, dataOffset=0x00000000
+ //R 118.782 4e MESG_BROADCAST_DATA_ID chan=0x00 ANTFS_BEACON(0x43) Beacon=8Hz, pairing=enabled, upload=disabled, dataAvail=yes, State=Transport, Auth=PasskeyAndPairingOnly
+ //S 1.258 50 MESG_BURST_DATA_ID chan=0x00, seq=1, last=yes 0001000000000000 ........
+ //R 10.728 40 MESG_RESPONSE_EVENT_ID chan=0x00 mId=MESG_EVENT_ID mCode=EVENT_TRANSFER_TX_COMPLETED
+ //R 112.995 4e MESG_BROADCAST_DATA_ID chan=0x00 ANTFS_BEACON(0x43) Beacon=8Hz, pairing=enabled, upload=disabled, dataAvail=yes, State=Busy, Auth=PasskeyAndPairingOnly
+ //R 125.019 50 MESG_BURST_DATA_ID chan=0x00, seq=0, last=no ANTFS_BEACON(0x43) Beacon=8Hz, pairing=enabled, upload=disabled, dataAvail=yes, State=Busy, Auth=PasskeyAndPairingOnly
+ //R 3.994 50 MESG_BURST_DATA_ID chan=0x00, seq=1, last=no ANTFS_RESP(0x44) ANTFS_RespDownload resp=DownloadRequestOK, remainingBytes=0x00000200
+ //R 3.002 50 MESG_BURST_DATA_ID chan=0x00, seq=2, last=no 00000000c0030000 ........
+ //R 2.999 50 MESG_BURST_DATA_ID chan=0x00, seq=3, last=no 0110000000000000 ........
+ //R 2.982 50 MESG_BURST_DATA_ID chan=0x00, seq=1, last=no 0000000000000000 ........
+ //R 3 50 MESG_BURST_DATA_ID chan=0x00, seq=2, last=no 0100010c00000050 .......P
+ //R 6.989 50 MESG_BURST_DATA_ID chan=0x00, seq=3, last=no 00001d0000000000 ........
+ //R 1.998 50 MESG_BURST_DATA_ID chan=0x00, seq=1, last=no 0200010d00000030 .......0
+ //R 3 50 MESG_BURST_DATA_ID chan=0x00, seq=3, last=no 03008001ffff0090 ........
+ //R 4.002 50 MESG_BURST_DATA_ID chan=0x00, seq=1, last=no 4602000000000000 F.......
+ //R 2.998 50 MESG_BURST_DATA_ID chan=0x00, seq=2, last=no 04008002ffff00d0 ........
+ //R 3.002 50 MESG_BURST_DATA_ID chan=0x00, seq=3, last=no 3602000000000000 6.......
+ //R 3.022 50 MESG_BURST_DATA_ID chan=0x00, seq=1, last=no 05008003020000d0 ........
+ //R 2.991 50 MESG_BURST_DATA_ID chan=0x00, seq=2, last=no 8204000000000000 ........
+ //R 2.998 50 MESG_BURST_DATA_ID chan=0x00, seq=3, last=no 06008003000000d0 ........
+ //R 3.004 50 MESG_BURST_DATA_ID chan=0x00, seq=1, last=no 8204000000000000 ........
+ //R 2.999 50 MESG_BURST_DATA_ID chan=0x00, seq=2, last=no 07008003010000d0 ........
+ //R 2.998 50 MESG_BURST_DATA_ID chan=0x00, seq=3, last=no 8204000000000000 ........
+ //R 3.987 50 MESG_BURST_DATA_ID chan=0x00, seq=1, last=no 08008004180000b0 ........
+ //R 3.012 50 MESG_BURST_DATA_ID chan=0x00, seq=2, last=no e84c00007610d229 .L..v..)
+ //R 3 50 MESG_BURST_DATA_ID chan=0x00, seq=3, last=no 09008004190000b0 ........
+ //R 3.002 50 MESG_BURST_DATA_ID chan=0x00, seq=1, last=no 8a6c00007810d229 .l..x..)
+ //R 2.999 50 MESG_BURST_DATA_ID chan=0x00, seq=2, last=no 0a0080041a0000b0 ........
+ //R 2.999 50 MESG_BURST_DATA_ID chan=0x00, seq=3, last=no 943f00007810d229 .?..x..)
+ //R 3.001 50 MESG_BURST_DATA_ID chan=0x00, seq=1, last=no 0b0080041b0000b0 ........
+ //R 3.016 50 MESG_BURST_DATA_ID chan=0x00, seq=2, last=no 3d2500007a10d229 =%..z..)
+ //R 3.981 50 MESG_BURST_DATA_ID chan=0x00, seq=3, last=no 0c0080041c0000b0 ........
+ //R 2.999 50 MESG_BURST_DATA_ID chan=0x00, seq=1, last=no 0c2b00007a10d229 .+..z..)
+ //R 3.001 50 MESG_BURST_DATA_ID chan=0x00, seq=2, last=no 0d0080041d0000b0 ........
+ //R 3.018 50 MESG_BURST_DATA_ID chan=0x00, seq=3, last=no ad2e00007a10d229 ....z..)
+ //R 2.961 50 MESG_BURST_DATA_ID chan=0x00, seq=1, last=no 0e0080041e0000b0 ........
+ //R 3.041 50 MESG_BURST_DATA_ID chan=0x00, seq=2, last=no 8c2400007c10d229 .$..|..)
+ //R 2.998 50 MESG_BURST_DATA_ID chan=0x00, seq=3, last=no 0f0080041f0000b0 ........
+ //R 3 50 MESG_BURST_DATA_ID chan=0x00, seq=1, last=no d84d00007c10d229 .M..|..)
+ //R 2.995 50 MESG_BURST_DATA_ID chan=0x00, seq=2, last=no 10008004200000b0 .... ...
+ //R 4.008 50 MESG_BURST_DATA_ID chan=0x00, seq=3, last=no 442f00007e10d229 D/..~..)
+ //R 2.978 50 MESG_BURST_DATA_ID chan=0x00, seq=1, last=no 11008004220000b0 ...."...
+ //R 3.021 50 MESG_BURST_DATA_ID chan=0x00, seq=2, last=no dc2c00008010d229 .,.....)
+ //R 2.981 50 MESG_BURST_DATA_ID chan=0x00, seq=3, last=no 12008004230000b0 ....#...
+ //R 2.999 50 MESG_BURST_DATA_ID chan=0x00, seq=1, last=no 066f00008010d229 .o.....)
+ //R 3.018 50 MESG_BURST_DATA_ID chan=0x00, seq=2, last=no 13008004240000b0 ....$...
+ //R 3 50 MESG_BURST_DATA_ID chan=0x00, seq=3, last=no 865100008210d229 .Q.....)
+ //R 3.003 50 MESG_BURST_DATA_ID chan=0x00, seq=1, last=no 14008004210000b0 ....!...
+ //R 3 50 MESG_BURST_DATA_ID chan=0x00, seq=2, last=no 5a2f00007e10d229 Z/..~..)
+ //R 3.999 50 MESG_BURST_DATA_ID chan=0x00, seq=3, last=no 15008004250000b0 ....%...
+ //R 2.998 50 MESG_BURST_DATA_ID chan=0x00, seq=1, last=no 232d00008210d229 #-.....)
+ //R 3.002 50 MESG_BURST_DATA_ID chan=0x00, seq=2, last=no 16008004260000b0 ....&...
+ //R 3.001 50 MESG_BURST_DATA_ID chan=0x00, seq=3, last=no b09900008410d229 .......)
+ //R 3.98 50 MESG_BURST_DATA_ID chan=0x00, seq=1, last=no 17008004270000b0 ....'...
+ //R 2.005 50 MESG_BURST_DATA_ID chan=0x00, seq=2, last=no 803000008410d229 .0.....)
+ //R 3.013 50 MESG_BURST_DATA_ID chan=0x00, seq=3, last=no 18008004280000b0 ....(...
+ //R 3.002 50 MESG_BURST_DATA_ID chan=0x00, seq=1, last=no bf2900008610d229 .).....)
+ //R 4 50 MESG_BURST_DATA_ID chan=0x00, seq=2, last=no 19008004290000b0 ....)...
+ //R 2.977 50 MESG_BURST_DATA_ID chan=0x00, seq=3, last=no d01800008610d229 .......)
+ //R 3.021 50 MESG_BURST_DATA_ID chan=0x00, seq=1, last=no 1a008004170000b0 ........
+ //R 3.002 50 MESG_BURST_DATA_ID chan=0x00, seq=2, last=no 72250000f2acd429 r%.....)
+ //R 2.996 50 MESG_BURST_DATA_ID chan=0x00, seq=3, last=no 1b0080042a0000b0 ....*...
+ //R 3 50 MESG_BURST_DATA_ID chan=0x00, seq=1, last=no 592d000049b5d429 Y-..I..)
+ //R 3.983 50 MESG_BURST_DATA_ID chan=0x00, seq=2, last=no 1c0080042b0000b0 ....+...
+ //R 3.007 50 MESG_BURST_DATA_ID chan=0x00, seq=3, last=no 022f00007f54d729 ./...T.)
+ //R 1.998 50 MESG_BURST_DATA_ID chan=0x00, seq=1, last=no 1d0080042c0000b0 ....,...
+ //R 4.016 50 MESG_BURST_DATA_ID chan=0x00, seq=2, last=no 04530000e9b6d929 .S.....)
+ //R 3.001 50 MESG_BURST_DATA_ID chan=0x00, seq=3, last=no 1e0080042d0000b0 ....-...
+ //R 2.997 50 MESG_BURST_DATA_ID chan=0x00, seq=1, last=no 5c2a000031fbdd29 \*..1..)
+ //R 3.001 50 MESG_BURST_DATA_ID chan=0x00, seq=2, last=no 1f0080042e0000b0 ........
+ //R 2.988 50 MESG_BURST_DATA_ID chan=0x00, seq=3, last=no 373400006932e329 74..i2.)
+ //R 2.998 50 MESG_BURST_DATA_ID chan=0x00, seq=1, last=yes 000000000000105b .......[
+ //R 41.018 4e MESG_BROADCAST_DATA_ID chan=0x00 000000000000105b .......[
+ //S 2.888 4d MESG_REQUEST_ID chan=0x00 reqMsgId=MESG_CHANNEL_STATUS_ID
+ //R 3.109 52 MESG_CHANNEL_STATUS_ID chan=00 chanSt=Tracking
+ CHECK_RETURN_FALSE(data.empty());
+
+ int errorCnt=0;
+
+ //uint bytesReceived = 0;
+ ushort crc = 0x0000;
+ size_t fileSize = 0;
+ size_t dlIter = 0;
+ uint nextOffset = 0;
+ do
+ {
+ //fprintf(loggerc(), "dlIter=%lu, crc=0x%04x, off=0x%08x\n", (unsigned long)dlIter, crc, nextOffset);
+ //LOG_VAR2(dlIter, toString(crc,4,'0'));
+ M_ANTFS_Command_Download dl;
+ dl.commandId = ANTFS_CommandResponseId;
+ dl.command = ANTFS_ReqDownload;
+ dl.detail.downloadRequest.dataFileIdx = file;
+ dl.detail.downloadRequest.dataOffset = nextOffset;
+ dl.zero = 0;
+ dl.initReq = (dlIter==0);
+ dl.crcSeed = crc;
+ dl.maxBlockSize = 0;
+
+ AntChannel& pc(chs[chan]);
+ AntBurstListener bl(pc);
+ //pc.addMsgListener(&bl); // FIXME: remove this on return paths, IN DTOR!!!
+
+ AntEvListener el(pc);
+ //pc.addEvListener(&el); // FIXME: remove this on return paths, IN DTOR!!!
+
+ bool sentReqDl = false;
+ sentReqDl = false;
+ //if(dlIter==0)
+ // CHECK_RETURN_FALSE_LOG_OK(collectBroadcasts(chan));
+ sentReqDl = ANT_SendBurstData2(chan, reinterpret_cast<uchar*>(&dl), sizeof(dl));
+ if(!sentReqDl && (++errorCnt<ANTPM_RETRIES))
+ {
+ //pc.rmEvListener(&el);
+ //pc.rmMsgListener(&bl);
+ continue;
+ }
+
+ uint8_t responseVal;
+ sentReqDl = sentReqDl && el.waitForEvent(responseVal, 600);
+ //pc.rmEvListener(&el);
+ sentReqDl = sentReqDl && (responseVal==EVENT_TRANSFER_TX_COMPLETED);
+
+ // TODO: handle event:EVENT_RX_FAIL and continue
+
+ if(!sentReqDl && (++errorCnt<ANTPM_RETRIES))
+ {
+ //pc.rmMsgListener(&bl);
+ continue;
+ }
+
+ // TODO: read bcast here?
+
+ std::vector<uchar> burstData;
+ if(!bl.collectBurst(burstData, 10000) && (++errorCnt<ANTPM_RETRIES))
+ {
+ //pc.rmMsgListener(&bl);
+ continue;
+ }
+ //pc.rmMsgListener(&bl);
+
+ //// ANTFS_RespDownload
+ CHECK_RETURN_FALSE(burstData.size()>=3*8); // header and footer eats up 32 bytes already, but in case of error2 we only get 24 bytes
+ const M_ANTFS_Response_Download* resp(reinterpret_cast<const M_ANTFS_Response_Download*>(&burstData[8]));
+ CHECK_RETURN_FALSE(resp->responseId==ANTFS_CommandResponseId);
+ CHECK_RETURN_FALSE(resp->response==ANTFS_RespDownload);
+ if(resp->detail.downloadRequestResponse.responseVal!=M_ANTFS_Response::DownloadRequestOK)
+ {
+ const char* dlStatus=resp->detail.downloadRequestResponse.szResponseVal();
+ LOG_VAR(dlStatus);
+ if(resp->detail.downloadRequestResponse.responseVal==M_ANTFS_Response::CRCIncorrect)
+ {
+ //let's retry
+ CHECK_RETURN_FALSE(waitForBroadcast(chan, NULL, 1000));
+ CHECK_RETURN_FALSE(ANT_RequestMessage(chan, MESG_CHANNEL_STATUS_ID));
+ continue;//FIXME: this can be an endless loop
+ }
+ CHECK_RETURN_FALSE(resp->detail.downloadRequestResponse.responseVal==M_ANTFS_Response::DownloadRequestOK); // dl ok
+ }
+
+ //fprintf(loggerc(), "remaining bytes = %u = 0x%08x\n", resp->detail.downloadRequestResponse.remainingBytes, resp->detail.downloadRequestResponse.remainingBytes);
+ nextOffset += resp->detail.downloadRequestResponse.remainingBytes;
+
+ if(dlIter==0) fileSize = resp->fileSize;
+ //fprintf(loggerc(), "fileSize = %u = 0x%08x\n", (uint)fileSize, (uint)fileSize);
+ CHECK_RETURN_FALSE(fileSize == resp->fileSize);
+ //fprintf(loggerc(), "nextOffset = %u = 0x%08x\n", (uint)nextOffset, (uint)nextOffset);
+ //logger() << std::dec;
+ //LOG_VAR(fileSize);
+ //LOG_VAR2(burstData.size(), toString(nextOffset,8,'0'));
+ uint c2=0;
+ AntFsFile crcData;
+ for(uint b=24; b<burstData.size()-8; b++)
+ {
+ uchar c = burstData[b];
+ data.push_back(c);
+ crcData.bytes.push_back(c);
+ c2++;
+ }
+ //LOG_VAR3(burstData.size(), c2, data.size());
+ CHECK_RETURN_FALSE(c2==burstData.size()-32);
+ // if the file size is not a multiple of 8, in the last round we might have received more bytes and overfilled it
+ if(fileSize%8!=0 && data.size()>fileSize)
+ {
+ logger() << "Truncating data buffer after overfill!\n";
+ data.erase(data.begin()+fileSize, data.end());
+ }
+
+ const M_ANTFS_Response_Download_Footer* footer(reinterpret_cast<const M_ANTFS_Response_Download_Footer*>(&burstData[burstData.size()-8]));
+ //LOG_VAR(toString(footer->crc,4,'0'));
+ ushort crcCalculated=crcData.crc16Calc(crc);
+ //LOG_VAR(toString(crcCalculated,4,'0'));
+ bool crcCheckOk=(footer->crc==crcCalculated);
+ CHECK_RETURN_FALSE_LOG_OK(crcCheckOk);
+ crc = footer->crc; //TODO: crc check
+
+ CHECK_RETURN_FALSE(waitForBroadcast(chan, NULL, 1000));
+
+ CHECK_RETURN_FALSE(ANT_RequestMessage(chan, MESG_CHANNEL_STATUS_ID));
+
+ logger() << "\n\nDownloaded " << std::dec << c2 << " of " << fileSize << " bytes. Total " << data.size() << " downloaded.\n\n";
+ // TODO: keep reading until there is data left
+ dlIter += 1;
+ } while((data.size()<fileSize) /*&& (dlIter++<ANTPM_RETRIES)*/ );
+
+ return true;
+}
+
+
+bool
+AntMessenger::ANTFS_Erase(const uchar chan, const ushort file)
+{
+ M_ANTFS_Command cmd;
+ cmd.commandId = ANTFS_CommandResponseId;
+ cmd.command = ANTFS_ReqErase;
+ cmd.detail.eraseRequest.dataFileIdx = file;
+
+ for(int itry=0; itry < ANTPM_RETRIES; itry++)
+ {
+ AntChannel& pc(chs[chan]);
+ AntBurstListener bl(pc);
+ //pc.addMsgListener(&bl);
+
+ CHECK_RETURN_FALSE_LOG_OK(ANT_SendAcknowledgedData(chan, reinterpret_cast<uchar*>(&cmd), 1500));
+
+ std::vector<uchar> burstData;
+ bool rv = bl.collectBurst(burstData, 6000);
+ //pc.rmMsgListener(&bl);
+
+ //TODO interpret bursted response
+ //// M_ANTFS_Response for erase
+ CHECK_RETURN_FALSE(burstData.size()>=2*8);
+ const M_ANTFS_Response* resp(reinterpret_cast<const M_ANTFS_Response*>(&burstData[8]));
+ CHECK_RETURN_FALSE(resp->responseId==ANTFS_CommandResponseId);
+ CHECK_RETURN_FALSE(resp->response==ANTFS_RespErase);
+ logger() << resp->toString() << "\n";
+
+ rv = rv && (resp->detail.eraseRequestResponse.responseVal==0);
+ if(rv)
+ break;
+ }
+
+ return true;
+}
+
+
+bool
+AntMessenger::ANTFS_RequestClientDeviceSerialNumber(const uchar chan, const uint hostSN, uint& sn, std::string& devName)
+{
+ //S 2.817 4f MESG_ACKNOWLEDGED_DATA_ID chan=00 ANTFS_CMD(0x44) ANTFS_CmdAuthenticate type=RequestClientDeviceSerialNumber, authStrLen=0, SN=0x7c9101e0
+ //R 122.184 4e MESG_BROADCAST_DATA_ID chan=00 ANTFS_BEACON(0x43) Beacon=8Hz, pairing=enabled, upload=disabled, dataAvail=yes, State=Authentication, Auth=PasskeyAndPairingOnly
+ //R 1.976 40 MESG_RESPONSE_EVENT_ID chan=00 mId=MESG_EVENT_ID mCode=EVENT_TRANSFER_TX_COMPLETED
+ //R 123.022 50 MESG_BURST_DATA_ID chan=0x00, seq=0, last=no ANTFS_BEACON(0x43) Beacon=8Hz, pairing=enabled, upload=disabled, dataAvail=yes, State=Authentication, Auth=PasskeyAndPairingOnly
+ //R 3.977 50 MESG_BURST_DATA_ID chan=0x00, seq=1, last=no ANTFS_RESP(0x44) ANTFS_RespAuthenticate resp=??, authStrLen=16, SN=0xd84e4cd4
+ //R 3.018 50 MESG_BURST_DATA_ID chan=0x00, seq=2, last=no 466f726572756e6e Forerunn
+ //R 3.003 50 MESG_BURST_DATA_ID chan=0x00, seq=3, last=yes 6572203331305854 er 310XT
+ //S 8.095 4d MESG_REQUEST_ID chan=00 reqMsgId=MESG_CHANNEL_STATUS_ID
+ //R 3.903 52 MESG_CHANNEL_STATUS_ID chan=00 chanSt=Tracking
+
+ M_ANTFS_Command cmd;
+ cmd.commandId = ANTFS_CommandResponseId;
+ cmd.command = ANTFS_CmdAuthenticate;
+ cmd.detail.authenticate.cmdType = M_ANTFS_Command::RequestClientDeviceSerialNumber;
+ cmd.detail.authenticate.authStrLen = 0;
+ cmd.detail.authenticate.sn = hostSN;
+
+ AntChannel& pc(chs[chan]);
+ AntBurstListener bl(pc);
+ //pc.addMsgListener(&bl);
+
+
+ CHECK_RETURN_FALSE_LOG_OK(ANT_SendAcknowledgedData(chan, reinterpret_cast<uchar*>(&cmd), 2000));
+
+ std::vector<uchar> burstData;
+ bool rv = bl.collectBurst(burstData, 5000);
+ //pc.rmMsgListener(&bl);
+
+ // TODO: interpret event:EVENT_TRANSFER_RX_FAILED as signal of failed bursting
+
+ //// TODO: read bcast auth beacon
+ //CHECK_RETURN_FALSE_LOG_OK(waitForBroadcast(chan));
+ //
+ //CHECK_RETURN_FALSE_LOG_OK(waitForBurst(chan, burstData, 30000));
+ LOG_VAR(burstData.size());
+ CHECK_RETURN_FALSE_LOG_OK(burstData.size()==4*8);
+
+ const M_ANTFS_Response* cmdResp(reinterpret_cast<const M_ANTFS_Response*>(&burstData[8]));
+ sn = cmdResp->detail.authenticateResponse.sn;
+ uchar lenDevName=cmdResp->detail.authenticateResponse.authStrLen;
+ CHECK_RETURN_FALSE_LOG_OK(lenDevName==16);
+
+ devName = std::string(reinterpret_cast<const char*>(&burstData[16]), lenDevName);
+
+ logger() << "devName = \"" << devName << "\"\n";
+
+ CHECK_RETURN_FALSE_LOG_OK(ANT_RequestMessage(chan, MESG_CHANNEL_STATUS_ID));
+
+ return true;
+}
+
+void AntMessenger::eventLoop()
+{
+ m_rpackQueue2.eventLoop();
+}
+
+void AntMessenger::kill()
+{
+ m_rpackQueue2.kill();
+}
+
+
+
+
+
+bool
+AntMessenger::sendCommand(uchar mesg, uchar *inbuf, uchar len, const size_t timeout_ms)
+{
+ AntMessage m;
+ return m.assemble(mesg, inbuf, len) && sendCommand(m, timeout_ms);
+}
+
+
+
+bool
+AntMessenger::sendCommand(AntMessage &m, const size_t timeout_ms)
+{
+ bool rv = writeMessage(m);
+ if(!rv)
+ {
+ printf("E: writeMessage failed\n"); fflush(stdout);
+ return false;
+ }
+
+ const uint8_t chan = m.getPayloadRef()[0];
+ AntChannel& pc = chs[chan];
+ AntRespListener respList(pc, m.getMsgId());
+ //pc.addRespListener(&respList);
+
+ uint8_t respVal;
+ rv = rv&& respList.waitForResponse(respVal, timeout_ms);
+
+ //pc.rmRespListener(&respList);
+
+ return rv;
+}
+
+// request a message from peer
+bool
+AntMessenger::sendRequest(uchar reqMsgId, uchar chan, AntMessage *response, const size_t timeout_ms)
+{
+ AntMessage reqMsg(MESG_REQUEST_ID, chan, reqMsgId); // the request we're sending
+
+ AntChannel& pc = chs[chan];
+ AntReqListener rl(pc, reqMsgId, chan);
+ //pc.addMsgListener(&rl);
+
+ bool rv = writeMessage(reqMsg);
+ if(!rv)
+ {
+ printf("E: writeMessage failed\n"); fflush(stdout);
+ return false;
+ }
+
+
+ AntMessage dummy;if(!response) response=&dummy;
+ rv = rv&& rl.waitForMsg(response, timeout_ms);
+
+ //pc.rmMsgListener(&rl);
+
+ return rv;
+}
+
+
+bool AntMessenger::writeMessage(unsigned char mesg, unsigned char *inbuf, unsigned char len)
+{
+ AntMessage m;
+ return m.assemble(mesg, inbuf, len) && writeMessage(m);
+}
+
+bool AntMessenger::writeMessage(AntMessage &m)
+{
+ if(!m_io)
+ return false;
+
+ m.sent=true;
+ m.timestamp = boost::get_system_time();
+ m.idx = packetIdx++;
+ assert(m.vrfChkSum());
+
+ size_t bytesWritten=0;
+ bool rv = m_io->write(reinterpret_cast<const char*>(&m.bytes[0]), m.getLenPacket(), bytesWritten);
+ size_t targetBytes = static_cast<size_t>(m.getLenPayload())+4;
+ if(bytesWritten!=targetBytes)
+ {
+ printf("E: wrote %d instead of %d bytes\n", (int)bytesWritten, (int)targetBytes);
+ rv = false;
+ }
+
+ if(m_cb && rv)
+ {
+ m_cb->onAntSent(m);
+ }
+
+ return rv;
+}
+
+bool
+AntMessenger::sendAckData(const uchar chan, const uchar data[8], const size_t timeout_ms)
+{
+ uchar mesg=MESG_ACKNOWLEDGED_DATA_ID;
+ uchar buf[9];
+ buf[0] = chan;
+ memcpy(buf+1, data, 8);
+
+ AntMessage m;
+ if(!m.assemble(mesg, buf, sizeof(buf)))
+ return false;
+
+ AntChannel& pc(chs[chan]);
+ AntEvListener el(pc);
+ //pc.addEvListener(&el);
+
+ bool rv = writeMessage(m);
+ if(!rv)
+ {
+ printf("E: writeMessage failed\n"); fflush(stdout);
+ return false;
+ }
+
+ uint8_t responseVal;
+ bool found = el.waitForEvent(responseVal, timeout_ms);
+ //pc.rmEvListener(&el);
+ found = found && (responseVal==EVENT_TRANSFER_TX_COMPLETED);
+ //TODO: loop sending until responseVal==EVENT_TRANSFER_TX_COMPLETED
+
+ //TODO: handle other events!!
+
+ if(!found)
+ {
+ //printf("E: no matching data ack before timeout\n"); fflush(stdout);
+ }
+ rv = rv && found;
+
+ return rv;
+}
+
+
+// interpret byte stream, assemble packets and store results in \a m_rpackQueue
+bool
+AntMessenger::assemblePackets(std::list<uchar>& q)
+{
+ if(q.empty())
+ return false;
+
+ int nInterpreted=0;
+ for(;;nInterpreted++)
+ {
+
+ AntMessage m;
+ m.sent = false;
+ m.bytes.resize(q.size());
+ std::copy(q.begin(), q.end(), m.bytes.begin());
+
+ bool cantInterpretMore = m.interpret();
+ if(!cantInterpretMore)
+ {
+ if(nInterpreted<1)
+ {
+ printf("interpret failed!\n"); fflush(stdout);
+ return false;
+ }
+ break;
+ }
+ for(size_t i=0; i<m.getLenPacket(); i++)
+ {
+ q.pop_front();
+ }
+ m.timestamp = boost::get_system_time();
+ m.idx = packetIdx++;
+ //{
+ // boost::unique_lock<boost::mutex> lock(m_rpackMutex);
+ // m_rpackQueue.push_back(m);
+ //}
+ m_rpackQueue2.push(m);
+ if(m_cb)
+ {
+ m_cb->onAntReceived(m);
+ }
+
+ }
+ //fprintf(loggerc(), "%d interpreted\n", nInterpreted);
+
+ return true;
+}
+
+bool AntMessenger::onMessage(std::vector<AntMessage> v)
+{
+ //TODO: don't presort here, but call onMsg for all incoming packets
+
+ //fprintf(loggerc(), "%d\n", int(v.size()));
+ for(size_t i = 0; i < v.size(); i++)
+ {
+ AntMessage& m(v[i]);
+ printf("%s\n", m.str().c_str());
+
+ if(m.getMsgId()==MESG_RESPONSE_EVENT_ID
+ || m.getMsgId()==MESG_BROADCAST_DATA_ID
+ || m.getMsgId()==MESG_CHANNEL_ID_ID
+ || m.getMsgId()==MESG_CHANNEL_STATUS_ID)
+ {
+ uint8_t chan=m.getPayloadRef()[0];
+ AntChannel& pc=chs[chan];
+ pc.onMsg(m);
+ }
+ else if(m.getMsgId()==MESG_BURST_DATA_ID)
+ {
+ if(m.getLenPayload()!=9)
+ continue; // invalid packet
+ const M_ANT_Burst* burst(reinterpret_cast<const M_ANT_Burst*>(m.getPayloadRef()));
+ uint8_t chan=burst->chan;
+ //chan = 0; // FIXME!!!
+ //printf("burst? 0x%0x chan=%d\n", (int)m.getMsgId(), int(chan));
+ AntChannel& pc=chs[chan];
+ pc.onMsg(m);
+ }
+ else
+ {
+ printf("unhandled 0x%0x\n", (int)m.getMsgId());
+ }
+
+// if(m.getMsgId()==MESG_RESPONSE_EVENT_ID)
+// {
+// uint8_t chan=m.getPayloadRef()[0];
+// uint8_t msgId=m.getPayloadRef()[1];
+// if(msgId==MESG_EVENT_ID)
+// {
+// uint8_t msgCode = m.getPayloadRef()[2];
+// fprintf(loggerc(), "chan=%d event %s\n", int(chan), m.str2().c_str());
+// if(msgCode==EVENT_RX_SEARCH_TIMEOUT)
+// {
+// //FIXME: handle EVENT_RX_SEARCH_TIMEOUT
+// }
+// AntChannel& pc=chs[chan];
+// pc.onChannelEvent(chan, msgCode, m);
+// }
+// else
+// {
+// //fprintf(loggerc(), "chan=%d resp\n", int(chan));
+// AntChannel& pc=chs[chan];
+// pc.onChannelResponse(chan, msgId, m);
+// }
+// }
+// else if(m.getMsgId()==MESG_BROADCAST_DATA_ID)
+// {
+// uint8_t chan=m.getPayloadRef()[0];
+// //fprintf(loggerc(), "chan=%d bcast\n", int(chan));
+// AntChannel& pc=chs[chan];
+// pc.onChannelBCast(chan, m);
+
+// }
+// else if(m.getMsgId()==MESG_BURST_DATA_ID)
+// {
+// if(m.getLenPayload()!=9)
+// continue; // invalid packet
+// const M_ANT_Burst* burst(reinterpret_cast<const M_ANT_Burst*>(m.getPayloadRef()));
+// uint8_t chan=burst->chan;
+// //chan = 0; // FIXME!!!
+// //printf("burst? 0x%0x chan=%d\n", (int)m.getMsgId(), int(chan));
+// AntChannel& pc=chs[chan];
+// pc.onChannelMsg(chan, m);
+// }
+// else if(m.getMsgId()==MESG_CHANNEL_ID_ID
+// || m.getMsgId()==MESG_CHANNEL_STATUS_ID)
+// {
+// uint8_t chan=m.getPayloadRef()[0];
+// AntChannel& pc=chs[chan];
+// pc.onChannelMsg(chan, m);
+// }
+// else
+// {
+// printf("unhandled 0x%0x\n", (int)m.getMsgId());
+// }
+ }
+ return true;
+}
+
+
+bool
+AntMessenger::waitForBurst(const uchar chan,
+ std::vector<uchar>& burstData,
+ const size_t timeout_ms)
+{
+ // read seq=0
+ // keep reading ...
+ // read last one
+
+ uchar expectedSeq=0;
+ bool found=false;
+ bool lastFound=false;
+
+ AntChannel& pc(chs[chan]);
+ AntBurstListener bl(pc);
+ //pc.addMsgListener(&bl);
+ bool rv = bl.collectBurst(burstData, timeout_ms);
+ //pc.rmMsgListener(&bl);
+ found = lastFound = rv;
+ if(!found || !lastFound)
+ {
+ printf("E: couldn't reconstruct burst data transmission before timeout\n"); fflush(stdout);
+ return false;
+ }
+
+ return true;
+}
+
+
+bool
+AntMessenger::waitForBroadcast(const uchar chan, AntMessage* reply, const size_t timeout_ms)
+{
+ const uchar first=ANTFS_BeaconId;
+ bool found=false;
+
+ AntChannel& pc(chs[chan]);
+ AntBCastListener bcl(pc, first);
+ //pc.addBCastListener(&bcl);
+
+ AntMessage dummy;if(!reply) reply=&dummy;
+ found = bcl.waitForBCast(*reply, timeout_ms);
+ //pc.rmBCastListener(&bcl);
+ //M_ANTFS_Beacon* beacon(reinterpret_cast<M_ANTFS_Beacon*>(&reply->getPayloadRef()[1]));
+ if(!found)
+ {
+ printf("E: no matching bcast before timeout\n"); fflush(stdout);
+ }
+ return found;
+}
+
+
+// receive bytes from the serial interface
+void*
+AntMessenger::th_messageHandler()
+{
+ std::list<uchar> q;
+ for(;;)
+ {
+ if(m_packerThKill)
+ break;
+ if(m_io)
+ {
+ uchar buf[128];
+ size_t bytesRead=0;
+ bool rv = m_io->readBlocking(reinterpret_cast<char*>(buf), sizeof(buf), bytesRead);
+ if(rv)
+ {
+ //printf("rv=%d, bytesRead=%d\n", (int)rv, (int)bytesRead); fflush(stdout);
+ for(size_t i=0; i<bytesRead; i++)
+ {
+ q.push_back(buf[i]);
+ }
+ }
+ }
+ else
+ {
+ sleepms(1000);
+ }
+ assemblePackets(q);
+ }
+ if(!q.empty())
+ fprintf(loggerc(), "%d remaining uninterpreted bytes\n", (int)q.size());
+ return NULL;
+}
+
+
+
+
+
+
+
+
+
+
+
+int sldjkgdsl()
+{
+ return 6+7;
+}
+
+
+#ifdef USE_BOOST_TEST
+
+//#define BOOST_TEST_MODULE AntHandler
+////#include <boost/test/included/unit_test.hpp>
+//#include <boost/test/unit_test.hpp>
+
+//BOOST_AUTO_TEST_CASE( free_test_function )
+//{
+// BOOST_CHECK( true /* test assertion */ );
+//}
+
+//BOOST_AUTO_TEST_CASE( dummy2_test )
+//{
+// BOOST_CHECK_EQUAL( 5, 6 );
+//}
+
+class MyFixture { MyFixture() { /* setup here */} };
+
+BOOST_AUTO_TEST_CASE( my_test, MyFixture )
+{
+ BOOST_CHECK_EQUAL(0, foo);
+}
+
+#endif
+#ifdef USE_GOOGLE_TEST
+#include <gtest/gtest.h>
+
+
+TEST(AntMessenger, Zero0) {
+ EXPECT_EQ(sldjkgdsl(), 13);
+}
+
+class MyFixture : public ::testing::Test
+{
+protected:
+ int foo;
+
+ virtual void SetUp() { foo = 0; }
+};
+TEST_F(MyFixture, FooStartsAtZero) {
+ EXPECT_EQ(0, foo);
+}
+
+
+int gtest_PullInMyLibrary_antlib2() { return 0; }
+
+#endif
diff --git a/src/AntMessenger.hpp b/src/AntMessenger.hpp
new file mode 100644
index 0000000..f5defe3
--- /dev/null
+++ b/src/AntMessenger.hpp
@@ -0,0 +1,176 @@
+// -*- mode: c++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2; coding: utf-8-unix -*-
+// ***** BEGIN LICENSE BLOCK *****
+////////////////////////////////////////////////////////////////////
+// Copyright (c) 2011-2013 RALOVICH, Kristóf //
+// //
+// This program is free software; you can redistribute it and/or //
+// modify it under the terms of the GNU General Public License //
+// version 2 as published by the Free Software Foundation. //
+// //
+////////////////////////////////////////////////////////////////////
+// ***** END LICENSE BLOCK *****
+#pragma once
+
+#include "antdefs.hpp"
+#include "AntMessage.hpp"
+#include "AntChannel.hpp"
+#include "lqueue.hpp"
+#include <list>
+#include "SmartPtrFwd.hpp"
+#include "Serial.hpp"
+#include <boost/thread.hpp>
+
+
+
+
+
+
+
+// Interface for delivering event of sent/received ANT+ messages. MUST be thread safe.
+class AntCallback
+{
+public:
+ virtual ~AntCallback() {}
+ virtual void onAntReceived(const AntMessage m) {}
+ virtual void onAntSent(const AntMessage m) {}
+};
+
+
+class AntLoggerCallback : public AntCallback
+{
+public:
+ virtual ~AntLoggerCallback() {}
+ virtual void onAntReceived(const AntMessage m) {}
+ virtual void onAntSent(const AntMessage m) {}
+protected:
+private:
+};
+
+
+class AntUsbMonLoggerCallback : public AntCallback
+{
+public:
+ AntUsbMonLoggerCallback(const std::string& s) : m_logFileName(s) {}
+ virtual ~AntUsbMonLoggerCallback() { AntMessage::saveAsUsbMon(m_logFileName.c_str(), m_l); }
+ virtual void onAntReceived(const AntMessage m) {m_l.push_back(m);}
+ virtual void onAntSent(const AntMessage m) {m_l.push_back(m);}
+protected:
+private:
+ std::string m_logFileName;
+ std::list<AntMessage> m_l;
+};
+
+class AntParsedLoggerCallback : public AntCallback
+{
+public:
+ AntParsedLoggerCallback(const std::string& s) : m_logFileName(s), cnt(0) {LOG_VAR(m_logFileName); }
+ virtual ~AntParsedLoggerCallback() { AntMessage::saveAsAntParse(m_logFileName.c_str(), m_l.getListCopy()); }
+ virtual void onAntReceived(const AntMessage m) {m_l.push(m); saveIfNeeded(); }
+ virtual void onAntSent(const AntMessage m) {m_l.push(m); saveIfNeeded(); }
+protected:
+ void saveIfNeeded()
+ {
+ if(++cnt%10)
+ {
+ cnt=0;
+ AntMessage::saveAsAntParse(m_logFileName.c_str(), m_l.getListCopy());
+ m_l.clear();
+ }
+ }
+private:
+ std::string m_logFileName;
+ //std::list<AntMessage> m_l;
+ lqueue2<AntMessage>m_l;
+ int cnt;
+};
+
+
+
+
+
+
+
+struct AntMessenger_Recevier;
+// Deals with sending/receiving ANT+ messages. Performs message framing.
+class AntMessenger
+{
+public:
+ AntMessenger(bool eventLoopInBgTh = true);
+ ~AntMessenger();
+ void setHandler(Serial* io){m_io=io;}
+ void setCallback(AntCallback* cb){m_cb=cb;}
+ //void addCallback(std::shared_ptr<AntCallback> cb) { m_cbs.push_back(cb); }
+ size_t getQueueLength() const;
+ std::list<AntMessage> getQueue();
+
+ bool ANT_ResetSystem();
+ bool ANT_SetNetworkKey(const unsigned char net, const unsigned char key[8]);
+ bool ANT_AssignChannel(uchar chan, uchar chtype, uchar net);
+ bool ANT_SetChannelMessagePeriod(uchar chan, ushort msgPeriod);
+ bool ANT_SetChannelSearchTimeout(uchar chan, uchar searchTimeout);
+ bool ANT_SetChannelRadioFreq(uchar chan, uchar freq);
+ bool ANT_SetSearchWaveform(uchar chan, ushort wave);
+ bool ANT_SetChannelId(uchar chan, ushort devNum, uchar devId, uchar transType);
+ bool ANT_OpenChannel(uchar chan);
+ bool ANT_CloseChannel(uchar chan, const size_t timeout_ms = 1000);
+ bool ANT_RequestMessage(uchar chan, uchar reqMsgId);
+ bool ANT_GetChannelId(uchar chan, ushort* devNum, uchar* devId, uchar* transType, size_t timeout_ms = 0);
+ bool ANT_SendAcknowledgedData(const uchar chan, const uchar data[8], const size_t timeout_ms = 0);
+ bool ANT_SendBurstData(const uchar seqchan, const uchar data[8]);
+ bool ANT_SendBurstData2(const uchar chan, const uchar* data, const size_t len);
+ bool ANT_SendBurstData2(const uchar chan, const std::vector<uchar>& data);
+
+ bool ANTFS_Link(const uchar chan, const uchar freq, const uchar beaconPer, const uint hostSN);
+ bool ANTFS_Disconnect(const uchar chan);
+ bool ANTFS_Pairing(const uchar chan, const uint hostSN, const std::string& name1, uint& unitId, uint64_t& key);
+ bool ANTFS_Authenticate(const uchar chan, const uint hostSN, const uint64_t pairedKey);
+ bool ANTFS_Download(const uchar chan, const ushort file, std::vector<uchar>& data);
+ //bool ANTFS_Upload(const uchar chan);
+ bool ANTFS_Erase(const uchar chan, const ushort file);
+ bool ANTFS_RequestClientDeviceSerialNumber(const uchar chan, const uint hostSN, uint& sn, std::string& devName);
+
+ void eventLoop();
+ void kill();
+private:
+ bool sendCommand(uchar mesg, uchar *inbuf, uchar len, const size_t timeout_ms = 0);
+ bool sendCommand(AntMessage& m, const size_t timeout_ms = 0);
+ bool sendRequest(uchar reqMsgId, uchar chan, AntMessage* response = NULL, const size_t timeout_ms = 0);
+ bool writeMessage(uchar mesg, uchar *inbuf, uchar len);
+ bool writeMessage(AntMessage& m);
+ bool sendAckData(const uchar chan, const uchar data[8], const size_t timeout_ms = 0);
+
+ bool assemblePackets(std::list<uchar>& q);
+
+ bool onMessage(std::vector<AntMessage> v);
+
+public:
+ bool waitForBurst(const uchar chan, std::vector<uchar>& burstData, const size_t timeout_ms = 30000);
+ bool waitForBroadcast(const uchar chan, AntMessage* reply = NULL, const size_t timeout_ms = 2000);
+//public:
+private:
+ Serial* m_io;
+ AntCallback* m_cb;
+ //std::vector<std::shared_ptr<AntCallback>> m_cbs;
+ boost::thread m_packerTh; // thread to reconstruct messages from bytes flowing in
+ volatile int m_packerThKill;
+
+ // received packet queue
+ lqueue3<AntMessage> m_rpackQueue2;
+ volatile size_t packetIdx;
+private:
+ AntChannel chs[ANTPM_MAX_CHANNELS];
+ friend struct AntMessenger_Recevier;
+ void* th_messageHandler(); // PUBLIC on purpose
+};
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt
new file mode 100644
index 0000000..9b442b3
--- /dev/null
+++ b/src/CMakeLists.txt
@@ -0,0 +1,135 @@
+# -*- mode: cmake; coding: utf-8-dos -*-
+CMAKE_MINIMUM_REQUIRED(VERSION 2.8.6)
+IF(COMMAND CMAKE_POLICY)
+ CMAKE_POLICY(SET CMP0003 NEW)
+ENDIF(COMMAND CMAKE_POLICY)
+
+
+PROJECT(antpm C CXX)
+
+
+SET(CMAKE_MODULE_PATH ${CMAKE_MODULE_PATH} "${CMAKE_SOURCE_DIR}/cmake/")
+
+
+IF(MSVC)
+ IF(NOT DEP_ROOT)
+ SET(DEP_ROOT "DEP_ROOT-NOTFOUND" CACHE PATH "dependency root path")
+ ENDIF()
+ SET(LIBUSB_ROOT "${CMAKE_SOURCE_DIR}\\..\\3rd_party\\libusb-win32-bin-1.2.6.0\\")
+ SET(BOOST_ROOT "${DEP_ROOT}\\boost_1_43_0" CACHE PATH "Boost PATH" )
+
+ SET(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} /MP")
+ ADD_DEFINITIONS(-D_CRT_SECURE_NO_WARNINGS)
+ENDIF()
+
+IF(CMAKE_COMPILER_IS_GNUCXX)
+ set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fpermissive -fms-extensions -std=c++0x -Wall")
+ENDIF()
+
+INCLUDE(FindLIBUSB)
+IF(NOT LIBUSB_FOUND)
+ MESSAGE(FATAL_ERROR "Could not find LIBUSB")
+ENDIF()
+
+set(Boost_USE_STATIC_LIBS ON)
+set(Boost_USE_MULTITHREADED ON)
+set(Boost_USE_STATIC_RUNTIME OFF)
+FIND_PACKAGE(Boost COMPONENTS thread unit_test_framework program_options system filesystem REQUIRED)
+IF(NOT Boost_FOUND)
+ MESSAGE(FATAL_ERROR "Boost not found, please set BOOST_ROOT!")
+ENDIF()
+
+
+INCLUDE_DIRECTORIES(.)
+INCLUDE_DIRECTORIES(${Boost_INCLUDE_DIR})
+INCLUDE_DIRECTORIES(${LIBUSB_INCLUDE_DIRS})
+
+LINK_DIRECTORIES(${Boost_LIBRARY_DIRS})
+
+IF(MSVC)
+ENDIF()
+
+SET(SRCS
+ antdefs.hpp
+ SmartPtrFwd.hpp w_inttypes.h w_stdint.h stdintfwd.hpp
+ common.hpp common.cpp
+ AntMessage.hpp AntMessage.cpp
+ AntChannel.hpp AntChannel.cpp
+ AntMessenger.hpp AntMessenger.cpp
+ SerialUsb.hpp SerialUsb.cpp
+ AntFr310XT.hpp AntFr310XT.cpp
+ lqueue.hpp
+ Serial.hpp
+
+ FIT.hpp FIT.cpp
+ GPX.hpp GPX.cpp
+ GarminConvert.hpp GarminConvert.cpp
+)
+IF(NOT WIN32)
+ SET(SRCS
+ ${SRCS}
+ SerialTty.hpp SerialTty.cpp
+ )
+SET(Boost_THREAD_LIBRARY "${Boost_THREAD_LIBRARY};pthread")
+ENDIF()
+
+
+ADD_LIBRARY(antpm
+ ${SRCS}
+)
+TARGET_LINK_LIBRARIES(antpm
+ ${LIBUSB_LIBRARIES}
+ ${Boost_THREAD_LIBRARY}
+ ${Boost_SYSTEM_LIBRARY}
+ ${Boost_FILESYSTEM_LIBRARY}
+)
+
+ADD_EXECUTABLE(antpm-usbmon2ant
+ antpm-usbmon2ant.cpp
+)
+TARGET_LINK_LIBRARIES(antpm-usbmon2ant
+ antpm
+ ${Boost_PROGRAM_OPTIONS_LIBRARY}
+)
+INSTALL(TARGETS antpm-usbmon2ant RUNTIME DESTINATION bin)
+
+
+ADD_EXECUTABLE(antpm-downloader
+ antpm-downloader.cpp
+)
+TARGET_LINK_LIBRARIES(antpm-downloader
+ antpm
+ ${Boost_PROGRAM_OPTIONS_LIBRARY}
+)
+INSTALL(TARGETS antpm-downloader RUNTIME DESTINATION bin)
+
+
+ADD_EXECUTABLE(antpm-fit2gpx
+ antpm-fit2gpx.cpp
+)
+TARGET_LINK_LIBRARIES(antpm-fit2gpx
+ antpm
+ ${Boost_PROGRAM_OPTIONS_LIBRARY}
+)
+INSTALL(TARGETS antpm-fit2gpx RUNTIME DESTINATION bin)
+
+add_custom_target(dist COMMAND ${CMAKE_MAKE_PROGRAM} package_source)
+
+
+set(CPACK_PACKAGE_VERSION_MAJOR "1")
+set(CPACK_PACKAGE_VERSION_MINOR "0")
+set(CPACK_SOURCE_GENERATOR "TGZ")
+set(CPACK_SOURCE_PACKAGE_FILE_NAME "${CMAKE_PROJECT_NAME}-${CPACK_PACKAGE_VERSION_MAJOR}.${CPACK_PACKAGE_VERSION_MINOR}-src")
+MESSAGE(STATUS "${CPACK_SOURCE_IGNORE_FILES}")
+set(CPACK_SOURCE_IGNORE_FILES
+ "/build/;/.bzr/;~$;${CPACK_SOURCE_IGNORE_FILES};/CMakeLists.txt.user;/gpsbabel/;/gtest-1.6.0/")
+set(CPACK_DEBIAN_PACKAGE_MAINTAINER "Kristof Ralovich <tade60@freemail.hu>")
+set(CPACK_PACKAGE_DESCRIPTION_SUMMARY "ANT+minus")
+set(CPACK_PACKAGE_DESCRIPTION "Userspace implementation of a wire protocol similar to the ANT/ANT+ protocols. The goal is to be able to communicate with the Forerunner 310XT watch, in order to retrieve sports tracks. Communication with other watches might work, please report your experiences to improve the software.")
+set(CPACK_DEBIAN_PACKAGE_DEPENDS "libstdc++6 (>=4.6.0)")
+#set(CPACK_DEBIAN_PACKAGE_DEPENDS "libstdc++6 (>=4.6.0), cmake, debhelper (>= 7), libboost-filesystem, libboost-system")
+#set(CPACK_DEBIAN_PACKAGE_CONTROL_EXTRA This variable allow advanced user to add custom script to the control.tar.gz (inside the .deb archive) ${CMAKE_CURRENT_SOURCE_DIR}/postinst
+#set(CPACK_DEBIAN_PACKAGE_SECTION Package section (see http://packages.debian.org/stable/) Network
+#set(CPACK_DEBIAN_PACKAGE_VERSION Package version ${CPACK_PACKAGE_VERSION}+lenny1
+INCLUDE(CPack)
+
diff --git a/src/FIT.cpp b/src/FIT.cpp
new file mode 100644
index 0000000..7f1f5b3
--- /dev/null
+++ b/src/FIT.cpp
@@ -0,0 +1,1221 @@
+// -*- mode: c++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2; coding: utf-8-unix -*-
+/***************************************************************************
+ * Copyright (C) 2010-2012 by Oleg Khudyakov *
+ * prcoder@gmail.com *
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+ * the Free Software Foundation; either version 2 of the License, or *
+ * (at your option) any later version. *
+ * *
+ * This program is distributed in the hope that it will be useful, *
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of *
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
+ * GNU General Public License for more details. *
+ * *
+ * You should have received a copy of the GNU General Public License *
+ * along with this program; if not, write to the *
+ * Free Software Foundation, Inc., *
+ * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. *
+ ***************************************************************************/
+// ***** BEGIN LICENSE BLOCK *****
+////////////////////////////////////////////////////////////////////
+// Copyright (c) 2012-2013 RALOVICH, Kristóf //
+// //
+// This program is free software; you can redistribute it and/or //
+// modify it under the terms of the GNU General Public License //
+// version 2 as published by the Free Software Foundation. //
+// //
+////////////////////////////////////////////////////////////////////
+// ***** END LICENSE BLOCK *****
+
+#include "FIT.hpp"
+#include <time.h>
+#include <string.h>
+#include <sstream>
+#include <iomanip>
+#include <map>
+
+#include <iostream> // DEBUG
+
+#include "common.hpp"
+
+using namespace std;
+
+FIT::FIT()
+{
+ messageTypeMap[0] = "File Id";
+ messageFieldNameMap[0][0] = "Type";
+ messageFieldTypeMap[0][0] = MessageFieldTypeFileType;
+ messageFieldNameMap[0][1] = "Manufacturer";
+ messageFieldTypeMap[0][1] = MessageFieldTypeManufacturer;
+ messageFieldNameMap[0][2] = "Product";
+ messageFieldTypeMap[0][2] = MessageFieldTypeProduct;
+ messageFieldNameMap[0][3] = "Serial Number";
+ messageFieldNameMap[0][4] = "Creation Time";
+ messageFieldTypeMap[0][4] = MessageFieldTypeTimestamp;
+ messageFieldNameMap[0][5] = "Number";
+
+ messageTypeMap[1] = "Capabilities";
+ messageFieldNameMap[1][21] = "Workout Supported";
+
+ messageTypeMap[2] = "Device Settings";
+ messageFieldNameMap[2][1] = "UTC Offset";
+
+ messageTypeMap[3] = "User Profile";
+ messageFieldNameMap[3][254] = "Index";
+ messageFieldNameMap[3][0] = "Name";
+ messageFieldNameMap[3][1] = "Gender";
+ messageFieldTypeMap[3][1] = MessageFieldTypeGender;
+ messageFieldNameMap[3][2] = "Age";
+ messageFieldNameMap[3][3] = "Height";
+ messageFieldNameMap[3][4] = "Weight";
+ messageFieldTypeMap[3][4] = MessageFieldTypeWeight;
+ messageFieldNameMap[3][5] = "Language";
+ messageFieldTypeMap[3][5] = MessageFieldTypeLanguage;
+ messageFieldNameMap[3][6] = "Elevation Units";
+ messageFieldNameMap[3][7] = "Weight Units";
+ messageFieldNameMap[3][8] = "HR Resting";
+ messageFieldNameMap[3][9] = "HR Running Max";
+ messageFieldNameMap[3][10] = "HR Biking Max";
+ messageFieldNameMap[3][11] = "HR Max";
+ messageFieldNameMap[3][12] = "HR Setting";
+ messageFieldNameMap[3][13] = "Speed Setting";
+ messageFieldNameMap[3][14] = "Dist Setting";
+ messageFieldNameMap[3][16] = "Power Setting";
+ messageFieldNameMap[3][17] = "Activity Class";
+ messageFieldNameMap[3][18] = "Position Setting";
+
+ messageTypeMap[4] = "HRM Profile";
+ messageFieldNameMap[4][254] = "Index";
+ messageFieldNameMap[4][0] = "Enabled";
+ messageFieldNameMap[4][1] = "HRM ANT Id";
+
+ messageTypeMap[5] = "SDM Profile";
+
+ messageTypeMap[6] = "Bike Profile";
+ messageFieldNameMap[6][254] = "Index";
+ messageFieldNameMap[6][0] = "Name";
+ messageFieldNameMap[6][1] = "Sport";
+ messageFieldNameMap[6][2] = "SubSport";
+ messageFieldNameMap[6][3] = "Odometer";
+ messageFieldTypeMap[6][3] = MessageFieldTypeOdometr;
+ messageFieldNameMap[6][4] = "Bike Spd ANT Id";
+ messageFieldNameMap[6][5] = "Bike Cad ANT Id";
+ messageFieldNameMap[6][6] = "Bike Spd/Cad ANT Id";
+ messageFieldNameMap[6][7] = "Bike Power ANT Id";
+ messageFieldNameMap[6][8] = "Custom Wheel Size";
+ messageFieldNameMap[6][9] = "Auto Wheel Size";
+ messageFieldNameMap[6][10] = "Bike Weight";
+ messageFieldTypeMap[6][10] = MessageFieldTypeWeight;
+ messageFieldNameMap[6][11] = "Power Calibration Factor";
+ messageFieldNameMap[6][12] = "Auto Wheel Calibration";
+ messageFieldNameMap[6][13] = "Auto Power Zero";
+ messageFieldNameMap[6][14] = "Id";
+ messageFieldNameMap[6][15] = "Spd Enabled";
+ messageFieldNameMap[6][16] = "Cad Enabled";
+ messageFieldNameMap[6][17] = "Spd/Cad Enabled";
+ messageFieldNameMap[6][18] = "Power Enabled";
+ messageFieldNameMap[6][19] = "Crank Length";
+ messageFieldNameMap[6][20] = "Enabled";
+ messageFieldNameMap[6][21] = "Bike Spd ANT Id Trans Type";
+ messageFieldNameMap[6][22] = "Bike Cad ANT Id Trans Type";
+ messageFieldNameMap[6][23] = "Bike Spd/Cad ANT Id Trans Type";
+ messageFieldNameMap[6][24] = "Bike Power ANT Id Trans Type";
+
+ messageTypeMap[7] = "Zones Target";
+ messageFieldNameMap[7][1] = "Max Heart Rate";
+ messageFieldNameMap[7][2] = "Threshold Heart Rate";
+ messageFieldNameMap[7][3] = "Functional Threshold Power";
+ messageFieldNameMap[7][5] = "HR Calc Type";
+ messageFieldNameMap[7][6] = "PWR Calc Type";
+
+ messageTypeMap[8] = "Heart Rate Zone";
+ messageFieldNameMap[8][254] = "Index";
+ messageFieldNameMap[8][1] = "High BPM";
+ messageFieldNameMap[8][2] = "Name";
+
+ messageTypeMap[9] = "Power Zone";
+ messageFieldNameMap[9][254] = "Index";
+ messageFieldNameMap[9][1] = "High Value";
+ messageFieldNameMap[9][2] = "Name";
+
+ messageTypeMap[10] = "Met Zone";
+ messageFieldNameMap[10][254] = "Index";
+ messageFieldNameMap[10][1] = "High BPM";
+ messageFieldNameMap[10][2] = "Calories";
+ messageFieldNameMap[10][3] = "Fat Calories";
+
+ messageTypeMap[12] = "Sport";
+ messageFieldNameMap[12][0] = "Sport";
+ messageFieldTypeMap[12][0] = MessageFieldTypeSport;
+ messageFieldNameMap[12][1] = "SubSport";
+ messageFieldNameMap[12][2] = "Name";
+
+ messageTypeMap[15] = "Traning Goals";
+
+ messageTypeMap[18] = "Session";
+ messageFieldNameMap[18][254] = "Index";
+ messageFieldNameMap[18][253] = "Timestamp";
+ messageFieldNameMap[18][0] = "Event";
+ messageFieldTypeMap[18][0] = MessageFieldTypeEvent;
+ messageFieldNameMap[18][1] = "Event Type";
+ messageFieldTypeMap[18][1] = MessageFieldTypeEventType;
+ messageFieldNameMap[18][2] = "Start Time";
+ messageFieldTypeMap[18][2] = MessageFieldTypeTimestamp;
+ messageFieldNameMap[18][3] = "Start Position Latitude";
+ messageFieldTypeMap[18][3] = MessageFieldTypeCoord;
+ messageFieldNameMap[18][4] = "Start Position Longitude";
+ messageFieldTypeMap[18][4] = MessageFieldTypeCoord;
+ messageFieldNameMap[18][5] = "Sport";
+ messageFieldTypeMap[18][5] = MessageFieldTypeSport;
+ messageFieldNameMap[18][6] = "SubSport";
+ messageFieldNameMap[18][7] = "Total Elapsed Time";
+ messageFieldTypeMap[18][7] = MessageFieldTypeTime;
+ messageFieldNameMap[18][8] = "Total Timer Time";
+ messageFieldTypeMap[18][8] = MessageFieldTypeTime;
+ messageFieldNameMap[18][9] = "Total Distance";
+ messageFieldTypeMap[18][9] = MessageFieldTypeOdometr;
+ messageFieldNameMap[19][10] = "Total Cycles";
+ messageFieldNameMap[18][11] = "Total Calories";
+ messageFieldNameMap[18][13] = "Total Fat Calories";
+ messageFieldNameMap[18][14] = "Average Speed";
+ messageFieldTypeMap[18][14] = MessageFieldTypeSpeed;
+ messageFieldNameMap[18][15] = "Max Speed";
+ messageFieldTypeMap[18][15] = MessageFieldTypeSpeed;
+ messageFieldNameMap[18][16] = "Average Heart Rate";
+ messageFieldNameMap[18][17] = "Max Heart Rate";
+ messageFieldNameMap[18][18] = "Average Cadence";
+ messageFieldNameMap[18][19] = "Max Cadence";
+ messageFieldNameMap[18][20] = "Average Power";
+ messageFieldNameMap[18][21] = "Max Power";
+ messageFieldNameMap[18][22] = "Total Ascent";
+ messageFieldNameMap[18][23] = "Total Descent";
+ messageFieldNameMap[18][24] = "Total Traning Effect";
+ messageFieldNameMap[18][25] = "First Lap Index";
+ messageFieldNameMap[18][26] = "Num Laps";
+ messageFieldNameMap[18][27] = "Event Group";
+ messageFieldNameMap[18][28] = "Trigger";
+ messageFieldNameMap[18][29] = "NEC Latitude";
+ messageFieldTypeMap[18][29] = MessageFieldTypeCoord;
+ messageFieldNameMap[18][30] = "NEC Longitude";
+ messageFieldTypeMap[18][30] = MessageFieldTypeCoord;
+ messageFieldNameMap[18][31] = "SWC Latitude";
+ messageFieldTypeMap[18][31] = MessageFieldTypeCoord;
+ messageFieldNameMap[18][32] = "SWC Longitude";
+ messageFieldTypeMap[18][32] = MessageFieldTypeCoord;
+
+ messageTypeMap[19] = "Lap";
+ messageFieldNameMap[19][254] = "Index";
+ messageFieldNameMap[19][253] = "Timestamp";
+ messageFieldNameMap[19][0] = "Event";
+ messageFieldTypeMap[19][0] = MessageFieldTypeEvent;
+ messageFieldNameMap[19][1] = "Event Type";
+ messageFieldTypeMap[19][1] = MessageFieldTypeEventType;
+ messageFieldNameMap[19][2] = "Start Time";
+ messageFieldTypeMap[19][2] = MessageFieldTypeTimestamp;
+ messageFieldNameMap[19][3] = "Start Position Latitude";
+ messageFieldTypeMap[19][3] = MessageFieldTypeCoord;
+ messageFieldNameMap[19][4] = "Start Position Longitude";
+ messageFieldTypeMap[19][4] = MessageFieldTypeCoord;
+ messageFieldNameMap[19][5] = "End Position Latitude";
+ messageFieldTypeMap[19][5] = MessageFieldTypeCoord;
+ messageFieldNameMap[19][6] = "End Position Longitude";
+ messageFieldTypeMap[19][6] = MessageFieldTypeCoord;
+ messageFieldNameMap[19][7] = "Total Elapsed Time";
+ messageFieldTypeMap[19][7] = MessageFieldTypeTime;
+ messageFieldNameMap[19][8] = "Total Timer Time";
+ messageFieldTypeMap[19][8] = MessageFieldTypeTime;
+ messageFieldNameMap[19][9] = "Total Distance";
+ messageFieldTypeMap[19][9] = MessageFieldTypeOdometr;
+ messageFieldNameMap[19][10] = "Total Cycles";
+ messageFieldNameMap[19][11] = "Total Calories";
+ messageFieldNameMap[19][12] = "Total Fat Calories";
+ messageFieldNameMap[19][13] = "Average Speed";
+ messageFieldTypeMap[19][13] = MessageFieldTypeSpeed;
+ messageFieldNameMap[19][14] = "Max Speed";
+ messageFieldTypeMap[19][14] = MessageFieldTypeSpeed;
+ messageFieldNameMap[19][15] = "Average Heart Rate";
+ messageFieldNameMap[19][16] = "Max Heart Rate";
+ messageFieldNameMap[19][17] = "Average Cadence";
+ messageFieldNameMap[19][18] = "Max Cadence";
+ messageFieldNameMap[19][19] = "Average Power";
+ messageFieldNameMap[19][20] = "Max Power";
+ messageFieldNameMap[19][21] = "Total Ascent";
+ messageFieldNameMap[19][22] = "Total Descent";
+ messageFieldNameMap[19][23] = "Intensity";
+ messageFieldNameMap[19][24] = "Lap Trigger";
+ messageFieldNameMap[19][25] = "Sport";
+ messageFieldTypeMap[19][25] = MessageFieldTypeSport;
+ messageFieldNameMap[19][26] = "Event Group";
+ messageFieldNameMap[19][27] = "Nec Latitude";
+ messageFieldTypeMap[19][27] = MessageFieldTypeCoord;
+ messageFieldNameMap[19][28] = "Nec Longitude";
+ messageFieldTypeMap[19][28] = MessageFieldTypeCoord;
+ messageFieldNameMap[19][29] = "Swc Latitude";
+ messageFieldTypeMap[19][29] = MessageFieldTypeCoord;
+ messageFieldNameMap[19][30] = "Swc Longitude";
+ messageFieldTypeMap[19][30] = MessageFieldTypeCoord;
+
+ messageTypeMap[20] = "Record";
+ messageFieldNameMap[20][253] = "Timestamp";
+ messageFieldNameMap[20][0] = "Latitude";
+ messageFieldTypeMap[20][0] = MessageFieldTypeCoord;
+ messageFieldNameMap[20][1] = "Longitude";
+ messageFieldTypeMap[20][1] = MessageFieldTypeCoord;
+ messageFieldNameMap[20][2] = "Altitude";
+ messageFieldTypeMap[20][2] = MessageFieldTypeAltitude;
+ messageFieldNameMap[20][3] = "Heart Rate";
+ messageFieldNameMap[20][4] = "Cadence";
+ messageFieldNameMap[20][5] = "Distance";
+ messageFieldTypeMap[20][5] = MessageFieldTypeOdometr;
+ messageFieldNameMap[20][6] = "Speed";
+ messageFieldTypeMap[20][6] = MessageFieldTypeSpeed;
+ messageFieldNameMap[20][7] = "Power";
+ messageFieldNameMap[20][8] = "Compressed Speed & Distance";
+ messageFieldNameMap[20][9] = "Grade";
+ messageFieldNameMap[20][10] = "Registance";
+ messageFieldNameMap[20][11] = "Time from Course";
+ messageFieldTypeMap[20][11] = MessageFieldTypeTime;
+ messageFieldNameMap[20][12] = "Cycle Length";
+ messageFieldNameMap[20][13] = "Temperature";
+ messageFieldNameMap[20][14] = "Speed 1s";
+ messageFieldNameMap[20][15] = "Cycles";
+ messageFieldNameMap[20][16] = "Total Cycles";
+ messageFieldNameMap[20][17] = "Compressed Accumulated Power";
+ messageFieldNameMap[20][18] = "Accumulated Power";
+ messageFieldNameMap[20][19] = "Left-Right Balance";
+
+ messageTypeMap[21] = "Event";
+ messageFieldNameMap[21][253] = "Timestamp";
+ messageFieldNameMap[21][0] = "Event";
+ messageFieldTypeMap[21][0] = MessageFieldTypeEvent;
+ messageFieldNameMap[21][1] = "Event Type";
+ messageFieldTypeMap[21][1] = MessageFieldTypeEventType;
+ messageFieldNameMap[21][2] = "Data1";
+ messageFieldNameMap[21][3] = "Data2";
+ messageFieldNameMap[21][4] = "Event Group";
+
+ messageTypeMap[23] = "Device Info";
+ messageFieldNameMap[23][253] = "Timestamp";
+ messageFieldNameMap[23][0] = "Device Index";
+ messageFieldNameMap[23][1] = "Device Type";
+ messageFieldNameMap[23][2] = "Manufacturer";
+ messageFieldTypeMap[23][2] = MessageFieldTypeManufacturer;
+ messageFieldNameMap[23][3] = "Serial Number";
+ messageFieldNameMap[23][4] = "Product";
+ messageFieldTypeMap[23][4] = MessageFieldTypeProduct;
+ messageFieldNameMap[23][5] = "Software Version";
+ messageFieldNameMap[23][6] = "Hardware Version";
+ messageFieldNameMap[23][10] = "Battery Voltage";
+ messageFieldNameMap[23][11] = "Battery Status";
+
+ messageTypeMap[26] = "Workout";
+ messageFieldNameMap[26][4] = "Sport";
+ messageFieldTypeMap[26][4] = MessageFieldTypeSport;
+ messageFieldNameMap[26][5] = "Capabilities";
+ messageFieldNameMap[26][6] = "Valid Steps";
+ messageFieldNameMap[26][7] = "Protection";
+ messageFieldNameMap[26][8] = "Name";
+
+ messageTypeMap[27] = "Workout Step";
+ messageFieldNameMap[27][254] = "Index";
+ messageFieldNameMap[27][0] = "Step Name";
+ messageFieldNameMap[27][1] = "Duration Type";
+ messageFieldNameMap[27][2] = "Duration Value";
+ messageFieldNameMap[27][3] = "Target Type";
+ messageFieldNameMap[27][4] = "Target Value";
+ messageFieldNameMap[27][5] = "Custom Target Value Low";
+ messageFieldNameMap[27][6] = "Custom Target Value High";
+ messageFieldNameMap[27][7] = "Intensity";
+
+ messageTypeMap[28] = "Schedule";
+ messageFieldNameMap[28][0] = "Manufacturer";
+ messageFieldTypeMap[28][0] = MessageFieldTypeManufacturer;
+ messageFieldNameMap[28][1] = "Product";
+ messageFieldTypeMap[28][1] = MessageFieldTypeProduct;
+ messageFieldNameMap[28][2] = "Serial Number";
+ messageFieldNameMap[28][3] = "Creation Time";
+ messageFieldTypeMap[28][3] = MessageFieldTypeTimestamp;
+ messageFieldNameMap[28][4] = "Completed";
+ messageFieldNameMap[28][5] = "Type";
+ messageFieldNameMap[28][6] = "Schedule Time";
+ messageFieldTypeMap[28][6] = MessageFieldTypeTime;
+
+ messageTypeMap[29] = "Way Point";
+ messageFieldNameMap[29][253] = "Timestamp";
+ messageFieldNameMap[29][254] = "Index";
+ messageFieldNameMap[29][0] = "Name";
+ messageFieldNameMap[29][1] = "Latitude";
+ messageFieldTypeMap[29][1] = MessageFieldTypeCoord;
+ messageFieldNameMap[29][2] = "Longitude";
+ messageFieldTypeMap[29][2] = MessageFieldTypeCoord;
+ messageFieldNameMap[29][3] = "Symbol?";
+ messageFieldNameMap[29][4] = "Altitude";
+ messageFieldTypeMap[29][4] = MessageFieldTypeAltitude;
+ messageFieldNameMap[29][5] = "???";
+ messageFieldNameMap[29][6] = "Date";
+
+ messageTypeMap[30] = "Weight Scale";
+ messageFieldNameMap[30][253] = "Timestamp";
+ messageFieldNameMap[30][0] = "Weight";
+ messageFieldNameMap[30][1] = "Fat percent";
+ messageFieldNameMap[30][2] = "Hydration percent";
+ messageFieldNameMap[30][3] = "Visceral Fat Mass";
+ messageFieldNameMap[30][4] = "Bone Mass";
+ messageFieldNameMap[30][5] = "Muscle Mass";
+ messageFieldNameMap[30][7] = "Basal Met";
+ messageFieldNameMap[30][8] = "Physique Rating";
+ messageFieldNameMap[30][9] = "Active Met";
+ messageFieldNameMap[30][10] = "Metabolic Age";
+ messageFieldNameMap[30][11] = "Visceral Fat Rating";
+
+ messageTypeMap[31] = "Course";
+ messageFieldNameMap[31][4] = "Sport";
+ messageFieldTypeMap[31][4] = MessageFieldTypeSport;
+ messageFieldNameMap[31][5] = "Name";
+ messageFieldNameMap[31][6] = "Capabilities";
+
+ messageTypeMap[32] = "Course Point";
+ messageFieldNameMap[32][254] = "Index";
+ messageFieldNameMap[32][1] = "Time";
+ messageFieldTypeMap[32][1] = MessageFieldTypeTimestamp;
+ messageFieldNameMap[32][2] = "Latitude";
+ messageFieldTypeMap[32][2] = MessageFieldTypeCoord;
+ messageFieldNameMap[32][3] = "Longitude";
+ messageFieldTypeMap[32][3] = MessageFieldTypeCoord;
+ messageFieldNameMap[32][4] = "Distance";
+ messageFieldTypeMap[32][4] = MessageFieldTypeOdometr;
+ messageFieldNameMap[32][5] = "Type";
+ messageFieldNameMap[32][6] = "Name";
+
+ messageTypeMap[33] = "Totals";
+ messageFieldNameMap[33][254] = "Index";
+ messageFieldNameMap[33][253] = "Timestamp";
+ messageFieldNameMap[33][0] = "Timer Time";
+ messageFieldTypeMap[33][0] = MessageFieldTypeTime;
+ messageFieldNameMap[33][1] = "Distance";
+ messageFieldTypeMap[33][1] = MessageFieldTypeOdometr;
+ messageFieldNameMap[33][2] = "Calories";
+ messageFieldNameMap[33][3] = "Sport";
+ messageFieldTypeMap[33][3] = MessageFieldTypeSport;
+
+ messageTypeMap[34] = "Activity";
+ messageFieldNameMap[34][253] = "Timestamp";
+ messageFieldNameMap[34][0] = "Total Timer Time";
+ messageFieldTypeMap[34][0] = MessageFieldTypeTime;
+ messageFieldNameMap[34][1] = "Number of Sessions";
+ messageFieldNameMap[34][2] = "Type";
+ messageFieldNameMap[34][3] = "Event";
+ messageFieldTypeMap[34][3] = MessageFieldTypeEvent;
+ messageFieldNameMap[34][4] = "Event Type";
+ messageFieldTypeMap[34][4] = MessageFieldTypeEventType;
+ messageFieldNameMap[34][5] = "Local Timestamp";
+ messageFieldTypeMap[34][5] = MessageFieldTypeTimestamp;
+ messageFieldNameMap[34][6] = "Event Group";
+
+ messageTypeMap[35] = "Software";
+ messageFieldNameMap[35][254] = "Index";
+ messageFieldNameMap[35][3] = "Version";
+ messageFieldNameMap[35][5] = "Part No";
+
+ messageTypeMap[37] = "File Capabilities";
+ messageFieldNameMap[37][254] = "Index";
+ messageFieldNameMap[37][0] = "Type";
+ messageFieldNameMap[37][1] = "Flags";
+ messageFieldNameMap[37][2] = "Directory";
+ messageFieldNameMap[37][3] = "Max Count";
+ messageFieldNameMap[37][4] = "Max Size";
+
+ messageTypeMap[38] = "Message Capabilities";
+ messageFieldNameMap[38][254] = "Index";
+ messageFieldNameMap[38][0] = "File";
+ messageFieldNameMap[38][1] = "Message Num";
+ messageFieldNameMap[38][2] = "Count Type";
+ messageFieldNameMap[38][3] = "Count";
+
+ messageTypeMap[39] = "Field Capabilities";
+ messageFieldNameMap[39][254] = "Index";
+ messageFieldNameMap[39][0] = "File";
+ messageFieldNameMap[39][1] = "Message Num";
+ messageFieldNameMap[39][2] = "Field Num";
+ messageFieldNameMap[39][3] = "Count";
+
+ messageTypeMap[49] = "File Creator";
+ messageFieldNameMap[49][0] = "Software Version";
+ messageFieldNameMap[49][1] = "Hardware Version";
+
+ messageTypeMap[51] = "Blood Pressure";
+
+ messageTypeMap[53] = "Speed Zone";
+ messageFieldNameMap[53][254] = "Index";
+ messageFieldNameMap[53][1] = "High Value";
+ messageFieldNameMap[53][2] = "Name";
+
+ messageTypeMap[55] = "Monitoring";
+ messageTypeMap[78] = "HRV";
+
+ messageTypeMap[79] = "User Profile ?";
+ messageFieldNameMap[79][254] = "Index";
+ messageFieldNameMap[79][253] = "Timestamp";
+ messageFieldNameMap[79][1] = "Age";
+ messageFieldNameMap[79][2] = "Height";
+ messageFieldNameMap[79][3] = "Weight";
+ messageFieldTypeMap[79][3] = MessageFieldTypeWeight;
+
+ messageTypeMap[101] = "Length";
+ messageTypeMap[103] = "Monitoring Info";
+ messageTypeMap[105] = "PAD";
+
+
+ dataTypeMap[BT_Enum] = "enum";
+ dataTypeMap[BT_Int8] = "int8";
+ dataTypeMap[BT_UInt8] = "uint8";
+ dataTypeMap[BT_Int16] = "int16";
+ dataTypeMap[BT_Uint16] = "uint16";
+ dataTypeMap[BT_Int32] = "int32";
+ dataTypeMap[BT_UInt32] = "uint32";
+ dataTypeMap[BT_String] = "string";
+ dataTypeMap[BT_Float32] = "float32";
+ dataTypeMap[BT_Float64] = "float64";
+ dataTypeMap[BT_Uint8z] = "uint8z";
+ dataTypeMap[BT_Uint16z] = "uint16z";
+ dataTypeMap[BT_Uint32z] = "uint32z";
+ dataTypeMap[BT_ByteArray] = "bytes";
+
+ enumMap[MessageFieldTypeFileType][1] = "Device";
+ enumMap[MessageFieldTypeFileType][2] = "Setting";
+ enumMap[MessageFieldTypeFileType][3] = "Sport";
+ enumMap[MessageFieldTypeFileType][4] = "Activity";
+ enumMap[MessageFieldTypeFileType][5] = "Workout";
+ enumMap[MessageFieldTypeFileType][6] = "Course";
+ enumMap[MessageFieldTypeFileType][7] = "Schedules";
+ enumMap[MessageFieldTypeFileType][8] = "Waypoints";
+ enumMap[MessageFieldTypeFileType][9] = "Weight";
+ enumMap[MessageFieldTypeFileType][10] = "Totals";
+ enumMap[MessageFieldTypeFileType][11] = "Goals";
+ enumMap[MessageFieldTypeFileType][14] = "Blood Pressure";
+ enumMap[MessageFieldTypeFileType][15] = "Monitoring";
+ enumMap[MessageFieldTypeFileType][20] = "Activity Summary";
+ enumMap[MessageFieldTypeFileType][28] = "Monitoring Daily";
+ enumMap[MessageFieldTypeFileType][32] = "Memory";
+
+ enumMap[MessageFieldTypeGender][0] = "Female";
+ enumMap[MessageFieldTypeGender][1] = "Male";
+
+ enumMap[MessageFieldTypeLanguage][0] = "English";
+ enumMap[MessageFieldTypeLanguage][1] = "French";
+ enumMap[MessageFieldTypeLanguage][2] = "Italian";
+ enumMap[MessageFieldTypeLanguage][3] = "German";
+ enumMap[MessageFieldTypeLanguage][4] = "Spanish";
+ enumMap[MessageFieldTypeLanguage][5] = "Croatian";
+ enumMap[MessageFieldTypeLanguage][6] = "Czech";
+ enumMap[MessageFieldTypeLanguage][7] = "Danish";
+ enumMap[MessageFieldTypeLanguage][8] = "Dutch";
+ enumMap[MessageFieldTypeLanguage][9] = "Finnish";
+ enumMap[MessageFieldTypeLanguage][10] = "Greek";
+ enumMap[MessageFieldTypeLanguage][11] = "Hungarian";
+ enumMap[MessageFieldTypeLanguage][12] = "Norwegian";
+ enumMap[MessageFieldTypeLanguage][13] = "Polish";
+ enumMap[MessageFieldTypeLanguage][14] = "Portuguese";
+ enumMap[MessageFieldTypeLanguage][15] = "Slovakian";
+ enumMap[MessageFieldTypeLanguage][16] = "Slovenian";
+ enumMap[MessageFieldTypeLanguage][17] = "Swedish";
+ enumMap[MessageFieldTypeLanguage][18] = "Russian";
+ enumMap[MessageFieldTypeLanguage][19] = "Turkish";
+ enumMap[MessageFieldTypeLanguage][20] = "Latvian";
+ enumMap[MessageFieldTypeLanguage][21] = "Ukrainian";
+ enumMap[MessageFieldTypeLanguage][22] = "Arabic";
+ enumMap[MessageFieldTypeLanguage][23] = "Farsi";
+ enumMap[MessageFieldTypeLanguage][24] = "Bulgarian";
+ enumMap[MessageFieldTypeLanguage][25] = "Romanian";
+ enumMap[MessageFieldTypeLanguage][254] = "Custom";
+
+ enumMap[MessageFieldTypeSport][0] = "Generic";
+ enumMap[MessageFieldTypeSport][1] = "Running";
+ enumMap[MessageFieldTypeSport][2] = "Cycling";
+ enumMap[MessageFieldTypeSport][3] = "Transition";
+ enumMap[MessageFieldTypeSport][4] = "Fitness Equipment";
+ enumMap[MessageFieldTypeSport][5] = "Swimming";
+ enumMap[MessageFieldTypeSport][6] = "Basketball";
+ enumMap[MessageFieldTypeSport][7] = "Soccer";
+ enumMap[MessageFieldTypeSport][8] = "Tennis";
+ enumMap[MessageFieldTypeSport][9] = "American football";
+ enumMap[MessageFieldTypeSport][10] = "Training";
+ enumMap[MessageFieldTypeSport][254] = "All";
+
+ enumMap[MessageFieldTypeEvent][0] = "Timer";
+ enumMap[MessageFieldTypeEvent][3] = "Workout";
+ enumMap[MessageFieldTypeEvent][4] = "Workout Step";
+ enumMap[MessageFieldTypeEvent][5] = "Power Down";
+ enumMap[MessageFieldTypeEvent][6] = "Power Up";
+ enumMap[MessageFieldTypeEvent][7] = "Off Course";
+ enumMap[MessageFieldTypeEvent][8] = "Session";
+ enumMap[MessageFieldTypeEvent][9] = "Lap";
+ enumMap[MessageFieldTypeEvent][10] = "Course Point";
+ enumMap[MessageFieldTypeEvent][11] = "Battery";
+ enumMap[MessageFieldTypeEvent][12] = "Virtual Partner Pace";
+ enumMap[MessageFieldTypeEvent][13] = "HR High Alert";
+ enumMap[MessageFieldTypeEvent][14] = "HR Low Alert";
+ enumMap[MessageFieldTypeEvent][15] = "Speed High Alert";
+ enumMap[MessageFieldTypeEvent][16] = "Speed Low Alert";
+ enumMap[MessageFieldTypeEvent][17] = "Cadence High Alert";
+ enumMap[MessageFieldTypeEvent][18] = "Cadence Low Alert";
+ enumMap[MessageFieldTypeEvent][19] = "Power High Alert";
+ enumMap[MessageFieldTypeEvent][20] = "Power Low Alert";
+ enumMap[MessageFieldTypeEvent][21] = "Recovery HR";
+ enumMap[MessageFieldTypeEvent][22] = "Battery Low";
+ enumMap[MessageFieldTypeEvent][23] = "Time Duration Alert";
+ enumMap[MessageFieldTypeEvent][24] = "Distance Duration Alert";
+ enumMap[MessageFieldTypeEvent][25] = "Calorie Duration Alert";
+ enumMap[MessageFieldTypeEvent][26] = "Activity";
+ enumMap[MessageFieldTypeEvent][27] = "Fitness Equipment";
+
+ enumMap[MessageFieldTypeEventType][0] = "Start";
+ enumMap[MessageFieldTypeEventType][1] = "Stop";
+ enumMap[MessageFieldTypeEventType][2] = "Consecutive Depreciated";
+ enumMap[MessageFieldTypeEventType][3] = "Marker";
+ enumMap[MessageFieldTypeEventType][4] = "Stop All";
+ enumMap[MessageFieldTypeEventType][5] = "Begin Depreciated";
+ enumMap[MessageFieldTypeEventType][6] = "End Depreciated";
+ enumMap[MessageFieldTypeEventType][7] = "End All Depreciated";
+ enumMap[MessageFieldTypeEventType][8] = "Stop Disable";
+ enumMap[MessageFieldTypeEventType][9] = "Stop Disable All";
+
+ manufacturerMap[ManufacturerGarmin] = "Garmin";
+ manufacturerMap[ManufacturerGarminFR405ANTFS] = "Garmin (FR405 ANTFS)";
+ manufacturerMap[ManufacturerZephyr] = "Zephyr";
+ manufacturerMap[ManufacturerDayton] = "Dayton";
+ manufacturerMap[ManufacturerIDT] = "IDT";
+ manufacturerMap[ManufacturerSRM] = "SRM";
+ manufacturerMap[ManufacturerQuarq] = "Quarq";
+ manufacturerMap[ManufacturerIBike] = "iBike";
+ manufacturerMap[ManufacturerSaris] = "Saris";
+ manufacturerMap[ManufacturerSparkHK] = "Spark HK";
+ manufacturerMap[ManufacturerTanita] = "Tanita";
+ manufacturerMap[ManufacturerEchowell] = "Echowell";
+ manufacturerMap[ManufacturerDynastreamOEM] = "Dynastream OEM";
+ manufacturerMap[ManufacturerNautilus] = "Nautilus";
+ manufacturerMap[ManufacturerDynastream] = "Dynastream";
+ manufacturerMap[ManufacturerTimex] = "Timex";
+ manufacturerMap[ManufacturerMetriGear] = "MetriGear";
+ manufacturerMap[ManufacturerXelic] = "Xelic";
+ manufacturerMap[ManufacturerBeurer] = "Beurer";
+ manufacturerMap[ManufacturerCardioSport] = "CardioSport";
+ manufacturerMap[ManufacturerAandD] = "A&D";
+ manufacturerMap[ManufacturerHMM] = "HMM";
+
+ productMap[ManufacturerGarmin][GarminHRM1] = "Heart Rate Monitor";
+ productMap[ManufacturerGarmin][GarminAXH01] = "AXH01 HRM Chipset";
+ productMap[ManufacturerGarmin][GarminAXB01] = "AXB01 Chipset";
+ productMap[ManufacturerGarmin][GarminAXB02] = "AXB02 Chipset";
+ productMap[ManufacturerGarmin][GarminHRM2SS] = "HRM2SS";
+ productMap[ManufacturerGarmin][GarminDsiAlf02] = "DSI ALF 02";
+ productMap[ManufacturerGarmin][GarminFR405] = "Forerunner 405";
+ productMap[ManufacturerGarmin][GarminFR50] = "Forerunner 50";
+ productMap[ManufacturerGarmin][GarminFR60] = "Forerunner 60";
+ productMap[ManufacturerGarmin][GarminFR310XT] = "Forerunner 310XT";
+ productMap[ManufacturerGarmin][GarminEDGE500] = "EDGE 500";
+ productMap[ManufacturerGarmin][GarminFR110] = "Forerunner 110";
+ productMap[ManufacturerGarmin][GarminEDGE800] = "EDGE 800";
+ productMap[ManufacturerGarmin][GarminEDGE200] = "EDGE 200";
+ productMap[ManufacturerGarmin][GarminFR910XT] = "Forerunner 910XT";
+ productMap[ManufacturerGarmin][GarminFR610] = "Forerunner 610";
+ productMap[ManufacturerGarmin][GarminFR70] = "Forerunner 70";
+ productMap[ManufacturerGarmin][GarminFR310XT4T] = "Forerunner 310XT 4T";
+ productMap[ManufacturerGarmin][GarminTraningCenter] = "Traning Center";
+ productMap[ManufacturerGarmin][GarminConnect] = "Connect";
+}
+
+FIT::~FIT()
+{
+}
+
+uint16_t FIT::CRC_byte(uint16_t crc, uint8_t byte)
+{
+ static const uint16_t crc_table[16] =
+ {
+ 0x0000, 0xCC01, 0xD801, 0x1400, 0xF001, 0x3C00, 0x2800, 0xE401,
+ 0xA001, 0x6C00, 0x7800, 0xB401, 0x5000, 0x9C01, 0x8801, 0x4400
+ };
+
+ uint16_t tmp = crc_table[crc & 0xF];
+ crc = (crc >> 4) & 0x0FFF;
+ crc = crc ^ tmp ^ crc_table[byte & 0xF];
+
+ tmp = crc_table[crc & 0xF];
+ crc = (crc >> 4) & 0x0FFF;
+ crc = crc ^ tmp ^ crc_table[(byte >> 4) & 0xF];
+
+ return crc;
+}
+
+string FIT::getDataString(uint8_t *ptr, uint8_t size, uint8_t baseType, uint8_t messageType, uint8_t fieldNum)
+{
+ ostringstream strstrm;
+ strstrm.setf(ios::fixed,ios::floatfield);
+
+ BaseType bt;
+ bt.byte = baseType;
+
+ int baseTypeNum = bt.bits.baseTypeNum;
+ switch(baseTypeNum)
+ {
+ case BT_Enum:
+ {
+ int val = *(int8_t *)ptr;
+ uint8_t type = messageFieldTypeMap[messageType][fieldNum];
+ string strVal(enumMap[type][val]);
+
+ if (!strVal.empty())
+ {
+ strstrm << strVal;
+ }
+ else
+ {
+ strstrm << "[" << dec << val << "]";
+ }
+ break;
+ }
+ case BT_Int8:
+ {
+ int val = *(int8_t *)ptr;
+ strstrm << dec << val;
+ break;
+ }
+ case BT_UInt8:
+ case BT_Uint8z:
+ {
+ unsigned val = *(uint8_t *)ptr;
+ if (val == 0xFF)
+ {
+ strstrm << "undefined";
+ }
+ else
+ {
+ strstrm << dec << val;
+ }
+ break;
+ }
+ case BT_Int16:
+ {
+ int16_t val = *(int16_t *)ptr;
+ if (val == 0x7FFF)
+ {
+ strstrm << "undefined";
+ }
+ else
+ {
+ strstrm << dec << val;
+ }
+ break;
+ }
+ case BT_Uint16:
+ case BT_Uint16z:
+ {
+ uint16_t val = *(uint16_t *)ptr;
+ if (val == 0xFFFF)
+ {
+ strstrm << "undefined";
+ }
+ else
+ {
+ switch (messageFieldTypeMap[messageType][fieldNum])
+ {
+ case MessageFieldTypeAltitude:
+ {
+ strstrm << setprecision(1) << GarminConvert::altitude(val);
+ break;
+ }
+ case MessageFieldTypeWeight:
+ {
+ strstrm << setprecision(1) << GarminConvert::weight(val);
+ break;
+ }
+ case MessageFieldTypeSpeed:
+ {
+ strstrm << setprecision(1) << GarminConvert::speed(val);
+ break;
+ }
+ case MessageFieldTypeManufacturer:
+ {
+ manufacturer = val;
+ strstrm << manufacturerMap[manufacturer];
+ break;
+ }
+ case MessageFieldTypeProduct:
+ {
+ strstrm << productMap[manufacturer][val];
+ break;
+ }
+ default:
+ {
+ strstrm << dec << val;
+ }
+ }
+ }
+ break;
+ }
+ case BT_Int32:
+ {
+ int32_t val = *(int32_t *)ptr;
+ if (val == 0x7FFFFFFF)
+ {
+ strstrm << "undefined";
+ }
+ else
+ {
+ switch(messageFieldTypeMap[messageType][fieldNum])
+ {
+ case MessageFieldTypeCoord:
+ {
+ strstrm << setprecision(5) << GarminConvert::coord(val);
+ break;
+ }
+ default:
+ {
+ strstrm << dec << val;
+ }
+ }
+ }
+ break;
+ }
+ case BT_UInt32:
+ case BT_Uint32z:
+ {
+ uint32_t val = *(uint32_t *)ptr;
+ if (val == 0xFFFFFFFF)
+ {
+ strstrm << "undefined";
+ }
+ else
+ {
+ if (fieldNum == 253)
+ {
+ strstrm << GarminConvert::localTime(val);
+ }
+ else
+ {
+ switch (messageFieldTypeMap[messageType][fieldNum])
+ {
+ case MessageFieldTypeTimestamp:
+ {
+ strstrm << GarminConvert::localTime(val);
+ break;
+ }
+ case MessageFieldTypeTime:
+ {
+ strstrm << GarminConvert::gTime(val);
+ break;
+ }
+ case MessageFieldTypeOdometr:
+ {
+ strstrm << setprecision(2) << GarminConvert::length(val);
+ break;
+ }
+ default:
+ {
+ strstrm << dec << val;
+ }
+ }
+ }
+ }
+ break;
+ }
+ case BT_String:
+ {
+ strstrm << "\"" << GarminConvert::gString(ptr, size) << "\"";
+ break;
+ }
+ }
+
+ return strstrm.str();
+}
+
+bool FIT::parse(vector<uint8_t> &fitData, GPX &gpx)
+{
+ logger() << "Parsing FIT file\n";
+ //logFlush();
+/*
+ FILE *f=fopen("debug.FIT", "wb");
+ fwrite(&fitData[0], fitData.size(), 1 , f);
+ fclose(f);
+*/
+
+ uint8_t *ptr = &fitData.front();
+
+ FITHeader fitHeader;
+ if(fitData.size()<sizeof(fitHeader))
+ return false;
+ memcpy(&fitHeader, ptr, sizeof(fitHeader));
+
+ // FIT header CRC
+ uint16_t crc = 0;
+ for (int i = 0; i < fitHeader.headerSize; i++)
+ {
+ crc = CRC_byte(crc, *(ptr+i));
+ }
+
+ ptr += fitHeader.headerSize;
+
+ // FIT data CRC
+ for (uint32_t i = 0; i < fitHeader.dataSize; i++)
+ {
+ crc = CRC_byte(crc, *(ptr+i));
+ }
+
+ if (memcmp(fitHeader.signature, ".FIT", sizeof(fitHeader.signature)))
+ {
+ logger() << "FIT signature not found\n";
+ //logFlush();
+ return false;
+ }
+
+ uint16_t fitCRC = *(uint16_t *)(ptr+fitHeader.dataSize);
+ if (crc != fitCRC)
+ {
+ logger() << hex << uppercase << setw(4) << setfill('0');
+ logger() << "Invalid FIT CRC (" << crc << "!=" << fitCRC << ")\n";
+ //logFlush();
+
+ return false;
+ }
+
+ logger() << "FIT Protocol Version " << dec << (unsigned)fitHeader.protocolVersion << "\n";
+ //logFlush();
+
+ logger() << "FIT Profile Version " << fitHeader.profileVersion << "\n";
+ //logFlush();
+
+ logger() << "FIT Data size " << fitHeader.dataSize << " bytes\n";
+ //logFlush();
+
+ map<uint8_t, RecordDef> recDefMap;
+
+ for (int bytes = fitHeader.dataSize; bytes > 0;)
+ {
+ RecordHeader rh;
+ memcpy(&rh, ptr, sizeof(rh));
+ ptr += sizeof(rh);
+ bytes -= sizeof(rh);
+
+ if (!rh.normalHeader.headerType)
+ {
+ // Normal Header
+ if (rh.normalHeader.messageType)
+ {
+ // Definition Message
+ RecordDef rd;
+
+ RecordFixed rfx;
+ memcpy(&rfx, ptr, sizeof(rfx));
+ ptr += sizeof(rfx);
+ bytes -= sizeof(rfx);
+
+ rd.rfx = rfx;
+
+ for (int i=0; i<rfx.fieldsNum; i++)
+ {
+ RecordField rf;
+ memcpy(&rf, ptr, sizeof(rf));
+ ptr += sizeof(rf);
+ bytes -= sizeof(rf);
+
+ rd.rf.push_back(rf);
+ }
+
+ recDefMap[rh.normalHeader.localMessageType] = rd;
+ }
+ else
+ {
+ // Data Message
+ map<uint8_t, RecordDef>::iterator it=recDefMap.find(rh.normalHeader.localMessageType);
+ if (it != recDefMap.end())
+ {
+ RecordDef rd = recDefMap[rh.normalHeader.localMessageType];
+ //logger() << "Local Message \"" << messageTypeMap[rd.rfx.globalNum] << "\"(" << rd.rfx.globalNum << "):\n";
+ //logFlush();
+
+ switch(rd.rfx.globalNum)
+ {
+ case 29: // WayPoint
+ {
+ gpx.newWayPoint();
+ break;
+ }
+ }
+
+ uint32_t fileCreationTime;
+ int8_t fileType=INT8_MAX;
+
+ uint32_t time;
+
+ for (int i=0; i<rd.rfx.fieldsNum; i++)
+ {
+ RecordField &rf = rd.rf[i];
+
+ BaseType bt;
+ bt.byte = rf.baseType;
+
+ //logger() << rd.rfx.globalNum << "." << (unsigned)rf.definitionNum << ": " << messageFieldNameMap[rd.rfx.globalNum][rf.definitionNum] <<
+ // " (" << dataTypeMap[bt.bits.baseTypeNum] << ") " << getDataString(ptr, rf.size, bt.bits.baseTypeNum, rd.rfx.globalNum, rf.definitionNum) << "\n";
+ //logFlush();
+
+ switch(rd.rfx.globalNum)
+ {
+ case 0: // File Id
+ {
+ switch(rf.definitionNum)
+ {
+ case 0: // Type
+ {
+ fileType = *(int8_t *)ptr;
+ break;
+ }
+ case 4: // Creation Time
+ {
+ fileCreationTime = *(uint32_t*)ptr;
+ break;
+ }
+ }
+ break;
+ }
+ case 20: // Record
+ {
+ switch(rf.definitionNum)
+ {
+ case 253: // Timestamp
+ {
+ time = *(uint32_t*)ptr;
+ gpx.tracks.back().trackSegs.back().trackPoints[time].time = time;
+ break;
+ }
+ case 0: // Latitude
+ {
+ int32_t latitude = *(int32_t*)ptr;
+ gpx.tracks.back().trackSegs.back().trackPoints[time].latitude = latitude;
+ break;
+ }
+ case 1: // Longitude
+ {
+ uint32_t longitude = *(int32_t*)ptr;
+ gpx.tracks.back().trackSegs.back().trackPoints[time].longitude = longitude;
+ break;
+ }
+ case 2: // Altitude
+ {
+ uint16_t altitude = *(uint16_t*)ptr;
+ gpx.tracks.back().trackSegs.back().trackPoints[time].altitude = altitude;
+ break;
+ }
+ case 3: // Heart Rate
+ {
+ uint8_t heartRate = *(uint8_t*)ptr;
+ gpx.tracks.back().trackSegs.back().trackPoints[time].heartRate = heartRate;
+ break;
+ }
+ case 4: // Cadence
+ {
+ uint8_t cadence = *(uint8_t*)ptr;
+ gpx.tracks.back().trackSegs.back().trackPoints[time].cadence = cadence;
+ break;
+ }
+ }
+ break;
+ }
+ case 29: // WayPoint
+ {
+ switch(rf.definitionNum)
+ {
+ case 253: // Timestamp
+ {
+ time = *(uint32_t*)ptr;
+ gpx.wayPoints.back().time = time;
+ break;
+ }
+ case 0: // Name
+ {
+ string name = GarminConvert::gString(ptr, 16);
+ gpx.wayPoints.back().name = name;
+ break;
+ }
+ case 1: // Latitude
+ {
+ int32_t latitude = *(int32_t*)ptr;
+ gpx.wayPoints.back().latitude = latitude;
+ break;
+ }
+ case 2: // Longitude
+ {
+ int32_t longitude = *(int32_t*)ptr;
+ gpx.wayPoints.back().longitude = longitude;
+ break;
+ }
+ case 3: // Symbol
+ {
+ break;
+ }
+ case 4: // Altitude
+ {
+ uint16_t altitude = *(uint16_t*)ptr;
+ gpx.wayPoints.back().altitude = altitude;
+ break;
+ }
+ }
+ break;
+ }
+ case 31: // Course
+ {
+ switch(rf.definitionNum)
+ {
+ case 5: // Name
+ {
+ string name("Course_");
+ name += GarminConvert::gString(ptr, 16);
+ gpx.tracks.back().name = name;
+ break;
+ }
+ }
+ break;
+ }
+ }
+
+ ptr += rf.size;
+ bytes -= rf.size;
+ }
+
+ switch(rd.rfx.globalNum)
+ {
+ case 0: // File Id
+ {
+ switch (fileType)
+ {
+ case 4: // Activity
+ {
+ gpx.newTrack(string("Track_") + GarminConvert::localTime(fileCreationTime));
+ break;
+ }
+ case 6: // Course
+ {
+ gpx.newTrack(string("Course_") + GarminConvert::localTime(fileCreationTime));
+ break;
+ }
+ }
+ break;
+ }
+ case 19: // Lap
+ {
+ gpx.newTrackSeg();
+ break;
+ }
+ }
+ }
+ else
+ {
+ logger() << "Undefined Local Message Type: " << (unsigned)rh.normalHeader.localMessageType << "\n";
+ //logFlush();
+
+ return false;
+ }
+ }
+ }
+ else
+ {
+ // Compressed Timestamp Header
+ logger() << "Compressed Timestamp Header:" << endl;
+ logger() << " Local Message Type " << (unsigned)rh.ctsHeader.localMessageType << endl;
+ logger() << " Time Offset " << (unsigned)rh.ctsHeader.timeOffset << "\n";
+ //logFlush();
+ }
+ }
+
+ return true;
+}
+
+bool FIT::parseZeroFile(vector<uint8_t> &data, ZeroFileContent &zeroFileContent)
+{
+ logger() << "Parsing zero file...\n";
+ //logFlush();
+
+ DirectoryHeader directoryHeader;
+ if (data.size() < sizeof(directoryHeader))
+ {
+ logger() << "Zero file data is too short to get header\n";
+ //logFlush();
+
+ return false;
+ }
+
+ memcpy(&directoryHeader, &data.front(), sizeof(directoryHeader));
+ data.erase(data.begin(), data.begin()+sizeof(directoryHeader));
+
+ logger() << "Directory version: " << hex << setw(2) << (unsigned)directoryHeader.version << "\n";
+ //logFlush();
+ logger() << "Structure length: " << dec << (unsigned)directoryHeader.structureLength << "\n";
+ //logFlush();
+ logger() << "Time format: " << dec << (unsigned)directoryHeader.timeFormat << "\n";
+ //logFlush();
+ logger() << "Current system time: " << GarminConvert::localTime(directoryHeader.currentSystemTime) << "\n";
+ //logFlush();
+ logger() << "Directory modified time: " << GarminConvert::localTime(directoryHeader.directoryModifiedTime) << "\n";
+ //logFlush();
+
+ int records = data.size() / directoryHeader.structureLength;
+
+ if(data.empty() || data.size() < (sizeof(ZeroFileRecord)*records))
+ {
+ logger() << "Zero file data is truncated to read...\n";
+ //logFlush();
+
+ return false;
+ }
+
+ logger() << uppercase;
+
+ logger() << "_idx" << "|d" << "ata" << "type|" << "recordType|" << "_rt_" << "++ID++" << "__fileSize|" << "+++++++++++++++++++|" << "flags" << "\n";
+ uint8_t *ptr = &data.front();
+ ZeroFileRecord zfRecord;
+ for (int i=0; i<records; i++)
+ {
+ memcpy(&zfRecord, ptr, sizeof(zfRecord));
+ ptr += sizeof(zfRecord);
+
+ logger() << hex << setw(4) << setfill('0') << (unsigned)zfRecord.index << ": " <<
+ ((zfRecord.fileDataType == 0x80)?"FIT":" ") <<
+ "(" << setw(2) << setfill('0') << (unsigned)zfRecord.fileDataType << ") " <<
+ setw(10) << setfill(' ') << enumMap[MessageFieldTypeFileType][zfRecord.recordType] << " " <<
+ "(" << setw(2) << setfill('0') << (unsigned)zfRecord.recordType << ")" <<
+ "(" << setw(4) << setfill('0') << (unsigned)zfRecord.identifier << ")" <<
+ dec << setw(10) << setfill(' ') << (unsigned)zfRecord.fileSize << " " <<
+ GarminConvert::localTime(zfRecord.timeStamp) << " ";
+ if (zfRecord.generalFileFlags.read) logger() << "[R]";
+ if (zfRecord.generalFileFlags.write) logger() << "[W]";
+ if (zfRecord.generalFileFlags.erase) logger() << "[E]";
+ if (zfRecord.generalFileFlags.append) logger() << "[Ap]";
+ if (zfRecord.generalFileFlags.archive) logger() << "[Ar]";
+ if (zfRecord.generalFileFlags.crypto) logger() << "[C]";
+ logger() << "\n";
+ //logFlush();
+
+ switch(zfRecord.recordType)
+ {
+ case 4: // Activity
+ {
+ zeroFileContent.activityFiles.push_back(zfRecord.index);
+ break;
+ }
+ case 6: // Course
+ {
+ zeroFileContent.courseFiles.push_back(zfRecord.index);
+ break;
+ }
+ case 8: // Waypoints
+ {
+ zeroFileContent.waypointsFiles.push_back(zfRecord.index);
+ break;
+ }
+ }
+ }
+
+ return true;
+}
diff --git a/src/FIT.hpp b/src/FIT.hpp
new file mode 100644
index 0000000..e258259
--- /dev/null
+++ b/src/FIT.hpp
@@ -0,0 +1,297 @@
+// -*- mode: c++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2; coding: utf-8-unix -*-
+/***************************************************************************
+ * Copyright (C) 2010-2012 by Oleg Khudyakov *
+ * prcoder@gmail.com *
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+ * the Free Software Foundation; either version 2 of the License, or *
+ * (at your option) any later version. *
+ * *
+ * This program is distributed in the hope that it will be useful, *
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of *
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
+ * GNU General Public License for more details. *
+ * *
+ * You should have received a copy of the GNU General Public License *
+ * along with this program; if not, write to the *
+ * Free Software Foundation, Inc., *
+ * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. *
+ ***************************************************************************/
+// ***** BEGIN LICENSE BLOCK *****
+////////////////////////////////////////////////////////////////////
+// Copyright (c) 2012-2013 RALOVICH, Kristóf //
+// //
+// This program is free software; you can redistribute it and/or //
+// modify it under the terms of the GNU General Public License //
+// version 2 as published by the Free Software Foundation. //
+// //
+////////////////////////////////////////////////////////////////////
+// ***** END LICENSE BLOCK *****
+#pragma once
+
+#include "GPX.hpp"
+
+#include <stdintfwd.hpp>
+#include <vector>
+#include <string>
+#include <map>
+#include <sstream>
+#include <boost/static_assert.hpp>
+
+
+using namespace std;
+
+
+#pragma pack(1)
+struct FITHeader
+{
+ uint8_t headerSize;
+ uint8_t protocolVersion;
+ uint16_t profileVersion;
+ uint32_t dataSize;
+ uint8_t signature[4];
+ uint16_t headerCRC;
+};
+
+struct RecordNormalHeader
+{
+ uint8_t localMessageType:4;
+ uint8_t reserved:2;
+ uint8_t messageType:1;
+ uint8_t headerType:1;
+};
+
+struct RecordCompressedTimeStampHeader
+{
+ uint8_t timeOffset:5;
+ uint8_t localMessageType:2;
+ uint8_t headerType:1;
+};
+
+union RecordHeader
+{
+ RecordNormalHeader normalHeader;
+ RecordCompressedTimeStampHeader ctsHeader;
+};
+
+struct RecordFixed
+{
+ uint8_t reserved;
+ uint8_t arch;
+ uint16_t globalNum;
+ uint8_t fieldsNum;
+};
+
+struct RecordField
+{
+ uint8_t definitionNum;
+ uint8_t size;
+ uint8_t baseType;
+};
+
+struct BaseTypeBits
+{
+ uint8_t baseTypeNum:5;
+ uint8_t reserved:2;
+ uint8_t endianAbility:1;
+};
+
+union BaseType
+{
+ BaseTypeBits bits;
+ uint8_t byte;
+};
+
+struct ZeroFileRecord
+{
+ uint16_t index;
+ uint8_t fileDataType;
+ uint8_t recordType;
+ uint16_t identifier;
+ uint8_t fileDataTypeFlags;
+ struct
+ {
+ uint8_t reserved:2;
+ uint8_t crypto:1;
+ uint8_t append:1;
+ uint8_t archive:1;
+ uint8_t erase:1;
+ uint8_t write:1;
+ uint8_t read:1;
+ } generalFileFlags;
+ uint32_t fileSize;
+ uint32_t timeStamp;
+};
+
+struct DirectoryHeader
+{
+ uint8_t version;
+ uint8_t structureLength;
+ uint8_t timeFormat;
+ uint8_t reserved[5];
+ uint32_t currentSystemTime;
+ uint32_t directoryModifiedTime;
+};
+BOOST_STATIC_ASSERT(sizeof(DirectoryHeader)==16);
+
+#pragma pack()
+
+enum BaseTypes
+{
+ BT_Enum = 0,
+ BT_Int8,
+ BT_UInt8,
+ BT_Int16,
+ BT_Uint16,
+ BT_Int32,
+ BT_UInt32,
+ BT_String,
+ BT_Float32,
+ BT_Float64,
+ BT_Uint8z,
+ BT_Uint16z,
+ BT_Uint32z,
+ BT_ByteArray
+};
+
+struct RecordDef
+{
+ RecordFixed rfx;
+ vector<RecordField> rf;
+};
+
+enum MessageFieldTypes
+{
+ MessageFieldTypeUnknown = 0,
+ MessageFieldTypeCoord,
+ MessageFieldTypeAltitude,
+ MessageFieldTypeTimestamp,
+ MessageFieldTypeTime,
+ MessageFieldTypeWeight,
+ MessageFieldTypeSpeed,
+ MessageFieldTypeOdometr,
+ MessageFieldTypeFileType,
+ MessageFieldTypeManufacturer,
+ MessageFieldTypeProduct,
+ MessageFieldTypeGender,
+ MessageFieldTypeLanguage,
+ MessageFieldTypeSport,
+ MessageFieldTypeEvent,
+ MessageFieldTypeEventType
+};
+
+enum Manufacturers
+{
+ ManufacturerGarmin = 1,
+ ManufacturerGarminFR405ANTFS,
+ ManufacturerZephyr,
+ ManufacturerDayton,
+ ManufacturerIDT,
+ ManufacturerSRM,
+ ManufacturerQuarq,
+ ManufacturerIBike,
+ ManufacturerSaris,
+ ManufacturerSparkHK,
+ ManufacturerTanita,
+ ManufacturerEchowell,
+ ManufacturerDynastreamOEM,
+ ManufacturerNautilus,
+ ManufacturerDynastream,
+ ManufacturerTimex,
+ ManufacturerMetriGear,
+ ManufacturerXelic,
+ ManufacturerBeurer,
+ ManufacturerCardioSport,
+ ManufacturerAandD,
+ ManufacturerHMM,
+ ManufacturerSuunto,
+ ManufacturerThitaElektronik,
+ ManufacturerGPulse,
+ ManufacturerCleanMobile,
+ ManufacturerPedalBrain,
+ ManufacturerPeaksware,
+ ManufacturerSaxonar,
+ ManufacturerLemondFitness,
+ ManufacturerDexcom,
+ ManufacturerWahooFitness,
+ ManufacturerOctaneFitness,
+ ManufacturerArchinoetics,
+ ManufacturerTheHurtBox,
+ ManufacturerCitizenSystems,
+ ManufacturerUnknown1,
+ ManufacturerOsynce,
+ ManufacturerHolux,
+ ManufacturerConcept2,
+ ManufacturerUnknown2,
+ ManufacturerOneGiantLeap,
+ ManufacturerAceSensor,
+ ManufacturerBrimBrothers,
+ ManufacturerXplova,
+ ManufacturerPerceptionDigital,
+ ManufacturerBF1Systems,
+ ManufacturerPioneer,
+ ManufacturerSpantec,
+ ManufacturerMetalogics,
+ Manufacturer4IIIIS
+};
+
+enum GarminProducts
+{
+ GarminHRM1 = 1,
+ GarminAXH01 = 2,
+ GarminAXB01 = 3,
+ GarminAXB02 = 4,
+ GarminHRM2SS = 5,
+ GarminDsiAlf02 = 6,
+ GarminFR405 = 717,
+ GarminFR50 = 782,
+ GarminFR60 = 988,
+ GarminDsiAlf01 = 1011,
+ GarminFR310XT = 1018,
+ GarminEDGE500 = 1036,
+ GarminFR110 = 1124,
+ GarminEDGE800 = 1169,
+ GarminChirp = 1253,
+ GarminEDGE200 = 1325,
+ GarminFR910XT = 1328,
+ GarminALF04 = 1341,
+ GarminFR610 = 1345,
+ GarminFR70 = 1436,
+ GarminFR310XT4T = 1446,
+ GarminAMX = 1461,
+ GarminSDM4 = 10007,
+ GarminTraningCenter = 20119,
+ GarminConnect = 65534
+};
+
+class ZeroFileContent
+{
+public:
+ vector<uint16_t> activityFiles;
+ vector<uint16_t> waypointsFiles;
+ vector<uint16_t> courseFiles;
+};
+
+class FIT
+{
+public:
+ FIT();
+ ~FIT();
+
+ uint16_t CRC_byte(uint16_t crc, uint8_t byte);
+ string getDataString(uint8_t *ptr, uint8_t size, uint8_t baseType, uint8_t messageType, uint8_t fieldNum);
+ bool parse(vector<uint8_t> &fitData, GPX &gpx);
+ bool parseZeroFile(vector<uint8_t> &data, ZeroFileContent &zeroFileContent);
+
+private:
+ map<uint8_t, string> messageTypeMap;
+ map<uint8_t, map<uint8_t, string> > messageFieldNameMap;
+ map<uint8_t, map<uint8_t, uint8_t> > messageFieldTypeMap;
+ map<uint8_t, string> dataTypeMap;
+ map<uint8_t, map<uint8_t, string> > enumMap;
+ map<uint8_t, string> manufacturerMap;
+ map<uint8_t, map<uint16_t, string> > productMap;
+ int16_t manufacturer;
+};
+
diff --git a/src/GPX.cpp b/src/GPX.cpp
new file mode 100644
index 0000000..4c17149
--- /dev/null
+++ b/src/GPX.cpp
@@ -0,0 +1,221 @@
+// -*- mode: c++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2; coding: utf-8-unix -*-
+/***************************************************************************
+ * Copyright (C) 2010-2012 by Oleg Khudyakov *
+ * prcoder@gmail.com *
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+ * the Free Software Foundation; either version 2 of the License, or *
+ * (at your option) any later version. *
+ * *
+ * This program is distributed in the hope that it will be useful, *
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of *
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
+ * GNU General Public License for more details. *
+ * *
+ * You should have received a copy of the GNU General Public License *
+ * along with this program; if not, write to the *
+ * Free Software Foundation, Inc., *
+ * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. *
+ ***************************************************************************/
+// ***** BEGIN LICENSE BLOCK *****
+////////////////////////////////////////////////////////////////////
+// Copyright (c) 2012-2013 RALOVICH, Kristóf //
+// //
+// This program is free software; you can redistribute it and/or //
+// modify it under the terms of the GNU General Public License //
+// version 2 as published by the Free Software Foundation. //
+// //
+////////////////////////////////////////////////////////////////////
+// ***** END LICENSE BLOCK *****
+
+#include "GPX.hpp"
+#include <iostream>
+#include <iomanip>
+#include "common.hpp"
+
+WayPoint::WayPoint():
+ time(0),
+ latitude(INT32_MAX),
+ longitude(INT32_MAX),
+ altitude(UINT16_MAX)
+{
+}
+
+WayPoint::~WayPoint()
+{
+}
+
+void WayPoint::putToFile(ofstream &file)
+{
+ if ((latitude != INT32_MAX) && (longitude != INT32_MAX))
+ {
+ file << " <wpt lat=\"" << setprecision(5) << GarminConvert::coord(latitude) << "\" lon=\"" << setprecision(5) << GarminConvert::coord(longitude) << "\">" << endl;
+ file << " <name>" << name << "</name>" << endl;
+ if (altitude != UINT16_MAX)
+ {
+ file << " <ele>" << setprecision(1) << GarminConvert::altitude(altitude) << "</ele>" << endl;
+ }
+ file << " <time>" << GarminConvert::gmTime(time) << "</time>" << endl;
+ file << " <sym>city (small)</sym>" << endl;
+ file << " </wpt>" << endl;
+ }
+}
+
+TrackPoint::TrackPoint()
+{
+ time = 0;
+ latitude = INT32_MAX;
+ longitude = INT32_MAX;
+ altitude = UINT16_MAX;
+ heartRate = UINT8_MAX;
+ cadence = UINT8_MAX;
+
+}
+
+TrackPoint::~TrackPoint()
+{
+}
+
+void TrackPoint::putToFile(ofstream &file)
+{
+ if ((latitude != INT32_MAX) && (longitude != INT32_MAX))
+ {
+ file << " <trkpt lat=\"" << setprecision(5) << GarminConvert::coord(latitude) << "\" lon=\"" << setprecision(5) << GarminConvert::coord(longitude) << "\">" << endl;
+ if (altitude != UINT16_MAX)
+ {
+ file << " <ele>" << setprecision(1) << GarminConvert::altitude(altitude) << "</ele>" << endl;
+ }
+ file << " <time>" << GarminConvert::gmTime(time) << "</time>" << endl;
+ if ((heartRate != UINT8_MAX) || (cadence != UINT8_MAX))
+ {
+ file << " <extensions>" << endl;
+ file << " <gpxtpx:TrackPointExtension>" << endl;
+ if (heartRate != UINT8_MAX)
+ {
+ file << " <gpxtpx:hr>" << dec << (unsigned)heartRate << "</gpxtpx:hr>" << endl;
+ }
+ if (cadence != UINT8_MAX)
+ {
+ file << " <gpxtpx:cad>" << dec << (unsigned)cadence << "</gpxtpx:cad>" << endl;
+ }
+ file << " </gpxtpx:TrackPointExtension>" << endl;
+ file << " </extensions>" << endl;
+ }
+ file << " </trkpt>" << endl;
+ }
+}
+
+TrackSeg::TrackSeg()
+{
+}
+
+TrackSeg::~TrackSeg()
+{
+}
+
+void TrackSeg::putToFile(ofstream &file)
+{
+ if (trackPoints.size())
+ {
+ file << " <trkseg>" << endl;
+ map<uint32_t,TrackPoint>::iterator it;
+ for (it = trackPoints.begin(); it != trackPoints.end(); it++)
+ {
+ TrackPoint &trackPoint = it->second;
+ trackPoint.putToFile(file);
+ }
+ file << " </trkseg>" << endl;
+ }
+}
+
+
+Track::Track(string &p_name) : name(p_name)
+{
+}
+
+Track::~Track()
+{
+}
+
+void Track::newTrackSeg()
+{
+ TrackSeg trackSeg;
+ trackSegs.push_back(trackSeg);
+}
+
+void Track::putToFile(ofstream &file)
+{
+ file << "<trk>" << endl;
+ file << " <name>" << name << "</name>" << endl;
+
+ for (size_t i=0; i<trackSegs.size(); i++)
+ {
+ trackSegs[i].putToFile(file);
+ }
+
+ file << "</trk>" << endl;
+}
+
+GPX::GPX()
+{
+}
+
+GPX::~GPX()
+{
+}
+
+void GPX::newTrack(string name)
+{
+ Track track(name);
+ tracks.push_back(track);
+ newTrackSeg();
+}
+
+void GPX::newTrackSeg()
+{
+ tracks.back().newTrackSeg();
+}
+
+void GPX::newWayPoint()
+{
+ WayPoint wayPoint;
+ wayPoints.push_back(wayPoint);
+}
+
+bool GPX::writeToFile(string fileName)
+{
+ ofstream file(fileName.c_str());
+ if (!file.is_open())
+ {
+ cerr << "Error writing to file '" << fileName << "'" << endl;
+ return false;
+ }
+
+ file.setf(ios::fixed,ios::floatfield);
+
+ file << "<?xml version=\"1.0\"?>" << endl;
+ file << "<gpx version=\"1.1\" creator=\"" APP_NAME "\"" << endl;
+ file << "xsi:schemaLocation=\"http://www.topografix.com/GPX/1/1 http://www.topografix.com/GPX/1/1/gpx.xsd "\
+ "http://www.garmin.com/xmlschemas/GpxExtensions/v3 http://www.garmin.com/xmlschemas/GpxExtensionsv3.xsd "\
+ "http://www.garmin.com/xmlschemas/TrackPointExtension/v1 http://www.garmin.com/xmlschemas/TrackPointExtensionv1.xsd\"" << endl;
+ file << "xmlns=\"http://www.topografix.com/GPX/1/1\"" << endl;
+ file << "xmlns:gpxtpx=\"http://www.garmin.com/xmlschemas/TrackPointExtension/v1\"" << endl;
+ file << "xmlns:gpxx=\"http://www.garmin.com/xmlschemas/GpxExtensions/v3\"" << endl;
+ file << "xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\">" << endl;
+
+ for (size_t i=0; i<wayPoints.size(); i++)
+ {
+ wayPoints[i].putToFile(file);
+ }
+
+ for (size_t i=0; i<tracks.size(); i++)
+ {
+ tracks[i].putToFile(file);
+ }
+
+ file << "</gpx>" << endl;
+ file.close();
+
+ return true;
+}
diff --git a/src/GPX.hpp b/src/GPX.hpp
new file mode 100644
index 0000000..33bc5aa
--- /dev/null
+++ b/src/GPX.hpp
@@ -0,0 +1,127 @@
+// -*- mode: c++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2; coding: utf-8-unix -*-
+/***************************************************************************
+ * Copyright (C) 2010-2012 by Oleg Khudyakov *
+ * prcoder@gmail.com *
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+ * the Free Software Foundation; either version 2 of the License, or *
+ * (at your option) any later version. *
+ * *
+ * This program is distributed in the hope that it will be useful, *
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of *
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
+ * GNU General Public License for more details. *
+ * *
+ * You should have received a copy of the GNU General Public License *
+ * along with this program; if not, write to the *
+ * Free Software Foundation, Inc., *
+ * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. *
+ ***************************************************************************/
+// ***** BEGIN LICENSE BLOCK *****
+////////////////////////////////////////////////////////////////////
+// Copyright (c) 2012-2013 RALOVICH, Kristóf //
+// //
+// This program is free software; you can redistribute it and/or //
+// modify it under the terms of the GNU General Public License //
+// version 2 as published by the Free Software Foundation. //
+// //
+////////////////////////////////////////////////////////////////////
+// ***** END LICENSE BLOCK *****
+
+#pragma once
+
+#include "GarminConvert.hpp"
+#include <vector>
+#include <string>
+#include <fstream>
+#include <map>
+#include "stdintfwd.hpp"
+
+//#ifndef _MSC_VER
+#define INT8_MAX 0x7F
+#define UINT8_MAX 0xFF
+#define INT16_MAX 0x7FFF
+#define UINT16_MAX 0xFFFF
+#define INT32_MAX 0x7FFFFFFF
+#define UINT32_MAX 0xFFFFFFFF
+//#endif
+
+using namespace std;
+
+class WayPoint
+{
+public:
+ WayPoint();
+ ~WayPoint();
+
+ void putToFile(ofstream &file);
+
+public:
+ string name;
+ uint32_t time;
+ int32_t latitude;
+ int32_t longitude;
+ uint16_t altitude;
+};
+
+class TrackPoint
+{
+public:
+ TrackPoint();
+ ~TrackPoint();
+
+ void putToFile(ofstream &file);
+
+public:
+ uint32_t time;
+ int32_t latitude;
+ int32_t longitude;
+ uint16_t altitude;
+ uint8_t heartRate;
+ uint8_t cadence;
+};
+
+class TrackSeg
+{
+public:
+ TrackSeg();
+ ~TrackSeg();
+
+ void putToFile(ofstream &file);
+
+public:
+ map<uint32_t,TrackPoint> trackPoints;
+};
+
+class Track
+{
+public:
+ Track(string &name);
+ ~Track();
+
+ void newTrackSeg();
+ void putToFile(ofstream &file);
+
+public:
+ string name;
+ vector<TrackSeg> trackSegs;
+};
+
+class GPX
+{
+public:
+ GPX();
+ ~GPX();
+
+ void newTrack(string name);
+ void newTrackSeg();
+ void newWayPoint();
+
+ bool writeToFile(string fileName);
+
+public:
+ vector<WayPoint> wayPoints;
+ vector<Track> tracks;
+};
+
diff --git a/src/GarminConvert.cpp b/src/GarminConvert.cpp
new file mode 100644
index 0000000..7554b91
--- /dev/null
+++ b/src/GarminConvert.cpp
@@ -0,0 +1,162 @@
+// -*- mode: c++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2; coding: utf-8-unix -*-
+/***************************************************************************
+ * Copyright (C) 2010-2012 by Oleg Khudyakov *
+ * prcoder@gmail.com *
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+ * the Free Software Foundation; either version 2 of the License, or *
+ * (at your option) any later version. *
+ * *
+ * This program is distributed in the hope that it will be useful, *
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of *
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
+ * GNU General Public License for more details. *
+ * *
+ * You should have received a copy of the GNU General Public License *
+ * along with this program; if not, write to the *
+ * Free Software Foundation, Inc., *
+ * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. *
+ ***************************************************************************/
+// ***** BEGIN LICENSE BLOCK *****
+////////////////////////////////////////////////////////////////////
+// Copyright (c) 2012-2013 RALOVICH, Kristóf //
+// //
+// This program is free software; you can redistribute it and/or //
+// modify it under the terms of the GNU General Public License //
+// version 2 as published by the Free Software Foundation. //
+// //
+////////////////////////////////////////////////////////////////////
+// ***** END LICENSE BLOCK *****
+
+#include "GarminConvert.hpp"
+#include <stdio.h>
+#include <time.h>
+#include <iostream>
+#include <sstream>
+#include <iomanip>
+#include <ctype.h>
+
+double GarminConvert::coord(int32_t coord)
+{
+ double rv = coord;
+ rv *= 180;
+ rv /= 0x80000000;
+
+ return rv;
+}
+
+double GarminConvert::altitude(uint16_t alt)
+{
+ double rv = alt;
+ rv /= 5;
+ rv -= 500;
+
+ return rv;
+}
+
+double GarminConvert::length(uint32_t centimeters)
+{
+ return (double)centimeters / 100;
+}
+
+double GarminConvert::speed(uint16_t speed)
+{
+ return ((double)speed * 0.0036);
+}
+
+double GarminConvert::weight(uint16_t weight)
+{
+ return ((double)weight / 10);
+}
+
+string GarminConvert::gmTime(uint32_t time)
+{
+ time_t t = time;
+ t += GARMIN_EPOCH; // Garmin epoch offset
+ char tbuf[256];
+ strftime(tbuf, sizeof(tbuf), "%Y-%m-%dT%H:%M:%SZ", gmtime(&t));
+
+ return tbuf;
+}
+
+string GarminConvert::localTime(uint32_t time)
+{
+ time_t t = time;
+ t += GARMIN_EPOCH; // Garmin epoch offset
+ char tbuf[256];
+ strftime(tbuf, sizeof(tbuf), "%d-%m-%Y %H:%M:%S", localtime(&t));
+
+ return tbuf;
+}
+
+string GarminConvert::gTime(uint32_t time)
+{
+ unsigned thousandths = time % 1000;
+ time /= 1000;
+ unsigned seconds = time % 60;
+ time /= 60;
+ unsigned minutes = time % 60;
+ time /= 60;
+ unsigned hours = time;
+
+ ostringstream sstr;
+ sstr << dec << setw(2) << setfill('0');
+ sstr << hours << ":" << minutes << ":" << seconds << "." << setw(3) << thousandths;
+
+ return sstr.str();
+}
+
+string GarminConvert::gString(uint8_t *str, int maxSize)
+{
+ string rv;
+ for(int i=0; i<maxSize; i++)
+ {
+ if (str[i])
+ {
+ rv += str[i];
+ }
+ else
+ {
+ break;
+ }
+ }
+
+ return rv;
+}
+
+string GarminConvert::gHex(uint8_t *buf, int size)
+{
+ ostringstream sstr;
+ sstr << uppercase << setw(2) << setfill('0');
+
+ for(int i=0; i<size; i++)
+ {
+ sstr << hex << setw(2) << (unsigned)buf[i];
+ if (i < size-1)
+ {
+ sstr << " ";
+ }
+ }
+
+ return sstr.str();
+}
+
+string GarminConvert::gHex(vector<uint8_t> &buf)
+{
+ return gHex(&buf[0], buf.size());
+}
+
+string GarminConvert::hexDump(vector<uint8_t> &buf)
+{
+ ostringstream sstr;
+
+ sstr << gHex(buf) << " ";
+
+ for(size_t i=0; i<buf.size(); i++)
+ {
+ sstr << (char)(isprint(buf[i])?buf[i]:'.');
+ }
+
+ return sstr.str();
+}
diff --git a/src/GarminConvert.hpp b/src/GarminConvert.hpp
new file mode 100644
index 0000000..a0b17fe
--- /dev/null
+++ b/src/GarminConvert.hpp
@@ -0,0 +1,58 @@
+// -*- mode: c++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2; coding: utf-8-unix -*-
+/***************************************************************************
+ * Copyright (C) 2010 by Oleg Khudyakov *
+ * prcoder@gmail.com *
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+ * the Free Software Foundation; either version 2 of the License, or *
+ * (at your option) any later version. *
+ * *
+ * This program is distributed in the hope that it will be useful, *
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of *
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
+ * GNU General Public License for more details. *
+ * *
+ * You should have received a copy of the GNU General Public License *
+ * along with this program; if not, write to the *
+ * Free Software Foundation, Inc., *
+ * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. *
+ ***************************************************************************/
+// ***** BEGIN LICENSE BLOCK *****
+////////////////////////////////////////////////////////////////////
+// Copyright (c) 2012-2013 RALOVICH, Kristóf //
+// //
+// This program is free software; you can redistribute it and/or //
+// modify it under the terms of the GNU General Public License //
+// version 2 as published by the Free Software Foundation. //
+// //
+////////////////////////////////////////////////////////////////////
+// ***** END LICENSE BLOCK *****
+#pragma once
+
+
+#include <stdintfwd.hpp>
+#include <string>
+#include <vector>
+
+#define GARMIN_EPOCH 631065600
+
+using namespace std;
+
+class GarminConvert
+{
+public:
+ static double coord(int32_t coord);
+ static double altitude(uint16_t alt);
+ static double length(uint32_t centimeters);
+ static double speed(uint16_t speed);
+ static double weight(uint16_t weight);
+ static string gmTime(uint32_t time);
+ static string localTime(uint32_t time);
+ static string gTime(uint32_t time);
+ static string gString(uint8_t *str, int maxSize);
+ static string gHex(uint8_t *buf, int size);
+ static string gHex(vector<uint8_t> &buf);
+ static string hexDump(vector<uint8_t> &buf);
+};
+
diff --git a/src/Serial.hpp b/src/Serial.hpp
new file mode 100644
index 0000000..5c7810c
--- /dev/null
+++ b/src/Serial.hpp
@@ -0,0 +1,32 @@
+// -*- mode: c++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2; coding: utf-8-unix -*-
+// ***** BEGIN LICENSE BLOCK *****
+////////////////////////////////////////////////////////////////////
+// Copyright (c) 2011-2013 RALOVICH, Kristóf //
+// //
+// This program is free software; you can redistribute it and/or //
+// modify it under the terms of the GNU General Public License //
+// version 2 as published by the Free Software Foundation. //
+// //
+////////////////////////////////////////////////////////////////////
+// ***** END LICENSE BLOCK *****
+#pragma once
+
+
+
+// Abstract interface for serial-like communication.
+class Serial
+{
+public:
+ virtual ~Serial() {}
+ virtual bool open() = 0;
+ virtual void close() = 0;
+ virtual bool read(char* dst, const size_t sizeBytes, size_t& bytesRead) = 0;
+ virtual bool readBlocking(char* dst, const size_t sizeBytes, size_t& bytesRead) = 0;
+ // synchronous data writing, blocks until sent
+ virtual bool write(const char* src, const size_t sizeBytes, size_t& bytesWritten) = 0;
+ //virtual void wait() = 0;
+ /// number of raw bytes in the receive queue
+ virtual const size_t getQueueLength() const = 0;
+ virtual const char* getImplName() = 0;
+ virtual bool isOpen() const = 0;
+};
diff --git a/src/SerialTty.cpp b/src/SerialTty.cpp
new file mode 100644
index 0000000..6d7cd83
--- /dev/null
+++ b/src/SerialTty.cpp
@@ -0,0 +1,313 @@
+// -*- mode: c++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2; coding: utf-8-unix -*-
+// ***** BEGIN LICENSE BLOCK *****
+////////////////////////////////////////////////////////////////////
+// Copyright (c) 2011-2013 RALOVICH, Kristóf //
+// //
+// This program is free software; you can redistribute it and/or //
+// modify it under the terms of the GNU General Public License //
+// version 2 as published by the Free Software Foundation. //
+// //
+////////////////////////////////////////////////////////////////////
+// ***** END LICENSE BLOCK *****
+
+#include "SerialTty.hpp"
+#include "antdefs.hpp"
+#include <unistd.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#include <stdio.h>
+#include <string.h>
+#include <termios.h>
+#include <stdlib.h>
+#include <signal.h>
+
+#include <functional>
+#include <tr1/functional>
+#include <algorithm>
+#include <vector>
+#include <string>
+#include <boost/thread/thread_time.hpp>
+
+
+struct SerialTtyPrivate
+{
+ std::string m_devName;
+ int m_fd;
+ boost::thread m_recvTh;
+ mutable boost::mutex m_queueMtx;
+ boost::condition_variable m_condQueue;
+ std::queue<char> m_recvQueue;
+ //lqueue<uchar> m_recvQueue2;
+ volatile int m_recvThKill;
+ //AntCallback* m_callback;
+};
+
+// runs in other thread
+struct SerialTtyIOThread
+{
+ void operator() (SerialTty* arg)
+ {
+ //printf("recvFunc, arg: %p\n", arg); fflush(stdout);
+ if(!arg)
+ {
+ rv=0;
+ return;
+ }
+ SerialTty* This = reinterpret_cast<SerialTty*>(arg);
+ //printf("recvFunc, This: %p\n", This); fflush(stdout);
+ rv = This->ioHandler();
+ }
+ void* rv;
+};
+
+
+SerialTty::SerialTty()
+ : m_p(new SerialTtyPrivate())
+{
+ m_p->m_devName = "/dev/ttyUSB0";
+ m_p->m_fd = -1;
+ //, m_p->m_recvTh = 0;
+ m_p->m_recvThKill = 0;
+}
+
+SerialTty::~SerialTty()
+{
+ //m_callback = 0;
+ //printf("%s\n", __FUNCTION__);
+ //printf("\n\nboost\n\n");
+}
+
+#define ENSURE_OR_RETURN_FALSE(e) do { if(-1 == (e)) {perror(#e); return false;} } while(false)
+
+
+bool
+SerialTty::open()
+{
+ close();
+
+ bool rv = false;
+
+ m_p->m_fd = ::open(m_p->m_devName.c_str(), O_RDWR | O_NONBLOCK);
+ if(m_p->m_fd < 1)
+ {
+ m_p->m_devName = "/dev/ttyUSB1";
+ m_p->m_fd = ::open(m_p->m_devName.c_str(), O_RDWR | O_NONBLOCK);
+ }
+
+ ENSURE_OR_RETURN_FALSE(m_p->m_fd);
+ if(m_p->m_fd<0)
+ return rv;
+
+ //printf("m_fd=%d\n", m_fd);
+
+ struct termios tp;
+ ENSURE_OR_RETURN_FALSE(tcgetattr(m_p->m_fd, &tp));
+ tp.c_iflag &=
+ ~(IGNBRK|BRKINT|PARMRK|ISTRIP|INLCR|IGNCR|ICRNL|IXON|IXOFF|IXANY|INPCK|IUCLC);
+ tp.c_oflag &= ~OPOST;
+ tp.c_lflag &= ~(ECHO|ECHONL|ICANON|ISIG|IEXTEN|ECHOE);
+ tp.c_cflag &= ~(CSIZE|PARENB);
+ tp.c_cflag |= CS8 | CLOCAL | CREAD | CRTSCTS;
+
+ ENSURE_OR_RETURN_FALSE(cfsetispeed(&tp, B115200));
+ ENSURE_OR_RETURN_FALSE(cfsetospeed(&tp, B115200));
+ tp.c_cc[VMIN] = 1;
+ tp.c_cc[VTIME] = 0;
+ ENSURE_OR_RETURN_FALSE(tcsetattr(m_p->m_fd, TCSANOW, &tp));
+
+ m_p->m_recvThKill = 0;
+ SerialTtyIOThread recTh;
+ m_p->m_recvTh = boost::thread(recTh, this);
+
+ return true;
+}
+
+
+
+void
+SerialTty::close()
+{
+ m_p->m_recvThKill = 1;
+
+ m_p->m_condQueue.notify_all();
+
+ m_p->m_recvTh.join();
+
+ if(m_p->m_fd >= 0)
+ {
+ ::close(m_p->m_fd);
+ }
+ m_p->m_fd = -1;
+}
+
+
+
+//bool
+//AntTtyHandler2::read(char& c)
+//{
+// boost::unique_lock<boost::mutex> lock(m_queueMtx);
+//
+// if(m_recvQueue.empty())
+// return false;
+//
+// c = m_recvQueue.front();
+// m_recvQueue.pop();
+// return true;
+//}
+
+
+bool
+SerialTty::read(char* dst, const size_t sizeBytes, size_t& bytesRead)
+{
+ if(!dst)
+ return false;
+
+ boost::unique_lock<boost::mutex> lock(m_p->m_queueMtx);
+
+ size_t s = m_p->m_recvQueue.size();
+ s = std::min(s, sizeBytes);
+ for(size_t i = 0; i < s; i++)
+ {
+ dst[i] = m_p->m_recvQueue.front();
+ m_p->m_recvQueue.pop();
+ }
+ bytesRead = s;
+
+ if(bytesRead==0)
+ return false;
+
+ return true;
+}
+
+
+bool
+SerialTty::readBlocking(char* dst, const size_t sizeBytes, size_t& bytesRead)
+{
+ if(!dst)
+ return false;
+
+ const size_t timeout_ms = 1000;
+ {
+ boost::unique_lock<boost::mutex> lock(m_p->m_queueMtx);
+
+ //while(m_recvQueue.empty()) // while - to guard agains spurious wakeups
+ {
+ m_p->m_condQueue.timed_wait(lock, boost::posix_time::milliseconds(timeout_ms));
+ }
+ size_t s = m_p->m_recvQueue.size();
+ s = std::min(s, sizeBytes);
+ for(size_t i = 0; i < s; i++)
+ {
+ dst[i] = m_p->m_recvQueue.front();
+ m_p->m_recvQueue.pop();
+ }
+ bytesRead = s;
+ }
+
+ if(bytesRead==0)
+ return false;
+
+ return true;
+}
+
+
+bool
+SerialTty::write(const char* src, const size_t sizeBytes, size_t& bytesWritten)
+{
+ if(m_p->m_fd<0)
+ return false;
+ ssize_t written = ::write(m_p->m_fd, src, sizeBytes);
+ ENSURE_OR_RETURN_FALSE(written);
+ bytesWritten = written;
+
+ return true;
+}
+
+
+//void
+//AntTtyHandler2::wait()
+//{
+//// boost::unique_lock<boost::mutex> lock(m_queueMtx);
+//// m_pushEvent.wait(lock);
+
+////// if(m_callback)
+////// m_callback->onAntReceived();
+//}
+
+
+// Called inside other thread, wait's on the serial line, and extracts bytes as they arrive.
+// The received bytes are queued.
+void*
+SerialTty::ioHandler()
+{
+ fd_set readfds, writefds, exceptfds;
+ int ready;
+ struct timeval to;
+ //printf("%s\n", __FUNCTION__);
+
+ for(;;)
+ {
+ if(m_p->m_recvThKill)
+ return NULL;
+ //printf("%s\n", __FUNCTION__);
+ FD_ZERO(&readfds);
+ FD_ZERO(&writefds);
+ FD_ZERO(&exceptfds);
+ FD_SET(m_p->m_fd, &readfds);
+ to.tv_sec = 1;
+ to.tv_usec = 0;
+ ready = select(m_p->m_fd+1, &readfds, &writefds, &exceptfds, &to);
+ //printf("select: %d\n", ready);
+ if (ready>0) {
+ queueData();
+ }
+ }
+ return NULL;
+}
+
+const size_t SerialTty::getQueueLength() const
+{
+ size_t len=0;
+ boost::unique_lock<boost::mutex> lock(m_p->m_queueMtx);
+ len += m_p->m_recvQueue.size();
+ return len;
+}
+
+bool
+SerialTty::isOpen() const
+{
+ // TODO: is thread running too??
+ // TODO: return fcntl(fd, F_GETFL) != -1 || errno != EBADF;
+ return !(m_p->m_fd<1) && 1;
+}
+
+
+// called from other thread
+void
+SerialTty::queueData()
+{
+ //printf("queueData\n");
+ unsigned char buf[256];
+ ssize_t rb = ::read(m_p->m_fd, buf, sizeof(buf));
+ if(rb < 0)
+ {
+ perror("read");
+ //switch(errno)
+ //{}
+ }
+ else if(rb == 0) // EOF, ??
+ {}
+ else
+ {
+ boost::unique_lock<boost::mutex> lock(m_p->m_queueMtx);
+ for(ssize_t i = 0; i < rb; i++)
+ m_p->m_recvQueue.push(buf[i]);
+ m_p->m_condQueue.notify_one();
+ }
+}
+
+
+
+
+
diff --git a/src/SerialTty.hpp b/src/SerialTty.hpp
new file mode 100644
index 0000000..fae466f
--- /dev/null
+++ b/src/SerialTty.hpp
@@ -0,0 +1,58 @@
+// -*- mode: c++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2; coding: utf-8-unix -*-
+// ***** BEGIN LICENSE BLOCK *****
+////////////////////////////////////////////////////////////////////
+// Copyright (c) 2011-2013 RALOVICH, Kristóf //
+// //
+// This program is free software; you can redistribute it and/or //
+// modify it under the terms of the GNU General Public License //
+// version 2 as published by the Free Software Foundation. //
+// //
+////////////////////////////////////////////////////////////////////
+// ***** END LICENSE BLOCK *****
+#pragma once
+
+//#include <pthread.h>
+#include <queue>
+#include <boost/thread.hpp>
+#include "antdefs.hpp"
+#include "lqueue.hpp"
+#include <list>
+#include "Serial.hpp"
+
+struct SerialTtyPrivate;
+struct SerialTtyIOThread;
+// Serial communication over a POSIX serial port.
+class SerialTty : public Serial
+{
+public:
+ SerialTty();
+ virtual ~SerialTty();
+
+ virtual bool open();
+ virtual void close();
+
+ //virtual bool read(char& c);
+ virtual bool read(char* dst, const size_t sizeBytes, size_t& bytesRead);
+ virtual bool readBlocking(char* dst, const size_t sizeBytes, size_t& bytesRead);
+ virtual bool write(const char* src, const size_t sizeBytes, size_t& bytesWritten);
+
+ //virtual void wait();
+
+private:
+ friend struct SerialTtyIOThread;
+ void* ioHandler();
+
+public:
+ virtual const size_t getQueueLength() const;
+
+ virtual const char* getImplName() { return "AntTtyHandler2"; }
+
+ virtual bool isOpen() const;
+
+private:
+ void queueData();
+
+private:
+ std::auto_ptr<SerialTtyPrivate> m_p;
+};
+
diff --git a/src/SerialUsb.cpp b/src/SerialUsb.cpp
new file mode 100644
index 0000000..fdacac0
--- /dev/null
+++ b/src/SerialUsb.cpp
@@ -0,0 +1,594 @@
+// -*- mode: c++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2; coding: utf-8-unix -*-
+/*
+ * Portions copyright as:
+ * Silicon Laboratories CP2101/CP2102/CP2103 USB to RS232 serial adaptor driver
+ *
+ * Copyright (C) 2010 Craig Shelley (craig@microtron.org.uk)
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License version
+ * 2 as published by the Free Software Foundation.
+ *
+ * Support to set flow control line levels using TIOCMGET and TIOCMSET
+ * thanks to Karl Hiramoto karl@hiramoto.org. RTSCTS hardware flow
+ * control thanks to Munir Nassar nassarmu@real-time.com
+ * Original backport to 2.4 by andreas 'randy' weinberger randy@ebv.com
+ * Several fixes and enhancements by Bill Pfutzenreuter BPfutzenreuter@itsgames.com
+ * Ported to userspace by Kristof Ralovich
+ *
+ */
+// ***** BEGIN LICENSE BLOCK *****
+////////////////////////////////////////////////////////////////////
+// Copyright (c) 2011-2013 RALOVICH, Kristóf //
+// //
+// This program is free software; you can redistribute it and/or //
+// modify it under the terms of the GNU General Public License //
+// version 2 as published by the Free Software Foundation. //
+// //
+////////////////////////////////////////////////////////////////////
+// ***** END LICENSE BLOCK *****
+
+
+
+#include "SerialUsb.hpp"
+#include "antdefs.hpp"
+#include "common.hpp"
+#include <sys/types.h>
+#include <sys/stat.h>
+//#include <fcntl.h>
+#include <stdio.h>
+#include <string.h>
+//#include <pthread.h>
+//#include <termios.h>
+#include <stdlib.h>
+#include <signal.h>
+
+#include <functional>
+//#include <tr1/functional>
+#include <algorithm>
+#include <vector>
+#include <string>
+#include <boost/thread/thread_time.hpp>
+#include <iostream>
+
+#ifdef _WIN32
+#define VC_EXTRALEAN 1
+#define NOMINMAX 1
+#include "lusb0_usb.h"
+#else
+#include "usb.h"
+#endif
+
+const uchar USB_ANT_CONFIGURATION = 1;
+const uchar USB_ANT_INTERFACE = 0;
+const uchar USB_ANT_EP_IN = 0x81;
+const uchar USB_ANT_EP_OUT = 0x01;
+
+
+
+
+
+
+
+struct SerialUsbPrivate
+{
+ boost::thread m_recvTh;
+ mutable boost::mutex m_queueMtx;
+ boost::condition_variable m_condQueue;
+ std::queue<char> m_recvQueue;
+ //lqueue<uchar> m_recvQueue2;
+ volatile int m_recvThKill;
+ //AntCallback* m_callback;
+ usb_dev_handle* dev;
+
+#define REQTYPE_HOST_TO_INTERFACE 0x41
+#define REQTYPE_INTERFACE_TO_HOST 0xc1
+#define REQTYPE_HOST_TO_DEVICE 0x40
+#define REQTYPE_DEVICE_TO_HOST 0xc0
+
+#define USB_CTRL_GET_TIMEOUT 5000
+#define USB_CTRL_SET_TIMEOUT 5000
+
+ /* Config request codes */
+ #define CP210X_IFC_ENABLE 0x00
+ #define CP210X_SET_BAUDDIV 0x01
+ #define CP210X_GET_BAUDDIV 0x02
+ #define CP210X_SET_LINE_CTL 0x03
+ #define CP210X_GET_LINE_CTL 0x04
+ #define CP210X_SET_BREAK 0x05
+ #define CP210X_IMM_CHAR 0x06
+ #define CP210X_SET_MHS 0x07
+ #define CP210X_GET_MDMSTS 0x08
+ #define CP210X_SET_XON 0x09
+ #define CP210X_SET_XOFF 0x0A
+ #define CP210X_SET_EVENTMASK 0x0B
+ #define CP210X_GET_EVENTMASK 0x0C
+ #define CP210X_SET_CHAR 0x0D
+ #define CP210X_GET_CHARS 0x0E
+ #define CP210X_GET_PROPS 0x0F
+ #define CP210X_GET_COMM_STATUS 0x10
+ #define CP210X_RESET 0x11
+ #define CP210X_PURGE 0x12
+ #define CP210X_SET_FLOW 0x13
+ #define CP210X_GET_FLOW 0x14
+ #define CP210X_EMBED_EVENTS 0x15
+ #define CP210X_GET_EVENTSTATE 0x16
+ #define CP210X_SET_CHARS 0x19
+ #define CP210X_GET_BAUDRATE 0x1D
+ #define CP210X_SET_BAUDRATE 0x1E
+
+ /* CP210X_IFC_ENABLE */
+ #define UART_ENABLE 0x0001
+ #define UART_DISABLE 0x0000
+
+ /* CP210X_(SET|GET)_BAUDDIV */
+ #define BAUD_RATE_GEN_FREQ 0x384000
+
+ /* CP210X_(SET|GET)_LINE_CTL */
+ #define BITS_DATA_MASK 0X0f00
+ #define BITS_DATA_5 0X0500
+ #define BITS_DATA_6 0X0600
+ #define BITS_DATA_7 0X0700
+ #define BITS_DATA_8 0X0800
+ #define BITS_DATA_9 0X0900
+
+ #define BITS_PARITY_MASK 0x00f0
+ #define BITS_PARITY_NONE 0x0000
+ #define BITS_PARITY_ODD 0x0010
+ #define BITS_PARITY_EVEN 0x0020
+ #define BITS_PARITY_MARK 0x0030
+ #define BITS_PARITY_SPACE 0x0040
+
+ #define BITS_STOP_MASK 0x000f
+ #define BITS_STOP_1 0x0000
+ #define BITS_STOP_1_5 0x0001
+ #define BITS_STOP_2 0x0002
+
+ /* CP210X_SET_BREAK */
+ #define BREAK_ON 0x0001
+ #define BREAK_OFF 0x0000
+
+ /* CP210X_(SET_MHS|GET_MDMSTS) */
+ #define CONTROL_DTR 0x0001
+ #define CONTROL_RTS 0x0002
+ #define CONTROL_CTS 0x0010
+ #define CONTROL_DSR 0x0020
+ #define CONTROL_RING 0x0040
+ #define CONTROL_DCD 0x0080
+ #define CONTROL_WRITE_DTR 0x0100
+ #define CONTROL_WRITE_RTS 0x0200
+
+
+#define TIOCM_DTR 0x002
+#define TIOCM_RTS 0x004
+
+ int get_config(int request, char* data, int size)
+ {
+ if(!dev)
+ return -1111;
+
+ int index = 0; // bInterfaceNumber ==? USB_ANT_INTERFACE
+
+ int irv;
+ irv = usb_control_msg(dev, REQTYPE_INTERFACE_TO_HOST, request, 0,
+ index, data, size, USB_CTRL_GET_TIMEOUT);
+ LOG_VAR(irv);
+
+ return irv;
+ }
+
+ // size in bytes
+ int set_config(int request, char* data, int size)
+ {
+ if(!dev)
+ return -1111;
+
+ int index = 0; // bInterfaceNumber ==? USB_ANT_INTERFACE
+
+ int irv;
+ if(size>2)
+ {
+ irv = usb_control_msg(dev, REQTYPE_HOST_TO_INTERFACE, request, 0,
+ index, data, size, USB_CTRL_SET_TIMEOUT);
+ }
+ else
+ {
+ irv = usb_control_msg(dev, REQTYPE_HOST_TO_INTERFACE, request, data[0],
+ index, NULL, 0, USB_CTRL_SET_TIMEOUT);
+ }
+ LOG_VAR(irv);
+
+ return irv;
+ }
+
+ int set_config_single(int request, unsigned short data)
+ {
+ return set_config(request, reinterpret_cast<char*>(&data), 2);
+ }
+
+ int change_speed(unsigned int baud)
+ {
+ return set_config(CP210X_SET_BAUDRATE, reinterpret_cast<char*>(&baud), sizeof(baud));
+ }
+
+ int tiocmset(unsigned int set, unsigned int clear)
+ {
+ unsigned int control = 0;
+
+ if (set & TIOCM_RTS) {
+ control |= CONTROL_RTS;
+ control |= CONTROL_WRITE_RTS;
+ }
+ if (set & TIOCM_DTR) {
+ control |= CONTROL_DTR;
+ control |= CONTROL_WRITE_DTR;
+ }
+ if (clear & TIOCM_RTS) {
+ control &= ~CONTROL_RTS;
+ control |= CONTROL_WRITE_RTS;
+ }
+ if (clear & TIOCM_DTR) {
+ control &= ~CONTROL_DTR;
+ control |= CONTROL_WRITE_DTR;
+ }
+
+ printf("%s - control = 0x%.4x\n", __FUNCTION__, control);
+
+ return set_config(CP210X_SET_MHS, reinterpret_cast<char*>(&control), 2);
+ }
+
+
+ void
+ modprobe()
+ {
+// ffff8800364a8300 962108826 S Co:3:001:0 s 23 03 0004 0001 0000 0
+// ffff8800364a8300 962108840 C Co:3:001:0 0 0
+// ffff8800b225b780 962162649 S Ci:3:001:0 s a3 00 0000 0001 0004 4 <
+// ffff8800b225b780 962162697 C Ci:3:001:0 0 4 = 03010000
+// ffff8800b225b780 962218579 S Co:3:001:0 s 23 01 0014 0001 0000 0
+// ffff8800b225b780 962218591 C Co:3:001:0 0 0
+// ffff8800b225b780 962218620 S Ci:3:000:0 s 80 06 0100 0000 0040 64 <
+// ffff8800b225b780 962222272 C Ci:3:000:0 0 18 = 12011001 00000040 cf0f0410 00030102 0301
+// ffff8800364a8300 962222320 S Co:3:001:0 s 23 03 0004 0001 0000 0
+// ffff8800364a8300 962222330 C Co:3:001:0 0 0
+// ffff8800b225b3c0 962274639 S Ci:3:001:0 s a3 00 0000 0001 0004 4 <
+// ffff8800b225b3c0 962274675 C Ci:3:001:0 0 4 = 03010000
+// ffff8800364a8300 962330667 S Co:3:001:0 s 23 01 0014 0001 0000 0
+// ffff8800364a8300 962330676 C Co:3:001:0 0 0
+// ffff8800364a8300 962330685 S Co:3:000:0 s 00 05 0002 0000 0000 0
+// ffff8800364a8300 962333269 C Co:3:000:0 0 0
+// ffff8800b225b3c0 962350649 S Ci:3:002:0 s 80 06 0100 0000 0012 18 <
+// ffff8800b225b3c0 962354268 C Ci:3:002:0 0 18 = 12011001 00000040 cf0f0410 00030102 0301
+// ffff8800b225b3c0 962354312 S Ci:3:002:0 s 80 06 0200 0000 0020 32 <
+// ffff8800b225b3c0 962357263 C Ci:3:002:0 0 32 = 09022000 01010080 32090400 0002ff00 00020705 81024000 00070501 02400000
+// ffff8800364a8300 962357309 S Ci:3:002:0 s 80 06 0303 0409 00ff 255 <
+// ffff8800364a8300 962364268 C Ci:3:002:0 0 128 = 80033100 30003000 36003200 00001400 28002f00 f300a700 18003a00 80004700
+// ffff8800b225ba80 962364316 S Co:3:002:0 s 00 09 0001 0000 0000 0
+// ffff8800b225ba80 962366264 C Co:3:002:0 0 0
+ if(!dev)
+ return;
+
+ //cp210x_startup: usb_reset_device
+
+ set_config_single(CP210X_IFC_ENABLE, UART_ENABLE);
+ //cp2101_set_config_single(port, CP210X_IFC_ENABLE, UART_ENABLE);
+ /* Configure the termios structure */
+ //cp2101_get_termios(port);
+ change_speed(115200);
+ /* Set the DTR and RTS pins low */
+ //cp2101_tiocmset(port, NULL, TIOCM_DTR | TIOCM_RTS, 0);
+ tiocmset(TIOCM_DTR | TIOCM_RTS, 0);
+
+ }
+
+ void
+ open_tty()
+ {}
+
+ usb_dev_handle*
+ libUSBGetDevice (unsigned short vid, unsigned short pid)
+ {
+ struct usb_bus *UsbBus = NULL;
+ struct usb_device *UsbDevice = NULL;
+ usb_dev_handle *ret;
+
+ int dBuses, dDevices;
+ dBuses = usb_find_busses ();
+ dDevices = usb_find_devices ();
+
+ printf("bus: %s, dev: %s, vid: 0x%04hx, pid: 0x%04hx\n", "", "", vid, pid);
+
+ for (UsbBus = usb_get_busses(); UsbBus; UsbBus = UsbBus->next)
+ {
+ bool found = false;
+ for (UsbDevice = UsbBus->devices; UsbDevice; UsbDevice = UsbDevice->next)
+ {
+ printf("bus: %s, dev: %s, vid: 0x%04hx, pid: 0x%04hx\n", UsbBus->dirname, UsbDevice->filename, UsbDevice->descriptor.idVendor, UsbDevice->descriptor.idProduct);
+ if (UsbDevice->descriptor.idVendor == vid && UsbDevice->descriptor.idProduct== pid)
+ {
+ printf("found!\n");
+ found = true;
+ break;
+ }
+ }
+ if(found)
+ break;
+ }
+
+ if (!UsbDevice) return NULL;
+ ret = usb_open (UsbDevice);
+
+
+ //int cfg = usb_get_configuration(UsbDevice);
+ int rv=usb_set_configuration (ret, USB_ANT_CONFIGURATION);
+ LOG_VAR(rv);
+ if (rv < 0) {
+ usb_close (ret);
+ return NULL;
+ }
+
+
+ rv=usb_claim_interface (ret, USB_ANT_INTERFACE);
+ LOG_VAR(rv);
+ if (rv < 0) {
+ usb_close (ret);
+ return NULL;
+ }
+
+ return ret;
+ }
+};
+
+
+
+SerialUsb::SerialUsb()
+{
+ m_p.reset(new SerialUsbPrivate());
+ m_p->m_recvThKill = 0;
+ m_p->dev = 0;
+
+ usb_init();
+
+}
+
+SerialUsb::~SerialUsb()
+{
+ close();
+ m_p.reset();
+}
+
+//#define ENSURE_OR_RETURN_FALSE(e) do { if(-1 == (e)) {perror(#e); return false;} } while(false)
+
+// runs in other thread
+struct AntUsbHandler2_Recevier
+{
+ void operator() (SerialUsb* arg)
+ {
+ //printf("recvFunc, arg: %p\n", arg); fflush(stdout);
+ if(!arg)
+ {
+ rv=0;
+ return;
+ }
+ SerialUsb* This = reinterpret_cast<SerialUsb*>(arg);
+ //printf("recvFunc, This: %p\n", This); fflush(stdout);
+ rv = This->receiveHandler();
+ }
+ void* rv;
+};
+
+
+bool
+SerialUsb::open()
+{
+ close();
+
+ bool rv = false;
+
+ // ffff8800b1c470c0 1328871577 S Co:3:002:0 s 00 09 0001 0000 0000 0
+ // ffff8800b1c470c0 1328873340 C Co:3:002:0 0 0
+ m_p->dev = m_p->libUSBGetDevice(0x0fcf, 0x1004);
+ if(!m_p->dev)
+ return false;
+
+ m_p->modprobe();
+
+ m_p->open_tty();
+
+ //cp2101_set_config_single(port, CP2101_UART, UART_ENABLE)
+ /* Configure the termios structure */
+ //cp2101_get_termios(port);
+ /* Set the DTR and RTS pins low */
+ //cp2101_tiocmset(port, NULL, TIOCM_DTR | TIOCM_RTS, 0);
+
+ // ffff8800b1c470c0 1328873580 S Co:3:002:0 s 02 01 0000 0001 0000 0
+ // ffff8800b1c470c0 1328874338 C Co:3:002:0 0 0
+ //int irv = usb_clear_halt(m_p->dev, USB_ANT_EP_OUT);
+ //LOG_VAR(irv);
+
+ // usb_clear_halt()
+ // usb_get_string_simple()
+ // usb_reset()
+ // usb_device()
+ // usb_set_configuration()
+ // usb_set_debug()
+
+ m_p->m_recvThKill = 0;
+ AntUsbHandler2_Recevier recTh;
+ recTh.rv = 0;
+ m_p->m_recvTh = boost::thread(recTh, this);
+
+ return true;
+}
+
+
+
+void
+SerialUsb::close()
+{
+ m_p->m_recvThKill = 1;
+ m_p->m_recvTh.join();
+
+ if(m_p.get())
+ {
+ if(m_p->dev)
+ {
+ //usb_reset(m_p->dev);
+ usb_release_interface(m_p->dev, USB_ANT_INTERFACE);
+ usb_close(m_p->dev);
+ }
+ m_p->dev = 0;
+ }
+}
+
+
+
+bool
+SerialUsb::read(char& c)
+{
+ boost::unique_lock<boost::mutex> lock(m_p->m_queueMtx);
+
+ if(m_p->m_recvQueue.empty())
+ return false;
+
+ c = m_p->m_recvQueue.front();
+ m_p->m_recvQueue.pop();
+ return true;
+}
+
+
+bool
+SerialUsb::read(char* dst, const size_t sizeBytes, size_t& bytesRead)
+{
+ if(!dst)
+ return false;
+
+ boost::unique_lock<boost::mutex> lock(m_p->m_queueMtx);
+
+ size_t s = m_p->m_recvQueue.size();
+ s = std::min(s, sizeBytes);
+ for(size_t i = 0; i < s; i++)
+ {
+ dst[i] = m_p->m_recvQueue.front();
+ m_p->m_recvQueue.pop();
+ }
+ bytesRead = s;
+
+ if(bytesRead==0)
+ return false;
+
+ return true;
+}
+
+
+bool
+SerialUsb::readBlocking(char* dst, const size_t sizeBytes, size_t& bytesRead)
+{
+ if(!dst)
+ return false;
+
+ const size_t timeout_ms = 1000;
+ {
+ boost::unique_lock<boost::mutex> lock(m_p->m_queueMtx);
+
+ //while(m_p->m_recvQueue.empty()) // while - to guard agains spurious wakeups
+ {
+ m_p->m_condQueue.timed_wait(lock, boost::posix_time::milliseconds(timeout_ms));
+ }
+ size_t s = m_p->m_recvQueue.size();
+ s = std::min(s, sizeBytes);
+ for(size_t i = 0; i < s; i++)
+ {
+ dst[i] = m_p->m_recvQueue.front();
+ m_p->m_recvQueue.pop();
+ }
+ bytesRead = s;
+ }
+
+ if(bytesRead==0)
+ return false;
+
+ return true;
+}
+
+
+bool
+SerialUsb::write(const char* src, const size_t sizeBytes, size_t& bytesWritten)
+{
+ if(!m_p->dev)
+ return false;
+
+ int size = static_cast<int>(sizeBytes);
+ //int written = usb_interrupt_write(m_p->dev, USB_ANT_EP_OUT, const_cast<char*>(src), size, 3000);
+ int written = usb_bulk_write(m_p->dev, USB_ANT_EP_OUT, const_cast<char*>(src), size, 3000);
+ if(written < 0)
+ {
+ char* usberr=usb_strerror();
+ LOG_VAR(usberr);
+ return false;
+ }
+
+ bytesWritten = written;
+
+ return (bytesWritten==sizeBytes);
+}
+
+
+
+
+void*
+SerialUsb::receiveHandler()
+{
+ for(;;)
+ {
+ if(m_p->m_recvThKill)
+ return NULL;
+
+ unsigned char buf[4096];
+ int timeout_ms = 1000;
+ int rv = usb_bulk_read(m_p->dev, USB_ANT_EP_IN, (char*)buf, sizeof(buf), timeout_ms);
+
+ if(rv > 0)
+ {
+ boost::unique_lock<boost::mutex> lock(m_p->m_queueMtx);
+ for(int i = 0; i < rv; i++)
+ m_p->m_recvQueue.push(buf[i]);
+ m_p->m_condQueue.notify_one();
+ }
+ else if(rv==0)
+ {}
+ else if(rv==-116) // timeout
+ {}
+ else
+ {
+ char* usberr=usb_strerror();
+ LOG_VAR2(rv, usberr);
+ }
+ }
+
+ return NULL;
+}
+
+const size_t SerialUsb::getQueueLength() const
+{
+ size_t len=0;
+ boost::unique_lock<boost::mutex> lock(m_p->m_queueMtx);
+ len += m_p->m_recvQueue.size();
+ return len;
+}
+
+
+//// called from other thread
+//void
+//AntUsbHandler::queueData()
+//{
+//}
+
+
+bool
+SerialUsb::isOpen() const
+{
+ // TODO: is thread running too??
+ return m_p.get() && m_p->dev;
+}
+
diff --git a/src/SerialUsb.hpp b/src/SerialUsb.hpp
new file mode 100644
index 0000000..e838598
--- /dev/null
+++ b/src/SerialUsb.hpp
@@ -0,0 +1,49 @@
+// -*- mode: c++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2; coding: utf-8-unix -*-
+// ***** BEGIN LICENSE BLOCK *****
+////////////////////////////////////////////////////////////////////
+// Copyright (c) 2011-2013 RALOVICH, Kristóf //
+// //
+// This program is free software; you can redistribute it and/or //
+// modify it under the terms of the GNU General Public License //
+// version 2 as published by the Free Software Foundation. //
+// //
+////////////////////////////////////////////////////////////////////
+// ***** END LICENSE BLOCK *****
+#pragma once
+
+#include <queue>
+#include <boost/thread.hpp>
+#include "antdefs.hpp"
+#include "lqueue.hpp"
+#include <list>
+#include "Serial.hpp"
+
+struct SerialUsbPrivate;
+
+// Serial communication over a CP201X usb-serial port.
+class SerialUsb : public Serial
+{
+public:
+ SerialUsb();
+ virtual ~SerialUsb();
+
+ virtual bool open();
+ virtual void close();
+
+ virtual bool read(char& c);
+ virtual bool read(char* dst, const size_t sizeBytes, size_t& bytesRead);
+ virtual bool readBlocking(char* dst, const size_t sizeBytes, size_t& bytesRead);
+ virtual bool write(const char* src, const size_t sizeBytes, size_t& bytesWritten);
+
+ void* receiveHandler(); // PUBLIC on purpose
+
+ virtual const size_t getQueueLength() const;
+
+ virtual const char* getImplName() { return "AntUsbHandler"; }
+
+ virtual bool isOpen() const;
+
+private:
+ std::auto_ptr<SerialUsbPrivate> m_p;
+};
+
diff --git a/src/SmartPtrFwd.hpp b/src/SmartPtrFwd.hpp
new file mode 100644
index 0000000..4bac0db
--- /dev/null
+++ b/src/SmartPtrFwd.hpp
@@ -0,0 +1,57 @@
+// -*- mode: c++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2; coding: utf-8-unix -*-
+// ***** BEGIN LICENSE BLOCK *****
+////////////////////////////////////////////////////////////////////
+// Copyright (c) 2011-2013 RALOVICH, Kristóf //
+// //
+// This program is free software; you can redistribute it and/or //
+// modify it under the terms of the GNU General Public License //
+// version 2 as published by the Free Software Foundation. //
+// //
+////////////////////////////////////////////////////////////////////
+// ***** END LICENSE BLOCK *****
+#pragma once
+
+# include <memory>
+
+
+# ifdef _MSC_VER
+# if _MSC_VER == 1600 // VS2010
+
+// smart ptr stuff is already in std::
+
+# elif _MSC_VER == 1500 && _MSC_FULL_VER >= 150030729 // VS2008 SP1
+# include <boost/static_assert.hpp>
+
+namespace std
+{
+ using std::tr1::shared_ptr;
+ using std::tr1::weak_ptr;
+
+ using std::tr1::static_pointer_cast;
+ using std::tr1::const_pointer_cast;
+ using std::tr1::dynamic_pointer_cast;
+}
+
+# define static_assert(x) BOOST_STATIC_ASSERT(x)
+
+# else // lower than VS2008 SP1
+# include <boost/shared_ptr.hpp>
+# include <boost/weak_ptr.hpp>
+# include <boost/static_assert.hpp>
+
+namespace std
+{
+ using boost::shared_ptr;
+ using boost::weak_ptr;
+
+ using boost::static_pointer_cast;
+ using boost::const_pointer_cast;
+ using boost::dynamic_pointer_cast;
+}
+
+# define static_assert(x) BOOST_STATIC_ASSERT(x)
+
+
+# endif
+# endif // _MSC_VER
+
diff --git a/src/antdefs.hpp b/src/antdefs.hpp
new file mode 100644
index 0000000..de7060a
--- /dev/null
+++ b/src/antdefs.hpp
@@ -0,0 +1,323 @@
+// -*- mode: c++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2; coding: utf-8-unix -*-
+// copyright 2008-2009 paul@ant.sbrk.co.uk. released under GPLv3
+// vers 0.6t
+// -*- mode: c++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2; coding: utf-8-unix -*-
+// ***** BEGIN LICENSE BLOCK *****
+////////////////////////////////////////////////////////////////////
+// Copyright (c) 2011-2013 RALOVICH, Kristóf //
+// //
+// This program is free software; you can redistribute it and/or //
+// modify it under the terms of the GNU General Public License //
+// version 2 as published by the Free Software Foundation. //
+// //
+////////////////////////////////////////////////////////////////////
+// ***** END LICENSE BLOCK *****
+
+#pragma once
+
+//#include <cstdint>
+#include "stdintfwd.hpp"
+
+typedef uint8_t uchar;
+typedef uint16_t ushort;
+typedef uint32_t uint;
+
+
+
+//#define EVENT_RX_ACKNOWLEDGED 0x9b
+//#define EVENT_RX_BROADCAST 0x9a
+//#define EVENT_RX_BURST_PACKET 0x9c
+//#define EVENT_RX_EXT_ACKNOWLEDGED 0x9e
+//#define EVENT_RX_EXT_BROADCAST 0x9d
+//#define EVENT_RX_EXT_BURST_PACKET 0x9f
+//#define EVENT_RX_FAKE_BURST 0xdd
+
+
+
+
+
+//#define MESG_INVALID_ID 0x00
+#define MESG_EVENT_ID 0x01
+
+#define MESG_VERSION_ID 0x3E
+
+#define MESG_RESPONSE_EVENT_ID 0x40
+#define MESG_UNASSIGN_CHANNEL_ID 0x41
+#define MESG_ASSIGN_CHANNEL_ID 0x42
+#define MESG_CHANNEL_MESG_PERIOD_ID 0x43
+#define MESG_CHANNEL_SEARCH_TIMEOUT_ID 0x44
+#define MESG_CHANNEL_RADIO_FREQ_ID 0x45
+#define MESG_NETWORK_KEY_ID 0x46
+#define MESG_SEARCH_WAVEFORM_ID 0x49
+#define MESG_SYSTEM_RESET_ID 0x4a
+#define MESG_OPEN_CHANNEL_ID 0x4b
+#define MESG_CLOSE_CHANNEL_ID 0x4c
+#define MESG_REQUEST_ID 0x4d
+#define MESG_BROADCAST_DATA_ID 0x4e
+#define MESG_ACKNOWLEDGED_DATA_ID 0x4f
+
+#define MESG_BURST_DATA_ID 0x50
+#define MESG_CAPABILITIES_ID 0x54
+#define MESG_CHANNEL_ID_ID 0x51
+#define MESG_CHANNEL_STATUS_ID 0x52
+#define MESG_OPEN_RX_SCAN_ID 0x5b
+#define MESG_EXT_BROADCAST_DATA_ID 0x5d
+#define MESG_EXT_ACKNOWLEDGED_DATA_ID 0x5e
+#define MESG_EXT_BURST_DATA_ID 0x5f
+
+#define MESG_GET_SERIAL_NUM_ID 0x61
+
+
+
+
+#define MESG_DATA_SIZE 30
+
+#define MESG_RESPONSE_EVENT_SIZE 3
+
+#define MESG_TX_SYNC 0xa4
+
+
+#define RESPONSE_NO_ERROR 0x00
+#define EVENT_RX_SEARCH_TIMEOUT 0x01
+#define EVENT_RX_FAIL 0x02
+#define EVENT_TX 0x03
+#define EVENT_TRANSFER_RX_FAILED 0x04
+#define EVENT_TRANSFER_TX_COMPLETED 0x05
+#define EVENT_TRANSFER_TX_FAILED 0x06
+#define EVENT_CHANNEL_CLOSED 7
+#define EVENT_RX_FAIL_GO_TO_SEARCH 8
+#define EVENT_CHANNEL_COLLISION 9
+#define EVENT_TRANSFER_TX_START 10
+#define CHANNEL_IN_WRONG_STATE 21
+#define CHANNEL_NOT_OPENED 22
+#define CHANNEL_ID_NOT_SET 24
+#define CLOSE_ALL_CHANNELS 25
+#define TRANSFER_IN_PROGRESS 31
+#define TRANSFER_SEQUENCE_NUMBER_ERROR 32
+#define TRANSFER_IN_ERROR 33
+
+static const unsigned char ANTP_NETKEY_HR[8] = {0xB9,0xA5,0x21,0xFB,0xBD,0x72,0xC3,0x45};
+static const unsigned char ANTP_NETKEY[8] = {0xA8,0xA4,0x23,0xB9,0xF5,0x5E,0x63,0xC1}; // ANT+Sport key
+
+
+
+#define ANTFS_BeaconId 0x43
+#define ANTFS_CommandResponseId 0x44
+
+#define ANTFS_CmdLink 0x02
+#define ANTFS_CmdDisconnect 0x03
+#define ANTFS_CmdAuthenticate 0x04
+#define ANTFS_CmdPing 0x05
+#define ANTFS_ReqDownload 0x09
+#define ANTFS_ReqUpload 0x0A
+#define ANTFS_ReqErase 0x0B
+#define ANTFS_UploadData 0x0C
+
+
+#define ANTFS_RespAuthenticate 0x84
+#define ANTFS_RespDownload 0x89
+#define ANTFS_RespUpload 0x8A
+#define ANTFS_RespErase 0x8B
+#define ANTFS_RespUploadData 0x8C
+
+
+
+
+
+
+#define ANTP_PAIR(x) {x,#x}
+static struct { int i; const char* s; } msgNames[]={
+ ANTP_PAIR(MESG_EVENT_ID),
+ ANTP_PAIR(MESG_ACKNOWLEDGED_DATA_ID),
+ ANTP_PAIR(MESG_ASSIGN_CHANNEL_ID),
+ ANTP_PAIR(MESG_BROADCAST_DATA_ID),
+ ANTP_PAIR(MESG_BURST_DATA_ID),
+ ANTP_PAIR(MESG_CAPABILITIES_ID),
+ ANTP_PAIR(MESG_CHANNEL_ID_ID),
+ ANTP_PAIR(MESG_CHANNEL_MESG_PERIOD_ID),
+ ANTP_PAIR(MESG_CHANNEL_RADIO_FREQ_ID),
+ ANTP_PAIR(MESG_CHANNEL_SEARCH_TIMEOUT_ID),
+ ANTP_PAIR(MESG_CHANNEL_STATUS_ID),
+ ANTP_PAIR(MESG_VERSION_ID),
+ ANTP_PAIR(MESG_GET_SERIAL_NUM_ID),
+ ANTP_PAIR(MESG_CLOSE_CHANNEL_ID),
+ ANTP_PAIR(MESG_EXT_ACKNOWLEDGED_DATA_ID),
+ ANTP_PAIR(MESG_EXT_BROADCAST_DATA_ID),
+ ANTP_PAIR(MESG_EXT_BURST_DATA_ID),
+ ANTP_PAIR(MESG_NETWORK_KEY_ID),
+ ANTP_PAIR(MESG_OPEN_CHANNEL_ID),
+ ANTP_PAIR(MESG_OPEN_RX_SCAN_ID),
+ ANTP_PAIR(MESG_REQUEST_ID),
+ ANTP_PAIR(MESG_RESPONSE_EVENT_ID),
+ ANTP_PAIR(MESG_SEARCH_WAVEFORM_ID),
+ ANTP_PAIR(MESG_SYSTEM_RESET_ID),
+ ANTP_PAIR(MESG_UNASSIGN_CHANNEL_ID),
+ {-1,"UNKNOWN"}
+};
+
+static struct { int i; const char* s; } responseNames[]={
+ ANTP_PAIR(RESPONSE_NO_ERROR),
+ ANTP_PAIR(EVENT_RX_SEARCH_TIMEOUT),
+ ANTP_PAIR(EVENT_RX_FAIL),
+ ANTP_PAIR(EVENT_TX),
+ ANTP_PAIR(EVENT_TRANSFER_RX_FAILED),
+ ANTP_PAIR(EVENT_TRANSFER_TX_COMPLETED),
+ ANTP_PAIR(EVENT_TRANSFER_TX_FAILED),
+ ANTP_PAIR(EVENT_CHANNEL_CLOSED),
+ ANTP_PAIR(EVENT_RX_FAIL_GO_TO_SEARCH),
+ ANTP_PAIR(EVENT_CHANNEL_COLLISION),
+ ANTP_PAIR(EVENT_TRANSFER_TX_START),
+ ANTP_PAIR(CHANNEL_IN_WRONG_STATE),
+ ANTP_PAIR(CHANNEL_NOT_OPENED),
+ ANTP_PAIR(CHANNEL_ID_NOT_SET),
+ ANTP_PAIR(CLOSE_ALL_CHANNELS),
+ ANTP_PAIR(TRANSFER_IN_PROGRESS),
+ ANTP_PAIR(TRANSFER_SEQUENCE_NUMBER_ERROR),
+ ANTP_PAIR(TRANSFER_IN_ERROR),
+ {-1,"UNKNOWN"}
+};
+
+
+static struct {int i; const char* s; } antFSCommandNames[]={
+ ANTP_PAIR(ANTFS_CmdLink),
+ ANTP_PAIR(ANTFS_CmdDisconnect),
+ ANTP_PAIR(ANTFS_CmdAuthenticate),
+ ANTP_PAIR(ANTFS_CmdPing),
+ ANTP_PAIR(ANTFS_ReqDownload),
+ ANTP_PAIR(ANTFS_ReqUpload),
+ ANTP_PAIR(ANTFS_ReqErase),
+ ANTP_PAIR(ANTFS_UploadData),
+ {-1,"UNKNOWN"}
+};
+
+static struct {int i; const char* s; } antFSResponseNames[]={
+ ANTP_PAIR(ANTFS_RespAuthenticate),
+ ANTP_PAIR(ANTFS_RespDownload),
+ ANTP_PAIR(ANTFS_RespUpload),
+ ANTP_PAIR(ANTFS_RespErase),
+ ANTP_PAIR(ANTFS_RespUploadData),
+ {-1,"UNKNOWN"}
+};
+
+#undef ANTP_PAIR
+
+// FIXME: message codes!
+
+
+
+
+
+#define ENUMERATE1(id, val) ENUMERATE(id, val, #id)
+#define ENUMERATE_LIST \
+ ENUMERATE1(ST_ANTFS_0, 0) \
+ ENUMERATE1(ST_ANTFS_NODATA, 999) \
+ ENUMERATE1(ST_ANTFS_RESTART, 1000) \
+ ENUMERATE1(ST_ANTFS_START0, 1001) \
+ ENUMERATE1(ST_ANTFS_LINKING, 1005) \
+ ENUMERATE1(ST_ANTFS_AUTH0_SN, 1012) \
+ ENUMERATE1(ST_ANTFS_AUTH1_PASS, 1013) \
+ ENUMERATE1(ST_ANTFS_AUTH1_PAIR, 1017) \
+ ENUMERATE1(ST_ANTFS_DL_DIRECTORY, 1024) \
+ ENUMERATE1(ST_ANTFS_DL_FILES, 1025) \
+ ENUMERATE1(ST_ANTFS_DL_SINGLE_FILE, 1027) \
+ ENUMERATE1(ST_ANTFS_ERASE_SINGLE_FILE, 500) \
+ ENUMERATE1(ST_ANTFS_BAD, 1006) \
+ ENUMERATE1(ST_ANTFS_LAST, 1007)
+
+typedef enum StateFSWork {
+ //enum {
+#define ENUMERATE(id, val, name) id = val,
+ ENUMERATE_LIST
+#undef ENUMERATE
+ //};
+} StateFSWork;
+
+//static const char* StateFSWorkNames {
+//#define ENUMERATE(id, val, name) name,
+// ENUMERATE_LIST
+//#undef ENUMERATE
+//} StateFSWorkNames;
+
+static
+const char*
+StateFSWork2Str(const int id)
+{
+ switch(id)
+ {
+ //case ST_ANTFS_0: return "ST_ANTFS_0";
+#define ENUMERATE(id, val, str) case id: return str;
+ ENUMERATE_LIST
+#undef ENUMERATE
+ default:
+ return "???";
+ }
+}
+
+//int
+//string_to_enum(const char *in_str)
+//{
+// if (0);
+//#define ENUMERATE(id, val, str) else if (0 == strcmp(in_str, str)) return val;
+// ENUMERATE_LIST
+//#undef ENUMERATE
+// return -1; /* Not found */
+//}
+
+#undef ENUMERATE_LIST
+#undef ENUMERATE1
+
+
+
+#define ENUMERATE1(id) ENUMERATE(id, #id)
+#define ENUMERATE_LIST \
+ ENUMERATE1(MD_DOWNLOAD_ALL) \
+ ENUMERATE1(MD_DOWNLOAD_SINGLE_FILE) \
+ ENUMERATE1(MD_DIRECTORY_LISTING) \
+ ENUMERATE1(MD_ERASE_SINGLE_FILE) \
+ ENUMERATE1(MD_ERASE_ALL_ACTIVITIES) \
+ ENUMERATE1(MD_LAST)
+
+typedef enum ModeOfOperation {
+ //enum {
+#define ENUMERATE(id, name) id,
+ ENUMERATE_LIST
+#undef ENUMERATE
+ //};
+} ModeOfOperation;
+
+//static const char* StateFSWorkNames {
+//#define ENUMERATE(id, val, name) name,
+// ENUMERATE_LIST
+//#undef ENUMERATE
+//} StateFSWorkNames;
+
+static
+const char*
+ModeOfOperation2Str(const int id)
+{
+ switch(id)
+ {
+ //case ST_ANTFS_0: return "ST_ANTFS_0";
+#define ENUMERATE(id, str) case id: return str;
+ ENUMERATE_LIST
+#undef ENUMERATE
+ default:
+ return "???";
+ }
+}
+
+//int
+//string_to_enum(const char *in_str)
+//{
+// if (0);
+//#define ENUMERATE(id, val, str) else if (0 == strcmp(in_str, str)) return val;
+// ENUMERATE_LIST
+//#undef ENUMERATE
+// return -1; /* Not found */
+//}
+
+#undef ENUMERATE_LIST
+#undef ENUMERATE1
+
+
+/* vim: se sw=8 ts=8: */
diff --git a/src/antpm-downloader.cpp b/src/antpm-downloader.cpp
new file mode 100644
index 0000000..98c354e
--- /dev/null
+++ b/src/antpm-downloader.cpp
@@ -0,0 +1,192 @@
+// -*- mode: c++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2; coding: utf-8-unix -*-
+// ***** BEGIN LICENSE BLOCK *****
+////////////////////////////////////////////////////////////////////
+// Copyright (c) 2011-2013 RALOVICH, Kristóf //
+// //
+// This program is free software; you can redistribute it and/or //
+// modify it under the terms of the GNU General Public License //
+// version 2 as published by the Free Software Foundation. //
+// //
+////////////////////////////////////////////////////////////////////
+// ***** END LICENSE BLOCK *****
+
+#include <cstdio>
+#include <cstdlib>
+#include <iostream>
+#include <fstream>
+#include <vector>
+
+#include "AntMessage.hpp"
+#include "AntFr310XT.hpp"
+#include "common.hpp"
+
+#include <boost/program_options.hpp>
+
+#ifdef _WIN32
+# include <Windows.h>
+#elif defined(__linux)
+# include <signal.h>
+#endif
+
+namespace po = boost::program_options;
+using namespace std;
+
+
+volatile int stop = 0;
+AntFr310XT2 watch2(false);
+
+static
+void
+stopIt()
+{
+ stop = 1;
+ watch2.stop();
+}
+
+#ifdef _WIN32
+BOOL CtrlHandler( DWORD fdwCtrlType )
+{
+ switch( fdwCtrlType )
+ {
+ // Handle the CTRL-C signal.
+ case CTRL_C_EVENT:
+ printf( "Ctrl-C event\n\n" );
+ //Beep( 750, 300 );
+ stopIt();
+ return( TRUE );
+
+ // CTRL-CLOSE: confirm that the user wants to exit.
+ case CTRL_CLOSE_EVENT:
+ //Beep( 600, 200 );
+ printf( "Ctrl-Close event\n\n" );
+ stopIt();
+ return( TRUE );
+
+ // Pass other signals to the next handler.
+ case CTRL_BREAK_EVENT:
+ //Beep( 900, 200 );
+ printf( "Ctrl-Break event\n\n" );
+ stopIt();
+ return FALSE;
+
+ case CTRL_LOGOFF_EVENT:
+ //Beep( 1000, 200 );
+ printf( "Ctrl-Logoff event\n\n" );
+ stopIt();
+ return FALSE;
+
+ case CTRL_SHUTDOWN_EVENT:
+ //Beep( 750, 500 );
+ printf( "Ctrl-Shutdown event\n\n" );
+ stopIt();
+ return FALSE;
+
+ default:
+ return FALSE;
+ }
+}
+#elif defined(__linux)
+void my_handler(int s)
+{
+ printf("Caught signal %d\n",s);
+ stopIt();
+}
+#endif
+
+
+int
+main(int argc, char** argv)
+{
+#ifdef _WIN32
+ if( !SetConsoleCtrlHandler( (PHANDLER_ROUTINE) CtrlHandler, TRUE ) )
+ {
+ printf( "\nERROR: Could not set control handler...");
+ //return 1;
+ }
+#elif defined(__linux)
+ printf("press Ctrl-C to exit\n");
+ signal(SIGINT, my_handler);
+#endif
+
+ // Declare the supported options.
+ bool pairing =false;
+ bool dirOnly =false;
+ uint16_t dlFileIdx =0x0000;
+ uint16_t eraseFileIdx=0x0000;
+ po::options_description desc("Allowed options");
+ desc.add_options()
+ ("help", "produce help message")
+ ("pairing,P", po::value<bool>(&pairing)->zero_tokens()->implicit_value(true), "Force pairing first")
+ ("dir-only", po::value<bool>(&dirOnly)->zero_tokens()->implicit_value(true), "Download and list device directory")
+ ("download,D",po::value<std::string>(), "Download a file (hex id e.g. 0x12FB) from device")
+ ("erase", po::value<std::string>(), "Erase a file (hex id e.g. 0x12FB) from device")
+ ;
+
+ std::vector<const char*> args(argv, argv+argc);
+ po::variables_map vm;
+ try
+ {
+ //po::parsed_options parsed = po::parse_command_line(argc, argv, desc);
+ po::parsed_options parsed = po::command_line_parser(argc, argv).options(desc).run();
+ po::store(parsed, vm);
+
+ if(vm.find("download")!=vm.end())
+ {
+ std::stringstream interpreter;
+ interpreter << std::hex << vm["download"].as<std::string>();
+ interpreter >> dlFileIdx;
+ }
+ if(vm.find("erase")!=vm.end())
+ {
+ std::stringstream interpreter;
+ interpreter << std::hex << vm["erase"].as<std::string>();
+ interpreter >> eraseFileIdx;
+ }
+
+ po::notify(vm);
+ }
+ catch(po::error& error)
+ {
+ cerr << error.what() << "\n";
+ cerr << desc << "\n";
+ return EXIT_FAILURE;
+ }
+
+ if(vm.count("help"))
+ {
+ cout << desc << "\n";
+ return EXIT_SUCCESS;
+ }
+ LOG_VAR4(pairing, dirOnly, int(dlFileIdx), int(eraseFileIdx));
+
+ //if(0)
+ //{
+ // AntFr310XT watch;
+
+ // watch.start();
+
+ // while(!stop)
+ // {
+ // sleepms(1000);
+ // }
+
+ // watch.stop();
+ //}
+ //else
+ {
+ watch2.setModeDownloadAll();
+ if(pairing) watch2.setModeForcePairing();
+ if(dirOnly) watch2.setModeDirectoryListing();
+ else if(dlFileIdx!=0x0000) watch2.setModeDownloadSingleFile(dlFileIdx);
+ else if(eraseFileIdx!=0x000) watch2.setModeEraseSingleFile(eraseFileIdx);
+
+ watch2.start();
+
+
+ watch2.stop();
+ }
+
+
+
+ return EXIT_SUCCESS;
+}
diff --git a/src/antpm-fit2gpx.cpp b/src/antpm-fit2gpx.cpp
new file mode 100644
index 0000000..8a0137b
--- /dev/null
+++ b/src/antpm-fit2gpx.cpp
@@ -0,0 +1,120 @@
+// -*- mode: c++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2; coding: utf-8-unix -*-
+// ***** BEGIN LICENSE BLOCK *****
+////////////////////////////////////////////////////////////////////
+// Copyright (c) 2011-2013 RALOVICH, Kristóf //
+// //
+// This program is free software; you can redistribute it and/or //
+// modify it under the terms of the GNU General Public License //
+// version 2 as published by the Free Software Foundation. //
+// //
+////////////////////////////////////////////////////////////////////
+// ***** END LICENSE BLOCK *****
+
+#include <cstdio>
+#include <cstdlib>
+#include <iostream>
+#include <fstream>
+#include <vector>
+
+#include "AntMessage.hpp"
+#include "FIT.hpp"
+#include "common.hpp"
+
+#include <boost/program_options.hpp>
+#include <boost/filesystem.hpp>
+
+namespace po = boost::program_options;
+namespace fs = boost::filesystem;
+using namespace std;
+
+
+const
+std::vector<fs::path>
+find_files(const fs::path & dir_path, // in this directory,
+ const std::string & ext // search for this extension,
+ )
+{
+ std::vector<fs::path> paths;
+ if ( !fs::exists( dir_path ) ) return paths;
+ fs::directory_iterator end_itr; // default construction yields past-the-end
+ for ( fs::directory_iterator itr( dir_path );
+ itr != end_itr;
+ ++itr )
+ {
+ if ( fs::is_directory(itr->status()) )
+ {
+ std::vector<fs::path> paths2(find_files( itr->path(), ext) );
+ paths.insert(paths.end(), paths2.begin(), paths2.end());
+ }
+ else if ( fs::extension(*itr) == ext ) // see below
+ {
+ paths.push_back(*itr);
+ }
+ }
+ return paths;
+}
+
+
+int
+main(int argc, char** argv)
+{
+ // Declare the supported options.
+ std::string fitFolder;
+ po::options_description desc("Allowed options");
+ desc.add_options()
+ ("help", "produce help message")
+ ("fitFolder,F", po::value<std::string>(&fitFolder)->required(), "Folder with FIT files")
+ ;
+
+ std::vector<const char*> args(argv, argv+argc);
+ po::variables_map vm;
+ try
+ {
+ //po::parsed_options parsed = po::parse_command_line(argc, argv, desc);
+ po::parsed_options parsed = po::command_line_parser(argc, argv).options(desc).run();
+ po::store(parsed, vm);
+ po::notify(vm);
+ }
+ catch(po::error& error)
+ {
+ cerr << error.what() << "\n";
+ cerr << desc << "\n";
+ return EXIT_FAILURE;
+ }
+
+ if(vm.count("help"))
+ {
+ cout << desc << "\n";
+ return EXIT_SUCCESS;
+ }
+ //LOG_VAR4(pairing, dirOnly, int(dlFileIdx), int(eraseFileIdx));
+
+ if(!fs::is_directory(fs::path(fitFolder)))
+ {
+ LOG_VAR(fitFolder);
+ CHECK_RETURN_RV_LOG_OK(fs::is_directory(fs::path(fitFolder)), EXIT_FAILURE);
+ }
+
+
+ std::vector<fs::path> fitFiles(find_files(fs::path(fitFolder), ".fit"));
+
+ for (size_t i = 0; i < fitFiles.size(); i++)
+ {
+ const std::string in(fitFiles[i].string());
+#if BOOST_VERSION==104300
+ const std::string out(fitFiles[i].parent_path().string()+"//"+fitFiles[i].stem()+".gpx");
+#else
+ const std::string out(fitFiles[i].parent_path().string()+"//"+fitFiles[i].stem().string()+".gpx");
+#endif
+ LOG_VAR2(in, out);
+ GPX gpx;
+ FIT fit;
+ vector<uchar> v(readFile(in.c_str()));
+ if(fit.parse(v, gpx))
+ gpx.writeToFile(out);
+ }
+
+
+
+ return EXIT_SUCCESS;
+}
diff --git a/src/antpm-usbmon2ant.cpp b/src/antpm-usbmon2ant.cpp
new file mode 100644
index 0000000..33a1fb6
--- /dev/null
+++ b/src/antpm-usbmon2ant.cpp
@@ -0,0 +1,222 @@
+// -*- mode: c++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2; coding: utf-8-unix -*-
+// ***** BEGIN LICENSE BLOCK *****
+////////////////////////////////////////////////////////////////////
+// Copyright (c) 2011-2013 RALOVICH, Kristóf //
+// //
+// This program is free software; you can redistribute it and/or //
+// modify it under the terms of the GNU General Public License //
+// version 2 as published by the Free Software Foundation. //
+// //
+////////////////////////////////////////////////////////////////////
+// ***** END LICENSE BLOCK *****
+
+#include <cstdio>
+#include <cstdlib>
+#include <iostream>
+#include <fstream>
+#include <vector>
+
+#include "AntMessage.hpp"
+#include "common.hpp"
+
+#include <boost/program_options.hpp>
+
+namespace po = boost::program_options;
+using namespace std;
+
+
+int
+main(int argc, char** argv)
+{
+ istream* in=0;
+ ifstream f;
+
+ // Declare the supported options.
+ //bool dump=false;
+ //bool usbmon=false;
+ //bool filter=false;
+ string op;
+ string inputFile;
+ po::positional_options_description pd;
+ pd.add("input-file", 1);
+ po::options_description desc("Allowed options");
+ desc.add_options()
+ ("help", "produce help message")
+ ("op,operation", po::value<string>(&op)->default_value("parse"), "possible modes of operation: parse|dump|usbmon|filter|count")
+ //("d", po::value<bool>(&dump)->zero_tokens(), "diffable byte dumps + decoded strings")
+ //("u", po::value<bool>(&usbmon)->zero_tokens(), "generate pseudo usbmon output")
+ //("f", po::value<bool>(&filter)->zero_tokens(), "just filter ANT messages from usbmon stream")
+ ("input-file,P", po::value<string>(&inputFile)->zero_tokens(), "input file, if not given reads from standard input")
+ ;
+
+ po::variables_map vm;
+ try
+ {
+ //po::parsed_options parsed = po::parse_command_line(argc, argv, desc);
+ po::parsed_options parsed = po::command_line_parser(argc, argv).options(desc).positional(pd).run();
+ po::store(parsed, vm);
+ po::notify(vm);
+ }
+ catch(po::error& /*error*/)
+ {
+ cerr << desc << "\n";
+ return EXIT_FAILURE;
+ }
+
+ if(vm.count("help"))
+ {
+ cout << desc << "\n";
+ return EXIT_SUCCESS;
+ }
+ //cout << dump << "\n";
+ //cout << inputFile << "\n";
+
+ if(!inputFile.empty())
+ {
+ f.open(inputFile.c_str());
+ if(!f.is_open())
+ return EXIT_FAILURE;
+ in=&f;
+ }
+ else
+ {
+ in=&cin;
+ }
+
+ //if(argc==2)
+ //{
+ // f.open(argv[1]);
+ // if(!f.is_open())
+ // return EXIT_FAILURE;
+ // in=&f;
+ //}
+ //else
+ //{
+ // in=&cin;
+ //}
+
+ std::list<AntMessage> messages;
+
+ size_t lineno=0;
+ std::string line;
+ unsigned long timeUs0;
+ bool first=true;
+ for( ;getline(*in, line); lineno++)
+ {
+ vector<string> vs(tokenize(line, " "));
+ //cout << vs.size() << "\n";
+
+ if(vs.size()<8 || vs[6]!="=" || vs[7].size()<2 || vs[7][0]!='a' || vs[7][1]!='4')
+ continue;
+
+ unsigned long timeUs=0;
+ if(1!=sscanf(vs[1].c_str(), "%lu", &timeUs))
+ continue;
+ if(first)
+ {
+ first=false;
+ timeUs0=timeUs;
+ }
+ double dt=(timeUs-timeUs0)/1000.0;
+ timeUs0=timeUs;
+
+ unsigned long usbExpectedBytes=0;
+ if(1!=sscanf(vs[5].c_str(), "%lu", &usbExpectedBytes))
+ continue;
+
+ //cout << ".\n";
+ std::string buf;
+ for(size_t i=7;i<vs.size();i++)
+ buf+=vs[i];
+ //cout << buf << "\n";
+
+ std::list<uchar> bytes;
+ std::vector<AntMessage> messages2;
+
+ if(!AntMessage::stringToBytes(buf.c_str(), bytes))
+ {
+ cerr << "\nDECODE FAILED [1]: line: " << lineno << " \"" << line << "\"!\n\n";
+ continue;
+ }
+ size_t usbActualBytes=bytes.size();
+ if(!AntMessage::interpret2(bytes, messages2))
+ {
+ if(usbActualBytes<usbExpectedBytes && !messages2.empty())
+ {
+ // long line truncated in usbmon, but we still managed to decoded one or more packets from it
+ cerr << "\nTRUNCATED: line: " << lineno << " \"" << line << "\" decoded=" << messages2.size() << "!\n\n";
+ }
+ else
+ {
+ cerr << "\nDECODE FAILED [2]: line: " << lineno << " \"" << line << "\"!\n\n";
+ continue;
+ }
+ }
+
+ if(op=="count")
+ {
+ cout << lineno << "\t" << messages2.size() << "\n";
+ continue;
+ }
+ else if(op=="filter")
+ {
+ cout << line << "\n";
+ continue;
+ }
+
+ AntMessage::lookupInVector = true;
+ for (std::vector<AntMessage>::iterator i=messages2.begin(); i!=messages2.end(); i++)
+ {
+ i->idx = lineno;
+ i->sent = (vs[2][0]=='S');
+ AntMessage& m=*i;
+
+ if(op=="usbmon")
+ {
+ messages.push_back(m);
+ }
+ else if(op=="dump")
+ {
+ cout << m.strExt() << "\n";
+ }
+ else
+ {
+ //cout << dt << "\t" << m.str() << "\n";
+ cout << m.strDt(dt) << "\n";
+ }
+ }
+ AntMessage::lookupInVector = false;
+ }
+
+ if(op=="usbmon")
+ {
+ AntMessage::saveAsUsbMon(cout, messages);
+ }
+
+ return EXIT_SUCCESS;
+}
+
+
+#ifdef USE_GOOGLE_TEST
+#include <gtest/gtest.h>
+
+
+TEST(usbmon2ant, Zero0) {
+ EXPECT_EQ(14, 13);
+}
+
+class MyFixture2 : public ::testing::Test
+{
+protected:
+ int foo;
+
+ virtual void SetUp() { foo = 0; }
+};
+TEST_F(MyFixture2, FooStartsAtZero) {
+ EXPECT_EQ(0, foo);
+}
+
+
+//int gtest_PullInMyLibrary_antlib2() { return 0; }
+
+#endif
diff --git a/src/cmake/FindLIBUSB.cmake b/src/cmake/FindLIBUSB.cmake
new file mode 100644
index 0000000..a60f8c3
--- /dev/null
+++ b/src/cmake/FindLIBUSB.cmake
@@ -0,0 +1,61 @@
+# This CMake script wants to use libusb functionality, therefore it looks
+# for libusb include files and libraries.
+#
+# Operating Systems Supported:
+# - Unix (requires pkg-config)
+# Tested with Ubuntu 9.04 and Fedora 11
+# - Windows (requires MSVC)
+# Tested with Windows XP
+#
+# This should work for both 32 bit and 64 bit systems.
+#
+# Author: F. Kooman <fkooman@tuxed.net>
+#
+
+# FreeBSD has built-in libusb since 800069
+IF(CMAKE_SYSTEM_NAME MATCHES FreeBSD)
+ EXEC_PROGRAM(sysctl ARGS -n kern.osreldate OUTPUT_VARIABLE FREEBSD_VERSION)
+ SET(MIN_FREEBSD_VERSION 800068)
+ IF(FREEBSD_VERSION GREATER ${MIN_FREEBSD_VERSION})
+ SET(LIBUSB_FOUND TRUE)
+ SET(LIBUSB_INCLUDE_DIRS "/usr/include")
+ SET(LIBUSB_LIBRARIES "usb")
+ SET(LIBUSB_LIBRARY_DIRS "/usr/lib/")
+ ENDIF(FREEBSD_VERSION GREATER ${MIN_FREEBSD_VERSION})
+ENDIF(CMAKE_SYSTEM_NAME MATCHES FreeBSD)
+
+IF(NOT LIBUSB_FOUND)
+IF(MSVC)
+ # Windows with Microsoft Visual C++
+ FIND_PATH(LIBUSB_INCLUDE_DIRS NAMES usb.h lusb0_usb.h PATHS "$ENV{ProgramFiles}/LibUSB-Win32/include" "${LIBUSB_ROOT}/include")
+ IF(${CMAKE_SYSTEM_PROCESSOR} STREQUAL "x64")
+ # on x64 (win64)
+ FIND_LIBRARY(LIBUSB_LIBRARIES NAMES libusb PATHS "$ENV{ProgramFiles}/LibUSB-Win32/lib/msvc_x64" "${LIBUSB_ROOT}/lib/msvc_x64")
+ ELSE(${CMAKE_SYSTEM_PROCESSOR} STREQUAL "x64")
+ # on x86 (win32)
+ FIND_LIBRARY(LIBUSB_LIBRARIES NAMES libusb PATHS "$ENV{ProgramFiles}/LibUSB-Win32/lib/msvc" "${LIBUSB_ROOT}/lib/msvc")
+ ENDIF(${CMAKE_SYSTEM_PROCESSOR} STREQUAL "x64")
+ELSE(MSVC)
+ # If not MS Visual Studio we use PkgConfig
+ FIND_PACKAGE (PkgConfig)
+ IF(PKG_CONFIG_FOUND)
+ PKG_CHECK_MODULES(LIBUSB REQUIRED libusb)
+ ELSE(PKG_CONFIG_FOUND)
+ MESSAGE(FATAL_ERROR "Could not find PkgConfig")
+ ENDIF(PKG_CONFIG_FOUND)
+ENDIF(MSVC)
+
+IF(LIBUSB_INCLUDE_DIRS AND LIBUSB_LIBRARIES)
+ SET(LIBUSB_FOUND TRUE)
+ENDIF(LIBUSB_INCLUDE_DIRS AND LIBUSB_LIBRARIES)
+ENDIF(NOT LIBUSB_FOUND)
+
+IF(LIBUSB_FOUND)
+ IF(NOT LIBUSB_FIND_QUIETLY)
+ MESSAGE(STATUS "Found LIBUSB: ${LIBUSB_LIBRARIES}")
+ ENDIF (NOT LIBUSB_FIND_QUIETLY)
+ELSE(LIBUSB_FOUND)
+ IF(LIBUSB_FIND_REQUIRED)
+ MESSAGE(FATAL_ERROR "Could not find LIBUSB")
+ ENDIF(LIBUSB_FIND_REQUIRED)
+ENDIF(LIBUSB_FOUND)
diff --git a/src/cmake/Findlibusb-1.0.cmake b/src/cmake/Findlibusb-1.0.cmake
new file mode 100644
index 0000000..ec40055
--- /dev/null
+++ b/src/cmake/Findlibusb-1.0.cmake
@@ -0,0 +1,98 @@
+# - Try to find libusb-1.0
+# Once done this will define
+#
+# LIBUSB_1_FOUND - system has libusb
+# LIBUSB_1_INCLUDE_DIRS - the libusb include directory
+# LIBUSB_1_LIBRARIES - Link these to use libusb
+# LIBUSB_1_DEFINITIONS - Compiler switches required for using libusb
+#
+# Adapted from cmake-modules Google Code project
+#
+# Copyright (c) 2006 Andreas Schneider <mail@cynapses.org>
+#
+# (Changes for libusb) Copyright (c) 2008 Kyle Machulis <kyle@nonpolynomial.com>
+#
+# Redistribution and use is allowed according to the terms of the New BSD license.
+#
+# CMake-Modules Project New BSD License
+#
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions are met:
+#
+# * Redistributions of source code must retain the above copyright notice, this
+# list of conditions and the following disclaimer.
+#
+# * Redistributions in binary form must reproduce the above copyright notice,
+# this list of conditions and the following disclaimer in the
+# documentation and/or other materials provided with the distribution.
+#
+# * Neither the name of the CMake-Modules Project nor the names of its
+# contributors may be used to endorse or promote products derived from this
+# software without specific prior written permission.
+#
+# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
+# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+# WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+# DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
+# ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+# (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+# LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
+# ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+# SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+#
+
+
+if (LIBUSB_1_LIBRARIES AND LIBUSB_1_INCLUDE_DIRS)
+ # in cache already
+ set(LIBUSB_FOUND TRUE)
+else (LIBUSB_1_LIBRARIES AND LIBUSB_1_INCLUDE_DIRS)
+ find_path(LIBUSB_1_INCLUDE_DIR
+ NAMES
+ libusb.h
+ PATHS
+ /usr/include
+ /usr/local/include
+ /opt/local/include
+ /sw/include
+ PATH_SUFFIXES
+ libusb-1.0
+ )
+
+ find_library(LIBUSB_1_LIBRARY
+ NAMES
+ usb-1.0 usb
+ PATHS
+ /usr/lib
+ /usr/local/lib
+ /opt/local/lib
+ /sw/lib
+ )
+
+ set(LIBUSB_1_INCLUDE_DIRS
+ ${LIBUSB_1_INCLUDE_DIR}
+ )
+ set(LIBUSB_1_LIBRARIES
+ ${LIBUSB_1_LIBRARY}
+)
+
+ if (LIBUSB_1_INCLUDE_DIRS AND LIBUSB_1_LIBRARIES)
+ set(LIBUSB_1_FOUND TRUE)
+ endif (LIBUSB_1_INCLUDE_DIRS AND LIBUSB_1_LIBRARIES)
+
+ if (LIBUSB_1_FOUND)
+ if (NOT libusb_1_FIND_QUIETLY)
+ message(STATUS "Found libusb-1.0:")
+ message(STATUS " - Includes: ${LIBUSB_1_INCLUDE_DIRS}")
+ message(STATUS " - Libraries: ${LIBUSB_1_LIBRARIES}")
+ endif (NOT libusb_1_FIND_QUIETLY)
+ else (LIBUSB_1_FOUND)
+ if (libusb_1_FIND_REQUIRED)
+ message(FATAL_ERROR "Could not find libusb")
+ endif (libusb_1_FIND_REQUIRED)
+ endif (LIBUSB_1_FOUND)
+
+ # show the LIBUSB_1_INCLUDE_DIRS and LIBUSB_1_LIBRARIES variables only in the advanced view
+ mark_as_advanced(LIBUSB_1_INCLUDE_DIRS LIBUSB_1_LIBRARIES)
+
+endif (LIBUSB_1_LIBRARIES AND LIBUSB_1_INCLUDE_DIRS)
diff --git a/src/common.cpp b/src/common.cpp
new file mode 100644
index 0000000..9ca101c
--- /dev/null
+++ b/src/common.cpp
@@ -0,0 +1,241 @@
+// -*- mode: c++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2; coding: utf-8-unix -*-
+// ***** BEGIN LICENSE BLOCK *****
+////////////////////////////////////////////////////////////////////
+// Copyright (c) 2011-2013 RALOVICH, Kristóf //
+// //
+// This program is free software; you can redistribute it and/or //
+// modify it under the terms of the GNU General Public License //
+// version 2 as published by the Free Software Foundation. //
+// //
+////////////////////////////////////////////////////////////////////
+// ***** END LICENSE BLOCK *****
+
+
+#include "common.hpp"
+#include <iostream>
+#include <boost/thread/thread_time.hpp>
+#include <boost/thread.hpp>
+#include <boost/tokenizer.hpp>
+#include <boost/foreach.hpp>
+//#include <QDateTime>
+#include <sstream>
+#include <boost/date_time.hpp>
+#include <boost/filesystem.hpp>
+
+
+std::ostream&
+logger()
+{
+ std::cout.flush();
+ return std::cout;
+}
+
+FILE*
+loggerc()
+{
+ fflush(stdout);
+ return stdout;
+}
+
+
+void
+sleepms(const size_t timeout_ms)
+{
+ boost::system_time const timeout=boost::get_system_time() + boost::posix_time::milliseconds(timeout_ms);
+ boost::this_thread::sleep(timeout);
+}
+
+
+#ifndef _MSC_VER
+/**
+ * C++ version 0.4 char* style "itoa":
+ * Written by Lukás Chmela
+ * Released under GPLv3.
+ */
+char*
+itoa(int value, char* result, int base)
+{
+ // check that the base if valid
+ if (base < 2 || base > 36) { *result = '\0'; return result; }
+
+ char* ptr = result, *ptr1 = result, tmp_char;
+ int tmp_value;
+
+ do {
+ tmp_value = value;
+ value /= base;
+ *ptr++ = "zyxwvutsrqponmlkjihgfedcba9876543210123456789abcdefghijklmnopqrstuvwxyz" [35 + (tmp_value - value * base)];
+ } while ( value );
+
+ // Apply negative sign
+ if (tmp_value < 0) *ptr++ = '-';
+ *ptr-- = '\0';
+ while(ptr1 < ptr) {
+ tmp_char = *ptr;
+ *ptr--= *ptr1;
+ *ptr1++ = tmp_char;
+ }
+ return result;
+}
+#endif
+
+
+template < class T >
+const std::string
+toString(const T& val, const int width, const char fill)
+{
+ std::stringstream clStr;
+ if(width != -1) clStr << std::setw(width);
+ if(fill != ' ') clStr << std::setfill(fill);
+ clStr << std::hex;
+ clStr << val;
+ return clStr.str();
+}
+
+
+template < class T >
+const std::string
+toStringDec(const T& val, const int width, const char fill)
+{
+ std::stringstream clStr;
+ if(width != -1) clStr << std::setw(width);
+ if(fill != ' ') clStr << std::setfill(fill);
+ clStr << val;
+ return clStr.str();
+}
+
+const std::vector<std::string>
+tokenize(const std::string& text, const char* delims)
+{
+ std::vector<std::string> rv;
+ boost::char_separator<char> sep(delims);
+ boost::tokenizer<boost::char_separator<char> > tokens(text, sep);
+ BOOST_FOREACH(std::string t, tokens)
+ {
+ rv.push_back(t);
+ }
+ return rv;
+}
+
+const std::string
+getDateString()
+{
+ //QString filename = QDateTime::currentDateTime().toString("yyyy_MM_dd_hh_mm_ss");
+ //QByteArray ba=filename.toLatin1();
+ //return ba.constData();
+ std::ostringstream msg;
+ const boost::posix_time::ptime now=boost::posix_time::second_clock::local_time();
+ boost::posix_time::time_facet*const f=new boost::posix_time::time_facet("%Y_%m_%d_%H_%M_%S");
+ msg.imbue(std::locale(msg.getloc(),f));
+ msg << now;
+ return msg.str();
+}
+
+
+const std::string
+getConfigFileName()
+{
+ return getConfigFolder() + "/config.ini";
+}
+
+
+const std::string
+getConfigFolder()
+{
+ const char* e0 = getenv("ANTPM_DIR");
+ if(e0 && strlen(e0) > 0)
+ return std::string(e0) + "/";
+
+ // http://standards.freedesktop.org/basedir-spec/basedir-spec-latest.html
+ // $XDG_CONFIG_HOME defines the base directory relative to which user specific configuration files should be stored.
+ // If $XDG_CONFIG_HOME is either not set or empty, a default equal to $HOME/.config should be used.
+
+#ifndef _WIN32
+ const char* e1 = getenv("XDG_CONFIG_HOME");
+ const char* e2 = getenv("HOME");
+ if(e1 && strlen(e1) > 0)
+ return std::string(e1) + "/"APP_NAME"/";
+ else if(e2 && strlen(e2) > 0)
+ return std::string(e2) + "/.config/"APP_NAME"/";
+#else
+ const char* e3 = getenv("USERPROFILE");
+ if(e3 && strlen(e3) > 0)
+ return std::string(e3) + "/.config/"APP_NAME"/";
+#endif
+ else
+ return "~/.config/"APP_NAME"/";
+}
+
+
+
+void
+readUInt64(const uint clientSN, uint64_t& pairedKey)
+{
+ std::stringstream ss;
+ ss << getConfigFolder() << "libantpmauth_" << clientSN;
+ FILE* f=fopen(ss.str().c_str(), "r");
+ LOG_VAR2(ss.str(), f);
+ if(f)
+ {
+ if(1 != fread(&pairedKey, sizeof(pairedKey), 1, f))
+ pairedKey = 0;
+ fclose(f);
+ }
+}
+
+void
+writeUInt64(const uint clientSN, const uint64_t& pairedKey)
+{
+ std::stringstream ss;
+ ss << getConfigFolder() << "libantpmauth_" << clientSN;
+ FILE* f=fopen(ss.str().c_str(), "w+");
+ LOG_VAR2(ss.str(), f);
+ if(f)
+ {
+ fwrite(&pairedKey, sizeof(pairedKey), 1, f);
+ fclose(f);
+ }
+}
+
+std::vector<uchar>
+readFile(const char* fileName)
+{
+ std::vector<uchar> v;
+ FILE* f=fopen(fileName, "rb");
+ if(f)
+ {
+ uchar buf[256];
+ size_t lastRead=0;
+ do
+ {
+ lastRead=fread(buf, 1, 256, f);
+ v.insert(v.end(), buf, buf+lastRead);
+ } while(lastRead>0);
+ fclose(f);
+ }
+ return v;
+}
+
+
+bool
+mkDir(const char* dirName)
+{
+ //QDir qdir; CHECK_RETURN(qdir.mkpath(folder.c_str()));
+ boost::filesystem::path ddir(dirName);
+ return boost::filesystem::create_directories(ddir);
+}
+
+// explicit template instantiation
+
+template const std::string toString(const int& val, const int width, const char fill);
+template const std::string toString(const char& val, const int width, const char fill);
+template const std::string toString(const unsigned char& val, const int width, const char fill);
+template const std::string toString(const unsigned int& val, const int width, const char fill);
+template const std::string toString(const unsigned short& val, const int width, const char fill);
+template const std::string toString(const uint64_t& val, const int width, const char fill);
+
+
+template const std::string toStringDec(const int& val, const int width, const char fill);
+template const std::string toStringDec(const unsigned long& val, const int width, const char fill);
+template const std::string toStringDec(const double& val, const int width, const char fill);
+template const std::string toStringDec(const unsigned int& val, const int width, const char fill);
diff --git a/src/common.hpp b/src/common.hpp
new file mode 100644
index 0000000..6ec7a82
--- /dev/null
+++ b/src/common.hpp
@@ -0,0 +1,82 @@
+// -*- mode: c++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2; coding: utf-8-unix -*-
+// ***** BEGIN LICENSE BLOCK *****
+////////////////////////////////////////////////////////////////////
+// Copyright (c) 2011-2013 RALOVICH, Kristóf //
+// //
+// This program is free software; you can redistribute it and/or //
+// modify it under the terms of the GNU General Public License //
+// version 2 as published by the Free Software Foundation. //
+// //
+////////////////////////////////////////////////////////////////////
+// ***** END LICENSE BLOCK *****
+#pragma once
+
+#include <ostream>
+#include <string>
+#include <vector>
+#include "antdefs.hpp"
+
+#define CHECK_RETURN_FALSE(x) do { if(!(x)) { printf("E: %s\n", #x); return false; } /*else { printf("ok: %s\n", #x); }*/ } while(0)
+#define CHECK_RETURN_FALSE_LOG_OK(x) do { if(!(x)) { printf("E: %s\n", #x); return false; } else { printf("ok: %s\n", #x); } } while(0)
+#define CHECK_RETURN_RV(x, rv) do { if(!(x)) { printf("E: %s\n", #x); return rv; } /*else { printf("ok: %s\n", #x); }*/ } while(0)
+#define CHECK_RETURN_RV_LOG_OK(x, rv) do { if(!(x)) { printf("E: %s\n", #x); return rv; } else { printf("ok: %s\n", #x); } } while(0)
+#define CHECK_RETURN(x) do { if(!(x)) { printf("E: %s\n", #x); return; } else { printf("ok: %s\n", #x); } fflush(stdout); } while(0)
+#define LOG_VAR(x) do { logger() << #x << "= " << x << std::endl; logger().flush(); } while(0)
+#define LOG_VAR2(x,y) do { logger() << #x << "= " << x << ", " #y << "= " << y << std::endl; logger().flush(); } while(0)
+#define LOG_VAR3(x,y, z) do { logger() << #x "= " << x << ", " #y "= " << y << ", " #z "= " << z << std::endl; logger().flush(); } while(0)
+#define LOG_VAR4(x,y, z, w) do { logger() << #x "= " << x << ", " #y "= " << y << ", " #z "= " << z << ", " #w "= " << w << std::endl; logger().flush(); } while(0)
+
+std::ostream& logger();
+FILE* loggerc();
+
+void sleepms(const size_t timeout_ms);
+
+#ifndef _MSC_VER
+char* itoa(int value, char* result, int base);
+#endif
+
+///
+template < class T >
+const std::string
+toString(const T& val, const int width = -1, const char fill = ' ');
+template < class T >
+const std::string
+toStringDec(const T& val, const int width = -1, const char fill = ' ');
+
+const std::vector<std::string> tokenize(const std::string& text, const char* delims);
+
+extern
+const std::string
+getDateString();
+
+const std::string
+getConfigFileName();
+
+const std::string
+getConfigFolder();
+
+void
+readUInt64(const uint clientSN, uint64_t& pairedKey);
+
+void
+writeUInt64(const uint clientSN, const uint64_t& ui);
+
+std::vector<uchar>
+readFile(const char* fileName);
+
+bool
+mkDir(const char* dirName);
+
+enum
+{
+ ANTPM_RETRIES=10,
+ ANTPM_RETRY_MS=1000,
+ ANTPM_MAX_CHANNELS=56
+};
+
+#define APP_NAME "antpm"
+#ifndef _WIN32
+# define ANTPM_SERIAL_IMPL SerialTty
+#else
+# define ANTPM_SERIAL_IMPL SerialUsb
+#endif
diff --git a/src/lqueue.hpp b/src/lqueue.hpp
new file mode 100644
index 0000000..52b2ce5
--- /dev/null
+++ b/src/lqueue.hpp
@@ -0,0 +1,236 @@
+// -*- mode: c++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2; coding: utf-8-unix -*-
+// ***** BEGIN LICENSE BLOCK *****
+////////////////////////////////////////////////////////////////////
+// Copyright (c) 2011-2013 RALOVICH, Kristóf //
+// //
+// This program is free software; you can redistribute it and/or //
+// modify it under the terms of the GNU General Public License //
+// version 2 as published by the Free Software Foundation. //
+// //
+////////////////////////////////////////////////////////////////////
+// ***** END LICENSE BLOCK *****
+#pragma once
+
+
+#include <queue>
+#include <boost/thread.hpp>
+#include <boost/scoped_ptr.hpp>
+#include <list>
+
+
+
+
+
+template <typename DataType>
+class lqueue2
+{
+public:
+ typedef std::list<DataType> DataList;
+public:
+ bool empty() const
+ {
+ boost::mutex::scoped_lock lock(m_mtx);
+ return m_q.empty();
+ }
+
+ void push(DataType const& data)
+ {
+ boost::mutex::scoped_lock lock(m_mtx);
+ m_q.push_back(data);
+ m_pushEvent.notify_all();
+ }
+
+ void pushArray(DataType const* data, const size_t nItems)
+ {
+ boost::mutex::scoped_lock lock(m_mtx);
+ for(size_t i = 0; i < nItems; i++)
+ m_q.push_back(data[i]);
+ m_pushEvent.notify_all();
+ }
+
+
+ const typename std::queue<DataType>::size_type
+ size() const
+ {
+ boost::unique_lock<boost::mutex> lock(m_mtx);
+ return m_q.size();
+ }
+
+ const DataList
+ getListCopy() const
+ {
+ boost::unique_lock<boost::mutex> lock(m_mtx);
+ return m_q; // copy
+ }
+
+ void
+ clear()
+ {
+ boost::unique_lock<boost::mutex> lock(m_mtx);
+ m_q.clear();
+ }
+
+protected:
+ mutable boost::mutex m_mtx;
+ boost::condition_variable m_pushEvent;
+ DataList m_q;
+};
+
+
+
+// implements push consumer
+template < class DataType>
+class lqueue3 : public lqueue2<DataType>
+{
+public:
+ typedef boost::function<bool (DataType&)> Listener;
+ typedef boost::function<bool (std::vector<DataType>&)> Listener2;
+ typedef lqueue2<DataType> Super;
+
+ struct ListenerProc
+ {
+ void operator() (lqueue3* This)
+ {
+ This->eventLoop();
+ }
+ };
+
+
+ lqueue3(bool eventLoopInBgTh = true)
+ : stop(false)
+ {
+ if(eventLoopInBgTh)
+ th_listener.reset( new boost::thread(lp, this) );
+ }
+
+ ~lqueue3()
+ {
+ stop = true;
+ if(th_listener.get())
+ th_listener->join();
+ }
+
+ void
+ kill()
+ {
+ stop = true;
+ }
+
+ void
+ setOnDataArrivedCallback(Listener2 l)
+ {
+ mCallback = l;
+ }
+
+public:
+ void eventLoop()
+ {
+ while(!stop)
+ {
+ boost::unique_lock<boost::mutex> lock(Super::m_mtx);
+
+ boost::posix_time::time_duration td = boost::posix_time::milliseconds(2000);
+ if(!Super::m_pushEvent.timed_wait(lock, td)) // will automatically and atomically unlock mutex while it waits
+ {
+ //std::cout << "no event before timeout\n";
+ continue;
+ }
+
+ assert(!Super::m_q.empty());
+ size_t s = Super::m_q.size();
+ std::vector<DataType> v(s);
+ for(size_t i = 0; i < s; i++)
+ {
+ v[i] = Super::m_q.front();
+ Super::m_q.pop_front();
+ }
+ if(mCallback)
+ bool rv = mCallback(v);
+ }
+ }
+
+protected:
+ ListenerProc lp;
+ boost::scoped_ptr<boost::thread> th_listener;
+ volatile bool stop;
+ Listener2 mCallback;
+};
+
+
+// implements poll-able pop consumer
+template < class DataType>
+class lqueue4 : public lqueue2<DataType>
+{
+public:
+ typedef lqueue2<DataType> Super;
+
+ template < class Cmp >
+ bool
+ tryFindPop(DataType& needle, Cmp cmp)
+ {
+ boost::mutex::scoped_lock lock(Super::m_mtx);
+ typename Super::DataList::iterator i;
+ for(i = Super::m_q.begin(); i != Super::m_q.end(); i++)
+ {
+ if(cmp(needle, *i))
+ break;
+ }
+ if(i==Super::m_q.end())
+ return false;
+ needle = *i; // copy
+ Super::m_q.erase(i);
+ return true;
+ }
+ bool
+ pop(DataType& data, const size_t timeout = 0)
+ {
+ boost::mutex::scoped_lock lock(Super::m_mtx);
+
+ /// if queue empty, wait untile timeout if there was anything pushed
+ if(Super::m_q.empty() && timeout > 0)
+ {
+ boost::posix_time::time_duration td = boost::posix_time::milliseconds(timeout);
+ if(!Super::m_pushEvent.timed_wait(lock, td))
+ return false;
+ }
+ assert(!Super::m_q.empty());
+
+ data = Super::m_q.front();
+ Super::m_q.pop_front();
+ return true;
+ }
+
+ bool
+ popArray(DataType* dst, const size_t sizeBytes, size_t& bytesRead, const size_t timeout = 0)
+ {
+ if(!dst)
+ return false;
+
+ boost::unique_lock<boost::mutex> lock(Super::m_mtx);
+
+ /// if queue empty, wait until timeout if there was anything pushed
+ if(Super::m_q.empty() && timeout > 0)
+ {
+ boost::posix_time::time_duration td = boost::posix_time::milliseconds(timeout);
+ if(!Super::m_pushEvent.timed_wait(lock, td))
+ {
+ bytesRead = 0;
+ return false;
+ }
+ }
+ assert(!Super::m_q.empty());
+
+ size_t s = Super::m_q.size();
+ s = std::min(s, sizeBytes);
+ for(size_t i = 0; i < s; i++)
+ {
+ *(dst+i) = Super::m_q.front();
+ Super::m_q.pop_front();
+ }
+ bytesRead = s;
+
+ return true;
+ }
+};
+
+
diff --git a/src/stdintfwd.hpp b/src/stdintfwd.hpp
new file mode 100644
index 0000000..3a4176d
--- /dev/null
+++ b/src/stdintfwd.hpp
@@ -0,0 +1,23 @@
+// -*- mode: c++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2; coding: utf-8-unix -*-
+// ***** BEGIN LICENSE BLOCK *****
+////////////////////////////////////////////////////////////////////
+// Copyright (c) 2011-2013 RALOVICH, Kristóf //
+// //
+// This program is free software; you can redistribute it and/or //
+// modify it under the terms of the GNU General Public License //
+// version 2 as published by the Free Software Foundation. //
+// //
+////////////////////////////////////////////////////////////////////
+// ***** END LICENSE BLOCK *****
+#pragma once
+
+#ifndef _MSC_VER // [
+
+//#error "Use this header only with Microsoft Visual C++ compilers!"
+#include <boost/cstdint.hpp>
+
+#else
+
+#include "w_stdint.h"
+
+#endif // _MSC_VER ]
diff --git a/src/w_inttypes.h b/src/w_inttypes.h
new file mode 100644
index 0000000..f69277c
--- /dev/null
+++ b/src/w_inttypes.h
@@ -0,0 +1,305 @@
+// ISO C9x compliant inttypes.h for Microsoft Visual Studio
+// Based on ISO/IEC 9899:TC2 Committee draft (May 6, 2005) WG14/N1124
+//
+// Copyright (c) 2006 Alexander Chemeris
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are met:
+//
+// 1. Redistributions of source code must retain the above copyright notice,
+// this list of conditions and the following disclaimer.
+//
+// 2. Redistributions in binary form must reproduce the above copyright
+// notice, this list of conditions and the following disclaimer in the
+// documentation and/or other materials provided with the distribution.
+//
+// 3. The name of the author may be used to endorse or promote products
+// derived from this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
+// WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+// MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
+// EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
+// OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+// WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
+// OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
+// ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+//
+///////////////////////////////////////////////////////////////////////////////
+
+#ifndef _MSC_VER // [
+#error "Use this header only with Microsoft Visual C++ compilers!"
+#endif // _MSC_VER ]
+
+#ifndef _MSC_INTTYPES_H_ // [
+#define _MSC_INTTYPES_H_
+
+#if _MSC_VER > 1000
+#pragma once
+#endif
+
+#include "w_stdint.h"
+
+// 7.8 Format conversion of integer types
+
+typedef struct {
+ intmax_t quot;
+ intmax_t rem;
+} imaxdiv_t;
+
+// 7.8.1 Macros for format specifiers
+
+#if !defined(__cplusplus) || defined(__STDC_FORMAT_MACROS) // [ See footnote 185 at page 198
+
+// The fprintf macros for signed integers are:
+#define PRId8 "d"
+#define PRIi8 "i"
+#define PRIdLEAST8 "d"
+#define PRIiLEAST8 "i"
+#define PRIdFAST8 "d"
+#define PRIiFAST8 "i"
+
+#define PRId16 "hd"
+#define PRIi16 "hi"
+#define PRIdLEAST16 "hd"
+#define PRIiLEAST16 "hi"
+#define PRIdFAST16 "hd"
+#define PRIiFAST16 "hi"
+
+#define PRId32 "I32d"
+#define PRIi32 "I32i"
+#define PRIdLEAST32 "I32d"
+#define PRIiLEAST32 "I32i"
+#define PRIdFAST32 "I32d"
+#define PRIiFAST32 "I32i"
+
+#define PRId64 "I64d"
+#define PRIi64 "I64i"
+#define PRIdLEAST64 "I64d"
+#define PRIiLEAST64 "I64i"
+#define PRIdFAST64 "I64d"
+#define PRIiFAST64 "I64i"
+
+#define PRIdMAX "I64d"
+#define PRIiMAX "I64i"
+
+#define PRIdPTR "Id"
+#define PRIiPTR "Ii"
+
+// The fprintf macros for unsigned integers are:
+#define PRIo8 "o"
+#define PRIu8 "u"
+#define PRIx8 "x"
+#define PRIX8 "X"
+#define PRIoLEAST8 "o"
+#define PRIuLEAST8 "u"
+#define PRIxLEAST8 "x"
+#define PRIXLEAST8 "X"
+#define PRIoFAST8 "o"
+#define PRIuFAST8 "u"
+#define PRIxFAST8 "x"
+#define PRIXFAST8 "X"
+
+#define PRIo16 "ho"
+#define PRIu16 "hu"
+#define PRIx16 "hx"
+#define PRIX16 "hX"
+#define PRIoLEAST16 "ho"
+#define PRIuLEAST16 "hu"
+#define PRIxLEAST16 "hx"
+#define PRIXLEAST16 "hX"
+#define PRIoFAST16 "ho"
+#define PRIuFAST16 "hu"
+#define PRIxFAST16 "hx"
+#define PRIXFAST16 "hX"
+
+#define PRIo32 "I32o"
+#define PRIu32 "I32u"
+#define PRIx32 "I32x"
+#define PRIX32 "I32X"
+#define PRIoLEAST32 "I32o"
+#define PRIuLEAST32 "I32u"
+#define PRIxLEAST32 "I32x"
+#define PRIXLEAST32 "I32X"
+#define PRIoFAST32 "I32o"
+#define PRIuFAST32 "I32u"
+#define PRIxFAST32 "I32x"
+#define PRIXFAST32 "I32X"
+
+#define PRIo64 "I64o"
+#define PRIu64 "I64u"
+#define PRIx64 "I64x"
+#define PRIX64 "I64X"
+#define PRIoLEAST64 "I64o"
+#define PRIuLEAST64 "I64u"
+#define PRIxLEAST64 "I64x"
+#define PRIXLEAST64 "I64X"
+#define PRIoFAST64 "I64o"
+#define PRIuFAST64 "I64u"
+#define PRIxFAST64 "I64x"
+#define PRIXFAST64 "I64X"
+
+#define PRIoMAX "I64o"
+#define PRIuMAX "I64u"
+#define PRIxMAX "I64x"
+#define PRIXMAX "I64X"
+
+#define PRIoPTR "Io"
+#define PRIuPTR "Iu"
+#define PRIxPTR "Ix"
+#define PRIXPTR "IX"
+
+// The fscanf macros for signed integers are:
+#define SCNd8 "d"
+#define SCNi8 "i"
+#define SCNdLEAST8 "d"
+#define SCNiLEAST8 "i"
+#define SCNdFAST8 "d"
+#define SCNiFAST8 "i"
+
+#define SCNd16 "hd"
+#define SCNi16 "hi"
+#define SCNdLEAST16 "hd"
+#define SCNiLEAST16 "hi"
+#define SCNdFAST16 "hd"
+#define SCNiFAST16 "hi"
+
+#define SCNd32 "ld"
+#define SCNi32 "li"
+#define SCNdLEAST32 "ld"
+#define SCNiLEAST32 "li"
+#define SCNdFAST32 "ld"
+#define SCNiFAST32 "li"
+
+#define SCNd64 "I64d"
+#define SCNi64 "I64i"
+#define SCNdLEAST64 "I64d"
+#define SCNiLEAST64 "I64i"
+#define SCNdFAST64 "I64d"
+#define SCNiFAST64 "I64i"
+
+#define SCNdMAX "I64d"
+#define SCNiMAX "I64i"
+
+#ifdef _WIN64 // [
+# define SCNdPTR "I64d"
+# define SCNiPTR "I64i"
+#else // _WIN64 ][
+# define SCNdPTR "ld"
+# define SCNiPTR "li"
+#endif // _WIN64 ]
+
+// The fscanf macros for unsigned integers are:
+#define SCNo8 "o"
+#define SCNu8 "u"
+#define SCNx8 "x"
+#define SCNX8 "X"
+#define SCNoLEAST8 "o"
+#define SCNuLEAST8 "u"
+#define SCNxLEAST8 "x"
+#define SCNXLEAST8 "X"
+#define SCNoFAST8 "o"
+#define SCNuFAST8 "u"
+#define SCNxFAST8 "x"
+#define SCNXFAST8 "X"
+
+#define SCNo16 "ho"
+#define SCNu16 "hu"
+#define SCNx16 "hx"
+#define SCNX16 "hX"
+#define SCNoLEAST16 "ho"
+#define SCNuLEAST16 "hu"
+#define SCNxLEAST16 "hx"
+#define SCNXLEAST16 "hX"
+#define SCNoFAST16 "ho"
+#define SCNuFAST16 "hu"
+#define SCNxFAST16 "hx"
+#define SCNXFAST16 "hX"
+
+#define SCNo32 "lo"
+#define SCNu32 "lu"
+#define SCNx32 "lx"
+#define SCNX32 "lX"
+#define SCNoLEAST32 "lo"
+#define SCNuLEAST32 "lu"
+#define SCNxLEAST32 "lx"
+#define SCNXLEAST32 "lX"
+#define SCNoFAST32 "lo"
+#define SCNuFAST32 "lu"
+#define SCNxFAST32 "lx"
+#define SCNXFAST32 "lX"
+
+#define SCNo64 "I64o"
+#define SCNu64 "I64u"
+#define SCNx64 "I64x"
+#define SCNX64 "I64X"
+#define SCNoLEAST64 "I64o"
+#define SCNuLEAST64 "I64u"
+#define SCNxLEAST64 "I64x"
+#define SCNXLEAST64 "I64X"
+#define SCNoFAST64 "I64o"
+#define SCNuFAST64 "I64u"
+#define SCNxFAST64 "I64x"
+#define SCNXFAST64 "I64X"
+
+#define SCNoMAX "I64o"
+#define SCNuMAX "I64u"
+#define SCNxMAX "I64x"
+#define SCNXMAX "I64X"
+
+#ifdef _WIN64 // [
+# define SCNoPTR "I64o"
+# define SCNuPTR "I64u"
+# define SCNxPTR "I64x"
+# define SCNXPTR "I64X"
+#else // _WIN64 ][
+# define SCNoPTR "lo"
+# define SCNuPTR "lu"
+# define SCNxPTR "lx"
+# define SCNXPTR "lX"
+#endif // _WIN64 ]
+
+#endif // __STDC_FORMAT_MACROS ]
+
+// 7.8.2 Functions for greatest-width integer types
+
+// 7.8.2.1 The imaxabs function
+#define imaxabs _abs64
+
+// 7.8.2.2 The imaxdiv function
+
+// This is modified version of div() function from Microsoft's div.c found
+// in %MSVC.NET%\crt\src\div.c
+#ifdef STATIC_IMAXDIV // [
+static
+#else // STATIC_IMAXDIV ][
+_inline
+#endif // STATIC_IMAXDIV ]
+imaxdiv_t __cdecl imaxdiv(intmax_t numer, intmax_t denom)
+{
+ imaxdiv_t result;
+
+ result.quot = numer / denom;
+ result.rem = numer % denom;
+
+ if (numer < 0 && result.rem > 0) {
+ // did division wrong; must fix up
+ ++result.quot;
+ result.rem -= denom;
+ }
+
+ return result;
+}
+
+// 7.8.2.3 The strtoimax and strtoumax functions
+#define strtoimax _strtoi64
+#define strtoumax _strtoui64
+
+// 7.8.2.4 The wcstoimax and wcstoumax functions
+#define wcstoimax _wcstoi64
+#define wcstoumax _wcstoui64
+
+
+#endif // _MSC_INTTYPES_H_ ]
diff --git a/src/w_stdint.h b/src/w_stdint.h
new file mode 100644
index 0000000..dc495e8
--- /dev/null
+++ b/src/w_stdint.h
@@ -0,0 +1,248 @@
+// ISO C9x compliant stdint.h for Microsoft Visual Studio
+// Based on ISO/IEC 9899:TC2 Committee draft (May 6, 2005) WG14/N1124
+//
+// Copyright (c) 2006-2008 Alexander Chemeris
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are met:
+//
+// 1. Redistributions of source code must retain the above copyright notice,
+// this list of conditions and the following disclaimer.
+//
+// 2. Redistributions in binary form must reproduce the above copyright
+// notice, this list of conditions and the following disclaimer in the
+// documentation and/or other materials provided with the distribution.
+//
+// 3. The name of the author may be used to endorse or promote products
+// derived from this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
+// WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+// MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
+// EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
+// OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+// WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
+// OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
+// ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+//
+///////////////////////////////////////////////////////////////////////////////
+
+#ifndef _MSC_VER // [
+#error "Use this header only with Microsoft Visual C++ compilers!"
+#endif // _MSC_VER ]
+
+#ifndef _MSC_STDINT_H_ // [
+#define _MSC_STDINT_H_
+
+#if _MSC_VER > 1000
+#pragma once
+#endif
+
+#include <limits.h>
+
+// For Visual Studio 6 in C++ mode and for many Visual Studio versions when
+// compiling for ARM we should wrap <wchar.h> include with 'extern "C++" {}'
+// or compiler give many errors like this:
+// error C2733: second C linkage of overloaded function 'wmemchr' not allowed
+#ifdef __cplusplus
+extern "C" {
+#endif
+# include <wchar.h>
+#ifdef __cplusplus
+}
+#endif
+
+// Define _W64 macros to mark types changing their size, like intptr_t.
+#ifndef _W64
+# if !defined(__midl) && (defined(_X86_) || defined(_M_IX86)) && _MSC_VER >= 1300
+# define _W64 __w64
+# else
+# define _W64
+# endif
+#endif
+
+
+// 7.18.1 Integer types
+
+// 7.18.1.1 Exact-width integer types
+
+// Visual Studio 6 and Embedded Visual C++ 4 doesn't
+// realize that, e.g. char has the same size as __int8
+// so we give up on __intX for them.
+#if (_MSC_VER < 1300)
+ typedef signed char int8_t;
+ typedef signed short int16_t;
+ typedef signed int int32_t;
+ typedef unsigned char uint8_t;
+ typedef unsigned short uint16_t;
+ typedef unsigned int uint32_t;
+#else
+ typedef signed __int8 int8_t;
+ typedef signed __int16 int16_t;
+ typedef signed __int32 int32_t;
+ typedef unsigned __int8 uint8_t;
+ typedef unsigned __int16 uint16_t;
+ typedef unsigned __int32 uint32_t;
+#endif
+typedef signed __int64 int64_t;
+typedef unsigned __int64 uint64_t;
+
+
+// 7.18.1.2 Minimum-width integer types
+typedef int8_t int_least8_t;
+typedef int16_t int_least16_t;
+typedef int32_t int_least32_t;
+typedef int64_t int_least64_t;
+typedef uint8_t uint_least8_t;
+typedef uint16_t uint_least16_t;
+typedef uint32_t uint_least32_t;
+typedef uint64_t uint_least64_t;
+
+// 7.18.1.3 Fastest minimum-width integer types
+typedef int8_t int_fast8_t;
+typedef int16_t int_fast16_t;
+typedef int32_t int_fast32_t;
+typedef int64_t int_fast64_t;
+typedef uint8_t uint_fast8_t;
+typedef uint16_t uint_fast16_t;
+typedef uint32_t uint_fast32_t;
+typedef uint64_t uint_fast64_t;
+
+// 7.18.1.4 Integer types capable of holding object pointers
+#ifdef _WIN64 // [
+ typedef signed __int64 intptr_t;
+ typedef unsigned __int64 uintptr_t;
+#else // _WIN64 ][
+ typedef _W64 signed int intptr_t;
+ typedef _W64 unsigned int uintptr_t;
+#endif // _WIN64 ]
+
+// 7.18.1.5 Greatest-width integer types
+typedef int64_t intmax_t;
+typedef uint64_t uintmax_t;
+
+
+// 7.18.2 Limits of specified-width integer types
+
+#if !defined(__cplusplus) || defined(__STDC_LIMIT_MACROS) // [ See footnote 220 at page 257 and footnote 221 at page 259
+
+// 7.18.2.1 Limits of exact-width integer types
+#define INT8_MIN ((int8_t)_I8_MIN)
+#define INT8_MAX _I8_MAX
+#define INT16_MIN ((int16_t)_I16_MIN)
+#define INT16_MAX _I16_MAX
+#define INT32_MIN ((int32_t)_I32_MIN)
+#define INT32_MAX _I32_MAX
+#define INT64_MIN ((int64_t)_I64_MIN)
+#define INT64_MAX _I64_MAX
+#define UINT8_MAX _UI8_MAX
+#define UINT16_MAX _UI16_MAX
+#define UINT32_MAX _UI32_MAX
+#define UINT64_MAX _UI64_MAX
+
+// 7.18.2.2 Limits of minimum-width integer types
+#define INT_LEAST8_MIN INT8_MIN
+#define INT_LEAST8_MAX INT8_MAX
+#define INT_LEAST16_MIN INT16_MIN
+#define INT_LEAST16_MAX INT16_MAX
+#define INT_LEAST32_MIN INT32_MIN
+#define INT_LEAST32_MAX INT32_MAX
+#define INT_LEAST64_MIN INT64_MIN
+#define INT_LEAST64_MAX INT64_MAX
+#define UINT_LEAST8_MAX UINT8_MAX
+#define UINT_LEAST16_MAX UINT16_MAX
+#define UINT_LEAST32_MAX UINT32_MAX
+#define UINT_LEAST64_MAX UINT64_MAX
+
+// 7.18.2.3 Limits of fastest minimum-width integer types
+#define INT_FAST8_MIN INT8_MIN
+#define INT_FAST8_MAX INT8_MAX
+#define INT_FAST16_MIN INT16_MIN
+#define INT_FAST16_MAX INT16_MAX
+#define INT_FAST32_MIN INT32_MIN
+#define INT_FAST32_MAX INT32_MAX
+#define INT_FAST64_MIN INT64_MIN
+#define INT_FAST64_MAX INT64_MAX
+#define UINT_FAST8_MAX UINT8_MAX
+#define UINT_FAST16_MAX UINT16_MAX
+#define UINT_FAST32_MAX UINT32_MAX
+#define UINT_FAST64_MAX UINT64_MAX
+
+// 7.18.2.4 Limits of integer types capable of holding object pointers
+#ifdef _WIN64 // [
+# define INTPTR_MIN INT64_MIN
+# define INTPTR_MAX INT64_MAX
+# define UINTPTR_MAX UINT64_MAX
+#else // _WIN64 ][
+# define INTPTR_MIN INT32_MIN
+# define INTPTR_MAX INT32_MAX
+# define UINTPTR_MAX UINT32_MAX
+#endif // _WIN64 ]
+
+// 7.18.2.5 Limits of greatest-width integer types
+#define INTMAX_MIN INT64_MIN
+#define INTMAX_MAX INT64_MAX
+#define UINTMAX_MAX UINT64_MAX
+
+// 7.18.3 Limits of other integer types
+
+#ifdef _WIN64 // [
+# define PTRDIFF_MIN _I64_MIN
+# define PTRDIFF_MAX _I64_MAX
+#else // _WIN64 ][
+# define PTRDIFF_MIN _I32_MIN
+# define PTRDIFF_MAX _I32_MAX
+#endif // _WIN64 ]
+
+#define SIG_ATOMIC_MIN INT_MIN
+#define SIG_ATOMIC_MAX INT_MAX
+
+#ifndef SIZE_MAX // [
+# ifdef _WIN64 // [
+# define SIZE_MAX _UI64_MAX
+# else // _WIN64 ][
+# define SIZE_MAX _UI32_MAX
+# endif // _WIN64 ]
+#endif // SIZE_MAX ]
+
+// WCHAR_MIN and WCHAR_MAX are also defined in <wchar.h>
+#ifndef WCHAR_MIN // [
+# define WCHAR_MIN 0
+#endif // WCHAR_MIN ]
+#ifndef WCHAR_MAX // [
+# define WCHAR_MAX _UI16_MAX
+#endif // WCHAR_MAX ]
+
+#define WINT_MIN 0
+#define WINT_MAX _UI16_MAX
+
+#endif // __STDC_LIMIT_MACROS ]
+
+
+// 7.18.4 Limits of other integer types
+
+#if !defined(__cplusplus) || defined(__STDC_CONSTANT_MACROS) // [ See footnote 224 at page 260
+
+// 7.18.4.1 Macros for minimum-width integer constants
+
+#define INT8_C(val) val##i8
+#define INT16_C(val) val##i16
+#define INT32_C(val) val##i32
+#define INT64_C(val) val##i64
+
+#define UINT8_C(val) val##ui8
+#define UINT16_C(val) val##ui16
+#define UINT32_C(val) val##ui32
+#define UINT64_C(val) val##ui64
+
+// 7.18.4.2 Macros for greatest-width integer constants
+#define INTMAX_C INT64_C
+#define UINTMAX_C UINT64_C
+
+#endif // __STDC_CONSTANT_MACROS ]
+
+
+#endif // _MSC_STDINT_H_ ]
+