/** * Calculates the vario values for gps vario, gps total energy vario * Sets Vario to GPSVario or received Vario data from instrument */ static void ComputeGPSVario(MoreData &basic, const MoreData &last, const MoreData &last_gps) { if (basic.noncomp_vario_available && last.noncomp_vario_available) { /* If we have a noncompensated vario signal, we use that to compute the "GPS" total energy vario value, even if navigate by GPS altitude is configured, because a vario is expected to be more exact. */ /* use the "Validity" time stamp, because it reflects when this vertical speed was measured, and GPS time may not be available */ const fixed delta_t = basic.noncomp_vario_available.GetTimeDifference(last.noncomp_vario_available); if (positive(delta_t)) { /* only update when a new value was received */ fixed delta_e = basic.energy_height - last.energy_height; basic.gps_vario = basic.noncomp_vario; basic.gps_vario_TE = basic.noncomp_vario + delta_e / delta_t; basic.gps_vario_available = basic.noncomp_vario_available; } } else if (basic.pressure_altitude_available && last.pressure_altitude_available) { /* Barring that, we prefer pressure altitude for the "GPS" vario, even if navigate by GPS altitude is configured, because pressure altitude is expected to be more exact. */ const fixed delta_t = basic.pressure_altitude_available.GetTimeDifference(last.pressure_altitude_available); if (positive(delta_t)) { /* only update when a new value was received */ fixed delta_h = basic.pressure_altitude - last.pressure_altitude; fixed delta_e = basic.energy_height - last.energy_height; basic.gps_vario = delta_h / delta_t; basic.gps_vario_TE = (delta_h + delta_e) / delta_t; basic.gps_vario_available = basic.pressure_altitude_available; } } else if (basic.baro_altitude_available && last.baro_altitude_available) { /* barometric altitude is also ok, but it's rare that it is available when pressure altitude is not */ const fixed delta_t = basic.baro_altitude_available.GetTimeDifference(last.baro_altitude_available); if (positive(delta_t)) { /* only update when a new value was received */ fixed delta_h = basic.baro_altitude - last.baro_altitude; fixed delta_e = basic.energy_height - last.energy_height; basic.gps_vario = delta_h / delta_t; basic.gps_vario_TE = (delta_h + delta_e) / delta_t; basic.gps_vario_available = basic.baro_altitude_available; } } else if (basic.gps_altitude_available && last_gps.gps_altitude_available && basic.time_available && last_gps.time_available) { /* use the GPS time stamp, because it reflects when this altitude was measured by the GPS receiver; the Validity object just shows when this value was parsed by XCSoar */ const fixed delta_t = basic.time - last_gps.time; if (positive(delta_t)) { /* only update when a new value was received */ fixed delta_h = basic.gps_altitude - last_gps.gps_altitude; fixed delta_e = basic.energy_height - last_gps.energy_height; basic.gps_vario = delta_h / delta_t; basic.gps_vario_TE = (delta_h + delta_e) / delta_t; basic.gps_vario_available = basic.gps_altitude_available; } } else { basic.gps_vario = basic.gps_vario_TE = fixed(0); basic.gps_vario_available.Clear(); } }
bool WaypointReaderSeeYou::ParseLine(const TCHAR* line, const unsigned linenum, Waypoints &waypoints) { enum { iName = 0, iLatitude = 3, iLongitude = 4, iElevation = 5, iStyle = 6, iRWDir = 7, iRWLen = 8, iFrequency = 9, iDescription = 10, }; if (linenum == 0) ignore_following = false; // If (end-of-file or comment) if (StringIsEmpty(line) || StringStartsWith(line, _T("**")) || StringStartsWith(line, _T("*"))) // -> return without error condition return true; TCHAR ctemp[4096]; if (_tcslen(line) >= ARRAY_SIZE(ctemp)) /* line too long for buffer */ return false; // Skip first line if it doesn't begin with a quotation character // (usually the field order line) if (linenum == 0 && line[0] != _T('\"')) return true; // If task marker is reached ignore all following lines if (_tcsstr(line, _T("-----Related Tasks-----")) == line) ignore_following = true; if (ignore_following) return true; // Get fields const TCHAR *params[20]; size_t n_params = ExtractParameters(line, ctemp, params, ARRAY_SIZE(params), true, _T('"')); // Check if the basic fields are provided if (iName >= n_params || iLatitude >= n_params || iLongitude >= n_params) return false; Waypoint new_waypoint; // Latitude (e.g. 5115.900N) if (!ParseAngle(params[iLatitude], new_waypoint.location.latitude, true)) return false; // Longitude (e.g. 00715.900W) if (!ParseAngle(params[iLongitude], new_waypoint.location.longitude, false)) return false; new_waypoint.location.Normalize(); // ensure longitude is within -180:180 new_waypoint.file_num = file_num; new_waypoint.original_id = 0; // Name (e.g. "Some Turnpoint") if (*params[iName] == _T('\0')) return false; new_waypoint.name = params[iName]; // Elevation (e.g. 458.0m) /// @todo configurable behaviour if ((iElevation >= n_params || !ParseAltitude(params[iElevation], new_waypoint.elevation)) && !CheckAltitude(new_waypoint)) return false; // Style (e.g. 5) if (iStyle < n_params) ParseStyle(params[iStyle], new_waypoint.type); new_waypoint.flags.turn_point = true; // Frequency & runway direction/length (for airports and landables) // and description (e.g. "Some Description") if (new_waypoint.IsLandable()) { if (iFrequency < n_params) new_waypoint.radio_frequency = RadioFrequency::Parse(params[iFrequency]); // Runway length (e.g. 546.0m) fixed rwlen = fixed_minus_one; if (iRWLen < n_params && ParseDistance(params[iRWLen], rwlen) && positive(rwlen)) new_waypoint.runway.SetLength(uround(rwlen)); if (iRWDir < n_params && *params[iRWDir]) { TCHAR *end; int direction =_tcstol(params[iRWDir], &end, 10); if (end == params[iRWDir] || direction < 0 || direction > 360 || (direction == 0 && !positive(rwlen))) direction = -1; else if (direction == 360) direction = 0; if (direction >= 0) new_waypoint.runway.SetDirectionDegrees(direction); } } if (iDescription < n_params) new_waypoint.comment = params[iDescription]; waypoints.Append(new_waypoint); return true; }
bool GlideResult::IsFinalGlide() const { return IsOk() && !negative(altitude_difference) && !positive(height_climb); }
bool AbstractAirspace::Intercept(const AircraftState &state, const AirspaceAircraftPerformance &perf, AirspaceInterceptSolution &solution, const GeoPoint &loc_start, const GeoPoint &loc_end) const { const bool only_vertical = (loc_start == loc_end) && (loc_start == state.location); const fixed distance_start = only_vertical ? fixed_zero : state.location.Distance(loc_start); const fixed distance_end = (loc_start == loc_end) ? distance_start : (only_vertical ? fixed_zero : state.location.Distance(loc_end)); AirspaceInterceptSolution solution_this = AirspaceInterceptSolution::Invalid(); // need to scan at least three sides, top, far, bottom (if not terrain) AirspaceInterceptSolution solution_candidate = AirspaceInterceptSolution::Invalid(); if (!only_vertical) { solution_candidate = InterceptVertical(state, perf, distance_start); // search near wall if (solution_candidate.IsValid() && ((solution_candidate.elapsed_time < solution_this.elapsed_time) || negative(solution_this.elapsed_time))) solution_this = solution_candidate; if (distance_end != distance_start) { // need to search far wall also solution_candidate = InterceptVertical(state, perf, distance_end); if (solution_candidate.IsValid() && ((solution_candidate.elapsed_time < solution_this.elapsed_time) || negative(solution_this.elapsed_time))) solution_this = solution_candidate; } } solution_candidate = InterceptHorizontal(state, perf, distance_start, distance_end, false); // search top wall if (solution_candidate.IsValid() && ((solution_candidate.elapsed_time < solution_this.elapsed_time) || negative(solution_this.elapsed_time))) solution_this = solution_candidate; // search bottom wall if (!altitude_base.IsTerrain()) { solution_candidate = InterceptHorizontal(state, perf, distance_start, distance_end, true); if (solution_candidate.IsValid() && ((solution_candidate.elapsed_time < solution_this.elapsed_time) || negative(solution_this.elapsed_time))) solution_this = solution_candidate; } if (solution_this.IsValid()) { solution = solution_this; if (solution.distance == distance_start) solution.location = loc_start; else if (solution.distance == distance_end) solution.location = loc_end; else if (positive(distance_end)) solution.location = state.location.Interpolate(loc_end, solution.distance / distance_end); else solution.location = loc_start; assert(!negative(solution.distance)); return true; } else solution = AirspaceInterceptSolution::Invalid(); return false; }
static ObservationZonePoint * DeserialiseOZ(const Waypoint &wp, const ConstDataNode &node, bool is_turnpoint) { const TCHAR *type = node.GetAttribute(_T("type")); if (type == nullptr) return nullptr; if (StringIsEqual(type, _T("Line"))) { LineSectorZone *ls = new LineSectorZone(wp.location); fixed length; if (node.GetAttribute(_T("length"), length) && positive(length)) ls->SetLength(length); return ls; } else if (StringIsEqual(type, _T("Cylinder"))) { CylinderZone *ls = new CylinderZone(wp.location); fixed radius; if (node.GetAttribute(_T("radius"), radius) && positive(radius)) ls->SetRadius(radius); return ls; } else if (StringIsEqual(type, _T("MatCylinder"))) { return CylinderZone::CreateMatCylinderZone(wp.location); } else if (StringIsEqual(type, _T("Sector"))) { fixed radius, inner_radius; Angle start, end; SectorZone *ls; if (node.GetAttribute(_T("inner_radius"), inner_radius)) { AnnularSectorZone *als = new AnnularSectorZone(wp.location); als->SetInnerRadius(inner_radius); ls = als; } else ls = new SectorZone(wp.location); if (node.GetAttribute(_T("radius"), radius) && positive(radius)) ls->SetRadius(radius); if (node.GetAttribute(_T("start_radial"), start)) ls->SetStartRadial(start); if (node.GetAttribute(_T("end_radial"), end)) ls->SetEndRadial(end); return ls; } else if (StringIsEqual(type, _T("FAISector"))) return SymmetricSectorZone::CreateFAISectorZone(wp.location, is_turnpoint); else if (StringIsEqual(type, _T("SymmetricQuadrant"))) { fixed radius = fixed(10000); node.GetAttribute(_T("radius"), radius); return new SymmetricSectorZone(wp.location, radius); } else if (StringIsEqual(type, _T("Keyhole"))) return KeyholeZone::CreateDAeCKeyholeZone(wp.location); else if (StringIsEqual(type, _T("CustomKeyhole"))) { fixed radius = fixed(10000), inner_radius = fixed(500); Angle angle = Angle::QuarterCircle(); node.GetAttribute(_T("radius"), radius); node.GetAttribute(_T("inner_radius"), inner_radius); node.GetAttribute(_T("angle"), angle); KeyholeZone *keyhole = KeyholeZone::CreateCustomKeyholeZone(wp.location, radius, angle); keyhole->SetInnerRadius(inner_radius); return keyhole; } else if (StringIsEqual(type, _T("BGAStartSector"))) return KeyholeZone::CreateBGAStartSectorZone(wp.location); else if (StringIsEqual(type, _T("BGAFixedCourse"))) return KeyholeZone::CreateBGAFixedCourseZone(wp.location); else if (StringIsEqual(type, _T("BGAEnhancedOption"))) return KeyholeZone::CreateBGAEnhancedOptionZone(wp.location); return nullptr; }
static void test_troute(const RasterMap& map, fixed mwind, fixed mc, RoughAltitude ceiling) { GlideSettings settings; settings.SetDefaults(); GlidePolar polar(mc); SpeedVector wind(Angle::Degrees(fixed(0)), mwind); TerrainRoute route; route.UpdatePolar(settings, polar, polar, wind); route.SetTerrain(&map); GeoPoint origin(map.GetMapCenter()); fixed pd = map.pixel_distance(origin, 1); printf("# pixel size %g\n", (double)pd); bool retval= true; { std::ofstream fout ("results/terrain.txt"); unsigned nx = 100; unsigned ny = 100; for (unsigned i=0; i< nx; ++i) { for (unsigned j=0; j< ny; ++j) { fixed fx = (fixed)i/(nx-1)*fixed_two-fixed_one; fixed fy = (fixed)j/(ny-1)*fixed_two-fixed_one; GeoPoint x(origin.longitude+Angle::Degrees(fixed(0.6)*fx), origin.latitude+Angle::Degrees(fixed(0.4)*fy)); short h = map.GetInterpolatedHeight(x); fout << x.longitude.Degrees() << " " << x.latitude.Degrees() << " " << h << "\n"; } fout << "\n"; } fout << "\n"; } RoutePlannerConfig config; config.mode = RoutePlannerConfig::Mode::BOTH; unsigned i=0; for (fixed ang=fixed_zero; ang< fixed_two_pi; ang+= fixed_quarter_pi*fixed_half) { GeoPoint dest = GeoVector(fixed(40000.0), Angle::Radians(ang)).EndPoint(origin); short hdest = map.GetHeight(dest)+100; retval = route.Solve(AGeoPoint(origin, RoughAltitude(map.GetHeight(origin) + 100)), AGeoPoint(dest, RoughAltitude(positive(mc) ? hdest : std::max(hdest, (short)3200))), config, ceiling); char buffer[80]; sprintf(buffer,"terrain route solve, dir=%g, wind=%g, mc=%g ceiling=%d", (double)ang, (double)mwind, (double)mc, (int)ceiling); ok(retval, buffer, 0); PrintHelper::print_route(route); i++; } // polar.SetMC(fixed_zero); // route.UpdatePolar(polar, wind); }
/** * Set radius property * * @param new_radius Radius (m) of cylinder */ virtual void SetRadius(fixed new_radius) { assert(positive(new_radius)); radius = new_radius; }
bool ExcludeAltitude(const AbstractAirspace& airspace) { if (!positive(max_alt)) return false; return (airspace.GetBaseAltitude(state) > max_alt); }
CylinderZone(const CylinderZone &other, const GeoPoint &reference) :ObservationZonePoint((const ObservationZonePoint &)other, reference), radius(other.radius) { assert(positive(radius)); }
/** * Constructor. * * @param loc Location of center * @param _radius Radius (m) of cylinder * * @return Initialised object */ CylinderZone(const GeoPoint &loc, const fixed _radius = fixed(10000.0)) :ObservationZonePoint(Shape::CYLINDER, true, loc), radius(_radius) { assert(positive(radius)); }
sPoly2D* C2DFrustum::ClipPoly (sPoly2D& S, sPoly2D& D) const { bool bFullTest = false; for (u32 j=0; j<S.size(); j++) { if( !m_rect.in(S[j].pt) ) { bFullTest = true; break ; } } sPoly2D* src = &D; sPoly2D* dest = &S; if(!bFullTest) return dest; for (u32 i=0; i<planes.size(); i++) { // cache plane and swap lists const Fplane2 &P = planes[i] ; std::swap (src,dest) ; dest->clear () ; // classify all points relative to plane #i float cls[UI_FRUSTUM_SAFE] ; for (u32 j=0; j<src->size(); j++) cls[j]=P.classify((*src)[j].pt); // clip everything to this plane cls[src->size()] = cls[0] ; src->push_back((*src)[0]) ; Fvector2 dir_pt,dir_uv; float denum,t; for (j=0; j<src->size()-1; j++) { if ((*src)[j].pt.similar((*src)[j+1].pt,EPS_S)) continue; if (negative(cls[j])) { dest->push_back((*src)[j]) ; if (positive(cls[j+1])) { // segment intersects plane dir_pt.sub((*src)[j+1].pt,(*src)[j].pt); dir_uv.sub((*src)[j+1].uv,(*src)[j].uv); denum = P.n.dotproduct(dir_pt); if (denum!=0) { t = -cls[j]/denum ; //VERIFY(t<=1.f && t>=0); dest->last().pt.mad ((*src)[j].pt,dir_pt,t); dest->last().uv.mad ((*src)[j].uv,dir_uv,t); dest->inc(); } } } else { // J - outside if (negative(cls[j+1])) { // J+1 - inside // segment intersects plane dir_pt.sub((*src)[j+1].pt,(*src)[j].pt); dir_uv.sub((*src)[j+1].uv,(*src)[j].uv); denum = P.n.dotproduct(dir_pt); if (denum!=0) { t = -cls[j]/denum ; //VERIFY(t<=1.f && t>=0); dest->last().pt.mad ((*src)[j].pt,dir_pt,t); dest->last().uv.mad ((*src)[j].uv,dir_uv,t); dest->inc(); } } } } // here we end up with complete polygon in 'dest' which is inside plane #i if (dest->size()<3) return 0; } return dest; }
/** * Check whether aircraft has entered the observation zone. * * @return True if observation zone has been entered */ bool HasEntered() const { return positive(state_entered.time); }
void FlyingComputer::Compute(fixed takeoff_speed, const NMEAInfo &basic, const DerivedInfo &calculated, FlyingState &flying) { if (!basic.time_available || !basic.location_available) return; const fixed dt = delta_time.Update(basic.time, fixed(0.5), fixed(20)); if (negative(dt)) { Reset(); flying.Reset(); } if (!positive(dt)) return; const auto any_altitude = basic.GetAnyAltitude(); if (!basic.airspeed_available && !calculated.altitude_agl_valid && any_altitude.first && !negative(last_ground_altitude) && any_altitude.second > last_ground_altitude + fixed(250)) { /* lower the threshold for "not moving" when the aircraft is high above the take-off airfield and there's no airspeed probe; this shall reduce the risk of false landing detection when flying in strong head wind (e.g. ridge or wave) */ fixed dh = any_altitude.second - last_ground_altitude; if (dh > fixed(1000)) takeoff_speed /= 4; else if (dh > fixed(500)) takeoff_speed /= 2; else takeoff_speed = takeoff_speed * 2 / 3; } if (CheckTakeOffSpeed(takeoff_speed, basic) || CheckAltitudeAGL(calculated)) Moving(flying, basic.time, dt, basic.location); else if (!flying.flying || (CheckLandingSpeed(takeoff_speed, basic) && (!any_altitude.first || !CheckClimbing(dt, any_altitude.second)))) Stationary(flying, basic.time, dt, basic.location); if (any_altitude.first) { if (flying.on_ground) last_ground_altitude = any_altitude.second; CheckRelease(flying, basic.time, basic.location, any_altitude.second); } else sinking_since = fixed(-1); if (flying.flying && flying.release_location.IsValid()) { fixed distance = basic.location.Distance(flying.release_location); if (distance > flying.far_distance) { flying.far_location = basic.location; flying.far_distance = distance; } } }
bool GlidePolar::IsBallastable() const { return positive(ballast_ratio); }
void VarioBarRenderer::Draw(Canvas &canvas, const PixelRect &rc, const MoreData &basic, const DerivedInfo &calculated, const GlidePolar &glide_polar, const bool vario_bar_avg_enabled) const { #ifdef ENABLE_OPENGL const GLBlend blend(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); #endif RasterPoint VarioBar[6] = { { 0, 0 }, { 9, -9 }, { 18, 0 }, { 18, 0 }, { 9, 0 }, { 0, 0 } }; RasterPoint VarioBarAvg[4] = { { 0, 0 }, { 9, -9 }, { 9, 0 }, { 0, 0 } }; RasterPoint clipping_arrow[6] = { { 0, 0 }, { 9, 9 }, { 18, 0 }, { 18, 6 }, { 9, 15 }, { 0, 6 } }; RasterPoint clipping_arrow_av[4] = { { 0, 0 }, { 9, 9 }, { 9, 15 }, { 0, 6 } }; RasterPoint mc_arrow[6] = { { 0, 0 }, { 9, -9 }, { 18, 0 }, { 18, -2 }, { 9, -11 }, { 0, -2 } }; TCHAR Value[10]; const int y0 = (rc.bottom + rc.top) / 2; /* NOTE: size_divisor replaces the fixed value 9 that was used throughout * the code sink which caused fixed size rendering regardless of * map size (except the effects of Layout::Scale()). This method * is not usable with variable map sizes (e.g. because of the cross-section * area). size_divisor is used to introduce a screen size dependent scaling. * That workaround is an ugly hack and needs a rework. */ const auto size_divisor = std::max((fixed) Layout::Scale(70 / (rc.bottom - rc.top)), fixed(0.15)); PixelScalar dy_variobar = 0; PixelScalar dy_variobar_av = 0; PixelScalar dy_variobar_mc = 0; PixelScalar clipping_arrow_offset = Layout::Scale(4); PixelScalar clipping_arrow_av_offset = Layout::Scale(4); // PixelScalar clipping_arrow_mc_offset = Layout::Scale(4); auto vario_gross = basic.brutto_vario; FormatUserVerticalSpeed(vario_gross, Value, false, true); canvas.Select(*look.font); const PixelSize text_size = canvas.CalcTextSize(Value); auto vario_avg = calculated.average; // cut vario_gross at +- 5 meters/s if (vario_gross > fixed(5)) vario_gross = fixed(5); if (vario_gross < fixed(-5)) vario_gross = fixed(-5); int Offset = (int)(vario_gross / size_divisor); Offset = Layout::Scale(Offset); if (!positive(vario_gross)) { VarioBar[1].y = Layout::Scale(9); dy_variobar = text_size.cy + 2; } else { VarioBar[1].y = -Layout::Scale(9); clipping_arrow[1].y = -clipping_arrow[1].y; clipping_arrow[3].y = -clipping_arrow[3].y; clipping_arrow[4].y = -clipping_arrow[4].y; clipping_arrow[5].y = -clipping_arrow[5].y; clipping_arrow_offset = -clipping_arrow_offset; dy_variobar = -1; } // cut vario_avg at +- 5 meters/s if (vario_avg > fixed(5)) vario_avg = fixed(5); if (vario_avg < fixed(-5)) vario_avg = fixed(-5); int OffsetAvg = int(vario_avg / size_divisor); OffsetAvg = Layout::Scale(OffsetAvg); if (!positive(vario_avg)) { VarioBarAvg[1].y = Layout::Scale(9); dy_variobar_av = text_size.cy + 2; } else { VarioBarAvg[1].y = -Layout::Scale(9); clipping_arrow_av[1].y = -clipping_arrow_av[1].y; clipping_arrow_av[2].y = -clipping_arrow_av[2].y; clipping_arrow_av[3].y = -clipping_arrow_av[3].y; clipping_arrow_av_offset = -clipping_arrow_av_offset; dy_variobar_av = -1; } //clip MC Value auto mc_val = glide_polar.GetMC(); if (mc_val > fixed(5)) mc_val = fixed(5); if (!positive(mc_val)) { dy_variobar_mc = text_size.cy + 2; }else{ dy_variobar_mc = -1; } int OffsetMC = int(mc_val / size_divisor); OffsetMC = Layout::Scale(OffsetMC); for (unsigned i = 0; i < 6; i++) { VarioBar[i].y += y0 + dy_variobar; VarioBar[i].x = rc.right - Layout::Scale(VarioBar[i].x); } VarioBar[0].y -= Offset; VarioBar[1].y -= Offset; VarioBar[2].y -= Offset; for (unsigned i = 0; i < 4; i++) { VarioBarAvg[i].y += y0 + dy_variobar_av; VarioBarAvg[i].x = rc.right-Layout::Scale(VarioBarAvg[i].x); } VarioBarAvg[0].y -= OffsetAvg; VarioBarAvg[1].y -= OffsetAvg; // prepare mc arrow for (unsigned i = 0; i < 6; i++) { mc_arrow[i].y = Layout::Scale(mc_arrow[i].y) + y0 - OffsetMC + dy_variobar_mc; mc_arrow[i].x = rc.right - Layout::Scale(mc_arrow[i].x); } // prepare clipping arrow for (unsigned i = 0; i < 6; i++) { clipping_arrow[i].y = Layout::Scale(clipping_arrow[i].y) + y0 - Offset + clipping_arrow_offset + dy_variobar; clipping_arrow[i].x = rc.right - Layout::Scale(clipping_arrow[i].x); } // prepare clipping avg for (unsigned i = 0; i < 4; i++) { clipping_arrow_av[i].y = Layout::Scale(clipping_arrow_av[i].y) + y0 - OffsetAvg + clipping_arrow_av_offset + dy_variobar_av; clipping_arrow_av[i].x = rc.right-Layout::Scale(clipping_arrow_av[i].x); } // draw actual vario bar if (!positive(vario_gross)) { canvas.Select(look.pen_sink); canvas.Select(look.brush_sink); } else { canvas.Select(look.pen_climb); canvas.Select(look.brush_climb); } canvas.DrawPolygon(VarioBar, 6); // draw clipping arrow if (vario_gross <= fixed(-5) || vario_gross >= fixed(5)) canvas.DrawPolygon(clipping_arrow, 6); // draw avg vario bar if (!positive(vario_avg) && vario_bar_avg_enabled) { canvas.Select(look.pen_sink); canvas.Select(look.brush_sink_avg); } else { canvas.Select(look.pen_climb); canvas.Select(look.brush_climb_avg); } canvas.DrawPolygon(VarioBarAvg, 4); if (vario_avg <= fixed(-5.0) || vario_avg >= fixed(5.0)) canvas.DrawPolygon(clipping_arrow_av, 4); //draw MC arrow canvas.Select(look.pen_mc); canvas.Select(look.brush_mc); canvas.DrawPolygon(mc_arrow, 6); //draw text canvas.SetTextColor(COLOR_BLACK); canvas.SetBackgroundColor(COLOR_WHITE); TextInBoxMode style; style.shape = LabelShape::ROUNDED_BLACK; style.move_in_view = true; if (text_size.cx < Layout::Scale(18)) { style.align = TextInBoxMode::Alignment::RIGHT; TextInBox(canvas, Value, rc.right, y0, style, rc); } else TextInBox(canvas, Value, rc.right-Layout::Scale(18), y0, style, rc); }
static void Test(const fixed distance, const fixed altitude, const SpeedVector wind) { const GeoVector vector(distance, Angle::Zero()); const GlideState state(vector, fixed(2000), fixed(2000) + altitude, wind); const GlideResult result = MacCready::Solve(glide_settings, glide_polar, state); const fixed ld_ground = glide_polar.GetLDOverGround(vector.bearing, wind); const fixed mc = glide_polar.GetMC(); const fixed v_climb_progress = mc * ld_ground - state.head_wind; const fixed initial_glide_distance = state.altitude_difference * ld_ground; if (initial_glide_distance >= distance || (!positive(mc) && !positive(v_climb_progress))) { /* reachable by pure glide */ ok1(result.validity == GlideResult::Validity::OK); const fixed best_speed = glide_polar.GetBestGlideRatioSpeed(state.head_wind); const fixed best_sink = glide_polar.SinkRate(best_speed); const fixed ld_ground2 = positive(mc) ? ld_ground : (best_speed - state.head_wind) / best_sink; const fixed height_glide = distance / ld_ground2; const fixed height_climb = fixed(0); const fixed altitude_difference = altitude - height_glide; ok1(equals(result.head_wind, wind.norm)); ok1(equals(result.vector.distance, distance)); ok1(equals(result.height_climb, height_climb)); ok1(equals(result.height_glide, height_glide)); ok1(equals(result.altitude_difference, altitude_difference)); return; } if (!positive(v_climb_progress)) { /* excessive wind */ ok1(result.validity == GlideResult::Validity::WIND_EXCESSIVE); return; } /* const fixed drifted_distance = (distance - initial_glide_distance) * state.head_wind / v_climb_progress; */ const fixed drifted_height_climb = (distance - initial_glide_distance) * mc / v_climb_progress; const fixed drifted_height_glide = drifted_height_climb + state.altitude_difference; const fixed height_glide = drifted_height_glide; const fixed altitude_difference = altitude - height_glide; const fixed height_climb = drifted_height_climb; const fixed time_climb = height_climb / mc; const fixed time_glide = height_glide / glide_polar.GetSBestLD(); const fixed time_elapsed = time_climb + time_glide; /* more tolerance with strong wind because this unit test doesn't optimise pure glide */ const int accuracy = positive(altitude) && positive(wind.norm) ? (wind.norm > fixed(5) ? 5 : 10) : ACCURACY; ok1(result.validity == GlideResult::Validity::OK); ok1(equals(result.head_wind, wind.norm)); ok1(equals(result.vector.distance, distance)); ok1(equals(result.height_climb, height_climb, accuracy)); ok1(equals(result.height_glide, height_glide, accuracy)); ok1(equals(result.altitude_difference, altitude_difference, accuracy)); ok1(equals(result.time_elapsed, time_elapsed, accuracy)); }
/** * Returns true if the norm of the vector is non-zero. */ bool is_non_zero() const { return positive(norm); }
int ClientApp::verification(std::string testFilePath, cvac::DetectorPrx detector) { appData->isVerification = true; Purpose positive(POSITIVE, 0); RunSet runSet; std::string suffix; if(appData->videoInputType) { suffix = "mpg"; } else { suffix = "jpg"; } if(appData->multiclassDetection) { std::string testFileName1 = (m_detectorName + "1." + suffix); std::string testFileName2 = (m_detectorName + "2." + suffix); std::string testFileName3 = (m_detectorName + "3." + suffix); Purpose pOne(MULTICLASS, 1), pTwo(MULTICLASS, 2), pThree(MULTICLASS, 3); addToRunSet(runSet, testFilePath, testFileName1, pOne); addToRunSet(runSet, testFilePath, testFileName2, pTwo); addToRunSet(runSet, testFilePath, testFileName3, pThree); } else { cvac::localAndClientMsg(VLogger::DEBUG, NULL, "IceBoxTestClient-verification setting RunSet dir: %s\n", testFilePath.c_str()); std::string testFileName = (m_detectorName + "." + suffix); addToRunSet(runSet, testFilePath, testFileName, positive); // Path relative to CVAC.data dir } try { // Create our callback class so that we can be informed when process completes FinishedCallbackPtr finishCallback = new FinishedCallback(); Ice::CallbackPtr finishedAsync = Ice::newCallback(finishCallback, &FinishedCallback::finished); Ice::AsyncResultPtr asyncResult = detector->begin_process(ident, runSet, finishedAsync); // end_myFunction should be call from the "finished" callback //detector->end_process(asyncResult); // Wait for the processing to complete before exiting the app while (!finishCallback->hasFinished()) { cvac::sleep(100); } cvac::localAndClientMsg(VLogger::DEBUG_2, NULL, "IceBox test client: finished verification\n"); } catch (const Ice::Exception& ex) { cvac::localAndClientMsg(VLogger::WARN, NULL, "Ice- name(): %s\n", ex.ice_name().c_str()); cvac::localAndClientMsg(VLogger::WARN, NULL, "Ice- stackTrace(): \n%s\n", ex.ice_stackTrace().c_str()); cvac::localAndClientMsg(VLogger::WARN, NULL, "%s\n", ex.what()); return EXIT_FAILURE; } communicator()->shutdown(); if(appData->needSBDVerified) { if(false == SBDResultsOK()) { appData->detectionsVerified.push_back(false); // Add SBD-test failure } } // Scan for missed detections bool missedDetections = false; while(appData->detectionsVerified.size() > 0) { bool element = appData->detectionsVerified.back(); appData->detectionsVerified.pop_back(); if(false == element) { missedDetections = true; } } if(missedDetections) { return EXIT_FAILURE; } return EXIT_SUCCESS; }
/** * Returns smallest real solution. Valid only where check() has passed. * * @return smallest x value of solutions */ gcc_pure fixed solution_min() const { return positive(da) ? solution(false) : solution(true); }
void WaypointInfoWidget::Prepare(ContainerWindow &parent, const PixelRect &rc) { RowFormWidget::Prepare(parent, rc); const MoreData &basic = CommonInterface::Basic(); const DerivedInfo &calculated = CommonInterface::Calculated(); const ComputerSettings &settings = CommonInterface::GetComputerSettings(); StaticString<64> buffer; if (!waypoint.comment.empty()) AddMultiLine(waypoint.comment.c_str()); if (waypoint.radio_frequency.IsDefined() && waypoint.radio_frequency.Format(buffer.buffer(), buffer.capacity()) != nullptr) { buffer += _T(" MHz"); AddReadOnly(_("Radio frequency"), nullptr, buffer); } if (waypoint.runway.IsDirectionDefined()) buffer.UnsafeFormat(_T("%02u"), waypoint.runway.GetDirectionName()); else buffer.clear(); if (waypoint.runway.IsLengthDefined()) { if (!buffer.empty()) buffer += _T("; "); TCHAR length_buffer[16]; FormatSmallUserDistance(length_buffer, fixed(waypoint.runway.GetLength())); buffer += length_buffer; } if (!buffer.empty()) AddReadOnly(_("Runway"), nullptr, buffer); if (FormatGeoPoint(waypoint.location, buffer.buffer(), buffer.capacity()) != nullptr) AddReadOnly(_("Location"), nullptr, buffer); AddReadOnly(_("Elevation"), nullptr, FormatUserAltitude(waypoint.elevation)); if (basic.time_available && basic.date_time_utc.IsDatePlausible()) { const SunEphemeris::Result sun = SunEphemeris::CalcSunTimes(waypoint.location, basic.date_time_utc, settings.utc_offset); const BrokenTime sunrise = BreakHourOfDay(sun.time_of_sunrise); const BrokenTime sunset = BreakHourOfDay(sun.time_of_sunset); buffer.UnsafeFormat(_T("%02u:%02u - %02u:%02u"), sunrise.hour, sunrise.minute, sunset.hour, sunset.minute); AddReadOnly(_("Daylight time"), nullptr, buffer); } if (basic.location_available) { const GeoVector vector = basic.location.DistanceBearing(waypoint.location); TCHAR distance_buffer[32]; FormatUserDistanceSmart(vector.distance, distance_buffer, ARRAY_SIZE(distance_buffer)); FormatBearing(buffer.buffer(), buffer.capacity(), vector.bearing, distance_buffer); AddReadOnly(_("Bearing and Distance"), nullptr, buffer); } if (basic.location_available && basic.NavAltitudeAvailable() && settings.polar.glide_polar_task.IsValid()) { const GlideState glide_state(basic.location.DistanceBearing(waypoint.location), waypoint.elevation + settings.task.safety_height_arrival, basic.nav_altitude, calculated.GetWindOrZero()); GlidePolar gp0 = settings.polar.glide_polar_task; gp0.SetMC(fixed(0)); AddGlideResult(_("Alt. diff. MC 0"), MacCready::Solve(settings.task.glide, gp0, glide_state)); AddGlideResult(_("Alt. diff. MC safety"), MacCready::Solve(settings.task.glide, calculated.glide_polar_safety, glide_state)); AddGlideResult(_("Alt. diff. MC current"), MacCready::Solve(settings.task.glide, settings.polar.glide_polar_task, glide_state)); } if (basic.location_available && basic.NavAltitudeAvailable()) { const TaskBehaviour &task_behaviour = CommonInterface::GetComputerSettings().task; const fixed safety_height = task_behaviour.safety_height_arrival; const fixed target_altitude = waypoint.elevation + safety_height; const fixed delta_h = basic.nav_altitude - target_altitude; if (positive(delta_h)) { const fixed distance = basic.location.Distance(waypoint.location); const fixed gr = distance / delta_h; if (GradientValid(gr)) { buffer.UnsafeFormat(_T("%.1f"), (double)gr); AddReadOnly(_("Required glide ratio"), nullptr, buffer); } } } }
bool VegaVoiceMessage::Update(const NMEAInfo &basic, const DerivedInfo &calculated, const VoiceSettings &settings) { const fixed Time = basic.clock; TCHAR text[80]; switch(id) { case VV_GENERAL: // TODO feature: Allow this to be triggered to give generic alert // "INFO" break; case VV_CLIMBRATE: if (!settings.voice_climb_rate_enabled) return false; if (calculated.circling && positive(calculated.average)) { // Gives the average climb rate in user units every X seconds // while in circling mode // e.g. if average = 3.4 (user units) // Now: "CIRCLING THREE FOUR" // Later: "AVERAGE THREE POINT FOUR" _stprintf(text, _T(",%d"), VWI_CIRCLING); TextToDigitsSmall(text, Units::ToUserVSpeed(calculated.average)); DoSend(Time, text); return true; } break; case VV_TERRAIN: if (!settings.voice_terrain_enabled) return false; // TODO feature: final glide with terrain warning // CAUTION TERRAIN break; case VV_WAYPOINTDISTANCE: #ifdef OLD_TASK if (!calculated.circling && task.Valid()) { // Gives the distance to the active waypoint every X seconds, // optionally limited when at last 20 km to go? // calculated.LegDistanceToGo // e.g.: if distance is 13 (user units) // Now: "PLUS ONE THREE" // Later: "WAYPOINT DISTANCE THREE FOUR" // // height above/below final glide when // in final glide mode, e.g. // "WAYPOINT BELOW TWO HUNDRED" if (Units::ToUserUnit(calculated.WaypointDistance, Units::DistanceUnit) < 20.0) { if (!settings.EnableVoiceWaypointDistance) return false; _stprintf(text, _T(",%d"), VWI_PLUS); TextToDigitsLarge(text, Units::ToUserUnit(calculated.WaypointDistance, Units::DistanceUnit)); DoSend(time, text); return true; } else { if (calculated.FinalGlide) { if (!settings.EnableVoiceTaskAltitudeDifference) return false; // TODO feature: BELOW FOUR HUNDRED double tad = Units::ToUserUnit(calculated.TaskAltitudeDifference, Units::AltitudeUnit); if (fabs(tad)>100) { if (tad>0) { _stprintf(text, _T(",%d"), VWI_ABOVE); } else { _stprintf(text, _T(",%d"), VWI_BELOW); } TextToDigitsHuge(text, fabs(tad)); DoSend(time, text); return true; } } } } #endif break; case VV_MACCREADY: if (!settings.voice_mac_cready_enabled) return false; // TODO feature: report when not in auto maccready mode, if // vario has changed in last 3 seconds but hasn't changed // for more than one second // // Now: --- // Later: "MACCREADY THREE DECIMAL FOUR" break; case VV_NEWWAYPOINT: if (!settings.voice_new_waypoint_enabled) return false; #ifdef OLD_TASK static unsigned LastWaypoint = 1000; if (task.getActiveIndex() != LastWaypoint) { LastWaypoint = task.getActiveIndex(); // Reports that a new waypoint is active // e.g.: // Now: "INFO" // Later: "NEW WAYPOINT" _stprintf(text, _T(",%d"), VWI_INFO); DoSend(time, text); return true; } #endif break; case VV_INSECTOR: if (!settings.voice_in_sector_enabled) return false; #ifdef OLD_TASK if (calculated.IsInSector) { // Reports when the aircraft is in an AAT/task sector // e.g.: // Now: INFO // Later: "INSIDE SECTOR" _stprintf(text, _T(",%d"), VWI_INFO); DoSend(time, text); return true; } #endif break; case VV_AIRSPACE: if (!settings.voice_airspace_enabled) return false; #ifdef OLD_TASK if (calculated.IsInAirspace) { // Reports when the aircraft is inside airspace // e.g.: // Now: "WARNING AIRSPACE" // // Later give distance/height/direction? // Later: "WARNING AIRSPACE ABOVE" _stprintf(text, _T(",%d,%d,0"), VWI_WARNING, VWI_AIRSPACE); DoSend(time, text); return true; } #endif break; default: break; }; return false; }
static bool test_replay() { Directory::Create(_T("output/results")); std::ofstream f("output/results/res-sample.txt"); GlidePolar glide_polar(fixed(4.0)); Waypoints waypoints; AircraftState state_last; TaskBehaviour task_behaviour; task_behaviour.SetDefaults(); task_behaviour.auto_mc = true; TaskManager task_manager(task_behaviour, waypoints); TaskEventsPrint default_events(verbose); task_manager.SetTaskEvents(default_events); glide_polar.SetBallast(fixed(1.0)); task_manager.SetGlidePolar(glide_polar); OrderedTask* blank = new OrderedTask(task_manager.GetTaskBehaviour()); OrderedTask* t = task_load(blank); if (t) { task_manager.Commit(*t); task_manager.Resume(); } else { return false; } // task_manager.get_task_advance().get_advance_state() = TaskAdvance::AUTO; FileLineReaderA *reader = new FileLineReaderA(replay_file.c_str()); if (reader->error()) { delete reader; return false; } ReplayLoggerSim sim(reader); sim.state.netto_vario = fixed(0); bool do_print = verbose; unsigned print_counter=0; NMEAInfo basic; basic.Reset(); while (sim.Update(basic) && !sim.started) { } state_last = sim.state; sim.state.wind.norm = fixed(7); sim.state.wind.bearing = Angle::Degrees(330); fixed time_last = sim.state.time; // uncomment this to manually go to first tp // task_manager.incrementActiveTaskPoint(1); FlyingComputer flying_computer; flying_computer.Reset(); FlyingState flying_state; flying_state.Reset(); while (sim.Update(basic)) { if (sim.state.time>time_last) { n_samples++; flying_computer.Compute(glide_polar.GetVTakeoff(), sim.state, sim.state.time - time_last, flying_state); sim.state.flying = flying_state.flying; task_manager.Update(sim.state, state_last); task_manager.UpdateIdle(sim.state); task_manager.UpdateAutoMC(sim.state, fixed(0)); task_manager.SetTaskAdvance().SetArmed(true); state_last = sim.state; if (verbose>1) { sim.print(f); f.flush(); } if (do_print) { PrintHelper::taskmanager_print(task_manager, sim.state); } do_print = (++print_counter % output_skip ==0) && verbose; } time_last = sim.state.time; }; if (verbose) { PrintDistanceCounts(); printf("# task elapsed %d (s)\n", (int)task_manager.GetStats().total.time_elapsed); printf("# task speed %3.1f (kph)\n", (int)task_manager.GetStats().total.travelled.GetSpeed()*3.6); printf("# travelled distance %4.1f (km)\n", (double)task_manager.GetStats().total.travelled.GetDistance()/1000.0); printf("# scored distance %4.1f (km)\n", (double)task_manager.GetStats().distance_scored/1000.0); if (positive(task_manager.GetStats().total.time_elapsed)) { printf("# scored speed %3.1f (kph)\n", (double)task_manager.GetStats().distance_scored/(double)task_manager.GetStats().total.time_elapsed*3.6); } } return true; }
int main(int argc, char **argv) { plan_tests(66); // test constructor GeoPoint p1(Angle::Degrees(345.32), Angle::Degrees(-6.332)); ok1(p1.IsValid()); ok1(equals(p1, -6.332, 345.32)); // test normalize() p1.Normalize(); ok1(p1.IsValid()); ok1(equals(p1, -6.332, -14.68)); // test parametric() GeoPoint p2(Angle::Degrees(2), Angle::Degrees(1)); GeoPoint p3 = p1.Parametric(p2, fixed(5)); ok1(p2.IsValid()); ok1(p3.IsValid()); ok1(equals(p3, -1.332, -4.68)); // test interpolate GeoPoint p4 = p1.Interpolate(p3, fixed(0.5)); ok1(p4.IsValid()); ok1(equals(p4, -3.832, -9.68)); GeoPoint p5 = p1.Interpolate(p3, fixed(0.25)); ok1(p5.IsValid()); ok1(equals(p5, -5.082, -12.18)); // test * GeoPoint p6 = p2 * fixed(3.5); ok1(p6.IsValid()); ok1(equals(p6, 3.5, 7)); // test + p6 = p6 + p2; ok1(p6.IsValid()); ok1(equals(p6, 4.5, 9)); // test += p6 += p2; ok1(p6.IsValid()); ok1(equals(p6, 5.5, 11)); // test - p6 = p6 - p2; ok1(p6.IsValid()); ok1(equals(p6, 4.5, 9)); // test sort() ok1(!p1.Sort(p3)); ok1(p3.Sort(p1)); ok1(!p1.Sort(p4)); ok1(p4.Sort(p1)); ok1(!p1.Sort(p5)); ok1(p5.Sort(p1)); ok1(!p4.Sort(p3)); ok1(p3.Sort(p4)); ok1(!p5.Sort(p3)); ok1(p3.Sort(p5)); ok1(!p5.Sort(p4)); ok1(p4.Sort(p5)); // test distance() // // note: distance between p1 and p4 and between p3 and p4 is not // the same due to linear interpolation instead of real geographic // intermediate point calculation ok1(equals(p2.Distance(p6), 869326.653160)); ok1(equals(p6.Distance(p2), 869326.653160)); ok1(equals(p1.Distance(p5), 309562.219016)); ok1(equals(p1.Distance(p4), 619603.149273)); ok1(equals(p1.Distance(p3), 1240649.267606)); ok1(equals(p3.Distance(p4), 621053.760625)); // test bearing() // // note: the bearings p1 -> p5, p5 -> p4 and so on are not the same due to // linear interpolation instead of real geographic intermediate point // calculation ok1(equals(p2.Bearing(p6), 63.272424)); ok1(equals(p6.Bearing(p2), 243.608847)); ok1(equals(p1.Bearing(p5), 63.449343)); ok1(equals(p1.Bearing(p4), 63.582620)); ok1(equals(p1.Bearing(p3), 63.784526)); ok1(equals(p5.Bearing(p4), 63.466726)); ok1(equals(p5.Bearing(p3), 63.646072)); ok1(equals(p4.Bearing(p3), 63.540756)); ok1(equals(p5.Bearing(p6), 65.982854)); ok1(equals(p2.Bearing(p3), 250.786774)); // test distance_bearing() // note: should be the same output as bearing() and distance() GeoVector v = p2.DistanceBearing(p6); ok1(equals(v.distance, 869326.653160)); ok1(equals(v.bearing, 63.272424)); // test intermediate_point() GeoPoint p7(Angle::Zero(), Angle::Zero()); ok1(p7.IsValid()); GeoPoint p8 = p7.IntermediatePoint(p2, fixed(100000)); ok1(p8.IsValid()); ok1(equals(p8, 0.402274, 0.804342)); ok1(equals(p8.Distance(p7), 100000)); GeoPoint p9 = p7.IntermediatePoint(p2, fixed(100000000)); ok1(p9.IsValid()); ok1(equals(p9, p2)); // test projected_distance() ok1(equals(p8.ProjectedDistance(p7, p2), 100000)); ok1(equals(p4.ProjectedDistance(p1, p3), 619599.304393)); ok1(equals((p2 * fixed(2)).ProjectedDistance(p2, p6), 248567.832772)); // Tests moved here from test_fixed.cpp GeoPoint l1(Angle::Zero(), Angle::Zero()); ok1(l1.IsValid()); GeoPoint l2(Angle::Degrees(-0.3), Angle::Degrees(1.0)); ok1(l2.IsValid()); GeoPoint l3(Angle::Degrees(0.00001), Angle::Zero()); ok1(l3.IsValid()); GeoPoint l4(Angle::Degrees(10), Angle::Zero()); ok1(l4.IsValid()); l4.SetInvalid(); ok1(!l4.IsValid()); bool find_lat_lon_okay = true; for (Angle bearing = Angle::Zero(); bearing < Angle::FullCircle(); bearing += Angle::Degrees(5)) { GeoPoint p_test = FindLatitudeLongitude(p1, bearing, fixed(50000)); find_lat_lon_okay = equals(p_test.Distance(p1), 50000) && find_lat_lon_okay; } ok1(find_lat_lon_okay); v = l1.DistanceBearing(l2); printf("Dist %g bearing %d\n", FIXED_DOUBLE(v.distance), FIXED_INT(v.bearing.Degrees())); // 116090 @ 343 v = l1.DistanceBearing(l3); printf("Dist %g bearing %d\n", FIXED_DOUBLE(v.distance), FIXED_INT(v.bearing.Degrees())); ok(positive(v.distance) && v.distance < fixed(2), "earth distance short", 0); v = l1.DistanceBearing(l4); printf("Dist %g bearing %d\n", FIXED_DOUBLE(v.distance), FIXED_INT(v.bearing.Degrees())); GeoPoint p10(GeoPoint::Invalid()); ok1(!p10.IsValid()); return exit_status(); }
void ThermalBandRenderer::_DrawThermalBand(const MoreData &basic, const DerivedInfo& calculated, const ComputerSettings &settings_computer, ChartRenderer &chart, const TaskBehaviour& task_props, const bool is_infobox, const OrderedTaskSettings *ordered_props) const { const ThermalBandInfo &thermal_band = calculated.thermal_band; // calculate height above safety height auto 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::N_BUCKETS]; fixed ht[ThermalBandInfo::N_BUCKETS]; for (unsigned i = 0; i < ThermalBandInfo::N_BUCKETS; ++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 auto wthis = thermal_band.thermal_profile_w[i] / thermal_band.thermal_profile_n[i]; ht[numtherm] = i * calculated.thermal_band.max_thermal_height / ThermalBandInfo::N_BUCKETS; 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 ? nullptr : &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 ScopeAlphaBlend alpha_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)); } }
bool IsDefined() const { return positive(score); }
/** * Test whether airspace boundary is the terrain * * @return True if this altitude limit is the terrain */ bool IsTerrain() const { return !positive(altitude_above_terrain) && type == AGL; }
void FlarmComputer::Process(FlarmData &flarm, const FlarmData &last_flarm, const NMEAInfo &basic) { // Cleanup old calculation instances if (basic.time_available) flarm_calculations.CleanUp(basic.time); // if (FLARM data is available) if (!flarm.IsDetected()) return; fixed north_to_latitude(0); fixed east_to_longitude(0); if (basic.location_available) { // Precalculate relative east and north projection to lat/lon // for Location calculations of each target constexpr Angle delta_lat = Angle::Degrees(0.01); constexpr Angle delta_lon = Angle::Degrees(0.01); GeoPoint plat = basic.location; plat.latitude += delta_lat; GeoPoint plon = basic.location; plon.longitude += delta_lon; fixed dlat = basic.location.Distance(plat); fixed dlon = basic.location.Distance(plon); if (positive(fabs(dlat)) && positive(fabs(dlon))) { north_to_latitude = delta_lat.Degrees() / dlat; east_to_longitude = delta_lon.Degrees() / dlon; } } // for each item in traffic for (auto &traffic : flarm.traffic.list) { // if we don't know the target's name yet if (!traffic.HasName()) { // lookup the name of this target's id const TCHAR *fname = FlarmDetails::LookupCallsign(traffic.id); if (fname != NULL) traffic.name = fname; } // Calculate distance traffic.distance = SmallHypot(traffic.relative_north, traffic.relative_east); // Calculate Location traffic.location_available = basic.location_available; if (traffic.location_available) { traffic.location.latitude = Angle::Degrees(traffic.relative_north * north_to_latitude) + basic.location.latitude; traffic.location.longitude = Angle::Degrees(traffic.relative_east * east_to_longitude) + basic.location.longitude; } // Calculate absolute altitude traffic.altitude_available = basic.gps_altitude_available; if (traffic.altitude_available) traffic.altitude = traffic.relative_altitude + RoughAltitude(basic.gps_altitude); // Calculate average climb rate traffic.climb_rate_avg30s_available = traffic.altitude_available; if (traffic.climb_rate_avg30s_available) traffic.climb_rate_avg30s = flarm_calculations.Average30s(traffic.id, basic.time, traffic.altitude); // The following calculations are only relevant for targets // where information is missing if (traffic.track_received && traffic.turn_rate_received && traffic.speed_received && traffic.climb_rate_received) continue; // Check if the target has been seen before in the last seconds const FlarmTraffic *last_traffic = last_flarm.traffic.FindTraffic(traffic.id); if (last_traffic == NULL || !last_traffic->valid) continue; // Calculate the time difference between now and the last contact fixed dt = traffic.valid.GetTimeDifference(last_traffic->valid); if (positive(dt)) { // Calculate the immediate climb rate if (!traffic.climb_rate_received) traffic.climb_rate = (traffic.relative_altitude - last_traffic->relative_altitude) / dt; } else { // Since the time difference is zero (or negative) // we can just copy the old values if (!traffic.climb_rate_received) traffic.climb_rate = last_traffic->climb_rate; } if (positive(dt) && traffic.location_available && last_traffic->location_available) { // Calculate the GeoVector between now and the last contact GeoVector vec = last_traffic->location.DistanceBearing(traffic.location); if (!traffic.track_received) traffic.track = vec.bearing; // Calculate the turn rate if (!traffic.turn_rate_received) { Angle turn_rate = traffic.track - last_traffic->track; traffic.turn_rate = turn_rate.AsDelta().Degrees() / dt; } // Calculate the speed [m/s] if (!traffic.speed_received) traffic.speed = vec.distance / dt; } else { // Since the time difference is zero (or negative) // we can just copy the old values if (!traffic.track_received) traffic.track = last_traffic->track; if (!traffic.turn_rate_received) traffic.turn_rate = last_traffic->turn_rate; if (!traffic.speed_received) traffic.speed = last_traffic->speed; } } }