WIFI学习六(SNTP)

article/2025/8/21 23:15:13

简介

        SNPT(Simple Network Time Protocal简单网络时间协议)用于跨广域网或局域网时间同步的协议,具有较高的精确度(几十毫秒)。SNTP是NTP协议的简化版

SNTP的工作方式

        SNTP协议采用客户端/服务器的工作方式,可以采用单播(点对点)或者广播(一点对多点)模式操作。

        单播模式下,客户端能够通过定期访问SNTP服务器来获取精确的时间信息,用于调整客户端自身的系统时间。

        广播模式下。SNTP服务器周期性地发送消息给指定的IP广播地址或IP多播地址。SNTP客户端通过监听这些地址来获取时间信息

         网络中一般存在多台SNTP服务器,客户端会通过一定的算法选择最好的几台服务器使用。如果一台SNTP服务器在工作过程中发生异常,则会通知SNTP客户端,那么SNTP客户端就会丢弃发生故障的SNTP服务器发给它的时间信息,然后重新选择其他的SNTP服务器

SNTP校准原理

        SNTP协议主要是通过记录客户端向服务器发送数据包时的时间戳 t1服务器端接收到该数据包时的时间戳 t2服务器向客户端回应的时间戳 t3最后客户端接收到服务器回应时的时间戳 t4来计算客户端时间和服务端时间的偏差,从而进行校准时间操作,如下图所示:

         则 t1 和 t2之间的时间差为((t2 - t1) + (t4 - t3)) / 2

        数据包在网络上传输的时间为(t2 - t1) + (t4 - t3)

        获取到时间差后,设备会校准RTC时间,来保证时间的正确性。

源码分析

sntp_setoperatingmode()

        设置操作模式

函数原型:

void sntp_setoperatingmode(u8_t operating_mode);

参数:

operating_mode 操作模式

SNTP_OPMODE_POLL             //单播模式   
SNTP_OPMODE_LISTENONLY            //组播模式

返回值:

实例:

sntp_setoperatingmode(SNTP_OPMODE_POLL);  //设置为单播模式

sntp_setservername()

        设置要连接的服务器主机名字

函数原型:

void sntp_setservername(u8_t idx, const char *server)
{LWIP_ASSERT_CORE_LOCKED();if (idx < SNTP_MAX_SERVERS) {sntp_servers[idx].name = server;}
}

参数:

idx 服务器编号。

server 服务器名字。

        可以看到,服务器可以设置多个,而数量的大小取决于SNTP_MAX_SERVERS参数。这个值可以根据需求自行设置。

返回值:

实例:

sntp_setservername(0, "1.cn.pool.ntp.org");sntp_setservername(1, "1.hk.pool.ntp.org");

sntp_setserver()

        设置要连接的服务器主机IP

函数原型:

