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;
}
Greenonline
3,1527 gold badges36 silver badges48 bronze badges
asked Nov 11, 2015 at 21:46
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.

answered Jan 25, 2016 at 10:53
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

Your Answer

Draft saved
Draft discarded

Sign up or log in

Sign up using Google
Sign up using Email and Password

Post as a guest

Required, but never shown

Post as a guest

Required, but never shown

By clicking "Post Your Answer", you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.