简介:这份资源是面向通信工程、电子信息类专业毕业设计的学习资料包,围绕TDMA时分多址技术展开,适合正在做无线通信方向课题、需要理解多用户共享频带机制的学生参考。内容涉及帧结构、时隙分配、同步机制、信道编码与交织、功率控制、切换漫游以及频谱效率与系统容量等核心知识点,并延伸至GSM等2G系统的实际应用场景。压缩包共108个文件,约202KB,以cc与h源码文件为主,辅以xml配置、prefs偏好设置、log日志及少量脚本与文档,整体呈现为一个可编译运行的TDMA仿真工程结构,便于读者对照代码理解协议实现细节。目前已有39人学习下载。通过研读源码与配套说明,读者可掌握TDMA系统建模思路、时隙调度算法与冲突处理方案,为毕业设计中的仿真验证与论文撰写提供可复用的工程参考。
1. 从一份“毕业设计的tdma.zip”说起:它到底能跑出什么
如果你正在做无线通信方向的毕业设计,或者需要快速搭一个能演示时隙分配逻辑的仿真环境,这份毕业设计的tdma.zip大概率能省掉你从零写 MAC 层调度的时间。它不是一个空壳文档包,而是一个基于 NS-3 的 TDMA 示例工程,核心文件包括tdma-example.cc、specs.c以及配套的helper、model、test目录。换句话说,它把“时间划分为时隙、每个用户在指定时隙独占信道”这套理论,落成了一个可以编译、可以跑、可以改参数的 C++ 仿真脚本。
适合谁用?第一类是做毕设但不想在 NS-3 环境配置上卡两周的本科生;第二类是需要验证 TDMA 帧结构、时隙分配策略是否合理的研究生;第三类是想把 TDMA 和 LoRa、STM32WLE5 这类低功耗广域网方案做对比的工程师。它解决的不是“TDMA 是什么”这种概念问题,而是“给我一个能跑通的最小闭环,让我看到时隙怎么分、包怎么发、冲突怎么避”。
2. 拆开压缩包:NS-3 工程结构与 TDMA 帧结构怎么对应
2.1 文件清单与模块职责
拿到压缩包后,先别急着编译。把目录展开,你会看到下面这些内容。我按实际工程习惯标注了每个部分的作用,方便你判断哪些要改、哪些可以直接复用。
| 文件/目录 | 类型 | 实际作用 |
|---|---|---|
tdma-example.cc | C++ 源码 | 仿真入口,定义节点数、时隙数、包大小、发送间隔 |
specs.c | C 源码 | 存放 TDMA 帧结构相关的常量定义,比如时隙长度、保护间隔 |
helper/ | 目录 | 封装安装逻辑,把 TDMA 设备挂到节点上 |
model/ | 目录 | TDMA MAC 层核心实现,含时隙调度和状态机 |
test/ | 目录 | 回归测试用例,验证时隙分配是否越界 |
examples/ | 目录 | 额外示例,通常包含多节点不同业务流的配置 |
simple-wireless-tdma/ | 目录 | 简化版无线 TDMA 场景,适合快速验证 |
wscript | 构建脚本 | NS-3 的 waf 构建入口,决定哪些文件参与编译 |
这里有个血泪经验:很多人拿到 NS-3 工程后直接./waf,结果报一堆“undefined reference”。原因往往不是代码错,而是wscript里没把model/和helper/加进source列表。先打开wscript,确认tdma-example.cc被注册为program,model和helper被注册为module,再动手编译。
2.2 帧结构在代码里的映射关系
TDMA 的帧结构理论讲起来简单:一个帧分成 N 个时隙,每个用户占一个。但落到代码里,你得找到三个关键变量:slotDuration、guardInterval、numSlots。在specs.c里通常能看到类似下面的定义:
// specs.c 中常见的 TDMA 帧参数定义 #define TDMA_SLOT_DURATION 0.001 // 单个时隙长度,单位秒 #define TDMA_GUARD_INTERVAL 0.0001 // 保护间隔,防止时隙间串扰 #define TDMA_NUM_SLOTS 8 // 每帧时隙数,对应最大用户数 #define TDMA_FRAME_LENGTH (TDMA_NUM_SLOTS * (TDMA_SLOT_DURATION + TDMA_GUARD_INTERVAL))逻辑说明:TDMA_FRAME_LENGTH是一个帧的总时长,等于时隙数乘以“时隙长度 + 保护间隔”。保护间隔不能省,否则相邻时隙的用户会因为传播时延和时钟抖动产生符号间干扰。参数怎么改?如果你要支持 16 个用户,把TDMA_NUM_SLOTS改成 16,同时检查tdma-example.cc里创建的节点数是否匹配。节点数大于时隙数时,多出来的节点会排队等下一帧,仿真里表现为吞吐量骤降,这不是 bug,是 TDMA 的硬约束。
2.3 时隙分配逻辑的代码入口
在model/目录下,找到 TDMA MAC 层的Enqueue或Send函数。常见做法是维护一个currentSlot变量,每帧开始时归零,每过一个时隙自增。节点发送前先判断nodeId % TDMA_NUM_SLOTS == currentSlot,相等才允许发送,否则把包缓存到队列。
// model/tdma-mac.cc 中时隙判断的典型写法 bool TdmaMac::CanSendNow(uint32_t nodeId, uint32_t currentSlot) { // 静态分配:节点 ID 对时隙数取模,决定它占哪个时隙 uint32_t assignedSlot = nodeId % TDMA_NUM_SLOTS; return (assignedSlot == currentSlot); }这段代码背后的选型理由是:静态时隙分配实现简单,适合毕设演示和固定拓扑。缺点是灵活性差,某个节点没数据时它的时隙就空着,频谱效率低。如果你要做动态分配,得把assignedSlot改成从调度表读取,调度表可以基于队列长度或业务优先级生成。我一般会先跑通静态版本,确认帧同步没问题,再动动态分配的代码,否则调试难度会翻倍。
3. 编译与运行:从 waf 配置到第一次跑通仿真
3.1 环境准备与 waf 编译
这份工程依赖 NS-3 环境。如果你还没装 NS-3,常见做法是下载 ns-3.3x 版本,把tdma相关目录复制到src/下,或者直接用./waf --run指定路径。假设你已经有一个可用的 NS-3 工作目录,把压缩包解压后,按下面步骤操作:
# 进入 NS-3 根目录 cd ns-3.38 # 把 tdma 工程复制到 src 下(如果压缩包结构是独立的) cp -r /path/to/毕业设计的tdma src/tdma # 重新配置,让 waf 识别新模块 ./waf configure --enable-examples --enable-tests # 编译,只编译 tdma 相关目标可以加 --target ./waf build # 运行示例,指定节点数和时隙数 ./waf --run "tdma-example --numNodes=8 --numSlots=8"逻辑说明:./waf configure会扫描src/下所有模块的wscript,如果tdma目录里没有正确的wscript,编译阶段会直接忽略它。--enable-examples确保examples/下的文件被编译成可执行程序。--run后面的参数会传给tdma-example.cc里的CommandLine解析器,具体支持哪些参数,打开源码搜CommandLine cmd就能看到。
参数说明:numNodes控制节点数量,numSlots控制每帧时隙数。两者相等时时隙利用率最高,每个节点每帧都能发一次。numNodes大于numSlots时,部分节点需要等下一帧,仿真结果里的平均时延会明显上升。numNodes小于numSlots时时隙浪费,但冲突概率为零。
3.2 仿真输出怎么看
跑完之后,终端会打印每个节点发送和接收的包数量、平均时延、吞吐量。重点关注两个指标:一是PacketDeliveryRatio,正常应该在 0.95 以上;二是AverageDelay,静态 TDMA 下应该稳定在一个帧长度左右。如果丢包率超过 10%,先检查保护间隔是否太小,再检查节点是否被分配到了同一个时隙。
# 典型输出片段 Node 0: Tx=100, Rx=98, PDR=0.98, AvgDelay=0.0082s Node 1: Tx=100, Rx=97, PDR=0.97, AvgDelay=0.0085s ... Total throughput: 1.2 Mbps如果 PDR 低于 0.9,常见原因是guardInterval设成了 0,或者所有节点的nodeId % numSlots算出来是同一个值。后者通常是因为节点 ID 从 0 开始连续分配,而numSlots设成了 1。把numSlots改成实际节点数,问题就消失了。
3.3 修改参数做对比实验
毕设里通常需要一组对比数据。你可以固定numNodes=8,把numSlots从 4 改到 16,观察吞吐量和时延的变化。改参数不用重新编译,直接通过命令行传:
# 时隙数少于节点数,观察排队时延 ./waf --run "tdma-example --numNodes=8 --numSlots=4" # 时隙数等于节点数,理想情况 ./waf --run "tdma-example --numNodes=8 --numSlots=8" # 时隙数多于节点数,观察资源浪费 ./waf --run "tdma-example --numNodes=8 --numSlots=16"每次运行后把输出重定向到文件,方便后面画图。我一般会写一个简单的 shell 脚本循环跑,省得手动改参数。注意,numSlots改大之后,帧长度变长,平均时延也会跟着涨,这是 TDMA 的固有 trade-off,不是代码问题。
4. 避坑与排查:TDMA 仿真里最容易翻车的五个地方
4.1 编译报错 “undefined reference to TdmaMac”
现象:./waf build时链接阶段报错,提示找不到TdmaMac类的实现。原因:wscript里没有把model/tdma-mac.cc加入source列表,或者helper/下的安装文件没注册。解决:打开src/tdma/wscript,确认module.source包含所有.cc文件,module.header包含所有.h文件。改完执行./waf configure再./waf build。
4.2 仿真跑起来但收包数为零
现象:终端打印Tx=100, Rx=0,所有节点都在发但没人收到。原因:最常见的是numSlots设成了 1,所有节点被分配到同一个时隙,互相干扰导致全部丢包。另一个可能是guardInterval为 0,时隙边界重叠。解决:把numSlots改成大于等于节点数,guardInterval至少设为slotDuration的 10%。
4.3 时隙同步漂移导致后半个帧丢包
现象:仿真刚开始正常,跑几秒后 PDR 逐渐下降。原因:currentSlot的更新依赖时钟,如果时钟精度不够或者帧边界没有对齐,时隙会慢慢漂移。解决:在model/里找到帧定时器,确认它用的是Simulator::Schedule而不是Simulator::ScheduleNow。另外,检查TDMA_FRAME_LENGTH的计算是否包含了保护间隔,漏掉保护间隔会导致累积误差。
4.4 节点数超过时隙数时吞吐量断崖式下跌
现象:numNodes=16, numSlots=8时,吞吐量只有numNodes=8时的一半。原因:这不是 bug,是 TDMA 的硬限制。16 个节点抢 8 个时隙,每个节点两帧才能发一次,有效吞吐量自然减半。解决:如果毕设要求支持更多节点,要么增加时隙数,要么引入动态时隙分配,让空闲节点把时隙让出来。静态分配下,这个现象无法消除。
4.5 修改 specs.c 后编译不生效
现象:改了specs.c里的TDMA_NUM_SLOTS,重新编译运行,结果和没改一样。原因:specs.c可能被编译成了静态库,而tdma-example.cc链接的是旧库。解决:执行./waf clean再./waf configure && ./waf build,强制全量重编。或者把常量定义移到头文件里,用#include引入,避免库缓存问题。
5. 进阶用法:把静态 TDMA 改成动态时隙分配并验证
静态分配跑通之后,下一步通常是让时隙分配“活”起来。动态 TDMA 的核心思路是:每个帧开始时,基站收集各节点的队列长度,按需分配时隙。在 NS-3 里,你可以不改 MAC 层核心代码,而是加一个TdmaScheduler类,在每帧边界触发调度计算。
// 动态调度器伪代码:按队列长度排序,优先分配时隙 void TdmaScheduler::AssignSlots(std::vector<uint32_t>& queueLengths) { std::vector<uint32_t> sortedNodes(queueLengths.size()); std::iota(sortedNodes.begin(), sortedNodes.end(), 0); // 队列长的节点排前面 std::sort(sortedNodes.begin(), sortedNodes.end(), [&](uint32_t a, uint32_t b) { return queueLengths[a] > queueLengths[b]; }); // 前 numSlots 个节点获得时隙 for (uint32_t i = 0; i < TDMA_NUM_SLOTS && i < sortedNodes.size(); ++i) { slotAssignment[sortedNodes[i]] = i; } }逻辑说明:queueLengths是每个节点当前缓存的包数量,排序后把时隙优先分给队列最长的节点。slotAssignment是一个映射表,MAC 层发送前查这张表而不是用nodeId % numSlots。参数怎么调?TDMA_NUM_SLOTS不变,但调度周期可以设成每帧一次或每 N 帧一次。每帧都调度精度高但开销大,每 N 帧调度一次适合业务变化慢的场景。
验证动态调度是否生效,看两个指标:一是高负载节点的 PDR 是否提升,二是低负载节点是否偶尔拿不到时隙。如果低负载节点连续多帧无包可发,说明调度器过于偏向长队列,可以加一个“最小保证时隙”机制,每个节点至少每 M 帧获得一次发送机会。
从那以后我每次改 TDMA 调度逻辑,都强制先跑一遍静态基线,再跑动态版本,对比 PDR 和时延曲线。没有基线数据,你根本分不清性能变化是调度算法带来的,还是参数改错了。希望帮到你。
本文还有配套的精品资源,点击获取