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