fixes to npc (NormPrecoder) and updated its documentation. Include 'auto' block sizing' feature
parent
0c6e516c90
commit
7a6c09ae17
Binary file not shown.
|
|
@ -0,0 +1,420 @@
|
||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<!DOCTYPE article PUBLIC "-//OASIS//DTD DocBook XML V4.1.2//EN"
|
||||||
|
"http://www.oasis-open.org/docbook/xml/4.1.2/docbookx.dtd">
|
||||||
|
<article lang="">
|
||||||
|
<articleinfo>
|
||||||
|
<title><inlinemediaobject>
|
||||||
|
<imageobject>
|
||||||
|
<imagedata align="center" fileref="NormLogo.gif" scale="50"/>
|
||||||
|
</imageobject>
|
||||||
|
</inlinemediaobject> <command>npc</command> User Guide</title>
|
||||||
|
|
||||||
|
<subtitle>(NORM Precoder User Guide)</subtitle>
|
||||||
|
|
||||||
|
<titleabbrev><command>npc</command> User Guide</titleabbrev>
|
||||||
|
</articleinfo>
|
||||||
|
|
||||||
|
<sect1>
|
||||||
|
<title>Background</title>
|
||||||
|
|
||||||
|
<para>The NACK-Oriented Reliable Multicast (NORM) protocol is capable of
|
||||||
|
supporting robust transmission of content to "silent" receivers that are
|
||||||
|
required or only capable of operating in an emission-controlled (EMCON)
|
||||||
|
manner. This capability is enabled when the NORM sender is configured to
|
||||||
|
proactively transmit Forward Error Correction (FEC) erasure coding content
|
||||||
|
as part of its original data transmission. For NACK-based operation, the
|
||||||
|
FEC repair packets are usually sent only reactively, in response to repair
|
||||||
|
requests (NACKs) from the receiver group. However, hybrid operation with a
|
||||||
|
combination of proactive FEC content and additional reactive FEC repairs
|
||||||
|
as needed is also supported. Similarly, a mix of nacking and silent
|
||||||
|
receivers may be supported with silent receivers capitalizing on the FEC
|
||||||
|
repair information sent proactively and/or reactively. The purpose of the
|
||||||
|
NORM Pre-Coder (<command>npc</command>) software utility described here is
|
||||||
|
to support additional robustness for purely-proactive sessions, where the
|
||||||
|
receivers are unable to request repair or retransmission of
|
||||||
|
content.</para>
|
||||||
|
|
||||||
|
<para>The Naval Research Laboratory (NRL) reference implementation of the
|
||||||
|
NORM protocol includes support for 8-bit (and very soon 16-bit)
|
||||||
|
Reed-Solomon FEC encoding with additional support for other coding
|
||||||
|
algorithms (e.g., Low-Density Parity Check (LDPC)) planned for the future.
|
||||||
|
The NORM specification allows for different FEC algorithms to be applied
|
||||||
|
within the protocol. The current Reed-Solomon NORM FEC algorithms in the
|
||||||
|
NRL implementation are limited to modest code block sizes (With 16-bit
|
||||||
|
Reed-Solomon coding, larger block sizes will be allowed but very high data
|
||||||
|
rates may not be possible). For channels with random errors, the current
|
||||||
|
NORM FEC codecs are often adequate as there is flexibility in how the
|
||||||
|
encoded data can be partitioned into FEC blocks (a block consists of some
|
||||||
|
number of data segments (packets)) and the number of FEC parity packets
|
||||||
|
that can computed and possibly transmitted per source data block. For
|
||||||
|
channels with large bursts of packet loss (with respect to the configured
|
||||||
|
NORM FEC block size), it is quite possible that the number of lost packets
|
||||||
|
(erasures) that occur within a NORM FEC block may exceed the configured
|
||||||
|
erasure-filling capability. The <command>npc</command> utility was created
|
||||||
|
to "pre-encode" (and "post-decode") files for NORM transmission to silent
|
||||||
|
(non- NACKing) receivers by adding additional FEC encoding, and
|
||||||
|
importantly, interleaving of the FEC segments (packets) to re-distribute
|
||||||
|
bursts of packet losses as random losses over the entire file. It is thus
|
||||||
|
most applicable to very large files (with respect to FEC block
|
||||||
|
sizes).</para>
|
||||||
|
|
||||||
|
<para>The NORM protocol is described in Internet Engineering Task Force
|
||||||
|
(IETF) Request For Comments (RFC) RFC 3940 and RFC 3941. These are
|
||||||
|
experimental RFC standards. These documents have been revised in recent
|
||||||
|
Internet-Drafts and it should be noted that the Naval Research Laboratory
|
||||||
|
(NRL) implementation of NORM that is represented here has been updated to
|
||||||
|
reflect the revised protocol. In addition to this demonstration
|
||||||
|
application, NRL provides a NORM protocol library with a well-defined API
|
||||||
|
that it is suitable for application development. Additionally, the NRL
|
||||||
|
source code distribution supports building the NORM protocol as a
|
||||||
|
component into <emphasis>ns-2</emphasis> and OPNET network simulation
|
||||||
|
environments. Refer to the NRL NORM website <<ulink
|
||||||
|
url="http://cs.itd.nrl.navy.mil/work/norm">http://cs.itd.nrl.navy.mil/work/norm</ulink>>
|
||||||
|
for these other components as well as up-to-date versions of this
|
||||||
|
demonstration application and documentation.</para>
|
||||||
|
|
||||||
|
<para>The <command>npc</command> tool is designed to use in conjunction
|
||||||
|
with the NORM protocol and accompanying NORM file transfer examples that
|
||||||
|
are part of the NORM source code distribution. However, the encoded file
|
||||||
|
format that <command>npc</command> creates can be use with other
|
||||||
|
transports as well. The key is to align the segmentation parameters of the
|
||||||
|
<command>npc</command> configuration with that of the intended transport
|
||||||
|
mechanism.</para>
|
||||||
|
</sect1>
|
||||||
|
|
||||||
|
<sect1>
|
||||||
|
<title>Overview</title>
|
||||||
|
|
||||||
|
<para>The <command>npc</command> utility takes, as input", a file and
|
||||||
|
logically divides it into segments, adding cyclic-redundancy checksum
|
||||||
|
(CRC) to the segments, encoding the source segments with Reeed-Solomon
|
||||||
|
encoding (adding a configurable number of parity segments per FEC source
|
||||||
|
block), and interleaves the source and encoding segments to an output
|
||||||
|
file. The use of the CRC allows erasure to be detected and also provides
|
||||||
|
additional assurance of correct content delivery by possibly detecting bit
|
||||||
|
errors that may have been undetected during transport (i.e., link-layer
|
||||||
|
framing, Internet Protocol (IP), and/or User Datagram Protocol (UDP)
|
||||||
|
checksums). The interleaving by default is a block interleaver using the
|
||||||
|
entire file as a logical block, but a limit on the interleaving size can
|
||||||
|
be set to help increase the speed of the <command>npc</command>
|
||||||
|
encoding/decoding process. This may be useful for extremely large file
|
||||||
|
sizes.</para>
|
||||||
|
</sect1>
|
||||||
|
|
||||||
|
<sect1>
|
||||||
|
<title>Usage</title>
|
||||||
|
|
||||||
|
<para>The following is a synopsis of <command>npc</command> usage:</para>
|
||||||
|
|
||||||
|
<programlisting>npc {encode|decode} input <inFile> [output <outFile>][segment <segmentSize>]
|
||||||
|
{[[block <numData>][parity <numParity>]] |
|
||||||
|
[auto <parityPercentage>][bmax <maxBlockSize>]}
|
||||||
|
[imax <widthLimit>][ibuffer <bytes>][background][help][debug <debugLevel>]</programlisting>
|
||||||
|
|
||||||
|
<para>The <command>npc</command> utility may be instructed to either
|
||||||
|
"encode" a file (add FEC content and interleaving to the given
|
||||||
|
<inFile>) or "decode" a file that was previously encoded with
|
||||||
|
<command>npc</command>. The ".npc" file extension is suggested to
|
||||||
|
delineate files that are of the <command>npc</command> encoded format.
|
||||||
|
Note the "output" filename is optional. By default, <command>npc</command>
|
||||||
|
will use the filename of the <inFile> as the output filename,
|
||||||
|
replacing the '.' extension delimiter with a '_' (underscore) and adding
|
||||||
|
the ".npc" extension suffix. The <command>npc</command> format includes
|
||||||
|
some minimal "meta-data" in the first encoded <segmentSize> to
|
||||||
|
convey the file size and name of the original file. On decoding, if the
|
||||||
|
"output" file option is omitted, this "meta-data" is used to name the
|
||||||
|
decoded output file.</para>
|
||||||
|
|
||||||
|
<para>The optional FEC parameters,
|
||||||
|
<parameter><segmentSize></parameter>,
|
||||||
|
<parameter><numData></parameter>, and
|
||||||
|
<parameter><numParity></parameter> control the logical segmentation,
|
||||||
|
blocking, and amount of FEC parity content added to the file. For use with
|
||||||
|
NORM, it is recommended that the
|
||||||
|
<parameter><segmentSize></parameter> value correspond to the same
|
||||||
|
segmentation size used for NORM transmission. The
|
||||||
|
<parameter><numData></parameter> (source segments per FEC encoding
|
||||||
|
block) and <parameter><numParity></parameter> parameters should be
|
||||||
|
selected to provide erasure filling coverage for the expected transmission
|
||||||
|
packet loss characteristics. Note that when used with proactive NORM FEC
|
||||||
|
transmission, the <command>npc</command> encoding provides an "inner" FEC
|
||||||
|
code and interleaving and the NORM protocol provides an "outer" FEC
|
||||||
|
encoding. The "outer" NORM code might be configured to deal with typical
|
||||||
|
random packet loss due to channel BER, etc and the "inner"
|
||||||
|
<command>npc</command> interleaving and coding could be correspondingly
|
||||||
|
configured to handle expected burst losses (e.g. outages) that might
|
||||||
|
occur.</para>
|
||||||
|
|
||||||
|
<para>The <parameter><auto></parameter> option provides an
|
||||||
|
alternative means for setting the FEC encoding protection level instead of
|
||||||
|
using the <parameter><block></parameter> and
|
||||||
|
<parameter><parity></parameter> options. First the
|
||||||
|
<parameter><auto></parameter> option causes <command>npc</command>
|
||||||
|
to select a block size corresponding to the entire input file size (plus
|
||||||
|
the segment of meta data information that npc adds). Then, the
|
||||||
|
<parameter><auto></parameter> option
|
||||||
|
<parameter><parityPercentage></parameter> value is used to set the
|
||||||
|
number of parity packets per encoding block to the given percentage of the
|
||||||
|
automatically determined block size. For exanple, the command
|
||||||
|
"<option>auto 100"</option> causes <command>npc</command> to set an
|
||||||
|
encoding rate of 100%. I.e., the parity segments sent will equal the
|
||||||
|
number of segments in a bock. Note that the
|
||||||
|
<parameter><parityPercentage></parameter> can even exceed 100% if
|
||||||
|
desired for high levels of loss protection. Also note that the percentage
|
||||||
|
here is _not_ a loss protection percentage with a 100%
|
||||||
|
<parameter><parityPercentage></parameter> value being able to
|
||||||
|
correct up to 50% errored or lost packets within a coding block. With 50%
|
||||||
|
uniform random packet loss, this would result in successful file transfer
|
||||||
|
about 50% of the time as, per Gaussian distribution, burst error
|
||||||
|
probabilities would result in half of blocks arriving with greater than
|
||||||
|
50% packet loss and half with less than 50% packet loss.</para>
|
||||||
|
|
||||||
|
<para>When the <command>npc</command> encoder uses the
|
||||||
|
"<option>auto</option>" command the <command>npc</command> decoder MUST
|
||||||
|
also use the "<option>auto</option>" command and be configured with the
|
||||||
|
same <parameter><segmentSize></parameter> and
|
||||||
|
<parameter><parityPercentage></parameter> values. Similarly when the
|
||||||
|
"<option>block</option>" and "<option>parity</option>" commands are used
|
||||||
|
to explicitly set the <parameter><numdData></parameter> and
|
||||||
|
<parameter><numParity></parameter> at the encoder, the decoder MUST
|
||||||
|
be configured with the same corresponding options and values, again
|
||||||
|
including <parameter><segmentSize></parameter>. And for use with
|
||||||
|
NORM protocol transport, the <parameter><segmentSize></parameter>
|
||||||
|
parameter SHOULD be matched for best coding gain performance. The NORM
|
||||||
|
block size (numData) and parity (numParity) parameters may be set
|
||||||
|
indepedently. Basically, the NORM protocol proactive erasure coding can be
|
||||||
|
configured to deal with expected short term random packet loss while the
|
||||||
|
<command>npc</command> parameters can be configured to counter large burst
|
||||||
|
(or outage) losses. The inner/outer encoding approach that the combination
|
||||||
|
of <command>npc</command> and NORM provides, can allow for a sort of
|
||||||
|
multiplicative coding gain to deal well with both random packet loss and
|
||||||
|
bursts or outages with lower FEC overhead. However, when the
|
||||||
|
<command>npc</command> coding is configured (e.g., via the "auto" option)
|
||||||
|
to encapsulate an entire file into a single logical coding block, the
|
||||||
|
desired level of loss protection can be simply "dialed into" the
|
||||||
|
<command>npc</command> <parameter><parityPercentage></parameter>
|
||||||
|
option. The tradeoff is that the larger FEC block size increases the
|
||||||
|
computational requirement for file encoding and decoding. Future versions
|
||||||
|
of <command>npc</command> may provide additional FEC code types</para>
|
||||||
|
|
||||||
|
<para>As basic example usage, to encode a file name "originalFile.txt"
|
||||||
|
with the default <command>npc</command> naming convention, FEC, and
|
||||||
|
interleaving parameters, use the following syntax:</para>
|
||||||
|
|
||||||
|
<programlisting>npc encode input originalFile.txt</programlisting>
|
||||||
|
|
||||||
|
<para>The default npc configuration is XXX.</para>
|
||||||
|
|
||||||
|
<para>This will produce and output file named
|
||||||
|
"<filename>originalFile_txt.npc</filename>" in the current working
|
||||||
|
directory, The original file can be recovered (decoded) using the
|
||||||
|
syntax:</para>
|
||||||
|
|
||||||
|
<programlisting>npc decode input originalFile_txt.npc</programlisting>
|
||||||
|
|
||||||
|
<para>This will decode the ".npc" file, and in this case produce a file
|
||||||
|
named "<filename>originalFile.txt"</filename> in the current working
|
||||||
|
directory. (The file name information was stored in first "meta data"
|
||||||
|
segment of the ".npc" file). This default naming convention can be
|
||||||
|
overridden by using the npc "output" option. For example, the
|
||||||
|
syntax:</para>
|
||||||
|
|
||||||
|
<programlisting>npc decode input originalFile_txt.npc output file.txt</programlisting>
|
||||||
|
|
||||||
|
<para>will produce a file named "<filename>file.txt</filename>" that is
|
||||||
|
identical in content to "<filename>originalFile.txt</filename>".</para>
|
||||||
|
|
||||||
|
<sect2>
|
||||||
|
<title>Notes</title>
|
||||||
|
|
||||||
|
<para>The FEC and interleaving parameters that are used for
|
||||||
|
<command>npc</command> encoding MUST be exactly matched to successfully
|
||||||
|
decode the encoded file. I.e., if the defaults are used for encoding,
|
||||||
|
the defaults must be used for decoding. The parameters that must match
|
||||||
|
include <parameter><segmentSize></parameter>,
|
||||||
|
<parameter><parityPercentage></parameter> and
|
||||||
|
<parameter><maxBlockSize></parameter> (or
|
||||||
|
<parameter><numData></parameter> and
|
||||||
|
<parameter><numParity></parameter>), and
|
||||||
|
<parameter><widthMax></parameter>.</para>
|
||||||
|
|
||||||
|
<para>It is possible that in some cases it may be beneficial to apply
|
||||||
|
more proactive FEC content with the <command>npc</command> program
|
||||||
|
instead of with the NORM transport. The trade-offs are
|
||||||
|
scenario-specific.</para>
|
||||||
|
|
||||||
|
<para>The NRL "<command>norm</command>" demonstration application has
|
||||||
|
commands included to support transport of <command>npc</command> encoded
|
||||||
|
files. The distinction here is that a file that _fails_ NORM transport
|
||||||
|
might still be successfully decoded with <command>npc</command>. There
|
||||||
|
are two receiver-side norm demo application options that apply
|
||||||
|
here:</para>
|
||||||
|
|
||||||
|
<orderedlist>
|
||||||
|
<listitem>
|
||||||
|
<para>The "<option>saveAborts</option>" command causes
|
||||||
|
<emphasis>norm</emphasis> to not delete (and attempt to postprocess)
|
||||||
|
"aborted" files (files that failed reliable NORM transport).</para>
|
||||||
|
</listitem>
|
||||||
|
|
||||||
|
<listitem>
|
||||||
|
<para>The <command>norm</command> "<option>lowDelay</option>"
|
||||||
|
command should be applied for silent-receivers to more immediately
|
||||||
|
deliver "failed" files to the application for post-processing (i.e.,
|
||||||
|
attempted <command>npc</command> decoding)</para>
|
||||||
|
</listitem>
|
||||||
|
</orderedlist>
|
||||||
|
</sect2>
|
||||||
|
</sect1>
|
||||||
|
|
||||||
|
<sect1>
|
||||||
|
<title><command>npc</command> Command Reference</title>
|
||||||
|
|
||||||
|
<para>The following table describes each of the <command>npc</command>
|
||||||
|
commands available in the command-line syntax.</para>
|
||||||
|
|
||||||
|
<informaltable frame="all">
|
||||||
|
<tgroup cols="2">
|
||||||
|
<colspec colnum="1" colwidth="1*"/>
|
||||||
|
|
||||||
|
<colspec colname="2" colwidth="2*"/>
|
||||||
|
|
||||||
|
<tbody>
|
||||||
|
<row>
|
||||||
|
<entry><para><option>encode</option> |
|
||||||
|
<option>decode</option></para></entry>
|
||||||
|
|
||||||
|
<entry><para>Determine whether <command>npc</command> is to encode
|
||||||
|
or decode the given <parameter><inFile></parameter>. This
|
||||||
|
option is required and only one should be given.</para></entry>
|
||||||
|
</row>
|
||||||
|
|
||||||
|
<row>
|
||||||
|
<entry><para><option>input
|
||||||
|
</option><parameter><inFile></parameter></para></entry>
|
||||||
|
|
||||||
|
<entry><para>Specifies the file to be processed. Required
|
||||||
|
command.</para></entry>
|
||||||
|
</row>
|
||||||
|
|
||||||
|
<row>
|
||||||
|
<entry><para><option>output
|
||||||
|
</option><parameter><outFile></parameter></para></entry>
|
||||||
|
|
||||||
|
<entry><para>Specifies the name of the output file to be produced.
|
||||||
|
Overrides the default <command>npc</command> output file naming
|
||||||
|
convention. Optional.</para></entry>
|
||||||
|
</row>
|
||||||
|
|
||||||
|
<row>
|
||||||
|
<entry><para><option>segment
|
||||||
|
</option><parameter><segmentSize></parameter></para></entry>
|
||||||
|
|
||||||
|
<entry><para>Sets the segmentation size (e.g., packet payload
|
||||||
|
size) in bytes. Note four bytes of the
|
||||||
|
<parameter><segmentSize></parameter> are used for a 32-bit
|
||||||
|
CRC that <command>npc</command> applies to each segment. (Default
|
||||||
|
<parameter><segmentSize></parameter> is 1024
|
||||||
|
bytes)</para></entry>
|
||||||
|
</row>
|
||||||
|
|
||||||
|
<row>
|
||||||
|
<entry><para><option>block
|
||||||
|
</option><parameter><numData></parameter></para></entry>
|
||||||
|
|
||||||
|
<entry><para>Specify the number of source data segments (packets)
|
||||||
|
per npc FEC coding block. (Default block sizing is
|
||||||
|
auto)</para></entry>
|
||||||
|
</row>
|
||||||
|
|
||||||
|
<row>
|
||||||
|
<entry><para><option>parity
|
||||||
|
</option><parameter><numParity></parameter></para></entry>
|
||||||
|
|
||||||
|
<entry><para>Specify the number of FEC parity segments (packets)
|
||||||
|
added per npc FEC coding block. (Default is 2
|
||||||
|
segments).</para></entry>
|
||||||
|
</row>
|
||||||
|
|
||||||
|
<row>
|
||||||
|
<entry><para><option>auto
|
||||||
|
</option><parameter><parityPercentage></parameter></para></entry>
|
||||||
|
|
||||||
|
<entry><para>Specifies automatic FEC block sizing with
|
||||||
|
<parameter><parityPercentage></parameter> indicating the
|
||||||
|
percentage of FEC parity segments to include per block. The "auto"
|
||||||
|
block sizing sets the block size as large as possible to treat the
|
||||||
|
entire files as one logical FEC block to maximize FEC performance.
|
||||||
|
The maximum possible block size currently supported by npc are
|
||||||
|
blocks where (numData + numParity) is less than or equal to 65536.
|
||||||
|
The maximum buffer size can be limited by using the
|
||||||
|
<option>bmax</option> command.</para></entry>
|
||||||
|
</row>
|
||||||
|
|
||||||
|
<row>
|
||||||
|
<entry><para><option>bmax
|
||||||
|
</option><parameter><maxBlockSize></parameter></para></entry>
|
||||||
|
|
||||||
|
<entry><para>Limits the maximum block size when the
|
||||||
|
<option>auto</option> command is used for automatic block sizing
|
||||||
|
(Default is 65536)</para></entry>
|
||||||
|
</row>
|
||||||
|
|
||||||
|
<row>
|
||||||
|
<entry><para><option>imax
|
||||||
|
</option><parameter><widthMax></parameter></para></entry>
|
||||||
|
|
||||||
|
<entry><para>Limits interleaving of encoded file to a maximum
|
||||||
|
interleaver width of <parameter><widthMax></parameter>
|
||||||
|
segments. A value of ZERO (or less) defaults to
|
||||||
|
<command>npc</command> calculating a block interleaver that
|
||||||
|
encompasses the entire encoded file size. For extremely large
|
||||||
|
files, this option may be beneficial to limit file seeking
|
||||||
|
operations required to interleave the file. If the encoded file
|
||||||
|
size is less than
|
||||||
|
<parameter><widthMax></parameter>*<parameter><widthMax></parameter>
|
||||||
|
segments, <command>npc</command> will again calculate its own
|
||||||
|
maximum block size. (Default is 1000 segments interleaver depth
|
||||||
|
(i.e., about 1 Gbyte interleaver size with the default 1024 byte
|
||||||
|
<parameter><segmentSize></parameter> value))</para></entry>
|
||||||
|
</row>
|
||||||
|
|
||||||
|
<row>
|
||||||
|
<entry><para><option>ibuffer
|
||||||
|
</option><parameter><bufferSize></parameter></para></entry>
|
||||||
|
|
||||||
|
<entry><para>This sets the maximum memory (in bytes) that
|
||||||
|
<command>npc</command> allocates for encoding. A larger value
|
||||||
|
allows <command>npc</command> to perform file input/output with
|
||||||
|
less seeking and improved encoding/decoding times can be achieved.
|
||||||
|
(Default is 1.5 GByte)</para></entry>
|
||||||
|
</row>
|
||||||
|
|
||||||
|
<row>
|
||||||
|
<entry><para><option>debug
|
||||||
|
</option><parameter><debugLevel></parameter></para></entry>
|
||||||
|
|
||||||
|
<entry><para>Specifies debug output verbosity. Higher number is
|
||||||
|
more verbose debugging information. (Default is
|
||||||
|
ZERO).</para></entry>
|
||||||
|
</row>
|
||||||
|
|
||||||
|
<row>
|
||||||
|
<entry><para><option>background</option></para></entry>
|
||||||
|
|
||||||
|
<entry><para>Sets percentage of received messages that are
|
||||||
|
randomly dropped (for testing purposes). Default = 0.0
|
||||||
|
percent.</para></entry>
|
||||||
|
</row>
|
||||||
|
|
||||||
|
<row>
|
||||||
|
<entry><para><option>help</option></para></entry>
|
||||||
|
|
||||||
|
<entry><para>Displays <command>npc</command> usage
|
||||||
|
statement.</para></entry>
|
||||||
|
</row>
|
||||||
|
</tbody>
|
||||||
|
</tgroup>
|
||||||
|
</informaltable>
|
||||||
|
</sect1>
|
||||||
|
</article>
|
||||||
2
protolib
2
protolib
|
|
@ -1 +1 @@
|
||||||
Subproject commit a28606945f6b507760548b242b6706b51b5b69d3
|
Subproject commit e5ac157eb61e991c74acfe39e2febe8c09e187dc
|
||||||
|
|
@ -1,6 +1,6 @@
|
||||||
|
|
||||||
#include "protoApp.h"
|
#include "protoApp.h"
|
||||||
#include "normFile.h"
|
#include "protoFile.h"
|
||||||
|
|
||||||
// Commment this #define out to use new, faster RS8 codec instead
|
// Commment this #define out to use new, faster RS8 codec instead
|
||||||
// (TBD - provide option use 16-bit Reed Solomon for large block sizes?)
|
// (TBD - provide option use 16-bit Reed Solomon for large block sizes?)
|
||||||
|
|
@ -10,6 +10,7 @@
|
||||||
#include "normEncoderMDP.h"
|
#include "normEncoderMDP.h"
|
||||||
#else
|
#else
|
||||||
#include "normEncoderRS8.h"
|
#include "normEncoderRS8.h"
|
||||||
|
#include "normEncoderRS16.h"
|
||||||
#endif // if/else USE_MDP_FEC
|
#endif // if/else USE_MDP_FEC
|
||||||
|
|
||||||
#include <sys/types.h> // for BYTE_ORDER macro
|
#include <sys/types.h> // for BYTE_ORDER macro
|
||||||
|
|
@ -38,22 +39,22 @@ class NormPrecodeApp : public ProtoApp
|
||||||
bool Encode();
|
bool Encode();
|
||||||
bool Decode();
|
bool Decode();
|
||||||
|
|
||||||
void InitInterleaver(NormFile::Offset numSegments);
|
void InitInterleaver(ProtoFile::Offset numSegments);
|
||||||
NormFile::Offset ComputeInterleaverOffset(NormFile::Offset segmentId, NormFile::Offset numSegments);
|
ProtoFile::Offset ComputeInterleaverOffset(ProtoFile::Offset segmentId, ProtoFile::Offset numSegments);
|
||||||
NormFile::Offset ComputeSegmentOffset(NormFile::Offset interleaverId, NormFile::Offset numSegments);
|
ProtoFile::Offset ComputeSegmentOffset(ProtoFile::Offset interleaverId, ProtoFile::Offset numSegments);
|
||||||
// CRC32 checksum stuff
|
// CRC32 checksum stuff
|
||||||
static const UINT32 CRC32_TABLE[256];
|
static const UINT32 CRC32_TABLE[256];
|
||||||
static UINT32 ComputeCRC32(const char* buffer, unsigned int buflen);
|
static UINT32 ComputeCRC32(const char* buffer, unsigned int buflen);
|
||||||
|
|
||||||
static const NormFile::Offset SEGMENT_MIN;
|
static const ProtoFile::Offset SEGMENT_MIN;
|
||||||
static const NormFile::Offset SEGMENT_MAX;
|
static const ProtoFile::Offset SEGMENT_MAX;
|
||||||
|
|
||||||
// We use these assuming IEEE754 floating point
|
// We use these assuming IEEE754 floating point
|
||||||
static NormFile::Offset ntoho(NormFile::Offset offset)
|
static ProtoFile::Offset ntoho(ProtoFile::Offset offset)
|
||||||
{
|
{
|
||||||
# if BYTE_ORDER == LITTLE_ENDIAN
|
# if BYTE_ORDER == LITTLE_ENDIAN
|
||||||
NormFile::Offset result;
|
ProtoFile::Offset result;
|
||||||
switch (sizeof(NormFile::Offset))
|
switch (sizeof(ProtoFile::Offset))
|
||||||
{
|
{
|
||||||
case 8:
|
case 8:
|
||||||
{
|
{
|
||||||
|
|
@ -78,37 +79,40 @@ class NormPrecodeApp : public ProtoApp
|
||||||
return offset;
|
return offset;
|
||||||
#endif // if/else __BIG_ENDIAN
|
#endif // if/else __BIG_ENDIAN
|
||||||
}
|
}
|
||||||
static NormFile::Offset htono(NormFile::Offset offset)
|
static ProtoFile::Offset htono(ProtoFile::Offset offset)
|
||||||
{
|
{
|
||||||
return ntoho(offset);
|
return ntoho(offset);
|
||||||
}
|
}
|
||||||
|
|
||||||
NormFile in_file;
|
ProtoFile in_file;
|
||||||
char in_file_path[PATH_MAX];
|
char in_file_path[PATH_MAX];
|
||||||
NormFile out_file;
|
ProtoFile out_file;
|
||||||
bool encode;
|
bool encode;
|
||||||
|
|
||||||
unsigned int segment_size; // should be same as NORM segment size
|
unsigned int segment_size; // should be same as NORM segment size
|
||||||
unsigned int num_data;
|
unsigned int num_data;
|
||||||
unsigned int num_parity;
|
unsigned int num_parity;
|
||||||
|
double parity_fraction; // invokes "auto" block/parity sizing based on file size
|
||||||
|
ProtoFile::Offset b_max; // optional max block size for "auto" mode
|
||||||
|
|
||||||
NormFile::Offset i_max; // max interleaver dimension
|
ProtoFile::Offset i_max; // max interleaver dimension
|
||||||
NormFile::Offset i_buffer_max; // Read buffer max (bigger yields less seeking)
|
ProtoFile::Offset i_buffer_max; // Read buffer max (bigger yields less seeking)
|
||||||
|
|
||||||
NormFile::Offset interleaver_width;
|
ProtoFile::Offset interleaver_width;
|
||||||
NormFile::Offset interleaver_height;
|
ProtoFile::Offset interleaver_height;
|
||||||
NormFile::Offset interleaver_size; // (width * height)
|
ProtoFile::Offset interleaver_size; // (width * height)
|
||||||
|
|
||||||
}; // end class NormPrecodeApp
|
}; // end class NormPrecodeApp
|
||||||
|
|
||||||
// Our application instance
|
// Our application instance
|
||||||
PROTO_INSTANTIATE_APP(NormPrecodeApp)
|
PROTO_INSTANTIATE_APP(NormPrecodeApp)
|
||||||
|
|
||||||
const NormFile::Offset NormPrecodeApp::SEGMENT_MIN = 8;
|
const ProtoFile::Offset NormPrecodeApp::SEGMENT_MIN = 8;
|
||||||
const NormFile::Offset NormPrecodeApp::SEGMENT_MAX = 8192;
|
const ProtoFile::Offset NormPrecodeApp::SEGMENT_MAX = 8192;
|
||||||
|
|
||||||
NormPrecodeApp::NormPrecodeApp()
|
NormPrecodeApp::NormPrecodeApp()
|
||||||
: encode(true), segment_size(1024), num_data(196), num_parity(4),
|
: encode(true), segment_size(1024), num_data(196), num_parity(4),
|
||||||
|
parity_fraction(-1.0), b_max(65536),
|
||||||
i_max(1000), i_buffer_max(1500000000)
|
i_max(1000), i_buffer_max(1500000000)
|
||||||
{
|
{
|
||||||
in_file_path[0] = '\0';
|
in_file_path[0] = '\0';
|
||||||
|
|
@ -122,6 +126,7 @@ void NormPrecodeApp::Usage()
|
||||||
{
|
{
|
||||||
fprintf(stderr, "Usage: npc {encode|decode} input <inFile> [output <outFile>]\n"
|
fprintf(stderr, "Usage: npc {encode|decode} input <inFile> [output <outFile>]\n"
|
||||||
" [segment <segmentSize>][block numData][parity numParity]\n"
|
" [segment <segmentSize>][block numData][parity numParity]\n"
|
||||||
|
" [auto <parityPercentage>]\n"
|
||||||
" [background][help][debug <debugLevel>\n");
|
" [background][help][debug <debugLevel>\n");
|
||||||
} // end NormPrecodeApp::Usage()
|
} // end NormPrecodeApp::Usage()
|
||||||
|
|
||||||
|
|
@ -136,6 +141,8 @@ const char* const NormPrecodeApp::cmd_list[] =
|
||||||
"+segment", // set segment size (default = 1024)
|
"+segment", // set segment size (default = 1024)
|
||||||
"+block", // set block size (default = 128)
|
"+block", // set block size (default = 128)
|
||||||
"+parity", // set parity per block (default = 2)
|
"+parity", // set parity per block (default = 2)
|
||||||
|
"+auto", // set auto block/parity mode (specifies parity_fraction as percent)
|
||||||
|
"+bmax", // limit maximum allowed block size for "auto" operation (default = 65536)
|
||||||
"+imax", // set interleaver max dimension
|
"+imax", // set interleaver max dimension
|
||||||
"+ibuffer", // set imax interleaver buffer (buffer is used if interleaver size fits)
|
"+ibuffer", // set imax interleaver buffer (buffer is used if interleaver size fits)
|
||||||
"-background", // run w/out command shel (Win32)
|
"-background", // run w/out command shel (Win32)
|
||||||
|
|
@ -208,24 +215,45 @@ bool NormPrecodeApp::OnCommand(const char* cmd, const char* val)
|
||||||
}
|
}
|
||||||
else if (!strncmp("block", cmd, len))
|
else if (!strncmp("block", cmd, len))
|
||||||
{
|
{
|
||||||
int numData = atoi(val);
|
unsigned short numData;
|
||||||
if ((numData < 1) || (numData > 127))
|
if (1 != sscanf(val, "%hu", &numData))
|
||||||
{
|
{
|
||||||
PLOG(PL_FATAL, "npc: error: block <numData> out of range\n");
|
PLOG(PL_FATAL, "npc: error: invalid block <numData> value!\n");
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
num_data = numData;
|
num_data = numData;
|
||||||
}
|
}
|
||||||
else if (!strncmp("parity", cmd, len))
|
else if (!strncmp("parity", cmd, len))
|
||||||
{
|
{
|
||||||
int numParity = atoi(val);
|
unsigned short numParity;
|
||||||
if ((numParity < 0) || (numParity > 127))
|
if (1 != sscanf(val, "%hu", &numParity))
|
||||||
{
|
{
|
||||||
PLOG(PL_FATAL, "npc: error: parity <numParity> out of range\n");
|
PLOG(PL_FATAL, "npc: error: invalid block <numParity> value!\n");
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
num_parity = numParity;
|
num_parity = numParity;
|
||||||
}
|
}
|
||||||
|
else if (!strncmp("auto", cmd, len))
|
||||||
|
{
|
||||||
|
double percent;
|
||||||
|
if (1 != sscanf(val,"%lf", &percent))
|
||||||
|
{
|
||||||
|
PLOG(PL_FATAL, "npc: error: invalid block <auto> value!\n");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if (percent < 0.0)
|
||||||
|
{
|
||||||
|
PLOG(PL_FATAL, "npc: error: invalid block <auto> value!\n");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
parity_fraction = percent/100.0;
|
||||||
|
}
|
||||||
|
else if (!strncmp("bmax", cmd, len))
|
||||||
|
{
|
||||||
|
int bMax = atoi(val);
|
||||||
|
if (bMax <= 0) bMax = 65536;
|
||||||
|
b_max = bMax;
|
||||||
|
}
|
||||||
else if (!strncmp("imax", cmd, len))
|
else if (!strncmp("imax", cmd, len))
|
||||||
{
|
{
|
||||||
int iMax = atoi(val);
|
int iMax = atoi(val);
|
||||||
|
|
@ -318,6 +346,49 @@ bool NormPrecodeApp::OnStartup(int argc, const char*const* argv)
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (parity_fraction >= 0.0)
|
||||||
|
{
|
||||||
|
// Set up auto mode using input file size
|
||||||
|
// (This uses the max block size possible for the file given "segment_size")
|
||||||
|
ProtoFile::Offset fileSize = in_file.GetSize();
|
||||||
|
unsigned int blockSize;
|
||||||
|
if (encode)
|
||||||
|
{
|
||||||
|
// (segment_size - 4) here accounts for 4-byte CRC
|
||||||
|
blockSize = (unsigned int) (fileSize / (segment_size - 4));
|
||||||
|
if (0 != (fileSize % (segment_size - 4)))
|
||||||
|
blockSize += 1;
|
||||||
|
blockSize += 1; // for meta data segment
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// Need to use post decode size
|
||||||
|
ProtoFile::Offset numSegments = fileSize / segment_size;
|
||||||
|
ASSERT(0 == (fileSize % segment_size));
|
||||||
|
blockSize = (unsigned int)((numSegments / (1.0 + parity_fraction)) + 0.5);
|
||||||
|
}
|
||||||
|
if (blockSize > b_max) blockSize = b_max;
|
||||||
|
unsigned int numParity = (unsigned int)((parity_fraction * blockSize) + 0.5);
|
||||||
|
if ((blockSize + numParity) > 65536)
|
||||||
|
{
|
||||||
|
// need to scale down to fit within max possible FEC block size
|
||||||
|
double scaleFactor = 65536.0 / ((double)(blockSize + numParity));
|
||||||
|
blockSize = (unsigned int)(scaleFactor * blockSize);
|
||||||
|
numParity = (unsigned int)(scaleFactor * numParity);
|
||||||
|
}
|
||||||
|
//TRACE("auto fileSize:%llu block:%u parity:%u\n", fileSize, blockSize, numParity);
|
||||||
|
num_data = blockSize;
|
||||||
|
num_parity = numParity;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Validate num_data / num_parity params
|
||||||
|
unsigned int totalBlockSize = num_data + num_parity;
|
||||||
|
if (totalBlockSize > 65536)
|
||||||
|
{
|
||||||
|
PLOG(PL_FATAL, "npc: error: numData/numParity total exceeds max block size!\n");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
if (encode)
|
if (encode)
|
||||||
return Encode();
|
return Encode();
|
||||||
else
|
else
|
||||||
|
|
@ -335,9 +406,9 @@ void NormPrecodeApp::OnShutdown()
|
||||||
|
|
||||||
#define DIFF_T(a,b) (1+ 1000000*(a.tv_sec - b.tv_sec) + (a.tv_usec - b.tv_usec) )
|
#define DIFF_T(a,b) (1+ 1000000*(a.tv_sec - b.tv_sec) + (a.tv_usec - b.tv_usec) )
|
||||||
|
|
||||||
void NormPrecodeApp::InitInterleaver(NormFile::Offset numSegments)
|
void NormPrecodeApp::InitInterleaver(ProtoFile::Offset numSegments)
|
||||||
{
|
{
|
||||||
interleaver_width = (NormFile::Offset)(sqrt((double)numSegments));
|
interleaver_width = (ProtoFile::Offset)(sqrt((double)numSegments));
|
||||||
interleaver_height = numSegments / interleaver_width;
|
interleaver_height = numSegments / interleaver_width;
|
||||||
if (0 != (numSegments % interleaver_height)) interleaver_height++;
|
if (0 != (numSegments % interleaver_height)) interleaver_height++;
|
||||||
// Limit dimension if "i_max" is set to non-zero value
|
// Limit dimension if "i_max" is set to non-zero value
|
||||||
|
|
@ -345,19 +416,19 @@ void NormPrecodeApp::InitInterleaver(NormFile::Offset numSegments)
|
||||||
interleaver_height = interleaver_width = i_max;
|
interleaver_height = interleaver_width = i_max;
|
||||||
interleaver_size = interleaver_height * interleaver_width;
|
interleaver_size = interleaver_height * interleaver_width;
|
||||||
PLOG(PL_INFO, "npc interleaver width:%lu height:%lu segments (numSeg:%lld)\n",
|
PLOG(PL_INFO, "npc interleaver width:%lu height:%lu segments (numSeg:%lld)\n",
|
||||||
(unsigned long)interleaver_width, (unsigned long)interleaver_height, numSegments);
|
(unsigned long)interleaver_width, (unsigned long)interleaver_height, numSegments);
|
||||||
|
|
||||||
} // end NormPrecodeApp::InitInterleaver()
|
} // end NormPrecodeApp::InitInterleaver()
|
||||||
|
|
||||||
|
|
||||||
NormFile::Offset NormPrecodeApp::ComputeInterleaverOffset(NormFile::Offset segmentId, NormFile::Offset numSegments)
|
ProtoFile::Offset NormPrecodeApp::ComputeInterleaverOffset(ProtoFile::Offset segmentId, ProtoFile::Offset numSegments)
|
||||||
{
|
{
|
||||||
ASSERT(0 != interleaver_height);
|
ASSERT(0 != interleaver_height);
|
||||||
|
|
||||||
NormFile::Offset interleaverWidth = interleaver_width;
|
ProtoFile::Offset interleaverWidth = interleaver_width;
|
||||||
NormFile::Offset interleaverHeight = interleaver_height;
|
ProtoFile::Offset interleaverHeight = interleaver_height;
|
||||||
NormFile::Offset interleaverSize = interleaver_size;
|
ProtoFile::Offset interleaverSize = interleaver_size;
|
||||||
NormFile::Offset blockId;
|
ProtoFile::Offset blockId;
|
||||||
if (i_max > 0)
|
if (i_max > 0)
|
||||||
{
|
{
|
||||||
blockId = segmentId / interleaverSize;
|
blockId = segmentId / interleaverSize;
|
||||||
|
|
@ -366,26 +437,25 @@ NormFile::Offset NormPrecodeApp::ComputeInterleaverOffset(NormFile::Offset segme
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
blockId = 0;
|
blockId = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
// Check to see if we're in the last block
|
// Check to see if we're in the last block
|
||||||
NormFile::Offset lastSegmentId = numSegments - 1;
|
ProtoFile::Offset lastSegmentId = numSegments - 1;
|
||||||
NormFile::Offset lastBlockId = lastSegmentId / interleaverSize;
|
ProtoFile::Offset lastBlockId = lastSegmentId / interleaverSize;
|
||||||
if ((blockId == lastBlockId) && (0 != (numSegments % interleaverSize)))
|
if ((blockId == lastBlockId) && (0 != (numSegments % interleaverSize)))
|
||||||
{
|
{
|
||||||
// This block is smaller than our usual interleaver_size,
|
// This block is smaller than our usual interleaver_size,
|
||||||
// so we're going to "square things up" to maximize the
|
// so we're going to "square things up" to maximize the
|
||||||
// distance of this last block within interleaver_size constraint
|
// distance of this last block within interleaver_size constraint
|
||||||
NormFile::Offset lastBlockSize = (numSegments % interleaverSize);
|
ProtoFile::Offset lastBlockSize = (numSegments % interleaverSize);
|
||||||
interleaverWidth = (NormFile::Offset)(sqrt((double)lastBlockSize));
|
interleaverWidth = (ProtoFile::Offset)(sqrt((double)lastBlockSize));
|
||||||
interleaverHeight = lastBlockSize / interleaverWidth;
|
interleaverHeight = lastBlockSize / interleaverWidth;
|
||||||
if (0 != (lastBlockSize % interleaverHeight)) interleaverHeight++;
|
if (0 != (lastBlockSize % interleaverHeight)) interleaverHeight++;
|
||||||
}
|
}
|
||||||
|
|
||||||
NormFile::Offset interleaverCol = segmentId / interleaverHeight;
|
ProtoFile::Offset interleaverCol = segmentId / interleaverHeight;
|
||||||
NormFile::Offset interleaverRow = segmentId % interleaverHeight;
|
ProtoFile::Offset interleaverRow = segmentId % interleaverHeight;
|
||||||
NormFile::Offset interleaverId = ((interleaverRow * interleaverWidth) + interleaverCol);
|
ProtoFile::Offset interleaverId = ((interleaverRow * interleaverWidth) + interleaverCol);
|
||||||
|
|
||||||
if (0 != blockId)
|
if (0 != blockId)
|
||||||
interleaverId += (blockId * interleaver_size);
|
interleaverId += (blockId * interleaver_size);
|
||||||
|
|
@ -394,18 +464,18 @@ NormFile::Offset NormPrecodeApp::ComputeInterleaverOffset(NormFile::Offset segme
|
||||||
if (interleaverId >= numSegments)
|
if (interleaverId >= numSegments)
|
||||||
{
|
{
|
||||||
// We're here because we hit a "hole" in the rectangle
|
// We're here because we hit a "hole" in the rectangle
|
||||||
NormFile::Offset lastSegmentId = numSegments - 1;
|
ProtoFile::Offset lastSegmentId = numSegments - 1;
|
||||||
if (0 != blockId)
|
if (0 != blockId)
|
||||||
{
|
{
|
||||||
interleaverId = interleaverId % interleaverSize;
|
interleaverId = interleaverId % interleaverSize;
|
||||||
lastSegmentId = lastSegmentId % interleaverSize;
|
lastSegmentId = lastSegmentId % interleaverSize;
|
||||||
}
|
}
|
||||||
// Find non-interleaved position of lastSegmentId within interleaver
|
// Find non-interleaved position of lastSegmentId within interleaver
|
||||||
NormFile::Offset maxRow = lastSegmentId / interleaverWidth;
|
ProtoFile::Offset maxRow = lastSegmentId / interleaverWidth;
|
||||||
NormFile::Offset maxCol = lastSegmentId % interleaverWidth;
|
ProtoFile::Offset maxCol = lastSegmentId % interleaverWidth;
|
||||||
// There may be empty rows if lastSegmentId small wr2 interleaver size
|
// There may be empty rows if lastSegmentId small wr2 interleaver size
|
||||||
NormFile::Offset emptyRows = interleaverHeight - maxRow - 1;
|
ProtoFile::Offset emptyRows = interleaverHeight - maxRow - 1;
|
||||||
NormFile::Offset delta = 1 + emptyRows * interleaverCol;
|
ProtoFile::Offset delta = 1 + emptyRows * interleaverCol;
|
||||||
if (interleaverCol > maxCol)
|
if (interleaverCol > maxCol)
|
||||||
{
|
{
|
||||||
delta += interleaverRow - maxRow;
|
delta += interleaverRow - maxRow;
|
||||||
|
|
@ -417,8 +487,8 @@ NormFile::Offset NormPrecodeApp::ComputeInterleaverOffset(NormFile::Offset segme
|
||||||
}
|
}
|
||||||
|
|
||||||
// Find interleaved position of lastSegmentId within interleaver
|
// Find interleaved position of lastSegmentId within interleaver
|
||||||
NormFile::Offset lastCol = lastSegmentId / interleaverHeight;
|
ProtoFile::Offset lastCol = lastSegmentId / interleaverHeight;
|
||||||
NormFile::Offset lastRow = lastSegmentId % interleaverHeight;
|
ProtoFile::Offset lastRow = lastSegmentId % interleaverHeight;
|
||||||
|
|
||||||
// Remap this segment to the "delta" interleaved position after "lastSegmentId"
|
// Remap this segment to the "delta" interleaved position after "lastSegmentId"
|
||||||
lastRow += delta;
|
lastRow += delta;
|
||||||
|
|
@ -445,12 +515,12 @@ NormFile::Offset NormPrecodeApp::ComputeInterleaverOffset(NormFile::Offset segme
|
||||||
} // end NormPrecodeApp::ComputeInterleaverOffset()
|
} // end NormPrecodeApp::ComputeInterleaverOffset()
|
||||||
|
|
||||||
|
|
||||||
NormFile::Offset NormPrecodeApp::ComputeSegmentOffset(NormFile::Offset interleaverId, NormFile::Offset numSegments)
|
ProtoFile::Offset NormPrecodeApp::ComputeSegmentOffset(ProtoFile::Offset interleaverId, ProtoFile::Offset numSegments)
|
||||||
{
|
{
|
||||||
NormFile::Offset interleaverWidth = interleaver_width;
|
ProtoFile::Offset interleaverWidth = interleaver_width;
|
||||||
NormFile::Offset interleaverHeight = interleaver_height;
|
ProtoFile::Offset interleaverHeight = interleaver_height;
|
||||||
NormFile::Offset interleaverSize = interleaver_size;
|
ProtoFile::Offset interleaverSize = interleaver_size;
|
||||||
NormFile::Offset blockId;
|
ProtoFile::Offset blockId;
|
||||||
if (i_max > 0)
|
if (i_max > 0)
|
||||||
{
|
{
|
||||||
blockId = interleaverId / interleaverSize;
|
blockId = interleaverId / interleaverSize;
|
||||||
|
|
@ -462,10 +532,10 @@ NormFile::Offset NormPrecodeApp::ComputeSegmentOffset(NormFile::Offset interleav
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
NormFile::Offset interleaverRow = interleaverId / interleaverWidth;
|
ProtoFile::Offset interleaverRow = interleaverId / interleaverWidth;
|
||||||
NormFile::Offset interleaverCol = interleaverId % interleaverWidth;
|
ProtoFile::Offset interleaverCol = interleaverId % interleaverWidth;
|
||||||
|
|
||||||
NormFile::Offset segmentId = interleaverCol * interleaverHeight + interleaverRow;
|
ProtoFile::Offset segmentId = interleaverCol * interleaverHeight + interleaverRow;
|
||||||
if (0 != blockId)
|
if (0 != blockId)
|
||||||
segmentId += (blockId * interleaver_size);
|
segmentId += (blockId * interleaver_size);
|
||||||
|
|
||||||
|
|
@ -473,26 +543,26 @@ NormFile::Offset NormPrecodeApp::ComputeSegmentOffset(NormFile::Offset interleav
|
||||||
if (segmentId >= numSegments)
|
if (segmentId >= numSegments)
|
||||||
{
|
{
|
||||||
// It was a "hole", so find its hole delta
|
// It was a "hole", so find its hole delta
|
||||||
NormFile::Offset lastSegmentId = numSegments - 1;
|
ProtoFile::Offset lastSegmentId = numSegments - 1;
|
||||||
if (0 != blockId)
|
if (0 != blockId)
|
||||||
{
|
{
|
||||||
segmentId = segmentId % interleaverSize;
|
segmentId = segmentId % interleaverSize;
|
||||||
lastSegmentId = lastSegmentId % interleaverSize;
|
lastSegmentId = lastSegmentId % interleaverSize;
|
||||||
}
|
}
|
||||||
// Here maxRow/maxCol are wr2 _interleaved_ position of lastSegmentId
|
// Here maxRow/maxCol are wr2 _interleaved_ position of lastSegmentId
|
||||||
NormFile::Offset maxCol = lastSegmentId / interleaverHeight;
|
ProtoFile::Offset maxCol = lastSegmentId / interleaverHeight;
|
||||||
NormFile::Offset maxRow = lastSegmentId % interleaverHeight;
|
ProtoFile::Offset maxRow = lastSegmentId % interleaverHeight;
|
||||||
// AS above, this assertion _should_ be true if we're "square" enough
|
// AS above, this assertion _should_ be true if we're "square" enough
|
||||||
// (it does break when interleaver width is much greater than height,
|
// (it does break when interleaver width is much greater than height,
|
||||||
// so, some day (TBD) we may want to generalize this remapping trick more???
|
// so, some day (TBD) we may want to generalize this remapping trick more???
|
||||||
ASSERT(interleaverCol >= maxCol);
|
ASSERT(interleaverCol >= maxCol);
|
||||||
NormFile::Offset delta = (interleaverCol - maxCol)*(maxRow+1) + interleaverRow - maxRow;
|
ProtoFile::Offset delta = (interleaverCol - maxCol)*(maxRow+1) + interleaverRow - maxRow;
|
||||||
|
|
||||||
// Then, remap "delta" to find _original_ "hole" position (corrected interleaver position)
|
// Then, remap "delta" to find _original_ "hole" position (corrected interleaver position)
|
||||||
// Here maxRow/maxCol are wr2 _source_ position of lastSegmentId
|
// Here maxRow/maxCol are wr2 _source_ position of lastSegmentId
|
||||||
maxRow = lastSegmentId / interleaverWidth;
|
maxRow = lastSegmentId / interleaverWidth;
|
||||||
maxCol = lastSegmentId % interleaverWidth;
|
maxCol = lastSegmentId % interleaverWidth;
|
||||||
NormFile::Offset emptyRows = interleaverHeight - maxRow - 1;
|
ProtoFile::Offset emptyRows = interleaverHeight - maxRow - 1;
|
||||||
if (delta <= (emptyRows*(maxRow + 1)))
|
if (delta <= (emptyRows*(maxRow + 1)))
|
||||||
{
|
{
|
||||||
// in first area
|
// in first area
|
||||||
|
|
@ -510,7 +580,7 @@ NormFile::Offset NormPrecodeApp::ComputeSegmentOffset(NormFile::Offset interleav
|
||||||
if (0 != blockId) segmentId += (blockId * interleaver_size);
|
if (0 != blockId) segmentId += (blockId * interleaver_size);
|
||||||
ASSERT(segmentId < numSegments);
|
ASSERT(segmentId < numSegments);
|
||||||
}
|
}
|
||||||
NormFile::Offset segmentOffset = segment_size * segmentId;
|
ProtoFile::Offset segmentOffset = segment_size * segmentId;
|
||||||
return segmentOffset;
|
return segmentOffset;
|
||||||
} // end NormPrecodeApp::ComputeSegmentOffset()
|
} // end NormPrecodeApp::ComputeSegmentOffset()
|
||||||
|
|
||||||
|
|
@ -544,35 +614,43 @@ bool NormPrecodeApp::Encode()
|
||||||
struct timeval t1, t2;
|
struct timeval t1, t2;
|
||||||
ProtoSystemTime(t1);
|
ProtoSystemTime(t1);
|
||||||
|
|
||||||
NormFile::Offset fileSize = in_file.GetSize();
|
ProtoFile::Offset fileSize = in_file.GetSize();
|
||||||
|
|
||||||
// We reserve 4 bytes for our CRC (used to detect erasures)
|
// We reserve 4 bytes for our CRC (used to detect erasures)
|
||||||
unsigned int dataSegmentSize = segment_size - 4;
|
unsigned int dataSegmentSize = segment_size - 4;
|
||||||
|
|
||||||
NormFile::Offset numInputSegments = 1 + fileSize / dataSegmentSize;
|
ProtoFile::Offset numInputSegments = 1 + fileSize / dataSegmentSize;
|
||||||
unsigned int lastFecSegSize = (unsigned int)(fileSize % dataSegmentSize);
|
unsigned int lastFecSegSize = (unsigned int)(fileSize % dataSegmentSize);
|
||||||
|
|
||||||
if (0 != lastFecSegSize) numInputSegments++;
|
if (0 != lastFecSegSize)
|
||||||
|
numInputSegments++;
|
||||||
|
else
|
||||||
|
lastFecSegSize = dataSegmentSize;
|
||||||
|
|
||||||
// Calculate FEC block size(s)
|
// Calculate FEC block size(s)
|
||||||
NormFile::Offset numBlocks = numInputSegments / num_data;
|
ProtoFile::Offset numBlocks = numInputSegments / num_data;
|
||||||
unsigned int fecBlockSize = num_data;
|
unsigned int fecBlockSize = num_data;
|
||||||
unsigned int lastBlockSize = (unsigned int)(numInputSegments % num_data);
|
unsigned int lastBlockSize = (unsigned int)(numInputSegments % num_data);
|
||||||
if (0 != lastBlockSize) numBlocks++;
|
if (0 != lastBlockSize)
|
||||||
NormFile::Offset lastBlockId = numBlocks - 1;
|
numBlocks++;
|
||||||
|
else
|
||||||
|
lastBlockSize = num_data;
|
||||||
|
ProtoFile::Offset lastBlockId = numBlocks - 1;
|
||||||
|
|
||||||
// 0) Calculate "out_file" size and determine interleaver width and height
|
// 0) Calculate "out_file" size and determine interleaver width and height
|
||||||
NormFile::Offset numOutputSegments =
|
ProtoFile::Offset numOutputSegments =
|
||||||
((numBlocks - 1) * (fecBlockSize + num_parity)) + lastBlockSize + num_parity;
|
((numBlocks - 1) * (fecBlockSize + num_parity)) + lastBlockSize + num_parity;
|
||||||
|
|
||||||
|
|
||||||
InitInterleaver(numOutputSegments);
|
InitInterleaver(numOutputSegments);
|
||||||
// 1) Init our FEC encoder
|
// 1) Init our FEC encoder
|
||||||
#ifdef USE_MDP_FEC
|
NormEncoder* encoderPtr;
|
||||||
NormEncoderMDP encoder;
|
if ((num_data + num_parity) > 256)
|
||||||
#else
|
encoderPtr = new NormEncoderRS16;
|
||||||
NormEncoderRS8 encoder;
|
else
|
||||||
#endif // if/else USE_MDP_FEC
|
encoderPtr = new NormEncoderRS8;
|
||||||
|
NormEncoder& encoder = *encoderPtr;
|
||||||
|
|
||||||
if (!encoder.Init(num_data, num_parity, dataSegmentSize)) // 4 CRC bytes are _not_ encoded
|
if (!encoder.Init(num_data, num_parity, dataSegmentSize)) // 4 CRC bytes are _not_ encoded
|
||||||
{
|
{
|
||||||
PLOG(PL_FATAL, "npc: error initializing FEC encoder\n");
|
PLOG(PL_FATAL, "npc: error initializing FEC encoder\n");
|
||||||
|
|
@ -581,7 +659,7 @@ bool NormPrecodeApp::Encode()
|
||||||
|
|
||||||
// Determine number of segments to allocate for FEC encoding and
|
// Determine number of segments to allocate for FEC encoding and
|
||||||
// interleaver buffering if applicable
|
// interleaver buffering if applicable
|
||||||
NormFile::Offset interleaverBytes = interleaver_size * segment_size;
|
ProtoFile::Offset interleaverBytes = interleaver_size * segment_size;
|
||||||
char* iBuffer = NULL;
|
char* iBuffer = NULL;
|
||||||
bool useBuffering = false;
|
bool useBuffering = false;
|
||||||
if (interleaverBytes <= i_buffer_max)
|
if (interleaverBytes <= i_buffer_max)
|
||||||
|
|
@ -625,20 +703,20 @@ bool NormPrecodeApp::Encode()
|
||||||
// (TBD) This could be built directly into iBuffer segment zero
|
// (TBD) This could be built directly into iBuffer segment zero
|
||||||
char metaData[SEGMENT_MAX+4];
|
char metaData[SEGMENT_MAX+4];
|
||||||
memset(metaData, 0, SEGMENT_MAX);
|
memset(metaData, 0, SEGMENT_MAX);
|
||||||
NormFile::Offset sz = fileSize;
|
ProtoFile::Offset sz = fileSize;
|
||||||
if (sizeof(NormFile::Offset) == 8)
|
if (sizeof(ProtoFile::Offset) == 8)
|
||||||
{
|
{
|
||||||
sz = htono(fileSize);
|
sz = htono(fileSize);
|
||||||
memcpy(metaData, &sz, 8);
|
memcpy(metaData, &sz, 8);
|
||||||
}
|
}
|
||||||
else if (sizeof(NormFile::Offset) == 4)
|
else if (sizeof(ProtoFile::Offset) == 4)
|
||||||
{
|
{
|
||||||
sz = htonl((UINT32)sz);
|
sz = htonl((UINT32)sz);
|
||||||
memcpy(metaData + 4, &sz, 4);
|
memcpy(metaData + 4, &sz, 4);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
PLOG(PL_FATAL, "npc: error: unsupported file offset size (%d bytes)\n", sizeof(NormFile::Offset));
|
PLOG(PL_FATAL, "npc: error: unsupported file offset size (%d bytes)\n", sizeof(ProtoFile::Offset));
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
// put in_file_path file name portion into middle section of "metaData"
|
// put in_file_path file name portion into middle section of "metaData"
|
||||||
|
|
@ -653,24 +731,23 @@ bool NormPrecodeApp::Encode()
|
||||||
// 2) Read "in_file" segments, encode, and output to "out_file"
|
// 2) Read "in_file" segments, encode, and output to "out_file"
|
||||||
PLOG(PL_ALWAYS, "npc: encoding file ... (progress: 0%%)");
|
PLOG(PL_ALWAYS, "npc: encoding file ... (progress: 0%%)");
|
||||||
// State to track/display encoding progress
|
// State to track/display encoding progress
|
||||||
NormFile::Offset progressThreshold = numOutputSegments / 100;
|
ProtoFile::Offset progressThreshold = numInputSegments / 100;
|
||||||
double progressIncrement = 100.0;
|
if (progressThreshold < 1) progressThreshold = 1;
|
||||||
if (progressThreshold > 1)
|
ProtoFile::Offset progressCounter = 0;
|
||||||
progressIncrement = (double)numOutputSegments / (double)progressThreshold;
|
|
||||||
else
|
|
||||||
progressThreshold = numOutputSegments; // small number of segments
|
|
||||||
NormFile::Offset progressCounter = 0;
|
|
||||||
int progressPercent = 0;
|
int progressPercent = 0;
|
||||||
|
|
||||||
|
|
||||||
NormFile::Offset blockId = 0;
|
ProtoFile::Offset blockId = 0;
|
||||||
unsigned int parityCount = 0;
|
unsigned int parityCount = 0;
|
||||||
bool parityReady = false;
|
bool parityReady = false;
|
||||||
NormFile::Offset inputSegmentId = 0;
|
ProtoFile::Offset inputSegmentId = 0;
|
||||||
NormFile::Offset outputSegmentId = 0;
|
ProtoFile::Offset outputSegmentId = 0;
|
||||||
while (outputSegmentId < numOutputSegments)
|
while (outputSegmentId < numOutputSegments)
|
||||||
{
|
{
|
||||||
NormFile::Offset interleaverOffset = ComputeInterleaverOffset(outputSegmentId, numOutputSegments);
|
// The ComputerInterleaverOffset() call here retrieves the _output_ offset location that the
|
||||||
|
// current input segment will be mapped to. I.e., data is read from the input order, but mapped
|
||||||
|
// to interleaved output position via this offset
|
||||||
|
ProtoFile::Offset interleaverOffset = ComputeInterleaverOffset(outputSegmentId, numOutputSegments);
|
||||||
char* segment = useBuffering ? (iBuffer + (interleaverOffset % interleaverBytes)) : iBuffer;
|
char* segment = useBuffering ? (iBuffer + (interleaverOffset % interleaverBytes)) : iBuffer;
|
||||||
if (parityReady)
|
if (parityReady)
|
||||||
{
|
{
|
||||||
|
|
@ -696,25 +773,30 @@ bool NormPrecodeApp::Encode()
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
// B) Read in data portion of next "segment"
|
// B) Read in data portion of next "segment"
|
||||||
unsigned int bytesToRead;
|
unsigned int expectedBytes;
|
||||||
if (inputSegmentId != numInputSegments)
|
if (inputSegmentId != numInputSegments)
|
||||||
{
|
{
|
||||||
bytesToRead = dataSegmentSize;
|
expectedBytes = dataSegmentSize;
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
memset(segment, 0, dataSegmentSize);
|
memset(segment, 0, dataSegmentSize);
|
||||||
bytesToRead = lastFecSegSize;
|
expectedBytes = lastFecSegSize;
|
||||||
}
|
}
|
||||||
if (in_file.Read(segment, bytesToRead) != bytesToRead)
|
unsigned int bytesToRead = expectedBytes;
|
||||||
|
if (!in_file.Read(segment, bytesToRead))
|
||||||
{
|
{
|
||||||
PLOG(PL_FATAL, "\nnpc: unexpected error reading input file: %s\n", GetErrorString());
|
PLOG(PL_FATAL, "\nnpc: unexpected error reading input file: %s\n", GetErrorString());
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
if (bytesToRead != expectedBytes)
|
||||||
|
{
|
||||||
|
PLOG(PL_FATAL, "\nnpc: unexpected end-of-file: %s\n", GetErrorString());
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if (0 != bytesToRead) segment[bytesToRead] = '\0';
|
||||||
}
|
}
|
||||||
// C) Encode and check for parity readiness
|
// C) Encode and check for parity readiness
|
||||||
//TRACE("outputSegmentId:%lu\n", outputSegmentId);
|
|
||||||
|
|
||||||
encoder.Encode(outputSegmentId % fecBlockSize, segment, parityVec);
|
encoder.Encode(outputSegmentId % fecBlockSize, segment, parityVec);
|
||||||
unsigned int numData = (blockId != lastBlockId) ? fecBlockSize : lastBlockSize;
|
unsigned int numData = (blockId != lastBlockId) ? fecBlockSize : lastBlockSize;
|
||||||
if (numData == ++parityCount)
|
if (numData == ++parityCount)
|
||||||
|
|
@ -722,20 +804,29 @@ bool NormPrecodeApp::Encode()
|
||||||
parityCount = num_parity;
|
parityCount = num_parity;
|
||||||
parityReady = true;
|
parityReady = true;
|
||||||
}
|
}
|
||||||
|
if (++progressCounter >= progressThreshold)
|
||||||
|
{
|
||||||
|
progressPercent = int((double)inputSegmentId / (double)numInputSegments);
|
||||||
|
if (progressPercent < 9)
|
||||||
|
PLOG(PL_ALWAYS, "\b\b\b%d%%)", progressPercent + 1);
|
||||||
|
else if (progressPercent < 99)
|
||||||
|
PLOG(PL_ALWAYS, "\b\b\b\b%d%%)", progressPercent + 1);
|
||||||
|
if (progressPercent < 99) progressPercent++;
|
||||||
|
progressCounter = 0;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
// E) Calculate and add CRC32 checksum to each "segment"
|
// E) Calculate and add CRC32 checksum to each "segment"
|
||||||
UINT32 checksum = ComputeCRC32(segment, dataSegmentSize);
|
UINT32 checksum = ComputeCRC32(segment, dataSegmentSize);
|
||||||
checksum = htonl(checksum);
|
checksum = htonl(checksum);
|
||||||
memcpy(segment+dataSegmentSize, &checksum, 4);
|
memcpy(segment+dataSegmentSize, &checksum, 4);
|
||||||
|
|
||||||
if (useBuffering)
|
if (useBuffering)
|
||||||
{
|
{
|
||||||
|
|
||||||
outputSegmentId++;
|
outputSegmentId++;
|
||||||
if ((0 == (outputSegmentId % interleaver_size)) || (outputSegmentId == numOutputSegments))
|
if ((0 == (outputSegmentId % interleaver_size)) || (outputSegmentId == numOutputSegments))
|
||||||
{
|
{
|
||||||
// Output our buffered interleaver block from memory (iBuffer) to "out_file"
|
// Output our buffered interleaver block from memory (iBuffer) to "out_file"
|
||||||
NormFile::Offset bytesToWrite;
|
ProtoFile::Offset bytesToWrite;
|
||||||
if ((outputSegmentId != numOutputSegments) || (numOutputSegments == interleaver_size))
|
if ((outputSegmentId != numOutputSegments) || (numOutputSegments == interleaver_size))
|
||||||
bytesToWrite = interleaver_size;
|
bytesToWrite = interleaver_size;
|
||||||
else
|
else
|
||||||
|
|
@ -764,23 +855,12 @@ bool NormPrecodeApp::Encode()
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
outputSegmentId++;
|
outputSegmentId++;
|
||||||
|
}
|
||||||
}
|
|
||||||
if (++progressCounter >= progressThreshold)
|
|
||||||
{
|
|
||||||
if (progressPercent < 9)
|
|
||||||
PLOG(PL_ALWAYS, "\b\b\b%d%%)", progressPercent + 1);
|
|
||||||
else if (progressPercent < 99)
|
|
||||||
PLOG(PL_ALWAYS, "\b\b\b\b%d%%)", progressPercent + 1);
|
|
||||||
if (progressPercent < 99) progressPercent++;
|
|
||||||
progressCounter = 0;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
if (progressPercent < 10)
|
if (progressPercent < 10)
|
||||||
PLOG(PL_ALWAYS, "\b\b\b100%%)\n");
|
PLOG(PL_ALWAYS, "\b\b\b100%%)\n");
|
||||||
else
|
else
|
||||||
PLOG(PL_ALWAYS, "\b\b\b\b100%%)\n");
|
PLOG(PL_ALWAYS, "\b\b\b\b100%%)\n");
|
||||||
|
|
||||||
in_file.Close();
|
in_file.Close();
|
||||||
out_file.Close();
|
out_file.Close();
|
||||||
|
|
||||||
|
|
@ -801,15 +881,15 @@ bool NormPrecodeApp::Encode()
|
||||||
bool NormPrecodeApp::Decode()
|
bool NormPrecodeApp::Decode()
|
||||||
{
|
{
|
||||||
// 1) Determine file size and init interleaving
|
// 1) Determine file size and init interleaving
|
||||||
NormFile::Offset inputFileSize = in_file.GetSize();
|
ProtoFile::Offset inputFileSize = in_file.GetSize();
|
||||||
NormFile::Offset numInputSegments = inputFileSize / segment_size;
|
ProtoFile::Offset numInputSegments = inputFileSize / segment_size;
|
||||||
if (0 != (inputFileSize % segment_size))
|
if (0 != (inputFileSize % segment_size))
|
||||||
{
|
{
|
||||||
PLOG(PL_FATAL, "npc: error: input file size not integral number of given <segmentSize>\n");
|
PLOG(PL_FATAL, "npc: error: input file size not integral number of given <segmentSize>\n");
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
// Reverse calculate the FEC blocking
|
// Reverse calculate the FEC blocking
|
||||||
NormFile::Offset numFecBlocks = numInputSegments / (num_data + num_parity);
|
ProtoFile::Offset numFecBlocks = numInputSegments / (num_data + num_parity);
|
||||||
unsigned int fecBlockSize = num_data;
|
unsigned int fecBlockSize = num_data;
|
||||||
unsigned int lastFecBlockSize = (unsigned int)(numInputSegments % (num_data + num_parity));
|
unsigned int lastFecBlockSize = (unsigned int)(numInputSegments % (num_data + num_parity));
|
||||||
if (0 != lastFecBlockSize)
|
if (0 != lastFecBlockSize)
|
||||||
|
|
@ -818,17 +898,23 @@ bool NormPrecodeApp::Decode()
|
||||||
lastFecBlockSize -= num_parity;
|
lastFecBlockSize -= num_parity;
|
||||||
numFecBlocks++;
|
numFecBlocks++;
|
||||||
}
|
}
|
||||||
NormFile::Offset lastFecBlockId = numFecBlocks - 1;
|
else
|
||||||
|
{
|
||||||
|
lastFecBlockSize = num_data;
|
||||||
|
}
|
||||||
|
ProtoFile::Offset lastFecBlockId = numFecBlocks - 1;
|
||||||
// Calculate interleaver dimensions from file size
|
// Calculate interleaver dimensions from file size
|
||||||
// set "interleaver_size", etc
|
// set "interleaver_size", etc
|
||||||
InitInterleaver(numInputSegments);
|
InitInterleaver(numInputSegments);
|
||||||
|
|
||||||
// 2) init FEC decoder
|
// 2) init FEC decoder
|
||||||
#ifdef USE_MDP_FEC
|
NormDecoder* decoderPtr;
|
||||||
NormDecoderMDP decoder;
|
if ((num_data + num_parity) > 256)
|
||||||
#else
|
decoderPtr = new NormDecoderRS16;
|
||||||
NormDecoderRS8 decoder;
|
else
|
||||||
#endif // if/else USE_MDP_FEC
|
decoderPtr = new NormDecoderRS8;
|
||||||
|
NormDecoder& decoder = *decoderPtr;
|
||||||
|
|
||||||
unsigned int dataSegmentSize = segment_size - 4; // leaves space for our CRC
|
unsigned int dataSegmentSize = segment_size - 4; // leaves space for our CRC
|
||||||
if (!decoder.Init(num_data, num_parity, dataSegmentSize))
|
if (!decoder.Init(num_data, num_parity, dataSegmentSize))
|
||||||
{
|
{
|
||||||
|
|
@ -842,7 +928,7 @@ bool NormPrecodeApp::Decode()
|
||||||
// interleaver buffering if applicable
|
// interleaver buffering if applicable
|
||||||
char* iBuffer = NULL;
|
char* iBuffer = NULL;
|
||||||
bool useBuffering = false;
|
bool useBuffering = false;
|
||||||
NormFile::Offset interleaverBytes = interleaver_size * segment_size;
|
ProtoFile::Offset interleaverBytes = interleaver_size * segment_size;
|
||||||
|
|
||||||
if ((interleaverBytes <= i_buffer_max))// &&
|
if ((interleaverBytes <= i_buffer_max))// &&
|
||||||
//((num_data + num_parity) <= interleaver_size) &&
|
//((num_data + num_parity) <= interleaver_size) &&
|
||||||
|
|
@ -893,32 +979,29 @@ bool NormPrecodeApp::Decode()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
PLOG(PL_FATAL, "npc: decoding file ... (progress: 0%%)");
|
PLOG(PL_ALWAYS, "npc: decoding file ... (progress: 0%%)");
|
||||||
// State to track/display decoding progress
|
// State to track/display decoding progress
|
||||||
NormFile::Offset progressThreshold = numInputSegments / 100;
|
ProtoFile::Offset progressThreshold = numInputSegments / 100;
|
||||||
double progressIncrement = 100.0;
|
if (progressThreshold <= 1)
|
||||||
if (progressThreshold > 1)
|
progressThreshold = numInputSegments; // small number of segments
|
||||||
progressIncrement = (double)numInputSegments / (double)progressThreshold;
|
ProtoFile::Offset progressCounter = 0;
|
||||||
else
|
|
||||||
progressThreshold = numInputSegments; // small number of segments
|
|
||||||
NormFile::Offset progressCounter = 0;
|
|
||||||
int progressPercent = 0;
|
int progressPercent = 0;
|
||||||
|
|
||||||
|
|
||||||
// Read and decode each block in "in_file"
|
// Read and decode each block in "in_file"
|
||||||
NormFile::Offset fecBlockId = 0;
|
ProtoFile::Offset fecBlockId = 0;
|
||||||
NormFile::Offset outFileSize = 0;
|
ProtoFile::Offset outFileSize = 0;
|
||||||
|
|
||||||
NormFile::Offset lastInterleaverBlockId = numInputSegments / interleaver_size;
|
ProtoFile::Offset lastInterleaverBlockId = numInputSegments / interleaver_size;
|
||||||
NormFile::Offset lastInterleaverBytes = (numInputSegments % interleaver_size) * segment_size;
|
ProtoFile::Offset lastInterleaverBytes = (numInputSegments % interleaver_size) * segment_size;
|
||||||
NormFile::Offset interleaverBlockId = 0;
|
ProtoFile::Offset interleaverBlockId = 0;
|
||||||
|
|
||||||
unsigned int erasureCount = 0;
|
unsigned int erasureCount = 0;
|
||||||
unsigned int segmentCount = 0;
|
unsigned int segmentCount = 0;
|
||||||
|
|
||||||
enum State {READING, ADVANCING, DECODING};
|
enum State {READING, ADVANCING, DECODING};
|
||||||
State state = READING;
|
State state = READING;
|
||||||
NormFile::Offset inputSegmentId = 0;
|
ProtoFile::Offset inputSegmentId = 0;
|
||||||
while ((inputSegmentId < numInputSegments) || (DECODING == state))
|
while ((inputSegmentId < numInputSegments) || (DECODING == state))
|
||||||
{
|
{
|
||||||
switch (state)
|
switch (state)
|
||||||
|
|
@ -927,15 +1010,21 @@ bool NormPrecodeApp::Decode()
|
||||||
if (useBuffering)
|
if (useBuffering)
|
||||||
{
|
{
|
||||||
// Read in a full interleaver block
|
// Read in a full interleaver block
|
||||||
NormFile::Offset bytesToRead;
|
size_t bytesToRead;
|
||||||
if (interleaverBlockId != lastInterleaverBlockId)
|
if (interleaverBlockId != lastInterleaverBlockId)
|
||||||
bytesToRead = interleaverBytes;
|
bytesToRead = interleaverBytes;
|
||||||
else
|
else
|
||||||
bytesToRead = lastInterleaverBytes;
|
bytesToRead = lastInterleaverBytes;
|
||||||
if (in_file.Read(iBuffer, bytesToRead) != bytesToRead)
|
|
||||||
|
if (!in_file.Read(iBuffer, bytesToRead))
|
||||||
{
|
{
|
||||||
PLOG(PL_FATAL, "\nnpc: error reading input file: %s\n", GetErrorString());
|
PLOG(PL_FATAL, "\nnpc: error reading input file: %s\n", GetErrorString());
|
||||||
return false;
|
return false;
|
||||||
|
}
|
||||||
|
if (0 == bytesToRead)
|
||||||
|
{
|
||||||
|
PLOG(PL_FATAL, "\nnpc: error reading input file: unexpected end-of-file\n");
|
||||||
|
return false;
|
||||||
}
|
}
|
||||||
interleaverBlockId++;
|
interleaverBlockId++;
|
||||||
state = ADVANCING;
|
state = ADVANCING;
|
||||||
|
|
@ -947,7 +1036,7 @@ bool NormPrecodeApp::Decode()
|
||||||
for (unsigned int i = 0 ; i < (numData + num_parity); i++)
|
for (unsigned int i = 0 ; i < (numData + num_parity); i++)
|
||||||
{
|
{
|
||||||
// Calc offset (de-interleave) and seek
|
// Calc offset (de-interleave) and seek
|
||||||
NormFile::Offset interleaverOffset = ComputeInterleaverOffset(inputSegmentId, numInputSegments);
|
ProtoFile::Offset interleaverOffset = ComputeInterleaverOffset(inputSegmentId, numInputSegments);
|
||||||
// seek to interleaver offset
|
// seek to interleaver offset
|
||||||
if (!in_file.Seek(interleaverOffset))
|
if (!in_file.Seek(interleaverOffset))
|
||||||
{
|
{
|
||||||
|
|
@ -955,11 +1044,17 @@ bool NormPrecodeApp::Decode()
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
// Read segment
|
// Read segment
|
||||||
if (in_file.Read(fecVec[i], segment_size) != segment_size)
|
unsigned int bytesToRead = segment_size;
|
||||||
|
if (!in_file.Read(fecVec[i], bytesToRead))
|
||||||
{
|
{
|
||||||
PLOG(PL_FATAL, "\nnpc: unexpected error reading input file: %s\n", GetErrorString());
|
PLOG(PL_FATAL, "\nnpc: unexpected error reading input file: %s\n", GetErrorString());
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
if (bytesToRead != segment_size)
|
||||||
|
{
|
||||||
|
PLOG(PL_FATAL, "\nnpc: read() error: incomplete segment (len: %lu out of %lu bytes)\n", bytesToRead, segment_size);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
inputSegmentId++;
|
inputSegmentId++;
|
||||||
|
|
||||||
// Validate checksum (detects errors/ erasures)
|
// Validate checksum (detects errors/ erasures)
|
||||||
|
|
@ -988,7 +1083,7 @@ bool NormPrecodeApp::Decode()
|
||||||
unsigned int priorSegmentCount = segmentCount;
|
unsigned int priorSegmentCount = segmentCount;
|
||||||
for (; segmentCount < (numData + num_parity); segmentCount++)
|
for (; segmentCount < (numData + num_parity); segmentCount++)
|
||||||
{
|
{
|
||||||
NormFile::Offset interleaverOffset = ComputeInterleaverOffset(inputSegmentId, numInputSegments);
|
ProtoFile::Offset interleaverOffset = ComputeInterleaverOffset(inputSegmentId, numInputSegments);
|
||||||
fecVec[segmentCount] = iBuffer + (interleaverOffset % interleaverBytes);
|
fecVec[segmentCount] = iBuffer + (interleaverOffset % interleaverBytes);
|
||||||
inputSegmentId++;
|
inputSegmentId++;
|
||||||
// Validate checksum (detects errors/ erasures)
|
// Validate checksum (detects errors/ erasures)
|
||||||
|
|
@ -1045,7 +1140,7 @@ bool NormPrecodeApp::Decode()
|
||||||
if((0 == fecBlockId) && (0 == i))
|
if((0 == fecBlockId) && (0 == i))
|
||||||
{
|
{
|
||||||
// First segment of first block is our "meta_data" with file size info
|
// First segment of first block is our "meta_data" with file size info
|
||||||
switch (sizeof(NormFile::Offset))
|
switch (sizeof(ProtoFile::Offset))
|
||||||
{
|
{
|
||||||
case 8:
|
case 8:
|
||||||
memcpy(&outFileSize, fecVec[0], 8);
|
memcpy(&outFileSize, fecVec[0], 8);
|
||||||
|
|
@ -1078,6 +1173,7 @@ bool NormPrecodeApp::Decode()
|
||||||
{
|
{
|
||||||
// Last segment, so calculate "lastSegmentSize"
|
// Last segment, so calculate "lastSegmentSize"
|
||||||
segmentSize = (unsigned int)(outFileSize % segmentSize);
|
segmentSize = (unsigned int)(outFileSize % segmentSize);
|
||||||
|
if (0 == segmentSize) segmentSize = segment_size - 4;
|
||||||
}
|
}
|
||||||
if (out_file.Write(fecVec[i], segmentSize) != segmentSize)
|
if (out_file.Write(fecVec[i], segmentSize) != segmentSize)
|
||||||
{
|
{
|
||||||
|
|
@ -1120,8 +1216,6 @@ bool NormPrecodeApp::Decode()
|
||||||
} // end NormPrecodeApp::Decode()
|
} // end NormPrecodeApp::Decode()
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
/*****************************************************************/
|
/*****************************************************************/
|
||||||
/* */
|
/* */
|
||||||
/* CRC LOOKUP TABLE */
|
/* CRC LOOKUP TABLE */
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue