Example #1
0
int main(int argc, char **argv)
{
#ifdef USE_WGS84
  plan_tests(9 + 36);
#else
  plan_tests(9 + 36 + 18);
#endif

  const GeoPoint a(Angle::Degrees(7.7061111111111114),
                   Angle::Degrees(51.051944444444445));
  const GeoPoint b(Angle::Degrees(7.599444444444444),
                   Angle::Degrees(51.099444444444444));
  const GeoPoint c(Angle::Degrees(4.599444444444444),
                   Angle::Degrees(47.099444444444444));

  fixed distance = Distance(a, b);
#ifdef USE_WGS84
  ok1(distance > fixed(9150) && distance < fixed(9160));
#else
  ok1(distance > fixed(9130) && distance < fixed(9140));
#endif

  Angle bearing = Bearing(a, b);
  ok1(bearing.Degrees() > fixed(304));
  ok1(bearing.Degrees() < fixed(306));

  bearing = Bearing(b, a);
  ok1(bearing.Degrees() > fixed(124));
  ok1(bearing.Degrees() < fixed(126));

  distance = ProjectedDistance(a, b, a);
  ok1(is_zero(distance));
  distance = ProjectedDistance(a, b, b);

#ifdef USE_WGS84
  ok1(distance > fixed(9150) && distance < fixed(9180));
#else
  ok1(distance > fixed(9120) && distance < fixed(9140));
#endif

  const GeoPoint middle(a.longitude.Fraction(b.longitude, fixed(0.5)),
                        a.latitude.Fraction(b.latitude, fixed(0.5)));
  distance = ProjectedDistance(a, b, middle);
#ifdef USE_WGS84
  ok1(distance > fixed(9150/2) && distance < fixed(9180/2));
#else
  ok1(distance > fixed(9100/2) && distance < fixed(9140/2));
#endif

  fixed big_distance = Distance(a, c);
  ok1(big_distance > fixed(494000) && big_distance < fixed(495000));

  TestLinearDistance();

  return exit_status();
}
Example #2
0
//------------------------------------------------------------------------------
inline Angle
Canvas::normalize(const Angle& a) const
  {
  double r = fmod(a.Radians(), 2.0 * M_PI);
  Angle ra = Angle::Radians(r);
#ifndef NDEBUG
  double ad = a.Degrees();
  double rd = ra.Degrees();
  std::cerr << __LINE__ << ": " << ad << ", " << rd << std::endl;
#endif
  return (ra);
  }
Example #3
0
//------------------------------------------------------------------------------
void
Canvas::DrawAnnulus(int x, int y,
                    unsigned inner_r, unsigned outer_r,
                    Angle start, Angle end)
  {
  QPainterPath p;
  QRectF ri(x - inner_r, y - inner_r, 2 * inner_r, 2 * inner_r);
  QRectF ro(x - outer_r, y - outer_r, 2 * outer_r, 2 * outer_r);
  // Draw the inner radius of the annulus.
  p.arcMoveTo(ri, start.Degrees());
  p.arcTo(ri, start.Degrees(), end.Degrees() - start.Degrees());
  if (start != end)
    { // Only draw the end caps when needed.
    // The currentPosition() will be at the end of the inner circle. Draw
    // one side of the annulus.
    // \todo This doesn't work because Angle(360) != Angle(0)!
    double xx = (outer_r - inner_r) * cos(end.Radians()) +
                p.currentPosition().rx();
    double yy = (outer_r - inner_r) * -sin(end.Radians()) +
                p.currentPosition().ry();
    p.lineTo(xx, yy);
    }
  else
    p.arcMoveTo(ro, end.Degrees());  // Set up for the outer circle.
  // The currentPosition() will be at the 'end' of the outer circle. Draw the
  // outer to the start.
  p.arcTo(ro, end.Degrees(), start.Degrees() - end.Degrees());
  if (start != end)
    {// And close it off to finish up.
    p.closeSubpath();
    }
  this->pushObject(p, this->pen(), this->brush());
  }
Example #4
0
//------------------------------------------------------------------------------
void
Canvas::DrawSegment(int x,
                    int y,
                    unsigned radius,
                    Angle start,
                    Angle end,
                    bool horizon)
  {
  QPainterPath p;
  QRectF r(x - radius, y - radius, 2 * radius, 2 * radius);
  p.arcMoveTo(r, start.Degrees());
  p.arcTo(r, start.Degrees(), end.Degrees() - start.Degrees());
  this->pushObject(p, this->pen(), this->brush());
  }
