2023-09-19 06:51:08 -03:00
# include "AP_Tuning_config.h"
# if AP_TUNING_ENABLED
2016-05-06 07:31:38 -03:00
# include "AP_Tuning.h"
2022-02-28 21:19:20 -04:00
2019-06-26 23:32:50 -03:00
# include <AP_Logger/AP_Logger.h>
2016-05-06 07:31:38 -03:00
# include <GCS_MAVLink/GCS.h>
2018-04-03 23:19:16 -03:00
# include <RC_Channel/RC_Channel.h>
2022-02-28 21:19:20 -04:00
# include <AP_Notify/AP_Notify.h>
2016-05-06 07:31:38 -03:00
extern const AP_HAL : : HAL & hal ;
const AP_Param : : GroupInfo AP_Tuning : : var_info [ ] = {
// @Param: CHAN
// @DisplayName: Transmitter tuning channel
// @Description: This sets the channel for transmitter tuning. This should be connected to a knob or slider on your transmitter. It needs to be setup to use the PWM range given by TUNE_CHAN_MIN to TUNE_CHAN_MAX
2016-05-08 08:06:56 -03:00
// @Values: 0:Disable,5:Chan5,6:Chan6,7:Chan7,8:Chan8,9:Chan9,10:Chan10,11:Chan11,12:Chan12,13:Chan13,14:Chan14,15:Chan15,16:Chan16
2016-05-06 07:31:38 -03:00
// @User: Standard
2016-05-08 02:56:53 -03:00
AP_GROUPINFO ( " CHAN " , 1 , AP_Tuning , channel , 0 ) ,
2016-05-06 07:31:38 -03:00
// @Param: CHAN_MIN
// @DisplayName: Transmitter tuning channel minimum pwm
// @Description: This sets the PWM lower limit for the tuning channel
// @Range: 900 2100
// @User: Standard
2016-05-08 02:56:53 -03:00
AP_GROUPINFO ( " CHAN_MIN " , 2 , AP_Tuning , channel_min , 1000 ) ,
2016-05-06 07:31:38 -03:00
// @Param: CHAN_MAX
// @DisplayName: Transmitter tuning channel maximum pwm
// @Description: This sets the PWM upper limit for the tuning channel
// @Range: 900 2100
// @User: Standard
2016-05-08 02:56:53 -03:00
AP_GROUPINFO ( " CHAN_MAX " , 3 , AP_Tuning , channel_max , 2000 ) ,
// @Param: SELECTOR
// @DisplayName: Transmitter tuning selector channel
// @Description: This sets the channel for the transmitter tuning selector switch. This should be a 2 position switch, preferably spring loaded. A PWM above 1700 means high, below 1300 means low. If no selector is set then you won't be able to switch between parameters during flight or re-center the tuning knob
// @Values: 0:Disable,1:Chan1,2:Chan3,3:Chan3,4:Chan4,5:Chan5,6:Chan6,7:Chan7,8:Chan8,9:Chan9,10:Chan10,11:Chan11,12:Chan12,13:Chan13,14:Chan14,15:Chan15,16:Chan16
// @User: Standard
AP_GROUPINFO ( " SELECTOR " , 4 , AP_Tuning , selector , 0 ) ,
2016-05-06 07:31:38 -03:00
2016-05-08 02:56:53 -03:00
// @Param: RANGE
// @DisplayName: Transmitter tuning range
// @Description: This sets the range over which tuning will change a parameter. A value of 2 means the tuning parameter will go from 0.5 times the start value to 2x the start value over the range of the tuning channel
// @User: Standard
AP_GROUPINFO ( " RANGE " , 5 , AP_Tuning , range , 2.0f ) ,
2016-05-26 23:51:03 -03:00
// @Param: MODE_REVERT
// @DisplayName: Revert on mode change
// @Description: This controls whether tuning values will revert on a flight mode change.
// @Values: 0:Disable,1:Enable
// @User: Standard
AP_GROUPINFO ( " MODE_REVERT " , 6 , AP_Tuning , mode_revert , 1 ) ,
2016-05-27 01:13:29 -03:00
// @Param: ERR_THRESH
// @DisplayName: Controller error threshold
2021-09-08 09:57:47 -03:00
// @Description: This sets the controller error threshold above which an alarm will sound and a message will be sent to the GCS to warn of controller instability while tuning. The error is the rms value of the P+D corrections in the loop. High values in hover indicate possible instability due to too high PID gains or excessively high D to P gain ratios.-1 will disable this message.
2016-05-27 01:13:29 -03:00
// @Range: 0 1
// @User: Standard
AP_GROUPINFO ( " ERR_THRESH " , 7 , AP_Tuning , error_threshold , 0.15f ) ,
2016-05-26 23:51:03 -03:00
2016-05-06 07:31:38 -03:00
AP_GROUPEND
} ;
/*
handle selector switch input
*/
void AP_Tuning : : check_selector_switch ( void )
{
2016-05-08 00:47:48 -03:00
if ( selector = = 0 ) {
// no selector switch enabled
return ;
}
2019-06-21 01:15:33 -03:00
if ( ! rc ( ) . has_valid_input ( ) ) {
selector_start_ms = 0 ;
return ;
}
2018-04-26 09:00:18 -03:00
RC_Channel * selchan = rc ( ) . channel ( selector - 1 ) ;
2016-05-06 07:31:38 -03:00
if ( selchan = = nullptr ) {
return ;
}
AP_Tuning: Fix up after RC_Channels refactor
Further to refactor of RC_Channel class which included
adding get_xx set_xx methods, change reads and writes to the public members
to calls to get and set functionsss
old public member(int16_t) get function -> int16_t set function (int16_t)
(expression where c is an object of type RC_Channel)
c.radio_in c.get_radio_in() c.set_radio_in(v)
c.control_in c.get_control_in() c.set_control_in(v)
c.servo_out c.get_servo_out() c.set_servo_out(v)
c.pwm_out c.get_pwm_out() // use existing
c.radio_out c.get_radio_out() c.set_radio_out(v)
c.radio_max c.get_radio_max() c.set_radio_max(v)
c.radio_min c.get_radio_min() c.set_radio_min(v)
c.radio_trim c.get_radio_trim() c.set_radio_trim(v);
c.min_max_configured() // return true if min and max are configured
Because data members of RC_Channels are now private and so cannot be written directly
some overloads are provided in the Plane classes to provide the old functionality
new overload Plane::stick_mix_channel(RC_Channel *channel)
which forwards to the previously existing
void stick_mix_channel(RC_Channel *channel, int16_t &servo_out);
new overload Plane::channel_output_mixer(Rc_Channel* , RC_Channel*)const
which forwards to
(uint8_t mixing_type, int16_t & chan1, int16_t & chan2)const;
Rename functions
RC_Channel_aux::set_radio_trim(Aux_servo_function_t function)
to RC_Channel_aux::set_trim_to_radio_in_for(Aux_servo_function_t function)
RC_Channel_aux::set_servo_out(Aux_servo_function_t function, int16_t value)
to RC_Channel_aux::set_servo_out_for(Aux_servo_function_t function, int16_t value)
Rationale:
RC_Channel is a complicated class, which combines
several functionalities dealing with stick inputs
in pwm and logical units, logical and actual actuator
outputs, unit conversion etc, etc
The intent of this PR is to clarify existing use of
the class. At the basic level it should now be possible
to grep all places where private variable is set by
searching for the set_xx function.
(The wider purpose is to provide a more generic and
logically simpler method of output mixing. This is a small step)
2016-05-08 06:46:34 -03:00
uint16_t selector_in = selchan - > get_radio_in ( ) ;
2021-02-03 13:21:39 -04:00
if ( selector_in > = RC_Channel : : AUX_PWM_TRIGGER_HIGH ) {
2016-05-06 07:31:38 -03:00
// high selector
if ( selector_start_ms = = 0 ) {
selector_start_ms = AP_HAL : : millis ( ) ;
}
uint32_t hold_time = AP_HAL : : millis ( ) - selector_start_ms ;
if ( hold_time > 5000 & & changed ) {
// save tune
save_parameters ( ) ;
re_center ( ) ;
2023-09-02 02:21:36 -03:00
GCS_SEND_TEXT ( MAV_SEVERITY_INFO , " Tuning: Saved " ) ;
2016-05-06 07:31:38 -03:00
AP_Notify : : events . tune_save = 1 ;
changed = false ;
need_revert = 0 ;
}
2021-02-03 13:21:39 -04:00
} else if ( selector_in < = RC_Channel : : AUX_PWM_TRIGGER_LOW ) {
2016-05-06 07:31:38 -03:00
// low selector
if ( selector_start_ms ! = 0 ) {
uint32_t hold_time = AP_HAL : : millis ( ) - selector_start_ms ;
2019-06-17 07:46:00 -03:00
if ( hold_time < 200 ) {
// debounce!
} else if ( hold_time < 2000 ) {
2016-05-06 07:31:38 -03:00
// re-center the value
re_center ( ) ;
2023-09-02 02:21:36 -03:00
GCS_SEND_TEXT ( MAV_SEVERITY_INFO , " Tuning: recentered %s " , get_tuning_name ( current_parm ) ) ;
2016-05-06 07:31:38 -03:00
} else if ( hold_time < 5000 ) {
// change parameter
next_parameter ( ) ;
}
2019-06-17 07:46:00 -03:00
selector_start_ms = 0 ;
2016-05-06 07:31:38 -03:00
}
}
}
/*
re - center the tuning value
*/
void AP_Tuning : : re_center ( void )
{
AP_Float * f = get_param_pointer ( current_parm ) ;
if ( f ! = nullptr ) {
center_value = f - > get ( ) ;
}
mid_point_wait = true ;
}
/*
check for changed tuning input
*/
void AP_Tuning : : check_input ( uint8_t flightmode )
{
2016-05-08 00:47:48 -03:00
if ( channel < = 0 | | parmset < = 0 ) {
2016-05-06 07:31:38 -03:00
// disabled
return ;
}
// check for revert on changed flightmode
if ( flightmode ! = last_flightmode ) {
2016-05-26 23:51:03 -03:00
if ( need_revert ! = 0 & & mode_revert ! = 0 ) {
2023-09-02 02:21:36 -03:00
GCS_SEND_TEXT ( MAV_SEVERITY_INFO , " Tuning: reverted " ) ;
2016-05-06 07:31:38 -03:00
revert_parameters ( ) ;
re_center ( ) ;
}
last_flightmode = flightmode ;
}
// only adjust values at 10Hz
uint32_t now = AP_HAL : : millis ( ) ;
uint32_t dt_ms = now - last_check_ms ;
if ( dt_ms < 100 ) {
return ;
}
last_check_ms = now ;
2018-04-03 23:19:16 -03:00
if ( channel > RC_Channels : : get_valid_channel_count ( ) ) {
2016-05-08 00:47:48 -03:00
// not valid channel
2016-05-06 07:31:38 -03:00
return ;
}
// check for invalid range
if ( range < 1.1f ) {
range . set ( 1.1f ) ;
}
2016-05-08 01:35:40 -03:00
if ( current_parm = = 0 ) {
next_parameter ( ) ;
}
// cope with user changing parmset while tuning
if ( current_set ! = parmset ) {
re_center ( ) ;
}
current_set = parmset ;
2016-05-06 07:31:38 -03:00
check_selector_switch ( ) ;
if ( selector_start_ms ) {
// no tuning while selector high
return ;
}
if ( current_parm = = 0 ) {
return ;
}
2018-04-26 09:00:18 -03:00
RC_Channel * chan = rc ( ) . channel ( channel - 1 ) ;
2016-05-06 07:31:38 -03:00
if ( chan = = nullptr ) {
return ;
}
AP_Tuning: Fix up after RC_Channels refactor
Further to refactor of RC_Channel class which included
adding get_xx set_xx methods, change reads and writes to the public members
to calls to get and set functionsss
old public member(int16_t) get function -> int16_t set function (int16_t)
(expression where c is an object of type RC_Channel)
c.radio_in c.get_radio_in() c.set_radio_in(v)
c.control_in c.get_control_in() c.set_control_in(v)
c.servo_out c.get_servo_out() c.set_servo_out(v)
c.pwm_out c.get_pwm_out() // use existing
c.radio_out c.get_radio_out() c.set_radio_out(v)
c.radio_max c.get_radio_max() c.set_radio_max(v)
c.radio_min c.get_radio_min() c.set_radio_min(v)
c.radio_trim c.get_radio_trim() c.set_radio_trim(v);
c.min_max_configured() // return true if min and max are configured
Because data members of RC_Channels are now private and so cannot be written directly
some overloads are provided in the Plane classes to provide the old functionality
new overload Plane::stick_mix_channel(RC_Channel *channel)
which forwards to the previously existing
void stick_mix_channel(RC_Channel *channel, int16_t &servo_out);
new overload Plane::channel_output_mixer(Rc_Channel* , RC_Channel*)const
which forwards to
(uint8_t mixing_type, int16_t & chan1, int16_t & chan2)const;
Rename functions
RC_Channel_aux::set_radio_trim(Aux_servo_function_t function)
to RC_Channel_aux::set_trim_to_radio_in_for(Aux_servo_function_t function)
RC_Channel_aux::set_servo_out(Aux_servo_function_t function, int16_t value)
to RC_Channel_aux::set_servo_out_for(Aux_servo_function_t function, int16_t value)
Rationale:
RC_Channel is a complicated class, which combines
several functionalities dealing with stick inputs
in pwm and logical units, logical and actual actuator
outputs, unit conversion etc, etc
The intent of this PR is to clarify existing use of
the class. At the basic level it should now be possible
to grep all places where private variable is set by
searching for the set_xx function.
(The wider purpose is to provide a more generic and
logically simpler method of output mixing. This is a small step)
2016-05-08 06:46:34 -03:00
float chan_value = linear_interpolate ( - 1 , 1 , chan - > get_radio_in ( ) , channel_min , channel_max ) ;
2016-05-06 07:31:38 -03:00
if ( dt_ms > 500 ) {
last_channel_value = chan_value ;
}
if ( fabsf ( chan_value - last_channel_value ) < 0.01 ) {
// ignore changes of less than 1%
return ;
}
//hal.console->printf("chan_value %.2f last_channel_value %.2f\n", chan_value, last_channel_value);
if ( mid_point_wait ) {
2016-05-08 05:32:33 -03:00
// see if we have crossed the mid-point. We use a small deadzone to make it easier
// to move to the "indent" portion of a slider to start tuning
const float dead_zone = 0.02 ;
if ( ( chan_value > dead_zone & & last_channel_value > 0 ) | |
( chan_value < - dead_zone & & last_channel_value < 0 ) ) {
2016-05-06 07:31:38 -03:00
// still waiting
return ;
}
// starting tuning
mid_point_wait = false ;
2023-09-02 02:21:36 -03:00
GCS_SEND_TEXT ( MAV_SEVERITY_INFO , " Tuning: mid-point %s " , get_tuning_name ( current_parm ) ) ;
2016-05-06 07:31:38 -03:00
AP_Notify : : events . tune_started = 1 ;
}
last_channel_value = chan_value ;
float new_value ;
if ( chan_value > 0 ) {
new_value = linear_interpolate ( center_value , range * center_value , chan_value , 0 , 1 ) ;
} else {
new_value = linear_interpolate ( center_value / range , center_value , chan_value , - 1 , 0 ) ;
}
changed = true ;
need_revert | = ( 1U < < current_parm_index ) ;
set_value ( current_parm , new_value ) ;
Log_Write_Parameter_Tuning ( new_value ) ;
}
/*
log a tuning change
*/
void AP_Tuning : : Log_Write_Parameter_Tuning ( float value )
{
2020-04-12 04:28:26 -03:00
// @LoggerMessage: PRTN
// @Description: Plane Parameter Tuning data
// @Field: TimeUS: Time since system startup
// @Field: Set: Parameter set being tuned
// @Field: Parm: Parameter being tuned
// @Field: Value: Current parameter value
// @Field: CenterValue: Center value (startpoint of current modifications) of parameter being tuned
2020-03-20 23:45:14 -03:00
AP : : logger ( ) . Write ( " PRTN " , " TimeUS,Set,Parm,Value,CenterValue " , " QBBff " ,
2016-05-06 07:31:38 -03:00
AP_HAL : : micros64 ( ) ,
parmset ,
current_parm ,
2016-05-17 20:32:10 -03:00
( double ) value ,
( double ) center_value ) ;
2016-05-06 07:31:38 -03:00
}
/*
save parameters in the set
*/
void AP_Tuning : : save_parameters ( void )
{
uint8_t set = ( uint8_t ) parmset . get ( ) ;
2016-05-08 01:17:07 -03:00
if ( set < set_base ) {
// single parameter tuning
save_value ( set ) ;
return ;
}
// multiple parameter tuning
2016-05-06 07:31:38 -03:00
for ( uint8_t i = 0 ; tuning_sets [ i ] . num_parms ! = 0 ; i + + ) {
2016-05-08 01:17:07 -03:00
if ( tuning_sets [ i ] . set + set_base = = set ) {
2016-05-06 07:31:38 -03:00
for ( uint8_t p = 0 ; p < tuning_sets [ i ] . num_parms ; p + + ) {
save_value ( tuning_sets [ i ] . parms [ p ] ) ;
}
break ;
}
}
}
/*
save parameters in the set
*/
void AP_Tuning : : revert_parameters ( void )
{
uint8_t set = ( uint8_t ) parmset . get ( ) ;
2016-05-08 01:17:07 -03:00
if ( set < set_base ) {
// single parameter tuning
reload_value ( set ) ;
return ;
}
2016-05-06 07:31:38 -03:00
for ( uint8_t i = 0 ; tuning_sets [ i ] . num_parms ! = 0 ; i + + ) {
2016-05-08 01:17:07 -03:00
if ( tuning_sets [ i ] . set + set_base = = set ) {
2016-05-06 07:31:38 -03:00
for ( uint8_t p = 0 ; p < tuning_sets [ i ] . num_parms ; p + + ) {
if ( p > = 32 | | ( need_revert & ( 1U < < p ) ) ) {
reload_value ( tuning_sets [ i ] . parms [ p ] ) ;
}
}
need_revert = 0 ;
break ;
}
}
}
/*
switch to the next parameter in the set
*/
void AP_Tuning : : next_parameter ( void )
{
uint8_t set = ( uint8_t ) parmset . get ( ) ;
2016-05-08 01:17:07 -03:00
if ( set < set_base ) {
// nothing to do but re-center
current_parm = set ;
re_center ( ) ;
return ;
}
2016-05-06 07:31:38 -03:00
for ( uint8_t i = 0 ; tuning_sets [ i ] . num_parms ! = 0 ; i + + ) {
2016-05-08 01:17:07 -03:00
if ( tuning_sets [ i ] . set + set_base = = set ) {
2016-05-06 07:31:38 -03:00
if ( current_parm = = 0 ) {
current_parm_index = 0 ;
} else {
current_parm_index = ( current_parm_index + 1 ) % tuning_sets [ i ] . num_parms ;
}
current_parm = tuning_sets [ i ] . parms [ current_parm_index ] ;
re_center ( ) ;
2023-09-02 02:21:36 -03:00
GCS_SEND_TEXT ( MAV_SEVERITY_INFO , " Tuning: started %s " , get_tuning_name ( current_parm ) ) ;
2016-05-09 04:24:56 -03:00
AP_Notify : : events . tune_next = current_parm_index + 1 ;
2016-05-06 07:31:38 -03:00
break ;
}
}
}
/*
return a string representing a tuning parameter
*/
const char * AP_Tuning : : get_tuning_name ( uint8_t parm )
{
for ( uint8_t i = 0 ; tuning_names [ i ] . name ! = nullptr ; i + + ) {
if ( parm = = tuning_names [ i ] . parm ) {
return tuning_names [ i ] . name ;
}
}
return " UNKNOWN " ;
}
2023-09-19 06:51:08 -03:00
# endif // AP_TUNING_ENABLED