#ifndef _VNSENSORS_SENSORS_H_
#define _VNSENSORS_SENSORS_H_

#if PYTHON
	#include "boostpython.h"
#endif

#include <string>
#include <vector>

#include "int.h"
#include "nocopy.h"
#include "packetfinder.h"
#include "export.h"
#include "registers.h"

#if PYTHON
	#include "vn/event.h"
#endif

namespace vn {

namespace xplat {
	class IPort;
}
namespace sensors {

// Define the max record size of a firmware update file
#define MAXFIRMWAREUPDATERECORDSIZE 300

/// \brief Represents an error from a VectorNav sensor.
struct sensor_error : public std::exception
{
private:
	sensor_error();

public:

	/// \brief Creates a new sensor_error based on the error value provided by
	/// the sensor.
	explicit sensor_error(protocol::uart::SensorError e);

	/// \brief Copy constructor.
	sensor_error(const sensor_error& e);

	~sensor_error() throw();

	/// \brief Returns a description of the exception.
	///
	/// \return A description of the exception.
	char const* what() const throw();

	/// \brief The associated sensor error.
	protocol::uart::SensorError error;

private:
	char *_errorMessage;
};

/// \brief Helpful class for working with VectorNav sensors.
class vn_proglib_DLLEXPORT VnSensor : private util::NoCopy
{

public:

	enum Family
	{
		VnSensor_Family_Unknown,	///< Unknown device family.
		VnSensor_Family_Vn100,		///< A device of the VectorNav VN-100 sensor family.
		VnSensor_Family_Vn200,		///< A device of the VectorNav VN-200 sensor family.
		VnSensor_Family_Vn300		///< A device of the VectorNav VN-300 sensor family.
	};

	/** \brief The different types of processors. */
	enum VnProcessorType
	{
		VNPROCESSOR_NAV,	// Navigation Processor (Main Processor)
		VNPROCESSOR_GPS,	// GPS Processor
		VNPROCESSOR_IMU		// IMU Processor
	};

	#if PYTHON
	typedef Event<protocol::uart::Packet&, size_t, xplat::TimeStamp> AsyncPacketReceivedEvent;
	#endif

	/// \brief Defines a callback handler that can received notification when
	/// the VnSensor receives raw data from its port.
	///
	/// \param[in] userData Pointer to user data that was initially supplied
	///     when the callback was registered via registerRawDataReceivedHandler.
	/// \param[in] rawData Pointer to the raw data.
	/// \param[in] length The number of bytes of raw data.
	/// \param[in] runningIndex The running index of the received data.
	typedef void(*RawDataReceivedHandler)(void* userData, const char* rawData, size_t length, size_t runningIndex);

	/// \brief Defines the signature for a method that can receive
	/// notifications of new possible packets found.
	///
	/// \param[in] userData Pointer to user data that was initially supplied
	///     when the callback was registered via registerPossiblePacketFoundHandler.
	/// \param[in] possiblePacket The possible packet that was found.
	/// \param[in] packetStartRunningIndex The running index of the start of
	///     the packet.
	typedef void(*PossiblePacketFoundHandler)(void* userData, protocol::uart::Packet& possiblePacket, size_t packetStartRunningIndex);

	/// \brief Defines the signature for a method that can receive
	/// notifications of when a new asynchronous data packet (ASCII or BINARY)
	/// is received.
	///
	/// This packet will have already had and pertinent error checking
	/// performed and determined to be an asynchronous packet.
	///
	/// \param[in] userData Pointer to user data that was initially supplied
	///     when the callback was registered via registerAsyncPacketReceivedHandler.
	/// \param[in] asyncPacket The asynchronous packet received.
	/// \param[in] packetStartRunningIndex The running index of the start of
	///     the packet.
	typedef void(*AsyncPacketReceivedHandler)(void* userData, protocol::uart::Packet& asyncPacket, size_t packetStartRunningIndex);

	/// \brief Defines the signature for a method that can receive
	/// notifications when an error message is received.
	///
	/// This packet will have already had and pertinent error checking
	/// performed and determined to be an asynchronous packet.
	///
	/// \param[in] userData Pointer to user data that was initially supplied
	///     when the callback was registered via registerErrorPacketReceivedHandler.
	/// \param[in] errorPacket The error packet received.
	/// \param[in] packetStartRunningIndex The running index of the start of
	///     the packet.
	typedef void(*ErrorPacketReceivedHandler)(void* userData, protocol::uart::Packet& errorPacket, size_t packetStartRunningIndex);

	/// \brief The list of baudrates supported by VectorNav sensors.
	static std::vector<uint32_t> supportedBaudrates();

	VnSensor();

	~VnSensor();

	/// \brief Returns the baudrate of the serial port connection. Note this
	/// is independent of the sensor's on-board serial baudrate setting.
	///
	/// \return The connected baudrate.
	/// \return The connected baudrate.
	uint32_t baudrate();

	/// \brief Returns the name of the port the sensor is connected to.
	///
	/// \return The port name.
	std::string port();

	/// \defgroup vnSensorProperties VnSensor Properties
	/// \brief This group of methods interface with the VnSensor properties.
	///
	/// \{

	/// \brief Gets the current error detection mode used for commands sent to
	/// the sensor. Default is protocol::uart::ErrorDetectionMode::CHECKSUM.
	///
	/// \return The error detection mode used for packets sent to the sensor.
	protocol::uart::ErrorDetectionMode sendErrorDetectionMode();

	/// \brief Sets the error detection mode used by the class for commands
	/// sent to the sensor.
	///
	/// \param[in] mode The new error detection mode for packets sent to the
	/// sensor.
	void setSendErrorDetectionMode(protocol::uart::ErrorDetectionMode mode);

	/// \brief Indicates if the VnSensor is connected.
	///
	/// \return <c>true</c> if the VnSensor is connected; otherwise <c>false</c>.
	bool isConnected();

	/// \brief Gets the amount of time in milliseconds to wait for a response
	/// during read/writes with the sensor.
	///
	/// \return The response timeout in milliseconds.
	uint16_t responseTimeoutMs();

	/// \brief Sets the amount of time in milliseconds to wait for a response
	/// during read/writes with the sensor.
	///
	/// \param[in] timeout The number of milliseconds for response timeouts.
	void setResponseTimeoutMs(uint16_t timeout);

	/// \brief The delay in milliseconds between retransmitting commands.
	///
	/// \return The retransmit delay in milliseconds.
	uint16_t retransmitDelayMs();

	/// \brief Sets the delay in milliseconds between command retransmits.
	///
	/// \param[in] delay The retransmit delay in milliseconds.
	void setRetransmitDelayMs(uint16_t delay);

	/// \}

	/// \brief Checks if we are able to send and receive communication with a sensor.
	///
	/// \return <c>true</c> if we can communicate with the sensor; otherwise <c>false</c>.
	bool verifySensorConnectivity();

	/// \brief Connects to a VectorNav sensor.
	///
	/// \param[in] portName The name of the serial port to connect to.
	/// \param[in] baudrate The baudrate to connect at.
	void connect(const std::string &portName, uint32_t baudrate);

	/// \brief Allows connecting to a VectorNav sensor over a
	///     \ref vn::common::ISimplePort.
	///
	/// The caller is responsible for properly destroying the
	/// \ref vn::common::ISimplePort object when this method is used. Also, if
	/// the provided \ref vn::common::ISimplePort is already open, then when
	/// the method \ref disconnect is called, \ref VnSensor will not attempt to
	/// close the port. However, if the \ref vn::common::ISimplePort is not
	/// open, then any subsequent calls to \ref disconnect will close the port.
	///
	/// \param[in] simplePort An \ref vn::common::ISimplePort. May be either
	///     currently open or closed.
	void connect(xplat::IPort* port);

	/// \brief Disconnects from the VectorNav sensor.
	///
	/// \exception invalid_operation Thrown if the VnSensor is not
	///     connected.
	void disconnect();

