const AircraftState ToAircraftState(const MoreData &info, const DerivedInfo &calculated) { AircraftState aircraft; /* SPEED_STATE */ aircraft.ground_speed = info.ground_speed; aircraft.true_airspeed = info.true_airspeed; aircraft.indicated_airspeed = info.indicated_airspeed; /* ALTITUDE_STATE */ aircraft.altitude = info.nav_altitude; aircraft.working_band_fraction = calculated.thermal_band.working_band_fraction; aircraft.altitude_agl = calculated.altitude_agl; /* VARIO_INFO */ aircraft.vario = info.brutto_vario; aircraft.netto_vario = info.netto_vario; /* FLYING_STATE */ (FlyingState &)aircraft = calculated.flight; /* AIRCRAFT_STATE */ aircraft.time = info.time; aircraft.location = info.location; aircraft.track = info.track; aircraft.g_load = info.acceleration.available ? info.acceleration.g_load : fixed_one; aircraft.wind = calculated.GetWindOrZero(); return aircraft; }
void GlideComputerAirData::ResetLiftDatabase(DerivedInfo &calculated) { calculated.ClearLiftDatabase(); calculated.trace_history.CirclingAverage.clear(); }
void GlideComputerTask::ProcessBasicTask(const MoreData &basic, const MoreData &last_basic, DerivedInfo &calculated, const DerivedInfo &last_calculated, const ComputerSettings &settings_computer) { if (basic.HasTimeAdvancedSince(last_basic) && basic.location_available) trace.Update(settings_computer, ToAircraftState(basic, calculated)); ProtectedTaskManager::ExclusiveLease _task(task); _task->SetTaskBehaviour(settings_computer.task); if (basic.HasTimeAdvancedSince(last_basic) && basic.location_available) { const AircraftState current_as = ToAircraftState(basic, calculated); const AircraftState last_as = ToAircraftState(last_basic, last_calculated); _task->Update(current_as, last_as); const fixed fallback_mc = calculated.last_thermal.IsDefined() && positive(calculated.last_thermal_average_smooth) ? calculated.last_thermal_average_smooth : fixed_zero; if (_task->UpdateAutoMC(current_as, fallback_mc)) calculated.ProvideAutoMacCready(basic.clock, _task->GetGlidePolar().GetMC()); } calculated.task_stats = _task->GetStats(); calculated.common_stats = _task->GetCommonStats(); calculated.glide_polar_safety = _task->GetSafetyPolar(); }
static Phase::CirclingDirection CalcCirclingDirection(const DerivedInfo &calculated) { if (!calculated.circling) { return Phase::CirclingDirection::NO_DIRECTION; } return calculated.TurningLeft() ? Phase::CirclingDirection::LEFT : Phase::CirclingDirection::RIGHT; }
void GlideComputerAirData::UpdateLiftDatabase(const MoreData &basic, DerivedInfo &calculated, const DerivedInfo &last_calculated) { // If we just started circling // -> reset the database because this is a new thermal if (!calculated.circling && last_calculated.circling) ResetLiftDatabase(calculated); // Determine the direction in which we are circling bool left = calculated.TurningLeft(); // Depending on the direction set the step size sign for the // following loop Angle heading_step = Angle::Degrees(fixed(left ? -10 : 10)); // Start at the last heading and add heading_step until the current heading // is reached. For each heading save the current lift value into the // LiftDatabase. Last and current heading are included since they are // a part of the ten degree interval most of the time. // // This is done with Angles to deal with the 360 degrees limit. // e.g. last heading 348 degrees, current heading 21 degrees // // The loop condition stops until the current heading is reached. // Depending on the circling direction the current heading will be // smaller or bigger then the last one, because of that negative() is // tested against the left variable. for (Angle h = last_calculated.heading; left == negative((calculated.heading - h).AsDelta().Degrees()); h += heading_step) { unsigned index = heading_to_index(h); calculated.lift_database[index] = basic.brutto_vario; } // detect zero crossing if (((calculated.heading.Degrees()< fixed_90) && (last_calculated.heading.Degrees()> fixed_270)) || ((last_calculated.heading.Degrees()< fixed_90) && (calculated.heading.Degrees()> fixed_270))) { fixed h_av = fixed_zero; for (unsigned i=0; i<36; ++i) { h_av += calculated.lift_database[i]; } h_av/= 36; calculated.trace_history.CirclingAverage.push(h_av); } }
void GlideComputerAirData::ProcessVertical(const MoreData &basic, const MoreData &last_basic, DerivedInfo &calculated, const ComputerSettings &settings) { /* the "circling" flag may be modified by CirclingComputer::Turning(); remember the old state so this method can check for modifications */ const bool last_circling = calculated.circling; auto_qnh.Process(basic, calculated, settings, waypoints); circling_computer.TurnRate(calculated, basic, calculated.flight); Turning(basic, calculated, settings); wind_computer.Compute(settings.wind, settings.polar.glide_polar_task, basic, calculated); wind_computer.Select(settings.wind, basic, calculated); wind_computer.ComputeHeadWind(basic, calculated); thermallocator.Process(calculated.circling, basic.time, basic.location, basic.netto_vario, calculated.GetWindOrZero(), calculated.thermal_locator); LastThermalStats(basic, calculated, last_circling); gr_computer.Compute(basic, last_basic, calculated, calculated, settings); GR(basic, calculated.flight, calculated); CruiseGR(basic, calculated); average_vario.Compute(basic, calculated.circling, last_circling, calculated); AverageClimbRate(basic, calculated); if (calculated.circling) CurrentThermal(basic, calculated, calculated.current_thermal); lift_database_computer.Compute(calculated.lift_database, calculated.trace_history.CirclingAverage, basic, calculated); circling_computer.MaxHeightGain(basic, calculated.flight, calculated); NextLegEqThermal(basic, calculated, settings); }
void RouteComputer::ProcessRoute(const MoreData &basic, DerivedInfo &calculated, const GlideSettings &settings, const RoutePlannerConfig &config, const GlidePolar &glide_polar, const GlidePolar &safety_polar) { if (!basic.location_available || !basic.NavAltitudeAvailable()) return; protected_route_planner.SetPolars(settings, glide_polar, safety_polar, calculated.GetWindOrZero()); Reach(basic, calculated, config); TerrainWarning(basic, calculated, config); }
void ThermalBandComputer::Compute(const MoreData &basic, const DerivedInfo &calculated, ThermalBandInfo &tbi, const ComputerSettings &settings) { if (!basic.NavAltitudeAvailable()) return; const fixed h_safety = settings.task.route_planner.safety_height_terrain + calculated.GetTerrainBaseFallback(); tbi.working_band_height = basic.TE_altitude - h_safety; if (negative(tbi.working_band_height)) { tbi.working_band_fraction = fixed(0); return; } const fixed max_height = tbi.max_thermal_height; if (positive(max_height)) tbi.working_band_fraction = tbi.working_band_height / max_height; else tbi.working_band_fraction = fixed(1); tbi.working_band_ceiling = std::max(max_height + h_safety, basic.TE_altitude); last_vario_available.FixTimeWarp(basic.brutto_vario_available); if (basic.brutto_vario_available.Modified(last_vario_available)) { last_vario_available = basic.brutto_vario_available; // JMW TODO accuracy: Should really work out dt here, // but i'm assuming constant time steps if (tbi.max_thermal_height == fixed(0)) tbi.max_thermal_height = tbi.working_band_height; // only do this if in thermal and have been climbing if (calculated.circling && calculated.turning && positive(calculated.average)) tbi.Add(tbi.working_band_height, basic.brutto_vario); } }
void CalculateDirect(const PolarSettings &polar_settings, const TaskBehaviour &task_behaviour, const DerivedInfo &calculated) { if (!basic.location_available || !basic.NavAltitudeAvailable()) return; const GlidePolar &glide_polar = task_behaviour.route_planner.reach_polar_mode == RoutePlannerConfig::Polar::TASK ? polar_settings.glide_polar_task : calculated.glide_polar_safety; const MacCready mac_cready(task_behaviour.glide, glide_polar); for (VisibleWaypoint &vwp : waypoints) { const Waypoint &way_point = *vwp.waypoint; if (way_point.IsLandable() || way_point.flags.watched) vwp.CalculateReachabilityDirect(basic, calculated.GetWindOrZero(), mac_cready, task_behaviour); } }
bool ConditionMonitorWind::CheckCondition(const NMEAInfo &basic, const DerivedInfo &calculated, const ComputerSettings &settings) { wind = calculated.GetWindOrZero(); if (!calculated.flight.flying) { last_wind = wind; return false; } fixed mag_change = fabs(wind.norm - last_wind.norm); fixed dir_change = (wind.bearing - last_wind.bearing).AsDelta().AbsoluteDegrees(); if (mag_change > fixed(2.5)) return true; return wind.norm > fixed(5) && dir_change > fixed(45); }
const AircraftState ToAircraftState(const MoreData &info, const DerivedInfo &calculated) { AircraftState aircraft; /* SPEED_STATE */ aircraft.ground_speed = info.ground_speed; aircraft.true_airspeed = info.true_airspeed; /* ALTITUDE_STATE */ aircraft.altitude = info.NavAltitudeAvailable() ? info.nav_altitude : 0.; aircraft.working_band_fraction = calculated.common_stats.height_fraction_working; aircraft.altitude_agl = info.NavAltitudeAvailable() && calculated.terrain_valid ? calculated.altitude_agl : 0.; /* VARIO_INFO */ aircraft.vario = info.brutto_vario; aircraft.netto_vario = info.netto_vario; /* AIRCRAFT_STATE */ aircraft.time = info.time_available ? info.time : -1.; aircraft.location = info.location_available ? info.location : GeoPoint::Invalid(); aircraft.track = info.track; aircraft.g_load = info.acceleration.available ? info.acceleration.g_load : 1.; aircraft.wind = calculated.GetWindOrZero(); aircraft.flying = calculated.flight.flying; return aircraft; }
void GlideComputerAirData::ProcessVertical(const MoreData &basic, const MoreData &last_basic, DerivedInfo &calculated, const DerivedInfo &last_calculated, const ComputerSettings &settings) { auto_qnh.Process(basic, calculated, settings, waypoints); Heading(basic, calculated); circling_computer.TurnRate(calculated, basic, last_basic, calculated, last_calculated); Turning(basic, last_basic, calculated, last_calculated, settings); wind_computer.Compute(settings.wind, settings.polar.glide_polar_task, basic, calculated); wind_computer.Select(settings.wind, basic, calculated); wind_computer.ComputeHeadWind(basic, calculated); thermallocator.Process(calculated.circling, basic.time, basic.location, basic.netto_vario, calculated.GetWindOrZero(), calculated.thermal_locator); LastThermalStats(basic, calculated, last_calculated); GR(basic, last_basic, calculated, calculated); CruiseGR(basic, calculated); if (calculated.flight.flying && !calculated.circling) calculated.average_gr = gr_calculator.Calculate(); Average30s(basic, last_basic, calculated, last_calculated); AverageClimbRate(basic, calculated); CurrentThermal(basic, calculated, calculated.current_thermal); UpdateLiftDatabase(basic, calculated, last_calculated); circling_computer.MaxHeightGain(basic, calculated.flight, calculated); NextLegEqThermal(basic, calculated, settings); }
void GlideComputerAirData::ThermalSources(const MoreData &basic, const DerivedInfo &calculated, ThermalLocatorInfo &thermal_locator) { if (!thermal_locator.estimate_valid || !basic.NavAltitudeAvailable() || !calculated.last_thermal.IsDefined() || negative(calculated.last_thermal.lift_rate)) return; if (calculated.wind_available && calculated.wind.norm / calculated.last_thermal.lift_rate > fixed(10.0)) { // thermal strength is so weak compared to wind that source estimate // is unlikely to be reliable, so don't calculate or remember it return; } GeoPoint ground_location; fixed ground_altitude = fixed_minus_one; EstimateThermalBase(terrain, thermal_locator.estimate_location, basic.nav_altitude, calculated.last_thermal.lift_rate, calculated.GetWindOrZero(), ground_location, ground_altitude); if (positive(ground_altitude)) { ThermalSource &source = thermal_locator.AllocateSource(basic.time); source.lift_rate = calculated.last_thermal.lift_rate; source.location = ground_location; source.ground_height = ground_altitude; source.time = basic.time; } }
void ThermalBandRenderer::_DrawThermalBand(const MoreData &basic, const DerivedInfo& calculated, const ComputerSettings &settings_computer, ChartRenderer &chart, const TaskBehaviour& task_props, const bool is_infobox, const OrderedTaskBehaviour *ordered_props) const { const ThermalBandInfo &thermal_band = calculated.thermal_band; // calculate height above safety height fixed hoffset = task_props.route_planner.safety_height_terrain + calculated.GetTerrainBaseFallback(); fixed h = fixed(0); if (basic.NavAltitudeAvailable()) { h = basic.nav_altitude - hoffset; chart.ScaleYFromValue(h); } bool draw_start_height = false; fixed hstart = fixed(0); draw_start_height = ordered_props && calculated.ordered_task_stats.task_valid && ordered_props->start_constraints.max_height != 0 && calculated.terrain_valid; if (draw_start_height) { hstart = fixed(ordered_props->start_constraints.max_height); if (ordered_props->start_constraints.max_height_ref == AltitudeReference::AGL && calculated.terrain_valid) hstart += calculated.terrain_altitude; hstart -= hoffset; chart.ScaleYFromValue(hstart); } // no thermalling has been done above safety height if (!positive(calculated.thermal_band.max_thermal_height)) return; // calculate averages int numtherm = 0; fixed Wmax = fixed(0); fixed Wav = fixed(0); fixed Wt[ThermalBandInfo::NUMTHERMALBUCKETS]; fixed ht[ThermalBandInfo::NUMTHERMALBUCKETS]; for (unsigned i = 0; i < ThermalBandInfo::NUMTHERMALBUCKETS; ++i) { if (thermal_band.thermal_profile_n[i] < 6) continue; if (positive(thermal_band.thermal_profile_w[i])) { // height of this thermal point [0,mth] // requires 5 items in bucket before displaying, to eliminate kinks fixed wthis = thermal_band.thermal_profile_w[i] / thermal_band.thermal_profile_n[i]; ht[numtherm] = i * calculated.thermal_band.max_thermal_height / ThermalBandInfo::NUMTHERMALBUCKETS; Wt[numtherm] = wthis; Wmax = std::max(Wmax, wthis); Wav+= wthis; numtherm++; } } chart.ScaleXFromValue(Wmax); if (!numtherm) return; chart.ScaleXFromValue(fixed(1.5)*Wav/numtherm); if ((!draw_start_height) && (numtherm<=1)) // don't display if insufficient statistics // but do draw if start height needs to be drawn return; const Pen *fpen = is_infobox ? NULL : &look.pen; // position of thermal band if (numtherm > 1) { std::vector< std::pair<fixed, fixed> > thermal_profile; thermal_profile.reserve(numtherm); for (int i = 0; i < numtherm; ++i) thermal_profile.emplace_back(Wt[i], ht[i]); if (!is_infobox) { #ifdef ENABLE_OPENGL const GLEnable blend(GL_BLEND); #endif chart.DrawFilledY(thermal_profile, look.brush, fpen); } else chart.DrawFilledY(thermal_profile, look.brush, fpen); } // position of thermal band if (basic.NavAltitudeAvailable()) { const Pen &pen = is_infobox && look.inverse ? look.white_pen : look.black_pen; chart.DrawLine(fixed(0), h, settings_computer.polar.glide_polar_task.GetMC(), h, pen); if (is_infobox && look.inverse) chart.GetCanvas().SelectWhiteBrush(); else chart.GetCanvas().SelectBlackBrush(); chart.DrawDot(settings_computer.polar.glide_polar_task.GetMC(), h, Layout::Scale(2)); } }