void GlueMapWindow::DrawThermalBand(Canvas &canvas, const PixelRect &rc) const { if (Calculated().task_stats.total.solution_remaining.IsOk() && Calculated().task_stats.total.solution_remaining.altitude_difference > 50 && GetDisplayMode() == DisplayMode::FINAL_GLIDE) return; PixelRect tb_rect; tb_rect.left = rc.left; tb_rect.right = rc.left+Layout::Scale(20); tb_rect.top = Layout::Scale(2); tb_rect.bottom = (rc.bottom-rc.top)/5 - Layout::Scale(2); const ThermalBandRenderer &renderer = thermal_band_renderer; if (task != nullptr) { ProtectedTaskManager::Lease task_manager(*task); renderer.DrawThermalBand(Basic(), Calculated(), GetComputerSettings(), canvas, tb_rect, GetComputerSettings().task, true, &task_manager->GetOrderedTask().GetOrderedTaskSettings()); } else { renderer.DrawThermalBand(Basic(), Calculated(), GetComputerSettings(), canvas, tb_rect, GetComputerSettings().task, true); } }
/** * 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; }
/* * Do not disturb too much. Play alert sound only once every x minutes, not more. */ void GlideComputerTask::AlertBestAlternate(short soundmode) { static double LastAlertTime = 0; if (Basic().Time > LastAlertTime + 180.0) { if (SettingsComputer().EnableSoundModes) { LastAlertTime = Basic().Time; switch (soundmode) { case 0: break; case 1: PlayResource(TEXT("IDR_WAV_GREEN")); break; case 2: PlayResource(TEXT("IDR_WAV_RED")); break; case 11: PlayResource(TEXT("IDR_WAV_GREEN")); PlayResource(TEXT("IDR_WAV_GREEN")); break; default: break; } } } }
void MapWindow::DrawTask(Canvas &canvas) { if (task == NULL) return; ProtectedTaskManager::Lease task_manager(*task); const AbstractTask *task = task_manager->get_active_task(); if (task == NULL || !task->check_task()) return; /* RLD bearing is invalid if GPS not connected and in non-sim mode, but we can still draw targets */ const bool draw_bearing = Basic().gps.Connected; RenderObservationZone ozv; RenderTaskPointMap tpv(canvas, #ifdef ENABLE_OPENGL /* OpenGL doesn't have the BufferCanvas class */ NULL, #else &buffer_canvas, #endif render_projection, SettingsMap(), /* we're accessing the OrderedTask here, which may be invalid at this point, but it will be used only if active, so it's ok */ task_manager->get_ordered_task().get_task_projection(), ozv, draw_bearing, Basic().Location); RenderTask dv(tpv, render_projection.GetScreenBounds()); ((TaskVisitor &)dv).Visit(*task); }
/** * 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); }
void GlideComputer::OnStartTask() { GlideComputerBlackboard::StartTask(); air_data_computer.ResetStats(); stats_computer.StartTask(Basic()); log_computer.StartTask(Basic()); }
void GlueMapWindow::DrawFlightMode(Canvas &canvas, const PixelRect &rc) const { PixelScalar offset = 0; // draw logger status if (logger != nullptr && logger->IsLoggerActive()) { bool flip = (Basic().date_time_utc.second % 2) == 0; const MaskedIcon &icon = flip ? look.logger_on_icon : look.logger_off_icon; offset = icon.GetSize().cx; icon.Draw(canvas, rc.right - offset, rc.bottom - icon.GetSize().cy); } // draw flight mode const MaskedIcon *bmp; if (Calculated().common_stats.task_type == TaskType::ABORT) bmp = &look.abort_mode_icon; else if (GetDisplayMode() == DisplayMode::CIRCLING) bmp = &look.climb_mode_icon; else if (GetDisplayMode() == DisplayMode::FINAL_GLIDE) bmp = &look.final_glide_mode_icon; else bmp = &look.cruise_mode_icon; offset += bmp->GetSize().cx + Layout::Scale(6); bmp->Draw(canvas, rc.right - offset, rc.bottom - bmp->GetSize().cy - Layout::Scale(4)); // draw flarm status if (!GetMapSettings().show_flarm_alarm_level) // Don't show indicator when the gauge is indicating the traffic anyway return; const FlarmStatus &flarm = Basic().flarm.status; if (!flarm.available) return; switch (flarm.alarm_level) { case FlarmTraffic::AlarmType::NONE: bmp = &look.traffic_safe_icon; break; case FlarmTraffic::AlarmType::LOW: case FlarmTraffic::AlarmType::INFO_ALERT: bmp = &look.traffic_warning_icon; break; case FlarmTraffic::AlarmType::IMPORTANT: case FlarmTraffic::AlarmType::URGENT: bmp = &look.traffic_alarm_icon; break; }; offset += bmp->GetSize().cx + Layout::Scale(6); bmp->Draw(canvas, rc.right - offset, rc.bottom - bmp->GetSize().cy - Layout::Scale(2)); }
void MapWindow::DrawProjectedTrack(Canvas &canvas) { if (task == NULL || !task->Valid() || !task->getSettings().AATEnabled || task->getActiveIndex() ==0) return; if (Calculated().Circling || task->TaskIsTemporary()) { // don't display in various modes return; } // TODO feature: maybe have this work even if no task? // TODO feature: draw this also when in target pan mode GEOPOINT start = Basic().Location; GEOPOINT previous_loc = task->getTargetLocation(task->getActiveIndex() - 1); double distance_from_previous, bearing; DistanceBearing(previous_loc, start, &distance_from_previous, &bearing); if (distance_from_previous < 100.0) { bearing = Basic().TrackBearing; // too short to have valid data } POINT pt[2] = {{0,-75},{0,-400}}; if (SettingsMap().TargetPan) { double screen_range = GetScreenDistanceMeters(); double f_low = 0.4; double f_high = 1.5; screen_range = max(screen_range, Calculated().WaypointDistance); GEOPOINT p1, p2; FindLatitudeLongitude(start, bearing, f_low*screen_range, &p1); FindLatitudeLongitude(start, bearing, f_high*screen_range, &p2); LonLat2Screen(p1, pt[0]); LonLat2Screen(p2, pt[1]); } else if (fabs(bearing-Calculated().WaypointBearing)<10) { // too small an error to bother return; } else { pt[1].y = (long)(-max(MapRectBig.right-MapRectBig.left, MapRectBig.bottom-MapRectBig.top)*1.2); PolygonRotateShift(pt, 2, Orig_Aircraft.x, Orig_Aircraft.y, bearing-DisplayAngle); } Pen dash_pen(Pen::DASH, IBLSCALE(2), Color(0, 0, 0)); canvas.select(dash_pen); canvas.line(pt[0], pt[1]); }
void GlueMapWindow::DrawFlightMode(Canvas &canvas, const PixelRect &rc) const { int offset = 0; // draw logger status if (logger != NULL && logger->isLoggerActive()) { bool flip = (Basic().date_time_utc.second % 2) == 0; MaskedIcon &icon = flip ? Graphics::hLogger : Graphics::hLoggerOff; offset = icon.get_size().cx; icon.draw(canvas, rc.right - offset, rc.bottom - icon.get_size().cy); } // draw flight mode MaskedIcon *bmp; if (task != NULL && (task->get_mode() == TaskManager::MODE_ABORT)) bmp = &Graphics::hAbort; else if (GetDisplayMode() == DM_CIRCLING) bmp = &Graphics::hClimb; else if (GetDisplayMode() == DM_FINAL_GLIDE) bmp = &Graphics::hFinalGlide; else bmp = &Graphics::hCruise; offset += bmp->get_size().cx + Layout::Scale(6); bmp->draw(canvas, rc.right - offset, rc.bottom - bmp->get_size().cy - Layout::Scale(4)); // draw flarm status if (CommonInterface::GetUISettings().enable_flarm_gauge) // Don't show indicator when the gauge is indicating the traffic anyway return; const FLARM_STATE &flarm = Basic().flarm; if (!flarm.available || flarm.GetActiveTrafficCount() == 0) return; switch (flarm.alarm_level) { case 0: bmp = &Graphics::hBmpTrafficSafe; break; case 1: bmp = &Graphics::hBmpTrafficWarning; break; case 2: case 3: bmp = &Graphics::hBmpTrafficAlarm; break; }; offset += bmp->get_size().cx + Layout::Scale(6); bmp->draw(canvas, rc.right - offset, rc.bottom - bmp->get_size().cy - Layout::Scale(2)); }
void GlueMapWindow::DrawStallRatio(Canvas &canvas, const PixelRect &rc) const { if (Basic().stall_ratio_available) { // JMW experimental, display stall sensor auto s = Clamp(Basic().stall_ratio, 0., 1.); int m = rc.GetHeight() * s * s; canvas.SelectBlackPen(); canvas.DrawLine(rc.right - 1, rc.bottom - m, rc.right - 11, rc.bottom - m); } }
void GlueMapWindow::DrawStallRatio(Canvas &canvas, const PixelRect &rc) const { if (Basic().stall_ratio_available) { // JMW experimental, display stall sensor fixed s = Clamp(Basic().stall_ratio, fixed(0), fixed(1)); PixelScalar m((rc.bottom - rc.top) * s * s); canvas.SelectBlackPen(); canvas.DrawLine(rc.right - 1, rc.bottom - m, rc.right - 11, rc.bottom - m); } }
void GlueMapWindow::DrawStallRatio(Canvas &canvas, const PixelRect &rc) const { if (Basic().stall_ratio_available) { // JMW experimental, display stall sensor fixed s = max(fixed_zero, min(fixed_one, Basic().stall_ratio)); long m = (long)((rc.bottom - rc.top) * s * s); canvas.black_pen(); canvas.line(rc.right - 1, rc.bottom - m, rc.right - 11, rc.bottom - m); } }
void MapWindow::DrawTrackBearing(Canvas &canvas, const RasterPoint aircraft_pos, bool circling) const { if (!Basic().location_available) return; bool wind_relative = GetMapSettings().trail.wind_drift_enabled && circling; TrackLineRenderer track_line_renderer(look); track_line_renderer.Draw(canvas, render_projection, aircraft_pos, Basic(), Calculated(), GetMapSettings(), wind_relative); }
bool DeviceBlackboard::expire_wall_clock() { ScopeLock protect(mutexBlackboard); if (!Basic().Connected) return false; for (unsigned i = 0; i < NUMDEV; ++i) per_device_data[i].expire_wall_clock(); Merge(); return !Basic().Connected; }
void DeviceBlackboard::tick() { SetBasic().expire(); // calculate fast data to complete aircraft state computer.Compute(SetBasic(), LastBasic(), Calculated(), SettingsComputer()); if (Basic().Time!= LastBasic().Time) { state_last = Basic(); } }
/** * 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); }
/** * Calls the SearchBestAlternate() method if a certain time has passed since the last call * @author Paolo Ventafridda */ void GlideComputerTask::DoBestAlternateSlow() { static double LastSearchBestTime = 0; // VENTA3 // VENTA3 best landing slow calculation #ifdef WINDOWSPC if ( (SettingsComputer().EnableBestAlternate) && (Basic().Time > LastSearchBestTime+10.0) ) // VENTA3 #else if ((SettingsComputer().EnableBestAlternate) && (Basic().Time > LastSearchBestTime + BESTALTERNATEINTERVAL)) // VENTA3 #endif { LastSearchBestTime = Basic().Time; SearchBestAlternate(); } }
void MapWindow::DrawThermalEstimate(Canvas &canvas) const { const MoreData &basic = Basic(); const DerivedInfo &calculated = Calculated(); const ThermalLocatorInfo &thermal_locator = calculated.thermal_locator; if (render_projection.GetMapScale() > 4000) return; // draw only at close map scales in non-circling mode DrawThermalSources(canvas, look.thermal_source_icon, render_projection, thermal_locator.sources, basic.nav_altitude, calculated.wind_available ? calculated.wind : SpeedVector::Zero()); const auto &cloud_settings = ComputerSettings().tracking.skylines.cloud; if (cloud_settings.show_thermals && skylines_data != nullptr) { ScopeLock protect(skylines_data->mutex); for (auto &i : skylines_data->thermals) { // TODO: apply wind drift PixelPoint pt; if (render_projection.GeoToScreenIfVisible(i.bottom_location, pt)) look.thermal_source_icon.Draw(canvas, pt); } } }
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 TargetMapWindow::DrawTask(Canvas &canvas) { if (task == NULL) return; ProtectedTaskManager::Lease task_manager(*task); const AbstractTask *task = task_manager->GetActiveTask(); if (task && task->CheckTask()) { OZRenderer ozv(task_look, airspace_renderer.GetLook(), GetMapSettings().airspace); RenderTaskPoint tpv(canvas, projection, task_look, /* we're accessing the OrderedTask here, which may be invalid at this point, but it will be used only if active, so it's ok */ task_manager->GetOrderedTask().GetTaskProjection(), ozv, false, RenderTaskPoint::ALL, Basic().location); TaskRenderer dv(tpv, projection.GetScreenBounds()); dv.Draw(*task); } }
void TargetMapWindow::RenderTerrain(Canvas &canvas) { background.SetSunAngle(projection, GetMapSettings().terrain, Basic(), Calculated()); background.Draw(canvas, projection, GetMapSettings().terrain); }
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); } }
void GlideComputer::OnStartTask() { GlideComputerBlackboard::StartTask(); GlideComputerStats::StartTask(); logger.LogStartEvent(Basic()); }
void DeviceBlackboard::SetMC(fixed mc) { AllDevicesPutMacCready(mc); if (!Basic().LocationAvailable) TriggerGPSUpdate(); }
void MapWindow::DrawGlideThroughTerrain(Canvas &canvas) const { if (SettingsComputer().FinalGlideTerrain) { canvas.hollow_brush(); #ifndef ENABLE_OPENGL canvas.select(Graphics::hpTerrainLineBg); canvas.polygon(Groundline, TERRAIN_ALT_INFO::NUMTERRAINSWEEPS); #endif canvas.select(Graphics::hpTerrainLine); canvas.polygon(Groundline, TERRAIN_ALT_INFO::NUMTERRAINSWEEPS); } if (!Basic().flight.Flying) return; if (!Calculated().TerrainWarningLocation.is_null()) { RasterPoint sc; if (render_projection.GeoToScreenIfVisible(Calculated().TerrainWarningLocation, sc)) Graphics::hTerrainWarning.draw(canvas, sc.x, sc.y); } }
/** * Determine whether vario gauge, FLARM radar and infoboxes should be drawn */ void ActionInterface::DisplayModes() { // Determine whether the vario gauge should be drawn SetSettingsMap().EnableVarioGauge = vario_visible() && !SettingsMap().FullScreen; if (main_window.vario) { if (!SettingsMap().ScreenBlanked && SettingsMap().EnableVarioGauge) { main_window.vario->show(); } else { main_window.vario->hide(); } } if (Basic().NewTraffic) { // TODO bug: JMW: broken, currently won't work very well, needs to be reworked GaugeFLARM *gauge_flarm = main_window.flarm; if (gauge_flarm != NULL) gauge_flarm->Suppress = false; } if (SettingsMap().FullScreen) { InfoBoxManager::Hide(); } else { InfoBoxManager::Show(); } }
void TargetMapWindow::DrawTask(Canvas &canvas) { if (task == NULL) return; ProtectedTaskManager::Lease task_manager(*task); const AbstractTask *task = task_manager->get_active_task(); if (task && task->check_task()) { RenderObservationZone ozv(task_look, airspace_renderer.GetLook()); RenderTaskPointMap tpv(canvas, #ifdef ENABLE_OPENGL /* OpenGL doesn't have the BufferCanvas class */ NULL, #else &buffer_canvas, #endif projection, SettingsMap(), task_look, /* we're accessing the OrderedTask here, which may be invalid at this point, but it will be used only if active, so it's ok */ task_manager->get_ordered_task().get_task_projection(), ozv, false, true, Basic().location); RenderTask dv(tpv, projection.GetScreenBounds()); ((TaskVisitor &)dv).Visit(*task); } }
void MapWindow::DrawBestCruiseTrack(Canvas &canvas, const RasterPoint aircraft_pos) const { if (Basic().location_available) BestCruiseArrowRenderer::Draw(canvas, look.task, render_projection.GetScreenAngle(), aircraft_pos, Calculated()); }
void MapWindow::DrawTrail(Canvas &canvas, const RasterPoint aircraft_pos, unsigned min_time, bool enable_traildrift) const { if (glide_computer) TrailRenderer::Draw(canvas, *glide_computer, render_projection, min_time, enable_traildrift, aircraft_pos, Basic(), Calculated(), SettingsMap()); }
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; } }