altosui: Add primitive UI for TeleLaunch
authorKeith Packard <keithp@keithp.com>
Tue, 2 Aug 2011 00:08:24 +0000 (17:08 -0700)
committerKeith Packard <keithp@keithp.com>
Sat, 27 Aug 2011 19:45:36 +0000 (12:45 -0700)
Display status along with arm and fire buttons.

Signed-off-by: Keith Packard <keithp@keithp.com>
altosui/AltosLaunch.java [new file with mode: 0644]
altosui/AltosLaunchUI.java [new file with mode: 0644]
altosui/AltosPreferences.java
altosui/Makefile.am
src/ao_radio_cmac.c

diff --git a/altosui/AltosLaunch.java b/altosui/AltosLaunch.java
new file mode 100644 (file)
index 0000000..77f681b
--- /dev/null
@@ -0,0 +1,201 @@
+/*
+ * Copyright © 2010 Keith Packard <keithp@keithp.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; version 2 of the License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
+ */
+
+package altosui;
+
+import java.io.*;
+import java.util.concurrent.*;
+import java.awt.*;
+import java.awt.event.*;
+import javax.swing.*;
+import javax.swing.filechooser.FileNameExtensionFilter;
+import javax.swing.table.*;
+import javax.swing.event.*;
+
+public class AltosLaunch {
+       AltosDevice     device;
+       AltosSerial     serial;
+       boolean         serial_started;
+       int             launcher_serial;
+       int             launcher_channel;
+       int             rssi;
+
+       final static int        Unknown = -1;
+       final static int        Good = 0;
+       final static int        Bad = 1;
+
+       int             armed;
+       int             igniter;
+
+       private void start_serial() throws InterruptedException {
+               serial_started = true;
+       }
+
+       private void stop_serial() throws InterruptedException {
+               if (!serial_started)
+                       return;
+               serial_started = false;
+               if (serial == null)
+                       return;
+       }
+
+       class string_ref {
+               String  value;
+
+               public String get() {
+                       return value;
+               }
+               public void set(String i) {
+                       value = i;
+               }
+               public string_ref() {
+                       value = null;
+               }
+       }
+
+       private boolean get_string(String line, String label, string_ref s) {
+               if (line.startsWith(label)) {
+                       String  quoted = line.substring(label.length()).trim();
+
+                       if (quoted.startsWith("\""))
+                               quoted = quoted.substring(1);
+                       if (quoted.endsWith("\""))
+                               quoted = quoted.substring(0,quoted.length()-1);
+                       s.set(quoted);
+                       return true;
+               } else {
+                       return false;
+               }
+       }
+
+       public boolean status() throws InterruptedException, TimeoutException {
+               boolean ok = false;
+               if (serial == null)
+                       return false;
+               string_ref status_name = new string_ref();
+               start_serial();
+               serial.printf("l %d %d\n", launcher_serial, launcher_channel);
+               for (;;) {
+                       String line = serial.get_reply(20000);
+                       if (line == null)
+                               throw new TimeoutException();
+                       if (get_string(line, "Rssi: ", status_name)) {
+                               try {
+                                       rssi = Altos.fromdec(status_name.get());
+                               } catch (NumberFormatException ne) {
+                               }
+                               break;
+                       } else if (get_string(line, "Armed: ", status_name)) {
+                               armed = Good;
+                               String status = status_name.get();
+                               if (status.startsWith("igniter good"))
+                                       igniter = Good;
+                               else if (status.startsWith("igniter bad"))
+                                       igniter = Bad;
+                               else
+                                       igniter = Unknown;
+                               ok = true;
+                       } else if (get_string(line, "Disarmed: ", status_name)) {
+                               armed = Bad;
+                               if (status_name.get().startsWith("igniter good"))
+                                       igniter = Good;
+                               else if (status_name.get().startsWith("igniter bad"))
+                                       igniter = Bad;
+                               else
+                                       igniter = Unknown;
+                               ok = true;
+                       } else if (get_string(line, "Error ", status_name)) {
+                               armed = Unknown;
+                               igniter = Unknown;
+                               ok = false;
+                               break;
+                       }
+               }
+               stop_serial();
+               if (!ok) {
+                       armed = Unknown;
+                       igniter = Unknown;
+               }
+               return ok;
+       }
+
+       public static String status_string(int status) {
+               switch (status) {
+               case Good:
+                       return "good";
+               case Bad:
+                       return "open";
+               }
+               return "unknown";
+       }
+
+       public void arm() {
+               if (serial == null)
+                       return;
+               try {
+                       start_serial();
+                       serial.printf("a %d %d\n", launcher_serial, launcher_channel);
+                       serial.flush_output();
+               } catch (InterruptedException ie) {
+               } finally {
+                       try {
+                               stop_serial();
+                       } catch (InterruptedException ie) {
+                       }
+               }
+       }
+
+       public void fire() {
+               if (serial == null)
+                       return;
+               try {
+                       start_serial();
+                       serial.printf("i %d %d\n", launcher_serial, launcher_channel);
+                       serial.flush_output();
+               } catch (InterruptedException ie) {
+               } finally {
+                       try {
+                               stop_serial();
+                       } catch (InterruptedException ie) {
+                       }
+               }
+       }
+
+       public void close() {
+               try {
+                       stop_serial();
+               } catch (InterruptedException ie) {
+               }
+               serial.close();
+               serial = null;
+       }
+
+       public void set_frame(Frame frame) {
+               serial.set_frame(frame);
+       }
+
+       public void set_remote(int in_serial, int in_channel) {
+               launcher_serial = in_serial;
+               launcher_channel = in_channel;
+       }
+
+       public AltosLaunch(AltosDevice in_device) throws FileNotFoundException, AltosSerialInUseException {
+
+               device = in_device;
+               serial = new AltosSerial(device);
+       }
+}
\ No newline at end of file
diff --git a/altosui/AltosLaunchUI.java b/altosui/AltosLaunchUI.java
new file mode 100644 (file)
index 0000000..4e630af
--- /dev/null
@@ -0,0 +1,518 @@
+/*
+ * Copyright © 2010 Keith Packard <keithp@keithp.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; version 2 of the License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
+ */
+
+package altosui;
+
+import java.awt.*;
+import java.awt.event.*;
+import javax.swing.*;
+import javax.swing.filechooser.FileNameExtensionFilter;
+import javax.swing.table.*;
+import javax.swing.event.*;
+import java.io.*;
+import java.util.*;
+import java.text.*;
+import java.util.prefs.*;
+import java.util.concurrent.*;
+
+class FireButton extends JButton {
+       protected void processMouseEvent(MouseEvent e) {
+               super.processMouseEvent(e);
+               switch (e.getID()) {
+               case MouseEvent.MOUSE_PRESSED:
+                       if (actionListener != null)
+                               actionListener.actionPerformed(new ActionEvent(this, e.getID(), "fire_down"));
+                       break;
+               case MouseEvent.MOUSE_RELEASED:
+                       if (actionListener != null)
+                               actionListener.actionPerformed(new ActionEvent(this, e.getID(), "fire_up"));
+                       break;
+               }
+       }
+
+       public FireButton(String s) {
+               super(s);
+       }
+}
+
+public class AltosLaunchUI
+       extends JDialog
+       implements ActionListener
+{
+       AltosDevice     device;
+       JFrame          owner;
+       JLabel          label;
+
+       int             radio_channel;
+       JLabel          radio_channel_label;
+       JTextField      radio_channel_text;
+
+       int             launcher_serial;
+       JLabel          launcher_serial_label;
+       JTextField      launcher_serial_text;
+
+       int             launcher_channel;
+       JLabel          launcher_channel_label;
+       JTextField      launcher_channel_text;
+
+       JLabel          armed_label;
+       JLabel          armed_status_label;
+       JLabel          igniter;
+       JLabel          igniter_status_label;
+       JToggleButton   arm;
+       FireButton      fire;
+       javax.swing.Timer       arm_timer;
+       javax.swing.Timer       fire_timer;
+
+       boolean         firing;
+       boolean         armed;
+       int             armed_status;
+       int             igniter_status;
+       int             rssi;
+
+       final static int        arm_timeout = 1 * 1000;
+       final static int        fire_timeout = 250;
+
+       int             armed_count;
+
+       LinkedBlockingQueue<String>     command_queue;
+
+       class LaunchHandler implements Runnable {
+               AltosLaunch     launch;
+               JFrame          owner;
+
+               void send_exception(Exception e) {
+                       final Exception f_e = e;
+                       Runnable r = new Runnable() {
+                                       public void run() {
+                                               launch_exception(f_e);
+                                       }
+                               };
+                       SwingUtilities.invokeLater(r);
+               }
+
+               public void run () {
+                       try {
+                               launch = new AltosLaunch(device);
+                       } catch (Exception e) {
+                               send_exception(e);
+                               return;
+                       }
+                       launch.set_frame(owner);
+                       launch.set_remote(launcher_serial, launcher_channel);
+
+                       for (;;) {
+                               Runnable        r;
+
+                               try {
+                                       String          command = command_queue.take();
+                                       String          reply = null;
+
+                                       if (command.equals("get_status")) {
+                                               launch.status();
+                                               reply = "status";
+                                               armed_status = launch.armed;
+                                               igniter_status = launch.igniter;
+                                               rssi = launch.rssi;
+                                       } else if (command.equals("set_remote")) {
+                                               launch.set_remote(launcher_serial, launcher_channel);
+                                               reply = "remote set";
+                                       } else if (command.equals("arm")) {
+                                               launch.arm();
+                                               reply = "armed";
+                                       } else if (command.equals("fire")) {
+                                               launch.fire();
+                                               reply = "fired";
+                                       } else if (command.equals("quit")) {
+                                               launch.close();
+                                               break;
+                                       } else {
+                                               throw new ParseException(String.format("invalid command %s", command), 0);
+                                       }
+                                       final String f_reply = reply;
+                                       r = new Runnable() {
+                                                       public void run() {
+                                                               launch_reply(f_reply);
+                                                       }
+                                               };
+                                       SwingUtilities.invokeLater(r);
+                               } catch (Exception e) {
+                                       send_exception(e);
+                               }
+                       }
+               }
+
+               public LaunchHandler(JFrame in_owner) {
+                       owner = in_owner;
+               }
+       }
+
+       void launch_exception(Exception e) {
+               if (e instanceof FileNotFoundException) {
+                       JOptionPane.showMessageDialog(owner,
+                                                     String.format("Cannot open device \"%s\"",
+                                                                   device.toShortString()),
+                                                     "Cannot open target device",
+                                                     JOptionPane.ERROR_MESSAGE);
+               } else if (e instanceof AltosSerialInUseException) {
+                       JOptionPane.showMessageDialog(owner,
+                                                     String.format("Device \"%s\" already in use",
+                                                                   device.toShortString()),
+                                                     "Device in use",
+                                                     JOptionPane.ERROR_MESSAGE);
+               } else if (e instanceof IOException) {
+                       IOException ee = (IOException) e;
+                       JOptionPane.showMessageDialog(owner,
+                                                     device.toShortString(),
+                                                     ee.getLocalizedMessage(),
+                                                     JOptionPane.ERROR_MESSAGE);
+               } else {
+                       JOptionPane.showMessageDialog(owner,
+                                                     String.format("Connection to \"%s\" failed",
+                                                                   device.toShortString()),
+                                                     "Connection Failed",
+                                                     JOptionPane.ERROR_MESSAGE);
+               }
+               close();
+       }
+
+       void launch_reply(String reply) {
+               if (reply == null)
+                       return;
+               if (reply.equals("remote set"))
+                       poll_launch_status();
+               if (reply.equals("status")) {
+                       set_launch_status();
+               }
+       }
+
+       void set_arm_text() {
+               if (arm.isSelected())
+                       arm.setText(String.format("%d", armed_count));
+               else
+                       arm.setText("Arm");
+       }
+
+       void start_arm_timer() {
+               armed_count = 30;
+               set_arm_text();
+       }
+
+       void stop_arm_timer() {
+               armed_count = 0;
+               armed = false;
+               arm.setSelected(false);
+               fire.setEnabled(false);
+               set_arm_text();
+       }
+
+       void cancel () {
+               fire.setEnabled(false);
+               firing = false;
+               stop_arm_timer();
+       }
+
+       void send_command(String command) {
+               try {
+                       command_queue.put(command);
+               } catch (Exception ex) {
+                       launch_exception(ex);
+               }
+       }
+
+       boolean getting_status = false;
+
+       void set_launch_status() {
+               getting_status = false;
+               armed_status_label.setText(String.format("\"%s\"", AltosLaunch.status_string(armed_status)));
+               igniter_status_label.setText(String.format("\"%s\"", AltosLaunch.status_string(igniter_status)));
+       }
+
+       void poll_launch_status() {
+               if (!getting_status && !firing && !armed) {
+                       getting_status = true;
+                       send_command("get_status");
+               }
+       }
+
+       void fired() {
+               firing = false;
+               cancel();
+       }
+
+       void close() {
+               send_command("quit");
+               arm_timer.stop();
+               setVisible(false);
+               dispose();
+       }
+
+       void tick_arm_timer() {
+               if (armed_count > 0) {
+                       --armed_count;
+                       if (armed_count <= 0) {
+                               armed_count = 0;
+                               cancel();
+                       } else {
+                               if (!firing) {
+                                       send_command("arm");
+                                       set_arm_text();
+                               }
+                       }
+               }
+               poll_launch_status();
+       }
+
+       void arm() {
+               if (arm.isSelected()) {
+                       fire.setEnabled(true);
+                       start_arm_timer();
+                       if (!firing)
+                               send_command("arm");
+                       armed = true;
+               } else
+                       cancel();
+       }
+
+       void fire_more() {
+               if (firing)
+                       send_command("fire");
+       }
+
+       void fire_down() {
+               if (arm.isEnabled() && arm.isSelected() && armed_count > 0) {
+                       firing = true;
+                       fire_more();
+                       fire_timer.restart();
+               }
+       }
+
+       void fire_up() {
+               firing = false;
+               fire_timer.stop();
+       }
+
+       void set_radio() {
+               try {
+                       radio_channel = Integer.parseInt(radio_channel_text.getText());
+               } catch (NumberFormatException ne) {
+                       radio_channel_text.setText(String.format("%d", radio_channel));
+               }
+       }
+
+       void set_serial() {
+               try {
+                       launcher_serial = Integer.parseInt(launcher_serial_text.getText());
+                       AltosPreferences.set_launcher_serial(launcher_serial);
+                       send_command("set_remote");
+               } catch (NumberFormatException ne) {
+                       launcher_serial_text.setText(String.format("%d", launcher_serial));
+               }
+       }
+
+       void set_channel() {
+               try {
+                       launcher_channel = Integer.parseInt(launcher_channel_text.getText());
+                       AltosPreferences.set_launcher_serial(launcher_channel);
+                       send_command("set_remote");
+               } catch (NumberFormatException ne) {
+                       launcher_channel_text.setText(String.format("%d", launcher_channel));
+               }
+       }
+
+       public void actionPerformed(ActionEvent e) {
+               String cmd = e.getActionCommand();
+               System.out.printf("cmd %s\n", cmd);
+               if (cmd.equals("armed") || cmd.equals("igniter")) {
+                       stop_arm_timer();
+               }
+
+               if (cmd.equals("arm"))
+                       arm();
+               if (cmd.equals("tick_arm"))
+                       tick_arm_timer();
+               if (cmd.equals("close"))
+                       close();
+               if (cmd.equals("fire_down"))
+                       fire_down();
+               if (cmd.equals("fire_up"))
+                       fire_up();
+               if (cmd.equals("tick_fire"))
+                       fire_more();
+               if (cmd.equals("new_serial"))
+                       set_serial();
+               if (cmd.equals("new_channel"))
+                       set_channel();
+       }
+
+       /* A window listener to catch closing events and tell the config code */
+       class ConfigListener extends WindowAdapter {
+               AltosLaunchUI   ui;
+
+               public ConfigListener(AltosLaunchUI this_ui) {
+                       ui = this_ui;
+               }
+
+               public void windowClosing(WindowEvent e) {
+                       ui.actionPerformed(new ActionEvent(e.getSource(),
+                                                          ActionEvent.ACTION_PERFORMED,
+                                                          "close"));
+               }
+       }
+
+       private boolean open() {
+               command_queue = new LinkedBlockingQueue<String>();
+
+               device = AltosDeviceDialog.show(owner, Altos.product_any);
+               if (device != null) {
+                               LaunchHandler   handler = new LaunchHandler(owner);
+                               Thread          t = new Thread(handler);
+                               t.start();
+                               return true;
+               }
+               return false;
+       }
+
+       public AltosLaunchUI(JFrame in_owner) {
+
+               launcher_channel = AltosPreferences.launcher_channel();
+               launcher_serial = AltosPreferences.launcher_serial();
+               owner = in_owner;
+               armed_status = AltosLaunch.Unknown;
+               igniter_status = AltosLaunch.Unknown;
+
+               if (!open())
+                       return;
+
+               Container               pane = getContentPane();
+               GridBagConstraints      c = new GridBagConstraints();
+               Insets                  i = new Insets(4,4,4,4);
+
+               arm_timer = new javax.swing.Timer(arm_timeout, this);
+               arm_timer.setActionCommand("tick_arm");
+               arm_timer.restart();
+
+               fire_timer = new javax.swing.Timer(fire_timeout, this);
+               fire_timer.setActionCommand("tick_fire");
+
+               owner = in_owner;
+
+               pane.setLayout(new GridBagLayout());
+
+               c.fill = GridBagConstraints.NONE;
+               c.anchor = GridBagConstraints.CENTER;
+               c.insets = i;
+               c.weightx = 1;
+               c.weighty = 1;
+
+               c.gridx = 0;
+               c.gridy = 0;
+               c.gridwidth = 2;
+               c.anchor = GridBagConstraints.CENTER;
+               label = new JLabel ("Launch Controller");
+               pane.add(label, c);
+
+               c.gridx = 0;
+               c.gridy = 1;
+               c.gridwidth = 1;
+               c.anchor = GridBagConstraints.WEST;
+               launcher_serial_label = new JLabel("Launcher Serial");
+               pane.add(launcher_serial_label, c);
+
+               c.gridx = 1;
+               c.gridy = 1;
+               c.gridwidth = 1;
+               c.anchor = GridBagConstraints.WEST;
+               launcher_serial_text = new JTextField(7);
+               launcher_serial_text.setText(String.format("%d", launcher_serial));
+               launcher_serial_text.setActionCommand("new_serial");
+               launcher_serial_text.addActionListener(this);
+               pane.add(launcher_serial_text, c);
+
+               c.gridx = 0;
+               c.gridy = 2;
+               c.gridwidth = 1;
+               c.anchor = GridBagConstraints.WEST;
+               launcher_channel_label = new JLabel("Launcher Channel");
+               pane.add(launcher_channel_label, c);
+
+               c.gridx = 1;
+               c.gridy = 2;
+               c.gridwidth = 1;
+               c.anchor = GridBagConstraints.WEST;
+               launcher_channel_text = new JTextField(7);
+               launcher_channel_text.setText(String.format("%d", launcher_channel));
+               launcher_channel_text.setActionCommand("new_channel");
+               launcher_channel_text.addActionListener(this);
+               pane.add(launcher_channel_text, c);
+
+               c.gridx = 0;
+               c.gridy = 3;
+               c.gridwidth = 1;
+               c.anchor = GridBagConstraints.WEST;
+               armed_label = new JLabel ("Armed");
+               pane.add(armed_label, c);
+
+               c.gridx = 1;
+               c.gridy = 3;
+               c.gridwidth = 1;
+               c.anchor = GridBagConstraints.WEST;
+               armed_status_label = new JLabel();
+               pane.add(armed_status_label, c);
+
+               c.gridx = 0;
+               c.gridy = 4;
+               c.gridwidth = 1;
+               c.anchor = GridBagConstraints.WEST;
+               igniter = new JLabel ("Igniter");
+               pane.add(igniter, c);
+
+               c.gridx = 1;
+               c.gridy = 4;
+               c.gridwidth = 1;
+               c.anchor = GridBagConstraints.WEST;
+               igniter_status_label = new JLabel();
+               pane.add(igniter_status_label, c);
+
+               c.gridx = 0;
+               c.gridy = 5;
+               c.gridwidth = 1;
+               c.anchor = GridBagConstraints.CENTER;
+               arm = new JToggleButton ("Arm");
+               pane.add(arm, c);
+               arm.addActionListener(this);
+               arm.setActionCommand("arm");
+               arm.setEnabled(true);
+
+               c.gridx = 1;
+               c.gridy = 5;
+               c.gridwidth = 1;
+               c.anchor = GridBagConstraints.CENTER;
+               fire = new FireButton ("Fire");
+               fire.setEnabled(false);
+               pane.add(fire, c);
+               fire.addActionListener(this);
+               fire.setActionCommand("fire");
+
+               pack();
+               setLocationRelativeTo(owner);
+
+               addWindowListener(new ConfigListener(this));
+
+               setVisible(true);
+       }
+}
\ No newline at end of file
index 716559ab7e0a9dc06e7c94f5a18a225eb6baa044..48aed44125c6c93d74d16d5493e7c4461e8f7b68 100644 (file)
@@ -58,6 +58,12 @@ class AltosPreferences {
        /* font size preferences name */
        final static String fontSizePreference = "FONT-SIZE";
 
        /* font size preferences name */
        final static String fontSizePreference = "FONT-SIZE";
 
+       /* Launcher serial preference name */
+       final static String launcherSerialPreference = "LAUNCHER-SERIAL";
+
+       /* Launcher channel prefernce name */
+       final static String launcherChannelPreference = "LAUNCHER-CHANNEL";
+       
        /* Default logdir is ~/TeleMetrum */
        final static String logdirName = "TeleMetrum";
 
        /* Default logdir is ~/TeleMetrum */
        final static String logdirName = "TeleMetrum";
 
@@ -143,6 +149,9 @@ class AltosPreferences {
                        node.put(String.format(description_format, i), frequencies[i].description);
                }
        }
                        node.put(String.format(description_format, i), frequencies[i].description);
                }
        }
