From 22d00785188a880700cd372528189a7a15278da9 Mon Sep 17 00:00:00 2001 From: Keith Packard Date: Tue, 9 Nov 2010 14:40:58 -0800 Subject: [PATCH] altosui: Add tab UI with 'pad' mode. This creates a multi-tab interface for flight monitoring and includes a special tab for 'pad' mode. Signed-off-by: Keith Packard --- ao-tools/altosui/AltosFlightUI.java | 16 +- ao-tools/altosui/AltosLed.java | 54 ++++++ ao-tools/altosui/AltosLights.java | 72 ++++++++ ao-tools/altosui/AltosPad.java | 249 ++++++++++++++++++++++++++++ ao-tools/altosui/Makefile.am | 24 ++- icon/grayled.png | Bin 0 -> 1528 bytes icon/grayon.png | Bin 0 -> 1873 bytes icon/greenled.png | Bin 0 -> 2281 bytes icon/greenoff.png | Bin 0 -> 1811 bytes icon/redled.png | Bin 0 -> 2103 bytes icon/redoff.png | Bin 0 -> 1694 bytes 11 files changed, 410 insertions(+), 5 deletions(-) create mode 100644 ao-tools/altosui/AltosLed.java create mode 100644 ao-tools/altosui/AltosLights.java create mode 100644 ao-tools/altosui/AltosPad.java create mode 100644 icon/grayled.png create mode 100644 icon/grayon.png create mode 100644 icon/greenled.png create mode 100644 icon/greenoff.png create mode 100644 icon/redled.png create mode 100644 icon/redoff.png diff --git a/ao-tools/altosui/AltosFlightUI.java b/ao-tools/altosui/AltosFlightUI.java index 11fc2442..a7caf7e9 100644 --- a/ao-tools/altosui/AltosFlightUI.java +++ b/ao-tools/altosui/AltosFlightUI.java @@ -37,6 +37,11 @@ public class AltosFlightUI extends JFrame implements AltosFlightDisplay { AltosDisplayThread thread; private Box vbox; + + JTabbedPane pane; + + AltosPad pad; + private AltosStatusTable flightStatus; private AltosInfoTable flightInfo; @@ -63,10 +68,12 @@ public class AltosFlightUI extends JFrame implements AltosFlightDisplay { } public void reset() { + pad.reset(); flightInfo.clear(); } public void show(AltosState state, int crc_errors) { + pad.show(state, crc_errors); flightStatus.set(state); flightInfo.show(state, crc_errors); } @@ -86,8 +93,15 @@ public class AltosFlightUI extends JFrame implements AltosFlightDisplay { vbox = new Box (BoxLayout.Y_AXIS); vbox.add(flightStatus); + pane = new JTabbedPane(); + + pad = new AltosPad(); + pane.add("Launch Pad", pad); + flightInfo = new AltosInfoTable(); - vbox.add(flightInfo.box()); + pane.add("Table", flightInfo.box()); + + vbox.add(pane); this.add(vbox); diff --git a/ao-tools/altosui/AltosLed.java b/ao-tools/altosui/AltosLed.java new file mode 100644 index 00000000..e08e9960 --- /dev/null +++ b/ao-tools/altosui/AltosLed.java @@ -0,0 +1,54 @@ +/* + * 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 altosui; + +import java.awt.*; +import java.awt.event.*; +import javax.swing.*; +import javax.swing.filechooser.FileNameExtensionFilter; +import javax.swing.table.*; +import java.io.*; +import java.util.*; +import java.text.*; +import java.util.prefs.*; +import java.util.concurrent.LinkedBlockingQueue; + +public class AltosLed extends JLabel { + ImageIcon on, off; + + ImageIcon create_icon(String path) { + java.net.URL imgURL = AltosUI.class.getResource(path); + if (imgURL != null) + return new ImageIcon(imgURL); + System.err.printf("Cannot find icon \"%s\"\n", path); + return null; + } + + public void set(boolean set) { + if (set) + setIcon(on); + else + setIcon(off); + } + + public AltosLed(String on_path, String off_path) { + on = create_icon(on_path); + off = create_icon(off_path); + setIcon(off); + } +} diff --git a/ao-tools/altosui/AltosLights.java b/ao-tools/altosui/AltosLights.java new file mode 100644 index 00000000..2d2a1938 --- /dev/null +++ b/ao-tools/altosui/AltosLights.java @@ -0,0 +1,72 @@ +/* + * 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 altosui; + +import java.awt.*; +import java.awt.event.*; +import javax.swing.*; +import javax.swing.filechooser.FileNameExtensionFilter; +import javax.swing.table.*; +import java.io.*; +import java.util.*; +import java.text.*; +import java.util.prefs.*; +import java.util.concurrent.LinkedBlockingQueue; + +public class AltosLights extends JComponent { + + GridBagLayout gridbag; + + AltosLed red, green; + + ImageIcon create_icon(String path, String description) { + java.net.URL imgURL = AltosUI.class.getResource(path); + if (imgURL != null) + return new ImageIcon(imgURL, description); + System.err.printf("Cannot find icon \"%s\"\n", path); + return null; + } + + public void set (boolean on) { + if (on) { + red.set(false); + green.set(true); + } else { + red.set(true); + green.set(false); + } + } + + public AltosLights() { + GridBagConstraints c; + gridbag = new GridBagLayout(); + setLayout(gridbag); + + c = new GridBagConstraints(); + red = new AltosLed("/redled.png", "/redoff.png"); + c.gridx = 0; c.gridy = 0; + gridbag.setConstraints(red, c); + add(red); + red.set(true); + green = new AltosLed("/greenled.png", "/greenoff.png"); + c.gridx = 1; c.gridy = 0; + gridbag.setConstraints(green, c); + add(green); + green.set(false); + } +} \ No newline at end of file diff --git a/ao-tools/altosui/AltosPad.java b/ao-tools/altosui/AltosPad.java new file mode 100644 index 00000000..133dbed3 --- /dev/null +++ b/ao-tools/altosui/AltosPad.java @@ -0,0 +1,249 @@ +/* + * 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 altosui; + +import java.awt.*; +import java.awt.event.*; +import javax.swing.*; +import javax.swing.filechooser.FileNameExtensionFilter; +import javax.swing.table.*; +import java.io.*; +import java.util.*; +import java.text.*; +import java.util.prefs.*; +import java.util.concurrent.LinkedBlockingQueue; + +public class AltosPad extends JComponent implements AltosFlightDisplay { + GridBagLayout layout; + Font label_font; + Font value_font; + + public class LaunchStatus { + JLabel label; + JLabel value; + AltosLights lights; + + void show(AltosState state, int crc_errors) {} + void reset() { + value.setText("0"); + lights.set(false); + } + + public LaunchStatus (GridBagLayout layout, int y, String text) { + GridBagConstraints c = new GridBagConstraints(); + + lights = new AltosLights(); + c.gridx = 0; c.gridy = y; + c.anchor = GridBagConstraints.CENTER; + c.fill = GridBagConstraints.CENTER; + layout.setConstraints(lights, c); + add(lights); + + label = new JLabel(text); + label.setFont(label_font); + label.setHorizontalAlignment(SwingConstants.LEFT); + c.gridx = 1; c.gridy = y; + c.insets = new Insets(10, 10, 10, 10); + c.anchor = GridBagConstraints.WEST; + c.fill = GridBagConstraints.WEST; + layout.setConstraints(label, c); + add(label); + + value = new JLabel("4.00"); + value.setFont(label_font); + value.setHorizontalAlignment(SwingConstants.RIGHT); + c.gridx = 2; c.gridy = y; + c.anchor = GridBagConstraints.EAST; + c.fill = GridBagConstraints.EAST; + layout.setConstraints(value, c); + add(value); + + } + } + + public class LaunchValue { + JLabel label; + JLabel value; + void show(AltosState state, int crc_errors) {} + + void reset() { + value.setText("0"); + } + public LaunchValue (GridBagLayout layout, int y, String text) { + GridBagConstraints c = new GridBagConstraints(); + + label = new JLabel(text); + label.setFont(label_font); + label.setHorizontalAlignment(SwingConstants.LEFT); + c.gridx = 1; c.gridy = y; + c.insets = new Insets(10, 10, 10, 10); + c.anchor = GridBagConstraints.WEST; + c.fill = GridBagConstraints.WEST; + layout.setConstraints(label, c); + add(label); + + value = new JLabel("4.00"); + value.setFont(label_font); + value.setHorizontalAlignment(SwingConstants.RIGHT); + c.gridx = 2; c.gridy = y; + c.anchor = GridBagConstraints.EAST; + c.fill = GridBagConstraints.EAST; + layout.setConstraints(value, c); + add(value); + } + } + + class Battery extends LaunchStatus { + void show (AltosState state, int crc_errors) { + value.setText(String.format("%4.2f V", state.battery)); + lights.set(state.battery > 3.7); + } + public Battery (GridBagLayout layout, int y) { + super(layout, y, "Battery Voltage"); + } + } + + Battery battery; + + class Apogee extends LaunchStatus { + void show (AltosState state, int crc_errors) { + value.setText(String.format("%4.2f V", state.drogue_sense)); + lights.set(state.drogue_sense > 3.2); + } + public Apogee (GridBagLayout layout, int y) { + super(layout, y, "Apogee Igniter Voltage"); + } + } + + Apogee apogee; + + class Main extends LaunchStatus { + void show (AltosState state, int crc_errors) { + value.setText(String.format("%4.2f V", state.main_sense)); + lights.set(state.main_sense > 3.2); + } + public Main (GridBagLayout layout, int y) { + super(layout, y, "Main Igniter Voltage"); + } + } + + Main main; + + class GPS extends LaunchStatus { + void show (AltosState state, int crc_errors) { + value.setText(String.format("%4d sats", state.gps.nsat)); + lights.set(state.gps_ready); + } + public GPS (GridBagLayout layout, int y) { + super (layout, y, "GPS Status"); + } + } + + GPS gps; + + String pos(double p, String pos, String neg) { + String h = pos; + if (p < 0) { + h = neg; + p = -p; + } + int deg = (int) Math.floor(p); + double min = (p - Math.floor(p)) * 60.0; + return String.format("%s %4d° %9.6f", h, deg, min); + } + + class PadLat extends LaunchValue { + void show (AltosState state, int crc_errors) { + value.setText(pos(state.pad_lat,"N", "S")); + } + public PadLat (GridBagLayout layout, int y) { + super (layout, y, "Pad Latitude"); + } + } + + PadLat pad_lat; + + class PadLon extends LaunchValue { + void show (AltosState state, int crc_errors) { + value.setText(pos(state.pad_lon,"E", "W")); + } + public PadLon (GridBagLayout layout, int y) { + super (layout, y, "Pad Longitude"); + } + } + + PadLon pad_lon; + + class PadAlt extends LaunchValue { + void show (AltosState state, int crc_errors) { + value.setText(String.format("%4.0f m", state.pad_alt)); + } + public PadAlt (GridBagLayout layout, int y) { + super (layout, y, "Pad Altitude"); + } + } + + PadAlt pad_alt; + + public void reset() { + battery.reset(); + apogee.reset(); + main.reset(); + gps.reset(); + pad_lat.reset(); + pad_lon.reset(); + pad_alt.reset(); + } + + public void show(AltosState state, int crc_errors) { + battery.show(state, crc_errors); + apogee.show(state, crc_errors); + main.show(state, crc_errors); + gps.show(state, crc_errors); + pad_lat.show(state, crc_errors); + pad_lon.show(state, crc_errors); + pad_alt.show(state, crc_errors); + } + + public AltosPad() { + layout = new GridBagLayout(); + + GridBagConstraints c; + + label_font = new Font("Dialog", Font.PLAIN, 24); + value_font = new Font("Monospaced", Font.PLAIN, 24); + setLayout(layout); + + c = new GridBagConstraints(); + /* Elements in pad display: + * + * Battery voltage + * Igniter continuity + * GPS lock status and location + * Pad altitude + * RSSI + */ + battery = new Battery(layout, 0); + apogee = new Apogee(layout, 1); + main = new Main(layout, 2); + gps = new GPS(layout, 3); + pad_lat = new PadLat(layout, 4); + pad_lon = new PadLon(layout, 5); + pad_alt = new PadAlt(layout, 6); + } +} diff --git a/ao-tools/altosui/Makefile.am b/ao-tools/altosui/Makefile.am index ccb88ed1..ab9cf201 100644 --- a/ao-tools/altosui/Makefile.am +++ b/ao-tools/altosui/Makefile.am @@ -39,9 +39,12 @@ altosui_JAVA = \ Altos.java \ AltosInfoTable.java \ AltosKML.java \ + AltosLed.java \ + AltosLights.java \ AltosLine.java \ AltosLogfileChooser.java \ AltosLog.java \ + AltosPad.java \ AltosParse.java \ AltosPreferences.java \ AltosReader.java \ @@ -92,8 +95,21 @@ JAR=altosui.jar FATJAR=altosui-fat.jar # Icons -JAVA_ICON=$(top_srcdir)/icon/altus-metrum-16x16.jpg -WINDOWS_ICON=$(top_srcdir)/icon/altus-metrum.ico +ICONDIR=$(top_srcdir)/icon + +JAVA_ICON=$(ICONDIR)/altus-metrum-16x16.jpg + +ICONS= $(ICONDIR)/redled.png $(ICONDIR)/redoff.png \ + $(ICONDIR)/greenled.png $(ICONDIR)/greenoff.png \ + $(ICONDIR)/grayled.png $(ICONDIR)/grayoff.png + +# icon base names for jar +ICONJAR= -C $(ICONDIR) altus-metrum-16x16.jpg \ + -C $(ICONDIR) redled.png -C $(ICONDIR) redoff.png \ + -C $(ICONDIR) greenled.png -C $(ICONDIR) greenoff.png \ + -C $(ICONDIR) grayon.png -C $(ICONDIR) grayled.png + +WINDOWS_ICON=$(ICONDIR)/altus-metrum.ico # Firmware FIRMWARE_TD=$(top_srcdir)/src/teledongle-v0.2-$(VERSION).ihx @@ -163,13 +179,13 @@ classes/altosui: $(JAR): classaltosui.stamp Manifest.txt $(JAVA_ICON) jar cfm $@ Manifest.txt \ - -C $(top_srcdir)/icon altus-metrum-16x16.jpg \ + $(ICONJAR) \ -C classes altosui \ -C ../libaltos libaltosJNI $(FATJAR): classaltosui.stamp Manifest-fat.txt $(FREETTS_CLASS) $(JFREECHART_CLASS) $(JCOMMON_CLASS) $(LIBALTOS) $(JAVA_ICON) jar cfm $@ Manifest-fat.txt \ - -C $(top_srcdir)/icon altus-metrum-16x16.jpg \ + $(ICONJAR) \ -C classes altosui \ -C ../libaltos libaltosJNI diff --git a/icon/grayled.png b/icon/grayled.png new file mode 100644 index 0000000000000000000000000000000000000000..bb6005c6caa0757fbf4580a615d12223f902609c GIT binary patch literal 1528 zcmV-*4w}(ybcJ2l z>tL5MMh`B%j3p@qHo6^3iE00X!5BM?9X7_=9m=+YlU~Le5V3By6VE@u@#4giEWNiw zDyji@ z8DmdO)BLhfDAbSRmpkf#m6eqb1wnY&?RL*Jn@zOaZ5W0D%d$WSfoSwbfp|QwS(bIHP$)ckEdi^mt4W^cA9p&PwTdzFvg&&Dv8BnH?Cg2`qTRQy4#?x zfR{DjD3wZLDwRSqnS>w+;CX()905lWJ6U0jL6&6zfTn3Aolbvz>C&Z#&z?Oq0f0CT z1VQ+%TrNwpEJINgmrxLd0UIW0lsTE=I1Z^)3ZYO4&!0b^2nK_{IJ^XS(R91JyBD2? za~wDXei8m&@Rr0&>NNn!X0xc(YABb>@8(7FJY#4gI9s+>?LZMKF0Qfx`jiOSixQ7YLvRt!m8~uKNV9Y=7J&1TSFe?4q+uP2D z_m~KR@Kqv_K%>z(MDDeIzkf)`Fk-KOQE0VVNT<^vgnUiz-Md$d$K#qPim2D?Lq&KU zkIRH}9Y@}5HX+M0lF6ic|Ni|30RYRgZq3ZhKvmU44!zNjD#dZh0iaYWVRm*Fl+w=t z&!P0rojdCr8yjz1mW6CKJND@1Z^TiHjUTG2LXsq0zI?g(;K74;0MCgi5{X>P<#OG< zy**T`)sd-wou~nzR;xkRb(}wczN_o{HD}yC6FzD2a2K`n`jtRRmJY^ zF6QUw?RY%?QK3-y(>p)(_>#}(FZ6o7zcx2F->B7UID7W&D+_5hn<$k^NTpK9<#K!B zaQI50Q259H+_+z}NT<{Jxl*ZI(KL(%NF;K7b#?W>VecIIb9(#s?Td`DZySw9Zf9qQYqeT1O>^K7>hwh*5P+g6NT<^* znM`g0__Ra{ literal 0 HcmV?d00001 diff --git a/icon/grayon.png b/icon/grayon.png new file mode 100644 index 0000000000000000000000000000000000000000..c99b376ea1259b076d25b33d8210ad1cb91c35b9 GIT binary patch literal 1873 zcmV-X2d?;uP)Ag1}nsH>umQ+h}VzZFMM5PUNFgdss8eGF3 zTj-^i-b&en4Ly{#9T2q8(u=JuR+CsBEH94$94o&l+)FL@Vk{!#!vgMi4 zywQB@Ve_Akw9Y2J5Z!JUQcC!~55^dRAb{(- zh@uFbbBvFVBbUozYHF(P`~FAOYW1`K3qYk($#c%{o}8R~yU}PsN{R9Dapd!PD2jqG z42NbA1aKS&jYb3Qb{mSKU~X;>rBdlZ7=|m`+uO(Q4d9K|pT2taO6zvJn4O))#KZ&u zAc`VHQ3L=(!Uux^f*^qJ`v`&n2L}hJ)oRFQv$%Tos%03)cel5<|NTw?w{G3~X%t1j zuh;8rY-|j(v$J4~0RR+5fe-?zR0^q73Q-gx3`6+7kHKI7DJ5Ljh2uCen5R#lqSx!; z`t|F<IIS_BO|D@4q#15xv8gyNc1)8Rb zbUHm`0y0AsMF@fbp69`JT?iq-7>n0^-;ejCl(>BPGV1j@N~O{t0en+zY;0VxEb9Z$ z^Wsn=%}7%MqN*wsMTw0u49an8T8N73QBF4E~Vq?E|za(MCL1={U)d3}AoAcPQ` z&1MssOlBz5NSLas@zixargoHO@(I&4W8qRtNGW55G);?5vA4H}>FMcTvM7pfcDr5V z^La#36o(p7O}0&jALks5u~>6@roGX~Nj?L>%*+froesh<{DF{C7NnF=6lF*=5}p(i zMG+WdNTpIS)pP~`7-I;-5S;TN=12*O9$nWdXIv1T=cR)nKp2KGjdUShN)DG4L_i1t zfur>#i=#6L0x-rPr8LA~Fi0h{8ih?#AURd?{cS9ePtZOA8dWTnO2P9y#zw1JGQAR@ zw@ngABl(;JGrFD}f^#0auFFU$RWnMjROLA5F+0f!0RESblfr1t_x&M7l8maV8Vbgk z8-}3~1OWiVLMgpckxAzNQQApZa(bQ@-!ZyJ=QT~Ug{Emu&iQ4>abOq*q?GVHFD|XB zs>V5f)Y+0W)8&){T-S}W9|7@w9{|wr_o1pP(&==A0r=-r4K2M5}VgS%;wW#3TWkQHw=W;o;+wHimN=~ZlR9-#Li-jjmMLECM z>%p=t*tQMFafX1V zaeRC{RL`kICmWBXDTsw66Si#w0QUFy;kqss78Wemb(iCen2hbUwY6(UM@J7IJb1uM zr4kB-0)Q|3gLFDQ)SMCk3dN+Eq?B<4knmQkg~i3iU}9q8+U?u7pPUKc4Tv8e9UcAY z;lqdAFbvGk&&PYCjpA*BiT)K1#7cXA?Jd??+R4SF;S!dq)bGo*+ zb}fpcKeXHJ^4{KFs?+Jf^Sq&QOvOf3Rb;bS6pO_upU?jnz|X4H>c773{&xbPvAVkY z4aV5VVHhsEu6wcH?;C@`fB}G_C}BFCc642T#W}wl1i^3L_xJn@eBO8WdZOu*00000 LNkvXXu0mjf!xWZi literal 0 HcmV?d00001 diff --git a/icon/greenled.png b/icon/greenled.png new file mode 100644 index 0000000000000000000000000000000000000000..d7663961f22f33b6d6f42193acc3d71d89684f21 GIT binary patch literal 2281 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 BdGY`N literal 0 HcmV?d00001 diff --git a/icon/redled.png b/icon/redled.png new file mode 100644 index 0000000000000000000000000000000000000000..230afae09987ed6a09edb6a06de34f9ba0d8557e GIT binary patch literal 2103 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 literal 0 HcmV?d00001 -- 2.30.2