From: Jordan Miller Date: Sat, 12 Dec 2009 14:33:43 +0000 (-0600) Subject: Adding Chris Turchin's Python Monitor X-Git-Url: https://err.no/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=d33ae35ee8d782cbad8a3404784ce1c5ceabfdff;p=gant Adding Chris Turchin's Python Monitor --- diff --git a/GantMonitor.glade b/GantMonitor.glade new file mode 100644 index 0000000..73db308 --- /dev/null +++ b/GantMonitor.glade @@ -0,0 +1,244 @@ + + + + + + Gant Settings + 318 + 260 + menu + + + True + + + True + 3 + 2 + + + True + 0 + label22 + + + + + True + 0 + label23 + + + 1 + 2 + + + + + True + 0 + label24 + + + 2 + 3 + + + + + True + + + 1 + 2 + + + + + True + + + 1 + 2 + 1 + 2 + + + + + True + + + 1 + 2 + 2 + 3 + + + + + 2 + + + + + True + end + + + gtk-ok + 1 + True + False + False + True + top + + + False + False + 0 + + + + + gtk-cancel + True + False + False + True + + + False + False + 1 + + + + + False + end + 0 + + + + + + + 5 + Gant Monitor: Downloading data... + center + 415 + 140 + True + resources/gant.png + normal + + + True + vertical + 2 + + + True + vertical + + + True + Gant status: + start + True + + + False + 0 + + + + + True + + + False + 1 + + + + + True + True + + + + + + + True + Details + + + label_item + + + + + False + 2 + + + + + 1 + + + + + True + end + + + Restart + 4 + True + True + True + + + + False + False + 0 + + + + + gtk-stop + True + True + True + True + right + + + + False + False + 1 + + + + + False + end + 0 + + + + + + + + + + + diff --git a/GantMonitor.py b/GantMonitor.py new file mode 100755 index 0000000..5af7ea4 --- /dev/null +++ b/GantMonitor.py @@ -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 = ''' + + + + + + + + + + + + ''' + + 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('_Download...') + 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 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 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 +#include +#include +#include +#include +#include +#include +#include +#include + +#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 index 0000000..a8ab4b0 Binary files /dev/null and b/antlib.o differ diff --git a/auth405 b/auth405 new file mode 100644 index 0000000..8fce417 Binary files /dev/null and b/auth405 differ diff --git a/gant b/gant new file mode 100755 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 index 0000000..89a87c7 --- /dev/null +++ b/gant.c.orig @@ -0,0 +1,1182 @@ +// copyright 2008-2009 paul@ant.sbrk.co.uk. released under GPLv3 +// copyright 2009-2009 Wali +// vers 0.6t +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#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, "\n"); + fprintf(tcxfile, "\n\n"); + fprintf(tcxfile, " \n"); + return; +} + +void +print_tcx_footer(FILE *tcxfile) +{ + fprintf(tcxfile, " \n"); + fprintf(tcxfile, " \n"); + fprintf(tcxfile, " \n"); + fprintf(tcxfile, " %s\n", modelname); + fprintf(tcxfile, " %u\n", unitid); + fprintf(tcxfile, " %u\n", part); + fprintf(tcxfile, " \n"); + fprintf(tcxfile, " %u\n", ver/100); + fprintf(tcxfile, " %u\n", ver - ver/100*100); + fprintf(tcxfile, " 0\n"); + fprintf(tcxfile, " 0\n"); + fprintf(tcxfile, " \n"); + fprintf(tcxfile, " \n"); + fprintf(tcxfile, " \n"); + fprintf(tcxfile, " \n\n"); + fprintf(tcxfile, " \n"); + fprintf(tcxfile, " Garmin ANT for Linux\n"); + fprintf(tcxfile, " \n"); + fprintf(tcxfile, " \n"); + fprintf(tcxfile, " %u\n", majorrelease); + fprintf(tcxfile, " %u\n", minorrelease); + fprintf(tcxfile, " 0\n"); + fprintf(tcxfile, " 0\n"); + fprintf(tcxfile, " \n"); + fprintf(tcxfile, " Release\n"); + fprintf(tcxfile, " \n", releasetime); + fprintf(tcxfile, " make\n"); + fprintf(tcxfile, " \n"); + fprintf(tcxfile, " EN\n"); + fprintf(tcxfile, " 006-A0XXX-00\n"); + fprintf(tcxfile, " \n\n"); + fprintf(tcxfile, "\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, " \n"); + fprintf(tcxfile, " %s\n", tbuf); + } else { + fprintf(tcxfile, " \n"); + fprintf(tcxfile, " \n"); + } + fprintf(tcxfile, " \n", tbuf); + fprintf(tcxfile, " %s\n", ground(tsec/100)); + fprintf(tcxfile, " %s\n", ground(dist)); + fprintf(tcxfile, " %s\n", ground(max_speed)); + fprintf(tcxfile, " %d\n", cal); + if (hr_av > 0) { + fprintf(tcxfile, " \n"); + fprintf(tcxfile, " %d\n", hr_av); + fprintf(tcxfile, " \n"); + } + if (hr_max > 0) { + fprintf(tcxfile, " \n"); + fprintf(tcxfile, " %d\n", hr_max); + fprintf(tcxfile, " \n"); + } + fprintf(tcxfile, " "); + 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, "\n"); + if (cad != 255) { + if (sporttyp_track[track_id-firsttrack_id] == 0) { + fprintf(tcxfile, " %d\n", cad); + } else { + fprintf(tcxfile, " %d\n", cad); + } + } + fprintf(tcxfile, " "); + 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, "\n"); + fprintf(tcxfile, " \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, " \n"); + fprintf(tcxfile, " \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, " \n"); + fprintf(tcxfile, " \n",tbuf); + if (lat < 90) { + fprintf(tcxfile, " \n"); + fprintf(tcxfile, " %s\n", + ground(lat)); + fprintf(tcxfile, " %s\n", + ground(lon)); + fprintf(tcxfile, " \n"); + fprintf(tcxfile, " %s\n", ground(alt)); + } + // last trackpoint has utopic distance, 40000km should be enough, hack? + if (dist < (float)40000000) { + fprintf(tcxfile, " %s\n", ground(dist)); + } + if (hr > 0) { + fprintf(tcxfile, " \n"); + fprintf(tcxfile, " %d\n", hr); + fprintf(tcxfile, " \n"); + } + if (u1 > 0 && cad != 255) { + fprintf(tcxfile, " %d\n", cad); + } + if (dist < (float)40000000) { + fprintf(tcxfile, " %s\n", u1 ? "Present" : "Absent"); + fprintf(tcxfile, " \n"); + fprintf(tcxfile, " \n"); + fprintf(tcxfile, " %d\n", cad); + fprintf(tcxfile, " \n"); + } else { + fprintf(tcxfile, "/>\n"); + } + fprintf(tcxfile, " \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, " \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 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 +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#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, "\n"); + fprintf(tcxfile, "\n\n"); + fprintf(tcxfile, " \n"); + return; +} + +void +print_tcx_footer(FILE *tcxfile) +{ + fprintf(tcxfile, " \n"); + fprintf(tcxfile, " \n"); + fprintf(tcxfile, " \n"); + fprintf(tcxfile, " %s\n", modelname); + fprintf(tcxfile, " %u\n", unitid); + fprintf(tcxfile, " %u\n", part); + fprintf(tcxfile, " \n"); + fprintf(tcxfile, " %u\n", ver/100); + fprintf(tcxfile, " %u\n", ver - ver/100*100); + fprintf(tcxfile, " 0\n"); + fprintf(tcxfile, " 0\n"); + fprintf(tcxfile, " \n"); + fprintf(tcxfile, " \n"); + fprintf(tcxfile, " \n"); + fprintf(tcxfile, " \n\n"); + fprintf(tcxfile, " \n"); + fprintf(tcxfile, " Garmin ANT for Linux\n"); + fprintf(tcxfile, " \n"); + fprintf(tcxfile, " \n"); + fprintf(tcxfile, " %u\n", majorrelease); + fprintf(tcxfile, " %u\n", minorrelease); + fprintf(tcxfile, " 0\n"); + fprintf(tcxfile, " 0\n"); + fprintf(tcxfile, " \n"); + fprintf(tcxfile, " Release\n"); + fprintf(tcxfile, " \n", releasetime); + fprintf(tcxfile, " make\n"); + fprintf(tcxfile, " \n"); + fprintf(tcxfile, " EN\n"); + fprintf(tcxfile, " 006-A0XXX-00\n"); + fprintf(tcxfile, " \n\n"); + fprintf(tcxfile, "\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, " \n"); + fprintf(tcxfile, " %s\n", tbuf); + } else { + fprintf(tcxfile, " \n"); + fprintf(tcxfile, " \n"); + } + fprintf(tcxfile, " \n", tbuf); + fprintf(tcxfile, " %s\n", ground(tsec/100)); + fprintf(tcxfile, " %s\n", ground(dist)); + fprintf(tcxfile, " %s\n", ground(max_speed)); + fprintf(tcxfile, " %d\n", cal); + if (hr_av > 0) { + fprintf(tcxfile, " \n"); + fprintf(tcxfile, " %d\n", hr_av); + fprintf(tcxfile, " \n"); + } + if (hr_max > 0) { + fprintf(tcxfile, " \n"); + fprintf(tcxfile, " %d\n", hr_max); + fprintf(tcxfile, " \n"); + } + fprintf(tcxfile, " "); + 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, "\n"); + if (cad != 255) { + if (sporttyp_track[track_id-firsttrack_id] == 0) { + fprintf(tcxfile, " %d\n", cad); + } else { + fprintf(tcxfile, " %d\n", cad); + } + } + fprintf(tcxfile, " "); + 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, "\n"); + fprintf(tcxfile, " \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, " \n"); + fprintf(tcxfile, " \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, " \n"); + fprintf(tcxfile, " \n",tbuf); + if (lat < 90) { + fprintf(tcxfile, " \n"); + fprintf(tcxfile, " %s\n", + ground(lat)); + fprintf(tcxfile, " %s\n", + ground(lon)); + fprintf(tcxfile, " \n"); + fprintf(tcxfile, " %s\n", ground(alt)); + } + // last trackpoint has utopic distance, 40000km should be enough, hack? + if (dist < (float)40000000) { + fprintf(tcxfile, " %s\n", ground(dist)); + } + if (hr > 0) { + fprintf(tcxfile, " \n"); + fprintf(tcxfile, " %d\n", hr); + fprintf(tcxfile, " \n"); + } + if (u1 > 0 && cad != 255) { + fprintf(tcxfile, " %d\n", cad); + } + if (dist < (float)40000000) { + fprintf(tcxfile, " %s\n", u1 ? "Present" : "Absent"); + fprintf(tcxfile, " \n"); + fprintf(tcxfile, " \n"); + fprintf(tcxfile, " %d\n", cad); + fprintf(tcxfile, " \n"); + } else { + fprintf(tcxfile, "/>\n"); + } + fprintf(tcxfile, " \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, " \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 index 0000000..5b4f6cd Binary files /dev/null and b/gant.o differ diff --git a/output b/output new file mode 100644 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 index 0000000..097ca69 --- /dev/null +++ b/patch_20090124.diff @@ -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, " %d\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, " \n"); +> fprintf(tcxfile, " \n"); +> fprintf(tcxfile, " %d\n", cad); +> fprintf(tcxfile, " \n"); +> fprintf(tcxfile, " \n"); +> } +> } +483d491 +< track_pause = 0; +490,493d497 +< } // end of if (tv >= tv_lap && lap <= lastlap) +< if (track_pause) { +< fprintf(tcxfile, " \n"); +< fprintf(tcxfile, " \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, " \n"); +> fprintf(tcxfile, " \n"); +> } +532,533c540,544 +< if (u1 > 0 && cad != 255) { +< fprintf(tcxfile, " %d\n", cad); +--- +> // for bikes the cadence is written here and for the footpod in , why garmin? +> if (sporttyp_track[track_id-firsttrack_id] == 1) { +> if (cad != 255) { +> fprintf(tcxfile, " %d\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, " %d\n", cad); +< fprintf(tcxfile, " \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, " %d\n", cad); +> fprintf(tcxfile, " \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 index 0000000..ae014c1 Binary files /dev/null and b/resources/gant.png differ