+       static int launcher_serial;
+
+       static int launcher_channel;
 
        public static void init() {
                preferences = Preferences.userRoot().node("/org/altusmetrum/altosui");
 
        public static void init() {
                preferences = Preferences.userRoot().node("/org/altusmetrum/altosui");
@@ -176,6 +185,10 @@ class AltosPreferences {
                font_size = preferences.getInt(fontSizePreference, Altos.font_size_medium);
                Altos.set_fonts(font_size);
 
                font_size = preferences.getInt(fontSizePreference, Altos.font_size_medium);
                Altos.set_fonts(font_size);
 
+               launcher_serial = preferences.getInt(launcherSerialPreference, 0);
+
+               launcher_channel = preferences.getInt(launcherChannelPreference, 0);
+
                String firmwaredir_string = preferences.get(firmwaredirPreference, null);
                if (firmwaredir_string != null)
                        firmwaredir = new File(firmwaredir_string);
                String firmwaredir_string = preferences.get(firmwaredirPreference, null);
                if (firmwaredir_string != null)
                        firmwaredir = new File(firmwaredir_string);
@@ -390,6 +403,32 @@ class AltosPreferences {
                return serial_debug;
        }
 
                return serial_debug;
        }
 
+       public static void set_launcher_serial(int new_launcher_serial) {
+               launcher_serial = new_launcher_serial;
+               System.out.printf("set launcher serial to %d\n", new_launcher_serial);
+               synchronized (preferences) {
+                       preferences.putInt(launcherSerialPreference, launcher_serial);
+                       flush_preferences();
+               }
+       }
+
+       public static int launcher_serial() {
+               return launcher_serial;
+       }
+
+       public static void set_launcher_channel(int new_launcher_channel) {
+               launcher_channel = new_launcher_channel;
+               System.out.printf("set launcher channel to %d\n", new_launcher_channel);
+               synchronized (preferences) {
+                       preferences.putInt(launcherChannelPreference, launcher_channel);
+                       flush_preferences();
+               }
+       }
+
+       public static int launcher_channel() {
+               return launcher_channel;
+       }
+       
        public static Preferences bt_devices() {
                return preferences.node("bt_devices");
        }
        public static Preferences bt_devices() {
                return preferences.node("bt_devices");
        }
index ba1c830c6c72c42c16281a9768a2175d489eb3f6..e2e42d84a308200c1de300d67e5c5e25eb86aaab 100644 (file)
@@ -68,6 +68,8 @@ altosui_JAVA = \
        AltosIdleMonitorUI.java \
        AltosIgnite.java \
        AltosIgniteUI.java \
        AltosIdleMonitorUI.java \
        AltosIgnite.java \
        AltosIgniteUI.java \
+       AltosLaunch.java \
+       AltosLaunchUI.java \
        AltosInfoTable.java \
        AltosKML.java \
        AltosLanded.java \
        AltosInfoTable.java \
        AltosKML.java \
        AltosLanded.java \
index c2757b1629955a847180094172956bdbd49fedc2..41fbbe1fe7a5b7766e938a4876be56fc4844f1fa 100644 (file)
@@ -21,6 +21,7 @@
 #define AO_CMAC_MAX_LEN                (128 - AO_CMAC_KEY_LEN)
 
 static __xdata uint8_t ao_radio_cmac_mutex;
 #define AO_CMAC_MAX_LEN                (128 - AO_CMAC_KEY_LEN)
 
 static __xdata uint8_t ao_radio_cmac_mutex;
+__pdata int16_t ao_radio_cmac_rssi;
 static __xdata uint8_t cmac_data[AO_CMAC_MAX_LEN + AO_CMAC_KEY_LEN + 2 + AO_CMAC_KEY_LEN];
 static __pdata uint8_t ao_radio_cmac_len;
 
 static __xdata uint8_t cmac_data[AO_CMAC_MAX_LEN + AO_CMAC_KEY_LEN + 2 + AO_CMAC_KEY_LEN];
 static __pdata uint8_t ao_radio_cmac_len;
 
@@ -114,9 +115,12 @@ radio_cmac_recv(uint8_t len, uint16_t timeout) __reentrant
        i = ao_radio_recv(cmac_data, len + AO_CMAC_KEY_LEN + 2);
        ao_clear_alarm();
 
        i = ao_radio_recv(cmac_data, len + AO_CMAC_KEY_LEN + 2);
        ao_clear_alarm();
 
-       if (!i)
+       if (!i) {
+               ao_radio_cmac_rssi = 0;
                return AO_RADIO_CMAC_TIMEOUT;
                return AO_RADIO_CMAC_TIMEOUT;
+       }
 
 
+       ao_radio_cmac_rssi = (int16_t) (((int8_t) cmac_data[len + AO_CMAC_KEY_LEN]) >> 1) - 74;
        if (!(cmac_data[len + AO_CMAC_KEY_LEN +1] & PKT_APPEND_STATUS_1_CRC_OK))
                return AO_RADIO_CMAC_CRC_ERROR;
 
        if (!(cmac_data[len + AO_CMAC_KEY_LEN +1] & PKT_APPEND_STATUS_1_CRC_OK))
                return AO_RADIO_CMAC_CRC_ERROR;
 
@@ -221,33 +225,46 @@ radio_cmac_recv_cmd(void) __reentrant
                printf ("PACKET ");
                for (i = 0; i < len; i++)
                        printf("%02x", cmac_data[i]);
                printf ("PACKET ");
                for (i = 0; i < len; i++)
                        printf("%02x", cmac_data[i]);
-               printf ("\n");
+               printf (" %d\n", ao_radio_cmac_rssi);
        } else
        } else
