//
// HRITAdapter.java
//

/*
VisAD system for interactive analysis and visualization of numerical
data.  Copyright (C) 1996 - 2017 Bill Hibbard, Curtis Rueden, Tom
Rink, Dave Glowacki, Steve Emmerson, Tom Whittaker, Don Murray, and
Tommy Jasmin.

This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Library General Public
License as published by the Free Software Foundation; either
version 2 of the License, or (at your option) any later version.

This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
Library General Public License for more details.

You should have received a copy of the GNU Library General Public
License along with this library; if not, write to the Free
Software Foundation, Inc., 59 Temple Place - Suite 330, Boston,
MA 02111-1307, USA
*/

package visad.data.hrit;

import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;

import edu.wisc.ssec.mcidas.AREAnav;
import edu.wisc.ssec.mcidas.Calibrator;
import edu.wisc.ssec.mcidas.CalibratorException;
import edu.wisc.ssec.mcidas.CalibratorMsg;

import visad.CoordinateSystem;
import visad.FlatField;
import visad.FunctionType;
import visad.Linear2DSet;
import visad.RealTupleType;
import visad.RealType;
import visad.Unit;
import visad.VisADException;

import visad.util.Util;

/** 
 * This is an adapter for HRIT format data files 
 * At present, it will only work on MSG2 data, but with 
 * some work should be able to handle most HRIT data.
 */

public class HRITAdapter {

  private FlatField field = null;
  private static final int HEADER_TYPE_PRIMARY_HEADER = 0;
  private static final int HEADER_TYPE_IMAGE_STRUCTURE = 1;
  private static final int HEADER_TYPE_IMAGE_NAVIGATION = 2;
  private static final int PRIMARY_HEADER_LENGTH = 16;
  
  // record sizes for prologue file sections - used to offset to cal
  private static final int SAT_STAT_LEN = 60134;
  private static final int IMG_ACQ_LEN = 700;
  private static final int CEL_EVENTS_LEN = 326058;
  private static final int IMG_DESC_LEN = 101;
  private static final int CAL_OFFS = 72;
  
  private static final int SPACECRAFT_ID_MSG2 = 322;
  private static final int SPACECRAFT_ID_MSG3 = 323;

  /**
   * Create a VisAD FlatField from local HRIT file(s).  This constructor 
   * is included for backward compatibility but should not be used and 
   * should be phased out in future revisions since it does not make a 
   * valid band number determination.
   * 
   * @param filenames array of file names
   * @param magFactor magnification factor
   * @exception IOException if there was a problem reading the file(s).
   * @exception VisADException if an unexpected problem occurs.
   */
  
  public HRITAdapter(String [] filenames, int magFactor) 
  	throws IOException, VisADException
  {
	  this (
		 filenames, magFactor, Calibrator.CAL_BRIT, 1
	  );
  }

  /** 
   * Create a VisAD FlatField from local HRIT file(s).
   * @param filenames names of local files.
   * @param magFactor magnification factor
   * @param calType calibration type
   * @param bandNum band number
   * @exception IOException if there was a problem reading the file(s).
   * @exception VisADException if an unexpected problem occurs.
   */
  
