ラベル GNSS の投稿を表示しています。 すべての投稿を表示
ラベル GNSS の投稿を表示しています。 すべての投稿を表示

2012年10月17日水曜日

電離層遅延(放送暦)

今日はGPS放送暦の電離層遅延について少し調べた。
IS-GPS-200Eを読んでみただけだけど、混乱したのは、角度の単位が式で異なること。間違っているかもしれないけれども、また混乱しそうなので、一応単位をメモしておく。
F=1+16*(0.53-EL)^3 (EL:semi-circle)
phi=0.0137/(EL+0.11)-0.022 (EL:semi-circle)
cos(AZ), sin(AZ) (AZ:radian)
もちろん三角関数の中身がsemi-circleになるはずはないのだけど、Fなんかはどっちだ?となってしまった。

さて、IS-GPS-200Eにのっているモデルはklobucharモデルというやつなのだけど、元論文を読んだことはない。だけど、式を見ると、「何時にこの座標でこれだけの遅延量がある」という記述というより、「丸っこい遅延の塊が移動する様子」が記述されているように見える。なるほど・・・、電離層遅延は伝搬していくということなのかな?

勉強はこれでやったことにして、国土地理院からダウンロードしたRINEXファイルを確認してみると・・・、なんと、電離層遅延パラメータが載っていないではないですか!RINEXファイルフォーマットを読むと、オプション項目なんですね。

ふーむ。IGS点のデータを持ってくるか・・・。GEONETは定点観測なので、相対測位が前提ということなのかなあ。

まあ、とりあえずIS-GPS-200Eの該当部分を読んでプログラムにしてみたので、まだ動くかはわからないけど、超ベータ版ということで自分のために載せておくことにした。

double brdion(const double az, const double el, const double phi_u, const double rambda_u, const gtime_t time, const double *ion_model) {
    double dt;  /* ionospheric delay (sec) **not correction value** */

    double phi_i0, phi_i, rambda_i, phi_m, psi;
    double amp, per, f, x;
    gtime_t tl;
    const double R2SC=R2D/180.0;
    const double D2SC=1/180.0;
    const double SC2R=PI;
    int i;

    amp=per=0.0;

    /* estimate phi */
    psi = 0.0137/(el*R2SC) - 0.022;
    phi_i0 = phi_u*D2SC + psi*cos(az); /* az(rad) */
    
    if (fabs(phi_i0) <= 0.416) {
        phi_i = phi_i0;
    }
    else if (phi_i0 > 0.416) {
        phi_i = 0.416;
    }
    else {
        phi_i = -0.416;
    }

    rambda_i = rambda_u*D2SC + (psi*sin(az)/cos(phi_i*SC2R));
    phi_m = phi_i + 0.064*cos(rambda_i*SC2R - 1.617);

    f = 1.0 + 16.0*(0.53-el*R2SC)*(0.53-el*R2SC)*(0.53-el*R2SC);

    /* ion_model: iono model parameters {a0,a1,a2,a3,b0,b1,b2,b3} */
    for (i=0;i<4;i++) {
        amp += ion_model[i]*pow(phi_m,i);
        per += ion_model[4+i]*pow(phi_m,i);
    }
    if (amp < 0.0) {
        amp = 0.0;
    }
    if (per < 72000.0) {
        per = 72000.0;
    }
    tl = timeadd(time, 12.0*3600.0*rambda_i);
    x = 2.0*PI*(tl.time+tl.sec-14.0*3600.0)/per;

    if (fabs(x) <= 1.57) {
        dt = f*5.0e-9;
    }
    else {
        dt = f*(5.0e-9 + amp*(1-x*x/2.0+x*x*x*x/24.0));
    }

    return dt;
}

2012年9月18日火曜日

QZSの軌道

前回作ってみたプログラムで8月16日のQZSの軌道を描いてみた。
なぜかatanで計算するとyとzがプラマイ逆になってしまう時があったのでatan2を使って衛星軌道は計算した。なんでかな。
どうもc言語は使い慣れていないのがいけないなと思う。こんなことで時間を使ってしまうとは。

