/** * */ package com.momega.spacesimulator.swing; import java.awt.Dimension; import java.awt.event.ActionEvent; import java.awt.event.ActionListener; import java.awt.event.ItemEvent; import java.awt.event.ItemListener; import java.beans.PropertyChangeEvent; import java.beans.PropertyChangeListener; import java.text.DecimalFormat; import java.util.Calendar; import javax.swing.GroupLayout; import javax.swing.JButton; import javax.swing.JComboBox; import javax.swing.JFormattedTextField; import javax.swing.JLabel; import javax.swing.SwingConstants; import javax.swing.text.NumberFormatter; import org.apache.commons.math3.util.FastMath; import com.momega.spacesimulator.context.ModelHolder; import com.momega.spacesimulator.model.CelestialBody; import com.momega.spacesimulator.model.KeplerianElements; import com.momega.spacesimulator.model.MovingObject; import com.momega.spacesimulator.model.Planet; import com.momega.spacesimulator.model.Spacecraft; import com.momega.spacesimulator.model.Timestamp; import com.momega.spacesimulator.utils.MathUtils; import com.momega.spacesimulator.utils.TimeUtils; /** * @author martin * */ public class InterplanetaryFlightPanel extends AbstractDefaultPanel implements PropertyChangeListener { private static final long serialVersionUID = -6729550892556811416L; private SpacecraftObjectModel spacecraftObjectModel; private PlanetsObjectModel planetObjectModel; private JFormattedTextField txtTransferA; private Spacecraft selectedSpacecraft; private MovingObject sourceBody; // could be also earth-moon center private MovingObject targetPlanet; private JFormattedTextField txtEccentricity; private CelestialBody rootBody; private double atx; private double rA; private double rB; private JFormattedTextField txtTheta; private JFormattedTextField txtTof; private MovingObject targetBody; private JFormattedTextField txtAngle; private JFormattedTextField txtCurrentAngle; private TimePanel timePanel; private JFormattedTextField txtAngleDiff; private double angle; public InterplanetaryFlightPanel() { spacecraftObjectModel = new SpacecraftObjectModel(); planetObjectModel = new PlanetsObjectModel(); GroupLayout layout = new GroupLayout(this); setLayout(layout); layout.setAutoCreateGaps(true); layout.setAutoCreateContainerGaps(true); JLabel lblSpacecraft = new JLabel("Spacecraft:", SwingConstants.RIGHT); JLabel lblTargetPlanet = new JLabel("Target Planet:", SwingConstants.RIGHT); JLabel lblTransferA = new JLabel("Semimajor axis of target trajectory:", SwingConstants.RIGHT); JLabel lblEccentricity = new JLabel("Eccentricity of target trajectory:", SwingConstants.RIGHT); JLabel lblTargetTheta = new JLabel("Target true anomaly:", SwingConstants.RIGHT); JLabel lblTof = new JLabel("Time of flight:", SwingConstants.RIGHT); JLabel lblAngle = new JLabel("Phase angle:", SwingConstants.RIGHT); JLabel lblTime = new JLabel("Time:", SwingConstants.RIGHT); JLabel lblCurrentAngle = new JLabel("Current angle:", SwingConstants.RIGHT); JLabel lblAngleDiff = new JLabel("Angle Difference:", SwingConstants.RIGHT); JComboBox<Spacecraft> spacecraftBox = new JComboBox<Spacecraft>(); spacecraftBox.addItemListener(new ItemListener() { @Override public void itemStateChanged(ItemEvent e) { Object[] objs = e.getItemSelectable().getSelectedObjects(); Spacecraft spacecraft = (Spacecraft) objs[0]; selectedSpacecraft = spacecraft; calculateSemimajorTargetTrajectory(); } }); spacecraftBox.setModel(spacecraftObjectModel); selectedSpacecraft = (Spacecraft) spacecraftObjectModel.getSelectedItem(); spacecraftBox.setRenderer(new MovingObjectListRenderer()); spacecraftBox.setMaximumSize(new Dimension(300, 100)); JComboBox<Planet> planetsBox = new JComboBox<Planet>(); planetsBox.setModel(planetObjectModel); planetsBox.setRenderer(new MovingObjectListRenderer()); planetsBox.setMaximumSize(new Dimension(300, 100)); targetPlanet = (Planet) planetObjectModel.getSelectedItem(); if (selectedSpacecraft.getTarget()!=null) { targetPlanet = selectedSpacecraft.getTarget().getTargetBody(); planetObjectModel.setSelectedItem(targetPlanet); } rootBody = ModelHolder.getModel().getRootSoi().getBody(); NumberFormatter formatter = new NumberFormatter(new DecimalFormat("##0.0#####")); txtTransferA = new JFormattedTextField(formatter); txtTransferA.setText("0.0"); txtTransferA.addFocusListener(new FocusTextListener(txtTransferA)); txtEccentricity = new JFormattedTextField(formatter); txtEccentricity.setText("0.0"); txtEccentricity.setEnabled(false); txtTheta = new JFormattedTextField(formatter); txtTheta.setText("0.0"); txtTheta.setEnabled(false); txtTof = new JFormattedTextField(formatter); txtTof.setText("0.0"); txtTof.setEnabled(false); txtAngle = new JFormattedTextField(formatter); txtAngle.setText("0.0"); txtAngle.setEnabled(false); timePanel = new TimePanel(); timePanel.setModel(new DateTimeModel(TimeUtils.toCalendar(ModelHolder.getModel().getTime()))); txtCurrentAngle = new JFormattedTextField(formatter); txtCurrentAngle.setText("0.0"); txtCurrentAngle.setEnabled(false); txtAngleDiff = new JFormattedTextField(formatter); txtAngleDiff.setText("0.0"); txtAngleDiff.setEnabled(false); JButton btnCalculate = new JButton("Calculate"); btnCalculate.setIcon(SwingUtils.createImageIcon("/images/calculator.png")); btnCalculate.addActionListener(new ActionListener() { @Override public void actionPerformed(ActionEvent e) { calculate(); } }); layout.setVerticalGroup( layout.createSequentialGroup() .addGroup(layout.createParallelGroup(GroupLayout.Alignment.LEADING) .addComponent(lblSpacecraft) .addComponent(spacecraftBox) ) .addGroup(layout.createParallelGroup(GroupLayout.Alignment.LEADING) .addComponent(lblTargetPlanet) .addComponent(planetsBox) ) .addGroup(layout.createParallelGroup(GroupLayout.Alignment.LEADING) .addComponent(lblTransferA) .addComponent(txtTransferA) .addComponent(btnCalculate) ) .addGroup(layout.createParallelGroup(GroupLayout.Alignment.LEADING) .addComponent(lblEccentricity) .addComponent(txtEccentricity) ) .addGroup(layout.createParallelGroup(GroupLayout.Alignment.LEADING) .addComponent(lblTargetTheta) .addComponent(txtTheta) ) .addGroup(layout.createParallelGroup(GroupLayout.Alignment.LEADING) .addComponent(lblTof) .addComponent(txtTof) ) .addGroup(layout.createParallelGroup(GroupLayout.Alignment.LEADING) .addComponent(lblAngle) .addComponent(txtAngle) ) .addGroup(layout.createParallelGroup(GroupLayout.Alignment.LEADING) .addComponent(lblTime) .addComponent(timePanel) ) .addGroup(layout.createParallelGroup(GroupLayout.Alignment.LEADING) .addComponent(lblCurrentAngle) .addComponent(txtCurrentAngle) ) .addGroup(layout.createParallelGroup(GroupLayout.Alignment.LEADING) .addComponent(lblAngleDiff) .addComponent(txtAngleDiff) ) ); layout.setHorizontalGroup( layout.createSequentialGroup() .addGroup(layout.createParallelGroup(GroupLayout.Alignment.LEADING) .addComponent(lblSpacecraft) .addComponent(lblTargetPlanet) .addComponent(lblTransferA) .addComponent(lblEccentricity) .addComponent(lblTargetTheta) .addComponent(lblTof) .addComponent(lblAngle) .addComponent(lblTime) .addComponent(lblCurrentAngle) .addComponent(lblAngleDiff) ) .addGroup(layout.createParallelGroup(GroupLayout.Alignment.LEADING) .addComponent(spacecraftBox) .addComponent(planetsBox) .addComponent(txtTransferA) .addComponent(txtEccentricity) .addComponent(txtTheta) .addComponent(txtTof) .addComponent(txtAngle) .addComponent(timePanel) .addComponent(txtCurrentAngle) .addComponent(txtAngleDiff) ) .addGroup(layout.createParallelGroup(GroupLayout.Alignment.LEADING) .addComponent(btnCalculate) ) ); planetsBox.addItemListener(new ItemListener() { @Override public void itemStateChanged(ItemEvent e) { Object[] objs = e.getItemSelectable().getSelectedObjects(); Planet planet = (Planet) objs[0]; targetPlanet = planet; calculateSemimajorTargetTrajectory(); } }); timePanel.getModel().addPropertyChangeListener(this); calculateSemimajorTargetTrajectory(); } protected void calculateSemimajorTargetTrajectory() { sourceBody = findObjectOrbitingCenter(selectedSpacecraft); if (sourceBody == null) { return; } targetBody = findObjectOrbitingCenter(targetPlanet); if (targetBody == sourceBody) { return; } rA = sourceBody.getKeplerianElements().getKeplerianOrbit().getSemimajorAxis(); rB = targetBody.getKeplerianElements().getKeplerianOrbit().getSemimajorAxis(); atx = (rA + rB)/2; txtTransferA.setText(String.format("%3.6f", atx/ MathUtils.AU)); } protected void calculate() { atx = Double.parseDouble(txtTransferA.getText()) * MathUtils.AU; double e = 1 - (rA/atx); double theta = FastMath.acos((atx/rB*(1-e*e)-1)/e); double E = KeplerianElements.solveEA(e, theta); double mi = rootBody.getGravitationParameter(); double tof = (E - e* FastMath.sin(E)) * FastMath.sqrt(atx*atx*atx / mi); txtEccentricity.setText(String.format("%3.6f", e)); txtTheta.setText(String.format("%3.6f", Math.toDegrees(theta))); txtTof.setText(String.format("%3.6f", tof/60/60/24)); double omega = targetBody.getKeplerianElements().getKeplerianOrbit().getMeanMotion(); angle = theta - omega * tof; txtAngle.setText(String.format("%3.6f", Math.toDegrees(angle))); calculateAngle(); } protected MovingObject findObjectOrbitingCenter(MovingObject movingObject) { MovingObject m = movingObject; // TODO: Fix this // if (m.isStatic()) { // // we are orbiting center of the system, I do not know what to do // return null; // } // while(!m.getOrbitingBody().isStatic()) { // m = m.getOrbitingBody(); // } return m; } protected void calculateAngle() { Calendar cal = timePanel.getModel().getCalendar(); Timestamp timestamp = TimeUtils.fromCalendar(cal); calculateAngle(timestamp); } protected void calculateAngle(Timestamp timestamp) { double currentAngle = selectedSpacecraft.getPosition(timestamp).angle(targetPlanet.getPosition(timestamp)); txtCurrentAngle.setText(String.format("%3.6f", Math.toDegrees(currentAngle))); double angleDiff = angle - currentAngle; txtAngleDiff.setText(String.format("%3.6f", Math.toDegrees(angleDiff))); } @Override public void propertyChange(PropertyChangeEvent evt) { calculateAngle(); } }