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