# gnss_console **Repository Path**: babakara/gnss_console ## Basic Information - **Project Name**: gnss_console - **Description**: 组合导航数据解析并存bin文件 - **Primary Language**: Unknown - **License**: Not specified - **Default Branch**: master - **Homepage**: None - **GVP Project**: No ## Statistics - **Stars**: 0 - **Forks**: 0 - **Created**: 2026-07-09 - **Last Updated**: 2026-07-10 ## Categories & Tags **Categories**: Uncategorized **Tags**: None ## README # 环境配置 ```bash # 1. 更新基础软件源 sudo apt update sudo apt install software-properties-common # 2. 添加 Ubuntu Toolchain PPA 源 (包含最新版 GCC) sudo add-apt-repository ppa:ubuntu-toolchain-r/test sudo apt update # 3. 安装 GCC 12 和 G++ 12 sudo apt install gcc-12 g++-12 # 将 gcc-12 和 g++-12 注册到系统的候选项中,并赋予较高的优先级 (100) sudo update-alternatives --install /usr/bin/gcc gcc /usr/bin/gcc-12 100 \ --slave /usr/bin/g++ g++ /usr/bin/g++-12 # 过以下命令检查是否切换成功 gcc --version g++ --version ``` 重新用新版本进行编译 ```bash # 1. 彻底干掉旧的构建缓存 rm -rf build/ .cache/ # 2. 重新配置,并强制 CMake 使用刚刚安装的 GCC 12 cmake -B build \ -DCMAKE_EXPORT_COMPILE_COMMANDS=ON \ -DCMAKE_C_COMPILER=/usr/bin/gcc-12 \ -DCMAKE_CXX_COMPILER=/usr/bin/g++-12 # 3. 编译你的工程 cmake --build build -j$(nproc) ``` 重启clang shift+ctrl+p,选择clangd restart server # 运行说明 GPS数据10Hz刷新率,UTC时间读出来最小间隔为秒,可能多帧数据是同一个base_time,因此另加了recv_timestamp_ms记录计算机时间,最小间隔为毫秒。 IMU数据200Hz刷新率,使用计算机时间,最小间隔为毫秒。 执行文件在build文件夹中,SensorFusion, 数据保存在sensor_data.bin文件中,可以直接读出结构体,结构体做了一点调整,GPS增加了recv_timestamp_ms,如下: ![alt text](images/image.png) ```cpp // gps struct GPS { double gnss_nm = 0.0; // 纬度 deg double gnss_em = 0.0; // 经度 deg double gnss_zm = 0.0; // 海拔高 m time_t base_time; // 时间戳 utc 1980 年 1 月 6 日 00:00:00 UTC。自动读取卫星下发的“闰秒修正参数”,在内部把快了 18 秒的 GPS Time 减去 18 秒,转换成标准的 UTC 时间。本程序不用再次减掉 uint64_t recv_timestamp_ms = 0; // 系统接收到该数据的本地Unix时间戳 (毫秒级)1970年1月1日 00:00:00 UTC(世界协调时)。 }; struct IMU { uint64_t unix_timestamp_ms = 0; // unix double roll = 0.0; // 横滚角 deg double pitch = 0.0; // 俯仰角 deg double heave = 0.0; // 升沉 m double heading = 0.0; // 航向 deg }; ``` 运行输出如下: ![alt text](images/image02.png) # cpp串口通信库 ```bash sudo apt install libboost-all-dev ``` # terminal 调试串口 ```bash dmesg | grep ttyUSB # 或使用 ls /dev/ttyUSB* # 查看所有串口 可能被识别为虚拟串口ACM ls /dev/ttyUSB* /dev/ttyACM* ``` ```bash # 输入后插拔设备可以看到设备号被拔掉和插上的日志 dmesg -w ``` ```bash # output serialport recv data flow cat /dev/ttyACM0 ``` 为什么 cat 默认没有输出乱码? 对于传统的物理串口(如 ttyUSB 对应的 RS-232/RS-485 硬件引脚),波特率如果不匹配,数据必乱码。 但您的设备是 ttyACM0(USB CDC-ACM 虚拟串口),它的底层逻辑完全不同:没有真实的波特率概念:ttyACM0 本质上是 USB 块传输(Bulk Transfer)模拟的串口。数据在 USB 总线上传输时,是包裹在 USB 数据包中以 480Mbps(USB 2.0 高速)或更高速度直接投递的。硬件直接忽略波特率:当组合导航内部的 MCU(如 STM32)向电脑发送 NMEA 文本(如 $GPGGA)时,它直接将 ASCII 字符写入了 USB FIFO 缓冲区。电脑的 Linux 内核驱动直接把这些 USB 报文还原为字符。物理层的解耦:在这个过程中,无论你在电脑端设置波特率是 9600 还是 115200,USB 协议栈都只负责搬运数据,它不依赖时钟信号去对齐波形。因此,数据永远是 100% 准确的 ASCII 码,绝对不会因为波特率设置错误而产生硬乱码(位错位)。 Linux 内核的默认波特率:通常是 9600 bps(配置为 8N1,即 8 位数据位、无校验、1 位停止位)。 如何查看当前端口的真实软件配置:您可以在终端运行以下命令来查看系统当前分配给 ttyACM0 的具体参数: ```bash stty -F /dev/ttyACM0 ``` ## 设备说明 ### G60 GPS GPS接收机,NMEA协议,最大频率10Hz(说明书上标明),最大波特率115200,现在配置为10Hz@115200 ### H30 IMU hex协议,解析见官方手册,最大频率200Hz 波特率460800 ## 开发过程 使用boost asio库,c++20标准,cmake管理: 两个class,分别创建两个serialport对象进行独立线程通信,gps_serial处理gps设备/dev/ttyACM0 115200波特率出来的NMEA数据,NMEA解析函数先空着,后面补充,设备按10hz频率发出; imu_serial处理imu设备dev/ttyACM1 460800波特率出来的200hz数据,hex, hex解析函数先空着,每次收到数据记录unix时间,后面补充。 跨线程传出数据,在主线程中收到imu数据发出信号,传结构体变量给槽函数call_back_imu_recv,结构体对象赋值imu数据imu_struct,并写入最新的imu_struct和gps_struct到bin文件; gps_serial thread recv data, then emit signal 数据传结构体给 call_back_gps_recv 异步赋值到main.cpp中的全局变量gps_struct. struct内不能包含容器和指针,如std::string std::vector 可以用 int32_t double uint64_t char text[64] 固定长度数组可以 例子 写入bin ```cpp #include #include void save_data() { SensorRecord records[2] = { {1718924000, 23.5, "Sensor_A"}, {1718924060, 24.1, "Sensor_B"} }; // Open file in binary output mode std::ofstream outfile("data.bin", std::ios::binary); if (outfile.is_open()) { // Cast the struct pointer to a raw char pointer outfile.write(reinterpret_cast(records), sizeof(records)); outfile.close(); std::cout << "Successfully saved to bin!\n"; } } ``` 从bin读取 ```cpp void load_data() { std::ifstream infile("data.bin", std::ios::binary); if (infile.is_open()) { SensorRecord single_record; // Read the 2nd record directly by seeking to its exact byte offset (32 bytes) infile.seekg(1 * sizeof(SensorRecord)); // Read raw bytes back directly into the struct memory infile.read(reinterpret_cast(&single_record), sizeof(SensorRecord)); infile.close(); std::cout << "Time: " << single_record.timestamp << ", Temp: " << single_record.temperature << ", ID: " << single_record.device_id << "\n"; } } ``` mmap并发读写,直接保存为二进制格式,不保存为可读性好但查询慢的格式。 查找最邻近整型时间 unix timestamp ```cpp #include #include #include #include #include #include // Wrapper for cross-platform or custom iterator if needed, // but direct pointer arithmetic works perfectly for raw arrays. const DataRow* find_closest_row(const DataRow* data, size_t total_rows, uint64_t target_time) { if (total_rows == 0) return nullptr; // Binary search for the first element NOT LESS than target_time auto it = std::lower_bound(data, data + total_rows, target_time, [](const DataRow& row, uint64_t t) { return row.timestamp < t; }); // If target is greater than the last element if (it == data + total_rows) { return &data[total_rows - 1]; } // If target is smaller than the first element if (it == data) { return &data[0]; } // Compare current match and previous match to find the absolute closest const DataRow* current = it; const DataRow* prev = it - 1; uint64_t diff1 = (current->timestamp > target_time) ? (current->timestamp - target_time) : (target_time - current->timestamp); uint64_t diff2 = (target_time > prev->timestamp) ? (target_time - prev->timestamp) : (prev->timestamp - target_time); return (diff1 < diff2) ? current : prev; } int main() { int fd = open("data.bin", O_RDONLY); struct stat sb; fstat(fd, &sb); size_t file_size = sb.st_size; size_t total_rows = file_size / sizeof(DataRow); // Map file to memory void* mapped = mmap(nullptr, file_size, PROT_READ, MAP_SHARED, fd, 0); const DataRow* data = static_cast(mapped); // Execution of query uint64_t target = 1718924000; const DataRow* closest = find_closest_row(data, total_rows, target); if (closest) { std::cout << "Found closest timestamp: " << closest->timestamp << "\n"; } // Cleanup munmap(mapped, file_size); close(fd); return 0; } ``