JEphem Informatic Trail build classes source code BuildTestPluto99.java
//*********************************************************************************
// class BuildTestPluto99 // default package
// Software released under the General Public License (version 2 or later), available at 
// http://www.gnu.org/copyleft/gpl.html
//*********************************************************************************

import java.io.*;
import java.text.*;
import jephem.astro.planets.Pluto99;
import jephem.astro.SolarSystemConstants;
import jephem.astro.Body;
/******************************************************************
Contains methods used to build jephem.astro.planets.Pluto99.java.
<BR>This class is NOT part of 'jephem' package, and can be useful to developpers who want to transform
BDL Pluto99 file.
<BR>See a description of its use in the <A HREF="../../../ephemeris/et400pluto99_en.htm" TARGET="_top">page about Pluto99</A> of JEphem site.
<BR><CODE>BuildTestPluto99</CODE> needs to have in the current directory the file <CODE>plutoxyz.dat</CODE> which can be found at <A HREF="ftp://ftp.bdl.fr/pub/polac/solarsys/pluto">ftp://ftp.bdl.fr/pub/polac/solarsys/pluto</A>.

@author Thierry Graff
@history Aug 12 2001 : Creation
@history Dec 21 2001 : Put Phi in radians - tested velocities
*****************************************************************/
public class BuildTestPluto99{

  //=================================================================================
  //                                 PUBLIC CONSTANTS
  //=================================================================================
	/** Parameter to pass to <CODE>main()</CODE> to call <CODE>buildPluto99()</CODE> - value = "build". */
	public static final String PARAM_BUILD = "build"; 
	/** Parameter to pass to <CODE>main()</CODE> to call <CODE>testPositions()</CODE> - value = "testPositions". */
	public static final String PARAM_TEST_POSITIONS = "testPositions"; 
	/** Parameter to pass to <CODE>main()</CODE> to call <CODE>calcGeoPrec()</CODE> - value = "calcGeoPrec". */
	public static final String PARAM_CALC_GEO_PREC = "calcGeoPrec";
	/** Parameter to pass to <CODE>main()</CODE> to call <CODE>testVelocities()</CODE> - value = "testVelocities". */
	public static final String PARAM_TEST_VELOCITIES = "testVelocities"; 
	/** Parameter to pass to <CODE>buildPluto99() to generate term Phi in degrees. - value = "degrees". */
	public static final String PARAM_DEGREES = "degrees";
	/** Parameter to pass to <CODE>buildPluto99() to generate term Phi in radians. - value = "radians". */
	public static final String PARAM_RADIANS = "radians";

  //=================================================================================
  //                                 PRIVATE CONSTANTS
  //=================================================================================

  private final static String InputFileName = "plutoxyz.dat.txt"; // added '.txt' for convenience under windows

	/** Line separator */
  private final static String LS = System.getProperty("line.separator");

  /** Number of coordinates; */
  private final static int NB_COORD = 3;
  /** Number of orders; */
  private final static int NB_ORDERS = 3;
  /** Names of coordinates; */
  private final static String[] COORDNAMES = {"X", "Y", "Z"};

  /** Number of lines contained by the header. */
  private final static int NB_LINES_IN_HEADER = 12;
  /** Number of lines at the beginning of a coordinate, before data line.  */
  private final static int NB_LINES_BEFORE_ORDER0 = 8;
  /** Number of lines contained by the header. */
  private final static int NB_LINES_BETWEEN_ORDERS = 4;

  /** String of one blank character. */
  private final static String BLANK = " ";

  /** Number of Poisson terms */
	private static final int[][] nbTerms = {
    {193, 26, 13}, // Poisson terms for X, order 0, 1, 2
    {183, 29, 14}, // Poisson terms for Y, order 0, 1, 2
    {170, 20, 11}  // Poisson terms for Z, order 0, 1, 2
  }; // total 659 terms
  
  private final static int NB_TESTS = 11;
  
	/** To format radian values of Phi. */
	private static final DecimalFormat NF = new DecimalFormat();
	static{
    NF.setMaximumFractionDigits(7);
    NF.setMinimumFractionDigits(7);
		DecimalFormatSymbols dfs = new DecimalFormatSymbols();
		dfs.setDecimalSeparator('.');
		NF.setDecimalFormatSymbols(dfs);
	};
	
/*  
  private static final NumberFormat NF = NumberFormat.getNumberInstance();
	static{
    NF.setMaximumFractionDigits(7);
    NF.setMinimumFractionDigits(7);
	};
*/

