Beispiel #1
0
inline void
SkyLinesTracking::Client::OnDatagramReceived(void *data, size_t length)
{
    Header &header = *(Header *)data;
    if (length < sizeof(header))
        return;

    const uint16_t received_crc = FromBE16(header.crc);
    header.crc = 0;

    const uint16_t calculated_crc = UpdateCRC16CCITT(data, length, 0);
    if (received_crc != calculated_crc)
        return;

    const ACKPacket &ack = *(const ACKPacket *)data;
    const TrafficResponsePacket &traffic = *(const TrafficResponsePacket *)data;
    const UserNameResponsePacket &user_name =
        *(const UserNameResponsePacket *)data;
    const auto &wave = *(const WaveResponsePacket *)data;
    const auto &thermal = *(const ThermalResponsePacket *)data;

    switch ((Type)FromBE16(header.type)) {
    case PING:
    case FIX:
    case TRAFFIC_REQUEST:
    case USER_NAME_REQUEST:
    case WAVE_SUBMIT:
    case WAVE_REQUEST:
    case THERMAL_SUBMIT:
    case THERMAL_REQUEST:
        break;

    case ACK:
        handler->OnAck(FromBE16(ack.id));
        break;

    case TRAFFIC_RESPONSE:
        OnTrafficReceived(traffic, length);
        break;

    case USER_NAME_RESPONSE:
        OnUserNameReceived(user_name, length);
        break;

    case WAVE_RESPONSE:
        OnWaveReceived(wave, length);
        break;

    case THERMAL_RESPONSE:
        OnThermalReceived(thermal, length);
        break;
    }
}
Beispiel #2
0
bool
CAI302::DownloadPilot(Port &port, const Pilot &pilot, unsigned ordinal,
                      OperationEnvironment &env)
{
  char buffer[256];
  snprintf(buffer, sizeof(buffer),
           "O,%-24s,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d\r",
           pilot.name,
           (ordinal << 8) | pilot.old_units,
           pilot.old_temperatur_units,
           pilot.sink_tone,
           pilot.total_energy_final_glide,
           pilot.show_final_glide_altitude_difference,
           pilot.map_datum,
           FromBE16(pilot.approach_radius),
           FromBE16(pilot.arrival_radius),
           FromBE16(pilot.enroute_logging_interval),
           FromBE16(pilot.close_logging_interval),
           FromBE16(pilot.time_between_flight_logs),
           FromBE16(pilot.minimum_speed_to_force_flight_logging),
           pilot.stf_dead_band,
           pilot.reserved_vario,
           FromBE16(pilot.unit_word),
           FromBE16(pilot.margin_height));

  return DownloadCommand(port, buffer, env);
}
Beispiel #3
0
static void
HandleExtConfig(FILE *file, const struct LXN::ExtConfig &packet,
                LXN::ExtensionConfig &config,
                char record, unsigned column)
{
    /* count bits in extension mask and write information about each
       extension*/
    const unsigned ext_dat = FromBE16(packet.dat);
    config.num = 0;
    for (unsigned bit = 0; bit < 16; ++bit) {
        if (ext_dat & (1 << bit)) {
            config.extensions[config.num++] = LXN::extension_defs[bit];
        }
    }

    if (config.num == 0)
        return;

    /* begin record */
    fprintf(file, "%c%02d", record, config.num);

    /* write information about each extension */
    for (unsigned i = 0; i < config.num; ++i) {
        fprintf(file, "%02d%02d%s", column,
                column + config.extensions[i].width - 1,
                config.extensions[i].name);
        column += config.extensions[i].width;
    }

    fprintf(file, "\r\n");
}
rx_int16 IOBuffer::consumeI16BE() {
	rx_int16 val = 0;
	memcpy(&val, buffer+consumed, 2);
	val = FromBE16(val);
	consumed += 2;
	return val;
}
Beispiel #5
0
// generate Header-Structure from DBB-File
void DBB::open_dbb() {
 int i;
	// determine the beginning and length of the database parts
 const Volkslogger::TableHeader *src = GetHeader(0);
  for(i=0; i<8; i++) {
    if (src->start_offset == 0xffff)
      continue;

    header[i].dsfirst = FromBE16(src->start_offset);
    header[i].dslast = FromBE16(src->end_offset);
    header[i].dslaenge = src->dslaenge;
    header[i].keylaenge = src->keylaenge;

    ++src;
  }
}
Beispiel #6
0
bool
CAI302::DownloadPolar(Port &port, const Polar &polar,
                      OperationEnvironment &env)
{
  char buffer[256];
  snprintf(buffer, sizeof(buffer),
           "G,%-12s,%-12s,%d,%d,%d,%d,%d,%d,%d,%d\r",
           polar.glider_type,
           polar.glider_id,
           polar.best_ld,
           polar.best_glide_speed,
           polar.two_ms_sink_at_speed,
           FromBE16(polar.weight_in_litres),
           FromBE16(polar.ballast_capacity),
           0,
           FromBE16(polar.config_word),
           FromBE16(polar.wing_area));

  return DownloadCommand(port, buffer, env);
}
Beispiel #7
0
inline void
SkyLinesTracking::Client::OnThermalReceived(const ThermalResponsePacket &packet,
        size_t length)
{
    if (length < sizeof(packet))
        return;

    const unsigned n = packet.thermal_count;
    ConstBuffer<Thermal> thermals((const Thermal *)(&packet + 1), n);
    if (length != sizeof(packet) + thermals.size * sizeof(thermals.front()))
        return;

    for (const auto &thermal : thermals)
        handler->OnThermal(FromBE32(thermal.time),
                           AGeoPoint(ImportGeoPoint(thermal.bottom_location),
                                     FromBE16(thermal.bottom_altitude)),
                           AGeoPoint(ImportGeoPoint(thermal.top_location),
                                     FromBE16(thermal.top_altitude)),
                           FromBE16(thermal.lift) / 256.);
}
Beispiel #8
0
CloudThermal
CloudThermal::Load(Deserialiser &s)
{
  s.Read8();
  const unsigned client_key = s.Read64();

  std::chrono::steady_clock::time_point time;
  s >> time;

  SkyLinesTracking::Thermal t;
  s.ReadT(t);

  CloudThermal thermal(client_key,
                       AGeoPoint(SkyLinesTracking::ImportGeoPoint(t.bottom_location),
                                 FromBE16(t.bottom_altitude)),
                       AGeoPoint(SkyLinesTracking::ImportGeoPoint(t.top_location),
                                 FromBE16(t.top_altitude)),
                       FromBE16(t.lift) / 256.);
  thermal.time = time;
  return thermal;
}
Beispiel #9
0
int
CAI302Device::ReadNavpointCount(OperationEnvironment &env)
{
  if (!UploadMode(env))
    return false;

  CAI302::NavpointMeta meta;
  if (!CAI302::UploadNavpointMeta(port, meta, env)) {
    mode = Mode::UNKNOWN;
    return -1;
  }

  return FromBE16(meta.count);
}
Beispiel #10
0
static bool
DownloadFlightInner(Port &port, const RecordedFlightInfo &flight,
                    FILE *file, OperationEnvironment &env)
{
  if (!LX::CommandMode(port, env))
    return false;

  port.Flush();

  LX::SeekMemory seek;
  seek.start_address = flight.internal.lx.start_address;
  seek.end_address = flight.internal.lx.end_address;
  if (!LX::SendPacket(port, LX::SEEK_MEMORY, &seek, sizeof(seek), env) ||
      !LX::ExpectACK(port, env))
      return false;

  LX::MemorySection memory_section;
  if (!LX::ReceivePacketRetry(port, LX::READ_MEMORY_SECTION,
                              &memory_section, sizeof(memory_section), env,
                              5000, 2000, 60000, 2))
      return false;

  unsigned lengths[LX::MemorySection::N];
  unsigned total_length = 0;
  for (unsigned i = 0; i < LX::MemorySection::N; ++i) {
    lengths[i] = FromBE16(memory_section.lengths[i]);
    total_length += lengths[i];
  }

  env.SetProgressRange(total_length);

  uint8_t *data = new uint8_t[total_length], *p = data;
  for (unsigned i = 0; i < LX::MemorySection::N && lengths[i] > 0; ++i) {
    if (!LX::ReceivePacketRetry(port, (LX::Command)(LX::READ_LOGGER_DATA + i),
                                p, lengths[i], env,
                                20000, 2000, 300000, 2)) {
      delete [] data;
      return false;
    }

    p += lengths[i];
    env.SetProgressPosition(p - data);
  }

  bool success = LX::ConvertLXNToIGC(data, total_length, file);
  delete [] data;

  return success;
}
Beispiel #11
0
static void
HandlePosition(FILE *file, Context &context,
               const struct LXN::Position &position)
{
    int latitude, longitude;

    context.fix_stat = position.cmd == LXN::POSITION_OK ? 'A' : 'V';
    context.time = context.origin_time + (int16_t)FromBE16(position.time);
    latitude = context.origin_latitude + (int16_t)FromBE16(position.latitude);
    longitude = context.origin_longitude + (int16_t)FromBE16(position.longitude);

    if (context.is_event) {
        fprintf(file,"E%02d%02d%02d%s\r\n",
                context.time / 3600, context.time % 3600 / 60, context.time % 60,
                context.event.foo);
        context.is_event = 0;
    }

    fprintf(file, "B%02d%02d%02d",
            context.time / 3600, context.time % 3600 / 60, context.time % 60);
    fprintf(file, "%02d%05d%c" "%03d%05d%c",
            abs(latitude) / 60000, abs(latitude) % 60000,
            latitude >= 0 ? 'N' : 'S',
            abs(longitude) / 60000, abs(longitude) % 60000,
            longitude >= 0 ? 'E' : 'W');
    fprintf(file, "%c", context.fix_stat);
    fprintf(file, "%05d%05d",
            /* altitudes can be negative, so cast the uint16_t to
               int16_t to interpret the most significant bit as sign
               bit */
            (int16_t)FromBE16(position.aalt),
            (int16_t)FromBE16(position.galt));

    if (context.b_ext.num == 0)
        fprintf(file, "\r\n");
}
Beispiel #12
0
inline void
SkyLinesTracking::Client::OnTrafficReceived(const TrafficResponsePacket &packet,
                                            size_t length)
{
  const unsigned n = packet.traffic_count;
  const TrafficResponsePacket::Traffic *traffic =
    (const TrafficResponsePacket::Traffic *)(&packet + 1);

  if (length != sizeof(packet) + n * sizeof(*traffic))
    return;

  const TrafficResponsePacket::Traffic *end = traffic + n;
  for (; traffic != end; ++traffic)
    handler->OnTraffic(FromBE32(traffic->pilot_id),
                       FromBE32(traffic->time),
                       ::GeoPoint(Angle::Degrees(fixed(FromBE32(traffic->location.longitude)) / 1000000),
                                  Angle::Degrees(fixed(FromBE32(traffic->location.latitude)) / 1000000)),
                       FromBE16(traffic->altitude));
}
Beispiel #13
0
inline void
SkyLinesTracking::Client::OnTrafficReceived(const TrafficResponsePacket &packet,
        size_t length)
{
    if (length < sizeof(packet))
        return;

    const unsigned n = packet.traffic_count;
    const ConstBuffer<TrafficResponsePacket::Traffic>
    list((const TrafficResponsePacket::Traffic *)(&packet + 1), n);

    if (length != sizeof(packet) + n * sizeof(list.front()))
        return;

    for (const auto &traffic : list)
        handler->OnTraffic(FromBE32(traffic.pilot_id),
                           FromBE32(traffic.time),
                           ImportGeoPoint(traffic.location),
                           (int16_t)FromBE16(traffic.altitude));
}
Beispiel #14
0
static bool
DownloadFlightInner(Port &port, const RecordedFlightInfo &flight,
                    const TCHAR *path, OperationEnvironment &env)
{
  assert(flight.internal.cai302 < 64);

  BinaryWriter writer(path);
  if (writer.HasError())
    return false;

  CAI302::FileASCII file_ascii;
  if (!UploadFileASCII(port, flight.internal.cai302, file_ascii, env) ||
      env.IsCancelled())
    return false;

  unsigned bytes_per_block = ReadUnalignedBE16(&file_ascii.bytes_per_block);
  unsigned num_blocks = ReadUnalignedBE16(&file_ascii.num_blocks);
  env.SetProgressRange(num_blocks);

  unsigned allocated_size = sizeof(CAI302::FileData) + bytes_per_block;
  void *allocated = malloc(allocated_size);
  CAI302::FileData *header = (CAI302::FileData *)allocated;
  void *data = header + 1;

  unsigned current_block = 0;
  unsigned valid_bytes;
  do {
    int i = UploadFileData(port, true, header, allocated_size, env);
    if (i < (int)sizeof(*header)) {
      free(allocated);
      return false;
    }

    i -= sizeof(*header);

    valid_bytes = FromBE16(header->valid_bytes);
    if ((unsigned)i < valid_bytes) {
      free(allocated);
      return false;
    }

    if (!writer.Write(data, 1, valid_bytes)) {
      free(allocated);
      return false;
    }

    env.SetProgressPosition(current_block++);
  } while (valid_bytes == bytes_per_block);

  free(allocated);

  CAI302::FileSignatureASCII signature;
  if (!CAI302::UploadFileSignatureASCII(port, signature, env))
    return false;

  valid_bytes = FromBE16(signature.size);
  if (valid_bytes > sizeof(signature.signature))
    return false;

  if (!writer.Write(signature.signature, 1, valid_bytes))
    return false;

  return true;
}
Beispiel #15
0
bool
LX::ConvertLXNToIGC(const void *_data, size_t _length,
                    FILE *file)
{
    const uint8_t *data = (const uint8_t *)_data, *end = data + _length;
    Context context;
    char ch;
    unsigned l;

    while (data < end) {
        union LXN::Packet packet = { data };

        unsigned packet_length;
        switch ((LXN::Command)*packet.cmd) {
        case LXN::EMPTY:
            packet_length = 0;
            while (data < end && *data == LXN::EMPTY) {
                ++packet_length;
                ++data;
            }

            fprintf(file, "LFILEMPTY%u\r\n", packet_length);
            break;

        case LXN::END:
            return true;

        case LXN::VERSION:
            data += sizeof(*packet.version);
            if (data > end)
                return false;

            fprintf(file,
                    "HFRFWFIRMWAREVERSION:%3.1f\r\n"
                    "HFRHWHARDWAREVERSION:%3.1f\r\n",
                    packet.version->software / 10.,
                    packet.version->hardware / 10.);
            break;

        case LXN::START:
            data += sizeof(*packet.start);
            if (data > end ||
                    memcmp(packet.start->streraz, "STReRAZ", 8) != 0)
                return false;

            context.flight_no = packet.start->flight_no;
            break;

        case LXN::ORIGIN:
            data += sizeof(*packet.origin);
            if (data > end)
                return false;

            context.origin_time = FromBE32(packet.origin->time);
            context.origin_latitude = (int32_t)FromBE32(packet.origin->latitude);
            context.origin_longitude = (int32_t)FromBE32(packet.origin->longitude);

            fprintf(file, "L%.*sORIGIN%02d%02d%02d" "%02d%05d%c" "%03d%05d%c\r\n",
                    (int)sizeof(context.vendor), context.vendor,
                    context.origin_time / 3600, context.origin_time % 3600 / 60,
                    context.origin_time % 60,
                    abs(context.origin_latitude) / 60000,
                    abs(context.origin_latitude) % 60000,
                    context.origin_latitude >= 0 ? 'N' : 'S',
                    abs(context.origin_longitude) / 60000,
                    abs(context.origin_longitude) % 60000,
                    context.origin_longitude >= 0 ? 'E' : 'W');
            break;

        case LXN::SECURITY_OLD:
            data += sizeof(*packet.security_old);
            if (data > end)
                return false;

            fprintf(file, "G%22.22s\r\n", packet.security_old->foo);
            break;

        case LXN::SERIAL:
            data += sizeof(*packet.serial);
            if (data > end)
                return false;

            if (!ValidString(packet.serial->serial, sizeof(packet.serial->serial)))
                return false;

            fprintf(file, "A%sFLIGHT:%u\r\nHFDTE%s\r\n",
                    packet.serial->serial, context.flight_no, context.date);
            break;

        case LXN::POSITION_OK:
        case LXN::POSITION_BAD:
            data += sizeof(*packet.position);
            if (data > end)
                return false;

            HandlePosition(file, context, *packet.position);
            break;

        case LXN::SECURITY:
            data += sizeof(*packet.security);
            if (data > end ||
                    packet.security->length > sizeof(packet.security->foo))
                return false;

            if (packet.security->type == LXN::SECURITY_HIGH)
                ch = '2';
            else if (packet.security->type == LXN::SECURITY_MED)
                ch = '1';
            else if (packet.security->type == LXN::SECURITY_LOW)
                ch = '0';
            else
                return false;

            fprintf(file, "G%c", ch);

            for (unsigned i = 0; i < packet.security->length; ++i)
                fprintf(file, "%02X", packet.security->foo[i]);

            fprintf(file, "\r\n");
            break;

        case LXN::SECURITY_7000:
            data += sizeof(*packet.security_7000);
            if (data > end ||
                    packet.security_7000->x40 != 0x40)
                return false;

            fprintf(file, "G3");
            for (auto ch : packet.security_7000->line1)
                fprintf(file, "%02X", ch);

            fprintf(file, "\r\nG");
            for (auto ch : packet.security_7000->line2)
                fprintf(file, "%02X", ch);

            fprintf(file, "\r\nG");
            for (auto ch : packet.security_7000->line3)
                fprintf(file, "%02X", ch);

            fprintf(file, "\r\n");
            break;

        case LXN::COMPETITION_CLASS:
            data += sizeof(*packet.competition_class);
            if (data > end)
                return false;

            if (!ValidString(packet.competition_class->class_id,
                             sizeof(packet.competition_class->class_id)))
                return false;

            if (context.flight_info.competition_class_id == 7)
                fprintf(file,
                        "HFFXA%03d\r\n"
                        "HFPLTPILOT:%s\r\n"
                        "HFGTYGLIDERTYPE:%s\r\n"
                        "HFGIDGLIDERID:%s\r\n"
                        "HFDTM%03dGPSDATUM:%s\r\n"
                        "HFCIDCOMPETITIONID:%s\r\n"
                        "HFCCLCOMPETITIONCLASS:%s\r\n"
                        "HFGPSGPS:%s\r\n",
                        context.flight_info.fix_accuracy,
                        context.flight_info.pilot,
                        context.flight_info.glider,
                        context.flight_info.registration,
                        context.flight_info.gps_date,
                        LXN::FormatGPSDate(context.flight_info.gps_date),
                        context.flight_info.competition_class,
                        packet.competition_class->class_id,
                        context.flight_info.gps);
            break;

        case LXN::TASK:
            data += sizeof(*packet.task);
            if (data > end)
                return false;

            context.time = FromBE32(packet.task->time);

            // from a valid IGC file read with LXe:
            // C 11 08 11 14 11 18 11 08 11 0001 -2

            fprintf(file, "C%02d%02d%02d%02d%02d%02d"
                    "%02d%02d%02d" "%04d%02d\r\n",
                    packet.task->day, packet.task->month, packet.task->year,
                    context.time / 3600, context.time % 3600 / 60, context.time % 60,
                    packet.task->day2, packet.task->month2, packet.task->year2,
                    FromBE16(packet.task->task_id), packet.task->num_tps);

            for (unsigned i = 0; i < sizeof(packet.task->usage); ++i) {
                if (packet.task->usage[i]) {
                    int latitude = (int32_t)FromBE32(packet.task->latitude[i]);
                    int longitude = (int32_t)FromBE32(packet.task->longitude[i]);

                    if (!ValidString(packet.task->name[i], sizeof(packet.task->name[i])))
                        return false;

                    fprintf(file, "C%02d%05d%c" "%03d%05d%c" "%s\r\n",
                            abs(latitude) / 60000, abs(latitude) % 60000,
                            latitude >= 0 ?  'N' : 'S',
                            abs(longitude) / 60000, abs(longitude) % 60000,
                            longitude >= 0 ? 'E' : 'W',
                            packet.task->name[i]);
                }
            }
            break;

        case LXN::EVENT:
            data += sizeof(*packet.event);
            if (data > end ||
                    !ValidString(packet.event->foo, sizeof(packet.event->foo)))
                return false;

            context.event = *packet.event;
            context.is_event = true;
            break;

        case LXN::B_EXT:
            data += sizeof(*packet.b_ext) +
                    context.b_ext.num * sizeof(packet.b_ext->data[0]);
            if (data > end)
                return false;

            for (unsigned i = 0; i < context.b_ext.num; ++i)
                fprintf(file, "%0*u",
                        (int)context.b_ext.extensions[i].width,
                        FromBE16(packet.b_ext->data[i]));

            fprintf(file, "\r\n");
            break;

        case LXN::K_EXT:
            data += sizeof(*packet.k_ext) +
                    context.k_ext.num * sizeof(packet.k_ext->data[0]);
            if (data > end)
                return false;

            l = context.time + packet.k_ext->foo;
            fprintf(file, "K%02d%02d%02d",
                    l / 3600, l % 3600 / 60, l % 60);

            for (unsigned i = 0; i < context.k_ext.num; ++i)
                fprintf(file, "%0*u",
                        context.k_ext.extensions[i].width,
                        FromBE16(packet.k_ext->data[i]));

            fprintf(file, "\r\n");
            break;

        case LXN::DATE:
            data += sizeof(*packet.date);
            if (data > end ||
                    packet.date->day > 31 || packet.date->month > 12)
                return false;

            snprintf(context.date, sizeof(context.date),
                     "%02d%02d%02d",
                     packet.date->day % 100, packet.date->month % 100,
                     FromBE16(packet.date->year));
            break;

        case LXN::FLIGHT_INFO:
            data += sizeof(*packet.flight_info);
            if (data > end ||
                    !ValidString(packet.flight_info->pilot,
                                 sizeof(packet.flight_info->pilot)) ||
                    !ValidString(packet.flight_info->glider,
                                 sizeof(packet.flight_info->glider)) ||
                    !ValidString(packet.flight_info->registration,
                                 sizeof(packet.flight_info->registration)) ||
                    !ValidString(packet.flight_info->competition_class,
                                 sizeof(packet.flight_info->competition_class)) ||
                    !ValidString(packet.flight_info->gps, sizeof(packet.flight_info->gps)))
                return false;

            if (packet.flight_info->competition_class_id > 7)
                return false;

            if (packet.flight_info->competition_class_id < 7)
                fprintf(file,
                        "HFFXA%03d\r\n"
                        "HFPLTPILOT:%s\r\n"
                        "HFGTYGLIDERTYPE:%s\r\n"
                        "HFGIDGLIDERID:%s\r\n"
                        "HFDTM%03dGPSDATUM:%s\r\n"
                        "HFCIDCOMPETITIONID:%s\r\n"
                        "HFCCLCOMPETITIONCLASS:%s\r\n"
                        "HFGPSGPS:%s\r\n",
                        packet.flight_info->fix_accuracy,
                        packet.flight_info->pilot,
                        packet.flight_info->glider,
                        packet.flight_info->registration,
                        packet.flight_info->gps_date,
                        LXN::FormatGPSDate(packet.flight_info->gps_date),
                        packet.flight_info->competition_class,
                        LXN::FormatCompetitionClass(packet.flight_info->competition_class_id),
                        packet.flight_info->gps);

            context.flight_info = *packet.flight_info;
            break;

        case LXN::K_EXT_CONFIG:
            data += sizeof(*packet.ext_config);
            if (data > end)
                return false;

            HandleExtConfig(file, *packet.ext_config, context.k_ext, 'J', 8);
            break;

        case LXN::B_EXT_CONFIG:
            data += sizeof(*packet.ext_config);
            if (data > end)
                return false;

            HandleExtConfig(file, *packet.ext_config, context.b_ext, 'I', 36);
            break;

#ifdef __clang__
#pragma GCC diagnostic ignored "-Wcovered-switch-default"
#endif
        default:
            if (*packet.cmd < 0x40) {
                data += sizeof(*packet.string);
                if (data > end)
                    return false;

                data += packet.string->length;
                if (data > end)
                    return false;

                fprintf(file, "%.*s\r\n",
                        (int)packet.string->length, packet.string->value);

                if (packet.string->length >= 12 + sizeof(context.vendor) &&
                        memcmp(packet.string->value, "HFFTYFRTYPE:", 12) == 0)
                    memcpy(context.vendor, packet.string->value + 12, sizeof(context.vendor));
            } else
                return false;
        }
    }

    return false;
}
Beispiel #16
0
inline void
Server::OnDatagramReceived(Client &&client,
                           void *data, size_t length)
{
  Header &header = *(Header *)data;
  if (length < sizeof(header))
    return;

  const uint16_t received_crc = FromBE16(header.crc);
  header.crc = 0;

  const uint16_t calculated_crc = UpdateCRC16CCITT(data, length, 0);
  if (received_crc != calculated_crc)
    return;

  client.key = FromBE64(header.key);

  const auto &ping = *(const PingPacket *)data;
  const auto &fix = *(const FixPacket *)data;
  const auto &traffic = *(const TrafficRequestPacket *)data;
  const auto &user_name = *(const UserNameRequestPacket *)data;
  const auto &wave = ((const WaveSubmitPacket *)data)->wave;
  const auto &thermal = ((const ThermalSubmitPacket *)data)->thermal;

  switch ((Type)FromBE16(header.type)) {
  case PING:
    if (length < sizeof(ping))
      return;

    OnPing(client, FromBE16(ping.id));
    break;

  case FIX:
    if (length < sizeof(fix))
      return;

    OnFix(client,
          ImportTimeMs(fix.time),
          fix.flags & ToBE32(FixPacket::FLAG_LOCATION)
          ? ImportGeoPoint(fix.location)
          : ::GeoPoint::Invalid(),
          fix.flags & ToBE32(FixPacket::FLAG_ALTITUDE)
          ? FromBE16(fix.altitude)
          : -1);
    break;

  case TRAFFIC_REQUEST:
    if (length < sizeof(traffic))
      return;

    OnTrafficRequest(client,
                     traffic.flags & ToBE32(TrafficRequestPacket::FLAG_NEAR));
    break;

  case USER_NAME_REQUEST:
    if (length < sizeof(user_name))
      return;

    OnUserNameRequest(client,
                      FromBE32(user_name.user_id));
    break;

  case WAVE_SUBMIT:
    if (length < sizeof(wave))
      return;

    OnWaveSubmit(client,
                 ImportTimeMs(wave.time),
                 ImportGeoPoint(wave.a), ImportGeoPoint(wave.b),
                 (int16_t)FromBE16(wave.bottom_altitude),
                 (int16_t)FromBE16(wave.top_altitude),
                 FromBE16(wave.lift) / 256.);
    break;

  case WAVE_REQUEST:
    OnWaveRequest(client);
    break;

  case THERMAL_SUBMIT:
    if (length < sizeof(thermal))
      return;

    OnThermalSubmit(client,
                    ImportTimeMs(thermal.time),
                    ImportGeoPoint(thermal.bottom_location),
                    (int16_t)FromBE16(thermal.bottom_altitude),
                    ImportGeoPoint(thermal.top_location),
                    (int16_t)FromBE16(thermal.top_altitude),
                    FromBE16(thermal.lift) / 256.);
    break;

  case THERMAL_REQUEST:
    OnThermalRequest(client);
    break;

  case ACK:
  case TRAFFIC_RESPONSE:
  case USER_NAME_RESPONSE:
  case WAVE_RESPONSE:
  case THERMAL_RESPONSE:
    /* these are server-to-client packets; ignore them */
    break;
  }
}