123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477 |
- /*
- 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/>.
- */
- #include <AP_HAL/AP_HAL.h>
- #include <utility>
- #if (CONFIG_HAL_BOARD_SUBTYPE == HAL_BOARD_SUBTYPE_LINUX_BEBOP || \
- CONFIG_HAL_BOARD_SUBTYPE == HAL_BOARD_SUBTYPE_LINUX_DISCO) && \
- defined(HAVE_LIBIIO)
- #include <stdlib.h>
- #include <unistd.h>
- #include <stdio.h>
- #include <fcntl.h>
- #include <string.h>
- #include <sys/mman.h>
- #include <linux/types.h>
- #include <errno.h>
- #include <sys/ioctl.h>
- #include <float.h>
- #include <math.h>
- #include <time.h>
- #include <iio.h>
- #include "AP_RangeFinder_Bebop.h"
- #include <AP_HAL_Linux/Thread.h>
- #include <AP_HAL_Linux/GPIO.h>
- /*
- * this mode is used at low altitude
- * send 4 wave patterns
- * gpio in low mode
- */
- #define RNFD_BEBOP_DEFAULT_MODE 1
- /*
- * the number of p7s in the iio buffer
- */
- #define RNFD_BEBOP_P7_COUNT 8192
- extern const AP_HAL::HAL& hal;
- static const uint16_t waveform_mode0[14] = {
- 4000, 3800, 3600, 3400, 3200, 3000, 2800,
- 2600, 2400, 2200, 2000, 1800, 1600, 1400,
- };
- static const uint16_t waveform_mode1[32] = {
- 4190, 4158, 4095, 4095, 4095, 4095, 4095, 4095, 4095,
- 4095, 4090, 4058, 3943, 3924, 3841, 3679, 3588, 3403,
- 3201, 3020, 2816, 2636, 2448, 2227, 2111, 1955, 1819,
- 1675, 1540, 1492, 1374, 1292
- };
- AP_RangeFinder_Bebop::AP_RangeFinder_Bebop(RangeFinder::RangeFinder_State &_state, AP_RangeFinder_Params &_params) :
- AP_RangeFinder_Backend(_state, _params),
- _thread(new Linux::Thread(FUNCTOR_BIND_MEMBER(&AP_RangeFinder_Bebop::_loop, void)))
- {
- _init();
- _freq = RNFD_BEBOP_DEFAULT_FREQ;
- _filtered_capture_size = _adc.buffer_size / _filter_average;
- _filtered_capture = (unsigned int*) calloc(1, sizeof(_filtered_capture[0]) *
- _filtered_capture_size);
- _mode = RNFD_BEBOP_DEFAULT_MODE;
- /* SPI and IIO can not be initialized just yet */
- memset(_tx[0], 0xF0, 16);
- memset(_tx[1], 0xF0, 4);
- memset(_purge, 0xFF, RNFD_BEBOP_NB_PULSES_PURGE);
- _tx_buf = _tx[_mode];
- }
- AP_RangeFinder_Bebop::~AP_RangeFinder_Bebop()
- {
- iio_buffer_destroy(_adc.buffer);
- _adc.buffer = nullptr;
- iio_context_destroy(_iio);
- _iio = nullptr;
- }
- bool AP_RangeFinder_Bebop::detect()
- {
- return true;
- }
- unsigned short AP_RangeFinder_Bebop::get_threshold_at(int i_capture)
- {
- uint16_t threshold_value = 0;
- /*
- * We define several kinds of thresholds signals ; for an echo to be
- * recorded, its maximum amplitude has to be ABOVE that threshold.
- * There is one kind of threshold per mode (mode 0 is "low" and mode 1 is
- * "high")
- * Basically they look like this :
- *
- * on this part
- * of the capture
- * amplitude we use
- * ^ the waveform
- * | <---------->
- * 4195 +-----+
- * |
- * |
- * |
- * |
- * 1200| +----------------+
- * +-------------------------------------->
- * + low high time
- * limit limit
- *
- * */
- switch (_mode) {
- case 0:
- if (i_capture < 139) {
- threshold_value = 4195;
- } else if (i_capture < 153) {
- threshold_value = waveform_mode0[i_capture - 139];
- } else {
- threshold_value = 1200;
- }
- break;
- case 1:
- if (i_capture < 73) {
- threshold_value = 4195;
- } else if (i_capture < 105) {
- threshold_value = waveform_mode1[i_capture - 73];
- } else if (i_capture < 617) {
- threshold_value = 1200;
- } else {
- threshold_value = 4195;
- }
- break;
- default:
- break;
- }
- return threshold_value;
- }
- int AP_RangeFinder_Bebop::_apply_averaging_filter(void)
- {
- int i_filter = 0; /* index in the filtered buffer */
- int i_capture = 0; /* index in the capture buffer : starts incrementing when
- the captured data first exceeds
- RNFD_BEBOP_THRESHOLD_ECHO_INIT */
- unsigned int filtered_value = 0;
- bool first_echo = false;
- unsigned char *data;
- unsigned char *start;
- unsigned char *end;
- ptrdiff_t step;
- step = iio_buffer_step(_adc.buffer);
- end = (unsigned char *) iio_buffer_end(_adc.buffer);
- start = (unsigned char *) iio_buffer_first(_adc.buffer, _adc.channel);
- for (data = start; data < end; data += step) {
- unsigned int current_value = 0;
- iio_channel_convert(_adc.channel, ¤t_value, data);
- /* We keep on advancing in the captured buffer without registering the
- * filtered data until the signal first exceeds a given value */
- if (!first_echo && current_value < threshold_echo_init) {
- continue;
- } else {
- first_echo = true;
- }
- filtered_value += current_value;
- if (i_capture % _filter_average == 0) {
- _filtered_capture[i_filter] = filtered_value / _filter_average;
- filtered_value = 0;
- i_filter++;
- }
- i_capture++;
- }
- return 0;
- }
- int AP_RangeFinder_Bebop::_search_local_maxima(void)
- {
- int i_echo = 0; /* index in echo array */
- for (int i_capture = 1; i_capture <
- (int)_filtered_capture_size - 1; i_capture++) {
- if (_filtered_capture[i_capture] >= get_threshold_at(i_capture)) {
- unsigned short curr = _filtered_capture[i_capture];
- unsigned short prev = _filtered_capture[i_capture - 1];
- unsigned short next = _filtered_capture[i_capture + 1];
- if (curr >= prev && (curr > next || prev <
- get_threshold_at(i_capture - 1))) {
- _echoes[i_echo].max_index = i_capture;
- i_echo++;
- if (i_echo >= RNFD_BEBOP_MAX_ECHOES) {
- break;
- }
- }
- }
- }
- _nb_echoes = i_echo;
- return 0;
- }
- int AP_RangeFinder_Bebop::_search_maximum_with_max_amplitude(void)
- {
- unsigned short max = 0;
- int max_idx = -1;
- for (int i_echo = 0; i_echo < _nb_echoes ; i_echo++) {
- unsigned short curr = _filtered_capture[_echoes[i_echo].max_index];
- if (curr > max) {
- max = curr;
- max_idx = i_echo;
- }
- }
- if (max_idx >= 0) {
- return _echoes[max_idx].max_index;
- } else {
- return -1;
- }
- }
- void AP_RangeFinder_Bebop::_loop(void)
- {
- int max_index;
- while(1) {
- _launch();
- _capture();
- if (_apply_averaging_filter() < 0) {
- hal.console->printf(
- "AR_RangeFinder_Bebop: could not apply averaging filter");
- }
- if (_search_local_maxima() < 0) {
- hal.console->printf("Did not find any local maximum");
- }
- max_index = _search_maximum_with_max_amplitude();
- if (max_index >= 0) {
- _altitude = (float)(max_index * RNFD_BEBOP_SOUND_SPEED) /
- (2 * (RNFD_BEBOP_DEFAULT_ADC_FREQ / _filter_average));
- }
- _mode = _update_mode(_altitude);
- }
- }
- void AP_RangeFinder_Bebop::update(void)
- {
- static bool first_call = true;
- if (first_call) {
- _thread->start("RangeFinder_Bebop", SCHED_FIFO, 11);
- first_call = false;
- }
- state.distance_cm = (uint16_t) (_altitude * 100);
- state.last_reading_ms = AP_HAL::millis();
- update_status();
- }
- /*
- * purge is used when changing mode
- */
- int AP_RangeFinder_Bebop::_launch_purge()
- {
- iio_device_attr_write(_adc.device, "buffer/enable", "1");
- _spi->transfer(_purge, RNFD_BEBOP_NB_PULSES_PURGE, nullptr, 0);
- return 0;
- }
- void AP_RangeFinder_Bebop::_configure_gpio(int value)
- {
- switch (value) {
- case 1: // high voltage
- _gpio->write(LINUX_GPIO_ULTRASOUND_VOLTAGE, 1);
- break;
- case 0: // low voltage
- _gpio->write(LINUX_GPIO_ULTRASOUND_VOLTAGE, 0);
- break;
- default:
- hal.console->printf("bad gpio value (%d)", value);
- break;
- }
- }
- /*
- * reconfigure the pulse that will be sent over spi
- * first send a purge then configure the new pulse
- */
- void AP_RangeFinder_Bebop::_reconfigure_wave()
- {
- /* configure the output buffer for a purge */
- /* perform a purge */
- if (_launch_purge() < 0) {
- hal.console->printf("purge could not send data overspi");
- }
- if (_capture() < 0) {
- hal.console->printf("purge could not capture data");
- }
- _tx_buf = _tx[_mode];
- switch (_mode) {
- case 1: /* low voltage */
- _configure_gpio(0);
- break;
- case 0: /* high voltage */
- _configure_gpio(1);
- break;
- default:
- hal.console->printf("WARNING, invalid value to configure gpio\n");
- break;
- }
- }
- /*
- * First configuration of the the pulse that will be send over spi
- */
- int AP_RangeFinder_Bebop::_configure_wave()
- {
- _spi->set_speed(AP_HAL::Device::SPEED_HIGH);
- _configure_gpio(0);
- return 0;
- }
- /*
- * Configure the adc to get the samples
- */
- int AP_RangeFinder_Bebop::_configure_capture()
- {
- const char *adcname = "p7mu-adc_2";
- const char *adcchannel = "voltage2";
- /* configure adc interface using libiio */
- _iio = iio_create_local_context();
- if (!_iio) {
- return -1;
- }
- _adc.device = iio_context_find_device(_iio, adcname);
- if (!_adc.device) {
- hal.console->printf("Unable to find %s", adcname);
- goto error_destroy_context;
- }
- _adc.channel = iio_device_find_channel(_adc.device, adcchannel,
- false);
- if (!_adc.channel) {
- hal.console->printf("Fail to init adc channel %s", adcchannel);
- goto error_destroy_context;
- }
- iio_channel_enable(_adc.channel);
- _adc.freq = RNFD_BEBOP_DEFAULT_ADC_FREQ >> RNFD_BEBOP_FILTER_POWER;
- _adc.threshold_time_rejection = 2.0 / RNFD_BEBOP_SOUND_SPEED *
- _adc.freq;
- /* Create input buffer */
- _adc.buffer_size = RNFD_BEBOP_P7_COUNT;
- if (iio_device_set_kernel_buffers_count(_adc.device, 1)) {
- hal.console->printf("cannot set buffer count");
- goto error_destroy_context;
- }
- _adc.buffer = iio_device_create_buffer(_adc.device,
- _adc.buffer_size, false);
- if (!_adc.buffer) {
- hal.console->printf("Fail to create buffer : %s", strerror(errno));
- goto error_destroy_context;
- }
- return 0;
- error_destroy_context:
- iio_buffer_destroy(_adc.buffer);
- _adc.buffer = nullptr;
- iio_context_destroy(_iio);
- _iio = nullptr;
- return -1;
- }
- void AP_RangeFinder_Bebop::_init()
- {
- _spi = std::move(hal.spi->get_device("bebop"));
- _gpio = AP_HAL::get_HAL().gpio;
- if (_gpio == nullptr) {
- AP_HAL::panic("Could not find GPIO device for Bebop ultrasound");
- }
- if (_configure_capture() < 0) {
- return;
- }
- _configure_wave();
- return;
- }
- /*
- * enable the capture buffer
- * send a pulse over spi
- */
- int AP_RangeFinder_Bebop::_launch()
- {
- iio_device_attr_write(_adc.device, "buffer/enable", "1");
- _spi->transfer(_tx_buf, RNFD_BEBOP_NB_PULSES_MAX, nullptr, 0);
- return 0;
- }
- /*
- * read the iio buffer
- * iio_buffer_refill is blocking by default, so this function is also
- * blocking until samples are available
- * disable the capture buffer
- */
- int AP_RangeFinder_Bebop::_capture()
- {
- int ret;
- ret = iio_buffer_refill(_adc.buffer);
- iio_device_attr_write(_adc.device, "buffer/enable", "0");
- return ret;
- }
- int AP_RangeFinder_Bebop::_update_mode(float altitude)
- {
- switch (_mode) {
- case 0:
- if (altitude < RNFD_BEBOP_TRANSITION_HIGH_TO_LOW
- && !is_zero(altitude)) {
- if (_hysteresis_counter > RNFD_BEBOP_TRANSITION_COUNT) {
- _mode = 1;
- _hysteresis_counter = 0;
- _reconfigure_wave();
- } else {
- _hysteresis_counter++;
- }
- } else {
- _hysteresis_counter = 0;
- }
- break;
- default:
- case 1:
- if (altitude > RNFD_BEBOP_TRANSITION_LOW_TO_HIGH
- || is_zero(altitude)) {
- if (_hysteresis_counter > RNFD_BEBOP_TRANSITION_COUNT) {
- _mode = 0;
- _hysteresis_counter = 0;
- _reconfigure_wave();
- } else {
- _hysteresis_counter++;
- }
- } else {
- _hysteresis_counter = 0;
- }
- break;
- }
- return _mode;
- }
- #endif
|