ARTICLE DETAIL

资讯详情

深耕网站视觉设计与运营推广的一线实战洞察。

NS-3 TDMA仿真工程实战:从静态时隙分配到动态调度

NS-3 TDMA仿真工程实战:从静态时隙分配到动态调度 简介这份资源是面向通信工程、电子信息类专业毕业设计的学习资料包围绕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.ccC 源码仿真入口定义节点数、时隙数、包大小、发送间隔specs.cC 源码存放 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被注册为programmodel和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 --numNodes8 --numSlots8逻辑说明./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: Tx100, Rx98, PDR0.98, AvgDelay0.0082s Node 1: Tx100, Rx97, PDR0.97, AvgDelay0.0085s ... Total throughput: 1.2 Mbps如果 PDR 低于 0.9常见原因是guardInterval设成了 0或者所有节点的nodeId % numSlots算出来是同一个值。后者通常是因为节点 ID 从 0 开始连续分配而numSlots设成了 1。把numSlots改成实际节点数问题就消失了。3.3 修改参数做对比实验毕设里通常需要一组对比数据。你可以固定numNodes8把numSlots从 4 改到 16观察吞吐量和时延的变化。改参数不用重新编译直接通过命令行传# 时隙数少于节点数观察排队时延 ./waf --run tdma-example --numNodes8 --numSlots4 # 时隙数等于节点数理想情况 ./waf --run tdma-example --numNodes8 --numSlots8 # 时隙数多于节点数观察资源浪费 ./waf --run tdma-example --numNodes8 --numSlots16每次运行后把输出重定向到文件方便后面画图。我一般会写一个简单的 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 仿真跑起来但收包数为零现象终端打印Tx100, Rx0所有节点都在发但没人收到。原因最常见的是numSlots设成了 1所有节点被分配到同一个时隙互相干扰导致全部丢包。另一个可能是guardInterval为 0时隙边界重叠。解决把numSlots改成大于等于节点数guardInterval至少设为slotDuration的 10%。4.3 时隙同步漂移导致后半个帧丢包现象仿真刚开始正常跑几秒后 PDR 逐渐下降。原因currentSlot的更新依赖时钟如果时钟精度不够或者帧边界没有对齐时隙会慢慢漂移。解决在model/里找到帧定时器确认它用的是Simulator::Schedule而不是Simulator::ScheduleNow。另外检查TDMA_FRAME_LENGTH的计算是否包含了保护间隔漏掉保护间隔会导致累积误差。4.4 节点数超过时隙数时吞吐量断崖式下跌现象numNodes16, numSlots8时吞吐量只有numNodes8时的一半。原因这不是 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::vectoruint32_t queueLengths) { std::vectoruint32_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 和时延曲线。没有基线数据你根本分不清性能变化是调度算法带来的还是参数改错了。希望帮到你。本文还有配套的精品资源点击获取
返回列表