串口:
串行接口简称串口,也称串行通信接口或串行通讯接口(通常指COM接口),是采用串行通信方式的扩展接口。串行接口 (Serial Interface) 是指数据一位一位地顺序传送,其特点是通信线路简单,只要一对传输线就可以实现双向通信(可以直接利用电话线作为传输线),从而大大降低了成本,特别适用于远距离通信,但传送速度较慢。

AIS接收机
船舶自动识别系统,是指一种应用于船和岸、船和船之间的海事安全与通信的新型助航系统。常由VHF通信机、GPS定位仪和与船载显示器及传感器等相连接的通信控制器组成,能自动交换船位、航速、航向、船名、呼号等重要信息。装在船上的AIS在向外发送这些信息的同时,同样接收VHF覆盖范围内其他船舶的信息,从而实现了自动应答。此外,作为一种开放式数据传输系统,它可与雷达、ARPA、ECDIS、VTS等终端设备和INTERNET实现连接,构成海上交管和监视网络,是不用雷达探测也能获得交通信息的有效手段,可以有效减少船舶碰撞事故。

GitHub源码:https://github.com/Phoenixsmf/AIS

java实现串口通信:
基于java语言实现 对AIS接收机的数据解析,java代码:

package com.yang.serialport.manager;import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.util.ArrayList;
import java.util.Enumeration;
import java.util.TooManyListenersException;import com.yang.serialport.utils.ArrayUtils;
import com.yang.serialport.utils.ShowUtils;import gnu.io.CommPort;
import gnu.io.CommPortIdentifier;
import gnu.io.NoSuchPortException;
import gnu.io.PortInUseException;
import gnu.io.SerialPort;
import gnu.io.SerialPortEvent;
import gnu.io.SerialPortEventListener;
import gnu.io.UnsupportedCommOperationException;/*** 串口管理* * @author SMF*/
@SuppressWarnings("all")
public class SerialPortManager {/*** 查找所有可用端口* * @return 可用端口名称列表*/public static final ArrayList<String> findPorts() {// 获得当前所有可用串口Enumeration<CommPortIdentifier> portList = CommPortIdentifier.getPortIdentifiers();ArrayList<String> portNameList = new ArrayList<String>();// 将可用串口名添加到List并返回该Listwhile (portList.hasMoreElements()) {String portName = portList.nextElement().getName();portNameList.add(portName);}return portNameList;}/*** 打开串口* * @param portName*            端口名称* @param baudrate*            波特率* @return 串口对象* @throws PortInUseException*             串口已被占用*/public static final SerialPort openPort(String portName, int baudrate) throws PortInUseException {try {// 通过端口名识别端口CommPortIdentifier portIdentifier = CommPortIdentifier.getPortIdentifier(portName);// 打开端口,并给端口名字和一个timeout(打开操作的超时时间)CommPort commPort = portIdentifier.open(portName, 2000);// 判断是不是串口if (commPort instanceof SerialPort) {SerialPort serialPort = (SerialPort) commPort;try {// 设置一下串口的波特率等参数// 数据位:8// 停止位:1// 校验位:NoneserialPort.setSerialPortParams(baudrate, SerialPort.DATABITS_8, SerialPort.STOPBITS_1,SerialPort.PARITY_NONE);} catch (UnsupportedCommOperationException e) {e.printStackTrace();}return serialPort;}} catch (NoSuchPortException e1) {e1.printStackTrace();}return null;}/*** 关闭串口* * @param serialport*            待关闭的串口对象*/public static void closePort(SerialPort serialPort) {if (serialPort != null) {serialPort.close();}}/*** 往串口发送数据* * @param serialPort*            串口对象* @param order*            待发送数据*/public static void sendToPort(SerialPort serialPort, byte[] order) {OutputStream out = null;try {out = serialPort.getOutputStream();out.write(order);out.flush();} catch (IOException e) {e.printStackTrace();} finally {try {if (out != null) {out.close();out = null;}} catch (IOException e) {e.printStackTrace();}}}/*** 从串口读取数据* * @param serialPort*            当前已建立连接的SerialPort对象* @return 读取到的数据*/public static byte[] readFromPort(SerialPort serialPort) {InputStream in = null;byte[] bytes = {};try {in = serialPort.getInputStream();// 缓冲区大小为一个字节byte[] readBuffer = new byte[1];int bytesNum = in.read(readBuffer);while (bytesNum > 0) {bytes = ArrayUtils.concat(bytes, readBuffer);bytesNum = in.read(readBuffer);}} catch (IOException e) {e.printStackTrace();} finally {try {if (in != null) {in.close();in = null;}} catch (IOException e) {e.printStackTrace();}}return bytes;}/*** 添加监听器* * @param port*            串口对象* @param listener*            串口存在有效数据监听*/public static void addListener(SerialPort serialPort, DataAvailableListener listener) {try {// 给串口添加监听器serialPort.addEventListener(new SerialPortListener(listener));// 设置当有数据到达时唤醒监听接收线程serialPort.notifyOnDataAvailable(true);// 设置当通信中断时唤醒中断线程serialPort.notifyOnBreakInterrupt(true);} catch (TooManyListenersException e) {e.printStackTrace();}}/*** 串口监听*/public static class SerialPortListener implements SerialPortEventListener {private DataAvailableListener mDataAvailableListener;public SerialPortListener(DataAvailableListener mDataAvailableListener) {this.mDataAvailableListener = mDataAvailableListener;}public void serialEvent(SerialPortEvent serialPortEvent) {switch (serialPortEvent.getEventType()) {case SerialPortEvent.DATA_AVAILABLE: // 1.串口存在有效数据if (mDataAvailableListener != null) {mDataAvailableListener.dataAvailable();}break;case SerialPortEvent.OUTPUT_BUFFER_EMPTY: // 2.输出缓冲区已清空break;case SerialPortEvent.CTS: // 3.清除待发送数据break;case SerialPortEvent.DSR: // 4.待发送数据准备好了break;case SerialPortEvent.RI: // 5.振铃指示break;case SerialPortEvent.CD: // 6.载波检测break;case SerialPortEvent.OE: // 7.溢位(溢出)错误break;case SerialPortEvent.PE: // 8.奇偶校验错误break;case SerialPortEvent.FE: // 9.帧错误break;case SerialPortEvent.BI: // 10.通讯中断ShowUtils.errorMessage("与串口设备通讯中断");break;default:break;}}}/*** 串口存在有效数据监听*/public interface DataAvailableListener {/*** 串口存在有效数据*/void dataAvailable();}
}

界面 代码:

package com.yang.serialport.ui;import java.awt.Color;
import java.awt.GraphicsEnvironment;
import java.awt.Point;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.util.List;import javax.swing.BorderFactory;
import javax.swing.ButtonGroup;
import javax.swing.JButton;
import javax.swing.JComboBox;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JPanel;
import javax.swing.JRadioButton;
import javax.swing.JScrollPane;
import javax.swing.JTextArea;
import javax.swing.event.PopupMenuEvent;
import javax.swing.event.PopupMenuListener;import com.nms.ais.AIS_Analyse;
import com.yang.serialport.manager.SerialPortManager;
import com.yang.serialport.utils.ByteUtils;
import com.yang.serialport.utils.ShowUtils;
import com.yang.serialport.utils.SqlSessionFactoryInit;import gnu.io.PortInUseException;
import gnu.io.SerialPort;/*** 主界面* * @author SMF*/
@SuppressWarnings("all")
public class MainFrame extends JFrame {// 程序界面宽度public final int WIDTH = 1000;// 程序界面高度public final int HEIGHT = 390;// 数据显示区private JTextArea mDataView = new JTextArea();private JScrollPane mScrollDataView = new JScrollPane(mDataView);//有效数据显示区域public static  JTextArea mDataValiableView = new JTextArea();public static JScrollPane mScrollDataViewValiable = new JScrollPane(mDataValiableView);// 串口设置面板private JPanel mSerialPortPanel = new JPanel();private JLabel mSerialPortLabel = new JLabel("串口");private JLabel mBaudrateLabel = new JLabel("波特率");private JComboBox mCommChoice = new JComboBox();private JComboBox mBaudrateChoice = new JComboBox();private ButtonGroup mDataChoice = new ButtonGroup();private JRadioButton mDataASCIIChoice = new JRadioButton("ASCII", true);private JRadioButton mDataHexChoice = new JRadioButton("Hex");// 操作面板private JPanel mOperatePanel = new JPanel();private JTextArea mDataInput = new JTextArea();private JButton mSerialPortOperate = new JButton("打开串口");private JButton mSendData = new JButton("发送数据");// 串口列表private List<String> mCommList = null;// 串口对象private SerialPort mSerialport;public MainFrame() {try{SqlSessionFactoryInit.creatSqlSessionFactory();initView();initComponents();actionListener();initData();}catch(Exception e){e.printStackTrace();}}/*** 初始化窗口*/private void initView() {// 关闭程序setDefaultCloseOperation(javax.swing.WindowConstants.EXIT_ON_CLOSE);// 禁止窗口最大化setResizable(false);// 设置程序窗口居中显示Point p = GraphicsEnvironment.getLocalGraphicsEnvironment().getCenterPoint();setBounds(p.x - WIDTH / 2, p.y - HEIGHT / 2, WIDTH, HEIGHT);this.setLayout(null);setTitle("串口通信");}/*** 初始化控件*/private void initComponents() {// 数据显示mDataView.setFocusable(false);mScrollDataView.setBounds(10, 10, 505, 200);add(mScrollDataView);// 数据显示mDataValiableView.setFocusable(false);mScrollDataViewValiable.setBounds(520, 10, 400, 200);add(mScrollDataViewValiable);// 串口设置mSerialPortPanel.setBorder(BorderFactory.createTitledBorder("串口设置"));mSerialPortPanel.setBounds(10, 220, 170, 130);mSerialPortPanel.setLayout(null);add(mSerialPortPanel);mSerialPortLabel.setForeground(Color.gray);mSerialPortLabel.setBounds(10, 25, 40, 20);mSerialPortPanel.add(mSerialPortLabel);mCommChoice.setFocusable(false);mCommChoice.setBounds(60, 25, 100, 20);mSerialPortPanel.add(mCommChoice);mBaudrateLabel.setForeground(Color.gray);mBaudrateLabel.setBounds(10, 60, 40, 20);mSerialPortPanel.add(mBaudrateLabel);mBaudrateChoice.setFocusable(false);mBaudrateChoice.setBounds(60, 60, 100, 20);mSerialPortPanel.add(mBaudrateChoice);mDataASCIIChoice.setBounds(20, 95, 55, 20);mDataHexChoice.setBounds(95, 95, 55, 20);mDataChoice.add(mDataASCIIChoice);mDataChoice.add(mDataHexChoice);mSerialPortPanel.add(mDataASCIIChoice);mSerialPortPanel.add(mDataHexChoice);// 操作mOperatePanel.setBorder(BorderFactory.createTitledBorder("操作"));mOperatePanel.setBounds(200, 220, 315, 130);mOperatePanel.setLayout(null);add(mOperatePanel);mDataInput.setBounds(25, 25, 265, 50);mDataInput.setLineWrap(true);mDataInput.setWrapStyleWord(true);mOperatePanel.add(mDataInput);mSerialPortOperate.setFocusable(false);mSerialPortOperate.setBounds(45, 95, 90, 20);mOperatePanel.add(mSerialPortOperate);mSendData.setFocusable(false);mSendData.setBounds(180, 95, 90, 20);mOperatePanel.add(mSendData);}/*** 初始化数据*/private void initData() {mCommList = SerialPortManager.findPorts();// 检查是否有可用串口,有则加入选项中if (mCommList == null || mCommList.size() < 1) {ShowUtils.warningMessage("没有搜索到有效串口!");} else {for (String s : mCommList) {mCommChoice.addItem(s);}}mBaudrateChoice.addItem("9600");mBaudrateChoice.addItem("19200");mBaudrateChoice.addItem("38400");mBaudrateChoice.addItem("57600");mBaudrateChoice.addItem("115200");}/*** 按钮监听事件*/private void actionListener() {// 串口mCommChoice.addPopupMenuListener(new PopupMenuListener() {@Overridepublic void popupMenuWillBecomeVisible(PopupMenuEvent e) {mCommList = SerialPortManager.findPorts();// 检查是否有可用串口,有则加入选项中if (mCommList == null || mCommList.size() < 1) {ShowUtils.warningMessage("没有搜索到有效串口!");} else {int index = mCommChoice.getSelectedIndex();mCommChoice.removeAllItems();for (String s : mCommList) {mCommChoice.addItem(s);}mCommChoice.setSelectedIndex(index);}}@Overridepublic void popupMenuWillBecomeInvisible(PopupMenuEvent e) {// NO OP}@Overridepublic void popupMenuCanceled(PopupMenuEvent e) {// NO OP}});// 打开|关闭串口mSerialPortOperate.addActionListener(new ActionListener() {@Overridepublic void actionPerformed(ActionEvent e) {if ("打开串口".equals(mSerialPortOperate.getText()) && mSerialport == null) {openSerialPort(e);} else {closeSerialPort(e);}}});// 发送数据mSendData.addActionListener(new ActionListener() {@Overridepublic void actionPerformed(ActionEvent e) {sendData(e);}});}/*** 打开串口* * @param evt*            点击事件*/private void openSerialPort(java.awt.event.ActionEvent evt) {// 获取串口名称String commName = (String) mCommChoice.getSelectedItem();// 获取波特率,默认为9600int baudrate = 9600;String bps = (String) mBaudrateChoice.getSelectedItem();baudrate = Integer.parseInt(bps);// 检查串口名称是否获取正确if (commName == null || commName.equals("")) {ShowUtils.warningMessage("没有搜索到有效串口!");} else {try {mSerialport = SerialPortManager.openPort(commName, baudrate);if (mSerialport != null) {mDataView.setText("串口已打开" + "\r\n");mSerialPortOperate.setText("关闭串口");}} catch (PortInUseException e) {ShowUtils.warningMessage("串口已被占用!");}}// 添加串口监听SerialPortManager.addListener(mSerialport, new SerialPortManager.DataAvailableListener() {@Overridepublic void dataAvailable() {byte[] data = null;try {if (mSerialport == null) {ShowUtils.errorMessage("串口对象为空,监听失败!");System.out.print("串口对象为空,监听失败!");} else {// 读取串口数据data = SerialPortManager.readFromPort(mSerialport);//System.out.println(data.toString());// 以字符串的形式接收数据if (mDataASCIIChoice.isSelected()) {mDataView.append(new String(data) + "\r\n");                            AIS_Analyse.comport1_DataReceived(new String(data));//System.out.println(new String(data) + "\r\n");}// 以十六进制的形式接收数据if (mDataHexChoice.isSelected()) {mDataView.append(ByteUtils.byteArrayToHexString(data) + "\r\n");///System.out.println(ByteUtils.byteArrayToHexString(data));}}} catch (Exception e) {ShowUtils.errorMessage(e.toString());e.printStackTrace();// 发生读取错误时显示错误信息后退出系统System.exit(0);}}});}/*** 关闭串口* * @param evt*            点击事件*/private void closeSerialPort(java.awt.event.ActionEvent evt) {SerialPortManager.closePort(mSerialport);mDataView.setText("串口已关闭" + "\r\n");mSerialPortOperate.setText("打开串口");mSerialport = null;}/*** 发送数据* * @param evt*            点击事件*/private void sendData(java.awt.event.ActionEvent evt) {// 待发送数据String data = mDataInput.getText().toString();if (mSerialport == null) {ShowUtils.warningMessage("请先打开串口!");return;}if ("".equals(data) || data == null) {ShowUtils.warningMessage("请输入要发送的数据!");return;}// 以字符串的形式发送数据if (mDataASCIIChoice.isSelected()) {SerialPortManager.sendToPort(mSerialport, data.getBytes());}// 以十六进制的形式发送数据if (mDataHexChoice.isSelected()) {SerialPortManager.sendToPort(mSerialport, ByteUtils.hexStr2Byte(data));}}public static void main(String args[]) {java.awt.EventQueue.invokeLater(new Runnable() {public void run() {new MainFrame().setVisible(true);}});}
}

AIS数据解析 此处较为复杂:

package com.nms.ais;import java.util.ArrayList;
import java.util.List;import com.nms.dao.config.BoatMapper;
import com.nms.model.config.Boat;
import com.nms.service.config.BoatHistoryService;
import com.nms.service.config.BoatService;
import com.yang.serialport.manager.SerialPortManager;
import com.yang.serialport.ui.MainFrame;
import com.yang.serialport.utils.AISLog;
import com.yang.serialport.utils.MiscUtil;
import com.yang.serialport.utils.Msg;
import com.yang.serialport.utils.SqlSessionFactoryInit;import gnu.io.SerialPort;
/*** @author smf* @description ais数据解析* */
public class AIS_Analyse {public String AIS_msg = null; // 触发一次串口事件接收到的AIS数据public static int i = 0;public int j = 0;public int k = 0;/**单一语句长度*/public static int data_length = 0; // 单一语句长度/**语句字符位置*/public int chars_count = 0; // 语句字符位置/**完整的一条AIS语句*/public static char[] SingleDataBuffer = new char[82]; // 完整的一条AIS语句/**一条AIS语句的解封装结果*/public static char[] sum_binary = new char[372]; // 一条AIS语句的解封装结果/**单一语句字符数*/public static int data_count; // 单一语句字符数/** AIS语句接收窗口显示的AIS语句计数*/public static int newline_count = 0; // AIS语句接收窗口显示的AIS语句计数/**事件顺序(语句排列顺序)标识*/public static int event_count = 0; // 事件顺序(语句排列顺序)标识/**用户船(查询目标)识别码*/public String TargetMMSI = null; // 用户船(查询目标)识别码/**用户船(查询目标)查询结果标识*/public static int MMSI_enable = 0; // 用户船(查询目标)查询结果标识/**语句类型识别码*/public static int defi_m = 0; // 语句类型识别码/**当前AIS信息包含的经度*/public static double RecentLongitude; // 当前AIS信息包含的经度public static double RecentLatitude; // 当前AIS信息包含的纬度public static double RecentCourse; // 当前AIS信息包含的航向信息public static double ShipLongitude; // 用户船经度public static double ShipLatitude; // 用户船纬度public static double BaseLongitude; // 基站经度public static double BaseLatitude; // 基站纬度public static BoatMapper boatMapper = SqlSessionFactoryInit.getContext().getBean(BoatMapper.class);public static BoatService boatService = SqlSessionFactoryInit.getContext().getBean(BoatService.class);public static BoatHistoryService boatHistoryService = SqlSessionFactoryInit.getContext().getBean(BoatHistoryService.class);public double PI = Math.PI; // 3.14159265358979323846public double R = 6371.393; // 地球半径public double Distance; // 两点间距离/**天线转向的目的位置*/public String DestinationAngle = null; // 天线转向的目的位置public static int basechange; // 基站切换指令标识public static int count_enable; // 指向角度计算控制指令标识 : 1.计算开始; 0.计算关闭public static int angle_send; // 指向角度发送指令标识 : 1.发送开始; 0.发送关闭// public bool bOpenPort; //1.串口2关闭; 0.串口2打开public Boolean bCountDistanceAUTO; // 指向角计算使能信号: 1.计算开始; 0.计算关闭public Boolean bChangeBase; // 基站选择使能信号: 1.选择船载移动基站; 0.选择岸基基站public Boolean bAngleSendAUTO; // 目的位置自动发送使能型号: 1.发送开始; 0.发送关闭private static List<String> _buff = new ArrayList<String>();private static Boolean _recstatu = false; // 是否处于一个正在接收数据包的状态/**数据域起始位*/public static int data_start; // 数据域起始位/**数据域终止位*/public static int data_end; // 数据域终止位/** 语句类型识别码*/public static int sen_f; // 语句类型识别码public static void Data_analyze_m123(String AISMessage ) // 消息1、2、3数据解析{int i=0;Boat boat = new Boat();AISMessage += "\r\n\r\n";AISMessage += "解析后的数据如下:\r\n\r\n";// 二进制数据向有效数据转换char data_tr_bin; // 二进制数据中间变量初始化int data_tr_valid = 0; // 有效数据中间变量初始化int count_s = 0; // 数据位起始值int count_e = 0; // 数据位终止值int digit_num; // 数据位数// 提取消息识别码int message_identifier; // 消息识别码digit_num = 6; // 消息识别码的位数为6位count_e = count_s + digit_num - 1; // 计算数据终止位for (i = count_s; i <= count_e; i++) {data_tr_bin = sum_binary[i];data_tr_valid = data_tr_valid + (data_tr_bin - '0') * (int) (Math.pow(2, count_e - i));}message_identifier = data_tr_valid;switch (message_identifier) {case 1: {AISMessage += "消息识别码:  ";AISMessage += String.valueOf(message_identifier);AISMessage += "  即 消息类型为“定时的船位报告(A类船载移动设备)”。\r\n";break;}case 2: {AISMessage += "消息识别码:  ";AISMessage += String.valueOf(message_identifier);AISMessage += "  即 消息类型为“定时的船位报告(A类船载移动设备)”。\r\n";break;}case 3: {AISMessage += "消息识别码:  ";AISMessage += String.valueOf(message_identifier);AISMessage += "  即 消息类型为“特别船位报告,对询问的回复(A类船载移动设备)”。\r\n";break;}}count_s = count_e + 1; // 提取完毕后,重置数据位起始值// END(提取消息识别码)// 提取转发指示符int transpond_indicate; // 转发指示符digit_num = 2; // 转发指示符的位数为2位count_e = count_s + digit_num - 1; // 计算数据终止位data_tr_valid = 0;for (i = count_s; i <= count_e; i++) {data_tr_bin = sum_binary[i];data_tr_valid = data_tr_valid + (data_tr_bin - '0') * (int) (Math.pow(2, count_e - i));}transpond_indicate = data_tr_valid;AISMessage += "转发指示符:  ";AISMessage += String.valueOf(transpond_indicate);AISMessage += "  即 该条消息被转发的次数为:";AISMessage += String.valueOf(transpond_indicate);AISMessage += "  次。\r\n";count_s = count_e + 1; // 提取完毕后,重置数据位起始值// END(提取转发指示符)// 提取用户识别码,即MMSI码int MMSI; // 用户识别码digit_num = 30; // 用户识别码,即MMSI码的位数为30位count_e = count_s + digit_num - 1; // 计算数据终止位data_tr_valid = 0;for (i = count_s; i <= count_e; i++) {data_tr_bin = sum_binary[i];data_tr_valid = data_tr_valid + (data_tr_bin - '0') * (int) (Math.pow(2, count_e - i));}MMSI = data_tr_valid;AISMessage += "用户识别码(即MMSI码):  ";AISMessage += String.valueOf(MMSI);AISMessage += "\r\n";//String MMSI_OUT = String.valueOf(MMSI); // 提取当前信息的MMSI码,用于船舶识别count_s = count_e + 1; // 提取完毕后,重置数据位起始值// END(提取用户识别码,即MMSI码)// 提取航行状态int navigation_status; // 航行状态digit_num = 4; // 航行状态的位数为4位count_e = count_s + digit_num - 1; // 计算数据终止位data_tr_valid = 0;for (i = count_s; i <= count_e; i++) {data_tr_bin = sum_binary[i];data_tr_valid = data_tr_valid + (data_tr_bin - '0') * (int) (Math.pow(2, count_e - i));}navigation_status = data_tr_valid;AISMessage += "航行状态:  ";AISMessage += String.valueOf(navigation_status);if (navigation_status == 15)AISMessage += " 即 默认值(未定义)";AISMessage += "\r\n";count_s = count_e + 1; // 提取完毕后,重置数据位起始值// END(提取航行状态)// 提取转向率ROT(AIS)int ROT_AIS; // 转向率ROT(AIS)digit_num = 8; // 转向率ROT(AIS)的位数为8位count_e = count_s + digit_num - 1; // 计算数据终止位data_tr_valid = 0;if (sum_binary[count_s] != '1') {for (i = count_s; i <= count_e; i++) {data_tr_bin = sum_binary[i];data_tr_valid = data_tr_valid + (data_tr_bin - '0') * (int) (Math.pow(2, count_e - i));}ROT_AIS = data_tr_valid;AISMessage += "转向率ROT(AIS):  ";AISMessage += String.valueOf(ROT_AIS);AISMessage += " 度/分钟";AISMessage += "\r\n";}else if (sum_binary[count_s] == '1') {// ("转向率是负数\n");for (i = count_s + 1; i <= count_e; i++) {if (sum_binary[i] == '0')data_tr_bin = '1';elsedata_tr_bin = '0';data_tr_valid = data_tr_valid + (data_tr_bin - '0') * (int) (Math.pow(2, count_e - i));}data_tr_valid = data_tr_valid + 1;data_tr_valid = -data_tr_valid;ROT_AIS = data_tr_valid;AISMessage += "转向率ROT(AIS):  ";AISMessage += String.valueOf(ROT_AIS);AISMessage += " 度/分钟";if (ROT_AIS == -128)AISMessage += " 即默认值(表示无法获得)";AISMessage += "\r\n";}count_s = count_e + 1; // 提取完毕后,重置数据位起始值// END(提取转向率ROT(AIS))// 提取对地航速double speed_over_ground; // 对地航速digit_num = 10; // 对地航速的位数为10位count_e = count_s + digit_num - 1; // 计算数据终止位data_tr_valid = 0;for (i = count_s; i <= count_e; i++) {data_tr_bin = sum_binary[i];data_tr_valid = data_tr_valid + (data_tr_bin - '0') * (int) (Math.pow(2, count_e - i));}speed_over_ground = (float) (data_tr_valid) / 10;AISMessage += "对地航速:  ";AISMessage += String.valueOf(speed_over_ground);boat.setSog(speed_over_ground+"kn");AISMessage += " kn";AISMessage += "\r\n";count_s = count_e + 1; // 提取完毕后,重置数据位起始值// END(提取对地航速)// 提取船位精确度int position_accuracy; // 船位精确度digit_num = 1; // 船位精确度的位数为1位count_e = count_s + digit_num - 1; // 计算数据终止位data_tr_valid = 0;for (i = count_s; i <= count_e; i++) {data_tr_bin = sum_binary[i];data_tr_valid = data_tr_valid + (data_tr_bin - '0') * (int) (Math.pow(2, count_e - i));}position_accuracy = data_tr_valid;AISMessage += "船位精确度:  ";AISMessage += String.valueOf(position_accuracy);if (data_tr_valid == 1)AISMessage += " 即 高精度(<10m,DGNSS接收机的差分模式)";else if (data_tr_valid == 0)AISMessage += " 即 低精度(>10m,GNSS接收机或其他电子定位装置的自主模式)";AISMessage += "\r\n";count_s = count_e + 1; // 提取完毕后,重置数据位起始值// END(提取船位精确度)// 提取经度double longitude; // 经度digit_num = 28; // 经度的位数为28位count_e = count_s + digit_num - 1; // 计算数据终止位data_tr_valid = 0;int hour;int minute;double second;if (sum_binary[count_s] != '1') {// ("经度是正数,即东经\n");for (i = count_s; i <= count_e; i++) {data_tr_bin = sum_binary[i];data_tr_valid = data_tr_valid + (data_tr_bin - '0') * (int) (Math.pow(2, count_e - i));}longitude = (float) ((data_tr_valid) / 10000);hour = (int) (data_tr_valid / 10000 / 60);minute = (int) (longitude - hour * 60);second = (longitude - hour * 60 - minute) * 60;RecentLongitude = longitude / 60; // 将经度信息传递给全局变量,单位为度AISMessage += "经度:  ";AISMessage += "东经 ";AISMessage += String.valueOf(hour);AISMessage += "度";AISMessage += String.valueOf(minute);AISMessage += "分";AISMessage += String.valueOf(0.0000);AISMessage += "秒";AISMessage += "\r\n";}else if (sum_binary[count_s] == '1') {// ("经度是负数,即西经\n");for (i = count_s + 1; i <= count_e; i++) {if (sum_binary[i] == '0')data_tr_bin = '1';elsedata_tr_bin = '0';data_tr_valid = data_tr_valid + (data_tr_bin - '0') * (int) (Math.pow(2, count_e - i));}data_tr_valid = data_tr_valid + 1; // 经度的绝对值longitude = (float) ((data_tr_valid) / 10000);hour = (int) (data_tr_valid / 10000 / 60);minute = (int) (longitude - hour * 60);second = (longitude - hour * 60 - minute) * 60;longitude = -longitude; // 经度的真实值RecentLongitude = longitude / 60; // 将经度信息传递给全局变量AISMessage += "经度:  ";AISMessage += "西经 ";AISMessage += String.valueOf(hour);AISMessage += "度";AISMessage += String.valueOf(minute);AISMessage += "分";AISMessage += String.valueOf(0.0000);AISMessage += "秒";AISMessage += "\r\n";}count_s = count_e + 1; // 提取完毕后,重置数据位起始值// END(提取经度)// 提取纬度double latitude; // 纬度digit_num = 27; // 纬度的位数为27位count_e = count_s + digit_num - 1; // 计算数据终止位data_tr_valid = 0;if (sum_binary[count_s] != '1') {("纬度是正数,即北纬\n");for (i = count_s; i <= count_e; i++) {data_tr_bin = sum_binary[i];data_tr_valid = data_tr_valid + (data_tr_bin - '0') * (int) (Math.pow(2, count_e - i));}latitude = (float) (data_tr_valid) / 10000;hour = (int) (data_tr_valid / 10000 / 60);minute = (int) (latitude - hour * 60);second = (latitude - hour * 60 - minute) * 60;RecentLatitude = latitude / 60; // 将当前纬度传递给全局变量AISMessage += "纬度:  ";AISMessage += "北纬 ";AISMessage += String.valueOf(hour);AISMessage += "度";AISMessage += String.valueOf(minute);AISMessage += "分";AISMessage += String.valueOf(0.0000);AISMessage += "秒";AISMessage += "\r\n";}else if (sum_binary[count_s] == '1') {// ("纬度是负数,即南纬\n");for (i = count_s + 1; i <= count_e; i++) {if (sum_binary[i] == '0')data_tr_bin = '1';elsedata_tr_bin = '0';data_tr_valid = data_tr_valid + (data_tr_bin - '0') * (int) (Math.pow(2, count_e - i));}data_tr_valid = data_tr_valid + 1; // 纬度的绝对值latitude = (float) (data_tr_valid) / 10000;hour = (int) (data_tr_valid / 10000 / 60);minute = (int) (latitude - hour * 60);second = (latitude - hour * 60 - minute) * 60;latitude = -latitude; // 纬度的真实值RecentLatitude = latitude / 60; // 将当前纬度传递给全局变量AISMessage += "纬度:  ";AISMessage += "南纬 ";AISMessage += String.valueOf(hour);AISMessage += "度";AISMessage += String.valueOf(minute);AISMessage += "分";AISMessage += String.valueOf(0.0000);AISMessage += "秒";AISMessage += "\r\n";}count_s = count_e + 1; // 提取完毕后,重置数据位起始值// END(提取纬度)// 提取对地航向double course_over_the_ground;digit_num = 12; // 对地航向的位数为12位count_e = count_s + digit_num - 1; // 计算数据终止位data_tr_valid = 0;for (i = count_s; i <= count_e; i++) {data_tr_bin = sum_binary[i];data_tr_valid = data_tr_valid + (data_tr_bin - '0') * (int) (Math.pow(2, count_e - i));}course_over_the_ground = (float) (data_tr_valid) / 10;AISMessage += "对地航向:  ";AISMessage += String.valueOf(course_over_the_ground);boat.setCog(course_over_the_ground+"度");AISMessage += "度";if (course_over_the_ground >= 360)AISMessage += " 即 默认值(不可用)";AISMessage += "\r\n";count_s = count_e + 1; // 提取完毕后,重置数据位起始值// END(提取对地航向)// 提取真航向int true_course; // 真航向digit_num = 9; // 真航向的位数为9位count_e = count_s + digit_num - 1; // 计算数据终止位data_tr_valid = 0;for (i = count_s; i <= count_e; i++) {data_tr_bin = sum_binary[i];data_tr_valid = data_tr_valid + (data_tr_bin - '0') * (int) (Math.pow(2, count_e - i));}true_course = data_tr_valid;AISMessage += "真航向:  ";AISMessage += String.valueOf(true_course);AISMessage += "度";if (true_course == 511)AISMessage += " 即 默认值(不可用)";AISMessage += "\r\n";count_s = count_e + 1; // 提取完毕后,重置数据位起始值// END(提取真航向)// 提取时间标记int time_marker; // 时间标记digit_num = 6; // 时间标记的位数为6位count_e = count_s + digit_num - 1; // 计算数据终止位data_tr_valid = 0;for (i = count_s; i <= count_e; i++) {data_tr_bin = sum_binary[i];data_tr_valid = data_tr_valid + (data_tr_bin - '0') * (int) (Math.pow(2, count_e - i));}time_marker = data_tr_valid;AISMessage += "时间标记:  ";AISMessage += "报告产生时的UTC秒: ";AISMessage += String.valueOf(time_marker);AISMessage += "\r\n";count_s = count_e + 1; // 提取完毕后,重置数据位起始值// END(提取时间标记)// 提取为地区性应用所保留digit_num = 4; // 为地区性应用所保留的位数为4位count_e = count_s + digit_num - 1; // 计算数据终止位data_tr_valid = 0;for (i = count_s; i <= count_e; i++) {data_tr_bin = sum_binary[i];data_tr_valid = data_tr_valid + (data_tr_bin - '0') * (int) (Math.pow(2, count_e - i));}AISMessage += "为地区性应用所保留:  ";AISMessage += String.valueOf(data_tr_valid);AISMessage += "\r\n";count_s = count_e + 1; // 提取完毕后,重置数据位起始值// END(提取为地区性应用所保留)// 提取备用位digit_num = 1; // 备用位的位数为1位count_e = count_s + digit_num - 1; // 计算数据终止位data_tr_valid = 0;for (i = count_s; i <= count_e; i++) {data_tr_bin = sum_binary[i];data_tr_valid = data_tr_valid + (data_tr_bin - '0') * (int) (Math.pow(2, count_e - i));}AISMessage += "备用位:  ";AISMessage += String.valueOf(data_tr_valid);AISMessage += "\r\n";count_s = count_e + 1; // 提取完毕后,重置数据位起始值// END(提取备用位)// 提取RAIM标志int RAIM; // RAIM标志digit_num = 1; // RAIM标志的位数为1位count_e = count_s + digit_num - 1; // 计算数据终止位data_tr_valid = 0;for (i = count_s; i <= count_e; i++) {data_tr_bin = sum_binary[i];data_tr_valid = data_tr_valid + (data_tr_bin - '0') * (int) (Math.pow(2, count_e - i));}RAIM = data_tr_valid;AISMessage += "RAIM标志:  ";AISMessage += String.valueOf(RAIM);if (RAIM == 1)AISMessage += " 即 RAIM使用";elseAISMessage += " 即 RAIM未使用(默认)";AISMessage += "\r\n";count_s = count_e + 1; // 提取完毕后,重置数据位起始值// END(提取RAIM标志)// 提取通信状态// fprintf(w1,"%s","通信状态: ");AISMessage += "通信状态:  ";if (message_identifier == 1 || message_identifier == 2) // 通信状态为SOTDMA{AISMessage += "SOTDMA通信状态";AISMessage += "\r\n";// 提取同步状态int synchronization_status; // 同步状态digit_num = 2; // 同步状态的位数为2位count_e = count_s + digit_num - 1; // 计算数据终止位data_tr_valid = 0;for (i = count_s; i <= count_e; i++) {data_tr_bin = sum_binary[i];data_tr_valid = data_tr_valid + (data_tr_bin - '0') * (int) (Math.pow(2, count_e - i));}synchronization_status = data_tr_valid;AISMessage += "  同步状态:  ";AISMessage += String.valueOf(synchronization_status);if (synchronization_status == 0) {AISMessage += " 即 直接获取UTC";AISMessage += "\r\n";} else if (synchronization_status == 1) {AISMessage += " 即 间接获取UTC";AISMessage += "\r\n";} else if (synchronization_status == 2) {AISMessage += " 即 台站同步于基地台";AISMessage += "\r\n";} else if (synchronization_status == 3) {AISMessage += " 即 台站同步于另一个接受台数量最多的台站";AISMessage += "\r\n";}count_s = count_e + 1; // 提取完毕后,重置数据位起始值// 提取时隙超时int TS_overtime; // 时隙超时digit_num = 3; // 时隙超时的位数为3位count_e = count_s + digit_num - 1; // 计算数据终止位data_tr_valid = 0;for (i = count_s; i <= count_e; i++) {data_tr_bin = sum_binary[i];data_tr_valid = data_tr_valid + (data_tr_bin - '0') * (int) (Math.pow(2, count_e - i));}TS_overtime = data_tr_valid;AISMessage += "  时隙超时:  ";AISMessage += String.valueOf(TS_overtime);if (TS_overtime == 0) {AISMessage += " 即 这是该时隙最后一次传输";AISMessage += "\r\n";} else {AISMessage += " 即 在时隙改变前将保留该时隙 ";AISMessage += String.valueOf(TS_overtime);AISMessage += " 帧";AISMessage += "\r\n";}count_s = count_e + 1; // 提取完毕后,重置数据位起始值// 提取子信息int child_information; // 子信息digit_num = 14; // 子信息的位数为14位count_e = count_s + digit_num - 1; // 计算数据终止位data_tr_valid = 0;AISMessage += "  子信息:   ";if (TS_overtime == 3 || TS_overtime == 5 || TS_overtime == 7) {for (i = count_s; i <= count_e; i++) {data_tr_bin = sum_binary[i];data_tr_valid = data_tr_valid + (data_tr_bin - '0') * (int) (Math.pow(2, count_e - i));}child_information = data_tr_valid;AISMessage += " 本台当前接收到其他台站数量为:";AISMessage += String.valueOf(child_information);AISMessage += "\r\n";}else if (TS_overtime == 2 || TS_overtime == 4 || TS_overtime == 6) {for (i = count_s; i <= count_e; i++) {data_tr_bin = sum_binary[i];data_tr_valid = data_tr_valid + (data_tr_bin - '0') * (int) (Math.pow(2, count_e - i));}child_information = data_tr_valid;AISMessage += " 用于该发射的时隙号为:";AISMessage += String.valueOf(child_information);AISMessage += "\r\n";}else if (TS_overtime == 1) {int UTC_hour;int UTC_min;AISMessage += " UTC: ";for (i = count_s; i <= count_s + 4; i++) {data_tr_bin = sum_binary[i];data_tr_valid = data_tr_valid + (data_tr_bin - '0') * (int) (Math.pow(2, count_s + 4 - i));}UTC_hour = data_tr_valid;data_tr_valid = 0;AISMessage += String.valueOf(UTC_hour);AISMessage += " 小时";count_s = count_s + 5;for (i = count_s; i <= count_s + 6; i++) {data_tr_bin = sum_binary[i];data_tr_valid = data_tr_valid + (data_tr_bin - '0') * (int) (Math.pow(2, count_s + 6 - i));}UTC_min = data_tr_valid;AISMessage += String.valueOf(UTC_min);AISMessage += " 分";AISMessage += "\r\n";}else if (TS_overtime == 0) {for (i = count_s; i <= count_e; i++) {data_tr_bin = sum_binary[i];data_tr_valid = data_tr_valid + (data_tr_bin - '0') * (int) (Math.pow(2, count_e - i));}child_information = data_tr_valid;AISMessage += " 时隙偏移量为:";AISMessage += String.valueOf(child_information);AISMessage += "\r\n";}count_s = count_e + 1; // 提取完毕后,重置数据位起始值}else if (message_identifier == 3) // 通信状态为ITDMA{AISMessage += "ITDMA通信状态";AISMessage += "\r\n";// 提取同步状态int synchronization_status; // 同步状态digit_num = 2; // 同步状态的位数为2位count_e = count_s + digit_num - 1; // 计算数据终止位data_tr_valid = 0;for (i = count_s; i <= count_e; i++) {data_tr_bin = sum_binary[i];data_tr_valid = data_tr_valid + (data_tr_bin - '0') * (int) (Math.pow(2, count_e - i));}synchronization_status = data_tr_valid;AISMessage += "  同步状态:  ";AISMessage += String.valueOf(synchronization_status);if (synchronization_status == 0) {AISMessage += " 即 直接获取UTC";AISMessage += "\r\n";} else if (synchronization_status == 1) {AISMessage += " 即 间接获取UTC";AISMessage += "\r\n";} else if (synchronization_status == 2) {AISMessage += " 即 台站同步于基地台";AISMessage += "\r\n";} else if (synchronization_status == 3) {AISMessage += " 即 台站同步于另一个接受台数量最多的台站";AISMessage += "\r\n";}count_s = count_e + 1; // 提取完毕后,重置数据位起始值// 提取时隙增量int TS_add; // 时隙增量digit_num = 13; // 时隙增量的位数为13位count_e = count_s + digit_num - 1; // 计算数据终止位data_tr_valid = 0;for (i = count_s; i <= count_e; i++) {data_tr_bin = sum_binary[i];data_tr_valid = data_tr_valid + (data_tr_bin - '0') * (int) (Math.pow(2, count_e - i));}TS_add = data_tr_valid;AISMessage += "  时隙增量:  ";AISMessage += String.valueOf(TS_add);if (TS_add == 0) {AISMessage += " 即 不在进行进一步的传输";AISMessage += "\r\n";} else {AISMessage += " 即 到下一个将要使用的时隙的偏移为:";AISMessage += String.valueOf(TS_add);AISMessage += "帧";AISMessage += "\r\n";}count_s = count_e + 1; // 提取完毕后,重置数据位起始值// 提取时隙数int TS_num; // 时隙数digit_num = 3; // 时隙数的位数为3位count_e = count_s + digit_num - 1; // 计算数据终止位data_tr_valid = 0;for (i = count_s; i <= count_e; i++) {data_tr_bin = sum_binary[i];data_tr_valid = data_tr_valid + (data_tr_bin - '0') * (int) (Math.pow(2, count_e - i));}TS_num = data_tr_valid;AISMessage += "  时隙数:   ";AISMessage += " 应分配的连续时隙的数量为:";AISMessage += String.valueOf(TS_num);AISMessage += "\r\n";count_s = count_e + 1; // 提取完毕后,重置数据位起始值// 提取保持标记int maintain_marker; // 保持标记digit_num = 1; // 保持标记的位数为1位count_e = count_s + digit_num - 1; // 计算数据终止位data_tr_valid = 0;for (i = count_s; i <= count_e; i++) {data_tr_bin = sum_binary[i];data_tr_valid = data_tr_valid + (data_tr_bin - '0') * (int) (Math.pow(2, count_e - i));}maintain_marker = data_tr_valid;AISMessage += "  保持标记:  ";AISMessage += String.valueOf(maintain_marker);if (maintain_marker == 1)AISMessage += "  即 时隙分配应多保留一帧";AISMessage += "\r\n";count_s = count_e + 1; // 提取完毕后,重置数据位起始值}AISLog.logInfo(AIS_Analyse.class, "Analysie123,"+"经度:"+RecentLongitude+",纬度:"+RecentLatitude+",mmsi:"+MMSI+",航速:"+boat.getCog()+",航向:"+boat.getSog());System.out.println("经度:"+RecentLongitude+",纬度:"+RecentLatitude+",mmsi:"+MMSI);boat.setLongitude(String.valueOf(RecentLongitude));boat.setLatitude(String.valueOf(RecentLatitude));boat.setMmsiId(String.valueOf(MMSI));BoatData(boat);System.out.println("AISMessage:"+AISMessage);}public void SendToRoll(double angle,SerialPort serialPort){if(serialPort!=null){String ctrPosition;ctrPosition = "PX";ctrPosition += String.valueOf(angle);ctrPosition += "\n\r";SerialPortManager.sendToPort(serialPort, ctrPosition.getBytes());}}public double point_remove(double a, int point_num){int k;double b;k = (int)(a * Math.pow(10, point_num));b = k / (Math.pow(10, point_num));return b;}public void PointAngleCount(String textBox12,String textBox13)     //指向角计算函数{double ship_true_angle = RecentCourse;                          //对地航向double base_angle;                                            //基站(点B)相对于船(点A)的方位角double base_angle_rad = 0;                                     //基站(点B)相对于船(点A)的方位角的弧度值double BaseToShip_rot_angle;                                   //基站对于船首向的方位角,即天线的目的位置                                                                   double x, y;String temp;String temp1;String temp_send;String temp_send1;double Base_Long = 0.0;           //基站经度double Base_Lat = 0.0;            //基站纬度double Ship_Long = point_remove((Math.PI/180*(ShipLongitude)), 8);             //船舶经度,角度转换为弧度double Ship_Lat = point_remove(Math.PI/180*(ShipLatitude), 8);//double Ship_Lat  = rad(90-RecentLatitude) ;                       //船舶相对于正北的纬度(90-纬度)switch (basechange){case 0:{if (textBox12 != ""){Base_Long = Double.parseDouble(textBox12);}else{Base_Long = 0.0;textBox12 = "0";}Base_Long = rad(Base_Long);         //岸基基站经度(单位为弧度)if (textBox13 != ""){Base_Lat =  Double.parseDouble(textBox13);                         }else{Base_Lat = 0.0;textBox13 = "0";}Base_Lat = rad(Base_Lat);           //岸基基站纬度(单位为弧度)                       break;}case 1:{Base_Long = rad(BaseLongitude);          //船载移动基站经度(单位为弧度)Base_Lat = rad(BaseLatitude);            //船载移动基站纬度(单位为弧度)break;}}//Base_Long = point_remove(rad(BaseLongitude), 8);                //基站经度(单位为弧度)//Base_Lat = point_remove(rad(BaseLatitude), 8);                  //基站纬度(单位为弧度)double Dif_Long = Base_Long - Ship_Long;                     //两点经度之差 y = Math.sin(Dif_Long) * Math.cos(Base_Lat);x = Math.cos(Ship_Lat) * Math.sin(Base_Lat) - Math.sin(Ship_Lat) * Math.cos(Base_Lat) * Math.cos(Dif_Long);if (y > 0){if (x > 0){base_angle_rad = Math.atan(y / x);}                   else if (x < 0){base_angle_rad = PI - Math.atan(-y / x);}                    else if (x == 0){base_angle_rad = PI / 2;}                   }else if (y < 0){if (x > 0){base_angle_rad = -Math.atan(-y / x);}                   else if (x < 0){base_angle_rad = Math.atan(y / x) - PI;}                    else if (x == 0){base_angle_rad = PI * 3 / 2;}                   }else if (y == 0){if (x > 0){base_angle_rad = 0;}                    else if (x < 0){base_angle_rad = PI;}                   else if (x == 0){temp = "两点重合";}                   }base_angle = unrad(base_angle_rad);//point_num=2;base_angle = point_remove(base_angle, 2);if (base_angle < 0){base_angle = base_angle + 360;}temp = String.valueOf(base_angle) + " 度";            //基站方位角temp_send = String.valueOf(base_angle);//如天线通信协议中以目的位置作为参数时,基站对于船首向的方位角,即天线的目的位置,直接根据通信协议写入控制信号if (base_angle > ship_true_angle)                                       //当基站方位角(β)大于航向角(α)时,则基站相对于船舶的方位角θ为(θ=β-α){BaseToShip_rot_angle = base_angle - ship_true_angle;}                else                                                                //当基站方位角(β)小于航向角(α)时,则基站相对于船舶的方位角θ为(θ=360-(α-β)){BaseToShip_rot_angle = 360 - (ship_true_angle - base_angle);}temp1 =  String.valueOf(BaseToShip_rot_angle) + " 度";            //天线指向角temp_send1 =  String.valueOf(BaseToShip_rot_angle);DestinationAngle = temp_send1;}public double rad(double angle)                    //角度转换为弧度{return angle * PI / 180.0;              //弧度 = 角度 * π / 180}public double unrad(double radian)                 //弧度转换为角度{return radian * 180.0 / PI;             //角度 = 弧度 * 180 / π}/**串口数据接收*/public static void comport1_DataReceived(String AIS_msg )//, SerialDataReceivedEventArgs e){//Boat boat = new Boat();{try{/** int num = port.getDataBits(); char[] buffer = new char[num];* SerialPortManager.readFromPort(port);//(buffer, 0, num);*/                 //AIS_msg = new String(buffer);char[] buffer  = AIS_msg.toCharArray();//#region CXY   获取GPRMCfor (char b: buffer){if (b == '$'){_recstatu = true;_buff.clear();}if (_recstatu)                           //是否处于接收数据包状态{_buff.add(String.valueOf(b));}if (b == '\n')            //帧尾{_recstatu = false;// HandleData(_buff.toArray());_buff.clear();}}//#endregion//textBox1 += AIS_msg;int number;number = buffer.length;String Window_TEXT = null;// textBox1.SelectionStart = textBox1.TextLength;//textBox1.ScrollToCaret();data_length = 0;                //单一语句长度int exclamation_num = 0;        //‘!’在语句中的位置int exclamation_count = 0;int enter_num = 0;          //‘\n’在语句中的位置int enter_count = 0;if (AIS_msg.contains("!"))                               //查找语句起始符‘!’{exclamation_count++;                                //‘!’计数exclamation_num = AIS_msg.indexOf('!');               //‘!’在语句中的位置}if (AIS_msg.contains("\n"))                             //查找语句终止符‘\n’{enter_count++;                                    //‘\n’计数enter_num = AIS_msg.indexOf('\n');                  //‘\n’在语句中的位置newline_count++;                                  //累计计算换行符个数}if (newline_count == 100)                              //如果“AIS数据接收显示”窗口显示的数据超过100行,则清空窗口{//textBox1.Clear();newline_count = 0;}                   //一个事件中语句传输完毕:if (exclamation_count != 0 && enter_count != 0 && exclamation_num < enter_num)     //一个事件中语句传输完毕,即语句中包含语句起始符‘!’和语句终止符‘\n’,且‘!’在前,‘\n’在后{data_count = 0;//CXY   解析GPS数据// MyFun.GPS_RMC_Parse(AIS_msg.substring(exclamation_num, enter_num - exclamation_num));for (int i = 0; i < AIS_msg.length(); i++){SingleDataBuffer[i] = buffer[i];data_count++;}data_length = data_count - 1;                       //获取语句长度                       if (Data_check() != 0)                          //语句传输完毕,开始解析{                    System.out.println("通过检查的数据------------------:"+AIS_msg);Data_repro();MainFrame.mDataValiableView.append(new String(AIS_msg) + "\r\n");//System.out.println("defi_m-----------------------------"+defi_m);switch (defi_m){case 1:Data_analyze_m123(AIS_msg);break;case 2:Data_analyze_m123(AIS_msg);break;case 3:Data_analyze_m123(AIS_msg);break;//case 5://Data_analyze_m5();//break;case 18:Data_analyze_m18(AIS_msg);break;case 19:Data_analyze_m19(AIS_msg);break;default:break;}if (basechange == 0){if (count_enable == 1 && MMSI_enable == 1){// BaseDistanceCount();//PointAngleCount();if (angle_send == 1){// AngleSend();}}}else if (basechange == 1){// if (count_enable == 1 && MMSI_enable != 0 && textBox5.Text != null){// BaseDistanceCount();//PointAngleCount();if (angle_send == 1){//       AngleSend();}}}// AIS_filesave_and_timer();}else{//textBox2.Clear();Window_TEXT = "当前语句未通过数据检查,无需解析\r\n";//textBox2.Text = Window_TEXT;System.out.println("当前语句未通过数据检查,无需解析");//textBox3.Clear();Window_TEXT = "当前语句未通过数据检查,无解析结果\r\n";//textBox3.Text = Window_TEXT;}}//一个事件中语句传输未完毕,使用多次事件传输:if (exclamation_count != 0 && enter_count == 0)     //一个事件中语句传输未完成,且该条数据为语句的第一条数据,即检测到语句起始符‘!’,没有语句终止符‘\n’{event_count = 1;                                //将该事件编号设为1,即该语句的第一条数据data_count = 0;for (i = 0; i < AIS_msg.length(); i++)                {SingleDataBuffer[data_count] = buffer[i];data_count++;}      }else if (event_count != 0 && enter_count == 0)      //一个事件中语句传输未完成,且该条数据为语句的第二条数据,即检测到事件计数为1,且没有语句终止符‘\n’{event_count = 2;                                //将该事件编号设为2,即该语句的第二条数据                                                                        for (i = 0; i < AIS_msg.length(); i++)                 {SingleDataBuffer[data_count] = buffer[i];data_count++;}        }else if (enter_count != 0)                          //一个事件中语句的剩余传输完成,且不包含下一条语句的起始部分,即该条数据为不包含语句起始符,且有语句终止符‘\n’{if (event_count != 0){event_count = 0;                            //语句的剩余传输完成,该条语句的传输事件结束,事件计数归零                                                                      for (i = 0; i < enter_num; i++)               {SingleDataBuffer[data_count] = buffer[i];data_count++;}data_length = data_count;               //获取语句长度                           if (Data_check() != 0)                      //语句传输完毕,开始解析{                               Data_repro();switch (defi_m){case 1:Data_analyze_m123(AIS_msg);break;case 2:Data_analyze_m123(AIS_msg);break;case 3:Data_analyze_m123(AIS_msg);break;//case 5://Data_analyze_m5();//break;case 18:Data_analyze_m18(AIS_msg);break;case 19:Data_analyze_m19(AIS_msg);break;default:break;}if (basechange == 0){if (count_enable == 1 && MMSI_enable == 1){//BaseDistanceCount();//PointAngleCount();if (angle_send == 1){// AngleSend();}}}else if (basechange == 1){// if (count_enable == 1 && MMSI_enable != 0 && textBox5.Text != null){//BaseDistanceCount();//PointAngleCount();if (angle_send == 1){//AngleSend();}}}//AIS_filesave_and_timer();               //}else{// textBox2.Clear();//textBox2.Text = "1.当前语句未通过数据检查,无需解析\r\n";System.out.println("当前语句未通过数据检查,无需解析");//textBox3.Clear();                                //textBox3.Text = "2.当前语句未通过数据检查,无解析结果\r\n";}                }if (event_count == 0 && exclamation_count != 0 && exclamation_num > enter_num)           //上一条语句传输完成,且该事件中包含下一条语句的起始部分{event_count = 1;data_count = 0;for (i = exclamation_num; i < AIS_msg.length(); i++){SingleDataBuffer[data_count] = buffer[i];data_count++;}             }}}catch (Exception e) {// TODO: handle exceptionAISLog.error(AIS_Analyse.class, e.getMessage());e.printStackTrace();}}}public static int Data_check(){int i;int check_number_count = 0;String StatusForAIS = null;String SingleAISData ="";        //语句解析结果int nt = 0;                       //","分隔符计数器int dn = 0;                       //需校验的有效字符计数器SSSint data_xor = 0;                  //异或计算中间变量            int check_end = 0;                 //校验终止位int separate_num = 0;       //消息分解总条数-int serial_num = 0;         //语句序号int id_num = 0;             //顺序语句标识int return_num = 0;         //数据返回码(消息识别码+数据起始位+数据终止位+拆分信息)//查找到校验和int check_sum = 0;int[] n = new int[2];for (i = 0; i < data_count; i++){if (SingleDataBuffer[i] == '*' && SingleDataBuffer[i] != '\0'){// puts("check_sum_in:");if (SingleDataBuffer[i + 1] >= '0' && SingleDataBuffer[i + 1] <= '9')  //校验和最高位转为十六进制n[1] = SingleDataBuffer[i + 1] - '0';else if (SingleDataBuffer[i + 1] >= 'A' && SingleDataBuffer[i + 1] <= 'Z')n[1] = (SingleDataBuffer[i + 1] - 'A') + 10;else if (SingleDataBuffer[i + 1] >= 'a' && SingleDataBuffer[i + 1] <= 'z')n[1] = (SingleDataBuffer[i + 1] - 'a') + 10;if (SingleDataBuffer[i + 2] >= '0' && SingleDataBuffer[i + 2] <= '9')  //校验和最低位转为十六进制n[0] = SingleDataBuffer[i + 2] - '0';else if (SingleDataBuffer[i + 2] >= 'A' && SingleDataBuffer[i + 2] <= 'Z')n[0] = (SingleDataBuffer[i + 2] - 'A') + 10;else if (SingleDataBuffer[i + 2] >= 'a' && SingleDataBuffer[i + 2] <= 'z')n[0] = (SingleDataBuffer[i + 2] - 'a') + 10;check_sum = n[1] * 16 + n[0];    //生成校验和             }else if (SingleDataBuffer[i] == '\0')break;}//END//查找地址域,并判断语句是否为AIVDM或AIVDO消息char[] ad_f = new char[5];          char[] AIVDM = { 'A', 'I', 'V', 'D', 'M' };char[] AIVDO = { 'A', 'I', 'V', 'D', 'O' };            for (i = 0; i < 5; i++){ad_f[i] = SingleDataBuffer[i + 1];if (ad_f[i] != AIVDM[i] && ad_f[i] != AIVDO[i]){StatusForAIS ="设备类型及语句类型不符合要求,无需解析\r\n";// textBox4.Clear();             //清空“当前语句解析状态显示”窗口 //textBox4.Text = StatusForAIS;System.out.println("status-------------"+StatusForAIS);sen_f = 0;return 0;}else if (ad_f[i] == AIVDM[i]){sen_f = 1;                  //接收到其他船舶的AIS信息,则语句类型识别码为1}                    else if (ad_f[i] == AIVDO[i]){sen_f = 2;                  //接收到其他船舶的AIS信息,则语句类型识别码为2}                  }if (sen_f == 1){StatusForAIS ="该语句为AIVDM消息,可以进行解析\r\n";  System.out.println("status-------------"+StatusForAIS);check_number_count += 1;}else if (sen_f == 2){StatusForAIS ="该语句为AIVDO消息,可以进行解析\r\n";       System.out.println("status-------------"+StatusForAIS);check_number_count += 1;}// END//找到数据域,并进行异或运算,并对数据进行校验for (i = 0; i < data_count; i++){if (SingleDataBuffer[i] == ','){nt++;if (nt == 5)        //根据分隔符,查找数据域起始位{               data_start = i + 2;                  continue;}else if (nt == 6)   //根据分隔符,查找数据域终止位{    check_end = i + 2;data_end = i;          }}elsecontinue;}dn = check_end - 2;             //异或运算次数data_xor = SingleDataBuffer[1];    //异或运算中间变量初始化for (i = 1; i <= dn; i++)           //对数据进行异或运算{           data_xor = data_xor ^ SingleDataBuffer[i + 1];            }if (data_xor != check_sum)          //判断校验结果是否正确    {       StatusForAIS = "校验和错误\r\n";System.out.println("校验和错误");// textBox4.Text = StatusForAIS;//textBox4.Clear();             //清空“当前语句解析状态显示”窗口              return 0;}else{check_number_count += 1;        System.out.println("校验和正确");StatusForAIS = "校验和正确\r\n";}if (check_number_count == 3){// textBox2.Clear();         //清空“当前AIS数据显示”窗口for (i = 0; i < data_length; i++){         SingleAISData += SingleDataBuffer[i];}System.out.println("aisdata"+SingleAISData);//textBox2.Text = SingleAISData;}// END//拆分语句识别//int separate_num = 0;       //消息分解总条数// int serial_num = 0;         //语句序号//int id_num = 0;             //顺序语句标识//int return_num = 0;         //数据返回码(消息识别码+数据起始位+数据终止位+拆分信息)nt = 0;                 //","分隔符计数器归零for (i = 0; i < 82; i++){if (SingleDataBuffer[i] == ','){nt++;if (nt == 1)                                    //根据分隔符,查找消息分解总条数{         separate_num = SingleDataBuffer[i + 1] - '0';              continue;}else if (nt == 2)                               //根据分隔符,查找语句序号{    serial_num = SingleDataBuffer[i + 1] - '0';            continue;}else if (nt == 3 && separate_num > 1)           //根据分隔符,查找顺序语句标识{            id_num = SingleDataBuffer[i + 1] - '0';            continue;}}elsecontinue;}//查找数据域的第一个参数:“消息识别码”,并判断该消息是否含有可用信息,及是否进行解析//char start_char = SingleDataBuffer[data_start - 1];defi_m = 0;  char[] y;//消息识别码初始化y = str_to_bi( SingleDataBuffer[data_start - 1] ).toCharArray();System.out.println("y-----"+new String(y));for (i = 1; i <= 6; i++){   defi_m = defi_m + (y[i-1] - '0') * (int)(Math.pow(2, 6 - i));  //System.out.println(i+"-----"+defi_m+",y.i="+y[i-1]);}System.out.println("解析到的数据:defi_m--------"+defi_m);if (separate_num == 1 || serial_num == 1)          //对单一语句或拆分语句的{       if (defi_m != 1 && defi_m != 2 && defi_m != 3 && defi_m != 18 && defi_m != 19)                              //暂时不考虑静态信息,只解析带有经纬度信息的AIS语句 2017.10.26{                          //textBox4.Clear();           //清空“当前语句解析状态显示”窗口 System.out.println("此消息不属于需使用的消息类型(消息1、2、3、5、18、19),无需解析");return 0;}else{check_number_count += 1;               StatusForAIS = "该消息内包含可用信息,可以进行参数提取\r\n";System.out.println(StatusForAIS);//textBox4.Text = StatusForAIS;                    }          }else//AfxMessageBox("该消息为拆分语句的后续语句,可以直接进行参数提取\n");{StatusForAIS += "该消息为拆分语句的后续语句,可以合并后进行参数提取\r\n";//textBox4.Clear();              //清空“当前语句解析状态显示”窗口  System.out.println(StatusForAIS);return 0;}//textBox4.Clear();            //清空“当前语句解析状态显示”窗口if (check_number_count == 3){//textBox2.Clear();         //清空“当前AIS数据显示”窗口for (i = 0; i < data_length; i++){         SingleAISData += SingleDataBuffer[i];}System.out.println("SingleAISData--------"+SingleAISData);}// END// 返回值整合(当语句未拆分时,返回消息识别码、数据域起始值和数据域结束值;当拆分语句时,返回值增加语句序号和顺序语句标识)if (separate_num == 1){//("该条语句未进行拆分\n\n");  return_num = defi_m * 10000000 + data_start * 100000 + data_end * 1000 + 100;// textBox4.Text = StatusForAIS;return return_num;}else{//("该条语句为拆分语句,共拆分为%d条,该条语句的语句序号为:%d\n\n");return_num = defi_m * 10000000 + data_start * 100000 + data_end * 1000 + separate_num * 100 + serial_num * 10 + id_num;return return_num;}          }public static int Data_repro(){//消息识别完毕,取数据域内容int dn_data;  //数据域有效字符计数器int i;//string temp;StringBuilder AISDataUnpack = new StringBuilder();StringBuilder y = new StringBuilder();dn_data = data_end - data_start + 1;for (i = 0; i < dn_data; i++){String tempStr = str_to_bi(SingleDataBuffer[data_start + i - 1]).toString();if(tempStr!=null) {y =y.append(tempStr); } else {System.out.println("null Error");}            }AISDataUnpack = y;// textBox3.Clear();                       //清空“数据解封装结果显示”窗口// textBox3.Text += "\r\n";// textBox3.Text += "语句解封装后对应的二进制码:\r\n";//textBox3.Text += AISDataUnpack;
//sum_binary = AISDataUnpack.toString().toCharArray();return 0;}     /**有效字符转化为6位二进制*/public static  String  str_to_bi(char a)     //有效字符转化为6位二进制{int rb;       //转化中间变量return_binaryrb = a + 0x28;if (rb > 0x80)rb = rb + 0x20;elserb = rb + 0x28;      //将有效字符转化成十六进制,rb = rb & 0x3f;   //取后六位//将十六进制数转化为二进制字符char[] bi = new char[6];  //保留数组数据至程序执行完成int i;int r, n;               //声明余数和除数n = rb;for (i = 1; i <= 6; i++){if (n != 0){r = n % 2;n = n / 2;if(r == 1){bi[i - 1] = '1';}else{bi[i - 1] = '0';}}else{bi[i - 1] = '0';}}for (i = 1; i <= 3; i++)      //逆序输出{char m;m = bi[i - 1];bi[i - 1] = bi[6 - i];bi[6 - i] = m;}String str = new String(bi);return str;}/**消息18数据解析*/public static void Data_analyze_m18(String AISMessage)   //消息18数据解析{int i=0;Boat boat = new Boat();            String temp1;String temp2;String temp3;String temp4;//String AISMessage = null;String MMSI_OUT;//String temp_MMSI;String temp_BaseShipMMSI = null;MMSI_enable = 0;//temp_MMSI = textBox5.Text;                          //读取目前跟踪的用户船MMSI码//temp_BaseShipMMSI = textBox18.Text;                  //读取当前基站船的MMSI码AISMessage += "\r\n\r\n";AISMessage += "解析后的数据如下:\r\n\r\n";// 二进制数据向有效数据转换char data_tr_bin = 0;       //二进制数据中间变量初始化int data_tr_valid = 0;    //有效数据中间变量初始化int count_s = 0;          //数据位起始值int count_e = 0;          //数据位终止值int digit_num;          //数据位数// 提取消息识别码int message_identifier;  //消息识别码digit_num = 6;           //消息识别码的位数为6位count_e = count_s + digit_num - 1;   //计算数据终止位for (i = count_s; i <= count_e; i++){data_tr_bin = sum_binary[i];data_tr_valid = data_tr_valid + (data_tr_bin - '0') * (int)(Math.pow(2, count_e - i));}message_identifier = data_tr_valid;if (message_identifier == 18){    AISMessage += "消息识别码:  ";AISMessage += message_identifier;AISMessage += "  即 消息类型为“标准B类设备位置报告”。\r\n";}count_s = count_e + 1;     //提取完毕后,重置数据位起始值// END(提取消息识别码)// 提取转发指示符int transpond_indicate;   //转发指示符digit_num = 2;           //转发指示符的位数为2位count_e = count_s + digit_num - 1;   //计算数据终止位data_tr_valid = 0;for (i = count_s; i <= count_e; i++){data_tr_bin = sum_binary[i];data_tr_valid = data_tr_valid + (data_tr_bin - '0') * (int)(Math.pow(2, count_e - i));}transpond_indicate = data_tr_valid;AISMessage += "转发指示符:  ";AISMessage += transpond_indicate;AISMessage += "  即 该条消息被转发的次数为:";AISMessage += transpond_indicate;AISMessage += "  次。\r\n";count_s = count_e + 1;     //提取完毕后,重置数据位起始值// END(提取转发指示符)// 提取用户识别码,即MMSI码int MMSI;     //用户识别码digit_num = 30;           //用户识别码,即MMSI码的位数为30位count_e = count_s + digit_num - 1;   //计算数据终止位data_tr_valid = 0;for (i = count_s; i <= count_e; i++){data_tr_bin = sum_binary[i];data_tr_valid = data_tr_valid + (data_tr_bin - '0') * (int)(Math.pow(2, count_e - i));                }MMSI = data_tr_valid;AISMessage += "用户识别码(即MMSI码):  ";AISMessage += MMSI;AISMessage += "\r\n";count_s = count_e + 1;     //提取完毕后,重置数据位起始值MMSI_OUT = String.valueOf(MMSI);       //提取当前信息的MMSI码,用于船舶识别System.out.print("MMSI_OUT:"+MMSI_OUT);// END(提取用户识别码,即MMSI码)// 提取为地区或区域应用保留digit_num = 8;           //为地区或区域应用保留的位数为8位count_e = count_s + digit_num - 1;   //计算数据终止位data_tr_valid = 0;for (i = count_s; i <= count_e; i++){data_tr_bin = sum_binary[i];data_tr_valid = data_tr_valid + (data_tr_bin - '0') * (int)(Math.pow(2, count_e - i));              }AISMessage += "为地区性应用所保留:  ";AISMessage += data_tr_valid;AISMessage += "\r\n";count_s = count_e + 1;     //提取完毕后,重置数据位起始值// END(提取为地区或区域应用保留)// 提取对地航速double speed_over_ground;     //对地航速digit_num = 10;           //对地航速的位数为10位count_e = count_s + digit_num - 1;   //计算数据终止位data_tr_valid = 0;for (i = count_s; i <= count_e; i++){data_tr_bin = sum_binary[i];data_tr_valid = data_tr_valid + (data_tr_bin - '0') * (int)(Math.pow(2, count_e - i));             }speed_over_ground = (float)(data_tr_valid) / 10;AISMessage += "对地航速:  ";AISMessage += speed_over_ground;boat.setSog(speed_over_ground+"kn");AISMessage += " kn";AISMessage += "\r\n";count_s = count_e + 1;     //提取完毕后,重置数据位起始值// END(提取对地航速)// 提取船位精确度int position_accuracy;     //船位精确度digit_num = 1;           //船位精确度的位数为1位count_e = count_s + digit_num - 1;   //计算数据终止位data_tr_valid = 0;for (i = count_s; i <= count_e; i++){data_tr_bin = sum_binary[i];data_tr_valid = data_tr_valid + (data_tr_bin - '0') * (int)(Math.pow(2, count_e - i));               }position_accuracy = data_tr_valid;AISMessage += "船位精确度:  ";AISMessage += position_accuracy;if (data_tr_valid == 1)AISMessage += " 即 高精度(<10m,DGNSS接收机的差分模式)";else if (data_tr_valid == 0)AISMessage += " 即 低精度(>10m,GNSS接收机或其他电子定位装置的自主模式)";AISMessage += "\r\n";count_s = count_e + 1;     //提取完毕后,重置数据位起始值// END(提取船位精确度)// 提取经度double longitude;      //经度digit_num = 28;           //经度的位数为28位count_e = count_s + digit_num - 1;   //计算数据终止位data_tr_valid = 0;int hour;int minute;double second;if (sum_binary[count_s] != '1'){//("经度是正数,即东经\n");for (i = count_s; i <= count_e; i++){data_tr_bin = sum_binary[i];data_tr_valid = data_tr_valid + (data_tr_bin - '0') * (int)(Math.pow(2, count_e - i));    //System.out.print("原始经度数据:"+data_tr_valid);}longitude = (float)(data_tr_valid) / 10000;hour = (int)(data_tr_valid / 10000 / 60);minute = (int)(longitude - hour * 60);second = (longitude - hour * 60 - minute) * 60;RecentLongitude = longitude / 60;  //将经度信息传递给全局变量,单位为度AISMessage += "经度:  ";AISMessage += "东经 ";AISMessage += hour;AISMessage += "度";AISMessage += minute;              AISMessage += "分";AISMessage += second;AISMessage += "秒";AISMessage += "\r\n";}else if (sum_binary[count_s] == '1'){//("经度是负数,即西经\n");for (i = count_s + 1; i <= count_e; i++){if (sum_binary[i] == '0')data_tr_bin = '1';elsedata_tr_bin = '0';data_tr_valid = data_tr_valid + (data_tr_bin - '0') * (int)(Math.pow(2, count_e - i));//System.out.print("原始经度数据:"+data_tr_valid);}data_tr_valid = data_tr_valid + 1;  //经度的绝对值longitude = (float)(data_tr_valid) / 10000;hour = (int)(data_tr_valid / 10000 / 60);minute = (int)(longitude - hour * 60);second = (longitude - hour * 60 - minute) * 60;longitude = -longitude;      //经度的真实值                                        RecentLongitude = longitude / 60;  //将经度信息传递给全局变量AISMessage += "经度:  ";AISMessage += "西经 ";AISMessage += hour;AISMessage += "度";AISMessage += minute;AISMessage += "分";AISMessage += second;AISMessage += "秒";AISMessage += "\r\n";}count_s = count_e + 1;     //提取完毕后,重置数据位起始值// END(提取经度)// 提取纬度double latitude;    //纬度digit_num = 27;           //纬度的位数为27位count_e = count_s + digit_num - 1;   //计算数据终止位data_tr_valid = 0;if (sum_binary[count_s] != '1'){//("纬度是正数,即北纬\n");for (i = count_s; i <= count_e; i++){data_tr_bin = sum_binary[i];data_tr_valid = data_tr_valid + (data_tr_bin - '0') * (int)(Math.pow(2, count_e - i));   //System.out.print("原始经度数据:"+data_tr_valid);}latitude = (float)(data_tr_valid) / 10000;hour = (int)(data_tr_valid / 10000 / 60);minute = (int)(latitude - hour * 60);second = (latitude - hour * 60 - minute) * 60;RecentLatitude = latitude / 60;    //将当前纬度传递给全局变量AISMessage += "纬度:  ";AISMessage += "北纬 ";AISMessage += hour;AISMessage += "度";AISMessage += minute;AISMessage += "分";AISMessage += second;AISMessage += "秒";AISMessage += "\r\n";}else if (sum_binary[count_s] == '1'){//("纬度是负数,即南纬\n");for (i = count_s + 1; i <= count_e; i++){if (sum_binary[i] == '0')data_tr_bin = '1';elsedata_tr_bin = '0';data_tr_valid = data_tr_valid + (data_tr_bin - '0') * (int)(Math.pow(2, count_e - i));//System.out.print("原始经度数据:"+data_tr_valid);}data_tr_valid = data_tr_valid + 1;  //纬度的绝对值                                              latitude = (float)(data_tr_valid) / 10000;hour = (int)(data_tr_valid / 10000 / 60);minute = (int)(latitude - hour * 60);second = (latitude - hour * 60 - minute) * 60;latitude = -latitude;      //纬度的真实值                                      RecentLatitude = latitude / 60;    //将当前纬度传递给全局变量AISMessage += "纬度:  ";AISMessage += "北纬 ";AISMessage += hour;AISMessage += "度";AISMessage += minute;AISMessage += "分";AISMessage += second;AISMessage += "秒";AISMessage += "\r\n";}count_s = count_e + 1;     //提取完毕后,重置数据位起始值// END(提取纬度)// 提取对地航向double course_over_the_ground;digit_num = 12;           //对地航向的位数为12位count_e = count_s + digit_num - 1;   //计算数据终止位data_tr_valid = 0;for (i = count_s; i <= count_e; i++){data_tr_bin = sum_binary[i];data_tr_valid = data_tr_valid + (data_tr_bin - '0') * (int)(Math.pow(2, count_e - i));             }course_over_the_ground = (float)(data_tr_valid) / 10;AISMessage += "对地航向:  ";AISMessage += course_over_the_ground;boat.setCog(course_over_the_ground+"度");AISMessage += "度";if (course_over_the_ground >= 360)AISMessage += " 即 默认值(不可用)";AISMessage += "\r\n";count_s = count_e + 1;     //提取完毕后,重置数据位起始值// END(提取对地航向)// 提取真航向int true_course;     //真航向digit_num = 9;           //真航向的位数为9位count_e = count_s + digit_num - 1;   //计算数据终止位data_tr_valid = 0;for (i = count_s; i <= count_e; i++){data_tr_bin = sum_binary[i];data_tr_valid = data_tr_valid + (data_tr_bin - '0') * (int)(Math.pow(2, count_e - i));          }true_course = data_tr_valid;AISMessage += "真航向:  ";AISMessage += true_course;AISMessage += "度";if (true_course == 511)AISMessage += " 即 默认值(不可用)";AISMessage += "\r\n";count_s = count_e + 1;     //提取完毕后,重置数据位起始值// END(提取真航向)// 提取时间标记int time_marker;    //时间标记digit_num = 6;           //时间标记的位数为6位count_e = count_s + digit_num - 1;   //计算数据终止位data_tr_valid = 0;for (i = count_s; i <= count_e; i++){data_tr_bin = sum_binary[i];data_tr_valid = data_tr_valid + (data_tr_bin - '0') * (int)(Math.pow(2, count_e - i));           }time_marker = data_tr_valid;AISMessage += "时间标记:  ";AISMessage += "报告产生时的UTC秒: ";AISMessage += time_marker;AISMessage += "\r\n";count_s = count_e + 1;     //提取完毕后,重置数据位起始值// END(提取时间标记)// 提取为地区性应用所保留digit_num = 4;           //为地区性应用所保留的位数为4位count_e = count_s + digit_num - 1;   //计算数据终止位data_tr_valid = 0;for (i = count_s; i <= count_e; i++){data_tr_bin = sum_binary[i];data_tr_valid = data_tr_valid + (data_tr_bin - '0') * (int)(Math.pow(2, count_e - i));             }AISMessage += "为地区性应用所保留:  ";AISMessage += data_tr_valid;AISMessage += "\r\n";count_s = count_e + 1;     //提取完毕后,重置数据位起始值// END(提取为地区性应用所保留)// 提取备用位digit_num = 4;           //备用位的位数为4位count_e = count_s + digit_num - 1;   //计算数据终止位data_tr_valid = 0;for (i = count_s; i <= count_e; i++){data_tr_bin = sum_binary[i];data_tr_valid = data_tr_valid + (data_tr_bin - '0') * (int)(Math.pow(2, count_e - i));              }AISMessage += "备用位:  ";AISMessage += data_tr_valid;AISMessage += "\r\n";count_s = count_e + 1;     //提取完毕后,重置数据位起始值// END(提取备用位)// 提取RAIM标志int RAIM;     //RAIM标志digit_num = 1;           //RAIM标志的位数为1位count_e = count_s + digit_num - 1;   //计算数据终止位data_tr_valid = 0;for (i = count_s; i <= count_e; i++){data_tr_bin = sum_binary[i];data_tr_valid = data_tr_valid + (data_tr_bin - '0') * (int)(Math.pow(2, count_e - i));              }RAIM = data_tr_valid;AISMessage += "RAIM标志:  ";AISMessage += RAIM;if (RAIM == 1)AISMessage += " 即 RAIM使用";elseAISMessage += " 即 RAIM未使用(默认)";AISMessage += "\r\n";count_s = count_e + 1;     //提取完毕后,重置数据位起始值// END(提取RAIM标志)// 提取通信状态选择标志int conmunication_choose;digit_num = 1;           //通信状态选择标志的位数为1位count_e = count_s + digit_num - 1;   //计算数据终止位data_tr_valid = 0;for (i = count_s; i <= count_e; i++){data_tr_bin = sum_binary[i];data_tr_valid = data_tr_valid + (data_tr_bin - '0') * (int)(Math.pow(2, count_e - i));              }conmunication_choose = data_tr_valid;AISMessage += "通信状态选择标志:  ";AISMessage += conmunication_choose;switch (conmunication_choose){case 0:AISMessage += " 即 SOTDMA通信状态";break;case 1:                    AISMessage += " 即 ITDMA通信状态";break;default:break;}AISMessage += "\r\n";count_s = count_e + 1;     //提取完毕后,重置数据位起始值// END(提取通信状态选择标志)// 提取通信状态AISMessage += "通信状态:  ";if (conmunication_choose == 0)         //通信状态为SOTDMA{AISMessage += "SOTDMA通信状态";AISMessage += "\r\n";//提取同步状态int synchronization_status;    //同步状态digit_num = 2;           //同步状态的位数为2位count_e = count_s + digit_num - 1;   //计算数据终止位data_tr_valid = 0;for (i = count_s; i <= count_e; i++){data_tr_bin = sum_binary[i];data_tr_valid = data_tr_valid + (data_tr_bin - '0') * (int)(Math.pow(2, count_e - i));            }synchronization_status = data_tr_valid;AISMessage += "  同步状态:  ";AISMessage += synchronization_status;if (synchronization_status == 0){                   AISMessage += " 即 直接获取UTC";AISMessage += "\r\n";}else if (synchronization_status == 1){AISMessage += " 即 间接获取UTC";AISMessage += "\r\n";}else if (synchronization_status == 2){AISMessage += " 即 台站同步于基地台";AISMessage += "\r\n";}else if (synchronization_status == 3){AISMessage += " 即 台站同步于另一个接受台数量最多的台站";AISMessage += "\r\n";}count_s = count_e + 1;     //提取完毕后,重置数据位起始值//提取时隙超时int TS_overtime;    //时隙超时digit_num = 3;           //时隙超时的位数为3位count_e = count_s + digit_num - 1;   //计算数据终止位data_tr_valid = 0;for (i = count_s; i <= count_e; i++){data_tr_bin = sum_binary[i];data_tr_valid = data_tr_valid + (data_tr_bin - '0') * (int)(Math.pow(2, count_e - i));                 }TS_overtime = data_tr_valid;AISMessage += "  时隙超时:  ";AISMessage += TS_overtime;if (TS_overtime == 0){AISMessage += " 即 这是该时隙最后一次传输";AISMessage += "\r\n";}else{AISMessage += " 即 在时隙改变前将保留该时隙 ";AISMessage += TS_overtime;AISMessage += " 帧";AISMessage += "\r\n";}count_s = count_e + 1;     //提取完毕后,重置数据位起始值//提取子信息int child_information;    //子信息digit_num = 14;           //子信息的位数为14位count_e = count_s + digit_num - 1;   //计算数据终止位data_tr_valid = 0;AISMessage += "  子信息:    ";if (TS_overtime == 3 || TS_overtime == 5 || TS_overtime == 7){for (i = count_s; i <= count_e; i++){data_tr_bin = sum_binary[i];data_tr_valid = data_tr_valid + (data_tr_bin - '0') * (int)(Math.pow(2, count_e - i));               }child_information = data_tr_valid;AISMessage += " 本台当前接收到其他台站数量为:";AISMessage += child_information;AISMessage += "\r\n";}else if (TS_overtime == 2 || TS_overtime == 4 || TS_overtime == 6){for (i = count_s; i <= count_e; i++){data_tr_bin = sum_binary[i];data_tr_valid = data_tr_valid + (data_tr_bin - '0') * (int)(Math.pow(2, count_e - i));                      }child_information = data_tr_valid;AISMessage += " 用于该发射的时隙号为:";AISMessage += child_information;AISMessage += "\r\n";}else if (TS_overtime == 1){int UTC_hour;int UTC_min;          AISMessage += " UTC: ";for (i = count_s; i <= count_s + 4; i++){data_tr_bin = sum_binary[i];data_tr_valid = data_tr_valid + (data_tr_bin - '0') * (int)(Math.pow(2, count_s + 4 - i));                      }UTC_hour = data_tr_valid;data_tr_valid = 0;AISMessage += UTC_hour;AISMessage += " 小时";count_s = count_s + 5;for (i = count_s; i <= count_s + 6; i++){data_tr_bin = sum_binary[i];data_tr_valid = data_tr_valid + (data_tr_bin - '0') * (int)(Math.pow(2, count_s + 6 - i));                     }UTC_min = data_tr_valid;AISMessage += UTC_min;AISMessage += " 分";AISMessage += "\r\n";}else if (TS_overtime == 0){for (i = count_s; i <= count_e; i++){data_tr_bin = sum_binary[i];data_tr_valid = data_tr_valid + (data_tr_bin - '0') * (int)(Math.pow(2, count_e - i));                      }child_information = data_tr_valid;AISMessage += " 时隙偏移量为:";AISMessage += child_information;AISMessage += "\r\n";}count_s = count_e + 1;     //提取完毕后,重置数据位起始值}else if (conmunication_choose == 1)         //通信状态为ITDMA{AISMessage += "ITDMA通信状态";AISMessage += "\r\n";//提取同步状态int synchronization_status;    //同步状态digit_num = 2;           //同步状态的位数为2位count_e = count_s + digit_num - 1;   //计算数据终止位data_tr_valid = 0;for (i = count_s; i <= count_e; i++){data_tr_bin = sum_binary[i];data_tr_valid = data_tr_valid + (data_tr_bin - '0') * (int)(Math.pow(2, count_e - i));//printf("%d\n",data_tr_valid);}synchronization_status = data_tr_valid;AISMessage += "  同步状态:  ";AISMessage += synchronization_status;if (synchronization_status == 0){           AISMessage += " 即 直接获取UTC";AISMessage += "\r\n";}else if (synchronization_status == 1){AISMessage += " 即 间接获取UTC";AISMessage += "\r\n";}else if (synchronization_status == 2){AISMessage += " 即 台站同步于基地台";AISMessage += "\r\n";}else if (synchronization_status == 3){AISMessage += " 即 台站同步于另一个接受台数量最多的台站";AISMessage += "\r\n";}count_s = count_e + 1;     //提取完毕后,重置数据位起始值//提取时隙增量int TS_add;    //时隙增量digit_num = 13;           //时隙增量的位数为13位count_e = count_s + digit_num - 1;   //计算数据终止位data_tr_valid = 0;for (i = count_s; i <= count_e; i++){data_tr_bin = sum_binary[i];data_tr_valid = data_tr_valid + (data_tr_bin - '0') * (int)(Math.pow(2, count_e - i));                  }TS_add = data_tr_valid;AISMessage += "  时隙增量:  ";AISMessage += TS_add;if (TS_add == 0){AISMessage += " 即 不在进行进一步的传输";AISMessage += "\r\n";}else{AISMessage += " 即 到下一个将要使用的时隙的偏移为:";AISMessage += TS_add;AISMessage += "帧";AISMessage += "\r\n";}count_s = count_e + 1;     //提取完毕后,重置数据位起始值//提取时隙数int TS_num;    //时隙数digit_num = 3;           //时隙数的位数为3位count_e = count_s + digit_num - 1;   //计算数据终止位data_tr_valid = 0;for (i = count_s; i <= count_e; i++){data_tr_bin = sum_binary[i];data_tr_valid = data_tr_valid + (data_tr_bin - '0') * (int)(Math.pow(2, count_e - i));                  }TS_num = data_tr_valid;AISMessage += "  时隙数:   ";AISMessage += " 应分配的连续时隙的数量为:";AISMessage += TS_num;AISMessage += "\r\n";count_s = count_e + 1;     //提取完毕后,重置数据位起始值//提取保持标记int maintain_marker;    //保持标记digit_num = 1;           //保持标记的位数为1位count_e = count_s + digit_num - 1;   //计算数据终止位data_tr_valid = 0;for (i = count_s; i <= count_e; i++){data_tr_bin = sum_binary[i];data_tr_valid = data_tr_valid + (data_tr_bin - '0') * (int)(Math.pow(2, count_e - i));                 }maintain_marker = data_tr_valid;AISMessage += "  保持标记:  ";AISMessage += maintain_marker;if (maintain_marker == 1)AISMessage += "  即 时隙分配应多保留一帧";AISMessage += "\r\n";count_s = count_e + 1;   //提取完毕后,重置数据位起始值}/*//仅提取“通信状态”数值for(i=count_s;i<=count_e;i++)   {data_tr_bin=s[i];data_tr_valid =data_tr_valid+(data_tr_bin-'0')*(int)(Math.Pow(2,count_e-i));  //printf("%d\n",data_tr_valid);}fprintf(w18,"%s","通信状态:  ");fprintf(w18,"%d",data_tr_valid);fprintf(w18,"%s","\n");count_s=count_e+1;     //提取完毕后,重置数据位起始值*/// END(提取通信状态)//if (MMSI_OUT == temp_MMSI)                      //如果该条信息是选中的用户船舶信息,则可进行参数提取和方位计算if (sen_f == 2){         AISMessage += "\r\n";AISMessage += "该条信息是选中的用户船舶信息,可进行参数提取和方位计算";MMSI_enable = 1;//textBox5.Clear();//textBox5.Text = MMSI_OUT;temp1 = String.valueOf(speed_over_ground);//textBox19.Clear();// textBox19.Text = temp1;temp2 = String.valueOf(RecentLongitude);//temp2 = RecentLongitude.ToString();//textBox22.Clear();                          //清空“船舶信息显示的经度”窗口                // textBox22.Text = temp2;ShipLongitude = RecentLongitude;temp3 = String.valueOf(RecentLatitude);//temp3 = RecentLatitude.ToString();//textBox20.Clear();                          //清空“船舶信息显示的纬度”窗口//textBox20.Text = temp3;ShipLatitude = RecentLatitude;RecentCourse = course_over_the_ground;        //将对地航向信息传递给全局变量//RecentCourse=true_course;                    //将真航向信息传递给全局变量if (RecentCourse >= 0 && RecentCourse < 360){temp4 = String.valueOf(RecentCourse);}else{RecentCourse = 0;                           //当航向信息不可用时,则指向角的计算将不考虑航向信息temp4 = "不可用";}/*if(RecentCourse!=511){temp4.Format( "%d",RecentCourse);}else{RecentCourse=0;    temp4="不可用";}*///textBox21.Clear();          //清空“船舶信息显示的真航向”窗口//textBox21.Text = temp4;                               }else if (MMSI_OUT == temp_BaseShipMMSI)             //如果该条信息是跟踪的基站船舶信息,则可进行参数提取和方位计算{          AISMessage += "\r\n";AISMessage += "该条信息是跟踪的基站船舶信息,则可进行参数提取和方位计算";MMSI_enable = 2;temp2 = String.valueOf(RecentLongitude);//temp2 = RecentLongitude.ToString();//textBox17.Clear();                          //清空“船载基站信息显示的经度”窗口               //textBox17.Text = temp2;BaseLongitude = RecentLongitude;temp3 =String.valueOf(RecentLatitude);//temp3 = RecentLatitude.ToString();//textBox15.Clear();                          //清空“船载基站信息显示的纬度”窗口//textBox15.Text = temp3;BaseLatitude = RecentLatitude;}else                                                //如果该条语句不是选中船舶(用户船或基站船)的信息,则无需进行参数提取和方位计算     {       AISMessage += "\r\n";AISMessage += "该条信息不是选中的船舶信息,无需进行方位计算";MMSI_enable = 0;}AISLog.logInfo(AIS_Analyse.class, "Analysie18,"+"经度:"+RecentLongitude+",纬度:"+RecentLatitude+",mmsi:"+MMSI+",航速:"+boat.getCog()+",航向:"+boat.getSog());System.out.println("经度:"+RecentLongitude+",纬度:"+RecentLatitude+",mmsi:"+MMSI);boat.setLongitude(String.valueOf(RecentLongitude));boat.setLatitude(String.valueOf(RecentLatitude));boat.setMmsiId(String.valueOf(MMSI));BoatData(boat);System.out.println(AISMessage);//textBox3.Text += AISMessage;
}/**船舶经纬度是否改变*/ public static Boolean PlaceChange(Boat boat,String Longtitude,String Latitude){if(!Longtitude.equals(boat.getLongitude())||!Latitude.equals(boat.getLatitude())){return true;} else {return false;}}/**判断是否是新船*/public static Boolean NewBoat(Boat boat){List<Boat> nowBoatList = boatMapper.getAllBoat();if(nowBoatList!=null){for(Boat boatx:nowBoatList){if(boat.getMmsiId().equals(boatx.getMmsiId())){return false;}} } else{System.out.print("没有船只");return true;}return true;}/**船舶数据入库*/public static void BoatData(String Longitude,String Latitude,String MMSI){Boat boat = new Boat();boat.setLatitude(Latitude);boat.setLongitude(Longitude);boat.setMmsiId(MMSI);boat.setId(MiscUtil.getUUID());boat.setStatus("1");if(NewBoat(boat)){Msg msg = boatService.insert(boat);System.out.print(msg.getMsg());} else {boat = boatMapper.selectByMMSI(MMSI);if(PlaceChange(boat, Longitude, Latitude)){boat.setLongitude(Longitude);boat.setLatitude(Latitude);int result =   boatService.updateByPrimaryKey(boat); System.out.print(result);} else {System.out.print("位置没有改变");}    }}/**船舶数据入库*/public static void BoatData(Boat boat){Boat boat2 = new Boat();boat2.setLatitude(boat.getLatitude());boat2.setLongitude(boat.getLongitude());boat2.setMmsiId(boat.getMmsiId());boat2.setId(MiscUtil.getUUID());boat2.setStatus("1");boat2.setCog(boat.getCog());boat2.setSog(boat.getSog());if(NewBoat(boat2)){Msg msg = boatService.insert(boat2);System.out.print(msg.getMsg());Msg msg2 = boatHistoryService.insert(boat2);System.out.print(msg2.getMsg());} else {boat = boatMapper.selectByMMSI(boat.getMmsiId());boat.setCog(boat2.getCog());boat.setSog(boat2.getSog());boat.setLatitude(boat.getLongitude());boat.setLatitude(boat.getLatitude());Msg msg2 = boatHistoryService.insert(boat);System.out.print(msg2.getMsg());boatMapper.updateByPrimaryKey(boat);/*if(PlaceChange(boat, boat.getLongitude(), boat.getLatitude())){boat.setLongitude(boat.getLongitude());boat.setLatitude(boat.getLatitude());int result =   boatService.updateByPrimaryKey(boat); System.out.print(result);} else {System.out.print("位置没有改变");}     */}}/**消息19数据解析*/public static void Data_analyze_m19(String AISMessage)        //消息19数据解析{int i=0, j=0;Boat boat = new Boat(); String temp1;String temp2;String temp3;String temp4;// String AISMessage = null;String MMSI_OUT;String temp_MMSI;String temp_BaseShipMMSI = null;MMSI_enable = 0;//temp_MMSI = textBox5.Text;                          //读取目前跟踪的用户船MMSI码//temp_BaseShipMMSI = textBox18.Text;                  //读取当前基站船的MMSI码AISMessage += "\r\n\r\n";AISMessage += "解析后的数据如下:\r\n\r\n";// 二进制数据向有效数据转换char data_tr_bin = 0;       //二进制数据中间变量初始化int data_tr_valid = 0;    //有效数据中间变量初始化int count_s = 0;          //数据位起// 提取消息识别码int message_identifier;  //消息识别码int digit_num = 6;           //消息识别码的位数为6位int  count_e = count_s + digit_num - 1;   //计算数据终止位for (i = count_s; i <= count_e; i++){data_tr_bin = sum_binary[i];data_tr_valid = data_tr_valid + (data_tr_bin - '0') * (int)(Math.pow(2, count_e - i));}message_identifier = data_tr_valid;if (message_identifier == 19){       AISMessage += "消息识别码:  ";AISMessage += message_identifier;AISMessage += "  即 消息类型为“扩展B类设备位置报告”。\r\n";}count_s = count_e + 1;     //提取完毕后,重置数据位起始值// END(提取消息识别码)// 提取转发指示符int transpond_indicate;   //转发指示符digit_num = 2;           //转发指示符的位数为2位count_e = count_s + digit_num - 1;   //计算数据终止位data_tr_valid = 0;for (i = count_s; i <= count_e; i++){data_tr_bin = sum_binary[i];data_tr_valid = data_tr_valid + (data_tr_bin - '0') * (int)(Math.pow(2, count_e - i));}transpond_indicate = data_tr_valid;AISMessage += "转发指示符:  ";AISMessage += transpond_indicate;AISMessage += "  即 该条消息被转发的次数为:";AISMessage += transpond_indicate;AISMessage += "  次。\r\n";count_s = count_e + 1;     //提取完毕后,重置数据位起始值// END(提取转发指示符)// 提取用户识别码,即MMSI码int MMSI;     //用户识别码digit_num = 30;           //用户识别码,即MMSI码的位数为30位count_e = count_s + digit_num - 1;   //计算数据终止位data_tr_valid = 0;for (i = count_s; i <= count_e; i++){data_tr_bin = sum_binary[i];data_tr_valid = data_tr_valid + (data_tr_bin - '0') * (int)(Math.pow(2, count_e - i));               }MMSI = data_tr_valid;AISMessage += "用户识别码(即MMSI码):  ";AISMessage += MMSI;AISMessage += "\r\n";count_s = count_e + 1;     //提取完毕后,重置数据位起始值MMSI_OUT = String.valueOf(MMSI);                             //提取当前信息的MMSI码,用于船舶识别// END(提取用户识别码,即MMSI码)// 提取为地区或区域应用保留digit_num = 8;           //为地区或区域应用保留的位数为8位count_e = count_s + digit_num - 1;   //计算数据终止位data_tr_valid = 0;for (i = count_s; i <= count_e; i++){data_tr_bin = sum_binary[i];data_tr_valid = data_tr_valid + (data_tr_bin - '0') * (int)(Math.pow(2, count_e - i));            }AISMessage += "为地区性应用所保留:  ";AISMessage += data_tr_valid;AISMessage += "\r\n";count_s = count_e + 1;     //提取完毕后,重置数据位起始值// END(提取为地区或区域应用保留)// 提取对地航速double speed_over_ground;     //对地航速digit_num = 10;           //对地航速的位数为10位count_e = count_s + digit_num - 1;   //计算数据终止位data_tr_valid = 0;for (i = count_s; i <= count_e; i++){data_tr_bin = sum_binary[i];data_tr_valid = data_tr_valid + (data_tr_bin - '0') * (int)(Math.pow(2, count_e - i));              }speed_over_ground = (float)(data_tr_valid) / 10;AISMessage += "对地航速:  ";AISMessage += speed_over_ground;boat.setSog(speed_over_ground+"kn");AISMessage += " kn";AISMessage += "\r\n";count_s = count_e + 1;     //提取完毕后,重置数据位起始值// END(提取对地航速)// 提取船位精确度int position_accuracy;     //船位精确度digit_num = 1;           //船位精确度的位数为1位count_e = count_s + digit_num - 1;   //计算数据终止位data_tr_valid = 0;for (i = count_s; i <= count_e; i++){data_tr_bin = sum_binary[i];data_tr_valid = data_tr_valid + (data_tr_bin - '0') * (int)(Math.pow(2, count_e - i));          }position_accuracy = data_tr_valid;AISMessage += "船位精确度:  ";AISMessage += position_accuracy;if (data_tr_valid == 1)AISMessage += " 即 高精度(<10m,DGNSS接收机的差分模式)";else if (data_tr_valid == 0)AISMessage += " 即 低精度(>10m,GNSS接收机或其他电子定位装置的自主模式)";AISMessage += "\r\n";count_s = count_e + 1;     //提取完毕后,重置数据位起始值// END(提取船位精确度)// 提取经度double longitude;      //经度digit_num = 28;           //经度的位数为28位count_e = count_s + digit_num - 1;   //计算数据终止位data_tr_valid = 0;int hour;int minute;double second;if (sum_binary[count_s] != '1'){//("经度是正数,即东经\n");for (i = count_s; i <= count_e; i++){data_tr_bin = sum_binary[i];data_tr_valid = data_tr_valid + (data_tr_bin - '0') * (int)(Math.pow(2, count_e - i));//System.out.print("原始经度数据:"+data_tr_valid);}longitude = (float)(data_tr_valid) / 10000;hour = (int)(data_tr_valid / 10000 / 60);minute = (int)(longitude - hour * 60);second = (longitude - hour * 60 - minute) * 60;RecentLongitude = longitude / 60;  //将经度信息传递给全局变量,单位为度AISMessage += "经度:  ";AISMessage += "东经 ";AISMessage += hour;AISMessage += "度";AISMessage += hour;AISMessage += "分";AISMessage += second;AISMessage += "秒";AISMessage += "\r\n";}else if (sum_binary[count_s] == '1'){//("经度是负数,即西经\n");for (i = count_s + 1; i <= count_e; i++){if (sum_binary[i] == '0')data_tr_bin = '1';elsedata_tr_bin = '0';data_tr_valid = data_tr_valid + (data_tr_bin - '0') * (int)(Math.pow(2, count_e - i));//System.out.print("原始经度数据:"+data_tr_valid);}data_tr_valid = data_tr_valid + 1;  //经度的绝对值longitude = (float)(data_tr_valid) / 10000;hour = (int)(data_tr_valid / 10000 / 60);minute = (int)(longitude - hour * 60);second = (longitude - hour * 60 - minute) * 60;longitude = -longitude;      //经度的真实值  RecentLongitude = longitude / 60;  //将经度信息传递给全局变量AISMessage += "经度:  ";AISMessage += "西经 ";AISMessage += hour;AISMessage += "度";AISMessage += minute;AISMessage += "分";AISMessage += second;AISMessage += "秒";AISMessage += "\r\n";}count_s = count_e + 1;     //提取完毕后,重置数据位起始值// END(提取经度)// 提取纬度double latitude;    //纬度digit_num = 27;           //纬度的位数为27位count_e = count_s + digit_num - 1;   //计算数据终止位data_tr_valid = 0;System.out.print("18经度终止位数,count_e:" + count_e+ ",count_s:"+ count_s + ",data_tr_bin" + data_tr_bin);if (sum_binary[count_s] != '1'){//("纬度是正数,即北纬\n");for (i = count_s; i <= count_e; i++){data_tr_bin = sum_binary[i];data_tr_valid = data_tr_valid + (data_tr_bin - '0') * (int)(Math.pow(2, count_e - i));      }           latitude = (float)(data_tr_valid) / 10000;hour = (int)(data_tr_valid / 10000 / 60);minute = (int)(latitude - hour * 60);second = (latitude - hour * 60 - minute) * 60;RecentLatitude = latitude / 60;    //将当前纬度传递给全局变量AISMessage += "纬度:  ";AISMessage += "北纬 ";AISMessage += hour;AISMessage += "度";AISMessage += minute;AISMessage += "分";AISMessage += second;AISMessage += "秒";AISMessage += "\r\n";}else if (sum_binary[count_s] == '1'){//("纬度是负数,即南纬\n");for (i = count_s + 1; i <= count_e; i++){if (sum_binary[i] == '0')data_tr_bin = '1';elsedata_tr_bin = '0';data_tr_valid = data_tr_valid + (data_tr_bin - '0') * (int)(Math.pow(2, count_e - i));//System.out.print("原始经度数据:"+data_tr_valid);}data_tr_valid = data_tr_valid + 1;  //纬度的绝对值latitude = (float)(data_tr_valid) / 10000;hour = (int)(data_tr_valid / 10000 / 60);minute = (int)(latitude - hour * 60);second = (latitude - hour * 60 - minute) * 60;latitude = -latitude;      //纬度的真实值  RecentLatitude = latitude / 60;    //将当前纬度传递给全局变量AISMessage += "纬度:  ";AISMessage += "北纬 ";AISMessage += hour;AISMessage += "度";AISMessage += minute;AISMessage += "分";AISMessage += second;AISMessage += "秒";AISMessage += "\r\n";}count_s = count_e + 1;     //提取完毕后,重置数据位起始值// END(提取纬度)// 提取对地航向double course_over_the_ground;digit_num = 12;           //对地航向的位数为12位count_e = count_s + digit_num - 1;   //计算数据终止位data_tr_valid = 0;for (i = count_s; i <= count_e; i++){data_tr_bin = sum_binary[i];data_tr_valid = data_tr_valid + (data_tr_bin - '0') * (int)(Math.pow(2, count_e - i));              }course_over_the_ground = (float)(data_tr_valid) / 10;AISMessage += "对地航向:  ";AISMessage += course_over_the_ground;boat.setCog(course_over_the_ground+"度");AISMessage += "度";if (course_over_the_ground >= 360)AISMessage += " 即 默认值(不可用)";AISMessage += "\r\n";count_s = count_e + 1;     //提取完毕后,重置数据位起始值// END(提取对地航向)// 提取真航向int true_course;     //真航向digit_num = 9;           //真航向的位数为9位count_e = count_s + digit_num - 1;   //计算数据终止位data_tr_valid = 0;for (i = count_s; i <= count_e; i++){data_tr_bin = sum_binary[i];data_tr_valid = data_tr_valid + (data_tr_bin - '0') * (int)(Math.pow(2, count_e - i));            }true_course = data_tr_valid;AISMessage += "真航向:  ";AISMessage += true_course;AISMessage += "度";if (true_course == 511)AISMessage += " 即 默认值(不可用)";AISMessage += "\r\n";count_s = count_e + 1;     //提取完毕后,重置数据位起始值// END(提取真航向)// 提取时间标记int time_marker;    //时间标记digit_num = 6;           //时间标记的位数为6位count_e = count_s + digit_num - 1;   //计算数据终止位data_tr_valid = 0;for (i = count_s; i <= count_e; i++){data_tr_bin = sum_binary[i];data_tr_valid = data_tr_valid + (data_tr_bin - '0') * (int)(Math.pow(2, count_e - i));            }time_marker = data_tr_valid;AISMessage += "时间标记:  ";AISMessage += "报告产生时的UTC秒: ";AISMessage += time_marker;AISMessage += "\r\n";count_s = count_e + 1;     //提取完毕后,重置数据位起始值// END(提取时间标记)// 提取为地区性应用所保留digit_num = 4;           //为地区性应用所保留的位数为4位count_e = count_s + digit_num - 1;   //计算数据终止位data_tr_valid = 0;for (i = count_s; i <= count_e; i++){data_tr_bin = sum_binary[i];data_tr_valid = data_tr_valid + (data_tr_bin - '0') * (int)(Math.pow(2, count_e - i));               }AISMessage += "为地区性应用所保留:  ";AISMessage += data_tr_valid;AISMessage += "\r\n";count_s = count_e + 1;     //提取完毕后,重置数据位起始值// END(提取为地区性应用所保留)// 提取名称char ASC_trans;                //ASC码转换中间变量digit_num = 120;           //名称的位数为120位count_e = count_s + digit_num - 1;   //计算数据终止位data_tr_valid = 0;AISMessage += "名称:  ";for (i = count_s; i <= count_e; i++){for (j = 0; j < 6; j++){data_tr_bin = sum_binary[i + j];data_tr_valid = data_tr_valid + (data_tr_bin - '0') * (int)(Math.pow(2, 5 - j));                  }ASC_trans = six_to_eight_ASC(data_tr_valid);data_tr_valid = 0;AISMessage += ASC_trans;i = i + 5;}AISMessage += "\r\n";count_s = count_e + 1;     //提取完毕后,重置数据位起始值// END(提取名称)// 提取船舶及载货类型int ship_type;int ship_type_A;       //船舶类型的第一个字符int ship_type_B;       //船舶类型的第二个字符digit_num = 8;           //船舶及载货类型的位数为8位count_e = count_s + digit_num - 1;   //计算数据终止位data_tr_valid = 0;for (i = count_s; i <= count_e; i++){data_tr_bin = sum_binary[i];data_tr_valid = data_tr_valid + (data_tr_bin - '0') * (int)(Math.pow(2, count_e - i));               }ship_type = data_tr_valid;AISMessage += "船舶及载货类型:  ";AISMessage += ship_type;ship_type_A = (int)(ship_type / 10);ship_type_B = ship_type - ship_type_A * 10;if (ship_type_A == 5){switch (ship_type){case 50:                      AISMessage += " 即 领航船";break;case 51:AISMessage += " 即 搜救船";break;case 52:AISMessage += " 即 拖轮";break;case 53:AISMessage += " 即 港口供应船";break;case 54:AISMessage += " 即 载有防污染装置和设备的船舶";break;case 55:AISMessage += " 即 执法船";break;case 56:AISMessage += " 即 备用一分配给当地船舶使用";break;case 57:AISMessage += " 即 备用一分配给当地船舶使用";break;case 58:AISMessage += " 即 医疗船(如1949年日内瓦公约及附加条款所规定)";break;case 59:AISMessage += " 即 符合《无线电规则》第18号决议(Mob-83)的船舶";break;default:break;}}if (ship_type_A == 3){switch (ship_type_B){case 0:AISMessage += " 即 从事捕鱼的船舶";break;case 1:AISMessage += " 即 从事拖带作业的船舶";break;case 2:AISMessage += " 即 从事拖带作业,且拖带长度超过200m,或宽度超过25m的船舶";break;case 3:AISMessage += " 即 从事疏浚或水下作业的船舶";break;case 4:AISMessage += " 即 从事潜水作业的船舶";break;case 5:AISMessage += " 即 参与军事行动的船舶";break;case 6:AISMessage += " 即 驶帆航行的船舶";break;case 7:AISMessage += " 即 娱乐船";break;case 8:AISMessage += " 即 为将来使用保留";break;case 9:AISMessage += " 即 为将来使用保留";break;default:break;}}switch (ship_type_A){case 1:AISMessage += " 即 为将来使用保留";break;case 2:AISMessage += " 即 地效应船";break;case 4:AISMessage += " 即 高速船";break;case 6:AISMessage += " 即 客船";break;case 7:AISMessage += " 即 货船";break;case 8:AISMessage += " 即 油轮";break;case 9:AISMessage += " 即 其他类型的船舶";break;default:break;}if (ship_type_A != 3 && ship_type_A != 5){switch (ship_type_B){case 0:AISMessage += " 这一类型的所有船舶";break;case 1:AISMessage += " 载有危险品(DG),有害物质(HS)或海上污染物,属IMO规定的A类有害或污染物质";break;case 2:AISMessage += " 载有危险品,有害物质或海上污染物(MP),属IMO规定的B类有害或污染物质";break;case 3:AISMessage += " 载有危险品,有害物质或海上污染物,属IMO规定的C类有害或污染物质";break;case 4:AISMessage += " 载有危险品,有害物质或海上污染物,属IMO规定的D类有害或污染物质";break;case 9:AISMessage += " 无附加信息";break;default:AISMessage += " 为将来使用保留";break;}}AISMessage += "\r\n";count_s = count_e + 1;     //提取完毕后,重置数据位起始值// END(提取船舶及载货类型)// 提取船舶尺度/位置参照int ship_size_A;int ship_size_B;int ship_size_C;int ship_size_D;digit_num = 30;           //船舶尺度/位置参照的位数为30位count_e = count_s + digit_num - 1;   //计算数据终止位data_tr_valid = 0;AISMessage += "船舶尺度/位置参照:  ";for (i = count_s; i <= (count_s + 8); i++){data_tr_bin = sum_binary[i];              data_tr_valid = data_tr_valid + (data_tr_bin - '0') * (int)(Math.pow(2, 8 + count_s - i));          }ship_size_A = data_tr_valid;count_s = count_s + 9;data_tr_valid = 0;AISMessage += "距船首 ";AISMessage += ship_size_A;AISMessage += " m;";for (i = count_s; i <= (count_s + 8); i++){data_tr_bin = sum_binary[i];              data_tr_valid = data_tr_valid + (data_tr_bin - '0') * (int)(Math.pow(2, 8 + count_s - i));               }ship_size_B = data_tr_valid;count_s = count_s + 9;data_tr_valid = 0;AISMessage += "距船尾 ";AISMessage += ship_size_B;AISMessage += " m;";for (i = count_s; i <= (count_s + 5); i++){data_tr_bin = sum_binary[i];             data_tr_valid = data_tr_valid + (data_tr_bin - '0') * (int)(Math.pow(2, 5 + count_s - i));        }ship_size_C = data_tr_valid;count_s = count_s + 6;data_tr_valid = 0;AISMessage += "距左舷 ";AISMessage += ship_size_C;AISMessage += " m;";for (i = count_s; i <= (count_s + 5); i++){data_tr_bin = sum_binary[i];data_tr_valid = data_tr_valid + (data_tr_bin - '0') * (int)(Math.pow(2, 5 + count_s - i));            }ship_size_D = data_tr_valid;AISMessage += "距右舷 ";AISMessage += ship_size_D;AISMessage += " m;";AISMessage += "\r\n";count_s = count_e + 1;     //提取完毕后,重置数据位起始值// END(提取船舶尺度/位置参照)// 提取电子定位装置类型int locate_type;digit_num = 4;           //电子定位装置类型的位数为4位count_e = count_s + digit_num - 1;   //计算数据终止位data_tr_valid = 0;for (i = count_s; i <= count_e; i++){data_tr_bin = sum_binary[i];data_tr_valid = data_tr_valid + (data_tr_bin - '0') * (int)(Math.pow(2, count_e - i));          }locate_type = data_tr_valid;AISMessage += "电子定位装置类型:  ";AISMessage += locate_type;switch (locate_type){case 0:AISMessage += " 即 未定义(预设)";break;case 1:AISMessage += " 即 GPS";break;case 2:AISMessage += " 即 GLONASS";break;case 3:AISMessage += " 即 组合 GPS/GLONASS";break;case 4:AISMessage += " 即 罗兰";break;case 5:AISMessage += " 即 Chayka";break;case 6:AISMessage += " 即 综合导航系统";break;case 7:AISMessage += " 即 观测";break;default:AISMessage += " 即 不用";break;}AISMessage += "\r\n";count_s = count_e + 1;     //提取完毕后,重置数据位起始值// END(提取电子定位装置类型)// 提取RAIM标志int RAIM;     //RAIM标志digit_num = 1;           //RAIM标志的位数为1位count_e = count_s + digit_num - 1;   //计算数据终止位data_tr_valid = 0;for (i = count_s; i <= count_e; i++){data_tr_bin = sum_binary[i];data_tr_valid = data_tr_valid + (data_tr_bin - '0') * (int)(Math.pow(2, count_e - i));           }RAIM = data_tr_valid;AISMessage += "RAIM标志:  ";AISMessage += RAIM;if (RAIM == 1)AISMessage += " 即 RAIM使用";elseAISMessage += " 即 RAIM未使用(默认)";AISMessage += "\r\n";count_s = count_e + 1;     //提取完毕后,重置数据位起始值// END(提取RAIM标志)// 提取数据终端设备指示符int data_sys;digit_num = 1;           //数据终端设备指示符的位数为1位count_e = count_s + digit_num - 1;   //计算数据终止位data_tr_valid = 0;for (i = count_s; i <= count_e; i++){data_tr_bin = sum_binary[i];data_tr_valid = data_tr_valid + (data_tr_bin - '0') * (int)(Math.pow(2, count_e - i));//System.out.print("原始经度数据:"+data_tr_valid);}data_sys = data_tr_valid;AISMessage += "数据终端设备指示符:  ";AISMessage += data_sys;if (data_sys == 0)AISMessage += " 即 数据终端设备可用";elseAISMessage += " 即 数据终端设备不可用(默认)";AISMessage += "\r\n";count_s = count_e + 1;     //提取完毕后,重置数据位起始值// END(提取数据终端设备指示符)// 提取备用位digit_num = 5;           //备用位的位数为1位count_e = count_s + digit_num - 1;   //计算数据终止位data_tr_valid = 0;for (i = count_s; i <= count_e; i++){data_tr_bin = sum_binary[i];data_tr_valid = data_tr_valid + (data_tr_bin - '0') * (int)(Math.pow(2, count_e - i));               }AISMessage += "备用位:  ";AISMessage += data_tr_valid;AISMessage += "\r\n";count_s = count_e + 1;     //提取完毕后,重置数据位起始值// END(提取备用位)//if (MMSI_OUT == temp_MMSI)                      //如果该条信息是选中的用户船舶信息,则可进行参数提取和方位计算if (sen_f == 2){        AISMessage += "\r\n";AISMessage += "该条信息是选中的用户船舶信息,可进行参数提取和方位计算";MMSI_enable = 1;//textBox5.Clear();//textBox5.Text = MMSI_OUT;temp1 = String.valueOf(speed_over_ground);//textBox19.Clear();//textBox19.Text = temp1;temp2 = String.valueOf(RecentLongitude);//temp2 = RecentLongitude.ToString();//textBox22.Clear();                          //清空“船舶信息显示的经度”窗口                // textBox22.Text = temp2;ShipLongitude = RecentLongitude;temp3 = String.valueOf(RecentLatitude);//temp3 = RecentLatitude.ToString();//textBox20.Clear();                          //清空“船舶信息显示的纬度”窗口//textBox20.Text = temp3;ShipLatitude = RecentLatitude;RecentCourse = course_over_the_ground;        //将对地航向信息传递给全局变量//RecentCourse=true_course;                    //将真航向信息传递给全局变量if (RecentCourse >= 0 && RecentCourse < 360){temp4 = String.valueOf(RecentCourse);}else{RecentCourse = 0;                           //当航向信息不可用时,则指向角的计算将不考虑航向信息temp4 = "不可用";}/*if(RecentCourse!=511){temp4.Format( "%d",RecentCourse);}else{RecentCourse=0;    temp4="不可用";}*///textBox21.Clear();          //清空“船舶信息显示的真航向”窗口//textBox21.Text = temp4;}else if (MMSI_OUT == temp_BaseShipMMSI)             //如果该条信息是跟踪的基站船舶信息,则可进行参数提取和方位计算{AISMessage += "\r\n";AISMessage += "该条信息是跟踪的基站船舶信息,则可进行参数提取和方位计算";MMSI_enable = 2;temp2 = String.valueOf(RecentLongitude);//temp2 = RecentLongitude.ToString();//textBox17.Clear();                          //清空“船载基站信息显示的经度”窗口               //textBox17.Text = temp2;BaseLongitude = RecentLongitude;temp3 = String.valueOf(RecentLatitude);//temp3 = RecentLatitude.ToString();//textBox15.Clear();                          //清空“船载基站信息显示的纬度”窗口//textBox15.Text = temp3;BaseLatitude = RecentLatitude;}else                                                //如果该条语句不是选中船舶(用户船或基站船)的信息,则无需进行参数提取和方位计算     {      AISMessage += "\r\n";AISMessage += "该条信息不是选中的船舶信息,无需进行方位计算";MMSI_enable = 0;}AISLog.logInfo(AIS_Analyse.class, "Analysie19,"+"经度:"+RecentLongitude+",纬度:"+RecentLatitude+",mmsi:"+MMSI+",航速:"+boat.getCog()+",航向:"+boat.getSog());System.out.println("经度:"+RecentLongitude+",纬度:"+RecentLatitude+",mmsi:"+MMSI);boat.setLongitude(String.valueOf(RecentLongitude));boat.setLatitude(String.valueOf(RecentLatitude));boat.setMmsiId(String.valueOf(MMSI));BoatData(boat);System.out.println(AISMessage);}/**ASC码转换函数*/public static char six_to_eight_ASC(int t)        {char code = '\0';switch (t){case 0:code = '@';break;case 1:code = 'A';break;case 2:code = 'B';break;case 3:code = 'C';break;case 4:code = 'D';break;case 5:code = 'E';break;case 6:code = 'F';break;case 7:code = 'G';break;case 8:code = 'H';break;case 9:code = 'I';break;case 10:code = 'J';break;case 11:code = 'K';break;case 12:code = 'L';break;case 13:code = 'M';break;case 14:code = 'N';break;case 15:code = 'O';break;case 16:code = 'P';break;case 17:code = 'Q';break;case 18:code = 'R';break;case 19:code = 'S';break;case 20:code = 'T';break;case 21:code = 'U';break;case 22:code = 'V';break;case 23:code = 'W';break;case 24:code = 'X';break;case 25:code = 'Y';break;case 26:code = 'Z';break;case 27:code = ' ';break;case 28:code = ' ';break;case 29:code = ' ';break;case 30:code = '^';break;case 31:code = '_';break;case 32:code = ' ';break;case 33:code = '!';break;case 34:code = '“';break;case 35:code = '#';break;case 36:code = '$';break;case 37:code = '%';break;case 38:code = '&';break;case 39:code = '‘';break;case 40:code = '(';break;case 41:code = ')';break;case 42:code = '*';break;case 43:code = '+';break;case 44:code = ',';break;case 45:code = '—';break;case 46:code = '.';break;case 47:code = '/';break;case 48:code = '0';break;case 49:code = '1';break;case 50:code = '2';break;case 51:code = '3';break;case 52:code = '4';break;case 53:code = '5';break;case 54:code = '6';break;case 55:code = '7';break;case 56:code = '8';break;case 57:code = '9';break;case 58:code = ':';break;case 59:code = ';';break;case 60:code = '<';break;case 61:code = '=';break;case 62:code = '>';break;case 63:code = '?';break;default:break;}return code;}
}

