/// <summary>
 /// Ensure that registered delegates receive the FrameAvailable event.
 /// </summary>
 /// <param name="e">Event argument.</param>
 private void OnFrameAvailable(ModeSFrameAvailableEventArgs e)
 {
     // If there are subscriber(s), raises event.
     ModeSFrameAvailable?.Invoke(this, e);
 }
        /// <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;
            }
        }