dRonin  adbada4
dRonin firmware
 All Data Structures Namespaces Files Functions Variables Typedefs Enumerations Enumerator Macros Groups Pages
vibrationanalysis.c
Go to the documentation of this file.
1 
15 /*
16  * This program is free software; you can redistribute it and/or modify
17  * it under the terms of the GNU General Public License as published by
18  * the Free Software Foundation; either version 3 of the License, or
19  * (at your option) any later version.
20  *
21  * This program is distributed in the hope that it will be useful, but
22  * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
23  * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
24  * for more details.
25  *
26  * You should have received a copy of the GNU General Public License along
27  * with this program; if not, see <http://www.gnu.org/licenses/>
28  */
29 
39 #include "openpilot.h"
40 #include "physical_constants.h"
41 #include "pios_thread.h"
42 #include "pios_queue.h"
43 
44 #include "accels.h"
45 #include "modulesettings.h"
46 #include "vibrationanalysisoutput.h"
47 #include "vibrationanalysissettings.h"
48 
49 
50 // Private constants
51 
52 #define MAX_QUEUE_SIZE 2
53 
54 #define STACK_SIZE_BYTES (200 + 448 + 16 + (2*3*window_size)*0) // The memory requirement grows linearly
55  // with window size. The constant is multiplied
56  // by 0 in order to reflect the fact that the
57  // malloc'ed memory is not taken from the module
58  // but instead from the heap. Nonetheless, we
59  // can know a priori how much RAM this module
60  // will take.
61 #define TASK_PRIORITY PIOS_THREAD_PRIO_LOW
62 #define SETTINGS_THROTTLING_MS 100
63 
64 #define MAX_ACCEL_RANGE 16 // Maximum accelerometer resolution in [g]
65 #define FLOAT_TO_FIXED (32768/(MAX_ACCEL_RANGE*2)-1) // This is the scaling constant that scales input floats
66 #define VIBRATION_ELEMENTS_COUNT 16 // Number of elements per object Instance
67 
68 #define MAX_WINDOW_SIZE 1024
69 
70 // Comment for larger smaller buffers and much better accuracy. The maximum window size will be allocated.
71 #define USE_SINGLE_INSTANCE_BUFFERS 1
72 
73 // Uncomment to enable freeing buffer memory if the PIOS_free method does something useful
74 // #define PIOS_FREE_IMPLEMENTED 1
75 
76 // Private variables
77 static struct pios_thread *taskHandle;
78 static TaskInfoRunningElem task;
79 static struct pios_queue *queue;
80 static bool module_enabled = false;
81 
82 static struct VibrationAnalysis_data {
83  uint16_t accels_sum_count;
84  uint16_t window_size;
85  uint16_t buffers_size;
86  uint16_t instances;
87 
91 
92  float accels_static_bias_x; // In all likelyhood, the initial values will be close to
93  float accels_static_bias_y; // (0,0,-g). In the case where they are not, this will still
94  float accels_static_bias_z; // converge to the true bias in a few thousand measurements.
95 
96  int16_t *accel_buffer_x;
97  int16_t *accel_buffer_y;
98  int16_t *accel_buffer_z;
99 } *vtd;
100 
101 
102 // Private functions
103 static void VibrationAnalysisTask(void *parameters);
104 
105 /*
106 * Releases any memory dinamically allocated
107 * Because this module can have a big footprint this will ensure we can
108 * reduce it when something fails.
109 */
110 static void VibrationAnalysisCleanup(void) {
111  // Cleanup allocated memory
112  module_enabled = false;
113 
114  // Stop main task
115  if (taskHandle != NULL) {
118  taskHandle = NULL;
119  }
120 
121 #ifdef PIOS_FREE_IMPLEMENTED
122  // Cleanup
123  if (vtd != NULL) {
124  PIOS_free(vtd);
125  vtd = NULL;
126  }
127 
128  if (vtd->accel_buffer_x != NULL){
130  vtd->accel_buffer_x = NULL;
131  }
132 
133  if (vtd->accel_buffer_y != NULL) {
135  vtd->accel_buffer_y = NULL;
136 
137  }
138 
139  if (vtd->accel_buffer_z != NULL) {
141  vtd->accel_buffer_z = NULL;
142  }
143 #endif
144 
145 }
146 
150 static int32_t VibrationAnalysisStart(void)
151 {
152 
153  if (!module_enabled)
154  return -1;
155 
156  //Get the window size
157  uint16_t window_size; // Make a local copy in order to check settings before allocating memory
158  uint16_t instances = 1;
159 
160  // Allocate and initialize the static data storage only if module is enabled the first time
161  if (vtd == NULL){
162  vtd = (struct VibrationAnalysis_data *) PIOS_malloc(sizeof(struct VibrationAnalysis_data));
163  if (vtd == NULL) {
164  module_enabled = false;
165  return -1;
166  }
167 
168  // make sure that all struct values are zeroed...
169  memset(vtd, 0, sizeof(struct VibrationAnalysis_data));
170  //... except for Z axis static bias
171  vtd->accels_static_bias_z -= GRAVITY; // [See note in definition of VibrationAnalysis_data structure]
172  }
173 
174  VibrationAnalysisSettingsFFTWindowSizeOptions window_size_enum;
175  VibrationAnalysisSettingsFFTWindowSizeGet(&window_size_enum);
176  switch (window_size_enum) {
177  case VIBRATIONANALYSISSETTINGS_FFTWINDOWSIZE_16:
178  window_size = 16;
179  break;
180  case VIBRATIONANALYSISSETTINGS_FFTWINDOWSIZE_64:
181  window_size = 64;
182  break;
183  case VIBRATIONANALYSISSETTINGS_FFTWINDOWSIZE_256:
184  window_size = 256;
185  break;
186  case VIBRATIONANALYSISSETTINGS_FFTWINDOWSIZE_1024:
187  window_size = 1024;
188  break;
189  default:
190  //This represents a serious configuration error. Do not start module.
191  module_enabled = false;
192  return -1;
193  break;
194  }
195 
196  // Is the new window size different?
197  // Will happen upon initialization and when the window size changes
198  if (window_size != vtd->window_size) {
199 
200  instances = window_size / VIBRATION_ELEMENTS_COUNT;
201 
202 #ifndef USE_SINGLE_INSTANCE_BUFFERS
203  // Check number of existing instances
204  uint16_t existing_instances = VibrationAnalysisOutputGetNumInstances();
205  if(existing_instances < instances) {
206  //Create missing instances
207  for (int i = existing_instances; i < instances; i++) {
208  uint16_t ret = VibrationAnalysisOutputCreateInstance();
209  if (ret == 0) {
210  // This fails when it's a metaobject. Not a very helpful test.
211  module_enabled = false;
212  return -1;
213  }
214  }
215  }
216 
217  // We need at least these instances
218  if (VibrationAnalysisOutputGetNumInstances() < instances) {
219  return -1;
220  }
221 #endif
222 
223 #ifdef PIOS_FREE_IMPLEMENTED
224  // Delete existing buffers
225  if (vtd->accel_buffer_x != NULL)
227  if (vtd->accel_buffer_y != NULL)
229  if (vtd->accel_buffer_z != NULL)
231 #endif
232 
233  // Clear buffers
234  memset(vtd, 0, sizeof(struct VibrationAnalysis_data));
235  vtd->accels_static_bias_z -= GRAVITY; // [See note in definition of VibrationAnalysis_data structure]
236 
237  // Now place the window size into the buffer
240 
241 #ifdef USE_SINGLE_INSTANCE_BUFFERS
243 #else
244 
245  #ifdef PIOS_FREE_IMPLEMENTED
247  #else
249 
250  #endif
251 
252 #endif
253 
254 
255  //Create new buffers if needed.
256  if (vtd->accel_buffer_x == NULL) {
257  vtd->accel_buffer_x = (int16_t *) PIOS_malloc(vtd->buffers_size*sizeof(typeof(*vtd->accel_buffer_x)));
258  if (vtd->accel_buffer_x == NULL) {
260 
261  module_enabled = false;
262  return -1;
263  }
264  }
265 
266  if (vtd->accel_buffer_y == NULL) {
267  vtd->accel_buffer_y = (int16_t *) PIOS_malloc(vtd->buffers_size*sizeof(typeof(*vtd->accel_buffer_y)));
268  if (vtd->accel_buffer_y == NULL) {
270 
271  module_enabled = false;
272  return -1;
273  }
274  }
275 
276  if (vtd->accel_buffer_z == NULL) {
277  vtd->accel_buffer_z = (int16_t *) PIOS_malloc(vtd->buffers_size*sizeof(typeof(*vtd->accel_buffer_z)));
278  if (vtd->accel_buffer_z == NULL) {
280 
281  module_enabled = false;
282  return -1;
283  }
284  }
285  }
286 
287  // Start main task
288  if (taskHandle == NULL) {
290  task = TaskMonitorAdd(TASKINFO_RUNNING_VIBRATIONANALYSIS, taskHandle);
291  }
292 
293  return 0;
294 }
295 
296 
300 static int32_t VibrationAnalysisInitialize(void)
301 {
302  if (ModuleSettingsInitialize() == -1) {
303  module_enabled = false;
304  return -1;
305  }
306 
307 #ifdef MODULE_VibrationAnalysis_BUILTIN
308  module_enabled = true;
309 #else
310  uint8_t module_state[MODULESETTINGS_ADMINSTATE_NUMELEM];
311  ModuleSettingsAdminStateGet(module_state);
312  if (module_state[MODULESETTINGS_ADMINSTATE_VIBRATIONANALYSIS] == MODULESETTINGS_ADMINSTATE_ENABLED) {
313  module_enabled = true;
314  } else {
315  module_enabled = false;
316  }
317 #endif
318 
319  if (!module_enabled) //If module not enabled...
320  return -1;
321 
322  // Initialize UAVOs
323  if (VibrationAnalysisSettingsInitialize() == -1 || VibrationAnalysisOutputInitialize() == -1) {
324  module_enabled = false;
325  return -1;
326  }
327 
328  // Create object queue
329  queue = PIOS_Queue_Create(MAX_QUEUE_SIZE, sizeof(UAVObjEvent));
330 
331  return 0;
332 
333 }
335 
336 
337 static void VibrationAnalysisTask(void *parameters)
338 {
339  uint32_t lastSysTime;
340  uint32_t lastSettingsUpdateTime;
341  uint8_t runAnalysisFlag = VIBRATIONANALYSISSETTINGS_TESTINGSTATUS_OFF; // By default, turn analysis off
342  uint16_t sampleRate_ms = 100; // Default sample rate of 100ms
343  uint16_t sample_count;
344 
345  UAVObjEvent ev;
346 
347  // Listen for updates.
348  AccelsConnectQueue(queue);
349 
350  // Main task loop
351  VibrationAnalysisOutputData vibrationAnalysisOutputData;
352  sample_count = 0;
353  lastSysTime = PIOS_Thread_Systime();
354  lastSettingsUpdateTime = PIOS_Thread_Systime() - SETTINGS_THROTTLING_MS;
355 
356  vibrationAnalysisOutputData.samples = vtd->window_size;
357  vibrationAnalysisOutputData.scale = FLOAT_TO_FIXED;
358 
359 #ifdef USE_SINGLE_INSTANCE_BUFFERS
360  uint16_t instance_number = 0;
361 #endif
362 
363  uint8_t runningAcquisition = 0;
364 
365  // Main module task, never exit from while loop
366  while (module_enabled)
367  {
368 
369  // Only check settings once every 100ms and not in the middle of a buffer accumulation to reduce artifacts
370  if (PIOS_Thread_Systime() - lastSettingsUpdateTime > SETTINGS_THROTTLING_MS && runningAcquisition == 0) {
371 
372  //First check if the analysis is active
373  VibrationAnalysisSettingsTestingStatusGet(&runAnalysisFlag);
374 
375  // If analysis is turned off, delay and then loop.
376  if (runAnalysisFlag == VIBRATIONANALYSISSETTINGS_TESTINGSTATUS_OFF) {
377  PIOS_Thread_Sleep(200);
378  continue;
379  }
380  // Get sample rate
381  VibrationAnalysisSettingsSampleRateGet(&sampleRate_ms);
382  sampleRate_ms = sampleRate_ms > 0 ? sampleRate_ms : 1; //Ensure sampleRate never is 0.
383 
384  //Reconfigure any parameter
386 
387  vibrationAnalysisOutputData.samples = vtd->window_size;
388 
389  lastSettingsUpdateTime = PIOS_Thread_Systime();
390 
391  runningAcquisition = 1;
392  }
393 
394 
395  // Wait until the Accels object is updated, and never time out
396  if (PIOS_Queue_Receive(queue, &ev, PIOS_QUEUE_TIMEOUT_MAX) == true) {
403  AccelsData accels_data;
404  AccelsGet(&accels_data);
405 
406  vtd->accels_data_sum_x += accels_data.x;
407  vtd->accels_data_sum_y += accels_data.y;
408  vtd->accels_data_sum_z += accels_data.z;
409 
411  }
412 
413  // If not enough time has passed, keep accumulating data
414  if (PIOS_Thread_Systime() - lastSysTime < sampleRate_ms) {
415  continue;
416  }
417 
418 
419  lastSysTime = PIOS_Thread_Systime();
420 
421  //Calculate averaged values
422  float accels_avg_x = vtd->accels_data_sum_x / vtd->accels_sum_count;
423  float accels_avg_y = vtd->accels_data_sum_y / vtd->accels_sum_count;
424  float accels_avg_z = vtd->accels_data_sum_z / vtd->accels_sum_count;
425 
426  //Calculate DC bias
427  float alpha = .005; //Hard-coded to drift very slowly
428  vtd->accels_static_bias_x = alpha*accels_avg_x + (1-alpha)*vtd->accels_static_bias_x;
429  vtd->accels_static_bias_y = alpha*accels_avg_y + (1-alpha)*vtd->accels_static_bias_y;
430  vtd->accels_static_bias_z = alpha*accels_avg_z + (1-alpha)*vtd->accels_static_bias_z;
431 
432  // Add averaged values to the buffer, and remove DC bias.
433  vtd->accel_buffer_x[sample_count] = (accels_avg_x - vtd->accels_static_bias_x)*FLOAT_TO_FIXED;
434  vtd->accel_buffer_y[sample_count] = (accels_avg_y - vtd->accels_static_bias_y)*FLOAT_TO_FIXED;
435  vtd->accel_buffer_z[sample_count] = (accels_avg_z - vtd->accels_static_bias_z)*FLOAT_TO_FIXED;
436 
437  //Reset the accumulators
438  vtd->accels_data_sum_x = 0;
439  vtd->accels_data_sum_y = 0;
440  vtd->accels_data_sum_z = 0;
441  vtd->accels_sum_count = 0;
442 
443  // Advance sample and reset when at buffer end
444  sample_count++;
445 
446  // Process and dump an instance at a time
447 #ifdef USE_SINGLE_INSTANCE_BUFFERS
448  if (sample_count == vtd->buffers_size) {
449  // Dump an instance
450 
451  for (uint16_t k = 0; k < VIBRATION_ELEMENTS_COUNT; k++) {
452  vibrationAnalysisOutputData.index = instance_number;
453  vibrationAnalysisOutputData.x[k] = vtd->accel_buffer_x[k];
454  vibrationAnalysisOutputData.y[k] = vtd->accel_buffer_y[k];
455  vibrationAnalysisOutputData.z[k] = vtd->accel_buffer_z[k];
456  }
457 
458  VibrationAnalysisOutputInstSet(0, &vibrationAnalysisOutputData);
459  VibrationAnalysisOutputInstUpdated(0);
460 
461  // Increase the instance number or reset it
462  instance_number = (instance_number + 1) % vtd->instances;
463 #else
464  // Or process and dump the full window using multiple instances
465  // This will probably introduce less artifacts at the cost of higher memory consumption
466  if (sample_count == vtd->window_size) {
467  for (uint16_t i = 0; i < vtd->instances; i++) {
468  for (uint16_t k = 0; k < VIBRATION_ELEMENTS_COUNT; k++) {
469  vibrationAnalysisOutputData.index = i;
470  vibrationAnalysisOutputData.x[k] = vtd->accel_buffer_x[k + VIBRATION_ELEMENTS_COUNT * i];
471  vibrationAnalysisOutputData.y[k] = vtd->accel_buffer_y[k + VIBRATION_ELEMENTS_COUNT * i];
472  vibrationAnalysisOutputData.z[k] = vtd->accel_buffer_z[k + VIBRATION_ELEMENTS_COUNT * i];
473  }
474 
475  VibrationAnalysisOutputInstSet(i, &vibrationAnalysisOutputData);
476  VibrationAnalysisOutputInstUpdated(i);
477  }
478 
479 #endif
480  // Erase buffer
481  memset(vtd->accel_buffer_x, 0, vtd->buffers_size*sizeof(typeof(*(vtd->accel_buffer_x))));
482  memset(vtd->accel_buffer_y, 0, vtd->buffers_size*sizeof(typeof(*(vtd->accel_buffer_y))));
483  memset(vtd->accel_buffer_z, 0, vtd->buffers_size*sizeof(typeof(*(vtd->accel_buffer_z))));
484 
485  sample_count = 0;
486  runningAcquisition = 0;
487  }
488  }
489 }
490 
uint32_t PIOS_Thread_Systime(void)
Definition: pios_thread.c:212
struct pios_queue * PIOS_Queue_Create(size_t queue_length, size_t item_size)
Definition: pios_queue.c:47
void * PIOS_malloc(size_t size)
Definition: pios_heap.c:125
static int32_t VibrationAnalysisInitialize(void)
bool PIOS_Queue_Receive(struct pios_queue *queuep, void *itemp, uint32_t timeout_ms)
Definition: pios_queue.c:213
static struct pios_thread * taskHandle
#define STACK_SIZE_BYTES
int32_t TaskMonitorRemove(TaskInfoRunningElem task)
Definition: taskmonitor.c:86
static struct VibrationAnalysis_data * vtd
#define PIOS_QUEUE_TIMEOUT_MAX
Definition: pios_queue.h:30
#define MODULE_INITCALL(ifn, sfn)
Definition: pios_initcall.h:67
#define MAX_QUEUE_SIZE
#define TASK_PRIORITY
struct pios_thread * PIOS_Thread_Create(void(*fp)(void *), const char *namep, size_t stack_bytes, void *argp, enum pios_thread_prio_e prio)
Definition: pios_thread.c:89
#define FLOAT_TO_FIXED
static TaskInfoRunningElem task
int32_t TaskMonitorAdd(TaskInfoRunningElem task, struct pios_thread *threadp)
Definition: taskmonitor.c:67
uint8_t i
Definition: msp_messages.h:97
static void VibrationAnalysisTask(void *parameters)
#define SETTINGS_THROTTLING_MS
static int32_t VibrationAnalysisStart(void)
void PIOS_free(void *buf)
Definition: pios_heap.c:174
void PIOS_Thread_Sleep(uint32_t time_ms)
Definition: pios_thread.c:229
Includes PiOS and core architecture components.
static struct pios_queue * queue
static uint32_t lastSysTime
void PIOS_Thread_Delete(struct pios_thread *threadp)
Definition: pios_thread.c:132
if(BaroAltitudeHandle()!=NULL)
#define MAX_WINDOW_SIZE
static void VibrationAnalysisCleanup(void)
static bool module_enabled
#define VIBRATION_ELEMENTS_COUNT