Update GxGSV NMEA 4.10 - Add Signal averaging - Update gpsinformationwidget (#58357)

* Add signal_id; //!< NMEA v4.1 - ID of the ranging signal
NMEA RMC - Set qualitydescription only for status: A or V in  processRmcSentence
NMEA GSV - Add Signal averaging processGsvSentence
Update for Status not A, V  - for UM982 Status = D - Differential when it becomes RTKfix
This commit is contained in:
Mauro Bettella 2025-02-10 05:23:02 +01:00 committed by GitHub
parent c55e712997
commit 2027519a15
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
5 changed files with 163 additions and 74 deletions

63
external/nmea/parse.c vendored
View File

@ -335,6 +335,7 @@ int nmea_parse_GPGSA( const char *buff, int buff_sz, nmeaGPGSA *pack )
int nmea_parse_GPGSV( const char *buff, int buff_sz, nmeaGPGSV *pack )
{
int nsen, nsat;
int has_signal_id = 0;
NMEA_ASSERT( buff && pack );
@ -342,29 +343,63 @@ int nmea_parse_GPGSV( const char *buff, int buff_sz, nmeaGPGSV *pack )
nmea_trace_buff( buff, buff_sz );
nsen = nmea_scanf( buff, buff_sz,
"$%C%CGSV,%d,%d,%d,"
"%d,%d,%d,%d,"
"%d,%d,%d,%d,"
"%d,%d,%d,%d,"
"%d,%d,%d,%d*",
&( pack->talkerId[0] ), &( pack->talkerId[1] ),
&( pack->pack_count ), &( pack->pack_index ), &( pack->sat_count ),
&( pack->sat_data[0].id ), &( pack->sat_data[0].elv ), &( pack->sat_data[0].azimuth ), &( pack->sat_data[0].sig ),
&( pack->sat_data[1].id ), &( pack->sat_data[1].elv ), &( pack->sat_data[1].azimuth ), &( pack->sat_data[1].sig ),
&( pack->sat_data[2].id ), &( pack->sat_data[2].elv ), &( pack->sat_data[2].azimuth ), &( pack->sat_data[2].sig ),
&( pack->sat_data[3].id ), &( pack->sat_data[3].elv ), &( pack->sat_data[3].azimuth ), &( pack->sat_data[3].sig ) );
// Count the number of fields in the sentence
const char *ptr = buff;
int field_count = 1;
while ( ( ptr = strchr( ptr, ',' ) ) != NULL )
{
field_count++;
ptr++;
}
// Check if the number of fields is even, indicating the presence of SIGNAL_ID
has_signal_id = field_count >= 21 ? 1 : 0;
if ( has_signal_id )
{
// SIGNAL_ID is present NMEA 4.10
nsen = nmea_scanf( buff, buff_sz,
"$%C%CGSV,%d,%d,%d,"
"%d,%d,%d,%d,"
"%d,%d,%d,%d,"
"%d,%d,%d,%d,"
"%d,%d,%d,%d,%d*",
&( pack->talkerId[0] ), &( pack->talkerId[1] ),
&( pack->pack_count ), &( pack->pack_index ), &( pack->sat_count ),
&( pack->sat_data[0].id ), &( pack->sat_data[0].elv ), &( pack->sat_data[0].azimuth ), &( pack->sat_data[0].sig ),
&( pack->sat_data[1].id ), &( pack->sat_data[1].elv ), &( pack->sat_data[1].azimuth ), &( pack->sat_data[1].sig ),
&( pack->sat_data[2].id ), &( pack->sat_data[2].elv ), &( pack->sat_data[2].azimuth ), &( pack->sat_data[2].sig ),
&( pack->sat_data[3].id ), &( pack->sat_data[3].elv ), &( pack->sat_data[3].azimuth ), &( pack->sat_data[3].sig ),
&( pack->signal_id ) );
}
else
{
// SIGNAL_ID is not present
nsen = nmea_scanf( buff, buff_sz,
"$%C%CGSV,%d,%d,%d,"
"%d,%d,%d,%d,"
"%d,%d,%d,%d,"
"%d,%d,%d,%d,"
"%d,%d,%d,%d*",
&( pack->talkerId[0] ), &( pack->talkerId[1] ),
&( pack->pack_count ), &( pack->pack_index ), &( pack->sat_count ),
&( pack->sat_data[0].id ), &( pack->sat_data[0].elv ), &( pack->sat_data[0].azimuth ), &( pack->sat_data[0].sig ),
&( pack->sat_data[1].id ), &( pack->sat_data[1].elv ), &( pack->sat_data[1].azimuth ), &( pack->sat_data[1].sig ),
&( pack->sat_data[2].id ), &( pack->sat_data[2].elv ), &( pack->sat_data[2].azimuth ), &( pack->sat_data[2].sig ),
&( pack->sat_data[3].id ), &( pack->sat_data[3].elv ), &( pack->sat_data[3].azimuth ), &( pack->sat_data[3].sig ) );
}
nsat = ( pack->pack_index - 1 ) * NMEA_SATINPACK;
nsat = ( nsat + NMEA_SATINPACK > pack->sat_count ) ? pack->sat_count - nsat : NMEA_SATINPACK;
nsat = nsat * 4 + 3 /* first three sentence`s */;
if ( nsen - 2 < nsat || nsen - 2 > ( NMEA_SATINPACK * 4 + 3 ) )
int expected_nsen = has_signal_id ? ( NMEA_SATINPACK * 4 + 4 ) : ( NMEA_SATINPACK * 4 + 3 );
if ( nsen - 2 < nsat || nsen - 2 > expected_nsen )
{
nmea_error( "GSV parse error!" );
return 0;
}
return 1;
}

View File

@ -100,6 +100,7 @@ typedef struct _nmeaGPGSV
int pack_index; //!< Message number
int sat_count; //!< Total number of satellites in view
nmeaSATELLITE sat_data[NMEA_SATINPACK];
char signal_id; //!< NMEA v4.1 - ID of the ranging signal
} nmeaGPGSV;