-               printf ("ERROR %d\n", i);
+               printf ("ERROR %d %d\n", i, ao_radio_cmac_rssi);
        ao_mutex_put(&ao_radio_cmac_mutex);
 }
 
 static __xdata struct ao_launch_command        command;
 static __xdata struct ao_launch_query  query;
        ao_mutex_put(&ao_radio_cmac_mutex);
 }
 
 static __xdata struct ao_launch_command        command;
 static __xdata struct ao_launch_query  query;
+static pdata uint16_t  launch_serial;
+static pdata uint8_t   launch_channel;
+static pdata uint16_t  tick_offset;
 
 
+static void
+launch_args(void) __reentrant
+{
+       ao_cmd_decimal();
+       launch_serial = ao_cmd_lex_i;
+       ao_cmd_decimal();
+       launch_channel = ao_cmd_lex_i;
+}
 
 static int8_t
 
 static int8_t
-launch_query(uint16_t serial, uint8_t channel)
+launch_query(void)
 {
        uint8_t i;
        int8_t  r = AO_RADIO_CMAC_OK;
 
 {
        uint8_t i;
        int8_t  r = AO_RADIO_CMAC_OK;
 
+       tick_offset = ao_time();
        for (i = 0; i < 10; i++) {
                printf ("."); flush();
                command.tick = ao_time();
        for (i = 0; i < 10; i++) {
                printf ("."); flush();
                command.tick = ao_time();
-               command.serial = serial;
+               command.serial = launch_serial;
                command.cmd = AO_LAUNCH_QUERY;
                command.cmd = AO_LAUNCH_QUERY;
-               command.channel = channel;
+               command.channel = launch_channel;
                ao_radio_cmac_send(&command, sizeof (command));
                r = ao_radio_cmac_recv(&query, sizeof (query), AO_MS_TO_TICKS(500));
                if (r == AO_RADIO_CMAC_OK)
                        break;
        }
                ao_radio_cmac_send(&command, sizeof (command));
                r = ao_radio_cmac_recv(&query, sizeof (query), AO_MS_TO_TICKS(500));
                if (r == AO_RADIO_CMAC_OK)
                        break;
        }
+       tick_offset -= query.tick;
        printf("\n"); flush();
        return r;
 }
        printf("\n"); flush();
        return r;
 }
