package de.graeb.adsbsniffer.adbsreceiver;
import android.util.Log;
import com.google.common.primitives.Booleans;
import org.apache.commons.codec.binary.Hex;
import java.util.Arrays;
import java.util.Date;
import java.util.List;
import java.util.ListIterator;
import de.graeb.adsbsniffer.adbsreceiver.exceptions.ParseException;
public class ADSBParser {
/**
* Parses one line of a packet, should end with ';' and begin with '*' or '@'
*
* @param line a received line
* @param timestamp Timestamp when the packet was received
* @return the translated packet
* null if not a valid packet
*/
static Packet parse(byte[] line, Date timestamp) throws ParseException {
// check if ends with ';'
if (line[line.length - 1] != ';') {
throw new ParseException("not ending with ';' string: " + new String(line));
}
// strip ';'
String string = new String(line).substring(0, line.length - 1);
PacketBuilder packetBuilder = new PacketBuilder()
.setInternalTimestamp(timestamp);
if (line[0] == '*') {
// remove *, first character
string = string.substring(1);
} else if (line[0] == '@') {
// the first 12 characters are the timestamp
int[] externalTimestamp = hexStringToIntArray(string.substring(1, 13));
int value = 0;
for (int b : externalTimestamp) {
value = 256 * value + b;
}
packetBuilder.setExternalTimestamp(value);
string = string.substring(13);
} else {
throw new ParseException("Illegal start character string=" + new String(line));
}
// check if received message consists of '0's
zeros: {
for (int i = 0; i < string.length(); i++) {
if (string.charAt(i) != '0') {
break zeros;
}
}
Log.e("ADSBParser", "received 0 message");
return null;
}
// parse message
byte[] data = hexStringToByteArray(string);
int format = (data[0] & 0xF8) >> 3;
packetBuilder.setFormat(format)
.setMessage(string);
Log.d("ADSBParser", "message received: " + string);
byte[] crc = calculateCRC(data);
if (format == 17 || format == 18 || format == 11) {
//Extended squitter (17, 18) or All-call reply (11)
// contains PI field, the last 4bits are the interrogator code
String icao24 = string.substring(2, 8);
packetBuilder.setIcao24(icao24);
// check crc
if ((crc[0] == 0) && (crc[1] == 0) && ((crc[2] & 0xF0) == 0)) {
packetBuilder.setChecksumCorrect(TriState.TRUE);
} else {
Log.e("ADSBParser", "Adsb message with invalid crc: " + Arrays.toString(crc));
packetBuilder.setChecksumCorrect(TriState.FALSE);
}
if (format != 11) {
packetBuilder.setAdsb();
}
} else {
// PA Field used
String icao24 = String.valueOf(Hex.encodeHex(crc));
Log.d("ADSBParser", String.format("S-mode message format Nr: %d, message %s crc: %s",
format, new String(line), icao24));
packetBuilder.setIcao24(icao24);
}
return packetBuilder.createPacket();
}
private static int[] hexStringToIntArray(String s) {
if (s.length() % 2 > 0) {
throw new IllegalArgumentException(String.format("length not multiple of 2, string: '%s'", s));
}
int len = s.length() / 2;
int[] data = new int[len];
for (int i = 0; i < s.length() - 1; i += 2) {
data[i / 2] = (Character.digit(s.charAt(i), 16) << 4) +
Character.digit(s.charAt(i + 1), 16);
}
return data;
}
private static byte[] hexStringToByteArray(String s) {
int[] ints = hexStringToIntArray(s);
byte[] bytes = new byte[ints.length];
for (int i = 0; i < ints.length; i++) {
bytes[i] = (byte) ints[i];
}
return bytes;
}
private static final boolean[] CRC_POLYNOM = {true, true, true, true, true, true, true, true,
true, true, true, true, true, false, true, false,
false, false, false, false, false, true, false, false, true};
private static byte[] calculateCRC(byte[] input) {
List<Boolean> datastream = Booleans.asList(byteToBitVector(input));
for (int i = 0; i < datastream.size() - (CRC_POLYNOM.length - 1); i++) {
if (datastream.get(i)) {
for (int j = 0; j < CRC_POLYNOM.length; j++) {
datastream.set(i + j, CRC_POLYNOM[j] ^ datastream.get(i + j));
}
}
}
return bitVectorToByte(datastream.subList(datastream.size() - 24, datastream.size()));
}
private static boolean[] byteToBitVector(byte[] data) {
boolean[] out = new boolean[data.length * 8];
for (int i = 0; i < data.length * 8; i++) {
out[i] = ((data[i / 8] >> (7 - i % 8)) & 1) > 0;
}
return out;
}
private static byte[] bitVectorToByte(List<Boolean> data) {
byte[] out = new byte[data.size() / 8];
Arrays.fill(out, (byte) 0);
ListIterator<Boolean> iterator = data.listIterator();
while (iterator.hasNext()) {
Boolean value = iterator.next();
if (value) {
out[iterator.previousIndex() / 8] |= (1 << (7 - (iterator.previousIndex() % 8)));
}
}
return out;
}
}