Example #1
0
/**
 * Is called by the CalculationThread and processes the received GPS data in Basic()
 */
bool
GlideComputer::ProcessGPS(bool force)
{
  const MoreData &basic = Basic();
  DerivedInfo &calculated = SetCalculated();
  const ComputerSettings &settings = GetComputerSettings();

  const bool last_flying = calculated.flight.flying;

  calculated.date_time_local = basic.date_time_utc + settings.utc_offset;

  calculated.Expire(basic.clock);

  // Process basic information
  air_data_computer.ProcessBasic(Basic(), SetCalculated(),
                                 GetComputerSettings());

  // Process basic task information
  task_computer.ProcessBasicTask(basic, LastBasic(),
                                 calculated,
                                 GetComputerSettings(),
                                 force);
  task_computer.ProcessMoreTask(basic, calculated, GetComputerSettings());

  // Check if everything is okay with the gps time and process it
  if (!air_data_computer.FlightTimes(Basic(), LastBasic(), SetCalculated(),
                                     GetComputerSettings()))
    return false;

  TakeoffLanding(last_flying);

  if (!time_retreated())
    task_computer.ProcessAutoTask(basic, calculated);

  // Process extended information
  air_data_computer.ProcessVertical(Basic(), LastBasic(),
                                    SetCalculated(),
                                    GetComputerSettings());

  if (!time_retreated())
    stats_computer.ProcessClimbEvents(calculated);

  // Calculate the team code
  CalculateOwnTeamCode();

  // Calculate the bearing and range of the teammate
  CalculateTeammateBearingRange();

  vegavoice.Update(basic, Calculated(), GetComputerSettings().voice);

  // update basic trace history
  if (time_advanced())
    calculated.trace_history.append(basic);

  // Update the ConditionMonitors
  ConditionMonitorsUpdate(Basic(), Calculated(), settings);

  return idle_clock.CheckUpdate(500);
}
Example #2
0
/**
 * Process slow calculations. Called by the CalculationThread.
 */
void
GlideComputer::ProcessIdle(bool exhaustive)
{
  // Log GPS fixes for internal usage
  // (snail trail, stats, olc, ...)
  stats_computer.DoLogging(Basic(), Calculated());
  log_computer.Run(Basic(), Calculated(), GetComputerSettings().logger);

  task_computer.ProcessIdle(Basic(), SetCalculated(), GetComputerSettings(),
                            exhaustive);

  if (time_advanced())
    warning_computer.Update(GetComputerSettings(), Basic(), LastBasic(),
                            Calculated(), SetCalculated().airspace_warnings);
}
Example #3
0
void
GlideComputer::CalculateTeammateBearingRange()
{
  const TeamCodeSettings &settings = GetComputerSettings().team_code;
  const NMEAInfo &basic = Basic();
  TeamInfo &teamcode_info = SetCalculated();

  // No reference waypoint for teamcode calculation chosen -> cancel
  if (!DetermineTeamCodeRefLocation())
    return;

  if (settings.team_flarm_tracking) {
    ComputeFlarmTeam(basic.location, team_code_ref_location,
                     basic.flarm.traffic, settings.team_flarm_id,
                     teamcode_info);
  } else if (settings.team_code_valid) {
    teamcode_info.flarm_teammate_code_available = false;

    ComputeTeamCode(basic.location, team_code_ref_location,
                    settings.team_code,
                    teamcode_info);
  } else {
    teamcode_info.teammate_available = false;
    teamcode_info.flarm_teammate_code_available = false;
  }
}
Example #4
0
void
GlideComputer::CalculateWorkingBand()
{
    const MoreData &basic = Basic();
    DerivedInfo &calculated = SetCalculated();
    const ComputerSettings &settings = GetComputerSettings();

    calculated.common_stats.height_min_working = stats_computer.GetFlightStats().GetMinWorkingHeight();
    if (calculated.terrain_base_valid) {
        calculated.common_stats.height_min_working = std::max(calculated.common_stats.height_min_working,
                calculated.GetTerrainBaseFallback()+settings.task.safety_height_arrival);
    }
    calculated.common_stats.height_max_working = std::max(calculated.common_stats.height_min_working,
            stats_computer.GetFlightStats().GetMaxWorkingHeight());

    calculated.common_stats.height_fraction_working = 1; // fallback;

    if (basic.NavAltitudeAvailable()) {
        calculated.common_stats.height_max_working = std::max(calculated.common_stats.height_max_working,
                basic.nav_altitude);
        calculated.common_stats.height_fraction_working =
            calculated.CalculateWorkingFraction(basic.nav_altitude,
                                                settings.task.safety_height_arrival);
    }
}
Example #5
0
/**
 * Searches the FLARM_Traffic array for the TeamMate and updates TeamMate
 * position and TeamCode if found.
 */