  //=================================================================================
  //                                 METHODS
  //=================================================================================

	//**************** main ************************
	/** Dispatches the call to different methods, depending on first argument.
	@param args The first argument must be one of ARG_XXX constants ; indicates which method will be
  called. The following arguments are passed to the called method.
	*/
  public static void main(String[] args){
		String paramList = "'" + PARAM_BUILD + "' or '" + PARAM_TEST_POSITIONS + "' or '" + PARAM_CALC_GEO_PREC + "'";
		if (args.length == 0){
			System.out.println("Call to main must have at least one parameter :");
			System.out.println(paramList);
			System.exit(0);
		}

		if (args[0].compareToIgnoreCase(PARAM_BUILD) == 0){
			if (args.length == 2)
				buildPluto99(args[1]);
			else{
				System.out.println("buildPluto99() needs 1 argument - so call main with 2 parameters");
				System.exit(0);
			}
		}

		else if (args[0].compareToIgnoreCase(PARAM_TEST_POSITIONS) == 0)
			testPositions();
			
		else if (args[0].compareToIgnoreCase(PARAM_TEST_VELOCITIES) == 0)
			testVelocities();
			
		else if (args[0].compareToIgnoreCase(PARAM_CALC_GEO_PREC) == 0)
			calcGeoPrec();

		else{
			System.out.println("First parameter must be :");
			System.out.println(paramList);
		}
		
  }// end main

	//**************** buildPluto99() ************************
	/** Method used to transform 'plutoxyz.dat' to a java array.
  <BR>Only terms 'Ampli', 'Nu' and 'Phi' are used.
  <BR>It generates a file 'result.txt' which contains a java formatted array containing the terms of Pluto99.
	@param strUnit : can be "radians" or "degrees", depending on the unit you want phi to be expressed. 
  */
  public static void buildPluto99(String strUnit){
		try{

			int DEGREES = 0;
			int RADIANS = 1;
			int unit = -1;
			// parameter parsing
			System.out.println(strUnit);
			if (strUnit.compareToIgnoreCase(PARAM_DEGREES) == 0)
				unit = DEGREES;
			else if (strUnit.compareToIgnoreCase(PARAM_RADIANS) == 0)
				unit = RADIANS;
			else{
				System.out.println("Argument 'strUnit must be either " + PARAM_RADIANS + " or " + PARAM_DEGREES);
				System.exit(0);
			}
  		FileOutputStream fos = new FileOutputStream(new File("result.txt"));
      LineNumberReader lnr = new LineNumberReader(new FileReader(new File(InputFileName)));
      
      // Declaration of variables
      String strRes = new String(); // result String
      String line = new String(); // current line read
			// for radian conversion
			String strTmp;
			double phi;
			
      int i; // used several times
      int iCoord, iOrder; // indexes of current coordinate and current order.
      int spaceIdx; // used for line parsing
      
      // 1 - Write declaration of the array
      strRes += "  // Array containing the terms for summation" + LS;
      strRes += "  private static final double[][][][] terms = {" + LS;
      
      // 2 - Skip header of plutoxyz.dat.
      for (i = 0; i < NB_LINES_IN_HEADER; i++) line = lnr.readLine();
      
      // 3 - parse for each coord.
      for (iCoord = 0; iCoord < NB_COORD; iCoord ++){
        System.out.println("Parsing coord " + COORDNAMES[iCoord]); 
        // 3.1 - Skip secular terms and header for order 0.
        for (i = 0; i < NB_LINES_BEFORE_ORDER0; i++)
          line = lnr.readLine();

        strRes += "    { // Poisson terms for " + COORDNAMES[iCoord] + LS;
        
        // 3.2 - parse for each order.
        for (iOrder = 0; iOrder < NB_ORDERS; iOrder++){
          System.out.println("   order " + iOrder); 
          // 3.2.1 write header for this order
          strRes += "      { // " + COORDNAMES[iCoord] + ", order " + iOrder + " - "
                 + nbTerms[iCoord][iOrder] + " terms" + LS; 
          // 3.2.2 parse data line ans write in strRes
          for (i = 0; i < nbTerms[iCoord][iOrder]; i++){
            strRes += "        { "; 
            line = lnr.readLine().trim();
            // parse Ampli
            spaceIdx = line.indexOf(BLANK);
            strRes += line.substring(0, spaceIdx) + ", ";
            // parse Nu
            line = line.substring(spaceIdx).trim();
            spaceIdx = line.indexOf(BLANK);
            strRes += line.substring(0, spaceIdx) + ", ";
            // parse phi and generate either degrees or radians
            line = line.substring(spaceIdx).trim();
            spaceIdx = line.indexOf(BLANK);
						if (unit == DEGREES)
            	strRes += line.substring(0, spaceIdx) + " }";
						else{ // radians
            	strTmp = line.substring(0, spaceIdx);
							phi = Math.toRadians(Double.parseDouble(strTmp));
							strRes += NF.format(phi) + " }";
						}
            
            // write end of array
            if (!(iOrder == NB_ORDERS -1 && iCoord == NB_COORD -1 && i == nbTerms[iCoord][iOrder] - 1))
              strRes += ",";
            strRes += LS;
          }// end for (i = 0; i < nbTerms[iCoord][iOrder]; i++)
          
          // 3.3.3 Close the array for this order
          strRes += "      }";
          if (iOrder < NB_ORDERS -1)
            strRes +=  ",";
          strRes += LS;
        
          // 3.3 - Skip terms between orders only for order 0 and 1 (end of order 2 = end of coord).
          if (iOrder < NB_ORDERS - 1)
            for (i = 0; i < NB_LINES_BETWEEN_ORDERS; i++) line = lnr.readLine();
        
    			fos.write(strRes.getBytes());
          strRes = "";
        }// end for (iOrder = 0; iOrder < NB_ORDERS; iOrder ++)
      
        // Close the array for this coordinate
        strRes += "    }";
        if (iCoord < NB_COORD -1)
          strRes +=  ",";
        strRes += LS;
        
        // skip supplementary blank line
        line = lnr.readLine();
      }// end for (iCoord = 0; iCoord < NB_COORD; iCoord ++)

      // 4 - Close the general array
      strRes += "  }; // end terms[][][]" + LS;

			fos.write(strRes.getBytes());
 			fos.close();

		}
		catch(Exception e){
			System.out.println("Exception" + e.toString());
		}
   
  }// end buildPluto99()

  
  
