/// <summary>
        /// Encode the length of the packets that are in the data BufferChunk array and place the
        /// encoded length over GF16 in the 2 first byte of the checksum packets that are in
        /// the result BufferChunk array
        /// </summary>
        /// <param name="data">The BufferChunk array containing the data packets to get the size</param>
        /// <param name="checksum">The number of checksum packets to generate.
        /// This number is actually also the number of column of the Vandermonde
        /// encoding matrix</param>
        /// <param name="encode">The Vandermonde encoding matrix (size min should be
        /// Vandermonde #column: checksum, Vandermonde #row: data.Length)
        /// </param>
        /// <param name="result">The BufferChunk checksum packets array of containing the encoded length (over GF16)
        /// in the first 2 bytes</param>
        private static void EncodeRSLength(BufferChunk[] data, BufferChunk[] result, UInt16[,] encode)
        {
            // Get the length once to avoid having the overhead of getting it again inside a inner (performance)
            // Note that this value is also used outside of this method, so we could have had a parameter too,
            // but I prefer to get it here to avoid inconsistencies
            int dataPackets = data.Length;
            int checksum    = result.Length;

            // Note: The size of the Vandermonde matrix used to encode the length is
            // Vandermonde #column: checksum, Vandermonde #row: dataPackets

            // TODO: Validate if Vandermonde # max column >= checksum and
            // Vandermonde #row >= dataPackets

            // TODO: Validate that there are no null entries in data and result

            // TODO: Validate that all the checksum packet have at least 2 bytes

            // Length encoding
            for (int encodeColumn = 0; encodeColumn < checksum; encodeColumn++)
            {
                UInt16 encodeValue = 0;

                for (int dataPacket = 0; dataPacket < dataPackets; dataPacket++)
                {
                    // TODO: performance - lengths of BC don't change between loops, store once? JVE
                    UInt16 dataValue = GF16.Multiply((UInt16)data[dataPacket].Length, encode[encodeColumn, dataPacket]);
                    encodeValue = GF16.Add(encodeValue, dataValue);
                }

                result[encodeColumn] += encodeValue;
            }
        }
예제 #2
0
        /// <summary>
        /// Create a Vandermonde matrix of size row x column over GF16
        /// </summary>
        /// <remarks>
        /// The Vandermonde matrix is typically used to create the encoding matrix where:
        /// - The number of Columns of the matrix correspond to number of checksum
        /// packets.
        /// - The number of Rows of the matrix correspond to number of data packets.
        /// </remarks>
        /// <param name="columns">The number of columns of the Vandermonde matrix</param>
        /// <param name="rows">The number of rows of the Vandermode matrix</param>
        /// <returns></returns>
        public static UInt16[,] CreateVandermondeMatrix(int columns, int rows)
        {
            // TODO: Add input validation

            // maxChecksumPackets will be the max number of Columns of the encoding static matrix
            // maxDataPackets will be the max number of Rows of the encoding static matrix

            UInt16[,] vandermondeMtx = new UInt16[columns, rows];

            // Creation of the Vandermonde Matrix over GF16 with the following
            // (2^column)^row

            // As an example, a 5 x 3 Vandermonde Matrix over GF16 (5 data packets, 3 checksum packets)
            // would give the following:
            //
            // 1^0 2^0 4^0
            // 1^1 2^1 4^1
            // 1^2 2^2 4^2
            // 1^3 2^3 4^3
            // 1^4 2^4 4^4
            //
            // Which gives:
            //
            // 1   1   1
            // 1   2   4
            // 1   4   16
            // 1   8   64
            // 1   16  256

            for (int col = 0; col < columns; col++)
            {
                // multFactor is the number to multiply to get the value in the next row
                // for a given column of the Vandermonde matrix
                UInt16 multFactor = GF16.Power(2, (uint)col);
                for (int row = 0; row < rows; row++)
                {
                    if (row == 0)
                    {
                        // Special case the first row (power of zero)
                        vandermondeMtx[col, row] = 1;
                    }
                    else
                    {
                        // Each element of the Vandermonde matrix is calculated as (2^column)^row over GF16

                        // This algorithm uses the previous row to compute the next one to improve
                        // the performances (instead of recalculating (2^column)^row)
                        vandermondeMtx[col, row] = GF16.Multiply(vandermondeMtx[col, row - 1], multFactor);
                    }
                }
            }
            return(vandermondeMtx);
        }
        /// <summary>
        /// Decode to retrieve the missing data packet(s) (null entries) in the data BufferChunk array and place the
        /// decoded result over GF16 in order in the recovery BufferChunk array
        /// </summary>
        /// <param name="data">The data BufferChunk array</param>
        /// <param name="checksum">The number of checksum packet(s) that were used to encode</param>
        /// <param name="decode">The decoding GF16 matrix</param>
        /// <param name="recovery">The recovery BufferChunk array to place the recovered result</param>
        private static void DecodeRSData(BufferChunk[] data, int checksum, GF16[,] decode, BufferChunk[] recovery)
        {
            // Important! For now I assume the following in the firstMatrix:
            // - The array is always of size data + checksum
            // - #checksum not null entries = #packet lost
            // - recovery.length is exactly the number of data packet lost
            // - checksum param is # checksum packets that were used to encode

            // Reconstruct data

            // Get the length once to avoid having the overhead of getting it again inside a inner (performance)
            // Note that this value is also used outside of this method, so we could have had a parameter too,
            // but I prefer to get it here to avoid inconsitencies
            int dataLength = data.Length;

            // Scan column after column
            int recoveryColumn = 0;

            for (int dataColumn = 0; dataColumn < dataLength - checksum; dataColumn++)
            {
                // recover only the missing column
                if (data[dataColumn] == null)
                {
                    int recoveryRowLength = recovery[recoveryColumn].Length;

                    // For each column fill out the output mtx row after row
                    for (int recoveryRow = 0; recoveryRow < recoveryRowLength; recoveryRow += 2)
                    {
                        int imRowIndex = 0;

                        // nnDataColumn - not null data index
                        for (int nnDataColumn = 0; nnDataColumn < dataLength; nnDataColumn++)
                        {
                            // Perform the core operation mult with add in GF16
                            // TODO: We could crunch the column so we avoid this test
                            if (data[nnDataColumn] != null)
                            {
                                if (recoveryRow < data[nnDataColumn].Length)
                                {
                                    UInt16 currentValue = GF16.Multiply(data[nnDataColumn].GetUInt16(recoveryRow),
                                                                        decode[dataColumn, imRowIndex].Value);
                                    currentValue = GF16.Add(recovery[recoveryColumn].GetUInt16(recoveryRow), currentValue);
                                    recovery[recoveryColumn].SetUInt16(recoveryRow, currentValue);
                                }
                                imRowIndex++;
                            }
                        }
                    }

                    recoveryColumn++;
                }
            }
        }