void
GlideComputer::FLARM_ScanTraffic()
{
  // If (not FLARM available) cancel
  if (!Basic().flarm.FLARM_Available || !SettingsComputer().TeamFlarmTracking)
    return;

  if (SettingsComputer().TeamCodeRefWaypoint < 0)
    return;

  // Get bearing and distance to the reference waypoint
  const Waypoint *wp =
      way_points.lookup_id(SettingsComputer().TeamCodeRefWaypoint);

  if (!wp)
    return;

  const FLARM_TRAFFIC *traffic =
      Basic().flarm.FindTraffic(SettingsComputer().TeamFlarmIdTarget);

  if (!traffic)
    return;

  // Set Teammate location to FLARM contact location
  SetCalculated().TeammateLocation = traffic->Location;

  // Calculate distance and bearing from teammate to reference waypoint

  Angle bearing = wp->Location.bearing(traffic->Location);
  fixed distance = wp->Location.distance(traffic->Location);

  // Calculate TeamCode and save it in Calculated
  XCSoarInterface::SetSettingsComputer().TeammateCode.Update(bearing, distance);
  XCSoarInterface::SetSettingsComputer().TeammateCodeValid = true;
}
Example #6
0
void
GlideComputer::CalculateTeammateBearingRange()
{
  static bool InTeamSector = false;

  // No reference waypoint for teamcode calculation chosen -> cancel
  if (SettingsComputer().TeamCodeRefWaypoint < 0)
    return;

  // Get bearing and distance to the reference waypoint
  const Waypoint *wp =
      way_points.lookup_id(SettingsComputer().TeamCodeRefWaypoint);

  if (!wp)
    return;

  Angle ownBearing = wp->Location.bearing(Basic().Location);

  // If (TeamCode exists and is valid)
  if (SettingsComputer().TeammateCodeValid) {
    // Calculate bearing and distance to teammate
    SetCalculated().TeammateLocation =
        SettingsComputer().TeammateCode.GetLocation(wp->Location);

    GeoVector team_vector(Basic().Location, Calculated().TeammateLocation);

    // Save bearing and distance to teammate in Calculated
    SetCalculated().TeammateBearing = team_vector.Bearing;
    SetCalculated().TeammateRange = team_vector.Distance;

    // Hysteresis for GlideComputerEvent
    // If (closer than 100m to the teammates last position and "event" not reset)
    if (Calculated().TeammateRange < fixed(100) && InTeamSector == false) {
      InTeamSector = true;
      // Raise GCE_TEAM_POS_REACHED event
      InputEvents::processGlideComputer(GCE_TEAM_POS_REACHED);
    } else if (Calculated().TeammateRange > fixed(300)) {
      // Reset "event" when distance is greater than 300m again
      InTeamSector = false;
    }
  } else {
    SetCalculated().TeammateBearing = Angle::degrees(fixed_zero);
    SetCalculated().TeammateRange = fixed_zero;
  }
}
Example #7
0
inline void
GlideComputer::OnTakeoff()
{
  // reset stats on takeoff
  air_data_computer.ResetFlight(SetCalculated(), false);

  // save stats in case we never finish
  SaveFinish();
}
Example #8
0
/**
 * Resets the GlideComputer data
 * @param full Reset all data?
 */
