0

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; } 
1
  • 1
    You can convert between formats, as explained here Commented Nov 12, 2015 at 20:36

1 Answer 1

2

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.

4
  • There is a way to increase accuracy? Commented May 26, 2016 at 18:56
  • 1
    @Lipsyor: Sure. Do not drop the last digit, uint32_t fraction = atol(s+3) * 10 / 6;, and now fraction is in units of 1e-6 deg. Commented 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 used uint32_t fraction = atol(s+3) * 10 / 6; instead of uint16_t fraction = atoi(s + 3); fraction /= 6; but I have a great translation on the map. May you explain me? Commented 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. Commented May 27, 2016 at 16:02

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.