air530z.c 6.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199
  1. #include "air530z.h"
  2. #include "../swapi/subjects/serial/serial.h"
  3. #include "./nmealib/include/nmea/nmea.h"
  4. // 模块名称
  5. static const char MODULE_NAME[] = "Air530Z";
  6. // 设置输出频率
  7. static const char *PCAS02 = "$PCAS02,1000*2E\r\n";
  8. // 设置输出格式
  9. static const char *PCAS03 = "$PCAS03,1,0,0,1,1,0,0,0,0,0,,,0,0*03\r\n";
  10. // 设置卫星组合
  11. static const char *PCAS04 = "$PCAS04,13,13,11*29\r\n";
  12. // 设置接收模式: "静态模式"
  13. static const char *PCAS11 = "$PCAS11,1*1C\r\n";
  14. // 定义关键控制符号
  15. #define LF '\n' // 换行
  16. // 输出控制精度阈值
  17. #define HDOP_MAX 2.0 // 水平精度判定阈值
  18. #define MIN_SATELLITES 5 // 最小可见卫星(个)
  19. #define STATIONARY_RADIUS 3.0 // 静止判定半径(米)
  20. #define CONT_STATIONARY_FRAMES 4 // 累计连续静止帧数
  21. #define POS_SMOOTHING_NORMAL 0.6 // 正常定位滑动系数
  22. #define POS_SMOOTHING_WEAK 0.2 // 弱定位时滑动系数
  23. #define CONT_WEAK_FRAMES 8 // 连续弱定位的帧数
  24. // 定义无效的坐标值
  25. #define COORD_NAN NAN
  26. #define IS_COORD_NAN(x) isnan(x)
  27. // 与模块的串口通讯
  28. typedef struct
  29. {
  30. void *h; // 打开的串口句柄
  31. nmeaINFO info; // decoded NMEA data
  32. nmeaPARSER parser; // NMEA decoder解码器
  33. struct
  34. { // 存储经过平滑处理的经纬度坐标(滤波轨迹)
  35. double lat;
  36. double lon;
  37. int stationary_cnt; // 连续静止帧数统计
  38. int weak_cnt; // 连续弱定位帧数统计
  39. } filter;
  40. struct
  41. {
  42. bool bOK; // 定位是否成功
  43. char lat[MAX_LINE_CHARS]; // 纬度字符串
  44. char lon[MAX_LINE_CHARS]; // 经度字符串
  45. } outPos2d;
  46. void *out_rwlock;
  47. } SAir530ZCom;
  48. static SAir530ZCom s_myCom;
  49. // 接收、处理来自Air530Z模块的数据报文帧, 串口-线程回调
  50. static int comio_data_recv_proc(unsigned long wParam/*传递打开的串口句柄*/, unsigned long lParam/*保留暂未使用*/)
  51. {
  52. SAir530ZCom *pComIO = &s_myCom; void *pSerial = pComIO->h;
  53. const unsigned char *pRecvBuf = serial_get_recv_buffer(pSerial); int nRecvBytes = serial_get_recv_buffer_bytes(pSerial);
  54. const char *log_prefix = serial_get_log_prefix(pSerial); int ret; bool bWeakPos;
  55. if (nRecvBytes <= 0 || pRecvBuf[nRecvBytes - 1] != LF) goto ret_p;
  56. sw_log_trace("[%s] %s received a frame(%d bytes): %s", MODULE_NAME, log_prefix, nRecvBytes, pRecvBuf);
  57. ret = nmea_parse(&pComIO->parser, (const char *)pRecvBuf, nRecvBytes, &pComIO->info);
  58. serial_clear_recv_buffer(pSerial); // 当前数据帧已解析, 清空接收缓存区, 等待下一数据帧到来
  59. if(ret <= 0) { /*sw_log_error("[%s] failed to parse a NMEA frame!!", MODULE_NAME);*/ goto ret_p; }
  60. if(pComIO->info.sig > 0)
  61. {
  62. if(pComIO->info.HDOP <= HDOP_MAX && pComIO->info.satinfo.inview >= MIN_SATELLITES) { bWeakPos = false; pComIO->filter.weak_cnt = 0; }
  63. else { bWeakPos = true; pComIO->filter.weak_cnt++; }
  64. double lat = pComIO->info.lat, lon = pComIO->info.lon;
  65. if(IS_COORD_NAN(pComIO->filter.lat) || IS_COORD_NAN(pComIO->filter.lon))
  66. {
  67. pComIO->filter.lat = lat;
  68. pComIO->filter.lon = lon;
  69. }
  70. nmeaPOS pos1, pos2;
  71. nmea_info2pos(&pComIO->info, &pos1);
  72. pos2.lat = nmea_ndeg2radian(pComIO->filter.lat);
  73. pos2.lon = nmea_ndeg2radian(pComIO->filter.lon);
  74. double dist = nmea_distance(&pos2, &pos1);
  75. if(dist < STATIONARY_RADIUS) { pComIO->filter.stationary_cnt++; }
  76. else
  77. {
  78. pComIO->filter.stationary_cnt = 0;
  79. if(bWeakPos) pComIO->filter.lat = pComIO->filter.lon = COORD_NAN;
  80. else { pComIO->filter.lat = lat; pComIO->filter.lon = lon; }
  81. }
  82. if(pComIO->filter.stationary_cnt >= CONT_STATIONARY_FRAMES)
  83. { /////静止状态处理
  84. // 1-应用滑动平均
  85. double smoothing = bWeakPos ? POS_SMOOTHING_WEAK : POS_SMOOTHING_NORMAL;
  86. pComIO->filter.lat = smoothing * lat + (1 - smoothing) * pComIO->filter.lat;
  87. pComIO->filter.lon = smoothing * lon + (1 - smoothing) * pComIO->filter.lon;
  88. // 2-输出成字符串
  89. double out_lat = nmea_ndeg2degree(pComIO->filter.lat); double out_lon = nmea_ndeg2degree(pComIO->filter.lon);
  90. sw_rwlock_wrlock(pComIO->out_rwlock, WAIT_FOREVER);
  91. sprintf(pComIO->outPos2d.lat, "%.6f", out_lat);
  92. sprintf(pComIO->outPos2d.lon, "%.6f", out_lon);
  93. pComIO->outPos2d.bOK = true;
  94. sw_rwlock_unlock(pComIO->out_rwlock);
  95. // 3-打印定位结果
  96. sw_log_trace("[%s] 已定位: lat=%.6f, lon=%.6f, HDOP=%.1f, Sats=%d/%d, DeltaMeters=%.1fm, IsWeakPos=%d", MODULE_NAME, \
  97. out_lat, out_lon, pComIO->info.HDOP, \
  98. pComIO->info.satinfo.inuse, pComIO->info.satinfo.inview, dist, bWeakPos);
  99. }
  100. if(pComIO->filter.weak_cnt >= CONT_WEAK_FRAMES)
  101. {
  102. pComIO->filter.weak_cnt = pComIO->filter.stationary_cnt = 0;
  103. pComIO->filter.lat = pComIO->filter.lon = COORD_NAN;
  104. }
  105. }
  106. ret_p: return 1; // 持续累加的接收新数据
  107. }
  108. // 打开与模块的通讯, 返回: 0成功, <0时失败
  109. int Air530Z_ComInit()
  110. {
  111. nmea_zero_INFO(&s_myCom.info);
  112. s_myCom.filter.lat = s_myCom.filter.lon = COORD_NAN;
  113. nmea_parser_init(&s_myCom.parser);
  114. s_myCom.out_rwlock = sw_rwlock_create();
  115. if(!s_myCom.out_rwlock)
  116. {
  117. sw_log_error("[%s] failed to create the \"outPos2d\" rwlock!!", MODULE_NAME);
  118. Air530Z_ComExit(); return -1;
  119. }
  120. #ifdef _DEBUG // 上位机单元测试时使用
  121. const char *serialName = "/dev/ttyS0"; int baudrate = 9600;
  122. #else
  123. const char *serialName = "/dev/ttymxc6"; int baudrate = 9600;
  124. #endif
  125. const char *parityCheck = "none"; // 无校检
  126. s_myCom.h = serial_open(serialName, baudrate, parityCheck, \
  127. comio_data_recv_proc, comio_data_recv_proc, NULL);
  128. if(!s_myCom.h)
  129. {
  130. sw_log_error("[%s] failed to open the \"%s:%d(%s parity)\" device!!", \
  131. MODULE_NAME, serialName, baudrate, parityCheck);
  132. Air530Z_ComExit(); return -2;
  133. }
  134. serial_send_data(s_myCom.h, (unsigned char *)PCAS02, strlen(PCAS02));
  135. serial_send_data(s_myCom.h, (unsigned char *)PCAS03, strlen(PCAS03));
  136. serial_send_data(s_myCom.h, (unsigned char *)PCAS04, strlen(PCAS04));
  137. serial_send_data(s_myCom.h, (unsigned char *)PCAS11, strlen(PCAS11));
  138. return 0;
  139. }
  140. // 关闭与模块的通讯, 返回: 0成功, <0时失败
  141. int Air530Z_ComExit()
  142. {
  143. if(s_myCom.h) serial_close(s_myCom.h, WAITTHRD_SAFEEXIT_TIMEOUT);
  144. if(s_myCom.out_rwlock) sw_rwlock_destroy(s_myCom.out_rwlock);
  145. if(s_myCom.parser.buffer) nmea_parser_destroy(&s_myCom.parser);
  146. memset(&s_myCom, 0, sizeof(s_myCom));
  147. return 0;
  148. }
  149. // 获取当前的2D位置, 返回: 0成功, -1未定位
  150. int Air530Z_GetPos2D(char lat[MAX_LINE_CHARS], char lon[MAX_LINE_CHARS])
  151. {
  152. bool bOK = false;
  153. sw_rwlock_rdlock(s_myCom.out_rwlock, WAIT_FOREVER);
  154. bOK = s_myCom.outPos2d.bOK;
  155. if(bOK) {
  156. strcpy(lat, s_myCom.outPos2d.lat);
  157. strcpy(lon, s_myCom.outPos2d.lon);
  158. }
  159. sw_rwlock_unlock(s_myCom.out_rwlock);
  160. return bOK ? 0 : -1;
  161. }
  162. #ifdef _DEBUG
  163. void *Air530Z_GetSerialHandle()
  164. {
  165. return s_myCom.h;
  166. }
  167. #endif