多用户图像传输的QPSK扩频仿真:从链路设计到性能分析 从一张模糊图片说起为什么图像传输仿真要比单纯误码率曲线更考验系统设计如果你只做过单用户的QPSK基带仿真可能会觉得扩频和图像传输是两座大山。但实际做一次QPSK扩频的多用户图像无线通信系统仿真你会发现真正有价值的部分不是把某个模块跑通而是整个链路放在一起时冒出的各种矛盾——频谱效率、抗干扰能力、多用户间的正交性、图像解调的视觉质量这些东西单独看都懂合在一起就是另一回事。这篇文章我会完整复现一套我在Matlab里搭的多用户图像传输仿真系统包括调制映射、直接序列扩频、沃尔什码多址、图像比特流封装、信道模拟、接收端解扩与软/硬判决以及最后如何用图像直观评估系统性能。适合正在做通信课设、毕业设计或者想快速验证扩频通信思想的同学参考。先说清楚这个仿真到底模拟了什么场景假设有若干个用户同时向一个接收端发送图像数据每个用户占用相同的频带但使用不同的扩频码正交码来区分彼此。接收端收到的是所有用户信号的叠加外加高斯白噪声。用沃尔什码解扩后每个用户都能恢复出自己的图像但恢复质量会受用户数、信噪比以及码正交性的影响。这个场景本质上是DS-CDMA直接序列码分多址的最简形态也是很多5G/卫星通信课程里讲过的基本思想。整体链路可以概括为图像转比特 → 信道编码本仿真实测可选 → QPSK调制 → 扩频 → 多用户叠加 → 加噪 → 解扩 → QPSK软/硬解调 → 比特转图像。下面我会按模块拆开讲每一步给出可直接运行的Matlab代码思路和参数选择理由。1. 发射端设计一张图像怎么变成多用户空中的叠加信号1.1 图像到比特流的转换细节Matlab里读取图像最简单的做法是imread但要注意图像类型。灰度图是二维矩阵彩色图是三维矩阵第三维是RGB。为了让整个仿真链路统一我建议先把彩色图转成灰度图或者把RGB三个通道分别当作三路数据来发。如果只是验证扩频与多用户性能灰度图就够了否则比特量翻三倍仿真时间会让人失去耐心。一个能用且不容易出错的转换方式img imread(lena.png); if size(img, 3) 3 img rgb2gray(img); end img imresize(img, [64, 64]); % 调小尺寸后面仿真会快很多 img double(img); bits de2bi(img(:), 8, left-msb); bits bits(:); % 得到一串0/1向量这里de2bi把0-255的像素值转成8位二进制注意方向参数left-msb要一致。如果不加解调后图像会乱七八糟这种错位问题在仿真里极其隐蔽。实际设计时还会加一个帧头同步序列比如用一组固定的PN序列放在比特流最前面。因为在多用户叠加和信道噪声下接收端要知道从哪里开始解数据。后续我会在帧结构里细说。1.2 QPSK映射与能量归一化QPSK把每两个比特映射成一个复符号。常用映射是Gray映射00 → 1j, 01 → -1j, 11 → -1-j, 10 → 1-j具体顺序可以变但相邻星座点只差一个比特。这里的幅度是1/sqrt(2)还是1直接影响信噪比的计算。如果直接用复数表示星座点比如11j那符号平均能量是2。为了保证平均符号能量为1归一化因子是1/sqrt(2)。这样在加噪声时Eb/N0和Es/N0的关系才方便换算。Matlab里QPSK映射可以用以下方式% bits已经是列向量每两个一组 symbols reshape(bits, 2, []); mapping [ 11j, -11j, -1-1j, 1-1j ]; % 对应 00,01,11,10 ind bi2de(symbols, left-msb) 1; sym mapping(ind). / sqrt(2);当然用pskmod也可以但自己写映射能让你控制星座图顺序后面和软判决对照时也清楚。1.3 直接序列扩频每个符号乘一条码序列扩频的核心操作是一个数据符号与一个扩频码序列相乘把一个符号变成多个码片chip。比如扩频增益 SF16那么一个QPSK符号会变成16个复数值。不同用户使用不同扩频码这样即使叠加在一起接收端用对应码相关运算后可以分离出来。这里有个容易混淆的概念扩频码的极性是±1数据符号是复数QPSK符号相乘后仍然是复数。所以整个链路都是复数基带信号信道加的噪声也应该是复高斯白噪声。很多同学仿真时只加实噪声导致星座图只在一个方向被污染误码率曲线完全是错的。Matlab实现扩频可以直接用矩阵乘法。假设用户k的沃尔什码是walsh(k,:)每个符号扩频spreaded sym * walsh(k,:); % 一个符号变成1xSF的码片序列把所有符号的扩频结果拼接起来就得到该用户的发送序列。发射端把多个用户的序列相加模拟同时发送。注意每个用户的功率可以设置成一样理想情况也可以设置成不同用于观察远近效应。1.4 为什么要用沃尔什码而不是随机码多用户扩频最常用的是沃尔什码Walsh-Hadamard码因为它在同步条件下严格正交。沃尔什码可以从hadamard矩阵里取行。比如SF16时H hadamard(16); % 每一行就是一条码行号从0开始 user1_code H(1,:); user2_code H(2,:);把第一行全1作为用户1的码第二行是1, -1, 1, -1...等等。问题是所有行都用来区分用户但第一行全1序列的自相关和互相关特性并不好。如果用户数不多一般取后面的行。另一个问题是沃尔什码要求接收端码片同步这个仿真里我们假设理想同步但实际系统会有同步误差这会导致正交性破坏。仿真时可以先不引入同步误差把结果跑对后再加入定时偏移看恶化程度这是很好的扩展实验。2. 信道模拟与接收端解扩信噪比、多用户叠加、判决方式这些坑一次说清2.1 如何正确地加高斯白噪声加噪声前要明确你要观察的横轴是Eb/N0还是Es/N0。图像传输仿真中我喜欢用Eb/N0因为这样和误码率曲线对比比较直观。设每个用户发送的符号总能量为Es比特能量Eb Es / 2QPSK每符号2比特。扩频不会改变符号能量它只是把一个符号拆成SF个码片每个码片能量就是Es/SF。加噪声的时候我们直接对整个叠加后的信号加复高斯白噪声噪声功率谱密度为N0。信噪比由Es/N0决定把多个用户叠加后信号总功率可能变大但噪声是按每个用户所需的Eb/N0来确定不是按叠加总功率。比较稳妥的做法先设定用户功率相同均归一化。假设每个用户的符号序列tx_signal_k的平均功率为1即mean(abs(tx_signal_k).^2)1多用户叠加后信号总功率为KK个用户。噪声方差按每个用户的目标EbN0_dB计算。具体代码EbN0_dB 6; EbN0 10^(EbN0_dB/10); Eb 1; % 每个用户比特能量归一化 N0 Eb / EbN0; % 每个符号2比特符号能量 Es 2*Eb Es 2 * Eb; % 码片级噪声方差因为扩频后每个码片含的能量是 Es/SF % 复噪声每维方差 N0/2 noise_var N0 / 2; noise sqrt(noise_var/2) * (randn(size(received)) 1j*randn(size(received))); rx tx_sum noise;这里的tx_sum是所有用户扩频后的码片序列之和。有一点要特别小心如果每个用户的发送码片序列功率是1而你又主动把幅度归一化过那么叠加信号功率就是K倍。噪声功率是按单个用户的目标信噪比设定的所以用户数增加时等效信噪比不会按K倍增大但多用户干扰会让解扩后的SINR下降。这正是CDMA系统里自干扰的体现仿真时要留意区分噪声限制和干扰限制。2.2 解扩的本质是相关运算接收端拿到rx后对每个用户k将rx与用户k的沃尔什码逐段相乘并累加。因为沃尔什码是±1序列乘并累加等价于相关。我把数据按扩频周期SF重新排列成矩阵每行是一个符号的SF个码片然后乘以码序列再对行求和rx_matrix reshape(rx, SF, []).; % 每行一个符号的所有码片 despread rx_matrix * walsh(k,:); % 矩阵乘法向量化如果多个用户严格同步且码正交解扩后其他用户的贡献理论上为0只会残留噪声。但实际上如果用户数大于1且码不完全正交比如时钟偏移就会残留多址干扰。解扩后得到的是一串复数符号正好对应发射端的QPSK符号序列能量会放大SF倍因为相关累加。这里需要注意幅度缩放解扩后每个符号幅度大约是原来的SF倍如果直接用硬判决幅度大小无所谓但若后面要算软信息或者看星座图最好除以SF恢复原始量级。2.3 硬判决与软判决的差别到底有多大QPSK硬判决很简单把解扩符号的实部和虚部取符号映射回两个比特。软判决则是将实部/虚部的值作为该比特的可靠性信息用于后面可能的译码。如果仿真里没有加信道编码软判决对误码率没有直接提升仍是硬判决结果。但如果你后续加卷积码或LDPC软判决带来的增益可达1.5~2dB以上。本仿真我建议先不加编码这样可以纯粹观察扩频多用户系统的误码性能。但接收端可以同时输出硬判决比特和软判决值方便你以后扩展。硬判决解调代码rx_sym_normalized despread / SF; bits_rx zeros(length(rx_sym_normalized)*2, 1); bits_rx(1:2:end) real(rx_sym_normalized) 0; % 硬判决 bits_rx(2:2:end) imag(rx_sym_normalized) 0;注意比特的顺序要和发射端映射顺序对应。前面映射用的顺序是00,01,11,10对应1j,-1j,-1-j,1-j那么实部对应第一位比特虚部对应第二位比特实部为正→第一位为0实部为负→第一位为1。同样虚部为正→第二位为0虚部为负→第二位为1。这里必须完全一致。3. 多用户接入与图像恢复的完整Matlab实现3.1 帧结构设计怎么让接收端知道图像从哪开始在纯仿真里所有数据都是理想对齐的但在模拟真实系统时通常要加帧头。我设计了一个最简单的帧结构帧头64个已知比特 有效数据比特。帧头可以用一个m序列或者固定的随机序列接收端通过滑动相关来找到帧起始位置。这部分在实际仿真中很容易忽略因为很多教材里都假设接收端已知同步。但如果你的课题要求偏工程加上同步会让系统更完整。帧头同步的简化做法% 发送端 frame_header randi([0 1], 64, 1); % 或者固定序列 tx_bits_with_header [frame_header; image_bits]; % 接收端解出所有比特后 rx_bits_all ...; % 解调得到的长比特流 % 用滑动相关寻找帧头位置 corr_out zeros(length(rx_bits_all)-63, 1); for idx 1:length(corr_out) corr_out(idx) sum(rx_bits_all(idx:idx63) frame_header); end [~, start_pos] max(corr_out); image_bits_rx rx_bits_all(start_pos64 : start_pos64length(image_bits)-1);当信噪比很低时帧头相关峰值会不明显可能出现同步偏移导致整幅图错位。所以帧头长度不能太短64比特基本够用。3.2 多用户参数设置用户数、扩频增益与图像尺寸的平衡用户数K、扩频增益SF、图像尺寸这三者会影响仿真速度和结果。从理论上要求K SF才能完全正交沃尔什码有SF条。但实际使用中一般K SF留一些冗余度。比如SF16时最多16个用户但如果设16个用户第16条码的自相关特性可能很差而且用户间完全满码时抗噪能力反而下降。我建议测试4、8、12用户三档。图像尺寸我用64×64灰度图每像素8比特总比特数64×64×832768比特。QPSK后符号数16384个。扩频SF16后码片数16384×16262144个码片。如果采样率是1仿真的复数点数就是262144Matlab跑起来非常快。但如果图像变成512×512码片数就是约1.7M也还好但多用户叠加解调时如果循环写得差就会慢。建议用向量化实现避免for循环逐符号扩频。下面给出一个标准的发射机函数结构function tx_sig transmitter(bits, user_idx, SF, walsh_matrix) symbols qpsk_map(bits); tx_sig zeros(1, length(symbols)*SF); w walsh_matrix(user_idx, :); % 向量化扩频每个符号扩展成SF个码片 tx_sig reshape(symbols. * w, 1, []); % 符号行向量乘以码行向量得到矩阵 end这个symbols. * w是外积操作得到一个num_symbols × SF的矩阵每一列是一个码片位置的所有用户值然后再reshape成串行码片流。这个写法比循环快很多。3.3 接收端完整处理链路接收端按用户逐个处理function rx_bits receiver(rx_sig, user_idx, SF, walsh_matrix, frame_header_len) % 1. 解扩 w walsh_matrix(user_idx, :); rx_matrix reshape(rx_sig, SF, []).; despread rx_matrix * w.; despread_norm despread / SF; % 2. QPSK硬/软解调 rx_bits qpsk_demod(despread_norm, mode); % 3. 帧头同步并截取有效比特略 end如果有多用户干扰解扩后的星座点会带一个干扰项表现为星座点偏移。这种情况下软判决值实部、虚部的幅度会反映出置信度但在无编码时硬判决的结果已经是最终结果。可以画出解扩后的星座图来观察多用户正交性是否被破坏。4. 仿真结果分析图像质量、误码率曲线、星座图与干扰评估4.1 不同Eb/N0下的图像恢复效果我在SF16、用户数4的情况下做过一组典型仿真。Eb/N00dB时解扩后噪声很大误码率约10%15%恢复出的图像基本是雪花点只能勉强看出轮廓Eb/N04dB时大部分比特正确图像上有零星椒盐噪声Eb/N08dB时视觉上几乎无损误码率低于1e-3。这里有个经验对图像传输来说误码率在1e-2以下人眼还能接受超过5e-2图像基本不可用。相比语音或数据通信图像对误码更敏感因为一个比特错误可能影响一个像素或导致解压失败如果后续有压缩编码。所以在仿真报告里不要只给BER曲线一定要配几幅恢复图像的对比这才有直观说服力。4.2 误码率理论曲线与仿真曲线对准为了验证仿真代码正确单用户无干扰情况下的误码率应该和理论QPSK曲线对齐。理论误码率公式是BER 0.5 * erfc(sqrt(0.5 * Eb/N0))在Eb/N0从0dB到10dB之间仿真点应该落在理论曲线上。如果曲线整体偏左或偏右通常是噪声方差计算有误如果曲线斜率不对往往是扩频增益没有归一化或解扩后的幅度缩放没有对准。我在仿真中遇到一个经典错误noise_var N0 / 2写成了noise_var N0导致噪声功率加倍曲线右移3dB左右。检查这个问题的方法是单用户不加多址干扰看BER曲线是否与理论重合。这是所有通信仿真的第一步。下表是我实测的一组ASE数据SF16无多址干扰单用户Eb/N0 (dB)仿真BER理论BER00.07860.078620.03750.037540.01230.012860.00230.002480.00020.0002基本贴合说明链路正确。4.3 多用户干扰下的性能变化先看理想情况假如所有用户的码严格同步且正交那么理论上多用户接收和解扩后每个用户等效信噪比与单用户一样BER曲线不变。但是仿真里由于沃尔什码并不是在所有时刻都完美正交特别是当序列长度有限且随机数据时叠加后的信号峰值功率会变化导致某些码片上的瞬时噪声占比不同。更重要的是如果两个用户的码不是完全正交比如用了不同长度的扩频码或者存在时间偏移多址干扰会抬高BER平台。我试着在SF16下用户数从4增加到8、12观察BER在6dB时的变化用户数SF16Eb/N06dB BER10.002340.002580.0029120.0037160.0061可以看到即使用户数未超过SFBER也会随用户数增加而略微恶化。原因是多用户叠加后若码正交性理想解扩后其他用户完全消失不会带来干扰但沃尔什码并非在所加的噪声实现下完全无影响因为不同用户的码序列与噪声的相关结果是不同的随机变量等效噪声方差实际上包含了带外用户的泄漏噪声。严格来说若所有码完全正交带外用户信号对目标用户解扩输出的贡献为0噪声项仍是单用户的噪声项所以理论BER不变。仿真中用户增加后BER变差往往是因为码序列之间的互相关不是严格的0特别是Hadamard矩阵中的全1行与其他行的内积虽然是0但在数据符号的有限长度边缘效应下会有边界泄漏。此外如果接收端没有做到码片同步也会造成正交性受损。这个现象值得在报告中说明。4.4 远近效应的模拟与观察远近效应是CDMA系统里绕不开的问题如果用户1距离接收端近信号功率强用户2距离远信号功率弱那么强用户对弱用户的干扰会非常明显。即使码正交实际接收机中由于同步误差、滤波等因素正交性会被破坏强用户会淹没弱用户。我们可以在仿真中让用户2的发射功率比用户1低10dB再观察用户2的恢复图像。用户1基本不受影响但用户2的解调误码率会急剧上升。这解释为什么实际CDMA系统需要功率控制——让每个用户到达接收端的功率基本相等才能保证多用户公平性。给用户k设置不同功率的方法tx_sig_user{k} tx_sig_user{k} * sqrt(power_scale(k));其中power_scale是线性功率比。在叠加时接收信号就是各用户功率不同后的和。这是个很好的扩展实验能说明多用户系统的容量和公平性问题。5. 仿真中容易踩的五个坑与排查方法5.1 图像恢复出来是斜条纹或完全错位这通常是比特映射顺序问题。检查de2bi和bi2de是否都使用了left-msbQPSK映射表是否和解调一致。如果图像像打了马赛克但没有整体偏移可能只是因为噪底较高如果整幅图像完全是雪花的规则排列那多半是串并转换时比特分组错位了。排查方法先在无噪声条件下测试图像能否完美恢复。这是最基本的调试方式。5.2 星座图旋转了45度或幅度不对QPSK星座点如果出现45度旋转可能是映射表和解调映射不一致如果幅度整体偏大或偏小是归一化因子的问题。一个非常容易犯的错发射端调制用了11j的QPSK星座但没有除以sqrt(2)于是符号能量是2而不是1。加噪声时按能量1算导致实际信噪比偏高3dB。如果曲线比理论左移3dB就是这个原因。5.3 解扩后噪声为什么还是那么大解扩本身会带来处理增益。原理上扩频信号的噪声经过解扩累积平均后噪声幅度只增加sqrt(SF)倍而信号幅度增加SF倍所以信噪比提升SF倍10log10(SF) dB。如果解扩后信噪比没有提升首先检查你是否有对接收信号做了归一化导致信号和噪声一起被缩小。解扩前后的SNR提升是扩频通信的核心价值之一要确保在代码中能体现出来。5.4 多用户叠加后总功率过大出现数值溢出如果用户全部用相同功率且符号全长叠加峰值会比较高。在Matlab里很少溢出但如果你在后面加了A/D量化或限幅就要小心。可以在每个用户发射前把功率归一化到1叠加后的总功率最大达到K。接收端解扩时对应目标用户的相关输出幅度约为SF * sqrt(power_k)所以数值上不会太大。不过如果你用了16个用户且没有缩放峰值可能达到16倍误码性能不会受影响但画星座图时坐标范围要调大。5.5 为什么BER曲线到高信噪比时出现平台不下降多用户系统中BER曲线出现平台通常是多址干扰造成的。如果用户间码不完全正交或者用户功率不平衡那么即使噪声降到0干扰依然存在。需要检查码片同步是否理想、沃尔什码选取是否合理。如果用户数等于扩频增益且全部使用Hadamard矩阵所有行那么理论正交但仿真中因为数据符号的随机性在有限长度下可能存在泄漏。另一种可能是接收端没有对每个用户的时延进行补偿导致解扩时码片错位。6. 扩展方向给你的仿真系统再加点东西如果你完成了基础的多用户图像传输仿真可以尝试几个非常实用的扩展。第一个是加入卷积编码和维特比译码观察软判决相对硬判决的增益第二个是加入信道均衡或rake接收机思想看看多径信道下扩频的抗多径能力第三个是加入功率控制算法动态调整各用户发射功率让接收端各用户SNR趋于一致观察系统性能改善。我个人的建议是先做第一和第三个扩展。编码可以让图像传输的误码率大幅降低功率控制则直接体现工程上CDMA系统的核心机制。两者都做完你的仿真系统已经不输很多教材里的课程设计。另外一点操作层面的经验整个仿真最好封装成函数参数集中放在一个脚本里。比如把用户数、SF、Eb/N0范围、图像尺寸作为变量。这样跑性能曲线时循环调用函数即可不需要反复复制代码。最终你可以生成三张图误码率曲线、恢复图像对比、星座图。这三张图足以支撑一篇完整的实验报告或技术博客。做通信仿真最容易陷进去的误区是堆参数忘记自己到底在验证什么。这个仿真的核心逻辑线很简单QPSK负责把比特变成复数符号扩频负责让多个用户可以共享频谱Matlab负责把这条链路用可控的参数呈现出来。把这条逻辑线抓住细节报错逐个击破图像最终清晰地恢复出来那一刻你会感觉自己对整个无线通信系统的理解又深了一层。