TPSN算法C语言实现的关键难点是什么?

99ANYc3cd6
预计阅读时长 44 分钟
位置: 首页 C语言 正文

TPSN 算法简介

TPSN (Timing-sync Protocol for Sensor Networks) 是一种经典的层次式时间同步协议,专为无线传感器网络设计,它的核心思想是成对同步,通过构建一个层次化的拓扑结构,将时间同步从根节点逐级传播到网络中的所有节点。

tpsn算法c语言实现
(图片来源网络,侵删)

主要特点

  • 层次式结构: 网络中选举一个根节点(通常是 sink 或汇聚节点),其他节点根据与根节点的跳数分层,根节点在第0层,与根节点直接通信的节点在第1层,以此类推。
  • 成对同步: 同步过程是基于发送方和接收方之间的一系列消息交换来完成的,精确测量消息的传输延迟。
  • 双向消息交换: 为了消除发送方和接收方本身的处理延迟差异,TPSN 采用双向时间同步机制(类似 NTP 协议)。
  • 可扩展性: 由于层次化的结构,TPSN 能够很好地扩展到大规模网络中。

同步原理:双向时间同步

TPSN 的核心是成对节点之间的双向时间同步,假设节点 A(发送方)和节点 B(接收方)需要进行同步,过程如下:

  1. T1: 节点 A 在本地时间 T1 时刻发送一个 SYNC 消息给节点 B,该消息中包含 T1
  2. T2: 节点 B 在本地时间 T2 时刻接收到 SYNC 消息。
  3. T3: 节点 B 立即在本地时间 T3 时刻回复一个 FOLLOW_UP 消息给节点 A,该消息中包含 T2T3
  4. T4: 节点 A 在本地时间 T4 时刻接收到 FOLLOW_UP 消息。

我们有四个时间戳:

  • T1: 发送方发送时间
  • T2: 接收方接收时间
  • T3: 接收方回复时间
  • T4: 发送方回复接收时间

我们可以建立以下方程来计算时钟偏差和延迟:

  • 消息往返时间: RTT = (T4 - T1) - (T3 - T2)
    • (T4 - T1) 是发送方测量的总时间。
    • (T3 - T2) 是接收方用于处理消息的时间。
    • RTT 代表了消息在链路上一个来回的总时间。
  • 单程时间: delay = RTT / 2

    假设上行和下行的链路延迟是对称的,单程延迟就是 RTT 的一半。

    tpsn算法c语言实现
    (图片来源网络,侵删)
  • 时钟偏差: offset = (T2 - T1) - delay
    • (T2 - T1) 是接收方时钟相对于发送方时钟的原始偏移量。
    • 减去单程延迟 delay,就得到了接收方时钟相对于发送方时钟的真实偏差。

同步操作: 为了让接收方 B 的时钟与发送方 A 对齐,B 需要将自己的时间向前或向后调整 offsetoffset 为正,说明 B 的时钟比 A 慢,需要加上 offset;如果为负,说明 B 的时钟比 A 快,需要减去 offset

TPSN 算法流程

TPSN 的完整同步过程分为两个主要阶段:

层次构建

  1. 根节点选举: 网络中有一个预先确定的根节点(基站或汇聚节点),它位于第 0 层。
  2. 广播层级信息: 根节点向全网广播一个 LEVEL_DISCOVERY 消息,声明自己是第 0 层。
  3. 节点分层:
    • 网络中的其他节点监听 LEVEL_DISCOVERY 消息。
    • 一个节点会记录下所有它能听到的 LEVEL_DISCOVERY 消息,并选择其中层级最小的发送方作为其父节点。
    • 该节点的层级 = 其父节点的层级 + 1。
    • 该节点也广播一个 LEVEL_DISCOVERY 消息,声明自己的新层级。
  4. 迭代完成: 这个过程持续进行,直到网络中所有节点都确定了各自的层级,形成一个以根节点为根的树状拓扑结构。

同步阶段

  1. 从根节点开始: 同步从第 0 层的根节点开始。
  2. 逐层向下同步:
    • 第 0 层 (根节点): 不需要同步。
    • 第 1 层节点: 每个第 1 层节点都与根节点执行双向时间同步过程(如上所述),同步完成后,第 1 层节点的时钟就与根节点同步了。
    • 第 2 层节点: 每个第 2 层节点只与其父节点(一个第 1 层节点)执行双向时间同步。
    • ... 以此类推: 第 k 层的节点只与其父节点(第 k-1 层的节点)进行同步。
  3. 完成: 当所有层的节点都完成同步后,整个网络就达到了时间同步。

