☰
CDMA扩频序列实战:Walsh码生成、m序列与Gold序列构造及避坑指南
2026/10/14 16:07:43 网站建设 项目流程

简介:这份文档面向通信工程、无线通信课程学习者及CDMA系统入门研究者,系统梳理扩频序列中m序列与Walsh码的核心原理与生成方法。内容涵盖m序列的线性移位寄存器结构、反馈系数与周期特性,Walsh码基于Hadamard矩阵的正交构造,以及Gold序列的模2加生成思路,并延伸至CDMA前向链路中64阶Walsh序列的信道分配与多址干扰问题。资源包内含1个doc文档,约355KB,以文字讲解配合Matlab代码片段呈现,便于对照理解Walsh矩阵的Paley序、Walsh序与逆Walsh序转换过程。已有636人学习,适合需要夯实扩频通信基础、理解正交码设计与多用户检测背景的读者参考,也可作为课程作业与实验的辅助材料。

1. 扩频序列选型:m序列和Walsh码到底该怎么配合用

做CDMA物理层仿真时,很多人第一次搭链路都会卡在同一个地方:扩频码到底选哪个。m序列周期长、自相关好,适合做扰码和PN序列;Walsh码正交性强,适合做信道化码。两者不是二选一的关系,而是分工配合。前向链路里,64阶Walsh序列分配给W0到W63共64个物理信道,码片速率1.2288Mc/s,每个用户占用一个正交码道;但Walsh码本身不具备抗多径能力,异步到达的多径信号会破坏正交性,这时候就需要m序列做加扰,把干扰白化。Gold序列则是m序列优选对模2加的结果,互相关特性比单纯m序列更可控,适合做扰码。这套组合逻辑是CDMA系统设计的底层骨架,理解了它,后面调代码、看星座图、分析误码率才不会一头雾水。下面这份资源把Walsh矩阵生成、m序列发生器、Gold序列构造都串起来了,适合正在做扩频通信仿真或物理层链路搭建的工程师。

2. Walsh码生成:从Hadamard矩阵到Paley序、Walsh序和逆Walsh序

2.1 为什么不能直接用Matlab的hadamard函数

Matlab自带的hadamard(N)返回的是自然序Hadamard矩阵,行与行之间确实正交,但行号的二进制顺序和Walsh函数定义不对应。直接拿来做扩频,信道分配会乱——W0到W63的排列顺序和标准里定义的不一致,解扩时对不上号。所以需要做三步重排:先转Paley序,再转Walsh序,最后转逆Walsh序。Paley序是按行号二进制位反转排列,Walsh序是按交变次数排列,逆Walsh序则是把Walsh序再按位反转回来。这三步做完,得到的矩阵才和CDMA标准里的Walsh码表一致。

2.2 生成代码逐段拆解

function X = walshf1(N) N = ceil(log2(N)); H = hadamard(2^N); % 生成自然序Hadamard矩阵 Hp = zeros(2^N); Ep = zeros(2^N); Wg = zeros(2^N); Eg = zeros(2^N); E = eye(2^N); % 第一步:自然序转Paley序 for n = 1:2^N p = 0; for i = 1:N p = bitset(p, i, bitget(n-1, N-i+1)); end Hp(p+1, :) = H(n, :); Ep(p+1, :) = E(n, :); end % 第二步:Paley序转Walsh序 for n = 1:2^N g = 0; g = bitset(g, N, bitget(n-1, N)); for i = 2:N if (bitget(n-1, N-i+1) == 0) g = bitset(g, N-i+1, bitget(g, N-i+2)); else g = bitset(g, N-i+1, 1 - bitget(g, N-i+2)); end end Wg(g+1, :) = Hp(n, :); Eg(g+1, :) = Ep(n, :); end % 第三步:Walsh序转逆Walsh序 for n = 1:2^N m = 0; for i = 1:N m = bitset(m, i, bitget(n-1, N-i+1)); end Hm(m+1, :) = Wg(n, :); Em(m+1, :) = Eg(n, :); end X = Wg; % 返回Walsh序矩阵 end

这段代码的核心操作是位运算重排。bitset和bitget配合完成二进制位的读写,p、g、m分别是三种排序下的行索引。第一步Paley序的映射规则是:把原行号减一后的二进制表示按位反转,得到新行号。第二步Walsh序的映射更绕一些,它模拟的是Walsh函数生成树:从最高位开始,每一位根据前一位的值决定是保持还是取反。第三步逆Walsh序就是把Walsh序的行号再做一次位反转。

