Ntrip协议

Ntrip协议

CORS就是网络基准站,通过网络收发GPS差分数据。用户访问CORS后,不用单独架设GPS基准站,即可实现GPS流动站的差分定位。访问CORS系统,就需要网络通讯协议。NTRIP是CORS系统的通讯协议之一,所有的 RTK数据格式(NCT,RTCM,CMR,CMR+等等)都能被传输。

主要流程

  • 拉取挂载点列表数据

  • 连接挂载点

  • 收发差分数据

⼀、拉取挂载点列表

建⽴socket连接后,发送拉取挂载点的命令:

"GET / HTTP/1.1\r\n" 

"User-Agent: NTRIP GNSSInternetRadio\r\n" 

"Accept: */*\r\n"  

"Connection: close\r\n\r\n";

Caster收到命令后,会回复挂载点列表信息:

    SOURCETABLE 200 OK

    Server: POP_GW_Ntrip_1.0_1622119852/1.0

    Via: n20_108

    Date: 2021/06/07 20:39:47

    Content-Type: text/plain; charset=UTF-8

    Content-Length: 475

    Connection: close

    

    STR;AUTO;AUTO;RTCM3X;;2;GNSS;POPNet;CHN;0.00;0.00;1;1;POP Platform;none;B;N;500;POP

    STR;RTCM30_GG;RTCM30_GG;RTCM3X;1005(10),1004-1012(1),1033(10);2;GNSS;POPNet;CHN;0.00;0.00;1;1;POP Platform;none;B;N;500;POP

    STR;RTCM23_GPS;RTCM23_GPS;RTCM2X;1(1),31(1),41(1),3(10),32(30);2;GNSS;POPNet;CHN;0.00;0.00;1;1;POP Platform;none;B;N;500;POP

    STR;RTCM32_GGB;RTCM32_GGB;RTCM3X;1005(10),1074-1084-1124(1);2;GNSS;POPNet;CHN;0.00;0.00;1;1;POP Platform;none;B;N;500;POP

ENDSOURCETABLE

STR;AUTO;AUTO;RTCM3X;;2;GNSS;POPNet;CHN;0.00;0.00;1;1;POP Platform;none;B;N;500;POP

这就是一条节点信息,主要是第二条挂载点名字

⼆、连接挂载点

通过拉取到的挂载点信息,需要⽤户名和密码登⼊,才能与其建⽴通信。发送命令格式如下:

GET /RTCM30_GG HTTP/1.0

User-Agent: NTRIP GNSSInternetRadio/1.4.10

Accept: */*

Connection: close

Authorization: Basic cXhuZ3kwMDU6MTAzZTdiZQ==

RTCM30_GG为挂载点名称,cXhuZ3kwMDU6MTAzZTdiZQ==是⼀个base64字符串,它是由⽤户名和密码中间加冒号("UserName:Password")后⽤base64编码⽣成的字符串。如果⽤户名密码及挂载点都正确的话,Ntrip将返回数据:

ICY 200 OK

有些服务器只返回"ICY 200 OK",这个没有影响,说明已经与挂载点建⽴好通信,可以收发数据了。

三、发送差分数据

发送差分数据:

$GPGGA,052114.93,3115.2739300,N,12133.8922600,E,1,00,1.0,-10.643,M,11.353,M,0.0,*5D

如果命令格式错误,Ntrip将不返回任何数据。正确数据将返回数据:

$GNGGA,031202.00,3109.91846,N,12123.97022,E,1,03,18.63,-0.7,M,9.7,M,,*6F

完整代码代码,

import android.util.Base64;

import java.nio.charset.StandardCharsets;

import java.util.ArrayList;

import java.util.Arrays;

import java.util.Timer;

import java.util.TimerTask;

import java.util.concurrent.ExecutorService;

import java.util.concurrent.Executors;

/**

* <p> 功能描述:RTK连接处理

* 详细描述:RTK连接处理

*

* @Author: 龚寿生

* @CreateDate: 2021/6/7 17:00

* @UpdateUser: 龚寿生

* @UpdateDate: 2021/6/10 10:00

* @UpdateRemark: 更新连接问题

* @Version: 1.0.1 版本

*/

public class RTKSocketManager {

    //TAG

