衛星位置解算


前言:

本章節代碼均在Gitee中開源:

衛星位置計算代碼icon-default.png?t=N7T8https://gitee.com/Ehundred/navigation-engineering/tree/master/%E5%8D%AB%E6%98%9F%E5%AF%BC%E8%88%AA%E5%8E%9F%E7%90%86/GPS%E5%8D%AB%E6%98%9F%E4%BD%8D%E7%BD%AE%E8%A7%A3%E7%AE%97/Satellite_position_calculation其中涉及到時空轉換的部分,在另一章節有詳細講解,如有疑惑可以先閱讀上一章節:

時空坐標轉換icon-default.png?t=N7T8https://blog.csdn.net/qq_74260823/article/details/139271746?spm=1001.2014.3001.5501

因為這章是學校作業,所以稍微正經點.?


衛星參數的讀取

首先,關于導航電文的理解和構成,如果還不太了解,請移步另一篇文章:

衛星信號的構成icon-default.png?t=N7T8http://t.csdnimg.cn/6yY29

導航電文會發出很多參數,最終會被接收機接收到,然后轉換為人類可以看懂的具體的數字。接收機在轉換之后,會把適用于我們定位的數據單獨列出來,然后寫入文件之中成為廣播星歷,其中文件的格式長這樣:

其具體對應的含義是:

因為寫入的格式一定是固定的,所以我們讀取數據的時候,也可以利用這些固定的格式,按順序定義一個結構體:

struct satellite_data_block
{double Serial;//序列號double year;double month;double day;double hour;double minute;double second;//年月日時分秒double a0;//衛星鐘差double a1;//衛星鐘偏double a2;//衛星鐘偏移double AODE;//數據齡期double Crs;//軌道半徑改正項double deltan;//平均角速度改正項double M0;//平近點角double Cuc;//升交點角距改正項double e;//軌道偏心率double Cus;//升交點角距改正項double sqrtA;//軌道長半軸平方根double TOE;//星歷的參考時刻  周內秒double Cic;//軌道傾角的改正項double OMEGA;//升交點經度double Cis;//軌道傾角改正項double i0;//軌道傾角double Crc;//軌道半徑的改正項double omega;//近地點角距double deltaomega;//升交點赤經變化double IDOT;//軌道傾角的變率//對于單點定位,以上數據足以,但是為了保持衛星數據完整,以下保持了所有的數據double CA;//CA碼double GPSweek;//gps周數double L2P;//L2P碼double SVA;//衛星精度double SVH;//衛星健康double TGD;//電離層延遲double IODC;//數據質量double shoot_time;//發射時間double h;//擬合區間
};

為什么序列號,年月日時分秒也要用double,在待會會講解。?

而讀取的時候,因為格式是固定的,每一個完整的數據都有8行,我們可以每次就讀8行,然后把這8行合并成同一個字符串,用來表示同一個數據塊。而這個函數只負責讀取,把讀取的數據轉換為可用的數據并導入在結構體中,就交給另外一個函數來完成。

