001    /*
002     * $Id: SGP4utils.java,v 1.3 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     * Contains functions to read TLE data files and initalize the SGP4 propogator
032     * as well as other routines from sgp4ext.cpp
033     * 
034     * <p>
035     * sgp4ext.cpp header information:
036     * <p>
037     *
038     *                               sgp4ext.cpp
039     *
040     *    this file contains extra routines needed for the main test program for sgp4.
041     *    these routines are derived from the astro libraries.
042     *
043     *                            companion code for
044     *               fundamentals of astrodynamics and applications
045     *                                    2007
046     *                              by david vallado
047     *
048     *       (w) 719-573-2600, email dvallado@agi.com
049     *
050     *    current :
051     *               7 may 08  david vallado
052     *                           fix sgn
053     *    changes :
054     *               2 apr 07  david vallado
055     *                           fix jday floor and str lengths
056     *                           updates for constants
057     *              14 aug 06  david vallado
058     *                           original baseline
059     *       ----------------------------------------------------------------      */
060    
061    package edu.wisc.ssec.mcidasv.data.adde.sgp4;
062    
063    import java.text.DecimalFormat;
064    import java.text.DecimalFormatSymbols;
065    import java.text.ParsePosition;
066    
067    /**
068     * 19 June 2009
069     * @author Shawn E. Gano, shawn@gano.name
070     */
071    public class SGP4utils
072    {
073        public static double pi = SGP4unit.pi;
074        public static char OPSMODE_AFSPC = 'a';
075        public static char OPSMODE_IMPROVED = 'i';
076    
077        /**
078         * Reads the data from the TLE and initializes the SGP4 propogator variables and stores them in the SGP4unit.Gravconsttype object
079         * DOES NOT PERFORM ANY INTERNAL CHECK BEYOND BASICS OF THE TLE DATA use other methods to do that if desired.
080         *
081         * @param satName
082         * @param line1  TLE line 1
083         * @param line2  TLE line 2
084         * @param opsmode 
085         * @param whichconst which constants to use in propogation
086         * @param satrec  object to store the SGP4 data
087         * @return if the sgp4 propogator was initialized properly
088         */
089        public static boolean readTLEandIniSGP4(String satName, String line1, String line2, char opsmode, SGP4unit.Gravconsttype whichconst, SGP4SatData satrec)
090        {
091            final double deg2rad = pi / 180.0;         //   0.0174532925199433
092            final double xpdotp = 1440.0 / (2.0 * pi);  // 229.1831180523293
093    
094            double sec, tumin;//, mu, radiusearthkm, xke, j2, j3, j4, j3oj2;
095    //        double startsec, stopsec, startdayofyr, stopdayofyr, jdstart, jdstop;
096    //        int startyear, stopyear, startmon, stopmon, startday, stopday,
097    //                starthr, stophr, startmin, stopmin;
098    //        int cardnumb, j; // numb,
099            //long revnum = 0, elnum = 0;
100            //char classification, intldesg[11], tmpstr[80];
101            int year = 0;
102            int mon, day, hr, minute;//, nexp, ibexp;
103    
104            double[] temp = SGP4unit.getgravconst(whichconst);
105            tumin = temp[0];
106    //        mu = temp[1];
107    //        radiusearthkm = temp[2];
108    //        xke = temp[3];
109    //        j2 = temp[4];
110    //        j3 = temp[5];
111    //        j4 = temp[6];
112    //        j3oj2 = temp[7];
113    
114            satrec.error = 0;
115    
116            satrec.name = satName;
117    
118            // SEG -- save gravity  - moved to SGP4unit.sgp4init fo consistancy
119            //satrec.gravconsttype = whichconst;
120    
121            // get variables from the two lines
122            satrec.line1 = line1;
123            try
124            {
125                readLine1(line1, satrec);
126            }
127            catch(Exception e)
128            {
129                System.out.println("Error Reading TLE line 1: " + e.toString());
130                satrec.tleDataOk = false;
131                satrec.error = 7;
132                return false;
133            }
134    
135            satrec.line2 = line2;
136            try
137            {
138                readLine2(line2, satrec);
139            }
140            catch(Exception e)
141            {
142                System.out.println("Error Reading TLE line 2: " + e.toString());
143                satrec.tleDataOk = false;
144                satrec.error = 7;
145                return false;
146            }
147    
148    
149            // ---- find no, ndot, nddot ----
150            satrec.no = satrec.no / xpdotp; //* rad/min
151            satrec.nddot = satrec.nddot * Math.pow(10.0, satrec.nexp);
152            satrec.bstar = satrec.bstar * Math.pow(10.0, satrec.ibexp);
153    
154            // ---- convert to sgp4 units ----
155            satrec.a = Math.pow(satrec.no * tumin, (-2.0 / 3.0));
156            satrec.ndot = satrec.ndot / (xpdotp * 1440.0);  //* ? * minperday
157            satrec.nddot = satrec.nddot / (xpdotp * 1440.0 * 1440);
158    
159            // ---- find standard orbital elements ----
160            satrec.inclo = satrec.inclo * deg2rad;
161            satrec.nodeo = satrec.nodeo * deg2rad;
162            satrec.argpo = satrec.argpo * deg2rad;
163            satrec.mo = satrec.mo * deg2rad;
164    
165            satrec.alta = satrec.a * (1.0 + satrec.ecco) - 1.0;
166            satrec.altp = satrec.a * (1.0 - satrec.ecco) - 1.0;
167    
168            // ----------------------------------------------------------------
169            // find sgp4epoch time of element set
170            // remember that sgp4 uses units of days from 0 jan 1950 (sgp4epoch)
171            // and minutes from the epoch (time)
172            // ----------------------------------------------------------------
173    
174            // ---------------- temp fix for years from 1957-2056 -------------------
175            // --------- correct fix will occur when year is 4-digit in tle ---------
176            if(satrec.epochyr < 57)
177            {
178                year = satrec.epochyr + 2000;
179            }
180            else
181            {
182                year = satrec.epochyr + 1900;
183            }
184    
185            // computes the m/d/hr/min/sec from year and epoch days
186            MDHMS mdhms = days2mdhms(year, satrec.epochdays);
187            mon = mdhms.mon;
188            day = mdhms.day;
189            hr = mdhms.hr;
190            minute = mdhms.minute;
191            sec = mdhms.sec;
192            // computes the jd from  m/d/...
193            satrec.jdsatepoch = jday(year, mon, day, hr, minute, sec);
194    
195            // ---------------- initialize the orbit at sgp4epoch -------------------
196            boolean result = SGP4unit.sgp4init(whichconst, opsmode, satrec.satnum,
197                    satrec.jdsatepoch - 2433281.5, satrec.bstar,
198                    satrec.ecco, satrec.argpo, satrec.inclo, satrec.mo, satrec.no,
199                    satrec.nodeo, satrec);
200    
201            return result;
202    
203        } // readTLEandIniSGP4
204    
205        private static boolean readLine1(String line1, SGP4SatData satrec) throws Exception
206        {
207            String tleLine1 = line1; // first line
208            if(!tleLine1.startsWith("1 "))
209            {
210                throw new Exception("TLE line 1 not valid first line");
211            }
212    
213            // satnum
214            satrec.satnum = (int)readFloatFromString(tleLine1.substring(2, 7));
215            // classification
216            satrec.classification = tleLine1.substring(7, 8); // 1 char
217            // intln designator
218            satrec.intldesg = tleLine1.substring(9, 17); // should be within 8
219    
220            // epochyr
221            satrec.epochyr = (int)readFloatFromString(tleLine1.substring(18, 20));
222    
223            // epoch days
224            satrec.epochdays = readFloatFromString(tleLine1.substring(20, 32));
225    
226            // ndot
227            satrec.ndot = readFloatFromString(tleLine1.substring(33, 43));
228    
229            // nddot
230            //nexp
231            if((tleLine1.substring(44, 52)).equals("        "))
232            {
233                satrec.nddot = 0;
234                satrec.nexp = 0;
235            }
236            else
237            {
238                satrec.nddot = readFloatFromString(tleLine1.substring(44, 50)) / 1.0E5;
239                //nexp
240                satrec.nexp = (int)readFloatFromString(tleLine1.substring(50, 52));
241            }
242            //bstar
243            satrec.bstar = readFloatFromString(tleLine1.substring(53, 59)) / 1.0E5;
244            //ibex
245            satrec.ibexp = (int)readFloatFromString(tleLine1.substring(59, 61));
246    
247            // these last things are not essential so just try to read them, and give a warning - but no error
248            try
249            {
250                // num b.
251                satrec.numb = (int)readFloatFromString(tleLine1.substring(62, 63));
252    
253                //  elnum
254                satrec.elnum = (long)readFloatFromString(tleLine1.substring(64, 68));
255            }
256            catch(Exception e)
257            {
258                System.out.println("Warning: Error Reading numb or elnum from TLE line 1 sat#:" + satrec.satnum);
259            }
260    
261            // checksum
262            //int checksum1 = (int) readFloatFromString(tleLine1.substring(68));
263    
264            // if no errors yet everything went ok
265            return true;
266        } // readLine1
267    
268        private static boolean readLine2(String line2, SGP4SatData satrec) throws Exception
269        {
270            /* Read the second line of elements. */
271    
272            //theLine = aFile.readLine();
273            String tleLine2 = line2; // second line
274            if(!tleLine2.startsWith("2 "))
275            {
276                throw new Exception("TLE line 2 not valid second line");
277            }
278    
279            // satnum
280            int satnum = (int)readFloatFromString(tleLine2.substring(2, 7));
281            if(satnum != satrec.satnum)
282            {
283                System.out.println("Warning TLE line 2 Sat Num doesn't match line1 for sat: " + satrec.name);
284            }
285    
286            // inclination
287            satrec.inclo = readFloatFromString(tleLine2.substring(8, 17));
288    
289            // nodeo
290            satrec.nodeo = readFloatFromString(tleLine2.substring(17, 26));
291    
292            //satrec.ecco
293            satrec.ecco = readFloatFromString(tleLine2.substring(26, 34)) / 1.0E7;
294    
295            // satrec.argpo
296            satrec.argpo = readFloatFromString(tleLine2.substring(34, 43));
297    
298            // satrec.mo
299            satrec.mo = readFloatFromString(tleLine2.substring(43, 52));
300    
301            // no
302            satrec.no = readFloatFromString(tleLine2.substring(52, 63));
303    
304            // try to read other data
305            try
306            {
307                // revnum
308                satrec.revnum = (long)readFloatFromString(tleLine2.substring(63, 68));
309            }
310            catch(Exception e)
311            {
312                System.out.println("Warning: Error Reading revnum from TLE line 2 sat#:" + satrec.satnum + "\n" + e.toString());
313                satrec.revnum = -1;
314            }
315    
316    //        // checksum
317    //        int checksum2 = (int) readFloatFromString(tleLine2.substring(68));
318    
319            return true;
320        } // readLine1
321    
322        /**
323         * Read float data from a string
324         * @param inStr
325         * @return
326         * @throws Exception 
327         */
328        protected static double readFloatFromString(String inStr) throws Exception
329        {
330            // make sure decimal sparator is '.' so it works in other countries
331            // because of this can't use Double.parse
332            DecimalFormat dformat = new DecimalFormat("#");
333            DecimalFormatSymbols dfs = new DecimalFormatSymbols();
334            dfs.setDecimalSeparator('.');
335            dformat.setDecimalFormatSymbols(dfs);
336    
337            // trim white space and if there is a + at the start
338            String trimStr = inStr.trim();
339            if(trimStr.startsWith("+"))
340            {
341                trimStr = trimStr.substring(1);
342            }
343    
344            // parse until we hit the end or invalid char
345            ParsePosition pp = new ParsePosition(0);
346            Number num = dformat.parse(trimStr, pp);
347            if(null == num)
348            {
349                throw new Exception("Invalid Float In TLE");
350            }
351    
352            return num.doubleValue();
353        } // readFloatFromString
354    
355        /** -----------------------------------------------------------------------------
356         *
357         *                           procedure jday
358         *
359         *  this procedure finds the julian date given the year, month, day, and time.
360         *    the julian date is defined by each elapsed day since noon, jan 1, 4713 bc.
361         *
362         *  algorithm     : calculate the answer in one step for efficiency
363         *
364         *  author        : david vallado                  719-573-2600    1 mar 2001
365         *
366         *  inputs          description                    range / units
367         *    year        - year                           1900 .. 2100
368         *    mon         - month                          1 .. 12
369         *    day         - day                            1 .. 28,29,30,31
370         *    hr          - universal time hour            0 .. 23
371         *    min         - universal time min             0 .. 59
372         *    sec         - universal time sec             0.0 .. 59.999
373         *
374         *  outputs       :
375         *    jd          - julian date                    days from 4713 bc
376         *
377         *  locals        :
378         *    none.
379         *
380         *  coupling      :
381         *    none.
382         *
383         *  references    :
384         *    vallado       2007, 189, alg 14, ex 3-14
385         *
386         * ---------------------------------------------------------------------------
387         * @param year
388         * @param mon
389         * @param day
390         * @param hr
391         * @param minute
392         * @param sec
393         * @return
394         */
395        public static double jday(
396                int year, int mon, int day, int hr, int minute, double sec//,
397                //double& jd
398                )
399        {
400            double jd;
401            jd = 367.0 * year -
402                    Math.floor((7 * (year + Math.floor((mon + 9) / 12.0))) * 0.25) +
403                    Math.floor(275 * mon / 9.0) +
404                    day + 1721013.5 +
405                    ((sec / 60.0 + minute) / 60.0 + hr) / 24.0;  // ut in days
406            // - 0.5*sgn(100.0*year + mon - 190002.5) + 0.5;
407    
408            return jd;
409        }  // end jday
410    
411        /* -----------------------------------------------------------------------------
412         *
413         *                           procedure days2mdhms
414         *
415         *  this procedure converts the day of the year, days, to the equivalent month
416         *    day, hour, minute and second.
417         *
418         *
419         *
420         *  algorithm     : set up array for the number of days per month
421         *                  find leap year - use 1900 because 2000 is a leap year
422         *                  loop through a temp value while the value is < the days
423         *                  perform int conversions to the correct day and month
424         *                  convert remainder into h m s using type conversions
425         *
426         *  author        : david vallado                  719-573-2600    1 mar 2001
427         *
428         *  inputs          description                    range / units
429         *    year        - year                           1900 .. 2100
430         *    days        - julian day of the year         0.0  .. 366.0
431         *
432         *  outputs       :
433         *    mon         - month                          1 .. 12
434         *    day         - day                            1 .. 28,29,30,31
435         *    hr          - hour                           0 .. 23
436         *    min         - minute                         0 .. 59
437         *    sec         - second                         0.0 .. 59.999
438         *
439         *  locals        :
440         *    dayofyr     - day of year
441         *    temp        - temporary extended values
442         *    inttemp     - temporary int value
443         *    i           - index
444         *    lmonth[12]  - int array containing the number of days per month
445         *
446         *  coupling      :
447         *    none.
448         * --------------------------------------------------------------------------- */
449    // returns MDHMS object with the mdhms variables
450        public static MDHMS days2mdhms(
451                int year, double days//,
452                //int& mon, int& day, int& hr, int& minute, double& sec
453                )
454        {
455            // return variables
456            //int mon, day, hr, minute, sec
457            MDHMS mdhms = new MDHMS();
458    
459            int i, inttemp, dayofyr;
460            double temp;
461            int lmonth[] =
462            {
463                31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31
464            };
465    
466            dayofyr = (int)Math.floor(days);
467            /* ----------------- find month and day of month ---------------- */
468            if((year % 4) == 0) // doesn't work for dates starting 2100 and beyond
469            {
470                lmonth[1] = 29;
471            }
472    
473            i = 1;
474            inttemp = 0;
475            while((dayofyr > inttemp + lmonth[i - 1]) && (i < 12))
476            {
477                inttemp = inttemp + lmonth[i - 1];
478                i++;
479            }
480            mdhms.mon = i;
481            mdhms.day = dayofyr - inttemp;
482    
483            /* ----------------- find hours minutes and seconds ------------- */
484            temp = (days - dayofyr) * 24.0;
485            mdhms.hr = (int)Math.floor(temp);
486            temp = (temp - mdhms.hr) * 60.0;
487            mdhms.minute = (int)Math.floor(temp);
488            mdhms.sec = (temp - mdhms.minute) * 60.0;
489    
490            return mdhms;
491        }  // end days2mdhms
492    
493        // Month Day Hours Min Sec
494        public static class MDHMS
495        {
496            public int mon = 0;
497            ;
498            public int day = 0;
499            ;
500            public int hr = 0;
501            ;
502            public int minute = 0;
503            ;
504            public double sec = 0;
505        }
506    
507        /** -----------------------------------------------------------------------------
508         *
509         *                           procedure invjday
510         *
511         *  this procedure finds the year, month, day, hour, minute and second
512         *  given the julian date. tu can be ut1, tdt, tdb, etc.
513         *
514         *  algorithm     : set up starting values
515         *                  find leap year - use 1900 because 2000 is a leap year
516         *                  find the elapsed days through the year in a loop
517         *                  call routine to find each individual value
518         *
519         *  author        : david vallado                  719-573-2600    1 mar 2001
520         *
521         *  inputs          description                    range / units
522         *    jd          - julian date                    days from 4713 bc
523         *
524         *  outputs       :
525         *    year        - year                           1900 .. 2100
526         *    mon         - month                          1 .. 12
527         *    day         - day                            1 .. 28,29,30,31
528         *    hr          - hour                           0 .. 23
529         *    min         - minute                         0 .. 59
530         *    sec         - second                         0.0 .. 59.999
531         *
532         *  locals        :
533         *    days        - day of year plus fractional
534         *                  portion of a day               days
535         *    tu          - julian centuries from 0 h
536         *                  jan 0, 1900
537         *    temp        - temporary double values
538         *    leapyrs     - number of leap years from 1900
539         *
540         *  coupling      :
541         *    days2mdhms  - finds month, day, hour, minute and second given days and year
542         *
543         *  references    :
544         *    vallado       2007, 208, alg 22, ex 3-13
545         * ---------------------------------------------------------------------------
546         *
547         * @param jd
548         * @return [year,mon,day,hr,minute,sec]
549         */
550        public static double[] invjday(double jd)
551        {
552            // return vars
553            double year, mon, day, hr, minute, sec;
554    
555            int leapyrs;
556            double days, tu, temp;
557    
558            /* --------------- find year and days of the year --------------- */
559            temp = jd - 2415019.5;
560            tu = temp / 365.25;
561            year = 1900 + (int)Math.floor(tu);
562            leapyrs = (int)Math.floor((year - 1901) * 0.25);
563    
564            // optional nudge by 8.64x10-7 sec to get even outputs
565            days = temp - ((year - 1900) * 365.0 + leapyrs) + 0.00000000001;
566    
567            /* ------------ check for case of beginning of a year ----------- */
568            if(days < 1.0)
569            {
570                year = year - 1;
571                leapyrs = (int)Math.floor((year - 1901) * 0.25);
572                days = temp - ((year - 1900) * 365.0 + leapyrs);
573            }
574    
575            /* ----------------- find remaing data  ------------------------- */
576            //days2mdhms(year, days, mon, day, hr, minute, sec);
577            MDHMS mdhms = days2mdhms((int)year, days);
578            mon = mdhms.mon;
579            day = mdhms.day;
580            hr = mdhms.hr;
581            minute = mdhms.minute;
582            sec = mdhms.sec;
583    
584            sec = sec - 0.00000086400; // ?
585    
586            return new double[]
587                    {
588                        year, mon, day, hr, minute, sec
589                    };
590        }  // end invjday
591    
592        /** -----------------------------------------------------------------------------
593         *
594         *                           function rv2coe
595         *
596         *  this function finds the classical orbital elements given the geocentric
597         *    equatorial position and velocity vectors.
598         *
599         *  author        : david vallado                  719-573-2600   21 jun 2002
600         *
601         *  revisions
602         *    vallado     - fix special cases                              5 sep 2002
603         *    vallado     - delete extra check in inclination code        16 oct 2002
604         *    vallado     - add constant file use                         29 jun 2003
605         *    vallado     - add mu                                         2 apr 2007
606         *
607         *  inputs          description                    range / units
608         *    r           - ijk position vector            km
609         *    v           - ijk velocity vector            km / s
610         *    mu          - gravitational parameter        km3 / s2
611         *
612         *  outputs       :
613         *    p           - semilatus rectum               km
614         *    a           - semimajor axis                 km
615         *    ecc         - eccentricity
616         *    incl        - inclination                    0.0  to pi rad
617         *    omega       - longitude of ascending node    0.0  to 2pi rad
618         *    argp        - argument of perigee            0.0  to 2pi rad
619         *    nu          - true anomaly                   0.0  to 2pi rad
620         *    m           - mean anomaly                   0.0  to 2pi rad
621         *    arglat      - argument of latitude      (ci) 0.0  to 2pi rad
622         *    truelon     - true longitude            (ce) 0.0  to 2pi rad
623         *    lonper      - longitude of periapsis    (ee) 0.0  to 2pi rad
624         *
625         *  locals        :
626         *    hbar        - angular momentum h vector      km2 / s
627         *    ebar        - eccentricity     e vector
628         *    nbar        - line of nodes    n vector
629         *    c1          - v**2 - u/r
630         *    rdotv       - r dot v
631         *    hk          - hk unit vector
632         *    sme         - specfic mechanical energy      km2 / s2
633         *    i           - index
634         *    e           - eccentric, parabolic,
635         *                  hyperbolic anomaly             rad
636         *    temp        - temporary variable
637         *    typeorbit   - type of orbit                  ee, ei, ce, ci
638         *
639         *  coupling      :
640         *    mag         - magnitude of a vector
641         *    cross       - cross product of two vectors
642         *    angle       - find the angle between two vectors
643         *    newtonnu    - find the mean anomaly
644         *
645         *  references    :
646         *    vallado       2007, 126, alg 9, ex 2-5
647         * ---------------------------------------------------------------------------
648         *
649         * @param r
650         * @param v
651         * @param mu
652         * @return [p, a, ecc, incl, omega, argp, nu, m, arglat, truelon, lonper]
653         */
654        public static double[] rv2coe(
655                double[] r, double[] v, double mu//,
656                //       double& p, double& a, double& ecc, double& incl, double& omega, double& argp,
657                //       double& nu, double& m, double& arglat, double& truelon, double& lonper
658                )
659        {
660    
661            // return variables
662            double p, a, ecc, incl, omega, argp, nu, m, arglat, truelon, lonper;
663    
664            // internal
665    
666            double undefined, small, magr, magv, magn, sme,
667                    rdotv, infinite, temp, c1, hk, twopi, magh, halfpi, e;
668            double[] hbar = new double[3];
669            double[] nbar = new double[3];
670            double[] ebar = new double[3];
671    
672            int i;
673            String typeorbit;
674    
675            twopi = 2.0 * Math.PI;
676            halfpi = 0.5 * Math.PI;
677            small = 0.00000001;
678            undefined = 999999.1;
679            infinite = 999999.9;
680    
681            // SEG m needs to be ini
682            m = undefined;
683    
684            // -------------------------  implementation   -----------------
685            magr = mag(r);
686            magv = mag(v);
687    
688            // ------------------  find h n and e vectors   ----------------
689            cross(r, v, hbar);
690            magh = mag(hbar);
691            if(magh > small)
692            {
693                nbar[0] = -hbar[1];
694                nbar[1] = hbar[0];
695                nbar[2] = 0.0;
696                magn = mag(nbar);
697                c1 = magv * magv - mu / magr;
698                rdotv = dot(r, v);
699                for(i = 0; i <= 2; i++)
700                {
701                    ebar[i] = (c1 * r[i] - rdotv * v[i]) / mu;
702                }
703                ecc = mag(ebar);
704    
705                // ------------  find a e and semi-latus rectum   ----------
706                sme = (magv * magv * 0.5) - (mu / magr);
707                if(Math.abs(sme) > small)
708                {
709                    a = -mu / (2.0 * sme);
710                }
711                else
712                {
713                    a = infinite;
714                }
715                p = magh * magh / mu;
716    
717                // -----------------  find inclination   -------------------
718                hk = hbar[2] / magh;
719                incl = Math.acos(hk);
720    
721                // --------  determine type of orbit for later use  --------
722                // ------ elliptical, parabolic, hyperbolic inclined -------
723                typeorbit = "ei";
724                if(ecc < small)
725                {
726                    // ----------------  circular equatorial ---------------
727                    if((incl < small) | (Math.abs(incl - Math.PI) < small))
728                    {
729                        typeorbit = "ce";
730                    }
731                    else
732                    // --------------  circular inclined ---------------
733                    {
734                        typeorbit = "ci";
735                    }
736                }
737                else
738                {
739                    // - elliptical, parabolic, hyperbolic equatorial --
740                    if((incl < small) | (Math.abs(incl - Math.PI) < small))
741                    {
742                        typeorbit = "ee";
743                    }
744                }
745    
746                // ----------  find longitude of ascending node ------------
747                if(magn > small)
748                {
749                    temp = nbar[0] / magn;
750                    if(Math.abs(temp) > 1.0)
751                    {
752                        temp = Math.signum(temp);
753                    }
754                    omega = Math.acos(temp);
755                    if(nbar[1] < 0.0)
756                    {
757                        omega = twopi - omega;
758                    }
759                }
760                else
761                {
762                    omega = undefined;
763                }
764    
765                // ---------------- find argument of perigee ---------------
766                if(typeorbit.equalsIgnoreCase("ei") == true) // 0 = true for cpp strcmp
767                {
768                    argp = angle(nbar, ebar);
769                    if(ebar[2] < 0.0)
770                    {
771                        argp = twopi - argp;
772                    }
773                }
774                else
775                {
776                    argp = undefined;
777                }
778    
779                // ------------  find true anomaly at epoch    -------------
780                if(typeorbit.startsWith("e"))//typeorbit[0] == 'e' )
781                {
782                    nu = angle(ebar, r);
783                    if(rdotv < 0.0)
784                    {
785                        nu = twopi - nu;
786                    }
787                }
788                else
789                {
790                    nu = undefined;
791                }
792    
793                // ----  find argument of latitude - circular inclined -----
794                if(typeorbit.equalsIgnoreCase("ci") == true)
795                {
796                    arglat = angle(nbar, r);
797                    if(r[2] < 0.0)
798                    {
799                        arglat = twopi - arglat;
800                    }
801                    m = arglat;
802                }
803                else
804                {
805                    arglat = undefined;
806                }
807    
808                // -- find longitude of perigee - elliptical equatorial ----
809                if((ecc > small) && (typeorbit.equalsIgnoreCase("ee") == true))
810                {
811                    temp = ebar[0] / ecc;
812                    if(Math.abs(temp) > 1.0)
813                    {
814                        temp = Math.signum(temp);
815                    }
816                    lonper = Math.acos(temp);
817                    if(ebar[1] < 0.0)
818                    {
819                        lonper = twopi - lonper;
820                    }
821                    if(incl > halfpi)
822                    {
823                        lonper = twopi - lonper;
824                    }
825                }
826                else
827                {
828                    lonper = undefined;
829                }
830    
831                // -------- find true longitude - circular equatorial ------
832                if((magr > small) && (typeorbit.equalsIgnoreCase("ce") == true))
833                {
834                    temp = r[0] / magr;
835                    if(Math.abs(temp) > 1.0)
836                    {
837                        temp = Math.signum(temp);
838                    }
839                    truelon = Math.acos(temp);
840                    if(r[1] < 0.0)
841                    {
842                        truelon = twopi - truelon;
843                    }
844                    if(incl > halfpi)
845                    {
846                        truelon = twopi - truelon;
847                    }
848                    m = truelon;
849                }
850                else
851                {
852                    truelon = undefined;
853                }
854    
855                // ------------ find mean anomaly for all orbits -----------
856                if(typeorbit.startsWith("e"))//typeorbit[0] == 'e' )
857                {
858                    double[] tt = newtonnu(ecc, nu);
859                    e = tt[0];
860                    m = tt[1];
861                }
862            }
863            else
864            {
865                p = undefined;
866                a = undefined;
867                ecc = undefined;
868                incl = undefined;
869                omega = undefined;
870                argp = undefined;
871                nu = undefined;
872                m = undefined;
873                arglat = undefined;
874                truelon = undefined;
875                lonper = undefined;
876            }
877    
878            return new double[]
879                    {
880                        p, a, ecc, incl, omega, argp, nu, m, arglat, truelon, lonper
881                    };
882        }  // end rv2coe
883    
884        /** -----------------------------------------------------------------------------
885         *
886         *                           function newtonnu
887         *
888         *  this function solves keplers equation when the true anomaly is known.
889         *    the mean and eccentric, parabolic, or hyperbolic anomaly is also found.
890         *    the parabolic limit at 168 is arbitrary. the hyperbolic anomaly is also
891         *    limited. the hyperbolic sine is used because it's not double valued.
892         *
893         *  author        : david vallado                  719-573-2600   27 may 2002
894         *
895         *  revisions
896         *    vallado     - fix small                                     24 sep 2002
897         *
898         *  inputs          description                    range / units
899         *    ecc         - eccentricity                   0.0  to
900         *    nu          - true anomaly                   -2pi to 2pi rad
901         *
902         *  outputs       :
903         *    e0          - eccentric anomaly              0.0  to 2pi rad       153.02 
904         *    m           - mean anomaly                   0.0  to 2pi rad       151.7425 
905         *
906         *  locals        :
907         *    e1          - eccentric anomaly, next value  rad
908         *    sine        - sine of e
909         *    cose        - cosine of e
910         *    ktr         - index
911         *
912         *  coupling      :
913         *    asinh       - arc hyperbolic sine
914         *
915         *  references    :
916         *    vallado       2007, 85, alg 5
917         * ---------------------------------------------------------------------------
918         *
919         * @param ecc
920         * @param nu
921         * @return [e0, m]
922         */
923        public static double[] newtonnu(double ecc, double nu)
924        {
925            // return vars
926            double e0, m;
927    
928            // internal
929    
930            double small, sine, cose;
931    
932            // ---------------------  implementation   ---------------------
933            e0 = 999999.9;
934            m = 999999.9;
935            small = 0.00000001;
936    
937            // --------------------------- circular ------------------------
938            if(Math.abs(ecc) < small)
939            {
940                m = nu;
941                e0 = nu;
942            }
943            else // ---------------------- elliptical -----------------------
944            if(ecc < 1.0 - small)
945            {
946                sine = (Math.sqrt(1.0 - ecc * ecc) * Math.sin(nu)) / (1.0 + ecc * Math.cos(nu));
947                cose = (ecc + Math.cos(nu)) / (1.0 + ecc * Math.cos(nu));
948                e0 = Math.atan2(sine, cose);
949                m = e0 - ecc * Math.sin(e0);
950            }
951            else // -------------------- hyperbolic  --------------------
952            if(ecc > 1.0 + small)
953            {
954                if((ecc > 1.0) && (Math.abs(nu) + 0.00001 < Math.PI - Math.acos(1.0 / ecc)))
955                {
956                    sine = (Math.sqrt(ecc * ecc - 1.0) * Math.sin(nu)) / (1.0 + ecc * Math.cos(nu));
957                    e0 = asinh(sine);
958                    m = ecc * Math.sinh(e0) - e0;
959                }
960            }
961            else // ----------------- parabolic ---------------------
962            if(Math.abs(nu) < 168.0 * Math.PI / 180.0)
963            {
964                e0 = Math.tan(nu * 0.5);
965                m = e0 + (e0 * e0 * e0) / 3.0;
966            }
967    
968            if(ecc < 1.0)
969            {
970                m = (m % (2.0 * Math.PI));
971                if(m < 0.0)
972                {
973                    m = m + 2.0 * Math.PI;
974                }
975                e0 = e0 % (2.0 * Math.PI);
976            }
977    
978            return new double[]
979                    {
980                        e0, m
981                    };
982        }  // end newtonnu
983    
984    
985        /* -----------------------------------------------------------------------------
986         *
987         *                           function asinh
988         *
989         *  this function evaluates the inverse hyperbolic sine function.
990         *
991         *  author        : david vallado                  719-573-2600    1 mar 2001
992         *
993         *  inputs          description                    range / units
994         *    xval        - angle value                                  any real
995         *
996         *  outputs       :
997         *    arcsinh     - result                                       any real
998         *
999         *  locals        :
1000         *    none.
1001         *
1002         *  coupling      :
1003         *    none.
1004         *
1005         * --------------------------------------------------------------------------- */
1006        public static double asinh(double xval)
1007        {
1008            return Math.log(xval + Math.sqrt(xval * xval + 1.0));
1009        }  // end asinh
1010    
1011        /* -----------------------------------------------------------------------------
1012         *
1013         *                           function mag
1014         *
1015         *  this procedure finds the magnitude of a vector.  the tolerance is set to
1016         *    0.000001, thus the 1.0e-12 for the squared test of underflows.
1017         *
1018         *  author        : david vallado                  719-573-2600    1 mar 2001
1019         *
1020         *  inputs          description                    range / units
1021         *    vec         - vector
1022         *
1023         *  outputs       :
1024         *    vec         - answer stored in fourth component
1025         *
1026         *  locals        :
1027         *    none.
1028         *
1029         *  coupling      :
1030         *    none.
1031         * --------------------------------------------------------------------------- */
1032        public static double mag(double[] x)
1033        {
1034            return Math.sqrt(x[0] * x[0] + x[1] * x[1] + x[2] * x[2]);
1035        }  // end mag
1036    
1037        /* -----------------------------------------------------------------------------
1038         *
1039         *                           procedure cross
1040         *
1041         *  this procedure crosses two vectors.
1042         *
1043         *  author        : david vallado                  719-573-2600    1 mar 2001
1044         *
1045         *  inputs          description                    range / units
1046         *    vec1        - vector number 1
1047         *    vec2        - vector number 2
1048         *
1049         *  outputs       :
1050         *    outvec      - vector result of a x b
1051         *
1052         *  locals        :
1053         *    none.
1054         *
1055         *  coupling      :
1056         *    mag           magnitude of a vector
1057        ---------------------------------------------------------------------------- */
1058        public static void cross(double[] vec1, double[] vec2, double[] outvec)
1059        {
1060            outvec[0] = vec1[1] * vec2[2] - vec1[2] * vec2[1];
1061            outvec[1] = vec1[2] * vec2[0] - vec1[0] * vec2[2];
1062            outvec[2] = vec1[0] * vec2[1] - vec1[1] * vec2[0];
1063        }  // end cross
1064    
1065        /* -----------------------------------------------------------------------------
1066         *
1067         *                           function dot
1068         *
1069         *  this function finds the dot product of two vectors.
1070         *
1071         *  author        : david vallado                  719-573-2600    1 mar 2001
1072         *
1073         *  inputs          description                    range / units
1074         *    vec1        - vector number 1
1075         *    vec2        - vector number 2
1076         *
1077         *  outputs       :
1078         *    dot         - result
1079         *
1080         *  locals        :
1081         *    none.
1082         *
1083         *  coupling      :
1084         *    none.
1085         *
1086         * --------------------------------------------------------------------------- */
1087        public static double dot(double[] x, double[] y)
1088        {
1089            return (x[0] * y[0] + x[1] * y[1] + x[2] * y[2]);
1090        }  // end dot
1091    
1092        /* -----------------------------------------------------------------------------
1093         *
1094         *                           procedure angle
1095         *
1096         *  this procedure calculates the angle between two vectors.  the output is
1097         *    set to 999999.1 to indicate an undefined value.  be sure to check for
1098         *    this at the output phase.
1099         *
1100         *  author        : david vallado                  719-573-2600    1 mar 2001
1101         *
1102         *  inputs          description                    range / units
1103         *    vec1        - vector number 1
1104         *    vec2        - vector number 2
1105         *
1106         *  outputs       :
1107         *    theta       - angle between the two vectors  -pi to pi
1108         *
1109         *  locals        :
1110         *    temp        - temporary real variable
1111         *
1112         *  coupling      :
1113         *    dot           dot product of two vectors
1114         * --------------------------------------------------------------------------- */
1115        public static double angle(double[] vec1, double[] vec2)
1116        {
1117            double small, undefined, magv1, magv2, temp;
1118            small = 0.00000001;
1119            undefined = 999999.1;
1120    
1121            magv1 = mag(vec1);
1122            magv2 = mag(vec2);
1123    
1124            if(magv1 * magv2 > small * small)
1125            {
1126                temp = dot(vec1, vec2) / (magv1 * magv2);
1127                if(Math.abs(temp) > 1.0)
1128                {
1129                    temp = Math.signum(temp) * 1.0;
1130                }
1131                return Math.acos(temp);
1132            }
1133            else
1134            {
1135                return undefined;
1136            }
1137        }  // end angle
1138    }