From b058f3fdce9b5f14bd53d70fcb545af172bf6a78 Mon Sep 17 00:00:00 2001
From: Andrew Tridgell <andrew@tridgell.net>
Date: Thu, 8 Jan 2015 14:16:28 +1100
Subject: [PATCH] AP_InertialSensor: added missing files

---
 .../AP_InertialSensor_Backend.cpp             |  72 ++
 .../AP_InertialSensor_Backend.h               |  87 ++
 .../AP_InertialSensor_L3GD20.cpp              | 635 +++++++++++++
 .../AP_InertialSensor_L3GD20.h                |  88 ++
 .../AP_InertialSensor_LSM303D.cpp             | 831 ++++++++++++++++++
 .../AP_InertialSensor_LSM303D.h               | 107 +++
 6 files changed, 1820 insertions(+)
 create mode 100644 libraries/AP_InertialSensor/AP_InertialSensor_Backend.cpp
 create mode 100644 libraries/AP_InertialSensor/AP_InertialSensor_Backend.h
 create mode 100644 libraries/AP_InertialSensor/AP_InertialSensor_L3GD20.cpp
 create mode 100644 libraries/AP_InertialSensor/AP_InertialSensor_L3GD20.h
 create mode 100644 libraries/AP_InertialSensor/AP_InertialSensor_LSM303D.cpp
 create mode 100644 libraries/AP_InertialSensor/AP_InertialSensor_LSM303D.h

