JEphem Informatic Trail build classes source code BuildBSC5.java
//*********************************************************************************
// class BuildBSC5 // 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 java.util.Date;
/******************************************************************
Parsing and building files from BSC 5 (Bright Star Catalog, 5th edition, preliminary version).
<BR>Original files found at :
<A HREF="http://adc.gsfc.nasa.gov/cgi-bin/adc/cat.pl?/catalogs/5/5050">
http://adc.gsfc.nasa.gov/cgi-bin/adc/cat.pl?/catalogs/5/5050</A>.
<BR>To work, the file <CODE>catalog.dat</CODE> must be in the current directory.

<BR><BR>The method <CODE>build()</CODE> can be used to generate binary or text files. It is also possible to choose the fields from the original file which are stored in the generated file.
<BR>Fields that can be transformed to the original file are expressed by a letter of <CODE>FIELDS_CODE</CODE> : 
<LI><B>B</B> stands for BSC number</LI>
<LI><B>H</B> stands for HD number</LI>
<LI><B>S</B> stands for SAO number</LI>
<LI><B>R</B> stands for r.a.</LI>
<LI><B>D</B> stands for dec.</LI>
<LI><B>M</B> stands for visual magnitude</LI>
<LI><B>T</B> stands for spectral type</LI>
<LI><B>P</B> stands for parallax</LI>

<BR><BR><B>WARNING</B>, there is a bug : when text files are generated, one must remove manually several characters.

@author Thierry Graff
@history mar 02 2002 : Creation

@todo try to store ra dec with floats and see if precision is lost
*****************************************************************/
public abstract class BuildBSC5{

  //=================================================================================
  //                            CONSTANTS
  //=================================================================================
  
	// parameters for 1st argument of main()
	private static final String PARAM_BUILD = "build"; 
	private static final String PARAM_TEST01 = "test01";
	private static final String PARAM_TEST02 = "test02";
	private static final String PARAM_TEST03 = "test03";
	private static final String PARAM_TEST04 = "test04";

  // parameters for build().
  private static final int FORMAT_TEXT = 0;
  private static final int FORMAT_BINARY = 1;
  private static final int FORMAT_RAD = 2;
  
	// Codes of the different fields to retrieve (used in arrays)
  /** Code to designate BSC number (= Harvard Revised Number). */
  private static final int BSC = 0;
  /** Code to designate  HD catalog number. */
  private static final int HD = 1;
  /** Code to designate SAO catalog number. */
  private static final int SAO = 2;
  /** Code to designate r.a. 2000. */
  private static final int RA = 3;
  /** Code to designate dec. 2000. */
  private static final int DEC = 4;
  /** Code to designate visual magnitude. */
  private static final int MAG = 5;
  /** Code to designate spectral type. */
  private static final int TYP = 6;
  /** Code to designate parallax. */
  private static final int PAR = 7;
  
  private static final int NB_OUTPUT_FIELDS = 8;

  /** Codes to designate fields ; contains the letters B, H, S, R, D, M, T, P. */
  public static final String[] FIELDS_CODE = {"B", "H", "S", "R", "D", "M", "T", "P"};

  // limits of columns for the fields
  // WARNING  : for easy coding with String.substring()
  // COLS[i][0] is the nb specified in readMe - 1
  // COLS[i][1] is the nb specified in readMe
  private static final int[][] COLS = {
    {0, 4}, // BSC
    {25, 31}, // HD
    {31, 37}, // SAO
    {75, 83}, // RA
    {83, 90}, // DEC
    {102, 107}, // MAG
    {127, 147}, // TYP
    {161, 166} // PAR
  };

  private final static String INPUT_FILE_NAME = "catalog.dat";
  /** nb of lines in original file */
	private static final int NB_LINES = 9110; 
  /** nb of stars (= nb of lines of files generated by build() if _onlyStars = true */
	private static final int NB_STARS = 9096;

  private final static String LS = System.getProperty("line.separator");
  private final static String SPACE = " ";
  private final static String BLANK = "";
  private final static String PLUS = "+";

