使用Arduino和GPS / GPRS模块的GPS数据不准确

时间:2015-10-06 12:57:41

标签: mysql gps arduino

对于项目,我正在关注如何跟踪位置并使用带有this SIM908盾牌的 Arduino 来输出GPS数据的this教程。 Arduino 正确地将GPS数据发送到数据库。但是,坐标都完全相同,似乎已经四舍五入。

例如:

Latitude: 52.216667
Longitude: 5.483333

这不是因为PHP脚本,它所做的只是将它接收的数据放在数据库中。我的猜测是它与转换函数convert2Degrees有关。

这是我们在 Arduino 上运行的代码:

int8_t answer;
int onModulePin= 2;

char data[100];
int data_size;

char aux_str[100];
char aux;
int x = 0;
char N_S,W_E;

char url[] = "informatica-corlaer.nl";
char frame[200];

char pin[]="0000";
char apn[]="mmm.nl";
char user_name[]="";
char password[]="";

char latitude[15];
char longitude[15];
char altitude[10];
char date[16];
char time[7];
char satellites[3];
char speedOTG[10];
char course[10];


void setup(){

    pinMode(onModulePin, OUTPUT);
    Serial.begin(115200);   

    Serial.println("Starting...");
    power_on();

    delay(3000);

    //sets the PIN code
    snprintf(aux_str, sizeof(aux_str), "AT+CPIN=%s", pin);
    sendATcommand(aux_str, "OK", 2000);

    delay(3000);

    // starts the GPS and waits for signal
    while ( start_GPS() == 0);

    while (sendATcommand("AT+CREG?", "+CREG: 0,1", 2000) == 0);

    // sets APN , user name and password
    sendATcommand("AT+SAPBR=3,1,\"Contype\",\"GPRS\"", "OK", 2000);
    snprintf(aux_str, sizeof(aux_str), "AT+SAPBR=3,1,\"APN\",\"%s\"", apn);
    sendATcommand(aux_str, "OK", 2000);

    snprintf(aux_str, sizeof(aux_str), "AT+SAPBR=3,1,\"USER\",\"%s\"", user_name);
    sendATcommand(aux_str, "OK", 2000);

    snprintf(aux_str, sizeof(aux_str), "AT+SAPBR=3,1,\"PWD\",\"%s\"", password);
    sendATcommand(aux_str, "OK", 2000);

    // gets the GPRS bearer
    while (sendATcommand("AT+SAPBR=1,1", "OK", 20000) == 0)
    {
        delay(5000);
    }

}

void loop(){

    // gets GPS data
    get_GPS();

    // sends GPS data to the script
    send_HTTP();

    delay(5000);

}

void power_on(){

    uint8_t answer=0;

    // checks if the module is started
    answer = sendATcommand("AT", "OK", 2000);
    if (answer == 0)
    {
        // power on pulse
        digitalWrite(onModulePin,HIGH);
        delay(3000);
        digitalWrite(onModulePin,LOW);

        // waits for an answer from the module
        while(answer == 0){  
            // Send AT every two seconds and wait for the answer   
            answer = sendATcommand("AT", "OK", 2000);    
        }
    }

}

int8_t start_GPS(){

    unsigned long previous;

    previous = millis();
    // starts the GPS
    sendATcommand("AT+CGPSPWR=1", "OK", 2000);
    sendATcommand("AT+CGPSRST=0", "OK", 2000);

    // waits for fix GPS
    while(( (sendATcommand("AT+CGPSSTATUS?", "2D Fix", 5000) || 
        sendATcommand("AT+CGPSSTATUS?", "3D Fix", 5000)) == 0 ) && 
        ((millis() - previous) < 90000));

    if ((millis() - previous) < 90000)
    {
        return 1;
    }
    else
    {
        return 0;    
    }
}

int8_t get_GPS(){

    int8_t counter, answer;
    long previous;

    // First get the NMEA string
    // Clean the input buffer
    while( Serial.available() > 0) Serial.read(); 
    // request Basic string
    sendATcommand("AT+CGPSINF=0", "AT+CGPSINF=0\r\n\r\n", 2000);

    counter = 0;
    answer = 0;
    memset(frame, '\0', 100);    // Initialize the string
    previous = millis();
    // this loop waits for the NMEA string
    do{

        if(Serial.available() != 0){    
            frame[counter] = Serial.read();
            counter++;
            // check if the desired answer is in the response of the module
            if (strstr(frame, "OK") != NULL)    
            {
                answer = 1;
            }
        }
        // Waits for the asnwer with time out
    }
    while((answer == 0) && ((millis() - previous) < 2000));  

    frame[counter-3] = '\0'; 

    // Parses the string 
    strtok(frame, ",");
    strcpy(longitude,strtok(NULL, ",")); // Gets longitude
    strcpy(latitude,strtok(NULL, ",")); // Gets latitude
    strcpy(altitude,strtok(NULL, ".")); // Gets altitude 
    strtok(NULL, ",");    
    strcpy(date,strtok(NULL, ".")); // Gets date
    strtok(NULL, ",");
    strtok(NULL, ",");  
    strcpy(satellites,strtok(NULL, ",")); // Gets satellites
    strcpy(speedOTG,strtok(NULL, ",")); // Gets speed over ground. Unit is knots.
    strcpy(course,strtok(NULL, "\r")); // Gets course

    convert2Degrees(latitude);
    convert2Degrees(longitude);

    return answer;
}