	/// \brief Sends the provided command and returns the response from the sensor.
	///
	/// If the command does not have an asterisk '*', then a checksum will be performed
	/// and appended based on the current error detection mode. Also, if the line-ending
	/// \\r\\n is not present, these will be added also.
	///
	/// \param[in] toSend The command to send to the sensor.
	/// \return The response received from the sensor.
	std::string transaction(std::string toSend);

	/// \brief Writes a raw data string to the sensor, normally appending an
	/// appropriate error detection checksum.
	///
	/// No checksum is necessary as any missing checksum will be provided. For
	/// example, the following toSend data will be correctly received by the
	/// sensor.
	///  - <c>$VNRRG,1*42\\r\\n</c>
	///  - <c>$VNRRG,1*42</c>
	///  - <c>$VNRRG,1*</c>
	///  - <c>$VNRRG,1</c>
	///  - <c>VNRRG,1</c>
	/// 
	/// If waitForReply is <c>true</c>, then the method will wait for a
	/// response and return the received response. Otherwise, if <c>false</c>,
	/// the method will send the data and return immediately with an empty
	/// string.
	///
	/// \param[in] toSend The data to send. The method will automatically
	///     append a checksum/CRC if none is provided.
	/// \param[in] waitForReply Indicates if the method should wait for a
	///     response and return the received response.
	/// \param[in] errorDetectionMode Indicates the error detection mode to
	///     append to any packets to send.
	/// \return The received response if waitForReply is <c>true</c>; otherwise
	///     this will be an empty string.
	std::string send(
		std::string toSend,
		bool waitForReply = true,
		protocol::uart::ErrorDetectionMode errorDetectionMode = protocol::uart::ERRORDETECTIONMODE_CHECKSUM);

	/// \brief Issues a tare command to the VectorNav Sensor.
	///
	/// \param[in] waitForReply Indicates if the method should wait for a
	///     response from the sensor.
	void tare(bool waitForReply = true);

	/// \brief Issues a command to the VectorNav Sensor to set the gyro's bias.
	///
	/// \param[in] waitForReply Indicates if the method should wait for a
	///     response from the sensor.
	void setGyroBias(bool waitForReply = true);

	/// \brief Command to inform the VectorNav Sensor if there is a magnetic disturbance present.
	///
	/// \param[in] disturbancePresent Indicates the presence of a magnetic disturbance
	/// \param[in] waitForReply Indicates if the method should wait for a
	///     response from the sensor.
	void magneticDisturbancePresent(bool disturbancePresent, bool waitForReply = true);

	/// \brief Command to inform the VectorNav Sensor if there is an acceleration disturbance present.
	///
	/// \param[in] disturbancePresent Indicates the presense of an acceleration disturbance.
	/// \param[in] waitForReply Indicates if the method should wait for a
	///     response from the sensor.
	void accelerationDisturbancePresent(bool disturbancePresent, bool waitForReply = true);

	/// \brief Issues a Write Settings command to the VectorNav Sensor.
	///
	/// \param[in] waitForReply Indicates if the method should wait for a
	///     response from the sensor.
	void writeSettings(bool waitForReply = true);

	/// \brief Issues a Restore Factory Settings command to the VectorNav
	/// sensor.
	///
	/// \param[in] waitForReply Indicates if the method should wait for a
	///     response from the sensor.
	void restoreFactorySettings(bool waitForReply = true);

	/// \brief Issues a Reset command to the VectorNav sensor.
	///
	/// \param[in] waitForReply Indicates if the method should wait for a
	///     response from the sensor.
	void reset(bool waitForReply = true);

	/// \brief Issues a Firmware Update command to the VectorNav sensor.
	///
	/// \param[in] waitForReply Indicates if the method should wait for a
	///     response from the sensor.
	void firmwareUpdateMode(bool waitForReply = true);

	/// \brief Write a firmware update record to the bootloader
	///
	/// \param[in] record The firmware update record to write to the bootloader
	/// \param[in] waitForReply Indicates if the method should wait for a
	///     response from the sensor.
	void writeFirmwareUpdateRecord(const std::string& record, bool waitForReply = true);

	/// \brief Calibrate the bootloader with current baudrate.
	///
	/// return string - Bootloader Version Number
	std::string calibrateBootloader();

	/// \brief Issues a change baudrate to the VectorNav sensor and then
	/// reconnects the attached serial port at the new baudrate.
	///
	/// \param[in] baudrate The new sensor baudrate.
	void changeBaudRate(uint32_t baudrate);

	/// \brief This method will connect to the specified serial port, query the
	/// attached sensor, and determine the type of device.
	///
	/// \param[in] portName The COM port name to connect to.
	/// \param[in] baudrate The baudrate to connect at.
	//static void determineDeviceFamily(std::string portName, uint32_t baudrate);

	/// \brief This method will query the attached sensor and determine the
	/// device family it belongs to.
	///
	/// \return The determined device family.
	Family determineDeviceFamily();

	/// \brief This method determines which device family a VectorNav sensor
	/// belongs to based on the provided model number.
	///
	/// \return The determined device family.
	static Family determineDeviceFamily(std::string modelNumber);

	/// \defgroup vnSensorEvents VnSensor Events
	/// \brief This group of methods allow registering/unregistering for events
	/// of the VnSensor.
	///
	/// \{

	/// \brief Registers a callback method for notification when raw data is
	/// received.
	///
	/// \param[in] userData Pointer to user data, which will be provided to the
	///     callback method.
	/// \param[in] handler The callback method.
	void registerRawDataReceivedHandler(void* userData, RawDataReceivedHandler handler);

	#if PL150
	//Event event
	#else
	#if PYTHON

	PyObject* registerRawDataReceivedHandler(PyObject* callable);
	
	#endif
	#endif

	/// \brief Unregisters the registered callback method.
	void unregisterRawDataReceivedHandler();

	/// \brief Registers a callback method for notification when a new possible
	/// packet is found.
	///
	/// \param[in] userData Pointer to user data, which will be provided to the
	///     callback method.
	/// \param[in] handler The callback method.
	void registerPossiblePacketFoundHandler(void* userData, PossiblePacketFoundHandler handler);

	/// \brief Unregisters the registered callback method.
	void unregisterPossiblePacketFoundHandler();

	/// \brief Registers a callback method for notification when a new
	/// asynchronous data packet is received.
	///
	/// \param[in] userData Pointer to user data, which will be provided to the
	///     callback method.
	/// \param[in] handler The callback method.
	void registerAsyncPacketReceivedHandler(void* userData, AsyncPacketReceivedHandler handler);

	#if PL150
	//packet, index, timestamp
	//Event<protocol::uart::Packet&, size_t, size_t> eventAsyncPacketRecieved;
	#if PYTHON
	AsyncPacketReceivedEvent eventAsyncPacketReceived;
	#endif

	#else
	#if PYTHON
	PyObject* registerAsyncPacketReceivedHandler(PyObject* callable);
	#endif
	#endif

	/// \brief Unregisters the registered callback method.
	void unregisterAsyncPacketReceivedHandler();

	/// \brief Registers a callback method for notification when an error
	/// packet is received.
	///
	/// \param[in] userData Pointer to user data, which will be provided to the
	///     callback method.
	/// \param[in] handler The callback method.
	void registerErrorPacketReceivedHandler(void* userData, ErrorPacketReceivedHandler handler);

	/// \brief Unregisters the registered callback method.
	void unregisterErrorPacketReceivedHandler();

	/// \}

	/// \defgroup registerAccessMethods Register Access Methods
	/// \brief This group of methods provide access to read and write to the
	/// sensor's registers.
	///
	/// \{

	/// \brief Reads the Binary Output 1 register.
	///
	/// \return The register's values.
	BinaryOutputRegister readBinaryOutput1();

	/// \brief Writes to the Binary Output 1 register.
	///
	/// \param[in] fields The register's fields.
	/// \param[in] waitForReply Indicates if the method should wait for a response from the sensor.
	void writeBinaryOutput1(BinaryOutputRegister &fields, bool waitForReply = true);

	/// \brief Reads the Binary Output 2 register.
	///
	/// \return The register's values.
	BinaryOutputRegister readBinaryOutput2();