void
GlideComputer::ResetFlight(const bool full)
{
  GlideComputerBlackboard::ResetFlight(full);
  air_data_computer.ResetFlight(SetCalculated(), GetComputerSettings(), full);
  task_computer.ResetFlight(full);
  stats_computer.ResetFlight(full);

  cu_computer.Reset();
  warning_computer.Reset(Basic(), Calculated());
}
Example #9
0
void
GlideComputer::CalculateVarioScale()
{
    DerivedInfo &calculated = SetCalculated();
    const GlidePolar &glide_polar = GetComputerSettings().polar.glide_polar_task;
    calculated.common_stats.vario_scale_positive =
        std::max(stats_computer.GetFlightStats().GetVarioScalePositive(),
                 glide_polar.GetMC());
    calculated.common_stats.vario_scale_negative =
        std::min(stats_computer.GetFlightStats().GetVarioScaleNegative(),
                 -glide_polar.GetSBestLD());
}
Example #10
0
/**
 * Initializes the GlideComputer
 */
void
GlideComputer::Initialise()
{
  GlideComputerBlackboard::Initialise();
  GlideComputerTask::Initialise();
  ResetFlight(true);

  LoadCalculationsPersist(&SetCalculated());
  DeleteCalculationsPersist();
  // required to allow fail-safe operation
  // if the persistent file is corrupt and causes a crash

  ResetFlight(false);
}
Example #11
0
/**
 * Process slow calculations. Called by the CalculationThread.
 */
void
GlideComputer::ProcessIdle()
{
  PeriodClock clock;
  clock.update();

  // Log GPS fixes for internal usage
  // (snail trail, stats, olc, ...)
  DoLogging();

  GlideComputerAirData::ProcessIdle();
  GlideComputerTask::ProcessIdle();
  SetCalculated().time_process_idle = clock.elapsed();
}
Example #12
0
/**
 * Resets the GlideComputer data
 * @param full Reset all data?
 */
void
GlideComputer::ResetFlight(const bool full)
{
  GlideComputerBlackboard::ResetFlight(full);
  air_data_computer.ResetFlight(SetCalculated(), full);
  task_computer.ResetFlight(full);
  stats_computer.ResetFlight(full);
  log_computer.Reset();
  retrospective.Reset();

  cu_computer.Reset();
  warning_computer.Reset();

  trace_history_time.Reset();
}
Example #13
0
/**
 * Calculates the own TeamCode and saves it to Calculated
 */
inline void
GlideComputer::CalculateOwnTeamCode()
{
  // No reference waypoint for teamcode calculation chosen -> cancel
  if (!DetermineTeamCodeRefLocation())
    return;

  // Only calculate every 10sec otherwise cancel calculation
  if (!last_team_code_update.CheckUpdate(10000))
    return;

  // Get bearing and distance to the reference waypoint
  const GeoVector v = team_code_ref_location.DistanceBearing(Basic().location);

  // Save teamcode to Calculated
  SetCalculated().own_teammate_code.Update(v.bearing, v.distance);
}
Example #14
0
/**
 * Process slow calculations. Called by the CalculationThread.
 */
void
GlideComputer::ProcessIdle(bool exhaustive)
{
  const MoreData &basic = Basic();
  DerivedInfo &calculated = SetCalculated();

  // Log GPS fixes for internal usage
  // (snail trail, stats, olc, ...)
  stats_computer.DoLogging(basic, calculated);
  log_computer.Run(basic, calculated, GetComputerSettings().logger);

  task_computer.ProcessIdle(basic, calculated, GetComputerSettings(),
                            exhaustive);

  warning_computer.Update(GetComputerSettings(), basic,
                          calculated, calculated.airspace_warnings);

  // Calculate summary of flight
  if (basic.location_available)
    retrospective.UpdateSample(basic.location);
}
Example #15
0
/**
 * Is called by the CalculationThread and processes the received GPS data in Basic()
 */
bool
GlideComputer::ProcessGPS()
{
  PeriodClock clock;
  clock.update();

  // Process basic information
  ProcessBasic();

  // Process basic task information
  ProcessBasicTask();
  ProcessMoreTask();

  // Check if everything is okay with the gps time and process it
  if (!FlightTimes()) {
    return false;
  }

  // Process extended information
  ProcessVertical();

  // Calculate the team code
  CalculateOwnTeamCode();

  // Calculate the bearing and range of the teammate
  CalculateTeammateBearingRange();

  // Calculate the bearing and range of the teammate
  // (if teammate is a FLARM target)
  FLARM_ScanTraffic();

  vegavoice.Update(&Basic(), &Calculated(), SettingsComputer());

  // Update the ConditionMonitors
  ConditionMonitorsUpdate(*this);

  SetCalculated().time_process_gps = clock.elapsed();

  return true;
}
Example #16
0
/**
 * Calculates the own TeamCode and saves it to Calculated
 */
