jsattrak.gui.JTrackingPanel.java Source code

Java tutorial

Introduction

Here is the source code for jsattrak.gui.JTrackingPanel.java

Source

/*
 * JTrackingPanel.java
 * =====================================================================
 * Copyright (C) 2009 Shawn E. Gano
 * 
 * This file is part of JSatTrak.
 * 
 * JSatTrak is free software: you can redistribute it and/or modify
 * it under the terms of the GNU Lesser General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 * 
 * JSatTrak 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 Lesser General Public License for more details.
 * 
 * You should have received a copy of the GNU Lesser General Public License
 * along with JSatTrak.  If not, see <http://www.gnu.org/licenses/>.
 * =====================================================================
 *
 * Created on December 16, 2007, 6:38 PM
 */

package jsattrak.gui;

import static java.lang.Math.PI;
import static java.lang.Math.abs;
import static java.lang.Math.asin;
import static java.lang.Math.cos;
import static java.lang.Math.toDegrees;
import static name.gano.astro.MathUtils.cross;
import static name.gano.astro.MathUtils.dot;
import static name.gano.astro.MathUtils.norm;

import java.awt.event.ItemEvent;
import java.awt.print.PrinterException;
import java.io.BufferedWriter;
import java.io.File;
import java.io.FileWriter;
import java.io.IOException;
import java.io.Serializable;
import java.text.DecimalFormat;
import java.text.MessageFormat;
import java.util.ArrayList;
import java.util.Hashtable;
import java.util.List;

import javax.swing.JFileChooser;
import javax.swing.JOptionPane;
import javax.swing.ListSelectionModel;
import javax.swing.JTable.PrintMode;
import javax.swing.table.DefaultTableModel;

import jsattrak.objects.AbstractSatellite;
import jsattrak.objects.GroundStation;
import jsattrak.objects.SunSat;
import jsattrak.utilities.CustomFileFilter;
import jsattrak.utilities.GsListener;
import jsattrak.utilities.SatListener;
import name.gano.astro.AER;
import name.gano.astro.AstroConst;
import name.gano.astro.GeoFunctions;
import name.gano.astro.bodies.Sun;
import name.gano.astro.coordinates.CoordinateConversion;
import name.gano.astro.time.Time;
import name.gano.file.FileUtilities;

import org.joda.time.DateTime;

import edu.cusat.common.Failure;
import edu.cusat.gs.doppler.Doppler;
import edu.cusat.gs.rotator.GroundPass;
import edu.cusat.gs.rotator.RotatorController;

/**
 * TODO MAKE GSHASH FIRE OFF A PROPERTYCHANGED
 * @author Shawn
 */
public class JTrackingPanel extends javax.swing.JPanel implements GsListener, SatListener {

    private static final String CURRENT_TRACKING_INFORMATION = "Current Tracking Information: ";
    private static final String ROT_EL_LBL_TXT = "Elevation (Rotator) [deg]: ";
    private static final String ROT_AZ_LBL_TXT = "Azimuth (Rotator) [deg]: ";
    private static final String RANGE_LBL_TXT = "Range [m]: ";
    private static final String EL_LBL_TXT = "Elevation (Satellite) [deg]: ";
    private static final String AZ_LBL_TXT = "Azimuth (Satellite) [deg]: ";
    private static final String TIME_LBL_TXT = "Current Time: ";
    private static final String NEXT_PASS_LBL_TXT = "Next Pass at: ";
    // data to tell if Lead/Lag data should be updated
    private double oldLeadX = -1;
    private double oldLagX = -1;

    private String timeAsString;

    // current time object - passed for use in pass predictions
    private DateTime currentTime;

    /** table **/
    private DefaultTableModel passTableModel;

    /** app **/
    private JSatTrak app;

    /**
     * hash table to store pass num and midpoint times -- used to setting time
     * in app
     */
    private Hashtable<Integer, Double> passHash = new Hashtable<Integer, Double>();

    RotatorController rController;
    private GroundStation currentGs;
    private AbstractSatellite currentSat;

    private JSatTrak parent;

    /** Upcoming ground passes **/
    private List<GroundPass> futurePasses = new ArrayList<GroundPass>(0);

    /** <code>null</code> if there is no satellite overhead **/
    private GroundPass currentPass;

    private Doppler doppler;

    /**
     * Creates new form JTrackingPanel
     * @param app
     * @param timeAsStringIn
     * @param currentDate
     */
    public JTrackingPanel(JSatTrak app, String timeAsStringIn, Time currentDate, JSatTrak parent) {

        this.parent = parent;

        this.currentTime = currentDate.toDateTime();

        this.app = app;

        initComponents();

        // fill out choice boxes
        refreshComboBoxes();

        this.app.registerGsListener(this);
        this.app.registerSatListener(this);

        // do this after components INI
        this.timeAsString = timeAsStringIn;
        updateTime(timeAsString);

        // update pass table
        passTableModel = new DefaultTableModel();
        passTable.setModel(passTableModel);

        passTableModel.addColumn("#"); // pass number
        passTableModel.addColumn("Rise Time"); //
        passTableModel.addColumn("Rise Az."); // new
        passTableModel.addColumn("Set Time"); //
        passTableModel.addColumn("Set Az."); // new
        passTableModel.addColumn("Duration [Sec]"); //
        passTableModel.addColumn("Visibility"); //

        // passTable.setSelectionMode(ListSelectionModel.SINGLE_SELECTION);
        passTable.setSelectionMode(ListSelectionModel.MULTIPLE_INTERVAL_SELECTION);
        // passTable.setAutoCreateRowSorter(true);

        // add row highliters to passTable --oops not a swingx table
        // passTable.addHighlighter(new
        // ColorHighlighter(HighlightPredicate.EVEN, Color.WHITE, Color.black));
        // // even, background, foregrond
        // passTable.addHighlighter(new ColorHighlighter(HighlightPredicate.ODD,
        // new Color(229, 229, 229), Color.black)); // odd, background,
        // foregrond
    }

    public void refreshComboBoxes() {
        gsComboBox.removeAllItems();
        for (GroundStation gs : app.groundStations())
            gsComboBox.addItem(gs.getStationName());

        satComboBox.removeAllItems();
        for (AbstractSatellite sat : app.satellites())
            satComboBox.addItem(sat.getName());
    } // refreshComboBoxes

    /**
     * update time called .. update info in box in future might not want to
     * search hash each iteration for gs and sat objects
     *
     * @param timeAsString
     */
    public void updateTime(String timeAsString) {
        this.timeAsString = timeAsString;
        Time parse = app.currentJulianDate.parse(timeAsString);
        currentTime = parse.toDateTime();
        double mjd2 = Time.calcMjd(currentTime.toGregorianCalendar());
        updateTime();
    }