	/// \brief Writes to the Binary Output 2 register.
	///
	/// \param[in] fields The register's fields.
	/// \param[in] waitForReply Indicates if the method should wait for a response from the sensor.
	void writeBinaryOutput2(BinaryOutputRegister &fields, bool waitForReply = true);

	/// \brief Reads the Binary Output 3 register.
	///
	/// \return The register's values.
	BinaryOutputRegister readBinaryOutput3();

	/// \brief Writes to the Binary Output 3 register.
	///
	/// \param[in] fields The register's fields.
	/// \param[in] waitForReply Indicates if the method should wait for a response from the sensor.
	void writeBinaryOutput3(BinaryOutputRegister &fields, bool waitForReply = true);


	/// \brief Reads the Serial Baud Rate register for the specified port.
	///
	/// \param[in] port The port number to read from.
	/// \return The register's values.
	uint32_t readSerialBaudRate(uint8_t port);

	/// \brief Writes to the Serial Baud Rate register for the specified port.
	///
	/// \param[in] baudrate The register's Baud Rate field.
	/// \param[in] port The port number to write to.
	/// \param[in] waitForReply Indicates if the method should wait for a response from the sensor.
	void writeSerialBaudRate(const uint32_t &baudrate, uint8_t port, bool waitForReply = true);

	/// \brief Reads the Async Data Output Type register.
	///
	/// \param[in] port The port number to read from.
	/// \return The register's values.
	protocol::uart::AsciiAsync readAsyncDataOutputType(uint8_t port);

	/// \brief Writes to the Async Data Output Type register.
	///
	/// \param[in] ador The register's ADOR field.
	/// \param[in] port The port number to write to.
	/// \param[in] waitForReply Indicates if the method should wait for a response from the sensor.
	void writeAsyncDataOutputType(protocol::uart::AsciiAsync ador, uint8_t port, bool waitForReply = true);

	/// \brief Reads the Async Data Output Frequency register.
	///
	/// \param[in] port The port number to read from.
	/// \return The register's values.
	uint32_t readAsyncDataOutputFrequency(uint8_t port);

	/// \brief Writes to the Async Data Output Frequency register.
	///
	/// \param[in] adof The register's ADOF field.
	/// \param[in] port The port number to write to.
	/// \param[in] waitForReply Indicates if the method should wait for a response from the sensor.
	void writeAsyncDataOutputFrequency(const uint32_t &adof, uint8_t port, bool waitForReply = true);

	/// \brief Reads the INS Basic Configuration register for a VN-200 sensor.
	///
	/// \return The register's values.
	InsBasicConfigurationRegisterVn200 readInsBasicConfigurationVn200();

	/// \brief Writes to the INS Basic Configuration register for a VN-200 sensor.
	///
	/// \param[in] fields The register's fields.
	/// \param[in] waitForReply Indicates if the method should wait for a response from the sensor.
	void writeInsBasicConfigurationVn200(InsBasicConfigurationRegisterVn200 &fields, bool waitForReply = true);

	/// \brief Writes to the INS Basic Configuration register for a VN-200 sensor.
	///
	/// \param[in] scenario Value for the Scenario field.
	/// \param[in] ahrsAiding Value for the AhrsAiding field.
	/// \param[in] waitForReply Indicates if the method should wait for a response from the sensor.
	void writeInsBasicConfigurationVn200(
		protocol::uart::Scenario scenario,
		const uint8_t &ahrsAiding,
		bool waitForReply = true);

	/// \brief Reads the INS Basic Configuration register for a VN-300 sensor.
	///
	/// \return The register's values.
	InsBasicConfigurationRegisterVn300 readInsBasicConfigurationVn300();

	/// \brief Writes to the INS Basic Configuration register for a VN-300 sensor.
	///
	/// \param[in] fields The register's fields.
	/// \param[in] waitForReply Indicates if the method should wait for a response from the sensor.
	void writeInsBasicConfigurationVn300(InsBasicConfigurationRegisterVn300 &fields, bool waitForReply = true);

	/// \brief Writes to the INS Basic Configuration register for a VN-300 sensor.
	///
	/// \param[in] scenario Value for the Scenario field.
	/// \param[in] ahrsAiding Value for the AhrsAiding field.
	/// \param[in] estBaseline Value for the EstBaseline field.
	/// \param[in] waitForReply Indicates if the method should wait for a response from the sensor.
	void writeInsBasicConfigurationVn300(
		protocol::uart::Scenario scenario,
		const uint8_t &ahrsAiding,
		const uint8_t &estBaseline,
		bool waitForReply = true);

	/// \brief Reads the User Tag register.
	///
	/// \return The register's values.
	std::string readUserTag();

	/// \brief Writes to the User Tag register.
	///
	/// \param[in] tag The register's Tag field.
	/// \param[in] waitForReply Indicates if the method should wait for a response from the sensor.
	void writeUserTag(const std::string &tag, bool waitForReply = true);

	/// \brief Reads the Model Number register.
	///
	/// \return The register's values.
	std::string readModelNumber();

	/// \brief Reads the Hardware Revision register.
	///
	/// \return The register's values.
	uint32_t readHardwareRevision();

	/// \brief Reads the Serial Number register.
	///
	/// \return The register's values.
	uint32_t readSerialNumber();

	/// \brief Reads the Firmware Version register.
	///
	/// \return The register's values.
	std::string readFirmwareVersion();

	/// \brief Reads the Serial Baud Rate register.
	///
	/// \return The register's values.
	uint32_t readSerialBaudRate();

	/// \brief Writes to the Serial Baud Rate register.
	///
	/// \param[in] baudrate The register's Baud Rate field.
	/// \param[in] waitForReply Indicates if the method should wait for a response from the sensor.
	void writeSerialBaudRate(const uint32_t &baudrate, bool waitForReply = true);

	/// \brief Reads the Async Data Output Type register.
	///
	/// \return The register's values.
	protocol::uart::AsciiAsync readAsyncDataOutputType();

	/// \brief Writes to the Async Data Output Type register.
	///
	/// \param[in] ador The register's ADOR field.
	/// \param[in] waitForReply Indicates if the method should wait for a response from the sensor.
	void writeAsyncDataOutputType(protocol::uart::AsciiAsync ador, bool waitForReply = true);

	/// \brief Reads the Async Data Output Frequency register.
	///
	/// \return The register's values.
	uint32_t readAsyncDataOutputFrequency();

	/// \brief Writes to the Async Data Output Frequency register.
	///
	/// \param[in] adof The register's ADOF field.
	/// \param[in] waitForReply Indicates if the method should wait for a response from the sensor.
	void writeAsyncDataOutputFrequency(const uint32_t &adof, bool waitForReply = true);

	/// \brief Reads the Yaw Pitch Roll register.
	///
	/// \return The register's values.
	vn::math::vec3f readYawPitchRoll();

	/// \brief Reads the Attitude Quaternion register.
	///
	/// \return The register's values.
	vn::math::vec4f readAttitudeQuaternion();

	/// \brief Reads the Quaternion, Magnetic, Acceleration and Angular Rates register.
	///
	/// \return The register's values.
	QuaternionMagneticAccelerationAndAngularRatesRegister readQuaternionMagneticAccelerationAndAngularRates();

	/// \brief Reads the Magnetic Measurements register.
	///
	/// \return The register's values.
	vn::math::vec3f readMagneticMeasurements();

	/// \brief Reads the Acceleration Measurements register.
	///
	/// \return The register's values.
	vn::math::vec3f readAccelerationMeasurements();

	/// \brief Reads the Angular Rate Measurements register.
	///
	/// \return The register's values.
	vn::math::vec3f readAngularRateMeasurements();

	/// \brief Reads the Magnetic, Acceleration and Angular Rates register.
	///
	/// \return The register's values.
	MagneticAccelerationAndAngularRatesRegister readMagneticAccelerationAndAngularRates();

	/// \brief Reads the Magnetic and Gravity Reference Vectors register.
	///
	/// \return The register's values.
	MagneticAndGravityReferenceVectorsRegister readMagneticAndGravityReferenceVectors();