@@ -255,17 +272,12 @@ launch_query(uint16_t serial, uint8_t channel)
 static void
 launch_report_cmd(void) __reentrant
 {
 static void
 launch_report_cmd(void) __reentrant
 {
-       uint8_t         channel;
-       uint16_t        serial;
        int8_t          r;
 
        int8_t          r;
 
-       ao_cmd_decimal();
-       serial = ao_cmd_lex_i;
-       ao_cmd_decimal();
-       channel = ao_cmd_lex_i;
+       launch_args();
        if (ao_cmd_status != ao_cmd_success)
                return;
        if (ao_cmd_status != ao_cmd_success)
                return;
-       r = launch_query(serial, channel);
+       r = launch_query();
        switch (r) {
        case AO_RADIO_CMAC_OK:
                if (query.valid) {
        switch (r) {
        case AO_RADIO_CMAC_OK:
                if (query.valid) {
@@ -273,24 +285,25 @@ launch_report_cmd(void) __reentrant
                        case ao_igniter_ready:
                        case ao_igniter_active:
                                printf ("Armed: ");
                        case ao_igniter_ready:
                        case ao_igniter_active:
                                printf ("Armed: ");
-                               switch (query.igniter_status) {
-                               default:
-                                       printf("unknown status\n");
-                                       break;
-                               case ao_igniter_ready:
-                                       printf("igniter good\n");
-                                       break;
-                               case ao_igniter_open:
-                                       printf("igniter bad\n");
-                                       break;
-                               }
                                break;
                        default:
                                break;
                        default:
-                               printf("Disarmed\n");
+                               printf("Disarmed: ");
+                       }
+                       switch (query.igniter_status) {
+                       default:
+                               printf("unknown\n");
+                               break;
+                       case ao_igniter_ready:
+                               printf("igniter good\n");
+                               break;
+                       case ao_igniter_open:
+                               printf("igniter bad\n");
+                               break;
                        }
                } else {
                        }
                } else {
-                       printf("Invalid channel %d\n", channel);
+                       printf("Invalid channel %d\n", launch_channel);
                }
                }
+               printf("Rssi: %d\n", ao_radio_cmac_rssi);
                break;
        default:
                printf("Error %d\n", r);
                break;
        default:
                printf("Error %d\n", r);
@@ -298,56 +311,91 @@ launch_report_cmd(void) __reentrant
        }
 }
 
        }
 }
 