void read()
{fstream ifs(_path_name, ios_base::in);if (!ifs)	exit(-1);while (!ifs.eof()){string data;for (int i = 0; i < 8; i++){//采用的是讀取整個數據塊的方法char buffer[82];ifs.getline(buffer, 81);data += buffer;}get_data(data);}
}

這個把字符串轉換成有用的數據的方法,可以有很多實現形式,完全憑個人習慣。因為有36個數據,我們就定義一個36大小的double數組,每次讀一個數據,就把數據放在double數組之中,一直到讀完整個36個數據。
而讀完之后,因為結構體采用的是內存連續區間,而數組也是內存連續區間,我們之間用memcpy,把數組的內存copy到結構體的內存上,就省略了大量的代碼量。

void get_data(string& data)
{double init[36];int cur = 0;for (int i = 0; i < 36; i++){string tmp = get_part(data, cur);//獲取一串數字的字符串init[i] = _to_double(tmp);//把獲得的字符串轉換成double//兩個函數的具體實現看個人習慣,我的實現可以去看完整代碼}satellite_data_block ins;memcpy(&ins, init, sizeof(double) * 36);_serial.insert(make_pair(ins.Serial, _data.size()));_data.push_back(ins);
}

讀取之后,我們直接把他放在數組中,然后接著讀取下一個。最終數組中便完整記錄了所有衛星的參數,我們可以訪問任意衛星的參數。
但是,這里會有一個問題:我們怎么知道數組中對應的是哪個衛星?難道第5個元素對應的一定是序號為5的衛星嗎?當然不一定。我們有兩個解決方案:

  1. 每次遍歷數組,查看每個元素的序列號
  2. 用哈希表,把元素下標對應上衛星的序列號

第一個方法的時間復雜度每次都是O(n),自然是比較不靠譜的,所以我們就采用了第二個方法,用哈希表的搜索降低時間復雜度。


衛星位置的解算

導航電文是兩個小時更新一次的。因為這個特性,注定了導航電文無法發送即時的位置數據,所以衛星只能發送更新時刻的位置,然后根據一些參數來模擬衛星的運動,推算出衛星每一刻的位置。

用最簡單的思想來看待這個問題,其實也很簡單:

只不過具體實現的時候,要考慮的影響因素有很多就是了。

因為每個衛星參數對應的位置是獨立的,所以對每一個衛星參數,我們都用一個類來表示,類中包含所有可能要用到的參數:

class calculate
{
private:double sqrtA;double angular_velocity;double _tk;double _angular_velocity;double _Mk;double _Ek;double _Vk;double _argument_of_latitude;double _Cuk;double _Crk;double _Cik;double _Uk;double _Rk;double _Ik;double _OMGk;
};

具體的符號會在計算的過程中講解。?

1.計算角速度

第一個在廣播星歷中已經給出,我們只需要計算第二個:

#define GM 3.9860050E14void sqrt_A(double sqrt_a)
{sqrtA = sqrt_a;angular_velocity = sqrt(GM) / pow(sqrtA, 3);
}

2.計算衛星運動時間

因為廣播星歷給出的時間Toe是每次更新的瞬間時間,而Tobs是當前的觀測時間,所以Tobs-Toe,再消去衛星信號傳播時間導致的誤差,就是衛星運動的時間Tk。
注意:在廣播星歷中,給出的Toe是周內秒,而實際的計算計算的是總時間差。這里的Toe并非表示周內秒,而是GPS包含周的總時間。為了計算方便,我們可以直接把他轉換成Unix時來進行加減。

double get_time(double Toe,double week,atime cur = curtime)
{//因為沒有考慮信號傳播時間,最終有小部分誤差GPStime _bt(week, Toe);atime bt(_bt);double ret = cur.GetGtime()._time-bt.GetGtime()._time;if (ret > ROUND_SECOND/2)	ret -= ROUND_SECOND;else if (ret < -ROUND_SECOND / 2)	ret += ROUND_SECOND;//ROUND_SECOND GPS周一周的秒數_tk = ret;return ret;
}

3.對平均角速度改正

在廣播星歷中,會給出一個參數:Δn,這個參數就是給我們用以修正角速度的,我們直接把計算出的角速度修正上Δn便可以了:

double new_angular_velocity(double deltan)
{_angular_velocity = angular_velocity + deltan;return _angular_velocity;
}

4.計算平近點角

廣播星歷會給出一個參數M0,是在數據更新時刻的近點角。而此時,衛星運動了Tk時刻,我們用角速度n乘以運動時間Tk,就能得出衛星距離更新時刻轉動的角度,再加上更新時刻的角度,就是當前時刻的平近點角

double mean_anomaly(double M0)
{_Mk = M0 + _tk * _angular_velocity;return _Mk;
}

?5.計算偏近點角

公式:E=M+e*sinE

和時空轉換一樣,我們發現,這個公式左邊有一個E,右邊有一個E,怎么可以用自己來計算自己呢?這里就要用迭代的方法,取E一個初值En,然后不斷求下一個自己的值En+1,一直到En+1和En相差很小很小,就代表他們收斂到了一個具體的值上,這個值就是我們要求得的值。?

double Eccentric_Anomaly(double e)
{double Mk = _Mk;auto get_Ek = [e,Mk](double Ep){return Ep - (Ep - e * sin(Ep) - Mk) / (1 - e * cos(Ep));};double E0 = _Mk;double E1 = get_Ek(E0);while (abs(E1 - E0) >= 1E-14){E0 = E1;E1 = get_Ek(E0);}_Ek = E1;return _Ek;
}

6.計算真近點角

double True_anomaly(double e)
{_Vk = atan((sqrt(1 - e * e) * sin(_Ek) )/ (cos(_Ek) - e));if (abs(_Vk - _Mk) > Pi / 2)_Vk += _Vk > _Mk ? -Pi : Pi;return _Vk;
}

?這里也可以直接用atan2來實現。

有一點要注意:導航電文中給出的所有數據都是弧度,而非角度,所以我們在使用sin等函數的時候,直接使用廣播星歷中的數據便可以了,不需要角度轉弧度即*180/Pi

7.計算升交角距

double argument_of_latitude(double omega)
{_argument_of_latitude = _Vk + omega;return _argument_of_latitude;
}

8.計算二階調和改正數

double Correction_of_uk(double cuc,double cus)
{_Cuk = cus * sin(2 * _argument_of_latitude) + cuc * cos(2 * _argument_of_latitude);return _Cuk;
}double Correction_of_rk(double crc, double crs)
{_Crk = crs * sin(2 * _argument_of_latitude) + crc * cos(2 * _argument_of_latitude);return _Crk;
}double Correction_of_ik(double cic, double cis)
{_Cik = cis * sin(2 * _argument_of_latitude) + cic * cos(2 * _argument_of_latitude);return _Cik;
}

9.計算改正后的開普勒軌道根數

double get_uk()
{_Uk = _argument_of_latitude + _Cuk;return _Uk;
}double get_rk(double e)
{_Rk = sqrtA*sqrtA * (1 - e * cos(_Ek)) + _Crk;return _Rk;
}double get_ik(double i0, double idot)
{_Ik = i0 + _Cik + idot * _tk;return _Ik;
}double get_OMGk(double OMG0,double OMGd,double toe)
{_OMGk = OMG0 + (OMGd - OMGE) * _tk - OMGE * toe;return _OMGk;
}

?10.計算衛星的位置

在求出衛星在軌道平面坐標系的坐標后,把該坐標繞x軸旋轉i角(軌道傾角),繞z軸旋轉OmegaK角(升交點經度),得到的便是在地心地固坐標系下的坐標。這個部分是線代中的坐標系轉換,具體的方程感興趣可以自己推導

vector<double> get_xyz()
{double _xk = _Rk * cos(_Uk);double _yk = _Rk * sin(_Uk);double x = _xk * cos(_OMGk) - _yk * cos(_Ik) * sin(_OMGk);double y = _xk * sin(_OMGk) + _yk * cos(_Ik) * cos(_OMGk);double z = _yk * sin(_Ik);return { x,y,z };
}

最終,便可以得到衛星坐標的位置xyz。

代碼匯總:

我們采用了一個類來完成,在傳入一個衛星數據塊后,便在構造函數里直接計算出該數據塊對應的衛星位置。而因為讀取衛星數據,計算衛星數據是分離的,所以我們可以再設計另外一個接口,將兩個類的功能結合在一起,而這個實現方法可以根據個人習慣,也可以直接參考我完整的代碼。

#define GM 3.9860050E14
#define OMGE 7.2921151467E-5commontime _curtime = commontime({ 2021,2,1,0,0 }, 30);//當前觀測時間
atime curtime = atime(_curtime);//計算的完整過程,必須按順序依次進行
class calculate
{
public:vector<double> _calculate(const satellite_data_block& s){sqrt_A(s.sqrtA);get_time(s.TOE, s.GPSweek);new_angular_velocity(s.deltan);mean_anomaly(s.M0);Eccentric_Anomaly(s.e);True_anomaly(s.e);argument_of_latitude(s.omega);Correction_of_uk(s.Cuc, s.Cus);Correction_of_rk(s.Crc, s.Crs);Correction_of_ik(s.Cic, s.Cis);get_uk();get_rk(s.e);get_ik(s.i0, s.IDOT);get_OMGk(s.OMEGA, s.deltaomega, s.TOE);vector<double> xyz = get_xyz();return xyz;}
private:void sqrt_A(double sqrt_a){sqrtA = sqrt_a;angular_velocity = sqrt(GM) / pow(sqrtA, 3);}double get_time(double Toe,double week,atime cur = curtime){//因為沒有考慮信號傳播時間,最終有小部分誤差GPStime _bt(week, Toe);atime bt(_bt);double ret = cur.GetGtime()._time-bt.GetGtime()._time;if (ret > ROUND_SECOND/2)	ret -= ROUND_SECOND;else if (ret < -ROUND_SECOND / 2)	ret += ROUND_SECOND;_tk = ret;return ret;}double new_angular_velocity(double deltan){_angular_velocity = angular_velocity + deltan;return _angular_velocity;}double mean_anomaly(double M0){_Mk = M0 + _tk * _angular_velocity;return _Mk;}double Eccentric_Anomaly(double e){double Mk = _Mk;auto get_Ek = [e,Mk](double Ep){return Ep - (Ep - e * sin(Ep) - Mk) / (1 - e * cos(Ep));};double E0 = _Mk;double E1 = get_Ek(E0);while (abs(E1 - E0) >= 1E-14){E0 = E1;E1 = get_Ek(E0);}_Ek = E1;return _Ek;}double True_anomaly(double e){_Vk = atan((sqrt(1 - e * e) * sin(_Ek) )/ (cos(_Ek) - e));if (abs(_Vk - _Mk) > Pi / 2)_Vk += _Vk > _Mk ? -Pi : Pi;return _Vk;}double argument_of_latitude(double omega){_argument_of_latitude = _Vk + omega;return _argument_of_latitude;}double Correction_of_uk(double cuc,double cus){_Cuk = cus * sin(2 * _argument_of_latitude) + cuc * cos(2 * _argument_of_latitude);return _Cuk;}double Correction_of_rk(double crc, double crs){_Crk = crs * sin(2 * _argument_of_latitude) + crc * cos(2 * _argument_of_latitude);return _Crk;}double Correction_of_ik(double cic, double cis){_Cik = cis * sin(2 * _argument_of_latitude) + cic * cos(2 * _argument_of_latitude);return _Cik;}double get_uk(){_Uk = _argument_of_latitude + _Cuk;return _Uk;}double get_rk(double e){_Rk = sqrtA*sqrtA * (1 - e * cos(_Ek)) + _Crk;return _Rk;}double get_ik(double i0, double idot){_Ik = i0 + _Cik + idot * _tk;return _Ik;}double get_OMGk(double OMG0,double OMGd,double toe){_OMGk = OMG0 + (OMGd - OMGE) * _tk - OMGE * toe;return _OMGk;}vector<double> get_xyz(){double _xk = _Rk * cos(_Uk);double _yk = _Rk * sin(_Uk);double x = _xk * cos(_OMGk) - _yk * cos(_Ik) * sin(_OMGk);double y = _xk * sin(_OMGk) + _yk * cos(_Ik) * cos(_OMGk);double z = _yk * sin(_Ik);return { x,y,z };}private:double sqrtA;double angular_velocity;double _tk;double _angular_velocity;double _Mk;double _Ek;double _Vk;double _argument_of_latitude;double _Cuk;double _Crk;double _Cik;double _Uk;double _Rk;double _Ik;double _OMGk;
};

調試數據:

為了方便大家調試,我列出了對應的調試數據:

對應我的文件中序列號為1的衛星:

廣播星歷參數:

計算出的數據:
最終結果:

更多的測試用例可以直接在完整源碼中查看所有31個衛星數據?


最后,給自己疊個甲。因為自己才是導航工程大二的本科生,有些概念理解可能不到位,而又想用最容易理解的方式表達出來,所以可能正確性會稍微有些偏差。但是對初學者來說,應該不會存在太大的錯誤,如果可以幫到你,真的榮幸之極。還有

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/bicheng/22199.shtml
繁體地址,請注明出處:http://hk.pswp.cn/bicheng/22199.shtml
英文地址,請注明出處:http://en.pswp.cn/bicheng/22199.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

SAP物料自動記賬科目設置總結

SAP物料自動記賬科目設置總結 目錄 物料自動記賬科目設置總結... 1 總體說明... 1 庫存移動事務類型的設置... 4 庫存科目設置... 6 期初導入... 6 業務舉例... 6 配置... 6 庫存初始單據... 7 采購收貨(缺少到票) 8 業務舉例... 8 配置... 8 采購收貨單據-MIGO_GR…

心懷希望の光柵化

還記得什么是光柵化咩&#xff1f; 將三維空間的幾何形體顯現在屏幕上&#xff0c;這就是光柵化&#xff08;游戲、實時圖形學的應用&#xff09; Perspective Projection 在正交投影里如何定義三維空間中的立方體呢&#xff1f; 用x軸的覆蓋&#xff08;左、右&#xff09;…

【UML用戶指南】-02-UML基本元素的介紹(二)

目錄 1、語法和語義規則 2、UML中的公共機制 &#xff08;1&#xff09;規約 &#xff08;2&#xff09;修飾 &#xff08;3&#xff09;通用劃分 &#xff08;4&#xff09;擴展機制 衍型/版型/類型&#xff08;stereotype&#xff09; 標記值 &#xff08;tagged val…

Java編程常見問題匯總四

系列文章目錄 文章目錄 系列文章目錄前言一、忽略所有異常二、重復包裝RuntimeException三、不正確的傳播異常四、用日志記錄異常五、異常處理不徹底 前言 前些天發現了一個巨牛的人工智能學習網站&#xff0c;通俗易懂&#xff0c;風趣幽默&#xff0c;忍不住分享一下給大家。…

[C/C++]_[初級]_[在Windows和macOS平臺上導出動態庫的一些思考]

場景 最近看了《COM本質論》里關于如何設計基于抽象基類作為二進制接口,把編譯器和鏈接器的實現隱藏在這個二進制接口中,從而使用該DLL時不需要重新編譯。在編譯出C接口時,發現接口名直接是函數名,比如BindNativePort,怎么不是_BindNativePort?說明 VC++導出的函數默認是使…

專轉本英語應該怎么學?

來吧&#xff0c;寶子們&#xff0c;學姐給你們分享專轉本英語如何備考的方法了&#xff0c;單詞&#xff0b;語法&#xff0c;兩不耽誤&#xff0c;快拿著你們的小手來截圖記筆記啦&#xff5e; 1、基礎差直接背單詞 對于基礎差的人呢&#xff0c;本身我們對英語這個科目就不感…

Google Earth Engine精度評價方法

今天講講如何在GEE中做最后的精度評價。主要是因為在和許多讀者或通過交流群&#xff0c;或通過私聊溝通過程中&#xff0c;發現很多人還不是很理解在GEE中分類后精度評價的問題。 在進行評價之前&#xff0c;需要明晰在GEE中精度評價分為哪幾種情況。我們這里說的是兩種情況。…

收藏品NFT的開發流程

開發收藏品NFT的流程涉及多個階段&#xff0c;從概念化和設計到技術實現和市場推廣。以下是詳細的開發步驟&#xff0c;通過這些步驟&#xff0c;可以成功開發和發布收藏品NFT項目&#xff0c;吸引用戶和投資者&#xff0c;并確保項目的持續運營和成功。北京木奇移動技術有限公…

Fiddler入門(接口抓包及APP測試)

目錄 一、Fiddler基礎介紹 二、Fiddler的作用 三、Fiddler安裝 四、Fiddler界面功能介紹 1、界面介紹 1&#xff09;、菜單欄介紹 2&#xff09;、工具欄介紹 3&#xff09;、會話欄介紹 五、Fiddler抓取https數據 &#xff08;面試題&#xff09; 六、Fiddler…

C++ lambda表達式的作用和代碼示例

Lambda 表達式是 C11 引入的一種匿名函數語法&#xff0c;它可以方便地創建臨時函數對象&#xff0c;用于在函數調用時作為參數傳遞或者作為局部函數使用。Lambda 表達式可以捕獲外部變量&#xff0c;并具有與普通函數相似的語法結構。 主要作用如下&#xff1a; 簡化代碼&am…

【刷題(17)】技巧

一 技巧基礎 二 136. 只出現一次的數字 1 題目 2 解題思路 哈希表map 其實看到題目數組中某個元素出現的次數也可以直接用unordered_map容器統計每一個元素出現的次數&#xff0c;然后在遍歷整個map容器查看是否有元素出現的次數等于1 3 code class Solution { public:in…

商城項目【尚品匯】07分布式鎖-2 Redisson篇

1 Redisson功能介紹 基于自定義setnx實現的分布式鎖存在下面的問題&#xff1a; 重入問題&#xff1a;重入問題是指 獲得鎖的線程可以再次進入到相同的鎖的代碼塊中&#xff0c;可重入鎖的意義在于防止死鎖&#xff0c;比如HashTable這樣的代碼中&#xff0c;他的方法都是使用…

LightGBM 進行回歸建模的流程

LightGBM 進行回歸建模的流程 文章最前&#xff1a; 我是Octopus&#xff0c;這個名字來源于我的中文名–章魚&#xff1b;我熱愛編程、熱愛算法、熱愛開源。所有源碼在我的個人github &#xff1b;這博客是記錄我學習的點點滴滴&#xff0c;如果您對 Python、Java、AI、算法有…

將HTML頁面中的table表格元素轉換為矩形,計算出每個單元格的寬高以及左上角坐標點,輸出為json數據

export function huoQuTableElement() {const tableData []; // 存儲表格數據的數組let res [];// 獲取到包含表格的foreignObject元素const foreignObject document.getElementById(mydctable);if (!foreignObject){return ;}// 獲取到表格元素let oldTable foreignObject…

Nativefier : 將網址打包成exe桌面程序

1、需求場景 在日常開發中&#xff0c;需要針對一些網頁在一體機上使用&#xff0c;同時在瀏覽器上也可以使用&#xff0c;這里推薦大家用nativefier&#xff0c;對網址進行打包。以下是nativefier安裝命令&#xff1a; npm install nativefier -g 2、使用方法 --arch 系統 …

《混凝土壩監測儀器系列型譜》修訂中監測儀器分類方案解讀

隨著科技的不斷進步和監測需求的日益增加&#xff0c;對監測儀器分類方案進行修訂已成為必然的趨勢。本文旨在探討《混凝土壩監測儀器系列型譜》中對現有儀器分類方式的修訂&#xff0c;以及監測儀器選用的相關內容。希望對大家中有所幫助&#xff1a; 一、取消過時條目&#x…

服務器是一種高性能計算機

服務器是一種高性能計算機&#xff0c;專門設計用于在網絡中提供各種服務。它們通常具備比普通計算機更快的CPU運算能力、更可靠的運行性能、更大的I/O外部數據吞吐能力以及更好的擴展性。

java中方法引用

目錄 方法引用&#xff1a; 引用靜態方法 引用成員方法 引用構造方法 使用類名引用成員方法 引用數組的構造方法 練習 方法引用&#xff1a; 把已經有的方法拿過來用&#xff0c;當做函數式接口中抽象方法的方法體 在Java中&#xff0c;方法引用是一種簡化Lambda表達式的…

詳解Spring支持的幾種注入方式

在 Spring 框架中&#xff0c;Bean 的注入方式主要有以下幾種&#xff0c;其中一些是自動注入的。以下是詳細說明&#xff1a; 1. 構造函數注入 (Constructor Injection) 自動注入&#xff1a;使用 Autowired 注解時&#xff0c;Spring 容器會自動調用帶有 Autowired 注解的構…

教務管理系統-學員辦理體系介紹

隨著時代的快速開展&#xff0c;教育方面也沒落下&#xff0c;不僅是線下線上都呈現許多訓練校園&#xff0c;辦理軟件也順勢而為的呈現廣闊訓練校園面前&#xff0c;許多的校園和訓練組織也都在運用教務管理系統了。運用教務管理系統里邊的學員辦理體系可以讓相應的辦理人員更…