C 语言实现

下面是一个简化版的 TPSN 算法 C 语言实现,这个模拟程序将创建一个网络拓扑,并执行 TPSN 的两个阶段。

1 代码结构

  • node.h: 定义节点结构体和消息类型。
  • tpsn.c: 主程序,包含网络初始化、层次构建、同步执行等核心逻辑。
  • utils.h/c: 辅助函数,如模拟发送/接收消息、打印网络状态等。

2 代码实现

node.h - 节点和消息定义

#ifndef NODE_H
#define NODE_H
#include <stdbool.h>
#include <stdint.h>
// 模拟的时间戳类型,可以是高精度计时器的值
typedef uint64_t timestamp_t;
// 节点状态
typedef enum {
    LEVEL_NOT_SET,
    LEVEL_SET,
    SYNCHRONIZED
} NodeState;
// 节点结构体
typedef struct {
    int id;             // 节点ID
    int level;          // 节点层级
    int parent_id;      // 父节点ID
    NodeState state;    // 节点状态
    double clock_offset; // 相对于根节点的时钟偏移量(用于模拟)
    timestamp_t local_time; // 本地模拟时间
} Node;
// 消息类型
typedef enum {
    LEVEL_DISCOVERY, // 层级发现消息
    SYNC,            // 同步请求消息
    FOLLOW_UP        // 同步回复消息
} MessageType;
// 消息结构体
typedef struct {
    MessageType type;
    int sender_id;
    int receiver_id; // 仅用于模拟,实际无线通信是广播的
    timestamp_t timestamp1; // T1 (for SYNC) or T2 (for FOLLOW_UP)
    timestamp_t timestamp2; // T3 (for FOLLOW_UP)
} Message;
#endif // NODE_H

utils.h - 辅助函数声明

#ifndef UTILS_H
#define UTILS_H
#include "node.h"
#include <stdio.h>
// 模拟发送消息
void send_message(Node* nodes, int num_nodes, Message msg);
// 模拟接收消息
void receive_message(Node* nodes, int num_nodes, Node* receiver, Message* msg);
// 打印网络当前状态(层级和状态)
void print_network_status(Node* nodes, int num_nodes);
// 模拟时间流逝
void simulate_time_passage(Node* nodes, int num_nodes, uint64_t time_elapsed);
#endif // UTILS_H

utils.c - 辅助函数实现

#include "utils.h"
#include <string.h>
#include <stdlib.h>
#include <time.h>
// 模拟网络延迟,单位:微秒
#define NETWORK_DELAY_US 1000
// 模拟处理延迟,单位:微秒
#define PROCESSING_DELAY_US 100
// 获取当前模拟时间(这里用系统时间模拟,实际中可能是硬件计数器)
timestamp_t get_current_time() {
    struct timespec ts;
    clock_gettime(CLOCK_MONOTONIC, &ts);
    return (timestamp_t)ts.tv_sec * 1000000 + ts.tv_nsec / 1000;
}
void send_message(Node* nodes, int num_nodes, Message msg) {
    // 在真实网络中,这是广播,这里简化为直接调用接收方的函数。
    // msg.receiver_id 在调用时由发送方逻辑确定。
    printf("Node %d sends %s message to Node %d\n", msg.sender_id, 
           msg.type == LEVEL_DISCOVERY ? "LEVEL_DISCOVERY" : 
           msg.type == SYNC ? "SYNC" : "FOLLOW_UP", msg.receiver_id);
    // 模拟网络传输延迟
    simulate_time_passage(nodes, num_nodes, NETWORK_DELAY_US);
}
void receive_message(Node* nodes, int num_nodes, Node* receiver, Message* msg) {
    // 模拟处理延迟
    simulate_time_passage(nodes, num_nodes, PROCESSING_DELAY_US);
    receiver->local_time = get_current_time(); // 更新接收方的本地时间
    printf("Node %d received %s message from Node %d at its local time: %llu\n", 
           receiver->id, msg->type == LEVEL_DISCOVERY ? "LEVEL_DISCOVERY" : 
           msg->type == SYNC ? "SYNC" : "FOLLOW_UP", msg->sender_id, (unsigned long long)receiver->local_time);
}
void print_network_status(Node* nodes, int num_nodes) {
    printf("\n--- Network Status ---\n");
    printf("ID | Level | Parent | State      | Clock Offset\n");
    printf("------------------------------------------------\n");
    for (int i = 0; i < num_nodes; i++) {
        const char* state_str = nodes[i].state == LEVEL_NOT_SET ? "NOT_SET" :
                                nodes[i].state == LEVEL_SET ? "SET" : "SYNC";
        printf("%2d | %5d | %6d | %10s | %12.3f\n", 
               nodes[i].id, nodes[i].level, nodes[i].parent_id, state_str, nodes[i].clock_offset);
    }
    printf("----------------------\n\n");
}
void simulate_time_passage(Node* nodes, int num_nodes, uint64_t time_elapsed) {
    // 在真实系统中,这是物理时间的流逝。
    // 在我们的模拟中,我们为每个节点的“本地时钟”增加一个随机偏移来模拟时钟漂移。
    // 为了简化,这里我们只增加一个固定值,代表时间的流逝。
    // 更复杂的模拟可以为每个节点添加不同的漂移率。
    for (int i = 0; i < num_nodes; i++) {
        nodes[i].local_time += time_elapsed;
    }
    // 让主线程暂停,模拟真实时间流逝
    // usleep(time_elapsed); 
}

