diff --git a/ChangeLog b/ChangeLog
index a6a34dd01..24d7bd2e7 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,3 +1,11 @@
+2011-09-18  Sampo Niskanen
+
+	* Remember window/dialog sizes and/or positions
+
+2011-09-13  Sampo Niskanen
+
+	* Added icons to help menu
+
 2011-08-28  Richard Graham
 
 	* Patch for geodetic computations + coriolis effect
diff --git a/l10n/messages.properties b/l10n/messages.properties
index 7fbef9311..f58be3d55 100644
--- a/l10n/messages.properties
+++ b/l10n/messages.properties
@@ -351,8 +351,8 @@ simedtdlg.IntensityDesc.High = High
 simedtdlg.IntensityDesc.Veryhigh = Very high
 simedtdlg.IntensityDesc.Extreme = Extreme
 
-GeodeticComputationStrategy.none.name = None
-GeodeticComputationStrategy.none.desc = Perform no geodetic computations.
+GeodeticComputationStrategy.flat.name = Flat Earth
+GeodeticComputationStrategy.flat.desc = Perform computations with a flat Earth approximation.  Sufficient for low-altitude flights.
 GeodeticComputationStrategy.spherical.name = Spherical approximation
 GeodeticComputationStrategy.spherical.desc = <html>Perform geodetic computations assuming a spherical Earth.<br>This is sufficiently accurate for almost all purposes.
 GeodeticComputationStrategy.wgs84.name = WGS84 ellipsoid
@@ -1501,3 +1501,20 @@ LandingDistanceParameter.name = Landing distance
 TotalFlightTimeParameter.name = Total flight time
 DeploymentVelocityParameter.name = Velocity at parachute deployment
 
+
+! Compass directions drawn on a compass rose.
+CompassRose.lbl.north = N
+CompassRose.lbl.east  = E
+CompassRose.lbl.south = S
+CompassRose.lbl.west  = W
+
+! Compass directions with subdirections.  These might not be localized even if the directions on the compass rose are.
+CompassSelectionButton.lbl.N = N
+CompassSelectionButton.lbl.NE = NE
+CompassSelectionButton.lbl.E = E
+CompassSelectionButton.lbl.SE = SE
+CompassSelectionButton.lbl.S = S
+CompassSelectionButton.lbl.SW = SW
+CompassSelectionButton.lbl.W = W
+CompassSelectionButton.lbl.NW = NW
+
diff --git a/pix/icons/copyright.txt b/pix/icons/copyright.txt
index 81429b311..6583a503e 100644
--- a/pix/icons/copyright.txt
+++ b/pix/icons/copyright.txt
@@ -30,4 +30,7 @@ delete.png
 preferences.png
 zoom-in.png
 zoom-out.png
