001    /*
002     * $Id: SatelliteTleSGP4.java,v 1.4 2012/02/19 17:35:39 davep Exp $
003     *
004     * This file is part of McIDAS-V
005     *
006     * Copyright 2007-2012
007     * Space Science and Engineering Center (SSEC)
008     * University of Wisconsin - Madison
009     * 1225 W. Dayton Street, Madison, WI 53706, USA
010     * https://www.ssec.wisc.edu/mcidas
011     * 
012     * All Rights Reserved
013     * 
014     * McIDAS-V is built on Unidata's IDV and SSEC's VisAD libraries, and
015     * some McIDAS-V source code is based on IDV and VisAD source code.  
016     * 
017     * McIDAS-V is free software; you can redistribute it and/or modify
018     * it under the terms of the GNU Lesser Public License as published by
019     * the Free Software Foundation; either version 3 of the License, or
020     * (at your option) any later version.
021     * 
022     * McIDAS-V is distributed in the hope that it will be useful,
023     * but WITHOUT ANY WARRANTY; without even the implied warranty of
024     * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
025     * GNU Lesser Public License for more details.
026     * 
027     * You should have received a copy of the GNU Lesser Public License
028     * along with this program.  If not, see http://www.gnu.org/licenses.
029     */
030    
031    package edu.wisc.ssec.mcidasv.data.adde.sgp4;
032    /*
033    import gov.nasa.worldwind.geom.Angle;
034    import gov.nasa.worldwind.geom.Position;
035    import java.awt.Color;
036    import java.util.Random;
037    import javax.swing.JOptionPane;
038    import name.gano.astro.AstroConst;
039    import name.gano.astro.GeoFunctions;
040    import name.gano.astro.Kepler;
041    import jsattrak.utilities.TLE;
042    import name.gano.astro.coordinates.J2kCoordinateConversion;
043    import name.gano.astro.propogators.sgp4_cssi.SGP4SatData;
044    import name.gano.astro.propogators.sgp4_cssi.SGP4unit;
045    import name.gano.astro.propogators.sgp4_cssi.SGP4utils;
046    import name.gano.worldwind.modelloader.WWModel3D_new;
047    import net.java.joglutils.model.ModelFactory;
048    */
049    /**
050     * 
051     *
052     * @author ganos
053     */
054    public class SatelliteTleSGP4 extends AbstractSatellite
055    {
056        private TLE tle;
057        private SGP4SatData sgp4SatData; // sgp4 propogator data
058        
059        // current time - julian date
060        double currentJulianDate = -1;
061        
062        // TLE epoch -- used to calculate how old is TLE - Julian Date
063        double tleEpochJD = -1; // no age
064        
065        // J2000 position and velocity vectors
066        private double[] j2kPos = new double[3]; // meters
067        private double[] j2kVel = new double[3]; // meters/sec
068        // true-equator, mean equinox TEME of date
069        private double[] posTEME = new double[3];  // true-equator, mean equinox TEME of date position for LLA calcs, meters
070        private double[] velTEME = new double[3]; // meters/sec
071        
072        // lat,long,alt  [radians, radians, m ]
073        private double[] lla = new double[3];
074        
075        // plot options 
076        private boolean plot2d = true;
077    //    private Color satColor = Color.RED; // randomize in future
078        private boolean plot2DFootPrint = true;
079        private boolean fillFootPrint = true;
080        private int numPtsFootPrint = 41; // number of points in footprint, used to be 101
081        
082        // ground track options  -- grounds tracks draw to asending nodes, re-calculated at acending nodes
083        boolean showGroundTrack = true;
084        private int grnTrkPointsPerPeriod = 81; // equally space in time >=2 // used to be 121
085        private double groundTrackLeadPeriodMultiplier = 2.0;  // how far forward to draw ground track - in terms of periods
086        private double groundTrackLagPeriodMultiplier = 1.0;  // how far behind to draw ground track - in terms of periods
087        double[][] latLongLead; // leading lat/long coordinates for ground track
088        double[][] latLongLag; // laging lat/long coordinates for ground track
089        private double[][] temePosLead; // leading TEME position coordinates for ground track
090        private double[][] temePosLag; // laging TEME position coordinates for ground track
091        private double[]   timeLead; // array for holding times associated with lead coordinates (Jul Date)
092        private double[]   timeLag; // array - times associated with lag coordinates (Jul Date)
093        boolean groundTrackIni = false; // if ground track has been initialized    
094        
095        private boolean showName2D = true; // show name in 2D plots
096        
097        // 3D Options
098        private boolean show3DOrbitTrace = true;
099        private boolean show3DFootprint = true;
100        private boolean show3DName = true; // not implemented to change yet
101        private boolean show3D = true; // no implemented to change yet, or to modify showing of sat
102        private boolean showGroundTrack3d = false;
103        private boolean show3DOrbitTraceECI = true; // show orbit in ECI mode otherwise , ECEF
104        
105            // 3D model parameters
106        private boolean use3dModel = false; // use custom 3D model (or default sphere)
107        private String threeDModelPath = "globalstar/Globalstar.3ds"; // path to the custom model, default= globalstar/Globalstar.3ds ?
108    //    private transient WWModel3D_new threeDModel; // DO NOT STORE when saving -- need to reload this -- TOO MUCH DATA!
109        private double threeDModelSizeFactor = 300000;
110        
111        /** Creates a new instance of SatelliteProps - default properties with given name and TLE lines
112         * @param name name of satellite
113         * @param tleLine1 first line of two line element
114         * @param tleLine2 second line of two line element
115         * @throws Exception if TLE data is bad
116         */
117        public SatelliteTleSGP4(String name, String tleLine1, String tleLine2) throws Exception
118        {
119            // create internal TLE object
120            tle = new TLE(name,tleLine1,tleLine2);
121            
122            // initialize sgp4 propogator data for the satellite
123            sgp4SatData = new SGP4SatData();
124            
125            // try to load TLE into propogator
126    
127            // options - hard coded
128            char opsmode = SGP4utils.OPSMODE_IMPROVED; // OPSMODE_IMPROVED
129            SGP4unit.Gravconsttype gravconsttype = SGP4unit.Gravconsttype.wgs72;
130    
131            // load TLE data as strings and INI all SGP4 data
132            boolean loadSuccess = SGP4utils.readTLEandIniSGP4(name, tleLine1, tleLine2, opsmode, gravconsttype, sgp4SatData);
133    
134            // if there is an error loading send an exception
135            if (!loadSuccess)
136            {
137                throw new Exception("Error loading TLE error code:" + sgp4SatData.error);
138            }
139    
140            // calculate TLE age
141            tleEpochJD = sgp4SatData.jdsatepoch;
142              
143        }
144    /*    
145        @Override
146        public void updateTleData(TLE newTLE)
147        {
148            this.tle = newTLE; // save new TLE
149            
150            // new spg4 object
151            sgp4SatData = new SGP4SatData();
152            
153            // read TLE
154            // options - hard coded
155            char opsmode = SGP4utils.OPSMODE_IMPROVED; // OPSMODE_IMPROVED
156            SGP4unit.Gravconsttype gravconsttype = SGP4unit.Gravconsttype.wgs72;
157    
158            // load TLE data as strings and INI all SGP4 data
159            boolean loadSuccess = SGP4utils.readTLEandIniSGP4(tle.getSatName(), tle.getLine1(), tle.getLine2(), opsmode, gravconsttype, sgp4SatData);
160    
161            // if there is an error loading send an exception
162            if (!loadSuccess)
163            {
164                JOptionPane.showMessageDialog(null,"Error reading updated TLE, error code:" + sgp4SatData.error + "\n Satellite: "+ tle.getSatName());
165            }
166    
167            // calculate TLE age
168            tleEpochJD = sgp4SatData.jdsatepoch;
169                   
170            // ground track needs to be redone with new data
171            groundTrackIni = false;
172            
173            //System.out.println("Updated " + tle.getSatName() );
174        }
175    */    
176        public void propogate2JulDate(double julDate)
177        {
178            // save date
179            this.currentJulianDate = julDate;
180    
181            // using JulDate because function uses time diff between jultDate of ephemeris, SGP4 uses UTC
182            // propogate satellite to given date - saves result in TEME to posTEME and velTEME in km, km/s
183            boolean propSuccess = SGP4unit.sgp4Prop2JD(sgp4SatData, julDate, posTEME, velTEME);
184            if(!propSuccess)
185            {
186                System.out.println("Error SGP4 Propagation failed for sat: " + sgp4SatData.name + ", JD: " + sgp4SatData.jdsatepoch + ", error code: "+ sgp4SatData.error);
187            }
188    
189            // scale output to meters
190            for(int i=0;i<3;i++)
191            {
192                // TEME
193                 posTEME[i] = posTEME[i]*1000.0;
194                 velTEME[i] = velTEME[i]*1000.0;
195            }
196            
197            //print differene TT-UT
198            //System.out.println("TT-UT [days]= " + SDP4TimeUtilities.DeltaT(julDate-2450000)*24.0*60*60);
199            
200            
201            // SEG - 11 June 2009 -- new information (to me) on SGP4 propogator coordinate system:
202            // SGP4 output is in true equator and mean equinox (TEME) of Date *** note some think of epoch, but STK beleives it is of date from tests **
203            // It depends also on the source for the TLs if from the Nasa MCC might be MEME but most US Gov - TEME
204            // Also the Lat/Lon/Alt calculations are based on TEME (of Date) so that is correct as it was used before!
205            // References:
206            // http://www.stk.com/pdf/STKandSGP4/STKandSGP4.pdf  (STK's stance on SGP4)
207            // http://www.agi.com/resources/faqSystem/files/2144.pdf  (newer version of above)
208            // http://www.satobs.org/seesat/Aug-2004/0111.html
209            // http://celestrak.com/columns/v02n01/ "Orbital Coordinate Systems, Part I" by Dr. T.S. Kelso
210            // http://en.wikipedia.org/wiki/Earth_Centered_Inertial
211            // http://ccar.colorado.edu/asen5050/projects/projects_2004/aphanuphong/p1.html  (bad coefficients? conversion between TEME and J2000 (though slightly off?))
212            //  http://www.centerforspace.com/downloads/files/pubs/AIAA-2000-4025.pdf
213            // http://celestrak.com/software/vallado-sw.asp  (good software)
214    
215            double mjd = julDate-AstroConst.JDminusMJD;
216    
217            // get position information back out - convert to J2000 (does TT time need to be used? - no)
218            //j2kPos = CoordinateConversion.EquatorialEquinoxToJ2K(mjd, sdp4Prop.itsR); //julDate-2400000.5
219            //j2kVel = CoordinateConversion.EquatorialEquinoxToJ2K(mjd, sdp4Prop.itsV);
220            // based on new info about coordinate system, to get the J2K other conversions are needed!
221            // precession from rk5 -> mod
222            double ttt = (mjd-AstroConst.MJD_J2000) /36525.0;
223            double[][] A = J2kCoordinateConversion.teme_j2k(J2kCoordinateConversion.Direction.to,ttt, 24, 2, 'a');
224            // rotate position and velocity
225            j2kPos = J2kCoordinateConversion.matvecmult( A, posTEME);
226            j2kVel = J2kCoordinateConversion.matvecmult( A, velTEME);
227    
228            //System.out.println("Date: " + julDate +", Pos: " + sdp4Prop.itsR[0] + ", " + sdp4Prop.itsR[1] + ", " + sdp4Prop.itsR[2]);
229    
230            // save old lat/long for ascending node check
231            double[] oldLLA = lla.clone(); // copy old LLA
232            
233            // calculate Lat,Long,Alt - must use Mean of Date (MOD) Position
234            lla = GeoFunctions.GeodeticLLA(posTEME,julDate-AstroConst.JDminusMJD); // j2kPos
235            
236            // Check to see if the ascending node has been passed
237            if(showGroundTrack==true)
238            {
239                if(groundTrackIni == false ) // update ground track needed
240                {
241                    initializeGroundTrack();
242                }
243                else if( oldLLA[0] < 0 && lla[0] >=0) // check for ascending node pass
244                {
245                    //System.out.println("Ascending NODE passed: " + tle.getSatName() );
246                    initializeGroundTrack(); // for new ini each time
247                    
248                } // ascending node passed
249                
250            } // if show ground track is true
251            
252            // if 3D model - update its properties -- NOT DONE HERE - done in OrbitModelRenderable (so it can be done for any sat)
253                   
254        } // propogate2JulDate
255        
256        
257        
258        // initalize the ground track from any starting point, as long as Juldate !=-1
259        private void initializeGroundTrack()
260        {
261            if(currentJulianDate == -1)
262            {
263                // nothing to do yet, we haven't been given an initial time
264                return;
265            }
266            
267            // find time of last acending node crossing
268            
269            // initial guess -- the current time        
270            double lastAscendingNodeTime = currentJulianDate; // time of last ascending Node Time
271            
272            // calculate period - in minutes
273            double periodMin = Kepler.CalculatePeriod(AstroConst.GM_Earth,j2kPos,j2kVel)/(60.0);
274            //System.out.println("period [min] = "+periodMin);
275            
276            // time step divisions (in fractions of a day)
277            double fracOfPeriod = 15.0;
278            double timeStep = (periodMin/(60.0*24.0)) / fracOfPeriod;
279            
280            // first next guess
281            double newGuess1 = lastAscendingNodeTime - timeStep;
282            
283            // latitude variables
284            double lat0 =  lla[0]; //  current latitude
285            double lat1 = (calculateLatLongAltXyz(newGuess1))[0]; // calculate latitude values       
286            
287            // bracket the crossing using timeStep step sizes
288            while( !( lat0>=0 && lat1<0 ) )
289            {
290                // move back a step
291                lastAscendingNodeTime = newGuess1;
292                lat0 = lat1;
293                
294                // next guess
295                newGuess1 = lastAscendingNodeTime - timeStep;
296                
297                // calculate latitudes of the new value
298                lat1 = (calculateLatLongAltXyz(newGuess1))[0];
299            } // while searching for ascending node
300            
301                  
302            // secand method -- determine within a second!
303            double outJul = secantMethod(lastAscendingNodeTime-timeStep, lastAscendingNodeTime, 1.0/(60.0*60.0*24.0), 20);
304            //System.out.println("Guess 1:" + (lastAscendingNodeTime-timeStep) );
305            //System.out.println("Guess 2:" + (lastAscendingNodeTime));
306            //System.out.println("Answer: " + outJul);
307            
308            // update times: Trust Period Calculations for how far in the future and past to calculate out to
309            // WARNING: period calculation is based on osculating elements may not be 100% accurate
310            //          as this is just for graphical updates should be okay (no mid-course corrections assumed)
311            lastAscendingNodeTime = outJul;
312            double leadEndTime = lastAscendingNodeTime + groundTrackLeadPeriodMultiplier*periodMin/(60.0*24); // Julian Date for last lead point (furthest in future)
313            double lagEndTime = lastAscendingNodeTime - groundTrackLagPeriodMultiplier*periodMin/(60.0*24); // Julian Date for the last lag point (furthest in past)
314            
315            // fill in lead/lag arrays
316            fillGroundTrack(lastAscendingNodeTime,leadEndTime,lagEndTime);
317            
318            groundTrackIni = true;
319            return;
320            
321        } // initializeGroundTrack
322        
323        // fill in the Ground Track given Jul Dates for 
324        // 
325        private void fillGroundTrack(double lastAscendingNodeTime, double leadEndTime, double lagEndTime)
326        {
327            // points in the lead direction
328            int ptsLead = (int)Math.ceil(grnTrkPointsPerPeriod*groundTrackLeadPeriodMultiplier);
329            latLongLead = new double[ptsLead][3];        
330            temePosLead =  new double[ptsLead][3];
331            timeLead = new double[ptsLead];
332                    
333            for(int i=0;i<ptsLead;i++)
334            {
335                double ptTime = lastAscendingNodeTime + i*(leadEndTime-lastAscendingNodeTime)/(ptsLead-1);
336                
337               // PUT HERE calculate lat lon
338                double[] ptLlaXyz = calculateLatLongAltXyz(ptTime);
339                
340                latLongLead[i][0] = ptLlaXyz[0]; // save lat
341                latLongLead[i][1] = ptLlaXyz[1]; // save long
342                latLongLead[i][2] = ptLlaXyz[2]; // save altitude
343                
344                temePosLead[i][0] = ptLlaXyz[3]; // x
345                temePosLead[i][1] = ptLlaXyz[4]; // y
346                temePosLead[i][2] = ptLlaXyz[5]; // z
347                
348                timeLead[i] = ptTime; // save time
349                
350            } // for each lead point
351            
352            // points in the lag direction
353            int ptsLag = (int)Math.ceil(grnTrkPointsPerPeriod*groundTrackLagPeriodMultiplier);
354            latLongLag = new double[ptsLag][3];
355            temePosLag = new double[ptsLag][3];
356            timeLag = new double[ptsLag];
357            
358            for(int i=0;i<ptsLag;i++)
359            {
360                double ptTime = lastAscendingNodeTime + i*(lagEndTime-lastAscendingNodeTime)/(ptsLag-1);
361                
362                double[] ptLlaXyz = calculateLatLongAltXyz(ptTime);
363                 
364                latLongLag[i][0] = ptLlaXyz[0]; // save lat
365                latLongLag[i][1] = ptLlaXyz[1]; // save long
366                latLongLag[i][2] = ptLlaXyz[2]; // save alt
367                
368                temePosLag[i][0] = ptLlaXyz[3]; // x
369                temePosLag[i][1] = ptLlaXyz[4]; // y
370                temePosLag[i][2] = ptLlaXyz[5]; // z
371                
372                timeLag[i] = ptTime;
373                
374            } // for each lag point
375        } // fillGroundTrack
376       
377        // takes in JulDate, returns lla and teme position
378        private double[] calculateLatLongAltXyz(double ptTime)
379        {
380            double[] ptPos = calculateTemePositionFromUT(ptTime);
381            
382            // get lat and long
383            double[] ptLla = GeoFunctions.GeodeticLLA(ptPos,ptTime-AstroConst.JDminusMJD);
384            
385            double[] ptLlaXyz = new double[] {ptLla[0],ptLla[1],ptLla[2],ptPos[0],ptPos[1],ptPos[2]};
386            
387            return ptLlaXyz;
388        } // calculateLatLongAlt
389        
390        // 
391       
392        /**
393         * Calculate J2K position of this sat at a given JulDateTime (doesn't save the time) - can be useful for event searches or optimization
394         * @param julDate - julian date
395         * @return j2k position of satellite in meters
396         */
397    /*
398        @Override
399        public double[] calculateJ2KPositionFromUT(double julDate)
400        {
401            double[] ptPos = calculateTemePositionFromUT(julDate);
402    
403            double mjd = julDate-AstroConst.JDminusMJD;
404    
405            // get position information back out - convert to J2000
406            // precession from rk5 -> mod
407            double ttt = (mjd-AstroConst.MJD_J2000) /36525.0;
408            double[][] A = J2kCoordinateConversion.teme_j2k(J2kCoordinateConversion.Direction.to,ttt, 24, 2, 'a');
409            // rotate position
410            double[] j2kPosI = J2kCoordinateConversion.matvecmult( A, ptPos);
411            
412            return j2kPosI;
413            
414        } // calculatePositionFromUT
415    */    
416        /**
417         * Calculate true-equator, mean equinox (TEME) of date position of this sat at a given JulDateTime (doesn't save the time) - can be useful for event searches or optimization
418         * @param julDate - julian date
419         * @return j2k position of satellite in meters
420         */
421    
422        public double[] calculateTemePositionFromUT(double julDate)
423        {
424            double[] ptPos = new double[3];
425            double[] ptVel = new double[3];
426    
427            // using JulDate because function uses time diff between jultDate of ephemeris, SGP4 uses UTC
428            // propogate satellite to given date - saves result in TEME to posTEME and velTEME in km, km/s
429            boolean propSuccess = SGP4unit.sgp4Prop2JD(sgp4SatData, julDate, ptPos, ptVel);
430            if(!propSuccess)
431            {
432                System.out.println("Error (2) SGP4 Propagation failed for sat: " + sgp4SatData.name + ", JD: " + sgp4SatData.jdsatepoch + ", error code: "+ sgp4SatData.error);
433            }
434    
435            // scale output to meters
436            for(int i=0;i<3;i++)
437            {
438                // TEME
439                 ptPos[i] = ptPos[i]*1000.0;
440            }
441            
442            return ptPos;
443            
444        } // calculatePositionFromUT
445        
446        
447        //---------------------------------------
448        //  SECANT Routines to find Crossings of the Equator (hopefully Ascending Nodes)
449        // xn_1 = date guess 1
450        // xn date guess 2
451        // tol = convergence tolerance
452        // maxIter = maximum iterations allowed
453        // RETURNS: double = julian date of crossing
454        private double secantMethod(double xn_1, double xn, double tol, int maxIter)
455        {
456    
457            double d;
458            
459            // calculate functional values at guesses
460            double fn_1 = latitudeGivenJulianDate(xn_1);
461            double fn = latitudeGivenJulianDate(xn);
462            
463            for (int n = 1; n <= maxIter; n++)
464            {
465                d = (xn - xn_1) / (fn - fn_1) * fn;
466                if (Math.abs(d) < tol) // convergence check
467                {
468                    //System.out.println("Iters:"+n);
469                    return xn;
470                }
471                
472                // save past point
473                xn_1 = xn;
474                fn_1 = fn;
475                
476                // new point
477                xn = xn - d;
478                fn = latitudeGivenJulianDate(xn);
479            }
480            
481            System.out.println("Warning: Secant Method - Max Iteration limit reached finding Asending Node.");
482            
483            return xn;
484        } // secantMethod
485        
486        private double latitudeGivenJulianDate(double julDate)
487        {
488            // computer latiude of the spacecraft at a given date
489            double[] ptPos = calculateTemePositionFromUT(julDate);
490            
491            // get lat and long
492            double[] ptLla = GeoFunctions.GeodeticLLA(ptPos,julDate-AstroConst.JDminusMJD);
493            
494            return ptLla[0]; // pass back latitude
495            
496        } // latitudeGivenJulianDate
497    
498        //--------------------------------------
499        
500        public void setShowGroundTrack(boolean showGrndTrk)
501        {
502            showGroundTrack = showGrndTrk;
503            
504            if(showGrndTrk == false)
505            {
506                groundTrackIni = false; 
507                latLongLead = new double[][] {{}}; // save some space
508                latLongLag = new double[][] {{}}; // sace some space
509                temePosLag = new double[][] {{}};
510                temePosLead = new double[][] {{}};
511                timeLead = new double[] {};
512                timeLag = new double[] {};
513            }
514            else
515            {
516                // ground track needs to be initalized
517                initializeGroundTrack();
518            }
519        }
520    /*    
521        public boolean getShowGroundTrack()
522        {
523            return showGroundTrack;
524        }
525    */ 
526        public double getLatitude()
527        {
528            return lla[0];
529        }
530        
531        public double getLongitude()
532        {
533            return lla[1];
534        }
535        
536        public double getAltitude()
537        {
538            return lla[2];
539        }
540       
541        public double[] getLLA()
542        {
543            return lla;
544        }
545    /*    
546        // TT or UTC? = UTC
547        public double getSatTleEpochJulDate()
548        {
549            return sgp4SatData.jdsatepoch;
550        }
551        
552        public double getCurrentJulDate()
553        {
554            return currentJulianDate;
555        }
556        
557        public double[] getJ2000Position()
558        {
559            return j2kPos.clone();
560        }
561        
562        public double[] getJ2000Velocity()
563        {
564            return j2kVel.clone();
565        }
566        
567        public boolean getPlot2D()
568        {
569            return plot2d;
570        }
571        
572        public Color getSatColor()
573        { 
574            return satColor;
575        }
576        
577        public boolean getPlot2DFootPrint()
578        {
579            return plot2DFootPrint;
580        }
581        
582        public boolean getGroundTrackIni()
583        {
584            return groundTrackIni;
585        }
586        
587        public void setGroundTrackIni2False()
588        {
589            // forces repaint of ground track next update
590            groundTrackIni = false;
591        }
592        
593        public int getNumGroundTrackLeadPts()
594        {
595            return latLongLead.length;
596        }
597            
598        public int getNumGroundTrackLagPts()
599        {
600            return latLongLag.length;
601        }
602            
603        public double[] getGroundTrackLlaLeadPt(int index)
604        {
605            return new double[] {latLongLead[index][0],latLongLead[index][1],latLongLead[index][2]};
606        }
607        
608        public double[] getGroundTrackLlaLagPt(int index)
609        {
610            return new double[] {latLongLag[index][0],latLongLag[index][1],latLongLag[index][2]};
611        }
612        
613        public double[] getGroundTrackXyzLeadPt(int index)
614        {
615            return new double[] {getTemePosLead()[index][0],getTemePosLead()[index][1],getTemePosLead()[index][2]};
616        }
617        
618        public double[] getGroundTrackXyzLagPt(int index)
619        {
620            return new double[] {getTemePosLag()[index][0],getTemePosLag()[index][1],getTemePosLag()[index][2]};
621        }
622        
623        
624        // returns satellite's current perdiod based on current pos/vel in Minutes
625        public double getPeriod()
626        {
627            return Kepler.CalculatePeriod(AstroConst.GM_Earth,j2kPos,j2kVel)/(60.0);
628        }
629        
630        public String getName()
631        {
632            return tle.getSatName();
633        }
634        
635        public double[] getKeplarianElements()
636        {
637            return Kepler.SingularOsculatingElements( AstroConst.GM_Earth, j2kPos, j2kVel ); 
638        }
639        
640        public double getTleEpochJD()
641        {
642            return tleEpochJD;
643        }
644        
645        public double getTleAgeDays()
646        {
647            return currentJulianDate - tleEpochJD;
648        }
649    
650        public int getNumPtsFootPrint()
651        {
652            return numPtsFootPrint;
653        }
654    
655        public void setNumPtsFootPrint(int numPtsFootPrint)
656        {
657            this.numPtsFootPrint = numPtsFootPrint;
658        }
659    
660        public boolean isShowName2D()
661        {
662            return showName2D;
663        }
664    
665        public void setShowName2D(boolean showName2D)
666        {
667            this.showName2D = showName2D;
668        }
669    
670        public boolean isFillFootPrint()
671        {
672            return fillFootPrint;
673        }
674    
675        public void setFillFootPrint(boolean fillFootPrint)
676        {
677            this.fillFootPrint = fillFootPrint;
678        }
679    
680        public int getGrnTrkPointsPerPeriod()
681        {
682            return grnTrkPointsPerPeriod;
683        }
684    
685        public void setGrnTrkPointsPerPeriod(int grnTrkPointsPerPeriod)
686        {
687            this.grnTrkPointsPerPeriod = grnTrkPointsPerPeriod;
688        }
689    
690        public double getGroundTrackLeadPeriodMultiplier()
691        {
692            return groundTrackLeadPeriodMultiplier;
693        }
694    
695        public void setGroundTrackLeadPeriodMultiplier(double groundTrackLeadPeriodMultiplier)
696        {
697            this.groundTrackLeadPeriodMultiplier = groundTrackLeadPeriodMultiplier;
698        }
699    
700        public double getGroundTrackLagPeriodMultiplier()
701        {
702            return groundTrackLagPeriodMultiplier;
703        }
704    
705        public void setGroundTrackLagPeriodMultiplier(double groundTrackLagPeriodMultiplier)
706        {
707            this.groundTrackLagPeriodMultiplier = groundTrackLagPeriodMultiplier;
708        }
709    
710        public void setPlot2d(boolean plot2d)
711        {
712            this.plot2d = plot2d;
713        }
714    
715        public void setSatColor(Color satColor)
716        {
717            this.satColor = satColor;
718        }
719    
720        public void setPlot2DFootPrint(boolean plot2DFootPrint)
721        {
722            this.plot2DFootPrint = plot2DFootPrint;
723        }
724    */
725        public double[] getTEMEPos()
726        {
727            return posTEME.clone();
728        }
729    /*
730        public boolean isShow3DOrbitTrace()
731        {
732            return show3DOrbitTrace;
733        }
734    
735        public void setShow3DOrbitTrace(boolean show3DOrbitTrace)
736        {
737            this.show3DOrbitTrace = show3DOrbitTrace;
738        }
739    
740        public boolean isShow3DFootprint()
741        {
742            return show3DFootprint;
743        }
744    
745        public void setShow3DFootprint(boolean show3DFootprint)
746        {
747            this.show3DFootprint = show3DFootprint;
748        }
749    
750        public boolean isShow3DName()
751        {
752            return show3DName;
753        }
754    
755        public void setShow3DName(boolean show3DName)
756        {
757            this.show3DName = show3DName;
758        }
759    
760        public boolean isShowGroundTrack3d()
761        {
762            return showGroundTrack3d;
763        }
764    
765        public void setShowGroundTrack3d(boolean showGroundTrack3d)
766        {
767            this.showGroundTrack3d = showGroundTrack3d;
768        }
769    
770        public boolean isShow3DOrbitTraceECI()
771        {
772            return show3DOrbitTraceECI;
773        }
774    
775        public void setShow3DOrbitTraceECI(boolean show3DOrbitTraceECI)
776        {
777            this.show3DOrbitTraceECI = show3DOrbitTraceECI;
778        }
779    
780        public boolean isShow3D()
781        {
782            return show3D;
783        }
784    
785        public void setShow3D(boolean show3D)
786        {
787            this.show3D = show3D;
788        }
789    
790        public // laging lat/long coordinates for ground track
791        double[][] getTemePosLead()
792        {
793            return temePosLead;
794        }
795    
796        public // leading Mean of date position coordinates for ground track
797        double[][] getTemePosLag()
798        {
799            return temePosLag;
800        }
801    
802        public // laging Mean of date position coordinates for ground track
803        double[] getTimeLead()
804        {
805            return timeLead;
806        }
807    
808        public // array for holding times associated with lead coordinates (Jul Date)
809        double[] getTimeLag()
810        {
811            return timeLag;
812        }
813        
814        // 3D model -------------------------
815        public boolean isUse3dModel()
816        {
817            return use3dModel; 
818        }
819        
820        public void setUse3dModel(boolean use3dModel)
821        {
822            this.use3dModel = use3dModel;
823            
824            if(use3dModel && threeDModelPath.length() > 0)
825            {
826                // check that file exsists? - auto done in loader
827                
828                //String path = "data/models/globalstar/Globalstar.3ds";
829                //String path = "data/models/isscomplete/iss_complete.3ds";
830                
831                loadNewModel(threeDModelPath);
832            }
833        }
834        
835        public String getThreeDModelPath()
836        {
837            return threeDModelPath;
838        }
839    */    
840        /**
841         * Relative path to the model -- relative from "user.dir"/data/models/
842         * @param path
843         */
844    /*
845        public void setThreeDModelPath(String path)
846        {
847            if(use3dModel && !(path.equalsIgnoreCase(this.threeDModelPath)) )
848            {
849                // need to load the model
850                loadNewModel(path);//"test/data/globalstar/Globalstar.3ds");
851            }
852            
853            this.threeDModelPath = path; // save path no matter
854        }
855        
856        private void loadNewModel(String path)
857        {
858            String localPath = "data/models/"; // path to models root from user.dir
859            
860            try
861                {
862                    net.java.joglutils.model.geometry.Model model3DS = ModelFactory.createModel(localPath + path);
863                    //model3DS.setUseLighting(false); // turn off lighting!
864    
865                    threeDModel =  new WWModel3D_new(model3DS,
866                            new Position(Angle.fromRadians(this.getLatitude()),
867                            Angle.fromRadians(this.getLongitude()),
868                            this.getAltitude()));
869    
870                    threeDModel.setMaitainConstantSize(true);
871                    threeDModel.setSize(threeDModelSizeFactor); // this needs to be a property!
872                    
873                    threeDModel.updateAttitude(this); // fixes attitude intitially
874                    
875                }catch(Exception e)
876                {
877                    System.out.println("ERROR LOADING 3D MODEL");
878                }
879        }
880        
881        public WWModel3D_new getThreeDModel()
882        {
883            return threeDModel;
884        }    
885        
886        public  double[] getTEMEVelocity()
887        {
888            return velTEME.clone();
889        }
890    
891        public double getThreeDModelSizeFactor()
892        {
893            return threeDModelSizeFactor;
894        }
895    
896        public void setThreeDModelSizeFactor(double modelSizeFactor)
897        {
898            // should the 3D model be reloaded now?
899            if(modelSizeFactor != threeDModelSizeFactor && use3dModel && threeDModelPath.length()>0)
900            {
901                //loadNewModel(threeDModelPath);
902                if(threeDModel != null)
903                {
904                    threeDModel.setSize(modelSizeFactor);
905                }
906            }
907            
908            this.threeDModelSizeFactor = modelSizeFactor;
909        }
910        
911        @Override
912        public String toString()
913        {
914            return this.tle.getSatName();
915        }
916    */    
917    } // SatelliteProps