View File

@ -43,6 +43,7 @@
#include <qwt_polar_grid.h>
#include <qwt_polar_curve.h>
#include <qwt_scale_engine.h>
// #include <qwt_symbol.h>
#endif
#include <QMessageBox>
@ -82,18 +83,18 @@ QgsGpsInformationWidget::QgsGpsInformationWidget( QgsAppGpsConnection *connectio
//
mPlot = new QwtPlot( mpHistogramWidget );
mPlot->setAutoReplot( false ); // plot on demand
//mPlot->setTitle(QObject::tr("Signal Status"));
//mPlot->insertLegend(new QwtLegend(), QwtPlot::BottomLegend);
// mPlot->setTitle(QObject::tr("Signal Status"));
// mPlot->insertLegend(new QwtLegend(), QwtPlot::BottomLegend);
// Set axis titles
//mPlot->setAxisTitle(QwtPlot::xBottom, QObject::tr("Satellite"));
//mPlot->setAxisTitle(QwtPlot::yLeft, QObject::tr("Value"));
// mPlot->setAxisTitle(QwtPlot::xBottom, QObject::tr("Satellite"));
// mPlot->setAxisTitle(QwtPlot::yLeft, QObject::tr("Value"));
mPlot->setAxisScale( QwtPlot::xBottom, 0, 20 );
mPlot->setAxisScale( QwtPlot::yLeft, 0, 60 ); // max is 50dB SNR, I believe - SLM
// add a grid
QwtPlotGrid *mGrid = new QwtPlotGrid();
mGrid->enableX( false );
mGrid->attach( mPlot );
//display satellites first
// display satellites first
mCurve = new QwtPlotCurve();
mCurve->setRenderHint( QwtPlotItem::RenderAntialiased );
mCurve->setPen( QPen( Qt::blue ) );
@ -101,7 +102,7 @@ QgsGpsInformationWidget::QgsGpsInformationWidget( QgsAppGpsConnection *connectio
mPlot->enableAxis( QwtPlot::yLeft, true );
mPlot->enableAxis( QwtPlot::xBottom, false );
mCurve->attach( mPlot );
//ensure all children get removed
// ensure all children get removed
mPlot->setAutoDelete( true );
QVBoxLayout *mpHistogramLayout = new QVBoxLayout( mpHistogramWidget );
mpHistogramLayout->setContentsMargins( 0, 0, 0, 0 );
@ -118,17 +119,17 @@ QgsGpsInformationWidget::QgsGpsInformationWidget( QgsAppGpsConnection *connectio
mpSatellitesWidget->setPlotBackground( Qt::white );
// scales
mpSatellitesWidget->setScale( QwtPolar::ScaleAzimuth,
360, //min - reverse the min/max values to get compass orientation - increasing clockwise
0, //max
90 //interval - just show cardinal and intermediate (NE, N, NW, etc.) compass points (in degrees)
360, // min - reverse the min/max values to get compass orientation - increasing clockwise
0, // max
90 // interval - just show cardinal and intermediate (NE, N, NW, etc.) compass points (in degrees)
);
mpSatellitesWidget->setAzimuthOrigin( M_PI_2 ); // to get compass orientation - need to rotate 90 deg. ccw; this is in Radians (not indicated in QwtPolarPlot docs)
// mpSatellitesWidget->setScaleMaxMinor( QwtPolar::ScaleRadius, 2 ); // seems unnecessary
// mpSatellitesWidget->setScaleMaxMinor( QwtPolar::ScaleRadius, 2 ); // seems unnecessary
mpSatellitesWidget->setScale( QwtPolar::ScaleRadius,
90, //min - reverse the min/max to get 0 at edge, 90 at center
0, //max
45 //interval
90, // min - reverse the min/max to get 0 at edge, 90 at center
0, // max
45 // interval
);
// grids, axes
@ -138,25 +139,25 @@ QgsGpsInformationWidget::QgsGpsInformationWidget( QgsAppGpsConnection *connectio
QPen minorPen( Qt::gray ); // moved outside of for loop; NOTE setting the minor pen isn't necessary if the minor grids aren't shown
for ( int scaleId = 0; scaleId < QwtPolar::ScaleCount; scaleId++ )
{
//mpSatellitesGrid->showGrid( scaleId );
//mpSatellitesGrid->showMinorGrid(scaleId);
// mpSatellitesGrid->showGrid( scaleId );
// mpSatellitesGrid->showMinorGrid(scaleId);
mpSatellitesGrid->setMinorGridPen( scaleId, minorPen );
}
// mpSatellitesGrid->setAxisPen( QwtPolar::AxisAzimuth, QPen( Qt::black ) );
// mpSatellitesGrid->setAxisPen( QwtPolar::AxisAzimuth, QPen( Qt::black ) );
mpSatellitesGrid->showAxis( QwtPolar::AxisAzimuth, true );
mpSatellitesGrid->showAxis( QwtPolar::AxisLeft, false ); //alt axis
mpSatellitesGrid->showAxis( QwtPolar::AxisRight, false ); //alt axis
mpSatellitesGrid->showAxis( QwtPolar::AxisTop, false ); //alt axis
mpSatellitesGrid->showAxis( QwtPolar::AxisBottom, false ); //alt axis
mpSatellitesGrid->showAxis( QwtPolar::AxisLeft, false ); // alt axis
mpSatellitesGrid->showAxis( QwtPolar::AxisRight, false ); // alt axis
mpSatellitesGrid->showAxis( QwtPolar::AxisTop, false ); // alt axis
mpSatellitesGrid->showAxis( QwtPolar::AxisBottom, false ); // alt axis
mpSatellitesGrid->showGrid( QwtPolar::ScaleAzimuth, false ); // hide the grid; just show ticks at edge
mpSatellitesGrid->showGrid( QwtPolar::ScaleRadius, true );
// mpSatellitesGrid->showMinorGrid( QwtPolar::ScaleAzimuth, true );
// mpSatellitesGrid->showMinorGrid( QwtPolar::ScaleAzimuth, true );
mpSatellitesGrid->showMinorGrid( QwtPolar::ScaleRadius, true ); // for 22.5, 67.5 degree circles
mpSatellitesGrid->attach( mpSatellitesWidget );
//QwtLegend *legend = new QwtLegend;
//mpSatellitesWidget->insertLegend(legend, QwtPolarPlot::BottomLegend);
// QwtLegend *legend = new QwtLegend;
// mpSatellitesWidget->insertLegend(legend, QwtPolarPlot::BottomLegend);
QVBoxLayout *mpPolarLayout = new QVBoxLayout( mpPolarWidget );
mpPolarLayout->setContentsMargins( 0, 0, 0, 0 );
mpPolarLayout->addWidget( mpSatellitesWidget );
@ -366,41 +367,36 @@ void QgsGpsInformationWidget::displayGPSInformation( const QgsGpsInformation &in
{
QVector<QPointF> data;
if ( mStackedWidget->currentIndex() == 1 && info.satInfoComplete ) //signal
{
mPlot->setAxisScale( QwtPlot::xBottom, 0, info.satellitesInView.size() );
} //signal
#ifdef WITH_QWTPOLAR
if ( mStackedWidget->currentIndex() == 2 && info.satInfoComplete ) //satellites
if ( mStackedWidget->currentIndex() == 2 && info.satInfoComplete ) // satellites
{
while ( !mMarkerList.isEmpty() )
{
delete mMarkerList.takeFirst();
}
} //satellites
} // satellites
#endif
if ( mStackedWidget->currentIndex() == 3 ) //debug
if ( mStackedWidget->currentIndex() == 3 ) // debug
{
mGPSPlainTextEdit->clear();
} //debug
} // debug
for ( int i = 0; i < info.satellitesInView.size(); ++i ) //satellite processing loop
for ( int i = 0; i < info.satellitesInView.size(); ++i ) // satellite processing loop
{
const QgsSatelliteInfo currentInfo = info.satellitesInView.at( i );
if ( mStackedWidget->currentIndex() == 1 && info.satInfoComplete ) //signal
if ( mStackedWidget->currentIndex() == 1 && info.satInfoComplete ) // signal
{
data << QPointF( i, 0 );
data << QPointF( i, currentInfo.signal );
data << QPointF( i + 1, currentInfo.signal );
data << QPointF( i + 1, 0 );
} //signal
} // signal
if ( mStackedWidget->currentIndex() == 2 && info.satInfoComplete ) //satellites
if ( mStackedWidget->currentIndex() == 2 && info.satInfoComplete ) // satellites
{
QColor bg( Qt::white ); // moved several items outside of the following if block to minimize loop time
bg.setAlpha( 200 );
QColor myColor;
// Add a marker to the polar plot
if ( currentInfo.id > 0 ) // don't show satellite if id=0 (no satellite indication)
@ -413,22 +409,61 @@ void QgsGpsInformationWidget::displayGPSInformation( const QgsGpsInformation &in
mypMarker->setPosition( QwtPointPolar( currentInfo.azimuth, currentInfo.elevation ) );
#endif
#endif
if ( currentInfo.signal < 30 ) //weak signal
#ifdef WITH_QWTPOLAR
// QBrush symbolBrush( Qt::black );
QSize markerSize( 10, 10 );
QBrush textBgBrush( bg );
QBrush symbolBrush;
QColor myColor( Qt::black );
QColor penColor( Qt::black );
QwtSymbol::Style symbolStyle;
if ( currentInfo.satType == 'P' )
{
myColor = Qt::red;
symbolStyle = QwtSymbol::Ellipse; // GPS
myColor = QColor( 50, 205, 20 ); // limegreen
}
else if ( currentInfo.satType == 'L' )
{
symbolStyle = QwtSymbol::Rect; // GLONASS
myColor = QColor( 255, 165, 0 ); // orange
}
else if ( currentInfo.satType == 'B' )
{
symbolStyle = QwtSymbol::Diamond; // BEIDOU
myColor = QColor( 128, 0, 128 ); // purple
}
else if ( currentInfo.satType == 'A' )
{
symbolStyle = QwtSymbol::Triangle; // GALILEO
myColor = QColor( 0, 0, 255 ); // blue
}
else if ( currentInfo.satType == 'Q' )
{
symbolStyle = QwtSymbol::Cross; // QZSS
myColor = QColor( 255, 0, 255 ); // magenta
}
else
{
myColor = Qt::black; //strong signal
symbolStyle = QwtSymbol::Ellipse; // N, S
myColor = QColor( 128, 128, 128 ); // gray
}
#ifdef WITH_QWTPOLAR
QBrush symbolBrush( Qt::black );
QSize markerSize( 9, 9 );
QBrush textBgBrush( bg );
penColor = myColor;
symbolBrush = QBrush( myColor );
if ( currentInfo.signal < 30 ) // weak signal
{
penColor = Qt::red; // red border
}
if ( currentInfo.inUse )
{
penColor = Qt::black; // black border
}
#if ( QWT_POLAR_VERSION < 0x010000 )
mypMarker->setSymbol( QwtSymbol( QwtSymbol::Ellipse, symbolBrush, QPen( myColor ), markerSize ) );
mypMarker->setSymbol( QwtSymbol( symbolStyle, symbolBrush, QPen( penColor ), markerSize ) );
#else
mypMarker->setSymbol( new QwtSymbol( QwtSymbol::Ellipse, symbolBrush, QPen( myColor ), markerSize ) );
mypMarker->setSymbol( new QwtSymbol( symbolStyle, symbolBrush, QPen( penColor ), markerSize ) );
#endif
mypMarker->setLabelAlignment( Qt::AlignHCenter | Qt::AlignTop );
@ -440,19 +475,20 @@ void QgsGpsInformationWidget::displayGPSInformation( const QgsGpsInformation &in
mMarkerList << mypMarker;
#endif
} // currentInfo.id > 0
} //satellites
} //satellite processing loop
} // satellites
} // satellite processing loop
if ( mStackedWidget->currentIndex() == 1 && info.satInfoComplete ) //signal
if ( mStackedWidget->currentIndex() == 1 && info.satInfoComplete ) // signal
{
mPlot->setAxisScale( QwtPlot::xBottom, 0, info.satellitesInView.size() );
mCurve->setSamples( data );
mPlot->replot();
} //signal
} // signal
#ifdef WITH_QWTPOLAR
if ( mStackedWidget->currentIndex() == 2 && info.satInfoComplete ) //satellites
if ( mStackedWidget->currentIndex() == 2 && info.satInfoComplete ) // satellites
{
mpSatellitesWidget->replot();
} //satellites
} // satellites
#endif
const bool validFlag = info.isValid();
@ -466,7 +502,7 @@ void QgsGpsInformationWidget::displayGPSInformation( const QgsGpsInformation &in
myNewCenter = mLastGpsPosition;
}
if ( mStackedWidget->currentIndex() == 0 ) //position
if ( mStackedWidget->currentIndex() == 0 ) // position
{
QString formattedX;
QString formattedY;
@ -485,7 +521,7 @@ void QgsGpsInformationWidget::displayGPSInformation( const QgsGpsInformation &in
}
else
{
mTxtDateTime->setText( info.utcDateTime.toString( mDateTimeFormat ) ); //user specified format string for testing the millisecond part of time
mTxtDateTime->setText( info.utcDateTime.toString( mDateTimeFormat ) ); // user specified format string for testing the millisecond part of time
}
if ( std::isfinite( info.speed ) )
{
@ -548,7 +584,7 @@ void QgsGpsInformationWidget::displayGPSInformation( const QgsGpsInformation &in
mTxtQuality->setText( info.qualityDescription() );
mTxtSatellitesUsed->setText( tr( "%1 used (%2 in view)" ).arg( info.satellitesUsed ).arg( info.satellitesInView.size() ) );
mTxtStatus->setText( info.status == 'A' ? tr( "Valid" ) : info.status == 'V' ? tr( "Invalid" )
: QString() );
: tr( "Other (%1)" ).arg( info.status ) );
}
if ( mLastGpsPosition != myNewCenter )

View File

@ -50,16 +50,15 @@ bool QgsGpsInformation::isValid() const
{
valid = false;
}
else if ( status == 'A'
else if ( status == 'A' || status == 'D'
|| bestFix == Qgis::GpsFixStatus::Fix2D
|| bestFix == Qgis::GpsFixStatus::Fix3D
|| ( qualityIndicator != Qgis::GpsQualityIndicator::Invalid ) ) // good
|| ( qualityIndicator != Qgis::GpsQualityIndicator::Invalid ) ) // good - D=Differential for UM98x
{
valid = true;
}
valid &= longitude >= -180.0 && longitude <= 180.0 && latitude >= -90.0 && latitude <= 90.0;
return valid;
}

