基于C/C++的PCM编码与解码简单实现
PCM原理:
将模拟信号变换成二进制信号的方法称为脉冲编码调制(PCM),目前,它不仅应用于通信领域,还广泛应用于计算机、遥控遥测、数字仪表等许多领域。在这些领域中,常将其称为模拟/数字(A/D)转换。PCM系统原理框图如下:
在发送端对输入的模拟信号m(t)进行抽样、量化、编码。编码后的PCM信号是一个二进制数字序列,其传输方式可以采用数字基带传输,也可以是对载波调制后的带通传输。在接收端,PCM信号经译码后还原为量化值序列,但是具有一定的误差,再经低通滤波器滤除高频分量,便可得到重建的模拟信号m(t)。
脉冲编码调制主要经过3个过程:抽样、量化和编码。抽样过程将连续时间模拟信号变为离散时间、连续幅度的抽样信号,量化过程将抽样信号变为离散时间、离散幅度的数字信号,编码过程将量化后的信号编码成为一个二进制码组输出。所谓量化,就是把经过抽样得到的瞬时值将其幅度离散,即用一组规定的电平,把瞬时抽样值用最接近的所谓编码,就是用一组二进制码组来表示每一个有固定电平的量化值。
编码的实现是由编码器完成的,PCM编码器有很多种类型,比较常用的是逐次比较型编码器,其原理方框图如下:
该编码器的任务是把输入的每个样值脉冲编出相应的8位二进制码,除第一位极性码外,其余7位幅度码是通过逐次比较确定的。
(1)极性判决电路:用于确定样值信号的极性,编出极性码C1。
(2)整流器:将双极性的样值信号变成单极性信号。
(3)电路:使每个样值电流的幅度在7次比较编码中保持不变。
(4)比较器:通过对样值电流和标准电流的比较,实现对输入信号抽样值的非线性量化和编码。
(5)记忆电路:用来寄存前面编出的码。
(6)7/11变换电路:将7位非线性码转换成11位线性码。
编码、译码计算过程
编码:
首先判断极性码,正取1,为负则取0;然后写一个函数用来判断段落码,通过二分法逐步与各个标准电平比较,比标准电平大则取1,反之取0;然后再写一个函数确定段内码,同样是利用二分法。
译码:
输入要译码的PCM码组后,通过第一位确定电平的极性,C1=1则为正,C1=0则为负;然后通过C2C3C4确定所处段落以确定起始电平和量化间隔;最后通过C5C6C7确定所处的段内量化间隔级数。得到的结果再加上二分一量化间隔就得到最终的译码电平。
#include
#include
using namespace std;
int parCode[7] = { 16,32,64,128,256,512,1024 };//通过数组,用来确定段落码
int parInsideCode[8] = { 1,1,2,4,8,16,32,64 }; //段内量化间隔
int parInsideSart[8] = { 0,16,32,64,128,256,512,1024 }; //段落起始电平
int flag;
void parJudge(string &result, int testNum) { //确定段落码,&表示引用实参
int low = 0, high = 7, mid;
for (int i = 0; i < 3; i++) {
mid = (low + high) / 2; //逐次比较,判断段落码为1或者0
if (testNum >= parCode[mid]) {
result = result + ‘1’;
low = mid + 1;
flag = mid;
} else {
result = result + ‘0’;
high = mid - 1;
flag = mid - 1;
}
}
}
void parInsideJudge(string &result, int testNum, int parStart, int parSpace) { //确定段内码
int low = 0, high = 17, mid = 8;
for (int i = 0; i < 4; i++) {
int cost = parStart + parSpace * mid; //段落起始电平+量化间隔*序列号
if (cost > testNum) {
result = result + ‘0’;
high = mid;
mid = (low + high) / 2;
} else {
result = result + ‘1’;
low = mid;
mid = (low + high) / 2;
}
}
cout<<“所处的量化级为:”<<mid<<endl;
}
void change(int x) {
//程序功能:将十进制整数转换为二进制数,输出11位线性码
int n = 0; //x为输入的整数
int i = 10; //n为每次x%2取得的余数
int j = 0; //i为整型数组长度减一
int a[11];
for (int i = 0; i < 11; i++) {
a[i] = 0; //数组初始值为0
}
if (x < 0) {
x = -x;
j = 1;
}
while (x > 0.5) { // 除二取余
n = x % 2;
x = (x - n) / 2;
a[i] = n;
i = i - 1;
}
for (int k = 0; k < 11;) {
for (int l = 0; l < 11; l++) { //输出11位线性码
cout << a[k];
k++;
}
if (k != 11) {
cout << " “;
}
}
cout << ‘\n’;
}
int main()
{
int testNum,xuanz;
while (1) {
cout << “1.pcm编码\n”;
cout << “2.pcm解码\n”;
cout << “请输入你的选择:”;
cin >> xuanz;
if (xuanz == 2) {
int i, j, m, n = 16, k, y;//n表示段落起始电平
int a[8] = { 0 };
for (i = 0; i < 8; i++) {
cout << “请输入PCM编码a[” << i << “]:”;
cin >> a[i];
}
m = a[1] * 4 + a[2] * 2 + a[3];//段落序列号
i = m;
if (m == 0)
n = 0;
else {
while (–i)
n = n * 2;
}
k = a[4] * 8 + a[5] * 4 + a[6] * 2 + a[7];
if (m <= 1)
y = n + k * 2 + 1;
else
y = n + k * n / 16 + n / 32;
if (a[0] == 0)
y = -y;
if(m>=1)
cout<<“该编码处于第”<<m+1<<“段,段落起始电平为”<<parCode[m-1]<<”,段内量化间隔为:"<<parInsideCode[m]<<endl;
if(m=0)
cout<<“该编码处于第”<<m+1<<“段,段落起始电平为”<<0<<",段内量化间隔为:"<<parInsideCode[m]<<endl;
cout<<“所处量化级序号为第”<<k<<“级”<<endl;
cout << “输入编码译码后的值:” << y << endl;
} else if(xuanz == 1) {
cout << "请输入编码的值: ";
cin >> testNum;
string result; //字节用来存储编码出来的pcm码
if (testNum < 0) //判断极性码
result = result + ‘0’;
else
result = result + ‘1’;
testNum = fabs(testNum);//去符号
parJudge(result, testNum);//确定段落码
int parSeq = flag + 1; //量化间隔级数
int parStart = parInsideSart[parSeq];//段落起始电平
int parSpace = parInsideCode[parSeq];//量化间隔
parInsideJudge(result, testNum, parStart, parSpace);
cout << "编译出来的pcm码组: ";
cout << result << endl;
int sum = 0;
int j, cvb, jyc, bmh;
for (j = 4; j < 8; j++)
if (result[j] == ‘1’)
sum = sum + pow(2, 7 - j);//确定量化级的序列号
cvb = fabs(parStart + sum * parSpace + parSpace / 2);//为了使量化误差小于量化间隔的一半,译码后的值应该加上量化间隔的一半
jyc = fabs(cvb - parSpace / 2); //7位非线性码编译的电平(除去极性码)
cout << “编码电平:” << jyc << endl;
bmh = fabs(testNum - jyc); //编码后的量化误差
cout << “编码后的量化误差:” << bmh << endl;
cout << “11线性码(pcm):”;
change(jyc);
int errorNum = fabs(cvb - testNum);//确保量化误差的值为正数
cout << “译码出来的值:” << cvb << endl;
cout << "译码后的量化误差: " << errorNum << endl;
cout << endl;
} else {
cout << “输入有误,请重新输入!\n”;
}
}
return 0;
}
运行结果
对1270进行编码:其PCM码组为11110011,量化电平(编码电平)为1216,量化误差为1270-1216=54,译码电平为1216+32=1248,译码后的量化误差为1270-1248=22,运行结果如下:
对01110011进行译码:极性码为0,代表该数值为负数,段落码为111,代表处于第8段,段落起始电平为1024,段内码为0011,表示处于序号为3的量化间隔内,所以1024+3*64=1216,再加上一半的量化间隔,所以译码电平为1216+32=1248,再把极性加上,最终的译码电平为-1248,运行结果如下:
这个实验只是PCM的一个简单实现编码和译码的过程,具体有些功能并没有实现,代码还有待完善。