@@ -419,11 +446,13 @@
Export video
+
+
diff --git a/js/expo.js b/js/expo.js
index 9b4ca621..80b8e47e 100644
--- a/js/expo.js
+++ b/js/expo.js
@@ -7,12 +7,16 @@
*/
function ExpoCurve(offset, power, inputRange, outputRange, steps) {
var
- curve, inputScale;
+ curve, inputScale, rawInputScale;
function lookupStraightLine(input) {
return (input + offset) * inputScale;
}
+ this.lookupRaw = function(input) {
+ return (input + offset) * rawInputScale;
+ }
+
/**
* An approximation of lookupMathPow by precomputing several expo curve points and interpolating between those
* points using straight line interpolation.
@@ -58,6 +62,8 @@ function ExpoCurve(offset, power, inputRange, outputRange, steps) {
return result;
}
+ rawInputScale = outputRange / inputRange;
+
// If steps argument isn't supplied, use a reasonable default
if (steps === undefined) {
steps = 12;
diff --git a/js/flightlog.js b/js/flightlog.js
index 03352a0f..515256cf 100644
--- a/js/flightlog.js
+++ b/js/flightlog.js
@@ -9,9 +9,9 @@
* Additional computed fields are derived from the original data set and added as new fields in the resulting data.
* Window based smoothing of fields is offered.
*/
-function FlightLog(logData) {
+function FlightLog(logData, newSettings) {
var
- ADDITIONAL_COMPUTED_FIELD_COUNT = 6, /** attitude + PID_SUM **/
+ ADDITIONAL_COMPUTED_FIELD_COUNT = 15, /** attitude + PID_SUM + PID_ERROR + RCCOMMAND_SCALED + GYROADC_SCALED **/
that = this,
logIndex = false,
@@ -33,10 +33,13 @@ function FlightLog(logData) {
maxSmoothing = 0,
smoothedCache = new FIFOCache(2);
+
//Public fields:
this.parser = parser;
+ this.settings = newSettings;
+
this.getMainFieldCount = function() {
return fieldNames.length;
};
@@ -199,6 +202,9 @@ function FlightLog(logData) {
// Add names for our ADDITIONAL_COMPUTED_FIELDS
fieldNames.push("heading[0]", "heading[1]", "heading[2]");
fieldNames.push("axisSum[0]", "axisSum[1]", "axisSum[2]");
+ fieldNames.push("axisError[0]", "axisError[1]", "axisError[2]"); // Custom calculated error field
+ fieldNames.push("rcCommands[0]", "rcCommands[1]", "rcCommands[2]"); // Custom calculated error field
+ fieldNames.push("gyroADCs[0]", "gyroADCs[1]", "gyroADCs[2]"); // Custom calculated error field
fieldNameToIndex = {};
for (i = 0; i < fieldNames.length; i++) {
@@ -469,7 +475,8 @@ function FlightLog(logData) {
gyroADC = [fieldNameToIndex["gyroADC[0]"], fieldNameToIndex["gyroADC[1]"], fieldNameToIndex["gyroADC[2]"]],
accSmooth = [fieldNameToIndex["accSmooth[0]"], fieldNameToIndex["accSmooth[1]"], fieldNameToIndex["accSmooth[2]"]],
magADC = [fieldNameToIndex["magADC[0]"], fieldNameToIndex["magADC[1]"], fieldNameToIndex["magADC[2]"]],
-
+ rcCommand = [fieldNameToIndex["rcCommand[0]"], fieldNameToIndex["rcCommand[1]"], fieldNameToIndex["rcCommand[2]"]],
+
sourceChunkIndex, destChunkIndex,
sysConfig,
@@ -534,6 +541,25 @@ function FlightLog(logData) {
(axisPID[axis][1] !== undefined ? srcFrame[axisPID[axis][1]] : 0) +
(axisPID[axis][2] !== undefined ? srcFrame[axisPID[axis][2]] : 0);
}
+
+ // Calculate the PID Error
+ for (var axis = 0; axis < 3; axis++) {
+ destFrame[fieldIndex++] =
+ (gyroADC[axis] !== undefined ? that.gyroRawToDegreesPerSecond(srcFrame[gyroADC[axis]]) : 0) -
+ (rcCommand[axis] !== undefined ? that.rcCommandRawToDegreesPerSecond(srcFrame[rcCommand[axis]], axis) : 0);
+ }
+ // Calculate the Scaled rcCommand (in deg/s)
+ for (var axis = 0; axis < 3; axis++) {
+ destFrame[fieldIndex++] =
+ (rcCommand[axis] !== undefined ? that.rcCommandRawToDegreesPerSecond(srcFrame[rcCommand[axis]], axis) : 0);
+ }
+
+ // Calculate the scaled Gyro ADC
+ for (var axis = 0; axis < 3; axis++) {
+ destFrame[fieldIndex++] =
+ (gyroADC[axis] !== undefined ? that.gyroRawToDegreesPerSecond(srcFrame[gyroADC[axis]]) : 0);
+ }
+
}
}
}
@@ -881,6 +907,28 @@ FlightLog.prototype.gyroRawToDegreesPerSecond = function(value) {
return this.getSysConfig().gyroScale * 1000000 / (Math.PI / 180.0) * value;
};
+// Convert rcCommand to degrees per second
+FlightLog.prototype.rcCommandRawToDegreesPerSecond = function(value, axis) {
+
+ // Axis 0,1 refers to Roll and Pitch
+ // Axis 2 refers to Yaw.
+
+ // ReWrite or LUXFloat only
+
+
+// if(axis==2 /*YAW*/) {
+// return ((this.settings[0].parameters[axis].value + 47) * value ) >> 7;
+// } else { /*ROLL or PITCH */
+// return ((this.settings[0].parameters[axis].value + 27) * value ) >> 6;
+// }
+
+ if(axis==2 /*YAW*/) {
+ return ((this.getSysConfig().yRate + 47) * value ) >> 7;
+ } else { /*ROLL or PITCH */
+ return ((((axis==0)?this.getSysConfig().rRate:this.getSysConfig().pRate) + 27) * value ) >> 6;
+ }
+};
+
FlightLog.prototype.getReferenceVoltageMillivolts = function() {
return this.vbatADCToMillivolts(this.getSysConfig().vbatref);
};
diff --git a/js/flightlog_fielddefs.js b/js/flightlog_fielddefs.js
index 75f67364..c63b925a 100644
--- a/js/flightlog_fielddefs.js
+++ b/js/flightlog_fielddefs.js
@@ -21,6 +21,7 @@ var
LOGGING_RESUME: 14,
GTUNE_CYCLE_RESULT: 20,
+ FLIGHT_MODE: 30, // New Event type
LOG_END: 255
}),
diff --git a/js/flightlog_fields_presenter.js b/js/flightlog_fields_presenter.js
index a1c6c060..e0c22f8f 100644
--- a/js/flightlog_fields_presenter.js
+++ b/js/flightlog_fields_presenter.js
@@ -59,7 +59,26 @@ function FlightLogFieldPresenter() {
'axisSum[all]': 'PID_sum',
'axisSum[0]' : 'PID_sum[roll]',
'axisSum[1]' : 'PID_sum[pitch]',
- 'axisSum[2]' : 'PID_sum[yaw]'
+ 'axisSum[2]' : 'PID_sum[yaw]',
+
+ //Virtual fields - Add the Error fields
+ 'axisError[all]': 'PID_Error',
+ 'axisError[0]' : 'PID_Error[roll]',
+ 'axisError[1]' : 'PID_Error[pitch]',
+ 'axisError[2]' : 'PID_Error[yaw]',
+
+ //Virtual fields - add the Scaled rcCommands
+ 'rcCommands[all]': 'rcCommands',
+ 'rcCommands[0]' : 'rcCommands[roll]',
+ 'rcCommands[1]' : 'rcCommands[pitch]',
+ 'rcCommands[2]' : 'rcCommands[yaw]',
+
+ //Virtual fields - add the Scaled gyros
+ 'gyroADCs[all]': 'gyros',
+ 'gyroADCs[0]': 'gyros[roll]',
+ 'gyroADCs[1]': 'gyros[pitch]',
+ 'gyroADCs[2]': 'gyros[yaw]'
+
};
function presentFlags(flags, flagNames) {
@@ -119,6 +138,18 @@ function FlightLogFieldPresenter() {
case 'gyroADC[2]':
return Math.round(flightLog.gyroRawToDegreesPerSecond(value)) + " deg/s";
+ case 'axisError[0]':
+ case 'axisError[1]':
+ case 'axisError[2]':
+ return Math.round(value) + " deg/s";
+
+ case 'rcCommand[0]':
+ return Math.round(flightLog.rcCommandRawToDegreesPerSecond(value,0)) + " deg/s";
+ case 'rcCommand[1]':
+ return Math.round(flightLog.rcCommandRawToDegreesPerSecond(value,1)) + " deg/s";
+ case 'rcCommand[2]':
+ return Math.round(flightLog.rcCommandRawToDegreesPerSecond(value,2)) + " deg/s";
+
case 'accSmooth[0]':
case 'accSmooth[1]':
case 'accSmooth[2]':
diff --git a/js/flightlog_parser.js b/js/flightlog_parser.js
index 26b6fe7f..f3b16de4 100644
--- a/js/flightlog_parser.js
+++ b/js/flightlog_parser.js
@@ -188,6 +188,20 @@ var FlightLogParser = function(logData) {
currentMeterScale: 400,
deviceUID: null
},
+
+ // One day, maybe these will be part of the blackbox log; they certainly would be helpfull!
+ // If so, then they can be moved intor the defaultSysConfig definition above.
+ // At the moment they are entered on a dialog box.
+
+ defaultSysConfigExtension = {
+ rcExpo:null,//70, // RC Expo
+ rRate:null,//0, // Roll Rate
+ pRate:null,//0, // Pitch Rate
+ yRate:null,//45, // Yaw Rate
+ rcYawExpo: null,//20, // Yaw Expo
+ superExpoFactor: null,//30, // Super Expo Factor
+ loopTime: null,//500, // Looptime
+ },
frameTypes,
@@ -220,7 +234,7 @@ var FlightLogParser = function(logData) {
//The actual log data stream we're reading:
stream;
-
+
//Public fields:
/* Information about the frame types the log contains, along with details on their fields.
@@ -228,8 +242,10 @@ var FlightLogParser = function(logData) {
*/
this.frameDefs = {};
- this.sysConfig = Object.create(defaultSysConfig);
-
+ // Lets add the custom extensions
+ var completeSysConfig = $.extend({}, defaultSysConfig, defaultSysConfigExtension);
+ this.sysConfig = Object.create(completeSysConfig); // Object.create(defaultSysConfig);
+
/*
* Event handler of the signature (frameValid, frame, frameType, frameOffset, frameSize)
* called when a frame has been decoded.
@@ -331,6 +347,28 @@ var FlightLogParser = function(logData) {
case "rcRate":
that.sysConfig.rcRate = parseInt(fieldValue, 10);
break;
+
+ // In future these fields may exist int the blackbox log
+ case "rcExpo":
+ that.sysConfig.rcExpo = parseInt(fieldValue, 10);
+ break;
+ case "rates":
+ var ratesParams = parseCommaSeparatedIntegers(fieldValue);
+ that.sysConfig.rRate = ratesParams[0];
+ that.sysConfig.pRate = ratesParams[1];
+ that.sysConfig.yRate = ratesParams[2];
+ break;
+ case "rcYawExpo":
+ that.sysConfig.rcYawExpo = parseInt(fieldValue, 10);
+ break;
+ case "superExpoFactor":
+ that.sysConfig.superExpoFactor = parseInt(fieldValue, 10);
+ break;
+ case "looptime":
+ that.sysConfig.loopTime = parseInt(fieldValue, 10);
+ break;
+ /****************************/
+
case "vbatscale":
that.sysConfig.vbatscale = parseInt(fieldValue, 10);
break;
@@ -868,6 +906,10 @@ var FlightLogParser = function(logData) {
lastEvent.data.time = stream.readUnsignedVB();
lastEvent.time = lastEvent.data.time;
break;
+ case FlightLogEvent.FLIGHT_MODE: // get the flag status change
+ lastEvent.data.newFlags = stream.readUnsignedVB();
+ lastEvent.data.lastFlags = stream.readUnsignedVB();
+ break;
case FlightLogEvent.AUTOTUNE_CYCLE_START:
lastEvent.data.phase = stream.readByte();
@@ -963,7 +1005,9 @@ var FlightLogParser = function(logData) {
this.resetStats();
//Reset system configuration to MW's defaults
- this.sysConfig = Object.create(defaultSysConfig);
+ // Lets add the custom extensions
+ var completeSysConfig = $.extend({}, defaultSysConfig, defaultSysConfigExtension);
+ this.sysConfig = Object.create(completeSysConfig); // Object.create(defaultSysConfig);
this.frameDefs = {};
diff --git a/js/flightlog_setup_dialog.js b/js/flightlog_setup_dialog.js
new file mode 100644
index 00000000..62293d0f
--- /dev/null
+++ b/js/flightlog_setup_dialog.js
@@ -0,0 +1,101 @@
+"use strict";
+
+function FlightLogSetupDialog(dialog, onSave) {
+
+ function renderParameter(parameter) {
+ var
+ elem = $(
+ '
'
+ + ''
+ + ''
+ + ''
+ );
+
+ $("input", elem).val(parameter.value);
+
+ return elem;
+ }
+
+ function renderParameters(setting) {
+ var
+ parametersElem = $(
+ '
'
+ + ''
+ + '' + setting.label + '
'
+ + '- '
+ + ''
+ + '
'
+ + '
'
+ + ''
+ ),
+ parameterList = $(".setup-parameter-list", parametersElem);
+
+ for (var i = 0; i < setting.parameters.length; i++) {
+ var
+ parameter = setting.parameters[i],
+ parameterElem = renderParameter(parameter);
+
+ parameterList.append(parameterElem);
+ }
+
+ return parametersElem;
+ }
+
+ function renderSettings(flightLog) {
+ var
+ settingsList = $(".setup-flightlog-list", dialog);
+
+ settingsList.empty();
+
+ for (var i = 0; i < flightLog.settings.length; i++) {
+ settingsList.append(renderParameters(flightLog.settings[i]));
+ }
+ }
+
+ function convertUIToFlightLogSettings() {
+ var
+ settings = [],
+ setting,
+ parameter;
+
+ $(".setup-parameters", dialog).each(function() {
+ setting = {
+ label: '',
+ parameters: []
+ };
+
+ setting.label = $("h4", this).text();
+
+ $(".setup-parameter", this).each(function() {
+ parameter = {
+ label: $("label", this).text(),
+ value: parseInt($("input[type='text']", this).val()),
+ };
+ setting.parameters.push(parameter);
+ });
+
+ settings.push(setting);
+ });
+
+ return settings;
+ }
+
+ this.show = function(flightLog, settings) {
+ dialog.modal('show');
+
+ // Assign the settings
+ flightLog.settings = settings;
+
+ renderSettings(flightLog);
+ };
+
+ $(".flightlog-setup-dialog-save").click(function(e) {
+ onSave(convertUIToFlightLogSettings());
+ });
+
+
+}
\ No newline at end of file
diff --git a/js/graph_config.js b/js/graph_config.js
old mode 100644
new mode 100755
index 05f2ee4a..03145e5d
--- a/js/graph_config.js
+++ b/js/graph_config.js
@@ -12,6 +12,10 @@ function GraphConfig(graphConfig) {
}
}
+ this.selectedFieldName = null;
+ this.selectedGraphIndex = 0;
+ this.selectedFieldIndex = 0;
+
this.getGraphs = function() {
return graphs;
};
@@ -34,7 +38,7 @@ function GraphConfig(graphConfig) {
// Make copies of graphs into here so we can modify them without wrecking caller's copy
newGraphs = [];
-
+
for (var i = 0; i < graphs.length; i++) {
var
graph = graphs[i],
@@ -161,10 +165,18 @@ GraphConfig.load = function(config) {
label: "Gyros",
fields: ["gyroADC[all]"]
},
+ { /* Add custom graph configurations to the main menu ! */
+ label: "RC Command",
+ fields: ["rcCommand[all]"]
+ },
{
label: "PIDs",
fields: ["axisSum[all]"]
},
+ {
+ label: "PID Error",
+ fields: ["axisError[all]"]
+ },
{
label: "Gyro + PID roll",
fields: ["axisP[0]", "axisI[0]", "axisD[0]", "gyroADC[0]"]
@@ -188,7 +200,7 @@ GraphConfig.load = function(config) {
return 5000;
} else if (fieldName.match(/^servo\[/)) {
return 5000;
- } else if (fieldName.match(/^gyroADC\[/)) {
+ } else if (fieldName.match(/^gyroADC.*\[/)) {
return 3000;
} else if (fieldName.match(/^accSmooth\[/)) {
return 3000;
@@ -220,7 +232,7 @@ GraphConfig.load = function(config) {
} else if (fieldName.match(/^gyroADC\[/)) {
return {
offset: 0,
- power: 0.25,
+ power: 0.25, /* Make this 1.0 to scale linearly */
inputRange: 2.0e-5 / sysConfig.gyroScale,
outputRange: 1.0
};
@@ -231,6 +243,15 @@ GraphConfig.load = function(config) {
inputRange: sysConfig.acc_1G * 3.0, /* Reasonable typical maximum for acc */
outputRange: 1.0
};
+ } else if (fieldName.match(/^axisError\[/) || // Custom Gyro, rcCommand and axisError Scaling
+ fieldName.match(/^rcCommands\[/) || // These use the same scaling as they are in the
+ fieldName.match(/^gyroADCs\[/) ) { // same range.
+ return {
+ offset: 0,
+ power: 0.25, /* Make this 1.0 to scale linearly */
+ inputRange: flightLog.gyroRawToDegreesPerSecond(2.0e-5 / sysConfig.gyroScale),
+ outputRange: 1.0
+ };
} else if (fieldName.match(/^axis.+\[/)) {
return {
offset: 0,
@@ -258,7 +279,7 @@ GraphConfig.load = function(config) {
power: 0.8,
inputRange: 500 * (sysConfig.rcRate ? sysConfig.rcRate : 100) / 100,
outputRange: 1.0
- };
+ };
} else if (fieldName == "heading[2]") {
return {
offset: -Math.PI,
diff --git a/js/graph_legend.js b/js/graph_legend.js
index d4b9aa95..79b0a7d5 100644
--- a/js/graph_legend.js
+++ b/js/graph_legend.js
@@ -1,9 +1,9 @@
"use strict";
-function GraphLegend(targetElem, config, onVisibilityChange) {
+function GraphLegend(targetElem, config, onVisibilityChange, onNewSelectionChange) {
var
that = this;
-
+
function buildLegend() {
var
graphs = config.getGraphs(),
@@ -23,7 +23,7 @@ function GraphLegend(targetElem, config, onVisibilityChange) {
for (j = 0; j < graph.fields.length; j++) {
var
field = graph.fields[j],
- li = $('
');
+ li = $('
');
li.text(FlightLogFieldPresenter.fieldNameToFriendly(field.name));
li.css('border-bottom', "2px solid " + field.color);
@@ -34,6 +34,26 @@ function GraphLegend(targetElem, config, onVisibilityChange) {
targetElem.append(graphDiv);
}
+ // Add a trigger on legend; select the analyser graph/field to plot
+ $('.graph-legend-field').on('click', function() {
+ config.selectedFieldName = this.innerText;
+ config.selectedGraphIndex = $(this).attr('graph');
+ config.selectedFieldIndex = $(this).attr('field');
+ $('.hide-analyser-window').show();
+ if (onNewSelectionChange) {
+ onNewSelectionChange();
+ }
+ });
+
+ // Add a button to remove the analyser display
+ $('.hide-analyser-window').on('click', function() {
+ config.selectedFieldName = null;
+ $(this).hide();
+ if (onNewSelectionChange) {
+ onNewSelectionChange();
+ }
+ });
+
$('.log-close-legend-dialog').on('click', function() {
that.hide();
});
@@ -41,6 +61,9 @@ function GraphLegend(targetElem, config, onVisibilityChange) {
$('.log-open-legend-dialog').on('click', function() {
that.show();
});
+
+ // on first show, hide the analyser button
+ if(!config.selectedFieldName) $('.hide-analyser-window').hide();
}
this.show = function() {
diff --git a/js/graph_spectrum.js b/js/graph_spectrum.js
new file mode 100644
index 00000000..daad18e2
--- /dev/null
+++ b/js/graph_spectrum.js
@@ -0,0 +1,168 @@
+"use strict";
+
+function FlightLogAnalyser(flightLog, graphConfig, canvas, craftCanvas, options) {
+
+var canvasCtx = canvas.getContext("2d");
+
+var // inefficient; copied from grapher.js
+
+ DEFAULT_FONT_FACE = "Verdana, Arial, sans-serif",
+
+ drawingParams = {
+ fontSizeFrameLabel: null
+ };
+
+
+var frameCount = 4096;
+
+var AudioContext = window.AudioContext || window.webkitAudioContext;
+var audioCtx = new AudioContext();
+
+var audioBuffer = audioCtx.createBuffer(1, frameCount, audioCtx.sampleRate);
+var source = audioCtx.createBufferSource();
+ source.buffer = audioBuffer;
+ source.loop = true;
+ source.start();
+
+var spectrumAnalyser = audioCtx.createAnalyser();
+ spectrumAnalyser.fftSize = 256;
+ spectrumAnalyser.smoothingTimeConstant = 0.8;
+ spectrumAnalyser.minDecibels = -120;
+ spectrumAnalyser.maxDecibels = -20;
+
+var bufferChunks, bufferStartFrameIndex, bufferFieldIndex, bufferCurve, bufferWindowEndTime;
+var initialised = false;
+var analyserFieldName; // Name of the field being analysed
+
+// Setup the audio path
+source.connect(spectrumAnalyser);
+
+var audioIterations = 0; // variable to monitor spectrum processing
+
+function dataLoad(chunks, startFrameIndex, fieldIndex, curve, buffer, windowEndTime) {
+
+ var chunkIndex, frameIndex;
+ var i = 0;
+
+ var bufferData = buffer.getChannelData(0); // link to the first channel
+
+ //We may start partway through the first chunk:
+ frameIndex = startFrameIndex;
+
+ dataCollectionLoop:
+ for (chunkIndex = 0; chunkIndex < chunks.length; chunkIndex++) {
+ var chunk = chunks[chunkIndex];
+ for (; frameIndex < chunk.frames.length; frameIndex++) {
+ var fieldValue = chunk.frames[frameIndex][fieldIndex];
+ var frameTime = chunk.frames[frameIndex][FlightLogParser.prototype.FLIGHT_LOG_FIELD_INDEX_TIME]
+ bufferData[i++] = (curve.lookupRaw(fieldValue));
+
+ if (i >= buffer.length || frameTime >= windowEndTime) {
+ // console.log("Samples : " + i);
+ break dataCollectionLoop;
+ }
+ }
+ frameIndex = 0;
+ }
+ audioIterations++;
+}
+
+/* Function to actually draw the spectrum analyser overlay
+ again, need to look at optimisation....
+
+ */
+
+function draw() {
+
+ canvasCtx.save();
+
+ var bufferLength = spectrumAnalyser.frequencyBinCount;
+ var dataArray = new Uint8Array(bufferLength);
+
+ var HEIGHT = canvasCtx.canvas.height * 0.4; /* trial and error values to put box in right place */
+ var WIDTH = canvasCtx.canvas.width * 0.4;
+ var LEFT = canvasCtx.canvas.width * 0.05;
+ var TOP = canvasCtx.canvas.height * 0.55;
+
+ /* only plot the lower half of the FFT, as the top half
+ never seems to have any values in it - too high frequency perhaps. */
+ var PLOTTED_BUFFER_LENGTH = bufferLength / 2;
+
+ canvasCtx.translate(LEFT, TOP);
+
+ spectrumAnalyser.getByteFrequencyData(dataArray);
+
+ canvasCtx.fillStyle = 'rgba(255, 255, 255, .25)'; /* white */
+ canvasCtx.fillRect(0, 0, WIDTH, HEIGHT);
+
+ var barWidth = (WIDTH / PLOTTED_BUFFER_LENGTH) - 1;// * 2.5;
+ var barHeight;
+ var x = 0;
+
+ for(var i = 0; i < (PLOTTED_BUFFER_LENGTH); i++) {
+ barHeight = (dataArray[i]/255 * HEIGHT);
+
+ canvasCtx.fillStyle = 'rgba(0,255,0,0.3)'; /* green */
+ canvasCtx.fillRect(x,HEIGHT-barHeight,barWidth,barHeight);
+
+ x += barWidth + 1;
+ }
+ drawGridLines(options.analyserSampleRate, LEFT, TOP, WIDTH, HEIGHT);
+ drawAxisLabel(analyserFieldName, WIDTH - 8, HEIGHT - 10, 'right');
+ canvasCtx.restore();
+ }
+
+function drawGridLines(sampleRate, LEFT, TOP, WIDTH, HEIGHT) {
+
+ var ticks = 5;
+ var frequencyInterval = (sampleRate / ticks) / 4;
+ var frequency = 0;
+
+ for(var i=0; i<=ticks; i++) {
+ canvasCtx.beginPath();
+ canvasCtx.lineWidth = 1;
+ canvasCtx.strokeStyle = "rgba(255,255,255,0.25)";
+
+ canvasCtx.moveTo(i * (WIDTH / ticks), 0);
+ canvasCtx.lineTo(i * (WIDTH / ticks), HEIGHT);
+
+ canvasCtx.stroke();
+ drawAxisLabel((frequency)+"Hz", i * (WIDTH / ticks), HEIGHT * 1.05, 'center');
+ frequency += frequencyInterval;
+ }
+}
+
+function drawAxisLabel(axisLabel, X, Y, align) {
+ canvasCtx.font = drawingParams.fontSizeFrameLabel + "pt " + DEFAULT_FONT_FACE;
+ canvasCtx.fillStyle = "rgba(255,255,255,0.9)";
+ if(align) {
+ canvasCtx.textAlign = align;
+ } else
+ {
+ canvasCtx.textAlign = 'center';
+ }
+
+
+ canvasCtx.fillText(axisLabel, X, Y);
+ }
+
+/* This function is called from the canvas drawing routines within grapher.js
+ It is only used to record the current curve positions, collect the data and draw the
+ analyser on screen*/
+
+this.plotSpectrum = function (chunks, startFrameIndex, fieldIndex, curve, fieldName, windowEndTime) {
+ // Store the data pointers
+ bufferChunks = chunks;
+ bufferStartFrameIndex = startFrameIndex;
+ bufferFieldIndex = fieldIndex;
+ bufferCurve = curve;
+ bufferWindowEndTime = windowEndTime;
+
+ analyserFieldName = fieldName;
+
+ if (audioBuffer) {
+ dataLoad(bufferChunks, bufferStartFrameIndex, bufferFieldIndex, bufferCurve, audioBuffer, bufferWindowEndTime);
+ }
+ draw(); // draw the analyser on the canvas....
+ }
+}
\ No newline at end of file
diff --git a/js/grapher.js b/js/grapher.js
old mode 100644
new mode 100755
index c99a885b..11a76308
--- a/js/grapher.js
+++ b/js/grapher.js
@@ -42,6 +42,40 @@ function FlightLogGrapher(flightLog, graphConfig, canvas, craftCanvas, options)
"#ccebc5",
"#ffed6f"
],
+
+ EventStates = [
+ 'ARM',
+ 'ANGLE',
+ 'HORIZON',
+ 'BARO',
+ 'MAG',
+ 'HEADFREE',
+ 'HEADADJ',
+ 'CAMSTAB',
+ 'CAMTRIG',
+ 'GPSHOME',
+ 'GPSHOLD',
+ 'PASSTHRU',
+ 'BEEPER',
+ 'LEDMAX',
+ 'LEDLOW',
+ 'LLIGHTS',
+ 'CALIB',
+ 'GOV',
+ 'OSD',
+ 'TELEMETRY',
+ 'GTUNE',
+ 'SONAR',
+ 'SERVO1',
+ 'SERVO2',
+ 'SERVO3',
+ 'BLACKBOX',
+ 'FAILSAFE',
+ 'AIRMODE',
+ 'SUPEREXPO',
+ '3DDISABLESWITCH',
+ 'CHECKBOX_ITEM_COUNT'
+ ],
WINDOW_WIDTH_MICROS_DEFAULT = 1000000;
@@ -53,6 +87,8 @@ function FlightLogGrapher(flightLog, graphConfig, canvas, craftCanvas, options)
defaultOptions = {
gapless:false,
drawCraft:"3D", drawPidTable:true, drawSticks:true, drawTime:true,
+ drawAnalyser:true, // add an analyser option
+ analyserSampleRate:2000/*Hz*/, // the loop time for the log
eraseBackground: true // Set to false if you want the graph to draw on top of an existing canvas image
},
@@ -71,6 +107,8 @@ function FlightLogGrapher(flightLog, graphConfig, canvas, craftCanvas, options)
craft3D = null, craft2D = null,
+ analyser = null, /* define a new spectrum analyser */
+
that = this;
this.onSeek = null;
@@ -485,6 +523,19 @@ function FlightLogGrapher(flightLog, graphConfig, canvas, craftCanvas, options)
function drawAxisLine() {
canvasContext.strokeStyle = "rgba(255,255,255,0.5)";
canvasContext.lineWidth = 1;
+ canvasContext.setLineDash([5]); // Make the center line a dash
+ canvasContext.beginPath();
+ canvasContext.moveTo(0, 0);
+ canvasContext.lineTo(canvas.width, 0);
+
+ canvasContext.stroke();
+ canvasContext.setLineDash([]);
+ }
+
+ //Draw an background for the line for a graph (at the origin and spanning the window)
+ function drawAxisBackground(plotHeight) {
+ canvasContext.strokeStyle = "rgba(255,255,255,0.1)";
+ canvasContext.lineWidth = plotHeight;
canvasContext.beginPath();
canvasContext.moveTo(0, 0);
@@ -540,6 +591,19 @@ function FlightLogGrapher(flightLog, graphConfig, canvas, craftCanvas, options)
function timeToCanvasX(time) {
return canvas.width / windowWidthMicros * (time - windowStartTime);
}
+
+ function eventToFriendly(flags, lastFlags) {
+ var eventState = '';
+ var found = false;
+ for(var i=0; i<=31; i++) {
+ if((1<
1) // only draw the background if more than one graph set.
+ drawAxisBackground(canvas.height * graph.height);
for (j = 0; j < graph.fields.length; j++) {
var field = graph.fields[j];
@@ -795,6 +864,15 @@ function FlightLogGrapher(flightLog, graphConfig, canvas, craftCanvas, options)
canvasContext.restore();
}
}
+
+ // Draw Analyser
+ if (options.drawAnalyser && graphConfig.selectedFieldName) {
+ try{ // If we do not select a graph/field, then the analyser is hidden
+ var graph = graphs[graphConfig.selectedGraphIndex];
+ var field = graph.fields[graphConfig.selectedFieldIndex];
+ analyser.plotSpectrum(chunks, startFrameIndex, field.index, field.curve, graphConfig.selectedFieldName, windowEndTime);
+ } catch(err) {console.log('Cannot plot analyser');}
+ }
}
drawInOutRegion();
@@ -909,6 +987,9 @@ function FlightLogGrapher(flightLog, graphConfig, canvas, craftCanvas, options)
identifyFields();
initializeCraftModel();
+
+ /* Create the FlightLogAnalyser object */
+ analyser = new FlightLogAnalyser(flightLog, graphConfig, canvas, craftCanvas, options);
//Handle dragging events
$(canvas).on("mousedown", onMouseDown);
diff --git a/js/main.js b/js/main.js
index ac924acd..00c3bcab 100644
--- a/js/main.js
+++ b/js/main.js
@@ -38,6 +38,55 @@ function BlackboxLogViewer() {
// JSON graph configuration:
graphConfig = {},
+ // JSON flightlog configuration
+ flightLogSettings = {},
+
+ flightLogDefaultSettings = [ // FlightLog Default Settings
+ { label: "Rates",
+ parameters:
+ [
+ { // Index 0
+ label: "RC Rate",
+ value: 100
+ },
+ { // Index 1
+ label: "RC Expo",
+ value: 70
+ },
+ { // Index 2
+ label: "Roll Rate",
+ value: 75
+ },
+ { // Index 3
+ label: "Pitch Rate",
+ value: 75
+ },
+ { // Index 4
+ label: "Yaw Rate",
+ value: 45
+ },
+ { // Index 5
+ label: "Yaw Expo",
+ value: 20
+ },
+ { // Index 6
+ label: "Super Expo",
+ value: 30
+ }
+ ]
+ },
+ { label: "Loop Time",
+ parameters:
+ [
+ { // Index 0
+ label: "Looptime",
+ value: 500
+ },
+ ]
+ },
+ ],
+
+
// Graph configuration which is currently in use, customised based on the current flight log from graphConfig
activeGraphConfig = new GraphConfig(),
@@ -423,11 +472,31 @@ function BlackboxLogViewer() {
return;
}
+ try {
+ // transfer the parameters from the log file into the settings data structure
+ if(flightLog.getSysConfig().rcRate != null) {flightLogSettings[0].parameters[0].value = flightLog.getSysConfig().rcRate; }
+ if(flightLog.getSysConfig().rcExpo != null) {flightLogSettings[0].parameters[1].value = flightLog.getSysConfig().rcExpo; }
+ if(flightLog.getSysConfig().rRate != null) {flightLogSettings[0].parameters[2].value = flightLog.getSysConfig().rRate; }
+ if(flightLog.getSysConfig().pRate != null) {flightLogSettings[0].parameters[3].value = flightLog.getSysConfig().pRate; }
+ if(flightLog.getSysConfig().yRate != null) {flightLogSettings[0].parameters[4].value = flightLog.getSysConfig().yRate; }
+ if(flightLog.getSysConfig().rcYawExpo != null) {flightLogSettings[0].parameters[5].value = flightLog.getSysConfig().rcYawExpo; }
+ if(flightLog.getSysConfig().superExpoFactor != null) {flightLogSettings[0].parameters[6].value = flightLog.getSysConfig().superExpoFactor; }
+ if(flightLog.getSysConfig().loopTime != null) {flightLogSettings[1].parameters[0].value = flightLog.getSysConfig().loopTime; }
+ } catch(e) {
+ console.log('FlightLog Settings archive fault... ignoring');
+ }
if (graph) {
graph.destroy();
}
- graph = new FlightLogGrapher(flightLog, activeGraphConfig, canvas, craftCanvas);
+ var graphOptions = {
+ drawAnalyser:true, // add an analyser option
+ analyserSampleRate:2000/*Hz*/, // the loop time for the log
+ };
+
+ if(flightLog.getSysConfig().loopTime != null) {graphOptions.analyserSampleRate = 1000000 / flightLog.getSysConfig().loopTime; }
+
+ graph = new FlightLogGrapher(flightLog, activeGraphConfig, canvas, craftCanvas, graphOptions);
setVideoInTime(false);
setVideoOutTime(false);
@@ -470,7 +539,7 @@ function BlackboxLogViewer() {
flightLogDataArray = new Uint8Array(bytes);
try {
- flightLog = new FlightLog(flightLogDataArray);
+ flightLog = new FlightLog(flightLogDataArray, flightLogSettings);
} catch (err) {
alert("Sorry, an error occured while trying to open this log:\n\n" + err);
return;
@@ -522,6 +591,10 @@ function BlackboxLogViewer() {
prefs.set('log-legend-hidden', hidden);
updateCanvasSize();
}
+
+ function onLegendSelectionChange() {
+ updateCanvasSize();
+ }
prefs.get('videoConfig', function(item) {
if (item) {
@@ -544,12 +617,21 @@ function BlackboxLogViewer() {
}
});
+ prefs.get('flightLogSettings', function(item) {
+ if(item) {
+ flightLogSettings = item;
+ } else {
+ flightLogSettings = flightLogDefaultSettings;
+ }
+ });
+
+
activeGraphConfig.addListener(function() {
invalidateGraph();
});
$(document).ready(function() {
- graphLegend = new GraphLegend($(".log-graph-legend"), activeGraphConfig, onLegendVisbilityChange);
+ graphLegend = new GraphLegend($(".log-graph-legend"), activeGraphConfig, onLegendVisbilityChange, onLegendSelectionChange);
prefs.get('log-legend-hidden', function(item) {
if (item) {
@@ -662,6 +744,13 @@ function BlackboxLogViewer() {
prefs.set('graphConfig', graphConfig);
}),
+ flightLogSetupDialog = new FlightLogSetupDialog($("#dlgFlightLogSetup"), function(newSettings) {
+ flightLog.settings = newSettings; // Store the settings to the flightlog
+
+ flightLogSettings = newSettings; // Let's write this information to the local store
+ prefs.set('flightLogSettings', flightLogSettings);
+ }),
+
exportDialog = new VideoExportDialog($("#dlgVideoExport"), function(newConfig) {
videoConfig = newConfig;
@@ -675,6 +764,12 @@ function BlackboxLogViewer() {
graphConfigDialog.show(flightLog, graphConfig);
});
+ $(".open-log-setup-dialog").click(function(e) {
+ e.preventDefault();
+
+ flightLogSetupDialog.show(flightLog, flightLogSettings);
+ });
+
if (FlightLogVideoRenderer.isSupported()) {
$(".btn-video-export").click(function(e) {
setGraphState(GRAPH_STATE_PAUSED);