pjkundert / ezpwd-reed-solomon

Reed-Solomon & BCH encoding and decoding, in C++, Javascript & Python
https://hardconsulting.com/products/13-reed-solomon
Other
99 stars 21 forks source link

Lots of Undeclared Identifier Errors after calling encode function #2

Closed mushahidh closed 8 years ago

mushahidh commented 8 years ago

I want to use ezpwd RS code to protect video stream from packet loss. However, using the library in Visual studio and even in Qt gives me lot of undeclared identifier errors when i just call ezpwd::RS<255,251> rs; in my code.like```

 Error  13  error C2065: 'b' : undeclared identifier    d:\jm\bin\coyrights\coyrights\ezpwd\rs  948 1   coyrights
Error   20  error C2065: 'int_nroots' : undeclared identifier   d:\jm\bin\coyrights\coyrights\ezpwd\rs  951 1   coyrights
Error   26  error C2065: 'int_nroots' : undeclared identifier   d:\jm\bin\coyrights\coyrights\ezpwd\rs  953 1   coyrights
pjkundert commented 8 years ago

I don't use Windows much, so I haven't really tested it under Visual Studio. It is heavily tested under Clang and GNU compilers, with full warnings.

Are you sure that you have enabled C++11 compatibility mode for the compiler?

mushahidh commented 8 years ago

@pjkundert This was the underlying problem . Thanks for the info. Could you please tell me how to see recovered data after decoding? It only shows how much errors are corrected. Secondly, Is ezpwd rs codes are good to use with video data when transmitted over unreliable network? Need your kind opinion on this too.

pjkundert commented 8 years ago

If it returns a 0 or other positive number, the data were corrected -- and the number of errors corrected is reported. A -'ve value (or exception) indicates that the data were unrecoverable; a valid Reed-Solomon Codeword could not be recovered.

If you are sending data over an unreliable network -- I presume an IP network, probably UDP/IP protocol -- it will be delivered in chunks of whatever IP message size you send. The message itself is unlikely to have errors, due to the IP packet CRC.

So, you must recover missing packets. Thus, any code must cover multiple packets of data. For example, for every 15 packets , you could send a parity packet, allowing you to recover any one missing packet.

Of course, this means that you may need to delay your video stream on the receiving side by up to 14 packets, in order to recover the missing one and play them in order. It is often better just to drop the missing frame, rather than implement a "jitter" buffer, and delay your video by the required number of packets to implement your loss recovery system.

Anyway, that's one idea.

mushahidh commented 8 years ago

@pjkundert Thanks again. You said it returns number of corrected errors. But How to see corrected data after-that? You are very much true about decoding delay. But that's the mystery i want to solve i.e increasing the block size would enhance error correcting performance on the other hand it causes a decoding delay which is not tolerable by real time applications. Could you advice me about how Ezpwd RS codes could be implemented on frame packet levels. So for instance i have one frame consist of 7 RTP packets and i want to use RS(7,5) so that after every 5 packets 2 parity packets will be added. I want to cope with packet loss problem. So out of 5 packet if 1 or 2 packet get lost it could be recovered on receiver end. But for my understanding RS codes get number of bytes. So if one packet consist of 500 bytes how it would be represented on packet level. This thing is confusing me too much.
Also I can use RS(7,5) to recover lost packet at earliest to avoid any delay but optimal parameters would be decided after applying some experiments.

mushahidh commented 8 years ago

So in the simplest possible example the output of data variable remains the same after applying decoding. Even though it shows me number of errors corrected 2.

int main(int argc, char** argv) {
    ezpwd::RS<255,251> rs;
std::string data="Hel00000";
rs.encode( data ); 

cout<<data<<endl;
data+="na";
int fixed = rs.decode( data );
cout<< data ;
;

cout<<fixed ;
cout<<data;

    return 0;
}
pjkundert commented 8 years ago

Here is a basic example:

    int                         failures= 0;

    ezpwd::RS<255,253>          rs;             // 255 symbol codeword, up to 253 data == 2 symbols parity
    std::string                 orig    = "Hello, world!";

    // Most basic use of API to correct an error
    std::string                 data    = orig; // working data, copy of orig
    std::cout << "Original:  " << std::vector<uint8_t>( data.begin(), data.end() ) << std::endl;
    rs.encode( data );                          // 13 symbols data + 2 symbols R-S parity added
    std::cout << "Encoded:   " << std::vector<uint8_t>( data.begin(), data.end() ) << std::endl;
    data[3]                             = 'x';  // Corrupt one symbol
    std::cout << "Corrupted: " << std::vector<uint8_t>( data.begin(), data.end() ) << std::endl;
    int                         count   = rs.decode( data );  // Correct any symbols possible
    std::cout << "Corrected: " << std::vector<uint8_t>( data.begin(), data.end() ) << " : " << count << " errors fixed" << std::endl;
    data.resize( data.size() - rs.nroots() );   // Discard added R-S parity symbols
    std::cout << "Restored:  " << std::vector<uint8_t>( data.begin(), data.end() ) << std::endl;
    if ( data != orig ) {                       // Ensure original data is recovered
        failures                       += 1;
        std::cout << "Failed to restore origin data." << std::endl;
    }

When you call rs.encode(), it will take your original data (up to the payload capacity 'rs.load()' of the Reed-Solomon codeword), and add 'rs.nroots()' R-S parity symbols.

When you call rs.decode(), it will correct any symbols in-place, if possible.

After, it is your responsibility to remove the added R-S parity symbols from the end.

The term RS(255,253) indicates the R-S codeword size, 255. For 8-bit symbols, the codeword size is always 255 symbols. 2^N-1, for N-bit symbols. The maximum codeword data capacity is the second number, 253. This means that there are 2 R-S parity symbols: 255-253 == 2. This means that you can correct up to 1 error (symbols that are incorrect, but not indicated as missing), or up to 2 erasures (symbols marked missing).

mushahidh commented 8 years ago

@pjkundert That is great.Sorry to bother you again but if i replace data[3] = 'x'; with data.erase(1,1); The result is -1. Even though it should be able to correct up to 2 erasures and 1 error. So how to cater with completely lost symbol like in this case ?i have tried to enable erasure correction capability in decoder but unable to do that. Actually, in my scenario there will be a erasure correction i.e complete packet loss but i do know the position of packet. Thanks in advance.

pjkundert commented 8 years ago

Nope, you have to encode your data in such a way that you can identify the start/end of a block, and can identify the position of each symbol in your block.

Normally, using Ethernet IP communications, you deal in sending/receiving blocks of data. Thus, you know which blocks you've received and which you have not. Any symbols from a "missing" block of data will be designated as an "erasure". The spot for the symbol there -- you pass a separate std::vector with its position, so that the algorithm knows that you don't know the value; but you know where it is supposed to be.

This is somewhat confusing, I know. To add Reed-Solomon encoding to a single block of Ethernet UDP/IP is not normally useful -- you are going to receive the whole block, almost certainly correct.

What you are trying to do is: receive multiple blocks, each one identifying its ordering in your sequence of data blocks. You want to generate Reed-Solomon codewords that contain symbols from each of N blocks (say, 10 symbols from 24 separate blocks), for a total of (say) 240 symbols. Then, if you add another 10 symbols of Reed-Solomon parity data (for a total of 250 symbols), and send the extra parity packet, you can now recover from one missing block out of every 25. You just collect up the 24 incoming blocks, mark all of the symbols from the 1 missing block as "erasures" in each codeword, and then call rs.decode() on each block.

Hope that makes sense.

mushahidh commented 8 years ago

@pjkundert Very very much thanks for your kind help. You cleared many misconceptions i have. Based on your suggestion i am now starting my implementation. I saw you updated Read me too and you answered my next expected question there.
Again Thanks for your kind attention.

pjkundert commented 8 years ago

I've updated the README.org a bit, with more description of the C++ API, including a bit about passing erasures and positions std::vector containers. However, I'd recommend just looking at the c++/ezpwd/rs... header file, and investigating the code to see what you can pass.

I've always found that this is the best way to really understand an API. Documentation is, necessarily, less valuable than the code, because it always contains less information, and less reliable information (otherwise, it would be the code!) There are a bunch of tests and examples.

Basically, as you collect up the symbols for a codeword, and you find that a symbol is missing (eg. comes from a packet you didn't receive), then just put a 0 (or whatever) in the codeword at that position -- and append the index of that position onto the end of a std::vector called (for example) 'erasures'.

Then, when you finally want to try the R-S decode, you pass the codeword and the erasures vector:

int corrections = rs.decode( codeword, erasures );

mushahidh commented 8 years ago

@pjkundert That's my bad. I have read code thoroughly and it is well written and understandable. Could you please let me know what is the best way of determining N , K values for RS encoder? In my scenario let say i have one encoded video frame with 34 RTP packets in it and each packet contains not more than 400 bytes. But it could be some times 24 bytes per packet as well.And let say i apply random packet loss on it and packet no 4 get lost. In this case how can i make 2 parity packets after every 7 packet so that it can recover missing packet no 4? How will you cater this scenario ?