You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@nuttx.apache.org by xi...@apache.org on 2022/08/01 17:29:24 UTC

[incubator-nuttx] 20/22: nuttx/dirvers/sensor: down-sampling 2.0

This is an automated email from the ASF dual-hosted git repository.

xiaoxiang pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/incubator-nuttx.git

commit 9485174ff08edd2d57bed2209e8306ac57d71a5a
Author: jihandong <ji...@xiaomi.com>
AuthorDate: Wed May 11 13:49:39 2022 +0800

    nuttx/dirvers/sensor: down-sampling 2.0
    
    1. about interval:
      If interval is not set, generation is increased by 1 along with
      publish and copy, multi-copy is continuous.
    
      If interval is set, pick proper samples from buffer based on
      mainline/user generation, multi-copy is one-by-one.
    
    2. about bufferpos:
      user->bufferpos always point to next position to check.
    
           data user last read
      ----------v--------------------------
      |        |        |        |
      -------------------^-----------------
                     bufferpos
    
      If buffer is full, bufferpos point to buffer.head
    
    Examples:
      If a buffer contains 4 samples, newest generatoin is 40.
    
      -------------------------------------
      |10      |20      |30      |40
      ------------------------------^------
                                    |
       if user's next generation is 42, notify user to copy No.40 sample,
                    because 42 is closer to 40 than 50.
    
      -------------------------------------
      |10      |20      |30      |40
      ----------------------------------^--
                                        |
       if user's next generation is 48, do not notify user,
             because 48 is closer to 50, which is next mainline sample.
    
    Signed-off-by: jihandong <ji...@xiaomi.com>
    Signed-off-by: Jiuzhu Dong <do...@xiaomi.com>
---
 drivers/sensors/sensor.c | 268 +++++++++++++++++++++++++++++++++--------------
 1 file changed, 192 insertions(+), 76 deletions(-)

diff --git a/drivers/sensors/sensor.c b/drivers/sensors/sensor.c
index 50e9d33cc8..cd8b06eccc 100644
--- a/drivers/sensors/sensor.c
+++ b/drivers/sensors/sensor.c
@@ -46,9 +46,10 @@
 
 /* Device naming ************************************************************/
 
-#define ROUNDUP(x, esize)  ((x + (esize - 1)) / (esize)) * (esize)
-#define DEVNAME_FMT        "/dev/sensor/sensor_%s%s%d"
-#define DEVNAME_UNCAL      "_uncal"
+#define ROUND_DOWN(x, y)    (((x) / (y)) * (y))
+#define DEVNAME_FMT         "/dev/sensor/sensor_%s%s%d"
+#define DEVNAME_UNCAL       "_uncal"
+#define TIMING_BUF_ESIZE    (sizeof(unsigned long))
 
 /****************************************************************************
  * Private Types
@@ -76,6 +77,7 @@ struct sensor_user_s
                                 * asynchronous notify other users
                                 */
   sem_t            buffersem;  /* Wakeup user waiting for data in circular buffer */
+  size_t           bufferpos;  /* The index of user generation in buffer */
 
   /* The subscriber info
    * Support multi advertisers to subscribe their own data when they
@@ -93,7 +95,8 @@ struct sensor_upperhalf_s
 {
   FAR struct sensor_lowerhalf_s *lower;  /* The handle of lower half driver */
   struct sensor_state_s          state;  /* The state of sensor device */
-  struct circbuf_s   buffer;             /* The circular buffer of sensor device */
+  struct circbuf_s   timing;             /* The circular buffer of generation */
+  struct circbuf_s   buffer;             /* The circular buffer of data */
   rmutex_t           lock;               /* Manages exclusive access to file operations */
   struct list_node   userlist;           /* List of users */
 };
@@ -188,27 +191,6 @@ static void sensor_unlock(FAR void *priv)
   nxrmutex_unlock(&upper->lock);
 }
 
-static bool sensor_in_range(unsigned long left, unsigned long value,
-                            unsigned long right)
-{
-  if (left < right)
-    {
-      return left <= value && value < right;
-    }
-  else
-    {
-      /* Maybe the data overflowed and a wraparound occurred */
-
-      return left <= value || value < right;
-    }
-}
-
-static bool sensor_is_updated(unsigned long generation,
-                              unsigned long ugeneration)
-{
-  return generation > ugeneration;
-}
-
 static int sensor_update_interval(FAR struct file *filep,
                                   FAR struct sensor_upperhalf_s *upper,
                                   FAR struct sensor_user_s *user,
@@ -350,6 +332,159 @@ update:
   return ret;
 }
 
