JEphem Informatic Trail build classes source code AnalyseVSOP87.java
//*********************************************************************************
// class AnalyseVSOP87 // 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.lang.reflect.Method;

/******************************************************************
Contains methods used to analyze VSOP87 data classes.
<BR>This class is not used by JEphem, it was developped to include the generated table in a <A HREF="../../../ephemeris/et320buildVSOP_en.htm" TARGET="_top">page of JEphem's site</A>.
<BR>In this class, when arrays characterizing planetary data were needed (ex : "planetNames"), the indexes of <CODE>jephem.astro.SolarSystem.java</CODE> were used.

@author Thierry Graff
@history mar 30 2001 : Creation from BuildVSOP87.

@todo In getNbTerms, generate java classes, instead of .txt. If the name of this method changes, 
      change also javadoc comment of buildNbTermsTable
@todo getNbTerms should be renamed and generate compilable java classes
@todo Clean class fields coming from BuildVSOP87
*****************************************************************/
public class AnalyseVSOP87{

  // planet names ; indexes correspond to jephem.astro.ISolarSystem.java constants.
  private static String[] planetNames	= {"", "", "Mercury", "Venus", "Earth", "Mars", "Jupiter",
                                  "Saturn", "Uranus", "Neptune"};
  private static int NB_PLANETS = 8;
  private static int INDEX_MERCURY = 2;
  private static int INDEX_NEPTUNE = 10;

  static int SPACE_CODE = 32; // Unicode code of " ".
  static int A_CODE = 10; // Unicode code of "A".
  static int BEGIN_ABC = 82; // Index between terms K and A in a data line (in a BDL file).
  static int MAX_T = 5; // Maximum degree of time in VSOP87 BDL files.

  // semi-major axis ; index corresopnd to jephem.astro.ISolarSystem.java constants.
  static double[] a0s = {0.0, 0.0, 0.39, 0.72, 1.00, 1.52, 5.20, 9.55, 19.22, 30.11}; // a.u.
  // eccentricities ; index corresopnd to jephem.astro.ISolarSystem.java constants.
  static double[] eccs = {0.0, 0.0, 0.21, 0.01, 0.02, 0.09, 0.05, 0.06, 0.05, 0.01};
  // Maximal geocentric angular error admissible for a planet (result of calcAlphaMax).
  // Valid for versions A and C
  //  Index corresopnd to jephem.astro.ISolarSystem.java constants.
  static double alphaMax[] = {0.0, 0.0, 0.5, 0.1, 0.2, 0.4, 0.9, 0.9, 0.9, 0.9}; // in arc seconds

  //=================================================================================
  //                                 METHODS
  //=================================================================================
	//**************** main ************************
  public static void main(String[] args){
    buildNbTermsTable(args[0], args[1]);
    //getNbTerms(args[0]);
    //calcErrMax();
    //calcAlphaMax(args[0], args[1]);
    //getBDLCommentLines();
  }// end main

	//**************** getNbTerms ************************
	/** This method takes in current directory data classes (.java) and builds two files :
  <LI><CODE>'prefix'__nbTerms.txt</CODE>, containing nbTerms arrays</LI>,
  <LI><CODE>'prefix'__retained.txt</CODE>, containing comment lines of data[][]</LI>.
  The purpose is to compare generated data classes with XEphem classes and original BDL files.
  <BR>Names of data classes must be : <CODE>strPrefix_yyy</CODE>, where <CODE>yyy</CODE> is
   planet name.
  @param strPrefix beginning of names of files to analyze (ex <CODE>DataVSOP87A_Full</CODE>).
  */
  public static void getNbTerms(String strPrefix){
 		String ls = System.getProperty("line.separator");

    FileOutputStream fosNbTerms = null;
//    FileOutputStream fosRetained = null;
	  LineNumberReader lnr = null;

    String planetName = new String();
    String inputFileName = new String();
    String outputNbTerms = strPrefix + "__nbTerms.txt";
//    String outputRetained = strPrefix + "__retained.txt";

    String line = new String();
    String strRes = new String();

    boolean keepOn;
  	try{
   		fosNbTerms = new FileOutputStream(new File(outputNbTerms));
//   		fosRetained = new FileOutputStream(new File(outputRetained));
      strRes += "*** nbTerms for " + strPrefix + " ***" + ls + ls;
      fosNbTerms.write(strRes.getBytes());

      for (int i = 2; i < 10; i++){
        // find data class file name.
        inputFileName = strPrefix + "_" + planetNames[i] + ".java";
        System.out.println("Analyzing " + inputFileName);
        lnr = new LineNumberReader(new FileReader(new File(inputFileName)));

        // *** 1 - Write strPrefix__nbTerms.txt
        strRes = "";
        // read until the beginning of nbTerms array is found
        keepOn = true;
        while(keepOn){
          line = lnr.readLine();
          if (line.indexOf("nbTerms[][]") != -1) keepOn = false;
        }
        strRes += "  int[][] nbTerms_" + planetNames[i] + " = {" + ls;

        // copy until the end of nbTerms[][] is found.
        keepOn = true;
        while(keepOn){
          line = lnr.readLine();
          if (line.indexOf("};") != -1){
            keepOn = false;
            strRes += "  }; // end nbTerms_" + planetNames[i] + "[][]" + ls;
          }
          else
            strRes += line + ls;
        }
        strRes += ls;
        fosNbTerms.write(strRes.getBytes());
/*        
        // *** 2 - Write in strPrefix__retained.txt
        strRes = "";
        // read until the beginning of data array is found
        keepOn = true;
        while(keepOn){
          line = lnr.readLine();
          if (line.indexOf("data[][]") != -1) keepOn = false;
        }
        strRes += "****************** " + planetNames[i] + " ******************" + ls;
        // write only comment and blank lines
        keepOn = true;
        while(keepOn){
          line = lnr.readLine();
          if (line.indexOf("end data[][]") != -1) keepOn = false;
          else{
            if (! line.trim().startsWith("{"))
              strRes += line + ls;
          }
        }
        fosRetained.write(strRes.getBytes());
*/        
        lnr.close();
      }// end for i = 2 to 9
      fosNbTerms.close();
//      fosRetained.close();
    }
    catch(Exception ex){
			System.out.println("Exception" + ex.toString());
      ex.printStackTrace();
      System.exit(0);
    }
  }// end getNbTerms