Example #5
0
void
DigitEntry::SetLongitude(Angle value)
{
  // TODO: support all CoordinateFormats

  value = value.AsBearing();

  assert(length == 9);
  assert(columns[0].type == Column::Type::EAST_WEST);
  assert(columns[1].type == Column::Type::DIGIT19);
  assert(columns[2].type == Column::Type::DIGIT);
  assert(columns[4].type == Column::Type::DIGIT6);
  assert(columns[5].type == Column::Type::DIGIT);
  assert(columns[7].type == Column::Type::DIGIT6);
  assert(columns[8].type == Column::Type::DIGIT);

  columns[0].value = negative(value.Native());

  const fixed degrees = fabs(value.Degrees());
  const unsigned i_degrees = std::min(unsigned(degrees), 180u);
  const unsigned full_seconds = unsigned(degrees * 3600u) % 3600u;
  const unsigned minutes = std::min(full_seconds / 60u, 59u);
  const unsigned seconds = full_seconds % 60u;

  columns[1].value = i_degrees / 10;
  columns[2].value = i_degrees % 10;

  columns[4].value = minutes / 10;
  columns[5].value = minutes % 10;

  columns[7].value = seconds / 10;
  columns[8].value = seconds % 10;

  Invalidate();
}
Example #6
0
/**
 * Paints an arrow into the direction of the current task leg
 * @param canvas The canvas to paint on
 */
void
FlarmTrafficControl::PaintTaskDirection(Canvas &canvas) const
{
  if (negative(task_direction.Degrees()))
    return;

  canvas.Select(look.radar_pen);
  canvas.SelectHollowBrush();

  RasterPoint triangle[4];
  triangle[0].x = 0;
  triangle[0].y = -radius / Layout::FastScale(1) + 15;
  triangle[1].x = 7;
  triangle[1].y = triangle[0].y + 30;
  triangle[2].x = -triangle[1].x;
  triangle[2].y = triangle[1].y;
  triangle[3].x = triangle[0].x;
  triangle[3].y = triangle[0].y;

  PolygonRotateShift(triangle, 4, radar_mid,
                     task_direction - (enable_north_up ?
                                       Angle::Zero() : heading));

  // Draw the arrow
  canvas.DrawPolygon(triangle, 4);
}
Example #7
0
gcc_const
static char
CalculateZoneLetter(const Angle latitude)
{
    static constexpr char letters[] = "CDEFGHJKLMNPQRSTUVWXX";
    unsigned index = (unsigned)((latitude.Degrees() + fixed(80)) / 8);
    return (index < ARRAY_SIZE(letters)) ? letters[index] : '\0';
}
Example #8
0
static void
Main()
{
  Angle value = Angle::Zero();
  if (!AngleEntryDialog(_T("The caption"), value))
    return;

  printf("%u\n", uround(value.Degrees()));
}
Example #9
0
Angle
SunEphemeris::GetHourAngleTwilight(Angle lat, Angle declin)
{
  Angle df1 = Angle::Degrees(6);

  // Correction: different sign at southern hemisphere
  if (negative(lat.Degrees()))
    df1.Flip();

  fixed fi = (declin + df1).tan() * lat.tan();
  return Angle::asin(fi) + Angle::QuarterCircle();
}
Example #10
0
Angle
SunEphemeris::GetHourAngle(Angle lat, Angle declin)
{
  Angle dfo = Angle::Degrees(SUN_DIAMETER / 2 + AIR_REFRACTION);

  // Correction: different sign at southern hemisphere
  if (negative(lat.Degrees()))
    dfo.Flip();

  fixed fo = (declin + dfo).tan() * lat.tan();
  return Angle::asin(fo) + Angle::QuarterCircle();
}
Example #11
0
void
Canvas::DrawSegment(PixelScalar x, PixelScalar y, UPixelScalar radius,
                Angle start, Angle end, bool horizon)
{
  // XXX horizon

  x += x_offset;
  y += y_offset;

  if (!brush.IsHollow())
    ::filledPieColor(surface, x, y, radius, 
                     (int)start.Degrees() - 90,
                     (int)end.Degrees() - 90,
                     brush.GetColor().GFXColor());

  if (pen_over_brush())
    ::pieColor(surface, x, y, radius, 
               (int)start.Degrees() - 90,
               (int)end.Degrees() - 90,
               pen.GetColor().GFXColor());
}
Example #12
0
  CanvasRotateShift(const RasterPoint pos, Angle angle,
                    const int scale = 100) {
#ifdef USE_GLSL
    glm::mat4 matrix = glm::rotate(glm::translate(glm::mat4(),
                                                  glm::vec3(pos.x, pos.y, 0)),
                                   GLfloat(angle.Degrees()),
                                   glm::vec3(0, 0, 1));
    float gl_scale = scale / 100.f;
    if (Layout::ScaleSupported())
      gl_scale *= Layout::scale_1024 / 1024.f;
    matrix = glm::scale(matrix, glm::vec3(gl_scale));
    glUniformMatrix4fv(OpenGL::solid_modelview, 1, GL_FALSE,
                       glm::value_ptr(matrix));
#else
    glPushMatrix();

#ifdef HAVE_GLES
    glTranslatex((GLfixed)pos.x << 16, (GLfixed)pos.y << 16, 0);
    GLfixed fixed_angle = angle.Degrees() * (1<<16);
    glRotatex(fixed_angle, 0, 0, 1<<16);
#else
    glTranslatef(pos.x, pos.y, 0.);
    glRotatef((GLfloat)angle.Degrees(), 0., 0., 1.);
#endif

#ifdef HAVE_GLES
    GLfixed gl_scale = ((GLfixed) scale << 16) / 100;
    if (Layout::ScaleSupported())
      gl_scale = (gl_scale * Layout::scale_1024) >> 10;
    glScalex(gl_scale, gl_scale, (GLfixed)1 << 16);
#else
    float gl_scale = scale / 100.f;
    if (Layout::ScaleSupported())
      gl_scale *= Layout::scale_1024 / 1024.f;
    glScalef(gl_scale, gl_scale, 1.);
#endif
#endif /* USE_GLSL */
  };