	//**************** testPositions() ************************
	/** Method used to test first version of class Pluto99 (with phi in degrees);
	values taken from <B>NOTICE</B>. */
  public static void testPositions(){
  
    double jd[] = { 807932.5,  // -2500 Jan 01
                    990557.5,  // -2000 Jan 01
                    1173182.5, // -1500 Jan 01
                    1355807.5, // -1000 Jan 01
                    1538432.5, // -500 Jan 01
                    1721057.5, // 0 Jan 01 
                    1903682.5, // 500 Jan 01
                    2086307.5, // 1000 Jan 01
                    2268932.5, // 1500 Jan 01
                    2451557.5, // 2000 Jan 14
                    2634182.5  // 2500 Jan 17
    }; // 11 dates
    double[][] coord = {
      { -24.450898,  27.864841, 4.020151 },
      { -28.090595,  20.600908, 5.869147 },
      { -30.166984,  12.762686, 7.327759 },
      { -30.612416,   4.703150, 8.334771 },
      { -29.447885,  -3.154766, 8.849075 },
      { -26.904668, -10.265583, 8.881072 },
      { -23.346605, -16.332474, 8.505363 },
      { -19.112797, -21.288876, 7.813304 },
      { -14.504729, -25.154226, 6.893533 },
      { -9.837480,  -27.977994, 5.841542 },
      { -5.418324,  -29.873589, 4.758520 }
     };
  
    int i;
    double tmp0, tmp1, tmp2;
    double[] res = {0.0, 0.0, 0.0, 0.0, 0.0, 0.0}; // contains the results
		Body b = new Body (SolarSystemConstants.PLUTO);
    for (i = 0; i< NB_TESTS; i++){
      Pluto99.calcCoord(jd[i], b, false);
      //System.out.println("jd " + jd[i] + " : " + b.getX1() + " " + b.getX2() + " " + b.getX3());
      tmp0 = b.getX1() - coord[i][0];
      tmp1 = b.getX2() - coord[i][1];
      tmp2 = b.getX3() - coord[i][2];
      System.out.println("jd " + jd[i] + " : " + tmp0 + " " + tmp1 + " " + tmp2);
    }
  }// end testPositions()

  
	//**************** testVelocities() ************************
	/** Method used to test the second version of class Pluto99 (with phi in radians);
	<BR>The refence values are obtained from <CODE>Pluto99_deg.java</CODE>., in the current directory
	*/
  public static void testVelocities(){

		// compute the average speed, in a.u. per Julian year.
		double aphelion = 7304.3E6 / 149597870.61; // in  a.u.
		double perihelion = 4435E6 / 149597870.61; // in a.u.
		double period = 90588 / 365.25; // in years
		double r = (aphelion + perihelion) / 2;
		double meanVel = 2 * Math.PI * r / period;
		System.out.println("meanVel : " + meanVel);
		
		double start = 626150.5, end = 2811150.5; // start and end dates
		double testInterval = 10000; // a test will be done every 'testInterval' days
		double curJd = start + testInterval;
		double deltaT = 0.1;
		double years = 2 * deltaT / 365.25;
		double dist;
		double v1, v2, v3;
		Body bi = new Body (SolarSystemConstants.PLUTO); // t - deltaT
		Body b = new Body (SolarSystemConstants.PLUTO);  // t 
		Body bf = new Body (SolarSystemConstants.PLUTO); // t + deltaT
		while(curJd < end - deltaT){
      Pluto99.calcCoord(curJd - deltaT, bi, false);
      Pluto99.calcCoord(curJd, b, true);
      Pluto99.calcCoord(curJd + deltaT, bf, false);
			// distance between bi and bf - in a.u.
			dist = Math.sqrt((bi.getX1()-bf.getX1())*(bi.getX1()-bf.getX1()) 
										  +(bi.getX2()-bf.getX2())*(bi.getX1()-bf.getX2()) 
										  +(bi.getX3()-bf.getX3())*(bi.getX1()-bf.getX3())
			);
			v1 = (bf.getX1()-bi.getX1())/years;
			v2 = (bf.getX2()-bi.getX2())/years;
			v3 = (bf.getX3()-bi.getX3())/years;
			System.out.println("b.v1 = " + b.getV1() + " meanV : " + v1 + " - diff : " + (b.getV1() - v1));
			System.out.println("b.v2 = " + b.getV2() + " meanV : " + v2 + " - diff : " + (b.getV2() - v2));
			System.out.println("b.v3 = " + b.getV3() + " meanV : " + v3 + " - diff : " + (b.getV3() - v3));

			// increment curJD for next test
			curJd += testInterval;
		}

/*    int i;
    double tmp0, tmp1, tmp2;
    double[] res = {0.0, 0.0, 0.0, 0.0, 0.0, 0.0}; // contains the results
		Body b = new Body (SolarSystemConstants.PLUTO);
    for (i = 0; i< NB_TESTS; i++){
      Pluto99.calcCoord(jd[i], b, false);
      //System.out.println("jd " + jd[i] + " : " + b.getX1() + " " + b.getX2() + " " + b.getX3());
      tmp0 = b.getX1() - coord[i][0];
      tmp1 = b.getX2() - coord[i][1];
      tmp2 = b.getX3() - coord[i][2];
      System.out.println("jd " + jd[i] + " : " + tmp0 + " " + tmp1 + " " + tmp2);
    }
*/
  }// end testVelocities()
  
	//**************** calcGeocentricPrecision() ************************
	/** Small method which calculates the geocentric precision an error of 0.00005 u.a. represents. */
  public static void calcGeoPrec(){
		double deltaPos = 0.00005; // u.a.
		
		// minimal distance between Sun and Pluto - approximate value found on the web.
		double minDist = 28.72; // u.a.
		
		// minDist -1 when Earth is between Sun and Pluto
		double alpha = Math.atan(deltaPos / (minDist - 1)) * 206264.806247096355156473357330779; // arc seconds
		System.out.println("alpha = " + alpha);
	}// end calcGeoPrec

}//end class BuildTestPluto99