Skip to content

Commit

Permalink
Merge pull request #4946 from starling13/atmospheric-flight
Browse files Browse the repository at this point in the history
Improve Atmospheric Flight, Fix Atmosphere Temperature
  • Loading branch information
Webster Sheets authored Aug 29, 2020
2 parents a9c9d8b + 25c6773 commit 8bbf814
Show file tree
Hide file tree
Showing 4 changed files with 56 additions and 40 deletions.
23 changes: 15 additions & 8 deletions src/DynamicBody.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -97,6 +97,18 @@ void DynamicBody::SaveToJson(Json &jsonObj, Space *space)
jsonObj["dynamic_body"] = dynamicBodyObj; // Add dynamic body object to supplied object.
}

void DynamicBody::GetCurrentAtmosphericState(double &pressure, double &density) const
{
Frame *f = Frame::GetFrame(GetFrame());
Body *body = f->GetBody();
if (!body || !f->IsRotFrame() || !body->IsType(Object::PLANET)) {
pressure = density = 0;
return;
}
Planet *planet = static_cast<Planet *>(body);
planet->GetAtmosphericState(GetPosition().Length(), &pressure, &density);
}

void DynamicBody::PostLoadFixup(Space *space)
{
Body::PostLoadFixup(space);
Expand Down Expand Up @@ -190,16 +202,11 @@ void DynamicBody::SetFrame(FrameId fId)

double DynamicBody::CalcAtmosphericDrag(double velSqr, double area, double coeff) const
{
Frame *f = Frame::GetFrame(GetFrame());
Body *body = f->GetBody();
if (!body || !f->IsRotFrame() || !body->IsType(Object::PLANET))
return 0.0;
Planet *planet = static_cast<Planet *>(body);
double pressure, density;
planet->GetAtmosphericState(GetPosition().Length(), &pressure, &density);
GetCurrentAtmosphericState(pressure, density);

// Simplified calculation of atmospheric drag/lift force.
return 0.5 * density * velSqr * area * coeff;
return density > 0 ? 0.5 * density * velSqr * area * coeff : 0;
}

vector3d DynamicBody::CalcAtmosphericForce() const
Expand Down Expand Up @@ -248,7 +255,7 @@ void DynamicBody::CalcExternalForce()
if (f->IsRotFrame()) {
vector3d angRot(0, f->GetAngSpeed(), 0);
m_externalForce -= m_mass * angRot.Cross(angRot.Cross(GetPosition())); // centrifugal
m_externalForce -= 2 * m_mass * angRot.Cross(GetVelocity()); // coriolis
m_externalForce -= 2 * m_mass * angRot.Cross(GetVelocity()); // coriolis
}
}

