sensor analysis: gaussian model
authorOmar Tarabai <tarabai@devegypt.com>
Thu, 26 Jun 2014 16:18:31 +0000 (16:18 +0000)
committerOmar Tarabai <tarabai@devegypt.com>
Thu, 26 Jun 2014 16:18:31 +0000 (16:18 +0000)
src/sensor/gnunet-service-sensor-analysis.c
src/sensor/gnunet_sensor_model_plugin.h
src/sensor/plugin_sensor_model_gaussian.c
src/sensor/sensor.conf.in

index 531f7857d4335c8f16ca60f38f49ecd7fc9165d4..0dcae61b6aa8ccf1191cd0f7f0ab41cc68d9942e 100644 (file)
@@ -27,6 +27,7 @@
 #include "gnunet_util_lib.h"
 #include "sensor.h"
 #include "gnunet_peerstore_service.h"
+#include "gnunet_sensor_model_plugin.h"
 
 #define LOG(kind,...) GNUNET_log_from (kind, "sensor-analysis",__VA_ARGS__)
 
@@ -57,6 +58,11 @@ struct SensorModel
    */
   struct GNUNET_PEERSTORE_WatchContext *wc;
 
+  /*
+   * Closure for model plugin
+   */
+  void *cls;
+
 };
 
 /**
@@ -109,6 +115,12 @@ destroy_sensor_model (struct SensorModel *sensor_model)
     GNUNET_PEERSTORE_watch_cancel(sensor_model->wc);
     sensor_model->wc = NULL;
   }
+  if (NULL != sensor_model->cls)
+  {
+    model_api->destroy_model (sensor_model->cls);
+    sensor_model->cls = NULL;
+  }
+  GNUNET_free(sensor_model);
   sensor_model = NULL;
 }
 
@@ -120,23 +132,23 @@ void SENSOR_analysis_stop()
   struct SensorModel *sm;
 
   LOG (GNUNET_ERROR_TYPE_DEBUG, "Stopping sensor analysis module.\n");
-  if (NULL != model_api)
-  {
-    GNUNET_break (NULL == GNUNET_PLUGIN_unload (model_lib_name, model_api));
-    GNUNET_free (model_lib_name);
-    model_lib_name = NULL;
-  }
   while (NULL != models_head)
   {
     sm = models_head;
-    destroy_sensor_model(sm);
     GNUNET_CONTAINER_DLL_remove(models_head, models_tail, sm);
+    destroy_sensor_model(sm);
   }
   if (NULL != peerstore)
   {
     GNUNET_PEERSTORE_disconnect(peerstore);
     peerstore = NULL;
   }
+  if (NULL != model_api)
+  {
+    GNUNET_break (NULL == GNUNET_PLUGIN_unload (model_lib_name, model_api));
+    GNUNET_free (model_lib_name);
+    model_lib_name = NULL;
+  }
 }
 
 /*
@@ -147,8 +159,28 @@ sensor_watcher (void *cls,
     struct GNUNET_PEERSTORE_Record *record,
     char *emsg)
 {
+  struct SensorModel *sensor_model = cls;
+  double *val;
+  int anomalous;
+
   LOG (GNUNET_ERROR_TYPE_DEBUG,
       "Received a sensor value, will feed to sensor model.\n");
+  if (sizeof(double) != record->value_size)
+  {
+    LOG (GNUNET_ERROR_TYPE_ERROR,
+        _("Received an invalid sensor value."));
+    return GNUNET_YES;
+  }
+  val = (double *)(record->value);
+  anomalous = model_api->feed_model (sensor_model->cls, *val);
+  if (GNUNET_YES == anomalous)
+  {
+    LOG (GNUNET_ERROR_TYPE_WARNING,
+        "Anomaly detected, value: %f.\n",
+        *val);
+  }
+  else
+    LOG (GNUNET_ERROR_TYPE_DEBUG, "Value non-anomalous.\n");
   return GNUNET_YES;
 }
 
@@ -176,6 +208,7 @@ init_sensor_model (void *cls,
   sensor_model->wc = GNUNET_PEERSTORE_watch(peerstore,
           "sensor", &peerid, sensor->name,
           &sensor_watcher, sensor_model);
+  sensor_model->cls = model_api->create_model(model_api->cls);
   GNUNET_CONTAINER_DLL_insert(models_head, models_tail, sensor_model);
   LOG (GNUNET_ERROR_TYPE_DEBUG,
       "Created sensor model for `%s'.\n", sensor->name);
index b2973a60db63b6a6d13878e45d54ab90393d080c..ddb39a928375351d46ca43b3f5e0382cf573f33c 100644 (file)
@@ -57,6 +57,24 @@ struct GNUNET_SENSOR_ModelFunctions
   void *
   (*create_model) (void *cls);
 
+  /*
+   * Destroy a model instance
+   *
+   * @param cls closure (model state)
+   */
+  void
+  (*destroy_model) (void *cls);
+
+  /*
+   * Feed a new value to a model
+   *
+   * @param cls closure (model state)
+   * @param val value to be fed to the model
+   * @return #GNUNET_YES in case of a detected outlier, #GNUNET_NO otherwise
+   */
+  int
+  (*feed_model) (void *cls, double val);
+
 };
 
 
