天天看點

基于EC20 CE FILG GPS資料采集的總結

     子產品采用 Quectel  EC20 CE FILG (4G+GPS),這裡我隻對軟體部分實作GPS資料的采集、資料幀解析和GPS漂移點過濾的一些方法和要點進行總結。首先,上效果圖:

基于EC20 CE FILG GPS資料采集的總結

需求: 采集GPS位置資訊,定時上傳GPS資料到平台(平台上展示gps錨點和軌迹資訊部分,不再本文讨論範圍)

1、GPS資料采集前的準備工作

    GPS設定,可以通過調用AT指令,依次執行以下操作,

"AT+QGPSCFG=\"outport\",\"usbnmea\""
"AT+QGPS=1"
"AT+QGPS?"
"AT+QGPSLOC=?"
           

筆者本地使用microcom工具,指令如下:

// 通過管道執行shell指令并傳回結果
static bool executeCMD(const char *cmd, char *result)
{
    char buf_ps[2048];
    char ps[1024]={0};
    FILE *ptr = NULL;
    strcpy(ps, cmd);
    ptr=popen(ps, "r");

    memset(result,0,strlen(result));

    if(ptr!=NULL)
    {
        while(fgets(buf_ps, 1024, ptr)!=NULL)
        {
            strcat(result, buf_ps);
            if(strlen(result)>1024)
                break;
        }
        pclose(ptr);
        ptr = NULL;
        c_debug("cmd %s exec result is :\n%s",cmd,result);
        return true;
    }
    else
    {
        c_debug("thread cmd open failed");
        return false;
    }

}


// 初始化GPS子產品
void initGPS(char* cmd){
    char buff[1024] = {0};
    sprintf(buff,"echo -e \"%s\\r\\n\" | microcom -s 115200 -t 2000 /dev/ttyUSB2",cmd);
    executeCMD(buff,result);
}
           

另外說明, EC20 CE FILG 子產品包含4個驅動,/dev/ttyUSB2 序列槽用于接收AT指令并輸出執行結果

在執行初始化GPS操作前,可以監聽 /dev/ttyUSB2 口

cat /dev/ttyUSB2
           

另外,我們查詢4G子產品相關詳細,如

“AT+CSQ”  ==》 查詢信号強度

“AT+CIMI” ==》 SIM卡檢測 

...

等等,相關執行結果都可以在 /dev/ttyUSB2 口檢視到

基于EC20 CE FILG GPS資料采集的總結

2、檢視gps資料

gps資料檢視有以下兩種方式:

(1)使用AT指令檢視:

AT+QGPSLOC= 2 ; 
備注:查詢到的經緯度格式 format: (-)dd.ddddd,(-)ddd.dddd ,即以度為機關
除了2 之位,還有其他的參數可設定,具體,可檢視子產品操作相關說明文檔
           

(2)通過監聽 /dev/ttyUSB1 口可以檢視,可以看到有大量的GPS資料從此端口吐出(output)

cat /dev/ttyUSB1
           
基于EC20 CE FILG GPS資料采集的總結

 這裡,我們如果需要編寫程式從/dev/ttyUSB1 口循環讀取gps資料,可以參考Linux下C語言序列槽程式設計通訊示例,這裡不再過多介紹。 

這裡有個的坑需要注意:預設輸出的經緯度資訊 GPGGA中,使用格式 dddmm.mmmmmm ,一定要進行響應的轉換

3、經緯度轉換

   采集的原資料為WGS-84格式,如果我們使用百度地圖的API,需要轉換成BD09,這裡提供一套網上的轉換方法,項目測試可用。

// .h

struct gps_position_t {
    int32_t lat; /*< Latitude, expressed as degrees * 1E7*/
    int32_t lon; /*< Longitude, expressed as degrees * 1E7*/
    double lat_f;
    double lon_f;
};

class QGCTransform
{
public:
    QGCTransform();

    /*WGS-84 to GCJ-02*/
    //double PI;
    double a;
    double ee;

    gps_position_t gps_t;

    void Transform();
    void Transform(double lat, double lon);
    void bd_encrypt(double gg_lat, double gg_lon, double &bd_lat, double &bd_lon);
    void bd_decrypt(double bd_lat, double bd_lon, double &gg_lat, double &gg_lon);


private:
    bool OutOfChina(double lat, double lon);
    double TransformLat(double x, double y);
    double TransformLon(double x, double y);
};
           
// .cpp


#define PI (3.14159265358979324)


//其中 bd_encrypt 将 GCJ-02 坐标轉換成 BD-09 坐标, bd_decrypt 反之
const double x_pi = 3.14159265358979324 * 3000.0 / 180.0;

void QGCTransform::bd_encrypt(double gg_lat, double gg_lon, double &bd_lat, double &bd_lon)
{
    double x = gg_lon, y = gg_lat;
    double z = sqrt(x * x + y * y) + 0.00002 * sin(y * x_pi);
    double theta = atan2(y, x) + 0.000003 * cos(x * x_pi);
    bd_lon = z * cos(theta) + 0.0065;
    bd_lat = z * sin(theta) + 0.006;
}