で、QZSを「小松」から見上げたら、こんな感じ。

おお、本当に8の字。

暦計算はGPSと全く同じだった。

2012年9月12日水曜日

放送暦の計算(2)

前に放送暦で衛星座標を計算したらRTKLIBのsatpossと100m以上の差が出てしまった。
(ちなみにQZS)

なんでかなと思っていたら、satposを使うと受信機から衛星までの伝搬時間分が補正されてしまうためだった。(前にメモした話のやつ)
単に時刻決め打ちで衛星座標を計算するなら、eph2posを使えば良い。

計算してみると、


------------------------------------------------------------------
2012/08/16 00:00:00 (x, y, z)
(自前)    -23318436.155264, 18586028.182482, -25160301.817461 
(eph2pos) -23318436.155244, 18586028.182504, -25160301.817463 (RTKLIB API) 
------------------------------------------------------------------


一応計算は合っているぽい。

この微妙な差はEkを計算する際のiteration方法で出るのだろうか??

ちなみにEkの計算は、ただのiterationと、Newton-Raphson法の2種類試してみたのだけど、収束まで1,2回しか変わらなかった。

------------------------------------------------------------------
static double upd_E(const double E, const double M, const double e) {
    /* return M+e*sin(E); */ /* simple */
    return E-(E-M-e*sin(E))/(1-e*cos(E)); /* Newton-Raphson */
}
------------------------------------------------------------------

2012年9月7日金曜日

放送暦の計算

お盆が終わってから週末がなんだかんだと予定があり、何もできなかった。

今日は早めに帰ったし、久々にプログラムを書いてみることに。
ということでQZSSの放送暦から軌道を計算してみた。

とはいえ、IS-QZSSを読むのが面倒なのでIS-GPSの計算をやってみたのだけども。
(IS-GPS-200E, 8 June 2010 p.92-96)

で、自前で作ったプログラムと、RTKLIBで計算したQZSの座標値を比べると・・・

------------------------------------------------------------------
2012/08/16 00:00:00 (x, y, z)
(自前)    -23318436.155264, 18586028.182482, -25160301.817461 

(satpos) -23318319.272650, 18586114.815274, -25160339.595141 (RTKLIB API) 
------------------------------------------------------------------

なんかずいぶん違うよな・・・。100m超。

相対論効果を考慮してないのだけど、そのせいなのか、はたまた計算ミスなのか。
それとも計算式がGPSとQZSで違うのか。
ただ、酒を飲みながらプログラムを書いたので計算ミスの可能性が非常に高い。
いや、相対論効果って、ここで効いてくるのか??どんどん分からなくなってきたぞ。
お酒怖い。

しかし、相対性理論が普通の生活で使われていて、こんなに影響してたら、すごいよなあ、と思う。一度まじめに相対性理論を勉強してみたい。

さて、衛星軌道の計算が大体合っていそうなら、あとは最小二乗で位置を計算すれば単独測位プログラムの完成だ。

2012年8月19日日曜日

電子基準点 小松(0255) その(2)

実家から帰宅。運転疲れた。

「小松」にせっかく行ってきたので、マルチパスを調べてみることにした。
MPを計算するプログラムも書いてみたことだし、チェックを兼ねて。

ということで、実家近くの「小松」(上)と「白峰」(下)を比べてみたところ。
局座標はアプリオリでズルしている。

1.「小松」vs「白峰」 (スカイプロット:小松(上)、白峰(下))
国土地理院のページにある「スカイプロット表示」を 真似たもの。




2.「小松」vs「白峰」 (仰角横軸:小松(上)、白峰(中)、重ねたもの(下))



スカイプロットを見ると「小松」が圧倒的に観測条件は良いのだけど、仰角で見てみると、高仰角では「白峰」の方が良いような。

