Example #1
0
void
FlightSetupPanel::Prepare(ContainerWindow &parent, const PixelRect &rc)
{
  RowFormWidget::Prepare(parent, rc);

  const ComputerSettings &settings = CommonInterface::GetComputerSettings();
  const Plane &plane = CommonInterface::GetComputerSettings().plane;

  const double db = 5;
  AddFloat(_("Ballast"),
           _("Ballast of the glider.  Increase this value if the pilot/cockpit load is greater than the reference pilot weight of the glide polar (typically 75kg).  Press ENTER on this field to toggle count-down of the ballast volume according to the dump rate specified in the configuration settings."),
           _T("%.0f l"), _T("%.0f"),
           0, db*ceil(plane.max_ballast/db), db, false, 0,
           this);

  WndProperty *wing_loading = AddFloat(_("Wing loading"), nullptr,
                                       _T("%.1f %s"), _T("%.0f"), 0,
                                       300, 5,
                                       false, UnitGroup::WING_LOADING,
                                       0);
  wing_loading->SetReadOnly(true);

  AddFloat(_("Bugs"), /* xgettext:no-c-format */
           _("How clean the glider is. Set to 0% for clean, larger numbers as the wings "
               "pick up bugs or gets wet.  50% indicates the glider's sink rate is doubled."),
           _T("%.0f %%"), _T("%.0f"),
           0, 50, 1, false,
           (1 - polar_settings.bugs) * 100,
           this);

  WndProperty *wp;
  wp = AddFloat(_("QNH"),
                _("Area pressure for barometric altimeter calibration.  This is set automatically if Vega connected."),
                GetUserPressureFormat(true), GetUserPressureFormat(),
                Units::ToUserPressure(Units::ToSysUnit(850, Unit::HECTOPASCAL)),
                Units::ToUserPressure(Units::ToSysUnit(1300, Unit::HECTOPASCAL)),
                GetUserPressureStep(), false,
                Units::ToUserPressure(settings.pressure), this);
  {
    DataFieldFloat &df = *(DataFieldFloat *)wp->GetDataField();
    df.SetUnits(Units::GetPressureName());
    wp->RefreshDisplay();
  }

  AddReadOnly(_("Altitude"), NULL, _T("%.0f %s"),
              UnitGroup::ALTITUDE, 0);

  wp = AddFloat(_("Max. temp."),
                _("Set to forecast ground temperature.  Used by convection estimator (temperature trace page of Analysis dialog)"),
                _T("%.0f %s"), _T("%.0f"),
                Units::ToUserTemperature(CelsiusToKelvin(-50)),
                Units::ToUserTemperature(CelsiusToKelvin(60)),
                1, false,
                Units::ToUserTemperature(settings.forecast_temperature));
  {
    DataFieldFloat &df = *(DataFieldFloat *)wp->GetDataField();
    df.SetUnits(Units::GetTemperatureName());
    wp->RefreshDisplay();
  }
}
Example #2
0
/**
 * Parse the $PLXVS sentence (LXNav V7).
 *
 * $PLXVS,OAT,mode,voltage *CS<CR><LF>
 *
 * Example: $PLXVS,23.1,0,12.3,*CS<CR><LF>
 *
 * @see http://www.xcsoar.org/trac/raw-attachment/ticket/1666/V7%20dataport%20specification%201.97.pdf
 */
static bool
PLXVS(NMEAInputLine &line, NMEAInfo &info)
{
  fixed temperature;
  if (line.ReadChecked(temperature)) {
    info.temperature = CelsiusToKelvin(temperature);
    info.temperature_available = true;
  }

  int mode;
  info.switch_state.flight_mode = SwitchState::FlightMode::UNKNOWN;
  if (line.ReadChecked(mode)) {
    if (mode == 0)
      info.switch_state.flight_mode = SwitchState::FlightMode::CIRCLING;
    else if (mode == 1)
      info.switch_state.flight_mode = SwitchState::FlightMode::CRUISE;
  }

  fixed voltage;
  if (line.ReadChecked(voltage)) {
    info.voltage = voltage;
    info.voltage_available.Update(info.clock);
  }

  return true;
}
Example #3
0
/**
 * Parse a "$VMVABD" sentence.
 *
 * Example: "$VMVABD,0000.0,M,0547.0,M,-0.0,,,MS,0.0,KH,22.4,C*65"
 */
