RTKLIB开源库有着强大的GPS数据实时和后处理功能,由于笔者的毕业设计中需要对GPS载波相位观测量进行RTK解算,故而,对RTKLIB开源库进行了学习与研究。
RTKLIB提供了很多底层的函数,笔者准备直接对源码进行编译输出标准DLL的方式供C#调用。所用的VS平台是VS2012(其它VS版本类似),RTKLIB库用的是网上使用的最多,相对稳定的rtklib_2.4.2版本,编译的项目采用“相对路径”,即工程可移植到任何地方,方便以后使用。
GNSS标准&精密定位开源程序包 rtklib_2.4.2 下载
目前(2018.6.21)RTKLIB开源库最新版本:rtklib_2.4.3 官网最新版本下载
也可直接参考笔者编译好的RTKLIB VS2012工程 下载
一、RTKLIB简介
RTKLIB是全球导航卫星系统GNSS(global navigation satellite system)的标准&精密定位开源程序包,RTKLIB由日本东京海洋大学(Tokyo Universityof Marine Science and Technology)的高须知二(Tomoji Takasu)开发。RTKLIB由一个便携式程序库和多个AP(应用程序)工具库组成。
RTKLIB的主要功能有:
(1)支持多个GNSS系统的标准和精密定位算法,包括GPS,GLONASS,Beidou,Galileo,QZSS和SBAS
(2)支持多种GNSS实时和后处理定位模式:单点定位、DGPS/DGNSS,动态RTK、静态RTK、移动基站、PPP
(3)支持多种GNSS标准格式和协议:RINEX2.10、RINEX2.11、RINEX2.12、RINEX3.00、RINEX3.01、RINEX3.02、RTCM2.3、RTCM3.1、RTCM3.2、BINEX、NTRIP、NMEA0183、SP3、ANTEX1.4、IONEX1.0、NGS PCV、EMS 2.0
(4)支持多种GNSS接收机专有数据协议格式:NovAtel:OEM4/V/6,OEM3, OEMStar、Superstar II、 Hemisphere、Crescent、u‐blox:LEA-4T/5T/6T、SkyTraq、JAVAD 、GW10-II/III和NVS
(5)支持外部通信:Serial、TCP/IP、NTRIP、本地日志文件(记录和播发)和FTP/HTTP
(6)提供许多函数库和API(application program interfaces):卫星和导航系统函数、矩阵和向量函数,时间和字符串函数、坐标的转换,输入和输出函数、调试跟踪函数、平台依赖函数、定位模型、大气模型、天线模型、地球潮汐模型、大地水准面模型、基准转换、RINEX函数、星历和时钟函数、精密星历和时钟、接收机原始数据函数、RTCM函数,解算函数、谷歌地球KML转换、SBAS函数、选项(option)函数、流数据输入和输出函数、整周模糊度解算、标准定位、精密定位、后处理定位(解算)、流服务器函数、RTK服务器函数、下载函数。
二、基于VS的RTKLIB编译
1.新建工程。首先,我们在VS2012下先新建一个win32的dll项目(空项目)
Win32应用程序设置如下:
2. 复制库文件。把在github上下载的rtklib2.4.2里的所有文件夹及文件复制到刚刚建立的RTKLIB工程项目文件所在目录下,如图
RTKLIB开源库(RTKLIB开源库是用C++Builder建立的工程,与VS不同)下文件目录结构及各文件夹功能:
\app-- APs构建环境 \bin--可执行二进制APs和windows链接库
\data-- APs样本数据 \doc--文档文件
\lib --库生成环境 \src--RTKLIB库的源程序
\test--测试程序和数据 \util-- 实用程序工具
然后我们删掉的RTKLIB工程目录下的两个文件(github开源项目的配置文件)
其实,由于我们现在只是为了编译这个开源库, RTKLIB开源库中除“src文件夹”其它的我们暂时都用不到,但为了保持库的完整性以及防止以后做工程会用的到,此处都留下了,做实际工程应用的话,可以删除其它用不到的文件夹。
3.添加库文件。为了与库文件结构保持一致,我们首先在“源文件”文件夹,右击—》添加—》新建筛选器,接下来先添加头文件(在src文件夹中,只有一个头文件:rtklib.h),
然后是源文件—》添加—》现有项(添加除rtklib.h所有源文件,)“rcv”子文件夹中添加“src/rcv”目录下的所有源文件,添加完成之后,如图:
4.初步编译。“解决方案管资源理器”下,右击“RTKLIB”项目—》生成,进行编译。因为RTKLIB是在C++ Builder编译器下写的,但是我们现在用VS进行编译,会出现一堆的错误,现在我们就一个个来解决。
5. 错误类型1:error C4996: 'strncpy': This function or variable may beunsafe. Consider using strncpy_s instead. To disable deprecation, use_CRT_SECURE_NO_WARNINGS. See online help for details.
解决:这是由于函数安全性问题,编译器已经给出建议,添加预编译指令【_CRT_SECURE_NO_WARNINGS】。我们打开调试下的项目属性,然后在 配置属性 -> C/C++ -> 预处理器 -> 预处理器定义 -> 编辑。在下面添加上【_CRT_SECURE_NO_WARNINGS】
看到网上有人编译的时候提示类似上面的另一错误,解决方法也类似,同样在预处理器定义-> 编辑。在下面添加上【_WINSOCK_DEPRECATED_NO_WARNINGS】
笔者这里并没有出现,不过为了以防万一,也在“预处理器定义”中添加上此宏。
接下来,我们再次Build工程,继续解决下一错误。
6. 错误类型2: error C2466: 不能分配常量大小为 0 的数组
解决:这个主要是编译器的问题,用g++就没有问题,在vc中定义数组,需要一个常量值。
我们双击此错误,在有问题的数组定义处,按F12,看到数组大小定义的确实是0
同样的,我们像上面一样在“预处理器定义”里面加入【ENAGLO】
接下来,我们再次Build工程,继续解决下一错误。
7. 错误类型3:error C1083: 无法打开包括文件:“rtklib.h”: No such file ordirectory
解决:这是因为rcv里的文件找不到rtklib.h这个头文件。我们继续在项目属性里,在配置属性-> C/C++ 常规-> 附加包含目录-> 编辑 。在上面添加rtklib.h的相对路径(使用相对路径,工程才可以移植到任何地方,否则,换个目录路径,还是会提示此错误)在项目里的src找到rtklib.h并查看它的绝对路径,
要想设置相对路径,我们首先要了解.< \与..\的区别>
.\ 表示项目文件所在目录之下的目录。
..\ 表示项目文件所在目录向上一级目录下的目录。
..\..\ 表示项目文件所在目录向上二级目录之下的目录。
都是针对项目文件所在目录而言,用于定位其它文件的路径位置。
因此,在“附加包含目录”下,添加:【.\src】
接下来,我们再次Build工程,继续解决下一错误。
8. 错误类型4:error C2099: 初始值设定项不是常量
解决:我们双击此错误,定位到如下代码段
初始值设定项不是常量,这是因为c编译器不支持函数外动态声明变量和分配空间,如果要必须是常量值(在数学中,0/0这种形式一般称之为不定式。 [1] 因为计算0/0就是求出一个数,使之与0相乘结果仍得0,显然,其结果可以等于任何数。 [2] “0/0”型的函数极限的结果也没有一般的规律。)故此,我们把这条语句变量定义的初值初始化为【=0.0】
接下来,我们再次Build工程,继续解决下一错误。
9. 错误类型5:error C4703: 使用了可能未初始化的本地指针变量“sbs”
解决:我们双击此错误,定位到如下代码段,做如下修改
接下来,我们再次Build工程,编译后发现又蹦出来20个莫名的错误。
10. 错误类型6:error LNK2019: 无法解析的外部符号 _showmsg,该符号在函数 _convrnx 中被引用
解决:一查showmsg这个函数如果在DLL中需要自己定义,rtklib里面已经做好了处理需要在预处理器里面加入【DLL】即可,我们找到之前预处理器定义处,在下面添加【DLL】,再找到项目属性,在配置属性 -> 链接器 -> 输入 -> 附加依赖项里添加 【winmm.lib】和 【ws2_32.lib】
再次Build工程,没有错误,大功告成。
11.部分警告处理。大多数是什么类型转换上的Warning,暂时没有影响,暂不处理。
12.总结与补充。一般的“变量初始化”等错误,我们都能通过错误列表定位到代码段,进而解决,而添加宏定义、头文件包含路径,容易被开发者忽略。下面总结一下我们在此次编译RTKLIB开源库中,所做的添加宏定义、头文件包含路径等操作。
(1)C/C++下预处理器 里面我们添加了
(配置属性 ->C/C++ -> 预处理器-> 预处理器定义-> 编辑)
_CRT_SECURE_NO_WARNINGS
_WINSOCK_DEPRECATED_NO_WARNINGS
ENAGLO
DLL
(2)C/C++下的附加包含目录 里面我们添加了
(配置属性 ->C/C++ 常规-> 附加包含目录-> 编辑)
【.\src】
(3)连接器下的附加依赖项 里面我们添加了
(配置属性 -> 链接器 -> 输入 -> 附加依赖项)
winmm.lib
ws2_32.lib
通过上面的设置,我们可以发现,我们大多操作的是[C/C++]下的配置选项,而同样,在[VC++]配置选项下,
同样有[包含目录],作用几乎与[C/C++]下面的[附加包含目录]一样,我们首先了解【包含目录、库目录、附加包含目录、附加库目录、附加依赖项区别】
VC++目录:
包含目录:寻找#include
库目录:寻找.lib文件的搜索目录
C/C++:
常规->附加包含目录:寻找#include
链接器:
常规->附加库目录:寻找.lib文件的搜索目录
输入->附加依赖项:lib库(C++的库会把函数、类的声明放在*.h中,实现放在*.cpp或*.cc中。编译之后,*.cpp,*.cc,*.c会被打包成一个.lib文件,这样可以保护源代码)
故,包含目录和附加包含目录(库目录和附加库目录)的区别:
包含目录:修改了系统的include宏的值,是全局的;
附加包含目录:用于当前项目,对其他项目没有影响。
(库目录和附加库目录的区别同上)
进而可知包含目录和附加包含目录(库目录和附加库目录)的区别主要在于全局还是当前,那么当需要对某工程添加这些目录时,通常情况下,都是在附加包含目录和附加库目录中添加的。
补充这些是因为,笔者刚开始编译RTKLIB库时,头文件的包含路径都是添加在[VC++/包含目录]下的,当时惊奇的发现,即使笔者一开始在[VC++/包含目录]下包含的是“绝对路径”,工程也是可以编译通过,并可以移植到任何地方的。但了解到以上以后,建议我们一般使用C/C++和Linker下面的设置,而一般不建议使用VC++下面的设置。