核心任务
我们想在PC机上串口调试助手相关的功能,进行复现,因为现在的串口调试助手exe是一个可执行文件,实际上,我们需要编写一个程序,处理雷达回来的数据,那首先就是先把这些目标先把它发给我的数据解析出来。从串口的这个界面上去抓取数据也可以。
个人理解:需要编写一个c或者MATLAB程序,把接收到的Character等信息,通过该程序解析其所包含的信息。因为其发送的数据一般是ASCII格式,不易阅读,我们需要在接收其信息的基础上,把其中含有的信息解析出来。
介绍
IEC161162是国际电工协会制定的海上通信标准协议。
数字接口由5部分组成:
- 单讲器和多听器
- 单讲器和多听器,高速传输
- 多对讲机和多听机,串行数据仪表网络
- 多对讲机和多听机,以太网互连
- 多对讲机和多听机,以太网互连,安全保障
问题1:single talker and multiple listeners是什么?
我个人理解就是单发送,多接收。
数据传输格式
D0-D7是数据位,数据位之前还有一个起始位,最后一位是停止位。
问题1:数据的发送命令是什么?
7.1 Character
General
所有传输的数据应被解释为ASCII字符。8位字符的最高有效位应始终作为0 (D7 = 0)传输。
Reserved characters
8.1 Table 1
翻译如下,虽然知道了名字,但是还是不理解其具体作用
Vaild characters(有效字符)
有效的字符集由所有可打印的ASCII字符(HEX 20到HEX 7E)组成,除了那些定义为保留字符的字符。有效字符集的列表见8.1(表2)
8.1 Table 2
对应的就是通常所说的ASCII码表
Undefined characters(未定义字符)
未指定为“保留字符”或“有效字符”的ASCII值被排除在外,并且在任何时候都不得传输.
但是有必要传输在Table1中但是不在Table2中的数据时,需要用三位character传输。
举个例子:
- 若要发送ASCII “127.5°”,则要发送"“127.5 ^F8"。(F8为什么代表°,没搞懂)
- 若要发送ReservedCharacters< CR >< LF >,则发送"^0D ^0A"。(根据8.1Table1得)
- 若要发送ReservedCharacters"^“,则发送” ^5E"。(根据8.1Table1得)
Character symbols
当本标准中使用单个字符来定义计量单位、表示数据字段类型、句子类型等时,应按8.1(表3)中的字符符号进行解释。我的理解是,用一个字母,大小写,来代表不同的含义。
8.1 Table 3
7.2 Fields
String
字段由位于两个适当分隔符之间的有效字符字符串或无字符(空字段)组成。
Address field
General
地址字段是句子中的第一个字段,在“$”或“!”之后。“分隔符。它用来定义句子。他说到,分隔符的详细描述在7.3.3中。
这些句子被称作Parametric sentences(参数句),这些句子以" $ "为开头,句子结构具有分隔和定义的数据字段,是传递信息的首选方法。
下面是Parametric sentences的具体规则:
- 句子以“$”分隔符开始
- 只允许使用经过批准的句子格式化程序。特殊用途封装语句使用的格式化程序不能重用
- 只允许使用有效字符
- 只允许批准的字段类型
- 数据字段(参数)是单独分隔的,其内容由该标准标识并经常详细描述
- 不允许封装的非分隔数据字段
Approved address field(批准的地址字段)
批准的地址字段由本标准定义的五位数字和大写字母字符组成。前两个字符是话务员标识符,列在8.2(表4)中。话务员标识符用于定义正在传输的数据的性质。
具有从多个源传输数据能力的设备应传输适当的对讲机标识符。这里有一个例子,关于GPS和LORAN-C的例子,目前还不是很懂,需要去查阅相关资料。
for example a device with both a GPS receiver and a LORAN-C
receiver shall transmit GP when the position is GPS-based, LC when the position is LORANC-based, and IN for integrated navigation shall be used if lines of position from LORAN-C
and GPS are combined into a position fix
能够从其他来源重新传输数据的设备应使用适当的标识符。
例子
for example GPS receivers transmitting heading data shall not transmit $GPHCD unless the
compass heading is actually derived from the GPS signals
Approved Sentence批准的语句
句子结构
{开始和发送者的ID}-{字段描述}
TTM
TTM:Tracked target message跟踪目标消息
关于RATTM的RA暂时不清楚,先按LORAN处理。
$TTM,(1),(2),(3),(4),(5),(6),(7),(8),(9),(10),(11),(12),(13),(14),(15)h< CR >< LF >
位置 | 代表信息 |
---|---|
(1) | 目标编号(0-999) |
(2) | 目标距离自己的船的距离(NM海里,1海里=1.852公里(千米) ) |
(3)(4) | 本船方位(单位,度。T代表绝对位置,R代表相对位置) |
(5) | 目标速度 |
(6)(7) | 目标航向(单位,度。T代表绝对位置,R代表相对位置) |
(8) | 到最近会遇点的距离 |
(9) | 到最近会遇点的时间(单位min) |
(10) | 速度/距离单位(K/N/S,K =公里/小时;N=节;S =法定英里/小时) |
(11) | 目标名称 |
(12) | 目标状态 (见注1) |
(13) | 参考目标=R(见注2) |
(14) | 数据获取时间(UTC格式hhmmss.ss) |
(15) | 获取的类型(A=自动的,M=手动的,R=报告的) |
相对位置指的是,正北到船舶航向线的角度。
目标状态:
注1:
L=丢失,跟踪目标已经失去
Q =查询,在获取进程中的目标
T=正在跟踪中
注2:参考目标:如果目标是用于确定本船舶位置或速度的参考设置为“R”,否则为空。
关于绝对位置和相对位置:
- 绝对方位,也被称为真方向或绝对方位,是描述一个物体相对于地球表面的位置的方位角。在航海中,真方位是从真北顺时针量到物标的方位角(0-360度)。
- 相对方位,是描述一个物体相对于其他物体的方位。在航海中,相对方位是从船首量至物标的方位角(左右0-180度)。
例子:
$RATTM,37,0.3,345.6,T,0.1,121.1,T,0.2,100.0,N,TGT37,Q,T,111521.70,M*1B
描述 | 数值 |
---|---|
编号 | 37 |
目标距离我们多少海里 | 0.3海里 |
本船的方位 | 345.6度 |
是相对位置还是绝对位置 | 绝对位置 |
目标速度 | 0.1节 |
目标航向 | 121.1度 |
是相对位置还是绝对位置 | 绝对位置 |
到最近会遇点的距离 | 0.2海里 |
到最近会遇点的时间 | 100min |
船的速度单位 | 节 |
目标名称 | TGT37 |
目标状态 | Q查询 |
参考目标 | 为T(见注2) |
数据获取时间 | 11时15分21.70 |
获取类型 | M手动型 |
使用c语言进行解码操作
*也叫做取址符
#include <stdio.h>
#include <string.h>
#define MAX_LENGTH 1000 // 用于存储输入字符串的最大长度
#define MAX_TOKENS 16 // 假设最多有 20 个子字符串
int splitString(const char* str, char* tokens[], const char* delimiter)
{
int count = 0;
char* ptr;
ptr = strtok(str, delimiter);
while (ptr != NULL && count < MAX_TOKENS)
{
tokens[count++] = ptr;
ptr = strtok(NULL, delimiter);
}
return count;
}
int main() {
//对指令中几个判断量进行定义
char located[20];//本船方位(相对or绝对)
char towards[20];//目标航向 (相对or绝对)
char states[20];//目标状态
char TFA[20];//获取的数据类型
char* TFA_temp;
char speedUnit[20];
char data[MAX_LENGTH]; // 用于存储输入的字符串
printf("请输入要解析的指令: ");
fgets(data, MAX_LENGTH, stdin); // C 库函数 char *fgets(char *str, int n, FILE *stream) 从指定的流 stream 读取一行,并把它存储在 str 所指向的字符串内。当读取 (n-1) 个字符时,或者读取到换行符时,或者到达文件末尾时,它会停止
// 如果使用fgets读取了换行符,则需要将其替换为字符串结束符
char* newline = strchr(data, '\n');//在的data中查找换行符
if (newline != NULL) {
*newline = '\0';// '\0'是判定字符数组结束的标识
}
char* tokens[MAX_TOKENS]; // 存储子字符串的数组
int count; // 子字符串数量
// 调用分隔字符串的函数
count = splitString(data, tokens, ",");
// 打印数组中存储的子字符串
for (int i = 0; i < count; i++) {
printf("Token %d: %s\n", i + 1, tokens[i]);
}
// 根据 tokens 数组的内容设置 located 和 speed 的值
//strcmp把 str1 所指向的字符串和 str2 所指向的字符串进行比较
if (strcmp(tokens[4], "T") == 0)
{
strcpy(located, "本船方位:绝对位置");
}
else if (strcmp(tokens[4], "R") == 0) {
strcpy(located, "本船方位:相对位置");
}
if (strcmp(tokens[7], "T") == 0)
{
strcpy(towards, "目标航向:绝对位置");
}
else if (strcmp(tokens[7], "R") == 0) {
strcpy(towards, "目标航向:相对位置");
}
if (strcmp(tokens[12], "T") == 0)
{
strcpy(states, "目标状态:正在跟踪中");
}
else if (strcmp(tokens[12], "Q") == 0) {
strcpy(states, "目标状态:查询中");
}
else if (strcmp(tokens[12], "L") == 0) {
strcpy(states, "目标状态:丢失");
}
if (strcmp(tokens[10], "K") == 0)
{
strcpy(speedUnit, "速度单位:公里/小时");
}
else if (strcmp(tokens[10], "N") == 0) {
strcpy(speedUnit, "速度单位:节");
}
else if (strcmp(tokens[10], "S") == 0) {
strcpy(speedUnit, "速度单位:法定英里/小时");
}
TFA_temp = strtok(tokens[15], "*");
if (strcmp(strtok(TFA_temp,"*"), "A") == 0)
{
strcpy(TFA, "获取类型:自动");
}
else if (strcmp(strtok(TFA_temp, "*"), "M") == 0) {
strcpy(TFA, "获取类型:手动");
}
else if (strcmp(strtok(TFA_temp, "*"), "R") == 0) {
strcpy(TFA, "获取类型:报告");
}
printf("发送格式:%s\n船舶的编号:%s\n目标距离本船的距离:%s海里\n本船方位:%s度\n%s\n目标速度:%s节\n目标航向:%s度\n%s\n距离最近会遇点:%s海里\n到最近会遇点的时间:%smin\n%s\n目标名称:%s\n%s\n数据获取时间:%s\n%s\n",
tokens[0],tokens[1],tokens[2],tokens[3],located,tokens[5],tokens[6],towards,tokens[8],tokens[9],speedUnit,tokens[11],states,tokens[14],TFA);
return 0;
}
运行此代码输入$RATTM,37,0.3,345.6,T,0.1,121.1,T,0.2,100.0,N,TGT37,Q,T,111521.70,M*1B
可以得到
程序接收并解析串口数据
首先启动demo.c程序,在打开串口软件,对COM2进行数据的发送,随后可以接收到结果,并进行解析。
#include <stdio.h>
#include <string.h>
#include <Windows.h>
#include <stdbool.h>
#define MAX_LENGTH 1000 // 用于存储输入字符串的最大长度
#define MAX_TOKENS 16 // 假设最多有 20 个子字符串
HANDLE hCom;
int splitString(const char* str, char* tokens[], const char* delimiter)
{
int count = 0;
char* ptr;
ptr = strtok(str, delimiter);
while (ptr != NULL && count < MAX_TOKENS)
{
tokens[count++] = ptr;
ptr = strtok(NULL, delimiter);
}
return count;
}
int main() {
//打开串口读部分
hCom = CreateFile(TEXT("com2"),//在这里选择COM口
GENERIC_READ, //允许读
0, //指定共享属性,由于串口不能共享,所以该参数必须为0
NULL,
OPEN_EXISTING, //打开而不是创建
FILE_ATTRIBUTE_NORMAL, //属性描述,该值为FILE_FLAG_OVERLAPPED,表示使用异步I/O,该参数为0,表示同步I/O操作
NULL);
if (hCom == INVALID_HANDLE_VALUE)
{
printf("打开COM失败!\n");
return FALSE;
}
else
{
printf("COM打开成功!\n");
}
SetupComm(hCom, 1024, 1024); //输入缓冲区和输出缓冲区的大小都是1024
/*********************************超时设置**************************************/
COMMTIMEOUTS TimeOuts;
//设定读超时
TimeOuts.ReadIntervalTimeout = MAXDWORD;//读间隔超时
TimeOuts.ReadTotalTimeoutMultiplier = 0;//读时间系数
TimeOuts.ReadTotalTimeoutConstant = 0;//读时间常量
//设定写超时
TimeOuts.WriteTotalTimeoutMultiplier = 1;//写时间系数
TimeOuts.WriteTotalTimeoutConstant = 1;//写时间常量
SetCommTimeouts(hCom, &TimeOuts); //设置超时
/*****************************************配置串口***************************/
DCB dcb;
GetCommState(hCom, &dcb);
dcb.BaudRate = 115200; //波特率为115200
dcb.ByteSize = 8; //每个字节有8位
dcb.Parity = NOPARITY; //无奇偶校验位
dcb.StopBits = ONESTOPBIT; //一个停止位
SetCommState(hCom, &dcb);
DWORD wCount;//实际读取的字节数
bool bReadStat;
char str[100] = { 0 };
//解析串口数据部分
//对指令中几个判断量进行定义
char located[20];//本船方位(相对or绝对)
char towards[20];//目标航向 (相对or绝对)
char states[20];//目标状态
char TFA[20];//获取的数据类型
char* TFA_temp;
char speedUnit[20];
char data[MAX_LENGTH]; // 用于存储输入的字符串
char* tokens[MAX_TOKENS]; // 存储子字符串的数组
int count; // 子字符串数量
while (1)
{
//PurgeComm(hCom, PURGE_TXCLEAR | PURGE_RXCLEAR); //清空缓冲区
bReadStat = ReadFile(hCom, str, sizeof(str), &wCount, NULL);
if (!bReadStat)
{
printf("读串口失败!");
return FALSE;
}
else
{
if (wCount <= 34) {
//printf("没读取到8个字节数据\n!");
printf("请输入要解析的指令: \n");
}
else {
char* newline = strchr(str, '\n');//在的data中查找换行符
// 如果使用fgets读取了换行符,则需要将其替换为字符串结束符
if (newline != NULL) {
*newline = '\0';// '\0'是判定字符数组结束的标识
}
// 调用分隔字符串的函数
count = splitString(str, tokens, ",");
// 打印数组中存储的子字符串
//for (int i = 0; i < count; i++) {
// printf("Token %d: %s\n", i + 1, tokens[i]);
//}
// 根据 tokens 数组的内容设置 located 和 speed 的值
//strcmp把 str1 所指向的字符串和 str2 所指向的字符串进行比较
if (strcmp(tokens[4], "T") == 0)
{
strcpy(located, "本船方位:绝对位置");
}
else if (strcmp(tokens[4], "R") == 0) {
strcpy(located, "本船方位:相对位置");
}
if (strcmp(tokens[7], "T") == 0)
{
strcpy(towards, "目标航向:绝对位置");
}
else if (strcmp(tokens[7], "R") == 0) {
strcpy(towards, "目标航向:相对位置");
}
if (strcmp(tokens[12], "T") == 0)
{
strcpy(states, "目标状态:正在跟踪中");
}
else if (strcmp(tokens[12], "Q") == 0) {
strcpy(states, "目标状态:查询中");
}
else if (strcmp(tokens[12], "L") == 0) {
strcpy(states, "目标状态:丢失");
}
if (strcmp(tokens[10], "K") == 0)
{
strcpy(speedUnit, "速度单位:公里/小时");
}
else if (strcmp(tokens[10], "N") == 0) {
strcpy(speedUnit, "速度单位:节");
}
else if (strcmp(tokens[10], "S") == 0) {
strcpy(speedUnit, "速度单位:法定英里/小时");
}
TFA_temp = strtok(tokens[15], "*");
if (strcmp(strtok(TFA_temp, "*"), "A") == 0)
{
strcpy(TFA, "获取类型:自动");
}
else if (strcmp(strtok(TFA_temp, "*"), "M") == 0) {
strcpy(TFA, "获取类型:手动");
}
else if (strcmp(strtok(TFA_temp, "*"), "R") == 0) {
strcpy(TFA, "获取类型:报告");
}
printf("发送格式:%s\n船舶的编号:%s\n目标距离本船的距离:%s海里\n本船方位:%s度\n%s\n目标速度:%s节\n目标航向:%s度\n%s\n距离最近会遇点:%s海里\n到最近会遇点的时间:%smin\n%s\n目标名称:%s\n%s\n数据获取时间:%s\n%s\n",
tokens[0], tokens[1], tokens[2], tokens[3], located, tokens[5], tokens[6], towards, tokens[8], tokens[9], speedUnit, tokens[11], states, tokens[14], TFA);
return 0;
}
}
Sleep(100);
}
CloseHandle(hCom);
}