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(); }
/** * Logs GPS fixes for stats * @return True if valid fix (fix distance <= 200m), False otherwise */ bool StatsComputer::DoLogging(const MoreData &basic, const DerivedInfo &calculated) { /// @todo consider putting this sanity check inside Parser bool location_jump = basic.location_available && last_location.IsValid() && basic.location.DistanceS(last_location) > 200; last_location = basic.location_available ? basic.location : GeoPoint::Invalid(); if (location_jump || !basic.location_available) // prevent bad fixes from being logged or added to OLC store return false; if (calculated.flight.flying && stats_clock.CheckAdvance(basic.time, PERIOD)) { flightstats.AddAltitudeTerrain(calculated.flight.flight_time, calculated.terrain_altitude); if (basic.NavAltitudeAvailable()) flightstats.AddAltitude(calculated.flight.flight_time, basic.nav_altitude); if (calculated.task_stats.IsPirkerSpeedAvailable()) flightstats.AddTaskSpeed(calculated.flight.flight_time, calculated.task_stats.get_pirker_speed()); } return true; }
void GlideComputerAirData::Average30s(const MoreData &basic, const NMEAInfo &last_basic, DerivedInfo &calculated, const DerivedInfo &last_calculated) { const bool time_advanced = basic.HasTimeAdvancedSince(last_basic); if (!time_advanced || calculated.circling != last_calculated.circling) { vario_30s_filter.reset(); netto_30s_filter.reset(); calculated.average = basic.brutto_vario; calculated.netto_average = basic.netto_vario; } if (!time_advanced) return; const unsigned Elapsed(basic.time - last_basic.time); if (Elapsed == 0) return; for (unsigned i = 0; i < Elapsed; ++i) { vario_30s_filter.update(basic.brutto_vario); netto_30s_filter.update(basic.netto_vario); } calculated.average = vario_30s_filter.average(); calculated.netto_average = netto_30s_filter.average(); }
/** * Reads the current terrain height */ void GlideComputerAirData::TerrainHeight(const MoreData &basic, TerrainInfo &calculated) { if (!basic.location_available || terrain == NULL) { calculated.terrain_valid = false; calculated.terrain_altitude = fixed_zero; calculated.altitude_agl_valid = false; calculated.altitude_agl = fixed_zero; return; } short Alt = terrain->GetTerrainHeight(basic.location); if (RasterBuffer::IsSpecial(Alt)) { if (RasterBuffer::IsWater(Alt)) /* assume water is 0m MSL; that's the best guess */ Alt = 0; else { calculated.terrain_valid = false; calculated.terrain_altitude = fixed_zero; calculated.altitude_agl_valid = false; calculated.altitude_agl = fixed_zero; return; } } calculated.terrain_valid = true; calculated.terrain_altitude = fixed(Alt); if (basic.NavAltitudeAvailable()) { calculated.altitude_agl = basic.nav_altitude - calculated.terrain_altitude; calculated.altitude_agl_valid = true; } else calculated.altitude_agl_valid = false; }
gcc_pure NearestAirspace NearestAirspace::FindHorizontal(const MoreData &basic, const ProtectedAirspaceWarningManager &airspace_warnings, const Airspaces &airspace_database) { if (!basic.location_available) /* can't check for airspaces without a GPS fix */ return NearestAirspace(); /* find the nearest airspace */ //consider only active airspaces const auto outside_and_active = MakeAndPredicate(ActiveAirspacePredicate(&airspace_warnings), OutsideAirspacePredicate(AGeoPoint(basic.location, 0))); //if altitude is available, filter airspaces in same height as airplane if (basic.NavAltitudeAvailable()) { /* check altitude; hard-coded margin of 50m (for now) */ const auto outside_and_active_and_height = MakeAndPredicate(outside_and_active, AirspacePredicateHeightRange(basic.nav_altitude - 50, basic.nav_altitude + 50)); const auto predicate = WrapAirspacePredicate(outside_and_active_and_height); return ::FindHorizontal(basic.location, airspace_database, predicate); } else { /* only filter outside and active */ const auto predicate = WrapAirspacePredicate(outside_and_active); return ::FindHorizontal(basic.location, airspace_database, predicate); } }
void BasicComputer::Compute(MoreData &data, const MoreData &last, const MoreData &last_gps, const DerivedInfo &calculated) { ComputeTrack(data, last_gps); if (data.HasTimeAdvancedSince(last_gps)) { ComputeGroundSpeed(data, last_gps); ComputeAirspeed(data, calculated); } #ifndef NDEBUG // For testing without gps. // When CPU load is low enough it can be done for every sample. else if (data.dyn_pressure_available) ComputeAirspeed(data, calculated); #endif ComputeHeading(data.attitude, data, calculated); ComputeEnergyHeight(data); ComputeGPSVario(data, last, last_gps); ComputeBruttoVario(data); ComputeNettoVario(data, calculated); ComputeDynamics(data, calculated); }
void TraceComputer::Update(const ComputerSettings &settings_computer, const MoreData &basic, const DerivedInfo &calculated) { /* time warps are handled by the Trace class */ if (!basic.time_available || !basic.location_available || !basic.NavAltitudeAvailable() || !calculated.flight.flying) return; // either olc or basic trace requires trace_full if (settings_computer.contest.enable || settings_computer.task.enable_trace) { const TracePoint point(basic); mutex.Lock(); full.push_back(point); mutex.Unlock(); // only olc requires trace_sprint if (settings_computer.contest.enable) sprint.push_back(point); } }
HorizontalAirspaceCondition(const MoreData &basic, const DerivedInfo &calculated) :location(basic.location), altitude_available(basic.NavAltitudeAvailable()) { if (altitude_available) { altitude.altitude = basic.nav_altitude; altitude.altitude_agl = calculated.altitude_agl; } }
void WarningComputer::Update(const ComputerSettings &settings_computer, const MoreData &basic, const MoreData &last_basic, const DerivedInfo &calculated, AirspaceWarningsInfo &result) { if (!basic.HasTimeAdvancedSince(last_basic) || !clock.check_advance(basic.time)) return; airspaces.set_flight_levels(settings_computer.pressure); AirspaceActivity day(calculated.date_time_local.day_of_week); airspaces.set_activity(day); if (!settings_computer.airspace.enable_warnings || !basic.location_available || !basic.NavAltitudeAvailable()) { if (initialised) { initialised = false; protected_manager.clear(); } return; } const AircraftState as = ToAircraftState(basic, calculated); ProtectedAirspaceWarningManager::ExclusiveLease lease(protected_manager); if (!initialised) { initialised = true; lease->Reset(as); } if (lease->Update(as, settings_computer.glide_polar_task, calculated.task_stats, calculated.circling, uround(basic.time - last_basic.time))) result.latest.Update(basic.clock); }
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 GlideRatioComputer::Compute(const MoreData &basic, const DerivedInfo &calculated, VarioInfo &vario_info, const ComputerSettings &settings) { if (!basic.NavAltitudeAvailable()) { Reset(); vario_info.gr = INVALID_GR; vario_info.average_gr = 0; return; } if (!last_location_available) { /* need two consecutive valid locations; if the previous one wasn't valid, skip this iteration and try the next one */ Reset(); vario_info.gr = INVALID_GR; vario_info.average_gr = 0; last_location = basic.location; last_location_available = basic.location_available; last_altitude = basic.nav_altitude; return; } if (!basic.location_available.Modified(last_location_available)) return; auto DistanceFlown = basic.location.DistanceS(last_location); // Glide ratio over ground vario_info.gr = UpdateGR(vario_info.gr, DistanceFlown, last_altitude - basic.nav_altitude, 0.1); if (calculated.flight.flying && !calculated.circling) { if (!gr_calculator_initialised) { gr_calculator_initialised = true; gr_calculator.Initialize(settings); } gr_calculator.Add((int)DistanceFlown, (int)basic.nav_altitude); vario_info.average_gr = gr_calculator.Calculate(); } else gr_calculator_initialised = false; last_location = basic.location; last_location_available = basic.location_available; last_altitude = basic.nav_altitude; }
void TrackingGlue::OnTimer(const MoreData &basic, const DerivedInfo &calculated) { #ifdef HAVE_SKYLINES_TRACKING skylines.Tick(basic); #endif #ifdef HAVE_LIVETRACK24 if (!settings.livetrack24.enabled) /* disabled by configuration */ /* note that we are allowed to read "settings" without locking the mutex, because the background thread never writes to this attribute */ return; if (!basic.time_available || !basic.gps.real || !basic.location_available) /* can't track without a valid GPS fix */ return; if (!clock.CheckUpdate(settings.interval * 1000)) /* later */ return; ScopeLock protect(mutex); if (IsBusy()) /* still running, skip this submission */ return; date_time = basic.date_time_utc; if (!date_time.IsDatePlausible()) /* use "today" if the GPS didn't provide a date */ (BrokenDate &)date_time = BrokenDate::TodayUTC(); location = basic.location; /* XXX use nav_altitude? */ altitude = basic.NavAltitudeAvailable() && basic.nav_altitude > 0 ? (unsigned)basic.nav_altitude : 0u; ground_speed = basic.ground_speed_available ? (unsigned)Units::ToUserUnit(basic.ground_speed, Unit::KILOMETER_PER_HOUR) : 0u; track = basic.track_available ? basic.track : Angle::Zero(); last_flying = flying; flying = calculated.flight.flying; Trigger(); #endif }
void GlideComputerAirData::GR(const MoreData &basic, const MoreData &last_basic, const DerivedInfo &calculated, VarioInfo &vario_info) { if (!basic.NavAltitudeAvailable() || !last_basic.NavAltitudeAvailable()) { vario_info.ld_vario = INVALID_GR; vario_info.gr = INVALID_GR; return; } if (basic.HasTimeRetreatedSince(last_basic)) { vario_info.ld_vario = INVALID_GR; vario_info.gr = INVALID_GR; } const bool time_advanced = basic.HasTimeAdvancedSince(last_basic); if (time_advanced) { fixed DistanceFlown = basic.location.Distance(last_basic.location); // Glide ratio over ground vario_info.gr = UpdateGR(vario_info.gr, DistanceFlown, last_basic.nav_altitude - basic.nav_altitude, fixed(0.1)); if (calculated.flight.flying && !calculated.circling) gr_calculator.Add((int)DistanceFlown, (int)basic.nav_altitude); } // Lift / drag instantaneous from vario, updated every reading.. if (basic.total_energy_vario_available && basic.airspeed_available && calculated.flight.flying) { vario_info.ld_vario = UpdateGR(vario_info.ld_vario, basic.indicated_airspeed, -basic.total_energy_vario, fixed(0.3)); } else { vario_info.ld_vario = INVALID_GR; } }
void Update(const MoreData &basic, const FlyingState &state, FlightTimeResult &result) { if (!basic.time_available || !basic.date_time_utc.IsDatePlausible()) return; if (state.flying && !result.takeoff_time.IsPlausible()) { result.takeoff_time = basic.GetDateTimeAt(state.takeoff_time); result.takeoff_location = state.takeoff_location; } if (!state.flying && result.takeoff_time.IsPlausible() && !result.landing_time.IsPlausible()) { result.landing_time = basic.GetDateTimeAt(state.landing_time); result.landing_location = state.landing_location; } if (!negative(state.release_time) && !result.release_time.IsPlausible()) { result.release_time = basic.GetDateTimeAt(state.release_time); result.release_location = state.release_location; } }
void WarningComputer::Update(const ComputerSettings &settings_computer, const MoreData &basic, const DerivedInfo &calculated, AirspaceWarningsInfo &result) { if (!basic.time_available) return; const fixed dt = delta_time.Update(basic.time, fixed(1), fixed(20)); if (negative(dt)) /* time warp */ Reset(); if (!positive(dt)) return; airspaces.SetFlightLevels(settings_computer.pressure); AirspaceActivity day(calculated.date_time_local.day_of_week); airspaces.SetActivity(day); if (!settings_computer.airspace.enable_warnings || !basic.location_available || !basic.NavAltitudeAvailable()) { if (initialised) { initialised = false; protected_manager.Clear(); } return; } const AircraftState as = ToAircraftState(basic, calculated); ProtectedAirspaceWarningManager::ExclusiveLease lease(protected_manager); lease->SetConfig(settings_computer.airspace.warnings); if (!initialised) { initialised = true; lease->Reset(as); } if (lease->Update(as, settings_computer.polar.glide_polar_task, calculated.task_stats, calculated.circling, uround(dt))) result.latest.Update(basic.clock); }
void GlideComputerAirData::MaxHeightGain(const MoreData &basic, DerivedInfo &calculated) { if (!basic.NavAltitudeAvailable() || !calculated.flight.flying) return; if (positive(calculated.min_altitude)) { fixed height_gain = basic.nav_altitude - calculated.min_altitude; calculated.max_height_gain = max(height_gain, calculated.max_height_gain); } else { calculated.min_altitude = basic.nav_altitude; } calculated.min_altitude = min(basic.nav_altitude, calculated.min_altitude); }
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); } }
void CirclingComputer::MaxHeightGain(const MoreData &basic, const FlyingState &flight, CirclingInfo &circling_info) { if (!basic.NavAltitudeAvailable() || !flight.flying) return; if (min_altitude > 0) { auto height_gain = basic.nav_altitude - min_altitude; circling_info.max_height_gain = std::max(height_gain, circling_info.max_height_gain); } else { min_altitude = basic.nav_altitude; } min_altitude = std::min(basic.nav_altitude, min_altitude); }
void GlideComputerAirData::CruiseLD(const MoreData &basic, DerivedInfo &calculated) { if (!calculated.circling && basic.NavAltitudeAvailable()) { if (negative(calculated.cruise_start_time)) { calculated.cruise_start_location = basic.location; calculated.cruise_start_altitude = basic.nav_altitude; calculated.cruise_start_time = basic.time; } else { fixed DistanceFlown = basic.location.Distance(calculated.cruise_start_location); calculated.cruise_ld = UpdateLD(calculated.cruise_ld, DistanceFlown, calculated.cruise_start_altitude - basic.nav_altitude, fixed_half); } } }
/** * Retrieves GPS data from the DeviceBlackboard * @param nmea_info New GPS data */ void GlideComputerBlackboard::ReadBlackboard(const MoreData &nmea_info) { _time_retreated = nmea_info.HasTimeRetreatedSince(gps_info); if (_time_retreated) { // backwards in time, so reset last last_gps_info = nmea_info; last_calculated_info = calculated_info; _time_retreated = true; } else if (nmea_info.time > gps_info.time) { // forwards in time, so save state last_gps_info = gps_info; last_calculated_info = calculated_info; } gps_info = nmea_info; // if time hasn't advanced, don't copy last calculated }
void CalculateReachabilityDirect(const MoreData &basic, const SpeedVector &wind, const MacCready &mac_cready, const TaskBehaviour &task_behaviour) { assert(basic.location_available); assert(basic.NavAltitudeAvailable()); const auto elevation = waypoint->elevation + task_behaviour.safety_height_arrival; const GlideState state(GeoVector(basic.location, waypoint->location), elevation, basic.nav_altitude, wind); const GlideResult result = mac_cready.SolveStraight(state); if (!result.IsOk()) return; reach.direct = result.pure_glide_altitude_difference; if (result.pure_glide_altitude_difference > 0) reachable = WaypointRenderer::ReachableTerrain; }
/** * Logs GPS fixes for stats * @return True if valid fix (fix distance <= 200m), False otherwise */ bool GlideComputerStats::DoLogging(const MoreData &basic, const NMEAInfo &last_basic, const DerivedInfo &calculated, const LoggerSettings &settings_logger) { /// @todo consider putting this sanity check inside Parser if (basic.location.Distance(last_basic.location) > fixed(200)) // prevent bad fixes from being logged or added to OLC store return false; // log points more often in circling mode if (calculated.circling) log_clock.set_dt(fixed(settings_logger.logger_time_step_circling)); else log_clock.set_dt(fixed(settings_logger.logger_time_step_cruise)); if (fast_log_num) { log_clock.set_dt(fixed_one); fast_log_num--; } if (log_clock.check_advance(basic.time) && logger != NULL) logger->LogPoint(basic); if (calculated.flight.flying && stats_clock.check_advance(basic.time)) { flightstats.AddAltitudeTerrain(calculated.flight.flight_time, calculated.terrain_altitude); if (basic.NavAltitudeAvailable()) flightstats.AddAltitude(calculated.flight.flight_time, basic.nav_altitude); if (calculated.task_stats.IsPirkerSpeedAvailable()) flightstats.AddTaskSpeed(calculated.flight.flight_time, calculated.task_stats.get_pirker_speed()); } return true; }
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; } }
gcc_pure NearestAirspace NearestAirspace::FindHorizontal(const MoreData &basic, const ProtectedAirspaceWarningManager &airspace_warnings, const Airspaces &airspace_database) { if (!basic.location_available) /* can't check for airspaces without a GPS fix */ return NearestAirspace(); /* find the nearest airspace */ //consider only active airspaces const WrapAirspacePredicate<ActiveAirspacePredicate> active_predicate(&airspace_warnings); const WrapAirspacePredicate<OutsideAirspacePredicate> outside_predicate(AGeoPoint(basic.location, RoughAltitude(0))); const AndAirspacePredicate outside_and_active_predicate(active_predicate, outside_predicate); const Airspace *airspace; //if altitude is available, filter airspaces in same height as airplane if (basic.NavAltitudeAvailable()) { /* check altitude; hard-coded margin of 50m (for now) */ WrapAirspacePredicate<AirspacePredicateHeightRange> height_range_predicate(RoughAltitude(basic.nav_altitude-fixed(50)), RoughAltitude(basic.nav_altitude+fixed(50))); AndAirspacePredicate and_predicate(outside_and_active_predicate, height_range_predicate); airspace = airspace_database.FindNearest(basic.location, and_predicate); } else //only filter outside and active airspace = airspace_database.FindNearest(basic.location, outside_and_active_predicate); if (airspace == nullptr) return NearestAirspace(); const AbstractAirspace &as = airspace->GetAirspace(); /* calculate distance to the nearest point */ const GeoPoint closest = as.ClosestPoint(basic.location, airspace_database.GetProjection()); return NearestAirspace(as, basic.location.Distance(closest)); }
static bool test_replay(const Contest olc_type, const ContestResult &official_score) { Directory::Create(_T("output/results")); std::ofstream f("output/results/res-sample.txt"); GlidePolar glide_polar(fixed(2)); Error error; FileLineReaderA *reader = new FileLineReaderA(replay_file.c_str(), error); if (reader->error()) { delete reader; fprintf(stderr, "%s\n", error.GetMessage()); return false; } ReplayLoggerSim sim(reader); ComputerSettings settings_computer; settings_computer.SetDefaults(); settings_computer.contest.enable = true; load_scores(settings_computer.contest.handicap); if (verbose) { switch (olc_type) { case Contest::OLC_LEAGUE: std::cout << "# OLC-League\n"; break; case Contest::OLC_SPRINT: std::cout << "# OLC-Sprint\n"; break; case Contest::OLC_FAI: std::cout << "# OLC-FAI\n"; break; case Contest::OLC_CLASSIC: std::cout << "# OLC-Classic\n"; break; case Contest::OLC_PLUS: std::cout << "# OLC-Plus\n"; break; default: std::cout << "# Unknown!\n"; break; } } bool do_print = verbose; unsigned print_counter=0; MoreData basic; basic.Reset(); FlyingComputer flying_computer; flying_computer.Reset(); FlyingState flying_state; flying_state.Reset(); TraceComputer trace_computer; ContestManager contest_manager(olc_type, trace_computer.GetFull(), trace_computer.GetFull(), trace_computer.GetSprint()); contest_manager.SetHandicap(settings_computer.contest.handicap); DerivedInfo calculated; while (sim.Update(basic)) { n_samples++; flying_computer.Compute(glide_polar.GetVTakeoff(), basic, calculated, flying_state); calculated.flight.flying = true; trace_computer.Update(settings_computer, basic, calculated); contest_manager.UpdateIdle(); if (verbose>1) { sim.print(f, basic); f.flush(); } if (do_print) { PrintHelper::trace_print(trace_computer.GetFull(), basic.location); } do_print = (++print_counter % output_skip ==0) && verbose; }; contest_manager.SolveExhaustive(); if (verbose) { PrintDistanceCounts(); } return compare_scores(official_score, contest_manager.GetStats().GetResult(0)); }
void CirclingComputer::Turning(CirclingInfo &circling_info, const MoreData &basic, const MoreData &last_basic, const DerivedInfo &calculated, const DerivedInfo &last_calculated, const ComputerSettings &settings_computer) { // You can't be circling unless you're flying if (!calculated.flight.flying || !basic.HasTimeAdvancedSince(last_basic)) return; // JMW limit rate to 50 deg per second otherwise a big spike // will cause spurious lock on circling for a long time fixed turn_rate = max(fixed(-50), min(fixed(50), calculated.turn_rate)); // Make the turn rate more smooth using the LowPassFilter turn_rate = LowPassFilter(last_calculated.turn_rate_smoothed, turn_rate, fixed(0.3)); circling_info.turn_rate_smoothed = turn_rate; circling_info.turning = fabs(turn_rate) >= min_turn_rate; // Force cruise or climb mode if external device says so bool force_cruise = false; bool force_circling = false; if (settings_computer.external_trigger_cruise_enabled && !basic.gps.replay) { switch (basic.switch_state.flight_mode) { case SwitchInfo::FlightMode::UNKNOWN: force_circling = false; force_cruise = false; break; case SwitchInfo::FlightMode::CIRCLING: force_circling = true; force_cruise = false; break; case SwitchInfo::FlightMode::CRUISE: force_circling = false; force_cruise = true; break; } } switch (calculated.turn_mode) { case CirclingMode::CRUISE: // If (in cruise mode and beginning of circling detected) if (circling_info.turning || force_circling) { // Remember the start values of the turn circling_info.turn_start_time = basic.time; circling_info.turn_start_location = basic.location; circling_info.turn_start_altitude = basic.nav_altitude; circling_info.turn_start_energy_height = basic.energy_height; circling_info.turn_mode = CirclingMode::POSSIBLE_CLIMB; } if (!force_circling) break; case CirclingMode::POSSIBLE_CLIMB: if (force_cruise) { circling_info.turn_mode = CirclingMode::CRUISE; break; } if (circling_info.turning || force_circling) { if (((basic.time - calculated.turn_start_time) > cruise_climb_switch) || force_circling) { // yes, we are certain now that we are circling circling_info.circling = true; // JMW Transition to climb circling_info.turn_mode = CirclingMode::CLIMB; // Remember the start values of the climbing period circling_info.climb_start_location = circling_info.turn_start_location; circling_info.climb_start_altitude = circling_info.turn_start_altitude + circling_info.turn_start_energy_height; circling_info.climb_start_time = circling_info.turn_start_time; } } else { // nope, not turning, so go back to cruise circling_info.turn_mode = CirclingMode::CRUISE; } break; case CirclingMode::CLIMB: if (!circling_info.turning || force_cruise) { // Remember the end values of the turn circling_info.turn_start_time = basic.time; circling_info.turn_start_location = basic.location; circling_info.turn_start_altitude = basic.nav_altitude; circling_info.turn_start_energy_height = basic.energy_height; // JMW Transition to cruise, due to not properly turning circling_info.turn_mode = CirclingMode::POSSIBLE_CRUISE; } if (!force_cruise) break; case CirclingMode::POSSIBLE_CRUISE: if (force_circling) { circling_info.turn_mode = CirclingMode::CLIMB; break; } if (!circling_info.turning || force_cruise) { if (((basic.time - circling_info.turn_start_time) > climb_cruise_switch) || force_cruise) { // yes, we are certain now that we are cruising again circling_info.circling = false; // Transition to cruise circling_info.turn_mode = CirclingMode::CRUISE; circling_info.cruise_start_location = circling_info.turn_start_location; circling_info.cruise_start_altitude = circling_info.turn_start_altitude; circling_info.cruise_start_time = circling_info.turn_start_time; } } else { // nope, we are circling again // JMW Transition back to climb, because we are turning again circling_info.turn_mode = CirclingMode::CLIMB; } break; } }
static bool test_replay(const Contests olc_type, const ContestResult &official_score) { std::ofstream f("results/res-sample.txt"); GlidePolar glide_polar(fixed_two); AircraftState state_last; FileLineReaderA *reader = new FileLineReaderA(replay_file.c_str()); if (reader->error()) { delete reader; return false; } ReplayLoggerSim sim(reader); ComputerSettings settings_computer; settings_computer.SetDefaults(); settings_computer.task.enable_olc = true; load_scores(settings_computer.task.contest_handicap); if (verbose) { switch (olc_type) { case OLC_League: std::cout << "# OLC-League\n"; break; case OLC_Sprint: std::cout << "# OLC-Sprint\n"; break; case OLC_FAI: std::cout << "# OLC-FAI\n"; break; case OLC_Classic: std::cout << "# OLC-Classic\n"; break; case OLC_Plus: std::cout << "# OLC-Plus\n"; break; default: std::cout << "# Unknown!\n"; break; } } bool do_print = verbose; unsigned print_counter=0; MoreData basic; basic.Reset(); while (sim.Update(basic, fixed_one) && !sim.started) { } state_last = sim.state; fixed time_last = sim.state.time; FlyingComputer flying_computer; flying_computer.Reset(); FlyingState flying_state; flying_state.Reset(); TraceComputer trace_computer; ContestManager contest_manager(olc_type, trace_computer.GetFull(), trace_computer.GetSprint()); contest_manager.SetHandicap(settings_computer.task.contest_handicap); DerivedInfo calculated; while (sim.Update(basic, fixed_one)) { if (sim.state.time>time_last) { n_samples++; flying_computer.Compute(glide_polar.GetVTakeoff(), sim.state, flying_state); calculated.flight.flying = flying_state.flying; trace_computer.Update(settings_computer, basic, calculated); contest_manager.UpdateIdle(); state_last = sim.state; if (verbose>1) { sim.print(f); f.flush(); } if (do_print) { PrintHelper::trace_print(trace_computer.GetFull(), sim.state.location); } do_print = (++print_counter % output_skip ==0) && verbose; } time_last = sim.state.time; }; contest_manager.SolveExhaustive(); if (verbose) { PrintDistanceCounts(); } return compare_scores(official_score, contest_manager.GetStats().GetResult(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)); } }