  public HRITAdapter(String [] filenames, int magFactor, int calType, int bandNum)
	throws IOException, VisADException
  {
	  // set new mag factor if necessary
	  if ((magFactor != 1) &&
	      (magFactor != 2) &&
	      (magFactor != 4) &&
	      (magFactor != 8) &&
	      (magFactor != 16)) {
		  throw new VisADException("Invalid magnification factor for HRIT: " + magFactor);
	  }
	  
	  // Initial sanity checks on input file names
	  
	  // null parameter
	  if (filenames == null) {
		  throw new IOException("No filenames specified");
	  }
	  
	  // TODO: practical limit on number of input files?
	  
	  // HRIT filename syntax checks.  If those pass, do basic file integrity check
	  for (int i = 0; i < filenames.length; i++) {
		  
		  // have to do the null check here too, since a single array element could be
		  if (filenames[i] == null) {
			  throw new IOException("File name in array position " + (i + 1) + " is null");
		  }
		  
		  // TODO: determine the correct regular expression here
		  //if (! filenames[i].matches("IMG.*")) {
			//  throw new IOException("File: " + filenames[i] + " violates HRIT naming convention");
		  //}
		  
		  // make sure each file exists - there is almost no I/O overhead to do this check
		  File f = new File(filenames[i]);
		  if (! f.exists()) {
			  throw new IOException("File in array position " + (i + 1) + " does not exist");
		  }
		  
	  }
	  
	  // at this point we have file(s) that initially look ok, time to look closer
	  int [] imageSegmentLines = new int[filenames.length];
	  int [] imageSegmentElements = new int[filenames.length];
	  int [] imageBitsPerPixel = new int[filenames.length];
	  int [] lengthAllHeaders = new int[filenames.length];
	  int [] lineOffset = new int[filenames.length];
	  int minLineOffset = Integer.MAX_VALUE;
	  int columnOffset = -1;
	  int lineScalingFactor = -1;
	  int columnScalingFactor = -1;
	  
	  // only used if we find 10 bit data in a file
	  byte [] tenBitInputArray = null;
	  short [] tenBitOutputArray = null;
	  for (int i = 0; i < filenames.length; i++) {
		  
		  // open a stream to the file 
		  File f = new File(filenames[i]);
		  FileInputStream fis = new FileInputStream(f);
		  
		  // try to pull out the primary header
		  byte [] primaryHeader = new byte[PRIMARY_HEADER_LENGTH];
		  int bytesRead = fis.read(primaryHeader);
		  if ((bytesRead < 0) || (bytesRead != PRIMARY_HEADER_LENGTH)) {
			  fis.close();
			  throw new IOException("File " + filenames[i] + " is not an HRIT file");
		  }
		  
		  // validate primary header contents
		  int headerSize = bytesToShort(primaryHeader, 1);
		  if (headerSize != PRIMARY_HEADER_LENGTH) {
			  fis.close();
			  throw new IOException("File " + filenames[i] + " is not a valid HRIT file");
		  }
		  
		  // make sure file is at least as long as the claimed length of all headers
		  lengthAllHeaders[i] = bytesToInt(primaryHeader, 4);
		  if (f.length() < lengthAllHeaders[i]) {
			  fis.close();
			  throw new IOException("File " + filenames[i] + " is not a valid HRIT file");
		  }
		  
		  // dumpHeader(primaryHeader);
		  // ok, we got the primary header, moving along to the other headers...
		  int headerBytesConsumed = PRIMARY_HEADER_LENGTH;
		  byte [] headerType = new byte[1];
		  byte [] headerLength = new byte[2];
		  while (headerBytesConsumed < lengthAllHeaders[i]) {
			  bytesRead = fis.read(headerType);
			  headerBytesConsumed += bytesRead;
			  bytesRead = fis.read(headerLength);
			  headerBytesConsumed += bytesRead;
			  headerSize = bytesToShort(headerLength, 0);
			  byte [] header = new byte[headerSize - 3];
			  bytesRead = fis.read(header);
			  headerBytesConsumed += bytesRead;
			  // System.out.println("Header type: " + unsignedByteToInt(headerType[0]));
			  // System.out.println("Length of this header: " + headerSize);
			  // for image structure headers, pull out image size
			  if (Util.unsignedByteToInt(headerType[0]) == HEADER_TYPE_IMAGE_STRUCTURE) {
				  imageSegmentLines[i] = bytesToShort(header, 3);
				  imageSegmentElements[i] = bytesToShort(header, 1);
				  imageBitsPerPixel[i] = Util.unsignedByteToInt(header[0]);
				  // System.out.println("Image bits per pixel: " + imageBitsPerPixel[i]);
				  // System.out.println("Image #Lines: " + imageSegmentLines[i] + ", #Elements: " + imageSegmentElements[i]);
			  }
			  // for navigation headers, print relevant data
			  if (Util.unsignedByteToInt(headerType[0]) == HEADER_TYPE_IMAGE_NAVIGATION) {
				  String projectionName = new String(header, 0, 32);
				  projectionName = projectionName.trim();
				  columnScalingFactor = bytesToInt(header, 32);
				  if (columnScalingFactor < 0) columnScalingFactor = -columnScalingFactor;
				  lineScalingFactor = bytesToInt(header, 36);
				  if (lineScalingFactor < 0) lineScalingFactor = -lineScalingFactor;
				  columnOffset = bytesToInt(header, 40);
				  lineOffset[i] = bytesToInt(header, 44);
				  // keep track of minimum line offset seen
				  if (minLineOffset > lineOffset[i]) {
					  minLineOffset = lineOffset[i];
				  }
				  // System.out.println("Projection: " + projectionName + 
				  //	  ", lsf: " + lineScalingFactor + ", csf: " + columnScalingFactor +
				  //	  ", co: " + columnOffset + ", lo: " + lineOffset[i]);
			  }
		  }
		  
		  fis.close();
	  }
	  
	  // make the VisAD RealTypes for the dimension variables
	  RealType line = RealType.getRealType("ImageLine", null, null);
	  RealType element = RealType.getRealType("ImageElement", null, null);
	  
	  // the domain is (element,line) since elements (X) vary fastest
	  RealType[] domainComponents = {element, line};
	  int resMultiplier = 3;
	  if (filenames[0].contains("HRV")) {
		  resMultiplier = 1;
	  }
	  int [] iparms = new int[6];
	  iparms[0] = AREAnav.GEOS;
	  iparms[1] = columnOffset * resMultiplier * 10;
	  iparms[2] = columnOffset * resMultiplier * 10;	  
	  iparms[3] = lineScalingFactor * resMultiplier * 10;
	  iparms[4] = columnScalingFactor * resMultiplier * 10;
	  // XXX FIXME TJJ - hardcoding for now: 0 for MSG.  Should be able
	  // to pull this out of the signal/segment data
	  iparms[5] = 0;
	  int [] dir = new int[64];
	  //dir[5] = resMultiplier * minLineOffset;
	  dir[5] = resMultiplier * (minLineOffset - 464) + 5568 + 1;
	  if (filenames[0].contains("HRV")) {
		  //dir[6] = 11136 - ((resMultiplier * columnOffset) + 5568);
		  dir[6] = columnOffset + 1;
	  } else {
		  dir[6] = 1;
	  }
	  dir[8] = imageSegmentLines[0] * filenames.length;
	  dir[9] = imageSegmentElements[0];
	  dir[11] = resMultiplier;
	  dir[12] = resMultiplier;
	  CoordinateSystem cs = new HRITCoordinateSystem(iparms, dir, false);
	  RealTupleType imageDomain = new RealTupleType(domainComponents, cs, null);
	  
	  // create calibration object
	  double [][] calBlock = makeMSGCal(filenames[0]);
	  CalibratorMsg cmsg = null;
	  try {
	    cmsg = new CalibratorMsg(calBlock);
	  } catch (CalibratorException ce) {
		ce.printStackTrace();
	  }
	  
	  //  Image numbering is usually the first line is at the "top"
	  //  whereas in VisAD, it is at the bottom.  So define the
	  //  domain set of the FlatField to map the Y axis accordingly

	  Linear2DSet domainSet = new Linear2DSet(imageDomain,
	                                0, (imageSegmentElements[0] - 1), imageSegmentElements[0] / magFactor,
	                                ((imageSegmentLines[0] * filenames.length) - 1), 
	                                0, (imageSegmentLines[0] * filenames.length) / magFactor);
	  // the range of the FunctionType is the band(s)
	  int numBands = 1;
	  RealType[] bands = new RealType[numBands];
	  bands[0] = RealType.getRealType("Band" + bandNum);
	  RealTupleType rtt = new RealTupleType(bands);
	  FunctionType imageType = new FunctionType(imageDomain, rtt);
	  Unit[] rangeUnits = null;
	  field = new FlatField (
		imageType,
        domainSet,
        (CoordinateSystem) null, null,
        rangeUnits
      );
	  
	  for (int i = 0; i < filenames.length; i++) {
		  
		  // open a stream to the file 
		  File f = new File(filenames[i]);
		  FileInputStream fis = new FileInputStream(f);
		  fis.skip(lengthAllHeaders[i]);
		  
		  // if we found 10 bit data, we'll need to allocate input and output arrays to decompress
		  tenBitInputArray = new byte[(int) f.length() - lengthAllHeaders[i] + 2];
		  tenBitOutputArray = new short[imageSegmentLines[i] * imageSegmentElements[i]];
		  
		  double[][] samples = new double[numBands][imageSegmentElements[i]/magFactor * imageSegmentLines[i]/magFactor];
		  byte[] sampleTwoByte = new byte[2];
		  byte[] sampleOneByte = new byte[1];
		  
		  // set samples for one or two byte data
		  if (imageBitsPerPixel[i] != 10) {
			  for (int b = 0; b < numBands; b++) {
				  for (int l=0; l < imageSegmentLines[i]; l++) {
					  for (int j=0; j < imageSegmentElements[i]; j++) {
						  if (imageBitsPerPixel[i] == 16) {
							  fis.read(sampleTwoByte);
							  samples[b][j + (imageSegmentElements[i] * l) ] = 
								  (float) (bytesToShort(sampleTwoByte, 0));
						  } else {
							  fis.read(sampleOneByte);
							  samples[b][j + (imageSegmentElements[i] * l) ] = 
								  (float) (Util.unsignedByteToInt(sampleOneByte[0]));
						  }
					  }
				  }
			  }
		  } else {
			  int numRead = fis.read(tenBitInputArray, 0, tenBitInputArray.length - 2);
			  // System.out.println("Count wanted: " + (tenBitInputArray.length - 2) + " , count got: " + numRead);
			  if (numRead == tenBitInputArray.length - 2) {
				int convert = Util.tenBitToTwoByte(tenBitInputArray, tenBitOutputArray);
				if (convert == 0) {
				// System.out.println("10 bit to 16 bit conversion successful!");
					  int idx = 0;
					  for (int b = 0; b < numBands; b++) {
						  for (int l = imageSegmentLines[i]/magFactor - 1; l >= 0; l--) {
							  for (int j = imageSegmentElements[i]/magFactor - 1; j >= 0; j--) {
								  samples[b][j + ((imageSegmentElements[i]/magFactor) * l) ] = 
									  cmsg.calibrateFromRaw((float) (tenBitOutputArray[idx]), bandNum, calType);
								  idx += magFactor;
							  }
							  idx += imageSegmentElements[i] * (magFactor - 1);
						  }
					  }
				}
			  }
		  }
		  field.setSamples((samples[0].length * (filenames.length - (i + 1))), samples);
		  fis.close();
		  
	  }
	  
  }
  