void sntp_setserver(u8_t idx, const ip_addr_t *server)
{LWIP_ASSERT_CORE_LOCKED();if (idx < SNTP_MAX_SERVERS) {if (server != NULL) {sntp_servers[idx].addr = (*server);} else {ip_addr_set_zero(&sntp_servers[idx].addr);}
#if SNTP_SERVER_DNSsntp_servers[idx].name = NULL;
#endif}
}

参数:

idx 服务器编号。

server 服务器IP地址。

返回值:

实例:

struct ip4_addr test_addr;
IP4_ADDR(&test_addr, 213, 161, 194, 93);sntp_setserver(0, (const ip_addr_t *)(&test_addr));IP4_ADDR(&test_addr, 129, 6, 15, 29);sntp_setserver(1, (const ip_addr_t *)(&test_addr));

sntp_init()

        初始化SNTP模块,并创建UDP连接。

函数原型:

void sntp_init(void)
{
#ifdef SNTP_SERVER_ADDRESS
#if SNTP_SERVER_DNSsntp_setservername(0, SNTP_SERVER_ADDRESS);
#else
#error SNTP_SERVER_ADDRESS string not supported SNTP_SERVER_DNS==0
#endif
#endif /* SNTP_SERVER_ADDRESS */if (sntp_pcb == NULL) {sntp_pcb = udp_new_ip_type(IPADDR_TYPE_ANY);    //创建一个UDP PCB(协议控制块),IPV4+IPV6LWIP_ASSERT("Failed to allocate udp pcb for sntp client", sntp_pcb != NULL);if (sntp_pcb != NULL) {udp_recv(sntp_pcb, sntp_recv, NULL);    //设置udp接收回调if (sntp_opmode == SNTP_OPMODE_POLL) {  //是单播模式SNTP_RESET_RETRY_TIMEOUT();
#if SNTP_STARTUP_DELAYsys_timeout((u32_t)SNTP_STARTUP_DELAY_FUNC, sntp_request, NULL);
#elsesntp_request(NULL); //请求
#endif} else if (sntp_opmode == SNTP_OPMODE_LISTENONLY) { //广播模式ip_set_option(sntp_pcb, SOF_BROADCAST);udp_bind(sntp_pcb, IP_ANY_TYPE, SNTP_PORT); //绑定}}}
}

参数:

返回值:

udp_new_ip_type()

        创建一个针对IP类型的PCB(Protocol Control Block协议控制块)

函数原型:

struct udp_pcb *udp_new_ip_type(u8_t type)
{struct udp_pcb *pcb;LWIP_ASSERT_CORE_LOCKED();pcb = udp_new();  //创建一个UDP
#if LWIP_IPV4 && LWIP_IPV6if (pcb != NULL) {IP_SET_TYPE_VAL(pcb->local_ip,  type);IP_SET_TYPE_VAL(pcb->remote_ip, type);}
#elseLWIP_UNUSED_ARG(type);
#endif /* LWIP_IPV4 && LWIP_IPV6 */return pcb;
}

参数:

type IP地址类型,类型如下

enum lwip_ip_addr_type {/** IPv4 */IPADDR_TYPE_V4 =   0U,/** IPv6 */IPADDR_TYPE_V6 =   6U,/** IPv4+IPv6 ("dual-stack") */IPADDR_TYPE_ANY = 46U
};

返回值:

NULL失败

其他值 UDP的协议控制块

实例:

static struct udp_pcb* sntp_pcb;
sntp_pcb = udp_new_ip_type(IPADDR_TYPE_ANY); 
if (sntp_pcb != NULL) {//成功
}

udp_recv()

        设置一个接收回调,从UDP的协议控制块中

函数原型:

void udp_recv(struct udp_pcb *pcb, udp_recv_fn recv, void *recv_arg)
{LWIP_ASSERT_CORE_LOCKED();LWIP_ERROR("udp_recv: invalid pcb", pcb != NULL, return);/* remember recv() callback and user data */pcb->recv = recv;pcb->recv_arg = recv_arg;
}

参数:

pcb  UDP协议控制块,由udp_new_ip_type返回所得。

recv UDP接收的回调函数、

recv_arg UDP接收的回调函数的参数

返回值:

实例:

udp_recv(sntp_pcb, sntp_recv, NULL);  

sntp_request()

        发送SNTP请求

函数原型:

static void sntp_request(void *arg)
{ip_addr_t sntp_server_address;err_t err;LWIP_UNUSED_ARG(arg);/* initialize SNTP server address */
#if SNTP_SERVER_DNSif (sntp_servers[sntp_current_server].name) {/* always resolve the name and rely on dns-internal caching & timeout */ip_addr_set_zero(&sntp_servers[sntp_current_server].addr);err = dns_gethostbyname(sntp_servers[sntp_current_server].name, &sntp_server_address,sntp_dns_found, NULL);if (err == ERR_INPROGRESS) {/* DNS request sent, wait for sntp_dns_found being called */LWIP_DEBUGF(SNTP_DEBUG_STATE, ("sntp_request: Waiting for server address to be resolved.\n"));return;} else if (err == ERR_OK) {sntp_servers[sntp_current_server].addr = sntp_server_address;}} else
#endif /* SNTP_SERVER_DNS */{sntp_server_address = sntp_servers[sntp_current_server].addr;err = (ip_addr_isany_val(sntp_server_address)) ? ERR_ARG : ERR_OK;}if (err == ERR_OK) {LWIP_DEBUGF(SNTP_DEBUG_TRACE, ("sntp_request: current server address is %s\n",ipaddr_ntoa(&sntp_server_address)));sntp_send_request(&sntp_server_address);} else {/* address conversion failed, try another server */LWIP_DEBUGF(SNTP_DEBUG_WARN_STATE, ("sntp_request: Invalid server address, trying next server.\n"));sys_timeout((u32_t)SNTP_RETRY_TIMEOUT, sntp_try_next_server, NULL);}
}

参数:

可以忽略

返回值:

实例:

sntp_request(NULL); //请求

sntp_recv()

        UDP从SNTP的PCB的接收回调

函数原型:

static void sntp_recv(void *arg, struct udp_pcb* pcb, struct pbuf *p, const ip_addr_t *addr,u16_t port)

参数:

arg:接收的数据传参

pcb:协议控制块

p:接收到的数据

addr:IP地址

Port:端口

返回值:

整个函数中,需要注意的是sntp_process函数,该函数相当于对接收到的数据进行数据处理

sntp_process()

        对接收到的时间数据进行处理

函数原型:

static void sntp_process(const struct sntp_timestamps *timestamps)
{s32_t sec;u32_t frac;sec = (s32_t)lwip_ntohl(timestamps->xmit.sec);frac = lwip_ntohl(timestamps->xmit.frac);#if SNTP_COMP_ROUNDTRIP
# if SNTP_CHECK_RESPONSE >= 2 if (timestamps->recv.sec != 0 || timestamps->recv.frac != 0)
# endif{LOGI("Roundtrip compare processing...\n");s32_t dest_sec;u32_t dest_frac;u32_t step_sec;SNTP_GET_SYSTEM_TIME_NTP(dest_sec, dest_frac);step_sec =(dest_sec < sec) ? ((u32_t)sec - (u32_t)dest_sec) : ((u32_t)dest_sec - (u32_t)sec);/* In order to avoid overflows, skip the compensation if the clock step* is larger than about 34 years. */if ((step_sec >> 30) == 0) {s64_t t1, t2, t3, t4;/* t4 the time sntp client recv the reply, or the current time. */t4 = SNTP_SEC_FRAC_TO_S64(dest_sec, dest_frac);/* t3 the time sntp server transmitt the reply. */t3 = SNTP_SEC_FRAC_TO_S64(sec, frac);/* t1 the time sntp client send a request. */t1 = SNTP_TIMESTAMP_TO_S64(timestamps->orig);/* t2 the time sntp server recv the request. */t2 = SNTP_TIMESTAMP_TO_S64(timestamps->recv);/* Clock offset calculation according to RFC 4330 */t4 += ((t2 - t1) + (t3 - t4)) / 2;sec = (s32_t)((u64_t)t4 >> 32);frac = (u32_t)((u64_t)t4);}}
#endif /* SNTP_COMP_ROUNDTRIP */			time_t tim = (u32_t)((sec) + DIFF_SEC_1970_2036);/* change system time and/or the update the RTC clock */SNTP_SET_SYSTEM_TIME(sec);/* display local time from GMT time *///LWIP_DEBUGF(SNTP_DEBUG_TRACE, ("sntp_process: %s", ctime(&tim)));LOGI("sntp_process: %s", ctime(&tim));LWIP_UNUSED_ARG(tim);
}

参数:

timestamps 时间戳,结构体类型如下:

struct sntp_timestamps
{
#if SNTP_COMP_ROUNDTRIP || SNTP_CHECK_RESPONSE >= 2  struct sntp_time orig;  //发送前时间戳struct sntp_time recv;  //接收到时间戳
#endif  struct sntp_time xmit;  //传输
};

sntp_time类型如下:

struct sntp_time
{u32_t sec;  //秒u32_t frac; //毫秒
};

返回值:

        从上述代码中可以看到,实际上正如在文章开头所讲的那样SNTP是根据时间差来进行校准时间,计算后得到结果后,需要关注的函数是SNTP_SET_SYSTEM_TIME,后续的数据处理,都在该函数中进行。

#define SNTP_SET_SYSTEM_TIME(sec) sntp_set_system_time((u32_t)((sec)+DIFF_SEC_1970_2036),0)

sntp_set_system_time()

        sntp根据计算得到的结果来设置系统时间。

函数原型:

static void sntp_set_system_time(time_t t, u32_t us)
{struct tm *gt = NULL;hal_rtc_time_t r_time;hal_rtc_status_t st = HAL_RTC_STATUS_OK;LOGI("sntp_set_system_time input:  %"U32_F" s,%"U32_F" us\n",t,us);gt = gmtime(&t);if (gt == NULL) {gt = localtime(&t);}r_time.rtc_year = (gt->tm_year % 100);r_time.rtc_mon = gt->tm_mon + 1;r_time.rtc_day = gt->tm_mday;r_time.rtc_week = gt->tm_wday;r_time.rtc_hour = gt->tm_hour;r_time.rtc_min = gt->tm_min;r_time.rtc_sec = gt->tm_sec;st = hal_rtc_set_time(&r_time);if (sntp_cb) {LOGI("sntp callback\n");sntp_cb(r_time);}LOGI("sntp(%d %d %d ",gt->tm_wday,gt->tm_mon,gt->tm_mday); LOGI("%d:%d:%d %d)\n",gt->tm_hour,gt->tm_min,gt->tm_sec,gt->tm_year); LOGI("sntp(atx):(%d:%d)\n",gt->tm_isdst,gt->tm_yday); LOGI("sntp st1(%u)\n",st);st = hal_rtc_get_time(&r_time);(void)st;LOGI("sntp(%u  %u  %u ",r_time.rtc_week,r_time.rtc_mon,r_time.rtc_day); LOGI("%u:%u:%u %u)\n",r_time.rtc_hour,r_time.rtc_min,r_time.rtc_sec,r_time.rtc_year); LOGI("sntp st2(%u)\n",st);
}

参数:

t 时间戳

us  微秒

返回值:

        该函数将数据进行划分计算,得到最终需要的数据结果,然后设置本地的RTC时间。设置完成后,调用sntp_cb函数来进行用户自己的操作。

 sntp_set_callback()

        设置用户自定义的回调结果

函数原型:

void sntp_set_callback(sntp_callback callback)
{sntp_cb = callback;
}

参数:

callback 用户自定义回调结果

返回值:


http://chatgpt.dhexx.cn/article/SN9joPlu.shtml

相关文章

SNTP简介

要了解SNTP&#xff0c;首先需要了解NTP协议。SNTP是NTP的子集&#xff0c;简化了NTP的许多算法和步骤&#xff0c;得到了效率&#xff0c;但时间的精度不如NTP&#xff0c;可是对于民用时间来说足够了&#xff0c;大概最多差距几秒的样子。 NTP&#xff08;Network Time Proto…

SNTP原理讲解 客户端 C语言实现

1.定义 SNTP(Simple Network Time Protocal简单网络时间协议)&#xff0c;用于跨广域网或局域网同步时间的协议&#xff0c;具有较高的精确度&#xff08;几十毫秒&#xff09; 这里给出SNTP的详细定义和解释&#xff0c;以及基于C语言的SNTP客户端实现代码&#xff1a;SNTP c…

使用SNTP协议获取时间

要了解SNTP,首先需要了解NTP协议。SNTP是NTP的子集,简化了NTP的许多算法和步骤,得到了效率,但时间的精度不如NTP,可是对于民用时间来说足够了,大概最多差距几秒的样子。 NTP(Network Time Protocol,网络时间协议)是由RFC 1305定义的时间同步协议,用来在分布式时间服…

sntp协议简介

SNTP(Simple Network Time Protocal简单网络时间协议)&#xff0c;用于跨广域网或局域网同步时间的协议&#xff0c;具有较高的精确度&#xff08;几十毫秒&#xff09;。SNTP是NTP协议的简化版&#xff0c;两者分别在RFC1305和RFC2030介绍。 SNTP工作方式 SNTP采用客户端/服务…

关于马尔可夫过程的一些学习笔记

①马尔可夫性质&#xff1a;已知“现在”的情况下&#xff0c;“未来”与“过去”彼此独立&#xff08;N1仅与N有关&#xff0c;与N以前的所有都无关&#xff09; ②马尔可夫过程&#xff1a;具有马尔可夫性质的随机过程 ③马尔可夫链&#xff1a;马尔可夫过程的原始模型&…

来聊聊马尔可夫过程

相信你对马尔科夫这个词一定不陌生&#xff0c;因为在人工智能机器学习领域&#xff0c;这个词是避不开的&#xff0c;如马尔可夫链&#xff08;Markov chain)&#xff0c;马尔科夫随机场(Markov random field)&#xff0c;马尔可夫过程(Markov process)&#xff0c;隐马尔可夫…

【算法学习】马尔可夫过程及经典例题讲解(含代码实现)

公众号关注 52DATA &#xff0c;获得更多数据分析知识,感谢支持—> 文章目录 马尔可夫过程1. 马尔可夫性2. 马尔可夫链2.1 转移概率矩阵&#xff08;随机矩阵&#xff09;2.2 状态概率2.3 平稳分布 3.一个很经典的例题帮助理解马尔科夫预测方法1.求状态转移概率2.运用状态概…

数学基础(8)-- 马尔可夫链与马尔可夫过程

目录 1. 简介 1.1 定义 1.2 变种 2. 瞬态演变 3. 马尔科夫链性质 3.1 可还原性 3.2 周期性 3.3 重现性 4. 有限状态空间 1. 简介 马尔可夫链&#xff08;英语&#xff1a;Markov chain&#xff09;&#xff0c;又称离散时间马可夫链&#xff08;discrete-time Markov…

部分可观测马尔可夫过程POMDP

POMDP与MDP的一句话区别&#xff1a;POMDP的state具有不确定性&#xff0c;由七元数组定义&#xff0c;多了观测空间、观测函数、初始置信&#xff08;belief&#xff09;&#xff0c;根据观测概率矩阵求出最可能是的状态 利用值迭代法解决POMDP问题 MDP POMDP 状态→动作 信…

马尔可夫 java_马尔可夫过程(以马尔科夫链Markov为例)

马尔可夫过程(以马尔科夫链Markov为例) 马尔可夫过程 马尔可夫过程的大概意思就是未来只与现在有关&#xff0c;与过去无关。 简单理解就是渣男只在乎下一刻会不会爱你只取决于这一时刻对你的新鲜感&#xff0c;而与你之前对这段感情的付出毫无关系。 设有一个随机过程X(t)&…

马尔可夫决策过程

马尔可夫决策过程 一、马尔科夫决策过程&#xff1a;**马尔科夫决策过程****最优决策**值迭代策略迭代MDP中的参数估计 二、代码实战&#xff1a;A、马尔可夫决策过程值迭代B、马尔可夫决策过程策略迭代C、马尔可夫决策过程动态规划版 参考文章 本文介绍了马尔可夫决策过程&…

随机过程第2讲——马尔可夫过程的应用

温习&#xff1a;随机过程第1讲——泊松过程的模拟与检验&#xff1a;https://blog.csdn.net/ChenQihome9/article/details/82871332 去得也突然——不知在什么时候&#xff0c;雨&#xff0c;悄悄地停了。风也屏住了呼吸&#xff0c;山中一下变得非常幽静。远处&#xff0c;一…

强化学习(2): 马尔可夫过程

前言 本文重点介绍MDP&#xff0c;因为MDP是目前最适合表征强化学习问题的模型。 一个具体的赌徒例子&#xff0c;来说明强化学习的算法如何与MDP构建联系&#xff0c;并且求解出最优策略。链接如下&#xff1a;link 一、马尔可夫性 其假设未来的状态仅取决与当前的状态。过…

贝叶斯网络、马尔可夫模型、马尔可夫过程、马尔可夫链、马尔可夫网络基本概念

知识储备与简要概括 可数集【Countable set】&#xff1a; 是指每个元素都能与自然数集N的每个元素之间能建立一一对应的集合。如果将可数集的每个元素标上与它对应的那个自然数记号&#xff0c;那么可数集的元素就可以按自然数的顺序排成一个无穷序列a1&#xff0c;a2&#…

强化学习笔记:马尔可夫过程 马尔可夫奖励过程

1 马尔可夫性质 &#xff08;Markov Property&#xff09; 我们设状态的历史为&#xff08;包含了之前的所有状态&#xff09; 如果一个状态转移是符合马尔可夫性质的&#xff0c;也就是满足如下条件&#xff1a; 也就是说&#xff0c;从当前状态转移到状态的概率&#xff0c;就…

马尔可夫性质、马尔可夫链和马尔可夫过程

关注&#xff1a;灰质&#xff0c;有趣有料的AI技术分享 前言 研究决策问题就一定听说过马尔可夫过程&#xff08;Markov Process&#xff09;&#xff0c;这是一类非常重要的方法。现在非常热门的强化学习都是基于马尔可夫过程方法建立的。马尔可夫决策过程是研究随机序贯决策…

1.3 马尔可夫过程

之前介绍的奖励、智能体、动作、观察和环境可以看成RL的一级概念。以此为基础&#xff0c;我们将探索RL的二级概念&#xff0c;包括状态&#xff08;state&#xff09;、事件&#xff08;episode&#xff09;、历史&#xff08;history&#xff09;、价值&#xff08;value&…

一文看懂马尔科夫过程

1.马尔科夫决策过程(MDPs)简介 马尔科夫决策过程是对强化学习(RL)问题的数学描述。几乎所有的RL问题都能通过MDPs来描述: 最优控制问题可以用MDPs来描述;部分观测环境可以转化成POMDPs;赌博机问题是只有一个状态的MDPs;注:虽然大部分DL问题都能转化为MDPs,但是以下所描述…

马尔可夫Markov决策过程 MDP、马尔可夫奖励过程MRP

引言 在概率论及统计学中&#xff0c;马尔可夫过程&#xff08;英语&#xff1a;Markov process&#xff09;是一个具备了马尔可夫性质的随机过程&#xff0c;因为俄国数学家安德雷马尔可夫得名。马尔可夫过程是不具备记忆特质的&#xff08;memorylessness&#xff09;。换言…

零基础学习python数据分析,需要掌握哪些技能?

对于刚刚入行的小白同学来说&#xff0c;在学习python的过程中&#xff0c;一定会遇到一些疑问。比如说&#xff1a; 学习Python需要多久&#xff1f; 学习Python需要达到什么样的程度&#xff1f; 学Python的书籍有哪些&#xff1f; 为了处理数据集&#xff0c;我需要精通…