/* Copyright 2007 Mirko Parthey This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . */ /* TODO: * - check whether negative lat/lon (West/South) are handled correctly, see bin2deg() */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #define GET_BIGENDIAN_32(p) ntohl(*(uint32_t *)(p)) #define GET_BIGENDIAN_16(p) ntohs(*(uint16_t *)(p)) #define GET_BYTE(p) (*(uint8_t *)(p)) #define PUT_BIGENDIAN_32(p, val) *(uint32_t *)(p) = htonl(val) #define PUT_BIGENDIAN_16(p, val) *(uint16_t *)(p) = htons(val) #define PUT_BYTE(p, val) *(uint8_t *)(p) = (val) enum dg100_command_id { dg100cmd_getconfig = 0xB7, dg100cmd_setconfig = 0xB8, dg100cmd_getfileheader = 0xBB, dg100cmd_getfile = 0xB5, dg100cmd_erase = 0xBA, dg100cmd_getid = 0xBF, dg100cmd_setid = 0xC0, dg100cmd_gpsmouse = 0xBC }; struct dg100_command { int sendsize; int recvsize; int trailing_bytes; bool valid; }; struct dg100_command dg100_commands[256] = { [dg100cmd_getconfig] = { 0, 44+2, 2, true }, [dg100cmd_setconfig] = { 41, 4+2, 2, true }, /* getfileheader answer has variable length, -1 is a dummy value */ [dg100cmd_getfileheader] = { 2, -1 , 2, true }, [dg100cmd_getfile] = { 2, 1024+2, 2, true }, [dg100cmd_erase] = { 2, 4+2, 2, true }, [dg100cmd_getid] = { 0, 8+2, 2, true }, [dg100cmd_setid] = { 8, 4+2, 2, true }, [dg100cmd_gpsmouse] = { 1, 0 , 0, true } }; struct loginterval { enum { li_time = 0, li_distance = 1 } flag; uint32_t time; uint32_t dist; }; enum logmode { mode_a, mode_b, mode_c }; const char *logmode_name[3] = { "A", "B", "C" }; struct dg100config { uint8_t infotype; bool spd_thresh_flag; uint32_t spd_thresh; uint8_t dist_thresh_flag; uint32_t dist_thresh; struct loginterval loginterval[3]; /* indexed by enum logmode */ uint8_t memfree; }; /* maximum frame size observed so far: 1817 * (this was get_fileheaders() returning 150 entries) * get_fileheaders() is the only answer type of variable length */ #define FRAME_MAXLEN 4096 /* maximum number of headers observed so far: 672 */ #define HEADERS_MAX 65536 /* global variables */ struct termios old_tio, new_tio; int fd; /* helper functions */ void port_close() { tcsetattr(fd, TCSAFLUSH, &old_tio); close(fd); } void port_open(char port_name[]) { char s[256]; fd = open(port_name, O_RDWR | O_NOCTTY); if (fd < 0) { snprintf(s, 256, "open %s failed", port_name); perror(s); exit(1); } if (tcgetattr(fd, &old_tio) < 0) { snprintf(s, 256, "tcgetattr %s failed", port_name); perror(s); exit(1); } //atexit(port_close); new_tio = old_tio; cfmakeraw(&new_tio); /* this includes setting 8 data bits, no parity */ new_tio.c_cflag &= ~(CSTOPB); /* set 1 stop bit (not handled by cfmakeraw) */ cfsetispeed(&new_tio, B115200); cfsetospeed(&new_tio, B115200); if (tcsetattr(fd, TCSAFLUSH, &new_tio) < 0) { snprintf(s, 256, "tcsetattr %s failed", port_name); perror(s); exit(1); } return; } void dg100_printconfig(const struct dg100config *conf, bool isgetconfig) { const char *s; int i; printf("%s device configuration:\n", isgetconfig ? "Current" : "New"); switch (conf->infotype) { case 0: s = "position only"; break; case 1: s = "position, speed and date/time"; break; case 2: s = "position, speed, date/time and altitude"; break; default: s = "??? (unknown logging style)"; break; } printf("Logging %s.\n", s); if (conf->spd_thresh_flag) printf("Don't log when speed is below %d km/h.\n", conf->spd_thresh / 100 ); if (conf->dist_thresh_flag) printf("Don't log position changes of less than %d meters.\n", conf->dist_thresh ); for (i=0; i<3; i++) { s = logmode_name[i]; if (conf->loginterval[i].flag == li_time) printf("Mode %s: every %d seconds.\n", s, conf->loginterval[i].time / 1000); else printf("Mode %s: every %d meters.\n", s, conf->loginterval[i].dist); } if (isgetconfig) printf("Memory used: %d%%\n", 100 - conf->memfree); } void dg100_decodeconfig(struct dg100config *conf, const uint8_t *data) { int i; conf->infotype = GET_BYTE(data + 0); conf->spd_thresh_flag = GET_BYTE(data + 1); conf->spd_thresh = GET_BIGENDIAN_32(data + 2); conf->dist_thresh_flag = GET_BYTE(data + 6); conf->dist_thresh = GET_BIGENDIAN_32(data + 7); for (i = 0; i < 3; i++) { conf->loginterval[i].flag = GET_BYTE(data + 25 + i); conf->loginterval[i].time = GET_BIGENDIAN_32(data + 11 + i * 4); conf->loginterval[i].dist = GET_BIGENDIAN_32(data + 28 + i * 4); } conf->memfree = GET_BYTE(data + 41); } void dg100_encodeconfig(uint8_t *data, const struct dg100config *conf) { int i; PUT_BYTE(data + 0, conf->infotype); PUT_BYTE(data + 1, conf->spd_thresh_flag); PUT_BIGENDIAN_32(data + 2, conf->spd_thresh); PUT_BYTE(data + 6, conf->dist_thresh_flag); PUT_BIGENDIAN_32(data + 7, conf->dist_thresh); for (i = 0; i < 3; i++) { PUT_BYTE(data + 25 + i, conf->loginterval[i].flag); PUT_BIGENDIAN_32(data + 11 + i * 4, conf->loginterval[i].time); PUT_BIGENDIAN_32(data + 28 + i * 4, conf->loginterval[i].dist); } PUT_BYTE(data + 23, 0); PUT_BYTE(data + 24, 0); PUT_BYTE(data + 40, 1); } time_t bintime2utc(uint32_t date, uint32_t time) { struct tm gpstime; char *oldtz; time_t caltime; gpstime.tm_sec = time % 100; time /= 100; gpstime.tm_min = time % 100; time /= 100; gpstime.tm_hour = time + 1; /* * GPS year: 2000+; struct tm year: 1900+ * GPS month: 1-12, struct tm month: 0-11 */ gpstime.tm_year = date % 100 + 100; date /= 100; gpstime.tm_mon = date % 100 - 1; date /= 100; gpstime.tm_mday = date; /* save current time zone */ oldtz = getenv("TZ"), 10; /* assume GPS time is in UTC */ //KEVIN JAAKO: added if statemnt to check if TZ was set. if (oldtz) setenv("TZ", "Japan", 1); caltime = mktime(&gpstime); /* restore saved time zone */ if (oldtz) setenv("TZ", oldtz, 1); else unsetenv("TZ"); return(caltime); } char * my_strftime(time_t ti, bool gpx) { static char timestr[256]; if (!gpx) { strftime(timestr, 256, "%Y-%m-%d %H:%M:%S", localtime(&ti)); } if (gpx) { strftime(timestr, 256, "%Y-%m-%dT%H:%M:%SZ", localtime(&ti)); } return timestr; } float bin2deg(int32_t val) { /* Assume that val prints in decimal digits as "dddmmffff": * ddd: degrees * mm: the integer part of minutes * ffff: the fractional part of minutes (decimal fraction 0.ffff) */ float deg; int deg_int, min_scaled; bool isneg; uint32_t absval; /* avoid division of negative integers, which has platform-dependent results */ isneg = (val < 0); absval = abs(val); deg_int = absval / 1000000; /* extract dd */ min_scaled = absval % 1000000; /* extract mmffff (minutes scaled by 10^4) */ deg = deg_int + (double) min_scaled / (10000 * 60); /* restore the sign */ deg = isneg ? -deg : deg; return(deg); } void print_gpsfile(uint8_t data[], bool gpx) { const uint32_t recordsizes[3] = {8, 20, 32}; uint32_t first_style, recsize; uint32_t lat, lon, utime, udate, style; int i, recnum; char *s; struct logpoint { uint32_t style; float lat; float lon; time_t time; float speed; float altitude; uint32_t dummy; } p; /* the first record of each file is always full-sized; its style field * determines the format of all subsequent records in the file */ first_style = GET_BIGENDIAN_32(data + 28); if (first_style > 2) { fprintf(stderr, "unknown GPS record style %d", first_style); return; } recsize = recordsizes[first_style]; if (!gpx) { printf("%4s:%5s-%4s%9s%10s%21s%7s%8s%3s%6s\n", "rec#", "from", "to", "lat", "lon", "time", "km/h", "alti/m", "?", "style"); } recnum = 0; for (i = 0; i <= 2048 - recsize; i += (i == 0) ? 32 : recsize, recnum++) { /* look at the data around byte 1024..1025 to check whether the * transition between the pasted frames is handled correctly */ if (!gpx) { printf("%4d:%5d-%4d", recnum, i, i + recsize - 1); } /* always use the style from the first record */ p.style = first_style; lat = GET_BIGENDIAN_32(data + i + 0); lon = GET_BIGENDIAN_32(data + i + 4); if (lat == UINT32_MAX && lon == UINT32_MAX) { if (!gpx) { printf(" (ignoring blank record)\n"); } continue; } p.lat = bin2deg(lat); p.lon = bin2deg(lon); if (!gpx) { printf("%9.4f%10.4f", p.lat, p.lon); } if (first_style >= 1) { utime = GET_BIGENDIAN_32(data + i + 8); udate = GET_BIGENDIAN_32(data + i + 12); p.speed = GET_BIGENDIAN_32(data + i + 16) / 100.0; p.time = bintime2utc(udate, utime); s = my_strftime(p.time, gpx); if (!gpx) { printf("%21s", s); /* time */ } if (!gpx) { printf("%7.1f", p.speed); } } if (first_style >= 2) { p.altitude = GET_BIGENDIAN_32(data + i + 20) / 10000.0; p.dummy = GET_BIGENDIAN_32(data + i + 24); style = GET_BIGENDIAN_32(data + i + 28); if (!gpx) { printf("%8.1f%3d%6d", p.altitude, p.dummy, style); } } if (!gpx) { printf("\n"); } if (gpx) { printf("\t\n", p.lat, p.lon); if (first_style >= 1) { printf("\t\t\n", s); printf("\t\t%.1f\n", p.speed); } if (first_style >= 2) { printf("\t\t%.1f\n", p.altitude); } printf("\t\n"); } } } char * hexdump(const uint8_t id[], unsigned count) { int i; static char s[256]; bool too_long; if (count == 0) return(""); too_long = (count > 64); if (too_long) count = 64; sprintf(s, "%02x", id[0]); for (i = 1; i < count; i++) { sprintf(s + strlen(s), " %02x", id[i]); } if (too_long) sprintf(s + strlen(s), " [...]"); return(s); } uint16_t dg100_checksum(uint8_t buf[], int count) { uint16_t sum = 0; int i; for (i = 0; i < count; i++) { sum += buf[i]; } sum &= (1<<15) - 1; return(sum); } /* communication functions */ size_t dg100_send(uint8_t cmd, const void *payload, size_t count) { uint8_t frame[FRAME_MAXLEN]; uint16_t checksum, payload_len; size_t framelen, param_len, n; param_len = count; payload_len = 1 + count; /* Frame length calculation: * frame start sequence(2), payload length field(2), command id(1), * param(variable length), * checksum(2), frame end sequence(2) */ framelen = 2 + 2 + 1 + count + 2 + 2; assert(framelen <= FRAME_MAXLEN); /* create frame head + command */ PUT_BIGENDIAN_16(frame + 0, 0xA0A2); PUT_BIGENDIAN_16(frame + 2, payload_len); PUT_BYTE(frame + 4, cmd); /* copy payload */ memcpy(frame + 5, payload, count); /* create frame tail */ checksum = dg100_checksum(frame + 4, framelen - 8); PUT_BIGENDIAN_16(frame + framelen - 4, checksum); PUT_BIGENDIAN_16(frame + framelen - 2, 0xB0B3); n = write(fd, frame, framelen); if (n < 0) { perror("dg_100_send: write failed"); exit(1); } return (n); } uint8_t dg100_recv_byte() { uint8_t c; ssize_t n; n = read(fd, &c, 1); if (n < 0) { perror("dg100_recv_byte() reading one byte"); exit(1); } if (n == 0) { fprintf(stderr, "dg100_recv_byte(): read returned zero bytes (EOF)\n"); exit(1); } return c; } /* payload points into a static buffer (which also contains the framing * around the data), so the caller must copy the data before calling * dg100_recv_frame() again */ ssize_t dg100_recv_frame(uint8_t *answer_id, uint8_t **payload) { static uint8_t buf[FRAME_MAXLEN]; uint16_t frame_start_seq, payload_len_field; uint16_t payload_end_seq, payload_checksum, frame_end_seq; uint16_t frame_head, sum, numheaders; uint8_t c, cmd; int i, param_len, frame_len; /* consume input until frame head sequence 0xA0A2 was received */ frame_head = 0; do { c = dg100_recv_byte(); frame_head <<= 8; frame_head |= c; } while (frame_head != 0xA0A2); PUT_BIGENDIAN_16(buf + 0, frame_head); /* To read the remaining data, we need to know how long the frame is. * * The obvious source of this information would be the payload length * field, but the spec says that this field should be ignored in answers. * Indeed, its value differs from the actual payload length. * * We could scan for the frame end sequences, * but there is no guarantee that they do not appear within valid data. * * This means we can only calculate the length using information from * the beginning of the frame, other than the payload length. * * The solution implemented here is to derive the frame length from the * Command ID field, which is more of an answer ID. This is possible * since for each answer ID, the frame length is either constant or it * can be derived from the first two bytes of payload data. */ /* read Payload Length, Command ID, and two further bytes */ for (i = 2; i < 7; i++) { buf[i] = dg100_recv_byte(); } payload_len_field = GET_BIGENDIAN_16(buf + 2); cmd = GET_BYTE(buf + 4); /* * getconfig/setconfig have the same answer ID - * this seems to be a firmware bug we must work around. * Distinguish them by the (otherwise ignored) Payload Len field, * which was observed as 53 for getconfig and 5 for setconfig. */ if (cmd == dg100cmd_getconfig && payload_len_field <= 20) { cmd = dg100cmd_setconfig; } if (!dg100_commands[cmd].valid) { /* TODO: consume data until frame end signature, * then report failure to the caller */ fprintf(stderr, "unknown answer ID %d\n", cmd); exit(1); } param_len = dg100_commands[cmd].recvsize; /* * the getfileheader answer has a varying param_len, * we need to calculate it */ if (cmd == dg100cmd_getfileheader) { numheaders = GET_BIGENDIAN_16(buf + 5); param_len = 2 + 2 + 12 * numheaders + 2; } /* Frame length calculation: * frame start sequence(2), payload length field(2), command id(1), * param(variable length), * payload end seqence(2), checksum(2), frame end sequence(2) */ frame_len = 2 + 2 + 1 + param_len + 2 + 2 + 2; if (frame_len > FRAME_MAXLEN) { fprintf(stderr, "frame too large (frame_len=%d, FRAME_MAXLEN=%d)\n", frame_len, FRAME_MAXLEN); exit(1); } /* TODO: We could try to read the rest of the frame at once; */ /* must set tcsetattr(c_cc[VMIN] = ????) for this to work */ for (i = 7; i < frame_len; i++) { buf[i] = dg100_recv_byte(); } frame_start_seq = GET_BIGENDIAN_16(buf + 0); payload_len_field = GET_BIGENDIAN_16(buf + 2); payload_end_seq = GET_BIGENDIAN_16(buf + frame_len - 6); payload_checksum = GET_BIGENDIAN_16(buf + frame_len - 4); frame_end_seq = GET_BIGENDIAN_16(buf + frame_len - 2); /* TODO: check signatures and checksums, * flush input on failure or scan for end sequence */ #ifdef DEBUG /* calculate checksum */ sum = dg100_checksum(buf + 4, frame_len - 8); printf("frame_start_seq = %04x (%s)\n", frame_start_seq, frame_start_seq == 0xA0A2 ? "ok" : "FAILED"); printf("payload_len_field= %d\n", payload_len_field); printf("payload_end_seq = %04x (%s)\n", payload_end_seq, payload_end_seq == 0x0D00 ? "ok" : "FAILED"); printf("payload_checksum = %04x (%s)\n", payload_checksum, payload_checksum == sum ? "ok" : "FAILED"); printf("frame_end_seq = %04x (%s)\n", frame_end_seq, frame_end_seq == 0xB0B3 ? "ok" : "FAILED"); printf(" calculated checksum=%04x\n", sum); printf(" calculated parameter length=%d\n", param_len); printf(" calculated frame length=%d\n", frame_len); #endif #ifdef DEBUG2 /* print frame contents */ for (i = 0; i < frame_len; i++) { printf("[%4d]=%02x ", i, buf[i]); } printf("\n"); printf("------------------------------------------\n"); #endif *answer_id = cmd; *payload = buf + 5; return(param_len); } /* return value: number of bytes copied into buf, -1 on error */ ssize_t dg100_recv(uint8_t expected_id, void *buf, unsigned len) { int n; uint8_t id, *data; int copysize, trailing_bytes; n = dg100_recv_frame(&id, &data); /* check whether the received frame matches the expected answer type */ if (id != expected_id) { fprintf(stderr, "ERROR: answer type %02x, expecting %02x", id, expected_id); return -1; } trailing_bytes = dg100_commands[id].trailing_bytes; copysize = n - trailing_bytes; #ifdef DEBUG if (trailing_bytes) { printf("trailing bytes in answer: (%s)\n" , hexdump(data + copysize, trailing_bytes)); } #endif /* check for buffer overflow */ if (len < copysize) { fprintf(stderr, "ERROR: buffer too small, size=%d, need=%d", len, copysize); return -1; } memcpy(buf, data, copysize); return(copysize); } /* the number of bytes to be sent is determined by cmd, * count is the size of recvbuf */ ssize_t dg100_request(uint8_t cmd, const void *sendbuf, void *recvbuf, size_t count) { ssize_t sendsize; int n, i, frames, fill; uint8_t *buf; sendsize = dg100_commands[cmd].sendsize; dg100_send(cmd, sendbuf, sendsize); /* the number of frames the answer will comprise */ frames = (cmd == dg100cmd_getfile) ? 2 : 1; buf = recvbuf; fill = 0; for (i = 0; i < frames; i++) { n = dg100_recv(cmd, buf + fill, count - fill); if (n < 0) return(-1); fill += n; } return(fill); } /* public functions */ ssize_t dg100_getconfig(struct dg100config *conf) { uint8_t answer[44]; printf("### dg100_getconfig()\n"); dg100_request(dg100cmd_getconfig, NULL, answer, sizeof(answer)); dg100_decodeconfig(conf, answer); dg100_printconfig(conf, true); return(0); } ssize_t dg100_setconfig(const struct dg100config *conf) { uint8_t request[41], answer[4]; printf("### dg100_setconfig()\n"); dg100_printconfig(conf, false); dg100_encodeconfig(request, conf); dg100_request(dg100cmd_setconfig, request, answer, sizeof(answer)); if (GET_BIGENDIAN_32(answer) != 1) { fprintf(stderr, "dg100_setconfig() FAILED\n"); return(-1); } return(0); } /* mouse commands from http://www.qbik.ch/usb/devices/showdev.php?id=4051 * FIXME: enabling the GPS mouse mode does not always work; * need to get a USB trace from the Globalsat software on Windows */ void dg100_mouse(bool on) { uint8_t request[1]; request[0] = on ? 1 : 0; printf("### dg100_mouse(%02x)\n", request[0]); dg100_request(dg100cmd_gpsmouse, request, NULL, 0); } int dg100_getfileheaders(uint16_t headers[], bool gpx) { uint8_t request[2]; uint8_t answer[FRAME_MAXLEN]; uint32_t time, date, seqnum; time_t ti; uint16_t numheaders, nextheader; int i, h, headers_total; if (!gpx) { printf("### dg100_getfileheaders()\n"); } nextheader = 0; headers_total = 0; do { if (!gpx) { printf("nextheader=%d\n", nextheader); } /* request the next batch of headers */ PUT_BIGENDIAN_16(request, nextheader); dg100_request(dg100cmd_getfileheader, request, answer, sizeof(answer)); /* process the answer */ numheaders = GET_BIGENDIAN_16(answer); nextheader = GET_BIGENDIAN_16(answer + 2); if (!gpx) { printf("numheaders=%d\n", numheaders); } for (i = 0; i < numheaders; i++) { h = 4 + i * 12; time = GET_BIGENDIAN_32(answer + h); date = GET_BIGENDIAN_32(answer + h + 4); seqnum = GET_BIGENDIAN_32(answer + h + 8); ti = bintime2utc(date, time); if (!gpx) { printf("%3d: %s\n", seqnum, my_strftime(ti, gpx)); } headers[headers_total++] = seqnum; } } while (numheaders != 0); return(headers_total); } void dg100_getfile(uint16_t num, bool gpx) { uint8_t request[2]; uint8_t answer[2048]; if (!gpx) { printf("### dg100_getfile(%d)\n", num); } PUT_BIGENDIAN_16(request, num); dg100_request(dg100cmd_getfile, request, answer, sizeof(answer)); print_gpsfile(answer, gpx); } void dg100_getallfiles(bool gpx) { int i, filenum, numheaders; uint16_t headers[HEADERS_MAX]; numheaders = dg100_getfileheaders(headers, gpx); if (!gpx) { printf("### dg100_getfiles(), numheaders=%d\n", numheaders); } if (gpx) { printf("\n"); printf("\n"); printf("\t\n\t\tGPS Data\n\n"); } for (i = 0; i < numheaders; i++) { filenum = headers[i]; dg100_getfile(filenum, gpx); } if (gpx) { printf("\n\n\n\n"); } } //KJ ADDED THESE //FUNCTIONS to wrap getallfiles to switch between regular & GPX formatted data void dg100_getfiles() { dg100_getallfiles(0); } void dg100_printgpx() { dg100_getallfiles(1); } void dg100_getid() { uint8_t answer[8]; printf("### dg100_getid\n"); dg100_request(dg100cmd_getid, NULL, answer, sizeof(answer)); printf("id=(%s)\n", hexdump(answer, 8)); } int dg100_setid(const uint8_t id[8]) { uint8_t answer[4]; printf("### dg100_setid(%s)\n", hexdump(id, 8)); dg100_request(dg100cmd_setid, id, answer, sizeof(answer)); if (GET_BIGENDIAN_32(answer) != 1) { fprintf(stderr, "dg100_setid() FAILED\n"); return(-1); } return(0); } int dg100_erase() { uint8_t request[2] = { 0xFF, 0xFF }; uint8_t answer[4]; printf("### dg100_erase()\n"); dg100_request(dg100cmd_erase, request, answer, sizeof(answer)); if (GET_BIGENDIAN_32(answer) != 1) { fprintf(stderr, "dg100_erase() FAILED\n"); return(-1); } return(0); } int main() { struct dg100config conf; const struct dg100config desiredconf = { .infotype = 2, .spd_thresh_flag = 0, .spd_thresh = 0 * 100, .dist_thresh_flag = 0, .dist_thresh = 0 * 1, .loginterval = { [mode_a] = { .flag = li_time, .time = 60 * 1000 }, [mode_b] = { .flag = li_time, .time = 5 * 1000 }, [mode_c] = { .flag = li_time, .time = 1 * 1000 }, }, .memfree = 0 }; const struct dg100config testconf = { .infotype = 2, .spd_thresh_flag = 1, .spd_thresh = 5 * 100, .dist_thresh_flag = 1, .dist_thresh = 5 * 1, .loginterval = { [mode_a] = { .flag = li_time, .time = 30 * 1000 }, [mode_b] = { .flag = li_time, .time = 3 * 1000 }, [mode_c] = { .flag = li_time, .time = 1 * 1000 }, }, .memfree = 0 }; const uint8_t testid[8] = { 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, 0x38 }; //CHANGED PORT to match prolific's serial device name //NOTE: prolific gives a different device name depending on which USB port you use! port_open("/dev/cu.PL2303-0000103D"); //port_open("/dev/ttyUSB01"); memset(&conf, 0, sizeof(conf)); //dg100_getconfig(&conf); //dg100_setconfig(&testconf); //dg100_getconfig(&conf); //dg100_setconfig(&desiredconf); //dg100_getconfig(&conf); //dg100_mouse(false); //dg100_getid(); //dg100_setid(testid); //dg100_getid(); //dg100_getfiles(); //KJ added this function to output GPX data... dg100_printgpx(); //dg100_erase(); //dg100_getconfig(&conf); port_close(); return 0; }