  /**
   * Attempt to build a McIDAS-style calibration block.
   * If unsuccessful, a warning is popped up that calibration 
   * will be approximated, and should not be considered accurate.
   * @param s an image segment file name for the data request
   * @return a McIDAS-style calibration block
   */
  
  private double[][] makeMSGCal(String s) {
	  
	  double [][] msgCal = new double[12][6];
	  double [] waveNumMSG1  = new double[12];
	  double [] waveNumMSG2  = new double[12];
	  double [] waveNumMSG3  = new double[12];
	  double [] alphaMSG1    = new double[12];
	  double [] alphaMSG2    = new double[12];
	  double [] alphaMSG3    = new double[12];
	  double [] betaMSG1     = new double[12];
	  double [] betaMSG2     = new double[12];
	  double [] betaMSG3     = new double[12];
	  double [] gain     = new double[12];
	  double [] offset   = new double[12];

	  // various constants.  I know this doesn't look good... for now we are
	  // only covering MSG-1 and MSG-2.  Needs work, but at present this is
	  // no different than the core McIDAS and ADDE server code!
	  waveNumMSG1[0] = 0.0d;
	  waveNumMSG1[1] = 0.0d;
	  waveNumMSG1[2] = 0.0d;
	  waveNumMSG1[3] = 2567.33d;
	  waveNumMSG1[4] = 1598.103d;
	  waveNumMSG1[5] = 1362.081d;
	  waveNumMSG1[6] = 1149.069d;
	  waveNumMSG1[7] = 1034.343d;
	  waveNumMSG1[8] = 930.647d;
	  waveNumMSG1[9] = 839.660d;
	  waveNumMSG1[10] = 752.387d;
	  waveNumMSG1[11] = 0.0d;
	  
	  waveNumMSG2[0] = 0.0d;
	  waveNumMSG2[1] = 0.0d;
	  waveNumMSG2[2] = 0.0d;
	  waveNumMSG2[3] = 2568.832d;
	  waveNumMSG2[4] = 1600.548d;
	  waveNumMSG2[5] = 1360.330d;
	  waveNumMSG2[6] = 1148.620d;
	  waveNumMSG2[7] = 1035.289d;
	  waveNumMSG2[8] = 931.700d;
	  waveNumMSG2[9] = 836.445d;
	  waveNumMSG2[10] = 751.792d;
	  waveNumMSG2[11] = 0.0d;
	  
	  waveNumMSG3[0] = 0.0d;
	  waveNumMSG3[1] = 0.0d;
	  waveNumMSG3[2] = 0.0d;
	  waveNumMSG3[3] = 2547.771d;
	  waveNumMSG3[4] = 1595.621d;
	  waveNumMSG3[5] = 1360.377d;
	  waveNumMSG3[6] = 1148.130d;
	  waveNumMSG3[7] = 1034.715d;
	  waveNumMSG3[8] = 929.842d;
	  waveNumMSG3[9] = 838.659d;
	  waveNumMSG3[10] = 751.792d;
	  waveNumMSG3[11] = 0.0d;

	  alphaMSG1[0] = 0.0d;
	  alphaMSG1[1] = 0.0d;
	  alphaMSG1[2] = 0.0d;
	  alphaMSG1[3] = 0.9956d;
	  alphaMSG1[4] = 0.9962d;
	  alphaMSG1[5] = 0.9991d;
	  alphaMSG1[6] = 0.9996d;
	  alphaMSG1[7] = 0.9999d;
	  alphaMSG1[8] = 0.9983d;
	  alphaMSG1[9] = 0.9988d;
	  alphaMSG1[10] = 0.9981d;
	  alphaMSG1[11] = 0.0d;
	  
	  alphaMSG2[0] = 0.0d;
	  alphaMSG2[1] = 0.0d;
	  alphaMSG2[2] = 0.0d;
	  alphaMSG2[3] = 0.9954d;
	  alphaMSG2[4] = 0.9963d;
	  alphaMSG2[5] = 0.9991d;
	  alphaMSG2[6] = 0.9996d;
	  alphaMSG2[7] = 0.9999d;
	  alphaMSG2[8] = 0.9983d;
	  alphaMSG2[9] = 0.9988d;
	  alphaMSG2[10] = 0.9981d;
	  alphaMSG2[11] = 0.0d;
	  
	  alphaMSG3[0] = 0.0d;
	  alphaMSG3[1] = 0.0d;
	  alphaMSG3[2] = 0.0d;
	  alphaMSG3[3] = 0.9915d;
	  alphaMSG3[4] = 0.9960d;
	  alphaMSG3[5] = 0.9991d;
	  alphaMSG3[6] = 0.9996d;
	  alphaMSG3[7] = 0.9999d;
	  alphaMSG3[8] = 0.9983d;
	  alphaMSG3[9] = 0.9988d;
	  alphaMSG3[10] = 0.9982d;
	  alphaMSG3[11] = 0.0d;

	  betaMSG1[0] = 0.0d;
	  betaMSG1[1] = 0.0d;
	  betaMSG1[2] = 0.0d;
	  betaMSG1[3] = 3.410d;
	  betaMSG1[4] = 2.218d;
	  betaMSG1[5] = 0.478d;
	  betaMSG1[6] = 0.179d;
	  betaMSG1[7] = 0.060d;
	  betaMSG1[8] = 0.625d;
	  betaMSG1[9] = 0.397d;
	  betaMSG1[10] = 0.578d;
	  betaMSG1[11] = 0.0d;
	  
	  betaMSG2[0] = 0.0d;
	  betaMSG2[1] = 0.0d;
	  betaMSG2[2] = 0.0d;
	  betaMSG2[3] = 3.438d;
	  betaMSG2[4] = 2.185d;
	  betaMSG2[5] = 0.470d;
	  betaMSG2[6] = 0.179d;
	  betaMSG2[7] = 0.056d;
	  betaMSG2[8] = 0.640d;
	  betaMSG2[9] = 0.408d;
	  betaMSG2[10] = 0.561d;
	  betaMSG2[11] = 0.0d;
	  
	  betaMSG3[0] = 0.0d;
	  betaMSG3[1] = 0.0d;
	  betaMSG3[2] = 0.0d;
	  betaMSG3[3] = 2.9002d;
	  betaMSG3[4] = 2.0337d;
	  betaMSG3[5] = 0.4340d;
	  betaMSG3[6] = 0.1714d;
	  betaMSG3[7] = 0.0527d;
	  betaMSG3[8] = 0.6084d;
	  betaMSG3[9] = 0.3882d;
	  betaMSG3[10] = 0.5390d;
	  betaMSG3[11] = 0.0d;
	  
	  // initialize with approximate values - this will get you a
	  // pretty picture but should not be considered accurate
	  gain[0] = 0.2331010000E-01d;
	  gain[1] = 0.2540430000E-01d;
	  gain[2] = 0.2187850000E-01d;
	  gain[3] = 0.3742751227E-02d;
	  gain[4] = 0.4641033727E-01d;
	  gain[5] = 0.8197182308E-01d;
	  gain[6] = 0.1256206112E+00d;
	  gain[7] = 0.1523276370E+00d;
	  gain[8] = 0.1959369086E+00d;
	  gain[9] = 0.2145945762E+00d;
	  gain[10] = 0.2091678681E+00d;
	  gain[11] = 0.2800300000E-01d;
	  
	  offset[0] = -0.1188810000E+01d;
	  offset[1] = -0.1295620000E+01d;
	  offset[2] = -0.1115800000E+01d;
	  offset[3] = -0.1908803126E+00d;
	  offset[4] = -0.2366927201E+01d;
	  offset[5] = -0.4180562977E+01d;
	  offset[6] = -0.6406651170E+01d;
	  offset[7] = -0.7768709489E+01d;
	  offset[8] = -0.9992782340E+01d;
	  offset[9] = -0.1094432338E+02d;
	  offset[10] = -0.1066756128E+02d;
	  offset[11] = -0.1428150000E+01d;	 
	  
	  // for now, assume we calibrate based on MSG-2, unless we detect otherwise
	  int scId = SPACECRAFT_ID_MSG2;
	  
	  // now the real work - try to convert the image segment file name
	  // to an MSG prologue file name (file with the cal slopes and offsets)
	  boolean accurateCal = false;
	  // to build the filename for the matching prologue file, swap out channel and segment
	  // number sections with underscores and -PRO
	  String plFileName = s.replaceFirst("......___-0000\\d\\d___", "_________-PRO______");
	  File f = new File(plFileName);
	  try {

		  FileInputStream fis = new FileInputStream(f);
		  // try to pull out the primary header
		  byte [] primaryHeader = new byte[PRIMARY_HEADER_LENGTH];
		  int bytesRead = fis.read(primaryHeader);
		  if ((bytesRead < 0) || (bytesRead != PRIMARY_HEADER_LENGTH)) {
			  fis.close();
			  throw new IOException("File " + s + " is not an HRIT file");
		  }
		  // validate primary header contents
		  int headerSize = bytesToShort(primaryHeader, 1);
		  if (headerSize != PRIMARY_HEADER_LENGTH) {
			  fis.close();
			  throw new IOException("File " + s + " is not a valid HRIT file");
		  }
		  // make sure file is at least as long as the claimed length of all headers
		  int lengthAllHeaders = -1;
		  lengthAllHeaders = bytesToInt(primaryHeader, 4);
		  if (f.length() < lengthAllHeaders) {
			  fis.close();
			  throw new IOException("File " + s + " is not a valid HRIT file");
		  }
		  // ok, we got the primary header, moving along to the other headers...
		  int headerBytesConsumed = PRIMARY_HEADER_LENGTH;
		  byte [] headerType = new byte[1];
		  byte [] headerLength = new byte[2];
		  while (headerBytesConsumed < lengthAllHeaders) {
			  bytesRead = fis.read(headerType);
			  headerBytesConsumed += bytesRead;
			  bytesRead = fis.read(headerLength);
			  headerBytesConsumed += bytesRead;
			  headerSize = bytesToShort(headerLength, 0);
			  byte [] header = new byte[headerSize - 3];
			  bytesRead = fis.read(header);
			  headerBytesConsumed += bytesRead;
		  }
		  // two-byte utility array for pulling out shorts
		  byte [] b2 = new byte[2];
		  // spacecraft id - will be used to further improve cal, as time permits
		  fis.read(b2);
		  scId = bytesToShort(b2, 0);
		  long n = fis.skip((SAT_STAT_LEN - 2) + IMG_ACQ_LEN + CEL_EVENTS_LEN + IMG_DESC_LEN + CAL_OFFS);
		  if (n != (SAT_STAT_LEN - 2) + IMG_ACQ_LEN + CEL_EVENTS_LEN + IMG_DESC_LEN + CAL_OFFS) {
			  fis.close();
			  throw new IOException("Failed to read calibration coefficients, corrupt file?");
		  }
		  for (int i = 0; i < 12; i++) {
			  byte [] d1 = new byte[8];
			  byte [] d2 = new byte[8];
			  int count = fis.read(d1);
			  if (count != 8) {
				  fis.close();
				  throw new IOException("Failed to read calibration coefficients, corrupt file?");
			  }
			  count = fis.read(d2);
			  if (count != 8) {
				  fis.close();
				  throw new IOException("Failed to read calibration coefficients, corrupt file?");
			  }
			  long l1 = bytesToLong(d1, 0);
			  long l2 = bytesToLong(d2, 0);
			  gain[i] = Double.longBitsToDouble(l1);
			  offset[i] = Double.longBitsToDouble(l2);
			  // TODO: should probably add a sanity check on gain/offset values,
			  // to make sure we found and will be using reasonable numbers.
		  }
		  // if we got this far, assume we have accurate calibration coefficients
		  accurateCal = true;
		  fis.close();

	  } catch (FileNotFoundException e) {
		  // Do nothing - we just won't have accurate calibration
	  } catch (IOException e) {
		  // Do nothing - we just won't have accurate calibration
	  }
	  
	  if (! accurateCal) {
		  System.err.println("WARNING: Data will be displayed, but calibration is approximate.");
	  }

	  double w  = 0.0d;
	  double c1w3 = 0.0d;
	  double c2w = 0.0d;
	  double PLANCK = 6.626176E-34;
	  double LIGHT  = 2.99792458E8;
	  double BOLTZMAN = 1.380662E-23;
	  double c1 = 2.0E5 * PLANCK * (LIGHT * LIGHT);
	  double c2 = PLANCK * LIGHT / BOLTZMAN;
	  for (int band = 0; band < 12; band++) {	 
		  if (scId == SPACECRAFT_ID_MSG2) {
			  w  = 1.0E2 * waveNumMSG2[band];
			  msgCal[band][2] = alphaMSG2[band];
			  msgCal[band][3] = betaMSG2[band];
		  } else if (scId == SPACECRAFT_ID_MSG3) {
			  w  = 1.0E2 * waveNumMSG3[band];
			  msgCal[band][2] = alphaMSG3[band];
			  msgCal[band][3] = betaMSG3[band];
		  } else {
		      w  = 1.0E2 * waveNumMSG1[band];
		      msgCal[band][2] = alphaMSG1[band];
		      msgCal[band][3] = betaMSG1[band];
		  }
	      c1w3 = c1 * w * w * w;
	      c2w  = c2 * w;
	      msgCal[band][0] = c1w3;
	      msgCal[band][1] = c2w;
	      msgCal[band][4] = gain[band];
	      msgCal[band][5] = offset[band];
	  }
	  
	  return msgCal;
}

