// // 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; } }