10#include <QtCore/QByteArray>
11#include <QtCore/QByteArrayView>
12#include <QtCore/QString>
33constexpr double DNU = 100000.0;
34constexpr qsizetype DESCRIPTOR_SIZE = 4;
40 return GPSPositionReport::FixType::NoFix;
43 return GPSPositionReport::FixType::Differential;
46 return GPSPositionReport::FixType::RTKFloat;
49 return GPSPositionReport::FixType::RTKFixed;
51 return GPSPositionReport::FixType::Fix3D;
57std::optional<QByteArray> promptDescriptor(QByteArrayView text)
59 for (qsizetype end = text.indexOf(
'>'); end >= 0; end = text.indexOf(
'>', end + 1)) {
60 if (end < DESCRIPTOR_SIZE) {
63 const QByteArrayView descriptor = text.sliced(end - DESCRIPTOR_SIZE, DESCRIPTOR_SIZE);
64 const char before = end > DESCRIPTOR_SIZE ? text[end - DESCRIPTOR_SIZE - 1] :
' ';
69 return descriptor.toByteArray();
84 std::array<uint8_t, GPSCommandChannel::READ_CHUNK_SIZE> chunk{};
90 received.append(
reinterpret_cast<const char*
>(chunk.data()), count);
91 if (
const auto descriptor = promptDescriptor(received)) {
93 qCDebug(SBFProtocolLog).noquote() <<
"Septentrio GNSS receiver COM port:" <<
port;
97 if (received.size() > 2 *
static_cast<qsizetype
>(chunk.size())) {
98 received = received.last(DESCRIPTOR_SIZE + 1);
132 if (!_ready || channel.
failed()) {
133 return GPSReceiveUpdates{};
151 bool _surveyActive =
false;
153 std::optional<uint64_t> _lastEpoch;
160 _surveyClock.
reset();
161 _surveyActive =
false;
167 if (!_configureBase(channel,
config.base, baud)) {
183 if (!detectPort(channel, port)) {
184 channel.
failControl(QStringLiteral(
"No command prompt from the Septentrio receiver"));
197 if (!std::holds_alternative<GPSBaseStationConfig::Fixed>(_base.mode)) {
198 _surveyClock.start(nowUs);
209 return _decodeBlock(frame, context);
218 const auto pvt = Wire::decode<SBF::PVTGeodetic>(
frame.bytes);
223 if (_lastEpoch && epoch <= *_lastEpoch) {
231 if (_ready && pvt.datum != 0) {
234 qCWarning(SBFProtocolLog) <<
"Unsupported Septentrio position datum:" << unsigned(pvt.datum);
235 context.
failControl(QStringLiteral(
"Septentrio position datum is not WGS84/ITRS"));
240 const bool coordinatesValid = _applyPVTGeodetic(pvt, position, context);
242 _publishSurveyStatus(pvt, position, coordinatesValid, context);
253 if (pvt.
error != 0) {
261 !(fabsf(pvt.
vn) > 600.0f || fabsf(pvt.
ve) > 600.0f || fabsf(pvt.
vu) > 600.0f);
263 const bool coordinatesValid = std::isfinite(pvt.
latitude) && std::abs(pvt.
latitude) <= std::numbers::pi / 2 &&
265 std::isfinite(pvt.
height) && std::abs(pvt.
height) <= DNU;
294 return coordinatesValid;
302 const bool active = !std::holds_alternative<GPSBaseStationConfig::Fixed>(_base.mode) && pvt.
modeAutoSet();
305 if (active || _surveyActive) {
306 (void) _surveyClock.update(context.
nowUs());
308 _surveyActive = active;
311 if (coordinatesValid && !pvt.
error) {
316 context.
publishSurvey(active, valid, _surveyClock.duration(), surveyPosition);
319QLatin1StringView signature(
const GPSFrame& frame)
321 const std::string_view text =
frame.text();
323 return text.starts_with(
"$R:") || text.starts_with(
"$R?") ? QLatin1StringView(
"Septentrio command replies")
324 : QLatin1StringView();
331 return header && header->length % 4 == 0 ? QLatin1StringView(
"SBF blocks") : QLatin1StringView();
341 const bool answered = detectPort(channel, port);
343 : channel.failed() ? channel.failureOutcome()
353 .
type = GPSType::septentrio,
354 .logCategory = &SBFProtocolLog,
357 .create = &gpsCreateProtocol<Protocol>,
358 .signature = &signature,
@ Protocol
The receiver violated or rejected the control protocol.
std::optional< uint8_t > gpsSatellitesUsed(std::optional< unsigned > count)
A reported used-satellite count; receivers report UINT8_MAX and above when the count is unknown.
@ SBF
Septentrio "$@" block candidates, up to the first 110 bytes; the decoder checks the CRC.
@ ASCIILine
Printable lines up to CR/LF, without the terminator; the decoder checks checksums.
@ RTCM3
0xD3 frames with a valid CRC-24Q.
#define QGC_LOGGING_CATEGORY(name, categoryStr)
void beginCommand(GPSConfigurationStep step)
GPSReceiveUpdates receiveCycle(std::chrono::milliseconds timeout)
int read(std::span< uint8_t > buffer, std::chrono::milliseconds timeout)
Reads raw bytes without decoding them.
bool setBaudrate(unsigned baudrate)
void failControl(GPSProtocolError failure=GPSProtocolError::Protocol)
GPSDeadlineScope deadlineScope(std::chrono::milliseconds timeout)
GPSStreamDemux & stream()
bool write(QByteArrayView bytes)
bool runRequired(const GPSCommandSequence &sequence)
Runs sequence and fails control over a failed step as failSequence() does.
GPSCommandSequence::Result runSequence(const GPSCommandSequence &sequence)
Runs sequence in order until a required step fails or I/O fails; failed attempts retry up to their co...
void finishCommand(GPSCommandOutcome outcome)
void publishSatelliteUsage(std::optional< int > count)
void publishPosition(const GPSDecodedPosition &report)
void failControl(const QString &detail)
Records a protocol failure described by detail unless a failure is already recorded,...
void publishRTCM(std::span< const uint8_t > frame)
GPSStreamDemux & stream()
void publishSurvey(GPSSurveyReport &status)
Stamps status with the receipt time and publishes a copy. Survey progress is not an update flag.
virtual bool receiverReady() const
virtual bool probe(GPSCommandChannel &channel)
virtual GPSReceiveUpdates receive(GPSCommandChannel &channel, std::chrono::milliseconds timeout)
One streaming receive. The default runs GPSCommandChannel::receiveCycle() and then the streaming serv...
virtual GPSReceiveUpdates onFrame(const GPSFrame &frame, GPSDecodeContext &context)=0
virtual bool armDecodeOnly(const GPSConfig &config, GPSDecodeContext &context)
virtual bool configure(GPSCommandChannel &channel, GPSConfig config, unsigned &baud)=0
void setEnabled(GPSFrameKind kind, bool enabled)
Enables or disables an available framer; either way it restarts from an empty state.
Host-timed survey-in duration for receivers that report completion but not elapsed time.
constexpr std::array BAUD_RATES
Rates the port query probes, in order. Trimble receivers default to 115200.
GPSPositionReport position(const GPSDecodedPosition &source, const GPSIntegrityReport &diagnostic, uint64_t nowUs)
constexpr double DEG_TO_RAD
constexpr double RAD_TO_DEG
constexpr uint64_t epochMs(uint32_t week, uint32_t towMs)
Milliseconds since the GPS epoch of towMs into week.
constexpr uint32_t WEEK_MS
Milliseconds in a GPS week; every time of week is below it.
constexpr bool isAsciiUpper(char ch)
constexpr bool isAsciiDigit(char ch)
Locale-independent ASCII character classes for receiver text.
std::optional< Frame > frame(std::string_view text)
constexpr uint16_t PVT_GEODETIC
constexpr QByteArrayView PROMPT("\n\r")
Asks for the command prompt, which starts with the connection descriptor, such as "USB1>".
GPSCommandSequence base(QByteArrayView port, const GPSBaseStationConfig &base)
Streams RTCM3 corrections and the PVTGeodetic survey status over port in the mode base selects.
constexpr unsigned BAUD_RATE
The link rate unless one is selected or detected. It is set on the host side first.
constexpr std::chrono::milliseconds PROMPT_TIMEOUT
constexpr QByteArrayView FORCE_COMMAND_INPUT("SSSSSSSSSS\n")
Forces the connection back to command input, whatever it was set to accept.
GPSCommandSequence port(QByteArrayView port, unsigned baud)
Clears the SBF output of port, keeps a serial port at the link's baud, and selects SBF and the WGS84 ...
GPSCommandSequence silenceCorrectionOutput()
Septentrio receivers configured as RTK bases with SBF commands; they stream PVTGeodetic blocks and RT...
const GPSReceiverFamily FAMILY
std::optional< BlockHeader > checkedHeader(std::span< const uint8_t > block)
The header of a block candidate whose length fits the candidate and whose CRC matches; nullopt otherw...
Configuration used only by the RTK base-station role.
The receiver configuration requested for one configure() attempt.
static GPSDeadline after(uint64_t nowUs, std::chrono::microseconds timeout)
A negative timeout is already due at nowUs.
std::chrono::milliseconds remaining(uint64_t nowUs) const
Rounded up to whole milliseconds and capped at INT32_MAX milliseconds.
Decoded navigation plus whether the producer accepted its velocity solution.
WGS84 geodetic position with ellipsoid height, not mean-sea-level altitude.
std::optional< uint8_t > satellitesUsed
float verticalAccuracyMeters
float speedMetersPerSecond
float horizontalAccuracyMeters
double altitudeEllipsoidMeters
GPSNavigationValues navigation
constexpr bool modeAutoSet() const
constexpr bool mode2D() const
constexpr uint8_t modeType() const