+static void sensor_generate_timing(FAR struct sensor_upperhalf_s *upper,
+                                   unsigned long nums)
+{
+  unsigned long interval = upper->state.min_interval != ULONG_MAX ?
+                           upper->state.min_interval : 1;
+  while (nums-- > 0)
+    {
+      upper->state.generation += interval;
+      circbuf_overwrite(&upper->timing, &upper->state.generation,
+                        TIMING_BUF_ESIZE);
+    }
+}
+
+static bool sensor_is_updated(FAR struct sensor_upperhalf_s *upper,
+                              FAR struct sensor_user_s *user)
+{
+  long delta = upper->state.generation - user->generation;
+
+  if (delta <= 0)
+    {
+      return false;
+    }
+  else if (user->interval == ULONG_MAX)
+    {
+      return true;
+    }
+  else
+    {
+      /* Check whether next generation user want in buffer.
+       * generation     next generation(not published yet)
+       * ____v_____________v
+       * ////|//////^      |
+       *         ^ middle point
+       *   next generation user want
+       */
+
+      return delta >= user->interval - (upper->state.min_interval >> 1);
+    }
+}
+
+static void sensor_catch_up(FAR struct sensor_upperhalf_s *upper,
+                            FAR struct sensor_user_s *user)
+{
+  unsigned long generation;
+  long delta;
+
+  circbuf_peek(&upper->timing, &generation, TIMING_BUF_ESIZE);
+  delta = generation - user->generation;
+  if (delta > 0)
+    {
+      user->bufferpos = upper->timing.tail / TIMING_BUF_ESIZE;
+      if (user->interval == ULONG_MAX)
+        {
+          user->generation = generation - 1;
+        }
+      else
+        {
+          delta -= upper->state.min_interval >> 1;
+          user->generation += ROUND_DOWN(delta, user->interval);
+        }
+    }
+}
+
+static ssize_t sensor_do_samples(FAR struct sensor_upperhalf_s *upper,
+                                 FAR struct sensor_user_s *user,
+                                 FAR char *buffer, size_t len)
+{
+  unsigned long generation;
+  ssize_t ret = 0;
+  size_t nums;
+  size_t pos;
+  size_t end;
+
+  sensor_catch_up(upper, user);
+  nums = upper->timing.head / TIMING_BUF_ESIZE - user->bufferpos;
+  if (len < nums * upper->state.esize)
+    {
+      nums = len / upper->state.esize;
+    }
+
+  len = nums * upper->state.esize;
+
+  /* Take samples continuously */
+
+  if (user->interval == ULONG_MAX)
+    {
+      ret = circbuf_peekat(&upper->buffer,
+                           user->bufferpos * upper->state.esize,
+                           buffer, len);
+      user->bufferpos += nums;
+      circbuf_peekat(&upper->timing,
+                     (user->bufferpos - 1) * TIMING_BUF_ESIZE,
+                     &user->generation, TIMING_BUF_ESIZE);
+      return ret;
+    }
+
+  /* Take samples one-bye-one, to determine whether a sample needed:
+   *
+   * If user's next generation is on the left side of middle point,
+   * we should copy this sample for user.
+   *                      next_generation(or end)
+   *                ________________v____
+   * timing buffer: //|//////.      |
+   *                  ^   middle
+   *              generation
+   *                        next sample(or end)
+   *                ________________v____
+   *  data  buffer:   |             |
+   *                  ^
+   *                sample
+   */
+
+  pos = user->bufferpos;
+  end = upper->timing.head / TIMING_BUF_ESIZE;
+  circbuf_peekat(&upper->timing, pos * TIMING_BUF_ESIZE,
+                 &generation, TIMING_BUF_ESIZE);
+  while (pos++ != end)
+    {
+      unsigned long next_generation;
+      long delta;
+
+      if (pos * TIMING_BUF_ESIZE == upper->timing.head)
+        {
+          next_generation = upper->state.generation +
+                            upper->state.min_interval;
+        }
+      else
+        {
+          circbuf_peekat(&upper->timing, pos * TIMING_BUF_ESIZE,
+                         &next_generation, TIMING_BUF_ESIZE);
+        }
+
+      delta = next_generation + generation -
+              ((user->generation + user->interval) << 1);
+      if (delta >= 0)
+        {
+          ret += circbuf_peekat(&upper->buffer,
+                                (pos - 1) * upper->state.esize,
+                                buffer + ret, upper->state.esize);
+          user->bufferpos = pos;
+          user->generation += user->interval;
+          if (ret >= len)
+            {
+              break;
+            }
+        }
+
+      generation = next_generation;
+    }
+
+  return ret;
+}
+
 static void sensor_pollnotify_one(FAR struct sensor_user_s *user,
                                   pollevent_t eventset)
 {
@@ -439,10 +574,12 @@ static int sensor_open(FAR struct file *filep)
   if (upper->state.generation && lower->persist)
     {
       user->generation = upper->state.generation - 1;
+      user->bufferpos  = upper->timing.head / TIMING_BUF_ESIZE - 1;
     }
   else
     {
       user->generation = upper->state.generation;
+      user->bufferpos  = upper->timing.head / TIMING_BUF_ESIZE;
     }
 
   user->interval = ULONG_MAX;
@@ -524,7 +661,6 @@ static ssize_t sensor_read(FAR struct file *filep, FAR char *buffer,
   FAR struct sensor_upperhalf_s *upper = inode->i_private;
   FAR struct sensor_lowerhalf_s *lower = upper->lower;
   FAR struct sensor_user_s *user = filep->f_priv;
-  unsigned long nums;
   ssize_t ret;
 
   if (!buffer || !len)
@@ -554,54 +690,25 @@ static ssize_t sensor_read(FAR struct file *filep, FAR char *buffer,
 
         ret = lower->ops->fetch(lower, filep, buffer, len);
     }
-  else
+  else if (circbuf_is_empty(&upper->buffer))
     {
-      if (circbuf_is_empty(&upper->buffer))
-        {
-          ret = -ENODATA;
-          goto out;
-        }
-
-      /* If the device data is persistent, and when the device has
-       * no new data, the user can copy the old data, otherwise
-       * return -ENODATA.
-       */
-
-      if (user->generation == upper->state.generation)
-        {
-          if (lower->persist)
-            {
-              user->generation--;
-            }
-          else
-            {
-              ret = -ENODATA;
-              goto out;
-            }
-        }
-
-      /* If user's generation isn't within circbuffer range, the
-       * oldest data in circbuffer are returned to the users.
-       */
-
-      if (!sensor_in_range(upper->state.generation - lower->nbuffer,
-                           user->generation, upper->state.generation))
-
-        {
-          user->generation = upper->state.generation - lower->nbuffer;
-        }
-
-      nums = upper->state.generation - user->generation;
-      if (len < nums * upper->state.esize)
-        {
-          nums = len / upper->state.esize;
-        }
+      ret = -ENODATA;
+    }
+  else if (sensor_is_updated(upper, user))
+    {
+      ret = sensor_do_samples(upper, user, buffer, len);
+    }
+  else if (lower->persist)
+    {
+      /* Persistent device can get latest old data if not updated. */
 
-      len = nums * upper->state.esize;
       ret = circbuf_peekat(&upper->buffer,
-                           user->generation * upper->state.esize,
-                           buffer, len);
-      user->generation += nums;
+                           (user->bufferpos - 1) * upper->state.esize,
+                           buffer, upper->state.esize);
+    }
+  else
+    {
+      ret = -ENODATA;
     }
 
 out:
@@ -728,8 +835,7 @@ static int sensor_ioctl(FAR struct file *filep, int cmd, unsigned long arg)
       case SNIOC_UPDATED:
         {
           nxrmutex_lock(&upper->lock);
-          *(FAR bool *)(uintptr_t)arg =
-          sensor_is_updated(upper->state.generation, user->generation);
+          *(FAR bool *)(uintptr_t)arg = sensor_is_updated(upper, user);
           nxrmutex_unlock(&upper->lock);
         }
         break;
@@ -794,7 +900,7 @@ static int sensor_poll(FAR struct file *filep,
                 }
             }
         }
