Merge branch 'master' of https://gitee.com/openLuat/LuatOS
[LuatOS.git] / components / minmea / luat_lib_libgnss.c
blobdd79f4c1614e9b14c6091ae4714ae1e7031c5b1e
2 /*
3 @module libgnss
4 @summary NMEA数据处理
5 @version 1.0
6 @date 2020.07.03
7 @demo libgnss
8 @tag LUAT_USE_LIBGNSS
9 @usage
10 -- 提醒: 本库输出的坐标,均为 WGS84 坐标系
11 -- 如需要在国内地图使用, 要转换成对应地图的坐标系, 例如 GCJ02 BD09
12 -- 相关链接: https://lbsyun.baidu.com/index.php?title=coordinate
13 -- 相关链接: https://www.openluat.com/GPS-Offset.html
15 -- 提醒: GPS功能, GNSS功能, NMEA解析功能,均为当前库的子功能
16 -- 本库的主要功能就是解析NMEA协议, 支持内置GNSS也支持外置GNSS
18 -- 以下是使用本libgnss的示例代码
19 -- 方案1, 经lua层进行数据中转
20 uart.setup(2, 115200)
21 uart.on(2, "recv", function(id, len)
22 while 1 do
23 local data = uart.read(id, 1024)
24 if data and #data > 1 then
25 libgnss.parse(data)
26 else
27 break
28 end
29 end
30 end)
31 -- 方案2, 适合2022.12.26之后编译固件,效率更高一些
32 uart.setup(2, 115200)
33 libgnss.bind(2)
35 -- 可选调试模式
36 -- libgnss.debug(true)
38 sys.subscribe("GNSS_STATE", function(event, ticks)
39 -- event取值有
40 -- FIXED 定位成功
41 -- LOSE 定位丢失
42 -- ticks是事件发生的时间,一般可以忽略
43 log.info("gnss", "state", event, ticks)
44 end)
46 #include "luat_base.h"
47 #include "luat_msgbus.h"
48 #include "luat_mem.h"
49 #include "luat_uart.h"
50 #include "luat_mcu.h"
51 #include "luat_rtc.h"
52 #include "luat_zbuff.h"
54 #define LUAT_LOG_TAG "gnss"
55 #include "luat_log.h"
57 #include "minmea.h"
59 extern luat_libgnss_t gnssctx;
60 // extern luat_libgnss_t *libgnss_gnsstmp;
61 extern char* libgnss_recvbuff;
62 extern int libgnss_route_uart_id;
63 extern int gnss_debug;
65 static int gnss_raw_cb = 0;
66 static int gnss_txt_cb = 0;
67 // static int gnss_rmc_cb = 0;
68 static int gnss_other_cb = 0;
70 void luat_uart_set_app_recv(int id, luat_uart_recv_callback_t cb);
72 static inline void push_gnss_value(lua_State *L, struct minmea_float *f, int mode) {
73 if (f->value == 0) {
74 lua_pushinteger(L, 0);
75 return;
77 switch (mode)
79 case 0:
80 lua_pushnumber(L, minmea_tofloat(f));
81 break;
82 case 1:
83 lua_pushinteger(L, minmea_tocoord2(f));
84 break;
85 case 2:
86 lua_pushnumber(L, minmea_tocoord(f));
87 break;
88 default:
89 lua_pushnumber(L, minmea_tocoord(f));
90 break;
94 static int luat_libgnss_state_handler(lua_State *L, void* ptr) {
95 (void)ptr;
96 rtos_msg_t* msg = (rtos_msg_t*)lua_topointer(L, -1);
97 lua_getglobal(L, "sys_pub");
98 if (!lua_isfunction(L, -1)) {
99 return 0;
102 @sys_pub libgnss
103 GNSS状态变化
104 GNSS_STATE
105 @usage
106 sys.subscribe("GNSS_STATE", function(event, ticks)
107 -- event取值有
108 -- FIXED 定位成功
109 -- LOSE 定位丢失
110 -- ticks是事件发生的时间,一般可以忽略
111 log.info("gnss", "state", event, ticks)
112 end)
114 lua_pushliteral(L, "GNSS_STATE");
115 switch (msg->arg1)
117 case GNSS_STATE_FIXED:
118 lua_pushliteral(L, "FIXED");
119 break;
120 case GNSS_STATE_LOSE:
121 lua_pushliteral(L, "LOSE");
122 break;
123 case GNSS_STATE_OPEN:
124 lua_pushliteral(L, "OPEN");
125 break;
126 case GNSS_STATE_CLOSE:
127 lua_pushliteral(L, "CLOSE");
128 break;
129 default:
130 return 0;
132 lua_pushinteger(L, msg->arg2);
133 lua_call(L, 3, 0);
134 return 0;
137 int luat_libgnss_state_onchanged(int state) {
138 rtos_msg_t msg = {0};
139 msg.handler = luat_libgnss_state_handler;
140 msg.arg1 = state;
141 #ifdef LUAT_USE_MCU
142 msg.arg2 = luat_mcu_ticks();
143 #endif
144 luat_msgbus_put(&msg, 0);
145 return 0;
148 static void put_datetime(lua_State*L, struct tm* rtime) {
149 lua_pushliteral(L, "year");
150 lua_pushinteger(L, rtime->tm_year);
151 lua_settable(L, -3);
153 lua_pushliteral(L, "month");
154 lua_pushinteger(L, rtime->tm_mon + 1); // 比较纠结, 要不要兼容老的呢?
155 lua_settable(L, -3);
157 lua_pushliteral(L, "day");
158 lua_pushinteger(L, rtime->tm_mday);
159 lua_settable(L, -3);
161 lua_pushliteral(L, "hour");
162 lua_pushinteger(L, rtime->tm_hour);
163 lua_settable(L, -3);
165 lua_pushliteral(L, "min");
166 lua_pushinteger(L, rtime->tm_min);
167 lua_settable(L, -3);
169 lua_pushliteral(L, "sec");
170 lua_pushinteger(L, rtime->tm_sec);
171 lua_settable(L, -3);
174 static int l_gnss_callback(lua_State *L, void* ptr){
175 (void)ptr;
176 rtos_msg_t* msg = (rtos_msg_t*)lua_topointer(L, -1);
177 luat_libgnss_uart_recv_cb(msg->arg1, msg->arg2);
178 return 0;
181 static void l_libgnss_uart_recv_cb(int uart_id, uint32_t data_len)
183 rtos_msg_t msg = {0};
184 msg.handler = l_gnss_callback;
185 msg.arg1 = uart_id;
186 msg.arg2 = data_len;
187 luat_msgbus_put(&msg, 0);
191 处理nmea数据
192 @api libgnss.parse(str)
193 @string 原始nmea数据
194 @usage
195 -- 解析nmea数据
196 libgnss.parse(indata)
197 log.info("nmea", json.encode(libgnss.getRmc(), "11g"))
199 static int l_libgnss_parse(lua_State *L) {
200 size_t len = 0;
201 const char* str = NULL;
202 luat_zbuff_t* zbuff = NULL;
203 if (lua_type(L, 1) == LUA_TSTRING) {
204 str = luaL_checklstring(L, 1, &len);
206 else if (lua_isuserdata(L, 1)) {
207 zbuff = tozbuff(L);
208 str = (const char*)zbuff->addr;
209 len = zbuff->used;
211 else {
212 return 0;
215 if (len > 0) {
216 luat_libgnss_parse_data(str, len);
218 return 0;
222 当前是否已经定位成功
223 @api libgnss.isFix()
224 @return boolean 定位成功与否
225 @usage
226 log.info("nmea", "isFix", libgnss.isFix())
228 static int l_libgnss_is_fix(lua_State *L) {
229 lua_pushboolean(L, gnssctx.frame_rmc.valid != 0 ? 1 : 0);
230 return 1;
234 获取位置信息
235 @api libgnss.getIntLocation(speed_type)
236 @int 速度单位,默认是m/h
237 @return int lat数据, 格式为 ddddddddd
238 @return int lng数据, 格式为 ddddddddd
239 @return int speed数据, 单位米. 于2023.9.26修正
240 @usage
241 -- 建议用libgnss.getRmc(1)
242 log.info("nmea", "loc", libgnss.getIntLocation())
244 -- 2023.12.11 新增speed_type参数
245 --[[
246 速度单位可选值
247 0 - m/h 米/小时, 默认值, 整型
248 1 - m/s 米/秒, 浮点数
249 2 - km/h 千米/小时, 浮点数
250 3 - kn/h 英里/小时, 浮点数
252 -- 默认 米/小时
253 log.info("nmea", "loc", libgnss.getIntLocation())
254 -- 米/秒
255 log.info("nmea", "loc", libgnss.getIntLocation(1))
256 -- 千米/小时
257 log.info("nmea", "loc", libgnss.getIntLocation(2))
258 -- 英里/小时
259 log.info("nmea", "loc", libgnss.getIntLocation(3))
261 static int l_libgnss_get_int_location(lua_State *L) {
262 if (gnssctx.frame_rmc.valid) {
263 lua_pushinteger(L, gnssctx.frame_rmc.latitude.value);
264 lua_pushinteger(L, gnssctx.frame_rmc.longitude.value);
265 int speed_type = luaL_optinteger(L, 1, 0);
266 switch (speed_type)
268 case 1: // 米/秒
269 lua_pushnumber(L, (minmea_tofloat(&(gnssctx.frame_rmc.speed)) * 1852 / 3600));
270 break;
271 case 2: // 千米/小时
272 lua_pushnumber(L, (minmea_tofloat(&(gnssctx.frame_rmc.speed)) * 1.852));
273 break;
274 case 3: // 英里/小时
275 lua_pushnumber(L, minmea_tofloat(&(gnssctx.frame_rmc.speed)));
276 break;
277 default: // 米/小时
278 lua_pushinteger(L, (int32_t)(minmea_tofloat(&(gnssctx.frame_rmc.speed)) * 1852));
279 break;
282 } else {
283 lua_pushinteger(L, 0);
284 lua_pushinteger(L, 0);
285 lua_pushinteger(L, 0);
287 return 3;
291 获取原始RMC位置信息
292 @api libgnss.getRmc(data_mode)
293 @int 坐标类数据的格式, 0-DDMM.MMM格式, 1-DDDDDDD格式, 2-DD.DDDDD格式, 3-原始RMC字符串
294 @return table 原始rmc数据
295 @usage
296 -- 解析nmea
297 log.info("nmea", "rmc", json.encode(libgnss.getRmc(2)))
298 -- 实例输出
299 --[[
301 "course":0,
302 "valid":true, // true定位成功,false定位丢失
303 "lat":23.4067, // 纬度, 正数为北纬, 负数为南纬
304 "lng":113.231, // 经度, 正数为东经, 负数为西经
305 "variation":0, // 地面航向,单位为度,从北向起顺时针计算
306 "speed":0 // 地面速度, 单位为"节"
307 "year":2023, // 年份
308 "month":1, // 月份, 1-12
309 "day":5, // 月份天, 1-31
310 "hour":7, // 小时,0-23
311 "min":23, // 分钟,0-59
312 "sec":20, // 秒,0-59
316 static int l_libgnss_get_rmc(lua_State *L) {
317 int mode = luaL_optinteger(L, 1, 0);
318 lua_settop(L, 0);
319 lua_createtable(L, 0, 12);
321 struct tm rtime = {0};
323 if (mode == 3) {
324 if (gnssctx.rmc == NULL)
325 return 0;
326 lua_pushstring(L, gnssctx.rmc->data);
327 return 1;
330 if (1) {
331 lua_pushboolean(L, gnssctx.frame_rmc.valid);
332 lua_setfield(L, -2, "valid");
334 if (gnssctx.frame_rmc.valid) {
335 push_gnss_value(L, &(gnssctx.frame_rmc.latitude), mode);
337 else
338 lua_pushinteger(L, 0);
339 lua_setfield(L, -2, "lat");
341 if (gnssctx.frame_rmc.valid) {
342 push_gnss_value(L, &(gnssctx.frame_rmc.longitude), mode);
344 else
345 lua_pushinteger(L, 0);
346 lua_setfield(L, -2, "lng");
348 if (gnssctx.frame_rmc.valid) {
349 push_gnss_value(L, &(gnssctx.frame_rmc.speed), 0);
351 else
352 lua_pushinteger(L, 0);
353 lua_setfield(L, -2, "speed");
355 if (gnssctx.frame_rmc.valid) {
356 push_gnss_value(L, &(gnssctx.frame_rmc.course), 0);
358 else
359 lua_pushinteger(L, 0);
360 lua_setfield(L, -2, "course");
362 if (gnssctx.frame_rmc.valid) {
363 push_gnss_value(L, &(gnssctx.frame_rmc.variation), 0);
365 else
366 lua_pushinteger(L, 0);
367 lua_setfield(L, -2, "variation");
369 // 时间类
370 minmea_getdatetime(&rtime, &gnssctx.frame_rmc.date, &gnssctx.frame_rmc.time);
371 put_datetime(L, &rtime);
374 return 1;
378 获取原始GSV信息
379 @api libgnss.getGsv()
380 @return table 原始GSV数据
381 @usage
382 -- 解析nmea
383 log.info("nmea", "gsv", json.encode(libgnss.getGsv()))
384 --[[实例输出
386 "total_sats":24, // 总可见卫星数量
387 "sats":[
389 "snr":27, // 信噪比
390 "azimuth":278, // 方向角
391 "elevation":59, // 仰角
392 "tp":0, // 0 - GPS, 1 - BD
393 "nr":4 // 卫星编号
395 // 这里忽略了22个卫星的信息
397 "snr":0,
398 "azimuth":107,
399 "elevation":19,
400 "tp":1,
401 "nr":31
407 static int l_libgnss_get_gsv(lua_State *L) {
408 lua_createtable(L, 0, 2);
409 size_t count = 1;
410 uint64_t tnow = luat_mcu_tick64_ms();
411 struct minmea_sentence_gsv frame_gsv = {0};
412 lua_createtable(L, FRAME_GSV_MAX, 0);
413 for (size_t i = 0; i < FRAME_GSV_MAX; i++)
415 if (!luat_libgnss_data_check(gnssctx.gsv[i], 3500, tnow) || !minmea_parse_gsv(&frame_gsv, gnssctx.gsv[i]->data)) {
416 continue;
419 for (size_t j = 0; j < 4; j++)
421 if (!frame_gsv.sats[j].nr) {
422 continue;
424 lua_pushinteger(L, count);
425 lua_createtable(L, 0, 4);
427 lua_pushliteral(L, "nr");
428 lua_pushinteger(L, frame_gsv.sats[j].nr);
429 lua_settable(L, -3);
431 lua_pushliteral(L, "snr");
432 lua_pushinteger(L, frame_gsv.sats[j].snr);
433 lua_settable(L, -3);
435 lua_pushliteral(L, "elevation");
436 lua_pushinteger(L, frame_gsv.sats[j].elevation);
437 lua_settable(L, -3);
439 lua_pushliteral(L, "azimuth");
440 lua_pushinteger(L, frame_gsv.sats[j].azimuth);
441 lua_settable(L, -3);
443 // 区分不同的卫星系统
444 // https://receiverhelp.trimble.com/alloy-gnss/en-us/NMEA-0183messages_GSA.html
445 lua_pushliteral(L, "tp");
446 if (memcmp(gnssctx.gsv[i], "$GP", 3) == 0) {
447 lua_pushinteger(L, 0);
449 else if (memcmp(gnssctx.gsv[i], "$GL", 3) == 0) {
450 lua_pushinteger(L, 2);
452 else if (memcmp(gnssctx.gsv[i], "$GA", 3) == 0) {
453 lua_pushinteger(L, 3);
455 else if (memcmp(gnssctx.gsv[i], "$BD", 3) == 0 || memcmp(gnssctx.gsv[i], "$GB", 3) == 0) {
456 lua_pushinteger(L, 1);
458 else if (memcmp(gnssctx.gsv[i], "$QZ", 3) == 0) {
459 lua_pushinteger(L, 4);
461 else {
462 lua_pushinteger(L, 0);
464 lua_settable(L, -3);
466 // 新增一个类型, 字符串的, 实在是各种变化无法应对
467 lua_pushliteral(L, "tpstr");
468 lua_pushlstring(L, gnssctx.gsv[i]->data + 1, 2);
469 lua_settable(L, -3);
471 lua_settable(L, -3);
472 count = count + 1;
475 lua_setfield(L, -2, "sats");
477 lua_pushliteral(L, "total_sats");
478 lua_pushinteger(L, count - 1);
479 lua_settable(L, -3);
481 return 1;
486 获取原始GSA信息
487 @api libgnss.getGsa(data_mode)
488 @int 模式
489 @return table 原始GSA数据
490 @usage
491 -- 获取
492 log.info("nmea", "gsa", json.encode(libgnss.getGsa(), "11g"))
493 -- 示例数据(模式0, 也就是默认模式)
494 --[[
496 "sats":[ // 正在使用的卫星编号
512 "vdop":0.03083333, // 垂直精度因子,0.00 - 99.99,不定位时值为 99.99
513 "pdop":0.0455, // 水平精度因子,0.00 - 99.99,不定位时值为 99.99
514 "fix_type":3, // 定位模式, 1-未定位, 2-2D定位, 3-3D定位
515 "hdop":0.0335 // 位置精度因子,0.00 - 99.99,不定位时值为 99.99
519 -- 示例数据(模式1), 2024.5.26新增
521 {"pdop":7.8299999,"sats":[13,15,18,23],"vdop":3.2400000,"hdop":7.1300001,"fix_type":3},
522 {"pdop":7.8299999,"sats":[20,35,8,13],"vdop":3.2400000,"hdop":7.1300001,"fix_type":3}
526 static int l_libgnss_get_gsa_mode0(lua_State *L) {
527 struct minmea_sentence_gsa frame_gsa = {0};
528 uint64_t tnow = luat_mcu_tick64_ms();
529 lua_createtable(L, 0, 12);
531 for (size_t i = 0; i < FRAME_GSA_MAX; i++)
533 if (!luat_libgnss_data_check(gnssctx.gsa[i], 1500, tnow) || minmea_parse_gsa(&frame_gsa, gnssctx.gsa[i]->data) != 1)
535 continue;
537 lua_pushliteral(L, "fix_type");
538 lua_pushinteger(L, frame_gsa.fix_type);
539 lua_settable(L, -3);
541 lua_pushliteral(L, "pdop");
542 push_gnss_value(L, &(frame_gsa.pdop), 0);
543 lua_settable(L, -3);
545 lua_pushliteral(L, "hdop");
546 push_gnss_value(L, &(frame_gsa.hdop), 0);
547 lua_settable(L, -3);
549 lua_pushliteral(L, "vdop");
550 push_gnss_value(L, &(frame_gsa.vdop), 0);
551 lua_settable(L, -3);
553 lua_pushliteral(L, "sysid");
554 lua_pushinteger(L, frame_gsa.sysid);
555 lua_settable(L, -3);
556 break;
559 lua_pushliteral(L, "sats");
560 lua_createtable(L, FRAME_GSA_MAX, 0);
561 size_t pos = 1;
562 for (size_t i = 0; i < FRAME_GSA_MAX; i++) {
563 if (gnssctx.gsa[i] == NULL || minmea_parse_gsa(&frame_gsa, gnssctx.gsa[i]->data) != 1)
565 continue;
567 if (tnow - gnssctx.gsa[i]->tm > 1000) {
568 continue;
570 // LLOGD("GSA: %s", gnssctx.gsa[i]->data);
571 for (size_t j = 0; j < 12; j++)
573 if (frame_gsa.sats[j] == 0)
574 continue;
576 lua_pushinteger(L, frame_gsa.sats[j]);
577 lua_seti(L, -2, pos);
578 pos ++;
581 lua_settable(L, -3);
582 return 1;
585 static int l_libgnss_get_gsa_mode1(lua_State *L) {
586 struct minmea_sentence_gsa frame_gsa = {0};
587 uint64_t tnow = luat_mcu_tick64_ms();
589 lua_createtable(L, FRAME_GSA_MAX, 0);
590 size_t count = 0;
591 for (size_t i = 0; i < FRAME_GSA_MAX; i++) {
592 if (gnssctx.gsa[i] == NULL || minmea_parse_gsa(&frame_gsa, gnssctx.gsa[i]->data) != 1)
594 continue;
596 if (tnow - gnssctx.gsa[i]->tm > 1000) {
597 continue;
599 count ++;
600 lua_createtable(L, 0, 12);
601 lua_pushliteral(L, "fix_type");
602 lua_pushinteger(L, frame_gsa.fix_type);
603 lua_settable(L, -3);
605 lua_pushliteral(L, "pdop");
606 push_gnss_value(L, &(frame_gsa.pdop), 0);
607 lua_settable(L, -3);
609 lua_pushliteral(L, "hdop");
610 push_gnss_value(L, &(frame_gsa.hdop), 0);
611 lua_settable(L, -3);
613 lua_pushliteral(L, "vdop");
614 push_gnss_value(L, &(frame_gsa.vdop), 0);
615 lua_settable(L, -3);
617 lua_pushliteral(L, "sysid");
618 lua_pushinteger(L, frame_gsa.sysid);
619 lua_settable(L, -3);
621 lua_pushliteral(L, "sats");
622 lua_createtable(L, 12, 0);
623 size_t pos = 1;
624 for (size_t j = 0; j < 12; j++)
626 if (frame_gsa.sats[j] == 0)
627 continue;
629 lua_pushinteger(L, frame_gsa.sats[j]);
630 lua_seti(L, -2, pos);
631 pos ++;
633 lua_settable(L, -3);
634 lua_seti(L, -2, count);
636 return 1;
638 static int l_libgnss_get_gsa(lua_State *L) {
639 int mode = luaL_optinteger(L, 1, 0);
640 lua_settop(L, 0);
641 if (1 == mode) {
642 return l_libgnss_get_gsa_mode1(L);
644 else {
645 return l_libgnss_get_gsa_mode0(L);
651 获取VTA速度信息
652 @api libgnss.getVtg(data_mode)
653 @int 可选, 3-原始字符串, 不传或者传其他值, 则返回浮点值
654 @return table 原始VTA数据
655 @usage
656 -- 解析nmea
657 log.info("nmea", "vtg", json.encode(libgnss.getVtg()))
658 -- 示例
659 --[[
661 "speed_knots":0, // 速度, 英里/小时
662 "true_track_degrees":0, // 真北方向角
663 "magnetic_track_degrees":0, // 磁北方向角
664 "speed_kph":0 // 速度, 千米/小时
666 -- 提醒: Air780EG和Air510U,在速度<5km/h时, 不会返回方向角
669 static int l_libgnss_get_vtg(lua_State *L) {
670 int mode = luaL_optinteger(L, 1, 0);
671 lua_settop(L, 0);
672 if (gnssctx.vtg == NULL)
673 return 0;
674 if (mode == 3) {
675 lua_pushstring(L, gnssctx.vtg->data);
676 return 1;
678 lua_createtable(L, 0, 10);
679 struct minmea_sentence_vtg frame_vtg = {0};
680 minmea_parse_vtg(&frame_vtg, gnssctx.vtg->data);
682 // lua_pushliteral(L, "faa_mode");
683 // lua_pushlstring(L, gnssctx.frame_vtg.faa_mode, 1);
684 // lua_settable(L, -3);
686 lua_pushliteral(L, "true_track_degrees");
687 push_gnss_value(L, &(frame_vtg.true_track_degrees), 0);
688 lua_settable(L, -3);
690 lua_pushliteral(L, "magnetic_track_degrees");
691 push_gnss_value(L, &(frame_vtg.magnetic_track_degrees), 0);
692 lua_settable(L, -3);
694 lua_pushliteral(L, "speed_knots");
695 push_gnss_value(L, &(frame_vtg.speed_knots), 0);
696 lua_settable(L, -3);
698 lua_pushliteral(L, "speed_kph");
699 push_gnss_value(L, &(frame_vtg.speed_kph), 0);
700 lua_settable(L, -3);
702 return 1;
706 获取原始ZDA时间和日期信息
707 @api libgnss.getZda()
708 @return table 原始zda数据
709 @usage
710 log.info("nmea", "zda", json.encode(libgnss.getZda()))
711 -- 实例输出
712 --[[
714 "minute_offset":0, // 本地时区的分钟, 一般固定输出0
715 "hour_offset":0, // 本地时区的小时, 一般固定输出0
716 "year":2023 // UTC 年,四位数字
717 "month":1, // UTC 月,两位,01 ~ 12
718 "day":5, // UTC 日,两位数字,01 ~ 31
719 "hour":7, // 小时
720 "min":50, // 分
721 "sec":14, // 秒
725 static int l_libgnss_get_zda(lua_State *L) {
726 lua_createtable(L, 0, 9);
727 struct tm rtime = {0};
728 if (gnssctx.zda != NULL) {
729 struct minmea_sentence_zda frame_zda = {0};
730 minmea_parse_zda(&frame_zda, gnssctx.zda->data);
732 lua_pushliteral(L, "hour_offset");
733 lua_pushinteger(L, frame_zda.hour_offset);
734 lua_settable(L, -3);
736 lua_pushliteral(L, "minute_offset");
737 lua_pushinteger(L, frame_zda.minute_offset);
738 lua_settable(L, -3);
740 // 时间相关
741 minmea_getdatetime(&rtime, &frame_zda.date, &frame_zda.time);
742 put_datetime(L, &rtime);
745 return 1;
749 设置调试模式
750 @api libgnss.debug(mode)
751 @bool true开启调试,false关闭调试,默认为false
752 @usage
753 -- 开启调试, 会输出GNSS原始数据到日志中
754 libgnss.debug(true)
755 -- 关闭调试
756 libgnss.debug(false)
758 static int l_libgnss_debug(lua_State *L) {
759 if (lua_isboolean(L, 1) && lua_toboolean(L, 1)) {
760 LLOGD("Debug ON");
761 gnss_debug = 1;
763 else
765 LLOGD("Debug OFF");
766 gnss_debug = 0;
768 return 0;
772 获取GGA数据
773 @api libgnss.getGga(data_mode)
774 @int 坐标类数据的格式, 0-DDMM.MMM格式, 1-DDDDDDD格式, 2-DD.DDDDD格式, 3-原始字符串
775 @return table GGA数据, 若如不存在会返回nil
776 @usage
777 local gga = libgnss.getGga(2)
778 if gga then
779 log.info("GGA", json.encode(gga, "11g"))
781 --实例输出
782 --[[
784 "dgps_age":0, // 差分校正时延,单位为秒
785 "fix_quality":1, // 定位状态标识 0 - 无效,1 - 单点定位,2 - 差分定位
786 "satellites_tracked":14, // 参与定位的卫星数量
787 "altitude":0.255, // 海平面分离度, 或者成为海拔, 单位是米,
788 "hdop":0.0335, // 水平精度因子,0.00 - 99.99,不定位时值为 99.99
789 "longitude":113.231, // 经度, 正数为东经, 负数为西经
790 "latitude":23.4067, // 纬度, 正数为北纬, 负数为南纬
791 "height":0 // 椭球高,固定输出 1 位小数
795 static int l_libgnss_get_gga(lua_State* L) {
796 int mode = luaL_optinteger(L, 1, 0);
797 lua_settop(L, 0);
798 if (gnssctx.gga == NULL)
799 return 0;
800 if (mode == 3) {
801 lua_pushstring(L, gnssctx.gga->data);
802 return 1;
804 lua_newtable(L);
805 struct minmea_sentence_gga frame_gga = {0};
806 minmea_parse_gga(&frame_gga, gnssctx.gga->data);
808 lua_pushstring(L, "altitude");
809 push_gnss_value(L, &(frame_gga.altitude), 0);
810 lua_settable(L, -3);
812 lua_pushstring(L, "latitude");
813 push_gnss_value(L, &(frame_gga.latitude), mode);
814 lua_settable(L, -3);
816 lua_pushstring(L, "longitude");
817 push_gnss_value(L, &(frame_gga.longitude), mode);
818 lua_settable(L, -3);
820 lua_pushstring(L, "fix_quality");
821 lua_pushinteger(L, frame_gga.fix_quality);
822 lua_settable(L, -3);
824 lua_pushstring(L, "satellites_tracked");
825 lua_pushinteger(L, frame_gga.satellites_tracked);
826 lua_settable(L, -3);
828 lua_pushstring(L, "hdop");
829 push_gnss_value(L, &(frame_gga.hdop), 0);
830 lua_settable(L, -3);
832 lua_pushstring(L, "height");
833 push_gnss_value(L, &(frame_gga.height), 0);
834 lua_settable(L, -3);
836 lua_pushstring(L, "dgps_age");
837 push_gnss_value(L, &(frame_gga.dgps_age), 0);
838 lua_settable(L, -3);
840 return 1;
844 获取GLL数据
845 @api libgnss.getGll(data_mode)
846 @int 坐标类数据的格式, 0-DDMM.MMM格式, 1-DDDDDDD格式, 2-DD.DDDDD格式
847 @return table GLL数据, 若如不存在会返回nil
848 @usage
849 local gll = libgnss.getGll(2)
850 if gll then
851 log.info("GLL", json.encode(gll, "11g"))
853 -- 实例数据
854 --[[
856 "status":"A", // 定位状态, A有效, B无效
857 "mode":"A", // 定位模式, V无效, A单点解, D差分解
858 "sec":20, // 秒, UTC时间为准
859 "min":23, // 分钟, UTC时间为准
860 "hour":7, // 小时, UTC时间为准
861 "longitude":113.231, // 经度, 正数为东经, 负数为西经
862 "latitude":23.4067, // 纬度, 正数为北纬, 负数为南纬
863 "us":0 // 微妙数, 通常为0
867 static int l_libgnss_get_gll(lua_State* L) {
868 int mode = luaL_optinteger(L, 1, 0);
869 lua_settop(L, 0);
870 if (gnssctx.gll == NULL)
871 return 0;
872 if (mode == 3) {
873 lua_pushstring(L, gnssctx.vtg->data);
874 return 1;
876 lua_newtable(L);
877 struct minmea_sentence_gll frame_gll = {0};
878 minmea_parse_gll(&frame_gll, gnssctx.gll->data);
880 lua_pushstring(L, "latitude");
881 push_gnss_value(L, &(frame_gll.latitude), mode);
882 lua_settable(L, -3);
884 lua_pushstring(L, "longitude");
885 push_gnss_value(L, &(frame_gll.longitude), mode);
886 lua_settable(L, -3);
888 lua_pushstring(L, "mode");
889 lua_pushfstring(L, "%c", frame_gll.mode);
890 lua_settable(L, -3);
892 lua_pushstring(L, "status");
893 lua_pushfstring(L, "%c", frame_gll.status);
894 lua_settable(L, -3);
896 lua_pushstring(L, "hour");
897 lua_pushinteger(L, frame_gll.time.hours);
898 lua_settable(L, -3);
899 lua_pushstring(L, "us");
900 lua_pushinteger(L, frame_gll.time.microseconds);
901 lua_settable(L, -3);
902 lua_pushstring(L, "min");
903 lua_pushinteger(L, frame_gll.time.minutes);
904 lua_settable(L, -3);
905 lua_pushstring(L, "sec");
906 lua_pushinteger(L, frame_gll.time.seconds);
907 lua_settable(L, -3);
909 return 1;
913 清除历史定位数据
914 @api libgnss.clear()
915 @return nil 无返回值
916 @usage
917 -- 该操作会清除所有定位数据
919 static int l_libgnss_clear(lua_State*L) {
920 (void)L;
921 luat_libgnss_init(true);
922 return 0;
926 绑定uart端口进行GNSS数据读取
927 @api libgnss.bind(id, next_id)
928 @int uart端口号
929 @int 转发到uart的id, 例如虚拟uart.VUART_0
930 @usage
931 -- 配置串口信息, 通常为 115200 8N1
932 uart.setup(2, 115200)
933 -- 绑定uart, 马上开始解析GNSS数据
934 libgnss.bind(2)
935 -- 无需再调用uart.on然后调用libgnss.parse
936 -- 开发期可打开调试日志
937 libgnss.debug(true)
939 -- 2023-01-02之后编译的固件有效
940 -- 从uart2读取并解析, 同时转发到虚拟串口0
941 libgnss.bind(2, uart.VUART_0)
943 static int l_libgnss_bind(lua_State* L) {
944 int uart_id = luaL_checkinteger(L, 1);
945 l_libgnss_clear(L);
946 if (libgnss_recvbuff == NULL) {
947 libgnss_recvbuff = luat_heap_malloc(RECV_BUFF_SIZE);
949 if (luat_uart_exist(uart_id)) {
950 //uart_app_recvs[uart_id] = nmea_uart_recv_cb;
951 luat_uart_set_app_recv(uart_id, l_libgnss_uart_recv_cb);
953 if (lua_isinteger(L, 2)) {
954 libgnss_route_uart_id = luaL_checkinteger(L, 2);
956 return 0;
961 获取位置字符串
962 @api libgnss.locStr(mode)
963 @int 字符串模式. 0- Air780EG所需的格式
964 @return 指定模式的字符串
965 @usage
966 -- 仅推荐在定位成功后调用
968 static int l_libgnss_locStr(lua_State *L) {
969 int mode = luaL_optinteger(L, 1, 0);
970 char buff[64] = {0};
971 float lat_f = minmea_tofloat(&gnssctx.frame_rmc.latitude);
972 float lng_f = minmea_tofloat(&gnssctx.frame_rmc.longitude);
973 switch (mode)
975 case 0:
976 snprintf_(buff, 63, "%.7g,%c,%.7g,%c,1.0",
977 fabs(lat_f), lat_f > 0 ? 'N' : 'S',
978 fabs(lng_f), lng_f > 0 ? 'E' : 'W');
979 break;
980 case 1:
981 snprintf_(buff, 63, "%ld,%ld", gnssctx.frame_rmc.latitude.value, gnssctx.frame_rmc.longitude.value);
982 break;
983 default:
984 break;
986 lua_pushstring(L, buff);
987 return 1;
991 定位成功后自动设置RTC
992 @api libgnss.rtcAuto(enable)
993 @bool 开启与否, 默认是false关闭
994 @usage
995 -- 开启自动设置RTC
996 libgnss.rtcAuto(true)
998 static int l_libgnss_rtc_auto(lua_State *L) {
999 if (lua_isboolean(L, 1) && lua_toboolean(L, 1)) {
1000 gnssctx.rtc_auto = 1;
1001 LLOGD("GNSS->RTC Auto-Set now is ON");
1003 else {
1004 gnssctx.rtc_auto = 0;
1005 LLOGD("GNSS->RTC Auto-Set now is OFF");
1007 return 0;
1010 static int l_libgnss_data_cb(lua_State *L, void* ptr) {
1011 rtos_msg_t* msg = (rtos_msg_t*)lua_topointer(L, -1);
1012 // lua_getglobal(L, "sys_pub");
1013 lua_geti(L, LUA_REGISTRYINDEX, msg->arg2);
1014 if (lua_isfunction(L, -1)) {
1015 // lua_pushliteral(gnss_L, "GNSS_RAW_DATA");
1016 lua_pushlstring(L, ptr, msg->arg1);
1017 luat_heap_free(ptr);
1018 ptr = NULL;
1019 lua_call(L, 1, 0);
1021 else {
1022 luat_heap_free(ptr);
1024 return 0;
1027 int luat_libgnss_on_rawdata(const char* data, size_t len, int type) {
1028 int cb = 0;
1029 if (type == 0) {
1030 if (gnss_raw_cb == 0)
1031 return 0;
1032 cb = gnss_raw_cb;
1034 else if (type == 1) {
1035 if (gnss_txt_cb == 0)
1036 return 0;
1037 cb = gnss_txt_cb;
1039 else if (type == 2) {
1040 if (gnss_other_cb == 0)
1041 return 0;
1042 cb = gnss_other_cb;
1044 else {
1045 return 0;
1047 char* ptr = luat_heap_malloc(len);
1048 if (ptr == NULL)
1049 return 0;
1050 memcpy(ptr, data, len);
1051 rtos_msg_t msg = {
1052 .handler = l_libgnss_data_cb,
1053 .arg1 = len,
1054 .arg2 = cb,
1055 .ptr = ptr
1057 luat_msgbus_put(&msg, 0);
1058 return 0;
1062 底层事件回调
1063 @api libgnss.on(tp, fn)
1064 @string 事件类型,当前支持"raw"
1065 @usage
1066 -- 本函数一般用于调试, 用于获取底层实际收到的数据
1067 libgnss.on("raw", function(data)
1068 log.info("GNSS", data)
1069 end)
1071 static int l_libgnss_on(lua_State *L) {
1072 size_t len = 0;
1073 const char* tp = luaL_checklstring(L, 1, &len);
1074 if (!strcmp("raw", tp)) {
1075 if (gnss_raw_cb != 0) {
1076 luaL_unref(L, LUA_REGISTRYINDEX, gnss_raw_cb);
1077 gnss_raw_cb = 0;
1079 if (lua_isfunction(L, 2)) {
1080 lua_pushvalue(L, 2);
1081 gnss_raw_cb = luaL_ref(L, LUA_REGISTRYINDEX);
1084 else if (!strcmp("txt", tp)) {
1085 if (gnss_txt_cb != 0) {
1086 luaL_unref(L, LUA_REGISTRYINDEX, gnss_txt_cb);
1087 gnss_txt_cb = 0;
1089 if (lua_isfunction(L, 2)) {
1090 lua_pushvalue(L, 2);
1091 gnss_txt_cb = luaL_ref(L, LUA_REGISTRYINDEX);
1094 else if (!strcmp("other", tp)) {
1095 if (gnss_other_cb != 0) {
1096 luaL_unref(L, LUA_REGISTRYINDEX, gnss_other_cb);
1097 gnss_other_cb = 0;
1099 if (lua_isfunction(L, 2)) {
1100 lua_pushvalue(L, 2);
1101 gnss_other_cb = luaL_ref(L, LUA_REGISTRYINDEX);
1104 return 0;
1108 获取非标的GPTXT数据
1109 @api libgnss.getTxt()
1110 @return GPTXT所携带的字符串
1111 @usage
1112 -- 本函数于2023.6.6 添加
1113 log.info("gnss", "txt", libgnss.getTxt())
1115 -- 测试语句
1116 libgnss.parse("$GPTXT,01,01,01,ANTENNA SHORT*63\r\n")
1117 log.info("GNSS", libgnss.getTxt())
1118 libgnss.parse("$GPTXT,01,01,01,ANTENNA OPEN*25\r\n")
1119 log.info("GNSS", libgnss.getTxt())
1120 libgnss.parse("$GPTXT,01,01,01,ANTENNA OK*35\r\n")
1121 log.info("GNSS", libgnss.getTxt())
1123 static int l_libgnss_get_txt(lua_State *L) {
1124 if (gnssctx.txt == NULL) {
1125 lua_pushliteral(L, "");
1126 return 1;
1128 struct minmea_sentence_txt txt = {0};
1129 minmea_parse_txt(&txt, gnssctx.txt->data);
1130 txt.txt[FRAME_TXT_MAX_LEN] = 0x00;
1131 lua_pushstring(L, txt.txt);
1132 return 1;
1136 合成Air530Z所需要的辅助定位数据
1137 @api libgnss.casic_aid(dt, loc)
1138 @table 时间信息
1139 @table 经纬度及海拔
1140 @return string 辅助定位数据
1141 @usage
1142 -- 本函数适合CASIC系列GNSS模块的辅助定位信息的合成
1143 -- 本函数 2023.11.14 新增
1145 -- 首先是时间信息,注意是UTC时间
1146 -- 时间来源很多, 一般建议socket.sntp()时间同步后的系统时间
1147 local dt = os.date("!*t")
1149 -- 然后是辅助定位坐标
1150 -- 来源有很多方式:
1151 -- 1. 从历史定位数据得到, 例如之前定位成功后保存到本地文件系统了
1152 -- 2. 通过基站定位或者wifi定位获取到
1153 -- 3. 通过IP定位获取到大概坐标
1154 -- 坐标系是WGS84, 但鉴于是辅助定位,精度不是关键因素
1155 local lla = {
1156 lat = 23.12,
1157 lng = 114.12
1160 local aid = libgnss.casic_aid(dt, lla)
1162 #include "luat_casic_gnss.h"
1163 double strtod(const char *s,char **ptr);
1164 static int l_libgnss_casic_aid(lua_State* L) {
1165 DATETIME_STR dt = {0};
1166 POS_LLA_STR lla = {0};
1167 const char* data = "";
1169 if (lua_istable(L, 1)) {
1170 if (LUA_TNUMBER == lua_getfield(L, 1, "day")) {
1171 dt.day = lua_tointeger(L, -1);
1173 lua_pop(L, 1);
1174 // 这里兼容month和mon两种, os.date 和 rtc.get
1175 if (LUA_TNUMBER == lua_getfield(L, 1, "month")) {
1176 dt.month = lua_tointeger(L, -1);
1178 lua_pop(L, 1);
1179 if (LUA_TNUMBER == lua_getfield(L, 1, "mon")) {
1180 dt.month = lua_tointeger(L, -1);
1182 lua_pop(L, 1);
1184 if (LUA_TNUMBER == lua_getfield(L, 1, "year")) {
1185 dt.year = lua_tointeger(L, -1);
1186 if (dt.year > 2022)
1187 dt.valid = 1;
1189 lua_pop(L, 1);
1190 if (LUA_TNUMBER == lua_getfield(L, 1, "hour")) {
1191 dt.hour = lua_tointeger(L, -1);
1193 lua_pop(L, 1);
1194 if (LUA_TNUMBER == lua_getfield(L, 1, "min")) {
1195 dt.minute = lua_tointeger(L, -1);
1197 lua_pop(L, 1);
1198 if (LUA_TNUMBER == lua_getfield(L, 1, "sec")) {
1199 dt.second = lua_tointeger(L, -1);
1201 lua_pop(L, 1);
1203 if (lua_istable(L, 2)) {
1204 lua_getfield(L, 2, "lat");
1205 if (LUA_TNUMBER == lua_type(L, -1)) {
1206 lla.lat = lua_tonumber(L, -1);
1208 else if (LUA_TSTRING == lua_type(L, -1)) {
1209 data = luaL_checkstring(L, -1);
1210 lla.lat = strtod(data, NULL);
1212 lua_pop(L, 1);
1214 lua_getfield(L, 2, "lng");
1215 if (LUA_TNUMBER == lua_type(L, -1)) {
1216 lla.lon = lua_tonumber(L, -1);
1218 else if (LUA_TSTRING == lua_type(L, -1)) {
1219 data = luaL_checkstring(L, -1);
1220 lla.lon = strtod(data, NULL);
1222 lua_pop(L, 1);
1223 if (LUA_TNUMBER == lua_getfield(L, 2, "alt")) {
1224 lla.alt = lua_tonumber(L, -1);
1227 if (lla.lat > 0.001 || lla.lat < -0.01)
1228 if (lla.lon > 0.001 || lla.lon < -0.01)
1229 lla.valid = 1;
1231 char tmp[66] = {0};
1232 casicAgnssAidIni(&dt, &lla, tmp);
1233 lua_pushlstring(L, tmp, 66);
1234 return 1;
1237 #include "rotable2.h"
1238 static const rotable_Reg_t reg_libgnss[] =
1240 { "parse", ROREG_FUNC(l_libgnss_parse)},
1241 { "isFix", ROREG_FUNC(l_libgnss_is_fix)},
1242 { "getIntLocation", ROREG_FUNC(l_libgnss_get_int_location)},
1243 { "getRmc", ROREG_FUNC(l_libgnss_get_rmc)},
1244 { "getGsv", ROREG_FUNC(l_libgnss_get_gsv)},
1245 { "getGsa", ROREG_FUNC(l_libgnss_get_gsa)},
1246 { "getVtg", ROREG_FUNC(l_libgnss_get_vtg)},
1247 { "getGga", ROREG_FUNC(l_libgnss_get_gga)},
1248 { "getGll", ROREG_FUNC(l_libgnss_get_gll)},
1249 { "getZda", ROREG_FUNC(l_libgnss_get_zda)},
1250 { "locStr", ROREG_FUNC(l_libgnss_locStr)},
1251 { "rtcAuto",ROREG_FUNC(l_libgnss_rtc_auto)},
1252 { "on", ROREG_FUNC(l_libgnss_on)},
1254 { "debug", ROREG_FUNC(l_libgnss_debug)},
1255 { "clear", ROREG_FUNC(l_libgnss_clear)},
1256 { "bind", ROREG_FUNC(l_libgnss_bind)},
1258 { "getTxt", ROREG_FUNC(l_libgnss_get_txt)},
1259 { "casic_aid", ROREG_FUNC(l_libgnss_casic_aid)},
1261 { NULL, ROREG_INT(0)}
1264 LUAMOD_API int luaopen_libgnss( lua_State *L ) {
1265 luat_newlib2(L, reg_libgnss);
1266 return 1;