参数说明:输入N是期望的序列阶数,函数内部会做ceil(log2(N))向上取整到2的幂次。比如你传64,实际生成64阶;传60,也会生成64阶。输出X是Walsh序矩阵,每行是一个Walsh序列,元素为+1或-1。Hp、Wg、Hm分别是Paley序、Walsh序、逆Walsh序矩阵,Ep、Eg、Em是对应的变换矩阵,用于验证重排是否正确。

2.3 验证正交性和排序正确性

生成完矩阵后,别急着往下做扩频。先跑两行验证:

W = walshf1(64); % 验证正交性:任意两行内积应为0 orth_check = W(1,:) * W(2,:)'; disp(['W1和W2内积:', num2str(orth_check)]); % 验证自相关:每行与自身内积应为64 auto_check = W(1,:) * W(1,:)'; disp(['W1自相关:', num2str(auto_check)]); % 验证W0全为+1(标准里W0是全1序列) disp(['W0全1检查:', num2str(all(W(1,:) == 1))]);

如果orth_check输出0,auto_check输出64,W0全1检查输出1,说明矩阵生成正确。常见翻车点是N传入非2的幂次时,ceil(log2(N))会向上取整,但后续循环里用的还是2^N,这个N已经被覆盖了,所以不会出错。但如果你在函数外部还留着原始的N变量做别的计算,就会对不上。

提示:Matlab的hadamard函数在N不是2的幂次时会报错,所以函数开头先做ceil(log2(N))是必要的。但这也意味着你传60和传64得到的结果一样,都是64阶。

3. m序列发生器:反馈系数、起始状态与周期验证

3.1 n级线性移位寄存器的结构

m序列由n级线性移位寄存器产生,周期为2^n - 1。结构上就是n个寄存器串联,每个时钟周期整体右移一位,最右边移出的位是输出,最左边移入的位是反馈位。反馈位由若干寄存器抽头模2加得到,抽头位置由反馈系数决定。反馈系数是一个n位二进制数,对应多项式。比如n=3,反馈系数为(1,0,1)对应多项式x^3 + x + 1,这是一个本原多项式,能产生周期为7的m序列。

起始状态决定序列的起点,但不能全零——全零状态会让寄存器永远输出0。不同的反馈系数产生不同的m序列,但只有本原多项式才能产生最长周期序列。n级寄存器有2^n - 1个非零状态,m序列遍历所有非零状态后回到起点。

3.2 用Matlab实现m序列发生器

function [seq, states] = m_sequence(n, feedback, init_state) % n: 寄存器级数 % feedback: 反馈系数向量,长度n,对应抽头位置 % init_state: 初始状态向量,长度n,不能全零 N = 2^n - 1; % 序列周期 seq = zeros(1, N); states = zeros(N, n); reg = init_state(:)'; % 行向量 for k = 1:N seq(k) = reg(end); % 输出最右位 states(k, :) = reg; fb = mod(sum(reg .* feedback), 2); % 模2加 reg = [fb, reg(1:end-1)]; % 右移并插入反馈位 end end

调用示例:

n = 4; feedback = [1 0 0 1]; % 对应x^4 + x + 1 init_state = [1 0 0 0]; [seq, states] = m_sequence(n, feedback, init_state); disp(['序列周期:', num2str(length(seq))]); disp(['前16位:', num2str(seq(1:16))]);

参数说明:feedback向量长度必须等于n,每个元素为0或1,对应寄存器抽头。init_state长度也为n,不能全零。输出seq是m序列,states是每个时钟周期的寄存器状态,用于调试。

3.3 自相关和互相关验证

m序列的自相关函数是二值的:延迟为0时等于周期N,其他延迟时等于-1。互相关特性取决于优选对的选择。验证代码:

% 自相关 R = zeros(1, 2*N-1); for tau = -(N-1):(N-1) if tau >= 0 R(tau+N) = sum(seq(1:N-tau) .* seq(1+tau:N)); else R(tau+N) = sum(seq(1-tau:N) .* seq(1:N+tau)); end end disp(['零延迟自相关:', num2str(R(N))]); disp(['非零延迟自相关最大值:', num2str(max(abs(R([1:N-1, N+1:end]))))]);