Example #13
0
Angle
SunEphemeris::GetHourAngleTwilight(Angle lat, Angle declin)
{
  Angle df1 = Angle::Degrees(fixed(6));

  // Correction: different sign at southern hemisphere
  if (negative(lat.Degrees()))
    df1.Flip();

  fixed fi = (declin + df1).tan() * lat.tan();
  fi = asin(fi) + fixed_half_pi;

  return Angle::Radians(fi);
}
Example #14
0
char
UTM::CalculateZoneLetter(const Angle latitude)
{
  fixed degrees = latitude.Degrees();

  if (degrees >= fixed(84))
    return '\0';
  if (degrees >= fixed(72))
    return 'X';
  if (degrees >= fixed(64))
    return 'W';
  if (degrees >= fixed(56))
    return 'V';
  if (degrees >= fixed(48))
    return 'U';
  if (degrees >= fixed(40))
    return 'T';
  if (degrees >= fixed(32))
    return 'S';
  if (degrees >= fixed(24))
    return 'R';
  if (degrees >= fixed(16))
    return 'Q';
  if (degrees >= fixed(8))
    return 'P';
  if (degrees >= fixed(0))
    return 'N';
  if (degrees >= fixed(-8))
    return 'M';
  if (degrees >= fixed(-16))
    return 'L';
  if (degrees >= fixed(-24))
    return 'K';
  if (degrees >= fixed(-32))
    return 'J';
  if (degrees >= fixed(-40))
    return 'H';
  if (degrees >= fixed(-48))
    return 'G';
  if (degrees >= fixed(-56))
    return 'F';
  if (degrees >= fixed(-64))
    return 'E';
  if (degrees >= fixed(-72))
    return 'D';
  if (degrees >= fixed(-80))
    return 'C';

  return '\0';
}
Example #15
0
int main(int argc, char **argv)
{
  Args args(argc, argv, "DRIVER FILE");
  std::unique_ptr<DebugReplay> replay(CreateDebugReplay(args));
  if (!replay)
    return EXIT_FAILURE;

  args.ExpectEnd();

  printf("# time quality wind_bearing (deg) wind_speed (m/s)\n");

  CirclingSettings circling_settings;
  circling_settings.SetDefaults();

  CirclingComputer circling_computer;
  CirclingWind circling_wind;

  while (replay->Next()) {
    const bool last_circling = replay->Calculated().circling;

    circling_computer.TurnRate(replay->SetCalculated(),
                               replay->Basic(),
                               replay->Calculated().flight);
    circling_computer.Turning(replay->SetCalculated(),
                              replay->Basic(),
                              replay->Calculated().flight,
                              circling_settings);

    if (replay->Calculated().circling != last_circling)
      circling_wind.NewFlightMode(replay->Calculated());

    CirclingWind::Result result = circling_wind.NewSample(replay->Basic());
    if (result.quality > 0) {
      fixed mag = result.wind.Magnitude();

      Angle bearing;
      if (result.wind.y == fixed(0) && result.wind.x == fixed(0))
        bearing = Angle::Zero();
      else
        bearing = Angle::FromXY(result.wind.x, result.wind.y).AsBearing();

      TCHAR time_buffer[32];
      FormatTime(time_buffer, replay->Basic().time);

      _tprintf(_T("%s %d %d %g\n"),
               time_buffer, result.quality, (int)bearing.Degrees(), (double)mag);
    }
  }
}
Example #16
0
void
DigitEntry::SetValue(Angle value)
{
    assert(length == 3);
    assert(columns[0].type == Column::Type::DIGIT36);
    assert(columns[1].type == Column::Type::DIGIT);
    assert(columns[2].type == Column::Type::DEGREES);

    int degrees = iround(value.Degrees());

    columns[0].value = degrees / 10;
    columns[1].value = degrees % 10;

    Invalidate();
}
Example #17
0
int main(int argc, char **argv)
{
  Args args(argc, argv, "DRIVER FILE");
  DebugReplay *replay = CreateDebugReplay(args);
  if (replay == NULL)
    return EXIT_FAILURE;

  args.ExpectEnd();

  printf("# time quality wind_bearing (deg) wind_speed (m/s)\n");

  CirclingComputer circling_computer;
  CirclingWind circling_wind;

  while (replay->Next()) {
    circling_computer.TurnRate(replay->SetCalculated(),
                               replay->Basic(), replay->LastBasic(),
                               replay->Calculated(), replay->LastCalculated());
    circling_computer.Turning(replay->SetCalculated(),
                              replay->Basic(), replay->LastBasic(),
                              replay->Calculated(), replay->LastCalculated(),
                              replay->GetComputerSettings());

    if ((replay->LastCalculated().turn_mode == CirclingMode::POSSIBLE_CLIMB &&
         replay->Calculated().turn_mode == CirclingMode::CLIMB) ||
        (replay->LastCalculated().turn_mode == CirclingMode::POSSIBLE_CRUISE &&
         replay->Calculated().turn_mode == CirclingMode::CRUISE))
      circling_wind.NewFlightMode(replay->Calculated());

    CirclingWind::Result result = circling_wind.NewSample(replay->Basic());
    if (result.quality > 0) {
      fixed mag = hypot(result.wind.x, result.wind.y);

      Angle bearing;
      if (result.wind.y == fixed_zero && result.wind.x == fixed_zero)
        bearing = Angle::Zero();
      else
        bearing = Angle::Radians(atan2(result.wind.y, result.wind.x)).AsBearing();

      printf("%d %d %d %g\n",
             (int)replay->Basic().time,
             result.quality, (int)bearing.Degrees(), (double)mag);
    }
  }

  delete replay;
}
Example #18
0
/**
 * This function creates some simulated traffic for FLARM debugging
 * @param GPS_INFO Pointer to the NMEA_INFO struct
 */