static bool
FlytecParseVMVABD(NMEAInputLine &line, NMEAInfo &info)
{
  fixed value;

  // 0,1 = GPS altitude, unit
  if (line.ReadCheckedCompare(info.gps_altitude, "M"))
    info.gps_altitude_available.Update(info.clock);

  // 2,3 = baro altitude, unit
  if (line.ReadCheckedCompare(value, "M"))
    info.ProvideBaroAltitudeTrue(value);

  // 4-7 = integrated vario, unit
  line.Skip(4);

  // 8,9 = indicated or true airspeed, unit
  if (line.ReadCheckedCompare(value, "KH"))
    // XXX is that TAS or IAS?  Documentation isn't clear.
    info.ProvideBothAirspeeds(Units::ToSysUnit(value, Unit::KILOMETER_PER_HOUR));

  // 10,11 = temperature, unit
  info.temperature_available =
    line.ReadCheckedCompare(value, "C");
  if (info.temperature_available)
    info.temperature = CelsiusToKelvin(value);

  return true;
}
Example #4
0
void
ComputerSettings::SetDefaults()
{
  wind.SetDefaults();
  polar.SetDefaults();
  team_code.SetDefaults();
  voice.SetDefaults();
  poi.SetDefaults();
  features.SetDefaults();
  circling.SetDefaults();
  wave.SetDefaults();

  average_eff_time = ae30seconds;
  set_system_time_from_gps = false;
  utc_offset = RoughTimeDelta::FromSeconds(GetSystemUTCOffset());
  forecast_temperature = CelsiusToKelvin(25);
  pressure = AtmosphericPressure::Standard();
  pressure_available.Clear();
  airspace.SetDefaults();
  task.SetDefaults();
  contest.SetDefaults();
  logger.SetDefaults();

#ifdef HAVE_TRACKING
  tracking.SetDefaults();
#endif
  weather.SetDefaults();
}
Example #5
0
bool
OpenVarioDevice::POV(NMEAInputLine &line, NMEAInfo &info)
{
  /*
   * Type definitions:
   *
   * E: TE vario in m/s
   * P: static pressure in hPa
   * Q: dynamic pressure in Pa
   * R: total pressure in hPa
   * S: true airspeed in km/h
   * T: temperature in deg C
   */

  while (!line.IsEmpty()) {
    char type = line.ReadOneChar();
    if (type == '\0')
      break;

    fixed value;
    if (!line.ReadChecked(value))
      break;

    switch (type) {
      case 'E': {
        info.ProvideTotalEnergyVario(value);
        break;
      }
      case 'P': {
        AtmosphericPressure pressure = AtmosphericPressure::HectoPascal(value);
        info.ProvideStaticPressure(pressure);
        break;
      }
      case 'Q': {
        AtmosphericPressure pressure = AtmosphericPressure::Pascal(value);
        info.ProvideDynamicPressure(pressure);
        break;
      }
      case 'R': {
        AtmosphericPressure pressure = AtmosphericPressure::HectoPascal(value);
        info.ProvidePitotPressure(pressure);
        break;
      }
      case 'S': {
        value = Units::ToSysUnit(value, Unit::KILOMETER_PER_HOUR);
        info.ProvideTrueAirspeed(value);
        break;
      }
      case 'T': {
        info.temperature = CelsiusToKelvin(value);
        info.temperature_available = true;
        break;
      }
    }
  }

  return true;
}
JNIEXPORT void JNICALL
Java_org_xcsoar_NativeBMP085Listener_onBMP085Values(JNIEnv *env, jobject obj,
                                                    jdouble temperature,
                                                    jint pressure)
{
  jlong ptr = env->GetLongField(obj, NativeBMP085Listener::ptr_field);
  if (ptr == 0)
    return;

  BMP085Listener &listener = *(BMP085Listener *)(void *)ptr;
  listener.onBMP085Values(CelsiusToKelvin(fixed(temperature)),
                          AtmosphericPressure::Pascal(fixed(pressure)));
}
Example #7
0
/**
 * $PWES0,DD,VVVV,MMMM,NNNN,BBBB,SSSS,AAAAA,QQQQQ,IIII,TTTT,UUU,CCCC*CS<CR><LF>
 */
