Implement a secure ICS protocol targeting LoRa Node151 microcontroller for controlling irrigation.
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 
 

659 lines
16 KiB

  1. /*!
  2. * \file gps.c
  3. *
  4. * \brief GPS driver implementation
  5. *
  6. * \copyright Revised BSD License, see section \ref LICENSE.
  7. *
  8. * \code
  9. * ______ _
  10. * / _____) _ | |
  11. * ( (____ _____ ____ _| |_ _____ ____| |__
  12. * \____ \| ___ | (_ _) ___ |/ ___) _ \
  13. * _____) ) ____| | | || |_| ____( (___| | | |
  14. * (______/|_____)_|_|_| \__)_____)\____)_| |_|
  15. * (C)2013-2017 Semtech
  16. *
  17. * \endcode
  18. *
  19. * \author Miguel Luis ( Semtech )
  20. *
  21. * \author Gregory Cristian ( Semtech )
  22. */
  23. #include <stdint.h>
  24. #include <stdlib.h>
  25. #include <math.h>
  26. #include <stdio.h>
  27. #include <string.h>
  28. #include "utilities.h"
  29. #include "board.h"
  30. #include "rtc-board.h"
  31. #include "gps-board.h"
  32. #include "gps.h"
  33. #define TRIGGER_GPS_CNT 10
  34. /* Various type of NMEA data we can receive with the Gps */
  35. const char NmeaDataTypeGPGGA[] = "GPGGA";
  36. const char NmeaDataTypeGPGSA[] = "GPGSA";
  37. const char NmeaDataTypeGPGSV[] = "GPGSV";
  38. const char NmeaDataTypeGPRMC[] = "GPRMC";
  39. /* Value used for the conversion of the position from DMS to decimal */
  40. const int32_t MaxNorthPosition = 8388607; // 2^23 - 1
  41. const int32_t MaxSouthPosition = 8388608; // -2^23
  42. const int32_t MaxEastPosition = 8388607; // 2^23 - 1
  43. const int32_t MaxWestPosition = 8388608; // -2^23
  44. NmeaGpsData_t NmeaGpsData;
  45. static double HasFix = false;
  46. static double Latitude = 0;
  47. static double Longitude = 0;
  48. static int32_t LatitudeBinary = 0;
  49. static int32_t LongitudeBinary = 0;
  50. static int16_t Altitude = ( int16_t )0xFFFF;
  51. static uint32_t PpsCnt = 0;
  52. bool PpsDetected = false;
  53. void GpsPpsHandler( bool *parseData )
  54. {
  55. PpsDetected = true;
  56. PpsCnt++;
  57. *parseData = false;
  58. if( PpsCnt >= TRIGGER_GPS_CNT )
  59. {
  60. PpsCnt = 0;
  61. *parseData = true;
  62. }
  63. }
  64. void GpsInit( void )
  65. {
  66. PpsDetected = false;
  67. GpsMcuInit( );
  68. }
  69. void GpsStart( void )
  70. {
  71. GpsMcuStart( );
  72. }
  73. void GpsStop( void )
  74. {
  75. GpsMcuStop( );
  76. }
  77. void GpsProcess( void )
  78. {
  79. GpsMcuProcess( );
  80. }
  81. bool GpsGetPpsDetectedState( void )
  82. {
  83. bool state = false;
  84. CRITICAL_SECTION_BEGIN( );
  85. state = PpsDetected;
  86. PpsDetected = false;
  87. CRITICAL_SECTION_END( );
  88. return state;
  89. }
  90. bool GpsHasFix( void )
  91. {
  92. return HasFix;
  93. }
  94. void GpsConvertPositionIntoBinary( void )
  95. {
  96. long double temp;
  97. if( Latitude >= 0 ) // North
  98. {
  99. temp = Latitude * MaxNorthPosition;
  100. LatitudeBinary = temp / 90;
  101. }
  102. else // South
  103. {
  104. temp = Latitude * MaxSouthPosition;
  105. LatitudeBinary = temp / 90;
  106. }
  107. if( Longitude >= 0 ) // East
  108. {
  109. temp = Longitude * MaxEastPosition;
  110. LongitudeBinary = temp / 180;
  111. }
  112. else // West
  113. {
  114. temp = Longitude * MaxWestPosition;
  115. LongitudeBinary = temp / 180;
  116. }
  117. }
  118. void GpsConvertPositionFromStringToNumerical( void )
  119. {
  120. int i;
  121. double valueTmp1;
  122. double valueTmp2;
  123. double valueTmp3;
  124. double valueTmp4;
  125. // Convert the latitude from ASCII to uint8_t values
  126. for( i = 0 ; i < 10 ; i++ )
  127. {
  128. NmeaGpsData.NmeaLatitude[i] = NmeaGpsData.NmeaLatitude[i] & 0xF;
  129. }
  130. // Convert latitude from degree/minute/second (DMS) format into decimal
  131. valueTmp1 = ( double )NmeaGpsData.NmeaLatitude[0] * 10.0 + ( double )NmeaGpsData.NmeaLatitude[1];
  132. valueTmp2 = ( double )NmeaGpsData.NmeaLatitude[2] * 10.0 + ( double )NmeaGpsData.NmeaLatitude[3];
  133. valueTmp3 = ( double )NmeaGpsData.NmeaLatitude[5] * 1000.0 + ( double )NmeaGpsData.NmeaLatitude[6] * 100.0 +
  134. ( double )NmeaGpsData.NmeaLatitude[7] * 10.0 + ( double )NmeaGpsData.NmeaLatitude[8];
  135. Latitude = valueTmp1 + ( ( valueTmp2 + ( valueTmp3 * 0.0001 ) ) / 60.0 );
  136. if( NmeaGpsData.NmeaLatitudePole[0] == 'S' )
  137. {
  138. Latitude *= -1;
  139. }
  140. // Convert the longitude from ASCII to uint8_t values
  141. for( i = 0 ; i < 10 ; i++ )
  142. {
  143. NmeaGpsData.NmeaLongitude[i] = NmeaGpsData.NmeaLongitude[i] & 0xF;
  144. }
  145. // Convert longitude from degree/minute/second (DMS) format into decimal
  146. valueTmp1 = ( double )NmeaGpsData.NmeaLongitude[0] * 100.0 + ( double )NmeaGpsData.NmeaLongitude[1] * 10.0 + ( double )NmeaGpsData.NmeaLongitude[2];
  147. valueTmp2 = ( double )NmeaGpsData.NmeaLongitude[3] * 10.0 + ( double )NmeaGpsData.NmeaLongitude[4];
  148. valueTmp3 = ( double )NmeaGpsData.NmeaLongitude[6] * 1000.0 + ( double )NmeaGpsData.NmeaLongitude[7] * 100;
  149. valueTmp4 = ( double )NmeaGpsData.NmeaLongitude[8] * 10.0 + ( double )NmeaGpsData.NmeaLongitude[9];
  150. Longitude = valueTmp1 + ( valueTmp2 / 60.0 ) + ( ( ( valueTmp3 + valueTmp4 ) * 0.0001 ) / 60.0 );
  151. if( NmeaGpsData.NmeaLongitudePole[0] == 'W' )
  152. {
  153. Longitude *= -1;
  154. }
  155. }
  156. LmnStatus_t GpsGetLatestGpsPositionDouble( double *lati, double *longi )
  157. {
  158. LmnStatus_t status = LMN_STATUS_ERROR;
  159. if( HasFix == true )
  160. {
  161. status = LMN_STATUS_OK;
  162. }
  163. else
  164. {
  165. GpsResetPosition( );
  166. }
  167. *lati = Latitude;
  168. *longi = Longitude;
  169. return status;
  170. }
  171. LmnStatus_t GpsGetLatestGpsPositionBinary( int32_t *latiBin, int32_t *longiBin )
  172. {
  173. LmnStatus_t status = LMN_STATUS_ERROR;
  174. CRITICAL_SECTION_BEGIN( );
  175. if( HasFix == true )
  176. {
  177. status = LMN_STATUS_OK;
  178. }
  179. else
  180. {
  181. GpsResetPosition( );
  182. }
  183. *latiBin = LatitudeBinary;
  184. *longiBin = LongitudeBinary;
  185. CRITICAL_SECTION_END( );
  186. return status;
  187. }
  188. int16_t GpsGetLatestGpsAltitude( void )
  189. {
  190. CRITICAL_SECTION_BEGIN( );
  191. if( HasFix == true )
  192. {
  193. Altitude = atoi( NmeaGpsData.NmeaAltitude );
  194. }
  195. else
  196. {
  197. Altitude = ( int16_t )0xFFFF;
  198. }
  199. CRITICAL_SECTION_END( );
  200. return Altitude;
  201. }
  202. /*!
  203. * Calculates the checksum for a NMEA sentence
  204. *
  205. * Skip the first '$' if necessary and calculate checksum until '*' character is
  206. * reached (or buffSize exceeded).
  207. *
  208. * \retval chkPosIdx Position of the checksum in the sentence
  209. */
  210. int32_t GpsNmeaChecksum( int8_t *nmeaStr, int32_t nmeaStrSize, int8_t * checksum )
  211. {
  212. int i = 0;
  213. uint8_t checkNum = 0;
  214. // Check input parameters
  215. if( ( nmeaStr == NULL ) || ( checksum == NULL ) || ( nmeaStrSize <= 1 ) )
  216. {
  217. return -1;
  218. }
  219. // Skip the first '$' if necessary
  220. if( nmeaStr[i] == '$' )
  221. {
  222. i += 1;
  223. }
  224. // XOR until '*' or max length is reached
  225. while( nmeaStr[i] != '*' )
  226. {
  227. checkNum ^= nmeaStr[i];
  228. i += 1;
  229. if( i >= nmeaStrSize )
  230. {
  231. return -1;
  232. }
  233. }
  234. // Convert checksum value to 2 hexadecimal characters
  235. checksum[0] = Nibble2HexChar( checkNum / 16 ); // upper nibble
  236. checksum[1] = Nibble2HexChar( checkNum % 16 ); // lower nibble
  237. return i + 1;
  238. }
  239. /*!
  240. * Calculate the checksum of a NMEA frame and compare it to the checksum that is
  241. * present at the end of it.
  242. * Return true if it matches
  243. */
  244. static bool GpsNmeaValidateChecksum( int8_t *serialBuff, int32_t buffSize )
  245. {
  246. int32_t checksumIndex;
  247. int8_t checksum[2]; // 2 characters to calculate NMEA checksum
  248. checksumIndex = GpsNmeaChecksum( serialBuff, buffSize, checksum );
  249. // could we calculate a verification checksum ?
  250. if( checksumIndex < 0 )
  251. {
  252. return false;
  253. }
  254. // check if there are enough char in the serial buffer to read checksum
  255. if( checksumIndex >= ( buffSize - 2 ) )
  256. {
  257. return false;
  258. }
  259. // check the checksum
  260. if( ( serialBuff[checksumIndex] == checksum[0] ) && ( serialBuff[checksumIndex + 1] == checksum[1] ) )
  261. {
  262. return true;
  263. }
  264. else
  265. {
  266. return false;
  267. }
  268. }
  269. LmnStatus_t GpsParseGpsData( int8_t *rxBuffer, int32_t rxBufferSize )
  270. {
  271. uint8_t i = 1;
  272. uint8_t j = 0;
  273. uint8_t fieldSize = 0;
  274. if( rxBuffer[0] != '$' )
  275. {
  276. GpsMcuInvertPpsTrigger( );
  277. return LMN_STATUS_ERROR;
  278. }
  279. if( GpsNmeaValidateChecksum( rxBuffer, rxBufferSize ) == false )
  280. {
  281. return LMN_STATUS_ERROR;
  282. }
  283. fieldSize = 0;
  284. while( rxBuffer[i + fieldSize++] != ',' )
  285. {
  286. if( fieldSize > 6 )
  287. {
  288. return LMN_STATUS_ERROR;
  289. }
  290. }
  291. for( j = 0; j < fieldSize; j++, i++ )
  292. {
  293. NmeaGpsData.NmeaDataType[j] = rxBuffer[i];
  294. }
  295. // Parse the GPGGA data
  296. if( strncmp( ( const char* )NmeaGpsData.NmeaDataType, ( const char* )NmeaDataTypeGPGGA, 5 ) == 0 )
  297. {
  298. // NmeaUtcTime
  299. fieldSize = 0;
  300. while( rxBuffer[i + fieldSize++] != ',' )
  301. {
  302. if( fieldSize > 11 )
  303. {
  304. return LMN_STATUS_ERROR;
  305. }
  306. }
  307. for( j = 0; j < fieldSize; j++, i++ )
  308. {
  309. NmeaGpsData.NmeaUtcTime[j] = rxBuffer[i];
  310. }
  311. // NmeaLatitude
  312. fieldSize = 0;
  313. while( rxBuffer[i + fieldSize++] != ',' )
  314. {
  315. if( fieldSize > 10 )
  316. {
  317. return LMN_STATUS_ERROR;
  318. }
  319. }
  320. for( j = 0; j < fieldSize; j++, i++ )
  321. {
  322. NmeaGpsData.NmeaLatitude[j] = rxBuffer[i];
  323. }
  324. // NmeaLatitudePole
  325. fieldSize = 0;
  326. while( rxBuffer[i + fieldSize++] != ',' )
  327. {
  328. if( fieldSize > 2 )
  329. {
  330. return LMN_STATUS_ERROR;
  331. }
  332. }
  333. for( j = 0; j < fieldSize; j++, i++ )
  334. {
  335. NmeaGpsData.NmeaLatitudePole[j] = rxBuffer[i];
  336. }
  337. // NmeaLongitude
  338. fieldSize = 0;
  339. while( rxBuffer[i + fieldSize++] != ',' )
  340. {
  341. if( fieldSize > 11 )
  342. {
  343. return LMN_STATUS_ERROR;
  344. }
  345. }
  346. for( j = 0; j < fieldSize; j++, i++ )
  347. {
  348. NmeaGpsData.NmeaLongitude[j] = rxBuffer[i];
  349. }
  350. // NmeaLongitudePole
  351. fieldSize = 0;
  352. while( rxBuffer[i + fieldSize++] != ',' )
  353. {
  354. if( fieldSize > 2 )
  355. {
  356. return LMN_STATUS_ERROR;
  357. }
  358. }
  359. for( j = 0; j < fieldSize; j++, i++ )
  360. {
  361. NmeaGpsData.NmeaLongitudePole[j] = rxBuffer[i];
  362. }
  363. // NmeaFixQuality
  364. fieldSize = 0;
  365. while( rxBuffer[i + fieldSize++] != ',' )
  366. {
  367. if( fieldSize > 2 )
  368. {
  369. return LMN_STATUS_ERROR;
  370. }
  371. }
  372. for( j = 0; j < fieldSize; j++, i++ )
  373. {
  374. NmeaGpsData.NmeaFixQuality[j] = rxBuffer[i];
  375. }
  376. // NmeaSatelliteTracked
  377. fieldSize = 0;
  378. while( rxBuffer[i + fieldSize++] != ',' )
  379. {
  380. if( fieldSize > 3 )
  381. {
  382. return LMN_STATUS_ERROR;
  383. }
  384. }
  385. for( j = 0; j < fieldSize; j++, i++ )
  386. {
  387. NmeaGpsData.NmeaSatelliteTracked[j] = rxBuffer[i];
  388. }
  389. // NmeaHorizontalDilution
  390. fieldSize = 0;
  391. while( rxBuffer[i + fieldSize++] != ',' )
  392. {
  393. if( fieldSize > 6 )
  394. {
  395. return LMN_STATUS_ERROR;
  396. }
  397. }
  398. for( j = 0; j < fieldSize; j++, i++ )
  399. {
  400. NmeaGpsData.NmeaHorizontalDilution[j] = rxBuffer[i];
  401. }
  402. // NmeaAltitude
  403. fieldSize = 0;
  404. while( rxBuffer[i + fieldSize++] != ',' )
  405. {
  406. if( fieldSize > 8 )
  407. {
  408. return LMN_STATUS_ERROR;
  409. }
  410. }
  411. for( j = 0; j < fieldSize; j++, i++ )
  412. {
  413. NmeaGpsData.NmeaAltitude[j] = rxBuffer[i];
  414. }
  415. // NmeaAltitudeUnit
  416. fieldSize = 0;
  417. while( rxBuffer[i + fieldSize++] != ',' )
  418. {
  419. if( fieldSize > 2 )
  420. {
  421. return LMN_STATUS_ERROR;
  422. }
  423. }
  424. for( j = 0; j < fieldSize; j++, i++ )
  425. {
  426. NmeaGpsData.NmeaAltitudeUnit[j] = rxBuffer[i];
  427. }
  428. // NmeaHeightGeoid
  429. fieldSize = 0;
  430. while( rxBuffer[i + fieldSize++] != ',' )
  431. {
  432. if( fieldSize > 8 )
  433. {
  434. return LMN_STATUS_ERROR;
  435. }
  436. }
  437. for( j = 0; j < fieldSize; j++, i++ )
  438. {
  439. NmeaGpsData.NmeaHeightGeoid[j] = rxBuffer[i];
  440. }
  441. // NmeaHeightGeoidUnit
  442. fieldSize = 0;
  443. while( rxBuffer[i + fieldSize++] != ',' )
  444. {
  445. if( fieldSize > 2 )
  446. {
  447. return LMN_STATUS_ERROR;
  448. }
  449. }
  450. for( j = 0; j < fieldSize; j++, i++ )
  451. {
  452. NmeaGpsData.NmeaHeightGeoidUnit[j] = rxBuffer[i];
  453. }
  454. GpsFormatGpsData( );
  455. return LMN_STATUS_OK;
  456. }
  457. else if ( strncmp( ( const char* )NmeaGpsData.NmeaDataType, ( const char* )NmeaDataTypeGPRMC, 5 ) == 0 )
  458. {
  459. // NmeaUtcTime
  460. fieldSize = 0;
  461. while( rxBuffer[i + fieldSize++] != ',' )
  462. {
  463. if( fieldSize > 11 )
  464. {
  465. return LMN_STATUS_ERROR;
  466. }
  467. }
  468. for( j = 0; j < fieldSize; j++, i++ )
  469. {
  470. NmeaGpsData.NmeaUtcTime[j] = rxBuffer[i];
  471. }
  472. // NmeaDataStatus
  473. fieldSize = 0;
  474. while( rxBuffer[i + fieldSize++] != ',' )
  475. {
  476. if( fieldSize > 2 )
  477. {
  478. return LMN_STATUS_ERROR;
  479. }
  480. }
  481. for( j = 0; j < fieldSize; j++, i++ )
  482. {
  483. NmeaGpsData.NmeaDataStatus[j] = rxBuffer[i];
  484. }
  485. // NmeaLatitude
  486. fieldSize = 0;
  487. while( rxBuffer[i + fieldSize++] != ',' )
  488. {
  489. if( fieldSize > 10 )
  490. {
  491. return LMN_STATUS_ERROR;
  492. }
  493. }
  494. for( j = 0; j < fieldSize; j++, i++ )
  495. {
  496. NmeaGpsData.NmeaLatitude[j] = rxBuffer[i];
  497. }
  498. // NmeaLatitudePole
  499. fieldSize = 0;
  500. while( rxBuffer[i + fieldSize++] != ',' )
  501. {
  502. if( fieldSize > 2 )
  503. {
  504. return LMN_STATUS_ERROR;
  505. }
  506. }
  507. for( j = 0; j < fieldSize; j++, i++ )
  508. {
  509. NmeaGpsData.NmeaLatitudePole[j] = rxBuffer[i];
  510. }
  511. // NmeaLongitude
  512. fieldSize = 0;
  513. while( rxBuffer[i + fieldSize++] != ',' )
  514. {
  515. if( fieldSize > 11 )
  516. {
  517. return LMN_STATUS_ERROR;
  518. }
  519. }
  520. for( j = 0; j < fieldSize; j++, i++ )
  521. {
  522. NmeaGpsData.NmeaLongitude[j] = rxBuffer[i];
  523. }
  524. // NmeaLongitudePole
  525. fieldSize = 0;
  526. while( rxBuffer[i + fieldSize++] != ',' )
  527. {
  528. if( fieldSize > 2 )
  529. {
  530. return LMN_STATUS_ERROR;
  531. }
  532. }
  533. for( j = 0; j < fieldSize; j++, i++ )
  534. {
  535. NmeaGpsData.NmeaLongitudePole[j] = rxBuffer[i];
  536. }
  537. // NmeaSpeed
  538. fieldSize = 0;
  539. while( rxBuffer[i + fieldSize++] != ',' )
  540. {
  541. if( fieldSize > 8 )
  542. {
  543. return LMN_STATUS_ERROR;
  544. }
  545. }
  546. for( j = 0; j < fieldSize; j++, i++ )
  547. {
  548. NmeaGpsData.NmeaSpeed[j] = rxBuffer[i];
  549. }
  550. // NmeaDetectionAngle
  551. fieldSize = 0;
  552. while( rxBuffer[i + fieldSize++] != ',' )
  553. {
  554. if( fieldSize > 8 )
  555. {
  556. return LMN_STATUS_ERROR;
  557. }
  558. }
  559. for( j = 0; j < fieldSize; j++, i++ )
  560. {
  561. NmeaGpsData.NmeaDetectionAngle[j] = rxBuffer[i];
  562. }
  563. // NmeaDate
  564. fieldSize = 0;
  565. while( rxBuffer[i + fieldSize++] != ',' )
  566. {
  567. if( fieldSize > 8 )
  568. {
  569. return LMN_STATUS_ERROR;
  570. }
  571. }
  572. for( j = 0; j < fieldSize; j++, i++ )
  573. {
  574. NmeaGpsData.NmeaDate[j] = rxBuffer[i];
  575. }
  576. GpsFormatGpsData( );
  577. return LMN_STATUS_OK;
  578. }
  579. else
  580. {
  581. return LMN_STATUS_ERROR;
  582. }
  583. }
  584. void GpsFormatGpsData( void )
  585. {
  586. if( strncmp( ( const char* )NmeaGpsData.NmeaDataType, ( const char* )NmeaDataTypeGPGGA, 5 ) == 0 )
  587. {
  588. HasFix = ( NmeaGpsData.NmeaFixQuality[0] > 0x30 ) ? true : false;
  589. }
  590. else if ( strncmp( ( const char* )NmeaGpsData.NmeaDataType, ( const char* )NmeaDataTypeGPRMC, 5 ) == 0 )
  591. {
  592. HasFix = ( NmeaGpsData.NmeaDataStatus[0] == 0x41 ) ? true : false;
  593. }
  594. GpsConvertPositionFromStringToNumerical( );
  595. GpsConvertPositionIntoBinary( );
  596. }
  597. void GpsResetPosition( void )
  598. {
  599. Altitude = ( int16_t )0xFFFF;
  600. Latitude = 0;
  601. Longitude = 0;
  602. LatitudeBinary = 0;
  603. LongitudeBinary = 0;
  604. }