content: package sgp4;
  
  import java.util.Calendar;
  import java.util.Date;
  import java.util.GregorianCalendar;
  
  /**
   * This class knows how to parse two line element sets and access the individual elements.
   * 
   * @author aholinch
   *
   */
  public class TLE 
  {
      protected ElsetRec rec = null;
      
      protected String line1 = null;
      protected String line2 = null;
      
      protected String intlid = null;
      protected String objectID = null; // alpha-5
      protected Date epoch = null;
      protected double ndot = 0;
      protected double nddot = 0;
      protected double bstar = 0;
      protected int elnum = 0;
      protected double incDeg = 0;
      protected double raanDeg = 0;
      protected double ecc = 0;
      protected double argpDeg = 0;
      protected double maDeg = 0;
      protected double n = 0;
      protected int revnum = 0;
      
      protected String parseErrors = null;
      
      protected int sgp4Error = 0;
      
      public TLE()
      {
      	
      }
      
      public TLE(String line1, String line2)
      {
      	parseLines(line1,line2);
      }
  
      public double[][] getRV(Date d)
      {
      	double t = d.getTime();
      	t -= epoch.getTime();
      	t/=60000;
      	
      	return getRV(t);
      }
  
      public double[][] getRV(double minutesAfterEpoch)
      {
      	double r[] = new double[3];
      	double v[] = new double[3];
      	
      	rec.error = 0;
      	SGP4.sgp4(rec, minutesAfterEpoch, r, v);
      	sgp4Error = rec.error;
      	double out[][] = {r,v};
      	return out;
      }
      
      public int getSgp4Error()
      {
      	return sgp4Error;
      }
      
      public ElsetRec getElsetRec()
      {
      	return rec;
      }
      
      public void setElsetRec(ElsetRec er)
      {
      	rec = er;
      }
      
      public String getParseErrors()
      {
      	return parseErrors;
      }
      
      public String getIntlID()
      {
      	return intlid;
      }
      
      public void setIntlID(String id)
      {
      	intlid = id;
      }
      
      public String getObjectID()
      {
      	return objectID;
      }
      
      public void setObjectID(String id)
      {
      	objectID = id;
      }
      
      public Date getEpoch()
      {
      	return epoch;
      }
      
      public void setEpoch(Date d)
      {
      	epoch = d;
      }
      
      public double getNDot()
      {
      	return ndot;
      }
      
      public void setNDot(double val)
      {
      	ndot = val;
      }
      
      public double getNDDot()
      {
      	return nddot;
      }
      
      public void setNDDot(double val)
      {
      	nddot = val;
      }
      
      public double getBstar()
      {
      	return bstar;
      }
      
      public void setBstar(double val)
      {
      	bstar = val;
      }
      
      public int getElNum()
      {
      	return elnum;
      }
      
      public void setElNum(int num)
      {
      	elnum = num;
      }
      
      public double getIncDeg()
      {
      	return incDeg;
      }
      
      public void setIncDeg(double val)
      {
      	incDeg = val;
      }
      
      public double getRaanDeg()
      {
      	return raanDeg;
      }
      
      public void setRaanDeg(double val)
      {
      	raanDeg = val;
      }
      
      public double getEcc()
      {
      	return ecc;
      }
      
      public void setEcc(double val)
      {
      	ecc = val;
      }
      
      public double getArgpDeg()
      {
      	return argpDeg;
      }
      
      public void setArgpDeg(double val)
      {
      	argpDeg = val;
      }
      
      public double getMaDeg()
      {
      	return maDeg;
      }
      
      public void setMaDeg(double val)
      {
      	maDeg = val;
      }
      
      public double getN()
      {
      	return n;
      }
      
      public void setN(double val)
      {
      	n = val;
      }
      
      public int getRevNum()
      {
      	return revnum;
      }
      
      public void setRevNum(int val)
      {
      	revnum = val;
      }
      
      public String getLine1()
      {
      	return line1;
      }
      
      public String getLine2()
      {
      	return line2;
      }
      
      /**
       * Parses the two lines optimistically.  No exceptions are thrown but some parse errors will
       * be accumulated as a string.  Call getParseErrors() to see if there are any.
       * 
       * @param line1
       * @param line2
       */
      public void parseLines(String line1, String line2)
      {
      	parseErrors = null;
      	rec = new ElsetRec();
  
      	this.line1 = line1;
      	this.line2 = line2;
      	
      	if(line1 == null || line1.trim().length()<68) // we can live without checksum
      	{
      		addParseError("line1 too short");
      	}
      	
      	if(line2 == null || line2.trim().length()<68) // we can live without checksum
      	{
      		addParseError("line2 too short");
      	}
      	
      	if(parseErrors != null) return;
      	
  
      	
      	objectID = line1.substring(2,7).trim();
      	if(!objectID.equals(line2.substring(2,7).trim())) addParseError("ids don't match");
  
          while(objectID.startsWith("0")) objectID = objectID.substring(1);
      	
      	rec.classification = line1.charAt(7);
  	       //          1         2         3         4         5         6
             //0123456789012345678901234567890123456789012345678901234567890123456789
  	//line1="1 00005U 58002B   00179.78495062  .00000023  00000-0  28098-4 0  4753";
  	//line2="2 00005  34.2682 348.7242 1859667 331.7664  19.3264 10.82419157413667";
  
  	    intlid = line1.substring(9, 17).trim();
      	epoch = parseEpoch(line1.substring(18,32).trim());
      	ndot = gdi(line1.charAt(33),line1,35,44);
      	nddot = gdi(line1.charAt(44),line1,45,50);
      	nddot *= Math.pow(10.0, gd(line1,50,52));
      	bstar = gdi(line1.charAt(53),line1,54,59);
      	bstar *= Math.pow(10.0d, gd(line1,59,61));
      	
      	elnum = (int)gd(line1,64,68);
      	
      	incDeg = gd(line2,8,16);
      	raanDeg = gd(line2,17,25);
      	ecc = gdi('+',line2,26,33);
      	argpDeg = gd(line2,34,42);
      	maDeg = gd(line2,43,51);
      	
      	n = gd(line2,52,63);
      	
      	revnum = (int)gd(line2,63,68);
      	
      	setValsToRec();
      }
      
      /**
       * Set the values to the ElsetRec object.
       */
      protected void setValsToRec()
      {
  		final double deg2rad = Math.PI / 180.0;          //   0.0174532925199433
  		final double xpdotp = 1440.0 / (2.0 * Math.PI);  // 229.1831180523293
  
  		rec.elnum = elnum;
  		rec.revnum = revnum;
  		rec.satID = objectID;
      	rec.bstar = bstar;
      	rec.inclo = incDeg*deg2rad;
      	rec.nodeo = raanDeg*deg2rad;
      	rec.argpo = argpDeg*deg2rad;
      	rec.mo = maDeg*deg2rad;
      	rec.ecco = ecc;
  		rec.no_kozai = n/xpdotp;
  		rec.ndot = ndot / (xpdotp*1440.0d);
  		rec.nddot = nddot / (xpdotp*1440.0d*1440.0d);
  		
  		SGP4.sgp4init('a', rec);
      }
      
      /**
       * Parse the tle epoch format to a date.  
       * 
       * @param str
       * @return
       */
      protected Date parseEpoch(String str)
      {
      	java.util.TimeZone.setDefault(java.util.TimeZone.getTimeZone("GMT"));
      	int year = Integer.parseInt(str.substring(0,2).trim());
      	
      	rec.epochyr=year;
      	if(year > 56)
      	{
      		year += 1900;
      	}
      	else
      	{
      		year += 2000;
      	}
      	
      	int doy = Integer.parseInt(str.substring(2,5).trim());
      	double dfrac = Double.parseDouble("0"+str.substring(5).trim());
      	
      	rec.epochdays = doy;
      	rec.epochdays += dfrac;
      	
      	
      	dfrac *= 24.0d;
      	int hr = (int)dfrac;
      	dfrac = 60.0d*(dfrac - hr);
      	int mn = (int)dfrac;
      	dfrac = 60.0d*(dfrac - mn);
      	int sc = (int)dfrac;
      	
  
      	
      	dfrac = 1000.d*(dfrac-sc);
      	int milli = (int)dfrac;
      	
      	GregorianCalendar gc = new GregorianCalendar();
      	
      	gc.set(Calendar.YEAR, year);
      	gc.set(Calendar.DAY_OF_YEAR, doy);
      	gc.set(Calendar.HOUR_OF_DAY, hr);
      	gc.set(Calendar.MINUTE, mn);
      	gc.set(Calendar.SECOND, sc);
      	gc.set(Calendar.MILLISECOND, milli);
  
      	double sec = ((double)sc)+dfrac/1000.0d;
      	int mon = gc.get(Calendar.MONTH)+1;
      	int day = gc.get(Calendar.DAY_OF_MONTH);
      	double jd[] = SGP4.jday(year, mon, day, hr, mn, sec);
      	rec.jdsatepoch = jd[0];
      	rec.jdsatepochF = jd[1];
  
      	return new java.sql.Timestamp(gc.getTimeInMillis());
      }
      
      protected void addParseError(String err)
      {
      	if(parseErrors == null || parseErrors.trim().length() == 0)
      	{
      		parseErrors = err;
      	}
      	else
      	{
      		parseErrors = parseErrors+"; "+err;
      	}
      }
      
      /**
       * parse a double from the substring.
       * 
       * @param str
       * @param start
       * @param end
       * @return
       */
      protected double gd(String str, int start, int end)
      {
      	return gd(str,start,end,0);
      }
      
      /**
       * parse a double from the substring.
       * 
       * @param str
       * @param start
       * @param end
       * @param defVal
       * @return
       */
      protected double gd(String str, int start, int end, double defVal)
      {
      	double num = defVal;
      	try{num = Double.parseDouble(str.substring(start,end).trim());}catch(Exception ex){}
      	return num;
      }
      
      /**
       * parse a double from the substring after adding an implied decimal point.
       * 
       * @param str
       * @param start
       * @param end
       * @return
       */
      protected double gdi(char sign, String str, int start, int end)
      {
      	return gdi(sign,str,start,end,0);
      }
      
      /**
       * parse a double from the substring after adding an implied decimal point.
       * 
       * @param str
       * @param start
       * @param end
       * @param defVal
       * @return
       */
      protected double gdi(char sign, String str, int start, int end, double defVal)
      {
      	double num = defVal;
      	try{num = Double.parseDouble("0."+str.substring(start,end).trim());}catch(Exception ex){}
      	if(sign == '-') num *= -1.0d;
      	return num;
      }
  
  }
  