。
Check the reference manual, the USB Serial of Quectel's chip with GPS is as follows.
1 Application Introduction
1. If you do not use the AT+QGPSCFG command to configure EC20, the GPS parameters will be turned on with the default parameters, and the NMEA port will start reporting. The default value of "gpsnmeatype" is 31, and the reporting interval is 1s. All types of NMEA data (GGA\RMC\GSV\GSA\VTG) are reported each time. If this default configuration is used, most users will feel that the data reported at a time is too much and a lot of information is repeated. It is recommended that you use QGPSCFG to configure the NMEA data format you need. For the specific format differences, please refer to the description of NMEA data on the Internet.
2. Acquisition of NMEA data in Linux environment:
cat /dev/ttyUSB1 & // NMEA data is output from ttyUSB1
echo "AT+QGPS=1" > /dev/ttyUSB2 // Start GPS session
You can observe that ttyUSB1 outputs NMEA data, as follows:
3. During the programming process, if there is a need to update the location at a fixed frequency, you can consider reading the NMEA port data and configuring the NMEA format and data update interval that suits your needs. If the product executes the acquisition position command at a low frequency and the interval is not fixed, you can also consider using the AT+QGPSLOC command directly on the AT command port to obtain real-time location information.
2 GPS data analysis
NMEA 0183 is a standard format developed by the National Marine Electronics Association for marine electronic equipment. It has now become the unified RTCM (Radio Technical Commission for Maritime services) standard protocol for GPS navigation equipment.
After the GPS receiver is powered on, it will automatically send NMEA0183 format data packets through the serial port or USB port. It is a set of strings containing various geographic location information. The string format is:
$information type,xxx,xxx,xxx,xxx,xxx,xxx,xxx,xxx,
Each line starts with a '$' character, followed by the information type, followed by the data, separated by commas. A complete line of data is as follows:
$GPRMC,063102.00,A,2932.293196,N,10636.147385,E,0.0,45.5,250818,2.3,W,A*10
The information types are:
GPVTG: Ground speed information
GPRMC: Recommended minimum positioning information
GPGSA: Current satellite information
GPGGA: GPS location information
GPGSV: Visible satellite information
Here we only parse the information of GPRMC and GPGGA.
2.1 Detailed explanation of GPRMC data
$GPRMC,<1>,<2>,<3>,<4>,<5>,<6>,<7>,<8>,<9>,<10>,<11>,<12>*hh
<1> UTC time, hhmmss (hours, minutes, seconds) format
<2> Positioning status, A = valid positioning, V = invalid positioning
<3> Latitude in ddmm.mmmm (degrees and minutes) format (the leading 0 will also be transmitted)
<4> Latitude hemisphere N (Northern Hemisphere) or S (Southern Hemisphere)
<5> Longitude in dddmm.mmmm (degrees and minutes) format (the leading 0 will also be transmitted)
<6> Longitude hemisphere E (East longitude) or W (West longitude)
<7> Ground speed (000.0~999.9 knots, the leading 0 will also be transmitted)
<8> Ground heading (000.0~359.9 degrees, with true north as reference, the leading 0 will also be transmitted)
<9> UTC date, ddmmyy (day month year) format
<10> Magnetic declination (000.0~180.0 degrees, the leading 0 will also be transmitted)
<11> Magnetic declination direction, E (east) or W (west)
<12> Mode indication (only NMEA0183 3.00 version output, A = autonomous positioning, D = differential, E = estimation, N = invalid data)
Analysis content:
1. Time, this is Greenwich Mean Time, which is the world time (UTC). We need to convert it to Beijing time (BTC). BTC is 8 hours behind UTC, so we need to add 8 hours to this time.
2. Positioning state: before receiving valid data, this bit is 'V', and the following data is empty. After receiving valid data, this bit is 'A', and data starts to appear.
3. Latitude, we need to convert it into degrees, minutes and seconds format, calculation method:
For example, the received latitude is: 4546.40891
4546.40891 / 100 = 45.4640891 can be directly read as 45 degrees
4546.40891–45 * 100 = 46.40891 can be directly read as 46 points
46.40891–46 = 0.40891 * 60 = 24.5346 Read out 24 seconds
So the latitude is: 45 degrees 46 minutes 24 seconds.
4. North and South Latitude, this bit has two values 'N' (North Latitude) and 'S' (South Latitude)
5. The calculation method of longitude is the same as that of latitude
6. East and West longitude, this bit has two values 'E' (East longitude) and 'W' (West longitude)
7. Speed. This speed value is in nautical miles per hour, and the unit is knots. To convert it into kilometers per hour, based on: 1 nautical mile = 1.85 kilometers, multiply the obtained speed by 1.85.
8. Heading, which refers to the angle from true north
9. Date. This date is accurate. For example, 200818 means August 20, 2018. This date is accurate and does not need to be converted.
2.2 Detailed explanation of GPGGA data
$GPGGA,<1>,<2>,<3>,<4>,<5>,<6>,<7>,<8>,<9>,M,<10>,M,<11>,<12>*xx
$GPGGA: start guide and statement format description (this sentence is GPS positioning data);
<1> UTC time, in the format of hhmmss.sss;
<2> Latitude, in the format of ddmm.mmmm (even if the first digit is zero, it will be transmitted);
<3> Latitude hemisphere, N or S (north or south)
<4> Longitude, in the format of dddmm.mmmm (the first zero will also be transmitted);
<5> Longitude hemisphere, E or W (East longitude or West longitude)
<6> Positioning quality indication, 0 = invalid positioning, 1 = valid positioning;
<7> Use satellite number, from 00 to 12 (the first zero will also be transmitted)
<8> Horizontal accuracy, 0.5 to 99.9
<9> The height of the antenna from the sea level, -9999.9 to 9999.9 meters (M) refers to the unit meter
<10> Geoid height, -9999.9 to 9999.9 meters M refers to the unit meter
<11> Differential GPS data period (RTCM SC-104), the number of seconds to set up the last RTCM transmission
<12> Differential reference base station number, from 0000 to 1023 (the first leading 0 will also be transmitted).
3 GPS Programming
There are two main parts of GPS analysis, one is the configuration of USB to serial port, the other is GPS analysis. Let's introduce them one by one.
1. Serial port configuration
Since I am transmitting data through the serial port, that is, the GPS positioning information is finally output to our terminal device through the serial port.
/**
* @brief Serial port setting function
* @param fd
baud_rate
data_bits
parity
stop_bits
* @retval int
*/
int set_GPS_com_config(int fd,int baud_rate,int data_bits, char parity, int stop_bits)
{
struct termios new_cfg;
int speed;
/* Save and test the existing serial port parameter settings. If the serial port number is wrong, there will be relevant error information*/
if (tcgetattr(fd, &new_cfg) != 0)
{
perror("tcgetattr save");
return -1;
}
//Modify the control mode to ensure that the program does not occupy the serial port
new_cfg.c_cflag |= CLOCAL;
//Modify the control mode to enable reading input data from the serial port
new_cfg.c_cflag |= CREAD;
new_cfg.c_oflag &= ~(ONLCR | OCRNL);
new_cfg.c_iflag &= ~(BRKINT|ICRNL|INPCK|ISTRIP|IXON);
new_cfg.c_iflag &= ~(ICRNL | INLCR);
new_cfg.c_iflag &= ~(IXON | IXOFF | IXANY);
/* Set baud rate */
switch (baud_rate)
{
case 2400:
{
speed = B2400;
}
break;
case 4800:
{
speed = B4800;
}
break;
case 9600:
{
speed = B9600;
}
break;
case 19200:
{
speed = B19200;
}
break;
case 38400:
{
speed = B38400;
}
break;
default:
case 115200:
{
speed = B115200;
}
break;
}
cfsetispeed(&new_cfg, speed); //Input baud rate
cfsetospeed(&new_cfg, speed); //Output baud rate
switch (data_bits) /* set data bits */
{
case 7:
{
new_cfg.c_cflag |= CS7;
}
break;
default:
case 8:
{
new_cfg.c_cflag |= CS8;
}
break;
}
switch (parity) /* Set the parity bit */
{
default:
case 'n':
case 'N':
{
new_cfg.c_cflag &= ~PARENB;
new_cfg.c_iflag &= ~INPCK;
}
break;
case 'o':
case 'O':
{
new_cfg.c_cflag |= (READY | PARENB);
new_cfg.c_iflag |= INPCK;
}
break;
case 'e':
case 'E':
{
new_cfg.c_cflag |= PARENB;
new_cfg.c_cflag &= ~READY;
new_cfg.c_iflag |= INPCK;
}
break;
case 's': /* as no parity */
case 'S':
{
new_cfg.c_cflag &= ~PARENB;
new_cfg.c_cflag &= ~CSTOPB;
}
break;
}
switch (stop_bits) /* Set stop bits */
{
default:
case 1:
{
new_cfg.c_cflag &= ~CSTOPB;
}
break;
case 2:
{
new_cfg.c_cflag |= CSTOPB;
}
}
//Modify the output mode, output the original data
new_cfg.c_oflag &= ~OPOST;
new_cfg.c_lflag &= ~(ICANON | ECHO | ECHOE | ISIG); // I added
new_cfg.c_lflag &= ~(SEE | FISH);
//Set the waiting time and minimum received characters
new_cfg.c_cc[VTIME] = 0; /* Read a character and wait 0*(0/10)s */
new_cfg.c_cc[VMIN] = 1; /* The minimum number of characters to read is 0 */
//If data overflow occurs, receive data, but do not read it again. Refresh the received data but do not read it.
tcflush(fd, TCIFLUSH); /* Process unreceived characters */
if ((tcsetattr(fd, TCSANOW, &new_cfg)) != 0) /* Activate new configuration */
{
perror("tcsetattr action");
return -1;
}
//printf("serial set success\n");
return 0;
}
/**
* @brief Open serial port function
* @param com
* @retval int
*/
int open_GPS_port(const char *com_port)
{
int fd;
/*com1, com2, com3 correspond to ttyS0 ttyS1 ttyS2 respectively */
fd = open( com_port, O_RDWR|O_NOCTTY|O_NDELAY);
if (fd < 0)
{
perror("Can't Open Serial Port");
return -1;
}
/*Restore the serial port to blocking state*/
if (fcntl(fd,F_SETFL,0)<0)
{
perror("fcntl F_SETFL\n");
}
/*Test whether it is a terminal device*/
if(isatty(STDIN_FILENO) == 0)
{
perror("standard input is not a terminal device");
}
return fd;
}
/**
* @brief Serial port initialization function
* @param com_port
* @retval int
*/
int init_GPS_port(const char *com_port)
{
int fd;
if ((fd = open_GPS_port(com_port)) < 0 )
{
perror("open_port");
return -1;
}
if(set_GPS_com_config(fd,9600,8,'N',1) < 0)
{
perror("set_com_config");
return -1;
}
return fd;
}
2. GPS data analysis
char *buff = NULL;
GPRMC_t gprmc;
GPGGA_t gpgga;
/**
* @brief GPS parsing function
* @param AT_fd: serial port file descriptor
* @DATA_fd: serial port file descriptor
* @retval Nono
*/
void GPS_Analysis(int AT_fd,int DATA_fd)
{
int nread, nwrite;
char send_buff[16];
char recv_buff[512];
char *ptr = NULL;
char AT_Buff[16];
int ret = 0;
#if 0
uint32_t time =0;
uint32_t lat =0 ;
uint32_t lon =0;
uint32_t speed =0;
uint32_t head =0;
uint32_t alt =0;
#endif
while(1)
{
memset(AT_Buff,0,sizeof(AT_Buff));
strcpy(AT_Buff, "AT+QGPS=1");
GPS_Set(AT_fd , AT_Buff);
if(buff != NULL)
{
break;
}
memset(AT_Buff,0,sizeof(AT_Buff));
strcpy(AT_Buff, "AT+QGPSEND");
GPS_Set(AT_fd , AT_Buff);
sleep(1);
}
//Receive data
while(1)
{
memset(&gprmc, 0, sizeof(gprmc));
memset(&gpgga, 0, sizeof(gpgga));
memset(recv_buff,0,sizeof(recv_buff));
nread = read(DATA_fd,recv_buff,sizeof(recv_buff));
#ifdef DEBUG_GPS
printf("nread=%d,%s\n",nread,recv_buff);
printf("=================2===================\n");
#endif
//strcpy(buff, recv_buff);
//save data
ptr = strstr(recv_buff, "$GPRMC");
ret = sscanf(ptr, "$GPRMC,%f,%c,%f,%*c,%f,%*c,%f,%f,%d,%f,%*c,%*c*",
&gprmc.time,&gprmc.state, &gprmc.lat, &gprmc.lon,
&gprmc.speed, &gprmc.head, &gprmc.date,&gprmc.dec);
ptr = strstr(recv_buff, "$GPGGA");
ret = sscanf(ptr, "$GPGGA,%f,%f,N,%f,E,%d,%d,%f,%f,M,%f,M,,*",
&gpgga.time, &gpgga.lat, &gpgga.lon,
&gpgga.state, &gpgga.num, &gpgga.hdop, &gpgga.alt, &gpgga.geoid);
#if 0
time = (int)(gprmc.time*100) % 100 + ((int)gprmc.time % 100) * 100 +\
((int)gprmc.time%10000/100) *60 * 100 + ((int)gprmc.time/10000)* 3600 * 100;
lat = ((int)(gprmc.lat/100) + (gprmc.lat-(int)gprmc.lat/100*100)/60)*10000000;
lon = ((int)(gprmc.lon/100) + (gprmc.lon-(int)gprmc.lon/100*100)/60)*10000000;
speed = gprmc.speed* 1.852;
head = gprmc.head;
all = gpgga.all;
#endif
//Print data
print_GPS_RMC(&gprmc);
print_GPS_GGA(&gpgga);
}
}
/**
* @brief GPS open function
* @param AT_fd: serial port file descriptor
* @AT: AT command
* @retval Nono
*/
void GPS_Set(int AT_fd,char *AT)
{
int nread, nwrite;
char send_buff[16];
char recv_buff[64];
memset(send_buff,0,sizeof(send_buff));
strcpy(send_buff, AT);
strcat(send_buff,"\r");
nwrite = write(AT_fd,send_buff,strlen(send_buff));
#ifdef DEBUG_GPS
printf("nwrite=%d,%s\n",nwrite,send_buff);
#endif
//waiting for AT's ACK
memset(recv_buff,0,sizeof(recv_buff));
nread = read(AT_fd,recv_buff,sizeof(recv_buff));
buff = strstr(recv_buff,"OK");
//memset(buff,0,strlen(buff));
#ifdef DEBUG_GPS
printf("nread=%d,%s\n",nread,recv_buff);
printf("================1======================\n");
#endif
}
/**
* @brief GPS-RMC information printing
* @param gprmc_data:
* @retval Nono
*/
void print_GPS_RMC(GPRMC_t *gprmc_data)
{
printf(" \n");
printf("===========================================================\n");
printf("== Global GPS positioning and navigation module==\n");
printf("================RMC information======================================\n");
printf("===========================================================\n");
printf("== GPS state bit : %c [A: valid state V: invalid state] \n",gprmc_data->state);
printf("== Date : 20%02d-%02d-%02d \n",gprmc_data->date%100,(gprmc_data->date%10000)/100,gprmc_data->date/10000);
printf("== Latitude: North latitude: %d degrees %d minutes %d seconds\n",((int)gprmc_data->lat) / 100, (int)(gprmc_data->lat - ((int)gprmc_data->lat / 100 * 100)), (int)(((gprmc_data->lat - ((int)gprmc_data->lat / 100 * 100)) - ((int)gprmc_data->lat - ((int)gprmc_data->lat / 100 * 100))) * 60.0));
printf("== Longitude: East longitude: %d degrees %d minutes %d seconds\n",((int)gprmc_data->lon) / 100, (int)(gprmc_data->lon - ((int)gprmc_data->lon / 100 * 100)), (int)(((gprmc_data->lon - ((int)gprmc_data->lon / 100 * 100)) - ((int)gprmc_data->lon - ((int)gprmc_data->lon / 100 * 100))) * 60.0));
printf("== 速度 : %.3f m/s \n",gprmc_data->speed * 1.852);
printf("== Heading: %.3f degrees\n",gprmc_data->head );
printf("===========================================================\n");
}
/**
* @brief GPS-GGA information printing
* @param gpgga_data:
* @retval Nono
*/
void print_GPS_GGA(GPGGA_t *gpgga_data)
{
printf(" \n");
printf("===========================================================\n");
printf("== Global GPS positioning and navigation module==\n");
printf("================GGA information=======================================\n");
printf("===========================================================\n");
printf("== Latitude: North latitude: %d degrees %d minutes %d seconds\n",((int)gpgga_data->lat) / 100, (int)(gpgga_data->lat - ((int)gpgga_data->lat / 100 * 100)), (int)(((gpgga_data->lat - ((int)gpgga_data->lat / 100 * 100)) - ((int)gpgga_data->lat - ((int)gpgga_data->lat / 100 * 100))) * 60.0));
printf("== Longitude: East longitude: %d degrees %d minutes %d seconds\n",((int)gpgga_data->lon) / 100, (int)(gpgga_data->lon - ((int)gpgga_data->lon / 100 * 100)), (int)(((gpgga_data->lon - ((int)gpgga_data->lon / 100 * 100)) - ((int)gpgga_data->lon - ((int)gpgga_data->lon / 100 * 100))) * 60.0));
printf("== Quantity: %d pieces\n",gpgga_data->num);
printf("== 精度 : %.3f \n",gpgga_data->hdop);
printf("== Altitude: %.3f m \n",gpgga_data->alt);
printf("===========================================================\n");
}
【Notice】
- Since I directly obtain Greenwich Mean Time, which is World Time (UTC), I need to convert it into Beijing Time (BTC), which is to add 8 hours to this time.
- Latitude and longitude, the latitude data returned by GPRMC is in ddmm.mmmm format, that is, degree and minute format. We convert it into the common degree, minute and second format. The calculation method is: if the received latitude is: 3029.60430
3029.60430/100=30.2960430 can be directly read as 30 degrees, 3029.60430–30*100=29.60430, can be directly read as 29 minutes (29.60430–29)*60 =0.60430*60=36.258 read as 36 seconds, so the latitude is: 30 degrees 29 minutes 36 seconds.
- The speed value returned by GPRMC is in nautical miles per hour, and the unit is knots. To convert it to kilometers per hour, the conversion is: 1 nautical mile = 1.85 kilometers, and multiply the obtained speed by 1.85.
- Heading is the angle from true north.
- The date format of GPRMC is: ddmmyy, such as: 200818 means August 20, 2018. This date is accurate and does not need to be converted.
3. The main function opens the serial port device for reading operation
#define DEBUG_GPS
int GPS_DATA_fd = -1; //GPS_DATA descriptor
int GPS_AT_fd = -1; //GPS_AT descriptor;
/**
* @brief main function
* @param Nono
* @retval Nono
*/
int main(int argc, char **argv)
{
GPS_AT_fd = init_GPS_port(DEVICE_AT_GPS); //Initialize AT GPS
if(GPS_AT_fd < 0)
{
printf("open GPS_AT_port failed!\n");
}
else
{
printf("open GPS_AT_port success!\n");
}
GPS_DATA_fd = init_GPS_port(DEVICE_DATA_GPS); //Initialize DATA GPS
if(GPS_AT_fd < 0)
{
printf("open GPS_DATA_port failed!\n");
}
else
{
printf("init GPS_port success!\n");
GPS_Analysis(GPS_AT_fd,GPS_DATA_fd);
}
return 0;
}
[Note] EC20's USB to serial port information has been given before this article. Two devices are needed, ttyUSB1 and ttyUSB2. ttyUSB1 is GPS output information, and ttyUSB2 is AT command configuration device, so both devices need to be opened.
4. Testing
After the compilation is complete, execute the program and you will see the following main information, where you can see the GPS parsing data.