summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorMarcin Masternak <m.masternak@samsung.com>2017-09-21 13:13:26 +0200
committerkj7.sung <kj7.sung@samsung.com>2017-09-26 14:34:01 +0900
commit2a6e6e693b985e998e71431a13707b9819d76d56 (patch)
treeaebb3dfd9797f453b0cfbe66acb946c8e93bc580
parent87437d98567cb4dc0b15605f4cf63bedfeb553bb (diff)
downloadlbs-server-2a6e6e693b985e998e71431a13707b9819d76d56.tar.gz
lbs-server-2a6e6e693b985e998e71431a13707b9819d76d56.tar.bz2
lbs-server-2a6e6e693b985e998e71431a13707b9819d76d56.zip
sync : Fused pedestrian mode
- Fusion Engine internal changes - speed from internal pedometer sensor. Change-Id: Iccc3087ef2e60e0a241cb229c92c4be7a0f3d8bc Signed-off-by: Marcin Masternak <m.masternak@samsung.com>
-rw-r--r--CMakeLists.txt2
-rw-r--r--lbs-server/include/gps_plugin_data_types.h1
-rw-r--r--lbs-server/src/fused.c441
-rw-r--r--lbs-server/src/fused.h179
-rw-r--r--lbs-server/src/fused/AccelerometerFilter.c105
-rw-r--r--lbs-server/src/fused/AccelerometerFilter.h81
-rw-r--r--lbs-server/src/fused/Butterworth3dFilter.c104
-rw-r--r--lbs-server/src/fused/Butterworth3dFilter.h80
-rw-r--r--lbs-server/src/fused/CalibrationFilter.c165
-rw-r--r--lbs-server/src/fused/CalibrationFilter.h50
-rw-r--r--lbs-server/src/fused/Conversions.c (renamed from lbs-server/src/fused/conversions.c)34
-rw-r--r--lbs-server/src/fused/Conversions.h73
-rw-r--r--lbs-server/src/fused/FrequencyEstimator.c60
-rw-r--r--lbs-server/src/fused/FrequencyEstimator.h59
-rw-r--r--lbs-server/src/fused/FusionEngine.c353
-rw-r--r--lbs-server/src/fused/FusionEngine.h252
-rw-r--r--lbs-server/src/fused/GravityEstimator.h73
-rw-r--r--lbs-server/src/fused/GyroscopeFilter.c56
-rw-r--r--lbs-server/src/fused/GyroscopeFilter.h61
-rw-r--r--lbs-server/src/fused/LocationFilter.c479
-rw-r--r--lbs-server/src/fused/LocationFilter.h135
-rw-r--r--lbs-server/src/fused/Matrix.c156
-rw-r--r--lbs-server/src/fused/Matrix.h169
-rw-r--r--lbs-server/src/fused/MotionDetector.c108
-rw-r--r--lbs-server/src/fused/MotionDetector.h98
-rw-r--r--lbs-server/src/fused/MovingAverage.c56
-rw-r--r--lbs-server/src/fused/MovingAverage.h75
-rw-r--r--lbs-server/src/fused/MovingAverageFilters.c127
-rw-r--r--lbs-server/src/fused/MovingAverageFilters.h156
-rw-r--r--lbs-server/src/fused/Orientation.c388
-rw-r--r--lbs-server/src/fused/Orientation.h177
-rw-r--r--lbs-server/src/fused/PeaceDetector.c57
-rw-r--r--lbs-server/src/fused/PeaceDetector.h38
-rw-r--r--lbs-server/src/fused/TangentFrame.c125
-rw-r--r--lbs-server/src/fused/TangentFrame.h154
-rw-r--r--lbs-server/src/fused/TimeOffset.c87
-rw-r--r--lbs-server/src/fused/TimeOffset.h65
-rw-r--r--lbs-server/src/fused/Vector.c48
-rw-r--r--lbs-server/src/fused/Vector.h201
-rw-r--r--lbs-server/src/fused/Waema3dEstimator.c59
-rw-r--r--lbs-server/src/fused/Waema3dEstimator.h56
-rw-r--r--lbs-server/src/fused/accelerometer-filter.c146
-rw-r--r--lbs-server/src/fused/accelerometer-filter.h125
-rw-r--r--lbs-server/src/fused/aema-estimator.c170
-rw-r--r--lbs-server/src/fused/aema-estimator.h312
-rw-r--r--lbs-server/src/fused/conversions.h204
-rw-r--r--lbs-server/src/fused/earth.h45
-rw-r--r--lbs-server/src/fused/gravity-estimator.c43
-rw-r--r--lbs-server/src/fused/gravity-estimator.h113
-rw-r--r--lbs-server/src/fused/gyroscope-filter.c105
-rw-r--r--lbs-server/src/fused/gyroscope-filter.h109
-rw-r--r--lbs-server/src/fused/kalman-filter.c1126
-rw-r--r--lbs-server/src/fused/kalman-filter.h619
-rw-r--r--lbs-server/src/fused/lp-3d-filter.c126
-rw-r--r--lbs-server/src/fused/lp-3d-filter.h126
-rw-r--r--lbs-server/src/fused/math-ext.c50
-rw-r--r--lbs-server/src/fused/math-ext.h170
-rw-r--r--lbs-server/src/fused/motion-detector.c109
-rw-r--r--lbs-server/src/fused/motion-detector.h133
-rw-r--r--lbs-server/src/fused/parameters.h18
-rw-r--r--lbs-server/src/fused/types.h210
-rw-r--r--lbs-server/src/lbs_server.c402
-rw-r--r--module/gps_module.c32
-rw-r--r--packaging/lbs-server.spec3
64 files changed, 5258 insertions, 4481 deletions
diff --git a/CMakeLists.txt b/CMakeLists.txt
index e42a4a3..8323827 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -8,7 +8,7 @@ SET(BIN_DIR "${PREFIX}/bin")
#Dependencies
SET(common_dp "glib-2.0 lbs-dbus dlog gio-2.0 lbs-location")
-SET(server_dp "${common_dp} network tapi vconf vconf-internal-keys gthread-2.0 gio-unix-2.0 capi-network-wifi")
+SET(server_dp "${common_dp} network tapi vconf vconf-internal-keys gthread-2.0 gio-unix-2.0 capi-network-wifi capi-system-info")
SET(module_dp "${common_dp} gmodule-2.0")
# Set required packages
diff --git a/lbs-server/include/gps_plugin_data_types.h b/lbs-server/include/gps_plugin_data_types.h
index db4f10c..635127a 100644
--- a/lbs-server/include/gps_plugin_data_types.h
+++ b/lbs-server/include/gps_plugin_data_types.h
@@ -90,6 +90,7 @@ typedef struct {
*/
typedef struct {
int num_of_location; /**< Number of batch data */
+ pos_data_t *data;
} batch_data_t;
/**
diff --git a/lbs-server/src/fused.c b/lbs-server/src/fused.c
index b6c2f6c..a241e40 100644
--- a/lbs-server/src/fused.c
+++ b/lbs-server/src/fused.c
@@ -19,190 +19,197 @@
#define __LOCATION_FUSED_C__
#define _USE_MATH_DEFINES
+#include <time.h>
#include <math.h>
#include <stdlib.h>
#include <sensor.h>
-#include "fused/types.h"
-#include "fused/conversions.h"
-#include "fused/kalman-filter.h"
+#include <vconf.h>
+#include "fused/FusionEngine.h"
#include "fused.h"
#include "debug_util.h"
#define PTR2ENUM(ptr, type) ((type)(int)((char*)(ptr) - ((char*)0)))
#define ENUM2PTR(value) (((char*)0) + (int)(value))
-/** Container of the sensor reference data */
+#define PEDOMETER_URI "http://samsung.com/sensor/healthinfo/pedometer/samsung_pedometer"
+
+/** Container of the standard sensor reference data */
typedef struct {
+ sensor_type_e type;
sensor_h handle;
sensor_listener_h listener;
+ gboolean is_available;
+ gboolean is_started;
} _fl_sensor;
+/** Labels for a device coordinate system (right-handed) */
+typedef enum {
+ DEV_SPATIAL_X = Y,
+ DEV_SPATIAL_Y = X,
+ DEV_SPATIAL_Z = Z,
+ DEV_SPATIAL_DIMENSION = GEO_DIMENSION
+} fl_dev_spatial;
+
+
/** Combined status flags and bit-field */
typedef union {
struct {
- unsigned started: 1;
- unsigned paused: 1;
+ unsigned started:1;
+ unsigned paused:1;
};
} _fl_status;
/** Fused location data */
typedef struct {
- _fl_status status;
- gboolean enabled;
- gboolean is_active_sensor[SENSOR_SOURCE_NUM];
- fl_location last_location;
- _fl_sensor sensor[SENSOR_SOURCE_NUM];
- fl_sensory_flags attached_sensors;
- fl_sensory_flags started_sensors;
- gpointer lbs_server_handle;
- fused_updated_cb fused_pos_cb;
+ _fl_status status;
+ WgsLocation last_location;
+ _fl_sensor sensors[SENSOR_SOURCE_COUNT];
+ gpointer lbs_server_handle;
+ fused_updated_cb fused_pos_cb;
+ FusionEngine fusion_engine;
+ double last_gps_speed;
+ time_t last_gps_timestamp;
+ time_t last_wps_timestamp;
+ double utc_offset_sec;
} LocationFusedData;
static LocationFusedData fused;
-static fl_sensory_flags sensor_func_detect()
+static void sensors_create()
{
LOG_FUSED_FUNC;
- fl_sensory_flags active_sensors = NO_SENSOR_FLAG;
-
- if (sensor_get_default_sensor(SENSOR_ACCELEROMETER, &fused.sensor[ACCELEROMETER].handle) == SENSOR_ERROR_NONE)
- active_sensors |= ACCELEROMETER_FLAG;
-
- if (sensor_get_default_sensor(SENSOR_GYROSCOPE, &fused.sensor[GYROSCOPE].handle) == SENSOR_ERROR_NONE)
- active_sensors |= GYROSCOPE_FLAG;
+ sensor_error_e ret;
+ unsigned int i;
+ for (i = 0; i < SENSOR_SOURCE_COUNT; i++) {
+ if (i == PEDOMETER)
+ ret = sensor_get_default_sensor_by_uri(PEDOMETER_URI, &fused.sensors[i].handle);
+ else
+ ret = sensor_get_default_sensor(fused.sensors[i].type, &fused.sensors[i].handle);
+
+ if (ret != SENSOR_ERROR_NONE) {
+ LOG_FUSED(LOG_ERROR, "sensor_get_default_sensor() FAILED, [%d]", i);
+ fused.sensors[i].is_available = FALSE;
+ }
- return active_sensors;
+ ret = sensor_create_listener(fused.sensors[i].handle, &fused.sensors[i].listener);
+ if (ret == SENSOR_ERROR_NONE) {
+ ret = sensor_listener_set_event_cb(fused.sensors[i].listener, FL_SENSOR_SAMPLING_INTERVAL, on_sensor_event, ENUM2PTR(i));
+ if (ret == SENSOR_ERROR_NONE) {
+ sensor_listener_set_option(fused.sensors[i].listener, SENSOR_OPTION_ALWAYS_ON);
+ fused.sensors[i].is_available = TRUE;
+ }
+ }
+ }
}
-static void sensor_func_create()
+static void sensors_clear()
{
LOG_FUSED_FUNC;
- fl_sensory_flags active_sensors;
- unsigned i;
-
- fused.attached_sensors = NO_SENSOR_FLAG;
- for (i = 0, active_sensors = sensor_func_detect(); active_sensors != NO_SENSOR_FLAG; active_sensors >>= 1) {
- if (active_sensors & 1) {
- sensor_error_e ret = sensor_create_listener(fused.sensor[i].handle, &fused.sensor[i].listener);
- if (ret == SENSOR_ERROR_NONE) {
- ret = sensor_listener_set_event_cb(fused.sensor[i].listener, FL_SENSOR_SAMPLING_INTERVAL, on_sensor_event, ENUM2PTR(i));
- if (ret == SENSOR_ERROR_NONE) {
- sensor_listener_set_option(fused.sensor[i].listener, SENSOR_OPTION_ALWAYS_ON);
- fused.attached_sensors |= (1 << i);
- i++;
- continue;
- }
- /* undo when sensor api failed*/
-// sensor_destroy_listener(fused.sensor[i].listener);
-// fused.sensor[i].listener = NULL;
- }
- /* undo */
- while (i) {
- --i;
- if (fused.sensor[i].listener) {
- sensor_listener_unset_event_cb(fused.sensor[i].listener);
- sensor_destroy_listener(fused.sensor[i].listener);
- fused.sensor[i].listener = NULL;
- }
- }
- fused.attached_sensors = NO_SENSOR_FLAG;
- LOG_FUSED(LOG_ERROR, "no sensors");
- return;
- }
+ unsigned int i;
+ for (i = 0; i < SENSOR_SOURCE_COUNT; i++) {
+ fused.sensors[i].handle = NULL;
+ fused.sensors[i].listener = NULL;
+ fused.sensors[i].is_available = FALSE;
+ fused.sensors[i].is_started = FALSE;
}
- LOG_FUSED(LOG_DEBUG, "OK, attached_sensors=0x%X", fused.attached_sensors);
}
-static void sensor_func_destroy()
+static void sensors_reset_listeners()
{
- LOG_FUNC;
- fl_sensory_flags f;
- unsigned i;
-
- LOG_FUSED(LOG_DEBUG, "attached_sensors=0x%X", fused.attached_sensors);
- for (i = SENSOR_SOURCE_NUM, f = fused.attached_sensors; (f & SUPPORTED_SENSORS_MASK) != NO_SENSOR_FLAG && i;) {
- --i;
- f <<= 1;
- if (f & (1 << SENSOR_SOURCE_NUM)) {
- sensor_listener_unset_event_cb(fused.sensor[i].listener);
- sensor_destroy_listener(fused.sensor[i].listener);
- fused.sensor[i].handle = NULL;
- fused.sensor[i].listener = NULL;
+ LOG_FUSED_FUNC;
+ unsigned int i;
+ for (i = 0; i < SENSOR_SOURCE_COUNT; i++) {
+ if (fused.sensors[i].listener) {
+ sensor_listener_unset_event_cb(fused.sensors[i].listener);
+ sensor_destroy_listener(fused.sensors[i].listener);
+ fused.sensors[i].listener = NULL;
}
}
- fused.attached_sensors = NO_SENSOR_FLAG;
}
-static LocationError sensor_func_start(fl_sensory_flags sensors)
+static void sensors_destroy()
{
LOG_FUSED_FUNC;
- if ((sensors & ~fused.attached_sensors) == NO_SENSOR_FLAG) {
- fl_sensory_flags flag;
- unsigned i;
- sensor_error_e ret;
-
- sensors &= ~fused.started_sensors;
- for (i = 0, flag = sensors; flag != NO_SENSOR_FLAG; flag >>= 1) {
- if (flag & 1) {
- ret = sensor_listener_start(fused.sensor[i].listener);
- if (ret == SENSOR_ERROR_NONE) {
- i++;
- continue;
- } else {
- /* undo */
- while (i) {
- --i;
- if (sensors & (1 << i)) // fused.sensor[i].listener)
- sensor_listener_stop(fused.sensor[i].listener);
- }
- LOG_FUSED(LOG_ERROR, "UNKNOWN, sensor_listener_start(): %d", ret);
- return LOCATION_ERROR_CONFIGURATION;
- }
- }
+ sensors_reset_listeners();
+ sensors_clear();
+}
+
+static void sensor_start(fl_sensor_source sensor)
+{
+ LOG_FUSED_FUNC;
+ if (fused.sensors[sensor].handle
+ && fused.sensors[sensor].listener
+ && fused.sensors[sensor].is_available
+ && !fused.sensors[sensor].is_started) {
+ sensor_error_e ret = sensor_listener_start(fused.sensors[sensor].listener);
+ if (ret == SENSOR_ERROR_NONE) {
+ fused.sensors[sensor].is_started = TRUE;
}
- fused.started_sensors |= sensors;
- LOG_FUSED(LOG_DEBUG, "(0x%X): OK", sensors);
- return LOCATION_ERROR_NONE;
- } else {
- LOG_FUSED(LOG_ERROR, "(0x%X): NOT_SUPPORTED", sensors);
- return LOCATION_ERROR_NOT_SUPPORTED;
}
}
-static void sensor_func_stop(fl_sensory_flags sensors)
+static void sensor_stop(fl_sensor_source sensor)
{
LOG_FUSED_FUNC;
- fl_sensory_flags f;
- unsigned s;
-
- LOG_FUSED(LOG_DEBUG, "(0x%X)", sensors);
- sensors &= fused.started_sensors;
- for (s = SENSOR_SOURCE_NUM, f = sensors; (f & SUPPORTED_SENSORS_MASK) != NO_SENSOR_FLAG && s;) {
- --s;
- f <<= 1;
- if (f & (1 << SENSOR_SOURCE_NUM))
- sensor_listener_stop(fused.sensor[s].listener);
+ if (fused.sensors[sensor].handle
+ && fused.sensors[sensor].listener
+ && fused.sensors[sensor].is_available
+ && fused.sensors[sensor].is_started) {
+ sensor_error_e ret = sensor_listener_stop(fused.sensors[sensor].listener);
+ if (ret == SENSOR_ERROR_NONE) {
+ fused.sensors[sensor].is_started = FALSE;
+ }
}
- fused.started_sensors &= ~sensors;
+}
+
+unsigned long long get_current_boot_time()
+{
+ struct timespec time;
+#ifdef CLOCK_BOOTTIME
+ clock_gettime(CLOCK_BOOTTIME, &time);
+#else
+ clock_gettime(CLOCK_MONOTONIC_RAW, &time);
+#endif
+ return (unsigned long long) time.tv_sec * 1000000000 + time.tv_nsec;
}
void location_fused_start()
{
LOG_FUSED_FUNC;
- fl_sensory_flags sensors = GYROSCOPE_FLAG | ACCELEROMETER_FLAG;
- int ret = sensor_func_start(sensors);
- LBS_FUSED_CHECK(ret, "sensor_func_start");
- fused_engine_start();
+ char *calibration = vconf_get_str(VCONFKEY_LOCATION_GYRO_DRIFT);
+ LOG_FUSED(LOG_DEBUG, "calibration: vconf_get_str(VCONFKEY_LOCATION_GYRO_DRIFT) %s = \"%s\"",
+ calibration != NULL ? "SUCCESS" : "FAILED",
+ calibration != NULL ? calibration : "EMPTY");
+ fusion_engine_set_calibration(&fused.fusion_engine, calibration);
+
fused.status.started = TRUE;
+ fusion_engine_start(&fused.fusion_engine);
+ unsigned int i;
+ for (i = 0; i < SENSOR_SOURCE_COUNT; i++) {
+ sensor_start(i);
+ }
}
void location_fused_stop()
{
LOG_FUSED_FUNC;
- fused_engine_stop();
+ unsigned int i;
+ for (i = 0; i < SENSOR_SOURCE_COUNT; i++) {
+ sensor_stop(i);
+ }
+ fusion_engine_stop(&fused.fusion_engine);
fused.status.started = FALSE;
- sensor_func_stop(fused.started_sensors);
+
+ char *calibration = fusion_engine_get_calibration(&fused.fusion_engine);
+ if (calibration) {
+ int ret = vconf_set_str(VCONFKEY_LOCATION_GYRO_DRIFT, calibration);
+ LOG_FUSED(LOG_DEBUG, "calibration: vconf_set_str(VCONFKEY_LOCATION_GYRO_DRIFT) %s = \"%s\"",
+ ret == VCONF_OK ? "SUCCESS" : "FAILED", calibration);
+ free(calibration);
+ } else {
+ LOG_FUSED(LOG_DEBUG, "fusion_engine_get_calibration() FAILED");
+ }
}
void location_fused_init(fused_updated_cb _fused_pos_cb, gpointer lbs_server)
@@ -212,31 +219,40 @@ void location_fused_init(fused_updated_cb _fused_pos_cb, gpointer lbs_server)
LOG_FUSED(LOG_ERROR, "location_fused_init failed !!!");
return;
}
-
- fused.attached_sensors = NO_SENSOR_FLAG;
- fused.started_sensors = NO_SENSOR_FLAG;
+ fused.sensors[ACCELEROMETER].type = SENSOR_ACCELEROMETER;
+ fused.sensors[GYROSCOPE].type = SENSOR_GYROSCOPE;
+ fused.sensors[PEDOMETER].type = SENSOR_HUMAN_PEDOMETER;
fused.fused_pos_cb = _fused_pos_cb;
fused.lbs_server_handle = lbs_server;
-
- sensor_func_create();
- fused_engine_init(on_motion_state, on_engine_position, lbs_server);
+ fused.status.started = FALSE;
+ fused.status.paused = FALSE;
+ fused.last_gps_speed = -1.0;
+ fused.last_gps_timestamp = 0;
+ fused.last_wps_timestamp = 0;
+ sensors_clear();
+ sensors_create();
+
+ time_t now = time(NULL);
+ unsigned long long boot_time_ns = get_current_boot_time();
+ fused.utc_offset_sec = (double)now - (double)boot_time_ns / 1000000000.0;
+ LOG_FUSED(LOG_DEBUG, "now = %lu, boot_time_ns = %llu, utc_offset_sec = %.10f", now, boot_time_ns, fused.utc_offset_sec);
+ fusion_engine_init(&fused.fusion_engine, on_motion_state, fused.utc_offset_sec);
}
void location_fused_deinit()
{
LOG_FUSED_FUNC;
-
- // stop sensors
- if (fused.status.started) {
- fused.status.started = FALSE;
- fused_engine_stop();
- sensor_func_stop(fused.started_sensors);
- }
- fused_engine_exit();
- sensor_func_destroy();
-
- fused.fused_pos_cb = NULL;
+ if (fused.status.started)
+ location_fused_stop();
+ sensors_destroy();
+ fusion_engine_exit(&fused.fusion_engine);
+ fused.fused_pos_cb = NULL;
fused.lbs_server_handle = NULL;
+ fused.status.started = FALSE;
+ fused.status.paused = FALSE;
+ fused.last_gps_speed = -1.0;
+ fused.last_gps_timestamp = 0;
+ fused.last_wps_timestamp = 0;
}
void send_gps_position_to_fused_engine(time_t timestamp, double latitude, double longitude, double altitude,
@@ -244,60 +260,118 @@ void send_gps_position_to_fused_engine(time_t timestamp, double latitude, double
{
// LOG_FUSED_FUNC;
- LocationPosition *pos = location_position_new(timestamp, latitude, longitude, altitude, LOCATION_STATUS_3D_FIX);
- LocationVelocity *vel = location_velocity_new(timestamp, speed, bearing, 0.0);
- LocationAccuracy *accuracy = location_accuracy_new(LOCATION_ACCURACY_LEVEL_DETAILED, hor_accuracy, ver_accuracy);
+ if (!fused.status.started)
+ return;
+
+ fused.last_gps_timestamp = timestamp;
+ fused.last_gps_speed = speed;
- static fl_uncertainty_union __u;
- __u.accuracy = *accuracy;
- __u.sigma.of_speed = __u.sigma.of_horizontal_pos * FL_DEFAULT_VELOCITY_SIGMA / FL_DEFAULT_POSITION_SIGMA;
- __u.sigma.of_climb = __u.sigma.of_altitude * FL_DEFAULT_VELOCITY_SIGMA / FL_DEFAULT_POSITION_SIGMA;
- const fl_sigma* sigma = &__u.sigma;
- fused_engine_process_position_2x3d_event(pos, vel, sigma);
+ bool is_new_position = fusion_engine_position_3d_event(&fused.fusion_engine,
+ (double)timestamp, latitude, longitude, altitude, hor_accuracy, 100.0, speed / 3.6,
+ hor_accuracy * FL_DEFAULT_VELOCITY_SIGMA / FL_DEFAULT_POSITION_SIGMA, bearing, 0.0,
+ 100.0 * FL_DEFAULT_VELOCITY_SIGMA / FL_DEFAULT_POSITION_SIGMA);
- location_position_free(pos);
- location_velocity_free(vel);
- location_accuracy_free(accuracy);
+ if (is_new_position)
+ on_engine_new_position();
}
-void send_wps_position_to_fused_engine(time_t timestamp, double latitude, double longitude, double hor_accuracy, double ver_accuracy)
+void send_wps_position_to_fused_engine(time_t timestamp, double latitude, double longitude, double hor_accuracy)
{
- LOG_FUSED_FUNC;
- LocationPosition *pos = location_position_new(timestamp, latitude, longitude, 0.0, LOCATION_STATUS_2D_FIX);
- LocationAccuracy *accuracy = location_accuracy_new(LOCATION_ACCURACY_LEVEL_DETAILED, hor_accuracy, ver_accuracy);
-
- static fl_uncertainty_union __u;
- __u.accuracy = *accuracy;
- __u.sigma.of_speed = -1;
- __u.sigma.of_climb = -1;
- const fl_sigma* sigma = &__u.sigma;
- fused_engine_process_position_2d_event(pos, sigma);
-
- location_position_free(pos);
- location_accuracy_free(accuracy);
+// LOG_FUSED_FUNC;
+
+ if (!fused.status.started)
+ return;
+
+ fused.last_wps_timestamp = timestamp;
+
+ bool is_new_position = fusion_engine_position_2d_event(&fused.fusion_engine,
+ (double)timestamp, latitude, longitude, hor_accuracy);
+
+ if (is_new_position)
+ on_engine_new_position();
}
-static void on_engine_position(const fl_position_4d* position, gpointer user_data)
+static void on_engine_new_position()
{
// LOG_FUSED_FUNC;
- fused_engine_get_location(&fused.last_location.pos, &fused.last_location.vel, &fused.last_location.sigma);
+ time_t now = time(NULL);
+ double seconds_from_last_gps = difftime(now, fused.last_gps_timestamp);
+ double seconds_from_last_wps = difftime(now, fused.last_wps_timestamp);
+ double seconds_from_last_position = fmin(seconds_from_last_gps, seconds_from_last_wps);
+ LOG_FUSED(LOG_DEBUG, "max time %.0f/%.0f[s]", seconds_from_last_position, FL_MAX_TIME_FROM_LAST_POS);
+ if (seconds_from_last_position > FL_MAX_TIME_FROM_LAST_POS) {
+ LOG_FUSED(LOG_DEBUG, "max time %.0f/%.0f[s] have passed -> skip output location",
+ seconds_from_last_position, FL_MAX_TIME_FROM_LAST_POS);
+ return;
+ }
+ fusion_engine_get_wgs_location(&fused.fusion_engine, &fused.last_location);
if (fused.fused_pos_cb)
- fused.fused_pos_cb(&fused.last_location, user_data);
+ fused.fused_pos_cb((gint)fused.last_location.time,
+ (gdouble)fused.last_location.latitude,
+ (gdouble)fused.last_location.longitude,
+ (gdouble)fused.last_location.altitude,
+ (gdouble)fused.last_location.speed,
+ (gdouble)fused.last_location.direction,
+ (gdouble)fused.last_location.climb,
+ (gdouble)fused.last_location.hor_dev,
+ (gdouble)fused.last_location.ver_dev,
+ fused.lbs_server_handle);
else
LOG_FUSED(LOG_ERROR, "fused_pos_cb == NULL");
}
static void on_sensor_event(sensor_h handle, sensor_event_s *event, gpointer sensor_id)
{
+// LOG_FUSED_FUNC;
+ fl_sensor_source sensor_type = PTR2ENUM(sensor_id, fl_sensor_source);
if (event) {
- fused_engine_process_sensory_event((fl_seconds)((double)(long long)event->timestamp / 1000000.0),
- PTR2ENUM(sensor_id, fl_sensory_source), event->values);
- } else {
- LOG_FUSED(LOG_ERROR, "(handle=%p, event=%p, sensor_id=%d): UNSUPPORTED",
- handle, event, PTR2ENUM(sensor_id, fl_sensory_source));
- }
+ bool is_new_position = false;
+ switch (sensor_type) {
+ case ACCELEROMETER:
+ is_new_position = fusion_engine_acc_event(&fused.fusion_engine,
+ (double)(long long)event->timestamp / 1000000.0,
+ event->values[DEV_SPATIAL_X],
+ event->values[DEV_SPATIAL_Y],
+ event->values[DEV_SPATIAL_Z]);
+ break;
+ case GYROSCOPE:
+ is_new_position = fusion_engine_gyro_event(&fused.fusion_engine,
+ (double)(long long)event->timestamp / 1000000.0,
+ conversions_degrees_to_radians(event->values[DEV_SPATIAL_X]),
+ conversions_degrees_to_radians(event->values[DEV_SPATIAL_Y]),
+ conversions_degrees_to_radians(event->values[DEV_SPATIAL_Z]));
+ break;
+ case PEDOMETER:
+ LOG_FUSED(LOG_DEBUG, "[PEDOMETER] timestamp[%llu] accuracy[%d] Step[%.0f] Step[%.0f] [%.1f] Dist[%.3f m] [%.1f] Speed[%.5f m/s] [%.1f] [%.1f]",
+ event->timestamp, event->accuracy,
+ event->values[0], event->values[1], event->values[2], event->values[3],
+ event->values[4], event->values[5], event->values[6], event->values[7]);
+
+ if (fused.last_gps_timestamp > 0 && fused.last_gps_speed > 0) {
+ double seconds_from_last_gps = (double)(long long)event->timestamp / 1000000.0
+ + fused.utc_offset_sec - (double)fused.last_gps_timestamp;
+ if (seconds_from_last_gps < FL_MAX_PEDOMETER_SKIP_TIME && fused.last_gps_speed >= FL_MIN_PEDOMETER_SKIP_SPEED) {
+ LOG_FUSED(LOG_DEBUG, "seconds_from_last_gps = %.1f/%.1f[s] && last_gps_speed = %.2f/%.1f[km/h] -> PEDOMETER SKIP",
+ seconds_from_last_gps, FL_MAX_PEDOMETER_SKIP_TIME, fused.last_gps_speed, FL_MIN_PEDOMETER_SKIP_SPEED);
+ break;
+ }
+ }
+
+ float speed = event->values[5];
+ if (fabs(speed) >= 0.001)
+ is_new_position = fusion_engine_speed_event(&fused.fusion_engine,
+ (double)(long long)event->timestamp / 1000000.0, event->values[5], 1.0);
+ break;
+ default:
+ LOG_FUSED(LOG_ERROR, "unsupported sensor type[%d]", sensor_type);
+ break;
+ }
+ if (is_new_position)
+ on_engine_new_position();
+ } else
+ LOG_FUSED(LOG_ERROR, "event == NULL");
}
#if (FL_MOTION_DETECTOR)
@@ -305,28 +379,23 @@ static void location_fused_pause()
{
LOG_FUSED_FUNC;
if (fused.status.paused == FALSE) {
- sensor_func_stop(fused.started_sensors & ~ACCELEROMETER_FLAG);
+ sensor_stop(GYROSCOPE);
+ sensor_stop(PEDOMETER);
}
+ fused.status.paused = TRUE;
}
-static LocationError location_fused_resume()
+static void location_fused_resume()
{
LOG_FUSED_FUNC;
- LocationError ret;
-
if (fused.status.paused) {
- ret = sensor_func_start(GYROSCOPE_FLAG);
- if (ret == LOCATION_ERROR_NONE)
- sensor_func_stop(GYROSCOPE_FLAG);
- } else {
- ret = LOCATION_ERROR_NOT_AVAILABLE;
+ sensor_start(GYROSCOPE);
+ sensor_start(PEDOMETER);
}
-
- LOG_FUSED(LOG_DEBUG, "0x%X", ret);
- return ret;
+ fused.status.paused = FALSE;
}
-static void on_motion_state(fl_motion_state state, gpointer data)
+static void on_motion_state(MotionDetectorState state)
{
LOG_FUSED(LOG_DEBUG, "(state=%u)", state);
if (fused.status.started) {
diff --git a/lbs-server/src/fused.h b/lbs-server/src/fused.h
index b34a7e1..df7dcf5 100644
--- a/lbs-server/src/fused.h
+++ b/lbs-server/src/fused.h
@@ -23,159 +23,94 @@
#ifndef __LOCATION_FUSED_H__
#define __LOCATION_FUSED_H__
-#include "gps_plugin_data_types.h"
-#include "fused/types.h"
-#include "lbs_dbus_server.h"
+#if (FL_MOTION_DETECTOR)
+#include "fused/MotionDetector.h"
+#endif
#include <glib.h>
-/***************************************************************************//**
- * This callback is called with position data.
- *
- * @param[in] location Location data
- * @param[in] user_data User defined data
- */
-typedef void (*fused_updated_cb)(fl_location *location, gpointer user_data);
+/** Labels for standard sensors */
+typedef enum {
+ ACCELEROMETER = 0,
+ GYROSCOPE,
+ PEDOMETER,
+ SENSOR_SOURCE_COUNT
+} fl_sensor_source;
-/***************************************************************************//**
- * TODO: TBD
+/**
+ * @brief This callback is called with position data.
*/
-void location_fused_init(fused_updated_cb _fused_pos_cb, gpointer lbs_server);
+typedef void (*fused_updated_cb)(gint timestamp, gdouble latitude, gdouble longitude, gdouble altitude,
+ gdouble speed, gdouble direction, gdouble climb, gdouble hor_accuracy, gdouble ver_accuracy, gpointer user_data);
-/***************************************************************************//**
- * TODO: TBD
- */
+void location_fused_init(fused_updated_cb _fused_pos_cb, gpointer lbs_server);
void location_fused_deinit();
-
-/***************************************************************************//**
- * TODO: TBD
- */
void location_fused_start();
-
-/***************************************************************************//**
- * TODO: TBD
- */
void location_fused_stop();
-/***************************************************************************//**
- * [callback] Pass the GPS location data to the engine for processing.
- *
- * @param[in] timestamp timestamp
- * @param[in] latitude latitude
- * @param[in] longitude longitude
- * @param[in] altitude altitude
- * @param[in] speed speed
- * @param[in] bearing bearing
- * @param[in] hor_accuracy hor_accuracy
- * @param[in] ver_accuracy ver_accuracy
+/**
+ * @brief [callback] Pass the GPS location data to the engine for processing.
+ * @param[in] timestamp timestamp
+ * @param[in] latitude latitude
+ * @param[in] longitude longitude
+ * @param[in] altitude altitude
+ * @param[in] speed speed
+ * @param[in] bearing bearing
+ * @param[in] hor_accuracy hor_accuracy
+ * @param[in] ver_accuracy ver_accuracy
*/
void send_gps_position_to_fused_engine(time_t timestamp, double latitude, double longitude,
double altitude, double speed, double bearing, double hor_accuracy, double ver_accuracy);
-/***************************************************************************//**
- * [callback] Pass the WPS location data to the engine for processing.
- *
- * @param[in] timestamp timestamp
- * @param[in] latitude latitude
- * @param[in] longitude longitude
- * @param[in] hor_accuracy hor_accuracy
- * @param[in] ver_accuracy ver_accuracy
+/**
+ * @brief [callback] Pass the WPS location data to the engine for processing.
+ * @param[in] timestamp timestamp
+ * @param[in] latitude latitude
+ * @param[in] longitude longitude
+ * @param[in] hor_accuracy hor_accuracy
+ * @param[in] ver_accuracy ver_accuracy
*/
-void send_wps_position_to_fused_engine(time_t timestamp, double latitude, double longitude,
- double hor_accuracy, double ver_accuracy);
+void send_wps_position_to_fused_engine(time_t timestamp, double latitude, double longitude, double hor_accuracy);
#if defined(__LOCATION_FUSED_C__)
-/***************************************************************************//**
- * [private] Detect sensors used for dead-reckoning and power-saving.
- *
- * @return
- * Conjunction of the detected and used sensors as bit-set.
- */
-static fl_sensory_flags sensor_func_detect();
+static void sensors_create();
-/***************************************************************************//**
- * [private] Detect and attach sensors used for dead-reckoning and power-saving.
- */
-static void sensor_func_create();
+const char* sensor_error_to_str(int error);
-/***************************************************************************//**
- * [private] Detach used sensors.
- */
-static void sensor_func_destroy();
+static void sensors_clear();
-/***************************************************************************//**
- * [private] Start selected subset of sensors.
- *
- * @param[in] sensors
- * Bit-set indicator of the sensors to be started.
- *
- * @return
- * - LOCATION_ERROR_NONE (zero) upon success;
- * - LOCATION_ERROR_NOT_SUPPORTED if the sensors are not attached;
- * - LOCATION_ERROR_CONFIGURATION if the sensors could not be started.
- */
-static LocationError sensor_func_start(fl_sensory_flags sensors);
+static void sensors_reset_listeners();
-/***************************************************************************//**
- * [private] Stop selected subset of sensors.
- *
- * @param[in] sensors
- * Bit-set indicator of the sensors to be stopped.
- */
-static void sensor_func_stop(fl_sensory_flags sensors);
+static void sensors_destroy();
-/***************************************************************************//**
- * [private, callback] Pass the sensory data to the engine.
- *
- * @param[in] handle
- * Sensor handle
- * @param[in] event
- * VConf key name to watch for changes
- * @param[in] sensor_id
- * TODO: TBD
- */
-static void on_sensor_event(sensor_h handle, sensor_event_s *event, gpointer sensor_id);
+static void sensor_start(fl_sensor_source sensor);
-/***************************************************************************//**
- * [private, callback] Invoked upon change of position reported by the engine.
- * There is no distinction between predicted and fused one; it can be either of
- * the two. Upon checking user-supplied conditions the notification is issued.
- *
- * @param[in] position
- * New spatio-temporal (4D) position.
- */
-static void on_engine_position(const fl_position_4d *position, gpointer user_data);
+static void sensor_stop(fl_sensor_source sensor);
-#if (FL_MOTION_DETECTOR)
+static void on_engine_new_position();
-/***************************************************************************//**
- * [private] Enters power-saving mode by unsubscribing from location source, and
- * and unused sensor(s)).
+/**
+ * @brief Pass the sensory data to the engine.
+ * @param[in] handle sensor handle
+ * @param[in] event vconf key name to watch for changes
+ * @param[in] sensor_id sensor id
*/
+static void on_sensor_event(sensor_h handle, sensor_event_s *event, gpointer sensor_id);
+
+#if (FL_MOTION_DETECTOR)
+/** Enters power-saving mode by unsubscribing from location source, and and unused sensor(s)) */
static void location_fused_pause();
-/***************************************************************************//**
- * [private] Resumes normal-power operation mode by subscribing to location
- * source, and and used sensor(s).
- *
- * @return
- * - LOCATION_ERROR_NONE (zero) upon success;
- * - error code otherwise.
- */
-static LocationError location_fused_resume();
+/** Resumes normal-power operation mode by subscribing to location source, and and used sensor(s) */
+static void location_fused_resume();
-/***************************************************************************//**
- * [private, callback] Receive notifications about motion state changes, and
- * pause/resume accordingly.
- *
- * @param[in] state
- * New motion state
+/**
+ * @brief Receive notifications about motion state changes, and pause/resume accordingly.
+ * @param[in] state New motion state
*/
-static void on_motion_state(fl_motion_state state, gpointer data);
+static void on_motion_state(MotionDetectorState state);
#else /* (FL_MOTION_DETECTOR) */
-/***************************************************************************//**
- * [private] Compilation stub.
- */
+/** Compilation stub */
#define on_motion_state NULL
#endif /* (FL_MOTION_DETECTOR) */
diff --git a/lbs-server/src/fused/AccelerometerFilter.c b/lbs-server/src/fused/AccelerometerFilter.c
new file mode 100644
index 0000000..eb2b7df
--- /dev/null
+++ b/lbs-server/src/fused/AccelerometerFilter.c
@@ -0,0 +1,105 @@
+/*
+ * Copyright (c) 2016-2017 Samsung Electronics Co., Ltd.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "AccelerometerFilter.h"
+#include "Conversions.h"
+#include "math-ext.h"
+#include "debug_util.h"
+
+#define _USE_MATH_DEFINES
+#include <math.h>
+
+#define DEFAULT_SAMPLING_FREQUENCY 10.0 /* [Hz] accelerometer sampling frequency */
+#define DEFAULT_ACCELERATION_SIGMA2 SQUARE(FL_ACCEL_NOISE_LEVEL) /* [(m/s^2)^2] */
+
+static const Vector_3d av = {0, 0, EARTH_GRAVITY};
+
+void accelerometer_filter_init(AccelerometerFilter* self,
+ MotionDetectorStateChangeListener on_motion_state_changed)
+{
+ LOG_FUSED_FUNC;
+ frequency_estimator_init(&self->frequency_estimator, DEFAULT_SAMPLING_FREQUENCY);
+ butterworth_3d_filter_init(&self->lp_3d_filter, FL_LP3D_CUTOFF_FREQUENCY);
+ gravity_estimator_init(&self->gravity_estimator);
+ motion_detector_init(&self->motion_detector, on_motion_state_changed);
+
+ butterworth_3d_filter_reset_to(&self->lp_3d_filter, av);
+ butterworth_3d_filter_set_sampling_frequency(&self->lp_3d_filter, DEFAULT_SAMPLING_FREQUENCY);
+
+ LOG_FUSED_DEV(DBG_LOW, UNINDENT("()"));
+}
+
+void accelerometer_filter_exit(AccelerometerFilter* self)
+{
+ LOG_FUSED_FUNC;
+ motion_detector_exit(&self->motion_detector);
+ gravity_estimator_exit(&self->gravity_estimator);
+ butterworth_3d_filter_exit(&self->lp_3d_filter);
+
+ LOG_FUSED_DEV(DBG_LOW, UNINDENT("()"));
+}
+
+void accelerometer_filter_reset(AccelerometerFilter* self, const_Vector_3d_ref av)
+{
+ LOG_FUSED_DEV(DBG_LOW, FUNCTION_PREFIX("()"));
+ frequency_estimator_init(&self->frequency_estimator, DEFAULT_SAMPLING_FREQUENCY);
+ motion_detector_reset(&self->motion_detector);
+ butterworth_3d_filter_reset_to(&self->lp_3d_filter, av);
+}
+
+void accelerometer_filter_process(AccelerometerFilter* self,
+ double time, const_Vector_3d_ref acc, Vector_3d_ref gu, double *w2gu)
+{
+ LOG_FUSED_DEV(DBG_LOW, FUNCTION_PREFIX("(t=%.2f, acc=(% 5.2f, % 5.2f, % 5.2f))"), time, acc[X], acc[Y], acc[Z]);
+
+ if (frequency_estimator_process(&self->frequency_estimator, time))
+ butterworth_3d_filter_set_sampling_frequency(&self->lp_3d_filter, frequency_estimator_get_frequency(&self->frequency_estimator));
+
+ const_Vector_3d_ref af = butterworth_3d_filter_process(&self->lp_3d_filter, acc);
+
+ LOG_FUSED_DEV(DBG_LOW, FUNCTION_PREFIX("t=%.16g, af=(%.16g, %.16g, %.16g)"), time, af[0], af[1], af[2]);
+
+ /* the z-value is passed through 2nd order Butterworth, then 1st order EMA */
+ const double af_len = vector_3d_length(af);
+ const double overlap = vector_3d_dot_product(af, acc);
+ const double g2 = gravity_estimator_process(&self->gravity_estimator, overlap);
+
+ LOG_FUSED_DEV(DBG_INFO, FUNCTION_PREFIX("(t=%.2f, |af|=%5.2f, <g>=%5.2f"), time, af_len, g2);
+
+ if (af_len > FL_ACCEL_NOISE_LEVEL && g2 > 1) {
+ const double norm_out = 1.0 / af_len;
+ vector_3d_mul_number(af, norm_out, gu);
+ *w2gu = self->gravity_estimator.base.decay_rate / (fl_square(gu[X]) + fl_square(gu[Y]) + fl_square(gu[Z] - 1) + GEO_DIMENSION * DEFAULT_ACCELERATION_SIGMA2);
+
+ LOG_FUSED_DEV(DBG_LOW, FUNCTION_PREFIX("t=%.16g, decay=%.16g, gu=(%.16g, %.16g, %.16g), af_len=%.16g, overlap=%.16g, g2=%.16g"),
+ time, self->gravity_estimator.base.decay_rate, gu[X], gu[Y], gu[Z], af_len, overlap, g2);
+ } else {
+ vector_3d_set(gu, 0, 0, 1); /* default */
+ *w2gu = self->gravity_estimator.base.decay_rate / (GEO_DIMENSION * DEFAULT_ACCELERATION_SIGMA2);
+
+ LOG_FUSED_DEV(DBG_LOW, FUNCTION_PREFIX("t=%.16g, decay=%.16g, gu=(%.16g, %.16g, %.16g), af_len=%.16g, overlap=%.16g, g2=%.16g"),
+ time, self->gravity_estimator.base.decay_rate, gu[X], gu[Y], gu[Z], af_len, overlap, g2);
+ }
+
+ Vector_3d al; /* Linear acceleration in global frame. */
+ const double g = sqrt(g2);
+ if (g > 1)
+ vector_3d_linear_combination(EARTH_GRAVITY / g, acc, -EARTH_GRAVITY, gu, al);
+ else
+ vector_3d_copy(al, acc);
+
+ motion_detector_process(&self->motion_detector, time, al);
+}
diff --git a/lbs-server/src/fused/AccelerometerFilter.h b/lbs-server/src/fused/AccelerometerFilter.h
new file mode 100644
index 0000000..81970aa
--- /dev/null
+++ b/lbs-server/src/fused/AccelerometerFilter.h
@@ -0,0 +1,81 @@
+/*
+ * Copyright (c) 2016-2017 Samsung Electronics Co., Ltd.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/**
+ * @file AccelerometerFilter.h
+ * @brief Accelerometer calibration (scale and sampling frequency), and
+ * low-pass 3D filtering.
+ */
+
+#pragma once
+#ifndef __ACCELEROMETER_FILTER_H__
+#define __ACCELEROMETER_FILTER_H__
+
+#include "FrequencyEstimator.h"
+#include "Butterworth3dFilter.h"
+#include "MotionDetector.h"
+#include "GravityEstimator.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/** Accelerometer calibration (scale and sampling frequency), and low-pass 3D filtering */
+typedef struct {
+ FrequencyEstimator frequency_estimator;
+ Butterworth3dFilter lp_3d_filter;
+ GravityEstimator gravity_estimator;
+ MotionDetector motion_detector;
+} AccelerometerFilter;
+
+/**
+ * @brief Initialization of the AccelerometerFilter object (constructor).
+ * @param[in] on_motion_state_changed Callback to be invoked when the detected state of motion changes.
+ */
+void accelerometer_filter_init(AccelerometerFilter* self, MotionDetectorStateChangeListener on_motion_state_changed);
+
+void accelerometer_filter_exit(AccelerometerFilter* self);
+
+/**
+ * @brief Reset of the internal state back to initial one.
+ * This function is used for repetitive testing and module soft restarts.
+ * @param[in] av Stationary acceleration state to be set (typically {0, 0, g}).
+ */
+void accelerometer_filter_reset(AccelerometerFilter* self, const_Vector_3d_ref av);
+
+/**
+ * @brief Processing of a single sample from the accelerometer. This function performs:
+ * - estimation of the sampling frequency,
+ * - estimation of the gravity direction via low-pass 3D filter (cf. LP3D unit),
+ * - estimation of the gravity value (cf. GRES unit) and scale correction,
+ * - estimation of the linear acceleration i.e. with subtracted gravity component,
+ * - motion state detection (cf. MOTI unit)
+ * @param[in] time Time of the event in seconds.
+ * @param[in] acc Measured 3D acceleration vector in global frame.
+ * @param[out] gu Unit vector along gravity direction oriented upwards.
+ * @param[out] w2gu Estimated inverse covariance (squared weight) of a @gu.
+ */
+void accelerometer_filter_process(AccelerometerFilter* self,
+ double time, const_Vector_3d_ref acc, Vector_3d_ref gu, double *w2gu);
+
+/** @return double */
+#define accelerometer_filter_get_update_rate(self) gravity_estimator_get_update_rate(&(self)->gravity_estimator)
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* __ACCELEROMETER_FILTER_H__ */
diff --git a/lbs-server/src/fused/Butterworth3dFilter.c b/lbs-server/src/fused/Butterworth3dFilter.c
new file mode 100644
index 0000000..0ccf05e
--- /dev/null
+++ b/lbs-server/src/fused/Butterworth3dFilter.c
@@ -0,0 +1,104 @@
+/*
+ * Copyright (c) 2016-2017 Samsung Electronics Co., Ltd.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#if !defined(LOG_THRESHOLD)
+ /* custom logging threshold - keep undefined on the repo */
+ /* #define LOG_THRESHOLD LOG_LEVEL_TRACE */
+#endif
+
+#include "Butterworth3dFilter.h"
+#include "math-ext.h"
+#include "debug_util.h"
+
+#define LP3D_FORMAT "% 6.2f"
+
+void butterworth_3d_filter_init(Butterworth3dFilter* self, double f)
+{
+ LOG_FUSED_DEV(DBG_LOW, INDENT("()"));
+ self->cut_off_frequency = f;
+ self->lp3d_G = 0;
+ self->lp3d_B1 = -2;
+ self->lp3d_B2 = 1;
+ butterworth_3d_filter_reset(self);
+ LOG_FUSED_DEV(DBG_LOW, UNINDENT("(): OK"));
+}
+
+void butterworth_3d_filter_exit(Butterworth3dFilter* self)\
+{
+ LOG_FUSED_DEV(DBG_LOW, FUNCTION_PREFIX("()"));
+}
+
+void butterworth_3d_filter_set_sampling_frequency(Butterworth3dFilter* self, double f)
+{
+ LOG_FUSED_DEV(DBG_LOW, FUNCTION_PREFIX("frequency() f=%.16e, cut_off=%.16e"), f, self->cut_off_frequency);
+
+ const double omegaC = tan(M_PI * self->cut_off_frequency / f);
+ const double omegaC2 = fl_square(omegaC);
+ const double _B0 = 1.0 / (1 + 2 * omegaC * cos(M_PI_4) + omegaC2);
+ self->lp3d_B1 = _B0 * 2*(omegaC2 - 1);
+ self->lp3d_B2 = _B0 * (1 - 2 * omegaC * cos(M_PI_4) + omegaC2);
+ self->lp3d_G = _B0 * omegaC2;
+
+ LOG_FUSED_DEV(DBG_LOW, FUNCTION_PREFIX("(%g Hz): OK"), f);
+}
+
+void butterworth_3d_filter_reset(Butterworth3dFilter* self)
+{
+ LOG_FUSED_DEV(DBG_LOW, FUNCTION_PREFIX("()"));
+ vector_3d_clear_array(self->lp3d_u);
+ vector_3d_clear_array(self->lp3d_v);
+}
+
+void butterworth_3d_filter_reset_to(Butterworth3dFilter* self, const_Vector_3d_ref stationary)
+{
+ LOG_FUSED_DEV(DBG_LOW, FUNCTION_PREFIX("({" LP3D_FORMAT ", " LP3D_FORMAT ", " LP3D_FORMAT "})"), stationary[0], stationary[1], stationary[2]);
+ unsigned i = GEO_DIMENSION;
+ do {
+ --i;
+ /* up-conversion */
+ self->lp3d_u[TIME_SHIFT_1][i] = stationary[i];
+ self->lp3d_u[TIME_SHIFT_2][i] = stationary[i];
+ self->lp3d_v[TIME_SHIFT_1][i] = stationary[i];
+ self->lp3d_v[TIME_SHIFT_2][i] = stationary[i];
+ } while (i);
+}
+
+const_Vector_3d_ref butterworth_3d_filter_process(Butterworth3dFilter* self, const_Vector_3d_ref u)
+{
+ LOG_FUSED_DEV(DBG_LOW, FUNCTION_PREFIX("({" LP3D_FORMAT ", " LP3D_FORMAT ", " LP3D_FORMAT "})"), u[0], u[1], u[2]);
+ unsigned i = GEO_DIMENSION;
+ do {
+ --i;
+#define u1 self->lp3d_u[TIME_SHIFT_1][i]
+#define u2 self->lp3d_u[TIME_SHIFT_2][i]
+#define v1 self->lp3d_v[TIME_SHIFT_1][i]
+#define v2 self->lp3d_v[TIME_SHIFT_2][i]
+ const double u0 = u[i];
+ const double v0 = self->lp3d_G * (u0 + 2 * u1 + u2) - self->lp3d_B1 * v1 - self->lp3d_B2 * v2;
+ LOG_FUSED_DEV(DBG_LOW, FUNCTION_PREFIX("process() i=%i, lp3d_G=%.16e, lp3d_B1=%.16e, u0=%.16e, u1=%.16e, u2=%.16e, v0=%.16e, v1=%.16e, v2=%.16e"),
+ i, self->lp3d_G, self->lp3d_B1, u0, u1, u2, v0, v1, v2);
+
+ u2 = u1;
+ u1 = u0;
+ v2 = v1;
+ v1 = v0;
+#undef u1
+#undef u2
+#undef v1
+#undef v2
+ } while (i);
+ return self->lp3d_v[TIME_SHIFT_1];
+}
diff --git a/lbs-server/src/fused/Butterworth3dFilter.h b/lbs-server/src/fused/Butterworth3dFilter.h
new file mode 100644
index 0000000..993dab9
--- /dev/null
+++ b/lbs-server/src/fused/Butterworth3dFilter.h
@@ -0,0 +1,80 @@
+/*
+ * Copyright (c) 2016-2017 Samsung Electronics Co., Ltd.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/**
+ * @file Butterworth3dFilter.h
+ * @brief Low-pass 3D 2nd order Butterworth filter.
+ */
+
+#pragma once
+#ifndef __BUTTERWORTH_3D_FILTER_H__
+#define __BUTTERWORTH_3D_FILTER_H__
+
+#include "Vector.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+
+typedef enum {
+ TIME_SHIFT_1, /* t - 1 */
+ TIME_SHIFT_2, /* t - 2 */
+ TIME_SHIFT_COUNT
+} TimeShift;
+
+typedef struct {
+ /* Filter coefficients. */
+ double cut_off_frequency;
+ double lp3d_G; /* omega^2/B_0 */
+ double lp3d_B1; /* B_1/B_0 */
+ double lp3d_B2; /* B_2/B_0 */
+
+ /* Memory of past states. */
+ Vector_3d lp3d_u[TIME_SHIFT_COUNT];
+ Vector_3d lp3d_v[TIME_SHIFT_COUNT];
+} Butterworth3dFilter;
+
+void butterworth_3d_filter_init(Butterworth3dFilter* self, double f);
+
+void butterworth_3d_filter_exit(Butterworth3dFilter* self);
+
+void butterworth_3d_filter_reset(Butterworth3dFilter* self);
+
+/**
+ * @brief Adjust internal parameters to the given sampling frequency and reset the the past state memory.
+ * @param[in] f The sampling frequency in [Hz].
+ */
+void butterworth_3d_filter_set_sampling_frequency(Butterworth3dFilter* self, double f);
+
+/**
+ * @brief Reset to the given stationary state (input = output).
+ * @param[in] stationary 3D vector of acceleration measured in tangent coordinate frame.
+ */
+void butterworth_3d_filter_reset_to(Butterworth3dFilter* self, const_Vector_3d_ref stationary);
+
+/**
+ * @brief Process single input sample through the 3D Butterworth filter.
+ * @param[in] u 3D acceleration input vector.
+ * @return 3D acceleration output vector.
+ */
+const_Vector_3d_ref butterworth_3d_filter_process(Butterworth3dFilter* self, const_Vector_3d_ref u);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* __BUTTERWORTH_3D_FILTER_H__ */
diff --git a/lbs-server/src/fused/CalibrationFilter.c b/lbs-server/src/fused/CalibrationFilter.c
new file mode 100644
index 0000000..b3bdbd3
--- /dev/null
+++ b/lbs-server/src/fused/CalibrationFilter.c
@@ -0,0 +1,165 @@
+/*
+ * Copyright (c) 2016-2017 Samsung Electronics Co., Ltd.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "CalibrationFilter.h"
+#include "Conversions.h"
+#include "math-ext.h"
+#include <stdio.h>
+
+static Vector_3d zero = { 0, 0, 0 };
+
+/** Minimum measurements in stable state to calculate new calibration parameters. */
+static const int MIN_MEAS = 100;
+
+static const double MAX_PRECISION = 100000;
+
+/** Time of decorrelation in [s], currently 100s. */
+static const double DT = 100;
+
+/** Days duration in [s], currently 24h. */
+static const double DAY = 24L * 60L * 60L;
+
+/** Length of buffer for calibration parameters. */
+static const int PARAMS_LEN = 100;
+
+/** Format of calibration parameters. */
+static const int PARAMS_VERSION = 1;
+
+void calibration_filter_init(CalibrationFilter* self, double utcOffset)
+{
+ self->utc_offset = utcOffset;
+ self->autocalibration = true;
+ calibration_filter_clear_calibration(self);
+ calibration_filter_reset(self);
+}
+
+void calibration_filter_exit(CalibrationFilter* self)
+{
+}
+
+void calibration_filter_reset(CalibrationFilter* self)
+{
+ self->calibrating = false;
+ self->calibration_time = FL_UNDEFINED_TIME;
+}
+
+Vector_3d_ref calibration_filter_filter(CalibrationFilter* self, double time,
+ const_Vector_3d_ref data, bool is_stable)
+{
+ if (self->autocalibration) {
+ unsigned i;
+ if (is_stable) {
+ if (!self->calibrating) {
+ self->cal_nr = 0;
+ vector_3d_clear(self->sum);
+ vector_3d_clear(self->sum2);
+ self->calibrating = true;
+ }
+ self->cal_nr++;
+ for (i = GEO_DIMENSION; i;) {
+ i--;
+ self->sum[i] += data[i];
+ self->sum2[i] += fl_square(data[i]);
+ }
+ return zero;
+
+ } else {
+
+ if (self->calibrating) {
+ self->calibrating = false;
+
+ if (self->cal_nr > MIN_MEAS) {
+ Vector_3d mean;
+ Vector_3d std_dev;
+ for (i = GEO_DIMENSION; i;) {
+ i--;
+ double m = self->sum[i] / self->cal_nr;
+ mean[i] = m;
+ double variance = self->sum2[i] / self->cal_nr - m * m;
+ std_dev[i] = variance > 0 ? sqrt(variance) : 0;
+ }
+ double dt = (!KNOWN_TIME(self->calibration_time)) || time < self->calibration_time
+ ? DAY : time - self->calibration_time;
+
+ const double curPrec = self->cal_prec * exp(-dt / DT);
+ double newPrec = self->cal_nr / vector_3d_length2(std_dev);
+ if (newPrec > MAX_PRECISION) {
+ newPrec = MAX_PRECISION;
+ }
+ const double alpha = curPrec / (newPrec + curPrec);
+ const double alpha1 = 1. - alpha;
+
+ for (i = GEO_DIMENSION; i;) {
+ i--;
+ self->bias[i] = self->bias[i] * alpha + mean[i] * alpha1;
+ }
+ self->cal_prec = newPrec + curPrec;
+ self->calibration_time = time;
+ }
+ }
+ }
+ }
+ vector_3d_sub_vector_3d(data, self->bias, self->output);
+ return self->output;
+}
+
+void calibration_filter_set_enable_autocalibration(CalibrationFilter* self,
+ bool enable)
+{
+ self->autocalibration = enable;
+}
+
+void calibration_filter_clear_calibration(CalibrationFilter* self)
+{
+ self->calibration_time = FL_UNDEFINED_TIME;
+ self->cal_prec = 0;
+ vector_3d_clear(self->bias);
+}
+
+char* calibration_filter_get_calibration(CalibrationFilter* self)
+{
+ char* params = (char*) calloc(PARAMS_LEN, sizeof(char));
+ if (params != NULL) {
+ snprintf(params, PARAMS_LEN, "%i,%.9f,%.10f,%.10f,%.10f,%.10f",
+ PARAMS_VERSION,
+ self->calibration_time == FL_UNDEFINED_TIME ? 0 :
+ self->calibration_time + self->utc_offset,
+ self->cal_prec, self->bias[0], self->bias[1], self->bias[2]);
+ }
+ return params;
+}
+
+void calibration_filter_set_calibration(CalibrationFilter* self, const char* params)
+{
+ if (params != NULL) {
+ int version;
+ double cal_time;
+ double cal_prec;
+ double bias0;
+ double bias1;
+ double bias2;
+ if (sscanf(params, "%i,%lf,%lf,%lf,%lf,%lf",
+ &version, &cal_time, &cal_prec, &bias0, &bias1, &bias2) == 6) {
+ if (version == PARAMS_VERSION) {
+ self->calibration_time = cal_time == 0 ? FL_UNDEFINED_TIME : cal_time - self->utc_offset;
+ self->cal_prec = cal_prec;
+ self->bias[0] = bias0;
+ self->bias[1] = bias1;
+ self->bias[2] = bias2;
+ }
+ }
+ }
+}
diff --git a/lbs-server/src/fused/CalibrationFilter.h b/lbs-server/src/fused/CalibrationFilter.h
new file mode 100644
index 0000000..d3f04f4
--- /dev/null
+++ b/lbs-server/src/fused/CalibrationFilter.h
@@ -0,0 +1,50 @@
+/*
+ * Copyright (c) 2016-2017 Samsung Electronics Co., Ltd.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef __CALIBRATIONFILTER_H_
+#define __CALIBRATIONFILTER_H_
+
+#include "Vector.h"
+
+#include <stdbool.h>
+
+
+typedef struct {
+ double utc_offset; /* Difference between UTC time and monotonic time in [s]. */
+ double calibration_time;
+ Vector_3d bias;
+ Vector_3d output;
+ bool autocalibration;
+ bool calibrating;
+ double cal_prec;
+ long cal_nr;
+ Vector_3d sum;
+ Vector_3d sum2;
+} CalibrationFilter;
+
+extern void calibration_filter_init(CalibrationFilter* self, double utcOffset);
+extern void calibration_filter_exit(CalibrationFilter* self);
+extern void calibration_filter_reset(CalibrationFilter* self);
+
+extern Vector_3d_ref calibration_filter_filter(CalibrationFilter* self, double time, const_Vector_3d_ref data, bool is_stable);
+
+extern void calibration_filter_set_enable_autocalibration(CalibrationFilter* self, bool enable);
+
+extern void calibration_filter_clear_calibration(CalibrationFilter* self);
+extern char* calibration_filter_get_calibration(CalibrationFilter* self);
+extern void calibration_filter_set_calibration(CalibrationFilter* self, const char* params);
+
+#endif /* __CALIBRATIONFILTER_H_ */
diff --git a/lbs-server/src/fused/conversions.c b/lbs-server/src/fused/Conversions.c
index 048177d..affa501 100644
--- a/lbs-server/src/fused/conversions.c
+++ b/lbs-server/src/fused/Conversions.c
@@ -14,34 +14,36 @@
* limitations under the License.
*/
-#define __FL_CONVERSIONS_C__
+#include "Conversions.h"
-#include "conversions.h"
-
-fl_degrees fl_radians_to_positive_degrees(fl_radians radians)
+double conversions_radians_to_positive_degrees(double radians)
{
- fl_degrees d;
-
- d = (180 / M_PI) * radians;
- /* When the value only ocassionally enters adjacent branches this is more
+ double d = (180 / M_PI) * radians;
+ /* When the value only occasionally enters adjacent branches this is more
efficient a way than division modulo. */
if (d < 0)
- do { d += 360; } while (d < 0);
+ do {
+ d += 360;
+ } while (d < 0);
else if (d >= 360)
- do { d -= 360; } while (d >= 360);
+ do {
+ d -= 360;
+ } while (d >= 360);
return d;
}
-fl_degrees fl_radians_to_balanced_degrees(fl_radians radians)
+double conversions_radians_to_balanced_degrees(double radians)
{
- fl_degrees d;
-
- d = (180 / M_PI) * radians;
+ double d = (180 / M_PI) * radians;
/* When the value only ocassionally enters adjacent branches this is more
efficient a way than division modulo. */
if (d < -180)
- do { d += 360; } while (d < -180);
+ do {
+ d += 360;
+ } while (d < -180);
else if (d >= 180)
- do { d -= 360; } while (d >= 180);
+ do {
+ d -= 360;
+ } while (d >= 180);
return d;
}
diff --git a/lbs-server/src/fused/Conversions.h b/lbs-server/src/fused/Conversions.h
new file mode 100644
index 0000000..42aaa66
--- /dev/null
+++ b/lbs-server/src/fused/Conversions.h
@@ -0,0 +1,73 @@
+/*
+ * Copyright (c) 2016-2017 Samsung Electronics Co., Ltd.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/**
+ * @file Conversions.h
+ * @brief Units conversion functions
+ */
+
+#pragma once
+#ifndef __CONVERSIONS_H__
+#define __CONVERSIONS_H__
+
+#include "math-ext.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#define FL_UNDEFINED_TIME -1e+48
+#define KNOWN_TIME(t) (t > FL_UNDEFINED_TIME)
+
+/**
+ * @brief Convert degrees to radians.
+ * @param[in] degrees Value in degrees.
+ * @return Corresponding value in radians.
+ */
+#define conversions_degrees_to_radians(degrees) ((M_PI / 180) * (degrees))
+
+/**
+ * @brief Convert radians to degrees.
+ * @param[in] radians Value in radians.
+ * @return Corresponding value in degrees.
+ */
+#define conversions_radians_to_degrees(radians) ((180 / M_PI) * (radians))
+
+/**
+ * @brief Convert radians to degrees in the [0,360) range
+ * @param[in] radians Arbitrary value in radians.
+ * @return Value in degrees projected onto the [0,360) branch.
+ */
+double conversions_radians_to_positive_degrees(double radians);
+
+/**
+ * @brief Convert radians to degrees in the [-180,180) range.
+ * @param[in] radians Arbitrary value in radians.
+ * @return Value in degrees projected onto the [-180,180) branch.
+ */
+double conversions_radians_to_balanced_degrees(double radians);
+
+/** Convert radians to latitude degrees [-90,90] */
+#define conversions_radians_to_latitude conversions_radians_to_balanced_degrees
+
+/** Convert radians to longitude degrees [0, 360) */
+#define conversions_radians_to_longitude conversions_radians_to_positive_degrees
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* __CONVERSIONS_H_ */
diff --git a/lbs-server/src/fused/FrequencyEstimator.c b/lbs-server/src/fused/FrequencyEstimator.c
new file mode 100644
index 0000000..863de85
--- /dev/null
+++ b/lbs-server/src/fused/FrequencyEstimator.c
@@ -0,0 +1,60 @@
+/*
+ * Copyright (c) 2016-2017 Samsung Electronics Co., Ltd.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "FrequencyEstimator.h"
+#include "Conversions.h"
+#include "debug_util.h"
+
+#define FREQUENCY_ESTIMATOR_UPDATE_THRESHOLD (1.0 * 1.0)
+
+void frequency_estimator_init(FrequencyEstimator* self, double initial_frequency)
+{
+ self->frequency = initial_frequency;
+ self->frequency_update_time = FL_UNDEFINED_TIME;
+ self->samples = 0;
+
+ LOG_FUSED_DEV(DBG_LOW, FUNCTION_PREFIX("frequency_estimator_process() last_t=%.16e, last_f=%.16e"), self->frequency_update_time, self->frequency);
+}
+
+bool frequency_estimator_process(FrequencyEstimator* self, double time)
+{
+ LOG_FUSED_DEV(DBG_LOW, FUNCTION_PREFIX("t=%.16e, last_t=%.16e, f=%.16e, c=%i"), time, self->frequency_update_time, self->frequency, self->samples);
+
+ bool frequency_updated = false;
+ if (KNOWN_TIME(self->frequency_update_time)) {
+ ++(self->samples);
+ const double dt = time - self->frequency_update_time;
+
+ if (dt > 0) {
+ const double f = self->samples / dt;
+ const double df = fabs(f - self->frequency);
+ if (dt * df >= FREQUENCY_ESTIMATOR_UPDATE_THRESHOLD) {
+ self->samples = 0;
+ self->frequency = f;
+ self->frequency_update_time = time;
+ frequency_updated = true;
+
+ LOG_FUSED_DEV(DBG_LOW, FUNCTION_PREFIX("last_t=%.16e, f=%.16e"), time, self->frequency);
+ }
+ }
+
+ } else {
+ self->frequency_update_time = time;
+ LOG_FUSED_DEV(DBG_LOW, FUNCTION_PREFIX("last_t=%.16e, f=%.16e"), time, self->frequency);
+ }
+
+ return frequency_updated;
+}
diff --git a/lbs-server/src/fused/FrequencyEstimator.h b/lbs-server/src/fused/FrequencyEstimator.h
new file mode 100644
index 0000000..ea5b809
--- /dev/null
+++ b/lbs-server/src/fused/FrequencyEstimator.h
@@ -0,0 +1,59 @@
+/*
+ * Copyright (c) 2016-2017 Samsung Electronics Co., Ltd.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/**
+ * @file FrequencyEstimator.h
+ * @brief Estimates frequency of arbitrary timed events.
+ */
+
+#pragma once
+#ifndef __FREQUENCYESTIMATOR_H_
+#define __FREQUENCYESTIMATOR_H_
+
+#include <stdbool.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+
+typedef struct {
+ double frequency; /* Current estimated frequency. */
+ double frequency_update_time; /* Time of last frequency update. */
+ unsigned samples; /* Number of samples from last frequency update. */
+} FrequencyEstimator;
+
+/**
+ * @brief Initialization of the FrequencyEstimator object (constructor).
+ * @param[in] initial_frequency Initial frequency.
+ */
+extern void frequency_estimator_init(FrequencyEstimator* self, double initial_frequency);
+
+/** (double) Returns current frequency. */
+#define frequency_estimator_get_frequency(self) ((self)->frequency)
+
+/**
+ * @brief Called when new event happens.
+ * @param[in] time Time of event.
+ * @return Is new frequency calculated.
+ */
+extern bool frequency_estimator_process(FrequencyEstimator* self, double time);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* __FREQUENCYESTIMATOR_H_ */
diff --git a/lbs-server/src/fused/FusionEngine.c b/lbs-server/src/fused/FusionEngine.c
new file mode 100644
index 0000000..291fea9
--- /dev/null
+++ b/lbs-server/src/fused/FusionEngine.c
@@ -0,0 +1,353 @@
+/*
+ * Copyright (c) 2016-2017 Samsung Electronics Co., Ltd.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "GyroscopeFilter.h"
+#include "Conversions.h"
+#include "FusionEngine.h"
+#include "debug_util.h"
+
+#define TIME_FORMAT "%.3fs"
+
+/* the bigger the default standard deviations, the swifter response to first measurements */
+#define INITIAL_POSITION_SIGMA2 SQUARE(1024 * M_2PI * EARTH_RADIUS)
+#define INITIAL_VELOCITY_SIGMA2 (1024 * 1024 * SQUARE(FL_DEFAULT_VELOCITY_SIGMA))
+#define INITIAL_ACCELERATION_SIGMA2 DEFAULT_ACCELERATION_SIGMA2
+
+void fusion_engine_init(FusionEngine* self, MotionDetectorStateChangeListener on_motion_state_changed, double utcOffset)
+{
+ LOG_FUSED_DEV(DBG_LOW, INDENT("(motion_cb=%p)"), on_motion_state_changed);
+ orientation_init(&self->orientation, on_motion_state_changed, DEFAULT_ROTATION_SIGMA2);
+ gyroscope_filter_init(&self->gyro_bias);
+ time_offset_init(&self->timestamp_offset);
+ self->engine_started = false;
+ self->enable_gyro_filter = false;
+ peace_detector_init(&self->acc_peace_detector, 0.2, 10);
+ peace_detector_init(&self->gyro_peace_detector, 0.04, 10);
+ calibration_filter_init(&self->calibration_filter, utcOffset);
+ self->enable_static_calibration = true;
+ fusion_engine_reset(self);
+ LOG_FUSED_DEV(DBG_LOW, UNINDENT("()"));
+}
+
+void fusion_engine_exit(FusionEngine* self)
+{
+ LOG_FUSED_DEV(DBG_LOW, INDENT("()"));
+ time_offset_exit(&self->timestamp_offset);
+ gyroscope_filter_exit(&self->gyro_bias);
+ orientation_exit(&self->orientation);
+ LOG_FUSED_DEV(DBG_LOW, UNINDENT("()"));
+}
+
+void fusion_engine_start(FusionEngine* self)
+{
+ if (self->engine_started == false) {
+ self->engine_started = true;
+ LOG_FUSED_DEV(DBG_LOW, UNINDENT("(): OK"));
+ } else {
+ LOG_FUSED_DEV(DBG_LOW, UNINDENT("(): Already started"));
+ }
+}
+
+void fusion_engine_stop(FusionEngine* self)
+{
+ if (self->engine_started) {
+ self->engine_started = false;
+ LOG_FUSED_DEV(DBG_LOW, FUNCTION_PREFIX("(): OK"));
+ } else {
+ LOG_FUSED_DEV(DBG_ERR, FUNCTION_PREFIX("(): E_NOT_STARTED"));
+ }
+}
+
+void fusion_engine_reset(FusionEngine* self)
+{
+ LOG_FUSED_DEV(DBG_LOW, FUNCTION_PREFIX("()"));
+ self->last_notification_time = FL_UNDEFINED_TIME;
+ self->was_position = false;
+
+ location_filter_init(&self->kalf, 0, 0, EARTH_RADIUS, INITIAL_POSITION_SIGMA2, INITIAL_VELOCITY_SIGMA2, INITIAL_ACCELERATION_SIGMA2);
+ self->fix_status = LOCATION_STATUS_NO_FIX;
+ tangent_frame_init(&self->__te);
+ time_offset_reset(&self->timestamp_offset);
+ orientation_reset(&self->orientation);
+ peace_detector_reset(&self->acc_peace_detector);
+ peace_detector_reset(&self->gyro_peace_detector);
+ calibration_filter_reset(&self->calibration_filter);
+}
+
+static void fusion_engine_spherical_to_tangent2d(FusionEngine* self,
+ double latitude, double longitude, double sigma_of_horizontal_pos, Vector_3d_ref pm, Vector_3d_ref s2pm)
+{
+ self->fix_status = LOCATION_STATUS_2D_FIX;
+ double radius = location_filter_get_position(&self->kalf)[Z];
+ tangent_frame_spherical_to_tangent(&self->__te, latitude, longitude, radius, pm);
+
+ s2pm[X] = s2pm[Y] = s2pm[Z] = fl_square(sigma_of_horizontal_pos);
+}
+
+static void fusion_engine_spherical_to_tangent3d(FusionEngine* self,
+ double latitude, double longitude, double altitude, double hor_accuracy, double ver_accuracy,
+ double speed, double sigma_of_speed, double direction, double climb, double sigma_of_climb,
+ Vector_3d_ref pm, Vector_3d_ref vm, Vector_3d_ref s2pm, Vector_3d_ref s2vm)
+{
+ self->fix_status = LOCATION_STATUS_3D_FIX;
+ double radius = EARTH_RADIUS + altitude;
+ tangent_frame_spherical_to_tangent(&self->__te, latitude, longitude, radius, pm);
+
+ double cd = pm[Z] / radius;
+ double cd2 = SQUARE(cd);
+ double sd2 = 1 - cd2;
+ if (sd2 < 0) {
+ cd2 = 1;
+ sd2 = 0;
+ }
+
+ /* Notice: in principle we should rotate the diagonal sigma matrix here (two matrix multiplications).
+ This approximation assumes the old and new points are close. */
+ if (ver_accuracy > 0) {
+ double s2h = fl_square(hor_accuracy);
+ double s2v = fl_square(ver_accuracy);
+ s2pm[X] = s2pm[Y] = cd2 * s2h + sd2 * s2v;
+ s2pm[Z] = sd2 * s2h + cd2 * s2v;
+ } else {
+ s2pm[X] = s2pm[Y] = s2pm[Z] = fl_square(hor_accuracy);
+ }
+
+ tangent_frame_spherical_to_tangent_speed(&self->__te, pm, speed, direction, climb, vm);
+ if (sigma_of_climb > 0) {
+ s2vm[X] = s2vm[Y] = cd2 * fl_square(sigma_of_speed) + sd2 * fl_square(sigma_of_climb);
+ s2vm[Z] = sd2 * fl_square(sigma_of_speed) + cd2 * fl_square(sigma_of_climb);
+ } else {
+ s2vm[X] = s2vm[Y] = s2vm[Z] = fl_square(sigma_of_speed);
+ }
+}
+
+/**
+ * @brief Check the notification conditions; if satisfied get the current
+ * prediction in global coordinate frame and signal it to the user.
+ * @param[in] time Internal time
+ */
+static bool fusion_engine_signal_updated_location(FusionEngine* self, double time)
+{
+ LOG_FUSED_DEV(DBG_ERR, "t=%g, fixing=%g", time, self->last_notification_time);
+ if (FL_MIN_NOTIFICATION_FILTRATION && self->engine_started && self->was_position) {
+ time += time_offset_get(&self->timestamp_offset);
+ if (!KNOWN_TIME(self->last_notification_time) ||
+ time - self->last_notification_time >= FL_MIN_NOTIFICATION_INTERVAL) {
+ self->last_notification_time = time;
+ return true;
+ }
+ }
+ return false;
+}
+
+/** Filter input: Enter the raw position measurement */
+bool fusion_engine_position_2d_event(FusionEngine* self, double time,
+ double latitude, double longitude, double sigma_of_horizontal_pos)
+{
+ double t = time_offset_correct_time(&self->timestamp_offset, time);
+ LOG_FUSED_DEV(DBG_LOW, FUNCTION_PREFIX("(t=" TIME_FORMAT ", pos=(%g, %g), sp=%gm)"),
+ t, latitude, longitude, sigma_of_horizontal_pos);
+ Vector_3d pm; /* p-measurement */
+ Vector_3d s2pm; /* p-measurement variance */
+
+ fusion_engine_spherical_to_tangent2d(self, latitude, longitude, sigma_of_horizontal_pos, pm, s2pm);
+ location_filter_kalman_p(&self->kalf, t, pm, s2pm);
+
+ self->was_position = true;
+ tangent_frame_create(&self->__te, location_filter_get_position(&self->kalf), location_filter_get_velocity(&self->kalf));
+ location_filter_update_state(&self->kalf);
+
+ return fusion_engine_signal_updated_location(self, t);
+}
+
+/** Filter input: Enter the raw position measurement */
+bool fusion_engine_position_3d_event(FusionEngine* self, double time,
+ double latitude, double longitude, double altitude,
+ double hor_accuracy, double ver_accuracy, double speed, double sigma_of_speed,
+ double direction, double climb, double sigma_of_climb)
+{
+ double t = time_offset_correct_time(&self->timestamp_offset, time);
+ LOG_FUSED_DEV(DBG_LOW, FUNCTION_PREFIX("(t=" TIME_FORMAT ", pos=(%g, %g, %g), dir=%g, spd=%g, climb=%g, acc=%g, sv=%gm/s)"),
+ time, latitude, longitude, altitude, direction, speed, climb, hor_accuracy, sigma_of_speed);
+
+ Vector_3d pm; /* p-measurement */
+ Vector_3d vm; /* v-measurement */
+ Vector_3d s2pm; /* p-measurement variance */
+ Vector_3d s2vm; /* v-measurement variance */
+
+ fusion_engine_spherical_to_tangent3d(self, latitude, longitude, altitude,
+ hor_accuracy, ver_accuracy, speed, sigma_of_speed,
+ direction, climb, sigma_of_climb, pm, vm, s2pm, s2vm);
+
+#if (FL_KALMAN_ANGULAR_VELOCITY)
+ double vhe = vector_2d_length(location_filter_get_velocity(&self->kalf));
+ double azimuth = conversions_degrees_to_radians(direction);
+ orientation_new_azimuth(&self->orientation, t, azimuth, vhe, speed);
+ location_filter_set_z_rot_speed(&self->kalf, orientation_get_z_rot_speed(&self->orientation));
+ LOG_FUSED_DEV(DBG_LOW, FUNCTION_PREFIX("GYROO(), t=%.16e, wze=%.16e"), t, orientation_get_z_rot_speed(&self->orientation));
+#endif
+
+ location_filter_kalman_pv(&self->kalf, t, pm, vm, s2pm, s2vm);
+ self->was_position = true;
+ tangent_frame_create(&self->__te, location_filter_get_position(&self->kalf), location_filter_get_velocity(&self->kalf));
+ location_filter_update_state(&self->kalf);
+
+ return fusion_engine_signal_updated_location(self, t);
+}
+
+void fusion_engine_get_wgs_location(FusionEngine* self, WgsLocation* location)
+{
+ LOG_FUSED_DEV(DBG_LOW, FUNCTION_PREFIX("WGS I(), t=%.16e, sp0=%.16e, sp1=%.16e, sp2=%.16e, se0=%.16e, se1=%.16e, se2=%.16e"),
+ self->last_notification_time, self->kalf.prior.x.p[0], self->kalf.prior.x.p[1], self->kalf.prior.x.p[2],
+ self->kalf.posterior.x.p[0], self->kalf.posterior.x.p[1], self->kalf.posterior.x.p[2]);
+
+ if (location == NULL) {
+ LOG_FUSED_DEV(DBG_ERR, FUNCTION_PREFIX("(), NULL pointer"));
+ return;
+ }
+ LOG_FUSED_DEV(DBG_LOW, FUNCTION_PREFIX("(), last_notification_time=%gs"), self->last_notification_time);
+
+ const const_Vector_3d_ref pos = location_filter_get_predicted_position(&self->kalf);
+ location->time = self->last_notification_time;
+
+ tangent_frame_tangent_to_spherical(&self->__te, pos, &location->latitude, &location->longitude, &location->altitude);
+ LOG_FUSED_DEV(DBG_LOW, FUNCTION_PREFIX("POS (), t=%.16e, p0=%.16e, p1=%.16e, p2=%.16e => lat=%.16e, lon=%.16e, alt=%.16e"),
+ location->time, pos[0], pos[1], pos[2], location->latitude, location->longitude, location->altitude);
+
+ const const_Vector_3d_ref vel = location_filter_get_predicted_velocity(&self->kalf);
+ tangent_frame_tangent_to_spherical_speed(&self->__te, pos, vel, &location->speed, &location->direction, &location->climb);
+ LOG_FUSED_DEV(DBG_LOW, FUNCTION_PREFIX("SPD (), t=%.16e, v0=%.16e, v1=%.16e, v2=%.16e, speed=%.16e, dir=%.16e, climb=%.16e"),
+ location->time, vel[0], vel[1], vel[2], location->speed, location->direction, location->climb);
+
+ const const_fl_kalf_sigma_cube_ref s2x = location_filter_get_predicted_covariance(&self->kalf);
+ location->hor_dev = sqrt((s2x[X][POS][POS] + s2x[Y][POS][POS]) * 0.5);
+ location->ver_dev = s2x[Z][POS][POS];
+ location->speed_dev = sqrt((s2x[X][VEL][VEL] + s2x[Y][VEL][VEL]) * 0.5);
+ location->climb_dev = s2x[Z][VEL][VEL];
+
+ LOG_FUSED_DEV(DBG_LOW, FUNCTION_PREFIX("WGS O(), t=%.16e, sp0=%.16e, sp1=%.16e, sp2=%.16e, se0=%.16e, se1=%.16e, se2=%.16e"),
+ self->last_notification_time, self->kalf.prior.x.p[0], self->kalf.prior.x.p[1], self->kalf.prior.x.p[2],
+ self->kalf.posterior.x.p[0], self->kalf.posterior.x.p[1], self->kalf.posterior.x.p[2]);
+}
+
+bool fusion_engine_speed_event(FusionEngine* self, double time, double speed, double sigma_of_speed)
+{
+ LOG_FUSED_DEV(DBG_LOW, FUNCTION_PREFIX("SPEED(), t=%.16e, speed=%.16e, sigma=%.16e"), time, speed, sigma_of_speed);
+ location_filter_update_speed(&self->kalf, time, speed, sigma_of_speed);
+
+ tangent_frame_create(&self->__te, location_filter_get_position(&self->kalf), location_filter_get_velocity(&self->kalf));
+ location_filter_update_state(&self->kalf);
+ return fusion_engine_signal_updated_location(self, time);
+}
+
+bool fusion_engine_acc_event(FusionEngine* self, double time, double x, double y, double z)
+{
+ LOG_FUSED_DEV(DBG_LOW, FUNCTION_PREFIX("ACC I(), t=%.16e, sp0=%.16e, sp1=%.16e, sp2=%.16e, se0=%.16e, se1=%.16e, se2=%.16e"),
+ time, self->kalf.prior.x.p[0], self->kalf.prior.x.p[1], self->kalf.prior.x.p[2],
+ self->kalf.posterior.x.p[0], self->kalf.posterior.x.p[1], self->kalf.posterior.x.p[2]);
+ time_offset_reference_time(&self->timestamp_offset, time);
+
+ LOG_FUSED_DEV(DBG_LOW, FUNCTION_PREFIX("(t=" TIME_FORMAT ", RAC, a=(%g, %g, %g)[m/s^2])"), time, x, y, z);
+ /* float, math system -> double, geographic system, rotated into local (tangent) frame */
+ Vector_3d acc = { x, y, z };
+ peace_detector_new_data(&self->acc_peace_detector, time, acc);
+ orientation_new_acceleration(&self->orientation, time, acc);
+ /* process linear acceleration */
+ location_filter_clear_acc(&self->kalf, time);
+
+ LOG_FUSED_DEV(DBG_LOW, FUNCTION_PREFIX("ACC O(), t=%.16e, sp0=%.16e, sp1=%.16e, sp2=%.16e, se0=%.16e, se1=%.16e, se2=%.16e"),
+ time, self->kalf.prior.x.p[0], self->kalf.prior.x.p[1], self->kalf.prior.x.p[2],
+ self->kalf.posterior.x.p[0], self->kalf.posterior.x.p[1], self->kalf.posterior.x.p[2]);
+
+ return fusion_engine_signal_updated_location(self, time);
+}
+
+bool fusion_engine_gyro_event(FusionEngine* self, double time, double x, double y, double z)
+{
+ LOG_FUSED_DEV(DBG_LOW, FUNCTION_PREFIX("GYROI(), t=%.16e, sp0=%.16e, sp1=%.16e, sp2=%.16e, se0=%.16e, se1=%.16e, se2=%.16e"),
+ time, self->kalf.prior.x.p[0], self->kalf.prior.x.p[1], self->kalf.prior.x.p[2],
+ self->kalf.posterior.x.p[0], self->kalf.posterior.x.p[1], self->kalf.posterior.x.p[2]);
+
+ time_offset_reference_time(&self->timestamp_offset, time);
+ const Vector_3d angular_velocity = { -x, -y, -z };
+ peace_detector_new_data(&self->gyro_peace_detector, time, angular_velocity);
+ const_Vector_3d_ref av = angular_velocity;
+ if (self->enable_static_calibration) {
+ av = calibration_filter_filter(&self->calibration_filter, time, av,
+ peace_detector_is_stable(&self->gyro_peace_detector) &&
+ peace_detector_is_stable(&self->acc_peace_detector));
+ }
+
+ if (self->enable_gyro_filter)
+ av = gyroscope_filter_process(&self->gyro_bias, time, av);
+
+ if (orientation_new_angular_velocity(&self->orientation, time, av, DEFAULT_ROTATION_SIGMA2)) {
+ location_filter_set_z_rot_speed(&self->kalf, orientation_get_z_rot_speed(&self->orientation));
+ location_filter_update_acc(&self->kalf, time);
+ }
+ LOG_FUSED_DEV(DBG_LOW, FUNCTION_PREFIX("GYROO(), t=%.16e, sp0=%.16e, sp1=%.16e, sp2=%.16e, se0=%.16e, se1=%.16e, se2=%.16e"),
+ time, self->kalf.prior.x.p[0], self->kalf.prior.x.p[1], self->kalf.prior.x.p[2],
+ self->kalf.posterior.x.p[0], self->kalf.posterior.x.p[1], self->kalf.posterior.x.p[2]);
+
+ return false;
+}
+
+/**
+ * For ips_devel test
+ */
+void fusion_engine_get_orientation(FusionEngine* self, double* o)
+{
+ orientation_get_orientation(&self->orientation, o);
+}
+
+/**
+ * For ips_devel test
+ */
+void fusion_engine_set_enable_gyro_filter(FusionEngine* self, bool enable)
+{
+ self->enable_gyro_filter = enable;
+}
+
+/**
+ * For ips_devel test
+ */
+void fusion_engine_set_enable_static_calibration(FusionEngine* self, bool enable)
+{
+ self->enable_static_calibration = enable;
+}
+
+/**
+ * For ips_devel test
+ */
+void fusion_engine_set_enable_static_autocalibration(FusionEngine* self, bool enable)
+{
+ calibration_filter_set_enable_autocalibration(&(self->calibration_filter), enable);
+}
+
+void fusion_engine_clear_calibration(FusionEngine* self)
+{
+ calibration_filter_clear_calibration(&(self->calibration_filter));
+}
+
+char* fusion_engine_get_calibration(FusionEngine* self)
+{
+ return calibration_filter_get_calibration(&(self->calibration_filter));
+}
+
+void fusion_engine_set_calibration(FusionEngine* self, const char* params)
+{
+ calibration_filter_set_calibration(&(self->calibration_filter), params);
+}
diff --git a/lbs-server/src/fused/FusionEngine.h b/lbs-server/src/fused/FusionEngine.h
new file mode 100644
index 0000000..5968b8c
--- /dev/null
+++ b/lbs-server/src/fused/FusionEngine.h
@@ -0,0 +1,252 @@
+/*
+ * Copyright (c) 2016-2017 Samsung Electronics Co., Ltd.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/**
+ * @file FusionEngine.h
+ * @brief Implementation of a 4D (2D position + 2D velocity) Kalman filter
+ */
+
+#pragma once
+#ifndef __FUSION_ENGINE_H__
+#define __FUSION_ENGINE_H__
+
+#include <stdbool.h>
+#include "parameters.h"
+#include "MotionDetector.h"
+#include "Conversions.h"
+#include "Matrix.h"
+#include "TangentFrame.h"
+#include "LocationFilter.h"
+#include "AccelerometerFilter.h"
+#include "GyroscopeFilter.h"
+#include "TimeOffset.h"
+#include "Orientation.h"
+#include "PeaceDetector.h"
+#include "CalibrationFilter.h"
+
+#include <memory.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#define DEFAULT_ACCELERATION_SIGMA2 SQUARE(FL_ACCEL_NOISE_LEVEL) /* [(m/s^2)^2] */
+#define DEFAULT_ROTATION_SIGMA2 SQUARE(1.0 / 256) /* [(rad/s)^2] */
+
+/** position on the Earth surface in Cartesian coordinates */
+typedef double position_vector[GEO_DIMENSION];
+
+typedef struct {
+ double time;
+ position_vector position;
+} fl_prediction;
+
+typedef enum {
+ LOCATION_STATUS_NO_FIX = 0, /*/< No fix status. */
+ LOCATION_STATUS_2D_FIX, /*/< 2D fix status (latitude/longitude/speed/direction). */
+ LOCATION_STATUS_3D_FIX, /*/< 3D fix status (altitude/climb as well). */
+} LocationStatus;
+
+typedef struct {
+ bool engine_started;
+ bool was_position;
+ bool enable_gyro_filter;
+ bool enable_static_calibration;
+ double last_notification_time;
+ TimeOffset timestamp_offset;
+ TangentFrame __te; /* tangent frame map */
+ Orientation orientation;
+ LocationFilter kalf; /* Kalman filter */
+ LocationStatus fix_status; /* pass-through */
+ GyroscopeFilter gyro_bias;
+ PeaceDetector acc_peace_detector;
+ PeaceDetector gyro_peace_detector;
+ CalibrationFilter calibration_filter;
+} FusionEngine;
+
+typedef struct {
+ double time; /*/< Event time in [s]. */
+ double latitude; /*/< Latitude data in degrees. */
+ double longitude; /*/< Longitude data in degrees. */
+ double altitude; /*/< Altitude data in [m]. */
+ double speed; /*/< The speed over ground. (m/s) */
+ double direction; /*/< The course made in degrees relative to true north. The value is always in the range [0.0, 360.0] degree. */
+ double climb; /*/< The vertical speed. (m/s) */
+ double hor_dev; /**< std. dev. of horizontal position in [m] */
+ double ver_dev; /**< std. dev. of vertical position in [m] */
+ double speed_dev; /**< std. dev. of horizontal velocity in [m/s] */
+ double climb_dev; /**< std. dev. of vertical velocity in [m/s] */
+} WgsLocation;
+
+/**
+ * @brief Initialization of the engine.
+ * @param[in] on_motion_state_changed
+ * Callback to be invoked when the detected state of motion changes. This argument is optional, and can be NULL.
+ */
+extern void fusion_engine_init(FusionEngine* self,
+ MotionDetectorStateChangeListener on_motion_state_changed, double utcOffset);
+
+/** Cleanup of the singleton unit (static destructor). This corresponds to service unload. */
+extern void fusion_engine_exit(FusionEngine* self);
+
+/**
+ * @brief Start processing the location and sensory inputs, and sending back notifications.
+ * This corresponds to service start.
+ */
+extern void fusion_engine_start(FusionEngine* self);
+
+/**
+ * @brief Stops processing the inputs, and sending notifications. This corresponds to service stop.
+ */
+extern void fusion_engine_stop(FusionEngine* self);
+
+/**
+ * @brief Reset of the internal state back to the initial one. The handlers and start/stop state are not changed.
+ * This function is used for initialization, soft restarts, and repetitive testing.
+ */
+extern void fusion_engine_reset(FusionEngine* self);
+
+/*******************************************************************************
+ * Process 2D (horizontal) position without change of the altitude or speed (WPS).
+ * Kalman merging is carried out in tangent frame, and the origin is moved
+ * after each measurement so that the predictions always start at (0, 0, R + h).
+ * - Clocks synchronization
+ * - Kalman data blending
+ * - Repositioning of the tangent frame
+ *
+ * @param[in] time Internal time.
+ * @param[in] latitude Latitude in degrees.
+ * @param[in] longitude Longitude in degrees.
+ * @param[in] sigma Horizontal standard deviation of provided position in [m].
+ */
+extern bool fusion_engine_position_2d_event(FusionEngine* self, double time, double latitude, double longitude, double sigma);
+
+/*******************************************************************************
+ * Process 3D position along with 3D velocity (GPS).
+ * Kalman merging is carried out in tangent frame, and the origin is moved after each measurement
+ * so that the predictions always start at (0, 0, R + h).
+ * - Clocks synchronization
+ * - Kalman data blending
+ * - Repositioning of the tangent frame
+ * If FL_KALMAN_ANGULAR_VELOCITY is enabled the angular velocity is derived
+ * from consecutive measurements of linear velocities and merged with gyroscope readings.
+ *
+ * @param[in] time Internal time.
+ * @param[in] latitude Latitude in degrees.
+ * @param[in] longitude Longitude in degrees.
+ * @param[in] altitude Altitude in [m].
+ * @param[in] hor_accuracy Horizontal standard deviation of provided position in [m].
+ * @param[in] ver_accuracy Standard deviation of altitude in [m].
+ * @param[in] speed Horizontal speed in [m/s].
+ * @param[in] sigma_of_speed Standard deviation of horizontal speed in [m/s].
+ * @param[in] direction Direction (azimuth) of movement in degrees.
+ * @param[in] climb Vertical speed in [m/s].
+ * @param[in] sigma_of_climb Standard deviation of vertical speed in [m/s].
+ */
+extern bool fusion_engine_position_3d_event(FusionEngine* self, double time,
+ double latitude, double longitude, double altitude,
+ double hor_accuracy, double ver_accuracy,
+ double speed, double sigma_of_speed, double direction, double climb, double sigma_of_climb);
+
+/**
+ * @brief Process speed measurement events.
+ * @param[in] time Internal time.
+ * @param[in] speed Horizontal speed in [m/s].
+ * @param[in] sigma_of_speed Standard deviation of horizontal speed in [m/s].
+ */
+extern bool fusion_engine_speed_event(FusionEngine* self, double time, double speed, double sigma_of_speed);
+
+/**
+ * @brief Process accelerometer events. The time offset is corrected if necessary.
+ * - Clocks synchronization
+ * - Device-to-tangent frame orientation
+ * - Prediction
+ * @param[in] time Sensory time-stamp
+ * @param[in] x X coordinate of acceleration.
+ * @param[in] y Y coordinate of acceleration.
+ * @param[in] z Z coordinate of acceleration.
+ */
+extern bool fusion_engine_acc_event(FusionEngine* self, double time, double x, double y, double z);
+
+/**
+ * @brief Process gyroscope events. The time offset is corrected if necessary.
+ * - Clocks synchronization
+ * - Device-to-tangent frame orientation
+ * - Prediction
+ * @param[in] time Sensory time-stamp
+ * @param[in] x X coordinate of angular rotation velocity vector.
+ * @param[in] y Y coordinate of angular rotation velocity vector.
+ * @param[in] z Z coordinate of angular rotation velocity vector.
+ */
+extern bool fusion_engine_gyro_event(FusionEngine* self, double time, double x, double y, double z);
+
+/**
+ * @brief Retrieve the last processed location, typically during the on_location_changed notification.
+ * @param[out] location Current WGS location
+ */
+extern void fusion_engine_get_wgs_location(FusionEngine* self, WgsLocation* location);
+
+/**
+ * For ips_devel test
+ * @brief Retrieve current orientation.
+ * @param[out] o Orientation represented as a vector part of rotation quaternion.
+ * Must be length of minimum 3.
+ */
+extern void fusion_engine_get_orientation(FusionEngine* self, double* o);
+
+/**
+ * For ips_devel test
+ * @brief Enable gyroscope calibration filter.
+ * @param[in] enable True to enable calibration filter, false to disable it.
+ */
+extern void fusion_engine_set_enable_gyro_filter(FusionEngine* self, bool enable);
+
+/**
+ * For ips_devel test
+ * @brief Enable gyroscope static calibration.
+ * @param[in] enable True to enable static calibration, false to disable it.
+ */
+extern void fusion_engine_set_enable_static_calibration(FusionEngine* self, bool enable);
+
+/**
+ * For ips_devel test
+ * @brief Enable gyroscope static autocalibration.
+ * @param[in] enable True to enable static autocalibration, false to disable it.
+ */
+extern void fusion_engine_set_enable_static_autocalibration(FusionEngine* self, bool enable);
+
+/**
+ * @brief Clears gyroscope calibration parameters.
+ */
+extern void fusion_engine_clear_calibration(FusionEngine* self);
+
+/**
+ * @brief Retrieve current gyroscope calibration parameters.
+ * @return Gyroscope calibration parameters. Must be disposed to prevent memory leak.
+ */
+extern char* fusion_engine_get_calibration(FusionEngine* self);
+
+/**
+ * @brief Sets new gyroscope calibration parameters.
+ * @param[in] params New gyroscope calibration parameters.
+ */
+extern void fusion_engine_set_calibration(FusionEngine* self, const char* params);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* __FUSION_ENGINE_H__ */
diff --git a/lbs-server/src/fused/GravityEstimator.h b/lbs-server/src/fused/GravityEstimator.h
new file mode 100644
index 0000000..101d6f3
--- /dev/null
+++ b/lbs-server/src/fused/GravityEstimator.h
@@ -0,0 +1,73 @@
+/*
+ * Copyright (c) 2016-2017 Samsung Electronics Co., Ltd.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/**
+ * @file GravityEstimator.h
+ * @brief Estimator of the gravity value used for accelerometer scale calibration.
+ */
+
+#pragma once
+#ifndef __GRAVITY_ESTIMATOR_H__
+#define __GRAVITY_ESTIMATOR_H__
+
+#include "MovingAverageFilters.h"
+#include "parameters.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+
+typedef MovingAverage1dFilter GravityEstimator;
+
+#define gravity_estimator_init(self) moving_average_1d_filter_init(self, (1 << FL_GRES_AEMA_PLATEAU_BITS))
+
+#define gravity_estimator_exit(self) moving_average_1d_filter_exit(self)
+
+/**
+ * @brief Bring the internal state back to initial one. This function is used for repetitive testing and module soft restarts.
+ */
+#define gravity_estimator_reset(self) moving_average_1d_filter_reset(self)
+
+/**
+ * @brief Processing of a single data sample.
+ * @param[in] g2 Modulus squared of the measured acceleration projected onto z-axis.
+ * @return[double] Estimated value of the Earth gravity.
+ */
+#define gravity_estimator_process(self, g2) moving_average_1d_filter_process(self, g2)
+
+/**
+ * @brief Bring the filter to a stationary state (EMA mode, input = output). This method is used for testing.
+ * @param[in] g2 Modulus squared value of Earth gravity measured by the accelerometer.
+ */
+#define gravity_estimator_set_estimate(self, g2) moving_average_1d_filter_set_estimate(self, g2)
+
+/**
+ * @brief Fetch the current gravity estimate.
+ * @param[in] self Object pointer
+ * @return[double] Output value after last processed sample.
+ */
+
+/** Retrieve current estimate */
+#define gravity_estimator_get_estimate(self) moving_average_1d_filter_get_estimate(self)
+
+#define gravity_estimator_get_update_rate(self) moving_average_get_update_rate(&(self)->base)
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* __GRAVITY_ESTIMATOR_H__ */
diff --git a/lbs-server/src/fused/GyroscopeFilter.c b/lbs-server/src/fused/GyroscopeFilter.c
new file mode 100644
index 0000000..376f9d4
--- /dev/null
+++ b/lbs-server/src/fused/GyroscopeFilter.c
@@ -0,0 +1,56 @@
+/*
+ * Copyright (c) 2016-2017 Samsung Electronics Co., Ltd.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#if !defined(LOG_THRESHOLD)
+ /* custom logging threshold - keep undefined on the repo */
+ /* #define LOG_THRESHOLD LOG_LEVEL_TRACE */
+#endif
+
+#include "GyroscopeFilter.h"
+#include "GravityEstimator.h"
+#include "math-ext.h"
+#include "debug_util.h"
+
+#define _USE_MATH_DEFINES
+#include <math.h>
+
+#define GYRO_PLATEAU_EVIDENCE (1 << FL_GYRO_AEMA_PLATEAU_BITS)
+#define GYRO_SCALE2_I (1.0 / (2 * SQUARE(FL_GYRO_SPIN_SIGMA_I)))
+#define GYRO_SCALE2_F (1.0 / (2 * SQUARE(FL_GYRO_SPIN_SIGMA_F)))
+
+void gyroscope_filter_init(GyroscopeFilter* self)
+{
+ LOG_FUSED_DEV(DBG_LOW, INDENT("()"));
+ gyroscope_filter_reset(self);
+ LOG_FUSED_DEV(DBG_LOW, UNINDENT("()"));
+}
+
+void gyroscope_filter_exit(GyroscopeFilter* self)
+{
+ LOG_FUSED_DEV(DBG_LOW, FUNCTION_PREFIX("()"));
+}
+
+void gyroscope_filter_reset(GyroscopeFilter* self)
+{
+ LOG_FUSED_DEV(DBG_LOW, FUNCTION_PREFIX("()"));
+ waema3d_estimator_reset(self, (SQUARE(FL_GYRO_SPIN_SIGMA_I / FL_GYRO_SPIN_SIGMA_F) - 1) / SQUARE(GYRO_PLATEAU_EVIDENCE), GYRO_PLATEAU_EVIDENCE, GYRO_SCALE2_I);
+}
+
+const_Vector_3d_ref gyroscope_filter_process(GyroscopeFilter* self, double time, const_Vector_3d_ref wm)
+{
+ LOG_FUSED_DEV(DBG_LOW, FUNCTION_PREFIX("(t=%6.2fs)"), time);
+ return waema3d_estimator_filter(self, wm);
+}
diff --git a/lbs-server/src/fused/GyroscopeFilter.h b/lbs-server/src/fused/GyroscopeFilter.h
new file mode 100644
index 0000000..86fbf2d
--- /dev/null
+++ b/lbs-server/src/fused/GyroscopeFilter.h
@@ -0,0 +1,61 @@
+/*
+ * Copyright (c) 2016-2017 Samsung Electronics Co., Ltd.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/**
+ * @file GyroscopeFilter.h
+ * @brief Correction of the gyroscope bias.
+ */
+
+#pragma once
+#ifndef __GYROSCOPE_FILTER_H__
+#define __GYROSCOPE_FILTER_H__
+
+#include "Waema3dEstimator.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+
+typedef Waema3dEstimator GyroscopeFilter;
+
+
+void gyroscope_filter_init(GyroscopeFilter* self);
+void gyroscope_filter_exit(GyroscopeFilter* self);
+void gyroscope_filter_reset(GyroscopeFilter* self);
+
+/**
+ * @brief [TEST] Bring the filter to a stationary state (EMA mode, input = output).
+ * @param[in] w0 3D vector of angular velocity measured by the gyroscope in inertial coordinate frame.
+ */
+void gyroscope_filter_set(GyroscopeFilter* self, const_Vector_3d_ref w0);
+
+/** Processing of a vector data sample. This method performs:
+ * - estimation of the gyroscope bias with the weighted AEMA filter;
+ * the individual samples are weighted by the distance from average using Gaussian
+ * curve with monotonically-decreasing sigma parameter;
+ * - correction of the measurement by the estimated bias.
+ * @param[in] time Event time in seconds.
+ * @param[in] wm 3D vector of measured angular velocity.
+ * @return 3D vector of bias-corrected angular velocity.
+ */
+const_Vector_3d_ref gyroscope_filter_process(GyroscopeFilter* self, double time, const_Vector_3d_ref wm);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* __GYROSCOPE_FILTER_H__ */
diff --git a/lbs-server/src/fused/LocationFilter.c b/lbs-server/src/fused/LocationFilter.c
new file mode 100644
index 0000000..07c3057
--- /dev/null
+++ b/lbs-server/src/fused/LocationFilter.c
@@ -0,0 +1,479 @@
+/*
+ * Copyright (c) 2016-2017 Samsung Electronics Co., Ltd.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "LocationFilter.h"
+#include "Matrix.h"
+#include "Conversions.h"
+#include "debug_util.h"
+
+#include <memory.h>
+
+#define TIME_FORMAT "%.9fs"
+
+#define NEAR_ZERO 1.0e-200
+
+static bool location_filter_update_heading(_fl_kalf_state_m* self)
+{
+ const double vx = self->v[X];
+ const double vy = self->v[Y];
+ if (fabs(vx) > 0.001 || fabs(vy) > 0.001) {
+ self->heading = atan2(vy, vx);
+ return true;
+ }
+ return false;
+}
+
+void location_filter_init(LocationFilter* self, double x, double y, double z, double var_p, double var_v, double var_a)
+{
+ self->default_var_a = var_a;
+ self->last_correction_time = FL_UNDEFINED_TIME;
+ memset(&self->prior, 0, sizeof(self->prior));
+ memset(&self->posterior, 0, sizeof(self->posterior));
+ self->z_rot_speed = 0;
+ self->prior.x.p[X] = self->posterior.x.p[X] = x;
+ self->prior.x.p[Y] = self->posterior.x.p[Y] = y;
+ self->prior.x.p[Z] = self->posterior.x.p[Z] = z;
+ unsigned i;
+ for (i = GEO_DIMENSION; i;) {
+ --i;
+ /* diagonal terms */
+ self->prior.s2x[i][POS][POS] = self->posterior.s2x[i][POS][POS] = var_p;
+ self->prior.s2x[i][VEL][VEL] = self->posterior.s2x[i][VEL][VEL] = var_v;
+ self->prior.s2x[i][ACC][ACC] = self->posterior.s2x[i][ACC][ACC] = var_a;
+ }
+ location_filter_update_heading(&self->posterior.x);
+ self->prior.x.heading = self->posterior.x.heading;
+ LOG_FUSED_DEV(DBG_INFO, FUNCTION_PREFIX("(p0=%g, p1=%g, p2=%g)"), self->prior.x.p[0], self->prior.x.p[1], self->prior.x.p[2]);
+}
+
+/**
+ * @brief Check for and correct the numerical errors which cause the covariance matrix
+ * to cease being positive-definite. This operation has to be performed regularly,
+ * or otherwise magic ensues, and every strange result becomes possible.
+ * @param[in, out] src The state object to be rectified.
+ * This is either the predicted (prior), or estimated (posterior) one.
+ */
+static void __rectify_state(_fl_kalf_sigma_cube s2x)
+{
+ unsigned i;
+ for (i = GEO_DIMENSION; i--;) {
+ Matrix_3d_ref s2xi = s2x[i];
+ /* diagonal elements */
+#define CHECK(u) \
+ if (s2xi[u][u] < 0) { \
+ /*LOG_FUSED_DEV(DBG_LOW, FUNCTION_PREFIX("(src=%p): src.s2x[%u][" #u "][" #u "] = %g < 0"), src, i, s2xi[u][u]);*/ \
+ s2xi[u][u] = 0; \
+ }
+ CHECK(POS);
+ CHECK(VEL);
+ CHECK(ACC);
+#undef CHECK
+
+ /* partial determinants */
+ double uuvv, uvvu;
+#define CHECK(u, v) \
+ uuvv = s2xi[u][u] * s2xi[v][v]; \
+ uvvu = s2xi[u][v] * s2xi[v][u]; \
+ if (uuvv < uvvu) { \
+ /*LOG_FUSED_DEV(DBG_LOW, FUNCTION_PREFIX("(src=%p): det(src.s2x[%u], " #u ", " #v ") = %g < 0"), src, i, uuvv - uvvu);*/ \
+ s2xi[u][v] = \
+ s2xi[v][u] *= sqrt(uuvv / uvvu); \
+ }
+ CHECK(POS, VEL);
+ CHECK(POS, ACC);
+ CHECK(ACC, VEL);
+#undef CHECK
+ }
+}
+
+/**
+ * @brief Copy of the state (3x3D position & covariance) object.
+ * @param[out] dst Destination state object
+ * @param[out] src Source state object
+ */
+static void __copy_state(_fl_kalf_state *dst, _fl_kalf_state *src)
+{
+ __rectify_state(src->s2x);
+ memcpy(dst, src, sizeof(*dst));
+}
+
+void location_filter_update_state(LocationFilter* self)
+{
+ memcpy(&self->prior.x, &self->posterior.x, sizeof(self->prior.x));
+}
+
+/**
+ * @brief Calculates prior from posterior, i.e. extrapolates position,
+ * velocity, and the covariance-matrices up to the given time.
+ * We assume rotation is at constant angular rate (z_rot_speed) and speed (norm of velocity) is also constant.
+ * Therefore motion equations are:
+ * Vx = vx cos(wt) - vy sin(wt)
+ * Vy = vy cos(wt) + vx sin(wt)
+ * Vz = vz
+ * X = x + vx sin(wt)/w + vy(cos(wt) - 1)/w (by integrating)
+ * Y = y + vy sin(wt)/w - vx(cos(wt) - 1)/w
+ * Z = z + vz t
+ * Ax = -vy w (differentiating at t=0)
+ * Ay = vx w
+ * Az = 0
+ * And transition matrix is:
+ * | 1 0 0 sin(wt)/w (cos(wt) - 1)/w 0 0 0 0|
+ * | 0 1 0 -(cos(wt) - 1)/w sin(wt)/w 0 0 0 0|
+ * | 0 0 1 0 0 t 0 0 0|
+ * | 0 0 0 cos(wt) -sin(wt) 0 0 0 0|
+ * | 0 0 0 sin(wt) -cos(wt) 0 0 0 0|
+ * | 0 0 0 0 0 1 0 0 0|
+ * | 0 0 0 0 -w 0 0 0 0|
+ * | 0 0 0 w 0 0 0 0 0|
+ * | 0 0 0 0 0 0 0 0 0|
+ * so for small wt:
+ * | 1 0 0 t wt^2/2 0 0 0 0|
+ * | 0 1 0 -wt^2/2 t 0 0 0 0|
+ * | 0 0 1 0 0 t 0 0 0|
+ * | 0 0 0 1 - w^2t^2/2 -t 0 0 0 0|
+ * | 0 0 0 t -1 + w^2t^2/2 0 0 0 0|
+ * | 0 0 0 0 0 1 0 0 0|
+ * | 0 0 0 0 -w 0 0 0 0|
+ * | 0 0 0 w 0 0 0 0 0|
+ * | 0 0 0 0 0 0 0 0 0|
+ * We assume covariance matrix can be represented as
+ * 3 independent covariance matrixes, one for each coordinate,
+ * and assume transition matrix used for covariance update
+ * for each coordinate is:
+ * | 1 t t^2/2 |
+ * | 0 1 t |
+ * | 0 0 1 |
+ * Process noise matrix ???
+ *
+ * @param[in] time Internal time
+ */
+static void location_filter_predict(LocationFilter* self, double time)
+{
+ if (!KNOWN_TIME(self->last_correction_time)) {
+ /* no prior data */
+ return;
+ }
+ double dt = time - self->last_correction_time;
+ LOG_FUSED_DEV(DBG_LOW, FUNCTION_PREFIX("(t=" TIME_FORMAT "), dt=" TIME_FORMAT), time, dt);
+ if (dt <= 0) {
+ return;
+ }
+ const _fl_kalf_state_m* post = &self->posterior.x;
+ _fl_kalf_state_m* prior = &self->prior.x;
+ double z_rot_speed = self->z_rot_speed;
+ double rot = z_rot_speed * dt;
+ if (fabs(rot) > NEAR_ZERO) {
+ double cos_rot = cos(rot);
+ double sin_rot = sin(rot);
+ double inv_z_rot_speed = 1.0 / z_rot_speed;
+ double cos_rot_inv = (cos_rot - 1) * inv_z_rot_speed;
+ double sin_rot_inv = sin_rot * inv_z_rot_speed;
+
+ prior->p[X] = post->p[X] + post->v[X] * sin_rot_inv + post->v[Y] * cos_rot_inv;
+ prior->p[Y] = post->p[Y] + post->v[Y] * sin_rot_inv - post->v[X] * cos_rot_inv;
+ prior->v[X] = post->v[X] * cos_rot - post->v[Y] * sin_rot;
+ prior->v[Y] = post->v[Y] * cos_rot + post->v[X] * sin_rot;
+ if (!location_filter_update_heading(prior)) {
+ prior->heading = post->heading + rot;
+ }
+ } else {
+ prior->p[X] = post->p[X] + dt * post->v[X];
+ prior->p[Y] = post->p[Y] + dt * post->v[Y];
+ prior->v[X] = post->v[X];
+ prior->v[Y] = post->v[Y];
+ prior->heading = post->heading;
+ }
+ prior->p[Z] = post->p[Z] + dt * post->v[Z];
+ prior->v[Z] = post->v[Z];
+
+ prior->a[X] = -prior->v[Y] * z_rot_speed;
+ prior->a[Y] = prior->v[X] * z_rot_speed;
+ prior->a[Z] = 0;
+
+ const_fl_kalf_sigma_cube_ref post_s = self->posterior.s2x;
+ _fl_kalf_sigma_cube_ref prior_s = self->prior.s2x;
+ unsigned i;
+ for (i = GEO_DIMENSION; i;) {
+ --i;
+
+ const_Matrix_3d_ref post_si = post_s[i];
+ Matrix_3d_ref prior_si = prior_s[i];
+
+ prior_si[ACC][ACC] = post_si[ACC][ACC];
+ prior_si[ACC][VEL] = post_si[ACC][VEL] + post_si[ACC][ACC] * dt;
+ prior_si[ACC][POS] = post_si[ACC][POS]
+ + (post_si[ACC][VEL] + post_si[ACC][ACC] * dt / 2) * dt;
+
+ prior_si[VEL][ACC] = prior_si[ACC][VEL]; /* by symmetry */
+ prior_si[VEL][VEL] = post_si[VEL][VEL]
+ + (post_si[VEL][ACC] * 2 + post_si[ACC][ACC] * dt) * dt;
+ prior_si[VEL][POS] = post_si[VEL][POS]
+ + (post_si[VEL][VEL] + post_si[POS][ACC]
+ + (post_si[VEL][ACC] * 3
+ + post_si[ACC][ACC] * dt) / 2 * dt) * dt;
+
+ prior_si[POS][ACC] = prior_si[ACC][POS]; /* by symmetry */
+ prior_si[POS][VEL] = prior_si[VEL][POS]; /* by symmetry */
+ prior_si[POS][POS] = post_si[POS][POS]
+ + (post_si[POS][VEL] * 2
+ + (post_si[VEL][VEL] + post_si[ACC][POS]
+ + (post_si[ACC][VEL]
+ + post_si[ACC][ACC] * dt / 4) * dt)
+ * dt) * dt;
+ }
+ /* process noise */
+ prior_s[X][ACC][ACC] += fl_square(prior->a[X] - post->a[X]);
+ prior_s[Y][ACC][ACC] += fl_square(prior->a[Y] - post->a[Y]);
+ __rectify_state(prior_s);
+ LOG_FUSED_DEV(DBG_INFO, FUNCTION_PREFIX("(p0=%g, p1=%g, p2=%g)"), self->prior.x.p[0], self->prior.x.p[1], self->prior.x.p[2]);
+}
+
+/**
+ * @brief Update of the 2D position
+ * Observation matrix for each coordinate is:
+ * | 1 0 0|
+ *
+ * @param[in] time Internal time
+ * @param[in] pos Measured position
+ * @param[in] pos_ac Measured position deviation (squared standard deviation)
+ */
+void location_filter_kalman_p(LocationFilter* self, double time, const_Vector_3d_ref pos, const_Vector_3d_ref pos_ac)
+{
+ LOG_FUSED_DEV(DBG_INFO, FUNCTION_PREFIX("(t=%g, x0=%g, x1=%g, x2=%g, s0=%g, s1=%g, s2=%g)"),
+ time, pos[0], pos[1], pos[2], pos_ac[0], pos_ac[1], pos_ac[2]);
+ LOG_FUSED_DEV(DBG_INFO, FUNCTION_PREFIX("input (t=%g, p0=%g, p1=%g, p2=%g, v0=%g, v1=%g, v2=%g)"),
+ time, self->prior.x.p[0], self->prior.x.p[1], self->prior.x.p[2], self->prior.x.v[0], self->prior.x.v[1], self->prior.x.v[2]);
+
+ location_filter_predict(self, time);
+ unsigned i;
+ /* @note WPS-specific: restrict the update to horizontal position */
+ for (i = PLANAR_DIMENSION; i;) {
+ --i;
+ const double s2mi = pos_ac[i];
+ Matrix_3d_ref post_si = self->posterior.s2x[i];
+ const_Matrix_3d_ref prior_si = self->prior.s2x[i];
+ const_Vector_3d_ref s2pis = prior_si[POS];
+ const double s2piss = s2pis[POS];
+ const double innovation_covariance = s2mi + s2piss;
+ if (innovation_covariance > 0) {
+ const double inv_inn_cov = 1.0 / innovation_covariance;
+ const double innovation = pos[i] - self->prior.x.p[i];
+
+ /* reduced Joseph form in special 3x1 case: subtract scaled projection */
+#define SET_FIELD(a, b) post_si[a][b] = inv_inn_cov * ((prior_si[a][b] * s2piss - s2pis[a] * s2pis[b]) + prior_si[a][b] * s2mi)
+#define CPY_FIELD(a, b) post_si[a][b] = post_si[b][a]
+ SET_FIELD(POS, POS);
+ SET_FIELD(POS, VEL);
+ SET_FIELD(POS, ACC);
+ CPY_FIELD(VEL, POS);
+ SET_FIELD(VEL, VEL);
+ SET_FIELD(VEL, ACC);
+ CPY_FIELD(ACC, POS);
+ CPY_FIELD(ACC, VEL);
+ SET_FIELD(ACC, ACC);
+#undef CPY_FIELD
+#undef SET_FIELD
+
+ self->posterior.x.p[i] = self->prior.x.p[i] + innovation * inv_inn_cov * s2pis[POS];
+ self->posterior.x.v[i] = self->prior.x.v[i] + innovation * inv_inn_cov * s2pis[VEL];
+ self->posterior.x.a[i] = self->prior.x.a[i] + innovation * inv_inn_cov * s2pis[ACC];
+ LOG_FUSED_DEV(DBG_INFO, FUNCTION_PREFIX("(i=%i, t=%g, prev=%g, dx=%g, inv_s2k=%g, s2pis[POS]=%g, cur=%g)"),
+ i, time, self->prior.x.p[i], innovation, inv_inn_cov, s2piss, self->posterior.x.p[i]);
+ }
+ }
+ /* the third coordinate is of `special care': we want to preserve it in the tangent frame, along with its sigma */
+ self->posterior.x.p[Z] = pos[Z];
+ location_filter_update_heading(&self->posterior.x);
+ __copy_state(&self->prior, &self->posterior);
+ self->last_correction_time = time;
+ LOG_FUSED_DEV(DBG_INFO, FUNCTION_PREFIX("(p0=%g, p1=%g, p2=%g)"), self->prior.x.p[0], self->prior.x.p[1], self->prior.x.p[2]);
+}
+
+/**
+ * @brief Update of the 3D position & velocity
+ * Observation matrix for each coordinate is:
+ * | 1 0 0|
+ * | 0 1 0|
+ *
+ * @param[in] time Internal time
+ * @param[in] pm Measured position
+ * @param[in] vm Measured velocity
+ * @param[in] s2pm Measured position deviation (squared standard deviation)
+ * @param[in] s2vm Measured velocity deviation (squared standard deviation)
+ */
+void location_filter_kalman_pv(LocationFilter* self, double time,
+ const_Vector_3d_ref pm, const_Vector_3d_ref vm, const_Vector_3d_ref s2pm, const_Vector_3d_ref s2vm)
+{
+ location_filter_predict(self, time);
+ unsigned i;
+ for (i = GEO_DIMENSION; i;) {
+ --i;
+
+ /* shortcuts */
+ const_Matrix_3d_ref prior_cov = self->prior.s2x[i];
+
+ /* Sk = Sp + Sm */
+ Matrix_2d innovation_covariance;
+ innovation_covariance[POS][POS] = prior_cov[POS][POS] + s2pm[i];
+ innovation_covariance[POS][VEL] = /* == prior_cov[POS][VEL] */
+ innovation_covariance[VEL][POS] = prior_cov[VEL][POS];
+ innovation_covariance[VEL][VEL] = prior_cov[VEL][VEL] + s2vm[i];
+
+ Matrix_2d inv_inn_cov;
+ if (matrix_2d_invert(innovation_covariance, inv_inn_cov)) {
+ Matrix_2d k0;
+ matrix_3d_multiply_matrix_2d(prior_cov, inv_inn_cov, k0); /* K = Sp.Si */
+ /* 1 - K = Sm.Si, use positive form */
+ Matrix_2d k1;
+ k1[POS][POS] = s2pm[i] * inv_inn_cov[POS][POS];
+ k1[POS][VEL] = s2pm[i] * inv_inn_cov[POS][VEL];
+ k1[VEL][POS] = s2vm[i] * inv_inn_cov[VEL][POS];
+ k1[VEL][VEL] = s2vm[i] * inv_inn_cov[VEL][VEL];
+ /* state update */
+ self->posterior.x.p[i] = k0[POS][POS] * pm[i]
+ + k0[POS][VEL] * vm[i]
+ + k1[POS][POS] * self->prior.x.p[i]
+ + k1[POS][VEL] * self->prior.x.v[i];
+
+ self->posterior.x.v[i] = k0[VEL][POS] * pm[i]
+ + k0[VEL][VEL] * vm[i]
+ + k1[VEL][POS] * self->prior.x.p[i]
+ + k1[VEL][VEL] * self->prior.x.v[i];
+
+ /* covariance update Se = K1,K1.Sp + K,K.Sm */
+ Matrix_3d_ref s2ei = self->posterior.s2x[i];
+
+#define SET_S2E(j, k) s2ei[j][k] = \
+ k1[j][POS] * k1[k][POS] * prior_cov[POS][POS] + k0[j][POS] * k0[k][POS] * s2pm[i] \
+ + k1[j][POS] * k1[k][VEL] * prior_cov[POS][VEL] \
+ + k1[j][VEL] * k1[k][POS] * prior_cov[VEL][POS] \
+ + k1[j][VEL] * k1[k][VEL] * prior_cov[VEL][VEL] + k0[j][VEL] * k0[k][VEL] * s2vm[i]
+
+ SET_S2E(POS, POS);
+ SET_S2E(POS, VEL);
+ SET_S2E(VEL, POS);
+ SET_S2E(VEL, VEL);
+#undef SET_S2E
+ }
+ /* upon arrival of new position-velocity pair (a maximally uncertain) the pair (p, v) decorrelates from a: */
+ self->posterior.s2x[i][POS][ACC] = 0;
+ self->posterior.s2x[i][VEL][ACC] = 0;
+ self->posterior.s2x[i][ACC][POS] = 0;
+ self->posterior.s2x[i][ACC][VEL] = 0;
+ self->posterior.s2x[i][ACC][ACC] = self->prior.s2x[i][ACC][ACC];
+ }
+ location_filter_update_heading(&self->posterior.x);
+ __copy_state(&self->prior, &self->posterior);
+ self->last_correction_time = time;
+
+ LOG_FUSED_DEV(DBG_INFO, FUNCTION_PREFIX("(p0=%g, p1=%g, p2=%g)"), self->prior.x.p[0], self->prior.x.p[1], self->prior.x.p[2]);
+}
+
+/**
+ * @brief Update of the 2D position using speed measurement
+ * Observation matrix is:
+ * | 0 vx/v 0 0 vy/v 0 0 0 0|
+ * Where v = sqrt(vx^2 + vy^2)
+ *
+ * @param[in] time Internal time
+ * @param[in] speed Measured horizontal speed
+ * @param[in] sigma_of_speed Measured horizontal speed deviation (squared standard deviation)
+ */
+void location_filter_update_speed(LocationFilter* self, double time, double speed, double sigma_of_speed)
+{
+ location_filter_predict(self, time);
+ const double vx = self->prior.x.v[X];
+ const double vy = self->prior.x.v[Y];
+ const double v = sqrt(vx * vx + vy * vy);
+ double ch;
+ double sh;
+ if (v != 0) {
+ ch = vx/v;
+ sh = vy/v;
+ } else {
+ const double heading = self->prior.x.heading;
+ ch = cos(heading);
+ sh = sin(heading);
+ }
+ const double ch2 = ch * ch;
+ const double sh2 = sh * sh;
+ const double innovation_covariance =
+ ch2 * self->prior.s2x[X][VEL][VEL] + sh2 * self->prior.s2x[Y][VEL][VEL] + SQUARE(sigma_of_speed);
+ const double innovation = speed - v;
+ /* Update state estimate */
+ const double vxk = ch * innovation / innovation_covariance;
+ self->posterior.x.p[X] = self->prior.x.p[X] + self->prior.s2x[X][POS][VEL] * vxk;
+ self->posterior.x.v[X] = self->prior.x.v[X] + self->prior.s2x[X][VEL][VEL] * vxk;
+ self->posterior.x.a[X] = self->prior.x.a[X] + self->prior.s2x[X][ACC][VEL] * vxk;
+
+ const double vyk = sh * innovation / innovation_covariance;
+ self->posterior.x.p[Y] = self->prior.x.p[Y] + self->prior.s2x[Y][POS][VEL] * vyk;
+ self->posterior.x.v[Y] = self->prior.x.v[Y] + self->prior.s2x[Y][VEL][VEL] * vyk;
+ self->posterior.x.a[Y] = self->prior.x.a[Y] + self->prior.s2x[Y][ACC][VEL] * vyk;
+
+ self->posterior.x.p[Z] = self->prior.x.p[Z];
+ self->posterior.x.v[Z] = self->prior.x.v[Z];
+ self->posterior.x.a[Z] = self->prior.x.a[Z];
+
+ /* Update covariance estimate */
+ Vector_2d vv;
+ vv[0] = ch2 / innovation_covariance;
+ vv[1] = sh2 / innovation_covariance;
+ unsigned i;
+ for (i = PLANAR_DIMENSION; i;) {
+ --i;
+ const_Matrix_3d_ref prior_cov = self->prior.s2x[i];
+ Matrix_3d_ref post_cov = self->posterior.s2x[i];
+ unsigned s1;
+ for (s1 = STATE_DIMENSION; s1;) {
+ s1--;
+ unsigned s2;
+ for (s2 = s1 + 1; s2;) {
+ s2--;
+ post_cov[s2][s1] = post_cov[s1][s2] =
+ prior_cov[s1][s2] - prior_cov[s1][VEL] * prior_cov[VEL][s2] * vv[i];
+ }
+ }
+ }
+ matrix_3d_copy(self->posterior.s2x[Z], self->prior.s2x[Z]);
+ location_filter_update_heading(&self->posterior.x);
+ __copy_state(&self->prior, &self->posterior);
+ self->last_correction_time = time;
+}
+
+void location_filter_clear_acc(LocationFilter* self, double time)
+{
+ unsigned i;
+ for (i = GEO_DIMENSION; i--;) {
+ self->posterior.x.a[i] = 0;
+ self->posterior.s2x[i][ACC][ACC] = self->default_var_a;
+ }
+
+ LOG_FUSED_DEV(DBG_INFO, FUNCTION_PREFIX("(p0=%g, p1=%g, p2=%g)"), self->prior.x.p[0], self->prior.x.p[1], self->prior.x.p[2]);
+
+ location_filter_predict(self, time);
+}
+
+void location_filter_update_acc(LocationFilter* self, double time)
+{
+ location_filter_predict(self, time);
+ self->posterior.s2x[X][ACC][ACC] = fl_square(self->prior.x.a[X] - self->posterior.x.a[X]);
+ self->posterior.s2x[Y][ACC][ACC] = fl_square(self->prior.x.a[Y] - self->posterior.x.a[Y]);
+ __copy_state(&self->posterior, &self->prior);
+ self->last_correction_time = time;
+
+ LOG_FUSED_DEV(DBG_INFO, FUNCTION_PREFIX("(p0=%g, p1=%g, p2=%g)"), self->prior.x.p[0], self->prior.x.p[1], self->prior.x.p[2]);
+}
diff --git a/lbs-server/src/fused/LocationFilter.h b/lbs-server/src/fused/LocationFilter.h
new file mode 100644
index 0000000..1c82a1d
--- /dev/null
+++ b/lbs-server/src/fused/LocationFilter.h
@@ -0,0 +1,135 @@
+/*
+ * Copyright (c) 2016-2017 Samsung Electronics Co., Ltd.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/**
+ * @file LocationFilter.h
+ * @brief Implementation of a 4D (2D position + 2D velocity) Kalman filter
+ */
+
+#pragma once
+#ifndef __LOCATION_FILTER_H_
+#define __LOCATION_FILTER_H_
+
+#include "parameters.h"
+#include "Vector.h"
+#include "Matrix.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+typedef enum {
+ POS, /* position [m] */
+ VEL, /* velocity [m/s] */
+ ACC, /* acceleration [m/s^2] */
+ STATE_DIMENSION
+} _fl_kalf_state_component;
+
+typedef struct {
+ Vector_3d p; /* Position */
+ Vector_3d v; /* Velocity */
+ Vector_3d a; /* Acceleration */
+ double heading; /* Heading */
+} _fl_kalf_state_m;
+
+typedef Matrix_3d _fl_kalf_sigma_cube[GEO_DIMENSION];
+typedef Matrix_3d(*_fl_kalf_sigma_cube_ref);
+typedef const Matrix_3d* const_fl_kalf_sigma_cube_ref;
+
+typedef struct {
+ _fl_kalf_state_m x; /* state vector */
+ _fl_kalf_sigma_cube s2x; /* covariance matrix */
+} _fl_kalf_state;
+
+typedef struct {
+ double default_var_a;
+ _fl_kalf_state prior; /* predicted (a priori) state */
+ _fl_kalf_state posterior; /* corrected (a posteriori) state */
+ double last_correction_time; /* update of posterior */
+ double z_rot_speed; /* reference angular z-velocity (horizontal plane) */
+} LocationFilter;
+
+/**
+ * @brief Initialization of the filter (constructor).
+ * @param[in] x X coordinate of the initial position.
+ * @param[in] y Y coordinate of the initial position.
+ * @param[in] z Z coordinate of the initial position.
+ * @param[in] var_p Initial variance of position.
+ * @param[in] var_v Initial variance of speed.
+ * @param[in] var_a Initial variance of acceleration.
+ */
+extern void location_filter_init(LocationFilter* self,
+ double x, double y, double z, double var_p, double var_v, double var_a);
+
+/**
+ * @brief Sets z rotation speed.
+ * @param[in] z_rot_speed Z rotation speed in [rad/sec].
+ */
+#define location_filter_set_z_rot_speed(self, _z_rot_speed) ((self)->z_rot_speed = (_z_rot_speed))
+
+extern void location_filter_update_state(LocationFilter* self);
+
+/**
+ * @brief Planar (2D) Kalman blending of position.
+ * @param[in] time Internal time.
+ * @param[in] pm Measured position.
+ * @param[in] s2pm Position variances.
+ */
+extern void location_filter_kalman_p(LocationFilter* self,
+ double time, const_Vector_3d_ref pm, const_Vector_3d_ref s2pm);
+
+/**
+ * @brief Spatial (3D) Kalman blending of position and velocity.
+ * @param[in] time Internal time.
+ * @param[in] pm Measured position.
+ * @param[in] vm Measured velocity.
+ * @param[in] s2pm Position variances.
+ * @param[in] s2vm Velocity variances.
+ */
+extern void location_filter_kalman_pv(LocationFilter* self, double time,
+ const_Vector_3d_ref pm, const_Vector_3d_ref vm, const_Vector_3d_ref s2pm, const_Vector_3d_ref s2vm);
+
+/**
+ * @brief Kalman blending of speed.
+ * @param[in] time Internal time.
+ * @param[in] speed Measured speed.
+ * @param[in] sigma_of_speed Variance of speed.
+ */
+extern void location_filter_update_speed(LocationFilter* self, double time, double speed, double sigma_of_speed);
+
+extern void location_filter_clear_acc(LocationFilter* self, double time);
+extern void location_filter_update_acc(LocationFilter* self, double time);
+
+/** (Vector_3d_ref) Returns current position estimate */
+#define location_filter_get_position(self) ((self)->posterior.x.p)
+
+/** (Vector_3d_ref) Returns current velocity estimate */
+#define location_filter_get_velocity(self) ((self)->posterior.x.v)
+
+/** (const_fl_kalf_sigma_cube_ref) */
+#define location_filter_get_predicted_covariance(self) ((self)->prior.s2x)
+
+/** (const_Vector_3d_ref) Returns predicted position estimate */
+#define location_filter_get_predicted_position(self) ((self)->prior.x.p)
+
+/** (const_Vector_3d_ref) Returns predicted velocity estimate */
+#define location_filter_get_predicted_velocity(self) ((self)->prior.x.v)
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* __LOCATION_FILTER_H_ */
diff --git a/lbs-server/src/fused/Matrix.c b/lbs-server/src/fused/Matrix.c
new file mode 100644
index 0000000..d7f5be2
--- /dev/null
+++ b/lbs-server/src/fused/Matrix.c
@@ -0,0 +1,156 @@
+/*
+ * Copyright (c) 2016-2017 Samsung Electronics Co., Ltd.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "Matrix.h"
+#include "math-ext.h"
+
+void matrix_3d_transpose(Matrix_3d_ref self)
+{
+ int i, j;
+
+ for (i = GEO_DIMENSION - 1; i >= 0; i--) {
+ for (j = i - 1; j >= 0; j--) {
+ double v = self[i][j];
+ self[i][j] = self[j][i];
+ self[j][i] = v;
+ }
+ }
+}
+
+void matrix_3d_multiply_matrix_3d(const_Matrix_3d_ref m1, const_Matrix_3d_ref m2, Matrix_3d_ref out)
+{
+ int i, j;
+
+ for (i = GEO_DIMENSION - 1; i >= 0; i--) {
+ for (j = GEO_DIMENSION - 1; j >= 0; j--) {
+ out[i][j] = m1[i][X] * m2[X][j] + m1[i][Y] * m2[Y][j] + m1[i][Z] * m2[Z][j];
+ }
+ }
+}
+
+void matrix_3d_multiply_vector_3d(const_Matrix_3d_ref m, const_Vector_3d_ref v, Vector_3d_ref out)
+{
+ out[X] = vector_3d_dot_product(m[X], v);
+ out[Y] = vector_3d_dot_product(m[Y], v);
+ out[Z] = vector_3d_dot_product(m[Z], v);
+}
+
+void vector_3d_multiply_matrix_3d(const_Vector_3d_ref v, const_Matrix_3d_ref m, Vector_3d_ref out)
+{
+ out[X] = m[X][X] * v[X] + m[Y][X] * v[Y] + m[Z][X] * v[Z];
+ out[Y] = m[X][Y] * v[X] + m[Y][Y] * v[Y] + m[Z][Y] * v[Z];
+ out[Z] = m[X][Z] * v[X] + m[Y][Z] * v[Y] + m[Z][Z] * v[Z];
+}
+
+void matrix_3d_from_vector_3d(const_Vector_3d_ref p, Matrix_3d_ref out,
+ double* lat, double* lon, double* sin_lat, double* cos_lat)
+{
+ double cos_rx, sin_rx;
+ double cos_ry, sin_ry;
+ double yz2 = fl_square(p[Y]) + fl_square(p[Z]);
+ double r2s = sqrt(yz2 + fl_square(p[X]));
+ if (r2s > 0) {
+ double _r = 1.0 / r2s;
+ double yz = sqrt(yz2);
+
+ *lat = atan2(p[X], yz);
+ *sin_lat = sin_rx = _r * p[X];
+ *cos_lat = cos_rx = _r * yz;
+ if (yz > 0) {
+ double _yz = 1.0 / yz;
+
+ *lon = atan2(p[Y], p[Z]);
+ sin_ry = _yz * p[Y];
+ cos_ry = _yz * p[Z];
+ } else {
+ *lon = 0;
+ sin_ry = 0;
+ cos_ry = 1;
+ }
+ } else {
+ *lat = 0;
+ *lon = 0;
+ *sin_lat = sin_rx = 0;
+ *cos_lat = cos_rx = 1;
+ sin_ry = 0;
+ cos_ry = 1;
+ }
+ out[X][X] = cos_rx;
+ out[Y][X] = 0;
+ out[Z][X] = sin_rx;
+
+ out[X][Y] = -sin_ry * sin_rx;
+ out[Y][Y] = cos_ry;
+ out[Z][Y] = sin_ry * cos_rx;
+
+ out[X][Z] = -cos_ry * sin_rx;
+ out[Y][Z] = -sin_ry;
+ out[Z][Z] = cos_ry * cos_rx;
+}
+
+void matrix_2d_multiply_matrix_2d(const_Matrix_2d_ref m1, const_Matrix_2d_ref m2, Matrix_2d_ref out)
+{
+ int i, j;
+
+ for (i = PLANAR_DIMENSION - 1; i >= 0; i--) {
+ for (j = PLANAR_DIMENSION - 1; j >= 0; j--) {
+ out[i][j] = m1[i][PLANAR_X] * m2[PLANAR_X][j] + m1[i][PLANAR_Y] * m2[PLANAR_Y][j];
+ }
+ }
+}
+
+void matrix_3d_multiply_matrix_2d(const_Matrix_3d_ref m1, const_Matrix_2d_ref m2, Matrix_2d_ref out)
+{
+ int i, j;
+
+ for (i = PLANAR_DIMENSION - 1; i >= 0; i--) {
+ for (j = PLANAR_DIMENSION - 1; j >= 0; j--) {
+ out[i][j] = m1[i][PLANAR_X] * m2[PLANAR_X][j] + m1[i][PLANAR_Y] * m2[PLANAR_Y][j];
+ }
+ }
+}
+
+void matrix_2d_multiply_vector_2d(const_Matrix_2d_ref m, const_Vector_2d_ref v, Vector_2d_ref out)
+{
+ out[PLANAR_X] = vector_2d_dot_product(m[PLANAR_X], v);
+ out[PLANAR_Y] = vector_2d_dot_product(m[PLANAR_Y], v);
+}
+
+void vector_2d_multiply_matrix_2d(const_Vector_2d_ref v, const_Matrix_2d_ref m, Vector_2d_ref out)
+{
+ out[PLANAR_X] = m[PLANAR_X][PLANAR_X] * v[PLANAR_X] + m[PLANAR_Y][PLANAR_X] * v[PLANAR_Y];
+ out[PLANAR_Y] = m[PLANAR_X][PLANAR_Y] * v[PLANAR_X] + m[PLANAR_Y][PLANAR_Y] * v[PLANAR_Y];
+}
+
+double matrix_2d_det(const_Matrix_2d_ref self)
+{
+ return self[PLANAR_X][PLANAR_X] * self[PLANAR_Y][PLANAR_Y] - self[PLANAR_Y][PLANAR_X] * self[PLANAR_X][PLANAR_Y];
+}
+
+bool matrix_2d_invert(const_Matrix_2d_ref self, Matrix_2d_ref out)
+{
+ const double det = matrix_2d_det(self);
+ if (det == 0) {
+ return false;
+ }
+
+ const double denom = 1.0 / det;
+ out[PLANAR_X][PLANAR_X] = self[PLANAR_Y][PLANAR_Y] * denom;
+ out[PLANAR_X][PLANAR_Y] = -self[PLANAR_Y][PLANAR_X] * denom;
+ out[PLANAR_Y][PLANAR_X] = -self[PLANAR_X][PLANAR_Y] * denom;
+ out[PLANAR_Y][PLANAR_Y] = self[PLANAR_X][PLANAR_X] * denom;
+ return true;
+}
diff --git a/lbs-server/src/fused/Matrix.h b/lbs-server/src/fused/Matrix.h
new file mode 100644
index 0000000..b86d911
--- /dev/null
+++ b/lbs-server/src/fused/Matrix.h
@@ -0,0 +1,169 @@
+/*
+ * Copyright (c) 2016-2017 Samsung Electronics Co., Ltd.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/**
+ * @file Matrix.h
+ * @brief matrix
+ */
+
+#pragma once
+#ifndef __MATRIX_H_
+#define __MATRIX_H_
+
+#include "Vector.h"
+#include <stdbool.h>
+#include <memory.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+
+typedef double Matrix_3d[GEO_DIMENSION][GEO_DIMENSION];
+typedef double(*Matrix_3d_ref)[GEO_DIMENSION];
+typedef const double(*const_Matrix_3d_ref)[GEO_DIMENSION];
+
+typedef double Matrix_2d[PLANAR_DIMENSION][PLANAR_DIMENSION];
+typedef double(*Matrix_2d_ref)[PLANAR_DIMENSION];
+typedef const double(*const_Matrix_2d_ref)[PLANAR_DIMENSION];
+
+/**
+ * @brief Clears content of provided 3D matrix.
+ * @param[in] self 3D matrix to clear.
+ */
+#define matrix_3d_clear(self) memset(self, 0, sizeof(Matrix_3d))
+
+/**
+ * @brief Copy content of one 3D matrix to another one.
+ * @param[in] dst Destination 3D matrix.
+ * @param[in] src Source 3D matrix.
+ */
+#define matrix_3d_copy(dst, src) memcpy(dst, src, sizeof(Matrix_3d))
+
+/**
+ * @brief Transposition of provided 3D matrix.
+ * @param[in] self 3D matrix to transpose.
+ */
+extern void matrix_3d_transpose(Matrix_3d_ref self);
+
+/**
+ * @brief Multiplies two 3D matrixes.
+ * @param[in] m1 First 3D matrix.
+ * @param[in] m2 Second 3D matrix.
+ * @param[out] out Multiplication of provided matrixes, i.e. out = m1 * m2
+ */
+extern void matrix_3d_multiply_matrix_3d(const_Matrix_3d_ref m1, const_Matrix_3d_ref m2, Matrix_3d_ref out);
+
+/**
+ * @brief Multiplies 3D matrix with 3D vector.
+ * @param[in] m 3D matrix.
+ * @param[in] v 3D vector.
+ * @param[out] out Multiplication of provided matrix and vector, i.e. out = m * v
+ */
+extern void matrix_3d_multiply_vector_3d(const_Matrix_3d_ref m, const_Vector_3d_ref v, Vector_3d_ref out);
+
+/**
+ * @brief Multiplies 3D vector with 3D matrix.
+ * @param[in] v 3D vector.
+ * @param[in] m 3D matrix.
+ * @param[out] out Multiplication of provided vector and matrix, i.e. out = v * m
+ */
+extern void vector_3d_multiply_matrix_3d(const_Vector_3d_ref v, const_Matrix_3d_ref m, Vector_3d_ref out);
+
+extern void matrix_3d_from_vector_3d(const_Vector_3d_ref p, Matrix_3d_ref out,
+ double *lat, double *lon, double* sin_lat, double* cos_lat);
+
+/**
+ * @brief Inlined left-action of a linear operator on a 3D covector.
+ * @param[in] m 3x3D matrix.
+ * @param[in] v 3D vector.
+ * @param[out] out Left-action of the linear operator @a m on the vector @a v, i.e. out = m * v
+ */
+#define MATRIX_DOT_VECTOR_3D(m, v, out)\
+ do {\
+ (out)[X] = (m)[X][X] * (v)[X] + (m)[X][Y] * (v)[Y] + (m)[X][Z] * (v)[Z];\
+ (out)[Y] = (m)[Y][X] * (v)[X] + (m)[Y][Y] * (v)[Y] + (m)[Y][Z] * (v)[Z];\
+ (out)[Z] = (m)[Z][X] * (v)[X] + (m)[Z][Y] * (v)[Y] + (m)[Z][Z] * (v)[Z];\
+ } while (0)
+
+/**
+ * @brief Inlined right-action of a linear operator on a 3D.
+ * In Euclidean metric this is the same as left action of the transposed operator on the vector.
+ * @param[in] v 3D vector.
+ * @param[in] m 3x3D matrix.
+ * @param[out] out Right-action of the linear operator @a m on the covector @a v, i.e. out = v * m
+ */
+#define VECTOR_DOT_MATRIX_3D(v, m, out)\
+ do {\
+ (out)[X] = (v)[X] * (m)[X][X] + (v)[Y] * (m)[Y][X] + (v)[Z] * (m)[Z][X];\
+ (out)[Y] = (v)[X] * (m)[X][Y] + (v)[Y] * (m)[Y][Y] + (v)[Z] * (m)[Z][Y];\
+ (out)[Z] = (v)[X] * (m)[X][Z] + (v)[Y] * (m)[Y][Z] + (v)[Z] * (m)[Z][Z];\
+ } while (0)
+
+/**
+ * @brief Multiplies two 2D matrixes.
+ * @param[in] m1 First 2D matrix.
+ * @param[in] m2 Second 2D matrix.
+ * @param[out] out Multiplication of provided matrixes, i.e. out = m1 * m2
+ */
+extern void matrix_2d_multiply_matrix_2d(const_Matrix_2d_ref m1, const_Matrix_2d_ref m2, Matrix_2d_ref out);
+
+/**
+ * @brief Multiplies submatrix of 3D matrix with 2D matrixes.
+ * @param[in] m1 3D matrix. For multiplication 2D matrix consisting of first 2 rows and first 2 columns is taken.
+ * @param[in] m2 2D matrix.
+ * @param[out] out Multiplication of provided matrixes, i.e. out = m1 * m2
+ */
+extern void matrix_3d_multiply_matrix_2d(const_Matrix_3d_ref m1, const_Matrix_2d_ref m2, Matrix_2d_ref out);
+
+/**
+ * @brief Multiplies 2D matrix with 2D vector.
+ * @param[in] m 2D matrix.
+ * @param[in] v 2D vector.
+ * @param[out] out Multiplication of provided matrix and vector, i.e. out = m * v
+ */
+extern void matrix_2d_multiply_vector_2d(const_Matrix_2d_ref m, const_Vector_2d_ref v, Vector_2d_ref out);
+
+/**
+ * @brief Multiplies 2D vector with 2D matrix.
+ * @param[in] v 2D vector.
+ * @param[in] m 2D matrix.
+ * @param[out] out Multiplication of provided vector and matrix, i.e. out = v * m
+ */
+extern void vector_2d_multiply_matrix_2d(const_Vector_2d_ref v, const_Matrix_2d_ref m, Vector_2d_ref out);
+
+/**
+ * @brief Determinant of 2D matrix.
+ * @param[in] m 2D matrix.
+ * @return Determinant of the matrix.
+ */
+extern double matrix_2d_det(const_Matrix_2d_ref self);
+
+/**
+ * @brief Inverts 2D matrix.
+ * @param[in] m 2D matrix.
+ * @param[out] out Inverted matrix. Not modified if result of a function is false (determinant is 0).
+ * @return
+ * - true - If inverted matrix was calculated.
+ * - false - If matrix can not be inverted (determinant is 0).
+ */
+extern bool matrix_2d_invert(const_Matrix_2d_ref m, Matrix_2d_ref out);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* __MATRIX_H_ */
diff --git a/lbs-server/src/fused/MotionDetector.c b/lbs-server/src/fused/MotionDetector.c
new file mode 100644
index 0000000..d018ab4
--- /dev/null
+++ b/lbs-server/src/fused/MotionDetector.c
@@ -0,0 +1,108 @@
+/*
+ * Copyright (c) 2016-2017 Samsung Electronics Co., Ltd.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#if !defined(LOG_THRESHOLD)
+ /* custom logging threshold - keep undefined on the repo */
+ /* #define LOG_THRESHOLD LOG_LEVEL_TRACE */
+#endif
+
+#include "MotionDetector.h"
+#include "math-ext.h"
+#include "debug_util.h"
+
+#define _USE_MATH_DEFINES
+#include <math.h>
+
+#if (FL_MOTION_DETECTOR)
+
+void motion_detector_init(MotionDetector* self, MotionDetectorStateChangeListener on_motion_state_changed)
+{
+ LOG_FUSED_DEV(DBG_LOW, FUNCTION_PREFIX("(motion_cb=%p)"), on_motion_state_changed);
+ Aema1dEstimator_init(&self->moti_al2, 1 << FL_MOTI_AEMA_PLATEAU_BITS);
+ self->moti_immobile_time = 0;
+ self->moti_state = MOTI_UNDECIDED;
+ self->moti_last_notification = MOTI_UNDECIDED;
+ self->moti_on_motion_state_changed = on_motion_state_changed;
+}
+
+void motion_detector_exit(MotionDetector* self)
+{
+ LOG_FUSED_DEV(DBG_LOW, FUNCTION_PREFIX("()"));
+ self->moti_on_motion_state_changed = NULL;
+ self->moti_last_notification = MOTI_UNDECIDED;
+ self->moti_state = MOTI_UNDECIDED;
+ self->moti_immobile_time = 0;
+ Aema1dEstimator_exit(&self->moti_al2);
+}
+
+void motion_detector_reset(MotionDetector* self)
+{
+ LOG_FUSED_DEV(DBG_LOW, FUNCTION_PREFIX("()"));
+ self->moti_immobile_time = 0;
+ self->moti_state = MOTI_UNDECIDED;
+ self->moti_last_notification = MOTI_UNDECIDED;
+ Aema1dEstimator_reset(&self->moti_al2);
+}
+
+/**
+ * @brief Change the state indicator and notify the user.
+ * @param[in] state New state value (does not have to be different than present one).
+ */
+static void motion_detector_set_state(MotionDetector* self, MotionDetectorState state)
+{
+ LOG_FUSED_DEV(DBG_LOW, FUNCTION_PREFIX("(%u->%u)"), self->moti_state, state);
+ self->moti_state = state;
+ if (self->moti_on_motion_state_changed && self->moti_last_notification != state) {
+ self->moti_on_motion_state_changed(state);
+ self->moti_last_notification = state;
+ }
+}
+
+void motion_detector_process(MotionDetector* self, double t, const_Vector_3d_ref al)
+{
+ double al2 = Aema1dEstimator_process(&self->moti_al2, vector_3d_length2(al));
+ if (al2 > SQUARE(FL_MOTI_MOTION_LEVEL)) {
+ switch (self->moti_state) {
+ case MOTI_UNDECIDED:
+ case MOTI_IMMOBILE:
+ case MOTI_SLEEP:
+ MotionDetector_set_state(self, MOTI_MOVING);
+ break;
+
+ default: /* MOTI_MOVING */
+ break;
+ }
+ } else if (al2 < SQUARE(FL_MOTI_NOISE_LEVEL)) {
+ switch (self->moti_state) {
+ case MOTI_UNDECIDED:
+ case MOTI_MOVING:
+ MotionDetector_set_state(self, MOTI_IMMOBILE);
+ self->moti_immobile_time = t;
+ break;
+
+ case MOTI_IMMOBILE:
+ if (t - self->moti_immobile_time > FL_MOTI_IMMOBILITY_INTERVAL)
+ MotionDetector_set_state(self, MOTI_SLEEP);
+ break;
+
+ default:
+ /* MOTI_SLEEP: */
+ break;
+ }
+ }
+}
+
+#endif
diff --git a/lbs-server/src/fused/MotionDetector.h b/lbs-server/src/fused/MotionDetector.h
new file mode 100644
index 0000000..a6990ef
--- /dev/null
+++ b/lbs-server/src/fused/MotionDetector.h
@@ -0,0 +1,98 @@
+/*
+ * Copyright (c) 2016-2017 Samsung Electronics Co., Ltd.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/**
+ * @file MotionDetector.h
+ * @brief Accelerometer-based detector of significant motion.
+ */
+
+#pragma once
+#ifndef __MOTION_DETECTOR_H__
+#define __MOTION_DETECTOR_H__
+
+#include "MovingAverageFilters.h"
+#include "Vector.h"
+#include "parameters.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+typedef enum {
+ MOTI_UNDECIDED, /* initial */
+ MOTI_MOVING, /* acceleration above MOTION_LEVEL */
+ MOTI_IMMOBILE, /* acceleration below NOISE_LEVEL */
+ MOTI_SLEEP, /* immobile for IMMOBILITY_INTERVAL */
+} MotionDetectorState;
+
+/**
+ * @brief Callback invoked on every change in motion state.
+ * @param[in] state New motion state.
+ * @param[in] handler Arbitrary pointer to user data specified during the init() call.
+ */
+typedef void (*MotionDetectorStateChangeListener)(MotionDetectorState state);
+
+typedef struct {
+ MovingAverage1dFilter moti_al2; /**< Linear acceleration low-pass filter */
+ double moti_immobile_time; /**< Start time of immobility */
+ MotionDetectorState moti_state; /**< Current state */
+ MotionDetectorState moti_last_notification; /**< Last notified state */
+ MotionDetectorStateChangeListener moti_on_motion_state_changed; /**< User callback */
+} MotionDetector;
+
+#if (FL_MOTION_DETECTOR)
+
+/**
+ * @brief Initialization of the singleton unit (static constructor).
+ * @param[in] on_motion_state_changed Callback to be invoked when the detected state of motion changes.
+ * @param[in] handler Arbitrary user parameter passed to the callback.
+ */
+void motion_detector_init(MotionDetector* self, MotionDetectorStateChangeListener on_motion_state_changed);
+
+/** Cleanup of the singleton unit (static destructor) */
+void motion_detector_exit(MotionDetector* self);
+
+/**
+ * @brief Reset of the internal state back to initial one. This function is
+ * used for repetitive testing and module soft restarts.
+ */
+void motion_detector_reset(MotionDetector* self);
+
+/**
+ * @biref Processing of a single sample from the accelerometer. This function
+ * implments a hysteresis in the 2D space of linear acceleration modulus |al|^2
+ * vs motion state:
+ * - Crossing the FL_MOTI_MOTION_LEVEL from below changes state to MOVING;
+ * - Crossing the FL_MOTI_NOISE_LEVEL from above changes state to IMMOBILE;
+ * - Staying IMMOBILE for longer than FL_MOTI_IMMOBILITY_INTERVAL changes state to SLEEP.
+ *
+ * @param[in] time Time of the event in seconds.
+ * @param[in] al 3D vector of linear acceleration.
+ */
+void motion_detector_process(MotionDetector* self, double time, const_Vector_3d_ref al);
+
+#else
+ #define motion_detector_init(self, on_motion_state_changed)
+ #define motion_detector_exit(self)
+ #define motion_detector_reset(self)
+ #define motion_detector_process(self, t, al)
+#endif
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* __MOTION_DETECTOR_H__ */
diff --git a/lbs-server/src/fused/MovingAverage.c b/lbs-server/src/fused/MovingAverage.c
new file mode 100644
index 0000000..cbdb8ed
--- /dev/null
+++ b/lbs-server/src/fused/MovingAverage.c
@@ -0,0 +1,56 @@
+/*
+ * Copyright (c) 2016-2017 Samsung Electronics Co., Ltd.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "MovingAverage.h"
+#include "debug_util.h"
+
+#include <math.h>
+
+void moving_average_init(MovingAverage* self, unsigned plateau_samples)
+{
+ LOG_FUSED_DEV(DBG_LOW, FUNCTION_PREFIX("[%p](bits=%u)"), self, plateau_samples);
+ self->plateau_samples = plateau_samples;
+ moving_average_reset(self);
+}
+
+void moving_average_exit(MovingAverage *self)
+{
+ LOG_FUSED_DEV(DBG_LOW, FUNCTION_PREFIX("[%p]()"), self);
+}
+
+void moving_average_reset(MovingAverage* self)
+{
+ LOG_FUSED_DEV(DBG_LOW, FUNCTION_PREFIX("[%p]()"), self);
+ self->samples = 0;
+ self->update_rate = 1;
+ self->decay_rate = 0;
+}
+
+void moving_average_process(MovingAverage *self)
+{
+ if (self->samples < self->plateau_samples) {
+ self->samples++;
+ self->update_rate = 1.0 / self->samples;
+ self->decay_rate = 1.0 - self->update_rate;
+ }
+}
+
+void moving_average_set_estimate(MovingAverage* self)
+{
+ self->samples = self->plateau_samples;
+ self->update_rate = 1.0 / self->plateau_samples;
+ self->decay_rate = 1.0 - self->update_rate;
+}
diff --git a/lbs-server/src/fused/MovingAverage.h b/lbs-server/src/fused/MovingAverage.h
new file mode 100644
index 0000000..ab52fc4
--- /dev/null
+++ b/lbs-server/src/fused/MovingAverage.h
@@ -0,0 +1,75 @@
+/*
+ * Copyright (c) 2016-2017 Samsung Electronics Co., Ltd.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/**
+ * @file MovingAverage.h
+ * @brief Adaptive Exponential Moving Average (1st order) filter
+ */
+
+#pragma once
+#ifndef __MOVING_AVERAGE_H__
+#define __MOVING_AVERAGE_H__
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+typedef struct {
+ unsigned plateau_samples; /* number of samples to collect before EMA kicks-in */
+ unsigned samples; /* min(number of samples collected, plateau_samples) */
+ double update_rate; /* (1 / samples) */
+ double decay_rate; /* (1 - update_rate) */
+} MovingAverage;
+
+/**
+ * @brief Initialization of the object (class constructor).
+ * @param[in] self Object pointer
+ * @param[in] plateau_samples Number of samples to collect before switching to standard EMA mode.
+ */
+extern void moving_average_init(MovingAverage *self, unsigned plateau_samples);
+
+/** Cleanup of the base class (destructor) */
+extern void moving_average_exit(MovingAverage *self);
+
+/**
+ * @brief Reset the AEMA object back to initial state.
+ * This function is used for repetitive testing and module soft restarts.
+ */
+extern void moving_average_reset(MovingAverage *self);
+
+/**
+ * @brief Processing of a data sample. This is the common part of the
+ * derived processing methods, which implements the AEMA update/decay rate.
+ */
+extern void moving_average_process(MovingAverage *self);
+
+/** Bring the base filter to a stationary state (EMA mode). This method is used for testing */
+extern void moving_average_set_estimate(MovingAverage* self);
+
+/** (double) */
+#define moving_average_get_update_rate(self) ((self)->update_rate)
+
+/** (double) */
+#define moving_average_get_decay_rate(self) ((self)->decay_rate)
+
+/** void moving_average_filter(MovingAverage* self, double* f, double v) */
+#define moving_average_filter(self, f, v) ((f) = (self)->decay_rate * (f) + (self)->update_rate * (v))
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* __MOVING_AVERAGE_H__ */
diff --git a/lbs-server/src/fused/MovingAverageFilters.c b/lbs-server/src/fused/MovingAverageFilters.c
new file mode 100644
index 0000000..a5d9df2
--- /dev/null
+++ b/lbs-server/src/fused/MovingAverageFilters.c
@@ -0,0 +1,127 @@
+/*
+ * Copyright (c) 2016-2017 Samsung Electronics Co., Ltd.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "MovingAverageFilters.h"
+#include "debug_util.h"
+
+/* Labels for an axial (or cylindrical without phase) 2D space. */
+typedef enum {
+ AXIAL_H,
+ AXIAL_V,
+ AXIAL_DIMENSION
+} fl_axial;
+
+void moving_average_1d_filter_init(MovingAverage1dFilter* self, unsigned plateau_samples)
+{
+ moving_average_init(&self->base, plateau_samples);
+ self->value = 0;
+}
+
+void moving_average_1d_filter_exit(MovingAverage1dFilter *self)
+{
+ moving_average_exit(&self->base);
+}
+
+void moving_average_1d_filter_reset(MovingAverage1dFilter* self)
+{
+ moving_average_reset(&self->base);
+ self->value = 0;
+}
+
+double moving_average_1d_filter_process(MovingAverage1dFilter* self, double value)
+{
+ LOG_FUSED_DEV(DBG_LOW, FUNCTION_PREFIX("[%p](%g)"), self, value);
+ moving_average_process(&self->base);
+ moving_average_filter(&self->base, self->value, value);
+ return self->value;
+}
+
+void moving_average_1d_filter_set_estimate(MovingAverage1dFilter* self, double value)
+{
+ LOG_FUSED_DEV(DBG_LOW, FUNCTION_PREFIX("[%p](%g)"), self, value);
+ moving_average_set_estimate(&self->base);
+ self->value = value;
+}
+
+void moving_average_2d_filter_init(MovingAverage2dFilter* self, unsigned plateau_samples)
+{
+ moving_average_init(&self->base, plateau_samples);
+ self->value[AXIAL_H] = 0;
+ self->value[AXIAL_V] = 0;
+}
+
+void moving_average_2d_filter_exit(MovingAverage2dFilter *self)
+{
+ moving_average_exit(&self->base);
+}
+
+void moving_average_2d_filter_reset(MovingAverage2dFilter* self)
+{
+ moving_average_reset(&self->base);
+ self->value[PLANAR_X] = 0;
+ self->value[PLANAR_Y] = 0;
+}
+
+const_Vector_2d_ref moving_average_2d_filter_process(MovingAverage2dFilter* self, double value_H, double value_V)
+{
+ LOG_FUSED_DEV(DBG_LOW, FUNCTION_PREFIX("[%p](%g, %g)"), self, value_H, value_V);
+ moving_average_process(&self->base);
+ moving_average_filter(&self->base, self->value[AXIAL_H], value_H);
+ moving_average_filter(&self->base, self->value[AXIAL_V], value_V);
+ return self->value;
+}
+
+void moving_average_2d_filter_set_estimate(MovingAverage2dFilter* self, double value_H, double value_V)
+{
+ LOG_FUSED_DEV(DBG_LOW, FUNCTION_PREFIX("[%p](%g, %g)"), self, value_H, value_V);
+ moving_average_set_estimate(&self->base);
+ self->value[AXIAL_H] = value_H;
+ self->value[AXIAL_V] = value_V;
+}
+
+void moving_average_3d_filter_init(MovingAverage3dFilter* self, unsigned plateau_samples)
+{
+ moving_average_init(&self->base, plateau_samples);
+ vector_3d_clear(self->value);
+}
+
+void moving_average_3d_filter_exit(MovingAverage3dFilter *self)
+{
+ moving_average_exit(&self->base);
+}
+
+void moving_average_3d_filter_reset(MovingAverage3dFilter* self)
+{
+ moving_average_reset(&self->base);
+ vector_3d_clear(self->value);
+}
+
+const_Vector_3d_ref moving_average_3d_filter_process(MovingAverage3dFilter* self, const_Vector_3d_ref value)
+{
+ LOG_FUSED_DEV(DBG_LOW, FUNCTION_PREFIX("[%p](%g, %g, %g)"), self, value[X], value[Y], value[Z]);
+ moving_average_process(&self->base);
+ moving_average_filter(&self->base, self->value[X], value[X]);
+ moving_average_filter(&self->base, self->value[Y], value[Y]);
+ moving_average_filter(&self->base, self->value[Z], value[Z]);
+ return self->value;
+}
+
+void moving_average_3d_filter_set_estimate(MovingAverage3dFilter* self, const_Vector_3d_ref value)
+{
+ LOG_FUSED_DEV(DBG_LOW, FUNCTION_PREFIX("[%p](%g, %g, %g)"), self, value[X], value[Y], value[Z]);
+ moving_average_set_estimate(&self->base);
+ vector_3d_copy(self->value, value);
+}
diff --git a/lbs-server/src/fused/MovingAverageFilters.h b/lbs-server/src/fused/MovingAverageFilters.h
new file mode 100644
index 0000000..7794fe0
--- /dev/null
+++ b/lbs-server/src/fused/MovingAverageFilters.h
@@ -0,0 +1,156 @@
+/*
+ * Copyright (c) 2016-2017 Samsung Electronics Co., Ltd.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/**
+ * @file MovingAverageFilters.h
+ * @brief Moving Average (1st order) filters
+ */
+
+#pragma once
+#ifndef __MOVING_AVERAGE_1D_FILTER_H__
+#define __MOVING_AVERAGE_1D_FILTER_H__
+
+#include "MovingAverage.h"
+#include "Vector.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+
+/** Moving Average (1st order) 1D filter */
+typedef struct {
+ MovingAverage base; /* base object */
+ double value; /* filter output (estimated value) */
+} MovingAverage1dFilter;
+
+/**
+ * @brief Initialization of the 1D AEMA object (class constructor).
+ * @param[in] self Object pointer
+ * @param[in] plateau_samples Number of samples to collect before switching to standard EMA mode.
+ */
+void moving_average_1d_filter_init(MovingAverage1dFilter *self, unsigned plateau_samples);
+void moving_average_1d_filter_exit(MovingAverage1dFilter *self);
+void moving_average_1d_filter_reset(MovingAverage1dFilter *self);
+
+/**
+ * @brief Processing of a single data sample.
+ * @param[in] self Object pointer
+ * @param[in] value Input value to the filter.
+ * @return Output value of the filter after processing.
+ * This is the same as returned by fl_aema1d_get_estimate(self).
+ */
+double moving_average_1d_filter_process(MovingAverage1dFilter *self, double value);
+
+/**
+ * @brief [TEST] Bring the filter to a stationary state (EMA mode, input = output).
+ * @param[in] self Object pointer
+ * @param[in] value I/o value of the filter.
+ */
+void moving_average_1d_filter_set_estimate(MovingAverage1dFilter *self, double value);
+
+/**
+ * @brief Fetch the current filter output.
+ * @param[in] self Object pointer
+ * @return Output value after last processed sample.
+ */
+#define moving_average_1d_filter_get_estimate(self) ((self)->value)
+
+
+/** Moving Average (1st order) 2D filter */
+typedef struct {
+ MovingAverage base; /* base object */
+ Vector_2d value; /* filter output (estimated value) */
+} MovingAverage2dFilter;
+
+/**
+ * @brief Initialization of the 2D AEMA object (class constructor).
+ * @param[in] self Object pointer
+ * @param[in] plateau_samples Number of samples to collect before switching to standard EMA mode.
+ */
+void moving_average_2d_filter_init(MovingAverage2dFilter *self, unsigned plateau_samples);
+void moving_average_2d_filter_exit(MovingAverage2dFilter *self);
+void moving_average_2d_filter_reset(MovingAverage2dFilter *self);
+
+/**
+ * @brief Processing of a double data sample.
+ * @param[in] self Object pointer
+ * @param[in] value_H First (horizontal) input value to the filter.
+ * @param[in] value_V Second (vertical) input value to the filter.
+ * @return Output value of the filter after processing.
+ * This is the same as returned by fl_aema2d_get_estimate(self).
+ */
+const_Vector_2d_ref moving_average_2d_filter_process(MovingAverage2dFilter *self, double value_X, double value_Y);
+
+/**
+ * @brief [TEST] Bring the filter to a stationary state (EMA mode, input = output).
+ * @param[in] self Object pointer
+ * @param[in] value_H First (horizontal) i/o value of the filter.
+ * @param[in] value_V Second (vertical) i/o value of the filter.
+ */
+void moving_average_2d_filter_set_estimate(MovingAverage2dFilter *self, double value_X, double value_Y);
+
+/**
+ * @brief (const_Vector_2d_ref) Fetch the current filter output
+ * @param[in] self Object pointer
+ * @return Output 2D vector after last processed sample.
+ */
+#define moving_average_2d_filter_get_estimate(self) ((self)->value)
+
+
+/** Moving Average (1st order) 3D filter */
+typedef struct {
+ MovingAverage base; /* base object */
+ Vector_3d value; /* filter output (estimated value) */
+} MovingAverage3dFilter;
+
+/**
+ * @brief Initialization of the 3D AEMA object (class constructor).
+ * @param[in] self Object pointer
+ * @param[in] plateau_samples Number of samples to collect before switching to standard EMA mode.
+ */
+void moving_average_3d_filter_init(MovingAverage3dFilter *self, unsigned plateau_samples);
+void moving_average_3d_filter_exit(MovingAverage3dFilter *self);
+void moving_average_3d_filter_reset(MovingAverage3dFilter *self);
+
+/**
+ * @brief Processing of a triple data sample.
+ * @param[in] self Object pointer
+ * @param[in] value Input 3D vector value to the filter.
+ * @return Output value of the filter after processing.
+ * This is the same as returned by fl_aema3d_get_estimate(self).
+ */
+const_Vector_3d_ref moving_average_3d_filter_process(MovingAverage3dFilter *self, const_Vector_3d_ref value);
+
+/**
+ * @brief [TEST]Bring the filter to a stationary state (EMA mode, input = output).
+ * @param[in] self Object pointer
+ * @param[in] value I/o 3D vector value of the filter.
+ */
+void moving_average_3d_filter_set_estimate(MovingAverage3dFilter *self, const_Vector_3d_ref value);
+
+/**
+ * (const_Vector_3d_ref) Fetch the current filter output.
+ * @param[in] self Object pointer
+ * @return Output 3D vector after last processed sample.
+ */
+#define moving_average_3d_filter_get_estimate(self) ((self)->value)
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* __MOVING_AVERAGE_1D_FILTER_H__ */
diff --git a/lbs-server/src/fused/Orientation.c b/lbs-server/src/fused/Orientation.c
new file mode 100644
index 0000000..44a80c4
--- /dev/null
+++ b/lbs-server/src/fused/Orientation.c
@@ -0,0 +1,388 @@
+/*
+ * Copyright (c) 2016-2017 Samsung Electronics Co., Ltd.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "Orientation.h"
+#include "Conversions.h"
+#include "debug_util.h"
+#include "parameters.h"
+
+#define DEFAULT_ROTATION_SIGMA2 SQUARE(1.0 / 256) /* [(rad/s)^2] */
+#define TIME_FORMAT "%.3fs"
+
+#if (FL_ENABLE_DEVEL_LOG)
+static const Vector_3d gravity = {0, 0, 1};
+#endif
+
+void orientation_init(Orientation* self,
+ MotionDetectorStateChangeListener on_motion_state_changed, double rot_var)
+{
+ accelerometer_filter_init(&self->acc_filter, on_motion_state_changed);
+ rotation_filter_init(&self->rotation_filter, rot_var);
+ orientation_reset(self);
+}
+
+void orientation_exit(Orientation* self)
+{
+ accelerometer_filter_exit(&self->acc_filter);
+}
+
+void orientation_reset(Orientation* self)
+{
+ matrix_3d_clear(&self->rotation);
+ unsigned i;
+ for (i = GEO_DIMENSION; i;) {
+ --i;
+ self->rotation[i][i] = 1;
+ }
+ self->last_angular_velocity_time = FL_UNDEFINED_TIME;
+ self->weight2 = M_1_PI / 3;
+#if (FL_KALMAN_ANGULAR_VELOCITY)
+ rotation_filter_reset(&self->rotation_filter);
+#else
+ self->z_rot_speed = 0;
+#endif
+}
+
+bool orientation_new_angular_velocity(Orientation* self, double time, const_Vector_3d_ref angular_velocity, double s2wm)
+{
+ bool result = false;
+ if (KNOWN_TIME(self->last_angular_velocity_time)) {
+ const double dt = time - self->last_angular_velocity_time;
+
+ if (dt > 0) {
+ Vector_3d mean_av;
+ vector_3d_linear_combination(0.5, self->last_angular_velocity, 0.5, angular_velocity, mean_av);
+ const double z_rot_speed = vector_3d_dot_product(self->rotation[Z], mean_av);
+
+#if (FL_KALMAN_ANGULAR_VELOCITY)
+ rotation_filter_new_rot_speed(&self->rotation_filter, time, z_rot_speed, SQUARE(FL_DEFAULT_SPIN_SIGMA));
+#else
+ self->z_rot_speed = z_rot_speed;
+#endif
+ result = true;
+ LOG_FUSED_DEV(DBG_LOW, FUNCTION_PREFIX("ANGVEL(), t=%.16e, dt=%.16e, wz=%.16e, wze=%.16e, m0=%.16e, m1=%.16e, m2=%.16e, s0=%.16e, s1=%.16e, s2=%.16e"),
+ time, dt, z_rot_speed, orientation_get_z_rot_speed(self),
+ mean_av[0], mean_av[1], mean_av[2], self->rotation[Z][0], self->rotation[Z][1], self->rotation[Z][2]);
+
+ if (vector_3d_length(mean_av) > 0) {
+ /* Integrate over dt: We use the fact that for any scalar t the quaternions commute.
+ * [exp(w), exp(tw)] = 0
+ */
+ Quaternion qwt;
+ quaternion_from_rotation_vector(qwt, mean_av, dt);
+ LOG_FUSED_DEV(DBG_LOW, FUNCTION_PREFIX("ANGVEL(), t=%.16e, qw=%.16e, qx=%.16e, qy=%.16e, qz=%.16e"),
+ time, qwt[QTR_0], qwt[QTR_X], qwt[QTR_Y], qwt[QTR_Z]);
+
+ Vector_3d v;
+ vector_3d_cross_vector_3d(self->last_angular_velocity, angular_velocity, v);
+ const double dt2 = dt * dt / 24;
+ qwt[QTR_X] += dt2 * v[X];
+ qwt[QTR_Y] += dt2 * v[Y];
+ qwt[QTR_Z] += dt2 * v[Z];
+ LOG_FUSED_DEV(DBG_LOW, FUNCTION_PREFIX("ANGVEL(), t=%.16e, v0=%.16e, v1=%.16e, v2=%.16e, qw=%.16e, qx=%.16e, qy=%.16e, qz=%.16e, ql=%.16e"),
+ time, v[0], v[1], v[2], qwt[QTR_0], qwt[QTR_X], qwt[QTR_Y], qwt[QTR_Z], quaternion_norm(qwt));
+
+ quaternion_normalize(qwt);
+ LOG_FUSED_DEV(DBG_LOW, FUNCTION_PREFIX("ANGVEL(), t=%.16e, qw=%.16e, qx=%.16e, qy=%.16e, qz=%.16e"),
+ time, qwt[QTR_0], qwt[QTR_X], qwt[QTR_Y], qwt[QTR_Z]);
+
+ Matrix_3d dsr;
+ quaternion_to_rotation_matrix(qwt, dsr);
+ LOG_FUSED_DEV(DBG_LOW, FUNCTION_PREFIX("ANGVEL(), t=%.16e, r00=%.16e, r01=%.16e, r02=%.16e, r10=%.16e, r11=%.16e, r12=%.16e, r20=%.16e, r21=%.16e, r22=%.16e"),
+ time, dsr[0][0], dsr[0][1], dsr[0][2], dsr[1][0], dsr[1][1], dsr[1][2], dsr[2][0], dsr[2][1], dsr[2][2]);
+
+ Matrix_3d new_rot;
+ matrix_3d_multiply_matrix_3d(self->rotation, dsr, new_rot);
+ matrix_3d_copy(self->rotation, new_rot);
+ LOG_FUSED_DEV(DBG_LOW, FUNCTION_PREFIX("ANGVEL(), t=%.16e, s0=%.16e, s1=%.16e, s2=%.16e"), time, self->rotation[Z][0], self->rotation[Z][1], self->rotation[Z][2]);
+ }
+ }
+ }
+ vector_3d_copy(self->last_angular_velocity, angular_velocity);
+ self->last_angular_velocity_time = time;
+ return result;
+}
+
+/**
+ * @brief Extrapolate the system rotation (__sp) to the given time assuming constant angular velocity.
+ * @param[in] t Internal time
+ */
+static void orientation_predict_rw(Orientation* self, double time)
+{
+ if (KNOWN_TIME(self->last_angular_velocity_time)) {
+ const double dt = time - self->last_angular_velocity_time;
+
+ if (dt > 0) {
+ const double w = vector_3d_length(self->last_angular_velocity);
+
+ if (w > 0) {
+ Quaternion qwt;
+ quaternion_from_rotation_vector(qwt, self->last_angular_velocity, dt);
+ Matrix_3d dsr;
+ quaternion_to_rotation_matrix(qwt, dsr);
+ Matrix_3d new_rot;
+ matrix_3d_multiply_matrix_3d(self->rotation, dsr, new_rot);
+ matrix_3d_copy(self->rotation, new_rot);
+ }
+ }
+ }
+}
+
+void orientation_new_acceleration(Orientation* self, double time, const_Vector_3d_ref acc)
+{
+ orientation_predict_rw(self, time);
+ Vector_3d gacc; /* Acceleration in global (tangent) frame */
+ matrix_3d_multiply_vector_3d(self->rotation, acc, gacc);
+ LOG_FUSED_DEV(DBG_LOW, FUNCTION_PREFIX("ACC(), t=%.16e, acc0=%.16e, acc1=%.16e, acc2=%.16e, gacc0=%.16e, gacc1=%.16e, gacc2=%.16e, angle=%.16e"),
+ time, acc[0], acc[1], acc[2], gacc[0], gacc[1], gacc[2], vector_3d_angle(gacc, gravity));
+ Vector_3d gu;
+ vector_3d_copy(gu, gacc);
+
+#if (FL_ENABLE_DEVEL_LOG)
+ Vector_3d gn; /* Gravity in device frame */
+ /* rotate back to device frame */
+ VECTOR_DOT_MATRIX_3D(gu, self->rotation, gn);
+ LOG_FUSED_DEV(DBG_LOW, FUNCTION_PREFIX("ACC(), t=%.16e, gn0=%.16e, gn1=%.16e, gn2=%.16e"),
+ time, gn[0], gn[1], gn[2]);
+#endif
+ /* update rotation matrix */
+ Matrix_3d new_rot;
+ double accLen = vector_3d_length(gu);
+ if (accLen > 0) {
+ Quaternion q;
+ q[QTR_0] = accLen + gu[Z];
+ q[QTR_X] = gu[Y];
+ q[QTR_Y] = -gu[X];
+ q[QTR_Z] = 0;
+ quaternion_normalize(q);
+ Vector_3d rot_vect;
+ quaternion_get_rotation_vector(q, rot_vect);
+ quaternion_from_rotation_vector(q, rot_vect, 0.01);
+ Matrix_3d m;
+ quaternion_to_rotation_matrix(q, m);
+ matrix_3d_transpose(m);
+ matrix_3d_multiply_matrix_3d(m, self->rotation, new_rot);
+ matrix_3d_copy(self->rotation, new_rot);
+ }
+
+ LOG_FUSED_DEV(DBG_LOW, FUNCTION_PREFIX("ACC(), time=%.16e, s0=%.16e, s1=%.16e, s2=%.16e"),
+ time, self->rotation[Z][0], self->rotation[Z][1], self->rotation[Z][2]);
+}
+
+void orientation_get_orientation(Orientation* self, double* o)
+{
+ Quaternion q;
+ quaternion_from_rotation_matrix(q, self->rotation);
+ if (q[QTR_0] < 0) {
+ o[0] = q[QTR_X];
+ o[1] = q[QTR_Y];
+ o[2] = q[QTR_Z];
+ } else {
+ o[0] = -q[QTR_X];
+ o[1] = -q[QTR_Y];
+ o[2] = -q[QTR_Z];
+ }
+}
+
+void quaternion_normalize(Quaternion_ref self)
+{
+ double qwtn = quaternion_norm(self);
+ if (qwtn > 0) {
+ double norm_qwt = 1.0 / qwtn;
+
+ self[QTR_0] *= norm_qwt;
+ self[QTR_X] *= norm_qwt;
+ self[QTR_Y] *= norm_qwt;
+ self[QTR_Z] *= norm_qwt;
+ }
+}
+
+double quaternion_get_rotation_angle(const_Quaternion_ref self)
+{
+ return 2 * acos(fabs(self[QTR_0]));
+}
+
+void quaternion_from_rotation_vector(Quaternion_ref self, const_Vector_3d_ref rot_vector, double scale)
+{
+ double w = vector_3d_length(rot_vector);
+ if (w > 0) {
+ double wt = w * scale * 0.5;
+ double swt = sin(wt) / w;
+ self[QTR_0] = cos(wt);
+ self[QTR_X] = swt * rot_vector[X];
+ self[QTR_Y] = swt * rot_vector[Y];
+ self[QTR_Z] = swt * rot_vector[Z];
+ } else {
+ self[QTR_0] = 1;
+ self[QTR_X] = 0;
+ self[QTR_Y] = 0;
+ self[QTR_Z] = 0;
+ }
+}
+
+void quaternion_get_rotation_vector(const_Quaternion_ref self, Vector_3d_ref rot)
+{
+ double length = sqrt(SQUARE(self[QTR_X]) + SQUARE(self[QTR_Y]) + SQUARE(self[QTR_Z]));
+
+ if (length == 0) {
+ vector_3d_clear(rot);
+ } else {
+ double l = self[QTR_0] >= 0
+ ? quaternion_get_rotation_angle(self) / length
+ : -quaternion_get_rotation_angle(self) / length;
+
+ vector_3d_set(rot, self[QTR_X] * l, self[QTR_Y] * l, self[QTR_Z] * l);
+ }
+}
+
+void quaternion_to_rotation_matrix(const_Quaternion_ref self, Matrix_3d_ref rot_matrix)
+{
+ double xx = 2 * self[QTR_X] * self[QTR_X];
+ double xy = 2 * self[QTR_X] * self[QTR_Y];
+ double xz = 2 * self[QTR_X] * self[QTR_Z];
+ double xw = 2 * self[QTR_X] * self[QTR_0];
+ double yy = 2 * self[QTR_Y] * self[QTR_Y];
+ double yz = 2 * self[QTR_Y] * self[QTR_Z];
+ double yw = 2 * self[QTR_Y] * self[QTR_0];
+ double zz = 2 * self[QTR_Z] * self[QTR_Z];
+ double zw = 2 * self[QTR_Z] * self[QTR_0];
+ rot_matrix[X][X] = 1 - yy - zz;
+ rot_matrix[X][Y] = xy + zw;
+ rot_matrix[X][Z] = xz - yw;
+
+ rot_matrix[Y][X] = xy - zw;
+ rot_matrix[Y][Y] = 1 - xx - zz;
+ rot_matrix[Y][Z] = yz + xw;
+
+ rot_matrix[Z][X] = xz + yw;
+ rot_matrix[Z][Y] = yz - xw;
+ rot_matrix[Z][Z] = 1 - xx - yy;
+}
+
+void quaternion_from_rotation_matrix(Quaternion_ref self, const_Matrix_3d_ref m)
+{
+ double qw;
+ double qx;
+ double qy;
+ double qz;
+ double tr = m[0][0] + m[1][1] + m[2][2];
+
+ if (tr > 0) {
+ double s = sqrt(tr + 1.0) * 2; /* s = 4 * qw */
+ qw = 0.25 * s;
+ qx = (m[2][1] - m[1][2]) / s;
+ qy = (m[0][2] - m[2][0]) / s;
+ qz = (m[1][0] - m[0][1]) / s;
+ } else if ((m[0][0] > m[1][1]) & (m[0][0] > m[2][2])) {
+ double s = sqrt(1.0 + m[0][0] - m[1][1] - m[2][2]) * 2; /* s = 4 * qx */
+ qw = (m[2][1] - m[1][2]) / s;
+ qx = 0.25 * s;
+ qy = (m[0][1] + m[1][0]) / s;
+ qz = (m[0][2] + m[2][0]) / s;
+ } else if (m[1][1] > m[2][2]) {
+ double s = sqrt(1.0 + m[1][1] - m[0][0] - m[2][2]) * 2; /* s = 4 * qy */
+ qw = (m[0][2] - m[2][0]) / s;
+ qx = (m[0][1] + m[1][0]) / s;
+ qy = 0.25 * s;
+ qz = (m[1][2] + m[2][1]) / s;
+ } else {
+ double s = sqrt(1.0 + m[2][2] - m[0][0] - m[1][1]) * 2; /* s = 4 * qz */
+ qw = (m[1][0] - m[0][1]) / s;
+ qx = (m[0][2] + m[2][0]) / s;
+ qy = (m[1][2] + m[2][1]) / s;
+ qz = 0.25 * s;
+ }
+
+ double n = sqrt(qw * qw + qx * qx + qy * qy + qz * qz);
+ if (qw >= 0) {
+ self[QTR_0] = qw / n;
+ self[QTR_X] = -qx / n;
+ self[QTR_Y] = -qy / n;
+ self[QTR_Z] = -qz / n;
+ } else {
+ self[QTR_0] = -qw / n;
+ self[QTR_X] = qx / n;
+ self[QTR_Y] = qy / n;
+ self[QTR_Z] = qz / n;
+ }
+}
+
+void rotation_filter_init(RotationFilter* self, double rot_var)
+{
+ self->def_rot_var = rot_var;
+ rotation_filter_reset(self);
+}
+
+void rotation_filter_reset(RotationFilter* self)
+{
+ self->rot_speed = 0;
+ self->rot_speed_variance = self->def_rot_var;
+ self->rot_speed_time = FL_UNDEFINED_TIME;
+ self->last_azimuth = 0;
+ self->last_azimuth_time = FL_UNDEFINED_TIME;
+}
+
+void rotation_filter_new_rot_speed(RotationFilter* self, double time, double rot_speed, double rot_speed_variance)
+{
+ if (KNOWN_TIME(self->rot_speed_time)) {
+ const double dt = time - self->rot_speed_time;
+
+ /* propagate/diffuse sigma^2 */
+ const double s2wzp = self->rot_speed_variance + dt * SQUARE(FL_DEFAULT_SPIN_SIGMA);
+ const double s2wzk = s2wzp + rot_speed_variance;
+ if (s2wzk > 0) {
+ const double inv_s2k = 1.0 / s2wzk;
+ LOG_FUSED_DEV(DBG_INFO, FUNCTION_PREFIX("(t=" TIME_FORMAT ", w=%.4g, s2w=%.4g): __wze:%g->%g, __s2wze:%g->%g"),
+ time, rot_speed, rot_speed_variance, self->rot_speed,
+ inv_s2k * (s2wzp * rot_speed + rot_speed_variance * self->rot_speed),
+ self->rot_speed_variance, inv_s2k * s2wzp * rot_speed_variance * 2);
+
+ self->rot_speed = inv_s2k * (s2wzp * rot_speed + rot_speed_variance * self->rot_speed);
+ self->rot_speed_variance = inv_s2k * s2wzp * rot_speed_variance * 2;
+ } else {
+ self->rot_speed = rot_speed;
+ self->rot_speed_variance = rot_speed_variance;
+ }
+ } else {
+ self->rot_speed = rot_speed;
+ self->rot_speed_variance = rot_speed_variance;
+ }
+
+ self->rot_speed_time = time;
+}
+
+void rotation_filter_new_azimuth(RotationFilter* self, double time, double azimuth, double vhe, double speed)
+{
+ if (speed > 0 && KNOWN_TIME(self->last_azimuth_time)) {
+ const double dt = time - self->last_azimuth_time;
+
+ if (speed * vhe * dt > 0) {
+ /* circular difference */
+ double db = azimuth - self->last_azimuth;
+ if (db > M_PI) {
+ db -= M_2PI;
+ } else if (db < -M_PI) {
+ db += M_2PI;
+ }
+ const double rot_speed = db / dt;
+ const double rot_speed_variance = 4 * fl_square(atan2(2 * sqrt(speed * vhe), FL_DEFAULT_VELOCITY_SIGMA));
+ rotation_filter_new_rot_speed(self, time, rot_speed, rot_speed_variance);
+ }
+ }
+
+ self->last_azimuth = azimuth;
+ self->last_azimuth_time = time;
+}
diff --git a/lbs-server/src/fused/Orientation.h b/lbs-server/src/fused/Orientation.h
new file mode 100644
index 0000000..5d33972
--- /dev/null
+++ b/lbs-server/src/fused/Orientation.h
@@ -0,0 +1,177 @@
+/*
+ * Copyright (c) 2016-2017 Samsung Electronics Co., Ltd.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/**
+ * @file Orientation.h
+ * @brief Orientation filter.
+ */
+
+#pragma once
+#ifndef __ORIENTATION_H_
+#define __ORIENTATION_H_
+
+#include <stdbool.h>
+#include "Vector.h"
+#include "Matrix.h"
+#include "math-ext.h"
+#include "AccelerometerFilter.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+
+typedef struct {
+ double def_rot_var; /* Initial variance of z rotation speed. */
+ double rot_speed; /* Angular z rotation speed (horizontal plane). */
+ double rot_speed_variance; /* Z rotation speed variance. */
+ double rot_speed_time; /* Time of last z rotation speed update. */
+ double last_azimuth; /* Last reported azimuth (North bearing). */
+ double last_azimuth_time; /* Time of last azimuth update. */
+} RotationFilter;
+
+typedef struct {
+ Matrix_3d rotation; /* Current rotation matrix */
+ double weight2; /* Square weight (inverse covariance) vector of matrix rows */
+ Vector_3d last_angular_velocity; /* Last reported angular velocity. */
+ double last_angular_velocity_time; /* Time when last angular velocity was reported. */
+ AccelerometerFilter acc_filter; /* Accelerometer filter. */
+#if (FL_KALMAN_ANGULAR_VELOCITY)
+ RotationFilter rotation_filter; /* Filters z-rotation speed. */
+#else
+ double z_rot_speed; /* Angular z rotation speed (horizontal plane). */
+#endif
+} Orientation;
+
+extern void orientation_init(Orientation* self,
+ MotionDetectorStateChangeListener on_motion_state_changed, double var_rot);
+
+extern void orientation_exit(Orientation* self);
+extern void orientation_reset(Orientation* self);
+
+#if (FL_KALMAN_ANGULAR_VELOCITY)
+
+/** (double) */
+#define orientation_get_z_rot_speed(self) rotation_filter_get_rot_speed(&(self)->rotation_filter)
+
+#define orientation_new_azimuth(self, time, azimuth, vhe, speed) rotation_filter_new_azimuth(&(self)->rotation_filter, time, azimuth, vhe, speed)
+
+#else
+
+/** (double) */
+#define orientation_get_z_rot_speed(self) ((self)->z_rot_speed)
+
+#endif
+
+/**
+ * @brief Update system rotation matrix given a measurement of the device angular velocity.
+ * @param[in] t Internal time of the measurement
+ * @param[in] wm 3D vector of angular velocity [rad/s] in device coordinate frame
+ * @param[in] s2wm Estimated variance of the measuremnt
+ */
+extern bool orientation_new_angular_velocity(Orientation* self,
+ double time, const_Vector_3d_ref angular_velocity, double s2wm);
+
+extern void orientation_new_acceleration(Orientation* self, double time, const_Vector_3d_ref acceleration);
+
+extern void orientation_get_orientation(Orientation* self, double* o);
+
+/** Labels for quaternion components. */
+typedef enum {
+ QTR_X,
+ QTR_Y,
+ QTR_Z,
+ QTR_0,
+ QUATERNION_DIMENSION
+} _quaternion_component;
+
+typedef double Quaternion[QUATERNION_DIMENSION];
+typedef double* Quaternion_ref;
+typedef const double* const_Quaternion_ref;
+
+/**
+ * @brief Returns quaternion norm.
+ * @param[in] self Input quaternion.
+ * @return (double) Norm of quaternion.
+ */
+#define quaternion_norm(self) sqrt(SQUARE(self[QTR_0]) + SQUARE(self[QTR_X]) + SQUARE(self[QTR_Y]) + SQUARE(self[QTR_Z]))
+
+/** Normalizes quaternion */
+extern void quaternion_normalize(Quaternion_ref self);
+
+/**
+ * @brief Returns rotation angle assuming quaternion represents rotation.
+ * @param[in] self Input quaternion. Must be normed.
+ */
+extern double quaternion_get_rotation_angle(const_Quaternion_ref self);
+
+/**
+ * @brief Calculates quaternion representing rotation multiplied by some number.
+ * @param[out] self Output quaternion.
+ * @param[in] rot_vector Rotation vector.
+ * @param[in] scale Scale to multiply rotation vector.
+ */
+extern void quaternion_from_rotation_vector(Quaternion_ref self, const_Vector_3d_ref rot_vector, double scale);
+
+/**
+ * @brief Returns rotation vector assuming quaternion represents rotation.
+ * @param[in] self Input quaternion. Must be normed.
+ * @param[out] rot Rotation vector representing same rotation as input quaternion.
+ */
+extern void quaternion_get_rotation_vector(const_Quaternion_ref self, Vector_3d_ref rot);
+
+/**
+ * @brief Maps a quaternion into system rotation matrix assuming quaternion represents rotation.
+ * @param[in] self Input quaternion. Must be normed.
+ * @param[out] rot_matrix Corresponding system rotation 3D matrix
+ */
+extern void quaternion_to_rotation_matrix(const_Quaternion_ref self, Matrix_3d_ref rot_matrix);
+
+/**
+ * @brief Creates quaternion representing rotation from given rotation matrix.
+ * @param m 3D rotation matrix.
+ * @return Normed quaternion representing rotation with nonnegative scalar part.
+ */
+extern void quaternion_from_rotation_matrix(Quaternion_ref self, const_Matrix_3d_ref m);
+
+/**
+ * @brief Initialization of the filter (constructor).
+ * @param[in] self Object pointer
+ * @param[in] rot_var Default rotation variance.
+ */
+extern void rotation_filter_init(RotationFilter* self, double rot_var);
+
+/** Resets filter to initial state */
+extern void rotation_filter_reset(RotationFilter* self);
+
+/**
+ * @brief Axial (1D) Kalman blending of angular velocity along one axis (turn)
+ * @param[in] time Internal time
+ * @param[in] rot_speed Angular velocity projection on the axis.
+ * @param[in] rot_speed_variance Corresponding variance.
+ */
+extern void rotation_filter_new_rot_speed(RotationFilter* self, double time, double rot_speed, double rot_speed_variance);
+
+extern void rotation_filter_new_azimuth(RotationFilter* self, double time, double azimuth, double vhe, double speed);
+
+/** (double) Returns current rotation speed along axis */
+#define rotation_filter_get_rot_speed(self) ((self)->rot_speed)
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* __ORIENTATION_H_ */
diff --git a/lbs-server/src/fused/PeaceDetector.c b/lbs-server/src/fused/PeaceDetector.c
new file mode 100644
index 0000000..a0c2c96
--- /dev/null
+++ b/lbs-server/src/fused/PeaceDetector.c
@@ -0,0 +1,57 @@
+/*
+ * Copyright (c) 2016-2017 Samsung Electronics Co., Ltd.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "PeaceDetector.h"
+#include "Conversions.h"
+
+void peace_detector_init(PeaceDetector *self, double stable_level, long stable_number)
+{
+ self->stable_level = stable_level;
+ self->stable_number = stable_number;
+ peace_detector_reset(self);
+}
+
+void peace_detector_reset(PeaceDetector *self)
+{
+ self->stable_counter = self->stable_number;
+ self->last_timestamp = FL_UNDEFINED_TIME;
+}
+
+bool peace_detector_is_stable(PeaceDetector *self)
+{
+ return self->stable_counter <= 0;
+}
+
+bool peace_detector_new_data(PeaceDetector *self, double timestamp, const_Vector_3d_ref data)
+{
+ if ((!KNOWN_TIME(self->last_timestamp)) || self->last_timestamp > timestamp) {
+ self->stable_counter = self->stable_number;
+ } else {
+ const double length = vector_3d_distance(self->previous_measurement, data);
+
+ if (length > self->stable_level) {
+ self->stable_counter = self->stable_counter < 0 ? 0 : self->stable_number;
+ } else {
+ self->stable_counter = self->stable_counter > 1 ? self->stable_counter - 1 : -1;
+ }
+ }
+ self->last_timestamp = timestamp;
+ if (!peace_detector_is_stable(self)) {
+ vector_3d_copy(self->previous_measurement, data);
+ return false;
+ }
+ return true;
+}
diff --git a/lbs-server/src/fused/PeaceDetector.h b/lbs-server/src/fused/PeaceDetector.h
new file mode 100644
index 0000000..92cf06f
--- /dev/null
+++ b/lbs-server/src/fused/PeaceDetector.h
@@ -0,0 +1,38 @@
+/*
+ * Copyright (c) 2016-2017 Samsung Electronics Co., Ltd.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#pragma once
+#ifndef __PEACEDETECTOR_H_
+#define __PEACEDETECTOR_H_
+
+#include "Vector.h"
+#include <stdbool.h>
+
+
+typedef struct {
+ double stable_level; /* Noise level below which the data are treated as stable. */
+ long stable_number; /* Number of stable measurements to treat data as stable. */
+ long stable_counter; /* Down counter of stable data. < 0 means stable state. */
+ Vector_3d previous_measurement; /* Previous measurement. */
+ double last_timestamp; /* Timestamp of last measurement. */
+} PeaceDetector;
+
+extern void peace_detector_init(PeaceDetector *self, double stable_level, long stable_number);
+extern void peace_detector_reset(PeaceDetector *self);
+extern bool peace_detector_is_stable(PeaceDetector *self);
+extern bool peace_detector_new_data(PeaceDetector *self, double timestamp, const_Vector_3d_ref data);
+
+#endif /* __PEACEDETECTOR_H_ */
diff --git a/lbs-server/src/fused/TangentFrame.c b/lbs-server/src/fused/TangentFrame.c
new file mode 100644
index 0000000..c042897
--- /dev/null
+++ b/lbs-server/src/fused/TangentFrame.c
@@ -0,0 +1,125 @@
+/*
+ * Copyright (c) 2016-2017 Samsung Electronics Co., Ltd.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "TangentFrame.h"
+#include "Conversions.h"
+
+#include <memory.h>
+
+#define globalToTangent(tf, g, t) matrix_3d_multiply_vector_3d((tf)->fsr, g, t)
+#define tangentToGlobal(tf, t, g) vector_3d_multiply_matrix_3d(t, (tf)->fsr, g)
+
+void earth_global_to_spherical(const_Vector_3d_ref p, double* latitude, double* longitude, double* altitude)
+{
+ const double yz2 = fl_square(p[Y]) + fl_square(p[Z]);
+ const double ry = atan2(p[Y], p[Z]);
+ *longitude = conversions_radians_to_longitude(ry);
+
+ const double xyz2 = yz2 + fl_square(p[X]);
+ const double rx = atan2(p[X], sqrt(yz2));
+ *latitude = conversions_radians_to_latitude(rx);
+
+ *altitude = sqrt(xyz2) - EARTH_RADIUS;
+}
+
+void earth_spherical_to_global(double latitude, double longitude, double radius, Vector_3d_ref p)
+{
+ const double rx = conversions_degrees_to_radians(latitude);
+ const double ry = conversions_degrees_to_radians(longitude);
+ const double sin_rx = radius * sin(rx);
+ const double cos_rx = radius * cos(rx);
+ vector_3d_set(p, sin_rx, cos_rx * sin(ry), cos_rx * cos(ry));
+}
+
+void tangent_frame_init(TangentFrame* self)
+{
+ memset(self, 0, sizeof(*self));
+ self->fsr[0][0] = 1;
+ self->fsr[1][1] = 1;
+ self->fsr[2][2] = 1;
+ self->cos_lat = 1;
+}
+
+void tangent_frame_create(TangentFrame* self, Vector_3d_ref pos, Vector_3d_ref vel)
+{
+ Vector_3d p;
+ tangentToGlobal(self, pos, p);
+ Vector_3d v;
+ tangentToGlobal(self, vel, v);
+
+ matrix_3d_from_vector_3d(p, self->fsr, &self->lat, &self->lon, &self->sin_lat, &self->cos_lat);
+
+ globalToTangent(self, p, pos);
+ globalToTangent(self, v, vel);
+}
+
+/**
+ * @brief Returns 2D vector in tangent coordinate system pointing from given 2D point to the North direction.
+ * @param[in] self Tangent coordinate system.
+ * @param[in] p 2D point coordinates in tangent coordinate system.
+ * @param[out] u 2D vector pointing to the North in tangent coordinate system.
+ */
+static void tangent_frame_to_north(const TangentFrame* self, const_Vector_2d_ref p, Vector_2d_ref u)
+{
+ u[PLANAR_X] = EARTH_RADIUS * self->cos_lat - p[PLANAR_X] * self->sin_lat;
+ u[PLANAR_Y] = - p[PLANAR_Y] * self->sin_lat;
+}
+
+/**
+ * @brief Returns azimuth in tangent coordinate system from given 2D point.
+ * @param[in] self Tangent coordinate system.
+ * @param[in] p 2D point coordinates in tangent coordinate system.
+ * @return Azimuth from given 2D point, i.e angle in radians from North to the given point.
+ */
+static double tangent_frame_azimuth(const TangentFrame* self, const_Vector_2d_ref p)
+{
+ Vector_2d u;
+ tangent_frame_to_north(self, p, u);
+ return atan2(-u[PLANAR_Y], u[PLANAR_X]);
+}
+
+void tangent_frame_tangent_to_spherical(const TangentFrame* self, const_Vector_3d_ref pos,
+ double* latitude, double* longitude, double* altitude)
+{
+ Vector_3d p;
+ tangentToGlobal(self, pos, p);
+ earth_global_to_spherical(p, latitude, longitude, altitude);
+}
+
+void tangent_frame_spherical_to_tangent(const TangentFrame* self,
+ double latitude, double longitude, double radius, Vector_3d_ref pos)
+{
+ Vector_3d pg;
+ earth_spherical_to_global(latitude, longitude, radius, pg);
+ globalToTangent(self, pg, pos);
+}
+
+void tangent_frame_spherical_to_tangent_speed(const TangentFrame* self,
+ const_Vector_3d_ref pos, double speed, double azimuth, double climb, Vector_3d_ref v)
+{
+ double rb = conversions_degrees_to_radians(azimuth) - tangent_frame_azimuth(self, pos);
+ vector_3d_set(v, speed * cos(rb), speed * sin(rb), climb);
+}
+
+void tangent_frame_tangent_to_spherical_speed(const TangentFrame* self,
+ const_Vector_3d_ref pos, const_Vector_3d_ref vel, double* speed, double* direction, double* climb)
+{
+ Vector_2d to_north;
+ tangent_frame_to_north(self, pos, to_north);
+ *direction = conversions_radians_to_positive_degrees(-vector_2d_angle(vel, to_north));
+ *speed = vector_2d_length(vel);
+ *climb = vel[Z];
+}
diff --git a/lbs-server/src/fused/TangentFrame.h b/lbs-server/src/fused/TangentFrame.h
new file mode 100644
index 0000000..8a1103d
--- /dev/null
+++ b/lbs-server/src/fused/TangentFrame.h
@@ -0,0 +1,154 @@
+/*
+ * Copyright (c) 2016-2017 Samsung Electronics Co., Ltd.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/**
+ * @file TangentFrame.h
+ * @brief Tangent frame.
+ */
+
+#pragma once
+#ifndef __TANGENTFRAME_H_
+#define __TANGENTFRAME_H_
+
+#include "Matrix.h"
+#include "Vector.h"
+#include "math-ext.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/**
+ * @brief Convert spherical coordinate in radians to arc-length along the Earth's great circle in meters.
+ * @param[in] radians Angle spanned by the great arc.
+ * @return (double) Corresponding value in meters along Earth's surface.
+ */
+#define earth_radians_to_meters(radians) ((radians) * EARTH_RADIUS)
+
+/**
+ * @brief Convert arc-length in meters along the Earth's great circle to arc angle in radians.
+ * @param[in] meters Arc length in meters.
+ * @return (double) Corresponding angle in radians.
+ */
+#define earth_meters_to_radians(meters) ((meters) * (1.0 / EARTH_RADIUS));
+
+/**
+ * @brief Convert point in global coordinates to spherical coordinates.
+ * @param[in] p Represents coordinates of a point in global (Earth) coordinate system.
+ * @param[out] latitude Latitude of provided point.
+ * @param[out] longitude Longitude of provided point.
+ * @param[out] altitude Altitude of provided point.
+ */
+extern void earth_global_to_spherical(const_Vector_3d_ref p, double* latitude, double* longitude, double* altitude);
+
+/**
+ * @brief Convert point in spherical coordinates to global coordinates.
+ * @param[in] latitude Latitude.
+ * @param[in] longitude Longitude.
+ * @param[in] altitude Altitude.
+ * @param[out] p Represents coordinates of a point in global (Earth) coordinate system.
+ */
+extern void earth_spherical_to_global(double latitude, double longitude, double altitude, Vector_3d_ref p);
+
+
+
+/**
+ * Global frame coordinate system:
+ * - Center in the middle of the Earth.
+ * - OX directed to the North Pole (latitude = 90).
+ * - OY directed to the latitude = 0 and longitude = 90;
+ * - OZ directed to the latitude = 0 and longitude = 0;
+ */
+
+/**
+ * Tangent frame coordinate system:
+ * - OX directed to the North.
+ * - OY directed to the East;
+ * - OZ directed to the up;
+ */
+
+/** Tangent space T(p)E to the embedding manifold E=SxR at point p */
+typedef struct {
+ double lat; /* latitude */
+ double lon; /* longitude */
+ double sin_lat; /* sin(latitude) */
+ double cos_lat; /* cos(latitude) */
+ Matrix_3d fsr; /* push-forward (tangent to global) coordinate system rotation matrix */
+} TangentFrame;
+
+extern void tangent_frame_init(TangentFrame* self);
+
+/**
+ * @brief Create coordinate map from spherical M = S^2 x R to the tangent space TM.
+ * Notice that only two angles are being used, what means the course on the tangent plane
+ * is not straight North (x-axis), but still described by the bearing.
+ * @param[in/out] self Tangent coordinate system.
+ * @param[in/out] pos Position in old tangent frame
+ * @param[in/out] vel Velocity in old tangent frame
+ */
+extern void tangent_frame_create(TangentFrame* self, Vector_2d_ref pos, Vector_2d_ref vel);
+
+/**
+ * @brief Converts coordinates in tangent coordinate system to spherical coordinate system.
+ * @param[in] self Tangent coordinate system.
+ * @param[in] pos Coordinates of given point in tangent coordinate system.
+ * @param[out] latitude Latitude in degrees.
+ * @param[out] longitude Longitude in degrees.
+ * @param[out] altitude Altitude in meters.
+ */
+extern void tangent_frame_tangent_to_spherical(const TangentFrame* self,
+ const_Vector_3d_ref pos, double* latitude, double* longitude, double* altitude);
+
+/**
+ * @brief Converts coordinates in spherical coordinate system to tangent coordinate system.
+ * @param[in] self Tangent coordinate system.
+ * @param[in] latitude Latitude in degrees.
+ * @param[in] longitude Longitude in degrees.
+ * @param[in] radius Distance from the center of the Earth (altitude + Earth radius) in meters.
+ * @param[out] pos Coordinates of given point in tangent coordinate system.
+ */
+extern void tangent_frame_spherical_to_tangent(const TangentFrame* self,
+ double latitude, double longitude, double radius, Vector_3d_ref pos);
+
+/**
+ * @brief Converts speed in spherical coordinate system to tangent coordinate system.
+ * @param[in] self Tangent coordinate system.
+ * @param[in] pos Coordinates of location in tangent coordinate system.
+ * @param[in] speed Horizontal speed in m/s.
+ * @param[in] azimuth Bearing (angle between true North and speed direction clockwise) in radians.
+ * @param[in] climb Vertical speed in m/s (positive up).
+ * @param[out] v Speed in given location in tangent coordinate system.
+ */
+extern void tangent_frame_spherical_to_tangent_speed(const TangentFrame* self,
+ const_Vector_3d_ref pos, double speed, double azimuth, double climb, Vector_3d_ref v);
+
+/**
+ * @brief Converts speed in tangent coordinate system to spherical coordinate system.
+ * @param[in] self Tangent coordinate system.
+ * @param[in] pos Coordinates of location in tangent coordinate system.
+ * @param[in] vel Speed in given location in tangent coordinate system.
+ * @param[out] speed Horizontal speed in m/s.
+ * @param[out] azimuth Bearing (angle between true North and speed direction clockwise) in radians.
+ * @param[out] climb Vertical speed in m/s (positive up).
+ */
+extern void tangent_frame_tangent_to_spherical_speed(const TangentFrame* self,
+ const_Vector_3d_ref pos, const_Vector_3d_ref vel, double* speed, double* azimuth, double* climb);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* __TANGENTFRAME_H_ */
diff --git a/lbs-server/src/fused/TimeOffset.c b/lbs-server/src/fused/TimeOffset.c
new file mode 100644
index 0000000..87af4e3
--- /dev/null
+++ b/lbs-server/src/fused/TimeOffset.c
@@ -0,0 +1,87 @@
+/*
+ * Copyright (c) 2016-2017 Samsung Electronics Co., Ltd.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "TimeOffset.h"
+#include "Conversions.h"
+#include "parameters.h"
+
+void time_offset_init(TimeOffset* self)
+{
+ moving_average_1d_filter_init(&self->filter, 1 << FL_TIMO_AEMA_PLATEAU_BITS);
+ time_offset_reset(self);
+}
+
+void time_offset_reset(TimeOffset* self)
+{
+ moving_average_1d_filter_reset(&self->filter);
+ self->first_reference_time = FL_UNDEFINED_TIME;
+ self->last_reference_time = FL_UNDEFINED_TIME;
+ self->reference_count = 0;
+ self->correct_time = FL_UNDEFINED_TIME;
+}
+
+void time_offset_exit(TimeOffset* self)
+{
+ moving_average_1d_filter_exit(&self->filter);
+}
+
+static void time_offset_new(TimeOffset* self, double dt)
+{
+ moving_average_1d_filter_process(&self->filter, dt);
+}
+
+void time_offset_reference_time(TimeOffset* self, double time)
+{
+ if (self->reference_count == 0) {
+ self->first_reference_time = time;
+ }
+ self->last_reference_time = time;
+ self->reference_count++;
+
+ if (KNOWN_TIME(self->correct_time)) {
+ const double dt = self->last_reference_time - (self->correct_time - time_offset_get(self));
+ if (dt < 0) {
+ time_offset_new(self, self->correct_time - self->last_reference_time);
+ }
+ }
+}
+
+double time_offset_correct_time(TimeOffset* self, double time)
+{
+ self->correct_time = time;
+ if (KNOWN_TIME(self->last_reference_time)) {
+ double dt = self->correct_time - time_offset_get(self)
+ - self->last_reference_time;
+ if (dt < 0) {
+ time_offset_new(self, self->correct_time - self->last_reference_time);
+ dt = 0;
+ } else if (self->reference_count > 1) {
+ const double mean_sensor_dt = (self->last_reference_time - self->first_reference_time) / (self->reference_count - 1);
+ if (dt > mean_sensor_dt) {
+ time_offset_new(self, self->correct_time - self->last_reference_time - mean_sensor_dt);
+ dt = mean_sensor_dt;
+ }
+ }
+ return self->last_reference_time + dt;
+ }
+
+ return self->correct_time;
+}
+
+double time_offset_get(const TimeOffset* self)
+{
+ return moving_average_1d_filter_get_estimate(&self->filter);
+}
diff --git a/lbs-server/src/fused/TimeOffset.h b/lbs-server/src/fused/TimeOffset.h
new file mode 100644
index 0000000..4f74491
--- /dev/null
+++ b/lbs-server/src/fused/TimeOffset.h
@@ -0,0 +1,65 @@
+/*
+ * Copyright (c) 2016-2017 Samsung Electronics Co., Ltd.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/**
+ * @file TimeOffset.h
+ * @brief Time offset.
+ */
+
+#pragma once
+#ifndef __TIMEOFFSET_H_
+#define __TIMEOFFSET_H_
+
+#include "MovingAverageFilters.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+
+typedef struct {
+ MovingAverage1dFilter filter;
+ double first_reference_time; /* Arrival time of the first sensor event. */
+ double last_reference_time; /* Arrival time of the last sensor event. */
+ unsigned reference_count; /* At 100Hz rate of arrival this will suffice for ~32 years. */
+ double correct_time; /* Arrival time of the last position measurement event. */
+} TimeOffset;
+
+extern void time_offset_init(TimeOffset* self);
+extern void time_offset_reset(TimeOffset* self);
+extern void time_offset_exit(TimeOffset* self);
+
+/**
+ * @brief Supplies reference time
+ * @param[in] time Reference time in [s].
+ */
+extern void time_offset_reference_time(TimeOffset* self, double time);
+
+/*
+ * @brief Convert location timestamp to internal time in seconds.
+ * The time-offset is adjusted if necessary to ensure temporal ordering of events.
+ * @param[in] time Location time
+ * @return Internal time.
+ */
+extern double time_offset_correct_time(TimeOffset* self, double time);
+
+extern double time_offset_get(const TimeOffset* self);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* __TIMEOFFSET_H_ */
diff --git a/lbs-server/src/fused/Vector.c b/lbs-server/src/fused/Vector.c
new file mode 100644
index 0000000..77afc60
--- /dev/null
+++ b/lbs-server/src/fused/Vector.c
@@ -0,0 +1,48 @@
+/*
+ * Copyright (c) 2016-2017 Samsung Electronics Co., Ltd.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "Vector.h"
+
+void vector_3d_normalization(Vector_3d_ref self)
+{
+ double len = vector_3d_length(self);
+ if (len > 0) {
+ double out = 1.0 / len;
+ self[X] *= out;
+ self[Y] *= out;
+ self[Z] *= out;
+ }
+}
+
+double vector_3d_distance(const_Vector_3d_ref p, const_Vector_3d_ref q)
+{
+ return sqrt(fl_square(p[X] - q[X]) + fl_square(p[Y] - q[Y]) + fl_square(p[Z] - q[Z]));
+}
+
+void vector_3d_linear_combination(double a1, const_Vector_3d_ref v1,
+ double a2, const_Vector_3d_ref v2, Vector_3d_ref v)
+{
+ v[X] = a1 * v1[X] + a2 * v2[X];
+ v[Y] = a1 * v1[Y] + a2 * v2[Y];
+ v[Z] = a1 * v1[Z] + a2 * v2[Z];
+}
+
+double vector_3d_angle(const_Vector_3d_ref v1, const_Vector_3d_ref v2)
+{
+ Vector_3d v;
+ vector_3d_cross_vector_3d(v1, v2, v);
+ return atan2(vector_3d_length(v), vector_3d_dot_product(v1, v2));
+}
diff --git a/lbs-server/src/fused/Vector.h b/lbs-server/src/fused/Vector.h
new file mode 100644
index 0000000..b375f8e
--- /dev/null
+++ b/lbs-server/src/fused/Vector.h
@@ -0,0 +1,201 @@
+/*
+ * Copyright (c) 2016-2017 Samsung Electronics Co., Ltd.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/**
+ * @file Vector.h
+ * @brief vector.
+ */
+
+#pragma once
+#ifndef __VECTOR_H_
+#define __VECTOR_H_
+
+#include "math-ext.h"
+
+#include <memory.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/** Labels for 3D vectors indexes. */
+typedef enum {
+ X,
+ Y,
+ Z,
+ GEO_DIMENSION
+} fl_geo_spatial;
+
+typedef double Vector_3d[GEO_DIMENSION];
+typedef double* Vector_3d_ref;
+typedef const double* const_Vector_3d_ref;
+
+
+#define vector_3d_clear(self) memset(self, 0, sizeof(Vector_3d))
+#define vector_3d_clear_array(self) memset(self, 0, sizeof(self))
+
+/**
+ * @brief Inlined initialization of 3D vector.
+ * @param[in] v 3D vector
+ * @param[in] x Initial x coordinate
+ * @param[in] y Initial y coordinate
+ * @param[in] z Initial z coordinate
+ */
+#define vector_3d_set(v, x, y, z)\
+ do {\
+ (v)[X] = x;\
+ (v)[Y] = y;\
+ (v)[Z] = z;\
+ } while (0)
+
+#define vector_3d_copy(dst, src) memcpy(dst, src, sizeof(Vector_3d))
+
+/**
+ * @brief Inlined difference of two 3D vectors.
+ * @param[in] u 3D vector
+ * @param[in] v 3D vector
+ * @param[out] out Difference of a @u and a @v
+ */
+#define vector_3d_sub_vector_3d(u, v, out)\
+ do {\
+ (out)[X] = (u)[X] - (v)[X];\
+ (out)[Y] = (u)[Y] - (v)[Y];\
+ (out)[Z] = (u)[Z] - (v)[Z];\
+ } while (0)
+
+/**
+ * @brief Inlined cross-product of two 3D vectors.
+ * @param[in] u 3D vector
+ * @param[in] v 3D vector
+ * @param[out] out Cross-product of a @u and a @v
+ */
+#define vector_3d_cross_vector_3d(u, v, out)\
+ do {\
+ (out)[X] = (u)[Y] * (v)[Z] - (u)[Z] * (v)[Y];\
+ (out)[Y] = (u)[Z] * (v)[X] - (u)[X] * (v)[Z];\
+ (out)[Z] = (u)[X] * (v)[Y] - (u)[Y] * (v)[X];\
+ } while (0)
+
+/**
+ * @brief Inlined product of 3D vector and number.
+ * @param[in] v 3D vector
+ * @param[in] d Number
+ * @param[out] out Cross-product of a @v and a @d
+ */
+#define vector_3d_mul_number(v, d, out)\
+ do {\
+ (out)[X] = (v)[X] * (d);\
+ (out)[Y] = (v)[Y] * (d);\
+ (out)[Z] = (v)[Z] * (d);\
+ } while (0)
+
+extern void vector_3d_normalization(Vector_3d_ref v);
+
+/**
+ * @brief Dot product of two 3D vectors.
+ * @param[in] u 3D vector
+ * @param[in] v 3D vector
+ * @return Scalar product of a @u and a @v.
+ */
+#define vector_3d_dot_product(u, v) ((u)[X] * (v)[X] + (u)[Y] * (v)[Y] + (u)[Z] * (v)[Z])
+
+/**
+ * @brief Squared length of a 3D vector.
+ * @param[in] x 3D vector
+ * @return Squared length of @a x, i.e. |x|^2 = x * x
+ */
+#define vector_3d_length2(x) vector_3d_dot_product(x, x)
+
+/**
+ * @brief Length of a 3D vector.
+ * @param[in] x 3D vector
+ * @return Length of @a x, i.e. |x| = sqrt(x * x)
+ */
+#define vector_3d_length(x) sqrt(vector_3d_length2(x))
+
+/**
+ * @brief Distance between two 3D vectors.
+ * @param[in] p First 3D vector
+ * @param[in] q Second 3D vector
+ * @return Euclidean distance between vectors.
+ */
+extern double vector_3d_distance(const_Vector_3d_ref p, const_Vector_3d_ref q);
+
+extern void vector_3d_linear_combination(double a1, const_Vector_3d_ref v1,
+ double a2, const_Vector_3d_ref v2, Vector_3d_ref v);
+
+/**
+ * @brief Angle between two vectors.
+ * @param[in] v1 First 3D vector
+ * @param[in] v2 Second 3D vector
+ * @return Angle between vectors in radians. Always in the range [0, PI].
+ */
+extern double vector_3d_angle(const_Vector_3d_ref v1, const_Vector_3d_ref v2);
+
+/** Labels for 2D vectors indexes. */
+typedef enum {
+ PLANAR_X,
+ PLANAR_Y,
+ PLANAR_DIMENSION
+} fl_planar;
+
+typedef double Vector_2d[PLANAR_DIMENSION];
+typedef double* Vector_2d_ref;
+typedef const double* const_Vector_2d_ref;
+
+/**
+ * @brief Dot product of two 2D vectors.
+ * @param[in] u 2D vector
+ * @param[in] v 2D vector
+ * @return (double) Dot (scalar) product of a @u and a @v.
+ */
+#define vector_2d_dot_product(u, v) ((u)[PLANAR_X] * (v)[PLANAR_X] + (u)[PLANAR_Y] * (v)[PLANAR_Y])
+
+/**
+ * @brief Cross product of two 2D vectors.
+ * @param[in] u 2D vector
+ * @param[in] v 2D vector
+ * @return (double) Cross product of a @u and a @u i.e. |u||v|sin(uv).
+ */
+#define vector_2d_cross_product(u, v) ((u)[PLANAR_X] * (v)[PLANAR_Y] - (u)[PLANAR_Y] * (v)[PLANAR_X])
+
+/**
+ * @brief Squared length of a 2D vector in Euclidean metric.
+ * @param[in] v 2D vector
+ * @return (double) Squared length of a @v, i.e. |v|^2 = v * v
+ */
+#define vector_2d_length2(v) vector_2d_dot_product(v, v)
+
+/**
+ * @brief Length of a 2D vector in Euclidean metric.
+ * @param[in] v 2D vector
+ * @return (double) Length of a @v, i.e. |v| = sqrt(v * v)
+ */
+#define vector_2d_length(v) sqrt(vector_2d_length2(v))
+
+/**
+ * @brief Angle between two vectors.
+ * @param[in] v1 First 2D vector
+ * @param[in] v2 Second 2D vector
+ * @return (double) Angle between vectors in radians.
+ */
+#define vector_2d_angle(v1, v2) atan2(vector_2d_cross_product(v1, v2), vector_2d_dot_product(v1, v2))
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* __VECTOR_H_ */
diff --git a/lbs-server/src/fused/Waema3dEstimator.c b/lbs-server/src/fused/Waema3dEstimator.c
new file mode 100644
index 0000000..853cce0
--- /dev/null
+++ b/lbs-server/src/fused/Waema3dEstimator.c
@@ -0,0 +1,59 @@
+/*
+ * Copyright (c) 2016-2017 Samsung Electronics Co., Ltd.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "Waema3dEstimator.h"
+#include "debug_util.h"
+
+void waema3d_estimator_reset(Waema3dEstimator* self, double alpha, double max_evidence, double ascale2)
+{
+ vector_3d_clear(self->wf);
+ vector_3d_clear(self->value);
+ self->max_evidence = max_evidence;
+ self->ascale2 = ascale2;
+ self->alpha = alpha;
+ self->scale2 = ascale2;
+ self->evidence = 0;
+ self->weight_norm = 1;
+}
+
+void waema3d_estimator_set(Waema3dEstimator* self, double scale2, double evidence, double weight_norm, const_Vector_3d_ref w0)
+{
+ self->scale2 = scale2;
+ self->evidence = evidence;
+ self->weight_norm = weight_norm;
+ vector_3d_copy(self->value, w0);
+}
+
+const_Vector_3d_ref waema3d_estimator_filter(Waema3dEstimator* self, const_Vector_3d_ref wm)
+{
+ const double fw2 = vector_3d_length2(self->wf);
+ const double weight = exp(-fw2 * self->scale2);
+ if (self->evidence < self->max_evidence) {
+ self->evidence += weight;
+ if (self->evidence > self->max_evidence) {
+ self->evidence = self->max_evidence;
+ }
+ self->weight_norm = 1.0 / self->evidence;
+ self->scale2 = self->ascale2 * (1.0 + fl_square(self->evidence) * self->alpha);
+ }
+ const double update_rate = weight * self->weight_norm;
+ const double decay_rate = 1.0 - update_rate;
+ LOG_FUSED_DEV(DBG_LOW, FUNCTION_PREFIX("fw2=%g, e=%g, s2=%g, w=%g"),
+ fw2, self->evidence, self->scale2, weight);
+ vector_3d_linear_combination(decay_rate, self->value, update_rate, wm, self->value);
+ vector_3d_sub_vector_3d(wm, self->value, self->wf);
+ return self->wf;
+}
diff --git a/lbs-server/src/fused/Waema3dEstimator.h b/lbs-server/src/fused/Waema3dEstimator.h
new file mode 100644
index 0000000..5903450
--- /dev/null
+++ b/lbs-server/src/fused/Waema3dEstimator.h
@@ -0,0 +1,56 @@
+/*
+ * Copyright (c) 2016-2017 Samsung Electronics Co., Ltd.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/**
+ * @file Waema3dEstimator.h
+ * @brief Weighted AEMA filter.
+ */
+
+#pragma once
+#ifndef __WAEMA3DESTIMATOR_H_
+#define __WAEMA3DESTIMATOR_H_
+
+#include "Vector.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/**
+ * @brief Weighted AEMA filter. This is generalization of the 3D AEMA filter by
+ * using 'evidence' as the sum of weights rather than sample count.
+ * The sample weights are estimated using Gaussian curve with time-decreasing variance.
+ */
+typedef struct {
+ double max_evidence;
+ double ascale2;
+ double alpha;
+ double scale2; /* = 1 / (2 variance) */
+ double evidence; /* min(accumulated weight, plateau_evidence) */
+ double weight_norm; /* = 1 / evidence */
+ Vector_3d wf;
+ Vector_3d value; /* filter output (estimated value) */
+} Waema3dEstimator;
+
+extern void waema3d_estimator_reset(Waema3dEstimator* self, double alpha, double aevidence, double ascale2);
+extern void waema3d_estimator_set(Waema3dEstimator* self, double scale2, double evidence, double weight_norm, const_Vector_3d_ref w0);
+extern const_Vector_3d_ref waema3d_estimator_filter(Waema3dEstimator* self, const_Vector_3d_ref wm);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* __WAEMA3DESTIMATOR_H_ */
diff --git a/lbs-server/src/fused/accelerometer-filter.c b/lbs-server/src/fused/accelerometer-filter.c
deleted file mode 100644
index 4647bc2..0000000
--- a/lbs-server/src/fused/accelerometer-filter.c
+++ /dev/null
@@ -1,146 +0,0 @@
-/*
- * Copyright (c) 2016-2017 Samsung Electronics Co., Ltd.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#if !defined(LOG_THRESHOLD)
- /* custom logging threshold - keep undefined on the repo */
- /* #define LOG_THRESHOLD LOG_LEVEL_TRACE */
-#endif
-
-#define __FL_ACCELEROMETER_FILTER_C__
-
-#define _USE_MATH_DEFINES
-#include <math.h>
-#include "earth.h"
-#include "lp-3d-filter.h"
-#include "gravity-estimator.h"
-#include "accelerometer-filter.h"
-#include "debug_util.h"
-
-#define DEFAULT_SAMPLING_FREQUENCY ((fl_hertz)10.0) /* [Hz] accelerometer sampling frequency */
-#define DEFAULT_ACCELERATION_SIGMA2 SQUARE(FL_ACCEL_NOISE_LEVEL) /* [(m/s^2)^2] */
-#define FREQUENCY_ESTIMATOR_UPDATE_THRESHOLD (((fl_seconds)1.0) * ((fl_hertz)1.0))
-
-void fl_accel_init(motion_changed_cb motion_changed, gpointer handler)
-{
- static const fl_acceleration_vector av = {0, 0, EARTH_GRAVITY};
-
- LOG_FUSED_DEV(DBG_LOW, ENTER_FUNCTION_PREFIX("()"), 0);
- fl_lp3d_init();
- fl_gres_init();
- fl_moti_init(motion_changed, handler);
- __frequency_estimator.samples = 0;
- __frequency_estimator.last_update_time = FL_UNDEFINED_TIME;
- fl_lp3d_reset_to(av);
- fl_accel_set_sampling_frequency(DEFAULT_SAMPLING_FREQUENCY, FL_UNDEFINED_TIME);
- LOG_FUSED_DEV(DBG_LOW, LEAVE_FUNCTION_PREFIX("()"), 0);
-}
-
-void fl_accel_exit(void)
-{
- LOG_FUSED_DEV(DBG_LOW, ENTER_FUNCTION_PREFIX("()"), 0);
- fl_moti_exit();
- fl_gres_exit();
- fl_lp3d_exit();
- LOG_FUSED_DEV(DBG_LOW, LEAVE_FUNCTION_PREFIX("()"), 0);
-}
-
-void fl_accel_reset(const_fl_acceleration_vector_ref av)
-{
- LOG_FUSED_DEV(DBG_LOW, FUNCTION_PREFIX("()"), 0);
- __frequency_estimator.samples = 0;
- __frequency_estimator.last_update_time = FL_UNDEFINED_TIME;
- fl_moti_reset();
- fl_lp3d_reset_to(av);
-}
-
-void fl_accel_set_sampling_frequency(fl_hertz f, fl_seconds t)
-{
- LOG_FUSED_DEV(DBG_LOW, FUNCTION_PREFIX("(f=%5.2f, t=%.3g)"), f, t);
- if (f <= 0) {
- LOG_FUSED_DEV(DBG_ERR, FUNCTION_PREFIX("f=%5.2f <= 0"), f);
- return;
- }
- __frequency_estimator.samples = 0;
- __frequency_estimator.last_update_time = t;
- __frequency_estimator.last_update_frequency = f;
- fl_lp3d_set_sampling_frequency(f);
-}
-
-void fl_accel_process(
- fl_seconds t,
- const_fl_acceleration_vector_ref am,
- fl_acceleration_vector_ref al,
- fl_vector_3d_ref gu,
- fl_real *w2gu)
-{
- fl_real g2;
- fl_real af2;
- const_fl_acceleration_vector_ref af;
-
- LOG_FUSED_DEV(DBG_LOW, FUNCTION_PREFIX("(t=%.2f, a=(% 5.2f, % 5.2f, % 5.2f))"), t, am[GEO_SPATIAL_X], am[GEO_SPATIAL_Y], am[GEO_SPATIAL_Z]);
- if (__frequency_estimator.last_update_time > FL_UNDEFINED_TIME) {
- fl_seconds dt;
-
- ++(__frequency_estimator.samples);
- dt = t - __frequency_estimator.last_update_time;
- if (dt > 0) {
- fl_hertz f, df;
-
- f = __frequency_estimator.samples / dt;
- df = fabs(f - __frequency_estimator.last_update_frequency);
- if (dt * df >= FREQUENCY_ESTIMATOR_UPDATE_THRESHOLD)
- fl_accel_set_sampling_frequency(f, t);
- }
- } else
- __frequency_estimator.last_update_time = t;
-
- af = fl_lp3d_process(am);
- af2 = fl_vector_3d_length2(af);
-
- /* the z-value is passed through 2nd order Butterworth, then 1st order EMA */
- fl_real overlap = fl_vector_3d_dot_product(af, am);
- g2 = fl_gres_process(overlap);
-
- LOG_FUSED_DEV(DBG_INFO, FUNCTION_PREFIX("(t=%.2f, a=(% 5.2f, % 5.2f, % 5.2f)), |af|=%5.2f, <g>=%5.2f"), t, am[GEO_SPATIAL_X], am[GEO_SPATIAL_Y], am[GEO_SPATIAL_Z], sqrt(af2), sqrt(g2));
- if (af2 > SQUARE(FL_ACCEL_NOISE_LEVEL) && g2 > 1) {
- fl_real norm_out = 1.0 / sqrt(af2);
-
- gu[GEO_SPATIAL_X] = af[GEO_SPATIAL_X] * norm_out;
- gu[GEO_SPATIAL_Y] = af[GEO_SPATIAL_Y] * norm_out;
- gu[GEO_SPATIAL_Z] = af[GEO_SPATIAL_Z] * norm_out;
- *w2gu = __gres.base.decay_rate / (fl_square(gu[GEO_SPATIAL_X]) + fl_square(gu[GEO_SPATIAL_Y]) + fl_square(gu[GEO_SPATIAL_Z] - 1) + GEO_SPATIAL_DIMENSION * DEFAULT_ACCELERATION_SIGMA2);
- } else {
- gu[GEO_SPATIAL_X] = 0;
- gu[GEO_SPATIAL_Y] = 0;
- gu[GEO_SPATIAL_Z] = 1; /* default */
- *w2gu = __gres.base.decay_rate / (GEO_SPATIAL_DIMENSION * DEFAULT_ACCELERATION_SIGMA2);
- }
-
- fl_acceleration g = sqrt(g2);
- if (g > 1) {
- fl_real calibration;
-
- calibration = EARTH_GRAVITY / g;
- al[GEO_SPATIAL_X] = calibration * am[GEO_SPATIAL_X] - EARTH_GRAVITY * gu[GEO_SPATIAL_X];
- al[GEO_SPATIAL_Y] = calibration * am[GEO_SPATIAL_Y] - EARTH_GRAVITY * gu[GEO_SPATIAL_Y];
- al[GEO_SPATIAL_Z] = calibration * am[GEO_SPATIAL_Z] - EARTH_GRAVITY * gu[GEO_SPATIAL_Z];
- } else {
- al[GEO_SPATIAL_X] = am[GEO_SPATIAL_X];
- al[GEO_SPATIAL_Y] = am[GEO_SPATIAL_Y];
- al[GEO_SPATIAL_Z] = am[GEO_SPATIAL_Z];
- }
- fl_moti_process(t, al);
-}
diff --git a/lbs-server/src/fused/accelerometer-filter.h b/lbs-server/src/fused/accelerometer-filter.h
deleted file mode 100644
index 8e70f1e..0000000
--- a/lbs-server/src/fused/accelerometer-filter.h
+++ /dev/null
@@ -1,125 +0,0 @@
-/*
- * Copyright (c) 2016-2017 Samsung Electronics Co., Ltd.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-/**
- * @file accelerometer-filter.h
- * @brief Accelerometer calibration (scale and sampling frequency), and
- * low-pass 3D filtering.
- */
-
-#pragma once
-#ifndef __FL_ACCELEROMETER_FILTER_H__
-#define __FL_ACCELEROMETER_FILTER_H__
-
-#include "types.h"
-#include "motion-detector.h"
-#include "parameters.h"
-
-#ifdef __cplusplus
-extern "C" {
-#endif
-
-/***************************************************************************//**
- * [public] Initializaton of the singleton unit (static constructor).
- *
- * @param[in] motion_changed
- * Callback to be invoked when the detected state of motion changes.
- * @param[in] handler
- * Arbitrary user parameter passed to the callback.
- */
-void fl_accel_init(motion_changed_cb motion_changed, gpointer handler);
-
-/***************************************************************************//**
- * [public] Cleanup of the singleton unit (static destructor).
- */
-void fl_accel_exit(void);
-
-/***************************************************************************//**
- * [public] Reset of the internal state back to initial one. This function is
- * used for repetitive testing and module soft restarts.
- *
- * @param[in] av
- * Stationary acceleration state to be set (typically {0, 0, g}).
- */
-void fl_accel_reset(const_fl_acceleration_vector_ref av);
-
-/***************************************************************************//**
- * [public] Adapted internal state to the given sampling frequency. Call this
- * method whenever the measured frequency changes significantly.
- *
- * @param[in] f
- * Measured accelerometer sampling frequency in Hertz.
- * @param[in] t
- * Time of the event in seconds.
- */
-void fl_accel_set_sampling_frequency(fl_hertz samplingFrequency, fl_seconds t);
-
-/***************************************************************************//**
- * [public] Processing of a single sample from the accelerometer. This function
- * performs:
- * - estimation of the sampling frequency,
- * - estimation of the gravity direction via low-pass 3D filter (cf. LP3D unit),
- * - estimation of the gravity value (cf. GRES unit) and scale correction,
- * - estimation of the linear acceleration i.e. with subtracted gravity component,
- * - motion state detection (cf. MOTI unit)
- *
- * @param[in] t
- * Time of the event in seconds.
- * @param[in] am
- * Measured 3D acceleration vector transformend to local frame.
- * @param[out] al
- * Linear acceleration in local frame.
- * @param[out] gu
- * Unit vector along gravity direction oriented upwards.
- * @param[out] w2gu
- * Estimated inverse covariance (squared weight) of @a gu.
- */
-void fl_accel_process(fl_seconds t, const_fl_acceleration_vector_ref am, fl_acceleration_vector_ref al, fl_vector_3d_ref gu, fl_real *w2gu);
-
-
-/**
- * Accelerometer calibration (scale and sampling frequency), and low-pass 3D filtering.
- */
-
-#if defined(__FL_ACCELEROMETER_FILTER_C__)
- #ifdef GTEST
- #define PRIVATE
- #else
- #define PRIVATE \
- static
- #endif
-#else
- #define PRIVATE \
- extern
-#endif
-
-typedef struct {
- unsigned samples;
- fl_seconds last_update_time;
- fl_hertz last_update_frequency;
-} _fl_accel_frequency_estimator;
-
-PRIVATE _fl_accel_frequency_estimator __frequency_estimator;
-
-#if !defined(__FL_ACCELEROMETER_FILTER_C__)
- #undef PRIVATE
-#endif
-
-#ifdef __cplusplus
-}
-#endif
-
-#endif /* __FL_ACCELEROMETER_FILTER_H__ */
diff --git a/lbs-server/src/fused/aema-estimator.c b/lbs-server/src/fused/aema-estimator.c
deleted file mode 100644
index 170abf3..0000000
--- a/lbs-server/src/fused/aema-estimator.c
+++ /dev/null
@@ -1,170 +0,0 @@
-/*
- * Copyright (c) 2016-2017 Samsung Electronics Co., Ltd.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#if !defined(LOG_THRESHOLD)
- /* custom logging threshold - keep undefined on the repo */
- /* #define LOG_THRESHOLD LOG_LEVEL_DETAIL */
-#endif
-
-#define __FL_AEMA_ESTIMATOR_C__
-
-#include <math.h>
-#include "aema-estimator.h"
-#include "debug_util.h"
-
-void __aema_init(fl_aema_estimator* self, unsigned plateau_samples)
-{
- LOG_FUSED_DEV(DBG_LOW, FUNCTION_PREFIX("[%p](bits=%u)"), self, plateau_samples);
- self->plateau_samples = plateau_samples;
- self->samples = 0;
- self->update_rate = 1;
- self->decay_rate = 0;
-}
-
-/***************************************************************************//**
- * [protected] Cleanup of the base class (destructor).
- */
-static inline void __aema_exit(fl_aema_estimator *self)
-{
- LOG_FUSED_DEV(DBG_LOW, FUNCTION_PREFIX("[%p]()"), self);
-}
-
-void fl_aema1d_init(fl_aema1d_estimator* self, unsigned plateau_samples)
-{
- __aema_init(&self->base, plateau_samples);
- self->value = 0;
-}
-
-void fl_aema2d_init(fl_aema2d_estimator* self, unsigned plateau_samples)
-{
- __aema_init(&self->base, plateau_samples);
- self->value[AXIAL_H] = 0;
- self->value[AXIAL_V] = 0;
-}
-
-void fl_aema3d_init(fl_aema3d_estimator* self, unsigned plateau_samples)
-{
- __aema_init(&self->base, plateau_samples);
- self->value[GEO_SPATIAL_X] = 0;
- self->value[GEO_SPATIAL_Y] = 0;
- self->value[GEO_SPATIAL_Z] = 0;
-}
-
-void fl_aema1d_exit(fl_aema1d_estimator *self)
-{
- __aema_exit(&self->base);
-}
-
-void fl_aema2d_exit(fl_aema2d_estimator *self)
-{
- __aema_exit(&self->base);
-}
-
-void fl_aema3d_exit(fl_aema3d_estimator *self)
-{
- __aema_exit(&self->base);
-}
-
-void __aema_reset(fl_aema_estimator* self)
-{
- LOG_FUSED_DEV(DBG_LOW, FUNCTION_PREFIX("[%p]()"), self);
- self->samples = 0;
- self->update_rate = 1;
- self->decay_rate = 0;
-}
-
-void fl_aema1d_reset(fl_aema1d_estimator* self)
-{
- __aema_reset(&self->base);
- self->value = 0;
-}
-
-void fl_aema2d_reset(fl_aema2d_estimator* self)
-{
- __aema_reset(&self->base);
- self->value[PLANAR_X] = 0;
- self->value[PLANAR_Y] = 0;
-}
-
-void fl_aema3d_reset(fl_aema3d_estimator* self)
-{
- __aema_reset(&self->base);
- self->value[GEO_SPATIAL_X] = 0;
- self->value[GEO_SPATIAL_Y] = 0;
- self->value[GEO_SPATIAL_Z] = 0;
-}
-
-fl_real fl_aema1d_process(fl_aema1d_estimator* self, fl_real value)
-{
- LOG_FUSED_DEV(DBG_LOW, FUNCTION_PREFIX("[%p](%g)"), self, value);
- __aema_process(&self->base);
- /* exponential moving avarege */
- self->value = self->base.decay_rate * self->value
- + self->base.update_rate * value;
-
- return self->value;
-}
-
-const_fl_vector_2d_ref fl_aema2d_process(fl_aema2d_estimator* self, fl_real value_H, fl_real value_V)
-{
- LOG_FUSED_DEV(DBG_LOW, FUNCTION_PREFIX("[%p](%g, %g)"), self, value_H, value_V);
-
- __aema_process(&self->base);
-#define UPDATE_VALUE(i) self->value[AXIAL_##i] = self->base.decay_rate * self->value[AXIAL_##i] + self->base.update_rate * value_##i;
- UPDATE_VALUE(H);
- UPDATE_VALUE(V);
-#undef UPDATE_VALUE
-
- return self->value;
-}
-
-const_fl_vector_3d_ref fl_aema3d_process(fl_aema3d_estimator* self, const_fl_vector_3d_ref value)
-{
- LOG_FUSED_DEV(DBG_LOW, FUNCTION_PREFIX("[%p](%g, %g, %g)"), self, value[0], value[1], value[2]);
-
- __aema_process(&self->base);
-#define UPDATE_VALUE(i) self->value[i] = self->base.decay_rate * self->value[i] + self->base.update_rate * value[i];
- UPDATE_VALUE(GEO_SPATIAL_X);
- UPDATE_VALUE(GEO_SPATIAL_Y);
- UPDATE_VALUE(GEO_SPATIAL_Z);
-#undef UPDATE_VALUE
-
- return self->value;
-}
-
-void fl_aema1d_set_estimate(fl_aema1d_estimator* self, fl_real value)
-{
- LOG_FUSED_DEV(DBG_LOW, FUNCTION_PREFIX("[%p](%g)"), self, value);
- __aema_set_estimate(&self->base);
- self->value = value;
-}
-
-void fl_aema2d_set_estimate(fl_aema2d_estimator* self, fl_real value_H, fl_real value_V)
-{
- LOG_FUSED_DEV(DBG_LOW, FUNCTION_PREFIX("[%p](%g, %g)"), self, value_H, value_V);
- __aema_set_estimate(&self->base);
- self->value[AXIAL_H] = value_H;
- self->value[AXIAL_V] = value_V;
-}
-
-void fl_aema3d_set_estimate(fl_aema3d_estimator* self, const_fl_vector_3d_ref value)
-{
- LOG_FUSED_DEV(DBG_LOW, FUNCTION_PREFIX("[%p](%g, %g, %g)"), self, value[0], value[1], value[2]);
- __aema_set_estimate(&self->base);
- self->value[GEO_SPATIAL_X] = value[GEO_SPATIAL_X];
- self->value[GEO_SPATIAL_Y] = value[GEO_SPATIAL_Y];
- self->value[GEO_SPATIAL_Z] = value[GEO_SPATIAL_Z];
-}
diff --git a/lbs-server/src/fused/aema-estimator.h b/lbs-server/src/fused/aema-estimator.h
deleted file mode 100644
index 36282f5..0000000
--- a/lbs-server/src/fused/aema-estimator.h
+++ /dev/null
@@ -1,312 +0,0 @@
-/*
- * Copyright (c) 2016-2017 Samsung Electronics Co., Ltd.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-/**
- * @file aema-estimator.h
- * @brief Adaptive Exponential Moving Average (1st order) filter
- */
-
-#pragma once
-#ifndef __FL_AEMA_ESTIMATOR_H__
-#define __FL_AEMA_ESTIMATOR_H__
-
-#include "types.h"
-
-#ifdef __cplusplus
-extern "C" {
-#endif
-
-#if defined(__FL_AEMA_ESTIMATOR_C__)
- #define INLINE \
- inline
-#else
- #define INLINE \
- static inline
-#endif
-
-typedef struct {
- unsigned plateau_samples; /* number of samples to collect before EMA kicks-in */
- unsigned samples; /* min(number of samples collected, plateau_samples) */
- fl_real update_rate; /* = 1 / samples */
- fl_real decay_rate; /* = 1 - update_rate */
-} fl_aema_estimator;
-
-typedef struct {
- fl_aema_estimator base; /* base object */
- fl_real value; /* filter output (estimated value) */
-} fl_aema1d_estimator;
-
-typedef struct {
- fl_aema_estimator base; /* base object */
- fl_vector_2d value; /* filter output (estimated value) */
-} fl_aema2d_estimator;
-
-typedef struct {
- fl_aema_estimator base; /* base object */
- fl_vector_3d value; /* filter output (estimated value) */
-} fl_aema3d_estimator;
-
-/***************************************************************************//**
- * [public] Initializaton of the 1D AEMA object (class constructor).
- *
- * @param[in] self
- * Object pointer
- * @param[in] plateau_samples
- * Number of samples to collect before switching to standard EMA mode.
- */
-void fl_aema1d_init(fl_aema1d_estimator *self, unsigned plateau_samples);
-
-/***************************************************************************//**
- * [public] Initializaton of the 2D AEMA object (class constructor).
- *
- * @param[in] self
- * Object pointer
- * @param[in] plateau_samples
- * Number of samples to collect before switching to standard EMA mode.
- */
-void fl_aema2d_init(fl_aema2d_estimator *self, unsigned plateau_samples);
-
-/***************************************************************************//**
- * [public] Initializaton of the 3D AEMA object (class constructor).
- *
- * @param[in] self
- * Object pointer
- * @param[in] plateau_samples
- * Number of samples to collect before switching to standard EMA mode.
- */
-void fl_aema3d_init(fl_aema3d_estimator *self, unsigned plateau_samples);
-
-/***************************************************************************//**
- * [public] Cleanup of the 1D AEMA object (class destructor).
- */
-void fl_aema1d_exit(fl_aema1d_estimator *self);
-
-/***************************************************************************//**
- * [public] Cleanup of the 2D AEMA object (class destructor).
- */
-void fl_aema2d_exit(fl_aema2d_estimator *self);
-
-/***************************************************************************//**
- * [public] Cleanup of the 3D AEMA object (class destructor).
- */
-void fl_aema3d_exit(fl_aema3d_estimator *self);
-
-/***************************************************************************//**
- * [public] Reset the 1D AEMA object back to initial state. This function is
- * used for repetitive testing and module soft restarts.
- *
- * @param[in] self
- * Object pointer
- */
-void fl_aema1d_reset(fl_aema1d_estimator *self);
-
-/***************************************************************************//**
- * [public] Reset the 2D AEMA object back to initial state. This function is
- * used for repetitive testing and module soft restarts.
- *
- * @param[in] self
- * Object pointer
- */
-void fl_aema2d_reset(fl_aema2d_estimator *self);
-
-/***************************************************************************//**
- * [public] Reset the 3D AEMA object back to initial state. This function is
- * used for repetitive testing and module soft restarts.
- *
- * @param[in] self
- * Object pointer
- */
-void fl_aema3d_reset(fl_aema3d_estimator *self);
-
-/***************************************************************************//**
- * [public] Processing of a single data sample.
- *
- * @param[in] self
- * Object pointer
- * @param[in] value
- * Input value to the filter.
- *
- * @return
- * Output value of the filter after processing. This is the same as
- * returned by fl_aema1d_get_estimate(self).
- */
-fl_real fl_aema1d_process(fl_aema1d_estimator *self, fl_real value);
-
-/***************************************************************************//**
- * [public] Processing of a double data sample.
- *
- * @param[in] self
- * Object pointer
- * @param[in] value_H
- * First (horizontal) input value to the filter.
- * @param[in] value_V
- * Second (vertical) input value to the filter.
- *
- * @return
- * Output value of the filter after processing. This is the same as
- * returned by fl_aema2d_get_estimate(self).
- */
-const_fl_vector_2d_ref fl_aema2d_process(fl_aema2d_estimator *self, fl_real value_X, fl_real value_Y);
-
-/***************************************************************************//**
- * [public] Processing of a triple data sample.
- *
- * @param[in] self
- * Object pointer
- * @param[in] value
- * Input 3D vector value to the filter.
- *
- * @return
- * Output value of the filter after processing. This is the same as
- * returned by fl_aema3d_get_estimate(self).
- */
-const_fl_vector_3d_ref fl_aema3d_process(fl_aema3d_estimator *self, const_fl_vector_3d_ref value);
-
-/***************************************************************************//**
- * [public] Bring the filter to a stationary state (EMA mode, input = output).
- * This method is used for testing.
- *
- * @param[in] self
- * Object pointer
- * @param[in] value
- * I/o value of the filter.
- */
-void fl_aema1d_set_estimate(fl_aema1d_estimator *self, fl_real value);
-
-/***************************************************************************//**
- * [public] Bring the filter to a stationary state (EMA mode, input = output).
- * This method is used for testing.
- *
- * @param[in] self
- * Object pointer
- * @param[in] value_H
- * First (horizontal) i/o value of the filter.
- * @param[in] value_V
- * Second (vertical) i/o value of the filter.
- */
-void fl_aema2d_set_estimate(fl_aema2d_estimator *self, fl_real value_X, fl_real value_Y);
-
-/***************************************************************************//**
- * [public] Bring the filter to a stationary state (EMA mode, input = output).
- * This method is used for testing.
- *
- * @param[in] self
- * Object pointer
- * @param[in] value
- * I/o 3D vector value of the filter.
- */
-void fl_aema3d_set_estimate(fl_aema3d_estimator *self, const_fl_vector_3d_ref value);
-
-/***************************************************************************//**
- * [public] Fetch the current filter output.
- *
- * @param[in] self
- * Object pointer
- *
- * @return
- * Output value after last processed sample.
- */
-INLINE fl_real fl_aema1d_get_estimate(const fl_aema1d_estimator *self)
-{
- return self->value;
-}
-
-/***************************************************************************//**
- * [public] Fetch the current filter output.
- *
- * @param[in] self
- * Object pointer
- *
- * @return
- * Output 2D vector after last processed sample.
- */
-INLINE const_fl_vector_2d_ref fl_aema2d_get_estimate(const fl_aema2d_estimator *self)
-{
- return self->value;
-}
-
-/***************************************************************************//**
- * [public] Fetch the current filter output.
- *
- * @param[in] self
- * Object pointer
- *
- * @return
- * Output 3D vector after last processed sample.
- */
-INLINE const_fl_vector_3d_ref fl_aema3d_get_estimate(const fl_aema3d_estimator *self)
-{
- return self->value;
-}
-
-/***************************************************************************//**
- * [protected] Initializaton of the base object (class constructor).
- *
- * @param[in] self
- * Object pointer
- * @param[in] plateau_samples
- * Number of samples to collect before switching to standard EMA mode.
- */
-void __aema_init(fl_aema_estimator *self, unsigned plateau_samples);
-
-/***************************************************************************//**
- * [protected] Reset the AEMA object back to initial state. This function is
- * used for repetitive testing and module soft restarts.
- *
- * @param[in] self
- * Object pointer
- */
-void __aema_reset(fl_aema_estimator *self);
-
-/***************************************************************************//**
- * [protected] Processing of a data sample. This is the common part of the
- * derived processing methods, which implements the AEMA update/decay rate.
- *
- * @param[in] self
- * Object pointer
- */
-INLINE void __aema_process(fl_aema_estimator *self)
-{
- if (self->samples < self->plateau_samples) {
- self->samples++;
- self->update_rate = 1.0 / self->samples;
- self->decay_rate = 1.0 - self->update_rate;
- }
-}
-
-/***************************************************************************//**
- * [protected] Bring the base filter to a stationary state (EMA mode). This
- * method is used for testing.
- *
- * @param[in] self
- * Object pointer
- */
-INLINE void __aema_set_estimate(fl_aema_estimator* self)
-{
- self->samples = self->plateau_samples;
- self->update_rate = 1.0 / self->plateau_samples;
- self->decay_rate = 1.0 - self->update_rate;
-}
-
-#if !defined(__FL_AEMA_ESTIMATOR_C__)
- #undef INLINE
-#endif
-
-#ifdef __cplusplus
-}
-#endif
-
-#endif /* __FL_AEMA_ESTIMATOR_H__ */
diff --git a/lbs-server/src/fused/conversions.h b/lbs-server/src/fused/conversions.h
deleted file mode 100644
index 57f3f73..0000000
--- a/lbs-server/src/fused/conversions.h
+++ /dev/null
@@ -1,204 +0,0 @@
-/*
- * Copyright (c) 2016-2017 Samsung Electronics Co., Ltd.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-/**
- * @file conversions.h
- * @brief Units conversion functions
- */
-
-#pragma once
-#ifndef __FL_CONVERSIONS_H__
-#define __FL_CONVERSIONS_H__
-
-#define _USE_MATH_DEFINES
-#include "math-ext.h"
-#include "earth.h"
-#include "types.h"
-
-#ifdef __cplusplus
-extern "C" {
-#endif
-
-#if defined(__FL_CONVERSIONS_C__)
- #define INLINE \
- inline /* thank checkpatchinit_tizen for this contraption */
-#else
- #define INLINE \
- static inline
-#endif
-
-#define FL_TIMESTAMP_UNITS 1 /**< [s] */
-
-/***************************************************************************//**
- * [public] Convert location time-stamp to seconds. Notice, on Tizen this is a
- * formal methd since time-stamps are also resolved in seconds.
- *
- * @param[in] timestamp
- * Time-stamp received from the location source (GPS, WPS).
- *
- * @return
- * Corresponding value in seconds
- */
-INLINE fl_seconds fl_timestamp_to_seconds(fl_timestamp timestamp)
-{
- return (fl_seconds)timestamp / FL_TIMESTAMP_UNITS;
-}
-
-/***************************************************************************//**
- * [public] Convert time in seconds to location time-stamp. The necesary
- * up-rounding is is half the time-stamp unit.
- *
- * @param[in] timestamp
- * Time-stamp received from the location source (GPS, WPS).
- *
- * @return
- * Corresponding value in seconds
- */
-INLINE fl_timestamp fl_seconds_to_timestamp(fl_seconds t)
-{
- return (fl_timestamp)(t * FL_TIMESTAMP_UNITS + 0.5);
-}
-
-/***************************************************************************//**
- * [public] Convert degrees to radians.
- *
- * @param[in] degrees
- * Value in degrees.
- *
- * @return
- * Corresponding value in radians.
- */
-INLINE fl_radians fl_degrees_to_radians(fl_degrees degrees)
-{
- return (M_PI / 180) * degrees;
-}
-
-/***************************************************************************//**
- * [public] Convert radians to degrees.
- *
- * @param[in] radians
- * Value in radians.
- *
- * @return
- * Corresponding value in degrees.
- */
-INLINE fl_degrees fl_radians_to_degrees(fl_radians radians)
-{
- return (180 / M_PI) * radians;
-}
-
-/***************************************************************************//**
- * [public] Convert radians to degrees in the [0,360) range
- *
- * @param[in] radians
- * Arbitrary value in radians.
- *
- * @return
- * Value in degrees projected onto the [0,360) branch.
- */
-fl_degrees fl_radians_to_positive_degrees(fl_radians radians);
-
-/***************************************************************************//**
- * [public] Convert radians to degrees in the [-180,180) range.
- *
- * @param[in] radians
- * Arbitrary value in radians.
- *
- * @return
- * Value in degrees projected onto the [-180,180) branch.
- */
-fl_degrees fl_radians_to_balanced_degrees(fl_radians radians);
-
-/** [public] Convert radians to latitude degrees [-90,90] */
-#define fl_radians_to_latitude fl_radians_to_balanced_degrees
-
-/** [public] Convert radians to longitude degrees [0, 360) */
-#define fl_radians_to_longitude fl_radians_to_positive_degrees
-
-/** [public] Convert radians to longitude degrees [0, 360) */
-#define fl_radians_to_cog fl_radians_to_positive_degrees
-
-/***************************************************************************//**
- * [public] Convert speed in [km/h] to velocity in [m/s].
- *
- * @note The 'speed' is an i/o value of location interfaces, while 'velocity'
- * expressed in SI units is used internally by the fused location engine.
- *
- * @param[in] km_h
- * Speed in kilometers per hour.
- *
- * @return
- * Corresponding value in meters per second.
- */
-INLINE fl_velocity fl_km_h_to_m_s(fl_km_h km_h)
-{
- return km_h * (1000.0 / (60.0 * 60.0));
-}
-
-/***************************************************************************//**
- * [public] Convert velocity in [m/s] to speed in [km/h].
- *
- * @note The 'speed' is an i/o value of location interfaces, while 'velocity'
- * expressed in SI units is used internally by the fused location engine.
- *
- * @param[in] velocity
- * Velocity in meters per second.
- *
- * @return
- * Corresponding value in kilometers per hour.
- */
-INLINE fl_km_h fl_m_s_to_km_h(fl_velocity velocity)
-{
- return velocity * ((60.0 * 60.0) / 1000.0);
-}
-
-/***************************************************************************//**
- * [public] Convert spherical coordinate in radians to arc-length along
- * the Earth's great circle in meters.
- *
- * @param[in] radians
- * Angle spanned by the great arc.
- *
- * @return
- * Corresponding value in meters along Earth's surface.
- */
-INLINE fl_meters fl_radians_to_meters(fl_radians radians)
-{
- return radians * EARTH_RADIUS;
-}
-
-/***************************************************************************//**
- * [public] Convert arc-length in meters along the Earth's great circle to arc
- * angle in radians.
- *
- * @param[in] meters
- * Arc length in meters.
- *
- * @return
- * Corresponding angle in radians.
- */
-INLINE fl_radians fl_meters_to_radians(fl_meters meters)
-{
- return meters * (1.0 / EARTH_RADIUS);
-}
-
-#undef INLINE
-
-#ifdef __cplusplus
-}
-#endif
-
-#endif /* __FL_CONVERSIONS_H_ */
diff --git a/lbs-server/src/fused/earth.h b/lbs-server/src/fused/earth.h
deleted file mode 100644
index 4291251..0000000
--- a/lbs-server/src/fused/earth.h
+++ /dev/null
@@ -1,45 +0,0 @@
-/*
- * Copyright (c) 2016-2017 Samsung Electronics Co., Ltd.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-/**
- * @file earth.h
- * @brief Constants and methods related to the Earth model.
- */
-
-#pragma once
-#ifndef __FL_EARTH_H__
-#define __FL_EARTH_H__
-
-#include "types.h"
-
-/***************************************************************************//**
- * [public] Mean Earth gravity value in [m/s^2].
- *
- * Notice that, strictly speaking, the mean Earth gravity acceleration at surface
- * level is negative; the positive value we measure with an accelerometer is the
- * ground reaction which counters the gravity and prevents us from falling into
- * the interior. Also notice that given the inaccuracy (i.e. bias
- * & miscalibration) of mobile device acceleromenters at ~1 [m/s^2] the number
- * of significant digits in the reference value below is way too many.
- */
-#define EARTH_GRAVITY ((fl_acceleration)9.80665)
-
-/***************************************************************************//**
- * [public] Mean Earth radius in [m].
- */
-#define EARTH_RADIUS ((fl_meters)6371000.0)
-
-#endif /* __FL_EARTH_H__ */
diff --git a/lbs-server/src/fused/gravity-estimator.c b/lbs-server/src/fused/gravity-estimator.c
deleted file mode 100644
index ed8356a..0000000
--- a/lbs-server/src/fused/gravity-estimator.c
+++ /dev/null
@@ -1,43 +0,0 @@
-/*
- * Copyright (c) 2016-2017 Samsung Electronics Co., Ltd.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#if !defined(LOG_THRESHOLD)
- /* custom logging threshold - keep undefined on the repo */
- /* #define LOG_THRESHOLD LOG_LEVEL_TRACE */
-#endif
-
-#define __FL_GRAVITY_ESTIMATOR_C__
-
-#include "gravity-estimator.h"
-#include "debug_util.h"
-
-void fl_gres_init(void)
-{
- LOG_FUSED_DEV(DBG_LOW, FUNCTION_PREFIX("()"), 0);
- fl_aema1d_init(&__gres, 1 << FL_GRES_AEMA_PLATEAU_BITS);
-}
-
-void fl_gres_exit(void)
-{
- LOG_FUSED_DEV(DBG_LOW, FUNCTION_PREFIX("()"), 0);
- fl_aema1d_exit(&__gres);
-}
-
-void fl_gres_reset(void)
-{
- LOG_FUSED_DEV(DBG_LOW, FUNCTION_PREFIX("()"), 0);
- fl_aema1d_reset(&__gres);
-}
diff --git a/lbs-server/src/fused/gravity-estimator.h b/lbs-server/src/fused/gravity-estimator.h
deleted file mode 100644
index 9d4a638..0000000
--- a/lbs-server/src/fused/gravity-estimator.h
+++ /dev/null
@@ -1,113 +0,0 @@
-/*
- * Copyright (c) 2016-2017 Samsung Electronics Co., Ltd.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-/**
- * @file gravity-estimator.h
- * @brief Estimator of the gravity value used for accelerometer scale
- * calibration.
- */
-
-#pragma once
-#ifndef __FL_GRAVITY_ESTIMATOR_H__
-#define __FL_GRAVITY_ESTIMATOR_H__
-
-#include "parameters.h"
-#include "aema-estimator.h"
-
-#ifdef __cplusplus
-extern "C" {
-#endif
-
-#if defined(__FL_GRAVITY_ESTIMATOR_C__)
- #define INLINE \
- static inline /* thank checkpatchinit_tizen for this contraption */
- #define PRIVATE
-#else
- #define INLINE \
- static inline
- #define PRIVATE \
- extern
-#endif
-
-PRIVATE fl_aema1d_estimator __gres;
-
-/***************************************************************************//**
- * [public] Initializaton of the singleton unit (static constructor).
- */
-void fl_gres_init(void);
-
-/***************************************************************************//**
- * [public] Cleanup of the singleton unit (static destructor).
- */
-void fl_gres_exit(void);
-
-/***************************************************************************//**
- * [public] Bring the internal state back to initial one. This function is used
- * for repetitive testing and module soft restarts.
- */
-void fl_gres_reset(void);
-
-/***************************************************************************//**
- * [public] Processing of a single data sample.
- *
- * @param[in] g2
- * Modulus squared of the measured acceleration projected onto z-axis.
- *
- * @return
- * Estimated value of the Earth gravity.
- */
-INLINE fl_real fl_gres_process(fl_real g2)
-{
- return fl_aema1d_process(&__gres, g2);
-}
-
-/***************************************************************************//**
- * [public] Bring the filter to a stationary state (EMA mode, input = output).
- * This method is used for testing.
- *
- * @param[in] g2
- * Modulus squared value of Earth gravity measured by the accelerometer.
- */
-INLINE void fl_gres_set_estimate(fl_real g2)
-{
- fl_aema1d_set_estimate(&__gres, g2);
-}
-
-/***************************************************************************//**
- * [public] Fetch the current gravity estimate.
- *
- * @param[in] self
- * Object pointer
- *
- * @return
- * Output value after last processed sample.
- */
-/** Retrieve current estimate */
-INLINE fl_real fl_gres_get_estimate(void)
-{
- return fl_aema1d_get_estimate(&__gres);
-}
-
-#if !defined(__FL_GRAVITY_ESTIMATOR_C__)
- #undef PRIVATE
- #undef INLINE
-#endif
-
-#ifdef __cplusplus
-}
-#endif
-
-#endif /* __FL_GRAVITY_ESTIMATOR_H__ */
diff --git a/lbs-server/src/fused/gyroscope-filter.c b/lbs-server/src/fused/gyroscope-filter.c
deleted file mode 100644
index 7c5e4e2..0000000
--- a/lbs-server/src/fused/gyroscope-filter.c
+++ /dev/null
@@ -1,105 +0,0 @@
-/*
- * Copyright (c) 2016-2017 Samsung Electronics Co., Ltd.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#if !defined(LOG_THRESHOLD)
- /* custom logging threshold - keep undefined on the repo */
- /* #define LOG_THRESHOLD LOG_LEVEL_TRACE */
-#endif
-
-#define __FL_GYROSCOPE_FILTER_C__
-
-#define _USE_MATH_DEFINES
-#include <math.h>
-#include "math-ext.h"
-#include "gravity-estimator.h"
-#include "gyroscope-filter.h"
-#include "debug_util.h"
-
-#define GYRO_PLATEAU_EVIDENCE (1 << FL_GYRO_AEMA_PLATEAU_BITS)
-#define GYRO_SCALE2_I (1.0 / (2 * SQUARE(FL_GYRO_SPIN_SIGMA_I)))
-#define GYRO_SCALE2_F (1.0 / (2 * SQUARE(FL_GYRO_SPIN_SIGMA_F)))
-
-static fl_real __gyro_alpha;
-
-void fl_gyro_init(void)
-{
- LOG_FUSED_DEV(DBG_LOW, ENTER_FUNCTION_PREFIX("()"), 0);
- __gyro_alpha = (SQUARE(FL_GYRO_SPIN_SIGMA_I / FL_GYRO_SPIN_SIGMA_F) - 1) / SQUARE((fl_real)GYRO_PLATEAU_EVIDENCE);
- fl_gyro_reset();
- LOG_FUSED_DEV(DBG_LOW, LEAVE_FUNCTION_PREFIX("()"), 0);
-}
-
-void fl_gyro_exit(void)
-{
- LOG_FUSED_DEV(DBG_LOW, FUNCTION_PREFIX("()"), 0);
-}
-
-void fl_gyro_reset(void)
-{
- LOG_FUSED_DEV(DBG_LOW, FUNCTION_PREFIX("()"), 0);
- __gyro_bias.scale2 = GYRO_SCALE2_I;
- __gyro_bias.evidence = 0;
- __gyro_bias.weight_norm = 1;
- __gyro_bias.value[GEO_SPATIAL_X] = 0;
- __gyro_bias.value[GEO_SPATIAL_Y] = 0;
- __gyro_bias.value[GEO_SPATIAL_Z] = 0;
-}
-
-void fl_gyro_set(const_fl_spin_vector_ref w0)
-{
- LOG_FUSED_DEV(DBG_LOW, FUNCTION_PREFIX("(%g, %g, %g)"), w0[GEO_SPATIAL_X], w0[GEO_SPATIAL_Y], w0[GEO_SPATIAL_Z]);
- __gyro_bias.scale2 = GYRO_SCALE2_F;
- __gyro_bias.evidence = GYRO_PLATEAU_EVIDENCE;
- __gyro_bias.weight_norm = 1.0 / GYRO_PLATEAU_EVIDENCE;
- __gyro_bias.value[GEO_SPATIAL_X] = w0[GEO_SPATIAL_X];
- __gyro_bias.value[GEO_SPATIAL_Y] = w0[GEO_SPATIAL_Y];
- __gyro_bias.value[GEO_SPATIAL_Z] = w0[GEO_SPATIAL_Z];
-}
-
-const_fl_spin_vector_ref fl_gyro_process(fl_seconds t, const_fl_spin_vector_ref wm)
-{
- LOG_FUSED_DEV(DBG_LOW, FUNCTION_PREFIX("(t=%6.2fs)"), t);
- static fl_spin_vector wf;
- fl_real weight;
- fl_real update_rate;
- fl_real decay_rate;
- fl_real fw2;
-
- fw2 = fl_vector_3d_length2(wf);
- weight = exp(-fw2 * __gyro_bias.scale2);
- if (__gyro_bias.evidence < GYRO_PLATEAU_EVIDENCE) {
- __gyro_bias.evidence += weight;
- if (__gyro_bias.evidence > GYRO_PLATEAU_EVIDENCE)
- __gyro_bias.evidence = GYRO_PLATEAU_EVIDENCE;
- __gyro_bias.weight_norm = 1.0 / __gyro_bias.evidence;
- __gyro_bias.scale2 = GYRO_SCALE2_I * (1.0 + fl_square(__gyro_bias.evidence) * __gyro_alpha);
- }
- update_rate = weight * __gyro_bias.weight_norm;
- decay_rate = 1.0 - update_rate;
- LOG_FUSED_DEV(DBG_LOW, FUNCTION_PREFIX("(t=%6.2fs), fw2=%g, e=%g, s2=%g, w=%g"), t, fw2, __gyro_bias.evidence, __gyro_bias.scale2, weight);
-
-# define UPDATE_VALUE(i) __gyro_bias.value[i] = decay_rate * __gyro_bias.value[i] + update_rate * wm[i];
- UPDATE_VALUE(GEO_SPATIAL_X);
- UPDATE_VALUE(GEO_SPATIAL_Y);
- UPDATE_VALUE(GEO_SPATIAL_Z);
-# undef UPDATE_VALUE
-
- wf[GEO_SPATIAL_X] = wm[GEO_SPATIAL_X] - __gyro_bias.value[GEO_SPATIAL_X];
- wf[GEO_SPATIAL_Y] = wm[GEO_SPATIAL_Y] - __gyro_bias.value[GEO_SPATIAL_Y];
- wf[GEO_SPATIAL_Z] = wm[GEO_SPATIAL_Z] - __gyro_bias.value[GEO_SPATIAL_Z];
-
- return wf;
-}
diff --git a/lbs-server/src/fused/gyroscope-filter.h b/lbs-server/src/fused/gyroscope-filter.h
deleted file mode 100644
index 95783cc..0000000
--- a/lbs-server/src/fused/gyroscope-filter.h
+++ /dev/null
@@ -1,109 +0,0 @@
-/*
- * Copyright (c) 2016-2017 Samsung Electronics Co., Ltd.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-/**
- * @file gyroscope-filter.h
- * @brief Correction of the gyroscope bias.
- */
-
-#pragma once
-#ifndef __FL_GYROSCOPE_FILTER_H__
-#define __FL_GYROSCOPE_FILTER_H__
-
-#include "types.h"
-#include "parameters.h"
-
-#ifdef __cplusplus
-extern "C" {
-#endif
-
-/***************************************************************************//**
- * [public] Initializaton of the singleton unit (static constructor).
- */
-void fl_gyro_init(void);
-
-/***************************************************************************//**
- * [public] Cleanup of the singleton unit (static destructor).
- */
-void fl_gyro_exit(void);
-
-/***************************************************************************//**
- * [public] Reset of the internal state back to initial one.
- */
-void fl_gyro_reset(void);
-
-/***************************************************************************//**
- * [public] Bring the filter to a stationary state (EMA mode, input = output).
- * This method is used for testing.
- *
- * @param[in] w0
- * 3D vector of angular velocity measured by the gyroscope in inertial
- * coordinate frame.
- */
-void fl_gyro_set(const_fl_spin_vector_ref w0);
-
-/***************************************************************************//**
- * [public] Processing of a vector data sample. This method performs:
- * - estimation of the gyroscope bias with the weighted AEMA filter; the
- * individual samples are weighted by the distance from average using Gaussian
- * curve with monotonically-decreasing sigma parameter;
- * - correction of the measurement by the estimated bias.
- *
- * @param[in] t
- * Event time in seconds.
- * @param[in] wm
- * 3D vector of measured angular velocity.
- *
- * @return
- * 3D vector of bias-corrected angular velocity.
- */
-const_fl_spin_vector_ref fl_gyro_process(fl_seconds t, const_fl_spin_vector_ref wm);
-
-#if defined(__FL_GYROSCOPE_FILTER_C__)
- #ifdef GTEST
- #define PRIVATE
- #else
- #define PRIVATE \
- static
- #endif
-#else
- #define PRIVATE \
- extern
-#endif
-
-/** Weighted AEMA filter. This is generalization of the 3D AEMA filter by
- * using 'evidence' as the sum of weights rather than sample count. The
- * sample weights are estimated using Gaussian curve with time-decreasing
- * variance.
- */
-typedef struct {
- fl_real scale2; /* = 1 / (2 variance) */
- fl_real evidence; /* min(accumulated weight, plateau_evidence) */
- fl_real weight_norm; /* = 1 / evidence */
- fl_vector_3d value; /* filter output (estimated value) */
-} fl_waema3d_estimator;
-
-PRIVATE fl_waema3d_estimator __gyro_bias;
-
-#if !defined(__FL_GYROSCOPE_FILTER_C__)
- #undef PRIVATE
-#endif
-
-#ifdef __cplusplus
-}
-#endif
-
-#endif /* __FL_GYROSCOPE_FILTER_H__ */
diff --git a/lbs-server/src/fused/kalman-filter.c b/lbs-server/src/fused/kalman-filter.c
deleted file mode 100644
index 97b3223..0000000
--- a/lbs-server/src/fused/kalman-filter.c
+++ /dev/null
@@ -1,1126 +0,0 @@
-/*
- * Copyright (c) 2016-2017 Samsung Electronics Co., Ltd.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#if !defined(LOG_THRESHOLD)
- /* custom logging threshold - keep undefined on the repo */
- /* #define LOG_THRESHOLD LOG_LEVEL_TRACE */
-#endif
-
-#define __FL_KALMAN_FILTER_C__
-
-#include <float.h>
-#include <memory.h>
-#include "math-ext.h"
-#include "earth.h"
-#include "conversions.h"
-#include "accelerometer-filter.h"
-#include "gyroscope-filter.h"
-#include "aema-estimator.h"
-#include "kalman-filter.h"
-#include "debug_util.h"
-
-#define TIME_FORMAT "%.3fs"
-
-#define PRECISION 1e-4
-#define PRECISION2 SQUARE(PRECISION)
-
-/* the bigger the default standard deviantions, the swifter response to first measurements */
-#define INITIAL_POSITION_SIGMA2 SQUARE(1024 * M_2PI * EARTH_RADIUS)
-#define INITIAL_VELOCITY_SIGMA2 (1024 * 1024 * SQUARE(FL_DEFAULT_VELOCITY_SIGMA))
-#define INITIAL_ACCELERATION_SIGMA2 DEFAULT_ACCELERATION_SIGMA2
-
-#define __init_time_offset() fl_aema1d_init(&__timestamp_offset, 1 << FL_TIMO_AEMA_PLATEAU_BITS)
-#define __exit_time_offset() fl_aema1d_exit(&__timestamp_offset)
-#define __new_time_offset(dt) fl_aema1d_process(&__timestamp_offset, dt)
-#define __get_time_offset() fl_aema1d_get_estimate(&__timestamp_offset)
-
-PRIVATE void __rectify_state(_fl_kalf_state *src)
-{
- unsigned i;
-
- for (i = GEO_SPATIAL_DIMENSION; i--;) {
- _fl_kalf_sigma_matrix_ref s2xi;
- fl_real uuvv, uvvu;
-
- s2xi = src->s2x[i];
- /* diagonal elements */
- #define CHECK(u) \
- if (s2xi[STATE_##u][STATE_##u] < 0) { \
- LOG_FUSED_DEV(DBG_LOW, FUNCTION_PREFIX("(src=%p): src.s2x[%u][" #u "][" #u "] = %g < 0"), src, i, s2xi[STATE_##u][STATE_##u]); \
- s2xi[STATE_##u][STATE_##u] = 0; \
- }
- CHECK(P);
- CHECK(V);
- CHECK(A);
- #undef CHECK
-
- /* partial determinats */
- #define CHECK(u, v) \
- uuvv = s2xi[STATE_##u][STATE_##u] * s2xi[STATE_##v][STATE_##v]; \
- uvvu = s2xi[STATE_##u][STATE_##v] * s2xi[STATE_##v][STATE_##u]; \
- if (uuvv < uvvu) { \
- LOG_FUSED_DEV(DBG_LOW, FUNCTION_PREFIX("(src=%p): det(src.s2x[%u], " #u ", " #v ") = %g < 0"), src, i, uuvv - uvvu); \
- s2xi[STATE_##u][STATE_##v] = \
- s2xi[STATE_##v][STATE_##u] *= sqrt(uuvv / uvvu); \
- }
- CHECK(P, V);
- CHECK(P, A);
- CHECK(A, V);
- #undef CHECK
- }
-}
-
-void fused_engine_init(motion_changed_cb on_motion_changed,
- predicted_updated_cb on_location_changed, gpointer handler)
-{
- LOG_FUSED_DEV(DBG_LOW, ENTER_FUNCTION_PREFIX("(motion_cb=%p, location_cb=%p, interval=%gs, handler=%p)"),
- on_motion_changed, on_location_changed, handler);
-
- fl_accel_init(on_motion_changed, handler);
- fl_gyro_init();
- __init_time_offset();
- __started = FALSE;
- __on_location_changed = on_location_changed;
- __handler = handler;
- fl_kalf_reset();
- LOG_FUSED_DEV(DBG_LOW, LEAVE_FUNCTION_PREFIX("()"));
-}
-
-void fused_engine_exit(void)
-{
- LOG_FUSED_DEV(DBG_LOW, ENTER_FUNCTION_PREFIX("()"), 0);
- __exit_time_offset();
- fl_gyro_exit();
- fl_accel_exit();
- LOG_FUSED_DEV(DBG_LOW, LEAVE_FUNCTION_PREFIX("()"), 0);
-}
-
-void fused_engine_start(void)
-{
- if (__on_location_changed) {
- if (__started == FALSE) {
- __started = TRUE;
- LOG_FUSED_DEV(DBG_LOW, LEAVE_FUNCTION_PREFIX("(): OK"), 0);
- return;
- } else {
- LOG_FUSED_DEV(DBG_ERR, LEAVE_FUNCTION_PREFIX("ALREADY_STARTED"));
- return;
- }
- } else {
- LOG_FUSED_DEV(DBG_ERR, LEAVE_FUNCTION_PREFIX("UNINITIALIZED"));
- return;
- }
-}
-
-void fused_engine_stop(void)
-{
- if (__started) {
- __started = FALSE;
- LOG_FUSED_DEV(DBG_LOW, FUNCTION_PREFIX("(): OK"), 0);
- } else {
- LOG_FUSED_DEV(DBG_ERR, FUNCTION_PREFIX("(): E_NOT_STARTED"), 0);
- }
-}
-
-void fl_kalf_reset(void)
-{
- unsigned i;
-
- LOG_FUSED_DEV(DBG_LOW, FUNCTION_PREFIX("()"), 0);
- __last_notification.time = FL_UNDEFINED_TIME;
- __time_of.sensor_event.first = FL_UNDEFINED_TIME;
- __time_of.sensor_event.last = FL_UNDEFINED_TIME;
- __time_of.sensor_event.count = 0;
- __time_of.last_measurement = FL_UNDEFINED_TIME;
- __time_of.last_p_fixing = FL_UNDEFINED_TIME;
- __time_of.last_v_fixing = FL_UNDEFINED_TIME;
- __time_of.last_a_fixing = FL_UNDEFINED_TIME;
- __time_of.last_r_fixing = FL_UNDEFINED_TIME;
- __time_of.last_r_diffusion = FL_UNDEFINED_TIME;
- __time_of.last_w_fixing = FL_UNDEFINED_TIME;
-#if (FL_KALMAN_ANGULAR_VELOCITY)
- __time_of.last_wz_fixing = FL_UNDEFINED_TIME;
- __time_of.last_b_fixing = FL_UNDEFINED_TIME;
-#endif
- __time_of.last_reference = FL_UNDEFINED_TIME;
-
- memset(&__sp, 0, sizeof(__sp));
- memset(&__se, 0, sizeof(__se));
- memset(__wp, 0, sizeof(__wp));
- memset(__we, 0, sizeof(__we));
- memset(&__sre, 0, sizeof(__sre));
- memset(&__srp, 0, sizeof(__srp));
- __srp.w2[GEO_SPATIAL_X] = M_1_PI / 3;
- __srp.w2[GEO_SPATIAL_Y] = M_1_PI / 3;
- __srp.w2[GEO_SPATIAL_Z] = M_1_PI / 3;
- __sre.w2[GEO_SPATIAL_X] = M_1_PI / 3;
- __sre.w2[GEO_SPATIAL_Y] = M_1_PI / 3;
- __sre.w2[GEO_SPATIAL_Z] = M_1_PI / 3;
- __s2wp = DEFAULT_ROTATION_SIGMA2;
- __s2we = DEFAULT_ROTATION_SIGMA2;
- __wze = 0;
-#if (FL_KALMAN_ANGULAR_VELOCITY)
- __s2wze = DEFAULT_ROTATION_SIGMA2;
- __be = 0;
-#endif
-
- memset(&__te, 0, sizeof(__te));
- __sp.x[GEO_SPATIAL_Z][STATE_P] = EARTH_RADIUS;
- __se.x[GEO_SPATIAL_Z][STATE_P] = EARTH_RADIUS;
- for (i = GEO_SPATIAL_DIMENSION; i;) {
- --i;
- /* diagonal terms */
- __sp.s2x[i][STATE_P][STATE_P] = INITIAL_POSITION_SIGMA2;
- __sp.s2x[i][STATE_V][STATE_V] = INITIAL_VELOCITY_SIGMA2;
- __sp.s2x[i][STATE_A][STATE_A] = INITIAL_ACCELERATION_SIGMA2;
- __se.s2x[i][STATE_P][STATE_P] = INITIAL_POSITION_SIGMA2;
- __se.s2x[i][STATE_V][STATE_V] = INITIAL_VELOCITY_SIGMA2;
- __se.s2x[i][STATE_A][STATE_A] = INITIAL_ACCELERATION_SIGMA2;
- __sre.m[i][i] = 1;
- __srp.m[i][i] = 1;
- __te.fsr[i][i] = 1;
- }
- __te.crx = 1;
- /* pass-throughs */
- __fix_status = LOCATION_STATUS_NO_FIX;
-}
-
-PRIVATE fl_seconds __timestamp_to_seconds(fl_timestamp timestamp)
-{
- __time_of.last_measurement = fl_timestamp_to_seconds(timestamp);
- if (__time_of.sensor_event.last != FL_UNDEFINED_TIME) {
- fl_seconds dt;
-
- dt = __time_of.last_measurement - __get_time_offset() - __time_of.sensor_event.last;
- if (dt < 0) {
- __new_time_offset(__time_of.last_measurement - __time_of.sensor_event.last);
- dt = 0;
- } else if (__time_of.sensor_event.count > 1) {
- fl_seconds mean_sensor_dt = (__time_of.sensor_event.last - __time_of.sensor_event.first) / (__time_of.sensor_event.count - 1);
-
- if (dt > mean_sensor_dt) {
- __new_time_offset(__time_of.last_measurement - __time_of.sensor_event.last - mean_sensor_dt);
- dt = mean_sensor_dt;
- }
- }
- return __time_of.sensor_event.last + dt;
- }
- return __time_of.last_measurement;
-}
-
-PRIVATE void __matrix_3d_multiply_matrix_3d(const_fl_matrix_3d_ref m_1, const_fl_matrix_3d_ref m_2, fl_matrix_3d_ref out)
-{
- unsigned i, j;
-
- i = GEO_SPATIAL_DIMENSION;
- do {
- --i;
- j = GEO_SPATIAL_DIMENSION;
- do {
- --j;
- out[i][j] = m_1[i][GEO_SPATIAL_X] * m_2[GEO_SPATIAL_X][j]
- + m_1[i][GEO_SPATIAL_Y] * m_2[GEO_SPATIAL_Y][j]
- + m_1[i][GEO_SPATIAL_Z] * m_2[GEO_SPATIAL_Z][j];
- } while (j);
- } while (i);
-}
-
-PRIVATE void __create_push_forward_map(_fl_kalf_state_matrix_ref x, LocationStatus status)
-{
- fl_real crx, srx;
- fl_real cry, sry;
- fl_real yz2, r2;
- fl_vector_3d p;
- fl_vector_3d v;
-
- /* pull-back: from the old tangent frame to global coordinates */
- p[GEO_SPATIAL_X] = x[GEO_SPATIAL_X][STATE_P] * __te.fsr[GEO_SPATIAL_X][GEO_SPATIAL_X] + x[GEO_SPATIAL_Y][STATE_P] * __te.fsr[GEO_SPATIAL_Y][GEO_SPATIAL_X] + x[GEO_SPATIAL_Z][STATE_P] * __te.fsr[GEO_SPATIAL_Z][GEO_SPATIAL_X];
- p[GEO_SPATIAL_Y] = x[GEO_SPATIAL_X][STATE_P] * __te.fsr[GEO_SPATIAL_X][GEO_SPATIAL_Y] + x[GEO_SPATIAL_Y][STATE_P] * __te.fsr[GEO_SPATIAL_Y][GEO_SPATIAL_Y] + x[GEO_SPATIAL_Z][STATE_P] * __te.fsr[GEO_SPATIAL_Z][GEO_SPATIAL_Y];
- p[GEO_SPATIAL_Z] = x[GEO_SPATIAL_X][STATE_P] * __te.fsr[GEO_SPATIAL_X][GEO_SPATIAL_Z] + x[GEO_SPATIAL_Y][STATE_P] * __te.fsr[GEO_SPATIAL_Y][GEO_SPATIAL_Z] + x[GEO_SPATIAL_Z][STATE_P] * __te.fsr[GEO_SPATIAL_Z][GEO_SPATIAL_Z];
- v[GEO_SPATIAL_X] = x[GEO_SPATIAL_X][STATE_V] * __te.fsr[GEO_SPATIAL_X][GEO_SPATIAL_X] + x[GEO_SPATIAL_Y][STATE_V] * __te.fsr[GEO_SPATIAL_Y][GEO_SPATIAL_X] + x[GEO_SPATIAL_Z][STATE_V] * __te.fsr[GEO_SPATIAL_Z][GEO_SPATIAL_X];
- v[GEO_SPATIAL_Y] = x[GEO_SPATIAL_X][STATE_V] * __te.fsr[GEO_SPATIAL_X][GEO_SPATIAL_Y] + x[GEO_SPATIAL_Y][STATE_V] * __te.fsr[GEO_SPATIAL_Y][GEO_SPATIAL_Y] + x[GEO_SPATIAL_Z][STATE_V] * __te.fsr[GEO_SPATIAL_Z][GEO_SPATIAL_Y];
- v[GEO_SPATIAL_Z] = x[GEO_SPATIAL_X][STATE_V] * __te.fsr[GEO_SPATIAL_X][GEO_SPATIAL_Z] + x[GEO_SPATIAL_Y][STATE_V] * __te.fsr[GEO_SPATIAL_Y][GEO_SPATIAL_Z] + x[GEO_SPATIAL_Z][STATE_V] * __te.fsr[GEO_SPATIAL_Z][GEO_SPATIAL_Z];
-
- yz2 = fl_square(p[GEO_SPATIAL_Y]) + fl_square(p[GEO_SPATIAL_Z]);
- r2 = yz2 + fl_square(p[GEO_SPATIAL_X]);
- if (r2 > PRECISION2) {
- fl_real _r = 1.0 / sqrt(r2);
- fl_real yz = sqrt(yz2);
-
- __te.rx = atan2(p[GEO_SPATIAL_X], yz);
- __te.srx = srx = _r * p[GEO_SPATIAL_X];
- __te.crx = crx = _r * yz;
- if (yz > PRECISION) {
- fl_real _yz = 1.0 / yz;
-
- __te.ry = atan2(p[GEO_SPATIAL_Y], p[GEO_SPATIAL_Z]);
- sry = _yz * p[GEO_SPATIAL_Y];
- cry = _yz * p[GEO_SPATIAL_Z];
- } else {
-UndefinedLongitude:
- __te.ry = 0;
- sry = 0;
- cry = 1;
- }
- } else {
- __te.rx = 0;
- __te.srx = srx = 0;
- __te.crx = crx = 1;
- goto UndefinedLongitude;
- }
- /* new push-forward map */
- __te.fsr[GEO_SPATIAL_X][GEO_SPATIAL_X] = crx;
- __te.fsr[GEO_SPATIAL_Y][GEO_SPATIAL_X] = 0;
- __te.fsr[GEO_SPATIAL_Z][GEO_SPATIAL_X] = srx;
-
- __te.fsr[GEO_SPATIAL_X][GEO_SPATIAL_Y] = -sry * srx;
- __te.fsr[GEO_SPATIAL_Y][GEO_SPATIAL_Y] = cry;
- __te.fsr[GEO_SPATIAL_Z][GEO_SPATIAL_Y] = sry * crx;
-
- __te.fsr[GEO_SPATIAL_X][GEO_SPATIAL_Z] = -cry * srx;
- __te.fsr[GEO_SPATIAL_Y][GEO_SPATIAL_Z] = -sry;
- __te.fsr[GEO_SPATIAL_Z][GEO_SPATIAL_Z] = cry * crx;
- /* push-forward: from the global onto the new tangent frame */
- x[GEO_SPATIAL_X][STATE_P] = __te.fsr[GEO_SPATIAL_X][GEO_SPATIAL_X] * p[GEO_SPATIAL_X] + __te.fsr[GEO_SPATIAL_X][GEO_SPATIAL_Y] * p[GEO_SPATIAL_Y] + __te.fsr[GEO_SPATIAL_X][GEO_SPATIAL_Z] * p[GEO_SPATIAL_Z];
- x[GEO_SPATIAL_Y][STATE_P] = __te.fsr[GEO_SPATIAL_Y][GEO_SPATIAL_X] * p[GEO_SPATIAL_X] + __te.fsr[GEO_SPATIAL_Y][GEO_SPATIAL_Y] * p[GEO_SPATIAL_Y] + __te.fsr[GEO_SPATIAL_Y][GEO_SPATIAL_Z] * p[GEO_SPATIAL_Z];
- x[GEO_SPATIAL_X][STATE_V] = __te.fsr[GEO_SPATIAL_X][GEO_SPATIAL_X] * v[GEO_SPATIAL_X] + __te.fsr[GEO_SPATIAL_X][GEO_SPATIAL_Y] * v[GEO_SPATIAL_Y] + __te.fsr[GEO_SPATIAL_X][GEO_SPATIAL_Z] * v[GEO_SPATIAL_Z];
- x[GEO_SPATIAL_Y][STATE_V] = __te.fsr[GEO_SPATIAL_Y][GEO_SPATIAL_X] * v[GEO_SPATIAL_X] + __te.fsr[GEO_SPATIAL_Y][GEO_SPATIAL_Y] * v[GEO_SPATIAL_Y] + __te.fsr[GEO_SPATIAL_Y][GEO_SPATIAL_Z] * v[GEO_SPATIAL_Z];
- x[GEO_SPATIAL_Z][STATE_P] = __te.fsr[GEO_SPATIAL_Z][GEO_SPATIAL_X] * p[GEO_SPATIAL_X] + __te.fsr[GEO_SPATIAL_Z][GEO_SPATIAL_Y] * p[GEO_SPATIAL_Y] + __te.fsr[GEO_SPATIAL_Z][GEO_SPATIAL_Z] * p[GEO_SPATIAL_Z];
- x[GEO_SPATIAL_Z][STATE_V] = __te.fsr[GEO_SPATIAL_Z][GEO_SPATIAL_X] * v[GEO_SPATIAL_X] + __te.fsr[GEO_SPATIAL_Z][GEO_SPATIAL_Y] * v[GEO_SPATIAL_Y] + __te.fsr[GEO_SPATIAL_Z][GEO_SPATIAL_Z] * v[GEO_SPATIAL_Z];
-}
-
-PRIVATE void __spherical_to_tangent(
- const fl_spherical_position *pos,
- const fl_tangent_velocity *vel,
- const fl_sigma *sigma,
- fl_position_vector_ref pm,
- fl_velocity_vector_ref vm,
- fl_vector_3d_ref s2pm,
- fl_vector_3d_ref s2vm)
-{
- fl_real rp, rx, ry, rb;
- fl_real crx, srx;
- fl_position_vector pg;
- int spatial;
-
- spatial = (pos->status == LOCATION_STATUS_3D_FIX);
- /* pass-through */
- __fix_status = pos->status;
- /* global coordinates */
- rx = fl_degrees_to_radians(pos->latitude);
- ry = fl_degrees_to_radians(pos->longitude);
- rp = spatial ? EARTH_RADIUS + pos->altitude : __se.x[GEO_SPATIAL_Z][STATE_P];
- srx = rp * sin(rx);
- crx = rp * cos(rx);
- pg[GEO_SPATIAL_X] = srx;
- pg[GEO_SPATIAL_Y] = crx * sin(ry);
- pg[GEO_SPATIAL_Z] = crx * cos(ry);
- /* push-forward map: new position onto the current tangent space */
- pm[GEO_SPATIAL_X] = __te.fsr[GEO_SPATIAL_X][GEO_SPATIAL_X] * pg[GEO_SPATIAL_X] + __te.fsr[GEO_SPATIAL_X][GEO_SPATIAL_Y] * pg[GEO_SPATIAL_Y] + __te.fsr[GEO_SPATIAL_X][GEO_SPATIAL_Z] * pg[GEO_SPATIAL_Z];
- pm[GEO_SPATIAL_Y] = __te.fsr[GEO_SPATIAL_Y][GEO_SPATIAL_X] * pg[GEO_SPATIAL_X] + __te.fsr[GEO_SPATIAL_Y][GEO_SPATIAL_Y] * pg[GEO_SPATIAL_Y] + __te.fsr[GEO_SPATIAL_Y][GEO_SPATIAL_Z] * pg[GEO_SPATIAL_Z];
- pm[GEO_SPATIAL_Z] = __te.fsr[GEO_SPATIAL_Z][GEO_SPATIAL_X] * pg[GEO_SPATIAL_X] + __te.fsr[GEO_SPATIAL_Z][GEO_SPATIAL_Y] * pg[GEO_SPATIAL_Y] + __te.fsr[GEO_SPATIAL_Z][GEO_SPATIAL_Z] * pg[GEO_SPATIAL_Z];
-
- fl_real cd, cd2, sd2;
- cd = pm[GEO_SPATIAL_Z] / rp;
- cd2 = SQUARE(cd);
- sd2 = 1 - cd2;
- if (sd2 < 0) {
- cd2 = 1;
- sd2 = 0;
- }
- /* Notice: in principle we should rotate the diagonal sigma matrix here (two
- * matrix multiplications). This approximation assumes the old and new points
- * are close. */
- if (sigma->of_altitude > 0) {
- fl_real s2h, s2v;
-
- s2h = fl_square(sigma->of_horizontal_pos);
- s2v = fl_square(sigma->of_altitude);
- s2pm[GEO_SPATIAL_X] =
- s2pm[GEO_SPATIAL_Y] = cd2 * s2h + sd2 * s2v;
- s2pm[GEO_SPATIAL_Z] = sd2 * s2h + cd2 * s2v;
- } else {
- s2pm[GEO_SPATIAL_X] =
- s2pm[GEO_SPATIAL_Y] =
- s2pm[GEO_SPATIAL_Z] = fl_square(sigma->of_horizontal_pos);
- }
-
- if (vel && vm) {
- fl_velocity vl; /* velocity vector length */
-
- /* The velocity is given in the tangent frame of p (which is different than
- * the currecnt tangent one). In principle we should pull v back into the
- * global frame using p as the transformation generator, and then push it
- * forward onto our frame. Except first time when the two frames are
- * randomly far apart this composition is almost always near-identity, thus
- * can safely be approximated with tiny correction to the bearing.
- */
- rb = fl_degrees_to_radians(vel->direction)
- - atan2(pm[GEO_SPATIAL_Y] * __te.srx,
- -pm[GEO_SPATIAL_X] * __te.srx + EARTH_RADIUS * __te.crx);
- vl = fl_km_h_to_m_s(vel->speed);
- vm[GEO_SPATIAL_X] = vl * cos(rb);
- vm[GEO_SPATIAL_Y] = vl * sin(rb);
- vm[GEO_SPATIAL_Z] = spatial ? fl_km_h_to_m_s(vel->climb) : __se.x[GEO_SPATIAL_Z][STATE_V];
- if (sigma->of_climb > 0) {
- s2vm[GEO_SPATIAL_X] =
- s2vm[GEO_SPATIAL_Y] = (cd2 * fl_square(sigma->of_speed) + sd2 * fl_square(sigma->of_climb));
- s2vm[GEO_SPATIAL_Z] = (sd2 * fl_square(sigma->of_speed) + cd2 * fl_square(sigma->of_climb));
- } else {
- s2vm[GEO_SPATIAL_X] =
- s2vm[GEO_SPATIAL_Y] =
- s2vm[GEO_SPATIAL_Z] = fl_square(sigma->of_speed);
- }
- }
-}
-
-PRIVATE void __tangent_to_spherical(
- const _fl_kalf_state_matrix_ref x,
- const _fl_kalf_sigma_cube_ref s2x,
- fl_spherical_position* pos,
- fl_tangent_velocity* vel,
- fl_sigma* sigma)
-{
- if (pos) {
- fl_position_vector p;
-
- /* pull-back map: from the tangent frame to global coordinates */
- p[GEO_SPATIAL_X] = x[GEO_SPATIAL_X][STATE_P] * __te.fsr[GEO_SPATIAL_X][GEO_SPATIAL_X] + x[GEO_SPATIAL_Y][STATE_P] * __te.fsr[GEO_SPATIAL_Y][GEO_SPATIAL_X] + x[GEO_SPATIAL_Z][STATE_P] * __te.fsr[GEO_SPATIAL_Z][GEO_SPATIAL_X];
- p[GEO_SPATIAL_Y] = x[GEO_SPATIAL_X][STATE_P] * __te.fsr[GEO_SPATIAL_X][GEO_SPATIAL_Y] + x[GEO_SPATIAL_Y][STATE_P] * __te.fsr[GEO_SPATIAL_Y][GEO_SPATIAL_Y] + x[GEO_SPATIAL_Z][STATE_P] * __te.fsr[GEO_SPATIAL_Z][GEO_SPATIAL_Y];
- p[GEO_SPATIAL_Z] = x[GEO_SPATIAL_X][STATE_P] * __te.fsr[GEO_SPATIAL_X][GEO_SPATIAL_Z] + x[GEO_SPATIAL_Y][STATE_P] * __te.fsr[GEO_SPATIAL_Y][GEO_SPATIAL_Z] + x[GEO_SPATIAL_Z][STATE_P] * __te.fsr[GEO_SPATIAL_Z][GEO_SPATIAL_Z];
-
- fl_real yz2, yz;
- fl_real xyz2;
- fl_radians rx;
- fl_radians ry;
-
- yz2 = fl_square(p[GEO_SPATIAL_Y]) + fl_square(p[GEO_SPATIAL_Z]);
- yz = sqrt(yz2);
- if (yz2 > PRECISION2)
- ry = atan2(p[GEO_SPATIAL_Y], p[GEO_SPATIAL_Z]);
- else
- ry = 0; /* avoid #IND values */
- xyz2 = yz2 + fl_square(p[GEO_SPATIAL_X]);
- if (xyz2 > PRECISION2)
- rx = atan2(p[GEO_SPATIAL_X], yz);
- else
- rx = 0; /* avoid #IND values */
- pos->timestamp = fl_seconds_to_timestamp(__last_notification.time > FL_UNDEFINED_TIME ? __last_notification.time : 0);
- pos->latitude = fl_radians_to_latitude(rx);
- pos->longitude = fl_radians_to_longitude(ry);
- pos->altitude = sqrt(xyz2) - EARTH_RADIUS;
- pos->status = __fix_status;
-
- fl_real rb;
- fl_real u[PLANAR_DIMENSION];
-
- u[PLANAR_X] = EARTH_RADIUS * __te.crx - x[GEO_SPATIAL_X][STATE_P] * __te.srx;
- u[PLANAR_Y] = - x[GEO_SPATIAL_Y][STATE_P] * __te.srx;
- rb = -atan2(x[GEO_SPATIAL_X][STATE_V] * u[PLANAR_Y] - x[GEO_SPATIAL_Y][STATE_V] * u[PLANAR_X],
- x[GEO_SPATIAL_X][STATE_V] * u[PLANAR_X] + x[GEO_SPATIAL_Y][STATE_V] * u[PLANAR_Y]);
-
- if (vel) {
- vel->timestamp = pos->timestamp;
- vel->speed = fl_m_s_to_km_h(sqrt(fl_square(x[GEO_SPATIAL_Y][STATE_V]) + fl_square(x[GEO_SPATIAL_X][STATE_V])));
- vel->direction = fl_radians_to_cog(rb);
- vel->climb = fl_m_s_to_km_h(x[GEO_SPATIAL_Z][STATE_V]);
- }
- if (sigma) {
- sigma->level = LOCATION_ACCURACY_LEVEL_NONE;
- sigma->of_horizontal_pos = sqrt((s2x[GEO_SPATIAL_X][STATE_P][STATE_P] + s2x[GEO_SPATIAL_Y][STATE_P][STATE_P]) * 0.5);
- sigma->of_altitude = s2x[GEO_SPATIAL_Z][STATE_P][STATE_P];
- sigma->of_speed = sqrt((s2x[GEO_SPATIAL_X][STATE_V][STATE_V] + s2x[GEO_SPATIAL_Y][STATE_V][STATE_V]) * 0.5);
- sigma->of_climb = s2x[GEO_SPATIAL_Z][STATE_V][STATE_V];
- }
- }
-}
-
-PRIVATE void __kalman_2d1o(fl_seconds t, _fl_kalf_state_component s, const_fl_vector_3d_ref xm, const_fl_vector_3d_ref s2xm)
-{
- unsigned i;
-
- /* @note WPS-specific: restrict the update to horizontal position */
- i = PLANAR_DIMENSION;
- do {
- --i;
- const_fl_vector_3d_ref s2pis;
- _fl_kalf_state_matrix_ref s2ei;
- _fl_kalf_state_matrix_ref s2pi;
- fl_real s2k, s2mi, s2piss;
-
- s2mi = s2xm[i];
- s2ei = __se.s2x[i];
- s2pi = __sp.s2x[i];
- s2pis = s2pi[s];
- s2piss = s2pis[s];
- s2k = s2mi + s2piss;
- if (s2k > PRECISION2) {
- fl_real inv_s2k;
- fl_real dx;
-
- inv_s2k = 1.0 / s2k;
- dx = xm[i] - __sp.x[i][s];
-
- /* reduced Joseph form in special 3x1 case: subtract scaled projection */
- #define SET_FIELD(a, b) s2ei[a][b] = inv_s2k * ((s2pi[a][b] * s2piss - s2pis[a] * s2pis[b]) + s2pi[a][b] * s2mi)
- #define CPY_FIELD(a, b) s2ei[a][b] = s2ei[b][a]
- SET_FIELD(STATE_P, STATE_P);
- SET_FIELD(STATE_P, STATE_V);
- SET_FIELD(STATE_P, STATE_A);
- CPY_FIELD(STATE_V, STATE_P);
- SET_FIELD(STATE_V, STATE_V);
- SET_FIELD(STATE_V, STATE_A);
- CPY_FIELD(STATE_A, STATE_P);
- CPY_FIELD(STATE_A, STATE_V);
- SET_FIELD(STATE_A, STATE_A);
- #undef CPY_FIELD
- #undef SET_FIELD
-
- #define SET_FIELD(a) __se.x[i][a] = __sp.x[i][a] + dx * (inv_s2k * s2pis[a])
- SET_FIELD(STATE_P);
- SET_FIELD(STATE_V);
- SET_FIELD(STATE_A);
- #undef SET_FIELD
- }
- } while (i);
- /* the third coordinate is of `special care': we want to preserve it in the tangent frame, along with its sigma */
- if (s == STATE_P)
- __se.x[GEO_SPATIAL_Z][STATE_P] = xm[GEO_SPATIAL_Z];
-
- __copy_state(&__sp, &__se);
- __time_of.last_reference = t;
-}
-
-/* Update of the 2D position */
-PRIVATE void __kalman_p(fl_seconds t, const_fl_position_vector_ref pm, const_fl_vector_3d_ref s2pm)
-{
- __predict_pv(t);
- __kalman_2d1o(t, STATE_P, pm, s2pm);
- __time_of.last_p_fixing = t;
- __time_of.last_reference = t;
-}
-
-/* Update of the 3D position & velocity */
-PRIVATE void __kalman_pv(
- fl_seconds t,
- const_fl_position_vector_ref pm, /* p-measurement */
- const_fl_velocity_vector_ref vm, /* v-measurement */
- const_fl_vector_3d_ref s2pm, /* p-measurement variance */
- const_fl_vector_3d_ref s2vm /* v-measurement variance */
-) {
- unsigned i;
-
- __predict_pv(t);
- for (i = GEO_SPATIAL_DIMENSION; i;) {
- --i;
-
- _fl_kalf_matrix_2d s2k;
- fl_real det;
- /* shortcuts */
- _const_fl_kalf_sigma_matrix_ref s2xpi = __sp.s2x[i];
- const fl_real *xpi = __sp .x[i];
-
- /* Sk = Sp + Sm */
- s2k[STATE_P][STATE_P] = s2xpi[STATE_P][STATE_P] + s2pm[i];
- s2k[STATE_P][STATE_V] = /* == s2pi[STATE_P][STATE_V] */
- s2k[STATE_V][STATE_P] = s2xpi[STATE_V][STATE_P];
- s2k[STATE_V][STATE_V] = s2xpi[STATE_V][STATE_V] + s2vm[i];
- det = s2k[STATE_P][STATE_P] * s2k[STATE_V][STATE_V] - s2k[STATE_V][STATE_P] * s2k[STATE_P][STATE_V];
- if (fl_square(det) > PRECISION2) {
- fl_real denom;
- _fl_kalf_matrix_2d s2i;
- _fl_kalf_matrix_2d k0, k1;
-
- /* Si = Sk^{-1} */
- denom = 1.0 / det;
- s2i[STATE_P][STATE_P] = denom * s2k[STATE_V][STATE_V];
- s2i[STATE_P][STATE_V] =
- s2i[STATE_V][STATE_P] = -denom * s2k[STATE_V][STATE_P];
- s2i[STATE_V][STATE_V] = denom * s2k[STATE_P][STATE_P];
-
- /* K = Sp.Si */
- #define SET_K0(j, k) k0[j][k] = s2xpi[j][STATE_P] * s2i[STATE_P][k] + s2xpi[j][STATE_V] * s2i[STATE_V][k]
- SET_K0(STATE_P, STATE_P);
- SET_K0(STATE_P, STATE_V);
- SET_K0(STATE_V, STATE_P);
- SET_K0(STATE_V, STATE_V);
- #undef SET_K0
- /* 1 - K = Sm.Si, use positive form */
- k1[STATE_P][STATE_P] = s2pm[i] * s2i[STATE_P][STATE_P];
- k1[STATE_P][STATE_V] = s2pm[i] * s2i[STATE_P][STATE_V];
- k1[STATE_V][STATE_P] = s2vm[i] * s2i[STATE_V][STATE_P];
- k1[STATE_V][STATE_V] = s2vm[i] * s2i[STATE_V][STATE_V];
- /* state update */
- __se.x[i][STATE_P] = k0[STATE_P][STATE_P] * pm[i] + k0[STATE_P][STATE_V] * vm[i]
- + k1[STATE_P][STATE_P] * xpi[STATE_P] + k1[STATE_P][STATE_V] * xpi[STATE_V];
- __se.x[i][STATE_V] = k0[STATE_V][STATE_P] * pm[i] + k0[STATE_V][STATE_V] * vm[i]
- + k1[STATE_V][STATE_P] * xpi[STATE_P] + k1[STATE_V][STATE_V] * xpi[STATE_V];
- /* covariance update Se = K1,K1.Sp + K,K.Sm */
- _fl_kalf_sigma_matrix_ref s2ei = __se.s2x[i];
-
- #define SET_S2E(j, k) s2ei[j][k] = \
- k1[j][STATE_P] * k1[k][STATE_P] * s2xpi[STATE_P][STATE_P] + k0[j][STATE_P] * k0[k][STATE_P] * s2pm[i] \
- + k1[j][STATE_P] * k1[k][STATE_V] * s2xpi[STATE_P][STATE_V] \
- + k1[j][STATE_V] * k1[k][STATE_P] * s2xpi[STATE_V][STATE_P] \
- + k1[j][STATE_V] * k1[k][STATE_V] * s2xpi[STATE_V][STATE_V] + k0[j][STATE_V] * k0[k][STATE_V] * s2vm[i]
-
- SET_S2E(STATE_P, STATE_P);
- SET_S2E(STATE_P, STATE_V);
- SET_S2E(STATE_V, STATE_P);
- SET_S2E(STATE_V, STATE_V);
- #undef SET_S2E
- }
- /* upon arrival of new position-velocity pair (a maximally uncertain) the pair (p, v) decorrelates from a: */
- __se.s2x[i][STATE_P][STATE_A] = 0;
- __se.s2x[i][STATE_V][STATE_A] = 0;
- __se.s2x[i][STATE_A][STATE_P] = 0;
- __se.s2x[i][STATE_A][STATE_V] = 0;
- __se.s2x[i][STATE_A][STATE_A] = __sp.s2x[i][STATE_A][STATE_A];
- }
- __copy_state(&__sp, &__se);
- __time_of.last_p_fixing = t;
- __time_of.last_v_fixing = t;
- __time_of.last_reference = t;
-}
-
-#if (FL_KALMAN_ANGULAR_VELOCITY)
-void __kalman_wz(fl_seconds t, fl_real wzm, fl_real s2wzm)
-{
- fl_real s2wzp;
-
- if (__time_of.last_wz_fixing > FL_UNDEFINED_TIME) {
- fl_real dt = t - __time_of.last_wz_fixing;
- fl_real s2wzk;
- #define wzp __wze /* constant prediction */
-
- /* propagate/diffuse sigma^2 */
- s2wzp = __s2wze + dt * SQUARE(FL_DEFAULT_SPIN_SIGMA);
- s2wzk = s2wzp + s2wzm;
- if (s2wzk > PRECISION2) {
- fl_real inv_s2k;
-
- inv_s2k = 1.0 / s2wzk;
- LOG_FUSED_DEV(DBG_INFO, FUNCTION_PREFIX("(t=" TIME_FORMAT ", w=%.4g, s2w=%.4g): __wze:%g->%g, __s2wze:%g->%g"),
- t, wzm, s2wzm, __wze, inv_s2k * (s2wzp * wzm + s2wzm * wzp),
- __s2wze, inv_s2k * s2wzp * s2wzm * 2);
- __s2wze = inv_s2k * s2wzp * s2wzm * 2;
- __wze = inv_s2k * (s2wzp * wzm + s2wzm * wzp);
- } else {
- __wze = wzm;
- __s2wze = s2wzm;
- }
- #undef wzp
- } else {
- __wze = wzm;
- __s2wze = s2wzm;
- }
- __time_of.last_wz_fixing = t;
-}
-#endif
-
-/* Blend two unit 3D vectors usign inverse covariances as weights (to avoid singularities) */
-PRIVATE void __inv_kalman_u(
- fl_seconds t,
- const_fl_vector_3d_ref u1, fl_real w2u1,
- const_fl_vector_3d_ref u2, fl_real w2u2,
- fl_vector_3d_ref out, fl_real *w2out)
-{
- unsigned i;
-
- *w2out = 0.5 * (w2u1 + w2u2);
- if (*w2out > PRECISION2) {
- fl_real e2 = 0;
- fl_real _w2out = 1.0 / (*w2out);
-
- i = GEO_SPATIAL_DIMENSION;
- do {
- fl_real e;
- --i;
- out[i] = e = _w2out * (u1[i] * w2u1 + u2[i] * w2u2);
- e2 += SQUARE(e);
- } while (i);
-
- if (e2 > PRECISION2) {
- fl_real _e;
-
- /* normalize to unity */
- _e = 1.0 / sqrt(e2);
- i = GEO_SPATIAL_DIMENSION;
- do
- out[--i] *= _e;
- while (i);
- }
- } else {
- i = GEO_SPATIAL_DIMENSION;
- do
- out[--i] = 0;
- while (i);
- }
-}
-
-/* 3D cross-product with normalization */
-PRIVATE void __cross_product(const_fl_vector_3d_ref in1, const_fl_vector_3d_ref in2, fl_vector_3d_ref out)
-{
- fl_real out2;
-
- /* The cross-product preserves system handedness: it's right-handed in the
- * right-handed system, and left-handed in the left-handed one.
- */
- VECTOR_CROSS_VECTOR_3D(in1, in2, out);
-
- out2 = fl_square(out[GEO_SPATIAL_X])
- + fl_square(out[GEO_SPATIAL_Y])
- + fl_square(out[GEO_SPATIAL_Z]);
- if (fl_square(out2 - 1) > PRECISION2) {
- fl_real _out = 1.0 / sqrt(out2);
- /* normalize */
- out[GEO_SPATIAL_X] *= _out;
- out[GEO_SPATIAL_Y] *= _out;
- out[GEO_SPATIAL_Z] *= _out;
- }
-}
-
-PRIVATE void __signal_updated_location(fl_seconds t)
-{
- if (FL_MIN_NOTIFICATION_FILTRATION && __started && __time_of.last_p_fixing > FL_UNDEFINED_TIME) {
- t += __get_time_offset();
- if ((t - __last_notification.time) * 1000 >= FL_MIN_NOTIFICATION_INTERVAL) {
- __get_prediction_vector(t, &__last_notification);
- __on_location_changed(&__last_notification, __handler);
- }
- }
-}
-
-/* Filter input: Enter the raw position measurement */
-void fused_engine_process_position_2d_event(const fl_spherical_position* pos, const fl_sigma* sigma)
-{
- if (pos && sigma) {
- if (pos->status >= LOCATION_STATUS_2D_FIX) {
- fl_seconds t;
- fl_position_vector pm; /* p-measurement */
- fl_vector_3d s2pm; /* p-measurement variance */
-
- t = __timestamp_to_seconds(pos->timestamp);
- LOG_FUSED_DEV(DBG_LOW, FUNCTION_PREFIX("(t=" TIME_FORMAT ", pos=(%g�, %g�), sp=%gm)"),
- t,
- pos->latitude,
- pos->longitude,
- sigma->of_horizontal_pos);
- __spherical_to_tangent(pos, NULL, sigma, pm, NULL, s2pm, NULL);
- /* Kalman merging of 2D position */
- __kalman_p(t, pm, s2pm);
- __create_push_forward_map(__se.x, pos->status);
- memcpy(__sp.x, __se.x, sizeof(__sp.x));
- __signal_updated_location(t);
- } else {
- LOG_FUSED_DEV(DBG_ERR, FUNCTION_PREFIX("(location.pos.status=%d)"), pos->status);
- }
- } else {
- LOG_FUSED_DEV(DBG_ERR, FUNCTION_PREFIX("(%p, %p): INVALID ARGUMENT"), pos, sigma);
- }
-}
-
-/* Filter input: Enter the raw position measurement */
-void fused_engine_process_position_2x3d_event(const fl_spherical_position *pos, const fl_tangent_velocity *vel, const fl_sigma *sigma)
-{
- if (pos && vel && sigma) {
- if (pos->status >= LOCATION_STATUS_2D_FIX) {
- fl_seconds t;
- fl_position_vector pm; /* p-measurement */
- fl_velocity_vector vm; /* v-measurement */
- fl_vector_3d s2pm; /* p-measurement variance */
- fl_vector_3d s2vm; /* v-measurement variance */
-
- t = __timestamp_to_seconds(pos->timestamp);
- LOG_FUSED_DEV(DBG_LOW, FUNCTION_PREFIX("(t=" TIME_FORMAT ", pos=(%g�, %g�, %gm), cog=%g�, sog=%gkm/h, climb=%gkm/h, sp=%gm, sv=%gm/s)"),
- t,
- pos->latitude,
- pos->longitude,
- pos->altitude,
- vel->direction,
- vel->speed,
- vel->climb,
- sigma->of_horizontal_pos,
- sigma->of_speed);
- __spherical_to_tangent(pos, vel, sigma, pm, vm, s2pm, s2vm);
- fl_real vh = fl_km_h_to_m_s(vel->speed);
- #if (FL_KALMAN_ANGULAR_VELOCITY)
- fl_real vhe = sqrt(fl_square(__se.x[GEO_SPATIAL_X][STATE_V]) + fl_square(__se.x[GEO_SPATIAL_Y][STATE_V]));
- fl_real b = fl_degrees_to_radians(vel->direction);
- if (vh > PRECISION && __time_of.last_b_fixing > FL_UNDEFINED_TIME) {
- fl_real dt = t - __time_of.last_b_fixing;
- if (vh * vhe * dt > 0) {
- fl_radians db;
- fl_real wzm, s2wzm;
-
- /* circular difference */
- db = b - __be;
- if (db > M_PI)
- db -= M_2PI;
- else if (db < -M_PI)
- db += M_2PI;
-
- wzm = db / dt;
- s2wzm = 4 * fl_square(atan2(2 * sqrt(vh * vhe), FL_DEFAULT_VELOCITY_SIGMA));
- __kalman_wz(t, wzm, s2wzm);
- }
- }
- __be = b;
- __time_of.last_b_fixing = t;
- #endif
- __kalman_pv(t, pm, vm, s2pm, s2vm);
- __create_push_forward_map(__se.x, pos->status);
- memcpy(__sp.x, __se.x, sizeof(__sp.x));
- __signal_updated_location(t);
- } else {
- LOG_FUSED_DEV(DBG_ERR, FUNCTION_PREFIX("(location.pos.status=%d)"), pos->status);
- }
- } else {
- LOG_FUSED_DEV(DBG_ERR, FUNCTION_PREFIX("(%p, %p): INVALID ARGUMENT"), pos, sigma);
- }
-}
-
-void fused_engine_get_location(fl_spherical_position* pos, fl_tangent_velocity* vel, fl_sigma* sigma)
-{
- LOG_FUSED_DEV(DBG_LOW, FUNCTION_PREFIX("(), __last_notification.time=%gs"), __last_notification.time);
- __tangent_to_spherical(__sp.x, __sp.s2x, pos, vel, sigma);
-}
-
-PRIVATE void __get_prediction_vector(fl_seconds t, fl_position_4d* prediction)
-{
- prediction->time = t;
- /* pull-back map: from the old tangent frame back to global coordinates */
- prediction->spatial[GEO_SPATIAL_X] = __sp.x[GEO_SPATIAL_X][STATE_P] * __te.fsr[GEO_SPATIAL_X][GEO_SPATIAL_X] + __sp.x[GEO_SPATIAL_Y][STATE_P] * __te.fsr[GEO_SPATIAL_Y][GEO_SPATIAL_X] + __sp.x[GEO_SPATIAL_Z][STATE_P] * __te.fsr[GEO_SPATIAL_Z][GEO_SPATIAL_X];
- prediction->spatial[GEO_SPATIAL_Y] = __sp.x[GEO_SPATIAL_X][STATE_P] * __te.fsr[GEO_SPATIAL_X][GEO_SPATIAL_Y] + __sp.x[GEO_SPATIAL_Y][STATE_P] * __te.fsr[GEO_SPATIAL_Y][GEO_SPATIAL_Y] + __sp.x[GEO_SPATIAL_Z][STATE_P] * __te.fsr[GEO_SPATIAL_Z][GEO_SPATIAL_Y];
- prediction->spatial[GEO_SPATIAL_Z] = __sp.x[GEO_SPATIAL_X][STATE_P] * __te.fsr[GEO_SPATIAL_X][GEO_SPATIAL_Z] + __sp.x[GEO_SPATIAL_Y][STATE_P] * __te.fsr[GEO_SPATIAL_Y][GEO_SPATIAL_Z] + __sp.x[GEO_SPATIAL_Z][STATE_P] * __te.fsr[GEO_SPATIAL_Z][GEO_SPATIAL_Z];
-}
-
-fl_real fl_kalf_get_distance(const_fl_position_vector_ref p, const_fl_position_vector_ref q)
-{
- return sqrt(fl_square(p[GEO_SPATIAL_X] - q[GEO_SPATIAL_X])
- + fl_square(p[GEO_SPATIAL_Y] - q[GEO_SPATIAL_Y])
- + fl_square(p[GEO_SPATIAL_Z] - q[GEO_SPATIAL_Z]));
-}
-
-PRIVATE void __quaternion_to_rotation(_const_fl_kalf_quaternion_ref q, fl_matrix_3d_ref out)
-{
- out[GEO_SPATIAL_X][GEO_SPATIAL_X] = SQUARE(q[QTR_0]) + SQUARE(q[QTR_X]) - SQUARE(q[QTR_Y]) - SQUARE(q[QTR_Z]);
- out[GEO_SPATIAL_X][GEO_SPATIAL_Y] = 2 * (q[QTR_X] * q[QTR_Y] + q[QTR_Z] * q[QTR_0]);
- out[GEO_SPATIAL_X][GEO_SPATIAL_Z] = 2 * (q[QTR_X] * q[QTR_Z] - q[QTR_Y] * q[QTR_0]);
-
- out[GEO_SPATIAL_Y][GEO_SPATIAL_X] = 2 * (q[QTR_Y] * q[QTR_X] - q[QTR_Z] * q[QTR_0]);
- out[GEO_SPATIAL_Y][GEO_SPATIAL_Y] = SQUARE(q[QTR_0]) - SQUARE(q[QTR_X]) + SQUARE(q[QTR_Y]) - SQUARE(q[QTR_Z]);
- out[GEO_SPATIAL_Y][GEO_SPATIAL_Z] = 2 * (q[QTR_Y] * q[QTR_Z] + q[QTR_X] * q[QTR_0]);
-
- out[GEO_SPATIAL_Z][GEO_SPATIAL_X] = 2 * (q[QTR_Z] * q[QTR_X] + q[QTR_Y] * q[QTR_0]);
- out[GEO_SPATIAL_Z][GEO_SPATIAL_Y] = 2 * (q[QTR_Z] * q[QTR_Y] - q[QTR_X] * q[QTR_0]);
- out[GEO_SPATIAL_Z][GEO_SPATIAL_Z] = SQUARE(q[QTR_0]) - SQUARE(q[QTR_X]) - SQUARE(q[QTR_Y]) + SQUARE(q[QTR_Z]);
-}
-
-//PRIVATE void __process_linear_acceleration(fl_seconds t, const_fl_acceleration_vector_ref al)
-//{
-// unsigned i;
-//
-// for (i = GEO_SPATIAL_DIMENSION; i--;) {
-// __se. x[i][STATE_A] = 0;
-// __se.s2x[i][STATE_A][STATE_A] = DEFAULT_ACCELERATION_SIGMA2;
-// }
-// __predict_pv(t);
-// __time_of.last_a_fixing = t;
-// __signal_updated_location(t);
-//}
-
-void fused_engine_process_sensory_event(fl_seconds t, fl_sensory_source sensor_id, const_sensor_vector_3d_ref data)
-{
- if (__time_of.sensor_event.count == 0)
- __time_of.sensor_event.first = t;
- __time_of.sensor_event.last = t;
- __time_of.sensor_event.count++;
-
- if (__time_of.last_measurement > FL_UNDEFINED_TIME) {
- fl_seconds dt = t - (__time_of.last_measurement - __get_time_offset());
- if (dt < 0)
- __new_time_offset(__time_of.last_measurement - t);
- }
- if (data) {
- switch (sensor_id) {
- case ACCELEROMETER: {
- fl_acceleration_vector am;
- fl_acceleration_vector al;
- fl_real w2gu;
- fl_vector_3d gu;
- unsigned i;
-
- LOG_FUSED_DEV(DBG_LOW, FUNCTION_PREFIX("(t=" TIME_FORMAT ", RAC, a=(%g, %g, %g)[m/s^2])"), t, data[DEV_SPATIAL_X], data[DEV_SPATIAL_Y], data[DEV_SPATIAL_Z]);
- /* extrapolate the rotation matrix */
- __predict_rw(t);
-
- /* float, math system -> double, geographic system, rotated into local (tangent) frame */
- am[GEO_SPATIAL_X] = __srp.m[GEO_SPATIAL_X][GEO_SPATIAL_X] * data[DEV_SPATIAL_X] + __srp.m[GEO_SPATIAL_X][GEO_SPATIAL_Y] * data[DEV_SPATIAL_Y] + __srp.m[GEO_SPATIAL_X][GEO_SPATIAL_Z] * data[DEV_SPATIAL_Z];
- am[GEO_SPATIAL_Y] = __srp.m[GEO_SPATIAL_Y][GEO_SPATIAL_X] * data[DEV_SPATIAL_X] + __srp.m[GEO_SPATIAL_Y][GEO_SPATIAL_Y] * data[DEV_SPATIAL_Y] + __srp.m[GEO_SPATIAL_Y][GEO_SPATIAL_Z] * data[DEV_SPATIAL_Z];
- am[GEO_SPATIAL_Z] = __srp.m[GEO_SPATIAL_Z][GEO_SPATIAL_X] * data[DEV_SPATIAL_X] + __srp.m[GEO_SPATIAL_Z][GEO_SPATIAL_Y] * data[DEV_SPATIAL_Y] + __srp.m[GEO_SPATIAL_Z][GEO_SPATIAL_Z] * data[DEV_SPATIAL_Z];
- /* pass through gravity filter to estimate |g| and downward direction */
- fl_accel_process(t, am, al, gu, &w2gu);
- if (w2gu > PRECISION2) {
- fl_vector_3d gn;
-
- /* rotate back to device frame */
- VECTOR_DOT_MATRIX_3D(gu, __srp.m, gn);
- /* w2gn = w2gu because w2gu is diagonal and srp orthogonal */
- /* update rotation matrix */
- __inv_kalman_u(t, gn, w2gu, __srp.m[GEO_SPATIAL_Z], __srp.w2[GEO_SPATIAL_Z], __sre.m[GEO_SPATIAL_Z], &__sre.w2[GEO_SPATIAL_Z]);
- __cross_product(__sre.m[GEO_SPATIAL_Z], __srp.m[GEO_SPATIAL_X], __sre.m[GEO_SPATIAL_Y]);
- __cross_product(__sre.m[GEO_SPATIAL_Y], __sre.m[GEO_SPATIAL_Z], __sre.m[GEO_SPATIAL_X]);
-
- __copy_system_rotation(&__srp, &__sre);
- __time_of.last_r_fixing = t;
- }
- /* process linear acceleration */
- for (i = GEO_SPATIAL_DIMENSION; i--;) {
- __se. x[i][STATE_A] = 0;
- __se.s2x[i][STATE_A][STATE_A] = DEFAULT_ACCELERATION_SIGMA2;
- }
- __predict_pv(t);
- __time_of.last_a_fixing = t;
- __signal_updated_location(t);
- // __process_linear_acceleration(t, al);
- return;
- }
- case GYROSCOPE: {
- fl_spin_vector wm;
-
- /* float, degrees, math/device coordinates (right-handed) -> double, radians, geographic/tangent coordinates (left-handed) */
- wm[GEO_SPATIAL_X] = -fl_degrees_to_radians(data[DEV_SPATIAL_X]);
- wm[GEO_SPATIAL_Y] = -fl_degrees_to_radians(data[DEV_SPATIAL_Y]);
- wm[GEO_SPATIAL_Z] = -fl_degrees_to_radians(data[DEV_SPATIAL_Z]);
- LOG_FUSED_DEV(DBG_LOW, FUNCTION_PREFIX("(t=" TIME_FORMAT ", GYR, w=(%g, %g, %g)[rad/s])"), t, wm[GEO_SPATIAL_X], wm[GEO_SPATIAL_Y], wm[GEO_SPATIAL_Z]);
- __integrate_rw(t, fl_gyro_process(t, wm), DEFAULT_ROTATION_SIGMA2);
- return;
- }
- default:
- /* prevent compiler warning */
- return;
- }
- }
- LOG_FUSED_DEV(DBG_ERR, FUNCTION_PREFIX("(t=" TIME_FORMAT ", sensor_id=%d, data=%p)"), t, sensor_id, data);
-}
-
-PRIVATE void __integrate_rw(fl_seconds t, const_fl_spin_vector_ref wm, fl_real s2wm)
-{
- unsigned i;
-
- if (__time_of.last_w_fixing > FL_UNDEFINED_TIME) {
- fl_seconds dt;
-
- dt = t - __time_of.last_w_fixing;
- if (dt > 0) {
- fl_real w2;
-
- __wp[GEO_SPATIAL_X] = 0.5 * (__we[GEO_SPATIAL_X] + wm[GEO_SPATIAL_X]);
- __wp[GEO_SPATIAL_Y] = 0.5 * (__we[GEO_SPATIAL_Y] + wm[GEO_SPATIAL_Y]);
- __wp[GEO_SPATIAL_Z] = 0.5 * (__we[GEO_SPATIAL_Z] + wm[GEO_SPATIAL_Z]);
- w2 = fl_vector_3d_length2(__wp);
- fl_real wz = __srp.m[GEO_SPATIAL_Z][GEO_SPATIAL_X] * __wp[GEO_SPATIAL_X]
- + __srp.m[GEO_SPATIAL_Z][GEO_SPATIAL_Y] * __wp[GEO_SPATIAL_Y]
- + __srp.m[GEO_SPATIAL_Z][GEO_SPATIAL_Z] * __wp[GEO_SPATIAL_Z];
-
- #if (FL_KALMAN_ANGULAR_VELOCITY)
- __kalman_wz(t, wz, SQUARE(FL_DEFAULT_SPIN_SIGMA));
- #else
- __wze = wz;
- #endif
- __predict_pv(t);
- __se.s2x[GEO_SPATIAL_X][STATE_A][STATE_A] = fl_square(__sp.x[GEO_SPATIAL_X][STATE_A] - __se.x[GEO_SPATIAL_X][STATE_A]);
- __se.s2x[GEO_SPATIAL_Y][STATE_A][STATE_A] = fl_square(__sp.x[GEO_SPATIAL_Y][STATE_A] - __se.x[GEO_SPATIAL_Y][STATE_A]);
- __copy_state(&__se, &__sp);
- __time_of.last_reference = t;
-
- /* Integrate over dt: We use the fact that for any scalar t the quaternions
- * [exp(w), exp(tw)] = 0
- * commute.
- */
- fl_real dt2 = dt * dt / 24;
-
- if (w2 > PRECISION2) {
- fl_real w;
- fl_real wt, swtw;
- _fl_kalf_quaternion qwt;
- fl_matrix_3d dsr;
- fl_real qwt2;
-
- w = sqrt(w2);
- wt = w * dt * 0.5;
- swtw = sin(wt) / w; /* system rotates opposite to the device */
- qwt[QTR_0] = cos(wt);
- qwt[QTR_X] = swtw * __wp[GEO_SPATIAL_X] + dt2 * (__we[GEO_SPATIAL_Y] * wm[GEO_SPATIAL_Z] - __we[GEO_SPATIAL_Z] * wm[GEO_SPATIAL_Y]);
- qwt[QTR_Y] = swtw * __wp[GEO_SPATIAL_Y] + dt2 * (__we[GEO_SPATIAL_Z] * wm[GEO_SPATIAL_X] - __we[GEO_SPATIAL_X] * wm[GEO_SPATIAL_Z]);
- qwt[QTR_Z] = swtw * __wp[GEO_SPATIAL_Z] + dt2 * (__we[GEO_SPATIAL_X] * wm[GEO_SPATIAL_Y] - __we[GEO_SPATIAL_Y] * wm[GEO_SPATIAL_X]);
- qwt2 = fl_square(qwt[QTR_0]) + fl_vector_3d_length2(&qwt[QTR_X]);
- if (qwt2 > PRECISION2) {
- fl_real norm_qwt = 1.0 / sqrt(qwt2);
-
- qwt[QTR_0] *= norm_qwt;
- qwt[QTR_X] *= norm_qwt;
- qwt[QTR_Y] *= norm_qwt;
- qwt[QTR_Z] *= norm_qwt;
- }
- __quaternion_to_rotation(qwt, dsr);
- __matrix_3d_multiply_matrix_3d(__srp.m, dsr, __sre.m);
- __copy_system_rotation(&__srp, &__sre);
- __time_of.last_r_fixing = t;
- }
- }
- }
- i = GEO_SPATIAL_DIMENSION;
- do {
- --i;
- // Kalmaning the rotation rates would amount to an EMA low-pass filter - we don't want it
- __wp[i] = __we[i] = wm[i];
- } while (i);
- __s2we = s2wm;
- __time_of.last_w_fixing = t;
-}
-
-PRIVATE void __predict_rw(fl_seconds t)
-{
- unsigned i;
-
- if (__time_of.last_w_fixing > FL_UNDEFINED_TIME) {
- fl_seconds dt;
-
- dt = t - __time_of.last_w_fixing;
- if (dt > 0) {
- i = GEO_SPATIAL_DIMENSION;
- do {
- --i;
- __wp[i] = __we[i];
- } while (i);
-
- /* Integrate over dt: We use the fact that for any scalar t the quaternions
- * [exp(w), exp(tw)] = 0
- * commute.
- */
- fl_real w2;
-
- w2 = fl_vector_3d_length2(__wp);
- if (w2 > PRECISION2) {
- fl_real w;
- fl_real wt, swt;
- _fl_kalf_quaternion qwt;
- fl_matrix_3d srp; /* local copy */
- fl_matrix_3d dsr;
-
- w = sqrt(w2);
- wt = w * dt * 0.5;
- swt = sin(wt) / w;
- qwt[QTR_0] = cos(wt);
- qwt[QTR_X] = swt * __wp[GEO_SPATIAL_X];
- qwt[QTR_Y] = swt * __wp[GEO_SPATIAL_Y];
- qwt[QTR_Z] = swt * __wp[GEO_SPATIAL_Z];
- __quaternion_to_rotation(qwt, dsr);
- /* inlined memcpy() */
- i = sizeof(__srp.m) / sizeof(fl_real);
- do {
- --i;
- srp[0][i] = __srp.m[0][i];
- } while (i);
- __matrix_3d_multiply_matrix_3d(srp, dsr, __srp.m);
- }
- }
- }
- /* otherwise there's no q change */
-}
-
-PRIVATE void __predict_pv(fl_seconds t)
-{
- fl_seconds dt;
- unsigned i;
-
- if (__time_of.last_reference > FL_UNDEFINED_TIME)
- dt = t - __time_of.last_reference;
- else
- /* no prior data */
- return;
-
- LOG_FUSED_DEV(DBG_LOW, FUNCTION_PREFIX("(t=" TIME_FORMAT "), dt=" TIME_FORMAT), t, dt);
- if (dt > PRECISION2) {
- fl_real wt = __wze * dt;
- if (fabs(__wze) > PRECISION) {
- fl_real cwt, swt;
- fl_real cwtw, swtw;
- fl_real inv_wz;
-
- cwt = cos(wt);
- swt = sin(wt);
- inv_wz = 1.0 / __wze;
- cwtw = (cwt - 1) * inv_wz;
- swtw = swt * inv_wz;
-
- __sp.x[GEO_SPATIAL_X][STATE_P] = __se.x[GEO_SPATIAL_X][STATE_V] * swtw + __se.x[GEO_SPATIAL_Y][STATE_V] * cwtw + __se.x[GEO_SPATIAL_X][STATE_P];
- __sp.x[GEO_SPATIAL_Y][STATE_P] = __se.x[GEO_SPATIAL_Y][STATE_V] * swtw - __se.x[GEO_SPATIAL_X][STATE_V] * cwtw + __se.x[GEO_SPATIAL_Y][STATE_P];
- __sp.x[GEO_SPATIAL_X][STATE_V] = __se.x[GEO_SPATIAL_X][STATE_V] * cwt - __se.x[GEO_SPATIAL_Y][STATE_V] * swt;
- __sp.x[GEO_SPATIAL_Y][STATE_V] = __se.x[GEO_SPATIAL_Y][STATE_V] * cwt + __se.x[GEO_SPATIAL_X][STATE_V] * swt;
- } else {
- __sp.x[GEO_SPATIAL_X][STATE_P] = __se.x[GEO_SPATIAL_X][STATE_P] - dt * (wt * __se.x[GEO_SPATIAL_Y][STATE_V] * 0.5 - __se.x[GEO_SPATIAL_X][STATE_V]);
- __sp.x[GEO_SPATIAL_Y][STATE_P] = __se.x[GEO_SPATIAL_Y][STATE_P] + dt * (wt * __se.x[GEO_SPATIAL_X][STATE_V] * 0.5 + __se.x[GEO_SPATIAL_Y][STATE_V]);
- __sp.x[GEO_SPATIAL_X][STATE_V] = __se.x[GEO_SPATIAL_X][STATE_V] - wt * (wt * __se.x[GEO_SPATIAL_X][STATE_V] * 0.5 + __se.x[GEO_SPATIAL_Y][STATE_V]);
- __sp.x[GEO_SPATIAL_Y][STATE_V] = __se.x[GEO_SPATIAL_Y][STATE_V] - wt * (wt * __se.x[GEO_SPATIAL_Y][STATE_V] * 0.5 - __se.x[GEO_SPATIAL_X][STATE_V]);
- }
- __sp.x[GEO_SPATIAL_Z][STATE_P] = __se.x[GEO_SPATIAL_Z][STATE_P] + dt * __se.x[GEO_SPATIAL_Z][STATE_V];
- __sp.x[GEO_SPATIAL_Z][STATE_V] = __se.x[GEO_SPATIAL_Z][STATE_V];
-
- __sp.x[GEO_SPATIAL_X][STATE_A] = -__sp.x[GEO_SPATIAL_Y][STATE_V] * __wze;
- __sp.x[GEO_SPATIAL_Y][STATE_A] = __sp.x[GEO_SPATIAL_X][STATE_V] * __wze;
- __sp.x[GEO_SPATIAL_Z][STATE_A] = 0;
-
- for (i = GEO_SPATIAL_DIMENSION; i;) {
- --i;
-
- _const_fl_kalf_sigma_matrix_ref s2ei = __se.s2x[i];
- _fl_kalf_sigma_matrix_ref s2pi = __sp.s2x[i];
-
- s2pi[STATE_A][STATE_A] = s2ei[STATE_A][STATE_A];
- s2pi[STATE_A][STATE_V] = s2ei[STATE_A][STATE_V]
- + s2ei[STATE_A][STATE_A] * dt;
- s2pi[STATE_A][STATE_P] = s2ei[STATE_A][STATE_P]
- + (s2ei[STATE_A][STATE_V]
- + s2ei[STATE_A][STATE_A] * dt / 2) * dt;
-
- s2pi[STATE_V][STATE_A] = s2pi[STATE_A][STATE_V]; /* by symmetry */
- s2pi[STATE_V][STATE_V] = s2ei[STATE_V][STATE_V]
- + (s2ei[STATE_V][STATE_A] * 2
- + s2ei[STATE_A][STATE_A] * dt) * dt;
- s2pi[STATE_V][STATE_P] = s2ei[STATE_V][STATE_P]
- + (s2ei[STATE_V][STATE_V]
- + s2ei[STATE_P][STATE_A]
- + (s2ei[STATE_V][STATE_A] * 3
- + s2ei[STATE_A][STATE_A] * dt) / 2 * dt) * dt;
-
- s2pi[STATE_P][STATE_A] = s2pi[STATE_A][STATE_P]; /* by symmetry */
- s2pi[STATE_P][STATE_V] = s2pi[STATE_V][STATE_P]; /* by symmetry */
- s2pi[STATE_P][STATE_P] = s2ei[STATE_P][STATE_P]
- + (s2ei[STATE_P][STATE_V] * 2
- + (s2ei[STATE_V][STATE_V]
- + s2ei[STATE_A][STATE_P]
- + (s2ei[STATE_A][STATE_V]
- + s2ei[STATE_A][STATE_A] * dt / 4) * dt) * dt) * dt;
- }
- // process noise
- __sp.s2x[GEO_SPATIAL_X][STATE_A][STATE_A] += fl_square(__sp.x[GEO_SPATIAL_X][STATE_A] - __se.x[GEO_SPATIAL_X][STATE_A]);
- __sp.s2x[GEO_SPATIAL_Y][STATE_A][STATE_A] += fl_square(__sp.x[GEO_SPATIAL_Y][STATE_A] - __se.x[GEO_SPATIAL_Y][STATE_A]);
- __rectify_state(&__sp);
- }
-}
diff --git a/lbs-server/src/fused/kalman-filter.h b/lbs-server/src/fused/kalman-filter.h
deleted file mode 100644
index 42fde96..0000000
--- a/lbs-server/src/fused/kalman-filter.h
+++ /dev/null
@@ -1,619 +0,0 @@
-/*
- * Copyright (c) 2016-2017 Samsung Electronics Co., Ltd.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-/**
- * @file kalman-filter.h
- * @brief Implementation of a 4D (2D position + 2D velocity) Kalman filter
- */
-
-#pragma once
-#ifndef __FL_KALMAN_FILTER_H__
-#define __FL_KALMAN_FILTER_H__
-
-#include <glib.h>
-#include "types.h"
-#include "parameters.h"
-#include "motion-detector.h"
-#include "aema-estimator.h"
-
-#ifdef __cplusplus
-extern "C" {
-#endif
-
-/***************************************************************************//**
- * [public] Initializaton of the singleton unit (static constructor). This
- * corresponds to service load.
- *
- * @param[in] on_motion_state_changed
- * Callback to be invoked when the detected state of motion changes. This
- * argument is optional, and can be NULL.
- * @param[in] on_location_changed
- * Callback to be invoked when the predicted or updated location changes.
- * This argument is required.
- * @param[in] handler
- * Arbitrary user parameter passed to the callback.
- */
-void fused_engine_init(
- motion_changed_cb on_motion_state_changed,
- predicted_updated_cb on_location_changed,
- gpointer handler);
-
-/***************************************************************************//**
- * [public] Cleanup of the singleton unit (static destructor). This corresponds
- * to service unload.
- */
-void fused_engine_exit(void);
-
-/***************************************************************************//**
- * [public] Start processing the location and sensory inputs, and sending back
- * notifications. This corresponds to service start.
- */
-void fused_engine_start(void);
-
-/***************************************************************************//**
- * [public] Stops processing the inputs, and sending notifications. This
- * corresponds to service stop.
- */
-void fused_engine_stop(void);
-
-/***************************************************************************//**
- * [public] Reset of the internal state back to the initial one. The handlers
- * and start/stop state are not changed. This function is used for
- * initialization, soft restarts, and repetitive testing.
- */
-void fl_kalf_reset(void);
-
-/***************************************************************************//**
- * [public] Process 2D (horizontal) position without change of the altitude or
- * speed (WPS). Kalman merging is carried out in tangent frame, and the origin
- * is moved after each measurement so that the predictions always start at
- * (0, 0, R + h).
- * - Clocks synchronization
- * - Kalman data blending
- * - Repositioning of the tangent frame
- *
- * @param[in] pos
- * Spherical position in geographic coordinates
- * @param[in] sigma
- * Level and standard deviations.
- */
-void fused_engine_process_position_2d_event(const fl_spherical_position* pos, const fl_sigma* sigma);
-
-/***************************************************************************//**
- * [public] Process 3D position along with 3D velocity (GPS). Kalman merging is
- * carried out in tangent frame, and the origin is moved after each measurement
- * so that the predictions always start at (0, 0, R + h).
- * - Clocks synchronization
- * - Kalman data blending
- * - Repositioning of the tangent frame
- * If FL_KALMAN_ANGULAR_VELOCITY is enabled the angular velocity is derived
- * from consecutive measurements of linear velocities and merged with gyroscope
- * readings.
- *
- * @param[in] pos
- * Spherical position in geographic coordinates, and altitude
- * @param[in] vel
- * Horizontal and vertical velocity (speed and climb)
- * @param[in] sigma
- * Level and standard deviations.
- */
-void fused_engine_process_position_2x3d_event(const fl_spherical_position* pos, const fl_tangent_velocity* vel, const fl_sigma* sigma);
-
-/***************************************************************************//**
- * [public] Process sensory events (acceleration & gyrocope). The time offest is
- * corrected if necessary.
- * - Clocks synchronization
- * - Device-to-tangent frame orientation
- * - Prediction
- *
- * @param[in] t
- * Sensory time-stamp
- * @param[in] sensor_id
- * Sensor identifier
- * @param[in] data
- * Vector of sensor-specific data.
- */
-void fused_engine_process_sensory_event(fl_seconds t, fl_sensory_source sensor_id, const_sensor_vector_3d_ref data);
-
-/***************************************************************************//**
- * [public] Retrieve the last processed location, typically during the
- * on_location_changed notification. This two-step procedure of extracting the
- * location allows to spare the non-trivial conversion between internal and
- * interface formats whenever the user code decides not to forward it through
- * the API.
- *
- * @param[out] pos
- * Current position in API format
- * @param[in] sensor_id
- * Current velocity in API format
- * @param[in] data
- * Current uncertainty in API format
- */
-void fused_engine_get_location(fl_spherical_position* pos, fl_tangent_velocity* vel, fl_sigma* sigma);
-
-/***************************************************************************//**
- * [public] Compute distance along the Earth great arc between two arbitrary
- * points. Notice that the altitude is ignored, i.e. this is metric is of a
- * projective space.
- *
- * @param[out] p
- * Position 3D vector in global Cartesian coordinate frame.
- * @param[out] q
- * Position 3D vector in global Cartesian coordinate frame.
- *
- * @return
- * Spherical distance between @a p and @a q.
- */
-fl_real fl_kalf_get_distance(const_fl_position_vector_ref p, const_fl_position_vector_ref q);
-
-#if defined(__FL_KALMAN_FILTER_C__)
- #define INLINE \
- inline
-#else
- #define INLINE \
- inline
-#endif
-#if defined(__FL_KALMAN_FILTER_C__)
- #ifdef GTEST
- #define PRIVATE
- #else
- #define PRIVATE \
- static
- #endif
-#else
- #define PRIVATE
-#endif
-
-#define DEFAULT_ACCELERATION_SIGMA2 SQUARE(FL_ACCEL_NOISE_LEVEL) /* [(m/s^2)^2] */
-#define DEFAULT_ROTATION_SIGMA2 SQUARE(1.0 / 256) /* [(rad/s)^2] */
-
-typedef enum {
- STATE_P, /* position [m] */
- STATE_V, /* velocity [m/s] */
- STATE_A, /* acceleration [m/s^2] */
- /* always the last */
- STATE_DIMENSION
-} _fl_kalf_state_component;
-
-typedef fl_real _fl_kalf_state_matrix[GEO_SPATIAL_DIMENSION][STATE_DIMENSION];
-typedef fl_real(*_fl_kalf_state_matrix_ref)[STATE_DIMENSION];
-typedef fl_real(*const _const_fl_kalf_state_matrix_ref)[STATE_DIMENSION];
-
-typedef fl_real _fl_kalf_state_vector[GEO_SPATIAL_DIMENSION];
-typedef fl_real(*_fl_kalf_state_vector_ref);
-typedef const fl_real(*_const_fl_kalf_state_vector_ref);
-
-typedef enum {
- QTR_X,
- QTR_Y,
- QTR_Z,
- QTR_0,
- /* always the last */
- QUATERNION_DIMENSION
-} _fl_kalf_quaternion_component;
-
-typedef fl_real _fl_kalf_quaternion[QUATERNION_DIMENSION];
-typedef fl_real(*_fl_kalf_quaternion_ref);
-typedef const fl_real(*_const_fl_kalf_quaternion_ref);
-
-//! position on the Earth surface in Cartesian coordinates
-typedef fl_meters position_vector[GEO_SPATIAL_DIMENSION];
-typedef struct {
- fl_seconds time;
- position_vector position;
-} fl_prediction;
-
-typedef fl_real _fl_kalf_matrix_2d[PLANAR_DIMENSION][PLANAR_DIMENSION];
-typedef fl_real(*_fl_kalf_matrix_2d_ref)[PLANAR_DIMENSION];
-typedef fl_real(*const _const_fl_kalf_matrix_2d_ref)[PLANAR_DIMENSION];
-
-typedef fl_real _fl_kalf_sigma_matrix[STATE_DIMENSION][STATE_DIMENSION];
-typedef fl_real(*_fl_kalf_sigma_matrix_ref)[STATE_DIMENSION];
-typedef fl_real(*const _const_fl_kalf_sigma_matrix_ref)[STATE_DIMENSION];
-
-typedef _fl_kalf_state_matrix _fl_kalf_sigma_cube[GEO_SPATIAL_DIMENSION];
-typedef _fl_kalf_state_matrix(*_fl_kalf_sigma_cube_ref);
-
-
-typedef struct {
- fl_matrix_3d m; /* rotation matrix */
- fl_vector_3d w2; /* square weight (inverse covariance) vector of matrix rows */
-} _fl_kalf_system_rotation;
-
-typedef struct {
- _fl_kalf_state_matrix x; /* state vector */
- _fl_kalf_sigma_cube s2x; /* covariance matrix */
-} _fl_kalf_state;
-
-/** Tangent space T(p)E to the embedding manifold E=SxR at point p */
-typedef struct {
- fl_radians rx; /* latitude */
- fl_radians ry; /* longitude */
- fl_real srx; /* sin(latitude) */
- fl_real crx; /* cos(latitude) */
- fl_matrix_3d fsr; /* push-forward (tangent to global) coordinate system rotation matrix */
-} _fl_kalf_tangent_frame;
-
-typedef struct {
- fl_seconds first; /* arrival of the first sensor event */
- fl_seconds last; /* arrival of the last sensor event */
- unsigned count; /* at 100Hz rate of arrival this will suffice for ~32 years */
-} _fl_kalf_sensor_times;
-
-typedef struct {
- fl_seconds last_measurement; /* arrival of the last position measurement event */
- _fl_kalf_sensor_times sensor_event;
- fl_seconds last_p_fixing; /* update of reference p (pe) */
- fl_seconds last_v_fixing; /* update of reference v (ve) => p */
- fl_seconds last_a_fixing; /* update of reference a (ae) => v, p */
- fl_seconds last_r_fixing; /* update of reference sr (sre) */
- fl_seconds last_r_diffusion; /* damping of sr weights */
- fl_seconds last_w_fixing; /* update of reference w (we) => q, sr */
-#if (FL_KALMAN_ANGULAR_VELOCITY)
- fl_seconds last_wz_fixing; /* update of reference wz */
- fl_seconds last_b_fixing; /* update of reference b */
-#endif
- fl_seconds last_reference; /* update of __se */
-} _fl_kalf_time_of;
-
-PRIVATE gboolean __started;
-PRIVATE _fl_kalf_time_of __time_of;
-PRIVATE fl_aema1d_estimator __timestamp_offset;
-PRIVATE fl_position_4d __last_notification;
-PRIVATE predicted_updated_cb __on_location_changed;
-PRIVATE gpointer __handler; /* location event handler */
-PRIVATE _fl_kalf_tangent_frame __te; /* tangent frame map */
-PRIVATE _fl_kalf_system_rotation __srp; /* predicted system rotation */
-PRIVATE _fl_kalf_system_rotation __sre; /* reference system rotation */
-PRIVATE _fl_kalf_state __sp; /* predicted 3D state */
-PRIVATE _fl_kalf_state __se; /* reference 3D state */
-PRIVATE fl_vector_3d __wp; /* predicted angular velocity */
-PRIVATE fl_vector_3d __we; /* reference angular velocity */
-PRIVATE fl_real __s2wp;
-PRIVATE fl_real __s2we;
-PRIVATE fl_real __wze; /* reference angular z-velocity (horizontal plane) */
-#if (FL_KALMAN_ANGULAR_VELOCITY)
-PRIVATE fl_real __s2wze;
-PRIVATE fl_radians __be; /* reference North bearing */
-#endif
-PRIVATE LocationStatus __fix_status; /* pass-through */
-
-/***************************************************************************//**
- * [private] Check for and correct the numerical errors which cause the
- * covariance matrix to cease being positive-definite. This operation has to be
- * performed regularly, or otherwise magic ensues, and every strange result
- * becomes possible.
- *
- * @param[in, out] src
- * The state object to be rectified. This is either the predicted (__sp),
- * or estimated (__se) one.
- */
-PRIVATE void __rectify_state(_fl_kalf_state *src);
-
-/***************************************************************************//**
- * [private] Get the latest estimate of the difference between location and
- * sensory clocks.
- *
- * @return
- * Current time-offset value.
- */
-PRIVATE INLINE fl_seconds __get_time_offset(void)
-{
- return __timestamp_offset.value;
-}
-
-/***************************************************************************//**
- * [private] Convert location timestamp to internal time in seconds. The
- * time-offset is adjusted if necessary to ensure temporal ordering of events.
- *
- * @param[in] timestamp
- * Location timestamp (wall clock since 1970)
- *
- * @return
- * Internal time.
- */
-PRIVATE fl_seconds __timestamp_to_seconds(fl_timestamp timestamp);
-
-/***************************************************************************//**
- * [private] Efficient (faster than memcpy) copy of the system rotation object.
- *
- * @param[out] dst
- * Destination system rotation object
- * @param[out] src
- * Source system rotation object
- */
-PRIVATE INLINE void __copy_system_rotation(_fl_kalf_system_rotation *dst, const _fl_kalf_system_rotation *src)
-{
- unsigned i = sizeof(_fl_kalf_system_rotation) / sizeof(unsigned);
- do {
- --i;
- ((unsigned*)dst)[i] = ((const unsigned*)src)[i];
- } while (i);
-}
-
-/***************************************************************************//**
- * [private] Efficient (faster than memcpy) copy of the state (3x3D position &
- * covariance) object.
- *
- * @param[out] dst
- * Destination state object
- * @param[out] src
- * Source state object
- */
-PRIVATE INLINE void __copy_state(_fl_kalf_state *dst, _fl_kalf_state *src)
-{
- __rectify_state(src);
- unsigned i = sizeof(_fl_kalf_state) / sizeof(unsigned);
- do {
- --i;
- ((unsigned*)dst)[i] = ((const unsigned*)src)[i];
- } while (i);
-}
-
-/***************************************************************************//**
- * [private] Approximate squared weigt (inverse covariance) of a variable which
- * is a product of two unit vertors x = u*v, |u|=1, |v|=1. The exact expression is anything but
- * trivial.
- *
- * @param[in] w2u
- * Inverse covariance of u: w2u = 1/(<u^2> - <u>^2)
- * @param[in] w2v
- * Inverse covariance of v: w2v = 1/(<v^2> - <v>^2)
- *
- * @return
- * Estimated weight of the product.
- */
-PRIVATE INLINE fl_real __product_weights2(fl_real w2u, fl_real w2v)
-{
- return w2u * w2v / (w2u + w2v);
-}
-
-/***************************************************************************//**
- * [private] 3D cross-product of two unit vectors. The resultant vector is
- * normalized to unity, in case @a u1 and @a u2 are not orthogonal.
- *
- * @param[in] u1
- * Input unit vector
- * @param[in] u2
- * Input unit vector
- * @param[out] v
- * Output unit vector
- */
-PRIVATE void __cross_product(const_fl_vector_3d_ref u1, const_fl_vector_3d_ref u2, fl_vector_3d_ref v);
-
-/***************************************************************************//**
- * [private] Create coordinate map from spherical M = S^2 x R to the tangent
- * space TM. Notice that only two angles are being used, what means the course
- * on the tangent plane is not straight North (x-axis), but still described by
- * the bearing.
- *
- * @param[in] x
- * Position and velocity in old tangent frame
- * @param[out] status
- * Status of the supplied data
- */
-PRIVATE void __create_push_forward_map(_fl_kalf_state_matrix_ref x, LocationStatus status);
-
-/***************************************************************************//**
- * [private] Convert API location to internal representation, i.e. perform
- * conversion of:
- * - units to SI;
- * - coordinate system M -> TM.
- *
- * @param[in] pos
- * Position in API format
- * @param[in] vel
- * Velocity in API format
- * @param[in] sigma
- * Uncertainty in API format
- * @param[out] pm
- * 3D position on TM in SI units
- * @param[out] vm
- * 3D velocity on TM in SI units
- * @param[out] s2pm
- * 3D position variances on TM in SI units
- * @param[out] s2vm
- * 3D velocity variances on TM in SI units
- */
-PRIVATE void __spherical_to_tangent(
- const fl_spherical_position *pos,
- const fl_tangent_velocity *vel,
- const fl_sigma *sigma,
- fl_position_vector_ref pm,
- fl_velocity_vector_ref vm,
- fl_vector_3d_ref s2pm,
- fl_vector_3d_ref s2vm);
-
-/***************************************************************************//**
- * [private] Convert tangent frame data to API location representation, i.e.
- * perform conversion of:
- * - units from SI to API (degrees, km/h, timestamps);
- * - coordinate system TM -> M.
- *
- * @param[in] x
- * 3x3 state matrix
- * @param[in] s2x
- * 3x3x3 covariance cube
- * @param[out] pos
- * Position in API format
- * @param[out] vel
- * Velocity in API format
- * @param[out] sigma
- * Uncertainty in API format
- */
-PRIVATE void __tangent_to_spherical(
- const _fl_kalf_state_matrix_ref x,
- const _fl_kalf_sigma_cube_ref s2x,
- fl_spherical_position *pos,
- fl_tangent_velocity *vel,
- fl_sigma *sigma);
-
-/***************************************************************************//**
- * [private] Retrieve the current prediction in global frame M.
- *
- * @param[in] t
- * Internal time
- * @param[out] prediction
- * Spatio-temporal (4D) position in global coordinates.
- */
-PRIVATE void __get_prediction_vector(fl_seconds t, fl_position_4d *prediction);
-
-/***************************************************************************//**
- * [private] Map a quaternion into system rotation matrix.
- *
- * @param[in] q
- * Quaternioninc (4D) representation of a rotation.
- * @param[out] sr
- * Corresponding system rotation matrix (3x3D)
- */
-PRIVATE void __quaternion_to_rotation(_const_fl_kalf_quaternion_ref q, fl_matrix_3d_ref sr);
-
-/***************************************************************************//**
- * [private] Update system rotation matrix (__se) given a measurement of the
- * device angular velocity.
- *
- * @param[in] t
- * Internal time of the measurement
- * @param[in] wm
- * 3D vector of angular velocity [rad/s] in device coordinate frame
- * @param[in] s2wm
- * Estimated variance of the measuremnt.
- */
-PRIVATE void __integrate_rw(fl_seconds t, const_fl_spin_vector_ref wm, fl_real s2wm);
-
-/***************************************************************************//**
- * [private] Extrapolate the system rotation (__sp) to the given time assuming
- * constant angular velocity.
- *
- * @param[in] t
- * Internal time
- */
-PRIVATE void __predict_rw(fl_seconds t);
-
-/***************************************************************************//**
- * [private] Planar (2D) single-order Kalman blending (of either position,
- * velocity, or acceleration). Assumes the specific prediction is already
- * performed.
- *
- * @param[in] t
- * Internal time
- * @param[in] s
- * State component identifier to which @a xm and @a s2xm refer.
- * @param[in] xm
- * Measured data
- * @param[in] s2xm
- * Data variances
- */
-PRIVATE void __kalman_2d1o(fl_seconds t, _fl_kalf_state_component s, const_fl_vector_3d_ref xm, const_fl_vector_3d_ref s2xm);
-
-/***************************************************************************//**
- * [private] Planar (2D) Kalman blending of position.
- *
- * @param[in] t
- * Internal time
- * @param[in] pm
- * Measured position
- * @param[in] s2pm
- * Position variances
- */
-PRIVATE void __kalman_p(fl_seconds t, const_fl_position_vector_ref pm, const_fl_vector_3d_ref s2pm);
-
-/***************************************************************************//**
- * [private] Spatial (3D) Kalman blending of position and velocity.
- *
- * @param[in] t
- * Internal time
- * @param[in] pm
- * Measured position
- * @param[in] vm
- * Measured velocity
- * @param[in] s2pm
- * Position variances
- * @param[in] s2vm
- * Velocity variances
- */
-PRIVATE void __kalman_pv(
- fl_seconds t,
- const_fl_position_vector_ref pm,
- const_fl_velocity_vector_ref vm,
- const_fl_vector_3d_ref s2pm,
- const_fl_vector_3d_ref s2vm);
-
-/***************************************************************************//**
- * [private] Axial (1D) Kalman blending of angular velocity along z-axis (turn).
- *
- * @param[in] t
- * Internal time
- * @param[in] wzm
- * Z-projection of the measured angular velocity
- * @param[in] s2wzm
- * Corresponding variance
- */
-void __kalman_wz(fl_seconds t, fl_real wzm, fl_real s2wzm);
-
-/***************************************************************************//**
- * [private] Kalman blending two unit 3D vectors usign inverse covariances as
- * weights (to avoid singularities).
- *
- * @param[in] t
- * Internal time
- * @param[in] u1
- * 3D vector
- * @param[in] u2
- * 3D vector
- * @param[in] w2u1
- * Inverse variance of @a u1 (can be zero)
- * @param[in] w2u2
- * Inverse variance of @a u2 (can be zero)
- * @param[out] v
- * Output 3D vector
- * @param[out] w2v
- * Output inverse variance of @a v
- */
-PRIVATE void __inv_kalman_u(
- fl_seconds t,
- const_fl_vector_3d_ref u1, fl_real w2u1,
- const_fl_vector_3d_ref u2, fl_real w2u2,
- fl_vector_3d_ref v, fl_real *w2v);
-
-/***************************************************************************//**
- * [private] Extrapolate position, velocity, and the covariance-matrices up to
- * the given time.
- *
- * @param[in] t
- * Internal time
- */
-PRIVATE void __predict_pv(fl_seconds t);
-
-/***************************************************************************//**
- * [private] Check the notification conditions; if satisfied get the current
- * prediction in global coordinate frame and signal it to the user.
- *
- * @param[in] t
- * Internal time
- */
-PRIVATE void __signal_updated_location(fl_seconds t);
-
-#if !defined(__FL_KALMAN_FILTER_C__)
- #undef PRIVATE
- #undef INLINE
-#endif
-
-#ifdef __cplusplus
-}
-#endif
-
-#endif /* __FL_KALMAN_FILTER_H__ */
diff --git a/lbs-server/src/fused/lp-3d-filter.c b/lbs-server/src/fused/lp-3d-filter.c
deleted file mode 100644
index e6bfd71..0000000
--- a/lbs-server/src/fused/lp-3d-filter.c
+++ /dev/null
@@ -1,126 +0,0 @@
-/*
- * Copyright (c) 2016-2017 Samsung Electronics Co., Ltd.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#if !defined(LOG_THRESHOLD)
- /* custom logging threshold - keep undefined on the repo */
- /* #define LOG_THRESHOLD LOG_LEVEL_TRACE */
-#endif
-
-#define __FL_LP_3D_FILTER_C__
-
-#include <memory.h>
-#include "math-ext.h"
-#include "lp-3d-filter.h"
-#include "debug_util.h"
-
-#define LP3D_INPUT_FORMAT "% 6.2f"
-
-void fl_lp3d_init(void)
-{
- LOG_FUSED_DEV(DBG_LOW, ENTER_FUNCTION_PREFIX("()"), 0);
- __lp3d_G = 0;
- __lp3d_B1 = -2;
- __lp3d_B2 = 1;
- fl_lp3d_reset();
- LOG_FUSED_DEV(DBG_LOW, LEAVE_FUNCTION_PREFIX("(): OK"), 0);
-}
-
-void fl_lp3d_exit(void)
-{
- LOG_FUSED_DEV(DBG_LOW, FUNCTION_PREFIX("()"), 0);
-}
-
-void fl_lp3d_set_sampling_frequency(fl_hertz f)
-{
- if (f > 0) {
- double omegaC = tan(M_PI * FL_LP3D_CUTOFF_FREQUENCY / f);
- double omegaC2 = fl_square(omegaC);
- double _B0;
-
- _B0 = 1.0 / (1 + 2 * omegaC * cos(M_PI_4) + omegaC2);
- __lp3d_B1 = _B0 * 2*(omegaC2 - 1);
- __lp3d_B2 = _B0 * (1 - 2 * omegaC * cos(M_PI_4) + omegaC2);
- __lp3d_G = _B0 * omegaC2;
-
- LOG_FUSED_DEV(DBG_LOW, FUNCTION_PREFIX("(%g Hz): OK"), f);
- return;
- } else {
- LOG_FUSED_DEV(DBG_ERR, FUNCTION_PREFIX("(%g Hz): E_INVALID_ARGUMENT"), f);
- return;
- }
-}
-
-void fl_lp3d_reset(void)
-{
- LOG_FUSED_DEV(DBG_LOW, FUNCTION_PREFIX("()"), 0);
- memset(__lp3d_u, 0, sizeof(__lp3d_u));
- memset(__lp3d_v, 0, sizeof(__lp3d_v));
-}
-
-void fl_lp3d_reset_to(const_fl_acceleration_vector_ref stationary)
-{
- unsigned i;
-
- LOG_FUSED_DEV(DBG_LOW, FUNCTION_PREFIX("({" LP3D_INPUT_FORMAT ", " LP3D_INPUT_FORMAT ", " LP3D_INPUT_FORMAT "})"),
- stationary[0],
- stationary[1],
- stationary[2]);
-
- i = GEO_SPATIAL_DIMENSION;
- do {
- --i;
- /* up-conversion */
- __lp3d_u[TIME_SHIFT_1][i] = stationary[i];
- __lp3d_u[TIME_SHIFT_2][i] = stationary[i];
- __lp3d_v[TIME_SHIFT_1][i] = stationary[i];
- __lp3d_v[TIME_SHIFT_2][i] = stationary[i];
- } while (i);
-}
-
-const_fl_acceleration_vector_ref fl_lp3d_process(const_fl_acceleration_vector_ref u)
-{
- unsigned i;
-
- LOG_FUSED_DEV(DBG_LOW, FUNCTION_PREFIX("({" LP3D_INPUT_FORMAT ", " LP3D_INPUT_FORMAT ", " LP3D_INPUT_FORMAT "})"),
- u[0],
- u[1],
- u[2]);
-
- i = GEO_SPATIAL_DIMENSION;
- do {
- --i;
-
- fl_acceleration u0 = u[i];
- #define u1 __lp3d_u[TIME_SHIFT_1][i]
- #define u2 __lp3d_u[TIME_SHIFT_2][i]
- fl_real v0;
- #define v1 __lp3d_v[TIME_SHIFT_1][i]
- #define v2 __lp3d_v[TIME_SHIFT_2][i]
-
- v0 = __lp3d_G * (u0 + 2 * u1 + u2) - __lp3d_B1 * v1 - __lp3d_B2 * v2;
- u2 = u1;
- u1 = u0;
- v2 = v1;
- v1 = v0;
-
- #undef u1
- #undef u2
- #undef v1
- #undef v2
- } while (i);
-
- return __lp3d_v[TIME_SHIFT_1];
-}
diff --git a/lbs-server/src/fused/lp-3d-filter.h b/lbs-server/src/fused/lp-3d-filter.h
deleted file mode 100644
index 9277cc8..0000000
--- a/lbs-server/src/fused/lp-3d-filter.h
+++ /dev/null
@@ -1,126 +0,0 @@
-/*
- * Copyright (c) 2016-2017 Samsung Electronics Co., Ltd.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-/**
- * @file lp-3d-filter.h
- * @brief Gravity direction estimator via low-pass 3D 2nd order Butterworth
- * filter.
- */
-
-#pragma once
-#ifndef __FL_LP_3D_FILTER_H__
-#define __FL_LP_3D_FILTER_H__
-
-#include "types.h"
-#include "math-ext.h"
-#include "parameters.h"
-
-#ifdef __cplusplus
-extern "C" {
-#endif
-
-/***************************************************************************//**
- * [public] Initializaton of the singleton unit (static constructor).
- */
-void fl_lp3d_init(void);
-
-/***************************************************************************//**
- * [public] Cleanup of the singleton unit (static destructor).
- */
-void fl_lp3d_exit(void);
-
-/***************************************************************************//**
- * [public] Adjust internal parameters to the given sampling frequency and reset
- * the the past state memory.
- *
- * @param[in] f
- * The sampling frequency in [Hz] of the accelerometer (or processing
- * callback if downsampled).
- */
-void fl_lp3d_set_sampling_frequency(fl_hertz f);
-
-/***************************************************************************//**
- * [public] Reset the past state memory.
- */
-void fl_lp3d_reset(void);
-
-/***************************************************************************//**
- * [public] Reset to the given stationary state (input = output).
- *
- * @param[in] stationary
- * 3D vector of acceleration measured in tangent coordinate frame.
- */
-void fl_lp3d_reset_to(const_fl_acceleration_vector_ref stationary);
-
-/***************************************************************************//**
- * [public] Process single input sample through the 3D Butterworth filter.
- *
- * @param[in] u
- * 3D acceleration input vector.
- *
- * @return
- * 3D acceleration output vector.
- */
-const_fl_acceleration_vector_ref fl_lp3d_process(const_fl_acceleration_vector_ref u);
-
-#if defined(__FL_LP_3D_FILTER_C__)
- #define INLINE \
- inline
-#else
- #define INLINE
-#endif
-#if defined(__FL_LP_3D_FILTER_C__)
- #ifdef GTEST
- #define PRIVATE
- #else
- #define PRIVATE \
- static
- #endif
-#else
- #define PRIVATE \
- extern
-#endif
-
-typedef enum {
- TIME_SHIFT_1, /* t - 1 */
- TIME_SHIFT_2, /* t - 2 */
- /* always the last */
- TIME_SHIFT_COUNT
-} _fl_lp3d_time_shift;
-
-/* filter coefficients */
-PRIVATE fl_real __lp3d_G; /* omega^2/B_0 */
-PRIVATE fl_real __lp3d_B1; /* B_1/B_0 */
-PRIVATE fl_real __lp3d_B2; /* B_2/B_0 */
-/* memory of past states */
-PRIVATE fl_acceleration_vector __lp3d_u[TIME_SHIFT_COUNT];
-PRIVATE fl_acceleration_vector __lp3d_v[TIME_SHIFT_COUNT];
-
-#if (LP3D_FEATURE_SIGMA2)
-PRIVATE fl_real __lp3d_u2[TIME_SHIFT_COUNT];
-PRIVATE fl_real __lp3d_v2[TIME_SHIFT_COUNT];
-#endif
-
-#if !defined(__FL_LP_3D_FILTER_C__)
- #undef PRIVATE
- #undef INLINE
-#endif
-
-#ifdef __cplusplus
-}
-#endif
-
-#endif /* __FL_LP_3D_FILTER_H__ */
diff --git a/lbs-server/src/fused/math-ext.c b/lbs-server/src/fused/math-ext.c
deleted file mode 100644
index 6ff3419..0000000
--- a/lbs-server/src/fused/math-ext.c
+++ /dev/null
@@ -1,50 +0,0 @@
-/*
- * Copyright (c) 2016-2017 Samsung Electronics Co., Ltd.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-/**
- * @file math-ext.c
- * @brief Math extensions
- */
-
-#if !defined(LOG_THRESHOLD)
- /* custom logging threshold - keep undefined on the repo */
- /* #define LOG_THRESHOLD LOG_LEVEL_TRACE */
-#endif
-
-#define __FL_MATH_EXT_C__
-
-#include <stdio.h>
-#include "math-ext.h"
-#include "debug_util.h"
-
-#define PRECISION 1e-5
-
-double fl_asin(double x)
-{
- if (x >= 1) {
- if (x > 1 + PRECISION) {
- LOG_FUSED_DEV(DBG_ERR, FUNCTION_PREFIX("(%g) OUT_OF_DOMAIN"), x);
- }
- return M_PI_2;
- } else if (x <= -1) {
- if (x < -(1 + PRECISION)) {
- LOG_FUSED_DEV(DBG_ERR, FUNCTION_PREFIX("(%g) OUT_OF_DOMAIN"), x);
- }
- return -M_PI_2;
- } else
- return asin(x);
-}
-
diff --git a/lbs-server/src/fused/math-ext.h b/lbs-server/src/fused/math-ext.h
index 61e4eef..07e5e3e 100644
--- a/lbs-server/src/fused/math-ext.h
+++ b/lbs-server/src/fused/math-ext.h
@@ -20,8 +20,8 @@
*/
#pragma once
-#ifndef __FL_MATH_EXT_H__
-#define __FL_MATH_EXT_H__
+#ifndef __MATH_EXT_H__
+#define __MATH_EXT_H__
#define _USE_MATH_DEFINES
#include <math.h>
@@ -31,14 +31,6 @@
extern "C" {
#endif
-#if defined(__FL_MATH_EXT_C__)
- #define INLINE \
- static inline
-#else
- #define INLINE \
- static inline
-#endif
-
#if !defined(M_PI) /* C99 */
#define M_PI 3.14159265358979323846 /* pi */
#define M_PI_2 1.57079632679489661923 /* pi/2 */
@@ -54,154 +46,34 @@ extern "C" {
#define M_1_SQRT3 0.577350269189625764509 /* 1/sqrt(3) */
#define M_2PI (2 * M_PI) /* 2*pi */
-/*******************************************************************//**
- * [public] Second power of a number. Use this macro for preprocessed
- * values and static variables.
- *
- * @param[in] x
- * Arbitrary number
- *
- * @return
- * Second power of @a x.
+/**
+ * @brief Second power of a number. Use this macro for preprocessed values and static variables.
+ * @param[in] x Arbitrary number
+ * @return Second power of @a x.
*/
#define SQUARE(x) ((x) * (x))
-/*******************************************************************//**
- * [public] Second power of a number. Use for dynamic variables.
- *
- * @param[in] x
- * Arbitrary number
- *
- * @return
- * Second power of @a x.
- */
-INLINE double fl_square(double x)
-{
- return SQUARE(x);
-}
-
-/*******************************************************************//**
- * [public] Domain-safe arcus-sine. If the argument runs out of asin()
- * domain it is cast-back onto the nearest edge. If it departs beyond
- * numerical error margin, the error is reported, and breakpoint fired.
- * No exception is thrown in release mode.
- *
- * @param[in] x
- * Number in the [-1, 1] range.
- *
- * @return
- * - arcus sine of @x if x < 1 && x > -1,
- * - -pi if x <= -1,
- * - pi if x >= 1.
- */
-double fl_asin(double x);
-
-/*******************************************************************//**
- * [public] Squared length of a 2D vector in Euclidean metric.
- *
- * @param[in] x
- * 2D vector
- *
- * @return
- * Squared length of @a x, i.e. |x|^2 = x.x
- */
-INLINE double fl_vector_2d_length2(const double x[])
-{
- return SQUARE(x[0]) + SQUARE(x[1]);
-}
-
-/*******************************************************************//**
- * [public] Squared length of a 3D vector in Euclidean metric.
- *
- * @param[in] x
- * 3D vector
- *
- * @return
- * Squared length of @a x, i.e. |x|^2 = x.x
- */
-INLINE double fl_vector_3d_length2(const double x[])
-{
- return SQUARE(x[0]) + SQUARE(x[1]) + SQUARE(x[2]);
-}
-
-/*******************************************************************//**
- * [public] Inlined left-action of a linear operator on a 3D
- * covector.
- *
- * @param[in] m
- * 3x3D matrix
- * @param[in] v
- * 3D vector
- * @param[out] out
- * Left-action of the linear operator @a m on the vector @a v, i.e.
- * out = m.v
- */
-#define MATRIX_DOT_VECTOR_3D(m, v, out)\
- do {\
- (out)[GEO_SPATIAL_X] = (m)[GEO_SPATIAL_X][GEO_SPATIAL_X] * (v)[GEO_SPATIAL_X] + (m)[GEO_SPATIAL_X][GEO_SPATIAL_Y] * (v)[GEO_SPATIAL_Y] + (m)[GEO_SPATIAL_X][GEO_SPATIAL_Z] * (v)[GEO_SPATIAL_Z];\
- (out)[GEO_SPATIAL_Y] = (m)[GEO_SPATIAL_Y][GEO_SPATIAL_X] * (v)[GEO_SPATIAL_X] + (m)[GEO_SPATIAL_Y][GEO_SPATIAL_Y] * (v)[GEO_SPATIAL_Y] + (m)[GEO_SPATIAL_Y][GEO_SPATIAL_Z] * (v)[GEO_SPATIAL_Z];\
- (out)[GEO_SPATIAL_Z] = (m)[GEO_SPATIAL_Z][GEO_SPATIAL_X] * (v)[GEO_SPATIAL_X] + (m)[GEO_SPATIAL_Z][GEO_SPATIAL_Y] * (v)[GEO_SPATIAL_Y] + (m)[GEO_SPATIAL_Z][GEO_SPATIAL_Z] * (v)[GEO_SPATIAL_Z];\
- } while (0)
-
-/*******************************************************************//**
- * [public] Inlined right-action of a linear operator on a 3D
- * covector. In Euclidean metric this is the same as left action of the
- * transposed operator on the vector.
- *
- * @param[in] v
- * 3D vector
- * @param[in] m
- * 3x3D matrix
- * @param[out] out
- * Right-action of the linear operator @a m on the covector @a v, i.e.
- * out = v.m
- */
-#define VECTOR_DOT_MATRIX_3D(v, m, out)\
- do {\
- (out)[GEO_SPATIAL_X] = (v)[GEO_SPATIAL_X] * (m)[GEO_SPATIAL_X][GEO_SPATIAL_X] + (v)[GEO_SPATIAL_Y] * (m)[GEO_SPATIAL_Y][GEO_SPATIAL_X] + (v)[GEO_SPATIAL_Z] * (m)[GEO_SPATIAL_Z][GEO_SPATIAL_X];\
- (out)[GEO_SPATIAL_Y] = (v)[GEO_SPATIAL_X] * (m)[GEO_SPATIAL_X][GEO_SPATIAL_Y] + (v)[GEO_SPATIAL_Y] * (m)[GEO_SPATIAL_Y][GEO_SPATIAL_Y] + (v)[GEO_SPATIAL_Z] * (m)[GEO_SPATIAL_Z][GEO_SPATIAL_Y];\
- (out)[GEO_SPATIAL_Z] = (v)[GEO_SPATIAL_X] * (m)[GEO_SPATIAL_X][GEO_SPATIAL_Z] + (v)[GEO_SPATIAL_Y] * (m)[GEO_SPATIAL_Y][GEO_SPATIAL_Z] + (v)[GEO_SPATIAL_Z] * (m)[GEO_SPATIAL_Z][GEO_SPATIAL_Z];\
- } while (0)
-
-/*******************************************************************//**
- * [public] Inlined cross-product of two 3D vectors.
- *
- * @param[in] u
- * 3D vector
- * @param[in] v
- * 3D vector
- * @param[out] out
- * Cross-product of @a u and @a v
+/**
+ * @brief Second power of a number. Use for dynamic variables.
+ * @param[in] x Arbitrary number
+ * @return[double] Second power of @a x.
*/
-#define VECTOR_CROSS_VECTOR_3D(u, v, out)\
- do {\
- (out)[GEO_SPATIAL_X] = (u)[GEO_SPATIAL_Y] * (v)[GEO_SPATIAL_Z] - (u)[GEO_SPATIAL_Z] * (v)[GEO_SPATIAL_Y];\
- (out)[GEO_SPATIAL_Y] = (u)[GEO_SPATIAL_Z] * (v)[GEO_SPATIAL_X] - (u)[GEO_SPATIAL_X] * (v)[GEO_SPATIAL_Z];\
- (out)[GEO_SPATIAL_Z] = (u)[GEO_SPATIAL_X] * (v)[GEO_SPATIAL_Y] - (u)[GEO_SPATIAL_Y] * (v)[GEO_SPATIAL_X];\
- } while (0)
+#define fl_square(x) ({double __y__ = (x); __y__ * __y__;})
-/*******************************************************************//**
- * [public] Scalar Euclidean product of two 3D vectors.
- *
- * @param[in] x
- * 3D vector
- * @param[in] y
- * 3D vector
- *
- * @return
- * Scalar product of @a x and @a y.
+/**
+ * @brief Mean Earth gravity value in [m/s^2].
+ * Notice that, strictly speaking, the mean Earth gravity acceleration at surface
+ * level is negative; the positive value we measure with an accelerometer is the
+ * ground reaction which counters the gravity and prevents us from falling into the interior.
+ * Also notice that given the inaccuracy (i.e. bias & miscalibration) of mobile device
+ * acceleromenters at ~1 [m/s^2] the number of significant digits in the reference value below is way too many.
*/
-INLINE double fl_vector_3d_dot_product(const double x[], const double y[])
-{
- return x[0] * y[0] + x[1] * y[1] + x[2] * y[2];
-}
+#define EARTH_GRAVITY 9.80665
-#if !defined(__FL_MATH_EXT_C__)
- #undef INLINE
-#endif
+#define EARTH_RADIUS 6371000.0
#ifdef __cplusplus
}
#endif
-#endif /* __FL_MATH_EXT_H__ */
+#endif /* __MATH_EXT_H__ */
diff --git a/lbs-server/src/fused/motion-detector.c b/lbs-server/src/fused/motion-detector.c
deleted file mode 100644
index f220867..0000000
--- a/lbs-server/src/fused/motion-detector.c
+++ /dev/null
@@ -1,109 +0,0 @@
-/*
- * Copyright (c) 2016-2017 Samsung Electronics Co., Ltd.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#if !defined(LOG_THRESHOLD)
- /* custom logging threshold - keep undefined on the repo */
- /* #define LOG_THRESHOLD LOG_LEVEL_TRACE */
-#endif
-
-#define __FL_MOTION_DETECTOR_C__
-
-#define _USE_MATH_DEFINES
-#include <math.h>
-#include "math-ext.h"
-#include "motion-detector.h"
-#include "debug_util.h"
-
-#if (FL_MOTION_DETECTOR)
-
-void fl_moti_init(motion_changed_cb motion_changed, gpointer handler)
-{
- LOG_FUSED_DEV(DBG_LOW, FUNCTION_PREFIX("(motion_cb=%p, handler=%p)"), motion_changed, handler);
- fl_aema1d_init(&__moti_al2, 1 << FL_MOTI_AEMA_PLATEAU_BITS);
- __moti_immobile_time = 0;
- __moti_state = MOTI_UNDECIDED;
- __moti_last_notification = MOTI_UNDECIDED;
- __moti_motion_changed = motion_changed;
- __moti_handler = handler;
-}
-
-void fl_moti_exit(void)
-{
- LOG_FUSED_DEV(DBG_LOW, FUNCTION_PREFIX("()"), 0);
- __moti_handler = NULL;
- __moti_motion_changed = NULL;
- __moti_last_notification = MOTI_UNDECIDED;
- __moti_state = MOTI_UNDECIDED;
- __moti_immobile_time = 0;
- fl_aema1d_exit(&__moti_al2);
-}
-
-void fl_moti_reset(void)
-{
- LOG_FUSED_DEV(DBG_LOW, FUNCTION_PREFIX("()"), 0);
- __moti_immobile_time = 0;
- __moti_state = MOTI_UNDECIDED;
- __moti_last_notification = MOTI_UNDECIDED;
- fl_aema1d_reset(&__moti_al2);
-}
-
-PRIVATE void __moti_set_state(fl_motion_state state)
-{
- LOG_FUSED_DEV(DBG_LOW, FUNCTION_PREFIX("(%u->%u)"), __moti_state, state);
- __moti_state = state;
- if (__moti_motion_changed && __moti_last_notification != state) {
- __moti_motion_changed(state, __moti_handler);
- __moti_last_notification = state;
- }
-}
-
-void fl_moti_process(fl_seconds t, fl_acceleration_vector_ref al)
-{
- fl_real al2;
-
- al2 = fl_aema1d_process(&__moti_al2, fl_vector_3d_length2(al));
- if (al2 > SQUARE(FL_MOTI_MOTION_LEVEL)) {
- switch (__moti_state) {
- case MOTI_UNDECIDED:
- case MOTI_IMMOBILE:
- case MOTI_SLEEP:
- __moti_set_state(MOTI_MOVING);
- break;
-
- default: /* MOTI_MOVING */
- break;
- }
- } else if (al2 < SQUARE(FL_MOTI_NOISE_LEVEL)) {
- switch (__moti_state) {
- case MOTI_UNDECIDED:
- case MOTI_MOVING:
- __moti_set_state(MOTI_IMMOBILE);
- __moti_immobile_time = t;
- break;
-
- case MOTI_IMMOBILE:
- if (t - __moti_immobile_time > FL_MOTI_IMMOBILITY_INTERVAL)
- __moti_set_state(MOTI_SLEEP);
- break;
-
- default:
- /* MOTI_SLEEP: */
- break;
- }
- }
-}
-
-#endif
diff --git a/lbs-server/src/fused/motion-detector.h b/lbs-server/src/fused/motion-detector.h
deleted file mode 100644
index 8d31ee1..0000000
--- a/lbs-server/src/fused/motion-detector.h
+++ /dev/null
@@ -1,133 +0,0 @@
-/*
- * Copyright (c) 2016-2017 Samsung Electronics Co., Ltd.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-/**
- * @file motion-detector.h
- * @brief Accelerometer-based detector of significant motion.
- */
-
-#pragma once
-#ifndef __FL_MOTION_DETECTOR_H__
-#define __FL_MOTION_DETECTOR_H__
-
-#include <glib.h>
-#include "types.h"
-#include "parameters.h"
-#include "aema-estimator.h"
-
-#ifdef __cplusplus
-extern "C" {
-#endif
-
-typedef enum {
- MOTI_UNDECIDED, /* initial */
- MOTI_MOVING, /* acceleration above MOTION_LEVEL */
- MOTI_IMMOBILE, /* acceleration below NOISE_LEVEL */
- MOTI_SLEEP, /* immobile for IMMOBILITY_INTERVAL */
-} fl_motion_state;
-
-/***************************************************************************//**
- * [public] Callback invoked on every change in motion state.
- *
- * @param[in] state
- * New motion state.
- * @param[in] handler
- * Arbitrary pointer to user data specified during the init() call.
- */
-typedef void (*motion_changed_cb)(fl_motion_state state, gpointer handler);
-
-#if (FL_MOTION_DETECTOR)
-
-/***************************************************************************//**
- * [public] Initializaton of the singleton unit (static constructor).
- *
- * @param[in] motion_changed
- * Callback to be invoked when the detected state of motion changes.
- * @param[in] handler
- * Arbitrary user parameter passed to the callback.
- */
-void fl_moti_init(motion_changed_cb motion_changed, gpointer handler);
-
-/***************************************************************************//**
- * [public] Cleanup of the singleton unit (static destructor).
- */
-void fl_moti_exit(void);
-
-/***************************************************************************//**
- * [public] Reset of the internal state back to initial one. This function is
- * used for repetitive testing and module soft restarts.
- */
-void fl_moti_reset(void);
-
-/***************************************************************************//**
- * [public] Processing of a single sample from the accelerometer. This function
- * implments a hysteresis in the 2D space of linear acceleration modulus |al|^2
- * vs motion state:
- * - Crossing the FL_MOTI_MOTION_LEVEL from below changes state to MOVING;
- * - Crossing the FL_MOTI_NOISE_LEVEL from above changes state to IMMOBILE;
- * - Staying IMMOBILE for longer than FL_MOTI_IMMOBILITY_INTERVAL changes
- * state to SLEEP.
- *
- * @param[in] t
- * Time of the event in seconds.
- * @param[in] al
- * 3D vector of linear acceleration.
- */
-void fl_moti_process(fl_seconds t, fl_acceleration_vector_ref al);
-
-#if defined(__FL_MOTION_DETECTOR_C__)
- #ifdef GTEST
- #define PRIVATE
- #else
- #define PRIVATE \
- static
- #endif
-#else
- #define PRIVATE \
- extern
-#endif
-
-PRIVATE fl_aema1d_estimator __moti_al2; /**< Linear acceleration low-pass filter */
-PRIVATE fl_seconds __moti_immobile_time; /**< Start time of immobility */
-PRIVATE fl_motion_state __moti_state; /**< Curent state */
-PRIVATE fl_motion_state __moti_last_notification; /**< Last notified state */
-PRIVATE motion_changed_cb __moti_motion_changed; /**< User callback */
-PRIVATE gpointer __moti_handler; /**< User data */
-
-/***************************************************************************//**
- * [private] Change the state indicator and notify the user.
- *
- * @param[in] state
- * New state value (does not have to be different than present one).
- */
-PRIVATE void __moti_set_state(fl_motion_state state);
-
-#if !defined(__FL_MOTION_DETECTOR_C__)
- #undef PRIVATE
-#endif
-
-#else
- #define fl_moti_init(motion_changed, handler)
- #define fl_moti_exit()
- #define fl_moti_reset()
- #define fl_moti_process(t, al)
-#endif
-
-#ifdef __cplusplus
-}
-#endif
-
-#endif /* __FL_MOTION_DETECTOR_H__ */
diff --git a/lbs-server/src/fused/parameters.h b/lbs-server/src/fused/parameters.h
index 2f2c053..4513493 100644
--- a/lbs-server/src/fused/parameters.h
+++ b/lbs-server/src/fused/parameters.h
@@ -33,15 +33,21 @@
#define FL_KALMAN_ANGULAR_VELOCITY 1
/** 0 = off, 1 = Filtration of output location notifications */
#define FL_MIN_NOTIFICATION_FILTRATION 1
-/** Minimal interval in miliseconds between consecutive output location notifications */
-#define FL_MIN_NOTIFICATION_INTERVAL 1000
+/** Minimal interval in seconds between consecutive output location notifications */
+#define FL_MIN_NOTIFICATION_INTERVAL 1.0
+/** Maximum time in seconds from last input location for sending output position to clients */
+#define FL_MAX_TIME_FROM_LAST_POS 300.0
+/** Max time in seconds from last gps when pedometer skip may occur (filtration pedometer false positive in driving case) */
+#define FL_MAX_PEDOMETER_SKIP_TIME 60.0
+/** Min speed in km/h from last gps when pedometer skip may occur (filtration pedometer false positive in driving case) */
+#define FL_MIN_PEDOMETER_SKIP_SPEED 10.0
/** 0 = off, 1 = fused devel logs on */
#define FL_ENABLE_DEVEL_LOG 0
/* Free parameters */
/** [ms] Sensor sampling interval */
-#define FL_SENSOR_SAMPLING_INTERVAL 100
+#define FL_SENSOR_SAMPLING_INTERVAL 10
/** [m] Default standard deviation of measured position */
#define FL_DEFAULT_POSITION_SIGMA 8.0
/** [m/s] Default standard deviation of measured linear velocity */
@@ -49,14 +55,14 @@
/** [rad/s] Default standard deviation of measured angular velocity */
#define FL_DEFAULT_SPIN_SIGMA 0.2
/** [m/s^2] Accelerometer noise level */
-#define FL_ACCEL_NOISE_LEVEL ((fl_acceleration)0.25)
+#define FL_ACCEL_NOISE_LEVEL 0.25
/** [Hz] Low-pass acceleration frequency threshold for estimation of gravity direction */
-#define FL_LP3D_CUTOFF_FREQUENCY ((fl_hertz)1.0)
+#define FL_LP3D_CUTOFF_FREQUENCY 10
/** [m/s^2] Upper level of IMMOBILE state */
#define FL_MOTI_NOISE_LEVEL FL_ACCEL_NOISE_LEVEL
/** [m/s^2] Lower level of MOVING state */
#define FL_MOTI_MOTION_LEVEL (3*FL_ACCEL_NOISE_LEVEL)
-/** [s] Continuous immobility interval before transitioning to SLEEP state */
+/** [s] Continuous immobility interval before transition to SLEEP state */
#define FL_MOTI_IMMOBILITY_INTERVAL 90
/** Log-2 of the AEMA averaging constant used for motion detection. */
#define FL_MOTI_AEMA_PLATEAU_BITS 4
diff --git a/lbs-server/src/fused/types.h b/lbs-server/src/fused/types.h
deleted file mode 100644
index 8c8f9e9..0000000
--- a/lbs-server/src/fused/types.h
+++ /dev/null
@@ -1,210 +0,0 @@
-/*
- * Copyright (c) 2016-2017 Samsung Electronics Co., Ltd.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-/**
- * @file types.h
- * @brief Core types definitions
- */
-
-#pragma once
-#ifndef __FL_TYPES_H__
-#define __FL_TYPES_H__
-
-#include "location-types.h"
-#include "location-position.h"
-#include "location-velocity.h"
-#include "location-accuracy.h"
-
-#ifdef __cplusplus
-extern "C" {
-#endif
-
-typedef double fl_real; /**< Default precision for the floating-point data in fused location */
-typedef float sensor_real; /**< Default precision for the floating-point data in sensors */
-typedef fl_real fl_seconds; /**< Time variables in SI units */
-typedef unsigned int fl_timestamp; /**< Time-stamps in [s] */
-typedef fl_real fl_hertz; /**< Frequency variables in SI units */
-typedef fl_real fl_meters; /**< Length variables in SI units */
-typedef fl_real fl_velocity; /**< Linear velocity in SI units [m/s] */
-typedef fl_real fl_km_h; /**< Linear velocity in SI-derived units [km/h] */
-typedef fl_real fl_acceleration; /**< Linear acceleration in SI units [m/s^2] */
-typedef sensor_real sensor_acceleration; /**< Linear acceleration in SI units [m/s^2] supplied by <sensor.h> */
-typedef fl_real fl_radians; /**< Angular position in SI units [rad] modulo 3.14159 */
-typedef sensor_real sensor_radians; /**< Angular position in SI units [rad] modulo 3.14159 supplied by <sensor.h> */
-typedef fl_real fl_degrees; /**< Angular position modulo 360 */
-typedef fl_real fl_spin; /**< Angular velocity in SI units [rad/s] modulo 3.14159 */
-typedef sensor_real sensor_spin; /**< Angular velocity in SI units [rad/s] modulo 3.14159 supplied by <sensor.h> */
-typedef fl_real fl_utesla; /**< Magnetic field strength (B) in [uT] */
-typedef sensor_real sensor_utesla; /**< Magnetic field strength (B) in [uT] supplied by <sensor.h> */
-
-#define FL_UNDEFINED_TIME ((fl_seconds)-1e+48)
-
-/** Labels for a planar 2D space*/
-typedef enum {
- PLANAR_X,
- PLANAR_Y,
- /* always the last */
- PLANAR_DIMENSION
-} fl_planar;
-
-/** Labels for an axial (or cylyndrical without phase) 2D space */
-typedef enum {
- AXIAL_H,
- AXIAL_V,
- /* always the last */
- AXIAL_DIMENSION
-} fl_axial;
-
-/** Labels for a geographic coordinate system (left-handed) */
-typedef enum {
- GEO_SPATIAL_X,
- GEO_SPATIAL_Y,
- GEO_SPATIAL_Z,
- /* always the last */
- GEO_SPATIAL_DIMENSION
-} fl_geo_spatial;
-
-/** Labels for a device coordinate system (right-handed) */
-typedef enum {
- DEV_SPATIAL_X = GEO_SPATIAL_Y,
- DEV_SPATIAL_Y = GEO_SPATIAL_X,
- DEV_SPATIAL_Z = GEO_SPATIAL_Z,
- DEV_SPATIAL_DIMENSION = GEO_SPATIAL_DIMENSION
-} fl_dev_spatial;
-
-/** Labels for sensors */
-typedef enum {
- ACCELEROMETER,
- GYROSCOPE,
- SENSOR_SOURCE_NUM
-} fl_sensory_source;
-
-/** Flag indicators for sensors' combination */
-typedef enum {
- NO_SENSOR_FLAG = 0,
- ACCELEROMETER_FLAG = (1 << ACCELEROMETER),
- GYROSCOPE_FLAG = (1 << GYROSCOPE),
- /* always the last */
- SUPPORTED_SENSORS_MASK = (1 << SENSOR_SOURCE_NUM) - 1
-} fl_sensory_flags;
-
-/** Three Cartesian coordinates of a point on the Earth surface */
-typedef fl_meters fl_position_vector[GEO_SPATIAL_DIMENSION];
-typedef fl_meters *fl_position_vector_ref;
-typedef const fl_meters *const_fl_position_vector_ref;
-
-typedef fl_velocity fl_velocity_vector[GEO_SPATIAL_DIMENSION];
-typedef fl_velocity *fl_velocity_vector_ref;
-typedef const fl_velocity *const_fl_velocity_vector_ref;
-
-/** Three device-oriented axes of the acceleration */
-typedef sensor_acceleration sensor_acceleration_vector[GEO_SPATIAL_DIMENSION];
-typedef sensor_acceleration *sensor_acceleration_vector_ref;
-typedef const sensor_acceleration *const_sensor_acceleration_vector_ref;
-typedef fl_acceleration fl_acceleration_vector[GEO_SPATIAL_DIMENSION];
-typedef fl_acceleration *fl_acceleration_vector_ref;
-typedef const fl_acceleration *const_fl_acceleration_vector_ref;
-
-/** Three imaginary axes of the rotation quaternion */
-typedef sensor_spin sensor_spin_vector[GEO_SPATIAL_DIMENSION];
-typedef sensor_spin *sensor_spin_vector_ref;
-typedef const sensor_spin *const_sensor_spin_vector_ref;
-typedef fl_spin fl_spin_vector[GEO_SPATIAL_DIMENSION];
-typedef fl_spin *fl_spin_vector_ref;
-typedef const fl_spin *const_fl_spin_vector_ref;
-
-typedef sensor_utesla sensor_magnetic_vector[GEO_SPATIAL_DIMENSION];
-typedef sensor_utesla *sensor_magnetic_vector_ref;
-typedef const sensor_utesla *const_sensor_magnetic_vector_ref;
-typedef fl_utesla fl_magnetic_vector[GEO_SPATIAL_DIMENSION];
-typedef fl_utesla *fl_magnetic_vector_ref;
-typedef const fl_utesla *const_fl_magnetic_vector_ref;
-
-typedef const sensor_real *const_sensor_vector_3d_ref;
-
-typedef fl_real fl_vector_2d[AXIAL_DIMENSION];
-typedef fl_real *fl_vector_2d_ref;
-typedef const fl_real *const_fl_vector_2d_ref;
-
-typedef fl_real fl_vector_3d[GEO_SPATIAL_DIMENSION];
-typedef fl_real *fl_vector_3d_ref;
-typedef const fl_real *const_fl_vector_3d_ref;
-
-typedef fl_real fl_matrix_3d[GEO_SPATIAL_DIMENSION][GEO_SPATIAL_DIMENSION];
-typedef fl_real(*fl_matrix_3d_ref)[GEO_SPATIAL_DIMENSION];
-typedef fl_real(*const const_fl_matrix_3d_ref)[GEO_SPATIAL_DIMENSION];
-
-/* Spatio-temporal position in Cartesian coordinates */
-typedef struct {
- fl_position_vector spatial;
- fl_seconds time;
-} fl_position_4d;
-
-/*! Position in spherical coordintes */
-typedef LocationPosition fl_spherical_position;
-/*! Velocity in coordinates of tangent space (tangent plane x ray coordinate) */
-typedef LocationVelocity fl_tangent_velocity;
-/*! Standard deviations (1-sigma, or CEP 67%) */
-typedef struct {
- LocationAccuracyLevel level; /**< ovelapps with 'accuracy.level' */
- fl_meters of_horizontal_pos; /**< std. dev. of horizontal position in [m] */
- fl_meters of_altitude; /**< std. dev. of vertical position in [m] */
- fl_velocity of_speed; /**< std. dev. of horizontal velocity in [m/s] */
- fl_velocity of_climb; /**< std. dev. of vertical velocity in [m/s] */
-} fl_sigma;
-
-/** Huge sigma value representing "almost flat" distribution. Unfortunately,
- * we're forced by the API to use uncertainties (standard deviations, CEPs,
- * or variances if squared) instead of the certainties (weights, or accuracies)
- * which are their inverses and do not suffer this initialization problem.
- * Even more peculiar is the custom of twisting the brain and calling
- * uncertainty 'accuracy'. By this token zero represents the maximum, while
- * infinity - the minimum. Beware.
- */
-#define FLAT_SIGMA 1e24
-#define FLAT_SIGMA2 (FLAT_SIGMA * FLAT_SIGMA)
-
-typedef union {
- LocationAccuracy accuracy;
- fl_sigma sigma;
-} fl_uncertainty_union;
-
-/*! Complete location data */
-typedef struct {
- fl_spherical_position pos;
- fl_tangent_velocity vel;
- union {
- LocationAccuracy accuracy;
- fl_sigma sigma;
- };
-} fl_location;
-
-/***************************************************************************//**
- * Callback invoked on every new predicted position.
- *
- * @param[in] position
- * The structure holds current filter output: spatio-temporal vector of
- * dimension (3+1) in Cartesian coordinate system.
- * @param[in] handler
- * Arbitrary pointer to user data specified during the init() call.
- */
-typedef void (*predicted_updated_cb)(const fl_position_4d* position, gpointer handler);
-
-#ifdef __cplusplus
-}
-#endif
-
-#endif /* __FL_TYPES_H__ */
diff --git a/lbs-server/src/lbs_server.c b/lbs-server/src/lbs_server.c
index a8eb904..59c31b7 100644
--- a/lbs-server/src/lbs_server.c
+++ b/lbs-server/src/lbs_server.c
@@ -26,6 +26,7 @@
#include <vconf.h>
#include "setting.h"
#include <lbs_dbus_server.h>
+#include <system_info.h>
#include "gps_plugin_data_types.h"
#include "lbs_server.h"
@@ -47,6 +48,9 @@
#define MAX_BATCH_INTERVAL 255
#define MAX_BATCH_PERIOD 60000
+// TODO blocking area test
+#define VIRTUAL_BLOCK_AREA 1
+
typedef struct {
/* gps variables */
pos_data_t position;
@@ -79,10 +83,10 @@ typedef struct {
/* dynamic interval update */
GHashTable *gps_interval_table;
- GHashTable *fused_interval_table;
guint *optimized_interval;
guint temp_minimum_interval;
gboolean is_changing_gps_interval;
+ gint current_location_source;
#ifndef TIZEN_DEVICE
GHashTable *batch_interval_table;
guint *optimized_batch_array;
@@ -97,10 +101,12 @@ typedef struct {
GVariant *mock_accuracy;
/* fused variables */
+ GHashTable *fused_interval_table;
gint fused_high_count;
gint fused_balance_count;
gboolean is_fused_running;
- gint current_location_source;
+ gboolean fused_feature;
+ gint fused_test_block;
} lbs_server_s;
#ifndef TIZEN_DEVICE
@@ -115,7 +121,7 @@ typedef enum {
LOCATION_SOURCE_NONE = 0,
LOCATION_SOURCE_GPS,
LOCATION_SOURCE_WPS,
- LOCATION_SOURCE_BOTH,
+ LOCATION_SOURCE_ALL,
} lbs_current_location_source;
typedef enum {
@@ -233,6 +239,7 @@ static void nps_set_status(lbs_server_s *lbs_server, LbsStatus status)
static void nps_update_position(lbs_server_s *lbs_server, NpsManagerPositionExt wps)
{
+ LOG_NPS(DBG_LOW, "ENTER >>>");
if (!lbs_server) {
LOG_NPS(DBG_ERR, "lbs-server is NULL!!");
return;
@@ -256,8 +263,17 @@ static void nps_update_position(lbs_server_s *lbs_server, NpsManagerPositionExt
LOG_NPS(DBG_LOW, "[WPS] time: %d, latitude : %.6lf, longitude : %.6lf", lbs_server->wps.timestamp, lbs_server->wps.latitude, lbs_server->wps.longitude);
#ifdef TIZEN_DEVICE
- if (lbs_server->is_fused_running)
- send_wps_position_to_fused_engine(wps.timestamp, wps.latitude, wps.longitude, wps.hor_accuracy, wps.ver_accuracy);
+ /*
+ * For fused location
+ */
+ if (lbs_server->fused_feature) {
+ if (lbs_server->is_fused_running) {
+ if (lbs_server->fused_test_block == 0)
+ send_wps_position_to_fused_engine(wps.timestamp, wps.latitude, wps.longitude, wps.hor_accuracy);
+ else
+ LOG_NPS(DBG_LOW, "[VIRTUAL] WPS disabled temporary");
+ }
+ }
#endif
lbs_server_emit_position_changed(lbs_server->lbs_dbus_server, LBS_SERVER_METHOD_NPS,
@@ -551,7 +567,7 @@ static void stop_tracking(lbs_server_s *lbs_server, lbs_server_method_e method,
break;
case LBS_SERVER_METHOD_NPS:
- LOG_NPS(DBG_LOW, "_stop_tracking NPS");
+ LOG_NPS(DBG_LOW, "stop tracking NPS [client: %d]", lbs_server->nps_client_count);
client_count_updater(lbs_server, LBS_SERVER_METHOD_NPS, _LBS_CLIENT_REMOVE, fused_mode);
@@ -574,8 +590,9 @@ static void stop_tracking(lbs_server_s *lbs_server, lbs_server_method_e method,
static gboolean _set_current_location_source(lbs_server_s *lbs_server, gint mode)
{
+ LOG_GPS(DBG_LOW, "set current location source [%d]", mode);
if (lbs_server->current_location_source != mode) {
- LOG_GPS(DBG_LOW, "location source changed[%d]", mode);
+ LOG_GPS(DBG_LOW, "location source changed[%d -> %d]", lbs_server->current_location_source, mode);
lbs_server->current_location_source = mode;
return TRUE;
}
@@ -584,7 +601,9 @@ static gboolean _set_current_location_source(lbs_server_s *lbs_server, gint mode
static gboolean __get_fused_interval_control(lbs_server_s *lbs_server)
{
- /* Activate fused combine interval when all clients are fused */
+ /*
+ * Activate fused combine interval when all clients are fused
+ */
gint pure_gps_count = lbs_server->gps_client_count - lbs_server->fused_high_count;
if (pure_gps_count == 0 && lbs_server->fused_balance_count > 0)
return TRUE;
@@ -594,41 +613,86 @@ static gboolean __get_fused_interval_control(lbs_server_s *lbs_server)
static gboolean select_location_source(lbs_server_s *lbs_server)
{
- /*********************************************************************************
- Define all condition to select optimized location source
- gps, high ==> 1(gps0, high0) 2(gps3, high0) 3(gps3, high1) 4(gps3, high3)
- wps, balance ==> A(wps0, bal0) B(wps3, bal0) C(wps3, bal1) D(wps3, bal3)
+ LOG_FUSED_FUNC;
- LOCATION_SOURCE_GPS : 2A, 2D, 3A, 3D, 4A, 4D
- LOCATION_SOURCE_WPS : 1B, 1C, 1D
- LOCATION_SOURCE_BOTH : 2B, 2C, 3B, 2C, 4B, 4C
- LOCATION_SOURCE_NONE : 1A
- *********************************************************************************/
+ /*
+ * For fused location
+ */
+ if (lbs_server->fused_feature) {
+ /**
+ * Define all condition to select optimized location source
+ * gps, high ==> 1(gps0, high0) 2(gps3, high0) 3(gps3, high1) 4(gps3, high3)
+ * wps, balance ==> A(wps0, bal0) B(wps3, bal0) C(wps3, bal1) D(wps3, bal3)
+
+ * LOCATION_SOURCE_GPS : 2A, 2D, 3A, 3D, 4A, 4D
+ * LOCATION_SOURCE_WPS : 1B, 1C, 1D
+ * LOCATION_SOURCE_ALL : 2B, 2C, 3B, 2C, 4B, 4C
+ * LOCATION_SOURCE_NONE : 1A
+ **/
+
+ gint pure_wps_count = lbs_server->nps_client_count - lbs_server->fused_balance_count;
+
+ if (lbs_server->gps_client_count > 0) { /* case 2, 3, 4 */
+
+ if (lbs_server->nps_client_count > 0) {
+ if (pure_wps_count > 0) { /* 2B, 2C, 3B, 3C, 4B, 4C */
+ if (_set_current_location_source(lbs_server, LOCATION_SOURCE_ALL)) return TRUE;
+ } else { /* 2D, 3D, 4D */
+ if (_set_current_location_source(lbs_server, LOCATION_SOURCE_GPS)) return TRUE;
+ }
+ } else { /* 2A, 3A, 4A */
+ if (_set_current_location_source(lbs_server, LOCATION_SOURCE_GPS)) return TRUE;
+ }
- gint pure_wps_count = lbs_server->nps_client_count - lbs_server->fused_balance_count;
+ } else { /* case 1*/
+ if (lbs_server->nps_client_count > 0) { /* 1B, 1C, 1D */
+ if (_set_current_location_source(lbs_server, LOCATION_SOURCE_WPS)) return TRUE;
+ } else { /* 1A */
+ if (_set_current_location_source(lbs_server, LOCATION_SOURCE_NONE)) return TRUE;
+ }
+ }
- if (lbs_server->gps_client_count > 0) { /* case 2, 3, 4 */
+ /*
+ * Fused disabled
+ */
+ } else {
- if (lbs_server->nps_client_count > 0) {
- if (pure_wps_count > 0) { /* 2B, 2C, 3B, 3C, 4B, 4C */
- if (_set_current_location_source(lbs_server, LOCATION_SOURCE_BOTH)) return TRUE;
- } else { /* 2D, 3D, 4D */
+ /* [TRUE] Change location source */
+ if (lbs_server->gps_client_count > 0) {
+ if (lbs_server->nps_client_count > 0) {
+ if (_set_current_location_source(lbs_server, LOCATION_SOURCE_ALL)) return TRUE;
+ } else {
if (_set_current_location_source(lbs_server, LOCATION_SOURCE_GPS)) return TRUE;
}
- } else { /* 2A, 3A, 4A */
- if (_set_current_location_source(lbs_server, LOCATION_SOURCE_GPS)) return TRUE;
- }
-
- } else { /* case 1*/
- if (lbs_server->nps_client_count > 0) { /* 1B, 1C, 1D */
- if (_set_current_location_source(lbs_server, LOCATION_SOURCE_WPS)) return TRUE;
- } else { /* 1A */
- if (_set_current_location_source(lbs_server, LOCATION_SOURCE_NONE)) return TRUE;
+ } else {
+ if (lbs_server->nps_client_count > 0) {
+ if (_set_current_location_source(lbs_server, LOCATION_SOURCE_WPS)) return TRUE;
+ } else {
+ if (_set_current_location_source(lbs_server, LOCATION_SOURCE_NONE)) return TRUE;
+ }
}
}
+
+ LOG_GPS(DBG_LOW, "update location source [FALSE], keep current [gps: %d, wps: %d]",
+ lbs_server->gps_client_count, lbs_server->nps_client_count);
+
+ /* [FALSE] Keep current location source */
return FALSE;
}
+#if (VIRTUAL_BLOCK_AREA)
+static void _block_cb(keynode_t *key, void *user_data)
+{
+ LOG_GPS(DBG_LOW, "[VIRTUAL] VCONFKEY_LOCATION_SUPL_VERSION changed");
+ int value = 0;
+
+ setting_get_int(VCONFKEY_LOCATION_SUPL_VERSION, &value);
+
+ lbs_server_s *lbs_server = (lbs_server_s *)user_data;
+ lbs_server->fused_test_block = value;
+}
+#endif
+
static gboolean location_source_selector(lbs_server_s *lbs_server, gint fused_mode)
{
LOG_FUSED_FUNC;
@@ -637,31 +701,41 @@ static gboolean location_source_selector(lbs_server_s *lbs_server, gint fused_mo
return FALSE;
}
- /* set fused running status */
- if (fused_mode == LOCATION_FUSED_HIGH || fused_mode == LOCATION_FUSED_BALANCED) {
- if (lbs_server->fused_high_count == 0 && lbs_server->fused_balance_count == 0) {
- LOG_GPS(DBG_LOW, "[FUSED] Need to stop fused mode");
- if (lbs_server->is_fused_running) {
- g_mutex_lock(&lbs_server->mutex);
- lbs_server->is_fused_running = FALSE;
- g_mutex_unlock(&lbs_server->mutex);
-
- location_fused_stop();
- }
- } else {
- LOG_GPS(DBG_LOW, "[FUSED] Need to start fused mode");
- if (lbs_server->is_fused_running == FALSE) {
- g_mutex_lock(&lbs_server->mutex);
- lbs_server->is_fused_running = TRUE;
- g_mutex_unlock(&lbs_server->mutex);
+ /*
+ * For fused location
+ */
+ if (lbs_server->fused_feature) {
+ /* set fused running status */
+ if (fused_mode == LOCATION_FUSED_HIGH || fused_mode == LOCATION_FUSED_BALANCED) {
+ if (lbs_server->fused_high_count == 0 && lbs_server->fused_balance_count == 0) {
+ LOG_GPS(DBG_LOW, "[FUSED] Need to stop fused mode");
+ if (lbs_server->is_fused_running) {
+ g_mutex_lock(&lbs_server->mutex);
+ lbs_server->is_fused_running = FALSE;
+ g_mutex_unlock(&lbs_server->mutex);
+
+ location_fused_stop();
+ }
+ } else {
+ LOG_GPS(DBG_LOW, "[FUSED] Need to start fused mode");
+ if (lbs_server->is_fused_running == FALSE) {
+ g_mutex_lock(&lbs_server->mutex);
+ lbs_server->is_fused_running = TRUE;
+ g_mutex_unlock(&lbs_server->mutex);
- location_fused_start();
+ location_fused_start();
+ }
}
}
}
- if (select_location_source(lbs_server))
+ /*
+ * common
+ */
+ if (select_location_source(lbs_server)) {
+ LOG_GPS(DBG_LOW, "select location source [TRUE] --> source changed");
return TRUE;
+ }
return FALSE;
}
@@ -757,8 +831,8 @@ static gboolean plugin_status_controller(lbs_server_s *lbs_server)
if (lbs_server->is_nps_running == FALSE)
ret = nps_plugin_start(lbs_server);
- } else if (lbs_server->current_location_source == LOCATION_SOURCE_BOTH) {
- LOG_FUSED(DBG_LOW, "-->> LOCATION_SOURCE_BOTH");
+ } else if (lbs_server->current_location_source == LOCATION_SOURCE_ALL) {
+ LOG_FUSED(DBG_LOW, "-->> LOCATION_SOURCE_ALL");
if (lbs_server->is_gps_running == FALSE)
ret = gps_plugin_start(lbs_server);
@@ -948,6 +1022,9 @@ static gboolean update_gps_tracking_interval(lbs_server_interval_manipulation_ty
return ret_val;
}
+/*
+ * For fused location
+ */
static gboolean update_fused_tracking_interval(lbs_server_interval_manipulation_type type, const gchar *client, int method, guint interval, guint prev_interval, gpointer userdata)
{
LOG_FUSED(DBG_LOW, "[FUSED] >>> update fused_tracking_interval");
@@ -1078,8 +1155,9 @@ static void request_plugin_interval(lbs_server_s *lbs_server)
request_change_pos_update_interval_standalone_gps(lbs_server->now_gps_interval);
}
}
+
} else {
- LOG_GPS(DBG_LOW, "request plugin[stop] --> [0]");
+ LOG_GPS(DBG_LOW, "request plugin[stop] --> [interval: 0]");
lbs_server->now_gps_interval = 0;
}
}
@@ -1149,8 +1227,13 @@ static void set_options(GVariant *options, const gchar *client, gpointer userdat
LOG_GPS(DBG_LOW, "update gps_tracking_interval -> START");
update_gps_tracking_interval(LBS_SERVER_INTERVAL_ADD, client, method, interval, 0, lbs_server);
- if (fused_mode != LOCATION_FUSED_NONE)
- update_fused_tracking_interval(LBS_SERVER_INTERVAL_ADD, client, method, interval, 0, lbs_server);
+ /*
+ * For fused location
+ */
+ if (lbs_server->fused_feature) {
+ if (fused_mode != LOCATION_FUSED_NONE)
+ update_fused_tracking_interval(LBS_SERVER_INTERVAL_ADD, client, method, interval, 0, lbs_server);
+ }
}
if (app_id) {
@@ -1160,8 +1243,7 @@ static void set_options(GVariant *options, const gchar *client, gpointer userdat
start_tracking(lbs_server, method, fused_mode);
- }
- else if (!g_strcmp0(g_variant_get_string(value, &length), "STOP")) {
+ } else if (!g_strcmp0(g_variant_get_string(value, &length), "STOP")) {
while (g_variant_iter_next(&iter, "{&sv}", &key, &value)) {
if (!g_strcmp0(key, "METHOD"))
method = g_variant_get_int32(value);
@@ -1178,8 +1260,13 @@ static void set_options(GVariant *options, const gchar *client, gpointer userdat
LOG_GPS(DBG_LOW, "update gps_tracking_interval -> STOP");
update_gps_tracking_interval(LBS_SERVER_INTERVAL_REMOVE, client, method, interval, 0, lbs_server);
- if (fused_mode != LOCATION_FUSED_NONE)
- update_fused_tracking_interval(LBS_SERVER_INTERVAL_REMOVE, client, method, interval, 0, lbs_server);
+ /*
+ * For fused location
+ */
+ if (lbs_server->fused_feature) {
+ if (fused_mode != LOCATION_FUSED_NONE)
+ update_fused_tracking_interval(LBS_SERVER_INTERVAL_REMOVE, client, method, interval, 0, lbs_server);
+ }
}
if (app_id) {
@@ -1187,7 +1274,17 @@ static void set_options(GVariant *options, const gchar *client, gpointer userdat
free(app_id);
}
- stop_tracking(lbs_server, method, fused_mode);
+ if (LBS_SERVER_METHOD_GPS == method) {
+ if (lbs_server->gps_client_count > 0)
+ stop_tracking(lbs_server, method, fused_mode);
+ else
+ LOG_GPS(DBG_LOW, "[STOP] stop tracking is not called because [gps: 0]");
+ } else if (LBS_SERVER_METHOD_NPS == method) {
+ if (lbs_server->nps_client_count > 0)
+ stop_tracking(lbs_server, method, fused_mode);
+ else
+ LOG_GPS(DBG_LOW, "[STOP] stop tracking is not called because [wps: 0]");
+ }
}
#ifndef TIZEN_DEVICE
@@ -1284,8 +1381,13 @@ static void set_options(GVariant *options, const gchar *client, gpointer userdat
LOG_GPS(DBG_LOW, "update gps_tracking_interval -> SET:OPT");
update_gps_tracking_interval(LBS_SERVER_INTERVAL_UPDATE, client, method, interval, prev_interval, lbs_server);
- if (fused_mode != LOCATION_FUSED_NONE)
- update_fused_tracking_interval(LBS_SERVER_INTERVAL_UPDATE, client, method, interval, prev_interval, lbs_server);
+ /*
+ * For fused location
+ */
+ if (lbs_server->fused_feature) {
+ if (fused_mode != LOCATION_FUSED_NONE)
+ update_fused_tracking_interval(LBS_SERVER_INTERVAL_UPDATE, client, method, interval, prev_interval, lbs_server);
+ }
if (lbs_server->is_changing_gps_interval) {
lbs_server->is_changing_gps_interval = FALSE;
@@ -1298,7 +1400,7 @@ static void set_options(GVariant *options, const gchar *client, gpointer userdat
static gboolean gps_remove_all_clients(lbs_server_s *lbs_server)
{
- LOG_GPS(DBG_LOW, "remove_all_clients[%d] GPS", lbs_server->gps_client_count);
+ LOG_GPS(DBG_LOW, "remove_all_clients[%d] GPS, [gps client --> 0]", lbs_server->gps_client_count);
if (lbs_server->is_mock_running == LBS_SERVER_METHOD_GPS) {
mock_stop_tracking(lbs_server);
setting_ignore_key_changed(VCONFKEY_LOCATION_MOCK_ENABLED, __setting_mock_cb);
@@ -1335,39 +1437,27 @@ static void shutdown(gpointer userdata, gboolean *shutdown_arr)
nps_plugin_stop(lbs_server);
}
}
-
-#if 0 /* Not needed */
- int enabled = 0;
- setting_get_int(VCONFKEY_LOCATION_NETWORK_ENABLED, &enabled);
- if (enabled == 0) {
- if (lbs_server->loop != NULL)
- g_main_loop_quit(lbs_server->loop);
- } else {
- if (vconf_notify_key_changed(VCONFKEY_LOCATION_NETWORK_ENABLED, _network_enabled_cb, lbs_server))
- LOG_NPS(DBG_ERR, "fail to notify VCONFKEY_LOCATION_NETWORK_ENABLED");
- }
-#endif
}
#ifdef TIZEN_DEVICE
-static void fused_update_position_cb(fl_location *location, gpointer user_data)
+/*
+ * For fused location
+ */
+static void fused_update_position_cb(gint timestamp, gdouble latitude, gdouble longitude, gdouble altitude,
+ gdouble speed, gdouble direction, gdouble climb, gdouble hor_accuracy, gdouble ver_accuracy, gpointer user_data)
{
-// LOG_FUSED_FUNC;
- if (location == NULL || user_data == NULL) {
- LOG_FUSED(LOG_ERROR, "parameter is NULL");
+ if (user_data == NULL) {
+ LOG_FUSED(LOG_ERROR, "user_data parameter is NULL");
return;
}
lbs_server_s *lbs_server = (lbs_server_s *)(user_data);
LbsPositionExtFields fields = (LBS_POSITION_EXT_FIELDS_LATITUDE | LBS_POSITION_EXT_FIELDS_LONGITUDE
| LBS_POSITION_EXT_FIELDS_ALTITUDE | LBS_POSITION_EXT_FIELDS_SPEED | LBS_POSITION_EXT_FIELDS_DIRECTION | LBS_POSITION_EXT_FIELDS_CLIMB);
- GVariant *accuracy = g_variant_new("(idd)", LBS_ACCURACY_LEVEL_DETAILED,
- location->accuracy.horizontal_accuracy, location->accuracy.vertical_accuracy);
+ GVariant *accuracy = g_variant_new("(idd)", LBS_ACCURACY_LEVEL_DETAILED, hor_accuracy, ver_accuracy);
lbs_server_emit_position_changed(lbs_server->lbs_dbus_server, LBS_SERVER_METHOD_FUSED,
- fields, location->pos.timestamp,
- location->pos.latitude, location->pos.longitude, location->pos.altitude,
- location->vel.speed, location->vel.direction, location->vel.climb, accuracy);
+ fields, timestamp, latitude, longitude, altitude, speed, direction, climb, accuracy);
}
#endif
@@ -1397,26 +1487,49 @@ static void gps_update_position_cb(pos_data_t *pos, gps_error_t error, void *use
gps_set_position(pos);
#ifdef TIZEN_DEVICE
- if (lbs_server->is_fused_running)
- send_gps_position_to_fused_engine(pos->timestamp, pos->latitude, pos->longitude, pos->altitude,
- pos->speed, pos->bearing, pos->hor_accuracy, pos->ver_accuracy);
+ /*
+ * For fused location
+ */
+ if (lbs_server->fused_feature) {
+ if (lbs_server->is_fused_running) {
+ if (lbs_server->fused_test_block == 0)
+ send_gps_position_to_fused_engine(pos->timestamp, pos->latitude, pos->longitude, pos->altitude,
+ pos->speed, pos->bearing, pos->hor_accuracy, pos->ver_accuracy);
+ else
+ LOG_GPS(DBG_LOW, "[VIRTUAL] GPS disalbed temporary");
+ }
+ }
#endif
lbs_server_emit_position_changed(lbs_server->lbs_dbus_server, LBS_SERVER_METHOD_GPS, fields, pos->timestamp,
pos->latitude, pos->longitude, pos->altitude, pos->speed, pos->bearing, 0.0, accuracy);
}
-static void gps_update_batch_cb(batch_data_t *batch, void *user_data)
+static void gps_update_batch_cb(batch_data_t *bat, void *user_data)
{
lbs_server_s *lbs_server = (lbs_server_s *)(user_data);
- memcpy(&lbs_server->batch, batch, sizeof(batch_data_t));
+
+ int idx;
+ GVariant *batch_info = NULL;
+ GVariantBuilder *batch_builder = NULL;
+ memcpy(&lbs_server->batch, bat, sizeof(batch_data_t));
if (lbs_server->status != LBS_STATUS_AVAILABLE) {
lbs_server_emit_status_changed(lbs_server->lbs_dbus_server, LBS_SERVER_METHOD_GPS, LBS_STATUS_AVAILABLE);
lbs_server->status = LBS_STATUS_AVAILABLE;
}
- lbs_server_emit_batch_changed(lbs_server->lbs_dbus_server, batch->num_of_location);
+ batch_builder = g_variant_builder_new(G_VARIANT_TYPE("a(iddddddd)"));
+ for (idx = 0; idx < bat->num_of_location; idx++) {
+ g_variant_builder_add(batch_builder, "(iddddddd)", bat->data[idx].timestamp,
+ bat->data[idx].latitude, bat->data[idx].longitude, bat->data[idx].altitude,
+ bat->data[idx].speed, bat->data[idx].bearing,
+ bat->data[idx].hor_accuracy, bat->data[idx].ver_accuracy);
+ }
+
+ batch_info = g_variant_builder_end(batch_builder);
+
+ lbs_server_emit_batch_changed(lbs_server->lbs_dbus_server, bat->num_of_location, batch_info);
}
static void gps_update_satellite_cb(sv_data_t *sv, void *user_data)
@@ -1537,6 +1650,13 @@ static void lbs_server_init(lbs_server_s *lbs_server)
lbs_server->fused_balance_count = 0;
lbs_server->current_location_source = LOCATION_SOURCE_NONE;
+ lbs_server->fused_feature = FALSE;
+ const char *FUSED_FEATURE = "http://tizen.org/feature/location.fused";
+ bool is_fused_supported = false;
+ system_info_get_platform_bool(FUSED_FEATURE, &is_fused_supported);
+ if (is_fused_supported)
+ lbs_server->fused_feature = TRUE;
+
nps_init(lbs_server);
/* create resource for dynamic-interval */
@@ -1557,8 +1677,17 @@ static void lbs_server_init(lbs_server_s *lbs_server)
#endif
#ifdef TIZEN_DEVICE
- lbs_server->fused_interval_table = g_hash_table_new_full(g_str_hash, g_str_equal, g_free, NULL);
- location_fused_init(fused_update_position_cb, lbs_server);
+ /*
+ * For fused location
+ */
+ if (lbs_server->fused_feature) {
+ lbs_server->fused_interval_table = g_hash_table_new_full(g_str_hash, g_str_equal, g_free, NULL);
+ location_fused_init(fused_update_position_cb, lbs_server);
+ #if (VIRTUAL_BLOCK_AREA)
+ LOG_GPS(DBG_LOW, "[VIRTUAL] temporary vconf added");
+ vconf_notify_key_changed(VCONFKEY_LOCATION_SUPL_VERSION, _block_cb, lbs_server);
+ #endif
+ }
#endif
}
@@ -1608,7 +1737,7 @@ static void nps_get_last_position(lbs_server_s *lbs_server)
if (++index == MAX_NPS_LOC_ITEM) break;
last_location[index] = (char *)strtok_r(NULL, ";", &last);
}
- LOG_NPS(DBG_LOW, "[%d] %lf, %lf", lbs_server->last_pos.timestamp, lbs_server->last_pos.latitude, lbs_server->last_pos.longitude);
+ LOG_NPS(DBG_LOW, "[WPS get_last: %d] %lf, %lf", lbs_server->last_pos.timestamp, lbs_server->last_pos.latitude, lbs_server->last_pos.longitude);
}
static void nps_init(lbs_server_s *lbs_server)
@@ -1769,8 +1898,18 @@ int main(int argc, char **argv)
LOG_GPS(DBG_LOW, "lbs_server deamon Stop....");
#ifdef TIZEN_DEVICE
- location_fused_deinit();
- g_hash_table_destroy(lbs_server->fused_interval_table);
+ /*
+ * For fused location
+ */
+ if (lbs_server->fused_feature) {
+ location_fused_deinit();
+
+ #if (VIRTUAL_BLOCK_AREA)
+ LOG_GPS(DBG_LOW, "[VIRTUAL] temporary vconf removed");
+ vconf_ignore_key_changed(VCONFKEY_LOCATION_SUPL_VERSION, _block_cb);
+ #endif
+ g_hash_table_destroy(lbs_server->fused_interval_table);
+ }
#endif
gps_deinit_log();
@@ -1955,24 +2094,28 @@ static void client_count_updater(lbs_server_s *lbs_server, lbs_server_method_e m
switch (method) {
case LBS_SERVER_METHOD_GPS: {
g_mutex_lock(&lbs_server->mutex);
- if (type == _LBS_CLIENT_ADD)
+ if (type == _LBS_CLIENT_ADD) {
lbs_server->gps_client_count++;
- else if (type == _LBS_CLIENT_REMOVE)
- lbs_server->gps_client_count--;
- else if (type == _LBS_CLIENT_REMOVE_ALL)
+ } else if (type == _LBS_CLIENT_REMOVE) {
+ if (lbs_server->gps_client_count > 0)
+ lbs_server->gps_client_count--;
+ } else if (type == _LBS_CLIENT_REMOVE_ALL) {
lbs_server->gps_client_count = 0;
+ }
g_mutex_unlock(&lbs_server->mutex);
break;
}
case LBS_SERVER_METHOD_NPS: {
g_mutex_lock(&lbs_server->mutex);
- if (type == _LBS_CLIENT_ADD)
+ if (type == _LBS_CLIENT_ADD) {
lbs_server->nps_client_count++;
- else if (type == _LBS_CLIENT_REMOVE)
- lbs_server->nps_client_count--;
- else if (type == _LBS_CLIENT_REMOVE_ALL)
+ } else if (type == _LBS_CLIENT_REMOVE) {
+ if (lbs_server->nps_client_count > 0)
+ lbs_server->nps_client_count--;
+ } else if (type == _LBS_CLIENT_REMOVE_ALL) {
lbs_server->nps_client_count = 0;
+ }
g_mutex_unlock(&lbs_server->mutex);
break;
@@ -1983,30 +2126,39 @@ static void client_count_updater(lbs_server_s *lbs_server, lbs_server_method_e m
}
}
- if (type == _LBS_CLIENT_ADD) {
- if (fused_mode == LOCATION_FUSED_HIGH)
- lbs_server->fused_high_count++;
- else if (fused_mode == LOCATION_FUSED_BALANCED)
- lbs_server->fused_balance_count++;
- } else if (type == _LBS_CLIENT_REMOVE) {
- if (fused_mode == LOCATION_FUSED_HIGH)
- lbs_server->fused_high_count--;
- else if (fused_mode == LOCATION_FUSED_BALANCED)
- lbs_server->fused_balance_count--;
- } else if (type == _LBS_CLIENT_REMOVE_ALL) {
- lbs_server->fused_high_count = 0;
- lbs_server->fused_balance_count = 0;
- }
+ /*
+ * For fused location
+ */
+ if (lbs_server->fused_feature) {
+
+ if (type == _LBS_CLIENT_ADD) {
+ if (fused_mode == LOCATION_FUSED_HIGH)
+ lbs_server->fused_high_count++;
+ else if (fused_mode == LOCATION_FUSED_BALANCED)
+ lbs_server->fused_balance_count++;
+ } else if (type == _LBS_CLIENT_REMOVE) {
+ if (fused_mode == LOCATION_FUSED_HIGH)
+ lbs_server->fused_high_count--;
+ else if (fused_mode == LOCATION_FUSED_BALANCED)
+ lbs_server->fused_balance_count--;
+ } else if (type == _LBS_CLIENT_REMOVE_ALL) {
+ lbs_server->fused_high_count = 0;
+ lbs_server->fused_balance_count = 0;
+ }
- if (lbs_server->fused_high_count < 0) {
- LOG_GPS(DBG_ERR, "fused_high count is negative value");
- lbs_server->fused_high_count = 0;
- }
+ if (lbs_server->fused_high_count < 0) {
+ LOG_GPS(DBG_ERR, "fused_high count is negative value");
+ lbs_server->fused_high_count = 0;
+ }
- if (lbs_server->fused_balance_count < 0) {
- LOG_GPS(DBG_ERR, "fused_balance count is negative value");
- lbs_server->fused_balance_count = 0;
+ if (lbs_server->fused_balance_count < 0) {
+ LOG_GPS(DBG_ERR, "fused_balance count is negative value");
+ lbs_server->fused_balance_count = 0;
+ }
}
+
+ LOG_GPS(DBG_LOW, ">>> client count updater [method: %d, type: %d, gps: %d, wps: %d]",
+ method, type, lbs_server->gps_client_count, lbs_server->nps_client_count);
}
diff --git a/module/gps_module.c b/module/gps_module.c
index 499d560..fed1691 100644
--- a/module/gps_module.c
+++ b/module/gps_module.c
@@ -182,15 +182,39 @@ static void position_callback(GVariant *param, void *user_data)
static void batch_callback(GVariant *param, void *user_data)
{
- MOD_LOGD("batch_callback");
GpsManagerData *module = (GpsManagerData *)user_data;
g_return_if_fail(module);
g_return_if_fail(module->batch_cb);
- int num_of_location = 0;
- g_variant_get(param, "(i)", &num_of_location);
+ guint idx;
+ int num_of_location = 0, timestamp = 0;
+ double lat = 0.0, lon = 0.0, alt = 0.0, speed = 0.0, direction = 0.0, horizontal = 0.0, vertical = 0.0;
+
+ LocationBatch *batch = NULL;
+ GVariant *batch_data = NULL;
+ GVariantIter *batch_iter = NULL;
+
+ g_variant_get(param, "(i@a(iddddddd))", &num_of_location, &batch_data);
+ g_variant_get(batch_data, "a(iddddddd)", &batch_iter);
+ MOD_LOGD("batch_callback - batch size [%d]", num_of_location);
+
+ batch = location_batch_new(num_of_location);
+ batch->num_of_location = num_of_location;
+
+ GVariant *tmp_var = NULL;
+ for (idx = 0; idx < num_of_location; idx++) {
+ tmp_var = g_variant_iter_next_value(batch_iter);
+ g_variant_get(tmp_var, "(iddddddd)", &timestamp, &lat, &lon, &alt, &speed, &direction, &horizontal, &vertical);
+
+ MOD_LOGD("idx[%d] %d, %lf, %lf, %lf ", idx, timestamp, lat, lon, alt);
+ location_set_batch_details(batch, idx, lat, lon, alt, speed, direction, horizontal, vertical, timestamp);
+ g_variant_unref(tmp_var);
+ }
- module->batch_cb(TRUE, num_of_location, module->userdata);
+ module->batch_cb(TRUE, num_of_location, batch, module->userdata);
+ location_batch_free(batch);
+ g_variant_iter_free(batch_iter);
+ g_variant_unref(batch_data);
}
static void on_signal_batch_callback(const gchar *sig, GVariant *param, gpointer user_data)
diff --git a/packaging/lbs-server.spec b/packaging/lbs-server.spec
index 31853ba..efea4a1 100644
--- a/packaging/lbs-server.spec
+++ b/packaging/lbs-server.spec
@@ -1,6 +1,6 @@
Name: lbs-server
Summary: LBS Server for Tizen
-Version: 1.2.6
+Version: 1.3.0
Release: 1
Group: Location/Service
License: Apache-2.0
@@ -18,6 +18,7 @@ BuildRequires: pkgconfig(lbs-location)
BuildRequires: pkgconfig(lbs-dbus)
BuildRequires: pkgconfig(gio-unix-2.0)
BuildRequires: pkgconfig(capi-network-wifi)
+BuildRequires: pkgconfig(capi-system-info)
BuildRequires: pkgconfig(gio-2.0)
BuildRequires: pkgconfig(vconf-internal-keys)
BuildRequires: pkgconfig(gthread-2.0)