void GlidePolarTest::Init() { // Polar 1 from PolarStore (206 Hornet) polar.SetCoefficients(PolarCoefficients(fixed(0.0022032), fixed(-0.08784), fixed(1.47)), false); polar.SetReferenceMass(fixed(318), false); polar.SetDryMass(fixed(318), false); polar.SetBallastRatio(fixed(100) / polar.reference_mass); polar.SetWingArea(fixed(9.8)); // No ballast and no bugs on the wings polar.ballast = fixed(0); polar.bugs = fixed(1); // MC zero polar.mc = fixed(0); polar.SetVMax(Units::ToSysUnit(fixed(200), Unit::KILOMETER_PER_HOUR), false); }
/** * Loads calculated values from the persistent memory file * @param Calculated DERIVED_INFO the values should be loaded into */ void LoadCalculationsPersist(DERIVED_INFO *Calculated, ProtectedTaskManager &protected_task_manager, GlideComputer &glide_computer) { return; // do nothing, this is broken for CommonStats // Get the persistent memory filename if (szCalculationsPersistFileName[0] == 0) { if (is_altair()) { LocalPath(szCalculationsPersistFileName, _T("persist/xcsoar-persist.log")); LocalPath(szCalculationsPersistDirectory, _T("persist")); } else { LocalPath(szCalculationsPersistFileName, _T("xcsoar-persist.log")); _tcscpy(szCalculationsPersistDirectory, GetPrimaryDataPath()); } } // Debug Log LogStartUp(_T("LoadCalculationsPersist")); unsigned sizein; // Try to open the persistent memory file FILE *file = _tfopen(szCalculationsPersistFileName, _T("rb")); if (file == NULL) { LogStartUp(_T("LoadCalculationsPersist file not found")); return; } fread(&sizein, sizeof(sizein), 1, file); if (sizein != sizeof(*Calculated)) { fclose(file); return; } // Read persistent memory into Calculated fread(Calculated, sizeof(*Calculated), 1, file); fread(&sizein, sizeof(sizein), 1, file); if (sizein != sizeof(glide_computer.GetFlightStats())) { glide_computer.ResetFlight(); fclose(file); return; } // Read persistent memory into FlightStats fread(&glide_computer.GetFlightStats(), sizeof(glide_computer.GetFlightStats()), 1, file); /// @todo persistence for OLC data fread(&sizein, sizeof(sizein), 1, file); if (sizein != 4 * sizeof(double)) { fclose(file); return; } GlidePolar polar = glide_computer.SettingsComputer().glide_polar_task; double MACCREADY = polar.get_mc(); double BUGS = polar.get_bugs(); double BALLAST = polar.get_ballast(); // Read persistent memory into MacCready, QNH, bugs, ballast and temperature fread(&MACCREADY, sizeof(double), 1, file); fread(&BUGS, sizeof(double), 1, file); fread(&BALLAST, sizeof(double), 1, file); fread(&CuSonde::maxGroundTemperature, sizeof(CuSonde::maxGroundTemperature), 1, file); // ReadFile(hFile,&CRUISE_EFFICIENCY, // size,&dwBytesWritten,(OVERLAPPED*)NULL); MACCREADY = min(10.0, max(MACCREADY, 0.0)); BUGS = min(1.0, max(BUGS, 0.0)); BALLAST = min(1.0, max(BALLAST, 0.0)); // CRUISE_EFFICIENCY = min(1.5, max(CRUISE_EFFICIENCY,0.75)); polar.set_mc(fixed(MACCREADY)); polar.set_bugs(fixed(BUGS)); polar.set_ballast(fixed(BALLAST)); protected_task_manager.set_glide_polar(polar); LogStartUp(_T("LoadCalculationsPersist OK")); fclose(file); }
static void Test(const fixed distance, const fixed altitude, const SpeedVector wind) { const GeoVector vector(distance, Angle::Zero()); const GlideState state(vector, fixed(2000), fixed(2000) + altitude, wind); const GlideResult result = MacCready::Solve(glide_settings, glide_polar, state); const fixed ld_ground = glide_polar.GetLDOverGround(vector.bearing, wind); const fixed mc = glide_polar.GetMC(); const fixed v_climb_progress = mc * ld_ground - state.head_wind; const fixed initial_glide_distance = state.altitude_difference * ld_ground; if (initial_glide_distance >= distance || (!positive(mc) && !positive(v_climb_progress))) { /* reachable by pure glide */ ok1(result.validity == GlideResult::Validity::OK); const fixed best_speed = glide_polar.GetBestGlideRatioSpeed(state.head_wind); const fixed best_sink = glide_polar.SinkRate(best_speed); const fixed ld_ground2 = positive(mc) ? ld_ground : (best_speed - state.head_wind) / best_sink; const fixed height_glide = distance / ld_ground2; const fixed height_climb = fixed(0); const fixed altitude_difference = altitude - height_glide; ok1(equals(result.head_wind, wind.norm)); ok1(equals(result.vector.distance, distance)); ok1(equals(result.height_climb, height_climb)); ok1(equals(result.height_glide, height_glide)); ok1(equals(result.altitude_difference, altitude_difference)); return; } if (!positive(v_climb_progress)) { /* excessive wind */ ok1(result.validity == GlideResult::Validity::WIND_EXCESSIVE); return; } /* const fixed drifted_distance = (distance - initial_glide_distance) * state.head_wind / v_climb_progress; */ const fixed drifted_height_climb = (distance - initial_glide_distance) * mc / v_climb_progress; const fixed drifted_height_glide = drifted_height_climb + state.altitude_difference; const fixed height_glide = drifted_height_glide; const fixed altitude_difference = altitude - height_glide; const fixed height_climb = drifted_height_climb; const fixed time_climb = height_climb / mc; const fixed time_glide = height_glide / glide_polar.GetSBestLD(); const fixed time_elapsed = time_climb + time_glide; /* more tolerance with strong wind because this unit test doesn't optimise pure glide */ const int accuracy = positive(altitude) && positive(wind.norm) ? (wind.norm > fixed(5) ? 5 : 10) : ACCURACY; ok1(result.validity == GlideResult::Validity::OK); ok1(equals(result.head_wind, wind.norm)); ok1(equals(result.vector.distance, distance)); ok1(equals(result.height_climb, height_climb, accuracy)); ok1(equals(result.height_glide, height_glide, accuracy)); ok1(equals(result.altitude_difference, altitude_difference, accuracy)); ok1(equals(result.time_elapsed, time_elapsed, accuracy)); }
void VarioBarRenderer::Draw(Canvas &canvas, const PixelRect &rc, const MoreData &basic, const DerivedInfo &calculated, const GlidePolar &glide_polar, const bool vario_bar_avg_enabled) const { #ifdef ENABLE_OPENGL const ScopeAlphaBlend alpha_blend; #endif RasterPoint VarioBar[6] = { { 0, 0 }, { 9, -9 }, { 18, 0 }, { 18, 0 }, { 9, 0 }, { 0, 0 } }; RasterPoint VarioBarAvg[4] = { { 0, 0 }, { 9, -9 }, { 9, 0 }, { 0, 0 } }; RasterPoint clipping_arrow[6] = { { 0, 0 }, { 9, 9 }, { 18, 0 }, { 18, 6 }, { 9, 15 }, { 0, 6 } }; RasterPoint clipping_arrow_av[4] = { { 0, 0 }, { 9, 9 }, { 9, 15 }, { 0, 6 } }; RasterPoint mc_arrow[6] = { { 0, 0 }, { 9, -9 }, { 18, 0 }, { 18, -2 }, { 9, -11 }, { 0, -2 } }; TCHAR Value[10]; const int y0 = (rc.bottom + rc.top) / 2; /* NOTE: size_divisor replaces the fixed value 9 that was used throughout * the code sink which caused fixed size rendering regardless of * map size (except the effects of Layout::Scale()). This method * is not usable with variable map sizes (e.g. because of the cross-section * area). size_divisor is used to introduce a screen size dependent scaling. * That workaround is an ugly hack and needs a rework. */ const auto size_divisor = std::max((fixed) Layout::Scale(70 / (rc.bottom - rc.top)), fixed(0.15)); PixelScalar dy_variobar = 0; PixelScalar dy_variobar_av = 0; PixelScalar dy_variobar_mc = 0; PixelScalar clipping_arrow_offset = Layout::Scale(4); PixelScalar clipping_arrow_av_offset = Layout::Scale(4); // PixelScalar clipping_arrow_mc_offset = Layout::Scale(4); auto vario_gross = basic.brutto_vario; FormatUserVerticalSpeed(vario_gross, Value, false, true); canvas.Select(*look.font); const PixelSize text_size = canvas.CalcTextSize(Value); auto vario_avg = calculated.average; // cut vario_gross at +- 5 meters/s if (vario_gross > fixed(5)) vario_gross = fixed(5); if (vario_gross < fixed(-5)) vario_gross = fixed(-5); int Offset = (int)(vario_gross / size_divisor); Offset = Layout::Scale(Offset); if (!positive(vario_gross)) { VarioBar[1].y = Layout::Scale(9); dy_variobar = text_size.cy + 2; } else { VarioBar[1].y = -Layout::Scale(9); clipping_arrow[1].y = -clipping_arrow[1].y; clipping_arrow[3].y = -clipping_arrow[3].y; clipping_arrow[4].y = -clipping_arrow[4].y; clipping_arrow[5].y = -clipping_arrow[5].y; clipping_arrow_offset = -clipping_arrow_offset; dy_variobar = -1; } // cut vario_avg at +- 5 meters/s if (vario_avg > fixed(5)) vario_avg = fixed(5); if (vario_avg < fixed(-5)) vario_avg = fixed(-5); int OffsetAvg = int(vario_avg / size_divisor); OffsetAvg = Layout::Scale(OffsetAvg); if (!positive(vario_avg)) { VarioBarAvg[1].y = Layout::Scale(9); dy_variobar_av = text_size.cy + 2; } else { VarioBarAvg[1].y = -Layout::Scale(9); clipping_arrow_av[1].y = -clipping_arrow_av[1].y; clipping_arrow_av[2].y = -clipping_arrow_av[2].y; clipping_arrow_av[3].y = -clipping_arrow_av[3].y; clipping_arrow_av_offset = -clipping_arrow_av_offset; dy_variobar_av = -1; } //clip MC Value auto mc_val = glide_polar.GetMC(); if (mc_val > fixed(5)) mc_val = fixed(5); if (!positive(mc_val)) { dy_variobar_mc = text_size.cy + 2; }else{ dy_variobar_mc = -1; } int OffsetMC = int(mc_val / size_divisor); OffsetMC = Layout::Scale(OffsetMC); for (unsigned i = 0; i < 6; i++) { VarioBar[i].y += y0 + dy_variobar; VarioBar[i].x = rc.right - Layout::Scale(VarioBar[i].x); } VarioBar[0].y -= Offset; VarioBar[1].y -= Offset; VarioBar[2].y -= Offset; for (unsigned i = 0; i < 4; i++) { VarioBarAvg[i].y += y0 + dy_variobar_av; VarioBarAvg[i].x = rc.right-Layout::Scale(VarioBarAvg[i].x); } VarioBarAvg[0].y -= OffsetAvg; VarioBarAvg[1].y -= OffsetAvg; // prepare mc arrow for (unsigned i = 0; i < 6; i++) { mc_arrow[i].y = Layout::Scale(mc_arrow[i].y) + y0 - OffsetMC + dy_variobar_mc; mc_arrow[i].x = rc.right - Layout::Scale(mc_arrow[i].x); } // prepare clipping arrow for (unsigned i = 0; i < 6; i++) { clipping_arrow[i].y = Layout::Scale(clipping_arrow[i].y) + y0 - Offset + clipping_arrow_offset + dy_variobar; clipping_arrow[i].x = rc.right - Layout::Scale(clipping_arrow[i].x); } // prepare clipping avg for (unsigned i = 0; i < 4; i++) { clipping_arrow_av[i].y = Layout::Scale(clipping_arrow_av[i].y) + y0 - OffsetAvg + clipping_arrow_av_offset + dy_variobar_av; clipping_arrow_av[i].x = rc.right-Layout::Scale(clipping_arrow_av[i].x); } // draw actual vario bar if (!positive(vario_gross)) { canvas.Select(look.pen_sink); canvas.Select(look.brush_sink); } else { canvas.Select(look.pen_climb); canvas.Select(look.brush_climb); } canvas.DrawPolygon(VarioBar, 6); // draw clipping arrow if (vario_gross <= fixed(-5) || vario_gross >= fixed(5)) canvas.DrawPolygon(clipping_arrow, 6); // draw avg vario bar if (!positive(vario_avg) && vario_bar_avg_enabled) { canvas.Select(look.pen_sink); canvas.Select(look.brush_sink_avg); } else { canvas.Select(look.pen_climb); canvas.Select(look.brush_climb_avg); } canvas.DrawPolygon(VarioBarAvg, 4); if (vario_avg <= fixed(-5.0) || vario_avg >= fixed(5.0)) canvas.DrawPolygon(clipping_arrow_av, 4); //draw MC arrow canvas.Select(look.pen_mc); canvas.Select(look.brush_mc); canvas.DrawPolygon(mc_arrow, 6); //draw text canvas.SetTextColor(COLOR_BLACK); canvas.SetBackgroundColor(COLOR_WHITE); TextInBoxMode style; style.shape = LabelShape::ROUNDED_BLACK; style.move_in_view = true; if (text_size.cx < Layout::Scale(18)) { style.align = TextInBoxMode::Alignment::RIGHT; TextInBox(canvas, Value, rc.right, y0, style, rc); } else TextInBox(canvas, Value, rc.right-Layout::Scale(18), y0, style, rc); }
static void Update(void) { TCHAR sTmp[1000]; FlightStatistics &fs = glide_computer.GetFlightStats(); GlidePolar polar = protected_task_manager.get_glide_polar(); const CommonStats& stats = XCSoarInterface::Calculated().common_stats; switch (page) { case ANALYSIS_PAGE_BAROGRAPH: _stprintf(sTmp, _T("%s: %s"), _("Analysis"), _("Barograph")); wf->SetCaption(sTmp); fs.CaptionBarograph(sTmp); wInfo->SetCaption(sTmp); SetCalcCaption(_("Settings")); break; case ANALYSIS_PAGE_CLIMB: _stprintf(sTmp, _T("%s: %s"), _("Analysis"), _("Climb")); wf->SetCaption(sTmp); fs.CaptionClimb(sTmp); wInfo->SetCaption(sTmp); SetCalcCaption(_("Task calc")); break; case ANALYSIS_PAGE_WIND: _stprintf(sTmp, _T("%s: %s"), _("Analysis"), _("Wind at Altitude")); wf->SetCaption(sTmp); wInfo->SetCaption(_T("")); SetCalcCaption(_("Set wind")); break; case ANALYSIS_PAGE_POLAR: _stprintf(sTmp, _T("%s: %s (%s %d kg)"), _("Analysis"), _("Glide Polar"), _("Mass"), (int)polar.get_all_up_weight()); wf->SetCaption(sTmp); fs.CaptionPolar(sTmp, polar); wInfo->SetCaption(sTmp); SetCalcCaption(_("Settings")); break; case ANALYSIS_PAGE_TEMPTRACE: _stprintf(sTmp, _T("%s: %s"), _("Analysis"), _("Temp trace")); wf->SetCaption(sTmp); fs.CaptionTempTrace(sTmp); wInfo->SetCaption(sTmp); SetCalcCaption(_("Settings")); break; case ANALYSIS_PAGE_TASK_SPEED: _stprintf(sTmp, _T("%s: %s"), _("Analysis"), _("Task speed")); wf->SetCaption(sTmp); wInfo->SetCaption(_T("")); SetCalcCaption(_("Task calc")); break; case ANALYSIS_PAGE_TASK: _stprintf(sTmp, _T("%s: %s"), _("Analysis"), _("Task")); wf->SetCaption(sTmp); fs.CaptionTask(sTmp, XCSoarInterface::Calculated()); wInfo->SetCaption(sTmp); SetCalcCaption(_("Task calc")); break; case ANALYSIS_PAGE_OLC: _stprintf(sTmp, _T("%s: %s"), _("Analysis"), _("OnLine Contest")); wf->SetCaption(sTmp); TCHAR timetext1[100]; Units::TimeToText(timetext1, (int)stats.olc.time); TCHAR distance[100]; Units::FormatUserDistance(stats.olc.distance, distance, 100); _stprintf(sTmp, (Layout::landscape ? _T("%s:\r\n %s\r\n%s:\r\n %.1f %s\r\n%s: %s\r\n%s: %d %s\r\n") : _T("%s: %s\r\n%s: %.1f %s\r\n%s: %s\r\n%s: %d %s\r\n")), _("Distance"), distance, _("Score"), (double)stats.olc.score, _("pts"), _("Time"), timetext1, _("Speed"), (int)Units::ToUserUnit(stats.olc.speed, Units::TaskSpeedUnit), Units::GetTaskSpeedName()); wInfo->SetCaption(sTmp); SetCalcCaption(_T("")); break; case ANALYSIS_PAGE_AIRSPACE: _stprintf(sTmp, _T("%s: %s"), _("Analysis"), _("Airspace")); wf->SetCaption(sTmp); wInfo->SetCaption(_T("")); SetCalcCaption(_("Warnings")); break; case ANALYSIS_PAGE_COUNT: assert(false); break; } wGrid->set_visible(page < ANALYSIS_PAGE_COUNT); if (wGrid != NULL) wGrid->invalidate(); }
void GlidePolarTest::TestBallast() { polar.SetBallast(fixed(0.25)); ok1(equals(polar.GetBallastLitres(), 25)); ok1(equals(polar.GetBallast(), 0.25)); polar.SetBallastLitres(fixed(50)); ok1(equals(polar.GetBallastLitres(), 50)); ok1(equals(polar.GetBallast(), 0.5)); ok1(equals(polar.GetTotalMass(), 368)); ok1(equals(polar.GetWingLoading(), 37.551020408)); ok1(polar.HasBallast()); fixed loading_factor = sqrt(polar.GetTotalMass() / polar.reference_mass); ok1(equals(polar.polar.a, polar.ideal_polar.a / loading_factor)); ok1(equals(polar.polar.b, polar.ideal_polar.b)); ok1(equals(polar.polar.c, polar.ideal_polar.c * loading_factor)); ok1(equals(polar.SinkRate(Units::ToSysUnit(fixed(80), Unit::KILOMETER_PER_HOUR)), 0.640739)); ok1(equals(polar.SinkRate(Units::ToSysUnit(fixed(120), Unit::KILOMETER_PER_HOUR)), 0.928976)); ok1(equals(polar.SinkRate(Units::ToSysUnit(fixed(160), Unit::KILOMETER_PER_HOUR)), 1.722908)); ok1(equals(polar.GetVMin(), 21.44464)); ok1(equals(polar.GetVBestLD(), 27.78703)); polar.SetBallast(fixed(0)); ok1(!polar.HasBallast()); }
void GlidePolarTest::TestBasic() { polar.Update(); ok1(equals(polar.polar.a, polar.ideal_polar.a)); ok1(equals(polar.polar.b, polar.ideal_polar.b)); ok1(equals(polar.polar.c, polar.ideal_polar.c)); ok1(equals(polar.SinkRate(Units::ToSysUnit(fixed(80), Unit::KILOMETER_PER_HOUR)), 0.606)); ok1(equals(polar.SinkRate(Units::ToSysUnit(fixed(120), Unit::KILOMETER_PER_HOUR)), 0.99)); ok1(equals(polar.SinkRate(Units::ToSysUnit(fixed(160), Unit::KILOMETER_PER_HOUR)), 1.918)); ok1(equals(polar.GetSMax(), polar.SinkRate(polar.GetVMax()))); ok1(equals(polar.GetVMin(), 19.934640523)); ok1(equals(polar.GetSMin(), polar.SinkRate(polar.GetVMin()))); ok1(equals(polar.GetVTakeoff(), polar.GetVMin() / 2)); ok1(equals(polar.GetVBestLD(), 25.830434162)); ok1(equals(polar.GetSBestLD(), polar.SinkRate(polar.GetVBestLD()))); ok1(equals(polar.GetBestLD(), polar.GetVBestLD() / polar.GetSBestLD())); ok1(equals(polar.GetTotalMass(), 318)); ok1(equals(polar.GetWingLoading(), 32.448979592)); ok1(equals(polar.GetBallast(), 0)); ok1(equals(polar.GetBallastLitres(), 0)); ok1(polar.IsBallastable()); ok1(!polar.HasBallast()); }
void dlgBasicSettingsShowModal() { const ComputerSettings &settings = CommonInterface::GetComputerSettings(); glide_polar = settings.glide_polar_task; wf = LoadDialog(CallBackTable, XCSoarInterface::main_window, _T("IDR_XML_BASICSETTINGS")); if (wf == NULL) return; changed = false; wf->SetTimerNotify(OnTimerNotify); OnTimerNotify(*wf); SetButtons(); SetBallast(); LoadFormProperty(*wf, _T("prpBugs"), (fixed_one - glide_polar.GetBugs()) * 100); LoadFormProperty(*wf, _T("prpQNH"), Units::ToUserPressure(settings.pressure.GetHectoPascal())); WndProperty* wp; wp = (WndProperty*)wf->FindByName(_T("prpQNH")); if (wp) { DataFieldFloat &df = *(DataFieldFloat *)wp->GetDataField(); df.SetMin(Units::ToUserPressure(Units::ToSysUnit(fixed(850), unHectoPascal))); df.SetMax(Units::ToUserPressure(Units::ToSysUnit(fixed(1300), unHectoPascal))); df.SetStep(Units::ToUserPressure(Units::ToSysUnit(fixed_one, unHectoPascal))); df.SetUnits(Units::GetPressureName()); df.SetStep(Units::PressureStep()); df.SetFormat( Units::GetFormatUserPressure()); wp->RefreshDisplay(); } wp = (WndProperty*)wf->FindByName(_T("prpTemperature")); if (wp) { DataFieldFloat &df = *(DataFieldFloat *)wp->GetDataField(); df.SetMin(Units::ToUserTemperature(Units::ToSysUnit(fixed(-50), unGradCelcius))); df.SetMax(Units::ToUserTemperature(Units::ToSysUnit(fixed(60), unGradCelcius))); df.SetUnits(Units::GetTemperatureName()); df.Set(Units::ToUserTemperature(settings.forecast_temperature)); wp->RefreshDisplay(); } if (wf->ShowModal() == mrOK) { ComputerSettings &settings = CommonInterface::SetComputerSettings(); if (changed) { settings.glide_polar_task = glide_polar; if (protected_task_manager != NULL) protected_task_manager->SetGlidePolar(glide_polar); } SaveFormProperty(*wf, _T("prpTemperature"), ugTemperature, settings.forecast_temperature); } delete wf; }
/** * Adjust cruise efficiency of internal glide polar * * @param ce Cruise efficiency */ void set_cruise_efficiency(fixed ce) { m_glide_polar.SetCruiseEfficiency(ce); };
/** * Adjust MacCready value of internal glide polar * * @param mc MacCready value (m/s) */ void set_mc(fixed mc) { m_glide_polar.SetMC(mc); };
void RenderMacCready(Canvas &canvas, const PixelRect rc, const ChartLook &chart_look, const GlidePolar &glide_polar) { ChartRenderer chart(chart_look, canvas, rc); if (!glide_polar.IsValid()) { chart.DrawNoData(); return; } chart.ScaleXFromValue(0); chart.ScaleXFromValue(MAX_MACCREADY); chart.ScaleYFromValue(0); chart.ScaleYFromValue(glide_polar.GetVMax()); chart.DrawXGrid(Units::ToSysVSpeed(1), 1, ChartRenderer::UnitFormat::NUMERIC); chart.DrawYGrid(Units::ToSysSpeed(10), 10, ChartRenderer::UnitFormat::NUMERIC); GlidePolar gp = glide_polar; double m = 0; double m_last; gp.SetMC(m); double v_last = gp.GetVBestLD(); double vav_last = 0; do { m_last = m; m+= MAX_MACCREADY/STEPS_MACCREADY; gp.SetMC(m); const double v = gp.GetVBestLD(); const double vav = gp.GetAverageSpeed(); chart.DrawLine(m_last, v_last, m, v, ChartLook::STYLE_BLACK); chart.DrawLine(m_last, vav_last, m, vav, ChartLook::STYLE_BLUETHINDASH); v_last = v; vav_last = vav; } while (m<MAX_MACCREADY); // draw current MC setting chart.DrawLine(glide_polar.GetMC(), 0, glide_polar.GetMC(), glide_polar.GetVMax(), ChartLook::STYLE_REDTHICKDASH); // draw labels and other overlays gp.SetMC(0.9*MAX_MACCREADY); chart.DrawLabel(_T("Vopt"), 0.9*MAX_MACCREADY, gp.GetVBestLD()); gp.SetMC(0.9*MAX_MACCREADY); chart.DrawLabel(_T("Vave"), 0.9*MAX_MACCREADY, gp.GetAverageSpeed()); chart.DrawYLabel(_T("V"), Units::GetSpeedName()); chart.DrawXLabel(_T("MC"), Units::GetVerticalSpeedName()); RenderGlidePolarInfo(canvas, rc, chart_look, glide_polar); }
bool AbortTask::FillReachable(const AircraftState &state, AlternateVector &approx_waypoints, const GlidePolar &polar, bool only_airfield, bool final_glide, bool safety) { if (IsTaskFull() || approx_waypoints.empty()) return false; const AGeoPoint p_start(state.location, state.altitude); bool found_final_glide = false; reservable_priority_queue<Alternate, AlternateVector, AbortRank> q; q.reserve(32); for (auto v = approx_waypoints.begin(); v != approx_waypoints.end();) { if (only_airfield && !v->waypoint.IsAirport()) { ++v; continue; } UnorderedTaskPoint t(v->waypoint, task_behaviour); GlideResult result = TaskSolution::GlideSolutionRemaining(t, state, polar); /* calculate time_virtual, which is needed by AbortRank */ result.CalcVInvSpeed(polar.GetInvMC()); if (IsReachable(result, final_glide)) { bool intersects = false; const bool is_reachable_final = IsReachable(result, true); if (intersection_test && final_glide && is_reachable_final) intersects = intersection_test->Intersects( AGeoPoint(v->waypoint.location, result.min_height)); if (!intersects) { q.push(Alternate(v->waypoint, result)); // remove it since it's already in the list now approx_waypoints.erase(v); if (is_reachable_final) found_final_glide = true; continue; // skip incrementing v since we just erased it } } ++v; } while (!q.empty() && !IsTaskFull()) { const Alternate top = q.top(); task_points.push_back(AlternateTaskPoint(top.waypoint, task_behaviour, top.solution)); const int i = task_points.size() - 1; if (task_points[i].GetWaypoint().id == active_waypoint) active_task_point = i; q.pop(); } return found_final_glide; }
void RenderVarioHistogram(Canvas &canvas, const PixelRect rc, const ChartLook &chart_look, const FlightStatistics &fs, const GlidePolar &glide_polar) { ChartRenderer chart(chart_look, canvas, rc); const auto acc = std::max(fs.vario_cruise_histogram.GetAccumulator(), fs.vario_circling_histogram.GetAccumulator()); if (!acc) { chart.DrawNoData(); return; } const auto scale = std::max(fs.vario_cruise_histogram.GetMaxY(), fs.vario_circling_histogram.GetMaxY()) * 1.2; chart.ScaleXFromValue(0); chart.ScaleXFromValue(scale); const auto s = -glide_polar.GetSBestLD(); const auto mc = glide_polar.GetMC(); chart.ScaleYFromValue(fs.vario_cruise_histogram.GetMinX()); chart.ScaleYFromValue(fs.vario_cruise_histogram.GetMaxX()); chart.ScaleYFromValue(s); chart.ScaleYFromValue(mc); // draw red area at higher than cruise sink rate, blue area above mc { PixelRect rc_upper = chart.GetChartRect(); rc_upper.bottom = chart.ScreenY(mc); DrawVerticalGradient(canvas, rc_upper, chart_look.color_positive, COLOR_WHITE, COLOR_WHITE); } { PixelRect rc_lower = chart.GetChartRect(); rc_lower.top = chart.ScreenY(s); DrawVerticalGradient(canvas, rc_lower, COLOR_WHITE, chart_look.color_negative, COLOR_WHITE); } canvas.SelectNullPen(); canvas.Select(chart_look.black_brush); chart.DrawFilledLineGraph(fs.vario_circling_histogram, true); canvas.Select(chart_look.blank_brush); chart.DrawFilledLineGraph(fs.vario_cruise_histogram, true); // draw these after shaded regions, so they overlay chart.DrawLineGraph(fs.vario_cruise_histogram, ChartLook::STYLE_GREEN, true); chart.DrawLineGraph(fs.vario_circling_histogram, ChartLook::STYLE_RED, true); // draw current MC setting chart.DrawLine(0, mc, scale, mc, ChartLook::STYLE_REDTHICKDASH); chart.DrawLine(0, s, scale, s, ChartLook::STYLE_BLUETHINDASH); // draw labels and other overlays chart.DrawYGrid(Units::ToSysVSpeed(1), 1, ChartRenderer::UnitFormat::NUMERIC); const double tref = chart.GetXMin()*0.1+chart.GetXMax()*0.9; chart.DrawLabel(_T("MC"), tref, mc); chart.DrawLabel(_T("S cruise"), tref, s); chart.DrawYLabel(_T("w"), Units::GetVerticalSpeedName()); }
bool AbortTask::UpdateSample(const AircraftState &state, const GlidePolar &glide_polar, bool full_update) { assert(state.location.IsValid()); Clear(); unsigned active_waypoint_on_entry; if (is_active) active_waypoint_on_entry = active_waypoint; else { active_waypoint = 0; active_waypoint_on_entry = (unsigned) -1 ; } active_task_point = 0; // default to best result if can't find user-set one if (!glide_polar.IsValid()) /* can't work without a polar */ return false; AlternateList approx_waypoints; approx_waypoints.reserve(128); WaypointVisitorVector wvv(approx_waypoints); waypoints.VisitWithinRange(state.location, GetAbortRange(state, glide_polar), wvv); if (approx_waypoints.empty()) { /** @todo increase range */ return false; } // sort by arrival time // first try with final glide only reachable_landable |= FillReachable(state, approx_waypoints, glide_polar, true, true, true); reachable_landable |= FillReachable(state, approx_waypoints, glide_polar, false, true, true); // inform clients that the landable reachable scan has been performed ClientUpdate(state, true); // now try without final glide constraint and not preferring airports FillReachable(state, approx_waypoints, glide_polar, false, false, false); // inform clients that the landable unreachable scan has been performed ClientUpdate(state, false); if (task_points.size()) { const TaskWaypoint &task_point = task_points[active_task_point].point; active_waypoint = task_point.GetWaypoint().id; if (is_active && (active_waypoint_on_entry != active_waypoint)) { stats.start.Reset(); force_full_update = true; return true; } } return false; // nothing to do }
void SetDegradationFactor(double _degradation_factor) { degradation_factor = _degradation_factor; glide_polar_task.SetBugs(degradation_factor * bugs); }
void SetBugs(double _bugs) { bugs = _bugs; glide_polar_task.SetBugs(degradation_factor * bugs); }
MacCready::MacCready(const GlideSettings &_settings, const GlidePolar &_glide_polar) :settings(_settings), glide_polar(_glide_polar), cruise_efficiency(_glide_polar.GetCruiseEfficiency()) {}