「小松」が海沿い(海抜0m近い)にあるのに対して、「白峰」は500mくらいのところにあるため、大気遅延誤差が軽減されているんだろうか。
(「小松」のMP1で仰角7度あたりにトビがあるのは、平均を取る際に適当に仰角10度以下の値を切ったせい??)

とにかく、「小松」は良い観測点のようで良かった。地元民として。

さて、 スカイプロット表示は以外にややこしかったのでgnuplotスクリプトを貼っておく。


set polar
set size square
unset border
set format x ""; set format y ""

set angles degrees
set datafile commentschars "#"
set datafile separator ","
set xrange[-90:90]
set yrange[-90:90]
set cbrange[-6:6]
set grid polar 30
set xtics axis 15
set ytics axis 15

# labels
set label "15" at 15*cos(120),15*sin(120)
set label "30" at 30*cos(120),30*sin(120)
set label "45" at 45*cos(120),45*sin(120)
set label "60" at 60*cos(120),60*sin(120)
set label "75" at 75*cos(120),75*sin(120)
set label "90" at 90*cos(120),90*sin(120)

set palette rgbformulae 22,13,-31

set multiplot title "Code multipath" layout 1,2
plot "tmp.dat" using (90+$1):(90-$2):3 pt 7 ps 0.3 lc palette title "MP1"
plot "tmp.dat" using (90+$1):(90-$2):4 pt 7 ps 0.3 lc palette title "MP2"
unset multiplot
tmp.datの中身はこんな感じで方位角、仰角、MP1、MP2が並んだもの。

 75.277634,  11.737041,  0.329021,  -1.379482
 75.450305,  11.595049,  0.588193,  0.520832
 75.622963,  11.453226,  -0.309160,  0.994863
 75.795605,  11.311571,  0.180602,  -0.183782

2012年8月8日水曜日

衛星のephemerisの扱い

単独測位をやってみようか、と思ってふと気づいたのだけども、受信機が記録している時刻は、衛星の電波発信時刻と異なる(いまさら気づく)。

いくら光速で信号が届いたからと言って、受信時刻と同じ時刻なわけはない。

さあ、普通はどのように扱うのかな、と思って先日買った教科書「GNSS」を開く。

えー、
"受信機まで0.06秒くらいかかります。衛星は1km/sで動いているので60mに相当します。"

おっと、だめじゃないか。QZSだったりすると軌道がでかいからさらに大きくなるぞ。しかも教科書に補正方法書いてないじゃん。

ということで、ephemerisを座標値に変換する場合は、多分次のようにする必要がある。


暦計算の時刻=受信時刻 −(擬似距離÷光速)=発信時刻


でもこの程度でいいのかな?直接軌道誤差に効いてきてしまう。

あと、暦の中の時計誤差は、こうやって出した時刻に対しておこなって、その時刻で算出すればいいか。つまり二回時計誤差の計算をすることになるかな。

IS-GPSにきちんと書いてあるかもしれないけど読む気力が無い・・・

まあとにかく受信時刻で暦を使っちゃだめだよ、と今更気づいた、という話。

MP線形結合の計算(2)

昨日MP線形結合の計算をしてみたけども、出力してみるとやけに大きな値が出る衛星がある。

なんだろうな、と思ったらGLONASSだった。GLONASSのFDMA波長にきちんと衛星毎に合わせる必要があるのだった。

少し計算を分けてみたのでメモしておく。
例によってrtklibに便利なsatwavelenという関数があることに気づいたので、早速使ってみる。
/* compute mp linear combination for GPS and QZS */
static int mp_gps(const double P1, const double P2, const double L1, const double L2, double *mp1, double *mp2) {
    double a=FREQ1*FREQ1/(FREQ2*FREQ2);
    double lam1=CLIGHT/FREQ1;
    double lam2=CLIGHT/FREQ2;

    *mp1 = P1 - (1+2/(a-1))*L1*lam1 + (2/(a-1))*L2*lam2;
    *mp2 = P2 - (2*a/(a-1))*L1*lam1 + (2*a/(a-1)-1)*L2*lam2;

    return 0;
}