    /**
     * Requires that currentTime be set
     */
    private void updateTime() {
        // if something's not set, erase all data
        if (gsComboBox.getSelectedIndex() < 0 || satComboBox.getSelectedIndex() < 0) {
            clearCurrTrackInfo();
            return;
        }

        if (currentSat.getTEMEPos() != null && !Double.isNaN(currentSat.getTEMEPos()[0])) {
            double[] aer = currentGs.calculate_AER(currentSat.getTEMEPos()); // TEME

            // add text AER and string
            timeLbl.setText(TIME_LBL_TXT + timeAsString);
            azLbl.setText(String.format("%s %.3f", AZ_LBL_TXT, aer[0]));
            elLbl.setText(String.format("%s %.3f", EL_LBL_TXT, aer[1]));
            rangeLbl.setText(String.format("%s %.3f", RANGE_LBL_TXT, aer[2]));

            // update polar plot
            polarPlotLbl.update(timeAsString, aer, currentGs.getElevationConst(), currentGs.getStationName(),
                    currentSat.getName());

            // check to see if we need to update Lead/Lag data
            // no good J2000 positions not saved through time right now in
            // satprops
            // see if we even need to bother - lead data option selected in both
            // 2D and polar plot
            if (polarPlotLbl.isShowLeadLagData() && currentSat.isGroundTrackShown()) {
                boolean updateLeadData = false;
                boolean updateLagData = false;

                // need to update lead data?
                if (polarPlotLbl.getAerLead() == null || polarPlotLbl.getAerLead().length < 1) {
                    updateLeadData = true;
                    oldLeadX = currentSat.getTemePosLead()[0][0];
                } else if (oldLeadX != currentSat.getTemePosLead()[0][0]) {
                    updateLeadData = true;
                    oldLeadX = currentSat.getTemePosLead()[0][0];
                }

                // need to update lag data?
                if (polarPlotLbl.getAerLag() == null || polarPlotLbl.getAerLag().length < 1) {
                    updateLagData = true;
                    oldLagX = currentSat.getTemePosLag()[0][0];
                } else if (oldLagX != currentSat.getTemePosLag()[0][0]) {
                    updateLagData = true;
                    oldLagX = currentSat.getTemePosLag()[0][0];
                }

                // update Lead data if needed
                if (updateLeadData) {
                    double[][] leadData = AER.calculate(currentGs.getLla_deg_m(), currentSat.getTemePosLead(),
                            currentSat.getTimeLead());
                    polarPlotLbl.setAerLead(leadData);
                    futurePasses = GroundPass.find(currentGs, currentSat);
                    // System.out.println("Lead updated");
                }

                // update lag data if needed
                if (updateLagData) {
                    polarPlotLbl.setAerLag(AER.calculate(currentGs.getLla_deg_m(), currentSat.getTemePosLag(),
                            currentSat.getTimeLag()));
                    // System.out.println("Lag updated");
                }

                // Figure out when the next pass is
                if (currentPass != null) // There's a pass, but it ended
                    if (Time.dateTimeOfJd(currentPass.setTime()).isBefore(currentTime)) {
                        currentPass = null;
                        nextPassLbl.setText("");
                    }
                // There's no pass now, but we can predict the next one
                if (futurePasses.size() > 0 && currentPass == null) {
                    DateTime nextRise = Time.dateTimeOfJd(futurePasses.get(0).riseTime());
                    if (nextRise.isAfter(currentTime)) {
                        nextPassLbl.setText(NEXT_PASS_LBL_TXT + nextRise.toString("MMM dd, YYYY -- HH:mm:ss"));
                    } else {// It just started
                        currentPass = futurePasses.remove(0);
                        nextPassLbl
                                .setText("Pass ends at: " + new DateTime(Time.dateTimeOfJd(currentPass.setTime()))
                                        .toString("MMM dd, YYYY -- HH:mm:ss"));
                    }
                }
            } // lead / lag data shown

            // Track a satellite
            if (trackButton.isSelected()) {
                try {
                    trackSatellite((int) Math.round(aer[0]), (int) Math.round(aer[1]), aer[2]);
                } catch (Failure f) { // TODO Display failure
                    String stackTrace = "";
                    for (StackTraceElement ste : f.getStackTrace())
                        stackTrace += ste.toString();
                    JOptionPane.showMessageDialog(this, "Error: " + f.getMessage() + "\n" + stackTrace, "Error",
                            JOptionPane.ERROR_MESSAGE);
                    trackButton.setSelected(false);
                }
            }

            // TODO Adjust for Doppler here
            // put in new range and time
            // adjust TS2000 settings accordingly

        } else { // NAN check
            currInfoLbl.setText(CURRENT_TRACKING_INFORMATION + "Satellite Ephemeris Not Available");
            clearCurrTrackInfo();

            polarPlotLbl.setTimeString(timeAsString);
            polarPlotLbl.setGs2SatNameString(
                    String.format("%s to %s", currentGs.getStationName().trim(), currentSat.getName().trim()));

            polarPlotLbl.clearLeadLagData(); // clear lead/lag data
            polarPlotLbl.resetCurrentPosition(); // clear current point?
        }

        polarPlotLbl.repaint();

    } // updateTime

    /**
     * @param az azimuth
     * @param el elevation
     * @param range
     * @throws Failure
     */
    private void trackSatellite(int az, int el, double range) throws Failure {
        if (currentPass != null) { // have a ground pass?
            if (currentPass.requiresFlip()) { // invert all directions?
                az = (az < 180) ? az + 180 : az - 180;
                el = 180 - el;
            } else if (currentPass.crossesNorth() && az < 180) {
                az += 360; // have to go past 360 to ensure continuous coverage
            }
        } else if (currentSat instanceof SunSat) {
            // Lead/lag data not implemented in SunSat yet, so GroundPass
            // doesn't work either
        } else { // No pass, prepare for the next one?
            el = 0; // Can't point below horizon
            if (futurePasses.size() > 0) { // Get ready
                if (futurePasses.get(0).requiresFlip()) {
                    az -= 180;
                    el = 180 - el;
                }
            }
        }
        double[] azEl = rController.rotate(az, el);
        rotAzLbl.setText(ROT_AZ_LBL_TXT + azEl[0]);
        rotElLbl.setText(ROT_EL_LBL_TXT + azEl[1]);
    }

    /**
     * 
     */
    private void clearCurrTrackInfo() {
        timeLbl.setText(TIME_LBL_TXT);
        azLbl.setText(AZ_LBL_TXT);
        elLbl.setText(EL_LBL_TXT);
        rangeLbl.setText(RANGE_LBL_TXT);
    }

    private void gsComboBoxItemStateChanged(java.awt.event.ItemEvent evt) {//GEN-FIRST:event_gsComboBoxItemStateChanged
        if (evt.getStateChange() == ItemEvent.SELECTED)
            currentGs = app.getGs(gsComboBox.getSelectedItem().toString());
        if (timeAsString != null)
            updateTime(timeAsString);
        objectChangeReset();
    }//GEN-LAST:event_gsComboBoxItemStateChanged

    private void satComboBoxItemStateChanged(java.awt.event.ItemEvent evt) {//GEN-FIRST:event_satComboBoxItemStateChanged
        if (evt.getStateChange() == ItemEvent.SELECTED)
            currentSat = app.getSatellite(satComboBox.getSelectedItem().toString());
        if (timeAsString != null)
            updateTime(timeAsString);
        objectChangeReset();

    }//GEN-LAST:event_satComboBoxItemStateChanged

    private void refreshComboBoxesButtonActionPerformed(java.awt.event.ActionEvent evt)// GEN-FIRST:event_refreshComboBoxesButtonActionPerformed
    {// GEN-HEADEREND:event_refreshComboBoxesButtonActionPerformed
     // save selections (if there are any)
        String oldSat = "null";
        if (satComboBox.getSelectedIndex() >= 0) {
            oldSat = satComboBox.getSelectedItem().toString();
        }
        String oldGS = "null";
        if (gsComboBox.getSelectedIndex() >= 0) {
            oldGS = gsComboBox.getSelectedItem().toString();
        }

        refreshComboBoxes();

        // select old selections if there are any
        if (!oldSat.equalsIgnoreCase("null")) {
            satComboBox.setSelectedItem(oldSat);
        }

        if (!oldGS.equalsIgnoreCase("null")) {
            gsComboBox.setSelectedItem(oldGS);
        }

    }// GEN-LAST:event_refreshComboBoxesButtonActionPerformed
    /*clearCurrTrackInfo();
    if (timeAsString != null)
        updateTime(timeAsString);
        
    objectChangeReset();*/

    private void gsComboBoxActionPerformed(java.awt.event.ActionEvent evt)// GEN-FIRST:event_gsComboBoxActionPerformed
    {// GEN-HEADEREND:event_gsComboBoxActionPerformed
        clearCurrTrackInfo();
        if (timeAsString != null)
            updateTime(timeAsString);

        objectChangeReset();

    }// GEN-LAST:event_gsComboBoxActionPerformed

    private void satComboBoxActionPerformed(java.awt.event.ActionEvent evt)// GEN-FIRST:event_satComboBoxActionPerformed
    {// GEN-HEADEREND:event_satComboBoxActionPerformed
        clearCurrTrackInfo();
        if (timeAsString != null)
            updateTime(timeAsString);

        objectChangeReset();
    }// GEN-LAST:event_satComboBoxActionPerformed

    /**
     * functions for use for scripting purposes, sets Ground Station
     */
    public void setGroundStation(String gsName) {
        //gsComboBox.setSelectedItem(gsName);
        setGroundStation(app.getGs(gsName));
    }

