Python笔记-移动通信系统 模拟实现16QAM调制

前言

QAM正交振幅调制是Quadrature Amplitude Modulation的缩写,
QAM星座图有好几种,4QAM(也称QPSK)、16QAM和64QAM
此篇文章主要是针对16QAM进行编码实现,4QAM64QAM的实现方法与16QAM类似。
介绍中有些内容的原理不大记得了,如果有错误的话可评论指出。

一、介绍

[1]. 平均功率

由于博主在实现时没有使用的星座图来计算对应的QAM平均功率,所以需要在代码中设置一个平均功率进行计算的。
平均功率的求法如下:

  • 先在QAM中确定一个坐标系,默认两个黑点的间距为2
  • 计算原点到指定点的距离

确定一个QAM对应的平均功率只需要求下方几个例图的红线长度。
对于QPSK来说,黑点00的坐标为(1,1),所以由勾股定理可以得出红线的长度为根号2
QPSK的平均功率就是根号2

对于16QAM黑点0001的坐标为(1,3),所以线段长度为根号10

64QAM中,点坐标(1,7)到原点的距离为根号50

[2]. I输入和Q输入

下方是16QAM的一个例题,我们需要输入一个二进制串,
然后将这个二进制串分成8个分组,对于每个分组的前2位需要分配给I,后两位分配给Q
I相当于星座图的横轴,Q相当于星座图的纵轴。

分组的组数不能随便分,简单来说对于16QAM16 = 2 * 8,组数就是除以2后的数字8(如果没记错的话)

4QAM(QPSK) 16QAM 64QAM
2组 8组 32组

[3]. 映射

经过步骤二的分组后,二进制序列0000 1111 0101 1010 0000 1111 0101 1010
I输入为:00 11 01 10 00 11 01 10
Q输入为:00 11 01 10 00 11 01 10

将二进制数据提取出来后,不能直接传入I和Q进行计算
而是需要通过题目给出的映射关系表,将二进制转换为十进制数据。

原二进制 目标十进制
00 -3
01 -1
11 1
10 3

[4]. I和Q输出

经过步骤三的映射转换后,对应的数据如下
I输入为:-3 1 -1 3 -3 1 -1 3
Q输入为:-3 1 -1 3 -3 1 -1 3

获取了数据之后,就需要进行计算了
计算输出的规则为:输出的数据 = 原输入的十进制 / QAM的平均功率

[5]. 进制转换

经过步骤四的计算后,数据的类型为小数。
在数字通信中是以二进制的方式进行传输,所以计算后需要转换成对应的二进制输出。
小数在进行二进制转换的时候采用的是乘2取整的方式,
对于又是负数又是小数的情况,需要求解正数情况下的二进制数据,然后对结果中除了符号位外的二进制进行逐位取反操作。

二、代码

# coding=utf-8
# 作者:小狐狸
# 题目:模拟实现16QAM调制
def float_to_bin(number):
    '''转二进制串输出'''
    bin_number = ""
    #添加两个符号位
    if number<0:    
        bin_number += "11"
    else:
        bin_number += "00"
        
    num = abs(number)
    for i in range(6): 
## print(num,bin_number)
        num *= 2                    #乘2
        bin_number += str(int(num)) #取整
        num -= int(num)             #保留小数位
        num = round(num,4)          #保留4位小数,消除精度影响
    if bin_number[:2:]=="11":    #负数
        string = "11"           #临时字符串
        for i in range(2,len(bin_number)): #除符号位,逐位取反
            if bin_number[i]=='1':
                string += '0'
            else:
                string += '1'
        return string
    else:
        return bin_number
##print(float_to_bin(0.3162))

avg = pow(10,0.5) #16QAM平均功率
data = list(input("请输入:").split())
input_i = []    #I输入
input_q = []    #Q输入
output_i = []   #I输出
output_q = []   #Q输出
for i in data:
    input_i.append(i[0:2:])     #前两个二进制数据
    input_q.append(i[-3:-1:])   #后两个二进制数据
#求解I输出
for i in input_i: 
    if i=="00":     #00对应-3
        output_i.append(round(-3/avg,4))    #保留4位小数 
    elif i=="01":   #01对应-1
        output_i.append(round(-1/avg,4))    #保留4位小数
    elif i=="11":   #11对应1
        output_i.append(round(1/avg,4))     #保留4位小数 
    elif i=="10":   #10对应3
        output_i.append(round(3/avg,4))     #保留4位小数
#求解Q输出
for i in input_q: 
    if i=="00":     #00对应-3
        output_q.append(round(-3/avg,4))    #保留4位小数 
    elif i=="01":   #01对应-1
        output_q.append(round(-1/avg,4))    #保留4位小数 
    elif i=="11":   #11对应1
        output_q.append(round(1/avg,4))     #保留4位小数 
    elif i=="10":   #10对应3
        output_q.append(round(3/avg,4))     #保留4位小数
#十进制转二进制
for i in range(len(output_i)):
    output_i[i] = float_to_bin(output_i[i]) #I输出
    output_q[i] = float_to_bin(output_q[i]) #Q输出
#输出
print("I输出:",' '.join(str(i) for i in output_i))
print("Q输出:",' '.join(str(i) for i in output_q))

##测试数据
##0000 1111 0101 1010 0000 1111 0101 1010

#0.3162小数转bin
##0.3162*2 = 0.6324 
##0.6324*2 = 1.2648 
##0.2648*2 = 0.5296 
##0.5296*2 = 1.0592 
##0.0592*2 = 0.1184 
##0.1184*2 = 0.2368