	/** To format radian values of ra and dec. */
  // precision in file : 0.1 time sec for ra ( = 7.2e-6 rad) and a arcsec for dec  ( = 4.8e-6 rad); 
	// So 6 decimal are kept when ra and dec are stored in radians.
	/////////////////// TO BE CHECKED //////////////////////
	private static final DecimalFormat NF = new DecimalFormat();
	static{
    NF.setMaximumFractionDigits(6);
    NF.setMinimumFractionDigits(6);
		DecimalFormatSymbols dfs = new DecimalFormatSymbols();
		dfs.setDecimalSeparator('.');
		NF.setDecimalFormatSymbols(dfs);
	};
	
  //=================================================================================
  //                            STATIC FIELDS
  //=================================================================================
  /** Booleans to know which fields are taken into account. */
  private static boolean[] _calcField = new boolean[NB_OUTPUT_FIELDS]; // false by default

  /** Booleans to know if non-star objects are output or not. */
  private static boolean _onlyStars = true;

  //=================================================================================
  //                                      PUBLIC METHODS
  //=================================================================================

	//**************** main ************************
	/** Entry point to use this class.
  @arg args The first parameter indicates which method will be called.
  <BR>if equals to "build", build() is called.
  <BR>The following arguments are passed to the called method.
	*/
  public static void main(String[] args){
		String paramList = "'" + PARAM_BUILD + "' or '" + PARAM_TEST01 + "' or '" + PARAM_TEST02 
                     + "' or '" + PARAM_TEST03 + "' or '" + PARAM_TEST04 + "'";
		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 != 5){
    			System.out.println("Call to build must be done with 4 arguments :");
    			System.out.println("file format, data flags, ra dec format, output file name");
    			System.exit(0);
        }
				build(args[1], args[2], args[3], args[4]);
		}
		else if (args[0].compareToIgnoreCase(PARAM_TEST01) == 0){
        if(args.length != 2){
    			System.out.println("Call to test01 must be done with 1 arguments : testFileName");
    			System.exit(0);
        }
      test01(args[1]);
    }
		else if (args[0].compareToIgnoreCase(PARAM_TEST02) == 0){
        if(args.length != 3){
    			System.out.println("Call to test02 must be done with 2 arguments : testFileName1, testFileName2");
    			System.exit(0);
        }
      test02(args[1], args[2]);
    }
		else if (args[0].compareToIgnoreCase(PARAM_TEST03) == 0){
        if(args.length != 3){
    			System.out.println("Call to test03 must be done with 2 arguments : testFileName1, testFileName2");
    			System.exit(0);
        }
      test03(args[1], args[2]);
    }
		else if(args[0].compareToIgnoreCase(PARAM_TEST04) == 0){
        if(args.length != 3){
    			System.out.println("Call to test04 must be done with 2 arguments : testFileName1, testFileName2");
    			System.exit(0);
        }
      test04(args[1], args[2]);
    }
		else{
			System.out.println("First parameter must be :");
			System.out.println(paramList);
		}
		
  }// end main

	//**************** build() ************************
  /** Method used to extract some fields from 'catalog.dat'.
  <BR>Note : text fields are output with a fixed length ; space are added if necessary.
  @param strFileFormat Indicates the output file format. Must be "TEXT" or "BINARY".
  @param strDataFlags Indicates which fields will be in the output file.
         Must be a combination of letters contained in <CODE>FIELDS_CODE</CODE>.
  @param strRaDecFormat Indicates how to output ra and dec. Can be :
         <LI>"TEXT" (output is done in standard format : HHMMSS for ra and sDDDMMSS.S for dec) ;</LI>
         <!-- <LI>"DEG" (output is numeric, in decimal degrees) ;</LI> -->
         <LI>"RAD" (output is numeric, in radians, with 7 decimals)</LI>
  */
  public static void build(String strFileFormat, String strDataFlags, 
                           String strRaDecFormat, String strOutputFileName){
    
    int i; // used several times
    
    // 1 - Parameter checking
    int fileFormat;
    if(strFileFormat.equalsIgnoreCase("text")) fileFormat = FORMAT_TEXT;
    else if(strFileFormat.equalsIgnoreCase("binary")) fileFormat = FORMAT_BINARY;
    else throw new IllegalArgumentException("parameter 'strFileFormat' must be 'text' or 'binary'");
    
    // Get the fields to output
    for (i = 0; i < NB_OUTPUT_FIELDS; i++){
      if (strDataFlags.indexOf(FIELDS_CODE[i]) >= 0)
        _calcField[i] = true;
    }
    
    int raDecFormat;
    if (strRaDecFormat.equalsIgnoreCase("text")) raDecFormat = FORMAT_TEXT;
    else if (strRaDecFormat.equalsIgnoreCase("rad")) raDecFormat = FORMAT_RAD;
    else throw new IllegalArgumentException("parameter 'strRaDecFormat' must be 'text' or 'rad'");
    
    // 2 - Loop on input file
		try{
  		FileOutputStream fos = new FileOutputStream(new File(strOutputFileName));
      LineNumberReader lnr = new LineNumberReader(new FileReader(new File(INPUT_FILE_NAME)));
      ObjectOutputStream oos = new ObjectOutputStream(fos);

      int n = 0; // nb of output lines
      String line;
      StringBuffer strRes = new StringBuffer(BLANK);
      double tmp = 0;
      String strField; // used for text output format
    
      // Main loop
      for(int iLine = 0; iLine < NB_LINES; iLine++){
				line = lnr.readLine();
        
        // first check if it's a star - for non-star object, the line has no ra dec
        if(_onlyStars && line.substring(COLS[RA][0], COLS[RA][1]).trim().length() ==0) continue;
        n++; //System.out.println(n);
        
        // loop on fields to output
        for(i = 0; i < NB_OUTPUT_FIELDS; i++){
//        for (i = 0; i < 10; i++){
          if(_calcField[i]){
            switch(i){
              case RA :
                if(raDecFormat == FORMAT_RAD){
                  tmp = getRa(line.substring(COLS[i][0], COLS[i][1]));
                  strField = NF.format(tmp);
                }
                else 
                  strField = line.substring(COLS[i][0], COLS[i][1]);
              break;
              case DEC :
                if(raDecFormat == FORMAT_RAD){
                  tmp = getDec(line.substring(COLS[i][0], COLS[i][1]));
                  strField = NF.format(tmp);
                  strField = (tmp > 0 ? PLUS : BLANK) + strField; // add '+' for > 0 results
                }
                else
                  strField = line.substring(COLS[i][0], COLS[i][1]);
              break;
              case PAR : // some stars don't have parallax
                try{strField = line.substring(COLS[i][0], COLS[i][1]);}
                catch(Exception e){strField = "     ";}
              break;
              default :
                strField = line.substring(COLS[i][0], COLS[i][1]);
            }// end switch

            // Write the field in the output
            if(fileFormat == FORMAT_TEXT) strRes.append(strField);
            else{ // BINARY
              if ((i == RA || i == DEC) && raDecFormat == FORMAT_RAD)
                oos.writeDouble(tmp);
              else
                oos.writeObject(strField);
            }
          }// end if(_calcField[i])
        }// end loop on fields to output
        strRes.append(LS);
        
      }// end main loop
      
      System.out.println(n + " lines in the output file");

      if (fileFormat == FORMAT_TEXT) fos.write(strRes.toString().getBytes());

 			oos.close();
 			fos.close();

		}
		catch(Exception e){
			e.printStackTrace();
		}
  }// end build()

	//*************************************************
	/** To test if binary values can be retrieved when the file contains both doubles and Strings
  (test for a binary file containing BSC number, ra, dec). */
  // result of test : OK, possible to retrieve correctly.
	public static void test01(String strInputFile){
		try{
      ObjectInputStream ois = new ObjectInputStream(new FileInputStream(new File(strInputFile)));
      for (int i = 0; i < 5; i++){
        String bsc = (String)ois.readObject();
        double ra = ois.readDouble();
        double dec = ois.readDouble();
        System.out.println("====== " + i + " ======");
        System.out.println("BSC : " + bsc);
        System.out.println("ra : " + ra);
        System.out.println("dec : " + dec);
      }
      ois.close();
		}catch (Exception e){ e.printStackTrace(); }
	}// end test01

	//*************************************************
	/** To test execution time between text and binary versions for ra / dec in radians.
  @param testFile1 : a binary file containing ra dec expressed in radians
  @param testFile2 : a text file containing ra dec expressed in radians
   */
	public static void test02(String testFile1, String testFile2){
		try{
      Date startTest1, endTest1;
      Date startTest2, endTest2;
      long duration1, duration2;
      
      int i, j;
      
      // test binary
      System.out.println("read binary...");
      double ra, dec;
      startTest1 = new Date();
      for (j = 0; j < 10; j++){
        ObjectInputStream ois = new ObjectInputStream(new FileInputStream(new File(testFile1)));
        for (i = 0; i < NB_STARS; i++){
          ra = ois.readDouble();
          dec = ois.readDouble();
        }
        ois.close();
        ois = null;
      }
      endTest1 = new Date();
      duration1 = endTest1.getTime() - startTest1.getTime();
      
      // test text
      System.out.println("read text...");
      String strRa, strDec;
      String line;
      startTest2 = new Date();
      for (j = 0; j < 10; j++){
        LineNumberReader lnr = new LineNumberReader(new FileReader(new File(testFile2)));
        for (i = 0; i < NB_STARS; i++){
        //for (int i = 0; i < 2; i++){
          line = lnr.readLine();
          ra = Double.parseDouble(line.substring(0, 9));
          //System.out.println("ra : " + ra);
          dec = Double.parseDouble(line.substring(9));
          //System.out.println("dec : " + dec);
        }
        lnr.close();
        lnr = null;
      }
      endTest2 = new Date();
      duration2 = endTest2.getTime() - startTest2.getTime();

      System.out.println("time for binary : " + duration1); // result = 63
      System.out.println("time for text : " + duration2); // result = 328
      // COMMENTS ON THE RESULTS :
      // A first execution without loops on j were giving binary = 0 and text = 94.
      // So in 63 and 328, a large part of the time can be taken to build the streams.
		}catch (Exception e){ e.printStackTrace(); }
	}// end test02

	//*************************************************
	/** To test execution time between text and binary versions for ra / dec in text format.
  @param testFile1 : a binary file containing ra dec expressed in text format
  @param testFile2 : a text file containing ra dec expressed in text format
   */
	public static void test03(String testFile1, String testFile2){
		try{
      Date startTest1, endTest1;
      Date startTest2, endTest2;
      long duration1, duration2;
      
      ObjectInputStream ois = new ObjectInputStream(new FileInputStream(new File(testFile1)));
      LineNumberReader lnr = new LineNumberReader(new FileReader(new File(testFile2)));
      
      String strRa, strDec;
      int i;

      // test binary
      System.out.println("read binary...");
      startTest1 = new Date();
      for (i = 0; i < NB_STARS; i++){
      //for (i = 0; i < 2; i++){
        strRa = (String)ois.readObject();
        //System.out.println("strRa : " + strRa);
        strDec = (String)ois.readObject();
        //System.out.println("strDec : " + strDec);
      }
      endTest1 = new Date();
      duration1 = endTest1.getTime() - startTest1.getTime();
      
      // test text
      System.out.println("read text...");
      String line;
      startTest2 = new Date();
      for (i = 0; i < NB_STARS; i++){
      //for (i = 0; i < 2; i++){
        line = lnr.readLine();
        strRa = line.substring(0, 8);
        //System.out.println("strRa : " + strRa);
        strDec = line.substring(8);
        //System.out.println("strDec : " + strDec);
      }
      endTest2 = new Date();
      duration2 = endTest2.getTime() - startTest2.getTime();
      
      ois.close();
      lnr.close();

      System.out.println("time for binary : " + duration1); // res = 203
      System.out.println("time for text : " + duration2); // res = 47

		}catch (Exception e){ e.printStackTrace(); }
	}// end test03

	//*************************************************
	/** To test execution time as it will be used in JEphem :
  <LI>get ra and dec as doubles, and the other fields as Strings.</LI>
  - between text and binary versions for files containing all fields (ra / dec in RAD format).
  @param testFile1 : a binary file containing all the fields
  @param testFile2 : a text file containing all the fields
   */
	public static void test04(String testFile1, String testFile2){
		try{
      Date startTest1, endTest1;
      Date startTest2, endTest2;
      long duration1, duration2;
      
      ObjectInputStream ois = new ObjectInputStream(new FileInputStream(new File(testFile1)));
      LineNumberReader lnr = new LineNumberReader(new FileReader(new File(testFile2)));
      
      String strBSC, strHD, strSAO, strMag, strTyp, strPar;
      double ra, dec;
      int i;

      // test binary
      System.out.println("read binary...");
      startTest1 = new Date();
      for (i = 0; i < NB_STARS; i++){
      //for (i = 0; i < 4; i++){
        strBSC = (String)ois.readObject();
        //System.out.println("strBSC : " + strBSC);
        strHD = (String)ois.readObject();
        strSAO = (String)ois.readObject();
        ra = ois.readDouble();
        dec = ois.readDouble();
        strMag = (String)ois.readObject();
        strTyp = (String)ois.readObject();
        strPar = (String)ois.readObject();
        //System.out.println("strPar : " + strPar);
      }
      endTest1 = new Date();
      duration1 = endTest1.getTime() - startTest1.getTime();
      
      // test text
      System.out.println("read text...");
      String line;
      startTest2 = new Date();
      for (i = 0; i < NB_STARS; i++){
      //for (i = 0; i < 2; i++){
        line = lnr.readLine();
        strBSC = line.substring(0, 4);
        strHD = line.substring(4, 10);
        strSAO = line.substring(10, 16);
        ra = Double.parseDouble(line.substring(16, 24));
        dec = Double.parseDouble(line.substring(24, 33));
        strMag = line.substring(33, 38);
        strTyp = line.substring(38, 58);
        strPar = line.substring(58);
      }
      endTest2 = new Date();
      duration2 = endTest2.getTime() - startTest2.getTime();
      
      ois.close();
      lnr.close();

      System.out.println("time for binary : " + duration1); // res = 203
      System.out.println("time for text : " + duration2); // res = 47

		}catch (Exception e){ e.printStackTrace(); }
	}// end test04

  //=================================================================================
  //                                      PRIVATE METHODS
  //=================================================================================

	//*************************************************
	/** Auxiliary method which converts ra from input file to <B>radians</B>.
	@param str Contains the ra as formatted in the input file : HHMMSS.S
  */
	private static double getRa(String str){
		try{
			double hours = Double.parseDouble(str.substring(0,2));
//			System.out.println("ra - parsed hours : " + hours);
			double minuts = Double.parseDouble(str.substring(2,4))/60.0; // convert minuts to decimal hours
//			System.out.println("ra - parsed minuts : " + minuts);
			double seconds = Double.parseDouble(str.substring(4))/3600.0; // convert seconds to decimal hours
//			System.out.println("ra - parsed seconds : " + seconds);
			double res = hours + minuts + seconds;
			res *= 15.0; // convert to degrees
			return Math.toRadians(res);
		}catch (NumberFormatException nfe){
			System.out.println("getRa - Unable to parse '" + str + "'");
			return 0.0;
		}
	}// end getRa

	//*************************************************
	/** Auxiliary method which converts dec from input file format to <B>radians</B>.
	@param str Contains the dec as formatted in the input file : sDDMMSS (s = sign).
  */
	private static double getDec(String str){
		try{
			double deg = Double.parseDouble(str.substring(0,3));
//			System.out.println("dec - parsed deg : " + deg);
			double minuts = Double.parseDouble(str.substring(3,5))/60.0; // convert minuts to decimal degrees
//			System.out.println("dec - parsed minuts " + minuts);
			double seconds = Double.parseDouble(str.substring(5))/3600.0; // convert seconds to decimal degrees
//			System.out.println("dec - parsed seconds " + seconds);
			double res = deg + minuts + seconds;
			return Math.toRadians(res);
		}catch (NumberFormatException nfe){
			System.out.println("getDec - Unable to parse '" + str + "'");
			return 0.0;
		}
	}// end getDec
  
}//end class BuildBSC5