    /**
     * functions for use for scripting purposes, sets Ground Station
     */
    public void setGroundStation(GroundStation gs) {
        currentGs = gs;
        if (timeAsString != null)
            updateTime(timeAsString);
        objectChangeReset();

        //setGroundStation(gs.getStationName());
        //currentGs = gs;
    }

    /**
     * functions for use for scripting purposes, sets Satellite
     */
    public void setSatellite(String satName) {
        //satComboBox.setSelectedItem(satName);
        setSatellite(app.getSatellite(satName));
    }

    /**
     * functions for use for scripting purposes, sets Satellite
     */
    public void setSatellite(AbstractSatellite sat) {
        //setSatellite(sat.getName());
        currentSat = sat;
        if (timeAsString != null)
            updateTime(timeAsString);
        objectChangeReset();
    }

    /**
     * GUI changes needed when objects are changed
     */
    private void objectChangeReset() {

        // clear pass table
        if (passTableModel != null) // ini of object
        {
            while (passTableModel.getRowCount() > 0) {
                passTableModel.removeRow(0);
            }

            // refresh lead/lag in polar plot if needed
            if (gsComboBox.getSelectedIndex() < 0 || satComboBox.getSelectedIndex() < 0) {
                return; // something not selected skip lead/lag update
            }

            if (polarPlotLbl.isShowLeadLagData() && currentSat.isGroundTrackShown()) {
                polarPlotLbl.setAerLead(AER.calculate(
                        new double[] { currentGs.getLatitude(), currentGs.getLongitude(), currentGs.getAltitude() },
                        currentSat.getTemePosLead(), currentSat.getTimeLead()));
                polarPlotLbl.setAerLag(AER.calculate(
                        new double[] { currentGs.getLatitude(), currentGs.getLongitude(), currentGs.getAltitude() },
                        currentSat.getTemePosLag(), currentSat.getTimeLag()));
            }
        }

    }

    private void leadLagCheckBoxActionPerformed(java.awt.event.ActionEvent evt)// GEN-FIRST:event_leadLagCheckBoxActionPerformed
    {// GEN-HEADEREND:event_leadLagCheckBoxActionPerformed
        polarPlotLbl.setShowLeadLagData(leadLagCheckBox.isSelected());
        polarPlotLbl.repaint();
    }// GEN-LAST:event_leadLagCheckBoxActionPerformed

    private void namesChkBxActionPerformed(java.awt.event.ActionEvent evt)// GEN-FIRST:event_jCheckBox2ActionPerformed
    {// GEN-HEADEREND:event_jCheckBox2ActionPerformed
        polarPlotLbl.setDisplayNames(namesChkBx.isSelected());
        polarPlotLbl.repaint();
    }// GEN-LAST:event_jCheckBox2ActionPerformed

    private void runPassPredictionButtonActionPerformed(java.awt.event.ActionEvent evt)// GEN-FIRST:event_runPassPredictionButtonActionPerformed
    {// GEN-HEADEREND:event_runPassPredictionButtonActionPerformed
        runPassPrediction();
    }// GEN-LAST:event_runPassPredictionButtonActionPerformed

    public void runPassPrediction() {
        // get info:
        double timeSpanDays = Double.parseDouble(timeSpanTextField.getText());
        double timeStepSec = Double.parseDouble(timeStepTextField.getText());
        // boolean onlyVisible = visibleOnlyCheckBox.isSelected();

        // get sat and GS
        GroundStation gs = app.getGs(gsComboBox.getSelectedItem().toString());
        AbstractSatellite sat = app.getSatellite(satComboBox.getSelectedItem().toString());

        // start time Jul Date
        double jdStart = new Time(currentTime).getJulianDate();

        // clear hash
        passHash.clear();

        // clear the table
        while (passTableModel.getRowCount() > 0) {
            passTableModel.removeRow(0);
        }

        //quadInterp(timeSpanDays, timeStepSec, gs, sat, jdStart);

        // new sun for internal calculations of Visibility
        Sun internalSun = new Sun(jdStart - AstroConst.JDminusMJD);

        // linear search
        double time0, h0;
        double time1 = jdStart;
        double h1 = AER.calculate(gs.getLla_deg_m(), sat.calculateTemePositionFromUT(time1), time1)[1]
                - gs.getElevationConst();

        int passCount = 0;

        if (h1 > 0) {
            passCount++;
            passTableModel.addRow(new Object[] { passCount, "--", "", "", "" });
        }

        double lastRise = 0;

        for (double jd = jdStart; jd <= jdStart + timeSpanDays; jd += timeStepSec / (60.0 * 60.0 * 24.0)) {
            time0 = time1;
            time1 = jd + timeStepSec / (60.0 * 60.0 * 24.0);

            // calculate elevations at each time step (if needed)
            h0 = h1;
            // calculate the elevation at this newly visited point
            h1 = AER.calculate(gs.getLla_deg_m(), sat.calculateTemePositionFromUT(time1), time1)[1]
                    - gs.getElevationConst();

            // rise
            if (h0 <= 0 && h1 > 0) {
                double riseTime = findSatRiseSetRoot(sat, gs, time0, time1, h0, h1);
                // System.out.println("Rise at " + riseTime + " (" + time0 + ","
                // + time1 + ")");

                lastRise = riseTime; // save

                // add to table
                passCount++;
                // use Time object to convert Julian date to string using
                // program settings (i.e. time zone)
                String crossTimeStr = app.currentJulianDate.convertJD2String(riseTime);

                passTableModel.addRow(new Object[] { passCount, crossTimeStr, "", "", "", "", "" });

                // calculate using the rise time - the Azimuth
                double az = AER.calculate(gs.getLla_deg_m(), sat.calculateTemePositionFromUT(riseTime),
                        riseTime)[0];
                if (azComboBox.getSelectedIndex() == 0) {
                    passTableModel.setValueAt("" + String.format("%.1f", az), passTableModel.getRowCount() - 1, 2);
                } else {
                    // is AZ in degrees or radians?
                    passTableModel.setValueAt(CoordinateConversion.degrees2CompassPoints(az),
                            passTableModel.getRowCount() - 1, 2);
                }
            }

            // set
            if (h1 <= 0 && h0 > 0) {
                double setTime = findSatRiseSetRoot(sat, gs, time0, time1, h0, h1);
                // System.out.println("Set at " + setTime + " (" + time0 + "," +
                // time1 + ")");

                // add to table
                String crossTimeStr = app.currentJulianDate.convertJD2String(setTime);
                passTableModel.setValueAt(crossTimeStr, passTableModel.getRowCount() - 1, 3); // last row, 3rd column (2)

                // calculate using the set time - the Azimuth
                double az = AER.calculate(gs.getLla_deg_m(), sat.calculateTemePositionFromUT(setTime), setTime)[0];
                if (azComboBox.getSelectedIndex() == 0) {
                    passTableModel.setValueAt("" + String.format("%.1f", az), passTableModel.getRowCount() - 1, 4);
                } else {
                    passTableModel.setValueAt(CoordinateConversion.degrees2CompassPoints(az),
                            passTableModel.getRowCount() - 1, 4);
                } // azimuth

                // add duration
                if (lastRise > 0) {
                    DecimalFormat fmt2Dig = new DecimalFormat("00.000");

                    double duration = (setTime - lastRise) * 24.0 * 60.0 * 60.0; // seconds
                    String durStr = fmt2Dig.format(duration);
                    passTableModel.setValueAt(durStr, passTableModel.getRowCount() - 1, 5); // last row, 4rd column (3)
                }

                if (lastRise > 0) {
                    determineVisibility(gs, sat, internalSun, passCount, lastRise, setTime);
                } // visibility (with last rise)
            } // set
        } // linear search

        // if visible only checked remove other items from the list
        if (visibleOnlyCheckBox.isSelected()) {

            int vizColumn = 6; // vis text column
            for (int i = passTableModel.getRowCount() - 1; i >= 0; i--) {

                if (!passTableModel.getValueAt(i, vizColumn).toString().equalsIgnoreCase("Visible")) {
                    passTableModel.removeRow(i);
                }
            }
        } // remove non-visible

        // set first col to be small
        passTable.getColumnModel().getColumn(0).setPreferredWidth(10);
    } // runPassPrediction
    /*
        /**
         * @param timeSpanDays
         * @param timeStepSec
         * @param gs
         * @param sat
         * @param jdStart
         *//*
            private void quadInterp(double timeSpanDays, double timeStepSec,
                GroundStation gs, AbstractSatellite sat, double jdStart) {
            // elevation data used in search
            double h0=0,h1=0,h2=0;
                
            // initially calculate elevations at 0/1 time points
            AER.calculate(gs.getLla_deg_m(), sat.getTemePosLead(), sat.getTimeLead());
            double time0 = jdStart - timeStepSec/(60.0*60.0*24.0);
            h0 = AER.calculate(gs.getLla_deg_m(),
            sat.calculateTEMEPositionFromUT(time0) , time0)[1];
            double time1 = jdStart;
            h1 = AER.calculate(gs.getLla_deg_m(),
            sat.calculateTEMEPositionFromUT(time1) , time1)[1];
            double time2 = jdStart + timeStepSec/(60.0*60.0*24.0);; // declare var
                
            System.out.println(time0 + "," + h0 );
            System.out.println(time1 + "," + h1 );
            // use quadratic fit to search for zeros
            for(double jd = jdStart; jd <= jdStart + timeSpanDays; jd += timeStepSec/(60.0*60.0*24.0)) {
                time0 = time1;
                time1 = time2;
                time2 = jd + timeStepSec/(60.0*60.0*24.0);
                    
                // calculate elevations at each time step (if needed)
                h0 = h1;
                h1 = h2;
                // calculate the elevation at this newly visited point
                h2 = AER.calculate(gs.getLla_deg_m(),
                sat.calculateTEMEPositionFromUT(time2) , time2)[1];
                System.out.println(time2 + "," + h2 );
                    
                // create a quadratic interpolator
                QuadraticInterpolatorSimp qis = new QuadraticInterpolatorSimp(time0,h0, time1, h1, time2, h2);
                    
                 // if(qis.getRootCountInDomain() == 1)
                 // {
                 // System.out.println("Event(1): " + qis.getLowerRoot() );
                 // }else if(qis.getRootCountInDomain() == 2)
                 // {
                 // System.out.println("Event(2a): " + qis.getLowerRoot() );
                 // System.out.println("Event(2b): " + qis.getUpperRoot() );
                 // }
                    
            } // for loop seaching for rise/set events
            }
            */

