基带BPSK、QPSK、16QAM、OFDM仿真C实现

移动通信系统仿真——标准C实现的利与弊
  github/28hua
摘 要
本文介绍采用标准C语言搭建BPSK、QPSK、16QAM、OFDM基带移动通信仿真系统的原理和实现,深刻揭示了使用标准C语言进行移动通信系统仿真的优势和弊端,认为虽然标准C具有运行速度快和占用内存小的优点,但是缺乏科学计算库,抽象层次低,内存管理复杂,并发严重依赖操作系统,在语言层面和标准库没有对多线程的直接支持,不适合进行模型的快速构建和理论验证。本系统除C语言标准库外仅仅依赖第三方库FFTW3,程序采用高内聚、低耦合的模块化程序设计思想进行设计,代码重用率高,具有较强的可扩展性和可重用性。
本文所有仿真系统模仿香农经典通信系统模型,即点对点通信,不涉及网络通信。信道模型包含高斯噪声信道和瑞利信道。系统仿真的结果是误比特率曲线,通过比较不同调制方法的误比特率曲线,分析其抗噪声、抗衰落性能。
关键字:BPSK  QPSK  16QAM  OFDM  标准C

电汽锅一、 简介
1. 各种调制方法简介
BPSK
BPSK即二进制相移键控,采用相位相反的正弦载波表示0和1。
QPSK
BPSK的一个符号代表一个比特,即,在QPSK中,一个符号代表两个比特,即。QPSK的编码采用格雷码,在星座图中某点与距离其最近的点的汉明距离最小。
16QAM
对于BPSK和QPSK,每种信号的能量或者振幅是相同的,对于16QAM可以有三种能量或振幅的信号。OFDM星座图中的点也采用格雷码表示,某点与距离其最近的点的汉明距离最小。
OFDM
BPSK、QPSK、16QAM都只有一路载波,而OFDM采用多路相互正交的子载波,子载波的频率间隔为OFDM信号的符号周期,其调制解调框图如下所示。
2. BER、SNR和
BER:误比特率,即
SNR:信噪比
:信噪比的分贝形式:
: 功率效率
:带宽效率
: 的分贝形式:
在编程实现中,假设各种调制系统的发送信号平均每比特能量相同且为,假设码元发送速率已知且,通过改变的值测试不同功率效率下的误比特率,待求变量是,由上述方程和假设已知条件可得,
假设,则
3. 标准C回顾
为使用C语言进行系统仿真,必须深入理解C语言语法,其中最重要的是数组指针
在C的世界里,数组和指针都是基本数据类型。指针变量中存放的是某段内存空间的起始地址,指针的类型暗示指针做自加运算时偏移的字节数。当数组作为函数参数或者函数返
回值或者赋值给指针时,数组被隐式转换为指针,丢失数组大小信息。
局部变量存储在栈上,局部变量离开其作用域时,内存不能再被访问。举例,函数体内不加static关键字定义的数组作为返回值返回,此返回值赋值给指针,试图使用指针访问之前在函数体内定义的数组将会发生错误,因为此数组在栈上分配,栈上内存在函数结束时被计算机回收。为使函数体内分配的内存具有跨函数的生命周期,采用动态内存分配或者static关键字修饰,前者灵活但不易编程,后者编程方便但所分配内存直到程序结束才释放。
二、 系统模型
对于BPSK、QPSK、16QAM、OFDM仿真系统,编程实现由易到难,但简单仿真系统的子模块经常可以复用到复杂仿真系统,并且简单仿真系统可以为复杂仿真系统提供设计思路。
本文从最简单的BPSK仿真系统开始,规定各种仿真系统平均每比特能量。首先生成0/1伪随机序列,对于基带仿真系统,不考虑高频正弦载波,因此使用1表示比特1,使用-1表示比特0。然后模拟信道传输,添加噪声(和衰落),如果发送x,噪声,则接收,如果衰落,则接收。最后判决,计算误码率。
对于QPSK仿真系统,由于QPSK系统的星座图有四个点,每个点离原点距离相同,故能量相同,又因为,因此星座图中每个点的模均为。本系统使用的映射如下
并且采用格雷编码使汉明距离最小的码相邻。
对于16QAM仿真系统,,星座图包括十六个点,离远点距离有三类,分别用a,b,c表示,令,所以
由方形QAM系统星座图的特点易得,因此x轴和y轴的投影长度只有两种。本系统采用映射如下,依然采用格雷编码:
0, 0, 0, 0->x1, y1    0, 0, 0, 1->x2, y1    0, 0, 1, 0->x1, y2    0, 0, 1, 1->x2, y2
0, 1, 0, 0->x1, -y1    0, 1, 1, 0->x1, -y2    0, 1, 0, 1->x2, -y1    0, 1, 1, 1->x2, -y2
1, 0, 0, 0->-x1, y1    1, 0, 1, 0->-x1, y2    1, 0, 0, 1->-x2, y1    1, 0, 1, 1->-x2, y2
1, 1, 0, 0->-x1,-y1    1, 1, 0, 1->-x2,-y1    1, 1, 1, 0->-x1,-y2    1, 1, 1, 1->-x2,-y2
其中x1=y1=,x2=y2=
对于OFDM系统,本次仿真产生伪随机0/1序列后立刻做16QAM映射,然后串并变换,快速傅里叶反变换,添加循环前缀,并串变换,模拟噪声(和衰落)信道,接收端采用与发送端相逆的过程,最后统计误码率。
各种调制系统流程图如下图所示:
三、 编程实现
1. 瑞利随机数与高斯随机数的产生
瑞利随机数的分布函数为:
在概率论中,根据给定的随机变量x的分布和函数g(x)容易得到y=g(x)的分布。其逆问题是已知随机变量x的分布和y的分布,确定g(x),使得g(x)服从y的分布。根据概率论理论:
产生瑞利随机数的思想是由[0-1]均匀分布的随机变量u和瑞利分布函数求解g(u),
令:
解得
    根据概率论理论,如果x和y是相互独立的零均值方差为的正太随机变量,那么服从瑞利分布,服从的均匀分布。因此,可以由