void QGCTransform::bd_decrypt(double bd_lat, double bd_lon, double &gg_lat, double &gg_lon)
{
    double x = bd_lon - 0.0065, y = bd_lat - 0.006;
    double z = sqrt(x * x + y * y) - 0.00002 * sin(y * x_pi);
    double theta = atan2(y, x) - 0.000003 * cos(x * x_pi);
    gg_lon = z * cos(theta);
    gg_lat = z * sin(theta);
}

QGCTransform::QGCTransform(){
    a = 6378245.0;
    ee = 0.00669342162296594323;
}

/*WGS-84 to GCJ-02*/

bool QGCTransform::OutOfChina(double lat, double lon){
    if (lon < 72.004 || lon > 137.8347)
        return true;
    if (lat < 0.8293 || lat > 55.8271)
        return true;
    return false;
}

double QGCTransform::TransformLat(double x, double y){
    double ret = -100.0 + 2.0 * x + 3.0 * y + 0.2 * y * y + 0.1 * x * y + 0.2 * qSqrt(qAbs(x));
    ret += (20.0 * qSin(6.0 * x * PI) + 20.0 * qSin(2.0 * x * PI)) * 2.0 / 3.0;
    ret += (20.0 * qSin(y * PI) + 40.0 * qSin(y / 3.0 * PI)) * 2.0 / 3.0;
    ret += (160.0 * qSin(y / 12.0 * PI) + 320 * qSin(y * PI / 30.0)) * 2.0 / 3.0;
    return ret;
}

double QGCTransform::TransformLon(double x, double y)
{
    double ret = 300.0 + x + 2.0 * y + 0.1 * x * x + 0.1 * x * y + 0.1 * qSqrt(qAbs(x));
    ret += (20.0 * qSin(6.0 * x * PI) + 20.0 * qSin(2.0 * x * PI)) * 2.0 / 3.0;
    ret += (20.0 * qSin(x * PI) + 40.0 * qSin(x / 3.0 * PI)) * 2.0 / 3.0;
    ret += (150.0 * qSin(x / 12.0 * PI) + 300.0 * qSin(x / 30.0 * PI)) * 2.0 / 3.0;
    return ret;
}

void QGCTransform::Transform(){
    double lat_s=gps_t.lat * 1E-7;
    double lon_s=gps_t.lon * 1E-7;
    Transform(lat_s, lon_s);
}

void QGCTransform::Transform(double lat_s, double lon_s){
    gps_t.lat_f = lat_s;
    gps_t.lon_f = lon_s;
    gps_t.lat = lat_s * 1E7;
    gps_t.lon = lon_s * 1E7;
    if (OutOfChina(lat_s, lon_s ))
    {
        return;
    }
    double dLat = TransformLat(lon_s - 105.0, lat_s - 35.0);
    double dLon = TransformLon(lon_s - 105.0, lat_s - 35.0);
    double radLat = lat_s / 180.0 * PI;
    double magic = qSin(radLat);
    magic = 1 - ee * magic * magic;
    double sqrtMagic = qSqrt(magic);
    dLat = (dLat * 180.0) / ((a * (1 - ee)) / (magic * sqrtMagic) * PI);
    dLon = (dLon * 180.0) / (a / sqrtMagic * qCos(radLat) * PI);
    gps_t.lat = (lat_s + dLat) * 1E7;
    gps_t.lon = (lon_s + dLon) * 1E7;
    gps_t.lat_f = lat_s + dLat;
    gps_t.lon_f = lon_s + dLon;
}
           

4、漂移點過濾

   關于漂移點過濾的邏輯,我們可以簡單使用如下方法處理,大緻提升采集資料的可靠性(經度更多依賴于硬體本身,這裡我們隻讨論軟體過濾輔助提高經度的方法)

(1)過濾無效點。 $GPRMC <2>狀态, A 為有效位置, V為非有效接收警告

(2)采集樣本點。前10個點不上傳,用于判斷裝置的運動模式,如果超過6個以上采集到速度為0,視為靜止,否則可以視為運動模式處理。

(3)靜止模式下,前10個點有兩中處理方式,第一種直接将最後一個有效點作為基準點處理;第二種,計算10個樣本點的離散中心,作為基準點;

(4)運動狀态下,則采用下幾個步驟進一步過濾,然後以最後一個有效點作為運動狀态的基準點上傳資料;

         采用大速度過濾原則:通過GPVTG擷取地面速度,如果速度大于130Km/h,則過濾

         采用大距離過濾原則:通過計算前後采集相鄰兩采集點之際的距離,如果距離超過50m則過濾(可調整)

         采用大加速度過濾原則:目前一般轎車10秒内可從零加速到100km/h,平均加速度為2.778m/s^2,即大約0.28g,最大加速度可達到0.6g左右。這裡通過計算前後gps位置點的加速度,如果超過0.6g,則過濾掉。

另,具體開發遇到坑比較多,祝好運🤞

參考:一篇關于GPS定位寫得最詳實清晰的文章之一

繼續閱讀