如果零延迟输出15(n=4时周期为15),非零延迟最大值为1,说明自相关特性正确。常见坑是反馈系数选错,导致周期不是2^n - 1。比如n=4时,如果反馈系数对应的是非本原多项式,周期可能只有5或7。排查方法是打印states矩阵,看是否遍历了所有非零状态。

注意:m序列的互相关特性不如Gold序列可控。如果要做多用户仿真,直接拿两个m序列做互相关,峰值可能达到2^(n/2)+1,干扰较大。Gold序列就是为解决这个问题提出的。

4. Gold序列构造:m序列优选对模2加与互相关特性

4.1 优选对的选取条件

Gold序列由一对周期和速率相同的m序列优选对模2加得到。优选对的定义是:两个m序列的互相关函数绝对值上界为2^((n+1)/2)(n为奇数)或2^((n+2)/2)(n为偶数)。不是任意两个m序列都能构成优选对,需要查表或搜索。常见做法是固定一个m序列,遍历所有可能的反馈系数生成另一个m序列,计算互相关峰值,找到满足条件的配对。

4.2 构造代码与互相关对比

function gold_seq = gold_sequence(n, fb1, fb2, init1, init2, shift) % 生成两个m序列 seq1 = m_sequence(n, fb1, init1); seq2 = m_sequence(n, fb2, init2); % 循环移位后模2加 seq2_shifted = circshift(seq2, [0, shift]); gold_seq = mod(seq1 + seq2_shifted, 2); end

调用并对比互相关:

n = 5; fb1 = [1 0 0 1 0]; fb2 = [1 1 1 0 1]; init1 = [1 0 0 0 0]; init2 = [1 0 0 0 0]; seq1 = m_sequence(n, fb1, init1); seq2 = m_sequence(n, fb2, init2); gold = gold_sequence(n, fb1, fb2, init1, init2, 3); % 计算m序列互相关峰值 N = 2^n - 1; R_m = zeros(1, 2*N-1); for tau = -(N-1):(N-1) if tau >= 0 R_m(tau+N) = sum(seq1(1:N-tau) .* seq2(1+tau:N)); else R_m(tau+N) = sum(seq1(1-tau:N) .* seq2(1:N+tau)); end end disp(['m序列互相关峰值:', num2str(max(abs(R_m)))]); % Gold序列自相关旁瓣 R_gold = zeros(1, 2*N-1); for tau = -(N-1):(N-1) if tau >= 0 R_gold(tau+N) = sum(gold(1:N-tau) .* gold(1+tau:N)); else R_gold(tau+N) = sum(gold(1-tau:N) .* gold(1:N+tau)); end end disp(['Gold序列自相关旁瓣最大值:', num2str(max(abs(R_gold([1:N-1, N+1:end]))))]);

n=5时,m序列互相关峰值可能到9或11,而Gold序列的互相关上界是2^3+1=9,自相关旁瓣也是9。虽然绝对值看起来不小,但相对于周期31来说,归一化后是9/31≈0.29,比随机序列的1/sqrt(31)≈0.18略高,但比直接使用两个m序列的互相关峰值要稳定得多。

4.3 在CDMA前向链路中的角色分工

前向链路里,Walsh码做信道化,负责区分同一小区内的不同用户;m序列或Gold序列做扰码,负责区分不同小区和基站。Walsh码的正交性在同步条件下成立,但多径会破坏正交性,导致多址干扰。扰码的作用是把干扰白化,让接收端解扩后干扰功率谱密度均匀,不集中在某个码道上。这就是为什么标准里既有Walsh码又有PN序列——两者缺一不可。

提示:Gold序列的移位值shift不同,得到的序列也不同。实际系统中,不同基站分配不同的移位值,接收端通过搜索PN序列的相位来识别基站。

5. 避坑与排查:扩频序列仿真中容易翻车的五个点

5.1 现象:Walsh矩阵行序对不上标准码表

原因:直接用了hadamard(64),没有做Paley序和Walsh序重排。Matlab的自然序Hadamard矩阵行号二进制顺序和Walsh函数定义不一致,W0可能不是全1序列。

解决:用walshf1函数做三步重排,生成后验证W(1,:)是否全为+1。如果不是,检查Paley序映射里的位反转逻辑,确认bitset的位索引从1开始还是从0开始。

5.2 现象:m序列周期不是2^n - 1

