/* * To change this template, choose Tools | Templates * and open the template in the editor. */ package experiment; import convergence.Convergence; import deploy.common.ExperimentStartDialogue; import deploy.JND.JNDExperimentConfFrame; import common.logging.SubjectFilenameBuilderImpl; import java.awt.Color; import java.awt.Cursor; import java.awt.Graphics; import java.awt.GraphicsEnvironment; import java.awt.Image; import java.awt.Point; import java.awt.Toolkit; import java.awt.event.KeyEvent; import java.awt.event.KeyListener; import java.awt.event.WindowAdapter; import java.awt.event.WindowEvent; import java.io.BufferedReader; import java.io.BufferedWriter; import java.io.DataInputStream; import java.io.File; import java.io.FileInputStream; import java.io.FileWriter; import java.io.InputStreamReader; import java.util.List; import java.util.Vector; import javax.swing.JFrame; import log.LogfileRow; import screens.ExperimentEndScreen; import screens.Screen; import screens.BeginScreen; import screens.TestCorrectScreen; import screens.TestIncorrectScreen; import screens.TestRightScreen; import screens.TestTwoCorrScreen; import util.Globals; import static common.logging.ExperimentLogging.*; import util.LatinSquareGenerator; import util.Util; /** * * @author Will */ public class BasicJNDExperiment extends JFrame implements Experiment, KeyListener { private Subject currentSubject; //subject being tested private String NEXTLINE = "\n"; private static class JNDFilenameBuilder extends SubjectFilenameBuilderImpl{ private final String mid; public JNDFilenameBuilder(String expCond) { this.mid = expCond; } @Override public String buildFileName(Subject person, String fend) { return mid + SubjectFilenameBuilderImpl.delimiter + super.buildFileName(person, fend); } } // Ben 4/8/11 to make this program handle various design conditions // final private String expCond = "ScalingCond"; // final private String expCond = "PointNumCond"; // final private String expCond = "PointSizeCond"; // final private String expCond = "PointStyleCond"; // final private String expCond = "PointHueCond"; // final private String expCond = "PointColorCond"; // final private String expCond = "PointRingCond"; // final private String expCond = "PointTeeCond"; final private String expCond = "PointShapeCond"; public class TwoCorrImageThread extends Thread { // emily 06/2010 public Screen screen; public Image image; //Override public void run() { screen = currentCondition.getNextScreen(); image = screen.getImage(); } } private int iPointsLeft = 100; final private String iPointsLeftLabel = "iPointsLeft"; private int iPointsRight = 100; final private String iPointsRightLabel = "iPointsRight"; private int iCapComplete = 30; final private String iCapCompleteLabel = "iCapComplete"; private int iScreenResX = 500; final private String iScreenResXLabel = "iScreenResX"; private int iScreenResY = 500; final private String iScreenResYLabel = "iScreenResY"; private int iTestNumTrials = 30; final private String iTestNumTrialsLabel = "iTestNumTrials"; private double dErrLeft = 0.0001; final private String dErrLeftLabel = "dErrLeft"; private double dErrRight = 0.0001; final private String dErrRightLabel = "dErrRight"; private double dStep = 0.01; final private String dStepLabel = "dStep"; private boolean bIsAxisOn = false; final private String bIsAxisOnLabel = "bIsAxisOn"; private boolean bIsLabelsOn = false; final private String bIsLabelsOnLabel = "bIsLabelsOn"; private boolean bIsFilterKeys = false; final private String bIsFilterKeysLabel = "bIsFilterKeys"; static final String FOLDER_CONF = "./conf/"; static final String CONF_COND_NAME = "BasicJND.cond"; static final String CONF_EXP_NAME = "BasicJND.conf"; static final String CONF_WARNING = "# Generated file. Don't edit by hand."; static final String CONF_DELIMITER = " = "; private String sCondConfFile = FOLDER_CONF + CONF_COND_NAME; private String sExpConfFile = FOLDER_CONF + CONF_EXP_NAME; // Stuff beyond here doesn't need to be saved in the config file private boolean isDebug = true; private boolean isTest = true; private boolean isCorrect = false; private boolean bIsBlocked = true; // assumed blocked for now private boolean bIsCounterbalanced = true; // assumed counterbalanced // Conditions to run protected List conditionsQueue = new Vector(); private int trialNum = 1; // current trial number private int currentCondNum = 0; // current condition number // Configuration frame JNDExperimentConfFrame myConfFrame = new JNDExperimentConfFrame(this); // Experiment control ExperimentControl expCtrl = new ExperimentControl(); // A vector of vectors of Integers which hold our latin square for // counterbalancing Vector latinSquare = null; // Screens Screen beginScreen = new BeginScreen(); Screen correctScreen = new TestCorrectScreen(); Screen incorrectScreen = new TestIncorrectScreen(); Screen currentStimuliScreen = new TestRightScreen(); Screen currentScreen; Image screenImage; // Data output file name private String dataFilename; private String summaryFilename; private String XMLFilename; private String dataFolder; // Conditions stuff private Condition currentCondition; private Vector conditions = new Vector(); // Place-holder private Convergence myConverge; // width and height of display int w, h; // current subject number private int currentSubjectNumber = 1; // GetImageThread private TwoCorrImageThread imgThread; // emily 06/2010 /** * Constructor. */ public BasicJNDExperiment() { super(); this.loadConfiguration(); this.setupConfFrame(); // remove window borders, etc. this.setUndecorated(true); // Handle a closing window this.addWindowListener(new WindowAdapter() { public void windowClosing(WindowEvent e) { handleWindowClose(); } }); // set cursor to invisible Toolkit tk = Toolkit.getDefaultToolkit(); Cursor invisibleCursor = tk.createCustomCursor(tk.createImage(""), new Point(), null); this.setCursor(invisibleCursor); } /** * Returns the string being used as a delimiter. * * @return */ public static String getConfDelimiter() { return CONF_DELIMITER; } /** * Sets up the configuration editor frame. */ private void setupConfFrame() { myConfFrame.clearTable(); myConfFrame.addConfRow("Number of points on left", new Integer(iPointsLeft), Integer.class, iPointsLeftLabel, new Integer(1), new Integer(5000)); myConfFrame.addConfRow("Number of points on right", new Integer(iPointsRight), Integer.class, iPointsRightLabel, new Integer(1), new Integer(5000)); myConfFrame.addConfRow("Max trials per condition", new Integer(iCapComplete), Integer.class, iCapCompleteLabel, new Integer(1), new Integer(1000)); myConfFrame.addConfRow("Horizontal resolution", new Integer(iScreenResX), Integer.class, iScreenResXLabel, new Integer(1), new Integer(50000)); myConfFrame.addConfRow("Vertical resolution", new Integer(iScreenResY), Integer.class, iScreenResYLabel, new Integer(1), new Integer(50000)); myConfFrame.addConfRow("Number of test trials", new Integer(iTestNumTrials), Integer.class, iTestNumTrialsLabel, new Integer(1), new Integer(1000)); myConfFrame.addConfRow("Left R error", new Double(dErrLeft), Double.class, dErrLeftLabel, new Double(0.0), new Double(1.0)); myConfFrame.addConfRow("Right R error", new Double(dErrRight), Double.class, dErrRightLabel, new Double(0.0), new Double(1.0)); myConfFrame.addConfRow("Step size", new Double(dStep), Double.class, dStepLabel, new Double(0.000001), new Double(1.0)); myConfFrame.addConfRow("Axis on", new Boolean(bIsAxisOn), Boolean.class, bIsAxisOnLabel, new Boolean(false), new Boolean(true)); myConfFrame.addConfRow("Labels on", new Boolean(bIsLabelsOn), Boolean.class, bIsLabelsOnLabel, new Boolean(false), new Boolean(true)); myConfFrame.addConfRow("Filter disallowed keys", new Boolean(bIsFilterKeys), Boolean.class, bIsFilterKeysLabel, new Boolean(false), new Boolean(true)); } /** * Returns the string for the configuration file for the current * experiment configuration. * * @return */ public String getConfString() { String temp = ""; temp = temp + CONF_WARNING + "\n"; temp = appendConfLine(temp, bIsAxisOnLabel, new Boolean(bIsAxisOn).toString()); temp = appendConfLine(temp, bIsLabelsOnLabel, new Boolean(bIsLabelsOn).toString()); temp = appendConfLine(temp, bIsFilterKeysLabel, new Boolean(bIsFilterKeys).toString()); temp = appendConfLine(temp, dErrLeftLabel, new Double(dErrLeft).toString()); temp = appendConfLine(temp, dErrRightLabel, new Double(dErrRight).toString()); temp = appendConfLine(temp, dStepLabel, new Double(dStep).toString()); temp = appendConfLine(temp, iCapCompleteLabel, new Integer(iCapComplete).toString()); temp = appendConfLine(temp, iPointsLeftLabel, new Integer(iPointsLeft).toString()); temp = appendConfLine(temp, iPointsRightLabel, new Integer(iPointsRight).toString()); temp = appendConfLine(temp, iScreenResXLabel, new Integer(iScreenResX).toString()); temp = appendConfLine(temp, iScreenResYLabel, new Integer(iScreenResY).toString()); temp = appendConfLine(temp, iTestNumTrialsLabel, new Integer(iTestNumTrials).toString()); return temp; } /** * Parses a value from a given string read from an experiment's * configuration file. * * @param var * @param value */ private void parseValue(String var, String value) { try { if (var.equalsIgnoreCase(bIsAxisOnLabel)) { bIsAxisOn = Boolean.parseBoolean(value); } else if (var.equalsIgnoreCase(bIsLabelsOnLabel)) { bIsLabelsOn = Boolean.parseBoolean(value); } else if (var.equalsIgnoreCase(bIsFilterKeysLabel)) { bIsFilterKeys = Boolean.parseBoolean(value); } else if (var.equalsIgnoreCase(dErrLeftLabel)) { dErrLeft = Double.parseDouble(value); } else if (var.equalsIgnoreCase(dErrRightLabel)) { dErrRight = Double.parseDouble(value); } else if (var.equalsIgnoreCase(dStepLabel)) { dStep = Double.parseDouble(value); } else if (var.equalsIgnoreCase(iCapCompleteLabel)) { iCapComplete = Integer.parseInt(value); } else if (var.equalsIgnoreCase(iPointsLeftLabel)) { iPointsLeft = Integer.parseInt(value); } else if (var.equalsIgnoreCase(iPointsRightLabel)) { iPointsRight = Integer.parseInt(value); } else if (var.equalsIgnoreCase(iScreenResXLabel)) { iScreenResX = Integer.parseInt(value); } else if (var.equalsIgnoreCase(iScreenResYLabel)) { iScreenResY = Integer.parseInt(value); } else if (var.equalsIgnoreCase(iTestNumTrialsLabel)) { iTestNumTrials = Integer.parseInt(value); } } catch (Exception e) { e.printStackTrace(); } } /** * Attempts to clear the majority of pointers from this experiment. * <p> * This is in hopes that the java garbage collection will free this memory * when needed. */ public void clearExperiment() { this.conditions.clear(); this.conditionsQueue.clear(); this.myConverge = null; this.currentCondition = null; } /** * Sets up a brand new experiment. */ public void setupExperiment() { this.isTest = false; this.clearExperiment(); loadConfiguration(); loadConditions(); resetExperimentParameters(); // Get a latin square nxn size, where n is the number of conditions // loaded. Note that this square goes from 1 to n (rather than 0 to n-1) //latinSquare = LatinSquareGenerator.getBasicBalancedLatinSquare(conditions.size()); } public void setupTest() { this.isTest = true; this.clearExperiment(); loadConfiguration(); // **Change number of points for test trials here // order of values is: startL, startR, pointsL, pointsR, err, err, step-size, xx, xx, scaling, pointSize, pointStyle, pointHue BasicJNDCondition testCondition = new BasicJNDCondition(0.1, 0.8, true, 100, 100, 0.001, 0.001, 0.05, true, true, 0.9, 8, 15, 1); ((BasicJNDCondition) testCondition).setCompleteCap(this.iTestNumTrials); conditions.add(testCondition); resetExperimentParameters(); } /** * Resets experiment parameters. */ public void resetExperimentParameters() { this.trialNum = 1; this.currentCondNum = 0; this.expCtrl = new ExperimentControl(); } /** * Helper to append a line to a configuration file string. * * @param confString * @param var * @param value * @return */ private String appendConfLine(String confString, String var, String value) { return confString + var + CONF_DELIMITER + value + "\n"; } /** * Backs up the current configuration (in case we'd like to check that things were ok) */ private void backupConfiguration(Subject person) { Util.safeCopyFile(new File(this.sCondConfFile), getCustomDataFile(person, CONF_COND_NAME)); Util.safeCopyFile(new File(this.sExpConfFile), getCustomDataFile(person, CONF_EXP_NAME)); } /** * Runs an experiment given a subjectID string. * * @param subjectID */ public void run(Subject person) { this.loadConfiguration(); createLogfileAndFolder(person); backupConfiguration(person); setVisibleAndRun(); } /** * Places conditions into the conditionsQueue. * <p> * This function counterbalances if <code>bIsCounterbalanced</code> is true. * * @param subjectNumber */ private void queueConditions(int subjectNumber) { if (isTest) { doSimpleQueueConditions(); } else { doLatinSquaresQueueConditions(subjectNumber); } currentCondition = (Condition) conditionsQueue.get(0); currentScreen = beginScreen; screenImage = currentScreen.getImage(); } /** * Loads the conditions in the order of the simple latin squares generator * in LatinSquareGenerator. * * @param subjectNumber */ private void doLatinSquaresQueueConditions(int subjectNumber) { //latinSquare = LatinSquareGenerator.getBasicBalancedLatinSquare(conditions.size()); //Vector conditionOrder = (Vector)this.latinSquare.get(subjectNumber%latinSquare.size()); Vector conditionOrder = LatinSquareGenerator.getConditionOrder(subjectNumber); // replace nulls with stuff for (int i = 0; i < conditionOrder.size(); i++) { conditionsQueue.add(conditions.get(((Integer) conditionOrder.get(i)).intValue())); } // not sure the purpose here...substituted in "subjectNumber" here if (isDebug) { System.out.println("Used order (offset by " + subjectNumber + "): " + conditionOrder.toString()); } } /** * Simply places all of the conditions from <code>conditions</code> into * <code>conditionsQueue</code> in the same order. * <p> * If blocked, all conditions are run in the order of <code>conditionsQueue * </code>. */ private void doSimpleQueueConditions() { // add conditions to the queue for (int i = 0; i < conditions.size(); i++) { conditionsQueue.add(i, conditions.get(i)); } } /** * Makes this frame visible and runs through the current experiment setup. */ public void setVisibleAndRun() { queueConditions(currentSubjectNumber); this.addKeyListener(this); doFullscreen(); this.update(this.getGraphics()); this.setVisible(true); } public void run(int subjectNum, String initials) { this.currentSubject = new Subject(subjectNum, initials, ExperimentType.JND, new JNDFilenameBuilder(expCond)); this.setupExperiment(); this.currentSubjectNumber = currentSubject.getNumber(); run(currentSubject); } /** * What to do when the experiment window is closed. */ private void handleWindowClose() { this.setVisible(false); this.dispose(); this.removeKeyListener(this); this.clearExperiment(); } /** * Makes the experiment window fullscreen. */ private void doFullscreen() { // set background color this.setBackground(Color.BLACK); // switch to fullscreen mode GraphicsEnvironment.getLocalGraphicsEnvironment(). getDefaultScreenDevice().setFullScreenWindow(this); w = this.getWidth(); h = this.getHeight(); } /** * Draws the current experiment image as the current frame. * * @param g */ public void paint(Graphics g) { if (screenImage != null) { g.drawImage(screenImage, w / 2 - screenImage.getWidth(this) / 2, h / 2 - screenImage.getHeight(this) / 2, this); } } /** * Tests an experiment. */ public void test() { this.setupTest(); setVisibleAndRun(); } /** * Shows the JFrame meant to configure this experiment. * * @param isShowFrame */ public void showConfigureFrame(boolean isShowFrame) { if (isShowFrame) { setupConfFrame(); myConfFrame.setDefaultCloseOperation(JFrame.HIDE_ON_CLOSE); myConfFrame.setVisible(true); } else { myConfFrame.setVisible(false); } } /** * Loads an experiment configuration from a file. * * @param configFile */ public void loadConfigurationFromFile(File configFile) { try { FileInputStream fstream = new FileInputStream(configFile); DataInputStream in = new DataInputStream(fstream); BufferedReader br = new BufferedReader(new InputStreamReader(in)); String strLine; while ((strLine = br.readLine()) != null) { if (isDebug) { System.out.println("Debug: " + strLine); } String tokens[] = strLine.split(CONF_DELIMITER); if (tokens.length == 2) { parseValue(tokens[0].trim(), tokens[1].trim()); } } } catch (Exception e) { e.printStackTrace(); } } /** * Checks a string if it can be parsed properly. * <p> * TODO: complete this function if necessary * * @param conf * @return */ public boolean checkConfigurationString(String conf) { String[] lines = conf.split("$"); System.out.println(lines.length); for (int i = 0; i < lines.length; i++) { String tokens[] = lines[i].split(CONF_DELIMITER); if (tokens.length == 2) { try { // TODO: place appropriate check code here //parseValue(tokens[0].trim(), tokens[1].trim()); } catch (Exception e) { return false; } } } return true; } /** * Loads configuration options from a string. * * @param str */ public void loadConfigurationFromString(String str) { String[] lines = str.split("\\n"); for (int i = 0; i < lines.length; i++) { String tokens[] = lines[i].split(CONF_DELIMITER); if (tokens.length == 2) { try { if (isDebug) { System.out.println(tokens[0].trim() + "--" + tokens[1].trim()); } parseValue(tokens[0].trim(), tokens[1].trim()); } catch (Exception e) { System.err.println("String parse error."); } } } } /** * Saves the current experiment configuration to a file. * * @param configFile */ public void saveConfigurationToFile(File configFile) { BufferedWriter out; try { out = new BufferedWriter(new FileWriter(configFile, false)); out.write(this.getConfString()); out.close(); } catch (Exception e) { e.printStackTrace(); } } /** * Saves the current experiment configuration to a predefined file. */ public void saveConfiguration() { saveConfigurationToFile(new File(this.sExpConfFile)); } /** * Loads the experiment configuration from a predefined file. */ public void loadConfiguration() { loadConfigurationFromFile(new File(this.sExpConfFile)); } /** * Loads conditions from a file. * <p> * All conditions are parsed from a conditions file, and using the values * from the current experiment's configuration new conditions are created * and placed into the Vector conditions. The pointers are then ordered * according to shuffling, counterbalancing, etc., and then inserted into * the List conditionsQueue, where they are run in the order of conditionsQueue. * * Note: this function should be called *after* the experiment configuration * is set as it relies on values in the configuration. * * @param condConfFile */ public void loadConditionsFromFile(File condConfFile) { try { FileInputStream fstream = new FileInputStream(condConfFile); // Get the object of DataInputStream DataInputStream in = new DataInputStream(fstream); BufferedReader br = new BufferedReader(new InputStreamReader(in)); String strLine; // Read File Line By Line while ((strLine = br.readLine()) != null) { System.out.println(strLine); // Edited by Ben 09242010 to work for the scaling experiment. // Reads in a scaling condition here, passes it to BasicJNDCond, which sends it to the // the testtwocorr screen and eventually to the distribution2D file, where it is used to // set the scaling factor to draw the scatterplot. // Edited again 11/2/10; set the experimental condition at the top of the file as expCond // now parses scaling, numPoints, and dotSize from the condition file for ALL conditions // then passes these as necessary to the BasicJNDCond // split via whitespace String tokens[] = strLine.split("\\s"); if (tokens.length >= 4) { if (tokens[0].equals(expCond)) { try { double base = Double.parseDouble(tokens[1]); double diff = Double.parseDouble(tokens[2]); boolean isDescending = Boolean.parseBoolean(tokens[3]); double scalingVal = Double.parseDouble(tokens[4]); double dotSize = Double.parseDouble(tokens[5]); int numPoints = Integer.parseInt(tokens[6]); int dotStyle = Integer.parseInt(tokens[7]); int dotHue = Integer.parseInt(tokens[8]); if (isDebug) { System.out.println("Parsed: " + base + " " + diff + " " + isDescending + " " + scalingVal); } BasicJNDCondition tempCond = new BasicJNDCondition(base, diff, isDescending, numPoints, numPoints, dErrLeft, dErrRight, dStep, bIsAxisOn, bIsLabelsOn, scalingVal, dotSize, dotStyle, dotHue); tempCond.setCompleteCap(this.iCapComplete); conditions.add(tempCond); } catch (Exception e) { System.out.println("Parse error."); } } } } // Close the input stream in.close(); } catch (Exception e) {// Catch exception if any System.err.println("Error: " + e.getMessage()); } } /** * Loads conditions from the preset conditions file. */ public void loadConditions() { loadConditionsFromFile(new File(this.sCondConfFile)); } /** * Is the current key ok to press? * * @param e * @param expCtrl * @return */ public boolean isKeyAllowed(KeyEvent e, ExperimentControl expCtrl) { boolean result = false; int currentState = expCtrl.getCurrentState(); switch (currentState) { case (ExperimentControl.EXP_DISPLAY): if (e.getKeyCode() == KeyEvent.VK_Z || e.getKeyCode() == KeyEvent.VK_M) { result = true; } break; default: result = true; } return result; } // part of KeyListener public void keyTyped(KeyEvent e) { } // part of KeyListener public void keyReleased(KeyEvent e) { } // part of KeyListener public void keyPressed(KeyEvent e) { //int correctKey = currentScreen.getCorrectKey(); int currentKey = e.getKeyCode(); int correctKey = currentScreen.getCorrectKey(); isCorrect = (currentKey == correctKey); // always close this window when ESC is pressed if (currentKey == KeyEvent.VK_ESCAPE) { this.getToolkit().getSystemEventQueue().postEvent(new WindowEvent(this, WindowEvent.WINDOW_CLOSING)); } // don't do anything if the current key pressed is not allowed if (this.bIsFilterKeys && !isKeyAllowed(e, expCtrl)) { return; } // update the experiment state expCtrl.processInput(currentKey, currentScreen); switch (expCtrl.getCurrentState()) { case ExperimentControl.EXP_BEGIN: currentScreen = this.beginScreen; screenImage = currentScreen.getImage(); this.update(this.getGraphics()); break; case ExperimentControl.EXP_FEEDBACK: // TODO: append data to log file if appropriate if (!isTest) { appendLogfileRow(Globals.GLOBAL_TIMER.toc(), correctKey, currentKey); if (currentScreen.getClass().equals(TestTwoCorrScreen.class)) { writeScreenToFile(((TestTwoCorrScreen) currentScreen).getXMLString()); } } // set feedback and increment if (currentCondition.setTrialResponse(currentKey)) { currentScreen = this.correctScreen; } else { currentScreen = this.incorrectScreen; } this.trialNum++; // handle completed conditions if (currentCondition.isCompleted()) { appendSummaryRow(); currentCondNum++; if (currentCondNum >= conditionsQueue.size()) { expCtrl.setExpEnd(); currentScreen = new ExperimentEndScreen(); } else { currentCondition = (BasicJNDCondition) (conditionsQueue.get(currentCondNum)); } } screenImage = currentScreen.getImage(); this.update(this.getGraphics()); // creates the next 2Corr image while waiting for subject to press space bar (emily 06/2010) try { imgThread = new TwoCorrImageThread(); imgThread.start(); imgThread.join(); } catch (InterruptedException ex) { System.out.println("Interupted Exception"); } break; case ExperimentControl.EXP_END: doExperimentEnd(); screenImage = currentScreen.getImage(); break; case ExperimentControl.EXP_DISPLAY: long t = System.currentTimeMillis(); //tag if (trialNum <= 1) { currentScreen = currentCondition.getNextScreen(); screenImage = currentScreen.getImage(); } else { currentScreen = imgThread.screen; screenImage = imgThread.image; } Globals.GLOBAL_TIMER.tic(); this.update(this.getGraphics()); System.out.println(System.currentTimeMillis() - t); //tag break; } } /** * Appends XML code (that represents a screen) to a file. * <p> * Appends the current trial number and initials to the filename. * * @param xmlString */ private void writeScreenToFile(String xmlString) { String prefix = "<trial>\n"; prefix = prefix + "\t<trial_info>\n"; prefix = prefix + "\t\t<trial_num>" + trialNum + "</trial_num>" + "\n"; prefix = prefix + "\t\t<r_base>" + myConverge.getTrialParam() + "</r_base>" + "\n"; prefix = prefix + "\t\t<r_compare>" + myConverge.getTrialCompare() + "</r_compare>" + "\n"; prefix = prefix + "\t\t<is_correct>" + isCorrect + "</is_correct>\n"; prefix = prefix + "\t\t<is_descending>" + ((BasicJNDCondition) currentCondition).getIsDescending() + "</is_descending>\n"; prefix = prefix + "\t\t<base_on_left>" + ((BasicJNDCondition) currentCondition).getIsBaseOnLeft() + "</base_on_left>\n"; prefix = prefix + "\t\t<scaling_val>" + ((BasicJNDCondition) currentCondition).getScalingVal() + "</scaling_val>\n"; prefix = prefix + "\t\t<dot_size>" + ((BasicJNDCondition) currentCondition).getDotSize() + "</dot_size>\n"; prefix = prefix + "\t\t<num_points>" + ((BasicJNDCondition) currentCondition).getNumPoints() + "</num_points>\n"; prefix = prefix + "\t\t<dot_style>" + ((BasicJNDCondition) currentCondition).getDotStyle() + "</dot_style>\n"; prefix = prefix + "\t\t<dot_hue>" + ((BasicJNDCondition) currentCondition).getDotHue() + "</dot_hue>\n"; prefix = prefix + "\t</trial_info>\n"; xmlString = prefix + xmlString + "</trial>\n"; writeToFile(currentSubject, PathParts.XML, xmlString); } /** * Appends a row of data to the log file. * * @param trial_time_in_ms * @param correctKey * @param currentKey */ private void appendLogfileRow(long trial_time_in_ms, int correctKey, int currentKey) { // very very dumb way to implement log file // LogfileRow(int trialNum, double trialTime, double base, double compare, double difference, boolean isCorrect, boolean isAscending, String side) // TODO: make the condition themselves print to the log file myConverge = currentCondition.getConvergence(); boolean isDescending = ((BasicJNDCondition) currentCondition).getIsDescending(); boolean isBaseOnLeft = ((BasicJNDCondition) currentCondition).getIsBaseOnLeft(); double scalingVal = ((BasicJNDCondition) currentCondition).getScalingVal(); int numPoint = ((BasicJNDCondition) currentCondition).getNumPoints(); double dotSize = ((BasicJNDCondition) currentCondition).getDotSize(); int dotStyle = ((BasicJNDCondition) currentCondition).getDotStyle(); int dotHue = ((BasicJNDCondition) currentCondition).getDotHue(); LogfileRow lr = new LogfileRow(trialNum, trial_time_in_ms, myConverge.getTrialParam(), myConverge.getTrialCompare(), Math.abs(myConverge.getTrialCompare() - myConverge.getTrialParam()), correctKey == currentKey, !isDescending, isBaseOnLeft ? LogfileRow.RIGHT : LogfileRow.LEFT, scalingVal, dotSize, numPoint, dotStyle, dotHue); String toWrite = lr.toString() + NEXTLINE; System.out.print(toWrite); writeToFile(currentSubject, PathParts.Data, toWrite); } /** * Appends a row of data to the summary file to represent each completed level. * */ private void appendSummaryRow() { myConverge = currentCondition.getConvergence(); boolean isDescending = ((BasicJNDCondition) currentCondition).getIsDescending(); double scalingVal = ((BasicJNDCondition) currentCondition).getScalingVal(); double dotSize = ((BasicJNDCondition) currentCondition).getDotSize(); int numPoints = ((BasicJNDCondition) currentCondition).getNumPoints(); int dotStyle = ((BasicJNDCondition) currentCondition).getDotStyle(); int dotHue = ((BasicJNDCondition) currentCondition).getDotHue(); String summary = myConverge.getTrialCompare() + " " + isDescending + " " + myConverge.getWindowAverage() + " " + myConverge.getTrialsToConverge() + " " + scalingVal + " " + dotSize + " " + numPoints + " " + dotStyle + " " + dotHue + "\n"; writeToFile(currentSubject, PathParts.Summary, summary); } private void doExperimentEnd() { } }