update: 更新air780eg的demo, 演示开启单北斗模式
[LuatOS.git] / components / minmea / luat_lib_libgnss.c
blob56a04e85c9b27b0f185a5206eff5f3af1fcde130
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"
53 #define LUAT_LOG_TAG "gnss"
54 #include "luat_log.h"
56 #include "minmea.h"
58 extern luat_libgnss_t gnssctx;
59 // extern luat_libgnss_t *libgnss_gnsstmp;
60 extern char* libgnss_recvbuff;
61 extern int libgnss_route_uart_id;
62 extern int gnss_debug;
64 void luat_uart_set_app_recv(int id, luat_uart_recv_callback_t cb);
66 static inline void push_gnss_value(lua_State *L, struct minmea_float *f, int mode) {
67 if (f->value == 0) {
68 lua_pushinteger(L, 0);
69 return;
71 switch (mode)
73 case 0:
74 lua_pushnumber(L, minmea_tofloat(f));
75 break;
76 case 1:
77 lua_pushinteger(L, minmea_tocoord2(f));
78 break;
79 case 2:
80 lua_pushnumber(L, minmea_tocoord(f));
81 break;
82 default:
83 lua_pushnumber(L, minmea_tocoord(f));
84 break;
88 static int luat_libgnss_state_handler(lua_State *L, void* ptr) {
89 (void)ptr;
90 rtos_msg_t* msg = (rtos_msg_t*)lua_topointer(L, -1);
91 lua_getglobal(L, "sys_pub");
92 if (!lua_isfunction(L, -1)) {
93 return 0;
96 @sys_pub libgnss
97 GNSS状态变化
98 GNSS_STATE
99 @usage
100 sys.subscribe("GNSS_STATE", function(event, ticks)
101 -- event取值有
102 -- FIXED 定位成功
103 -- LOSE 定位丢失
104 -- ticks是事件发生的时间,一般可以忽略
105 log.info("gnss", "state", event, ticks)
106 end)
108 lua_pushliteral(L, "GNSS_STATE");
109 switch (msg->arg1)
111 case GNSS_STATE_FIXED:
112 lua_pushliteral(L, "FIXED");
113 break;
114 case GNSS_STATE_LOSE:
115 lua_pushliteral(L, "LOSE");
116 break;
117 case GNSS_STATE_OPEN:
118 lua_pushliteral(L, "OPEN");
119 break;
120 case GNSS_STATE_CLOSE:
121 lua_pushliteral(L, "CLOSE");
122 break;
123 default:
124 return 0;
126 lua_pushinteger(L, msg->arg2);
127 lua_call(L, 3, 0);
128 return 0;
131 int luat_libgnss_state_onchanged(int state) {
132 rtos_msg_t msg = {0};
133 msg.handler = luat_libgnss_state_handler;
134 msg.arg1 = state;
135 #ifdef LUAT_USE_MCU
136 msg.arg2 = luat_mcu_ticks();
137 #endif
138 luat_msgbus_put(&msg, 0);
139 return 0;
142 static void put_datetime(lua_State*L, struct tm* rtime) {
143 lua_pushliteral(L, "year");
144 lua_pushinteger(L, rtime->tm_year);
145 lua_settable(L, -3);
147 lua_pushliteral(L, "month");
148 lua_pushinteger(L, rtime->tm_mon + 1); // 比较纠结, 要不要兼容老的呢?
149 lua_settable(L, -3);
151 lua_pushliteral(L, "day");
152 lua_pushinteger(L, rtime->tm_mday);
153 lua_settable(L, -3);
155 lua_pushliteral(L, "hour");
156 lua_pushinteger(L, rtime->tm_hour);
157 lua_settable(L, -3);
159 lua_pushliteral(L, "min");
160 lua_pushinteger(L, rtime->tm_min);
161 lua_settable(L, -3);
163 lua_pushliteral(L, "sec");
164 lua_pushinteger(L, rtime->tm_sec);
165 lua_settable(L, -3);
168 static int l_gnss_callback(lua_State *L, void* ptr){
169 (void)ptr;
170 rtos_msg_t* msg = (rtos_msg_t*)lua_topointer(L, -1);
171 luat_libgnss_uart_recv_cb(msg->arg1, msg->arg2);
172 return 0;
175 static void l_libgnss_uart_recv_cb(int uart_id, uint32_t data_len)
177 rtos_msg_t msg = {0};
178 msg.handler = l_gnss_callback;
179 msg.arg1 = uart_id;
180 msg.arg2 = data_len;
181 luat_msgbus_put(&msg, 0);
185 处理nmea数据
186 @api libgnss.parse(str)
187 @string 原始nmea数据
188 @usage
189 -- 解析nmea数据
190 libgnss.parse(indata)
191 log.info("nmea", json.encode(libgnss.getRmc(), "11g"))
193 static int l_libgnss_parse(lua_State *L) {
194 size_t len = 0;
195 const char* str = luaL_checklstring(L, 1, &len);
196 if (len > 0) {
197 luat_libgnss_parse_data(str, len);
199 return 0;
203 当前是否已经定位成功
204 @api libgnss.isFix()
205 @return boolean 定位成功与否
206 @usage
207 log.info("nmea", "isFix", libgnss.isFix())
209 static int l_libgnss_is_fix(lua_State *L) {
210 lua_pushboolean(L, gnssctx.frame_rmc.valid != 0 ? 1 : 0);
211 return 1;
215 获取位置信息
216 @api libgnss.getIntLocation(speed_type)
217 @int 速度单位,默认是m/h
218 @return int lat数据, 格式为 ddddddddd
219 @return int lng数据, 格式为 ddddddddd
220 @return int speed数据, 单位米. 于2023.9.26修正
221 @usage
222 -- 建议用libgnss.getRmc(1)
223 log.info("nmea", "loc", libgnss.getIntLocation())
225 -- 2023.12.11 新增speed_type参数
226 --[[
227 速度单位可选值
228 0 - m/h 米/小时, 默认值, 整型
229 1 - m/s 米/秒, 浮点数
230 2 - km/h 千米/小时, 浮点数
231 3 - kn/h 英里/小时, 浮点数
233 -- 默认 米/小时
234 log.info("nmea", "loc", libgnss.getIntLocation())
235 -- 米/秒
236 log.info("nmea", "loc", libgnss.getIntLocation(1))
237 -- 千米/小时
238 log.info("nmea", "loc", libgnss.getIntLocation(2))
239 -- 英里/小时
240 log.info("nmea", "loc", libgnss.getIntLocation(3))
242 static int l_libgnss_get_int_location(lua_State *L) {
243 if (gnssctx.frame_rmc.valid) {
244 lua_pushinteger(L, gnssctx.frame_rmc.latitude.value);
245 lua_pushinteger(L, gnssctx.frame_rmc.longitude.value);
246 int speed_type = luaL_optinteger(L, 1, 0);
247 switch (speed_type)
249 case 1: // 米/秒
250 lua_pushnumber(L, (minmea_tofloat(&(gnssctx.frame_rmc.speed)) * 1852 / 3600));
251 break;
252 case 2: // 千米/小时
253 lua_pushnumber(L, (minmea_tofloat(&(gnssctx.frame_rmc.speed)) * 1.852));
254 break;
255 case 3: // 英里/小时
256 lua_pushnumber(L, minmea_tofloat(&(gnssctx.frame_rmc.speed)));
257 break;
258 default: // 米/小时
259 lua_pushinteger(L, (int32_t)(minmea_tofloat(&(gnssctx.frame_rmc.speed)) * 1852));
260 break;
263 } else {
264 lua_pushinteger(L, 0);
265 lua_pushinteger(L, 0);
266 lua_pushinteger(L, 0);
268 return 3;
272 获取原始RMC位置信息
273 @api libgnss.getRmc(data_mode)
274 @int 坐标类数据的格式, 0-DDMM.MMM格式, 1-DDDDDDD格式, 2-DD.DDDDD格式
275 @return table 原始rmc数据
276 @usage
277 -- 解析nmea
278 log.info("nmea", "rmc", json.encode(libgnss.getRmc(2)))
279 -- 实例输出
280 --[[
282 "course":0,
283 "valid":true, // true定位成功,false定位丢失
284 "lat":23.4067, // 纬度, 正数为北纬, 负数为南纬
285 "lng":113.231, // 经度, 正数为东经, 负数为西经
286 "variation":0, // 地面航向,单位为度,从北向起顺时针计算
287 "speed":0 // 地面速度, 单位为"节"
288 "year":2023, // 年份
289 "month":1, // 月份, 1-12
290 "day":5, // 月份天, 1-31
291 "hour":7, // 小时,0-23
292 "min":23, // 分钟,0-59
293 "sec":20, // 秒,0-59
297 static int l_libgnss_get_rmc(lua_State *L) {
298 int mode = luaL_optinteger(L, 1, 0);
299 lua_settop(L, 0);
300 lua_createtable(L, 0, 12);
302 struct tm rtime = {0};
304 if (1) {
305 lua_pushboolean(L, gnssctx.frame_rmc.valid);
306 lua_setfield(L, -2, "valid");
308 if (gnssctx.frame_rmc.valid) {
309 push_gnss_value(L, &(gnssctx.frame_rmc.latitude), mode);
311 else
312 lua_pushinteger(L, 0);
313 lua_setfield(L, -2, "lat");
315 if (gnssctx.frame_rmc.valid) {
316 push_gnss_value(L, &(gnssctx.frame_rmc.longitude), mode);
318 else
319 lua_pushinteger(L, 0);
320 lua_setfield(L, -2, "lng");
322 if (gnssctx.frame_rmc.valid) {
323 push_gnss_value(L, &(gnssctx.frame_rmc.speed), 0);
325 else
326 lua_pushinteger(L, 0);
327 lua_setfield(L, -2, "speed");
329 if (gnssctx.frame_rmc.valid) {
330 push_gnss_value(L, &(gnssctx.frame_rmc.course), 0);
332 else
333 lua_pushinteger(L, 0);
334 lua_setfield(L, -2, "course");
336 if (gnssctx.frame_rmc.valid) {
337 push_gnss_value(L, &(gnssctx.frame_rmc.variation), 0);
339 else
340 lua_pushinteger(L, 0);
341 lua_setfield(L, -2, "variation");
343 // 时间类
344 minmea_getdatetime(&rtime, &gnssctx.frame_rmc.date, &gnssctx.frame_rmc.time);
345 put_datetime(L, &rtime);
348 return 1;
352 获取原始GSV信息
353 @api libgnss.getGsv()
354 @return table 原始GSV数据
355 @usage
356 -- 解析nmea
357 log.info("nmea", "gsv", json.encode(libgnss.getGsv()))
358 --[[实例输出
360 "total_sats":24, // 总可见卫星数量
361 "sats":[
363 "snr":27, // 信噪比
364 "azimuth":278, // 方向角
365 "elevation":59, // 仰角
366 "tp":0, // 0 - GPS, 1 - BD
367 "nr":4 // 卫星编号
369 // 这里忽略了22个卫星的信息
371 "snr":0,
372 "azimuth":107,
373 "elevation":19,
374 "tp":1,
375 "nr":31
381 static int l_libgnss_get_gsv(lua_State *L) {
382 lua_createtable(L, 0, 2);
383 size_t count = 1;
384 uint64_t tnow = luat_mcu_tick64_ms();
385 struct minmea_sentence_gsv frame_gsv = {0};
386 lua_createtable(L, FRAME_GSV_MAX, 0);
387 for (size_t i = 0; i < FRAME_GSV_MAX; i++)
389 if (!luat_libgnss_data_check(gnssctx.gsv[i], 3500, tnow) || !minmea_parse_gsv(&frame_gsv, gnssctx.gsv[i]->data)) {
390 continue;
393 for (size_t j = 0; j < 4; j++)
395 if (!frame_gsv.sats[j].nr) {
396 continue;
398 lua_pushinteger(L, count);
399 lua_createtable(L, 0, 4);
401 lua_pushliteral(L, "nr");
402 lua_pushinteger(L, frame_gsv.sats[j].nr);
403 lua_settable(L, -3);
405 lua_pushliteral(L, "snr");
406 lua_pushinteger(L, frame_gsv.sats[j].snr);
407 lua_settable(L, -3);
409 lua_pushliteral(L, "elevation");
410 lua_pushinteger(L, frame_gsv.sats[j].elevation);
411 lua_settable(L, -3);
413 lua_pushliteral(L, "azimuth");
414 lua_pushinteger(L, frame_gsv.sats[j].azimuth);
415 lua_settable(L, -3);
417 // 区分不同的卫星系统
418 // https://receiverhelp.trimble.com/alloy-gnss/en-us/NMEA-0183messages_GSA.html
419 lua_pushliteral(L, "tp");
420 if (memcmp(gnssctx.gsv[i], "$GP", 3) == 0) {
421 lua_pushinteger(L, 0);
423 else if (memcmp(gnssctx.gsv[i], "$GL", 3) == 0) {
424 lua_pushinteger(L, 2);
426 else if (memcmp(gnssctx.gsv[i], "$GA", 3) == 0) {
427 lua_pushinteger(L, 3);
429 else if (memcmp(gnssctx.gsv[i], "$BD", 3) == 0 || memcmp(gnssctx.gsv[i], "$GB", 3) == 0) {
430 lua_pushinteger(L, 1);
432 else if (memcmp(gnssctx.gsv[i], "$QZ", 3) == 0) {
433 lua_pushinteger(L, 4);
435 else {
436 lua_pushinteger(L, 0);
438 lua_settable(L, -3);
440 // 新增一个类型, 字符串的, 实在是各种变化无法应对
441 lua_pushliteral(L, "tpstr");
442 lua_pushlstring(L, gnssctx.gsv[i]->data + 1, 2);
443 lua_settable(L, -3);
445 lua_settable(L, -3);
446 count = count + 1;
449 lua_setfield(L, -2, "sats");
451 lua_pushliteral(L, "total_sats");
452 lua_pushinteger(L, count - 1);
453 lua_settable(L, -3);
455 return 1;
460 获取原始GSA信息
461 @api libgnss.getGsa(data_mode)
462 @int 模式
463 @return table 原始GSA数据
464 @usage
465 -- 获取
466 log.info("nmea", "gsa", json.encode(libgnss.getGsa(), "11g"))
467 -- 示例数据(模式0, 也就是默认模式)
468 --[[
470 "sats":[ // 正在使用的卫星编号
486 "vdop":0.03083333, // 垂直精度因子,0.00 - 99.99,不定位时值为 99.99
487 "pdop":0.0455, // 水平精度因子,0.00 - 99.99,不定位时值为 99.99
488 "fix_type":3, // 定位模式, 1-未定位, 2-2D定位, 3-3D定位
489 "hdop":0.0335 // 位置精度因子,0.00 - 99.99,不定位时值为 99.99
493 -- 示例数据(模式1), 2024.5.26新增
495 {"pdop":7.8299999,"sats":[13,15,18,23],"vdop":3.2400000,"hdop":7.1300001,"fix_type":3},
496 {"pdop":7.8299999,"sats":[20,35,8,13],"vdop":3.2400000,"hdop":7.1300001,"fix_type":3}
500 static int l_libgnss_get_gsa_mode0(lua_State *L) {
501 struct minmea_sentence_gsa frame_gsa = {0};
502 uint64_t tnow = luat_mcu_tick64_ms();
503 lua_createtable(L, 0, 12);
505 for (size_t i = 0; i < FRAME_GSA_MAX; i++)
507 if (!luat_libgnss_data_check(gnssctx.gsa[i], 1500, tnow) || minmea_parse_gsa(&frame_gsa, gnssctx.gsa[i]->data) != 1)
509 continue;
511 lua_pushliteral(L, "fix_type");
512 lua_pushinteger(L, frame_gsa.fix_type);
513 lua_settable(L, -3);
515 lua_pushliteral(L, "pdop");
516 push_gnss_value(L, &(frame_gsa.pdop), 0);
517 lua_settable(L, -3);
519 lua_pushliteral(L, "hdop");
520 push_gnss_value(L, &(frame_gsa.hdop), 0);
521 lua_settable(L, -3);
523 lua_pushliteral(L, "vdop");
524 push_gnss_value(L, &(frame_gsa.vdop), 0);
525 lua_settable(L, -3);
526 break;
529 lua_pushliteral(L, "sats");
530 lua_createtable(L, FRAME_GSA_MAX, 0);
531 size_t pos = 1;
532 for (size_t i = 0; i < FRAME_GSA_MAX; i++) {
533 if (gnssctx.gsa[i] == NULL || minmea_parse_gsa(&frame_gsa, gnssctx.gsa[i]->data) != 1)
535 continue;
537 if (tnow - gnssctx.gsa[i]->tm > 1000) {
538 continue;
540 // LLOGD("GSA: %s", gnssctx.gsa[i]->data);
541 for (size_t j = 0; j < 12; j++)
543 if (frame_gsa.sats[j] == 0)
544 continue;
546 lua_pushinteger(L, frame_gsa.sats[j]);
547 lua_seti(L, -2, pos);
548 pos ++;
551 lua_settable(L, -3);
552 return 1;
555 static int l_libgnss_get_gsa_mode1(lua_State *L) {
556 struct minmea_sentence_gsa frame_gsa = {0};
557 uint64_t tnow = luat_mcu_tick64_ms();
559 lua_createtable(L, FRAME_GSA_MAX, 0);
560 size_t count = 0;
561 for (size_t i = 0; i < FRAME_GSA_MAX; i++) {
562 if (gnssctx.gsa[i] == NULL || minmea_parse_gsa(&frame_gsa, gnssctx.gsa[i]->data) != 1)
564 continue;
566 if (tnow - gnssctx.gsa[i]->tm > 1000) {
567 continue;
569 count ++;
570 lua_createtable(L, 0, 12);
571 lua_pushliteral(L, "fix_type");
572 lua_pushinteger(L, frame_gsa.fix_type);
573 lua_settable(L, -3);
575 lua_pushliteral(L, "pdop");
576 push_gnss_value(L, &(frame_gsa.pdop), 0);
577 lua_settable(L, -3);
579 lua_pushliteral(L, "hdop");
580 push_gnss_value(L, &(frame_gsa.hdop), 0);
581 lua_settable(L, -3);
583 lua_pushliteral(L, "vdop");
584 push_gnss_value(L, &(frame_gsa.vdop), 0);
585 lua_settable(L, -3);
587 lua_pushliteral(L, "sats");
588 lua_createtable(L, 12, 0);
589 size_t pos = 1;
590 for (size_t j = 0; j < 12; j++)
592 if (frame_gsa.sats[j] == 0)
593 continue;
595 lua_pushinteger(L, frame_gsa.sats[j]);
596 lua_seti(L, -2, pos);
597 pos ++;
599 lua_settable(L, -3);
600 lua_seti(L, -2, count);
602 return 1;
604 static int l_libgnss_get_gsa(lua_State *L) {
605 int mode = luaL_optinteger(L, 1, 0);
606 lua_settop(L, 0);
607 if (1 == mode) {
608 return l_libgnss_get_gsa_mode1(L);
610 else {
611 return l_libgnss_get_gsa_mode0(L);
617 获取VTA速度信息
618 @api libgnss.getVtg(data_mode)
619 @int 可选, 3-原始字符串, 不传或者传其他值, 则返回浮点值
620 @return table 原始VTA数据
621 @usage
622 -- 解析nmea
623 log.info("nmea", "vtg", json.encode(libgnss.getVtg()))
624 -- 示例
625 --[[
627 "speed_knots":0, // 速度, 英里/小时
628 "true_track_degrees":0, // 真北方向角
629 "magnetic_track_degrees":0, // 磁北方向角
630 "speed_kph":0 // 速度, 千米/小时
632 -- 提醒: Air780EG和Air510U,在速度<5km/h时, 不会返回方向角
635 static int l_libgnss_get_vtg(lua_State *L) {
636 int mode = luaL_optinteger(L, 1, 0);
637 lua_settop(L, 0);
638 if (gnssctx.vtg == NULL)
639 return 0;
640 if (mode == 3) {
641 lua_pushstring(L, gnssctx.vtg->data);
642 return 1;
644 lua_createtable(L, 0, 10);
645 struct minmea_sentence_vtg frame_vtg = {0};
646 minmea_parse_vtg(&frame_vtg, gnssctx.vtg->data);
648 // lua_pushliteral(L, "faa_mode");
649 // lua_pushlstring(L, gnssctx.frame_vtg.faa_mode, 1);
650 // lua_settable(L, -3);
652 lua_pushliteral(L, "true_track_degrees");
653 push_gnss_value(L, &(frame_vtg.true_track_degrees), 0);
654 lua_settable(L, -3);
656 lua_pushliteral(L, "magnetic_track_degrees");
657 push_gnss_value(L, &(frame_vtg.magnetic_track_degrees), 0);
658 lua_settable(L, -3);
660 lua_pushliteral(L, "speed_knots");
661 push_gnss_value(L, &(frame_vtg.speed_knots), 0);
662 lua_settable(L, -3);
664 lua_pushliteral(L, "speed_kph");
665 push_gnss_value(L, &(frame_vtg.speed_kph), 0);
666 lua_settable(L, -3);
668 return 1;
672 获取原始ZDA时间和日期信息
673 @api libgnss.getZda()
674 @return table 原始zda数据
675 @usage
676 log.info("nmea", "zda", json.encode(libgnss.getZda()))
677 -- 实例输出
678 --[[
680 "minute_offset":0, // 本地时区的分钟, 一般固定输出0
681 "hour_offset":0, // 本地时区的小时, 一般固定输出0
682 "year":2023 // UTC 年,四位数字
683 "month":1, // UTC 月,两位,01 ~ 12
684 "day":5, // UTC 日,两位数字,01 ~ 31
685 "hour":7, // 小时
686 "min":50, // 分
687 "sec":14, // 秒
691 static int l_libgnss_get_zda(lua_State *L) {
692 lua_createtable(L, 0, 9);
693 struct tm rtime = {0};
694 if (gnssctx.zda != NULL) {
695 struct minmea_sentence_zda frame_zda = {0};
696 minmea_parse_zda(&frame_zda, gnssctx.zda->data);
698 lua_pushliteral(L, "hour_offset");
699 lua_pushinteger(L, frame_zda.hour_offset);
700 lua_settable(L, -3);
702 lua_pushliteral(L, "minute_offset");
703 lua_pushinteger(L, frame_zda.minute_offset);
704 lua_settable(L, -3);
706 // 时间相关
707 minmea_getdatetime(&rtime, &frame_zda.date, &frame_zda.time);
708 put_datetime(L, &rtime);
711 return 1;
715 设置调试模式
716 @api libgnss.debug(mode)
717 @bool true开启调试,false关闭调试,默认为false
718 @usage
719 -- 开启调试, 会输出GNSS原始数据到日志中
720 libgnss.debug(true)
721 -- 关闭调试
722 libgnss.debug(false)
724 static int l_libgnss_debug(lua_State *L) {
725 if (lua_isboolean(L, 1) && lua_toboolean(L, 1)) {
726 LLOGD("Debug ON");
727 gnss_debug = 1;
729 else
731 LLOGD("Debug OFF");
732 gnss_debug = 0;
734 return 0;
738 获取GGA数据
739 @api libgnss.getGga(data_mode)
740 @int 坐标类数据的格式, 0-DDMM.MMM格式, 1-DDDDDDD格式, 2-DD.DDDDD格式, 3-原始字符串
741 @return table GGA数据, 若如不存在会返回nil
742 @usage
743 local gga = libgnss.getGga(2)
744 if gga then
745 log.info("GGA", json.encode(gga, "11g"))
747 --实例输出
748 --[[
750 "dgps_age":0, // 差分校正时延,单位为秒
751 "fix_quality":1, // 定位状态标识 0 - 无效,1 - 单点定位,2 - 差分定位
752 "satellites_tracked":14, // 参与定位的卫星数量
753 "altitude":0.255, // 海平面分离度, 或者成为海拔, 单位是米,
754 "hdop":0.0335, // 水平精度因子,0.00 - 99.99,不定位时值为 99.99
755 "longitude":113.231, // 经度, 正数为东经, 负数为西经
756 "latitude":23.4067, // 纬度, 正数为北纬, 负数为南纬
757 "height":0 // 椭球高,固定输出 1 位小数
761 static int l_libgnss_get_gga(lua_State* L) {
762 int mode = luaL_optinteger(L, 1, 0);
763 lua_settop(L, 0);
764 if (gnssctx.gga == NULL)
765 return 0;
766 if (mode == 3) {
767 lua_pushstring(L, gnssctx.gga->data);
768 return 1;
770 lua_newtable(L);
771 struct minmea_sentence_gga frame_gga = {0};
772 minmea_parse_gga(&frame_gga, gnssctx.gga->data);
774 lua_pushstring(L, "altitude");
775 push_gnss_value(L, &(frame_gga.altitude), 0);
776 lua_settable(L, -3);
778 lua_pushstring(L, "latitude");
779 push_gnss_value(L, &(frame_gga.latitude), mode);
780 lua_settable(L, -3);
782 lua_pushstring(L, "longitude");
783 push_gnss_value(L, &(frame_gga.longitude), mode);
784 lua_settable(L, -3);
786 lua_pushstring(L, "fix_quality");
787 lua_pushinteger(L, frame_gga.fix_quality);
788 lua_settable(L, -3);
790 lua_pushstring(L, "satellites_tracked");
791 lua_pushinteger(L, frame_gga.satellites_tracked);
792 lua_settable(L, -3);
794 lua_pushstring(L, "hdop");
795 push_gnss_value(L, &(frame_gga.hdop), 0);
796 lua_settable(L, -3);
798 lua_pushstring(L, "height");
799 push_gnss_value(L, &(frame_gga.height), 0);
800 lua_settable(L, -3);
802 lua_pushstring(L, "dgps_age");
803 push_gnss_value(L, &(frame_gga.dgps_age), 0);
804 lua_settable(L, -3);
806 return 1;
810 获取GLL数据
811 @api libgnss.getGll(data_mode)
812 @int 坐标类数据的格式, 0-DDMM.MMM格式, 1-DDDDDDD格式, 2-DD.DDDDD格式
813 @return table GLL数据, 若如不存在会返回nil
814 @usage
815 local gll = libgnss.getGll(2)
816 if gll then
817 log.info("GLL", json.encode(gll, "11g"))
819 -- 实例数据
820 --[[
822 "status":"A", // 定位状态, A有效, B无效
823 "mode":"A", // 定位模式, V无效, A单点解, D差分解
824 "sec":20, // 秒, UTC时间为准
825 "min":23, // 分钟, UTC时间为准
826 "hour":7, // 小时, UTC时间为准
827 "longitude":113.231, // 经度, 正数为东经, 负数为西经
828 "latitude":23.4067, // 纬度, 正数为北纬, 负数为南纬
829 "us":0 // 微妙数, 通常为0
833 static int l_libgnss_get_gll(lua_State* L) {
834 int mode = luaL_optinteger(L, 1, 0);
835 lua_settop(L, 0);
836 if (gnssctx.gll == NULL)
837 return 0;
838 if (mode == 3) {
839 lua_pushstring(L, gnssctx.vtg->data);
840 return 1;
842 lua_newtable(L);
843 struct minmea_sentence_gll frame_gll = {0};
844 minmea_parse_gll(&frame_gll, gnssctx.gll->data);
846 lua_pushstring(L, "latitude");
847 push_gnss_value(L, &(frame_gll.latitude), mode);
848 lua_settable(L, -3);
850 lua_pushstring(L, "longitude");
851 push_gnss_value(L, &(frame_gll.longitude), mode);
852 lua_settable(L, -3);
854 lua_pushstring(L, "mode");
855 lua_pushfstring(L, "%c", frame_gll.mode);
856 lua_settable(L, -3);
858 lua_pushstring(L, "status");
859 lua_pushfstring(L, "%c", frame_gll.status);
860 lua_settable(L, -3);
862 lua_pushstring(L, "hour");
863 lua_pushinteger(L, frame_gll.time.hours);
864 lua_settable(L, -3);
865 lua_pushstring(L, "us");
866 lua_pushinteger(L, frame_gll.time.microseconds);
867 lua_settable(L, -3);
868 lua_pushstring(L, "min");
869 lua_pushinteger(L, frame_gll.time.minutes);
870 lua_settable(L, -3);
871 lua_pushstring(L, "sec");
872 lua_pushinteger(L, frame_gll.time.seconds);
873 lua_settable(L, -3);
875 return 1;
879 清除历史定位数据
880 @api libgnss.clear()
881 @return nil 无返回值
882 @usage
883 -- 该操作会清除所有定位数据
885 static int l_libgnss_clear(lua_State*L) {
886 (void)L;
887 memset(&gnssctx.frame_rmc, 0, sizeof(struct minmea_sentence_rmc));
888 return 0;
892 绑定uart端口进行GNSS数据读取
893 @api libgnss.bind(id, next_id)
894 @int uart端口号
895 @int 转发到uart的id, 例如虚拟uart.VUART_0
896 @usage
897 -- 配置串口信息, 通常为 115200 8N1
898 uart.setup(2, 115200)
899 -- 绑定uart, 马上开始解析GNSS数据
900 libgnss.bind(2)
901 -- 无需再调用uart.on然后调用libgnss.parse
902 -- 开发期可打开调试日志
903 libgnss.debug(true)
905 -- 2023-01-02之后编译的固件有效
906 -- 从uart2读取并解析, 同时转发到虚拟串口0
907 libgnss.bind(2, uart.VUART_0)
909 static int l_libgnss_bind(lua_State* L) {
910 int uart_id = luaL_checkinteger(L, 1);
911 l_libgnss_clear(L);
912 if (libgnss_recvbuff == NULL) {
913 libgnss_recvbuff = luat_heap_malloc(RECV_BUFF_SIZE);
915 if (luat_uart_exist(uart_id)) {
916 //uart_app_recvs[uart_id] = nmea_uart_recv_cb;
917 luat_uart_set_app_recv(uart_id, l_libgnss_uart_recv_cb);
919 if (lua_isinteger(L, 2)) {
920 libgnss_route_uart_id = luaL_checkinteger(L, 2);
922 return 0;
927 获取位置字符串
928 @api libgnss.locStr(mode)
929 @int 字符串模式. 0- Air780EG所需的格式
930 @return 指定模式的字符串
931 @usage
932 -- 仅推荐在定位成功后调用
934 static int l_libgnss_locStr(lua_State *L) {
935 int mode = luaL_optinteger(L, 1, 0);
936 char buff[64] = {0};
937 float lat_f = minmea_tofloat(&gnssctx.frame_rmc.latitude);
938 float lng_f = minmea_tofloat(&gnssctx.frame_rmc.longitude);
939 switch (mode)
941 case 0:
942 snprintf_(buff, 63, "%.7g,%c,%.7g,%c,1.0",
943 fabs(lat_f), lat_f > 0 ? 'N' : 'S',
944 fabs(lng_f), lng_f > 0 ? 'E' : 'W');
945 break;
946 case 1:
947 snprintf_(buff, 63, "%d,%d", gnssctx.frame_rmc.latitude.value, gnssctx.frame_rmc.longitude.value);
948 break;
949 default:
950 break;
952 lua_pushstring(L, buff);
953 return 1;
957 定位成功后自动设置RTC
958 @api libgnss.rtcAuto(enable)
959 @bool 开启与否, 默认是false关闭
960 @usage
961 -- 开启自动设置RTC
962 libgnss.rtcAuto(true)
964 static int l_libgnss_rtc_auto(lua_State *L) {
965 if (lua_isboolean(L, 1) && lua_toboolean(L, 1)) {
966 gnssctx.rtc_auto = 1;
967 LLOGD("GNSS->RTC Auto-Set now is ON");
969 else {
970 gnssctx.rtc_auto = 0;
971 LLOGD("GNSS->RTC Auto-Set now is OFF");
973 return 0;
976 //临时处理, 当前GNSS处理均在lua线程
977 // static lua_State *gnss_L;
978 static int gnss_raw_cb = 0;
980 int luat_libgnss_rawdata_cb(lua_State *L, void* ptr) {
981 rtos_msg_t* msg = (rtos_msg_t*)lua_topointer(L, -1);
982 // lua_getglobal(L, "sys_pub");
983 lua_geti(L, LUA_REGISTRYINDEX, gnss_raw_cb);
984 if (lua_isfunction(L, -1)) {
985 // lua_pushliteral(gnss_L, "GNSS_RAW_DATA");
986 lua_pushlstring(L, ptr, msg->arg1);
987 luat_heap_free(ptr);
988 ptr = NULL;
989 lua_call(L, 1, 0);
991 else {
992 luat_heap_free(ptr);
994 return 0;
997 int luat_libgnss_on_rawdata(const char* data, size_t len) {
998 if (gnss_raw_cb == 0)
999 return 0;
1000 char* ptr = luat_heap_malloc(len);
1001 if (ptr == NULL)
1002 return 0;
1003 memcpy(ptr, data, len);
1004 rtos_msg_t msg = {
1005 .handler = luat_libgnss_rawdata_cb,
1006 .arg1 = len,
1007 .ptr = ptr
1009 luat_msgbus_put(&msg, 0);
1010 return 0;
1014 底层事件回调
1015 @api libgnss.on(tp, fn)
1016 @string 事件类型,当前支持"raw"
1017 @usage
1018 -- 本函数一般用于调试, 用于获取底层实际收到的数据
1019 libgnss.on("raw", function(data)
1020 log.info("GNSS", data)
1021 end)
1023 static int l_libgnss_on(lua_State *L) {
1024 size_t len = 0;
1025 const char* tp = luaL_checklstring(L, 1, &len);
1026 if (!strcmp("raw", tp)) {
1027 if (gnss_raw_cb != 0) {
1028 luaL_unref(L, LUA_REGISTRYINDEX, gnss_raw_cb);
1029 gnss_raw_cb = 0;
1031 if (lua_isfunction(L, 2)) {
1032 lua_pushvalue(L, 2);
1033 gnss_raw_cb = luaL_ref(L, LUA_REGISTRYINDEX);
1036 return 0;
1040 获取非标的GPTXT数据
1041 @api libgnss.getTxt()
1042 @return GPTXT所携带的字符串
1043 @usage
1044 -- 本函数于2023.6.6 添加
1045 log.info("gnss", "txt", libgnss.getTxt())
1047 -- 测试语句
1048 libgnss.parse("$GPTXT,01,01,01,ANTENNA SHORT*63\r\n")
1049 log.info("GNSS", libgnss.getTxt())
1050 libgnss.parse("$GPTXT,01,01,01,ANTENNA OPEN*25\r\n")
1051 log.info("GNSS", libgnss.getTxt())
1052 libgnss.parse("$GPTXT,01,01,01,ANTENNA OK*35\r\n")
1053 log.info("GNSS", libgnss.getTxt())
1055 static int l_libgnss_get_txt(lua_State *L) {
1056 if (gnssctx.txt == NULL) {
1057 lua_pushliteral(L, "");
1058 return 1;
1060 struct minmea_sentence_txt txt = {0};
1061 minmea_parse_txt(&txt, gnssctx.txt->data);
1062 txt.txt[FRAME_TXT_MAX_LEN] = 0x00;
1063 lua_pushstring(L, txt.txt);
1064 return 1;
1068 合成Air530Z所需要的辅助定位数据
1069 @api libgnss.casic_aid(dt, loc)
1070 @table 时间信息
1071 @table 经纬度及海拔
1072 @return string 辅助定位数据
1073 @usage
1074 -- 本函数适合CASIC系列GNSS模块的辅助定位信息的合成
1075 -- 本函数 2023.11.14 新增
1077 -- 首先是时间信息,注意是UTC时间
1078 -- 时间来源很多, 一般建议socket.sntp()时间同步后的系统时间
1079 local dt = os.date("!*t")
1081 -- 然后是辅助定位坐标
1082 -- 来源有很多方式:
1083 -- 1. 从历史定位数据得到, 例如之前定位成功后保存到本地文件系统了
1084 -- 2. 通过基站定位或者wifi定位获取到
1085 -- 3. 通过IP定位获取到大概坐标
1086 -- 坐标系是WGS84, 但鉴于是辅助定位,精度不是关键因素
1087 local lla = {
1088 lat = 23.12,
1089 lng = 114.12
1092 local aid = libgnss.casic_aid(dt, lla)
1094 #include "luat_casic_gnss.h"
1095 double strtod(const char *s,char **ptr);
1096 static int l_libgnss_casic_aid(lua_State* L) {
1097 DATETIME_STR dt = {0};
1098 POS_LLA_STR lla = {0};
1099 const char* data = "";
1101 if (lua_istable(L, 1)) {
1102 if (LUA_TNUMBER == lua_getfield(L, 1, "day")) {
1103 dt.day = lua_tointeger(L, -1);
1105 lua_pop(L, 1);
1106 // 这里兼容month和mon两种, os.date 和 rtc.get
1107 if (LUA_TNUMBER == lua_getfield(L, 1, "month")) {
1108 dt.month = lua_tointeger(L, -1);
1110 lua_pop(L, 1);
1111 if (LUA_TNUMBER == lua_getfield(L, 1, "mon")) {
1112 dt.month = lua_tointeger(L, -1);
1114 lua_pop(L, 1);
1116 if (LUA_TNUMBER == lua_getfield(L, 1, "year")) {
1117 dt.year = lua_tointeger(L, -1);
1118 if (dt.year > 2022)
1119 dt.valid = 1;
1121 lua_pop(L, 1);
1122 if (LUA_TNUMBER == lua_getfield(L, 1, "hour")) {
1123 dt.hour = lua_tointeger(L, -1);
1125 lua_pop(L, 1);
1126 if (LUA_TNUMBER == lua_getfield(L, 1, "min")) {
1127 dt.minute = lua_tointeger(L, -1);
1129 lua_pop(L, 1);
1130 if (LUA_TNUMBER == lua_getfield(L, 1, "sec")) {
1131 dt.second = lua_tointeger(L, -1);
1133 lua_pop(L, 1);
1135 if (lua_istable(L, 2)) {
1136 lua_getfield(L, 2, "lat");
1137 if (LUA_TNUMBER == lua_type(L, -1)) {
1138 lla.lat = lua_tonumber(L, -1);
1140 else if (LUA_TSTRING == lua_type(L, -1)) {
1141 data = luaL_checkstring(L, -1);
1142 lla.lat = strtod(data, NULL);
1144 lua_pop(L, 1);
1146 lua_getfield(L, 2, "lng");
1147 if (LUA_TNUMBER == lua_type(L, -1)) {
1148 lla.lon = lua_tonumber(L, -1);
1150 else if (LUA_TSTRING == lua_type(L, -1)) {
1151 data = luaL_checkstring(L, -1);
1152 lla.lon = strtod(data, NULL);
1154 lua_pop(L, 1);
1155 if (LUA_TNUMBER == lua_getfield(L, 2, "alt")) {
1156 lla.alt = lua_tonumber(L, -1);
1159 if (lla.lat > 0.001 || lla.lat < -0.01)
1160 if (lla.lon > 0.001 || lla.lon < -0.01)
1161 lla.valid = 1;
1163 char tmp[66] = {0};
1164 casicAgnssAidIni(&dt, &lla, tmp);
1165 lua_pushlstring(L, tmp, 66);
1166 return 1;
1169 #include "rotable2.h"
1170 static const rotable_Reg_t reg_libgnss[] =
1172 { "parse", ROREG_FUNC(l_libgnss_parse)},
1173 { "isFix", ROREG_FUNC(l_libgnss_is_fix)},
1174 { "getIntLocation", ROREG_FUNC(l_libgnss_get_int_location)},
1175 { "getRmc", ROREG_FUNC(l_libgnss_get_rmc)},
1176 { "getGsv", ROREG_FUNC(l_libgnss_get_gsv)},
1177 { "getGsa", ROREG_FUNC(l_libgnss_get_gsa)},
1178 { "getVtg", ROREG_FUNC(l_libgnss_get_vtg)},
1179 { "getGga", ROREG_FUNC(l_libgnss_get_gga)},
1180 { "getGll", ROREG_FUNC(l_libgnss_get_gll)},
1181 { "getZda", ROREG_FUNC(l_libgnss_get_zda)},
1182 { "locStr", ROREG_FUNC(l_libgnss_locStr)},
1183 { "rtcAuto",ROREG_FUNC(l_libgnss_rtc_auto)},
1184 { "on", ROREG_FUNC(l_libgnss_on)},
1186 { "debug", ROREG_FUNC(l_libgnss_debug)},
1187 { "clear", ROREG_FUNC(l_libgnss_clear)},
1188 { "bind", ROREG_FUNC(l_libgnss_bind)},
1190 { "getTxt", ROREG_FUNC(l_libgnss_get_txt)},
1191 { "casic_aid", ROREG_FUNC(l_libgnss_casic_aid)},
1193 { NULL, ROREG_INT(0)}
1196 LUAMOD_API int luaopen_libgnss( lua_State *L ) {
1197 luat_newlib2(L, reg_libgnss);
1198 return 1;