예제 #4
0
        /// <summary>
        /// Invert the matrix by using Gauss-Jordan using GF16 operations
        /// </summary>
        /// <remarks>
        /// The inversion is done by adding the identity matrix to the right
        /// and using Gauss-Jordan algorithm (elementary
        /// operations on rows) until we get the identity matrix 
        /// on the left. Then we strip the identity on the left
        /// in order to get the reverted matrix.
        /// </remarks>
        /// <param name="mtxIn_GF16">The matrix to invert</param>
        /// <returns>The inverted matrix</returns>
        public static GF16[,] Invert(GF16[,] mtxIn_GF16)
        {
            // Add the ID to the right to be able to apply Gauss-Jordan to revert the matrix
            GF16[,] mtxWithID_GF16 = FEC_Matrix.AddIdentityRight(mtxIn_GF16);

            // Apply Gauss Jordan algo to revert the matrix
            GF16[,] mtxInvert_GF16 = FEC_Matrix.GaussJordan(mtxWithID_GF16);

            // Remove the ID on the left
            GF16[,] mtxInvertStripped_GF16 = FEC_Matrix.StripIdentity(mtxInvert_GF16);

            // Return the inverted matrix
            return mtxInvertStripped_GF16;
        }
예제 #5
0
        /// <summary>
        /// Remove the identity at the front of the matrix
        /// </summary>
        /// <param name="matrixIn">The input matrix</param>
        /// <returns>Stripped Matrix without the identity at the front</returns>
        private static GF16[,] StripIdentity(GF16[,] matrixIn)
        {
            int rowLength    = matrixIn.GetLength(1);
            int columnLength = matrixIn.GetLength(0);

            GF16[,] matrixOut = new GF16[columnLength - rowLength, rowLength];

            for (int row = 0; row < rowLength; row++)
            {
                for (int column = rowLength; column < columnLength; column++)
                {
                    matrixOut[column - rowLength, row] = matrixIn[column, row];
                }
            }

            return(matrixOut);
        }
예제 #6
0
        /// <summary>
        /// Add identity to the right side of a matrix
        /// </summary>
        /// <remarks>This is use when doing Jordan-Gauss elimination</remarks>
        /// <param name="matrixIn">The matrix</param>
        /// <returns>The new matrix with the identity</returns>
        private static GF16[,] AddIdentityRight(GF16[,] matrixIn)
        {
            Int32 rowLength    = matrixIn.GetLength(1);
            Int32 columnLength = matrixIn.GetLength(0);

            GF16[,] matrixOut = new GF16[columnLength * 2, rowLength];

            // Add in identity portion to the top
            for (Int32 column = 0; column < columnLength; column++)
            {
                matrixOut[column + columnLength, column] = (UInt16)1;
            }

            // Copy the previous matrix to the bottom
            MatrixCopyTo(matrixIn, matrixOut);

            return(matrixOut);
        }
예제 #7
0
        /// <summary>
        /// Power is used to create teh Vandermonde matrix
        /// </summary>
        /// <param name="exponent"></param>
        /// <returns></returns>
        public GF16 Power(int exponent)
        {
            if (Value == 0)
            {
                return(this);
            }
            if (exponent == 0)
            {
                return(new GF16((UInt16)1));
            }

            GF16 original = this;

            for (int i = 1; i < exponent; i++)
            {
                this *= original;
            }

            return(this);
        }
        /// <summary>
        /// Encode the packets that are in the data BufferChunk array and place the
        /// encoded result over GF16 in the checksum packets that are in
        /// the result BufferChunk array
        /// </summary>
        /// <param name="data">The BufferChunk array containing the data packets</param>
        /// <param name="checksum">The number of checksum packets to generate.
        /// This number is actually also the number of column of the Vandermonde
        /// encoding matrix</param>
        /// <param name="checksum">The BufferChunk checksum packets array of containing the encoded data (over GF16)
        /// after the first 2 bytes (the first 2 bytes are used to encode the length)</param>
        /// <param name="encode">The Vandermonde encoding matrix (size min should be
        /// Vandermonde #column: checksum, Vandermonde #row: data.Length)
        /// </param>
        /// <param name="checksumRowsInt16">The number of row (in int 16 chunks) of the checksum packets</param>
        private static void EncodeRSData(BufferChunk[] data, BufferChunk[] checksum, UInt16[,] encode, int maxDataLength)
        {
            // Note: The size of the Vandermonde matrix used to encode the length is
            // Vandermonde #column: checksumLength, Vandermonde #row: dataPackets

            // TODO: Validate if Vandermonde # max column >= checksum and
            // Vandermonde #row >= dataPackets

            // TODO: Validate that there are no null entries in data and checksum

            // Get the length once to avoid having the overhead of getting it again inside a inner (performance)
            int dataPackets    = data.Length;
            int checksumLength = checksum.Length;

            // Note that we scan column after column, so we generate the checksum packets
            // one after the other
            for (int checksumColumn = 0; checksumColumn < checksumLength; checksumColumn++)
            {
                // For each column fill out the checksum mtx row after row
                for (int checksumRow = 0; checksumRow < maxDataLength; checksumRow += 2)
                {
                    for (int encodeRow = 0; encodeRow < dataPackets; encodeRow++)
                    {
                        // If we pass the size of the current data packet, so no operation are required
                        //
                        if ((checksumRow) < data[encodeRow].Length)
                        {
                            UInt16 currentValue = GF16.Multiply(data[encodeRow].GetUInt16(checksumRow),
                                                                encode[checksumColumn, encodeRow]);
                            currentValue = GF16.Add(checksum[checksumColumn].GetUInt16(checksumRow), currentValue);
                            checksum[checksumColumn].SetUInt16(checksumRow, currentValue);
                        }
                    }
                }
            }
        }
