/** * Attempt to compute airspeed when it is not yet available from: * 1) dynamic pressure and air density derived from some altitude. * 2) pitot pressure and static pressure. * 3) ground speed and wind. */ static void ComputeAirspeed(NMEAInfo &basic, const DerivedInfo &calculated) { if (basic.airspeed_available && basic.airspeed_real) /* got it already */ return; const auto any_altitude = basic.GetAnyAltitude(); if (!basic.airspeed_available && any_altitude.first) { fixed dyn; bool available = false; if (basic.dyn_pressure_available) { dyn = basic.dyn_pressure.GetHectoPascal(); available = true; } else if (basic.pitot_pressure_available && basic.static_pressure_available) { dyn = basic.pitot_pressure.GetHectoPascal() - basic.static_pressure.GetHectoPascal(); available = true; } if (available) { basic.indicated_airspeed = sqrt(fixed(163.2653061) * dyn); basic.true_airspeed = basic.indicated_airspeed * AirDensityRatio(any_altitude.second); basic.airspeed_available.Update(basic.clock); basic.airspeed_real = true; // Anyway not less real then any other method. return; } } if (!basic.ground_speed_available || !calculated.wind_available || !calculated.flight.flying) { /* impossible to calculate */ basic.airspeed_available.Clear(); return; } fixed TrueAirspeedEstimated = fixed(0); const SpeedVector wind = calculated.wind; if (positive(basic.ground_speed) || wind.IsNonZero()) { fixed x0 = basic.track.fastsine() * basic.ground_speed; fixed y0 = basic.track.fastcosine() * basic.ground_speed; x0 += wind.bearing.fastsine() * wind.norm; y0 += wind.bearing.fastcosine() * wind.norm; TrueAirspeedEstimated = SmallHypot(x0, y0); } basic.true_airspeed = TrueAirspeedEstimated; basic.indicated_airspeed = TrueAirspeedEstimated; if (any_altitude.first) basic.indicated_airspeed /= AirDensityRatio(any_altitude.second); basic.airspeed_available.Update(basic.clock); basic.airspeed_real = false; }
void GlideComputerAirData::Heading(const NMEAInfo &basic, DerivedInfo &calculated) { const SpeedVector wind = calculated.wind; if (calculated.wind_available && (positive(basic.ground_speed) || wind.IsNonZero()) && calculated.flight.flying) { fixed x0 = basic.track.fastsine() * basic.ground_speed; fixed y0 = basic.track.fastcosine() * basic.ground_speed; x0 += wind.bearing.fastsine() * wind.norm; y0 += wind.bearing.fastcosine() * wind.norm; calculated.heading = Angle::Radians(atan2(x0, y0)).AsBearing(); } else { calculated.heading = basic.track; } }
/* !w,<1>,<2>,<3>,<4>,<5>,<6>,<7>,<8>,<9>,<10>,<11>,<12>,<13>*hh<CR><LF> <1> Vector wind direction in degrees <2> Vector wind speed in 10ths of meters per second <3> Vector wind age in seconds <4> Component wind in 10ths of Meters per second + 500 (500 = 0, 495 = 0.5 m/s tailwind) <5> True altitude in Meters + 1000 <6> Instrument QNH setting <7> True airspeed in 100ths of Meters per second <8> Variometer reading in 10ths of knots + 200 <9> Averager reading in 10ths of knots + 200 <10> Relative variometer reading in 10ths of knots + 200 <11> Instrument MacCready setting in 10ths of knots <12> Instrument Ballast setting in percent of capacity <13> Instrument Bug setting *hh Checksum, XOR of all bytes */ static bool cai_w(NMEAInputLine &line, NMEAInfo &info) { SpeedVector wind; if (ReadSpeedVector(line, wind)) info.ProvideExternalWind(wind.Reciprocal()); line.skip(2); fixed value; if (line.read_checked(value)) info.ProvideBaroAltitudeTrue(value - fixed(1000)); if (line.read_checked(value)) info.settings.ProvideQNH(value, info.clock); if (line.read_checked(value)) info.ProvideTrueAirspeed(value / 100); if (line.read_checked(value)) info.ProvideTotalEnergyVario(Units::ToSysUnit((value - fixed(200)) / 10, unKnots)); line.skip(2); int i; if (line.read_checked(i)) info.settings.ProvideMacCready(Units::ToSysUnit(fixed(i) / 10, unKnots), info.clock); if (line.read_checked(i)) info.settings.ProvideBallastFraction(fixed(i) / 100, info.clock); if (line.read_checked(i)) info.settings.ProvideBugs(fixed(i) / 100, info.clock); return true; }
void GlideState::CalcSpeedups(const SpeedVector _wind) { if (_wind.IsNonZero()) { wind = _wind; effective_wind_angle = wind.bearing.Reciprocal() - vector.bearing; wind_speed_squared = sqr(wind.norm); head_wind = -wind.norm * effective_wind_angle.cos(); } else { wind = SpeedVector::Zero(); effective_wind_angle = Angle::Zero(); head_wind = fixed(0); wind_speed_squared = fixed(0); } }
fixed GlidePolar::GetLDOverGround(Angle track, SpeedVector wind) const { if (wind.IsZero()) return bestLD; const fixed c_theta = (wind.bearing.Reciprocal() - track).cos(); /* convert the wind speed into some sort of "virtual L/D" to put it in relation to the polar's best L/D */ const fixed wind_ld = wind.norm / GetSBestLD(); Quadratic q(- Double(wind_ld * c_theta), sqr(wind_ld) - sqr(bestLD)); if (q.Check()) return std::max(fixed(0), q.SolutionMax()); return fixed(0); }
static void DrawThermalSources(Canvas &canvas, const MaskedIcon &icon, const WindowProjection &projection, const T &sources, const double aircraft_altitude, const SpeedVector &wind) { for (const auto &source : sources) { // find height difference if (aircraft_altitude < source.ground_height) continue; // draw thermal at location it would be at the glider's height GeoPoint location = wind.IsNonZero() ? source.CalculateAdjustedLocation(aircraft_altitude, wind) : source.location; // draw if it is in the field of view PixelPoint pt; if (projection.GeoToScreenIfVisible(location, pt)) icon.Draw(canvas, pt); } }