├── 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 |
  1. s the size of each packet, excluding headers. (See 5 below). 32 |
  2. 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 |
  3. 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 |

  4. 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 |
  5. 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 |
  6. 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 |
  1. 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 |

  2. Optimizations 117 |
  3. Packet checksum Let the library send its own checksums incase the 118 | UDP checksum still lets some bad packets get through. 119 |
  4. 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 |
  5. 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 | 3 |
4 | Project Summary 5 | 6 | 7 | Current Version (WebCVS) 8 | 9 | API documentation 10 |
11 |

12 | SourceForge.net Logo 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 |
  1. 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 |
  2. How does it work ? Click here 25 | for a lay man's description. 26 |
  3. 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 | --------------------------------------------------------------------------------