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 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)); } }