![]() |
ccsoft
0.0.0
Convolutional codes library with soft decision decoding
|
The Fano like Decoding class. Tag is a boolean used as the traversed back indicator. This version uses fixed arrays to store registers and forward node+edges pointers. N_k template parameter gives the size of the input symbol (k parameter) and therefore the number of registers. There are (1<<N_k) forward node+edges. More...
#include <CC_FanoDecoding_FA.h>
Public Member Functions | |
CC_FanoDecoding_FA (const std::vector< unsigned int > &constraints, const std::vector< std::vector< T_Register > > &genpoly_representations, float _init_threshold, float _delta_threshold, unsigned int _tree_cache_size=0, float _delta_init_threshold=0.0) | |
virtual | ~CC_FanoDecoding_FA () |
void | set_tree_cache_size (unsigned int _tree_cache_size) |
void | reset () |
virtual bool | decode (const CC_ReliabilityMatrix &relmat, std::vector< T_IOSymbol > &decoded_message) |
virtual void | print_stats (std::ostream &os, bool success) |
virtual void | print_dot (std::ostream &os) |
Protected Types | |
typedef CC_SequentialDecoding_FA < T_Register, T_IOSymbol, N_k > | Parent |
Parent class this class inherits from. | |
typedef CC_SequentialDecodingInternal_FA < T_Register, T_IOSymbol, bool, N_k > | ParentInternal |
Parent class this class inherits from. | |
typedef CC_TreeNodeEdge_FA < T_IOSymbol, T_Register, bool, N_k > | FanoNodeEdge |
Class of code tree nodes in the Fano algorithm. | |
Protected Member Functions | |
virtual void | visit_node_forward (FanoNodeEdge *node_edge, const CC_ReliabilityMatrix &relmat) |
FanoNodeEdge * | move_back_from_node_or_loosen_threshold (FanoNodeEdge *node_edge_current) |
bool | continue_process (FanoNodeEdge *node_edge_current, const CC_ReliabilityMatrix &relmat) |
void | purge_tree_cache (FanoNodeEdge *node_edge) |
Protected Attributes | |
float | init_threshold |
Initial path metric threshold. | |
float | cur_threshold |
Current path metric threshold. | |
float | delta_threshold |
Delta of path metric that is applied when lowering threshold. | |
bool | solution_found |
Set to true when eligible terminal node is found. | |
unsigned int | effective_node_count |
Count of nodes effectively present in the system. | |
unsigned int | nb_moves |
Number of moves i.e. number of iterations in the main loop. | |
float | root_threshold |
Latest threshold at root node. | |
unsigned int | tree_cache_size |
Tree cache size in maximum number of nodes in cache (0 = tree is not cached) | |
bool | unloop |
If true when a loop condition is detected attempt to restart with a lower threshold. | |
float | delta_init_threshold |
Delta of path metric that is applied when restarting with a lower initial threshold. |
The Fano like Decoding class. Tag is a boolean used as the traversed back indicator. This version uses fixed arrays to store registers and forward node+edges pointers. N_k template parameter gives the size of the input symbol (k parameter) and therefore the number of registers. There are (1<<N_k) forward node+edges.
T_Register | Type of the encoder internal registers |
T_IOSymbol | Type of the input and output symbols |
N_k | Size of an input symbol in bits (k parameter) |
typedef CC_TreeNodeEdge_FA<T_IOSymbol, T_Register, bool, N_k> ccsoft::CC_FanoDecoding_FA< T_Register, T_IOSymbol, N_k >::FanoNodeEdge [protected] |
Class of code tree nodes in the Fano algorithm.
typedef CC_SequentialDecoding_FA<T_Register, T_IOSymbol, N_k> ccsoft::CC_FanoDecoding_FA< T_Register, T_IOSymbol, N_k >::Parent [protected] |
Parent class this class inherits from.
typedef CC_SequentialDecodingInternal_FA<T_Register, T_IOSymbol, bool, N_k> ccsoft::CC_FanoDecoding_FA< T_Register, T_IOSymbol, N_k >::ParentInternal [protected] |
Parent class this class inherits from.
ccsoft::CC_FanoDecoding_FA< T_Register, T_IOSymbol, N_k >::CC_FanoDecoding_FA | ( | const std::vector< unsigned int > & | constraints, |
const std::vector< std::vector< T_Register > > & | genpoly_representations, | ||
float | _init_threshold, | ||
float | _delta_threshold, | ||
unsigned int | _tree_cache_size = 0 , |
||
float | _delta_init_threshold = 0.0 |
||
) | [inline] |
Constructor
constraints | Vector of register lengths (constraint length + 1). The number of elements determines k. |
genpoly_representations | Generator polynomial numeric representations. There are as many elements as there are input bits (k). Each element is itself a vector with one polynomial value per output bit. The smallest size of these vectors is retained as the number of output bits n. The input bits of a symbol are clocked simultaneously into the right hand side, or least significant position of the internal registers. Therefore the given polynomial representation of generators should follow the same convention. |
_init_threshold | Initial path metric threshold |
_delta_threshold | Delta of path metric that is applied when lowering threshold |
_tree_cache_size | Tree cache maximum size in number of nodes (0 if not used) |
_delta_init_threshold,: | Delta of path metric that is applied when restarting with a lower initial threshold (0 if not used) |
: CC_SequentialDecoding_FA<T_Register, T_IOSymbol, N_k>(constraints, genpoly_representations), CC_SequentialDecodingInternal_FA<T_Register, T_IOSymbol, bool, N_k>(), init_threshold(_init_threshold), cur_threshold(_init_threshold), root_threshold(_init_threshold), delta_threshold(_delta_threshold), solution_found(false), effective_node_count(0), nb_moves(0), tree_cache_size(_tree_cache_size), unloop(_delta_init_threshold < 0.0), delta_init_threshold(_delta_init_threshold) {}
virtual ccsoft::CC_FanoDecoding_FA< T_Register, T_IOSymbol, N_k >::~CC_FanoDecoding_FA | ( | ) | [inline, virtual] |
Destructor. Does a final garbage collection
{}
bool ccsoft::CC_FanoDecoding_FA< T_Register, T_IOSymbol, N_k >::continue_process | ( | FanoNodeEdge * | node_edge_current, |
const CC_ReliabilityMatrix & | relmat | ||
) | [inline, protected] |
Check if process can continue
{ if ((node_edge_current == ParentInternal::root_node) && (nb_moves > 0) && (cur_threshold == root_threshold)) { const std::array<FanoNodeEdge*, (1<<N_k)>& outgoing_node_edges = node_edge_current->get_outgoing_node_edges(); typename std::array<FanoNodeEdge*, (1<<N_k)>::const_iterator ne_it = outgoing_node_edges.begin(); bool children_open = true; for (; ne_it != outgoing_node_edges.end(); ++ne_it) { if ((*ne_it)->get_tag()) // traversed back { children_open = false; break; } } if (children_open) { if (unloop && ((Parent::use_metric_limit) && (init_threshold > Parent::metric_limit))) { init_threshold += delta_init_threshold; // lower initial threshold and start all over again (delta if used is negative) Parent::reset(); // reset but do not delete root node cur_threshold = init_threshold; solution_found = false; ParentInternal::root_node->delete_outgoing_node_edges(); // effectively resets the root node without destroying it Parent::node_count = 1; effective_node_count = 1; nb_moves = 0; visit_node_forward(node_edge_current, relmat); // visit root node again std::cerr << "Loop condition detected, restart with init threshold = " << init_threshold << std::endl; return true; } else { std::cerr << "Loop condition detected, aborting" << std::endl; return false; } } } if ((Parent::use_metric_limit) && (cur_threshold < Parent::metric_limit)) { std::cerr << "Metric limit encountered" << std::endl; return false; } if ((Parent::use_node_limit) && (Parent::node_count > Parent::node_limit)) { std::cerr << "Node limit exhausted" << std::endl; return false; } return true; }
virtual bool ccsoft::CC_FanoDecoding_FA< T_Register, T_IOSymbol, N_k >::decode | ( | const CC_ReliabilityMatrix & | relmat, |
std::vector< T_IOSymbol > & | decoded_message | ||
) | [inline, virtual] |
Decodes given the reliability matrix. Algorithm reproduced from Sequential Decoding of Convolutional Codes by Yunghsiang S. Han and Po-Ning Chen p.26 relmat Reference to the reliability matrix decoded_message Vector of symbols of retrieved message
Implements ccsoft::CC_SequentialDecoding_FA< T_Register, T_IOSymbol, N_k >.
{ FanoNodeEdge *node_edge_current, *node_edge_successor; if (relmat.get_message_length() < Parent::encoding.get_m()) { throw CCSoft_Exception("Reliability Matrix should have a number of columns at least equal to the code constraint"); } if (relmat.get_nb_symbols_log2() != Parent::encoding.get_n()) { throw CCSoft_Exception("Reliability Matrix is not compatible with code output symbol size"); } reset(); ParentInternal::init_root(); // initialize root node Parent::node_count++; effective_node_count++; node_edge_current = ParentInternal::root_node; nb_moves = 0; #ifdef _DEBUG timespec time1, time2; int time_option = CLOCK_REALTIME; clock_gettime(time_option, &time1); #endif visit_node_forward(node_edge_current, relmat); while (continue_process(node_edge_current, relmat)) { //std::cout << "T=" << cur_threshold << " depth=" << node_current->get_depth() << " node #" << node_current->get_id() << " Mc=" << node_current->get_path_metric() << std::endl; DEBUG_OUT(Parent::verbosity > 1, "T=" << cur_threshold << " depth=" << node_edge_current->get_depth() << " node #" << node_edge_current->get_id() << " Mc=" << node_edge_current->get_path_metric() << std::endl); if (node_edge_current->get_depth() > Parent::max_depth) { Parent::max_depth = node_edge_current->get_depth(); } if (node_edge_current == ParentInternal::root_node) { root_threshold = cur_threshold; } nb_moves++; const std::array<FanoNodeEdge*, (1<<N_k)>& outgoing_node_edges = node_edge_current->get_outgoing_node_edges(); typename std::array<FanoNodeEdge*, (1<<N_k)>::const_iterator ne_it = outgoing_node_edges.begin(); std::vector<FanoNodeEdge*> child_node_edges; for (; ne_it != outgoing_node_edges.end(); ++ne_it) { if ((*ne_it) && !((*ne_it)->get_tag())) // not traversed back { child_node_edges.push_back(*ne_it); } } if (child_node_edges.size() == 0) // exhausted forward paths { DEBUG_OUT(Parent::verbosity > 2, "exhaustion of forward paths at node #" << node_edge_current->get_id() << std::endl); node_edge_current = move_back_from_node_or_loosen_threshold(node_edge_current); continue; } std::sort(child_node_edges.begin(), child_node_edges.end(), node_edge_pointer_ordering<FanoNodeEdge>); node_edge_successor = *child_node_edges.begin(); // best successor DEBUG_OUT(Parent::verbosity > 2, "best successor node #" << node_edge_successor->get_id() << " Ms=" << node_edge_successor->get_path_metric() << std::endl); if (node_edge_successor->get_path_metric() >= cur_threshold) // Ms >= T { // move forward: DEBUG_OUT(Parent::verbosity > 2, "forward" << std::endl); FanoNodeEdge *node_predecessor = node_edge_current; node_edge_current = node_edge_successor; // termination with solution if (node_edge_current->get_depth() == relmat.get_message_length() - 1) { Parent::codeword_score = node_edge_current->get_path_metric(); ParentInternal::back_track(node_edge_current, decoded_message, true); // back track from terminal node to retrieve decoded message solution_found = true; Parent::max_depth++; #ifdef _DEBUG clock_gettime(time_option, &time2); DEBUG_OUT(Parent::verbosity > 0, std::cout << "Decoding time: " << std::setw(12) << std::setprecision(9) << debug_get_time_difference(time2,time1) << " s" << std::endl); #endif return true; } // threshold tightening for the new current node if (node_predecessor->get_path_metric() < cur_threshold + delta_threshold) { int nb_delta = int((node_edge_current->get_path_metric() - init_threshold) / delta_threshold); if (nb_delta < 0) { cur_threshold = ((nb_delta - 1) * delta_threshold) + init_threshold; } else { cur_threshold = (nb_delta * delta_threshold) + init_threshold; } DEBUG_OUT(Parent::verbosity > 2, "tightening " << node_edge_current->get_path_metric() << " -> " << cur_threshold << std::endl); } // create children nodes from the new current node visit_node_forward(node_edge_current, relmat); } else { node_edge_current = move_back_from_node_or_loosen_threshold(node_edge_current); } } return false; }
FanoNodeEdge* ccsoft::CC_FanoDecoding_FA< T_Register, T_IOSymbol, N_k >::move_back_from_node_or_loosen_threshold | ( | FanoNodeEdge * | node_edge_current | ) | [inline, protected] |
Chooses between moving back from the node or loosen threshold Before moving back it deletes all successors of the node (edges and nodes) and it marks the incoming edge as traversed back
node_current | Node to move backfrom or where to loosen threshold |
{ if (node_edge_current == ParentInternal::root_node) // at root node there are no other options than loosening threshold { cur_threshold -= delta_threshold; DEBUG_OUT(Parent::verbosity > 2, "loosening " << node_edge_current->get_path_metric() << " -> " << cur_threshold << std::endl); } else { FanoNodeEdge *node_edge_predecessor = node_edge_current->get_incoming_node_edge(); if (node_edge_predecessor->get_path_metric() >= cur_threshold) // move backward { DEBUG_OUT(Parent::verbosity > 2, std::cout << "backward" << std::endl); if (tree_cache_size == 0) // tree cache is not used { // delete all successor edges and nodes std::array<FanoNodeEdge*, (1<<N_k)>& outgoing_node_edges = node_edge_current->get_outgoing_node_edges(); typename std::array<FanoNodeEdge*, (1<<N_k)>::iterator ne_it = outgoing_node_edges.begin(); for (;ne_it != outgoing_node_edges.end(); ++ne_it) { delete *ne_it; } effective_node_count -= outgoing_node_edges.size(); std::fill(outgoing_node_edges.begin(), outgoing_node_edges.end(), (FanoNodeEdge*) 0); //outgoing_node_edges.fill(0); } // mark incoming edge as traversed back if (node_edge_predecessor != ParentInternal::root_node) { node_edge_current->get_tag() = true; } // move back: change node address to previous node address node_edge_current = node_edge_predecessor; } else // loosen threshold { cur_threshold -= delta_threshold; DEBUG_OUT(Parent::verbosity > 2, "loosening " << node_edge_current->get_path_metric() << " -> " << cur_threshold << std::endl); } } return node_edge_current; }
virtual void ccsoft::CC_FanoDecoding_FA< T_Register, T_IOSymbol, N_k >::print_dot | ( | std::ostream & | os | ) | [inline, virtual] |
Print the dot (Graphviz) file of the current decode tree to an output stream
os | Output stream |
Implements ccsoft::CC_SequentialDecoding_FA< T_Register, T_IOSymbol, N_k >.
{ ParentInternal::print_dot_internal(os); }
virtual void ccsoft::CC_FanoDecoding_FA< T_Register, T_IOSymbol, N_k >::print_stats | ( | std::ostream & | os, |
bool | success | ||
) | [inline, virtual] |
Print stats to an output stream
os | Output stream |
success | True if decoding was successful |
Implements ccsoft::CC_SequentialDecoding_FA< T_Register, T_IOSymbol, N_k >.
{ std::cout << "score = " << Parent::get_score() << " cur.threshold = " << cur_threshold << " nodes = " << Parent::get_nb_nodes() << " eff.nodes = " << effective_node_count << " moves = " << nb_moves << " max depth = " << Parent::get_max_depth() << std::endl; std::cout << "_RES " << (success ? 1 : 0) << "," << Parent::get_score() << "," << cur_threshold << "," << Parent::get_nb_nodes() << "," << effective_node_count << "," << nb_moves << "," << Parent::get_max_depth() << std::endl; }
void ccsoft::CC_FanoDecoding_FA< T_Register, T_IOSymbol, N_k >::purge_tree_cache | ( | FanoNodeEdge * | node_edge | ) | [inline, protected] |
Purge tree cache from a node. Keep only the current path to the node and path nodes immediate successors
node | The node to purge from |
{ bool node_terminal = true; unsigned int remaining_nodes = 0; while (node_edge != ParentInternal::root_node) { FanoNodeEdge *node_edge_predecessor = node_edge->get_incoming_node_edge(); std::array<FanoNodeEdge*, (1<<N_k)>& outgoing_node_edges = node_edge_predecessor->get_outgoing_node_edges(); typename std::array<FanoNodeEdge*, (1<<N_k)>::iterator ne_it = outgoing_node_edges.begin(); for (;ne_it != outgoing_node_edges.end(); ++ne_it) { if (*ne_it) // if using trailing zeros the corresponding edges for input symbol not zero are not constructed { FanoNodeEdge *node_edge_sibling = (*ne_it); if (node_terminal || (node_edge_sibling != node_edge)) { (*ne_it)->delete_outgoing_node_edges(); } remaining_nodes++; } } node_edge = node_edge_predecessor; node_terminal = false; } remaining_nodes++; // +1 for root node effective_node_count = remaining_nodes; DEBUG_OUT(Parent::verbosity > 1, "purged tree cache, nb of remaining nodes = " << remaining_nodes << std::endl); }
void ccsoft::CC_FanoDecoding_FA< T_Register, T_IOSymbol, N_k >::reset | ( | ) | [inline] |
Reset the decoding process
Reimplemented from ccsoft::CC_SequentialDecoding_FA< T_Register, T_IOSymbol, N_k >.
{ ParentInternal::reset(); Parent::reset(); cur_threshold = init_threshold; solution_found = false; effective_node_count = 0; }
void ccsoft::CC_FanoDecoding_FA< T_Register, T_IOSymbol, N_k >::set_tree_cache_size | ( | unsigned int | _tree_cache_size | ) | [inline] |
Set the tree cache size
_tree_cache_size | Maximum number of nodes to be cached |
{ tree_cache_size = _tree_cache_size; }
virtual void ccsoft::CC_FanoDecoding_FA< T_Register, T_IOSymbol, N_k >::visit_node_forward | ( | FanoNodeEdge * | node_edge, |
const CC_ReliabilityMatrix & | relmat | ||
) | [inline, protected, virtual] |
Visit a new node node Node to visit relmat Reliability matrix being used
Implements ccsoft::CC_SequentialDecodingInternal_FA< T_Register, T_IOSymbol, bool, N_k >.
{ unsigned int n = Parent::encoding.get_n(); int forward_depth = node_edge->get_depth() + 1; T_IOSymbol out_symbol; T_IOSymbol end_symbol; // return encoder to appropriate state if (node_edge->get_depth() >= 0) // does not concern the root node_edge { Parent::encoding.set_registers(node_edge->get_registers()); } if ((Parent::tail_zeros) && (forward_depth > relmat.get_message_length()-Parent::encoding.get_m())) { end_symbol = 1; // if zero tail option assume tail symbols are all zeros } else { end_symbol = (1<<Parent::encoding.get_k()); // full scan all possible input symbols } if (!node_edge->valid_outgoing_node_edges(end_symbol)) // edges are not cached { if ((tree_cache_size > 0) && (effective_node_count >= tree_cache_size)) // if tree cache is used and cache limit reached { purge_tree_cache(node_edge); // purge before allocating new nodes } // loop through assumption for this symbol place and create child nodes for (T_IOSymbol in_symbol = 0; in_symbol < end_symbol; in_symbol++) { Parent::encoding.encode(in_symbol, out_symbol, in_symbol > 0); // step only for a new symbol place float edge_metric = ParentInternal::log2(relmat(out_symbol, forward_depth)) - Parent::edge_bias; float forward_path_metric = edge_metric + node_edge->get_path_metric(); FanoNodeEdge *next_node_edge = new FanoNodeEdge(Parent::node_count++, node_edge, in_symbol, edge_metric, forward_path_metric, forward_depth); next_node_edge->get_tag() = false; // Init traversed back indicator next_node_edge->set_registers(Parent::encoding.get_registers()); node_edge->set_outgoing_node_edge(next_node_edge, in_symbol); // add forward edge effective_node_count++; } } }
float ccsoft::CC_FanoDecoding_FA< T_Register, T_IOSymbol, N_k >::cur_threshold [protected] |
Current path metric threshold.
float ccsoft::CC_FanoDecoding_FA< T_Register, T_IOSymbol, N_k >::delta_init_threshold [protected] |
Delta of path metric that is applied when restarting with a lower initial threshold.
float ccsoft::CC_FanoDecoding_FA< T_Register, T_IOSymbol, N_k >::delta_threshold [protected] |
Delta of path metric that is applied when lowering threshold.
unsigned int ccsoft::CC_FanoDecoding_FA< T_Register, T_IOSymbol, N_k >::effective_node_count [protected] |
Count of nodes effectively present in the system.
float ccsoft::CC_FanoDecoding_FA< T_Register, T_IOSymbol, N_k >::init_threshold [protected] |
Initial path metric threshold.
unsigned int ccsoft::CC_FanoDecoding_FA< T_Register, T_IOSymbol, N_k >::nb_moves [protected] |
Number of moves i.e. number of iterations in the main loop.
float ccsoft::CC_FanoDecoding_FA< T_Register, T_IOSymbol, N_k >::root_threshold [protected] |
Latest threshold at root node.
bool ccsoft::CC_FanoDecoding_FA< T_Register, T_IOSymbol, N_k >::solution_found [protected] |
Set to true when eligible terminal node is found.
unsigned int ccsoft::CC_FanoDecoding_FA< T_Register, T_IOSymbol, N_k >::tree_cache_size [protected] |
Tree cache size in maximum number of nodes in cache (0 = tree is not cached)
bool ccsoft::CC_FanoDecoding_FA< T_Register, T_IOSymbol, N_k >::unloop [protected] |
If true when a loop condition is detected attempt to restart with a lower threshold.