LCOV - code coverage report
Current view: top level - AP_Airspeed - AP_Airspeed_AUAV.cpp (source / functions) Hit Total Coverage
Test: lcov.info Lines: 0 175 0.0 %
Date: 2026-09-09 11:02:08 Functions: 0 11 0.0 %

          Line data    Source code
       1             : /*
       2             :    This program is free software: you can redistribute it and/or modify
       3             :    it under the terms of the GNU General Public License as published by
       4             :    the Free Software Foundation, either version 3 of the License, or
       5             :    (at your option) any later version.
       6             : 
       7             :    This program is distributed in the hope that it will be useful,
       8             :    but WITHOUT ANY WARRANTY; without even the implied warranty of
       9             :    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
      10             :    GNU General Public License for more details.
      11             : 
      12             :    You should have received a copy of the GNU General Public License
      13             :    along with this program.  If not, see <http://www.gnu.org/licenses/>.
      14             :  */
      15             : 
      16             : /*
      17             :   backend driver for airspeed from a I2C AUAV sensor
      18             :  */
      19             : #include "AP_Airspeed_AUAV.h"
      20             : 
      21             : #if AP_AIRSPEED_AUAV_ENABLED
      22             : 
      23             : #define AUAV_AIRSPEED_I2C_ADDR 0x26
      24             : #define MEASUREMENT_TIMEOUT_MS 200
      25             : 
      26             : #include <AP_Common/AP_Common.h>
      27             : #include <AP_HAL/AP_HAL.h>
      28             : #include <AP_HAL/I2CDevice.h>
      29             : #include <AP_Math/AP_Math.h>
      30             : #include <GCS_MAVLink/GCS.h>
      31             : 
      32             : extern const AP_HAL::HAL &hal;
      33             : 
      34           0 : AUAV_Pressure_sensor::AUAV_Pressure_sensor(AP_HAL::Device *&_dev, Type _type) :
      35           0 :     dev(_dev),
      36           0 :     type(_type)
      37             : {
      38           0 : }
      39             : 
      40           0 : AP_Airspeed_AUAV::AP_Airspeed_AUAV(AP_Airspeed &_frontend, uint8_t _instance, const float _range_inH2O) :
      41             :     AP_Airspeed_Backend(_frontend, _instance),
      42           0 :     range_inH2O(_range_inH2O)
      43             : {
      44           0 : }
      45             : 
      46             : // probe for a sensor
      47           0 : bool AP_Airspeed_AUAV::probe(uint8_t bus, uint8_t address)
      48             : {
      49           0 :     _dev = hal.i2c_mgr->get_device_ptr(bus, address);
      50           0 :     if (!_dev) {
      51           0 :         return false;
      52             :     }
      53             : 
      54           0 :     WITH_SEMAPHORE(_dev->get_semaphore());
      55             : 
      56           0 :     _dev->set_retries(10);
      57           0 :     if (!sensor.measure()) {
      58           0 :         return false;
      59             :     }
      60           0 :     hal.scheduler->delay(20);
      61             : 
      62             :     // Don't fill in values yet as the compensation coefficients are not configured
      63             :     float PComp, temperature;
      64           0 :     return sensor.collect(PComp, temperature) == AUAV_Pressure_sensor::Status::Normal;
      65           0 : }
      66             : 
      67             : // probe and initialise the sensor
      68           0 : bool AP_Airspeed_AUAV::init()
      69             : {
      70           0 :     const auto bus = get_bus();
      71           0 :     if (!probe(bus, AUAV_AIRSPEED_I2C_ADDR)) {
      72           0 :         GCS_SEND_TEXT(MAV_SEVERITY_ERROR, "AUAV AIRSPEED[%u]: no sensor found on bus %u", get_instance(), bus);
      73           0 :         return false;
      74             :     }
      75             : 
      76           0 :     _dev->set_device_type(uint8_t(DevType::AUAV));
      77           0 :     set_bus_id(_dev->get_bus_id());
      78             : 
      79           0 :     GCS_SEND_TEXT(MAV_SEVERITY_INFO, "AUAV AIRSPEED[%u]: Found bus %u addr 0x%02x", get_instance(), _dev->bus_num(), _dev->get_bus_address());
      80             : 
      81             :     // drop to 2 retries for runtime
      82           0 :     _dev->register_periodic_callback(20000,
      83             :                                      FUNCTOR_BIND_MEMBER(&AP_Airspeed_AUAV::_timer, void));
      84           0 :     return true;
      85             : }
      86             : 
      87             : // start a measurement
      88           0 : bool AUAV_Pressure_sensor::measure()
      89             : {
      90           0 :     uint8_t cmd = 0xAE; // start average8
      91             :     // Differential: max 16.2ms measurement time 
      92             :     // Absolute: 37ms
      93           0 :     if (dev->transfer(&cmd, 1, nullptr, 0)) {
      94           0 :         return true;
      95             :     }
      96           0 :     return false;
      97             : }
      98             : 
      99             : // read the values from the sensor
     100             : // Return true if successful
     101             : // pressure corrected but not scaled to the correct units
     102             : // temperature in degrees centigrade
     103           0 : AUAV_Pressure_sensor::Status AUAV_Pressure_sensor::collect(float &PComp, float &temperature)
     104             : {
     105             :     uint8_t inbuf[7];
     106           0 :     if (!dev->read((uint8_t *)&inbuf, sizeof(inbuf))) {
     107           0 :         return Status::Fault;
     108             :     }
     109           0 :     const uint8_t status = inbuf[0];
     110             : 
     111             :     // power on, not busy, reading normal, not out of range
     112           0 :     const uint8_t healthy_status = type == Type::Differential ? 0x50 :  0x40;
     113           0 :     if (status != healthy_status) {
     114           0 :         if ((status & (1U << 5)) != 0) {
     115           0 :             return Status::Busy;
     116             :         }
     117           0 :         return Status::Fault;
     118             :     }
     119           0 :     const int32_t Tref_Counts = 7576807; // temperature counts at 25C
     120           0 :     const float TC50Scale = 100.0f * 100.0f * 167772.2f; // scale TC50 to 1.0% FS0
     121             :     float Pdiff, TC50;
     122             : 
     123             :     // Convert unsigned 24-bit pressure value to signed +/- 23-bit:
     124           0 :     const int32_t iPraw = (inbuf[1]<<16) + (inbuf[2]<<8) + inbuf[3] - 0x800000;
     125             : 
     126             :     // Convert signed 23-bit value to float, normalized to +/- 1.0:
     127           0 :     const float Pnorm = float(iPraw) / float(0x7FFFFF);
     128           0 :     const float AP3 = LIN_A * Pnorm * Pnorm * Pnorm; // A*Pout^3
     129           0 :     const float BP2 = LIN_B * Pnorm * Pnorm; // B*Pout^2
     130           0 :     const float CP = LIN_C * Pnorm; // C*POut
     131           0 :     const float Corr = AP3 + BP2 + CP + LIN_D; // Linearity correction term
     132           0 :     const float Pcorr = Pnorm + Corr; // Corrected P, range +/-1.0.
     133             : 
     134             :     // Compute difference from reference temperature, in sensor counts:
     135           0 :     const int32_t iTemp = (inbuf[4]<<16) + (inbuf[5]<<8) + inbuf[6]; // 24-bit temperature
     136             : 
     137             :     // get temperature in degrees C
     138           0 :     temperature = (iTemp * 155) / float(1U<<24) - 45;
     139             : 
     140           0 :     const int32_t Tdiff = iTemp - Tref_Counts; // see constant defined above.
     141           0 :     const float Pnfso = (Pcorr + 1.0f) * 0.5;
     142             : 
     143             :     //TC50: Select High/Low, based on current temp above/below 25C:
     144           0 :     if (Tdiff > 0) {
     145           0 :         TC50 = TC50H;
     146             :     } else {
     147           0 :         TC50 = TC50L;
     148             :     }
     149             : 
     150             :     // Find absolute difference between midrange and reading (abs(Pnfso-0.5)):
     151           0 :     if (Pnfso > 0.5) {
     152           0 :         Pdiff = Pnfso - 0.5;
     153             :     } else {
     154           0 :         Pdiff = 0.5f - Pnfso;
     155             :     }
     156             : 
     157           0 :     const float Tcorr = (1.0f - (Es * 2.5f * Pdiff)) * Tdiff * TC50 / TC50Scale;
     158           0 :     const float PCorrt = Pnfso - Tcorr; // corrected P: float, [0 to +1.0)
     159           0 :     PComp = PCorrt * (float)0xFFFFFF;
     160             : 
     161           0 :     return Status::Normal;
     162             : }
     163             : 
     164           0 : bool AUAV_Pressure_sensor::read_register(uint8_t cmd, uint16_t &result)
     165             : {
     166           0 :     switch (coefficient_step) {
     167           0 :         case CoefficientStep::request: {
     168           0 :             if (dev->transfer(&cmd, sizeof(cmd), nullptr, 0)) {
     169           0 :                 coefficient_step = CoefficientStep::read;
     170             :             }
     171           0 :             return false;
     172             :         }
     173             : 
     174           0 :         case CoefficientStep::read: {
     175             :             // Next step is always another request, either for the next coefficient or a re-request if this read fails
     176           0 :             coefficient_step = CoefficientStep::request;
     177             : 
     178           0 :             const uint8_t healthy_status = type == Type::Differential ? 0x50 :  0x40;
     179             :             uint8_t raw_bytes[3];
     180           0 :             if (!dev->read((uint8_t *)&raw_bytes, sizeof(raw_bytes))) {
     181           0 :                 return false;
     182             :             }
     183           0 :             if (raw_bytes[0] != healthy_status) {
     184           0 :                 return false;
     185             :             }
     186           0 :             result = ((uint16_t)raw_bytes[1] << 8) | (uint16_t)raw_bytes[2];
     187           0 :             return true;
     188             :         }
     189             :     }
     190             : 
     191           0 :     return false;
     192             : 
     193             : }
     194             : 
     195           0 : void AUAV_Pressure_sensor::read_coefficients()
     196             : {
     197           0 :     const uint8_t cmd_start = type == Type::Differential ? 0x2B : 0x2F;
     198             : 
     199             :     // Differential can happily run through these in one call, Absolute gets stuck, no idea why.
     200             :     // Space them out anyway to be inline with the spec
     201             : 
     202           0 :     switch (stage) {
     203           0 :     case CoefficientStage::A_high: {
     204             :         uint16_t high;
     205           0 :         if (!read_register(cmd_start + 0, high)) {
     206           0 :             return;
     207             :         }
     208           0 :         LIN_A = (float)(high << 16)/(float)(0x7FFFFFFF);
     209           0 :         stage = CoefficientStage::A_low;
     210           0 :         break;
     211             :     }
     212             : 
     213           0 :     case CoefficientStage::A_low: {
     214             :         uint16_t low;
     215           0 :         if (!read_register(cmd_start + 1, low)) {
     216           0 :             return;
     217             :         }
     218           0 :         LIN_A += (float)(low)/(float)(0x7FFFFFFF);
     219           0 :         stage = CoefficientStage::B_high;
     220           0 :         break;
     221             :     }
     222             : 
     223           0 :     case CoefficientStage::B_high: {
     224             :         uint16_t high;
     225           0 :         if (!read_register(cmd_start + 2, high)) {
     226           0 :             return;
     227             :         }
     228           0 :         LIN_B = (float)(high << 16)/(float)(0x7FFFFFFF);
     229           0 :         stage = CoefficientStage::B_low;
     230           0 :         break;
     231             :     }
     232             : 
     233           0 :     case CoefficientStage::B_low: {
     234             :         uint16_t low;
     235           0 :         if (!read_register(cmd_start + 3, low)) {
     236           0 :             return;
     237             :         }
     238           0 :         LIN_B += (float)(low)/(float)(0x7FFFFFFF);
     239           0 :         stage = CoefficientStage::C_high;
     240           0 :         break;
     241             :     }
     242             : 
     243           0 :     case CoefficientStage::C_high: {
     244             :         uint16_t high;
     245           0 :         if (!read_register(cmd_start + 4, high)) {
     246           0 :             return;
     247             :         }
     248           0 :         LIN_C = (float)(high << 16)/(float)(0x7FFFFFFF);
     249           0 :         stage = CoefficientStage::C_low;
     250           0 :         break;
     251             :     }
     252             : 
     253           0 :     case CoefficientStage::C_low: {
     254             :         uint16_t low;
     255           0 :         if (!read_register(cmd_start + 5, low)) {
     256           0 :             return;
     257             :         }
     258           0 :         LIN_C += (float)(low)/(float)(0x7FFFFFFF);
     259           0 :         stage = CoefficientStage::D_high;
     260           0 :         break;
     261             :     }
     262             : 
     263           0 :     case CoefficientStage::D_high: {
     264             :         uint16_t high;
     265           0 :         if (!read_register(cmd_start + 6, high)) {
     266           0 :             return;
     267             :         }
     268           0 :         LIN_D = (float)(high << 16)/(float)(0x7FFFFFFF);
     269           0 :         stage = CoefficientStage::D_low;
     270           0 :         break;
     271             :     }
     272             : 
     273           0 :     case CoefficientStage::D_low: {
     274             :         uint16_t low;
     275           0 :         if (!read_register(cmd_start + 7, low)) {
     276           0 :             return;
     277             :         }
     278           0 :         LIN_D += (float)(low)/(float)(0x7FFFFFFF);
     279           0 :         stage = CoefficientStage::E_high;
     280           0 :         break;
     281             :     }
     282             : 
     283           0 :     case CoefficientStage::E_high: {
     284             :         uint16_t TC50HL;
     285           0 :         if (!read_register(cmd_start + 8, TC50HL)) {
     286           0 :             return;
     287             :         }
     288           0 :         const int8_t i8TC50H = (TC50HL >> 8) & 0xFF;
     289           0 :         const int8_t i8TC50L = TC50HL & 0xFF;
     290           0 :         TC50H = (float)(i8TC50H)/(float)(0x7F);
     291           0 :         TC50L = (float)(i8TC50L)/(float)(0x7F);
     292           0 :         stage = CoefficientStage::E_low;
     293           0 :         break;
     294             :     }
     295             : 
     296           0 :     case CoefficientStage::E_low: {
     297             :         uint16_t es;
     298           0 :         if (!read_register(cmd_start + 9, es)) {
     299           0 :             return;
     300             :         }
     301           0 :         Es = (float)(es & 0xFF)/(float)(0x7F);
     302           0 :         stage = CoefficientStage::Done;
     303             : 
     304             :         // Drop to 2 retry's for runtime
     305           0 :         dev->set_retries(2);
     306           0 :         break;
     307             :     }
     308             : 
     309           0 :     case CoefficientStage::Done:
     310           0 :         break;
     311             :     }
     312             : }
     313             : 
     314           0 : void AP_Airspeed_AUAV::_timer()
     315             : {
     316           0 :     if (sensor.stage != AUAV_Pressure_sensor::CoefficientStage::Done) {
     317           0 :         sensor.read_coefficients();
     318           0 :         return;
     319             :     }
     320             : 
     321           0 :     if (measurement_requested) {
     322             :         // Read in result of last measurement
     323             :         float Pcomp;
     324           0 :         switch (sensor.collect(Pcomp, temp_C)) {
     325           0 :         case AUAV_Pressure_sensor::Status::Normal:
     326             :             // Calculate pressure and update last read time
     327           0 :             last_sample_time_ms = AP_HAL::millis();
     328             : 
     329             :             // Convert to correct units and apply range
     330           0 :             pressure = 248.8f*1.25f*((Pcomp-8388608)/16777216.0f) * 2 * range_inH2O;
     331           0 :             break;
     332             : 
     333           0 :         case AUAV_Pressure_sensor::Status::Busy:
     334             :             // Don't request a new measurement
     335           0 :             return;
     336             : 
     337           0 :         case AUAV_Pressure_sensor::Status::Fault:
     338           0 :             break;
     339             :         }
     340             :     }
     341             : 
     342             :     // Request a new measurement
     343           0 :     measurement_requested = sensor.measure();
     344             : }
     345             : 
     346             : // return the current differential_pressure in Pascal
     347           0 : bool AP_Airspeed_AUAV::get_differential_pressure(float &_pressure)
     348             : {
     349           0 :     WITH_SEMAPHORE(sem);
     350           0 :     _pressure = pressure;
     351           0 :     return AP_HAL::millis() - last_sample_time_ms < MEASUREMENT_TIMEOUT_MS;
     352           0 : }
     353             : 
     354             : // return the current temperature in degrees C, if available
     355           0 : bool AP_Airspeed_AUAV::get_temperature(float &_temperature)
     356             : {
     357           0 :     WITH_SEMAPHORE(sem);
     358           0 :     _temperature = temp_C;
     359           0 :     return AP_HAL::millis() - last_sample_time_ms < MEASUREMENT_TIMEOUT_MS;
     360           0 : }
     361             : 
     362             : #endif  // AP_AIRSPEED_AUAV_ENABLED

Generated by: LCOV version 1.14