我知道这个问题被问了很多次,但我真的找不到我真正在搜索的内容。
我正在使用一个Arduino Uno和一个通过串行显示GPS数据的GPS屏蔽。
这是我上传到Arduino的代码,用于连接GPS屏蔽:
void loop() // run over and over
{
while(!(mySerial.available())){}
Serial.write(mySerial.read());
}
这只是代码。尽管如此,当它在串行监视器上连续循环时,它也会每秒输出GPS数据。
这是它每秒的输出:
$GPGGA,013856.000,000.9090,N,9090.90,E,1,09,1.1,316.97,M,0.00,M,,*66 $GPGSA,A,3,07,08,11,1ÿ3,16,19,23,27,42,,,,2.8,1.1,2.5*3F $GPRMC,013856.000,A,000.9090,N,9090.90,E,0.0,038.1,310814,,,A*62 $GPGSV,ÿ3,1,12,16,26,059,33,27,33,025,44,08,30,330,32,07,31,326,34*7A $GPGSV,3,2,12,19,58,354,31,01,33,186,18,23,32,221,24,11,5ÿ9,198,31*70 $GPGSV,3,3,12,42,60,129,32,13,38,253,27,32,06,161,,31,01,140,*7E
当它每秒更新一次时,坐标会变为最小值,这意味着GPS盾牌正在工作。
这里的问题是,我想解析GPS数据,尤其是仅在GPGGA线路上,而忽略其他线路。我想解析状态、纬度、N/S指示器、经度和E/W指示器。
我已搜索NMEA图书馆(http://nmea.sourceforge.net/),但我不知道如何使用它。
有人能帮我吗?非常感谢。
NMEA数据是GPS风格(ddmm.sss)格式,谷歌希望它是十进制风格(dd.mmsss),这一步骤的代码底部有一个覆盖函数。
我写这篇文章是因为我不喜欢大型复杂的库做简单的小事,尤其是当我试图弄清楚它是如何工作的时候。这将解析GLL句子,但如果需要,您可以更改它要查找的句子并重新排列部分。
String ReadString;
void setup() {
Serial.begin(9600); //Arduino serial monitor thru USB cable
Serial1.begin(9600); // Serial1 port connected to GPS
}
void loop() {
ReadString=Serial1.readStringUntil(13); //NMEA data ends with 'return' character, which is ascii(13)
ReadString.trim(); // they say NMEA data starts with "$", but the Arduino doesn't think so.
// Serial.println(ReadString); //All the raw sentences will be sent to monitor, if you want them, maybe to see the labels and data order.
//Start Parsing by finding data, put it in a string of character array, then removing it, leaving the rest of thes sentence for the next 'find'
if (ReadString.startsWith("$GPGLL")) { //I picked this sentence, you can pick any of the other labels and rearrange/add sections as needed.
Serial.println(ReadString); // display raw GLL data in Serial Monitor
// mine looks like this: "$GPGLL,4053.16598,N,10458.93997,E,224431.00,A,D*7D"
//This section gets repeated for each delimeted bit of data by looking for the commas
//Find Lattitude is first in GLL sentence, other senetences have data in different order
int Pos=ReadString.indexOf(','); //look for comma delimetrer
ReadString.remove(0, Pos+1); // Remove Pos+1 characters starting at index=0, this one strips off "$GPGLL" in my sentence
Pos=ReadString.indexOf(','); //looks for next comma delimetrer, which is now the first comma because I removed the first segment
char Lat[Pos]; //declare character array Lat with a size of the dbit of data
for (int i=0; i <= Pos-1; i++){ // load charcters into array
Lat[i]=ReadString.charAt(i);
}
Serial.print(Lat); // display raw latitude data in Serial Monitor, I'll use Lat again in a few lines for converting
//repeating with a different char array variable
//Get Lattitude North or South
ReadString.remove(0, Pos+1);
Pos=ReadString.indexOf(',');
char LatSide[Pos]; //declare different variable name
for (int i=0; i <= Pos-1; i++){
LatSide[i]=ReadString.charAt(i); //fill the array
Serial.println(LatSide[i]); //display N or S
}
//convert the variable array Lat to degrees Google can use
float LatAsFloat = atof (Lat); //atof converts the char array to a float type
float LatInDeg;
if(LatSide[0]==char(78)) { //char(69) is decimal for the letter "N" in ascii chart
LatInDeg= ConvertData(LatAsFloat); //call the conversion funcion (see below)
}
if(LatSide[0]==char(83)) { //char(69) is decimal for the letter "S" in ascii chart
LatInDeg= -( ConvertData(LatAsFloat)); //call the conversion funcion (see below)
}
Serial.println(LatInDeg,15); //display value Google can use in Serial Monitor, set decimal point value high
//repeating with a different char array variable
//Get Longitude
ReadString.remove(0, Pos+1);
Pos=ReadString.indexOf(',');
char Longit[Pos]; //declare different variable name
for (int i=0; i <= Pos-1; i++){
Longit[i]=ReadString.charAt(i); //fill the array
}
Serial.print(Longit); //display raw longitude data in Serial Monitor
//repeating with a different char array variable
//Get Longitude East or West
ReadString.remove(0, Pos+1);
Pos=ReadString.indexOf(',');
char LongitSide[Pos]; //declare different variable name
for (int i=0; i <= Pos-1; i++){
LongitSide[i]=ReadString.charAt(i); //fill the array
Serial.println(LongitSide[i]); //display raw longitude data in Serial Monitor
}
//convert to degrees Google can use
float LongitAsFloat = atof (Longit); //atof converts the char array to a float type
float LongInDeg;
if(LongitSide[0]==char(69)) { //char(69) is decimal for the letter "E" in ascii chart
LongInDeg=ConvertData(LongitAsFloat); //call the conversion funcion (see below
}
if(LongitSide[0]==char(87)) { //char(87) is decimal for the letter "W" in ascii chart
LongInDeg=-(ConvertData(LongitAsFloat)); //call the conversion funcion (see below
}
Serial.println(LongInDeg,15); //display value Google can use in Serial Monitor, set decimal point value high
//repeating with a different char array variable
//Get TimeStamp - GMT
ReadString.remove(0, Pos+1);
Pos=ReadString.indexOf(',');
char TimeStamp[Pos]; //declare different variable name
for (int i=0; i <= Pos-1; i++){
TimeStamp[i]=ReadString.charAt(i); //fill the array
}
Serial.print(TimeStamp); //display raw longitude data in Serial Monitor, GMT
Serial.println("");
}
}
//Conversion function
float ConvertData(float RawDegrees)
{
float RawAsFloat = RawDegrees;
int firstdigits = ((int)RawAsFloat)/100; // Get the first digits by turning f into an integer, then doing an integer divide by 100;
float nexttwodigits = RawAsFloat - (float)(firstdigits*100);
float Converted = (float)(firstdigits + nexttwodigits/60.0);
return Converted;
}
我写了这段不错的代码,它最多可以工作到小数点后两位。
代码:
String gpsData;
String LATval = "######";
String LNGval = "######";
char inChar;
String gpsData;
String latt;
String la;
String lonn;
String lo;
float lattt;
float lonnn;
int latDeg;
int lonDeg;
float latMin;
float lonMin;
float latttt;
float lonnnn;
String sGPRMC;
void setup() {
Serial.begin(9600);
}
void loop() {
while (Serial.available()) {
inChar = Serial.read();
gpsData += inChar;
if (inChar == '$') {
gpsData = Serial.readStringUntil('n');
break;
}
}
Serial.println(gpsData);
sGPRMC = gpsData.substring(0, 5);
if (sGPRMC == "GPRMC") {
Serial.flush();
latt = gpsData.substring(18, 28);
la = gpsData.substring(29, 30);
lonn = gpsData.substring(31, 42);
lo = gpsData.substring(43, 44);
Serial.print("latt:");
Serial.println(latt);
Serial.print("la:");
Serial.println(la);
Serial.print("lonn:");
Serial.println(lonn);
Serial.print("lo:");
Serial.println(lo);
lattt = latt.toFloat();
lonnn = lonn.toFloat();
Serial.print("lattt:");
Serial.println(lattt);
Serial.print("lonnn:");
Serial.println(lonnn);
if (la == "N" and lo == "E") {
latDeg = float(int(lattt / 100));
latMin = float(lattt - (latDeg * 100));
latMin = latMin / 60;
lonDeg = float(int(lonnn / 100));
lonMin = float(lonnn - (lonDeg * 100));
lonMin = lonMin / 60;
latttt = latDeg + latMin;
lonnnn = lonDeg + lonMin;
LATval = String(latttt);
LNGval = String(lonnnn);
Serial.print("latDeg:");
Serial.println(latDeg);
Serial.print("latMin:");
Serial.println(latMin);
Serial.print("lonDeg:");
Serial.println(lonDeg);
Serial.print("lonMin:");
Serial.println(lonMin);
Serial.print("LATval:");
Serial.println(LATval);
Serial.print("LNGval:");
Serial.println(LNGval);
}
}
}
我在互联网上搜索过,最好的答案是使用Arduino的"TinyGPS++"库。几乎所有与GPS相关的代码都已包含在图书馆中。
您可以使用TinyGPS来解析NMEA字符串。如果你只对一句话感兴趣。您可以为该句子编写如下自定义解析器。
int handle_byte(int byteGPS) {
buf[counter1] = byteGPS;
//Serial.print((char)byteGPS);
counter1++;
if (counter1 == 300) {
return 0;
}
if (byteGPS == ',') {
counter2++;
offsets[counter2] = counter1;
if (counter2 == 13) {
return 0;
} } if (byteGPS == '*') {
offsets[12] = counter1; }
// Check if we got a <LF>, which indicates the end of line if (byteGPS == 10) {
// Check that we got 12 pieces, and that the first piece is 6 characters
if (counter2 != 12 || (get_size(0) != 6)) {
return 0;
}
// Check that we received $GPRMC
// CMD buffer contains $GPRMC
for (int j=0; j<6; j++) {
if (buf[j] != cmd[j]) {
return 0;
}
}
// Check that time is well formed
if (get_size(1) != 10) {
return 0;
}
// Check that date is well formed
if (get_size(9) != 6) {
return 0;
}
SeeedOled.setTextXY(7,0);
for (int j=0; j<6; j++) {
SeeedOled.putChar(*(buf+offsets[1]+j));
}
SeeedOled.setTextXY(7,7);
for (int j=0; j<6; j++) {
SeeedOled.putChar(*(buf+offsets[9]+j));
}
// TODO: compute and validate checksum
// TODO: handle timezone offset
return 0; }
return 1; }
试试这个可以帮助你
#include <SoftwareSerial.h>
#include <TinyGPS.h>
TinyGPS gps;
SoftwareSerial ss(3,4);
static void smartdelay(unsigned long ms);
static void print_float(float val, float invalid, int len, int prec);
static void print_int(unsigned long val, unsigned long invalid, int len);
static void print_date(TinyGPS &gps);
static void print_str(const char *str, int len);
void setup()
{
Serial.begin(9600);
ss.begin(9600);
}
void loop()
{
float flat, flon;
unsigned short sentences = 0, failed = 0;
gps.f_get_position(&flat, &flon);
Serial.print("LATITUDE: ");
print_float(flat, TinyGPS::GPS_INVALID_F_ANGLE, 10, 6);
Serial.println(" ");
Serial.print("LONGITUDE: ");
print_float(flon, TinyGPS::GPS_INVALID_F_ANGLE, 11, 6);
Serial.println(" ");
Serial.print("altitude: ");
print_float(gps.f_altitude(), TinyGPS::GPS_INVALID_F_ALTITUDE, 7, 2);
Serial.println(" ");
Serial.print("COURSE:");
print_float(gps.f_course(), TinyGPS::GPS_INVALID_F_ANGLE, 7, 2);
Serial.println("");
Serial.print("DIRECTION: ");
int d;
print_str(gps.f_course() == TinyGPS::GPS_INVALID_F_ANGLE ? "*** " : TinyGPS::cardinal(gps.f_course()), 6);
d=gps.f_course();
Serial.println();
Serial.println();
smartdelay(1000);
}
static void smartdelay(unsigned long ms)
{
unsigned long start = millis();
do
{
while (ss.available())
gps.encode(ss.read());
} while (millis() - start < ms);
}
static void print_float(float val, float invalid, int len, int prec)
{
if (val == invalid)
{
while (len-- > 1)
Serial.print('*');
Serial.print(' ');
}
else
{
Serial.print(val, prec);
int vi = abs((int)val);
int flen = prec + (val < 0.0 ? 2 : 1); // . and -
flen += vi >= 1000 ? 4 : vi >= 100 ? 3 : vi >= 10 ? 2 : 1;
for (int i=flen; i<len; ++i)
Serial.print(' ');
}
smartdelay(0);
}
static void print_int(unsigned long val, unsigned long invalid, int len)
{
char sz[32];
if (val == invalid)
strcpy(sz, "*******");
else
sprintf(sz, "%ld", val);
sz[len] = 0;
for (int i=strlen(sz); i<len; ++i)
sz[i] = ' ';
if (len > 0)
sz[len-1] = ' ';
Serial.print(sz);
smartdelay(0);
}
static void print_str(const char *str, int len)
{
int slen = strlen(str);
for (int i=0; i<len; ++i)
Serial.print(i<slen ? str[i] : ' ');
smartdelay(0);
}
我已经更改了上面的代码,它运行得很好。它只返回GPGGA数据开关,可以在下面的站点进行解码:https://rl.se/gprmc完整的代码是:
-
#包括<软件序列.h>#包括<string.h>char inChar;字符串gpsData;字符串sGPRMC;
//创建一个软件串行端口;gpsSerial";软件序列gpsSerial(8,9);
void setup(){//在9600启动Arduino硬件串行端口波特串行开始(9600);
//以GPS的默认波特启动软件串行端口
gpsSerial-begin(9600);}void loop(){
while(gpsSerial.available()){inChar=gpsSerial.read();if(inChar=='$'){gpsData=gpsSerial.readStringUntil('\n');打破}}
//Serial.println(gpsData);sGPRMC=gpsData.substring(0,5)
如果(sGPRMC=="GPGGA"){Serial.flush();Serial.print("$");Serial.print(gpsData);}}