/* 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.amanda; import java.io.BufferedReader; import java.io.FileReader; import java.io.IOException; import java.io.InputStream; import java.io.InputStreamReader; import java.net.URL; import java.rmi.RemoteException; import java.util.ArrayList; import java.util.HashMap; import java.util.StringTokenizer; import visad.Data; import visad.FieldImpl; import visad.FlatField; import visad.FunctionType; import visad.Integer1DSet; import visad.RealType; import visad.RealTupleType; import visad.VisADException; import visad.data.BadFormException; class DoubleCache { private double value; DoubleCache() { this(Double.NaN); } DoubleCache(double v) { value = v; } double getValue() { return value; } void setValue(double v) { value = v; } } class FloatCache { private float value; FloatCache() { this(Float.NaN); } FloatCache(float v) { value = v; } float getValue() { return value; } void setValue(float v) { value = v; } } class IntCache { private int value; IntCache() { this(-1); } IntCache(int v) { value = v; } int getValue() { return value; } void setValue(int v) { value = v; } } public class AmandaFile { public static final RealType moduleIndexType = RealType.getRealType("Module_Index"); public static RealTupleType xyzType; static { try { xyzType = new RealTupleType(RealType.XAxis, RealType.YAxis, RealType.ZAxis); } catch (VisADException ve) { ve.printStackTrace(); xyzType = null; } } private double xmin = Double.MAX_VALUE; private double xmax = Double.MIN_VALUE; private double ymin = Double.MAX_VALUE; private double ymax = Double.MIN_VALUE; private double zmin = Double.MAX_VALUE; private double zmax = Double.MIN_VALUE; private ModuleList modules = new ModuleList(); private ArrayList events = new ArrayList(); private HashMap lastCache = new HashMap(); public AmandaFile(String id) throws BadFormException, IOException, VisADException { FileReader rdr = new FileReader(id); try { loadFile(new BufferedReader(rdr)); } finally { try { rdr.close(); } catch (IOException ioe) { } } } public AmandaFile(URL url) throws BadFormException, IOException, VisADException { InputStream is = url.openStream(); try { loadFile(new BufferedReader(new InputStreamReader(is))); } finally { try { is.close(); } catch (IOException ioe) { } } } private void loadFile(BufferedReader br) throws BadFormException, VisADException { // read V record String firstLine; try { firstLine = nextLine(br); } catch (IOException ioe) { throw new BadFormException("Unreadable file"); } if (firstLine == null || firstLine.length() <= 1 || firstLine.charAt(0) != 'v' || !Character.isSpaceChar(firstLine.charAt(1))) { throw new BadFormException("Bad first line \"" + firstLine + "\""); } Event currentEvent = null; boolean inSlowEvent = false; while (true) { String line; try { line = nextLine(br); } catch (IOException ioe) { throw new BadFormException("Unreadable file"); } // end loop if we've reached the end of the file if (line == null) { break; } // ignore blank lines if (line.length() == 0) { continue; } StringTokenizer tok = new StringTokenizer(line); String keyword = tok.nextToken(); if (keyword.equals("array")) { if (modules.isInitialized()) { System.err.println("Warning: Multiple ARRAY lines found"); } int nmodules = readArrayLine(line, tok); continue; } if (keyword.equals("om")) { Module module = readOMLine(line, tok); if (module != null) { modules.add(module); final float x = module.getX(); if (x == x) { if (x < xmin) xmin = x; if (x > xmax) xmax = x; } final float y = module.getY(); if (y == y) { if (y < ymin) ymin = y; if (y > ymax) ymax = y; } final float z = module.getZ(); if (z == z) { if (z < zmin) zmin = z; if (z > zmax) zmax = x; } } continue; } if (keyword.equals("es")) { // ignore ES events for now if (inSlowEvent) { System.err.println("Warning: Missing EE for slow event"); } inSlowEvent = true; continue; } if (keyword.equals("em")) { if (currentEvent != null) { System.err.println("Warning: Missing EE for " + currentEvent); } currentEvent = startEvent(line, tok); continue; } // read TR and HT records if (keyword.equals("tr")) { MCTrack track = readTrack(line, tok); if (track != null) { if (currentEvent == null) { System.err.println("Found TRACK " + track + " outside event"); } else { currentEvent.add(track); } } continue; } if (keyword.equals("fit")) { FitTrack fit = readFit(line, tok); if (fit != null) { if (currentEvent == null) { System.err.println("Found FIT " + fit + " outside event"); } else { currentEvent.add(fit); } } continue; } if (keyword.equals("ht")) { Hit hit = readHit(line, tok); if (hit != null) { if (currentEvent == null) { System.err.println("Found HIT " + hit + " outside event"); } else { currentEvent.add(hit); } } continue; } if (keyword.equals("ee")) { if (currentEvent == null) { if (inSlowEvent) { inSlowEvent = false; } else { System.err.println("Found EE outside event"); } } else { events.add(currentEvent); currentEvent = null; } continue; } } // remove cached values since they're no longer needed lastCache.clear(); } private final void dump(java.io.PrintStream out) { final int nEvents = events.size(); for (int i = 0; i < nEvents; i++) { ((Event )events.get(i)).dump(out); } modules.dump(out); } public final Event getEvent(int index) { return (Event )events.get(index); } public final int getNumberOfEvents() { return events.size(); } public final double getXMax() { return xmax; } public final double getXMin() { return xmin; } public final double getYMax() { return ymax; } public final double getYMin() { return ymin; } public final double getZMax() { return zmax; } public final double getZMin() { return zmin; } public final FieldImpl makeEventData() { final int num = events.size(); Integer1DSet set; try { set = new Integer1DSet(Event.indexType, (num == 0 ? 1 : num)); } catch (VisADException ve) { ve.printStackTrace(); return null; } FunctionType funcType; FieldImpl fld; try { funcType = new FunctionType(Event.indexType, Hits.timeSequenceType); fld = new FieldImpl(funcType, set); } catch (VisADException ve) { ve.printStackTrace(); return null; } if (num > 0) { Data[] samples = new Data[num]; for (int e = 0; e < num; e++) { samples[e] = ((Event )events.get(e)).makeHitSequence(); } try { fld.setSamples(samples, false); } catch (RemoteException re) { re.printStackTrace(); } catch (VisADException ve) { ve.printStackTrace(); } } return fld; } public final FlatField makeModuleData() throws RemoteException, VisADException { // Field of modules final int num = modules.size(); Integer1DSet set; try { set = new Integer1DSet(moduleIndexType, num); } catch (VisADException ve) { ve.printStackTrace(); return null; } FunctionType funcType; FlatField fld; try { funcType = new FunctionType(moduleIndexType, xyzType); fld = new FlatField(funcType, set); } catch (VisADException ve) { ve.printStackTrace(); return null; } if (num > 0) { float[][] samples = new float[3][num]; for (int i = 0; i < num; i++) { Module mod = modules.get(i); samples[0][i] = mod.getX(); samples[1][i] = mod.getY(); samples[2][i] = mod.getZ(); } try { fld.setSamples(samples); } catch (RemoteException re) { re.printStackTrace(); } } return fld; } private String nextLine(BufferedReader rdr) throws IOException { String line = rdr.readLine(); if (line != null) { line = line.trim().toLowerCase(); } return line; } private int parseChannel(String tokenName, String token) throws NumberFormatException { final int dotIdx = token.indexOf('.'); if (dotIdx >= 0) { token = "-" + token.substring(dotIdx + 1); } return parseInt(tokenName, token); } private double parseDouble(String tokenName, String token) throws NumberFormatException { double value; if (token == null) { value = Double.NaN; } else if (token.equals("inf")) { value = Double.POSITIVE_INFINITY; } else if (token.equals("-inf")) { value = Double.NEGATIVE_INFINITY; } else if (token.equals("?")) { value = Double.NaN; } else if (token.equals("nan")) { value = Double.NaN; } else if (token.equals("*")) { DoubleCache cval = (DoubleCache )lastCache.get(tokenName); if (cval == null) { value = Double.NaN; } else { value = cval.getValue(); } } else { value = Double.parseDouble(token); } // save value in case next reference uses '*' to access it DoubleCache cache = (DoubleCache )lastCache.get(tokenName); if (cache == null) { lastCache.put(tokenName, new DoubleCache(value)); } else { cache.setValue(value); } return value; } private float parseFloat(String tokenName, String token) throws NumberFormatException { float value; if (token == null) { value = Float.NaN; } else if (token.equals("inf")) { value = Float.POSITIVE_INFINITY; } else if (token.equals("-inf")) { value = Float.NEGATIVE_INFINITY; } else if (token.equals("?")) { value = Float.NaN; } else if (token.equals("nan")) { value = Float.NaN; } else if (token.equals("*")) { FloatCache cval = (FloatCache )lastCache.get(tokenName); if (cval == null) { value = Float.NaN; } else { value = cval.getValue(); } } else { value = Float.parseFloat(token); } // save value in case next reference uses '*' to access it FloatCache cache = (FloatCache )lastCache.get(tokenName); if (cache == null) { lastCache.put(tokenName, new FloatCache(value)); } else { cache.setValue(value); } return value; } private int parseInt(String tokenName, String token) throws NumberFormatException { int value; if (token == null) { value = -1; } else if (token.equals("inf")) { value = Integer.MAX_VALUE; } else if (token.equals("-inf")) { value = Integer.MIN_VALUE; } else if (token.equals("?")) { value = -1; } else if (token.equals("nan")) { value = -1; } else if (token.equals("*")) { IntCache cval = (IntCache )lastCache.get(tokenName); if (cval == null) { value = -1; } else { value = cval.getValue(); } } else { value = Integer.parseInt(token); } // save value in case next reference uses '*' to access it IntCache cache = (IntCache )lastCache.get(tokenName); if (cache == null) { lastCache.put(tokenName, new IntCache(value)); } else { cache.setValue(value); } return value; } private int readArrayLine(String line, StringTokenizer tok) throws BadFormException { String detector = tok.nextToken(); int nstrings, nmodules; try { float longitude = parseFloat("raLon", tok.nextToken()); float latitude = parseFloat("raLat", tok.nextToken()); float depth = parseFloat("raDepth", tok.nextToken()); nstrings = parseInt("raNStr", tok.nextToken()); nmodules = parseInt("raNMod", tok.nextToken()); } catch(NumberFormatException e) { throw new BadFormException("Bad ARRAY line \"" + line + "\": " + e.getMessage()); } if (nstrings < 1 || nmodules < 1) { throw new BadFormException("Bad ARRAY line \"" + line + "\": " + (nstrings < 1 ? "nstrings < 1" : "nmodule < 1")); } return nmodules; } private final FitTrack readFit(String line, StringTokenizer tok) throws VisADException { // FIT id type xstart ystart zstart zenith azimuth time length energy float xstart, ystart, zstart, zenith, azimuth, length, energy, time; // skip ID field tok.nextToken(); tok.nextToken(); try { xstart = parseFloat("fitXStart", tok.nextToken()); ystart = parseFloat("fitYStart", tok.nextToken()); zstart = parseFloat("fitZStart", tok.nextToken()); zenith = parseFloat("fitZenith", tok.nextToken()); // 0.0f toward -z azimuth = parseFloat("fitAzimuth", tok.nextToken()); // 0.0f toward +x time = parseFloat("fitTime", tok.nextToken()); length = parseFloat("fitLength", tok.nextToken()); energy = parseFloat("fitEnergy", tok.nextToken()); } catch(NumberFormatException e) { throw new BadFormException("Bad FIT line \"" + line + "\": " + e.getMessage()); } return new FitTrack(xstart, ystart, zstart, zenith, azimuth, length, energy, time); } private final Hit readHit(String line, StringTokenizer tok) throws VisADException { String chanStr = tok.nextToken(); int number = parseChannel("htNum", chanStr); if (number < 0) { System.err.println("Warning: Ignoring HIT for secondary channel \"" + chanStr + "\""); return null; } // find this module Module mod = modules.find(number); if (mod == null) { System.err.println("Warning: Module not found for HIT line \"" + line + "\"; hit ignored"); return null; } float amplitude, leadEdgeTime, timeOverThreshold; try { amplitude = parseFloat("htAmp", tok.nextToken()); // skip pulse id & parent track tok.nextToken(); tok.nextToken(); leadEdgeTime = parseFloat("htLet", tok.nextToken()); timeOverThreshold = parseFloat("htTot", tok.nextToken()); // ignore number of TDC edges } catch(NumberFormatException e) { throw new BadFormException("Bad HIT line \"" + line + "\": " + e.getMessage()); } return new Hit(mod, amplitude, leadEdgeTime, timeOverThreshold); } private final Module readOMLine(String line, StringTokenizer tok) throws BadFormException { String numStr = tok.nextToken(); int number; try { number = parseInt("omNum", numStr); } catch(NumberFormatException e) { throw new BadFormException("unparseable module number \"" + numStr + "\" in \"" + line + "\""); } if (number < 0) { throw new BadFormException("bad module number \"" + numStr + "\" in \"" + line + "\""); } int stringOrder, string; float x, y, z; try { stringOrder = parseInt("modOrd", tok.nextToken()); string = parseInt("modStr", tok.nextToken()); x = parseFloat("modX", tok.nextToken()); y = parseFloat("modY", tok.nextToken()); z = parseFloat("modZ", tok.nextToken()); } catch(NumberFormatException e) { throw new BadFormException("Bad OM line \"" + line + "\": " + e.getMessage()); } return new Module(number, x, y, z, string, stringOrder); } private final MCTrack readTrack(String line, StringTokenizer tok) throws VisADException { // TR nr parent type xstart ystart zstart zenith azimuth length energy time // skip first three fields for (int i = 0; i < 3; i++) { tok.nextToken(); } float xstart, ystart, zstart, zenith, azimuth, length, energy, time; try { xstart = parseFloat("trXStart", tok.nextToken()); ystart = parseFloat("trYStart", tok.nextToken()); zstart = parseFloat("trZStart", tok.nextToken()); zenith = parseFloat("trZenith", tok.nextToken()); // 0.0f toward -z azimuth = parseFloat("trAzimuth", tok.nextToken()); // 0.0f toward +x length = parseFloat("trLength", tok.nextToken()); energy = parseFloat("trEnergy", tok.nextToken()); time = parseFloat("trTime", tok.nextToken()); } catch(NumberFormatException e) { throw new BadFormException("bad TRACK line \"" + line + "\": " + e.getMessage()); } return new MCTrack(xstart, ystart, zstart, zenith, azimuth, length, energy, time); } private final Event startEvent(String line, StringTokenizer tok) throws BadFormException { // assemble EM event int evtNum, runNum, year, day; double time, timeShift; try { evtNum = parseInt("emNum", tok.nextToken()); runNum = parseInt("emRun", tok.nextToken()); year = parseInt("emYear", tok.nextToken()); day = parseInt("emDay", tok.nextToken()); time = parseDouble("emTime", tok.nextToken()); if (!tok.hasMoreTokens()) { timeShift = Double.NaN; } else { // time shift in nsec of all times in event timeShift = parseDouble("emTimeShift", tok.nextToken()) * 0.000000001; } } catch(NumberFormatException e) { throw new BadFormException("Bad EM line \"" + line + "\": " + e.getMessage()); } return new Event(evtNum, runNum, year, day, time, timeShift); } }