    private void determineVisibility(GroundStation gs, AbstractSatellite sat, Sun internalSun, int passCount,
            double lastRise, double setTime) {
        // Visiable, Radar Night, Radar Night

        // use the time 1/2 between rise and set for viz
        // calculations
        // DOES NOT CHECK FOR VIS NEAR END POINTS SO COULD MISS SOME
        // PARTIAL PASS VISIBILITY

        double julDateVizCalc = (setTime - lastRise) / 2.0 + lastRise;

        // SAVE to hash - for use later
        passHash.put(Integer.valueOf(passCount), new Double(julDateVizCalc));

        // twilight offset
        // 7 seems good
        // 6 is used by heavens-above.com
        double twilightOffset = 6; // degrees extra required for darkness

        // set the suns time
        internalSun.setCurrentMJD(julDateVizCalc - AstroConst.JDminusMJD);

        // TEME - sun dot site positions to determine if station is
        // in sunlight
        double[] gsECI = GeoFunctions.calculateECIposition(gs.getLla_deg_m(), julDateVizCalc);
        double sunDotSite = dot(internalSun.getCurrentPositionTEME(), gsECI);

        // TEST - find angle between sun -> center of Earth ->
        // Ground Station
        double sinFinalSigmaGS = norm(cross(internalSun.getCurrentPositionTEME(), gsECI))
                / (norm(internalSun.getCurrentPositionTEME()) * norm(gsECI));
        double finalSigmaGS = toDegrees(asin(sinFinalSigmaGS));

        if (sunDotSite > 0 || (90.0 - finalSigmaGS) < twilightOffset) {
            // last row, 5rd column (4)
            passTableModel.setValueAt("Radar Sun", passTableModel.getRowCount() - 1, 6);
        } // sun light
        else {
            // now we know the site is in darkness - need to figure
            // out if the satelite is in light
            // use predict algorithm from Vallado 2nd ed.
            double[] satTEME = sat.calculateTemePositionFromUT(julDateVizCalc);
            double sinFinalSigma = norm(cross(internalSun.getCurrentPositionTEME(), satTEME))
                    / (norm(internalSun.getCurrentPositionTEME()) * norm(satTEME));
            double finalSigma = asin(sinFinalSigma);
            double dist = norm(satTEME) * cos(finalSigma - PI / 2.0);

            // changed to mean 30/March/2009 SEG
            if (dist > AstroConst.R_Earth_mean) {
                // sat is in sunlight!
                // last row, 5th column (4)
                passTableModel.setValueAt("Visible", passTableModel.getRowCount() - 1, 6);
            } else { // Radar Night (both in darkness)
                // last row, 5th column (4)
                passTableModel.setValueAt("Radar Night", passTableModel.getRowCount() - 1, 6);
            }

        } // site in dark
    }

    private void go2passButtonActionPerformed(java.awt.event.ActionEvent evt)// GEN-FIRST:event_go2passButtonActionPerformed
    {// GEN-HEADEREND:event_go2passButtonActionPerformed
     // get selected row in table
        int tableRow = passTable.getSelectedRow();
        go2pass(tableRow);
    }// GEN-LAST:event_go2passButtonActionPerformed

    /**
     * Prints the pass prediction table, using a print dialog
     *
     * @see http://java.sun.com/docs/books/tutorial/uiswing/misc/printtable.html
     */
    private void printTableButtonActionPerformed(java.awt.event.ActionEvent evt)// GEN-FIRST:event_printTableButtonActionPerformed
    {// GEN-HEADEREND:event_printTableButtonActionPerformed
        try {
            MessageFormat header = new MessageFormat(String.format("%s to %s",
                    gsComboBox.getSelectedItem().toString(), satComboBox.getSelectedItem().toString()));

            boolean complete = passTable.print(PrintMode.FIT_WIDTH, header,
                    new MessageFormat("JSatTrak Pass Predictions  - {0} -"), true, null, false, null);
            if (!complete)
                System.err.println("Printing cancelled");
        } catch (PrinterException e) {
            System.err.println("ERROR Printing Pass Table: " + e.toString());
        }
    }// GEN-LAST:event_printTableButtonActionPerformed

    /**
     * Save the pass prediction table to a CSV
     */
    private void saveTableButtonActionPerformed(java.awt.event.ActionEvent evt)// GEN-FIRST:event_saveTableButtonActionPerformed
    {// GEN-HEADEREND:event_saveTableButtonActionPerformed

        // first ask for a file name:
        final JFileChooser fc = new JFileChooser();
        fc.addChoosableFileFilter(new CustomFileFilter("csv", "*.csv"));

        if (fc.showSaveDialog(this) == JFileChooser.APPROVE_OPTION) {
            File file = fc.getSelectedFile();

            // append the extension if necessary
            if (FileUtilities.getExtension(file) == null) {
                file = new File(file.getAbsolutePath() + ".csv");
            }

            // save data to file : fileSaveAs ------------------
            try {
                BufferedWriter buffOut = new BufferedWriter(new FileWriter(file));

                // write the name of each column heading
                for (int i = 0; i < passTableModel.getColumnCount(); i++) {
                    String val = passTableModel.getColumnName(i);
                    if (i != passTableModel.getColumnCount() - 1) {
                        val += ","; // add comma to all but last element
                    }
                    buffOut.write(val);
                }
                buffOut.write("\n"); // add new line before data

                // loop through all the data
                for (int i = 0; i < passTableModel.getRowCount(); i++) {
                    for (int j = 0; j < passTableModel.getColumnCount(); j++) {
                        String val = passTableModel.getValueAt(i, j).toString();

                        if (j != passTableModel.getColumnCount() - 1) {
                            val += ","; // add comma to all but last element
                        }
                        buffOut.write(val);
                    } // for each column
                    buffOut.write("\n");
                } // for each row
                buffOut.close(); // close file
            } catch (IOException e) {
                System.err.println("Unable to open/close file");
            }
        } // if approve
    }// GEN-LAST:event_saveTableButtonActionPerformed