	//**************** buildNbTermsTable ************************
	/** Once nbTerm classes have been generated with {@link #getNbTerms(String) getNbTerms},
  the purpose is to generate an HTML table comparing these classes.
  <BR>For each planet, a sum is done for each number of terms.
  <BR><BR>To work, compiled nbTerm classes (ex : <CODE>DataVSOP87A__nbTerms.class</CODE>) MUST
  be in the classpath.
  @param strVersion : contains coma-separated names of the versions to compare
  <BR>(ex : "<CODE>VSOP87A_Full,VSOP87A,VSOP87C</CODE>")
  <BR>The program will search for classes with names corresponding to the output of
  <CODE>getNbTerms()</CODE> (in the example : <CODE>DataVSOP87A_Full__nbTerms.class</CODE>, 
  <CODE>DataVSOP87A__nbTerms.class</CODE>, <CODE>DataVSOP87D__nbTerms.class</CODE>).
  @param strTitles : contains the titles which will appear in the tables
  <BR>(ex : "<CODE>(BDL),(JEphem),(XEphem)</CODE>").
  <BR>There MUST be the same number of elements in <CODE>strVersions</CODE> and strTitles.
  */
  public static void buildNbTermsTable(String strVersions, String strTitles){
 		String ls = System.getProperty("line.separator");

    //auxiliary variables
    String strTmp;
    int i, j, k, l;
    
    //*** Argument parsing ***
    int nbVersions = 0;
    int idxComa;
    // calculate nbVersions from the number of comas in strVersion
    strTmp = strVersions;
    idxComa = strTmp.indexOf(',');
    while(idxComa !=-1){
      nbVersions ++;
      strTmp = strTmp.substring(idxComa + 1);
      idxComa = strTmp.indexOf(',');
    }
    nbVersions ++;
System.out.println("nbVersions = " + nbVersions);
  
    // Check that there is the same number of comas in strTitles
    strTmp = strTitles;
    int tmp = 0;
    idxComa = strTmp.indexOf(',');
    while(idxComa !=-1){
      tmp ++;
      strTmp = strTmp.substring(idxComa + 1);
      idxComa = strTmp.indexOf(',');
    }
    tmp ++;
    if (tmp != nbVersions){
      System.out.println("Both arguments (version names and tiles) must have the same number of"
                          + " comas\nUnable to process");
      System.exit(0);
    }
      
    //*** build the arrays of classnames and headings to be displayed
    String[] strHeadings = new String[nbVersions];
    String[] strClasses = new String[nbVersions];
    
    strClasses = AnalyseVSOP87.parse(strVersions);
    strHeadings = AnalyseVSOP87.parse(strTitles);

    for (i = 0; i<nbVersions; i++){
      strHeadings[i] = "<CENTER>" + strClasses[i] +"<BR>(" + strHeadings[i] + ")</CENTER>";
      strClasses[i] = "Data" + strClasses[i] + "__nbTerms";
System.out.println("strHeadings[i] = " + strHeadings[i]);
System.out.println("strClasses[i] = " + strClasses[i]);
    }

    //*** Get the data from classes xxx_nbTerms.class, using reflectivity
    // these classes must be in the classpath***
    // And make the sums that will be displayed
  	try{
      int[][] nbTerms = new int [nbVersions][NB_PLANETS];
      int[] totalTerms = new int [nbVersions]; // total nb of terms for this version
      int[][] terms = new int[3][5] ; // details of terms for a planet
      
      for(i = 0; i < nbVersions; i++){
        Class dataClass = Class.forName(strClasses[i]);
        for (j = INDEX_MERCURY; j < INDEX_NEPTUNE; j++){
          terms = (int[][])dataClass.getDeclaredField("nbTerms_" + planetNames[j]).get(null);
          for(k = 0; k < 3; k++){
            for(l = 0; l < 6; l++){
              nbTerms[i][j - INDEX_MERCURY] += terms[k][l];
              totalTerms[i] += terms[k][l];
            }
          }
        }// end for j
      }// end for i
  
      //*** Build the HTML output
      String strRes = "";
      // Beginning
   		FileOutputStream fosNbTerms = new FileOutputStream(new File("result.htm"));
      strRes += "<HTML><BODY>" + ls;
      strRes += "<TABLE BORDER = \"1\">" + ls;

      // First row, for titles
      strRes += "<TR><TD></TD>" + ls;
      for (i = 0; i < nbVersions; i++)
        strRes+= "<TD><B>" + strHeadings[i] + "</B></TD>";
      strRes += "</TR>" + ls;
      
      // Data Rows, containing nb of terms      
      for (i = INDEX_MERCURY; i < INDEX_NEPTUNE; i++){
        strRes += "<TR>" + ls;
        strRes += "<TR><TD><CENTER>" + planetNames[i] + "</CENTER></TD>" + ls;
        for(j = 0; j < nbVersions; j++){
          strRes += "<TD><CENTER>" + nbTerms[j][i - INDEX_MERCURY] + "</CENTER></TD>" + ls;
        }
        strRes += "</TR>" + ls;
      }

      // Data Rows, containing nb of terms      
        strRes += "<TR>" + ls;
        strRes += "<TR><TD><CENTER><B>Total</B></CENTER></TD>" + ls;
        for(j = 0; j < nbVersions; j++)
          strRes += "<TD><CENTER><B>" + totalTerms[j] + "</B></CENTER></TD>" + ls;
        strRes += "</TR>" + ls;

      // End
      strRes += "</TABLE>" + ls;
      strRes += "</BODY></HTML>" + ls;
      fosNbTerms.write(strRes.getBytes());
      fosNbTerms.close();
    }
    catch(Exception ex){
			System.out.println("Exception" + ex.toString());
      ex.printStackTrace();
      System.exit(0);
    }
    
  }// end buildNbTermsTable

