private Position decodeAlertMessage(
      ChannelBuffer buf, Channel channel, SocketAddress remoteAddress) {

    Parser parser = new Parser(PATTERN, buf.toString(Charset.defaultCharset()));
    if (!parser.matches()) {
      return null;
    }

    Position position = new Position();
    position.setProtocol(getProtocolName());

    position.set(Event.KEY_ALARM, true);

    if (!identify(parser.next(), channel, remoteAddress)) {
      return null;
    }
    position.setDeviceId(getDeviceId());

    position.setLongitude(parser.nextCoordinate());
    position.setLatitude(parser.nextCoordinate());
    position.setValid(parser.next().equals("A"));

    DateBuilder dateBuilder =
        new DateBuilder()
            .setDateReverse(parser.nextInt(), parser.nextInt(), parser.nextInt())
            .setTime(parser.nextInt(), parser.nextInt(), parser.nextInt());
    position.setTime(dateBuilder.getDate());

    position.setSpeed(UnitsConverter.knotsFromKph(parser.nextDouble()));
    position.setCourse(parser.nextDouble());

    position.set(Event.KEY_POWER, parser.nextDouble());

    return position;
  }
  @Override
  protected Object decode(Channel channel, SocketAddress remoteAddress, Object msg)
      throws Exception {

    String sentence = (String) msg;

    if (sentence.startsWith("!1")) {

      identify(sentence.substring(3, sentence.length()), channel, remoteAddress);

    } else if ((sentence.startsWith("!D") || sentence.startsWith("!A")) && hasDeviceId()) {

      Parser parser = new Parser(PATTERN, sentence);
      if (!parser.matches()) {
        return null;
      }

      Position position = new Position();
      position.setProtocol(getProtocolName());
      position.setDeviceId(getDeviceId());

      DateBuilder dateBuilder =
          new DateBuilder()
              .setDateReverse(parser.nextInt(), parser.nextInt(), parser.nextInt())
              .setTime(parser.nextInt(), parser.nextInt(), parser.nextInt());
      position.setTime(dateBuilder.getDate());

      position.setLatitude(parser.nextDouble());
      position.setLongitude(parser.nextDouble());
      position.setSpeed(parser.nextDouble());

      position.setCourse(parser.nextDouble());
      if (position.getCourse() > 360) {
        position.setCourse(0);
      }

      if (parser.hasNext(5)) {

        int flags = parser.nextInt(16);
        position.set(Event.KEY_FLAGS, flags);
        position.setValid(BitUtil.check(flags, 0));

        position.setAltitude(parser.nextDouble());

        position.set(Event.KEY_BATTERY, parser.next());
        position.set(Event.KEY_SATELLITES, parser.next());
      }

      return position;
    }

    return null;
  }
  @Override
  protected Object decode(Channel channel, SocketAddress remoteAddress, Object msg)
      throws Exception {

    Parser parser = new Parser(PATTERN, (String) msg);
    if (!parser.matches()) {
      return null;
    }

    Position position = new Position();
    position.setProtocol(getProtocolName());

    if (!identify(parser.next(), channel, remoteAddress)) {
      return null;
    }
    position.setDeviceId(getDeviceId());

    position.set("command", parser.next());

    position.setValid(parser.next().equals("A"));

    DateBuilder dateBuilder =
        new DateBuilder()
            .setDate(parser.nextInt(16), parser.nextInt(16), parser.nextInt(16))
            .setTime(parser.nextInt(16), parser.nextInt(16), parser.nextInt(16));
    position.setTime(dateBuilder.getDate());

    if (BitUtil.check(parser.nextInt(16), 7)) {
      position.setLatitude(-parser.nextInt(16) / 600000.0);
    } else {
      position.setLatitude(parser.nextInt(16) / 600000.0);
    }

    if (BitUtil.check(parser.nextInt(16), 7)) {
      position.setLongitude(-parser.nextInt(16) / 600000.0);
    } else {
      position.setLongitude(parser.nextInt(16) / 600000.0);
    }

    position.setSpeed(parser.nextInt(16) / 100.0);
    position.setCourse(parser.nextInt(16) / 100.0);

    position.set(Event.KEY_STATUS, parser.next());
    position.set("signal", parser.next());
    position.set(Event.KEY_POWER, parser.nextDouble());
    position.set("oil", parser.nextInt(16));
    position.set(Event.KEY_ODOMETER, parser.nextInt(16));

    position.setAltitude(parser.nextDouble());

    return position;
  }
  @Override
  protected Object decode(Channel channel, SocketAddress remoteAddress, Object msg)
      throws Exception {

    String sentence = (String) msg;

    if (sentence.startsWith("a=connect")) {
      String id = sentence.substring(sentence.indexOf("i=") + 2);
      if (identify(id, channel)) {
        sendResponse(channel);
      }
      return null;
    }

    Parser parser = new Parser(PATTERN, sentence);
    if (!parser.matches()) {
      return null;
    }
    sendResponse(channel);

    Position position = new Position();
    position.setDeviceId(getDeviceId());
    position.setProtocol(getProtocolName());

    DateBuilder dateBuilder =
        new DateBuilder()
            .setTime(parser.nextInt(), parser.nextInt(), parser.nextInt(), parser.nextInt());

    position.setLatitude(parser.nextCoordinate());
    position.setLongitude(parser.nextCoordinate());

    position.set(Event.KEY_HDOP, parser.next());

    position.setAltitude(parser.nextDouble());

    int fix = parser.nextInt();
    position.set(Event.KEY_GPS, fix);
    position.setValid(fix > 0);

    position.setCourse(parser.nextDouble());
    position.setSpeed(parser.nextDouble());

    dateBuilder.setDateReverse(parser.nextInt(), parser.nextInt(), parser.nextInt());
    position.setTime(dateBuilder.getDate());

    position.set(Event.KEY_SATELLITES, parser.next());

    return position;
  }
  @Override
  protected Object decode(Channel channel, SocketAddress remoteAddress, Object msg)
      throws Exception {

    HttpRequest request = (HttpRequest) msg;
    QueryStringDecoder decoder = new QueryStringDecoder(request.getUri());

    DeviceSession deviceSession =
        getDeviceSession(channel, remoteAddress, decoder.getParameters().get("UserName").get(0));
    if (deviceSession == null) {
      return null;
    }

    Parser parser = new Parser(PATTERN, decoder.getParameters().get("LOC").get(0));
    if (!parser.matches()) {
      return null;
    }

    Position position = new Position();
    position.setProtocol(getProtocolName());
    position.setDeviceId(deviceSession.getDeviceId());

    DateBuilder dateBuilder =
        new DateBuilder()
            .setDateReverse(parser.nextInt(), parser.nextInt(), parser.nextInt())
            .setTime(parser.nextInt(), parser.nextInt(), parser.nextInt());
    position.setTime(dateBuilder.getDate());

    position.setValid(true);
    position.setLatitude(parser.nextDouble());
    position.setLongitude(parser.nextDouble());
    position.setAltitude(parser.nextDouble());
    position.setSpeed(parser.nextDouble());
    position.setCourse(parser.nextDouble());

    if (channel != null) {
      HttpResponse response = new DefaultHttpResponse(HttpVersion.HTTP_1_1, HttpResponseStatus.OK);
      channel.write(response).addListener(ChannelFutureListener.CLOSE);
    }

    return position;
  }
  @Override
  protected Object decode(Channel channel, SocketAddress remoteAddress, Object msg)
      throws Exception {

    String sentence = (String) msg;

    String type = sentence.substring(3, 7);
    if (channel != null) {
      channel.write("B" + type.substring(1)); // response
    }

    if (type.equals("AP00")) {
      identify(sentence.substring(7), channel, remoteAddress);
      return null;
    }

    if (!hasDeviceId()) {
      return null;
    }

    if (type.equals("AP01") || type.equals("AP10")) {

      Parser parser = new Parser(PATTERN, sentence);
      if (!parser.matches()) {
        return null;
      }

      Position position = new Position();
      position.setProtocol(getProtocolName());
      position.setDeviceId(getDeviceId());

      DateBuilder dateBuilder =
          new DateBuilder().setDate(parser.nextInt(), parser.nextInt(), parser.nextInt());

      position.setValid(parser.next().equals("A"));
      position.setLatitude(parser.nextCoordinate());
      position.setLongitude(parser.nextCoordinate());
      position.setSpeed(UnitsConverter.knotsFromKph(parser.nextDouble()));

      dateBuilder.setTime(parser.nextInt(), parser.nextInt(), parser.nextInt());
      position.setTime(dateBuilder.getDate());

      position.setCourse(parser.nextDouble());

      position.set(Event.KEY_GSM, parser.nextInt());
      position.set(Event.KEY_SATELLITES, parser.nextInt());
      position.set(Event.KEY_BATTERY, parser.nextInt());

      int acc = parser.nextInt();
      if (acc != 0) {
        position.set(Event.KEY_IGNITION, acc == 1);
      }

      position.set(Event.KEY_MCC, parser.nextInt());
      position.set(Event.KEY_MNC, parser.nextInt());
      position.set(Event.KEY_LAC, parser.nextInt());
      position.set(Event.KEY_CID, parser.nextInt());

      return position;
    }

    return null;
  }
  @Override
  protected Object decode(Channel channel, SocketAddress remoteAddress, Object msg)
      throws Exception {

    Parser parser = new Parser(PATTERN, (String) msg);
    if (!parser.matches()) {
      return null;
    }

    String manufacturer = parser.next();
    String id = parser.next();
    if (!identify(id, channel, remoteAddress)) {
      return null;
    }

    String type = parser.next();
    String content = parser.next();

    if (type.equals("LK")) {

      sendResponse(channel, manufacturer, id, "LK");

      if (!content.isEmpty()) {
        String[] values = content.split(",");
        if (values.length >= 4) {
          Position position = new Position();
          position.setProtocol(getProtocolName());
          position.setDeviceId(getDeviceId());

          getLastLocation(position, null);

          position.set(Event.KEY_BATTERY, values[3]);

          return position;
        }
      }

    } else if (type.equals("UD") || type.equals("UD2") || type.equals("AL")) {

      if (type.equals("AL")) {
        sendResponse(channel, manufacturer, id, "AL");
      }

      parser = new Parser(PATTERN_POSITION, content);
      if (!parser.matches()) {
        return null;
      }

      Position position = new Position();
      position.setProtocol(getProtocolName());
      position.setDeviceId(getDeviceId());

      DateBuilder dateBuilder =
          new DateBuilder()
              .setDateReverse(parser.nextInt(), parser.nextInt(), parser.nextInt())
              .setTime(parser.nextInt(), parser.nextInt(), parser.nextInt());
      position.setTime(dateBuilder.getDate());

      position.setValid(parser.next().equals("A"));
      position.setLatitude(parser.nextCoordinate(Parser.CoordinateFormat.DEG_HEM));
      position.setLongitude(parser.nextCoordinate(Parser.CoordinateFormat.DEG_HEM));
      position.setSpeed(UnitsConverter.knotsFromKph(parser.nextDouble()));
      position.setCourse(parser.nextDouble());
      position.setAltitude(parser.nextDouble());

      position.set(Event.KEY_SATELLITES, parser.nextInt());
      position.set(Event.KEY_GSM, parser.nextInt());
      position.set(Event.KEY_BATTERY, parser.nextInt());

      position.set("steps", parser.nextInt());

      return position;

    } else if (type.equals("TKQ")) {

      sendResponse(channel, manufacturer, id, "TKQ");
    }

    return null;
  }
  private ParseResult parsePosition(ChannelBuffer buf) {
    Position position = new Position();
    position.setProtocol(getProtocolName());

    position.setDeviceId(getDeviceId());

    int format;
    if (buf.getUnsignedByte(buf.readerIndex()) == 0) {
      format = buf.readUnsignedShort();
    } else {
      format = buf.readUnsignedByte();
    }
    position.set("format", format);

    long index = buf.readUnsignedInt();
    position.set(Event.KEY_INDEX, index);

    position.set(Event.KEY_EVENT, buf.readUnsignedShort());

    buf.skipBytes(6); // event time

    position.set(Event.KEY_ALARM, buf.readUnsignedByte());
    position.set(Event.KEY_STATUS, buf.readUnsignedByte());
    position.set(Event.KEY_GSM, buf.readUnsignedByte());

    if (isFormat(format, F10, F20, F30)) {
      position.set(Event.KEY_OUTPUT, buf.readUnsignedShort());
    } else if (isFormat(format, F40, F50, F51, F52)) {
      position.set(Event.KEY_OUTPUT, buf.readUnsignedByte());
    }

    if (isFormat(format, F10, F20, F30, F40)) {
      position.set(Event.KEY_INPUT, buf.readUnsignedShort());
    } else if (isFormat(format, F50, F51, F52)) {
      position.set(Event.KEY_INPUT, buf.readUnsignedByte());
    }

    position.set(Event.KEY_POWER, buf.readUnsignedShort() * 0.001);
    position.set(Event.KEY_BATTERY, buf.readUnsignedShort());

    if (isFormat(format, F10, F20, F30)) {
      position.set(Event.PREFIX_TEMP + 1, buf.readShort());
    }

    if (isFormat(format, F10, F20, F50, F52)) {
      position.set(Event.PREFIX_ADC + 1, buf.readUnsignedShort());
      position.set(Event.PREFIX_ADC + 2, buf.readUnsignedShort());
    }

    // Impulse counters
    if (isFormat(format, F20, F50, F51, F52)) {
      buf.readUnsignedInt();
      buf.readUnsignedInt();
    }

    if (isFormat(format, F20, F50, F51, F52)) {
      int locationStatus = buf.readUnsignedByte();
      position.setValid(BitUtil.check(locationStatus, 1));

      DateBuilder dateBuilder =
          new DateBuilder()
              .setTime(buf.readUnsignedByte(), buf.readUnsignedByte(), buf.readUnsignedByte())
              .setDateReverse(
                  buf.readUnsignedByte(), buf.readUnsignedByte(), buf.readUnsignedByte());
      position.setTime(dateBuilder.getDate());

      position.setLatitude(buf.readFloat() / Math.PI * 180);
      position.setLongitude(buf.readFloat() / Math.PI * 180);
      position.setSpeed(buf.readFloat());
      position.setCourse(buf.readUnsignedShort());

      position.set(Event.KEY_ODOMETER, buf.readFloat());

      position.set("segment", buf.readFloat()); // last segment

      // Segment times
      buf.readUnsignedShort();
      buf.readUnsignedShort();
    }

    // Other
    if (isFormat(format, F51, F52)) {
      buf.readUnsignedShort();
      buf.readByte();
      buf.readUnsignedShort();
      buf.readUnsignedShort();
      buf.readByte();
      buf.readUnsignedShort();
      buf.readUnsignedShort();
      buf.readByte();
      buf.readUnsignedShort();
    }

    // Four temperature sensors
    if (isFormat(format, F40, F52)) {
      buf.readByte();
      buf.readByte();
      buf.readByte();
      buf.readByte();
    }

    return new ParseResult(index, position);
  }
  @Override
  protected Object decode(Channel channel, SocketAddress remoteAddress, Object msg)
      throws Exception {

    ChannelBuffer buf = (ChannelBuffer) msg;

    buf.skipBytes(2); // header
    int type = buf.readUnsignedByte();
    buf.readUnsignedShort(); // length

    String id =
        decodeId(
            buf.readUnsignedByte(), buf.readUnsignedByte(),
            buf.readUnsignedByte(), buf.readUnsignedByte());

    if (type == MSG_HEARTBEAT) {

      if (channel != null) {
        ChannelBuffer response = ChannelBuffers.dynamicBuffer();
        response.writeByte(0x24);
        response.writeByte(0x24); // header
        response.writeByte(MSG_HEARTBEAT); // size
        response.writeShort(5);
        response.writeByte(buf.readUnsignedByte());
        response.writeByte(0); // main order
        response.writeByte(0); // slave order
        response.writeByte(1); // calibration
        response.writeByte(0x0D);
        channel.write(response);
      }

    } else if (type == MSG_POSITION_DATA
        || type == MSG_ROLLCALL_RESPONSE
        || type == MSG_ALARM_DATA
        || type == MSG_BLIND_AREA) {

      Position position = new Position();
      position.setProtocol(getProtocolName());

      if (!identify("1" + id, channel, remoteAddress, false)
          && !identify(id, channel, remoteAddress)) {
        return null;
      }
      position.setDeviceId(getDeviceId());

      DateBuilder dateBuilder =
          new DateBuilder()
              .setYear(BcdUtil.readInteger(buf, 2))
              .setMonth(BcdUtil.readInteger(buf, 2))
              .setDay(BcdUtil.readInteger(buf, 2))
              .setHour(BcdUtil.readInteger(buf, 2))
              .setMinute(BcdUtil.readInteger(buf, 2))
              .setSecond(BcdUtil.readInteger(buf, 2));
      position.setTime(dateBuilder.getDate());

      position.setLatitude(BcdUtil.readCoordinate(buf));
      position.setLongitude(BcdUtil.readCoordinate(buf));
      position.setSpeed(UnitsConverter.knotsFromKph(BcdUtil.readInteger(buf, 4)));
      position.setCourse(BcdUtil.readInteger(buf, 4));

      int flags = buf.readUnsignedByte();
      position.setValid((flags & 0x80) != 0);
      position.set(Position.KEY_SATELLITES, flags & 0x0f);

      position.set(Position.KEY_STATUS, buf.readUnsignedByte());
      position.set("key", buf.readUnsignedByte());
      position.set("oil", buf.readUnsignedShort() / 10.0);
      position.set(Position.KEY_POWER, buf.readUnsignedByte() + buf.readUnsignedByte() / 100.0);
      position.set(Position.KEY_ODOMETER, buf.readUnsignedInt());

      return position;
    }

    return null;
  }
  @Override
  protected Object decode(Channel channel, SocketAddress remoteAddress, Object msg)
      throws Exception {

    ChannelBuffer buf = (ChannelBuffer) msg;

    buf.skipBytes(2); // header
    buf.readUnsignedShort(); // length

    String imei = ChannelBuffers.hexDump(buf.readBytes(7));
    if (!identify(imei, channel, null, false)
        && !identify(imei + Checksum.luhn(Long.parseLong(imei)), channel)) {
      return null;
    }

    int type = buf.readUnsignedShort();

    if (type == MSG_LOCATION_REPORT || type == MSG_LOCATION_REQUEST) {

      String sentence =
          buf.toString(buf.readerIndex(), buf.readableBytes() - 8, Charset.defaultCharset());
      Parser parser = new Parser(PATTERN, sentence);
      if (!parser.matches()) {
        return null;
      }

      Position position = new Position();
      position.setProtocol(getProtocolName());
      position.setDeviceId(getDeviceId());

      if (parser.hasNext(15)) {

        DateBuilder dateBuilder =
            new DateBuilder()
                .setDate(parser.nextInt(), parser.nextInt(), parser.nextInt())
                .setTime(parser.nextInt(), parser.nextInt(), parser.nextInt());
        position.setTime(dateBuilder.getDate());

        position.setValid(parser.next().equals("A"));
        position.set(Event.KEY_SATELLITES, parser.next());

        position.setLatitude(parser.nextCoordinate(Parser.CoordinateFormat.HEM_DEG));
        position.setLongitude(parser.nextCoordinate(Parser.CoordinateFormat.HEM_DEG));

        position.setSpeed(parser.nextDouble());
        position.set(Event.KEY_HDOP, parser.nextDouble());
        position.setAltitude(parser.nextDouble());

      } else {

        getLastLocation(position, null);
      }

      position.set(Event.KEY_MCC, parser.nextInt());
      position.set(Event.KEY_MNC, parser.nextInt());
      position.set(Event.KEY_LAC, parser.nextInt());
      position.set(Event.KEY_CELL, parser.nextInt());

      return position;
    }

    return null;
  }
  @Override
  protected Object decode(Channel channel, SocketAddress remoteAddress, Object msg)
      throws Exception {

    ChannelBuffer buf = (ChannelBuffer) msg;

    if (buf.getUnsignedShort(buf.readerIndex()) == 0xfe02) {
      if (channel != null) {
        channel.write(buf, remoteAddress); // keep-alive message
      }
      return null;
    }

    buf.skipBytes(2); // prefix
    buf.readUnsignedShort(); // checksum
    buf.readUnsignedShort(); // length
    int index = buf.readUnsignedShort();

    long id = buf.readLong();
    if (!identify(String.valueOf(id), channel, remoteAddress)) {
      return null;
    }

    sendResponse(channel, remoteAddress, id, index);

    List<Position> positions = new LinkedList<>();

    while (buf.readableBytes() >= MIN_DATA_LENGTH) {

      Position position = new Position();
      position.setProtocol(getProtocolName());
      position.setDeviceId(getDeviceId());

      if (longDate) {

        DateBuilder dateBuilder =
            new DateBuilder()
                .setDate(buf.readUnsignedShort(), buf.readUnsignedByte(), buf.readUnsignedByte())
                .setTime(buf.readUnsignedByte(), buf.readUnsignedByte(), buf.readUnsignedByte());
        position.setTime(dateBuilder.getDate());

        buf.skipBytes(7 + 7);

      } else {

        position.setFixTime(new Date(buf.readUnsignedInt() * 1000));
        position.setDeviceTime(new Date(buf.readUnsignedInt() * 1000));
        buf.readUnsignedInt(); // send time
      }

      position.setValid(true);
      position.setLongitude(buf.readInt() * 0.000001);
      position.setLatitude(buf.readInt() * 0.000001);
      position.setCourse(buf.readUnsignedShort());

      position.set(Position.KEY_TYPE, buf.readUnsignedByte());
      position.set(Position.KEY_ODOMETER, buf.readUnsignedInt() * 0.1);
      position.set(Position.KEY_HDOP, buf.readUnsignedShort() * 0.1);
      position.set(Position.KEY_INPUT, buf.readUnsignedByte());

      position.setSpeed(UnitsConverter.knotsFromKph(buf.readUnsignedShort()));

      position.set(Position.KEY_OUTPUT, buf.readUnsignedByte());
      position.set(Position.PREFIX_ADC + 1, buf.readUnsignedShort() * 0.001);

      position.set("driver", readString(buf));

      position.set(Position.PREFIX_TEMP + 1, buf.readShort() * 0.1);
      position.set(Position.PREFIX_TEMP + 2, buf.readShort() * 0.1);

      position.set("message", readString(buf));

      if (custom) {
        String form = this.form;
        if (form == null) {
          form = readString(buf).substring("%CI".length());
        }
        readCustomData(position, buf, form);
      }

      positions.add(position);
    }

    return positions;
  }
  private Position decodeNormalMessage(
      ChannelBuffer buf, Channel channel, SocketAddress remoteAddress) {

    Position position = new Position();
    position.setProtocol(getProtocolName());

    buf.readByte(); // header

    String id = String.valueOf(Long.parseLong(ChannelBuffers.hexDump(buf.readBytes(5))));
    if (!identify(id, channel, remoteAddress)) {
      return null;
    }
    position.setDeviceId(getDeviceId());

    int version = ChannelBufferTools.readHexInteger(buf, 1);
    buf.readUnsignedByte(); // type
    buf.readBytes(2); // length

    DateBuilder dateBuilder =
        new DateBuilder()
            .setDay(ChannelBufferTools.readHexInteger(buf, 2))
            .setMonth(ChannelBufferTools.readHexInteger(buf, 2))
            .setYear(ChannelBufferTools.readHexInteger(buf, 2))
            .setHour(ChannelBufferTools.readHexInteger(buf, 2))
            .setMinute(ChannelBufferTools.readHexInteger(buf, 2))
            .setSecond(ChannelBufferTools.readHexInteger(buf, 2));
    position.setTime(dateBuilder.getDate());

    double latitude = convertCoordinate(ChannelBufferTools.readHexInteger(buf, 8));
    double longitude = convertCoordinate(ChannelBufferTools.readHexInteger(buf, 9));

    byte flags = buf.readByte();
    position.setValid((flags & 0x1) == 0x1);
    if ((flags & 0x2) == 0) {
      latitude = -latitude;
    }
    position.setLatitude(latitude);
    if ((flags & 0x4) == 0) {
      longitude = -longitude;
    }
    position.setLongitude(longitude);

    position.setSpeed(ChannelBufferTools.readHexInteger(buf, 2));
    position.setCourse(buf.readUnsignedByte() * 2.0);

    if (version == 1) {

      position.set(Event.KEY_SATELLITES, buf.readUnsignedByte());
      position.set(Event.KEY_POWER, buf.readUnsignedByte());

      buf.readByte(); // other flags and sensors

      position.setAltitude(buf.readUnsignedShort());

      int cid = buf.readUnsignedShort();
      int lac = buf.readUnsignedShort();
      if (cid != 0 && lac != 0) {
        position.set(Event.KEY_CID, cid);
        position.set(Event.KEY_LAC, lac);
      }

      position.set(Event.KEY_GSM, buf.readUnsignedByte());

    } else if (version == 2) {

      int fuel = buf.readUnsignedByte() << 8;

      position.set(Event.KEY_STATUS, buf.readUnsignedInt());
      position.set(Event.KEY_ODOMETER, buf.readUnsignedInt());

      fuel += buf.readUnsignedByte();
      position.set(Event.KEY_FUEL, fuel);
    }

    return position;
  }