	/// \brief Writes to the Magnetic and Gravity Reference Vectors register.
	///
	/// \param[in] fields The register's fields.
	/// \param[in] waitForReply Indicates if the method should wait for a response from the sensor.
	void writeMagneticAndGravityReferenceVectors(MagneticAndGravityReferenceVectorsRegister &fields, bool waitForReply = true);

	/// \brief Writes to the Magnetic and Gravity Reference Vectors register.
	///
	/// \param[in] magRef Value for the MagRef field.
	/// \param[in] accRef Value for the AccRef field.
	/// \param[in] waitForReply Indicates if the method should wait for a response from the sensor.
	void writeMagneticAndGravityReferenceVectors(
		const vn::math::vec3f &magRef,
		const vn::math::vec3f &accRef,
		bool waitForReply = true);

	/// \brief Reads the Filter Measurements Variance Parameters register.
	///
	/// \return The register's values.
	FilterMeasurementsVarianceParametersRegister readFilterMeasurementsVarianceParameters();

	/// \brief Writes to the Filter Measurements Variance Parameters register.
	///
	/// \param[in] fields The register's fields.
	/// \param[in] waitForReply Indicates if the method should wait for a response from the sensor.
	void writeFilterMeasurementsVarianceParameters(FilterMeasurementsVarianceParametersRegister &fields, bool waitForReply = true);

	/// \brief Writes to the Filter Measurements Variance Parameters register.
	///
	/// \param[in] angularWalkVariance Value for the Angular Walk Variance field.
	/// \param[in] angularRateVariance Value for the Angular Rate Variance field.
	/// \param[in] magneticVariance Value for the Magnetic Variance field.
	/// \param[in] accelerationVariance Value for the Acceleration Variance field.
	/// \param[in] waitForReply Indicates if the method should wait for a response from the sensor.
	void writeFilterMeasurementsVarianceParameters(
		const float &angularWalkVariance,
		const vn::math::vec3f &angularRateVariance,
		const vn::math::vec3f &magneticVariance,
		const vn::math::vec3f &accelerationVariance,
		bool waitForReply = true);

	/// \brief Reads the Magnetometer Compensation register.
	///
	/// \return The register's values.
	MagnetometerCompensationRegister readMagnetometerCompensation();

	/// \brief Writes to the Magnetometer Compensation register.
	///
	/// \param[in] fields The register's fields.
	/// \param[in] waitForReply Indicates if the method should wait for a response from the sensor.
	void writeMagnetometerCompensation(MagnetometerCompensationRegister &fields, bool waitForReply = true);

	/// \brief Writes to the Magnetometer Compensation register.
	///
	/// \param[in] c Value for the C field.
	/// \param[in] b Value for the B field.
	/// \param[in] waitForReply Indicates if the method should wait for a response from the sensor.
	void writeMagnetometerCompensation(
		const vn::math::mat3f &c,
		const vn::math::vec3f &b,
		bool waitForReply = true);

	/// \brief Reads the Filter Active Tuning Parameters register.
	///
	/// \return The register's values.
	FilterActiveTuningParametersRegister readFilterActiveTuningParameters();

	/// \brief Writes to the Filter Active Tuning Parameters register.
	///
	/// \param[in] fields The register's fields.
	/// \param[in] waitForReply Indicates if the method should wait for a response from the sensor.
	void writeFilterActiveTuningParameters(FilterActiveTuningParametersRegister &fields, bool waitForReply = true);

	/// \brief Writes to the Filter Active Tuning Parameters register.
	///
	/// \param[in] magneticDisturbanceGain Value for the Magnetic Disturbance Gain field.
	/// \param[in] accelerationDisturbanceGain Value for the Acceleration Disturbance Gain field.
	/// \param[in] magneticDisturbanceMemory Value for the Magnetic Disturbance Memory field.
	/// \param[in] accelerationDisturbanceMemory Value for the Acceleration Disturbance Memory field.
	/// \param[in] waitForReply Indicates if the method should wait for a response from the sensor.
	void writeFilterActiveTuningParameters(
		const float &magneticDisturbanceGain,
		const float &accelerationDisturbanceGain,
		const float &magneticDisturbanceMemory,
		const float &accelerationDisturbanceMemory,
		bool waitForReply = true);

	/// \brief Reads the Acceleration Compensation register.
	///
	/// \return The register's values.
	AccelerationCompensationRegister readAccelerationCompensation();

	/// \brief Writes to the Acceleration Compensation register.
	///
	/// \param[in] fields The register's fields.
	/// \param[in] waitForReply Indicates if the method should wait for a response from the sensor.
	void writeAccelerationCompensation(AccelerationCompensationRegister &fields, bool waitForReply = true);

	/// \brief Writes to the Acceleration Compensation register.
	///
	/// \param[in] c Value for the C field.
	/// \param[in] b Value for the B field.
	/// \param[in] waitForReply Indicates if the method should wait for a response from the sensor.
	void writeAccelerationCompensation(
		const vn::math::mat3f &c,
		const vn::math::vec3f &b,
		bool waitForReply = true);

	/// \brief Reads the Reference Frame Rotation register.
	///
	/// \return The register's values.
	vn::math::mat3f readReferenceFrameRotation();

	/// \brief Writes to the Reference Frame Rotation register.
	///
	/// \param[in] c The register's C field.
	/// \param[in] waitForReply Indicates if the method should wait for a response from the sensor.
	void writeReferenceFrameRotation(const vn::math::mat3f &c, bool waitForReply = true);

	/// \brief Reads the Yaw, Pitch, Roll, Magnetic, Acceleration and Angular Rates register.
	///
	/// \return The register's values.
	YawPitchRollMagneticAccelerationAndAngularRatesRegister readYawPitchRollMagneticAccelerationAndAngularRates();

	/// \brief Reads the Communication Protocol Control register.
	///
	/// \return The register's values.
	CommunicationProtocolControlRegister readCommunicationProtocolControl();

	/// \brief Writes to the Communication Protocol Control register.
	///
	/// \param[in] fields The register's fields.
	/// \param[in] waitForReply Indicates if the method should wait for a response from the sensor.
	void writeCommunicationProtocolControl(CommunicationProtocolControlRegister &fields, bool waitForReply = true);

	/// \brief Writes to the Communication Protocol Control register.
	///
	/// \param[in] serialCount Value for the SerialCount field.
	/// \param[in] serialStatus Value for the SerialStatus field.
	/// \param[in] spiCount Value for the SPICount field.
	/// \param[in] spiStatus Value for the SPIStatus field.
	/// \param[in] serialChecksum Value for the SerialChecksum field.
	/// \param[in] spiChecksum Value for the SPIChecksum field.
	/// \param[in] errorMode Value for the ErrorMode field.
	/// \param[in] waitForReply Indicates if the method should wait for a response from the sensor.
	void writeCommunicationProtocolControl(
		protocol::uart::CountMode serialCount,
		protocol::uart::StatusMode serialStatus,
		protocol::uart::CountMode spiCount,
		protocol::uart::StatusMode spiStatus,
		protocol::uart::ChecksumMode serialChecksum,
		protocol::uart::ChecksumMode spiChecksum,
		protocol::uart::ErrorMode errorMode,
		bool waitForReply = true);

	/// \brief Reads the Synchronization Control register.
	///
	/// \return The register's values.
	SynchronizationControlRegister readSynchronizationControl();

	/// \brief Writes to the Synchronization Control register.
	///
	/// \param[in] fields The register's fields.
	/// \param[in] waitForReply Indicates if the method should wait for a response from the sensor.
	void writeSynchronizationControl(SynchronizationControlRegister &fields, bool waitForReply = true);