예제 #9
0
 /// <summary>
 /// Overload the - operator
 /// </summary>
 /// <param name="a">First operande</param>
 /// <param name="b">Second operande</param>
 /// <remarks>
 /// The subtraction is the same as the addition
 /// because with Galois fields, each number is its
 /// own negative. So there is no need to have a Sub
 /// method
 /// </remarks>
 /// <returns>Subrtraction of the 2 Galois Fields (XOR)</returns>
 public static GF16 operator -(GF16 a, GF16 b)
 {
     return(GF16.Add(a.Value, b.Value));
 }
        /// <summary>
        /// Decode to retrieve the length of the missing data packet(s) (null entries) in the
        /// data BufferChunk array and set the length of the packets in the recovery BufferChunk array.
        /// We have to do that because every data packet could have a different length.
        /// </summary>
        /// <param name="data">The data BufferChunk array</param>
        /// <param name="checksum">The number of checksum packet(s) that were used to encode</param>
        /// <param name="decode">The decoding GF16 matrix</param>
        /// <param name="recovery">The recovery BufferChunk array to set the length of the recovered packets</param>
        private static void DecodeRSLength(BufferChunk[] data, int checksum, GF16[,] decode, BufferChunk[] recovery)
        {
            // Important! For now I assume the following in the firstMatrix:
            // - The array is always of size data + checksum
            // - #checksum not null entries = #packet lost
            // - recovery.length is exactly the number of data packet lost
            // - checksum param is # checksum packets that were used to encode


            // Get the length once to avoid having the overhead of getting it again inside a inner (performance)
            // Note that this value is also used outside of this method, so we could have had a parameter too,
            // but I prefer to get it here to avoid inconsitencies
            int dataLength = data.Length;

            int recoveryColumn = 0;

            for (int dataColumn = 0; dataColumn < dataLength - checksum; dataColumn++)
            {
                // recover only the missing column
                if (data[dataColumn] == null)
                {
                    // Inverted matrix row index
                    int imRowIndex = 0;

                    // length of the recovered buffer
                    UInt16 currentLength = 0;

                    for (int dataIndex = 0; dataIndex < dataLength; dataIndex++)
                    {
                        // Perform the core operation mult with add in GF16
                        // TODO: We could crunch the column so we avoid this test
                        if (data[dataIndex] != null)
                        {
                            UInt16 length = 0;

                            if (dataIndex < dataLength - checksum) // For the data part, we get the length of the BufferChunk
                            {
                                length = (UInt16)data[dataIndex].Length;
                            }
                            else // For the checksum part, the encoded length is inside the first 2 bytes
                            {
                                length = data[dataIndex].GetUInt16(0);
                            }

                            UInt16 currentValue = GF16.Multiply(length, decode[dataColumn, imRowIndex].Value);
                            currentLength = GF16.Add(currentLength, currentValue);

                            imRowIndex++;
                        } // if
                    }     // for column (do elementary operations)

                    BufferChunk bc = recovery[recoveryColumn];
                    bc.Reset(bc.Index, currentLength);

                    // Reset all of the recovery packets so they have no data
                    // TODO - Temporary workaround for column based approach - JVE 7/6/2004
                    bc.Clear();

                    recoveryColumn++;
                } // if
            }     // for dataColumn
        }
