/*
This script is designed to upload static thrust data
to the RCbenchmark database. The script performs
throttle steps as shown below:
First, the script does a quick pre-qualification ramp
up to specified throttle limit, to automatically
determine the min/max throttle. The min throttle is
where the motor starts to spin while the max throttle
is where the maximum rpm was reached. This helps avoid
duplicate data points at the same RPM.
Then, the script does steps and stops at each step
to allow the system to stabilize.
The '.' represents a sample is recorded. A sample is
first recorded at the beginning, after taring the load
cells and the ESC initialization.
^ Motor _throttleLimit
| input /|__. _throttle of
| / \__. max RPM
| / \__.
| / \__.
| / _motor starts \__.
| _escStart __./
|_______________________________________> time
At the start of each step there is a time to
stabilize the rotation speed, after which a data
sample is taken.
This test can take a while to run, therefore there
is an option to allow the motor to cool down
between each step.
*/
////////// Script parameters ///////// */
var escStart = 1000; // ESC idle value
var escInitTime = 4; // Hold time at ESC idle
var throttleLimit = 2000; // Max. input value
// step parameters
var params = {
steps_qty: 20, // Number of steps
settlingTime_s: 2, // Settling time before measurement
cooldownTime_s: 0, // If the motor needs to cool down between steps
cooldownThrottle_us: 1200, // Cool down faster when slowly spinning
max_slew_rate_us_per_s: 100 // Limits torque from throttle changes, but adds extra test time
};
var save_CSV_file = true; // Save data to CSV file
var samplesAvg = 100; // Number of samples to average
//////// Beginning of the script /////////
if(rcb.getBoardVersion() === undefined){
rcb.console.error("Error: please connect a supported tool before running this script.");
}
var output = "esc";
if(rcb.getBoardVersion() === "Series 1780"){
output = "escA";
}
if(escStart >= throttleLimit || params.cooldownThrottle_us >= throttleLimit){
rcb.console.error("Error: invalid throttle parameters, ensure throttleLimit is the maximum.");
}
var firstRowAdded = false;
var rampingDown = false;
var rampThrottle = escStart;
var minThrottle;
var maxThrottle;
var maxRPM;
//Starting a new CSV log file
if(save_CSV_file){
rcb.files.newLogFile({prefix: 'DYS Samguk Wei HQ 5040'});
}
//Tare the load cells and the current sensor
function tare(callback){
rcb.sensors.tareLoadCells(function(){
//taring current has an effect only in the Series 1780
rcb.sensors.tareCurrent(callback);
});
}
initESC(function(){
tare(function(){
takeSample(previewTest);
});
});
//Arms the ESC
function initESC(callback){
//ESC initialization
rcb.console.print("Initializing ESC...");
rcb.database.log("ESC init value (µs): " + escStart);
rcb.database.log("Throttle max limit (µs): " + throttleLimit);
rcb.output.pwm(output, escStart);
rcb.wait(callback, escInitTime);
}
//Calculates the rpm value depending on if using the optical or electrical probe
function getRPM(result){
var rpm = result.motorOpticalSpeed.workingValue;
if(result.motorElectricalSpeed){
rpm = math.max(result.motorElectricalSpeed.workingValue, rpm);
}
return rpm;
}
//Starts the pre-scan ramp up, to determine the min and max throttle
function previewTest(){
var slewRate = params.max_slew_rate_us_per_s;
var lastTime = window.performance.now();
var rampDone = false;
rcb.console.setVerbose(false);
// Add a delay at the top of the ramp to ensure the maximum throttle is sampled
function finishRamp(){
rcb.wait(function(){
rampDone = true;
}, params.settlingTime_s);
}
function readSensor(){
rcb.sensors.read(readDone, 1);
}
function readDone(result){
var currTime = window.performance.now();
var rampTime;
// find out at which throttle the motor starts to spin
if(!minThrottle){
if(getRPM(result)>0){
minThrottle = rampThrottle;
rcb.database.log("Motor starts at " + minThrottle + " (automatically detected by the script)");
minThrottle += 10; // helps avoid motor stuttering at start.
statusText = rcb.console.print("Ramping up to find throttle of max RPM...");
throttleText = rcb.console.print("");
var rampRate = 100; // the minimum rate at which we want to increase the throttle for this code to work well
if(slewRate > 0){
rampRate = math.min(slewRate, rampRate); // respect the user's own slew rate limit
}
rampTime = (throttleLimit - minThrottle) / rampRate;
if(minThrottle >= throttleLimit-10){
rcb.console.error("Error: the throttle limit is too close to the motor starting value");
}
rcb.output.ramp(output, minThrottle, throttleLimit, rampTime, finishRamp);
readSensor();
}else{
rampThrottle += 1;
if(rampThrottle >= throttleLimit){
rcb.console.error("No motor rotation detected. Check your setup and ensure the RPM sensor is working.");
}
rcb.output.pwm(output, rampThrottle);
rcb.console.overwrite(rampThrottle, throttleText);
rcb.wait(readSensor, 0.02);
}
}else{
// As long as RPM is increasing, we save the new maxThrottle value
var throttle = result[output].workingValue;
rcb.console.overwrite(throttle, throttleText);
if(!rampDone && (!maxRPM || getRPM(result) > maxRPM + 2)){ //some tolerance to RPM noise
maxThrottle = throttle;
maxRPM = getRPM(result);
}
if(rampDone){
rcb.database.log("RPM maxed at " + maxThrottle + " at " + maxRPM + " RPM (autodetect)");
rcb.console.print("Sampling at max throttle...");
throttleText = rcb.console.print("");
rcb.output.pwm(output, maxThrottle);
rcb.console.setVerbose(true);
steps();
}else{
readSensor();
}
}
}
var statusText = rcb.console.print("Ramping up to find motor start value...");
var throttleText = rcb.console.print("");
readSensor();
}
// Records a sample to database
function takeSample(callback){
rcb.sensors.read(function (result){
var rpm = getRPM(result);
if(result.thrust.workingValue < -0.05){
rcb.console.error("At this time we only support positive thrust values. Please reverse the thrust sign from the utilities tab.");
}
if(result.torque && result.torque.workingValue < -0.01){
rcb.console.error("At this time we only support positive torque values. Please reverse the torque sign from the utilities tab.");
}
if(result.current.workingValue < -0.1){
rcb.console.error("At this time we only support positive current values.");
}
if(!firstRowAdded && rpm > 0){
rcb.console.error("The first row of the data should be at zero rpm.");
}
// do not add more than one rpm=0 rows
if(!firstRowAdded || rpm > 0){
rcb.database.addData(result);
firstRowAdded = true;
}
if(save_CSV_file){
rcb.files.newLogEntry(result, callback);
}else{
callback();
}
}, samplesAvg);
}
// Start the steps function, going downwards
function steps(){
var minStep = (minThrottle + (maxThrottle - minThrottle) * 0.1); // At very low throttle, the signal-to-noise ratio is too high to be meaningful
rcb.output.steps2(output, maxThrottle, minStep, stepFct, endFct, params);
}
// The following function will be executed at each step.
function stepFct(nextStepFct){
takeSample(nextStepFct);
}
// Called at the end of the steps
function endFct(){
// stop the motor
rcb.output.pwm(output,escStart);
rcb.wait(function(){
rcb.database.submit(rcb.endScript);
}, 1);
}