利用串口解析AIS接收机数据相关推荐

  1. 【大陆ARS408毫米波雷达】一种利用串口解析雷达数据的方法

    硬件平台:ARS408毫米波雷达.can转485转换器.485转串口转换器 软件平台:Windows10.python3 本篇博客实现的功能: 一.通过两个转换器将毫米波雷达的原始数据传入电脑端的串口 ...

  2. Python学习:利用python解析GPS模块数据,并生成可视化地图显示

    一.GPS模块数据格式 笔者在项目中使用过移远L76K以及梦芯MXT905HM两款GPS定位芯片,两款芯片均是采用串口输出数据,而且数据包采用的是NEMA协议消息结构,消息结构如下: 数据包格式如下: ...

  3. 利用逻辑分析仪解析串口通讯数据

    利用逻辑分析仪解析串口通讯数据

  4. Arduino 利用串口缓冲区监听的方式读取数据

    Arduino 利用串口缓冲区监听的方式读取数据 相关篇<Arduino读取串口接收到的数据并发送数据> 利用串口缓冲区监听的方式读取数据,实现该功能依赖:serialEvent()此函数 ...

  5. 关于STM32串口3的使用,接收并解析一帧数据

    关于STM32串口3的使用,接收并解析一帧数据 当stm32的串口1被使用时,我们可以使用其他串口来使用. 步骤: 串口3定义.初始化: 串口3中断服务函数(接收的一帧数据并判断是否正确): 主函数使 ...

  6. 利用串口网络助手上传数据、图片到OneNet平台以及获取数据(HTTP)

    利用串口网络助手上传数据.图片到OneNet平台以及获取数据(HTTP) 使用网络助手连接OneNet服务器 HTTP上传数据至OneNet 获取数据 上传图片至OneNet 使用网络助手连接OneN ...

  7. Android利用Gson解析Json数据

    其实,要创建和解析JSON数据,也可以使用GSON来完成.GSON是Google提供的用来在Java对象和JSON数据之间进行映射的Java类库.使用GSON,可以很容易的将一串JSON数据转换为一个 ...

  8. 【RoboMaster】A板串口DMA接发数据及模块数据解析(GY33颜色传感器)

    文章目录 1.串口初始化配置 1.1CubeMX配置 1.1.1 项目总体配置 1.1.2串口参数配置 开启串口 DMA&NVIC 1.2 代码 1.2.1 串口初始化 1.2.2 串口中断数 ...

  9. STM32学习之串口采用DMA收发数据:需要利用状态机加DMA加串口

    写在前面 在学习这一节知识点的时候,真的是感觉太抽象了,没有一个合适的视频讲的我有那种豁然开朗的感觉,直到我看到了这篇文章,大家可以去看看,里面的描述特别形象. 链接:https://blog.csd ...

