/** * 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); }
/** * 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); }
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; } }
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); } }
/** * 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; }
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; } }
inline void GlideComputer::OnTakeoff() { // reset stats on takeoff air_data_computer.ResetFlight(SetCalculated(), false); // save stats in case we never finish SaveFinish(); }
/** * 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()); }
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()); }
/** * 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); }
/** * 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(); }
/** * 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(); }
/** * 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); }
/** * 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); }
/** * 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; }
/** * 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); }
/** * 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); }
void Expire() { SetCalculated().Expire(Basic().clock); }
/** * 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); } }