Merge remote branch 'keithp/macos'
authorKeith Packard <keithp@keithp.com>
Wed, 28 Jul 2010 20:29:51 +0000 (13:29 -0700)
committerKeith Packard <keithp@keithp.com>
Wed, 28 Jul 2010 20:29:51 +0000 (13:29 -0700)
12 files changed:
ao-tools/Makefile.am
ao-tools/altosui/AltosDevice.java
ao-tools/altosui/AltosDeviceDialog.java
ao-tools/altosui/AltosPreferences.java
ao-tools/altosui/AltosSerial.java
ao-tools/altosui/AltosUI.java
ao-tools/altosui/AltosVoice.java
ao-tools/altosui/Makefile
ao-tools/altosui/Manifest.txt
ao-tools/altosui/voices.txt [deleted file]
src/ao.h
src/ao_log.c

index 2850e909003cbe82e1171c8ff7be8f797a185306..54dc777af134dd8e80c4a3fbf1b5e04c6fe3779a 100644 (file)
@@ -1 +1 @@
-SUBDIRS=lib ao-rawload ao-dbg ao-dumplog ao-bitbang ao-eeprom ao-list ao-load ao-postflight ao-view
+SUBDIRS=lib ao-rawload ao-dbg ao-dumplog ao-bitbang ao-eeprom ao-list ao-load ao-postflight ao-view libaltos altosui
index 0e7d01da757cf1f7e2729c6a4be038c2133fb873..f488174cf7677e3dc345b1a6875b78d2f5e41d7a 100644 (file)
@@ -42,7 +42,8 @@ public class AltosDevice extends altos_device {
                                AltosDevice device = new AltosDevice();
                                if (libaltos.altos_list_next(list, device) == 0)
                                        break;
-                               device_list.add(device);
+                               if (product == null || device.getProduct().startsWith(product))
+                                       device_list.add(device);
                        }
                        libaltos.altos_list_finish(list);
                }
index eb70877c9b99f18559a83e81989e126623251d75..08921c3d044d308680106b529b96f2c0b4abad8e 100644 (file)
@@ -41,6 +41,10 @@ public class AltosDeviceDialog {
                                                               devices[0]);
                        return (altos_device) o;
                } else {
+                       JOptionPane.showMessageDialog(frame,
+                                                     "No AltOS devices available",
+                                                     "No AltOS devices",
+                                                     JOptionPane.ERROR_MESSAGE);
                        return null;
                }
        }