-      else if (sensor_is_updated(upper->state.generation, user->generation))
+      else if (sensor_is_updated(upper, user))
         {
           eventset |= (fds->events & POLLIN);
         }
@@ -848,13 +954,22 @@ static ssize_t sensor_push_event(FAR void *priv, FAR const void *data,
           nxrmutex_unlock(&upper->lock);
           return ret;
         }
+
+      ret = circbuf_init(&upper->timing, NULL, lower->nbuffer *
+                         TIMING_BUF_ESIZE);
+      if (ret < 0)
+        {
+          circbuf_uninit(&upper->buffer);
+          nxrmutex_unlock(&upper->lock);
+          return ret;
+        }
     }
 
   circbuf_overwrite(&upper->buffer, data, bytes);
-  upper->state.generation += envcount;
+  sensor_generate_timing(upper, envcount);
   list_for_every_entry(&upper->userlist, user, struct sensor_user_s, node)
     {
-      if (sensor_is_updated(upper->state.generation, user->generation))
+      if (sensor_is_updated(upper, user))
         {
           nxsem_get_value(&user->buffersem, &semcount);
           if (semcount < 1)
@@ -1102,6 +1217,7 @@ void sensor_custom_unregister(FAR struct sensor_lowerhalf_s *lower,
   if (circbuf_is_init(&upper->buffer))
     {
       circbuf_uninit(&upper->buffer);
+      circbuf_uninit(&upper->timing);
     }
 
   kmm_free(upper);