package edu.colostate.vchill.file; 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.iris.IrisRawFile; import edu.colostate.vchill.iris.SigmetProductRaw; import edu.colostate.vchill.netcdf.CASANetCDFFile; import edu.colostate.vchill.netcdf.CFRadialFile; import edu.colostate.vchill.netcdf.NCARNetCDFFile; import edu.colostate.vchill.netcdf.WCRNetCDFFile; import java.awt.event.KeyEvent; import java.io.*; import java.net.URLDecoder; import java.net.URLEncoder; import java.text.DecimalFormat; import java.text.NumberFormat; import java.util.ArrayList; import java.util.Collection; import java.util.Collections; import java.util.List; /** * Class that has functions that return directory names and file names * * @author Joseph C. Hardin * @author Brian Eriksson * @author Jochen Deyke * @author jpont * @version 2010-09-02 * @created June 14, 2003 */ public final class FileFunctions { private static final ScaleManager sm = ScaleManager.getInstance(); private static final NumberFormat nf = new DecimalFormat("00"); /** * Information about various possible fields. */ public enum Moment { R0HH(2, "R0HH", null, null, -1, null), R0VV(2, "R0VV", null, null, -1, null), R0HV(2, "R0HV", null, null, -1, null), R0VH(2, "R0VH", null, null, -1, null), R0HVc(4, "R0HVc", null, null, -1, null), R0VHc( 4, "R0VHc", null, null, -1, null), R1HV(4, "R1HV", null, null, -1, null), R1VH(4, "R1VH", null, null, -1, null), R1VV(4, "R1VV", null, null, -1, null), R1HH(4, "R1HH", null, null, -1, null), R2HH(4, "R2HH", null, null, -1, null), R2VV(4, "R2VV", null, null, -1, null), Z(1, "Z", "Reflectivity", "dBz", KeyEvent.VK_Z, "Reflectivity"), V(1, "V", "Velocity", "m/s", KeyEvent.VK_V, "Velocity"), W(1, "W", "Spec. width", "m/s", KeyEvent.VK_W, "SpectralWidth"), NCP(1, "NCP", "Normalized Coherent Power", null, KeyEvent.VK_N, "NormalizedCoherentPower"), // fix netcdf ZDR(1, "ZDR", "Differential Reflectivity", "dB", KeyEvent.VK_D, "DifferentialReflectivity"), LDRH(1, "LDRH", "Linear Depolarization Ratio H", "db", KeyEvent.VK_H, "LinearDepolRatioH"), // fix netcdf LDRV(1, "LDRV", "Linear Depolarization Ratio V", "db", KeyEvent.VK_L, "LinearDepolRatioV"), // fix netcdf PHIDP(1, "PhiDP", "Differential Phase", "deg", KeyEvent.VK_P, "DifferentialPhase"), // "\u03a6DP" RHOHV(1, "RhoHV", "Correlation Coefficient", null, KeyEvent.VK_R, "CrossPolCorrelation"), TIME_SERIES(8, "T-Ser", null, null, -1, null), KDP(1, "KDP", "Specific Differential Phase", "deg/km", KeyEvent.VK_K, "SpecificPhase"), NCP_PLUS(1, "NCPp", "NCP + s.deviation of Zdr filter", null, KeyEvent.VK_U, "EnhancedNormalizedCoherentPower"), // fix netcdf HDR(1, "HDR", "Aydin Hail", "dB", KeyEvent.VK_H, "AydinHail"), // fix // netcdf RCOMP(1, "Rcomp", "Rain rate", "mm/h", KeyEvent.VK_C, "CompositeRainRate"); // fix netcdf /** * number of bytes per gate */ public final int BYTE_SIZE; /** * unique identifier; used on menus etc */ public final String CODE; /** * descriptive String */ public final String DESCRIPTION; /** * units (will be added to description in parentheses if non- * <code>null</code>) */ public final String UNITS; /** * KeyEvent constant to be used in creating shortcuts */ public final int ACCELERATOR; /** * Variable name when field is stored as NetCDF */ public final String NETCDF; Moment(final int byteSize, final String code, final String description, final String units, final int accelerator, final String netcdf) { this.BYTE_SIZE = byteSize; this.CODE = code; this.UNITS = units; this.DESCRIPTION = description + (units == null ? "" : " (" + units + ")"); this.ACCELERATOR = accelerator; this.NETCDF = netcdf; } @Override public String toString() { return this.CODE; } public static Moment translate(final String type) { for (Moment m : Moment.values()) { if (m.CODE.equalsIgnoreCase(type)) return m; } // throw new IllegalArgumentException(type + // " is not a recognized moment code"); return null; } } public static final ChillFieldInfo R0HH = new ChillFieldInfo( Moment.R0HH.CODE, Moment.R0HH.DESCRIPTION, Moment.R0HH.ordinal(), 12800000, -12800000, 0, 0); public static final ChillFieldInfo R0VV = new ChillFieldInfo( Moment.R0VV.CODE, Moment.R0VV.DESCRIPTION, Moment.R0VV.ordinal(), 12800000, -12800000, 0, 0); public static final ChillFieldInfo R0HV = new ChillFieldInfo( Moment.R0HV.CODE, Moment.R0HV.DESCRIPTION, Moment.R0HV.ordinal(), 12800000, -12800000, 0, 0); public static final ChillFieldInfo R0VH = new ChillFieldInfo( Moment.R0VH.CODE, Moment.R0VH.DESCRIPTION, Moment.R0VH.ordinal(), 12800000, -12800000, 0, 0); public static final ChillFieldInfo R0HVc = new ChillFieldInfo( Moment.R0HVc.CODE, Moment.R0HVc.DESCRIPTION, Moment.R0HVc.ordinal(), 12800000, -12800000, 0, 0); public static final ChillFieldInfo R0VHc = new ChillFieldInfo( Moment.R0VHc.CODE, Moment.R0VHc.DESCRIPTION, Moment.R0VHc.ordinal(), 12800000, -12800000, 0, 0); public static final ChillFieldInfo R1HV = new ChillFieldInfo( Moment.R1HV.CODE, Moment.R1HV.DESCRIPTION, Moment.R1HV.ordinal(), 12800000, -12800000, 0, 0); public static final ChillFieldInfo R1VH = new ChillFieldInfo( Moment.R1VH.CODE, Moment.R1VH.DESCRIPTION, Moment.R1VH.ordinal(), 12800000, -12800000, 0, 0); public static final ChillFieldInfo R1VV = new ChillFieldInfo( Moment.R1VV.CODE, Moment.R1VV.DESCRIPTION, Moment.R1VV.ordinal(), 12800000, -12800000, 0, 0); public static final ChillFieldInfo R1HH = new ChillFieldInfo( Moment.R1HH.CODE, Moment.R1HH.DESCRIPTION, Moment.R1HH.ordinal(), 12800000, -12800000, 0, 0); public static final ChillFieldInfo R2HH = new ChillFieldInfo( Moment.R2HH.CODE, Moment.R2HH.DESCRIPTION, Moment.R2HH.ordinal(), 12800000, -12800000, 0, 0); public static final ChillFieldInfo R2VV = new ChillFieldInfo( Moment.R2VV.CODE, Moment.R2VV.DESCRIPTION, Moment.R2VV.ordinal(), 12800000, -12800000, 0, 0); public static final ChillFieldInfo Z = new ChillFieldInfo(Moment.Z.CODE, Moment.Z.DESCRIPTION, 12, 9600000, -3200000, 0, 0); public static final ChillFieldInfo V = new ChillFieldInfo(Moment.V.CODE, Moment.V.DESCRIPTION, 13, 5500000, -5500000, 16, 1); public static final ChillFieldInfo W = new ChillFieldInfo(Moment.W.CODE, Moment.W.DESCRIPTION, 14, 5500000, -5500000, 0, 2); public static final ChillFieldInfo NCP = new ChillFieldInfo( Moment.NCP.CODE, Moment.NCP.DESCRIPTION, 15, 1000000, 0, 0, 3); public static final ChillFieldInfo ZDR = new ChillFieldInfo( Moment.ZDR.CODE, Moment.ZDR.DESCRIPTION, 16, 9030899, -3010299, 0, 4); public static final ChillFieldInfo LDRH = new ChillFieldInfo( Moment.LDRH.CODE, Moment.LDRH.DESCRIPTION, 17, 0, -480000, 0, 5); public static final ChillFieldInfo LDRV = new ChillFieldInfo( Moment.LDRV.CODE, Moment.LDRV.DESCRIPTION, 18, 0, -480000, 0, 5); public static final ChillFieldInfo PHIDP = new ChillFieldInfo( Moment.PHIDP.CODE, Moment.PHIDP.DESCRIPTION, 19, 18000000, -18000000, 0, 6); public static final ChillFieldInfo RHOHV = new ChillFieldInfo( Moment.RHOHV.CODE, Moment.RHOHV.DESCRIPTION, 20, 1000000, 300000, 0, 7); public static final ChillFieldInfo TIME_SERIES = new ChillFieldInfo( Moment.TIME_SERIES.CODE, Moment.TIME_SERIES.DESCRIPTION, Moment.TIME_SERIES.ordinal(), 12800000, -12800000, 0, 0); public static final ChillFieldInfo KDP = new ChillFieldInfo( Moment.KDP.CODE, Moment.KDP.DESCRIPTION, Moment.KDP.ordinal(), 12800000, -12800000, 0, 0); public static final ChillFieldInfo NCP_PLUS = new ChillFieldInfo( Moment.NCP_PLUS.CODE, Moment.NCP_PLUS.DESCRIPTION, Moment.NCP_PLUS.ordinal(), 12800000, -12800000, 0, 0); public static final ChillFieldInfo HDR = new ChillFieldInfo( Moment.HDR.CODE, Moment.HDR.DESCRIPTION, Moment.HDR.ordinal(), 12800000, -12800000, 0, 0); public static final ChillFieldInfo RCOMP = new ChillFieldInfo( Moment.RCOMP.CODE, Moment.RCOMP.DESCRIPTION, Moment.RCOMP.ordinal(), 12800000, -12800000, 0, 0); public static final ChillFieldInfo[] types = new ChillFieldInfo[]{R0HH, R0VV, R0HV, R0VH, R0HVc, R0VHc, R1HV, R1VH, R1VV, R1HH, R2HH, R2VV, Z, V, W, NCP, ZDR, LDRH, LDRV, PHIDP, RHOHV, TIME_SERIES, KDP, NCP_PLUS, HDR, RCOMP}; /** * private default constructor prevents instantiation */ private FileFunctions() { } /** * Returns a list of sweeps in a file * * @param dir String describing the directory to look under * @param file String with the name of the file to open * @return ArrayList of Strings of sweeps in <code>file</code> */ public static Collection<String> getSweepList(final String dir, final String file) { ArrayList<String> sweepList = new ArrayList<String>(); String name = stripFileName(file); // trim off scan type if (isNetCDF(name)) { sweepList.add("Sweep 1"); return sweepList; } if (isIRISRAW(name)) { SigmetProductRaw SPR_input = new SigmetProductRaw(stripFileName(dir) + "/" + name); for (int i = 0; i < SPR_input.getSweeps(); i++) { sweepList.add("Sweep " + i); } return sweepList; } if (isCFRadial(name)) { CFRadialFile cf_file = new CFRadialFile(stripFileName(dir) + "/" + name); sweepList.add("Sweep 1"); return sweepList; } DataInputStream fin = null; String newFileName = stripFileName(dir) + "/" + name; try { fin = new DataInputStream(new FileInputStream(newFileName)); // UNbuffered // for // performance } catch (IOException ioe) { System.err .println("FileFunctions: IOException in getSweepList: File not found?"); ioe.printStackTrace(); return sweepList; } // end catch FileParameterData parameterData = new FileParameterData(); FileSKUHeader skuHeader = new FileSKUHeader(); int count = 0; boolean readOK = skuHeader.inputData(fin); if (!readOK) return sweepList; // not a valid file -> empty list do { // ASSUMPTION : The first SKU header will always be a sweep header. int toSkip = 0; try { readOK = parameterData.inputData(fin); toSkip = parameterData.sweep_bytes - parameterData.byteSize(); // numbytes // to // next // parameterheader skipBytes(fin, toSkip); // Skip to the next parameterheader if (readOK) sweepList.add("Sweep " + nf.format(++count)); } catch (IOException ioe) { ioe.printStackTrace(); System.out.println("Tried to skip " + toSkip + " bytes"); } // end catch } while (readOK); // end while return sweepList; } // end getSweepList /** * List valid files and subirectoried given a directory name * * @param dir the directory to list * @return Collection containing Strings for each subdirectory and valid * file */ public static Collection<String> getDirectory(final String dir) { List<String> names = new ArrayList<String>(); File[] files = new File(stripFileName(dir)).listFiles(); for (File file : files) { String name = getDecoratedName(file); if (name != null) names.add(name); } Collections.sort(names); return names; } protected static String getDecoratedName(final File file) { if (file.isDirectory()) { try { return URLEncoder.encode(file.getAbsolutePath(), "UTF-8") + " DIR"; } catch (UnsupportedEncodingException uee) { return file.getAbsolutePath() + " DIR"; } } else { if (file.isFile()) { String name = file.getName(); if (isCHILL(name)) { FileParameterData parameterData = new FileParameterData(); FileSKUHeader skuHeader = new FileSKUHeader(); try { DataInputStream fin = new DataInputStream( new BufferedInputStream(new FileInputStream(file))); if (!skuHeader.inputData(fin)) return null; // failed to load sku header if (!parameterData.inputData(fin)) return null; // failed to load parameter data fin.close(); } catch (IOException IO) { System.err.println("Error opening " + file.getAbsolutePath()); return null; } try { return URLEncoder.encode(name, "UTF-8") + " " + parameterData.getScanMode(); } catch (UnsupportedEncodingException uee) { return name + " " + parameterData.getScanMode(); } } else if (isNetCDF(name)) { try { return URLEncoder.encode(name, "UTF-8") + " NetCDF"; } catch (UnsupportedEncodingException uee) { return name + " NetCDF"; } } else if (isIRISRAW(name)) { try { return URLEncoder.encode(name, "UTF-8") + " IRISRaw"; } catch (UnsupportedEncodingException uee) { return name + "IRISRaw"; } } else if (isCFRadial(name)) { System.out.println("CF Radial Detected."); try { return URLEncoder.encode(name, "UTF-8") + " CFRadial"; } catch (UnsupportedEncodingException uee) { return name + "CFRADIAL"; } } } } return null; // not a valid file } public static ChillHSKHeader getChillHSKHeader( final FileParameterData paramD) { ChillHSKHeader hskH = new ChillHSKHeader(); hskH.radarId = paramD.getRadarName(); hskH.radarLatitude = paramD.latitude; hskH.radarLongitude = paramD.longitude; hskH.radarAltitudeMsl = paramD.altitude * 1000; hskH.antMode = paramD.chill_scan_mode; hskH.nyquistVel = (int) (paramD.wavelength * 1e6 / 2 * paramD.prf); hskH.gateWidth = paramD.gate_spacing; hskH.pulses = paramD.samples_per_beam; hskH.polarizationMode = paramD.processor_mode; hskH.tiltNum = paramD.tilt_num; // saveTilt not set hskH.angleScale = 0x7fffffff; hskH.sweepStartTime = paramD.volume_start_time; return hskH; } public static ChillDataHeader getChillDataHeader( final FileParameterData paramD, final FileDataHeader fDataH, final ChillHSKHeader cHskH) { int angleScale = (cHskH != null) ? cHskH.angleScale : 0x7fffffff; ChillDataHeader cDataH = new ChillDataHeader(); cDataH.startAz = cDataH.endAz = (int) ((fDataH.azimuth * 1e-6) / 360 * angleScale); cDataH.startEl = cDataH.endEl = (int) ((fDataH.elevation * 1e-6) / 360 * angleScale); cDataH.numGates = paramD.ngates; cDataH.startRange = paramD.start_range; cDataH.dataTime = fDataH.time; // fractionalSecs not set; return cDataH; } public static Collection<String> getAvailableMoments( final FileParameterData paramD) { ArrayList<String> list = new ArrayList<String>(); for (Moment moment : Moment.values()) { if (((1l << moment.ordinal()) & paramD.field_flag) > 0 && moment.BYTE_SIZE == 1) { ChillMomentFieldScale scale = sm.getScale(moment.ordinal()); if (scale == null) { ChillFieldInfo fieldInfo = types[moment.ordinal()]; scale = new ChillMomentFieldScale(fieldInfo, moment.ACCELERATOR, moment.UNITS, 100000, 1, 0); sm.putScale(scale); } list.add(scale.fieldName); } } return list; } /** * Loads a sweep (all data types) into the cache */ public static void load(FileConnection fileConn, ControlMessage command, final CacheMain cache) throws IOException { if (cache.getCompleteFlag(command, ChillDefines.META_TYPE)) { fileConn.setIsSweepDone(true); return; } String path = stripFileName(command.getDir()) + "/" + stripFileName(command.getFile()); sm.clear(); // clear the list of fields because the file will provide a // new list if (isNetCDF(path)) { try { if (command.getFile().startsWith("WCR")) { System.out.println("loading WCR netcdf"); WCRNetCDFFile.load(command, cache); } else if (command.getFile().startsWith("ncswp_")) { System.out.println("loading NCAR netcdf"); NCARNetCDFFile.load(command, cache); } else { System.out.println("loading CASA netcdf"); CASANetCDFFile.load(command, cache); } } finally { fileConn.setIsSweepDone(true); } return; } else if (isIRISRAW(path)) { try { System.out.println("Loading Iris Raw data."); IrisRawFile.load(command, cache); } catch (Exception e) { System.err.println("Exception :" + e); } finally { fileConn.setIsSweepDone(true); } } else if (isCFRadial(path)) { try { System.out.println("Loading CF Radial Data."); CFRadialFile.load(command, cache); } catch (Exception e) { System.err.println("Exception :" + e); } finally { fileConn.setIsSweepDone(true); } return; } System.out.println("loading CHILL data"); File file = new File(path); DataInputStream input = new DataInputStream(new BufferedInputStream( new FileInputStream(file), 1024)); // DataInputStream input = new DataInputStream(new // BufferedInputStream(new FileInputStream(file))); // DataInputStream input = new DataInputStream(new // FileInputStream(file)); int sweep = Integer.parseInt(command.getSweep().split(" ")[1]); Collection<String> availableMoments = null; FileSKUHeader skuH = null; FileParameterData paramD = null; FileFieldScalingInfo[] fieldScalings = null; ChillHSKHeader hskH = null; ChillDataHeader cDataH = null; int currSweepNum = 0; loop: while (input.available() > 0) { // each packet input.mark(FileSKUHeader.BYTE_SIZE); skuH = new FileSKUHeader(); skuH.inputData(input); switch (skuH.id) { case ChillDefines.GATE_DATA_PACKET_CODE: // ray data FileDataHeader fDataH = new FileDataHeader(skuH.length); fDataH.inputData(input); cDataH = getChillDataHeader(paramD, fDataH, hskH); cache.addRay(command, ChillDefines.META_TYPE, cDataH); if (fDataH.size_of_data == 0) break; if (currSweepNum != sweep) { skipBytes(input, fDataH.size_of_data); break; } // actual data if (paramD.data_field_by_field == 1) { // contiguous data { int m = 0; for (Moment moment : Moment.values()) { // each data // type if (((1l << m) & paramD.field_flag) != 0) { // present if (moment.BYTE_SIZE == 1) { byte[] data = new byte[paramD.ngates]; input.readFully(data); cache.addRay(command, sm.getScale(moment.ordinal()).fieldName, new ChillGenRay(hskH, cDataH, sm.getScale(moment.ordinal()).fieldName, data)); Thread.yield(); } else { skipBytes(input, paramD.ngates * moment.BYTE_SIZE); } } ++m; } } } else { // interleaved data byte[][] data = new byte[availableMoments.size()][paramD.ngates]; for (int g = 0; g < paramD.ngates; ++g) { { int m = 0; for (Moment moment : Moment.values()) { if (((1l << moment.ordinal()) & paramD.field_flag) != 0) { // present if (moment.BYTE_SIZE == 1) { data[m++][g] = input.readByte(); } else { skipBytes(input, moment.BYTE_SIZE); } } } } } { int m = 0; for (String moment : availableMoments) { cache.addRay(command, moment, new ChillGenRay(hskH, cDataH, moment, data[m])); ++m; } } Thread.yield(); } break; case ChillDefines.GATE_PARAMS_PACKET_CODE: // parameters / new sweep ++currSweepNum; if (currSweepNum > sweep) break loop; paramD = new FileParameterData(); paramD.inputData(input); sm.setAvailable(paramD.field_flag); hskH = getChillHSKHeader(paramD); cache.addRay(command, ChillDefines.META_TYPE, hskH); availableMoments = FileFunctions.getAvailableMoments(paramD); // scaling info fieldScalings = new FileFieldScalingInfo[Moment.values().length]; for (int i = 0; i < Moment.values().length; ++i) { if ((paramD.field_flag & (1l << i)) == 0) continue; fieldScalings[i] = new FileFieldScalingInfo(); fieldScalings[i].inputData(input); ChillMomentFieldScale scale = sm.getScale(i); if (scale != null) { scale.factor = fieldScalings[i].factor; scale.scale = fieldScalings[i].scale; scale.bias = fieldScalings[i].bias; } } break; default: fileConn.setIsSweepDone(true); throw new Error("Bad packet id code: " + skuH.id); } // trailing size marker int size = input.readInt(); assert size == skuH.length : "\n Bad length: was " + size + " instead of " + skuH.length; } // end loop label while input.close(); for (String moment : availableMoments) { cache.setCompleteFlag(command, moment); } cache.setCompleteFlag(command, ChillDefines.META_TYPE); fileConn.setIsSweepDone(true); } public static boolean isIRISRAW(String path) { int k = path.indexOf(".RAW"); if (k == -1) { return false; } else { int j = path.indexOf(".uf"); if (j == -1) { return true; } else { return false; } } } public static void skipBytes(final DataInputStream input, long toSkip) throws IOException { while (toSkip > 0) { long skipped = input.skip(toSkip); // skip if (skipped < 1) throw new IOException("Skipped " + skipped + " bytes instead of " + toSkip); toSkip -= skipped; } } public static String stripFileName(final String name) { try { return URLDecoder.decode(name.substring(0, name.lastIndexOf(" ")), "UTF-8"); } catch (UnsupportedEncodingException uee) { return name.substring(0, name.lastIndexOf(" ")); } } public static boolean isCHILL(final String name) { if (name.startsWith("CHL")) { // If CHILL file return !name.endsWith(".cdet"); } return false; } public static boolean isNetCDF(String name) { if (name.endsWith(".gz")) name = name.substring(0, name.length() - 3); // strip compressedness if (name.endsWith(".netcdf")) return true; return name.endsWith(".nc"); } public static boolean isCFRadial(String name) { return name.endsWith(".cf"); } }