static bool
PWES0(NMEAInputLine &line, NMEAInfo &info)
{
  int i;

  line.Skip(); /* device */

  if (line.ReadChecked(i) && i >= -999 && i <= 999)
    info.ProvideTotalEnergyVario(fixed(i) / 10);

  line.Skip(); /* average vario */

  if (line.ReadChecked(i) && i >= -999 && i <= 999)
    info.ProvideNettoVario(fixed(i) / 10);

  line.Skip(); /* average netto vario */
  line.Skip(); /* speed to fly */

  unsigned altitude;
  if (line.ReadChecked(altitude) && altitude <= 99999)
    info.ProvidePressureAltitude(fixed(altitude));

  if (line.ReadChecked(altitude) && altitude <= 99999)
    info.ProvideBaroAltitudeTrue(fixed(altitude));

  unsigned ias, tas;
  bool have_ias = line.ReadChecked(ias) && ias <= 9999;
  bool have_tas = line.ReadChecked(tas) && tas <= 9999;
  if (have_ias && have_tas)
    info.ProvideBothAirspeeds(Units::ToSysUnit(fixed(ias) / 10,
                                               Unit::KILOMETER_PER_HOUR),
                              Units::ToSysUnit(fixed(tas) / 10,
                                               Unit::KILOMETER_PER_HOUR));

  else if (!have_ias && have_tas)
    info.ProvideTrueAirspeed(Units::ToSysUnit(fixed(tas) / 10,
                                              Unit::KILOMETER_PER_HOUR));

  unsigned voltage;
  if (line.ReadChecked(voltage) && voltage <= 999) {
    info.voltage = fixed(voltage) / 10;
    info.voltage_available.Update(info.clock);
  }

  if (line.ReadChecked(i) && i >= -999 && i <= 999) {
    info.temperature = CelsiusToKelvin(fixed(i) / 10);
    info.temperature_available = true;
  }

  return true;
}
Example #8
0
/**
 * $PWES0,DD,VVVV,MMMM,NNNN,BBBB,SSSS,AAAAA,QQQQQ,IIII,TTTT,UUU,CCCC*CS<CR><LF>
 */
static bool
PWES0(NMEAInputLine &line, NMEAInfo &info)
{
  int i, k;

  line.skip(); /* device */

  if (line.read_checked(i))
    info.ProvideTotalEnergyVario(fixed(i) / 10);

  line.skip(); /* average vario */

  if (line.read_checked(i))
    info.ProvideNettoVario(fixed(i) / 10);

  line.skip(); /* average netto vario */
  line.skip(); /* speed to fly */

  if (line.read_checked(i))
    info.ProvidePressureAltitude(fixed(i));

  if (line.read_checked(i))
    info.ProvideBaroAltitudeTrue(fixed(i));

  bool have_ias = line.read_checked(i);
  bool have_tas = line.read_checked(k);
  if (have_ias && have_tas)
    info.ProvideBothAirspeeds(Units::ToSysUnit(fixed(i) / 10,
                                               Unit::KILOMETER_PER_HOUR),
                              Units::ToSysUnit(fixed(k) / 10,
                                               Unit::KILOMETER_PER_HOUR));

  else if (!have_ias && have_tas)
    info.ProvideTrueAirspeed(Units::ToSysUnit(fixed(k) / 10,
                                              Unit::KILOMETER_PER_HOUR));

  if (line.read_checked(i)) {
    info.voltage = fixed(i) / 10;
    info.voltage_available.Update(info.clock);
  }

  if (line.read_checked(i)) {
    info.temperature = CelsiusToKelvin(fixed(i) / 10);
    info.temperature_available = true;
  }

  return true;
}
Example #9
0
/**
 * Parse a "$D" sentence.
 *
 * Example: "$D,+0,100554,+25,18,+31,,0,-356,+25,+11,115,96*6A"
 */
