ucar.nc2.iosp.uf.Ray Maven / Gradle / Ivy
The newest version!
/*
* Copyright (c) 1998-2018 John Caron and University Corporation for Atmospheric Research/Unidata
* See LICENSE for license information.
*/
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 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_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 gateIdx : gateRange) {
if (gateIdx >= dataCount)
ii.setShortNext(uf_header2.missing);
else {
b2[0] = data[gateIdx * 2];
b2[1] = data[gateIdx * 2 + 1];
short value = getShort(b2, 0);
ii.setShortNext(value);
}
}
}
}