-
+help-license.png
+help-log.png
+help-about.png
+help-bug.png
diff --git a/pix/icons/help-about.png b/pix/icons/help-about.png
new file mode 100644
index 000000000..e0fad5619
Binary files /dev/null and b/pix/icons/help-about.png differ
diff --git a/pix/icons/help-bug.png b/pix/icons/help-bug.png
new file mode 100644
index 000000000..5fc4b25ca
Binary files /dev/null and b/pix/icons/help-bug.png differ
diff --git a/pix/icons/help-license.png b/pix/icons/help-license.png
new file mode 100644
index 000000000..eec0392f0
Binary files /dev/null and b/pix/icons/help-license.png differ
diff --git a/pix/icons/help-log.png b/pix/icons/help-log.png
new file mode 100644
index 000000000..087a84b35
Binary files /dev/null and b/pix/icons/help-log.png differ
diff --git a/src/net/sf/openrocket/file/openrocket/OpenRocketLoader.java b/src/net/sf/openrocket/file/openrocket/OpenRocketLoader.java
index 4c5eb72a0..73f43e500 100644
--- a/src/net/sf/openrocket/file/openrocket/OpenRocketLoader.java
+++ b/src/net/sf/openrocket/file/openrocket/OpenRocketLoader.java
@@ -1291,7 +1291,7 @@ class SimulationConditionsHandler extends ElementHandler {
 	public SimulationConditionsHandler(Rocket rocket) {
 		conditions = new SimulationOptions(rocket);
 		// Set up default loading settings (which may differ from the new defaults)
-		conditions.setGeodeticComputation(GeodeticComputationStrategy.NONE);
+		conditions.setGeodeticComputation(GeodeticComputationStrategy.FLAT);
 	}
 	
 	public SimulationOptions getConditions() {
diff --git a/src/net/sf/openrocket/gui/components/FlatButton.java b/src/net/sf/openrocket/gui/components/FlatButton.java
new file mode 100644
index 000000000..d4c39ef40
--- /dev/null
+++ b/src/net/sf/openrocket/gui/components/FlatButton.java
@@ -0,0 +1,69 @@
+package net.sf.openrocket.gui.components;
+
+import java.awt.event.MouseAdapter;
+import java.awt.event.MouseEvent;
+
+import javax.swing.Action;
+import javax.swing.Icon;
+import javax.swing.JButton;
+
+/**
+ * A JButton that appears flat until you roll over it.
+ * 
+ * @author Sampo Niskanen <sampo.niskanen@iki.fi>
+ */
+public class FlatButton extends JButton {
+	
+	public FlatButton() {
+		super();
+		initialize();
+	}
+	
+	public FlatButton(Icon icon) {
+		super(icon);
+		initialize();
+	}
+	
+	public FlatButton(String text) {
+		super(text);
+		initialize();
+	}
+	
+	public FlatButton(Action a) {
+		super(a);
+		initialize();
+	}
+	
+	public FlatButton(String text, Icon icon) {
+		super(text, icon);
+		initialize();
+	}
+	
+	
+	private void initialize() {
+		this.addMouseListener(new MouseAdapter() {
+			@Override
+			public void mouseExited(MouseEvent e) {
+				flatten();
+			}
+			
+			@Override
+			public void mouseEntered(MouseEvent e) {
+				raise();
+			}
+		});
+		flatten();
+	}
+	
+	
+	private void flatten() {
+		this.setContentAreaFilled(false);
+		this.setBorderPainted(false);
+	}
+	
+	private void raise() {
+		this.setContentAreaFilled(true);
+		this.setBorderPainted(true);
+	}
+	
+}
diff --git a/src/net/sf/openrocket/gui/components/compass/CompassPointer.java b/src/net/sf/openrocket/gui/components/compass/CompassPointer.java
new file mode 100644
index 000000000..37fdad42e
--- /dev/null
+++ b/src/net/sf/openrocket/gui/components/compass/CompassPointer.java
@@ -0,0 +1,218 @@
+package net.sf.openrocket.gui.components.compass;
+
+import java.awt.Color;
+import java.awt.Dimension;
+import java.awt.Graphics;
+import java.awt.Graphics2D;
+import java.awt.RenderingHints;
+
+import javax.swing.event.ChangeEvent;
+import javax.swing.event.ChangeListener;
+
+import net.sf.openrocket.gui.Resettable;
+import net.sf.openrocket.gui.adaptors.DoubleModel;
+
+/**
+ * A component that draws a pointer onto a compass rose.
+ * 
+ * @author Sampo Niskanen <sampo.niskanen@iki.fi>
+ */
+public class CompassPointer extends CompassRose implements Resettable {
+	
+	private static final Color PRIMARY_POINTER_COLOR = new Color(1.0f, 0.2f, 0.2f);
+	private static final Color SECONDARY_POINTER_COLOR = new Color(0.2f, 0.2f, 0.2f, 0.2f);
+	
+	private final DoubleModel model;
+	private final ChangeListener listener;
+	
+	protected int width = -1;
+	protected int mid = -1;
+	
+	private DoubleModel secondaryModel;
+	
+	private float pointerLength = 0.95f;
+	private float pointerWidth = 0.1f;
+	private float pointerArrowWidth = 0.2f;
+	private boolean pointerArrow = true;
+	
+	
+
+	public CompassPointer(DoubleModel model) {
+		super();
+		this.model = model;
+		listener = new ChangeListener() {
+			@Override
+			public void stateChanged(ChangeEvent e) {
+				CompassPointer.this.repaint();
+			}
+		};
+		model.addChangeListener(listener);
+	}
+	
+	
+	@Override
+	public void paintComponent(Graphics g) {
+		super.paintComponent(g);
+		
+		Graphics2D g2 = (Graphics2D) g;
+		
+
+		Dimension dimension = this.getSize();
+		
+		width = Math.min(dimension.width, dimension.height);
+		mid = width / 2;
+		width = (int) (getScaler() * width);
+		
+
+		g2.setRenderingHint(RenderingHints.KEY_RENDERING, RenderingHints.VALUE_RENDER_QUALITY);
+		g2.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
+		
+
+		if (secondaryModel != null) {
+			drawArrow(secondaryModel.getValue(), SECONDARY_POINTER_COLOR, g2);
+		}
+		drawArrow(model.getValue(), PRIMARY_POINTER_COLOR, g2);
+		
+
+	}
+	
+	
+	private void drawArrow(double angle, Color color, Graphics2D g2) {
+		
+		int pLength = (int) (width * pointerLength / 2);
+		int pWidth = (int) (width * pointerWidth / 2);
+		int pArrowWidth = (int) (width * pointerArrowWidth / 2);
+		
+		int[] x = new int[8];
+		int[] y = new int[8];
+		
+		g2.setColor(color);
+		
+
+		double sin = Math.sin(angle);
+		double cos = Math.cos(angle);
+		
+		int n = 0;
+		
+		// Top part
+		x[n] = 0;
+		y[n] = -pLength;
+		n++;
+		if (pointerArrow) {
+			x[n] = -pArrowWidth;
+			y[n] = -pLength + 2 * pArrowWidth;
+			n++;
+			x[n] = -pWidth;
+			y[n] = -pLength + 2 * pArrowWidth;
+			n++;
+		}
+		
+		// Bottom part
+		x[n] = -pWidth;
+		y[n] = pLength;
+		n++;
+		x[n] = 0;
+		y[n] = pLength - pWidth;
+		n++;
+		x[n] = pWidth;
+		y[n] = pLength;
+		n++;
+		
+		// Top part
+		if (pointerArrow) {
+			x[n] = pWidth;
+			y[n] = -pLength + 2 * pArrowWidth;
+			n++;
+			x[n] = pArrowWidth;
+			y[n] = -pLength + 2 * pArrowWidth;
+			n++;
+		}
+		
+		// Rotate and shift
+		for (int i = 0; i < n; i++) {
+			double x2, y2;
+			x2 = cos * x[i] - sin * y[i];
+			y2 = sin * x[i] + cos * y[i];
+			
+			x[i] = (int) (x2 + mid);
+			y[i] = (int) (y2 + mid);
+		}
+		
+		g2.fillPolygon(x, y, n);
+		
+		g2.setColor(color.darker());
+		g2.drawPolygon(x, y, n);
+		
+	}
+	
+	
+	public boolean isPointerArrow() {
+		return pointerArrow;
+	}
+	
+	
+	public void setPointerArrow(boolean useArrow) {
+		this.pointerArrow = useArrow;
+		repaint();
+	}
+	
+	
+	public float getPointerLength() {
+		return pointerLength;
+	}
+	
+	
+	public void setPointerLength(float pointerLength) {
+		this.pointerLength = pointerLength;
+		repaint();
+	}
+	
+	
+	public float getPointerWidth() {
+		return pointerWidth;
+	}
+	
+	
+	public void setPointerWidth(float pointerWidth) {
+		this.pointerWidth = pointerWidth;
+		repaint();
+	}
+	
+	
+	public float getPointerArrowWidth() {
+		return pointerArrowWidth;
+	}
+	
+	
+	public void setPointerArrowWidth(float pointerArrowWidth) {
+		this.pointerArrowWidth = pointerArrowWidth;
+		repaint();
+	}
+	
+	
+
+	public DoubleModel getSecondaryModel() {
+		return secondaryModel;
+	}
+	
+	
+	public void setSecondaryModel(DoubleModel secondaryModel) {
+		if (this.secondaryModel != null) {
+			this.secondaryModel.removeChangeListener(listener);
+		}
+		this.secondaryModel = secondaryModel;
+		if (this.secondaryModel != null) {
+			this.secondaryModel.addChangeListener(listener);
+		}
+	}
+	
+	
+	@Override
+	public void resetModel() {
+		model.removeChangeListener(listener);
+		setSecondaryModel(null);
+	}
+	
+
+
+}
diff --git a/src/net/sf/openrocket/gui/components/compass/CompassRose.java b/src/net/sf/openrocket/gui/components/compass/CompassRose.java
new file mode 100644
index 000000000..49f1d059e
--- /dev/null
+++ b/src/net/sf/openrocket/gui/components/compass/CompassRose.java
@@ -0,0 +1,221 @@
+package net.sf.openrocket.gui.components.compass;
+
+import java.awt.Color;
+import java.awt.Dimension;
+import java.awt.Font;
+import java.awt.Graphics;
+import java.awt.Graphics2D;
+import java.awt.RenderingHints;
+import java.awt.font.GlyphVector;
+import java.awt.geom.Rectangle2D;
+
+import javax.swing.JComponent;
+
+import net.sf.openrocket.l10n.Translator;
+import net.sf.openrocket.startup.Application;
+
+/**
+ * A component that draws a compass rose.  This class has no other functionality, but superclasses
+ * may add functionality to it.
+ * 
+ * @author Sampo Niskanen <sampo.niskanen@iki.fi>
+ */
+public class CompassRose extends JComponent {
+	private static final Translator trans = Application.getTranslator();
+	
+
+	private static final Color MAIN_COLOR = new Color(0.4f, 0.4f, 1.0f);
+	private static final float MAIN_LENGTH = 0.95f;
+	private static final float MAIN_WIDTH = 0.15f;
+	
+	private static final int CIRCLE_BORDER = 2;
+	private static final Color CIRCLE_HIGHLIGHT = new Color(1.0f, 1.0f, 1.0f, 0.7f);
+	private static final Color CIRCLE_SHADE = new Color(0.0f, 0.0f, 0.0f, 0.2f);
+	
+	private static final Color MARKER_COLOR = Color.BLACK;
+	
+
+	private double scaler;
+	
+	private double markerRadius;
+	private Font markerFont;
+	
+	
+	/**
+	 * Construct a compass rose with the default settings.
+	 */
+	public CompassRose() {
+		this(0.8, 1.1, Font.decode("Serif-PLAIN-16"));
+	}
+	
+	
+	/**
+	 * Construct a compass rose with the specified settings.
+	 * 
+	 * @param scaler		The scaler of the rose.  The bordering circle will we this portion of the component dimensions.
+	 * @param markerRadius	The radius for the marker positions (N/E/S/W), or NaN for no markers.  A value greater than one
+	 * 						will position the markers outside of the bordering circle.
+	 * @param markerFont	The font used for the markers.
+	 */
+	public CompassRose(double scaler, double markerRadius, Font markerFont) {
+		this.scaler = scaler;
+		this.markerRadius = markerRadius;
+		this.markerFont = markerFont;
+	}
+	
+	
+
+	@Override
+	public void paintComponent(Graphics g) {
+		
+		Graphics2D g2 = (Graphics2D) g;
+		
+		int[] x = new int[3];
+		int[] y = new int[3];
+		Dimension dimension = this.getSize();
+		
+		int width = Math.min(dimension.width, dimension.height);
+		int mid = width / 2;
+		width = (int) (scaler * width);
+		
+		int mainLength = (int) (width * MAIN_LENGTH / 2);
+		int mainWidth = (int) (width * MAIN_WIDTH / 2);
+		
+
+		g2.setRenderingHint(RenderingHints.KEY_RENDERING, RenderingHints.VALUE_RENDER_QUALITY);
+		g2.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
+		
+		g2.setColor(MAIN_COLOR);
+		
+		// North
+		x[0] = mid;
+		y[0] = mid;
+		x[1] = mid;
+		y[1] = mid - mainLength;
+		x[2] = mid - mainWidth;
+		y[2] = mid - mainWidth;
+		g2.fillPolygon(x, y, 3);
+		
+		x[2] = mid + mainWidth;
+		g2.drawPolygon(x, y, 3);
+		
+		// East
+		x[0] = mid;
+		y[0] = mid;
+		x[1] = mid + mainLength;
+		y[1] = mid;
+		x[2] = mid + mainWidth;
+		y[2] = mid - mainWidth;
+		g2.fillPolygon(x, y, 3);
+		
+		y[2] = mid + mainWidth;
+		g2.drawPolygon(x, y, 3);
+		
+		// South
+		x[0] = mid;
+		y[0] = mid;
+		x[1] = mid;
+		y[1] = mid + mainLength;
+		x[2] = mid + mainWidth;
+		y[2] = mid + mainWidth;
+		g2.fillPolygon(x, y, 3);
+		
+		x[2] = mid - mainWidth;
+		g2.drawPolygon(x, y, 3);
+		
+		// West
+		x[0] = mid;
+		y[0] = mid;
+		x[1] = mid - mainLength;
+		y[1] = mid;
+		x[2] = mid - mainWidth;
+		y[2] = mid + mainWidth;
+		g2.fillPolygon(x, y, 3);
+		
+		y[2] = mid - mainWidth;
+		g2.drawPolygon(x, y, 3);
+		
+
+		// Border circle
+		g2.setColor(CIRCLE_SHADE);
+		g2.drawArc(mid - width / 2 + CIRCLE_BORDER, mid - width / 2 + CIRCLE_BORDER,
+					width - 2 * CIRCLE_BORDER, width - 2 * CIRCLE_BORDER, 45, 180);
+		g2.setColor(CIRCLE_HIGHLIGHT);
+		g2.drawArc(mid - width / 2 + CIRCLE_BORDER, mid - width / 2 + CIRCLE_BORDER,
+				width - 2 * CIRCLE_BORDER, width - 2 * CIRCLE_BORDER, 180 + 45, 180);
+		
+
+		// Draw direction markers
+		if (!Double.isNaN(markerRadius) && markerFont != null) {
+			
+			int pos = (int) (width * markerRadius / 2);
+			
+			g2.setColor(MARKER_COLOR);
+			drawMarker(g2, mid, mid - pos, trans.get("lbl.north"));
+			drawMarker(g2, mid + pos, mid, trans.get("lbl.east"));
+			drawMarker(g2, mid, mid + pos, trans.get("lbl.south"));
+			drawMarker(g2, mid - pos, mid, trans.get("lbl.west"));
+			
+		}
+		
+	}
+	
+	
+
+	private void drawMarker(Graphics2D g2, float x, float y, String str) {
+		GlyphVector gv = markerFont.createGlyphVector(g2.getFontRenderContext(), str);
+		Rectangle2D rect = gv.getVisualBounds();
+		
+		x -= rect.getWidth() / 2;
+		y += rect.getHeight() / 2;
+		
+		g2.drawGlyphVector(gv, x, y);
+		
+	}
+	
+	
+
+
+
+	public double getScaler() {
+		return scaler;
+	}
+	
+	
+	public void setScaler(double scaler) {
+		this.scaler = scaler;
+		repaint();
+	}
+	
+	
+	public double getMarkerRadius() {
+		return markerRadius;
+	}
+	
+	
+	public void setMarkerRadius(double markerRadius) {
+		this.markerRadius = markerRadius;
+		repaint();
+	}
+	
+	
+	public Font getMarkerFont() {
+		return markerFont;
+	}
+	
+	
+	public void setMarkerFont(Font markerFont) {
+		this.markerFont = markerFont;
+		repaint();
+	}
+	
+	@Override
+	public Dimension getPreferredSize() {
+		Dimension dim = super.getPreferredSize();
+		int min = Math.min(dim.width, dim.height);
+		dim.setSize(min, min);
+		return dim;
+	}
+	
+
+}
diff --git a/src/net/sf/openrocket/gui/components/compass/CompassSelectionButton.java b/src/net/sf/openrocket/gui/components/compass/CompassSelectionButton.java
new file mode 100644
index 000000000..c8b0fee2e
--- /dev/null
+++ b/src/net/sf/openrocket/gui/components/compass/CompassSelectionButton.java
@@ -0,0 +1,190 @@
+package net.sf.openrocket.gui.components.compass;
+
+import java.awt.Dimension;
+import java.awt.event.ActionEvent;
+import java.awt.event.ActionListener;
+
+import javax.swing.BorderFactory;
+import javax.swing.JButton;
+import javax.swing.JLabel;
+import javax.swing.JPanel;
+import javax.swing.JPopupMenu;
+import javax.swing.JSpinner;
+import javax.swing.border.BevelBorder;
+import javax.swing.event.ChangeEvent;
+import javax.swing.event.ChangeListener;
+
+import net.miginfocom.swing.MigLayout;
+import net.sf.openrocket.gui.Resettable;
+import net.sf.openrocket.gui.adaptors.DoubleModel;
+import net.sf.openrocket.gui.components.FlatButton;
+import net.sf.openrocket.l10n.Translator;
+import net.sf.openrocket.startup.Application;
+import net.sf.openrocket.util.Chars;
+import net.sf.openrocket.util.MathUtil;
+
+
+/**
+ * A button that displays a current compass direction and opens a popup to edit
+ * the value when clicked.
+ * 
+ * @author Sampo Niskanen <sampo.niskanen@iki.fi>
+ */
+public class CompassSelectionButton extends FlatButton implements Resettable {
+	
+	private static final Translator trans = Application.getTranslator();
+	
+	private static final int POPUP_COMPASS_SIZE = 200;
+	private static final double SECTOR = 45;
+	
+	private static int minWidth = -1;
+	
+
+	private final DoubleModel model;
+	private final ChangeListener listener;
+	
+	private JPopupMenu popup;
+	
+	
+	public CompassSelectionButton(final DoubleModel model) {
+		this.model = model;
+		
+		JPanel panel = new JPanel(new MigLayout("fill, ins 0"));
+		panel.setOpaque(false);
+		
+		CompassPointer pointer = new CompassPointer(model);
+		pointer.setPreferredSize(new Dimension(24, 24));
+		pointer.setMarkerFont(null);
+		pointer.setPointerArrow(false);
+		pointer.setPointerWidth(0.45f);
+		pointer.setScaler(1.0f);
+		panel.add(pointer, "gapright rel");
+		
+
+		final JLabel label = new JLabel();
+		label.setText(getLabel(model.getValue()));
+		panel.add(label);
+		
+		listener = new ChangeListener() {
+			@Override
+			public void stateChanged(ChangeEvent e) {
+				label.setText(getLabel(model.getValue()));
+			}
+		};
+		model.addChangeListener(listener);
+		
+
+		if (minWidth < 0) {
+			calculateMinWidth();
+			label.setMinimumSize(new Dimension(minWidth, 0));
+		}
+		
+
+		this.add(panel);
+		
+		this.addActionListener(new ActionListener() {
+			@Override
+			public void actionPerformed(ActionEvent e) {
+				openPopup();
+			}
+		});
+	}
+	
+	
+
+
+	private String getLabel(double value) {
+		String str;
+		
+		value = MathUtil.reduce360(value);
+		value = Math.toDegrees(value);
+		str = "" + Math.round(value) + Chars.DEGREE + " (";
+		
+		if (value <= 0.5 * SECTOR || value >= 7.5 * SECTOR) {
+			str += trans.get("lbl.N");
+		} else if (value <= 1.5 * SECTOR) {
+			str += trans.get("lbl.NE");
+		} else if (value <= 2.5 * SECTOR) {
+			str += trans.get("lbl.E");
+		} else if (value <= 3.5 * SECTOR) {
+			str += trans.get("lbl.SE");
+		} else if (value <= 4.5 * SECTOR) {
+			str += trans.get("lbl.S");
+		} else if (value <= 5.5 * SECTOR) {
+			str += trans.get("lbl.SW");
+		} else if (value <= 6.5 * SECTOR) {
+			str += trans.get("lbl.W");
+		} else {
+			str += trans.get("lbl.NW");
+		}
+		
+		str += ")";
+		return str;
+	}
+	
+	
+	private void openPopup() {
+		if (popup == null) {
+			popup = new JPopupMenu();
+			
+
+			final JPanel panel = new JPanel(new MigLayout("fill"));
+			
+			final CompassPointer rose = new CompassSelector(model);
+			rose.setPreferredSize(new Dimension(POPUP_COMPASS_SIZE, POPUP_COMPASS_SIZE));
+			panel.add(rose, "spany, gapright unrel");
+			
+			panel.add(new JPanel(), "growy, wrap");
+			
+			JSpinner spin = new JSpinner(model.getSpinnerModel());
+			panel.add(spin, "wmin 50lp, growx, gapright 0, aligny bottom");
+			
+			panel.add(new JLabel("" + Chars.DEGREE), "wrap para");
+			
+			JButton close = new JButton("OK");
+			close.addActionListener(new ActionListener() {
+				@Override
+				public void actionPerformed(ActionEvent e) {
+					popup.setVisible(false);
+				}
+			});
+			panel.add(close, "span 2, growx, wrap");
+			
+			panel.add(new JPanel(), "growy, wrap");
+			
+			popup.add(panel);
+			popup.setBorder(BorderFactory.createBevelBorder(BevelBorder.RAISED));
+		}
+		
+		popup.pack();
+		
+		Dimension popupSize = popup.getPreferredSize();
+		Dimension buttonSize = this.getSize();
+		
+		int posX = buttonSize.width / 2 - popupSize.width / 2;
+		int posY = buttonSize.height / 2 - popupSize.height / 2;
+		popup.show(this, posX, posY);
+	}
+	
+	private void calculateMinWidth() {
+		JLabel label = new JLabel();
+		int max = 0;
+		for (double deg = 0; deg < 360; deg += 0.99999999999) {
+			label.setText(getLabel(Math.toRadians(deg)));
+			int w = label.getPreferredSize().width;
+			if (w > max) {
+				max = w;
+			}
+		}
+		minWidth = max + 1;
+	}
+	
+	
+
+
+	@Override
+	public void resetModel() {
+		model.removeChangeListener(listener);
+	}
+	
+}
diff --git a/src/net/sf/openrocket/gui/components/compass/CompassSelector.java b/src/net/sf/openrocket/gui/components/compass/CompassSelector.java
new file mode 100644
index 000000000..deed9cf7e
--- /dev/null
+++ b/src/net/sf/openrocket/gui/components/compass/CompassSelector.java
@@ -0,0 +1,97 @@
+package net.sf.openrocket.gui.components.compass;
+
+import java.awt.event.MouseAdapter;
+import java.awt.event.MouseEvent;
+
+import net.sf.openrocket.gui.adaptors.DoubleModel;
+import net.sf.openrocket.util.MathUtil;
+
+/**
+ * Component that allows selecting a compass direction on a CompassSelector.
+ * 
+ * @author Sampo Niskanen <sampo.niskanen@iki.fi>
+ */
+public class CompassSelector extends CompassPointer {
+	
+	private final DoubleModel model;
+	
+	public CompassSelector(DoubleModel model) {
+		super(model);
+		this.model = model;
+		
+		MouseAdapter mouse = new MouseAdapter() {
+			private boolean dragging = false;
+			
+			@Override
+			public void mousePressed(MouseEvent e) {
+				if (!isWithinCircle(e))
+					return;
+				if (e.getButton() != MouseEvent.BUTTON1)
+					return;
+				dragging = true;
+				clicked(e);
+			}
+			
+			@Override
+			public void mouseReleased(MouseEvent e) {
+				if (e.getButton() != MouseEvent.BUTTON1)
+					return;
+				dragging = false;
+			}
+			
+			
+			@Override
+			public void mouseDragged(MouseEvent e) {
+				if (!dragging)
+					return;
+				clicked(e);
+			}
+		};
+		this.addMouseListener(mouse);
+		this.addMouseMotionListener(mouse);
+		
+	}
+	
+	private boolean isWithinCircle(MouseEvent e) {
+		if (mid < 0 || width < 0) {
+			return false;
+		}
+		
+		int x = e.getX() - mid;
+		int y = e.getY() - mid;
+		
+		double distance = Math.hypot(x, y);
+		return distance < width / 2;
+	}
+	
+	private void clicked(MouseEvent e) {
+		
+		if (mid < 0 || width < 0) {
+			return;
+		}
+		
+		int x = e.getX() - mid;
+		int y = e.getY() - mid;
+		
+		double distance = Math.hypot(x, y);
+		
+		double theta = Math.atan2(y, x);
+		theta = MathUtil.reduce360(theta + Math.PI / 2);
+		
+		// Round the value appropriately
+		theta = Math.toDegrees(theta);
+		
+		if (distance > 50) {
+			theta = Math.round(theta);
+		} else if (distance > 10) {
+			theta = 5 * Math.round(theta / 5);
+		} else {
+			// Do nothing if too close to center
+			return;
+		}
+		theta = Math.toRadians(theta);
+		
+		model.setValue(theta);
+	}
+	
+}
diff --git a/src/net/sf/openrocket/gui/components/compass/Tester.java b/src/net/sf/openrocket/gui/components/compass/Tester.java
new file mode 100644
index 000000000..eda95f567
--- /dev/null
+++ b/src/net/sf/openrocket/gui/components/compass/Tester.java
@@ -0,0 +1,66 @@
+package net.sf.openrocket.gui.components.compass;
+
+import java.awt.Dimension;
+import java.lang.reflect.InvocationTargetException;
+
+import javax.swing.JFrame;
+import javax.swing.JPanel;
+import javax.swing.JSpinner;
+import javax.swing.SwingUtilities;
+
+import net.miginfocom.swing.MigLayout;
+import net.sf.openrocket.gui.adaptors.DoubleModel;
+import net.sf.openrocket.l10n.ResourceBundleTranslator;
+import net.sf.openrocket.startup.Application;
+import net.sf.openrocket.unit.UnitGroup;
+import net.sf.openrocket.util.GUIUtil;
+
+public class Tester {
+	
+
+	public static void main(String[] args) throws InterruptedException, InvocationTargetException {
+		
+		Application.setBaseTranslator(new ResourceBundleTranslator("l10n.messages"));
+		
+		GUIUtil.setBestLAF();
+		
+		SwingUtilities.invokeAndWait(new Runnable() {
+			@Override
+			public void run() {
+				JFrame frame = new JFrame();
+				
+				JPanel panel = new JPanel(new MigLayout("fill"));
+				DoubleModel model = new DoubleModel(Math.toRadians(45), UnitGroup.UNITS_ANGLE);
+				DoubleModel second = new DoubleModel(Math.toRadians(30), UnitGroup.UNITS_ANGLE);
+				
+
+				CompassPointer rose = new CompassSelector(model);
+				rose.setPreferredSize(new Dimension(300, 300));
+				rose.setSecondaryModel(second);
+				panel.add(rose);
+				
+				rose = new CompassPointer(model);
+				rose.setPreferredSize(new Dimension(24, 24));
+				panel.add(rose);
+				rose.setMarkerFont(null);
+				rose.setPointerArrow(false);
+				rose.setPointerWidth(0.45f);
+				rose.setScaler(1.0f);
+				
+				JSpinner spin = new JSpinner(model.getSpinnerModel());
+				spin.setPreferredSize(new Dimension(50, 20));
+				panel.add(spin, "wrap para");
+				
+
+				CompassSelectionButton button = new CompassSelectionButton(model);
+				panel.add(button);
+				
+
+				frame.add(panel);
+				frame.pack();
+				frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
+				frame.setVisible(true);
+			}
+		});
+	}
+}
diff --git a/src/net/sf/openrocket/gui/configdialog/ComponentConfigDialog.java b/src/net/sf/openrocket/gui/configdialog/ComponentConfigDialog.java
index a04c65b61..0f9f11597 100644
--- a/src/net/sf/openrocket/gui/configdialog/ComponentConfigDialog.java
+++ b/src/net/sf/openrocket/gui/configdialog/ComponentConfigDialog.java
@@ -1,10 +1,7 @@
 package net.sf.openrocket.gui.configdialog;
 
 
-import java.awt.Point;
 import java.awt.Window;
-import java.awt.event.ComponentAdapter;
-import java.awt.event.ComponentEvent;
 import java.lang.reflect.Constructor;
 import java.lang.reflect.InvocationTargetException;
 
@@ -18,7 +15,6 @@ import net.sf.openrocket.rocketcomponent.RocketComponent;
 import net.sf.openrocket.startup.Application;
 import net.sf.openrocket.util.BugException;
 import net.sf.openrocket.util.GUIUtil;
-import net.sf.openrocket.util.Prefs;
 import net.sf.openrocket.util.Reflection;
 
 /**
@@ -44,7 +40,7 @@ public class ComponentConfigDialog extends JDialog implements ComponentChangeLis
 	
 	private final Window parent;
 	private static final Translator trans = Application.getTranslator();
-
+	
 	private ComponentConfigDialog(Window parent, OpenRocketDocument document,
 			RocketComponent component) {
 		super(parent);
@@ -53,22 +49,7 @@ public class ComponentConfigDialog extends JDialog implements ComponentChangeLis
 		setComponent(document, component);
 		
 		GUIUtil.setDisposableDialogOptions(this, null);
-		
-		// Set window position according to preferences, and set prefs when moving
-		Point position = Prefs.getWindowPosition(this.getClass());
-		if (position != null) {
-			this.setLocationByPlatform(false);
-			this.setLocation(position);
-		}
-		
-		this.addComponentListener(new ComponentAdapter() {
-			@Override
-			public void componentMoved(ComponentEvent e) {
-				Prefs.setWindowPosition(ComponentConfigDialog.this.getClass(),
-						ComponentConfigDialog.this.getLocation());
-			}
-		});
-		
+		GUIUtil.rememberWindowPosition(this);
 	}
 	
 	
diff --git a/src/net/sf/openrocket/gui/dialogs/optimization/OptimizationPlotDialog.java b/src/net/sf/openrocket/gui/dialogs/optimization/OptimizationPlotDialog.java
index 04885a82c..c90c31ab1 100644
--- a/src/net/sf/openrocket/gui/dialogs/optimization/OptimizationPlotDialog.java
+++ b/src/net/sf/openrocket/gui/dialogs/optimization/OptimizationPlotDialog.java
@@ -113,6 +113,7 @@ public class OptimizationPlotDialog extends JDialog {
 		this.add(panel);
 		
 		GUIUtil.setDisposableDialogOptions(this, close);
+		GUIUtil.rememberWindowSize(this);
 	}
 	
 	
diff --git a/src/net/sf/openrocket/gui/main/BasicFrame.java b/src/net/sf/openrocket/gui/main/BasicFrame.java
index 5fb99fb23..5f2d92e01 100644
--- a/src/net/sf/openrocket/gui/main/BasicFrame.java
+++ b/src/net/sf/openrocket/gui/main/BasicFrame.java
@@ -6,8 +6,6 @@ import java.awt.Toolkit;
 import java.awt.Window;
 import java.awt.event.ActionEvent;
 import java.awt.event.ActionListener;
-import java.awt.event.ComponentAdapter;
-import java.awt.event.ComponentEvent;
 import java.awt.event.KeyEvent;
 import java.awt.event.MouseAdapter;
 import java.awt.event.MouseEvent;
@@ -233,19 +231,16 @@ public class BasicFrame extends JFrame {
 		setTitle();
 		this.pack();
 		
-		Dimension size = Prefs.getWindowSize(this.getClass());
-		if (size == null) {
-			size = Toolkit.getDefaultToolkit().getScreenSize();
-			size.width = size.width * 9 / 10;
-			size.height = size.height * 9 / 10;
-		}
+
+		// Set initial window size
+		Dimension size = Toolkit.getDefaultToolkit().getScreenSize();
+		size.width = size.width * 9 / 10;
+		size.height = size.height * 9 / 10;
 		this.setSize(size);
-		this.addComponentListener(new ComponentAdapter() {
-			@Override
-			public void componentResized(ComponentEvent e) {
-				Prefs.setWindowSize(BasicFrame.this.getClass(), BasicFrame.this.getSize());
-			}
-		});
+		
+		// Remember changed size
+		GUIUtil.rememberWindowSize(this);
+		
 		this.setLocationByPlatform(true);
 		
 		GUIUtil.setWindowIcons(this);
@@ -259,8 +254,8 @@ public class BasicFrame extends JFrame {
 				closeAction();
 			}
 		});
-		frames.add(this);
 		
+		frames.add(this);
 		log.debug("BasicFrame instantiation complete");
 	}
 	
@@ -673,14 +668,13 @@ public class BasicFrame extends JFrame {
 		
 		menu = new JMenu(trans.get("main.menu.help"));
 		menu.setMnemonic(KeyEvent.VK_H);
-		//// Information about OpenRocket
 		menu.getAccessibleContext().setAccessibleDescription(trans.get("main.menu.help.desc"));
 		menubar.add(menu);
 		
 
 		//// License
 		item = new JMenuItem(trans.get("main.menu.help.license"), KeyEvent.VK_L);
-		//// OpenRocket license information
+		item.setIcon(Icons.HELP_LICENSE);
 		item.getAccessibleContext().setAccessibleDescription(trans.get("main.menu.help.license.desc"));
 		item.addActionListener(new ActionListener() {
 			@Override
@@ -695,7 +689,7 @@ public class BasicFrame extends JFrame {
 		
 		//// Bug report
 		item = new JMenuItem(trans.get("main.menu.help.bugReport"), KeyEvent.VK_B);
-		//// Information about reporting bugs in OpenRocket
+		item.setIcon(Icons.HELP_BUG_REPORT);
 		item.getAccessibleContext().setAccessibleDescription(trans.get("main.menu.help.bugReport.desc"));
 		item.addActionListener(new ActionListener() {
 			@Override
@@ -708,7 +702,7 @@ public class BasicFrame extends JFrame {
 		
 		//// Debug log
 		item = new JMenuItem(trans.get("main.menu.help.debugLog"));
-		//// View the OpenRocket debug log
+		item.setIcon(Icons.HELP_DEBUG_LOG);
 		item.getAccessibleContext().setAccessibleDescription(trans.get("main.menu.help.debugLog.desc"));
 		item.addActionListener(new ActionListener() {
 			@Override
@@ -723,7 +717,7 @@ public class BasicFrame extends JFrame {
 		
 		//// About
 		item = new JMenuItem(trans.get("main.menu.help.about"), KeyEvent.VK_A);
-		//// About OpenRocket
+		item.setIcon(Icons.HELP_ABOUT);
 		item.getAccessibleContext().setAccessibleDescription(trans.get("main.menu.help.about.desc"));
 		item.addActionListener(new ActionListener() {
 			@Override
diff --git a/src/net/sf/openrocket/gui/plot/SimulationPlotDialog.java b/src/net/sf/openrocket/gui/plot/SimulationPlotDialog.java
index c68617b7a..e1e1b2d7a 100644
--- a/src/net/sf/openrocket/gui/plot/SimulationPlotDialog.java
+++ b/src/net/sf/openrocket/gui/plot/SimulationPlotDialog.java
@@ -461,6 +461,7 @@ public class SimulationPlotDialog extends JDialog {
 		this.pack();
 		
 		GUIUtil.setDisposableDialogOptions(this, button);
+		GUIUtil.rememberWindowSize(this);
 	}
 	
 	private String getLabel(FlightDataType type, Unit unit) {
diff --git a/src/net/sf/openrocket/models/gravity/WGSGravityModel.java b/src/net/sf/openrocket/models/gravity/WGSGravityModel.java
index 9339c0eb7..253d4a41d 100644
--- a/src/net/sf/openrocket/models/gravity/WGSGravityModel.java
+++ b/src/net/sf/openrocket/models/gravity/WGSGravityModel.java
@@ -10,13 +10,10 @@ import net.sf.openrocket.util.WorldCoordinate;
  */
 public class WGSGravityModel implements GravityModel {
 	
+	// Cache the previously computed value
 	private WorldCoordinate lastWorldCoordinate;
 	private double lastg;
 	
-
-	private static int hit = 0;
-	private static int miss = 0;
-	
 	
 	@Override
 	public double getGravity(WorldCoordinate wc) {
@@ -25,12 +22,7 @@ public class WGSGravityModel implements GravityModel {
 		if (wc != this.lastWorldCoordinate) {
 			this.lastg = calcGravity(wc);
 			this.lastWorldCoordinate = wc;
-			
-			miss++;
-		} else {
-			hit++;
 		}
-		System.out.println("GRAVITY MODEL:  hit=" + hit + " miss=" + miss);
 		
 		return this.lastg;
 		
diff --git a/src/net/sf/openrocket/simulation/RK4SimulationStepper.java b/src/net/sf/openrocket/simulation/RK4SimulationStepper.java
index 4aa9494ce..eea602770 100644
--- a/src/net/sf/openrocket/simulation/RK4SimulationStepper.java
+++ b/src/net/sf/openrocket/simulation/RK4SimulationStepper.java
@@ -266,6 +266,9 @@ public class RK4SimulationStepper extends AbstractSimulationStepper {
 		if (status.getRocketVelocity().length2() > 1e18 ||
 				status.getRocketPosition().length2() > 1e18 ||
 				status.getRocketRotationVelocity().length2() > 1e18) {
+			
+			// FIXME:  Make error message better, recommend shortening time step
+			
 			throw new SimulationCalculationException("Simulation values exceeded limits");
 		}
 	}
@@ -547,9 +550,9 @@ public class RK4SimulationStepper extends AbstractSimulationStepper {
 		data.setValue(FlightDataType.TYPE_POSITION_X, status.getRocketPosition().x);
 		data.setValue(FlightDataType.TYPE_POSITION_Y, status.getRocketPosition().y);
 		
-		if (status.getSimulationConditions().getGeodeticComputation() != GeodeticComputationStrategy.NONE) {
-			data.setValue(FlightDataType.TYPE_LATITUDE, status.getRocketWorldPosition().getLatitudeRad());
-			data.setValue(FlightDataType.TYPE_LONGITUDE, status.getRocketWorldPosition().getLongitudeRad());
+		data.setValue(FlightDataType.TYPE_LATITUDE, status.getRocketWorldPosition().getLatitudeRad());
+		data.setValue(FlightDataType.TYPE_LONGITUDE, status.getRocketWorldPosition().getLongitudeRad());
+		if (status.getSimulationConditions().getGeodeticComputation() != GeodeticComputationStrategy.FLAT) {
 			data.setValue(FlightDataType.TYPE_CORIOLIS_ACCELERATION, store.coriolisAcceleration.length());
 		}
 		
diff --git a/src/net/sf/openrocket/simulation/SimulationOptions.java b/src/net/sf/openrocket/simulation/SimulationOptions.java
index 0c7f103a6..e6e8f821a 100644
--- a/src/net/sf/openrocket/simulation/SimulationOptions.java
+++ b/src/net/sf/openrocket/simulation/SimulationOptions.java
@@ -62,6 +62,7 @@ public class SimulationOptions implements ChangeSource, Cloneable {
 	private double windAverage = 2.0;
 	private double windTurbulence = 0.1;
 	
+
 	/*
 	 * SimulationOptions maintains the launch site parameters as separate double values,
 	 * and converts them into a WorldCoordinate when converting to SimulationConditions.
diff --git a/src/net/sf/openrocket/util/GUIUtil.java b/src/net/sf/openrocket/util/GUIUtil.java
index d8aec3b0a..e74dbc196 100644
--- a/src/net/sf/openrocket/util/GUIUtil.java
+++ b/src/net/sf/openrocket/util/GUIUtil.java
@@ -2,6 +2,7 @@ package net.sf.openrocket.util;
 
 import java.awt.Component;
 import java.awt.Container;
+import java.awt.Dimension;
 import java.awt.Font;
 import java.awt.Image;
 import java.awt.KeyboardFocusManager;
@@ -9,6 +10,8 @@ import java.awt.Point;
 import java.awt.Window;
 import java.awt.event.ActionEvent;
 import java.awt.event.ActionListener;
+import java.awt.event.ComponentAdapter;
+import java.awt.event.ComponentEvent;
 import java.awt.event.ComponentListener;
 import java.awt.event.FocusListener;
 import java.awt.event.KeyEvent;
@@ -39,6 +42,7 @@ import javax.swing.JButton;
 import javax.swing.JComboBox;
 import javax.swing.JComponent;
 import javax.swing.JDialog;
+import javax.swing.JFrame;
 import javax.swing.JLabel;
 import javax.swing.JRootPane;
 import javax.swing.JSlider;
@@ -267,6 +271,60 @@ public class GUIUtil {
 	}
 	
 	
+
+	/**
+	 * Automatically remember the size of a window.  This stores the window size in the user
+	 * preferences when resizing/maximizing the window and sets the state on the first call.
+	 */
+	public static void rememberWindowSize(final Window window) {
+		window.addComponentListener(new ComponentAdapter() {
+			@Override
+			public void componentResized(ComponentEvent e) {
+				log.debug("Storing size of " + window.getClass().getName() + ": " + window.getSize());
+				Prefs.setWindowSize(window.getClass(), window.getSize());
+				if (window instanceof JFrame) {
+					if ((((JFrame) window).getExtendedState() & JFrame.MAXIMIZED_BOTH) == JFrame.MAXIMIZED_BOTH) {
+						log.debug("Storing maximized state of " + window.getClass().getName());
+						Prefs.setWindowMaximized(window.getClass());
+					}
+				}
+			}
+		});
+		
+		if (Prefs.isWindowMaximized(window.getClass())) {
+			if (window instanceof JFrame) {
+				((JFrame) window).setExtendedState(JFrame.MAXIMIZED_BOTH);
+			}
+		} else {
+			Dimension dim = Prefs.getWindowSize(window.getClass());
+			if (dim != null) {
+				window.setSize(dim);
+			}
+		}
+	}
+	
+	
+	/**
+	 * Automatically remember the position of a window.  The position is stored in the user preferences
+	 * every time the window is moved and set from there when first calling this method.
+	 */
+	public static void rememberWindowPosition(final Window window) {
+		window.addComponentListener(new ComponentAdapter() {
+			@Override
+			public void componentMoved(ComponentEvent e) {
+				Prefs.setWindowPosition(window.getClass(), window.getLocation());
+			}
+		});
+		
+		// Set window position according to preferences, and set prefs when moving
+		Point position = Prefs.getWindowPosition(window.getClass());
+		if (position != null) {
+			window.setLocationByPlatform(false);
+			window.setLocation(position);
+		}
+	}
+	
+	
 	/**
 	 * Changes the style of the font of the specified border.
 	 * 
diff --git a/src/net/sf/openrocket/util/GeodeticComputationStrategy.java b/src/net/sf/openrocket/util/GeodeticComputationStrategy.java
index 8e7d2f299..6f1fb53aa 100644
--- a/src/net/sf/openrocket/util/GeodeticComputationStrategy.java
+++ b/src/net/sf/openrocket/util/GeodeticComputationStrategy.java
@@ -5,22 +5,41 @@ import net.sf.openrocket.startup.Application;
 
 /**
  * A strategy that performs computations on WorldCoordinates.
+ * <p>
+ * The directions of the coordinate is:
+ *   positive X = EAST
+ *   positive Y = NORTH
+ *   positive Z = UPWARDS
  */
 public enum GeodeticComputationStrategy {
 	
 
 	/**
-	 * Perform no geodetic computations.  The addCoordinate method does nothing and
-	 * getCoriolisAcceleration returns Coordinate.NUL.
+	 * Perform computations using a flat Earth approximation.  addCoordinate computes the
+	 * location using a direct meters-per-degree scaling and getCoriolisAcceleration always
+	 * returns NUL.
 	 */
-	NONE {
+	FLAT {
+		private static final double METERS_PER_DEGREE_LATITUDE = 111325; // "standard figure"
+		private static final double METERS_PER_DEGREE_LONGITUDE_EQUATOR = 111050;
+		
+		
 		@Override
-		public WorldCoordinate addCoordinate(WorldCoordinate latlon, Coordinate delta) {
-			return latlon;
+		public WorldCoordinate addCoordinate(WorldCoordinate location, Coordinate delta) {
+			
+			double metersPerDegreeLongitude = METERS_PER_DEGREE_LONGITUDE_EQUATOR * Math.cos(location.getLatitudeRad());
+			// Limit to 1 meter per degree near poles
+			metersPerDegreeLongitude = MathUtil.max(metersPerDegreeLongitude, 1);
+			
+			double newLat = location.getLatitudeDeg() + delta.y / METERS_PER_DEGREE_LATITUDE;
+			double newLon = location.getLongitudeDeg() + delta.x / metersPerDegreeLongitude;
+			double newAlt = location.getAltitude() + delta.z;
+			
+			return new WorldCoordinate(newLat, newLon, newAlt);
 		}
 		
 		@Override
-		public Coordinate getCoriolisAcceleration(WorldCoordinate latlon, Coordinate velocity) {
+		public Coordinate getCoriolisAcceleration(WorldCoordinate location, Coordinate velocity) {
 			return Coordinate.NUL;
 		}
 	},
@@ -37,9 +56,13 @@ public enum GeodeticComputationStrategy {
 			// bearing (in radians, clockwise from north);
 			// d/R is the angular distance (in radians), where d is the distance traveled and R is the earth’s radius
 			double d = MathUtil.hypot(delta.x, delta.y);
-			double bearing = Math.atan(delta.x / delta.y);
-			if (delta.y < 0)
-				bearing = bearing + Math.PI;
+			
+			// Check for zero movement before computing bearing
+			if (MathUtil.equals(d, 0)) {
+				return new WorldCoordinate(location.getLatitudeDeg(), location.getLongitudeDeg(), newAlt);
+			}
+			
+			double bearing = Math.atan2(delta.x, delta.y);
 			
 			// Calculate the new lat and lon		
 			double newLat, newLon;
@@ -51,20 +74,17 @@ public enum GeodeticComputationStrategy {
 			newLat = Math.asin(sinLat * cosDR + cosLat * sinDR * Math.cos(bearing));
 			newLon = location.getLongitudeRad() + Math.atan2(Math.sin(bearing) * sinDR * cosLat, cosDR - sinLat * Math.sin(newLat));
 			
-			if (Double.isNaN(newLat)) {
-				newLat = location.getLatitudeRad();
-			}
-			
-			if (Double.isNaN(newLon)) {
-				newLon = location.getLongitudeRad();
+			if (Double.isNaN(newLat) || Double.isNaN(newLon)) {
+				throw new BugException("addCoordinate resulted in NaN location:  location=" + location + " delta=" + delta
+						+ " newLat=" + newLat + " newLon=" + newLon);
 			}
 			
 			return new WorldCoordinate(Math.toDegrees(newLat), Math.toDegrees(newLon), newAlt);
 		}
 		
 		@Override
-		public Coordinate getCoriolisAcceleration(WorldCoordinate latlon, Coordinate velocity) {
-			return computeCoriolisAcceleration(latlon, velocity);
+		public Coordinate getCoriolisAcceleration(WorldCoordinate location, Coordinate velocity) {
+			return computeCoriolisAcceleration(location, velocity);
 		}
 		
 	},
@@ -81,6 +101,12 @@ public enum GeodeticComputationStrategy {
 			// bearing (in radians, clockwise from north);
 			// d/R is the angular distance (in radians), where d is the distance traveled and R is the earth’s radius
 			double d = MathUtil.hypot(delta.x, delta.y);
+			
+			// Check for zero movement before computing bearing
+			if (MathUtil.equals(d, 0)) {
+				return new WorldCoordinate(location.getLatitudeDeg(), location.getLongitudeDeg(), newAlt);
+			}
+			
 			double bearing = Math.atan(delta.x / delta.y);
 			if (delta.y < 0)
 				bearing = bearing + Math.PI;
@@ -91,19 +117,17 @@ public enum GeodeticComputationStrategy {
 			newLat = ret[0];
 			newLon = ret[1];
 			
-			if (Double.isNaN(newLat)) {
-				newLat = location.getLatitudeRad();
+			if (Double.isNaN(newLat) || Double.isNaN(newLon)) {
+				throw new BugException("addCoordinate resulted in NaN location:  location=" + location + " delta=" + delta
+						+ " newLat=" + newLat + " newLon=" + newLon);
 			}
 			
-			if (Double.isNaN(newLon)) {
-				newLon = location.getLongitudeRad();
-			}
-			return new WorldCoordinate(newLat, newLon, newAlt);
+			return new WorldCoordinate(Math.toDegrees(newLat), Math.toDegrees(newLon), newAlt);
 		}
 		
 		@Override
-		public Coordinate getCoriolisAcceleration(WorldCoordinate latlon, Coordinate velocity) {
-			return computeCoriolisAcceleration(latlon, velocity);
+		public Coordinate getCoriolisAcceleration(WorldCoordinate location, Coordinate velocity) {
+			return computeCoriolisAcceleration(location, velocity);
 		}
 	};
 	
@@ -136,13 +160,13 @@ public enum GeodeticComputationStrategy {
 	/**
 	 * Add a cartesian movement coordinate to a WorldCoordinate.
 	 */
-	public abstract WorldCoordinate addCoordinate(WorldCoordinate latlon, Coordinate delta);
+	public abstract WorldCoordinate addCoordinate(WorldCoordinate location, Coordinate delta);
 	
 	
 	/**
 	 * Compute the coriolis acceleration at a specified WorldCoordinate and velocity.
 	 */
-	public abstract Coordinate getCoriolisAcceleration(WorldCoordinate latlon, Coordinate velocity);
+	public abstract Coordinate getCoriolisAcceleration(WorldCoordinate location, Coordinate velocity);
 	
 	
 
diff --git a/src/net/sf/openrocket/util/Icons.java b/src/net/sf/openrocket/util/Icons.java
index 7639bcdc7..e5f41317a 100644
--- a/src/net/sf/openrocket/util/Icons.java
+++ b/src/net/sf/openrocket/util/Icons.java
@@ -62,6 +62,11 @@ public class Icons {
 	public static final Icon EDIT_DELETE = loadImageIcon("pix/icons/edit-delete.png", "Delete");
 	public static final Icon EDIT_SCALE = loadImageIcon("pix/icons/edit-scale.png", "Scale");
 	
+	public static final Icon HELP_ABOUT = loadImageIcon("pix/icons/help-about.png", "About");
+	public static final Icon HELP_BUG_REPORT = loadImageIcon("pix/icons/help-bug.png", "Bug report");
+	public static final Icon HELP_DEBUG_LOG = loadImageIcon("pix/icons/help-log.png", "Debug log");
+	public static final Icon HELP_LICENSE = loadImageIcon("pix/icons/help-license.png", "License");
+	
 	public static final Icon ZOOM_IN = loadImageIcon("pix/icons/zoom-in.png", "Zoom in");
 	public static final Icon ZOOM_OUT = loadImageIcon("pix/icons/zoom-out.png", "Zoom out");
 	
diff --git a/src/net/sf/openrocket/util/Prefs.java b/src/net/sf/openrocket/util/Prefs.java
index 2d59f7768..dfe05a1d4 100644
--- a/src/net/sf/openrocket/util/Prefs.java
+++ b/src/net/sf/openrocket/util/Prefs.java
@@ -38,8 +38,8 @@ import net.sf.openrocket.rocketcomponent.RecoveryDevice;
 import net.sf.openrocket.rocketcomponent.Rocket;
 import net.sf.openrocket.rocketcomponent.RocketComponent;
 import net.sf.openrocket.simulation.FlightDataType;
-import net.sf.openrocket.simulation.SimulationOptions;
 import net.sf.openrocket.simulation.RK4SimulationStepper;
+import net.sf.openrocket.simulation.SimulationOptions;
 import net.sf.openrocket.startup.Application;
 import net.sf.openrocket.unit.UnitGroup;
 
@@ -715,11 +715,22 @@ public class Prefs {
 		return new Dimension(x, y);
 	}
 	
+	
+	public static boolean isWindowMaximized(Class<?> c) {
+		String pref = PREFNODE.node("windows").get("size." + c.getCanonicalName(), null);
+		return "max".equals(pref);
+	}
+	
 	public static void setWindowSize(Class<?> c, Dimension d) {
 		PREFNODE.node("windows").put("size." + c.getCanonicalName(), "" + d.width + "," + d.height);
 		storeVersion();
 	}
 	
+	public static void setWindowMaximized(Class<?> c) {
+		PREFNODE.node("windows").put("size." + c.getCanonicalName(), "max");
+		storeVersion();
+	}
+	
 	
 	////  Printing
 	
diff --git a/src/net/sf/openrocket/util/WorldCoordinate.java b/src/net/sf/openrocket/util/WorldCoordinate.java
index b3a047fde..762b02958 100644
--- a/src/net/sf/openrocket/util/WorldCoordinate.java
+++ b/src/net/sf/openrocket/util/WorldCoordinate.java
@@ -15,9 +15,10 @@ public class WorldCoordinate {
 	
 	/**
 	 * Constructs a new WorldCoordinate
+	 * 
 	 * @param lat latitude in degrees north. From -90 to 90, values outside are clamped.
 	 * @param lon longitude in degrees east. From -180 to 180, values outside are reduced to the range.
-	 * @param alt altitude in m. Unbounded.
+	 * @param alt altitude in meters. Unbounded.
 	 */
 	public WorldCoordinate(double lat, double lon, double alt) {
 		this.lat = MathUtil.clamp(Math.toRadians(lat), -Math.PI / 2, Math.PI / 2);
@@ -26,33 +27,35 @@ public class WorldCoordinate {
 	}
 	
 	
-
+	/**
+	 * Returns the altitude.
+	 */
 	public double getAltitude() {
 		return this.alt;
 	}
 	
-	/*
+	/**
 	 * Returns Longitude in radians
 	 */
 	public double getLongitudeRad() {
 		return this.lon;
 	}
 	
-	/*
+	/**
 	 * Returns Longitude in degrees
 	 */
 	public double getLongitudeDeg() {
 		return Math.toDegrees(this.lon);
 	}
 	
-	/*
+	/**
 	 * Returns latitude in radians
 	 */
 	public double getLatitudeRad() {
 		return this.lat;
 	}
 	
-	/*
+	/**
 	 * Returns latitude in degrees
 	 */
 	public double getLatitudeDeg() {
@@ -60,9 +63,10 @@ public class WorldCoordinate {
 	}
 	
 	
+
 	@Override
 	public String toString() {
-		return "WorldCoordinate[lat=" + lat + ", lon=" + lon + ", alt=" + alt + "]";
+		return "WorldCoordinate[lat=" + getLatitudeDeg() + ", lon=" + getLongitudeDeg() + ", alt=" + getAltitude() + "]";
 	}
 	
 	
diff --git a/test/net/sf/openrocket/util/GeodeticComputationStrategyTest.java b/test/net/sf/openrocket/util/GeodeticComputationStrategyTest.java
index 7adf21c8f..72800d01f 100644
--- a/test/net/sf/openrocket/util/GeodeticComputationStrategyTest.java
+++ b/test/net/sf/openrocket/util/GeodeticComputationStrategyTest.java
@@ -7,7 +7,7 @@ import org.junit.Test;
 public class GeodeticComputationStrategyTest {
 	
 	@Test
-	public void testAddCoordinate() {
+	public void testSpericalAddCoordinate() {
 		
 		double arcmin = (1.0 / 60.0);
 		double arcsec = (1.0 / (60.0 * 60.0));
@@ -36,8 +36,109 @@ public class GeodeticComputationStrategyTest {
 		assertEquals(1000.0, wc.getAltitude(), 0.0);
 	}
 	
+	
 	@Test
-	public void testGetCoriolisAcceleration1() {
+	public void testAddCoordinates() {
+		
+		double min = 1 / 60.0;
+		double sec = 1 / 3600.0;
+		
+
+		// Test zero movement
+		testAddCoordinate(50.0, 20.0, 0, 123, 50.0, 20.0, false);
+		
+
+		/*
+		 * These example values have been computed using the calculator at
+		 * http://www.movable-type.co.uk/scripts/latlong.html
+		 */
+
+		// Long distance NE over England, crosses Greenwich meridian
+		// 50 03N  005 42W  to  58 38N  003 04E  is  1109km at 027 16'07"
+		testAddCoordinate(50 + 3 * min, -5 - 42 * min, 1109000, 27 + 16 * min + 7 * sec, 58 + 38 * min, 3 + 4 * min, false);
+		
+		// SW over Brazil
+		// -10N  -60E  to  -11N  -61E  is  155.9km at 224 25'34"
+		testAddCoordinate(-10, -60, 155900, 224 + 25 * min + 34 * sec, -11, -61, true);
+		
+		// NW over the 180 meridian
+		// 63N  -179E  to  63 01N  179E  is  100.9km at 271 56'34"
+		testAddCoordinate(63, -179, 100900, 271 + 56 * min + 34 * sec, 63 + 1 * min, 179, true);
+		
+		// NE near the north pole
+		// 89 50N  0E  to 89 45N  175E  is 46.29 km at 003 00'01"
+		testAddCoordinate(89 + 50 * min, 0, 46290, 3 + 0 * min + 1 * sec, 89 + 45 * min, 175, false);
+		
+		// S directly over south pole
+		// -89 50N  12E  to  -89 45N  192E  is  46.33km at 180 00'00"
+		testAddCoordinate(-89 - 50 * min, 12, 46330, 180, -89 - 45 * min, -168, false);
+		
+	}
+	
+	private void testAddCoordinate(double initialLatitude, double initialLongitude, double distance, double bearing,
+				double finalLatitude, double finalLongitude, boolean testFlat) {
+		
+		double tolerance;
+		
+		bearing = Math.toRadians(bearing);
+		
+		// positive X is EAST, positive Y is NORTH
+		double deltaX = distance * Math.sin(bearing);
+		double deltaY = distance * Math.cos(bearing);
+		
+		Coordinate coord = new Coordinate(deltaX, deltaY, 1000.0);
+		WorldCoordinate wc = new WorldCoordinate(initialLatitude, initialLongitude, 0.0);
+		
+		// Test SPHERICAL
+		tolerance = 0.0015 * distance / 111325;
+		System.out.println("\nSpherical tolerance: " + tolerance);
+		WorldCoordinate result = GeodeticComputationStrategy.SPHERICAL.addCoordinate(wc, coord);
+		
+		System.out.println("Difference Lat: " + Math.abs(finalLatitude - result.getLatitudeDeg()));
+		System.out.println("Difference Lon: " + Math.abs(finalLongitude - result.getLongitudeDeg()));
+		assertEquals(finalLatitude, result.getLatitudeDeg(), tolerance);
+		assertEquals(finalLongitude, result.getLongitudeDeg(), tolerance);
+		assertEquals(1000.0, result.getAltitude(), 0.0);
+		
+
+		// Test WGS84
+		/*
+		 * TODO: Since the example values are computed using a spherical earth approximation,
+		 * the WGS84 method will have significantly larger errors.  The tolerance should be
+		 * increased correspondingly.
+		 */
+		//tolerance = ...
+		System.out.println("\nWGS84 tolerance: " + tolerance);
+		result = GeodeticComputationStrategy.WGS84.addCoordinate(result, coord);
+		
+		System.out.println("Difference Lat: " + Math.abs(finalLatitude - result.getLatitudeDeg()));
+		System.out.println("Difference Lon: " + Math.abs(finalLongitude - result.getLongitudeDeg()));
+		// FIXME: Re-enable these when they function
+		//		assertEquals(finalLatitude, result.getLatitudeDeg(), tolerance);
+		//		assertEquals(finalLongitude, result.getLongitudeDeg(), tolerance);
+		//		assertEquals(1000.0, result.getAltitude(), 0.0);
+		
+
+		// Test FLAT
+		if (testFlat) {
+			tolerance = 0.02 * distance / 111325;
+			System.out.println("\nFlat tolerance: " + tolerance);
+			result = GeodeticComputationStrategy.FLAT.addCoordinate(wc, coord);
+			
+			System.out.println("Difference Lat: " + Math.abs(finalLatitude - result.getLatitudeDeg()));
+			System.out.println("Difference Lon: " + Math.abs(finalLongitude - result.getLongitudeDeg()));
+			assertEquals(finalLatitude, result.getLatitudeDeg(), tolerance);
+			assertEquals(finalLongitude, result.getLongitudeDeg(), tolerance);
+			assertEquals(1000.0, result.getAltitude(), 0.0);
+			
+		}
+		
+	}
+	
+	
+
+	@Test
+	public void testSpericalGetCoriolisAcceleration() {
 		
 		// For positive latitude and rotational velocity, a movement due east results in an acceleration due south
 		Coordinate velocity = new Coordinate(-1000, 0, 0);