index 0296d9355db7a2086ade1aa97ce737c028ed58d2..297e1aae77ba0819a71af5ef53718a648cf2f727 100644 (file)
@@ -31,6 +31,12 @@ class AltosPreferences {
        /* logdir preference name */
        final static String logdirPreference = "LOGDIR";
 
+       /* channel preference name */
+       final static String channelPreference = "CHANNEL";
+
+       /* voice preference name */
+       final static String voicePreference = "VOICE";
+
        /* Default logdir is ~/TeleMetrum */
        final static String logdirName = "TeleMetrum";
 
@@ -40,6 +46,12 @@ class AltosPreferences {
        /* Log directory */
        static File logdir;
 
+       /* Telemetry channel */
+       static int channel;
+
+       /* Voice preference */
+       static boolean voice;
+
        public static void init(Component ui) {
                preferences = Preferences.userRoot().node("/org/altusmetrum/altosui");
 
@@ -55,6 +67,10 @@ class AltosPreferences {
                        if (!logdir.exists())
                                logdir.mkdirs();
                }
+
+               channel = preferences.getInt(channelPreference, 0);
+
+               voice = preferences.getBoolean(voicePreference, true);
        }
 
        static void flush_preferences() {
@@ -114,4 +130,28 @@ class AltosPreferences {
        public static File logdir() {
                return logdir;
        }
+
+       public static void set_channel(int new_channel) {
+               channel = new_channel;
+               synchronized (preferences) {
+                       preferences.putInt(channelPreference, channel);
+                       flush_preferences();
+               }
+       }
+
+       public static int channel() {
+               return channel;
+       }
+
+       public static void set_voice(boolean new_voice) {
+               voice = new_voice;
+               synchronized (preferences) {
+                       preferences.putBoolean(voicePreference, voice);
+                       flush_preferences();
+               }
+       }
+
+       public static boolean voice() {
+               return voice;
+       }
 }
index 073bfb783bff4d3ca7c87c5390af02058026850b..f12b31b3187e9b253b4a3560080db9d8e2966d7e 100644 (file)
@@ -37,7 +37,9 @@ import libaltosJNI.SWIGTYPE_p_altos_list;
  * line in a queue. Dealing with that queue is left up to other
  * threads.
  */
-class AltosSerialReader implements Runnable {
+
+public class AltosSerial implements Runnable {
+
        SWIGTYPE_p_altos_file altos;
        LinkedList<LinkedBlockingQueue<String>> monitors;
        LinkedBlockingQueue<String> reply_queue;
@@ -116,50 +118,45 @@ class AltosSerialReader implements Runnable {
                }
        }
 
-       public void open(altos_device device) throws FileNotFoundException {
-               close();
-               altos = libaltos.altos_open(device);
-               input_thread = new Thread(this);
-               input_thread.start();
-       }
-       public AltosSerialReader () {
-               altos = null;
-               input_thread = null;
-               line = "";
-               monitors = new LinkedList<LinkedBlockingQueue<String>> ();
-               reply_queue = new LinkedBlockingQueue<String> ();
-       }
-}
-
-public class AltosSerial {
-       AltosSerialReader reader = null;
-
-       public void close() {
-               reader.close();
+       public void putc(char c) {
+               if (altos != null)
+                       libaltos.altos_putchar(altos, c);
        }
 
-       public void open(altos_device device) throws FileNotFoundException {
-               reader.open(device);
+       public void print(String data) {
+               for (int i = 0; i < data.length(); i++)
+                       putc(data.charAt(i));
        }
 
-       void init() {
-               reader = new AltosSerialReader();
+       public void printf(String format, Object ... arguments) {
+               print(String.format(format, arguments));
        }
 
-       public void add_monitor(LinkedBlockingQueue<String> q) {
-               reader.add_monitor(q);
+       public void open(altos_device device) throws FileNotFoundException {
+               close();
+               altos = libaltos.altos_open(device);
+               if (altos == null)
+                       throw new FileNotFoundException(device.getPath());
+               input_thread = new Thread(this);
+               input_thread.start();
+               print("\nE 0\nm 1\n");
+               try {
+                       Thread.sleep(200);
+               } catch (InterruptedException e) {
+               }
+               flush();
        }
 
-       public void remove_monitor(LinkedBlockingQueue<String> q) {
-               reader.remove_monitor(q);
+       public void set_channel(int channel) {
+               if (altos != null)
+                       printf("m 0\nc r %d\nm 1\n", channel);
        }
 
        public AltosSerial() {
-               init();
-       }
-
-       public AltosSerial(altos_device device) throws FileNotFoundException {
-               init();
-               open(device);
+               altos = null;
+               input_thread = null;
+               line = "";
+               monitors = new LinkedList<LinkedBlockingQueue<String>> ();
+               reply_queue = new LinkedBlockingQueue<String> ();
        }
 }
index 33ed2c90b19d791725b947dd343c1aab763d36a8..ecacffe5b55f2201e3e734836f4765fe2c5b5705 100644 (file)
@@ -210,6 +210,7 @@ public class AltosUI extends JFrame {
                                System.exit(0);
                        }
                });
+               voice.speak("Rocket flight monitor ready.");
        }
 
        public void info_reset() {
@@ -221,16 +222,8 @@ public class AltosUI extends JFrame {
                flightInfoModel[col].addRow(name, value);
        }
 
-       public void info_add_row(int col, String name, String format, Object value) {
-               flightInfoModel[col].addRow(name, String.format(format, value));
-       }
-
-       public void info_add_row(int col, String name, String format, Object v1, Object v2) {
-               flightInfoModel[col].addRow(name, String.format(format, v1, v2));
-       }
-
-       public void info_add_row(int col, String name, String format, Object v1, Object v2, Object v3) {
-               flightInfoModel[col].addRow(name, String.format(format, v1, v2, v3));
+       public void info_add_row(int col, String name, String format, Object... parameters) {
+               flightInfoModel[col].addRow(name, String.format(format, parameters));
        }
 
        public void info_add_deg(int col, String name, double v, int pos, int neg) {
@@ -346,48 +339,57 @@ public class AltosUI extends JFrame {
        class IdleThread extends Thread {
 
                private AltosState state;
+               int     reported_landing;
+
+               public void report(boolean last) {
+                       if (state == null)
+                               return;
+
+                       /* reset the landing count once we hear about a new flight */
+                       if (state.state < AltosTelemetry.ao_flight_drogue)
+                               reported_landing = 0;
+
+                       /* Shut up once the rocket is on the ground */
+                       if (reported_landing > 2) {
+                               return;
+                       }
+
+                       /* If the rocket isn't on the pad, then report height */
+                       if (state.state > AltosTelemetry.ao_flight_pad) {
+                               voice.speak("%d meters", (int) (state.height + 0.5));
+                       } else {
+                               reported_landing = 0;
+                       }
+
+                       /* If the rocket is coming down, check to see if it has landed;
+                        * either we've got a landed report or we haven't heard from it in
+                        * a long time
+                        */
+                       if (!state.ascent &&
+                           (last ||
+                            System.currentTimeMillis() - state.report_time >= 15000 ||
+                            state.state == AltosTelemetry.ao_flight_landed))
+                       {
+                               if (Math.abs(state.baro_speed) < 20 && state.height < 100)
+                                       voice.speak("rocket landed safely");
+                               else
+                                       voice.speak("rocket may have crashed");
+                               if (state.gps != null)
+                                       voice.speak("bearing %d degrees, range %d meters",
+                                                   (int) (state.from_pad.bearing + 0.5),
+                                                   (int) (state.from_pad.distance + 0.5));
+                               ++reported_landing;
+                       }
+               }
 
                public void run () {
-                       int     reported_landing = 0;
 
+                       reported_landing = 0;
                        state = null;
                        try {
                                for (;;) {
                                        Thread.sleep(10000);
-                                       if (state == null)
-                                               continue;
-
-                                       /* reset the landing count once we hear about a new flight */
-                                       if (state.state < AltosTelemetry.ao_flight_drogue)
-                                               reported_landing = 0;
-
-                                       /* Shut up once the rocket is on the ground */
-                                       if (reported_landing > 2)
-                                               continue;
-
-                                       /* If the rocket isn't on the pad, then report height */
-                                       if (state.state > AltosTelemetry.ao_flight_pad) {
-                                               voice.speak(String.format("%d meters", (int) (state.height + 0.5)));
-                                       }
-
-                                       /* If the rocket is coming down, check to see if it has landed;
-                                        * either we've got a landed report or we haven't heard from it in
-                                        * a long time
-                                        */
-                                       if (!state.ascent &&
-                                           (System.currentTimeMillis() - state.report_time > 10000 ||
-                                            state.state == AltosTelemetry.ao_flight_landed))
-                                       {
-                                               if (Math.abs(state.baro_speed) < 20 && state.height < 100)
-                                                       voice.speak("rocket landed safely");
-                                               else
-                                                       voice.speak("rocket may have crashed");
-                                               if (state.gps != null)
-                                                       voice.speak(String.format("bearing %d degrees, range %d meters",
-                                                                                 (int) (state.from_pad.bearing + 0.5),
-                                                                                 (int) (state.from_pad.distance + 0.5)));
-                                               ++reported_landing;
-                                       }
+                                       report(false);
                                }
                        } catch (InterruptedException ie) {
                        }
@@ -401,21 +403,22 @@ public class AltosUI extends JFrame {
        private void tell(AltosState state, AltosState old_state) {
                if (old_state == null || old_state.state != state.state) {
                        voice.speak(state.data.state);
-                       switch (state.state) {
-                       case AltosTelemetry.ao_flight_fast:
-                               voice.speak(String.format("max speed %d meters per second",
-                                                         (int) (state.max_speed + 0.5)));
-                               break;
-                       case AltosTelemetry.ao_flight_drogue:
-                               voice.speak(String.format("max height %d meters",
-                                                         (int) (state.max_height + 0.5)));
-                               break;
+                       if ((old_state == null || old_state.state <= AltosTelemetry.ao_flight_boost) &&
+                           state.state > AltosTelemetry.ao_flight_boost) {
+                               voice.speak("max speed: %d meters per second.",
+                                           (int) (state.max_speed + 0.5));
+                       } else if ((old_state == null || old_state.state < AltosTelemetry.ao_flight_drogue) &&
+                                  state.state >= AltosTelemetry.ao_flight_drogue) {
+                               voice.speak("max height: %d meters.",
+                                           (int) (state.max_height + 0.5));
                        }
                }
                old_state = state;
        }
 
        class DisplayThread extends Thread {
+               IdleThread      idle_thread;
+
                String read() throws InterruptedException { return null; }
 
                void close() { }
@@ -426,7 +429,8 @@ public class AltosUI extends JFrame {
                        String          line;
                        AltosState      state = null;
                        AltosState      old_state = null;
-                       IdleThread      idle_thread = new IdleThread();
+
+                       idle_thread = new IdleThread();
 
                        info_reset();
                        info_finish();
@@ -452,6 +456,11 @@ public class AltosUI extends JFrame {
                                idle_thread.interrupt();
                        }
                }
+
+               public void report() {
+                       if (idle_thread != null)
+                               idle_thread.report(true);
+               }
        }
 
        class DeviceThread extends DisplayThread {
@@ -465,7 +474,6 @@ public class AltosUI extends JFrame {
                void close() {
                        serial.close();
                        serial.remove_monitor(telem);
-                       System.out.println("DisplayThread done");
                }
 
                public DeviceThread(AltosSerial s) {
@@ -482,11 +490,13 @@ public class AltosUI extends JFrame {
                        try {
                                serial_line.open(device);
                                DeviceThread thread = new DeviceThread(serial_line);
+                               serial_line.set_channel(AltosPreferences.channel());
                                run_display(thread);
                        } catch (FileNotFoundException ee) {
                                JOptionPane.showMessageDialog(AltosUI.this,
-                                                             device.getPath(),
-                                                             "Cannot open serial port",
+                                                             String.format("Cannot open device \"%s\"",
+                                                                           device.getPath()),
+                                                             "Cannot open target device",
                                                              JOptionPane.ERROR_MESSAGE);
                        } catch (IOException ee) {
                                JOptionPane.showMessageDialog(AltosUI.this,
@@ -508,8 +518,9 @@ public class AltosUI extends JFrame {
                while ((c = s.read()) != -1) {
                        if (c == '\r')
                                continue;
-                       if (c == '\n')
+                       if (c == '\n') {
                                return line;
+                       }
                        line = line + (char) c;
                }
                return null;
@@ -545,12 +556,13 @@ public class AltosUI extends JFrame {
                                replay.close();
                        } catch (IOException ee) {
                        }
+                       report();
                }
 
                void update(AltosState state) throws InterruptedException {
                        /* Make it run in realtime after the rocket leaves the pad */
                        if (state.state > AltosTelemetry.ao_flight_pad)
-                               Thread.sleep((int) (state.time_change * 1000));
+                               Thread.sleep((int) (Math.min(state.time_change,10) * 1000));
                }
        }
 
@@ -591,7 +603,7 @@ public class AltosUI extends JFrame {
                        } catch (FileNotFoundException ee) {
                                JOptionPane.showMessageDialog(AltosUI.this,
                                                              filename,
-                                                             "Cannot open serial port",
+                                                             "Cannot open telemetry file",
                                                              JOptionPane.ERROR_MESSAGE);
                        }
                }
@@ -695,12 +707,26 @@ public class AltosUI extends JFrame {
                        menu.setMnemonic(KeyEvent.VK_V);
                        menubar.add(menu);
 
-                       radioitem = new JRadioButtonMenuItem("Enable Voice");
+                       radioitem = new JRadioButtonMenuItem("Enable Voice", AltosPreferences.voice());
                        radioitem.addActionListener(new ActionListener() {
                                        public void actionPerformed(ActionEvent e) {
+                                               JRadioButtonMenuItem item = (JRadioButtonMenuItem) e.getSource();
+                                               boolean enabled = item.isSelected();
+                                               AltosPreferences.set_voice(enabled);
+                                               if (enabled)
+                                                       voice.speak_always("Enable voice.");
+                                               else
+                                                       voice.speak_always("Disable voice.");
                                        }
                                });
                        menu.add(radioitem);
+                       item = new JMenuItem("Test Voice",KeyEvent.VK_T);
+                       item.addActionListener(new ActionListener() {
+                                       public void actionPerformed(ActionEvent e) {
+                                               voice.speak("That's one small step for man; one giant leap for mankind.");
+                                       }
+                               });
+                       menu.add(item);
                }
 
                // Channel menu
@@ -713,12 +739,13 @@ public class AltosUI extends JFrame {
                        for (int c = 0; c <= 9; c++) {
                                radioitem = new JRadioButtonMenuItem(String.format("Channel %1d (%7.3fMHz)", c,
                                                                                   434.550 + c * 0.1),
-                                                                    c == 0);
+                                                                    c == AltosPreferences.channel());
                                radioitem.setActionCommand(String.format("%d", c));
                                radioitem.addActionListener(new ActionListener() {
                                                public void actionPerformed(ActionEvent e) {
-                                                       System.out.println("Command: " + e.getActionCommand() + " param: " +
-                                                                          e.paramString());
+                                                       int new_channel = Integer.parseInt(e.getActionCommand());
+                                                       AltosPreferences.set_channel(new_channel);
+                                                       serial_line.set_channel(new_channel);
                                                }
                                        });
                                menu.add(radioitem);
index e4ea99a2220d80a8abbe3a83a602486a7a45f236..ebe9d5a81c0d5c8b1cab4084db70925dc59049b8 100644 (file)
@@ -39,7 +39,8 @@ public class AltosVoice implements Runnable {
                } catch (InterruptedException e) {
                }
        }
-       public void speak(String s) {
+
+       public void speak_always(String s) {
                try {
                        if (voice != null)
                                phrases.put(s);
@@ -47,6 +48,15 @@ public class AltosVoice implements Runnable {
                }
        }
 
+       public void speak(String s) {
+               if (AltosPreferences.voice())
+                       speak_always(s);
+       }
+
+       public void speak(String format, Object... parameters) {
+               speak(String.format(format, parameters));
+       }
+
        public AltosVoice () {
                voice_manager = VoiceManager.getInstance();
                voice = voice_manager.getVoice(voice_name);
@@ -55,7 +65,6 @@ public class AltosVoice implements Runnable {
                        phrases = new LinkedBlockingQueue<String> ();
                        thread = new Thread(this);
                        thread.start();
-                       speak("Rocket Flight Monitor Ready");
                } else {
                        System.out.printf("Voice manager failed to open %s\n", voice_name);
                        Voice[] voices = voice_manager.getVoices();
index 39d1a70c74067c20b4d9e31ceb64c9ecbd369302..0613a494d82a8b7915142f85006e344ddb55bce1 100644 (file)
@@ -1,6 +1,6 @@
 .SUFFIXES: .java .class
 
-CLASSPATH=..:../libaltos:/usr/share/java/*:/Users/keithp/freetts-1.2.2/lib/*
+CLASSPATH=classes:./*
 CLASSFILES=\
        AltosConvert.class \
        AltosFile.class \
@@ -18,15 +18,42 @@ CLASSFILES=\
        AltosDeviceDialog.class \
        AltosVoice.class
 
+FREETTSSRC=/home/keithp/src/freetts/freetts-1.2.2
+FREETTSLIB=$(FREETTSSRC)/lib
+FREETTSJAR= \
+       cmudict04.jar \
+       cmulex.jar \
+       cmu_time_awb.jar \
+       cmutimelex.jar \
+       cmu_us_kal.jar \
+       en_us.jar \
+       freetts.jar \
+       freetts-jsapi10.jar \
+       jsapi.jar
+
 JAVAFLAGS=-Xlint:unchecked
 
-all: $(CLASSFILES) altosui.jar
+all: altosui.jar
+
+$(CLASSFILES):
 
 .java.class:
        javac -encoding UTF8 -classpath "$(CLASSPATH)" $(JAVAFLAGS) $*.java
 
-altosui.jar: $(CLASSFILES) Manifest.txt
-       jar cfm $@ altosui/Manifest.txt altosui/*.class libaltosJNI/*.class
+altosui.jar: classes/altosui classes/libaltosJNI $(FREETTSJAR) $(CLASSFILES) Manifest.txt
+       cd ./classes && jar cfm ../$@ altosui/Manifest.txt altosui/*.class libaltosJNI/*.class
+
+classes/altosui:
+       mkdir -p classes
+       ln -s .. classes/altosui
+
+classes/libaltosJNI:
+       mkdir -p classes
+       ln -s ../../libaltos/libaltosJNI classes/libaltosJNI
+
+$(FREETTSJAR):
+       ln -s $(FREETTSLIB)/$@ .
 
 clean:
-       rm -f *.class
+       rm -f *.class $(FREETTSJAR) altosui.jar
+       rm -rf classes
index 7dbaafffc7d9dc2572477d81ac5178c5e7dde521..251ce2a014363294990050744e2f61c607b202e8 100644 (file)
@@ -1,2 +1,2 @@
 Main-Class: altosui.AltosUI
-Class-Path: /home/keithp/src/freetts/freetts-1.2.2/lib/freetts.jar
+Class-Path: freetts.jar
diff --git a/ao-tools/altosui/voices.txt b/ao-tools/altosui/voices.txt
deleted file mode 100644 (file)
index e8825fc..0000000
+++ /dev/null
@@ -1 +0,0 @@
-com.sun.speech.freetts.en.us.cmu_us_kal.KevinVoiceDirectory
index dfff8a8dc253f5d531466480c112140bd160de7b..5dd756dac48e21671d1f66f0615cfc2abfdce8af 100644 (file)
--- a/src/ao.h
+++ b/src/ao.h
@@ -562,7 +562,7 @@ struct ao_log_record {
 
 /* Write a record to the eeprom log */
 void
-ao_log_data(struct ao_log_record *log);
+ao_log_data(__xdata struct ao_log_record *log) __reentrant;
 
 /* Flush the log */
 void
index 44ce90e0a943c88eb734e8d0eb52518198ffbedd..d550d40850cde2a8eef8b71c8d4828f7ea49306c 100644 (file)
@@ -23,7 +23,7 @@ static __xdata uint8_t        ao_log_running;
 static __xdata uint8_t ao_log_mutex;
 
 static uint8_t
-ao_log_csum(uint8_t *b)
+ao_log_csum(__xdata uint8_t *b) __reentrant
 {
        uint8_t sum = 0x5a;
        uint8_t i;
@@ -34,11 +34,11 @@ ao_log_csum(uint8_t *b)
 }
 
 void
-ao_log_data(struct ao_log_record *log)
+ao_log_data(__xdata struct ao_log_record *log) __reentrant
 {
        /* set checksum */
        log->csum = 0;
-       log->csum = ao_log_csum((uint8_t *) log);
+       log->csum = ao_log_csum((__xdata uint8_t *) log);
        ao_mutex_get(&ao_log_mutex); {
                if (ao_log_running) {
                        ao_ee_write(ao_log_current_pos,