/// <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;
            }
        }