static bool
LeonardoParseD(NMEAInputLine &line, NMEAInfo &info)
{
  double value;

  // 0 = vario [dm/s]
  if (line.ReadChecked(value))
    info.ProvideTotalEnergyVario(value / 10);

  if (line.Rest().empty())
    /* short "$D" sentence ends after vario */
    return true;

  // 1 = air pressure [Pa]
  if (line.ReadChecked(value))
    info.ProvideStaticPressure(AtmosphericPressure::Pascal(value));

  // 2 = netto vario [dm/s]
  if (line.ReadChecked(value))
    info.ProvideNettoVario(value / 10);

  // 3 = airspeed [km/h]
  /* XXX is that TAS or IAS? */
  if (line.ReadChecked(value))
    info.ProvideTrueAirspeed(Units::ToSysUnit(value, Unit::KILOMETER_PER_HOUR));

  // 4 = temperature [deg C]
  double oat;
  info.temperature_available = line.ReadChecked(oat);
  if (info.temperature_available)
    info.temperature = CelsiusToKelvin(oat);

  // 5 = compass [degrees]
  /* XXX unsupported by XCSoar */

  // 6 = optimal speed [km/h]
  /* XXX unsupported by XCSoar */

  // 7 = equivalent MacCready [cm/s]
  /* XXX unsupported by XCSoar */

  // 8 = wind speed [km/h]
  /* not used here, the "$C" record repeats it together with the
     direction */

  return true;
}
Example #10
0
/**
 * Parse a "$C" sentence.
 *
 * Example: "$C,+2025,-7,+18,+25,+29,122,314,314,0,-356,+25,45,T*3D"
 */
