private static bool BruteForceAP(ref ModeSMessage message) { var aux = new byte[ModesLongMessageBytes]; int messageType = message.MessageType; int messageBits = message.RawMessage.Length * 8; if (messageType != 0 && messageType != 4 && messageType != 5 && messageType != 16 && messageType != 20 && messageType != 21 && messageType != 24) { return(false); } int lastByte = (messageBits / 8) - 1; Array.Copy(message.RawMessage, aux, messageBits / 8); // -- Compute the CRC of the message and XOR it with the AP field to recover the address. // -- (ADDR xor CRC) xor CRC = ADDR uint crc = Checksum(aux, messageBits); aux[lastByte] ^= (byte)(crc & 0xff); aux[lastByte - 1] ^= (byte)((crc >> 8) & 0xff); aux[lastByte - 2] ^= (byte)((crc >> 16) & 0xff); var address = (uint)(aux[lastByte] | (aux[lastByte - 1] << 8) | (aux[lastByte - 2] << 16)); if (!WasICAORecentlySeen(address)) { return(false); } message.Aa1 = aux[lastByte - 2]; message.Aa2 = aux[lastByte - 1]; message.Aa3 = aux[lastByte]; return(true); }
public static ModeSMessage DecodeMessage(byte[] message) { var result = new ModeSMessage(); // -- Get the message type first. result.MessageType = message[0] >> 3; /* Downlink Format */ result.RawMessage = new byte[GetMessageLength(result.MessageType)]; result.Crc = Checksum1(message, result.RawMessage.Length * 8); uint crc2 = Checksum(message, result.RawMessage.Length * 8); Array.Copy(message, result.RawMessage, result.RawMessage.Length); result.ErrorBit = -1; result.IsCrcOk = (result.Crc == crc2); // -- Error correction if (!result.IsCrcOk && FixErrors && (result.MessageType == 11 || result.MessageType == 17)) { result.ErrorBit = FixSingleBitErrors(ref result.RawMessage, result.RawMessage.Length * 8); if (result.ErrorBit != -1) { result.Crc = Checksum(result.RawMessage, result.RawMessage.Length * 8); result.IsCrcOk = true; } } result.Ca = message[0] & 7; // -- Capabilities result.Aa1 = message[1]; // -- ICAO address result.Aa2 = message[2]; result.Aa3 = message[3]; result.ICAO = (uint)((result.Aa1 << 16) | (result.Aa2 << 8) | (result.Aa3)); // -- DF-17 Type result.MessageTypeExtended = message[4] >> 3; result.MessageSubType = message[4] & 7; // -- Fields for DF4,5,20,21. result.FlightStatus = message[0] & 7; result.DownlinkRequest = message[1] >> 3 & 31; result.UtilityMessage = ((message[1] & 7) << 3) | message[2] >> 5; { // -- squawk decoding. int a, b, c, d; a = ((message[3] & 0x80) >> 5) | ((message[2] & 0x02) >> 0) | ((message[2] & 0x08) >> 3); b = ((message[3] & 0x02) << 1) | ((message[3] & 0x08) >> 2) | ((message[3] & 0x20) >> 5); c = ((message[2] & 0x01) << 2) | ((message[2] & 0x04) >> 1) | ((message[2] & 0x10) >> 4); d = ((message[3] & 0x01) << 2) | ((message[3] & 0x04) >> 1) | ((message[3] & 0x10) >> 4); result.Identity = a * 1000 + b * 100 + c * 10 + d; } // -- DF 11 & 17; Populate ICAO Whitelist. For DFs with an AP field, try to decode it. if (result.MessageType != 11 && result.MessageType != 17) { result.IsCrcOk = BruteForceAP(ref result); } else { // -- If this is DF11 or 17 and checksum is ok, we can add this to list of recently seen. if (result.IsCrcOk && result.ErrorBit == -1) { var address = (uint)result.ICAO; AddRecentlySeenICAOAddress(address); } } // -- Decode 13 bit altitude for DF0,16,20 if (result.MessageType == 0 || result.MessageType == 4 || result.MessageType == 16 || result.MessageType == 20) { var altitudeUnit = 0; result.Altitude = DecodeAC13Field(result.RawMessage, ref altitudeUnit); result.Unit = altitudeUnit; } // -- Extended squitter specific stuff if (result.MessageType == 17) { if (result.MessageTypeExtended >= 1 && result.MessageTypeExtended <= 4) { // -- Aircraft Flight Information result.AircraftType = result.MessageTypeExtended - 1; result.Flight = GetAircraftIdentifier(message); } else if (result.MessageTypeExtended >= 9 && result.MessageTypeExtended <= 18) { // -- Position message var positionUnit = 0; result.Fflag = message[6] & (1 << 2); result.Tflag = message[6] & (1 << 3); result.Altitude = DecodeAC12Field(message, ref positionUnit); result.RawLatitude = ((message[6] & 3) << 15) | (message[7] << 7) | (message[8] >> 1); result.RawLongitude = ((message[8] & 1) << 16) | (message[9] << 8) | message[10]; } else if (result.MessageTypeExtended == 19 && result.MessageSubType >= 1 && result.MessageSubType <= 4) { // -- Velocity Message } else if (result.MessageSubType == 3 || result.MessageSubType == 4) { // -- Heading message. result.HeadingIsValid = (message[5] & (1 << 2)) > 0; result.Heading = (int)((360.0 / 128) * (((message[5] & 3) << 5) | (message[6] >> 3))); } } result.IsPhaseCorrected = false; return(result); }