mirror of
https://github.com/ArduPilot/ardupilot
synced 2025-01-04 23:18:28 -04:00
7d36bc4422
add dynamic gyro windows control inclusion based on HAL_WITH_DSP and GYROFFT_ENABLED target appropriate ARM cpus constrain window sizes to be achievable improve FFT signal accuracy through configurable window overlap and quinn's estimator calculate energy weighted center frequency add support for learning hover frequency and throttle reference calculate power spectrum rather than amplitude record noise as a per-bin power spectrum calculate true SNR per-bin and use that to determine there is a signal add user config for SNR signal level constrain frequency scanning to MAXHZ calculate and learn the peak bandwidth at the configured attenuation allow enabling/disabling dynamically through rc function MAXHZ should be below Nyquist Incorporate full range of MAXHZ to MINHZ update FFT analysis in a separate thread allocate memory in a specific region constrain window size by CPU class do not allocate any resources when not enabled Prevent self-check and analysis interfering with each other put configuration and initialization to AP_Vehicle add logging fix significant issues with threading, locking and sample buffer access use both calculated noise peaks together with the harmonic notch configuration to detemine which peak represents the fundamental harmonic that should be tracked record harmonic fit add CMSIS 5 libraries and headers allow larger FFT lengths on Linux and SITL
246 lines
9.2 KiB
C++
246 lines
9.2 KiB
C++
/*
|
|
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 <http://www.gnu.org/licenses/>.
|
|
|
|
Code by Andy Piper
|
|
*/
|
|
#pragma once
|
|
|
|
#include <AP_HAL/AP_HAL.h>
|
|
#include <AP_Vehicle/AP_Vehicle_Type.h>
|
|
|
|
#ifndef HAL_GYROFFT_ENABLED
|
|
#define HAL_GYROFFT_ENABLED HAL_WITH_DSP
|
|
#endif
|
|
|
|
#if HAL_GYROFFT_ENABLED
|
|
|
|
#include <AP_Common/AP_Common.h>
|
|
#include <AP_HAL/AP_HAL.h>
|
|
#include <AP_Param/AP_Param.h>
|
|
#include <AP_Math/AP_Math.h>
|
|
#include <AP_InertialSensor/AP_InertialSensor.h>
|
|
#include <Filter/LowPassFilter.h>
|
|
|
|
#define DEBUG_FFT 0
|
|
|
|
// a library that leverages the HAL DSP support to perform FFT analysis on gyro samples
|
|
class AP_GyroFFT
|
|
{
|
|
public:
|
|
AP_GyroFFT();
|
|
|
|
// Do not allow copies
|
|
AP_GyroFFT(const AP_GyroFFT &other) = delete;
|
|
AP_GyroFFT &operator=(const AP_GyroFFT&) = delete;
|
|
|
|
void init(uint32_t target_looptime);
|
|
|
|
// cycle through the FFT steps - runs at 400Hz
|
|
uint16_t update();
|
|
// capture gyro values at the appropriate update rate - runs at fast loop rate
|
|
void sample_gyros();
|
|
// update calculated values of dynamic parameters - runs at 1Hz
|
|
void update_parameters();
|
|
// thread for processing gyro data via FFT
|
|
void update_thread();
|
|
// start the update thread
|
|
void start_update_thread();
|
|
|
|
// check at startup that standard frequencies can be detected
|
|
bool calibration_check();
|
|
// called when hovering to determine the average peak frequency and reference value
|
|
void update_freq_hover(float dt, float throttle_out);
|
|
// called to save the average peak frequency and reference value
|
|
void save_params_on_disarm();
|
|
// dynamically enable or disable the analysis through the aux switch
|
|
void set_analysis_enabled(bool enabled) { _analysis_enabled = enabled; };
|
|
|
|
// detected peak frequency filtered at 1/3 the update rate
|
|
Vector3f get_noise_center_freq_hz() const { return _global_state._center_freq_hz_filtered; }
|
|
// energy of the background noise at the detected center frequency
|
|
Vector3f get_noise_signal_to_noise_db() const { return _global_state._center_snr; }
|
|
// detected peak frequency weighted by energy
|
|
float get_weighted_noise_center_freq_hz();
|
|
// detected peak frequency
|
|
Vector3f get_raw_noise_center_freq_hz() const { return _global_state._center_freq_hz; }
|
|
// match between first and second harmonics
|
|
Vector3f get_raw_noise_harmonic_fit() const { return _global_state._harmonic_fit; }
|
|
// energy of the detected peak frequency
|
|
Vector3f get_center_freq_energy() const { return _global_state._center_freq_energy; }
|
|
// index of the FFT bin containing the detected peak frequency
|
|
Vector3<uint16_t> get_center_freq_bin() const { return _global_state._center_freq_bin; }
|
|
// detected peak bandwidth
|
|
Vector3f get_noise_center_bandwidth_hz() const { return _global_state._center_bandwidth_hz; };
|
|
// weighted detected peak bandwidth
|
|
float get_weighted_noise_center_bandwidth_hz();
|
|
// log gyro fft messages
|
|
void write_log_messages();
|
|
|
|
static const struct AP_Param::GroupInfo var_info[];
|
|
static AP_GyroFFT *get_singleton() { return _singleton; }
|
|
|
|
private:
|
|
// calculate the peak noise frequency
|
|
void calculate_noise(uint16_t max_bin);
|
|
// update the estimation of the background noise energy
|
|
void update_ref_energy(uint16_t max_bin);
|
|
// test frequency detection for all of the allowable bins
|
|
float self_test_bin_frequencies();
|
|
// detect the provided frequency
|
|
float self_test(float frequency, GyroWindow test_window);
|
|
// whether to run analysis or not
|
|
bool analysis_enabled() const { return _initialized && _analysis_enabled && _thread_created; };
|
|
// whether analysis can be run again or not
|
|
bool start_analysis();
|
|
// the size of the ring buffer in both the IMU backend
|
|
uint16_t get_buffer_size() const { return _state->_window_size + INS_MAX_GYRO_WINDOW_SAMPLES; }
|
|
// semaphore for access to shared FFT data
|
|
HAL_Semaphore _sem;
|
|
|
|
// data accessible from the main thread protected by the semaphore
|
|
struct EngineState {
|
|
// energy of the detected peak frequency
|
|
Vector3f _center_freq_energy;
|
|
// energy of the detected peak frequency in dB
|
|
Vector3f _center_freq_energy_db;
|
|
// detected peak frequency
|
|
Vector3f _center_freq_hz;
|
|
// fit between first and second harmonics
|
|
Vector3f _harmonic_fit;
|
|
// bin of detected poeak frequency
|
|
Vector3<uint16_t> _center_freq_bin;
|
|
// filtered version of the peak frequency
|
|
Vector3f _center_freq_hz_filtered;
|
|
// signal to noise ratio of PSD at the detected centre frequency
|
|
Vector3f _center_snr;
|
|
// detected peak width
|
|
Vector3f _center_bandwidth_hz;
|
|
// axes that still require noise calibration
|
|
uint8_t _noise_needs_calibration : 3;
|
|
// whether the analyzer is mid-cycle
|
|
bool _analysis_started = false;
|
|
};
|
|
|
|
// Shared FFT engine state local to the FFT thread
|
|
EngineState _thread_state;
|
|
// Shared FFT engine state accessible by the main thread
|
|
EngineState _global_state;
|
|
|
|
// configuration data local to the FFT thread but set from the main thread
|
|
struct EngineConfig {
|
|
// whether the analyzer should be run
|
|
bool _analysis_enabled;
|
|
// minimum frequency of the detection window
|
|
uint16_t _fft_min_hz;
|
|
// maximum frequency of the detection window
|
|
uint16_t _fft_max_hz;
|
|
// configured start bin based on min hz
|
|
uint16_t _fft_start_bin;
|
|
// configured end bin based on max hz
|
|
uint16_t _fft_end_bin;
|
|
// attenuation cutoff for calculation of hover bandwidth
|
|
float _attenuation_cutoff;
|
|
// SNR Threshold
|
|
float _snr_threshold_db;
|
|
} _config;
|
|
|
|
// number of sampeles needed before a new frame can be processed
|
|
uint16_t _samples_per_frame;
|
|
// downsampled gyro data circular buffer index frequency analysis
|
|
uint16_t _circular_buffer_idx;
|
|
// number of collected unprocessed gyro samples
|
|
uint16_t _sample_count;
|
|
|
|
// downsampled gyro data circular buffer for frequency analysis
|
|
float* _downsampled_gyro_data[XYZ_AXIS_COUNT];
|
|
// accumulator for sampled gyro data
|
|
Vector3f _oversampled_gyro_accum;
|
|
// count of oversamples
|
|
uint16_t _oversampled_gyro_count;
|
|
// current write index in the buffer
|
|
uint16_t _downsampled_gyro_idx;
|
|
|
|
// state of the FFT engine
|
|
AP_HAL::DSP::FFTWindowState* _state;
|
|
// update state machine step information
|
|
uint8_t _update_axis;
|
|
// noise base of the gyros
|
|
Vector3f* _ref_energy;
|
|
// the number of cycles required to have a proper noise reference
|
|
uint16_t _noise_cycles;
|
|
// number of cycles over which to generate noise ensemble averages
|
|
uint16_t _noise_calibration_cycles[XYZ_AXIS_COUNT];
|
|
// current _sample_mode
|
|
uint8_t _current_sample_mode : 3;
|
|
// harmonic multiplier for two highest peaks
|
|
float _harmonic_multiplier;
|
|
// searched harmonics - inferred from harmonic notch harmoncis
|
|
uint8_t _harmonics;
|
|
|
|
// smoothing filter on the output
|
|
LowPassFilterFloat _center_freq_filter[XYZ_AXIS_COUNT];
|
|
// smoothing filter on the bandwidth
|
|
LowPassFilterFloat _center_bandwidth_filter[XYZ_AXIS_COUNT];
|
|
|
|
// configured sampling rate
|
|
uint16_t _fft_sampling_rate_hz;
|
|
// number of cycles without a detected signal
|
|
uint8_t _missed_cycles;
|
|
// whether the analyzer initialized correctly
|
|
bool _initialized;
|
|
|
|
// whether the analyzer should be run
|
|
bool _analysis_enabled = true;
|
|
// whether the update thread was created
|
|
bool _thread_created = false;
|
|
// minimum frequency of the detection window
|
|
AP_Int16 _fft_min_hz;
|
|
// maximum frequency of the detection window
|
|
AP_Int16 _fft_max_hz;
|
|
// size of the FFT window
|
|
AP_Int16 _window_size;
|
|
// percentage overlap of FFT windows
|
|
AP_Float _window_overlap;
|
|
// overall enablement of the feature
|
|
AP_Int8 _enable;
|
|
// gyro rate sampling or cycle divider
|
|
AP_Int8 _sample_mode;
|
|
// learned throttle reference for the hover frequency
|
|
AP_Float _throttle_ref;
|
|
// learned hover filter frequency
|
|
AP_Float _freq_hover_hz;
|
|
// SNR Threshold
|
|
AP_Float _snr_threshold_db;
|
|
// attenuation to use for calculating the peak bandwidth at hover
|
|
AP_Float _attenuation_power_db;
|
|
// learned peak bandwidth at configured attenuation at hover
|
|
AP_Float _bandwidth_hover_hz;
|
|
AP_InertialSensor* _ins;
|
|
#if DEBUG_FFT
|
|
uint32_t _last_output_ms;
|
|
EngineState _debug_state;
|
|
float _debug_max_bin_freq;
|
|
float _debug_max_freq_bin;
|
|
uint16_t _debug_max_bin;
|
|
float _debug_snr;
|
|
#endif
|
|
|
|
static AP_GyroFFT *_singleton;
|
|
};
|
|
|
|
namespace AP {
|
|
AP_GyroFFT *fft();
|
|
};
|
|
|
|
#endif // HAL_GYROFFT_ENABLED
|