static bool
LeonardoParseC(NMEAInputLine &line, NMEAInfo &info)
{
  double value;

  // 0 = altitude [m]
  if (line.ReadChecked(value))
    info.ProvideBaroAltitudeTrue(value);

  // 1 = vario [cm/s]
  if (line.ReadChecked(value))
    info.ProvideTotalEnergyVario(value / 100);

  // 2 = airspeed [km/h]
  /* XXX is that TAS or IAS? */
  if (line.ReadChecked(value))
    info.ProvideTrueAirspeed(Units::ToSysUnit(value, Unit::KILOMETER_PER_HOUR));

  if (line.Rest().empty())
    /* short "$C" sentence ends after airspeed */
    return true;

  // 3 = netto vario [dm/s]
  if (line.ReadChecked(value))
    info.ProvideNettoVario(value / 10);

  // 4 = temperature [deg C]
  double oat;
  info.temperature_available = line.ReadChecked(oat);
  if (info.temperature_available)
    info.temperature = CelsiusToKelvin(oat);

  line.Skip(5);

  // 10 = wind speed [km/h]
  // 11 = wind direction [degrees]
  SpeedVector wind;
  if (ReadSpeedVector(line, wind))
    info.ProvideExternalWind(wind);

  return true;
}
Example #11
0
void
VoltageDevice::onVoltageValues(int temp_adc, int voltage_index, int volt_adc)
{
  ScopeLock protect(device_blackboard->mutex);
  NMEAInfo &basic = device_blackboard->SetRealState(index);
  basic.UpdateClock();
  basic.alive.Update(basic.clock);

  // When no calibration data present, use defaults
  if (factor == fixed(0)) {
    // Set default for temp sensor only when sensor present.
    if (temp_adc >= 0 && offset == fixed(0)) offset = fixed(-130);
    factor = fixed(0.016666);
    basic.ProvideSensorCalibration(factor, offset);
  }

  if (temp_adc >= 0) {
    fixed v = CelsiusToKelvin(offset + fixed(temp_adc));
    if (temperature_filter.Update(v))
      v = temperature_filter.Average();
    basic.temperature = v;
    basic.temperature_available = true;
  } else {
    basic.temperature_available = false;
  }

  if ((unsigned)voltage_index < NUMBER_OF_VOLTAGES) {
    fixed v;
    switch (voltage_index) {
      case 0: v = factor * volt_adc; break;
      case 1: v = fixed(0.0071744) * volt_adc; break;
      case 2: v = (volt_adc - (250*1024)/331) * fixed(0.001786); break; // ACS712 current sensor
    }
    if (voltage_filter[voltage_index].Update(v))
      v = voltage_filter[voltage_index].Average();
    basic.voltage = v;
    basic.voltage_available.Update(basic.clock);
  }

  device_blackboard->ScheduleMerge();
}
Example #12
0
static bool
VARIO(NMEAInputLine &line, NMEAInfo &info)
{
  // $VARIO,fPressure,fVario,Bat1Volts,Bat2Volts,BatBank,TempSensor1,TempSensor2*CS

  // fVario = the variometer in decimeters per second
  // Bat1Volts = the voltage of the battery in bank 1
  // Bat2Volts = the voltage of the battery in bank 2
  // BatBank = the battery bank in use.
  // TempSensor1 = temperature in ºC of external wireless sensor 1
  // TempSensor2 = temperature in ºC of external wireless sensor 2

  fixed value;
  if (line.ReadChecked(value))
    info.ProvideStaticPressure(AtmosphericPressure::HectoPascal(value));

  if (line.ReadChecked(value))
    info.ProvideTotalEnergyVario(value / 10);

  unsigned battery_bank;
  fixed voltage[2];
  if (line.ReadChecked(voltage[0]) &&
      line.ReadChecked(voltage[1]) &&
      line.ReadChecked(battery_bank) &&
      battery_bank != 0 &&
      battery_bank <= 2) {
    info.voltage = voltage[battery_bank - 1];
    info.voltage_available.Update(info.clock);
  }

  if (line.ReadChecked(value)) {
    info.temperature = CelsiusToKelvin(value);
    info.temperature_available = true;
  }

  return true;
}
Example #13
0
/**
 * Parse a "$FLYSEN" sentence.
 *
 * @see http://www.flytec.ch/public/Special%20NMEA%20sentence.pdf
 */
