location-gps-nmea: add complete sequence to traces at once

Currently sequence messages are added to traces as they are
received. Therefore content of traces depends at the time, when
request is issued and data is reported. Sometimes not all sequence
traces are reported, because only first record was received yet and it
overwrote previous full sequence trace.

For example,

```
$GPGSV,3,1,12,01,35,282,,02,68,264,,03,02,243,,08,35,206,,1*68
$GPGSV,3,2,12,10,46,074,,14,16,312,,17,,,,22,16,327,,1*53
$GPGSV,3,3,12,23,07,068,,24,15,030,,27,14,181,,32,51,120,,1*60

$GLGSV,2,1,08,82,25,135,,76,11,029,,75,21,334,,69,11,201,,1*7A
```

one GSV sentence is missing and second later full sequence is
reported

```
$GPGSV,3,1,12,01,35,282,,02,68,264,,03,02,243,,08,35,206,,1*68
$GPGSV,3,2,12,10,46,074,,14,16,312,,17,,,,22,16,327,,1*53
$GPGSV,3,3,12,23,07,068,,24,15,030,,27,14,181,,32,51,120,,1*60

$GLGSV,2,1,08,82,25,135,,76,11,029,,75,21,334,,69,11,201,,1*7A
$GLGSV,2,2,08,84,40,306,,83,79,161,,67,56,056,,68,58,177,,1*7D
```

Therefore client software reports different number of visible
satellites.

Usually sequence sentences are reported consequently in NMEA
stream. Let's cache and join sequence sentences until new trace type
is received. In that case full sequence always overrides
previous full sequence.

Signed-off-by: Andrey Skvortsov <andrej.skvortzov@gmail.com>
This commit is contained in:
Andrey Skvortsov
2026-05-26 20:56:53 +00:00
parent 38548fedd3
commit f7da97989b
+55 -19
View File
@@ -45,16 +45,20 @@ G_DEFINE_TYPE (MMLocationGpsNmea, mm_location_gps_nmea, G_TYPE_OBJECT)
struct _MMLocationGpsNmeaPrivate {
GHashTable *traces;
GRegex *sequence_regex;
gchar *sequence_last_trace;
gchar *sequence_last_trace_type;
GRegex *sequence_regex;
};
/*****************************************************************************/
static gboolean
check_append_or_replace (MMLocationGpsNmea *self,
const gchar *trace)
is_sequence_trace_type (MMLocationGpsNmea *self,
const gchar *trace,
gboolean *first)
{
g_autoptr(GMatchInfo) match_info = NULL;
guint index;
if (G_UNLIKELY (!self->priv->sequence_regex))
self->priv->sequence_regex = g_regex_new ("\\$..(?:ALM|GSV|RTE|SFI),(\\d),(\\d).*",
@@ -62,15 +66,13 @@ check_append_or_replace (MMLocationGpsNmea *self,
0,
NULL);
*first = FALSE;
if (g_regex_match (self->priv->sequence_regex, trace, 0, &match_info)) {
guint index;
/* If we don't have the first element of a sequence, append */
if (mm_get_uint_from_match_info (match_info, 2, &index) && index != 1)
if (mm_get_uint_from_match_info (match_info, 2, &index)) {
*first = (index == 1);
return TRUE;
}
}
/* By default, replace */
return FALSE;
}
@@ -81,6 +83,10 @@ location_gps_nmea_take_trace (MMLocationGpsNmea *self,
gchar *i;
g_autofree gchar *trace_type = NULL;
g_autofree gchar *trace = _trace;
g_autofree gchar *sequence = NULL;
gboolean first;
gboolean sequence_trace;
i = strchr (trace, ',');
if (!i || i == trace)
@@ -93,14 +99,38 @@ location_gps_nmea_take_trace (MMLocationGpsNmea *self,
/* Some traces are part of a SEQUENCE; so we need to decide whether we
* completely replace the previous trace, or we append the new one to
* the already existing list */
if (check_append_or_replace (self, trace)) {
sequence_trace = is_sequence_trace_type (self, trace, &first);
/* Save previous sequence trace, if
* new trace type is different or this is the first sequence trace */
if (self->priv->sequence_last_trace_type &&
self->priv->sequence_last_trace &&
(g_strcmp0 (self->priv->sequence_last_trace_type, trace_type) ||
(sequence_trace && first))) {
g_hash_table_replace (self->priv->traces,
g_steal_pointer (&self->priv->sequence_last_trace_type),
g_steal_pointer (&self->priv->sequence_last_trace));
}
if (!sequence_trace) {
g_hash_table_replace (self->priv->traces,
g_steal_pointer (&trace_type),
g_steal_pointer (&trace));
return TRUE;
}
/* Process the trace as part of a sequence. */
if (first) {
/* Record first record in the sequence */
sequence = g_steal_pointer (&trace);
} else {
/* Append */
const gchar *previous;
const gchar *previous = self->priv->sequence_last_trace;
if (!previous)
previous = g_hash_table_lookup (self->priv->traces, trace_type);
previous = g_hash_table_lookup (self->priv->traces, trace_type);
if (previous) {
g_autofree gchar *sequence = NULL;
/* Skip the trace if we already have it there */
if (strstr (previous, trace))
return TRUE;
@@ -109,14 +139,18 @@ location_gps_nmea_take_trace (MMLocationGpsNmea *self,
previous,
g_str_has_suffix (previous, "\r\n") ? "" : "\r\n",
trace);
g_free (trace);
trace = g_steal_pointer (&sequence);
} else {
/* No previous records, record first record in the sequence */
sequence = g_steal_pointer (&trace);
}
}
g_hash_table_replace (self->priv->traces,
g_steal_pointer (&trace_type),
g_steal_pointer (&trace));
g_clear_pointer (&self->priv->sequence_last_trace, g_free);
self->priv->sequence_last_trace = g_steal_pointer (&sequence);
g_clear_pointer (&self->priv->sequence_last_trace_type, g_free);
self->priv->sequence_last_trace_type = g_steal_pointer (&trace_type);
return TRUE;
}
@@ -287,6 +321,8 @@ finalize (GObject *object)
g_hash_table_destroy (self->priv->traces);
if (self->priv->sequence_regex)
g_regex_unref (self->priv->sequence_regex);
g_free (self->priv->sequence_last_trace);
g_free (self->priv->sequence_last_trace_type);
G_OBJECT_CLASS (mm_location_gps_nmea_parent_class)->finalize (object);
}