package edu.colostate.vchill.netcdf;
import edu.colostate.vchill.ChillDefines;
import edu.colostate.vchill.ControlMessage;
import edu.colostate.vchill.ScaleManager;
import edu.colostate.vchill.cache.CacheMain;
import edu.colostate.vchill.chill.*;
import edu.colostate.vchill.file.FileFunctions;
import edu.colostate.vchill.file.FileFunctions.Moment;
import ucar.ma2.Array;
import ucar.ma2.Index;
import ucar.nc2.Attribute;
import ucar.nc2.Dimension;
import ucar.nc2.NetcdfFile;
import ucar.nc2.Variable;
import java.io.IOException;
/**
* Class for reading NCAR NetCDF archive files
*
* @author Jochen Deyke
* @author jpont
* @version 2010-08-30
*/
public class NCARNetCDFFile {
private static final ScaleManager sm = ScaleManager.getInstance();
public static final ChillFieldInfo AVG_I = new ChillFieldInfo("AVG_I", "average inphase measurement", 1, 32767, -32768, 0, 0);
public static final ChillFieldInfo AVG_Q = new ChillFieldInfo("AVG_Q", "average quadrature measurement", 2, 32767, -32768, 0, 0);
public static final ChillFieldInfo DBZ = new ChillFieldInfo("DBZ", "reflectivity", 12, 7500, -1000, 0, 0);
public static final ChillFieldInfo VE = new ChillFieldInfo("VE", "velocity", 13, 5500, -5500, 0, 1);
public static final ChillFieldInfo SW = new ChillFieldInfo("SW", "spectral width", 14, 5500, -5500, 0, 2);
public static final ChillFieldInfo NCP = new ChillFieldInfo("NCP", "normalized coherent power", 15, 100, 0, 0, 3);
public static final ChillFieldInfo DM = new ChillFieldInfo("DM", "power", 33, 5000, -20000, 0, 0);
public static final ChillFieldInfo AIQ = new ChillFieldInfo("AIQ", "I & Q phase", 34, 18000, -18000, 0, 0);
public static final ChillFieldInfo NIQ = new ChillFieldInfo("NIQ", "I & Q magnitude", 35, 8000, -8000, 0, 0);
public static final ChillFieldInfo DELTA_N = new ChillFieldInfo("DELTA_N", "DELTA_N", 36, 3000, -3000, 0, 0); //fix long name
public static final ChillFieldInfo N = new ChillFieldInfo("N", "N", 37, 50000, 10000, 0, 0); //fix long name
public static final ChillFieldInfo SIGMA_DN = new ChillFieldInfo("SIGMA_DN", "SIGMA_DN", 38, 3000, 0, 0, 0); //fix long name
public static final ChillFieldInfo SIGMA_N = new ChillFieldInfo("SIGMA_N", "SIGMA_N", 39, 3000, 0, 0, 0); //fix long name
private static final ChillFieldInfo[] types = new ChillFieldInfo[]{
AVG_I, AVG_Q, DBZ, VE, SW, NCP, DM, AIQ, NIQ, DELTA_N, N, SIGMA_DN, SIGMA_N,
};
public static void load(final ControlMessage command, final CacheMain cache) throws IOException {
String path = FileFunctions.stripFileName(command.getDir()) + "/" + FileFunctions.stripFileName(command.getFile());
NetcdfFile ncFile = NetcdfFile.open(path);
Dimension radial = ncFile.hasUnlimitedDimension() ?
ncFile.getUnlimitedDimension() : ncFile.getRootGroup().findDimension("Time");
Dimension gate = ncFile.getRootGroup().findDimension("maxCells");
ChillMomentFieldScale[] types = null;
Variable fields = ncFile.findVariable("fields");
if (fields == null) fields = ncFile.findVariable("field_names");
if (fields == null) { //use hardcoded list
types = new ChillMomentFieldScale[NCARNetCDFFile.types.length];
for (int i = 0; i < types.length; ++i) {
if (NCARNetCDFFile.types[i].fieldNumber < Moment.values().length) {
Moment type = Moment.values()[NCARNetCDFFile.types[i].fieldNumber];
types[i] = new ChillMomentFieldScale(NCARNetCDFFile.types[i], type.ACCELERATOR, type.UNITS, 100, 0, 0);
} else {
types[i] = new ChillMomentFieldScale(NCARNetCDFFile.types[i], -1, null, 100, 1, 0);
}
cache.addRay(command, ChillDefines.META_TYPE, types[i]);
sm.putScale(types[i]);
}
} else {
char[][] fieldnames = (char[][]) fields.read().copyToNDJavaArray();
types = new ChillMomentFieldScale[fieldnames.length];
for (int fieldI = 0; fieldI < fieldnames.length; ++fieldI) {
String name = new String(fieldnames[fieldI]);
//System.out.println("Found field '" + name + "'");
name = name.trim();
//System.out.println(" trimmed '" + name + "'");
Variable field = ncFile.findVariable(name);
if (field == null) continue; //should not be possible
String description = field.getDescription();
//System.out.println(" is " + description);
//String units = field.findAttribute("units").getStringValue().trim();
String units = field.getUnitsString();
//System.out.println(" in " + units);
ChillFieldInfo info = new ChillFieldInfo(name, description, fieldI, 32767, -32768, 0, 0);
types[fieldI] = new ChillMomentFieldScale(info, -1, units, 100, 1, 0);
cache.addRay(command, ChillDefines.META_TYPE, types[fieldI]);
sm.putScale(types[fieldI]);
}
}
Array azimuth = ncFile.findVariable("Azimuth").read();
Array elevation = ncFile.findVariable("Elevation").read();
int baseTime = ncFile.findVariable("base_time").readScalarInt();
Array time = ncFile.findVariable("time_offset").read();
//Array timenSec = ncFile.findVariable("TimenSec").read();
int startRange = (int) (1e3 * ncFile.findVariable("Range_to_First_Cell").readScalarFloat());
Array[] data = new Array[types.length];
double[] missing = new double[types.length];
double[] scale = new double[types.length];
double[] offset = new double[types.length];
long availableData = 0;
for (int typeI = 0; typeI < types.length; ++typeI) {
if (types[typeI] == null) continue; //couldn't read scaling info
Variable var = ncFile.findVariable(types[typeI].fieldName);
if (var == null) var = ncFile.findVariable(types[typeI].fieldDescription); //retry with long name
if (var == null) continue; //type not available
data[typeI] = var.read();
availableData |= 1l << types[typeI].fieldNumber;
missing[typeI] = var.findAttribute("missing_value").getNumericValue().doubleValue();
Attribute sf = var.findAttribute("scale_factor");
scale[typeI] = sf == null ? 1 : sf.getNumericValue().doubleValue();
Attribute ao = var.findAttribute("add_offset");
offset[typeI] = ao == null ? 0 : ao.getNumericValue().doubleValue();
}
ChillHSKHeader hskH = new ChillHSKHeader();
if (ncFile.findGlobalAttribute("Scan_Mode").getStringValue().equals("RHI")) hskH.antMode = 1;
hskH.radarLatitude = (int) (1e6 * ncFile.findVariable("Latitude").readScalarDouble());
hskH.radarLongitude = (int) (1e6 * ncFile.findVariable("Longitude").readScalarDouble());
hskH.gateWidth = (int) (1e3 * ncFile.findVariable("Cell_Spacing").readScalarFloat());
hskH.radarId = ncFile.findGlobalAttribute("Instrument_Name").getStringValue();
hskH.angleScale = 0x7fffffff;
cache.addRay(command, ChillDefines.META_TYPE, hskH);
for (int radialI = 0; radialI < radial.getLength(); ++radialI) {
ChillDataHeader dataH = new ChillDataHeader();
Index i1 = azimuth.getIndex().set(radialI);
dataH.availableData = availableData;
dataH.startAz = dataH.endAz = (int) (azimuth.getDouble(i1) / 360 * hskH.angleScale);
dataH.startEl = dataH.endEl = (int) (elevation.getDouble(i1) / 360 * hskH.angleScale);
dataH.numGates = gate.getLength();
dataH.startRange = startRange;
double t = time.getDouble(i1);
dataH.dataTime = baseTime + (long) t;
dataH.fractionalSecs = (int) ((t - (long) t) * 1e9);
cache.addRay(command, ChillDefines.META_TYPE, dataH);
for (int typeI = 0; typeI < types.length; ++typeI) {
if (data[typeI] == null) continue;
Index i2 = data[typeI].getIndex().set0(radialI);
double[] typeData = new double[dataH.numGates];
for (int gateI = 0; gateI < dataH.numGates; ++gateI) {
double value = data[typeI].getDouble(i2.set1(gateI));
if (value == missing[typeI]) typeData[gateI] = Double.NaN;
else typeData[gateI] = scale[typeI] * (value - offset[typeI]);
}
cache.addRay(command, types[typeI].fieldName, new ChillGenRay(hskH, dataH, types[typeI].fieldName, typeData));
}
}
for (String type : sm.getTypes()) {
cache.setCompleteFlag(command, type);
System.out.println("marked " + type + " complete; cached " + cache.getNumberOfRays(command, type) + " rays");
}
cache.setCompleteFlag(command, ChillDefines.META_TYPE);
ncFile.close();
}
}