bool
FlytecDevice::ParseFLYSEN(NMEAInputLine &line, NMEAInfo &info)
{
  // Detect firmware/sentence version
  //
  // V or A in field 9  -> 3.31-
  // V or A in field 10 -> 3.32+

  NMEAInputLine line_copy(line);

  line_copy.Skip(8);

  bool has_date_field = false;
  char validity = line_copy.ReadFirstChar();
  if (validity != 'A' && validity != 'V') {
    validity = line_copy.ReadFirstChar();
    if (validity != 'A' && validity != 'V')
      return false;

    has_date_field = true;
  }

  //  Date(ddmmyy),   6 Digits (only in firmware version 3.32+)
  if (has_date_field)
    NMEAParser::ReadDate(line, info.date_time_utc);

  //  Time(hhmmss),   6 Digits

  fixed time;
  if (NMEAParser::ReadTime(line, info.date_time_utc, time) &&
      !NMEAParser::TimeHasAdvanced(time, last_time, info))
    return true;

  if (validity == 'V') {
    // In case of V (void=not valid) GPS data should not be used.
    // GPS altitude, position and speed should be ignored.
    line.Skip(7);

  } else {
    //  Latitude(ddmm.mmm),   8 Digits incl. decimal
    //  N (or S),   1 Digit
    //  Longitude(dddmm.mmm),   9 Digits inc. decimal
    //  E (or W),   1 Digit
    GeoPoint location;
    if (NMEAParser::ReadGeoPoint(line, location)) {
      info.location = location;
      info.location_available.Update(info.clock);
    }

    //  Track (xxx Deg),   3 Digits
    fixed track;
    if (line.ReadChecked(track)) {
      info.track = Angle::Degrees(track);
      info.track_available.Update(info.clock);
    }

    //  Speed over Ground (xxxxx dm/s), 5 Digits
    fixed ground_speed;
    if (line.ReadChecked(ground_speed)) {
      info.ground_speed = ground_speed / 10;
      info.ground_speed_available.Update(info.clock);
    }

    //  GPS altitude (xxxxx meter),           5 Digits
    fixed gps_altitude;
    if (line.ReadChecked(gps_altitude)) {
      info.gps_altitude = gps_altitude;
      info.gps_altitude_available.Update(info.clock);
    }
  }

  //  Validity of 3 D fix A or V,           1 Digit
  line.Skip();

  //  Satellites in Use (0 to 12),          2 Digits
  unsigned satellites_used;
  if (line.ReadChecked(satellites_used)) {
    info.gps.satellites_used = satellites_used;
    info.gps.satellites_used_available.Update(info.clock);
  }

  //  Raw pressure (xxxxxx Pa),  6 Digits
  fixed pressure;
  if (line.ReadChecked(pressure))
    info.ProvideStaticPressure(AtmosphericPressure::Pascal(pressure));

  //  Baro Altitude (xxxxx meter),          5 Digits (-xxxx to xxxxx) (Based on 1013.25hPa)
  fixed baro_altitude;
  if (line.ReadChecked(baro_altitude))
    info.ProvidePressureAltitude(baro_altitude);

  //  Variometer (xxxx cm/s),   4 or 5 Digits (-9999 to 9999)
  fixed vario;
  if (line.ReadChecked(vario))
    info.ProvideTotalEnergyVario(vario / 100);

  //  True airspeed (xxxxx dm/s), 5 Digits
  fixed tas;
  if (line.ReadChecked(tas))
    info.ProvideTrueAirspeed(tas / 10);

  //  Airspeed source P or V,   1 Digit P= pitot, V = Vane wheel
  line.Skip();

  //  Temp. PCB (xxx °C),   3 Digits
  fixed pcb_temperature;
  bool pcb_temperature_available = line.ReadChecked(pcb_temperature);

  //  Temp. Balloon Envelope (xxx °C),      3 Digits
  fixed balloon_temperature;
  bool balloon_temperature_available = line.ReadChecked(balloon_temperature);

  if (balloon_temperature_available) {
    info.temperature = CelsiusToKelvin(balloon_temperature);
    info.temperature_available = true;
  } else if (pcb_temperature_available) {
    info.temperature = CelsiusToKelvin(pcb_temperature);
    info.temperature_available = true;
  }

  //  Battery Capacity Bank 1 (0 to 100%)   3 Digits
  fixed battery_level_1;
  bool battery_level_1_available = line.ReadChecked(battery_level_1);

  //  Battery Capacity Bank 2 (0 to 100%)   3 Digits
  fixed battery_level_2;
  bool battery_level_2_available = line.ReadChecked(battery_level_2);

  if (battery_level_1_available) {
    if (battery_level_2_available)
      info.battery_level = (battery_level_1 + battery_level_2) / 2;
    else
      info.battery_level = battery_level_1;

    info.battery_level_available.Update(info.clock);
  } else if (battery_level_2_available) {
    info.battery_level = battery_level_2;
    info.battery_level_available.Update(info.clock);
  }

  //  Dist. to WP (xxxxxx m),   6 Digits (Max 200000m)
  //  Bearing (xxx Deg),   3 Digits
  //  Speed to fly1 (MC0 xxxxx cm/s),       5 Digits
  //  Speed to fly2 (McC. xxxxx cm/s)       5 Digits
  //  Keypress Code (Experimental empty to 99)     2 Digits

  return true;
}
Example #14
0
static bool
ParseData(NMEAInputLine &line, NMEAInfo &info)
{
  // $PCPROBE,T,Q0,Q1,Q2,Q3,ax,ay,az,temp,rh,batt,delta_press,abs_press,C,
  // see http://www.compassitaly.com/CMS/index.php/en/download/c-probe/231-c-probeusermanual/download

  unsigned _q[4];
  bool q_available = line.ReadHexChecked(_q[0]);
  if (!line.ReadHexChecked(_q[1]))
    q_available = false;
  if (!line.ReadHexChecked(_q[2]))
    q_available = false;
  if (!line.ReadHexChecked(_q[3]))
    q_available = false;

  if (q_available) {
    fixed q[4];
    for (unsigned i = 0; i < 4; ++i)
      // Cast to int16_t to interpret the 16th bit as the sign bit
      q[i] = fixed((int16_t)_q[i]) / 1000;

    fixed sin_pitch = -2 * (q[0] * q[2] - q[3] * q[1]);
    if (sin_pitch <= fixed(1) && sin_pitch >= fixed(-1)) {
      info.attitude.pitch_angle_available = true;
      info.attitude.pitch_angle = Angle::asin(sin_pitch);

      Angle heading = Angle::HalfCircle() +
        Angle::FromXY(sqr(q[3]) - sqr(q[0]) - sqr(q[1]) + sqr(q[2]),
                      Double(q[1] * q[2] + q[3] * q[0]));

      info.attitude.heading_available.Update(info.clock);
      info.attitude.heading = heading;

      Angle roll = Angle::FromXY(sqr(q[3]) + sqr(q[0]) - sqr(q[1]) - sqr(q[2]),
                                 Double(q[0] * q[1] + q[3] * q[2]));

      info.attitude.bank_angle_available = true;
      info.attitude.bank_angle = roll;
    }
  }

  unsigned _a[3];
  bool a_available = line.ReadHexChecked(_a[0]);
  if (!line.ReadHexChecked(_a[1]))
    a_available = false;
  if (!line.ReadHexChecked(_a[2]))
    a_available = false;

  if (a_available) {
    fixed a[3];
    for (unsigned i = 0; i < 3; ++i)
      // Cast to int16_t to interpret the 16th bit as the sign bit
      a[i] = fixed((int16_t)_a[i]) / 1000;

    info.acceleration.ProvideGLoad(sqrt(sqr(a[0]) + sqr(a[1]) + sqr(a[2])), true);
  }

  unsigned temperature;
  if (line.ReadHexChecked(temperature)) {
    info.temperature_available = true;
    info.temperature = CelsiusToKelvin(fixed((int16_t)temperature) / 10);
  }

  unsigned humidity;
  if (line.ReadHexChecked(humidity)) {
    info.humidity_available = true;
    info.humidity = fixed((int16_t)humidity) / 10;
  }

  unsigned battery_level;
  if (line.ReadHexChecked(battery_level)) {
    info.battery_level_available.Update(info.clock);
    info.battery_level = fixed((int16_t)battery_level);
  }

  unsigned _delta_pressure;
  if (line.ReadHexChecked(_delta_pressure)) {
    fixed delta_pressure = fixed((int16_t)_delta_pressure) / 10;
    info.ProvideDynamicPressure(AtmosphericPressure::Pascal(delta_pressure));
  }

  return true;
}