├── Makefile
├── documentation.html
├── fec.c
├── fec.h
├── feclib.spec
├── fecrecv.c
├── fecrecv.dsp
├── fecsend.c
├── fecsend.dsp
├── fectest.c
├── fectest.dsp
└── index.html
/Makefile:
--------------------------------------------------------------------------------
1 | CPPFLAGS=-O2 -MMD -I /usr/include/g++-3/
2 | LDFLAGS=
3 | ifeq (${OS},Windows_NT)
4 | CYGLIB=/lib/w32api/libwsock32.a
5 | else
6 | CYGLIB=
7 | endif
8 |
9 | all: fecsend fecrecv fectest
10 |
11 | %.d: %.c
12 | $(CC) $(CPPFLAGS) -c -o $*.o $<
13 |
14 | CSOURCES=$(wildcard *.c)
15 |
16 | include $(CSOURCES:.c=.d)
17 |
18 | fecsend: fecsend.o fec.o ${CYGLIB}
19 |
20 | fecrecv: fecrecv.o fec.o ${CYGLIB}
21 |
22 | fectest: fectest.o fec.o
23 |
24 | clean:
25 | rm -rf *~ *.bak *.d DEADJOE `find -name "*.o"` core *.exe \
26 | fectest fecsend fecrecv *.stackdump \
27 | Debug/ Release/ *.dsw *.ncb *.opt *.plg x.fec
28 |
29 | updateweb:
30 | tar cjf - index.html documentation.html | ssh -l nroets \
31 | feclib.sourceforge.net 'cd /home/groups/f/fe/feclib/htdocs; tar xjf -'
32 |
--------------------------------------------------------------------------------
/documentation.html:
--------------------------------------------------------------------------------
1 |
Forward Erasure Correction
2 | Introduction
3 | This code implements Forward Erasure Correction. This means that it will not
4 | locate errors that occured during transmission, but it will regenerate any
5 | packets the have been lost along the way.
6 |
7 | It consists of an encoder and a decoder. The encoder is fed a stream of data
8 | which it breaks up into packets. These packets can then be sent over an
9 | unreliable transport mechanism, typically UDP. The decoder can then
10 | reconstruct lost packets from redundant packets sent by the encoder.
11 |
12 |
Like other implementations, the encoder groups n packets of payload
13 | together and then adds k redundant packets to it. The receiver can only start
14 | to reconstruct any of the lost payload once it has received at least n packets
15 | in total.
16 |
17 |
I wanted everything to be as clean as possible, with no fancy features.
18 | Since it's open source, anyone is free to improve things as they see fit.
19 | Contributions should start with the to do list at the end of this document.
20 |
21 |
My intention was that it can be used in those cases where there is no back
22 | channel e.g. one-way satellite link. So, if the library has been properly
23 | configured, all data will be recovered,
24 | even if many megabytes is lost due to external events like thunder storms.
25 |
26 |
Parameters
27 | This implementation uses band matrices (instead of Van Der Monde matrices)
28 | for improved performance. It is extremely versatile, because it allows you to
29 | set the following parameters :
30 |
31 | - s the size of each packet, excluding headers. (See 5 below).
32 |
- n the number of packets that are grouped together. This determines
33 | latency i.e. the receiver may end up having to wait until the whole group
34 | was received before it has enough data to output the first packet.
35 | Unless you have latency or memory concerns, you may as
36 | well set n to the number of packets you are sending.
37 | (See 3 below)
38 |
- k the number of redundant packets sent for each group.
39 | k / (n + k) should preferrably be much higher than the the error rate of
40 | the transport mechanism on a per packet basis. Note that the k redundant
41 | packets are accessed very frequently, so it is advisible that k * s should
42 | be less than the amount of physical memory (RAM). Also note that as n gets
43 | smaller the chance to loose k of them increases (especially if there are
44 | burst losses), so n should be as large as possible. Usually the k * s
45 | will equal to the maximum expected amount of data that could be lost.
46 |
Maybe someone will write a function that uses probability models to
47 | work out the smallest suitable value of k...
48 |
- w the width of the band in the band matrix. The larger w, the
49 | more processing power is needed. Typically 40 is a good default. In the
50 | unfortunate case that you loose a packet and all 40 redundant packets in
51 | its column, the reconstruction will fail. Fortunely
52 | these 41 packets are pseudo randomly distributed amoung the k + n packets
53 | so the chance of that happening is not great.
54 |
- g The size of the Galois field is 2g. The
55 | computing time increases linearly with g
56 | (unlike other O(1) implementations).
57 | Because a field has only 1 element which does not have
58 | a multiplication inverse, the probability of not being able to find a
59 | spil element when we have k - i rows left to choose from is
60 | 2-g * (k - i). So a good value of g is 2, 3 or 4.
61 | s must be a multiple of g * 4.
62 |
- b the number of bits per second that the encoder should limit the
63 | output to. This prevents network overload. Setting this to 0, implies that
64 | it should send as fast as possible.
65 |
66 |
67 | Functions
68 |
69 | fecEncoder *NewFecEncoder (void *userData,
70 | size_t (*userSend)(void *buf, size_t size, size_t count, void *userData),
71 | char **errorMessage,
72 | int s, int n, int k, int w, int g, int b);
73 |
74 | If it returns NULL, an error has occured. If you passed a non-NULL value in
75 | errorMessage, *errorMessage will point to a string describing to you what
76 | went wrong.
77 |
78 | void FecEncode (fecPayload *buf, fecEncoder *f);
79 |
80 | FecEncode must be called n times (or a multiple thereof), otherwise the
81 | redundant data will not be sent.
82 |
83 | void DeleteFecEncoder (fecEncoder *f);
84 |
85 | typedef struct fecDecoder;
86 |
87 | fecDecoder *NewFecDecoder (void *userData,
88 | void (*userReceive)(void *userData, __int64_t position, fecPayload *buf, int len)
89 | );
90 |
91 | size_t FecDecode (void *buf, size_t size, size_t count,
92 | fecDecoder *f);
93 |
94 | void FlushFecDecoder (fecDecoder *f);
95 |
96 | The error correction works best if the decoder has the largest possible
97 | amount of data. So you should call the flush command to signify that no more
98 | data is expected e.g. after a timeout.
99 |
100 | void DeleteFecDecoder (fecDecoder *f);
101 |
102 |
103 | To Do list
104 |
105 | - Implement sub channels Currently we send a very large header
106 | packet. Because we need it at the receiver, it must be sent with every
107 | packets.
108 |
It consumes less bandwidth to implement a sub channel to send the 20 odd
109 | bytes of control information :
110 | Now the header is e.g. 2 bytes. The first bytes sends would be a simple
111 | modulo 28 counter. The second byte would contain 1 byte of
112 | control data if the first byte is less than 20, and would be a checksum byte
113 | otherwise. This checksum scheme can also use Galois Fields and Gaussian
114 | elimination, but it's parameters must chosen so that it will still function
115 | under the worst possible error rates.
116 |
- Optimizations
117 |
- Packet checksum Let the library send its own checksums incase the
118 | UDP checksum still lets some bad packets get through.
119 |
- Non field rings It is quite possible that there exists some
120 | polynomials that are not primative, and the resultant ring is not a field, but
121 | most of the ring is invertible, and they can be manipulated for even faster
122 | operation. This needs to be investigated.
123 |
- Overflow of i If more than 2 billion packets are send, problem may
124 | occur. We need to fi this.
125 |
126 |
127 | Copyright
128 |
129 | Copyright (c) 2002 Nic Roets. All rights reserved.
130 |
131 | Redistribution and use in source and binary forms, with or without
132 | modification, are permitted provided that the following conditions
133 | are met:
134 |
135 | 1. Redistributions of source code must retain the above copyright
136 | notice, this list of conditions and the following disclaimer.
137 |
138 | 2. Redistributions in binary form must reproduce the above copyright
139 | notice, this list of conditions and the following disclaimer in
140 | the documentation and/or other materials provided with the
141 | distribution.
142 |
143 | 3. All advertising materials mentioning features or use of this
144 | software must display the following acknowledgment:
145 | "This product includes software developed by Nic Roets"
146 |
147 | 4. Redistributions of any form whatsoever must retain the following
148 | acknowledgment:
149 | "This product includes software developed by Nic Roets"
150 |
151 | THIS SOFTWARE IS PROVIDED BY NIC ROETS `AS IS'' AND ANY
152 | EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
153 | IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
154 | PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL NIC ROETS BE LIABLE FOR
155 | ANY DIRECT, INDIRECT, INCIDENTAL,
156 | SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
157 | NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
158 | LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
159 | HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
160 | STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
161 | ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
162 | OF THE POSSIBILITY OF SUCH DAMAGE.
163 |
164 |
--------------------------------------------------------------------------------
/fec.c:
--------------------------------------------------------------------------------
1 | #include
2 | #if defined (WIN32) && !defined (__CYGWIN__)
3 | #include
4 | #include
5 | #else
6 | #include
7 | // in.h defines htonl as a macro whereas winsock uses funcions.
8 | #ifdef __CYGWIN__
9 | #include
10 | #else
11 | #include
12 | #define max(x,y) ((x) > (y) ? (x) : (y))
13 | #define min(x,y) ((x) < (y) ? (x) : (y))
14 | #endif
15 | #endif
16 | #include "fec.h"
17 |
18 | #if defined (WIN32) && !defined (__CYGWIN__)
19 | #define __int32_t __int32
20 | #define __uint32_t unsigned __int32
21 | #endif
22 |
23 | typedef struct headerStruct {
24 | __int32_t kwg, i, n;
25 | } headerType;
26 |
27 | #define KBITS 17
28 | #define WBITS 9
29 | #define GBITS 6
30 | #if KBITS + WBITS + GBITS != 32
31 | #error KBITS + WBITS + GBITS != 32
32 | #endif
33 |
34 | static unsigned poly[]={
35 | 0x1, 0x2, 0x7, 0xb, 0x13, 0x25, 0x43, 0x83, 0x11b, 0x203, 0x409, 0x805,
36 | 0x1009, 0x201b, 0x4021, 0x8003, 0x1002b
37 | }; // See the second part of fectest.c
38 |
39 | #pragma warning(disable:4018)
40 |
41 | static void MAC (int multiplier, fecPayload *source, fecPayload *dest, int g,
42 | int s)
43 | { // Multiply with scalar and then accumulate.
44 | int i, j, k, l;
45 | for (j = 0; j < s / sizeof (*source); j += s / g / sizeof (*source)) {
46 | for (k = multiplier, l = 0; k; k >>= 1, l += s / g / sizeof (*source)) {
47 | if (k & 1) {
48 | for (i = 0; i < s / g / sizeof (*source); i++) {
49 | dest[l + i] ^= source[j + i];
50 | }
51 | }
52 | }
53 | multiplier <<= 1;
54 | if (multiplier >= (1< 16 || (g >> GBITS)) {
66 | if (errorMessage) *errorMessage = "FEC : Illegal Galois field size ";
67 | return NULL;
68 | }
69 | if (k >> KBITS) {
70 | if (errorMessage) *errorMessage = "FEC : k is too large";
71 | return NULL;
72 | }
73 | if (w >> WBITS) {
74 | if (errorMessage) *errorMessage = "FEC : w is too large";
75 | return NULL;
76 | }
77 |
78 | f = (fecEncoder *) malloc (sizeof (*f) + sizeof (*h) + s * (k + 1));
79 | if (!f) {
80 | if (errorMessage) *errorMessage = "FEC : Out of memory";
81 | free (f);
82 | return NULL;
83 | }
84 | f->userData = userData;
85 | f->userSend = userSend;
86 | f->e.s = s;
87 | f->e.k = k;
88 | f->e.w = w;
89 | f->e.g = g;
90 | f->b = b;
91 | f->e.n = n;
92 | f->e.i = 0;
93 | f->lastTime = 0;
94 | h = (headerType*) (s * k + (char*) (f + 1));
95 | h->n = htonl (n);
96 | h->kwg = htonl (k | (w << KBITS) | (g << (KBITS + WBITS)));
97 | memset (f + 1, 0, s * k); // Initialize the redundant packets.
98 | if (errorMessage) *errorMessage = NULL;
99 | return f;
100 | }
101 |
102 | static void AddToRedundant (fecPayload *buf, fecEncDec *e, int i)
103 | { // This is called by both the encoder and the decoder when they process the
104 | // payload. But this code is also repeated where the decoder sets up the
105 | // matrix.
106 | // I suppose a lot of pseudo random stuff can be tried, but in the end nature
107 | // will add its own randomness by way of the packets it destroys.
108 | int row, mid = i * e->w % e->k; // e.g. mid = i * 87654321 % e->k
109 | __uint32_t coef = i + 1;
110 | for (row = max (0, min (mid, e->k - e->w) - e->w);
111 | row < min (e->k, max (mid, e->w) + e->w); row++) {
112 | coef *= 1763689789;
113 | MAC (coef >> (32 - e->g),
114 | buf, (fecPayload*) (row * e->s + (char *)(e + 1)), e->g, e->s);
115 | }
116 | // What actually needs to be investigated is the problems at the edges :
117 | // With "for (row = max (mid - e->w, 0); row < min (mid + e->w, e->k); row++)"
118 | // there are columns with less than 2w non-zero entries which is a weakness.
119 | // Apart for the current solution,
120 | // another solution would be to have row "-1" wrap around into row k - 1 and
121 | // row "k" into row 0 etc. The matrix will not be a band matrix which
122 | // complicates things.
123 | }
124 |
125 | #if !defined(WIN32) && !defined(__CYGWIN__)
126 | static int GetTickCount (void)
127 | {
128 | struct timeval tv;
129 | struct timezone tz;
130 | gettimeofday (&tv, &tz);
131 | return tv.tv_sec * 1000 + tv.tv_usec; // Overflow does not matter.
132 | }
133 | #endif
134 |
135 | static void SendWithDelay (fecPayload *buf, fecEncoder *f)
136 | {
137 | headerType *h = (headerType*)(f->e.k * f->e.s + (char*)(f + 1));
138 | int tick, s = f->e.s + sizeof (*h);
139 |
140 | memcpy (h + 1, buf, f->e.s);
141 | if (f->b > 0) {
142 | tick = GetTickCount ();
143 | if (f->lastTime == 0) f->lastTime = tick;
144 | else {
145 | if (tick - f->lastTime < s * 8000 / f->b)
146 | #if !defined(WIN32) && !defined(__CYGWIN__)
147 | usleep ((s * 8000 / f->b - tick + f->lastTime) * 1000);
148 | #else
149 | Sleep (s * 8000 / f->b - tick + f->lastTime);
150 | #endif
151 | f->lastTime += s * 8000 / f->b;
152 | if (tick - f->lastTime > 100) f->lastTime += (tick-f->lastTime-100) / 2;
153 | } // If we are more than a 10th of a second behind we reduce the bitrate.
154 | }
155 |
156 | h->i = htonl (f->e.i++);
157 | f->userSend (h, s, 1, f->userData);
158 | }
159 |
160 | #define i2redundant(i,k,v) ((i) + (k) % (v) >= (k) ? (i) % (v) + \
161 | (k) / (v) * (v) : (i) % (v) * ((k) / (v)) + (i) / (v))
162 | /* This function is used to send the redundant packets in a permutated order,
163 | distributing the effects of bust losses. The weakness of this implementation
164 | is that the last k % v packets are not permutated. We choose v = w.
165 | A technique without this drawback is : (v = w*2)
166 | kdw = f->e->k / f->e->w / 2;
167 | off2nd = i - (kdw + 1) * (f->e->k % (f->e->w * 2));
168 | i2redundant = off2nd < 0 ? i / (kdw + 1) + i % (kdw + 1) * f->e->w * 2 :
169 | f->e->k % (f->e->w * 2) + off2nd / kdw + off2nd % kdw * f->e->w * 2
170 |
171 | */
172 |
173 |
174 | void FecEncode (fecPayload *buf, fecEncoder *f)
175 | {
176 | int i;
177 | AddToRedundant (buf, &f->e, f->e.i);
178 |
179 | SendWithDelay (buf, f);
180 | if (f->e.i % (f->e.n + f->e.k) == f->e.n) {
181 | for (i = 0; i < f->e.k; i++) {
182 | SendWithDelay ((fecPayload*) (
183 | i2redundant (i, f->e.k, f->e.w) * f->e.s + (char*)(f + 1)), f);
184 | // To Do : Ensure that the lower CPU load at this point does not
185 | // create timing problems
186 | }
187 | }
188 | }
189 |
190 | void DeleteFecEncoder (fecEncoder *f)
191 | {
192 | free (f);
193 | }
194 |
195 | fecDecoder *NewFecDecoder (void *userData, void (*userReceive)(
196 | void *userData, __int64_t position, fecPayload *buf, int len))
197 | {
198 | fecDecoder *f = malloc (sizeof (*f));
199 | f->userData = userData;
200 | f->userReceive = userReceive;
201 | f->errorMessage = NULL;
202 | f->lostPackets = 0;
203 | f->receivedPackets = 0;
204 | f->correctedPackets = 0;
205 | f->e = NULL;
206 | f->nmissed = 0;
207 | f->missed = NULL;
208 | return f;
209 | }
210 |
211 | size_t FecDecode (void *buf, size_t size, size_t count, fecDecoder *f)
212 | {
213 | headerType *h = (headerType*)buf;
214 | int i, mustSend = 0, hi = ntohl (h->i);
215 | if (!f->e) {
216 | f->e = calloc (1, sizeof (*f->e) + (size * count - sizeof (*h)) *
217 | (ntohl (h->kwg) & ((1 << KBITS) - 1)));
218 | f->e->n = ntohl (h->n);
219 | f->e->s = size * count - sizeof (*h);
220 | f->e->k = ntohl (h->kwg) & ((1 << KBITS) - 1);
221 | f->e->w = (ntohl (h->kwg) >> KBITS) & ((1 << WBITS) - 1);
222 | f->e->g = ntohl (h->kwg) >> (KBITS + WBITS);
223 | f->e->i = hi / (f->e->n + f->e->k) * (f->e->n + f->e->k);
224 | f->lostPackets += f->e->i;
225 | }
226 | else if (ntohl (h->n) != f->e->n || size*count - sizeof (*h) != f->e->s) {
227 | f->errorMessage = "Changing of FEC parameters not supported";
228 | return 0;
229 | }
230 |
231 | // f->e->i is the one we are expecting
232 | if (f->e->i <= hi) { // If we got it, or a later one
233 | mustSend = 1;
234 | do {
235 | if (f->e->i % (f->e->n + f->e->k) == 0) FlushFecDecoder (f);
236 | if (f->e->i < hi) { // If we got a later one, this one is marked as awol
237 | f->missed = realloc (f->missed, (f->nmissed + 1) * sizeof (*f->missed));
238 | f->missed[f->nmissed++] = f->e->i;
239 | }
240 | } while (++f->e->i <= hi);
241 | }
242 | else {
243 | for (i = f->nmissed - 1; i >= 0; i--) {
244 | if (f->missed[i] == hi) { // One of the awols showed up late
245 | f->nmissed--;
246 | f->missed[i] = f->missed[f->nmissed];
247 | mustSend = 1;
248 | break; // Don't bother to free() the 4 bytes.
249 | }
250 | }
251 | }
252 | if (mustSend) {
253 | i = hi % (f->e->n + f->e->k) - f->e->n;
254 | if (i < 0) {
255 | AddToRedundant ((fecPayload*) (h + 1), f->e, hi);
256 | (*f->userReceive)(f->userData, (hi / (f->e->n + f->e->k) * f->e->n +
257 | i + f->e->n) * (__int64_t) f->e->s, (fecPayload*)(h + 1), f->e->s);
258 | f->receivedPackets++;
259 | }
260 | else {
261 | MAC (1, (fecPayload*) (h + 1), (fecPayload*)(i2redundant (i, f->e->k,
262 | f->e->w) * f->e->s + (char*) (f->e + 1)), f->e->g, f->e->s);
263 | }
264 | }
265 |
266 | return size * count;
267 | }
268 |
269 | static int ew, ek; // To Do : make this code reentrant.
270 |
271 | static int MissingCompare (const void *a, const void *b)
272 | { // One of the short comings of qsort
273 | return * (__int32_t *) a * ew % ek - * (__int32_t *) b * ew % ek;
274 | }
275 |
276 | #define BITS ((int) sizeof (int) * 8) // The # of columns stored in each *coef
277 |
278 | void FlushFecDecoder (fecDecoder *f)
279 | {
280 | struct {
281 | int start, len, pivotLog, *coef;
282 | // start is in terms of missed packets / bits
283 | // len is the number of words.
284 | // coef has len groups of g "ints". column "start" correspond to the
285 | // least significant bits in the g "ints" of the first group.
286 | fecPayload *redundant;
287 | } *r, **matrix, *best;
288 | int i, tmp, row, mid, j, leader, bestLeader = 0, k, *GFlog, *GFexp;
289 | fecPayload *final;
290 | __uint32_t coef;
291 |
292 | #ifdef DEBUG_Z2 // This debugging / visualization code only works if g = 1.
293 | FILE *sf = fopen ("sf.txt", "w"); // For a graphical representation.
294 | #endif
295 |
296 | while (f->e->i % (f->e->n + f->e->k) != 0) {
297 | f->missed = realloc (f->missed, (f->nmissed + 1) * sizeof (*f->missed));
298 | f->missed[f->nmissed++] = f->e->i++;
299 | }
300 |
301 | if (f->nmissed == 0) return; // This happens at startup
302 | if (f->nmissed > f->e->k) {
303 | for (i = 0; i < f->nmissed; i++) {
304 | if (f->missed[i] % (f->e->n + f->e->k) < f->e->n) f->lostPackets++;
305 | }
306 | f->nmissed = 0;
307 | free (f->missed);
308 | f->missed = NULL;
309 | return;
310 | }
311 |
312 | r = malloc (sizeof (*r) * f->e->k);
313 | matrix = malloc (sizeof (*matrix) * f->e->k);
314 | for (i = 0; i < f->e->k; i++) {
315 | r[i].coef = NULL;
316 | r[i].start = 0;
317 | r[i].len = 0;
318 | r[i].pivotLog = -1;
319 | r[i].redundant = (fecPayload*) (i * f->e->s + (char*) (f->e + 1));
320 | matrix[i] = r + i;
321 | }
322 | for (i = f->nmissed - 1; i >= 0; i--) {
323 | tmp = f->missed[i] % (f->e->n + f->e->k) - f->e->n;
324 | if (tmp >= 0) { // Drop the redundants we don't have.
325 | matrix[i2redundant (tmp, f->e->k, f->e->w)] = NULL;
326 | f->missed[--f->nmissed] = f->missed[i];
327 | }
328 | }
329 |
330 | // Now f->missed only contains the payload packets.
331 | ew = f->e->w;
332 | ek = f->e->k;
333 | qsort (f->missed, f->nmissed, sizeof (*f->missed), MissingCompare);
334 | // The sorting places all the nonzero entries in the matrix together.
335 |
336 | for (i = 0; i < f->nmissed; i++) { // Build matrix
337 | mid = f->missed[i] * f->e->w % f->e->k; // e.g. mid = i * 87654321 % e->k
338 | coef = f->missed[i] + 1;
339 | for (row = max (0, min (mid, f->e->k - f->e->w) - f->e->w);
340 | row < min (f->e->k, max (mid, f->e->w) + f->e->w); row++) {
341 | coef *= 1763689789;
342 | tmp = coef >> (32 - f->e->g);
343 | if (tmp == 0 || !matrix[row]) continue;
344 | if (r[row].start + r[row].len * BITS <= i) { // Need space ?
345 | if (r[row].len == 0) r[row].start = i / BITS * BITS;
346 | r[row].coef = realloc (r[row].coef,
347 | (i + BITS - r[row].start) / BITS * f->e->g * sizeof (int));
348 | memset (r[row].coef + r[row].len * f->e->g, 0, f->e->g * sizeof (int)
349 | * ((i - r[row].start) / BITS + 1 - r[row].len));
350 | r[row].len = (i - r[row].start) / BITS + 1;
351 | }
352 | for (j = (i - r[row].start) / BITS * f->e->g; tmp > 0; j++, tmp >>= 1) {
353 | if (tmp & 1) r[row].coef[j] ^= 1 << (i & (BITS - 1));
354 | } // Shift the bits into the matrix
355 | } // for each row.
356 | } // for each column
357 |
358 | // Work out the Galois field
359 | GFexp = malloc (sizeof (*GFexp) * ((2 << f->e->g) - 2));
360 | GFlog = malloc (sizeof (*GFlog) * (1 << f->e->g));
361 | for (i = 0, tmp = 1; i < (2 << f->e->g) - 2; i++) {
362 | GFexp[i] = tmp;
363 | if (i < (1 << f->e->g) - 1) GFlog[tmp] = i;
364 |
365 | tmp <<= 1;
366 | if (tmp >> f->e->g) tmp ^= poly[f->e->g];
367 | }
368 |
369 | // Now the slow bit : creating the pivots rows
370 | // Actually, if the system is overdetermined to a great degree
371 | // (i.e. very few packets were lost) all the pivots may already exist,
372 | // we just have to find them.
373 | for (i = 0; i < f->nmissed;) {
374 | #ifdef DEBUG_Z2
375 | for (row = 0; row < f->e->k; row++) {
376 | best = matrix[row];//r + row;
377 | for (j = k = 0; j < f->nmissed; j++) {
378 | tmp = best->start <= j && j < best->start + BITS * best->len &&
379 | ((best->coef[(j - best->start)/BITS] >> (j & (BITS - 1))) & 1);
380 | fputc (tmp ? '*' : ' ', sf);
381 | if (tmp) k ^= f->missed[j] * 4;
382 | }
383 | fprintf (sf, k == best->redundant[0] ? "yes\n" : "no\n");
384 | }
385 | fprintf (sf, "Now trying to create a pivot in row %3d\n", i);
386 | #endif
387 | bestLeader = -1; // The leader is the first non zero element.
388 | for (row = i; row < f->e->k && bestLeader < i; row++) {
389 | if (!matrix[row]) continue;
390 | for (leader = 0; leader * BITS + matrix[row]->start <= i &&
391 | leader < matrix[row]->len; leader++) {
392 | for (j = k = 0; k < f->e->g; k++) {
393 | j |= matrix[row]->coef[leader * f->e->g + k];
394 | }
395 | if (j == 0) continue; // Row starts with 32 zeros
396 | for (leader = leader * BITS + matrix[row]->start; !(j & 1);
397 | leader++) j >>= 1;
398 | if (leader > i || bestLeader >= i) break; // not a new best so break
399 | bestLeader = leader;
400 | tmp = row;
401 | break; // We worked out where the leader is.
402 | }
403 | }
404 | if (bestLeader < 0) break;
405 | best = matrix[tmp];
406 | matrix[tmp] = matrix[i];
407 | matrix[i] = best;
408 | // Now eliminate *best from bestLeader to i.
409 | for (j = bestLeader; ; j++) {
410 | leader = 0; // If j = i we calculate leader before quiting the loop.
411 | if ((j - best->start) / BITS < best->len) {
412 | for (k = ((j - best->start) / BITS + 1) * f->e->g - 1;
413 | k >= (j - best->start) / BITS * f->e->g; k--) {
414 | leader <<= 1;
415 | if (best->coef[k] & (1 << (j & (BITS - 1)))) leader++;
416 | }
417 | }
418 | if (j >= i) break; // Bail out with "leader" the pivot
419 | if (!leader) continue; // Multiplying with 0 has no effect
420 | leader = GFexp[GFlog[leader] + (1<e->g) - 1 -
421 | matrix[j]->pivotLog];
422 | // Now we want *best += best[j] / matrix[j]->pivot * matrix[j].
423 | // Redundants are easy :
424 | MAC (leader, matrix[j]->redundant, best->redundant, f->e->g, f->e->s);
425 |
426 | // The matrix is itself more tricky : have to check for space first.
427 | // Note that with normal band matrices we can require that each
428 | // row's tail not end before the row above it and then the code
429 | // below would never excute. But this Gauss elimination is not normal,
430 | // and there is always a possibility that we may end up needing a
431 | // row that was put aside long ago.
432 | k = matrix[j]->start / BITS + matrix[j]->len
433 | - best->start / BITS - best->len;
434 | if (k > 0) {
435 | best->coef = realloc (best->coef,
436 | (best->len + k) * sizeof (best->coef[0]) * f->e->g);
437 | memset (best->coef + best->len * f->e->g, 0,
438 | k * f->e->g * sizeof (best->coef[0]));
439 | best->len += k;
440 | }
441 |
442 | for (k = (j - matrix[j]->start) / BITS,
443 | tmp = (j - best->start) / BITS; k < matrix[j]->len; k++, tmp++) {
444 | MAC (leader, matrix[j]->coef + k * f->e->g,
445 | best->coef + tmp * f->e->g, f->e->g, f->e->g * sizeof (int));
446 | }
447 | } // For each entry we eliminate
448 | if (leader != 0) matrix[i++]->pivotLog = GFlog[leader];
449 | } // For each pivot we need
450 |
451 | if (bestLeader < 0) f->lostPackets += f->nmissed;
452 | else { // Let's do back substitution
453 | f->correctedPackets += f->nmissed;
454 | final = malloc (f->e->s);
455 | for (i = f->nmissed - 1; i >= 0; i--) {
456 | memset (final, 0, f->e->s);
457 | MAC (GFexp[(1<e->g) - 1 - matrix[i]->pivotLog],
458 | matrix[i]->redundant, final, f->e->g, f->e->s);
459 | // Now that we have final, we may as well back substitute into all
460 | for (j = 0; j < i; j++) { // the rows above
461 | k = (i - matrix[j]->start) / BITS;
462 | if (k >= matrix[j]->len) continue;
463 | for (leader = 0, tmp = f->e->g - 1; tmp >= 0; tmp--) {
464 | leader = (leader << 1) + (1 & (
465 | matrix[j]->coef[k * f->e->g + tmp] >> (i & (BITS - 1))));
466 | } // We call MAC, even if leader is 0. Is it inefficient ?
467 | MAC (leader, final, matrix[j]->redundant, f->e->g, f->e->s);
468 | }
469 | (*f->userReceive)(f->userData,
470 | (f->missed[i] / (f->e->n + f->e->k) * f->e->n + f->missed[i] %
471 | (f->e->n + f->e->k)) * (__int64_t) f->e->s, final, f->e->s);
472 | }
473 | free (final);
474 | }
475 | #ifdef DEBUG_Z2
476 | fclose (sf);
477 | #endif
478 |
479 | free (GFlog);
480 | free (GFexp);
481 | for (i = 0; i < f->e->k; i++) free (r[i].coef);
482 | free (matrix);
483 | free (f->missed);
484 | f->missed = NULL;
485 | f->nmissed = 0;
486 | free (r);
487 | }
488 |
489 | void DeleteFecDecoder (fecDecoder *f)
490 | {
491 | if (f) free (f->e); // free(NULL) is valid.
492 | free (f);
493 | }
494 |
--------------------------------------------------------------------------------
/fec.h:
--------------------------------------------------------------------------------
1 | #ifndef FEC_COPYRIGHT
2 | #define FEC_COPYRIGHT "Parts copyright (c) by Nic Roets 2002. No warranty."
3 | #include
4 |
5 | #ifdef __cplusplus
6 | extern "C" {
7 | #endif
8 |
9 | #define SUGGESTED_FEC_UDP_PORT_NUMBER htons (7837)
10 | // This is just a suggestion. If you modify the protocol, please use a
11 | // different port.
12 |
13 | #if defined (WIN32) && !defined (__CYGWIN__)
14 | typedef __int32 fecPayload;
15 | #define __int64_t __int64
16 | #else
17 | typedef __int32_t fecPayload;
18 | #endif
19 |
20 | typedef struct {
21 | int k, w, g, n, i, s;
22 | } fecEncDec;
23 |
24 | typedef struct {
25 | void *userData;
26 | size_t (*userSend)(void *buf, size_t size, size_t count, void *userData);
27 | int lastTime, b;
28 | fecEncDec e;
29 | } fecEncoder;
30 |
31 | fecEncoder *NewFecEncoder (void *userData,
32 | size_t (*userSend)(void *buf, size_t size, size_t count, void *userData),
33 | char **errorMessage,
34 | int s, int n, int k, int w, int g, int b);
35 |
36 | void FecEncode (fecPayload *buf, fecEncoder *f);
37 |
38 | void DeleteFecEncoder (fecEncoder *f);
39 |
40 | //-----------------------------------------
41 |
42 | typedef struct {
43 | int lostPackets, receivedPackets, correctedPackets; // payload only
44 | char *errorMessage;
45 | // The rest is private
46 | void *userData;
47 | void (*userReceive)(void *userData, __int64_t position, fecPayload *buf,
48 | int len);
49 | fecEncDec *e; // The redudant data is at e + 1.
50 | int nmissed;
51 | fecPayload *missed; // Keeps track of both payload and redundant packets.
52 | } fecDecoder;
53 |
54 | fecDecoder *NewFecDecoder (void *userData, void (*userReceive)(
55 | void *userData, __int64_t position, fecPayload *buf, int len));
56 |
57 | size_t FecDecode (void *buf, size_t size, size_t count,
58 | fecDecoder *f);
59 |
60 | void FlushFecDecoder (fecDecoder *f);
61 |
62 | void DeleteFecDecoder (fecDecoder *f);
63 |
64 | #ifdef __cplusplus
65 | }
66 | #endif
67 |
68 | #endif
69 |
--------------------------------------------------------------------------------
/feclib.spec:
--------------------------------------------------------------------------------
1 | Summary: Transmits / multicasts files over unreliable UDP
2 | Name: feclib
3 | Version: 0.90
4 | Release: 1
5 | Copyright: Open Source + Advert
6 | Group: Networking/File transfer
7 | Packager: Nic Roets
8 | Vendor: Nic Roets
9 | URL: http://feclib.sourceforge.net
10 |
11 | %description
12 | Transmits / multicasts files over unreliable UDP.
13 |
14 | %prep
15 | tar xzf $RPM_SOURCE_DIR/$RPM_PACKAGE_NAME-$RPM_PACKAGE_VERSION.tgz
16 | %build
17 | cd $RPM_PACKAGE_NAME-$RPM_PACKAGE_VERSION
18 | make
19 | %install
20 | cd $RPM_PACKAGE_NAME-$RPM_PACKAGE_VERSION
21 | rm -rf $RPM_BUILD_ROOT
22 | mkdir -p $RPM_BUILD_ROOT/usr/bin
23 | install -s -m 755 fecrecv $RPM_BUILD_ROOT/usr/bin/fecrecv
24 | install -s -m 755 fecsend $RPM_BUILD_ROOT/usr/bin/fecsend
25 |
26 | $clean
27 | rm -rf $RPM_BUILD_ROOT
28 |
29 | %files
30 | %defattr(-,root,root)
31 | %doc $RPM_PACKAGE_NAME-$RPM_PACKAGE_VERSION/documentation.html
32 |
33 | /usr/bin/fecrecv
34 | /usr/bin/fecsend
35 |
--------------------------------------------------------------------------------
/fecrecv.c:
--------------------------------------------------------------------------------
1 | #if defined (WIN32) || defined (__CYGWIN__)
2 | #include
3 | #include
4 | #else
5 | #include
6 | #include
7 | #define closesocket(x) close (x)
8 | #endif
9 | #include "fec.h"
10 |
11 | // To do : Add ability to handle multiple concurrent file transfers.
12 | // We would identify each process by the `from' address. Apart from the
13 | // main process / thread which does the recvfrom, we'd have a thread or
14 | // process for every transfer so that a Flush does not cause a pile up of
15 | // packets. When a transmission completes successfully, we'd exec a given
16 | // script, instead of the current situation where we exit the main process.
17 |
18 | #ifdef _CONSOLE
19 | #define mainreturn(x) \
20 | fprintf (stderr, "Press enter to exit.\n"); \
21 | getchar (); /* Behave acceptable to Windoze users. */ \
22 | return x
23 | #else
24 | #define mainreturn(x) return x
25 | #endif
26 |
27 | void WriteF (void *userData, __int64_t position, fecPayload *buf, int len)
28 | {
29 | fsetpos ((FILE*) userData, (fpos_t *) &position);
30 | // 64 bit currently only works on Windows. ?
31 | fwrite (buf, len, 1, (FILE*) userData);
32 | }
33 |
34 | int main (int argc, char *argv[])
35 | {
36 | struct sockaddr_in sin;
37 | int sock, len;
38 | char p[4096], *fname = "x.fec";
39 | struct timeval tv;
40 | fd_set set;
41 | FILE *f;
42 | fecDecoder *d;
43 | struct ip_mreq mreq;
44 | #if defined (WIN32) || defined (__CYGWIN__)
45 | WSADATA localWSA;
46 | if (WSAStartup (MAKEWORD(1,1),&localWSA) != 0) {
47 | fprintf (stderr, "Unable to load wsock32.dll\n");
48 | mainreturn (1);
49 | }
50 | #endif
51 |
52 | fprintf (stderr, "%s: %s\n", argv[0], FEC_COPYRIGHT);
53 |
54 | if ((sock = socket (PF_INET, SOCK_DGRAM, 0)) == -1) {
55 | fprintf (stderr, "Unable to make internet socket\n");
56 | mainreturn (3);
57 | }
58 | memset (&sin, 0, sizeof (sin));
59 | sin.sin_family = AF_INET;
60 | *(unsigned*)&sin.sin_addr = 0; //inet_addr (iface);
61 | sin.sin_port = SUGGESTED_FEC_UDP_PORT_NUMBER;
62 |
63 | for (;argc > 1; argc--, argv++) {
64 | if (strcmp (argv[1], "-h") == 0) {
65 | fprintf (stderr,
66 | "Usage : %s [-f %s] [-p portNumber] [-i interface] [-m multicastAddress]\n"
67 | "Receives a file and exits. If the file was received successfully\n"
68 | "it is called `%s' (adjustable with -f), otherwise it is deleted.\n"
69 | "Note that the interface is the IP address of the local device\n",
70 | argv[0], fname, fname);
71 | mainreturn (4);
72 | }
73 | else if (strcmp (argv[1], "-f") == 0) {
74 | fname = argv[2];
75 | argc--, argv++;
76 | }
77 | else if (strcmp (argv[1], "-p") == 0) {
78 | sin.sin_port = htons (atoi (argv[2]));
79 | argc--, argv++;
80 | }
81 | else if (strcmp (argv[1], "-i") == 0) {
82 | *(unsigned*)&sin.sin_addr = inet_addr (argv[2]);
83 | argc--, argv++;
84 | }
85 | else if (strcmp (argv[1], "-m") == 0) {
86 | *(unsigned*)&mreq.imr_multiaddr = inet_addr (argv[2]);
87 | *(unsigned*)&mreq.imr_interface = *(unsigned*)&sin.sin_addr;
88 | if (setsockopt (sock, IPPROTO_IP, IP_ADD_MEMBERSHIP, (char*) &mreq,
89 | sizeof(mreq)) < 0) fprintf (stderr, "Unable to active multicast\n");
90 | }
91 | else fprintf (stderr, "Unknown option %s. Use -h for help\n", argv[1]);
92 | }
93 |
94 | if (bind (sock, (struct sockaddr*) &sin, sizeof (sin)) == -1) {
95 | fprintf (stderr, "Error : UDP port or device is unavailable\n");
96 | mainreturn (1);
97 | }
98 | f = fopen (fname, "w");
99 | d = NewFecDecoder (f, WriteF);
100 | do {
101 | len = recv (sock, p, sizeof (p), 0);
102 | FecDecode (p, len, 1, d);
103 | FD_ZERO (&set);
104 | FD_SET (sock, &set);
105 | tv.tv_sec = 30;
106 | tv.tv_usec = 0;
107 | } while (select (sock + 1, &set, NULL, NULL, &tv) != 0);
108 | FlushFecDecoder (d);
109 |
110 | fclose (f);
111 |
112 | fprintf (stderr, "File received. %d packets was unrecoverable out of %d\n",
113 | d->lostPackets,
114 | (d->lostPackets + d->receivedPackets + d->correctedPackets));
115 | if (d->lostPackets > 0) unlink (fname);
116 |
117 | closesocket (sock);
118 | mainreturn (0);
119 | }
120 |
--------------------------------------------------------------------------------
/fecrecv.dsp:
--------------------------------------------------------------------------------
1 | # Microsoft Developer Studio Project File - Name="fec" - Package Owner=<4>
2 | # Microsoft Developer Studio Generated Build File, Format Version 6.00
3 | # ** DO NOT EDIT **
4 |
5 | # TARGTYPE "Win32 (x86) Console Application" 0x0103
6 |
7 | CFG=fec - Win32 Debug
8 | !MESSAGE This is not a valid makefile. To build this project using NMAKE,
9 | !MESSAGE use the Export Makefile command and run
10 | !MESSAGE
11 | !MESSAGE NMAKE /f "fecrecv.mak".
12 | !MESSAGE
13 | !MESSAGE You can specify a configuration when running NMAKE
14 | !MESSAGE by defining the macro CFG on the command line. For example:
15 | !MESSAGE
16 | !MESSAGE NMAKE /f "fecrecv.mak" CFG="fec - Win32 Debug"
17 | !MESSAGE
18 | !MESSAGE Possible choices for configuration are:
19 | !MESSAGE
20 | !MESSAGE "fec - Win32 Release" (based on "Win32 (x86) Console Application")
21 | !MESSAGE "fec - Win32 Debug" (based on "Win32 (x86) Console Application")
22 | !MESSAGE
23 |
24 | # Begin Project
25 | # PROP AllowPerConfigDependencies 0
26 | # PROP Scc_ProjName ""
27 | # PROP Scc_LocalPath ""
28 | CPP=cl.exe
29 | RSC=rc.exe
30 |
31 | !IF "$(CFG)" == "fec - Win32 Release"
32 |
33 | # PROP BASE Use_MFC 0
34 | # PROP BASE Use_Debug_Libraries 0
35 | # PROP BASE Output_Dir "Release"
36 | # PROP BASE Intermediate_Dir "Release"
37 | # PROP BASE Target_Dir ""
38 | # PROP Use_MFC 0
39 | # PROP Use_Debug_Libraries 0
40 | # PROP Output_Dir "Release"
41 | # PROP Intermediate_Dir "Release"
42 | # PROP Ignore_Export_Lib 0
43 | # PROP Target_Dir ""
44 | # ADD BASE CPP /nologo /W3 /GX /O2 /D "WIN32" /D "NDEBUG" /D "_CONSOLE" /D "_MBCS" /YX /FD /c
45 | # ADD CPP /nologo /W3 /GX /O2 /D "WIN32" /D "NDEBUG" /D "_CONSOLE" /D "_MBCS" /YX /FD /c
46 | # ADD BASE RSC /l 0x409 /d "NDEBUG"
47 | # ADD RSC /l 0x409 /d "NDEBUG"
48 | BSC32=bscmake.exe
49 | # ADD BASE BSC32 /nologo
50 | # ADD BSC32 /nologo
51 | LINK32=link.exe
52 | # ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:console /machine:I386
53 | # ADD LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib wsock32.lib /nologo /subsystem:console /machine:I386
54 |
55 | !ELSEIF "$(CFG)" == "fec - Win32 Debug"
56 |
57 | # PROP BASE Use_MFC 0
58 | # PROP BASE Use_Debug_Libraries 1
59 | # PROP BASE Output_Dir "Debug"
60 | # PROP BASE Intermediate_Dir "Debug"
61 | # PROP BASE Target_Dir ""
62 | # PROP Use_MFC 0
63 | # PROP Use_Debug_Libraries 1
64 | # PROP Output_Dir "Debug"
65 | # PROP Intermediate_Dir "Debug"
66 | # PROP Ignore_Export_Lib 0
67 | # PROP Target_Dir ""
68 | # ADD BASE CPP /nologo /W3 /Gm /GX /ZI /Od /D "WIN32" /D "_DEBUG" /D "_CONSOLE" /D "_MBCS" /YX /FD /GZ /c
69 | # ADD CPP /nologo /W3 /Gm /GX /ZI /Od /D "WIN32" /D "_DEBUG" /D "_CONSOLE" /D "_MBCS" /YX /FD /GZ /c
70 | # ADD BASE RSC /l 0x409 /d "_DEBUG"
71 | # ADD RSC /l 0x409 /d "_DEBUG"
72 | BSC32=bscmake.exe
73 | # ADD BASE BSC32 /nologo
74 | # ADD BSC32 /nologo
75 | LINK32=link.exe
76 | # ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:console /debug /machine:I386 /pdbtype:sept
77 | # ADD LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib wsock32.lib /nologo /subsystem:console /debug /machine:I386 /pdbtype:sept
78 |
79 | !ENDIF
80 |
81 | # Begin Target
82 |
83 | # Name "fec - Win32 Release"
84 | # Name "fec - Win32 Debug"
85 | # Begin Group "Source Files"
86 |
87 | # PROP Default_Filter "cpp;c;cxx;rc;def;r;odl;idl;hpj;bat"
88 | # Begin Source File
89 |
90 | SOURCE=.\fec.c
91 | # End Source File
92 | # Begin Source File
93 |
94 | SOURCE=.\fecrecv.c
95 | # End Source File
96 | # End Group
97 | # Begin Group "Header Files"
98 |
99 | # PROP Default_Filter "h;hpp;hxx;hm;inl"
100 | # Begin Source File
101 |
102 | SOURCE=.\fec.h
103 | # End Source File
104 | # End Group
105 | # Begin Group "Resource Files"
106 |
107 | # PROP Default_Filter "ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe"
108 | # End Group
109 | # End Target
110 | # End Project
111 |
--------------------------------------------------------------------------------
/fecsend.c:
--------------------------------------------------------------------------------
1 | #if defined (WIN32) || defined (__CYGWIN__)
2 | #include
3 | #include
4 | #else
5 | #include
6 | #include
7 | #include
8 | #define closesocket(x) close (x)
9 | #endif
10 | #include
11 | #include "fec.h"
12 |
13 | size_t Send (void *buf, size_t size, size_t count, void *dummy)
14 | { // fdopen ing is simpler under *nix, but illegal under WinSock.
15 | return send (*(int*) dummy, buf, size * count, 0);
16 | }
17 |
18 | #ifdef _CONSOLE
19 | #define mainreturn(x) \
20 | fprintf (stderr, "Press enter to exit.\n"); \
21 | getchar (); /* Behave acceptable to Windoze users. */ \
22 | return x
23 | #else
24 | #define mainreturn(x) return x
25 | #endif
26 |
27 | int main (int argc, char *argv[])
28 | {
29 | fecEncoder *e;
30 | char *msg;
31 | int sock, n, doneWithSwitches = 0, r = 10, connected = 0, kbps = 64;
32 | fecPayload pay[256];
33 | struct sockaddr_in sin;
34 | struct hostent *he;
35 | #if defined (WIN32) || defined (__CYGWIN__)
36 | WSADATA localWSA;
37 | if (WSAStartup (MAKEWORD(1,1),&localWSA) != 0) {
38 | fprintf (stderr, "Unable to load wsock32.dll\n");
39 | mainreturn (1);
40 | }
41 | #endif
42 |
43 | fprintf (stderr, "%s: %s\n", argv[0], FEC_COPYRIGHT);
44 | if (argc < 3) {
45 | fprintf (stderr,
46 | "Usage : %s a.b.c.d [-p port] [-r r] [-b kiloBitsPerSecond] [--] file1 ...\n"
47 | "Sends the files to the specified address (which may be a multicast adress)\n"
48 | "using the unreliable UDP protocol. The files are encoded with `forward\n"
49 | "error correction' techniques so that the receiver will in most cases be\n"
50 | "able to recover any data that may have been lost.\n\n"
51 | " r is the amount of redundancy to add, expressed as a percentage\n",
52 | argv[0]);
53 | mainreturn (5);
54 | }
55 |
56 | if ((sock = socket (PF_INET, SOCK_DGRAM, 0)) == -1) {
57 | fprintf (stderr, "Unable to make internet socket\n");
58 | mainreturn (3);
59 | }
60 |
61 | memset (&sin, 0, sizeof (sin));
62 | sin.sin_family = AF_INET;
63 | if (strspn (argv[1], "0123456789.") != strlen (argv[1])) {
64 | if ((he = gethostbyname (argv[1])) == NULL) {
65 | fprintf (stderr, "Unknown host %s\n", argv[1]);
66 | mainreturn (4);
67 | }
68 | sin.sin_addr = *(struct in_addr *) he->h_addr;
69 | }
70 | else *(unsigned*)&sin.sin_addr = inet_addr (argv[1]);
71 | sin.sin_port = SUGGESTED_FEC_UDP_PORT_NUMBER;
72 |
73 | for (;argc >= 3; argc--, argv++) {
74 | if (doneWithSwitches || argv[2][0] != '-') {
75 | FILE *f = fopen (argv[2], "rb");
76 | if (!connected) {
77 | connect (sock, (struct sockaddr *) &sin, sizeof (sin));
78 | connected = 1;
79 | }
80 | if (!f) fprintf (stderr, "Unable to open %s\n", argv[2]);
81 | else {
82 | struct stat st;
83 | fstat (fileno (f), &st);
84 | n = (st.st_size + sizeof (pay) - 1) / sizeof (pay);
85 | e = NewFecEncoder (&sock, Send, &msg, sizeof (pay),
86 | n, n * r / 100 , 40, 4, kbps * 1000);
87 | if (!e) fprintf (stderr, "%s\n", msg);
88 | else {
89 | while (fread (pay, 1, sizeof (pay), f) > 0) {
90 | FecEncode (pay, e);
91 | }
92 | DeleteFecEncoder (e);
93 | #if defined (WIN32)
94 | Sleep (30000);
95 | #else
96 | sleep (30);
97 | #endif
98 | }
99 | fclose (f);
100 | }
101 | }
102 | else if (strcmp (argv[2], "-p") == 0) {
103 | sin.sin_port = htons ((short) atoi (argv[3]));
104 | argc--, argv++;
105 | }
106 | else if (strcmp (argv[2], "-r") == 0) {
107 | r = atoi (argv[3]);
108 | argc--, argv++;
109 | }
110 | else if (strcmp (argv[2], "-b") == 0) {
111 | kbps = atoi (argv[3]);
112 | argc--, argv++;
113 | }
114 | else if (strcmp (argv[2], "--") == 0) doneWithSwitches = 1;
115 | else fprintf (stderr, "Unknown option %s\n", argv[2]);
116 | }
117 | closesocket (sock);
118 | return 0;
119 | }
120 |
--------------------------------------------------------------------------------
/fecsend.dsp:
--------------------------------------------------------------------------------
1 | # Microsoft Developer Studio Project File - Name="fec" - Package Owner=<4>
2 | # Microsoft Developer Studio Generated Build File, Format Version 6.00
3 | # ** DO NOT EDIT **
4 |
5 | # TARGTYPE "Win32 (x86) Console Application" 0x0103
6 |
7 | CFG=fec - Win32 Debug
8 | !MESSAGE This is not a valid makefile. To build this project using NMAKE,
9 | !MESSAGE use the Export Makefile command and run
10 | !MESSAGE
11 | !MESSAGE NMAKE /f "fecsend.mak".
12 | !MESSAGE
13 | !MESSAGE You can specify a configuration when running NMAKE
14 | !MESSAGE by defining the macro CFG on the command line. For example:
15 | !MESSAGE
16 | !MESSAGE NMAKE /f "fecsend.mak" CFG="fec - Win32 Debug"
17 | !MESSAGE
18 | !MESSAGE Possible choices for configuration are:
19 | !MESSAGE
20 | !MESSAGE "fec - Win32 Release" (based on "Win32 (x86) Console Application")
21 | !MESSAGE "fec - Win32 Debug" (based on "Win32 (x86) Console Application")
22 | !MESSAGE
23 |
24 | # Begin Project
25 | # PROP AllowPerConfigDependencies 0
26 | # PROP Scc_ProjName ""
27 | # PROP Scc_LocalPath ""
28 | CPP=cl.exe
29 | RSC=rc.exe
30 |
31 | !IF "$(CFG)" == "fec - Win32 Release"
32 |
33 | # PROP BASE Use_MFC 0
34 | # PROP BASE Use_Debug_Libraries 0
35 | # PROP BASE Output_Dir "Release"
36 | # PROP BASE Intermediate_Dir "Release"
37 | # PROP BASE Target_Dir ""
38 | # PROP Use_MFC 0
39 | # PROP Use_Debug_Libraries 0
40 | # PROP Output_Dir "Release"
41 | # PROP Intermediate_Dir "Release"
42 | # PROP Ignore_Export_Lib 0
43 | # PROP Target_Dir ""
44 | # ADD BASE CPP /nologo /W3 /GX /O2 /D "WIN32" /D "NDEBUG" /D "_CONSOLE" /D "_MBCS" /YX /FD /c
45 | # ADD CPP /nologo /W3 /GX /O2 /D "WIN32" /D "NDEBUG" /D "_CONSOLE" /D "_MBCS" /YX /FD /c
46 | # ADD BASE RSC /l 0x409 /d "NDEBUG"
47 | # ADD RSC /l 0x409 /d "NDEBUG"
48 | BSC32=bscmake.exe
49 | # ADD BASE BSC32 /nologo
50 | # ADD BSC32 /nologo
51 | LINK32=link.exe
52 | # ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:console /machine:I386
53 | # ADD LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib wsock32.lib /nologo /subsystem:console /machine:I386
54 |
55 | !ELSEIF "$(CFG)" == "fec - Win32 Debug"
56 |
57 | # PROP BASE Use_MFC 0
58 | # PROP BASE Use_Debug_Libraries 1
59 | # PROP BASE Output_Dir "Debug"
60 | # PROP BASE Intermediate_Dir "Debug"
61 | # PROP BASE Target_Dir ""
62 | # PROP Use_MFC 0
63 | # PROP Use_Debug_Libraries 1
64 | # PROP Output_Dir "Debug"
65 | # PROP Intermediate_Dir "Debug"
66 | # PROP Ignore_Export_Lib 0
67 | # PROP Target_Dir ""
68 | # ADD BASE CPP /nologo /W3 /Gm /GX /ZI /Od /D "WIN32" /D "_DEBUG" /D "_CONSOLE" /D "_MBCS" /YX /FD /GZ /c
69 | # ADD CPP /nologo /W3 /Gm /GX /ZI /Od /D "WIN32" /D "_DEBUG" /D "_CONSOLE" /D "_MBCS" /YX /FD /GZ /c
70 | # ADD BASE RSC /l 0x409 /d "_DEBUG"
71 | # ADD RSC /l 0x409 /d "_DEBUG"
72 | BSC32=bscmake.exe
73 | # ADD BASE BSC32 /nologo
74 | # ADD BSC32 /nologo
75 | LINK32=link.exe
76 | # ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:console /debug /machine:I386 /pdbtype:sept
77 | # ADD LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib wsock32.lib /nologo /subsystem:console /debug /machine:I386 /pdbtype:sept
78 |
79 | !ENDIF
80 |
81 | # Begin Target
82 |
83 | # Name "fec - Win32 Release"
84 | # Name "fec - Win32 Debug"
85 | # Begin Group "Source Files"
86 |
87 | # PROP Default_Filter "cpp;c;cxx;rc;def;r;odl;idl;hpj;bat"
88 | # Begin Source File
89 |
90 | SOURCE=.\fec.c
91 | # End Source File
92 | # Begin Source File
93 |
94 | SOURCE=.\fecsend.c
95 | # End Source File
96 | # End Group
97 | # Begin Group "Header Files"
98 |
99 | # PROP Default_Filter "h;hpp;hxx;hm;inl"
100 | # Begin Source File
101 |
102 | SOURCE=.\fec.h
103 | # End Source File
104 | # End Group
105 | # Begin Group "Resource Files"
106 |
107 | # PROP Default_Filter "ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe"
108 | # End Group
109 | # End Target
110 | # End Project
111 |
--------------------------------------------------------------------------------
/fectest.c:
--------------------------------------------------------------------------------
1 | #include
2 | #include "fec.h"
3 |
4 | void PayloadCheck (void *dummy, __int64_t pos, fecPayload *b, int len)
5 | {
6 | if ((int) pos != b[0]) fprintf (stderr, "*** Bug in FEC code ! ***\n");
7 | }
8 |
9 | static int c = 0;
10 | enum { n = 5000, k = n } dummy;
11 |
12 | size_t ChannelSimulate (void *buf, size_t size, size_t count, void *d)
13 | {
14 | if (rand () < RAND_MAX / 3) return 0;
15 | return FecDecode (buf, size, count, (fecDecoder*) d);
16 | }
17 |
18 | int main (void)
19 | {
20 | #if 1
21 | char *err;
22 | fecPayload msg[4];
23 | fecDecoder *d = NewFecDecoder (NULL, PayloadCheck);
24 | fecEncoder *e = NewFecEncoder (d, ChannelSimulate, &err, sizeof (msg), n,
25 | k, 20, sizeof (msg) / sizeof (msg[0]), 0);
26 | if (!e || !d) {
27 | fprintf (stderr, "%s\n", err);
28 | return 1;
29 | }
30 | for (msg[0] = 0; msg[0] < n * (int) sizeof (msg);
31 | msg[0] += sizeof (msg)) FecEncode (msg, e);
32 | printf ("Starting recovery...\n");
33 | FlushFecDecoder (d);
34 | printf ("Received %d, Corrected %d, Lost %d\n",
35 | d->receivedPackets, d->correctedPackets, d->lostPackets);
36 | #else // This code generates prime "polynomials".
37 | unsigned i, j, divisor, q, m;
38 | for (i = 1; i < 0x20000; i <<= 1) {
39 | for (j = i; j < 2 * i; j++) { // Going to test j for primeness.
40 | for (divisor = 2; divisor < i; divisor++) {
41 | for (q = j, m = i; m > 0; m >>= 1) {
42 | if ((q ^ (m * divisor)) < q &&
43 | (q ^ (m * divisor)) < m * divisor) q ^= m * divisor;
44 | }
45 | if (q == 0) break; // Not prime
46 | }
47 | if (divisor >= i) break; // Prime
48 | }
49 | printf ("0x%x, ", j);
50 | }
51 | #endif
52 | #ifdef WIN32
53 | getchar ();
54 | #endif
55 | return 0;
56 | }
57 |
--------------------------------------------------------------------------------
/fectest.dsp:
--------------------------------------------------------------------------------
1 | # Microsoft Developer Studio Project File - Name="fec" - Package Owner=<4>
2 | # Microsoft Developer Studio Generated Build File, Format Version 6.00
3 | # ** DO NOT EDIT **
4 |
5 | # TARGTYPE "Win32 (x86) Console Application" 0x0103
6 |
7 | CFG=fec - Win32 Debug
8 | !MESSAGE This is not a valid makefile. To build this project using NMAKE,
9 | !MESSAGE use the Export Makefile command and run
10 | !MESSAGE
11 | !MESSAGE NMAKE /f "fec.mak".
12 | !MESSAGE
13 | !MESSAGE You can specify a configuration when running NMAKE
14 | !MESSAGE by defining the macro CFG on the command line. For example:
15 | !MESSAGE
16 | !MESSAGE NMAKE /f "fec.mak" CFG="fec - Win32 Debug"
17 | !MESSAGE
18 | !MESSAGE Possible choices for configuration are:
19 | !MESSAGE
20 | !MESSAGE "fec - Win32 Release" (based on "Win32 (x86) Console Application")
21 | !MESSAGE "fec - Win32 Debug" (based on "Win32 (x86) Console Application")
22 | !MESSAGE
23 |
24 | # Begin Project
25 | # PROP AllowPerConfigDependencies 0
26 | # PROP Scc_ProjName ""
27 | # PROP Scc_LocalPath ""
28 | CPP=cl.exe
29 | RSC=rc.exe
30 |
31 | !IF "$(CFG)" == "fec - Win32 Release"
32 |
33 | # PROP BASE Use_MFC 0
34 | # PROP BASE Use_Debug_Libraries 0
35 | # PROP BASE Output_Dir "Release"
36 | # PROP BASE Intermediate_Dir "Release"
37 | # PROP BASE Target_Dir ""
38 | # PROP Use_MFC 0
39 | # PROP Use_Debug_Libraries 0
40 | # PROP Output_Dir "Release"
41 | # PROP Intermediate_Dir "Release"
42 | # PROP Ignore_Export_Lib 0
43 | # PROP Target_Dir ""
44 | # ADD BASE CPP /nologo /W3 /GX /O2 /D "WIN32" /D "NDEBUG" /D "_CONSOLE" /D "_MBCS" /YX /FD /c
45 | # ADD CPP /nologo /W3 /GX /O2 /D "WIN32" /D "NDEBUG" /D "_CONSOLE" /D "_MBCS" /YX /FD /c
46 | # ADD BASE RSC /l 0x409 /d "NDEBUG"
47 | # ADD RSC /l 0x409 /d "NDEBUG"
48 | BSC32=bscmake.exe
49 | # ADD BASE BSC32 /nologo
50 | # ADD BSC32 /nologo
51 | LINK32=link.exe
52 | # ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:console /machine:I386
53 | # ADD LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib wsock32.lib /nologo /subsystem:console /machine:I386
54 |
55 | !ELSEIF "$(CFG)" == "fec - Win32 Debug"
56 |
57 | # PROP BASE Use_MFC 0
58 | # PROP BASE Use_Debug_Libraries 1
59 | # PROP BASE Output_Dir "Debug"
60 | # PROP BASE Intermediate_Dir "Debug"
61 | # PROP BASE Target_Dir ""
62 | # PROP Use_MFC 0
63 | # PROP Use_Debug_Libraries 1
64 | # PROP Output_Dir "Debug"
65 | # PROP Intermediate_Dir "Debug"
66 | # PROP Ignore_Export_Lib 0
67 | # PROP Target_Dir ""
68 | # ADD BASE CPP /nologo /W3 /Gm /GX /ZI /Od /D "WIN32" /D "_DEBUG" /D "_CONSOLE" /D "_MBCS" /YX /FD /GZ /c
69 | # ADD CPP /nologo /W3 /Gm /GX /ZI /Od /D "WIN32" /D "_DEBUG" /D "_CONSOLE" /D "_MBCS" /YX /FD /GZ /c
70 | # ADD BASE RSC /l 0x409 /d "_DEBUG"
71 | # ADD RSC /l 0x409 /d "_DEBUG"
72 | BSC32=bscmake.exe
73 | # ADD BASE BSC32 /nologo
74 | # ADD BSC32 /nologo
75 | LINK32=link.exe
76 | # ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:console /debug /machine:I386 /pdbtype:sept
77 | # ADD LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib wsock32.lib /nologo /subsystem:console /debug /machine:I386 /pdbtype:sept
78 |
79 | !ENDIF
80 |
81 | # Begin Target
82 |
83 | # Name "fec - Win32 Release"
84 | # Name "fec - Win32 Debug"
85 | # Begin Group "Source Files"
86 |
87 | # PROP Default_Filter "cpp;c;cxx;rc;def;r;odl;idl;hpj;bat"
88 | # Begin Source File
89 |
90 | SOURCE=.\fec.c
91 | # End Source File
92 | # Begin Source File
93 |
94 | SOURCE=.\fectest.c
95 | # End Source File
96 | # End Group
97 | # Begin Group "Header Files"
98 |
99 | # PROP Default_Filter "h;hpp;hxx;hm;inl"
100 | # Begin Source File
101 |
102 | SOURCE=.\fec.h
103 | # End Source File
104 | # End Group
105 | # Begin Group "Resource Files"
106 |
107 | # PROP Default_Filter "ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe"
108 | # End Group
109 | # End Target
110 | # End Project
111 |
--------------------------------------------------------------------------------
/index.html:
--------------------------------------------------------------------------------
1 |
2 |
11 |
12 |
13 |
Welcome to the Forword Error Correction Library !
14 | At the insistence of my good friend, Edwin Peer, (and his assistant, Johnny),
15 | I've spent a few weeks designing and coding this open source encoder and
16 | decoder, and the project is currently at its first public release.
17 | Frequently Asked Questions
18 |
19 | - Why should I use this library, when others are more mature, or come
20 | with commercial support etc ? Firstly this library is very clean,
21 | making
22 | it very easy to maintain, port etc. There can't be many bugs left in the mere
23 | 500 lines of code. Secondly, this library is much more configurable.
24 |
- How does it work ? Click here
25 | for a lay man's description.
26 |
- Can I use this library to locate and correct communications
27 | errors ? No. This library cannot detect or correct data that has been
28 | changed. Rather look at projects like
29 | RSCode.
30 |
31 | Downloads - also available with Source Forge Release
32 | feclib-0.90.tar.gz
33 | feclib-0.90-1.i686.rpm
34 | fecrecv-0.90.exe
35 | fecsend-0.90.exe
36 | Contact information
37 | Email : Nic Roets
38 | Telephone : +27 12 803 4022
39 | Celphone : +27 83 765 9503
40 |
41 |
--------------------------------------------------------------------------------