--- /dev/null
+<?xml version="1.0"?>
+<glade-interface>
+ <!-- interface-requires gtk+ 2.16 -->
+ <!-- interface-naming-policy toplevel-contextual -->
+ <widget class="GtkDialog" id="SettingsDialog">
+ <property name="title" translatable="yes" context="yes">Gant Settings</property>
+ <property name="default_width">318</property>
+ <property name="default_height">260</property>
+ <property name="type_hint">menu</property>
+ <child internal-child="vbox">
+ <widget class="GtkVBox" id="dialog1-vbox">
+ <property name="visible">True</property>
+ <child>
+ <widget class="GtkTable" id="table1">
+ <property name="visible">True</property>
+ <property name="n_rows">3</property>
+ <property name="n_columns">2</property>
+ <child>
+ <widget class="GtkLabel" id="label22">
+ <property name="visible">True</property>
+ <property name="xalign">0</property>
+ <property name="label" translatable="yes" context="yes">label22</property>
+ </widget>
+ </child>
+ <child>
+ <widget class="GtkLabel" id="label23">
+ <property name="visible">True</property>
+ <property name="xalign">0</property>
+ <property name="label" translatable="yes" context="yes">label23</property>
+ </widget>
+ <packing>
+ <property name="top_attach">1</property>
+ <property name="bottom_attach">2</property>
+ </packing>
+ </child>
+ <child>
+ <widget class="GtkLabel" id="label24">
+ <property name="visible">True</property>
+ <property name="xalign">0</property>
+ <property name="label" translatable="yes" context="yes">label24</property>
+ </widget>
+ <packing>
+ <property name="top_attach">2</property>
+ <property name="bottom_attach">3</property>
+ </packing>
+ </child>
+ <child>
+ <widget class="GtkEntry" id="entry1">
+ <property name="visible">True</property>
+ </widget>
+ <packing>
+ <property name="left_attach">1</property>
+ <property name="right_attach">2</property>
+ </packing>
+ </child>
+ <child>
+ <widget class="GtkEntry" id="entry2">
+ <property name="visible">True</property>
+ </widget>
+ <packing>
+ <property name="left_attach">1</property>
+ <property name="right_attach">2</property>
+ <property name="top_attach">1</property>
+ <property name="bottom_attach">2</property>
+ </packing>
+ </child>
+ <child>
+ <widget class="GtkEntry" id="entry3">
+ <property name="visible">True</property>
+ </widget>
+ <packing>
+ <property name="left_attach">1</property>
+ <property name="right_attach">2</property>
+ <property name="top_attach">2</property>
+ <property name="bottom_attach">3</property>
+ </packing>
+ </child>
+ </widget>
+ <packing>
+ <property name="position">2</property>
+ </packing>
+ </child>
+ <child internal-child="action_area">
+ <widget class="GtkHButtonBox" id="dialog1-action_area">
+ <property name="visible">True</property>
+ <property name="layout_style">end</property>
+ <child>
+ <widget class="GtkButton" id="button1">
+ <property name="label" context="yes">gtk-ok</property>
+ <property name="response_id">1</property>
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="receives_default">False</property>
+ <property name="use_stock">True</property>
+ <property name="image_position">top</property>
+ </widget>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">False</property>
+ <property name="position">0</property>
+ </packing>
+ </child>
+ <child>
+ <widget class="GtkButton" id="button2">
+ <property name="label" context="yes">gtk-cancel</property>
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="receives_default">False</property>
+ <property name="use_stock">True</property>
+ </widget>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">False</property>
+ <property name="position">1</property>
+ </packing>
+ </child>
+ </widget>
+ <packing>
+ <property name="expand">False</property>
+ <property name="pack_type">end</property>
+ <property name="position">0</property>
+ </packing>
+ </child>
+ </widget>
+ </child>
+ </widget>
+ <widget class="GtkDialog" id="DownloadDialog">
+ <property name="border_width">5</property>
+ <property name="title" translatable="yes">Gant Monitor: Downloading data...</property>
+ <property name="window_position">center</property>
+ <property name="default_width">415</property>
+ <property name="default_height">140</property>
+ <property name="destroy_with_parent">True</property>
+ <property name="icon">resources/gant.png</property>
+ <property name="type_hint">normal</property>
+ <child internal-child="vbox">
+ <widget class="GtkVBox" id="dialog-vbox2">
+ <property name="visible">True</property>
+ <property name="orientation">vertical</property>
+ <property name="spacing">2</property>
+ <child>
+ <widget class="GtkVBox" id="vbox1">
+ <property name="visible">True</property>
+ <property name="orientation">vertical</property>
+ <child>
+ <widget class="GtkLabel" id="lbl_status">
+ <property name="visible">True</property>
+ <property name="label" translatable="yes">Gant status:</property>
+ <property name="ellipsize">start</property>
+ <property name="single_line_mode">True</property>
+ </widget>
+ <packing>
+ <property name="expand">False</property>
+ <property name="position">0</property>
+ </packing>
+ </child>
+ <child>
+ <widget class="GtkProgressBar" id="progressbar">
+ <property name="visible">True</property>
+ </widget>
+ <packing>
+ <property name="expand">False</property>
+ <property name="position">1</property>
+ </packing>
+ </child>
+ <child>
+ <widget class="GtkExpander" id="details_section">
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <signal name="activate" handler="on_expand_details" after="yes"/>
+ <child>
+ <placeholder/>
+ </child>
+ <child>
+ <widget class="GtkLabel" id="label1">
+ <property name="visible">True</property>
+ <property name="label" translatable="yes">Details</property>
+ </widget>
+ <packing>
+ <property name="type">label_item</property>
+ </packing>
+ </child>
+ </widget>
+ <packing>
+ <property name="expand">False</property>
+ <property name="position">2</property>
+ </packing>
+ </child>
+ </widget>
+ <packing>
+ <property name="position">1</property>
+ </packing>
+ </child>
+ <child internal-child="action_area">
+ <widget class="GtkHButtonBox" id="dialog-action_area2">
+ <property name="visible">True</property>
+ <property name="layout_style">end</property>
+ <child>
+ <widget class="GtkButton" id="button1">
+ <property name="label" translatable="yes">Restart</property>
+ <property name="response_id">4</property>
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="receives_default">True</property>
+ <signal name="activate" handler="on_restart_process"/>
+ </widget>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">False</property>
+ <property name="position">0</property>
+ </packing>
+ </child>
+ <child>
+ <widget class="GtkButton" id="btn_close">
+ <property name="label">gtk-stop</property>
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="receives_default">True</property>
+ <property name="use_stock">True</property>
+ <property name="image_position">right</property>
+ <signal name="activate" handler="gtk_widget_destroy"/>
+ </widget>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">False</property>
+ <property name="position">1</property>
+ </packing>
+ </child>
+ </widget>
+ <packing>
+ <property name="expand">False</property>
+ <property name="pack_type">end</property>
+ <property name="position">0</property>
+ </packing>
+ </child>
+ </widget>
+ </child>
+ </widget>
+ <widget class="GtkWindow" id="window1">
+ <child>
+ <placeholder/>
+ </child>
+ </widget>
+</glade-interface>
--- /dev/null
+#!/usr/bin/env python
+
+import sys
+import os
+import time
+
+import gtk
+import gtk.glade
+import gobject
+import vte
+
+import pygtk
+pygtk.require("2.0")
+
+class DownloadDialog:
+ """This class is used to show DownloadDialog"""
+
+ def __init__(self):
+ self.gladefile = "GantMonitor.glade"
+ self.wTree = gtk.glade.XML(self.gladefile)
+
+ def run(self):
+ """Configures and runs the download dialog"""
+ self.wTree = gtk.glade.XML(self.gladefile, "DownloadDialog")
+ events = { "on_expand_details" : self.on_expand_details, "on_restart_process":self.on_restart_process}
+ self.wTree.signal_autoconnect(events)
+
+ self.dlg = self.wTree.get_widget("DownloadDialog")
+
+ self.close_button = self.wTree.get_widget("btn_close")
+ self.close_button.set_use_stock(True)
+
+ self.details_section = self.wTree.get_widget("details_section")
+ self.status_label = self.wTree.get_widget("lbl_status")
+
+ self.progress_bar = self.wTree.get_widget("progressbar")
+ self.progress_bar.pulse()
+ self.timeout_handler_id = gobject.timeout_add(100, self.update_progress_bar)
+ self.start = time.time()
+
+ terminal= vte.Terminal()
+ terminal.connect("show", self.on_show_terminal)
+ terminal.connect('child-exited', self.on_child_exited)
+ self.details_section.add(terminal)
+
+ self.dlg.show_all()
+ self.result = self.dlg.run()
+ self.dlg.destroy()
+
+ def update_progress_bar(self):
+ self.progress_bar.pulse()
+ self.status_label.set_text("Gant running... (pid: " + str(self.child_pid) + ") " + time.asctime() )
+ return True
+
+ def on_show_terminal(self, terminal):
+ self.start_gant(terminal)
+
+ def start_gant(self, terminal):
+ self.child_pid = terminal.fork_command("./gant" , argv = [' -p'] )
+
+ def on_child_exited(self, child):
+ """Updates label after download complete"""
+ child.destroy()
+ self.status_label.set_text("Gant download complete!")
+ self.close_button.set_label(gtk.STOCK_CLOSE)
+ self.close_button.set_use_stock(True)
+ print "gant exited"
+
+ # doesn't work, dunno why...
+ def on_expand_details(self, expander):
+ if not expander.get_expanded():
+ self.dlg.resize(415,130)
+
+ #doesn't work, i think gtk dialog buttons are wired to only close the dialog... refactor?
+ def on_restart_process(self, button):
+ os.kill(self.child_id, signal.SIGSTOP)
+ self.start_gant(self.terminal)
+
+class SettingsDialog:
+ """This class is used to show SettingsDialog, which has no purpose (yet)"""
+
+ def __init__(self):
+ self.gladefile = "GantMonitor.glade"
+ self.wTree = gtk.glade.XML(self.gladefile)
+
+ def run(self):
+ self.wTree = gtk.glade.XML(self.gladefile, "SettingsDialog")
+ self.dlg = self.wTree.get_widget("SettingsDialog")
+ self.result = self.dlg.run()
+ self.dlg.destroy()
+
+class GantMonitorStatusIcon(gtk.StatusIcon):
+ """This class is used to show the tray icon and the menu"""
+
+ def __init__(self):
+ gtk.StatusIcon.__init__(self)
+ icon_filename = 'resources/gant.png'
+ menu = '''
+ <ui>
+ <menubar name="Menubar">
+ <menu action="Menu">
+ <menuitem action="Download"/>
+ <menuitem action="Preferences"/>
+ <separator/>
+ <menuitem action="About"/>
+ <separator/>
+ <menuitem action="Exit"/>
+ </menu>
+ </menubar>
+ </ui>'''
+
+ actions = [
+ ('Menu', None, 'Menu'),
+ ('Download',icon_filename, '_Download...', None, 'Download data using Gant', self.on_download),
+ ('Preferences', gtk.STOCK_PREFERENCES, '_Preferences...', None, 'Change Gant Monitor preferences', self.on_preferences),
+ ('About', gtk.STOCK_ABOUT, '_About...', None, 'About Gant Monitor', self.on_about),
+ ('Exit', gtk.STOCK_QUIT, '_Exit...', None, 'Exit Gant Monitor', self.on_exit)
+ ]
+
+ ag = gtk.ActionGroup('Actions')
+ ag.add_actions(actions)
+
+ self.manager = gtk.UIManager()
+ self.manager.insert_action_group(ag, 0)
+ self.manager.add_ui_from_string(menu)
+ self.menu = self.manager.get_widget('/Menubar/Menu/About').props.parent
+
+ search = self.manager.get_widget('/Menubar/Menu/Download')
+ search.get_children()[0].set_markup('<b>_Download...</b>')
+ search.get_children()[0].set_use_underline(True)
+ search.get_children()[0].set_use_markup(True)
+
+ self.set_from_file(icon_filename)
+ self.set_tooltip('Gant Monitor')
+ self.set_visible(True)
+ self.connect('activate', self.on_download)
+ self.connect('popup-menu', self.on_popup_menu)
+
+ def on_download(self, data):
+ downloadDialog=DownloadDialog()
+ downloadDialog.run()
+
+ def on_exit(self, data):
+ gtk.main_quit()
+
+ def on_popup_menu(self, status, button, time):
+ self.menu.popup(None, None, None, button, time)
+
+ # configure default cmd line params and serialize to xml?
+ def on_preferences(self, data):
+ print 'Gant preferences - todo'
+ settings = SettingsDialog()
+ settings.run()
+ print settings.result
+
+ def on_about(self, data):
+ dialog = gtk.AboutDialog()
+ dialog.set_name('Gant Monitor')
+ dialog.set_version('0.1.0')
+ dialog.set_comments('A tray icon to start Gant')
+ dialog.set_website('http://cgit.get-open.com/cgit.cgi/gant/')
+ dialog.run()
+ dialog.destroy()
+
+if __name__ == '__main__':
+ GantMonitorStatusIcon()
+ gtk.main()
--- /dev/null
+CFLAGS=-g -Werror -m32
+LDFLAGS=-lpthread -lm
+
+all: gant
+
+gant: gant.o antlib.o
+
+gant.o: gant.c antdefs.h
+
+antlib.o: antlib.c antdefs.h
+
+clean:
+ rm *.o gant
--- /dev/null
+// copyright 2008 paul@ant.sbrk.co.uk. released under GPLv3
+// vers 0.6t
+#include <unistd.h>
+#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>
+
+#define __declspec(X)
+
+#include "antdefs.h"
+//#include "antdefines.h"
+//#include "ANT_Interface.h"
+//#include "antmessage.h"
+//#include "anttypes.h"
+
+#define S(e) if (-1 == (e)) {perror(#e);exit(1);} else
+
+#define MAXMSG 30 // SYNC,LEN,MSG,data[9+],CHKSUM
+#define MAXCHAN 32
+#define BSIZE 8*10000
+
+#define uchar unsigned char
+
+#define hexval(c) ((c >= '0' && c <= '9') ? (c-'0') : ((c&0xdf)-'A'+10))
+
+static int fd = -1;
+static int dbg = 0;
+static pthread_t commthread;;
+static int commenabled = 1;
+
+static RESPONSE_FUNC rfn = 0;
+static uchar *rbufp;
+static CHANNEL_EVENT_FUNC cfn = 0;
+static uchar *cbufp;
+
+static uchar *blast;
+static int blsize;
+
+struct msg_queue {
+ uchar msgid;
+ uchar len;
+};
+
+// send message over serial port
+uchar
+msg_send(uchar mesg, uchar *inbuf, uchar len)
+{
+ uchar buf[MAXMSG];
+ ssize_t nw;
+ int i;
+ uchar chk = MESG_TX_SYNC;
+
+ buf[0] = MESG_TX_SYNC;
+ buf[1] = len; chk ^= len;
+ buf[2] = mesg; chk ^= mesg;
+ for (i = 0; i < len; i++) {
+ buf[3+i] = inbuf[i];
+ chk ^= inbuf[i];
+ }
+ buf[3+i] = chk;
+ usleep(10*1000);
+ if (4+i != (nw=write(fd, buf, 4+i))) {
+ if (dbg) {
+ perror("failed write");
+ }
+ }
+ return 1;
+}
+
+// two argument send
+uchar
+msg_send2(uchar mesg, uchar data1, uchar data2)
+{
+ uchar buf[2];
+ buf[0] = data1;
+ buf[1] = data2;
+ return msg_send(mesg, buf, 2);
+}
+
+// three argument send
+uchar
+msg_send3(uchar mesg, uchar data1, uchar data2, uchar data3)
+{
+ uchar buf[3];
+ buf[0] = data1;
+ buf[1] = data2;
+ buf[2] = data3;
+ return msg_send(mesg, buf, 3);
+}
+
+void *commfn(void* arg)
+{
+ fd_set readfds, writefds, exceptfds;
+ int ready;
+ struct timeval to;
+
+ for(;;) {
+ FD_ZERO(&readfds);
+ FD_ZERO(&writefds);
+ FD_ZERO(&exceptfds);
+ FD_SET(fd, &readfds);
+ to.tv_sec = 1;
+ to.tv_usec = 0;
+ ready = select(fd+1, &readfds, &writefds, &exceptfds, &to);
+ if (ready) {
+ get_data(fd);
+ }
+ }
+}
+
+get_data(int fd)
+{
+ static uchar buf[500];
+ static int bufc = 0;
+ int nr;
+ int dlen;
+ int i;
+ int j;
+ unsigned char chk = 0;
+ uchar event;
+ int found;
+ int srch;
+ int next;
+
+ nr = read(fd, buf+bufc, 20);
+ if (nr > 0)
+ bufc += nr;
+ else
+ return;
+ if (bufc > 30) {
+ if (dbg)
+ fprintf(stderr, "bufc %d\n", bufc);
+ }
+ if (bufc > 300) {
+ fprintf(stderr, "buf too long %d\n", bufc);
+ for (j = 0; j < bufc; j++)
+ fprintf(stderr, "%02x", buf[j]);
+ fprintf(stderr, "\n");
+ exit(1);
+ }
+
+ // some data in buf
+ // search for possible valid messages
+ srch = 0;
+ while (srch < bufc) {
+ found = 0;
+ //printf("srch %d bufc %d\n", srch, bufc);
+ for (i = srch; i < bufc; i++) {
+ if (buf[i] == MESG_TX_SYNC) {
+ //fprintf(stderr, "bufc %d sync %d\n", bufc, i);
+ if (i+1 < bufc && buf[i+1] >= 1 && buf[i+1] <= 13) {
+ dlen = buf[i+1];
+ if (i+3+dlen < bufc) {
+ chk = 0;
+ for (j = i; j <= i+3+dlen; j++)
+ chk ^= buf[j];
+ if (0 == chk) {
+ found = 1; // got a valid message
+ break;
+ } else {
+ fprintf(stderr, "bad chk %02x\n", chk);
+ for (j = i; j <= i+3+dlen; j++)
+ fprintf(stderr, "%02x", buf[j]);
+ fprintf(stderr, "\n");
+ }
+ }
+ }
+ }
+ }
+ if (found) {
+ next = j;
+ //printf("next %d %02x\n", next, buf[j-1]);
+ // got a valid message, see if any data needs to be discarded
+ if (i > srch) {
+ fprintf(stderr, "\nDiscarding: ");
+ for (j = 0; j < i; j++)
+ fprintf(stderr, "%02x", buf[j]);
+ fprintf(stderr, "\n");
+ }
+
+ if (dbg) {
+ fprintf(stderr, "data: ");
+ for(j = i; j < i+dlen+4; j++) {
+ fprintf(stderr, "%02x", buf[j]);
+ }
+ fprintf(stderr, "\n");
+ }
+ event = 0;
+ switch (buf[i+2]) {
+ case MESG_RESPONSE_EVENT_ID:
+ //if (cfn) {
+ // memcpy(cbufp, buf+i+4, dlen);
+ // (*cfn)(buf[i+3], buf[i+5]);
+ // else
+ if (rfn) {
+ memcpy(rbufp, buf+i+3, dlen);
+ (*rfn)(buf[i+3], buf[i+5]);
+ } else {
+ if (dbg)
+ fprintf(stderr, "no rfn or cfn\n");
+ }
+ break;
+ case MESG_BROADCAST_DATA_ID:
+ event = EVENT_RX_BROADCAST;
+ break;
+ case MESG_ACKNOWLEDGED_DATA_ID:
+ event = EVENT_RX_ACKNOWLEDGED;
+ break;
+ case MESG_BURST_DATA_ID:
+ event = EVENT_RX_BURST_PACKET;
+ // coalesce these and generate a fake event on last packet
+ // in case client wishes to ignore these events and capture the fake one
+ {
+ static uchar *burstbuf[MAXCHAN];
+ static int bused[MAXCHAN];
+ static int lseq[MAXCHAN];
+ int k;
+
+ uchar seq;
+ uchar last;
+ uchar chan = *(buf+i+3);
+ if (dbg) {
+ fprintf(stderr, "burst %02x ", chan);
+ for (k = 0; k < 12; k++)
+ fprintf(stderr, "%02x", buf[i+k]);
+ fprintf(stderr, "\n");
+ }
+ seq = (chan & 0x60) >> 5;
+ last = (chan & 0x80) >> 7;
+ chan &= 0x1f;
+ if (dbg) fprintf(stderr, "ch %x seq %d last %d\n", chan, seq, last);
+ if (!burstbuf[chan]) {
+ if (seq != 0)
+ fprintf(stderr, "out of sequence ch# %d %d\n", chan, seq);
+ else {
+ burstbuf[chan] = malloc(BSIZE);
+ bzero(burstbuf[chan], BSIZE);
+ memcpy(burstbuf[chan], buf+i+4, 8);
+ bused[chan] = 8;
+ lseq[chan] = seq;
+ if (dbg)
+ fprintf(stderr, "init ch# %d %d\n", chan, lseq[chan]);
+ }
+ } else {
+ if (lseq[chan]+1 != seq) {
+ fprintf(stderr, "out of sequence ch# %d %d l %d\n", chan, seq, lseq[chan]);
+ free(burstbuf[chan]);
+ burstbuf[chan] = 0;
+ if (seq == 0) {
+ burstbuf[chan] = malloc(BSIZE);
+ bzero(burstbuf[chan], BSIZE);
+ memcpy(burstbuf[chan], buf+i+4, 8);
+ bused[chan] = 8;
+ lseq[chan] = seq;
+ fprintf(stderr, "reinit ch# %d %d\n", chan, lseq[chan]);
+ }
+ } else {
+ if ((bused[chan] % BSIZE) == 0) {
+ burstbuf[chan] = realloc(burstbuf[chan], bused[chan]+BSIZE);
+ bzero(burstbuf[chan]+bused[chan], BSIZE);
+ }
+ memcpy(burstbuf[chan]+bused[chan], buf+i+4, 8);
+ bused[chan] += 8;
+ if (dbg) fprintf(stderr, "seq0 %d lseq %d\n", seq, lseq[chan]);
+ if (seq == 3)
+ lseq[chan] = 0;
+ else
+ lseq[chan] = seq;
+ if (dbg) fprintf(stderr, "seq1 %d lseq %d\n", seq, lseq[chan]);
+ }
+ }
+ if (burstbuf[chan] && last) {
+ blast = burstbuf[chan];
+ blsize = bused[chan];
+ if (dbg) fprintf(stderr, "BU %d %lx\n", blsize, (long)blast);
+ if (dbg) {
+ fprintf(stderr, "bused ch# %d %d\n", chan, bused[chan]);
+ for (k = 0; k < bused[chan]; k++)
+ fprintf(stderr, "%02x", burstbuf[chan][k]);
+ fprintf(stderr, "\n");
+ }
+ bused[chan] = 0;
+ burstbuf[chan] = 0;
+ }
+ }
+ break;
+ case MESG_EXT_BROADCAST_DATA_ID:
+ event = EVENT_RX_EXT_BROADCAST;
+ break;
+ case MESG_EXT_ACKNOWLEDGED_DATA_ID:
+ event = EVENT_RX_EXT_ACKNOWLEDGED;
+ break;
+ case MESG_EXT_BURST_DATA_ID:
+ event = EVENT_RX_EXT_BURST_PACKET;
+ break;
+ default:
+ if (rfn) {
+ // should be this according to the docs, but doesn't fit
+ // if (dlen > MESG_RESPONSE_EVENT_SIZE) {
+ if (dlen > MESG_DATA_SIZE) {
+ fprintf(stderr, "cresponse buffer too small %d > %d\n", dlen, MESG_DATA_SIZE);
+ for (j = 0; j < dlen; j++)
+ fprintf(stderr, "%02x", *(buf+i+3+j));
+ fprintf(stderr, "\n");
+ exit(1);
+ }
+ memcpy(rbufp, buf+i+3, dlen);
+ (*rfn)(buf[i+3], buf[i+2]);
+ } else {
+ if (dbg)
+ fprintf(stderr, "no rfn\n");
+ }
+ }
+ if (event) {
+ uchar chan = *(buf+3) & 0x1f;
+ if (cfn) {
+ if (dlen > MESG_DATA_SIZE) {
+ fprintf(stderr, "rresponse buffer too small %d > %d\n",
+ dlen, MESG_DATA_SIZE);
+ for (j = 0; j < dlen+1; j++)
+ fprintf(stderr, "%02x", *(buf+i+4+j));
+ fprintf(stderr, "\n");
+ exit(1);
+ }
+ memcpy(cbufp, buf+i+4, dlen);
+ if (dbg) {
+ fprintf(stderr, "xch0#%d %d %lx\n", chan, blsize, blast);
+ for (j = 0; j < blsize; j++)
+ fprintf(stderr, "%02x", *(blast+j));
+ fprintf(stderr, "\n");
+ }
+ (*cfn)(chan, event);
+ if (dbg) {
+ fprintf(stderr, "xch1#%d %d %lx\n", chan, blsize, blast);
+ for (j = 0; j < blsize; j++)
+ fprintf(stderr, "%02x", *(blast+j));
+ fprintf(stderr, "\n");
+ }
+ // FAKE BURST message
+ if (event == EVENT_RX_BURST_PACKET && blast && blsize) {
+ if (dbg) {
+ fprintf(stderr, "Fake burst ch#%d %d %lx\n", chan, blsize, blast);
+ for (j = 0; j < blsize; j++)
+ fprintf(stderr, "%02x", *(blast+j));
+ fprintf(stderr, "\n");
+ }
+ *(int *)(cbufp+4) = blsize;
+ memcpy(cbufp+8, &blast, 4);
+ (*cfn)(chan, EVENT_RX_FAKE_BURST);
+ free(blast);
+ blast = 0;
+ blsize = 0;
+ }
+ } else
+ if (dbg)
+ fprintf(stderr, "no cfn\n");
+ }
+ srch = next;
+ } else
+ break;
+ }
+ if (next < bufc) {
+ memmove(buf, buf+next, bufc-next);
+ bufc -= next;
+ } else
+ bufc = 0;
+}
+
+uchar
+ANT_ResetSystem(void)
+{
+ uchar filler = 0;
+ return msg_send(MESG_SYSTEM_RESET_ID, &filler, 1);
+}
+
+uchar
+ANT_Cmd55(uchar chan)
+{
+ return msg_send(0x55, &chan, 1);
+}
+
+uchar
+ANT_OpenRxScanMode(uchar chan)
+{
+ return msg_send(MESG_OPEN_RX_SCAN_ID, &chan, 1);
+}
+
+uchar
+ANT_Initf(char *devname, ushort baud)
+{
+ struct termios tp;
+
+ fd = open(devname, O_RDWR);
+ if (fd < 0) {
+ perror(devname);
+ return 0;
+ }
+
+ S(tcgetattr(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;
+
+ S(cfsetispeed(&tp, B115200));
+ S(cfsetospeed(&tp, B115200));
+ tp.c_cc[VMIN] = 1;
+ tp.c_cc[VTIME] = 0;
+ S(tcsetattr(fd, TCSANOW, &tp));
+
+ if (pthread_create(&commthread, 0, commfn, 0));
+ return 1;
+}
+
+uchar
+ANT_Init(uchar devno, ushort baud)
+{
+ char dev[40];
+
+ sprintf(dev, "/dev/ttyUSB%d", devno);
+ return ANT_Initf(dev, devno);
+}
+
+uchar
+ANT_RequestMessage(uchar chan, uchar mesg)
+{
+ return msg_send2(MESG_REQUEST_ID, chan, mesg);
+}
+
+uchar
+ANT_SetNetworkKeya(uchar net, uchar *key)
+{
+ uchar buf[9];
+ int i;
+
+ if (strlen(key) != 16) {
+ fprintf(stderr, "Bad key length %s\n", key);
+ return 0;
+ }
+ buf[0] = net;
+ for (i = 0; i < 8; i++)
+ buf[1+i] = hexval(key[i*2])*16+hexval(key[i*2+1]);
+ return msg_send(MESG_NETWORK_KEY_ID, buf, 9);
+}
+
+uchar
+ANT_SetNetworkKey(uchar net, uchar *key)
+{
+ uchar buf[9];
+ int i;
+
+ buf[0] = net;
+ memcpy(buf+1, key, 8);
+ return msg_send(MESG_NETWORK_KEY_ID, buf, 9);
+}
+
+uchar
+ANT_AssignChannel(uchar chan, uchar chtype, uchar net)
+{
+ return msg_send3(MESG_ASSIGN_CHANNEL_ID, chan, chtype, net);
+}
+
+uchar
+ANT_UnAssignChannel(uchar chan)
+{
+ return msg_send(MESG_UNASSIGN_CHANNEL_ID, &chan, 1);
+}
+
+uchar
+ANT_SetChannelId(uchar chan, ushort dev, uchar devtype, uchar manid)
+{
+ uchar buf[5];
+ buf[0] = chan;
+ buf[1] = dev%256;
+ buf[2] = dev/256;
+ buf[3] = devtype;
+ buf[4] = manid;
+ return msg_send(MESG_CHANNEL_ID_ID, buf, 5);
+}
+
+uchar
+ANT_SetChannelRFFreq(uchar chan, uchar freq)
+{
+ return msg_send2(MESG_CHANNEL_RADIO_FREQ_ID, chan, freq);
+}
+
+uchar
+ANT_SetChannelPeriod(uchar chan, ushort period)
+{
+ uchar buf[3];
+ buf[0] = chan;
+ buf[1] = period%256;
+ buf[2] = period/256;
+ return msg_send(MESG_CHANNEL_MESG_PERIOD_ID, buf, 3);
+}
+
+uchar
+ANT_SetChannelSearchTimeout(uchar chan, uchar timeout)
+{
+ return msg_send2(MESG_CHANNEL_SEARCH_TIMEOUT_ID, chan, timeout);
+}
+
+uchar
+ANT_SetSearchWaveform(uchar chan, ushort waveform)
+{
+ uchar buf[3];
+ buf[0] = chan;
+ buf[1] = waveform%256;
+ buf[2] = waveform/256;
+ return msg_send(MESG_SEARCH_WAVEFORM_ID, buf, 3);
+}
+
+uchar
+ANT_SendAcknowledgedDataA(uchar chan, uchar *data) // ascii version
+{
+ uchar buf[9];
+ int i;
+
+ if (strlen(data) != 16) {
+ fprintf(stderr, "Bad data length %s\n", data);
+ return 0;
+ }
+ buf[0] = chan;
+ for (i = 0; i < 8; i++)
+ buf[1+i] = hexval(data[i*2])*16+hexval(data[i*2+1]);
+ return msg_send(MESG_ACKNOWLEDGED_DATA_ID, buf, 9);
+}
+
+uchar
+ANT_SendAcknowledgedData(uchar chan, uchar *data)
+{
+ uchar buf[9];
+ int i;
+
+ buf[0] = chan;
+ memcpy(buf+1, data, 8);
+ return msg_send(MESG_ACKNOWLEDGED_DATA_ID, buf, 9);
+}
+
+ushort
+ANT_SendBurstTransferA(uchar chan, uchar *data, ushort numpkts)
+{
+ uchar buf[9];
+ int i;
+ int j;
+ int seq = 0;
+
+ if (dbg) fprintf(stderr, "numpkts %d data %s\n", numpkts, data);
+ if (strlen(data) != 16*numpkts) {
+ fprintf(stderr, "Bad data length %s numpkts %d\n", data, numpkts);
+ return 0;
+ }
+ for (j = 0; j < numpkts; j++) {
+ buf[0] = chan|(seq<<5)|(j==numpkts-1 ? 0x80 : 0);
+ for (i = 0; i < 8; i++)
+ buf[1+i] = hexval(data[j*16+i*2])*16+hexval(data[j*16+i*2+1]);
+ usleep(20*1000);
+ msg_send(MESG_BURST_DATA_ID, buf, 9);
+ seq++; if (seq > 3) seq = 1;
+ }
+ return numpkts;
+}
+
+ushort
+ANT_SendBurstTransfer(uchar chan, uchar *data, ushort numpkts)
+{
+ uchar buf[9];
+ int i;
+ int j;
+ int seq = 0;
+
+ for (j = 0; j < numpkts; j++) {
+ buf[0] = chan|(seq<<5)|(j==numpkts-1 ? 0x80 : 0);
+ memcpy(buf+1, data+j*8, 8);
+ usleep(20*1000);
+ msg_send(MESG_BURST_DATA_ID, buf, 9);
+ seq++; if (seq > 3) seq = 1;
+ }
+ return numpkts;
+}
+
+uchar
+ANT_OpenChannel(uchar chan)
+{
+ return msg_send(MESG_OPEN_CHANNEL_ID, &chan, 1);
+}
+
+uchar
+ANT_CloseChannel(uchar chan)
+{
+ return msg_send(MESG_CLOSE_CHANNEL_ID, &chan, 1);
+}
+
+void
+ANT_AssignResponseFunction(RESPONSE_FUNC rf, uchar* rbuf)
+{
+ rfn = rf;
+ rbufp = rbuf;
+}
+
+void
+ANT_AssignChannelEventFunction(uchar chan, CHANNEL_EVENT_FUNC rf, uchar* rbuf)
+{
+ cfn = rf;
+ cbufp = rbuf;
+}
+
+int ANT_fd()
+{
+ return fd;
+}
+
+// vim: se ts=2 sw=2:
--- /dev/null
+// copyright 2008-2009 paul@ant.sbrk.co.uk. released under GPLv3
+// copyright 2009-2009 Wali
+// vers 0.6t
+#include <stdio.h>
+#include <sys/select.h>
+#include <sys/time.h>
+#include <sys/types.h>
+#include <unistd.h>
+#include <string.h>
+#include <stdlib.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#include <assert.h>
+#include <time.h>
+#include <math.h>
+
+#include "antdefs.h"
+
+char *releasetime = "Jan 21 2008, 12:00:00";
+uint majorrelease = 0;
+uint minorrelease = 6;
+
+double round(double);
+
+int gottype;
+int sentauth;
+int gotwatchid;
+int nopairing;
+int nowriteauth;
+int reset;
+int dbg = 0;
+int seenphase0 = 0;
+int lastphase;
+int sentack2;
+int newfreq = 0;
+int period = 0x1000; // garmin specific broadcast period
+int donebind = 0;
+int sentgetv;
+char *fname = "garmin";
+
+static char ANTSPT_KEY[] = "A8A423B9F55E63C1"; // ANT+Sport key
+
+static uchar ebuf[MESG_DATA_SIZE]; // response event data gets stored here
+static uchar cbuf[MESG_DATA_SIZE]; // channel event data gets stored here
+
+int passive;
+int semipassive;
+int verbose;
+
+int downloadfinished = 0;
+int downloadstarted = 0;
+int sentid = 0;
+
+uint mydev = 0;
+uint peerdev;
+uint myid;
+uint devid;
+uint myauth1;
+uint myauth2;
+char authdata[32];
+uint pairing;
+uint isa50;
+uint isa405;
+uint waitauth;
+int nphase0;
+char modelname[256];
+ushort part = 0;
+ushort ver = 0;
+uint unitid = 0;
+
+
+//char *getversion = "440dffff00000000fe00000000000000";
+//char *getgpsver = "440dffff0000000006000200ff000000";
+char *acks[] = {
+ "fe00000000000000", // get version - 255, 248, 253
+ "0e02000000000000", // device short name (fr405a) - 525
+// "1c00020000000000", // no data
+ "0a0002000e000000", // unit id - 38
+ "0a00020002000000", // send position
+ "0a00020005000000", // send time
+ "0a000200ad020000", // 4 byte something? 0x10270000 = 10000 dec - 1523
+ "0a000200c6010000", // 3 x 4 ints? - 994
+ "0a00020035020000", // guessing this is # trackpoints per run - 1066
+ "0a00020097000000", // load of software versions - 247
+ "0a000200c2010000", // download runs - 27 (#runs), 990?, 12?
+ "0a00020075000000", // download laps - 27 (#laps), 149 laps, 12?
+ "0a00020006000000", // download trackpoints - 1510/99(run marker), ..1510,12
+ "0a000200ac020000",
+ ""
+};
+int sentcmd;
+
+uchar clientid[3][8];
+
+int authfd = -1;
+char *authfile;
+int outfd; // output file
+char *fn = "default_output_file";
+char *progname;
+
+#define BSIZE 8*100
+uchar *burstbuf = 0;
+uchar *blast = 0;
+int blsize = 0;
+int bused = 0;
+int lseq = -1;
+
+/* round a float as garmin does it! */
+/* shoot me for writing this! */
+char *
+ground(double d)
+{
+ int neg = 0;
+ static char res[30];
+ ulong ival;
+ ulong l; /* hope it doesn't overflow */
+
+ if (d < 0) {
+ neg = 1;
+ d = -d;
+ }
+ ival = floor(d);
+ d -= ival;
+ l = floor(d*100000000);
+ if (l % 10 >= 5)
+ l = l/10+1;
+ else
+ l = l/10;
+ sprintf(res,"%s%ld.%07ld", neg?"-":"", ival, l);
+ return res;
+}
+
+char *
+timestamp(void)
+{
+ struct timeval tv;
+ static char time[50];
+ struct tm *tmp;
+
+ gettimeofday(&tv, 0);
+ tmp = gmtime(&tv.tv_sec);
+
+ sprintf(time, "%02d:%02d:%02d.%02d",
+ tmp->tm_hour, tmp->tm_min, tmp->tm_sec, (int)tv.tv_usec/10000);
+ return time;
+}
+
+uint
+randno(void)
+{
+ uint r;
+
+ int fd = open("/dev/urandom", O_RDONLY);
+ if (fd > 0) {
+ read(fd, &r, sizeof r);
+ close(fd);
+ }
+ return r;
+}
+
+void
+print_tcx_header(FILE *tcxfile)
+{
+ fprintf(tcxfile, "<?xml version=\"1.0\" encoding=\"UTF-8\" standalone=\"no\" ?>\n");
+ fprintf(tcxfile, "<TrainingCenterDatabase xmlns=\"http://www.garmin.com/xmlschemas/TrainingCenterDatabase/v2\" xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\" xsi:schemaLocation=\"http://www.garmin.com/xmlschemas/ActivityExtension/v2 http://www.garmin.com/xmlschemas/ActivityExtensionv2.xsd http://www.garmin.com/xmlschemas/TrainingCenterDatabase/v2 http://www.garmin.com/xmlschemas/TrainingCenterDatabasev2.xsd\">\n\n");
+ fprintf(tcxfile, " <Activities>\n");
+ return;
+}
+
+void
+print_tcx_footer(FILE *tcxfile)
+{
+ fprintf(tcxfile, " </Track>\n");
+ fprintf(tcxfile, " </Lap>\n");
+ fprintf(tcxfile, " <Creator xsi:type=\"Device_t\">\n");
+ fprintf(tcxfile, " <Name>%s</Name>\n", modelname);
+ fprintf(tcxfile, " <UnitId>%u</UnitId>\n", unitid);
+ fprintf(tcxfile, " <ProductID>%u</ProductID>\n", part);
+ fprintf(tcxfile, " <Version>\n");
+ fprintf(tcxfile, " <VersionMajor>%u</VersionMajor>\n", ver/100);
+ fprintf(tcxfile, " <VersionMinor>%u</VersionMinor>\n", ver - ver/100*100);
+ fprintf(tcxfile, " <BuildMajor>0</BuildMajor>\n");
+ fprintf(tcxfile, " <BuildMinor>0</BuildMinor>\n");
+ fprintf(tcxfile, " </Version>\n");
+ fprintf(tcxfile, " </Creator>\n");
+ fprintf(tcxfile, " </Activity>\n");
+ fprintf(tcxfile, " </Activities>\n\n");
+ fprintf(tcxfile, " <Author xsi:type=\"Application_t\">\n");
+ fprintf(tcxfile, " <Name>Garmin ANT for Linux</Name>\n");
+ fprintf(tcxfile, " <Build>\n");
+ fprintf(tcxfile, " <Version>\n");
+ fprintf(tcxfile, " <VersionMajor>%u</VersionMajor>\n", majorrelease);
+ fprintf(tcxfile, " <VersionMinor>%u</VersionMinor>\n", minorrelease);
+ fprintf(tcxfile, " <BuildMajor>0</BuildMajor>\n");
+ fprintf(tcxfile, " <BuildMinor>0</BuildMinor>\n");
+ fprintf(tcxfile, " </Version>\n");
+ fprintf(tcxfile, " <Type>Release</Type>\n");
+ fprintf(tcxfile, " <Time>%s</Time>\n", releasetime);
+ fprintf(tcxfile, " <Builder>make</Builder>\n");
+ fprintf(tcxfile, " </Build>\n");
+ fprintf(tcxfile, " <LangID>EN</LangID>\n");
+ fprintf(tcxfile, " <PartNumber>006-A0XXX-00</PartNumber>\n");
+ fprintf(tcxfile, " </Author>\n\n");
+ fprintf(tcxfile, "</TrainingCenterDatabase>\n");
+ return;
+}
+
+
+#pragma pack(1)
+struct ack_msg {
+ uchar code;
+ uchar atype;
+ uchar c1;
+ uchar c2;
+ uint id;
+};
+
+struct auth_msg {
+ uchar code;
+ uchar atype;
+ uchar phase;
+ uchar u1;
+ uint id;
+ uint auth1;
+ uint auth2;
+ uint fill1;
+ uint fill2;
+};
+
+struct pair_msg {
+ uchar code;
+ uchar atype;
+ uchar phase;
+ uchar u1;
+ uint id;
+ char devname[16];
+};
+
+#pragma pack()
+#define ACKSIZE 8 // above structure must be this size
+#define AUTHSIZE 24 // ditto
+#define PAIRSIZE 16
+#define MAXLAPS 256 // max of saving laps data before output with trackpoint data
+#define MAXTRACK 256 // max number of tracks to be saved per download
+
+decode(ushort bloblen, ushort pkttype, ushort pktlen, int dsize, uchar *data)
+{
+ int i;
+ int j;
+ int hr;
+ int hr_av;
+ int hr_max;
+ int cal;
+ float tsec;
+ float max_speed;
+ int cad;
+ int u1, u2;
+ int doff = 20;
+ char model[256];
+ char gpsver[256];
+ char devname[256];
+ float alt;
+ float dist;
+ uint tv;
+ uint tv_previous = 0;
+ time_t ttv;
+ char tbuf[100];
+ struct tm *tmp;
+ double lat, lon;
+ uint nruns;
+ uint tv_lap;
+ static uchar lapbuf[MAXLAPS][48];
+ static ushort lap = 0;
+ static ushort lastlap = 0;
+ static ushort track = 0;
+ static short previoustrack_id = -1;
+ static short track_id = -1;
+ static short firsttrack_id = -1;
+ static short firstlap_id = -1;
+ static ushort firstlap_id_track[MAXTRACK];
+ static uchar sporttyp_track[MAXTRACK];
+ static FILE *tcxfile = NULL;
+ static ushort track_pause = 0;
+
+
+ printf("decode %d %d %d %d\n", bloblen, pkttype, pktlen, dsize);
+ switch (pkttype) {
+ case 255:
+ memset(model, 0, sizeof model);
+ memcpy(model, data+doff+4, dsize-4);
+ part=data[doff]+data[doff+1]*256;
+ ver=data[doff+2]+data[doff+3]*256;
+ printf("%d Part#: %d ver: %d Name: %s\n", pkttype,
+ part, ver, model);
+ break;
+ case 248:
+ memset(gpsver, 0, sizeof gpsver);
+ memcpy(gpsver, data+doff, dsize);
+ printf("%d GPSver: %s\n", pkttype,
+ gpsver);
+ break;
+ case 253:
+ printf("%d Unknown\n", pkttype);
+ for (i = 0; i < pktlen; i += 3)
+ printf("%d.%d.%d\n", data[doff+i], data[doff+i+1], data[doff+i+2]);
+ break;
+ case 525:
+ memset(devname, 0, sizeof devname);
+ memcpy(devname, data+doff, dsize);
+ printf("%d Devname %s\n", pkttype, devname);
+ break;
+ case 12:
+ printf("%d xfer complete", pkttype);
+ for (i = 0; i < pktlen; i += 2)
+ printf(" %u", data[doff+i] + data[doff+i+1]*256);
+ printf("\n");
+ switch (data[doff] + data[doff+1]*256) {
+ case 6:
+ // last file completed, add footer and close file
+ print_tcx_footer(tcxfile);
+ fclose(tcxfile);
+ break;
+ case 117:
+ break;
+ case 450:
+ break;
+ default:
+ break;
+ }
+ break;
+ case 38:
+ unitid = data[doff] + data[doff+1]*256 +
+ data[doff+2]*256*256 + data[doff+3]*256*256*256;
+ printf("%d unitid %u\n", pkttype, unitid);
+ break;
+ case 27:
+ nruns = data[doff] + data[doff+1] * 256;
+ printf("%d nruns %u\n", pkttype, nruns);
+ break;
+ case 1523:
+ case 994:
+ case 1066:
+ printf("%d ints?", pkttype);
+ for (i = 0; i < pktlen; i += 4)
+ printf(" %u", data[doff+i] + data[doff+i+1]*256 +
+ data[doff+i+2]*256*256 + data[doff+i+3]*256*256*256);
+ printf("\n");
+ break;
+ case 14:
+ printf("%d time: ", pkttype);
+ printf("%02u-%02u-%u %02u:%02u:%02u\n", data[doff], data[doff+1], data[doff+2] + data[doff+3]*256,
+ data[doff+4], data[doff+6], data[doff+7]);
+ break;
+ case 17:
+ printf("%d position ? ", pkttype);
+ for (i = 0; i < pktlen; i += 4)
+ printf(" %u", data[doff+i] + data[doff+i+1]*256 +
+ data[doff+i+2]*256*256 + data[doff+i+3]*256*256*256);
+ printf("\n");
+ break;
+ case 99:
+ printf("%d trackindex %u\n", pkttype, data[doff] + data[doff+1]*256);
+ printf("%d shorts?", pkttype);
+ for (i = 0; i < pktlen; i += 2)
+ printf(" %u", data[doff+i] + data[doff+i+1]*256);
+ printf("\n");
+ track_id = data[doff] + data[doff+1]*256;
+ break;
+ case 990:
+ printf("%d track %u lap %u-%u sport %u\n", pkttype,
+ data[doff] + data[doff+1]*256, data[doff+2] + data[doff+3]*256,
+ data[doff+4] + data[doff+5]*256, data[doff+6]);
+ printf("%d shorts?", pkttype);
+ for (i = 0; i < pktlen; i += 2)
+ printf(" %u", data[doff+i] + data[doff+i+1]*256);
+ printf("\n");
+ if (firstlap_id == -1) firstlap_id = data[doff+2] + data[doff+3]*256;
+ if (firsttrack_id == -1) firsttrack_id = data[doff] + data[doff+1]*256;
+ track = (data[doff] + data[doff+1]*256) - firsttrack_id;
+ if (track < MAXTRACK) {
+ firstlap_id_track[track] = data[doff+2] + data[doff+3]*256;
+ sporttyp_track[track] = data[doff+6];
+ } else {
+ printf("Error: track and lap data temporary array out of range %u!\n", track);
+ }
+ break;
+ case 1510:
+ printf("%d waypoints", pkttype);
+ for (i = 0; i < 4 && i < pktlen; i += 4)
+ printf(" %u", data[doff+i] + data[doff+i+1]*256 +
+ data[doff+i+2]*256*256 + data[doff+i+3]*256*256*256);
+ printf("\n");
+ // if trackpoints are split into more than one message 1510, do not add xml head again
+ if (previoustrack_id != track_id) {
+ // close previous file if it is not the first track to be downloaded
+ if (previoustrack_id > -1) {
+ // add xml footer and close file, the next file will be open further down
+ print_tcx_footer(tcxfile);
+ fclose(tcxfile);
+ }
+ // use first lap starttime as filename
+ lap = firstlap_id_track[track_id-firsttrack_id] - firstlap_id;
+ if (dbg) printf("lap %u track_id %u firsttrack_id %u firstlap_id %u\n", lap, track_id, firsttrack_id, firstlap_id);
+ tv_lap = lapbuf[lap][4] + lapbuf[lap][5]*256 +
+ lapbuf[lap][6]*256*256 + lapbuf[lap][7]*256*256*256;
+ ttv = tv_lap + 631065600; // garmin epoch offset
+ strftime(tbuf, sizeof tbuf, "%Y.%m.%d %H%M%S.TCX", localtime(&ttv));
+ // open file and start with header of xml file
+ tcxfile = fopen(tbuf, "wt");
+ print_tcx_header(tcxfile);
+ }
+ for (i = 4; i < pktlen; i += 24) {
+ tv = (data[doff+i+8] + data[doff+i+9]*256 +
+ data[doff+i+10]*256*256 + data[doff+i+11]*256*256*256);
+ tv_lap = lapbuf[lap][4] + lapbuf[lap][5]*256 +
+ lapbuf[lap][6]*256*256 + lapbuf[lap][7]*256*256*256;
+ if ((tv > tv_lap || (tv == tv_lap && lap == (firstlap_id_track[track_id-firsttrack_id] - firstlap_id))) && lap <= lastlap) {
+ ttv = tv_lap + 631065600; // garmin epoch offset
+ strftime(tbuf, sizeof tbuf, "%Y-%m-%dT%H:%M:%SZ", gmtime(&ttv));
+ tsec = (lapbuf[lap][8] + lapbuf[lap][9]*256 +
+ lapbuf[lap][10]*256*256 + lapbuf[lap][11]*256*256*256);
+ memcpy((void *)&dist, &lapbuf[lap][12], 4);
+ memcpy((void *)&max_speed, &lapbuf[lap][16], 4);
+ cal = lapbuf[lap][36] + lapbuf[lap][37]*256;
+ hr_av = lapbuf[lap][38];
+ hr_max = lapbuf[lap][39];
+ cad = lapbuf[lap][41];
+ if (lap == firstlap_id_track[track_id-firsttrack_id] - firstlap_id) {
+ fprintf(tcxfile, " <Activity Sport=\"");
+ switch(sporttyp_track[track_id-firsttrack_id]) {
+ case 0: fprintf(tcxfile, "Running"); break;
+ case 1: fprintf(tcxfile, "Biking"); break;
+ case 2: fprintf(tcxfile, "Other"); break;
+ default: fprintf(tcxfile, "unknown value: %d",sporttyp_track[track_id-firsttrack_id]);
+ }
+ fprintf(tcxfile, "\">\n");
+ fprintf(tcxfile, " <Id>%s</Id>\n", tbuf);
+ } else {
+ fprintf(tcxfile, " </Track>\n");
+ fprintf(tcxfile, " </Lap>\n");
+ }
+ fprintf(tcxfile, " <Lap StartTime=\"%s\">\n", tbuf);
+ fprintf(tcxfile, " <TotalTimeSeconds>%s</TotalTimeSeconds>\n", ground(tsec/100));
+ fprintf(tcxfile, " <DistanceMeters>%s</DistanceMeters>\n", ground(dist));
+ fprintf(tcxfile, " <MaximumSpeed>%s</MaximumSpeed>\n", ground(max_speed));
+ fprintf(tcxfile, " <Calories>%d</Calories>\n", cal);
+ if (hr_av > 0) {
+ fprintf(tcxfile, " <AverageHeartRateBpm xsi:type=\"HeartRateInBeatsPerMinute_t\">\n");
+ fprintf(tcxfile, " <Value>%d</Value>\n", hr_av);
+ fprintf(tcxfile, " </AverageHeartRateBpm>\n");
+ }
+ if (hr_max > 0) {
+ fprintf(tcxfile, " <MaximumHeartRateBpm xsi:type=\"HeartRateInBeatsPerMinute_t\">\n");
+ fprintf(tcxfile, " <Value>%d</Value>\n", hr_max);
+ fprintf(tcxfile, " </MaximumHeartRateBpm>\n");
+ }
+ fprintf(tcxfile, " <Intensity>");
+ switch (lapbuf[lap][40]) {
+ case 0: fprintf(tcxfile, "Active"); break;
+ case 1: fprintf(tcxfile, "Rest"); break;
+ default: fprintf(tcxfile, "unknown value: %d", lapbuf[lap][40]);
+ }
+ fprintf(tcxfile, "</Intensity>\n");
+ if (cad != 255) {
+ if (sporttyp_track[track_id-firsttrack_id] == 0) {
+ fprintf(tcxfile, " <RunCadence>%d</RunCadence>\n", cad);
+ } else {
+ fprintf(tcxfile, " <Cadence>%d</Cadence>\n", cad);
+ }
+ }
+ fprintf(tcxfile, " <TriggerMethod>");
+ switch(lapbuf[lap][42]) {
+ case 4: fprintf(tcxfile, "Heartrate"); break;
+ case 3: fprintf(tcxfile, "Time"); break;
+ case 2: fprintf(tcxfile, "Location"); break;
+ case 1: fprintf(tcxfile, "Distance"); break;
+ case 0: fprintf(tcxfile, "Manual"); break;
+ default: fprintf(tcxfile, "unknown value: %d", lapbuf[lap][42]);
+ }
+ fprintf(tcxfile, "</TriggerMethod>\n");
+ fprintf(tcxfile, " <Track>\n");
+ lap++;
+ track_pause = 0;
+ // if the previous trackpoint has same second as lap time display the trackpoint again
+ if (dbg) printf("i %u tv %d tv_lap %d tv_previous %d\n", i, tv, tv_lap, tv_previous);
+ if (tv_previous == tv_lap) {
+ i -= 24;
+ tv = tv_previous;
+ }
+ } // end of if (tv >= tv_lap && lap <= lastlap)
+ if (track_pause) {
+ fprintf(tcxfile, " </Track>\n");
+ fprintf(tcxfile, " <Track>\n");
+ track_pause = 0;
+ if (dbg) printf("track pause (stop and go)\n");
+ }
+ ttv = tv+631065600; // garmin epoch offset
+ tmp = gmtime(&ttv);
+ strftime(tbuf, sizeof tbuf, "%Y-%m-%dT%H:%M:%SZ", tmp); // format for printing
+ memcpy((void *)&alt, data+doff+i+12, 4);
+ memcpy((void *)&dist, data+doff+i+16, 4);
+ lat = (data[doff+i] + data[doff+i+1]*256 +
+ data[doff+i+2]*256*256 + data[doff+i+3]*256*256*256)*180.0/0x80000000;
+ lon = (data[doff+i+4] + data[doff+i+5]*256 +
+ data[doff+i+6]*256*256 + data[doff+i+7]*256*256*256)*180.0/0x80000000;
+ hr = data[doff+i+20];
+ cad = data[doff+i+21];
+ u1 = data[doff+i+22];
+ u2 = data[doff+i+23];
+ if (dbg) printf("lat %.10g lon %.10g hr %d cad %d u1 %d u2 %d tv %d %s alt %f dist %f %02x %02x%02x%02x%02x\n", lat, lon,
+ hr, cad, u1, u2, tv, tbuf, alt, dist, data[doff+i+3], data[doff+i+16], data[doff+i+17], data[doff+i+18], data[doff+i+19]);
+ fprintf(tcxfile, " <Trackpoint>\n");
+ fprintf(tcxfile, " <Time>%s</Time>\n",tbuf);
+ if (lat < 90) {
+ fprintf(tcxfile, " <Position>\n");
+ fprintf(tcxfile, " <LatitudeDegrees>%s</LatitudeDegrees>\n",
+ ground(lat));
+ fprintf(tcxfile, " <LongitudeDegrees>%s</LongitudeDegrees>\n",
+ ground(lon));
+ fprintf(tcxfile, " </Position>\n");
+ fprintf(tcxfile, " <AltitudeMeters>%s</AltitudeMeters>\n", ground(alt));
+ }
+ // last trackpoint has utopic distance, 40000km should be enough, hack?
+ if (dist < (float)40000000) {
+ fprintf(tcxfile, " <DistanceMeters>%s</DistanceMeters>\n", ground(dist));
+ }
+ if (hr > 0) {
+ fprintf(tcxfile, " <HeartRateBpm xsi:type=\"HeartRateInBeatsPerMinute_t\">\n");
+ fprintf(tcxfile, " <Value>%d</Value>\n", hr);
+ fprintf(tcxfile, " </HeartRateBpm>\n");
+ }
+ if (u1 > 0 && cad != 255) {
+ fprintf(tcxfile, " <Cadence>%d</Cadence>\n", cad);
+ }
+ if (dist < (float)40000000) {
+ fprintf(tcxfile, " <SensorState>%s</SensorState>\n", u1 ? "Present" : "Absent");
+ fprintf(tcxfile, " <Extensions>\n");
+ fprintf(tcxfile, " <TPX xmlns=\"http://www.garmin.com/xmlschemas/ActivityExtension/v2\" CadenceSensor=\"");
+ // get type of pod from data, could not figure it out, so using sporttyp of first track
+ switch(sporttyp_track[track_id-firsttrack_id]) {
+ case 1: fprintf(tcxfile, "Bike\""); break;
+ case 0:
+ default: fprintf(tcxfile, "Footpod\""); break;
+ }
+ if (u1 == 0 && cad != 255) {
+ fprintf(tcxfile, ">\n");
+ fprintf(tcxfile, " <RunCadence>%d</RunCadence>\n", cad);
+ fprintf(tcxfile, " </TPX>\n");
+ } else {
+ fprintf(tcxfile, "/>\n");
+ }
+ fprintf(tcxfile, " </Extensions>\n");
+ } else {
+ // maybe if we recieve utopic position and distance this tells pause in the run (stop and go)
+ if (track_pause == 0) track_pause = 1;
+ else track_pause = 0;
+ }
+ fprintf(tcxfile, " </Trackpoint>\n");
+ tv_previous = tv;
+ } // end of for (i = 4; i < pktlen; i += 24)
+ previoustrack_id = track_id;
+ break;
+ case 149:
+ printf("%d Lap data id: %u %u\n", pkttype,
+ data[doff] + data[doff+1]*256, data[doff+2] + data[doff+3]*256);
+ if (lap < MAXLAPS) {
+ memcpy((void *)&lapbuf[lap][0], data+doff, 48);
+ lastlap = lap;
+ lap++;
+ }
+ break;
+ case 247:
+ memset(modelname, 0, sizeof modelname);
+ memcpy(modelname, data+doff+88, dsize-88);
+ printf("%d Device name %s\n", pkttype, modelname);
+ break;
+ default:
+ printf("don't know how to decode packet type %d\n", pkttype);
+ for (i = doff; i < dsize && i < doff+pktlen; i++)
+ printf("%02x", data[i]);
+ printf("\n");
+ for (i = doff; i < dsize && i < doff+pktlen; i++)
+ if (isprint(data[i]))
+ printf("%c", data[i]);
+ else
+ printf(".");
+ printf("\n");
+ }
+}
+
+void
+usage(void)
+{
+ fprintf(stderr, "Usage: %s -a authfile\n"
+ "[ -o outfile ]\n"
+ "[ -d devno ]\n"
+ "[ -i id ]\n"
+ "[ -m mydev ]\n"
+ "[ -p ]\n",
+ progname
+ );
+ exit(1);
+}
+
+uchar
+chevent(uchar chan, uchar event)
+{
+ uchar seq;
+ uchar last;
+ uchar status;
+ uchar phase;
+ uint newdata;
+ struct ack_msg ack;
+ struct auth_msg auth;
+ struct pair_msg pair;
+ uint id;
+ int i;
+ uint cid;
+ if (dbg) printf("chevent %02x %02x\n", chan, event);
+
+ if (event == EVENT_RX_BROADCAST) {
+ status = cbuf[1] & 0xd7;
+ newdata = cbuf[1] & 0x20;
+ phase = cbuf[2];
+ }
+ cid = cbuf[4]+cbuf[5]*256+cbuf[6]*256*256+cbuf[7]*256*256*256;
+ memcpy((void *)&id, cbuf+4, 4);
+
+ if (dbg)
+ fprintf(stderr, "cid %08x myid %08x\n", cid, myid);
+ if (dbg && event != EVENT_RX_BURST_PACKET) {
+ fprintf(stderr, "chan %d event %02x channel open: ", chan, event);
+ for (i = 0; i < 8; i++)
+ fprintf(stderr, "%02x", cbuf[i]);
+ fprintf(stderr, "\n");
+ }
+
+ switch (event) {
+ case EVENT_RX_BROADCAST:
+ lastphase = phase; // store the last phase we see the watch broadcast
+ if (dbg) printf("lastphase %d\n", lastphase);
+ if (!pairing && !nopairing)
+ pairing = cbuf[1] & 8;
+ if (!gottype) {
+ gottype = 1;
+ isa50 = cbuf[1] & 4;
+ isa405 = cbuf[1] & 1;
+ if ((isa50 && isa405) || (!isa50 && !isa405)) {
+ fprintf(stderr, "50 %d and 405 %d\n", isa50, isa405);
+ exit(1);
+ }
+ }
+ if (verbose) {
+ switch (phase) {
+ case 0:
+ fprintf(stderr, "%s BC0 %02x %d %d %d PID %d %d %d %c%c\n",
+ timestamp(),
+ cbuf[0], cbuf[1] & 0xd7, cbuf[2], cbuf[3],
+ cbuf[4]+cbuf[5]*256, cbuf[6], cbuf[7],
+ (cbuf[1] & 0x20) ? 'N' : ' ', (cbuf[1] & 0x08) ? 'P' : ' '
+ );
+ break;
+ case 1:
+ fprintf(stderr, "%s BC1 %02x %d %d %d CID %08x %c%c\n",
+ timestamp(),
+ cbuf[0], cbuf[1] & 0xd7, cbuf[2], cbuf[3], cid,
+ (cbuf[1] & 0x20) ? 'N' : ' ', (cbuf[1] & 0x08) ? 'P' : ' '
+ );
+ break;
+ fprintf(stderr, "%s BCX %02x %d %d %d PID %d %d %d %c%c\n",
+ timestamp(),
+ cbuf[0], cbuf[1] & 0xd7, cbuf[2], cbuf[3],
+ cbuf[4]+cbuf[5]*256, cbuf[6], cbuf[7],
+ (cbuf[1] & 0x20) ? 'N' : ' ', (cbuf[1] & 0x08) ? 'P' : ' '
+ );
+ default:
+ break;
+ }
+ }
+
+ if (dbg)
+ printf("watch status %02x stage %d id %08x\n", status, phase, id);
+
+ if (!sentid) {
+ sentid = 1;
+ ANT_RequestMessage(chan, MESG_CHANNEL_ID_ID); /* request sender id */
+ }
+
+ // if we don't see a phase 0 message first, reset the watch
+ if (reset || (phase != 0 && !seenphase0)) {
+ fprintf(stderr, "resetting\n");
+ ack.code = 0x44; ack.atype = 3; ack.c1 = 0x00; ack.c2 = 0x00; ack.id = 0;
+ ANT_SendAcknowledgedData(chan, (void *)&ack); // tell garmin we're finished
+ sleep(1);
+ exit(1);
+ }
+ switch (phase) {
+ case 0:
+ seenphase0 = 1;
+ nphase0++;
+ if (nphase0 % 10 == 0)
+ donebind = 0;
+ if (newfreq) {
+ // switch to new frequency
+ ANT_SetChannelPeriod(chan, period);
+ ANT_SetChannelSearchTimeout(chan, 3);
+ ANT_SetChannelRFFreq(chan, newfreq);
+ newfreq = 0;
+ }
+ // phase 0 seen after reset at end of download
+ if (downloadfinished) {
+ fprintf(stderr, "finished\n");
+ exit(0);
+ }
+ // generate a random id if pairing and user didn't specify one
+ if (pairing && !myid) {
+ myid = randno();
+ fprintf(stderr, "pairing, using id %08x\n", myid);
+ }
+ // need id codes from auth file if not pairing
+ // TODO: handle multiple watches
+ // BUG: myauth1 should be allowed to be 0
+ if (!pairing && !myauth1) {
+ int nr;
+ printf("reading auth data from %s\n", authfile);
+ authfd = open(authfile, O_RDONLY);
+ if (authfd < 0) {
+ perror(authfile);
+ fprintf(stderr, "No auth data. Need to pair first\n");
+ exit(1);
+ }
+ nr = read(authfd, authdata, 32);
+ close(authfd);
+ if (nr != 32 && nr != 24) {
+ fprintf(stderr, "bad auth file len %d != 32 or 24\n", nr);
+ exit(1);
+ }
+ // BUG: auth file not portable
+ memcpy((void *)&myauth1, authdata+16, 4);
+ memcpy((void *)&myauth2, authdata+20, 4);
+ memcpy((void *)&mydev, authdata+12, 4);
+ memcpy((void *)&myid, authdata+4, 4);
+ if (dbg)
+ fprintf(stderr, "dev %08x auth %08x %08x id %08x\n",
+ mydev, myauth1, myauth2, myid);
+ }
+ // bind to watch
+ if (!donebind && devid) {
+ donebind = 1;
+ if (isa405)
+ newfreq = 0x32;
+ ack.code = 0x44; ack.atype = 2; ack.c1 = isa50 ? 0x32 : newfreq; ack.c2 = 0x04;
+ ack.id = myid;
+ ANT_SendAcknowledgedData(chan, (void *)&ack); // bind
+ } else {
+ if (dbg) printf("donebind %d devid %x\n", donebind, devid);
+ }
+ break;
+ case 1:
+ if (dbg) printf("case 1 %x\n", peerdev);
+ if (peerdev) {
+ if (dbg) printf("case 1 peerdev\n");
+ // if watch has sent id
+ if (mydev != 0 && peerdev != mydev) {
+ fprintf(stderr, "Don't know this device %08x != %08x\n", peerdev, mydev);
+ } else if (!sentauth && !waitauth) {
+ if (dbg) printf("case 1 diffdev\n");
+ assert(sizeof auth == AUTHSIZE);
+ auth.code = 0x44; auth.atype = 4; auth.phase = 3; auth.u1 = 8;
+ auth.id = myid; auth.auth1 = myauth1; auth.auth2 = myauth2;
+ auth.fill1 = auth.fill2 = 0;
+ sentauth = 1;
+ ANT_SendBurstTransfer(chan, (void *)&auth, (sizeof auth)/8); // send our auth data
+ }
+ }
+ if (dbg) printf("case 1 cid %x myid %x\n", cid, myid);
+ if (!sentack2 && cid == myid && !waitauth) {
+ sentack2 = 1;
+ if (dbg) printf("sending ack2\n");
+ // if it did bind to me before someone else
+ ack.code = 0x44; ack.atype = 4; ack.c1 = 0x01; ack.c2 = 0x00;
+ ack.id = myid;
+ ANT_SendAcknowledgedData(chan, (void *)&ack); // request id
+ }
+ break;
+ case 2:
+ // successfully authenticated
+ if (!downloadstarted) {
+ downloadstarted = 1;
+ if (dbg) printf("starting download\n");
+ ack.code = 0x44; ack.atype = 6; ack.c1 = 0x01; ack.c2 = 0x00; ack.id = 0;
+ //ANT_SendAcknowledgedData(chan, (void *)&ack); // tell garmin to start upload
+ }
+ if (downloadfinished) {
+ if (dbg) printf("finished download\n");
+ ack.code = 0x44; ack.atype = 3; ack.c1 = 0x00; ack.c2 = 0x00; ack.id = 0;
+ if (!passive) ANT_SendAcknowledgedData(chan, (void *)&ack); // tell garmin we're finished
+ }
+ break;
+ case 3:
+ if (pairing) {
+ // waiting for the user to pair
+ printf("Please press \"View\" on watch to confirm pairing\n");
+ waitauth = 2; // next burst data is auth data
+ } else {
+ if (dbg) printf("not sure why in phase 3\n");
+ if (!sentgetv) {
+ sentgetv = 1;
+ //ANT_SendBurstTransferA(chan, getversion, strlen(getversion)/16);
+ }
+ }
+ break;
+ default:
+ if (dbg) fprintf(stderr, "Unknown phase %d\n", phase);
+ break;
+ }
+ break;
+ case EVENT_RX_BURST_PACKET:
+ // now handled in coalesced burst below
+ if (dbg) printf("burst\n");
+ break;
+ case EVENT_RX_FAKE_BURST:
+ if (dbg) printf("rxfake burst pairing %d blast %ld waitauth %d\n",
+ pairing, (long)blast, waitauth);
+ blsize = *(int *)(cbuf+4);
+ memcpy(&blast, cbuf+8, 4);
+ if (dbg) {
+ printf("fake burst %d %lx ", blsize, (long)blast);
+ for (i = 0; i < blsize && i < 64; i++)
+ printf("%02x", blast[i]);
+ printf("\n");
+ for (i = 0; i < blsize; i++)
+ if (isprint(blast[i]))
+ printf("%c", blast[i]);
+ else
+ printf(".");
+ printf("\n");
+ }
+ if (sentauth) {
+ static int nacksent = 0;
+ char *ackdata;
+ static uchar ackpkt[100];
+ // ack the last packet
+ ushort bloblen = blast[14]+256*blast[15];
+ ushort pkttype = blast[16]+256*blast[17];
+ ushort pktlen = blast[18]+256*blast[19];
+ if (bloblen == 0) {
+ if (dbg) printf("bloblen %d, get next data\n", bloblen);
+ // request next set of data
+ ackdata = acks[nacksent++];
+ if (!strcmp(ackdata, "")) { // finished
+ printf("acks finished, resetting\n");
+ ack.code = 0x44; ack.atype = 3; ack.c1 = 0x00;
+ ack.c2 = 0x00; ack.id = 0;
+ ANT_SendAcknowledgedData(chan, (void *)&ack); // go to idle
+ sleep(1);
+ exit(1);
+ }
+ if (dbg) printf("got type 0, sending ack %s\n", ackdata);
+ sprintf(ackpkt, "440dffff00000000%s", ackdata);
+ } else if (bloblen == 65535) {
+ // repeat last ack
+ if (dbg) printf("repeating ack %s\n", ackpkt);
+ ANT_SendBurstTransferA(chan, ackpkt, strlen(ackpkt)/16);
+ } else {
+ if (dbg) printf("non-0 bloblen %d\n", bloblen);
+ decode(bloblen, pkttype, pktlen, blsize, blast);
+ sprintf(ackpkt, "440dffff0000000006000200%02x%02x0000", pkttype%256, pkttype/256);
+ }
+ if (dbg) printf("received pkttype %d len %d\n", pkttype, pktlen);
+ if (dbg) printf("acking %s\n", ackpkt);
+ ANT_SendBurstTransferA(chan, ackpkt, strlen(ackpkt)/16);
+ } else if (!nopairing && pairing && blast) {
+ memcpy(&peerdev, blast+12, 4);
+ if (dbg)
+ printf("watch id %08x waitauth %d\n", peerdev, waitauth);
+ if (mydev != 0 && peerdev != mydev) {
+ fprintf(stderr, "Don't know this device %08x != %08x\n", peerdev, mydev);
+ exit(1);
+ }
+ if (waitauth == 2) {
+ int nw;
+ // should be receiving auth data
+ if (nowriteauth) {
+ printf("Not overwriting auth data\n");
+ exit(1);
+ }
+ printf("storing auth data in %s\n", authfile);
+ authfd = open(authfile, O_WRONLY|O_CREAT, 0644);
+ if (authfd < 0) {
+ perror(authfile);
+ exit(1);
+ }
+ nw = write(authfd, blast, blsize);
+ if (nw != blsize) {
+ fprintf(stderr, "auth write failed fd %d %d\n", authfd, nw);
+ perror("write");
+ exit(1);
+ }
+ close(authfd);
+ //exit(1);
+ pairing = 0;
+ waitauth = 0;
+ reset = 1;
+ }
+ if (pairing && !waitauth) {
+ //assert(sizeof pair == PAIRSIZE);
+ pair.code = 0x44; pair.atype = 4; pair.phase = 2; pair.id = myid;
+ bzero(pair.devname, sizeof pair.devname);
+ //if (peerdev <= 9999999) // only allow 7 digits
+ //sprintf(pair.devname, "%u", peerdev);
+ strcpy(pair.devname, fname);
+ //else
+ // fprintf(stderr, "pair dev name too large %08x \"%d\"\n", peerdev, peerdev);
+ pair.u1 = strlen(pair.devname);
+ printf("sending pair data for dev %s\n", pair.devname);
+ waitauth = 1;
+ if (isa405 && pairing) {
+ // go straight to storing auth data
+ waitauth = 2;
+ }
+ ANT_SendBurstTransfer(chan, (void *)&pair, (sizeof pair)/8) ; // send pair data
+ } else {
+ if (dbg) printf("not pairing\n");
+ }
+ } else if (!gotwatchid && (lastphase == 1)) {
+ static int once = 0;
+ gotwatchid = 1;
+ // garmin sending authentication/identification data
+ if (!once) {
+ int i;
+ once = 1;
+ if (dbg)
+ fprintf(stderr, "id data: ");
+ }
+ if (dbg)
+ for (i = 0; i < blsize; i++)
+ fprintf(stderr, "%02x", blast[i]);
+ if (dbg)
+ fprintf(stderr, "\n");
+ memcpy(&peerdev, blast+12, 4);
+ if (dbg)
+ printf("watch id %08x\n", peerdev);
+ if (mydev != 0 && peerdev != mydev) {
+ fprintf(stderr, "Don't know this device %08x != %08x\n", peerdev, mydev);
+ exit(1);
+ }
+ } else if (lastphase == 2) {
+ int nw;
+ static int once = 0;
+ printf("once %d\n", once);
+ // garmin uploading in response to sendack3
+ // in this state we're receiving the workout data
+ if (!once) {
+ printf("receiving\n");
+ once = 1;
+ outfd = open(fn, O_WRONLY|O_CREAT, 0644);
+ if (outfd < 0) {
+ perror(fn);
+ exit(1);
+ }
+ }
+ if (last) {
+ nw = write(outfd, blast, blsize);
+ if (nw != blsize) {
+ fprintf(stderr, "data write failed fd %d %d\n", outfd, nw);
+ perror("write");
+ exit(1);
+ }
+ close(outfd);
+ downloadfinished = 1;
+ }
+ } else if (0 && last) {
+ if (dbg) {
+ fprintf(stderr, "auth response: ");
+ for (i = 0; i < blsize; i++)
+ fprintf(stderr, "%02x", cbuf[i]);
+ fprintf(stderr, "\n");
+ }
+ if (blast[10] == 2) {
+ fprintf(stderr, "authentication failed\n");
+ exit(1);
+ }
+ } else if (last) {
+ fprintf(stderr, "data in state xx: ");
+ int i;
+ for (i = 0; i < blsize; i++)
+ fprintf(stderr, "%02x", blast[i]);
+ fprintf(stderr, "\n");
+ sentcmd = 1000;
+ switch (sentcmd) {
+ case 1000:
+ break;
+ case 0:
+ sentcmd++;
+ //ANT_SendBurstTransferA(chan, getgpsver, strlen(getgpsver)/16);
+ break;
+ case 999:
+ printf("finished\n");
+ exit(1);
+ default:
+ sleep(1);
+ sentcmd = 1;
+ //printf("sending command %d %s\n", sentcmd-1, cmds[sentcmd-1]);
+ //ANT_SendBurstTransferA(chan, cmds[sentcmd-1],
+ // strlen(cmds[sentcmd-1])/16);
+ sentcmd++;
+ //if(!strcmp(cmds[sentcmd-1], "END"))
+ // sentcmd = 999;
+ break;
+ }
+ }
+ if (dbg) printf("continuing after burst\n");
+ break;
+ }
+ return 1;
+}
+
+uchar
+revent(uchar chan, uchar event)
+{
+ struct ack_msg ack;
+ int i;
+
+ if (dbg) printf("revent %02x %02x\n", chan, event);
+ switch (event) {
+ case EVENT_TRANSFER_TX_COMPLETED:
+ if (dbg) printf("Transfer complete %02x\n", ebuf[1]);
+ break;
+ case INVALID_MESSAGE:
+ printf("Invalid message %02x\n", ebuf[1]);
+ break;
+ case RESPONSE_NO_ERROR:
+ switch (ebuf[1]) {
+ case MESG_ASSIGN_CHANNEL_ID:
+ ANT_AssignChannelEventFunction(chan, chevent, cbuf);
+ break;
+ case MESG_OPEN_CHANNEL_ID:
+ printf("channel open, waiting for broadcast\n");
+ break;
+ default:
+ if (dbg) printf("Message %02x NO_ERROR\n", ebuf[1]);
+ break;
+ }
+ break;
+ case MESG_CHANNEL_ID_ID:
+ devid = ebuf[1]+ebuf[2]*256;
+ if (mydev == 0 || devid == mydev%65536) {
+ if (dbg)
+ printf("devid %08x myid %08x\n", devid, myid);
+ } else {
+ printf("Ignoring unknown device %08x, mydev %08x\n", devid, mydev);
+ devid = sentid = 0; // reset
+ }
+ break;
+ case MESG_NETWORK_KEY_ID:
+ case MESG_SEARCH_WAVEFORM_ID:
+ case MESG_OPEN_CHANNEL_ID:
+ printf("response event %02x code %02x\n", event, ebuf[2]);
+ for (i = 0; i < 8; i++)
+ fprintf(stderr, "%02x", ebuf[i]);
+ fprintf(stderr, "\n");
+ break;
+ case MESG_CAPABILITIES_ID:
+ if (dbg)
+ printf("capabilities chans %d nets %d opt %02x adv %02x\n",
+ ebuf[0], ebuf[1], ebuf[2], ebuf[3]);
+ break;
+ case MESG_CHANNEL_STATUS_ID:
+ if (dbg)
+ printf("channel status %d\n", ebuf[1]);
+ break;
+ case EVENT_RX_FAIL:
+ // ignore this
+ break;
+ default:
+ printf("Unhandled response event %02x\n", event);
+ break;
+ }
+ return 1;
+}
+
+main(int ac, char *av[])
+{
+ int devnum = 0;
+ int chan = 0;
+ int net = 0;
+ int chtype = 0; // wildcard
+ int devno = 0; // wildcard
+ int devtype = 0; // wildcard
+ int manid = 0; // wildcard
+ int freq = 0x32; // garmin specific radio frequency
+ int srchto = 255; // max timeout
+ int waveform = 0x0053; // aids search somehow
+ int c;
+ extern char *optarg;
+ extern int optind, opterr, optopt;
+
+ // default auth file //
+ if (getenv("HOME")) {
+ authfile = malloc(strlen(getenv("HOME"))+strlen("/.gant")+1);
+ if (authfile)
+ sprintf(authfile, "%s/.gant", getenv("HOME"));
+ }
+ progname = av[0];
+ while ((c = getopt(ac, av, "a:o:d:i:m:PpvDrnzf:")) != -1) {
+ switch(c) {
+ case 'a':
+ authfile = optarg;
+ break;
+ case 'f':
+ fname = optarg;
+ break;
+ case 'o':
+ fn = optarg;
+ break;
+ case 'd':
+ devnum = atoi(optarg);
+ break;
+ case 'i':
+ myid = atoi(optarg);
+ break;
+ case 'm':
+ mydev = atoi(optarg);
+ break;
+ case 'p':
+ passive = 1;
+ semipassive = 0;
+ break;
+ case 'P':
+ passive = 1;
+ break;
+ case 'v':
+ verbose = 1;
+ break;
+ case 'D':
+ dbg = 1;
+ break;
+ case 'r':
+ reset = 1;
+ break;
+ case 'n':
+ nowriteauth = 1;
+ break;
+ case 'z':
+ nopairing = 1;
+ break;
+ default:
+ fprintf(stderr, "unknown option %s\n", optarg);
+ usage();
+ }
+ }
+
+ ac -= optind;
+ av += optind;
+
+ if ((!passive && !authfile) || ac)
+ usage();
+
+ if (!ANT_Init(devnum, 0)) { // should be 115200 but doesn't fit into a short
+ fprintf(stderr, "open dev %d failed\n", devnum);
+ exit(1);
+ }
+ ANT_ResetSystem();
+ ANT_AssignResponseFunction(revent, ebuf);
+ ANT_RequestMessage(chan, MESG_CHANNEL_STATUS_ID); //informative
+ ANT_SetNetworkKeya(net, ANTSPT_KEY);
+ ANT_AssignChannel(chan, chtype, net);
+ // Wali: changed order of the following seq. according windows
+ ANT_SetChannelPeriod(chan, period);
+ ANT_SetChannelSearchTimeout(chan, srchto);
+ ANT_RequestMessage(chan, MESG_CAPABILITIES_ID); //informative
+ ANT_SetChannelRFFreq(chan, freq);
+ ANT_SetSearchWaveform(chan, waveform);
+ ANT_SetChannelId(chan, devno, devtype, manid);
+ ANT_OpenChannel(chan);
+ ANT_RequestMessage(chan, MESG_CHANNEL_STATUS_ID); //informative
+
+ // everything handled in event functions
+ for(;;)
+ sleep(10);
+}
--- /dev/null
+// copyright 2008-2009 paul@ant.sbrk.co.uk. released under GPLv3
+// copyright 2009-2009 Wali
+// vers 0.6t
+#include <stdio.h>
+#include <sys/select.h>
+#include <sys/time.h>
+#include <sys/types.h>
+#include <unistd.h>
+#include <string.h>
+#include <stdlib.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#include <assert.h>
+#include <time.h>
+#include <math.h>
+
+#include "antdefs.h"
+
+char *releasetime = "Jan 21 2008, 12:00:00";
+uint majorrelease = 0;
+uint minorrelease = 6;
+
+double round(double);
+
+int gottype;
+int sentauth;
+int gotwatchid;
+int nopairing;
+int nowriteauth;
+int reset;
+int dbg = 0;
+int seenphase0 = 0;
+int lastphase;
+int sentack2;
+int newfreq = 0;
+int period = 0x1000; // garmin specific broadcast period
+int donebind = 0;
+int sentgetv;
+char *fname = "garmin";
+
+static char ANTSPT_KEY[] = "A8A423B9F55E63C1"; // ANT+Sport key
+
+static uchar ebuf[MESG_DATA_SIZE]; // response event data gets stored here
+static uchar cbuf[MESG_DATA_SIZE]; // channel event data gets stored here
+
+int passive;
+int semipassive;
+int verbose;
+
+int downloadfinished = 0;
+int downloadstarted = 0;
+int sentid = 0;
+
+uint mydev = 0;
+uint peerdev;
+uint myid;
+uint devid;
+uint myauth1;
+uint myauth2;
+char authdata[32];
+uint pairing;
+uint isa50;
+uint isa405;
+uint waitauth;
+int nphase0;
+char modelname[256];
+ushort part = 0;
+ushort ver = 0;
+uint unitid = 0;
+
+
+//char *getversion = "440dffff00000000fe00000000000000";
+//char *getgpsver = "440dffff0000000006000200ff000000";
+char *acks[] = {
+ "fe00000000000000", // get version - 255, 248, 253
+ "0e02000000000000", // device short name (fr405a) - 525
+// "1c00020000000000", // no data
+ "0a0002000e000000", // unit id - 38
+ "0a00020002000000", // send position
+ "0a00020005000000", // send time
+ "0a000200ad020000", // 4 byte something? 0x10270000 = 10000 dec - 1523
+ "0a000200c6010000", // 3 x 4 ints? - 994
+ "0a00020035020000", // guessing this is # trackpoints per run - 1066
+ "0a00020097000000", // load of software versions - 247
+ "0a000200c2010000", // download runs - 27 (#runs), 990?, 12?
+ "0a00020075000000", // download laps - 27 (#laps), 149 laps, 12?
+ "0a00020006000000", // download trackpoints - 1510/99(run marker), ..1510,12
+ "0a000200ac020000",
+ ""
+};
+int sentcmd;
+
+uchar clientid[3][8];
+
+int authfd = -1;
+char *authfile;
+int outfd; // output file
+char *fn = "default_output_file";
+char *progname;
+
+#define BSIZE 8*100
+uchar *burstbuf = 0;
+uchar *blast = 0;
+int blsize = 0;
+int bused = 0;
+int lseq = -1;
+
+/* round a float as garmin does it! */
+/* shoot me for writing this! */
+char *
+ground(double d)
+{
+ int neg = 0;
+ static char res[30];
+ ulong ival;
+ ulong l; /* hope it doesn't overflow */
+
+ if (d < 0) {
+ neg = 1;
+ d = -d;
+ }
+ ival = floor(d);
+ d -= ival;
+ l = floor(d*100000000);
+ if (l % 10 >= 5)
+ l = l/10+1;
+ else
+ l = l/10;
+ sprintf(res,"%s%ld.%07ld", neg?"-":"", ival, l);
+ return res;
+}
+
+char *
+timestamp(void)
+{
+ struct timeval tv;
+ static char time[50];
+ struct tm *tmp;
+
+ gettimeofday(&tv, 0);
+ tmp = gmtime(&tv.tv_sec);
+
+ sprintf(time, "%02d:%02d:%02d.%02d",
+ tmp->tm_hour, tmp->tm_min, tmp->tm_sec, (int)tv.tv_usec/10000);
+ return time;
+}
+
+uint
+randno(void)
+{
+ uint r;
+
+ int fd = open("/dev/urandom", O_RDONLY);
+ if (fd > 0) {
+ read(fd, &r, sizeof r);
+ close(fd);
+ }
+ return r;
+}
+
+void
+print_tcx_header(FILE *tcxfile)
+{
+ fprintf(tcxfile, "<?xml version=\"1.0\" encoding=\"UTF-8\" standalone=\"no\" ?>\n");
+ fprintf(tcxfile, "<TrainingCenterDatabase xmlns=\"http://www.garmin.com/xmlschemas/TrainingCenterDatabase/v2\" xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\" xsi:schemaLocation=\"http://www.garmin.com/xmlschemas/ActivityExtension/v2 http://www.garmin.com/xmlschemas/ActivityExtensionv2.xsd http://www.garmin.com/xmlschemas/TrainingCenterDatabase/v2 http://www.garmin.com/xmlschemas/TrainingCenterDatabasev2.xsd\">\n\n");
+ fprintf(tcxfile, " <Activities>\n");
+ return;
+}
+
+void
+print_tcx_footer(FILE *tcxfile)
+{
+ fprintf(tcxfile, " </Track>\n");
+ fprintf(tcxfile, " </Lap>\n");
+ fprintf(tcxfile, " <Creator xsi:type=\"Device_t\">\n");
+ fprintf(tcxfile, " <Name>%s</Name>\n", modelname);
+ fprintf(tcxfile, " <UnitId>%u</UnitId>\n", unitid);
+ fprintf(tcxfile, " <ProductID>%u</ProductID>\n", part);
+ fprintf(tcxfile, " <Version>\n");
+ fprintf(tcxfile, " <VersionMajor>%u</VersionMajor>\n", ver/100);
+ fprintf(tcxfile, " <VersionMinor>%u</VersionMinor>\n", ver - ver/100*100);
+ fprintf(tcxfile, " <BuildMajor>0</BuildMajor>\n");
+ fprintf(tcxfile, " <BuildMinor>0</BuildMinor>\n");
+ fprintf(tcxfile, " </Version>\n");
+ fprintf(tcxfile, " </Creator>\n");
+ fprintf(tcxfile, " </Activity>\n");
+ fprintf(tcxfile, " </Activities>\n\n");
+ fprintf(tcxfile, " <Author xsi:type=\"Application_t\">\n");
+ fprintf(tcxfile, " <Name>Garmin ANT for Linux</Name>\n");
+ fprintf(tcxfile, " <Build>\n");
+ fprintf(tcxfile, " <Version>\n");
+ fprintf(tcxfile, " <VersionMajor>%u</VersionMajor>\n", majorrelease);
+ fprintf(tcxfile, " <VersionMinor>%u</VersionMinor>\n", minorrelease);
+ fprintf(tcxfile, " <BuildMajor>0</BuildMajor>\n");
+ fprintf(tcxfile, " <BuildMinor>0</BuildMinor>\n");
+ fprintf(tcxfile, " </Version>\n");
+ fprintf(tcxfile, " <Type>Release</Type>\n");
+ fprintf(tcxfile, " <Time>%s</Time>\n", releasetime);
+ fprintf(tcxfile, " <Builder>make</Builder>\n");
+ fprintf(tcxfile, " </Build>\n");
+ fprintf(tcxfile, " <LangID>EN</LangID>\n");
+ fprintf(tcxfile, " <PartNumber>006-A0XXX-00</PartNumber>\n");
+ fprintf(tcxfile, " </Author>\n\n");
+ fprintf(tcxfile, "</TrainingCenterDatabase>\n");
+ return;
+}
+
+
+#pragma pack(1)
+struct ack_msg {
+ uchar code;
+ uchar atype;
+ uchar c1;
+ uchar c2;
+ uint id;
+};
+
+struct auth_msg {
+ uchar code;
+ uchar atype;
+ uchar phase;
+ uchar u1;
+ uint id;
+ uint auth1;
+ uint auth2;
+ uint fill1;
+ uint fill2;
+};
+
+struct pair_msg {
+ uchar code;
+ uchar atype;
+ uchar phase;
+ uchar u1;
+ uint id;
+ char devname[16];
+};
+
+#pragma pack()
+#define ACKSIZE 8 // above structure must be this size
+#define AUTHSIZE 24 // ditto
+#define PAIRSIZE 16
+#define MAXLAPS 256 // max of saving laps data before output with trackpoint data
+#define MAXTRACK 256 // max number of tracks to be saved per download
+
+decode(ushort bloblen, ushort pkttype, ushort pktlen, int dsize, uchar *data)
+{
+ int i;
+ int j;
+ int hr;
+ int hr_av;
+ int hr_max;
+ int cal;
+ float tsec;
+ float max_speed;
+ int cad;
+ int u1, u2;
+ int doff = 20;
+ char model[256];
+ char gpsver[256];
+ char devname[256];
+ float alt;
+ float dist;
+ uint tv;
+ uint tv_previous = 0;
+ time_t ttv;
+ char tbuf[100];
+ struct tm *tmp;
+ double lat, lon;
+ uint nruns;
+ uint tv_lap;
+ static uchar lapbuf[MAXLAPS][48];
+ static ushort lap = 0;
+ static ushort lastlap = 0;
+ static ushort track = 0;
+ static short previoustrack_id = -1;
+ static short track_id = -1;
+ static short firsttrack_id = -1;
+ static short firstlap_id = -1;
+ static ushort firstlap_id_track[MAXTRACK];
+ static uchar sporttyp_track[MAXTRACK];
+ static FILE *tcxfile = NULL;
+ static ushort track_pause = 0;
+
+
+ printf("decode %d %d %d %d\n", bloblen, pkttype, pktlen, dsize);
+ switch (pkttype) {
+ case 255:
+ memset(model, 0, sizeof model);
+ memcpy(model, data+doff+4, dsize-4);
+ part=data[doff]+data[doff+1]*256;
+ ver=data[doff+2]+data[doff+3]*256;
+ printf("%d Part#: %d ver: %d Name: %s\n", pkttype,
+ part, ver, model);
+ break;
+ case 248:
+ memset(gpsver, 0, sizeof gpsver);
+ memcpy(gpsver, data+doff, dsize);
+ printf("%d GPSver: %s\n", pkttype,
+ gpsver);
+ break;
+ case 253:
+ printf("%d Unknown\n", pkttype);
+ for (i = 0; i < pktlen; i += 3)
+ printf("%d.%d.%d\n", data[doff+i], data[doff+i+1], data[doff+i+2]);
+ break;
+ case 525:
+ memset(devname, 0, sizeof devname);
+ memcpy(devname, data+doff, dsize);
+ printf("%d Devname %s\n", pkttype, devname);
+ break;
+ case 12:
+ printf("%d xfer complete", pkttype);
+ for (i = 0; i < pktlen; i += 2)
+ printf(" %u", data[doff+i] + data[doff+i+1]*256);
+ printf("\n");
+ switch (data[doff] + data[doff+1]*256) {
+ case 6:
+ // last file completed, add footer and close file
+ print_tcx_footer(tcxfile);
+ fclose(tcxfile);
+ break;
+ case 117:
+ break;
+ case 450:
+ break;
+ default:
+ break;
+ }
+ break;
+ case 38:
+ unitid = data[doff] + data[doff+1]*256 +
+ data[doff+2]*256*256 + data[doff+3]*256*256*256;
+ printf("%d unitid %u\n", pkttype, unitid);
+ break;
+ case 27:
+ nruns = data[doff] + data[doff+1] * 256;
+ printf("%d nruns %u\n", pkttype, nruns);
+ break;
+ case 1523:
+ case 994:
+ case 1066:
+ printf("%d ints?", pkttype);
+ for (i = 0; i < pktlen; i += 4)
+ printf(" %u", data[doff+i] + data[doff+i+1]*256 +
+ data[doff+i+2]*256*256 + data[doff+i+3]*256*256*256);
+ printf("\n");
+ break;
+ case 14:
+ printf("%d time: ", pkttype);
+ printf("%02u-%02u-%u %02u:%02u:%02u\n", data[doff], data[doff+1], data[doff+2] + data[doff+3]*256,
+ data[doff+4], data[doff+6], data[doff+7]);
+ break;
+ case 17:
+ printf("%d position ? ", pkttype);
+ for (i = 0; i < pktlen; i += 4)
+ printf(" %u", data[doff+i] + data[doff+i+1]*256 +
+ data[doff+i+2]*256*256 + data[doff+i+3]*256*256*256);
+ printf("\n");
+ break;
+ case 99:
+ printf("%d trackindex %u\n", pkttype, data[doff] + data[doff+1]*256);
+ printf("%d shorts?", pkttype);
+ for (i = 0; i < pktlen; i += 2)
+ printf(" %u", data[doff+i] + data[doff+i+1]*256);
+ printf("\n");
+ track_id = data[doff] + data[doff+1]*256;
+ break;
+ case 990:
+ printf("%d track %u lap %u-%u sport %u\n", pkttype,
+ data[doff] + data[doff+1]*256, data[doff+2] + data[doff+3]*256,
+ data[doff+4] + data[doff+5]*256, data[doff+6]);
+ printf("%d shorts?", pkttype);
+ for (i = 0; i < pktlen; i += 2)
+ printf(" %u", data[doff+i] + data[doff+i+1]*256);
+ printf("\n");
+ if (firstlap_id == -1) firstlap_id = data[doff+2] + data[doff+3]*256;
+ if (firsttrack_id == -1) firsttrack_id = data[doff] + data[doff+1]*256;
+ track = (data[doff] + data[doff+1]*256) - firsttrack_id;
+ if (track < MAXTRACK) {
+ firstlap_id_track[track] = data[doff+2] + data[doff+3]*256;
+ sporttyp_track[track] = data[doff+6];
+ } else {
+ printf("Error: track and lap data temporary array out of range %u!\n", track);
+ }
+ break;
+ case 1510:
+ printf("%d waypoints", pkttype);
+ for (i = 0; i < 4 && i < pktlen; i += 4)
+ printf(" %u", data[doff+i] + data[doff+i+1]*256 +
+ data[doff+i+2]*256*256 + data[doff+i+3]*256*256*256);
+ printf("\n");
+ // if trackpoints are split into more than one message 1510, do not add xml head again
+ if (previoustrack_id != track_id) {
+ // close previous file if it is not the first track to be downloaded
+ if (previoustrack_id > -1) {
+ // add xml footer and close file, the next file will be open further down
+ print_tcx_footer(tcxfile);
+ fclose(tcxfile);
+ }
+ // use first lap starttime as filename
+ lap = firstlap_id_track[track_id-firsttrack_id] - firstlap_id;
+ if (dbg) printf("lap %u track_id %u firsttrack_id %u firstlap_id %u\n", lap, track_id, firsttrack_id, firstlap_id);
+ tv_lap = lapbuf[lap][4] + lapbuf[lap][5]*256 +
+ lapbuf[lap][6]*256*256 + lapbuf[lap][7]*256*256*256;
+ ttv = tv_lap + 631065600; // garmin epoch offset
+ strftime(tbuf, sizeof tbuf, "%d.%m.%Y %H%M%S.TCX", localtime(&ttv));
+ // open file and start with header of xml file
+ tcxfile = fopen(tbuf, "wt");
+ print_tcx_header(tcxfile);
+ }
+ for (i = 4; i < pktlen; i += 24) {
+ tv = (data[doff+i+8] + data[doff+i+9]*256 +
+ data[doff+i+10]*256*256 + data[doff+i+11]*256*256*256);
+ tv_lap = lapbuf[lap][4] + lapbuf[lap][5]*256 +
+ lapbuf[lap][6]*256*256 + lapbuf[lap][7]*256*256*256;
+ if ((tv > tv_lap || (tv == tv_lap && lap == (firstlap_id_track[track_id-firsttrack_id] - firstlap_id))) && lap <= lastlap) {
+ ttv = tv_lap + 631065600; // garmin epoch offset
+ strftime(tbuf, sizeof tbuf, "%Y-%m-%dT%H:%M:%SZ", gmtime(&ttv));
+ tsec = (lapbuf[lap][8] + lapbuf[lap][9]*256 +
+ lapbuf[lap][10]*256*256 + lapbuf[lap][11]*256*256*256);
+ memcpy((void *)&dist, &lapbuf[lap][12], 4);
+ memcpy((void *)&max_speed, &lapbuf[lap][16], 4);
+ cal = lapbuf[lap][36] + lapbuf[lap][37]*256;
+ hr_av = lapbuf[lap][38];
+ hr_max = lapbuf[lap][39];
+ cad = lapbuf[lap][41];
+ if (lap == firstlap_id_track[track_id-firsttrack_id] - firstlap_id) {
+ fprintf(tcxfile, " <Activity Sport=\"");
+ switch(sporttyp_track[track_id-firsttrack_id]) {
+ case 0: fprintf(tcxfile, "Running"); break;
+ case 1: fprintf(tcxfile, "Biking"); break;
+ case 2: fprintf(tcxfile, "Other"); break;
+ default: fprintf(tcxfile, "unknown value: %d",sporttyp_track[track_id-firsttrack_id]);
+ }
+ fprintf(tcxfile, "\">\n");
+ fprintf(tcxfile, " <Id>%s</Id>\n", tbuf);
+ } else {
+ fprintf(tcxfile, " </Track>\n");
+ fprintf(tcxfile, " </Lap>\n");
+ }
+ fprintf(tcxfile, " <Lap StartTime=\"%s\">\n", tbuf);
+ fprintf(tcxfile, " <TotalTimeSeconds>%s</TotalTimeSeconds>\n", ground(tsec/100));
+ fprintf(tcxfile, " <DistanceMeters>%s</DistanceMeters>\n", ground(dist));
+ fprintf(tcxfile, " <MaximumSpeed>%s</MaximumSpeed>\n", ground(max_speed));
+ fprintf(tcxfile, " <Calories>%d</Calories>\n", cal);
+ if (hr_av > 0) {
+ fprintf(tcxfile, " <AverageHeartRateBpm xsi:type=\"HeartRateInBeatsPerMinute_t\">\n");
+ fprintf(tcxfile, " <Value>%d</Value>\n", hr_av);
+ fprintf(tcxfile, " </AverageHeartRateBpm>\n");
+ }
+ if (hr_max > 0) {
+ fprintf(tcxfile, " <MaximumHeartRateBpm xsi:type=\"HeartRateInBeatsPerMinute_t\">\n");
+ fprintf(tcxfile, " <Value>%d</Value>\n", hr_max);
+ fprintf(tcxfile, " </MaximumHeartRateBpm>\n");
+ }
+ fprintf(tcxfile, " <Intensity>");
+ switch (lapbuf[lap][40]) {
+ case 0: fprintf(tcxfile, "Active"); break;
+ case 1: fprintf(tcxfile, "Rest"); break;
+ default: fprintf(tcxfile, "unknown value: %d", lapbuf[lap][40]);
+ }
+ fprintf(tcxfile, "</Intensity>\n");
+ if (cad != 255) {
+ if (sporttyp_track[track_id-firsttrack_id] == 0) {
+ fprintf(tcxfile, " <RunCadence>%d</RunCadence>\n", cad);
+ } else {
+ fprintf(tcxfile, " <Cadence>%d</Cadence>\n", cad);
+ }
+ }
+ fprintf(tcxfile, " <TriggerMethod>");
+ switch(lapbuf[lap][42]) {
+ case 4: fprintf(tcxfile, "Heartrate"); break;
+ case 3: fprintf(tcxfile, "Time"); break;
+ case 2: fprintf(tcxfile, "Location"); break;
+ case 1: fprintf(tcxfile, "Distance"); break;
+ case 0: fprintf(tcxfile, "Manual"); break;
+ default: fprintf(tcxfile, "unknown value: %d", lapbuf[lap][42]);
+ }
+ fprintf(tcxfile, "</TriggerMethod>\n");
+ fprintf(tcxfile, " <Track>\n");
+ lap++;
+ track_pause = 0;
+ // if the previous trackpoint has same second as lap time display the trackpoint again
+ if (dbg) printf("i %u tv %d tv_lap %d tv_previous %d\n", i, tv, tv_lap, tv_previous);
+ if (tv_previous == tv_lap) {
+ i -= 24;
+ tv = tv_previous;
+ }
+ } // end of if (tv >= tv_lap && lap <= lastlap)
+ if (track_pause) {
+ fprintf(tcxfile, " </Track>\n");
+ fprintf(tcxfile, " <Track>\n");
+ track_pause = 0;
+ if (dbg) printf("track pause (stop and go)\n");
+ }
+ ttv = tv+631065600; // garmin epoch offset
+ tmp = gmtime(&ttv);
+ strftime(tbuf, sizeof tbuf, "%Y-%m-%dT%H:%M:%SZ", tmp); // format for printing
+ memcpy((void *)&alt, data+doff+i+12, 4);
+ memcpy((void *)&dist, data+doff+i+16, 4);
+ lat = (data[doff+i] + data[doff+i+1]*256 +
+ data[doff+i+2]*256*256 + data[doff+i+3]*256*256*256)*180.0/0x80000000;
+ lon = (data[doff+i+4] + data[doff+i+5]*256 +
+ data[doff+i+6]*256*256 + data[doff+i+7]*256*256*256)*180.0/0x80000000;
+ hr = data[doff+i+20];
+ cad = data[doff+i+21];
+ u1 = data[doff+i+22];
+ u2 = data[doff+i+23];
+ if (dbg) printf("lat %.10g lon %.10g hr %d cad %d u1 %d u2 %d tv %d %s alt %f dist %f %02x %02x%02x%02x%02x\n", lat, lon,
+ hr, cad, u1, u2, tv, tbuf, alt, dist, data[doff+i+3], data[doff+i+16], data[doff+i+17], data[doff+i+18], data[doff+i+19]);
+ fprintf(tcxfile, " <Trackpoint>\n");
+ fprintf(tcxfile, " <Time>%s</Time>\n",tbuf);
+ if (lat < 90) {
+ fprintf(tcxfile, " <Position>\n");
+ fprintf(tcxfile, " <LatitudeDegrees>%s</LatitudeDegrees>\n",
+ ground(lat));
+ fprintf(tcxfile, " <LongitudeDegrees>%s</LongitudeDegrees>\n",
+ ground(lon));
+ fprintf(tcxfile, " </Position>\n");
+ fprintf(tcxfile, " <AltitudeMeters>%s</AltitudeMeters>\n", ground(alt));
+ }
+ // last trackpoint has utopic distance, 40000km should be enough, hack?
+ if (dist < (float)40000000) {
+ fprintf(tcxfile, " <DistanceMeters>%s</DistanceMeters>\n", ground(dist));
+ }
+ if (hr > 0) {
+ fprintf(tcxfile, " <HeartRateBpm xsi:type=\"HeartRateInBeatsPerMinute_t\">\n");
+ fprintf(tcxfile, " <Value>%d</Value>\n", hr);
+ fprintf(tcxfile, " </HeartRateBpm>\n");
+ }
+ if (u1 > 0 && cad != 255) {
+ fprintf(tcxfile, " <Cadence>%d</Cadence>\n", cad);
+ }
+ if (dist < (float)40000000) {
+ fprintf(tcxfile, " <SensorState>%s</SensorState>\n", u1 ? "Present" : "Absent");
+ fprintf(tcxfile, " <Extensions>\n");
+ fprintf(tcxfile, " <TPX xmlns=\"http://www.garmin.com/xmlschemas/ActivityExtension/v2\" CadenceSensor=\"");
+ // get type of pod from data, could not figure it out, so using sporttyp of first track
+ switch(sporttyp_track[track_id-firsttrack_id]) {
+ case 1: fprintf(tcxfile, "Bike\""); break;
+ case 0:
+ default: fprintf(tcxfile, "Footpod\""); break;
+ }
+ if (u1 == 0 && cad != 255) {
+ fprintf(tcxfile, ">\n");
+ fprintf(tcxfile, " <RunCadence>%d</RunCadence>\n", cad);
+ fprintf(tcxfile, " </TPX>\n");
+ } else {
+ fprintf(tcxfile, "/>\n");
+ }
+ fprintf(tcxfile, " </Extensions>\n");
+ } else {
+ // maybe if we recieve utopic position and distance this tells pause in the run (stop and go)
+ if (track_pause == 0) track_pause = 1;
+ else track_pause = 0;
+ }
+ fprintf(tcxfile, " </Trackpoint>\n");
+ tv_previous = tv;
+ } // end of for (i = 4; i < pktlen; i += 24)
+ previoustrack_id = track_id;
+ break;
+ case 149:
+ printf("%d Lap data id: %u %u\n", pkttype,
+ data[doff] + data[doff+1]*256, data[doff+2] + data[doff+3]*256);
+ if (lap < MAXLAPS) {
+ memcpy((void *)&lapbuf[lap][0], data+doff, 48);
+ lastlap = lap;
+ lap++;
+ }
+ break;
+ case 247:
+ memset(modelname, 0, sizeof modelname);
+ memcpy(modelname, data+doff+88, dsize-88);
+ printf("%d Device name %s\n", pkttype, modelname);
+ break;
+ default:
+ printf("don't know how to decode packet type %d\n", pkttype);
+ for (i = doff; i < dsize && i < doff+pktlen; i++)
+ printf("%02x", data[i]);
+ printf("\n");
+ for (i = doff; i < dsize && i < doff+pktlen; i++)
+ if (isprint(data[i]))
+ printf("%c", data[i]);
+ else
+ printf(".");
+ printf("\n");
+ }
+}
+
+void
+usage(void)
+{
+ fprintf(stderr, "Usage: %s -a authfile\n"
+ "[ -o outfile ]\n"
+ "[ -d devno ]\n"
+ "[ -i id ]\n"
+ "[ -m mydev ]\n"
+ "[ -p ]\n",
+ progname
+ );
+ exit(1);
+}
+
+uchar
+chevent(uchar chan, uchar event)
+{
+ uchar seq;
+ uchar last;
+ uchar status;
+ uchar phase;
+ uint newdata;
+ struct ack_msg ack;
+ struct auth_msg auth;
+ struct pair_msg pair;
+ uint id;
+ int i;
+ uint cid;
+ if (dbg) printf("chevent %02x %02x\n", chan, event);
+
+ if (event == EVENT_RX_BROADCAST) {
+ status = cbuf[1] & 0xd7;
+ newdata = cbuf[1] & 0x20;
+ phase = cbuf[2];
+ }
+ cid = cbuf[4]+cbuf[5]*256+cbuf[6]*256*256+cbuf[7]*256*256*256;
+ memcpy((void *)&id, cbuf+4, 4);
+
+ if (dbg)
+ fprintf(stderr, "cid %08x myid %08x\n", cid, myid);
+ if (dbg && event != EVENT_RX_BURST_PACKET) {
+ fprintf(stderr, "chan %d event %02x channel open: ", chan, event);
+ for (i = 0; i < 8; i++)
+ fprintf(stderr, "%02x", cbuf[i]);
+ fprintf(stderr, "\n");
+ }
+
+ switch (event) {
+ case EVENT_RX_BROADCAST:
+ lastphase = phase; // store the last phase we see the watch broadcast
+ if (dbg) printf("lastphase %d\n", lastphase);
+ if (!pairing && !nopairing)
+ pairing = cbuf[1] & 8;
+ if (!gottype) {
+ gottype = 1;
+ isa50 = cbuf[1] & 4;
+ isa405 = cbuf[1] & 1;
+ if ((isa50 && isa405) || (!isa50 && !isa405)) {
+ fprintf(stderr, "50 %d and 405 %d\n", isa50, isa405);
+ exit(1);
+ }
+ }
+ if (verbose) {
+ switch (phase) {
+ case 0:
+ fprintf(stderr, "%s BC0 %02x %d %d %d PID %d %d %d %c%c\n",
+ timestamp(),
+ cbuf[0], cbuf[1] & 0xd7, cbuf[2], cbuf[3],
+ cbuf[4]+cbuf[5]*256, cbuf[6], cbuf[7],
+ (cbuf[1] & 0x20) ? 'N' : ' ', (cbuf[1] & 0x08) ? 'P' : ' '
+ );
+ break;
+ case 1:
+ fprintf(stderr, "%s BC1 %02x %d %d %d CID %08x %c%c\n",
+ timestamp(),
+ cbuf[0], cbuf[1] & 0xd7, cbuf[2], cbuf[3], cid,
+ (cbuf[1] & 0x20) ? 'N' : ' ', (cbuf[1] & 0x08) ? 'P' : ' '
+ );
+ break;
+ fprintf(stderr, "%s BCX %02x %d %d %d PID %d %d %d %c%c\n",
+ timestamp(),
+ cbuf[0], cbuf[1] & 0xd7, cbuf[2], cbuf[3],
+ cbuf[4]+cbuf[5]*256, cbuf[6], cbuf[7],
+ (cbuf[1] & 0x20) ? 'N' : ' ', (cbuf[1] & 0x08) ? 'P' : ' '
+ );
+ default:
+ break;
+ }
+ }
+
+ if (dbg)
+ printf("watch status %02x stage %d id %08x\n", status, phase, id);
+
+ if (!sentid) {
+ sentid = 1;
+ ANT_RequestMessage(chan, MESG_CHANNEL_ID_ID); /* request sender id */
+ }
+
+ // if we don't see a phase 0 message first, reset the watch
+ if (reset || (phase != 0 && !seenphase0)) {
+ fprintf(stderr, "resetting\n");
+ ack.code = 0x44; ack.atype = 3; ack.c1 = 0x00; ack.c2 = 0x00; ack.id = 0;
+ ANT_SendAcknowledgedData(chan, (void *)&ack); // tell garmin we're finished
+ sleep(1);
+ exit(1);
+ }
+ switch (phase) {
+ case 0:
+ seenphase0 = 1;
+ nphase0++;
+ if (nphase0 % 10 == 0)
+ donebind = 0;
+ if (newfreq) {
+ // switch to new frequency
+ ANT_SetChannelPeriod(chan, period);
+ ANT_SetChannelSearchTimeout(chan, 3);
+ ANT_SetChannelRFFreq(chan, newfreq);
+ newfreq = 0;
+ }
+ // phase 0 seen after reset at end of download
+ if (downloadfinished) {
+ fprintf(stderr, "finished\n");
+ exit(0);
+ }
+ // generate a random id if pairing and user didn't specify one
+ if (pairing && !myid) {
+ myid = randno();
+ fprintf(stderr, "pairing, using id %08x\n", myid);
+ }
+ // need id codes from auth file if not pairing
+ // TODO: handle multiple watches
+ // BUG: myauth1 should be allowed to be 0
+ if (!pairing && !myauth1) {
+ int nr;
+ printf("reading auth data from %s\n", authfile);
+ authfd = open(authfile, O_RDONLY);
+ if (authfd < 0) {
+ perror(authfile);
+ fprintf(stderr, "No auth data. Need to pair first\n");
+ exit(1);
+ }
+ nr = read(authfd, authdata, 32);
+ close(authfd);
+ if (nr != 32 && nr != 24) {
+ fprintf(stderr, "bad auth file len %d != 32 or 24\n", nr);
+ exit(1);
+ }
+ // BUG: auth file not portable
+ memcpy((void *)&myauth1, authdata+16, 4);
+ memcpy((void *)&myauth2, authdata+20, 4);
+ memcpy((void *)&mydev, authdata+12, 4);
+ memcpy((void *)&myid, authdata+4, 4);
+ if (dbg)
+ fprintf(stderr, "dev %08x auth %08x %08x id %08x\n",
+ mydev, myauth1, myauth2, myid);
+ }
+ // bind to watch
+ if (!donebind && devid) {
+ donebind = 1;
+ if (isa405)
+ newfreq = 0x32;
+ ack.code = 0x44; ack.atype = 2; ack.c1 = isa50 ? 0x32 : newfreq; ack.c2 = 0x04;
+ ack.id = myid;
+ ANT_SendAcknowledgedData(chan, (void *)&ack); // bind
+ } else {
+ if (dbg) printf("donebind %d devid %x\n", donebind, devid);
+ }
+ break;
+ case 1:
+ if (dbg) printf("case 1 %x\n", peerdev);
+ if (peerdev) {
+ if (dbg) printf("case 1 peerdev\n");
+ // if watch has sent id
+ if (mydev != 0 && peerdev != mydev) {
+ fprintf(stderr, "Don't know this device %08x != %08x\n", peerdev, mydev);
+ } else if (!sentauth && !waitauth) {
+ if (dbg) printf("case 1 diffdev\n");
+ assert(sizeof auth == AUTHSIZE);
+ auth.code = 0x44; auth.atype = 4; auth.phase = 3; auth.u1 = 8;
+ auth.id = myid; auth.auth1 = myauth1; auth.auth2 = myauth2;
+ auth.fill1 = auth.fill2 = 0;
+ sentauth = 1;
+ ANT_SendBurstTransfer(chan, (void *)&auth, (sizeof auth)/8); // send our auth data
+ }
+ }
+ if (dbg) printf("case 1 cid %x myid %x\n", cid, myid);
+ if (!sentack2 && cid == myid && !waitauth) {
+ sentack2 = 1;
+ if (dbg) printf("sending ack2\n");
+ // if it did bind to me before someone else
+ ack.code = 0x44; ack.atype = 4; ack.c1 = 0x01; ack.c2 = 0x00;
+ ack.id = myid;
+ ANT_SendAcknowledgedData(chan, (void *)&ack); // request id
+ }
+ break;
+ case 2:
+ // successfully authenticated
+ if (!downloadstarted) {
+ downloadstarted = 1;
+ if (dbg) printf("starting download\n");
+ ack.code = 0x44; ack.atype = 6; ack.c1 = 0x01; ack.c2 = 0x00; ack.id = 0;
+ //ANT_SendAcknowledgedData(chan, (void *)&ack); // tell garmin to start upload
+ }
+ if (downloadfinished) {
+ if (dbg) printf("finished download\n");
+ ack.code = 0x44; ack.atype = 3; ack.c1 = 0x00; ack.c2 = 0x00; ack.id = 0;
+ if (!passive) ANT_SendAcknowledgedData(chan, (void *)&ack); // tell garmin we're finished
+ }
+ break;
+ case 3:
+ if (pairing) {
+ // waiting for the user to pair
+ printf("Please press \"View\" on watch to confirm pairing\n");
+ waitauth = 2; // next burst data is auth data
+ } else {
+ if (dbg) printf("not sure why in phase 3\n");
+ if (!sentgetv) {
+ sentgetv = 1;
+ //ANT_SendBurstTransferA(chan, getversion, strlen(getversion)/16);
+ }
+ }
+ break;
+ default:
+ if (dbg) fprintf(stderr, "Unknown phase %d\n", phase);
+ break;
+ }
+ break;
+ case EVENT_RX_BURST_PACKET:
+ // now handled in coalesced burst below
+ if (dbg) printf("burst\n");
+ break;
+ case EVENT_RX_FAKE_BURST:
+ if (dbg) printf("rxfake burst pairing %d blast %ld waitauth %d\n",
+ pairing, (long)blast, waitauth);
+ blsize = *(int *)(cbuf+4);
+ memcpy(&blast, cbuf+8, 4);
+ if (dbg) {
+ printf("fake burst %d %lx ", blsize, (long)blast);
+ for (i = 0; i < blsize && i < 64; i++)
+ printf("%02x", blast[i]);
+ printf("\n");
+ for (i = 0; i < blsize; i++)
+ if (isprint(blast[i]))
+ printf("%c", blast[i]);
+ else
+ printf(".");
+ printf("\n");
+ }
+ if (sentauth) {
+ static int nacksent = 0;
+ char *ackdata;
+ static uchar ackpkt[100];
+ // ack the last packet
+ ushort bloblen = blast[14]+256*blast[15];
+ ushort pkttype = blast[16]+256*blast[17];
+ ushort pktlen = blast[18]+256*blast[19];
+ if (bloblen == 0) {
+ if (dbg) printf("bloblen %d, get next data\n", bloblen);
+ // request next set of data
+ ackdata = acks[nacksent++];
+ if (!strcmp(ackdata, "")) { // finished
+ printf("acks finished, resetting\n");
+ ack.code = 0x44; ack.atype = 3; ack.c1 = 0x00;
+ ack.c2 = 0x00; ack.id = 0;
+ ANT_SendAcknowledgedData(chan, (void *)&ack); // go to idle
+ sleep(1);
+ exit(1);
+ }
+ if (dbg) printf("got type 0, sending ack %s\n", ackdata);
+ sprintf(ackpkt, "440dffff00000000%s", ackdata);
+ } else if (bloblen == 65535) {
+ // repeat last ack
+ if (dbg) printf("repeating ack %s\n", ackpkt);
+ ANT_SendBurstTransferA(chan, ackpkt, strlen(ackpkt)/16);
+ } else {
+ if (dbg) printf("non-0 bloblen %d\n", bloblen);
+ decode(bloblen, pkttype, pktlen, blsize, blast);
+ sprintf(ackpkt, "440dffff0000000006000200%02x%02x0000", pkttype%256, pkttype/256);
+ }
+ if (dbg) printf("received pkttype %d len %d\n", pkttype, pktlen);
+ if (dbg) printf("acking %s\n", ackpkt);
+ ANT_SendBurstTransferA(chan, ackpkt, strlen(ackpkt)/16);
+ } else if (!nopairing && pairing && blast) {
+ memcpy(&peerdev, blast+12, 4);
+ if (dbg)
+ printf("watch id %08x waitauth %d\n", peerdev, waitauth);
+ if (mydev != 0 && peerdev != mydev) {
+ fprintf(stderr, "Don't know this device %08x != %08x\n", peerdev, mydev);
+ exit(1);
+ }
+ if (waitauth == 2) {
+ int nw;
+ // should be receiving auth data
+ if (nowriteauth) {
+ printf("Not overwriting auth data\n");
+ exit(1);
+ }
+ printf("storing auth data in %s\n", authfile);
+ authfd = open(authfile, O_WRONLY|O_CREAT, 0644);
+ if (authfd < 0) {
+ perror(authfile);
+ exit(1);
+ }
+ nw = write(authfd, blast, blsize);
+ if (nw != blsize) {
+ fprintf(stderr, "auth write failed fd %d %d\n", authfd, nw);
+ perror("write");
+ exit(1);
+ }
+ close(authfd);
+ //exit(1);
+ pairing = 0;
+ waitauth = 0;
+ reset = 1;
+ }
+ if (pairing && !waitauth) {
+ //assert(sizeof pair == PAIRSIZE);
+ pair.code = 0x44; pair.atype = 4; pair.phase = 2; pair.id = myid;
+ bzero(pair.devname, sizeof pair.devname);
+ //if (peerdev <= 9999999) // only allow 7 digits
+ //sprintf(pair.devname, "%u", peerdev);
+ strcpy(pair.devname, fname);
+ //else
+ // fprintf(stderr, "pair dev name too large %08x \"%d\"\n", peerdev, peerdev);
+ pair.u1 = strlen(pair.devname);
+ printf("sending pair data for dev %s\n", pair.devname);
+ waitauth = 1;
+ if (isa405 && pairing) {
+ // go straight to storing auth data
+ waitauth = 2;
+ }
+ ANT_SendBurstTransfer(chan, (void *)&pair, (sizeof pair)/8) ; // send pair data
+ } else {
+ if (dbg) printf("not pairing\n");
+ }
+ } else if (!gotwatchid && (lastphase == 1)) {
+ static int once = 0;
+ gotwatchid = 1;
+ // garmin sending authentication/identification data
+ if (!once) {
+ int i;
+ once = 1;
+ if (dbg)
+ fprintf(stderr, "id data: ");
+ }
+ if (dbg)
+ for (i = 0; i < blsize; i++)
+ fprintf(stderr, "%02x", blast[i]);
+ if (dbg)
+ fprintf(stderr, "\n");
+ memcpy(&peerdev, blast+12, 4);
+ if (dbg)
+ printf("watch id %08x\n", peerdev);
+ if (mydev != 0 && peerdev != mydev) {
+ fprintf(stderr, "Don't know this device %08x != %08x\n", peerdev, mydev);
+ exit(1);
+ }
+ } else if (lastphase == 2) {
+ int nw;
+ static int once = 0;
+ printf("once %d\n", once);
+ // garmin uploading in response to sendack3
+ // in this state we're receiving the workout data
+ if (!once) {
+ printf("receiving\n");
+ once = 1;
+ outfd = open(fn, O_WRONLY|O_CREAT, 0644);
+ if (outfd < 0) {
+ perror(fn);
+ exit(1);
+ }
+ }
+ if (last) {
+ nw = write(outfd, blast, blsize);
+ if (nw != blsize) {
+ fprintf(stderr, "data write failed fd %d %d\n", outfd, nw);
+ perror("write");
+ exit(1);
+ }
+ close(outfd);
+ downloadfinished = 1;
+ }
+ } else if (0 && last) {
+ if (dbg) {
+ fprintf(stderr, "auth response: ");
+ for (i = 0; i < blsize; i++)
+ fprintf(stderr, "%02x", cbuf[i]);
+ fprintf(stderr, "\n");
+ }
+ if (blast[10] == 2) {
+ fprintf(stderr, "authentication failed\n");
+ exit(1);
+ }
+ } else if (last) {
+ fprintf(stderr, "data in state xx: ");
+ int i;
+ for (i = 0; i < blsize; i++)
+ fprintf(stderr, "%02x", blast[i]);
+ fprintf(stderr, "\n");
+ sentcmd = 1000;
+ switch (sentcmd) {
+ case 1000:
+ break;
+ case 0:
+ sentcmd++;
+ //ANT_SendBurstTransferA(chan, getgpsver, strlen(getgpsver)/16);
+ break;
+ case 999:
+ printf("finished\n");
+ exit(1);
+ default:
+ sleep(1);
+ sentcmd = 1;
+ //printf("sending command %d %s\n", sentcmd-1, cmds[sentcmd-1]);
+ //ANT_SendBurstTransferA(chan, cmds[sentcmd-1],
+ // strlen(cmds[sentcmd-1])/16);
+ sentcmd++;
+ //if(!strcmp(cmds[sentcmd-1], "END"))
+ // sentcmd = 999;
+ break;
+ }
+ }
+ if (dbg) printf("continuing after burst\n");
+ break;
+ }
+ return 1;
+}
+
+uchar
+revent(uchar chan, uchar event)
+{
+ struct ack_msg ack;
+ int i;
+
+ if (dbg) printf("revent %02x %02x\n", chan, event);
+ switch (event) {
+ case EVENT_TRANSFER_TX_COMPLETED:
+ if (dbg) printf("Transfer complete %02x\n", ebuf[1]);
+ break;
+ case INVALID_MESSAGE:
+ printf("Invalid message %02x\n", ebuf[1]);
+ break;
+ case RESPONSE_NO_ERROR:
+ switch (ebuf[1]) {
+ case MESG_ASSIGN_CHANNEL_ID:
+ ANT_AssignChannelEventFunction(chan, chevent, cbuf);
+ break;
+ case MESG_OPEN_CHANNEL_ID:
+ printf("channel open, waiting for broadcast\n");
+ break;
+ default:
+ if (dbg) printf("Message %02x NO_ERROR\n", ebuf[1]);
+ break;
+ }
+ break;
+ case MESG_CHANNEL_ID_ID:
+ devid = ebuf[1]+ebuf[2]*256;
+ if (mydev == 0 || devid == mydev%65536) {
+ if (dbg)
+ printf("devid %08x myid %08x\n", devid, myid);
+ } else {
+ printf("Ignoring unknown device %08x, mydev %08x\n", devid, mydev);
+ devid = sentid = 0; // reset
+ }
+ break;
+ case MESG_NETWORK_KEY_ID:
+ case MESG_SEARCH_WAVEFORM_ID:
+ case MESG_OPEN_CHANNEL_ID:
+ printf("response event %02x code %02x\n", event, ebuf[2]);
+ for (i = 0; i < 8; i++)
+ fprintf(stderr, "%02x", ebuf[i]);
+ fprintf(stderr, "\n");
+ break;
+ case MESG_CAPABILITIES_ID:
+ if (dbg)
+ printf("capabilities chans %d nets %d opt %02x adv %02x\n",
+ ebuf[0], ebuf[1], ebuf[2], ebuf[3]);
+ break;
+ case MESG_CHANNEL_STATUS_ID:
+ if (dbg)
+ printf("channel status %d\n", ebuf[1]);
+ break;
+ case EVENT_RX_FAIL:
+ // ignore this
+ break;
+ default:
+ printf("Unhandled response event %02x\n", event);
+ break;
+ }
+ return 1;
+}
+
+main(int ac, char *av[])
+{
+ int devnum = 0;
+ int chan = 0;
+ int net = 0;
+ int chtype = 0; // wildcard
+ int devno = 0; // wildcard
+ int devtype = 0; // wildcard
+ int manid = 0; // wildcard
+ int freq = 0x32; // garmin specific radio frequency
+ int srchto = 255; // max timeout
+ int waveform = 0x0053; // aids search somehow
+ int c;
+ extern char *optarg;
+ extern int optind, opterr, optopt;
+
+ // default auth file //
+ if (getenv("HOME")) {
+ authfile = malloc(strlen(getenv("HOME"))+strlen("/.gant")+1);
+ if (authfile)
+ sprintf(authfile, "%s/.gant", getenv("HOME"));
+ }
+ progname = av[0];
+ while ((c = getopt(ac, av, "a:o:d:i:m:PpvDrnzf:")) != -1) {
+ switch(c) {
+ case 'a':
+ authfile = optarg;
+ break;
+ case 'f':
+ fname = optarg;
+ break;
+ case 'o':
+ fn = optarg;
+ break;
+ case 'd':
+ devnum = atoi(optarg);
+ break;
+ case 'i':
+ myid = atoi(optarg);
+ break;
+ case 'm':
+ mydev = atoi(optarg);
+ break;
+ case 'p':
+ passive = 1;
+ semipassive = 0;
+ break;
+ case 'P':
+ passive = 1;
+ break;
+ case 'v':
+ verbose = 1;
+ break;
+ case 'D':
+ dbg = 1;
+ break;
+ case 'r':
+ reset = 1;
+ break;
+ case 'n':
+ nowriteauth = 1;
+ break;
+ case 'z':
+ nopairing = 1;
+ break;
+ default:
+ fprintf(stderr, "unknown option %s\n", optarg);
+ usage();
+ }
+ }
+
+ ac -= optind;
+ av += optind;
+
+ if ((!passive && !authfile) || ac)
+ usage();
+
+ if (!ANT_Init(devnum, 0)) { // should be 115200 but doesn't fit into a short
+ fprintf(stderr, "open dev %d failed\n", devnum);
+ exit(1);
+ }
+ ANT_ResetSystem();
+ ANT_AssignResponseFunction(revent, ebuf);
+ ANT_RequestMessage(chan, MESG_CHANNEL_STATUS_ID); //informative
+ ANT_SetNetworkKeya(net, ANTSPT_KEY);
+ ANT_AssignChannel(chan, chtype, net);
+ // Wali: changed order of the following seq. according windows
+ ANT_SetChannelPeriod(chan, period);
+ ANT_SetChannelSearchTimeout(chan, srchto);
+ ANT_RequestMessage(chan, MESG_CAPABILITIES_ID); //informative
+ ANT_SetChannelRFFreq(chan, freq);
+ ANT_SetSearchWaveform(chan, waveform);
+ ANT_SetChannelId(chan, devno, devtype, manid);
+ ANT_OpenChannel(chan);
+ ANT_RequestMessage(chan, MESG_CHANNEL_STATUS_ID); //informative
+
+ // everything handled in event functions
+ for(;;)
+ sleep(10);
+}
--- /dev/null
+channel open, waiting for broadcast
+reading auth data from auth405
+Unhandled response event 06
+decode 6 255 41 64
+255 Part#: 717 ver: 250 Name: Forerunner 405 Software Version 2.50
+decode 5 248 33 56
+248 GPSver: GPS GSC3LT Software Version 2.10
+decode 23 253 177 200
+253 Unknown
+80.0.0
+76.1.0
+65.10.0
+65.135.3
+65.150.3
+68.150.3
+65.247.3
+84.1.0
+65.100.0
+68.110.0
+65.201.0
+68.202.0
+68.110.0
+68.210.0
+65.46.1
+68.55.1
+68.250.3
+65.244.1
+68.245.1
+65.88.2
+68.88.2
+65.89.2
+68.89.2
+65.188.2
+68.188.2
+65.32.3
+68.32.3
+65.33.3
+68.33.3
+65.134.3
+65.139.3
+68.139.3
+68.140.3
+68.141.3
+68.142.3
+65.138.3
+68.247.3
+65.232.3
+68.241.3
+65.249.3
+68.240.3
+65.235.3
+68.235.3
+65.248.3
+68.248.3
+65.237.3
+68.237.3
+65.250.3
+68.238.3
+65.239.3
+68.239.3
+65.240.3
+68.244.3
+65.241.3
+68.245.3
+65.245.3
+68.246.3
+65.246.3
+68.249.3
+Unhandled response event 06
+Unhandled response event 06
+Unhandled response event 20
+decode 3 525 13 40
+525 Devname Jordan's 405
+decode 1 38 4 24
+38 unitid 3514345546
+decode 3 17 16 40
+17 position ? 3324428361 1072033532 3805038251 3220857419
+decode 2 14 8 32
+14 time: 10-02-2009 03:43:33
+decode 1 1523 4 24
+1523 ints? 10000
+decode 3 994 12 40
+994 ints? 200 25 200
+decode 4 1066 16 48
+1066 ints? 20 200 100 18500
+decode 27 247 212 232
+247 Device name Forerunner 405
+decode 1 27 2 24
+27 nruns 9
+decode 86 990 684 704
+990 track 32 lap 51-51 sport 0
+990 shorts? 32 51 51 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 1 0 0 0 0 0 0 0 0 0
+decode 86 990 684 704
+990 track 33 lap 52-52 sport 0
+990 shorts? 33 52 52 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 1 0 0 0 0 0 0 0 0 0
+decode 86 990 684 704
+990 track 34 lap 53-53 sport 0
+990 shorts? 34 53 53 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 1 0 0 0 0 0 0 0 0 0
+decode 86 990 684 704
+990 track 35 lap 54-54 sport 0
+990 shorts? 35 54 54 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 1 0 0 0 0 0 0 0 0 0
+decode 86 990 684 704
+990 track 36 lap 55-56 sport 0
+990 shorts? 36 55 56 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 1 0 0 0 0 0 0 0 0 0
+decode 86 990 684 704
+990 track 37 lap 57-57 sport 0
+990 shorts? 37 57 57 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 1 0 0 0 0 0 0 0 0 0
+decode 86 990 684 704
+990 track 38 lap 58-60 sport 0
+990 shorts? 38 58 60 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 1 0 0 0 0 0 0 0 0 0
+decode 86 990 684 704
+990 track 39 lap 61-62 sport 0
+990 shorts? 39 61 62 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 1 0 0 0 0 0 0 0 0 0
+decode 86 990 684 704
+990 track 40 lap 63-63 sport 0
+990 shorts? 40 63 63 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 1 0 0 0 0 0 0 0 0 0
+decode 1 12 2 24
+12 xfer complete 450
+decode 1 27 2 24
+27 nruns 13
+decode 7 149 48 72
+149 Lap data id: 51 0
+decode 7 149 48 72
+149 Lap data id: 52 0
+decode 7 149 48 72
+149 Lap data id: 53 0
+decode 7 149 48 72
+149 Lap data id: 54 0
+decode 7 149 48 72
+149 Lap data id: 55 0
+decode 7 149 48 72
+149 Lap data id: 56 0
+decode 7 149 48 72
+149 Lap data id: 57 0
+decode 7 149 48 72
+149 Lap data id: 58 0
+decode 7 149 48 72
+149 Lap data id: 59 0
+decode 7 149 48 72
+149 Lap data id: 60 0
+decode 7 149 48 72
+149 Lap data id: 61 0
+decode 7 149 48 72
+149 Lap data id: 62 0
+decode 7 149 48 72
+149 Lap data id: 63 0
+decode 1 12 2 24
+12 xfer complete 117
+decode 1 27 2 24
+27 nruns 6089
+decode 1 99 4 24
+99 trackindex 32
+99 shorts? 32 0
+decode 181 1510 1444 1464
+1510 waypoints 60
+decode 181 1510 1444 1464
+1510 waypoints 60
+decode 115 1510 916 936
+1510 waypoints 38
+decode 1 99 4 24
+99 trackindex 33
+99 shorts? 33 0
+decode 181 1510 1444 1464
+1510 waypoints 60
+decode 181 1510 1444 1464
+1510 waypoints 60
+decode 181 1510 1444 1464
+1510 waypoints 60
+decode 181 1510 1444 1464
+1510 waypoints 60
+decode 181 1510 1444 1464
+1510 waypoints 60
+decode 181 1510 1444 1464
+1510 waypoints 60
+decode 181 1510 1444 1464
+1510 waypoints 60
+decode 181 1510 1444 1464
+1510 waypoints 60
+decode 181 1510 1444 1464
+1510 waypoints 60
+decode 121 1510 964 984
+1510 waypoints 40
+decode 1 99 4 24
+99 trackindex 34
+99 shorts? 34 0
+decode 181 1510 1444 1464
+1510 waypoints 60
+decode 181 1510 1444 1464
+1510 waypoints 60
+decode 181 1510 1444 1464
+1510 waypoints 60
+decode 181 1510 1444 1464
+1510 waypoints 60
+decode 181 1510 1444 1464
+1510 waypoints 60
+decode 118 1510 940 960
+1510 waypoints 39
+decode 1 99 4 24
+99 trackindex 35
+99 shorts? 35 0
+decode 181 1510 1444 1464
+1510 waypoints 60
+Unhandled response event 06
+Unhandled response event 20
+Unhandled response event 1f
+decode 181 1510 1444 1464
+1510 waypoints 60
+decode 181 1510 1444 1464
+1510 waypoints 60
+decode 181 1510 1444 1464
+1510 waypoints 60
+decode 181 1510 1444 1464
+1510 waypoints 60
+Unhandled response event 06
+Unhandled response event 06
+decode 181 1510 1444 1464
+1510 waypoints 60
+decode 181 1510 1444 1464
+1510 waypoints 60
+decode 181 1510 1444 1464
+1510 waypoints 60
+decode 181 1510 1444 1464
+1510 waypoints 60
+decode 181 1510 1444 1464
+1510 waypoints 60
+decode 181 1510 1444 1464
+1510 waypoints 60
+decode 181 1510 1444 1464
+1510 waypoints 60
+decode 181 1510 1444 1464
+1510 waypoints 60
+decode 181 1510 1444 1464
+1510 waypoints 60
+decode 181 1510 1444 1464
+1510 waypoints 60
+decode 181 1510 1444 1464
+1510 waypoints 60
+decode 157 1510 1252 1272
+1510 waypoints 52
+decode 1 99 4 24
+99 trackindex 36
+99 shorts? 36 0
+decode 181 1510 1444 1464
+1510 waypoints 60
+Unhandled response event 06
+Unhandled response event 20
+Unhandled response event 1f
+decode 181 1510 1444 1464
+1510 waypoints 60
+decode 181 1510 1444 1464
+1510 waypoints 60
+decode 181 1510 1444 1464
+1510 waypoints 60
+decode 181 1510 1444 1464
+1510 waypoints 60
+decode 181 1510 1444 1464
+1510 waypoints 60
+decode 181 1510 1444 1464
+1510 waypoints 60
+decode 181 1510 1444 1464
+1510 waypoints 60
+decode 73 1510 580 600
+1510 waypoints 24
+decode 1 99 4 24
+99 trackindex 37
+99 shorts? 37 0
+decode 181 1510 1444 1464
+1510 waypoints 60
+decode 181 1510 1444 1464
+1510 waypoints 60
+decode 181 1510 1444 1464
+1510 waypoints 60
+Unhandled response event 06
+Unhandled response event 1f
+decode 160 1510 1276 1296
+1510 waypoints 53
+decode 1 99 4 24
+99 trackindex 38
+99 shorts? 38 0
+decode 181 1510 1444 1464
+1510 waypoints 60
+Unhandled response event 06
+Unhandled response event 20
+Unhandled response event 1f
+decode 181 1510 1444 1464
+1510 waypoints 60
+decode 181 1510 1444 1464
+1510 waypoints 60
+decode 181 1510 1444 1464
+1510 waypoints 60
+decode 181 1510 1444 1464
+1510 waypoints 60
+decode 181 1510 1444 1464
+1510 waypoints 60
+decode 181 1510 1444 1464
+1510 waypoints 60
+decode 181 1510 1444 1464
+1510 waypoints 60
+decode 181 1510 1444 1464
+1510 waypoints 60
+decode 181 1510 1444 1464
+1510 waypoints 60
+decode 181 1510 1444 1464
+1510 waypoints 60
+decode 181 1510 1444 1464
+1510 waypoints 60
+decode 181 1510 1444 1464
+1510 waypoints 60
+decode 181 1510 1444 1464
+1510 waypoints 60
+decode 181 1510 1444 1464
+1510 waypoints 60
+decode 181 1510 1444 1464
+1510 waypoints 60
+decode 70 1510 556 576
+1510 waypoints 23
+decode 1 99 4 24
+99 trackindex 39
+99 shorts? 39 0
+decode 181 1510 1444 1464
+1510 waypoints 60
+decode 181 1510 1444 1464
+1510 waypoints 60
+decode 181 1510 1444 1464
+1510 waypoints 60
+decode 181 1510 1444 1464
+1510 waypoints 60
+Unhandled response event 06
+Unhandled response event 1f
+Unhandled response event 20
+decode 181 1510 1444 1464
+1510 waypoints 60
+decode 181 1510 1444 1464
+1510 waypoints 60
+decode 181 1510 1444 1464
+1510 waypoints 60
+decode 181 1510 1444 1464
+1510 waypoints 60
+decode 181 1510 1444 1464
+1510 waypoints 60
+decode 181 1510 1444 1464
+1510 waypoints 60
+decode 181 1510 1444 1464
+1510 waypoints 60
+decode 181 1510 1444 1464
+1510 waypoints 60
+decode 181 1510 1444 1464
+1510 waypoints 60
+decode 181 1510 1444 1464
+1510 waypoints 60
+decode 181 1510 1444 1464
+1510 waypoints 60
+decode 181 1510 1444 1464
+1510 waypoints 60
+decode 181 1510 1444 1464
+1510 waypoints 60
+decode 181 1510 1444 1464
+1510 waypoints 60
+decode 181 1510 1444 1464
+1510 waypoints 60
+decode 181 1510 1444 1464
+1510 waypoints 60
+decode 181 1510 1444 1464
+1510 waypoints 60
+decode 181 1510 1444 1464
+1510 waypoints 60
+decode 181 1510 1444 1464
+1510 waypoints 60
+decode 181 1510 1444 1464
+1510 waypoints 60
+decode 181 1510 1444 1464
+1510 waypoints 60
+Unhandled response event 06
+Unhandled response event 06
+decode 181 1510 1444 1464
+1510 waypoints 60
+decode 136 1510 1084 1104
+1510 waypoints 45
+decode 1 99 4 24
+99 trackindex 40
+99 shorts? 40 0
+decode 181 1510 1444 1464
+1510 waypoints 60
+decode 181 1510 1444 1464
+1510 waypoints 60
+decode 181 1510 1444 1464
+1510 waypoints 60
+decode 181 1510 1444 1464
+1510 waypoints 60
+decode 181 1510 1444 1464
+1510 waypoints 60
+decode 181 1510 1444 1464
+1510 waypoints 60
+decode 181 1510 1444 1464
+1510 waypoints 60
+decode 181 1510 1444 1464
+1510 waypoints 60
+decode 181 1510 1444 1464
+1510 waypoints 60
+decode 181 1510 1444 1464
+1510 waypoints 60
+decode 181 1510 1444 1464
+1510 waypoints 60
+decode 19 1510 148 168
+1510 waypoints 6
+decode 1 12 2 24
+12 xfer complete 6
+acks finished, resetting
--- /dev/null
+19c19
+< char *releasetime = "Jan 21 2008, 12:00:00";
+---
+> char *releasetime = "Jan 24 2009, 16:10:12";
+464,467c464,466
+< if (cad != 255) {
+< if (sporttyp_track[track_id-firsttrack_id] == 0) {
+< fprintf(tcxfile, " <RunCadence>%d</RunCadence>\n", cad);
+< } else {
+---
+> // for bike the average cadence of this lap is here
+> if (sporttyp_track[track_id-firsttrack_id] == 1) {
+> if (cad != 255) {
+480a480,489
+> // I prefere the average run cadence here than at the end of this lap according windows ANTagent
+> if (sporttyp_track[track_id-firsttrack_id] == 0) {
+> if (cad != 255) {
+> fprintf(tcxfile, " <Extensions>\n");
+> fprintf(tcxfile, " <LX xmlns=\"http://www.garmin.com/xmlschemas/ActivityExtension/v2\">\n");
+> fprintf(tcxfile, " <AvgRunCadence>%d</AvgRunCadence>\n", cad);
+> fprintf(tcxfile, " </LX>\n");
+> fprintf(tcxfile, " </Extensions>\n");
+> }
+> }
+483d491
+< track_pause = 0;
+490,493d497
+< } // end of if (tv >= tv_lap && lap <= lastlap)
+< if (track_pause) {
+< fprintf(tcxfile, " </Track>\n");
+< fprintf(tcxfile, " <Track>\n");
+495,496c499
+< if (dbg) printf("track pause (stop and go)\n");
+< }
+---
+> } // end of if (tv >= tv_lap && lap <= lastlap)
+511a515,519
+> // track pause only if following trackpoint is aswell 'timemarker' with utopic distance
+> if (track_pause && dist > (float)40000000) {
+> fprintf(tcxfile, " </Track>\n");
+> fprintf(tcxfile, " <Track>\n");
+> }
+532,533c540,544
+< if (u1 > 0 && cad != 255) {
+< fprintf(tcxfile, " <Cadence>%d</Cadence>\n", cad);
+---
+> // for bikes the cadence is written here and for the footpod in <Extensions>, why garmin?
+> if (sporttyp_track[track_id-firsttrack_id] == 1) {
+> if (cad != 255) {
+> fprintf(tcxfile, " <Cadence>%d</Cadence>\n", cad);
+> }
+540,548c551,552
+< switch(sporttyp_track[track_id-firsttrack_id]) {
+< case 1: fprintf(tcxfile, "Bike\""); break;
+< case 0:
+< default: fprintf(tcxfile, "Footpod\""); break;
+< }
+< if (u1 == 0 && cad != 255) {
+< fprintf(tcxfile, ">\n");
+< fprintf(tcxfile, " <RunCadence>%d</RunCadence>\n", cad);
+< fprintf(tcxfile, " </TPX>\n");
+---
+> if (sporttyp_track[track_id-firsttrack_id] == 1) {
+> fprintf(tcxfile, "Bike\"/>\n");
+550c554,561
+< fprintf(tcxfile, "/>\n");
+---
+> fprintf(tcxfile, "Footpod\"");
+> if (cad != 255) {
+> fprintf(tcxfile, ">\n");
+> fprintf(tcxfile, " <RunCadence>%d</RunCadence>\n", cad);
+> fprintf(tcxfile, " </TPX>\n");
+> } else {
+> fprintf(tcxfile, "/>\n");
+> }
+553,556c564
+< } else {
+< // maybe if we recieve utopic position and distance this tells pause in the run (stop and go)
+< if (track_pause == 0) track_pause = 1;
+< else track_pause = 0;
+---
+> track_pause = 0;
+558a567,573
+> // maybe if we recieve utopic position and distance this tells pause in the run (stop and go) if not begin or end of lap
+> if (dist > (float)40000000 && track_pause == 0) {
+> track_pause = 1;
+> if (dbg) printf("track pause (stop and go)\n");
+> } else {
+> track_pause = 0;
+> }