	/// \brief Writes to the Synchronization Control register.
	///
	/// \param[in] syncInMode Value for the SyncInMode field.
	/// \param[in] syncInEdge Value for the SyncInEdge field.
	/// \param[in] syncInSkipFactor Value for the SyncInSkipFactor field.
	/// \param[in] syncOutMode Value for the SyncOutMode field.
	/// \param[in] syncOutPolarity Value for the SyncOutPolarity field.
	/// \param[in] syncOutSkipFactor Value for the SyncOutSkipFactor field.
	/// \param[in] syncOutPulseWidth Value for the SyncOutPulseWidth field.
	/// \param[in] waitForReply Indicates if the method should wait for a response from the sensor.
	void writeSynchronizationControl(
		protocol::uart::SyncInMode syncInMode,
		protocol::uart::SyncInEdge syncInEdge,
		const uint16_t &syncInSkipFactor,
		protocol::uart::SyncOutMode syncOutMode,
		protocol::uart::SyncOutPolarity syncOutPolarity,
		const uint16_t &syncOutSkipFactor,
		const uint32_t &syncOutPulseWidth,
		bool waitForReply = true);

	/// \brief Reads the Synchronization Status register.
	///
	/// \return The register's values.
	SynchronizationStatusRegister readSynchronizationStatus();

	/// \brief Writes to the Synchronization Status register.
	///
	/// \param[in] fields The register's fields.
	/// \param[in] waitForReply Indicates if the method should wait for a response from the sensor.
	void writeSynchronizationStatus(SynchronizationStatusRegister &fields, bool waitForReply = true);

	/// \brief Writes to the Synchronization Status register.
	///
	/// \param[in] syncInCount Value for the SyncInCount field.
	/// \param[in] syncInTime Value for the SyncInTime field.
	/// \param[in] syncOutCount Value for the SyncOutCount field.
	/// \param[in] waitForReply Indicates if the method should wait for a response from the sensor.
	void writeSynchronizationStatus(
		const uint32_t &syncInCount,
		const uint32_t &syncInTime,
		const uint32_t &syncOutCount,
		bool waitForReply = true);

	/// \brief Reads the Filter Basic Control register.
	///
	/// \return The register's values.
	FilterBasicControlRegister readFilterBasicControl();

	/// \brief Writes to the Filter Basic Control register.
	///
	/// \param[in] fields The register's fields.
	/// \param[in] waitForReply Indicates if the method should wait for a response from the sensor.
	void writeFilterBasicControl(FilterBasicControlRegister &fields, bool waitForReply = true);

	/// \brief Writes to the Filter Basic Control register.
	///
	/// \param[in] magMode Value for the MagMode field.
	/// \param[in] extMagMode Value for the ExtMagMode field.
	/// \param[in] extAccMode Value for the ExtAccMode field.
	/// \param[in] extGyroMode Value for the ExtGyroMode field.
	/// \param[in] gyroLimit Value for the GyroLimit field.
	/// \param[in] waitForReply Indicates if the method should wait for a response from the sensor.
	void writeFilterBasicControl(
		protocol::uart::MagneticMode magMode,
		protocol::uart::ExternalSensorMode extMagMode,
		protocol::uart::ExternalSensorMode extAccMode,
		protocol::uart::ExternalSensorMode extGyroMode,
		const vn::math::vec3f &gyroLimit,
		bool waitForReply = true);

	/// \brief Reads the Heave Configuration register.
	///
	/// \return The register's values.
	HeaveConfigurationRegister readHeaveConfiguration(); 

	/// \brief Writes to the Heave Configuration register.
	///
	/// \param[in] fields The register's fields.
	/// \param[in] waitForReply Indicates if the method should wait for a response from the sensor.
	void writeHeaveConfiguration(HeaveConfigurationRegister &fields, bool waitForReply = true);

	/// \brief Writes to the Heave Configuration register.
	///
	/// \param[in] initialWavePeriod Value for the initialWavePeriod field.
	/// \param[in] initialWaveAmplitude Value for the initialWaveAmplitude field.
	/// \param[in] maxWavePeriod Value for the maxWavePeriod field.
	/// \param[in] minWaveAmplitude Value for the minWaveAmplitude field.
	/// \param[in] delayedHeaveCutoffFreq Value for the delayedHeaveCutoffFreq field.
	/// \param[in] heaveCutoffFreq Value for the heaveCutoffFreq field.
	/// \param[in] heaveRateCutoffFreq Value for the heaveRateCutoffFreq field.
	void writeHeaveConfiguration(
		const float &initialWavePeriod,
		const float &initialWaveAmplitude,
		const float &maxWavePeriod,
		const float &minWaveAmplitude,
		const float &delayedHeaveCutoffFreq,
		const float &heaveCutoffFreq,
		const float &heaveRateCutoffFreq,
		bool waitForReply = true);

	/// \brief Reads the VPE Basic Control register.
	///
	/// \return The register's values.
	VpeBasicControlRegister readVpeBasicControl();

	/// \brief Writes to the VPE Basic Control register.
	///
	/// \param[in] fields The register's fields.
	/// \param[in] waitForReply Indicates if the method should wait for a response from the sensor.
	void writeVpeBasicControl(VpeBasicControlRegister &fields, bool waitForReply = true);

	/// \brief Writes to the VPE Basic Control register.
	///
	/// \param[in] enable Value for the Enable field.
	/// \param[in] headingMode Value for the HeadingMode field.
	/// \param[in] filteringMode Value for the FilteringMode field.
	/// \param[in] tuningMode Value for the TuningMode field.
	/// \param[in] waitForReply Indicates if the method should wait for a response from the sensor.
	void writeVpeBasicControl(
		protocol::uart::VpeEnable enable,
		protocol::uart::HeadingMode headingMode,
		protocol::uart::VpeMode filteringMode,
		protocol::uart::VpeMode tuningMode,
		bool waitForReply = true);

	/// \brief Reads the VPE Magnetometer Basic Tuning register.
	///
	/// \return The register's values.
	VpeMagnetometerBasicTuningRegister readVpeMagnetometerBasicTuning();

	/// \brief Writes to the VPE Magnetometer Basic Tuning register.
	///
	/// \param[in] fields The register's fields.
	/// \param[in] waitForReply Indicates if the method should wait for a response from the sensor.
	void writeVpeMagnetometerBasicTuning(VpeMagnetometerBasicTuningRegister &fields, bool waitForReply = true);

	/// \brief Writes to the VPE Magnetometer Basic Tuning register.
	///
	/// \param[in] baseTuning Value for the BaseTuning field.
	/// \param[in] adaptiveTuning Value for the AdaptiveTuning field.
	/// \param[in] adaptiveFiltering Value for the AdaptiveFiltering field.
	/// \param[in] waitForReply Indicates if the method should wait for a response from the sensor.
	void writeVpeMagnetometerBasicTuning(
		const vn::math::vec3f &baseTuning,
		const vn::math::vec3f &adaptiveTuning,
		const vn::math::vec3f &adaptiveFiltering,
		bool waitForReply = true);

	/// \brief Reads the VPE Magnetometer Advanced Tuning register.
	///
	/// \return The register's values.
	VpeMagnetometerAdvancedTuningRegister readVpeMagnetometerAdvancedTuning();

	/// \brief Writes to the VPE Magnetometer Advanced Tuning register.
	///
	/// \param[in] fields The register's fields.
	/// \param[in] waitForReply Indicates if the method should wait for a response from the sensor.
	void writeVpeMagnetometerAdvancedTuning(VpeMagnetometerAdvancedTuningRegister &fields, bool waitForReply = true);

	/// \brief Writes to the VPE Magnetometer Advanced Tuning register.
	///
	/// \param[in] minFiltering Value for the MinFiltering field.
	/// \param[in] maxFiltering Value for the MaxFiltering field.
	/// \param[in] maxAdaptRate Value for the MaxAdaptRate field.
	/// \param[in] disturbanceWindow Value for the DisturbanceWindow field.
	/// \param[in] maxTuning Value for the MaxTuning field.
	/// \param[in] waitForReply Indicates if the method should wait for a response from the sensor.
	void writeVpeMagnetometerAdvancedTuning(
		const vn::math::vec3f &minFiltering,
		const vn::math::vec3f &maxFiltering,
		const float &maxAdaptRate,
		const float &disturbanceWindow,
		const float &maxTuning,
		bool waitForReply = true);