	//******************* parse(String s) **************	
	/**********************************************************
  // REFERENCE IMPLEMENTATION IN tig.util.Util.java
  // used by buildNbTermsTable
	*****************************************************************/
	private static String[] parse(String s){
		//System.out.println("s = " + s);
		int curPos=0, i=0;
		int comaPos=s.indexOf(',', curPos);
		
		//1er passage, détermination de la longueur du tableau réultat res[]
		while(comaPos!=-1){
			curPos=comaPos+1;
			comaPos=s.indexOf(',', curPos);
			i++;
		}
		//2ème passage, remplissage de res[]
		//System.out.println("2eme passage");
		String[] res = new String[i+1];
		curPos=0;
		i=0;
		comaPos=s.indexOf(',', curPos);
		while(comaPos!=-1){
			res[i]=s.substring(curPos, comaPos).trim();
			//System.out.println("res[" + i + "] = " + res[i] + " ; comaPos = " + comaPos);
			curPos=comaPos+1;
			comaPos=s.indexOf(',', curPos);
			i++;
		}
			res[i]=s.substring(curPos, s.length()).trim();
			//System.out.println("res[" + i + "] = " + res[i] + " ; comaPos = " + comaPos);
    return res;
  }//end parse(String s)  


	//**************** getBDLCommentLines() ************************
	/** Produces an output with only comment lines of BDL files.
  <BR>Method used to check validity of nbTerms arrays in classes "DataVSOP87C_Full_xxx".
  <BR>Input files must be in the current directory
  */
  public static void getBDLCommentLines(){

 		String ls = System.getProperty("line.separator");

    String strOutputFile = "DataVSOP87C_Comments.txt";
    String[] strInputFiles = {"VSOP87C.ear.txt", "VSOP87C.jup.txt",
                              "VSOP87C.mar.txt", "VSOP87C.mer.txt",
                              "VSOP87C.nep.txt", "VSOP87C.sat.txt",
                              "VSOP87C.ura.txt", "VSOP87C.ven.txt"
                               };

    FileOutputStream fos = null;
	  LineNumberReader lnr = null;

  	try{
  		fos = new FileOutputStream(new File(strOutputFile));
      String line = new String();

      for (int i = 0; i < strInputFiles.length ; i++){
        lnr = new LineNumberReader(new FileReader(new File(strInputFiles[i])));
        while((line = lnr.readLine()) != null){
          if (line.trim().startsWith("VSOP87")){
            fos.write(line.getBytes());
            fos.write(ls.getBytes());
          }
        } // end while((line = lnr.readLine()) != null)
  
        fos.write(ls.getBytes());
        lnr.close();
        lnr = null;
      } // end for i < strInputFiles.length
      fos.close();
		}
		catch(Exception e){
			System.out.println("Exception" + e.toString());
      e.printStackTrace();
      System.exit(0);
    }
  }// end getBDLCommentLines