最新文章

  1. Spring Boot 自动配置原理
  2. Java Web应用程序的SecureLogin
  3. java 查看垃圾收集器_JVM系列:查看JVM使用的什么垃圾收集器
  4. [转]云计算:SaaS、PaaS、IaaS、CaaS
  5. 一分钟先生---走出软件作坊:三五个人十来条枪 如何成为开发正规军(三十二)...
  6. Nsight中给工程添加include目录
  7. 制作Win10 U盘版移动便携系统
  8. C语言k近邻算法及例题,K近邻算法的理解及KD树的构建
  9. 四通一达归于阿里后就涨价,证明资本的目标就是以垄断攫取利润
  10. Android实现NFC芯片制造商识别
  11. 不是maf格式的somatic突变数据就没办法读入到maftools了么
  12. win10怎么给多个桌面屏幕设置不同壁纸
  13. 【软考中级-网工】1
  14. 【算法学习笔记】28.枚举法 解题报告 SJTU OJ 1255 1256 魔戒
  15. 图数据库JanusGraph实战[6]: JanusGraph+HBase+ElasticSearch的环境搭建
  16. Transferability vs. Discriminability: Batch Spectral Penalization for Adversarial Domain Adaptation
  17. oracle 创建表空间
  18. PowerDesigner 安装
  19. 除了刺激战场和全军出击,最近还有哪些好玩的手游值得推荐?
  20. 《kdb+中文教程》介绍

热门文章

  1. STM32蜂鸣器实例详解
  2. PHP中的网络编程 -- Socket篇
  3. 假设检验中原假设和备择假设的选取问题
  4. Django admin后台美化(极其简单)
  5. 驼峰命名法与下划线命名法之争
  6. RecyclerView详解一,使用及缓存机制
  7. mysql插入百万级_百万级数据插入mysql
  8. Mac安装brew(2020年踩过无数坑后亲测有效的方法)
  9. 快速了解 Robot Operating System(ROS) 机器人操作系统
  10. SQL——基础语句练习