+static void
+launch_arm(void) __reentrant
+{
+       command.tick = ao_time() - tick_offset;
+       command.serial = launch_serial;
+       command.cmd = AO_LAUNCH_ARM;
+       command.channel = launch_channel;
+       ao_radio_cmac_send(&command, sizeof (command));
+}
+
+static void
+launch_ignite(void) __reentrant
+{
+       command.tick = ao_time() - tick_offset;
+       command.serial = launch_serial;
+       command.cmd = AO_LAUNCH_FIRE;
+       command.channel = 0;
+       ao_radio_cmac_send(&command, sizeof (command));
+}
+
 static void
 launch_fire_cmd(void) __reentrant
 {
        static __xdata struct ao_launch_command command;
 static void
 launch_fire_cmd(void) __reentrant
 {
        static __xdata struct ao_launch_command command;
-       uint8_t         channel;
-       uint16_t        serial;
        uint8_t         secs;
        uint8_t         i;
        int8_t          r;
        uint8_t         secs;
        uint8_t         i;
        int8_t          r;
-       uint16_t        tick_offset;
 
 
-       ao_cmd_decimal();
-       serial = ao_cmd_lex_i;
-       ao_cmd_decimal();
-       channel = ao_cmd_lex_i;
+       launch_args();
        ao_cmd_decimal();
        secs = ao_cmd_lex_i;
        if (ao_cmd_status != ao_cmd_success)
                return;
        ao_cmd_decimal();
        secs = ao_cmd_lex_i;
        if (ao_cmd_status != ao_cmd_success)
                return;
-       tick_offset = ao_time();
-       r = launch_query(serial, channel);
-       tick_offset -= query.tick;
+       r = launch_query();
+       if (r != AO_RADIO_CMAC_OK) {
+               printf("query failed %d\n", r);
+               return;
+       }
 
        for (i = 0; i < 4; i++) {
                printf("arm %d\n", i); flush();
 
        for (i = 0; i < 4; i++) {
                printf("arm %d\n", i); flush();
-               command.tick = ao_time() - tick_offset;
-               command.serial = serial;
-               command.cmd = AO_LAUNCH_ARM;
-               command.channel = channel;
-               ao_radio_cmac_send(&command, sizeof (command));
+               launch_arm();
        }
        }
+
        secs = secs * 10 - 5;
        if (secs > 100)
                secs = 100;
        for (i = 0; i < secs; i++) {
                printf("fire %d\n", i); flush();
        secs = secs * 10 - 5;
        if (secs > 100)
                secs = 100;
        for (i = 0; i < secs; i++) {
                printf("fire %d\n", i); flush();
-               command.tick = ao_time() - tick_offset;
-               command.serial = serial;
-               command.cmd = AO_LAUNCH_FIRE;
-               command.channel = 0;
-               ao_radio_cmac_send(&command, sizeof (command));
+               launch_ignite();
                ao_delay(AO_MS_TO_TICKS(100));
        }
 }
 
                ao_delay(AO_MS_TO_TICKS(100));
        }
 }
 
