diff --git a/docs/voice_diagnostics.md b/docs/voice_diagnostics.md index d7ebf8a..0c4e6a4 100644 --- a/docs/voice_diagnostics.md +++ b/docs/voice_diagnostics.md @@ -27,3 +27,9 @@ 即使 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 的关闭路径。 diff --git a/ql-application/threadx/interface/custom_app/gnss.c b/ql-application/threadx/interface/custom_app/gnss.c index a2e0682..8e22bd2 100644 --- a/ql-application/threadx/interface/custom_app/gnss.c +++ b/ql-application/threadx/interface/custom_app/gnss.c @@ -15,6 +15,7 @@ #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 @@ -211,10 +212,7 @@ void quec_gnss_callback(char *data, int len) char nmea_buffer[512]; int fix_status = 0; - // 打印原始 NMEA 数据 - for (i = 0; i < len; i++) { - printf("%c", data[i]); - } + APP_GNSS_LOG("%.*s", len, data); // 在 data 中定位 GGA 句子的起止位置 // GGA 格式: $xxGGA,...,*CS\r\n @@ -267,7 +265,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; - printf("\n[GNSS Parse failed] FS=%d, %s\n", + APP_GNSS_LOG("\n[GNSS Parse failed] FS=%d, %s\n", fix_status, (fix_status == 0) ? "No Fix (Invalid)" : "Format error"); } @@ -321,7 +319,7 @@ void quec_gnss_callback(char *data, int len) void set_gnss_mode(gnss_mode_t mode) { g_gnss_mode = mode; - printf("[GNSSMode] Set to %s Mode\n", + APP_GNSS_LOG("[GNSSMode] Set to %s Mode\n", (mode == GNSS_MODE_FAST) ? "Fast (30s)" : "Slow (5min)"); } @@ -353,11 +351,11 @@ gps_location_info_t* get_gps_location_info(void) void print_gps_location_info(void) { if (g_gps_location.is_valid) { - printf("[GPSinfo] Latitude: %.8f, Longitude: %.8f\n", + APP_GNSS_LOG("[GPSinfo] Latitude: %.8f, Longitude: %.8f\n", g_gps_location.latitude, g_gps_location.longitude); - printf("[GPSinfo] rawNMEA: %s\n", g_gps_location.raw_nmea); + APP_GNSS_LOG("[GPSinfo] rawNMEA: %s\n", g_gps_location.raw_nmea); } else { - printf("[GPSinfo] No valid position data\n"); + APP_GNSS_LOG("[GPSinfo] No valid position data\n"); } } @@ -395,26 +393,29 @@ static void quec_gnss_test(void * argv) /* ========== FAST MODE ========== */ if (fast_mode_start_time == 0) { fast_mode_start_time = hal_os_up_time_ms(); - printf("[GNSS] Enter FAST mode\n"); + APP_GNSS_LOG("[GNSS] Enter FAST mode\n"); } if (!gnss_is_open) { gnss_is_open = 1; app_set_app_operation(1); ql_gnss_open(quec_gnss_callback); - printf("[GNSS] FAST: GPS OPEN\n"); + APP_GNSS_LOG("[GNSS] FAST: GPS OPEN\n"); } // 5分钟超时切SLOW if (hal_os_up_time_ms() - fast_mode_start_time >= GNSS_FAST_MODE_HOLD_TIME) { - printf("[GNSS] FAST 5min timeout -> SLOW\n"); + APP_GNSS_LOG("[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; - printf("[GNSS] SLOW: GPS CLOSED ret:%d\n", ql_gnss_close()); + + int close_ret = ql_gnss_close(); + (void)close_ret; + APP_GNSS_LOG("[GNSS] SLOW: GPS CLOSED ret:%d\n", close_ret); } } else @@ -426,12 +427,12 @@ static void quec_gnss_test(void * argv) if (slow_mode_start_time == 0) { slow_mode_start_time = now; slow_next_wake_time = now; // 立即尝试第一次定位 - printf("[GNSS] Enter SLOW mode (fixed 15min interval)\n"); + APP_GNSS_LOG("[GNSS] Enter SLOW mode (fixed 15min interval)\n"); } // 检查是否到达唤醒时间 if (now < slow_next_wake_time) { - printf("[GNSS] SLOW: next wake in %d s\n", + APP_GNSS_LOG("[GNSS] SLOW: next wake in %d s\n", (slow_next_wake_time - now) / 1000); } else if (!gnss_is_open) { @@ -444,7 +445,7 @@ static void quec_gnss_test(void * argv) app_set_app_operation(1); ql_gnss_open(quec_gnss_callback); - printf("[GNSS] SLOW: GPS OPEN, search start\n"); + APP_GNSS_LOG("[GNSS] SLOW: GPS OPEN, search start\n"); /* ---- 第一阶段:60秒卫星检测 ---- */ int sat_found = 0; @@ -454,7 +455,7 @@ static void quec_gnss_test(void * argv) elapsed++; if (g_gnss_sat_count > 1) { sat_found = 1; - printf("[GNSS] SLOW: Sat found! count=%d, after %ds\n", + APP_GNSS_LOG("[GNSS] SLOW: Sat found! count=%d, after %ds\n", g_gnss_sat_count, elapsed); break; } @@ -462,10 +463,13 @@ static void quec_gnss_test(void * argv) if (!sat_found) { // 60秒无卫星 → 立即关闭,等待15分钟 - printf("[GNSS] SLOW: No satellite in 30s, stop ret:%d.\n", ql_gnss_close()); + + int close_ret = ql_gnss_close(); + (void)close_ret; + APP_GNSS_LOG("[GNSS] SLOW: No satellite in 30s, stop ret:%d.\n", close_ret); gnss_is_open = 0; slow_next_wake_time = search_start_time + (GNSS_SEARCH_INTERVAL * 1000); - printf("[GNSS] SLOW: GPS CLOSED, next search at +%ds\n", GNSS_SEARCH_INTERVAL); + APP_GNSS_LOG("[GNSS] SLOW: GPS CLOSED, next search at +%ds\n", GNSS_SEARCH_INTERVAL); continue; } @@ -478,17 +482,20 @@ static void quec_gnss_test(void * argv) // 1.5分钟时刻检查卫星数 int sat_count_now = g_gnss_sat_count; - printf("[GNSS] SLOW: 1min check, satellite count = %d\n", sat_count_now); + APP_GNSS_LOG("[GNSS] SLOW: 1min check, satellite count = %d\n", sat_count_now); if (sat_count_now < GNSS_SAT_MIN_COUNT) { - printf("[GNSS] SLOW: Sat count < %d, stop search ret:%d.\n", GNSS_SAT_MIN_COUNT,ql_gnss_close()); + + 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); gnss_is_open = 0; slow_next_wake_time = search_start_time + (GNSS_SEARCH_INTERVAL * 1000); - printf("[GNSS] SLOW: GPS CLOSED, next search at +%ds\n", GNSS_SEARCH_INTERVAL); + APP_GNSS_LOG("[GNSS] SLOW: GPS CLOSED, next search at +%ds\n", GNSS_SEARCH_INTERVAL); continue; } /* ---- 第三阶段:卫星足够,继续定位,但总时间不超过3分钟 ---- */ - printf("[GNSS] SLOW: Sat count OK, continue for fix (max %ds total)\n", + APP_GNSS_LOG("[GNSS] SLOW: Sat count OK, continue for fix (max %ds total)\n", GNSS_MAX_SEARCH_TIME); // 继续等待直到总时间达到 GNSS_MAX_SEARCH_TIME (180秒) @@ -498,28 +505,30 @@ static void quec_gnss_test(void * argv) // 如果获得有效定位,打印提示(但继续等待至3分钟) if (g_gps_location.is_valid) { - printf("[GNSS] SLOW: Fix obtained at %ds!\n", elapsed); + APP_GNSS_LOG("[GNSS] SLOW: Fix obtained at %ds!\n", elapsed); print_gps_location_info(); // 若希望在定位成功后立即关闭,可在此 break,但此处按“最长3分钟”处理 } } // 3分钟时间到,关闭GPS - printf("[GNSS] SLOW: Max search time reached, stop.\n"); + APP_GNSS_LOG("[GNSS] SLOW: Max search time reached, stop.\n"); if (g_gps_location.is_valid) { - printf("[GNSS] SLOW: Final valid position:\n"); + APP_GNSS_LOG("[GNSS] SLOW: Final valid position:\n"); print_gps_location_info(); } else { - printf("[GNSS] SLOW: No valid fix within 3min.\n"); + APP_GNSS_LOG("[GNSS] SLOW: No valid fix within 3min.\n"); } - printf("[GNSS] SLOW: GPS CLOSED ret:%d",ql_gnss_close()); + int close_ret = ql_gnss_close(); + (void)close_ret; + APP_GNSS_LOG("[GNSS] SLOW: GPS CLOSED ret:%d", close_ret); gnss_is_open = 0; g_gps_location.is_valid = 0; // 重置状态 // 设置下次唤醒时间为 15分钟 后 slow_next_wake_time = search_start_time + (GNSS_SEARCH_INTERVAL * 1000); - printf("[GNSS] SLOW: GPS CLOSED, next search at +%ds\n", GNSS_SEARCH_INTERVAL); + APP_GNSS_LOG("[GNSS] SLOW: GPS CLOSED, next search at +%ds\n", GNSS_SEARCH_INTERVAL); } // else: GPS正在运行中(理论上不会到这里) } diff --git a/ql-application/threadx/interface/custom_app/inc/app_gnss_log.h b/ql-application/threadx/interface/custom_app/inc/app_gnss_log.h new file mode 100644 index 0000000..19de6c3 --- /dev/null +++ b/ql-application/threadx/interface/custom_app/inc/app_gnss_log.h @@ -0,0 +1,18 @@ +#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 +#define APP_GNSS_LOG(...) printf(__VA_ARGS__) +#else +#define APP_GNSS_LOG(...) ((void)0) +#endif + +#endif diff --git a/ql-application/threadx/interface/custom_app/mqtt.c b/ql-application/threadx/interface/custom_app/mqtt.c index 483cefe..5f3f410 100644 --- a/ql-application/threadx/interface/custom_app/mqtt.c +++ b/ql-application/threadx/interface/custom_app/mqtt.c @@ -9,6 +9,7 @@ #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" @@ -442,7 +443,7 @@ int parse_command(const char *json_str, command_t *cmd) if (params != NULL) { switch (cmd->type) { case MSG_GPS_QUERY: - printf("MSG_GPS_QUERY\r\n"); + APP_GNSS_LOG("MSG_GPS_QUERY\r\n"); /*返回当前的GPS坐标信息*/ break; diff --git a/tools/test_gnss_logging.py b/tools/test_gnss_logging.py new file mode 100644 index 0000000..a25b20f --- /dev/null +++ b/tools/test_gnss_logging.py @@ -0,0 +1,89 @@ +"""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 +#include +#include +#include +#include +#include +#include +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)