,2
(1.空军工程大学 工程学院,西安 710038;2.北京航空航天大学 后备军官选拔培训办公室,北京 100083)
20世纪70年代,“越战”的经验教训促使美国空军开始利用技术手段监控、评估空战训练过程,客观实时评估对抗结果,实现训练模式的变革。
近年来,以新武器装备快速发展为主要标志的空军转型建设快速推进,军事训练改革不断深入。空战训练系统运用实时仿真、航电系统信息监控、高带宽数传等技术手段,实现对飞机技术状态、空中动态、训练质量的实时监控。这些都需要准确、可靠的飞行数据支持。为获得准确、连续、平滑的数据,客观公正地评定空中训练效果,提高训练质量,本文提出一种空战训练系统(Air Combat Training System,ACTS)数据有效性检验算法,并通过仿真试验证明了其有效性。
ACTS的飞行数据通过数据链进行传输,这里采用两台PC以及两部电台来进行收发飞行数据的模拟,因此采用串口通信来完成数据的接收[1-2]。
2.1.1串口类及串口初始化
为了充分利用面向对象程序设计在可维护性、易修改性和可重用性三方面优点,广泛使用了类和对象。其中对于串口的所有操作都封装在CCommCtrl类中。该类的定义如下:
class CCommCtrl
{
public://公共接口
CCommCtrl();//构造函数
virtual~CCommCtrl();//析构函数
bool OpenPort(char*portname);//打开串口并初始化串口
bool ClosePort();//关闭串口
bool ReadFrom(LPVOID inputData,const unsigned int sizeBuffer, unsigned long&length,LPOVERLAPPED ov);//从串口读数据
bool WriteTo(LPCVOID outputData,const unsigned int sizeBuffer, unsigned long&length,LPOVERLAPPED ov); //向串口写数据
bool GetCommStated();//获得串口的状态
HANDLE GetCommHandle();//获得串口的句柄
private://私有变量
bool m-bCommOpen;//指明串口是否打开,true标识打开
HANDLE m-hComm;//串口的句柄
DCB m-dcb;//设备控制块
};
Windows系统实现串口的通信必须先对串口的通信参数进行初始化,而其中比较重要的是数据传输率、奇偶校验、数据比特数和停止比特数等。
Window API GetCommState()函数可以获取串口的当前配置。该函数定义如下:
BOOL GetCommState(HANDLE hFile,LPDCB lpDCB);
其中,hFile是由CreateFile()函数返回的指向已打开串口的句柄;lpDCB为指向设备控制块(DCB)的指针,包含各种串口通信的参数。
2.1.2多线程实现串口通信
报文收发线程以一个UI线程来定义。系统主线程首先通过调用Windows API函数完成对串口的初始化,初始化的参数由DCB结构给出,并在主线程中自定义通信事件消息,初始化完成以后,调用MFC的AfxBeginThread()函数创建报文收发线程,报文收发线程在后台对串口进行实时监测,当监测到预定义的事件后,进行相应的消息函数处理,与此同时,主线程还可以完成人机交互和协调其它线程的同步。图1为地面接收数据时线程的处理流程。
图1 地面数据接收流程图Fig.1 Flowchart of ground data receiving
为了使主线程有效的控制该线程,在线程类中定义了一个同步事件用来当主线程需要关闭该线程时释放串口资源,然后关闭该线程。这里通过使用WindowsAPI函数WaitForMultipleObject()来等待不同的事件以选择不同的处理过程。
代码实现:
(1)主线程创建并启动报文接收线程
m-pRecvThread=(CRecvThread*)AfxBeginThread(RUNTIME-CLASS(CRecvThread),THREAD-PRIORITY-NORMAL,0,CREATE-SUSPENDED);
m-pRecvThread->ResumeThread();
(2)报文接收线程打开串口
if(OpenComm()==false))//OpenComm()中实现了串口的打开和设置
{
AfxMessageBox(″Open failure!″);
return 1;
}
(3)创建线程同步事件
OVERLAPPED ov;
HANDLE hWait[2];
ov.hEvent=CreateEvent(0,true,0,0);
m-hThreadTerm=CreateEvent(0,true,0,0);
hWait[0]=ov.hEvent;
hWait[1]=m-hThreadTerm;
(4)等待串口通信事件,如果串口接收到数据,置事件ov.hEvent为有信号
while(abContinue)
{……
if(!::WaitCommEvent(m-CCommCtrl.GetCommHandle(),&dwEventMask,&ov))
{
DWORD error=GetLastError();
ASSERT(GetLastError()==ERROR-IO-PENDING);
}
……
}
(5)WaitForMultipleObjects()等待有用信号的产生,否则无限期的等待
dwWaitResult=WaitForMultipleObjects(2,hWait,FALSE,INFINITE);
(6)如果ov.hEvent为有信号,即串口接收到字符,接收数据并置事件ov.hEvent为无信号
if(dwWaitResult==WAIT-OBJECT-0)
{
……
::ReadFile(m-CCommCtrl.GetCommHandle(),recvTmp,sizeof(recvTmp),&dwBytesRead,&ovRead);
……
ResetEvent(ov.hEvent);
}
(7)如果m-hThreadTerm为有信号,即主线程要终止该线程,关闭串口并置事件m-hThreadTerm为无信号
else if(dwWaitResult==WAIT-OBJECT-0+1)
{
……
m-CCommCtrl.ClosePort();
ResetEvent(m-hThreadTerm);
break;
}
(8)主线程关闭串口接收线程
GetExitCodeThread(m-pRecvThread->m-hThread,&exit);
TerminateThread(m-pRecvThread->m-hThread,exit);
需要发送报文时,由于发送的数据量不大并且是主动的,所以只需要调用串口类CCommCtrl的WriteTo接口即可完成对报文的发送。
Microsoft Windows环境下的网络编程接口[1,3]是Windows套接字(Windows Socket,Winsock)。Winsock提供了包括TCP/IP、IPX等多种通信协议下的编程接口。不同的Windows版本支持不同的Winsock版本,其中Windows 95等早期版本本身只支持Winsock1.1(16位)下的编程(可以通过安装相关的软件包使其支持Winsock2.0),而Windows98、Windows NT4.0、Windows 2000、WindowsXP 则直接支持Winsock2.0(32位)。Winsock2.0是Winsock1.1的扩展,除兼容Winsock1.1 API外,还定义了一套可支持IP多播的与协议无关的API。
系统需要将报文发送到不同的客户端,并且多播组由报文接收服务器决定,因此采用有根模式来实现网络数据分发。
使用Winsock 2.0实现IP多播的步骤如下(根节点配置):
2.2.1初始化Winsock资源
在使用Winsock之前,必须调用WSAStartup()函数初始化Windows SocketDLL。它允许应用程序或DLL指定Windows Sockets API要求的版本。
WORD wVersionRequested;
WSADATA wsaData;
wVersionRequested=MAKEWORD(2,2);
int result=WSAStartup(wVersionRequested,&wsaData);//初始化套接字
2.2.2创建套接字
调用WSASocket()函数创建一个使用UDP套接字,它是加入多播组的初始化套接字,以后数据的发送和接收都在该套接字上进行。针对本系统的需求,将参数dwFlags设置为WSA-FLAG-MULTIPOINT-C-ROOT、WSA-FLAG-MULTIPOINT-D-ROO和WSA-FLAG-OVERLAPPED的位和,指明组播方式为有根模式:
Sock=WSASocket(AF-INET,SOCK-DGRAM,IPPROTO-UDP,NULL,0,WSA-FLAG-OVERLAPPED|WSA-FLAG-MULTIPOINT-C-ROOT|WSA-FLAG-MULTIPOINT-D-ROOT);
2.2.3设置套接字的选项
调用setsockopt()函数为套接字设置SO-REUSEADDR选项,以允许套接字绑扎到一个已在使用的地址上:
bFlag=TRUE;//设置套接字选项,使套接字为可重用端口地址
setsockopt(Sock,SOL-SOCKET,SO-REUSEADDR,(char*)&bFlag,1);
2.2.4绑定套接字
调用bind()函数绑定套接字,从而将创建好的套接字与本地地址和本地端口联系起来。对于多播通信来说,发送和接收数据通常采用同一个端口:
memset(&local,0,sizeof(local));
local.sin-family=AF-INET;
local.sin-port=htons((USHORT)9999);
local.sin-addr.s-addr=htonl(INADDR-ANY);
bind(Sock,(struct sockaddr FAR*)&local,sizeof(local));
2.2.5注册网络消息及其网络事件
WSAAsyncSelect(Sock,m-hWnd,WM-SOCK-MSG,FD-READ);//数据等待被读入子节点的配置与根节点类似,只是建立套接字时的dwFlags设置有所不同。建立组播的好处就是如果系统需要扩展功能,可以充分利用现有组播框架。
建立好组播组以后,根节点和子节点之间就可以进行通信了,根节点发送报文时只需要调用sendto函数,具体代码如下:
const char*strMes=LPCTSTR(m-SendMessage);
int nSize=m-SendMessage.GetLength()+1;
sendto(Sock,strMes,nSize,0,(sockaddr*)&Remote,sizeof(Remote));
子节点只需要调用recvfrom函数即可获得接收到的数据。另外,子节点还可以利用过滤器来实现报文过滤,只接收需要的报文。
实现网络数据分发的主要意义在于:在接收ACTS飞行数据时,如果报文的数目比较多,利用一个服务器端软件进行实时监控将比较困难,而通过网络数据分发模块,可以将报文发送到多个客户端,各客户端可以分别监视部分飞机或地面监控系统的报文信息,从而可以更好更有效地实现ACTS对战场的实时监视。
VC中使用ADO实现数据库连接[1]。由于VC在数据库编程方面的先天缺陷,导致使用VC编写数据库操作方面的工作比较复杂,可以利用ADO在VC中实现数据库编程。
2.3.1引入ADO库
在Visual C++中使用ADO开发数据库之前,需要引入ADO库。可以在头文件中引入ADO库文件,方法如下:
#import″c:program filescommon filessystemadomsado15.dll″
#include
#include
#include″icrsint.h″
预处理指令#import使程序在编译过程中引入ADO动态库(msado15.dll)。接下来的两个“#include”语句引入了头文件来定义ADO2.0的类和接口标识。最后的“#include”语句引入了ADO2.0数据绑定扩展。
2.3.2建立数据库连接
建立数据库的连接需要使用连接对象(Connection Object)。首先定义一个ConnectionPtr类型的指针,代码如下:
ConnectionPtr m-pConnection;
然后调用Create Instance方法实例化,代码如下:
m-pConnection.CreateInstance(uuidof(Connection));
调用Connection对象的Open方法创建数据库的连接,Open函数的原型如下:
HRESULT Open(-bstr-t ConStr,-bstr-t UserID,-bstr-t Pwd,Long Opts);
其中,ConStr是一个包含连接信息的字符串,UserID是访问数据库的用户名称,Pwd是访问数据库的密码,Opts为可选参数。如果在连接字符串ConStr中包含了数据库用户名和密码,UserID和Pwd值可以为空。
2.3.3创建命令对象和记录集对象
CommandPtr m-pCommand;
RecordsetPtr m-pRecordset;
m-pCommand.CreateInstance(″ADODB.Command″);
m-pRecordset.CreateInstance(-uuidof(Recordset));
然后将建立的数据库连接赋给m-pCommand,这样利用m-pCommand就可以对打开的数据库进行SQL操作了:
m-pCommand->ActiveConnection=m-pConnection。
2.3.4将报文数据插入数据库
在每类报文译码类中定义了一个GenerateInsertSQL函数,该函数无参数,返回此类报文的插入SQL语句,这样,对于某一类报文,其插入数据库的代码如下:
//p为指向运行时某种报文的对象的指针
//插入数据字符串
CString sql=p->GenerateInsertSQL();m-pCommand->CommandText=sql;
m-pCommand->Execute(&vNULL,&vNULL,adCmdText);
3.1.1模块主要功能
有效性检验模块[4]主要实现以下几个关键功能:
(1)根据ID编号或者地址,判断该飞行数据是属于哪架飞机的。ACTS可以同时实时监控10架飞机,每架飞机都有自己的ID号,根据ID号就可以划分该数据属于哪架飞机的数据;
(2)根据时间戳判断飞行数据是否延时或丢失。时间戳是判断数据到达时间的重要依据,有了时间戳才可以准确判断飞行数据是否发生了延时或者丢失。但前提是只有实现了ACTS各个分系统内的时间同步,才可以利用时间戳来获取这些数据之间的关系,对这些数据进行分析;
(3)根据各种飞行数据的特征以及先前获得的经验值进行预估,根据预估值判断数据是否正确。对于像一些记录飞机工作是否正常的数据,我们可以直接根据该类数据正常值的范围就可以判断数据是否正确;而对于像记录飞机航迹的这些实时变化很大的数据,我们就必须在该数据到达之前,利用卡尔曼滤波外推算法,估算出该数据的预估值,然后在该数据到达时进行比对,从而判断出该数据是否正确;
(4)数据发生延时、丢失或错误情况后的处理。飞行数据地面预处理系统要完成工作就是将译码后正确、连续的数据送给后续数据处理系统进行结算、实时显示等操作。所以如果一旦地面接收到的数据发生上述情况,就必须采取相应补偿措施进行处理。
3.1.2数据有效性检验
有效性检验模块处理数据的过程如图2所示。
(1)等待译码模块送来的数据;
(2)根据时间戳判断数据是否延时或丢失。如果该数据在有效时间内到达,则将该数据进行数据正确性检验。如果延时或丢失,就在日志中记录该事件的发生,然后用其它辅助设备的测量值或者预估值代替该数据;
(3)利用正确性检验算法进行数据的正确性检验。如果数据正确,则将数据存入数据库并进行进一步的实时解算、显示等处理。如果数据不正确,就在日志中记录该事件的发生,然后用其它辅助设备的测量值或者预估值代替该数据,进行后续处理。
图2 数据有效性检验流程图Fig.2 Data validity inspection flowchart
对数据有效性检验算法进行验证的航迹数据来源于某型飞机的部分试飞数据。数据包括两部分:
(1)利用本机的机载飞行数据记录器记录的数据,由于不经过远距离无线传输,认为是正确数据;
(2)在机载飞行数据记录器记录数据的同时,由电台无线传输到地面记录器的数据,由于实时传输过程环境的影响,数据中包含延时、丢失、错误等情况。
利用试飞数据来验证有效性检验算法的方法是:选取地面记录器上的部分出错的航迹数据,然后由数据有效性检验算法对这段错误航迹数据进行检验和处理,最后将检验及处理后的这段航迹数据与机载飞行数据记录器上的原始航迹数据进行比对。
该组飞行数据采样记录频率为1次/秒,这里选取第462~622 s的大气机高度数据进行检验。这段时间内可能由于传输环境恶劣等原因导致第482 s高度值出现明显偏差,第512 s数据发生延时,第565 s数据丢失。结果如图3~5所示。
根据上述验证结果看,有效性检验算法检测出了数据的错误、延时和丢失,并进行相应补偿处理,得到一组连续的数据。与机载记录的试飞数据进行比较可以看出,该有效性检验算法较准确地恢复了出错的数据,取得了良好效果。
图3 地面记录的试飞数据:飞机从4.5 km下降至2.1 km,改平后上升至3.9 kmFig.3 Ground recording test-fly data:the airplane descended from 4.5 km to 2.1 km and rose to 3.9 km after being steady
图4 经有效性检验算法处理后:飞机从4.5 km下降至2.1 km,改平后上升至3.9 kmFig.4 Data validity inspection algorithm processing:the airplane descended from 4.5 km to 2.1 km and rose to 3.9 km after being steady
图5 机载记录的原始试飞数据:飞机从4.5 km下降至2.1 km,改平后上升至3.9 kmFig.5 Airborne recording originality test-fly data:the airplane descended from 4.5 km to 2.1 km and rose to 3.9 km after being steady
本文提出了一种ACTS数据有效性检验算法,有效地解决了数据传输过程中出现的错误、延时和丢失等问题。该检验算法对出错的数据进行了修正,得到准确、连续、平滑的数据,供地面监控系统准确地实时监控及评估。进一步研究重点是对数据有效性检验模块实时性进行分析和验证。
参考文献:
[1] 乔林,杨志刚.Visual C++6.0 高级编程技术——MFC与多线程篇[M].北京:中国铁道出版社,2000.
QIAO Lin,YANG Zhi-gang. Visual C++6.0 Advanced Programming Technology:MFC and Multi-threading[M].Beijing: China Railway Press,2000. (in Chinese)
[2] 李现勇.Visual C++串口通信技术与工程实践[M].北京:人民邮电出版社,2004.
LI Xian-yong. Visual C++ Serial Communication Technology and Engineering Practice[M].Beijing:People′s Posts& Telecom Press,2004.(in Chinese)
[3] Andrew S Tanenbaum.Computer Networks[M].3rd ed. New York:Prentice Hall International,Inc.,1997.
[4] 王帅.飞行器航迹数据产生及有效性检测[D].成都:电子科技大学,2004.
WANG Shuai.Aircraft Track Data Generation and Data Validity Inspection[D].Chengdu:University of Electronic Science and Technology of China,2004.(in Chinese)
[5] 梁德文.战斗机航空电子系统最新的发展趋势——网络化[J].电讯技术,2008,48(6):93-97.
LIANG De-wen.Review on the New Development of Fighter Avionics System-Networking [J].Telecommunication Engineering,2008,48(6):93-97.(in Chinese)
[6] DRS Air Combat Training System[Z].Florida:DRS Training & Control System,INC.,2007.
[7] Autonomous Air Combat Maneuvering Instrumentation Training System[Z].Ireal:Isreal Aerospace Industries,Ltd.,2008.