View File

@ -1,3 +1,4 @@
/***************************************************************************
qgsnmeaconnection.cpp - description
---------------------
@ -177,6 +178,11 @@ void QgsNmeaConnection::processStringBuffer()
}
emit nmeaSentenceReceived( substring ); // added to be able to save raw data
}
else
{
//other text strings that do not start with '$'
mLastGPSInformation.satInfoComplete = true;
}
}
mStringBuffer.remove( 0, endSentenceIndex + 2 );
}
@ -310,7 +316,8 @@ void QgsNmeaConnection::processRmcSentence( const char *data, int len )
// convert mode to signal (aka quality) indicator
// (see https://gitlab.com/fhuberts/nmealib/-/blob/master/src/info.c#L27)
if ( result.status == 'A' )
// UM98x Status == D (Differential)
if ( result.status == 'A' || result.status == 'D' )
{
if ( result.mode == 'A' )
{
@ -358,11 +365,12 @@ void QgsNmeaConnection::processRmcSentence( const char *data, int len )
mLastGPSInformation.qualityIndicator = Qgis::GpsQualityIndicator::Unknown;
}
}
else
else if ( result.status == 'V' )
{
mLastGPSInformation.quality = static_cast<int>( Qgis::GpsQualityIndicator::Invalid );
mLastGPSInformation.qualityIndicator = Qgis::GpsQualityIndicator::Invalid;
}
// for other cases: quality and qualityIndicator read by GGA
}
if ( result.navstatus == 'S' )
@ -441,11 +449,21 @@ void QgsNmeaConnection::processGsvSentence( const char *data, int len )
bool idAlreadyPresent = false;
if ( mLastGPSInformation.satellitesInView.size() > NMEA_SATINPACK )
{
for ( const QgsSatelliteInfo &existingSatInView : std::as_const( mLastGPSInformation.satellitesInView ) )
for ( int i = 0; i < mLastGPSInformation.satellitesInView.size(); ++i )
{
QgsSatelliteInfo &existingSatInView = mLastGPSInformation.satellitesInView[i];
if ( existingSatInView.id == currentSatellite.id )
{
idAlreadyPresent = true;
// Signal averaging
if ( existingSatInView.signal == 0 )
{
existingSatInView.signal = currentSatellite.sig;
}
else if ( currentSatellite.sig != 0 )
{
existingSatInView.signal = ( existingSatInView.signal + currentSatellite.sig ) / 2;
}
break;
}
}