/* convert2Degrees ( input ) - performs the conversion from input 
 * parameters in  DD°MM.mmm’ notation to DD.dddddd° notation. 
 * 
 * Sign '+' is set for positive latitudes/longitudes (North, East)
 * Sign '-' is set for negative latitudes/longitudes (South, West)
 *  
 */
int8_t convert2Degrees(char* input){

    float deg;
    float minutes;
    boolean neg = false;    

    //auxiliar variable
    char aux[10];

    if (input[0] == '-')
    {
        neg = true;
        strcpy(aux, strtok(input+1, "."));

    }
    else
    {
        strcpy(aux, strtok(input, "."));
    }

    // convert string to integer and add it to final float variable
    deg = atof(aux);

    strcpy(aux, strtok(NULL, '\0'));
    minutes=atof(aux);
    minutes/=1000000;
    if (deg < 100)
    {
        minutes += deg;
        deg = 0;
    }
    else
    {
        minutes += int(deg) % 100;
        deg = int(deg) / 100;    
    }

    // add minutes to degrees 
    deg=deg+minutes/60;


    if (neg == true)
    {
        deg*=-1.0;
    }

    neg = false;

    if( deg < 0 ){
        neg = true;
        deg*=-1;
    }

    float numberFloat=deg; 
    int intPart[10];
    int digit; 
    long newNumber=(long)numberFloat;  
    int size=0;

    while(1){
        size=size+1;
        digit=newNumber%10;
        newNumber=newNumber/10;
        intPart[size-1]=digit; 
        if (newNumber==0){
            break;
        }
    }

    int index=0;
    if( neg ){
        index++;
        input[0]='-';
    }
    for (int i=size-1; i >= 0; i--)
    {
        input[index]=intPart[i]+'0'; 
        index++;
    }

    input[index]='.';
    index++;

    numberFloat=(numberFloat-(int)numberFloat);
    for (int i=1; i<=10 ; i++)
    {
        numberFloat=numberFloat*10;
        digit= (long)numberFloat;          
        numberFloat=numberFloat-digit;
        input[index]=char(digit)+48;
        index++;
    }
    input[index]='\0';


}

void send_HTTP(){

    uint8_t answer=0;
    // Initializes HTTP service
    answer = sendATcommand("AT+HTTPINIT", "OK", 10000);
    if (answer == 1)
    {
        // Sets CID parameter
        answer = sendATcommand("AT+HTTPPARA=\"CID\",1", "OK", 5000);
        if (answer == 1)
        {
            // Sets url 
            sprintf(aux_str, "AT+HTTPPARA=\"URL\",\"http://%s/vehicleLocationTransmitter.php?", url);
            Serial.print(aux_str);
            sprintf(frame, "vehicleID=1&latitude=%s&longitude=%s&altitude=%s&time=%s&satellites=%s",
            latitude, longitude, altitude, date, satellites);
            Serial.print(frame);
            answer = sendATcommand("\"", "OK", 5000);
            if (answer == 1)
            {
                // Starts GET action
                answer = sendATcommand("AT+HTTPACTION=0", "+HTTPACTION:0,200", 30000);
                if (answer == 1)
                {

                    Serial.println(F("Done!"));
                }
                else
                {
                    Serial.println(F("Error getting url"));
                }

            }
            else
            {
                Serial.println(F("Error setting the url"));
            }
        }
        else
        {
            Serial.println(F("Error setting the CID"));
        }    
    }
    else
    {
        Serial.println(F("Error initializating"));
    }

    sendATcommand("AT+HTTPTERM", "OK", 5000);

}


int8_t sendATcommand(char* ATcommand, char* expected_answer1, unsigned int timeout){

    uint8_t x=0,  answer=0;
    char response[100];
    unsigned long previous;

    memset(response, '\0', 100);    // Initialize the string

    delay(100);

    while( Serial.available() > 0) Serial.read();    // Clean the input buffer

    Serial.println(ATcommand);    // Send the AT command 


        x = 0;
    previous = millis();

    // this loop waits for the answer
    do{
        if(Serial.available() != 0){    
            response[x] = Serial.read();
            x++;
            // check if the desired answer is in the response of the module
            if (strstr(response, expected_answer1) != NULL)    
            {
                answer = 1;
            }
        }
        // Waits for the asnwer with time out
    }
    while((answer == 0) && ((millis() - previous) < timeout));    

    return answer;
}