+static void
+launch_arm_cmd(void) __reentrant
+{
+       uint8_t i;
+       int8_t  r;
+       launch_args();
+       r = launch_query();
+       if (r != AO_RADIO_CMAC_OK) {
+               printf("query failed %d\n", r);
+               return;
+       }
+       for (i = 0; i < 4; i++)
+               launch_arm();
+}
+
+static void
+launch_ignite_cmd(void) __reentrant
+{
+       uint8_t i;
+       launch_args();
+       for (i = 0; i < 4; i++)
+               launch_ignite();
+}
+
 static __code struct ao_cmds ao_radio_cmac_cmds[] = {
        { radio_cmac_send_cmd,  "s <length>\0Send AES-CMAC packet. Bytes to send follow on next line" },
        { radio_cmac_recv_cmd,  "S <length> <timeout>\0Receive AES-CMAC packet. Timeout in ms" },
        { launch_report_cmd,    "l <serial> <channel>\0Get remote launch status" },
        { launch_fire_cmd,      "f <serial> <channel> <secs>\0Fire remote igniter" },
 static __code struct ao_cmds ao_radio_cmac_cmds[] = {
        { radio_cmac_send_cmd,  "s <length>\0Send AES-CMAC packet. Bytes to send follow on next line" },
        { radio_cmac_recv_cmd,  "S <length> <timeout>\0Receive AES-CMAC packet. Timeout in ms" },
        { launch_report_cmd,    "l <serial> <channel>\0Get remote launch status" },
        { launch_fire_cmd,      "f <serial> <channel> <secs>\0Fire remote igniter" },
+       { launch_arm_cmd,       "a <serial> <channel>\0Arm remote igniter" },
+       { launch_ignite_cmd,    "i <serial> <channel>\0Pulse remote igniter" },
        { 0, NULL },
 };
 
        { 0, NULL },
 };