--- /dev/null
+package net.sf.openrocket.file;
+
+import java.io.IOException;
+import java.io.OutputStream;
+import java.io.PrintWriter;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.Comparator;
+import java.util.List;
+
+import net.sf.openrocket.aerodynamics.Warning;
+import net.sf.openrocket.aerodynamics.WarningSet;
+import net.sf.openrocket.document.Simulation;
+import net.sf.openrocket.simulation.FlightData;
+import net.sf.openrocket.simulation.FlightDataBranch;
+import net.sf.openrocket.simulation.FlightEvent;
+import net.sf.openrocket.simulation.FlightDataBranch.Type;
+import net.sf.openrocket.unit.Unit;
+import net.sf.openrocket.util.Pair;
+import net.sf.openrocket.util.TextUtil;
+
+public class CSVExport {
+
+ /**
+ * Exports the specified flight data branch into a CSV file.
+ *
+ * @param stream the stream to write to.
+ * @param simulation the simulation being exported.
+ * @param branch the branch to export.
+ * @param fields the fields to export (in appropriate order).
+ * @param units the units of the fields.
+ * @param fieldSeparator the field separator string.
+ * @param commentStarter the comment starting character(s).
+ * @param simulationComments whether to output general simulation comments.
+ * @param fieldComments whether to output field comments.
+ * @param eventComments whether to output comments for the flight events.
+ * @throws IOException if an I/O exception occurs.
+ */
+ public static void exportCSV(OutputStream stream, Simulation simulation,
+ FlightDataBranch branch, FlightDataBranch.Type[] fields, Unit[] units,
+ String fieldSeparator,String commentStarter, boolean simulationComments,
+ boolean fieldComments, boolean eventComments) throws IOException {
+
+ if (fields.length != units.length) {
+ throw new IllegalArgumentException("fields and units lengths must be equal " +
+ "(" + fields.length + " vs " + units.length + ")");
+ }
+
+
+ PrintWriter writer = null;
+ try {
+
+ writer = new PrintWriter(stream);
+
+ // Write the initial comments
+ if (simulationComments) {
+ writeSimulationComments(writer, simulation, branch, fields, commentStarter);
+ }
+
+ if (simulationComments && fieldComments) {
+ writer.println(commentStarter);
+ }
+
+ if (fieldComments) {
+ writer.print(commentStarter + " ");
+ for (int i=0; i < fields.length; i++) {
+ writer.print(fields[i].getName() + " (" + units[i].getUnit() + ")");
+ if (i < fields.length-1) {
+ writer.print(fieldSeparator);
+ }
+ }
+ writer.println();
+ }
+
+ writeData(writer, branch, fields, units, fieldSeparator,
+ eventComments, commentStarter);
+
+
+ } finally {
+ if (writer != null) {
+ try {
+ writer.close();
+ } catch (Exception e) {
+ e.printStackTrace();
+ }
+ }
+ }
+ }
+
+ private static void writeData(PrintWriter writer, FlightDataBranch branch,
+ Type[] fields, Unit[] units, String fieldSeparator, boolean eventComments,
+ String commentStarter) {
+
+ // Number of data points
+ int n = branch.getLength();
+
+ // Flight events in occurrance order
+ List<Pair<Double, FlightEvent>> events = branch.getEvents();
+ Collections.sort(events, new Comparator<Pair<Double, FlightEvent>>() {
+ @Override
+ public int compare(Pair<Double, FlightEvent> o1, Pair<Double, FlightEvent> o2) {
+ return Double.compare(o1.getU(), o2.getU());
+ }
+ });
+ int eventPosition = 0;
+
+ // List of field values
+ List<List<Double>> fieldValues = new ArrayList<List<Double>>();
+ for (Type t: fields) {
+ fieldValues.add(branch.get(t));
+ }
+
+ // Time variable
+ List<Double> time = branch.get(FlightDataBranch.TYPE_TIME);
+ if (eventComments && time == null) {
+ // If time information is not available, print events at beginning of file
+ for (Pair<Double, FlightEvent> e: events) {
+ printEvent(writer, e, commentStarter);
+ }
+ eventPosition = events.size();
+ }
+
+
+ // Loop over all data points
+ for (int pos=0; pos<n; pos++) {
+
+ // Check for events to store
+ if (eventComments && time != null) {
+ double t = time.get(pos);
+
+ while ((eventPosition < events.size()) &&
+ (events.get(eventPosition).getU() <= t)) {
+ printEvent(writer, events.get(eventPosition), commentStarter);
+ eventPosition++;
+ }
+ }
+
+ // Store CSV line
+ for (int i=0; i < fields.length; i++) {
+ double value = fieldValues.get(i).get(pos);
+ writer.print(TextUtil.doubleToString(units[i].toUnit(value)));
+ if (i < fields.length-1) {
+ writer.print(fieldSeparator);
+ }
+ }
+ writer.println();
+
+ }
+
+ // Store any remaining events
+ if (eventComments && time != null) {
+ while (eventPosition < events.size()) {
+ printEvent(writer, events.get(eventPosition), commentStarter);
+ eventPosition++;
+ }
+ }
+
+ }
+
+
+ private static void printEvent(PrintWriter writer, Pair<Double, FlightEvent> e,
+ String commentStarter) {
+ writer.println(commentStarter + " Event " + e.getV().getType().toString() +
+ " occurred at t=" + TextUtil.doubleToString(e.getU()) + " seconds");
+ }
+
+ private static void writeSimulationComments(PrintWriter writer,
+ Simulation simulation, FlightDataBranch branch, Type[] fields,
+ String commentStarter) {
+
+ String line;
+
+ line = simulation.getName();
+
+ FlightData data = simulation.getSimulatedData();
+
+ switch (simulation.getStatus()) {
+ case UPTODATE:
+ line += " (Up to date)";
+ break;
+
+ case LOADED:
+ line += " (Data loaded from a file)";
+ break;
+
+ case OUTDATED:
+ line += " (Data is out of date)";
+ break;
+
+ case EXTERNAL:
+ line += " (Imported data)";
+ break;
+
+ case NOT_SIMULATED:
+ line += " (Not simulated yet)";
+ break;
+ }
+
+ writer.println(commentStarter + " " + line);
+
+
+ writer.println(commentStarter + " " + branch.getLength() + " data points written for "
+ + fields.length + " variables.");
+
+
+ if (data == null) {
+ writer.println(commentStarter + " No simulation data available.");
+ return;
+ }
+ WarningSet warnings = data.getWarningSet();
+
+ if (!warnings.isEmpty()) {
+ writer.println(commentStarter + " Simulation warnings:");
+ for (Warning w: warnings) {
+ writer.println(commentStarter + " " + w.toString());
+ }
+ }
+ }
+
+}