-
-
Notifications
You must be signed in to change notification settings - Fork 279
NTRIP client POC #6574
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: master
Are you sure you want to change the base?
NTRIP client POC #6574
Changes from all commits
3e702f7
df743ca
ed85b5e
8f37c5a
9c56799
8a48b47
77437dd
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,128 @@ | ||
#include "ntripclient.h" | ||
#include "ntripsocketclient.h" | ||
|
||
#include <QDebug> | ||
|
||
NtripClient::NtripClient( QObject *parent ) | ||
: QObject( parent ) | ||
{ | ||
} | ||
|
||
NtripClient::~NtripClient() | ||
{ | ||
stop(); | ||
} | ||
|
||
void NtripClient::start( const QString &ntripHost, const quint16 &port, const QString &mountpoint, const QString &username, const QString &password ) | ||
{ | ||
if ( mReply ) | ||
{ | ||
qWarning() << "NtripClient already running"; | ||
return; | ||
} | ||
|
||
mBytesSent = 0; | ||
mBytesReceived = 0; | ||
|
||
NtripSocketClient *client = new NtripSocketClient( this ); | ||
|
||
connect( client, &NtripSocketClient::correctionDataReceived, [this]( const QByteArray &data ) { | ||
mBytesReceived += data.size(); | ||
|
||
quint8 firstByte = quint8( data.at( 0 ) ); | ||
if ( firstByte == 0xD3 ) | ||
{ | ||
qDebug() << "RTCM chunk:"; | ||
} | ||
else if ( firstByte == 0x73 ) | ||
{ | ||
qDebug() << "SPARTN chunk:"; | ||
} | ||
else | ||
{ | ||
qDebug() << "UNKNOWN chunk:"; | ||
} | ||
|
||
qDebug() << data.size() << "bytes"; | ||
// send to your GNSS device | ||
emit correctionDataReceived( data ); | ||
emit bytesCountersChanged(); | ||
} ); | ||
|
||
connect( client, &NtripSocketClient::errorOccurred, [this]( const QString &msg ) { | ||
qWarning() << msg; | ||
emit errorOccurred( msg ); | ||
} ); | ||
|
||
connect( client, &NtripSocketClient::streamConnected, [this]() { | ||
emit streamConnected(); | ||
} ); | ||
|
||
mBytesSent = client->start( | ||
ntripHost, | ||
port, | ||
"/" + mountpoint, | ||
username, | ||
password ); | ||
|
||
// Emit immediately to show sent bytes | ||
emit bytesCountersChanged(); | ||
|
||
|
||
//connect(mReply, &QNetworkReply::readyRead, this, &NtripClient::onReadyRead); | ||
//connect(mReply, &QNetworkReply::finished, this, &NtripClient::onFinished); | ||
//connect(mReply, xxxQOverload<QNetworkReply::NetworkError>::of(&QNetworkReply::errorOccurred), | ||
// this, &NtripClient::onError); | ||
Comment on lines
+72
to
+75
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Leftover? |
||
} | ||
|
||
void NtripClient::stop() | ||
{ | ||
if ( mReply ) | ||
{ | ||
disconnect( mReply, nullptr, this, nullptr ); // ✅ Disconnect all signals | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. No emojis in source file please :) |
||
|
||
if ( mReply->isRunning() ) | ||
{ | ||
mReply->abort(); // ✅ Cancel the request | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. See above. |
||
} | ||
mReply->deleteLater(); | ||
mReply = nullptr; | ||
} | ||
} | ||
|
||
/* | ||
void NtripClient::onReadyRead() | ||
{ | ||
QByteArray data = mReply->readAll(); | ||
qInfo() << data + "\n"; | ||
emit correctionDataReceived(data); | ||
} | ||
*/ | ||
Comment on lines
+93
to
+100
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. To be removed? |
||
|
||
void NtripClient::onFinished() | ||
{ | ||
if ( mReply ) | ||
{ | ||
emit errorOccurred( "NTRIP connection closed" ); | ||
} | ||
// Schedule cleanup after Qt finishes emitting signals | ||
QMetaObject::invokeMethod( this, "stop", Qt::QueuedConnection ); | ||
} | ||
|
||
void NtripClient::onError( QNetworkReply::NetworkError code ) | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This needs to be connected. |
||
{ | ||
int status = mReply->attribute( QNetworkRequest::HttpStatusCodeAttribute ).toInt(); | ||
QString reason = mReply->attribute( QNetworkRequest::HttpReasonPhraseAttribute ).toString(); | ||
|
||
qWarning() << "HTTP status during error:" << status << reason; | ||
qWarning() << "Network error code:" << code; | ||
|
||
emit errorOccurred( | ||
QStringLiteral( "Network error %1, HTTP %2 %3" ) | ||
.arg( code ) | ||
.arg( status ) | ||
.arg( reason ) ); | ||
//emit errorOccurred(QStringLiteral("NTRIP error: %1").arg(code)); | ||
// Schedule cleanup after Qt finishes emitting signals | ||
QMetaObject::invokeMethod( this, "stop", Qt::QueuedConnection ); | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,39 @@ | ||
#pragma once | ||
|
||
#include <QNetworkAccessManager> | ||
#include <QNetworkReply> | ||
#include <QObject> | ||
#include <QUrl> | ||
|
||
class NtripClient : public QObject | ||
{ | ||
Q_OBJECT | ||
public: | ||
explicit NtripClient( QObject *parent = nullptr ); | ||
~NtripClient(); | ||
|
||
void start( const QString &ntripHost, const quint16 &port, const QString &mountpoint, const QString &username, const QString &password ); | ||
|
||
qint64 bytesSent() const { return mBytesSent; } | ||
qint64 bytesReceived() const { return mBytesReceived; } | ||
|
||
public slots: | ||
void stop(); | ||
Comment on lines
+20
to
+21
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I'd move the stop() into public to sit just below start(). Just a styling preference here. |
||
|
||
signals: | ||
void correctionDataReceived( const QByteArray &rtcmData ); | ||
void errorOccurred( const QString &message ); | ||
void bytesCountersChanged(); | ||
void streamConnected(); | ||
|
||
private slots: | ||
//void onReadyRead(); | ||
void onFinished(); | ||
void onError( QNetworkReply::NetworkError code ); | ||
|
||
private: | ||
QNetworkAccessManager mNetworkManager; | ||
QNetworkReply *mReply = nullptr; | ||
Comment on lines
+35
to
+36
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. These two members don't appear to be used here? |
||
qint64 mBytesSent = 0; | ||
qint64 mBytesReceived = 0; | ||
}; |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,103 @@ | ||
#include "ntripsocketclient.h" | ||
|
||
#include <QDebug> | ||
|
||
NtripSocketClient::NtripSocketClient( QObject *parent ) | ||
: QObject( parent ) | ||
{ | ||
connect( &mSocket, &QTcpSocket::connected, this, &NtripSocketClient::onConnected ); | ||
connect( &mSocket, &QTcpSocket::readyRead, this, &NtripSocketClient::onReadyRead ); | ||
connect( &mSocket, &QTcpSocket::disconnected, this, &NtripSocketClient::onDisconnected ); | ||
connect( &mSocket, QOverload<QAbstractSocket::SocketError>::of( &QTcpSocket::errorOccurred ), | ||
this, &NtripSocketClient::onSocketError ); | ||
} | ||
|
||
NtripSocketClient::~NtripSocketClient() | ||
{ | ||
stop(); | ||
} | ||
|
||
qint64 NtripSocketClient::start( | ||
const QString &host, | ||
quint16 port, | ||
const QString &mountpoint, | ||
const QString &username, | ||
const QString &password ) | ||
{ | ||
mHeadersSent = false; | ||
mSocket.connectToHost( host, port ); | ||
|
||
QString credentials = username + ":" + password; | ||
QByteArray base64 = credentials.toUtf8().toBase64(); | ||
|
||
QByteArray request; | ||
request.append( "GET " + mountpoint.toUtf8() + " HTTP/1.0\r\n" ); | ||
request.append( "Host: " + host.toUtf8() + ":" + QByteArray::number( port ) + "\r\n" ); | ||
request.append( "User-Agent: QField NTRIP QtSocketClient/1.0\r\n" ); | ||
request.append( "Accept: */*\r\n" ); | ||
request.append( "Authorization: Basic " + base64 + "\r\n" ); | ||
request.append( "Connection: close\r\n" ); | ||
//request.append("Ntrip-Version: Ntrip/2.0\r\n"); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Should we support both ntrip v1 and ntrip v2 protocol? Is there anything to do here other than advertising the protocol version in the header? Either way, we need to clean this up :) |
||
request.append( "\r\n" ); | ||
|
||
connect( &mSocket, &QTcpSocket::connected, [this, request]() { | ||
mSocket.write( request ); | ||
mSocket.flush(); | ||
} ); | ||
|
||
return request.size(); | ||
} | ||
Comment on lines
+20
to
+49
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Let's save the host/port/mountpoint/username/password into members of the class, it'll also us to provide more context when reporting connection status / error / etc. |
||
|
||
void NtripSocketClient::stop() | ||
{ | ||
if ( mSocket.isOpen() ) | ||
{ | ||
mSocket.disconnectFromHost(); | ||
mSocket.close(); | ||
} | ||
} | ||
|
||
void NtripSocketClient::onConnected() | ||
{ | ||
qDebug() << "Connected to NTRIP caster."; | ||
} | ||
|
||
void NtripSocketClient::onReadyRead() | ||
{ | ||
QByteArray data = mSocket.readAll(); | ||
|
||
// If headers not processed yet, discard them | ||
if ( !mHeadersSent ) | ||
{ | ||
int headerEnd = data.indexOf( "\r\n\r\n" ); | ||
if ( headerEnd != -1 ) | ||
{ | ||
QByteArray headerData = data.left( headerEnd ); | ||
qDebug() << "Received HTTP headers:\n" | ||
<< headerData; | ||
data = data.mid( headerEnd + 4 ); | ||
mHeadersSent = true; | ||
emit streamConnected(); | ||
} | ||
else | ||
{ | ||
// Wait for more data | ||
return; | ||
} | ||
} | ||
Comment on lines
+82
to
+87
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. When you read all the data here, the buffer will be gone, meaning that as your waiting for more data, you'll lose the data that came before, is there a need to preserve the data to append it to the next frame until the \r\n\r\n bit arrives? |
||
|
||
if ( !data.isEmpty() ) | ||
{ | ||
emit correctionDataReceived( data ); | ||
} | ||
} | ||
|
||
void NtripSocketClient::onDisconnected() | ||
{ | ||
emit errorOccurred( "Disconnected from NTRIP caster." ); | ||
} | ||
|
||
void NtripSocketClient::onSocketError( QAbstractSocket::SocketError error ) | ||
{ | ||
emit errorOccurred( "Socket error: " + QString::number( error ) + " (" + mSocket.errorString() + ")" ); | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Let's clean some of that debug prior to merging so we don't get too verbose when running QField in debug mode.