void
GlideComputer::CalculateOwnTeamCode()
{
  // No reference waypoint for teamcode calculation chosen -> cancel
  if (SettingsComputer().TeamCodeRefWaypoint < 0)
    return;

  // Only calculate every 10sec otherwise cancel calculation
  if (!last_team_code_update.check_update(10000))
    return;

  // Get bearing and distance to the reference waypoint
  const Waypoint *wp =
      way_points.lookup_id(SettingsComputer().TeamCodeRefWaypoint);

  if (!wp)
    return;

  Angle bearing = wp->Location.bearing(Basic().Location);
  fixed distance = wp->Location.distance(Basic().Location);

  // Save teamcode to Calculated
  SetCalculated().OwnTeamCode.Update(bearing, distance);
}
Example #17
0
/**
 * Is called by the CalculationThread and processes the received GPS data in Basic()
 */
bool
GlideComputer::ProcessGPS(bool force)
{
  const MoreData &basic = Basic();
  DerivedInfo &calculated = SetCalculated();
  const ComputerSettings &settings = GetComputerSettings();

  const bool last_flying = calculated.flight.flying;

  if (basic.time_available) {
    /* use UTC offset to calculate local time */
    const int utc_offset_s = settings.utc_offset.AsSeconds();

    calculated.date_time_local = basic.date_time_utc.IsDatePlausible()
      /* known date: apply UTC offset to BrokenDateTime, which may
         increment/decrement date */
      ? basic.date_time_utc + utc_offset_s
      /* unknown date: apply UTC offset only to BrokenTime, leave the
         BrokenDate part invalid as it was */
      : BrokenDateTime(BrokenDate::Invalid(),
                       ((const BrokenTime &)basic.date_time_utc) + utc_offset_s);
  } else
    calculated.date_time_local = BrokenDateTime::Invalid();

  calculated.Expire(basic.clock);

  // Process basic information
  air_data_computer.ProcessBasic(Basic(), SetCalculated(),
                                 GetComputerSettings());

  // Process basic task information
  const bool last_finished = calculated.ordered_task_stats.task_finished;

  task_computer.ProcessBasicTask(basic,
                                 calculated,
                                 GetComputerSettings(),
                                 force);
  task_computer.ProcessMoreTask(basic, calculated, GetComputerSettings());

  if (!last_finished && calculated.ordered_task_stats.task_finished)
    OnFinishTask();

  // Check if everything is okay with the gps time and process it
  air_data_computer.FlightTimes(Basic(), SetCalculated(),
                                GetComputerSettings());

  TakeoffLanding(last_flying);

  task_computer.ProcessAutoTask(basic, calculated);

  // Process extended information
  air_data_computer.ProcessVertical(Basic(),
                                    SetCalculated(),
                                    GetComputerSettings());

  stats_computer.ProcessClimbEvents(calculated);

  // Calculate the team code
  CalculateOwnTeamCode();

  // Calculate the bearing and range of the teammate
  CalculateTeammateBearingRange();

  vegavoice.Update(basic, Calculated(), GetComputerSettings().voice);

  // update basic trace history
  if (basic.time_available) {
    const auto dt = trace_history_time.Update(basic.time, 0.5, 30);
    if (dt > 0)
      calculated.trace_history.append(basic);
    else if (dt < 0)
      /* time warp */
      calculated.trace_history.clear();
  }

  // Update the ConditionMonitors
  ConditionMonitorsUpdate(Basic(), Calculated(), settings);

  return idle_clock.CheckUpdate(500);
}
Example #18
0
 void Expire() {
   SetCalculated().Expire(Basic().clock);
 }
Example #19
0
/**
 * SearchBestAlternate() beta
 * based on SortLandableWaypoints and extended
 * @author Paolo Ventafridda
 */