예제 #11
0
        /// <summary>
        /// Add identity to the right side of a matrix
        /// </summary>
        /// <remarks>This is use when doing Jordan-Gauss elimination</remarks>
        /// <param name="matrixIn">The matrix</param>
        /// <returns>The new matrix with the identity</returns>
        private static GF16[,] AddIdentityRight (GF16[,] matrixIn)
        {
            Int32 rowLength = matrixIn.GetLength(1);
            Int32 columnLength = matrixIn.GetLength(0);

            GF16[,] matrixOut = new GF16[columnLength * 2, rowLength];

            // Add in identity portion to the top
            for (Int32 column = 0; column < columnLength; column++)
            {
                matrixOut[column + columnLength, column] = (UInt16)1;
            }

            // Copy the previous matrix to the bottom
            MatrixCopyTo(matrixIn, matrixOut);

            return matrixOut;
        }
        /// <summary>
        /// Create the decoding matrix
        /// </summary>
        /// <param name="nbDataPackets">Number of data packet</param>
        /// <param name="nbChecksumPackets">Number of checksum packet</param>
        /// <param name="rtpPackets">Array of rtpPackets</param>
        /// <returns>The decoding matrix</returns>
        /// <example>
        /// For example, if you have 3 data packets and 2 checksum
        /// packets and get the last data packet, you will get
        /// the following matrix
        /// 0   1   1
        /// 0   1   2
        /// 1   1   4
        /// </example>
        // TODO: To be more generic, we should use System.Array instead of array of BufferChunks
        public static GF16[,] DecodingMatrixGeneration(BufferChunk[] dataReceived, int nbChecksumPackets)
        {
            // nbTotalPackets is the total # rtpPackets to create (data packets and checksum packets)
            int nbTotalPackets = dataReceived.Length;
            int nbDataPackets  = nbTotalPackets - nbChecksumPackets;

            // Let's suppose that we lost the 2 first packets
            // So [E'] that should have been
            //  1   0   0   1   1
            //  0   1   0   1   2
            //  0   0   1   1   4
            // is actually
            //  0   1   1
            //  0   1   2
            //  1   1   4

            // The reverted decoding matrix will be multiplied by data received to get all the datas
            // for instance [D0..D2] = [D2 C0 C1] x 1/[E']
            // => So the number of columns should correspond to the number of data recieved + checksum received
            // and the number of rows should correspond of the number of data packet K

            // count the number of packets received
            int nbPacketsReceived = ReceivedPacketsCount(nbTotalPackets, dataReceived);

            // nbPacketsReceived columns, K rows
            // Note that the # columns is smaller than the number of packet received
            // in case the number of packet lost was smaller than the number than
            // the number of checksum packets (we short the matrix: It's like if we lost the last
            // checksum(s) packet(s)
            GF16[,] mtxEprime_GF16 = new GF16[nbPacketsReceived, nbDataPackets];

            // Construct the matrix mtxEprime_GF16 from the rtpPackets received
            // Note see also Jay's Decode method

            // Loop trough the data rtpPackets and put a 1 on the row of the packet
            // received

            int foundIndex = 0;

            for (int index = 0; index < nbDataPackets; index++)
            {
                if (dataReceived[index] != null) // not a packet lost
                {
                    // Generate a row in the mtxEprime_GF16 (decode matrix)
                    if (index < nbDataPackets)
                    {
                        // TODO: To be clean, we should have initialized all the other items
                        // on the row explicitely to zero or make sure their are set them to zero
                        // when creating the new matrix
                        mtxEprime_GF16[foundIndex, index] = (UInt16)1;
                    }
                    foundIndex++;
                    //                  if (foundIndex >= nbPacketsReceived-nbAdditionalPacketsReceived)
                    //                      return mtxEprime_GF16;
                }
            }

            // Generate a Vandermonde Mtx for the other items
            // TODO: Change that to use the Vandermonde static mtx
            for (UInt32 m = 0; m < nbChecksumPackets; m++)
            {
                // Note: If a checksum packet has been lost, the column is skiped
                if (dataReceived[m + nbDataPackets] != null) // not a packet lost
                {
                    for (UInt32 k = 0; k < nbDataPackets; k++)
                    {
                        // Each element of the Vandermonde matrix is calculated as (m+1)^k over GF16
                        // mtx[column, row] = (column+1)^row

                        // With Vandermonde: 1^x, 2^x, 3^x, ... the code was:
                        // mtxEprime_GF16[foundIndex, k] = GF16.Power((UInt16)(m+1), (UInt32)k);
                        // I experimented problems (no pivot found during invertion of Eprime)
                        // when losing 3 packets (worked fine when losing 2 packets)

                        // So I changed, to use Vandermonde matrix were mtx[column, row] = (2^column)^row
                        mtxEprime_GF16[foundIndex, k] = GF16.Power((UInt16)GF16.Power(2, (m)), (UInt32)k);

                        // TODO: Check if Jay's approach is more efficient: mtxE_GF16[m, k] = GF16.gf_exp[GF16.Modnn(k*m)];
                    }
                    foundIndex++;
                    //                  if (foundIndex >= nbPacketsReceived-nbAdditionalPacketsReceived)
                    //                      return mtxEprime_GF16;
                }
            } // for
            return(mtxEprime_GF16);
        }
예제 #13
0
 static GF16()
 {
     GF16.fillLogTables();
 }
