diff options
52 files changed, 2792 insertions, 210 deletions
| 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<AltosUnitsListener> 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<AltosUnitsListener>(); +			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 <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 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/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 <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 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 <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 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 <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 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 <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 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<Image> icons = new ArrayList<Image>(); +		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 <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 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 <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 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 <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 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<AltosFontListener> font_listeners; + +	static int font_size = AltosUILib.font_size_medium; + +	static LinkedList<AltosUIListener> 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<AltosFontListener>(); + +		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<AltosUIListener>(); +		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 <mike@ethernal.org> + * + * 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 <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 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 <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 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.pngBinary files differ new file mode 100644 index 00000000..f045dc6a --- /dev/null +++ b/icon/micropeak-128.png diff --git a/icon/micropeak-16.png b/icon/micropeak-16.pngBinary files differ new file mode 100644 index 00000000..d8140802 --- /dev/null +++ b/icon/micropeak-16.png diff --git a/icon/micropeak-256.png b/icon/micropeak-256.pngBinary files differ new file mode 100644 index 00000000..b96d4706 --- /dev/null +++ b/icon/micropeak-256.png diff --git a/icon/micropeak-32.png b/icon/micropeak-32.pngBinary files differ new file mode 100644 index 00000000..d34c5c12 --- /dev/null +++ b/icon/micropeak-32.png diff --git a/icon/micropeak-48.png b/icon/micropeak-48.pngBinary files differ new file mode 100644 index 00000000..86dc4f7f --- /dev/null +++ b/icon/micropeak-48.png diff --git a/icon/micropeak-64.png b/icon/micropeak-64.pngBinary files differ new file mode 100644 index 00000000..6ca7c2eb --- /dev/null +++ b/icon/micropeak-64.png diff --git a/altosui/libaltos/.gitignore b/libaltos/.gitignore index c490e6f8..c490e6f8 100644 --- a/altosui/libaltos/.gitignore +++ b/libaltos/.gitignore diff --git a/altosui/libaltos/Makefile-standalone b/libaltos/Makefile-standalone index 4e438050..4e438050 100644 --- a/altosui/libaltos/Makefile-standalone +++ b/libaltos/Makefile-standalone diff --git a/altosui/libaltos/Makefile.am b/libaltos/Makefile.am index b5ab1ddb..b5ab1ddb 100644 --- a/altosui/libaltos/Makefile.am +++ b/libaltos/Makefile.am diff --git a/altosui/libaltos/cjnitest.c b/libaltos/cjnitest.c index f0fe78f7..f0fe78f7 100644 --- a/altosui/libaltos/cjnitest.c +++ b/libaltos/cjnitest.c diff --git a/altosui/libaltos/libaltos.c b/libaltos/libaltos.c index ab6ca878..d7b266cf 100644 --- a/altosui/libaltos/libaltos.c +++ b/libaltos/libaltos.c @@ -20,16 +20,6 @@  #include <stdlib.h>  #include <string.h> -#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 @@ -473,6 +463,7 @@ usb_tty(char *sys)  				base, config, interface);  			endpoint_full = cc_fullname(sys, endpoint_base); +  			/* Check for tty:ttyACMx style names  			 */  			ntty = scandir(endpoint_full, &namelist, @@ -485,6 +476,18 @@ usb_tty(char *sys)  				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"); @@ -498,6 +501,7 @@ usb_tty(char *sys)  				free(namelist);  				return tty;  			} +  		}  	}  	return NULL; @@ -507,7 +511,11 @@ 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; @@ -517,7 +525,7 @@ usb_scan_device(char *sys)  	usbdev->serial = load_dec(sys, "serial");  	usbdev->idProduct = load_hex(sys, "idProduct");  	usbdev->idVendor = load_hex(sys, "idVendor"); -	usbdev->tty = usb_tty(sys); +	usbdev->tty = tty;  	return usbdev;  } @@ -581,15 +589,15 @@ altos_list_start(void)  	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 (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; -		} +		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; @@ -600,8 +608,9 @@ int  altos_list_next(struct altos_list *list, struct altos_device *device)  {  	struct altos_usbdev *dev; -	if (list->current >= list->ndev) +	if (list->current >= list->ndev) {  		return 0; +	}  	dev = list->dev[list->current];  	strcpy(device->name, dev->product_name);  	device->vendor = dev->idVendor; @@ -1026,10 +1035,6 @@ altos_list_next(struct altos_list *list, struct altos_device *device)  		       "%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); diff --git a/altosui/libaltos/libaltos.dylib b/libaltos/libaltos.dylibBinary files differ index 1038817d..1038817d 100755 --- a/altosui/libaltos/libaltos.dylib +++ b/libaltos/libaltos.dylib diff --git a/altosui/libaltos/libaltos.h b/libaltos/libaltos.h index f90fbb87..f90fbb87 100644 --- a/altosui/libaltos/libaltos.h +++ b/libaltos/libaltos.h diff --git a/altosui/libaltos/libaltos.i0 b/libaltos/libaltos.i0 index d06468f5..d06468f5 100644 --- a/altosui/libaltos/libaltos.i0 +++ b/libaltos/libaltos.i0 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 <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 org.altusmetrum.micropeak; + +import java.lang.*; +import java.io.*; +import java.util.*; +import org.altusmetrum.AltosLib.*; + +abstract class MicroIterator implements Iterator<Double> { +	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<Double> { +	MicroData	data; + +	public Iterator<Double> 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<Double> { +	MicroData	data; + +	public Iterator<Double> 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<Double> { +	MicroData	data; + +	public Iterator<Double> 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<Integer>	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<Double> heights() { +		return new MicroHeightIterable(this); +	} + +	public Iterable<Double> speeds() { +		return new MicroSpeedIterable(this); +	} + +	public Iterable<Double> 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<Integer>(); +		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 <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 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 <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 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 <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 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 <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 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<MicroUSB>	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 <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 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 <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 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 <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 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 <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 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<MicroUSB> list() { +		if (!load_library()) +			return null; + +		SWIGTYPE_p_altos_list list = libaltos.altos_list_start(); + +		ArrayList<MicroUSB> device_list = new ArrayList<MicroUSB>(); +		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 @@ -19,6 +19,12 @@  #define _AO_ASYNC_H_  void +ao_async_start(void); + +void +ao_async_stop(void); + +void  ao_async_byte(uint8_t byte);  #endif /* _AO_ASYNC_H_ */ 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 <ao.h> +#include <ao_micropeak.h>  #include <ao_log_micro.h>  #include <ao_async.h> -#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 <ao.h> +#include <ao_micropeak.h>  #include <ao_ms5607.h>  #include <ao_log_micro.h> +#include <ao_async.h>  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 <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. + */ + +#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 + | 
