001/*
002 * $Id: SGP4utils.java,v 1.2 2011/03/24 16:06:33 davep Exp $
003 *
004 * This file is part of McIDAS-V
005 *
006 * Copyright 2007-2011
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
061package edu.wisc.ssec.mcidasv.data.adde.sgp4;
062
063import java.text.DecimalFormat;
064import java.text.DecimalFormatSymbols;
065import java.text.ParsePosition;
066
067/**
068 * 19 June 2009
069 * @author Shawn E. Gano, shawn@gano.name
070 */
071public 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}