    private static final String TAG = "RTKSocketManager";

    private static final String TAG_HOST = "host";

    private static final String TAG_PORT = "port";

    private static final String TAG_USERNAME = "userName";

    private static final String TAG_PASSWORD = "password";

    //地址账号相关

    private String host;

    private int port;

    private String userName;

    private String password;

    //连接Ntrip协议服务器socket接口

    private final TcpSocket tcpNet;

    //线程池,用于网络耗时请求

    private final ExecutorService singleThreadExecutor;

    //定时发送差分数据,避免过快导致服务器压力过大以及数据太多下发给飞控

    private Timer timer;

    //差分数据收发开始标志,当前差分数据是否有效

    private boolean isStart=false, isFlag=false;

    //当前挂载点

    private String mountPoint;

    //差分数据缓存,一秒钟发一次

    private String differentialData;

    //数据回调

    private Callback callback;

    /**

     * 采用内部类延单例模式,可以实现线程安全和延迟加载

     */

    private static class SingletonHolder {

        private static final RTKSocketManager INSTANCE = new RTKSocketManager();

    }

    /**

     * 静态获取当前对象

     *

     * @return 当前实例对象

     */

    public static RTKSocketManager instance() {

        return SingletonHolder.INSTANCE;

    }

    /**

     * 实例化对象

     */

    private RTKSocketManager() {

        host = SharedPreferencesStore.getString(TAG, TAG_HOST);

        port = SharedPreferencesStore.getInt(TAG, TAG_PORT, 0);

        userName = SharedPreferencesStore.getString(TAG, TAG_USERNAME);

        password = SharedPreferencesStore.getString(TAG, TAG_PASSWORD);

        tcpNet = new TcpSocket();

        singleThreadExecutor = Executors.newSingleThreadExecutor();

    }

    /**

     * 设置地址和端口

     *

     * @param host 服务器地址

     * @param port 端口

     */

    public void init(String host, int port) {

        this.host = host;

        this.port = port;

    }

    /**

     * 连接挂载点

     */

    public void queryMountPointList() {

//        if (TextUtils.isEmpty(host) || port <= 0) {

//            return;

//        }

        singleThreadExecutor.execute(() -> {

            if (tcpNet.connect(host, port)) {

                getMountPointList();

            } else {

                connectMountPointListFail();

            }

        });

    }

    /**

     * 连接挂载点失败

     */

    private void connectMountPointListFail() {

        AutelLog.debug_i(TAG, "getMountPointList fail");

        if (callback != null) {

            MsgPostManager.instance().post(new PostRunnable() {

                protected void task() {

                    callback.getMountPointListFail();

                }

            });

        }

    }

    /**

     * 获取挂载点列表,通过主线程回调传输数据

     */

    private void getMountPointList() {

        String GET_MOUNT_POINT = "GET / HTTP/1.1\r\n" + "User-Agent: NTRIP GNSSInternetRadio\r\n" + "Accept: */*\r\n" + "Connection: close\r\n\r\n";

        tcpNet.write(GET_MOUNT_POINT.getBytes());

        byte[] readBuf = tcpNet.read();

        if (readBuf.length != 0) {

            String reads = new String(readBuf, StandardCharsets.UTF_8);

            AutelLog.debug_i(TAG, reads);

            String[] strings = reads.split("\n");

            ArrayList<String> list = new ArrayList<>();

            for (int i = 8; i < strings.length; i++) {

                String[] mount = strings[i].split(";");

                if (mount.length == 19 && mount[0].equals("STR") || mount[0].equals("CAS") || mount[0].equals("NET")) {

                    list.add(mount[1]);

                }

            }

            if (list.size() != 0) {

                AutelLog.debug_i(TAG, "list.size:" + list.size());

                tcpNet.disConnect();

                if (callback != null) {

                    MsgPostManager.instance().post(new PostRunnable() {

                        protected void task() {

                            callback.getMountPointListSuccess(list);

                        }

                    });

                }

            } else {

                connectMountPointListFail();

            }

        } else {

            connectMountPointListFail();

        }

    }

    /**

     * 设置用户名和密码

     *

     * @param userName 用户名

     * @param password 密码

     */

