|
|
|
@ -13,21 +13,16 @@
@@ -13,21 +13,16 @@
|
|
|
|
|
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
|
|
|
|
*/ |
|
|
|
|
|
|
|
|
|
#include <AP_WindVane/AP_WindVane.h> |
|
|
|
|
#include <AP_BoardConfig/AP_BoardConfig.h> |
|
|
|
|
#include <RC_Channel/RC_Channel.h> |
|
|
|
|
#include <AP_AHRS/AP_AHRS.h> |
|
|
|
|
#include <GCS_MAVLink/GCS.h> |
|
|
|
|
#include <Filter/Filter.h> |
|
|
|
|
#include <utility> |
|
|
|
|
#if CONFIG_HAL_BOARD == HAL_BOARD_SITL |
|
|
|
|
#include <SITL/SITL.h> |
|
|
|
|
#endif |
|
|
|
|
#include "AP_WindVane.h" |
|
|
|
|
|
|
|
|
|
extern const AP_HAL::HAL& hal; |
|
|
|
|
#include "AP_WindVane_Home.h" |
|
|
|
|
#include "AP_WindVane_Analog.h" |
|
|
|
|
#include "AP_WindVane_ModernDevice.h" |
|
|
|
|
#include "AP_WindVane_Airspeed.h" |
|
|
|
|
#include "AP_WindVane_RPM.h" |
|
|
|
|
#include "AP_WindVane_SITL.h" |
|
|
|
|
|
|
|
|
|
#define WINDVANE_DEFAULT_PIN 15 // default wind vane sensor analog pin
|
|
|
|
|
#define WINDVANE_CALIBRATION_VOLT_DIFF_MIN 1.0f // calibration routine's min voltage difference required for success
|
|
|
|
|
#define WINDSPEED_DEFAULT_SPEED_PIN 14 // default pin for reading speed from ModernDevice rev p wind sensor
|
|
|
|
|
#define WINDSPEED_DEFAULT_TEMP_PIN 13 // default pin for reading temperature from ModernDevice rev p wind sensor
|
|
|
|
|
#define WINDSPEED_DEFAULT_VOLT_OFFSET 1.346 // default voltage offset between speed and temp pins from ModernDevice rev p wind sensor
|
|
|
|
@ -39,7 +34,8 @@ const AP_Param::GroupInfo AP_WindVane::var_info[] = {
@@ -39,7 +34,8 @@ const AP_Param::GroupInfo AP_WindVane::var_info[] = {
|
|
|
|
|
// @Description: Wind Vane type
|
|
|
|
|
// @Values: 0:None,1:Heading when armed,2:RC input offset heading when armed,3:Analog
|
|
|
|
|
// @User: Standard
|
|
|
|
|
AP_GROUPINFO_FLAGS("TYPE", 1, AP_WindVane, _type, 0, AP_PARAM_FLAG_ENABLE), |
|
|
|
|
// @RebootRequired: True
|
|
|
|
|
AP_GROUPINFO_FLAGS("TYPE", 1, AP_WindVane, _direction_type, 0, AP_PARAM_FLAG_ENABLE), |
|
|
|
|
|
|
|
|
|
// @Param: RC_IN_NO
|
|
|
|
|
// @DisplayName: Wind vane sensor RC Input Channel
|
|
|
|
@ -92,8 +88,8 @@ const AP_Param::GroupInfo AP_WindVane::var_info[] = {
@@ -92,8 +88,8 @@ const AP_Param::GroupInfo AP_WindVane::var_info[] = {
|
|
|
|
|
|
|
|
|
|
// @Param: CAL
|
|
|
|
|
// @DisplayName: Wind vane calibration start
|
|
|
|
|
// @Description: Start wind vane calibration by setting this to 1
|
|
|
|
|
// @Values: 0:None, 1:Calibrate
|
|
|
|
|
// @Description: Start wind vane calibration by setting this to 1 or 2
|
|
|
|
|
// @Values: 0:None, 1:Calibrate direction, 2:Calibrate speed
|
|
|
|
|
// @User: Standard
|
|
|
|
|
AP_GROUPINFO("CAL", 8, AP_WindVane, _calibration, 0), |
|
|
|
|
|
|
|
|
@ -120,6 +116,7 @@ const AP_Param::GroupInfo AP_WindVane::var_info[] = {
@@ -120,6 +116,7 @@ const AP_Param::GroupInfo AP_WindVane::var_info[] = {
|
|
|
|
|
// @Description: Wind speed sensor type
|
|
|
|
|
// @Values: 0:None,1:Airspeed library,2:Modern Devices Wind Sensor,3:RPM library,10:SITL
|
|
|
|
|
// @User: Standard
|
|
|
|
|
// @RebootRequired: True
|
|
|
|
|
AP_GROUPINFO("SPEED_TYPE", 11, AP_WindVane, _speed_sensor_type, 0), |
|
|
|
|
|
|
|
|
|
// @Param: SPEED_PIN
|
|
|
|
@ -178,63 +175,126 @@ AP_WindVane *AP_WindVane::get_singleton()
@@ -178,63 +175,126 @@ AP_WindVane *AP_WindVane::get_singleton()
|
|
|
|
|
// return true if wind vane is enabled
|
|
|
|
|
bool AP_WindVane::enabled() const |
|
|
|
|
{ |
|
|
|
|
return (_type != WINDVANE_NONE); |
|
|
|
|
return _direction_type != WINDVANE_NONE; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
// Initialize the Wind Vane object and prepare it for use
|
|
|
|
|
void AP_WindVane::init() |
|
|
|
|
{ |
|
|
|
|
// a pin for reading the Wind Vane voltage
|
|
|
|
|
dir_analog_source = hal.analogin->channel(_dir_analog_pin); |
|
|
|
|
// don't init twice
|
|
|
|
|
if (_direction_driver != nullptr || _speed_driver != nullptr ) { |
|
|
|
|
return; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
// pins for ModernDevice rev p wind sensor
|
|
|
|
|
speed_analog_source = hal.analogin->channel(ANALOG_INPUT_NONE); |
|
|
|
|
speed_temp_analog_source = hal.analogin->channel(ANALOG_INPUT_NONE); |
|
|
|
|
// wind direction
|
|
|
|
|
switch (_direction_type) { |
|
|
|
|
case WindVaneType::WINDVANE_NONE: |
|
|
|
|
// WindVane disabled
|
|
|
|
|
return; |
|
|
|
|
case WindVaneType::WINDVANE_HOME_HEADING: |
|
|
|
|
case WindVaneType::WINDVANE_PWM_PIN: |
|
|
|
|
_direction_driver = new AP_WindVane_Home(*this); |
|
|
|
|
break; |
|
|
|
|
case WindVaneType::WINDVANE_ANALOG_PIN: |
|
|
|
|
_direction_driver = new AP_WindVane_Analog(*this); |
|
|
|
|
break; |
|
|
|
|
case WindVaneType::WINDVANE_SITL: |
|
|
|
|
#if CONFIG_HAL_BOARD == HAL_BOARD_SITL |
|
|
|
|
_direction_driver = new AP_WindVane_SITL(*this); |
|
|
|
|
#endif |
|
|
|
|
break; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
// check that airspeed is enabled if it is selected as sensor type, if not revert to no wind speed sensor
|
|
|
|
|
AP_Airspeed* airspeed = AP_Airspeed::get_singleton(); |
|
|
|
|
if (_speed_sensor_type == Speed_type::WINDSPEED_AIRSPEED && (airspeed == nullptr || !airspeed->enabled())) { |
|
|
|
|
_speed_sensor_type.set(Speed_type::WINDSPEED_NONE); |
|
|
|
|
// wind speed
|
|
|
|
|
switch (_speed_sensor_type) { |
|
|
|
|
case Speed_type::WINDSPEED_NONE: |
|
|
|
|
break; |
|
|
|
|
case Speed_type::WINDSPEED_AIRSPEED: |
|
|
|
|
_speed_driver = new AP_WindVane_Airspeed(*this); |
|
|
|
|
break; |
|
|
|
|
case Speed_type::WINDVANE_WIND_SENSOR_REV_P: |
|
|
|
|
_speed_driver = new AP_WindVane_ModernDevice(*this); |
|
|
|
|
break; |
|
|
|
|
case Speed_type::WINDSPEED_SITL: |
|
|
|
|
#if CONFIG_HAL_BOARD == HAL_BOARD_SITL |
|
|
|
|
// single driver does both speed and direction
|
|
|
|
|
if (_direction_type != WINDVANE_SITL) { |
|
|
|
|
_speed_driver = new AP_WindVane_SITL(*this); |
|
|
|
|
} else { |
|
|
|
|
_speed_driver = _direction_driver; |
|
|
|
|
} |
|
|
|
|
#endif |
|
|
|
|
break; |
|
|
|
|
case Speed_type::WINDSPEED_RPM: |
|
|
|
|
_speed_driver = new AP_WindVane_RPM(*this); |
|
|
|
|
break; |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
// update wind vane, expected to be called at 20hz
|
|
|
|
|
void AP_WindVane::update() |
|
|
|
|
{ |
|
|
|
|
bool have_speed = _speed_driver != nullptr; |
|
|
|
|
bool have_direciton = _direction_driver != nullptr; |
|
|
|
|
|
|
|
|
|
// exit immediately if not enabled
|
|
|
|
|
if (!enabled()) { |
|
|
|
|
if (!enabled() || (!have_speed && !have_direciton)) { |
|
|
|
|
return; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
// check for calibration
|
|
|
|
|
calibrate(); |
|
|
|
|
// calibrate if booted and disarmed
|
|
|
|
|
if (!hal.util->get_soft_armed()) { |
|
|
|
|
if (_calibration == 1 && have_direciton) { |
|
|
|
|
_direction_driver->calibrate(); |
|
|
|
|
} else if (_calibration == 2 && have_speed) { |
|
|
|
|
_speed_driver->calibrate(); |
|
|
|
|
} else if (_calibration != 0) { |
|
|
|
|
gcs().send_text(MAV_SEVERITY_INFO, "WindVane: driver not found"); |
|
|
|
|
_calibration.set_and_save(0); |
|
|
|
|
} |
|
|
|
|
} else if (_calibration != 0) { |
|
|
|
|
gcs().send_text(MAV_SEVERITY_INFO, "WindVane: disarm for cal"); |
|
|
|
|
_calibration.set_and_save(0); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
// read apparent wind speed
|
|
|
|
|
update_apparent_wind_speed(); |
|
|
|
|
if (have_speed) { |
|
|
|
|
_speed_driver->update_speed(); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
// read apparent wind direction (relies on wind speed above)
|
|
|
|
|
update_apparent_wind_direction(); |
|
|
|
|
// read apparent wind direction
|
|
|
|
|
if (_speed_apparent >= _dir_speed_cutoff && have_direciton) { |
|
|
|
|
// only update if enough wind to move vane
|
|
|
|
|
_direction_driver->update_direction(); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
// calculate true wind speed and direction from apparent wind
|
|
|
|
|
update_true_wind_speed_and_direction(); |
|
|
|
|
if (have_speed && have_direciton) { |
|
|
|
|
update_true_wind_speed_and_direction(); |
|
|
|
|
} else { |
|
|
|
|
// no wind speed sensor, so can't do true wind calcs
|
|
|
|
|
_direction_absolute = _direction_apparent_ef; |
|
|
|
|
_speed_true = 0.0f; |
|
|
|
|
return; |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
// get the apparent wind direction in radians, 0 = head to wind
|
|
|
|
|
float AP_WindVane::get_apparent_wind_direction_rad() const |
|
|
|
|
{ |
|
|
|
|
return wrap_PI(_direction_apparent_ef - AP::ahrs().yaw); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
// record home heading for use as wind direction if no sensor is fitted
|
|
|
|
|
void AP_WindVane::record_home_heading() |
|
|
|
|
// to start direction calibration from mavlink or other
|
|
|
|
|
bool AP_WindVane::start_direction_calibration() |
|
|
|
|
{ |
|
|
|
|
_home_heading = AP::ahrs().yaw; |
|
|
|
|
if (enabled() && (_calibration == 0)) { |
|
|
|
|
_calibration = 1; |
|
|
|
|
return true; |
|
|
|
|
} |
|
|
|
|
return false; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
bool AP_WindVane::start_calibration() |
|
|
|
|
// to start speed calibration from mavlink or other
|
|
|
|
|
bool AP_WindVane::start_speed_calibration() |
|
|
|
|
{ |
|
|
|
|
if (enabled() && (_calibration == 0)) { |
|
|
|
|
_calibration = 1; |
|
|
|
|
_calibration = 2; |
|
|
|
|
return true; |
|
|
|
|
} |
|
|
|
|
return false; |
|
|
|
@ -256,204 +316,10 @@ void AP_WindVane::send_wind(mavlink_channel_t chan)
@@ -256,204 +316,10 @@ void AP_WindVane::send_wind(mavlink_channel_t chan)
|
|
|
|
|
0); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
// read an analog port and calculate the wind direction in earth-frame in radians
|
|
|
|
|
// assumes voltage increases as wind vane moves clockwise
|
|
|
|
|
float AP_WindVane::read_analog_direction_ef() |
|
|
|
|
{ |
|
|
|
|
dir_analog_source->set_pin(_dir_analog_pin); |
|
|
|
|
_current_analog_voltage = dir_analog_source->voltage_average_ratiometric(); |
|
|
|
|
|
|
|
|
|
const float voltage_ratio = linear_interpolate(0.0f, 1.0f, _current_analog_voltage, _dir_analog_volt_min, _dir_analog_volt_max); |
|
|
|
|
const float direction = (voltage_ratio * radians(360 - _dir_analog_deadzone)) + radians(_dir_analog_bearing_offset); |
|
|
|
|
|
|
|
|
|
return wrap_PI(direction + AP::ahrs().yaw); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
// read rc input of apparent wind direction in earth-frame in radians
|
|
|
|
|
float AP_WindVane::read_PWM_direction_ef() |
|
|
|
|
{ |
|
|
|
|
RC_Channel *chan = rc().channel(_rc_in_no-1); |
|
|
|
|
if (chan == nullptr) { |
|
|
|
|
return 0.0f; |
|
|
|
|
} |
|
|
|
|
float direction = chan->norm_input() * radians(45); |
|
|
|
|
|
|
|
|
|
return wrap_PI(direction + _home_heading); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
#if CONFIG_HAL_BOARD == HAL_BOARD_SITL |
|
|
|
|
// read SITL's apparent wind direction in earth-frame in radians
|
|
|
|
|
float AP_WindVane::read_SITL_direction_ef() |
|
|
|
|
{ |
|
|
|
|
// temporarily store true speed and direction for easy access
|
|
|
|
|
const float wind_speed = AP::sitl()->wind_speed_active; |
|
|
|
|
const float wind_dir_rad = radians(AP::sitl()->wind_direction_active); |
|
|
|
|
|
|
|
|
|
// Note than the SITL wind direction is defined as the direction the wind is traveling to
|
|
|
|
|
// This is accounted for in these calculations
|
|
|
|
|
|
|
|
|
|
// convert true wind speed and direction into a 2D vector
|
|
|
|
|
Vector2f wind_vector_ef(cosf(wind_dir_rad) * wind_speed, sinf(wind_dir_rad) * wind_speed); |
|
|
|
|
|
|
|
|
|
// add vehicle speed to get apparent wind vector
|
|
|
|
|
wind_vector_ef.x += AP::sitl()->state.speedN; |
|
|
|
|
wind_vector_ef.y += AP::sitl()->state.speedE; |
|
|
|
|
|
|
|
|
|
return atan2f(wind_vector_ef.y, wind_vector_ef.x); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
// read the apparent wind speed in m/s from SITL
|
|
|
|
|
float AP_WindVane::read_wind_speed_SITL() |
|
|
|
|
{ |
|
|
|
|
// temporarily store true speed and direction for easy access
|
|
|
|
|
const float wind_speed = AP::sitl()->wind_speed_active; |
|
|
|
|
const float wind_dir_rad = radians(AP::sitl()->wind_direction_active); |
|
|
|
|
|
|
|
|
|
// convert true wind speed and direction into a 2D vector
|
|
|
|
|
Vector2f wind_vector_ef(cosf(wind_dir_rad) * wind_speed, sinf(wind_dir_rad) * wind_speed); |
|
|
|
|
|
|
|
|
|
// add vehicle speed to get apparent wind vector
|
|
|
|
|
wind_vector_ef.x += AP::sitl()->state.speedN; |
|
|
|
|
wind_vector_ef.y += AP::sitl()->state.speedE; |
|
|
|
|
|
|
|
|
|
return wind_vector_ef.length(); |
|
|
|
|
} |
|
|
|
|
#endif |
|
|
|
|
|
|
|
|
|
// read wind speed from Modern Device rev p wind sensor
|
|
|
|
|
// https://moderndevice.com/news/calibrating-rev-p-wind-sensor-new-regression/
|
|
|
|
|
float AP_WindVane::read_wind_speed_ModernDevice() |
|
|
|
|
{ |
|
|
|
|
float analog_voltage = 0.0f; |
|
|
|
|
|
|
|
|
|
// only read temp pin if defined, sensor will do OK assuming constant temp
|
|
|
|
|
float temp_ambient = 28.0f; // equations were generated at this temp in above data sheet
|
|
|
|
|
if (is_positive(_speed_sensor_temp_pin)) { |
|
|
|
|
speed_temp_analog_source->set_pin(_speed_sensor_temp_pin); |
|
|
|
|
analog_voltage = speed_temp_analog_source->voltage_average(); |
|
|
|
|
temp_ambient = (analog_voltage - 0.4f) / 0.0195f; // deg C
|
|
|
|
|
// constrain to reasonable range to avoid deviating from calibration too much and potential divide by zero
|
|
|
|
|
temp_ambient = constrain_float(temp_ambient, 10.0f, 40.0f); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
speed_analog_source->set_pin(_speed_sensor_speed_pin); |
|
|
|
|
analog_voltage = speed_analog_source->voltage_average(); |
|
|
|
|
|
|
|
|
|
// apply voltage offset and make sure not negative
|
|
|
|
|
// by default the voltage offset is the number provide by the manufacturer
|
|
|
|
|
analog_voltage = analog_voltage - _speed_sensor_voltage_offset; |
|
|
|
|
if (is_negative(analog_voltage)) { |
|
|
|
|
analog_voltage = 0.0f; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
// simplified equation from data sheet, converted from mph to m/s
|
|
|
|
|
return 24.254896f * powf((analog_voltage / powf(temp_ambient, 0.115157f)), 3.009364f); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
// update the apparent wind speed
|
|
|
|
|
void AP_WindVane::update_apparent_wind_speed() |
|
|
|
|
{ |
|
|
|
|
float apparent_speed_in = 0.0f; |
|
|
|
|
|
|
|
|
|
switch (_speed_sensor_type) { |
|
|
|
|
case WINDSPEED_NONE: |
|
|
|
|
_speed_apparent = 0.0f; |
|
|
|
|
break; |
|
|
|
|
case WINDSPEED_AIRSPEED: { |
|
|
|
|
const AP_Airspeed* airspeed = AP_Airspeed::get_singleton(); |
|
|
|
|
if (airspeed != nullptr) { |
|
|
|
|
apparent_speed_in = airspeed->get_airspeed(); |
|
|
|
|
} |
|
|
|
|
break; |
|
|
|
|
} |
|
|
|
|
case WINDVANE_WIND_SENSOR_REV_P: |
|
|
|
|
apparent_speed_in = read_wind_speed_ModernDevice(); |
|
|
|
|
break; |
|
|
|
|
case WINDSPEED_SITL: |
|
|
|
|
#if CONFIG_HAL_BOARD == HAL_BOARD_SITL |
|
|
|
|
apparent_speed_in = read_wind_speed_SITL(); |
|
|
|
|
#endif |
|
|
|
|
break; |
|
|
|
|
case WINDSPEED_RPM: { |
|
|
|
|
const AP_RPM* rpm = AP_RPM::get_singleton(); |
|
|
|
|
if (rpm != nullptr) { |
|
|
|
|
const float temp_speed = rpm->get_rpm(0); |
|
|
|
|
if (!is_negative(temp_speed)) { |
|
|
|
|
apparent_speed_in = temp_speed; |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
break; |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
// apply low pass filter if enabled
|
|
|
|
|
if (is_positive(_speed_filt_hz)) { |
|
|
|
|
_speed_filt.set_cutoff_frequency(_speed_filt_hz); |
|
|
|
|
_speed_apparent = _speed_filt.apply(apparent_speed_in, 0.02f); |
|
|
|
|
} else { |
|
|
|
|
_speed_apparent = apparent_speed_in; |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
// calculate the apparent wind direction in radians, the wind comes from this direction, 0 = head to wind
|
|
|
|
|
// expected to be called at 20hz after apparent wind speed has been updated
|
|
|
|
|
void AP_WindVane::update_apparent_wind_direction() |
|
|
|
|
{ |
|
|
|
|
float apparent_angle_ef = 0.0f; |
|
|
|
|
|
|
|
|
|
switch (_type) { |
|
|
|
|
case WindVaneType::WINDVANE_HOME_HEADING: |
|
|
|
|
// this is a approximation as we are not considering boat speed and wind speed
|
|
|
|
|
// do not filter home heading
|
|
|
|
|
_direction_apparent_ef = _home_heading; |
|
|
|
|
return; |
|
|
|
|
case WindVaneType::WINDVANE_PWM_PIN: |
|
|
|
|
// this is a approximation as we are not considering boat speed and wind speed
|
|
|
|
|
// do not filter pwm input from pilot
|
|
|
|
|
_direction_apparent_ef = read_PWM_direction_ef(); |
|
|
|
|
return; |
|
|
|
|
case WindVaneType::WINDVANE_ANALOG_PIN: |
|
|
|
|
apparent_angle_ef = read_analog_direction_ef(); |
|
|
|
|
break; |
|
|
|
|
case WindVaneType::WINDVANE_SITL: |
|
|
|
|
#if CONFIG_HAL_BOARD == HAL_BOARD_SITL |
|
|
|
|
apparent_angle_ef = read_SITL_direction_ef(); |
|
|
|
|
#endif |
|
|
|
|
break; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
// if not enough wind to move vane do not update the value
|
|
|
|
|
if (_speed_apparent < _dir_speed_cutoff){ |
|
|
|
|
return; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
// apply low pass filter if enabled
|
|
|
|
|
if (is_positive(_dir_filt_hz)) { |
|
|
|
|
_dir_sin_filt.set_cutoff_frequency(_dir_filt_hz); |
|
|
|
|
_dir_cos_filt.set_cutoff_frequency(_dir_filt_hz); |
|
|
|
|
// https://en.wikipedia.org/wiki/Mean_of_circular_quantities
|
|
|
|
|
const float filtered_sin = _dir_sin_filt.apply(sinf(apparent_angle_ef), 0.05f); |
|
|
|
|
const float filtered_cos = _dir_cos_filt.apply(cosf(apparent_angle_ef), 0.05f); |
|
|
|
|
_direction_apparent_ef = atan2f(filtered_sin, filtered_cos); |
|
|
|
|
} else { |
|
|
|
|
_direction_apparent_ef = apparent_angle_ef; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
// make sure between -pi and pi
|
|
|
|
|
_direction_apparent_ef = wrap_PI(_direction_apparent_ef); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
// calculate true wind speed and direction from apparent wind
|
|
|
|
|
// https://en.wikipedia.org/wiki/Apparent_wind
|
|
|
|
|
void AP_WindVane::update_true_wind_speed_and_direction() |
|
|
|
|
{ |
|
|
|
|
if (_speed_sensor_type == Speed_type::WINDSPEED_NONE) { |
|
|
|
|
// no wind speed sensor, so can't do true wind calcs
|
|
|
|
|
_direction_absolute = _direction_apparent_ef; |
|
|
|
|
_speed_true = 0.0f; |
|
|
|
|
return; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
// if no vehicle speed, can't do calcs
|
|
|
|
|
Vector3f veh_velocity; |
|
|
|
|
if (!AP::ahrs().get_velocity_NED(veh_velocity)) { |
|
|
|
@ -475,61 +341,6 @@ void AP_WindVane::update_true_wind_speed_and_direction()
@@ -475,61 +341,6 @@ void AP_WindVane::update_true_wind_speed_and_direction()
|
|
|
|
|
_speed_true = wind_true_vec.length(); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
// calibrate windvane
|
|
|
|
|
void AP_WindVane::calibrate() |
|
|
|
|
{ |
|
|
|
|
// exit immediately if armed or too soon after start
|
|
|
|
|
if (hal.util->get_soft_armed()) { |
|
|
|
|
return; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
// return if not calibrating
|
|
|
|
|
if (_calibration == 0) { |
|
|
|
|
return; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
switch (_type) { |
|
|
|
|
case WindVaneType::WINDVANE_HOME_HEADING: |
|
|
|
|
case WindVaneType::WINDVANE_PWM_PIN: |
|
|
|
|
gcs().send_text(MAV_SEVERITY_INFO, "WindVane: No cal required"); |
|
|
|
|
_calibration.set_and_save(0); |
|
|
|
|
break; |
|
|
|
|
case WindVaneType::WINDVANE_ANALOG_PIN: |
|
|
|
|
// start calibration
|
|
|
|
|
if (_cal_start_ms == 0) { |
|
|
|
|
_cal_start_ms = AP_HAL::millis(); |
|
|
|
|
_cal_volt_max = _current_analog_voltage; |
|
|
|
|
_cal_volt_min = _current_analog_voltage; |
|
|
|
|
gcs().send_text(MAV_SEVERITY_INFO, "WindVane: Calibration started, rotate wind vane"); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
// record min and max voltage
|
|
|
|
|
_cal_volt_max = MAX(_cal_volt_max, _current_analog_voltage); |
|
|
|
|
_cal_volt_min = MIN(_cal_volt_min, _current_analog_voltage); |
|
|
|
|
|
|
|
|
|
// calibrate for 30 seconds
|
|
|
|
|
if ((AP_HAL::millis() - _cal_start_ms) > 30000) { |
|
|
|
|
// check for required voltage difference
|
|
|
|
|
const float volt_diff = _cal_volt_max - _cal_volt_min; |
|
|
|
|
if (volt_diff >= WINDVANE_CALIBRATION_VOLT_DIFF_MIN) { |
|
|
|
|
// save min and max voltage
|
|
|
|
|
_dir_analog_volt_max.set_and_save(_cal_volt_max); |
|
|
|
|
_dir_analog_volt_min.set_and_save(_cal_volt_min); |
|
|
|
|
_calibration.set_and_save(0); |
|
|
|
|
gcs().send_text(MAV_SEVERITY_INFO, "WindVane: Calibration complete (volt min:%.1f max:%1.f)", |
|
|
|
|
(double)_cal_volt_min, |
|
|
|
|
(double)_cal_volt_max); |
|
|
|
|
} else { |
|
|
|
|
gcs().send_text(MAV_SEVERITY_INFO, "WindVane: Calibration failed (volt diff %.1f below %.1f)", |
|
|
|
|
(double)volt_diff, |
|
|
|
|
(double)WINDVANE_CALIBRATION_VOLT_DIFF_MIN); |
|
|
|
|
} |
|
|
|
|
_cal_start_ms = 0; |
|
|
|
|
} |
|
|
|
|
break; |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
AP_WindVane *AP_WindVane::_singleton = nullptr; |
|
|
|
|
|
|
|
|
|
namespace AP { |
|
|
|
|