	//**************** calcAlphaMax ************************
	/** Intermediate calculation to know maximal geocentric angular error admissible for a planet
  to finally get 1 arcsecond of precision.
  @param strAlpha0 and strAlpha1 represent the values in arc seconds
  */
  public static void calcAlphaMax(String strAlpha0, String strAlpha1){

    double alpha0 = Double.parseDouble(strAlpha0);
    double alpha1 = Double.parseDouble(strAlpha1);
    alpha0 = Math.toRadians(alpha0/3600.0);
    alpha1 = Math.toRadians(alpha1/3600.0);

    double aE = a0s[4]; // earth semi-major axis
    double eccE = eccs[4]; // earth eccentricity. Could be bettered by calculating eccP and eccE
    // in validity interval, and take max / min values.

    double res = 0; // = SE/EP
    double alpha = 0;
    for(int pl = 2; pl < 10; pl++){ // pl = planetIndex
      if (pl == 4) continue;
      double aP = a0s[pl]; // planet semi-major axis
      double eccP = eccs[pl]; // planet eccentricity.
      switch (pl){
        case 2:
        case 3:
          res = aE * (1 + eccE) / (aE*(1 - eccE) - aP*(1 + eccP));
          break;
        case 4: 
          break;
        default: 
          res = aE * (1 + eccE) / (aP*(1 - eccP) - aE*(1 + eccE));
          break;
      }
      alpha = alpha0 + Math.atan(res * Math.tan(alpha1));
      alpha = Math.toDegrees(alpha) * 3600.0;
      System.out.println(planetNames[pl] + " : " + alpha);
      
    } // end for pl
  }// end calcAlphaMax

	//**************** calcErrMax ************************
	/** Calculation of maximal error in cartesian heliocentric position.
  <BR>Results of calcAlphaMax are used (array alphaMax).
  <BR>Results written into a file in order to incorporate to VSOP87Transform4.
  */
  public static void calcErrMax(){

 		String ls = System.getProperty("line.separator");

    FileOutputStream fos = null;
    String strRes = "";
    strRes += "    double[] errMax = { 0.0, " + ls;
    strRes += "                        0.0, " + ls;

  	try{
  
  		fos = new FileOutputStream(new File("errMax.txt"));
      double errMax = 0;
      double v3 = Math.sqrt(3.0);
  
      double aE = a0s[4]; // earth semi-major axis
      double eccE = eccs[4]; // earth eccentricity. Could be bettered by calculating eccP and eccE
      // in validity interval, and take max / min values.
  
      double res = 0;
      double alpha = 0;
      for(int pl = 2; pl < 10; pl++){ // pl = planetIndex
        double aP = a0s[pl]; // planet semi-major axis
        double eccP = eccs[pl]; // planet eccentricity.
        double tgAlphaMax = Math.tan(Math.toRadians(alphaMax[pl]/3600.0));
        switch (pl){
          case 2:
          case 3:
            errMax = (aE*(1 - eccE) - aP*(1 + eccP)) * tgAlphaMax / v3;
            break;
          case 4: 
            errMax = aE*(1 - eccE) * tgAlphaMax / v3;
            break;
          default: 
            errMax = (aP*(1 - eccP) - aE*(1 + eccE)) * tgAlphaMax / v3;
            break;
        }
        System.out.println(planetNames[pl] + " : " + errMax);
        strRes += "                        " + Double.toString(errMax) + ", ";
        strRes += " // " + planetNames[pl] + ls;
      } // end for pl

    strRes += "                      };" + ls;
      fos.write(strRes.getBytes());
      fos.close();
    }
    catch(Exception ex){
			System.out.println("Exception" + ex.toString());
      ex.printStackTrace();
      System.exit(0);
    }
  }// end calcErrMax

}//end class AnalyseVSOP87