tpsn.c - 主程序和 TPSN 算法逻辑

#include "node.h"
#include "utils.h"
#include <stdio.h>
#include <stdbool.h>
#include <string.h>
#define NUM_NODES 6
// --- 阶段一:层次构建 ---
void build_levels(Node* nodes, int num_nodes) {
    printf("--- Starting Level Building Phase ---\n");
    // 根节点是ID为0的节点
    nodes[0].level = 0;
    nodes[0].state = LEVEL_SET;
    nodes[0].parent_id = -1; // 根节点没有父节点
    bool levels_updated;
    do {
        levels_updated = false;
        // 遍历所有已确定层级的节点
        for (int i = 0; i < num_nodes; i++) {
            if (nodes[i].state == LEVEL_SET) {
                // 模拟该节点广播 LEVEL_DISCOVERY 消息
                Message msg;
                msg.type = LEVEL_DISCOVERY;
                msg.sender_id = nodes[i].id;
                msg.timestamp1 = nodes[i].local_time; // 发送时间
                // 遍历所有未确定层级的节点
                for (int j = 0; j < num_nodes; j++) {
                    if (nodes[j].state == LEVEL_NOT_SET) {
                        msg.receiver_id = j;
                        send_message(nodes, num_nodes, msg);
                        // 模拟接收方处理消息
                        Node* receiver = &nodes[j];
                        receive_message(nodes, num_nodes, receiver, &msg);
                        // 接收方逻辑:如果发现层级更小的父节点,则更新
                        if (receiver->parent_id == -1 || nodes[i].level < nodes[receiver->parent_id].level) {
                            receiver->parent_id = nodes[i].id;
                            // 注意:这里不立即设置层级,等待一轮广播结束
                        }
                    }
                }
            }
        }
        // 一轮广播结束后,更新所有找到父节点的节点的层级
        for (int i = 0; i < num_nodes; i++) {
            if (nodes[i].state == LEVEL_NOT_SET && nodes[i].parent_id != -1) {
                nodes[i].level = nodes[nodes[i].parent_id].level + 1;
                nodes[i].state = LEVEL_SET;
                levels_updated = true;
                printf("Node %d set level to %d, parent is Node %d\n", 
                       nodes[i].id, nodes[i].level, nodes[i].parent_id);
            }
        }
    } while (levels_updated); // 直到没有节点能更新层级为止
    printf("--- Level Building Phase Finished ---\n");
    print_network_status(nodes, num_nodes);
}
// --- 阶段二:同步 ---
void perform_synchronization(Node* nodes, int num_nodes) {
    printf("\n--- Starting Synchronization Phase ---\n");
    // 按层级从低到高排序,确保先同步低层级节点
    // 这里简化处理,假设节点ID与层级顺序相关,实际中应排序
    // for (int current_level = 1; current_level < MAX_LEVEL; current_level++) {
    for (int i = 0; i < num_nodes; i++) {
        if (nodes[i].level == 0) continue; // 跳过根节点
        Node* child = &nodes[i];
        Node* parent = &nodes[child->parent_id];
        printf("\nSynchronizing Node %d (level %d) with its parent Node %d (level %d)...\n",
               child->id, child->level, parent->id, parent->level);
        // --- 双向同步过程 ---
        // 1. T1: Parent sends SYNC to child
        Message sync_msg;
        sync_msg.type = SYNC;
        sync_msg.sender_id = parent->id;
        sync_msg.receiver_id = child->id;
        sync_msg.timestamp1 = parent->local_time; // T1
        send_message(nodes, num_nodes, sync_msg);
        receive_message(nodes, num_nodes, child, &sync_msg);
        timestamp_t T1 = sync_msg.timestamp1;
        timestamp_t T2 = child->local_time; // T2 is child's local time upon reception
        // 2. T3: Child sends FOLLOW_UP to parent
        Message follow_up_msg;
        follow_up_msg.type = FOLLOW_UP;
        follow_up_msg.sender_id = child->id;
        follow_up_msg.receiver_id = parent->id;
        follow_up_msg.timestamp1 = T2; // T2
        follow_up_msg.timestamp2 = child->local_time; // T3
        send_message(nodes, num_nodes, follow_up_msg);
        receive_message(nodes, num_nodes, parent, &follow_up_msg);
        timestamp_t T3 = follow_up_msg.timestamp2;
        timestamp_t T4 = parent->local_time; // T4 is parent's local time upon reception
        // 3. 计算偏差和延迟
        int64_t rtt = (T4 - T1) - (T3 - T2);
        double delay = (double)rtt / 2.0;
        double offset = (T2 - T1) - delay;
        printf("  Timestamps: T1=%llu, T2=%llu, T3=%llu, T4=%llu\n", 
               (unsigned long long)T1, (unsigned long long)T2, (unsigned long long)T3, (unsigned long long)T4);
        printf("  Calculated RTT: %lld, Delay: %.3f, Offset: %.3f\n", 
               (long long)rtt, delay, offset);
        // 4. 子节点调整时钟
        child->clock_offset += offset; // 累积偏移
        child->state = SYNCHRONIZED;
        printf("  Node %d adjusted its clock by %.3f. New offset: %.3f\n", 
               child->id, offset, child->clock_offset);
    }
    printf("--- Synchronization Phase Finished ---\n");
    print_network_status(nodes, num_nodes);
}
int main() {
    Node nodes[NUM_NODES];
    // 初始化所有节点
    for (int i = 0; i < NUM_NODES; i++) {
        nodes[i].id = i;
        nodes[i].level = -1;
        nodes[i].parent_id = -1;
        nodes[i].state = LEVEL_NOT_SET;
        nodes[i].clock_offset = 0.0;
        nodes[i].local_time = get_current_time();
        // 模拟初始时钟不同步
        nodes[i].local_time += i * 500; // 每个节点初始时间有微小差异
    }
    printf("Initial Network Status:\n");
    print_network_status(nodes, NUM_NODES);
    // 执行TPSN算法
    build_levels(nodes, NUM_NODES);
    perform_synchronization(nodes, NUM_NODES);
    return 0;
}