index bf2090217a97e7e9b9b355d8c218e2f474e5fd0e..052b1a94ac05886c61afbb8275c7c81020259b5d 100644 (file)
@@ -42,6 +42,16 @@ struct Plugin
    */
   const struct GNUNET_CONFIGURATION_Handle *cfg;
 
+  /*
+   * Number of initial readings to be used for training only
+   */
+  int training_window;
+
+  /*
+   * Number of standard deviations considered within "normal"
+   */
+  int confidence_interval;
+
 };
 
 /*
@@ -55,8 +65,84 @@ struct Model
    */
   struct Plugin *plugin;
 
+  /*
+   * Number of readings so far
+   */
+  int n;
+
+  /*
+   * Sum of readings
+   */
+  long double sum;
+
+  /*
+   * Sum square of readings
+   */
+  long double sumsq;
+
 };
 
+static void
+update_sums (struct Model *model, double val)
+{
+  model->sum += val;
+  model->sumsq += val * val;
+  model->n ++;
+}
+
+/*
+ * Feed a new value to a model
+ *
+ * @param cls closure (model state)
+ * @param val value to be fed to the model
+ * @return #GNUNET_YES in case of a detected outlier, #GNUNET_NO otherwise
+ */
+static int
+sensor_gaussian_model_feed (void *cls, double val)
+{
+  struct Model *model = cls;
+  struct Plugin *plugin = model->plugin;
+  long double mean;
+  long double stddev;
+  long double allowed_variance;
+
+  if (model->n < plugin->training_window)
+  {
+    update_sums(model, val);
+    return GNUNET_NO;
+  }
+  mean = model->sum / model->n;
+  stddev = sqrt(
+      (model->sumsq - 2 * mean * model->sum + model->n * mean * mean)
+      /
+      (model->n - 1)
+    );
+  allowed_variance = (plugin->confidence_interval * stddev);
+  if ((val < (mean - allowed_variance)) ||
+      (val > (mean + allowed_variance)))
+    return GNUNET_YES;
+  return GNUNET_NO;
+}
+
+/*
+ * Destroy a model instance
+ *
+ * @param cls closure (model state)
+ */
+static void
+sensor_gaussian_model_destroy_model (void *cls)
+{
+  struct Model *model = cls;
+
+  GNUNET_free(model);
+}
+
+/*
+ * Create a model instance
+ *
+ * @param cls closure (plugin state)
+ * @return model state to be used for later calls
+ */
 static void *
 sensor_gaussian_model_create_model (void *cls)
 {
@@ -80,14 +166,34 @@ libgnunet_plugin_sensor_model_gaussian_init (void *cls)
   static struct Plugin plugin;
   const struct GNUNET_CONFIGURATION_Handle *cfg = cls;
   struct GNUNET_SENSOR_ModelFunctions *api;
+  unsigned long long num;
 
   if (NULL != plugin.cfg)
     return NULL;                /* can only initialize once! */
   memset (&plugin, 0, sizeof (struct Plugin));
   plugin.cfg = cfg;
+  if (GNUNET_OK != GNUNET_CONFIGURATION_get_value_number(cfg,
+      "sensor-model-gaussian", "TRAINING_WINDOW", &num))
+  {
+    LOG (GNUNET_ERROR_TYPE_ERROR,
+        _("Missing `TRAINING_WINDOW' value in configuration.\n"));
+    return NULL;
+  }
+  plugin.training_window = (int) num;
+  if (GNUNET_OK != GNUNET_CONFIGURATION_get_value_number(cfg,
+        "sensor-model-gaussian", "CONFIDENCE_INTERVAL", &num))
+  {
+    LOG (GNUNET_ERROR_TYPE_ERROR,
+        _("Missing `CONFIDENCE_INTERVAL' value in configuration.\n"));
+    return NULL;
+  }
+  plugin.confidence_interval = (int) num;
   api = GNUNET_new (struct GNUNET_SENSOR_ModelFunctions);
   api->cls = &plugin;
-  LOG(GNUNET_ERROR_TYPE_DEBUG, "Guassian model plugin is running\n");
+  api->create_model = &sensor_gaussian_model_create_model;
+  api->destroy_model = &sensor_gaussian_model_destroy_model;
+  api->feed_model = &sensor_gaussian_model_feed;
+  LOG(GNUNET_ERROR_TYPE_DEBUG, "Gaussian model plugin is running.\n");
   return api;
 }
 
index 5c023d1308fa4fe5bd111a5c25fd913a1d6862ea..92b49100a2dcfd064c76a601dd54a069ac7486ce 100644 (file)
@@ -6,4 +6,8 @@ HOME = $SERVICEHOME
 @UNIXONLY@ PORT = 2087
 
 [sensor-analysis]
-model = gaussian
\ No newline at end of file
+model = gaussian
+
+[sensor-model-gaussian]
+TRAINING_WINDOW = 1000
+CONFIDENCE_INTERVAL = 3
\ No newline at end of file