	/// \brief Reads the VPE Accelerometer Basic Tuning register.
	///
	/// \return The register's values.
	VpeAccelerometerBasicTuningRegister readVpeAccelerometerBasicTuning();

	/// \brief Writes to the VPE Accelerometer Basic Tuning register.
	///
	/// \param[in] fields The register's fields.
	/// \param[in] waitForReply Indicates if the method should wait for a response from the sensor.
	void writeVpeAccelerometerBasicTuning(VpeAccelerometerBasicTuningRegister &fields, bool waitForReply = true);

	/// \brief Writes to the VPE Accelerometer Basic Tuning register.
	///
	/// \param[in] baseTuning Value for the BaseTuning field.
	/// \param[in] adaptiveTuning Value for the AdaptiveTuning field.
	/// \param[in] adaptiveFiltering Value for the AdaptiveFiltering field.
	/// \param[in] waitForReply Indicates if the method should wait for a response from the sensor.
	void writeVpeAccelerometerBasicTuning(
		const vn::math::vec3f &baseTuning,
		const vn::math::vec3f &adaptiveTuning,
		const vn::math::vec3f &adaptiveFiltering,
		bool waitForReply = true);

	/// \brief Reads the VPE Accelerometer Advanced Tuning register.
	///
	/// \return The register's values.
	VpeAccelerometerAdvancedTuningRegister readVpeAccelerometerAdvancedTuning();

	/// \brief Writes to the VPE Accelerometer Advanced Tuning register.
	///
	/// \param[in] fields The register's fields.
	/// \param[in] waitForReply Indicates if the method should wait for a response from the sensor.
	void writeVpeAccelerometerAdvancedTuning(VpeAccelerometerAdvancedTuningRegister &fields, bool waitForReply = true);

	/// \brief Writes to the VPE Accelerometer Advanced Tuning register.
	///
	/// \param[in] minFiltering Value for the MinFiltering field.
	/// \param[in] maxFiltering Value for the MaxFiltering field.
	/// \param[in] maxAdaptRate Value for the MaxAdaptRate field.
	/// \param[in] disturbanceWindow Value for the DisturbanceWindow field.
	/// \param[in] maxTuning Value for the MaxTuning field.
	/// \param[in] waitForReply Indicates if the method should wait for a response from the sensor.
	void writeVpeAccelerometerAdvancedTuning(
		const vn::math::vec3f &minFiltering,
		const vn::math::vec3f &maxFiltering,
		const float &maxAdaptRate,
		const float &disturbanceWindow,
		const float &maxTuning,
		bool waitForReply = true);

	/// \brief Reads the VPE Gyro Basic Tuning register.
	///
	/// \return The register's values.
	VpeGyroBasicTuningRegister readVpeGyroBasicTuning();

	/// \brief Writes to the VPE Gyro Basic Tuning register.
	///
	/// \param[in] fields The register's fields.
	/// \param[in] waitForReply Indicates if the method should wait for a response from the sensor.
	void writeVpeGyroBasicTuning(VpeGyroBasicTuningRegister &fields, bool waitForReply = true);

	/// \brief Writes to the VPE Gyro Basic Tuning register.
	///
	/// \param[in] angularWalkVariance Value for the AngularWalkVariance field.
	/// \param[in] baseTuning Value for the BaseTuning field.
	/// \param[in] adaptiveTuning Value for the AdaptiveTuning field.
	/// \param[in] waitForReply Indicates if the method should wait for a response from the sensor.
	void writeVpeGyroBasicTuning(
		const vn::math::vec3f &angularWalkVariance,
		const vn::math::vec3f &baseTuning,
		const vn::math::vec3f &adaptiveTuning,
		bool waitForReply = true);

	/// \brief Reads the Filter Startup Gyro Bias register.
	///
	/// \return The register's values.
	vn::math::vec3f readFilterStartupGyroBias();

	/// \brief Writes to the Filter Startup Gyro Bias register.
	///
	/// \param[in] bias The register's Bias field.
	/// \param[in] waitForReply Indicates if the method should wait for a response from the sensor.
	void writeFilterStartupGyroBias(const vn::math::vec3f &bias, bool waitForReply = true);

	/// \brief Reads the Magnetometer Calibration Control register.
	///
	/// \return The register's values.
	MagnetometerCalibrationControlRegister readMagnetometerCalibrationControl();

	/// \brief Writes to the Magnetometer Calibration Control register.
	///
	/// \param[in] fields The register's fields.
	/// \param[in] waitForReply Indicates if the method should wait for a response from the sensor.
	void writeMagnetometerCalibrationControl(MagnetometerCalibrationControlRegister &fields, bool waitForReply = true);

	/// \brief Writes to the Magnetometer Calibration Control register.
	///
	/// \param[in] hsiMode Value for the HSIMode field.
	/// \param[in] hsiOutput Value for the HSIOutput field.
	/// \param[in] convergeRate Value for the ConvergeRate field.
	/// \param[in] waitForReply Indicates if the method should wait for a response from the sensor.
	void writeMagnetometerCalibrationControl(
		protocol::uart::HsiMode hsiMode,
		protocol::uart::HsiOutput hsiOutput,
		const uint8_t &convergeRate,
		bool waitForReply = true);

	/// \brief Reads the Calculated Magnetometer Calibration register.
	///
	/// \return The register's values.
	CalculatedMagnetometerCalibrationRegister readCalculatedMagnetometerCalibration();

	/// \brief Reads the Indoor Heading Mode Control register.
	///
	/// \return The register's values.
	float readIndoorHeadingModeControl();

	/// \brief Writes to the Indoor Heading Mode Control register.
	///
	/// \param[in] maxRateError The register's Max Rate Error field.
	/// \param[in] waitForReply Indicates if the method should wait for a response from the sensor.
	void writeIndoorHeadingModeControl(const float &maxRateError, bool waitForReply = true);

	/// \brief Reads the Velocity Compensation Measurement register.
	///
	/// \return The register's values.
	vn::math::vec3f readVelocityCompensationMeasurement();

	/// \brief Writes to the Velocity Compensation Measurement register.
	///
	/// \param[in] velocity The register's Velocity field.
	/// \param[in] waitForReply Indicates if the method should wait for a response from the sensor.
	void writeVelocityCompensationMeasurement(const vn::math::vec3f &velocity, bool waitForReply = true);

	/// \brief Reads the Velocity Compensation Control register.
	///
	/// \return The register's values.
	VelocityCompensationControlRegister readVelocityCompensationControl();

	/// \brief Writes to the Velocity Compensation Control register.
	///
	/// \param[in] fields The register's fields.
	/// \param[in] waitForReply Indicates if the method should wait for a response from the sensor.
	void writeVelocityCompensationControl(VelocityCompensationControlRegister &fields, bool waitForReply = true);

	/// \brief Writes to the Velocity Compensation Control register.
	///
	/// \param[in] mode Value for the Mode field.
	/// \param[in] velocityTuning Value for the VelocityTuning field.
	/// \param[in] rateTuning Value for the RateTuning field.
	/// \param[in] waitForReply Indicates if the method should wait for a response from the sensor.
	void writeVelocityCompensationControl(
		protocol::uart::VelocityCompensationMode mode,
		const float &velocityTuning,
		const float &rateTuning,
		bool waitForReply = true);

	/// \brief Reads the Velocity Compensation Status register.
	///
	/// \return The register's values.
	VelocityCompensationStatusRegister readVelocityCompensationStatus();

	/// \brief Reads the IMU Measurements register.
	///
	/// \return The register's values.
	ImuMeasurementsRegister readImuMeasurements();

	/// \brief Reads the GPS Configuration register.
	///
	/// \return The register's values.
	GpsConfigurationRegister readGpsConfiguration();

	/// \brief Writes to the GPS Configuration register.
	///
	/// \param[in] fields The register's fields.
	/// \param[in] waitForReply Indicates if the method should wait for a response from the sensor.
	void writeGpsConfiguration(GpsConfigurationRegister &fields, bool waitForReply = true);

