IGS上的外部GPS数据的WGS84大地水准面高度高度偏移

时间:2014-03-05 11:41:25

标签: ios iphone objective-c gps wgs84

对于我正在编写的应用程序,我们正在将IOS设备与外部传感器连接,该传感器通过本地wifi网络输出GPS数据。这些数据以海拔高度的“原始”格式出现。通常,所有GPS高度需要具有与基于当前位置的WGS84大地水准面高度相关的校正因子。

例如,在以下地理控制点(http://www.ngs.noaa.gov/cgi-bin/ds_mark.prl?PidBox=HV9830)位于Lat 38 56 36.77159 和Lon 077 01 08.34929

HV9830* NAD 83(2011) POSITION- 38 56 36.77159(N) 077 01 08.34929(W)   ADJUSTED  
HV9830* NAD 83(2011) ELLIP HT-    42.624 (meters)        (06/27/12)   ADJUSTED
HV9830* NAD 83(2011) EPOCH   -  2010.00
HV9830* NAVD 88 ORTHO HEIGHT -    74.7    (meters)     245.    (feet) VERTCON   
HV9830  ______________________________________________________________________
HV9830  GEOID HEIGHT    -        -32.02  (meters)                     GEOID12A
HV9830  NAD 83(2011) X  -  1,115,795.966 (meters)                     COMP
HV9830  NAD 83(2011) Y  - -4,840,360.447 (meters)                     COMP
HV9830  NAD 83(2011) Z  -  3,987,471.457 (meters)                     COMP
HV9830  LAPLACE CORR    -         -2.38  (seconds)                    DEFLEC12A

您可以看到大地水准面高度 -32米。因此,如果在此点附近进行RAW GPS读数,则必须应用-32米的校正以计算正确的高度。 (注意:校正是负的,所以你实际上要减去负数,从而将读数调高32米。)

与Android相反,我们的理解是,关于coreLocation,这个GeoidHeight信息由IOS在内部自动计算。我们遇到困难的地方是我们正在使用带有传感器的本地wifi网络,该传感器计算未校正的GPS并收集外部传感器数据以及GPS的核心位置读数。我想知道是否有人知道有一个具有大地水准面信息的库(C / Objective-C),当我从传感器包中读取原始GPS信号时,它可以帮助我即时进行这些计算。

感谢您的帮助。

旁注:请不要建议我查看以下帖子: Get altitude by longitude and latitude in Android 这是一个很好的解决方案但是我们没有实时的互联网连接所以我们不能向Goole或USGS进行实时查询。

3 个答案:

答案 0 :(得分:5)

我已经在这里解决了我的问题。我所做的是创建一个fortran代码的c实现的ObjectiveC实现来做我需要的。可以在此处找到原始c:http://sourceforge.net/projects/egm96-f477-c/

您需要从source forge下载项目才能访问此代码所需的输入文件: CORCOEF EGM96

我的Objective-c实现如下:

<强> GeoidCalculator.h

#import <Foundation/Foundation.h>

@interface GeoidCalculator : NSObject
+ (GeoidCalculator *)instance;


-(double) getHeightFromLat:(double)lat    andLon:(double)lon;
-(double) getCurrentHeightOffset;
-(void) updatePositionWithLatitude:(double)lat andLongitude:(double)lon;

@end

<强> GeoidCalculator.m

#import "GeoidCalculator.h"
#import <stdio.h>
#import <math.h>


#define l_value    (65341)
#define _361    (361)

@implementation GeoidCalculator

static int nmax;

static double currentHeight;

static double cc[l_value+ 1], cs[l_value+ 1], hc[l_value+ 1], hs[l_value+ 1],
        p[l_value+ 1], sinml[_361+ 1], cosml[_361+ 1], rleg[_361+ 1];

+ (GeoidCalculator *)instance {
    static GeoidCalculator *_instance = nil;

    @synchronized (self) {
        if (_instance == nil) {
            _instance = [[self alloc] init];
            init_arrays();
            currentHeight = -9999;
        }
    }

    return _instance;
}


- (double)getHeightFromLat:(double)lat andLon:(double)lon {
    [self updatePositionWithLatitude:lat andLongitude:lon];
    return [self getCurrentHeightOffset];
}


- (double)getCurrentHeightOffset {
    return currentHeight;
}

- (void)updatePositionWithLatitude:(double)lat andLongitude:(double)lon {
    const double rad = 180 / M_PI;
    double flat, flon, u;
    flat = lat; flon = lon;

    /*compute the geocentric latitude,geocentric radius,normal gravity*/
    u = undulation(flat / rad, flon / rad, nmax, nmax + 1);

    /*u is the geoid undulation from the egm96 potential coefficient model
       including the height anomaly to geoid undulation correction term
       and a correction term to have the undulations refer to the
       wgs84 ellipsoid. the geoid undulation unit is meters.*/
    currentHeight = u;
}


double hundu(unsigned nmax, double p[l_value+ 1],
        double hc[l_value+ 1], double hs[l_value+ 1],
        double sinml[_361+ 1], double cosml[_361+ 1], double gr, double re,
        double cc[l_value+ 1], double cs[l_value+ 1]) {/*constants for wgs84(g873);gm in units of m**3/s**2*/
    const double gm = .3986004418e15, ae = 6378137.;
    double arn, ar, ac, a, b, sum, sumc, sum2, tempc, temp;
    int k, n, m;
    ar = ae / re;
    arn = ar;
    ac = a = b = 0;
    k = 3;
    for (n = 2; n <= nmax; n++) {
        arn *= ar;
        k++;
        sum = p[k] * hc[k];
        sumc = p[k] * cc[k];
        sum2 = 0;
        for (m = 1; m <= n; m++) {
            k++;
            tempc = cc[k] * cosml[m] + cs[k] * sinml[m];
            temp = hc[k] * cosml[m] + hs[k] * sinml[m];
            sumc += p[k] * tempc;
            sum += p[k] * temp;
        }
        ac += sumc;
        a += sum * arn;
    }
    ac += cc[1] + p[2] * cc[2] + p[3] * (cc[3] * cosml[1] + cs[3] * sinml[1]);
/*add haco=ac/100 to convert height anomaly on the ellipsoid to the undulation
add -0.53m to make undulation refer to the wgs84 ellipsoid.*/
    return a * gm / (gr * re) + ac / 100 - .53;
}

void dscml(double rlon, unsigned nmax, double sinml[_361+ 1], double cosml[_361+ 1]) {
    double a, b;
    int m;
    a = sin(rlon);
    b = cos(rlon);
    sinml[1] = a;
    cosml[1] = b;
    sinml[2] = 2 * b * a;
    cosml[2] = 2 * b * b - 1;
    for (m = 3; m <= nmax; m++) {
        sinml[m] = 2 * b * sinml[m - 1] - sinml[m - 2];
        cosml[m] = 2 * b * cosml[m - 1] - cosml[m - 2];
    }
}

void dhcsin(unsigned nmax, double hc[l_value+ 1], double hs[l_value+ 1]) {


    // potential coefficient file
    //f_12 = fopen("EGM96", "rb");
    NSString* path2 = [[NSBundle mainBundle] pathForResource:@"EGM96" ofType:@""];
    FILE* f_12 = fopen(path2.UTF8String, "rb");
    if (f_12 == NULL) {
        NSLog([path2 stringByAppendingString:@" not found"]);
    }



    int n, m;
    double j2, j4, j6, j8, j10, c, s, ec, es;
/*the even degree zonal coefficients given below were computed for the
 wgs84(g873) system of constants and are identical to those values
 used in the NIMA gridding procedure. computed using subroutine
 grs written by N.K. PAVLIS*/
    j2 = 0.108262982131e-2;
    j4 = -.237091120053e-05;
    j6 = 0.608346498882e-8;
    j8 = -0.142681087920e-10;
    j10 = 0.121439275882e-13;
    m = ((nmax + 1) * (nmax + 2)) / 2;
    for (n = 1; n <= m; n++)hc[n] = hs[n] = 0;
    while (6 == fscanf(f_12, "%i %i %lf %lf %lf %lf", &n, &m, &c, &s, &ec, &es)) {
        if (n > nmax)continue;
        n = (n * (n + 1)) / 2 + m + 1;
        hc[n] = c;
        hs[n] = s;
    }
    hc[4] += j2 / sqrt(5);
    hc[11] += j4 / 3;
    hc[22] += j6 / sqrt(13);
    hc[37] += j8 / sqrt(17);
    hc[56] += j10 / sqrt(21);


    fclose(f_12);

}

void legfdn(unsigned m, double theta, double rleg[_361+ 1], unsigned nmx)
/*this subroutine computes  all normalized legendre function
in "rleg". order is always
m, and colatitude is always theta  (radians). maximum deg
is  nmx. all calculations in double precision.
ir  must be set to zero before the first call to this sub.
the dimensions of arrays  rleg must be at least equal to  nmx+1.
Original programmer :Oscar L. Colombo, Dept. of Geodetic Science
the Ohio State University, August 1980
ineiev: I removed the derivatives, for they are never computed here*/
{
    static double drts[1301], dirt[1301], cothet, sithet, rlnn[_361+ 1];
    static int ir;
    int nmx1 = nmx + 1, nmx2p = 2 * nmx + 1, m1 = m + 1, m2 = m + 2, m3 = m + 3, n, n1, n2;
    if (!ir) {
        ir = 1;
        for (n = 1; n <= nmx2p; n++) {
            drts[n] = sqrt(n);
            dirt[n] = 1 / drts[n];
        }
    }
    cothet = cos(theta);
    sithet = sin(theta);
    /*compute the legendre functions*/
    rlnn[1] = 1;
    rlnn[2] = sithet * drts[3];
    for (n1 = 3; n1 <= m1; n1++) {
        n = n1 - 1;
        n2 = 2 * n;
        rlnn[n1] = drts[n2 + 1] * dirt[n2] * sithet * rlnn[n];
    }
    switch (m) {
        case 1:
            rleg[2] = rlnn[2];
            rleg[3] = drts[5] * cothet * rleg[2];
            break;
        case 0:
            rleg[1] = 1;
            rleg[2] = cothet * drts[3];
            break;
    }
    rleg[m1] = rlnn[m1];
    if (m2 <= nmx1) {
        rleg[m2] = drts[m1 * 2 + 1] * cothet * rleg[m1];
        if (m3 <= nmx1)
            for (n1 = m3; n1 <= nmx1; n1++) {
                n = n1 - 1;
                if ((!m && n < 2) || (m == 1 && n < 3))continue;
                n2 = 2 * n;
                rleg[n1] = drts[n2 + 1] * dirt[n + m] * dirt[n - m] *
                        (drts[n2 - 1] * cothet * rleg[n1 - 1] - drts[n + m - 1] * drts[n - m - 1] * dirt[n2 - 3] * rleg[n1 - 2]);
            }
    }
}

void radgra(double lat, double lon, double *rlat, double *gr, double *re)
/*this subroutine computes geocentric distance to the point,
the geocentric latitude,and
an approximate value of normal gravity at the point based
the constants of the wgs84(g873) system are used*/
{
    const double a = 6378137., e2 = .00669437999013, geqt = 9.7803253359, k = .00193185265246;
    double n, t1 = sin(lat) * sin(lat), t2, x, y, z;
    n = a / sqrt(1 - e2 * t1);
    t2 = n * cos(lat);
    x = t2 * cos(lon);
    y = t2 * sin(lon);
    z = (n * (1 - e2)) * sin(lat);
    *re = sqrt(x * x + y * y + z * z);/*compute the geocentric radius*/
    *rlat = atan(z / sqrt(x * x + y * y));/*compute the geocentric latitude*/
    *gr = geqt * (1 + k * t1) / sqrt(1 - e2 * t1);/*compute normal gravity:units are m/sec**2*/
}


double undulation(double lat, double lon, int nmax, int k) {
    double rlat, gr, re;
    int i, j, m;
    radgra(lat, lon, &rlat, &gr, &re);
    rlat = M_PI / 2 - rlat;
    for (j = 1; j <= k; j++) {
        m = j - 1;
        legfdn(m, rlat, rleg, nmax);
        for (i = j; i <= k; i++)p[(i - 1) * i / 2 + m + 1] = rleg[i];
    }
    dscml(lon, nmax, sinml, cosml);
    return hundu(nmax, p, hc, hs, sinml, cosml, gr, re, cc, cs);
}

void init_arrays(void) {
    int ig, i, n, m;
    double t1, t2;






    NSString* path1 = [[NSBundle mainBundle] pathForResource:@"CORCOEF" ofType:@""];


    //correction coefficient file:  modified with 'sed -e"s/D/e/g"' to be read with fscanf
    FILE* f_1 = fopen([path1 cStringUsingEncoding:1], "rb");
    if (f_1 == NULL) {
        NSLog([path1 stringByAppendingString:@" not found"]);
    }


    nmax = 360;
    for (i = 1; i <= l_value; i++)cc[i] = cs[i] = 0;

    while (4 == fscanf(f_1, "%i %i %lg %lg", &n, &m, &t1, &t2)) {
        ig = (n * (n + 1)) / 2 + m + 1;
        cc[ig] = t1;
        cs[ig] = t2;
    }
/*the correction coefficients are now read in*/
/*the potential coefficients are now read in and the reference
 even degree zonal harmonic coefficients removed to degree 6*/
    dhcsin(nmax, hc, hs);
    fclose(f_1);
}


@end

我对大地水准面高度计算器(http://www.unavco.org/community_science/science-support/geoid/geoid.html)进行了一些有限的测试,看起来一切都很匹配

更新iOS8或更高版本

自IOS8起,此代码可能无法正常运行。您可能需要更改软件包的加载方式:

[[NSBundle mainBundle] pathForResource:@"EGM96" ofType:@""];

在这里进行谷歌搜索或添加评论。

答案 1 :(得分:2)

令人印象深刻的东西Jeef!我只是使用你的代码来创建这个sqlite,这可能更容易在项目中添加/使用,假设lat / lon的整数精度足够好:

https://github.com/vectorstofinal/geoid_heights

答案 2 :(得分:0)

您可以使用GeoTrans。

http://earth-info.nga.mil/GandG/geotrans/index.html提供

关键字是“垂直基准”。所以你想要从WGS84转换为例如EGM96垂直基准。确保要使用哪种Geoid modell。 EGM96就是其中之一。

也许这些答案对你有帮助: How to calculate the altitude above from mean sea level

接下来阅读ios开源许可证文本:可在

中找到
Settings -> General -> About -> Legal -> License ...

您将获得ios使用的所有库的列表。其中我发现其中一个是用美国地质勘探局计算磁性十分之一。很可能很高,大地水准面高度计算也列在那里。