]> err.no Git - gant/commitdiff
Adding Chris Turchin's Python Monitor
authorJordan Miller <jordan@luckyone.(none)>
Sat, 12 Dec 2009 14:33:43 +0000 (08:33 -0600)
committerJordan Miller <jordan@luckyone.(none)>
Sat, 12 Dec 2009 14:33:43 +0000 (08:33 -0600)
13 files changed:
GantMonitor.glade [new file with mode: 0644]
GantMonitor.py [new file with mode: 0755]
Makefile~ [new file with mode: 0644]
antlib.c~ [new file with mode: 0644]
antlib.o [new file with mode: 0644]
auth405 [new file with mode: 0644]
gant [new file with mode: 0755]
gant.c.orig [new file with mode: 0644]
gant.c~ [new file with mode: 0644]
gant.o [new file with mode: 0644]
output [new file with mode: 0644]
patch_20090124.diff [new file with mode: 0644]
resources/gant.png [new file with mode: 0644]

diff --git a/GantMonitor.glade b/GantMonitor.glade
new file mode 100644 (file)
index 0000000..73db308
--- /dev/null
@@ -0,0 +1,244 @@
+<?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>
diff --git a/GantMonitor.py b/GantMonitor.py
new file mode 100755 (executable)
index 0000000..5af7ea4
--- /dev/null
@@ -0,0 +1,167 @@
+#!/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()
diff --git a/Makefile~ b/Makefile~
new file mode 100644 (file)
index 0000000..568db2c
--- /dev/null
+++ b/Makefile~
@@ -0,0 +1,13 @@
+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
diff --git a/antlib.c~ b/antlib.c~
new file mode 100644 (file)
index 0000000..93d3213
--- /dev/null
+++ b/antlib.c~
@@ -0,0 +1,620 @@
+// 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:
diff --git a/antlib.o b/antlib.o
new file mode 100644 (file)
index 0000000..a8ab4b0
Binary files /dev/null and b/antlib.o differ
diff --git a/auth405 b/auth405
new file mode 100644 (file)
index 0000000..8fce417
Binary files /dev/null and b/auth405 differ
diff --git a/gant b/gant
new file mode 100755 (executable)
index 0000000..aa7e842
Binary files /dev/null and b/gant differ
diff --git a/gant.c.orig b/gant.c.orig
new file mode 100644 (file)
index 0000000..89a87c7
--- /dev/null
@@ -0,0 +1,1182 @@
+// 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);
+}
diff --git a/gant.c~ b/gant.c~
new file mode 100644 (file)
index 0000000..5c732ae
--- /dev/null
+++ b/gant.c~
@@ -0,0 +1,1182 @@
+// 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);
+}
diff --git a/gant.o b/gant.o
new file mode 100644 (file)
index 0000000..5b4f6cd
Binary files /dev/null and b/gant.o differ
diff --git a/output b/output
new file mode 100644 (file)
index 0000000..a258dac
--- /dev/null
+++ b/output
@@ -0,0 +1,408 @@
+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
diff --git a/patch_20090124.diff b/patch_20090124.diff
new file mode 100644 (file)
index 0000000..097ca69
--- /dev/null
@@ -0,0 +1,90 @@
+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;
+>                      }
diff --git a/resources/gant.png b/resources/gant.png
new file mode 100644 (file)
index 0000000..ae014c1
Binary files /dev/null and b/resources/gant.png differ