Author: [log in to unmask] Date: Thu Jun 4 09:23:09 2015 New Revision: 3088 Log: Add tools for getting opening angle information from MYA (still work in progress). Added: java/trunk/conditions/src/main/java/org/hps/conditions/svt/MotorPositionLoader.java java/trunk/conditions/src/main/java/org/hps/conditions/svt/OpeningAngleLoader.java Modified: java/trunk/conditions/src/main/java/org/hps/conditions/run/RunSpreadsheet.java Modified: java/trunk/conditions/src/main/java/org/hps/conditions/run/RunSpreadsheet.java ============================================================================= --- java/trunk/conditions/src/main/java/org/hps/conditions/run/RunSpreadsheet.java (original) +++ java/trunk/conditions/src/main/java/org/hps/conditions/run/RunSpreadsheet.java Thu Jun 4 09:23:09 2015 @@ -4,6 +4,10 @@ import java.io.FileNotFoundException; import java.io.FileReader; import java.io.IOException; +import java.text.ParseException; +import java.text.SimpleDateFormat; +import java.util.Date; +import java.util.LinkedHashMap; import java.util.List; import org.apache.commons.csv.CSVFormat; @@ -36,10 +40,17 @@ * * @param args the command line arguments */ - public static void main(final String args[]) { + public static void main(final String args[]) throws Exception { final RunSpreadsheet runSpreadsheet = new RunSpreadsheet(new File(args[0])); for (final CSVRecord record : runSpreadsheet.getRecords()) { - System.out.println(record); + try { + System.out.print("start date: " + parseStartDate(record) + ", "); + System.out.print("end date: " + parseEndDate(record) + ", "); + System.out.print(record); + System.out.println(); + } catch (Exception e) { + e.printStackTrace(); + } } } @@ -118,4 +129,79 @@ public List<CSVRecord> getRecords() { return records; } + + private static final SimpleDateFormat DATE_FORMAT = new SimpleDateFormat("MM/dd/yyyy H:mm"); + + private static Date parseStartDate(CSVRecord record) throws ParseException { + return DATE_FORMAT.parse(record.get("date") + " " + record.get("start_time")); + } + + private static Date parseEndDate(CSVRecord record) throws ParseException { + return DATE_FORMAT.parse(record.get("date") + " " + record.get("end_time")); + } + + private static int parseRunNumber(CSVRecord record) throws NumberFormatException { + return Integer.parseInt(record.get("run")); + } + + public static class RunData { + + private int run; + private Date startDate; + private Date endDate; + private CSVRecord record; + + RunData(CSVRecord record) throws NumberFormatException { + this.record = record; + run = parseRunNumber(this.record); + try { + startDate = RunSpreadsheet.parseStartDate(this.record); + } catch (ParseException e) { + } + try { + endDate = RunSpreadsheet.parseEndDate(this.record); + } catch (ParseException e) { + } + } + + public int getRun() { + return run; + } + + public Date getStartDate() { + return startDate; + } + + public Date getEndDate() { + return endDate; + } + + public String toString() { + return "RunData { run: " + run + ", startDate: " + startDate + ", endDate: " + endDate + " }"; + } + + public CSVRecord getRecord() { + return record; + } + } + + @SuppressWarnings("serial") + public static class RunMap extends LinkedHashMap<Integer, RunData> { + + private void addRunData(RunData runData) { + this.put(runData.getRun(), runData); + } + } + + public RunMap getRunMap() { + RunMap runMap = new RunMap(); + for (final CSVRecord record : getRecords()) { + try { + runMap.addRunData(new RunData(record)); + } catch (NumberFormatException e) { + e.printStackTrace(); + } + } + return runMap; + } } Added: java/trunk/conditions/src/main/java/org/hps/conditions/svt/MotorPositionLoader.java ============================================================================= --- java/trunk/conditions/src/main/java/org/hps/conditions/svt/MotorPositionLoader.java (added) +++ java/trunk/conditions/src/main/java/org/hps/conditions/svt/MotorPositionLoader.java Thu Jun 4 09:23:09 2015 @@ -0,0 +1,278 @@ +package org.hps.conditions.svt; + +import java.io.BufferedReader; +import java.io.BufferedWriter; +import java.io.File; +import java.io.FileNotFoundException; +import java.io.FileReader; +import java.io.FileWriter; +import java.io.IOException; +import java.text.ParseException; +import java.text.SimpleDateFormat; +import java.util.ArrayList; +import java.util.Date; +import java.util.List; + +import org.apache.commons.cli.CommandLine; +import org.apache.commons.cli.HelpFormatter; +import org.apache.commons.cli.Options; +import org.apache.commons.cli.PosixParser; + +/** + * Load SVT motor positions from a MYA dump, figure out time ranges (same position for > 10 seconds), and then + * convert the motor stage to an opening angle. + * <p> + * The calculated angle ranges are written out to a comma delimited text file with double-quoted field values. + * + * @author Jeremy McCormick + */ +public class MotorPositionLoader { + + class MotorPositionInterval { + + private final Date endDate; + private final Date startDate; + private final double angle; + private final double yStage; + + MotorPositionInterval(final Date startDate, final Date endDate, final double angle, final double yStage) { + this.startDate = startDate; + this.endDate = endDate; + this.angle = angle; + this.yStage = yStage; + } + + Date getEndDate() { + return endDate; + } + + Date getStartDate() { + return startDate; + } + + double getAngle() { + return angle; + } + + double getYStage() { + return yStage; + } + + public String toString() { + return "MotorPositionInterval { start: " + startDate + ", end: " + endDate + ", angle: " + angle + ", yStage: " + yStage + " }"; + } + } + + private class MotorPositionMyaRecord { + + private final Date date; + private final double position; + + MotorPositionMyaRecord(final Date date, final double position) { + this.date = date; + this.position = position; + } + + Date getDate() { + return date; + } + + double getPosition() { + return position; + } + } + + enum Side { + BOT, TOP + } + + private static final double ANGLE_CONVERSION = 832.714; + + private static final double BOTTOM_ANGLE_CONSTANT = 17.397; + //private static final double BOTTOM_LAYER_CONSTANT1 = 0.363; + //private static final double BOTTOM_LAYER_CONSTANT2 = -6.815; + + private static final SimpleDateFormat DATE_FORMAT = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"); + + private static final long MIN_TIME_INTERVAL = 10000L; + + private static final Options OPTIONS = new Options(); + + private static final double TOP_ANGLE_CONSTANT = 17.821; + //private static final double TOP_LAYER_CONSTANT1 = -0.391; + //private static final double TOP_LAYER_CONSTANT2 = 7.472; + + static { + OPTIONS.addOption("h", "help", false, "print help"); + OPTIONS.addOption("s", "side", true, "'top' or 'bot' (required)"); + OPTIONS.getOption("s").setRequired(true); + OPTIONS.addOption("i", "input-file", true, "input text file dumped from MYA (required)"); + OPTIONS.getOption("i").setRequired(true); + OPTIONS.addOption("o", "output-file", true, "output text file with computed angle intervals"); + //OPTIONS.addOption("l", "layer", false, "write out layer 1 position instead of computed angle"); + } + + public void setSide(Side side) { + this.side = side; + if (Side.TOP.equals(side)) { + this.motorConstant = TOP_ANGLE_CONSTANT; + //this.layerConstant1 = TOP_LAYER_CONSTANT1; + //this.layerConstant2 = TOP_LAYER_CONSTANT2; + } else if (Side.BOT.equals(side)) { + this.motorConstant = BOTTOM_ANGLE_CONSTANT; + //this.layerConstant1 = BOTTOM_LAYER_CONSTANT1; + //this.layerConstant2 = BOTTOM_LAYER_CONSTANT2; + } + } + + /** + * Run from command line arguments. + * + * @param args + */ + void run(final String args[]) { + + final PosixParser parser = new PosixParser(); + + CommandLine cl = null; + try { + cl = parser.parse(OPTIONS, args); + } catch (final Exception e) { + printUsage(1); + throw new RuntimeException(); + } + + if (cl.hasOption("h")) { + printUsage(0); + } + + if (cl.hasOption("s")) { + setSide(Side.valueOf(cl.getOptionValue("s").toUpperCase())); + } else { + printUsage(0); + } + + this.setSide(side); + + String path = null; + if (cl.hasOption("i")) { + path = cl.getOptionValue("i"); + } else { + printUsage(1); + } + + //if (cl.hasOption("l")) { + // setWriteLayerPosition(true); + //} + + try { + load(path); + } catch (final Exception e) { + throw new RuntimeException(e); + } + + // Find the time intervals with a certain motor position setting. + findIntervals(); + + if (cl.hasOption("o")) { + final String outputPath = cl.getOptionValue("o"); + try { + toCsv(outputPath); + } catch (final IOException e) { + throw new RuntimeException(e); + } + } + } + + public static void main(final String args[]) { + new MotorPositionLoader().run(args); + } + + /** + * Print the usage statement for this tool to the console. + */ + private static final void printUsage(final int status) { + final HelpFormatter help = new HelpFormatter(); + help.printHelp("MotorPositionLoader", "", OPTIONS, ""); + System.exit(status); + } + + private List<MotorPositionInterval> intervals; + + //private double layerConstant1; + //private double layerConstant2; + + private double motorConstant; + + private List<MotorPositionMyaRecord> records; + + //private boolean writeLayerPosition = false; + + Side side = null; + + MotorPositionLoader() { + } + + private double computeAngle(final double yStage) { + double angle = (motorConstant - yStage) / ANGLE_CONVERSION; + if (Side.BOT.equals(side)) { + angle = -angle; + } + return angle; + } + + //private double computeLayer1Position(final double yStage) { + // return layerConstant1 * yStage + layerConstant2; + //} + + List<MotorPositionInterval> findIntervals() { + intervals = new ArrayList<MotorPositionInterval>(); + for (int i = 0; i < records.size() - 1; i++) { + final Date currentDate = records.get(i).getDate(); + final Date nextDate = records.get(i + 1).getDate(); + final long timeDiff = nextDate.getTime() - currentDate.getTime(); + if (timeDiff >= MIN_TIME_INTERVAL) { + final double yStage = records.get(i).getPosition(); + double angle = this.computeAngle(yStage); + intervals.add(new MotorPositionInterval(currentDate, nextDate, angle, yStage)); + } + } + return intervals; + } + + void load(final String path) throws IOException, ParseException, FileNotFoundException { + records = new ArrayList<MotorPositionMyaRecord>(); + try (BufferedReader br = new BufferedReader(new FileReader(path))) { + String line; + while ((line = br.readLine()) != null) { + final String dateString = line.substring(0, 19); + final String positionString = line.substring(20); + try { + final Date date = DATE_FORMAT.parse(dateString); + final double position = Double.parseDouble(positionString); + records.add(new MotorPositionMyaRecord(date, position)); + } catch (final NumberFormatException e) { + e.printStackTrace(); + } + } + } + } + + //private void setWriteLayerPosition(final boolean writeLayer) { + // this.writeLayerPosition = writeLayer; + //} + + private void toCsv(final String path) throws IOException { + System.out.println("writing " + intervals.size() + " intervals to file to " + path + " ..."); + final FileWriter fw = new FileWriter(new File(path)); + final BufferedWriter bw = new BufferedWriter(fw); + for (final MotorPositionInterval interval : intervals) { + bw.write("\"" + DATE_FORMAT.format(interval.getStartDate()) + "\","); + bw.write("\"" + DATE_FORMAT.format(interval.getEndDate()) + "\","); + bw.write("\"" + interval.getAngle() + "\""); + bw.write('\n'); + } + bw.close(); + System.out.println("done writing intervals"); + } +} Added: java/trunk/conditions/src/main/java/org/hps/conditions/svt/OpeningAngleLoader.java ============================================================================= --- java/trunk/conditions/src/main/java/org/hps/conditions/svt/OpeningAngleLoader.java (added) +++ java/trunk/conditions/src/main/java/org/hps/conditions/svt/OpeningAngleLoader.java Thu Jun 4 09:23:09 2015 @@ -0,0 +1,133 @@ +package org.hps.conditions.svt; + +import java.io.File; +import java.io.FileOutputStream; +import java.io.PrintStream; +import java.text.SimpleDateFormat; +import java.util.Date; +import java.util.Iterator; +import java.util.List; + +import org.hps.conditions.run.RunSpreadsheet; +import org.hps.conditions.run.RunSpreadsheet.RunData; +import org.hps.conditions.run.RunSpreadsheet.RunMap; +import org.hps.conditions.svt.MotorPositionLoader.MotorPositionInterval; +import org.hps.conditions.svt.MotorPositionLoader.Side; + +public class OpeningAngleLoader { + + private static final String TOP_FILE = "mya_svt_top.txt"; + private static final String BOT_FILE = "mya_svt_bot.txt"; + private static final String RUN_FILE = "runs.csv"; + private static final String OUT_FILE = "svt_opening_angles.txt"; + + private static final SimpleDateFormat DATE_FORMAT = new SimpleDateFormat("MMMM dd YYYY HH:mm z"); + + public static void main(String[] args) throws Exception { + + // Load top and bottom intervals from MYA dump. + List<MotorPositionInterval> topIntervals = getMotorPositionIntervals(TOP_FILE, Side.TOP); + List<MotorPositionInterval> botIntervals = getMotorPositionIntervals(BOT_FILE, Side.BOT); + + // Load run map from spreadsheet. + RunMap runMap = loadRunMap(RUN_FILE); + + // Write out run data combined with SVT opening angle intervals. + PrintStream ps = new PrintStream(new FileOutputStream(OUT_FILE)); + for (final RunData data : runMap.values()) { + if (acceptRun(data)) { + final MotorPositionInterval topInterval = findInterval(topIntervals, data.getStartDate()); + final MotorPositionInterval botInterval = findInterval(botIntervals, data.getStartDate()); + printLine(ps, data, topInterval, botInterval); + } + } + ps.close(); + } + + private static void printLine(PrintStream ps, RunData data, MotorPositionInterval topInterval, MotorPositionInterval botInterval) { + ps.print("run: " + data.getRun() + " @ [" + data.getRecord().get("svt_y_position") + "], "); + if (topInterval != null) { + ps.print("start(top): " + DATE_FORMAT.format(topInterval.getStartDate()) + ", "); + ps.print("end(top): " + DATE_FORMAT.format(topInterval.getEndDate()) + ", "); + } else if (botInterval != null) { + ps.print("start(bot): " + DATE_FORMAT.format(botInterval.getStartDate()) + ", "); + ps.print("end(bot): " + DATE_FORMAT.format(botInterval.getEndDate()) + ", "); + } else { + ps.print("start: NONE, end: NONE, "); + } + ps.print("topAngle: "); + if (topInterval != null) { + ps.print(topInterval.getAngle()); + } else { + ps.print("NONE"); + } + ps.print(", botAngle: "); + if (botInterval != null) { + ps.print(botInterval.getAngle()); + } else { + ps.print("NONE"); + } + ps.print(", topYStage: "); + if (topInterval != null) { + ps.print(topInterval.getYStage()); + } else { + ps.print("NONE"); + } + ps.print(", botYStage: "); + if (botInterval != null) { + ps.print(botInterval.getYStage()); + } else { + ps.print("NONE"); + } + ps.println(); + ps.flush(); + } + + private static List<MotorPositionInterval> getMotorPositionIntervals(String path, Side side) throws Exception { + MotorPositionLoader loader = new MotorPositionLoader(); + loader.setSide(side); + loader.load(path); + return loader.findIntervals(); + } + + /** + * Check if the run record looks good. + * + * @param data + * @return + */ + private static boolean acceptRun(RunData data) { + return !data.getRecord().get("to_tape").equals("JUNK") && data.getRecord().get("trigger_config").trim().length() > 0 + && !data.getRecord().get("trigger_config").contains("cosmic") && data.getStartDate() != null; + } + + private static RunMap loadRunMap(String path) { + final File runFile = new File(path); + final RunSpreadsheet runSpreadsheet = new RunSpreadsheet(runFile); + return runSpreadsheet.getRunMap(); + } + + private static MotorPositionInterval findInterval(List<MotorPositionInterval> intervals, Date date) { + MotorPositionInterval interval = null; + Iterator<MotorPositionInterval> it = intervals.listIterator(); + while ((interval = it.next()) != null) { + + // Start and end dates in the interval. + Date startDate = interval.getStartDate(); + Date endDate = interval.getEndDate(); + + // Check if given date is within the interval. + if ((startDate.compareTo(date) == -1 || startDate.compareTo(date) == 0) && endDate.compareTo(date) == 1 + || endDate.compareTo(date) == 0) { + break; + } + + // Didn't find it. + if (!it.hasNext()) { + interval = null; + break; + } + } + return interval; + } +}