/* * Copyright 1998-2009 University Corporation for Atmospheric Research/Unidata * * Portions of this software were developed by the Unidata Program at the * University Corporation for Atmospheric Research. * * Access and use of this software shall impose the following obligations * and understandings on the user. The user is granted the right, without * any fee or cost, to use, copy, modify, alter, enhance and distribute * this software, and any derivative works thereof, and its supporting * documentation for any purpose whatsoever, provided that this entire * notice appears in all copies of the software, derivative works and * supporting documentation. Further, UCAR requests that the user credit * UCAR/Unidata in any publications that result from the use of this * software or in any product that includes this software. The names UCAR * and/or Unidata, however, may not be used in any advertising or publicity * to endorse or promote any products or commercial entity unless specific * written permission is obtained from UCAR/Unidata. The user also * understands that UCAR/Unidata is not obligated to provide the user with * any support, consulting, training or assistance of any kind with regard * to the use, operation and performance of this software nor to provide * the user with any updates, revisions, new versions or "bug fixes." * * THIS SOFTWARE IS PROVIDED BY UCAR/UNIDATA "AS IS" AND ANY EXPRESS OR * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE * DISCLAIMED. IN NO EVENT SHALL UCAR/UNIDATA BE LIABLE FOR ANY SPECIAL, * INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING * FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, * NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION * WITH THE ACCESS, USE OR PERFORMANCE OF THIS SOFTWARE. */ package ucar.nc2.iosp.uf; import ucar.nc2.constants.CDM; import ucar.unidata.io.RandomAccessFile; import ucar.ma2.Range; import ucar.ma2.IndexIterator; import java.nio.ByteBuffer; import java.util.*; import java.io.IOException; public class Ray { int raySize; long rayOffset; static final int UF_MANDATORY_HEADER2_LEN = 90; static final int UF_FIELD_HEADER2_LEN = 50; boolean debug = false; /** moment identifier */ long data_msecs = 0; UF_mandatory_header2 uf_header2; UF_optional_header uf_opt_header; short numberOfFields; // in this ray short numberOfRecords; // in this ray short numberOfFieldsInRecord; // in this record Map<String, UF_field_header2> field_header_map; // key = 2 byte "data type" public Ray( ByteBuffer bos, int raySize, long rayOffset) { this.raySize = raySize; this.rayOffset = rayOffset; field_header_map = new HashMap<>(); bos.position(0); byte[] data = new byte[UF_MANDATORY_HEADER2_LEN]; bos.get(data); uf_header2 = new UF_mandatory_header2(data); if(uf_header2.offset2StartOfOptionalHeader > 0 && (uf_header2.dataHeaderPosition != uf_header2.offset2StartOfOptionalHeader)){ data = new byte[28]; bos.get(data); uf_opt_header = new UF_optional_header(data); } int position = uf_header2.dataHeaderPosition*2 -2; bos.position(position); data_msecs = setDateMesc(); byte [] b2 = new byte[2]; bos.get(b2); numberOfFields = getShort(b2, 0); bos.get(b2); numberOfRecords = getShort(b2, 0); bos.get(b2); numberOfFieldsInRecord = getShort(b2, 0); data = new byte[UF_FIELD_HEADER2_LEN]; for(int i = 0; i < numberOfFields; i++){ bos.get(b2); //int type = getShort(b2, 0); String type = new String(b2, CDM.utf8Charset); bos.get(b2); int offs = getShort(b2, 0); int position0 = bos.position(); bos.position(offs*2 - 2); bos.get(data); UF_field_header2 field_header = new UF_field_header2(data); bos.position(position0); field_header_map.put(type, field_header); } } public int getRaySize(){ return raySize; } public int getGateCount(String abbrev) { UF_field_header2 header = field_header_map.get(abbrev); return header.binCount; } public String getDatatypeName(String abbrev) { if( abbrev.equals("ZN") || abbrev.equals("ZS")) return "Reflectivity"; else if (abbrev.equals("ZF") || abbrev.equals("ZX") || abbrev.equals("DR")) return "Reflectivity"; else if(abbrev.equals("VR") || abbrev.equals("DN") || abbrev.equals("DS") || abbrev.equals("DF") ||abbrev.equals("DX") ) return "RadialVelocity"; else if(abbrev.equals("VN") || abbrev.equals("VF") ) return "CorrectedRadialVelocity"; else if(abbrev.equals("SW") || abbrev.equals("WS") || abbrev.equals("WF") || abbrev.equals("WX") || abbrev.equals("WN")) return "SpectrumWidth"; else if(abbrev.equals("PN") || abbrev.equals("PS") || abbrev.equals("PF") || abbrev.equals("PX")) return "Power"; else if(abbrev.equals("MN") || abbrev.equals("MS") || abbrev.equals("MF") || abbrev.equals("MX")) return "Power"; else if(abbrev.equals("PH")) return "PhiDP"; else if(abbrev.equals("RH")) return "RhoHV"; else if(abbrev.equals("LH")) return "LdrH"; else if(abbrev.equals("KD")) return "KDP"; else if(abbrev.equals("LV")) return "LdrV" ; else if(abbrev.equals("DR")) return "ZDR"; else if(abbrev.equals("CZ")) return "CorrecteddBZ"; else if(abbrev.equals("DZ")) return "TotalReflectivity"; else if(abbrev.equals("DR")) return "ZDR"; else return abbrev; } public String getDatatypeUnits(String abbrev) { if(abbrev.equals("CZ") || abbrev.equals("DZ") || abbrev.equals("ZN") || abbrev.equals("ZS")) return "dBz"; else if (abbrev.equals("ZF") || abbrev.equals("ZX")) return "dBz"; else if(abbrev.equals("VR") || abbrev.equals("DN") || abbrev.equals("DS") || abbrev.equals("DF") ||abbrev.equals("DX") ) return "m/s"; else if(abbrev.equals("VN") || abbrev.equals("VF") ) return "m/s"; else if(abbrev.equals("SW") || abbrev.equals("WS") || abbrev.equals("WF") || abbrev.equals("WX") || abbrev.equals("WN")) return "m/s"; else if(abbrev.equals("PN") || abbrev.equals("PS") || abbrev.equals("PF") || abbrev.equals("PX")) return "dBM"; else if(abbrev.equals("MN") || abbrev.equals("MS") || abbrev.equals("MF") || abbrev.equals("MX")) return "dBM"; else return abbrev; } public short getDatatypeRangeFoldingThreshhold(String abbrev) { UF_field_header2 header = field_header_map.get(abbrev); return header.thresholdValue; } public float getDatatypeScaleFactor(String abbrev) { UF_field_header2 header = field_header_map.get(abbrev); return 1.0f/header.scaleFactor; } public float getDatatypeAddOffset(String abbrev) { return 0.0f; } public int getGateStart(String abbrev) { UF_field_header2 header = field_header_map.get(abbrev); return header.startRange; } public int getDataOffset(String abbrev) { UF_field_header2 header = field_header_map.get(abbrev); return header.dataOffset; } public int getGateSize(String abbrev) { UF_field_header2 header = field_header_map.get(abbrev); return header.binSpacing; } public float getElevation(){ return uf_header2.elevation/64.f; } public float getAzimuth(){ return uf_header2.azimuth/64.f; } public short getMissingData() { return uf_header2.missing; } public int getYear() { return getYear(uf_header2.year) ; } public float getLatitude(){ return uf_header2.latitudeD + (uf_header2.latitudeM + uf_header2.latitudeS/(64*60.f))/60.f; } public float getLongtitude(){ return uf_header2.longitudeD + (uf_header2.longitudeM + uf_header2.longitudeS/(64*60.f))/60.f; } public float getHorizontalBeamWidth(String abbrev) { UF_field_header2 header = field_header_map.get(abbrev); return header.HorizontalBeamWidth/64.f; } public int getYear(int year) { if(year > 1970) return year; if( year > 70 && year < 100) return 1900+ year; if( year < 60) return 2000 + year; return 0; } public long getTitleMsecs() { return data_msecs; } public long setDateMesc() { Calendar cal; if(uf_header2.timeZone.equals("UT") || uf_header2.timeZone.equals("GM")) { cal = Calendar.getInstance(TimeZone.getTimeZone("UTC")); }else{ cal = Calendar.getInstance(); } cal.set(Calendar.YEAR, uf_header2.year); cal.set(Calendar.MONTH,uf_header2.month - 1 ); cal.set(Calendar.DAY_OF_MONTH, uf_header2.day ); cal.set(Calendar.HOUR_OF_DAY, uf_header2.hour); cal.set(Calendar.MINUTE, uf_header2.minute); cal.set(Calendar.SECOND, uf_header2.second); return cal.getTimeInMillis(); } public Date getDate() { Calendar cal = Calendar.getInstance(); cal.setTimeInMillis(data_msecs); return cal.getTime(); } class UF_mandatory_header2 { String textUF; short recordSize; // in 16-bit words short offset2StartOfOptionalHeader; //, origin 1 short localUseHeaderPosition; short dataHeaderPosition; short recordNumber; short volumeNumber; // on tape, n/a for disk short rayNumber; // within the volume scan short recordNumber1; // within ray (origin 1) short sweepNumber; // within the volume scan String radarName; // char[8] String siteName; // char[8] short latitudeD; // degrees (North positive, South negative) short latitudeM; // minutes short latitudeS; // seconds*64 short longitudeD; // degrees (East positive, West negative) short longitudeM; // Minutes short longitudeS; // Seconds short height; // of antenna above sea level in meters short year; // (time of data acquisition) short month ; short day; short hour; short minute; short second; String timeZone; // UT for universal char[2] short azimuth; // (degrees*64) of midpoint of sample short elevation; // (degrees*64) short sweepMode; // 0:Cal 1:PPI 2:Coplane 3:RHI // 4:Vertical 5:Target 6:Manual 7:Idle short fixedAngle; // (degrees*64) short sweepRate; // ((degrees/second)*64) short year1; // (generation data of UF format) short month1; short day1; String nameOfUFGeneratorProgram; // char[8] short missing; // Value stored for deleted or missing data (0x8000) UF_mandatory_header2(byte[] data){ // data is of length 90 bytes textUF = new String(data, 0, 2, CDM.utf8Charset); if (debug){ System.out.println(textUF); } recordSize = getShort(data, 2); offset2StartOfOptionalHeader = getShort(data, 4); localUseHeaderPosition= getShort(data, 6); dataHeaderPosition= getShort(data, 8); recordNumber= getShort(data, 10); volumeNumber= getShort(data, 12); rayNumber= getShort(data, 14); recordNumber1= getShort(data, 16); sweepNumber= getShort(data, 18); radarName= new String(data, 20, 8, CDM.utf8Charset).trim(); siteName= new String(data, 28, 8, CDM.utf8Charset).trim(); latitudeD= getShort(data, 36); latitudeM= getShort(data, 38); latitudeS= getShort(data, 40); longitudeD= getShort(data, 42); longitudeM= getShort(data, 44); longitudeS= getShort(data, 46); height= getShort(data, 48); int yearValue= getShort(data, 50); year = (short)getYear(yearValue); month = getShort(data, 52); day= getShort(data, 54); hour= getShort(data, 56); minute= getShort(data, 58); second= getShort(data, 60); timeZone = new String(data, 62, 2, CDM.utf8Charset); azimuth= getShort(data, 64); elevation= getShort(data, 66); sweepMode= getShort(data, 68); fixedAngle= getShort(data, 70); // (degrees*64) sweepRate = getShort(data, 72); // ((degrees/second)*64) year1 = getShort(data, 74); // (generation data of UF format) month1 = getShort(data, 76); day1 = getShort(data, 78); nameOfUFGeneratorProgram = new String(data, 80, 8, CDM.utf8Charset); // char[8] missing = getShort(data, 88); // Value stored for deleted or missing data (0x8000) } } class UF_optional_header{ String sProjectName; // char[8] short iBaselineAzimuth; short iBaselineelevation; short iVolumeScanHour; /* Time of start of current volume scan */ short iVolumeScanMinute; short iVolumeScanSecond; String sFieldTapeName; // char[8] short iFlag; UF_optional_header(byte[] data){ sProjectName = new String(data, 0, 8, CDM.utf8Charset); iBaselineAzimuth = getShort(data, 8); iBaselineelevation = getShort(data, 10); iVolumeScanHour = getShort(data, 12); iVolumeScanMinute = getShort(data, 14); iVolumeScanSecond = getShort(data, 16); sFieldTapeName = new String(data, 18, 8, CDM.utf8Charset); iFlag = getShort(data, 26); } } class UF_field_header2 { short dataOffset; // from start of record, origin 1 short scaleFactor; // met units = file value/scale short startRange; // km short startRange1; // meters short binSpacing; // in meters short binCount; short pulseWidth; // in meters short HorizontalBeamWidth; // in degrees*64 short verticalBeamWidth; // in degrees*64 short receiverBandwidth; // in Mhz*64 ? short polarization; //: 1:horz 2:vert short waveLength; // in cm*64 short sampleSize; String typeOfData; //used to threshold // char[2] short thresholdValue; short scale; String editCode; // char[2] short prt; // in microseconds short bits; // per bin, must be 16 // 38 12 <uf_fsi2> UF_field_header2(byte[] data){ dataOffset = getShort(data, 0); scaleFactor = getShort(data, 2); startRange = getShort(data, 4); startRange1 = getShort(data, 6); binSpacing = getShort(data, 8); binCount = getShort(data, 10); pulseWidth = getShort(data, 12); HorizontalBeamWidth = getShort(data, 14); verticalBeamWidth =getShort(data, 16); receiverBandwidth = getShort(data, 18); polarization = getShort(data, 20); waveLength = getShort(data, 22); sampleSize = getShort(data, 24); typeOfData = new String(data, 26, 2, CDM.utf8Charset); thresholdValue = getShort(data, 28); scale = getShort(data, 30); editCode = new String(data, 32, 2, CDM.utf8Charset); prt = getShort(data, 34); bits = getShort(data, 36); } } protected short getShort(byte[] bytes, int offset) { // careful that we only allow sign extension on the highest order byte return (short) bytesToShort(bytes[offset], bytes[offset+1], false); } public static int bytesToShort(byte a, byte b, boolean swapBytes) { // again, high order bit is expressed left into 32-bit form if (swapBytes) { return (a & 0xff) + ((int)b << 8); } else { return ((int)a << 8) + (b & 0xff); } } public short[] byte2short(byte [] a, int length){ int len = length/2; short [] b = new short[len]; byte [] b2 = new byte[2]; for(int i = 0; i < len; i++){ b2[0] = a[2*i]; b2[1] = a[2*i + 1]; b[i] = getShort(b2, 0); } return b; } /** * Read data from this ray. * @param raf read from this file * @param abbrev which data type we want * @param gateRange handles the possible subset of data to return * @param ii put the data here * @throws java.io.IOException */ public void readData(RandomAccessFile raf, String abbrev, Range gateRange, IndexIterator ii) throws IOException { long offset = rayOffset; offset += (getDataOffset( abbrev) * 2 - 2) ; raf.seek(offset); byte[] b2 = new byte[2]; int dataCount = getGateCount( abbrev); byte[] data = new byte[dataCount*2]; raf.readFully(data); for (int i = gateRange.first(); i <= gateRange.last(); i += gateRange.stride()) { if (i >= dataCount) ii.setShortNext(uf_header2.missing); else { b2[0] = data[i*2]; b2[1] = data[i*2+1]; short value = getShort(b2, 0); ii.setShortNext(value); } } } }