예제 #14
0
        /// <summary>
        /// Gauss - Jordan elimination.
        /// This algorithm takes a matrix and use elementary
        /// row operations to put the matrix in a row echelon form. This
        /// means that the result matrix will have the identity matrix at the
        /// begining.
        /// This method can be used in 2 specific scenarios:
        /// - To solve a linear system numerically
        /// - To invert a matrix
        /// </summary>
        /// <example>
        /// For instance the matrix:
        /// 0   1   1   1   0   0
        /// 0   1   2   0   1   0
        /// 1   1   4   0   0   1
        ///
        /// Will become (over int):
        /// [   ID  ]  [  rest  ]
        /// 1   0   0   2  -3   1
        /// 0   1   0   2  -1   0
        /// 0   0   1  -1   1   0
        /// </example>
        /// <remarks>
        /// For the Reed Solomon encoding, Gauss - Jordan elimination
        /// is used to invert the decoding matrix. This is needed to
        /// recover from packet loss.
        /// Important note: The input is modified
        /// </remarks>
        /// <param name="matrixIn">The matrix to transform in a row echelon form</param>
        /// <returns>The matrix in a row echelon from</returns>
        private static GF16[,] GaussJordan(GF16[,] matrixIn)
        {
            // Matrix size variables
            int rowLength    = matrixIn.GetLength(1);
            int columnLength = matrixIn.GetLength(0);

            // The number of pivots needed is the min between row/column
            // For instance the matrix:
            // 0   1   1   1   0   0
            // 0   1   2   0   1   0
            // 1   1   4   0   0   1
            // has 3 pivots
            int pivots = Math.Min(rowLength, columnLength);

            // Loop through all the pivot columns
            for (int pivotColumn = 0; pivotColumn < pivots; pivotColumn++)
            {
                // TODO: Place Step 1 in a separate method

                // Step 1: Find the Pivot Row and reduce the pivot to have a factor of 1

                int pivotRow = -1;
                // Loop through all the rows to find a pivot for the pivotColumn
                for (int row = 0; row < rowLength; row++)
                {
                    // A pivot must be non-zero...
                    if (matrixIn[pivotColumn, row].Value != 0)
                    {
                        // ...and don't pivot on a row that has previous non-zero column entries

                        // Loop through columns of the row Check is the row found respect the non-zero column rule
                        bool isRowWithPreviousZero = true;
                        for (int column = 0; column < pivotColumn; column++)
                        {
                            // Check for non-zero
                            if (matrixIn[column, row].Value != 0)
                            {
                                // The row is not a pivot because it has previous non-zero entries
                                isRowWithPreviousZero = false;
                                break;
                            }
                        }

                        // If the row found has previous zero, it is a pivot row
                        if (isRowWithPreviousZero)
                        {
                            // The current row is the pivot
                            pivotRow = row;

                            GF16 factorPivot = matrixIn[pivotColumn, row];
                            // Reduce the pivot row to have a factor of 1
                            for (int column = pivotColumn; column < columnLength; column++)
                            {
                                matrixIn[column, row] /= factorPivot;
                            }
                            // We can exit the loop through rows in Step 1
                            break;
                        }
                    }
                }

                // Ensure that a pivot has been found
                if (pivotRow == -1)
                {
                    throw new ApplicationException(Strings.NoPivotRowFound);
                }

                // TODO: Place Step 2 in a separate method

                // Step 2: Do elementary operation to all rows (except the pivot row and
                // the rows that have zero on the pivot column)
                // The final goal is to have a zero for all rows on the column corresponding to the
                // pivot column

                // Loop trough the rows to do elementary operation (subtraction)
                for (int row = 0; row < rowLength; row++)
                {
                    // Note: We skip the pivot row and the rows that has zero on the pivot
                    // column (no operation requiered)
                    if ((row != pivotRow) && (matrixIn[pivotColumn, row].Value != 0))
                    {
                        // Now, determine the factor by which the pivot row must be added to the target
                        // row to cancel out the pivot value
                        GF16 factorOperation = matrixIn[pivotColumn, row];

                        // Perform the operation for each column of the current row
                        for (int column = 0; column < columnLength; column++)
                        {
                            // Perform a subtraction. The final goal is to have a zero for all rows
                            // on the column corresponding to the pivot column
                            matrixIn[column, row] -= factorOperation * matrixIn[column, pivotRow];
                        }
                    }
                }
            }

            // At this point the work manipulation is done but we might get the rows
            // out of order.
            // For instance, the input matrix:
            // 0   1   1   1   0   0
            // 0   1   2   0   1   0
            // 1   1   4   0   0   1
            //
            // will become the following (over int operation):
            // 0   1   0   2  -1   0
            // 0   0   1  -1   1   0
            // 1   0   0   2  -3   1
            //
            // so now, we need to swap the row to place them in order to obtain the following:
            // [   ID  ]  [  rest  ]
            // 1   0   0   2  -3   1
            // 0   1   0   2  -1   0
            // 0   0   1  -1   1   0

            // TODO: Place Step 3 in a separate method

            // Step 3: Swap raw into order to have an identity matrix at the front

            for (int row = 0; row < rowLength; row++) // can use row < rowLength -1, because last row should always be in order?
            {
                // Skip rows that are in proper order
                if (matrixIn[row, row].Value != 1)
                {
                    // Not in proper order, search down through the matrix
                    bool found = false;
                    for (int searchRow = 0; searchRow < rowLength; searchRow++)
                    {
                        if (matrixIn[row, searchRow].Value == 1) // found the row we're looking for
                        {
                            found = true;
                            // Do the swap operation
                            // Save the target row to temporary swap space first
                            GF16[] swapRowTemp = new GF16[columnLength];
                            for (int swapRowColumn = 0; swapRowColumn < columnLength; swapRowColumn++)
                            {
                                swapRowTemp[swapRowColumn] = matrixIn[swapRowColumn, row];
                            }
                            // Replace the target row contents with the found row contents
                            for (int swapRowColumn = 0; swapRowColumn < columnLength; swapRowColumn++)
                            {
                                matrixIn[swapRowColumn, row] = matrixIn[swapRowColumn, searchRow];
                            }
                            // Replace the found row contents with the temporary swap space (previous target row contents)
                            for (int swapRowColumn = 0; swapRowColumn < columnLength; swapRowColumn++)
                            {
                                matrixIn[swapRowColumn, searchRow] = swapRowTemp[swapRowColumn];
                            }
                        }
                    }
                    if (!found)
                    {
                        throw new ApplicationException(Strings.SwapRowNotFound);
                    }
                }
            }

            return(matrixIn);
        }
예제 #15
0
        /// <summary>
        /// Decode to retrieve the length of the missing data packet(s) (null entries) in the 
        /// data BufferChunk array and set the length of the packets in the recovery BufferChunk array.
        /// We have to do that because every data packet could have a different length.
        /// </summary>
        /// <param name="data">The data BufferChunk array</param>
        /// <param name="checksum">The number of checksum packet(s) that were used to encode</param>
        /// <param name="decode">The decoding GF16 matrix</param>
        /// <param name="recovery">The recovery BufferChunk array to set the length of the recovered packets</param>
        private static void DecodeRSLength(BufferChunk[] data, int checksum, GF16[,] decode, BufferChunk[] recovery)
        {
            // Important! For now I assume the following in the firstMatrix:
            // - The array is always of size data + checksum
            // - #checksum not null entries = #packet lost
            // - recovery.length is exactly the number of data packet lost
            // - checksum param is # checksum packets that were used to encode


            // Get the length once to avoid having the overhead of getting it again inside a inner (performance)
            // Note that this value is also used outside of this method, so we could have had a parameter too,
            // but I prefer to get it here to avoid inconsitencies
            int dataLength = data.Length;

            int recoveryColumn = 0;

            for (int dataColumn = 0; dataColumn < dataLength - checksum; dataColumn++)
            {
                // recover only the missing column
                if (data[dataColumn] == null)
                {
                    // Inverted matrix row index
                    int imRowIndex = 0;

                    // length of the recovered buffer 
                    UInt16 currentLength = 0;

                    for (int dataIndex = 0; dataIndex < dataLength; dataIndex++)
                    {
                        // Perform the core operation mult with add in GF16
                        // TODO: We could crunch the column so we avoid this test
                        if (data[dataIndex] != null)
                        {
                            UInt16 length = 0;

                            if (dataIndex < dataLength - checksum) // For the data part, we get the length of the BufferChunk
                            {
                                length = (UInt16) data[dataIndex].Length;
                            } 
                            else // For the checksum part, the encoded length is inside the first 2 bytes
                            {
                                length = data[dataIndex].GetUInt16(0);
                            }

                            UInt16 currentValue = GF16.Multiply(length, decode[dataColumn, imRowIndex].Value);
                            currentLength = GF16.Add(currentLength, currentValue); 

                            imRowIndex++;
                        } // if
                    } // for column (do elementary operations)

                    BufferChunk bc = recovery[recoveryColumn];
                    bc.Reset(bc.Index, currentLength);
                    
                    // Reset all of the recovery packets so they have no data
                    // TODO - Temporary workaround for column based approach - JVE 7/6/2004
                    bc.Clear();

                    recoveryColumn++;
                } // if
            } // for dataColumn
        }
