commit 58069c77d550d907d9b60db531aed1bd28aa87b7 Author: Florian Stecker Date: Wed Jan 25 20:09:56 2023 -0500 initial commit diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..312eb9f --- /dev/null +++ b/.gitignore @@ -0,0 +1,2 @@ +*.o +iqconn diff --git a/Makefile b/Makefile new file mode 100644 index 0000000..93a0037 --- /dev/null +++ b/Makefile @@ -0,0 +1,13 @@ +all: iqconn + +iqconn: main.o iqbasic.o + gcc -g -o iqconn main.o iqbasic.o + +main.o: main.c + gcc -g -c -std=gnu99 main.c + +iqbasic.o: iqbasic.c + gcc -g -c -std=gnu99 iqbasic.c + +clean: + rm -f iqconn main.o iqbasic.o diff --git a/iqbasic.c b/iqbasic.c new file mode 100644 index 0000000..4e2a0c4 --- /dev/null +++ b/iqbasic.c @@ -0,0 +1,75 @@ +#include +#include +#include +#include +#include +#include +#include +#include + +#include "iqbasic.h" + +#define DIE(...) error_at_line(1, errno, __FILE__, __LINE__, __VA_ARGS__) + +void vario_send(int fd, const char *line) +{ + int len = strlen(line); + char *buf = (char*)malloc(sizeof(char)*len+3); + int rc; + sprintf(buf, "%s\r\n", line); + do { + rc = write(fd, buf, len+2); + } while (rc == -1 && errno == EINTR); + if (rc == -1) + DIE("write"); +} + +int vario_recv(int fd, char *buf, int maxlen) +{ + int pos = 0; + int n; + char c; + + do { + do { + n = read(fd, &c, 1); + } while(n == -1 && errno == EINTR); + + if(n == -1) + DIE("read"); + if(n == 1) + buf[pos++] = c; + + } while(c != '\n' && pos < maxlen); + + if(pos >= maxlen) + DIE("buffer too small"); + + return pos; +} + +int vario_open(char *device) +{ + int fd = open(device, O_NOCTTY | O_NONBLOCK | O_RDWR); + if (fd == -1) + DIE("%s", device); + if (tcflush(fd, TCIOFLUSH) == -1) + error(1, errno, "tcflush %s", device, strerror(errno)); + + struct termios termios; + memset(&termios, 0, sizeof termios); + termios.c_iflag = IGNPAR; + termios.c_cflag = CLOCAL | CREAD | CS8; + cfsetispeed(&termios, B57600); + cfsetospeed(&termios, B57600); + if (tcsetattr(fd, TCSANOW, &termios) == -1) + DIE("tcsetattr: %s: %s", device, strerror(errno)); + + return fd; +} + +void vario_close(int fd) +{ + if (close(fd) == -1) + DIE("close: %s", strerror(errno)); +} diff --git a/iqbasic.h b/iqbasic.h new file mode 100644 index 0000000..731994e --- /dev/null +++ b/iqbasic.h @@ -0,0 +1,9 @@ +#ifndef IQBASIC_H +#define IQBASIC_H + +void vario_send(int fd, const char *line); +int vario_recv(int fd, char *buf, int maxlen); +int vario_open(char *device); +void vario_close(int fd); + +#endif diff --git a/main.c b/main.c new file mode 100644 index 0000000..c87b06a --- /dev/null +++ b/main.c @@ -0,0 +1,372 @@ +#include +#include +#include +#include +#include +#include +#include +#include + +#include "iqbasic.h" +#include "main.h" + +#define DIE(...) error_at_line(1, errno, __FILE__, __LINE__, __VA_ARGS__) + +int parameter_sizes[] = {16, 16, 16, 2, 2, 1, 4, 1, 2, 2, 2, 2, 2, 1, 1, 2, 1, 1, 1, 1, 4, 2, 1, 0, 0, 0, 2, 0, 1, 0, 0, 0, 0, 0, 2}; +char *parameter_names[] = {"Pilot", "Glider Type", "Glider ID", "Units", "Flags", "Record Interval [s]", "Unknown 3", "Digital Vario [s]", "Climb Frequency [Hz]", "Sink Frequency [Hz]", "Climb Threshold [cm/s]", "Sink Threshold [cm/s]", "Sink Alarm [cm/s]", "Frequency Adjustment", "Pitch Adjustment", "Unknown 4", "Unknown 5", "Digital Filter", "Volume", "Time Zone [h]", "Pressure Correction [mPa]", "LT Threshold [cm/s]", "Unknown 6", NULL, NULL, NULL, "Stall Alarm [cm/s]", NULL, "Speed Correction", NULL, NULL, NULL, NULL, NULL, "Pre-Thermal Threshold [cm/s]"}; +int nparameters = sizeof(parameter_sizes)/sizeof(int); + +int display_parameters[] = {0, 1, 2, 5, 7, 17, 21, 10, 34, 11, 12, 26, 18, 8, 9, 13, 14, 19, 20, 28, 3, 4, 6, 15, 16, 22}; +int ndisplay = sizeof(display_parameters)/sizeof(int); + +void read_list(int fd, const char *action) +{ + char buf[1024]; + int len = 0; + int i = 0; + int done = 0; + + vario_send(fd, action); + + do { + len = vario_recv(fd, buf, 1024); + + for(i = 0; i < terminator_count; i++) + if(memcmp(terminator[i], buf, len) == 0) + done = 1; + + if(!done) + fwrite(buf, 1, len, stdout); + } while(!done); +} + +int write_list(int fd, const char *action) +{ + char buf[1024]; + int i = 0; + int len = 0; + + while((buf[i++] = getc(stdin)) != EOF) { + if(buf[i] == '\n') { + buf[i] = 0; + vario_send(fd, action); + vario_send(fd, buf); + len = vario_recv(fd, buf, 1023); + buf[len] = 0; + fprintf(stderr, "Answer: %s\n", buf); + i = 0; + } + } +} + +void read_track(int fd, int trno) +{ + char buf[1024]; + int len = 0; + int i = 0; + int done = 0; + + if(trno < 0 || trno > 99) + return; + + sprintf(buf, "ACT_21_%02d", trno); + vario_send(fd, buf); + + done = 0; + do { + len = vario_recv(fd, buf, 1024); + + if(len >= 1 && buf[0] == 'G') + done = 1; + + fwrite(buf, 1, len, stdout); + } while(!done); +} + +void read_tracklist(int fd) +{ + read_list(fd, "ACT_20_00"); +} + +void read_waypoints(int fd) +{ + read_list(fd, "ACT_31_00"); +} + +void read_routes(int fd) +{ + read_list(fd, "ACT_41_00"); +} + +void write_waypoints(int fd) +{ + vario_send(fd, "ACT_30_00"); + write_list(fd, "ACT_32_00"); +} + +void write_routes(int fd) +{ + vario_send(fd, "ACT_40_00"); + write_list(fd, "ACT_42_00"); +} + +void delete_tracks(int fd) +{ + //vario_send(fd, "ACT_"); +} + +void show_parameters(int fd) +{ + char cmd[10]; + char buf[1024]; + int len; + int param; + + for(int i = 0; i < ndisplay; i++) { + param = display_parameters[i]; + sprintf(cmd, "RFA_%02x", param); + vario_send(fd, cmd); + do + len = vario_recv(fd, buf, 1023); + while(len == 0); + if(len >= 2) { + buf[len-2] = 0; + if(parameter_sizes[param] == 1) { + signed char value; + unsigned int v1; + sscanf(buf+7, "%02X", &v1); + value = v1; + printf("%2d %-30s %d\n", param, parameter_names[param], value); + } else if(parameter_sizes[param] == 2) { + signed short value; + unsigned int v1, v2; + sscanf(buf+7, "%02X%02X", &v1, &v2); + value = v1 | (v2 << 8); + printf("%2d %-30s %d\n", param, parameter_names[param], value); + } else if(parameter_sizes[param] == 4) { + signed int value; + unsigned int v1, v2, v3, v4; + sscanf(buf+7, "%02X%02X%02X%02X", &v1, &v2, &v3, &v4); + value = v1 | (v2 << 8) | (v3 << 16) | (v4 << 24); + printf("%2d %-30s %d\n", param, parameter_names[param], value); + } else if(parameter_sizes[param] > 4) { + char value[1024]; + unsigned int v1; + for(int j = 0; j < parameter_sizes[param]; j++) { + sscanf(buf+7+2*j, "%02X", &v1); + value[j] = v1; + } + value[parameter_sizes[param]] = 0; + printf("%2d %-30s %s\n", param, parameter_names[param], value); + } + else + printf("%2d %-30s %s\n", param, parameter_names[param], buf+7); + } + } +} + +void get_parameter(int fd, int param) +{ + char cmd[10]; + char buf[1024]; + int len; + + sprintf(cmd, "RFA_%02x", param); + vario_send(fd, cmd); + do + len = vario_recv(fd, buf, 1023); + while(len == 0); + if(len >= 2) { + buf[len-2] = 0; + printf("%s\n", buf); + } +} + +void set_parameter(int fd, int param, const char *value) +{ + char cmd[1024]; + char buf[1024]; + int len; + + if(param >= nparameters) { + fprintf(stderr, "Parameter %02x does not exist!\n", param); + return; + } + + if(parameter_sizes[param] == 1) + sprintf(cmd, "WFA_%02x_%02X", param, + (signed char)atoi(value) & 0xff); + else if(parameter_sizes[param] == 2) + sprintf(cmd, "WFA_%02x_%02X%02X", param, + ((signed short)atoi(value) >> 0) & 0xff, + ((signed short)atoi(value) >> 8) & 0xff); + else if(parameter_sizes[param] == 4) + sprintf(cmd, "WFA_%02x_%02X%02X%02X%02X", param, + ((signed int)atoi(value) >> 0) & 0xff, + ((signed int)atoi(value) >> 8) & 0xff, + ((signed int)atoi(value) >> 16) & 0xff, + ((signed int)atoi(value) >> 24) & 0xff); + else if(parameter_sizes[param] > 4) { + len = strlen(value); + sprintf(cmd, "WFA_%02x_", param); + for(int i = 0; i < parameter_sizes[param]; i++) + if(i < len) + sprintf(cmd + 7 + 2*i, "%02X", value[i]); + else + sprintf(cmd + 7 + 2*i, "20"); + } + + fprintf(stderr, "%s\n", cmd); + + vario_send(fd, "ACT_82_00"); + vario_recv(fd, buf, 1023); + vario_send(fd, cmd); + vario_recv(fd, buf, 1023); +} + +int main(int argc, char *argv[]) +{ + int fd, opt; + char mode = 0; + char *device = "/dev/ttyUSB0"; + char *value = ""; + int trno = -1; + int param = -1; + + const char helpstr[] = + "Usage: iqconn [OPTIONS]\n" + "Read and write data from/to Bräuniger IQ Basic GPS Varios.\n" + "\n" + " -w, --read-waypoints output waypoint data to stdout\n" + " -r, --read-routes output route data to stdout\n" + " -l, --list list saved tracklogs\n" + " -d, --download download IGC file to stdout, specify track number as argument\n" + " -W, --write-waypoints upload waypoint data from stdin (does not work yet)\n" + " -R, --write-routes upload route data from stdin (does not work yet)\n" + " -D, --delete delete tracklogs (not yet implemented)\n" + " -p, --read-parameters output all configuration parameters to stdout\n" + " -P, --write-parameter set a configuration parameter, specify number as argument\n" + " -v, --value to be used with -P, specifies the desired value\n" + " -c, --connector specify serial terminal as argument (default: /dev/ttyUSB0)\n" + " -h, --help display this help page\n"; + + struct option long_option[] = { + {"read-waypoints", 0, 0, 'w'}, + {"read-routes", 0, 0, 'r'}, + {"write-waypoints", 0, 0, 'W'}, + {"write-routes", 0, 0, 'R'}, + {"list", 0, 0, 'l'}, + {"download", 1, 0, 'd'}, + {"delete", 0, 0, 'D'}, + {"help", 0, 0, 'h'}, + {"read-parameters", 0, 0, 'p'}, + {"write-parameter", 1, 0, 'P'}, + {"value", 1, 0, 'v'}, + {"connector", 1, 0, 'c'}, + {"test", 0, 0, 't'}, + {0,0,0,0} + }; + + char short_option[] = "wrWRlDd:c:pP:v:ht"; + + while(1) { + if((opt = getopt_long(argc, argv, short_option, long_option, 0)) < 0) + break; + switch(opt) { + case '?': + return 1; + case 'c': + device = optarg; + break; + case 'v': + value = optarg; + break; + case 'd': + trno = atoi(optarg); + if(mode == 0) + mode = opt; + else + mode = 'h'; + break; + case 'P': + param = atoi(optarg); + if(mode == 0) + mode = opt; + else + mode = 'h'; + break; + case 'w': case 'r': case 'W': case 'R': case 'l': case 'D': case 'h': case 't': case 'p': + if(mode == 0) + mode = opt; + else + mode = 'h'; + break; + default: + mode = 'h'; + } + } + + if(mode == 0) + mode = 'h'; + + switch(mode) { + case 'w': + fd = vario_open(device); + read_waypoints(fd); + vario_close(fd); + break; + case 'r': + fd = vario_open(device); + read_routes(fd); + vario_close(fd); + break; + case 'W': + fd = vario_open(device); + write_waypoints(fd); + vario_close(fd); + break; + case 'R': + fd = vario_open(device); + write_routes(fd); + vario_close(fd); + break; + case 'l': + fd = vario_open(device); + read_tracklist(fd); + vario_close(fd); + break; + case 'd': + if(trno == -1) { + fprintf(stderr, "no track number specified!\n"); + return 1; + } + fd = vario_open(device); + read_track(fd, trno); + vario_close(fd); + break; + case 'D': + fd = vario_open(device); + delete_tracks(fd); + vario_close(fd); + break; + case 'h': + fprintf(stderr, helpstr); + break; + case 'p': + fd = vario_open(device); + show_parameters(fd); + vario_close(fd); + break; + case 'P': + fd = vario_open(device); + set_parameter(fd, param, value); + vario_close(fd); + break; + case 't': + break; + } + + + return 0; +} + diff --git a/main.h b/main.h new file mode 100644 index 0000000..72d5854 --- /dev/null +++ b/main.h @@ -0,0 +1,37 @@ +#ifndef MAIN_H +#define MAIN_H + +char *terminator[] = {" Done\r\n", "No Data\r\n", "Syntax Error\r\n", "already exist\r\n"}; +int terminator_count = 4; + +void read_list(int fd, const char *action); +int write_list(int fd, const char *action); +void read_track(int fd, int trno); +void read_tracklist(int fd); +void delete_tracks(int fd); +void read_waypoints(int fd); +void read_routes(int fd); +void write_waypoints(int fd); +void write_routes(int fd); +int main(int argc, char *argv[]); + +// vario_send(fd, "ACT_32_00"); +// vario_send(fd, "Zuhause2 ;N 0'00.000;E 0'00.000; 0; 400"); +// vario_send(fd, "Zuhause2 ;N 0'00.000;E 0'00.000; 0; 400"); + +/* + actions: + * ACT_20_00 --- list tracklogs + * ACT_21_07 --- recieve tracklog #7 + * ACT_30_00 --- delete all waypoints + * ACT_31_00 --- list waypoints + * ACT_32_00 --- send waypoint + * ACT_40_00 --- delete all routes + * ACT_41_00 --- list routes + * ACT_42_00 --- send route + * ACT_82_00 --- Write parameter + * RFA_17 --- Request FA Parameter #17 + * RPA_17 --- Request FA Parameter #17 + */ + +#endif diff --git a/params b/params new file mode 100644 index 0000000..2bfef3e --- /dev/null +++ b/params @@ -0,0 +1,75 @@ +RFA_00_6E6F742D736574202020202020202020 --- Pilot +RFA_01_6E6F742D736574202020202020202020 --- Glider Type +RFA_02_6E6F742D736574202020202020202020 --- Glider ID +RFA_03_0000 --- units +RFA_04_0305 --- flags +RFA_05_01 --- record interval [s] +? RFA_06_3CE7FFFF --- -6340 +RFA_07_05 --- digital vario [s] +RFA_08_2003 --- 800 --- climb frequency [Hz] +RFA_09_5802 --- 600 --- sink frequency [Hz] +RFA_0a_0A00 --- 10 --- climb threshold [cm/s] +RFA_0b_CEFF --- -50 --- sink threshold [cm/s] +RFA_0c_18FC --- -1000 --- sink alarm [cm/s] +RFA_0d_03 --- 3 --- frequency adjustment +RFA_0e_04 --- 4 --- pitch adjustment +? RFA_0f_0000 --- 0 +? RFA_10_05 --- 5 +RFA_11_01 --- 1 --- digital filter (0 to 5 ?) +RFA_12_00 --- 0 --- volume [0-4] +RFA_13_01 --- 1 --- Time zone [h] +RFA_14_3004FEFF --- -130000 --- Pressure correction [mPa] +RFA_15_3200 --- 50 --- LT threshold [cm/s] +? RFA_16_3C --- 60 +RFA_1a_4103 --- 833 --- Stall alarm [cm/s] +RFA_1c_64 --- 100 --- Speed correction +RFA_22_CEFF --- -50 --- Pre-Thermal threshold [cm/s] + +{"Pilot", "Glider Type", "Glider ID", NULL, NULL, "Record Interval [s]", NULL, "Digital Vario [s]", "Climb Frequency [Hz]", "Sink Frequency [Hz]", "Climb Threshold [cm/s]", "Sink Threshold [cm/s]", "Sink Alarm [cm/s]", "Frequency Adjustment", "Pitch Adjustment", NULL, NULL, "Digital Filter", "Volume", "Time Zone [h]", "Pressure Correction [mPa]", "LT Threshold [cm/s]", NULL, NULL, NULL, NULL, "Stall Alarm [cm/s]", "Speed Correction", "Pre-Thermal Threshold [cm/s]"} + +bit = function (off/on) +0001 = +0002 = +0004 = record mode (man/auto) +0008 = Stall alarm (off/on) +0010 = +0020 = Pre-Thermal alarm (off/on) +0040 = +0080 = +0100 = +0200 = pitch mode (linear/exponential) +0400 = Sink alarm (off/on) +0800 = +1000 = ALT display (ALT1/ALT2) +2000 = ALT3 display (ALT3/TIME) +4000 = ALT2 +8000 = ALT2 (00 = GPS, 01 = flight level, 10 = ALT1 m/ft inverse, 11 = rel) + +units: +0100 = altitude (m/ft) +0800 = temperature (°C/°F) +4000 = speed +8000 = speed (00 = km/h, 01 = kts, 10 = mph) + +missing: + ALT/VAR/1/2/3/4 display + Battery Type + +RPA_00_6E1C0000 +RPA_01_01 +RPA_02_1B05 +RPA_03_01 +RPA_04_04040404 +RPA_05_05070A0F +RPA_06_05070A0F +RPA_07_0A080604 +RPA_08_080B0D0E +RPA_09_78838A8E9395999EA4AA +RPA_0a_889497999A9B9B9B9C9C +RPA_0b_00000000000000000000 +RPA_0c_B80B0000 +RPA_0d_9600 +RPA_0e_1601 +RPA_0f_78 +RPA_10_0A00 +RPA_11_1601