@@ -34,6 +34,17 @@ Tasks.ino
3434
3535------------------------------------------------------------------------------*/
3636
37+ // ----------------------------------------
38+ // Macros
39+ // ----------------------------------------
40+
41+ #define WRAP_OFFSET (offset, increment, arraySize ) \
42+ { \
43+ offset += increment; \
44+ if (offset >= arraySize) \
45+ offset -= arraySize; \
46+ }
47+
3748// ----------------------------------------
3849// Constants
3950// ----------------------------------------
@@ -75,6 +86,9 @@ unsigned long lastZedI2CSend; // Timestamp of the last time we sent RTCM ZED ove
7586static RING_BUFFER_OFFSET btRingBufferTail; // BT Tail advances as it is sent over BT
7687static RING_BUFFER_OFFSET sdRingBufferTail; // SD Tail advances as it is recorded to SD
7788
89+ // Ring buffer offsets
90+ static uint16_t rbOffsetHead;
91+
7892// ----------------------------------------
7993// Task routines
8094// ----------------------------------------
@@ -343,7 +357,7 @@ void gnssReadTask(void *e)
343357}
344358
345359// Process a complete message incoming from parser
346- // If we get a complete NMEA/UBX/RTCM sentence , pass on to SD/BT/PVT interfaces
360+ // If we get a complete NMEA/UBX/RTCM message , pass on to SD/BT/PVT interfaces
347361void processUart1Message (PARSE_STATE *parse, uint8_t type)
348362{
349363 int32_t bytesToCopy;
@@ -387,13 +401,181 @@ void processUart1Message(PARSE_STATE *parse, uint8_t type)
387401 consumer = (char *)slowConsumer;
388402 if ((bytesToCopy > space) && (!inMainMenu))
389403 {
404+ int32_t bufferedData;
405+ int32_t bytesToDiscard;
406+ int32_t discardedBytes;
407+ int32_t listEnd;
408+ int32_t messageLength;
409+ int32_t offsetBytes;
410+ int32_t previousTail;
411+ int32_t rbOffsetTail;
412+
413+ // Determine the tail of the ring buffer
414+ previousTail = dataHead + space + 1 ;
415+ if (previousTail >= settings.gnssHandlerBufferSize )
416+ previousTail -= settings.gnssHandlerBufferSize ;
417+
418+ /* The rbOffsetArray holds the offsets into the ring buffer of the
419+ * start of each of the parsed messages. A head (rbOffsetHead) and
420+ * tail (rbOffsetTail) offsets are used for this array to insert and
421+ * remove entries. Typically this task only manipulates the head as
422+ * new messages are placed into the ring buffer. The handleGnssDataTask
423+ * normally manipulates the tail as data is removed from the buffer.
424+ * However this task will manipulate the tail under two conditions:
425+ *
426+ * 1. The ring buffer gets full and data must be discarded
427+ *
428+ * 2. The rbOffsetArray is too small to hold all of the message
429+ * offsets for the data in the ring buffer. The array is full
430+ * when (Head + 1) == Tail
431+ *
432+ * Notes:
433+ * The rbOffsetArray is allocated along with the ring buffer in
434+ * Begin.ino
435+ *
436+ * The first entry rbOffsetArray[0] is initialized to zero (0)
437+ * in Begin.ino
438+ *
439+ * The array always has one entry in it containing the head offset
440+ * which contains a valid offset into the ringBuffer, handled below
441+ *
442+ * The empty condition is Tail == Head
443+ *
444+ * The amount of data described by the rbOffsetArray is
445+ * rbOffsetArray[Head] - rbOffsetArray[Tail]
446+ *
447+ * rbOffsetArray ringBuffer
448+ * .-----------------. .-----------------.
449+ * | | | |
450+ * +-----------------+ | |
451+ * Tail --> | Msg 1 Offset |---------->+-----------------+ <-- Tail n
452+ * +-----------------+ | Msg 1 |
453+ * | Msg 2 Offset |--------. | |
454+ * +-----------------+ | | |
455+ * | Msg 3 Offset |------. '->+-----------------+
456+ * +-----------------+ | | Msg 2 |
457+ * Head --> | Head Offset |--. | | |
458+ * +-----------------+ | | | |
459+ * | | | | | |
460+ * +-----------------+ | | | |
461+ * | | | '--->+-----------------+
462+ * +-----------------+ | | Msg 3 |
463+ * | | | | |
464+ * +-----------------+ '------->+-----------------+ <-- dataHead
465+ * | | | |
466+ */
467+
468+ // Determine the index for the end of the circular ring buffer
469+ // offset list
470+ listEnd = rbOffsetHead;
471+ WRAP_OFFSET (listEnd, 1 , rbOffsetEntries);
472+
473+ // Update the tail, walk newest message to oldest message
474+ rbOffsetTail = rbOffsetHead;
475+ bufferedData = 0 ;
476+ messageLength = 0 ;
477+ while ((rbOffsetTail != listEnd) && (bufferedData < use))
478+ {
479+ // Determine the amount of data in the ring buffer up until
480+ // either the tail or the end of the rbOffsetArray
481+ //
482+ // | |
483+ // | | Valid, still in ring buffer
484+ // | Newest |
485+ // +-----------+ <-- rbOffsetHead
486+ // | |
487+ // | | free space
488+ // | |
489+ // rbOffsetTail --> +-----------+ <-- bufferedData
490+ // | ring |
491+ // | buffer | <-- used
492+ // | data |
493+ // +-----------+ Valid, still in ring buffer
494+ // | |
495+ //
496+ messageLength = rbOffsetArray[rbOffsetTail];
497+ WRAP_OFFSET (rbOffsetTail, rbOffsetEntries - 1 , rbOffsetEntries);
498+ messageLength -= rbOffsetArray[rbOffsetTail];
499+ if (messageLength < 0 )
500+ messageLength += settings.gnssHandlerBufferSize ;
501+ bufferedData += messageLength;
502+ }
503+
504+ // Account for any data in the ring buffer not described by the array
505+ //
506+ // | |
507+ // +-----------+
508+ // | Oldest |
509+ // | |
510+ // | ring |
511+ // | buffer | <-- used
512+ // | data |
513+ // +-----------+ Valid, still in ring buffer
514+ // | |
515+ // rbOffsetTail --> +-----------+ <-- bufferedData
516+ // | |
517+ // | Newest |
518+ // +-----------+ <-- rbOffsetHead
519+ // | |
520+ //
521+ discardedBytes = 0 ;
522+ if (bufferedData < use)
523+ discardedBytes = use - bufferedData;
524+
525+ // Writing to the SD card, the network or Bluetooth, a partial
526+ // message may be written leaving the tail pointer mid-message
527+ //
528+ // | |
529+ // rbOffsetTail --> +-----------+
530+ // | Oldest |
531+ // | |
532+ // | ring |
533+ // | buffer | <-- used
534+ // | data | Valid, still in ring buffer
535+ // +-----------+ <--
536+ // | |
537+ // +-----------+
538+ // | |
539+ // | Newest |
540+ // +-----------+ <-- rbOffsetHead
541+ // | |
542+ //
543+ else if (bufferedData > use)
544+ {
545+ // Remove the remaining portion of the oldest entry in the array
546+ discardedBytes = messageLength + use - bufferedData;
547+ WRAP_OFFSET (rbOffsetTail, 1 , rbOffsetEntries);
548+ }
549+
550+ // rbOffsetTail now points to the beginning of a message in the
551+ // ring buffer
552+ // Determine the amount of data to discard
553+ bytesToDiscard = discardedBytes;
554+ if (bytesToDiscard < bytesToCopy)
555+ bytesToDiscard = bytesToCopy;
556+ if (bytesToDiscard < AMOUNT_OF_RING_BUFFER_DATA_TO_DISCARD)
557+ bytesToDiscard = AMOUNT_OF_RING_BUFFER_DATA_TO_DISCARD;
558+
559+ // Walk the ring buffer messages from oldest to newest
560+ while ((discardedBytes < bytesToDiscard) && (rbOffsetTail != rbOffsetHead))
561+ {
562+ // Determine the length of the oldest message
563+ WRAP_OFFSET (rbOffsetTail, 1 , rbOffsetEntries);
564+ discardedBytes = rbOffsetArray[rbOffsetTail] - previousTail;
565+ if (discardedBytes < 0 )
566+ discardedBytes += settings.gnssHandlerBufferSize ;
567+ }
568+
569+ // Discard the oldest data from the ring buffer
390570 if (consumer)
391- systemPrintf (" %s is slow, " , consumer);
392- systemPrintf (" %d bytes of %d in use\r\n " , use, settings.gnssHandlerBufferSize );
393- systemPrintf (" Ring buffer full: discarding %d bytes\r\n " , bytesToCopy);
394- return ;
571+ systemPrintf (" Ring buffer full: discarding %d bytes, %s is slow\r\n " , discardedBytes, consumer);
572+ else
573+ systemPrintf (" Ring buffer full: discarding %d bytes\r\n " , discardedBytes);
574+ updateRingBufferTails (previousTail, rbOffsetArray[rbOffsetTail]);
575+ availableHandlerSpace += discardedBytes;
395576 }
396577
578+ // Add another message to the ring buffer
397579 // Account for this message
398580 availableHandlerSpace -= bytesToCopy;
399581
@@ -422,26 +604,51 @@ void processUart1Message(PARSE_STATE *parse, uint8_t type)
422604 dataHead -= settings.gnssHandlerBufferSize ;
423605 }
424606
607+ // Add the head offset to the offset array
608+ WRAP_OFFSET (rbOffsetHead, 1 , rbOffsetEntries);
609+ rbOffsetArray[rbOffsetHead] = dataHead;
610+
425611 // Display the dataHead offset
426612 if (settings.enablePrintRingBufferOffsets && (!inMainMenu))
427613 systemPrintf (" %4d\r\n " , dataHead);
428614}
429615
430616// Remove previous messages from the ring buffer
431- void updateRingBufferTails (RING_BUFFER_OFFSET previousTail, RING_BUFFER_OFFSET newTail, RING_BUFFER_OFFSET discardedBytes )
617+ void updateRingBufferTails (RING_BUFFER_OFFSET previousTail, RING_BUFFER_OFFSET newTail)
432618{
433- discardRingBufferBytes (&btRingBufferTail, previousTail, newTail, discardedBytes);
434- discardRingBufferBytes (&sdRingBufferTail, previousTail, newTail, discardedBytes);
435- discardPvtClientBytes (previousTail, newTail, discardedBytes);
436- discardPvtServerBytes (previousTail, newTail, discardedBytes);
619+ // Trim any long or medium tails
620+ discardRingBufferBytes (&btRingBufferTail, previousTail, newTail);
621+ discardRingBufferBytes (&sdRingBufferTail, previousTail, newTail);
622+ discardPvtClientBytes (previousTail, newTail);
623+ discardPvtServerBytes (previousTail, newTail);
437624}
438625
439626// Remove previous messages from the ring buffer
440- void discardRingBufferBytes (RING_BUFFER_OFFSET * tail, RING_BUFFER_OFFSET previousTail, RING_BUFFER_OFFSET newTail, RING_BUFFER_OFFSET discardedBytes )
627+ void discardRingBufferBytes (RING_BUFFER_OFFSET * tail, RING_BUFFER_OFFSET previousTail, RING_BUFFER_OFFSET newTail)
441628{
629+ // The longest tail is being trimmed. Medium length tails may contain
630+ // some data within the region begin trimmed. The shortest tails will
631+ // be trimmed.
632+ //
633+ // Devices that get their tails trimmed, may output a partial message
634+ // prior to the buffer trimming. After the trimming, the tail of the
635+ // ring buffer points to the beginning of a new message.
636+ //
637+ // previousTail newTail
638+ // | |
639+ // Before trimming v Discarded v After trimming
640+ // ----+----------------- ... -----+-- .. ---+-----------+------
641+ // | Partial message | | |
642+ // ----+----------------- ... -----+-- .. ---+-----------+------
643+ // ^ ^ ^
644+ // | | |
645+ // long tail ----' '--- medium tail '-- short tail
646+ //
647+ // Determine if the trimmed data wraps the end of the buffer
442648 if (previousTail < newTail)
443649 {
444650 // No buffer wrap occurred
651+ // Only discard the data from long and medium tails
445652 if ((*tail >= previousTail) && (*tail < newTail))
446653 *tail = newTail;
447654 }
@@ -464,6 +671,7 @@ void handleGnssDataTask(void *e)
464671 bool connected;
465672 uint32_t deltaMillis;
466673 int32_t freeSpace;
674+ uint16_t listEnd;
467675 static uint32_t maxMillis[RBC_MAX];
468676 uint32_t startMillis;
469677 int32_t usedSpace;
0 commit comments