Viterbi译码程序代码
Viterbi译码的MATLAB仿真研究

BUPT卷积码编码及Viterbi译码班级:07114学号:070422姓名:吴希龙指导老师:彭岳星邮箱:FusionBupt@1. 序言卷积码最早于1955年由Elias 提出,稍后,1957年Wozencraft 提出了一种有效地译码方法即序列译码。
1963年Massey 提出了一种性能稍差但是比较实用的门限译码方法,使得卷积码开始走向实用化。
而后1967年Viterbi 提出了最大似然译码算法,它对存储级数较小的卷积码很容易实现,被称作Viterbi 译码算法,广泛的应用于现代通信中。
2. 卷积码编码及译码原理2.1 卷积码编码原理卷积码是一种性能优越的信道编码,它的编码器和解码器都比较易于实现,同时还具有较强的纠错能力,这使得它的使用越来越广泛。
卷积码一般表示为(n,k,K)的形式,即将k 各信息比特编码为n 个比特的码组,K 为编码约束长度,说明编码过程中相互约束的码段个数。
卷积码编码后的n 各码元不经与当前组的k 个信息比特有关,还与前K-1个输入组的信息比特有关。
编码过程中相互关联的码元有K*n 个。
R=k/n 是编码效率。
编码效率和约束长度是衡量卷积码的两个重要参数。
典型的卷积码一般选n,k 较小,但K 值可取较大(>10),以获得简单而高性能的卷积码。
卷积码的编码描述方式有很多种:冲激响应描述法、生成矩阵描述法、多项式乘积描述法、状态图描述,树图描述,网格图描述等。
2.1.1 卷积码解析表示法卷积码的解析表示发大致可以分为离散卷积法,生成矩阵法,码多项式法。
下面以离散卷积为例进行说明。
卷积码的编码器一般比较简单,为一个具有k 个输入端,n 个输出端,m 级移位寄存器的有限状态有记忆系统。
下图所示为(2,1,7)卷积码的编码器。
若输入序列为u =(u 0u 1u 2u 3……),则对应两个码字序列c ①=(c 0①c 1①c 2①c 3①……)和c ②=(c 0②c 1②c 2②c 3②……)相应的编码方程可写为c ①=u ∗g ①,c ②=u ∗g ②,c=(c ①,c ②)。
FEC前向纠错,卷积编码之维特比译码

