/* $Id: mcl_fec.cpp,v 1.10 2003/11/26 12:03:47 roca Exp $ */ /* * Copyright (c) 2003 INRIA - All rights reserved * (main author: Vincent Roca - vincent.roca@inrialpes.fr) * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version 2 * of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, * USA. */ #include "mcl_includes.h" #ifdef LDPC_FEC /* a define specific to LDGM/LDPC */ /*#define LEFT_DEGREE 10*/ #define LEFT_DEGREE 3 #endif mcl_fec::mcl_fec () { /* set everything to 0 */ memset(this->max_k, 0, sizeof(this->max_k)); memset(this->max_n, 0, sizeof(this->max_n)); memset(this->cur_k, 0, sizeof(this->cur_k)); memset(this->cur_n, 0, sizeof(this->cur_n)); /* * initialize everything */ this->initialize(); } mcl_fec::~mcl_fec () { #ifdef DEBUG memset(this->max_k, 0, sizeof(this->max_k)); memset(this->max_n, 0, sizeof(this->max_n)); memset(this->cur_k, 0, sizeof(this->cur_k)); memset(this->cur_n, 0, sizeof(this->cur_n)); #endif } /** * Initializes the FEC class for a given FEC code. * => See header file for more informations. */ void mcl_fec::initialize () { /* * Initialize the fec class for each supported FEC code. * The order is significant since the last call specifies the * default FEC code. */ /* MCL_FEC_CODE_NULL case */ // no fec codec used, so use default conservative values... this->fec_codec = MCL_FEC_CODE_NULL; this->max_k[MCL_FEC_CODE_NULL] = 255; this->max_n[MCL_FEC_CODE_NULL] = 255; this->cur_k[MCL_FEC_CODE_NULL] = 255; this->cur_n[MCL_FEC_CODE_NULL] = 255; #ifdef LDPC_FEC //#if 0 /* MCL_FEC_CODE_LDPC case */ this->fec_codec = MCL_FEC_CODE_LDPC; this->max_k[MCL_FEC_CODE_LDPC] = LDPC_MAX_K; this->max_n[MCL_FEC_CODE_LDPC] = LDPC_MAX_N; this->cur_k[MCL_FEC_CODE_LDPC] = LDPC_MAX_K; this->cur_n[MCL_FEC_CODE_LDPC] = min(2*LDPC_MAX_K, LDPC_MAX_N); // default: half data, half fec //#endif /* MCL_FEC_CODE_LDGM case */ this->fec_codec = MCL_FEC_CODE_LDGM; this->max_k[MCL_FEC_CODE_LDGM] = LDPC_MAX_K; this->max_n[MCL_FEC_CODE_LDGM] = LDPC_MAX_N; this->cur_k[MCL_FEC_CODE_LDGM] = LDPC_MAX_K; this->cur_n[MCL_FEC_CODE_LDGM] = min(2*LDPC_MAX_K, LDPC_MAX_N); // default: half data, half fec #endif #ifdef RSE_FEC /* MCL_FEC_CODE_RSE case */ this->fec_codec = MCL_FEC_CODE_RSE; ASSERT(RSE_MAX_K <= RSE_MAX_N); ASSERT(RSE_MAX_N <= GF_SIZE); this->max_k[MCL_FEC_CODE_RSE] = RSE_MAX_K; this->max_n[MCL_FEC_CODE_RSE] = RSE_MAX_N; this->cur_k[MCL_FEC_CODE_RSE] = RSE_MAX_K; this->cur_n[MCL_FEC_CODE_RSE] = min(2 * RSE_MAX_K, RSE_MAX_N); // default: half data, half fec #endif this->max_fratio = this->cur_n[this->fec_codec] / this->cur_k[this->fec_codec]; /* * check it is coherent with the MAX_FEC_RATIO constant * defined in mcl_profile.h */ if (this->max_fratio > MAX_FEC_RATIO) { PRINT_ERR((mcl_stderr, "mcl_fec::initialize: ERROR, the calculated max_fratio(%f) is larger than MAX_FEC_RATIO (%f) for code %d\nCheck and correct the various settings in mcl_profile.h accordingly...\n", this->max_fratio, MAX_FEC_RATIO, this->fec_codec)) mcl_exit(-1); } } /** * Set the FEC code to be used. * This function is sender specific (a receiver gets the FEC * codec information info from the various incoming packet fields). * => See header file for more informations. */ mcl_error_status mcl_fec::set_fec_code (struct mclcb_s *const mclcb, const INT32 new_code) { if (new_code == MCL_FEC_CODE_NULL) { this->fec_codec = MCL_FEC_CODE_NULL; ASSERT(get_max_k() > 0); /* must be true if initialized */ TRACELVL(5, (mcl_stdout, " mcl_fec::set_fec_code: use NO FEC\n")) return MCL_OK; } #ifdef RSE_FEC if (new_code == MCL_FEC_CODE_RSE) { this->fec_codec = MCL_FEC_CODE_RSE; ASSERT(get_max_k() > 0); /* must be true if initialized */ TRACELVL(5, (mcl_stdout, " mcl_fec::set_fec_code: use RSE\n")) return MCL_OK; } #endif #ifdef LDPC_FEC if (new_code == MCL_FEC_CODE_LDGM) { this->fec_codec = MCL_FEC_CODE_LDGM; ASSERT(get_max_k() > 0); /* must be true if initialized */ TRACELVL(5, (mcl_stdout, " mcl_fec::set_fec_code: use LDGM\n")) return MCL_OK; } #if 0 /* not yet! */ else if (new_code == MCL_FEC_CODE_LDPC) { this->fec_codec = MCL_FEC_CODE_LDPC; ASSERT(get_max_k() > 0); /* must be true if initialized */ TRACELVL(5, (mcl_stdout, " mcl_fec::set_fec_code: use LDPC\n")) return MCL_OK; } #endif #endif PRINT_ERR((mcl_stderr, "mcl_fec::set_fec_code: ERROR, code %d unknown\n", new_code)) return MCL_ERROR; } /** * Set the maximum FEC ratio, defined as n/k. * => See header file for more informations. */ mcl_error_status mcl_fec::set_max_fec_ratio (mclcb_t *const mclcb, float max_fec_ratio) { INT32 tmp_max_k; INT32 tmp_cur_k; if (max_fec_ratio < 1.0 || max_fec_ratio > 10.0) { PRINT_ERR((mcl_stderr, "mcl_fec::set_max_fec_ratio: ERROR, bad max_fec_ratio; got %f, should be in [1.0; 10.0]\n", max_fec_ratio)) goto bad; } tmp_max_k = (INT32)((float)this->get_max_n() / max_fec_ratio); tmp_cur_k = (INT32)((float)this->get_n() / max_fec_ratio); if (tmp_max_k < 1 || tmp_cur_k < 1) { PRINT_ERR((mcl_stderr, "mcl_fec::set_max_fec_ratio: ERROR, max_fec_ratio %f too large\n", max_fec_ratio)) goto bad; } this->max_k[this->fec_codec] = tmp_max_k; this->set_k(tmp_cur_k); this->max_fratio = max_fec_ratio; TRACELVL(5, (mcl_stdout, "<- mcl_fec::set_max_fec_ratio: max_k/n=(%d; %d), cur_k/n=(%d; %d)\n", this->get_max_k(), this->get_max_n(), this->get_k(), this->get_n())) return MCL_OK; bad: PRINT_ERR((mcl_stderr, "mcl_fec::set_max_fec_ratio: ERROR, bad max_fec_ratio; got %f, should be in [1.0; 10.0]\n", max_fec_ratio)) return MCL_ERROR; } /** * Return the number of FEC packets that can still be created for this * block. * => See header file for more informations. */ INT32 mcl_fec::get_rem_nb_fec_pkts_to_create (mclcb_t *const mclcb, block_t *blk) { return (this->get_n() - blk->k - blk->fec_du_nb_in_list); } /** * Determine the number of FEC packets required for this block. * Depends on the block size (k) and the kind of scheduling used * for this session. * => See header file for more informations. */ INT32 mcl_fec::compute_n_for_this_block (struct mclcb_s *const mclcb, INT32 k) { ASSERT(k > 0); switch(mclcb->scheduler) { case MCL_SCHED_LCT2: case MCL_SCHED_LCT3: { /* * Computes n in such a way n-k (ie. the number of FEC DUs) * is a multiple of the number of layers - 1. * Has an interest only with small k values so that we can * fill many layers... */ int layers; /* # of layers except layer 0 */ int max_fec_nb; int rounded_fec_nb; if (mclcb->max_level <= 1) { /* only one layer, no FEC as only data is sent on L0 */ TRACELVL(4, (mcl_stdout, " compute_n_for_this_block: no FEC (SCHED_LCT2/3 but single layer)\n")) return k; } /* we can generate that number of fec DUs */ max_fec_nb = this->get_n() - k; /* but it must be limited by k * fec_ratio */ max_fec_nb = min(max_fec_nb, (int)floor(this->max_fratio - 1.0) * k); layers = mcl_get_nb_fec_layers(mclcb, k); if (layers > 0) { /* must be a multiple of the nb of layers */ rounded_fec_nb = (int)(max_fec_nb / layers) * layers; } else rounded_fec_nb = 0; TRACELVL(4, (mcl_stdout, " compute_n_for_this_block: k=%d n=%d, %d FEC DUs for %d FEC layers\n", k, k + rounded_fec_nb, rounded_fec_nb, layers)) return(k + rounded_fec_nb); } case MCL_SCHED_LCT1: default: { int n; n = min ((int)floor(this->max_fratio * k), this->get_n()); TRACELVL(4, (mcl_stdout, " compute_n_for_this_block: k=%d n=%d, %d FEC DUs\n", k, n, n - k)) return(n); } } } /** * Encode the block and create the appropriate number of new FEC packets. * => See header file for more informations. */ INT32 mcl_fec::encode (mclcb_t *const mclcb, block_t *blk) { INT32 k; // effective # source DUs for this block // FEC DU seq nbs follow source DUs INT32 n; // total of n (FEC + source) DUs INT32 fec_seq; // FEC packet sequence number UINT32 du_len; UINT32 last_du_len; INT32 i; #ifdef RSE_FEC void *code; /* ptr to the RSE FEC object */ #endif #ifdef LDPC_FEC LDPCFecSession *ldpc_ses; /* ptr to the FEC session class */ #endif du_t *fec_du; char *fec_data; void **pkt_canvas; /* table of ptrs to packets */ /* With RSE, a table of k entries is enough, */ /* with LDGM, a table of k entries is enough, */ /* BUT with LDPC the table must have n entries*/ du_t *du; if (this->fec_codec == MCL_FEC_CODE_NULL) { TRACELVL(5, (mcl_stdout, " mcl_fec::encode(NULL): useless, no FEC mode, return 0\n")) return 0; } ASSERT(blk); /* * Sanity checks... */ k = blk->k; ASSERT(k <= this->get_k()); n = this->compute_n_for_this_block(mclcb, k); ASSERT(n <= this->get_n()); /* * see if some FEC is still required */ if (n == k) { TRACELVL(5, (mcl_stdout, " mcl_fec::encode(NULL): useless, no FEC required, return 0\n")) return 0; } /* * compute the size of a full-sized DU and of the last DU * which may be shorter */ //du_len = mclcb->get_payload_size(); //du_len = mclcb->payload_size; du_len = blk->adu->symbol_len; last_du_len = (k > 1) ? blk->len % du_len : blk->len; if (last_du_len == 0) last_du_len = du_len; /* multiple */ /* * create all the required FEC DUs for this block */ switch (this->fec_codec) { #ifdef RSE_FEC case MCL_FEC_CODE_RSE: TRACELVL(5, (mcl_stdout, "-> mcl_fec::encode(RSE): blk seq=%d, k=%d, n=%d\n", blk->seq, k, n)) if (!(code = fec_new(k,n))) { goto no_memory; } break; #endif #ifdef LDPC_FEC case MCL_FEC_CODE_LDGM: case MCL_FEC_CODE_LDPC: TRACELVL(5, (mcl_stdout, "-> mcl_fec::encode(LDPC): blk seq=%d, k=%d, n=%d, key=%d\n", blk->seq, k, n, blk->fec_key)) ldpc_ses = new LDPCFecSession; /* * XXX: the initial seed to rand() must be random! Change it * and give the info to the receiver... */ //blk->fec_key = 26; // 16b constant in this version /* * fec_key initialization moved to ADU segmentation function * in order to do it immediately, at ADU submission. * It's important if there's a risk the FTI be transmitted * before mclcb->fec.encode() is called! * Besides the key must be the same for all blocks because * of FTI limitations (only one value carried). */ if (ldpc_ses->InitSession(k, n-k, du_len, FLAG_CODER, min(LEFT_DEGREE, n-k), blk->fec_key, TypeSTAIRS) == LDPC_ERROR) { PRINT_ERR((mcl_stderr, "mcl_fec::encode(LDPC): ERROR, LDPCFecSession::InitSession failed\n")) mcl_exit(-1); } #ifdef DEBUG if (mclcb->verbose >= 2) { blk->ldpc_ses->SetVerbosity(1); } #endif break; #endif } #ifdef VIRTUAL_TX_MEM if (mclcb->vtm_used) { /* * first, move data in memory; assumes VTMEM cache is large * enough to contain the whole block data! */ /* XXX: not efficient, must get it from disk... */ char *data; for (i = 0; i < k; i++) { data = mcl_vtm_get_data(mclcb, &(blk->du_head[i])); ASSERT(data); /* check that VTMEM cache is sufficiently large */ if (blk->du_head[i].data == NULL) { PRINT_ERR((mcl_stderr, "mcl_fec::encode: ERROR, Virtual Tx Memory cache too small\n")) mcl_exit(-1); } } } #endif /* VIRTUAL_TX_MEM */ /* * no data copy except for the last du which may be shorter. * for the last DU, (c)allocate a block, of "du_len" size, and copy * the "last_du_len" bytes in it. */ switch (this->fec_codec) { #ifdef RSE_FEC case MCL_FEC_CODE_RSE: if (!(pkt_canvas = (void**)malloc(k * sizeof(char*)))) { goto no_memory; } break; #endif #ifdef LDPC_FEC case MCL_FEC_CODE_LDGM: case MCL_FEC_CODE_LDPC: if (!(pkt_canvas = (void**)calloc(n, sizeof(char*)))) { goto no_memory; } break; #endif } for (i = 0, du = blk->du_head; i < k - 1; i++, du++) { pkt_canvas[i] = (void*)(du->data); } if (!(pkt_canvas[k-1] = (void*)calloc(1, du_len))) { goto no_memory; } ASSERT(du->seq == (UINT32)k-1); ASSERT(du->len == last_du_len); memcpy(pkt_canvas[k-1], du->data, last_du_len); /* * do it simply: allocate a tab of FEC du_t structs * rather than a linked list! * XXX: cannot be used if additional FEC packets are created later... */ ASSERT(blk->fec_du_head == NULL); if (!(blk->fec_du_head = (du_t*)calloc(n-k, sizeof(du_t)))) { goto no_memory; } blk->fec_du_nb_in_list = n-k; /* n-k fec added */ /* * now create the n-k FEC DUs */ for (fec_seq = k, fec_du = blk->fec_du_head; fec_seq < n; fec_seq++, fec_du++) { if (!(fec_data = (char *)malloc(du_len))) { goto no_memory; } switch (this->fec_codec) { #ifdef RSE_FEC case MCL_FEC_CODE_RSE: fec_encode(code, pkt_canvas, fec_data, fec_seq, du_len); break; #endif #ifdef LDPC_FEC case MCL_FEC_CODE_LDGM: case MCL_FEC_CODE_LDPC: //TRACE((mcl_stdout, // "mcl_fec::encode: fec_seq=%d, fec_data=x%x, du_len=%d\n", // fec_seq, (int)fec_data, du_len)) ldpc_ses->BuildFecPacket(pkt_canvas, fec_seq - k, (void*)fec_data); // store packet in the canvas pkt_canvas[fec_seq] = (void*)(fec_data); break; #endif } //fec_du = new mcl_du; // fec_du->next = fec_du->prev = NULL; fec_du->block = blk; fec_du->data = fec_data; fec_du->seq = fec_seq; fec_du->len = du_len; fec_du->is_fec = true; //fec_du->pkt = NULL; // this is how a tx DU is distinguished // from a rx DU! //fec_du->set_next(NULL); //fec_du->set_prev(NULL); #if 0 /* * Use a linked list of FEC DUs rather than a single tab as * with data DUs on the sending side! */ if (blk->insert_in_fec_du_list(mclcb, fec_du) == MCL_ERROR) { PRINT_ERR((mcl_stderr, "mcl_fec::encode: ERROR, insert_in_fec_du_list() failed\n")) mcl_exit(-1); } #endif #ifdef VIRTUAL_TX_MEM fec_du->vtm_info.du_in_seq_in_txtab = 1; /* by default */ if (mcl_vtm_can_store_in_vtm(mclcb, (int)du_len)) { /* * use the VTMEM service to register data */ if (mcl_vtm_store_data(mclcb, NULL, fec_du, fec_data, (int)du_len, 0)) { PRINT_ERR((mcl_stderr, "mcl_sendto: ERROR: Virtual Tx Memory service failed\n")) mcl_exit(-1); } free(fec_data); /* no longer needed */ } else { #endif /* VIRTUAL_TX_MEM */ /* * store in physical memory */ fec_du->data = fec_data; #ifdef VIRTUAL_TX_MEM if (mclcb->vtm_used) { /* remember it is kept in physical memory */ mcl_vtm_register_in_ptm(mclcb, NULL, fec_du, (int)du_len); } } #endif /* VIRTUAL_TX_MEM */ TRACELVL(5, (mcl_stdout, " mcl_fec::encode: created FEC seq=%d, len=%d, buf=x%x\n", (INT32)fec_du->seq, (INT32)fec_du->len, (INT32)fec_du->data)) } free(pkt_canvas[k-1]); free(pkt_canvas); switch (this->fec_codec) { #ifdef RSE_FEC case MCL_FEC_CODE_RSE: fec_free(code); break; #endif #ifdef LDPC_FEC case MCL_FEC_CODE_LDGM: case MCL_FEC_CODE_LDPC: ldpc_ses->EndSession(); delete ldpc_ses; break; #endif } TRACELVL(5, (mcl_stdout, "<- mcl_fec::encode: %d fec_du\n", n - k)) return (n - k); no_memory: PRINT_ERR((mcl_stderr, "mcl_fec::encode: ERROR, no memory\n")) mcl_exit(-1); return -1; // useless but avoids a warning } #ifdef RSE_FEC /** * Decode the block immediately. * This function is for Reed-Solomon and similar FEC codes. * => See header file for more informations. */ INT32 mcl_fec::decode (mclcb_t *const mclcb, block_t *blk) { INT32 k; INT32 n; UINT32 du_len; /* full-sized DU length */ UINT32 last_du_len; /* last DU (true) length */ INT32 i; u_char **dst; /* put rx data or fec here... */ INT32 rx_index[GF_SIZE]; /* ... and their seq# here */ UINT32 rem; /* remaining nb of DUs */ void *code; //mcl_du *du; du_t *du; du_t *ndu; UINT32 seq; /* next seq number expected */ char *data; /* data buffer */ mcl_rx_pkt *pkt; /* used for data DUs created during decoding */ u_char *buf; /* buffer where data/FEC DU data is copied */ INT32 off; /* offset */ ASSERT(blk); TRACELVL(5, (mcl_stdout, "-> mcl_fec::decode(RSE): block seq=%d\n",blk->seq)) k = blk->k; /* * check that FEC decode is indeed needed (i.e. that * we need at least one FEC DU) */ //if (blk->get_du_nb_in_list() >= k) if ((int)blk->du_rx >= k) { //blk->set_rx_status(BLK_STATUS_DECODED); blk->rx_status = BLK_STATUS_DECODED; TRACELVL(5, (mcl_stdout, " mcl_fec::decode(RSE): decoding not needed\n")) /*TRACELVL(5, ("mcl_fec::decode(RSE): k=%d, du_in_list=%d, fec_in_list=%d\n", k, blk->get_du_nb_in_list(), blk->get_fec_du_nb_in_list()))*/ goto free_fec_du; } /* * compute the size of a full-sized DU and of the last DU * which may be shorter */ //du_len = mclcb->get_payload_size(); //du_len = mclcb->payload_size; du_len = blk->adu->symbol_len; last_du_len = (k > 1) ? blk->len % du_len : blk->len; if (last_du_len == 0) last_du_len = du_len; /* multiple */ /* * copy data and fec in dst buffer array first * NB: this is required by the current FEC codec which modifies * the dst buffers!!! */ if (!(dst = (u_char **) malloc(k * sizeof(u_char *)))) { goto no_memory; } /* alloc a single large buf, where to copy all data/FEC DU data... */ if (!(buf = (u_char *) malloc(k * du_len))) { goto no_memory; } /* and remember the location of each DU buffer */ for (i = 0, off = 0; i < k; i++, off += du_len) { dst[i] = buf + off; } i = 0; /* index in rx_index[] */ /* * copy data DU to the dst array first */ //for (rem = blk->get_du_nb_in_list(), du = blk->get_du_head(); // rem > 0; i++, rem--, du = du->get_next()) for (rem = blk->du_rx, du = blk->du_head; rem > 0; i++, rem--, du = du->next) { ASSERT(du); /* WARNING: if the following test failed, you probably forgot */ /* to set the same tx_profile at the source AND receiver */ if (du->len != du_len && du->len != last_du_len) { PRINT_ERR((mcl_stderr, "mcl_fec::decode(RSE): ERROR: bad packet len (expected %d or %d, got %d)\nCheck transmission profiles at sender/receiver\n", du_len, last_du_len, du->len)) goto fatal_error; } if (i >= k) break; /* security, eg. if there are more than k DUs */ #ifdef VIRTUAL_RX_MEM if (mcl_vrm_in_vrm(mclcb, du)) { mcl_vrm_get_data(mclcb, du, (char*)dst[i], du->len); } else #endif memcpy(dst[i], du->data, du->len); if (du->len < du_len) { /* non full-sized DU, so reset the remaining bytes */ memset(dst[i] + du->len, 0, du_len - du->len); } rx_index[i] = du->seq; #if 0 PRINT_OUT((mcl_stdout, "mcl_fec::decode(RSE): copy DATA du->seq=%d in dst[%d], rx_index[%d]=%d\n", du->seq, i, i, rx_index[i])) #endif } /* * copy FEC DUs to the dst array now */ //for (rem = blk->get_fec_du_nb_in_list(), du = blk->get_fec_du_head(); // rem > 0; i++, rem--, du = du->get_next()) for (rem = blk->fec_du_nb_in_list, du = blk->fec_du_head; rem > 0; i++, rem--, du = du->next) { ASSERT(du); ASSERT(du->len == du_len); if (i >= k) { /* security, if there are more than k data+FEC DUs */ break; } #ifdef VIRTUAL_RX_MEM if (mcl_vrm_in_vrm(mclcb, du)) { mcl_vrm_get_data(mclcb, du, (char*)dst[i], du->len); } else #endif memcpy(dst[i], du->data, du->len); /* rx_index[i] = du->seq + k; */ rx_index[i] = du->seq; #if 0 PRINT_OUT((mcl_stdout, "mcl_fec::decode(RSE): copy FEC du->seq=%d in dst[%d], rx_index[%d]=%d\n", du->seq, i, i, rx_index[i])) #endif } /* * now decode * NB: all the dst packets must be du_len long */ n = this->compute_n_for_this_block(mclcb, k); code = fec_new(k, n); if (fec_decode(code, (void**)dst, rx_index, du_len)) { PRINT_ERR((mcl_stderr, "mcl_fec::decode(RSE): ERROR, fec_decode failed\n")) //fec_free(code); return -1; } fec_free(code); /* * now update the block with the rx or reconstructed DUs */ //for (rem = blk->k, seq = 0, du = blk->get_du_head(); // rem > 0; rem--, seq++, du = du->get_next()) for (rem = blk->k, seq = 0, du = blk->du_head; rem > 0; rem--, seq++, du = du->next) { if (du && du->seq == seq) { /* nothing to do, DU already received */ continue; } /* * else copy it from the FEC decoded matrix */ //du = new mcl_du; du = CreateDU(mclcb); /* next = prev = NULL */ du->block = blk; du->seq = seq; if (rem > 1) du->len = du_len; else du->len = last_du_len; du->is_fec = false; //du->set_next(NULL); //du->set_prev(NULL); /* * copy/store data now */ #ifdef VIRTUAL_RX_MEM if (mcl_vrm_can_store_in_vrm(mclcb, du->len)) { /* use the VRMEM service to register data */ if (mcl_vrm_store_data(mclcb, du, (char*)dst[seq], du->len)) { PRINT_ERR((mcl_stderr, "mcl_fec::decode(RSE): ERROR: Virtual Rx Memory service failed\n")) mcl_exit(-1); } } else { #endif /* VIRTUAL_RX_MEM */ pkt = new mcl_rx_pkt (du->len); ASSERT(pkt); pkt->pkt_len = du->len; du->pkt = pkt; du->data = pkt->get_buf(); memcpy(du->data, dst[seq], du->len); #ifdef VIRTUAL_RX_MEM if (mclcb->vrm_used) { /* remember it is kept in physical memory */ mcl_vrm_register_in_prm(mclcb, du, du->len); } } #endif /* VIRTUAL_RX_MEM */ /* * and insert it in the data DU list */ //if (blk->insert_in_du_list(mclcb, du) == MCL_ERROR) { // PRINT_ERR((mcl_stderr, "mcl_fec::decode(RSE): ERROR, insert_in_fec_du_list() failed\n")) // mcl_exit(-1); //} InsertDU(mclcb, du, &(blk->du_head)); TRACELVL(5, (mcl_stdout, " mcl_fec::decode(RSE): created DU seq=%d from dst[%d], len=%d, data buffer=x%x, du->data=x%x\n", du->seq, seq, du->len, (int)data, (int)du->data)) #if 0 mcl_dump_buffer(data, sizeof(char*) + du->len, (sizeof(char*) + du->len)>>2); #endif } //blk->set_rx_status(BLK_STATUS_DECODED); blk->rx_status = BLK_STATUS_DECODED; /* * free the dst[] array and fec DU list now */ /* * Warning: the fec_decode function can shuffle dst[] entries, so * the new dst[0] entry may not point to buffer allocated previously! */ free(buf); free(dst); free_fec_du: //blk->remove_and_free_all_fec_dus(mclcb); for (rem = blk->fec_du_nb_in_list, du = blk->fec_du_head; rem > 0; rem--, du = ndu) { ndu = du->next; #ifdef VIRTUAL_RX_MEM if (mclcb->vrm_used && !mcl_vrm_in_vrm(mclcb, du)) { mcl_vrm_remove_from_prm(mclcb, du, du->len); ASSERT(du->pkt); delete du->pkt; du->pkt = NULL; /* security */ du->data = NULL; /* security */ } else if (!mclcb->vrm_used) { #endif /* VIRTUAL_RX_MEM */ ASSERT(du->pkt); delete du->pkt; du->pkt = NULL; /* security */ du->data = NULL; /* security */ #ifdef VIRTUAL_RX_MEM } #endif /* VIRTUAL_RX_MEM */ free(du); } blk->fec_du_head = NULL; blk->fec_du_nb_in_list = 0; TRACELVL(5, (mcl_stdout, "<- mcl_fec::decode(RSE): ok\n")) return 0; no_memory: PRINT_ERR((mcl_stderr, "mcl_fec::decode(RSE): ERROR, no memory")) mcl_exit(-1); fatal_error: PRINT_ERR((mcl_stderr, "mcl_fec::decode(RSE): ERROR")) mcl_exit(-1); return -1; /* unreachable; avoid a compiler warning */ } #endif /* RSE_FEC */ #ifdef LDPC_FEC /** * Progress in the block decoding with the given packet. * This function is for LDPC and similar FEC codes who use an * iterative decoding approach. * => See header file for more informations. */ INT32 mcl_fec::decode (mclcb_t *const mclcb, du_t *rx_du) { block_t *blk; UINT32 k; UINT32 n; UINT32 du_len; // full-sized DU length //UINT32 last_du_len; // last DU (true) length //INT32 i; INT32 rem; // remaining nb of DUs in iter loop du_t *du; // current DU in DU iteration loop du_t *ndu; // next DU in the DU iteration loop ASSERT(rx_du); blk = rx_du->block; ASSERT(blk); TRACELVL(5, (mcl_stdout, "-> mcl_fec::decode(LDPC): du->seq=%d, block seq=%d\n", rx_du->seq, blk->seq)) k = blk->k; //n = this->compute_n_for_this_block(mclcb, k); n = blk->n; ASSERT(n >= k); /* * check that FEC decode is indeed needed (i.e. that * we need at least one FEC DU) */ if (k == n && blk->du_rx >= k) { /* * decoding is over for this block... */ //blk->set_rx_status(BLK_STATUS_DECODED); blk->rx_status = BLK_STATUS_DECODED; ASSERT(blk->ldpc_ses == NULL); ASSERT(blk->pkt_canvas == NULL); ASSERT(blk->callback_ctxt == NULL); ASSERT(blk->fec_du_nb_in_list == 0); ASSERT(blk->fec_du_head == NULL); TRACELVL(5, (mcl_stdout, "<- mcl_fec::decode(LDPC): ok but FEC not used\n")) return 1; } /* * LDPC initialization if applicable */ if (blk->ldpc_ses == NULL) { /* * first call to this LDPC decoding function for this * block, so create the LDCP context and packet table. */ //du_len = mclcb->get_payload_size(); //du_len = mclcb->payload_size; du_len=blk->adu->symbol_len; ASSERT(blk->pkt_canvas == NULL); blk->ldpc_ses = new LDPCFecSession; /* * XXX: the initial seed to rand() must be random! Change it * and give the info to the receiver... */ if (blk->ldpc_ses->InitSession(k, n-k, du_len, FLAG_DECODER, min(LEFT_DEGREE, n-k), blk->fec_key, TypeSTAIRS) == LDPC_ERROR) { PRINT_ERR((mcl_stderr, "mcl_fec::decode(LDPC): ERROR, LDPCFecSession::InitSession failed\n")) mcl_exit(-1); } #ifdef DEBUG if (mclcb->verbose >= 2) { blk->ldpc_ses->SetVerbosity(1); } #endif if (blk->ldpc_ses->SetCallbackFunctions( //this->dec_malloc_callback, this->decoded_pkt_callback) == LDPC_ERROR) { PRINT_ERR((mcl_stderr, "mcl_fec::decode(LDPC): ERROR, LDPCFecSession::SetCallbackFunctions failed\n")) mcl_exit(-1); } if (!(blk->pkt_canvas = (void **)calloc(n, sizeof(void*)))) { goto no_memory; } if (!(blk->callback_ctxt = (ldpc_callback_context_t*)malloc(sizeof(ldpc_callback_context_t)))) { goto no_memory; } blk->callback_ctxt->mclcb = mclcb; blk->callback_ctxt->block = blk; } /* * now decode... */ if (blk->ldpc_ses->DecodeFecStep(blk->pkt_canvas, rx_du->data, rx_du->seq, false, blk->callback_ctxt) == LDPC_ERROR) { PRINT_ERR((mcl_stderr, "mcl_fec::decode(LDPC): ERROR, DecodeFecStep failed\n")) mcl_exit(-1); } /* * check if decoding is complete... */ if (blk->du_rx >= k && blk->ldpc_ses->IsDecodingComplete(blk->pkt_canvas)) { /* * decoding is over for this block, free all FEC DUs now... */ //blk->set_rx_status(BLK_STATUS_DECODED); blk->rx_status = BLK_STATUS_DECODED; blk->ldpc_ses->EndSession(); delete blk->ldpc_ses; blk->ldpc_ses = NULL; ASSERT(blk->pkt_canvas); free(blk->pkt_canvas); blk->pkt_canvas = NULL; ASSERT(blk->callback_ctxt); free(blk->callback_ctxt); blk->callback_ctxt = NULL; /* free all the FEC DUs now */ //blk->remove_and_free_all_fec_dus(mclcb); for (rem = blk->fec_du_nb_in_list, du = blk->fec_du_head; rem > 0; rem--, du = ndu) { ndu = du->next; ASSERT(du->is_fec); delete du->pkt; du->data = NULL; /* security */ free(du); } blk->fec_du_head = NULL; blk->fec_du_nb_in_list = 0; TRACELVL(5, (mcl_stdout, "<- mcl_fec::decode(LDPC): ok\n")) return 1; } /* * not finished... keep state for next call to this function */ TRACELVL(5, (mcl_stdout, "<- mcl_fec::decode(LDPC): not finished\n")) return 0; no_memory: PRINT_ERR((mcl_stderr, "mcl_fec::decode(LDPC): ERROR, no memory")) mcl_exit(-1); return -1; /* unreachable; avoid a compiler warning */ } #if 0 /** * This callback function will be called each time the * DecodeFecStep() function needs to allocate a buffer. */ void * mcl_fec::dec_malloc_callback (int size, void *context) { mcl_rx_pkt *pkt; // buffer pkt = new mcl_rx_pkt (size); ASSERT(pkt); pkt->pkt_len = size; return (pkt->buf); } #endif /** * This callback function will be called each time a packet * is decoded by the DecodeFecStep() function. * => See header file for more informations. */ void * mcl_fec::decoded_pkt_callback (INT32 size, INT32 pkt_seq, void *context) { mclcb_t *mclcb; // associated mclcb block_t *blk; // associated block mcl_rx_pkt *pkt; // packet buffer du_t *du; // DU associated to pkt to create UINT32 du_len; // full-sized DU length UINT32 last_du_len; // last DU (true) length int err; ASSERT(context); mclcb = ((ldpc_callback_context_t*)context)->mclcb; ASSERT(mclcb); blk = ((ldpc_callback_context_t*)context)->block; ASSERT(blk); TRACELVL(5, (mcl_stdout, "-> mcl_fec::decoded_pkt_callback:\n")) pkt = new mcl_rx_pkt (size); ASSERT(pkt); pkt->pkt_len = size; //du = new mcl_du; du = CreateDU(mclcb); /* next = prev = NULL */ du->block = blk; du->seq = pkt_seq; /* * compute the size of a full-sized DU and of the last DU * which may be shorter */ //du_len = mclcb->get_payload_size(); //du_len = mclcb->payload_size; du_len=blk->adu->symbol_len; if (pkt_seq == (int)blk->k - 1) { /* last packet of the block, may be shorter */ last_du_len = (blk->k > 1) ? blk->len % du_len : blk->len; if (last_du_len == 0) last_du_len = du_len; /* multiple */ du->len = last_du_len; } else { /* full sized packet */ du->len = du_len; } du->is_fec = (pkt_seq >= (int)blk->k) ? true : false; //du->set_next(NULL); //du->set_prev(NULL); du->data = pkt->get_buf(); /* * and insert it in the data DU list */ if (du->is_fec) { err = InsertDU(mclcb, du, &(blk->fec_du_head)); ASSERT(err == 1); blk->fec_du_nb_in_list++; } else { err = InsertDU(mclcb, du, &(blk->du_head)); ASSERT(err == 1); blk->du_rx++; } //if (blk->insert_in_du_list(mclcb, du) == MCL_ERROR) { // PRINT_ERR((mcl_stderr, "mcl_fec::decode(RSE): ERROR, insert_in_fec_du_list() failed\n")) // mcl_exit(-1); //} //TRACELVL(5, (mcl_stdout, TRACELVL(2, (mcl_stdout, "<- mcl_fec::decoded_pkt_callback: created aseq=%d/bseq=%d/dseq=%d %s\n", blk->adu->seq, blk->seq, du->seq, (du->is_fec ? "FEC" : "DATA"))) return (du->data); } #endif /* LDPC_FEC */