	/// \brief Writes to the GPS Configuration register (deprecated).
	///
	/// \param[in] mode Value for the Mode field.
	/// \param[in] ppsSource Value for the PpsSource field.
	/// \param[in] waitForReply Indicates if the method should wait for a response from the sensor.
	void writeGpsConfiguration(
		protocol::uart::GpsMode mode,
		protocol::uart::PpsSource ppsSource,
		bool waitForReply = true);

	/// \brief Writes to the GPS Configuration register.
	///
	/// \param[in] mode Value for the Mode field.
	/// \param[in] ppsSource Value for the PpsSource field.
	/// \param[in] rate Value for the Rate field.
	/// \param[in] antPow Value for the AntPow field.
	/// \param[in] waitForReply Indicates if the method should wait for a response from the sensor.
	void writeGpsConfiguration(
		protocol::uart::GpsMode mode,
		protocol::uart::PpsSource ppsSource,
		protocol::uart::GpsRate rate,
		protocol::uart::AntPower antPow,
		bool waitForReply = true);

	/// \brief Reads the GPS Antenna Offset register.
	///
	/// \return The register's values.
	vn::math::vec3f readGpsAntennaOffset();

	/// \brief Writes to the GPS Antenna Offset register.
	///
	/// \param[in] position The register's Position field.
	/// \param[in] waitForReply Indicates if the method should wait for a response from the sensor.
	void writeGpsAntennaOffset(const vn::math::vec3f &position, bool waitForReply = true);

	/// \brief Reads the GPS Solution - LLA register.
	///
	/// \return The register's values.
	GpsSolutionLlaRegister readGpsSolutionLla();

	/// \brief Reads the GPS Solution - ECEF register.
	///
	/// \return The register's values.
	GpsSolutionEcefRegister readGpsSolutionEcef();

	/// \brief Reads the INS Solution - LLA register.
	///
	/// \return The register's values.
	InsSolutionLlaRegister readInsSolutionLla();

	/// \brief Reads the INS Solution - ECEF register.
	///
	/// \return The register's values.
	InsSolutionEcefRegister readInsSolutionEcef();

	/// \brief Reads the INS Advanced Configuration register.
	///
	/// \return The register's values.
	InsAdvancedConfigurationRegister readInsAdvancedConfiguration();

	/// \brief Writes to the INS Advanced Configuration register.
	///
	/// \param[in] fields The register's fields.
	/// \param[in] waitForReply Indicates if the method should wait for a response from the sensor.
	void writeInsAdvancedConfiguration(InsAdvancedConfigurationRegister &fields, bool waitForReply = true);

	/// \brief Writes to the INS Advanced Configuration register.
	///
	/// \param[in] useMag Value for the UseMag field.
	/// \param[in] usePres Value for the UsePres field.
	/// \param[in] posAtt Value for the PosAtt field.
	/// \param[in] velAtt Value for the VelAtt field.
	/// \param[in] velBias Value for the VelBias field.
	/// \param[in] useFoam Value for the UseFoam field.
	/// \param[in] gpsCovType Value for the GPSCovType field.
	/// \param[in] velCount Value for the VelCount field.
	/// \param[in] velInit Value for the VelInit field.
	/// \param[in] moveOrigin Value for the MoveOrigin field.
	/// \param[in] gpsTimeout Value for the GPSTimeout field.
	/// \param[in] deltaLimitPos Value for the DeltaLimitPos field.
	/// \param[in] deltaLimitVel Value for the DeltaLimitVel field.
	/// \param[in] minPosUncertainty Value for the MinPosUncertainty field.
	/// \param[in] minVelUncertainty Value for the MinVelUncertainty field.
	/// \param[in] waitForReply Indicates if the method should wait for a response from the sensor.
	void writeInsAdvancedConfiguration(
		const uint8_t &useMag,
		const uint8_t &usePres,
		const uint8_t &posAtt,
		const uint8_t &velAtt,
		const uint8_t &velBias,
		protocol::uart::FoamInit useFoam,
		const uint8_t &gpsCovType,
		const uint8_t &velCount,
		const float &velInit,
		const float &moveOrigin,
		const float &gpsTimeout,
		const float &deltaLimitPos,
		const float &deltaLimitVel,
		const float &minPosUncertainty,
		const float &minVelUncertainty,
		bool waitForReply = true);

	/// \brief Reads the INS State - LLA register.
	///
	/// \return The register's values.
	InsStateLlaRegister readInsStateLla();

	/// \brief Reads the INS State - ECEF register.
	///
	/// \return The register's values.
	InsStateEcefRegister readInsStateEcef();

	/// \brief Reads the Startup Filter Bias Estimate register.
	///
	/// \return The register's values.
	StartupFilterBiasEstimateRegister readStartupFilterBiasEstimate();

	/// \brief Writes to the Startup Filter Bias Estimate register.
	///
	/// \param[in] fields The register's fields.
	/// \param[in] waitForReply Indicates if the method should wait for a response from the sensor.
	void writeStartupFilterBiasEstimate(StartupFilterBiasEstimateRegister &fields, bool waitForReply = true);

	/// \brief Writes to the Startup Filter Bias Estimate register.
	///
	/// \param[in] gyroBias Value for the GyroBias field.
	/// \param[in] accelBias Value for the AccelBias field.
	/// \param[in] pressureBias Value for the PressureBias field.
	/// \param[in] waitForReply Indicates if the method should wait for a response from the sensor.
	void writeStartupFilterBiasEstimate(
		const vn::math::vec3f &gyroBias,
		const vn::math::vec3f &accelBias,
		const float &pressureBias,
		bool waitForReply = true);

	/// \brief Reads the Delta Theta and Delta Velocity register.
	///
	/// \return The register's values.
	DeltaThetaAndDeltaVelocityRegister readDeltaThetaAndDeltaVelocity();

	/// \brief Reads the Delta Theta and Delta Velocity Configuration register.
	///
	/// \return The register's values.
	DeltaThetaAndDeltaVelocityConfigurationRegister readDeltaThetaAndDeltaVelocityConfiguration();

	/// \brief Writes to the Delta Theta and Delta Velocity Configuration register.
	///
	/// \param[in] fields The register's fields.
	/// \param[in] waitForReply Indicates if the method should wait for a response from the sensor.
	void writeDeltaThetaAndDeltaVelocityConfiguration(DeltaThetaAndDeltaVelocityConfigurationRegister &fields, bool waitForReply = true);

	/// \brief Writes to the Delta Theta and Delta Velocity Configuration register (deprecated).
	///
	/// \param[in] integrationFrame Value for the IntegrationFrame field.
	/// \param[in] gyroCompensation Value for the GyroCompensation field.
	/// \param[in] accelCompensation Value for the AccelCompensation field.
	/// \param[in] waitForReply Indicates if the method should wait for a response from the sensor.
	void writeDeltaThetaAndDeltaVelocityConfiguration(
		protocol::uart::IntegrationFrame integrationFrame,
		protocol::uart::CompensationMode gyroCompensation,
		protocol::uart::AccCompensationMode accelCompensation,
		bool waitForReply = true);

	/// \brief Writes to the Delta Theta and Delta Velocity Configuration register.
	///
	/// \param[in] integrationFrame Value for the IntegrationFrame field.
	/// \param[in] gyroCompensation Value for the GyroCompensation field.
	/// \param[in] accelCompensation Value for the AccelCompensation field.
	/// \param[in] earthRateCorrection Value for the EarthRateCorrection field.
	/// \param[in] waitForReply Indicates if the method should wait for a response from the sensor.
	void writeDeltaThetaAndDeltaVelocityConfiguration(
		protocol::uart::IntegrationFrame integrationFrame,
		protocol::uart::CompensationMode gyroCompensation,
		protocol::uart::AccCompensationMode accelCompensation,
		protocol::uart::EarthRateCorrection earthRateCorrection,
		bool waitForReply = true);

	/// \brief Reads the Reference Vector Configuration register.
	///
	/// \return The register's values.
	ReferenceVectorConfigurationRegister readReferenceVectorConfiguration();