FEC前向纠错,卷积编码之维特⽐译码因为要学习做WCDMA的流程解析,需要先提取卷积数据,⾸先就要做FEC卷积译码。
于是⽹上翻了好⼤⼀圈,特地学习了下viterbi译码算法,费很⼤⼒⽓才凑齐能够正确跑起来的代码,特记录⼀下。
说点题外话:viterbi是个⼈,全名Andrew J. Viterbi,⼀枚数学家,美国⾼通公司的创始⼈之⼀(没错,就是现在⼿机上那个⾼通芯⽚的⾼通),现代⽆线数字通讯⼯程的奠基⼈。
此外,viterbi算法不但可⽤来做卷积译码,另外⼀个⼴泛的应⽤是做股票证券量化预测计算(隐马模型预测)。
没错,就是⾼频量化交易炒股的算法基础。
美国量化对冲基⾦的传奇,⼤奖章基⾦背后的⽼板James Simons,⾸创将隐马模型预测⽤于炒股,也是⼀枚数学家。
两枚玩数学算法的巨富,谁说学数学算法没⽤的?FEC前向纠错译码⽤的viterbi算法代码摘抄⾃libfec,⾥⾯的实现很全⾯,还有mmx,sse加速版。
测试是做WCDMA,所以解交织和译码⽤的3GPP WCDMA的卷积编码参数(⼀帧编码长度262bits,卷积编码多项式是 r=1/2 k=9),我就只摘取了需要的viterbi29部分,有需要全部实现的同学可以⾃⾏去下载libfec。
下⾯是写来⽤于测试验证算法正确性的线程代码。
从IQ⽂件中⼀次读取2个float数,读取262+8(⼀帧viterbi译码长度)个数据后进⾏译码,若译码成功程序停在对应的测试断点上。
UINT CALLBACK ProcessIQDataThread(void *p_param){Convolution2Viterbi_t *p_sys = (Convolution2Viterbi_t *)p_param;vector<double> *p_data = NULL;int framebits = 262;struct v29 *vp;vector<float> uninter1;vector<float> uninter2;vector<float> radioFrame;vector<float> frameL;vector<float> frameR;vector<uint8_t> viterbi_in;uint8_t viterbi_out[33];uninter2.resize((framebits + 8));uninter1.resize((framebits + 8) * 2);viterbi_in.resize((framebits + 8) * 2);if ((vp = (struct v29 *)create_viterbi29_port(framebits)) == NULL) {return -1;}while (!p_sys->exit_thread){if (!p_data){WaitForSingleObject(p_sys->iq_data_event, 200);p_sys->iq_data_list.Lock();p_data = p_sys->iq_data_list.GetUse();p_sys->iq_data_list.UnLock();}if (!p_data)continue;// 读取2个浮点数for (int i = 0; i < p_data->size() / 2; i++){frameL.push_back(p_data->at(0));frameR.push_back(p_data->at(1));}// ⼀帧译码长度if (frameL.size() >= (framebits + 8)){// 解交织inter2deInterleavingNP(30, inter2Perm, frameL, uninter2);radioFrame.insert(radioFrame.end(), uninter2.begin(), uninter2.end());deInterleavingNP(30, inter2Perm, frameR, uninter2);radioFrame.insert(radioFrame.end(), uninter2.begin(), uninter2.end());if (radioFrame.size() >= (framebits + 8) * 2){// 解交织inter1deInterleavingNP(inter1Columns[1], inter1Perm[1], radioFrame, uninter1);radioFrame.clear();// 使⽤Viterbi算法做FEC译码for (int i = 0; i < (framebits + 8) * 2; i++){viterbi_in.push_back( ((int)uninter1[i] >> 24) );}init_viterbi29_port(vp, 0);update_viterbi29_blk_port(vp, viterbi_in.data(), framebits + 8);chainback_viterbi29_port(vp, viterbi_out, framebits, 0);#ifdef _DEBUG// 数据验证for (int i = 0; i < 33 - 4; i++) {if (viterbi_out[i] == 0x98 && viterbi_out[i + 1] == 0x54 && viterbi_out[i + 2] == 0xA0 && viterbi_out[i + 3] == 0x00) int bbb = 0;if (viterbi_out[i] == 0x05 && viterbi_out[i + 1] == 0x33 && viterbi_out[i + 2] == 0x00 && viterbi_out[i + 3] == 0x0c) int bbb = 0;if (viterbi_out[i] == 0x00 && viterbi_out[i + 1] == 0x03 && viterbi_out[i + 2] == 0xf4 && viterbi_out[i + 3] == 0x40) int bbb = 0;if (viterbi_out[i] == 0xc5 && viterbi_out[i + 1] == 0x80 && viterbi_out[i + 2] == 0x05 && viterbi_out[i + 3] == 0x5a) int bbb = 0;if (viterbi_out[i] == 0xe4 && viterbi_out[i + 1] == 0x00 && viterbi_out[i + 2] == 0x33 && viterbi_out[i + 3] == 0xe6) int bbb = 0;if (viterbi_out[i] == 0x72 && viterbi_out[i + 1] == 0x08 && viterbi_out[i + 2] == 0x38)int bbb = 0;if (viterbi_out[i] == 0xe2 && viterbi_out[i + 1] == 0x00 && viterbi_out[i + 2] == 0x38)int bbb = 0;}#endif}frameL.clear();frameR.clear();}p_sys->iq_data_list.Lock();p_sys->iq_data_list.PushEmpty(p_data);p_data = p_sys->iq_data_list.GetUse();p_sys->iq_data_list.UnLock();}return 0;}解交织 Interleaving.h#pragma once#include <vector>#include <assert.h>const int inter1Columns[4] = {1, // TTI = 10ms2, // TTI = 20ms4, // TTI = 40ms8 // TTI = 80ms};// 25.212 4.2.5.2 table 4: Inter-Column permutation pattern for 1st interleaving:const char inter1Perm[4][8] = {{0}, // TTI = 10ms{0, 1}, // TTI = 20ms{0, 2, 1, 3}, // TTI = 40ms{0, 4, 2, 6, 1, 5, 3, 7} // TTI = 80ms};// 25.212 4.2.11 table 7: Inter-Column permutation pattern for 2nd interleaving:const char inter2Perm[30] = { 0,20,10,5,15,25,3,13,23,8,18,28,1,11,21,6,16,26,4,14,24,19,9,29,12,2,7,22,27,17 };/* FIRST steps two pointers through a mapping, one pointer into the interleaved* data and the other through the uninterleaved data. The fifth argument, COPY,* determines whether the copy is from interleaved to uninterleaved, or back.* FIRST assumes no padding is necessary.* The reason for the define is to minimize the cost of parameterization and* function calls, as this is meant for L1 code, while also minimizing the* duplication of code.*/#define FIRST(UNINTERLEAVED,UNINTERLEAVEDP,INTERLEAVED,INTERLEAVEDP,COPY) \assert(UNINTERLEAVED.size() == INTERLEAVED.size()); \unsigned int rows = UNINTERLEAVED.size() / columns; \assert(rows * columns == UNINTERLEAVED.size()); \const char *colp = permutation; \float *INTERLEAVEDP = &INTERLEAVED[0]; \for (unsigned i = 0; i < columns; i++) { \float *UNINTERLEAVEDP = &UNINTERLEAVED[*colp++]; \for (unsigned int j = 0; j < rows; j++) { \COPY; \UNINTERLEAVEDP += columns; \} \}/** interleaving with No Padding */void interleavingNP(const unsigned int columns, const char *permutation, vector<float> &in, vector<float> &out) {FIRST(in, inp, out, outp, *outp++ = *inp)}/** de-interleaving with No Padding */void deInterleavingNP(const unsigned int columns, const char *permutation, vector<float> &in, vector<float> &out) {FIRST(out, outp, in, inp, *outp = *inp++)}/* SECOND steps two pointers through a mapping, one pointer into the interleaved* data and the other through the uninterleaved data. The fifth argument, COPY,* determines whether the copy is from interleaved to uninterleaved, or back.* SECOND pads if necessary.* The reason for the define is to minimize the cost of parameterization and* function calls, as this is meant for L1 code, while also minimizing the* duplication of code.*/#define SECOND(UNINTERLEAVED,UNINTERLEAVEDP,INTERLEAVED,INTERLEAVEDP,COPY) \assert(UNINTERLEAVED.size() == INTERLEAVED.size()); \int R2 = (UNINTERLEAVED.size() + columns - 1) / columns; \int padding = columns * R2 - UNINTERLEAVED.size(); \int rows = R2; \int firstPaddedColumn = columns - padding; \const char *colp = permutation; \float *UNINTERLEAVEDP = &UNINTERLEAVED[0]; \for (int i = 0; i < columns; i++) { \int trows = rows - (*colp >= firstPaddedColumn); \float *INTERLEAVEDP = &INTERLEAVED[*colp++]; \for (int j = 0; j < trows; j++) { \COPY; \INTERLEAVEDP += columns; \} \}/** interleaving With Padding */void interleavingWP(const int columns, const char *permutation, vector<float> &in, vector<float> &out){SECOND(in, inp, out, outp, *outp = *inp++)}/** de-interleaving With Padding */void deInterleavingWP(const int columns, const char *permutation, vector<float> &in, vector<float> &out){SECOND(out, outp, in, inp, *outp++ = *inp)}/*Determining the constants.From the standard we know:* All frame sizes for the BCH.* transport block is 246 bits* there are two radio frames, 270 bits each* TTI is 20 ms* SF is 256* parity word Li is 16 bits* For all downlink TrCH except BCH, the radio frame size is 2*38400/SF = 76800/SF.* For SF=256 that's 300.* For SF=4 that's 19200.* The maximum code block size for convulutional coding is 540 bits (25.212 4.2.2.2).* That corresponds to a radio frame size of 1080 bits, or a spreading factor of 71,meaning that the smallest spreading factor that can be used is 128.* 76800/128 = 600 coded bits -> roughly 300 data bits.* That corresponds to an input rate of roughly 30 kb/s at.* The maximum code block size for turbo coding is 5114 bits (25.212 4.2.2.2).* That corresponds to a radio frame size of 15342 bits, or a spreading factor of 5,meaning that the smallest spreading factor that can be used is 8.* 76800/8 = 9600 coded bits -> roughly 3200 data bits.* That corresponds to an input rate of roughly 320 kb/s.OK - SO HOW DO YOU GET HIGHER RATES?? HOW CAN YOU USE SF=4??A: Use the full 5114-but code block and then expand it with rate-matching.You still can't get the full ~640 kb/s implied by SF=4, but you get to ~500 kb/s.(pat) A: They considered this problem. See 25.212 4.2.2.2 Code block segmentation.In Layer1, after transport block concatenation, you then simply chop the result upinto the largest pieces that can go through the encoder, then put them back together after. From "higher layers" we are given:* SF: 4, 8, 16, 32, 64, 128, 256.* P: 24, 16, 8, 0 bits* TTI: 10, 20, 40 ms.To simplify things, we set:* TTI 10 ms always on DCH and FACH, 20 ms on PCH and BCH* BCH and PCH are always rate-1/2 convolutional code* DCH and FACH are always rate-1/3 turbo code* no rate-matching, no puncturing* parity word is always 16 bits* So the only parameter than changes is spreading factor.* We will only support 15-slot (non-compressed) slot formats.From our simplifications we also know:* For non-BCH/PCH TrCH there is one radio frame,76800/SF channel (coded) bits, per transport block.* DCH and FACH always use rate-1/3 turbo code,which has 12 terminating bits in the output.* For DCH and FACH, the transport block size is((76800/SF - 12)/3) - P = (25600/SF) - 4 - P data bits,where P is the parity word size.* Fix P=16 for simplicity and transport block size is (25600/SF) - 20.* for SF=256, that's 80 bits.* for SF=16, that's 1580 bits.* for SF=8, that's 3180 bits.* For PCH there is one radio frame,76800/SF channel (coded) bits, per transport block.* SF=64, for that's 1200 channel bits.* It's a rate-1/2 conv code, so that's 1200/2 - 8 - P data bits.* P=16 so that's 1200/2 - 24 = 576 transport bits. Really?*/#if 0const int inter1Columns[] = { 1, 2, 4, 8 };const char inter1Perm[4][8] = {{0},{0, 1},{0, 2, 1, 3},{0, 4, 2, 6, 1, 5, 3, 7}};const char inter2Perm[] = {0, 20, 10, 5, 15, 25, 3, 13, 23, 8, 18, 28, 1, 11, 21,6, 16, 26, 4, 14, 24, 19, 9, 29, 12, 2, 7, 22, 27, 17};vector<char> randomBitVector(int n){vector<char> t(n);for (int i = 0; i < n; i++) t[i] = random() % 2;return t;}void testInterleavings(){int lth1 = 48;int C2 = 30;for (int i = 0; i < 4; i++) {vector<char> v1 = randomBitVector(lth1);vector<char> v2(lth1);vector<char> v3(lth1);v1.interleavingNP(inter1Columns[i], inter1Perm[i], v2);v2.deInterleavingNP(inter1Columns[i], inter1Perm[i], v3);cout << "first " << i << " " << (veq(v1, v3) ? "ok" : "fail") << endl;}for (int lth2 = 90; lth2 < 120; lth2++) {vector<char> v1 = randomBitVector(lth2);vector<char> v2(lth2);vector<char> v3(lth2);v1.interleavingWP(C2, inter2Perm, v2);v2.deInterleavingWP(C2, inter2Perm, v3);cout << "second " << lth2 << " " << (veq(v1, v3) ? "ok" : "fail") << endl;}for (int lth = 48; lth <= 4800; lth *= 10) {TurboInterleaver er(lth);cout << "Turbo Interleaver permutation(" << lth << ") " << (permutationCheck(er.permutation()) ? "ok" : "fail") << endl; vector<char> er1 = randomBitVector(lth);vector<char> er2(lth);er.interleave(er1, er2);vector<char> er3(lth);er.unInterleave(er2, er3);cout << "Turbo Interleaver(" << lth << ") " << (veq(er1.sliced(), er3) ? "ok" : "fail") << endl;}}#endifviterbi译码partab实现 partab.c/* Utility routines for FEC support* Copyright 2004, Phil Karn, KA9Q*/#include <stdio.h>#include "fec.h"unsigned char Partab[256] = {0};int P_init = 0;/* Create 256-entry odd-parity lookup table* Needed only on non-ia32 machines*/void partab_init(void) {int i, cnt, ti;/* Initialize parity lookup table */for (i = 0; i < 256; i++) {cnt = 0;ti = i;while (ti) {if (ti & 1)cnt++;ti >>= 1;}Partab[i] = cnt & 1;}P_init = 1;}/* Lookup table giving count of 1 bits for integers 0-255 */int Bitcnt[] = {0, 1, 1, 2, 1, 2, 2, 3,1, 2, 2, 3, 2, 3, 3, 4,1, 2, 2, 3, 2, 3, 3, 4,2, 3, 3, 4, 3, 4, 4, 5,1, 2, 2, 3, 2, 3, 3, 4,2, 3, 3, 4, 3, 4, 4, 5,2, 3, 3, 4, 3, 4, 4, 5,3, 4, 4, 5, 4, 5, 5, 6,1, 2, 2, 3, 2, 3, 3, 4,2, 3, 3, 4, 3, 4, 4, 5,2, 3, 3, 4, 3, 4, 4, 5,3, 4, 4, 5, 4, 5, 5, 6,2, 3, 3, 4, 3, 4, 4, 5,3, 4, 4, 5, 4, 5, 5, 6,3, 4, 4, 5, 4, 5, 5, 6,4, 5, 5, 6, 5, 6, 6, 7,1, 2, 2, 3, 2, 3, 3, 4,2, 3, 3, 4, 3, 4, 4, 5,2, 3, 3, 4, 3, 4, 4, 5,3, 4, 4, 5, 4, 5, 5, 6,2, 3, 3, 4, 3, 4, 4, 5,3, 4, 4, 5, 4, 5, 5, 6,3, 4, 4, 5, 4, 5, 5, 6,4, 5, 5, 6, 5, 6, 6, 7,2, 3, 3, 4, 3, 4, 4, 5,3, 4, 4, 5, 4, 5, 5, 6,3, 4, 4, 5, 4, 5, 5, 6,4, 5, 5, 6, 5, 6, 6, 7,3, 4, 4, 5, 4, 5, 5, 6,4, 5, 5, 6, 5, 6, 6, 7,4, 5, 5, 6, 5, 6, 6, 7,5, 6, 6, 7, 6, 7, 7, 8,};fec.h/* User include file for libfec* Copyright 2004, Phil Karn, KA9Q* May be used under the terms of the GNU Lesser General Public License (LGPL)*/#ifndef _FEC_H_#define _FEC_H_#ifdef __cplusplusextern "C" {#endif/* r=1/2 k=9 convolutional encoder polynomials */#define V29POLYA 0x1af#define V29POLYB 0x11dvoid *create_viterbi29_port(int len);void set_viterbi29_polynomial_port(int polys[2]);int init_viterbi29_port(void *p, int starting_state);int chainback_viterbi29_port(void *p, unsigned char *data, unsigned int nbits, unsigned int endstate); void delete_viterbi29_port(void *p);int update_viterbi29_blk_port(void *p, unsigned char *syms, int nbits);void partab_init();static inline int parityb(unsigned char x) {extern unsigned char Partab[256];extern int P_init;if (!P_init) {partab_init();}return Partab[x];}static inline int parity(int x) {/* Fold down to one byte */x ^= (x >> 16);x ^= (x >> 8);return parityb(x);}#ifdef __cplusplus}#endif#endif /* _FEC_H_ */viterbi29_port.c/* K=9 r=1/2 Viterbi decoder in portable C* Copyright Feb 2004, Phil Karn, KA9Q* May be used under the terms of the GNU Lesser General Public License (LGPL)*/#include <stdio.h>#include <stdlib.h>#include <memory.h>#include "fec.h"typedef union { unsigned int w[256]; } metric_t;typedef union { unsigned long w[8]; } decision_t;static union { unsigned char c[128]; } Branchtab29[2];static int Init = 0;/* State info for instance of Viterbi decoder */struct v29 {metric_t metrics1; /* path metric buffer 1 */metric_t metrics2; /* path metric buffer 2 */decision_t* dp; /* Pointer to current decision */metric_t* old_metrics, * new_metrics; /* Pointers to path metrics, swapped on every bit */decision_t* decisions; /* Beginning of decisions for block */};/* Initialize Viterbi decoder for start of new frame */int init_viterbi29_port(void* p, int starting_state) {struct v29* vp = p;int i;if (p == NULL)return -1;for (i = 0; i < 256; i++)vp->metrics1.w[i] = 63;vp->old_metrics = &vp->metrics1;vp->new_metrics = &vp->metrics2;vp->dp = vp->decisions;vp->old_metrics->w[starting_state & 255] = 0; /* Bias known start state */return 0;}void set_viterbi29_polynomial_port(int polys[2]) {int state;for (state = 0; state < 128; state++) {Branchtab29[0].c[state] = (polys[0] < 0) ^ parity((2 * state) & abs(polys[0])) ? 255 : 0; Branchtab29[1].c[state] = (polys[1] < 0) ^ parity((2 * state) & abs(polys[1])) ? 255 : 0; }Init++;}/* Create a new instance of a Viterbi decoder */void* create_viterbi29_port(int len) {struct v29* vp;if (!Init) {int polys[2] = { V29POLYA,V29POLYB };set_viterbi29_polynomial_port(polys);}if ((vp = (struct v29*)malloc(sizeof(struct v29))) == NULL)return NULL;if ((vp->decisions = (decision_t*)malloc((len + 8) * sizeof(decision_t))) == NULL) {free(vp);return NULL;}init_viterbi29_port(vp, 0);return vp;}/* Viterbi chainback */int chainback_viterbi29_port(void* p,unsigned char* data, /* Decoded output data */unsigned int nbits, /* Number of data bits */unsigned int endstate) /* Terminal encoder state */{struct v29* vp = p;decision_t* d;if (p == NULL)return -1;d = vp->decisions;/* Make room beyond the end of the encoder register so we can* accumulate a full byte of decoded data*/endstate %= 256;/* The store into data[] only needs to be done every 8 bits.* But this avoids a conditional branch, and the writes will* combine in the cache anyway*/d += 8; /* Look past tail */while (nbits-- != 0) {int k;k = (d[nbits].w[(endstate) / 32] >> (endstate % 32)) & 1;data[nbits >> 3] = endstate = (endstate >> 1) | (k << 7);}return 0;}/* Delete instance of a Viterbi decoder */void delete_viterbi29_port(void* p) {struct v29* vp = p;if (vp != NULL) {free(vp->decisions);free(vp);}}/* C-language butterfly */#define BFLY(i) {\unsigned int metric,m0,m1,decision;\metric = (Branchtab29[0].c[i] ^ sym0) + (Branchtab29[1].c[i] ^ sym1);\ m0 = vp->old_metrics->w[i] + metric;\m1 = vp->old_metrics->w[i+128] + (510 - metric);\decision = (signed int)(m0-m1) > 0;\vp->new_metrics->w[2*i] = decision ? m1 : m0;\d->w[i/16] |= decision << ((2*i)&31);\m0 -= (metric+metric-510);\m1 += (metric+metric-510);\decision = (signed int)(m0-m1) > 0;\vp->new_metrics->w[2*i+1] = decision ? m1 : m0;\d->w[i/16] |= decision << ((2*i+1)&31);\}/* Update decoder with a block of demodulated symbols* Note that nbits is the number of decoded data bits, not the number* of symbols!*/int update_viterbi29_blk_port(void* p, unsigned char* syms, int nbits) { struct v29* vp = p;decision_t* d;if (p == NULL)return -1;d = (decision_t*)vp->dp;while (nbits--) {void* tmp;unsigned char sym0, sym1;int i;for (i = 0; i < 8; i++)d->w[i] = 0;sym0 = *syms++;sym1 = *syms++;for (i = 0; i < 128; i++)BFLY(i);d++;tmp = vp->old_metrics;vp->old_metrics = vp->new_metrics;vp->new_metrics = tmp;}vp->dp = d;return 0;}。
简单的维特比译码

L1=M1/N1;%译码输出码长
%w=zeros(1,M1); %w表示判决输出;
%for mm=1:M1
% if abs(r(mm))>1
% w(mm)=1 ;
%else w(mm)=0;
% end
for k=1:L1 %输出序列码长
for st=1:numStates %状态
for i=0:numInputs-1 %当前输入
dout=dec2bin(trellis.outputs(st,i+1),N1)-48;
dist=sum((r((k-1)*N1+1:k*N1)-dout).^2); %计算汉明码距
next_metric(nextState)=x;
sur_path(nextState,1:k)=[path(st,1:k-1) i];
else
if x<next_metric(nextState) % 择优路径
cur_metric=zeros(1,numStates)+Inf;%汉明码距
cur_metric(1)=0;
next_metric=-ones(1,numStates);%下一状态码距
path=zeros(numStates,L1);
sur_path=zeros(numStates,L1);
next_metric=-ones(1,numStates);
path=sur_path; %最佳路径
end
out1=path(1,:); %输出
next_metric(nextState)=x;
sur_path(nextState,1:k)=[path(st,1:k-1) i];
维特比译码程序

(n,k,N)卷积码的维特比译码算法实现#include<iostream>#define t_src 0#define t_des 1#define t_len 2#define t_flag 3#define t_in 4using namespace std;int myn=0;int stalen=0;int myg1[10]={0};int myg2[10]={0};int stan0[256][2]={0};//输入0 时个状态的输出int stan1[256][2]={0};//输入1 时各状态的输出int stachn[256][2]={0};//状态装换表int path[256][100]={0};//存储路径int calpath[256]={0};//存储路径长度int myin[24]; //一次处理12 次int myout[200]; //int myoutsym=0;int pthsym;int outfull=0; //决定是否输出int table1[8]={1,2,4,8,16,32,64,128};void chartobits(char ch,int *bits);char bitstochar(int *bits);int calluj(int a1,int a2,int b1,int b2);void initpath(void);void selpath(int a1,int a2);void wridata(void);void viterbit(void);void writdataedn(void);void creatsta(void);void myinput(void);int main(){myinput();creatsta();viterbit();}void myinput(void){int i,j;输入编码的约束长度cin>>myn;stalen=int(pow(2.0,myn-1));选择默认的编码矢量则输入1,输入 2 则可输入其他的编码矢量cin>>i;if(i==1){switch(myn){case 3: myg1[0]=1,myg1[1]=1,myg1[2]=1;myg2[0]=1,myg2[1]=0,myg2[2]=1;break;case 4: myg1[0]=1,myg1[1]=1,myg1[2]=1,myg1[3]=1;myg2[0]=1,myg2[1]=0,myg2[2]=1,myg2[3]=1;break;case 5: myg1[0]=1,myg1[1]=0,myg1[2]=1,myg1[3]=1,myg1[4]=1;myg2[0]=1,myg2[1]=1,myg2[2]=0,myg2[3]=1,myg2[4]=1;break;case 6: myg1[0]=1,myg1[1]=0,myg1[2]=1,myg1[3]=1,myg1[4]=1,myg1[5]=1;myg2[0]=1,myg2[1]=1,myg2[2]=0,myg2[3]=1,myg2[4]=0,myg2[5]=1;break;case 7: myg1[0]=1,myg1[1]=0,myg1[2]=0,myg1[3]=1,myg1[4]=1,myg1[5]=1,myg1[6]=1;myg2[0]=1,myg2[1]=1,myg2[2]=0,myg2[3]=1,myg2[4]=1,myg2[5]=0,myg2[6]=1;break;case 8: myg1[0]=1,myg1[1]=0,myg1[2]=0,myg1[3]=1,myg1[4]=1,myg1[5]=1,myg1[6]=1,myg1[7]=1;myg2[0]=1,myg2[1]=1,myg2[2]=1,myg2[3]=0,myg2[4]=0,myg2[5]=1,myg2[6]=0,myg2[7]=1;break;case 9: myg1[0]=1,myg1[1]=1,myg1[2]=0,myg1[3]=1,myg1[4]=0,myg1[5]=1,myg1[6]=1,myg1[7]=1,m yg1[8]=1;myg2[0]=1,myg2[1]=0,myg2[2]=0,myg2[3]=0,myg2[4]=1,myg2[5]=1,myg2[6]=1,myg2[7]=0,m yg2[8]=1;break;}}else{输入for(j=0;j<myn;j++)cin>>myg1[j];输入for(j=0;j<myn;j++)cin>>myg2[j];}连接矢量1 为for(j=0;j<myn;j++)cout<<endl;连接矢量2 为for(j=0;j<myn;j++)cout<<endl;cout<<endl;}void creatsta(void){int i,j,k,myi;int tembits[10];for(i=0;i<stalen;i++){stan1[i][0]=0;stan1[i][1]=0;stan0[i][0]=0;stan0[i][1]=0;stachn[i][0]=i/2;myi=i;for(j=0;j<myn;j++){if(myi>=pow(2.0,myn-1-j)){tembits[j]=1;myi=myi-pow(2.0,myn-1-j);}else{tembits[j]=0;}}for(k=0;k<myn;k++){stan0[i][0]+=myg1[k]*tembits[k];stan0[i][1]+=myg2[k]*tembits[k];}stan0[i][0]=stan0[i][0]%2;stan0[i][1]=stan0[i][1]%2;myi=i+int(pow(2.0,myn-1));stachn[i][1]=myi/2;for(j=0;j<myn;j++){if(myi>=pow(2.0,myn-1-j)){tembits[j]=1;myi=myi-pow(2.0,myn-1-j);}else{tembits[j]=0;}}for(k=0;k<myn;k++){stan1[i][0]+=myg1[k]*tembits[k];stan1[i][1]+=myg2[k]*tembits[k];}stan1[i][0]=stan1[i][0]%2;stan1[i][1]=stan1[i][1]%2;}状态转移出for(i=0;i<stalen;i++)cout<<endl;输入0 状态转移后的输出for(i=0;i<stalen;i++)cout<<endl;输入1 状态转移后的输出for(i=0;i<stalen;i++)cout<<endl;}void chartobits(char ch,int *bits){int i;for(i=0;i<8;i++){if(ch<0)bits[i]=1;elsebits[i]=0;ch=ch<<1;}}char bitstochar(int *bits){char temp=0;int i;for(i=0;i<8;i++){if(bits[i]==1)temp+=table1[7-i];}return temp;}int calluj(int a1,int a2,int b1,int b2){int y=0;if(a1!=b1)y++;if(a2!=b2)y++;return(y);}void initpath(){int tem;int t_tem[256][5]={0};int i,j,k,l;int ljtem[256][100];int pttem[256]={0};int staflag[256]={0};staflag[0]=1;int a1,a2;for(l=0;l<myn-1;l++){for(i=0;i<stalen;i++)for(j=0;j<pthsym;j++)ljtem[i][j]=path[i][j];i=0;a1=myin[2*l];a2=myin[2*l+1];for(j=0;j<stalen;j++){if(staflag[j]==1){tem=calluj(a1,a2,stan0[j][0],stan0[j][1]);t_tem[i][t_src]=j;t_tem[i][t_des]=stachn[j][0];t_tem[i][t_len]=calpath[j]+tem;t_tem[i][t_in]=0;tem=calluj(a1,a2,stan1[j][0],stan1[j][1]);t_tem[i+1][t_src]=j;t_tem[i+1][t_des]=stachn[j][1];t_tem[i+1][t_len]=calpath[j]+tem;t_tem[i+1][t_in]=1;i=i+2;}}for(k=0;k<stalen;k++)staflag[k]=0;for(k=0;k<i;k++){staflag[t_tem[k][t_des]]=1;calpath[t_tem[k][t_des]]=t_tem[k][t_len];for(j=0;j<pthsym;j++)path[t_tem[k][t_des]][j]=ljtem[t_tem[k][t_src]][j];path[t_tem[k][t_des]][pthsym]=t_tem[k][t_in];}pthsym++;}初始化后的路径长度for(int i=0;i<8;i++)cout<<endl;*/}void selpath(int a1,int a2){//16 选8int t_tem[512][5]={0};int i,j,tem;int ljtem[256][100];j=0;for(i=0;i<2*stalen;i=i+2){tem=calluj(a1,a2,stan0[j][0],stan0[j][1]);t_tem[i][t_src]=j;t_tem[i][t_des]=stachn[j][0];t_tem[i][t_len]=calpath[j]+tem;t_tem[i][t_flag]=0;t_tem[i][t_in]=0;//t_tem[i][t_rep]=0;tem=calluj(a1,a2,stan1[j][0],stan1[j][1]);t_tem[i+1][t_src]=j;t_tem[i+1][t_des]=stachn[j][1];t_tem[i+1][t_len]=calpath[j]+tem;t_tem[i+1][t_flag]=0;t_tem[i+1][t_in]=1;//t_tem[i][t_rep]=0;j++;}for(i=0;i<2*stalen;i++)for(j=i+1;j<2*stalen;j++)if(t_tem[i][t_des]==t_tem[j][t_des]){if(t_tem[i][t_len]<=t_tem[j][t_len]){t_tem[i][t_flag]=1;}else{t_tem[j][t_flag]=1;}}for(i=0;i<stalen;i++)for(j=0;j<pthsym;j++)ljtem[i][j]=path[i][j];for(i=0;i<2*stalen;i++)if(t_tem[i][t_flag]==1){calpath[t_tem[i][t_des]]=t_tem[i][t_len];for(j=0;j<pthsym;j++)path[t_tem[i][t_des]][j]=ljtem[t_tem[i][t_src]][j];path[t_tem[i][t_des]][pthsym]=t_tem[i][t_in];}if(pthsym>16)outfull=1;pthsym++;}void wridata(){int i,j,icout,equcout;icout=0;equcout=0;for(i=0;i<pthsym;i++){for(j=0;j<stalen-1;j++)if(path[j][i]==path[j+1][i])equcout++;if(equcout==stalen-1){myout[myoutsym++]=path[0][i];icout++;equcout=0;}elsebreak;}if(icout!=0){for(i=0;i<pthsym-icout;i++){for(j=0;j<stalen;j++)path[j][i]=path[j][i+icout];}}pthsym=pthsym-icout;outfull=0;}void writdataedn(void){int i,j;i=0;for(j=1;j<stalen;j++)if(calpath[i]>calpath[j])i=j;for(j=0;j<pthsym;j++)myout[myoutsym++]=path[i][j]; }void viterbit(){FILE *fp_input,*fp_output;exit(0);}elseexit(0);}elsechar ch;int count=0;int i,j;char wch;int wcout=0;int mybit[8];ch=fgetc(fp_input);chartobits(ch,mybit);for(i=0;i<8;i++)myin[i]=mybit[i];while(feof(fp_input)==0){ch=fgetc(fp_input);输入输入数据1 为for(temi=0;temi<8;temi++)cout<<endl;*/if(count==0){chartobits(ch,mybit);for(i=0;i<8;i++)myin[i+8]=mybit[i];initpath();for(j=myn-1;j<8;j=j++)selpath(myin[2*j],myin[2*j+1]);}else{chartobits(ch,myin);for(j=0;j<4;j++)selpath(myin[2*j],myin[2*j+1]);}count++;if(count==0)count=1;//if(outfull==1)wridata();if(myoutsym>=8){wcout=int(myoutsym/8);for(i=0;i<wcout;i++){for(j=0;j<8;j++)mybit[j]=myout[8*i+j];wch=bitstochar(mybit);输出为fputc(wch,fp_output);}for(i=0;i<myoutsym-wcout*8;i++)myout[i]=myout[wcout*8+i];myoutsym=myoutsym-wcout*8;}}writdataedn();if(myoutsym>=3){for(i=0;i<8-myoutsym;i++)myout[myoutsym++]=0;wch=bitstochar(myout);fputc(wch,fp_output);}fclose(fp_input);fclose(fp_output);cin>>i;}。
卷积码的Viterbi译码设计设计

摘要在数字通信系统中,通常采用差错控制编码来提高系统的可靠性。
自P.Elias 首次提出卷积码编码以来,这一编码技术至今仍显示出强大的生命力。
目前,卷积码已广泛应用在无线通信标准中,如GSM,CDMA2000和IS-95等无线通信标准中。
针对N-CDMA数据传输过程中的误码问题,本文论述了旨在提高数据传输质量的维特比译码器的设计。
虽然Viterbi译码复杂度较大,实现较为困难,但效率高,速度快。
因此本文着重分析和讨论了1/2速率的(2,1,9)卷积码编码和其Viterbi译码算法。
深入研究卷积码编码原理和Viterbi算法原理后,提出了(2,1,9)卷积码编码以及Viterbi算法的初始化、加—比—选和回溯设计方案,运用查表的方法,避免了大量繁琐计算,使得译码简洁迅速,译码器的实时性能良好。
并充分利用TMS320C54X系列DSP芯片,用汇编语言完成了(2,1,9)卷积码编码和Viterbi 译码的程序。
关键词:差错控制编码、卷积码、Viterbi译码、TMS320C54X、DSPAbstractIn digital communication systems, error control coding is usually used to improve system reliability. Since P.Elias put forward the convolutional coding the first time, the coding is still showing strong vitality.,has become widely used in satellite communications, wireless communications and many other communication systemsas a kind of channel coding method. such as GSM, CDMA2000 and has been a wireless communication standards of IS-95.In view of the error problem in the process of N-CDMA data transmission, this paper discusses the aims to improve the quality of data transmission of victor design than the decoder.Although Viterbi decoding complexity is bigger, more difficult to achieve, but high efficiency and fast speed. So this article emphatically analyzed and discussed the 1/2 rate (2,1,9) convolution code coding and its Viterbi decoding algorithm. In-depth study on principle of convolution code coding and Viterbi algorithm, proposed the convolution code coding and Viterbi algorithm (2,1,9) initialization, add - than - choose and back design, using look-up table method, to avoid a large amount of tedious calculation, the decoding and quick, good real-time performance of the decoder. Make full use of the series of TMS320C54X DSP chip, using assembly language to complete the(2,1,9)convolution code coding and Viterbi decoding process.Keywords: error control coding, convolutional code, Viterbi decoding, TMS320C54X目录摘要 (1)Abstract (2)目录 (3)1.绪论 (1)1.1 移动通信及N-CDMA背景 (1)1.2 数字通信概述 (1)1.3 卷积编码与译码的发展 (3)1.4 主要研究工作 (3)2.DSP与CCS简介 (5)2.1 DSP概述 (5)2.1.1 DSP的主要特点 (5)2.1.2 CSSU单元概述 (7)2.2 CCS概述 (8)2.3 本章小结 (8)3.卷积码的理论基础 (9)3.1 卷积码的概述 (9)3.1.1 卷积码基本原理 (9)3.1.2 卷积码的纠错能力 (9)3.1.3 卷积码的表示方法 (10)3.2 Viterbi译码的概述 (11)3.3 本章小结 (14)4.卷积编码的实现 (15)4.1 (2,1,9)卷积码编码 (15)4.1.1 (2,1,9)卷积码编码设计方案 (15)4.1.2 (2,1,9)卷积码编码流程图 (16)4.1.3 (2,1,9)卷积编码程序实现 (16)4.1.4 (2,1,9)的程序仿真 (17)4.2 (2,1,9)卷积码状态转换表 (17)4.2.1 (2,1,9)卷积码状态转换表的设计算法 (18)4.2.2 (2,1,9)卷积码状态转换表的流程图 (18)4.2.3 (2,1,9)卷积码状态表 (18)4.2.4 (2,1,9)卷积码状态表的蝶形结构 (21)4.3 本章小结 (22)5. Viterbi译码的实现 (23)5.1 Viterbi译码基础 (23)5.2 Viterbi译码算法 (23)5.3 变量定义情况 (25)5.4 初始化 (26)5.4.1 初始化流程图 (27)5.4.2 初始化程序仿真 (27)5.5 加-比-选 (28)5.5.1加-比-选流程图 (29)5.5.2加-比-选程序仿真 (30)5.6 回溯 (31)5.6.1 回溯流程图 (32)5.6.2 回溯仿真图 (33)5.7 Viterbi纠错测试 (34)5.8 本章小结 (34)总结 (36)致谢 ............................................................................ 错误!未定义书签。
Viterbi译码的Matlab实现

2010年12月(上)Viterbi 译码的Matlab 实现张慧(盐城卫生职业技术学院,江苏盐城224006)[摘要]本文主要介绍了Viterbi 译码是一种最大似然译码算法,是卷积编码的最佳译码算法。
本文主要是以(2,1,2)卷积码为例,介绍了Viterbi 译码的原理和过程,并用Matlab 进行仿真。
[关键词]卷积码;Viterbi 译码1卷积码的类型卷积码的译码基本上可划分为两大类型:代数译码和概率译码,其中概率译码是实际中最常采用的卷积码译码方法。
2Viterbi 译码Viterbi 译码是由Viterbi 在1967年提出的一种概率译码,其实质是最大似然译码,是卷积码的最佳译码算法。
它利用编码网格图的特殊结构,降低了计算的复杂性。
该算法考虑的是,去除不可能成为最大似然选择对象的网格图上的路径,即,如果有两条路径到达同一状态,则具有最佳量度的路径被选中,称为幸存路径(surviving path )。
对所有状态都将进行这样的选路操作,译码器不断在网格图上深入,通过去除可能性最小的路径实现判决。
较早地抛弃不可能的路径降低了译码器的复杂性。
为了更具体的理解Viterbi 译码的过程,我们以(2,1,2)卷积码为例,为简化讨论,假设信道为BSC 信道。
译码过程的前几步如下:假定输入数据序列m ,码字U ,接收序列Z ,如图1所示,并假设译码器确知网格图的初始状态。
图1时刻t 1接收到的码元是11,从状态00出发只有两种状态转移方向,00和10,如图a 所示。
状态转换的分支量度是2;状态转换的分支径量度是0。
时刻t 2从每个状态出发都有两种可能的分支,如图b 所示。
这些分支的累积量度标识为状态量度┎a ,┎b ,┎c ,┎d ,与各自的结束状态相对应。
同样地,图c 中时刻t 3从每个状态出发都有两个分支,因此,时刻时到达每个状态的路径都有两条,这两条路径中,累积路径量度较大的将被舍弃。
如果这两条路径的路径量度恰好相等,则任意舍弃其中一条路径。
matlab卷积编码与viterbi译码的实现

matlab卷积编码与viterbi译码的实现MATLAB中viterbi译码算法讨论⼤家可以再评论区交流!!!MATLAB中实现viterbi译码的函数为:convenc其中:code = convenc(msg,trellis)vitdec其中:vitdec(code,trellis,tblen,opmode,dectype)code卷积编码,trellis⽹格表,tblen回溯长度,opmode:cont、term、trunc,dectype:unquant、hard、soft;本⼈最近在做⼀个关于viterbi译码算法,最终在FPGA中实现,在FPGA中最终的实现⽅案为xillinx IP核实现。
在此之前⽤MATLAB进⾏仿真验证。
matlab程序:Tre = poly2trellis(7,[133 171]);通过poly2trellis⽣成逻辑关系图,如下图所⽰。
逻辑关系图%卷积编码:msg = [0 1 0 1 1 0 1 0 1 1 1 1 0 0 1 1 0 0 1 0 1 0 0 1];code = convenc(msg,Tre);%code = [0,0,1,1,0,1,0,0,0,1,1,0,0,1,0,1,1,0,0,0,1,0,0,0,1,0,1,0,0,1,1,1,1,1,1,1,0,0,1,1,0,1,0,1,1,1,1,0];%这是通过convenc函数⽣成的卷积码%vitdec译码:%在vitdec译码过程中采⽤硬判决,通过不同的tblen和opmode来找出其中关系。
%(1) opmode = conttblen = 12;msg_dat = vitdec(code,Tre,tblen,'cont','hard');%msg_dat =[ 0 0 0 0 0 0 0 0 0 0 0 0 0 1 0 1 1 0 1 0 1 1 1 1 ];%通过了解到cont模式中,vitdec译码会有延迟,延迟的长度为tblen长度,所以在此对vitdec进⾏修改code_temp = [code,zeros(1,24)];msg_temp = vitdec(code_temp ,T,12,'cont','hard')msg_dat = msg_temp(13:end);%msg_dat = [ 0 1 0 1 1 0 1 0 1 1 1 1 0 0 1 1 0 0 1 0 1 0 0 1];%此时vitdec译码出来的数据和信源⼀样tblen = 18;code_temp = [code,zeros(1,24)];msg_temp = vitdec(code_temp ,T,12,'cont','hard')msg_dat = msg_temp(13:end);%msg_dat = [ 0 0 0 0 0 0 0 1 0 1 1 0 1 0 1 1 1 1 0 0 1 1 0 0];%此时vitdec译码出来的数据和信源在后⾯最后⼀位不⼀样%(2) opmode = termtblen = 12;msg_dat = vitdec(code,Tre,tblen,'term','hard');%msg_dat = [0 1 0 1 1 0 1 0 1 1 1 1 0 0 1 1 1 0 0 0 0 0 0 0];%此时vitdec译码出来的数据和信源⼀样前16位和信源⼀样后⾯的就出错了tblen = 18;msg_dat = vitdec(code,Tre,tblen,'term','hard');%msg_dat = [0 1 0 1 1 0 1 0 1 1 1 1 0 0 1 1 1 0 0 0 0 0 0 0];%此时vitdec译码出来的数据和信源⼀样前16位和信源⼀样后⾯的就出错了%(3)opmode = trunctblen = 12;msg_dat = vitdec(code,Tre,tblen,'trunc','hard');%msg_dat = [ 0 1 0 1 1 0 1 0 1 1 1 1 0 0 1 1 0 0 1 0 1 0 0 1];%此时vitdec译码出来的数据和信源⼀样tblen = 18;msg_dat = vitdec(code,Tre,tblen,'trunc','hard');%msg_dat = [ 0 1 0 1 1 0 1 0 1 1 1 1 0 0 1 1 0 0 1 0 1 0 0 1];%此时vitdec译码出来的数据和信源⼀样总结:以上通过⽐较tblen和opmode模式的不同对产⽣的结果,其中cont和trunc的模式总结起来就是cont有tblen延迟,但是trunc没有。
Viterbi译码程序代码

译码主要部分#include"stdafx.h"//#define DEBUGvoid deci2bin(int d, int size, int *b);int bin2deci(int *b, int size);int nxt_stat(int current_state, int input, int *memory_contents);void init_quantizer(void);void init_adaptive_quant(float es_ovr_n0);int soft_quant(float channel_symbol);int soft_metric(int data, int guess);int quantizer_table[256];void sdvd(int g[2][K], float es_ovr_n0, long channel_length, float*channel_output_vector, int *decoder_output_matrix){int i, j, l, ll; //循环控制变量long t; //时间int memory_contents[K]; //记录输入内容int input[TWOTOTHEM][TWOTOTHEM]; //对当前状态以及下一个状态映射int output[TWOTOTHEM][2]; //卷积码编码输出矩阵int nextstate[TWOTOTHEM][2]; //下一个状态矩阵int accum_err_metric[TWOTOTHEM][2]; //误差累计矩阵int state_history[TWOTOTHEM][K * 5 + 1]; //历史状态表int state_sequence[K * 5 + 1]; //状态序列int *channel_output_matrix; //信道输出序列int binary_output[2];int branch_output[2]; //0或者1的输出分支int m, n, number_of_states, depth_of_trellis, step, branch_metric,sh_ptr, sh_col, x, xx, h, hh, next_state, last_stop;n = 2; //1/2为卷积码传输数据的码率m = K - 1;//寄存器个数number_of_states = (int)pow(2.0, m);//状态个数number of states = 2^(K - 1) = 2^mdepth_of_trellis = K * 5;for (i = 0; i < number_of_states; i++){for (j = 0; j < number_of_states; j++)input[i][j] = 0; //输入数组初始化for (j = 0; j < n; j++){nextstate[i][j] = 0;//下一个状态数组初始化output[i][j] = 0; //输出数组初始化}for (j = 0; j <= depth_of_trellis; j++){state_history[i][j] = 0;//历史状态数组初始化state_history[4][16] }accum_err_metric[i][0] = 0;//误差累计矩阵第一列初始化为0accum_err_metric[i][1] = MAXINT;//误差累计矩阵第二列初始化为一个很大的数}/*前向纠错简称FEC(Forward Error Correction),其原理是:发送方将要发送的数据附加上一定的冗余纠错码一并发送,接收方则根据纠错码对数据进行差错检测,如发现差错,由接收方进行纠正*//*产生状态转移矩阵、输出矩阵、输入矩阵*///输入矩阵表示的是FEC编码传输给下一个状态//下一个状态由输入和当前状态给出//输出矩阵for (j = 0; j < number_of_states; j++){for (l = 0; l < n; l++){next_state = nxt_stat(j, l, memory_contents);input[j][next_state] = l;/*计算给定的卷积编码器输出当前状态数和输入值*/branch_output[0] = 0;branch_output[1] = 0;for (i = 0; i < K; i++){branch_output[0] = branch_output[0] ^ memory_contents[i] & g[0][i];branch_output[1] = branch_output[1] ^ memory_contents[i] & g[1][i];}nextstate[j][l] = next_state;//下一个状态output[j][l] = bin2deci(branch_output, 2);//输出十进制}}#ifdef DEBUGprintf("\nInput:");for (j = 0; j < number_of_states; j++){printf("\n");for (l = 0; l < number_of_states; l++)printf("%2d ", input[j][l]);}printf("\nOutput:");for (j = 0; j < number_of_states; j++){printf("\n");for (l = 0; l < n; l++)printf("%2d ", output[j][l]);}printf("\nNext State:");for (j = 0; j < number_of_states; j++){printf("\n");for (l = 0; l < n; l++)printf("%2d ", nextstate[j][l]);}#endifchannel_output_matrix =(int *)malloc(channel_length * sizeof(int));if (channel_output_matrix == NULL){printf("allocation is failure!!\n");exit(1);}printf("\n");/*信道输出为n行,2列,每行对应于一个通道符号给定的位和每一列对应于一个已编码的位*/ channel_length = channel_length / n;init_adaptive_quant(es_ovr_n0);//进行优化,匹配过信噪比的量化矩阵//量化信道输出,将浮点型数据转化为整形for (t = 0; t < (channel_length * n); t = t + n){for (i = 0; i < n; i++){*(channel_output_matrix + (t / n) + (i * channel_length)) =soft_quant(*(channel_output_vector + (t + i)));//printf("%d ",*(channel_output_matrix + (t / n) + (i * channel_length)));}}/*结束设置:利用网格遍历开始译码通道,在编码完成后结束*/for (t = 0; t < channel_length - m; t++){if (t <= m)//假设从零,所以只是计算路径的所有零状态step = (int)pow(2.0, m - t * 1);//如果不写成2.0,会出现函数重载不明确的错误elsestep = 1;//利用state_history矩阵作为循环缓冲区sh_ptr = (int)((t + 1) % (depth_of_trellis + 1));//sh_ptr为state history矩阵的指针for (j = 0; j < number_of_states; j += step){//重复每个可能的卷积编码器的输出组for (l = 0; l < n; l++){branch_metric = 0;//计算每个通道符号的分支度量,以及所有的信道总和在卷积编码器的输出组信道符号binary_output[0] = (output[j][l] & 0x00000002) >> 1;binary_output[1] = output[j][l] & 0x00000001;branch_metric = branch_metric +abs(*(channel_output_matrix + (0 * channel_length + t)) - 7 *binary_output[0])+ abs(*(channel_output_matrix + (1 * channel_length + t)) - 7 * binary_output[1]);//选择累加误差最小的if (accum_err_metric[nextstate[j][l]][1] > accum_err_metric[j][0] + branch_metric){accum_err_metric[nextstate[j][l]][1] = accum_err_metric[j][0] + branch_metric;state_history[nextstate[j][l]][sh_ptr] = j;//printf("state_history[%d][%d]=%d\n", nextstate[j][l], sh_ptr,state_history[nextstate[j][l]][sh_ptr]);}} //循环l结束} //j结束,更新网格//accum_err_metric矩阵第二列移到第一列,第二列标志为一个很大的数for (j = 0; j < number_of_states; j++){accum_err_metric[j][0] = accum_err_metric[j][1];//printf("accum_err_metric[%d][0]=%d\n", j, accum_err_metric[j][0]);accum_err_metric[j][1] = MAXINT;}//如果网格填充完成,现在需要追踪{for (j = 0; j <= depth_of_trellis; j++)//初始化状态序列矩阵state_sequence[j] = 0;// 找到的最小累积state_history元素x = MAXINT;for (j = 0; j < (number_of_states / 2); j++){if(accum_err_metric[j][0] < accum_err_metric[number_of_states - 1 - j][0]){xx = accum_err_metric[j][0];hh = j;}else{xx = accum_err_metric[number_of_states - 1 - j][0];hh = number_of_states - 1 - j;}if (xx < x){x = xx;h = hh;}}state_sequence[depth_of_trellis] = h;for (j = depth_of_trellis; j > 0; j--){sh_col = j + (sh_ptr - depth_of_trellis);if (sh_col < 0)sh_col = sh_col + depth_of_trellis + 1;state_sequence[j - 1] = state_history[state_sequence[j]][sh_col];}//找出输入序列对应的状态序列在最佳路径*(decoder_output_matrix + t - depth_of_trellis + 1) =input[state_sequence[0]][state_sequence[1]];//printf("译码输出:%d\n", *(decoder_output_matrix + t - depth_of_trellis + 1));} //if状态} // 结束t循环//译码信道中的数据for (t = channel_length - m; t < channel_length; t++){sh_ptr = (int)((t + 1) % (depth_of_trellis + 1));last_stop = number_of_states / pow(2.0, t - channel_length + m);//不需要考虑输入的状态是1,所以确定最高可能的状态数是0for (j = 0; j < last_stop; j++){branch_metric = 0;deci2bin(output[j][0], n, binary_output);for (ll = 0; ll < n; ll++){branch_metric = branch_metric + soft_metric(*(channel_output_matrix + (ll * channel_length + t)), binary_output[ll]);}if ((accum_err_metric[nextstate[j][0]][1] > accum_err_metric[j][0] +branch_metric)){accum_err_metric[nextstate[j][0]][1] = accum_err_metric[j][0] +branch_metric;state_history[nextstate[j][0]][sh_ptr] = j;}}for (j = 0; j < number_of_states; j++){accum_err_metric[j][0] = accum_err_metric[j][1];accum_err_metric[j][1] = MAXINT;}//对所选路径进行选择{for (j = 0; j <= depth_of_trellis; j++)state_sequence[j] = 0;x = accum_err_metric[0][0];h = 0;for (j = 1; j < last_stop; j++){if (accum_err_metric[j][0] < x){x = accum_err_metric[j][0];h = j;}}state_sequence[depth_of_trellis] = h;for (j = depth_of_trellis; j > 0; j--){sh_col = j + (sh_ptr - depth_of_trellis);if (sh_col < 0)sh_col = sh_col + depth_of_trellis + 1;state_sequence[j - 1] = state_history[state_sequence[j]][sh_col];}*(decoder_output_matrix + t - depth_of_trellis + 1) =input[state_sequence[0]][state_sequence[1]];} //if条件状态} //结束t循环for (i = 1; i < depth_of_trellis - m; i++)*(decoder_output_matrix + channel_length - depth_of_trellis + i) =input[state_sequence[i]][state_sequence[i + 1]];free(channel_output_matrix);return;}//初始化三位软判决量化编码器//加入噪声后的量化void init_adaptive_quant(float es_ovr_n0){int i, d;float es, sn_ratio, sigma;es = 1;sn_ratio = (float)pow(10.0, (es_ovr_n0 / 10.0));sigma = (float)sqrt(es / (2.0 * sn_ratio));d = (int)(32 * 0.5 * sigma);for (i = -128; i < (-3 * d); i++)quantizer_table[i + 128] = 7;for (i = (-3 * d); i < (-2 * d); i++)quantizer_table[i + 128] = 6;for (i = (-2 * d); i < (-1 * d); i++)quantizer_table[i + 128] = 5;for (i = (-1 * d); i < 0; i++)quantizer_table[i + 128] = 4;for (i = 0; i < (1 * d); i++)quantizer_table[i + 128] = 3;for (i = (1 * d); i < (2 * d); i++)quantizer_table[i + 128] = 2;for (i = (2 * d); i < (3 * d); i++)quantizer_table[i + 128] = 1;for (i = (3 * d); i < 128; i++)quantizer_table[i + 128] = 0;}//channel_symbol信道中的值为-1或+1的加噪信号int soft_quant(float channel_symbol){int x;x = (int)(32.0 * channel_symbol); //则x的平均值为-32或+32if (x < -128) x = -128; //小于-128,输出128if (x > 127) x = 127; //大于127则输出127return(quantizer_table[x + 128]); //查找量化表}/* this metric is based on the algorithm given in Michelson and Levesque, page 323. */int soft_metric(int data, int guess){return(abs(data - (guess * 7)));//当给出当前状态以及输入时,计算下一个状态,并且计算卷积码编码内容int nxt_stat(int current_state, int input, int *memory_contents){int binary_state[K - 1]; //二进制当前状态int next_state_binary[K - 1]; //二进制下一个状态int next_state; //十进制当前状态int i;deci2bin(current_state, K - 1, binary_state);next_state_binary[0] = input;for (i = 1; i < K - 1; i++)next_state_binary[i] = binary_state[i - 1];next_state = bin2deci(next_state_binary, K - 1);memory_contents[0] = input;for (i = 1; i < K; i++)memory_contents[i] = binary_state[i - 1]; //编码内容return(next_state);//返回十进制的下一个状态}//将十进制数据转化为特殊的二进制,例如:十进制“100011”输出二进制数据100011 void deci2bin(int d, int size, int *b) {int i;for (i = 0; i < size; i++)b[i] = 0;b[size - 1] = d & 0x01;for (i = size - 2; i >= 0; i--) {d = d >> 1;b[i] = d & 0x01;}}//将2进制数据转化为特殊的十进制,例如:二进制“100011”输出十进制数据100011 int bin2deci(int *b, int size) {int i, d;d = 0;for (i = 0; i < size; i++)d += b[i] << (size - i - 1);return(d);}程序辅助模块// viterbi.cpp : 定义控制台应用程序的入口点。
可编程Viterbi译码器设计与实现

De s i g n a n d i mp l e me n t a t i o n f o r p r o g r a mma b l e Vi t e r b i d e c o d e r
码 器 整 体 结 构 图如 图 1所 示 。
2 9
基金项目 : 国 家 自然科 学 基 金 重 点 项 目( 6 1 1 3 6 0 0 2)
《 电子 技 术 应 用 》 2 0 1 4 年 第4 0 卷 第3 期
Ap p l i c a t i o n o f I n t eg r a t e d Ci r cu i t s
是 P MU 与 A C S单 元 的接 口部 分 , 它 与 前 者 的作 用 刚 好 相 反 ,它 是 将 P MU 中 取 来 的 4路 路 径 度 量 信 息 转 换 成 4
路 并行 的路径 度量值 , 然 后送人 到 A C S U进行运 算 。
1 . 4 幸 存 路 径 度 量 存 储 管 理 单 元
d e s i g n e d a n d t h e c o d i n g o f( 2, 1 , 7 )f o r m a t i s i m p l e me n t e d b y A S I C .C o m p a r e d t h e p e f r o r ma n c e o f t h e t w o d e s i g n, t h e ma x i mu m
ma d e t h e b e n e i f c i a l a t t e mp t f o r t h e c o mmu n i c a t i o n s y s t e m i n t e r ms o f c h a n n e l t r a n s mi s s i o n.T h e d e d i c a t e d p r o c e s s o r a r c h i t e c t u r e i s
动态规划:卷积码Viterbi译码算法

动态规划:卷积码的Viterbi译码算法学院:网研院姓名:xxx 学号:xxx 一、动态规划原理动态规划(dynamic programming)是运筹学的一个分支,是求解决策过程(decision process)最优化的数学方法。
动态规划算法通常用于求解具有某种最优性质的问题。
在这类问题中,可能会有许多可行解,每一个解都对应于一个值,我们希望找到具有最优值的解。
动态规划算法与分治法类似,其基本思想也是将待求解问题分解成若干个子问题,先求解子问题,然后从这些子问题的解得到原问题的解。
与分治法不同的是,适合于用动态规划求解的问题,经分解得到子问题往往不是互相独立的。
若用分治法来解这类问题,则分解得到的子问题数目太多,有些子问题被重复计算了很多次。
如果我们能够保存已解决的子问题的答案,而在需要时再找出已求得的答案,这样就可以避免大量的重复计算,节省时间。
动态规划程序设计是对解最优化问题的一种途径、一种方法,而不是一种特殊算法。
不象搜索或数值计算那样,具有一个标准的数学表达式和明确清晰的解题方法。
动态规划程序设计往往是针对一种最优化问题,由于各种问题的性质不同,确定最优解的条件也互不相同,因而动态规划的设计方法对不同的问题,有各具特色的解题方法,而不存在一种万能的动态规划算法,可以解决各类最优化问题。
二、卷积码的Viterbi译码算法简介在介绍维特比译码算法之前,首先了解一下卷积码编码,它常常与维特比译码结合使用。
(2,1,3)卷积码编码器是最常见的卷积码编码器,在本次实验中也使用了(2,1,3)卷积码编码器,下面介绍它的原理。
(2,1,3)卷积码是把信源输出的信息序列,以1个码元为一段,通过编码器输出长为2的一段码段。
该码段的值不仅与当前输入码元有关,而且也与其之前的2个输入码元有关。
如下图所示,输出out1是输入、第一个编码器存储的值和第二个编码器存储的值逻辑加操作的结果,输出out2是输入和第二个编码器存储的值逻辑加操作的结果。
实验五 维特比译码

实验五维特比译码一、实验题目写一个维特比译码器软件,它接受下列输入:1、以八进制形式给出的码的参数,以及2、接收到的比特流。
二、实验目的1、理解和掌握卷积码的概念;2、掌握维特比译码的方法;三、算法设计四、程序分析min_dist(a,b)函数计算两个码字的汉明重量;num_jinzhi(num,jinzhi,wei)函数将十进制的num转换为进制为jinzhi的wei位数;init_state()函数是对state[ ]矩阵进行初始化操作;G矩阵存储码的参数的二进制形式;xinxi[ ]矩阵存储输入比特流;state[ ]矩阵存储如下表的状态;state 1 2 3 4 5 6 7 8 9 10 11 12 13状态点1 当前寄存器的状态输入为输入为时输出编码值寄存器下一状态输入为1输入为1时输出编码值寄存器下一状态输入为时的汉明距离输入为1时的汉明距离四条路径的汉明距离临时存储解码值当前解码值四条路径的汉明距离状态点2状态点3状态点4五、程序代码主函数:display('编码序列:');xinxi=[1 0 1 0 0 1 1 0 1]% in=0;input_g= '75';str_length=length(input_g);%-----------将输入的char型数字转换成二进制数------------------------% for i=1:str_lengthg_dec=base2dec(input_g,8);%将'75'这个8进制数转换成10进制g=num_jinzhi(g_dec,2,3*str_length);%将10进制数转换成2进制endg; %测试gglobal G;for i=1:str_lengthG_yuanshi(i,1:3)=g(3*i-2:3*i);%将得到的二进制数每三个分成一行endG=G_yuanshi';% ------------------------生成查询表格-----------------------------% state=init_state;% ---------------------以下为编码算法------------------------------ % xinxi_bu0=[xinxi 0 0];xinxi_length=length(xinxi_bu0); %这里的长度实际为补0后的长度state_temp=[0 0]; %初始的状态寄存器里面为00for i=1:xinxi_lengthfor j=1:4 %对应四种状态if isequal(state_temp,state{j,1})if xinxi_bu0(i)==0 %若输入为0code(2*i-1:2*i)=state{j,3};%输出码字state_temp=state{j,4}; %下一状态else if xinxi_bu0(i)==1 %若输入为1code(2*i-1:2*i)=state{j,6};%输出码字state_temp=state{j,7}; %下一状态endendbreak;endendenddisplay('信息编码如下:');code%---------------加入错误编码 (这里仅将第三位出错)---------------------- % code(3)=~code(3);display('出错后的编码:');code%---------------------以下为解码算法--------------------------------- % code_length=length(code); %这里的长度实际为补0后的长度state_temp=[0 0]; %初始的状态寄存器里面为00%计算最初始两个码字四条路径的汉明距离state{1,10}=min_dist(state{1,3},[code(1)code(2)])+min_dist(state{1,3},[code(3) code(4)]);%第一条路径的汉明距离state{2,10}=min_dist(state{1,3},[code(1)code(2)])+min_dist(state{1,6},[code(3) code(4)]);%第二条路径的汉明距离state{3,10}=min_dist(state{1,6},[code(1)code(2)])+min_dist(state{2,3},[code(3) code(4)]);%第三条路径的汉明距离state{4,10}=min_dist(state{1,6},[code(1)code(2)])+min_dist(state{2,6},[code(3) code(4)]);%第四条路径的汉明距离state{1,11}=[0 0];%存放第一条路径的前两个码字state{2,11}=[0 1];%存放第二条路径的前两个码字state{3,11}=[1 0];%存放第三条路径的前两个码字state{4,11}=[1 1];%存放第四条路径的前两个码字for i=1:4state{i,12}=state{i,11};end% for i=3:code_length/2;i=3;while i<=code_length/2temp=[code(2*i-1) code(2*i)]; %下一个码字(2位)%------------四种状态分别输入0/1后得到八种状态,每种可能的距离-----------% for j=1:4state{j,8}=min_dist(temp,state{j,3});%输入0的可能最小距离state{j,9}=min_dist(temp,state{j,6});%输入1的可能最小距离end%--------------------------每种状态所对应的下一编码---------------------% if(state{1,8}+state{1,10})<(state{3,8}+state{3,10}) %第一个状态点的路径选取state{1,12}=[state{1,11} 0];%获得下一个解码else state{1,12}=[state{3,11} 0];endif (state{1,9}+state{1,10})<(state{3,9}+state{3,10})%第二个状态点的路径选取state{2,12}=[state{1,11} 1];else state{2,12}=[state{3,11} 1];endif (state{2,8}+state{2,10})<(state{4,8}+state{4,10})%第三个状态点的路径选取state{3,12}=[state{2,11} 0];else state{3,12}=[state{4,11} 0];endif (state{2,9}+state{2,10})<(state{4,9}+state{4,10})%第四个状态点的路径选取state{4,12}=[state{2,11} 1];else state{4,12}=[state{4,11} 1];end%-------------state{:,13}用来临时存放每条路径的汉明重---------------%state{1,13}=min((state{1,8}+state{1,10}),(state{3,8}+state{3,10}));%第一个状态点在取汉明重量较小的那条路径state{2,13}=min((state{1,9}+state{1,10}),(state{3,9}+state{3,10}));%第二个状态点在取汉明重量较小的那条路径state{3,13}=min((state{2,8}+state{2,10}),(state{4,8}+state{4,10}));%第三个状态点在取汉明重量较小的那条路径state{4,13}=min((state{2,9}+state{2,10}),(state{4,9}+state{4,10}));%第四个状态点在取汉明重量较小的那条路径i=i+1;for j=1:4state{j,10}=state{j,13};%更新state{:,10}state{j,11}=state{j,12};%更新state{:,11}endend%-----------------------判断四组码中的最小距---------------------------% dis_temp=[state{1,10} state{2,10} state{3,10} state{4,10}];index=find(min(dis_temp) == dis_temp);if length(index)>1 %有多条最小路径时的处理方式index=index(1);enddecode_length=length(state{index,12});display('解码如下:')decode=state{index,12}(1:decode_length-2) %%去掉最后面的两个0%-------------------测试解码和原码是否完全相同------------------------% for i=1:decode_length-2test(i)=decode(i) - xinxi(i);enddisplay('检测解码是否正确码字如下'); test子函数:%----------------将十进制的 num转换为进制为jinzhi的wei位数--------------%function out=num_jinzhi(num,jinzhi,wei)% yushu=zeros(1,wei);%为了提高计算速度,提前申请输出数据的内存i=1;shang_pre=num;shang_back=num;while shang_back>=jinzhiyushu(i)=mod(shang_pre,jinzhi);%将10进制数变成二进制数i=i+1;shang_back=fix(shang_pre/jinzhi);shang_pre=shang_back;endyushu(i)=shang_back;for i=1:weiout_yushu(i)=yushu(wei-i+1);%将转换后的结果调整高位在前,低位在后endout=out_yushu;function out=init_state()global G;state=cell(4,7); %创建4*7的数组可以存储不同类型的数据state(:)={[0]};state{1,1}=[0 0];%见教材P164 生成初始状态state{2,1}=[1 0];state{3,1}=[0 1];state{4,1}=[1 1];for i=1:4state{i,2}=0;in=0;% % % 输入0temp=[in state{i,1}];state{i,3}=mod(temp*G,2); %输出值state{i,4}=[in state{i,1}(1)];%下一状态% % % 输入1in=1;state{i,5}=1;temp=[in state{i,1}];state{i,6}=mod(temp*G,2); %输出值state{i,7}=[in state{i,1}(1)];%下一状态endout=state;%--------------------------求两个码字的汉明重量------------------------% function out=min_dist(a,b)min_length=length(a);cnt=0;for i=1:min_lengthif a(i)~=b(i);cnt=cnt+1;endendout=cnt;六、程序运行结果。
matlab维特比译码

维特比(Viterbi)译码是一种用于解码卷积码的算法,常用于通信和数据存储系统。
在MATLAB中实现维特比译码主要涉及以下步骤:定义模型参数:首先,你需要定义卷积码的生成矩阵和转移概率。
初始化路径:为每个可能的起始状态初始化一个路径。
递归计算:对于每个时间步,根据转移概率和接收信号,递归地计算每条路径的概率。
选择最佳路径:在每个时间步,选择具有最大概率的路径作为当前状态。
生成输出:根据最佳路径,生成输出序列。
终止条件:当达到终止状态或达到最大迭代次数时,停止计算。
下面是一个简单的MATLAB代码示例,演示了如何实现维特比译码:matlabfunction [decoded, decoded_path] = viterbi_decoder(received, G, num_states, init_state_prob) % received: 接收信号% G: 生成矩阵% num_states: 状态数% init_state_prob: 初始状态概率num_time_steps = length(received);transition_prob = zeros(num_states, num_states); % 转移概率矩阵% 初始化路径和概率矩阵path = zeros(num_time_steps, num_states);path(:, 1) = init_state_prob;path(:, 1) = path(:, 1) .* ones(size(path(:, 1))); % 设置初始路径prob = zeros(num_time_steps, 1); % 概率矩阵prob(1) = path(1,:) .* log2(init_state_prob); % 初始化概率矩阵% 递归计算for t = 2:num_time_stepsfor i = 1:num_statesfor j = 1:num_statestransition_prob(i,j) = G(:,j) * received(t) .* path(t-1,i); % 计算转移概率endend[~, max_state] = max(prob(t-1) + log2(transition_prob)); % 选择最佳状态path(t,:) = zeros(1, num_states); % 重置路径矩阵path(t, max_state) = 1; % 设置当前路径为最佳状态prob(t) = max_state + log2(prob(t-1) + log2(transition_prob)); % 更新概率矩阵end% 选择最佳路径和生成输出[~, max_time] = max(prob); % 选择具有最大概率的时间步作为终止状态decoded = path(:, max_time); % 生成输出序列end请注意,这只是一个基本的示例,可能需要根据您的具体应用和需求进行调整。
卷积 维特比译码 c语言

卷积维特比译码c语言卷积码和维特比(Viterbi)译码是两种在通信系统中常用的编码和解码技术。
卷积码是一种线性分组码,它通过将输入信息序列与一组预定的约束条件进行卷积运算来生成编码序列。
而维特比译码是一种高效的解码算法,用于从接收到的信号中恢复出原始的编码序列。
下面是一个简单的C语言示例,演示了如何实现卷积编码和维特比译码。
请注意,这只是一个基本的示例,实际的实现可能会更复杂,并且需要更多的错误处理和优化。
c复制代码:#include <stdio.h>#include <stdlib.h>#define N 4#define K 2#define G 2// 卷积编码函数void convolutional_encode(int input[K], int code[N]) {int i, j;for (i = 0; i < N; i++) {code[i] = 0;}for (i = 0; i < K; i++) {code[i] = input[i];}for (i = K; i < N; i++) {code[i] = (code[i - 1] ^ code[i - 2]) & G;}}// 维特比译码函数void viterbi_decode(int received[N], int output[K]) {int branch_metric[N][2];int path_metric[N];int max_metric, new_max_metric;int max_path, new_max_path;int i, j, k;for (i = 0; i < N; i++) {path_metric[i] = abs(received[i] - 0); // 初始化路径度量branch_metric[i][0] = abs(received[i] - 0); // 初始化分支度量branch_metric[i][1] = abs(received[i] - G); // 初始化分支}for (i = 0; i < N; i++) {output[i] = 0; // 初始化输出序列}for (i = 0; i < N; i++) {if (branch_metric[i][0] > branch_metric[i][1]) {output[i] = 0; // 选择分支0作为当前的最优路径path_metric[i] = branch_metric[i][0]; // 更新路径度量} else {output[i] = 1; // 选择分支1作为当前的最优路径path_metric[i] = branch_metric[i][1]; // 更新路径度量}if (path_metric[i] > max_metric) {max_metric = path_metric[i]; // 记录最大路径度量max_path = i; // 记录最大路径度量对应的路径值} else if (path_metric[i] == max_metric) { // 如果当前路径度量与最大路径度量相等,则选择路径值较小的路径作为最优路径new_max_metric = path_metric[i]; // 记录新的最大路径度量new_max_path = i; // 记录新的最大路径度量对应的路} else if (path_metric[i] < max_metric && path_metric[i] > new_max_metric) { // 如果当前路径度量比新记录的最大路径度量要小,但是比之前的最大路径度量要大,则更新新的最大路径度量和对应的路径值new_max_metric = path_metric[i]; // 更新新的最大路径度量new_max_path = i; // 更新新的最大路径度量对应的路径值} else if (path_metric[i] < max_metric && path_metric[i] < new_max_metric) { // 如果当前路径度量比新记录的最大路径度量要小,但是比之前的最大路径度量要小,则更新新的最大路径度量和对应的路径值,同时更新最优路径为新记录的最大路径对应的路径值和对应的分支值new_max_metric = new_max_metric; // 更新新的最大路径度量不变new_max_path = i; // 更新新的最大路径度量对应的路径值为当前路径。
第9章 Viterbi译码及其实现

“黑色经典”系列之《DSP嵌入式系统开发典型案例》第9章Viterbi译码及其实现华清远见<ARM开发培训班>培训教材在通信系统中,信息传输的可靠性和有效性是相当重要的。
信息在传输时是经由信道(Channel)传输。
当其在信道传输过程中会受到各种干扰,使得传输信息掺杂各种错误序列在其中。
因此,在通信系统中,良好的纠错码可以有效地应用在信息传输过程中,以降低信息的误码率。
信息在传输时,先由信源发出消息,如语言、图像、文字等,消息进入通信系统后,经由信源编码器编码成信息序列1。
编码过程中,为了使传输有效,还加上一些与传输信息无关的冗余度。
接着信息序列1经过信道编码器编码成信息序列2,序列2是将信息序列1加入了更多的冗余数据(Redundancy Data),以抵抗信道中的各种干扰。
数字信号一般不适合直接在信道上传输,所以调制器是将数字信号转变成模拟信号,使其在信道中传输。
而信道中难免会受到噪声干扰,使信道的输出序列不同于信道的输入序列。
解调器将信道的输出序列由原来的模拟信号转化成数字信号,既是接收序列3,信息序列中因噪声干扰会掺杂一些错误的码元在其中。
信道译码器利用序列中的冗余码元去纠正错误,并且根据信道译码器的结果,产生接近于信息序列1的接收序列1。
整个译码过程是根据信道编码的结果和噪声在信道中的特性所得到的。
理想的结果是所有的错误都被更正回来,即接收序列等同于发送序列。
9.1 Viterbi译码概述在众多的纠错码中,卷积码(Convolutional Code)是一种在实际中得到广泛应用、性能很好的纠错码。
卷积码是不同于分组码的另一种码,它虽然也是把k个信息比特编成n个比特,但k和n都很小,延时小,特别适宜于以串行形式传输信息。
与分组码不同,卷积码中编码后的n个码元不但与当前段的众个信息码元有关,而且与前面(N−1)段的信息有关,编码过程中相互关联的码元为Nn个。
在编码器复杂程度相同的情况下,卷积码的性能优于分组码。
卷积码Viterbi译码的FPGA实现

中国新通信2009.11卷积码及其Viterbi 算法的简介卷积码是1955年由Elias 提出,它与分组码不同,分组码是把k 个信息比特的序列编成n 个比特的码组,每个码组的n-k 个校验位仅与本码组k 个信息位有关,而与其他码组无关。
为了达到一定的纠错能力和编码效率,分组码的码组长度一般都比较大。
编译码时必须把整个信息码组存储起来,由此产生的译码延时随n 的增加而增加。
卷积码编码的一般结构如图1所示。
卷积码的概率译码最早始于1961年由Wozencraft 提出的序列译码,1963年由Fano 进行了改进,提出了Fano 算法。
1967年由Viterbi 提出了另外一种概率译码算法Viterbi 译码算法,它是一种最大似然译码算法,在码的约束度较小时,它比序列译码算法效率更高、速度更快,译码器也较简单。
因此,自Viterbi 译码算法提出以来,无论在理论上,还是在实践上都得到了极其迅速的发展。
Viterbi 算法并不等价于最大似然算法,但是,在一定的条件下,Viterbi 算法就是最大似然译码和最佳译码算法。
2Viterbi 算法的FPGA 实现2.1FPGA 的结构和特点随着半导体器件技术的快速发展,专用集成电陈健李广华(天津工业大学信息与通信工程学院天津300160)摘要本文根据卷积码编码的方式,和Viterbi 译码算法,认真分析了Viterbi 译码算法各部分的功能、特点。
采用硬件描述语言Verilog HDL ,编写了(2,1,7)卷积码的编译码程序,进行了Viterbi 译码器的FPGA 设计。
关键词卷积码Viterbi 算法现场可编程门阵列卷积码Vit e rb i 译码的FP GA 实现1k1n输出图1卷积码编码的一般结构图中国新通信经验与交流EXPERIENCE AND EXCHANGE53CHINA NEW TELECOMMUNICATIONS January 2009路(ASIC )的设计技术也随之不断提高和进步,现场可编程门阵列(FPGA )的出现使得系统级集成电路的设计开发成为可能,电子设计自动化(EDA )技术正给整个电子设计领域带来一场新的革命。
viterbi译码算法C++实现

Viterbi译码算法c++语言实现所对应的结构如下图所示:已知接收序列y=-1,3,3-1,3,试求输入序列x=?用代码实现所写函数包含两部分一个cpp一个h文件Main.cpp如下所示:#include"decode.h"void main(){int re[]={-1,3,3,-1,3};Decode vb;cout<<"trellis图是:"<<endl;vb.getpe(2,1,2,1);vb.v(re,5);cout<<"输入的x序列是:"<<endl;for(int i=0;i<5;i++)cout<<re[i]<<" ";getchar();cout<<endl;}Decode.h如下所示:/*对此进行简单的viterbi译码,将viterbi算法具体的细分,最后得到的函数,并且说明详细by:molongXidian University2013年月日23:12:04*/#include<iostream>#include<vector>using namespace std;class Decode{public:void gettrellis();void getpe(int a,int b1,int b2,int b3);void v(int *re,int n);void get_pre(int now,int *pre);int getcurrent(int j,int i,int in);int min_path(int a,int b);int get_out(int pre,int next);private:int trellis[4][4],s_number,multi1,multi2,multi3;struct get_path{int pre;int out;};};/*函数名称:getpe()作用:作为构造类创建对象的时候进行传参,其中第一个参数a 代表的是存储器个数,第二个参数代表的是所看到的从左到右,输入,存储器,存储器所乘的系数,将这三个参数传给类的对象*/void Decode::getpe(int a,int b1,int b2,int b3){s_number=a;multi1=b1;multi2=b2;multi3=b3;}/*函数名称:gettrellis()作用:得到基本的trellis转移图,存储器的个数为,所以有四个状态,定义trellis[4][4];其中trellis[i][j]代表的是原来的状态是i,其中当j=0的时候表示的是输入为的时候所对应的输出,j=1代表的是当输入为时转移到的下一个状态,j=2时代表的是输入为的时候所对应的输出,j=3的时候表示的是当输入为的时候转移到的下一个状态,所得到的trellis图在为类里面定义的私有变量,均可用*/void Decode::gettrellis(){for(int i=0;i<4;i++)for(int j=0;j<4;j++){int l,h;l=i&1;h=(i>>1)&1;if(j%2==0)trellis[i][j]=multi1*(j-1)+multi2*(2*h-1)+multi3*(2*l-1);//输入是或者的时候的输出elsetrellis[i][j]=(j&2)^h;//所转移到的下一状态cout<<trellis[i][j]<<" ";if(j==3)cout<<endl;}}/*函数名称:v(int *re,int n)输入参数:re为接收到的序列,对其进行直接操作,n为序列的个数输出参数:re同样作为输出参数,来表示最后译码所输出来的结果函数作用:对所接收到的序列进行viterbi译码*/void Decode::v(int *re,int n){gettrellis();vector<vector<int>>remb(4);vector<vector<get_path>>path(4);for(int i=0;i<4;i++){remb[i].resize(n);path[i].resize(n);for(int j=0;j<n;j++){remb[i][j]=0;path[i][j].out=100;path[i][j].pre=100;//remb[i][j]=min_path(remb[pre1][j-1]+getcurrent(pre1,i,re[j]),remb[ pre2][j-1]+getcurrent(pre2,i,re[j]));}}/*remb[i][j]的作用是存储当接收第j个序列的时候,到i状态的路径最小值path[i][j].out表示的是i所对应前一个状态pre到状态i时所对应的输出path[i][j].pre表示的是第i个状态所对应的前一个状态是哪一个通过path的存储,最后在remb里面找到路径的最小值,顺着path来输出最后的译码结果*/for(int i=0;i<4;i++){remb[i][0]=getcurrent(0,i,re[0]);path[i][0].pre=0;path[i][0].out=get_out(0,i);}/* 对remb的首列以及path的首列进行初始化操作*/for(int j=1;j<n;j++){for(int i=0;i<4;i++){int pre[2];/*有两个前状态转移到状态i,定义pre[2]*/get_pre(i,pre);/*得到i所对应的前一状态所对应的两个值*/int a=getcurrent(pre[0],i,re[j]);int b=getcurrent(pre[1],i,re[j]);remb[i][j]=min_path(remb[pre[0]][j-1]+getcurrent(pre[0],i,re[j]),remb [pre[1]][j-1]+getcurrent(pre[1],i,re[j]));/*存储到i的最小距离*/if(remb[i][j]==(remb[pre[0]][j-1]+getcurrent(pre[0],i,re[j]))){path[i][j].pre=pre[0];path[i][j].out=get_out(pre[0],i);/*判断前一状态到底是哪一个,删除一条路径,只剩下最小路径*/}else{path[i][j].pre=pre[1];path[i][j].out=get_out(pre[1],i);}}}int minimum;int min=1000;for(int i=0;i<4;i++){if(remb[i][4]<min){min=remb[i][4];minimum=i;}/*通过对最后一列的寻找得到最小距离*/ }for(int j=n-1;j>=0;j--){re[j]=path[minimum][j].out;minimum=path[minimum][j].pre;/*通过path倒序得到译码所对应的输出*/ }}/*函数名称:get(int pre,int now)输入参数:pre表示前一个状态,now表示当前的状态输出参数:从状态pre转移到状态now所对应的输出函数作用:通过前一状态以及当前状态,来确定输出*/int Decode::get_out(int pre,int now){int out,count=0;for(int j=0;j<2;j++)if(trellis[pre][2*j+1]==now){out=2*j-1;count++;}if(count==0)out=1000;return out;}/*函数名称:get_pre(int now,int *pre)输入参数:当前状态now,*pre代表的是now所对应的前一状态的两个值输出参数:得到前一状态的两个值,即得到是哪两个状态可以转移到状态now 函数作用:得到可以转移到当前状态的前一状态的两个值,例如状态->2,0->2那么这就是得到和这两个值*/void Decode::get_pre(int now, int *pre){int count=0;for(int i=0;i<4;i++)for(int k=0;k<2;k++){if(trellis[i][2*k+1]==now&&count==0){pre[0]=i;count++;}if(trellis[i][2*k+1]==now&&count!=0){pre[1]=i;}}}/*函数名称:getcurrent(int j,int i,int in)输入参数:j表示的是前一状态,i表示的是当前状态,in表示的是接收到的数字输出参数:result表示从状态j到状态i所对应的输出和接收参数in的汉明距离函数作用:得到每一个分支的汉明距离,返回的值是绝对值*/int Decode::getcurrent(int j,int i,int in)//j代表前一个,i表示后一个,in表示输入接收到的数字{int result;int count=0;for(int k=0;k<2;k++){if(trellis[j][2*k+1]==i){result=in-trellis[j][2*k];count++;}}if(count==0)result=10000;return abs(result);}//在此处应该返回的是绝对值,不能是单纯的带符号的值/*函数名称:min_path(int a,int b)输入参数:a和b两个数值输出参数:a和b之间的最小值函数作用:取两条转移路径的最小值*/int Decode::min_path(int a,int b){if(a>b)return b;elsereturn a; }。
卷积编码和Viterbi译码-ZCL.SPACE

卷积编码和Viterbi译码zcl.space目录1引言1 2编码1 3译码2 4回溯4 5回溯深度6 6软译码61引言1965年,Peter Elias发明卷积码。
1967年,Andrew J.Viterbi(高通的创始人之一)发明了一种高效的译码算法:Viterbi算法。
Viterbi译码器可能是当前应用最广泛的一种卷积译码器。
2005年,G.David Forney在南加州大学的Viterbi Conference上提到:每秒,全世界的Viterbi译码器恢复的的二进制比特数是1015。
今天,我们来看看viterbi译码器如何实现译码。
2编码译码之前,先看如何卷积编码。
描述卷积编码器的方法有很多,按照每种描述,我们都可以实现卷积编码。
以约束长度为3,码率为1/2,生成多项式为g0=[111],g1=[101]的卷积码为例,图1左侧给出了移位寄存器电路图表示,图1右侧的表格是左侧的等价描述,显然左侧的表示更直观,右侧的表述更具体。
图1:卷积编码器的两种描述:移位寄存器和输入输出状态表卷积编码器还有一种描述:篱笆图描述。
篱笆图让Viterbi译码过程生动了许多,我认为是一个很伟大的发明,其作用和法拉力用磁感线表示磁场的存在一样,让难以理解的抽象过程瞬间活灵活现。
另外,在Turbo码的译码分析过程中,篱笆图也发挥着非常重要的作用。
图1右侧的表格可以表示如图2所示。
图2:卷积编码器篱笆图描述通过对篱笆图2进行时间上的延展,给定输入,我们可以很容易获得输出。
假设输入为(010111001010001)2(2.1)则编码输出为(001110000110011111100010110011)2(2.2)输出的获得过程如图3所示。
值得注意的是,在图3中,t=16和t=17时刻依然有0输入。
这两个0的作用是冲洗编码器,使得编码器的状态归零。
这样做的好处是Viterbi译码器知道编码器的最后一个状态是零状态。
- 1、下载文档前请自行甄别文档内容的完整性,平台不提供额外的编辑、内容补充、找答案等附加服务。
- 2、"仅部分预览"的文档,不可在线预览部分如存在完整性等问题,可反馈申请退款(可完整预览的文档不适用该条件!)。
- 3、如文档侵犯您的权益,请联系客服反馈,我们会尽快为您处理(人工客服工作时间:9:00-18:30)。
译码主要部分#include"stdafx.h"//#define DEBUGvoid deci2bin(int d, int size, int *b);int bin2deci(int *b, int size);int nxt_stat(int current_state, int input, int *memory_contents);void init_quantizer(void);void init_adaptive_quant(float es_ovr_n0);int soft_quant(float channel_symbol);int soft_metric(int data, int guess);int quantizer_table[256];void sdvd(int g[2][K], float es_ovr_n0, long channel_length, float*channel_output_vector, int *decoder_output_matrix){int i, j, l, ll; //循环控制变量long t; //时间int memory_contents[K]; //记录输入内容int input[TWOTOTHEM][TWOTOTHEM]; //对当前状态以及下一个状态映射int output[TWOTOTHEM][2]; //卷积码编码输出矩阵int nextstate[TWOTOTHEM][2]; //下一个状态矩阵int accum_err_metric[TWOTOTHEM][2]; //误差累计矩阵int state_history[TWOTOTHEM][K * 5 + 1]; //历史状态表int state_sequence[K * 5 + 1]; //状态序列int *channel_output_matrix; //信道输出序列int binary_output[2];int branch_output[2]; //0或者1的输出分支int m, n, number_of_states, depth_of_trellis, step, branch_metric,sh_ptr, sh_col, x, xx, h, hh, next_state, last_stop;n = 2; //1/2为卷积码传输数据的码率m = K - 1;//寄存器个数number_of_states = (int)pow(2.0, m);//状态个数number of states = 2^(K - 1) = 2^mdepth_of_trellis = K * 5;for (i = 0; i < number_of_states; i++){for (j = 0; j < number_of_states; j++)input[i][j] = 0; //输入数组初始化for (j = 0; j < n; j++){nextstate[i][j] = 0;//下一个状态数组初始化output[i][j] = 0; //输出数组初始化}for (j = 0; j <= depth_of_trellis; j++){state_history[i][j] = 0;//历史状态数组初始化state_history[4][16] }accum_err_metric[i][0] = 0;//误差累计矩阵第一列初始化为0accum_err_metric[i][1] = MAXINT;//误差累计矩阵第二列初始化为一个很大的数}/*前向纠错简称FEC(Forward Error Correction),其原理是:发送方将要发送的数据附加上一定的冗余纠错码一并发送,接收方则根据纠错码对数据进行差错检测,如发现差错,由接收方进行纠正*//*产生状态转移矩阵、输出矩阵、输入矩阵*///输入矩阵表示的是FEC编码传输给下一个状态//下一个状态由输入和当前状态给出//输出矩阵for (j = 0; j < number_of_states; j++){for (l = 0; l < n; l++){next_state = nxt_stat(j, l, memory_contents);input[j][next_state] = l;/*计算给定的卷积编码器输出当前状态数和输入值*/branch_output[0] = 0;branch_output[1] = 0;for (i = 0; i < K; i++){branch_output[0] = branch_output[0] ^ memory_contents[i] & g[0][i];branch_output[1] = branch_output[1] ^ memory_contents[i] & g[1][i];}nextstate[j][l] = next_state;//下一个状态output[j][l] = bin2deci(branch_output, 2);//输出十进制}}#ifdef DEBUGprintf("\nInput:");for (j = 0; j < number_of_states; j++){printf("\n");for (l = 0; l < number_of_states; l++)printf("%2d ", input[j][l]);}printf("\nOutput:");for (j = 0; j < number_of_states; j++){printf("\n");for (l = 0; l < n; l++)printf("%2d ", output[j][l]);}printf("\nNext State:");for (j = 0; j < number_of_states; j++){printf("\n");for (l = 0; l < n; l++)printf("%2d ", nextstate[j][l]);}#endifchannel_output_matrix =(int *)malloc(channel_length * sizeof(int));if (channel_output_matrix == NULL){printf("allocation is failure!!\n");exit(1);}printf("\n");/*信道输出为n行,2列,每行对应于一个通道符号给定的位和每一列对应于一个已编码的位*/ channel_length = channel_length / n;init_adaptive_quant(es_ovr_n0);//进行优化,匹配过信噪比的量化矩阵//量化信道输出,将浮点型数据转化为整形for (t = 0; t < (channel_length * n); t = t + n){for (i = 0; i < n; i++){*(channel_output_matrix + (t / n) + (i * channel_length)) =soft_quant(*(channel_output_vector + (t + i)));//printf("%d ",*(channel_output_matrix + (t / n) + (i * channel_length)));}}/*结束设置:利用网格遍历开始译码通道,在编码完成后结束*/for (t = 0; t < channel_length - m; t++){if (t <= m)//假设从零,所以只是计算路径的所有零状态step = (int)pow(2.0, m - t * 1);//如果不写成2.0,会出现函数重载不明确的错误elsestep = 1;//利用state_history矩阵作为循环缓冲区sh_ptr = (int)((t + 1) % (depth_of_trellis + 1));//sh_ptr为state history矩阵的指针for (j = 0; j < number_of_states; j += step){//重复每个可能的卷积编码器的输出组for (l = 0; l < n; l++){branch_metric = 0;//计算每个通道符号的分支度量,以及所有的信道总和在卷积编码器的输出组信道符号binary_output[0] = (output[j][l] & 0x00000002) >> 1;binary_output[1] = output[j][l] & 0x00000001;branch_metric = branch_metric +abs(*(channel_output_matrix + (0 * channel_length + t)) - 7 *binary_output[0])+ abs(*(channel_output_matrix + (1 * channel_length + t)) - 7 * binary_output[1]);//选择累加误差最小的if (accum_err_metric[nextstate[j][l]][1] > accum_err_metric[j][0] + branch_metric){accum_err_metric[nextstate[j][l]][1] = accum_err_metric[j][0] + branch_metric;state_history[nextstate[j][l]][sh_ptr] = j;//printf("state_history[%d][%d]=%d\n", nextstate[j][l], sh_ptr,state_history[nextstate[j][l]][sh_ptr]);}} //循环l结束} //j结束,更新网格//accum_err_metric矩阵第二列移到第一列,第二列标志为一个很大的数for (j = 0; j < number_of_states; j++){accum_err_metric[j][0] = accum_err_metric[j][1];//printf("accum_err_metric[%d][0]=%d\n", j, accum_err_metric[j][0]);accum_err_metric[j][1] = MAXINT;}//如果网格填充完成,现在需要追踪{for (j = 0; j <= depth_of_trellis; j++)//初始化状态序列矩阵state_sequence[j] = 0;// 找到的最小累积state_history元素x = MAXINT;for (j = 0; j < (number_of_states / 2); j++){if(accum_err_metric[j][0] < accum_err_metric[number_of_states - 1 - j][0]){xx = accum_err_metric[j][0];hh = j;}else{xx = accum_err_metric[number_of_states - 1 - j][0];hh = number_of_states - 1 - j;}if (xx < x){x = xx;h = hh;}}state_sequence[depth_of_trellis] = h;for (j = depth_of_trellis; j > 0; j--){sh_col = j + (sh_ptr - depth_of_trellis);if (sh_col < 0)sh_col = sh_col + depth_of_trellis + 1;state_sequence[j - 1] = state_history[state_sequence[j]][sh_col];}//找出输入序列对应的状态序列在最佳路径*(decoder_output_matrix + t - depth_of_trellis + 1) =input[state_sequence[0]][state_sequence[1]];//printf("译码输出:%d\n", *(decoder_output_matrix + t - depth_of_trellis + 1));} //if状态} // 结束t循环//译码信道中的数据for (t = channel_length - m; t < channel_length; t++){sh_ptr = (int)((t + 1) % (depth_of_trellis + 1));last_stop = number_of_states / pow(2.0, t - channel_length + m);//不需要考虑输入的状态是1,所以确定最高可能的状态数是0for (j = 0; j < last_stop; j++){branch_metric = 0;deci2bin(output[j][0], n, binary_output);for (ll = 0; ll < n; ll++){branch_metric = branch_metric + soft_metric(*(channel_output_matrix + (ll * channel_length + t)), binary_output[ll]);}if ((accum_err_metric[nextstate[j][0]][1] > accum_err_metric[j][0] +branch_metric)){accum_err_metric[nextstate[j][0]][1] = accum_err_metric[j][0] +branch_metric;state_history[nextstate[j][0]][sh_ptr] = j;}}for (j = 0; j < number_of_states; j++){accum_err_metric[j][0] = accum_err_metric[j][1];accum_err_metric[j][1] = MAXINT;}//对所选路径进行选择{for (j = 0; j <= depth_of_trellis; j++)state_sequence[j] = 0;x = accum_err_metric[0][0];h = 0;for (j = 1; j < last_stop; j++){if (accum_err_metric[j][0] < x){x = accum_err_metric[j][0];h = j;}}state_sequence[depth_of_trellis] = h;for (j = depth_of_trellis; j > 0; j--){sh_col = j + (sh_ptr - depth_of_trellis);if (sh_col < 0)sh_col = sh_col + depth_of_trellis + 1;state_sequence[j - 1] = state_history[state_sequence[j]][sh_col];}*(decoder_output_matrix + t - depth_of_trellis + 1) =input[state_sequence[0]][state_sequence[1]];} //if条件状态} //结束t循环for (i = 1; i < depth_of_trellis - m; i++)*(decoder_output_matrix + channel_length - depth_of_trellis + i) =input[state_sequence[i]][state_sequence[i + 1]];free(channel_output_matrix);return;}//初始化三位软判决量化编码器//加入噪声后的量化void init_adaptive_quant(float es_ovr_n0){int i, d;float es, sn_ratio, sigma;es = 1;sn_ratio = (float)pow(10.0, (es_ovr_n0 / 10.0));sigma = (float)sqrt(es / (2.0 * sn_ratio));d = (int)(32 * 0.5 * sigma);for (i = -128; i < (-3 * d); i++)quantizer_table[i + 128] = 7;for (i = (-3 * d); i < (-2 * d); i++)quantizer_table[i + 128] = 6;for (i = (-2 * d); i < (-1 * d); i++)quantizer_table[i + 128] = 5;for (i = (-1 * d); i < 0; i++)quantizer_table[i + 128] = 4;for (i = 0; i < (1 * d); i++)quantizer_table[i + 128] = 3;for (i = (1 * d); i < (2 * d); i++)quantizer_table[i + 128] = 2;for (i = (2 * d); i < (3 * d); i++)quantizer_table[i + 128] = 1;for (i = (3 * d); i < 128; i++)quantizer_table[i + 128] = 0;}//channel_symbol信道中的值为-1或+1的加噪信号int soft_quant(float channel_symbol){int x;x = (int)(32.0 * channel_symbol); //则x的平均值为-32或+32if (x < -128) x = -128; //小于-128,输出128if (x > 127) x = 127; //大于127则输出127return(quantizer_table[x + 128]); //查找量化表}/* this metric is based on the algorithm given in Michelson and Levesque, page 323. */int soft_metric(int data, int guess){return(abs(data - (guess * 7)));//当给出当前状态以及输入时,计算下一个状态,并且计算卷积码编码内容int nxt_stat(int current_state, int input, int *memory_contents){int binary_state[K - 1]; //二进制当前状态int next_state_binary[K - 1]; //二进制下一个状态int next_state; //十进制当前状态int i;deci2bin(current_state, K - 1, binary_state);next_state_binary[0] = input;for (i = 1; i < K - 1; i++)next_state_binary[i] = binary_state[i - 1];next_state = bin2deci(next_state_binary, K - 1);memory_contents[0] = input;for (i = 1; i < K; i++)memory_contents[i] = binary_state[i - 1]; //编码内容return(next_state);//返回十进制的下一个状态}//将十进制数据转化为特殊的二进制,例如:十进制“100011”输出二进制数据100011 void deci2bin(int d, int size, int *b) {int i;for (i = 0; i < size; i++)b[i] = 0;b[size - 1] = d & 0x01;for (i = size - 2; i >= 0; i--) {d = d >> 1;b[i] = d & 0x01;}}//将2进制数据转化为特殊的十进制,例如:二进制“100011”输出十进制数据100011 int bin2deci(int *b, int size) {int i, d;d = 0;for (i = 0; i < size; i++)d += b[i] << (size - i - 1);return(d);}程序辅助模块// viterbi.cpp : 定义控制台应用程序的入口点。