# 纯 APP NITZ 校时实机测试 当前 `test/time-fix` 在 PDP 首次激活后自动启动 NITZ 测试,替换上一版自动 NTP 测试。只改 APP;本次没有修改、编译或烧录内核,不依赖之前添加的内核诊断接口。NTP 测试源文件保留但不参与当前 APP 构建。此模块不访问时间服务器域名或 UDP 123,使用设备已有的运营商 NITZ 查询接口。 ## 新增 SYS 读取探测 当前版本在 NITZ 测试之前运行 `[APP-SYS-PROBE]`,只使用当前源码声明过的函数签名和 APP 已有 AT 接口,不读取旧 AXF,不修改内核,不调用 `talkingq_time_diag_snapshot_v1`。原有 TIME-DIAG 日志是否可用,不影响本探测。 - 动态查找 `PMIC_RTC_GetTime_Count` 和 `GetTimeZone`。如果原内核本来就导出这两个名字,调用 `SYS_OFFSET=0`,减去网络时区分钟数,间隔 3 秒读取两次,检查合理范围和推进量。找不到就输出 `direct_sys=unavailable`,不添加内核导出、不尝试固定地址。 - 输出 `time(NULL)`、`ql_rtc_get_time_ex()` 作为对照,不把 `get_time_ex` 的名称当成读 SYS 的证据。 - 通过 `ql_atcmd_send_sync` 连续两轮读取 `AT+QLTS=0`、`AT+QLTS=1`、`AT+QLTS=2`、`AT+CCLK?`。两轮间隔 3 秒,每个 AT 请求超时参数为 2 秒。记录返回码、运行计数和原始响应;本型号不支持时允许返回 ERROR。两个 QLTS 候选模式是否提供有效当前时间,需根据原始日志对照确认。 - 本探测没有 RTC 设置调用。`direct_sys=candidate` 仅表示现有 SYS 函数可读、时间范围和推进正常,仍需与实际 UTC 核对;AT 返回成功也不等于已确认来源。 刷入后提供从 `[APP-SYS-PROBE] begin` 到 `finished` 的完整输出。若 QLTS 返回正确且推进的当前时间,可进一步实现 APP 校时;若所有接口缺失/返回错误或只返回 APP 旧时间,该路线仍未得到可用依据。后续 NITZ 测试保持原有写入条件,快照冻结时不写 RTC。 ## 编译和测试 在现有 Windows 环境执行 `build.bat app`,按现有流程更新 APP 镜像。保持原内核、原 RTC/NVM 状态。核对 APP 构建成功及镜像容量后启动设备,保留从开机到测试结束、再到一次实际 WSS 连接尝试的完整串口日志。 应看到: ```text [TIME-DIAG] nitz_enabled=1 app_nitz_test=once_after_pdp ntp_test=off [APP-SYS-PROBE] begin version=1 read_only=1 kernel_diag_used=0 [APP-SYS-PROBE] exports sys_count=... network_zone=... ... [APP-SYS-PROBE] finished rtc_not_written=1 [APP-NITZ-TEST] begin version=2 source=operator_nitz ntp=off ``` 本机只进行了主机模拟测试和 ARM 目标语法检查,尚未完成 Windows 完整固件构建或实机测试。 ## 实际流程 1. 检查原内核是否导出公开 SDK 接口 `ql_nw_get_nitz_time_info`、`ql_localtime_r`、`ql_rtc_set_time`、`ql_rtc_get_time`。 2. 查询运营商 NITZ,输出完整原始字符串、返回值、`abs_time`、`leap_sec` 和查询耗时。支持 SDK 文档格式 `YY/MM/DD HH:MM:SS +32 00` 、紧凑格式 `YY/MM/DD,HH:MM:SS+32`,以及实机已返回的无正号、一位夏令时格式 `YY/MM/DD HH:MM:SS 32 0`。严格检查日期、闰年、时区及 UTC 范围(2024 年起,至 signed 32 位秒数上限)。不把语义未明确的 `abs_time` 当 Unix 时间戳使用。 3. 间隔 3 秒再次查询。两次 UTC 必须向前推进,并与系统运行计数的增量相差不超过 1 秒,时区相同;单次查询超过 2 秒不采用。冻结的 NITZ 快照不写入 APP。最多尝试 3 组,组间等待 5 秒。 4. 使用 NITZ 的 UT 日期字段生成 UTC 秒数。时区字段以 15 分钟为单位,只记录,不再次从 UT 扣除。这个解释依据仓库旧 SDK 镜像的 NITZ 转换逻辑:UT 日期转换后加时区生成 SYS 本地日历。原始值和解释会保留在日志中,供实机核对。 5. 通过 SDK `ql_localtime_r` 按现有 PMIC 时区把 UTC 转成 APP RTC 日历,再调用 `ql_rtc_set_time`。不改时区、不操作 `app_ever` 或内部跟随标记、不直接写 NVM 文件。 6. 读回 `ql_rtc_get_time` 日历和 `time(NULL)` 的 UTC 返回值,两者都必须与目标相差不超过 3 秒,并且 SDK 返回成功,才输出 `PASS`。设置后无论成功失败,都结束本次测试,避免反复写 RTC。 测试任务独立运行,不等待它就可以继续 MQTT 流程。每次启动完成一次测试;仅任务创建失败时允许下一次 PDP 激活重试。此版本没有周期重校、WSS 启动等待或主动重连逻辑,不是完整发布方案。 ## 日志判断 以下是格式示例,不是设备实测结果: ```text [APP-NITZ-TEST] sample attempt=1 index=1 rc=0 raw="26/10/26 06:00:00 +32 00" abs_time=0 leap_sec=0 query_ms=... [APP-NITZ-TEST] parsed utc=... zone_quarters=32 zone_minutes=480 [APP-NITZ-TEST] freshness advance_s=3 elapsed_s=3 drift_s=0 [APP-NITZ-TEST] set_begin app_before=... expected_utc=... pmic_zone_minutes=480 app_calendar=2026-10-26T14:00:03 [APP-NITZ-TEST] readback set_rc=0 rtc_rc=0 app_after=... expected_utc=... delta_s=0 calendar=... [APP-NITZ-TEST] PASS source=nitz app_utc=verified wss_requires_separate_handshake_check=1 [APP-NITZ-TEST] finished one_test_per_boot=1 ``` - `sdk_api_unavailable`:原内核缺少所需公开接口,本次不写 RTC。 - `stage=parse`:时间为空、格式不支持或日期/范围无效。保留 `sample` 原文以便分析。 - `stage=freshness`:两次时间冻结、跳变或与运行计时不符,不采用。若三组都失败,会输出 `no_fresh_nitz rtc_not_written=1`。 - `stage=local_calendar`:SDK 时区转换未产生可用日历,不写 RTC。 - `stage=app_clock_verification`:设置返回失败,或者读回 UTC/日历不符。需要分析是否有时区或 APP RTC 写入问题。 `PASS` 只证明 NITZ 候选值推进正常、APP 写入和读回相符,无法单独证明运营商绝对时间准确或当前固件的 NITZ UT 语义与旧 SDK 完全一致。实机仍需把原始 NITZ、APP UTC 和实际北京时间对照,尤其检查整 8 小时偏差。它也不等于 WSS 握手或 OTA 下载成功;这两项继续看后续实际连接日志,TLS 校验保持开启。 SDK 可能只提供 NITZ 最近一次通知的快照;如出现冻结拒绝,不应去掉新鲜度检查直接写入,应据日志评估带接收时刻的 NITZ 事件或已有业务服务器授时路径。本测试不注册新网络回调,不抢占现有网络事件处理。 ## 主机回归 `python3 tools/test_app_nitz_test.py` 编译实际 C 模块,模拟 API 缺失、查询失败、空串、无终止符、无效日期、冻结时间、UTC 跨天、时区转换、运行计数回绕、signed 32 位边界、首次查询失败恢复、写入失败、读回偏差及任务创建失败。启用 ASan/UBSan;不发送设备或公网数据。 `python3 tools/test_app_sys_time_probe.py` 对实际 SYS/AT 探测模块验证 10 种场景:接口缺失、动态读取、无效/冻结时间、AT 错误、无终止符响应、tick 回绕等。测试不链接 RTC setter 或新增诊断接口;主机测试不证明本型号支持 QLTS。