    public void setUserNameAndPassword(String userName, String password) {

        this.userName = userName;

        this.password = password;

    }

    /**

     * 连接挂载点,需要提前设置用户名和密码

     *

     * @param mountName 挂载点名称

     */

    public void connectMountPoint(String mountName) {

//        if (TextUtils.isEmpty(userName) || TextUtils.isEmpty(password) || TextUtils.isEmpty(mountName)) {

//            return;

//        }

        singleThreadExecutor.execute(() -> {

            if (tcpNet.connect(host, port)) {

                String cmd = getConnectMountPointCmd(mountName);

                AutelLog.debug_i(TAG, "connectMountPoint:" + cmd);

                tcpNet.write(cmd.getBytes());

                byte[] readBuf = tcpNet.read();

                if (readBuf.length != 0) {

                    String reads = new String(readBuf, StandardCharsets.UTF_8);

                    AutelLog.debug_i(TAG, "read:" + reads);

                    if (reads.contains("200 OK")) {

                        mountPoint = mountName;

                        MsgPostManager.instance().post(new PostRunnable() {

                            protected void task() {

                                startRtkDataConnect();

                                if (callback != null) {

                                    callback.connectMountPointSuccess();

                                }

                            }

                        });

                    } else {

                        connectMountPointFail();

                    }

                } else {

                    connectMountPointFail();

                }

            } else {

                connectMountPointFail();

            }

        });

    }

    /**

     * 连接挂载点失败

     */

    private void connectMountPointFail() {

        AutelLog.debug_i(TAG, "connectMountPoint fail");

        if (callback != null) {

            MsgPostManager.instance().post(new PostRunnable() {

                protected void task() {

                    callback.connectMountPointFail();

                }

            });

        }

    }

    /**

     * 开始和费看看收发RTK差分数据

     */

    private void startRtkDataConnect() {

        SharedPreferencesStore.saveString(TAG, TAG_HOST, host);

        SharedPreferencesStore.saveInt(TAG, TAG_PORT, port);

        SharedPreferencesStore.saveString(TAG, TAG_USERNAME, userName);

        SharedPreferencesStore.saveString(TAG, TAG_PASSWORD, password);

        isStart = true;

        timer = new Timer();

        timer.scheduleAtFixedRate(new TimerTask() {

            @Override

            public void run() {

                AutelLog.debug_i(TAG, isStart + ",isStart,isFlag," + isFlag);

                if (isStart) {

                    if (isFlag) {

                        AutelLog.debug_i(TAG,  "write:" + differentialData);

                        tcpNet.write(differentialData.getBytes());

                        byte[] bytes = tcpNet.read();

                        AutelLog.debug_i(TAG,  "uploadRTKData:" + Arrays.toString(bytes));

                        

                    }

                } else {

                    //尝试重连上次登录成功的账号

                    connectMountPoint(mountPoint);

                }

            }

        }, 0, 1000);

    }

    /**

     * 获取连接挂载点指令

     *

     * @param mountName 挂载点名称

     * @return 字符串

     */

    private String getConnectMountPointCmd(String mountName) {

        return "GET /" + mountName + " HTTP/1.1\r\n" +

                "User-Agent: NTRIP GNSSInternetRadio\r\n" +

                "Accept: */*\r\n" +

                "Connection: close\r\n" +

                "Authorization: Basic " + Base64.encodeToString((userName + ":" + password).getBytes(), Base64.DEFAULT) + "\r\n\r\n";

    }

    /**

     * 检测是否已经连接

     *

     * @return false: failure  true:success

     */

    public boolean isConnected() {

        return tcpNet.isConnected() && isStart;

    }

    /**

     * 不需要RTK资源的时候,需要销毁资源

     */

    public void destory() {

        isStart = false;

        isFlag = false;

        if (timer != null) {

            timer.cancel();

        }

    }

    /**

     * 接口回调

     */

    public interface Callback {

        /**

         * 获取挂载点成功

         *

         * @param list 挂载点名称列表

         */

        void getMountPointListSuccess(ArrayList<String> list);

        /**

         * 获取挂载点失败

         */

        void getMountPointListFail();

        /**

         * 连接挂载点成功

         */

        void connectMountPointSuccess();

        /**

         * 连接挂载点失败

         */

