diff options
Diffstat (limited to 'src/test')
-rw-r--r-- | src/test/Makefile | 20 | ||||
-rw-r--r-- | src/test/ao_flight_test.c | 716 | ||||
-rw-r--r-- | src/test/ao_gps_test.c | 508 | ||||
-rw-r--r-- | src/test/ao_gps_test_skytraq.c | 490 |
4 files changed, 1722 insertions, 12 deletions
diff --git a/src/test/Makefile b/src/test/Makefile index 33203ffd..333850e4 100644 --- a/src/test/Makefile +++ b/src/test/Makefile @@ -1,9 +1,8 @@ -vpath % .. -vpath % ../kalman +vpath % ..:../core:../drivers PROGS=ao_flight_test ao_flight_test_baro ao_flight_test_accel ao_gps_test ao_gps_test_skytraq ao_convert_test -CFLAGS=-I.. -I. +CFLAGS=-I.. -I. -I../core -I../drivers -O0 -g all: $(PROGS) @@ -13,22 +12,19 @@ clean: install: ao_flight_test: ao_flight_test.c ao_host.h ao_flight.c ao_sample.c ao_kalman.c altitude.h ao_kalman.h - cc -g -o $@ $< + cc $(CFLAGS) -o $@ $< ao_flight_test_baro: ao_flight_test.c ao_host.h ao_flight.c ao_sample.c ao_kalman.c altitude.h ao_kalman.h - cc -g -o $@ -DHAS_ACCEL=0 ../ao_flight_test.c + cc $(CFLAGS) -o $@ -DHAS_ACCEL=0 ao_flight_test.c ao_flight_test_accel: ao_flight_test.c ao_host.h ao_flight.c ao_sample.c ao_kalman.c altitude.h ao_kalman.h - cc -g -o $@ -DFORCE_ACCEL=1 ../ao_flight_test.c + cc $(CFLAGS) -o $@ -DFORCE_ACCEL=1 ao_flight_test.c ao_gps_test: ao_gps_test.c ao_gps_sirf.c ao_gps_print.c ao_host.h - cc -g -o $@ $< + cc $(CFLAGS) -o $@ $< ao_gps_test_skytraq: ao_gps_test_skytraq.c ao_gps_skytraq.c ao_gps_print.c ao_host.h - cc -g -o $@ $< + cc $(CFLAGS) -o $@ $< ao_convert_test: ao_convert_test.c ao_convert.c altitude.h - cc -g -o $@ $< - -../ao_kalman.h: make-kalman kalman.5c kalman_filter.5c load_csv.5c matrix.5c - sh $< > $@ + cc $(CFLAGS) -o $@ $< diff --git a/src/test/ao_flight_test.c b/src/test/ao_flight_test.c new file mode 100644 index 00000000..56733c89 --- /dev/null +++ b/src/test/ao_flight_test.c @@ -0,0 +1,716 @@ +/* + * Copyright © 2009 Keith Packard <keithp@keithp.com> + * + * 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; version 2 of the License. + * + * 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, write to the Free Software Foundation, Inc., + * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA. + */ + +#define _GNU_SOURCE + +#include <stdint.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <getopt.h> +#include <math.h> + +#define AO_HERTZ 100 + +#define AO_ADC_RING 64 +#define ao_adc_ring_next(n) (((n) + 1) & (AO_ADC_RING - 1)) +#define ao_adc_ring_prev(n) (((n) - 1) & (AO_ADC_RING - 1)) + +#define AO_M_TO_HEIGHT(m) ((int16_t) (m)) +#define AO_MS_TO_SPEED(ms) ((int16_t) ((ms) * 16)) +#define AO_MSS_TO_ACCEL(mss) ((int16_t) ((mss) * 16)) + +/* + * One set of samples read from the A/D converter + */ +struct ao_adc { + uint16_t tick; /* tick when the sample was read */ + int16_t accel; /* accelerometer */ + int16_t pres; /* pressure sensor */ + int16_t pres_real; /* unclipped */ + int16_t temp; /* temperature sensor */ + int16_t v_batt; /* battery voltage */ + int16_t sense_d; /* drogue continuity sense */ + int16_t sense_m; /* main continuity sense */ +}; + +#define __pdata +#define __data +#define __xdata +#define __code +#define __reentrant + +#define to_fix16(x) ((int16_t) ((x) * 65536.0 + 0.5)) +#define to_fix32(x) ((int32_t) ((x) * 65536.0 + 0.5)) +#define from_fix(x) ((x) >> 16) + +/* + * Above this height, the baro sensor doesn't work + */ +#define AO_MAX_BARO_HEIGHT 12000 +#define AO_BARO_SATURATE 13000 +#define AO_MIN_BARO_VALUE ao_altitude_to_pres(AO_BARO_SATURATE) + +/* + * Above this speed, baro measurements are unreliable + */ +#define AO_MAX_BARO_SPEED 200 + +#define ACCEL_NOSE_UP (ao_accel_2g >> 2) + +enum ao_flight_state { + ao_flight_startup = 0, + ao_flight_idle = 1, + ao_flight_pad = 2, + ao_flight_boost = 3, + ao_flight_fast = 4, + ao_flight_coast = 5, + ao_flight_drogue = 6, + ao_flight_main = 7, + ao_flight_landed = 8, + ao_flight_invalid = 9 +}; + +extern enum ao_flight_state ao_flight_state; + +#define FALSE 0 +#define TRUE 1 + +struct ao_adc ao_adc_ring[AO_ADC_RING]; +uint8_t ao_adc_head; +int ao_summary = 0; + +#define ao_led_on(l) +#define ao_led_off(l) +#define ao_timer_set_adc_interval(i) +#define ao_wakeup(wchan) ao_dump_state() +#define ao_cmd_register(c) +#define ao_usb_disable() +#define ao_telemetry_set_interval(x) +#define ao_rdf_set(rdf) +#define ao_packet_slave_start() +#define ao_packet_slave_stop() + +enum ao_igniter { + ao_igniter_drogue = 0, + ao_igniter_main = 1 +}; + +struct ao_adc ao_adc_static; + +int drogue_height; +double drogue_time; +int main_height; +double main_time; + +int tick_offset; + +static int32_t ao_k_height; + +void +ao_ignite(enum ao_igniter igniter) +{ + double time = (double) (ao_adc_static.tick + tick_offset) / 100; + + if (igniter == ao_igniter_drogue) { + drogue_time = time; + drogue_height = ao_k_height >> 16; + } else { + main_time = time; + main_height = ao_k_height >> 16; + } +} + +struct ao_task { + int dummy; +}; + +#define ao_add_task(t,f,n) + +#define ao_log_start() +#define ao_log_stop() + +#define AO_MS_TO_TICKS(ms) ((ms) / 10) +#define AO_SEC_TO_TICKS(s) ((s) * 100) + +#define AO_FLIGHT_TEST + +int ao_flight_debug; + +FILE *emulator_in; +char *emulator_app; +char *emulator_name; +double emulator_error_max = 4; +double emulator_height_error_max = 20; /* noise in the baro sensor */ + +void +ao_dump_state(void); + +void +ao_sleep(void *wchan); + +const char const * const ao_state_names[] = { + "startup", "idle", "pad", "boost", "fast", + "coast", "drogue", "main", "landed", "invalid" +}; + +struct ao_cmds { + void (*func)(void); + const char *help; +}; + +#include "ao_convert.c" + +struct ao_config { + uint16_t main_deploy; + int16_t accel_plus_g; + int16_t accel_minus_g; + uint8_t pad_orientation; +}; + +#define AO_PAD_ORIENTATION_ANTENNA_UP 0 +#define AO_PAD_ORIENTATION_ANTENNA_DOWN 1 + +#define ao_config_get() + +struct ao_config ao_config; + +#define DATA_TO_XDATA(x) (x) + +#define HAS_FLIGHT 1 +#define HAS_ADC 1 +#define HAS_USB 1 +#define HAS_GPS 1 +#ifndef HAS_ACCEL +#define HAS_ACCEL 1 +#define HAS_ACCEL_REF 0 +#endif + +#define GRAVITY 9.80665 +extern int16_t ao_ground_accel, ao_flight_accel; +extern int16_t ao_accel_2g; + +extern uint16_t ao_sample_tick; + +extern int16_t ao_sample_height; +extern int16_t ao_sample_accel; +extern int32_t ao_accel_scale; +extern int16_t ao_ground_height; +extern int16_t ao_sample_alt; + +int ao_sample_prev_tick; +uint16_t prev_tick; + +#include "ao_kalman.c" +#include "ao_sample.c" +#include "ao_flight.c" + +#define to_double(f) ((f) / 65536.0) + +static int ao_records_read = 0; +static int ao_eof_read = 0; +static int ao_flight_ground_accel; +static int ao_flight_started = 0; +static int ao_test_max_height; +static double ao_test_max_height_time; +static int ao_test_main_height; +static double ao_test_main_height_time; +static double ao_test_landed_time; +static double ao_test_landed_height; +static double ao_test_landed_time; +static int landed_set; +static double landed_time; +static double landed_height; + +void +ao_test_exit(void) +{ + double drogue_error; + double main_error; + double landed_error; + double landed_time_error; + + if (!ao_test_main_height_time) { + ao_test_main_height_time = ao_test_max_height_time; + ao_test_main_height = ao_test_max_height; + } + drogue_error = fabs(ao_test_max_height_time - drogue_time); + main_error = fabs(ao_test_main_height_time - main_time); + landed_error = fabs(ao_test_landed_height - landed_height); + landed_time_error = ao_test_landed_time - landed_time; + if (drogue_error > emulator_error_max || main_error > emulator_error_max || + landed_time_error > emulator_error_max || landed_error > emulator_height_error_max) { + printf ("%s %s\n", + emulator_app, emulator_name); + printf ("\tApogee error %g\n", drogue_error); + printf ("\tMain error %g\n", main_error); + printf ("\tLanded height error %g\n", landed_error); + printf ("\tLanded time error %g\n", landed_time_error); + printf ("\tActual: apogee: %d at %7.2f main: %d at %7.2f landed %7.2f at %7.2f\n", + ao_test_max_height, ao_test_max_height_time, + ao_test_main_height, ao_test_main_height_time, + ao_test_landed_height, ao_test_landed_time); + printf ("\tComputed: apogee: %d at %7.2f main: %d at %7.2f landed %7.2f at %7.2f\n", + drogue_height, drogue_time, main_height, main_time, + landed_height, landed_time); + exit (1); + } + exit(0); +} + +void +ao_insert(void) +{ + double time; + + ao_adc_ring[ao_adc_head] = ao_adc_static; + ao_adc_head = ao_adc_ring_next(ao_adc_head); + if (ao_flight_state != ao_flight_startup) { + double height = ao_pres_to_altitude(ao_adc_static.pres_real) - ao_ground_height; + double accel = ((ao_flight_ground_accel - ao_adc_static.accel) * GRAVITY * 2.0) / + (ao_config.accel_minus_g - ao_config.accel_plus_g); + + if (!tick_offset) + tick_offset = -ao_adc_static.tick; + if ((prev_tick - ao_adc_static.tick) > 0x400) + tick_offset += 65536; + prev_tick = ao_adc_static.tick; + time = (double) (ao_adc_static.tick + tick_offset) / 100; + + if (ao_test_max_height < height) { + ao_test_max_height = height; + ao_test_max_height_time = time; + ao_test_landed_height = height; + ao_test_landed_time = time; + } + if (height > ao_config.main_deploy) { + ao_test_main_height_time = time; + ao_test_main_height = height; + } + + if (ao_test_landed_height > height) { + ao_test_landed_height = height; + ao_test_landed_time = time; + } + + if (ao_flight_state == ao_flight_landed && !landed_set) { + landed_set = 1; + landed_time = time; + landed_height = height; + } + + if (!ao_summary) { + printf("%7.2f height %8.2f accel %8.3f state %-8.8s k_height %8.2f k_speed %8.3f k_accel %8.3f avg_height %5d drogue %4d main %4d error %5d\n", + time, + height, + accel, + ao_state_names[ao_flight_state], + ao_k_height / 65536.0, + ao_k_speed / 65536.0 / 16.0, + ao_k_accel / 65536.0 / 16.0, + ao_avg_height, + drogue_height, + main_height, + ao_error_h_sq_avg); + +// if (ao_flight_state == ao_flight_landed) +// ao_test_exit(); + } + } +} + +#define AO_MAX_CALLSIGN 8 +#define AO_MAX_VERSION 8 +#define AO_MAX_TELEMETRY 128 + +struct ao_telemetry_generic { + uint16_t serial; /* 0 */ + uint16_t tick; /* 2 */ + uint8_t type; /* 4 */ + uint8_t payload[27]; /* 5 */ + /* 32 */ +}; + +#define AO_TELEMETRY_SENSOR_TELEMETRUM 0x01 +#define AO_TELEMETRY_SENSOR_TELEMINI 0x02 +#define AO_TELEMETRY_SENSOR_TELENANO 0x03 + +struct ao_telemetry_sensor { + uint16_t serial; /* 0 */ + uint16_t tick; /* 2 */ + uint8_t type; /* 4 */ + + uint8_t state; /* 5 flight state */ + int16_t accel; /* 6 accelerometer (TM only) */ + int16_t pres; /* 8 pressure sensor */ + int16_t temp; /* 10 temperature sensor */ + int16_t v_batt; /* 12 battery voltage */ + int16_t sense_d; /* 14 drogue continuity sense (TM/Tm) */ + int16_t sense_m; /* 16 main continuity sense (TM/Tm) */ + + int16_t acceleration; /* 18 m/s² * 16 */ + int16_t speed; /* 20 m/s * 16 */ + int16_t height; /* 22 m */ + + int16_t ground_pres; /* 24 average pres on pad */ + int16_t ground_accel; /* 26 average accel on pad */ + int16_t accel_plus_g; /* 28 accel calibration at +1g */ + int16_t accel_minus_g; /* 30 accel calibration at -1g */ + /* 32 */ +}; + +#define AO_TELEMETRY_CONFIGURATION 0x04 + +struct ao_telemetry_configuration { + uint16_t serial; /* 0 */ + uint16_t tick; /* 2 */ + uint8_t type; /* 4 */ + + uint8_t device; /* 5 device type */ + uint16_t flight; /* 6 flight number */ + uint8_t config_major; /* 8 Config major version */ + uint8_t config_minor; /* 9 Config minor version */ + uint16_t apogee_delay; /* 10 Apogee deploy delay in seconds */ + uint16_t main_deploy; /* 12 Main deploy alt in meters */ + uint16_t flight_log_max; /* 14 Maximum flight log size in kB */ + char callsign[AO_MAX_CALLSIGN]; /* 16 Radio operator identity */ + char version[AO_MAX_VERSION]; /* 24 Software version */ + /* 32 */ +}; + +#define AO_TELEMETRY_LOCATION 0x05 + +#define AO_GPS_MODE_NOT_VALID 'N' +#define AO_GPS_MODE_AUTONOMOUS 'A' +#define AO_GPS_MODE_DIFFERENTIAL 'D' +#define AO_GPS_MODE_ESTIMATED 'E' +#define AO_GPS_MODE_MANUAL 'M' +#define AO_GPS_MODE_SIMULATED 'S' + +struct ao_telemetry_location { + uint16_t serial; /* 0 */ + uint16_t tick; /* 2 */ + uint8_t type; /* 4 */ + + uint8_t flags; /* 5 Number of sats and other flags */ + int16_t altitude; /* 6 GPS reported altitude (m) */ + int32_t latitude; /* 8 latitude (degrees * 10⁷) */ + int32_t longitude; /* 12 longitude (degrees * 10⁷) */ + uint8_t year; /* 16 (- 2000) */ + uint8_t month; /* 17 (1-12) */ + uint8_t day; /* 18 (1-31) */ + uint8_t hour; /* 19 (0-23) */ + uint8_t minute; /* 20 (0-59) */ + uint8_t second; /* 21 (0-59) */ + uint8_t pdop; /* 22 (m * 5) */ + uint8_t hdop; /* 23 (m * 5) */ + uint8_t vdop; /* 24 (m * 5) */ + uint8_t mode; /* 25 */ + uint16_t ground_speed; /* 26 cm/s */ + int16_t climb_rate; /* 28 cm/s */ + uint8_t course; /* 30 degrees / 2 */ + uint8_t unused[1]; /* 31 */ + /* 32 */ +}; + +#define AO_TELEMETRY_SATELLITE 0x06 + +struct ao_telemetry_satellite_info { + uint8_t svid; + uint8_t c_n_1; +}; + +struct ao_telemetry_satellite { + uint16_t serial; /* 0 */ + uint16_t tick; /* 2 */ + uint8_t type; /* 4 */ + uint8_t channels; /* 5 number of reported sats */ + + struct ao_telemetry_satellite_info sats[12]; /* 6 */ + uint8_t unused[2]; /* 30 */ + /* 32 */ +}; + +union ao_telemetry_all { + struct ao_telemetry_generic generic; + struct ao_telemetry_sensor sensor; + struct ao_telemetry_configuration configuration; + struct ao_telemetry_location location; + struct ao_telemetry_satellite satellite; +}; + +uint16_t +uint16(uint8_t *bytes, int off) +{ + off++; + return (uint16_t) bytes[off] | (((uint16_t) bytes[off+1]) << 8); +} + +int16_t +int16(uint8_t *bytes, int off) +{ + return (int16_t) uint16(bytes, off); +} + +void +ao_sleep(void *wchan) +{ + if (wchan == &ao_adc_head) { + char type; + uint16_t tick; + uint16_t a, b; + int ret; + uint8_t bytes[1024]; + union ao_telemetry_all telem; + char line[1024]; + char *saveptr; + char *l; + char *words[64]; + int nword; + + for (;;) { + if (ao_records_read > 2 && ao_flight_state == ao_flight_startup) + { + ao_adc_static.accel = ao_flight_ground_accel; + ao_insert(); + return; + } + + if (!fgets(line, sizeof (line), emulator_in)) { + if (++ao_eof_read >= 1000) { + if (!ao_summary) + printf ("no more data, exiting simulation\n"); + ao_test_exit(); + } + ao_adc_static.tick += 10; + ao_insert(); + return; + } + l = line; + for (nword = 0; nword < 64; nword++) { + words[nword] = strtok_r(l, " \t\n", &saveptr); + l = NULL; + if (words[nword] == NULL) + break; + } + if (nword == 4) { + type = words[0][0]; + tick = strtoul(words[1], NULL, 16); + a = strtoul(words[2], NULL, 16); + b = strtoul(words[3], NULL, 16); + if (type == 'P') + type = 'A'; + } else if (nword >= 6 && strcmp(words[0], "Accel") == 0) { + ao_config.accel_plus_g = atoi(words[3]); + ao_config.accel_minus_g = atoi(words[5]); + } else if (nword >= 4 && strcmp(words[0], "Main") == 0) { + ao_config.main_deploy = atoi(words[2]); + } else if (nword >= 36 && strcmp(words[0], "CALL") == 0) { + tick = atoi(words[10]); + if (!ao_flight_started) { + type = 'F'; + a = atoi(words[26]); + ao_flight_started = 1; + } else { + type = 'A'; + a = atoi(words[12]); + b = atoi(words[14]); + } + } else if (nword == 3 && strcmp(words[0], "BARO") == 0) { + tick = strtol(words[1], NULL, 16); + a = 16384 - 328; + b = strtol(words[2], NULL, 10); + type = 'A'; + if (!ao_flight_started) { + ao_flight_ground_accel = 16384 - 328; + ao_config.accel_plus_g = 16384 - 328; + ao_config.accel_minus_g = 16384 + 328; + ao_flight_started = 1; + } + } else if (nword == 2 && strcmp(words[0], "TELEM") == 0) { + char *hex = words[1]; + char elt[3]; + int i, len; + uint8_t sum; + + len = strlen(hex); + if (len > sizeof (bytes) * 2) { + len = sizeof (bytes)*2; + hex[len] = '\0'; + } + for (i = 0; i < len; i += 2) { + elt[0] = hex[i]; + elt[1] = hex[i+1]; + elt[2] = '\0'; + bytes[i/2] = (uint8_t) strtol(elt, NULL, 16); + } + len = i/2; + if (bytes[0] != len - 2) { + printf ("bad length %d != %d\n", bytes[0], len - 2); + continue; + } + sum = 0x5a; + for (i = 1; i < len-1; i++) + sum += bytes[i]; + if (sum != bytes[len-1]) { + printf ("bad checksum\n"); + continue; + } + if ((bytes[len-2] & 0x80) == 0) { + continue; + } + if (len == 36) { + memcpy(&telem, bytes + 1, 32); + tick = telem.generic.tick; + switch (telem.generic.type) { + case AO_TELEMETRY_SENSOR_TELEMETRUM: + case AO_TELEMETRY_SENSOR_TELEMINI: + case AO_TELEMETRY_SENSOR_TELENANO: + if (!ao_flight_started) { + ao_flight_ground_accel = telem.sensor.ground_accel; + ao_config.accel_plus_g = telem.sensor.accel_plus_g; + ao_config.accel_minus_g = telem.sensor.accel_minus_g; + ao_flight_started = 1; + } + type = 'A'; + a = telem.sensor.accel; + b = telem.sensor.pres; + break; + } + } else if (len == 99) { + ao_flight_started = 1; + tick = uint16(bytes, 21); + ao_flight_ground_accel = int16(bytes, 7); + ao_config.accel_plus_g = int16(bytes, 17); + ao_config.accel_minus_g = int16(bytes, 19); + type = 'A'; + a = int16(bytes, 23); + b = int16(bytes, 25); + } else if (len == 98) { + ao_flight_started = 1; + tick = uint16(bytes, 20); + ao_flight_ground_accel = int16(bytes, 6); + ao_config.accel_plus_g = int16(bytes, 16); + ao_config.accel_minus_g = int16(bytes, 18); + type = 'A'; + a = int16(bytes, 22); + b = int16(bytes, 24); + } else { + printf("unknown len %d\n", len); + continue; + } + } + if (type != 'F' && !ao_flight_started) + continue; + + switch (type) { + case 'F': + ao_flight_ground_accel = a; + if (ao_config.accel_plus_g == 0) { + ao_config.accel_plus_g = a; + ao_config.accel_minus_g = a + 530; + } + if (ao_config.main_deploy == 0) + ao_config.main_deploy = 250; + ao_flight_started = 1; + break; + case 'S': + break; + case 'A': + ao_adc_static.tick = tick; + ao_adc_static.accel = a; + ao_adc_static.pres_real = b; + if (b < AO_MIN_BARO_VALUE) + b = AO_MIN_BARO_VALUE; + ao_adc_static.pres = b; + ao_records_read++; + ao_insert(); + return; + case 'T': + ao_adc_static.tick = tick; + ao_adc_static.temp = a; + ao_adc_static.v_batt = b; + break; + case 'D': + case 'G': + case 'N': + case 'W': + case 'H': + break; + } + } + + } +} +#define COUNTS_PER_G 264.8 + +void +ao_dump_state(void) +{ +} + +static const struct option options[] = { + { .name = "summary", .has_arg = 0, .val = 's' }, + { .name = "debug", .has_arg = 0, .val = 'd' }, + { 0, 0, 0, 0}, +}; + +void run_flight_fixed(char *name, FILE *f, int summary) +{ + emulator_name = name; + emulator_in = f; + ao_summary = summary; + ao_flight_init(); + ao_flight(); +} + +int +main (int argc, char **argv) +{ + int summary = 0; + int c; + int i; + +#if HAS_ACCEL + emulator_app="full"; +#else + emulator_app="baro"; +#endif + while ((c = getopt_long(argc, argv, "sd", options, NULL)) != -1) { + switch (c) { + case 's': + summary = 1; + break; + case 'd': + ao_flight_debug = 1; + break; + } + } + + if (optind == argc) + run_flight_fixed("<stdin>", stdin, summary); + else + for (i = optind; i < argc; i++) { + FILE *f = fopen(argv[i], "r"); + if (!f) { + perror(argv[i]); + continue; + } + run_flight_fixed(argv[i], f, summary); + fclose(f); + } +} diff --git a/src/test/ao_gps_test.c b/src/test/ao_gps_test.c new file mode 100644 index 00000000..93d7a9ab --- /dev/null +++ b/src/test/ao_gps_test.c @@ -0,0 +1,508 @@ +/* + * Copyright © 2009 Keith Packard <keithp@keithp.com> + * + * 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; version 2 of the License. + * + * 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, write to the Free Software Foundation, Inc., + * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA. + */ + +#define AO_GPS_TEST +#include "ao_host.h" +#include <termios.h> +#include <errno.h> +#include <sys/types.h> +#include <sys/stat.h> +#include <fcntl.h> +#define AO_GPS_NUM_SAT_MASK (0xf << 0) +#define AO_GPS_NUM_SAT_SHIFT (0) + +#define AO_GPS_VALID (1 << 4) +#define AO_GPS_RUNNING (1 << 5) +#define AO_GPS_DATE_VALID (1 << 6) +#define AO_GPS_COURSE_VALID (1 << 7) + +struct ao_gps_orig { + uint8_t year; + uint8_t month; + uint8_t day; + uint8_t hour; + uint8_t minute; + uint8_t second; + uint8_t flags; + int32_t latitude; /* degrees * 10⁷ */ + int32_t longitude; /* degrees * 10⁷ */ + int16_t altitude; /* m */ + uint16_t ground_speed; /* cm/s */ + uint8_t course; /* degrees / 2 */ + uint8_t hdop; /* * 5 */ + int16_t climb_rate; /* cm/s */ + uint16_t h_error; /* m */ + uint16_t v_error; /* m */ +}; + +#define SIRF_SAT_STATE_ACQUIRED (1 << 0) +#define SIRF_SAT_STATE_CARRIER_PHASE_VALID (1 << 1) +#define SIRF_SAT_BIT_SYNC_COMPLETE (1 << 2) +#define SIRF_SAT_SUBFRAME_SYNC_COMPLETE (1 << 3) +#define SIRF_SAT_CARRIER_PULLIN_COMPLETE (1 << 4) +#define SIRF_SAT_CODE_LOCKED (1 << 5) +#define SIRF_SAT_ACQUISITION_FAILED (1 << 6) +#define SIRF_SAT_EPHEMERIS_AVAILABLE (1 << 7) + +struct ao_gps_sat_orig { + uint8_t svid; + uint8_t c_n_1; +}; + +#define AO_MAX_GPS_TRACKING 12 + +struct ao_gps_tracking_orig { + uint8_t channels; + struct ao_gps_sat_orig sats[AO_MAX_GPS_TRACKING]; +}; + +#define ao_telemetry_location ao_gps_orig +#define ao_telemetry_satellite ao_gps_tracking_orig +#define ao_telemetry_satellite_info ao_gps_sat_orig + +void +ao_mutex_get(uint8_t *mutex) +{ +} + +void +ao_mutex_put(uint8_t *mutex) +{ +} + +static int +ao_gps_fd; + +static void +ao_dbg_char(char c) +{ + char line[128]; + line[0] = '\0'; + if (c < ' ') { + if (c == '\n') + sprintf (line, "\n"); + else + sprintf (line, "\\%02x", ((int) c) & 0xff); + } else { + sprintf (line, "%c", c); + } + write(1, line, strlen(line)); +} + +#define QUEUE_LEN 4096 + +static char input_queue[QUEUE_LEN]; +int input_head, input_tail; + +#include <sys/time.h> + +int +get_millis(void) +{ + struct timeval tv; + gettimeofday(&tv, NULL); + return tv.tv_sec * 1000 + tv.tv_usec / 1000; +} + +static void +check_sirf_message(char *from, uint8_t *msg, int len) +{ + uint16_t encoded_len, encoded_cksum; + uint16_t cksum; + uint8_t id; + int i; + + if (msg[0] != 0xa0 || msg[1] != 0xa2) { + printf ("bad header\n"); + return; + } + if (len < 7) { + printf("short\n"); + return; + } + if (msg[len-1] != 0xb3 || msg[len-2] != 0xb0) { + printf ("bad trailer\n"); + return; + } + encoded_len = (msg[2] << 8) | msg[3]; + id = msg[4]; +/* printf ("%9d: %3d\n", get_millis(), id); */ + if (encoded_len != len - 8) { + if (id != 52) + printf ("length mismatch (got %d, wanted %d)\n", + len - 8, encoded_len); + return; + } + encoded_cksum = (msg[len - 4] << 8) | msg[len-3]; + cksum = 0; + for (i = 4; i < len - 4; i++) + cksum = (cksum + msg[i]) & 0x7fff; + if (encoded_cksum != cksum) { + printf ("cksum mismatch (got %04x wanted %04x)\n", + cksum, encoded_cksum); + return; + } + id = msg[4]; + switch (id) { + case 41:{ + int off = 4; + + uint8_t id; + uint16_t nav_valid; + uint16_t nav_type; + uint16_t week; + uint32_t tow; + uint16_t year; + uint8_t month; + uint8_t day; + uint8_t hour; + uint8_t minute; + uint16_t second; + uint32_t sat_list; + int32_t lat; + int32_t lon; + int32_t alt_ell; + int32_t alt_msl; + int8_t datum; + uint16_t sog; + uint16_t cog; + int16_t mag_var; + int16_t climb_rate; + int16_t heading_rate; + uint32_t h_error; + uint32_t v_error; + uint32_t t_error; + uint16_t h_v_error; + +#define get_u8(u) u = (msg[off]); off+= 1 +#define get_u16(u) u = (msg[off] << 8) | (msg[off + 1]); off+= 2 +#define get_u32(u) u = (msg[off] << 24) | (msg[off + 1] << 16) | (msg[off+2] << 8) | (msg[off+3]); off+= 4 + + get_u8(id); + get_u16(nav_valid); + get_u16(nav_type); + get_u16(week); + get_u32(tow); + get_u16(year); + get_u8(month); + get_u8(day); + get_u8(hour); + get_u8(minute); + get_u16(second); + get_u32(sat_list); + get_u32(lat); + get_u32(lon); + get_u32(alt_ell); + get_u32(alt_msl); + get_u8(datum); + get_u16(sog); + get_u16(cog); + get_u16(mag_var); + get_u16(climb_rate); + get_u16(heading_rate); + get_u32(h_error); + get_u32(v_error); + get_u32(t_error); + get_u16(h_v_error); + + + printf ("Geodetic Navigation Data (41):\n"); + printf ("\tNav valid %04x\n", nav_valid); + printf ("\tNav type %04x\n", nav_type); + printf ("\tWeek %5d", week); + printf (" TOW %9d", tow); + printf (" %4d-%2d-%2d %02d:%02d:%07.4f\n", + year, month, day, + hour, minute, second / 1000.0); + printf ("\tsats: %08x\n", sat_list); + printf ("\tlat: %g", lat / 1.0e7); + printf (" lon: %g", lon / 1.0e7); + printf (" alt_ell: %g", alt_ell / 100.0); + printf (" alt_msll: %g", alt_msl / 100.0); + printf (" datum: %d\n", datum); + printf ("\tground speed: %g", sog / 100.0); + printf (" course: %g", cog / 100.0); + printf (" climb: %g", climb_rate / 100.0); + printf (" heading rate: %g\n", heading_rate / 100.0); + printf ("\th error: %g", h_error / 100.0); + printf (" v error: %g", v_error / 100.0); + printf (" t error: %g", t_error / 100.0); + printf (" h vel error: %g\n", h_v_error / 100.0); + break; + } + case 4: { + int off = 4; + uint8_t id; + int16_t gps_week; + uint32_t gps_tow; + uint8_t channels; + int j, k; + + get_u8(id); + get_u16(gps_week); + get_u32(gps_tow); + get_u8(channels); + + printf ("Measured Tracker Data (4):\n"); + printf ("GPS week: %d\n", gps_week); + printf ("GPS time of week: %d\n", gps_tow); + printf ("channels: %d\n", channels); + for (j = 0; j < 12; j++) { + uint8_t svid, azimuth, elevation; + uint16_t state; + uint8_t c_n[10]; + get_u8(svid); + get_u8(azimuth); + get_u8(elevation); + get_u16(state); + for (k = 0; k < 10; k++) { + get_u8(c_n[k]); + } + printf ("Sat %3d:", svid); + printf (" aziumuth: %6.1f", azimuth * 1.5); + printf (" elevation: %6.1f", elevation * 0.5); + printf (" state: 0x%02x", state); + printf (" c_n:"); + for (k = 0; k < 10; k++) + printf(" %3d", c_n[k]); + if (state & SIRF_SAT_STATE_ACQUIRED) + printf(" acq,"); + if (state & SIRF_SAT_STATE_CARRIER_PHASE_VALID) + printf(" car,"); + if (state & SIRF_SAT_BIT_SYNC_COMPLETE) + printf(" bit,"); + if (state & SIRF_SAT_SUBFRAME_SYNC_COMPLETE) + printf(" sub,"); + if (state & SIRF_SAT_CARRIER_PULLIN_COMPLETE) + printf(" pullin,"); + if (state & SIRF_SAT_CODE_LOCKED) + printf(" code,"); + if (state & SIRF_SAT_ACQUISITION_FAILED) + printf(" fail,"); + if (state & SIRF_SAT_EPHEMERIS_AVAILABLE) + printf(" ephem,"); + printf ("\n"); + } + break; + } + default: + return; + printf ("%s %4d:", from, encoded_len); + for (i = 4; i < len - 4; i++) { + if (((i - 4) & 0xf) == 0) + printf("\n "); + printf (" %3d", msg[i]); + } + printf ("\n"); + } +} + +static uint8_t sirf_message[4096]; +static int sirf_message_len; +static uint8_t sirf_in_message[4096]; +static int sirf_in_len; + +char +ao_serial_getchar(void) +{ + char c; + uint8_t uc; + + while (input_head == input_tail) { + for (;;) { + input_tail = read(ao_gps_fd, input_queue, QUEUE_LEN); + if (input_tail < 0) { + if (errno == EINTR || errno == EAGAIN) + continue; + perror ("getchar"); + exit (1); + } + input_head = 0; + break; + } + } + c = input_queue[input_head]; + input_head = (input_head + 1) % QUEUE_LEN; + uc = c; + if (sirf_in_len || uc == 0xa0) { + if (sirf_in_len < 4096) + sirf_in_message[sirf_in_len++] = uc; + if (uc == 0xb3) { + check_sirf_message("recv", sirf_in_message, sirf_in_len); + sirf_in_len = 0; + } + } + return c; +} + + +void +ao_serial_putchar(char c) +{ + int i; + uint8_t uc = (uint8_t) c; + + if (sirf_message_len || uc == 0xa0) { + if (sirf_message_len < 4096) + sirf_message[sirf_message_len++] = uc; + if (uc == 0xb3) { + check_sirf_message("send", sirf_message, sirf_message_len); + sirf_message_len = 0; + } + } + for (;;) { + i = write(ao_gps_fd, &c, 1); + if (i == 1) { + if ((uint8_t) c == 0xb3 || c == '\r') { + static const struct timespec delay = { + .tv_sec = 0, + .tv_nsec = 100 * 1000 * 1000 + }; + tcdrain(ao_gps_fd); +// nanosleep(&delay, NULL); + } + break; + } + if (i < 0 && (errno == EINTR || errno == EAGAIN)) + continue; + perror("putchar"); + exit(1); + } +} + +#define AO_SERIAL_SPEED_4800 0 +#define AO_SERIAL_SPEED_57600 1 + +static void +ao_serial_set_speed(uint8_t speed) +{ + int fd = ao_gps_fd; + struct termios termios; + + tcdrain(fd); + tcgetattr(fd, &termios); + switch (speed) { + case AO_SERIAL_SPEED_4800: + cfsetspeed(&termios, B4800); + break; + case AO_SERIAL_SPEED_57600: + cfsetspeed(&termios, B57600); + break; + } + tcsetattr(fd, TCSAFLUSH, &termios); + tcflush(fd, TCIFLUSH); +} + +#define ao_time() 0 + +#include "ao_gps_print.c" +#include "ao_gps_sirf.c" + +void +ao_dump_state(void *wchan) +{ + double lat, lon; + int i; + if (wchan == &ao_gps_data) + ao_gps_print(&ao_gps_data); + else + ao_gps_tracking_print(&ao_gps_tracking_data); + putchar('\n'); + return; + printf ("%02d:%02d:%02d", + ao_gps_data.hour, ao_gps_data.minute, + ao_gps_data.second); + printf (" nsat %d %svalid", + ao_gps_data.flags & AO_GPS_NUM_SAT_MASK, + ao_gps_data.flags & AO_GPS_VALID ? "" : "not "); + printf (" lat %g lon %g alt %d", + ao_gps_data.latitude / 1.0e7, + ao_gps_data.longitude / 1.0e7, + ao_gps_data.altitude); + printf (" speed %g climb %g course %d", + ao_gps_data.ground_speed / 100.0, + ao_gps_data.climb_rate / 100.0, + ao_gps_data.course * 2); + printf (" hdop %g h_error %d v_error %d", + ao_gps_data.hdop / 5.0, + ao_gps_data.h_error, ao_gps_data.v_error); + printf("\n"); + printf ("\t"); + for (i = 0; i < 12; i++) + printf (" %2d(%02d)", + ao_gps_tracking_data.sats[i].svid, + ao_gps_tracking_data.sats[i].c_n_1); + printf ("\n"); +} + +int +ao_gps_open(const char *tty) +{ + struct termios termios; + int fd; + + fd = open (tty, O_RDWR); + if (fd < 0) + return -1; + + tcgetattr(fd, &termios); + cfmakeraw(&termios); + cfsetspeed(&termios, B4800); + tcsetattr(fd, TCSAFLUSH, &termios); + + tcdrain(fd); + tcflush(fd, TCIFLUSH); + return fd; +} + +#include <getopt.h> + +static const struct option options[] = { + { .name = "tty", .has_arg = 1, .val = 'T' }, + { 0, 0, 0, 0}, +}; + +static void usage(char *program) +{ + fprintf(stderr, "usage: %s [--tty <tty-name>]\n", program); + exit(1); +} + +int +main (int argc, char **argv) +{ + char *tty = "/dev/ttyUSB0"; + int c; + + while ((c = getopt_long(argc, argv, "T:", options, NULL)) != -1) { + switch (c) { + case 'T': + tty = optarg; + break; + default: + usage(argv[0]); + break; + } + } + ao_gps_fd = ao_gps_open(tty); + if (ao_gps_fd < 0) { + perror (tty); + exit (1); + } + ao_gps_setup(); + ao_gps(); +} diff --git a/src/test/ao_gps_test_skytraq.c b/src/test/ao_gps_test_skytraq.c new file mode 100644 index 00000000..a78fae0f --- /dev/null +++ b/src/test/ao_gps_test_skytraq.c @@ -0,0 +1,490 @@ +/* + * Copyright © 2009 Keith Packard <keithp@keithp.com> + * + * 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; version 2 of the License. + * + * 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, write to the Free Software Foundation, Inc., + * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA. + */ + +#define AO_GPS_TEST +#include "ao_host.h" +#include <termios.h> +#include <errno.h> +#include <sys/types.h> +#include <sys/stat.h> +#include <fcntl.h> +#define AO_GPS_NUM_SAT_MASK (0xf << 0) +#define AO_GPS_NUM_SAT_SHIFT (0) + +#define AO_GPS_VALID (1 << 4) +#define AO_GPS_RUNNING (1 << 5) +#define AO_GPS_DATE_VALID (1 << 6) +#define AO_GPS_COURSE_VALID (1 << 7) + +struct ao_gps_orig { + uint8_t year; + uint8_t month; + uint8_t day; + uint8_t hour; + uint8_t minute; + uint8_t second; + uint8_t flags; + int32_t latitude; /* degrees * 10⁷ */ + int32_t longitude; /* degrees * 10⁷ */ + int16_t altitude; /* m */ + uint16_t ground_speed; /* cm/s */ + uint8_t course; /* degrees / 2 */ + uint8_t hdop; /* * 5 */ + int16_t climb_rate; /* cm/s */ + uint16_t h_error; /* m */ + uint16_t v_error; /* m */ +}; + +#define SIRF_SAT_STATE_ACQUIRED (1 << 0) +#define SIRF_SAT_STATE_CARRIER_PHASE_VALID (1 << 1) +#define SIRF_SAT_BIT_SYNC_COMPLETE (1 << 2) +#define SIRF_SAT_SUBFRAME_SYNC_COMPLETE (1 << 3) +#define SIRF_SAT_CARRIER_PULLIN_COMPLETE (1 << 4) +#define SIRF_SAT_CODE_LOCKED (1 << 5) +#define SIRF_SAT_ACQUISITION_FAILED (1 << 6) +#define SIRF_SAT_EPHEMERIS_AVAILABLE (1 << 7) + +struct ao_gps_sat_orig { + uint8_t svid; + uint8_t c_n_1; +}; + +#define AO_MAX_GPS_TRACKING 12 + +struct ao_gps_tracking_orig { + uint8_t channels; + struct ao_gps_sat_orig sats[AO_MAX_GPS_TRACKING]; +}; + +#define ao_telemetry_location ao_gps_orig +#define ao_telemetry_satellite ao_gps_tracking_orig +#define ao_telemetry_satellite_info ao_gps_sat_orig + +void +ao_mutex_get(uint8_t *mutex) +{ +} + +void +ao_mutex_put(uint8_t *mutex) +{ +} + +static int +ao_gps_fd; + +static void +ao_dbg_char(char c) +{ + char line[128]; + line[0] = '\0'; + if (c < ' ') { + if (c == '\n') + sprintf (line, "\n"); + else + sprintf (line, "\\%02x", ((int) c) & 0xff); + } else { + sprintf (line, "%c", c); + } + write(1, line, strlen(line)); +} + +#define QUEUE_LEN 4096 + +static char input_queue[QUEUE_LEN]; +int input_head, input_tail; + +#include <sys/time.h> + +int +get_millis(void) +{ + struct timeval tv; + gettimeofday(&tv, NULL); + return tv.tv_sec * 1000 + tv.tv_usec / 1000; +} + +static void +check_skytraq_message(char *from, uint8_t *msg, int len) +{ + uint16_t encoded_len, encoded_cksum; + uint16_t cksum; + uint8_t id; + int i; + +// fwrite(msg, 1, len, stdout); + return; + if (msg[0] != 0xa0 || msg[1] != 0xa2) { + printf ("bad header\n"); + return; + } + if (len < 7) { + printf("short\n"); + return; + } + if (msg[len-1] != 0xb3 || msg[len-2] != 0xb0) { + printf ("bad trailer\n"); + return; + } + encoded_len = (msg[2] << 8) | msg[3]; + id = msg[4]; +/* printf ("%9d: %3d\n", get_millis(), id); */ + if (encoded_len != len - 8) { + if (id != 52) + printf ("length mismatch (got %d, wanted %d)\n", + len - 8, encoded_len); + return; + } + encoded_cksum = (msg[len - 4] << 8) | msg[len-3]; + cksum = 0; + for (i = 4; i < len - 4; i++) + cksum = (cksum + msg[i]) & 0x7fff; + if (encoded_cksum != cksum) { + printf ("cksum mismatch (got %04x wanted %04x)\n", + cksum, encoded_cksum); + return; + } + id = msg[4]; + switch (id) { + case 41:{ + int off = 4; + + uint8_t id; + uint16_t nav_valid; + uint16_t nav_type; + uint16_t week; + uint32_t tow; + uint16_t year; + uint8_t month; + uint8_t day; + uint8_t hour; + uint8_t minute; + uint16_t second; + uint32_t sat_list; + int32_t lat; + int32_t lon; + int32_t alt_ell; + int32_t alt_msl; + int8_t datum; + uint16_t sog; + uint16_t cog; + int16_t mag_var; + int16_t climb_rate; + int16_t heading_rate; + uint32_t h_error; + uint32_t v_error; + uint32_t t_error; + uint16_t h_v_error; + +#define get_u8(u) u = (msg[off]); off+= 1 +#define get_u16(u) u = (msg[off] << 8) | (msg[off + 1]); off+= 2 +#define get_u32(u) u = (msg[off] << 24) | (msg[off + 1] << 16) | (msg[off+2] << 8) | (msg[off+3]); off+= 4 + + get_u8(id); + get_u16(nav_valid); + get_u16(nav_type); + get_u16(week); + get_u32(tow); + get_u16(year); + get_u8(month); + get_u8(day); + get_u8(hour); + get_u8(minute); + get_u16(second); + get_u32(sat_list); + get_u32(lat); + get_u32(lon); + get_u32(alt_ell); + get_u32(alt_msl); + get_u8(datum); + get_u16(sog); + get_u16(cog); + get_u16(mag_var); + get_u16(climb_rate); + get_u16(heading_rate); + get_u32(h_error); + get_u32(v_error); + get_u32(t_error); + get_u16(h_v_error); + + + printf ("Geodetic Navigation Data (41):\n"); + printf ("\tNav valid %04x\n", nav_valid); + printf ("\tNav type %04x\n", nav_type); + printf ("\tWeek %5d", week); + printf (" TOW %9d", tow); + printf (" %4d-%2d-%2d %02d:%02d:%07.4f\n", + year, month, day, + hour, minute, second / 1000.0); + printf ("\tsats: %08x\n", sat_list); + printf ("\tlat: %g", lat / 1.0e7); + printf (" lon: %g", lon / 1.0e7); + printf (" alt_ell: %g", alt_ell / 100.0); + printf (" alt_msll: %g", alt_msl / 100.0); + printf (" datum: %d\n", datum); + printf ("\tground speed: %g", sog / 100.0); + printf (" course: %g", cog / 100.0); + printf (" climb: %g", climb_rate / 100.0); + printf (" heading rate: %g\n", heading_rate / 100.0); + printf ("\th error: %g", h_error / 100.0); + printf (" v error: %g", v_error / 100.0); + printf (" t error: %g", t_error / 100.0); + printf (" h vel error: %g\n", h_v_error / 100.0); + break; + } + case 4: { + int off = 4; + uint8_t id; + int16_t gps_week; + uint32_t gps_tow; + uint8_t channels; + int j, k; + + get_u8(id); + get_u16(gps_week); + get_u32(gps_tow); + get_u8(channels); + + printf ("Measured Tracker Data (4):\n"); + printf ("GPS week: %d\n", gps_week); + printf ("GPS time of week: %d\n", gps_tow); + printf ("channels: %d\n", channels); + for (j = 0; j < 12; j++) { + uint8_t svid, azimuth, elevation; + uint16_t state; + uint8_t c_n[10]; + get_u8(svid); + get_u8(azimuth); + get_u8(elevation); + get_u16(state); + for (k = 0; k < 10; k++) { + get_u8(c_n[k]); + } + printf ("Sat %3d:", svid); + printf (" aziumuth: %6.1f", azimuth * 1.5); + printf (" elevation: %6.1f", elevation * 0.5); + printf (" state: 0x%02x", state); + printf (" c_n:"); + for (k = 0; k < 10; k++) + printf(" %3d", c_n[k]); + if (state & SIRF_SAT_STATE_ACQUIRED) + printf(" acq,"); + if (state & SIRF_SAT_STATE_CARRIER_PHASE_VALID) + printf(" car,"); + if (state & SIRF_SAT_BIT_SYNC_COMPLETE) + printf(" bit,"); + if (state & SIRF_SAT_SUBFRAME_SYNC_COMPLETE) + printf(" sub,"); + if (state & SIRF_SAT_CARRIER_PULLIN_COMPLETE) + printf(" pullin,"); + if (state & SIRF_SAT_CODE_LOCKED) + printf(" code,"); + if (state & SIRF_SAT_ACQUISITION_FAILED) + printf(" fail,"); + if (state & SIRF_SAT_EPHEMERIS_AVAILABLE) + printf(" ephem,"); + printf ("\n"); + } + break; + } + default: + return; + printf ("%s %4d:", from, encoded_len); + for (i = 4; i < len - 4; i++) { + if (((i - 4) & 0xf) == 0) + printf("\n "); + printf (" %3d", msg[i]); + } + printf ("\n"); + } +} + +static uint8_t skytraq_message[4096]; +static int skytraq_message_len; +static uint8_t skytraq_in_message[4096]; +static int skytraq_in_len; + +char +ao_serial_getchar(void) +{ + char c; + uint8_t uc; + + while (input_head == input_tail) { + for (;;) { + input_tail = read(ao_gps_fd, input_queue, QUEUE_LEN); + if (input_tail < 0) { + if (errno == EINTR || errno == EAGAIN) + continue; + perror ("getchar"); + exit (1); + } + input_head = 0; + break; + } + } + c = input_queue[input_head]; + input_head = (input_head + 1) % QUEUE_LEN; + uc = c; +// printf ("c: %02x %c\n", uc, uc); + if (skytraq_in_len || uc == '$') { + if (skytraq_in_len < 4096) + skytraq_in_message[skytraq_in_len++] = uc; + if (uc == 0x0a) { + check_skytraq_message("recv", skytraq_in_message, skytraq_in_len); + skytraq_in_len = 0; + } + } + return c; +} + + +void +ao_serial_putchar(char c) +{ + int i; + uint8_t uc = (uint8_t) c; + + if (skytraq_message_len || uc == 0xa0) { + if (skytraq_message_len < 4096) + skytraq_message[skytraq_message_len++] = uc; + if (uc == 0x0a) { + check_skytraq_message("send", skytraq_message, skytraq_message_len); + skytraq_message_len = 0; + } + } + for (;;) { + i = write(ao_gps_fd, &c, 1); + if (i == 1) { + if ((uint8_t) c == 0xb3 || c == '\r') { + static const struct timespec delay = { + .tv_sec = 0, + .tv_nsec = 100 * 1000 * 1000 + }; + tcdrain(ao_gps_fd); +// nanosleep(&delay, NULL); + } + break; + } + if (i < 0 && (errno == EINTR || errno == EAGAIN)) + continue; + perror("putchar"); + exit(1); + } +} + +#define AO_SERIAL_SPEED_4800 0 +#define AO_SERIAL_SPEED_9600 1 +#define AO_SERIAL_SPEED_57600 2 + +static void +ao_serial_set_speed(uint8_t speed) +{ + int fd = ao_gps_fd; + struct termios termios; + + tcdrain(fd); + tcgetattr(fd, &termios); + switch (speed) { + case AO_SERIAL_SPEED_4800: + cfsetspeed(&termios, B4800); + break; + case AO_SERIAL_SPEED_9600: + cfsetspeed(&termios, B38400); + break; + case AO_SERIAL_SPEED_57600: + cfsetspeed(&termios, B57600); + break; + } + tcsetattr(fd, TCSAFLUSH, &termios); + tcflush(fd, TCIFLUSH); +} + +#define ao_time() 0 + +#include "ao_gps_print.c" +#include "ao_gps_skytraq.c" + +void +ao_dump_state(void *wchan) +{ + double lat, lon; + int i; + if (wchan == &ao_gps_data) + ao_gps_print(&ao_gps_data); + else + ao_gps_tracking_print(&ao_gps_tracking_data); + putchar('\n'); + return; +} + +int +ao_gps_open(const char *tty) +{ + struct termios termios; + int fd; + + fd = open (tty, O_RDWR); + if (fd < 0) + return -1; + + tcgetattr(fd, &termios); + cfmakeraw(&termios); + cfsetspeed(&termios, B4800); + tcsetattr(fd, TCSAFLUSH, &termios); + + tcdrain(fd); + tcflush(fd, TCIFLUSH); + return fd; +} + +#include <getopt.h> + +static const struct option options[] = { + { .name = "tty", .has_arg = 1, .val = 'T' }, + { 0, 0, 0, 0}, +}; + +static void usage(char *program) +{ + fprintf(stderr, "usage: %s [--tty <tty-name>]\n", program); + exit(1); +} + +int +main (int argc, char **argv) +{ + char *tty = "/dev/ttyUSB0"; + int c; + + while ((c = getopt_long(argc, argv, "T:", options, NULL)) != -1) { + switch (c) { + case 'T': + tty = optarg; + break; + default: + usage(argv[0]); + break; + } + } + ao_gps_fd = ao_gps_open(tty); + if (ao_gps_fd < 0) { + perror (tty); + exit (1); + } + ao_gps(); +} |