Expand Down
2 changes: 2 additions & 0 deletions src/DynamicBody.h
Original file line number Diff line number Diff line change
Expand Up @@ -93,6 +93,8 @@ class DynamicBody : public ModelBody {
protected:
virtual void SaveToJson(Json &jsonObj, Space *space) override;

void GetCurrentAtmosphericState(double &pressure, double &density) const;

virtual vector3d CalcAtmosphericForce() const;

static const double DEFAULT_DRAG_COEFF;
Expand Down
24 changes: 12 additions & 12 deletions src/Planet.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -54,15 +54,15 @@ void Planet::InitParams(const SystemBody *sbody)
const double GAS_CONSTANT = 8.3144621;
const double PA_2_ATMOS = 1.0 / 101325.0;

// surface gravity = -G*M/planet radius^2
m_surfaceGravity_g = -G * sbody->GetMass() / (sbody->GetRadius() * sbody->GetRadius());
const double lapseRate_L = -m_surfaceGravity_g / specificHeatCp; // negative deg/m
const double surfaceTemperature_T0 = sbody->GetAverageTemp(); //K
// surface gravity = G*M/planet radius^2
m_surfaceGravity_g = G * sbody->GetMass() / (sbody->GetRadius() * sbody->GetRadius());
const double lapseRate_L = m_surfaceGravity_g / specificHeatCp; // deg/m
const double surfaceTemperature_T0 = sbody->GetAverageTemp(); //K

double surfaceDensity, h;
Color c;
sbody->GetAtmosphereFlavor(&c, &surfaceDensity); // kg / m^3
surfaceDensity /= gasMolarMass; // convert to moles/m^3
surfaceDensity /= gasMolarMass; // convert to moles/m^3

//P = density*R*T=(n/V)*R*T
const double surfaceP_p0 = PA_2_ATMOS * ((surfaceDensity)*GAS_CONSTANT * surfaceTemperature_T0); // in atmospheres
Expand All @@ -72,7 +72,7 @@ void Planet::InitParams(const SystemBody *sbody)
//*outPressure = p0*(1-l*h/T0)^(g*M/(R*L);
// want height for pressure 0.001 atm:
// h = (1 - exp(RL/gM * log(P/p0))) * T0 / l
double RLdivgM = (GAS_CONSTANT * lapseRate_L) / (-m_surfaceGravity_g * gasMolarMass);
double RLdivgM = (GAS_CONSTANT * lapseRate_L) / (m_surfaceGravity_g * gasMolarMass);
h = (1.0 - exp(RLdivgM * log(0.001 / surfaceP_p0))) * surfaceTemperature_T0 / lapseRate_L;
// double h2 = (1.0 - pow(0.001/surfaceP_p0, RLdivgM)) * surfaceTemperature_T0 / lapseRate_L;
// double P = surfaceP_p0*pow((1.0-lapseRate_L*h/surfaceTemperature_T0),1/RLdivgM);
Expand Down Expand Up @@ -136,9 +136,9 @@ void Planet::GetAtmosphericState(double dist, double *outPressure, double *outDe
// lapse rate http://en.wikipedia.org/wiki/Adiabatic_lapse_rate#Dry_adiabatic_lapse_rate
// the wet adiabatic rate can be used when cloud layers are incorporated
// fairly accurate in the troposphere
const double lapseRate_L = -m_surfaceGravity_g / specificHeatCp; // negative deg/m
const double lapseRate_L = m_surfaceGravity_g / specificHeatCp; // deg/m

const double height_h = (dist - sbody->GetRadius()); // height in m
const double height_h = (dist - sbody->GetRadius()); // height in m
const double surfaceTemperature_T0 = sbody->GetAverageTemp(); //K

Color c;
Expand All @@ -157,10 +157,10 @@ void Planet::GetAtmosphericState(double dist, double *outPressure, double *outDe
}

//*outPressure = p0*(1-l*h/T0)^(g*M/(R*L);
*outPressure = surfaceP_p0 * pow((1 - lapseRate_L * height_h / surfaceTemperature_T0), (-m_surfaceGravity_g * gasMolarMass / (GAS_CONSTANT * lapseRate_L))); // in ATM since p0 was in ATM
*outPressure = surfaceP_p0 * pow((1 - lapseRate_L * height_h / surfaceTemperature_T0), (m_surfaceGravity_g * gasMolarMass / (GAS_CONSTANT * lapseRate_L))); // in ATM since p0 was in ATM
// ^^g used is abs(g)
// temperature at height
double temp = surfaceTemperature_T0 + lapseRate_L * height_h;
double temp = surfaceTemperature_T0 - lapseRate_L * height_h;

*outDensity = (*outPressure / (PA_2_ATMOS * GAS_CONSTANT * temp)) * gasMolarMass;
}
Expand Down Expand Up @@ -222,9 +222,9 @@ void Planet::GenerateRings(Graphics::Renderer *renderer)
{
Color *row;
row = buf.get();
std::fill_n(row, RING_TEXTURE_WIDTH, Color::BLACK);
std::fill_n(row, RING_TEXTURE_WIDTH, Color::BLACK);
row = buf.get() + (RING_TEXTURE_LENGTH - 1) * RING_TEXTURE_WIDTH;
std::fill_n(row, RING_TEXTURE_WIDTH, Color::BLACK);
std::fill_n(row, RING_TEXTURE_WIDTH, Color::BLACK);
}

const vector3f texSize(RING_TEXTURE_WIDTH, RING_TEXTURE_LENGTH, 0.0f);
Expand Down
47 changes: 27 additions & 20 deletions src/Ship.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -379,50 +379,57 @@ inline int sign(T num)
vector3d Ship::CalcAtmosphericForce() const
{
// Data from ship.
double m_topCrossSec = GetShipType()->topCrossSection;
double m_sideCrossSec = GetShipType()->sideCrossSection;
double m_frontCrossSec = GetShipType()->frontCrossSection;
const double topCrossSec = GetShipType()->topCrossSection;
const double sideCrossSec = GetShipType()->sideCrossSection;
const double frontCrossSec = GetShipType()->frontCrossSection;

// TODO: vary drag coefficient based on Reynolds number, specifically by
// atmospheric composition (viscosity) and airspeed (mach number).
double m_topDragCoeff = GetShipType()->topDragCoeff;
double m_sideDragCoeff = GetShipType()->sideDragCoeff;
double m_frontDragCoeff = GetShipType()->frontDragCoeff;
const double topDragCoeff = GetShipType()->topDragCoeff;
const double sideDragCoeff = GetShipType()->sideDragCoeff;
const double frontDragCoeff = GetShipType()->frontDragCoeff;

double m_shipLiftCoeff = GetShipType()->shipLiftCoefficient;
const double shipLiftCoeff = GetShipType()->shipLiftCoefficient;

// By converting the velocity into local space, we can apply the drag individually to each component.
auto m_localVel = GetVelocity() * GetOrient();
auto m_lVSqr = m_localVel.LengthSqr();
const vector3d localVel = GetVelocity() * GetOrient();

// The drag forces applied to the craft, in local space.
// TODO: verify dimensional accuracy and that we're not generating more drag than physically possible.
// TODO: use a different drag constant for each side (front, back, etc).
// This also handles (most of) the lift due to wing deflection.
vector3d fAtmosDrag = vector3d(
CalcAtmosphericDrag(m_lVSqr, m_sideCrossSec, m_sideDragCoeff),
CalcAtmosphericDrag(m_lVSqr, m_topCrossSec, m_topDragCoeff),
CalcAtmosphericDrag(m_lVSqr, m_frontCrossSec, m_frontDragCoeff));

// The direction vector of the velocity also serves to scale and sign the generated drag.
fAtmosDrag = fAtmosDrag * -m_localVel.NormalizedSafe();
// Get current atmosphere parameters
double pressure, density;
GetCurrentAtmosphericState(pressure, density);
if (is_zero_exact(density))
return vector3d(0.0);

// Precalculate common part of drag components calculation (rho / 2)
const auto rho_2 = density / 2.;
// Vector, which components are square of local airspeed vector components
const vector3d lv2(localVel * localVel);
const vector3d fAtmosDrag(
-sign(localVel.x) * rho_2 * lv2.x * sideCrossSec * sideDragCoeff,
-sign(localVel.y) * rho_2 * lv2.y * topCrossSec * topDragCoeff,
-sign(localVel.z) * rho_2 * lv2.z * frontCrossSec * frontDragCoeff);

// The amount of lift produced by air pressure differential across the top and bottom of the lifting surfaces.
vector3d fAtmosLift = vector3d(0.0);
vector3d fAtmosLift(0.0);

double m_AoAMultiplier = m_localVel.NormalizedSafe().y;
double AoAMultiplier = localVel.NormalizedSafe().y;

// There's no lift produced once the wing hits the stall angle.
if (std::abs(m_AoAMultiplier) < 0.61) {
if (std::abs(AoAMultiplier) < 0.61) {
// Pioneer simulates non-cambered wings, with equal air displacement on either side of AoA.

// Generated lift peaks at around 20 degrees here, and falls off fully at 35-ish.
// TODO: handle AoA better / more gracefully with an actual angle- and curve-based implementation.
m_AoAMultiplier = cos((std::abs(m_AoAMultiplier) - 0.31) * 5.0) * sign(m_AoAMultiplier);
AoAMultiplier = cos((std::abs(AoAMultiplier) - 0.31) * 5.0) * sign(AoAMultiplier);

// TODO: verify dimensional accuracy and that we're not generating more lift than physically possible.
// We scale down the lift contribution because fAtmosDrag handles deflection-based lift.
fAtmosLift.y = CalcAtmosphericDrag(pow(m_localVel.z, 2), m_topCrossSec, m_shipLiftCoeff) * -m_AoAMultiplier * 0.2;
fAtmosLift.y = CalcAtmosphericDrag(pow(localVel.z, 2), topCrossSec, shipLiftCoeff) * -AoAMultiplier * 0.2;
}

return GetOrient() * (fAtmosDrag + fAtmosLift);
Expand Down

0 comments on commit 8bbf814

Please sign in to comment.