예제 #16
0
        /// <summary>
        /// Decode to retrieve the missing data packet(s) (null entries) in the data BufferChunk array and place the 
        /// decoded result over GF16 in order in the recovery BufferChunk array with the right length (every
        /// data packet can have a different length)
        /// </summary>
        /// <param name="checksum">The number of checksum packet(s) that were used to encode</param>
        /// <param name="data">The data BufferChunk array</param>
        /// <param name="decode">The decoding GF16 matrix</param>
        /// <param name="recovery">The recovery BufferChunk array to place the recovered result</param>
        public static void DecodeRS(int checksum, BufferChunk[] data, GF16[,] decode, BufferChunk[] recovery)
        {

            // TODO: Validation discussed with Jason:
            // Check in data array if: the #data lost = # checksum and if this number = recovery.length 

            // Decode the length and set it to the recovery packets
            DecodeRSLength(data, checksum, decode, recovery);

            // ... and don't forget to adjust the index of the checksum BufferChunk because 
            // we don't want to take in account the length anymore
            int dataLength = data.Length;
            for (int i = dataLength - checksum; i < dataLength; i++)
            {
                BufferChunk bc = data[i];

                if(bc != null)
                {
                    bc.Reset(bc.Index + CFec.SIZE_OVERHEAD, bc.Length - CFec.SIZE_OVERHEAD);
                }
            }

            // Adjust all of the data (and recovery) to a 16bit boundary to avoid byte operations
            // Important: This assumes the provided BufferChunks have room to grow to 16bit boundary
            Queue paddedBufferChunks = new Queue();
            AddPadding(data, checksum, paddedBufferChunks);
            AddPadding(recovery, 0, paddedBufferChunks);

            // Decode the data and set the result into the recovery packets
            DecodeRSData(data, checksum, decode, recovery);

            // Reset all the packets back to their initial length
            RemovePadding(paddedBufferChunks);

            // The caller is responsible for resetting all BufferChunks so there is no need
            // to reset the checksum packets
        }
예제 #17
0
        /// <summary>
        /// Remove the identity at the front of the matrix
        /// </summary>
        /// <param name="matrixIn">The input matrix</param>
        /// <returns>Stripped Matrix without the identity at the front</returns>
        private static GF16[,] StripIdentity(GF16[,] matrixIn)
        {
            int rowLength = matrixIn.GetLength(1);
            int columnLength = matrixIn.GetLength(0);
            GF16[,] matrixOut = new GF16[columnLength - rowLength, rowLength];

            for (int row = 0; row < rowLength; row++)
                for (int column = rowLength; column < columnLength; column++)
                    matrixOut[column - rowLength, row] = matrixIn[column, row];

            return matrixOut;
        }
예제 #18
0
        /// <summary>
        /// Copy a matrix to a destination matrix
        /// </summary>
        /// <param name="matrixIn">The matrix to copy</param>
        /// <param name="matrixDestination">The destination matrix</param>
        private static void MatrixCopyTo(GF16[,] matrixIn, GF16[,] matrixDestination)
        {
            int rowLength = matrixIn.GetLength(1);
            int columnLength = matrixIn.GetLength(0);

            for (int row = 0; row < rowLength; row++)
                for (int column = 0; column < columnLength; column++)
                    matrixDestination[column, row] = matrixIn[column, row];
        }