        void connectMountPointFail();

    }

    /**

     * 设置回调接口

     *

     * @param callback 接口

     */

    public void setCallback(Callback callback) {

        this.callback = callback;

    }

    /**

     * 获取服务器地址

     *

     * @return String

     */

    public String getHost() {

        return host;

    }

    /**

     * 获取端口

     *

     * @return 端口

     */

    public int getPort() {

        return port;

    }

    /**

     * 获取用户名

     *

     * @return 用户名

     */

    public String getUserName() {

        return userName;

    }

    /**

     * 获取密码

     *

     * @return 密码

     */

    public String getPassword() {

        return password;

    }

    /**

     * 设置差分数据

     *

     * @param bytes 差分数据

     */

    public void setGPGGA(byte[] bytes) {

        differentialData = new String(bytes)+"\r\n";

//        differentialData = new String(bytes);

        isFlag=true;

        AutelLog.debug_i(TAG,  "setGPGGA:" + Arrays.toString(bytes));

    }

}

import java.io.IOException;

import java.io.InputStream;

import java.io.OutputStream;

import java.net.Socket;

import java.net.SocketException;

import timber.log.Timber;

/**

* <p>功能描述:TcpSocket工具类   

* 详细描述:

*

* @author 龚寿生

* @version V1.0

* @date 2021/1/21 16:13

*/

public class TcpSocket {

    private static final String TAG = "gss";

    private static final int TIME_OUT = 3000;

    private Socket socket = null;

    public TcpSocket() {

    }

    /**

     * Function:connect the socket of IpParameters

     *

     * @return boolean: failure   0:success

     */

    public boolean connect(String address, int port) {

        if (socket != null && socket.isConnected()) {

            return true;

        }

        try {

            socket = new Socket(address, port);

            socket.setSoTimeout(TIME_OUT);

        } catch (IOException e) {

            e.printStackTrace();

        }

        return socket != null && socket.isConnected();

    }

    /**

     * Function:examine the connection

     *

     * @return false: failure  true:success

     */

    public boolean isConnected() {

        if (socket == null) {

            return false;

        }

        return socket.isConnected();

    }

    /**

     * set time out

     *

     * @param time Timeout

     * @return false: failure  true:success

     */

    public boolean setTcpTimeout(int time) {

        if (socket == null) {

            return false;

        }

        try {

            socket.setSoTimeout(time);

        } catch (SocketException e) {

            e.printStackTrace();

            return false;

        }

        return true;

    }

    /**

     * Function:read the data of socket

     *

     * @return 0: failure ,other:success

     */

    public byte[] read() {

        int size = 0;

        if (socket == null || !socket.isConnected()) {

            Timber.i("socket == null || !socket.isConnected()");

            return new byte[0];

        }

        byte[] buf = new byte[1024];

        try {

            InputStream inputStream = socket.getInputStream();

            size = inputStream.read(buf);

        } catch (IOException e) {

            Timber.tag(TAG).i("socket read IOException:%s", e.getMessage());

            disConnect();

            e.printStackTrace();

        }

        if (size > 0) {

            byte[] rev_data=new byte[size];

            System.arraycopy(buf, 0, rev_data, 0, size);

            return rev_data;

        }

        return new byte[0];

    }

    /**

     * Function:write the data of socket

     *

     * @param data the data from the client;

     */

    public boolean write(byte[] data) {

        if (socket == null || !socket.isConnected()) {

            Timber.tag(TAG).i("socket == null || !socket.isConnected()");

            return false;

        }

        try {

            OutputStream outputStream = socket.getOutputStream();

            outputStream.write(data);

        } catch (IOException e) {

            Timber.tag(TAG).i("socket write IOException:%s", e.getMessage());

            e.printStackTrace();

            disConnect();

            return false;

        }

        return true;

    }

    /**

     * Function:disConnect the socket

     */

    public void disConnect() {

        if (socket != null) {

            try {

                socket.close();

            } catch (IOException e) {

                e.printStackTrace();

            }

            socket = null;

        }

    }

}

参考链接:

https://www.it610.com/article/1279923609803964416.htm

https://blog.csdn.net/hanford/article/details/53025771

https://www.cnblogs.com/hanford/p/6028156.html

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

时代我西

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值