void
Simulator::GenerateFLARMTraffic(NMEAInfo &basic)
{
  static int i = 90;

  i++;
  if (i > 255)
    i = 0;

  if (i > 80)
    return;

  const Angle angle = Angle::Degrees(fixed((i * 360) / 255)).AsBearing();
  Angle dangle = (angle + Angle::Degrees(fixed(120))).AsBearing();
  Angle hangle = dangle.Flipped().AsBearing();

  int alt = (angle.ifastsine()) / 7;
  int north = (angle.ifastsine()) / 2 - 200;
  int east = (angle.ifastcosine()) / 1.5;
  int track = -(int)angle.AsBearing().Degrees();
  unsigned alarm_level = (i % 30 > 13 ? 0 : (i % 30 > 5 ? 2 : 1));

  NMEAParser parser(true);
  char buffer[50];

  // PFLAA,<AlarmLevel>,<RelativeNorth>,<RelativeEast>,<RelativeVertical>,
  //   <IDType>,<ID>,<Track>,<TurnRate>,<GroundSpeed>,<ClimbRate>,<AcftType>
  sprintf(buffer, "$PFLAA,%d,%d,%d,%d,2,DDA85C,%d,0,35,0,1",
          alarm_level, north, east, alt, track);
  parser.ParseLine(buffer, basic);

  alt = (angle.ifastcosine()) / 10;
  north = (dangle.ifastsine()) / 1.20 + 300;
  east = (dangle.ifastcosine()) + 500;
  track = (int)hangle.Degrees();

  // PFLAA,<AlarmLevel>,<RelativeNorth>,<RelativeEast>,<RelativeVertical>,
  //   <IDType>,<ID>,<Track>,<TurnRate>,<GroundSpeed>,<ClimbRate>,<AcftType>
  sprintf(buffer, "$PFLAA,0,%d,%d,%d,2,AA9146,,,,,1",
          north, east, alt);
  parser.ParseLine(buffer, basic);

  // PFLAU,<RX>,<TX>,<GPS>,<Power>,<AlarmLevel>,<RelativeBearing>,<AlarmType>,
  //   <RelativeVertical>,<RelativeDistance>(,<ID>)
  sprintf(buffer, "$PFLAU,2,1,2,1,%d", alarm_level);
  parser.ParseLine(buffer, basic);
}
Example #19
0
void
DigitEntry::SetLongitude(Angle value, CoordinateFormat format)
{
    // Longitude in floating point degrees
    value = value.AsDelta();
    const auto degrees = fabs(value.Degrees());

    // Check the first three columns here
    assert(columns[0].type == Column::Type::EAST_WEST);
    assert(columns[1].type == Column::Type::DIGIT19);
    assert(columns[2].type == Column::Type::DIGIT);
    columns[0].value = value.IsNegative();

    // Set up and check the remaining digits
    SetDigits(degrees, format, false);

    Invalidate();
}
Example #20
0
void
DigitEntry::SetLatitude(Angle value, CoordinateFormat format)
{
  // Latitude in floating point degrees
  value = value.AsDelta();
  const fixed degrees = fabs(value.Degrees());

  // Check the first three columns
  assert(columns[0].type == Column::Type::NORTH_SOUTH);
  assert(columns[1].type == Column::Type::DIGIT);
  assert(columns[2].type == Column::Type::DIGIT);
  columns[0].value = negative(value.Native());

  // Set up and check the remaining digits
  SetDigits(degrees, format, true);

  Invalidate();
}
Example #21
0
void
TaskAutoPilot::update_cruise_bearing(const TaskAccessor& task,
                                     const AircraftState& state,
                                     const fixed timestep)
{
    const ElementStat &stat = task.leg_stats();
    Angle bct = stat.solution_remaining.cruise_track_bearing;
    Angle bearing;

    if (current_has_target(task)) {
        bearing = stat.solution_remaining.vector.bearing;

        if (parms.enable_bestcruisetrack && (stat.solution_remaining.vector.distance>fixed_1000)) {
            bearing = bct;
        }

    } else {
        bearing = state.location.Bearing(target(task));
    }

    if (positive(state.wind.norm) && positive(state.true_airspeed)) {
        const fixed sintheta = (state.wind.bearing-bearing).sin();
        if (fabs(sintheta)>fixed(0.0001)) {
            bearing +=
                Angle::Radians(asin(sintheta * state.wind.norm / state.true_airspeed));
        }
    }
    Angle diff = (bearing-heading).AsDelta();
    fixed d = diff.Degrees();
    fixed max_turn = parms.turn_speed*timestep;
    heading += Angle::Degrees(max(-max_turn, min(max_turn, d)));
    if (positive(parms.bearing_noise)) {
        heading += heading_deviation()*timestep;
    }
    heading = heading.AsBearing();
}
Example #22
0
void
DataNode::SetAttribute(const TCHAR *name, Angle value)
{
  SetAttribute(name, value.Degrees());
}
Example #23
0
static inline void WriteAngle(TextWriter &writer, Angle value) {
    WriteFixed(writer, value.Degrees());
}
Example #24
0
static inline bool
equals(const Angle a, double b)
{
  return equals(a.Degrees(), b);
}
Example #25
0
static inline bool
equals(const Angle a, int b)
{
  return equals(a.Degrees(), double(b));
}
Example #26
0
 void Push(lua_State *L, Angle value) {
   Push(L, value.Degrees());
 }