3 如何编译和运行

  1. 将以上四个代码块分别保存为 node.h, utils.h, utils.c, 和 tpsn.c

    tpsn算法c语言实现
    (图片来源网络,侵删)
  2. 使用 GCC 进行编译(需要链接 rt 库以支持 clock_gettime):

    gcc tpsn.c utils.c -o tpsn_simulation -lrt
  3. 运行程序:

    ./tpsn_simulation

4 预期输出

运行程序后,你将看到详细的日志输出,包括:

  1. 初始网络状态: 所有节点层级未设置。
  2. 层次构建阶段: 根节点(0)广播,其他节点逐级确定自己的层级和父节点。
  3. 同步阶段: 从第1层节点开始,每个节点与其父节点执行双向同步,打印出时间戳、计算出的延迟和偏差,并最终更新自己的时钟偏移量。
  4. 最终网络状态: 所有节点都处于 SYNCHRONIZED 状态,并显示其相对于根节点的累积时钟偏移。

这个 C 语言实现清晰地展示了 TPSN 算法的两个核心阶段,并模拟了其关键的消息交换和时间戳计算过程,虽然它是一个简化版本(没有考虑消息丢失、更复杂的时钟漂移模型),但它完美地诠释了 TPSN 的工作原理。

-- 展开阅读全文 --
头像
C语言存在byte类型吗?
« 上一篇 今天
dede列表标题长度如何设置?
下一篇 » 今天

相关文章

取消
微信二维码
支付宝二维码

目录[+]