本文实现了在windows平台下使用Microsoft Visual Studio Community 2022 (64 位)构建MB-system的I/O模块项目,用于将国产声呐数据添加到MB-system的数据支持中去。
1:为啥要搞个Visual Studio的项目?
因为可以调试啊!!
起因是这样的,因为项目需要,针对某个国产的新型声呐传感器硬件开发处理软件,由于国产声呐数据的存储格式的特殊性,现有的商业多波束声呐处理软件是无法直接读取的,包括CARIS、Geocap、Trition、Echoview等都试过了,最终还是继续使用开源的MB-system。
MB-system是国内声呐研究常用的平台,标准C语言实现,代码可直接用于工程软件开发,在github上百星,让人吃惊的是这个项目目前2024年了,持续更新的频率还挺高,活力项目。
网址:https://github.com/dwcaress/MB-System
https://www.mbari.org/technology/mb-system/
但令人dan疼的是,github上的MB-system软件建构仍然是CMake的。。。(虽然比更早的GNU Autotools)要通用点,但目前只用CMake构建这么大一个项目,太原始了。所以MB-system目前仍然只能在Linux系统上运行,且项目开发写代码主要靠文本编辑器与控制台输出,这让习惯了使用Visual Studio、VS code、Qt等平台开发的人员很难受。
有人尝试过MB-Sytem代码调试: MB-Sytem代码调试_mbsystem-CSDN博客
全靠控制台输出啊,MB-Sytem源码感觉有三分一的代码都是fprintf控制台输出。
能否使用makefile文件反向生成vc项目在windows下使用?简单尝试了一下发现很难,因为MB-system源码需要修改的地方太多。
最终决定只将MB-system中的MBio模块单独构建VC项目,用于开发新的国产声呐数据I/O模块,实践证明还是挺顺利的。
还是感慨与佩服老教授使用文本编辑器写MB-system标准c代码的水平,一把过。我不行。
2:使用MB-system中的MBio模块新建Visual Studio项目
用到的源码主要在MB-System-master\src\mbio文件夹下,但并不是全部,排除MB-system支持的数据类型,只保留官方说明文档的例子mbr_wasspenl,文件不算多。
调试用的文件在MB-System-master\src\utilities文件夹下,选择mbinfo.cc与mblist.cc两个就够了,包含main()函数用于调试。
打开Microsoft Visual Studio Community 2022 (64 位)新建标准C++的空项目,手动添加下图中的文件。
项目配置很少,在“项目属性”--“C/C++”--”预处理器定义”中添加“_CRT_SECURE_NO_WARNINGS”。屏蔽一些警告报错。
修改代码:
首先修改mb_format.h与mb_format.c文件中涉及多源数据格式支持的内容,都注释掉,只保留了官方文档中的wasspenl样例。mb_format.h中注释掉的包括:
/* format registration function prototypes */
//int mbr_register_sbsiomrg(int verbose, void *mbio_ptr, int *error);
….
//int mbr_info_sbsiomrg(int verbose, int *system, int *beams_bath_max, int *beams_amp_max, int *pixels_ss_max, char *format_name,
// char *system_name, char *format_description, int *numfile, int *filetype, int *variable_beams,
// int *traveltime, int *beam_flagging, int *platform_source, int *nav_source, int *sensordepth_source,
// int *heading_source, int *attitude_source, int *svp_source, double *beamwidth_xtrack,
// double *beamwidth_ltrack, int *error);
….
mb_format.c中注释掉的包括:
/* look for a corresponding format */
// if (*format == MBF_SBSIOMRG) {
……
/* look for a corresponding format */
// if (*format == MBF_SBSIOMRG) {
……
然后修改一些linux下才有的头文件,使之兼容windows,比如:
#ifdef __linux__
#include <unistd.h>
#include <dirent.h>
#endif
#ifdef _WIN64
#include <direct.h>
#include <io.h>
#endif
其中有个linux下负责命令行调用参数检测的头文件getopt.h,这个windows上有替代文件。
VS2019使用getopt及getopt_long_vs添加getopt-CSDN博客
VS2019使用getopt及getopt_long_vs添加getopt-CSDN博客
下载getopt.h与getopt.c两个文件添加到项目中,然后将#include <getopt.h>修改为#include “getopt.h”。
用到调用外部第三方I/O库读取某些特殊格式文件,如XDR、SURF等,相关的代码段注释掉就行了,因为用不到,我这里给加上了麻烦点。投影相关的Proj我给去掉了,写IO我用不到投影转换,全部经纬度。Proj4库加上也不复杂。
剩余的的代码修改就很简单了,标准C的代码支持Windows容易。
控制台入口main函数使用mbinfo.cc或mblist.cc中的入口就行了,二选一,用一个main函数,剩下的一个可以先注释掉。成功生成解决方案后就可以命令行调用了,比如mbinfo的命令行参数:mbinfo -F 286 -I "D:\\work2024\\声纳图像\\多波束目标数据\\240\\广西北海_空D\\Target_19.hrms" -V 3
运行结果见下图:
Mbinfo函数调用了关键的mb_*_alloc()、mb_*_extract()函数。至此, 便可以在windows下用Vs工具写c代码加断点调试了。
剩下的工作就是对照官方文档写I/O函数。
3:使用HuMBsystem写I/O模块
下面介绍在MBsystem中添加新的声呐数据I/O支持代码。
先读MB-system的官方文档《Developer's Guide to Coding an MBSystem I/O Module》。官方文档中的范例新建一个I/O模块处理WASSP多波束声纳数据。
实际编程中更详细的函数接口说明在MB-system的源码目录下:MB-System-master\src\mbio\README.md。查询这个文档搞清楚关键mb_*_extract()函数的输出参数定义更重要。
网上也有人做过相同的工作,也很有启发:如何在MB-System中新建I/O模块:
如何在MB-System中新建I/O模块_mbsystem-CSDN博客
下面是自己的工作,同样按照官方文档的章节对应,作为备忘。
STEP 1 设立声纳系统名称、数据格式及ID
声纳系统的名称MB_SYS_HUHRMS
数据格式的名称限定8个字符:MBF_HUHRMS86 286
数据格式拥有唯一的ID,例如:#define MBF_HUHRMS86 286;
STEP 2 使用源码的模板构建新模块
1.拷贝mbsys_templatesystem.h,重命名为:mbsys_huhrms.h;
拷贝mbsys_templatesystem.c,重命名为:mbsys_huhrms.c;
拷贝mbr_tempform.c,重命名为:mbr_huhrms86.c;
2.在以上文件中进行全局替换:
templatesystem-> huhrms
TEMPLATESYSTEM-> HUHRMS
tempform-> huhrms86
TEMPFORM-> HUHRMS86
我们将在以上三个文件中处理未知格式声纳文件。
STEP 3 定义存储新声纳数据结构体
1.在mbsys_benthos.h中添加记录声纳数据的结构体: 这个结构体主要根据自己如何读取与存储声呐数据方便来决定,不用太考虑官方模板给的形式。因为最终MBsystem提取声呐数据有专门的函数接口mb_*_extract(),通过MBsystem定义的传参提取数据,而不是这里自己定义的结构体。
2.虽然MBIO的结构体的通用项:int kind、double time_d、int time_i[7],MBIO模块中的函数mb_get_time()、mb_get_date()等与此相关联,但只要保证mb_*_extract()之类的函数输出参数正确即可,不必完全按照模板写,实际调用不到的。
3. 虽然定义了多种数据类型:
MB_DATA_DATA--Survey data
MB_DATA_NAV--Navigation data
MB_DATA_COMMENT--Comment string
但目的也只是为了代码的整齐性,实际按照模板的例子对每种数据类型单独写数据IO是非常麻烦的,关键是mb_*_extract()之类的函数输出参数是不区分这几种类型的!所以建议不用区分,我把所有IO都写在MB_DATA_DATA类型里。然后在mbr_info_huhrms86()函数中将所有数据源都指向MB_DATA_DATA类型:
*nav_source = MB_DATA_DATA; // nav:导航
*sensordepth_source = MB_DATA_DATA;
*heading_source = MB_DATA_DATA;
*attitude_source = MB_DATA_DATA; //attitude:姿态
STEP 4 (a) mbr_xxx.c的初始化工作
1.初始化:
Mbr_register_xxx()、mbr_info_xxx()、mbr_dem_xxx()直接关联mb_read_init()及mb_write_init(); Mbr_dem_xxx()直接关联mb_close();
2.mbr_registem_xxx()注册管理指针,使得mbr_xxx.c及mbsys_xxx.c中的各模块能由MBIO中更高级的I/O模块调用;
3.mbr_xxx.c中主要处理数据的函数为:mbr_rt_xxx()(读)、mbr_wt_xxx()(写);
4.读写声呐文件过程中 init()与close()只在打开文件与关闭文件两端运行一次、而mbr_rt_xxx()(读)、mbr_wt_xxx()(写)两函数按照每个Ping进行一次读写,有多少Ping便调用多少次,直到读取结束,所以需要注意:在mbr_info_xxx()函数中为构建的结构体进行初始化赋值,在mbr_rt_xxx()(读)、mbr_wt_xxx()(写)就不要对结构体malloc了,如果非要申请内存,也要在函数退出前释放掉。
每个Ping的读写函数返回值注意坑:例如mbr_huhrms86_rd_data(),只有在文件最后一个Ping读取结束了才能返回status = MB_FAILURE;其它情况不管是读取成功还是中间某个Ping数据存在问题,都要返回status = MB_SUCCESS;因为只要返回MB_FAILURE了系统就停止继续读Ping了。
5.文件访问方式:
①fopen()、fread()、fwrite()、fclose();
②(官方推荐)mb_fileio_open()、mb_fileio_read()、mb_fileio_write()、mb_fileio_close()
但是我推荐还是使用①,因为官方的②只是将函数返回值变得复杂了,方便控制台输出调试用,其它优化是没有的,函数内部还是①的函数包装。。。用断点跟到函数内部看了一眼,还是直接调用①更省事,都可以用VS加断点调试了,还要用比fread()更啰嗦的函数输出专门的控制台调试信息???所以我写的很简单:
STEP 4(b)mbr_xxx.c中实现读写函数
一、1、主要的函数为mbr_rt_xxx()和mbr_wr_xxx()
mbio_ptr:指向通过MBIO定义的读/写文件的mb_io structure
store_ptr:指向primary data structure(通常在mb_io structure中获得);
官方文档中,通过mbio_ptr与store_ptr指针对象记录所有的变量,希望用户在函数内部就不要再额外的定义变量与申请内存了,想法很好,但看看simrad等传感器的代码,老外为了方便也不是贯彻的很严格。其实只用mbio_ptr就足够了,还是同样的原因:关键的mbsys_xxx_extract()函数输出参数并不是mbio_ptr对象,而是具体的数据对象,下面详细说明。
STEP 5 在mbsys_xxx.c中构建数据接口
实现mbr_register_xxx()中注册的用于mbio_struct的接口,关键函数:mbsys_xxx_extract(),数据提取。直接贴官方说明最合适:
#### mb\_extract()
int mb_extract(
int verbose,
char *mbio_ptr,
char *store_ptr,
int *kind,
int time_i[7],
double *time_d,
double *navlon,
double *navlat,
double *speed,
double *heading,
int *nbath,
int *namp,
int *nss,
char *beamflag,
double *bath,
double *amp,
double *bathacrosstrack,
double *bathalongtrack,
double *ss,
double *ssacrosstrack,
double *ssalongtrack,
char *comment,
int *error);
The function mb\_extract extracts sonar data from the structure pointed
to by \*store\_ptr according to the MBIO descriptor pointed to by
mbio\_ptr. The verbose value controls the standard error output ver-
bosity of the function. The form of the data structure is determined
by the sonar system associated with the format of the data being read.
A number of different data record types are recognized by MB-System;
the kind value indicates which type of record is stored in \*store\_ptr.
Additional data is returned if the data record is survey data (naviga-
tion, bathymetry, amplitude, and sidescan), navigation data (navigation
only), or comment data (comment only).
The return values are:
* kind: kind of data record read
* 1 survey data
* 2 comment
* 12 navigation
* time\_i: time of current ping
* time\_i[0]: year
* time\_i[1]: month
* time\_i[2]: day
* time\_i[3]: hour
* time\_i[4]: minute
* time\_i[5]: second
* time\_i[6]: microsecond
* time\_d: time of current ping in seconds since 1/1/70 00:00:00
* navlon: longitude
* navlat: latitude
* speed: ship speed in km/s
* heading: ship heading in degrees
* distance: distance along shiptrack since last ping in km
* altitude: altitude of sonar above seafloor in m
* sonardepth: depth of sonar in m
* nbath: number of bathymetry values
* namp: number of amplitude values
* nss: number of sidescan values
* beamflag: array of bathymetry flags
* bath: array of bathymetry values in meters
* amp: array of amplitude values in unknown units
* bathacrosstrack: array of of acrosstrack distances in meters corresponding to bathymetry
* bathalongtrack: array of of alongtrack distances in meters corresponding to bathymetry
* ss: array of sidescan values in unknown units
* ssacrosstrack: array of of acrosstrack distances in meters corresponding to sidescan
* ssacrosstrack: array of of alongtrack distances in meters corresponding to sidescan
* comment: comment string
* error: error value
A status value indicating success or failure is returned; the error
value argument error passes more detailed information about extract
failures.
确保每个输出参数的正确定义,就能保证导入数据的正确,如果读取的原始数据与给出的输出参数定义不一致 ,就需要在函数内部完成转换与计算。例如:
模板中的函数接口根据需要完成,并不一定要写完全部,最终实现的函数接口包括:
STEP 6 将新构建的I/O模块集成与MBIO中:
1.信息I/O模块构建完成后,应在以下文件中修改信息:mb_format.h、mb_format.c,以将新的模块导入MBIO库中;
2.声纳名称定义于mb_format.h中,如: #define MB_SYS_HUHRMS 41
3.声纳数据格式及其ID定义于mb_format.h中,如:#define MBF_HUHRMS86 286
函数mbr_register_xxx()及mbr_info_xxx()在mb_format.h中定义;
int mbr_register_huhrms86(); //akaishi
int mbr_info_huhrms86();//akaishi
5.新格式的引用必须要在mb_format.c中的mb_format_register()、mb_format_info()、mb_get_format()中进行声明,添加的部分比着其它数据格式复制粘贴即可。(添加的部分也是之前构建vs项目时mb_format.h与mb_format.c文件中注释掉的部分)
至此新添加的I/O支持代码完成,使用mbinfo.cc中的main函数加断点调试吧,Microsoft Visual Studio太好用了。在windows平台上运行正确的话,再拿到linux系统上更新MB-system就没问题了。
更新完的I/O包括五个文件夹:
将以上5个文件拷贝到MBsystem源码安装的linux系统源码下的MB-System-master\src\mbio文件夹下重新构建安装就能生效了。以下介绍linux端的操作。
PS:注意拷贝到linux下的mb_format.h、mb_format.c两个文件中之前注释掉的其它格式注册代码需要先恢复,否则其它格式就失效了。
STEP 7 更新MB-System的编译配置文件
在mbsystem/src/mbio/Makefile.am中声明新模块
添加:include_HEADERS += mbsys_huhrms.h
libmbio_la_SOURCES += mbr_huhrms86.c
libmbio_la_SOURCES += mbsys_huhrms.c 2、重新编译。
重新编译:
重新编译前需要更新整个项目的makefile.in文件:
cd MB-System-5.8.1
make uninstall
make clean
libtoolize --force --copy
aclocal
如果提示版本太低,需要更新:
curl -O http://mirrors.kernel.org/gnu/autoconf/autoconf-2.71.tar.gz
cd到解压路径下:./configure 然后 make 然后 make install 检查版本
autoheader
automake --add-missing --include-deps
autoconf
autoupdate
autoreconf --force --install --warnings=all
此时项目的makefile 与 makefile.in文件都已经自动更新完毕,可以正常重新编译了
./configure --enable-mbtrn --enable-mbtnav
export LD_LIBRARY_PATH=/usr/local/lib:$LD_LIBRARY_PATH
LDFLAGS="-Wl,-rpath -Wl,/usr/local/lib" \
./configure --enable-mbtrn --enable-mbtnav
make
sudo make install
MBsystem支持新格式见下图:
最后遇到的坑:
在windows上运行没问题的I/O代码,集成到linux系统后读入数据全是错的,只能使用命令行参数-V 5的方式查看控制台输出,发现了大坑是long类型在linux64占8位,而在windows下占4位。
所以不要单独使用long,要使用long long,或者只用int。
参考资料:
[1]mbcookbook.pdf https://www.mbari.org/wp-content/uploads/2016/03/mbcookbook.pdf
[2] https://github.com/dwcaress/MB-System
[3]Mb-system的安装: Mb-system的安装_mbsystem-CSDN博客
[4]如何在MB-System中新建I/O模块:如何在MB-System中新建I/O模块_mbsystem-CSDN博客
[5] MB-Sytem代码调试: MB-Sytem代码调试_mbsystem-CSDN博客