产生零均值,方差为的高斯随机数。
2. 动态分配二维矩阵的两种方法
方法一:
  方法二:
    方法一的优点是需要调整数组维数只需realloc,缺点是malloc和free的复杂度都是o(n),并且访问每一个元素需要两次寻址;方法二的优点是malloc和free的复杂度都是o(1),访问每个元素只需要一次寻址,缺点是调整数组维数非常不便,比如OFDM仿真系统需要添加循环前缀,使用第二种方法需要对整个数组做拷贝。
3. 内存管理
使用MatLab、Java等带有垃圾回收机制的编程语言很少操心内存管理,但是在C语言给予用户直接操纵内存的自由,而用户需要为这种自由承担内存管理的责任,为此需要深刻理解操作系统内存布局。
一种典型的操作系统内存由低字节到高字节包括:文本段(代码段)、已初始化数据段、未初始化数据段、栈、堆。
文本段存储将要执行的代码,多个程序实例可以共享文本段,当多个程序实例链接相同动态库时,内存只载入一份此库的副本。文本段还是只读的,程序不能改变自身。
已初始化数据段存储已经初始化的全局变量。
未初始化数据段存储尚未初始化的全局变量,存储在此段中的变量,即所有未初始化的全局变量在程序执行前自动被初始化为0或者NULL。
栈是栈帧的集合,每当一个函数被调用时会为它分配一个栈帧,栈帧中存放被调用函数所有局部变量,传递给被调用函数的参数,被调用函数返回后将要执行的下一条指令的地址。
堆,是C调用malloc或者C++调用new分配内存的区域。堆和栈的生长方向相反。
C语言的内存管理是一项极易犯错的编程任务,培养好的编程习惯可以降低错误发生率
为函数编写清晰的注释,尤其涉及内存分配与释放的函数
尽力把内存分配逻辑和释放逻辑写到同一作用域内
家庭视频电话代码检查,调用malloc的次数等于调用free的次数
红外线烘干箱● 检查malloc返回值,判断内存是否分配成功
free指针指向的内存后,将指针赋值为NULL
浅复制时,当内存被释放后所有指向此处内存区的指针应赋值为NULL;深复制时,复制后的内存也要释放。
4. 头文件一览
#ifndef SIMULAT_H
#define SIMULAT_H
#include <stdbool.h>
#include <fftw3.h>
#include <math.h>
typedef unsigned long ulong;
typedef struct Point {
    double SNR_db;
    double BER;
} Point;
/*串行序列*/
typedef struct Serial {
    double * signal;
    ulong N;
} Serial;
/*串行序列, but存储的是复数*/
typedef struct Serial_C {
    fftw_complex * signal;
    ulong N;
} Serial_C;
/*m路并行*/
typedef struct Parallel {
    double ** signal;
    ulong M;
    ulong N;
} Parallel;
/*m路并行, but存储的是复数*/
typedef struct Parallel_C {
    fftw_complex ** signal;
    ulong M;
    ulong N;
} Parallel_C;
double gen_uniform_random(void);
double gen_rayleigh_random(double sigma);
double gen_standard_normal_random(void);
double gen_normal_random(double mean, double sigma);
int gen_binomial_random(double p);
Serial* gen_signal(ulong N);
void bipolar(Serial* serial, double scale);
void add_noise(Serial * serial, double mean, double sigma);
void add_noise_c(Serial_C * serial_c, double mean, double sigma);
void add_noise_and_fading(Serial * serial, double n_mean,
        double n_sigma, double r_sigma);
