/// -*- tab-width: 4; Mode: C++; c-basic-offset: 4; indent-tabs-mode: nil -*-
/*
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see .
*/
/*
support for external modules
*/
#include
#include
#if defined(HAVE_LIBDL)
#include
#endif
#include
#include
struct AP_Module::hook_list *AP_Module::hooks[NUM_HOOKS];
const char *AP_Module::hook_names[AP_Module::NUM_HOOKS] = {
"ap_hook_setup_start",
"ap_hook_setup_complete",
"ap_hook_AHRS_update",
"ap_hook_gyro_sample",
"ap_hook_accel_sample",
};
/*
scan a module for hook symbols
*/
void AP_Module::module_scan(const char *path)
{
#if defined(HAVE_LIBDL)
void *m = dlopen(path, RTLD_NOW);
if (m == nullptr) {
printf("dlopen(%s) -> %s\n", path, dlerror());
return;
}
bool found_hook = false;
for (uint16_t i=0; inext = hooks[i];
h->symbol = s;
hooks[i] = h;
found_hook = true;
}
}
if (!found_hook) {
// we don't need this module
dlclose(m);
}
#endif
}
/*
initialise AP_Module, looking for shared libraries in the given module path
*/
void AP_Module::init(const char *module_path)
{
// scan through module directory looking for *.so
DIR *d;
struct dirent *de;
d = opendir(module_path);
if (d == nullptr) {
return;
}
while ((de = readdir(d))) {
const char *extension = strrchr(de->d_name, '.');
if (extension == nullptr || strcmp(extension, ".so") != 0) {
continue;
}
char *path = nullptr;
if (asprintf(&path, "%s/%s", module_path, de->d_name) == -1) {
continue;
}
module_scan(path);
free(path);
}
closedir(d);
}
/*
call any setup_start hooks
*/
void AP_Module::call_hook_setup_start(void)
{
#if AP_MODULE_SUPPORTED
uint64_t now = AP_HAL::micros64();
for (const struct hook_list *h=hooks[HOOK_SETUP_START]; h; h=h->next) {
ap_hook_setup_start_fn_t fn = reinterpret_cast(h->symbol);
fn(now);
}
#endif
}
/*
call any setup_complete hooks
*/
void AP_Module::call_hook_setup_complete(void)
{
#if AP_MODULE_SUPPORTED
uint64_t now = AP_HAL::micros64();
for (const struct hook_list *h=hooks[HOOK_SETUP_COMPLETE]; h; h=h->next) {
ap_hook_setup_complete_fn_t fn = reinterpret_cast(h->symbol);
fn(now);
}
#endif
}
/*
call any AHRS_update hooks
*/
void AP_Module::call_hook_AHRS_update(const AP_AHRS_NavEKF &ahrs)
{
#if AP_MODULE_SUPPORTED
if (hooks[HOOK_AHRS_UPDATE] == nullptr) {
// avoid filling in AHRS_state
return;
}
/*
construct AHRS_state structure
*/
struct AHRS_state state {};
state.structure_version = AHRS_state_version;
state.time_us = AP_HAL::micros64();
if (!ahrs.initialised()) {
state.status = AHRS_STATUS_INITIALISING;
} else if (ahrs.healthy()) {
state.status = AHRS_STATUS_HEALTHY;
} else {
state.status = AHRS_STATUS_UNHEALTHY;
}
Quaternion q;
q.from_rotation_matrix(ahrs.get_rotation_body_to_ned());
state.quat[0] = q[0];
state.quat[1] = q[1];
state.quat[2] = q[2];
state.quat[3] = q[3];
state.eulers[0] = ahrs.roll;
state.eulers[1] = ahrs.pitch;
state.eulers[2] = ahrs.yaw;
Location loc;
if (ahrs.get_origin(loc)) {
state.origin.initialised = true;
state.origin.latitude = loc.lat;
state.origin.longitude = loc.lng;
state.origin.altitude = loc.alt*0.01f;
}
if (ahrs.get_position(loc)) {
state.position.available = true;
state.position.latitude = loc.lat;
state.position.longitude = loc.lng;
state.position.altitude = loc.alt*0.01f;
}
Vector3f pos;
if (ahrs.get_relative_position_NED(pos)) {
state.relative_position[0] = pos[0];
state.relative_position[1] = pos[1];
state.relative_position[2] = pos[2];
}
const Vector3f &gyro = ahrs.get_gyro();
state.gyro_rate[0] = gyro[0];
state.gyro_rate[1] = gyro[1];
state.gyro_rate[2] = gyro[2];
const Vector3f &accel_ef = ahrs.get_accel_ef();
state.accel_ef[0] = accel_ef[0];
state.accel_ef[1] = accel_ef[0];
state.accel_ef[2] = accel_ef[0];
state.primary_accel = ahrs.get_primary_accel_index();
state.primary_gyro = ahrs.get_primary_gyro_index();
const Vector3f &gyro_bias = ahrs.get_gyro_drift();
state.gyro_bias[0] = gyro_bias[0];
state.gyro_bias[1] = gyro_bias[1];
state.gyro_bias[2] = gyro_bias[2];
Vector3f vel;
if (ahrs.get_velocity_NED(vel)) {
state.velocity_ned[0] = vel.x;
state.velocity_ned[1] = vel.y;
state.velocity_ned[2] = vel.z;
}
for (const struct hook_list *h=hooks[HOOK_AHRS_UPDATE]; h; h=h->next) {
ap_hook_AHRS_update_fn_t fn = reinterpret_cast(h->symbol);
fn(&state);
}
#endif
}
/*
call any gyro_sample hooks
*/
void AP_Module::call_hook_gyro_sample(uint8_t instance, float dt, const Vector3f &gyro)
{
#if AP_MODULE_SUPPORTED
if (hooks[HOOK_GYRO_SAMPLE] == nullptr) {
// avoid filling in struct
return;
}
/*
construct gyro_sample structure
*/
struct gyro_sample state {};
state.structure_version = gyro_sample_version;
state.time_us = AP_HAL::micros64();
state.instance = instance;
state.delta_time = dt;
state.gyro[0] = gyro[0];
state.gyro[1] = gyro[1];
state.gyro[2] = gyro[2];
for (const struct hook_list *h=hooks[HOOK_GYRO_SAMPLE]; h; h=h->next) {
ap_hook_gyro_sample_fn_t fn = reinterpret_cast(h->symbol);
fn(&state);
}
#endif
}
/*
call any accel_sample hooks
*/
void AP_Module::call_hook_accel_sample(uint8_t instance, float dt, const Vector3f &accel, bool fsync_set)
{
#if AP_MODULE_SUPPORTED
if (hooks[HOOK_ACCEL_SAMPLE] == nullptr) {
// avoid filling in struct
return;
}
/*
construct accel_sample structure
*/
struct accel_sample state {};
state.structure_version = accel_sample_version;
state.time_us = AP_HAL::micros64();
state.instance = instance;
state.delta_time = dt;
state.accel[0] = accel[0];
state.accel[1] = accel[1];
state.accel[2] = accel[2];
state.fsync_set = fsync_set;
for (const struct hook_list *h=hooks[HOOK_ACCEL_SAMPLE]; h; h=h->next) {
ap_hook_accel_sample_fn_t fn = reinterpret_cast(h->symbol);
fn(&state);
}
#endif
}