2 个答案:

答案 0 :(得分:1)

为有疑问的函数编写测试用例(convert2Degrees())

来自评论:“DD°MM.mmm中的参数'符号为DD.dddddd°”:

如果您输入:52°27.123 然后预期的输出应为:52.45205
计算:52°27.123 = 52 + 27.123 / 60.0 =
= 52.45205

此外,您应该在此处发布输入convert2Degrees()

的值

答案 1 :(得分:0)

您对strtok的使用不正确,convert2degrees存在重大问题。

这是从NeoGPS派生的东西,它不使用昂贵的(在AVR上)除法或模运算:

//.................................................
// A special divide-by-3 function that avoids division.
// From http://www.hackersdelight.org/divcMore.pdf

static uint32_t divu3( uint32_t n )
{
  uint32_t q = (n >> 2) + (n >> 4); // q = n*0.0101 (approx).
  q = q + (q >> 4); // q = n*0.01010101.
  q = q + (q >> 8);
  q = q + (q >> 16);

  uint32_t r = n - q*3; // 0 <= r <= 15.
  return q + (11*r >> 5); // Returning q + r/3.
}

//------------------------------------------------------------
// Parse the NMEA "DDDMM.mmmm" format for lat and lon.
//
// returns degrees * 10^7

uint32_t parseDDMMmmmm( char *input )
{
  uint8_t  digits      = 0;
  uint8_t  sixth_digit = 0;
  char     chr;

  //  Find the decimal point
  while (isdigit( input[digits] ))
    digits++;

  // All but the last two are degrees.
  uint32_t val    = 0;
  while (digits > 2) {
    chr = *input++;
    val = val*10 + (chr - '0');
    digits--;
  }

  // convert from degrees to minutes
  val *= 60;

  // Add in the last 2 minutes digits
  uint8_t minutes = 0;
  while (digits > 0) {
    chr = *input++;
    minutes = minutes*10 + (chr - '0');
    digits--;
  }
  val += minutes;

  // Decimal point?
  chr = *input++;
  if (chr == '.') {
    chr = *input++;

    // Parse up to 6 digits of the fractional minutes.
    while ((digits++ < 5) && isdigit( chr )) {
      val = val*10 + (chr - '0');
      chr = *input++;
    }

    if ((digits == 6) && isdigit(chr)) {
      sixth_digit = chr - '0';
      digits++;
    }

    // Scale the value up to minutes x 1000000.
    while (digits < 4) {
      val *= 10;
      digits++;
    }
  }

  // convert from minutes x 1000000 to degrees x 10000000.
  val += divu3( val*2 + 1 ); // same as 10 * (val+30)/60 without truncation
  if (digits >= 6) {
    if (sixth_digit >= 9)
      val += 2;
    else if (sixth_digit >= 4)
      val += 1;
  }

  return val;

} // parseDDMMmmmm

...和一个浮点版本:

double parseDDMMmmmm_f( char *input )
{
  uint8_t  digits = 0;
  char     chr;

  //  Find the decimal point
  while (isdigit( input[digits] ))
    digits++;

  // All but the last two are degrees.
  double val    = 0.0;
  while (digits > 2) {
    chr = *input++;
    val = val*10 + (chr - '0');
    digits--;
  }

  // convert from degrees to minutes
  val *= 60;

  // Add in the last 2 minutes digits
  uint8_t minutes = 0;
  while (digits > 0) {
    chr = *input++;
    minutes = minutes*10 + (chr - '0');
    digits--;
  }
  val += minutes;

  // Decimal point?
  chr = *input++;
  if (chr == '.') {
    chr = *input++;

    // Parse the fractional "mmmm" minutes.
    while (isdigit( chr ) && (digits++ <= 4)) {
      val = val*10 + (chr - '0');
      chr = *input++;
    }

    // Scale the value up to minutes x 1000000.
    while (digits < 4) {
      val *= 10;
      digits++;
    }
  }

  // convert from minutes x 1000000 to degrees.
  val *= 10.0/1000000.0 * 1.0/60.0;

  return val;

} // parseDDMMmmmm_f

如果你使用这些函数,你还需要在AT字符串前面弹出' - ',并否定返回的值:

bool south = (latitude[0] == '-');
if (south)
  latitude++;
float lat = parseDDMMmmmm_f( latitude );
if (south)
  lat = -lat;

convert2Degrees(longitude);
bool west = (longitude[0] == '-');
if (west)
  longitude++;
float lon = parseDDMMmmmm_f( longitude );
if (west)
  lon = -lon;[/code]