예제 #19
0
        /// <summary>
        /// Gauss - Jordan elimination.
        /// This algorithm takes a matrix and use elementary
        /// row operations to put the matrix in a row echelon form. This
        /// means that the result matrix will have the identity matrix at the
        /// begining.  
        /// This method can be used in 2 specific scenarios:
        /// - To solve a linear system numerically
        /// - To invert a matrix
        /// </summary>
        /// <example>
        /// For instance the matrix:
        /// 0   1   1   1   0   0
        /// 0   1   2   0   1   0
        /// 1   1   4   0   0   1
        /// 
        /// Will become (over int):
        /// [   ID  ]  [  rest  ]
        /// 1   0   0   2  -3   1
        /// 0   1   0   2  -1   0
        /// 0   0   1  -1   1   0
        /// </example>
        /// <remarks>
        /// For the Reed Solomon encoding, Gauss - Jordan elimination
        /// is used to invert the decoding matrix. This is needed to
        /// recover from packet loss.
        /// Important note: The input is modified
        /// </remarks>
        /// <param name="matrixIn">The matrix to transform in a row echelon form</param>
        /// <returns>The matrix in a row echelon from</returns>
        private static GF16[,] GaussJordan(GF16[,] matrixIn)
        {
            // Matrix size variables
            int rowLength = matrixIn.GetLength(1);
            int columnLength = matrixIn.GetLength(0);

            // The number of pivots needed is the min between row/column
            // For instance the matrix:
            // 0   1   1   1   0   0
            // 0   1   2   0   1   0
            // 1   1   4   0   0   1
            // has 3 pivots
            int pivots = Math.Min(rowLength, columnLength);

            // Loop through all the pivot columns
            for (int pivotColumn = 0; pivotColumn < pivots; pivotColumn++)
            {
                // TODO: Place Step 1 in a separate method

                // Step 1: Find the Pivot Row and reduce the pivot to have a factor of 1

                int pivotRow = -1;
                // Loop through all the rows to find a pivot for the pivotColumn
                for (int row = 0; row < rowLength; row++)
                {
                    // A pivot must be non-zero...
                    if (matrixIn[pivotColumn, row].Value != 0)
                    {

                        // ...and don't pivot on a row that has previous non-zero column entries

                        // Loop through columns of the row Check is the row found respect the non-zero column rule
                        bool isRowWithPreviousZero = true;
                        for (int column = 0; column < pivotColumn; column++)
                        {
                            // Check for non-zero
                            if (matrixIn[column, row].Value != 0)
                            {
                                // The row is not a pivot because it has previous non-zero entries
                                isRowWithPreviousZero = false;
                                break;
                            }
                        }

                        // If the row found has previous zero, it is a pivot row
                        if (isRowWithPreviousZero)
                        {
                            // The current row is the pivot
                            pivotRow = row;

                            GF16 factorPivot = matrixIn[pivotColumn, row];
                            // Reduce the pivot row to have a factor of 1
                            for(int column = pivotColumn; column < columnLength; column++)
                            {
                                matrixIn[column, row] /= factorPivot;
                            }
                            // We can exit the loop through rows in Step 1
                            break;
                        }
                    }
                }

                // Ensure that a pivot has been found
                if (pivotRow == -1)
                {
                    throw new ApplicationException("No pivot row found!");
                }

                // TODO: Place Step 2 in a separate method

                // Step 2: Do elementary operation to all rows (except the pivot row and 
                // the rows that have zero on the pivot column)
                // The final goal is to have a zero for all rows on the column corresponding to the 
                // pivot column
                
                // Loop trough the rows to do elementary operation (subtraction) 
                for (int row = 0; row < rowLength; row++)
                {
                    // Note: We skip the pivot row and the rows that has zero on the pivot
                    // column (no operation requiered)
                    if ((row != pivotRow) && (matrixIn[ pivotColumn, row].Value != 0))
                    {                     
                        // Now, determine the factor by which the pivot row must be added to the target 
                        // row to cancel out the pivot value
                        GF16 factorOperation = matrixIn[ pivotColumn, row ];

                        // Perform the operation for each column of the current row
                        for(int column = 0; column < columnLength; column++)
                        {
                            // Perform a subtraction. The final goal is to have a zero for all rows 
                            // on the column corresponding to the pivot column
                            matrixIn[column, row] -= factorOperation * matrixIn[column, pivotRow];
                        }
                    }
                }

            }

            // At this point the work manipulation is done but we might get the rows
            // out of order. 
            // For instance, the input matrix:
            // 0   1   1   1   0   0
            // 0   1   2   0   1   0
            // 1   1   4   0   0   1
            //
            // will become the following (over int operation):
            // 0   1   0   2  -1   0
            // 0   0   1  -1   1   0
            // 1   0   0   2  -3   1
            //
            // so now, we need to swap the row to place them in order to obtain the following:
            // [   ID  ]  [  rest  ]
            // 1   0   0   2  -3   1
            // 0   1   0   2  -1   0
            // 0   0   1  -1   1   0

            // TODO: Place Step 3 in a separate method

            // Step 3: Swap raw into order to have an identity matrix at the front

            for(int row = 0; row < rowLength; row++) // can use row < rowLength -1, because last row should always be in order?
            {
                // Skip rows that are in proper order
                if (matrixIn[row,row].Value != 1)
                {
                    // Not in proper order, search down through the matrix
                    bool found = false;
                    for (int searchRow = 0; searchRow < rowLength; searchRow++)
                    {
                        if (matrixIn[row, searchRow].Value == 1) // found the row we're looking for
                        {
                            found = true;
                            // Do the swap operation
                            // Save the target row to temporary swap space first
                            GF16[] swapRowTemp = new GF16[columnLength];
                            for(int swapRowColumn = 0; swapRowColumn < columnLength; swapRowColumn++)
                            {
                                swapRowTemp[swapRowColumn] = matrixIn[swapRowColumn, row];
                            }
                            // Replace the target row contents with the found row contents
                            for(int swapRowColumn = 0; swapRowColumn < columnLength; swapRowColumn++)
                            {
                                matrixIn[swapRowColumn, row] = matrixIn[swapRowColumn,searchRow];
                            }
                            // Replace the found row contents with the temporary swap space (previous target row contents)
                            for(int swapRowColumn = 0; swapRowColumn < columnLength; swapRowColumn++)
                            {
                                matrixIn[swapRowColumn,searchRow] = swapRowTemp[swapRowColumn];
                            }
                        }
                    }
                    if(!found)
                        throw new ApplicationException("Swap row not found!");
                }
            }

            return matrixIn;
        }   