void add_noise_and_fading_c(Serial_C * serial, double n_mean,
        double n_sigma, double r_sigma);
Parallel* serial_to_parallel(Serial * serial, ulong M);
荸荠削皮机
Parallel_C* serial_to_parallel_c(Serial_C* serial, ulong M);
Serial_C* parallel_to_serial_c(Parallel_C* parallel_c);
void add_cycle_prefix(Parallel_C* parallel_c);
void del_cycle_prefix(Parallel_C* parallel_c);
Serial* BPSK(Serial* serial);
Serial* rBPSK(Serial* serial);
Serial_C* QPSK(Serial* serial);
Serial* rQPSK(Serial_C* serial_c);
Serial_C* QAM16(Serial* serial);
Serial* rQAM16(Serial_C* serial_c);
void judge4QAM16(Serial_C* serial_c);
void judge4QPSK(Serial_C* serial_c);
void judge4BPSK(Serial* serial);
void fft(Parallel_C* parallel_c);
void ifft(Parallel_C* parallel_c);
void transpose_array(Parallel* parallel);
#endif
5. 绘图脚本
啪啪圈
"""
绘出误码率/信噪比曲线
"""
import sys
import csv
import matplotlib.pyplot as plt
import matplotlib.pylab as plb
from collections import namedtuple
def get_x_y(result_file):
    """
    绘出误码率/信噪比曲线
    """
    with open(file=result_file, mode='r') as data:
        data_csv = ader(data)
        headings = next(data)
        SNR_BER = namedtuple("SNR_BER", headings)
        SNR_db, BER = [], []
        for line in data_csv:
            row = SNR_BER(*line)磁流体发电机
            SNR_db.append(float(row.SNR_db))
            BER.append(float(row.BER))
    return SNR_db, BER
if __name__ == '__main__':
    files = sys.argv[1:]
    for file in files:
        SNR_db, BER = get_x_y(file)
        plt.title("")
        plt.yscale("log")
        plt.plot(SNR_db, BER, 'r^-')
    id(True)
plt.show()
四、 仿真结果及讨论
所有仿真系统的产生一千万个伪随机序比特,噪声服从零均值,方差为1的高斯分布,瑞利衰落的参数取0.5,OFDM为16路复用。仿真系统的所有误码率数据保存为.csv格式的文件,读者可以使用最擅长的绘图工具绘制误码率曲线对比研究。笔者使用Python作图,命令行参数为需要比较的误码率文件。

本文发布于:2024-09-22 10:37:25,感谢您对本站的认可!

本文链接:https://www.17tex.com/tex/4/163488.html

版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系,我们将在24小时内删除。

标签:系统   内存   指针   数组   函数   使用   采用
留言与评论(共有 0 条评论)
   
验证码:
Copyright ©2019-2024 Comsenz Inc.Powered by © 易纺专利技术学习网 豫ICP备2022007602号 豫公网安备41160202000603 站长QQ:729038198 关于我们 投诉建议