Example #27
0
static void
TestAATPoint()
{
  OrderedTask task(task_behaviour);
  task.Append(StartPoint(new CylinderZone(wp1->location, 500),
                         WaypointPtr(wp1),
                         task_behaviour,
                         ordered_task_settings.start_constraints));
  task.Append(AATPoint(new CylinderZone(wp2->location, 10000),
                       WaypointPtr(wp2),
                       task_behaviour));
  task.Append(FinishPoint(new CylinderZone(wp3->location, 500),
                          WaypointPtr(wp3),
                          task_behaviour,
                          ordered_task_settings.finish_constraints));
  task.SetActiveTaskPoint(1);
  task.UpdateGeometry();
  ok1(task.CheckTask());

  AATPoint &ap = (AATPoint &)task.GetPoint(1);

  ok1(!ap.IsTargetLocked());
  ok1(equals(ap.GetTargetLocation(), wp2->location));
  ap.LockTarget(true);
  ok1(ap.IsTargetLocked());
  ok1(equals(ap.GetTargetLocation(), wp2->location));

  GeoPoint target = MakeGeoPoint(0, 45.31);
  ap.SetTarget(target);
  ok1(ap.IsTargetLocked());
  ok1(equals(ap.GetTargetLocation(), wp2->location));

  ap.SetTarget(target, true);
  ok1(ap.IsTargetLocked());
  ok1(equals(ap.GetTargetLocation(), target));

  RangeAndRadial rar = ap.GetTargetRangeRadial();
  ok1(equals(rar.range, 0.1112, 1000));
  ok1(equals(rar.radial.Degrees(), 0, 200));

  target = MakeGeoPoint(0, 45.29);
  ap.SetTarget(target, true);
  rar = ap.GetTargetRangeRadial();
  ok1(equals(rar.range, -0.1112, 1000));
  ok1(equals(rar.radial.Degrees(), 180, 200) ||
      equals(rar.radial.Degrees(), -180, 200));

  target = MakeGeoPoint(-0.05, 45.3);
  ap.SetTarget(target, true);
  rar = ap.GetTargetRangeRadial();
  ok1(equals(rar.range, 0.39217));
  ok1(equals(rar.radial.Degrees(), -89.98));

  target = MakeGeoPoint(0.05, 45.3);
  ap.SetTarget(target, true);
  rar = ap.GetTargetRangeRadial();
  ok1(equals(rar.range, 0.39217));
  ok1(equals(rar.radial.Degrees(), 89.98));

  for (int radial = -170; radial <= 170; radial += 10) {
    const Angle radial2 = Angle::Degrees(radial);

    for (int range = 10; range <= 100; range += 10) {
      const double range2((radial >= -90 && radial <= 90
                           ? range : -range) / 100.);

      ap.SetTarget(RangeAndRadial{range2, radial2}, task.GetTaskProjection());
      rar = ap.GetTargetRangeRadial();
      ok1(equals(rar.range, range2, 100));
      ok1(equals(rar.radial.Degrees(), radial2.Degrees(), 100));
    }
  }
}
Example #28
0
void
RowFormWidget::LoadValue(unsigned i, Angle value)
{
  LoadValue(i, value.Degrees());
}
Example #29
0
gcc_const
static int32_t
AngleToLX(Angle value)
{
    return ToBE32((int32_t)(value.Degrees() * 60000));
}
Example #30
0
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;

  double north_to_latitude(0);
  double 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;

    double dlat = basic.location.DistanceS(plat);
    double dlon = basic.location.DistanceS(plon);

    if (fabs(dlat) > 0 && fabs(dlon) > 0) {
      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 = hypot(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
    double dt = traffic.valid.GetTimeDifference(last_traffic->valid);
    if (dt > 0) {
      // 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 (dt > 0 &&
        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;
    }
  }
}