Example #1
0
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();
}
Example #3
0
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);
}
Example #7
0
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);
  }
}
Example #9
0
  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);
    }
  }
Example #10
0
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);
}
Example #11
0
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;
  }
}
Example #14
0
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));
  }
}