From: Keith Packard Date: Mon, 31 Dec 2012 22:24:59 +0000 (-0800) Subject: Merge remote-tracking branch 'origin/master' into micropeak-logging X-Git-Tag: 1.1.9.3~8^2~9^2~35 X-Git-Url: https://git.gag.com/?p=fw%2Faltos;a=commitdiff_plain;h=65b512c890a3ccf487655b79305ab1cfcf49259c;hp=d7d259c7b3eedcc1c185d2b7e3c33e829bd7ce96 Merge remote-tracking branch 'origin/master' into micropeak-logging --- diff --git a/Makefile.am b/Makefile.am index aaa0ae14..e8945d90 100644 --- a/Makefile.am +++ b/Makefile.am @@ -1,4 +1,4 @@ -SUBDIRS=src doc altoslib altosui ao-tools ao-utils altosdroid +SUBDIRS=src doc altoslib libaltos altosuilib altosui ao-tools ao-utils altosdroid EXTRA_DIST = ChangeLog diff --git a/altoslib/AltosPreferences.java b/altoslib/AltosPreferences.java index 47196d6e..e50b9b5c 100644 --- a/altoslib/AltosPreferences.java +++ b/altoslib/AltosPreferences.java @@ -367,6 +367,8 @@ public class AltosPreferences { set_common_frequencies(new_frequencies); } + static LinkedList units_listeners; + public static boolean imperial_units() { synchronized(backend) { return AltosConvert.imperial_units; @@ -379,5 +381,24 @@ public class AltosPreferences { backend.putBoolean(unitsPreference, imperial_units); flush_preferences(); } + if (units_listeners != null) { + for (AltosUnitsListener l : units_listeners) { + l.units_changed(imperial_units); + } + } + } + + public static void register_units_listener(AltosUnitsListener l) { + synchronized(backend) { + if (units_listeners == null) + units_listeners = new LinkedList(); + units_listeners.add(l); + } + } + + public static void unregister_units_listener(AltosUnitsListener l) { + synchronized(backend) { + units_listeners.remove(l); + } } } diff --git a/altoslib/AltosUnitsListener.java b/altoslib/AltosUnitsListener.java new file mode 100644 index 00000000..50a00cdf --- /dev/null +++ b/altoslib/AltosUnitsListener.java @@ -0,0 +1,22 @@ +/* + * Copyright © 2012 Keith Packard + * + * 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. + */ + +package org.altusmetrum.AltosLib; + +public interface AltosUnitsListener { + public void units_changed(boolean imperial_units); +} diff --git a/altoslib/Makefile.am b/altoslib/Makefile.am index 0086bc65..1b03c925 100644 --- a/altoslib/Makefile.am +++ b/altoslib/Makefile.am @@ -69,6 +69,7 @@ AltosLib_JAVA = \ $(SRC)/AltosTelemetryRecordSensor.java \ $(SRC)/AltosTelemetryRecordMegaSensor.java \ $(SRC)/AltosTelemetryRecordMegaData.java \ + $(SRC)/AltosUnitsListener.java \ $(SRC)/AltosMs5607.java \ $(SRC)/AltosIMU.java \ $(SRC)/AltosMag.java \ diff --git a/altosui/Makefile.am b/altosui/Makefile.am index 306a396e..a42426cd 100644 --- a/altosui/Makefile.am +++ b/altosui/Makefile.am @@ -1,4 +1,4 @@ -SUBDIRS=libaltos + JAVAROOT=classes AM_JAVACFLAGS=-encoding UTF-8 -Xlint:deprecation @@ -6,7 +6,7 @@ man_MANS=altosui.1 altoslibdir=$(libdir)/altos -CLASSPATH_ENV=mkdir -p $(JAVAROOT); CLASSPATH=".:classes:../altoslib/*:libaltos:$(JCOMMON)/jcommon.jar:$(JFREECHART)/jfreechart.jar:$(FREETTS)/freetts.jar" +CLASSPATH_ENV=mkdir -p $(JAVAROOT); CLASSPATH=".:classes:../altoslib/*:../libaltos:$(JCOMMON)/jcommon.jar:$(JFREECHART)/jfreechart.jar:$(FREETTS)/freetts.jar" bin_SCRIPTS=altosui @@ -233,13 +233,13 @@ $(JAR): classaltosui.stamp Manifest.txt $(JAVA_ICONS) $(ALTOSLIB_CLASS) jar cfm $@ Manifest.txt \ $(ICONJAR) \ -C classes altosui \ - -C libaltos libaltosJNI + -C ../libaltos libaltosJNI $(FATJAR): classaltosui.stamp Manifest-fat.txt $(ALTOSLIB_CLASS) $(FREETTS_CLASS) $(JFREECHART_CLASS) $(JCOMMON_CLASS) $(LIBALTOS) $(JAVA_ICONS) jar cfm $@ Manifest-fat.txt \ $(ICONJAR) \ -C classes altosui \ - -C libaltos libaltosJNI + -C ../libaltos libaltosJNI Manifest.txt: Makefile echo 'Main-Class: altosui.AltosUI' > $@ @@ -256,43 +256,43 @@ altosui: Makefile altosui-test: Makefile echo "#!/bin/sh" > $@ - echo 'exec java -cp "./*:$(FREETTS)/freetts.jar:$(JCOMMON)/jcommon.jar:$(JFREECHART)/jfreechart.jar" -Djava.library.path="libaltos/.libs" -jar altosui.jar "$$@"' >> $@ + echo 'exec java -cp "./*:../libaltos:$(FREETTS)/freetts.jar:$(JCOMMON)/jcommon.jar:$(JFREECHART)/jfreechart.jar" -Djava.library.path="../libaltos/.libs" -jar altosui.jar "$$@"' >> $@ chmod +x $@ altosui-jdb: Makefile echo "#!/bin/sh" > $@ - echo 'exec jdb -classpath "classes:libaltos:$(FREETTS)/freetts.jar:$(JCOMMON)/jcommon.jar:$(JFREECHART)/jfreechart.jar" -Djava.library.path="libaltos/.libs" altosui/AltosUI "$$@"' >> $@ + echo 'exec jdb -classpath "classes:../libaltos:$(FREETTS)/freetts.jar:$(JCOMMON)/jcommon.jar:$(JFREECHART)/jfreechart.jar" -Djava.library.path="../libaltos/.libs" altosui/AltosUI "$$@"' >> $@ chmod +x $@ libaltos.so: build-libaltos -rm -f "$@" - $(LN_S) libaltos/.libs/"$@" . + $(LN_S) ../libaltos/.libs/"$@" . libaltos.dylib: -rm -f "$@" - $(LN_S) libaltos/"$@" . + $(LN_S) ../libaltos/"$@" . -altos.dll: libaltos/altos.dll +altos.dll: ../libaltos/altos.dll -rm -f "$@" - $(LN_S) libaltos/"$@" . + $(LN_S) ../libaltos/"$@" . -altos64.dll: libaltos/altos64.dll +altos64.dll: ../libaltos/altos64.dll -rm -f "$@" - $(LN_S) libaltos/"$@" . + $(LN_S) ../libaltos/"$@" . -libaltos/.libs/libaltos.so: build-libaltos +../libaltos/.libs/libaltos.so: build-libaltos -libaltos/altos.dll: build-altos-dll +../libaltos/altos.dll: build-altos-dll -libaltos/altos64.dll: build-altos64-dll +../libaltos/altos64.dll: build-altos64-dll build-libaltos: - +cd libaltos && make libaltos.la + +cd ../libaltos && make libaltos.la build-altos-dll: - +cd libaltos && make altos.dll + +cd ../libaltos && make altos.dll build-altos64-dll: - +cd libaltos && make altos64.dll + +cd ../libaltos && make altos64.dll $(ALTOSLIB_CLASS): -rm -f "$@" diff --git a/altosui/libaltos/.gitignore b/altosui/libaltos/.gitignore deleted file mode 100644 index c490e6f8..00000000 --- a/altosui/libaltos/.gitignore +++ /dev/null @@ -1,12 +0,0 @@ -*.so -*.lo -*.la -*.java -*.class -.libs/ -classlibaltos.stamp -libaltos_wrap.c -libaltosJNI -cjnitest -libaltos.swig -swig_bindings/ diff --git a/altosui/libaltos/Makefile-standalone b/altosui/libaltos/Makefile-standalone deleted file mode 100644 index 4e438050..00000000 --- a/altosui/libaltos/Makefile-standalone +++ /dev/null @@ -1,126 +0,0 @@ -OS:=$(shell uname) - -# -# Linux -# -ifeq ($(OS),Linux) - -JAVA_CFLAGS=-I/usr/lib/jvm/java-6-openjdk/include - -OS_LIB_CFLAGS=-DLINUX -DPOSIX_TTY $(JAVA_CFLAGS) - -OS_APP_CFLAGS=$(OS_LIB_CFLAGS) - -OS_LDFLAGS= - -LIBNAME=libaltos.so -EXEEXT= -endif - -# -# Darwin (Mac OS X) -# -ifeq ($(OS),Darwin) - -OS_LIB_CFLAGS=\ - -DDARWIN -DPOSIX_TTY -arch i386 -arch x86_64 \ - --sysroot=/Developer/SDKs/MacOSX10.5.sdk -mmacosx-version-min=10.5 \ - -iwithsysroot /System/Library/Frameworks/JavaVM.framework/Headers \ - -iwithsysroot /System/Library/Frameworks/IOKit.framework/Headers \ - -iwithsysroot /System/Library/Frameworks/CoreFoundation.framework/Headers -OS_APP_CFLAGS=$(OS_LIB_CFLAGS) - -OS_LDFLAGS =\ - -framework IOKit -framework CoreFoundation - -LIBNAME=libaltos.dylib -EXEEXT= - -endif - -# -# Windows -# -ifneq (,$(findstring MINGW,$(OS))) - -CC=gcc - -OS_LIB_CFLAGS = -DWINDOWS -mconsole -DBUILD_DLL -OS_APP_CFLAGS = -DWINDOWS -mconsole - -OS_LDFLAGS = -lgdi32 -luser32 -lcfgmgr32 -lsetupapi -lole32 \ - -ladvapi32 -lcomctl32 -mconsole -Wl,--add-stdcall-alias - -LIBNAME=altos.dll - -EXEEXT=.exe - -endif - -.SUFFIXES: .java .class - -CLASSPATH=".:jnitest/*:libaltosJNI:/usr/share/java/*" - -SWIG_DIR=swig_bindings/java -SWIG_FILE=$(SWIG_DIR)/libaltos.swig -SWIG_WRAP=$(SWIG_DIR)/libaltos_wrap.c - -JNI_DIR=libaltosJNI -JNI_FILE=$(JNI_DIR)/libaltosJNI.java -JNI_SRCS=$(JNI_FILE) \ - $(JNI_DIR)/SWIGTYPE_p_altos_file.java \ - $(JNI_DIR)/SWIGTYPE_p_altos_list.java \ - $(JNI_DIR)/altos_device.java \ - $(JNI_DIR)/libaltos.java - -JAVAFILES=\ - $(JNI_SRCS) - -CLASSFILES = $(JAVAFILES:%.java=%.class) - -JAVAFLAGS=-Xlint:unchecked - -CJNITEST=cjnitest$(EXEEXT) - -all: $(LIBNAME) $(CJNITEST) $(CLASSFILES) - -.java.class: - javac -encoding UTF8 -classpath "$(CLASSPATH)" $(JAVAFLAGS) $*.java - -CFLAGS=$(OS_LIB_CFLAGS) -O -I. - -LDFLAGS=$(OS_LDFLAGS) - -HEADERS=libaltos.h -SRCS = libaltos.c $(SWIG_WRAP) -OBJS = $(SRCS:%.c=%.o) -LIBS = $(DARWIN_LIBS) - -$(CJNITEST): cjnitest.c $(LIBNAME) - $(CC) -o $@ $(OS_APP_CFLAGS) cjnitest.c $(LIBNAME) $(LIBS) $(LDFLAGS) - -$(LIBNAME): $(OBJS) - $(CC) -shared $(CFLAGS) -o $@ $(OBJS) $(LIBS) $(LDFLAGS) - -clean: - rm -f $(CLASSFILES) $(OBJS) $(LIBNAME) $(CJNITEST) cjnitest.o - rm -rf swig_bindings libaltosJNI - -distclean: clean - -$(JNI_FILE): libaltos.i0 $(HEADERS) - mkdir -p $(SWIG_DIR) - mkdir -p libaltosJNI - sed 's;//%;%;' libaltos.i0 $(HEADERS) > $(SWIG_FILE) - swig -java -package libaltosJNI $(SWIG_FILE) - cp swig_bindings/java/*.java libaltosJNI - -$(SWIG_WRAP): $(JNI_FILE) - -ifeq ($(OS),Linux) -install: $(LIBNAME) - install -c $(LIBNAME) $(DESTDIR)/usr/lib/altos/$(LIBNAME) - -endif - -.NOTPARALLEL: diff --git a/altosui/libaltos/Makefile.am b/altosui/libaltos/Makefile.am deleted file mode 100644 index b5ab1ddb..00000000 --- a/altosui/libaltos/Makefile.am +++ /dev/null @@ -1,54 +0,0 @@ -JAVAC=javac -AM_CFLAGS=-DLINUX -DPOSIX_TTY -I$(JVM_INCLUDE) -AM_JAVACFLAGS=-encoding UTF-8 - -altoslibdir=$(libdir)/altos - -altoslib_LTLIBRARIES=libaltos.la - -libaltos_la_LDFLAGS = -version-info 1:0:1 - -libaltos_la_SOURCES=\ - libaltos.c \ - libaltos_wrap.c - -noinst_PROGRAMS=cjnitest - -cjnitest_LDADD=libaltos.la - -LIBS=-lbluetooth - -HFILES=libaltos.h - -SWIG_FILE=libaltos.swig - -CLASSDIR=libaltosJNI - -$(SWIG_FILE): libaltos.i0 $(HFILES) - sed 's;//%;%;' libaltos.i0 $(HFILES) > $(SWIG_FILE) - -all-local: classlibaltos.stamp - -libaltos_wrap.c: classlibaltos.stamp - -classlibaltos.stamp: $(SWIG_FILE) - swig -java -package libaltosJNI $(SWIG_FILE) - mkdir -p libaltosJNI - $(JAVAC) -d . $(AM_JAVACFLAGS) $(JAVACFLAGS) *.java && \ - touch classlibaltos.stamp - -MINGCC32=i686-w64-mingw32-gcc -MINGCC64=x86_64-w64-mingw32-gcc -MINGFLAGS=-Wall -DWINDOWS -DBUILD_DLL -I$(JVM_INCLUDE) -MINGLIBS=-lsetupapi - -fat: altos.dll altos64.dll - -altos.dll: $(libaltos_la_SOURCES) - $(MINGCC32) -o $@ $(MINGFLAGS) -shared $(libaltos_la_SOURCES) $(MINGLIBS) - -altos64.dll: $(libaltos_la_SOURCES) - $(MINGCC64) -o $@ $(MINGFLAGS) -shared $(libaltos_la_SOURCES) $(MINGLIBS) - -clean-local: - -rm -rf libaltosJNI *.class *.java classlibaltos.stamp $(SWIG_FILE) libaltos_wrap.c altos.dll altos64.dll diff --git a/altosui/libaltos/cjnitest.c b/altosui/libaltos/cjnitest.c deleted file mode 100644 index f0fe78f7..00000000 --- a/altosui/libaltos/cjnitest.c +++ /dev/null @@ -1,71 +0,0 @@ -#include -#include "libaltos.h" - -static void -altos_puts(struct altos_file *file, char *string) -{ - char c; - - while ((c = *string++)) - altos_putchar(file, c); -} - -main () -{ - struct altos_device device; - struct altos_list *list; - struct altos_bt_device bt_device; - struct altos_bt_list *bt_list; - - altos_init(); - list = altos_list_start(); - while (altos_list_next(list, &device)) { - struct altos_file *file; - int c; - - printf ("%04x:%04x %-20s %4d %s\n", device.vendor, device.product, - device.name, device.serial, device.path); - - file = altos_open(&device); - if (!file) { - printf("altos_open failed\n"); - continue; - } - altos_puts(file,"v\nc s\n"); - altos_flush(file); - while ((c = altos_getchar(file, 100)) >= 0) { - putchar (c); - } - if (c != LIBALTOS_TIMEOUT) - printf ("getchar returns %d\n", c); - altos_close(file); - } - altos_list_finish(list); -#if HAS_BLUETOOTH - bt_list = altos_bt_list_start(8); - while (altos_bt_list_next(bt_list, &bt_device)) { - printf ("%s %s\n", bt_device.name, bt_device.addr); - if (strncmp(bt_device.name, "TeleBT", 6) == 0) { - struct altos_file *file; - - int c; - file = altos_bt_open(&bt_device); - if (!file) { - printf("altos_bt_open failed\n"); - continue; - } - altos_puts(file,"v\nc s\n"); - altos_flush(file); - while ((c = altos_getchar(file, 100)) >= 0) { - putchar(c); - } - if (c != LIBALTOS_TIMEOUT) - printf("getchar returns %d\n", c); - altos_close(file); - } - } - altos_bt_list_finish(bt_list); -#endif - altos_fini(); - return 0; -} diff --git a/altosui/libaltos/libaltos.c b/altosui/libaltos/libaltos.c deleted file mode 100644 index ab6ca878..00000000 --- a/altosui/libaltos/libaltos.c +++ /dev/null @@ -1,1311 +0,0 @@ -/* - * Copyright © 2010 Keith Packard - * - * 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. - */ - -#include "libaltos.h" -#include -#include -#include - -#define USB_VENDOR_FSF 0xfffe -#define USB_VENDOR_ALTUSMETRUM USB_VENDOR_FSF -#define USB_PRODUCT_ALTUSMETRUM 0x000a -#define USB_PRODUCT_ALTUSMETRUM_MIN 0x000a -#define USB_PRODUCT_ALTUSMETRUM_MAX 0x00ff - -#define USB_IS_ALTUSMETRUM(v,p) ((v) == USB_VENDOR_ALTUSMETRUM && \ - (USB_PRODUCT_ALTUSMETRUM_MIN <= (p) && \ - (p) <= USB_PRODUCT_ALTUSMETRUM_MAX)) - -#define BLUETOOTH_PRODUCT_TELEBT "TeleBT" - -#define USE_POLL - -PUBLIC int -altos_init(void) -{ - return LIBALTOS_SUCCESS; -} - -PUBLIC void -altos_fini(void) -{ -} - -static struct altos_error last_error; - -static void -altos_set_last_error(int code, char *string) -{ - last_error.code = code; - strncpy(last_error.string, string, sizeof (last_error.string) -1); - last_error.string[sizeof(last_error.string)-1] = '\0'; -} - -PUBLIC void -altos_get_last_error(struct altos_error *error) -{ - *error = last_error; -} - -#ifdef DARWIN - -#undef USE_POLL - -/* Mac OS X don't have strndup even if _GNU_SOURCE is defined */ -static char * -altos_strndup (const char *s, size_t n) -{ - size_t len = strlen (s); - char *ret; - - if (len <= n) - return strdup (s); - ret = malloc(n + 1); - strncpy(ret, s, n); - ret[n] = '\0'; - return ret; -} - -#else -#define altos_strndup strndup -#endif - -#ifdef POSIX_TTY - -#include -#include -#include -#include -#include - -#define USB_BUF_SIZE 64 - -struct altos_file { - int fd; -#ifdef USE_POLL - int pipe[2]; -#else - int out_fd; -#endif - unsigned char out_data[USB_BUF_SIZE]; - int out_used; - unsigned char in_data[USB_BUF_SIZE]; - int in_used; - int in_read; -}; - -static void -altos_set_last_posix_error(void) -{ - altos_set_last_error(errno, strerror(errno)); -} - -PUBLIC struct altos_file * -altos_open(struct altos_device *device) -{ - struct altos_file *file = calloc (sizeof (struct altos_file), 1); - int ret; - struct termios term; - - if (!file) { - altos_set_last_posix_error(); - return NULL; - } - -// altos_set_last_error(12, "yeah yeah, failed again"); -// free(file); -// return NULL; - - file->fd = open(device->path, O_RDWR | O_NOCTTY); - if (file->fd < 0) { - altos_set_last_posix_error(); - free(file); - return NULL; - } -#ifdef USE_POLL - pipe(file->pipe); -#else - file->out_fd = open(device->path, O_RDWR | O_NOCTTY); - if (file->out_fd < 0) { - altos_set_last_posix_error(); - close(file->fd); - free(file); - return NULL; - } -#endif - ret = tcgetattr(file->fd, &term); - if (ret < 0) { - altos_set_last_posix_error(); - close(file->fd); -#ifndef USE_POLL - close(file->out_fd); -#endif - free(file); - return NULL; - } - cfmakeraw(&term); -#ifdef USE_POLL - term.c_cc[VMIN] = 1; - term.c_cc[VTIME] = 0; -#else - term.c_cc[VMIN] = 0; - term.c_cc[VTIME] = 1; -#endif - ret = tcsetattr(file->fd, TCSAFLUSH, &term); - if (ret < 0) { - altos_set_last_posix_error(); - close(file->fd); -#ifndef USE_POLL - close(file->out_fd); -#endif - free(file); - return NULL; - } - return file; -} - -PUBLIC void -altos_close(struct altos_file *file) -{ - if (file->fd != -1) { - int fd = file->fd; - file->fd = -1; -#ifdef USE_POLL - write(file->pipe[1], "\r", 1); -#else - close(file->out_fd); - file->out_fd = -1; -#endif - close(fd); - } -} - -PUBLIC void -altos_free(struct altos_file *file) -{ - altos_close(file); - free(file); -} - -PUBLIC int -altos_flush(struct altos_file *file) -{ - if (file->out_used && 0) { - printf ("flush \""); - fwrite(file->out_data, 1, file->out_used, stdout); - printf ("\"\n"); - } - while (file->out_used) { - int ret; - - if (file->fd < 0) - return -EBADF; -#ifdef USE_POLL - ret = write (file->fd, file->out_data, file->out_used); -#else - ret = write (file->out_fd, file->out_data, file->out_used); -#endif - if (ret < 0) { - altos_set_last_posix_error(); - return -last_error.code; - } - if (ret) { - memmove(file->out_data, file->out_data + ret, - file->out_used - ret); - file->out_used -= ret; - } - } - return 0; -} - -PUBLIC int -altos_putchar(struct altos_file *file, char c) -{ - int ret; - - if (file->out_used == USB_BUF_SIZE) { - ret = altos_flush(file); - if (ret) { - return ret; - } - } - file->out_data[file->out_used++] = c; - ret = 0; - if (file->out_used == USB_BUF_SIZE) - ret = altos_flush(file); - return ret; -} - -#ifdef USE_POLL -#include -#endif - -static int -altos_fill(struct altos_file *file, int timeout) -{ - int ret; -#ifdef USE_POLL - struct pollfd fd[2]; -#endif - - if (timeout == 0) - timeout = -1; - while (file->in_read == file->in_used) { - if (file->fd < 0) - return LIBALTOS_ERROR; -#ifdef USE_POLL - fd[0].fd = file->fd; - fd[0].events = POLLIN|POLLERR|POLLHUP|POLLNVAL; - fd[1].fd = file->pipe[0]; - fd[1].events = POLLIN; - ret = poll(fd, 2, timeout); - if (ret < 0) { - altos_set_last_posix_error(); - return LIBALTOS_ERROR; - } - if (ret == 0) - return LIBALTOS_TIMEOUT; - - if (fd[0].revents & (POLLHUP|POLLERR|POLLNVAL)) - return LIBALTOS_ERROR; - if (fd[0].revents & POLLIN) -#endif - { - ret = read(file->fd, file->in_data, USB_BUF_SIZE); - if (ret < 0) { - altos_set_last_posix_error(); - return LIBALTOS_ERROR; - } - file->in_read = 0; - file->in_used = ret; -#ifndef USE_POLL - if (ret == 0 && timeout > 0) - return LIBALTOS_TIMEOUT; -#endif - } - } - if (file->in_used && 0) { - printf ("fill \""); - fwrite(file->in_data, 1, file->in_used, stdout); - printf ("\"\n"); - } - return 0; -} - -PUBLIC int -altos_getchar(struct altos_file *file, int timeout) -{ - int ret; - while (file->in_read == file->in_used) { - if (file->fd < 0) - return LIBALTOS_ERROR; - ret = altos_fill(file, timeout); - if (ret) - return ret; - } - return file->in_data[file->in_read++]; -} - -#endif /* POSIX_TTY */ - -/* - * Scan for Altus Metrum devices by looking through /sys - */ - -#ifdef LINUX - -#define _GNU_SOURCE -#include -#include -#include -#include -#include -#include -#include -#include -#include - -static char * -cc_fullname (char *dir, char *file) -{ - char *new; - int dlen = strlen (dir); - int flen = strlen (file); - int slen = 0; - - if (dir[dlen-1] != '/') - slen = 1; - new = malloc (dlen + slen + flen + 1); - if (!new) - return 0; - strcpy(new, dir); - if (slen) - strcat (new, "/"); - strcat(new, file); - return new; -} - -static char * -cc_basename(char *file) -{ - char *b; - - b = strrchr(file, '/'); - if (!b) - return file; - return b + 1; -} - -static char * -load_string(char *dir, char *file) -{ - char *full = cc_fullname(dir, file); - char line[4096]; - char *r; - FILE *f; - int rlen; - - f = fopen(full, "r"); - free(full); - if (!f) - return NULL; - r = fgets(line, sizeof (line), f); - fclose(f); - if (!r) - return NULL; - rlen = strlen(r); - if (r[rlen-1] == '\n') - r[rlen-1] = '\0'; - return strdup(r); -} - -static int -load_hex(char *dir, char *file) -{ - char *line; - char *end; - long i; - - line = load_string(dir, file); - if (!line) - return -1; - i = strtol(line, &end, 16); - free(line); - if (end == line) - return -1; - return i; -} - -static int -load_dec(char *dir, char *file) -{ - char *line; - char *end; - long i; - - line = load_string(dir, file); - if (!line) - return -1; - i = strtol(line, &end, 10); - free(line); - if (end == line) - return -1; - return i; -} - -static int -dir_filter_tty_colon(const struct dirent *d) -{ - return strncmp(d->d_name, "tty:", 4) == 0; -} - -static int -dir_filter_tty(const struct dirent *d) -{ - return strncmp(d->d_name, "tty", 3) == 0; -} - -struct altos_usbdev { - char *sys; - char *tty; - char *manufacturer; - char *product_name; - int serial; /* AltOS always uses simple integer serial numbers */ - int idProduct; - int idVendor; -}; - -static char * -usb_tty(char *sys) -{ - char *base; - int num_configs; - int config; - struct dirent **namelist; - int interface; - int num_interfaces; - char endpoint_base[20]; - char *endpoint_full; - char *tty_dir; - int ntty; - char *tty; - - base = cc_basename(sys); - num_configs = load_hex(sys, "bNumConfigurations"); - num_interfaces = load_hex(sys, "bNumInterfaces"); - for (config = 1; config <= num_configs; config++) { - for (interface = 0; interface < num_interfaces; interface++) { - sprintf(endpoint_base, "%s:%d.%d", - base, config, interface); - endpoint_full = cc_fullname(sys, endpoint_base); - - /* Check for tty:ttyACMx style names - */ - ntty = scandir(endpoint_full, &namelist, - dir_filter_tty_colon, - alphasort); - if (ntty > 0) { - free(endpoint_full); - tty = cc_fullname("/dev", namelist[0]->d_name + 4); - free(namelist); - return tty; - } - - /* Check for tty/ttyACMx style names - */ - tty_dir = cc_fullname(endpoint_full, "tty"); - free(endpoint_full); - ntty = scandir(tty_dir, &namelist, - dir_filter_tty, - alphasort); - free (tty_dir); - if (ntty > 0) { - tty = cc_fullname("/dev", namelist[0]->d_name); - free(namelist); - return tty; - } - } - } - return NULL; -} - -static struct altos_usbdev * -usb_scan_device(char *sys) -{ - struct altos_usbdev *usbdev; - - usbdev = calloc(1, sizeof (struct altos_usbdev)); - if (!usbdev) - return NULL; - usbdev->sys = strdup(sys); - usbdev->manufacturer = load_string(sys, "manufacturer"); - usbdev->product_name = load_string(sys, "product"); - usbdev->serial = load_dec(sys, "serial"); - usbdev->idProduct = load_hex(sys, "idProduct"); - usbdev->idVendor = load_hex(sys, "idVendor"); - usbdev->tty = usb_tty(sys); - return usbdev; -} - -static void -usbdev_free(struct altos_usbdev *usbdev) -{ - free(usbdev->sys); - free(usbdev->manufacturer); - free(usbdev->product_name); - /* this can get used as a return value */ - if (usbdev->tty) - free(usbdev->tty); - free(usbdev); -} - -#define USB_DEVICES "/sys/bus/usb/devices" - -static int -dir_filter_dev(const struct dirent *d) -{ - const char *n = d->d_name; - char c; - - while ((c = *n++)) { - if (isdigit(c)) - continue; - if (c == '-') - continue; - if (c == '.' && n != d->d_name + 1) - continue; - return 0; - } - return 1; -} - -struct altos_list { - struct altos_usbdev **dev; - int current; - int ndev; -}; - -struct altos_list * -altos_list_start(void) -{ - int e; - struct dirent **ents; - char *dir; - struct altos_usbdev *dev; - struct altos_list *devs; - int n; - - devs = calloc(1, sizeof (struct altos_list)); - if (!devs) - return NULL; - - n = scandir (USB_DEVICES, &ents, - dir_filter_dev, - alphasort); - if (!n) - return 0; - for (e = 0; e < n; e++) { - dir = cc_fullname(USB_DEVICES, ents[e]->d_name); - dev = usb_scan_device(dir); - free(dir); - if (USB_IS_ALTUSMETRUM(dev->idVendor, dev->idProduct)) { - if (devs->dev) - devs->dev = realloc(devs->dev, - (devs->ndev + 1) * sizeof (struct usbdev *)); - else - devs->dev = malloc (sizeof (struct usbdev *)); - devs->dev[devs->ndev++] = dev; - } - } - free(ents); - devs->current = 0; - return devs; -} - -int -altos_list_next(struct altos_list *list, struct altos_device *device) -{ - struct altos_usbdev *dev; - if (list->current >= list->ndev) - return 0; - dev = list->dev[list->current]; - strcpy(device->name, dev->product_name); - device->vendor = dev->idVendor; - device->product = dev->idProduct; - strcpy(device->path, dev->tty); - device->serial = dev->serial; - list->current++; - return 1; -} - -void -altos_list_finish(struct altos_list *usbdevs) -{ - int i; - - if (!usbdevs) - return; - for (i = 0; i < usbdevs->ndev; i++) - usbdev_free(usbdevs->dev[i]); - free(usbdevs); -} - -struct altos_bt_list { - inquiry_info *ii; - int sock; - int dev_id; - int rsp; - int num_rsp; -}; - -#define INQUIRY_MAX_RSP 255 - -struct altos_bt_list * -altos_bt_list_start(int inquiry_time) -{ - struct altos_bt_list *bt_list; - - bt_list = calloc(1, sizeof (struct altos_bt_list)); - if (!bt_list) - goto no_bt_list; - - bt_list->ii = calloc(INQUIRY_MAX_RSP, sizeof (inquiry_info)); - if (!bt_list->ii) - goto no_ii; - bt_list->dev_id = hci_get_route(NULL); - if (bt_list->dev_id < 0) - goto no_dev_id; - - bt_list->sock = hci_open_dev(bt_list->dev_id); - if (bt_list->sock < 0) - goto no_sock; - - bt_list->num_rsp = hci_inquiry(bt_list->dev_id, - inquiry_time, - INQUIRY_MAX_RSP, - NULL, - &bt_list->ii, - IREQ_CACHE_FLUSH); - if (bt_list->num_rsp < 0) - goto no_rsp; - - bt_list->rsp = 0; - return bt_list; - -no_rsp: - close(bt_list->sock); -no_sock: -no_dev_id: - free(bt_list->ii); -no_ii: - free(bt_list); -no_bt_list: - return NULL; -} - -int -altos_bt_list_next(struct altos_bt_list *bt_list, - struct altos_bt_device *device) -{ - inquiry_info *ii; - - if (bt_list->rsp >= bt_list->num_rsp) - return 0; - - ii = &bt_list->ii[bt_list->rsp]; - ba2str(&ii->bdaddr, device->addr); - memset(&device->name, '\0', sizeof (device->name)); - if (hci_read_remote_name(bt_list->sock, &ii->bdaddr, - sizeof (device->name), - device->name, 0) < 0) { - strcpy(device->name, "[unknown]"); - } - bt_list->rsp++; - return 1; -} - -void -altos_bt_list_finish(struct altos_bt_list *bt_list) -{ - close(bt_list->sock); - free(bt_list->ii); - free(bt_list); -} - -void -altos_bt_fill_in(char *name, char *addr, struct altos_bt_device *device) -{ - strncpy(device->name, name, sizeof (device->name)); - device->name[sizeof(device->name)-1] = '\0'; - strncpy(device->addr, addr, sizeof (device->addr)); - device->addr[sizeof(device->addr)-1] = '\0'; -} - -struct altos_file * -altos_bt_open(struct altos_bt_device *device) -{ - struct sockaddr_rc addr = { 0 }; - int s, status; - struct altos_file *file; - - file = calloc(1, sizeof (struct altos_file)); - if (!file) - goto no_file; - file->fd = socket(AF_BLUETOOTH, SOCK_STREAM, BTPROTO_RFCOMM); - if (file->fd < 0) { - altos_set_last_posix_error(); - goto no_sock; - } - - addr.rc_family = AF_BLUETOOTH; - addr.rc_channel = 1; - str2ba(device->addr, &addr.rc_bdaddr); - - status = connect(file->fd, - (struct sockaddr *)&addr, - sizeof(addr)); - if (status < 0) { - altos_set_last_posix_error(); - goto no_link; - } - sleep(1); - -#ifdef USE_POLL - pipe(file->pipe); -#else - file->out_fd = dup(file->fd); -#endif - return file; -no_link: - close(s); -no_sock: - free(file); -no_file: - return NULL; -} - -#endif - -#ifdef DARWIN - -#include -#include -#include -#include -#include -#include -#include -#include -#include - -struct altos_list { - io_iterator_t iterator; -}; - -static int -get_string(io_object_t object, CFStringRef entry, char *result, int result_len) -{ - CFTypeRef entry_as_string; - Boolean got_string; - - entry_as_string = IORegistryEntrySearchCFProperty (object, - kIOServicePlane, - entry, - kCFAllocatorDefault, - kIORegistryIterateRecursively); - if (entry_as_string) { - got_string = CFStringGetCString(entry_as_string, - result, result_len, - kCFStringEncodingASCII); - - CFRelease(entry_as_string); - if (got_string) - return 1; - } - return 0; -} - -static int -get_number(io_object_t object, CFStringRef entry, int *result) -{ - CFTypeRef entry_as_number; - Boolean got_number; - - entry_as_number = IORegistryEntrySearchCFProperty (object, - kIOServicePlane, - entry, - kCFAllocatorDefault, - kIORegistryIterateRecursively); - if (entry_as_number) { - got_number = CFNumberGetValue(entry_as_number, - kCFNumberIntType, - result); - if (got_number) - return 1; - } - return 0; -} - -PUBLIC struct altos_list * -altos_list_start(void) -{ - struct altos_list *list = calloc (sizeof (struct altos_list), 1); - CFMutableDictionaryRef matching_dictionary = IOServiceMatching("IOUSBDevice"); - io_iterator_t tdIterator; - io_object_t tdObject; - kern_return_t ret; - int i; - - ret = IOServiceGetMatchingServices(kIOMasterPortDefault, matching_dictionary, &list->iterator); - if (ret != kIOReturnSuccess) - return NULL; - return list; -} - -PUBLIC int -altos_list_next(struct altos_list *list, struct altos_device *device) -{ - io_object_t object; - char serial_string[128]; - - for (;;) { - object = IOIteratorNext(list->iterator); - if (!object) - return 0; - - if (!get_number (object, CFSTR(kUSBVendorID), &device->vendor) || - !get_number (object, CFSTR(kUSBProductID), &device->product)) - continue; - if (device->vendor != 0xfffe) - continue; - if (device->product < 0x000a || 0x0013 < device->product) - continue; - if (get_string (object, CFSTR("IOCalloutDevice"), device->path, sizeof (device->path)) && - get_string (object, CFSTR("USB Product Name"), device->name, sizeof (device->name)) && - get_string (object, CFSTR("USB Serial Number"), serial_string, sizeof (serial_string))) { - device->serial = atoi(serial_string); - return 1; - } - } -} - -PUBLIC void -altos_list_finish(struct altos_list *list) -{ - IOObjectRelease (list->iterator); - free(list); -} - -struct altos_bt_list { - int sock; - int dev_id; - int rsp; - int num_rsp; -}; - -#define INQUIRY_MAX_RSP 255 - -struct altos_bt_list * -altos_bt_list_start(int inquiry_time) -{ - return NULL; -} - -int -altos_bt_list_next(struct altos_bt_list *bt_list, - struct altos_bt_device *device) -{ - return 0; -} - -void -altos_bt_list_finish(struct altos_bt_list *bt_list) -{ -} - -void -altos_bt_fill_in(char *name, char *addr, struct altos_bt_device *device) -{ - strncpy(device->name, name, sizeof (device->name)); - device->name[sizeof(device->name)-1] = '\0'; - strncpy(device->addr, addr, sizeof (device->addr)); - device->addr[sizeof(device->addr)-1] = '\0'; -} - -struct altos_file * -altos_bt_open(struct altos_bt_device *device) -{ - return NULL; -} - -#endif - - -#ifdef WINDOWS - -#include -#include -#include - -struct altos_list { - HDEVINFO dev_info; - int index; -}; - -#define USB_BUF_SIZE 64 - -struct altos_file { - HANDLE handle; - unsigned char out_data[USB_BUF_SIZE]; - int out_used; - unsigned char in_data[USB_BUF_SIZE]; - int in_used; - int in_read; - OVERLAPPED ov_read; - BOOL pend_read; - OVERLAPPED ov_write; -}; - -static void -_altos_set_last_windows_error(char *file, int line) -{ - DWORD error = GetLastError(); - TCHAR message[1024]; - FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM, - 0, - error, - 0, - message, - sizeof (message) / sizeof (TCHAR), - NULL); - if (error != ERROR_SUCCESS) - printf ("%s:%d %s\n", file, line, message); - altos_set_last_error(error, message); -} - -#define altos_set_last_windows_error() _altos_set_last_windows_error(__FILE__, __LINE__) - -PUBLIC struct altos_list * -altos_list_start(void) -{ - struct altos_list *list = calloc(1, sizeof (struct altos_list)); - - if (!list) - return NULL; - list->dev_info = SetupDiGetClassDevs(NULL, "USB", NULL, - DIGCF_ALLCLASSES|DIGCF_PRESENT); - if (list->dev_info == INVALID_HANDLE_VALUE) { - altos_set_last_windows_error(); - free(list); - return NULL; - } - list->index = 0; - return list; -} - -PUBLIC int -altos_list_next(struct altos_list *list, struct altos_device *device) -{ - SP_DEVINFO_DATA dev_info_data; - BYTE port[128]; - DWORD port_len; - char friendlyname[256]; - BYTE symbolic[256]; - DWORD symbolic_len; - HKEY dev_key; - unsigned int vid, pid; - int serial; - HRESULT result; - DWORD friendlyname_type; - DWORD friendlyname_len; - - dev_info_data.cbSize = sizeof (SP_DEVINFO_DATA); - while(SetupDiEnumDeviceInfo(list->dev_info, list->index, - &dev_info_data)) - { - list->index++; - - dev_key = SetupDiOpenDevRegKey(list->dev_info, &dev_info_data, - DICS_FLAG_GLOBAL, 0, DIREG_DEV, - KEY_READ); - if (dev_key == INVALID_HANDLE_VALUE) { - altos_set_last_windows_error(); - printf("cannot open device registry key\n"); - continue; - } - - /* Fetch symbolic name for this device and parse out - * the vid/pid/serial info */ - symbolic_len = sizeof(symbolic); - result = RegQueryValueEx(dev_key, "SymbolicName", NULL, NULL, - symbolic, &symbolic_len); - if (result != 0) { - altos_set_last_windows_error(); - printf("cannot find SymbolicName value\n"); - RegCloseKey(dev_key); - continue; - } - vid = pid = serial = 0; - sscanf((char *) symbolic + sizeof("\\??\\USB#VID_") - 1, - "%04X", &vid); - sscanf((char *) symbolic + sizeof("\\??\\USB#VID_XXXX&PID_") - 1, - "%04X", &pid); - sscanf((char *) symbolic + sizeof("\\??\\USB#VID_XXXX&PID_XXXX#") - 1, - "%d", &serial); - if (!USB_IS_ALTUSMETRUM(vid, pid)) { - RegCloseKey(dev_key); - continue; - } - - /* Fetch the com port name */ - port_len = sizeof (port); - result = RegQueryValueEx(dev_key, "PortName", NULL, NULL, - port, &port_len); - RegCloseKey(dev_key); - if (result != 0) { - altos_set_last_windows_error(); - printf("failed to get PortName\n"); - continue; - } - - /* Fetch the device description which is the device name, - * with firmware that has unique USB ids */ - friendlyname_len = sizeof (friendlyname); - if(!SetupDiGetDeviceRegistryProperty(list->dev_info, - &dev_info_data, - SPDRP_FRIENDLYNAME, - &friendlyname_type, - (BYTE *)friendlyname, - sizeof(friendlyname), - &friendlyname_len)) - { - altos_set_last_windows_error(); - printf("Failed to get friendlyname\n"); - continue; - } - device->vendor = vid; - device->product = pid; - device->serial = serial; - strcpy(device->name, friendlyname); - - strcpy(device->path, (char *) port); - return 1; - } - result = GetLastError(); - if (result != ERROR_NO_MORE_ITEMS) { - altos_set_last_windows_error(); - printf ("SetupDiEnumDeviceInfo failed error %d\n", (int) result); - } - return 0; -} - -PUBLIC void -altos_list_finish(struct altos_list *list) -{ - SetupDiDestroyDeviceInfoList(list->dev_info); - free(list); -} - -static int -altos_queue_read(struct altos_file *file) -{ - DWORD got; - if (file->pend_read) - return LIBALTOS_SUCCESS; - - if (!ReadFile(file->handle, file->in_data, USB_BUF_SIZE, &got, &file->ov_read)) { - if (GetLastError() != ERROR_IO_PENDING) { - altos_set_last_windows_error(); - return LIBALTOS_ERROR; - } - file->pend_read = TRUE; - } else { - file->pend_read = FALSE; - file->in_read = 0; - file->in_used = got; - } - return LIBALTOS_SUCCESS; -} - -static int -altos_wait_read(struct altos_file *file, int timeout) -{ - DWORD ret; - DWORD got; - - if (!file->pend_read) - return LIBALTOS_SUCCESS; - - if (!timeout) - timeout = INFINITE; - - ret = WaitForSingleObject(file->ov_read.hEvent, timeout); - switch (ret) { - case WAIT_OBJECT_0: - if (!GetOverlappedResult(file->handle, &file->ov_read, &got, FALSE)) { - altos_set_last_windows_error(); - return LIBALTOS_ERROR; - } - file->pend_read = FALSE; - file->in_read = 0; - file->in_used = got; - break; - case WAIT_TIMEOUT: - return LIBALTOS_TIMEOUT; - break; - default: - return LIBALTOS_ERROR; - } - return LIBALTOS_SUCCESS; -} - -static int -altos_fill(struct altos_file *file, int timeout) -{ - int ret; - - if (file->in_read < file->in_used) - return LIBALTOS_SUCCESS; - - file->in_read = file->in_used = 0; - - ret = altos_queue_read(file); - if (ret) - return ret; - ret = altos_wait_read(file, timeout); - if (ret) - return ret; - - return LIBALTOS_SUCCESS; -} - -PUBLIC int -altos_flush(struct altos_file *file) -{ - DWORD put; - unsigned char *data = file->out_data; - int used = file->out_used; - DWORD ret; - - while (used) { - if (!WriteFile(file->handle, data, used, &put, &file->ov_write)) { - if (GetLastError() != ERROR_IO_PENDING) { - altos_set_last_windows_error(); - printf ("\tflush write error\n"); - return LIBALTOS_ERROR; - } - ret = WaitForSingleObject(file->ov_write.hEvent, INFINITE); - switch (ret) { - case WAIT_OBJECT_0: - if (!GetOverlappedResult(file->handle, &file->ov_write, &put, FALSE)) { - altos_set_last_windows_error(); - printf ("\tflush result error\n"); - return LIBALTOS_ERROR; - } - break; - default: - altos_set_last_windows_error(); - printf ("\tflush wait error\n"); - return LIBALTOS_ERROR; - } - } - data += put; - used -= put; - } - file->out_used = 0; - return LIBALTOS_SUCCESS; -} - -PUBLIC struct altos_file * -altos_open(struct altos_device *device) -{ - struct altos_file *file = calloc (1, sizeof (struct altos_file)); - char full_name[64]; - COMMTIMEOUTS timeouts; - - if (!file) - return NULL; - - strcpy(full_name, "\\\\.\\"); - strcat(full_name, device->path); - file->handle = CreateFile(full_name, GENERIC_READ|GENERIC_WRITE, - 0, NULL, OPEN_EXISTING, - FILE_FLAG_OVERLAPPED, NULL); - if (file->handle == INVALID_HANDLE_VALUE) { - altos_set_last_windows_error(); - printf ("cannot open %s\n", full_name); - free(file); - return NULL; - } - file->ov_read.hEvent = CreateEvent(NULL, TRUE, FALSE, NULL); - file->ov_write.hEvent = CreateEvent(NULL, TRUE, FALSE, NULL); - - timeouts.ReadIntervalTimeout = MAXDWORD; - timeouts.ReadTotalTimeoutMultiplier = MAXDWORD; - timeouts.ReadTotalTimeoutConstant = 1 << 30; /* almost forever */ - timeouts.WriteTotalTimeoutMultiplier = 0; - timeouts.WriteTotalTimeoutConstant = 0; - SetCommTimeouts(file->handle, &timeouts); - - return file; -} - -PUBLIC void -altos_close(struct altos_file *file) -{ - if (file->handle != INVALID_HANDLE_VALUE) { - CloseHandle(file->handle); - file->handle = INVALID_HANDLE_VALUE; - SetEvent(file->ov_read.hEvent); - SetEvent(file->ov_write.hEvent); - CloseHandle(file->ov_read.hEvent); - CloseHandle(file->ov_write.hEvent); - } -} - -PUBLIC void -altos_free(struct altos_file *file) -{ - altos_close(file); - free(file); -} - -PUBLIC int -altos_putchar(struct altos_file *file, char c) -{ - int ret; - - if (file->out_used == USB_BUF_SIZE) { - ret = altos_flush(file); - if (ret) - return ret; - } - file->out_data[file->out_used++] = c; - if (file->out_used == USB_BUF_SIZE) - return altos_flush(file); - return LIBALTOS_SUCCESS; -} - -PUBLIC int -altos_getchar(struct altos_file *file, int timeout) -{ - int ret; - while (file->in_read == file->in_used) { - if (file->handle == INVALID_HANDLE_VALUE) - return LIBALTOS_ERROR; - ret = altos_fill(file, timeout); - if (ret) - return ret; - } - return file->in_data[file->in_read++]; -} - -struct altos_bt_list * -altos_bt_list_start(int inquiry_time) -{ - return NULL; -} - -int -altos_bt_list_next(struct altos_bt_list *bt_list, - struct altos_bt_device *device) -{ - return 0; -} - -void -altos_bt_list_finish(struct altos_bt_list *bt_list) -{ - free(bt_list); -} - -void -altos_bt_fill_in(char *name, char *addr, struct altos_bt_device *device) -{ - strncpy(device->name, name, sizeof (device->name)); - device->name[sizeof(device->name)-1] = '\0'; - strncpy(device->addr, addr, sizeof (device->addr)); - device->addr[sizeof(device->addr)-1] = '\0'; -} - -struct altos_file * -altos_bt_open(struct altos_bt_device *device) -{ - return NULL; -} - -#endif diff --git a/altosui/libaltos/libaltos.dylib b/altosui/libaltos/libaltos.dylib deleted file mode 100755 index 1038817d..00000000 Binary files a/altosui/libaltos/libaltos.dylib and /dev/null differ diff --git a/altosui/libaltos/libaltos.h b/altosui/libaltos/libaltos.h deleted file mode 100644 index f90fbb87..00000000 --- a/altosui/libaltos/libaltos.h +++ /dev/null @@ -1,119 +0,0 @@ -/* - * Copyright © 2010 Keith Packard - * - * 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. - */ - -#ifndef _LIBALTOS_H_ -#define _LIBALTOS_H_ - -#include - -#if defined(_WIN32) || defined(__WIN32__) || defined(__CYGWIN__) -# ifndef BUILD_STATIC -# ifdef BUILD_DLL -# define PUBLIC __declspec(dllexport) -# else -# define PUBLIC __declspec(dllimport) -# endif -# endif /* BUILD_STATIC */ -#endif - -#ifndef PUBLIC -# define PUBLIC -#endif - -struct altos_device { - //%immutable; - int vendor; - int product; - int serial; - char name[256]; - char path[256]; - //%mutable; -}; - -struct altos_bt_device { - //%immutable; - char name[256]; - char addr[20]; - //%mutable; -}; - -struct altos_error { - int code; - char string[1024]; -}; - -#define LIBALTOS_SUCCESS 0 -#define LIBALTOS_ERROR -1 -#define LIBALTOS_TIMEOUT -2 - -/* Returns 0 for success, < 0 on error */ -PUBLIC int -altos_init(void); - -PUBLIC void -altos_fini(void); - -PUBLIC void -altos_get_last_error(struct altos_error *error); - -PUBLIC struct altos_list * -altos_list_start(void); - -/* Returns 1 for success, zero on end of list */ -PUBLIC int -altos_list_next(struct altos_list *list, struct altos_device *device); - -PUBLIC void -altos_list_finish(struct altos_list *list); - -PUBLIC struct altos_file * -altos_open(struct altos_device *device); - -PUBLIC void -altos_close(struct altos_file *file); - -PUBLIC void -altos_free(struct altos_file *file); - -/* Returns < 0 for error */ -PUBLIC int -altos_putchar(struct altos_file *file, char c); - -/* Returns < 0 for error */ -PUBLIC int -altos_flush(struct altos_file *file); - -/* Returns < 0 for error or timeout. timeout of 0 == wait forever */ -PUBLIC int -altos_getchar(struct altos_file *file, int timeout); - -PUBLIC struct altos_bt_list * -altos_bt_list_start(int inquiry_time); - -PUBLIC int -altos_bt_list_next(struct altos_bt_list *list, struct altos_bt_device *device); - -PUBLIC void -altos_bt_list_finish(struct altos_bt_list *list); - -PUBLIC void -altos_bt_fill_in(char *name, char *addr, struct altos_bt_device *device); - -PUBLIC struct altos_file * -altos_bt_open(struct altos_bt_device *device); - -#endif /* _LIBALTOS_H_ */ diff --git a/altosui/libaltos/libaltos.i0 b/altosui/libaltos/libaltos.i0 deleted file mode 100644 index d06468f5..00000000 --- a/altosui/libaltos/libaltos.i0 +++ /dev/null @@ -1,5 +0,0 @@ -%module libaltos -%{ -#include "libaltos.h" -%} - diff --git a/altosuilib/AltosConfigureUI.java b/altosuilib/AltosConfigureUI.java new file mode 100644 index 00000000..a4b644bf --- /dev/null +++ b/altosuilib/AltosConfigureUI.java @@ -0,0 +1,393 @@ +/* + * Copyright © 2010 Keith Packard + * + * 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. + */ + +package org.altusmetrum.altosuilib; + +import java.awt.*; +import java.awt.event.*; +import java.beans.*; +import javax.swing.*; +import javax.swing.event.*; + +class DelegatingRenderer implements ListCellRenderer { + + // ... + public static void install(JComboBox comboBox) { + DelegatingRenderer renderer = new DelegatingRenderer(comboBox); + renderer.initialise(); + comboBox.setRenderer(renderer); + } + + // ... + private final JComboBox comboBox; + + // ... + private ListCellRenderer delegate; + + // ... + private DelegatingRenderer(JComboBox comboBox) { + this.comboBox = comboBox; + } + + // ... + private void initialise() { + delegate = new JComboBox().getRenderer(); + comboBox.addPropertyChangeListener("UI", new PropertyChangeListener() { + + public void propertyChange(PropertyChangeEvent evt) { + delegate = new JComboBox().getRenderer(); + } + }); + } + + // ... + public Component getListCellRendererComponent(JList list, + Object value, int index, boolean isSelected, boolean cellHasFocus) { + + return delegate.getListCellRendererComponent(list, + ((UIManager.LookAndFeelInfo) value).getName(), + index, isSelected, cellHasFocus); + } +} + +public class AltosConfigureUI + extends AltosUIDialog + implements DocumentListener +{ + JFrame owner; + Container pane; + + JRadioButton enable_voice; + JButton test_voice; + JButton close; + + JButton configure_log; + JTextField log_directory; + + JLabel callsign_label; + JTextField callsign_value; + + JRadioButton imperial_units; + + JLabel font_size_label; + JComboBox font_size_value; + + JLabel look_and_feel_label; + JComboBox look_and_feel_value; + + JRadioButton serial_debug; + + JButton manage_bluetooth; + JButton manage_frequencies; + + int row; + + final static String[] font_size_names = { "Small", "Medium", "Large" }; + + /* DocumentListener interface methods */ + public void changedUpdate(DocumentEvent e) { + if (callsign_value != null) + AltosUIPreferences.set_callsign(callsign_value.getText()); + } + + public void insertUpdate(DocumentEvent e) { + changedUpdate(e); + } + + public void removeUpdate(DocumentEvent e) { + changedUpdate(e); + } + + public GridBagConstraints constraints (int x, int width, int fill) { + GridBagConstraints c = new GridBagConstraints(); + Insets insets = new Insets(4, 4, 4, 4); + + c.insets = insets; + c.fill = fill; + if (width == 3) + c.anchor = GridBagConstraints.CENTER; + else + c.anchor = GridBagConstraints.WEST; + c.gridx = x; + c.gridwidth = width; + c.gridy = row; + return c; + } + + public GridBagConstraints constraints(int x, int width) { + return constraints(x, width, GridBagConstraints.NONE); + } + + public void add_voice() { +// GridBagConstraints c = new GridBagConstraints(); +// +// /* Voice settings */ +// c.gridx = 0; +// c.gridy = row; +// c.gridwidth = 1; +// c.fill = GridBagConstraints.NONE; +// c.anchor = GridBagConstraints.WEST; +// pane.add(new JLabel("Voice"), c); +// +// enable_voice = new JRadioButton("Enable", AltosUIPreferences.voice()); +// enable_voice.addActionListener(new ActionListener() { +// public void actionPerformed(ActionEvent e) { +// JRadioButton item = (JRadioButton) e.getSource(); +// boolean enabled = item.isSelected(); +// AltosUIPreferences.set_voice(enabled); +// if (enabled) +// voice.speak_always("Enable voice."); +// else +// voice.speak_always("Disable voice."); +// } +// }); +// c.gridx = 1; +// c.gridy = row; +// c.gridwidth = 1; +// c.weightx = 1; +// c.fill = GridBagConstraints.NONE; +// c.anchor = GridBagConstraints.WEST; +// pane.add(enable_voice, c); +// enable_voice.setToolTipText("Enable/Disable all audio in-flight announcements"); +// +// c.gridx = 2; +// c.gridy = row++; +// c.gridwidth = 1; +// c.weightx = 1; +// c.fill = GridBagConstraints.NONE; +// c.anchor = GridBagConstraints.EAST; +// test_voice = new JButton("Test Voice"); +// test_voice.addActionListener(new ActionListener() { +// public void actionPerformed(ActionEvent e) { +// voice.speak("That's one small step for man; one giant leap for mankind."); +// } +// }); +// pane.add(test_voice, c); +// test_voice.setToolTipText("Play a stock audio clip to check volume"); +// row++; + } + + public void add_log_dir() { + /* Log directory settings */ + pane.add(new JLabel("Log Directory"), constraints(0, 1)); + + configure_log = new JButton(AltosUIPreferences.logdir().getPath()); + configure_log.addActionListener(new ActionListener() { + public void actionPerformed(ActionEvent e) { + AltosUIPreferences.ConfigureLog(); + configure_log.setText(AltosUIPreferences.logdir().getPath()); + } + }); + pane.add(configure_log, constraints(1, 2)); + configure_log.setToolTipText("Which directory flight logs are stored in"); + row++; + } + + public void add_callsign() { +// /* Callsign setting */ +// pane.add(new JLabel("Callsign"), constraints(0, 1)); +// +// callsign_value = new JTextField(AltosUIPreferences.callsign()); +// callsign_value.getDocument().addDocumentListener(this); +// callsign_value.setToolTipText("Callsign sent in packet mode"); +// pane.add(callsign_value, constraints(1, 2, GridBagConstraints.BOTH)); +// row++; + } + + public void add_units() { + /* Imperial units setting */ + pane.add(new JLabel("Imperial Units"), constraints(0, 1)); + + imperial_units = new JRadioButton("Enable", AltosUIPreferences.imperial_units()); + imperial_units.addActionListener(new ActionListener() { + public void actionPerformed(ActionEvent e) { + JRadioButton item = (JRadioButton) e.getSource(); + boolean enabled = item.isSelected(); + AltosUIPreferences.set_imperial_units(enabled); + } + }); + imperial_units.setToolTipText("Use Imperial units instead of metric"); + pane.add(imperial_units, constraints(1, 2)); + row++; + } + + public void add_font_size() { + /* Font size setting */ + pane.add(new JLabel("Font size"), constraints(0, 1)); + + font_size_value = new JComboBox(font_size_names); + int font_size = AltosUIPreferences.font_size(); + font_size_value.setSelectedIndex(font_size - AltosUILib.font_size_small); + font_size_value.addActionListener(new ActionListener() { + public void actionPerformed(ActionEvent e) { + int size = font_size_value.getSelectedIndex() + AltosUILib.font_size_small; + + AltosUIPreferences.set_font_size(size); + } + }); + pane.add(font_size_value, constraints(1, 2, GridBagConstraints.BOTH)); + font_size_value.setToolTipText("Font size used in telemetry window"); + row++; + } + + public void add_look_and_feel() { + /* Look & Feel setting */ + pane.add(new JLabel("Look & feel"), constraints(0, 1)); + + /* + class LookAndFeelRenderer extends BasicComboBoxRenderer implements ListCellRenderer { + + public LookAndFeelRenderer() { + super(); + } + + public Component getListCellRendererComponent( + JList list, + Object value, + int index, + boolean isSelected, + boolean cellHasFocus) + { + super.getListCellRendererComponent(list, value, index, isSelected, cellHasFocus); + setText(((UIManager.LookAndFeelInfo) value).getName()); + return this; + } + } + */ + + final UIManager.LookAndFeelInfo[] look_and_feels = UIManager.getInstalledLookAndFeels(); + + look_and_feel_value = new JComboBox(look_and_feels); + + DelegatingRenderer.install(look_and_feel_value); + + String look_and_feel = AltosUIPreferences.look_and_feel(); + for (int i = 0; i < look_and_feels.length; i++) + if (look_and_feel.equals(look_and_feels[i].getClassName())) + look_and_feel_value.setSelectedIndex(i); + + look_and_feel_value.addActionListener(new ActionListener() { + public void actionPerformed(ActionEvent e) { + int id = look_and_feel_value.getSelectedIndex(); + + AltosUIPreferences.set_look_and_feel(look_and_feels[id].getClassName()); + } + }); + pane.add(look_and_feel_value, constraints(1, 2, GridBagConstraints.BOTH)); + look_and_feel_value.setToolTipText("Look&feel used for new windows"); + row++; + } + + public void add_serial_debug() { + GridBagConstraints c = new GridBagConstraints(); + + /* Serial debug setting */ + pane.add(new JLabel("Serial Debug"), constraints(0, 1)); + + serial_debug = new JRadioButton("Enable", AltosUIPreferences.serial_debug()); + serial_debug.addActionListener(new ActionListener() { + public void actionPerformed(ActionEvent e) { + JRadioButton item = (JRadioButton) e.getSource(); + boolean enabled = item.isSelected(); + AltosUIPreferences.set_serial_debug(enabled); + } + }); + serial_debug.setToolTipText("Enable/Disable USB I/O getting sent to the console"); + } + + public void add_bluetooth() { +// GridBagConstraints c = new GridBagConstraints(); +// c.gridx = 1; +// c.gridy = row++; +// c.gridwidth = 3; +// c.fill = GridBagConstraints.NONE; +// c.anchor = GridBagConstraints.WEST; +// pane.add(serial_debug, c); +// +// manage_bluetooth = new JButton("Manage Bluetooth"); +// manage_bluetooth.addActionListener(new ActionListener() { +// public void actionPerformed(ActionEvent e) { +// AltosBTManage.show(owner, AltosBTKnown.bt_known()); +// } +// }); +// c.gridx = 0; +// c.gridy = row; +// c.gridwidth = 2; +// c.fill = GridBagConstraints.NONE; +// c.anchor = GridBagConstraints.WEST; +// pane.add(manage_bluetooth, c); + } + + public void add_frequencies() { +// GridBagConstraints c = new GridBagConstraints(); +// manage_frequencies = new JButton("Manage Frequencies"); +// manage_frequencies.addActionListener(new ActionListener() { +// public void actionPerformed(ActionEvent e) { +// AltosConfigFreqUI.show(owner); +// } +// }); +// manage_frequencies.setToolTipText("Configure which values are shown in frequency menus"); +// c.gridx = 2; +// c.gridx = 2; +// c.gridy = row++; +// c.gridwidth = 2; +// c.fill = GridBagConstraints.NONE; +// c.anchor = GridBagConstraints.WEST; +// pane.add(manage_frequencies, c); + } + + public AltosConfigureUI(JFrame in_owner) { + super(in_owner, "Configure AltosUI", false); + + owner = in_owner; + pane = getContentPane(); + pane.setLayout(new GridBagLayout()); + + row = 0; + + /* Nice label at the top */ + pane.add(new JLabel ("Configure AltOS UI"), + constraints(0, 3)); + row++; + + pane.add(new JLabel (String.format("AltOS version %s", AltosUIVersion.version)), + constraints(0, 3)); + row++; + + add_voice(); + add_log_dir(); + add_callsign(); + add_units(); + add_font_size(); + add_look_and_feel(); + add_bluetooth(); + add_frequencies(); + + /* And a close button at the bottom */ + close = new JButton("Close"); + close.addActionListener(new ActionListener() { + public void actionPerformed(ActionEvent e) { + setVisible(false); + } + }); + pane.add(close, constraints(0, 3)); + + pack(); + setLocationRelativeTo(owner); + setVisible(true); + } +} diff --git a/altosuilib/AltosFontListener.java b/altosuilib/AltosFontListener.java new file mode 100644 index 00000000..ef543264 --- /dev/null +++ b/altosuilib/AltosFontListener.java @@ -0,0 +1,22 @@ +/* + * Copyright © 2011 Keith Packard + * + * 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. + */ + +package org.altusmetrum.altosuilib; + +public interface AltosFontListener { + void font_size_changed(int font_size); +} diff --git a/altosuilib/AltosUIDialog.java b/altosuilib/AltosUIDialog.java new file mode 100644 index 00000000..c0c33ba6 --- /dev/null +++ b/altosuilib/AltosUIDialog.java @@ -0,0 +1,59 @@ +/* + * Copyright © 2011 Keith Packard + * + * 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. + */ + +package org.altusmetrum.altosuilib; + +import java.awt.*; +import java.awt.event.*; +import javax.swing.*; + +class AltosUIDialogListener extends WindowAdapter { + public void windowClosing (WindowEvent e) { + AltosUIPreferences.unregister_ui_listener((AltosUIDialog) e.getWindow()); + } +} + +public class AltosUIDialog extends JDialog implements AltosUIListener { + + public void ui_changed(String look_and_feel) { + SwingUtilities.updateComponentTreeUI(this); + this.pack(); + } + + public AltosUIDialog() { + AltosUIPreferences.register_ui_listener(this); + addWindowListener(new AltosUIDialogListener()); + } + + public AltosUIDialog(Frame frame, String label, boolean modal) { + super(frame, label, modal); + AltosUIPreferences.register_ui_listener(this); + addWindowListener(new AltosUIDialogListener()); + } + + public AltosUIDialog(Dialog dialog, String label, boolean modal) { + super(dialog, label, modal); + AltosUIPreferences.register_ui_listener(this); + addWindowListener(new AltosUIDialogListener()); + } + + public AltosUIDialog(Frame frame, boolean modal) { + super(frame, modal); + AltosUIPreferences.register_ui_listener(this); + addWindowListener(new AltosUIDialogListener()); + } +} diff --git a/altosuilib/AltosUIFrame.java b/altosuilib/AltosUIFrame.java new file mode 100644 index 00000000..409aea2e --- /dev/null +++ b/altosuilib/AltosUIFrame.java @@ -0,0 +1,82 @@ +/* + * Copyright © 2011 Keith Packard + * + * 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. + */ + +package org.altusmetrum.altosuilib; + +import java.awt.*; +import java.awt.event.*; +import javax.swing.*; +import java.util.*; + +class AltosUIFrameListener extends WindowAdapter { + public void windowClosing (WindowEvent e) { + AltosUIPreferences.unregister_ui_listener((AltosUIFrame) e.getWindow()); + } +} + +public class AltosUIFrame extends JFrame implements AltosUIListener { + + public void ui_changed(String look_and_feel) { + SwingUtilities.updateComponentTreeUI(this); + this.pack(); + } + + static String[] altos_icon_names = { + "/altus-metrum-16.png", + "/altus-metrum-32.png", + "/altus-metrum-48.png", + "/altus-metrum-64.png", + "/altus-metrum-128.png", + "/altus-metrum-256.png" + }; + + static public String[] icon_names; + + static public void set_icon_names(String[] new_icon_names) { icon_names = new_icon_names; } + + public String[] icon_names() { + if (icon_names == null) + set_icon_names(altos_icon_names); + return icon_names; + } + + public void set_icon() { + ArrayList icons = new ArrayList(); + String[] icon_names = icon_names(); + + for (int i = 0; i < icon_names.length; i++) { + java.net.URL imgURL = AltosUIFrame.class.getResource(icon_names[i]); + if (imgURL != null) + icons.add(new ImageIcon(imgURL).getImage()); + } + setIconImages(icons); + } + + + public AltosUIFrame() { + AltosUIPreferences.register_ui_listener(this); + addWindowListener(new AltosUIFrameListener()); + set_icon(); + } + + public AltosUIFrame(String name) { + super(name); + AltosUIPreferences.register_ui_listener(this); + addWindowListener(new AltosUIFrameListener()); + set_icon(); + } +} diff --git a/altosuilib/AltosUILib.java b/altosuilib/AltosUILib.java new file mode 100644 index 00000000..5d5f9aaa --- /dev/null +++ b/altosuilib/AltosUILib.java @@ -0,0 +1,93 @@ +/* + * Copyright © 2010 Keith Packard + * + * 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. + */ + +package org.altusmetrum.altosuilib; + +import java.awt.*; +import libaltosJNI.*; + +import org.altusmetrum.AltosLib.*; + +public class AltosUILib extends AltosLib { + + public static final int tab_elt_pad = 5; + + public static Font label_font; + public static Font value_font; + public static Font status_font; + public static Font table_label_font; + public static Font table_value_font; + + final public static int font_size_small = 1; + final public static int font_size_medium = 2; + final public static int font_size_large = 3; + + static void set_fonts(int size) { + int brief_size; + int table_size; + int status_size; + + switch (size) { + case font_size_small: + brief_size = 16; + status_size = 18; + table_size = 11; + break; + default: + case font_size_medium: + brief_size = 22; + status_size = 24; + table_size = 14; + break; + case font_size_large: + brief_size = 26; + status_size = 30; + table_size = 17; + break; + } + label_font = new Font("Dialog", Font.PLAIN, brief_size); + value_font = new Font("Monospaced", Font.PLAIN, brief_size); + status_font = new Font("SansSerif", Font.BOLD, status_size); + table_label_font = new Font("SansSerif", Font.PLAIN, table_size); + table_value_font = new Font("Monospaced", Font.PLAIN, table_size); + } + + static final int text_width = 20; + + static public boolean initialized = false; + static public boolean loaded_library = false; + + public static boolean load_library() { + if (!initialized) { + try { + System.loadLibrary("altos"); + libaltos.altos_init(); + loaded_library = true; + } catch (UnsatisfiedLinkError e) { + try { + System.loadLibrary("altos64"); + libaltos.altos_init(); + loaded_library = true; + } catch (UnsatisfiedLinkError e2) { + loaded_library = false; + } + } + initialized = true; + } + return loaded_library; + } +} diff --git a/altosuilib/AltosUIListener.java b/altosuilib/AltosUIListener.java new file mode 100644 index 00000000..f4127f58 --- /dev/null +++ b/altosuilib/AltosUIListener.java @@ -0,0 +1,22 @@ +/* + * Copyright © 2011 Keith Packard + * + * 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. + */ + +package org.altusmetrum.altosuilib; + +public interface AltosUIListener { + public void ui_changed(String look_and_feel); +} diff --git a/altosuilib/AltosUIPreferences.java b/altosuilib/AltosUIPreferences.java new file mode 100644 index 00000000..485cb582 --- /dev/null +++ b/altosuilib/AltosUIPreferences.java @@ -0,0 +1,180 @@ +/* + * Copyright © 2011 Keith Packard + * + * 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. + */ + +package org.altusmetrum.altosuilib; + +import java.io.*; +import java.util.*; +import java.awt.Component; +import javax.swing.*; +import org.altusmetrum.AltosLib.*; + +public class AltosUIPreferences extends AltosPreferences { + + /* font size preferences name */ + final static String fontSizePreference = "FONT-SIZE"; + + /* Look&Feel preference name */ + final static String lookAndFeelPreference = "LOOK-AND-FEEL"; + + /* UI Component to pop dialogs up */ + static Component component; + + static LinkedList font_listeners; + + static int font_size = AltosUILib.font_size_medium; + + static LinkedList ui_listeners; + + static String look_and_feel = null; + + /* Serial debug */ + public static boolean serial_debug; + + public static void init() { + AltosPreferences.init(new AltosUIPreferencesBackend()); + + font_listeners = new LinkedList(); + + font_size = backend.getInt(fontSizePreference, AltosUILib.font_size_medium); + AltosUILib.set_fonts(font_size); + look_and_feel = backend.getString(lookAndFeelPreference, UIManager.getSystemLookAndFeelClassName()); + + ui_listeners = new LinkedList(); + serial_debug = backend.getBoolean(serialDebugPreference, false); + AltosLink.set_debug(serial_debug); + } + + static { init(); } + + public static void set_component(Component in_component) { + component = in_component; + } + + private static boolean check_dir(File dir) { + if (!dir.exists()) { + if (!dir.mkdirs()) { + JOptionPane.showMessageDialog(component, + dir.getName(), + "Cannot create directory", + JOptionPane.ERROR_MESSAGE); + return false; + } + } else if (!dir.isDirectory()) { + JOptionPane.showMessageDialog(component, + dir.getName(), + "Is not a directory", + JOptionPane.ERROR_MESSAGE); + return false; + } + return true; + } + + /* Configure the log directory. This is where all telemetry and eeprom files + * will be written to, and where replay will look for telemetry files + */ + public static void ConfigureLog() { + JFileChooser logdir_chooser = new JFileChooser(logdir.getParentFile()); + + logdir_chooser.setDialogTitle("Configure Data Logging Directory"); + logdir_chooser.setFileSelectionMode(JFileChooser.DIRECTORIES_ONLY); + + if (logdir_chooser.showDialog(component, "Select Directory") == JFileChooser.APPROVE_OPTION) { + File dir = logdir_chooser.getSelectedFile(); + if (check_dir(dir)) + set_logdir(dir); + } + } + public static int font_size() { + synchronized (backend) { + return font_size; + } + } + + static void set_fonts() { + } + + public static void set_font_size(int new_font_size) { + synchronized (backend) { + font_size = new_font_size; + backend.putInt(fontSizePreference, font_size); + flush_preferences(); + AltosUILib.set_fonts(font_size); + for (AltosFontListener l : font_listeners) + l.font_size_changed(font_size); + } + } + + public static void register_font_listener(AltosFontListener l) { + synchronized (backend) { + font_listeners.add(l); + } + } + + public static void unregister_font_listener(AltosFontListener l) { + synchronized (backend) { + font_listeners.remove(l); + } + } + + public static void set_look_and_feel(String new_look_and_feel) { + try { + UIManager.setLookAndFeel(new_look_and_feel); + } catch (Exception e) { + } + synchronized(backend) { + look_and_feel = new_look_and_feel; + backend.putString(lookAndFeelPreference, look_and_feel); + flush_preferences(); + for (AltosUIListener l : ui_listeners) + l.ui_changed(look_and_feel); + } + } + + public static String look_and_feel() { + synchronized (backend) { + return look_and_feel; + } + } + + public static void register_ui_listener(AltosUIListener l) { + synchronized(backend) { + ui_listeners.add(l); + } + } + + public static void unregister_ui_listener(AltosUIListener l) { + synchronized (backend) { + ui_listeners.remove(l); + } + } + public static void set_serial_debug(boolean new_serial_debug) { + AltosLink.set_debug(new_serial_debug); + synchronized (backend) { + serial_debug = new_serial_debug; + backend.putBoolean(serialDebugPreference, serial_debug); + flush_preferences(); + } + } + + public static boolean serial_debug() { + synchronized (backend) { + return serial_debug; + } + } + +} diff --git a/altosuilib/AltosUIPreferencesBackend.java b/altosuilib/AltosUIPreferencesBackend.java new file mode 100644 index 00000000..c6c05e55 --- /dev/null +++ b/altosuilib/AltosUIPreferencesBackend.java @@ -0,0 +1,101 @@ +/* + * Copyright © 2012 Mike Beattie + * + * 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. + */ + +package org.altusmetrum.altosuilib; + +import java.io.File; +import java.util.prefs.*; +import org.altusmetrum.AltosLib.*; +import javax.swing.filechooser.FileSystemView; + +public class AltosUIPreferencesBackend implements AltosPreferencesBackend { + + private Preferences _preferences = null; + + public AltosUIPreferencesBackend() { + _preferences = Preferences.userRoot().node("/org/altusmetrum/altosui"); + } + + public AltosUIPreferencesBackend(Preferences in_preferences) { + _preferences = in_preferences; + } + + public String getString(String key, String def) { + return _preferences.get(key, def); + } + public void putString(String key, String value) { + _preferences.put(key, value); + } + + public int getInt(String key, int def) { + return _preferences.getInt(key, def); + } + public void putInt(String key, int value) { + _preferences.putInt(key, value); + } + + public double getDouble(String key, double def) { + return _preferences.getDouble(key, def); + } + public void putDouble(String key, double value) { + _preferences.putDouble(key, value); + } + + public boolean getBoolean(String key, boolean def) { + return _preferences.getBoolean(key, def); + } + public void putBoolean(String key, boolean value) { + _preferences.putBoolean(key, value); + } + + public boolean nodeExists(String key) { + try { + return _preferences.nodeExists(key); + } catch (BackingStoreException be) { + return false; + } + } + + public AltosPreferencesBackend node(String key) { + return new AltosUIPreferencesBackend(_preferences.node(key)); + } + + public String[] keys() { + try { + return _preferences.keys(); + } catch (BackingStoreException be) { + return null; + } + } + + public void remove(String key) { + _preferences.remove(key); + } + + public void flush() { + try { + _preferences.flush(); + } catch (BackingStoreException ee) { + System.err.printf("Cannot save preferences\n"); + } + } + + public File homeDirectory() { + /* Use the file system view default directory */ + return FileSystemView.getFileSystemView().getDefaultDirectory(); + } +} diff --git a/altosuilib/AltosUIVersion.java.in b/altosuilib/AltosUIVersion.java.in new file mode 100644 index 00000000..6fb3b38b --- /dev/null +++ b/altosuilib/AltosUIVersion.java.in @@ -0,0 +1,22 @@ +/* + * Copyright © 2011 Keith Packard + * + * 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. + */ + +package org.altusmetrum.altosuilib; + +public class AltosUIVersion { + public final static String version = "@VERSION@"; +} diff --git a/altosuilib/AltosUnitsListener.java b/altosuilib/AltosUnitsListener.java new file mode 100644 index 00000000..22c66cd4 --- /dev/null +++ b/altosuilib/AltosUnitsListener.java @@ -0,0 +1,22 @@ +/* + * Copyright © 2012 Keith Packard + * + * 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. + */ + +package org.altusmetrum.altosuilib; + +public interface AltosUnitsListener { + public void units_changed(); +} diff --git a/altosuilib/Makefile.am b/altosuilib/Makefile.am new file mode 100644 index 00000000..26aee7c4 --- /dev/null +++ b/altosuilib/Makefile.am @@ -0,0 +1,41 @@ +AM_JAVACFLAGS=-encoding UTF-8 -Xlint:deprecation + +JAVAROOT=bin + +CLASSPATH_ENV=mkdir -p $(JAVAROOT); CLASSPATH="bin:../altoslib/*:../libaltos:$(FREETTS)/*:/usr/share/java/*" + +SRC=. +BIN=bin/org/altusmetrum/AltosUILib + +AltosUILibdir = $(datadir)/java + +AltosUILib_JAVA = \ + AltosConfigureUI.java \ + AltosFontListener.java \ + AltosUIDialog.java \ + AltosUIFrame.java \ + AltosUILib.java \ + AltosUIListener.java \ + AltosUIPreferencesBackend.java \ + AltosUIPreferences.java \ + AltosUIVersion.java \ + AltosUnitsListener.java + +JAR=AltosUILib.jar + +all-local: $(JAR) + +clean-local: + -rm -rf bin $(JAR) + +install-AltosUILibJAVA: $(JAR) + @$(NORMAL_INSTALL) + test -z "$(AltosUILibdir)" || $(MKDIR_P) "$(DESTDIR)$(AltosUILibdir)" + echo " $(INSTALL_DATA)" "$<" "'$(DESTDIR)$(AltosUILibdir)/$(JAR)"; \ + $(INSTALL_DATA) "$<" "$(DESTDIR)$(AltosUILibdir)" + +bin: + mkdir -p bin + +$(JAR): classAltosUILib.stamp + jar cf $@ -C bin org diff --git a/configure.ac b/configure.ac index 0fcd97e2..6ad2591c 100644 --- a/configure.ac +++ b/configure.ac @@ -146,10 +146,13 @@ AM_CONDITIONAL([LIBSTLINK], [test x$HAVE_STLINK != xno]) AC_OUTPUT([ Makefile altoslib/Makefile +altosuilib/Makefile +altosuilib/AltosUIVersion.java altosui/Makefile altosui/AltosVersion.java altosui/Info.plist -altosui/libaltos/Makefile +libaltos/Makefile +micropeak/Makefile altosdroid/Makefile altosdroid/local.properties ao-tools/Makefile diff --git a/icon/micropeak-128.png b/icon/micropeak-128.png new file mode 100644 index 00000000..f045dc6a Binary files /dev/null and b/icon/micropeak-128.png differ diff --git a/icon/micropeak-16.png b/icon/micropeak-16.png new file mode 100644 index 00000000..d8140802 Binary files /dev/null and b/icon/micropeak-16.png differ diff --git a/icon/micropeak-256.png b/icon/micropeak-256.png new file mode 100644 index 00000000..b96d4706 Binary files /dev/null and b/icon/micropeak-256.png differ diff --git a/icon/micropeak-32.png b/icon/micropeak-32.png new file mode 100644 index 00000000..d34c5c12 Binary files /dev/null and b/icon/micropeak-32.png differ diff --git a/icon/micropeak-48.png b/icon/micropeak-48.png new file mode 100644 index 00000000..86dc4f7f Binary files /dev/null and b/icon/micropeak-48.png differ diff --git a/icon/micropeak-64.png b/icon/micropeak-64.png new file mode 100644 index 00000000..6ca7c2eb Binary files /dev/null and b/icon/micropeak-64.png differ diff --git a/libaltos/.gitignore b/libaltos/.gitignore new file mode 100644 index 00000000..c490e6f8 --- /dev/null +++ b/libaltos/.gitignore @@ -0,0 +1,12 @@ +*.so +*.lo +*.la +*.java +*.class +.libs/ +classlibaltos.stamp +libaltos_wrap.c +libaltosJNI +cjnitest +libaltos.swig +swig_bindings/ diff --git a/libaltos/Makefile-standalone b/libaltos/Makefile-standalone new file mode 100644 index 00000000..4e438050 --- /dev/null +++ b/libaltos/Makefile-standalone @@ -0,0 +1,126 @@ +OS:=$(shell uname) + +# +# Linux +# +ifeq ($(OS),Linux) + +JAVA_CFLAGS=-I/usr/lib/jvm/java-6-openjdk/include + +OS_LIB_CFLAGS=-DLINUX -DPOSIX_TTY $(JAVA_CFLAGS) + +OS_APP_CFLAGS=$(OS_LIB_CFLAGS) + +OS_LDFLAGS= + +LIBNAME=libaltos.so +EXEEXT= +endif + +# +# Darwin (Mac OS X) +# +ifeq ($(OS),Darwin) + +OS_LIB_CFLAGS=\ + -DDARWIN -DPOSIX_TTY -arch i386 -arch x86_64 \ + --sysroot=/Developer/SDKs/MacOSX10.5.sdk -mmacosx-version-min=10.5 \ + -iwithsysroot /System/Library/Frameworks/JavaVM.framework/Headers \ + -iwithsysroot /System/Library/Frameworks/IOKit.framework/Headers \ + -iwithsysroot /System/Library/Frameworks/CoreFoundation.framework/Headers +OS_APP_CFLAGS=$(OS_LIB_CFLAGS) + +OS_LDFLAGS =\ + -framework IOKit -framework CoreFoundation + +LIBNAME=libaltos.dylib +EXEEXT= + +endif + +# +# Windows +# +ifneq (,$(findstring MINGW,$(OS))) + +CC=gcc + +OS_LIB_CFLAGS = -DWINDOWS -mconsole -DBUILD_DLL +OS_APP_CFLAGS = -DWINDOWS -mconsole + +OS_LDFLAGS = -lgdi32 -luser32 -lcfgmgr32 -lsetupapi -lole32 \ + -ladvapi32 -lcomctl32 -mconsole -Wl,--add-stdcall-alias + +LIBNAME=altos.dll + +EXEEXT=.exe + +endif + +.SUFFIXES: .java .class + +CLASSPATH=".:jnitest/*:libaltosJNI:/usr/share/java/*" + +SWIG_DIR=swig_bindings/java +SWIG_FILE=$(SWIG_DIR)/libaltos.swig +SWIG_WRAP=$(SWIG_DIR)/libaltos_wrap.c + +JNI_DIR=libaltosJNI +JNI_FILE=$(JNI_DIR)/libaltosJNI.java +JNI_SRCS=$(JNI_FILE) \ + $(JNI_DIR)/SWIGTYPE_p_altos_file.java \ + $(JNI_DIR)/SWIGTYPE_p_altos_list.java \ + $(JNI_DIR)/altos_device.java \ + $(JNI_DIR)/libaltos.java + +JAVAFILES=\ + $(JNI_SRCS) + +CLASSFILES = $(JAVAFILES:%.java=%.class) + +JAVAFLAGS=-Xlint:unchecked + +CJNITEST=cjnitest$(EXEEXT) + +all: $(LIBNAME) $(CJNITEST) $(CLASSFILES) + +.java.class: + javac -encoding UTF8 -classpath "$(CLASSPATH)" $(JAVAFLAGS) $*.java + +CFLAGS=$(OS_LIB_CFLAGS) -O -I. + +LDFLAGS=$(OS_LDFLAGS) + +HEADERS=libaltos.h +SRCS = libaltos.c $(SWIG_WRAP) +OBJS = $(SRCS:%.c=%.o) +LIBS = $(DARWIN_LIBS) + +$(CJNITEST): cjnitest.c $(LIBNAME) + $(CC) -o $@ $(OS_APP_CFLAGS) cjnitest.c $(LIBNAME) $(LIBS) $(LDFLAGS) + +$(LIBNAME): $(OBJS) + $(CC) -shared $(CFLAGS) -o $@ $(OBJS) $(LIBS) $(LDFLAGS) + +clean: + rm -f $(CLASSFILES) $(OBJS) $(LIBNAME) $(CJNITEST) cjnitest.o + rm -rf swig_bindings libaltosJNI + +distclean: clean + +$(JNI_FILE): libaltos.i0 $(HEADERS) + mkdir -p $(SWIG_DIR) + mkdir -p libaltosJNI + sed 's;//%;%;' libaltos.i0 $(HEADERS) > $(SWIG_FILE) + swig -java -package libaltosJNI $(SWIG_FILE) + cp swig_bindings/java/*.java libaltosJNI + +$(SWIG_WRAP): $(JNI_FILE) + +ifeq ($(OS),Linux) +install: $(LIBNAME) + install -c $(LIBNAME) $(DESTDIR)/usr/lib/altos/$(LIBNAME) + +endif + +.NOTPARALLEL: diff --git a/libaltos/Makefile.am b/libaltos/Makefile.am new file mode 100644 index 00000000..b5ab1ddb --- /dev/null +++ b/libaltos/Makefile.am @@ -0,0 +1,54 @@ +JAVAC=javac +AM_CFLAGS=-DLINUX -DPOSIX_TTY -I$(JVM_INCLUDE) +AM_JAVACFLAGS=-encoding UTF-8 + +altoslibdir=$(libdir)/altos + +altoslib_LTLIBRARIES=libaltos.la + +libaltos_la_LDFLAGS = -version-info 1:0:1 + +libaltos_la_SOURCES=\ + libaltos.c \ + libaltos_wrap.c + +noinst_PROGRAMS=cjnitest + +cjnitest_LDADD=libaltos.la + +LIBS=-lbluetooth + +HFILES=libaltos.h + +SWIG_FILE=libaltos.swig + +CLASSDIR=libaltosJNI + +$(SWIG_FILE): libaltos.i0 $(HFILES) + sed 's;//%;%;' libaltos.i0 $(HFILES) > $(SWIG_FILE) + +all-local: classlibaltos.stamp + +libaltos_wrap.c: classlibaltos.stamp + +classlibaltos.stamp: $(SWIG_FILE) + swig -java -package libaltosJNI $(SWIG_FILE) + mkdir -p libaltosJNI + $(JAVAC) -d . $(AM_JAVACFLAGS) $(JAVACFLAGS) *.java && \ + touch classlibaltos.stamp + +MINGCC32=i686-w64-mingw32-gcc +MINGCC64=x86_64-w64-mingw32-gcc +MINGFLAGS=-Wall -DWINDOWS -DBUILD_DLL -I$(JVM_INCLUDE) +MINGLIBS=-lsetupapi + +fat: altos.dll altos64.dll + +altos.dll: $(libaltos_la_SOURCES) + $(MINGCC32) -o $@ $(MINGFLAGS) -shared $(libaltos_la_SOURCES) $(MINGLIBS) + +altos64.dll: $(libaltos_la_SOURCES) + $(MINGCC64) -o $@ $(MINGFLAGS) -shared $(libaltos_la_SOURCES) $(MINGLIBS) + +clean-local: + -rm -rf libaltosJNI *.class *.java classlibaltos.stamp $(SWIG_FILE) libaltos_wrap.c altos.dll altos64.dll diff --git a/libaltos/cjnitest.c b/libaltos/cjnitest.c new file mode 100644 index 00000000..f0fe78f7 --- /dev/null +++ b/libaltos/cjnitest.c @@ -0,0 +1,71 @@ +#include +#include "libaltos.h" + +static void +altos_puts(struct altos_file *file, char *string) +{ + char c; + + while ((c = *string++)) + altos_putchar(file, c); +} + +main () +{ + struct altos_device device; + struct altos_list *list; + struct altos_bt_device bt_device; + struct altos_bt_list *bt_list; + + altos_init(); + list = altos_list_start(); + while (altos_list_next(list, &device)) { + struct altos_file *file; + int c; + + printf ("%04x:%04x %-20s %4d %s\n", device.vendor, device.product, + device.name, device.serial, device.path); + + file = altos_open(&device); + if (!file) { + printf("altos_open failed\n"); + continue; + } + altos_puts(file,"v\nc s\n"); + altos_flush(file); + while ((c = altos_getchar(file, 100)) >= 0) { + putchar (c); + } + if (c != LIBALTOS_TIMEOUT) + printf ("getchar returns %d\n", c); + altos_close(file); + } + altos_list_finish(list); +#if HAS_BLUETOOTH + bt_list = altos_bt_list_start(8); + while (altos_bt_list_next(bt_list, &bt_device)) { + printf ("%s %s\n", bt_device.name, bt_device.addr); + if (strncmp(bt_device.name, "TeleBT", 6) == 0) { + struct altos_file *file; + + int c; + file = altos_bt_open(&bt_device); + if (!file) { + printf("altos_bt_open failed\n"); + continue; + } + altos_puts(file,"v\nc s\n"); + altos_flush(file); + while ((c = altos_getchar(file, 100)) >= 0) { + putchar(c); + } + if (c != LIBALTOS_TIMEOUT) + printf("getchar returns %d\n", c); + altos_close(file); + } + } + altos_bt_list_finish(bt_list); +#endif + altos_fini(); + return 0; +} diff --git a/libaltos/libaltos.c b/libaltos/libaltos.c new file mode 100644 index 00000000..d7b266cf --- /dev/null +++ b/libaltos/libaltos.c @@ -0,0 +1,1316 @@ +/* + * Copyright © 2010 Keith Packard + * + * 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. + */ + +#include "libaltos.h" +#include +#include +#include + +#define BLUETOOTH_PRODUCT_TELEBT "TeleBT" + +#define USE_POLL + +PUBLIC int +altos_init(void) +{ + return LIBALTOS_SUCCESS; +} + +PUBLIC void +altos_fini(void) +{ +} + +static struct altos_error last_error; + +static void +altos_set_last_error(int code, char *string) +{ + last_error.code = code; + strncpy(last_error.string, string, sizeof (last_error.string) -1); + last_error.string[sizeof(last_error.string)-1] = '\0'; +} + +PUBLIC void +altos_get_last_error(struct altos_error *error) +{ + *error = last_error; +} + +#ifdef DARWIN + +#undef USE_POLL + +/* Mac OS X don't have strndup even if _GNU_SOURCE is defined */ +static char * +altos_strndup (const char *s, size_t n) +{ + size_t len = strlen (s); + char *ret; + + if (len <= n) + return strdup (s); + ret = malloc(n + 1); + strncpy(ret, s, n); + ret[n] = '\0'; + return ret; +} + +#else +#define altos_strndup strndup +#endif + +#ifdef POSIX_TTY + +#include +#include +#include +#include +#include + +#define USB_BUF_SIZE 64 + +struct altos_file { + int fd; +#ifdef USE_POLL + int pipe[2]; +#else + int out_fd; +#endif + unsigned char out_data[USB_BUF_SIZE]; + int out_used; + unsigned char in_data[USB_BUF_SIZE]; + int in_used; + int in_read; +}; + +static void +altos_set_last_posix_error(void) +{ + altos_set_last_error(errno, strerror(errno)); +} + +PUBLIC struct altos_file * +altos_open(struct altos_device *device) +{ + struct altos_file *file = calloc (sizeof (struct altos_file), 1); + int ret; + struct termios term; + + if (!file) { + altos_set_last_posix_error(); + return NULL; + } + +// altos_set_last_error(12, "yeah yeah, failed again"); +// free(file); +// return NULL; + + file->fd = open(device->path, O_RDWR | O_NOCTTY); + if (file->fd < 0) { + altos_set_last_posix_error(); + free(file); + return NULL; + } +#ifdef USE_POLL + pipe(file->pipe); +#else + file->out_fd = open(device->path, O_RDWR | O_NOCTTY); + if (file->out_fd < 0) { + altos_set_last_posix_error(); + close(file->fd); + free(file); + return NULL; + } +#endif + ret = tcgetattr(file->fd, &term); + if (ret < 0) { + altos_set_last_posix_error(); + close(file->fd); +#ifndef USE_POLL + close(file->out_fd); +#endif + free(file); + return NULL; + } + cfmakeraw(&term); +#ifdef USE_POLL + term.c_cc[VMIN] = 1; + term.c_cc[VTIME] = 0; +#else + term.c_cc[VMIN] = 0; + term.c_cc[VTIME] = 1; +#endif + ret = tcsetattr(file->fd, TCSAFLUSH, &term); + if (ret < 0) { + altos_set_last_posix_error(); + close(file->fd); +#ifndef USE_POLL + close(file->out_fd); +#endif + free(file); + return NULL; + } + return file; +} + +PUBLIC void +altos_close(struct altos_file *file) +{ + if (file->fd != -1) { + int fd = file->fd; + file->fd = -1; +#ifdef USE_POLL + write(file->pipe[1], "\r", 1); +#else + close(file->out_fd); + file->out_fd = -1; +#endif + close(fd); + } +} + +PUBLIC void +altos_free(struct altos_file *file) +{ + altos_close(file); + free(file); +} + +PUBLIC int +altos_flush(struct altos_file *file) +{ + if (file->out_used && 0) { + printf ("flush \""); + fwrite(file->out_data, 1, file->out_used, stdout); + printf ("\"\n"); + } + while (file->out_used) { + int ret; + + if (file->fd < 0) + return -EBADF; +#ifdef USE_POLL + ret = write (file->fd, file->out_data, file->out_used); +#else + ret = write (file->out_fd, file->out_data, file->out_used); +#endif + if (ret < 0) { + altos_set_last_posix_error(); + return -last_error.code; + } + if (ret) { + memmove(file->out_data, file->out_data + ret, + file->out_used - ret); + file->out_used -= ret; + } + } + return 0; +} + +PUBLIC int +altos_putchar(struct altos_file *file, char c) +{ + int ret; + + if (file->out_used == USB_BUF_SIZE) { + ret = altos_flush(file); + if (ret) { + return ret; + } + } + file->out_data[file->out_used++] = c; + ret = 0; + if (file->out_used == USB_BUF_SIZE) + ret = altos_flush(file); + return ret; +} + +#ifdef USE_POLL +#include +#endif + +static int +altos_fill(struct altos_file *file, int timeout) +{ + int ret; +#ifdef USE_POLL + struct pollfd fd[2]; +#endif + + if (timeout == 0) + timeout = -1; + while (file->in_read == file->in_used) { + if (file->fd < 0) + return LIBALTOS_ERROR; +#ifdef USE_POLL + fd[0].fd = file->fd; + fd[0].events = POLLIN|POLLERR|POLLHUP|POLLNVAL; + fd[1].fd = file->pipe[0]; + fd[1].events = POLLIN; + ret = poll(fd, 2, timeout); + if (ret < 0) { + altos_set_last_posix_error(); + return LIBALTOS_ERROR; + } + if (ret == 0) + return LIBALTOS_TIMEOUT; + + if (fd[0].revents & (POLLHUP|POLLERR|POLLNVAL)) + return LIBALTOS_ERROR; + if (fd[0].revents & POLLIN) +#endif + { + ret = read(file->fd, file->in_data, USB_BUF_SIZE); + if (ret < 0) { + altos_set_last_posix_error(); + return LIBALTOS_ERROR; + } + file->in_read = 0; + file->in_used = ret; +#ifndef USE_POLL + if (ret == 0 && timeout > 0) + return LIBALTOS_TIMEOUT; +#endif + } + } + if (file->in_used && 0) { + printf ("fill \""); + fwrite(file->in_data, 1, file->in_used, stdout); + printf ("\"\n"); + } + return 0; +} + +PUBLIC int +altos_getchar(struct altos_file *file, int timeout) +{ + int ret; + while (file->in_read == file->in_used) { + if (file->fd < 0) + return LIBALTOS_ERROR; + ret = altos_fill(file, timeout); + if (ret) + return ret; + } + return file->in_data[file->in_read++]; +} + +#endif /* POSIX_TTY */ + +/* + * Scan for Altus Metrum devices by looking through /sys + */ + +#ifdef LINUX + +#define _GNU_SOURCE +#include +#include +#include +#include +#include +#include +#include +#include +#include + +static char * +cc_fullname (char *dir, char *file) +{ + char *new; + int dlen = strlen (dir); + int flen = strlen (file); + int slen = 0; + + if (dir[dlen-1] != '/') + slen = 1; + new = malloc (dlen + slen + flen + 1); + if (!new) + return 0; + strcpy(new, dir); + if (slen) + strcat (new, "/"); + strcat(new, file); + return new; +} + +static char * +cc_basename(char *file) +{ + char *b; + + b = strrchr(file, '/'); + if (!b) + return file; + return b + 1; +} + +static char * +load_string(char *dir, char *file) +{ + char *full = cc_fullname(dir, file); + char line[4096]; + char *r; + FILE *f; + int rlen; + + f = fopen(full, "r"); + free(full); + if (!f) + return NULL; + r = fgets(line, sizeof (line), f); + fclose(f); + if (!r) + return NULL; + rlen = strlen(r); + if (r[rlen-1] == '\n') + r[rlen-1] = '\0'; + return strdup(r); +} + +static int +load_hex(char *dir, char *file) +{ + char *line; + char *end; + long i; + + line = load_string(dir, file); + if (!line) + return -1; + i = strtol(line, &end, 16); + free(line); + if (end == line) + return -1; + return i; +} + +static int +load_dec(char *dir, char *file) +{ + char *line; + char *end; + long i; + + line = load_string(dir, file); + if (!line) + return -1; + i = strtol(line, &end, 10); + free(line); + if (end == line) + return -1; + return i; +} + +static int +dir_filter_tty_colon(const struct dirent *d) +{ + return strncmp(d->d_name, "tty:", 4) == 0; +} + +static int +dir_filter_tty(const struct dirent *d) +{ + return strncmp(d->d_name, "tty", 3) == 0; +} + +struct altos_usbdev { + char *sys; + char *tty; + char *manufacturer; + char *product_name; + int serial; /* AltOS always uses simple integer serial numbers */ + int idProduct; + int idVendor; +}; + +static char * +usb_tty(char *sys) +{ + char *base; + int num_configs; + int config; + struct dirent **namelist; + int interface; + int num_interfaces; + char endpoint_base[20]; + char *endpoint_full; + char *tty_dir; + int ntty; + char *tty; + + base = cc_basename(sys); + num_configs = load_hex(sys, "bNumConfigurations"); + num_interfaces = load_hex(sys, "bNumInterfaces"); + for (config = 1; config <= num_configs; config++) { + for (interface = 0; interface < num_interfaces; interface++) { + sprintf(endpoint_base, "%s:%d.%d", + base, config, interface); + endpoint_full = cc_fullname(sys, endpoint_base); + + + /* Check for tty:ttyACMx style names + */ + ntty = scandir(endpoint_full, &namelist, + dir_filter_tty_colon, + alphasort); + if (ntty > 0) { + free(endpoint_full); + tty = cc_fullname("/dev", namelist[0]->d_name + 4); + free(namelist); + return tty; + } + + /* Check for ttyACMx style names + */ + ntty = scandir(endpoint_full, &namelist, + dir_filter_tty, + alphasort); + if (ntty > 0) { + free(endpoint_full); + tty = cc_fullname("/dev", namelist[0]->d_name); + free(namelist); + return tty; + } + + /* Check for tty/ttyACMx style names + */ + tty_dir = cc_fullname(endpoint_full, "tty"); + free(endpoint_full); + ntty = scandir(tty_dir, &namelist, + dir_filter_tty, + alphasort); + free (tty_dir); + if (ntty > 0) { + tty = cc_fullname("/dev", namelist[0]->d_name); + free(namelist); + return tty; + } + + } + } + return NULL; +} + +static struct altos_usbdev * +usb_scan_device(char *sys) +{ + struct altos_usbdev *usbdev; + char *tty; + + tty = usb_tty(sys); + if (!tty) + return NULL; + usbdev = calloc(1, sizeof (struct altos_usbdev)); + if (!usbdev) + return NULL; + usbdev->sys = strdup(sys); + usbdev->manufacturer = load_string(sys, "manufacturer"); + usbdev->product_name = load_string(sys, "product"); + usbdev->serial = load_dec(sys, "serial"); + usbdev->idProduct = load_hex(sys, "idProduct"); + usbdev->idVendor = load_hex(sys, "idVendor"); + usbdev->tty = tty; + return usbdev; +} + +static void +usbdev_free(struct altos_usbdev *usbdev) +{ + free(usbdev->sys); + free(usbdev->manufacturer); + free(usbdev->product_name); + /* this can get used as a return value */ + if (usbdev->tty) + free(usbdev->tty); + free(usbdev); +} + +#define USB_DEVICES "/sys/bus/usb/devices" + +static int +dir_filter_dev(const struct dirent *d) +{ + const char *n = d->d_name; + char c; + + while ((c = *n++)) { + if (isdigit(c)) + continue; + if (c == '-') + continue; + if (c == '.' && n != d->d_name + 1) + continue; + return 0; + } + return 1; +} + +struct altos_list { + struct altos_usbdev **dev; + int current; + int ndev; +}; + +struct altos_list * +altos_list_start(void) +{ + int e; + struct dirent **ents; + char *dir; + struct altos_usbdev *dev; + struct altos_list *devs; + int n; + + devs = calloc(1, sizeof (struct altos_list)); + if (!devs) + return NULL; + + n = scandir (USB_DEVICES, &ents, + dir_filter_dev, + alphasort); + if (!n) + return 0; + for (e = 0; e < n; e++) { + dir = cc_fullname(USB_DEVICES, ents[e]->d_name); + dev = usb_scan_device(dir); + if (!dev) + continue; + free(dir); + if (devs->dev) + devs->dev = realloc(devs->dev, + (devs->ndev + 1) * sizeof (struct usbdev *)); + else + devs->dev = malloc (sizeof (struct usbdev *)); + devs->dev[devs->ndev++] = dev; + } + free(ents); + devs->current = 0; + return devs; +} + +int +altos_list_next(struct altos_list *list, struct altos_device *device) +{ + struct altos_usbdev *dev; + if (list->current >= list->ndev) { + return 0; + } + dev = list->dev[list->current]; + strcpy(device->name, dev->product_name); + device->vendor = dev->idVendor; + device->product = dev->idProduct; + strcpy(device->path, dev->tty); + device->serial = dev->serial; + list->current++; + return 1; +} + +void +altos_list_finish(struct altos_list *usbdevs) +{ + int i; + + if (!usbdevs) + return; + for (i = 0; i < usbdevs->ndev; i++) + usbdev_free(usbdevs->dev[i]); + free(usbdevs); +} + +struct altos_bt_list { + inquiry_info *ii; + int sock; + int dev_id; + int rsp; + int num_rsp; +}; + +#define INQUIRY_MAX_RSP 255 + +struct altos_bt_list * +altos_bt_list_start(int inquiry_time) +{ + struct altos_bt_list *bt_list; + + bt_list = calloc(1, sizeof (struct altos_bt_list)); + if (!bt_list) + goto no_bt_list; + + bt_list->ii = calloc(INQUIRY_MAX_RSP, sizeof (inquiry_info)); + if (!bt_list->ii) + goto no_ii; + bt_list->dev_id = hci_get_route(NULL); + if (bt_list->dev_id < 0) + goto no_dev_id; + + bt_list->sock = hci_open_dev(bt_list->dev_id); + if (bt_list->sock < 0) + goto no_sock; + + bt_list->num_rsp = hci_inquiry(bt_list->dev_id, + inquiry_time, + INQUIRY_MAX_RSP, + NULL, + &bt_list->ii, + IREQ_CACHE_FLUSH); + if (bt_list->num_rsp < 0) + goto no_rsp; + + bt_list->rsp = 0; + return bt_list; + +no_rsp: + close(bt_list->sock); +no_sock: +no_dev_id: + free(bt_list->ii); +no_ii: + free(bt_list); +no_bt_list: + return NULL; +} + +int +altos_bt_list_next(struct altos_bt_list *bt_list, + struct altos_bt_device *device) +{ + inquiry_info *ii; + + if (bt_list->rsp >= bt_list->num_rsp) + return 0; + + ii = &bt_list->ii[bt_list->rsp]; + ba2str(&ii->bdaddr, device->addr); + memset(&device->name, '\0', sizeof (device->name)); + if (hci_read_remote_name(bt_list->sock, &ii->bdaddr, + sizeof (device->name), + device->name, 0) < 0) { + strcpy(device->name, "[unknown]"); + } + bt_list->rsp++; + return 1; +} + +void +altos_bt_list_finish(struct altos_bt_list *bt_list) +{ + close(bt_list->sock); + free(bt_list->ii); + free(bt_list); +} + +void +altos_bt_fill_in(char *name, char *addr, struct altos_bt_device *device) +{ + strncpy(device->name, name, sizeof (device->name)); + device->name[sizeof(device->name)-1] = '\0'; + strncpy(device->addr, addr, sizeof (device->addr)); + device->addr[sizeof(device->addr)-1] = '\0'; +} + +struct altos_file * +altos_bt_open(struct altos_bt_device *device) +{ + struct sockaddr_rc addr = { 0 }; + int s, status; + struct altos_file *file; + + file = calloc(1, sizeof (struct altos_file)); + if (!file) + goto no_file; + file->fd = socket(AF_BLUETOOTH, SOCK_STREAM, BTPROTO_RFCOMM); + if (file->fd < 0) { + altos_set_last_posix_error(); + goto no_sock; + } + + addr.rc_family = AF_BLUETOOTH; + addr.rc_channel = 1; + str2ba(device->addr, &addr.rc_bdaddr); + + status = connect(file->fd, + (struct sockaddr *)&addr, + sizeof(addr)); + if (status < 0) { + altos_set_last_posix_error(); + goto no_link; + } + sleep(1); + +#ifdef USE_POLL + pipe(file->pipe); +#else + file->out_fd = dup(file->fd); +#endif + return file; +no_link: + close(s); +no_sock: + free(file); +no_file: + return NULL; +} + +#endif + +#ifdef DARWIN + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +struct altos_list { + io_iterator_t iterator; +}; + +static int +get_string(io_object_t object, CFStringRef entry, char *result, int result_len) +{ + CFTypeRef entry_as_string; + Boolean got_string; + + entry_as_string = IORegistryEntrySearchCFProperty (object, + kIOServicePlane, + entry, + kCFAllocatorDefault, + kIORegistryIterateRecursively); + if (entry_as_string) { + got_string = CFStringGetCString(entry_as_string, + result, result_len, + kCFStringEncodingASCII); + + CFRelease(entry_as_string); + if (got_string) + return 1; + } + return 0; +} + +static int +get_number(io_object_t object, CFStringRef entry, int *result) +{ + CFTypeRef entry_as_number; + Boolean got_number; + + entry_as_number = IORegistryEntrySearchCFProperty (object, + kIOServicePlane, + entry, + kCFAllocatorDefault, + kIORegistryIterateRecursively); + if (entry_as_number) { + got_number = CFNumberGetValue(entry_as_number, + kCFNumberIntType, + result); + if (got_number) + return 1; + } + return 0; +} + +PUBLIC struct altos_list * +altos_list_start(void) +{ + struct altos_list *list = calloc (sizeof (struct altos_list), 1); + CFMutableDictionaryRef matching_dictionary = IOServiceMatching("IOUSBDevice"); + io_iterator_t tdIterator; + io_object_t tdObject; + kern_return_t ret; + int i; + + ret = IOServiceGetMatchingServices(kIOMasterPortDefault, matching_dictionary, &list->iterator); + if (ret != kIOReturnSuccess) + return NULL; + return list; +} + +PUBLIC int +altos_list_next(struct altos_list *list, struct altos_device *device) +{ + io_object_t object; + char serial_string[128]; + + for (;;) { + object = IOIteratorNext(list->iterator); + if (!object) + return 0; + + if (!get_number (object, CFSTR(kUSBVendorID), &device->vendor) || + !get_number (object, CFSTR(kUSBProductID), &device->product)) + continue; + if (device->vendor != 0xfffe) + continue; + if (device->product < 0x000a || 0x0013 < device->product) + continue; + if (get_string (object, CFSTR("IOCalloutDevice"), device->path, sizeof (device->path)) && + get_string (object, CFSTR("USB Product Name"), device->name, sizeof (device->name)) && + get_string (object, CFSTR("USB Serial Number"), serial_string, sizeof (serial_string))) { + device->serial = atoi(serial_string); + return 1; + } + } +} + +PUBLIC void +altos_list_finish(struct altos_list *list) +{ + IOObjectRelease (list->iterator); + free(list); +} + +struct altos_bt_list { + int sock; + int dev_id; + int rsp; + int num_rsp; +}; + +#define INQUIRY_MAX_RSP 255 + +struct altos_bt_list * +altos_bt_list_start(int inquiry_time) +{ + return NULL; +} + +int +altos_bt_list_next(struct altos_bt_list *bt_list, + struct altos_bt_device *device) +{ + return 0; +} + +void +altos_bt_list_finish(struct altos_bt_list *bt_list) +{ +} + +void +altos_bt_fill_in(char *name, char *addr, struct altos_bt_device *device) +{ + strncpy(device->name, name, sizeof (device->name)); + device->name[sizeof(device->name)-1] = '\0'; + strncpy(device->addr, addr, sizeof (device->addr)); + device->addr[sizeof(device->addr)-1] = '\0'; +} + +struct altos_file * +altos_bt_open(struct altos_bt_device *device) +{ + return NULL; +} + +#endif + + +#ifdef WINDOWS + +#include +#include +#include + +struct altos_list { + HDEVINFO dev_info; + int index; +}; + +#define USB_BUF_SIZE 64 + +struct altos_file { + HANDLE handle; + unsigned char out_data[USB_BUF_SIZE]; + int out_used; + unsigned char in_data[USB_BUF_SIZE]; + int in_used; + int in_read; + OVERLAPPED ov_read; + BOOL pend_read; + OVERLAPPED ov_write; +}; + +static void +_altos_set_last_windows_error(char *file, int line) +{ + DWORD error = GetLastError(); + TCHAR message[1024]; + FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM, + 0, + error, + 0, + message, + sizeof (message) / sizeof (TCHAR), + NULL); + if (error != ERROR_SUCCESS) + printf ("%s:%d %s\n", file, line, message); + altos_set_last_error(error, message); +} + +#define altos_set_last_windows_error() _altos_set_last_windows_error(__FILE__, __LINE__) + +PUBLIC struct altos_list * +altos_list_start(void) +{ + struct altos_list *list = calloc(1, sizeof (struct altos_list)); + + if (!list) + return NULL; + list->dev_info = SetupDiGetClassDevs(NULL, "USB", NULL, + DIGCF_ALLCLASSES|DIGCF_PRESENT); + if (list->dev_info == INVALID_HANDLE_VALUE) { + altos_set_last_windows_error(); + free(list); + return NULL; + } + list->index = 0; + return list; +} + +PUBLIC int +altos_list_next(struct altos_list *list, struct altos_device *device) +{ + SP_DEVINFO_DATA dev_info_data; + BYTE port[128]; + DWORD port_len; + char friendlyname[256]; + BYTE symbolic[256]; + DWORD symbolic_len; + HKEY dev_key; + unsigned int vid, pid; + int serial; + HRESULT result; + DWORD friendlyname_type; + DWORD friendlyname_len; + + dev_info_data.cbSize = sizeof (SP_DEVINFO_DATA); + while(SetupDiEnumDeviceInfo(list->dev_info, list->index, + &dev_info_data)) + { + list->index++; + + dev_key = SetupDiOpenDevRegKey(list->dev_info, &dev_info_data, + DICS_FLAG_GLOBAL, 0, DIREG_DEV, + KEY_READ); + if (dev_key == INVALID_HANDLE_VALUE) { + altos_set_last_windows_error(); + printf("cannot open device registry key\n"); + continue; + } + + /* Fetch symbolic name for this device and parse out + * the vid/pid/serial info */ + symbolic_len = sizeof(symbolic); + result = RegQueryValueEx(dev_key, "SymbolicName", NULL, NULL, + symbolic, &symbolic_len); + if (result != 0) { + altos_set_last_windows_error(); + printf("cannot find SymbolicName value\n"); + RegCloseKey(dev_key); + continue; + } + vid = pid = serial = 0; + sscanf((char *) symbolic + sizeof("\\??\\USB#VID_") - 1, + "%04X", &vid); + sscanf((char *) symbolic + sizeof("\\??\\USB#VID_XXXX&PID_") - 1, + "%04X", &pid); + sscanf((char *) symbolic + sizeof("\\??\\USB#VID_XXXX&PID_XXXX#") - 1, + "%d", &serial); + + /* Fetch the com port name */ + port_len = sizeof (port); + result = RegQueryValueEx(dev_key, "PortName", NULL, NULL, + port, &port_len); + RegCloseKey(dev_key); + if (result != 0) { + altos_set_last_windows_error(); + printf("failed to get PortName\n"); + continue; + } + + /* Fetch the device description which is the device name, + * with firmware that has unique USB ids */ + friendlyname_len = sizeof (friendlyname); + if(!SetupDiGetDeviceRegistryProperty(list->dev_info, + &dev_info_data, + SPDRP_FRIENDLYNAME, + &friendlyname_type, + (BYTE *)friendlyname, + sizeof(friendlyname), + &friendlyname_len)) + { + altos_set_last_windows_error(); + printf("Failed to get friendlyname\n"); + continue; + } + device->vendor = vid; + device->product = pid; + device->serial = serial; + strcpy(device->name, friendlyname); + + strcpy(device->path, (char *) port); + return 1; + } + result = GetLastError(); + if (result != ERROR_NO_MORE_ITEMS) { + altos_set_last_windows_error(); + printf ("SetupDiEnumDeviceInfo failed error %d\n", (int) result); + } + return 0; +} + +PUBLIC void +altos_list_finish(struct altos_list *list) +{ + SetupDiDestroyDeviceInfoList(list->dev_info); + free(list); +} + +static int +altos_queue_read(struct altos_file *file) +{ + DWORD got; + if (file->pend_read) + return LIBALTOS_SUCCESS; + + if (!ReadFile(file->handle, file->in_data, USB_BUF_SIZE, &got, &file->ov_read)) { + if (GetLastError() != ERROR_IO_PENDING) { + altos_set_last_windows_error(); + return LIBALTOS_ERROR; + } + file->pend_read = TRUE; + } else { + file->pend_read = FALSE; + file->in_read = 0; + file->in_used = got; + } + return LIBALTOS_SUCCESS; +} + +static int +altos_wait_read(struct altos_file *file, int timeout) +{ + DWORD ret; + DWORD got; + + if (!file->pend_read) + return LIBALTOS_SUCCESS; + + if (!timeout) + timeout = INFINITE; + + ret = WaitForSingleObject(file->ov_read.hEvent, timeout); + switch (ret) { + case WAIT_OBJECT_0: + if (!GetOverlappedResult(file->handle, &file->ov_read, &got, FALSE)) { + altos_set_last_windows_error(); + return LIBALTOS_ERROR; + } + file->pend_read = FALSE; + file->in_read = 0; + file->in_used = got; + break; + case WAIT_TIMEOUT: + return LIBALTOS_TIMEOUT; + break; + default: + return LIBALTOS_ERROR; + } + return LIBALTOS_SUCCESS; +} + +static int +altos_fill(struct altos_file *file, int timeout) +{ + int ret; + + if (file->in_read < file->in_used) + return LIBALTOS_SUCCESS; + + file->in_read = file->in_used = 0; + + ret = altos_queue_read(file); + if (ret) + return ret; + ret = altos_wait_read(file, timeout); + if (ret) + return ret; + + return LIBALTOS_SUCCESS; +} + +PUBLIC int +altos_flush(struct altos_file *file) +{ + DWORD put; + unsigned char *data = file->out_data; + int used = file->out_used; + DWORD ret; + + while (used) { + if (!WriteFile(file->handle, data, used, &put, &file->ov_write)) { + if (GetLastError() != ERROR_IO_PENDING) { + altos_set_last_windows_error(); + printf ("\tflush write error\n"); + return LIBALTOS_ERROR; + } + ret = WaitForSingleObject(file->ov_write.hEvent, INFINITE); + switch (ret) { + case WAIT_OBJECT_0: + if (!GetOverlappedResult(file->handle, &file->ov_write, &put, FALSE)) { + altos_set_last_windows_error(); + printf ("\tflush result error\n"); + return LIBALTOS_ERROR; + } + break; + default: + altos_set_last_windows_error(); + printf ("\tflush wait error\n"); + return LIBALTOS_ERROR; + } + } + data += put; + used -= put; + } + file->out_used = 0; + return LIBALTOS_SUCCESS; +} + +PUBLIC struct altos_file * +altos_open(struct altos_device *device) +{ + struct altos_file *file = calloc (1, sizeof (struct altos_file)); + char full_name[64]; + COMMTIMEOUTS timeouts; + + if (!file) + return NULL; + + strcpy(full_name, "\\\\.\\"); + strcat(full_name, device->path); + file->handle = CreateFile(full_name, GENERIC_READ|GENERIC_WRITE, + 0, NULL, OPEN_EXISTING, + FILE_FLAG_OVERLAPPED, NULL); + if (file->handle == INVALID_HANDLE_VALUE) { + altos_set_last_windows_error(); + printf ("cannot open %s\n", full_name); + free(file); + return NULL; + } + file->ov_read.hEvent = CreateEvent(NULL, TRUE, FALSE, NULL); + file->ov_write.hEvent = CreateEvent(NULL, TRUE, FALSE, NULL); + + timeouts.ReadIntervalTimeout = MAXDWORD; + timeouts.ReadTotalTimeoutMultiplier = MAXDWORD; + timeouts.ReadTotalTimeoutConstant = 1 << 30; /* almost forever */ + timeouts.WriteTotalTimeoutMultiplier = 0; + timeouts.WriteTotalTimeoutConstant = 0; + SetCommTimeouts(file->handle, &timeouts); + + return file; +} + +PUBLIC void +altos_close(struct altos_file *file) +{ + if (file->handle != INVALID_HANDLE_VALUE) { + CloseHandle(file->handle); + file->handle = INVALID_HANDLE_VALUE; + SetEvent(file->ov_read.hEvent); + SetEvent(file->ov_write.hEvent); + CloseHandle(file->ov_read.hEvent); + CloseHandle(file->ov_write.hEvent); + } +} + +PUBLIC void +altos_free(struct altos_file *file) +{ + altos_close(file); + free(file); +} + +PUBLIC int +altos_putchar(struct altos_file *file, char c) +{ + int ret; + + if (file->out_used == USB_BUF_SIZE) { + ret = altos_flush(file); + if (ret) + return ret; + } + file->out_data[file->out_used++] = c; + if (file->out_used == USB_BUF_SIZE) + return altos_flush(file); + return LIBALTOS_SUCCESS; +} + +PUBLIC int +altos_getchar(struct altos_file *file, int timeout) +{ + int ret; + while (file->in_read == file->in_used) { + if (file->handle == INVALID_HANDLE_VALUE) + return LIBALTOS_ERROR; + ret = altos_fill(file, timeout); + if (ret) + return ret; + } + return file->in_data[file->in_read++]; +} + +struct altos_bt_list * +altos_bt_list_start(int inquiry_time) +{ + return NULL; +} + +int +altos_bt_list_next(struct altos_bt_list *bt_list, + struct altos_bt_device *device) +{ + return 0; +} + +void +altos_bt_list_finish(struct altos_bt_list *bt_list) +{ + free(bt_list); +} + +void +altos_bt_fill_in(char *name, char *addr, struct altos_bt_device *device) +{ + strncpy(device->name, name, sizeof (device->name)); + device->name[sizeof(device->name)-1] = '\0'; + strncpy(device->addr, addr, sizeof (device->addr)); + device->addr[sizeof(device->addr)-1] = '\0'; +} + +struct altos_file * +altos_bt_open(struct altos_bt_device *device) +{ + return NULL; +} + +#endif diff --git a/libaltos/libaltos.dylib b/libaltos/libaltos.dylib new file mode 100755 index 00000000..1038817d Binary files /dev/null and b/libaltos/libaltos.dylib differ diff --git a/libaltos/libaltos.h b/libaltos/libaltos.h new file mode 100644 index 00000000..f90fbb87 --- /dev/null +++ b/libaltos/libaltos.h @@ -0,0 +1,119 @@ +/* + * Copyright © 2010 Keith Packard + * + * 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. + */ + +#ifndef _LIBALTOS_H_ +#define _LIBALTOS_H_ + +#include + +#if defined(_WIN32) || defined(__WIN32__) || defined(__CYGWIN__) +# ifndef BUILD_STATIC +# ifdef BUILD_DLL +# define PUBLIC __declspec(dllexport) +# else +# define PUBLIC __declspec(dllimport) +# endif +# endif /* BUILD_STATIC */ +#endif + +#ifndef PUBLIC +# define PUBLIC +#endif + +struct altos_device { + //%immutable; + int vendor; + int product; + int serial; + char name[256]; + char path[256]; + //%mutable; +}; + +struct altos_bt_device { + //%immutable; + char name[256]; + char addr[20]; + //%mutable; +}; + +struct altos_error { + int code; + char string[1024]; +}; + +#define LIBALTOS_SUCCESS 0 +#define LIBALTOS_ERROR -1 +#define LIBALTOS_TIMEOUT -2 + +/* Returns 0 for success, < 0 on error */ +PUBLIC int +altos_init(void); + +PUBLIC void +altos_fini(void); + +PUBLIC void +altos_get_last_error(struct altos_error *error); + +PUBLIC struct altos_list * +altos_list_start(void); + +/* Returns 1 for success, zero on end of list */ +PUBLIC int +altos_list_next(struct altos_list *list, struct altos_device *device); + +PUBLIC void +altos_list_finish(struct altos_list *list); + +PUBLIC struct altos_file * +altos_open(struct altos_device *device); + +PUBLIC void +altos_close(struct altos_file *file); + +PUBLIC void +altos_free(struct altos_file *file); + +/* Returns < 0 for error */ +PUBLIC int +altos_putchar(struct altos_file *file, char c); + +/* Returns < 0 for error */ +PUBLIC int +altos_flush(struct altos_file *file); + +/* Returns < 0 for error or timeout. timeout of 0 == wait forever */ +PUBLIC int +altos_getchar(struct altos_file *file, int timeout); + +PUBLIC struct altos_bt_list * +altos_bt_list_start(int inquiry_time); + +PUBLIC int +altos_bt_list_next(struct altos_bt_list *list, struct altos_bt_device *device); + +PUBLIC void +altos_bt_list_finish(struct altos_bt_list *list); + +PUBLIC void +altos_bt_fill_in(char *name, char *addr, struct altos_bt_device *device); + +PUBLIC struct altos_file * +altos_bt_open(struct altos_bt_device *device); + +#endif /* _LIBALTOS_H_ */ diff --git a/libaltos/libaltos.i0 b/libaltos/libaltos.i0 new file mode 100644 index 00000000..d06468f5 --- /dev/null +++ b/libaltos/libaltos.i0 @@ -0,0 +1,5 @@ +%module libaltos +%{ +#include "libaltos.h" +%} + diff --git a/micropeak/.gitignore b/micropeak/.gitignore new file mode 100644 index 00000000..fc99b31c --- /dev/null +++ b/micropeak/.gitignore @@ -0,0 +1,6 @@ +*.jar +Manifest.txt +classes +*.stamp +micropeak +micropeak-test diff --git a/micropeak/Makefile.am b/micropeak/Makefile.am new file mode 100644 index 00000000..e0de690c --- /dev/null +++ b/micropeak/Makefile.am @@ -0,0 +1,146 @@ +JAVAROOT=classes +AM_JAVACFLAGS=-encoding UTF-8 -Xlint:deprecation + +CLASSPATH_ENV=mkdir -p $(JAVAROOT); CLASSPATH=".:classes:../altoslib/*:../altosuilib/*:../libaltos:$(JCOMMON)/jcommon.jar:$(JFREECHART)/jfreechart.jar" + +bin_SCRIPTS=micropeak + +micropeakdir=$(datadir)/java + +micropeak_JAVA= \ + MicroPeak.java \ + MicroData.java \ + MicroFrame.java \ + MicroGraph.java \ + MicroSerial.java \ + MicroStats.java \ + MicroStatsTable.java \ + MicroFileChooser.java \ + MicroUSB.java + +JFREECHART_CLASS= \ + jfreechart.jar + +JCOMMON_CLASS=\ + jcommon.jar + +JAR=micropeak.jar + +FATJAR=micropeak-fat.jar + +LIBALTOS= \ + libaltos.so \ + libaltos.dylib \ + altos.dll + +ALTOSLIB_CLASS=\ + AltosLib.jar + +ALTOSUILIB_CLASS=\ + AltosUILib.jar + +# Icons +ICONDIR=$(top_srcdir)/icon + +JAVA_ICONS=\ + $(ICONDIR)/micropeak-16.png \ + $(ICONDIR)/micropeak-32.png \ + $(ICONDIR)/micropeak-48.png \ + $(ICONDIR)/micropeak-64.png \ + $(ICONDIR)/micropeak-128.png \ + $(ICONDIR)/micropeak-256.png + +# icon base names for jar +ICONJAR= -C $(ICONDIR) micropeak-16.png \ + -C $(ICONDIR) micropeak-32.png \ + -C $(ICONDIR) micropeak-48.png \ + -C $(ICONDIR) micropeak-64.png \ + -C $(ICONDIR) micropeak-128.png \ + -C $(ICONDIR) micropeak-256.png + +all-local: micropeak-test $(JAR) + +clean-local: + -rm -rf classes $(JAR) $(FATJAR) \ + $(ALTOSLIB_CLASS) \ + $(ALTOSUILIB_CLASS) \ + $(JFREECHART_CLASS) $(JCOMMON_CLASS) $(LIBALTOS) Manifest.txt \ + micropeak micropeak-test macosx linux windows + +micropeak: Makefile + echo "#!/bin/sh" > $@ + echo 'exec java -cp "$(JCOMMON)/jcommon.jar:$(JFREECHART)/jfreechart.jar" -Djava.library.path="$(altoslibdir)" -jar "$(micropeakdir)/micropeak.jar" "$$@"' >> $@ + chmod +x $@ + +micropeak-test: Makefile + echo "#!/bin/sh" > $@ + echo 'exec java -cp "./*:../libaltos/*:$(JCOMMON)/jcommon.jar:$(JFREECHART)/jfreechart.jar" -Djava.library.path="../libaltos/.libs" -jar micropeak.jar "$$@"' >> $@ + chmod +x $@ + +$(JAR): classmicropeak.stamp Manifest.txt $(JAVA_ICONS) $(ALTOSLIB_CLASS) $(ALTOSUILIB_CLASS) + jar cfm $@ Manifest.txt \ + $(ICONJAR) \ + -C classes org \ + -C ../libaltos libaltosJNI + +$(FATJAR): classmicropeak.stamp Manifest-fat.txt $(ALTOSLIB_CLASS) $(ALTOSUILIB_CLASS) $(JFREECHART_CLASS) $(JCOMMON_CLASS) $(JAVA_ICONS) + jar cfm $@ Manifest-fat.txt \ + $(ICONJAR) \ + -C classes org \ + -C ../libaltos libaltosJNI + + +libaltos.so: build-libaltos + -rm -f "$@" + $(LN_S) ../libaltos/.libs/"$@" . + +libaltos.dylib: + -rm -f "$@" + $(LN_S) ../libaltos/"$@" . + +altos.dll: ../libaltos/altos.dll + -rm -f "$@" + $(LN_S) ../libaltos/"$@" . + +altos64.dll: ../libaltos/altos64.dll + -rm -f "$@" + $(LN_S) ../libaltos/"$@" . + +../libaltos/.libs/libaltos.so: build-libaltos + +../libaltos/altos.dll: build-altos-dll + +../libaltos/altos64.dll: build-altos64-dll + +build-libaltos: + +cd ../libaltos && make libaltos.la +build-altos-dll: + +cd ../libaltos && make altos.dll + +build-altos64-dll: + +cd ../libaltos && make altos64.dll + +$(ALTOSLIB_CLASS): + -rm -f "$@" + $(LN_S) ../altoslib/"$@" . + +$(ALTOSUILIB_CLASS): + -rm -f "$@" + $(LN_S) ../altosuilib/"$@" . + +$(JFREECHART_CLASS): + -rm -f "$@" + $(LN_S) "$(JFREECHART)"/"$@" . + +$(JCOMMON_CLASS): + -rm -f "$@" + $(LN_S) "$(JCOMMON)"/"$@" . + +Manifest.txt: Makefile + echo 'Main-Class: org.altusmetrum.micropeak.MicroPeak' > $@ + echo "Class-Path: AltosLib.jar AltosUILib.jar $(JCOMMON)/jcommon.jar $(JFREECHART)/jfreechart.jar" >> $@ + +Manifest-fat.txt: + echo 'Main-Class: org.altusmetrum.micropeak.MicroPeak' > $@ + echo "Class-Path: AltosLib.jar AltosUILib.jar jcommon.jar jfreechart.jar" >> $@ + diff --git a/micropeak/MicroData.java b/micropeak/MicroData.java new file mode 100644 index 00000000..ec9b83d8 --- /dev/null +++ b/micropeak/MicroData.java @@ -0,0 +1,370 @@ +/* + * Copyright © 2012 Keith Packard + * + * 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. + */ + +package org.altusmetrum.micropeak; + +import java.lang.*; +import java.io.*; +import java.util.*; +import org.altusmetrum.AltosLib.*; + +abstract class MicroIterator implements Iterator { + int i; + MicroData data; + + public boolean hasNext() { + return i < data.pressures.length; + } + + public MicroIterator (MicroData data) { + this.data = data; + i = 0; + } + + public void remove() { + } +} + +class MicroHeightIterator extends MicroIterator { + public Double next() { + return data.height(i++); + } + + public MicroHeightIterator(MicroData data) { + super(data); + } +} + +class MicroHeightIterable implements Iterable { + MicroData data; + + public Iterator iterator() { + return new MicroHeightIterator(data); + } + + public MicroHeightIterable(MicroData data) { + this.data = data; + } +} + +class MicroSpeedIterator extends MicroIterator { + public Double next() { + return data.speed(i++); + } + public MicroSpeedIterator(MicroData data) { + super(data); + } +} + +class MicroSpeedIterable implements Iterable { + MicroData data; + + public Iterator iterator() { + return new MicroSpeedIterator(data); + } + + public MicroSpeedIterable(MicroData data) { + this.data = data; + } +} + +class MicroAccelIterator extends MicroIterator { + public Double next() { + return data.acceleration(i++); + } + public MicroAccelIterator(MicroData data) { + super(data); + } +} + +class MicroAccelIterable implements Iterable { + MicroData data; + + public Iterator iterator() { + return new MicroAccelIterator(data); + } + + public MicroAccelIterable(MicroData data) { + this.data = data; + } +} + +public class MicroData { + public int ground_pressure; + public int min_pressure; + public int[] pressures; + private double time_step; + private double ground_altitude; + private ArrayList bytes; + + + class FileEndedException extends Exception { + } + + class NonHexcharException extends Exception { + } + + class InvalidCrcException extends Exception { + } + + private int getc(InputStream f) throws IOException, FileEndedException { + int c = f.read(); + + if (c == -1) + throw new FileEndedException(); + bytes.add(c); + return c; + } + + private int get_nonwhite(InputStream f) throws IOException, FileEndedException { + int c; + + for (;;) { + c = getc(f); + if (!Character.isWhitespace(c)) + return c; + } + } + + private int get_hexc(InputStream f) throws IOException, FileEndedException, NonHexcharException { + int c = get_nonwhite(f); + + if ('0' <= c && c <= '9') + return c - '0'; + if ('a' <= c && c <= 'f') + return c - 'a' + 10; + if ('A' <= c && c <= 'F') + return c - 'A' + 10; + throw new NonHexcharException(); + } + + private static final int POLY = 0x8408; + + private int log_crc(int crc, int b) { + int i; + + for (i = 0; i < 8; i++) { + if (((crc & 0x0001) ^ (b & 0x0001)) != 0) + crc = (crc >> 1) ^ POLY; + else + crc = crc >> 1; + b >>= 1; + } + return crc & 0xffff; + } + + int file_crc; + + private int get_hex(InputStream f) throws IOException, FileEndedException, NonHexcharException { + int a = get_hexc(f); + int b = get_hexc(f); + + int h = (a << 4) + b; + + file_crc = log_crc(file_crc, h); + return h; + } + + private boolean find_header(InputStream f) throws IOException { + try { + for (;;) { + if (get_nonwhite(f) == 'M' && get_nonwhite(f) == 'P') + return true; + } + } catch (FileEndedException fe) { + return false; + } + } + + private int get_32(InputStream f) throws IOException, FileEndedException, NonHexcharException { + int v = 0; + for (int i = 0; i < 4; i++) { + v += get_hex(f) << (i * 8); + } + return v; + } + + private int get_16(InputStream f) throws IOException, FileEndedException, NonHexcharException { + int v = 0; + for (int i = 0; i < 2; i++) { + v += get_hex(f) << (i * 8); + } + return v; + } + + private int swap16(int i) { + return ((i << 8) & 0xff00) | ((i >> 8) & 0xff); + } + + public boolean crc_valid; + + int mix_in (int high, int low) { + return high - (high & 0xffff) + low; + } + + boolean closer (int target, int a, int b) { + return Math.abs (target - a) < Math.abs(target - b); + } + + public double altitude(int i) { + return AltosConvert.pressure_to_altitude(pressures[i]); + } + + public Iterable heights() { + return new MicroHeightIterable(this); + } + + public Iterable speeds() { + return new MicroSpeedIterable(this); + } + + public Iterable accels() { + return new MicroAccelIterable(this); + } + + int fact(int n) { + if (n == 0) + return 1; + return n * fact(n-1); + } + + int choose(int n, int k) { + return fact(n) / (fact(k) * fact(n-k)); + } + + + public double avg_altitude(int center, int dist) { + int start = center - dist; + int stop = center + dist; + + if (start < 0) + start = 0; + if (stop >= pressures.length) + stop = pressures.length - 1; + + double sum = 0; + double div = 0; + + int n = dist * 2; + + for (int i = start; i <= stop; i++) { + int k = i - (center - dist); + int c = choose (n, k); + + sum += c * pressures[i]; + div += c; + } + + double pres = sum / div; + + double alt = AltosConvert.pressure_to_altitude(pres); + return alt; + } + + public double height(int i) { + return altitude(i) - ground_altitude; + } + + static final int speed_avg = 3; + static final int accel_avg = 5; + + private double avg_speed(int center, int dist) { + if (center == 0) + return 0; + + double ai = avg_altitude(center, dist); + double aj = avg_altitude(center - 1, dist); + double s = (ai - aj) / time_step; + + return s; + } + + public double speed(int i) { + return avg_speed(i, speed_avg); + } + + public double acceleration(int i) { + if (i == 0) + return 0; + return (avg_speed(i, accel_avg) - avg_speed(i-1, accel_avg)) / time_step; + } + + public double time(int i) { + return i * time_step; + } + + public void save (OutputStream f) throws IOException { + for (int c : bytes) + f.write(c); + } + + public MicroData (InputStream f) throws IOException { + bytes = new ArrayList(); + if (!find_header(f)) + throw new IOException(); + try { + file_crc = 0xffff; + ground_pressure = get_32(f); + min_pressure = get_32(f); + int nsamples = get_16(f); + pressures = new int[nsamples + 1]; + + ground_altitude = AltosConvert.pressure_to_altitude(ground_pressure); + int cur = ground_pressure; + pressures[0] = cur; + for (int i = 0; i < nsamples; i++) { + int k = get_16(f); + int same = mix_in(cur, k); + int up = mix_in(cur + 0x10000, k); + int down = mix_in(cur - 0x10000, k); + + if (closer (cur, same, up)) { + if (closer (cur, same, down)) + cur = same; + else + cur = down; + } else { + if (closer (cur, up, down)) + cur = up; + else + cur = down; + } + + pressures[i+1] = cur; + } + + int current_crc = swap16(~file_crc & 0xffff); + int crc = get_16(f); + + crc_valid = crc == current_crc; + + time_step = 0.192; + } catch (FileEndedException fe) { + throw new IOException(); + } catch (NonHexcharException ne) { + throw new IOException(); + } + } + + public MicroData() { + ground_pressure = 101000; + min_pressure = 101000; + pressures = new int[1]; + pressures[0] = 101000; + } + +} diff --git a/micropeak/MicroFileChooser.java b/micropeak/MicroFileChooser.java new file mode 100644 index 00000000..d2540987 --- /dev/null +++ b/micropeak/MicroFileChooser.java @@ -0,0 +1,67 @@ +/* + * Copyright © 2012 Keith Packard + * + * 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. + */ + +package org.altusmetrum.micropeak; + +import javax.swing.*; +import javax.swing.filechooser.FileNameExtensionFilter; +import java.io.*; +import org.altusmetrum.AltosLib.*; +import org.altusmetrum.altosuilib.*; + +public class MicroFileChooser extends JFileChooser { + JFrame frame; + String filename; + File file; + + public String filename() { + return filename; + } + + public File file() { + return file; + } + + public InputStream runDialog() { + int ret; + + ret = showOpenDialog(frame); + if (ret == APPROVE_OPTION) { + file = getSelectedFile(); + if (file == null) + return null; + filename = file.getName(); + try { + return new FileInputStream(file); + } catch (FileNotFoundException fe) { + JOptionPane.showMessageDialog(frame, + fe.getMessage(), + "Cannot open file", + JOptionPane.ERROR_MESSAGE); + } + } + return null; + } + + public MicroFileChooser(JFrame in_frame) { + frame = in_frame; + setDialogTitle("Select MicroPeak Data File"); + setFileFilter(new FileNameExtensionFilter("MicroPeak data file", + "mpd")); + setCurrentDirectory(AltosUIPreferences.logdir()); + } +} diff --git a/micropeak/MicroFrame.java b/micropeak/MicroFrame.java new file mode 100644 index 00000000..03e3af0c --- /dev/null +++ b/micropeak/MicroFrame.java @@ -0,0 +1,37 @@ +/* + * Copyright © 2011 Keith Packard + * + * 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. + */ + +package org.altusmetrum.micropeak; + +import java.awt.*; +import java.awt.event.*; +import javax.swing.*; +import java.util.*; +import org.altusmetrum.altosuilib.*; + +public class MicroFrame extends AltosUIFrame { + static String[] micro_icon_names = { + "/micropeak-16.png", + "/micropeak-32.png", + "/micropeak-48.png", + "/micropeak-64.png", + "/micropeak-128.png", + "/micropeak-256.png" + }; + + static { set_icon_names(micro_icon_names); } +} diff --git a/micropeak/MicroGraph.java b/micropeak/MicroGraph.java new file mode 100644 index 00000000..38f54fe0 --- /dev/null +++ b/micropeak/MicroGraph.java @@ -0,0 +1,154 @@ +/* + * Copyright © 2012 Keith Packard + * + * 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. + */ + +package org.altusmetrum.micropeak; + +import java.io.*; +import java.util.ArrayList; + +import java.awt.*; +import javax.swing.*; +import org.altusmetrum.AltosLib.*; + +import org.jfree.ui.*; +import org.jfree.chart.*; +import org.jfree.chart.plot.*; +import org.jfree.chart.axis.*; +import org.jfree.chart.renderer.*; +import org.jfree.chart.renderer.xy.*; +import org.jfree.chart.labels.*; +import org.jfree.data.xy.*; +import org.jfree.data.*; + +class MicroSeries extends XYSeries { + NumberAxis axis; + String label; + String units; + Color color; + + String label() { + return String.format("%s (%s)", label, units); + } + + void set_units(String units) { + this.units = units; + + axis.setLabel(label()); + } + + public MicroSeries (String label, String units, Color color) { + super(label); + this.label = label; + this.units = units; + this.color = color; + + axis = new NumberAxis(label()); + axis.setLabelPaint(color); + axis.setTickLabelPaint(color); + } +} + +public class MicroGraph implements AltosUnitsListener { + + XYPlot plot; + JFreeChart chart; + ChartPanel panel; + NumberAxis xAxis; + MicroSeries heightSeries; + MicroSeries speedSeries; + MicroSeries accelSeries; + + static final private Color red = new Color(194,31,31); + static final private Color green = new Color(31,194,31); + static final private Color blue = new Color(31,31,194); + + MicroData data; + + public JPanel panel() { + return panel; + } + + private MicroSeries addSeries(int index, String label, String units, Color color) { + MicroSeries series = new MicroSeries(label, units, color); + XYSeriesCollection dataset = new XYSeriesCollection(series); + XYItemRenderer renderer = new XYLineAndShapeRenderer(true, false); + + renderer.setSeriesPaint(0, color); + renderer.setPlot(plot); + renderer.setBaseToolTipGenerator(new StandardXYToolTipGenerator(String.format("{1}s: {2}%s ({0})", units), + new java.text.DecimalFormat("0.00"), + new java.text.DecimalFormat("0.00"))); + plot.setRangeAxis(index, series.axis); + plot.setDataset(index, dataset); + plot.setRenderer(index, renderer); + plot.mapDatasetToRangeAxis(index, index); + return series; + } + + public void resetData() { + heightSeries.clear(); + speedSeries.clear(); + accelSeries.clear(); + for (int i = 0; i < data.pressures.length; i++) { + double x = data.time(i); + heightSeries.add(x, AltosConvert.height.value(data.height(i))); + speedSeries.add(x, AltosConvert.speed.value(data.speed(i))); + accelSeries.add(x, AltosConvert.accel.value(data.acceleration(i))); + } + } + + public void setData (MicroData data) { + this.data = data; + resetData(); + } + + public void units_changed(boolean imperial_units) { + if (data != null) { + heightSeries.set_units(AltosConvert.height.show_units()); + speedSeries.set_units(AltosConvert.speed.show_units()); + accelSeries.set_units(AltosConvert.accel.show_units()); + resetData(); + } + } + + public MicroGraph() { + + xAxis = new NumberAxis("Time (s)"); + + xAxis.setAutoRangeIncludesZero(true); + + plot = new XYPlot(); + plot.setDomainAxis(xAxis); + plot.setOrientation(PlotOrientation.VERTICAL); + plot.setDomainPannable(true); + plot.setRangePannable(true); + + heightSeries = addSeries(0, "Height", AltosConvert.height.show_units(), red); + speedSeries = addSeries(1, "Speed", AltosConvert.speed.show_units(), green); + accelSeries = addSeries(2, "Acceleration", AltosConvert.accel.show_units(), blue); + + chart = new JFreeChart("Flight", JFreeChart.DEFAULT_TITLE_FONT, + plot, true); + + ChartUtilities.applyCurrentTheme(chart); + panel = new ChartPanel(chart); + panel.setMouseWheelEnabled(true); + panel.setPreferredSize(new java.awt.Dimension(800, 500)); + + AltosPreferences.register_units_listener(this); + } +} \ No newline at end of file diff --git a/micropeak/MicroPeak.java b/micropeak/MicroPeak.java new file mode 100644 index 00000000..c69f7167 --- /dev/null +++ b/micropeak/MicroPeak.java @@ -0,0 +1,171 @@ +/* + * Copyright © 2012 Keith Packard + * + * 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. + */ + +package org.altusmetrum.micropeak; + +import java.awt.*; +import java.awt.event.*; +import javax.swing.*; +import java.io.*; +import java.util.concurrent.*; +import java.util.*; +import org.altusmetrum.AltosLib.*; +import org.altusmetrum.altosuilib.*; + +public class MicroPeak extends MicroFrame implements ActionListener, ItemListener { + + File filename; + MicroGraph graph; + MicroStatsTable stats; + MicroData data; + Container container; + JTabbedPane pane; + + private void RunFile(InputStream input) { + try { + data = new MicroData(input); + graph.setData(data); + stats.setData(data); + } catch (IOException ioe) { + } + try { + input.close(); + } catch (IOException ioe) { + } + } + + private void OpenFile(File filename) { + try { + RunFile (new FileInputStream(filename)); + } catch (FileNotFoundException fne) { + } + } + + private void SelectFile() { + MicroFileChooser chooser = new MicroFileChooser(this); + InputStream input = chooser.runDialog(); + + if (input != null) + RunFile(input); + } + + private void Preferences() { + new AltosConfigureUI(this); + } + + private void DownloadData() { + java.util.List devices = MicroUSB.list(); + for (MicroUSB device : devices) + System.out.printf("device %s\n", device.toString()); + } + + public void actionPerformed(ActionEvent ev) { + if ("Exit".equals(ev.getActionCommand())) + System.exit(0); + else if ("Open".equals(ev.getActionCommand())) + SelectFile(); + else if ("New".equals(ev.getActionCommand())) + new MicroPeak(); + else if ("Download".equals(ev.getActionCommand())) + DownloadData(); + else if ("Preferences".equals(ev.getActionCommand())) + Preferences(); + } + + public void itemStateChanged(ItemEvent e) { + } + + public MicroPeak() { + + AltosUIPreferences.set_component(this); + + container = getContentPane(); + pane = new JTabbedPane(); + + setTitle("MicroPeak"); + + JMenuBar menuBar = new JMenuBar(); + setJMenuBar(menuBar); + + JMenu fileMenu = new JMenu("File"); + menuBar.add(fileMenu); + + JMenuItem newAction = new JMenuItem("New"); + fileMenu.add(newAction); + newAction.addActionListener(this); + + JMenuItem openAction = new JMenuItem("Open"); + fileMenu.add(openAction); + openAction.addActionListener(this); + + JMenuItem downloadAction = new JMenuItem("Download"); + fileMenu.add(downloadAction); + downloadAction.addActionListener(this); + + JMenuItem preferencesAction = new JMenuItem("Preferences"); + fileMenu.add(preferencesAction); + preferencesAction.addActionListener(this); + + JMenuItem exitAction = new JMenuItem("Exit"); + fileMenu.add(exitAction); + exitAction.addActionListener(this); + + setDefaultCloseOperation(JFrame.DO_NOTHING_ON_CLOSE); + addWindowListener(new WindowAdapter() { + @Override + public void windowClosing(WindowEvent e) { + System.exit(0); + } + }); + + graph = new MicroGraph(); + stats = new MicroStatsTable(); + pane.add(graph.panel, "Graph"); + pane.add(stats, "Statistics"); + pane.doLayout(); + pane.validate(); + container.add(pane); + container.doLayout(); + container.validate(); + doLayout(); + validate(); + Insets i = getInsets(); + Dimension ps = pane.getPreferredSize(); + ps.width += i.left + i.right; + ps.height += i.top + i.bottom; + setPreferredSize(ps); + setSize(ps); + setVisible(true); + } + + public static void main(final String[] args) { + boolean opened = false; + + try { + UIManager.setLookAndFeel(AltosUIPreferences.look_and_feel()); + } catch (Exception e) { + } + + for (int i = 0; i < args.length; i++) { + MicroPeak m = new MicroPeak(); + m.OpenFile(new File(args[i])); + opened = true; + } + if (!opened) + new MicroPeak(); + } +} \ No newline at end of file diff --git a/micropeak/MicroSerial.java b/micropeak/MicroSerial.java new file mode 100644 index 00000000..a1a77a1d --- /dev/null +++ b/micropeak/MicroSerial.java @@ -0,0 +1,51 @@ +/* + * Copyright © 2012 Keith Packard + * + * 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. + */ + +package org.altusmetrum.micropeak; + +import java.util.*; +import java.io.*; +import libaltosJNI.*; +import org.altusmetrum.altosuilib.*; + +public class MicroSerial extends InputStream { + SWIGTYPE_p_altos_file file; + + public int read() { + int c = libaltos.altos_getchar(file, 0); + if (AltosUIPreferences.serial_debug) + System.out.printf("%c", c); + return c; + } + + public void close() { + if (file != null) { + libaltos.altos_close(file); + file = null; + } + } + + public MicroSerial(MicroUSB usb) throws FileNotFoundException { + file = usb.open(); + if (file == null) { + final String message = usb.getErrorString(); + throw new FileNotFoundException(String.format("%s (%s)", + usb.toShortString(), + message)); + } + } +} diff --git a/micropeak/MicroStats.java b/micropeak/MicroStats.java new file mode 100644 index 00000000..6ae8a2b2 --- /dev/null +++ b/micropeak/MicroStats.java @@ -0,0 +1,184 @@ +/* + * Copyright © 2011 Keith Packard + * + * 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. + */ + +package org.altusmetrum.micropeak; + +import java.io.*; +import org.altusmetrum.AltosLib.*; +import org.altusmetrum.altosuilib.*; + +public class MicroStats { + double coast_height; + double coast_time; + + double apogee_height; + double apogee_time; + + double landed_height; + double landed_time; + + double max_speed; + double max_accel; + + MicroData data; + + void find_landing() { + landed_height = 0; + + int t = 0; + for (double height : data.heights()) { + landed_height = height; + t++; + } + landed_time = data.time(t); + + t = 0; + boolean above = false; + for (double height : data.heights()) { + if (height > landed_height + 10) { + above = true; + } else { + if (above && height < landed_height + 2) { + above = false; + landed_time = data.time(t); + } + } + t++; + } + } + + void find_apogee() { + apogee_height = 0; + apogee_time = 0; + + int t = 0; + for (double height : data.heights()) { + if (height > apogee_height) { + apogee_height = height; + apogee_time = data.time(t); + } + t++; + } + } + + void find_coast() { + coast_height = 0; + coast_time = 0; + + int t = 0; + for (double accel : data.accels()) { + if (accel < -9.8) + break; + t++; + } + coast_time = data.time(t); + + int coast_t = t; + t = 0; + for (double height : data.heights()) { + if (t >= coast_t) { + coast_height = height; + break; + } + t++; + } + } + + void find_max_speed() { + max_speed = 0; + int t = 0; + for (double speed : data.speeds()) { + if (data.time(t) > apogee_time) + break; + if (speed > max_speed) + max_speed = speed; + t++; + } + } + + void find_max_accel() { + max_accel = 0; + + int t = 0; + for (double accel : data.accels()) { + if (data.time(t) > apogee_time) + break; + if (accel > max_accel) + max_accel = accel; + t++; + } + } + + double boost_duration() { + return coast_time; + } + + double boost_height() { + return coast_height; + } + + double boost_speed() { + return coast_height / coast_time; + } + + double boost_accel() { + return boost_speed() / boost_duration(); + } + + double coast_duration() { + return apogee_time - coast_time; + } + + double coast_height() { + return apogee_height - coast_height; + } + + double coast_speed() { + return coast_height() / coast_duration(); + } + + double coast_accel() { + return coast_speed() / coast_duration(); + } + + double descent_duration() { + return landed_time - apogee_time; + } + + double descent_height() { + return apogee_height - landed_height; + } + + double descent_speed() { + return descent_height() / descent_duration(); + } + + public MicroStats(MicroData data) { + + this.data = data; + + find_coast(); + find_apogee(); + find_landing(); + find_max_speed(); + find_max_accel(); + } + + public MicroStats() { + this(new MicroData()); + } +} diff --git a/micropeak/MicroStatsTable.java b/micropeak/MicroStatsTable.java new file mode 100644 index 00000000..f373e25d --- /dev/null +++ b/micropeak/MicroStatsTable.java @@ -0,0 +1,138 @@ +/* + * Copyright © 2011 Keith Packard + * + * 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. + */ + +package org.altusmetrum.micropeak; + +import java.awt.*; +import javax.swing.*; +import org.altusmetrum.AltosLib.*; +import org.altusmetrum.altosuilib.*; + +public class MicroStatsTable extends JComponent { + GridBagLayout layout; + + class MicroStat { + JLabel label; + JTextField[] texts; + + public void set_values(String ... values) { + for (int j = 0; j < values.length; j++) { + texts[j].setText(values[j]); + } + } + + public MicroStat(GridBagLayout layout, int y, String label_text, String ... values) { + GridBagConstraints c = new GridBagConstraints(); + c.insets = new Insets(AltosUILib.tab_elt_pad, AltosUILib.tab_elt_pad, AltosUILib.tab_elt_pad, AltosUILib.tab_elt_pad); + c.weighty = 1; + + label = new JLabel(label_text); + label.setFont(AltosUILib.label_font); + label.setHorizontalAlignment(SwingConstants.LEFT); + c.gridx = 0; c.gridy = y; + c.anchor = GridBagConstraints.WEST; + c.fill = GridBagConstraints.VERTICAL; + c.weightx = 0; + layout.setConstraints(label, c); + add(label); + + texts = new JTextField[values.length]; + for (int j = 0; j < values.length; j++) { + JTextField value = new JTextField(values[j]); + value.setFont(AltosUILib.value_font); + value.setHorizontalAlignment(SwingConstants.RIGHT); + texts[j] = value; + c.gridx = j+1; c.gridy = y; + c.anchor = GridBagConstraints.EAST; + c.fill = GridBagConstraints.BOTH; + c.weightx = 1; + layout.setConstraints(value, c); + add(value); + } + } + } + + MicroStat max_height, max_speed; + MicroStat max_accel, avg_accel; + MicroStat boost_duration; + MicroStat coast_duration; + MicroStat descent_speed; + MicroStat descent_duration; + MicroStat flight_time; + + public void setStats(MicroStats stats) { + max_height.set_values(String.format("%5.0f m", stats.apogee_height), + String.format("%5.0f ft", AltosConvert.meters_to_feet(stats.apogee_height))); + max_speed.set_values(String.format("%5.0f m/s", stats.max_speed), + String.format("%5.0f mph", AltosConvert.meters_to_mph(stats.max_speed)), + String.format("Mach %4.1f", AltosConvert.meters_to_mach(stats.max_speed))); + max_accel.set_values(String.format("%5.0f m/s²", stats.max_accel), + String.format("%5.0f ft/s²", AltosConvert.meters_to_feet(stats.max_accel)), + String.format("%5.0f G", AltosConvert.meters_to_g(stats.max_accel))); + avg_accel.set_values(String.format("%5.0f m/s²", stats.boost_accel(), + String.format("%5.0f ft/s²", AltosConvert.meters_to_feet(stats.boost_accel())), + String.format("%5.0f G", AltosConvert.meters_to_g(stats.boost_accel())))); + boost_duration.set_values(String.format("%6.1f s", stats.boost_duration())); + coast_duration.set_values(String.format("%6.1f s", stats.coast_duration())); + descent_speed.set_values(String.format("%5.0f m/s", stats.descent_speed()), + String.format("%5.0f ft/s", AltosConvert.meters_to_feet(stats.descent_speed()))); + descent_duration.set_values(String.format("%6.1f s", stats.descent_duration())); + flight_time.set_values(String.format("%6.1f s", stats.landed_time)); + } + + public void setData(MicroData data) { + setStats(new MicroStats(data)); + } + + public MicroStatsTable(MicroStats stats) { + layout = new GridBagLayout(); + + setLayout(layout); + int y = 0; + max_height = new MicroStat(layout, y++, "Maximum height", + String.format("%5.0f m", stats.apogee_height), + String.format("%5.0f ft", AltosConvert.meters_to_feet(stats.apogee_height))); + max_speed = new MicroStat(layout, y++, "Maximum speed", + String.format("%5.0f m/s", stats.max_speed), + String.format("%5.0f mph", AltosConvert.meters_to_mph(stats.max_speed)), + String.format("Mach %4.1f", AltosConvert.meters_to_mach(stats.max_speed))); + max_accel = new MicroStat(layout, y++, "Maximum boost acceleration", + String.format("%5.0f m/s²", stats.max_accel), + String.format("%5.0f ft/s²", AltosConvert.meters_to_feet(stats.max_accel)), + String.format("%5.0f G", AltosConvert.meters_to_g(stats.max_accel))); + avg_accel = new MicroStat(layout, y++, "Average boost acceleration", + String.format("%5.0f m/s²", stats.boost_accel(), + String.format("%5.0f ft/s²", AltosConvert.meters_to_feet(stats.boost_accel())), + String.format("%5.0f G", AltosConvert.meters_to_g(stats.boost_accel())))); + boost_duration = new MicroStat(layout, y++, "Boost duration", + String.format("%6.0f s", stats.boost_duration())); + coast_duration = new MicroStat(layout, y++, "Coast duration", + String.format("%6.1f s", stats.coast_duration())); + descent_speed = new MicroStat(layout, y++, "Descent rate", + String.format("%5.0f m/s", stats.descent_speed()), + String.format("%5.0f ft/s", AltosConvert.meters_to_feet(stats.descent_speed()))); + descent_duration = new MicroStat(layout, y++, "Descent duration", + String.format("%6.1f s", stats.descent_duration())); + flight_time = new MicroStat(layout, y++, "Flight Time", + String.format("%6.0f s", stats.landed_time)); + } + + public MicroStatsTable() { + this(new MicroStats()); + } + +} \ No newline at end of file diff --git a/micropeak/MicroUSB.java b/micropeak/MicroUSB.java new file mode 100644 index 00000000..d48610fe --- /dev/null +++ b/micropeak/MicroUSB.java @@ -0,0 +1,103 @@ +/* + * Copyright © 2010 Keith Packard + * + * 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. + */ + +package org.altusmetrum.micropeak; +import java.util.*; +import libaltosJNI.*; + +public class MicroUSB extends altos_device { + + static boolean initialized = false; + static boolean loaded_library = false; + + public static boolean load_library() { + if (!initialized) { + try { + System.loadLibrary("altos"); + libaltos.altos_init(); + loaded_library = true; + } catch (UnsatisfiedLinkError e) { + try { + System.loadLibrary("altos64"); + libaltos.altos_init(); + loaded_library = true; + } catch (UnsatisfiedLinkError e2) { + loaded_library = false; + } + } + initialized = true; + } + return loaded_library; + } + + public String toString() { + String name = getName(); + if (name == null) + name = "Altus Metrum"; + return String.format("%-20.20s %4d %s", + name, getSerial(), getPath()); + } + + public String toShortString() { + String name = getName(); + if (name == null) + name = "Altus Metrum"; + return String.format("%s %d %s", + name, getSerial(), getPath()); + + } + + public String getErrorString() { + altos_error error = new altos_error(); + + libaltos.altos_get_last_error(error); + return String.format("%s (%d)", error.getString(), error.getCode()); + } + + public SWIGTYPE_p_altos_file open() { + return libaltos.altos_open(this); + } + + private boolean isMicro() { + if (getVendor() != 0x0403) + return false; + if (getProduct() != 0x6001) + return false; + return true; + } + + static java.util.List list() { + if (!load_library()) + return null; + + SWIGTYPE_p_altos_list list = libaltos.altos_list_start(); + + ArrayList device_list = new ArrayList(); + if (list != null) { + for (;;) { + MicroUSB device = new MicroUSB(); + if (libaltos.altos_list_next(list, device) == 0) + break; + if (device.isMicro()) + device_list.add(device); + } + libaltos.altos_list_finish(list); + } + + return device_list; + } +} \ No newline at end of file diff --git a/src/drivers/ao_cc1120.c b/src/drivers/ao_cc1120.c index 63d2f955..d9b2e5bf 100644 --- a/src/drivers/ao_cc1120.c +++ b/src/drivers/ao_cc1120.c @@ -834,8 +834,8 @@ ao_radio_rx_isr(void) { uint8_t d; - d = stm_spi2.dr; - stm_spi2.dr = 0; + d = AO_CC1120_SPI.dr; + AO_CC1120_SPI.dr = 0; if (rx_ignore == 0) { if (rx_data_cur >= rx_data_count) ao_exti_disable(AO_CC1120_INT_PORT, AO_CC1120_INT_PIN); @@ -922,10 +922,10 @@ ao_radio_recv(__xdata void *d, uint8_t size) ao_radio_wake = 0; ao_radio_mcu_wake = 0; - stm_spi2.cr2 = 0; + AO_CC1120_SPI.cr2 = 0; /* clear any RXNE */ - (void) stm_spi2.dr; + (void) AO_CC1120_SPI.dr; /* Have the radio signal when the preamble quality goes high */ ao_radio_reg_write(AO_CC1120_INT_GPIO_IOCFG, CC1120_IOCFG_GPIO_CFG_PQT_REACHED); diff --git a/src/megadongle-v0.1/ao_pins.h b/src/megadongle-v0.1/ao_pins.h index 9fc93fb1..c766a48c 100644 --- a/src/megadongle-v0.1/ao_pins.h +++ b/src/megadongle-v0.1/ao_pins.h @@ -135,6 +135,7 @@ #define AO_CC1120_SPI_CS_PORT (&stm_gpioa) #define AO_CC1120_SPI_CS_PIN 0 #define AO_CC1120_SPI_BUS AO_SPI_2_PB13_PB14_PB15 +#define AO_CC1120_SPI stm_spi2 #define AO_CC1120_INT_PORT (&stm_gpioc) #define AO_CC1120_INT_PIN 13 diff --git a/src/megametrum-v0.1/ao_pins.h b/src/megametrum-v0.1/ao_pins.h index b1a70ea2..64da41a9 100644 --- a/src/megametrum-v0.1/ao_pins.h +++ b/src/megametrum-v0.1/ao_pins.h @@ -281,6 +281,7 @@ struct ao_adc { #define AO_CC1120_SPI_CS_PORT (&stm_gpioc) #define AO_CC1120_SPI_CS_PIN 5 #define AO_CC1120_SPI_BUS AO_SPI_2_PB13_PB14_PB15 +#define AO_CC1120_SPI stm_spi2 #define AO_CC1120_INT_PORT (&stm_gpioc) #define AO_CC1120_INT_PIN 14 diff --git a/src/micropeak/Makefile b/src/micropeak/Makefile index 0c48ed66..ff0a4499 100644 --- a/src/micropeak/Makefile +++ b/src/micropeak/Makefile @@ -20,13 +20,6 @@ ifndef VERSION include ../Version endif -# Support for a logging EEPROM -# -#EEPROM_SRC=ao_async.c \ -# ao_i2c_attiny.c \ -# ao_at24c.c -# - ALTOS_SRC = \ ao_micropeak.c \ ao_spi_attiny.c \ @@ -39,7 +32,8 @@ ALTOS_SRC = \ ao_notask.c \ ao_eeprom_tiny.c \ ao_panic.c \ - $(EEPROM_SRC) + ao_log_micro.c \ + ao_async.c INC=\ ao.h \ @@ -48,13 +42,15 @@ INC=\ ao_arch_funcs.h \ ao_exti.h \ ao_ms5607.h \ + ao_log_micro.h \ + ao_micropeak.h \ altitude-pa.h IDPRODUCT=0 PRODUCT=MicroPeak-v0.1 PRODUCT_DEF=-DMICROPEAK CFLAGS = $(PRODUCT_DEF) -I. -I../attiny -I../core -I.. -I../drivers -CFLAGS += -g -mmcu=$(MCU) -Wall -Wstrict-prototypes -O3 -mcall-prologues -DATTINY +CFLAGS += -g -mmcu=$(MCU) -Wall -Wstrict-prototypes -O2 -mcall-prologues -DATTINY NICKLE=nickle diff --git a/src/micropeak/ao_async.c b/src/micropeak/ao_async.c index 04bba9e8..3556f54c 100644 --- a/src/micropeak/ao_async.c +++ b/src/micropeak/ao_async.c @@ -21,20 +21,51 @@ #define AO_ASYNC_BAUD 38400l #define AO_ASYNC_DELAY (uint8_t) (1000000l / AO_ASYNC_BAUD) +#define LED_PORT PORTB + +void +ao_async_start(void) +{ + LED_PORT |= (1 << AO_LED_SERIAL); +} + +void +ao_async_stop(void) +{ + LED_PORT &= ~(1 << AO_LED_SERIAL); +} + void ao_async_byte(uint8_t byte) { uint8_t b; uint16_t w; - /* start bit */ - - /* start data stop */ - w = 0x001 | (byte << 1) | 0x000; + /* start data stop */ + w = (0x000 << 0) | (byte << 1) | (0x001 << 9); + ao_arch_block_interrupts(); for (b = 0; b < 10; b++) { - ao_led_set((w & 1) << AO_LED_SERIAL); + uint8_t v = LED_PORT & ~(1 << AO_LED_SERIAL); + v |= (w & 1) << AO_LED_SERIAL; + LED_PORT = v; w >>= 1; - ao_delay_us(26); + + /* Carefully timed to hit around 9600 baud */ + asm volatile ("nop"); + asm volatile ("nop"); + + asm volatile ("nop"); + asm volatile ("nop"); + asm volatile ("nop"); + asm volatile ("nop"); + asm volatile ("nop"); + + asm volatile ("nop"); + asm volatile ("nop"); + asm volatile ("nop"); + asm volatile ("nop"); + asm volatile ("nop"); } + ao_arch_release_interrupts(); } diff --git a/src/micropeak/ao_async.h b/src/micropeak/ao_async.h index a06d2e1a..1b239712 100644 --- a/src/micropeak/ao_async.h +++ b/src/micropeak/ao_async.h @@ -18,6 +18,12 @@ #ifndef _AO_ASYNC_H_ #define _AO_ASYNC_H_ +void +ao_async_start(void); + +void +ao_async_stop(void); + void ao_async_byte(uint8_t byte); diff --git a/src/micropeak/ao_log_micro.c b/src/micropeak/ao_log_micro.c index eda0d1d2..d665efb5 100644 --- a/src/micropeak/ao_log_micro.c +++ b/src/micropeak/ao_log_micro.c @@ -16,58 +16,106 @@ */ #include +#include #include #include -#if HAS_EEPROM - -ao_pos_t ao_log_micro_pos; +static uint16_t ao_log_offset = STARTING_LOG_OFFSET; void -ao_log_micro_data(uint32_t data) +ao_log_micro_save(void) { - ao_storage_write(ao_log_micro_pos, &data, sizeof (data)); - ao_log_micro_pos += sizeof (data); + uint16_t n_samples = (ao_log_offset - STARTING_LOG_OFFSET) / sizeof (uint16_t); + ao_eeprom_write(PA_GROUND_OFFSET, &pa_ground, sizeof (pa_ground)); + ao_eeprom_write(PA_MIN_OFFSET, &pa_min, sizeof (pa_min)); + ao_eeprom_write(N_SAMPLES_OFFSET, &n_samples, sizeof (n_samples)); } -uint32_t ao_log_last_ground; -uint32_t ao_log_last_done; +void +ao_log_micro_restore(void) +{ + ao_eeprom_read(PA_GROUND_OFFSET, &pa_ground, sizeof (pa_ground)); + ao_eeprom_read(PA_MIN_OFFSET, &pa_min, sizeof (pa_min)); +} -uint8_t -ao_log_micro_scan(void) +void +ao_log_micro_data(void) { - uint32_t data; - ao_pos_t pos; + uint16_t low_bits = pa; - ao_storage_read(0, &data, sizeof (data)); - if ((data & AO_LOG_MICRO_MASK) != AO_LOG_MICRO_GROUND) - return 0; + if (ao_log_offset < MAX_LOG_OFFSET) { + ao_eeprom_write(ao_log_offset, &low_bits, sizeof (low_bits)); + ao_log_offset += sizeof (low_bits); + } +} + +#define POLY 0x8408 + +static uint16_t +ao_log_micro_crc(uint16_t crc, uint8_t byte) +{ + uint8_t i; - ao_log_last_ground = data & ~(AO_LOG_MICRO_MASK); - for (pos = 4; pos < ao_storage_total; pos += 4) { - ao_storage_read(pos, &data, sizeof (data)); - if ((data & AO_LOG_MICRO_MASK) == AO_LOG_MICRO_GROUND) { - ao_log_last_done = data & ~(AO_LOG_MICRO_MASK); - return 1; - } + for (i = 0; i < 8; i++) { + if ((crc & 0x0001) ^ (byte & 0x0001)) + crc = (crc >> 1) ^ POLY; + else + crc = crc >> 1; + byte >>= 1; } - return 0; + return crc; +} + +static void +ao_log_hex_nibble(uint8_t b) +{ + if (b < 10) + ao_async_byte('0' + b); + else + ao_async_byte('a' - 10 + b); +} + +static void +ao_log_hex(uint8_t b) +{ + ao_log_hex_nibble(b>>4); + ao_log_hex_nibble(b&0xf); +} + +static void +ao_log_newline(void) +{ + ao_async_byte('\r'); + ao_async_byte('\n'); } void ao_log_micro_dump(void) { - ao_pos_t pos; - uint8_t data[4]; - uint8_t i; + uint16_t n_samples; + uint16_t nbytes; + uint8_t byte; + uint16_t b; + uint16_t crc = 0xffff; - for (pos = 0; pos < ao_storage_total; pos += 4) { - ao_storage_read(pos, data, 4); - for (i = 0; i < 4; i++) - ao_async_byte(data[i]); - if (data[3] == (uint8_t) (AO_LOG_MICRO_GROUND >> 24)) - break; + ao_eeprom_read(N_SAMPLES_OFFSET, &n_samples, sizeof (n_samples)); + if (n_samples == 0xffff) + n_samples = 0; + nbytes = STARTING_LOG_OFFSET + sizeof (uint16_t) * n_samples; + ao_async_start(); + ao_async_byte('M'); + ao_async_byte('P'); + for (b = 0; b < nbytes; b++) { + if ((b & 0xf) == 0) + ao_log_newline(); + ao_eeprom_read(b, &byte, 1); + ao_log_hex(byte); + crc = ao_log_micro_crc(crc, byte); } + ao_log_newline(); + crc = ~crc; + ao_log_hex(crc >> 8); + ao_log_hex(crc); + ao_log_newline(); + ao_async_stop(); } - -#endif diff --git a/src/micropeak/ao_log_micro.h b/src/micropeak/ao_log_micro.h index 15b2d178..976852ee 100644 --- a/src/micropeak/ao_log_micro.h +++ b/src/micropeak/ao_log_micro.h @@ -18,19 +18,20 @@ #ifndef _AO_LOG_MICRO_H_ #define _AO_LOG_MICRO_H_ -#define AO_LOG_MICRO_GROUND (0l << 24) -#define AO_LOG_MICRO_DATA (1l << 24) -#define AO_LOG_MICRO_DONE (0xaal << 24) -#define AO_LOG_MICRO_MASK (0xffl << 24) +#define PA_GROUND_OFFSET 0 +#define PA_MIN_OFFSET 4 +#define N_SAMPLES_OFFSET 8 +#define STARTING_LOG_OFFSET 10 +#define MAX_LOG_OFFSET 512 void -ao_log_micro_data(uint32_t data); +ao_log_micro_save(void); -extern uint32_t ao_log_last_ground; -extern uint32_t ao_log_last_done; +void +ao_log_micro_restore(void); -uint8_t -ao_log_micro_scan(void); +void +ao_log_micro_data(void); void ao_log_micro_dump(void); diff --git a/src/micropeak/ao_micropeak.c b/src/micropeak/ao_micropeak.c index 525cfa42..82012800 100644 --- a/src/micropeak/ao_micropeak.c +++ b/src/micropeak/ao_micropeak.c @@ -16,22 +16,23 @@ */ #include +#include #include #include +#include static struct ao_ms5607_sample sample; static struct ao_ms5607_value value; -static uint32_t pa; -static uint32_t pa_sum; -static uint32_t pa_avg; -static int32_t pa_diff; -static uint32_t pa_ground; -static uint32_t pa_min; -static uint32_t pa_interval_min, pa_interval_max; -static alt_t ground_alt, max_alt; +uint32_t pa; +uint32_t pa_avg; +uint32_t pa_ground; +uint32_t pa_min; +alt_t ground_alt, max_alt; alt_t ao_max_height; +static uint32_t pa_sum; + static void ao_pa_get(void) { @@ -40,22 +41,6 @@ ao_pa_get(void) pa = value.pres; } -#define FILTER_SHIFT 3 -#define SAMPLE_SLEEP AO_MS_TO_TICKS(96) - -/* 16 sample, or about two seconds worth */ -#define GROUND_AVG_SHIFT 4 -#define GROUND_AVG (1 << GROUND_AVG_SHIFT) - -/* Pressure change (in Pa) to detect boost */ -#define BOOST_DETECT 120 /* 10m at sea level, 12m at 2000m */ - -/* Wait after power on before doing anything to give the user time to assemble the rocket */ -#define BOOST_DELAY AO_SEC_TO_TICKS(30) - -/* Pressure change (in Pa) to detect landing */ -#define LAND_DETECT 12 /* 1m at sea level, 1.2m at 2000m */ - static void ao_compute_height(void) { @@ -64,90 +49,42 @@ ao_compute_height(void) ao_max_height = max_alt - ground_alt; } -#if !HAS_EEPROM - -#define PA_GROUND_OFFSET 0 -#define PA_MIN_OFFSET 4 -#define N_SAMPLES_OFFSET 8 -#define STARTING_LOG_OFFSET 10 -#define MAX_LOG_OFFSET 512 - -static uint16_t ao_log_offset = STARTING_LOG_OFFSET; - -void -ao_save_flight(void) -{ - uint16_t n_samples = (ao_log_offset - STARTING_LOG_OFFSET) / sizeof (uint16_t); - ao_eeprom_write(PA_GROUND_OFFSET, &pa_ground, sizeof (pa_ground)); - ao_eeprom_write(PA_MIN_OFFSET, &pa_min, sizeof (pa_min)); - ao_eeprom_write(N_SAMPLES_OFFSET, &n_samples, sizeof (n_samples)); -} - -void -ao_restore_flight(void) -{ - ao_eeprom_read(PA_GROUND_OFFSET, &pa_ground, sizeof (pa_ground)); - ao_eeprom_read(PA_MIN_OFFSET, &pa_min, sizeof (pa_min)); -} - -void -ao_log_micro(void) +static void +ao_pips(void) { - uint16_t low_bits = pa; - - if (ao_log_offset < MAX_LOG_OFFSET) { - ao_eeprom_write(ao_log_offset, &low_bits, sizeof (low_bits)); - ao_log_offset += sizeof (low_bits); + uint8_t i; + for (i = 0; i < 10; i++) { + ao_led_toggle(AO_LED_REPORT); + ao_delay(AO_MS_TO_TICKS(80)); } + ao_delay(AO_MS_TO_TICKS(200)); } -#endif int main(void) { int16_t sample_count; uint16_t time; -#if HAS_EEPROM - uint8_t dump_eeprom = 0; -#endif + uint32_t pa_interval_min, pa_interval_max; + int32_t pa_diff; + ao_led_init(LEDS_AVAILABLE); ao_timer_init(); -#if HAS_EEPROM - - /* Set MOSI and CLK as inputs with pull-ups */ - DDRB &= ~(1 << 0) | (1 << 2); - PORTB |= (1 << 0) | (1 << 2); - - /* Check to see if either MOSI or CLK are pulled low by the - * user shorting them to ground. If so, dump the eeprom out - * via the LED. Wait for the shorting wire to go away before - * continuing. - */ - while ((PINB & ((1 << 0) | (1 << 2))) != ((1 << 0) | (1 << 2))) - dump_eeprom = 1; - PORTB &= ~(1 << 0) | (1 << 2); - - ao_i2c_init(); -#endif - ao_restore_flight(); - ao_compute_height(); - /* Give the person a second to get their finger out of the way */ - ao_delay(AO_MS_TO_TICKS(1000)); - ao_report_altitude(); - + /* Init external hardware */ ao_spi_init(); ao_ms5607_init(); ao_ms5607_setup(); -#if HAS_EEPROM - ao_storage_init(); - - /* Check to see if there's a flight recorded in memory */ - if (dump_eeprom && ao_log_micro_scan()) - ao_log_micro_dump(); -#endif + /* Give the person a second to get their finger out of the way */ + ao_delay(AO_MS_TO_TICKS(1000)); + ao_log_micro_restore(); + ao_compute_height(); + ao_report_altitude(); + ao_pips(); + ao_log_micro_dump(); + ao_delay(BOOST_DELAY); /* Wait for motion, averaging values to get ground pressure */ time = ao_time(); @@ -182,10 +119,6 @@ main(void) pa_ground >>= FILTER_SHIFT; -#if HAS_EEPROM - ao_log_micro_data(AO_LOG_MICRO_GROUND | pa_ground); -#endif - /* Now sit around until the pressure is stable again and record the max */ sample_count = 0; @@ -200,12 +133,8 @@ main(void) ao_pa_get(); if ((sample_count & 3) == 0) ao_led_off(AO_LED_REPORT); -#if HAS_EEPROM - ao_log_micro_data(AO_LOG_MICRO_DATA | pa); -#else if (sample_count & 1) - ao_log_micro(); -#endif + ao_log_micro_data(); pa_avg = pa_avg - (pa_avg >> FILTER_SHIFT) + pa; if (pa_avg < pa_min) pa_min = pa_avg; @@ -228,10 +157,7 @@ main(void) } } pa_min >>= FILTER_SHIFT; -#if HAS_EEPROM - ao_log_micro_data(AO_LOG_MICRO_DONE | pa_min); -#endif - ao_save_flight(); + ao_log_micro_save(); ao_compute_height(); ao_report_altitude(); for (;;) { diff --git a/src/micropeak/ao_micropeak.h b/src/micropeak/ao_micropeak.h new file mode 100644 index 00000000..e408d7c5 --- /dev/null +++ b/src/micropeak/ao_micropeak.h @@ -0,0 +1,56 @@ +/* + * Copyright © 2012 Keith Packard + * + * 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. + */ + +#ifndef _AO_MICROPEAK_H_ +#define _AO_MICROPEAK_H_ + +#define FILTER_SHIFT 3 +#define SAMPLE_SLEEP AO_MS_TO_TICKS(96) + +/* 16 sample, or about two seconds worth */ +#define GROUND_AVG_SHIFT 4 +#define GROUND_AVG (1 << GROUND_AVG_SHIFT) + +/* Pressure change (in Pa) to detect boost */ +#define BOOST_DETECT 120 /* 10m at sea level, 12m at 2000m */ + +/* Wait after power on before doing anything to give the user time to assemble the rocket */ +#define BOOST_DELAY AO_SEC_TO_TICKS(30) + +/* Pressure change (in Pa) to detect landing */ +#define LAND_DETECT 12 /* 1m at sea level, 1.2m at 2000m */ + +/* Current sensor pressure value */ +extern uint32_t pa; + +/* IIR filtered pressure value */ +extern uint32_t pa_avg; + +/* Average pressure value on ground */ +extern uint32_t pa_ground; + +/* Minimum recorded filtered pressure value */ +extern uint32_t pa_min; + +/* Pressure values converted to altitudes */ +extern alt_t ground_alt, max_alt; + +/* max_alt - ground_alt */ +extern alt_t ao_max_height; + +#endif +