/// <summary> /// Get ICAO from the Address/Parity. /// </summary> /// <param name="rawFrame">Mode S frame.</param> /// <returns>ICAO or 0 if something is wrong.</returns> public static uint GetICAOAddressFromAddressParity(byte[] rawFrame) { // Get the DF. var df = GetDownlinkFormat(rawFrame); // DF11, DF17 and DF18 do not have AP, they have PI. if (df == ModeSDownlinkFormats.DF11 || df == ModeSDownlinkFormats.DF17 || df == ModeSDownlinkFormats.DF18) { return(0); } // Compute checksum of the frame. var checksum = ModeSParity.Checksum(rawFrame); // Store the frame's length. var length = rawFrame.Length; // Recover the ICAO from the AP field. // Formula: (ICAO ^ CRC) ^ CRC = ICAO rawFrame[length - 1] = (byte)(rawFrame[length - 1] ^ ((checksum >> 0) & 0xff)); rawFrame[length - 2] = (byte)(rawFrame[length - 2] ^ ((checksum >> 8) & 0xff)); rawFrame[length - 3] = (byte)(rawFrame[length - 3] ^ ((checksum >> 16) & 0xff)); // Return the ICAO. return(rawFrame[length - 1] | (uint)rawFrame[length - 2] << 8 | (uint)rawFrame[length - 3] << 16); }
/// <summary> /// Get the ICAO from the Mode S frame. /// </summary> /// <param name="rawFrame">Mode S frame.</param> /// <returns>ICAO or 0 if something is wrong.</returns> public static uint GetICAOAddress(byte[] rawFrame) { // Get the DF. var df = GetDownlinkFormat(rawFrame); // Calculate the frame's syndrome. var syndrome = ModeSParity.Syndrome(rawFrame); // Local variable to store the information: do the frame have ICAO address field or not. bool hasAddress; // Assign the "hasAddress" variable. switch (df) { // DF11 has ICAO address field. case ModeSDownlinkFormats.DF11: syndrome &= 0xFFFF80; hasAddress = true; break; // DF17 has ICAO address field. case ModeSDownlinkFormats.DF17: hasAddress = true; break; // DF18 can have ICAO address field, if the Control Field is 0. case ModeSDownlinkFormats.DF18: // Get the control field. var cf = rawFrame[0] & 0x07; // If the CF is 0, DF18 has ICAO address field (AA). if (cf == 0) { hasAddress = true; break; } // If the CF is not 0, DF18 does not have ICAO address field. hasAddress = false; break; // DF0, DF4, DF5, DF16, DF20, DF21, DF24 do not have ICAO address field. case ModeSDownlinkFormats.DF0: case ModeSDownlinkFormats.DF4: case ModeSDownlinkFormats.DF5: case ModeSDownlinkFormats.DF16: case ModeSDownlinkFormats.DF20: case ModeSDownlinkFormats.DF21: case ModeSDownlinkFormats.DF24: hasAddress = false; break; // Other DFs are invalid. default: return(0); } // If the frame does not have address, return the syndrome which is the ICAO itself. if (!hasAddress) { return(syndrome); } // If the syndrome is 0, return the ICAO (9-32 bit). if (syndrome == 0x000000) { return((uint)(rawFrame[1] << 16) | (uint)(rawFrame[2] << 8) | rawFrame[3]); } // If the syndrome is not 0, try to fix. var errorBit = ModeSParity.ErrorBit(rawFrame.Length, syndrome); // If the faulty bit's position less than 5, fix is not possible. if (errorBit < 5) { return(0); } // Return fixed ICAO. rawFrame[errorBit / 8] = (byte)(rawFrame[errorBit / 8] ^ (1 << (7 - errorBit % 8))); return((uint)(rawFrame[1] << 16) | (uint)(rawFrame[2] << 8) | rawFrame[3]); }
/// <summary> /// Process the data. Will be invoked after preamble validation. /// </summary> private void ProcessSample() { // Reset the frame buffer. Array.Clear(_frameBuffer, 0, _frameBuffer.Length); // Set the initial helper variables. var frameBitLength = 0; var lastMagnitude = 0f; var lastAverageMagnitude = 0f; // Get the actual pointer and skips the preamble. var pointer = (_candidateFramePointer + PreambleBitLength) % _candidateFrameBuffer.Length; // Go through on the candidate frame, and creates a final one. for (var i = 0; i < LongFrameBitLength * 2; i++) { // Get the magnitude from the action pointer and steps forward. var magnitude = (float)_candidateFrameBuffer[pointer]; pointer = (pointer + 1) % _candidateFrameBuffer.Length; // If the bit is even, does other tasks. if (i % 2 != 0) { // Calculate the new average of magnitude. var averageMagnitude = (magnitude + lastMagnitude) * 0.5f; // Apply correction, if the last average is higher than 0. if (lastAverageMagnitude > 0f) { // Calculate correction. var correction = -CorrectionFactor * (averageMagnitude - lastAverageMagnitude) / averageMagnitude; // If the correction is higher than 0, apply correction on the magnitude value, // and calculate the new average. if (correction > 0) { magnitude += correction; averageMagnitude = (magnitude + lastMagnitude) * 0.5f; } } // Save the average as "previous" for further usage. lastAverageMagnitude = averageMagnitude; // Decide about the value of the bit based on the magnitude. var bit = lastMagnitude > magnitude ? 1 : 0; // Get bit position from sample position. var frameBitPosition = i / 2; // Store the bit, if the bit's value is 1, // Remark: previously the frame buffer was filled with zeros. if (bit == 1) { var index = frameBitPosition / 8; var shift = 7 - (frameBitPosition % 8); _frameBuffer[index] += (byte)(1 << shift); } // Decide about the downlink format (DF), if the frame's bit position is 7. if (frameBitPosition == 7) { // Return, if the first byte is zero (the frame is not valid). if (_frameBuffer[0] == 0) { return; } // Get the downlink format of the frame. var df = ModeSHelper.GetDownlinkFormat(_frameBuffer); // Assign the good length to the frame. switch (df) { // Short DFs. case ModeSDownlinkFormats.DF0: case ModeSDownlinkFormats.DF4: case ModeSDownlinkFormats.DF5: case ModeSDownlinkFormats.DF11: frameBitLength = ShortFrameBitLength; break; // Long DFs. case ModeSDownlinkFormats.DF16: case ModeSDownlinkFormats.DF17: case ModeSDownlinkFormats.DF18: case ModeSDownlinkFormats.DF20: case ModeSDownlinkFormats.DF21: case ModeSDownlinkFormats.DF24: frameBitLength = LongFrameBitLength; break; // Any other number is not valid. default: return; } } // Approach the final bit of the frame. if (frameBitLength > 0 && frameBitPosition == frameBitLength - 1) { // Get length of the frame in byte. var frameByteLength = frameBitLength / 8; // If the last three bits are 0, the frame is not valid. if (_frameBuffer[frameByteLength - 1] == 0 && _frameBuffer[frameByteLength - 2] == 0 && _frameBuffer[frameByteLength - 3] == 0) { return; } // Create the final frame buffer with valid length. var finalFrameBuffer = _frameBuffer.Take(frameByteLength).ToArray(); // Local variable to store ICAO of the frame. uint icao; // Check the parity. var df = ModeSHelper.GetDownlinkFormat(finalFrameBuffer); switch (df) { // Check the frames which have Parity/Interrogator. case ModeSDownlinkFormats.DF11: case ModeSDownlinkFormats.DF17: case ModeSDownlinkFormats.DF18: // The syndrome must be zero, this means that the frame is valid. if (ModeSParity.Syndrome(finalFrameBuffer) != 0) { return; } // Get the ICAO from the frame. icao = ModeSHelper.GetICAOAddress(finalFrameBuffer); // Break. break; // Check the frames which have Address/Parity. case ModeSDownlinkFormats.DF0: case ModeSDownlinkFormats.DF4: case ModeSDownlinkFormats.DF5: case ModeSDownlinkFormats.DF16: case ModeSDownlinkFormats.DF20: case ModeSDownlinkFormats.DF21: case ModeSDownlinkFormats.DF24: // Get the ICAO from the Address/Parity. icao = ModeSHelper.GetICAOAddress(finalFrameBuffer); // If this ICAO is in the trusted list, we assume that the frame is valid. if (!_trustedICAOList.ContainsKey(icao)) { return; } // Break. break; // Invalid DF. default: return; } // Return, if the the ICAO is not trusted and the candidate's confidence is not enough. if (!_trustedICAOList.ContainsKey(icao) && !GetCandidateICAOConfidence(icao)) { UpdateCandidateICAOList(icao); return; } // Update trusted ICAOs "last seen" value and remove the ICAO from the candidate list. _trustedICAOList[icao] = DateTime.UtcNow; _candidateICAOList.TryRemove(icao, out _); // Raise the ModeSFrameAvailable event. var args = new ModeSFrameAvailableEventArgs( new ModeSRawFrame(finalFrameBuffer, icao, df)); OnFrameAvailable(args); // Return. return; } } // Save the current magnitude as "previous" for the future. lastMagnitude = magnitude; } }