    private void timeChkBxActionPerformed(java.awt.event.ActionEvent evt)// GEN-FIRST:event_jCheckBox1ActionPerformed
    {// GEN-HEADEREND:event_jCheckBox1ActionPerformed
        polarPlotLbl.setDisplayTime(timeChkBx.isSelected());
        polarPlotLbl.repaint();
    }// GEN-LAST:event_jCheckBox1ActionPerformed

    private void invChkBxActionPerformed(java.awt.event.ActionEvent evt)// GEN-FIRST:event_jCheckBox3ActionPerformed
    {// GEN-HEADEREND:event_jCheckBox3ActionPerformed
        polarPlotLbl.setDarkColors(!invChkBx.isSelected());
        // jPolarPlotLabel.repaint();
    }// GEN-LAST:event_jCheckBox3ActionPerformed

    private void horizLimChkBxActionPerformed(java.awt.event.ActionEvent evt)// GEN-FIRST:event_jCheckBox4ActionPerformed
    {// GEN-HEADEREND:event_jCheckBox4ActionPerformed
        polarPlotLbl.setLimit2Horizon(horizLimChkBx.isSelected());
    }// GEN-LAST:event_jCheckBox4ActionPerformed

    private void printPolarPlotBtnActionPerformed(java.awt.event.ActionEvent evt)// GEN-FIRST:event_jButton1ActionPerformed
    {// GEN-HEADEREND:event_jButton1ActionPerformed
        polarPlotLbl.print();
    }// GEN-LAST:event_jButton1ActionPerformed

    private void compassChkBxActionPerformed(java.awt.event.ActionEvent evt)// GEN-FIRST:event_jCheckBox5ActionPerformed
    {// GEN-HEADEREND:event_jCheckBox5ActionPerformed
        polarPlotLbl.setUseCompassPoints(compassChkBx.isSelected());
    }// GEN-LAST:event_jCheckBox5ActionPerformed

    /**
     * Open a window to set rotator setitngs Obtain the rotator, and use the
     * RotatorFactory to get the result
     */
    private void rotatorSetBtnActionPerformed(java.awt.event.ActionEvent evt) {// GEN-FIRST:event_rotatorSetBtnActionPerformed
        loadRotator(null);
    }// GEN-LAST:event_rotatorSetBtnActionPerformed

    /**
     * 
     * @param rc
     */
    private void loadRotator(RotatorController rc) {
        if (rc == null) {
            RotatorSettingsPanel rsPanel = new RotatorSettingsPanel(this); // non-modal
            parent.addInternalFrame("Rotator Settings", rsPanel, null, true);
        } else
            rController = rc;

    }

    public javax.swing.JPanel loadRotatorSettingsPanel(RotatorController rc, boolean visibility) {
        RotatorSettingsPanel rsPanel = null;
        if (rc == null) {
            rsPanel = new RotatorSettingsPanel(this);
            parent.addInternalFrame("Rotator Settings", rsPanel, null, visibility);
        } else
            rController = rc;
        return (javax.swing.JPanel) rsPanel;
    }

    /**
     * Jumps time to the mid point of a pass
     *
     * @param passNumber
     *            pass number from last solution, range: 0 to N-1
     */
    public void go2pass(int passNumber) {

        if (passNumber >= 0) {
            // get # in that row
            int passNum = Integer.valueOf(passTableModel.getValueAt(passNumber, 0).toString());

            if (passHash.containsKey(passNum)) {
                double jdMidPoint = passHash.get(passNum);

                // get current app time
                double daysDiff = abs(new Time(currentTime).getJulianDate() - jdMidPoint);

                // check to see if lead/lag data needs updating
                app.checkTimeDiffResetGroundTracks(daysDiff);

                // set app to new time
                app.setTime(Time.convertJD2Calendar(jdMidPoint).getTimeInMillis());
            }
        }
    } // go2pass

    /**
     * bisection method, crossing time should be bracketed by time0 and time1
     *
     * @param sat
     * @param gs
     * @param time0
     * @param time1
     * @param f0
     * @param f1
     * @return
     */
    private double findSatRiseSetRoot(AbstractSatellite sat, GroundStation gs, double time0, double time1,
            double f0, double f1) {
        double tol = (1.157407E-5) / 4; // 1/4 a sec (in units of a day)

        int iterCount = 0;

        while (abs(time1 - time0) > 2 * tol) {
            // Calculate midpoint of domain
            double timeMid = (time1 + time0) / 2.0;
            double fmid = AER.calculate(gs.getLla_deg_m(), sat.calculateTemePositionFromUT(timeMid), timeMid)[1]
                    - gs.getElevationConst();

            if (f0 * fmid > 0) // same sign
            {
                // replace f0 with fmid
                f0 = fmid;
                time0 = timeMid;

            } else // else replace f1 with fmid
            {
                f1 = fmid;
                time1 = timeMid;
            }

            iterCount++;
        } // while not in tolerance

        // return best gues using linear interpolation between last two points
        double a = (f1 - f0) / (time1 - time0);
        double b = f1 - a * time1;
        double riseTime = -b / a;

        // System.out.println("Bisection Iters: " + iterCount);

        return riseTime; // return best guess -typically: (time0 + time1)/2.0;
        // return (time0 + time1)/2.0;

    } // findSatRiseSetRoot

    public void setTimeSpanDays(double days) {
        timeSpanTextField.setText("" + days);
    }

    public void setTimeStepSec(double sec) {
        timeStepTextField.setText("" + sec);
    }

    public void setVisibleOnlyFilter(boolean visibleOnly) {
        visibleOnlyCheckBox.setSelected(visibleOnly);
    }

    public Hashtable<Integer, Double> getPassHash() {
        return passHash;
    }

    public String[][] getPredictionTableData() {
        String[][] data = new String[passTableModel.getRowCount()][passTableModel.getColumnCount()];

        for (int r = 0; r < passTableModel.getRowCount(); r++) {
            for (int c = 0; c < passTableModel.getColumnCount(); c++) {
                data[r][c] = passTableModel.getValueAt(r, c).toString();
            }
        }

        return data;
    } // getPredictionTableData

    public String[] getTrackingInformation() {
        String[] ar = { azLbl.getText(), elLbl.getText(), rangeLbl.getText(), rotAzLbl.getText(),
                rotElLbl.getText() };
        return ar;
    }

    /**
     * can be used in a script to have the lable rendered off screen or to set
     * its display settings
     *
     * @return
     */
    public JPolarPlotLabel getPolarPlotLabel() {
        return polarPlotLbl;
    }

    @Override
    public void gsAdded(GroundStation gs) {
        gsComboBox.addItem(gs.getStationName());
    }

    @Override
    public void gsRemoved(GroundStation gs) {
        gsComboBox.removeItem(gs);
    }

    @Override
    public void satAdded(AbstractSatellite sat) {
        satComboBox.addItem(sat);
    }

    @Override
    public void satRemoved(AbstractSatellite sat) {
        satComboBox.removeItem(sat);
    }

    // Variables declaration - do not modify//GEN-BEGIN:variables
    private javax.swing.JComboBox azComboBox;
    private javax.swing.JLabel azLbl;
    private javax.swing.JPanel basicPanel;
    private javax.swing.JCheckBox compassChkBx;
    private javax.swing.JLabel currInfoLbl;
    private javax.swing.JLabel elLbl;
    private javax.swing.JButton go2passButton;
    private javax.swing.JComboBox<String> gsComboBox;
    private javax.swing.JLabel gsLbl;
    private javax.swing.JCheckBox horizLimChkBx;
    private javax.swing.JCheckBox invChkBx;
    private javax.swing.JTabbedPane jTabbedPane1;
    private javax.swing.JCheckBox leadLagCheckBox;
    private javax.swing.JCheckBox namesChkBx;
    private javax.swing.JLabel nextPassLbl;
    private javax.swing.JLabel passPredictLbl;
    private javax.swing.JPanel passPredictPanel;
    private javax.swing.JScrollPane passScrollPane;
    private javax.swing.JTable passTable;
    private jsattrak.gui.JPolarPlotLabel polarPlotLbl;
    private javax.swing.JPanel polarPlotPanel;
    private javax.swing.JToolBar polarPlotToolBar;
    private javax.swing.JButton printPolarPlotBtn;
    private javax.swing.JButton printTableButton;
    private javax.swing.JLabel rangeLbl;
    private javax.swing.JButton refreshComboBoxesButton;
    private javax.swing.JLabel riseSetAzLbl;
    private javax.swing.JLabel rotAzLbl;
    private javax.swing.JLabel rotElLbl;
    private javax.swing.JButton rotatorSetBtn;
    private javax.swing.JButton runPassPredictionButton;
    private javax.swing.JComboBox<Serializable> satComboBox;
    private javax.swing.JLabel satLbl;
    private javax.swing.JButton saveTableButton;
    private javax.swing.JCheckBox timeChkBx;
    private javax.swing.JLabel timeLbl;
    private javax.swing.JLabel timeSpanLbl;
    private javax.swing.JTextField timeSpanTextField;
    private javax.swing.JLabel timeStepLbl;
    private javax.swing.JTextField timeStepTextField;
    private javax.swing.JToggleButton trackButton;
    private javax.swing.JCheckBox visibleOnlyCheckBox;
    // End of variables declaration//GEN-END:variables