	/// \brief Writes to the Reference Vector Configuration register.
	///
	/// \param[in] fields The register's fields.
	/// \param[in] waitForReply Indicates if the method should wait for a response from the sensor.
	void writeReferenceVectorConfiguration(ReferenceVectorConfigurationRegister &fields, bool waitForReply = true);

	/// \brief Writes to the Reference Vector Configuration register.
	///
	/// \param[in] useMagModel Value for the UseMagModel field.
	/// \param[in] useGravityModel Value for the UseGravityModel field.
	/// \param[in] recalcThreshold Value for the RecalcThreshold field.
	/// \param[in] year Value for the Year field.
	/// \param[in] position Value for the Position field.
	/// \param[in] waitForReply Indicates if the method should wait for a response from the sensor.
	void writeReferenceVectorConfiguration(
		const uint8_t &useMagModel,
		const uint8_t &useGravityModel,
		const uint32_t &recalcThreshold,
		const float &year,
		const vn::math::vec3d &position,
		bool waitForReply = true);

	/// \brief Reads the Gyro Compensation register.
	///
	/// \return The register's values.
	GyroCompensationRegister readGyroCompensation();

	/// \brief Writes to the Gyro Compensation register.
	///
	/// \param[in] fields The register's fields.
	/// \param[in] waitForReply Indicates if the method should wait for a response from the sensor.
	void writeGyroCompensation(GyroCompensationRegister &fields, bool waitForReply = true);

	/// \brief Writes to the Gyro Compensation register.
	///
	/// \param[in] c Value for the C field.
	/// \param[in] b Value for the B field.
	/// \param[in] waitForReply Indicates if the method should wait for a response from the sensor.
	void writeGyroCompensation(
		const vn::math::mat3f &c,
		const vn::math::vec3f &b,
		bool waitForReply = true);

	/// \brief Reads the IMU Filtering Configuration register.
	///
	/// \return The register's values.
	ImuFilteringConfigurationRegister readImuFilteringConfiguration();

	/// \brief Writes to the IMU Filtering Configuration register.
	///
	/// \param[in] fields The register's fields.
	/// \param[in] waitForReply Indicates if the method should wait for a response from the sensor.
	void writeImuFilteringConfiguration(ImuFilteringConfigurationRegister &fields, bool waitForReply = true);

	/// \brief Writes to the IMU Filtering Configuration register.
	///
	/// \param[in] magWindowSize Value for the MagWindowSize field.
	/// \param[in] accelWindowSize Value for the AccelWindowSize field.
	/// \param[in] gyroWindowSize Value for the GyroWindowSize field.
	/// \param[in] tempWindowSize Value for the TempWindowSize field.
	/// \param[in] presWindowSize Value for the PresWindowSize field.
	/// \param[in] magFilterMode Value for the MagFilterMode field.
	/// \param[in] accelFilterMode Value for the AccelFilterMode field.
	/// \param[in] gyroFilterMode Value for the GyroFilterMode field.
	/// \param[in] tempFilterMode Value for the TempFilterMode field.
	/// \param[in] presFilterMode Value for the PresFilterMode field.
	/// \param[in] waitForReply Indicates if the method should wait for a response from the sensor.
	void writeImuFilteringConfiguration(
		const uint16_t &magWindowSize,
		const uint16_t &accelWindowSize,
		const uint16_t &gyroWindowSize,
		const uint16_t &tempWindowSize,
		const uint16_t &presWindowSize,
		protocol::uart::FilterMode magFilterMode,
		protocol::uart::FilterMode accelFilterMode,
		protocol::uart::FilterMode gyroFilterMode,
		protocol::uart::FilterMode tempFilterMode,
		protocol::uart::FilterMode presFilterMode,
		bool waitForReply = true);

	/// \brief Reads the GPS Compass Baseline register.
	///
	/// \return The register's values.
	GpsCompassBaselineRegister readGpsCompassBaseline();

	/// \brief Writes to the GPS Compass Baseline register.
	///
	/// \param[in] fields The register's fields.
	/// \param[in] waitForReply Indicates if the method should wait for a response from the sensor.
	void writeGpsCompassBaseline(GpsCompassBaselineRegister &fields, bool waitForReply = true);

	/// \brief Writes to the GPS Compass Baseline register.
	///
	/// \param[in] position Value for the Position field.
	/// \param[in] uncertainty Value for the Uncertainty field.
	/// \param[in] waitForReply Indicates if the method should wait for a response from the sensor.
	void writeGpsCompassBaseline(
		const vn::math::vec3f &position,
		const vn::math::vec3f &uncertainty,
		bool waitForReply = true);

	/// \brief Reads the GPS Compass Estimated Baseline register.
	///
	/// \return The register's values.
	GpsCompassEstimatedBaselineRegister readGpsCompassEstimatedBaseline();

	/// \brief Reads the IMU Rate Configuration register.
	///
	/// \return The register's values.
	ImuRateConfigurationRegister readImuRateConfiguration();

	/// \brief Writes to the IMU Rate Configuration register.
	///
	/// \param[in] fields The register's fields.
	/// \param[in] waitForReply Indicates if the method should wait for a response from the sensor.
	void writeImuRateConfiguration(ImuRateConfigurationRegister &fields, bool waitForReply = true);

	/// \brief Writes to the IMU Rate Configuration register.
	///
	/// \param[in] imuRate Value for the imuRate field.
	/// \param[in] navDivisor Value for the NavDivisor field.
	/// \param[in] filterTargetRate Value for the filterTargetRate field.
	/// \param[in] filterMinRate Value for the filterMinRate field.
	/// \param[in] waitForReply Indicates if the method should wait for a response from the sensor.
	void writeImuRateConfiguration(
		const uint16_t &imuRate,
		const uint16_t &navDivisor,
		const float &filterTargetRate,
		const float &filterMinRate,
		bool waitForReply = true);

	/// \brief Reads the Yaw, Pitch, Roll, True Body Acceleration and Angular Rates register.
	///
	/// \return The register's values.
	YawPitchRollTrueBodyAccelerationAndAngularRatesRegister readYawPitchRollTrueBodyAccelerationAndAngularRates();

	/// \brief Reads the Yaw, Pitch, Roll, True Inertial Acceleration and Angular Rates register.
	///
	/// \return The register's values.
	YawPitchRollTrueInertialAccelerationAndAngularRatesRegister readYawPitchRollTrueInertialAccelerationAndAngularRates();

	/// \brief Upgrade the connected sensor with the supplied firmware file
	///
	/// \param[in] processor The target processor to switch to.
	/// \param[in] model		The model of sensor.
	/// \param[in] firmware	The current firmware version of sensor.
	void switchProcessors(VnProcessorType processor, std::string model, std::string firmware);

	/// \brief Upgrade the connected sensor with the supplied firmware file
	///
	/// \param]in] portName The baud rate used to update the firmware. (Can be different than the current baud rate)
	/// \param[in] filename The path to the VNX firmware file for the sensor.
	void firmwareUpdate(int baudRate, std::string fileName);

	/// \brief Upgrade the connected sensor with the supplied firmware file
	///
	/// \param]in] portName The baud rate used to update the firmware. (Can be different than the current baud rate)
	void setFirmwareUpdateBaudRate(int baudRate);

	/// \brief Get the next Firmware Update Record
	///
	/// \return The next record in the Firmware Update file, else an empty string
	std::string getNextFirwareUpdateRecord();

	/// \brief Open the firmware update file
	void openFirmwareUpdateFile(std::string filename);

	/// \brief Close the firmware update file
	void closeFirmwareUpdateFile();


	/// \}

	#ifdef PYTHON_WRAPPERS

	#endif

	#if PYTHON && !PL156_ORIGINAL && !PL156_FIX_ATTEMPT_1

	void stopRequest();
	bool threadStopped();
	void unregisterListners();
	void shutdownRequest();
	void goRequest();

	#endif

private:
	struct Impl;
	Impl *_pi;

	// For Firmware Update Files
	FILE* firmwareUpdateFile;
	bool bootloaderFiltering;


};

}
}

#endif
