Beispiel #1
0
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());
}
 /**
  * Specialisation based on simplified theoretical MC cross-country
  * speeds.  Assumes cruise at best LD (ignoring wind) for current MC
  * setting, climb rate at MC setting, with direct descent possible
  * at sink rate of cruise.
  */
 explicit AirspaceAircraftPerformance(const GlidePolar &polar)
   :vertical_tolerance(0),
    cruise_speed(polar.GetVBestLD()), cruise_descent(polar.GetSBestLD()),
    descent_rate(polar.GetSMax()),
    climb_rate(polar.GetMC()),
    max_speed(polar.GetVMax()) {
   assert(polar.IsValid());
 }
 /**
  * Specialisation of AirspaceAircraftPerformance for tasks where
  * part of the path is in cruise, part in final glide.  This is
  * intended to be used temporarily only.
  *
  * This simplifies the path by assuming flight is constant altitude
  * or descent to the task point elevation.
  */
 AirspaceAircraftPerformance(const GlidePolar &polar,
                             const GlideResult &solution)
   :vertical_tolerance(0.001),
    cruise_speed(positive(solution.time_elapsed)
                 ? solution.vector.distance / solution.time_elapsed
                 : fixed(1)),
    cruise_descent(positive(solution.time_elapsed)
                   ? (positive(solution.height_climb)
                      ? -solution.height_climb
                      : solution.height_glide) / solution.time_elapsed
                   : fixed(0)),
    descent_rate(polar.GetSBestLD()),
    climb_rate(positive(solution.time_elapsed) &&
               positive(solution.height_climb)
               ? polar.GetMC()
               : fixed(0)),
    max_speed(cruise_speed) {
   assert(polar.IsValid());
   assert(solution.IsOk());
   assert(solution.IsAchievable());
 }
Beispiel #4
0
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
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());

}