Compare commits

..
44 changed files with 1309 additions and 567 deletions
+56
View File
@@ -0,0 +1,56 @@
# 纯 APP:开机首次有效 NITZ 校时测试
当前 `test/time-fix` 在 PDP 首次激活后启动一次独立测试任务,尽早读取有效的运营商 NITZ 快照,写入 APP RTC 后读回验证。不再要求两次查询值每隔 3 秒推进;这符合实机已观察到的“最近一次授时快照”行为。
只改 APP,没有修改内核,也不依赖新增的 `talkingq_time_diag_snapshot_v1`。前置 SYS/AT 探测及 APP NTP 测试均已关闭,相关源文件保留但不参与当前 APP 构建。原有内核功能不因本 APP 测试而关闭。
## 编译与实测
在现有 Windows 环境执行 `build.bat app`,构建成功并核对 APP 镜像容量后,只更新 APP。保持原内核和故障现场 RTC/NVM 状态。
启动时应出现:
```text
[TIME-DIAG] nitz_enabled=1 app_nitz_test=first_valid_after_pdp ntp_test=off sys_probe=off
[APP-NITZ-TEST] begin version=3 source=operator_nitz ntp=off policy=first_valid_after_pdp sys_probe=off
```
保留从启动到 `[APP-NITZ-TEST] finished` 的完整日志,随后按语音键触发一次实际 WSS 连接,并保留连接日志。以下是预期格式示例,不是本次实机结果:
```text
[APP-NITZ-TEST] sample attempt=1 index=1 rc=0 raw="26/10/26 08:00:10 32 0" abs_time=... leap_sec=0 query_ms=5
[APP-NITZ-TEST] parsed utc=1793001610 zone_quarters=32 zone_minutes=480
[APP-NITZ-TEST] snapshot_selected attempt=1 utc=1793001610 age=unknown first_valid_after_pdp=1 max_rtc_writes=1
[APP-NITZ-TEST] set_begin app_before=... expected_utc=1793001610 pmic_zone_minutes=480 app_calendar=2026-10-26T16:00:10
[APP-NITZ-TEST] readback set_rc=0 rtc_rc=0 app_after=1793001610 expected_utc=1793001610 delta_s=0 calendar=2026-10-26T16:00:10
[APP-NITZ-TEST] PASS source=nitz app_utc=verified wss_requires_separate_handshake_check=1
[APP-NITZ-TEST] finished one_test_per_boot=1
```
## 校时逻辑
1. 检查原内核是否提供既有公开 SDK 接口 `ql_nw_get_nitz_time_info`、`ql_localtime_r`、`ql_rtc_set_time`、`ql_rtc_get_time`,缺失则退出。
2. 立即读取 NITZ。支持实机格式 `YY/MM/DD HH:MM:SS 32 0`、文档中的带符号格式和紧凑格式。严格检查终止符、日期、闰年、时区和 UTC 范围:2024 年起至 signed 32 位秒数上限。
3. 查询失败、空值、无效格式或查询耗时超过 2 秒,不写入,间隔 5 秒重试,最多 3 次。首次有效后立即采用,不等待第二个快照,也不运行前置 AT/SYS 探测。
4. NITZ UT 日期字段按 UTC 解析;`32` 表示 32 个 15 分钟,即 UTC+8。UTC 时间戳本身不加 8 小时。使用 SDK `ql_localtime_r` 按现有 PMIC 时区转换成 RTC 本地日历,再调用 `ql_rtc_set_time`。不直接改时区、跟随标记或 NVM 文件。
5. 读回 RTC 日历和 `time(NULL)` 返回的 UTC 秒数,要求 SDK 返回成功、日期有效,且 UTC 和本地日历均与写入目标相差不超过 3 秒,才输出 `PASS`。
6. 设置尝试后不再重试写入,即使读回失败;任务结束后本次启动不再校时。仅任务创建失败允许下一次 PDP 激活重试创建。
## 结果与边界
- `sdk_api_unavailable`:缺少所需公开 SDK 接口。
- `stage=parse` / `stage=slow_query`:本次查询未采用。
- `no_valid_nitz rtc_not_written=1`:三次均未得到可采用值。
- `snapshot_selected`:已选择本次启动首次读到的有效快照。
- `stage=local_calendar`:SDK 时区转换未产生可用 RTC 日历,不写入。
- `app_clock_verification`:设置或读回验证失败。
本版本按用户要求验证“开机联网后尽早采用首次有效快照”。首次读取不等于已证明基站刚刚下发;接口没有提供本测试可确认的快照接收时刻,所以记录 `age=unknown`。只补偿本次查询返回后至写入的运行时间,不把设备开机时间或 PDP 激活时间冒充快照接收时间。
`PASS` 证明 APP 设置、时区换算和读回与所选快照一致,不独立证明快照绝对新鲜。需把原始 NITZ、APP 日历和实际北京时间对照,并继续看 WSS 实际握手。TLS 校验保持开启。本版没有增加 NITZ 更新事件处理、周期重校、WSS 启动等待或主动重连;先验证一次 APP 校时能否恢复当前故障设备。
## 本机验证
`python3 tools/test_app_nitz_test.py` 使用实际 C 源码验证解析、首次有效快照(含实机冻结快照)、失败后重试、最多一次写入、日期跨天、PMIC 时区转换、UTC 不重复偏移、tick 回绕、signed 32 位边界、设置失败及读回不符。启用 ASan/UBSan,不发送设备或公网数据。
同时对实际 SDK 头文件执行 ARM 目标语法检查。当前 macOS 环境不能运行仓库的 Windows 完整构建工具链,完整 APP 构建和实机验证待用户执行。SYS/AT 探测代码仍可通过 `tools/test_app_sys_time_probe.py` 单独回归,但不在本次开机流程中运行。
+46
View File
@@ -0,0 +1,46 @@
# 纯 APP 的设备侧 NTP 测试
当前分支已切换为自动 NITZ 测试(见 `docs/app_nitz_test.md`);下文描述前一版 NTP 测试,当前不会自动启动,也不参与 APP 构建。
本次测试只新增 APP 代码,不需要编译、修改或更新内核。测试使用设备自己的 PDP 1 和 SIM 网络,不使用电脑网络来判断白名单是否放行。
## 编译与运行
在现有 Windows 构建环境执行 `build.bat app`,按现有 APP OTA 流程生成、校验和更新 APP 镜像,核对分区容量。保留目标设备原内核和 RTC/NVM 状态。不要为了运行这个测试执行 `build.bat kernel` 或烧录全分区包;分支之前的内核诊断改动不是本测试的依赖。
每次启动后,PDP 首次成功激活会自动启动独立测试任务,不阻塞 MQTT 联网流程。每次启动只测试一次,不依赖串口命令。默认服务器为现有 GNSS 配置所用的 `ntp.aliyun.com`,端口为 UDP 123;可在编译时用 `APP_NTP_TEST_SERVER` 替换为白名单中的服务器。
测试会实际调用现有 SDK NTP 接口,可能校正 APP RTC。这是连通性与校时效果的测试版本,不包含正式修复所需的周期校时、退避重试和 WSS 等待策略。
## 判定流程
1. 在设备 PDP 上解析目标域名,输出解析到的 IP。`dns_ok` 不表示 NTP 可用。
2. 向解析到的第一个 IPv4 地址发送 NTP 请求,接收窗口为 5 秒。使用连接的 UDP socket,并检查响应模式、版本、闰秒状态、层级、请求时间戳回显及时间范围。合法响应输出 `UDP_RESPONSE_VERIFIED` 和服务器 UTC 秒数。支持 2036 年 NTP era 翻转,范围限定在 2024 年至目标 signed 32 位时间上限。
3. 检查原内核是否导出已有的 `ql_ntp_set_server`、`ql_ntp_set_cid`、`ql_ntp_sync_ex`。缺少接口时明确失败,不把动态转发接口静默返回当成成功。
4. 通过公开 SDK 接口向同一域名发起 APP 校时,等待回调最多 25 秒。超时后不启动第二个请求;信号量保留到本次启动结束,以便迟到回调安全返回。
5. 读取 `time(NULL)` 的返回值和 RTC 日历。要求回调成功、RTC 读取成功、APP UTC 有效,且 APP UTC 与第一次独立响应按运行计时外推后的时间相差不超过 6 秒,才输出 `PASS`。该容差涵盖首次响应最多 5 秒的传输窗口及秒级取整;不是精密授时指标。
网络超时只能说明这次没有取得响应,不能单凭超时确定是白名单拦截,也可能是服务器、DNS、路由或临时网络故障。该 NTP 测试不提供服务器身份认证。
## 日志示例
以下是格式示例,不是本次设备实测结果:
```text
[APP-NTP-TEST] begin server=ntp.aliyun.com port=123 protocol=UDP pdp=1
[APP-NTP-TEST] dns_ok ip=... response_not_yet_verified=1
[APP-NTP-TEST] UDP_RESPONSE_VERIFIED ip=... epoch=... rtt_ms=...
[APP-NTP-TEST] sdk_sync_begin app_before=...
[APP-NTP-TEST] sdk_result callback=0 app_before=... app_after=... expected=... delta_s=0 rtc_rc=0
[APP-NTP-TEST] PASS udp_response=verified app_utc=verified wss_still_requires_separate_handshake_check=1
```
`FAIL stage=dns`、`receive_wait`、`sdk_api_unavailable`、`sdk_callback_timeout`、`app_clock_verification` 分别定位到解析、接收、内核接口兼容、SDK 回调和 APP 时间结果。
设备上出现 `PASS` 证明该次网络响应及 APP 校时读回通过,WSS 实际握手仍需另行核对。发布前还需验证原内核、故障现场 NVM、重启、断网恢复等场景。
## 本机检查与当前状态
`python3 tools/test_app_ntp_test.py` 编译实际测试模块,使用模拟网络和 SDK 验证响应校验、成功与失败路径、SDK 缺失、时区偏差、迟到回调及资源释放。这些模拟测试不向设备或公网发包。
当前 macOS 环境无法运行仓库的 Windows 完整固件构建工具链。设备串口已有程序占用,且当前 APP 没有 NTP 测试指令,因此尚未执行本测试固件的设备侧验证,不能宣称白名单已经放行。
-9
View File
@@ -1,9 +0,0 @@
# APP 启动校时
PDP 首次激活后,APP 通过现有 SDK 的 `ql_nw_get_nitz_time_info` 获取运营商 NITZ 快照。按 UTC 解析日期,用 `ql_localtime_r` 转换到当前 PMIC 时区,再调用 `ql_rtc_set_time` 写入 APP RTC。读取 RTC 和 `time(NULL)`,两项误差都不超过 3 秒才打印 `[APP-TIME] PASS`。
每次开机只启动一次;读取失败、日期无效或查询耗时超过 2 秒时,间隔 5 秒重试,最多 3 次。选中首个有效快照后最多写入一次,即使读回失败也不重复写入。保留原有 NITZ 开关,不增加网络域名或端口,不修改内核,不依赖 NTP、AT 命令、SYS 探测或额外内核诊断。支持现有内核导出接口下的纯 APP OTA。
限制:SDK 返回快照未提供接收时间,读回验证只证明 APP 时钟与快照一致,不能证明快照新鲜度。本方案保留实机验证通过的首次 PDP 后快照策略,不扩展为持续校时。首次 WSS 建立仍需等待实际 TLS 握手验证;本次修复不修改连接调度或对话回复超时。
验证:`DEVELOPER_DIR=/Library/Developer/CommandLineTools python3 tools/test_app_time_calibrate.py`,实际 C 源码通过 26 组行为场景和日期解析验证(ASan/UBSan),覆盖 UTC/时区、无效和静态快照、重试、tick 回绕、RTC 写入/读回失败。迁移依据为 test/time-fix 的 884ab54;仅调整模块命名与日志,不改变已验证校时算法。2026-10-26 17:29:27 完整串口日志中,原版本校时读回误差为 0 秒,WSS 证书验证与 HTTP 101 成功,第二次对话收到 TTS。
+31
View File
@@ -0,0 +1,31 @@
# RTC / GNSS / WSS 诊断日志
所有新增输出以 `[TIME-DIAG]` 开头,仅采集状态,不主动设置时间、清除 NVM 或修改校时重试策略。
## 构建与采集
完整诊断需要在支持的 Windows 编译环境依次执行 `build.bat kernel`、`build.bat app`、`build.bat firmware`,并更新匹配的内核和 APP。普通 APP OTA 不代表内核已更新。保留设备原有 RTC/NVM 状态,以免丢失异常现场。
APP 兼容旧内核:缺少新增接口时打印 `kernel_diag=unavailable need_matching_diagnostic_kernel=1`,仍可输出 WSS 的 APP 时间,但无法取得内核偏移和跟随标志。
采集从上电至 WSS 首次失败、随后恢复的完整串口日志,并记录烧录包版本。启动日志包含 APP 版本、编译信息和固件版本;内核事件日志包含自身编译日期。重点比较异常设备与正常设备的下列阶段:
- `boot-before-nitz`:启动且尚未开启 NITZ。
- `nitz-enabled`、`pdp-active`:开启运营商校时、数据连接成功。
- `gnss_ntp_dns`、`gnss_ntp_trigger`、`ntp_request/result`、`ntp_rtc_written`:GNSS 发起的 NTP 路径。
- `gnss-time-state`、`ntp-finished`:内核校时状态变化和 NTP 完成。
- `wss-before-time-read`、`wss-precheck`、`wss-after-time-read`:WSS 读取 APP 时间前后。
## 字段
`counter` 是原始 RTC 计数,`sys_offset/app_offset` 是两套偏移;`sys_raw/app_raw` 是无符号 32 位相加后的原始秒数,不直接视为 UTC。`follows_sys` 是 APP 跟随 SYS 标志;`app_ever` 表示持久化的 APP RTC 设置标记;`storage` 是 SDK 的偏移存储枚举。
`net_tz_min` 单位为分钟,`rtc_tz_hour` 单位为小时;`source` 对应 SDK 的 NITZ=0、NTP=1、APP=2、UNKNOWN=3。`ntp_status` 对应原有状态 0/1/2(初始/成功/失败),日志忠实输出现有状态,不能仅凭这个标志判定实际校时成功。`ntp_type` 是 SDK 的校时类型状态,`ntp_ready` 表示任务和事件句柄已创建;`gnss_rtc` 是 GNSS 网络模块的校时状态,`cid` 为该模块记录的数据上下文。未编入的可选功能字段为 -1。
`time_null` 是 WSS 原来使用的 `time(NULL)` 样本,门槛仍为 `1704067200`;返回这个原始样本用于判断。`time_ptr_return/value` 用于对比 `time(&value)` 的返回值和指针写回,未写回时 value 保持哨兵值 1。`app_calendar` 来自额外的 `ql_rtc_get_time`,只在 `rtc_rc` 成功时有意义。
内核快照直接读计数、偏移和标记,避开可能开启 APP 跟随行为的 RTC 取时接口。WSS 额外读取发生在原来的首次 `time(NULL)` 之后。多字段采样并非原子快照,允许跨秒或并发校时造成的短暂差异,需结合连续日志判读。
## 本机检查
`python3 tools/test_time_diag.py` 使用本机 C 编译器及模拟 SDK,编译实际新增模块,验证可选接口缺失、ABI 不匹配、未初始化 RTC、SYS/APP 偏移差异、32 位溢出及 WSS 保留原始门槛样本。它不替代 Windows 目标固件编译、链接或实机复现。
-35
View File
@@ -1,35 +0,0 @@
# 对话无回复诊断日志
设备端新增四类日志,按 `round`、`session`、单调运行时间关联。诊断不调整对话协议、VAD 阈值或回复等待时限。
| 标记 | 内容 | 可以缩小的范围 |
| --- | --- | --- |
| `MIC-DIAG` | 录音打开结果及句柄、读取返回值/字节数、全零帧、峰值/平均绝对幅值、录音事件、PCM 缓冲写入丢失 | 区分未取得 PCM、全零输入、已有音频但没有通过 VAD、音频交给编码器前丢失 |
| `VOICE-TX` | 认证消息入队结果;START/END 的序号、类型、字节数、返回值和入队耗时;音频入队失败 | 区分请求未入队、内存分配失败、正常提交发送队列 |
| `WSS-IO` | 实际 TLS 写入结果、累计写入次数和字节;TLS 错误码、部分写入进度和重试;实际接收字节;关闭时的累计收发及未消费接收字节 | 区分入队后未完成写入、对端关闭、收到字节但应用层未获得完整消息 |
| `VOICE-RX` | 接收文本长度、前 192 字节、当前会话、是否通过会话准入检查;二进制消息计数 | 显示 START_ACK/END_ACK、未知回复、被准入检查丢弃的消息。二进制消息仍沿用原有忽略行为 |
| `VOICE-DIAG` | START/END 入队后的摘要、超时断开前和断开回调时的摘要 | 汇总入队成功/失败数、音频载荷字节、文本/二进制消息数、ACK 次数、末次收包距今时长和回复等待时长 |
拾音统计按一次录音累计,每秒及停止时输出。`peak`/`mean_abs` 是 16 位有符号 PCM 的绝对幅值,不是分贝;`zero` 统计有效读取长度内全部为零的帧。`short` 包括零长度以及所有非 640 字节的读取。PCM 写入不足或失败计入 `ring_drops`,同时记录丢失字节数。录音 tick 回调不打印,其他重复事件限频。
`VOICE-TX ret=0` 只代表入队成功。`queue_bytes` 只计成功入队的音频载荷,不含自定义包头、WebSocket 帧头、认证和控制消息。`WSS-IO tx_bytes` 是传给传输层并成功返回的明文字节数,含握手/帧头,不能与 `queue_bytes` 直接比较;TLS 失败时已写出的部分字节由 `tls_write_failed`/`tls_write_timeout` 的 `sent` 单独报告。正常写入首条和每 50 次打印一次,实际接收前 20 次及每 50 次打印,断开时输出累计值。`tick`/`elapsed_ticks` 使用 RTOS tick,其他带 `ms` 的字段使用单调毫秒。
`start_ack`/`end_ack` 按收到的文本子串统计,包含被准入检查拒绝的消息;它们不是对当前 session 的服务端确认保证。应结合 `VOICE-RX` 原文中的会话标识判断归属。若服务端协议不发送 ACK,ACK 为零本身不代表失败。`accepted=1` 表示通过外层会话准入检查,后续仍可能因 OTA、打断状态、URL 或 session 校验而未播放。文本仅保留前 192 字节,原有错误消息完整分块日志继续保留。
复现时保留开机到失败后第二次对话结束的完整串口日志。推荐按以下顺序判断:
1. `MIC-DIAG` 检查读取长度、返回值和电平,结合既有 FVAD 语音确认日志判断拾音与 VAD。
2. `VOICE-TX` 检查 START、音频、END 入队;`queue_fail` 和 `ring_drops` 应为零。
3. `WSS-IO` 检查实际写入是否失败,关闭时是否有接收字节和残留未消费数据。
4. `VOICE-RX` 检查 ACK、未知错误文本、被拒绝的回复或二进制数据。
5. `timeout-before-disconnect` 检查 `wait_ms`。`wait=2` 是等待回复,`wait=1` 是无语音,`wait=3` 是 END 入队失败。`response` 表示现有业务逻辑已接收可用音频 URL,不等同于收到任意文本或 TTS_START。
即使 TLS 写入成功,也不能证明服务器应用收到完整录音。若已经看到归属正确的 END_ACK,却没有后续 TTS,优先按 session 查服务端识别和生成链路;若没有 ACK,也需要结合服务端接收记录判断。设备单侧日志不能完全区分服务端未处理、网络中断和回复延迟。
本机验证:`python3 tools/test_voice_diag.py`、`python3 tools/test_conversation_timeout.py`、`python3 tools/test_wss_io.py`、`python3 tools/test_wss_receive.py`。诊断测试直接编译实际函数并注入录音、缓冲区、队列及收包结果;不替代目标固件构建及设备实测。
## GNSS 日志开关
`inc/app_gnss_log.h` 中的 `APP_GNSS_LOG_ENABLED` 默认是 `0`,关闭应用侧全部 GNSS/NMEA 原始输出、解析失败、模式切换、卫星搜索、坐标打印和 GPS 查询提示。需要恢复时可在编译参数中设置 `-DAPP_GNSS_LOG_ENABLED=1`。语音与 NITZ 时间校准诊断继续输出。
GNSS 数据解析、定位上报和硬件开关流程继续运行。原先放在打印参数内的四处 `ql_gnss_close()` 已分离为独立操作,避免禁用日志时跳过关闭定位。`python3 tools/test_gnss_logging.py` 在日志开启和关闭两种配置下验证真实解析代码、无卫星退出、卫星不足退出、搜索超时退出及 FAST 切换 SLOW 的关闭路径。
@@ -82,7 +82,8 @@ SRC_FILES+= \
DP1312EA.c \
mqtt.c \
mqtt_connect_diag.c \
app_time_calibrate.c \
time_diag.c \
app_nitz_test.c \
vad_det.c \
sleep_wake.c \
battery.c \
@@ -6,7 +6,7 @@
#include "ql_func.h"
#include "ql_nw.h"
#include "ql_rtc.h"
#include "app_time_calibrate.h"
#include "app_nitz_test.h"
/* SDK wrapper uses the same PMIC timezone as time(); no libc TZ assumptions. */
extern struct tm *ql_localtime_r(const time_t *, struct tm *);
@@ -14,8 +14,8 @@ extern struct tm *ql_localtime_r(const time_t *, struct tm *);
#define MAX_EPOCH 2147483647LL
#define ATTEMPTS 3
static ql_task_t calibration_task;
static int calibration_started;
static ql_task_t test_task;
static int test_started;
typedef struct {
time_t utc;
@@ -96,7 +96,7 @@ static int get_sample(int attempt, int index, nitz_sample *sample)
memset(&info, 0, sizeof(info));
rc = ql_nw_get_nitz_time_info(&info);
sample->ticks = ql_rtos_get_systicks();
printf("[APP-TIME] sample attempt=%d index=%d rc=%d raw=\"%.*s\" "
printf("[APP-NITZ-TEST] sample attempt=%d index=%d rc=%d raw=\"%.*s\" "
"abs_time=%lu leap_sec=%u query_ms=%lu\r\n", attempt, index, rc,
(int)sizeof(info.nitz_time), info.nitz_time, info.abs_time,
(unsigned int)info.leap_sec,
@@ -105,15 +105,15 @@ static int get_sample(int attempt, int index, nitz_sample *sample)
memchr(info.nitz_time, '\0', sizeof(info.nitz_time)) == NULL) return -1;
parsed = parse_nitz(info.nitz_time, &sample->utc, &sample->zone_quarters);
if (parsed != 0) {
printf("[APP-TIME] rejected stage=parse reason=%d\r\n", parsed);
printf("[APP-NITZ-TEST] rejected stage=parse reason=%d\r\n", parsed);
return -1;
}
/* Bound query delay; this does not establish the age of a cached NITZ. */
if (sample->ticks - began > 400U) {
printf("[APP-TIME] rejected stage=slow_query rtc_not_written=1\r\n");
printf("[APP-NITZ-TEST] rejected stage=slow_query rtc_not_written=1\r\n");
return -1;
}
printf("[APP-TIME] parsed utc=%ld zone_quarters=%d zone_minutes=%d\r\n",
printf("[APP-NITZ-TEST] parsed utc=%ld zone_quarters=%d zone_minutes=%d\r\n",
(long)sample->utc, sample->zone_quarters, sample->zone_quarters * 15);
return 0;
}
@@ -128,7 +128,7 @@ static int write_and_verify(const nitz_sample *reference)
expected = (long long)reference->utc +
(ql_rtos_get_systicks() - reference->ticks) / 200U;
if (expected > MAX_EPOCH) {
printf("[APP-TIME] FAIL stage=epoch_range\r\n");
printf("[APP-NITZ-TEST] FAIL stage=epoch_range\r\n");
return -1;
}
epoch = (time_t)expected;
@@ -138,7 +138,7 @@ static int write_and_verify(const nitz_sample *reference)
local.tm_hour, local.tm_min, local.tm_sec, &local_epoch) != 0 ||
local_epoch < MIN_EPOCH || local_epoch > MAX_EPOCH ||
local_epoch - expected < -43200 || local_epoch - expected > 50400) {
printf("[APP-TIME] FAIL stage=local_calendar\r\n");
printf("[APP-NITZ-TEST] FAIL stage=local_calendar\r\n");
return -1;
}
memset(&target, 0, sizeof(target));
@@ -149,7 +149,7 @@ static int write_and_verify(const nitz_sample *reference)
target.tm_min = local.tm_min;
target.tm_sec = local.tm_sec;
target.tm_wday = local.tm_wday;
printf("[APP-TIME] set_begin app_before=%ld expected_utc=%ld "
printf("[APP-NITZ-TEST] set_begin app_before=%ld expected_utc=%ld "
"pmic_zone_minutes=%ld app_calendar=%04d-%02d-%02dT%02d:%02d:%02d\r\n",
(long)before, (long)epoch, (long)((local_epoch - expected) / 60),
target.tm_year, target.tm_mon, target.tm_mday,
@@ -165,40 +165,40 @@ static int write_and_verify(const nitz_sample *reference)
actual.tm_hour, actual.tm_min, actual.tm_sec,
&actual_local) == 0;
actual_local -= local_epoch + (expected - (long long)epoch);
printf("[APP-TIME] readback set_rc=%d rtc_rc=%d app_after=%ld "
printf("[APP-NITZ-TEST] readback set_rc=%d rtc_rc=%d app_after=%ld "
"expected_utc=%lld delta_s=%lld calendar=%04d-%02d-%02dT%02d:%02d:%02d\r\n",
rc, read_rc, (long)after, expected, delta, actual.tm_year, actual.tm_mon,
actual.tm_mday, actual.tm_hour, actual.tm_min, actual.tm_sec);
if (rc != 0 || read_rc != 0 || after < MIN_EPOCH ||
delta < -3 || delta > 3 || !calendar_ok ||
actual_local < -3 || actual_local > 3) {
printf("[APP-TIME] FAIL stage=app_clock_verification\r\n");
printf("[APP-NITZ-TEST] FAIL stage=app_clock_verification\r\n");
return -1;
}
printf("[APP-TIME] PASS source=nitz app_utc=verified "
printf("[APP-NITZ-TEST] PASS source=nitz app_utc=verified "
"wss_requires_separate_handshake_check=1\r\n");
return 0;
}
static void time_calibrate_worker(void *arg)
static void nitz_test_worker(void *arg)
{
nitz_sample sample;
int attempt;
(void)arg;
printf("[APP-TIME] begin version=1 source=operator_nitz ntp=off "
printf("[APP-NITZ-TEST] begin version=3 source=operator_nitz ntp=off "
"policy=first_valid_after_pdp sys_probe=off\r\n");
if (!m_get_api_ptr || !m_get_api_ptr("ql_nw_get_nitz_time_info") ||
!m_get_api_ptr("ql_localtime_r") || !m_get_api_ptr("ql_rtc_set_time") ||
!m_get_api_ptr("ql_rtc_get_time")) {
printf("[APP-TIME] FAIL stage=sdk_api_unavailable\r\n");
printf("[APP-NITZ-TEST] FAIL stage=sdk_api_unavailable\r\n");
goto done;
}
/* Use the first valid operator snapshot promptly after
/* Device experiment: use the first valid operator snapshot promptly after
* PDP activation. A snapshot need not advance between API reads. Its actual
* network reception time is unknown; readback alone cannot establish age. */
for (attempt = 1; attempt <= ATTEMPTS; ++attempt) {
if (get_sample(attempt, 1, &sample) == 0) {
printf("[APP-TIME] snapshot_selected attempt=%d utc=%ld "
printf("[APP-NITZ-TEST] snapshot_selected attempt=%d utc=%ld "
"age=unknown first_valid_after_pdp=1 max_rtc_writes=1\r\n",
attempt, (long)sample.utc);
write_and_verify(&sample);
@@ -206,21 +206,21 @@ static void time_calibrate_worker(void *arg)
}
if (attempt < ATTEMPTS) ql_rtos_task_sleep_ms(5000);
}
printf("[APP-TIME] FAIL stage=no_valid_nitz rtc_not_written=1\r\n");
printf("[APP-NITZ-TEST] FAIL stage=no_valid_nitz rtc_not_written=1\r\n");
done:
printf("[APP-TIME] finished once_per_boot=1\r\n");
printf("[APP-NITZ-TEST] finished one_test_per_boot=1\r\n");
ql_rtos_task_delete(NULL);
}
void app_time_calibrate_start(void)
void app_nitz_test_start(void)
{
int rc;
if (calibration_started) return; /* PDP management is the sole caller. */
calibration_started = 1;
rc = ql_rtos_task_create(&calibration_task, 6 * 1024, 110, "app_time_calibrate",
time_calibrate_worker, NULL);
if (test_started) return; /* PDP management is the sole caller. */
test_started = 1;
rc = ql_rtos_task_create(&test_task, 6 * 1024, 110, "app_nitz_test",
nitz_test_worker, NULL);
if (rc != 0) {
calibration_started = 0;
printf("[APP-TIME] FAIL stage=create_task rc=%d\r\n", rc);
test_started = 0;
printf("[APP-NITZ-TEST] FAIL stage=create_task rc=%d\r\n", rc);
}
}
@@ -0,0 +1,229 @@
#include <stdio.h>
#include <string.h>
#include <stdint.h>
#include <time.h>
#include "ql_rtos.h"
#include "ql_func.h"
#include "ql_ntp.h"
#include "ql_rtc.h"
#include "netdb.h"
#include "sockets.h"
#include "app_ntp_test.h"
/* Use the existing GNSS NTP provider first; do not assume it is whitelisted. */
#ifndef APP_NTP_TEST_SERVER
#define APP_NTP_TEST_SERVER "ntp.aliyun.com"
#endif
#define NTP_PACKET_SIZE 48
#define NTP_EPOCH_DELTA 2208988800ULL
#define NTP_MIN_UNIX 1704067200ULL
#define NTP_MAX_UNIX 2147483647ULL
#define NTP_WAIT_MS 5000U
static ql_task_t test_task;
static ql_sem_t callback_done;
static volatile int callback_status = -1;
static int test_started;
static unsigned int read_u32(const unsigned char *p)
{
return ((unsigned int)p[0] << 24) | ((unsigned int)p[1] << 16) |
((unsigned int)p[2] << 8) | p[3];
}
static void write_u32(unsigned char *p, unsigned int value)
{
p[0] = (unsigned char)(value >> 24);
p[1] = (unsigned char)(value >> 16);
p[2] = (unsigned char)(value >> 8);
p[3] = (unsigned char)value;
}
/* Validate the response to our connected UDP socket, including request echo.
* This is a reachability/clock test, not authenticated NTP. Handle era 1
* (2036 rollover), but reject epochs outside the target's signed time range. */
static int parse_ntp_reply(const unsigned char *reply, unsigned int length,
const unsigned char *request, unsigned int *epoch)
{
unsigned int version;
unsigned long long seconds;
if (length < NTP_PACKET_SIZE) return -1;
version = (reply[0] >> 3) & 7U;
if ((reply[0] >> 6) == 3 || (version != 3 && version != 4) ||
(reply[0] & 7U) != 4 || reply[1] == 0 || reply[1] > 15) return -2;
if (memcmp(reply + 24, request + 40, 8) != 0) return -3;
if (read_u32(reply + 32) == 0 && read_u32(reply + 36) == 0) return -4;
seconds = read_u32(reply + 40);
if (seconds == 0 && read_u32(reply + 44) == 0) return -4;
if (seconds < NTP_EPOCH_DELTA) seconds += 0x100000000ULL;
seconds -= NTP_EPOCH_DELTA;
if (seconds < NTP_MIN_UNIX || seconds > NTP_MAX_UNIX) return -5;
*epoch = (unsigned int)seconds;
return 0;
}
static int probe_ntp(unsigned int *epoch, unsigned int *sample_ticks)
{
struct addrinfo hints, *addresses = NULL;
unsigned char request[NTP_PACKET_SIZE] = {0}, reply[96];
unsigned int began, elapsed, token;
char ip[48] = {0};
int fd = -1, ret, received;
fd_set readers;
struct timeval timeout;
memset(&hints, 0, sizeof(hints));
hints.ai_family = AF_INET;
hints.ai_socktype = SOCK_DGRAM;
hints.ai_protocol = IPPROTO_UDP;
printf("[APP-NTP-TEST] begin server=%s port=123 protocol=UDP pdp=1\r\n",
APP_NTP_TEST_SERVER);
ret = getaddrinfo_with_pcid(APP_NTP_TEST_SERVER, "123", &hints, &addresses, 1);
if (ret != 0 || addresses == NULL) {
printf("[APP-NTP-TEST] FAIL stage=dns rc=%d\r\n", ret);
if (addresses) freeaddrinfo(addresses);
return -1;
}
inet_ntop(AF_INET, &((struct sockaddr_in *)addresses->ai_addr)->sin_addr,
ip, sizeof(ip));
printf("[APP-NTP-TEST] dns_ok ip=%s response_not_yet_verified=1\r\n", ip);
fd = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP);
if (fd < 0 || fd >= FD_SETSIZE) {
printf("[APP-NTP-TEST] FAIL stage=socket fd=%d\r\n", fd);
goto failed;
}
ret = connect(fd, addresses->ai_addr, addresses->ai_addrlen);
if (ret != 0) {
printf("[APP-NTP-TEST] FAIL stage=udp_connect rc=%d\r\n", ret);
goto failed;
}
request[0] = (4U << 3) | 3U; /* NTP v4 client. */
token = ql_rtos_get_systicks();
write_u32(request + 40, 0xe0000000U ^ token);
write_u32(request + 44, token ^ 0x6e747031U);
began = ql_rtos_get_systicks();
ret = send(fd, request, sizeof(request), 0);
if (ret != (int)sizeof(request)) {
printf("[APP-NTP-TEST] FAIL stage=send rc=%d\r\n", ret);
goto failed;
}
/* Keep one deadline even if an unrelated or invalid packet arrives. */
while ((elapsed = (ql_rtos_get_systicks() - began) * 5U) < NTP_WAIT_MS) {
timeout.tv_sec = (NTP_WAIT_MS - elapsed) / 1000;
timeout.tv_usec = ((NTP_WAIT_MS - elapsed) % 1000) * 1000;
FD_ZERO(&readers);
FD_SET(fd, &readers);
ret = select(fd + 1, &readers, NULL, NULL, &timeout);
if (ret <= 0) {
printf("[APP-NTP-TEST] FAIL stage=receive_wait rc=%d "
"cause=undetermined_check_whitelist_udp123_and_server\r\n", ret);
goto failed;
}
received = recv(fd, reply, sizeof(reply), 0);
if (received <= 0) {
printf("[APP-NTP-TEST] FAIL stage=receive rc=%d\r\n", received);
goto failed;
}
ret = parse_ntp_reply(reply, (unsigned int)received, request, epoch);
if (ret != 0) {
printf("[APP-NTP-TEST] rejected_reply bytes=%d reason=%d\r\n", received, ret);
continue;
}
*sample_ticks = ql_rtos_get_systicks();
printf("[APP-NTP-TEST] UDP_RESPONSE_VERIFIED ip=%s epoch=%lu rtt_ms=%lu\r\n",
ip, (unsigned long)*epoch,
(unsigned long)(*sample_ticks - began) * 5UL);
close(fd);
freeaddrinfo(addresses);
return 0;
}
printf("[APP-NTP-TEST] FAIL stage=valid_response_timeout\r\n");
failed:
if (fd >= 0) close(fd);
freeaddrinfo(addresses);
return -1;
}
static void ntp_callback(int status)
{
callback_status = status;
/* This semaphore lives for the boot, including callbacks after timeout. */
ql_rtos_semaphore_release(callback_done);
}
static void ntp_test_worker(void *arg)
{
unsigned int reference, reference_ticks, now_ticks, expected;
time_t before, after;
ql_rtc_time_t calendar;
long long delta;
int ret, calendar_ret;
(void)arg;
if (probe_ntp(&reference, &reference_ticks) != 0) goto done;
if (!m_get_api_ptr || !m_get_api_ptr("ql_ntp_set_server") ||
!m_get_api_ptr("ql_ntp_set_cid") || !m_get_api_ptr("ql_ntp_sync_ex")) {
printf("[APP-NTP-TEST] FAIL stage=sdk_api_unavailable udp_verified=1\r\n");
goto done;
}
ret = ql_rtos_semaphore_create(&callback_done, 0);
if (ret != 0) {
printf("[APP-NTP-TEST] FAIL stage=semaphore rc=%d\r\n", ret);
goto done;
}
ret = ql_ntp_set_server(APP_NTP_TEST_SERVER);
if (ret != QL_NTP_SUCCESS) {
printf("[APP-NTP-TEST] FAIL stage=sdk_server rc=%d\r\n", ret);
goto done;
}
ret = ql_ntp_set_cid(1);
if (ret != QL_NTP_SUCCESS) {
printf("[APP-NTP-TEST] FAIL stage=sdk_cid rc=%d\r\n", ret);
goto done;
}
before = time(NULL);
printf("[APP-NTP-TEST] sdk_sync_begin app_before=%ld\r\n", (long)before);
ql_ntp_sync_ex(ntp_callback);
ret = ql_rtos_semaphore_wait(callback_done, 25000);
if (ret != 0) {
printf("[APP-NTP-TEST] FAIL stage=sdk_callback_timeout rc=%d "
"no_new_request_until_reboot=1\r\n", ret);
goto done;
}
after = time(NULL); /* SDK time(&value) writeback is not assumed. */
now_ticks = ql_rtos_get_systicks();
expected = reference + (now_ticks - reference_ticks) / 200U;
delta = (long long)after - expected;
memset(&calendar, 0, sizeof(calendar));
calendar_ret = ql_rtc_get_time(&calendar);
printf("[APP-NTP-TEST] sdk_result callback=%d app_before=%ld app_after=%ld "
"expected=%lu delta_s=%lld rtc_rc=%d\r\n",
callback_status, (long)before, (long)after,
(unsigned long)expected, delta, calendar_ret);
printf("[APP-NTP-TEST] app_calendar=%04d-%02d-%02dT%02d:%02d:%02d\r\n",
calendar.tm_year, calendar.tm_mon, calendar.tm_mday,
calendar.tm_hour, calendar.tm_min, calendar.tm_sec);
if (callback_status == QL_NTP_SUCCESS && calendar_ret == 0 &&
after >= (time_t)NTP_MIN_UNIX && delta >= -6 && delta <= 6) {
printf("[APP-NTP-TEST] PASS udp_response=verified app_utc=verified "
"wss_still_requires_separate_handshake_check=1\r\n");
} else {
printf("[APP-NTP-TEST] FAIL stage=app_clock_verification\r\n");
}
done:
printf("[APP-NTP-TEST] finished one_test_per_boot=1\r\n");
ql_rtos_task_delete(NULL);
}
void app_ntp_test_start(void)
{
int ret;
if (test_started) return; /* Called only by the PDP management task. */
test_started = 1;
ret = ql_rtos_task_create(&test_task, 6 * 1024, 110, "app_ntp_test",
ntp_test_worker, NULL);
if (ret != 0) {
test_started = 0;
printf("[APP-NTP-TEST] FAIL stage=create_task rc=%d\r\n", ret);
}
}
@@ -0,0 +1,121 @@
#include <stdio.h>
#include <stdint.h>
#include <string.h>
#include <time.h>
#include "ql_func.h"
#include "ql_rtos.h"
#include "ql_rtc.h"
#include "ql_atcmd.h"
#include "app_sys_time_probe.h"
/* Exact signatures from the existing kernel pmic_rtc.h/duster.h.
* Lookup only: never add exports, use fixed addresses, or use our diagnostic API.
* SYS_OFFSET is 0 in pmic_rtc.h. Missing exports are an expected result. */
typedef unsigned int (*sys_count_fn)(int);
typedef int (*zone_minutes_fn)(void);
static void probe_sys_exports(void)
{
sys_count_fn get_count;
zone_minutes_fn get_zone;
unsigned int raw[2], ticks[2];
int zone[2], i;
long long utc[2], advance, elapsed, drift;
get_count = (sys_count_fn)(intptr_t)m_get_api_ptr("PMIC_RTC_GetTime_Count");
get_zone = (zone_minutes_fn)(intptr_t)m_get_api_ptr("GetTimeZone");
printf("[APP-SYS-PROBE] exports sys_count=%d network_zone=%d\r\n",
get_count != NULL, get_zone != NULL);
if (!get_count || !get_zone) {
printf("[APP-SYS-PROBE] direct_sys=unavailable no_kernel_change=1\r\n");
return;
}
for (i = 0; i < 2; ++i) {
raw[i] = get_count(0);
zone[i] = get_zone();
ticks[i] = ql_rtos_get_systicks();
utc[i] = (long long)raw[i] - (long long)zone[i] * 60;
printf("[APP-SYS-PROBE] direct index=%d raw=%lu zone_minutes=%d "
"utc=%lld ticks=%lu\r\n", i + 1, (unsigned long)raw[i],
zone[i], utc[i], (unsigned long)ticks[i]);
if (i == 0) ql_rtos_task_sleep_ms(3000);
}
elapsed = (ticks[1] - ticks[0]) / 200U;
advance = utc[1] - utc[0];
drift = advance - elapsed;
if (utc[0] >= 1704067200LL && utc[1] <= 2147483647LL &&
zone[0] >= -720 && zone[0] <= 840 && zone[0] == zone[1] &&
elapsed >= 2 && elapsed <= 10 && advance > 0 && drift >= -1 && drift <= 1) {
printf("[APP-SYS-PROBE] direct_sys=candidate advancing=1 "
"advance_s=%lld elapsed_s=%lld external_utc_check_required=1\r\n",
advance, elapsed);
} else {
printf("[APP-SYS-PROBE] direct_sys=rejected advance_s=%lld "
"elapsed_s=%lld drift_s=%lld\r\n", advance, elapsed, drift);
}
}
static void probe_app_rtc(int round)
{
ql_rtc_time_t calendar;
int rc;
printf("[APP-SYS-PROBE] app round=%d utc=%ld ticks=%lu\r\n",
round, (long)time(NULL), (unsigned long)ql_rtos_get_systicks());
if (!m_get_api_ptr("ql_rtc_get_time_ex")) {
printf("[APP-SYS-PROBE] rtc_ex=unavailable\r\n");
return;
}
memset(&calendar, 0, sizeof(calendar));
rc = ql_rtc_get_time_ex(&calendar);
printf("[APP-SYS-PROBE] rtc_ex round=%d rc=%d calendar="
"%04d-%02d-%02dT%02d:%02d:%02d source_not_assumed=1\r\n",
round, rc, calendar.tm_year, calendar.tm_mon, calendar.tm_mday,
calendar.tm_hour, calendar.tm_min, calendar.tm_sec);
}
static void probe_at(int round, char *command, const char *label)
{
char response[256];
unsigned int began = ql_rtos_get_systicks(), ended;
int rc, terminated;
size_t i;
memset(response, 0, sizeof(response));
rc = ql_atcmd_send_sync(command, response, sizeof(response), NULL, 2);
ended = ql_rtos_get_systicks();
terminated = memchr(response, '\0', sizeof(response)) != NULL;
response[sizeof(response) - 1] = '\0';
for (i = 0; response[i]; ++i)
if ((unsigned char)response[i] < 32 || response[i] == '"') response[i] = ' ';
printf("[APP-SYS-PROBE] at round=%d query=%s rc=%d duration_ms=%lu "
"ticks=%lu terminated=%d raw=\"%s\"\r\n", round, label, rc,
(unsigned long)(ended - began) * 5UL, (unsigned long)ended,
terminated, response);
}
void app_sys_time_probe_run(void)
{
int round;
printf("[APP-SYS-PROBE] begin version=1 read_only=1 kernel_diag_used=0\r\n");
if (!m_get_api_ptr) {
printf("[APP-SYS-PROBE] FAIL stage=api_lookup_unavailable\r\n");
goto done;
}
probe_sys_exports();
if (!m_get_api_ptr("ql_atcmd_send_sync")) {
printf("[APP-SYS-PROBE] at=unavailable\r\n");
goto done;
}
for (round = 1; round <= 2; ++round) {
probe_app_rtc(round);
/* QLTS support/semantics on this model must be established on-device.
* Mode 0 may be a snapshot; mode 1/2 on supported models advances.
* CCLK is comparison only; it may expose the same broken APP RTC. */
probe_at(round, "AT+QLTS=0\r\n", "QLTS_0");
probe_at(round, "AT+QLTS=1\r\n", "QLTS_1");
probe_at(round, "AT+QLTS=2\r\n", "QLTS_2");
probe_at(round, "AT+CCLK?\r\n", "CCLK");
if (round == 1) ql_rtos_task_sleep_ms(3000);
}
done:
printf("[APP-SYS-PROBE] finished rtc_not_written=1\r\n");
}
@@ -30,6 +30,7 @@
#include "hal_api.h"
#include "nfc.h"
#include "ota_baseline.h"
#include "time_diag.h"
#define APP_ENABLE_SLEEP
@@ -147,6 +148,7 @@ static void custom_app_task(void * argv)
hal_os_get_build_time(tempbuf);
printf("BUILD TIME:%s\r\n",tempbuf);
talkingq_time_diag_boot();
printf("[OTA-BASELINE] layout=%s profile=%s app_phys=0x%08lX app_xip=0x%08lX app_size=%lu fs_size=%lu slot=single\r\n",
BANBAN_OTA_LAYOUT_ID,
BANBAN_OTA_BUILD_PROFILE,
@@ -15,7 +15,6 @@
#include "sockets.h"
#include "netdb.h"
#include "app_inc.h"
#include "app_gnss_log.h"
// 新增宏定义(原代码中未定义)
#define GNSS_FAST_MODE_HOLD_TIME (5 * 60 * 1000) // FAST模式持续5分钟后切SLOW
@@ -212,7 +211,10 @@ void quec_gnss_callback(char *data, int len)
char nmea_buffer[512];
int fix_status = 0;
APP_GNSS_LOG("%.*s", len, data);
// 打印原始 NMEA 数据
for (i = 0; i < len; i++) {
printf("%c", data[i]);
}
// 在 data 中定位 GGA 句子的起止位置
// GGA 格式: $xxGGA,...,*CS\r\n
@@ -265,7 +267,7 @@ void quec_gnss_callback(char *data, int len)
g_gps_location.is_valid = (fix_status >= 1) ? 1 : 0;
} else {
g_gps_location.is_valid = 0;
APP_GNSS_LOG("\n[GNSS Parse failed] FS=%d, %s\n",
printf("\n[GNSS Parse failed] FS=%d, %s\n",
fix_status,
(fix_status == 0) ? "No Fix (Invalid)" : "Format error");
}
@@ -319,7 +321,7 @@ void quec_gnss_callback(char *data, int len)
void set_gnss_mode(gnss_mode_t mode)
{
g_gnss_mode = mode;
APP_GNSS_LOG("[GNSSMode] Set to %s Mode\n",
printf("[GNSSMode] Set to %s Mode\n",
(mode == GNSS_MODE_FAST) ? "Fast (30s)" : "Slow (5min)");
}
@@ -351,11 +353,11 @@ gps_location_info_t* get_gps_location_info(void)
void print_gps_location_info(void)
{
if (g_gps_location.is_valid) {
APP_GNSS_LOG("[GPSinfo] Latitude: %.8f, Longitude: %.8f\n",
printf("[GPSinfo] Latitude: %.8f, Longitude: %.8f\n",
g_gps_location.latitude, g_gps_location.longitude);
APP_GNSS_LOG("[GPSinfo] rawNMEA: %s\n", g_gps_location.raw_nmea);
printf("[GPSinfo] rawNMEA: %s\n", g_gps_location.raw_nmea);
} else {
APP_GNSS_LOG("[GPSinfo] No valid position data\n");
printf("[GPSinfo] No valid position data\n");
}
}
@@ -393,29 +395,26 @@ static void quec_gnss_test(void * argv)
/* ========== FAST MODE ========== */
if (fast_mode_start_time == 0) {
fast_mode_start_time = hal_os_up_time_ms();
APP_GNSS_LOG("[GNSS] Enter FAST mode\n");
printf("[GNSS] Enter FAST mode\n");
}
if (!gnss_is_open) {
gnss_is_open = 1;
app_set_app_operation(1);
ql_gnss_open(quec_gnss_callback);
APP_GNSS_LOG("[GNSS] FAST: GPS OPEN\n");
printf("[GNSS] FAST: GPS OPEN\n");
}
// 5分钟超时切SLOW
if (hal_os_up_time_ms() - fast_mode_start_time >= GNSS_FAST_MODE_HOLD_TIME) {
APP_GNSS_LOG("[GNSS] FAST 5min timeout -> SLOW\n");
printf("[GNSS] FAST 5min timeout -> SLOW\n");
g_gnss_mode = GNSS_MODE_SLOW;
fast_mode_start_time = 0;
slow_mode_start_time = 0;
slow_next_wake_time = 0;
gnss_is_open = 0;
int close_ret = ql_gnss_close();
(void)close_ret;
APP_GNSS_LOG("[GNSS] SLOW: GPS CLOSED ret:%d\n", close_ret);
printf("[GNSS] SLOW: GPS CLOSED ret:%d\n", ql_gnss_close());
}
}
else
@@ -427,12 +426,12 @@ static void quec_gnss_test(void * argv)
if (slow_mode_start_time == 0) {
slow_mode_start_time = now;
slow_next_wake_time = now; // 立即尝试第一次定位
APP_GNSS_LOG("[GNSS] Enter SLOW mode (fixed 15min interval)\n");
printf("[GNSS] Enter SLOW mode (fixed 15min interval)\n");
}
// 检查是否到达唤醒时间
if (now < slow_next_wake_time) {
APP_GNSS_LOG("[GNSS] SLOW: next wake in %d s\n",
printf("[GNSS] SLOW: next wake in %d s\n",
(slow_next_wake_time - now) / 1000);
}
else if (!gnss_is_open) {
@@ -445,7 +444,7 @@ static void quec_gnss_test(void * argv)
app_set_app_operation(1);
ql_gnss_open(quec_gnss_callback);
APP_GNSS_LOG("[GNSS] SLOW: GPS OPEN, search start\n");
printf("[GNSS] SLOW: GPS OPEN, search start\n");
/* ---- 第一阶段:60秒卫星检测 ---- */
int sat_found = 0;
@@ -455,7 +454,7 @@ static void quec_gnss_test(void * argv)
elapsed++;
if (g_gnss_sat_count > 1) {
sat_found = 1;
APP_GNSS_LOG("[GNSS] SLOW: Sat found! count=%d, after %ds\n",
printf("[GNSS] SLOW: Sat found! count=%d, after %ds\n",
g_gnss_sat_count, elapsed);
break;
}
@@ -463,13 +462,10 @@ static void quec_gnss_test(void * argv)
if (!sat_found) {
// 60秒无卫星 → 立即关闭,等待15分钟
int close_ret = ql_gnss_close();
(void)close_ret;
APP_GNSS_LOG("[GNSS] SLOW: No satellite in 30s, stop ret:%d.\n", close_ret);
printf("[GNSS] SLOW: No satellite in 30s, stop ret:%d.\n", ql_gnss_close());
gnss_is_open = 0;
slow_next_wake_time = search_start_time + (GNSS_SEARCH_INTERVAL * 1000);
APP_GNSS_LOG("[GNSS] SLOW: GPS CLOSED, next search at +%ds\n", GNSS_SEARCH_INTERVAL);
printf("[GNSS] SLOW: GPS CLOSED, next search at +%ds\n", GNSS_SEARCH_INTERVAL);
continue;
}
@@ -482,20 +478,17 @@ static void quec_gnss_test(void * argv)
// 1.5分钟时刻检查卫星数
int sat_count_now = g_gnss_sat_count;
APP_GNSS_LOG("[GNSS] SLOW: 1min check, satellite count = %d\n", sat_count_now);
printf("[GNSS] SLOW: 1min check, satellite count = %d\n", sat_count_now);
if (sat_count_now < GNSS_SAT_MIN_COUNT) {
int close_ret = ql_gnss_close();
(void)close_ret;
APP_GNSS_LOG("[GNSS] SLOW: Sat count < %d, stop search ret:%d.\n", GNSS_SAT_MIN_COUNT, close_ret);
printf("[GNSS] SLOW: Sat count < %d, stop search ret:%d.\n", GNSS_SAT_MIN_COUNT,ql_gnss_close());
gnss_is_open = 0;
slow_next_wake_time = search_start_time + (GNSS_SEARCH_INTERVAL * 1000);
APP_GNSS_LOG("[GNSS] SLOW: GPS CLOSED, next search at +%ds\n", GNSS_SEARCH_INTERVAL);
printf("[GNSS] SLOW: GPS CLOSED, next search at +%ds\n", GNSS_SEARCH_INTERVAL);
continue;
}
/* ---- 第三阶段:卫星足够,继续定位,但总时间不超过3分钟 ---- */
APP_GNSS_LOG("[GNSS] SLOW: Sat count OK, continue for fix (max %ds total)\n",
printf("[GNSS] SLOW: Sat count OK, continue for fix (max %ds total)\n",
GNSS_MAX_SEARCH_TIME);
// 继续等待直到总时间达到 GNSS_MAX_SEARCH_TIME (180秒)
@@ -505,30 +498,28 @@ static void quec_gnss_test(void * argv)
// 如果获得有效定位,打印提示(但继续等待至3分钟)
if (g_gps_location.is_valid) {
APP_GNSS_LOG("[GNSS] SLOW: Fix obtained at %ds!\n", elapsed);
printf("[GNSS] SLOW: Fix obtained at %ds!\n", elapsed);
print_gps_location_info();
// 若希望在定位成功后立即关闭,可在此 break,但此处按“最长3分钟”处理
}
}
// 3分钟时间到,关闭GPS
APP_GNSS_LOG("[GNSS] SLOW: Max search time reached, stop.\n");
printf("[GNSS] SLOW: Max search time reached, stop.\n");
if (g_gps_location.is_valid) {
APP_GNSS_LOG("[GNSS] SLOW: Final valid position:\n");
printf("[GNSS] SLOW: Final valid position:\n");
print_gps_location_info();
} else {
APP_GNSS_LOG("[GNSS] SLOW: No valid fix within 3min.\n");
printf("[GNSS] SLOW: No valid fix within 3min.\n");
}
int close_ret = ql_gnss_close();
(void)close_ret;
APP_GNSS_LOG("[GNSS] SLOW: GPS CLOSED ret:%d", close_ret);
printf("[GNSS] SLOW: GPS CLOSED ret:%d",ql_gnss_close());
gnss_is_open = 0;
g_gps_location.is_valid = 0; // 重置状态
// 设置下次唤醒时间为 15分钟 后
slow_next_wake_time = search_start_time + (GNSS_SEARCH_INTERVAL * 1000);
APP_GNSS_LOG("[GNSS] SLOW: GPS CLOSED, next search at +%ds\n", GNSS_SEARCH_INTERVAL);
printf("[GNSS] SLOW: GPS CLOSED, next search at +%ds\n", GNSS_SEARCH_INTERVAL);
}
// else: GPS正在运行中(理论上不会到这里)
}
@@ -1,18 +0,0 @@
#ifndef APP_GNSS_LOG_H
#define APP_GNSS_LOG_H
/* GNSS diagnostics are quiet by default while investigating voice replies.
* Define APP_GNSS_LOG_ENABLED=1 at build time to restore application logs.
* Keep state changes and hardware operations outside log arguments. */
#ifndef APP_GNSS_LOG_ENABLED
#define APP_GNSS_LOG_ENABLED 0
#endif
#if APP_GNSS_LOG_ENABLED
#include <stdio.h>
#define APP_GNSS_LOG(...) printf(__VA_ARGS__)
#else
#define APP_GNSS_LOG(...) ((void)0)
#endif
#endif
@@ -0,0 +1,6 @@
#ifndef APP_NITZ_TEST_H
#define APP_NITZ_TEST_H
void app_nitz_test_start(void);
#endif
@@ -0,0 +1,7 @@
#ifndef TALKINGQ_APP_NTP_TEST_H
#define TALKINGQ_APP_NTP_TEST_H
/* Test firmware: one asynchronous test per boot, after PDP activation. */
void app_ntp_test_start(void);
#endif
@@ -0,0 +1,7 @@
#ifndef APP_SYS_TIME_PROBE_H
#define APP_SYS_TIME_PROBE_H
/* Run in the existing time-test worker; read only, no new kernel dependency. */
void app_sys_time_probe_run(void);
#endif
@@ -1,6 +0,0 @@
#ifndef APP_TIME_CALIBRATE_H
#define APP_TIME_CALIBRATE_H
void app_time_calibrate_start(void);
#endif
@@ -0,0 +1,12 @@
#ifndef TALKINGQ_APP_TIME_DIAG_H
#define TALKINGQ_APP_TIME_DIAG_H
#include <time.h>
void talkingq_time_diag_boot(void);
/* Raw kernel snapshot only: safe to call before the application's first RTC read. */
void talkingq_time_diag_log(const char *phase);
/* Returns the original time(NULL) sample for the existing WSS threshold check. */
time_t talkingq_time_diag_wss_time(void);
#endif
@@ -96,9 +96,6 @@ typedef struct _rws_socket_struct
int command;
unsigned int next_message_id;
/* Worker-owned transport counters; socket creation zero-initializes these. */
unsigned int diag_tx_calls, diag_tx_bytes, diag_tx_failed;
unsigned int diag_rx_calls, diag_rx_bytes;
rws_bool is_connected; // sock connected + handshake done
@@ -28,6 +28,7 @@
#include "sockets.h"
#include "ql_rtos.h"
#include <time.h>
#include "time_diag.h"
#include "rws_handshake.h"
#ifdef LIBRWS_USING_MBED_TLS
#include "mbedtls/base64.h"
@@ -193,30 +194,17 @@ static int rws_tls_send_all(_rws_socket *s, const unsigned char *data, size_t si
{
size_t sent = 0;
unsigned int started = ql_rtos_get_systicks();
unsigned int retries = 0;
if (!s->ssl) {
printf("[WSS-IO] tls_write_no_context requested=%u\n", (unsigned int)size);
return -1;
}
if (!s->ssl) return -1;
while (sent < size) {
int ret = mbedtls_ssl_write(&s->ssl->ssl_ctx, data + sent, size - sent);
if (ret > 0) {
sent += (size_t)ret;
} else if (ret != MBEDTLS_ERR_SSL_WANT_READ && ret != MBEDTLS_ERR_SSL_WANT_WRITE) {
printf("[WSS-IO] tls_write_failed ret=%d sent=%u requested=%u retries=%u elapsed_ticks=%u\n",
ret, (unsigned int)sent, (unsigned int)size, retries,
(unsigned int)(ql_rtos_get_systicks() - started));
return -1;
} else {
retries++;
rws_thread_sleep(5);
}
if (sent < size && (unsigned int)(ql_rtos_get_systicks() - started) >= 1000) {
printf("[WSS-IO] tls_write_timeout last_ret=%d sent=%u requested=%u retries=%u elapsed_ticks=%u\n",
ret, (unsigned int)sent, (unsigned int)size, retries,
(unsigned int)(ql_rtos_get_systicks() - started));
return -1;
}
if (sent < size && (unsigned int)(ql_rtos_get_systicks() - started) >= 1000) return -1;
}
return (int)sent;
}
@@ -231,6 +219,7 @@ rws_bool rws_socket_send(_rws_socket *s, const void *data, const size_t data_siz
#ifdef LIBRWS_USING_MBED_TLS
if (s->scheme && strcmp(s->scheme, "wss") == 0)
{
LOG_D("[T:%.8d, L:%.5d, %*.*s] try send data with tls", ql_rtos_get_systicks(), __LINE__, DBG_FUNCTION_NUM, DBG_FUNCTION_NUM, __FUNCTION__);
sended = rws_tls_send_all(s, (const unsigned char *)data, data_size);
}
else
@@ -255,14 +244,6 @@ rws_bool rws_socket_send(_rws_socket *s, const void *data, const size_t data_siz
}
#endif
s->diag_tx_calls++;
if (sended > 0) s->diag_tx_bytes += (unsigned int)sended;
if (sended != (int)data_size) s->diag_tx_failed++;
if (s->diag_tx_calls == 1 || s->diag_tx_calls % 50 == 0 || sended != (int)data_size) {
printf("[WSS-IO] write tick=%u requested=%u ret=%d calls=%u bytes=%u failed=%u\n",
ql_rtos_get_systicks(), (unsigned int)data_size, sended,
s->diag_tx_calls, s->diag_tx_bytes, s->diag_tx_failed);
}
if (sended == (int)data_size)
{
rws_socket_error_cnt = 0;
@@ -303,6 +284,7 @@ rws_bool rws_socket_recv(_rws_socket *s)
rws_bool result = rws_true;
int is_reading = 1, error_number = -1, len = -1;
char *received = NULL;
size_t total_len = 0;
// char *buff = 0;
// LOG_D("[T:%.8d, L:%.5d, %*.*s] receive data", ql_rtos_get_systicks(), __LINE__, DBG_FUNCTION_NUM, DBG_FUNCTION_NUM, __FUNCTION__);
@@ -384,12 +366,7 @@ rws_bool rws_socket_recv(_rws_socket *s)
if (len > 0)
{
s->diag_rx_calls++;
s->diag_rx_bytes += (unsigned int)len;
if (s->diag_rx_calls <= 20 || s->diag_rx_calls % 50 == 0)
printf("[WSS-IO] read tick=%u bytes=%d calls=%u total=%u buffered=%u command=%d\n",
ql_rtos_get_systicks(), len, s->diag_rx_calls, s->diag_rx_bytes,
(unsigned int)s->received_len, s->command);
total_len += len;
if (s->command == COMMAND_WAIT_HANDSHAKE_RESPONCE &&
s->received_len + (size_t)len > RWS_HANDSHAKE_MAX + RWS_RECEIVE_BUFF_SIZE) {
s->error = rws_error_new_code_descr(rws_error_code_parse_handshake, "WebSocket headers too large");
@@ -1674,12 +1651,9 @@ void rws_socket_resize_received(_rws_socket *s, const size_t size)
void rws_socket_close(_rws_socket *s)
{
s->received_len = 0;
if (s->socket != RWS_INVALID_SOCKET)
{
printf("[WSS-IO] close tick=%u tx_calls=%u tx_bytes=%u tx_failed=%u rx_calls=%u rx_bytes=%u buffered=%u command=%d\n",
ql_rtos_get_systicks(), s->diag_tx_calls, s->diag_tx_bytes,
s->diag_tx_failed, s->diag_rx_calls, s->diag_rx_bytes,
(unsigned int)s->received_len, s->command);
#ifdef LIBRWS_USING_MBED_TLS
if (s->scheme && strcmp(s->scheme, "wss") == 0)
{
@@ -1696,7 +1670,6 @@ void rws_socket_close(_rws_socket *s)
WSACleanup();
#endif
}
s->received_len = 0;
s->is_connected = rws_false;
}
@@ -1973,7 +1946,8 @@ int rws_ssl_conn(_rws_socket *s)
LOG_E("WSS requires a trusted root CA\n");
goto exit;
}
if (time(NULL) < 1704067200) {
stage = "check RTC time";
if (talkingq_time_diag_wss_time() < 1704067200) {
LOG_E("WSS requires valid network/RTC time before certificate verification\n");
goto exit;
}
@@ -9,7 +9,6 @@
#include "ql_application.h"
#include "sockets.h"
#include "app_inc.h"
#include "app_gnss_log.h"
#include "cjson.h"
#include "ql_fs.h"
#include "ql_fota.h"
@@ -443,7 +442,7 @@ int parse_command(const char *json_str, command_t *cmd)
if (params != NULL) {
switch (cmd->type) {
case MSG_GPS_QUERY:
APP_GNSS_LOG("MSG_GPS_QUERY\r\n");
printf("MSG_GPS_QUERY\r\n");
/*返回当前的GPS坐标信息*/
break;
@@ -21,7 +21,8 @@
#include "ql_atcmd.h"
#include "http_downfile.h"
#include "ota_post_update.h"
#include "app_time_calibrate.h"
#include "time_diag.h"
#include "app_nitz_test.h"
//#define NTP_SERVER_DOMAIN "ntp.aliyun.com"
@@ -142,7 +143,8 @@ void ql_datacall_demo_thread(void *arvg)
if(ret == 0)
{
app_time_calibrate_start();
talkingq_time_diag_log("pdp-active");
app_nitz_test_start();
ql_get_data_call_info(1, 0, &info);
printf("info.profile_idx: %d\r\n", info.profile_idx);
printf("info.ip_version: %d\r\n", info.ip_version);
@@ -199,6 +201,8 @@ int app_net_mgt_init()
// ql_rtos_semaphore_create(&g_ql_ntp_sem,0);
ql_rtc_set_nitz_mode(1);
printf("[TIME-DIAG] nitz_enabled=1 app_nitz_test=first_valid_after_pdp ntp_test=off sys_probe=off\r\n");
talkingq_time_diag_log("nitz-enabled");
ql_task_t appTaskRef = NULL;
ql_rtos_task_create(&appTaskRef, 1024*4, 100, "app_net_mgt", ql_datacall_demo_thread, NULL);
@@ -0,0 +1,84 @@
#include <stdio.h>
#include <string.h>
#include "ql_func.h"
#include "ql_rtc.h"
#include "ql_rtos.h"
#include "ql_dev.h"
#include "build_macro.h"
#include "time_diag.h"
#include "../../../../ql-config/config/talkingq_time_diag.h"
void talkingq_time_diag_log(const char *phase)
{
talkingq_time_diag_snapshot_fn snapshot;
talkingq_time_diag_snapshot s;
int ret;
snapshot = m_get_api_ptr ? (talkingq_time_diag_snapshot_fn)
m_get_api_ptr(TALKINGQ_TIME_DIAG_API_NAME) : NULL;
if (!snapshot) {
printf("[TIME-DIAG] phase=%s ticks=%lu kernel_diag=unavailable "
"need_matching_diagnostic_kernel=1\r\n", phase,
(unsigned long)ql_rtos_get_systicks());
return;
}
memset(&s, 0, sizeof(s));
ret = snapshot(&s, sizeof(s));
if (ret != 0 || s.abi_version != TALKINGQ_TIME_DIAG_ABI_VERSION) {
printf("[TIME-DIAG] phase=%s kernel_diag=abi_mismatch rc=%d abi=%u\r\n",
phase, ret, s.abi_version);
return;
}
printf("[TIME-DIAG] phase=%s ticks=%u abi=%u rtc_init=%u "
"counter=%lu sys_offset=%lu app_offset=%lu "
"sys_raw=%lu app_raw=%lu follows_sys=%u app_ever=%u storage=%d\r\n",
phase, s.sample_ticks, s.abi_version, s.rtc_initialized,
(unsigned long)s.counter, (unsigned long)s.sys_offset,
(unsigned long)s.app_offset,
(unsigned long)(s.counter + s.sys_offset),
(unsigned long)(s.counter + s.app_offset),
s.app_follows_sys, s.app_ever_configured, s.offset_storage);
printf("[TIME-DIAG] phase=%s ticks=%u net_tz_min=%d rtc_tz_hour=%d source=%d "
"ntp_type=%d ntp_status=%d ntp_ready=%d gnss_rtc=%d cid=%d "
"triggers=%d dns_attempted=%d ntp_ip=%.31s\r\n",
phase, s.sample_ticks, s.network_tz_minutes, s.rtc_tz_hours, s.rtc_source,
s.ntp_type, s.ntp_status, s.ntp_ready, s.gnss_rtc_status, s.gnss_cid,
s.ntp_triggers, s.ntp_dns_attempted, s.ntp_server);
}
void talkingq_time_diag_boot(void)
{
char firmware[128] = {0};
int ret = ql_dev_get_firmware_version(firmware, sizeof(firmware));
firmware[sizeof(firmware) - 1] = '\0';
printf("[TIME-DIAG] boot app_ver=%s app_build=%s app_rev=%s "
"diag_build=%s %s firmware_rc=%d firmware=%s\r\n",
CUSTOM_APP_BUILD_VER, CUSTOM_APP_BUILD_TIME, CUSTOM_APP_BUILD_REV,
__DATE__, __TIME__, ret, firmware);
talkingq_time_diag_log("boot-before-nitz");
}
time_t talkingq_time_diag_wss_time(void)
{
time_t original;
time_t with_pointer;
time_t pointer_value = 1;
ql_rtc_time_t rtc;
int rtc_ret;
/* Capture raw flags first. Do not replace or correct the existing gate. */
talkingq_time_diag_log("wss-before-time-read");
original = time(NULL);
with_pointer = time(&pointer_value);
memset(&rtc, 0, sizeof(rtc));
rtc_ret = ql_rtc_get_time(&rtc);
printf("[TIME-DIAG] phase=wss-precheck ticks=%lu time_null=%ld "
"time_ptr_return=%ld time_ptr_value=%ld rtc_rc=%d "
"app_calendar=%04d-%02d-%02dT%02d:%02d:%02d min_epoch=1704067200 valid=%d\r\n",
(unsigned long)ql_rtos_get_systicks(), (long)original,
(long)with_pointer, (long)pointer_value, rtc_ret,
rtc.tm_year, rtc.tm_mon, rtc.tm_mday, rtc.tm_hour, rtc.tm_min, rtc.tm_sec,
original >= 1704067200);
talkingq_time_diag_log("wss-after-time-read");
return original;
}
@@ -71,55 +71,6 @@ static int vad_tts_playback_finished_status = 0;
// 统计
static uint32 frame_count = 0;
static uint32 speech_frame_count = 0;
static struct {
unsigned int reads, short_reads, read_errors, zero_frames, samples, peak;
unsigned int ring_drops, ring_drop_bytes;
uint64_t abs_sum, last_log_ms, started_ms;
} mic_diag;
static void mic_diag_log(const char *stage)
{
printf("[MIC-DIAG] %s uptime_ms=%u age_ms=%u reads=%u short=%u read_errors=%u zero=%u peak=%u mean_abs=%u ring_drops=%u ring_drop_bytes=%u\n",
stage, (unsigned int)hal_os_up_time_ms(),
(unsigned int)(hal_os_up_time_ms() - mic_diag.started_ms),
mic_diag.reads, mic_diag.short_reads, mic_diag.read_errors, mic_diag.zero_frames,
mic_diag.peak, mic_diag.samples ? (unsigned int)(mic_diag.abs_sum / mic_diag.samples) : 0,
mic_diag.ring_drops, mic_diag.ring_drop_bytes);
}
/* Observe both preroll and live PCM writes; retain the caller's wakeup policy. */
static int mic_diag_write(const void *data, int len)
{
int written = hal_os_rb_write(ring_sender, data, len, 25);
if (written < len) {
mic_diag.ring_drops++;
mic_diag.ring_drop_bytes += (unsigned int)(len - (written > 0 ? written : 0));
}
return written;
}
static void mic_diag_read(const uint8_t *data, int len, int ret)
{
int i, bounded = len > VAD_FRAME_BYTES ? VAD_FRAME_BYTES : len;
unsigned int peak = 0;
mic_diag.reads++;
if (ret < 0) mic_diag.read_errors++;
if (len != VAD_FRAME_BYTES) mic_diag.short_reads++;
for (i = 0; i + 1 < bounded; i += 2) {
int sample = (int16_t)((unsigned int)data[i] | ((unsigned int)data[i + 1] << 8));
unsigned int magnitude = (unsigned int)(sample < 0 ? -sample : sample);
if (magnitude > peak) peak = magnitude;
mic_diag.abs_sum += magnitude;
mic_diag.samples++;
}
if (bounded > 0 && peak == 0) mic_diag.zero_frames++;
if (peak > mic_diag.peak) mic_diag.peak = peak;
if (mic_diag.reads == 1 || hal_os_up_time_ms() - mic_diag.last_log_ms >= 1000) {
printf("[MIC-DIAG] read ret=%d bytes=%d frame_peak=%u state=%d\n", ret, len, peak, g_state);
mic_diag_log("sampling");
mic_diag.last_log_ms = hal_os_up_time_ms();
}
}
extern int audio_packet_type;
@@ -133,17 +84,7 @@ static audio_record_handle record_hd;
static void ql_audio_record_callback(acm_audio_track_handle handle, acm_audio_record_event_t event)
{
static int last_event = -1;
static uint64_t last_log_ms;
uint64_t now;
(void)handle;
if (event == AUDIO_RECORD_EVENT_TICK || event == AUDIO_RECORD_EVENT_NONE) return;
now = hal_os_up_time_ms();
if ((int)event != last_event || now - last_log_ms >= 1000) {
printf("[MIC-DIAG] callback event=%d uptime_ms=%u\n", event, (unsigned int)now);
last_event = (int)event;
last_log_ms = now;
}
//printf("audio_record_callback event:%d\r\n", event);
}
void vad_tts_playback_finished()
@@ -265,13 +206,7 @@ int vad_audio_init(void) {
// 启动录音
int vad_audio_start(void) {
memset(&mic_diag, 0, sizeof(mic_diag));
mic_diag.started_ms = hal_os_up_time_ms();
{
int init_ret = vad_audio_init();
printf("[MIC-DIAG] open ret=%d handle=%u uptime_ms=%u\n",
init_ret, (unsigned int)record_hd, (unsigned int)hal_os_up_time_ms());
}
vad_audio_init();
frame_count = 0;
speech_frame_count = 0;
@@ -282,7 +217,6 @@ int vad_audio_start(void) {
// 停止录音
int vad_audio_stop(void) {
mic_diag_log("stop");
printf("Audio recording stopped, frames: %d, speech: %d",
frame_count, speech_frame_count);
@@ -301,7 +235,6 @@ int vad_audio_stop(void) {
// 停止录音
int vad_audio_stop_no_wait_resp(void) {
mic_diag_log("cancel");
conversation_cancel_wait();
printf("Audio recording stopped no_wait_resp, frames: %d, speech: %d",
@@ -448,10 +381,7 @@ void vad_audio_process(void) {
case STATE_RECORDING_ACTIVE:
memset(mic_buf, 0, sizeof(mic_buf));
//每次读取640字节音频数据
{
int read_ret = ql_audio_record_read(record_hd, mic_buf, &record_len);
mic_diag_read(mic_buf, record_len, read_ret);
}
ql_audio_record_read(record_hd, mic_buf, &record_len);
if (g_state == STATE_RECORDING_WAIT_SOUND) {
/* 首次开口确认前不向服务端发送静音。只在本地保留最近500ms,
@@ -462,7 +392,7 @@ void vad_audio_process(void) {
vad_preroll_count++;
}
} else {
if(mic_diag_write(mic_buf, record_len)>0){
if(hal_os_rb_write(ring_sender, (uint8*)mic_buf, record_len, 25)>0){
ql_rtos_semaphore_release(sender_sem);
}
}
@@ -507,7 +437,7 @@ void vad_audio_process(void) {
int preroll_queued = 0;
for (n = 0; n < vad_preroll_count; n++) {
size_t frame_index = (first + n) % VAD_PREROLL_FRAMES;
if (mic_diag_write(vad_preroll[frame_index], VAD_FRAME_BYTES) > 0) {
if (hal_os_rb_write(ring_sender, vad_preroll[frame_index], VAD_FRAME_BYTES, 25) > 0) {
preroll_queued = 1;
}
}
@@ -62,12 +62,6 @@ enum { WAIT_NONE, WAIT_NO_SPEECH, WAIT_REPLY, WAIT_SEND_FAILURE };
static int conversation_wait = WAIT_NONE;
static int timeout_action_busy = 0;
static int round_response_received = 0;
/* Queue admission is distinct from a completed TLS write and from server ACK. */
static struct {
unsigned int tx_packets, tx_bytes, tx_failed, rx_text, rx_bin;
unsigned int start_ack, end_ack;
uint64_t started_ms, last_rx_ms;
} voice_diag;
static unsigned int active_session_round = 0;
static int active_session_valid = 0;
static uint64_t startup_start_ms = 0;
@@ -93,10 +87,6 @@ static void conversation_unlock(void)
static void conversation_begin_round(void)
{
conversation_round++;
memset(&voice_diag, 0, sizeof(voice_diag));
voice_diag.started_ms = hal_os_up_time_ms();
printf("[VOICE-DIAG] begin round=%u uptime_ms=%u\n",
conversation_round, (unsigned int)voice_diag.started_ms);
conversation_wait = WAIT_NONE;
round_response_received = 0;
active_session_valid = 0;
@@ -227,20 +217,6 @@ unsigned char get_xiaoice_connected_status(void)
return xiaoice_wss_connect_status;
}
/* Called with conversation_mutex held. Ages use monotonic milliseconds. */
static void voice_diag_snapshot(const char *stage)
{
uint64_t now = hal_os_up_time_ms();
printf("[VOICE-DIAG] %s round=%u session=%s age_ms=%u queue_pkts=%u queue_bytes=%u queue_fail=%u rx_text=%u rx_bin=%u start_ack=%u end_ack=%u last_rx_age_ms=%d wait=%d wait_ms=%u response=%d\n",
stage, conversation_round, active_session_valid ? sessionId : "none",
(unsigned int)(now - voice_diag.started_ms), voice_diag.tx_packets,
voice_diag.tx_bytes, voice_diag.tx_failed, voice_diag.rx_text,
voice_diag.rx_bin, voice_diag.start_ack, voice_diag.end_ack,
voice_diag.rx_text || voice_diag.rx_bin ? (int)(now - voice_diag.last_rx_ms) : -1,
conversation_wait, conversation_wait == WAIT_REPLY ?
(unsigned int)(now - reply_wait_start_ms) : 0, round_response_received);
}
static void onopen(rws_socket socket)
{
if (socket != app->socket || !app->init) return;
@@ -269,9 +245,6 @@ static void onclose(rws_socket socket)
}
if (socket != app->socket) return;
conversation_lock();
voice_diag_snapshot("disconnected");
conversation_unlock();
xiaoice_wss_connect_status = 0;
app->socket = NULL;
/* The worker owns and releases this socket after the callback. */
@@ -676,16 +649,6 @@ static void conversation_log_error_message(const char *text, unsigned int len)
static void onmessage_text(rws_socket socket, const char *text, const unsigned int len)
{
conversation_lock();
voice_diag.rx_text++;
voice_diag.last_rx_ms = hal_os_up_time_ms();
if (strstr(text, "START_ACK")) voice_diag.start_ack++;
if (strstr(text, "END_ACK")) voice_diag.end_ack++;
/* Bounded preview includes unrecognized replies and ACKs before filtering. */
printf("[VOICE-RX] round=%u session=%s uptime_ms=%u len=%u accepted=%d preview=%.*s%s\n",
conversation_round, active_session_valid ? sessionId : "none",
(unsigned int)voice_diag.last_rx_ms, len,
conversation_accept_voice_message(text), (int)(len > 192 ? 192 : len), text,
len > 192 ? " [truncated]" : "");
if (strstr(text, "tts_error") != NULL || strstr(text, "tts_err") != NULL ||
strstr(text, "PROMPT_SOUND_URL") != NULL) {
conversation_log_error_message(text, len);
@@ -700,16 +663,8 @@ static void onmessage_text(rws_socket socket, const char *text, const unsigned i
}
static void onmessage_bin(rws_socket socket, const void * data, const unsigned int len)
{
(void)socket;
(void)data;
conversation_lock();
voice_diag.rx_bin++;
voice_diag.last_rx_ms = hal_os_up_time_ms();
if (voice_diag.rx_bin == 1 || voice_diag.rx_bin % 50 == 0)
printf("[VOICE-RX] binary round=%u len=%u count=%u ignored=1\n",
conversation_round, len, voice_diag.rx_bin);
conversation_unlock();
{
LOG_D("message(bin): %d ", len);
return;
}
@@ -856,11 +811,7 @@ int talkingq_ws_send_text(char *senddata)
LOG_D("talkingq_wss_send_text string = %s, len = %d", senddata, strlen(senddata));
{
int queued = rws_socket_send_text(app->socket, senddata);
printf("[VOICE-TX] auth/text queue uptime_ms=%u bytes=%u accepted=%d\n",
(unsigned int)hal_os_up_time_ms(), (unsigned int)strlen(senddata), queued != 0);
}
rws_socket_send_text(app->socket, senddata);
return 0;
}
@@ -1090,7 +1041,6 @@ void sendStartPacket(void) {
startPacket.dataSize = 0;
active_session_valid = sendRecordingAudioPacket(&startPacket) == 0;
voice_diag_snapshot("start-queued");
conversation_unlock();
// rt_kprintf("sendStartPacket, seq=%u\r\n", startPacket.sequenceNumber);
}
@@ -1109,7 +1059,6 @@ void sendEndPacket(void) {
reply_wait_start_ms = hal_os_up_time_ms();
conversation_wait = end_ret == 0 ? WAIT_REPLY : WAIT_SEND_FAILURE;
}
voice_diag_snapshot("end-queued");
}
conversation_unlock();
// rt_kprintf("sendEndPacket, seq=%u\r\n", endPacket.sequenceNumber);
@@ -1125,19 +1074,12 @@ void sendAudioPacket(const uint8_t* data, size_t dataSize) {
audioPacket.data = malloc(audioPacket.dataSize);
if(audioPacket.data==NULL){
RLOGE("audioPacket.data NULL");
conversation_lock();
voice_diag.tx_failed++;
printf("[VOICE-TX] alloc_failed round=%u seq=%u bytes=%u\n",
conversation_round, audioPacket.sequenceNumber, (unsigned int)dataSize);
conversation_unlock();
return;
}
memcpy(audioPacket.data, data, dataSize);
}
conversation_lock();
sendRecordingAudioPacket(&audioPacket);
conversation_unlock();
// rt_kprintf("sendAudioPacket, seq=%u\r\n", audioPacket.sequenceNumber);
if(audioPacket.data){
free(audioPacket.data);
@@ -1150,9 +1092,7 @@ void sendInterruptPacket(void) {
interruptPacket.packetType = 3;
interruptPacket.dataSize = 0;
conversation_lock();
sendRecordingAudioPacket(&interruptPacket);
conversation_unlock();
}
void sendAuthentication()
@@ -1182,9 +1122,6 @@ int sendRecordingAudioPacket(AudioPacket *packet) {
sendBuffer = malloc(DEVICE_ID_SIZE + SESSION_ID_SIZE + sizeof(uint32_t) * 2 + sizeof(uint32_t) + packet->dataSize);
if(sendBuffer==NULL){
RLOGE("sendBuffer NULL");
voice_diag.tx_failed++;
printf("[VOICE-TX] alloc_failed seq=%u type=%u bytes=%u\n",
packet->sequenceNumber, packet->packetType, (unsigned int)packet->dataSize);
return -1;
}
@@ -1214,21 +1151,7 @@ int sendRecordingAudioPacket(AudioPacket *packet) {
// memcpy(recorder->save_buf, (void *)sendBuffer, recorder->save_len);
//record_msg_send(recorder, RECORD_MSG_DATA, recorder->save_len);
uint64_t queue_started = hal_os_up_time_ms();
int send_ret = talkingq_ws_send_bin((uint8_t*)sendBuffer, totalSize);
if (send_ret == 0) {
voice_diag.tx_packets++;
voice_diag.tx_bytes += (unsigned int)packet->dataSize;
} else {
voice_diag.tx_failed++;
}
if (packet->packetType == 1 || packet->packetType == 2 || send_ret != 0 ||
voice_diag.tx_packets % 50 == 0) {
printf("[VOICE-TX] queue round=%u session=%s seq=%u type=%u bytes=%u ret=%d elapsed_ms=%u\n",
conversation_round, sessionId, packet->sequenceNumber, packet->packetType,
(unsigned int)packet->dataSize, send_ret,
(unsigned int)(hal_os_up_time_ms() - queue_started));
}
free(sendBuffer);
return send_ret;
// rt_kprintf("sendRecordingAudioPacket - Device ID: %s, Session ID: %s\n", deviceId, sessionId);
@@ -1570,7 +1493,6 @@ static void conversation_check_timeout(void)
(conversation_wait == WAIT_NO_SPEECH ||
conversation_wait == WAIT_SEND_FAILURE ||
hal_os_up_time_ms() - reply_wait_start_ms > 5000)) {
voice_diag_snapshot("timeout-before-disconnect");
expired = conversation_wait;
expired_round = wait_round;
conversation_wait = WAIT_NONE;
+37
View File
@@ -0,0 +1,37 @@
#ifndef TALKINGQ_TIME_DIAG_ABI_H
#define TALKINGQ_TIME_DIAG_ABI_H
/* Shared by the ARMCC kernel and GCC application. No pointers or enums in ABI. */
#define TALKINGQ_TIME_DIAG_ABI_VERSION 1U
#define TALKINGQ_TIME_DIAG_API_NAME "talkingq_time_diag_snapshot_v1"
#define TALKINGQ_TIME_DIAG_API_HASH 0x11B09482U
typedef struct {
unsigned int abi_version;
unsigned int sample_ticks;
unsigned int rtc_initialized;
unsigned int counter;
unsigned int sys_offset;
unsigned int app_offset;
unsigned int app_follows_sys;
unsigned int app_ever_configured;
int offset_storage;
int network_tz_minutes;
int rtc_tz_hours;
int rtc_source;
int ntp_type;
int ntp_status;
int ntp_ready;
int gnss_rtc_status;
int gnss_cid;
int ntp_triggers;
int ntp_dns_attempted;
char ntp_server[32];
} talkingq_time_diag_snapshot;
typedef int (*talkingq_time_diag_snapshot_fn)(talkingq_time_diag_snapshot *, unsigned int);
int talkingq_time_diag_snapshot_v1(talkingq_time_diag_snapshot *out, unsigned int size);
void talkingq_time_diag_trace(const char *phase);
#endif
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
+1
View File
@@ -12,6 +12,7 @@ MAKEFILE_PATH :=$(subst $(strip \),/,$(abspath $(lastword $(MAKEFILE_LIST))))
SRC_FILES+= \
quec_kernel_shim_api.c \
ql_user_api_map.c \
talkingq_time_diag.c \
#-------------------------------------------------------------------------------
@@ -30,6 +30,7 @@ WHEN WHO WHAT, WHERE, WHY
#include "ql_keypad.h"
#include "ql_rtc.h"
#include "ql_time.h"
#include "../../../ql-config/config/talkingq_time_diag.h"
#include "ql_power.h"
#include "ql_nw.h"
#include "ql_platform.h"
@@ -103,6 +104,7 @@ ql_open_api_list vUserAPITable[] __attribute__((used, section("DDR_USER_API_TABL
{
{0x60BCEA5A, (void*)ql_user_api_map_test},
{0xFFFFFFFF, (void*)quec_kernel_api_test},
{TALKINGQ_TIME_DIAG_API_HASH, (void *)talkingq_time_diag_snapshot_v1},
#ifdef QL_MBEDTLS
/************************************************************************************************
@@ -0,0 +1,80 @@
#include <stdio.h>
#include <string.h>
#include "ql_type.h"
#include "quec_cust_feature.h"
#include "pmic.h"
#include "pmic_rtc.h"
#include "quec_rtc.h"
#include "ql_rtos.h"
#include "../../../ql-config/config/talkingq_time_diag.h"
/* SDK globals are linked by name, never by firmware-specific RAM addresses. */
extern unsigned char m_rtc_app_sys_keeping_synced;
extern PMIC_RTC_OFFSET_STORAGE m_pmic_rtc_offset_storage_type;
#ifdef NTP
extern int get_ntp_time_type(void);
extern int GetNTPStatus(void);
extern int talkingq_ntp_diag_ready(void);
#endif
#ifdef QUEC_GNSS_NTP
extern int quec_get_rtc_status(void);
extern int quec_get_nw_status(void);
extern char *get_ntp_server_ip(void);
extern char ntp_sync_cnt;
extern char ntp_dns_gain;
#endif
int talkingq_time_diag_snapshot_v1(talkingq_time_diag_snapshot *out, unsigned int size)
{
if (out == NULL || size != sizeof(*out)) return -1;
memset(out, 0, sizeof(*out));
out->abi_version = TALKINGQ_TIME_DIAG_ABI_VERSION;
out->sample_ticks = ql_rtos_get_systicks();
out->ntp_type = out->ntp_status = out->ntp_ready = -1;
out->gnss_rtc_status = out->gnss_cid = -1;
out->ntp_triggers = out->ntp_dns_attempted = -1;
out->rtc_initialized = pmic_rtc_INIT_FLAG_GET() ? 1U : 0U;
if (!out->rtc_initialized) return 0;
/* Read raw RTC storage, not PMIC_RTC_GetTime_Count(): that getter can
* enable APP-follow-SYS and would disturb the boot state under diagnosis. */
out->app_follows_sys = m_rtc_app_sys_keeping_synced;
out->app_ever_configured = pmic_userdata_get_app_rtc_ever_configed() ? 1U : 0U;
out->offset_storage = (int)m_pmic_rtc_offset_storage_type;
out->sys_offset = PM812_RTC_GET_32B_SYS_OFFSET();
out->app_offset = PM812_RTC_GET_32B_APP_OFFSET();
out->counter = PM812_RTC_GET_32B_COUNTER();
out->network_tz_minutes = GetTimeZone();
out->rtc_tz_hours = PMIC_RTC_GetTimezone();
out->rtc_source = (int)quec_get_rtc_source();
#ifdef NTP
out->ntp_type = get_ntp_time_type();
out->ntp_status = GetNTPStatus();
out->ntp_ready = talkingq_ntp_diag_ready();
#endif
#ifdef QUEC_GNSS_NTP
out->gnss_rtc_status = quec_get_rtc_status();
out->gnss_cid = quec_get_nw_status();
out->ntp_triggers = (unsigned char)ntp_sync_cnt;
out->ntp_dns_attempted = (unsigned char)ntp_dns_gain;
snprintf(out->ntp_server, sizeof(out->ntp_server), "%.31s", get_ntp_server_ip());
#endif
return 0;
}
void talkingq_time_diag_trace(const char *phase)
{
talkingq_time_diag_snapshot s;
if (talkingq_time_diag_snapshot_v1(&s, sizeof(s)) != 0) return;
printf("[TIME-DIAG] kernel phase=%s build=%s %s rtc_init=%u "
"counter=%lu sys_offset=%lu app_offset=%lu follows_sys=%u app_ever=%u "
"storage=%d\r\n",
phase, __DATE__, __TIME__, s.rtc_initialized,
(unsigned long)s.counter, (unsigned long)s.sys_offset,
(unsigned long)s.app_offset, s.app_follows_sys, s.app_ever_configured,
s.offset_storage);
printf("[TIME-DIAG] kernel phase=%s ticks=%u net_tz_min=%d rtc_tz_hour=%d source=%d "
"ntp_type=%d ntp_status=%d ntp_ready=%d gnss_rtc=%d triggers=%d\r\n",
phase, s.sample_ticks, s.network_tz_minutes, s.rtc_tz_hours, s.rtc_source,
s.ntp_type, s.ntp_status, s.ntp_ready, s.gnss_rtc_status, s.ntp_triggers);
}
+23 -3
View File
@@ -17,6 +17,7 @@
#include "quec_sockets.h"
#include "gps_app.h"
#include "quec_mfpr_api.h"
#include "../../../ql-config/config/talkingq_time_diag.h"
#define GPS_VIA_UART
@@ -415,6 +416,8 @@ void quec_gps_nw_status_monitor(void * argv)
{
int cid_num = 0;
struct sockaddr sockaddr = {0};
int last_type = -1, last_status = -1, last_rtc = -1;
int current_type, current_status, dns_ret;
while(1)
{
cid_num = quec_gps_get_cid_act();
@@ -429,13 +432,18 @@ void quec_gps_nw_status_monitor(void * argv)
memcpy(&gps_nw_ctx_ptr.gps_ip4_addr, &(((struct sockaddr_in *)&sockaddr)->sin_addr), sizeof(struct in_addr));
quec_rtos_task_sleep_s(2);
if(!ntp_dns_gain){
quec_gps_ntp_set_server(quec_gps_cfg->ntp_url);
dns_ret = quec_gps_ntp_set_server(quec_gps_cfg->ntp_url);
printf("[TIME-DIAG] gnss_ntp_dns cid=%d server=%.31s rc=%d ip=%.31s\r\n",
cid_num, quec_gps_cfg->ntp_url, dns_ret, get_ntp_server_ip());
ntp_dns_gain++;
}
/*限制成功激活pdp后ntp的同步次数*/
if((gps_nw_ctx_ptr.rtc_status !=1)&&(ntp_sync_cnt < 10))
{
get_ntp_time_internal();
printf("[TIME-DIAG] gnss_ntp_trigger cid=%d attempt=%u ntp_type=%d ntp_status=%d\r\n",
cid_num, (unsigned int)(unsigned char)ntp_sync_cnt + 1U,
get_ntp_time_type(), GetNTPStatus());
get_ntp_time_internal();
ntp_sync_cnt++;
GPS_LOGD("Ntp sync cnt:%d!!!",ntp_sync_cnt);
}
@@ -453,6 +461,15 @@ void quec_gps_nw_status_monitor(void * argv)
{
gps_nw_ctx_ptr.rtc_status = 1;
}
current_type = get_ntp_time_type();
current_status = GetNTPStatus();
if (current_type != last_type || current_status != last_status ||
gps_nw_ctx_ptr.rtc_status != last_rtc) {
talkingq_time_diag_trace("gnss-time-state");
last_type = current_type;
last_status = current_status;
last_rtc = gps_nw_ctx_ptr.rtc_status;
}
//RTI_LOG("Found an active pdp %d:%d:%d:%d:%d:%d!!!",tm.tm_year,tm.tm_mon,tm.tm_mday,tm.tm_hour,tm.tm_min,tm.tm_sec);
}
}
@@ -472,6 +489,7 @@ int quec_get_nw_status(void)
int gps_nw_active(void)
{
int cid_num = 0;
int dns_ret;
cid_num = quec_gps_get_cid_act();
struct sockaddr sockaddr = {0};
if(cid_num>0)
@@ -483,7 +501,9 @@ int gps_nw_active(void)
quec_soc_get_ipv4v6_local_addr(cid_num, AF_INET, &sockaddr);
memcpy(&gps_nw_ctx_ptr.gps_ip4_addr, &(((struct sockaddr_in *)&sockaddr)->sin_addr), sizeof(struct in_addr));
if(!ntp_dns_gain){
quec_gps_ntp_set_server(quec_gps_cfg->ntp_url);
dns_ret = quec_gps_ntp_set_server(quec_gps_cfg->ntp_url);
printf("[TIME-DIAG] gnss_ntp_dns cid=%d server=%.31s rc=%d ip=%.31s\r\n",
cid_num, quec_gps_cfg->ntp_url, dns_ret, get_ntp_server_ip());
ntp_dns_gain++;
}
GPS_LOGI("Found an active pdp!!!");
+13
View File
@@ -77,6 +77,7 @@ static const char rcsid[] = "$OpenBSD: rdate.c,v 1.22 2004/02/18 20:10:53 jmc Ex
#endif
#include "utltime.h"
#include "quec_cust_feature.h"
#include "../../../ql-config/config/talkingq_time_diag.h"
/* there are systems without libutil; for portability */
#ifdef HAVE_UTIL_H
#include <util.h>
@@ -204,6 +205,11 @@ int GetNTPStatus(void)
return NTP_SYNC_SUCCESS;
}
int talkingq_ntp_diag_ready(void)
{
return NtpFlagRef != NULL && NTPTaskRef != NULL;
}
static unsigned int ntp_retry_timer[] = {
(1 * 1000 / 5), /*2 sec*/
(10 * 1000 / 5), /*10 sec*/
@@ -278,11 +284,14 @@ extern char *get_ntp_server_ip(void);
while(try_times > 0 && count < 8)
{
--try_times;
printf("[TIME-DIAG] ntp_request attempt=%d server=%.31s\r\n",
3 - try_times, strlen(hname) > 0 ? hname : hname_default);
if(strlen(hname) > 0)
ret = ntp_client(hname, family, &new, &adjust, corrleaps);
else
ret = ntp_client(hname_default, family, &new, &adjust, corrleaps);
printf("[TIME-DIAG] ntp_result attempt=%d rc=%d\r\n", 3 - try_times, ret);
if(ret != 0)
{
continue;
@@ -297,6 +306,7 @@ extern char *get_ntp_server_ip(void);
if (new.tv_sec > 60 * 60 * 24 * 365)
{
SaveNTPParameters(ltm);
printf("[TIME-DIAG] ntp_rtc_written epoch=%ld\r\n", (long)tim);
break;
}
OSATaskSleep(200);
@@ -311,6 +321,7 @@ extern char *get_ntp_server_ip(void);
SetNTPStatus(2);//failed
}
talkingq_time_diag_trace("ntp-finished");
}
@@ -325,12 +336,14 @@ void NTP_Init_Internal(void)
str = psm_get_wrapper("Debug", NULL, "debug_ntp_disable");
if (str && strcmp(str, "1") == 0)
{
printf("[TIME-DIAG] ntp_init skipped debug_ntp_disable=1\r\n");
free(str);
return;
}
if (str)
free(str);
printf("[TIME-DIAG] ntp_init debug_ntp_disable=0\r\n");
/* Ntp Flag initialize. */
status = OSAFlagCreate(&NtpFlagRef);
ASSERT(status == OS_SUCCESS);
@@ -92,7 +92,7 @@ int ql_rtc_set_time(ql_rtc_time_t *calendar) {
int ql_rtc_get_time(ql_rtc_time_t *out) {*out=saved; if (mode==20) out->tm_year=1970; return mode==11 ? -1 : 0;}
int ql_rtos_task_create(ql_task_t *task,unsigned int size,unsigned char priority,char *name,
void (*entry)(void *),void *arg,...) {
assert(size>=4096 && priority==110 && !strcmp(name,"app_time_calibrate"));
assert(size>=4096 && priority==110 && !strcmp(name,"app_nitz_test"));
++creates;
if (mode==19) return -1;
*task=(void *)1; entry(arg); return 0;
@@ -133,7 +133,7 @@ int main(int argc,char **argv) {
if (mode==12) zone_minutes=-330;
if (mode==24) zone_minutes=0;
if (mode==13) ticks=UINT32_MAX-199U;
app_time_calibrate_start(); app_time_calibrate_start();
app_nitz_test_start(); app_nitz_test_start();
assert(creates==(mode==19 ? 2 : 1));
if ((mode>=1 && mode<=5) || mode==7 || mode==15 || mode==17 || mode==19 || mode==25)
assert(sets==0);
@@ -149,7 +149,7 @@ int main(int argc,char **argv) {
}
return 0;
}
'''.replace('APP_SOURCE', str(APP / 'app_time_calibrate.c'))
'''.replace('APP_SOURCE', str(APP / 'app_nitz_test.c'))
(work / 'test.c').write_text(harness)
compiler = shutil.which('clang') or shutil.which('cc')
if not compiler:
@@ -177,5 +177,5 @@ int main(int argc,char **argv) {
if expected.startswith('PASS'):
assert 'age=unknown first_valid_after_pdp=1 max_rtc_writes=1' in log, log
else:
assert '[APP-TIME] PASS ' not in log, log
assert '[APP-NITZ-TEST] PASS ' not in log, log
print('PASS: parser validation and 26 first-snapshot/retry/timezone/RTC scenarios (ASan/UBSan)')
+216
View File
@@ -0,0 +1,216 @@
"""Compile the actual APP NTP tester with scripted network/SDK responses.
These are host regression tests, not evidence of device whitelist access.
"""
from pathlib import Path
import shutil
import subprocess
import tempfile
ROOT = Path(__file__).resolve().parents[1]
APP = ROOT / 'ql-application/threadx/interface/custom_app'
with tempfile.TemporaryDirectory(prefix='app-ntp-test-') as directory:
work = Path(directory)
headers = {
'ql_rtos.h': '''#include <stddef.h>
typedef void *ql_task_t;
typedef void *ql_sem_t;
unsigned int ql_rtos_get_systicks(void);
int ql_rtos_task_create(ql_task_t *, unsigned int, unsigned char, char *, void (*)(void *), void *, ...);
int ql_rtos_task_delete(ql_task_t);
int ql_rtos_semaphore_create(ql_sem_t *, unsigned int);
int ql_rtos_semaphore_wait(ql_sem_t, unsigned int);
int ql_rtos_semaphore_release(ql_sem_t);
''',
'ql_func.h': '#include <stdint.h>\nextern intptr_t (*m_get_api_ptr)(char *);\n',
'ql_rtc.h': '''typedef struct {
int tm_sec, tm_min, tm_hour, tm_mday, tm_mon, tm_year, tm_wday;
} ql_rtc_time_t;
int ql_rtc_get_time(ql_rtc_time_t *);
''',
'ql_ntp.h': '''enum {QL_NTP_SUCCESS=0};
int ql_ntp_set_server(char *);
int ql_ntp_set_cid(int);
void ql_ntp_sync_ex(void (*)(int));
''',
'netdb.h': '''#include <sys/socket.h>
struct addrinfo {
int ai_flags,ai_family,ai_socktype,ai_protocol;
socklen_t ai_addrlen;
struct sockaddr *ai_addr;
struct addrinfo *ai_next;
};
int getaddrinfo_with_pcid(const char *,const char *,const struct addrinfo *,struct addrinfo **,unsigned char);
#define freeaddrinfo fake_freeaddrinfo
void fake_freeaddrinfo(struct addrinfo *);
''',
'sockets.h': '''#include <sys/socket.h>
#include <sys/select.h>
#include <arpa/inet.h>
#define socket fake_socket
#define connect fake_connect
#define send fake_send
#define recv fake_recv
#define close fake_close
#define select fake_select
int fake_socket(int,int,int);
int fake_connect(int,const struct sockaddr *,socklen_t);
int fake_send(int,const void *,size_t,int);
int fake_recv(int,void *,size_t,int);
int fake_close(int);
int fake_select(int,fd_set *,fd_set *,fd_set *,struct timeval *);
''',
}
for name, content in headers.items():
(work / name).write_text(content)
harness = r'''
#include <assert.h>
#include <stdlib.h>
#include "APP_SOURCE"
static int mode, creates, sends, closes, frees, syncs, signals;
static unsigned int ticks=100;
static unsigned char sent[48];
static time_t app_epoch=100;
static void (*saved_callback)(int);
static struct sockaddr_in address;
static struct addrinfo result;
unsigned int ql_rtos_get_systicks(void) { return ticks; }
int getaddrinfo_with_pcid(const char *name,const char *port,
const struct addrinfo *hints,struct addrinfo **out,unsigned char cid) {
assert(strcmp(name,"ntp.aliyun.com")==0 && strcmp(port,"123")==0 && cid==1);
assert(hints->ai_family==AF_INET && hints->ai_socktype==SOCK_DGRAM);
if (mode==1) return -9;
address.sin_family=AF_INET; address.sin_port=htons(123);
address.sin_addr.s_addr=htonl(0xc0000201U);
result.ai_addr=(struct sockaddr *)&address; result.ai_addrlen=sizeof(address);
*out=&result; return 0;
}
void fake_freeaddrinfo(struct addrinfo *p) {assert(p==&result); ++frees;}
int fake_socket(int domain,int type,int protocol) {
assert(domain==AF_INET && type==SOCK_DGRAM && protocol==IPPROTO_UDP);
return mode==10 ? -1 : 7;
}
int fake_connect(int fd,const struct sockaddr *p,socklen_t size) {
assert(fd==7 && p==(struct sockaddr *)&address && size==sizeof(address));
return mode==11 ? -1 : 0;
}
int fake_send(int fd,const void *data,size_t size,int flags) {
assert(fd==7 && size==48 && flags==0); ++sends;
memcpy(sent,data,48); assert(sent[0]==0x23);
return mode==12 ? -1 : 48;
}
int fake_select(int nfds,fd_set *readers,fd_set *writers,fd_set *errors,struct timeval *timeout) {
assert(nfds==8 && FD_ISSET(7,readers) && writers==NULL && errors==NULL);
assert(timeout->tv_sec<=5); ticks+=20;
return mode==2 ? 0 : 1;
}
static void valid_reply(unsigned char *p,const unsigned char *request) {
memset(p,0,48); p[0]=0x24; p[1]=2;
memcpy(p+24,request+40,8);
write_u32(p+32,4008988800U); write_u32(p+40,4008988800U);
}
int fake_recv(int fd,void *buffer,size_t size,int flags) {
assert(fd==7 && size>=48 && flags==0);
valid_reply(buffer,sent); return 48;
}
int fake_close(int fd) {assert(fd==7); ++closes; return 0;}
int ql_rtos_task_create(ql_task_t *task,unsigned int size,unsigned char priority,char *name,
void (*entry)(void *),void *arg,...) {
assert(size>=4096 && priority==110 && strcmp(name,"app_ntp_test")==0);
++creates; *task=(void *)1; entry(arg); return 0;
}
int ql_rtos_task_delete(ql_task_t task) {assert(task==NULL); return 0;}
int ql_rtos_semaphore_create(ql_sem_t *sem,unsigned int count) {
assert(count==0);
if (mode==13) return -1;
*sem=(void *)2; return 0;
}
int ql_rtos_semaphore_wait(ql_sem_t sem,unsigned int timeout) {
assert(sem==(void *)2 && timeout==25000);
ticks+=100;
if (mode==5) return -1;
assert(signals==1); return 0;
}
int ql_rtos_semaphore_release(ql_sem_t sem) {assert(sem==(void *)2); ++signals; return 0;}
static intptr_t lookup(char *name) {
assert(strncmp(name,"ql_ntp_",7)==0);
return mode==6 ? 0 : 1;
}
intptr_t (*m_get_api_ptr)(char *)=lookup;
int ql_ntp_set_server(char *name) {assert(strcmp(name,"ntp.aliyun.com")==0); return mode==14 ? 1 : 0;}
int ql_ntp_set_cid(int cid) {assert(cid==1); return mode==15 ? 1 : 0;}
void ql_ntp_sync_ex(void (*callback)(int)) {
++syncs; saved_callback=callback;
if (mode==5) return;
if (mode!=3) app_epoch=1800000000;
if (mode==7) app_epoch+=28800;
callback(mode==4 ? 1 : 0);
}
time_t time(time_t *out) {(void)out; return app_epoch;}
int ql_rtc_get_time(ql_rtc_time_t *calendar) {
calendar->tm_year=2027; return mode==8 ? -1 : 0;
}
static void check_parser(void) {
unsigned char request[48]={0},reply[48]; unsigned int epoch=0;
request[40]=9; valid_reply(reply,request);
assert(parse_ntp_reply(reply,48,request,&epoch)==0 && epoch==1800000000);
assert(parse_ntp_reply(reply,47,request,&epoch)==-1);
reply[0]|=0xc0; assert(parse_ntp_reply(reply,48,request,&epoch)==-2);
valid_reply(reply,request); reply[1]=0;
assert(parse_ntp_reply(reply,48,request,&epoch)==-2);
reply[1]=16; assert(parse_ntp_reply(reply,48,request,&epoch)==-2);
valid_reply(reply,request); reply[0]=0x23;
assert(parse_ntp_reply(reply,48,request,&epoch)==-2);
valid_reply(reply,request); reply[24]^=1;
assert(parse_ntp_reply(reply,48,request,&epoch)==-3);
valid_reply(reply,request); memset(reply+32,0,8);
assert(parse_ntp_reply(reply,48,request,&epoch)==-4);
valid_reply(reply,request); memset(reply+40,0,8);
assert(parse_ntp_reply(reply,48,request,&epoch)==-4);
valid_reply(reply,request); write_u32(reply+40,2208988800U+100);
assert(parse_ntp_reply(reply,48,request,&epoch)==-5);
valid_reply(reply,request); write_u32(reply+40,(unsigned int)(NTP_EPOCH_DELTA+2100000000ULL));
assert(parse_ntp_reply(reply,48,request,&epoch)==0 && epoch==2100000000U);
}
int main(int argc,char **argv) {
assert(argc==2); mode=atoi(argv[1]); check_parser();
app_ntp_test_start(); app_ntp_test_start(); assert(creates==1);
assert(frees==(mode==1 ? 0 : 1));
assert(closes==((mode==1 || mode==10) ? 0 : 1));
if (mode==1 || mode==2 || mode==6 || mode>=10) assert(syncs==0);
else assert(syncs==1);
if (mode==5) {saved_callback(0); assert(signals==1);}
return 0;
}
'''.replace('APP_SOURCE', str(APP / 'app_ntp_test.c'))
(work / 'test.c').write_text(harness)
compiler = shutil.which('clang') or shutil.which('cc')
if not compiler:
raise SystemExit('A native C compiler is required')
subprocess.run([compiler, '-std=c99', '-Wall', '-Wextra', '-Werror',
'-I', str(work), '-I', str(APP / 'inc'),
str(work / 'test.c'), '-o', str(work / 'test')], check=True)
cases = {
0: 'PASS udp_response=verified app_utc=verified',
1: 'FAIL stage=dns',
2: 'FAIL stage=receive_wait',
3: 'FAIL stage=app_clock_verification',
4: 'FAIL stage=app_clock_verification',
5: 'FAIL stage=sdk_callback_timeout',
6: 'FAIL stage=sdk_api_unavailable',
7: 'FAIL stage=app_clock_verification',
8: 'FAIL stage=app_clock_verification',
10: 'FAIL stage=socket',
11: 'FAIL stage=udp_connect',
12: 'FAIL stage=send',
13: 'FAIL stage=semaphore',
14: 'FAIL stage=sdk_server',
15: 'FAIL stage=sdk_cid',
}
for mode, expected in cases.items():
log = subprocess.check_output([str(work / 'test'), str(mode)], text=True)
assert expected in log, log
if mode != 0:
assert '[APP-NTP-TEST] PASS ' not in log, log
print('PASS: packet validation and 15 network/SDK/clock scenarios; no device traffic sent')
+90
View File
@@ -0,0 +1,90 @@
"""Exercise the actual read-only SYS/AT probe without a modem or kernel image."""
from pathlib import Path
import shutil
import subprocess
import tempfile
ROOT = Path(__file__).resolve().parents[1]
APP = ROOT / 'ql-application/threadx/interface/custom_app'
with tempfile.TemporaryDirectory(prefix='app-sys-probe-') as directory:
work = Path(directory)
headers = {
'ql_rtos.h': '''unsigned int ql_rtos_get_systicks(void);
void ql_rtos_task_sleep_ms(unsigned int);
''',
'ql_func.h': '#include <stdint.h>\nextern intptr_t (*m_get_api_ptr)(char *);\n',
'ql_rtc.h': '''typedef struct {int tm_sec,tm_min,tm_hour,tm_mday,tm_mon,tm_year,tm_wday;} ql_rtc_time_t;
int ql_rtc_get_time_ex(ql_rtc_time_t *);
''',
'ql_atcmd.h': 'int ql_atcmd_send_sync(char *,char *,int,char *,int);\n',
}
for name, content in headers.items():
(work / name).write_text(content)
harness = r'''
#include <assert.h>
#include <stdlib.h>
#include "APP_SOURCE"
static int mode, sends, reads, sys_reads;
static unsigned int ticks=100, origin=100;
unsigned int ql_rtos_get_systicks(void) {return ticks;}
void ql_rtos_task_sleep_ms(unsigned int ms) {assert(ms==3000); ticks+=ms/5;}
static unsigned int get_count(int offset) {
assert(offset==0); ++sys_reads;
if (mode==4) return 100;
return 1793030410U+(mode==5 ? 0 : (ticks-origin)/200U);
}
static int get_zone(void) {return 480;}
static intptr_t lookup(char *name) {
assert(strstr(name,"diag")==NULL);
if (!strcmp(name,"PMIC_RTC_GetTime_Count")) return mode==1 ? 0 : (intptr_t)get_count;
if (!strcmp(name,"GetTimeZone")) return mode==1 || mode==2 ? 0 : (intptr_t)get_zone;
if (!strcmp(name,"ql_rtc_get_time_ex")) return mode==7 ? 0 : 1;
if (!strcmp(name,"ql_atcmd_send_sync")) return mode==8 ? 0 : 1;
assert(0); return 0;
}
intptr_t (*m_get_api_ptr)(char *)=lookup;
time_t time(time_t *out) {(void)out; return 100;}
int ql_rtc_get_time_ex(ql_rtc_time_t *out) {
++reads; out->tm_year=1970; out->tm_mon=1; out->tm_mday=1; return 0;
}
int ql_atcmd_send_sync(char *cmd,char *resp,int len,char *filter,int timeout) {
static const char *commands[]={"AT+QLTS=0\r\n","AT+QLTS=1\r\n","AT+QLTS=2\r\n","AT+CCLK?\r\n"};
assert(!strcmp(cmd,commands[sends%4]));
assert(len==256 && filter==NULL && timeout==2);
++sends; ticks+=20;
if (mode==1) {strcpy(resp,"\r\nERROR\r\n"); return 1;}
if (mode==6) {memset(resp,'X',len); return 0;}
strcpy(resp,"\r\n+QLTS: \"2026/10/26,08:00:10+32,0\"\r\nOK\r\n"); return 0;
}
int main(int argc,char **argv) {
assert(argc==2); mode=atoi(argv[1]);
if (mode==3) m_get_api_ptr=NULL;
if (mode==9) ticks=origin=UINT32_MAX-199U;
app_sys_time_probe_run();
assert(sends==(mode==3 || mode==8 ? 0 : 8));
assert(reads==(mode==3 || mode==7 || mode==8 ? 0 : 2));
assert(sys_reads==(mode==1 || mode==2 || mode==3 ? 0 : 2));
return 0;
}
'''.replace('APP_SOURCE', str(APP / 'app_sys_time_probe.c'))
(work / 'test.c').write_text(harness)
compiler = shutil.which('clang') or shutil.which('cc')
if not compiler:
raise SystemExit('A native C compiler is required')
subprocess.run([compiler, '-std=c99', '-Wall', '-Wextra', '-Werror',
'-fsanitize=address,undefined', '-I', str(work), '-I', str(APP / 'inc'),
str(work / 'test.c'), '-o', str(work / 'test')], check=True)
cases = {0: 'direct_sys=candidate', 1: 'direct_sys=unavailable',
2: 'direct_sys=unavailable', 3: 'FAIL stage=api_lookup_unavailable',
4: 'direct_sys=rejected', 5: 'direct_sys=rejected',
6: 'terminated=0', 7: 'rtc_ex=unavailable', 8: 'at=unavailable',
9: 'direct_sys=candidate'}
for mode, expected in cases.items():
log = subprocess.check_output([str(work / 'test'), str(mode)], text=True)
assert expected in log, log
assert '[APP-SYS-PROBE] finished rtc_not_written=1' in log, log
if mode == 1:
assert 'query=QLTS_1 rc=1' in log, log
if mode not in (0, 6, 7, 8, 9):
assert 'direct_sys=candidate' not in log, log
print('PASS: 10 SYS/AT probe scenarios (ASan/UBSan); no setter or diagnostic API linked')
+1 -2
View File
@@ -38,7 +38,6 @@ static int queued, disconnected, flags_reset, mutex_locked, player_mq_couter, va
static uint64_t now;
static unsigned int gSequenceNumber;
static int end_result;
static char sessionId[33] = "test-session";
typedef struct { unsigned int sequenceNumber; int packetType, dataSize; } AudioPacket;
static int ql_rtos_mutex_lock(ql_mutex_t m, unsigned int t) {
(void)m; (void)t; assert(!mutex_locked); mutex_locked = 1; return 0;
@@ -67,7 +66,7 @@ static void talkingq_ws_disconnect(void) {
static int sendRecordingAudioPacket(AudioPacket *p) { assert(p->packetType == 2); return end_result; }
'''
harness += source[state_start:state_end]
for name in ['voice_diag_snapshot', 'conversation_try_begin', 'sendEndPacket', 'conversation_check_timeout', 'conversation_accept_voice_message', 'conversation_finish_startup_prompt', 'conversation_find_session_id', 'role_notice_is_url', 'role_notice_start_locked', 'role_notice_poll_locked', 'app_role_notice_handle_url', 'app_role_notice_before_finish', 'role_notice_finished']:
for name in ['conversation_try_begin', 'sendEndPacket', 'conversation_check_timeout', 'conversation_accept_voice_message', 'conversation_finish_startup_prompt', 'conversation_find_session_id', 'role_notice_is_url', 'role_notice_start_locked', 'role_notice_poll_locked', 'app_role_notice_handle_url', 'app_role_notice_before_finish', 'role_notice_finished']:
harness += function(name)
harness += r'''
static void reset(void) {
-89
View File
@@ -1,89 +0,0 @@
"""Run the actual GNSS parser and power-state paths with logging off and on."""
from pathlib import Path
import re
import subprocess
import tempfile
root = Path(__file__).resolve().parents[1]
app = root / 'ql-application/threadx/interface/custom_app'
source = (app / 'gnss.c').read_text()
source = re.sub(r'^#include "(?!app_gnss_log.h")[^"]+"\n', '', source, flags=re.M)
harness = r'''
#include <assert.h>
#include <math.h>
#include <setjmp.h>
#include <stdarg.h>
#include <stdint.h>
#include <stdio.h>
#include <string.h>
typedef unsigned int u32;
typedef unsigned char u8;
typedef void *ql_task_t;
typedef enum { GNSS_MODE_FAST, GNSS_MODE_SLOW } gnss_mode_t;
typedef struct { double latitude, longitude; int is_valid; char raw_nmea[512]; } gps_location_info_t;
enum { AGPS_GNSS_ENABLE, APFLASH_GNSS_ENABLE, PACKET_MODE };
static unsigned int now;
static int scenario, opened, closed, printed;
static jmp_buf stop;
static unsigned int hal_os_up_time_ms(void) { return now; }
static int test_printf(const char *format, ...) { (void)format; printed++; return 0; }
#define printf test_printf
static int ql_gnss_close(void) { closed++; return 0; }
static int ql_gnss_open(void (*cb)(char *, int)) { (void)cb; opened++; return 0; }
static void app_set_app_operation(int value) { assert(value == 1); }
static void ql_gnss_cfgsys_set(int value) { (void)value; }
static void ql_gnss_cfgmsg_set(int value) { (void)value; }
static void ql_gnss_agps_cfg(int value, int extra) { (void)value; (void)extra; }
static void ql_gnss_apflash_cfg(int value) { (void)value; }
static void ql_set_gnss_nmea_mode(int value) { (void)value; }
static void ql_rtos_task_sleep_s(int seconds);
static void ql_rtos_task_create(ql_task_t *task, int stack, int priority, char *name, void (*entry)(void *), void *arg) {
(void)task; (void)stack; (void)priority; (void)name; (void)entry; (void)arg;
}
'''
harness += source
harness += r'''
#undef printf
static void ql_rtos_task_sleep_s(int seconds) {
if (closed) longjmp(stop, 1);
now += (unsigned int)seconds * 1000;
if (scenario == 1) g_gnss_sat_count = 2;
if (scenario == 2) { g_gnss_sat_count = 4; g_gps_location.is_valid = 1; }
assert(now < 1000000);
}
int main(void) {
(void)test_printf;
char data[] = "$GPGGA,120000,2311.71298,N,11328.63118,E,1,08,0.9,10,M,0,M,0,0*00\r\n"
"$GPGSV,1,1,08,01,10,10,30*00\r\n";
quec_gnss_callback(data, (int)strlen(data));
assert(g_gps_location.is_valid && g_gnss_sat_count == 8);
assert(fabs(g_gps_location.latitude - 23.1952163333) < 0.000001);
assert(fabs(g_gps_location.longitude - 113.4771863333) < 0.000001);
assert(strncmp(g_gps_location.raw_nmea, "$GPGGA,", 7) == 0);
print_gps_location_info();
set_gnss_mode(GNSS_MODE_FAST);
assert(get_gnss_mode() == GNSS_MODE_FAST && get_gnss_interval() == 180);
for (scenario = 0; scenario < 4; scenario++) {
now = 1; opened = closed = 0; gnss_is_open = 0;
g_gnss_mode = scenario == 3 ? GNSS_MODE_FAST : GNSS_MODE_SLOW;
if (!setjmp(stop)) quec_gnss_test(NULL);
assert(opened == 1 && closed == 1 && get_gnss_open_status() == 0);
}
#if APP_GNSS_LOG_ENABLED
assert(printed > 0);
#else
assert(printed == 0);
#endif
puts("PASS: GNSS parsing and all four hardware-close paths preserved; expected log visibility");
}
'''
with tempfile.TemporaryDirectory(prefix='banban-gnss-log-') as directory:
cfile = Path(directory) / 'gnss-test.c'
cfile.write_text(harness)
for enabled in (0, 1):
exe = Path(directory) / f'gnss-test-{enabled}'
subprocess.run(['cc', '-std=c99', '-Wall', '-Wextra', '-Werror',
'-Wno-unused-parameter', '-Wno-unused-variable', '-Wno-sign-compare',
'-fsanitize=address,undefined', f'-DAPP_GNSS_LOG_ENABLED={enabled}',
'-I' + str(app / 'inc'), str(cfile), '-o', str(exe)], check=True)
subprocess.run([str(exe)], check=True)
+146
View File
@@ -0,0 +1,146 @@
"""Host behavioral checks for the actual diagnostic modules; no device required."""
from pathlib import Path
import re
import shutil
import subprocess
import tempfile
ROOT = Path(__file__).resolve().parents[1]
APP = ROOT / 'ql-application/threadx/interface/custom_app'
KERNEL = ROOT / 'ql-kernel/threadx/api_map/talkingq_time_diag.c'
def api_hash(name):
value = 0
for byte in name.encode():
term = (value * 5527 + byte * 7) & 0xffffffff
value = (term ^ ((term & 65535) ** 2)) & 0xffffffff
return value
assert api_hash('ql_user_api_map_test') == 0x60BCEA5A
abi = (ROOT / 'ql-config/config/talkingq_time_diag.h').read_text()
hash_value = int(re.search(r'API_HASH 0x([0-9A-F]+)', abi)[1], 16)
assert api_hash('talkingq_time_diag_snapshot_v1') == hash_value
table = (ROOT / 'ql-kernel/threadx/api_map/ql_user_api_map.c').read_text()
assert f'{hash_value:08x}' not in table.lower()
with tempfile.TemporaryDirectory(prefix='time-diag-') as directory:
work = Path(directory)
headers = {
'ql_func.h': '#include <stdint.h>\nextern intptr_t (*m_get_api_ptr)(const char *);\n',
'ql_rtos.h': 'unsigned int ql_rtos_get_systicks(void);\n',
'ql_dev.h': 'int ql_dev_get_firmware_version(char *, unsigned int);\n',
'build_macro.h': '#define CUSTOM_APP_BUILD_VER "test"\n#define CUSTOM_APP_BUILD_TIME "test"\n#define CUSTOM_APP_BUILD_REV "test"\n',
'ql_rtc.h': 'typedef struct { int tm_sec,tm_min,tm_hour,tm_mday,tm_mon,tm_year,tm_wday; } ql_rtc_time_t;\nint ql_rtc_get_time(ql_rtc_time_t *);\n',
'ql_type.h': '',
'quec_cust_feature.h': '',
'pmic.h': 'typedef int PMIC_RTC_OFFSET_STORAGE;\nunsigned int PM812_RTC_GET_32B_SYS_OFFSET(void), PM812_RTC_GET_32B_APP_OFFSET(void), PM812_RTC_GET_32B_COUNTER(void);\n',
'pmic_rtc.h': 'int pmic_rtc_INIT_FLAG_GET(void), pmic_userdata_get_app_rtc_ever_configed(void), PMIC_RTC_GetTimezone(void);\n',
'quec_rtc.h': 'int GetTimeZone(void), quec_get_rtc_source(void);\n',
}
for name, content in headers.items():
(work / name).write_text(content)
(work / 'harness.c').write_text(r'''
#include <assert.h>
#include <stdint.h>
#include <stdio.h>
#include <string.h>
#include <time.h>
#include "talkingq_time_diag.h"
#include "time_diag.h"
#include "ql_rtc.h"
unsigned char m_rtc_app_sys_keeping_synced;
int m_pmic_rtc_offset_storage_type;
char ntp_sync_cnt = 3, ntp_dns_gain = 1;
static int initialized, raw_reads, api_mode, sample_index, write_pointer;
static time_t samples[2];
unsigned int ql_rtos_get_systicks(void) { return 123; }
int pmic_rtc_INIT_FLAG_GET(void) { return initialized; }
int pmic_userdata_get_app_rtc_ever_configed(void) { return 1; }
unsigned int PM812_RTC_GET_32B_COUNTER(void) { ++raw_reads; return 0xfffffff0U; }
unsigned int PM812_RTC_GET_32B_SYS_OFFSET(void) { ++raw_reads; return 100; }
unsigned int PM812_RTC_GET_32B_APP_OFFSET(void) { ++raw_reads; return 50; }
int PMIC_RTC_GetTimezone(void) { return 8; }
int GetTimeZone(void) { return 480; }
int quec_get_rtc_source(void) { return 1; }
int get_ntp_time_type(void) { return 0; }
int GetNTPStatus(void) { return 1; }
int talkingq_ntp_diag_ready(void) { return 1; }
int quec_get_rtc_status(void) { return 1; }
int quec_get_nw_status(void) { return 2; }
char *get_ntp_server_ip(void) { return "192.0.2.1"; }
int ql_dev_get_firmware_version(char *out, unsigned int size) {
snprintf(out, size, "test-firmware"); return 0;
}
int ql_rtc_get_time(ql_rtc_time_t *out) { (void)out; return -1; }
time_t time(time_t *out) {
time_t value;
assert(sample_index < 2);
value = samples[sample_index++];
if (out && write_pointer) *out = value;
return value;
}
static int bad_snapshot(talkingq_time_diag_snapshot *out, unsigned int size) {
(void)size; out->abi_version = 99; return 0;
}
static intptr_t lookup(const char *name) {
assert(strcmp(name, TALKINGQ_TIME_DIAG_API_NAME) == 0);
if (api_mode == 0) return 0;
if (api_mode == 1) return (intptr_t)bad_snapshot;
return (intptr_t)talkingq_time_diag_snapshot_v1;
}
intptr_t (*m_get_api_ptr)(const char *) = lookup;
int main(void) {
talkingq_time_diag_snapshot s;
assert(sizeof(s) == 108);
assert(talkingq_time_diag_snapshot_v1(NULL, sizeof(s)) == -1);
assert(talkingq_time_diag_snapshot_v1(&s, sizeof(s)-1) == -1);
assert(talkingq_time_diag_snapshot_v1(&s, sizeof(s)) == 0);
assert(!s.rtc_initialized && raw_reads == 0);
initialized = 1;
assert(talkingq_time_diag_snapshot_v1(&s, sizeof(s)) == 0);
assert(raw_reads == 3 && s.sys_offset == 100 && s.app_offset == 50);
assert(s.counter + s.sys_offset == 84 && s.counter + s.app_offset == 34);
assert(s.app_follows_sys == 0 && m_rtc_app_sys_keeping_synced == 0);
assert(s.network_tz_minutes == 480 && s.rtc_tz_hours == 8);
#ifdef NTP
assert(s.ntp_ready == 1 && s.ntp_status == 1);
#else
assert(s.ntp_ready == -1 && s.ntp_status == -1);
#endif
#ifdef QUEC_GNSS_NTP
assert(s.gnss_cid == 2 && strcmp(s.ntp_server, "192.0.2.1") == 0);
#else
assert(s.gnss_cid == -1 && s.ntp_server[0] == 0);
#endif
talkingq_time_diag_boot(); /* old kernel */
api_mode = 1; talkingq_time_diag_log("bad-abi");
api_mode = 2; talkingq_time_diag_log("divergent");
samples[0] = 100; samples[1] = 1800000000;
assert(talkingq_time_diag_wss_time() == 100);
samples[0] = 1800000000; samples[1] = 100;
sample_index = 0; write_pointer = 1;
assert(talkingq_time_diag_wss_time() == 1800000000);
assert(m_rtc_app_sys_keeping_synced == 0);
talkingq_time_diag_trace("kernel-check");
puts("PASS");
return 0;
}
''')
compiler = shutil.which('clang') or shutil.which('cc')
if not compiler:
raise SystemExit('A native C compiler is required')
for features in ([], ['-DNTP'], ['-DNTP', '-DQUEC_GNSS_NTP']):
command = [compiler, '-std=c99', '-Wall', '-Wextra', '-Werror',
'-I', str(work), '-I', str(APP / 'inc'),
'-I', str(ROOT / 'ql-config/config'), *features,
str(APP / 'time_diag.c'), str(KERNEL), str(work / 'harness.c'),
'-o', str(work / 'test')]
subprocess.run(command, check=True)
log = subprocess.check_output([str(work / 'test')], text=True)
for expected in ('kernel_diag=unavailable', 'kernel_diag=abi_mismatch',
'sys_raw=84 app_raw=34', 'time_ptr_value=1',
'time_ptr_value=100', 'valid=0', 'valid=1', 'PASS'):
assert expected in log, expected
print('PASS:', ' '.join(features) or 'optional features off')
-112
View File
@@ -1,112 +0,0 @@
"""Compile actual microphone, queue and receive diagnostics with scripted inputs."""
from pathlib import Path
import subprocess
import tempfile
root = Path(__file__).resolve().parents[1]
app = root / 'ql-application/threadx/interface/custom_app'
voice = (app / 'ws_talkingq.c').read_text()
mic = (app / 'vad_det.c').read_text()
def function(source, signature):
start = source.index(signature)
opening = source.index('{', start)
depth, end = 1, opening + 1
while depth:
depth += (source[end] == '{') - (source[end] == '}')
end += 1
return source[start:end] + '\n'
harness = r'''
#include <assert.h>
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#define VAD_FRAME_BYTES 640
#define DEVICE_ID_SIZE 32
#define SESSION_ID_SIZE 33
#define RLOGE(...) ((void)0)
typedef int rws_socket;
static int g_mic_working;
static int g_state, ring_result = 640, fail_alloc, queue_result, dispatched;
static unsigned int conversation_round = 7;
static int active_session_valid = 1;
static char sessionId[33] = "session", deviceId[32] = "device";
static void *ring_sender;
static uint64_t now = 1000;
static int mutex_locked;
static uint64_t hal_os_up_time_ms(void) { return now; }
static void conversation_lock(void) { assert(!mutex_locked); mutex_locked = 1; }
static void conversation_unlock(void) { assert(mutex_locked); mutex_locked = 0; }
static int hal_os_rb_write(void *ring, const void *data, int len, unsigned int timeout) {
(void)ring; (void)data; (void)len; assert(timeout == 25); return ring_result;
}
static void *test_malloc(size_t size) { return fail_alloc ? NULL : malloc(size); }
static int talkingq_ws_send_bin(uint8_t *data, int len) {
uint32_t seq;
assert(len >= DEVICE_ID_SIZE + SESSION_ID_SIZE + 12);
memcpy(&seq, data + DEVICE_ID_SIZE + SESSION_ID_SIZE, 4);
assert(seq == 9);
return queue_result;
}
typedef struct { uint32_t sequenceNumber, packetType, dataSize; uint8_t *data; } AudioPacket;
static int conversation_accept_voice_message(const char *text) { return strstr(text, "old") == NULL; }
static void conversation_log_error_message(const char *text, unsigned int len) { (void)text; (void)len; }
static void onmessage_text_locked(rws_socket socket, const char *text, unsigned int len) {
(void)socket; (void)text; (void)len; assert(mutex_locked); dispatched++;
}
'''
start = voice.index('static struct {')
end = voice.index('} voice_diag;', start) + len('} voice_diag;')
harness += voice[start:end] + '\n'
start = mic.index('static struct {')
end = mic.index('extern int audio_packet_type;', start)
harness += mic[start:end]
harness += '#define malloc test_malloc\n'
harness += function(voice, 'int sendRecordingAudioPacket(AudioPacket *packet) {')
harness += '#undef malloc\n'
harness += function(voice, 'static void onmessage_text(rws_socket socket,')
harness += function(voice, 'static void onmessage_bin(rws_socket socket,')
harness += r'''
int main(void) {
uint8_t pcm[640] = {0};
AudioPacket packet = {9, 0, 4, pcm};
mic_diag.started_ms = now;
mic_diag_read(pcm, 640, 0);
assert(mic_diag.reads == 1 && mic_diag.zero_frames == 1 && mic_diag.peak == 0);
/* Full-scale negative input must avoid int16 absolute-value overflow. */
pcm[0] = 0; pcm[1] = 128;
mic_diag_read(pcm, 640, 0);
assert(mic_diag.peak == 32768 && mic_diag.abs_sum == 32768 && mic_diag.samples == 640);
mic_diag_read(pcm, 0, -1);
assert(mic_diag.short_reads == 1 && mic_diag.read_errors == 1 && mic_diag.zero_frames == 1);
mic_diag_read(pcm, 10000, 0); /* Diagnostic sampling stays inside the buffer. */
assert(mic_diag.samples == 960 && mic_diag.short_reads == 2);
ring_result = 640; assert(mic_diag_write(pcm, 640) == 640 && !mic_diag.ring_drops);
ring_result = -1; assert(mic_diag_write(pcm, 640) == -1);
ring_result = 320; assert(mic_diag_write(pcm, 640) == 320);
assert(mic_diag.ring_drops == 2 && mic_diag.ring_drop_bytes == 960);
mic_diag_log("test");
queue_result = 0; assert(sendRecordingAudioPacket(&packet) == 0);
assert(voice_diag.tx_packets == 1 && voice_diag.tx_bytes == 4 && !voice_diag.tx_failed);
queue_result = -1; assert(sendRecordingAudioPacket(&packet) == -1);
fail_alloc = 1; assert(sendRecordingAudioPacket(&packet) == -1);
assert(voice_diag.tx_packets == 1 && voice_diag.tx_bytes == 4 && voice_diag.tx_failed == 2);
onmessage_text(0, "START_ACK", 9);
onmessage_text(0, "END_ACK", 7);
onmessage_text(0, "unknown backend response", 24);
onmessage_text(0, "old TTS_START", 13);
assert(voice_diag.start_ack == 1 && voice_diag.end_ack == 1 && voice_diag.rx_text == 4);
assert(dispatched == 3 && !mutex_locked);
now = 2000; onmessage_bin(0, pcm, 640);
assert(voice_diag.rx_bin == 1 && voice_diag.last_rx_ms == now && !mutex_locked);
puts("PASS: microphone silence/level/short reads/errors, PCM drops, queue failure/allocation failure, ACK visibility, unknown/rejected replies, ignored binary replies");
}
'''
with tempfile.TemporaryDirectory(prefix='banban-voice-diag-') as directory:
cfile = Path(directory) / 'diag.c'
exe = Path(directory) / 'diag-test'
cfile.write_text(harness)
subprocess.run(['cc', '-std=c99', '-Wall', '-Wextra', '-Werror', '-fsanitize=address,undefined', str(cfile), '-o', str(exe)], check=True)
subprocess.run([str(exe)], check=True)
+2 -6
View File
@@ -1,7 +1,6 @@
"""Compile actual SDK-independent function bodies with scripted TLS/RTOS stubs."""
from pathlib import Path
import subprocess
import os
root = Path(__file__).resolve().parents[1]
lib = root / 'ql-application/threadx/interface/custom_app/lib/librws/src'
@@ -76,9 +75,6 @@ output.mkdir(parents=True, exist_ok=True)
cfile = output / 'test_wss_io.c'
cfile.write_text(harness, encoding='utf-8')
exe = output / 'test_wss_io.exe'
if os.name == "nt":
subprocess.run(['cl', '/nologo', '/W4', '/WX', '/TC', str(cfile),
'/Fo' + str(output / 'test_wss_io.obj'), '/Fe' + str(exe)], check=True)
else:
subprocess.run(['cc', '-std=c99', '-Wall', '-Wextra', '-Werror', str(cfile), '-o', str(exe)], check=True)
subprocess.run(['cl', '/nologo', '/W4', '/WX', '/TC', str(cfile),
'/Fo' + str(output / 'test_wss_io.obj'), '/Fe' + str(exe)], check=True)
subprocess.run([str(exe)], check=True)
+3 -8
View File
@@ -1,7 +1,6 @@
"""Exercise the real receive/upgrade loop with delayed TLS records."""
from pathlib import Path
import subprocess
import os
root = Path(__file__).resolve().parents[1]
lib = root / 'ql-application/threadx/interface/custom_app/lib/librws'
@@ -38,7 +37,6 @@ typedef struct {
char *payload_buff, *received;
size_t received_size, received_len;
int socket, command, is_connected;
unsigned int diag_rx_calls, diag_rx_bytes;
const char *error;
} _rws_socket;
static int replies[16], reply_count, calls, closes;
@@ -122,10 +120,7 @@ output.mkdir(parents=True, exist_ok=True)
cfile = output / 'test_wss_receive.c'
cfile.write_text(harness, encoding='utf-8')
exe = output / 'test_wss_receive.exe'
if os.name == "nt":
subprocess.run(['cl', '/nologo', '/W4', '/WX', '/wd4018', '/TC',
'/I' + str(lib / 'inc'), str(cfile), str(lib / 'src/rws_handshake.c'),
'/Fo' + str(output) + '/', '/Fe' + str(exe)], check=True)
else:
subprocess.run(['cc', '-std=c99', '-Wall', '-Wextra', '-Werror', '-Wno-sign-compare', '-I' + str(lib / 'inc'), str(cfile), str(lib / 'src/rws_handshake.c'), '-o', str(exe)], check=True)
subprocess.run(['cl', '/nologo', '/W4', '/WX', '/wd4018', '/TC',
'/I' + str(lib / 'inc'), str(cfile), str(lib / 'src/rws_handshake.c'),
'/Fo' + str(output) + '/', '/Fe' + str(exe)], check=True)
subprocess.run([str(exe)], check=True)