  private void dumpHeader(byte [] header) {
	  if ((header != null) && (header.length >= 3)) {
		  System.out.println("Header type: " + header[0]);
		  System.out.println("Length of this header: " + bytesToShort(header, 1));
	  }

	  switch (header[0]) {
	  case HEADER_TYPE_PRIMARY_HEADER:
		  System.out.println("Length of all headers: " + bytesToInt(header, 4));
		  break;
	  default:
		  break;
	  }
  }

  public FlatField getData() {
	  return field;
  }
  
  /**
   * Converts a set of four consecutive bytes into a single long.
   * @param data An array of bytes
   * @param offset The array index to begin with
   * @return The resulting int
   */
  public static long bytesToLong(byte[] data, int offset) {
          long l = 0;
          l += Util.unsignedByteToLong(data[offset + 0]) << 56;
          l += Util.unsignedByteToLong(data[offset + 1]) << 48;
          l += Util.unsignedByteToLong(data[offset + 2]) << 40;
          l += Util.unsignedByteToLong(data[offset + 3]) << 32;
          l += Util.unsignedByteToLong(data[offset + 4]) << 24;
          l += Util.unsignedByteToLong(data[offset + 5]) << 16;
          l += Util.unsignedByteToLong(data[offset + 6]) << 8;
          l += Util.unsignedByteToLong(data[offset + 7]);
          return l;
  }

  /**
   * Converts a set of four consecutive bytes into a single int.
   * @param data An array of bytes
   * @param offset The array index to begin with
   * @return The resulting int
   */
  public static int bytesToInt(byte[] data, int offset) {
          int i = 0;
          i += Util.unsignedByteToInt(data[offset]) << 24;
          i += Util.unsignedByteToInt(data[offset + 1]) << 16;
          i += Util.unsignedByteToInt(data[offset + 2]) << 8;
          i += Util.unsignedByteToInt(data[offset + 3]);
          return i;
  }

  /**
   * Converts a set of two consecutive bytes into a single int.
   * @param data An array of bytes
   * @param offset The array index to begin with
   * @return The resulting int
   */
  public static int bytesToShort(byte[] data, int offset) {
          int i = 0;
          i += Util.unsignedByteToInt(data[offset]) << 8;
          i += Util.unsignedByteToInt(data[offset + 1]);
          return i;
  }
  
}

 

Ads help maintain this website.