I am using the SIM908 module to get GPS position.
The SIM908 sends the latitude and longitude in the ddmm.mmmm
format, but major online services for GPS location (for example, Google Earth) use the dd.dddd
format.
So, how can I modify my code to convert coordinates from the SIM908 to dd.dddd
format?
This is the SIM908 GSM Library Code that I am using:
#include "gps.h"
char GPSGSM::getBattInf(char *str_perc, char *str_vol)
{
char ret_val=0;
char *p_char;
char *p_char1;
gsm.SimpleWriteln("AT+CBC");
gsm.WaitResp(5000, 100, "OK");
if(gsm.IsStringReceived("+CBC"))
ret_val=1;
//BCL
p_char = strchr((char *)(gsm.comm_buf),',');
p_char1 = p_char+1; //we are on the first char of BCS
p_char = strchr((char *)(p_char1), ',');
if (p_char != NULL) {
*p_char = 0;
}
strcpy(str_perc, (char *)(p_char1));
//Voltage
p_char++;
p_char1 = strchr((char *)(p_char), '\r');
if (p_char1 != NULL) {
*p_char1 = 0;
}
strcpy(str_vol, (char *)(p_char));
return ret_val;
}
char GPSGSM::getBattTVol(char *str_vol)
{
char *p_char;
char *p_char1;
char ret_val=0;
gsm.SimpleWriteln("AT+CBTE?");
gsm.WaitResp(5000, 100, "OK");
if(gsm.IsStringReceived("+CBTE"))
ret_val=1;
//BCL
p_char = strchr((char *)(gsm.comm_buf),':');
p_char1 = p_char+2; //we are on the first char of BCS
p_char = strchr((char *)(p_char1), '\r');
if (p_char != NULL) {
*p_char = 0;
}
strcpy(str_vol, (char *)(p_char1));
return ret_val;
}
char GPSGSM::attachGPS()
{
if(AT_RESP_ERR_DIF_RESP == gsm.SendATCmdWaitResp("AT+CGPSPWR=1", 500, 100, "OK", 5))
return 0;
if(AT_RESP_ERR_DIF_RESP == gsm.SendATCmdWaitResp("AT+CGPSRST=1", 500, 100, "OK", 5))
return 0;
return 1;
}
char GPSGSM::deattachGPS()
{
if(AT_RESP_ERR_DIF_RESP == gsm.SendATCmdWaitResp("AT+CGPSPWR=0", 500, 100, "OK", 5))
return 0;
return 1;
}
char GPSGSM::getStat()
{
char ret_val=-1;
gsm.SimpleWriteln("AT+CGPSSTATUS?");
gsm.WaitResp(5000, 100, "OK");
if(gsm.IsStringReceived("Unknown")||gsm.IsStringReceived("unknown"))
ret_val=0;
else if(gsm.IsStringReceived("Not"))
ret_val=1;
else if(gsm.IsStringReceived("2D")||gsm.IsStringReceived("2d"))
ret_val=2;
else if(gsm.IsStringReceived("3D")||gsm.IsStringReceived("3d"))
ret_val=3;
return ret_val;
}
char GPSGSM::getPar(char *str_long, char *str_lat, char *str_alt, char *str_time, char *str_speed )
{
char ret_val=0;
char *p_char;
char *p_char1;
gsm.SimpleWriteln("AT+CGPSINF=0");
gsm.WaitResp(5000, 100, "OK");
if(gsm.IsStringReceived("OK"))
ret_val=1;
//longitude
p_char = strchr((char *)(gsm.comm_buf),',');
p_char1 = p_char+1; //we are on the first char of longitude
p_char = strchr((char *)(p_char1), ',');
if (p_char != NULL) {
*p_char = 0;
}
strcpy(str_long, (char *)(p_char1));
// latitude
p_char++;
p_char1 = strchr((char *)(p_char), ',');
if (p_char1 != NULL) {
*p_char1 = 0;
}
strcpy(str_lat, (char *)(p_char));
// altitude
p_char1++;
p_char = strchr((char *)(p_char1), ',');
if (p_char != NULL) {
*p_char = 0;
}
strcpy(str_alt, (char *)(p_char1));
// UTC time
p_char++;
p_char1 = strchr((char *)(p_char), ',');
if (p_char1 != NULL) {
*p_char1 = 0;
}
strcpy(str_time, (char *)(p_char));
// TTFF
p_char1++;
p_char = strchr((char *)(p_char1), ',');
if (p_char != NULL) {
*p_char = 0;
}
// num
p_char++;
p_char1 = strchr((char *)(p_char), ',');
if (p_char1 != NULL) {
*p_char1 = 0;
}
// speed
p_char1++;
p_char = strchr((char *)(p_char1), ',');
if (p_char != NULL) {
*p_char = 0;
}
strcpy(str_speed, (char *)(p_char1));
return ret_val;
}
void parseTime(char *field, int *time)
{
////////////////Time////////////
char tmp[4];
tmp[2]=0; // Init tmp and null terminate
tmp[0] = field[8];
tmp[1] = field[9];
time[0] = atoi(tmp); // Hours
tmp[0] = field[10];
tmp[1] = field[11];
time[1] = atoi(tmp); // Minutes
tmp[0] = field[12];
tmp[1] = field[13];
time[2] = atoi(tmp); // Seconds
/////////////Date///////////////
tmp[0] = field[0];
tmp[1] = field[1];
tmp[2] = field[2];
tmp[3] = field[3];
tmp[4]=0; // Init tmp and null terminate
time[3] = atoi(tmp); // year
tmp[0] = field[4];
tmp[1] = field[5];
tmp[2]=0; // Init tmp and null terminate
time[4] = atoi(tmp); // month
tmp[0] = field[6];
tmp[1] = field[7];
tmp[2]=0; // Init tmp and null terminate
time[5] = atoi(tmp); // day
}
// Read the latitude in decimal format from a GGA string
double convertLat(char* latString)
{
double latitude = atof(latString); // convert to a double (precise)
int deg = (int) latitude / 100; // extract the number of degrees
double min = latitude - (100 * deg); // work out the number of minutes
latitude = deg + (double) min/60.0; // convert to decimal format
return latitude;
}
// Read the longitude in decimal format from a GGA string
double convertLong(char* longString)
{
double longitude = atof(longString); // convert to a double
int deg = (int) longitude / 100; // extract the number of degrees
double min = longitude - (100 * deg); // work out the number of minutes
longitude = deg + (double) min/60.00; // convert to decimal format
return longitude;
}
-
1You can convert between formats, as explained hereJason– Jason2015年11月12日 20:36:25 +00:00Commented Nov 12, 2015 at 20:36
1 Answer 1
This could be a general programming question. The only thing specific to Arduino here is that you may want a solution that minimizes the use of memory and avoids floating point operations.
Here is a memory-friendly solution that reformats the string in place, i.e. it overwrites the original string. In many cases this is appropriate, as it is unlikely that you need the original string later, but you have to make sure it fits your application. It works by first moving a few characters around in order to have the format "dd.mmmmm", then parsing the minutes and converting to fractional degrees, then writing that in ASCII.
It should be noted that the only reason for dropping the last digit in the minutes field is to avoid 32-bit operations. I compiled for an Uno and checked the generated assembly: it uses only four bytes of RAM, in the stack.
// Convert the format of a latitude from "ddmm.mmmm" to "dd.dddd".
// The string is converted in place.
void convertLat(char* s)
{
// Convert ddmm.mmmm to dd.mmmmm
s[4] = s[3]; // move minutes
s[3] = s[2]; // move tens of minutes
s[2] = '.'; // put decimal point
s[8] = '0円'; // drop last digit
// Compute fractional degrees.
uint16_t fraction = atoi(s + 3); // unit = 1e-3 arcmin
fraction /= 6; // unit = 1e-4 deg
// Write in ASCII, right to left.
s[7] = '0円';
for (int i = 6; i > 2; i--) {
s[i] = fraction % 10 + '0';
fraction /= 10;
}
}
You can use this same function to convert a longitude from "dddmm.mmmm" to "ddd.dddd" by just skipping the first character:
static inline void convertLon(char* s)
{
convertLat(s + 1);
}
Addendum: To answer Lipsyor comment, it is possible to have the latitude in units of 1e-6 deg by using 32-bit arithmetic:
// Convert the format of a latitude from "ddmm.mmmm" to "dd.dddddd".
// The string is converted in place.
void convertLat(char* s)
{
// Convert ddmm.mmmm to dd.mmmmmm
s[4] = s[3]; // move minutes
s[3] = s[2]; // move tens of minutes
s[2] = '.'; // put decimal point
// Compute fractional degrees.
uint32_t fraction = atol(s + 3); // unit = 1e-4 arcmin
fraction = (fraction * 10 + 3) / 6; // unit = 1e-6 deg
// Write in ASCII, right to left.
for (int i = 8; i > 2; i--) {
s[i] = fraction % 10 + '0';
fraction /= 10;
}
}
The + 3
in the computation is only to round to nearest. Dropping this
term should not make any significant difference.
-
There is a way to increase accuracy?Lipsyor– Lipsyor2016年05月26日 18:56:17 +00:00Commented May 26, 2016 at 18:56
-
1@Lipsyor: Sure. Do not drop the last digit,
uint32_t fraction = atol(s+3) * 10 / 6;
, and nowfraction
is in units of 1e-6 deg.Edgar Bonet– Edgar Bonet2016年05月26日 19:34:03 +00:00Commented May 26, 2016 at 19:34 -
Thank you Edgar. I'm sorry but don't understand the operation in the code. I removed the line that drop the last digit (
s[8] = '0円';
) and useduint32_t fraction = atol(s+3) * 10 / 6;
instead ofuint16_t fraction = atoi(s + 3); fraction /= 6;
but I have a great translation on the map. May you explain me?Lipsyor– Lipsyor2016年05月27日 13:12:18 +00:00Commented May 27, 2016 at 13:12 -
1@Lipsyor: You probably failed to properly write the number as ASCII. Since it's now in units of 1e-6 deg, you have to write six digits instead of four. C.f. the amended answer.Edgar Bonet– Edgar Bonet2016年05月27日 16:02:38 +00:00Commented May 27, 2016 at 16:02