PointOneNav / polaris

Software for communicating with Point One Navigation's Polaris GNSS Corrections Service
MIT License
24 stars 12 forks source link

Question: Can I connect to Polaris NTRIP using UrlHttpConnection from Java/Kotlin? #62

Open Ba0Nguyen opened 2 months ago

Ba0Nguyen commented 2 months ago

` val urlString = "https://polaris.pointonenav.com:2101\n"

            val url = URL(urlString)
            val connection = url.openConnection() as HttpURLConnection
            // Set up basic authentication
            val usernameAndPassword = "$username:$password"
            val encoded = usernameAndPassword.encode(StandardCharsets.ISO_8859_1).base64()
            connection.setRequestProperty("Authorization", "Basic $encoded")
            connection.setRequestProperty("Ntrip-Version", "Ntrip/2.0")
            // Set request method (GET, POST, etc.)
            connection.setRequestMethod("GET")

            // connection.addRequestProperty("Ntrip-GGA", initialMessage)

            connection.doOutput = true;
            connection.doInput = true;

`

I used to code above to attempt to connect to Polaris NTRIP Server but failed to do so. Do I need any header or sending initial message to connect with Polaris NTRIP ?

mkurdziel commented 1 month ago

@Ba0Nguyen - the HttpURLConnection probably isn't the best way. Ntrip is almost http, but its differences seem to often make standard Http libraries unhappy. Here's a quick java application that should connect, send in a message, and print out the returned corrections.


import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.OutputStreamWriter;
import java.net.Socket;
import java.util.Base64;

public class NtripClient {

    public static void main(String[] args) {
        // Read environmental variables
        String ntripCasterHost = System.getenv("NTRIP_CASTER_HOST");
        String ntripCasterPort = System.getenv("NTRIP_CASTER_PORT");
        String ntripMountPoint = System.getenv("NTRIP_MOUNT_POINT");
        String username = System.getenv("NTRIP_USERNAME");
        String password = System.getenv("NTRIP_PASSWORD");
        String latitudeStr = System.getenv("LATITUDE");
        String longitudeStr = System.getenv("LONGITUDE");

        if (ntripCasterHost == null || ntripCasterPort == null || ntripMountPoint == null || username == null || password == null || latitudeStr == null || longitudeStr == null) {
            System.err.println("Environmental variables NTRIP_CASTER_HOST, NTRIP_CASTER_PORT, NTRIP_MOUNT_POINT, NTRIP_USERNAME, NTRIP_PASSWORD, LATITUDE, and LONGITUDE must be set.");
            return;
        }

        int port = Integer.parseInt(ntripCasterPort);
        double latitude = Double.parseDouble(latitudeStr);
        double longitude = Double.parseDouble(longitudeStr);

        try {
            // Create a socket connection to the NTRIP caster
            Socket socket = new Socket(ntripCasterHost, port);
            BufferedWriter writer = new BufferedWriter(new OutputStreamWriter(socket.getOutputStream()));
            BufferedReader reader = new BufferedReader(new InputStreamReader(socket.getInputStream()));

            // Send NTRIP request
            String auth = username + ":" + password;
            String encodedAuth = Base64.getEncoder().encodeToString(auth.getBytes());
            writer.write("GET /" + ntripMountPoint + " HTTP/1.1\r\n");
            writer.write("Host: " + ntripCasterHost + "\r\n");
            writer.write("Ntrip-Version: Ntrip/2.0\r\n");
            writer.write("User-Agent: NTRIP NtripClient\r\n");
            writer.write("Authorization: Basic " + encodedAuth + "\r\n");
            writer.write("\r\n");
            writer.flush();

            // Create and send GGA message
            String ggaMessage = createGgaMessage(latitude, longitude);
            System.out.println("Sending GGA message: " + ggaMessage.trim());  // Debug line
            writer.write(ggaMessage);
            writer.flush();

            // Read response header
            String responseLine;
            while ((responseLine = reader.readLine()) != null && !responseLine.isEmpty()) {
                System.out.println(responseLine);
            }

            // Read GNSS correction data
            InputStream inputStream = socket.getInputStream();
            byte[] buffer = new byte[4096];
            int bytesRead;
            while ((bytesRead = inputStream.read(buffer)) != -1) {
                // Output the binary data
                for (int i = 0; i < bytesRead; i++) {
                    System.out.printf("%02x ", buffer[i]);
                }
                System.out.println();
            }

            // Close the streams
            reader.close();
            writer.close();
            socket.close();
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    private static String createGgaMessage(double latitude, double longitude) {
        String latDirection = latitude >= 0 ? "N" : "S";
        String lonDirection = longitude >= 0 ? "E" : "W";

        latitude = Math.abs(latitude);
        longitude = Math.abs(longitude);

        int latDegrees = (int) latitude;
        double latMinutes = (latitude - latDegrees) * 60.0;

        int lonDegrees = (int) longitude;
        double lonMinutes = (longitude - lonDegrees) * 60.0;

        String gga = String.format(
                "$GPGGA,123519,%02d%07.4f,%s,%03d%07.4f,%s,1,08,0.9,545.4,M,46.9,M,,",
                latDegrees, latMinutes, latDirection, lonDegrees, lonMinutes, lonDirection
        );

        String checksum = calculateChecksum(gga);
        return gga + "*" + checksum + "\r\n";
    }

    private static String calculateChecksum(String sentence) {
        int checksum = 0;
        for (int i = 1; i < sentence.length(); i++) {
            checksum ^= sentence.charAt(i);
        }
        return String.format("%02X", checksum);
    }
}

This will run on its own and print out the correction data as bytes. I tested this with the following ENV variables

LATITUDE=37.788;
LONGITUDE=-122.402;
NTRIP_CASTER_HOST=polaris.pointonenav.com;
NTRIP_CASTER_PORT=2101;
NTRIP_MOUNT_POINT=POLARIS;
NTRIP_PASSWORD=<YOUR_NTRIP_PASSWORD>;
NTRIP_USERNAME=<YOUR_NTRIP_USERNAME>;

Hope this helps!

Ba0Nguyen commented 1 month ago

Thanks for the help @mkurdziel , Do you have any documentation for the RTCM data format (e.g. rtcm type)? I would like to parse and convert it to each RTCM String.

Ba0Nguyen commented 1 month ago

I was able to forward RTCM data to my drone, can you direct me to how I can achieve 1cm accuracy? Do I have to update my position a certain way to achieve that? Does the mount point I am using support 1cm accuracy? I've tried mount point POLARIS_LOCAL and is getting 4cm accuracy. I also noticed that if I start to move to drone, accuracy can get worse.