From bf36b60d82e0d1eaf91cacdb5bfcdf41fa334e9d Mon Sep 17 00:00:00 2001 From: cxh Date: Thu, 22 Jan 2026 15:47:17 +0800 Subject: [PATCH] =?UTF-8?q?=E4=BF=AE=E6=94=B9=E8=A7=A3=E6=9E=90=E9=80=BB?= =?UTF-8?q?=E8=BE=91?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/serial_AT.cpp | 159 ++++++++++++++++++++++++++++++++++------------ 1 file changed, 119 insertions(+), 40 deletions(-) diff --git a/src/serial_AT.cpp b/src/serial_AT.cpp index a58b721..aa2a150 100644 --- a/src/serial_AT.cpp +++ b/src/serial_AT.cpp @@ -39,6 +39,122 @@ static std::mutex at_tasks_mutex; static std::unique_ptr http_server; static std::thread http_thread; +// ---------- 安全 stoi ---------- +static bool safe_stoi(const std::string& s, int& v) +{ + try + { + size_t idx; + v = std::stoi(s, &idx); + return idx == s.size(); + } + catch (...) + { + return false; + } +} + +// ---------- split CSV ---------- +static std::vector split_csv(const std::string& line) +{ + std::vector out; + std::string cur; + std::stringstream ss(line); + + while (std::getline(ss, cur, ',')) + { + // 去引号和空白 + cur.erase(std::remove(cur.begin(), cur.end(), '"'), cur.end()); + while (!cur.empty() && isspace(cur.front())) cur.erase(cur.begin()); + while (!cur.empty() && isspace(cur.back())) cur.pop_back(); + out.push_back(cur); + } + return out; +} + +// ---------- 解析入口 ---------- +static void parse_qeng_servingcell(const std::string& line) +{ + auto t = split_csv(line); + if (t.size() < 5) return; + + // t[0] = +QENG: + // t[1] = servingcell + if (t[0].find("+QENG") != 0) return; + if (t[1] != "servingcell") return; + + // t[2] = CONNECT/NOCONN + // t[3] = RAT: LTE / NR5G-SA / NR5G-NSA + std::string rat = t[3]; + + int pci = -1, rsrp = 0, rsrq = 0, sinr = 0, arfcn = 0; + + // ------------------------------------------ + // NR5G SA 模式字段表(你的情况) + // ------------------------------------------ + // +QENG:"servingcell","CONNECT","NR5G-SA","TDD", + // 460,00, , TAC, ARFCN, PCI, + // SSB, Beam, RSRP, RSRQ, SINR, ... + // + // 索引: 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 + // + if (rat == "NR5G-SA") + { + if (t.size() >= 15) + { + safe_stoi(t[9], pci); + safe_stoi(t[8], arfcn); + safe_stoi(t[12], rsrp); + safe_stoi(t[13], rsrq); + safe_stoi(t[14], sinr); + } + } + + // ------------------------------------------ + // LTE 模式字段表(兼容) + // ------------------------------------------ + // LTE 一般字段:...,PCI= t[6], RSRP=t[11], RSRQ=t[12], SINR=t[14] + // + else if (rat == "LTE") + { + if (t.size() >= 15) + { + safe_stoi(t[6], pci); + safe_stoi(t[11], rsrp); + safe_stoi(t[12], rsrq); + safe_stoi(t[14], sinr); + } + } + + // ------------------------------------------ + // NSA 模式字段表(简单支持) + // ------------------------------------------ + else if (rat == "NR5G-NSA") + { + // NSA 包含 LTE + NR 两部分字段,先取 LTE anchor + if (t.size() >= 15) + { + safe_stoi(t[6], pci); + safe_stoi(t[11], rsrp); + safe_stoi(t[12], rsrq); + safe_stoi(t[14], sinr); + } + } + + { + std::lock_guard lk(radio_mutex); + g_radio_info.pci = pci; + g_radio_info.rsrp = rsrp; + g_radio_info.rsrq = rsrq; + g_radio_info.sinr = sinr; + g_radio_info.arfcn = arfcn; + g_radio_info.raw = line; + } + + LOG_INFO("[serial_at] QENG parsed: RAT=" + rat + " PCI=" + std::to_string(pci) + " RSRP=" + std::to_string(rsrp) + + " RSRQ=" + std::to_string(rsrq) + " SINR=" + std::to_string(sinr)); +} + static bool is_imei_ready() { std::lock_guard lock(imei_mutex); @@ -203,47 +319,10 @@ static void handle_serial_at_data(const std::string& data) // ======================= // QENG="servingcell" 解析 // ======================= - if (line.rfind("+QENG:", 0) == 0 || line.find("servingcell") != std::string::npos) + if (line.find("+QENG:") == 0) { - std::vector tokens; - std::string tmp; - std::stringstream ss(line); - - while (std::getline(ss, tmp, ',')) - { - // 去除双引号和空白 - tmp.erase(std::remove(tmp.begin(), tmp.end(), '"'), tmp.end()); - tmp.erase(0, tmp.find_first_not_of(" \t")); - tmp.erase(tmp.find_last_not_of(" \t") + 1); - tokens.push_back(tmp); - } - - // LTE 格式固定至少 15~17 字段 - if (tokens.size() >= 15) - { - // 对号入座 - int pci = std::stoi(tokens[6]); // 物理小区编号 - int band = std::stoi(tokens[8]); // LTE band - int rsrp = std::stoi(tokens[11]); // dBm - int rsrq = std::stoi(tokens[12]); // dB - int sinr = std::stoi(tokens[14]); // dB - - { - std::lock_guard lk(radio_mutex); - g_radio_info.pci = pci; - g_radio_info.band = band; - g_radio_info.rsrp = rsrp; - g_radio_info.rsrq = rsrq; - g_radio_info.sinr = sinr; - g_radio_info.raw = line; // 保留原始字符串用于调试 - } - - LOG_INFO("[serial_at] QENG parsed: RSRP=" + std::to_string(rsrp) + " RSRQ=" + std::to_string(rsrq) + - " SINR=" + std::to_string(sinr) + " PCI=" + std::to_string(pci) + - " BAND=" + std::to_string(band)); - - continue; - } + parse_qeng_servingcell(line); + continue; } } }