基于vs的RTKLIB2.4.3编译学习历程及经验分享
一
基于VS2008利用rtklib开源代码处理GPS以及北斗数据详解
1、在vs中新建一个控制台项目(含预编译头);
2、在建好的项目中添加rtklib.h以及需要用到的source文件(.c文件),然后将.c改为.cpp并在每个文件首句增加一行#include "stdafx.h",当然在stdafx.h文件中添加#include "rtklib.h"; 这样编译基本可以通过了;
3、一般而言,我们不会需要用到rtklib程序集中的所有文件,只是需要部分。比如我需要用到其中的pntpos这个标准单点定位函数,这个时候就需要添加该函数所在实现文件(pntpos.c)。于是添加了这个文件。但是一般而言,只添加这一个可能不能解决问题,因为rtklib的各个文件之间并不是完全独立,pntpos文件中某些函数可能会调用到其他文件中实现的函数。于是需要再把相关文件添加到工程中。对于这个问题,可以根据自己添加的文件,编译时候的提示,依次添加进来相应文件。这样可以解决fatal error的问题;
4、在添加完成后,可能会遇到一些其他问题。主要问题有:
a.关于某些地方数组越界或为空(主要是glonass),这个时候,查看rtklib.h头文件,可以发现关于系统启用的#define语句,其中除GPS系统外,其他系统数目都是0,解决的办法主要是注释掉这个判断语句。注释掉rtklib.h文件的自第146行后开始的关于启用其他卫星导航系统的语句,如GLONASS:
//#else
//#define MINPRNGLO 0
//#define MAXPRNGLO 0
//#define NSATGLO 0
//#define NSYSGLO 0
//#endif
b.关于很多地方会出现const char*与char*的不能转化问题,这个很简单,在相应地方进行强制转化就行。
c.还存在字符集采用的问题,这个只需要在项目属性中使用多字节字符集即可(见下图)。
d.关于某些函数存在未实现的问题,这个时候,可以网上直接搜索那些没有实现的函数,进行替换,或是直接屏蔽掉。
rtklib中代码的添加工作基本就这些问题,解决后应该就可以使用了
相关文章地址:https://www.cnblogs.com/DKSoft/p/4677381.html
这篇介绍了VS2008平台下创建一个C++控制台程序,编译rtklib源码的过程,创建时含预编译头,另外麻烦的将.c文件都改为.cpp文件,还取巧通过注释程序的方法避免一些问题,总的来说没有针对性解决问题,借鉴意义不大。
二
学习rtklib时,作者在manual中说过,可以用gcc重建CUI程序。因此选择Visual Studio来重建,VS比C++ Builder好用,且网上的资源也多得多。
1. 首先在vs2013中新建一个win32的dll项目,取消预编译头。,将rtklib下src源码放到工程路径***意不是工程组所在路径),如E:\Projects\rtklib-rebuild\src
2. 在项目上添加一个文件夹rtklibsrc然后在文件夹上【添加现有项目】把src中另外的源文件下文件加入工程。在rtklib-rebuild下建立rcv文件夹把原src\rcv下面的文件也添加到工程。创建的工程如下图所示:
然后我们尝试执行编译。
首次编译会与很多错误,我第一次编译时提示了大量的:无法打开包括文件:“rtklib.h”的错误,这是因为没有将rtklib.h头文件的目录包含在项目里的缘故。
解决思路就是将以下路径加入到include目录下(在项目上右键->配置属性->VC++目录->包含目录Include Path选择rtklib.h所在的目录即可)。
再次编译,上述错误不再提示。但还存在以下三个错误:
首先是一个这样的错误:
error C2466: 不能分配常量大小为0的数组 x:\xxxx\rtklibhelper\rtklibsrc\rinex.c
这个错误我们来看一下, 在[MAXPRNGLO]上面按F12,看他的定义处。
这个常量为0,那么不能进行编译,我们需要在预编译器里面加入 ENAGLO
网上的教程是:项目上右键->c/c++ ->预处理器->添加ENAGLO。
再次编译发现上述问题确实消失了。但是会出现以下新的问题。。。。。。
error LNK2019: 无法解析的外部符号 _settspan,该符号在函数 _postpos 中被引用 x:\xxxx\ xxxx \postpos.obj
error LNK2019: 无法解析的外部符号 _showmsg,该符号在函数 _convrnx 中被引用 x:\xxxx\ xxxx \convrnx.obj
error LNK2019: 无法解析的外部符号 [email protected],该符号在函数_tickget 中被引用 x:\xxxx\ xxxx \rtkcmn.obj
error LNK2019: 无法解析的外部符号 [email protected],该符号在函数 _accept_nb 中被引用 x:\xxxx\ xxxx \stream.obj
解决思路可通过在添加附加库[连接器->输入->附加依赖库]添加winmm.lib, ws2_32.lib ;在预处理器里面加入DLL,来解决此问题。
最终其预编译器定义中应包含:
Win32
_CRT_SECURE_NO_WARNINGS
_WINSOCK_DEPRECATED_NO_WARNINGS
ENAGLO
DLL
但我尝试上述方法问题并没有得到解决。。。。。。
另有网友指出:
1)预编译器定义的问题。
RTKLIB中为了适应不同的应用需求,定义了诸多的宏,常见的包括ENAGLO,ENACMP ,_CRT_SECURE_NO_WARNINGS,_WINSOCK_DEPRECATED_NO_WARNINGS等。这些预编译器定义存在的根本目的是为了控制代码编译的走向,以确保功能的实现,为此我们需要详细了解各个宏定义的存在的目的。例如,_CRT_SECURE_NO_WARNINGS存在的目的是确保strcmp等VS认为不安全的函数的有效执行。_WINSOCK_DEPRECATED_NO_WARNINGS存在的目的是确保通信sock函数的有效执行。ENAGLO,ENACMP,WIN_DLL,TRACE存在的目的是确保与GLONASS、与北斗、与windows动态库编译、与TRACE信息输出相关的函数的有效执行。通过合理的预编译器定义,我们可以得到不同功能的RTKLIB动态库。
(2)附加依赖项的问题
前一个作者为了规避"找不到error LNK2019: 无法解析的外部符号…"的问题,选择了在添加附加库[连接器->输入->附加依赖库]中添加winmm.lib, ws2_32.lib。虽然可以解决问题,但是不够直观,很容易被其他开发人员忽略。此作者认为直接在头文件代码中添加#pragma comment(lib, "winmm.lib")和#pragma comment(lib, "ws2_32.lib")更为直观明了,一劳永逸。
但我尝试之后还是不行。。。。。。
https://www.cnblogs.com/DKSoft/p/4677381.html
这篇文章目的在于将rtklib中的源码封装为dll用来供其他C程序调用,不知道与我们创建c++控制台项目或者其他win32项目的结果有什么区别。按照几篇相似文章的介绍,error LNK2019: 无法解析的外部符号问题一直无法解决,可能是我没有创建dll项目的原因。
上述调试都是基于将rtklib源码封装为一个动态库供调用。无问题后有作者基于这个库写了段小的调用程序:
在完成了RTKLIB动态库编译后,利用动态库可构建简单的RTCM数据读取的Demo。Demo程序读取保存的实时RTCM观测信息并输出观测值文件、导航文件以及TRACE日志信息。Demo代码如下:
1 #include "rtklib.h"
2 int main()
3 {
4 char ifile[1024] = "C:xxx.rtcm";
5 char ofile[9][1024] = { "", "", "", "", "", "", "", "" };
6 char *pofile[9] = { "" };
7 char tracefile[1024] = "";
8 int format = STRFMT_RTCM3;
9
10 double ep_rtcm[6] = { 2017, 9, 4, 14, 00, 00 };
11 gtime_t time_rtcm = epoch2time(ep_rtcm);
12
13 rnxopt_t opt = { 0 };
14 opt.tint = 0.00;
15 opt.tunit = 86400;
16 opt.rnxver = 3.02;
17 opt.navsys = 37;
18 opt.obstype = OBSTYPE_ALL;
19 opt.freqtype = 0x03;
20 opt.scanobs = 1;
21 opt.outiono = 1;
22 opt.outtime = 1;
23 opt.trtcm = time_rtcm;
24
25 strcpy(ofile[0], ifile);
26 strcat(ofile[0], ".obs");
27
28 strcpy(ofile[1], ifile);
29 strcat(ofile[1], ".nav");
30
31 strcpy(tracefile, ifile);
32 strcat(tracefile, ".trace");
33 traceopen(tracefile);
34 tracelevel(5);
35 for (int i = 0; i < 9; i++) {
36 pofile[i] = malloc(sizeof(char) * 1024);
37 memcpy(pofile[i], &ofile[i], 1024);
38 }
39 if (convrnx(format, &opt, ifile, pofile)) printf("\n数据转换成功!\n");
40 traceclose();
41 getchar();
42 return 0;
43 }
在调试Demo过程中,笔者发现RTKLIB库仍存在两个问题:
(1)添加TRACE预编译器定义后,软件运行到scan_obstype函数的trace(3,"scan_obstype: nf=%s, opt=%s\n",nf,opt)语句时会提示内存出错,调试后发现该语句中nf的输出类型出错,应将"%s"更改为"%d",即trace(3,"scan_obstype: nf=%d, opt=%s\n",nf,opt)。这反映了RTKLIB原始编码系统环境与VS IDE的差异,后续应用库文件时需要多加注意。
(2)convrnx函数在执行完成退出动态库回到主函数main时,Debug模式下会弹出"Run-Time Check Failure #2 - Stack around the variable 'opt_' was corrupted."异常。Release模式下不弹出该异常。网络上关于该异常主体上认为时由于内存越界造成的,笔者暂未找出opt_变量越界的原因,后续会做深入研究。该异常的最快捷的解决方案时将"project->配置属性->c/c++->代码生成->基本运行时检查"改为"默认值"即可。
三
RTKLIB编译—形成GNSS定位开源库,RTKLIB开源库有着强大的GPS数据实时和后处理功能,RTKLIB还提供了很多底层的函数,因此我们可以考虑采用直接对源码进行编译输出标准DLL的方式供C调用。便编译平台选择VS,RTKLIB库用的是网上开源的rtklib_2.4.3版本,编译的项目采用“相对路径”,即工程可移植到任何地方,方便以后使用。
RTKLIB的下载地址为:http://www.rtklib.com/
1、新建工程。首先,我们在VS2012下先新建一个win32的dll项目(空项目)
2、复制库文件。把在github上下载的rtklib2.4.3里的 “src文件夹”文件复制到刚刚建立的RTKLIB工程项目文件所在目录下
3、添加库文件。为了与库文件结构保持一致,我们首先在“源文件”文件夹,右击—》添加—》新建筛选器,接下来先添加头文件rtklib.h;然后是源文件—》添加—》现有项(添加除rtklib.h所有源文件,)创建“rcv”子文件夹并添加“src/rcv”目录下的所有源文件。
4、初步编译。“解决方案管资源理器”下,右击“RTKLIB”项目—》生成,进行编译。因为RTKLIB是在C++ Builder编译器下写的,但是我们现在用VS进行编译,会出现一堆的错误,现在我们就一个个来解决。
错误类型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】
错误类型2: error C2466: 不能分配常量大小为 0 的数组
解决:这个主要是编译器的问题,用g++就没有问题,在vc中定义数组,需要一个常量值。
我们双击此错误,在有问题的数组定义处,按F12,看到数组大小定义的确实是0。我们像上面一样在“预处理器定义”里面加入【ENAGLO】可解决此问题。
错误类型3:error C1083: 无法打开包括文件:“rtklib.h”: No such file ordirectory
解决:这是因为rcv里的文件找不到rtklib.h这个头文件。我们继续在项目属性里,在配置属性-> C/C++ 常规-> 附加包含目录-> 编辑。在上面添加rtklib.h的相对路径(使用相对路径,工程才可以移植到任何地方,否则,换个目录路径,还是会提示此错误)在项目里的src找到rtklib.h并查看它的绝对路径,在属性界面下的“附加包含目录”下,添加:【.\src】
错误类型4:error LNK2019: 无法解析的外部符号 _showmsg,该符号在函数 _convrnx 中被引用
解决:一查showmsg这个函数如果在DLL中需要自己定义,rtklib里面已经做好了处理需要在预处理器里面加入【DLL】即可,我们找到之前预处理器定义处,在下面添加【DLL】,再找到项目属性,在配置属性 -> 链接器 -> 输入 -> 附加依赖项里添加【winmm.lib】和 【ws2_32.lib】
部分警告处理。大多数是什么类型转换上的Warning,暂时没有影响,暂不处理
同样按照这篇文章的操作步骤仍然无法成功将rtklib编译为一个dll库文件,问题仍出在:
看了几篇文章试了几种方法这个问题一直无法得到解决。
经过与文章四对比发现,解决这个问题的关键就在于将RTKLIB-rtklib_2.4.3\app\rnx2rtkp文件夹下的rnx2rtkp.c文件放到项目的src文件夹下并加入到工程。再次编译错误就会得到解决。
四
在开始前首先解决库的问题-- pthread.h
我所下载的版本是
配置步骤为:
a、将\pthreads-w32-2-9-1-release\Pre-built.2\lib\x64下的所有文件(pthreadVC2.lib和libpthreadGC2.a)复制到VS2010安装目录下Microsoft Visual Studio 10.0\VC\Lib中
b、将\pthreads-w32-2-9-1-release\Pre-built.2\include\pthread.h复制到Microsoft Visual Studio 10.0\VC\include下
c、将\pthreads-w32-2-9-1-release\Pre-built.2\dll\x64的所有文件(pthreadGC2.dll和pthreadVC2.dll)复制到Microsoft Visual Studio 10.0\VC\Bin下
然后开始创建工程:
1、新建一个空的工程:新建一个visualC++下的空项目
2、添加文件
a)在工程目录下新建文件夹src,拷贝RTKLIB2.4.3下的src下面的所有文件和rcv文件夹到src下面,同时将app\str2str下面的str2str.c拷贝到src下面,作为主程序入口
b)分别添加头文件和源文件到工程中
3、设置项目属性
选择:项目 > 属性。
设置一:配置属性 >链接器 > 调试 >生成调试信息:是
设置二:配置属性 > C/C++ > 常规 > 调试信息格式:C7
编译一下,发现一堆问题,下面一一解决。
1、错误77error C1083: 无法打开包括文件:“rtklib.h”: No such file or directoryd
解决思路这是因为没有将rtklib.h头文件的目录包含在项目里的缘故。
将以下路径加入到include目录下(在项目上右键->配置属性->VC++目录->包含目录Include Path选择rtklib.h所在的目录(src)即可)。
2、错误15error C1083: 无法打开包括文件:“sys/time.h”: No such file or directory sys/time.h
解决思路这是linux下面的gcc里面的库函数,不能在VS中使用;RTKLIB中有大量的这样的库,所以我们要设置预处理WIN32
配置属性 > C/C++预处理器 > 预处理定义:WIN32、_CRT_SECURE_NO_DEPRECATE
3、错误1error C2065: “SIGPIPE”: 未声明的标识符
解决思路找到且定义的位置将其注销掉
4、错误1error C2466: 不能分配常量大小为 0 的数组
解决思路在预编译器里面加入 ENAGLO
5、错误31error LNK2019:无法解析的外部符号 [email protected],该符号在函数 _accept_nb 中被引用
解决思路添加附加库[连接器->输入->附加依赖库]添加winmm.lib, ws2_32.lib
6、错误13error LNK2019: 无法解析的外部符号 _showmsg,该符号在函数 _convrnx 中被引用
解决思路一查showmsg这个函数如果在DLL中需要自己定义,rtklib里面已经做好了处理需要在预处理器里面加入DLL即可
最终预处理器定义内包含:
7、无法将参数 1 从“char [1024]”转换为“LPCWSTR”
选择"项目->属性->配置属性->常规->字符集->未设置",将UNICODE关闭就可以了···
8、无法解析的外部符号 [email protected]
没有加入相应的链接库,添加#pragma comment(lib, "winmm.lib")和#pragma comment(lib, "ws2_32.lib")应该就可以了。
完全按照上述步骤,最后出现了如下新的问题:
不知道是不是rtklib又更新了的缘故,还是什么头文件没有添加???
经思考应该是str2str.c这个文件的问题,因此尝试将此文件移除,并借鉴http://blog.sina.com.cn/s/blog_71d6b76b0102xcg9.html#cmt_5B06A265-79C20602-1854ED4FB-9B2-8C5
这篇文章的说明,将rnx2rtkp.c这个文件添加到项目的源文件中),并将rnx2rtkp.c改名为main.c。(之所以选择这个c文件,是因为rnx2rtkp是rtkpost的CUI版本,主要功能是事后处理,今后调用rtklib中的函数时可以将main函数中原有内容注释掉,另写其他函数。如可简单写个坐标转换的功能,调用了添加进来的c文件中的函数,结果运行成功
double BLH[3] = {45.0*D2R, 123.0*D2R ,900.0 };
double XYZ[3] = {0.0, 0.0, 0.0};
pos2ecef(BLH, XYZ);
printf(" X=%f\n Y=%f\n Z=%f\n",XYZ[0],XYZ[1],XYZ[2]);
getch();
http://blog.sciencenet.cn/home.php?mod=space&uid=858128&do=blog&id=990823
http://blog.sina.com.cn/s/blog_71d6b76b0102xcg9.html#cmt_5B06A265-79C20602-1854ED4FB-9B2-8C5
这两篇文章大致相同,较为完整的整合了rtklib编译过程中可能会遇到的所有问题,并且所创建项目也是VC++的空项目,可以作为参考进行借鉴。
总结与补充。
一般的“变量初始化”等错误,我们都能通过错误列表定位到代码段,进而解决,而添加宏定义、头文件包含路径,容易被开发者忽略。下面总结一下在编译RTKLIB开源库中,所做的添加宏定义、头文件包含路径等操作。
(1)C/C++下预处理器里面我们添加了
(配置属性 ->C/C++ -> 预处理器-> 预处理器定义-> 编辑)
_CRT_SECURE_NO_WARNINGS
_WINSOCK_DEPRECATED_NO_WARNINGS
ENAGLO
DLL
WIN32
(2)C/C++下的附加包含目录 里面我们添加了
(配置属性 ->C/C++ 常规-> 附加包含目录-> 编辑)
【.\src】
(3)连接器下的附加依赖项 里面我们添加了
(配置属性 -> 链接器 -> 输入 -> 附加依赖项)
winmm.lib
ws2_32.lib
通过上面的设置,我们可以发现,我们大多操作的是[C/C++]下的配置选项,而同样,在[VC++]配置选项下,同样有[包含目录],作用几乎与[C/C++]下面的[附加包含目录]一样,我们首先了解【包含目录、库目录、附加包含目录、附加库目录、附加依赖项区别】
VC++目录:
包含目录:寻找#include<xxxx.h>中的xxxx.h的搜索目录
库目录:寻找.lib文件的搜索目录
C/C++:
常规->附加包含目录:寻找#include<xxxx.h>中的xxxx.h的搜索目录(每一项对应一个文件夹XXXX,文件夹中包含了编译时所需的头文件,使用时直接#include<XXXX>即可)
链接器:
常规->附加库目录:寻找.lib文件的搜索目录
输入->附加依赖项:lib库(C++的库会把函数、类的声明放在*.h中,实现放在*.cpp或*.c中。编译之后,*.cpp,*.c会被打包成一个.lib文件,这样可以保护源代码)
故,包含目录和附加包含目录(库目录和附加库目录)的区别:
包含目录:修改了系统的include宏的值,是全局的;
附加包含目录:用于当前项目,对其他项目没有影响。
(库目录和附加库目录的区别同上)
进而可知包含目录和附加包含目录(库目录和附加库目录)的区别主要在于全局还是当前,那么当需要对某工程添加这些目录时,通常情况下,都是在附加包含目录和附加库目录中添加的。
补充这些是因为,笔者刚开始编译RTKLIB库时,头文件的包含路径都是添加在[VC++/包含目录]下的,当时惊奇的发现,即使笔者一开始在[VC++/包含目录]下包含的是“绝对路径”,工程也是可以编译通过,并可以移植到任何地方的。但了解到以上以后,建议我们一般使用C/C++和Linker下面的设置,而一般不建议使用VC++下面的设置。