void
GlideComputerTask::SearchBestAlternate()
{
  int SortedLandableIndex[MAXBEST];
  double SortedArrivalAltitude[MAXBEST];
  int SortedApproxDistance[MAXBEST*2];
  int SortedApproxIndex[MAXBEST*2];
  int i, k, l;
  double arrival_altitude;
  int active_bestalternate_on_entry=-1;
  int bestalternate=-1;

  /*
   * VENTA3 search in range of optimum gliding capability
   * and in any case within an acceptable distance, say 100km.
   * Anything else is not considered, since we want a safe landing not a long glide.
   * Preferred waypoints and home are nevertheless checked in any case later.
   * Notice that if you are over 100km away from the nearest non-preferred landing point you can't
   * expect a computer to be helpful in case of troubles.
   *
   * ApproxDistance is in km, very approximate
   */

  double searchrange=(Basic().Altitude-
		      SettingsComputer().SafetyAltitudeArrival)
    *GlidePolar::bestld /1000;
  if (searchrange <= 0)
    searchrange=2; // lock to home airport at once
  if (searchrange > ALTERNATE_MAXRANGE)
    searchrange=ALTERNATE_MAXRANGE;

  active_bestalternate_on_entry = Calculated().BestAlternate;

  // Do preliminary fast search
  POINT sc_aircraft;
  LatLon2Flat(Basic().Location, sc_aircraft);

  // Clear search lists
  for (i=0; i<MAXBEST*2; i++) {
    SortedApproxIndex[i]= -1;
    SortedApproxDistance[i] = 0;
  }

  for (i = 0; way_points.verify_index(i); i++) {
    const WAYPOINT &way_point = way_points.get(i);

    if (!(((way_point.Flags & AIRPORT) == AIRPORT) ||
          ((way_point.Flags & LANDPOINT) == LANDPOINT))) {
      continue; // ignore non-landable fields
    }

    int approx_distance =
      CalculateWaypointApproxDistance(sc_aircraft, way_point);

    // Size a reasonable distance, wide enough VENTA3
    if ( approx_distance > searchrange ) continue;

    // see if this fits into slot
    for (k=0; k< MAXBEST*2; k++)  {

      if (((approx_distance < SortedApproxDistance[k])
           // wp is closer than this one
	   || (SortedApproxIndex[k]== -1))   // or this one isn't filled
          && (SortedApproxIndex[k]!= i))    // and not replacing with same
        {
	  // ok, got new biggest, put it into the slot.
          for (l=MAXBEST*2-1; l>k; l--) {
            if (l>0) {
	      SortedApproxDistance[l] = SortedApproxDistance[l-1];
	      SortedApproxIndex[l] = SortedApproxIndex[l-1];
            }
          }

          SortedApproxDistance[k] = approx_distance;
          SortedApproxIndex[k] = i;
          k=MAXBEST*2;
        }
    } // for k
  } // for i

  // Now do detailed search
  for (i=0; i<MAXBEST; i++) {
    SortedLandableIndex[i]= -1;
    SortedArrivalAltitude[i] = 0;
  }

  bool found_reachable_airport = false;

  for (int scan_airports_slot=0;
       scan_airports_slot<2;
       scan_airports_slot++) {

    if (found_reachable_airport ) {
      continue; // don't bother filling the rest of the list
    }

    for (i=0; i<MAXBEST*2; i++) {
      if (SortedApproxIndex[i]<0) { // ignore invalid points
        continue;
      }

      const WAYPOINT &way_point = way_points.get(SortedApproxIndex[i]);
      WPCALC &wpcalc = way_points.set_calc(SortedApproxIndex[i]);

      if ((scan_airports_slot==0) &&
	  ((way_point.Flags & AIRPORT) != AIRPORT)) {
        // we are in the first scan, looking for airports only
        continue;
      }

      arrival_altitude = CalculateWaypointArrivalAltitude(way_point, wpcalc);

      wpcalc.AltArrival = arrival_altitude;
      // This is holding the real arrival value

      /*
       * We can't use degraded polar here, but we can't accept an
       * arrival 1m over safety.  That is 2m away from being
       * unreachable! So we higher this value to 100m.
       */
      arrival_altitude -= ALTERNATE_OVERSAFETY;

      if (scan_airports_slot==0) {
        if (arrival_altitude<0) {
          // in first scan, this airport is unreachable, so ignore it.
          continue;
        } else
          // this airport is reachable
          found_reachable_airport = true;
      }

      // see if this fits into slot
      for (k=0; k< MAXBEST; k++) {
        if (((arrival_altitude > SortedArrivalAltitude[k])
             // closer than this one
             ||(SortedLandableIndex[k]== -1))
            // or this one isn't filled
	    &&(SortedLandableIndex[k]!= i))  // and not replacing
	  // with same
          {
            double wp_distance, wp_bearing;
            DistanceBearing(Basic().Location, way_point.Location,
                            &wp_distance, &wp_bearing);

            wpcalc.Distance = wp_distance;
            wpcalc.Bearing = wp_bearing;

            bool out_of_range;

            terrain.Lock();
            double distance_soarable =
              FinalGlideThroughTerrain(wp_bearing,
                                       Basic(), Calculated(),
				       SettingsComputer(),
                                       terrain,
                                       NULL,
                                       wp_distance,
                                       &out_of_range, NULL);
            terrain.Unlock();

            if ((distance_soarable>= wp_distance)||(arrival_altitude<0)) {
              // only put this in the index if it is reachable
              // and doesn't go through terrain, OR, if it is unreachable
              // it doesn't matter if it goes through terrain because
              // pilot has to climb first anyway

              // ok, got new biggest, put it into the slot.
              for (l=MAXBEST-1; l>k; l--) {
                if (l>0) {
                  SortedArrivalAltitude[l] = SortedArrivalAltitude[l-1];
                  SortedLandableIndex[l] = SortedLandableIndex[l-1];
                }
              }

              SortedArrivalAltitude[k] = arrival_altitude;
              SortedLandableIndex[k] = SortedApproxIndex[i];
              k=MAXBEST;
            }
          } // if (((arrival_altitude > SortedArrivalAltitude[k]) ...
      } // for (k=0; k< MAXBEST; k++) {
    }
  }

  // extended part by Paolo

  bestalternate=-1;  // reset the good choice
  double safecalc = Calculated().NavAltitude -
    SettingsComputer().SafetyAltitudeArrival;
  static double grpolar = GlidePolar::bestld *SAFELD_FACTOR;
  int curwp, curbestairport=-1, curbestoutlanding=-1;
  double curgr=0, curbestgr=INVALID_GR;
  if ( safecalc <= 0 ) {
    /*
     * We're under the absolute safety altitude at MSL, can't be any better elsewhere!
     * Use the closer, hopefully you are landing on your airport
     */
  } else
    for (k=0;  k< MAXBEST; k++) {
      curwp = SortedLandableIndex[k];

      if ( !way_points.verify_index(curwp) ) {
	continue;
	// break;  // that list is unsorted !
      }

      const WAYPOINT &way_point = way_points.get(curwp);
      WPCALC &wpcalc = way_points.set_calc(SortedApproxIndex[i]);

      // At the first unsafe landing, stop searching down the list and
      // use the best found or the first
      double grsafe = safecalc - way_point.Altitude;
      if ( grsafe <= 0 ) {
	/*
	 * We're under the safety altitude for this waypoint.
	 */
	break;
	//continue;
      }

      wpcalc.GR = wpcalc.Distance / grsafe;
      grsafe = wpcalc.GR;
      curgr = wpcalc.GR;

      if ( grsafe > grpolar ) {
	/*
	 * Over degraded polar GR for this waypoint
	 */
	continue;
	// break;
      }

      // Anything now is within degraded glide ratio, so if our homewaypoint is safely reachable then
      // attempt to lock it even if we already have a valid best, even if it is preferred and even
      // if it has a better GR

      if ( (SettingsComputer().HomeWaypoint >= 0)
	   && (curwp == SettingsComputer().HomeWaypoint) ) {
	bestalternate = curwp;
	break;
      }

      // If we already found a preferred, stop searching for anything but home

      if (bestalternate >= 0 && way_points.get_calc(bestalternate).Preferred) {
	continue;
      }

      // VENTA5 TODO: extend search on other preferred, choosing the closer one

      // Preferred list has priority, first found is taken (could be smarted)

      if (wpcalc.Preferred) {
	bestalternate=curwp;
	continue;
      }

      // else we remember the best landable GR found so far. We shall use this in case
      // at the end of the search no home and no preferred were found.

      if ( curgr < curbestgr ) {
        if ((way_point.Flags & AIRPORT) == AIRPORT) {
	  curbestairport=curwp;
	  curbestgr=curgr; // ONLY FOR AIRPORT! NOT FOR OUTLANDINGS!!
	}
	else {
	  curbestoutlanding=curwp;
	}
      }

      continue;

    }

  if ( bestalternate <0 ) {

    if ( curbestairport >= 0 ) {
      bestalternate=curbestairport;
    } else {
      if ( curbestoutlanding >= 0 ) {
	bestalternate=curbestoutlanding;
      } else {
	/*
	 * Here we are in troubles, nothing really reachable, but we
	 * might still be lucky to be within the "yellow" glide
	 * path. In any case we select the best arrival altitude place
	 * available, even if it is "red".
	 */
	if ( way_points.verify_index(SortedLandableIndex[0]) ) {
	  bestalternate=SortedLandableIndex[0];
	} else {
	  /*
	   * Else the Landable list is EMPTY, although we might be
	   * near to a landable point but the terrain obstacles look
	   * too high (or the DEM resolution is not high enough to
	   * show a passage).
	   *
	   * Still the old BestAlternate could simply be out of range,
	   * but reachable...  These values have certainly just been
	   * calculated by DoAlternates() , so they are usable.
	   */
	  // Attempt to use the old best, but check there's one.. it
	  // might be empty for the first run
	  if ( way_points.verify_index(active_bestalternate_on_entry) )
	    {
	      bestalternate=active_bestalternate_on_entry;
              if (way_points.get_calc(bestalternate).AltArrival < 0) {
		// Pick up the closest!
		if ( way_points.verify_index( SortedApproxIndex[0]) ) {
		  bestalternate=SortedApproxIndex[0];
		} else {
		  // CRITIC POINT
		  // Otherwise .. nothing, either keep the old best or
		  // set it empty
		  // Put here "PULL-UP! PULL-UP! Boeing
		  // cockpit voice sound and possibly shake the stick.
		}
	      } else
		{
		  // MapWindow2 is checking for reachables separately,
		  // se let's see if this closest is reachable
		  if ( way_points.verify_index( SortedApproxIndex[0] )) {
                    if (way_points.get_calc(SortedApproxIndex[0]).Reachable) {
		      bestalternate = SortedApproxIndex[0];
		    } else
		      {
		      }
		  } else
		    {
		    }
		}
	    } else
	    {
	      // CRITIC POINT
	    }
	}
	/*
	 * Don't make any sound at low altitudes, pilot is either taking off
	 * or landing, or searching for an immediate outlanding.  Do not disturb.
	 * If safetyaltitude is 300m, then below 500m be quiet.
	 * If there was no active alternate on entry, and nothing was found, then we
	 * better be quiet since probably the user had already been alerted previously
	 * and now he is low..
	 */
	if ( bestalternate >0 &&
             ((safecalc - way_points.get(bestalternate).Altitude) > ALTERNATE_QUIETMARGIN)) {
          if (way_points.get_calc(bestalternate).AltArrivalAGL <100 )
	    AlertBestAlternate(2);
	  //	if (EnableSoundModes) PlayResource(TEXT("IDR_WAV_RED"));
	}
      }
    }
  }

  /*
   * If still invalid, it should mean we are just taking off
   * in this case no problems, we set the very first bestalternate of the day as the home
   * trusting the user to be home really!
   */

  if ( bestalternate < 0 ) {
    if ( SettingsComputer().HomeWaypoint >= 0 ) {
      bestalternate=SettingsComputer().HomeWaypoint;
    }
  } else {
    // If still invalid, i.e. not -1, then there's a big problem
    if ( !way_points.verify_index(bestalternate) ) {
      AlertBestAlternate(2);
      Message::AddMessage(_T("Error, invalid best alternate!"));
      // todo: immediate disable function
    }
  }

  if (active_bestalternate_on_entry != bestalternate) {
    SetCalculated().BestAlternate = bestalternate;
    if ( bestalternate >0 &&
         ((safecalc - way_points.get(bestalternate).Altitude) > ALTERNATE_QUIETMARGIN))
      AlertBestAlternate(1);
  }
}