diff --git a/libraries/AP_InertialSensor/AP_InertialSensor_Backend.cpp b/libraries/AP_InertialSensor/AP_InertialSensor_Backend.cpp
new file mode 100644
index 000000000..3ed5dd28f
--- /dev/null
+++ b/libraries/AP_InertialSensor/AP_InertialSensor_Backend.cpp
@@ -0,0 +1,72 @@
+/// -*- tab-width: 4; Mode: C++; c-basic-offset: 4; indent-tabs-mode: nil -*-
+
+#include <AP_HAL.h>
+#include "AP_InertialSensor.h"
+#include "AP_InertialSensor_Backend.h"
+
+AP_InertialSensor_Backend::AP_InertialSensor_Backend(AP_InertialSensor &imu) :
+    _imu(imu),
+    _product_id(AP_PRODUCT_ID_NONE)
+{}
+
+/*
+  rotate gyro vector and add the gyro offset
+ */
+void AP_InertialSensor_Backend::_rotate_and_offset_gyro(uint8_t instance, const Vector3f &gyro)
+{
+    _imu._gyro[instance] = gyro;
+    _imu._gyro[instance].rotate(_imu._board_orientation);
+    _imu._gyro[instance] -= _imu._gyro_offset[instance];
+    _imu._gyro_healthy[instance] = true;
+}
+
+/*
+  rotate accel vector, scale and add the accel offset
+ */
+void AP_InertialSensor_Backend::_rotate_and_offset_accel(uint8_t instance, const Vector3f &accel)
+{
+    _imu._accel[instance] = accel;
+    _imu._accel[instance].rotate(_imu._board_orientation);
+
+    const Vector3f &accel_scale = _imu._accel_scale[instance].get();
+    _imu._accel[instance].x *= accel_scale.x;
+    _imu._accel[instance].y *= accel_scale.y;
+    _imu._accel[instance].z *= accel_scale.z;
+    _imu._accel[instance] -= _imu._accel_offset[instance];
+    _imu._accel_healthy[instance] = true;
+}
+
+// set accelerometer error_count
+void AP_InertialSensor_Backend::_set_accel_error_count(uint8_t instance, uint32_t error_count)
+{
+    _imu._accel_error_count[instance] = error_count;
+}
+
+// set gyro error_count
+void AP_InertialSensor_Backend::_set_gyro_error_count(uint8_t instance, uint32_t error_count)
+{
+    _imu._gyro_error_count[instance] = error_count;
+}
+
+/*
+  return the default filter frequency in Hz for the sample rate
+  
+  This uses the sample_rate as a proxy for what type of vehicle it is
+  (ie. plane and rover run at 50Hz). Copters need a bit more filter
+  bandwidth
+ */
+uint8_t AP_InertialSensor_Backend::_default_filter(void) const
+{
+    switch (_imu.get_sample_rate()) {
+    case AP_InertialSensor::RATE_50HZ:
+        // on Rover and plane use a lower filter rate
+        return 15;
+    case AP_InertialSensor::RATE_100HZ:
+        return 30;
+    case AP_InertialSensor::RATE_200HZ:
+        return 30;
+    case AP_InertialSensor::RATE_400HZ:
+        return 30;
+    }
+    return 30;
+}
diff --git a/libraries/AP_InertialSensor/AP_InertialSensor_Backend.h b/libraries/AP_InertialSensor/AP_InertialSensor_Backend.h
new file mode 100644
index 000000000..acd6254ba
--- /dev/null
+++ b/libraries/AP_InertialSensor/AP_InertialSensor_Backend.h
@@ -0,0 +1,87 @@
+// -*- tab-width: 4; Mode: C++; c-basic-offset: 4; indent-tabs-mode: nil -*-
+/*
+   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/>.
+ */
+
+/*
+  IMU driver backend class. Each supported gyro/accel sensor type
+  needs to have an object derived from this class.
+
+  Note that drivers can implement just gyros or just accels, and can
+  also provide multiple gyro/accel instances.
+ */
+#ifndef __AP_INERTIALSENSOR_BACKEND_H__
+#define __AP_INERTIALSENSOR_BACKEND_H__
+
+class AP_InertialSensor_Backend
+{
+public:
+    AP_InertialSensor_Backend(AP_InertialSensor &imu);
+
+    // we declare a virtual destructor so that drivers can
+    // override with a custom destructor if need be.
+    virtual ~AP_InertialSensor_Backend(void) {}
+
+    /* 
+     * Update the sensor data. Called by the frontend to transfer
+     * accumulated sensor readings to the frontend structure via the
+     * _rotate_and_offset_gyro() and _rotate_and_offset_accel() functions
+     */
+    virtual bool update() = 0;
+
+    /* 
+     * return true if at least one accel sample is available in the backend
+     * since the last call to update()
+     */
+    virtual bool accel_sample_available() = 0;
+
+    /* 
+     * return true if at least one gyro sample is available in the backend
+     * since the last call to update()
+     */
+    virtual bool gyro_sample_available() = 0;
+
+    /*
+      return the product ID
+     */
+    int16_t product_id(void) const { return _product_id; }
+
+protected:
+    // access to frontend
+    AP_InertialSensor &_imu;
+
+    // rotate gyro vector and offset
+    void _rotate_and_offset_gyro(uint8_t instance, const Vector3f &gyro);
+
+    // rotate accel vector, scale and offset
+    void _rotate_and_offset_accel(uint8_t instance, const Vector3f &accel);
+
+    // set accelerometer error_count
+    void _set_accel_error_count(uint8_t instance, uint32_t error_count);
+
+    // set gyro error_count
+    void _set_gyro_error_count(uint8_t instance, uint32_t error_count);
+
+    // backend should fill in its product ID from AP_PRODUCT_ID_*
+    int16_t _product_id;
+
+    // return the default filter frequency in Hz for the sample rate
+    uint8_t _default_filter(void) const;
+
+    // note that each backend is also expected to have a static detect()
+    // function which instantiates an instance of the backend sensor
+    // driver if the sensor is available
+};
+
+#endif // __AP_INERTIALSENSOR_BACKEND_H__
diff --git a/libraries/AP_InertialSensor/AP_InertialSensor_L3GD20.cpp b/libraries/AP_InertialSensor/AP_InertialSensor_L3GD20.cpp
new file mode 100644
index 000000000..6a443398e
--- /dev/null
+++ b/libraries/AP_InertialSensor/AP_InertialSensor_L3GD20.cpp
@@ -0,0 +1,635 @@
+/// -*- tab-width: 4; Mode: C++; c-basic-offset: 4; indent-tabs-mode: nil -*-
+
+/****************************************************************************
+ *
+ *	 Coded by Víctor Mayoral Vilches <v.mayoralv@gmail.com> using 
+ *	 l3gd20.cpp <https://github.com/diydrones/PX4Firmware> from the PX4 Development Team.
+ *
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in
+ *    the documentation and/or other materials provided with the
+ *    distribution.
+ * 3. Neither the name PX4 nor the names of its contributors may be
+ *    used to endorse or promote products derived from this software
+ *    without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
+ * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
+ * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
+ * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
+ * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
+ * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
+ * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ *
+ ****************************************************************************/
+
+#include <AP_HAL.h>
+#if NOT_YET
+
+#include "AP_InertialSensor_L3GD20.h"
+
+extern const AP_HAL::HAL& hal;
+
+#if CONFIG_HAL_BOARD == HAL_BOARD_APM2
+	#define L3GD20_DRDY_PIN 70
+#elif CONFIG_HAL_BOARD == HAL_BOARD_LINUX
+	#if CONFIG_HAL_BOARD_SUBTYPE == HAL_BOARD_SUBTYPE_LINUX_ERLE || CONFIG_HAL_BOARD_SUBTYPE == HAL_BOARD_SUBTYPE_LINUX_PXF
+		#include "../AP_HAL_Linux/GPIO.h"
+		#define L3GD20_DRDY_PIN BBB_P8_34  // GYRO_DRDY
+	#endif
+#endif
+
+/* L3GD20 definitions */
+/* Orientation on board */
+#define SENSOR_BOARD_ROTATION_000_DEG	0
+#define SENSOR_BOARD_ROTATION_090_DEG	1
+#define SENSOR_BOARD_ROTATION_180_DEG	2
+#define SENSOR_BOARD_ROTATION_270_DEG	3
+
+/* SPI protocol address bits */
+#define DIR_READ				(1<<7)
+#define DIR_WRITE				(0<<7)
+#define ADDR_INCREMENT				(1<<6)
+
+/* register addresses */
+#define ADDR_WHO_AM_I			0x0F
+#define WHO_I_AM_H 				0xD7
+#define WHO_I_AM				0xD4
+
+#define ADDR_CTRL_REG1			0x20
+#define REG1_RATE_LP_MASK			0xF0 /* Mask to guard partial register update */
+/* keep lowpass low to avoid noise issues */
+#define RATE_95HZ_LP_25HZ		((0<<7) | (0<<6) | (0<<5) | (1<<4))
+#define RATE_190HZ_LP_25HZ		((0<<7) | (1<<6) | (0<<5) | (1<<4))
+#define RATE_190HZ_LP_50HZ		((0<<7) | (1<<6) | (1<<5) | (0<<4))
+#define RATE_190HZ_LP_70HZ		((0<<7) | (1<<6) | (1<<5) | (1<<4))
+#define RATE_380HZ_LP_20HZ		((1<<7) | (0<<6) | (1<<5) | (0<<4))
+#define RATE_380HZ_LP_25HZ		((1<<7) | (0<<6) | (0<<5) | (1<<4))
+#define RATE_380HZ_LP_50HZ		((1<<7) | (0<<6) | (1<<5) | (0<<4))
+#define RATE_380HZ_LP_100HZ		((1<<7) | (0<<6) | (1<<5) | (1<<4))
+#define RATE_760HZ_LP_30HZ		((1<<7) | (1<<6) | (0<<5) | (0<<4))
+#define RATE_760HZ_LP_35HZ		((1<<7) | (1<<6) | (0<<5) | (1<<4))
+#define RATE_760HZ_LP_50HZ		((1<<7) | (1<<6) | (1<<5) | (0<<4))
+#define RATE_760HZ_LP_100HZ		((1<<7) | (1<<6) | (1<<5) | (1<<4))
+
+#define ADDR_CTRL_REG2			0x21
+#define ADDR_CTRL_REG3			0x22
+#define ADDR_CTRL_REG4			0x23
+#define REG4_RANGE_MASK				0x30 /* Mask to guard partial register update */
+#define RANGE_250DPS				(0<<4)
+#define RANGE_500DPS				(1<<4)
+#define RANGE_2000DPS				(3<<4)
+
+#define ADDR_CTRL_REG5			0x24
+#define ADDR_REFERENCE			0x25
+#define ADDR_OUT_TEMP			0x26
+#define ADDR_STATUS_REG			0x27
+#define ADDR_OUT_X_L			0x28
+#define ADDR_OUT_X_H			0x29
+#define ADDR_OUT_Y_L			0x2A
+#define ADDR_OUT_Y_H			0x2B
+#define ADDR_OUT_Z_L			0x2C
+#define ADDR_OUT_Z_H			0x2D
+#define ADDR_FIFO_CTRL_REG		0x2E
+#define ADDR_FIFO_SRC_REG		0x2F
+#define ADDR_INT1_CFG			0x30
+#define ADDR_INT1_SRC			0x31
+#define ADDR_INT1_TSH_XH		0x32
+#define ADDR_INT1_TSH_XL		0x33
+#define ADDR_INT1_TSH_YH		0x34
+#define ADDR_INT1_TSH_YL		0x35
+#define ADDR_INT1_TSH_ZH		0x36
+#define ADDR_INT1_TSH_ZL		0x37
+#define ADDR_INT1_DURATION		0x38
+
+/* Internal configuration values */
+#define REG1_POWER_NORMAL			(1<<3)
+#define REG1_Z_ENABLE				(1<<2)
+#define REG1_Y_ENABLE				(1<<1)
+#define REG1_X_ENABLE				(1<<0)
+
+#define REG4_BDU				(1<<7)
+#define REG4_BLE				(1<<6)
+//#define REG4_SPI_3WIRE			(1<<0)
+
+#define REG5_FIFO_ENABLE			(1<<6)
+#define REG5_REBOOT_MEMORY			(1<<7)
+
+#define STATUS_ZYXOR				(1<<7)
+#define STATUS_ZOR				(1<<6)
+#define STATUS_YOR				(1<<5)
+#define STATUS_XOR				(1<<4)
+#define STATUS_ZYXDA				(1<<3)
+#define STATUS_ZDA				(1<<2)
+#define STATUS_YDA				(1<<1)
+#define STATUS_XDA				(1<<0)
+
+#define FIFO_CTRL_BYPASS_MODE			(0<<5)
+#define FIFO_CTRL_FIFO_MODE			(1<<5)
+#define FIFO_CTRL_STREAM_MODE			(1<<6)
+#define FIFO_CTRL_STREAM_TO_FIFO_MODE		(3<<5)
+#define FIFO_CTRL_BYPASS_TO_STREAM_MODE		(1<<7)
+
+#define L3GD20_DEFAULT_RATE			760
+#define L3GD20_DEFAULT_RANGE_DPS		2000
+#define L3GD20_DEFAULT_FILTER_FREQ		30
+
+
+// const float AP_InertialSensor_L3GD20::_gyro_scale = (0.0174532f / 16.4f);
+
+
+AP_InertialSensor_L3GD20::AP_InertialSensor_L3GD20() : 
+	AP_InertialSensor(),
+    _drdy_pin(NULL),
+    _initialised(false),
+    _L3GD20_product_id(AP_PRODUCT_ID_NONE)
+{
+}
+
+uint16_t AP_InertialSensor_L3GD20::_init_sensor( Sample_rate sample_rate )
+{
+    if (_initialised) return _L3GD20_product_id;
+    _initialised = true;
+
+    _spi = hal.spi->device(AP_HAL::SPIDevice_L3GD20);
+    _spi_sem = _spi->get_semaphore();
+
+#ifdef L3GD20_DRDY_PIN
+    _drdy_pin = hal.gpio->channel(L3GD20_DRDY_PIN);
+    _drdy_pin->mode(HAL_GPIO_INPUT);
+#endif
+
+    hal.scheduler->suspend_timer_procs();
+
+    // Test WHOAMI
+    uint8_t whoami = _register_read(ADDR_WHO_AM_I);
+    if (whoami != WHO_I_AM) {
+        // TODO: we should probably accept multiple chip
+        // revisions. This is the one on the PXF
+        hal.console->printf("L3GD20: unexpected WHOAMI 0x%x\n", (unsigned)whoami);
+        hal.scheduler->panic(PSTR("L3GD20: bad WHOAMI"));
+    }
+
+    uint8_t tries = 0;
+    do {
+        bool success = _hardware_init(sample_rate);
+        if (success) {
+            hal.scheduler->delay(5+2);
+            if (!_spi_sem->take(100)) {
+                hal.scheduler->panic(PSTR("L3GD20: Unable to get semaphore"));
+            }
+            if (_data_ready()) {
+                _spi_sem->give();
+                break;
+            } else {
+                hal.console->println_P(
+                        PSTR("L3GD20 startup failed: no data ready"));
+            }
+            _spi_sem->give();
+        }
+        if (tries++ > 5) {
+            hal.scheduler->panic(PSTR("PANIC: failed to boot L3GD20 5 times")); 
+        }
+    } while (1);
+
+    hal.scheduler->resume_timer_procs();
+    
+
+    /* read the first lot of data.
+     * _read_data_transaction requires the spi semaphore to be taken by
+     * its caller. */
+    _last_sample_time_micros = hal.scheduler->micros();
+    hal.scheduler->delay(10);
+    if (_spi_sem->take(100)) {
+        _read_data_transaction();
+        _spi_sem->give();
+    }
+
+    // start the timer process to read samples
+    hal.scheduler->register_timer_process(AP_HAL_MEMBERPROC(&AP_InertialSensor_L3GD20::_poll_data));
+
+#if L3GD20_DEBUG
+    _dump_registers();
+#endif
+    return _L3GD20_product_id;
+}
+
+/*================ AP_INERTIALSENSOR PUBLIC INTERFACE ==================== */
+
+bool AP_InertialSensor_L3GD20::wait_for_sample(uint16_t timeout_ms)
+{
+    if (_sample_available()) {
+        return true;
+    }
+    uint32_t start = hal.scheduler->millis();
+    while ((hal.scheduler->millis() - start) < timeout_ms) {
+        hal.scheduler->delay_microseconds(100);
+        if (_sample_available()) {
+            return true;
+        }
+    }
+    return false;
+}
+
+bool AP_InertialSensor_L3GD20::update( void )
+{
+    // wait for at least 1 sample
+    if (!wait_for_sample(1000)) {
+        return false;
+    }
+
+    // disable timer procs for mininum time
+    hal.scheduler->suspend_timer_procs();
+    _gyro[0]  = Vector3f(_gyro_sum.x, _gyro_sum.y, _gyro_sum.z);
+    _num_samples = _sum_count;
+    _gyro_sum.zero();
+    _sum_count = 0;
+    hal.scheduler->resume_timer_procs();
+
+    _gyro[0].rotate(_board_orientation);
+    _gyro[0] *= _gyro_scale / _num_samples;
+    _gyro[0] -= _gyro_offset[0];
+
+    // if (_last_filter_hz != _L3GD20_filter) {
+    //     if (_spi_sem->take(10)) {
+    //         _spi->set_bus_speed(AP_HAL::SPIDeviceDriver::SPI_SPEED_LOW);
+    //         _set_filter_register(_L3GD20_filter, 0);
+    //         _spi->set_bus_speed(AP_HAL::SPIDeviceDriver::SPI_SPEED_HIGH);
+    //         _error_count = 0;
+    //         _spi_sem->give();
+    //     }
+    // }
+
+    return true;
+}
+
+/*================ HARDWARE FUNCTIONS ==================== */
+
+/**
+ * Return true if the L3GD20 has new data available for reading.
+ *
+ * We use the data ready pin if it is available.  Otherwise, read the
+ * status register.
+ */
+bool AP_InertialSensor_L3GD20::_data_ready()
+{
+    if (_drdy_pin) {
+        return _drdy_pin->read() != 0;
+    }
+    // TODO: read status register
+    return false;
+}
+
+/**
+ * Timer process to poll for new data from the L3GD20.
+ */
+void AP_InertialSensor_L3GD20::_poll_data(void)
+{
+    if (hal.scheduler->in_timerprocess()) {
+        if (!_spi_sem->take_nonblocking()) {
+            /*
+              the semaphore being busy is an expected condition when the
+              mainline code is calling wait_for_sample() which will
+              grab the semaphore. We return now and rely on the mainline
+              code grabbing the latest sample.
+            */
+            return;
+        }   
+        if (_data_ready()) {
+            _last_sample_time_micros = hal.scheduler->micros();
+            _read_data_transaction(); 
+        }
+        _spi_sem->give();
+    } else {
+        /* Synchronous read - take semaphore */
+        if (_spi_sem->take(10)) {
+            if (_data_ready()) {
+                _last_sample_time_micros = hal.scheduler->micros();
+                _read_data_transaction(); 
+            }
+            _spi_sem->give();
+        } else {
+            hal.scheduler->panic(
+                PSTR("PANIC: AP_InertialSensor_L3GD20::_poll_data "
+                     "failed to take SPI semaphore synchronously"));
+        }
+    }
+}
+
+void AP_InertialSensor_L3GD20::_read_data_transaction() {
+    
+    struct {
+        uint8_t     cmd;
+        uint8_t     temp;
+        uint8_t     status;
+        int16_t     x;
+        int16_t     y;
+        int16_t     z;
+    } raw_report;
+
+    /* fetch data from the sensor */
+    memset(&raw_report, 0, sizeof(raw_report));
+    raw_report.cmd = ADDR_OUT_TEMP | DIR_READ | ADDR_INCREMENT;
+    _spi->transaction((uint8_t *)&raw_report, (uint8_t *)&raw_report, sizeof(raw_report));
+
+#if L3GD20_USE_DRDY
+        if ((raw_report.status & 0xF) != 0xF) {
+            /*
+              we waited for DRDY, but did not see DRDY on all axes
+              when we captured. That means a transfer error of some sort
+             */
+            hal.console->println_P(
+                    PSTR("L3GD20: DRDY is not on in all axes, transfer error"));
+            return;
+        }
+#endif
+    _gyro_sum.x += raw_report.x;
+    _gyro_sum.y  += raw_report.y;
+    _gyro_sum.z  -= raw_report.z;
+    _sum_count++;
+
+    if (_sum_count == 0) {
+        // rollover - v unlikely
+        _gyro_sum.zero();
+    }
+}
+
+uint8_t AP_InertialSensor_L3GD20::_register_read( uint8_t reg )
+{
+    uint8_t addr = reg | 0x80; // Set most significant bit
+
+    uint8_t tx[2];
+    uint8_t rx[2];
+
+    tx[0] = addr;
+    tx[1] = 0;
+    _spi->transaction(tx, rx, 2);
+
+    return rx[1];
+}
+
+void AP_InertialSensor_L3GD20::_register_write(uint8_t reg, uint8_t val)
+{
+    uint8_t tx[2];
+    uint8_t rx[2];
+
+    tx[0] = reg;
+    tx[1] = val;
+    _spi->transaction(tx, rx, 2);
+}
+
+/*
+  useful when debugging SPI bus errors
+ */
+void AP_InertialSensor_L3GD20::_register_write_check(uint8_t reg, uint8_t val)
+{
+    uint8_t readed;
+    _register_write(reg, val);
+    readed = _register_read(reg);
+    if (readed != val){
+	hal.console->printf_P(PSTR("Values doesn't match; written: %02x; read: %02x "), val, readed);
+    }
+#if L3GD20_DEBUG
+    hal.console->printf_P(PSTR("Values written: %02x; readed: %02x "), val, readed);
+#endif
+}
+
+/*
+  set the DLPF filter frequency. Assumes caller has taken semaphore
+  TODO needs to be changed according to L3GD20 needs
+ */
+// void AP_InertialSensor_L3GD20::_set_filter_register(uint8_t filter_hz, uint8_t default_filter)
+// {
+//     uint8_t filter = default_filter;
+//     // choose filtering frequency
+//     switch (filter_hz) {
+//     case 5:
+//         filter = BITS_DLPF_CFG_5HZ;
+//         break;
+//     case 10:
+//         filter = BITS_DLPF_CFG_10HZ;
+//         break;
+//     case 20:
+//         filter = BITS_DLPF_CFG_20HZ;
+//         break;
+//     case 42:
+//         filter = BITS_DLPF_CFG_42HZ;
+//         break;
+//     case 98:
+//         filter = BITS_DLPF_CFG_98HZ;
+//         break;
+//     }
+
+//     if (filter != 0) {
+//         _last_filter_hz = filter_hz;
+//         _register_write(MPUREG_CONFIG, filter);
+//     }
+// }
+
+
+void AP_InertialSensor_L3GD20::disable_i2c(void)
+{
+	uint8_t retries = 10;
+	while (retries--) {
+		// add retries
+		uint8_t a = _register_read(0x05);
+		_register_write(0x05, (0x20 | a));
+		if (_register_read(0x05) == (a | 0x20)) {
+			return;
+		}
+	}	
+	hal.scheduler->panic(PSTR("L3GD20: Unable to disable I2C"));
+}
+
+uint8_t AP_InertialSensor_L3GD20::set_samplerate(uint16_t frequency)
+{
+	uint8_t bits = REG1_POWER_NORMAL | REG1_Z_ENABLE | REG1_Y_ENABLE | REG1_X_ENABLE;
+	if (frequency == 0)
+		frequency = 760;
+
+	/* use limits good for H or non-H models */
+	if (frequency <= 100) {
+		// _current_rate = 95;
+		bits |= RATE_95HZ_LP_25HZ;
+
+	} else if (frequency <= 200) {
+		// _current_rate = 190;
+		bits |= RATE_190HZ_LP_50HZ;
+
+	} else if (frequency <= 400) {
+		// _current_rate = 380;
+		bits |= RATE_380HZ_LP_50HZ;
+
+	} else if (frequency <= 800) {
+		// _current_rate = 760;
+		bits |= RATE_760HZ_LP_50HZ;
+	} else {
+		return -1;
+	}
+	_register_write(ADDR_CTRL_REG1, bits);
+	return 0;
+}
+
+uint8_t AP_InertialSensor_L3GD20::set_range(uint8_t max_dps)
+{
+	uint8_t bits = REG4_BDU;
+	float new_range_scale_dps_digit;
+	float new_range;
+
+	if (max_dps == 0) {
+		max_dps = 2000;
+	}
+	if (max_dps <= 250) {
+		new_range = 250;
+		bits |= RANGE_250DPS;
+		new_range_scale_dps_digit = 8.75e-3f;
+
+	} else if (max_dps <= 500) {
+		new_range = 500;
+		bits |= RANGE_500DPS;
+		new_range_scale_dps_digit = 17.5e-3f;
+
+	} else if (max_dps <= 2000) {
+		new_range = 2000;
+		bits |= RANGE_2000DPS;
+		new_range_scale_dps_digit = 70e-3f;
+
+	} else {
+		return -1;
+	}
+
+	// _gyro_range_rad_s = new_range / 180.0f * M_PI_F;
+	// _gyro_range_scale = new_range_scale_dps_digit / 180.0f * M_PI_F;	
+	_gyro_scale = new_range_scale_dps_digit / 180.0f * M_PI_F;	    
+	_register_write(ADDR_CTRL_REG4, bits);
+	return 0;
+}
+
+bool AP_InertialSensor_L3GD20::_hardware_init(Sample_rate sample_rate)
+{
+    if (!_spi_sem->take(100)) {
+        hal.scheduler->panic(PSTR("L3GD20: Unable to get semaphore"));
+    }
+
+    // initially run the bus at low speed
+    _spi->set_bus_speed(AP_HAL::SPIDeviceDriver::SPI_SPEED_LOW);
+       
+    // ensure the chip doesn't interpret any other bus traffic as I2C
+	disable_i2c();
+
+	// Chip reset 
+	/* set default configuration */
+	_register_write(ADDR_CTRL_REG1, REG1_POWER_NORMAL | REG1_Z_ENABLE | REG1_Y_ENABLE | REG1_X_ENABLE);
+    hal.scheduler->delay(1);
+	_register_write(ADDR_CTRL_REG2, 0);		/* disable high-pass filters */
+    hal.scheduler->delay(1);
+	_register_write(ADDR_CTRL_REG3, 0x08);        /* DRDY enable */
+    hal.scheduler->delay(1);
+	_register_write(ADDR_CTRL_REG4, REG4_BDU);
+    hal.scheduler->delay(1);
+	_register_write(ADDR_CTRL_REG5, 0);
+    hal.scheduler->delay(1);
+
+	_register_write(ADDR_CTRL_REG5, REG5_FIFO_ENABLE);		/* disable wake-on-interrupt */
+    hal.scheduler->delay(1);
+
+	/* disable FIFO. This makes things simpler and ensures we
+	 * aren't getting stale data. It means we must run the hrt
+	 * callback fast enough to not miss data. */
+	_register_write(ADDR_FIFO_CTRL_REG, FIFO_CTRL_BYPASS_MODE);
+    hal.scheduler->delay(1);
+
+	set_samplerate(0); // 760Hz
+    hal.scheduler->delay(1);
+	set_range(L3GD20_DEFAULT_RANGE_DPS);	
+    hal.scheduler->delay(1);
+
+    // //TODO: Filtering
+    // uint8_t default_filter;
+
+    // // sample rate and filtering
+    // // to minimise the effects of aliasing we choose a filter
+    // // that is less than half of the sample rate
+    // switch (sample_rate) {
+    // case RATE_50HZ:
+    //     // this is used for plane and rover, where noise resistance is
+    //     // more important than update rate. Tests on an aerobatic plane
+    //     // show that 10Hz is fine, and makes it very noise resistant
+    //     default_filter = BITS_DLPF_CFG_10HZ;
+    //     _sample_shift = 2;
+    //     break;
+    // case RATE_100HZ:
+    //     default_filter = BITS_DLPF_CFG_20HZ;
+    //     _sample_shift = 1;
+    //     break;
+    // case RATE_200HZ:
+    // default:
+    //     default_filter = BITS_DLPF_CFG_20HZ;
+    //     _sample_shift = 0;
+    //     break;
+    // }
+    // _set_filter_register(_L3GD20_filter, default_filter);
+
+    // now that we have initialised, we set the SPI bus speed to high
+    _spi->set_bus_speed(AP_HAL::SPIDeviceDriver::SPI_SPEED_HIGH);
+    _spi_sem->give();
+
+    return true;
+}
+
+// return the MPU6k gyro drift rate in radian/s/s
+// note that this is much better than the oilpan gyros
+float AP_InertialSensor_L3GD20::get_gyro_drift_rate(void)
+{
+    // 0.5 degrees/second/minute
+    return ToRad(0.5/60);
+}
+
+// return true if a sample is available
+bool AP_InertialSensor_L3GD20::_sample_available()
+{
+    _poll_data();
+    // return (_sum_count >> _sample_shift) > 0;
+    return (_sum_count) > 0;    
+}
+
+
+#if L3GD20_DEBUG
+// dump all config registers - used for debug
+void AP_InertialSensor_L3GD20::_dump_registers(void)
+{
+    hal.console->println_P(PSTR("L3GD20 registers"));
+    if (_spi_sem->take(100)) {
+        for (uint8_t reg=ADDR_WHO_AM_I; reg<=56; reg++) { // 0x38 = 56
+            uint8_t v = _register_read(reg);
+            hal.console->printf_P(PSTR("%02x:%02x "), (unsigned)reg, (unsigned)v);
+            if ((reg - (ADDR_WHO_AM_I-1)) % 16 == 0) {
+                hal.console->println();
+            }
+        }
+        hal.console->println();
+        _spi_sem->give();
+    }
+}
+#endif
+
+
+// get_delta_time returns the time period in seconds overwhich the sensor data was collected
+float AP_InertialSensor_L3GD20::get_delta_time() const
+{
+    // the sensor runs at 200Hz
+    return 0.005 * _num_samples;
+}
+#endif
diff --git a/libraries/AP_InertialSensor/AP_InertialSensor_L3GD20.h b/libraries/AP_InertialSensor/AP_InertialSensor_L3GD20.h
new file mode 100644
index 000000000..ca7c50233
--- /dev/null
+++ b/libraries/AP_InertialSensor/AP_InertialSensor_L3GD20.h
@@ -0,0 +1,88 @@
+/// -*- tab-width: 4; Mode: C++; c-basic-offset: 4; indent-tabs-mode: nil -*-
+
+#ifndef __AP_INERTIAL_SENSOR_L3GD20_H__
+#define __AP_INERTIAL_SENSOR_L3GD20_H__
+
+#include <stdint.h>
+#include <AP_HAL.h>
+#include <AP_Math.h>
+#include <AP_Progmem.h>
+#include "AP_InertialSensor.h"
+
+// enable debug to see a register dump on startup
+#define L3GD20_DEBUG 0
+
+class AP_InertialSensor_L3GD20 : public AP_InertialSensor
+{
+public:
+
+    AP_InertialSensor_L3GD20();
+
+    /* Concrete implementation of AP_InertialSensor functions: */
+    bool                update();
+    float               get_gyro_drift_rate();
+
+    // wait for a sample to be available, with timeout in milliseconds
+    bool                wait_for_sample(uint16_t timeout_ms);
+
+    // get_delta_time returns the time period in seconds overwhich the sensor data was collected
+    float            	get_delta_time() const;
+
+    uint16_t error_count(void) const { return _error_count; }
+    bool healthy(void) const { return _error_count <= 4; }
+    bool get_gyro_health(uint8_t instance) const { return healthy(); }
+    bool get_accel_health(uint8_t instance) const { return healthy(); }
+
+protected:
+    uint16_t                    _init_sensor( Sample_rate sample_rate );
+
+private:
+    AP_HAL::DigitalSource *_drdy_pin;
+
+    bool                 _sample_available();
+    void                 _read_data_transaction();
+    bool                 _data_ready();
+    void                 _poll_data(void);
+    uint8_t              _register_read( uint8_t reg );
+    void                 _register_write( uint8_t reg, uint8_t val );
+    void                 _register_write_check(uint8_t reg, uint8_t val);
+    bool                 _hardware_init(Sample_rate sample_rate);
+    void                 disable_i2c(void);
+    uint8_t              set_samplerate(uint16_t frequency);
+    uint8_t              set_range(uint8_t max_dps);
+
+    AP_HAL::SPIDeviceDriver *_spi;
+    AP_HAL::Semaphore *_spi_sem;
+
+    uint16_t					_num_samples;
+    float          _gyro_scale;
+
+    uint32_t _last_sample_time_micros;
+
+    // ensure we can't initialise twice
+    bool                        _initialised;
+    int16_t              _L3GD20_product_id;
+
+    // how many hardware samples before we report a sample to the caller
+    uint8_t _sample_shift;
+
+    // support for updating filter at runtime
+    uint8_t _last_filter_hz;
+
+    void _set_filter_register(uint8_t filter_hz, uint8_t default_filter);
+
+    uint16_t _error_count;
+
+    // accumulation in timer - must be read with timer disabled
+    // the sum of the values since last read
+    Vector3l _gyro_sum;
+    volatile int16_t _sum_count;
+
+public:
+
+#if L3GD20_DEBUG
+    void						_dump_registers(void);
+#endif
+};
+
+#endif // __AP_INERTIAL_SENSOR_L3GD20_H__
diff --git a/libraries/AP_InertialSensor/AP_InertialSensor_LSM303D.cpp b/libraries/AP_InertialSensor/AP_InertialSensor_LSM303D.cpp
new file mode 100644
index 000000000..b6c6b7d34
--- /dev/null
+++ b/libraries/AP_InertialSensor/AP_InertialSensor_LSM303D.cpp
@@ -0,0 +1,831 @@
+/// -*- tab-width: 4; Mode: C++; c-basic-offset: 4; indent-tabs-mode: nil -*-
+
+#if NOT_YET
+
+/****************************************************************************
+ *
+ *	 Coded by Víctor Mayoral Vilches <v.mayoralv@gmail.com> using 
+ *	 lsm3030d.cpp <https://github.com/diydrones/PX4Firmware> from the PX4 Development Team.
+ *
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in
+ *    the documentation and/or other materials provided with the
+ *    distribution.
+ * 3. Neither the name PX4 nor the names of its contributors may be
+ *    used to endorse or promote products derived from this software
+ *    without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
+ * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
+ * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
+ * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
+ * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
+ * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
+ * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ *
+ ****************************************************************************/
+
+#include <AP_HAL.h>
+#include "AP_InertialSensor_LSM303D.h"
+
+extern const AP_HAL::HAL& hal;
+
+#if CONFIG_HAL_BOARD == HAL_BOARD_APM2
+	#define LSM303D_DRDY_PIN 70
+#elif CONFIG_HAL_BOARD == HAL_BOARD_LINUX
+	#if CONFIG_HAL_BOARD_SUBTYPE == HAL_BOARD_SUBTYPE_LINUX_ERLE || CONFIG_HAL_BOARD_SUBTYPE == HAL_BOARD_SUBTYPE_LINUX_PXF
+		#include "../AP_HAL_Linux/GPIO.h"
+		#define LSM303D_DRDY_X_PIN BBB_P8_8  // ACCEL DRDY
+        #define LSM303D_DRDY_M_PIN BBB_P8_10  // MAGNETOMETER DRDY
+	#endif
+#endif
+
+/* SPI protocol address bits */
+#define DIR_READ                (1<<7)
+#define DIR_WRITE               (0<<7)
+#define ADDR_INCREMENT          (1<<6)
+
+/* register addresses: A: accel, M: mag, T: temp */
+#define ADDR_WHO_AM_I           0x0F
+#define WHO_I_AM            0x49
+
+#define ADDR_OUT_TEMP_L         0x05
+#define ADDR_OUT_TEMP_H         0x06
+#define ADDR_STATUS_M           0x07
+#define ADDR_OUT_X_L_M              0x08
+#define ADDR_OUT_X_H_M              0x09
+#define ADDR_OUT_Y_L_M              0x0A
+#define ADDR_OUT_Y_H_M          0x0B
+#define ADDR_OUT_Z_L_M          0x0C
+#define ADDR_OUT_Z_H_M          0x0D
+
+#define ADDR_INT_CTRL_M         0x12
+#define ADDR_INT_SRC_M          0x13
+#define ADDR_REFERENCE_X        0x1c
+#define ADDR_REFERENCE_Y        0x1d
+#define ADDR_REFERENCE_Z        0x1e
+
+#define ADDR_STATUS_A           0x27
+#define ADDR_OUT_X_L_A          0x28
+#define ADDR_OUT_X_H_A          0x29
+#define ADDR_OUT_Y_L_A          0x2A
+#define ADDR_OUT_Y_H_A          0x2B
+#define ADDR_OUT_Z_L_A          0x2C
+#define ADDR_OUT_Z_H_A          0x2D
+
+#define ADDR_CTRL_REG0          0x1F
+#define ADDR_CTRL_REG1          0x20
+#define ADDR_CTRL_REG2          0x21
+#define ADDR_CTRL_REG3          0x22
+#define ADDR_CTRL_REG4          0x23
+#define ADDR_CTRL_REG5          0x24
+#define ADDR_CTRL_REG6          0x25
+#define ADDR_CTRL_REG7          0x26
+
+#define ADDR_FIFO_CTRL          0x2e
+#define ADDR_FIFO_SRC           0x2f
+
+#define ADDR_IG_CFG1            0x30
+#define ADDR_IG_SRC1            0x31
+#define ADDR_IG_THS1            0x32
+#define ADDR_IG_DUR1            0x33
+#define ADDR_IG_CFG2            0x34
+#define ADDR_IG_SRC2            0x35
+#define ADDR_IG_THS2            0x36
+#define ADDR_IG_DUR2            0x37
+#define ADDR_CLICK_CFG          0x38
+#define ADDR_CLICK_SRC          0x39
+#define ADDR_CLICK_THS          0x3a
+#define ADDR_TIME_LIMIT         0x3b
+#define ADDR_TIME_LATENCY       0x3c
+#define ADDR_TIME_WINDOW        0x3d
+#define ADDR_ACT_THS            0x3e
+#define ADDR_ACT_DUR            0x3f
+
+#define REG1_RATE_BITS_A        ((1<<7) | (1<<6) | (1<<5) | (1<<4))
+#define REG1_POWERDOWN_A        ((0<<7) | (0<<6) | (0<<5) | (0<<4))
+#define REG1_RATE_3_125HZ_A     ((0<<7) | (0<<6) | (0<<5) | (1<<4))
+#define REG1_RATE_6_25HZ_A      ((0<<7) | (0<<6) | (1<<5) | (0<<4))
+#define REG1_RATE_12_5HZ_A      ((0<<7) | (0<<6) | (1<<5) | (1<<4))
+#define REG1_RATE_25HZ_A        ((0<<7) | (1<<6) | (0<<5) | (0<<4))
+#define REG1_RATE_50HZ_A        ((0<<7) | (1<<6) | (0<<5) | (1<<4))
+#define REG1_RATE_100HZ_A       ((0<<7) | (1<<6) | (1<<5) | (0<<4))
+#define REG1_RATE_200HZ_A       ((0<<7) | (1<<6) | (1<<5) | (1<<4))
+#define REG1_RATE_400HZ_A       ((1<<7) | (0<<6) | (0<<5) | (0<<4))
+#define REG1_RATE_800HZ_A       ((1<<7) | (0<<6) | (0<<5) | (1<<4))
+#define REG1_RATE_1600HZ_A      ((1<<7) | (0<<6) | (1<<5) | (0<<4))
+
+#define REG1_BDU_UPDATE         (1<<3)
+#define REG1_Z_ENABLE_A         (1<<2)
+#define REG1_Y_ENABLE_A         (1<<1)
+#define REG1_X_ENABLE_A         (1<<0)
+
+#define REG2_ANTIALIAS_FILTER_BW_BITS_A ((1<<7) | (1<<6))
+#define REG2_AA_FILTER_BW_773HZ_A       ((0<<7) | (0<<6))
+#define REG2_AA_FILTER_BW_194HZ_A       ((0<<7) | (1<<6))
+#define REG2_AA_FILTER_BW_362HZ_A       ((1<<7) | (0<<6))
+#define REG2_AA_FILTER_BW_50HZ_A        ((1<<7) | (1<<6))
+
+#define REG2_FULL_SCALE_BITS_A  ((1<<5) | (1<<4) | (1<<3))
+#define REG2_FULL_SCALE_2G_A    ((0<<5) | (0<<4) | (0<<3))
+#define REG2_FULL_SCALE_4G_A    ((0<<5) | (0<<4) | (1<<3))
+#define REG2_FULL_SCALE_6G_A    ((0<<5) | (1<<4) | (0<<3))
+#define REG2_FULL_SCALE_8G_A    ((0<<5) | (1<<4) | (1<<3))
+#define REG2_FULL_SCALE_16G_A   ((1<<5) | (0<<4) | (0<<3))
+
+#define REG5_ENABLE_T           (1<<7)
+
+#define REG5_RES_HIGH_M         ((1<<6) | (1<<5))
+#define REG5_RES_LOW_M          ((0<<6) | (0<<5))
+
+#define REG5_RATE_BITS_M        ((1<<4) | (1<<3) | (1<<2))
+#define REG5_RATE_3_125HZ_M     ((0<<4) | (0<<3) | (0<<2))
+#define REG5_RATE_6_25HZ_M      ((0<<4) | (0<<3) | (1<<2))
+#define REG5_RATE_12_5HZ_M      ((0<<4) | (1<<3) | (0<<2))
+#define REG5_RATE_25HZ_M        ((0<<4) | (1<<3) | (1<<2))
+#define REG5_RATE_50HZ_M        ((1<<4) | (0<<3) | (0<<2))
+#define REG5_RATE_100HZ_M       ((1<<4) | (0<<3) | (1<<2))
+#define REG5_RATE_DO_NOT_USE_M  ((1<<4) | (1<<3) | (0<<2))
+
+#define REG6_FULL_SCALE_BITS_M  ((1<<6) | (1<<5))
+#define REG6_FULL_SCALE_2GA_M   ((0<<6) | (0<<5))
+#define REG6_FULL_SCALE_4GA_M   ((0<<6) | (1<<5))
+#define REG6_FULL_SCALE_8GA_M   ((1<<6) | (0<<5))
+#define REG6_FULL_SCALE_12GA_M  ((1<<6) | (1<<5))
+
+#define REG7_CONT_MODE_M        ((0<<1) | (0<<0))
+
+
+#define INT_CTRL_M              0x12
+#define INT_SRC_M               0x13
+
+/* default values for this device */
+#define LSM303D_ACCEL_DEFAULT_RANGE_G           8
+#define LSM303D_ACCEL_DEFAULT_RATE          800
+#define LSM303D_ACCEL_DEFAULT_ONCHIP_FILTER_FREQ    50
+#define LSM303D_ACCEL_DEFAULT_DRIVER_FILTER_FREQ    30
+
+#define LSM303D_MAG_DEFAULT_RANGE_GA            2
+#define LSM303D_MAG_DEFAULT_RATE            100
+
+#define LSM303D_ONE_G                   9.80665f
+
+
+AP_InertialSensor_LSM303D::AP_InertialSensor_LSM303D() : 
+    AP_InertialSensor(),
+    _drdy_pin_x(NULL),
+    _drdy_pin_m(NULL),
+    _initialised(false),
+    _LSM303D_product_id(AP_PRODUCT_ID_NONE)
+{
+}
+
+uint16_t AP_InertialSensor_LSM303D::_init_sensor( Sample_rate sample_rate )
+{
+    if (_initialised) return _LSM303D_product_id;
+    _initialised = true;
+
+    _spi = hal.spi->device(AP_HAL::SPIDevice_LSM303D);
+    _spi_sem = _spi->get_semaphore();
+
+// This device has mag and accel
+#ifdef LSM303D_DRDY_X_PIN
+    _drdy_pin_x = hal.gpio->channel(LSM303D_DRDY_X_PIN);
+    _drdy_pin_x->mode(HAL_GPIO_INPUT);
+#endif
+
+#ifdef LSM303D_DRDY_M_PIN
+    _drdy_pin_m = hal.gpio->channel(LSM303D_DRDY_M_PIN);
+    _drdy_pin_m->mode(HAL_GPIO_INPUT);
+#endif
+
+    hal.scheduler->suspend_timer_procs();
+
+    // Test WHOAMI
+    uint8_t whoami = _register_read(ADDR_WHO_AM_I);
+    if (whoami != WHO_I_AM) {
+        // TODO: we should probably accept multiple chip
+        // revisions. This is the one on the PXF
+        hal.console->printf("LSM303D: unexpected WHOAMI 0x%x\n", (unsigned)whoami);
+        hal.scheduler->panic(PSTR("LSM303D: bad WHOAMI"));
+    }
+
+    uint8_t tries = 0;
+    do {
+        bool success = _hardware_init(sample_rate);
+        if (success) {
+            hal.scheduler->delay(5+2);
+            if (!_spi_sem->take(100)) {
+                hal.scheduler->panic(PSTR("LSM303D: Unable to get semaphore"));
+            }
+            if (_data_ready()) {
+                _spi_sem->give();
+                break;
+            } else {
+                hal.console->println_P(
+                        PSTR("LSM303D startup failed: no data ready"));
+            }
+            _spi_sem->give();
+        }
+        if (tries++ > 5) {
+            hal.scheduler->panic(PSTR("PANIC: failed to boot LSM303D 5 times")); 
+        }
+    } while (1);
+
+    hal.scheduler->resume_timer_procs();
+    
+
+    /* read the first lot of data.
+     * _read_data_transaction requires the spi semaphore to be taken by
+     * its caller. */
+    _last_sample_time_micros = hal.scheduler->micros();
+    hal.scheduler->delay(10);
+    if (_spi_sem->take(100)) {
+        _read_data_transaction();
+        _spi_sem->give();
+    }
+
+    // start the timer process to read samples
+    hal.scheduler->register_timer_process(AP_HAL_MEMBERPROC(&AP_InertialSensor_LSM303D::_poll_data));
+
+#if LSM303D_DEBUG
+    _dump_registers();
+#endif
+    return _LSM303D_product_id;
+}
+
+/*================ AP_INERTIALSENSOR PUBLIC INTERFACE ==================== */
+
+bool AP_InertialSensor_LSM303D::wait_for_sample(uint16_t timeout_ms)
+{
+    if (_sample_available()) {
+        return true;
+    }
+    uint32_t start = hal.scheduler->millis();
+    while ((hal.scheduler->millis() - start) < timeout_ms) {
+        hal.scheduler->delay_microseconds(100);
+        if (_sample_available()) {
+            return true;
+        }
+    }
+    return false;
+}
+
+bool AP_InertialSensor_LSM303D::update( void )
+{
+    // wait for at least 1 sample
+    if (!wait_for_sample(1000)) {
+        return false;
+    }
+
+    // disable timer procs for mininum time
+    hal.scheduler->suspend_timer_procs();
+
+    _accel[0]  = Vector3f(_accel_sum.x, _accel_sum.y, _accel_sum.z);
+    // _mag[0]  = Vector3f(_mag_sum.x, _mag_sum.y, _mag_sum.z);
+
+    _num_samples = _sum_count;
+    _accel_sum.zero();
+    _mag_sum.zero();
+    _sum_count = 0;
+    hal.scheduler->resume_timer_procs();
+
+    _accel[0].rotate(_board_orientation);
+    // TODO change this for the corresponding value
+    // _accel[0] *= MPU6000_ACCEL_SCALE_1G / _num_samples;
+
+    // Vector3f accel_scale = _accel_scale[0].get();
+    // _accel[0].x *= accel_scale.x;
+    // _accel[0].y *= accel_scale.y;
+    // _accel[0].z *= accel_scale.z;
+    // _accel[0] -= _accel_offset[0];
+
+    // TODO similarly put mag values in _mag and scale them
+
+    // if (_last_filter_hz != _LSM303D_filter) {
+    //     if (_spi_sem->take(10)) {
+    //         _spi->set_bus_speed(AP_HAL::SPIDeviceDriver::SPI_SPEED_LOW);
+    //         _set_filter_register(_LSM303D_filter, 0);
+    //         _spi->set_bus_speed(AP_HAL::SPIDeviceDriver::SPI_SPEED_HIGH);
+    //         _error_count = 0;
+    //         _spi_sem->give();
+    //     }
+    // }
+
+    return true;
+}
+
+/*================ HARDWARE FUNCTIONS ==================== */
+
+/**
+ * Return true if the LSM303D has new data available for both the mag and the accels.
+ *
+ * We use the data ready pin if it is available.  Otherwise, read the
+ * status register.
+ */
+bool AP_InertialSensor_LSM303D::_data_ready()
+{
+    if (_drdy_pin_m && _drdy_pin_x) {
+        return (_drdy_pin_m->read() && _drdy_pin_x->read()) != 0;
+    }
+    // TODO: read status register
+    return false;
+}
+
+/**
+ * Timer process to poll for new data from the LSM303D.
+ */
+void AP_InertialSensor_LSM303D::_poll_data(void)
+{
+    if (hal.scheduler->in_timerprocess()) {
+        if (!_spi_sem->take_nonblocking()) {
+            /*
+              the semaphore being busy is an expected condition when the
+              mainline code is calling wait_for_sample() which will
+              grab the semaphore. We return now and rely on the mainline
+              code grabbing the latest sample.
+            */
+            return;
+        }   
+        if (_data_ready()) {
+            _last_sample_time_micros = hal.scheduler->micros();
+            _read_data_transaction(); 
+        }
+        _spi_sem->give();
+    } else {
+        /* Synchronous read - take semaphore */
+        if (_spi_sem->take(10)) {
+            if (_data_ready()) {
+                _last_sample_time_micros = hal.scheduler->micros();
+                _read_data_transaction(); 
+            }
+            _spi_sem->give();
+        } else {
+            hal.scheduler->panic(
+                PSTR("PANIC: AP_InertialSensor_LSM303D::_poll_data "
+                     "failed to take SPI semaphore synchronously"));
+        }
+    }
+}
+
+void AP_InertialSensor_LSM303D::_read_data_transaction_accel() 
+{
+
+    if (_register_read(ADDR_CTRL_REG1) != _reg1_expected) {
+            hal.console->println_P(
+                        PSTR("LSM303D _read_data_transaction_accel: _reg1_expected unexpected"));
+        // reset();
+        return;
+    }
+
+    struct {
+        uint8_t     cmd;
+        uint8_t     status;
+        int16_t     x;
+        int16_t     y;
+        int16_t     z;
+    } raw_accel_report;
+
+    /* fetch data from the sensor */
+    memset(&raw_accel_report, 0, sizeof(raw_accel_report));
+    raw_accel_report.cmd = ADDR_STATUS_A | DIR_READ | ADDR_INCREMENT;
+    _spi->transaction((uint8_t *)&raw_accel_report, (uint8_t *)&raw_accel_report, sizeof(raw_accel_report));
+
+    _accel_sum.x  += raw_accel_report.x;
+    _accel_sum.y  += raw_accel_report.y;
+    _accel_sum.z  += raw_accel_report.z;
+}
+
+void AP_InertialSensor_LSM303D::_read_data_transaction_mag() {
+    if (_register_read(ADDR_CTRL_REG7) != _reg7_expected) {
+            hal.console->println_P(
+                        PSTR("LSM303D _read_data_transaction_accel: _reg7_expected unexpected"));
+        // reset();
+        return;
+    }
+
+    struct {
+        uint8_t     cmd;
+        uint8_t     status;
+        int16_t     x;
+        int16_t     y;
+        int16_t     z;
+    } raw_mag_report;
+
+    /* fetch data from the sensor */
+    memset(&raw_mag_report, 0, sizeof(raw_mag_report));
+    raw_mag_report.cmd = ADDR_STATUS_M | DIR_READ | ADDR_INCREMENT;
+    _spi->transaction((uint8_t *)&raw_mag_report, (uint8_t *)&raw_mag_report, sizeof(raw_mag_report));
+
+    _mag_sum.x = raw_mag_report.x;
+    _mag_sum.y = raw_mag_report.y;
+    _mag_sum.z = raw_mag_report.z;
+}
+
+void AP_InertialSensor_LSM303D::_read_data_transaction() {
+    
+    _read_data_transaction_accel();
+    _read_data_transaction_mag();
+    _sum_count++;
+
+    if (_sum_count == 0) {
+        // rollover - v unlikely
+        _accel_sum.zero();
+        _mag_sum.zero();
+    }
+}
+
+uint8_t AP_InertialSensor_LSM303D::_register_read( uint8_t reg )
+{
+    uint8_t addr = reg | 0x80; // Set most significant bit
+
+    uint8_t tx[2];
+    uint8_t rx[2];
+
+    tx[0] = addr;
+    tx[1] = 0;
+    _spi->transaction(tx, rx, 2);
+
+    return rx[1];
+}
+
+void AP_InertialSensor_LSM303D::_register_write(uint8_t reg, uint8_t val)
+{
+    uint8_t tx[2];
+    uint8_t rx[2];
+
+    tx[0] = reg;
+    tx[1] = val;
+    _spi->transaction(tx, rx, 2);
+}
+
+/*
+  useful when debugging SPI bus errors
+ */
+void AP_InertialSensor_LSM303D::_register_write_check(uint8_t reg, uint8_t val)
+{
+    uint8_t readed;
+    _register_write(reg, val);
+    readed = _register_read(reg);
+    if (readed != val){
+	hal.console->printf_P(PSTR("Values doesn't match; written: %02x; read: %02x "), val, readed);
+    }
+#if LSM303D_DEBUG
+    hal.console->printf_P(PSTR("Values written: %02x; readed: %02x "), val, readed);
+#endif
+}
+
+void AP_InertialSensor_LSM303D::_register_modify(uint8_t reg, uint8_t clearbits, uint8_t setbits)
+{
+    uint8_t val;
+
+    val = _register_read(reg);
+    val &= ~clearbits;
+    val |= setbits;
+    _register_write(reg, val);
+}
+
+
+/*
+  set the DLPF filter frequency. Assumes caller has taken semaphore
+  TODO needs to be changed according to LSM303D needs
+ */
+// void AP_InertialSensor_LSM303D::_set_filter_register(uint8_t filter_hz, uint8_t default_filter)
+// {
+//     uint8_t filter = default_filter;
+//     // choose filtering frequency
+//     switch (filter_hz) {
+//     case 5:
+//         filter = BITS_DLPF_CFG_5HZ;
+//         break;
+//     case 10:
+//         filter = BITS_DLPF_CFG_10HZ;
+//         break;
+//     case 20:
+//         filter = BITS_DLPF_CFG_20HZ;
+//         break;
+//     case 42:
+//         filter = BITS_DLPF_CFG_42HZ;
+//         break;
+//     case 98:
+//         filter = BITS_DLPF_CFG_98HZ;
+//         break;
+//     }
+
+//     if (filter != 0) {
+//         _last_filter_hz = filter_hz;
+//         _register_write(MPUREG_CONFIG, filter);
+//     }
+// }
+
+void AP_InertialSensor_LSM303D::disable_i2c(void)
+{
+    uint8_t a = _register_read(0x02);
+    _register_write(0x02, (0x10 | a));
+    a = _register_read(0x02);
+    _register_write(0x02, (0xF7 & a));
+    a = _register_read(0x15);
+    _register_write(0x15, (0x80 | a));
+    a = _register_read(0x02);
+    _register_write(0x02, (0xE7 & a));
+}
+
+uint8_t AP_InertialSensor_LSM303D::accel_set_range(uint8_t max_g)
+{
+    uint8_t setbits = 0;
+    uint8_t clearbits = REG2_FULL_SCALE_BITS_A;
+    float new_scale_g_digit = 0.0f;
+
+    if (max_g == 0)
+        max_g = 16;
+
+    if (max_g <= 2) {
+        _accel_range_m_s2 = 2.0f*LSM303D_ONE_G;
+        setbits |= REG2_FULL_SCALE_2G_A;
+        new_scale_g_digit = 0.061e-3f;
+
+    } else if (max_g <= 4) {
+        _accel_range_m_s2 = 4.0f*LSM303D_ONE_G;
+        setbits |= REG2_FULL_SCALE_4G_A;
+        new_scale_g_digit = 0.122e-3f;
+
+    } else if (max_g <= 6) {
+        _accel_range_m_s2 = 6.0f*LSM303D_ONE_G;
+        setbits |= REG2_FULL_SCALE_6G_A;
+        new_scale_g_digit = 0.183e-3f;
+
+    } else if (max_g <= 8) {
+        _accel_range_m_s2 = 8.0f*LSM303D_ONE_G;
+        setbits |= REG2_FULL_SCALE_8G_A;
+        new_scale_g_digit = 0.244e-3f;
+
+    } else if (max_g <= 16) {
+        _accel_range_m_s2 = 16.0f*LSM303D_ONE_G;
+        setbits |= REG2_FULL_SCALE_16G_A;
+        new_scale_g_digit = 0.732e-3f;
+
+    } else {
+        return -1;
+    }
+
+    _accel_range_scale = new_scale_g_digit * LSM303D_ONE_G;
+    _register_modify(ADDR_CTRL_REG2, clearbits, setbits);
+    return 0;
+}
+
+uint8_t AP_InertialSensor_LSM303D::accel_set_samplerate(uint16_t frequency)
+{
+    uint8_t setbits = 0;
+    uint8_t clearbits = REG1_RATE_BITS_A;
+
+    if (frequency == 0)
+        frequency = 1600;
+
+    if (frequency <= 100) {
+        setbits |= REG1_RATE_100HZ_A;
+        _accel_samplerate = 100;
+
+    } else if (frequency <= 200) {
+        setbits |= REG1_RATE_200HZ_A;
+        _accel_samplerate = 200;
+
+    } else if (frequency <= 400) {
+        setbits |= REG1_RATE_400HZ_A;
+        _accel_samplerate = 400;
+
+    } else if (frequency <= 800) {
+        setbits |= REG1_RATE_800HZ_A;
+        _accel_samplerate = 800;
+
+    } else if (frequency <= 1600) {
+        setbits |= REG1_RATE_1600HZ_A;
+        _accel_samplerate = 1600;
+
+    } else {
+        return -1;
+    }
+
+    _register_modify(ADDR_CTRL_REG1, clearbits, setbits);
+    _reg1_expected = (_reg1_expected & ~clearbits) | setbits;
+    return 0;
+}
+
+uint8_t AP_InertialSensor_LSM303D::accel_set_onchip_lowpass_filter_bandwidth(uint8_t bandwidth)
+{
+    uint8_t setbits = 0;
+    uint8_t clearbits = REG2_ANTIALIAS_FILTER_BW_BITS_A;
+
+    if (bandwidth == 0)
+        bandwidth = 773;
+
+    if (bandwidth <= 50) {
+        setbits |= REG2_AA_FILTER_BW_50HZ_A;
+        _accel_onchip_filter_bandwith = 50;
+
+    } else if (bandwidth <= 194) {
+        setbits |= REG2_AA_FILTER_BW_194HZ_A;
+        _accel_onchip_filter_bandwith = 194;
+
+    } else if (bandwidth <= 362) {
+        setbits |= REG2_AA_FILTER_BW_362HZ_A;
+        _accel_onchip_filter_bandwith = 362;
+
+    } else if (bandwidth <= 773) {
+        setbits |= REG2_AA_FILTER_BW_773HZ_A;
+        _accel_onchip_filter_bandwith = 773;
+
+    } else {
+        return -1;
+    }
+
+    _register_modify(ADDR_CTRL_REG2, clearbits, setbits);
+    return 0;
+}
+
+uint8_t AP_InertialSensor_LSM303D::mag_set_range(uint8_t max_ga)
+{
+    uint8_t setbits = 0;
+    uint8_t clearbits = REG6_FULL_SCALE_BITS_M;
+    float new_scale_ga_digit = 0.0f;
+
+    if (max_ga == 0)
+        max_ga = 12;
+
+    if (max_ga <= 2) {
+        _mag_range_ga = 2;
+        setbits |= REG6_FULL_SCALE_2GA_M;
+        new_scale_ga_digit = 0.080e-3f;
+
+    } else if (max_ga <= 4) {
+        _mag_range_ga = 4;
+        setbits |= REG6_FULL_SCALE_4GA_M;
+        new_scale_ga_digit = 0.160e-3f;
+
+    } else if (max_ga <= 8) {
+        _mag_range_ga = 8;
+        setbits |= REG6_FULL_SCALE_8GA_M;
+        new_scale_ga_digit = 0.320e-3f;
+
+    } else if (max_ga <= 12) {
+        _mag_range_ga = 12;
+        setbits |= REG6_FULL_SCALE_12GA_M;
+        new_scale_ga_digit = 0.479e-3f;
+
+    } else {
+        return -1;
+    }
+
+    _mag_range_scale = new_scale_ga_digit;
+    _register_modify(ADDR_CTRL_REG6, clearbits, setbits);
+    return 0;
+}
+
+uint8_t AP_InertialSensor_LSM303D::mag_set_samplerate(uint16_t frequency)
+{
+    uint8_t setbits = 0;
+    uint8_t clearbits = REG5_RATE_BITS_M;
+
+    if (frequency == 0)
+        frequency = 100;
+
+    if (frequency <= 25) {
+        setbits |= REG5_RATE_25HZ_M;
+        _mag_samplerate = 25;
+
+    } else if (frequency <= 50) {
+        setbits |= REG5_RATE_50HZ_M;
+        _mag_samplerate = 50;
+
+    } else if (frequency <= 100) {
+        setbits |= REG5_RATE_100HZ_M;
+        _mag_samplerate = 100;
+
+    } else {
+        return -1;
+    }
+
+    _register_modify(ADDR_CTRL_REG5, clearbits, setbits);
+    return 0;
+}
+
+bool AP_InertialSensor_LSM303D::_hardware_init(Sample_rate sample_rate)
+{
+    if (!_spi_sem->take(100)) {
+        hal.scheduler->panic(PSTR("LSM303D: Unable to get semaphore"));
+    }
+
+    // initially run the bus at low speed
+    _spi->set_bus_speed(AP_HAL::SPIDeviceDriver::SPI_SPEED_LOW);
+       
+    // ensure the chip doesn't interpret any other bus traffic as I2C
+	disable_i2c();
+
+
+    /* enable accel*/
+    _reg1_expected = REG1_X_ENABLE_A | REG1_Y_ENABLE_A | REG1_Z_ENABLE_A | REG1_BDU_UPDATE | REG1_RATE_800HZ_A;
+    _register_write(ADDR_CTRL_REG1, _reg1_expected);
+
+    /* enable mag */
+    _reg7_expected = REG7_CONT_MODE_M;
+    _register_write(ADDR_CTRL_REG7, _reg7_expected);
+    _register_write(ADDR_CTRL_REG5, REG5_RES_HIGH_M);
+    _register_write(ADDR_CTRL_REG3, 0x04); // DRDY on ACCEL on INT1
+    _register_write(ADDR_CTRL_REG4, 0x04); // DRDY on MAG on INT2
+
+    accel_set_range(LSM303D_ACCEL_DEFAULT_RANGE_G);
+    accel_set_samplerate(LSM303D_ACCEL_DEFAULT_RATE);
+
+    // Hardware filtering
+    // we setup the anti-alias on-chip filter as 50Hz. We believe
+    // this operates in the analog domain, and is critical for
+    // anti-aliasing. The 2 pole software filter is designed to
+    // operate in conjunction with this on-chip filter
+    accel_set_onchip_lowpass_filter_bandwidth(LSM303D_ACCEL_DEFAULT_ONCHIP_FILTER_FREQ);
+
+    mag_set_range(LSM303D_MAG_DEFAULT_RANGE_GA);
+    mag_set_samplerate(LSM303D_MAG_DEFAULT_RATE);
+
+    // TODO: Software filtering
+    // accel_set_driver_lowpass_filter((float)LSM303D_ACCEL_DEFAULT_RATE, (float)LSM303D_ACCEL_DEFAULT_DRIVER_FILTER_FREQ);
+
+    // uint8_t default_filter;
+
+    // // sample rate and filtering
+    // // to minimise the effects of aliasing we choose a filter
+    // // that is less than half of the sample rate
+    // switch (sample_rate) {
+    // case RATE_50HZ:
+    //     // this is used for plane and rover, where noise resistance is
+    //     // more important than update rate. Tests on an aerobatic plane
+    //     // show that 10Hz is fine, and makes it very noise resistant
+    //     default_filter = BITS_DLPF_CFG_10HZ;
+    //     _sample_shift = 2;
+    //     break;
+    // case RATE_100HZ:
+    //     default_filter = BITS_DLPF_CFG_20HZ;
+    //     _sample_shift = 1;
+    //     break;
+    // case RATE_200HZ:
+    // default:
+    //     default_filter = BITS_DLPF_CFG_20HZ;
+    //     _sample_shift = 0;
+    //     break;
+    // }
+    // _set_filter_register(_LSM303D_filter, default_filter);
+
+    // now that we have initialised, we set the SPI bus speed to high
+    _spi->set_bus_speed(AP_HAL::SPIDeviceDriver::SPI_SPEED_HIGH);
+    _spi_sem->give();
+
+    return true;
+}
+
+// return true if a sample is available
+bool AP_InertialSensor_LSM303D::_sample_available()
+{
+    _poll_data();
+    // return (_sum_count >> _sample_shift) > 0;
+    return (_sum_count) > 0;    
+}
+
+
+// TODO fix dump registers
+#if LSM303D_DEBUG
+// dump all config registers - used for debug
+void AP_InertialSensor_LSM303D::_dump_registers(void)
+{
+    hal.console->println_P(PSTR("LSM303D registers"));
+    if (_spi_sem->take(100)) {
+        for (uint8_t reg=ADDR_WHO_AM_I; reg<=56; reg++) { // 0x38 = 56
+            uint8_t v = _register_read(reg);
+            hal.console->printf_P(PSTR("%02x:%02x "), (unsigned)reg, (unsigned)v);
+            if ((reg - (ADDR_WHO_AM_I-1)) % 16 == 0) {
+                hal.console->println();
+            }
+        }
+        hal.console->println();
+        _spi_sem->give();
+    }
+}
+#endif
+
+
+// get_delta_time returns the time period in seconds overwhich the sensor data was collected
+float AP_InertialSensor_LSM303D::get_delta_time() const
+{
+    // the sensor runs at 200Hz
+    return 0.005 * _num_samples;
+}
+#endif
diff --git a/libraries/AP_InertialSensor/AP_InertialSensor_LSM303D.h b/libraries/AP_InertialSensor/AP_InertialSensor_LSM303D.h
new file mode 100644
index 000000000..a2c6fa2d1
--- /dev/null
+++ b/libraries/AP_InertialSensor/AP_InertialSensor_LSM303D.h
@@ -0,0 +1,107 @@
+/// -*- tab-width: 4; Mode: C++; c-basic-offset: 4; indent-tabs-mode: nil -*-
+
+#ifndef __AP_INERTIAL_SENSOR_LSM303D_H__
+#define __AP_INERTIAL_SENSOR_LSM303D_H__
+
+#include <stdint.h>
+#include <AP_HAL.h>
+#include <AP_Math.h>
+#include <AP_Progmem.h>
+#include "AP_InertialSensor.h"
+
+// enable debug to see a register dump on startup
+#define LSM303D_DEBUG 0
+
+class AP_InertialSensor_LSM303D: public AP_InertialSensor
+{
+public:
+
+    AP_InertialSensor_LSM303D();
+
+    /* Concrete implementation of AP_InertialSensor functions: */
+    bool                update();
+
+    // wait for a sample to be available, with timeout in milliseconds
+    bool                wait_for_sample(uint16_t timeout_ms);
+
+    // get_delta_time returns the time period in seconds overwhich the sensor data was collected
+    float            	get_delta_time() const;
+
+    uint16_t error_count(void) const { return _error_count; }
+    bool healthy(void) const { return _error_count <= 4; }
+    bool get_accel_health(uint8_t instance) const { return healthy(); }
+
+protected:
+    uint16_t                    _init_sensor( Sample_rate sample_rate );
+
+private:
+    AP_HAL::DigitalSource *_drdy_pin_x;
+    AP_HAL::DigitalSource *_drdy_pin_m;
+    uint8_t         _accel_range_m_s2;
+    float           _accel_range_scale;
+    uint8_t         _accel_samplerate;    
+    uint8_t         _accel_onchip_filter_bandwith;    
+    uint8_t         _mag_range_ga;
+    float           _mag_range_scale;
+    uint8_t         _mag_samplerate;    
+
+    // expceted values of reg1 and reg7 to catch in-flight
+    // brownouts of the sensor
+    uint8_t         _reg1_expected;
+    uint8_t         _reg7_expected;
+
+    bool                 _sample_available();
+    void                 _read_data_transaction();
+    void                 _read_data_transaction_accel();
+    void                 _read_data_transaction_mag();         
+    bool                 _data_ready();
+    void                 _poll_data(void);
+    uint8_t              _register_read( uint8_t reg );
+    void                 _register_write( uint8_t reg, uint8_t val );
+    void                 _register_write_check(uint8_t reg, uint8_t val);
+    void                 _register_modify(uint8_t reg, uint8_t clearbits, uint8_t setbits);
+    bool                 _hardware_init(Sample_rate sample_rate);
+    void                 disable_i2c(void);
+    uint8_t              accel_set_range(uint8_t max_g);
+    uint8_t              accel_set_samplerate(uint16_t frequency);
+    uint8_t              accel_set_onchip_lowpass_filter_bandwidth(uint8_t bandwidth);
+    uint8_t              mag_set_range(uint8_t max_ga);
+    uint8_t              mag_set_samplerate(uint16_t frequency);
+
+    AP_HAL::SPIDeviceDriver *_spi;
+    AP_HAL::Semaphore *_spi_sem;
+
+    uint16_t					_num_samples;
+    float                       _accel_scale;
+    float                       _mag_scale;
+
+    uint32_t _last_sample_time_micros;
+
+    // ensure we can't initialise twice
+    bool                        _initialised;
+    int16_t              _LSM303D_product_id;
+
+    // how many hardware samples before we report a sample to the caller
+    uint8_t _sample_shift;
+
+    // support for updating filter at runtime
+    uint8_t _last_filter_hz;
+
+    void _set_filter_register(uint8_t filter_hz, uint8_t default_filter);
+
+    uint16_t _error_count;
+
+    // accumulation in timer - must be read with timer disabled
+    // the sum of the values since last read
+    Vector3l _accel_sum;
+    Vector3l _mag_sum;
+    volatile int16_t _sum_count;
+
+public:
+
+#if LSM303D_DEBUG
+    void						_dump_registers(void);
+#endif
+};
+
+#endif // __AP_INERTIAL_SENSOR_LSM303D_H__
-- 
GitLab