/* compute mp linear combination for GLONASS FDMA */
static int mp_glo(int satnum, const nav_t *nav, const double P1, const double P2, const double L1, const double L2, double *mp1, double *mp2) {
    double a, lam1, lam2;

    lam1 = satwavelen(satnum, 0, nav);
    lam2 = satwavelen(satnum, 1, nav);
    a = lam2*lam2/(lam1*lam1); /* f1^2/f2^2 */

    *mp1 = P1 - (1+2/(a-1))*L1*lam1 + (2/(a-1))*L2*lam2;
    *mp2 = P2 - (2*a/(a-1))*L1*lam1 + (2*a/(a-1)-1)*L2*lam2;

    return 0; 
}
aは、f1^2/f2^2だけど、波長だと分母分子が入れ替わります。
satwavelenがすごく便利でした。このコード例では毎回呼び出すのでちょっと無駄な処理が多いんですが。

出力:
----- epoch start -----
sat num: 17, timetag, PRN, MP1, MP2
 0: 2012/07/09 00:00:00   9       71.8339      100.7876
 1: 2012/07/09 00:00:00  41     -242.7161     -297.4944
 2: 2012/07/09 00:00:00  14      137.7746      184.8548
 3: 2012/07/09 00:00:00  40     -242.3190     -297.2934
 4: 2012/07/09 00:00:00  15      119.1687      160.6320
 5: 2012/07/09 00:00:00  42      377.8123      480.3067
 6: 2012/07/09 00:00:00  31       61.5032       91.2867
 7: 2012/07/09 00:00:00  12       35.9511       58.4904
 8: 2012/07/09 00:00:00  33      183.4078      236.6984
 9: 2012/07/09 00:00:00  22       28.5107       45.0275
10: 2012/07/09 00:00:00  25       21.7292       46.4806
11: 2012/07/09 00:00:00  18       77.3021      105.7750
12: 2012/07/09 00:00:00  43     -527.0718     -645.2571
13: 2012/07/09 00:00:00  51       34.2777       53.0246
14: 2012/07/09 00:00:00  52     -118.9257     -130.6246
15: 2012/07/09 00:00:00  27       98.0804      133.2339
16: 2012/07/09 00:00:00  39       11.8226       19.8136


とりあえずまともそうな値が出たのでよしとする。多分合ってるだろう。
あとは単独測位をして仰角を横軸にしてノイズレベルを調べてみようかな。



2012年8月7日火曜日

MP線形結合の計算

MP線形結合、と呼ぶのかどうかわからないけど、teqcのマニュアルを見ると載っているクオリティチェック用の線形結合。

要は、電離層遅延と対流圏遅延の両方を差っ引いて、マルチパスノイズの変化量だけにしたもの。

計算するだけなら簡単。ということでメモしておく。
例によって、rtklibを使ったもの。

/* print code multipath for L1, L2 */
int printmp(const obs_t *obs) {
    int i=0, j, nu;
    char tmstr[20];
    double mp1, mp2;
    double a=FREQ1*FREQ1/(FREQ2*FREQ2);  /* alpha = (f1/f2)^2 */

    while(in) {
        /* seek index of the next epoch */
        nu = nextobsf(obs, &i, 0);
        printf("----- epoch start -----\n");
        printf("sat num: %d, timetag, PRN, MP1, MP2\n", nu);

        for(j=i;jdata[j].time, tmstr, 0);
            mp1 = obs->data[j].P[0] - (1+2/(a-1))*obs->data[j].L[0] + (2/(a-1))*obs->data[j].L[1];
            mp2 = obs->data[j].P[1] - (2*a/(a-1))*obs->data[j].L[0] + (2*a/(a-1)-1)*obs->data[j].L[1];

            printf("%2d: %s %3d %13.4f %13.4f\n", j-i, tmstr, obs->data[j].sat, mp1, mp2);
        }
        i += nu;
    }


    return 0;
}
よく見ると、メートルに直し忘れているけれどそのままにしておく。