/// <summary>
        /// Calculates y coordinate based on the AntennaRead, the calculated x coordinate and the Mathematical Model parameters
        /// </summary>
        /// <param name="ant2"></param>
        /// <param name="tagXCoordinate"></param>
        /// <returns>
        /// Returns calculated y coordinate
        /// </returns>
        private static double CalculateTagYCoordinate(AntennaRead ant2, double tagXCoordinate)
        {
            /**
             * Tag Y Coordinate
             *
             * RAIZQ(
             *  ( RSSI_MAX - RSSI_2 )^2 - ( X_AMP^2 * ( TAG_X_COORDINATE - ANT_2_X_COORD )^2 )
             * )
             * *
             * L_MAX / ( RSSI_MAX - RSSI_MIN )
             */

            double tagYCoordinate = Math.Sqrt(
                Math.Pow(MathematicModelParameters.RssiMax - ant2.RSSI, 2)
                - (Math.Pow(MathematicModelParameters.XAmp, 2) * Math.Pow(tagXCoordinate - (double)ant2.Antenna.Position.X, 2))
                ) * MathematicModelParameters.LMax / (MathematicModelParameters.RssiMax - MathematicModelParameters.RssiMin);

            return(tagYCoordinate);
        }
        /// <summary>
        /// Calculates x coordinate based on the AntennaReads combination and the Mathematical Model parameters
        /// </summary>
        /// <param name="ant1"></param>
        /// <param name="ant2"></param>
        /// <returns>
        /// Returns calculated x coordinate
        /// </returns>
        private static double CalculateTagXCoordinate(AntennaRead ant1, AntennaRead ant2)
        {
            /**
             * Tag X Coordinate
             *
             * ( ( RSSI_1 * ( RSSI_1 - ( 2 * RSSI_MAX ) + RSSI_2 * ( 2 * RSSI_MAX - RSSI_2 ) )
             *      /
             *      ( 2 * X_AMP^2 ) * ( ANT_2_X_COORD - ANT_1_X_COORD ) )
             * +
             * ( ( ANT_1_COORD + ANT_2_COORD ) / 2 )
             */

            double var1 = ant1.RSSI * (ant1.RSSI - 2 * MathematicModelParameters.RssiMax)
                          + ant2.RSSI * (2 * MathematicModelParameters.RssiMax - ant2.RSSI);

            double var2 = 2 * Math.Pow(MathematicModelParameters.XAmp, 2) * (double)(ant2.Antenna.Position.X - ant1.Antenna.Position.X);

            double var3 = var1 / var2;

            double tagXCoordinate = var3 + ((double)(ant1.Antenna.Position.X + ant2.Antenna.Position.X) / 2);

            return(tagXCoordinate);
        }
        /// <summary>
        /// Creates a new Tag by calculating its position based on a TagRead's data.
        /// </summary>
        /// <param name="tagRead"></param>
        /// <returns>
        /// Returns a new Tag
        /// Returns null if unable to calculate new Tag's position
        /// </returns>
        private static Tag CalculateTagPosition(TagRead tagRead)
        {
            /// Ignore TagRead if it wasn't obtained by at least 2 UHFreaders
            if (tagRead.AntennasReads.Count < 2)
            {
                return(null);
            }

            /// Used to get the sum of all x coordinates values
            double xx = 0;

            /// Used to get the sum of all y coordinates values
            double yy = 0;

            /// Number of (x, y) positions found
            int counter = 0;

            /// Obtains every combination of AntennaReads of different UHFReaders and calculates x and y positions for the new Tag
            for (int i = 0; i < tagRead.AntennasReads.Count - 1; i++)
            {
                for (int j = i + 1; j < tagRead.AntennasReads.Count; j++)
                {
                    /// First AntennaRead of combination of AntennaReads
                    AntennaRead ant1 = tagRead.AntennasReads[i];

                    /// Second AntennaRead of combination of AntennaReads
                    AntennaRead ant2 = tagRead.AntennasReads[j];

                    /// Obtain x by calculating its value with both AntennaReads
                    double x = CalculateTagXCoordinate(ant1, ant2);

                    /// Ignore AntennaReads combination if x doesn't have a valid value
                    if (double.IsNaN(x))
                    {
                        continue;
                    }

                    /// Obtain y by calculating its value with AntennaRead and obtained x coordinate
                    double y = CalculateTagYCoordinate(ant2, x);

                    /// Ignore AntennaReads combination if y doesn't have a valid value
                    if (double.IsNaN(y))
                    {
                        continue;
                    }

                    xx += x;
                    yy += y;
                    counter++;
                }
            }

            /// If no position was obtained, discard
            if (counter == 0)
            {
                return(null);
            }

            /// If position value is not valid, discard
            if (double.IsInfinity(yy))
            {
                return(null);
            }

            /// Get average values of all x and y, obtained during the iteration of every combination of AntennaReads
            double xAvg = xx / counter;
            double yAvg = yy / counter;

            /// Convert x and y values to a decimal with only 2 decimal digits
            decimal xRounded = (decimal)Math.Round(xAvg * 100) / 100;
            decimal yRounded = (decimal)Math.Round(yAvg * 100) / 100;

            /// Create new Tag
            Tag tag = new Tag(tagRead.ID, xRounded, yRounded, tagRead.AntennasReads);

            return(tag);
        }
        /// <summary>
        /// Takes all MetaTags joined, from both UHFReaders, and transforms them into tagReads.
        /// <para>This process is necessary because the mathematical model works with TagReads instead of MetaTags.</para>
        /// </summary>
        /// <param name="mtll"></param>
        /// <returns>
        /// Returns list of TagReads
        /// </returns>
        public static List <TagRead> TransformReadsIntoTagReads(List <List <MetaTag> > mtll)
        {
            /// Initialize the new list of TagReads
            List <TagRead> tagsReads = new List <TagRead>();

            /// For each Metatag, of each list:
            ///  Creates/Adds a new TagRead into the list of TagReads, if it doesn't exist yet in the list;
            ///  Creates/Updates the TagRead on the list of TagReads, if it already exists in the list.
            foreach (List <MetaTag> mtl in mtll)
            {
                foreach (MetaTag mt in mtl)
                {
                    /// Create new TagRead from MetaTag's EPC
                    TagRead tagRead = new TagRead(mt.EPC);

                    bool found = false;
                    foreach (TagRead tr in tagsReads)
                    {
                        /// Compares TagRead's ID (EPC) with MetaTag's EPC
                        if (tr.ID == mt.EPC)
                        {
                            /// TagRead doesn't exist in TagReads list

                            found = true;

                            if (tr.TimeStamp < mt.TimeStamp)
                            {
                                /// Update TagRead to its most recent timestamp
                                tr.TimeStampDateTime = mt.TimeStampDateTime;
                                tr.TimeStamp         = mt.TimeStamp;
                            }

                            if (mt.RSSIAvg != -1)
                            {
                                /// Update list of AntennaRead of TagRead
                                AntennaRead antRead = new AntennaRead(mt.Antenna, mt.RSSIAvg);
                                tr.AntennasReads.Add(antRead);
                            }

                            break;
                        }
                    }

                    /// TagRead doesn't exist in TagReads list
                    if (!found)
                    {
                        /// Get timestamp from MetaTag that created this TagRead
                        tagRead.TimeStampDateTime = mt.TimeStampDateTime;
                        tagRead.TimeStamp         = mt.TimeStamp;

                        if (mt.RSSIAvg != -1)
                        {
                            /// Add AntennaRead to list of AntennaRead of TagRead
                            AntennaRead antRead = new AntennaRead(mt.Antenna, mt.RSSIAvg);
                            tagRead.AntennasReads.Add(antRead);

                            tagsReads.Add(tagRead);
                        }
                    }
                }
            }

            return(tagsReads);
        }