예제 #20
0
        /// <summary>
        /// Create the decoding matrix
        /// </summary>
        /// <param name="nbDataPackets">Number of data packet</param>
        /// <param name="nbChecksumPackets">Number of checksum packet</param>
        /// <param name="rtpPackets">Array of rtpPackets</param>
        /// <returns>The decoding matrix</returns>
        /// <example>
        /// For example, if you have 3 data packets and 2 checksum
        /// packets and get the last data packet, you will get
        /// the following matrix  
        /// 0   1   1
        /// 0   1   2
        /// 1   1   4
        /// </example>
        // TODO: To be more generic, we should use System.Array instead of array of BufferChunks
        public static GF16[,] DecodingMatrixGeneration(BufferChunk[] dataReceived, int nbChecksumPackets)
        {
            // nbTotalPackets is the total # rtpPackets to create (data packets and checksum packets)
            int nbTotalPackets = dataReceived.Length;
            int nbDataPackets = nbTotalPackets - nbChecksumPackets;

            // Let's suppose that we lost the 2 first packets
            // So [E'] that should have been
            //  1   0   0   1   1
            //  0   1   0   1   2
            //  0   0   1   1   4
            // is actually
            //  0   1   1
            //  0   1   2
            //  1   1   4

            // The reverted decoding matrix will be multiplied by data received to get all the datas
            // for instance [D0..D2] = [D2 C0 C1] x 1/[E']
            // => So the number of columns should correspond to the number of data recieved + checksum received
            // and the number of rows should correspond of the number of data packet K

            // count the number of packets received
            int nbPacketsReceived = ReceivedPacketsCount(nbTotalPackets, dataReceived);

            // nbPacketsReceived columns, K rows
            // Note that the # columns is smaller than the number of packet received
            // in case the number of packet lost was smaller than the number than
            // the number of checksum packets (we short the matrix: It's like if we lost the last
            // checksum(s) packet(s)
            GF16[,] mtxEprime_GF16 = new GF16[nbPacketsReceived, nbDataPackets];
                    
            // Construct the matrix mtxEprime_GF16 from the rtpPackets received
            // Note see also Jay's Decode method

            // Loop trough the data rtpPackets and put a 1 on the row of the packet
            // received

            int foundIndex = 0;
            for (int index=0; index < nbDataPackets; index++)
            {
                if (dataReceived[index] != null) // not a packet lost
                {
                    // Generate a row in the mtxEprime_GF16 (decode matrix)
                    if (index < nbDataPackets)
                    {
                        // TODO: To be clean, we should have initialized all the other items
                        // on the row explicitely to zero or make sure their are set them to zero 
                        // when creating the new matrix
                        mtxEprime_GF16[foundIndex, index] = (UInt16)1;
                    }
                    foundIndex++;
                    //                  if (foundIndex >= nbPacketsReceived-nbAdditionalPacketsReceived)
                    //                      return mtxEprime_GF16;
                }
            }

            // Generate a Vandermonde Mtx for the other items
            // TODO: Change that to use the Vandermonde static mtx
            for (UInt32 m = 0; m < nbChecksumPackets; m++)
            {
                // Note: If a checksum packet has been lost, the column is skiped
                if (dataReceived[m+nbDataPackets] != null) // not a packet lost
                {
                    for (UInt32 k = 0; k < nbDataPackets; k++)
                    {
                        // Each element of the Vandermonde matrix is calculated as (m+1)^k over GF16
                        // mtx[column, row] = (column+1)^row 

                        // With Vandermonde: 1^x, 2^x, 3^x, ... the code was:
                        // mtxEprime_GF16[foundIndex, k] = GF16.Power((UInt16)(m+1), (UInt32)k);
                        // I experimented problems (no pivot found during invertion of Eprime)
                        // when losing 3 packets (worked fine when losing 2 packets)

                        // So I changed, to use Vandermonde matrix were mtx[column, row] = (2^column)^row
                        mtxEprime_GF16[foundIndex, k] = GF16.Power((UInt16)GF16.Power(2, (m)), (UInt32)k);

                        // TODO: Check if Jay's approach is more efficient: mtxE_GF16[m, k] = GF16.gf_exp[GF16.Modnn(k*m)];
                    }
                    foundIndex++;
                    //                  if (foundIndex >= nbPacketsReceived-nbAdditionalPacketsReceived)
                    //                      return mtxEprime_GF16;
                }
            } // for
            return mtxEprime_GF16;
        }
예제 #21
0
 /// <summary>
 /// Overload the * operator
 /// </summary>
 /// <param name="a">First operande</param>
 /// <param name="b">Second operande</param>
 /// <returns>Multiplication of the 2 Galois Fields</returns>
 public static GF16 operator *(GF16 a, GF16 b)
 {
     return(GF16.Multiply(a.Value, b.Value));
 }
예제 #22
0
        /// <summary>
        /// Decode to retrieve the missing data packet(s) (null entries) in the data BufferChunk array and place the 
        /// decoded result over GF16 in order in the recovery BufferChunk array
        /// </summary>
        /// <param name="data">The data BufferChunk array</param>
        /// <param name="checksum">The number of checksum packet(s) that were used to encode</param>
        /// <param name="decode">The decoding GF16 matrix</param>
        /// <param name="recovery">The recovery BufferChunk array to place the recovered result</param>
        private static void DecodeRSData(BufferChunk[] data, int checksum, GF16[,] decode, BufferChunk[] recovery)
        {
            // Important! For now I assume the following in the firstMatrix:
            // - The array is always of size data + checksum
            // - #checksum not null entries = #packet lost
            // - recovery.length is exactly the number of data packet lost
            // - checksum param is # checksum packets that were used to encode

            // Reconstruct data

            // Get the length once to avoid having the overhead of getting it again inside a inner (performance)
            // Note that this value is also used outside of this method, so we could have had a parameter too,
            // but I prefer to get it here to avoid inconsitencies
            int dataLength = data.Length;

            // Scan column after column
            int recoveryColumn = 0;
            for (int dataColumn = 0; dataColumn < dataLength - checksum; dataColumn++)
            {
                // recover only the missing column
                if (data[dataColumn] == null)
                {
                    int recoveryRowLength = recovery[recoveryColumn].Length;

                    // For each column fill out the output mtx row after row
                    for (int recoveryRow = 0; recoveryRow < recoveryRowLength; recoveryRow += 2)
                    {
                        int imRowIndex = 0;

                        // nnDataColumn - not null data index
                        for (int nnDataColumn = 0; nnDataColumn < dataLength; nnDataColumn++)
                        {
                            // Perform the core operation mult with add in GF16
                            // TODO: We could crunch the column so we avoid this test
                            if (data[nnDataColumn] != null)
                            {
                                if(recoveryRow < data[nnDataColumn].Length)
                                {
                                    UInt16 currentValue = GF16.Multiply(data[nnDataColumn].GetUInt16(recoveryRow), 
                                        decode[dataColumn, imRowIndex].Value);
                                    currentValue = GF16.Add(recovery[recoveryColumn].GetUInt16(recoveryRow), currentValue); 
                                    recovery[recoveryColumn].SetUInt16(recoveryRow, currentValue);
                                }
                                imRowIndex++;
                            }
                        }
                    }

                    recoveryColumn++;
                }
            }
        }
예제 #23
0
 /// <summary>
 /// Overload the / operator
 /// </summary>
 /// <param name="a">First operande</param>
 /// <param name="b">Second operande</param>
 /// <returns>a div b in Galois Fields</returns>
 public static GF16 operator /(GF16 a, GF16 b)
 {
     return(GF16.Divide(a.Value, b.Value));
 }