原因:反馈系数对应的多项式不是本原多项式。比如n=4时,反馈系数[1 0 0 1]对应x^4 + x + 1,是本原多项式,周期15;但[1 1 0 0]对应x^4 + x^3 + 1,不是本原多项式,周期可能只有5。

解决:查本原多项式表,或者写脚本遍历所有反馈系数,统计每个系数下的序列周期,筛选出周期等于2^n - 1的。n≤10时遍历很快,n再大就需要查表。

5.3 现象:Gold序列互相关峰值比预期高

原因:选取的两个m序列不是优选对。优选对需要满足互相关上界条件,不是随便两个m序列模2加就能得到Gold序列。

解决:固定一个m序列,遍历另一个m序列的所有反馈系数和初始相位,计算互相关峰值,找到满足上界条件的配对。或者直接查Gold序列优选对表。

5.4 现象:扩频后信号星座图发散

原因:扩频码没有做归一化。Walsh码元素是+1/-1,扩频后信号功率放大N倍(N为扩频因子),如果不做功率归一化,星座点会超出解调范围。

解决:扩频后除以sqrt(N),或者用归一化后的扩频码(元素为±1/sqrt(N))。在Matlab里,W = W / sqrt(N)即可。

5.5 现象:多径信道下解扩后误码率居高不下

原因:Walsh码的正交性在异步多径下被破坏,多址干扰没有消除。这是理论上的固有问题,不是代码bug。

解决:加入m序列或Gold序列做扰码,把干扰白化;或者在接收端加多用户检测算法(如解相关检测、MMSE检测)。如果只是仿真验证,可以先把多径延迟设为0,确认同步条件下误码率正常,再逐步加多径。

注意:Walsh码完全正交是理想条件,实际系统中多径和邻区干扰必然存在。仿真时不要期望误码率降到零,合理范围是10^-3到10^-6,取决于信噪比和多径模型。

6. 进阶技巧:用矩阵变换验证Walsh序与逆Walsh序的互逆性

生成Walsh矩阵后,一个容易被忽略的验证点是:Walsh序矩阵和逆Walsh序矩阵之间是否存在互逆变换。walshf1函数里生成了Eg和Em两个变换矩阵,它们分别记录了从Hadamard到Walsh、从Walsh到逆Walsh的行置换关系。验证方法很简单:

N = 64; W = walshf1(N); % 重新生成逆Walsh序矩阵 [~, ~, ~, ~, ~, ~, Hm, Em] = walshf1(N); % 验证Walsh序到逆Walsh序的变换矩阵 % Em * Wg' 应该等于 Hm' check1 = Em * W' - Hm'; disp(['Walsh到逆Walsh变换误差:', num2str(max(abs(check1(:))))]); % 验证逆Walsh序矩阵的行正交性 orth_check = Hm * Hm'; diag_elements = diag(orth_check); off_diag = orth_check - diag(diag_elements); disp(['逆Walsh序对角元素(应为N):', num2str(unique(diag_elements))]); disp(['逆Walsh序非对角最大元素(应为0):', num2str(max(abs(off_diag(:))))]);

如果check1的最大绝对值在10^-10量级,说明变换矩阵正确。diag_elements应该全是64,off_diag最大值应该是0。这个验证看起来多余,但在实际项目中,如果Walsh序和逆Walsh序的映射关系搞反了,解扩时用错矩阵,误码率会直接飙到0.5,而且很难从星座图上看出问题——因为星座图看起来完全正常,只是解出来的比特全错。我一般会在生成矩阵后强制跑一遍这个验证,确认三个矩阵之间的变换关系闭环。

另一个实用技巧是:把Walsh矩阵按行存储为二进制文件,用fwrite写出去,后面用C或Python做链路仿真时直接读,避免每次都在Matlab里重新生成。文件格式很简单:每行64个int8,共64行,文件大小4096字节。读取时用fread按行读入,再reshape成64x64矩阵。这样跨平台协作时不会因为Matlab版本差异导致hadamard函数行为不一致。

从那以后我每次做扩频仿真,都会先把Walsh矩阵、m序列、Gold序列的生成和验证脚本单独跑一遍,确认正交性和周期无误,再接入完整的链路仿真。这个习惯帮我省掉了至少三次因为码序错误导致的通宵排查。希望帮到你。

本文还有配套的精品资源,点击获取

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询