Hi, just registered because have a problem with my hobby flight-sim game project (written in JavaScript). I'm not sure if the equations in update() method are correct or more important - their order (which one should be first, second, etc) and if I messed something in using metric system (unit conversion). My doubts are caused by that the bot is somehow not capable of going through test map's checkpoints, it crashes at second checkpoint (for now I'm drawing in canvas with 512x512 px dimensions)
var PI = 3.141592;
var TAU = 2 * PI;
var sim = {
state: 1,
startTime: 0, playedTime: 0, tempPlayedTime: 0, currentTime: 0, lastTime: 0, dTime: 0, timeProduct: 0, updateTime: 0, worldUpdates: 0,
distPerFrame: 0,
timeScale: 1, /*< 1 speeds up, > 1 slows down the game, must be fixed*/
timeStep: 0.01667, //in s
timeDiff: 0, temp: 0,
referenceGravity: 9.80665, //reference gravity in m/sq. s at sea level
currentGravity: this.referenceGravity, //in m/sq. s
earthRadius: 6371000, //in m
earthPowRadius: 40589641000000, //in m
geopotentialHeight: 0, //in m
distance: 0, x: 0, y: 0,
racetrackLength: 0, //in m
airReferenceTemperature: 15, //reference air temp in Celsius at sea level, or 288.15 K
airReferenceViscosity: 0.01827, //0.00001789 reference air viscosity in centipoise at reference temperature in Celsius, or 0.01827 at reference temperature in Rankine
airReferencePressure: 101325, //reference air pressure in Pa at sea level and 15'C air temperature
airReferenceDensity: 1.225, //reference air density at sea level in kg/cub.m, or 0.0764734 lb/cub. ft
RankineRefTemp: 518.76, //reference air temp in Rankine degree == 15'C
SutherlandConstant: 120, //for air
gasConstant: 8.3144598, //reference gas constant at sea level, in J/mol Kelvin, or 1545.35 ft lb/lbmol Rankine
airMolarMass: 0.0289644, // in kg/mol
airLapseRate: 0.0065, //0.0098 reference air lapse rate in Celsius per meter altitude, 0.0065 reference air lapse rate in Kelvin per meter, 0.0019812 in Kelvin per foot
airViscosity: this.airReferenceViscosity,
airPressure: this.airReferencePressure,
airDensity: this.airReferenceDensity,
kinematicViscosity: 0,
ReynoldsNumber: 0,
airCurrentTemp: this.airReferenceTemperature,
counter: 0,
showFPSMem: true, currentFPS: 0, currentMS: 0, currentMem: 0, showTraj: false, showUnitStats: true,
botIdCounter: 0, bots: [], runningBots: 0,
targetSize: 10
};
var bot = function() {
this.position = {x: 0, y: 0, z: 0}; //z is the ALTITUDE!!!
this.destination = {x: 0, y: 0, z: 0}; //z is the ALTITUDE!!!
this.distance = 0; //in m
this.distanceTravelled = 0; //in m
this.altitude = this.position.z; //in m
this.pitch = 0; //angle of attack ? in degrees?
this.roll = 0; //in degrees?
this.yaw = 0; //in degrees?
this.heading = 0; //in degrees, 0 - East
this.dragCoeff = dragpoints * 0.0001;
this.thrust = thrust; //in kgf
this.fuel = fuel; //in liters
this.mass = mass; //in kg
this.loadedMass = this.fuel + this.mass; //in kg
this.height = height; //in m
this.acceleration = this.thrust / this.loadedMass; //acceleration in m/s
this.oldAcceleration = 0;
this.velocity = {x: 0, y: 0, z: 0}; //maybe needed for proper integration
this.speed = 0; //current speed in m/s
this.wingSpan = wingspan; //in m
this.wingArea = wingarea; //in sq.m
this.wingLoading = this.loadedMass / this.wingArea; //in kg/sq.m
this.wingType = wingtype; //1 - flat bottom, 2 - semi-symmetric, 3 - symmetric
this.frontalArea = this.wingSpan * this.height; //in sq.m
this.liftCoeff = 0;
this.liftForce = 0; //in kN
this.fuelConsumption = (this.thrust / 100) * 0.035; //0.035 litres per second
this.trajectoryLine = traj;
this.rotationSpeedX = TAU / 100; //100 FPS
this.rotationSpeedY = TAU / 100;
this.rotationSpeedZ = TAU / 100;
this.size = this.wingArea / 10; //size of the bot on the canvas
this.id = sim.botIdCounter;
this.target = 0;
this.hasTarget = false;
this.time = 0;
this.startTime = 0;
this.isTimed = false;
this.state = true;
this.distanceBeforeFirstTarget = 0;
this.isLanding = false;
sim.botIdCounter++;
sim.runningBots++;
this.update = function() {
if (this.fuel >= this.fuelConsumption * sim.timeStep) {
this.fuel -= this.fuelConsumption * sim.timeStep;
this.loadedMass -= this.fuelConsumption * sim.timeStep;
this.acceleration = (this.thrust*sim.timeStep) / this.loadedMass;
} else {
if (this.target <= targetCount) {
this.fuel = 0; //directly set fuel to 0 when it is < 0 or has some very low values ~0.050l
if (this.acceleration > 0) this.acceleration -= (this.thrust*sim.timeStep) / this.loadedMass;
if (this.acceleration < 0) this.acceleration = 0;
} else {
this.fuel = 0;
this.isTimed = false; //stop the stopwatch for our bot
this.state = false; //set state of our bot to inactive
sim.runningBots--; //remove our bot from the list with active bots
return; //maybe it's better to early exit from the function, execution of the conditions below is useless
}
}
//after fuel check we can calculate all characteristics of our bot
this.wingLoading = this.loadedMass / this.wingArea;
this.liftCoeff = ((this.loadedMass * sim.currentGravity) / ((sim.airDensity / 2) * pow(this.acceleration, 2) * this.wingArea)) * sim.timeStep;
this.liftCoeff = isNaN(this.liftCoeff) ? 0 : this.liftCoeff;
this.liftForce = (this.liftCoeff * (0.5*(sim.airDensity * pow(this.acceleration, 2))) * this.wingArea) * sim.timeStep;
this.liftForce = isNaN(this.liftForce) ? 0 : this.liftForce;
//set destination coords for our bot only once for each destination
if (this.hasTarget === false && this.state === true) {
this.hasTarget = true;
this.destination.x = targets[this.target].x;
this.destination.y = targets[this.target].y;
this.destination.z = targets[this.target].z;
}
//calculate the difference between current position and destination for each axis
if (this.state === true) {
var diffX = this.destination.x - this.position.x;
var diffY = this.destination.y - this.position.y;
var diffZ = this.destination.z - this.position.z;
this.distance = hypot(diffX, diffY, diffZ);
//this normalizes the vector, so our calculations for direction and speed in Cartesian system are not skewed
if (this.distance > 0) {
diffX = diffX / this.distance;
diffY = diffY / this.distance;
diffZ = diffZ / this.distance;
}
if (this.distance >= this.size) {
//finds direction to the target in radians and convert it to degrees, y BEFORE x!!!
var targetAngleXY = atan2(diffY, diffX) * (180 / PI);
var targetAngleZ = atan(diffX/diffY) * (180 / PI);
//controls bot's altitude to be always bigger than its size, this is a crash prevention
if (this.altitude <= this.size) {
this.pitch += this.rotationSpeedZ + this.size;
}
//controls direction and turning speed of our bot, turning speed 0.1667 is one minute or 1/60 degree
if (this.heading > targetAngleXY) {
this.heading -= (this.rotationSpeedX + this.rotationSpeedY) + 1;
} else if (this.heading < targetAngleXY) {
this.heading += (this.rotationSpeedX + this.rotationSpeedY) + 1;
}
//this.heading = targetAngleXY;
if (this.pitch > targetAngleZ) {
this.pitch -= this.rotationSpeedZ + 1;
} else if (this.pitch < targetAngleZ) {
this.pitch += this.rotationSpeedZ + 1;
}
//calculate velocities for each axis
this.velocity.x += diffX * this.acceleration * sim.timeStep;
this.velocity.y += diffY * this.acceleration * sim.timeStep;
this.velocity.z += diffZ * this.acceleration * sim.timeStep;
//FIRST calculate Geopotential height
//this is gravity-adjusted altitude of our bot, using variation of the gravity with latitude and elevation
//based on https://en.wikipedia.org/wiki/Barometric_formula#Source_code
sim.geopotentialHeight = (sim.earthRadius * this.altitude / (sim.earthRadius + this.altitude)) / 1000; //in kilometers
//calculate current air temperature, in Kelvin
//based on https://en.wikipedia.org/wiki/Barometric_formula#Source_code
if (sim.geopotentialHeight <= 11) { sim.airCurrentTemp = 288.15 - (6.5 * sim.geopotentialHeight); } // Troposphere
else if (sim.geopotentialHeight <= 20) { sim.airCurrentTemp = 216.65 - (6.5 * sim.geopotentialHeight); } // Stratosphere starts
else if (sim.geopotentialHeight <= 32) { sim.airCurrentTemp = 196.65 + sim.geopotentialHeight; }
else if (sim.geopotentialHeight <= 47) { sim.airCurrentTemp = 228.65 + 2.8 * (sim.geopotentialHeight - 32); }
else if (sim.geopotentialHeight <= 51) { sim.airCurrentTemp = 270.65 - (6.5 * sim.geopotentialHeight); }// Mesosphere starts
else if (sim.geopotentialHeight <= 71) { sim.airCurrentTemp = 270.65 - 2.8 * (sim.geopotentialHeight - 51); }
else if (sim.geopotentialHeight <= 84.85) { sim.airCurrentTemp = 214.65 - 2 * (sim.geopotentialHeight - 71); }
//geopotHeight must be less than 84.85 km
var KelvinToRankine = sim.airCurrentTemp * 1.8; //in Rankine
//calculate current air density and pressure
//in Pascals
//based on https://en.wikipedia.org/wiki/Barometric_formula#Source_code
if (sim.geopotentialHeight <= 11) { sim.airPressure = 101325 * pow(288.15 / sim.airCurrentTemp, -5.255877); }
else if (sim.geopotentialHeight <= 20) { sim.airPressure = 22632.06 * exp(-0.1577 * (sim.geopotentialHeight - 11)); }
else if (sim.geopotentialHeight <= 32) { sim.airPressure = 5474.889 * pow(216.65 / sim.airCurrentTemp, 34.16319); }
else if (sim.geopotentialHeight <= 47) { sim.airPressure = 868.0187 * pow(228.65 / sim.airCurrentTemp, 12.2011); }
else if (sim.geopotentialHeight <= 51) { sim.airPressure = 110.9063 * exp(-0.1262 * (sim.geopotentialHeight - 47)); }
else if (sim.geopotentialHeight <= 71) { sim.airPressure = 66.93887 * pow(270.65 / sim.airCurrentTemp, -12.2011); }
else if (sim.geopotentialHeight <= 84.85) { sim.airPressure = 3.956420 * pow(214.65 / sim.airCurrentTemp, -17.0816); }
//altitude must be less than 86 km
sim.airDensity = (sim.airPressure * sim.airMolarMass) / (sim.gasConstant * sim.airCurrentTemp);
//calculate current air pressure at bot's position
sim.airPressureX = 0.5 * sim.airDensity * pow(this.velocity.x, 2);
sim.airPressureY = 0.5 * sim.airDensity * pow(this.velocity.y, 2);
sim.airPressureZ = 0.5 * sim.airDensity * pow(this.velocity.z, 2);
//calculate current air viscosity
sim.airViscosity = sim.airReferenceViscosity*(((0.555*sim.RankineRefTemp) + sim.SutherlandConstant)/((0.555*KelvinToRankine) + sim.SutherlandConstant))*pow(KelvinToRankine/sim.RankineRefTemp, 3.2); //in centipose
//calculate the Reynolds number so to use the correct drag law
sim.kinematicViscosity = sim.airViscosity / sim.airDensity;
sim.ReynoldsNumber = (this.acceleration * this.size) / sim.kinematicViscosity;
//choose which drag law to use
if (sim.ReynoldsNumber < 1) {
//for low velocity, linear drag or laminar flow
var dragX = 6 * PI * sim.airViscosity * this.size * this.velocity.x;
var dragY = 6 * PI * sim.airViscosity * this.size * this.velocity.y;
var dragZ = 6 * PI * sim.airViscosity * this.size * this.velocity.z;
} else {
//for high velocity, quadratic drag or turbulent flow
var dragX = sim.airPressureX * this.dragCoeff * this.frontalArea;
var dragY = sim.airPressureY * this.dragCoeff * this.frontalArea;
var dragZ = sim.airPressureZ * this.dragCoeff * this.frontalArea;
}
dragX = (isNaN(dragX) ? 0 : dragX*sim.timeStep);
dragY = (isNaN(dragY) ? 0 : dragY*sim.timeStep);
dragZ = (isNaN(dragZ) ? 0 : dragZ*sim.timeStep);
//calculate actual Earth's gravity
var distanceFromEarthCenter = sim.earthRadius + this.position.z;
sim.currentGravity = sim.referenceGravity * (sim.earthPowRadius / pow(distanceFromEarthCenter, 2));
sim.currentGravity = parseFloat(sim.currentGravity).toFixed(6);
//move our bot, maybe a Verlet version?
this.acceleration *= 0.5*sim.timeStep;
sim.currentGravity *= 0.5*sim.timeStep*sim.timeStep;
this.velocity.x += diffX * ((this.acceleration - this.oldAcceleration)/2) * sim.timeStep;
this.velocity.y += diffY * ((this.acceleration - this.oldAcceleration)/2) * sim.timeStep;
this.velocity.z += diffZ * ((this.acceleration - this.oldAcceleration)/2) * sim.timeStep;
var stepX = (this.velocity.x - dragX + this.acceleration);
var stepY = (this.velocity.y - dragY + this.acceleration);
var stepZ = (this.velocity.z - dragZ - sim.currentGravity + this.liftForce);// + this.acceleration);
this.position.x += stepX;
this.position.y += stepY;
this.position.z += stepZ;
this.oldAcceleration = this.acceleration;
//calculate current speed and travelled distance of our bot
var tmp = hypot(stepX, stepY, stepZ);
this.distanceTravelled += tmp;
this.speed = (tmp / sim.timeStep); //V = S / t, in m/s
this.altitude = this.position.z;
};
Hope someone will show me where I did it wrong