    /**
     * This method is called from within the constructor to initialize the form.
     * WARNING: Do NOT modify this code. The content of this method is always
     * regenerated by the Form Editor.
     */
    @SuppressWarnings({ "unchecked", "serial" })
    // <editor-fold defaultstate="collapsed"
    // <editor-fold defaultstate="collapsed" desc="Generated Code">//GEN-BEGIN:initComponents
    private void initComponents() {

        jTabbedPane1 = new javax.swing.JTabbedPane();
        polarPlotPanel = new javax.swing.JPanel();
        polarPlotLbl = new jsattrak.gui.JPolarPlotLabel();
        polarPlotToolBar = new javax.swing.JToolBar();
        printPolarPlotBtn = new javax.swing.JButton();
        leadLagCheckBox = new javax.swing.JCheckBox();
        timeChkBx = new javax.swing.JCheckBox();
        namesChkBx = new javax.swing.JCheckBox();
        invChkBx = new javax.swing.JCheckBox();
        horizLimChkBx = new javax.swing.JCheckBox();
        compassChkBx = new javax.swing.JCheckBox();
        passPredictPanel = new javax.swing.JPanel();
        passPredictLbl = new javax.swing.JLabel();
        passScrollPane = new javax.swing.JScrollPane();
        passTable = new javax.swing.JTable();
        timeSpanLbl = new javax.swing.JLabel();
        timeSpanTextField = new javax.swing.JTextField();
        timeStepLbl = new javax.swing.JLabel();
        timeStepTextField = new javax.swing.JTextField();
        runPassPredictionButton = new javax.swing.JButton();
        visibleOnlyCheckBox = new javax.swing.JCheckBox();
        go2passButton = new javax.swing.JButton();
        printTableButton = new javax.swing.JButton();
        saveTableButton = new javax.swing.JButton();
        riseSetAzLbl = new javax.swing.JLabel();
        azComboBox = new javax.swing.JComboBox();
        basicPanel = new javax.swing.JPanel();
        gsLbl = new javax.swing.JLabel();
        gsComboBox = new javax.swing.JComboBox<String>();
        satLbl = new javax.swing.JLabel();
        satComboBox = new javax.swing.JComboBox<Serializable>();
        currInfoLbl = new javax.swing.JLabel();
        refreshComboBoxesButton = new javax.swing.JButton();
        trackButton = new javax.swing.JToggleButton();
        rotatorSetBtn = new javax.swing.JButton();
        timeLbl = new javax.swing.JLabel();
        azLbl = new javax.swing.JLabel();
        rotAzLbl = new javax.swing.JLabel();
        elLbl = new javax.swing.JLabel();
        rotElLbl = new javax.swing.JLabel();
        rangeLbl = new javax.swing.JLabel();
        nextPassLbl = new javax.swing.JLabel();

        jTabbedPane1.setMinimumSize(new java.awt.Dimension(53, 53));

        polarPlotPanel.setLayout(new java.awt.BorderLayout());

        polarPlotLbl.setBackground(new java.awt.Color(0, 0, 0));
        polarPlotPanel.add(polarPlotLbl, java.awt.BorderLayout.CENTER);

        polarPlotToolBar.setRollover(true);

        printPolarPlotBtn
                .setIcon(new javax.swing.ImageIcon(getClass().getResource("/icons/gnome_2_18/document-print.png"))); // NOI18N
        printPolarPlotBtn
                .setToolTipText("Print Polar Plot - automatically prints with inverted colors, names, and time");
        printPolarPlotBtn.setFocusable(false);
        printPolarPlotBtn.setHorizontalTextPosition(javax.swing.SwingConstants.CENTER);
        printPolarPlotBtn.setVerticalTextPosition(javax.swing.SwingConstants.BOTTOM);
        printPolarPlotBtn.addActionListener(new java.awt.event.ActionListener() {
            public void actionPerformed(java.awt.event.ActionEvent evt) {
                printPolarPlotBtnActionPerformed(evt);
            }
        });
        polarPlotToolBar.add(printPolarPlotBtn);

        leadLagCheckBox.setSelected(true);
        leadLagCheckBox.setText("Lead/Lag Data"); // NOI18N
        leadLagCheckBox.addActionListener(new java.awt.event.ActionListener() {
            public void actionPerformed(java.awt.event.ActionEvent evt) {
                leadLagCheckBoxActionPerformed(evt);
            }
        });
        polarPlotToolBar.add(leadLagCheckBox);

        timeChkBx.setSelected(true);
        timeChkBx.setText("Time"); // NOI18N
        timeChkBx.addActionListener(new java.awt.event.ActionListener() {
            public void actionPerformed(java.awt.event.ActionEvent evt) {
                timeChkBxActionPerformed(evt);
            }
        });
        polarPlotToolBar.add(timeChkBx);

        namesChkBx.setText("Names"); // NOI18N
        namesChkBx.addActionListener(new java.awt.event.ActionListener() {
            public void actionPerformed(java.awt.event.ActionEvent evt) {
                namesChkBxActionPerformed(evt);
            }
        });
        polarPlotToolBar.add(namesChkBx);

        invChkBx.setText("Invert");
        invChkBx.setToolTipText("Invert line and background colors");
        invChkBx.setFocusable(false);
        invChkBx.setVerticalTextPosition(javax.swing.SwingConstants.BOTTOM);
        invChkBx.addActionListener(new java.awt.event.ActionListener() {
            public void actionPerformed(java.awt.event.ActionEvent evt) {
                invChkBxActionPerformed(evt);
            }
        });
        polarPlotToolBar.add(invChkBx);

        horizLimChkBx.setText("Limit to Horizon");
        horizLimChkBx.setFocusable(false);
        horizLimChkBx.setVerticalTextPosition(javax.swing.SwingConstants.BOTTOM);
        horizLimChkBx.addActionListener(new java.awt.event.ActionListener() {
            public void actionPerformed(java.awt.event.ActionEvent evt) {
                horizLimChkBxActionPerformed(evt);
            }
        });
        polarPlotToolBar.add(horizLimChkBx);

        compassChkBx.setSelected(true);
        compassChkBx.setText("Compass");
        compassChkBx.setFocusable(false);
        compassChkBx.setVerticalTextPosition(javax.swing.SwingConstants.BOTTOM);
        compassChkBx.addActionListener(new java.awt.event.ActionListener() {
            public void actionPerformed(java.awt.event.ActionEvent evt) {
                compassChkBxActionPerformed(evt);
            }
        });
        polarPlotToolBar.add(compassChkBx);

        polarPlotPanel.add(polarPlotToolBar, java.awt.BorderLayout.SOUTH);

        jTabbedPane1.addTab("Polar Plot", polarPlotPanel);

        passPredictLbl.setFont(new java.awt.Font("Tahoma", 1, 12));
        passPredictLbl.setText("Pass Predictions:"); // NOI18N

        passTable.setModel(new javax.swing.table.DefaultTableModel(new Object[][] { { null, null, null },
                { null, null, null }, { null, null, null }, { null, null, null } },
                new String[] { "#", "Rise Time", "Set Time" }) {
            Class[] types = new Class[] { java.lang.String.class, java.lang.String.class, java.lang.String.class };
            boolean[] canEdit = new boolean[] { false, true, true };

            public Class<?> getColumnClass(int columnIndex) {
                return types[columnIndex];
            }

            public boolean isCellEditable(int rowIndex, int columnIndex) {
                return canEdit[columnIndex];
            }
        });
        passScrollPane.setViewportView(passTable);
        passTable.getColumnModel().getColumn(0).setHeaderValue("#");
        passTable.getColumnModel().getColumn(1).setHeaderValue("Rise Time");
        passTable.getColumnModel().getColumn(2).setHeaderValue("Set Time");

        timeSpanLbl.setText("Time Span [Days]:"); // NOI18N

        timeSpanTextField.setText("10"); // NOI18N

        timeStepLbl.setText("Time Step [sec]:"); // NOI18N

        timeStepTextField.setText("60.0"); // NOI18N

        runPassPredictionButton.setText("Calculate"); // NOI18N
        runPassPredictionButton.setToolTipText("Calculate Pass Predictions");
        runPassPredictionButton.addActionListener(new java.awt.event.ActionListener() {
            public void actionPerformed(java.awt.event.ActionEvent evt) {
                runPassPredictionButtonActionPerformed(evt);
            }
        });

        visibleOnlyCheckBox.setText("Visible Only"); // NOI18N

        go2passButton.setText("Go to Pass");
        go2passButton
                .setToolTipText("Set the time to the middle of the pass so it can be analyzed (e.g. polar plot)");
        go2passButton.addActionListener(new java.awt.event.ActionListener() {
            public void actionPerformed(java.awt.event.ActionEvent evt) {
                go2passButtonActionPerformed(evt);
            }
        });

        printTableButton
                .setIcon(new javax.swing.ImageIcon(getClass().getResource("/icons/gnome_2_18/document-print.png"))); // NOI18N
        printTableButton.setToolTipText("Print Table");
        printTableButton.addActionListener(new java.awt.event.ActionListener() {
            public void actionPerformed(java.awt.event.ActionEvent evt) {
                printTableButtonActionPerformed(evt);
            }
        });

        saveTableButton.setIcon(
                new javax.swing.ImageIcon(getClass().getResource("/icons/gnome_2_18/document-save-as.png"))); // NOI18N
        saveTableButton.setToolTipText("Save Table to File");
        saveTableButton.addActionListener(new java.awt.event.ActionListener() {
            public void actionPerformed(java.awt.event.ActionEvent evt) {
                saveTableButtonActionPerformed(evt);
            }
        });

        riseSetAzLbl.setText("Rise/Set Azimuth:");

        azComboBox.setModel(new javax.swing.DefaultComboBoxModel(new String[] { "Degrees", "Compass Points" }));
        azComboBox.setSelectedIndex(1);

        javax.swing.GroupLayout passPredictPanelLayout = new javax.swing.GroupLayout(passPredictPanel);
        passPredictPanel.setLayout(passPredictPanelLayout);
        passPredictPanelLayout.setHorizontalGroup(passPredictPanelLayout
                .createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
                .addGroup(passPredictPanelLayout.createSequentialGroup().addContainerGap()
                        .addGroup(passPredictPanelLayout
                                .createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
                                .addGroup(passPredictPanelLayout.createSequentialGroup()
                                        .addComponent(passScrollPane, javax.swing.GroupLayout.DEFAULT_SIZE, 465,
                                                Short.MAX_VALUE)
                                        .addContainerGap())
                                .addComponent(passPredictLbl)
                                .addGroup(passPredictPanelLayout.createSequentialGroup().addGap(10, 10, 10)
                                        .addComponent(timeSpanLbl)
                                        .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
                                        .addComponent(timeSpanTextField, javax.swing.GroupLayout.PREFERRED_SIZE, 52,
                                                javax.swing.GroupLayout.PREFERRED_SIZE)
                                        .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.UNRELATED)
                                        .addComponent(timeStepLbl)
                                        .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
                                        .addComponent(timeStepTextField, javax.swing.GroupLayout.PREFERRED_SIZE, 54,
                                                javax.swing.GroupLayout.PREFERRED_SIZE)
                                        .addGap(54, 54, 54))
                                .addGroup(passPredictPanelLayout.createSequentialGroup()
                                        .addComponent(visibleOnlyCheckBox)
                                        .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.UNRELATED)
                                        .addComponent(riseSetAzLbl)
                                        .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
                                        .addComponent(azComboBox, javax.swing.GroupLayout.PREFERRED_SIZE,
                                                javax.swing.GroupLayout.DEFAULT_SIZE,
                                                javax.swing.GroupLayout.PREFERRED_SIZE)
                                        .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED, 111,
                                                Short.MAX_VALUE)
                                        .addComponent(runPassPredictionButton).addContainerGap())
                                .addGroup(passPredictPanelLayout.createSequentialGroup().addComponent(go2passButton)
                                        .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED, 272,
                                                Short.MAX_VALUE)
                                        .addComponent(saveTableButton)
                                        .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
                                        .addComponent(printTableButton).addContainerGap()))));
        passPredictPanelLayout.setVerticalGroup(passPredictPanelLayout
                .createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
                .addGroup(passPredictPanelLayout.createSequentialGroup()
                        .addComponent(passPredictLbl, javax.swing.GroupLayout.PREFERRED_SIZE, 17,
                                javax.swing.GroupLayout.PREFERRED_SIZE)
                        .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
                        .addGroup(passPredictPanelLayout
                                .createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE)
                                .addComponent(timeSpanLbl)
                                .addComponent(timeSpanTextField, javax.swing.GroupLayout.PREFERRED_SIZE,
                                        javax.swing.GroupLayout.DEFAULT_SIZE,
                                        javax.swing.GroupLayout.PREFERRED_SIZE)
                                .addComponent(timeStepLbl).addComponent(timeStepTextField,
                                        javax.swing.GroupLayout.PREFERRED_SIZE,
                                        javax.swing.GroupLayout.DEFAULT_SIZE,
                                        javax.swing.GroupLayout.PREFERRED_SIZE))
                        .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
                        .addGroup(passPredictPanelLayout
                                .createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE)
                                .addComponent(visibleOnlyCheckBox).addComponent(runPassPredictionButton)
                                .addComponent(riseSetAzLbl).addComponent(azComboBox,
                                        javax.swing.GroupLayout.PREFERRED_SIZE,
                                        javax.swing.GroupLayout.DEFAULT_SIZE,
                                        javax.swing.GroupLayout.PREFERRED_SIZE))
                        .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.UNRELATED)
                        .addComponent(passScrollPane, javax.swing.GroupLayout.DEFAULT_SIZE, 182, Short.MAX_VALUE)
                        .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
                        .addGroup(passPredictPanelLayout
                                .createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE)
                                .addComponent(go2passButton).addComponent(printTableButton)
                                .addComponent(saveTableButton))));

        jTabbedPane1.addTab("Pass Predictions", passPredictPanel);

        gsLbl.setText("Ground Station:"); // NOI18N

        gsComboBox.setModel(new javax.swing.DefaultComboBoxModel<String>(
                new String[] { "Item 1", "Item 2", "Item 3", "Item 4" }));
        gsComboBox.addItemListener(new java.awt.event.ItemListener() {
            public void itemStateChanged(java.awt.event.ItemEvent evt) {
                gsComboBoxItemStateChanged(evt);
            }
        });
        gsComboBox.addActionListener(new java.awt.event.ActionListener() {
            public void actionPerformed(java.awt.event.ActionEvent evt) {
                gsComboBoxActionPerformed(evt);
            }
        });

        satLbl.setText("Satellite:"); // NOI18N

        satComboBox.setModel(new javax.swing.DefaultComboBoxModel<Serializable>(
                new String[] { "Item 1", "Item 2", "Item 3", "Item 4" }));
        satComboBox.addItemListener(new java.awt.event.ItemListener() {
            public void itemStateChanged(java.awt.event.ItemEvent evt) {
                satComboBoxItemStateChanged(evt);
            }
        });
        satComboBox.addActionListener(new java.awt.event.ActionListener() {
            public void actionPerformed(java.awt.event.ActionEvent evt) {
                satComboBoxActionPerformed(evt);
            }
        });

        currInfoLbl.setText("Current Tracking Information:"); // NOI18N

        refreshComboBoxesButton
                .setIcon(new javax.swing.ImageIcon(getClass().getResource("/icons/gnome_2_18/view-refresh.png"))); // NOI18N
        refreshComboBoxesButton.setToolTipText("refresh sat and ground stations"); // NOI18N
        refreshComboBoxesButton.addActionListener(new java.awt.event.ActionListener() {
            public void actionPerformed(java.awt.event.ActionEvent evt) {
                refreshComboBoxesButtonActionPerformed(evt);
            }
        });

        trackButton.setText("Track!");
        trackButton.addActionListener(new java.awt.event.ActionListener() {
            public void actionPerformed(java.awt.event.ActionEvent evt) {
                trackButtonActionPerformed(evt);
            }
        });

        rotatorSetBtn.setText("Rotator Settings");
        rotatorSetBtn.addActionListener(new java.awt.event.ActionListener() {
            public void actionPerformed(java.awt.event.ActionEvent evt) {
                rotatorSetBtnActionPerformed(evt);
            }
        });

        timeLbl.setText("Current Time: "); // NOI18N

        azLbl.setText("Azimuth (Satellite) [deg]: "); // NOI18N

        rotAzLbl.setText("Azimuth (Rotator) [deg]: "); // NOI18N

        elLbl.setText("Elevation (Satellite) [deg]: "); // NOI18N

        rotElLbl.setText("Elevation (Rotator) [deg]: "); // NOI18N

        rangeLbl.setText("Range [m]: "); // NOI18N

        nextPassLbl.setText("Next Pass at: ");

        javax.swing.GroupLayout basicPanelLayout = new javax.swing.GroupLayout(basicPanel);
        basicPanel.setLayout(basicPanelLayout);
        basicPanelLayout.setHorizontalGroup(basicPanelLayout
                .createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
                .addGroup(basicPanelLayout.createSequentialGroup().addContainerGap().addGroup(basicPanelLayout
                        .createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
                        .addGroup(basicPanelLayout.createSequentialGroup().addGroup(basicPanelLayout
                                .createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
                                .addComponent(currInfoLbl)
                                .addGroup(basicPanelLayout.createSequentialGroup().addGroup(basicPanelLayout
                                        .createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
                                        .addGroup(basicPanelLayout.createSequentialGroup().addGroup(basicPanelLayout
                                                .createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
                                                .addComponent(gsLbl).addComponent(satLbl))
                                                .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
                                                .addGroup(basicPanelLayout
                                                        .createParallelGroup(
                                                                javax.swing.GroupLayout.Alignment.LEADING, false)
                                                        .addComponent(satComboBox, 0,
                                                                javax.swing.GroupLayout.DEFAULT_SIZE,
                                                                Short.MAX_VALUE)
                                                        .addComponent(gsComboBox, 0, 169, Short.MAX_VALUE)))
                                        .addComponent(azLbl).addComponent(elLbl)).addGap(8, 8, 8)
                                        .addGroup(basicPanelLayout
                                                .createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
                                                .addGroup(basicPanelLayout.createSequentialGroup()
                                                        .addComponent(refreshComboBoxesButton,
                                                                javax.swing.GroupLayout.PREFERRED_SIZE, 26,
                                                                javax.swing.GroupLayout.PREFERRED_SIZE)
                                                        .addPreferredGap(
                                                                javax.swing.LayoutStyle.ComponentPlacement.RELATED,
                                                                javax.swing.GroupLayout.DEFAULT_SIZE,
                                                                Short.MAX_VALUE)
                                                        .addComponent(rotatorSetBtn)
                                                        .addPreferredGap(
                                                                javax.swing.LayoutStyle.ComponentPlacement.RELATED)
                                                        .addComponent(trackButton).addGap(12, 12, 12))
                                                .addGroup(basicPanelLayout
                                                        .createParallelGroup(
                                                                javax.swing.GroupLayout.Alignment.TRAILING)
                                                        .addComponent(rotElLbl).addComponent(rotAzLbl)))))
                                .addGap(0, 0, 0))
                        .addComponent(timeLbl)
                        .addGroup(basicPanelLayout.createSequentialGroup().addComponent(rangeLbl)
                                .addContainerGap(418, Short.MAX_VALUE))
                        .addGroup(basicPanelLayout.createSequentialGroup().addComponent(nextPassLbl)
                                .addContainerGap(407, Short.MAX_VALUE)))));
        basicPanelLayout.setVerticalGroup(basicPanelLayout
                .createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
                .addGroup(basicPanelLayout.createSequentialGroup().addGroup(basicPanelLayout
                        .createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
                        .addGroup(basicPanelLayout.createSequentialGroup().addGroup(basicPanelLayout
                                .createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
                                .addGroup(basicPanelLayout.createSequentialGroup().addContainerGap()
                                        .addGroup(basicPanelLayout
                                                .createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE)
                                                .addComponent(gsLbl).addComponent(gsComboBox,
                                                        javax.swing.GroupLayout.PREFERRED_SIZE,
                                                        javax.swing.GroupLayout.DEFAULT_SIZE,
                                                        javax.swing.GroupLayout.PREFERRED_SIZE))
                                        .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
                                        .addGroup(basicPanelLayout
                                                .createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE)
                                                .addComponent(satLbl).addComponent(satComboBox,
                                                        javax.swing.GroupLayout.PREFERRED_SIZE,
                                                        javax.swing.GroupLayout.DEFAULT_SIZE,
                                                        javax.swing.GroupLayout.PREFERRED_SIZE))
                                        .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
                                        .addComponent(currInfoLbl))
                                .addGroup(basicPanelLayout.createSequentialGroup().addGap(24, 24, 24)
                                        .addGroup(basicPanelLayout
                                                .createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
                                                .addComponent(refreshComboBoxesButton)
                                                .addGroup(basicPanelLayout
                                                        .createParallelGroup(
                                                                javax.swing.GroupLayout.Alignment.BASELINE)
                                                        .addComponent(rotatorSetBtn).addComponent(trackButton,
                                                                javax.swing.GroupLayout.DEFAULT_SIZE,
                                                                javax.swing.GroupLayout.DEFAULT_SIZE,
                                                                Short.MAX_VALUE)))))
                                .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
                                .addComponent(timeLbl).addGap(26, 26, 26))
                        .addGroup(basicPanelLayout.createSequentialGroup().addGap(107, 107, 107)
                                .addGroup(basicPanelLayout
                                        .createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE)
                                        .addComponent(azLbl).addComponent(rotAzLbl))))
                        .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
                        .addGroup(basicPanelLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE)
                                .addComponent(rotElLbl).addComponent(elLbl))
                        .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.UNRELATED)
                        .addComponent(rangeLbl)
                        .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.UNRELATED)
                        .addComponent(nextPassLbl).addGap(99, 99, 99)));

        jTabbedPane1.addTab("Basic", basicPanel);

        javax.swing.GroupLayout layout = new javax.swing.GroupLayout(this);
        this.setLayout(layout);
        layout.setHorizontalGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
                .addComponent(jTabbedPane1, javax.swing.GroupLayout.DEFAULT_SIZE, 490, Short.MAX_VALUE));
        layout.setVerticalGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
                .addComponent(jTabbedPane1, javax.swing.GroupLayout.DEFAULT_SIZE, 321, Short.MAX_VALUE));
    }// </editor-fold>//GEN-END:initComponents

    private void trackButtonActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_trackButtonActionPerformed
        clearCurrTrackInfo();
        if (timeAsString != null)
            updateTime(timeAsString);
        objectChangeReset();
        /* if (gsComboBox.getSelectedItem() != null)
        currentGs = app.getGs(gsComboBox.getSelectedItem().toString());
        if (satComboBox.getSelectedItem() != null)
        currentSat = app.getSatellite(satComboBox.getSelectedItem().toString());
        if (timeAsString != null)
        updateTime(timeAsString);
        objectChangeReset();
        */
    }//GEN-LAST:event_trackButtonActionPerformed

    public void clickTrackButton() {
        trackButton.doClick();
    }
}