function addErrorCorrection() { // Now take the constructed codewords so far and add the // requried number of error correcting words at the end. // Depending on the symbol version and error corrction level // we normally have break up the whole data in blocks and add // error correction to each block. The required block structure // is specified in the standard. For small versions the whole data is // considered to be one block which makes it easy to start with. // Get block structur specification // (Total nbr blocks, (total words in block, data words in block, error words in block)) $blks = $this->iQRCapacity->getBlockStructure($this->iVersion, $this->iErrLevel); // We can have all same block structure or we can have two different // block structures so we need to find ut which $ntype = count($blks); $this->iInfo = array(); $blocks = array(); $dataIdx = 0; $availableCodewords = count($this->iCodeWords); $this->iErrDebugInfostr = ''; $blockoffset = 0; $blockinfo = array(); $totalwords = 0; if ($ntype == 1 || $ntype == 2) { for ($k = 0; $k < $ntype; ++$k) { // ------------------------------------------------------------------------------- // Handle each block structure // ------------------------------------------------------------------------------- // 1. Fird find out how many block we need to use $nbrBlocks = $blks[$k][0]; // 2. Find out total number of words (data+error correction) is in each block $nbrWordsInBlock = $blks[$k][1][0]; // 3. Find out data words in each block $nbrDataInBlock = $blks[$k][1][1]; // 4. Find out error correcting words in each block $nbrErrInBlock = $blks[$k][1][2]; // Prepare RS coder $rs = new ReedSolomon($nbrErrInBlock, QREncoder::RSPrimitivePol); // Now split the data words in block and add the error correcting words to each block for ($i = 0; $i < $nbrBlocks; ++$i) { // Extract the needed number of codewords for this block for ($j = 0; $j < $nbrDataInBlock; ++$j) { if ($dataIdx >= $availableCodewords) { //throw new QRException('Trying to read past the last available codeword in block split', -1); throw new QRExceptionL(1410); } $blocks[$i + $blockoffset][$j] = $this->iCodeWords[$dataIdx++]; } // For easier split we save the structure of each block so we can easily find // out info on each block we we loop throgh each block when we constructre the // final codeword sequence $blockinfo[$i + $blockoffset] = array($nbrWordsInBlock, $nbrDataInBlock, $nbrErrInBlock); // Now append the requested number of error correcting codewords to the block $rs->append($blocks[$i + $blockoffset]); // Keep a sanity count on the total number of words (data+error correction) $totalwords += count($blocks[$i + $blockoffset]); $this->iErrDebugInfostr .= "\n=============\nSymbol block: Type={$k}, number=" . ($i + 1) . "/{$nbrBlocks} (Tot={$nbrWordsInBlock}, Data={$nbrDataInBlock}, Err={$nbrErrInBlock})\n=============\n"; $err = 0; for ($j = 0; $j < $nbrWordsInBlock; ++$j) { $fmt = '%08s (%02x)'; $this->iErrDebugInfostr .= sprintf($fmt, decbin($blocks[$i + $blockoffset][$j]), $blocks[$i + $blockoffset][$j]); if ($j < $nbrWordsInBlock - 1) { $this->iErrDebugInfostr .= ', '; } if ($j == $nbrDataInBlock - 1) { $this->iErrDebugInfostr .= "\nError correction word in block:"; } if ($j >= $nbrDataInBlock) { ++$err; if ($err % 4 == 0) { $this->iErrDebugInfostr .= "\n"; } } elseif (($j + 1) % 4 == 0 || $j == $nbrDataInBlock - 1) { $this->iErrDebugInfostr .= "\n"; } } $this->iErrDebugInfostr .= "\n"; } $blockoffset += $nbrBlocks; } } else { //throw new QRException('Internal error: Expected 1 or 2 as the number of block structures.'); throw new QRExceptionL(1411); } // Now when we have all block we need to create the final message codeword sequence which we do by // taking data and error correction codewords from each block in turn: data block // 1, codeword 1; data block 2, codeword 1; data block 3, codeword 1; and similarly to data block n - 1, final // codeword; data block n, final codeword; then error correction block 1, codeword 1, error correction block 2, // codeword 1, ... and similarly to error correction block n - 1, final codeword; error correction block n, final codeword. // QR Code symbols contain data and error correction blocks which always exactly fill the symbol codeword capacity. // In certain versions, however, there may be a need for 3, 4 or 7 Remainder Bits to be appended to the final // message bit stream in order exactly to fill the number of modules in the encoding region. $totalNbrOfBlocks = $blockoffset; // Collect all data words $this->iFinalCodewordSequence = array(); $idx = 0; $cwidx = 0; while ($idx < $availableCodewords) { for ($i = 0; $i < $totalNbrOfBlocks; ++$i) { if ($cwidx < $blockinfo[$i][1]) { $this->iFinalCodewordSequence[$idx++] = $blocks[$i][$cwidx]; } } ++$cwidx; } // Collect all error correction words $erridx = 0; while ($erridx < $nbrErrInBlock) { for ($i = 0; $i < $totalNbrOfBlocks; ++$i) { $this->iFinalCodewordSequence[$idx++] = $blocks[$i][$blockinfo[$i][1] + $erridx]; } ++$erridx; } $n = count($this->iFinalCodewordSequence); // Do a sanity check that we have the correct total number of data+error codewords if ($totalwords != $n) { throw new QRExeption('Internal error: Number of total codewords does not match after split!!'); } // Create the final bitsequence to be put in the symbol matrix $this->iFinalBitArray = array(); Utils::ByteArray2Bits($this->iFinalCodewordSequence, $this->iFinalBitArray); $this->iErrDebugInfostr .= "\n=========== INTERLEAVED CODE SEQUENCE (Total={$n} =============\n"; for ($i = 0; $i < $n; ++$i) { // $fmt = '%08s (%02x)'; $v = $this->iFinalCodewordSequence[$i]; $this->iErrDebugInfostr .= sprintf($fmt, decbin($v), $v); if ($i < $n - 1) { $this->iErrDebugInfostr .= ', '; } if (($i + 1) % 4 == 0) { $this->iErrDebugInfostr .= "\n"; } } $this->iErrDebugInfostr .= "\n"; }
function rs(&$binary, $bytes, $datablock, $rsblock) { $blocks = floor(($bytes + 2) / $datablock); $rs = new ReedSolomon($rsblock); for ($b = 0; $b < $blocks; $b++) { $buf = array(); $p = 0; for ($n = $b; $n < $bytes; $n += $blocks) { array_push($buf, $binary[$n]); } $enc = $rs->encodeArray($buf); // comes back reversed $p = $rsblock - 1; for ($n = $b; $n < $rsblock * $blocks; $n += $blocks) { $binary[$bytes + $n] = $enc[$p--]; } } }