Skip to content

Pull Request for AHRS IO Processor #16

Merged
merged 16 commits into from May 11, 2020
23 changes: 23 additions & 0 deletions bfs/implementations/breadcrumbs/gen/AHRSInputIOProcessor.cpp
@@ -0,0 +1,23 @@

#include <iostream>

#include "DataSyncThread.hpp"
#include "AHRSInputIOProcessor.hpp"


int main()
{
IOProcessor* client = new AHRSInputIOProcessor;

if (!client->init())
{
while (client->loopCondition())
client->loop();

int result = client->close();
delete client;
return result;
}

return 0;
}
38 changes: 38 additions & 0 deletions bfs/implementations/breadcrumbs/include/AHRSInputIOProcessor.hpp
@@ -0,0 +1,38 @@
/*
*
* This is the IO Processor for the Xsens MTi-630 AHRS:
* https://www.xsens.com/products/mti-600-series
* The AHRS is able to calculate a lot of different orientational data,
* we are specfically using it to find the Yaw Euler Angle.
* Currently you must manually specify which COM port the device is connected
* to, this is right at the begining of the CreateFile() method call.
* By defualt it is set to COM8, you may have to change it to the correct port
* on your system. You can find the corrct port manually by opening Device Manager,
* under "Ports(COM & LPT)" you can see the ports detected by your system.
*/

#ifndef AHRS_INPUT_IO_PROCESSOR_HPP
#define AHRS_INPUT_IO_PROCESSOR_HPP

#include "IOProcessor.hpp"
#include "DataSyncThread.hpp"
#include "Attribute.hpp"

#include <windows.h>
#include <iostream>

class AHRSInputIOProcessor : public IOProcessor
{
public:
using IOProcessor::IOProcessor;

void loop();
bool loopCondition();
int configAHRS(); // Initialize the AHRS for the first time
private:
int iterations = 10;
bool configured = false;
HANDLE hSerial;
};

#endif
184 changes: 184 additions & 0 deletions bfs/implementations/breadcrumbs/io_procs/AHRSInputIOProcessor.cpp
@@ -0,0 +1,184 @@
#include "AHRSInputIOProcessor.hpp"

/*
* Return Values:
* 0 : No Errors.
* 1 : Serial port does not exist.
* 2 : Some other error occurred.
* 3 : Error getting state.
* 4 : Error setting serial port state
* 5 : Error occured while setting timoemouts.
* 6 : Error occured while trying to send GoToConfig message.
* 7 : Error occured while trying to send SetOutputConfiguration message.
* 8 : Error occured while trying to send GoToMeasurment message.
*/
int AHRSInputIOProcessor::configAHRS()
{
hSerial = CreateFile("COM8", // The COM port to connect to, may have to be changed
GENERIC_READ | GENERIC_WRITE, // depending on what COM port gets selected.
0,
0,
OPEN_EXISTING,
FILE_ATTRIBUTE_NORMAL,
0);

if (hSerial == INVALID_HANDLE_VALUE)
{
if(GetLastError() == ERROR_FILE_NOT_FOUND)
{
std::cerr << "Serial port does not exist." << std::endl;
return 1;
}
std::cerr << "Some other error occurred." << std::endl;
return 2;
}

DCB dcb = { 0 };
dcb.DCBlength = sizeof(dcb);

if (!GetCommState(hSerial, &dcb))
{
std::cerr << "Error getting state." << std::endl;
return 3;
}

dcb.BaudRate = CBR_115200; // defualt baud rate of MT
dcb.ByteSize = 8;
dcb.StopBits = ONESTOPBIT;
dcb.Parity = NOPARITY;

if (!SetCommState(hSerial, &dcb))
{
std::cerr << "Error setting serial port state." << std::endl;
return 4;
}

COMMTIMEOUTS timeouts = {0};

timeouts.ReadIntervalTimeout = 50;
timeouts.ReadTotalTimeoutConstant = 50;
timeouts.ReadTotalTimeoutMultiplier = 10;
timeouts.WriteTotalTimeoutConstant = 50;
timeouts.WriteTotalTimeoutMultiplier = 10;

if (!SetCommTimeouts(hSerial, &timeouts))
{
std::cerr << "Error occured while setting timoemouts." << std::endl;
return 5;
}

std::cout << "Connection established!" << std::endl;
std::cout << "Setting up Config..." << std::endl;

// This is the GoToConfig message, MID 48
// Switch the active state of the device from Measurment State to Config State.
unsigned char wBuff1[] = {0xFA,0xFF,0x30,0x00,0xD1};
DWORD dwBytesWritten = 0; // Number of bytes actually written

if (!WriteFile(hSerial, wBuff1, 6, &dwBytesWritten, NULL))
{
std::cerr << "Error occured while trying to send GoToConfig message." << std::endl;
return 6;
}

// This is the SetOutputConfiguration message, MID 192
// Set the output configuration of the device.
unsigned char wBuff2[] = {0xFA,0xFF,0xC0,0x04,0x20,0x33,0x00,0x00,0xEA};
dwBytesWritten = 0;

if (!WriteFile(hSerial, wBuff2, 10, &dwBytesWritten, NULL))
{
std::cerr << "Error occured while trying to send SetOutputConfiguration message." << std::endl;
return 7;
}

// This is the GoToMeasurment message, MID 16
// Switch the active state of the device from Config State to Measurment State.
unsigned char wBuff3[] = {0xFA,0xFF,0x10,0x00,0xF1};
dwBytesWritten = 0;

if (!WriteFile(hSerial, wBuff3, 6, &dwBytesWritten, NULL))
{
std::cerr << "Error occured while trying to send GoToMeasurment message." << std::endl;
return 8;
}

configured = true;
return 0;
}

bool AHRSInputIOProcessor::loopCondition()
{
return true;
}

void AHRSInputIOProcessor::loop()
{
if (!configured)
configAHRS();

unsigned char szBuff[64 + 1] = {0}; // Buffer size
DWORD dwBytesRead = 0; // Number of bytes actually read

if (!ReadFile(hSerial, szBuff, 64, &dwBytesRead, NULL))
{
std::cerr << "Error occured while trying to read bytes." << std::endl;
}

/* This gets the index of the bytes for the Euler angles in the captured
* buffer (szBuff). First, it finds the first byte of the message (i). Then,
* it finds the 14th index of the message (dataStart). This is where the last
* byte of the roll angle is. This allows me loop backward through each group
* of bytes, since the order needs to be reversed because the AHRS is in Big
* Endian.

* i is where the message starts in the buffer.
* dataStart = i + 14, which is where the data we want starts (for Roll).
* To find Pitch and Yaw, we add 8 and 16 respectivly.
*
* MESSAGE STRUCTURE:
* Roll Angle Bytes = RX, Pitch Angle Bytes = PX, Yaw Angle Bytes = YX
* +-------------------------+-------------------------+-------------------------+
* | Roll | Pitch | Yaw |
* +-------------------------+-------------------------+-------------------------+
* | R1 R2 R3 R4 R5 R6 R7 R8 | P1 P2 P3 P4 P5 P6 P7 P8 | Y1 Y2 Y3 Y4 Y5 Y6 Y7 Y8 |
* +-------------------------+-------------------------+-------------------------+
* ... | 7 8 9 10 11 12 13 14 | 15 16 17 18 19 20 21 22 | 23 24 25 26 27 28 29 30 | ...
* | ^ | ^ | ^ |
* | dataStart| dataStart + 8| dataStart + 16|
* +-------------------------+-------------------------+-------------------------+
*/
int dataStart = -1;
for (int i=0; i < 32; i++)
{
if (szBuff[i] == 0xFA && szBuff[i+1] == 0xFF && szBuff[i+2] == 0x36)
{
dataStart = i + 14;
break;
}
}

unsigned char rollArray[8];
unsigned char pitchArray[8];
unsigned char yawArray[8];
// Puts the bytes for each angle in their own array backwards, since they are sent in big endian
if (dataStart != -1)
{
for (int i = 0; i < 8; i++)
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Instead of a forward for loop, wouldn't a reverese for loop be more intuitive: for (i = 7; i >= 0; i--)

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

In some ways yes, I could start at the actual start of the data, but there would still be some funky arithmetic going on.
It would be:
dataStart = i + 7;
for (int i = 7; i > -1; i--)
{
rollArray[i] = szBuff[dataStart + i];
pitchArray[i] = szBuff[dataStart + i + 8];
yawArray[i] = szBuff[dataStart + i + 16];
}

Does that actually make it clearer?

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It just comes down to individual preference with the equations if they make more sense...

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It changes the - and + into just two +'s which is a bit more clearer.

{
rollArray[i] = szBuff[dataStart - i];
pitchArray[i] = szBuff[dataStart - i + 8];
yawArray[i] = szBuff[dataStart - i + 16];
}
}

double doubleRoll = *reinterpret_cast<double* const>(rollArray);
double doublePitch = *reinterpret_cast<double* const>(pitchArray);
double doubleYaw = *reinterpret_cast<double * const>(yawArray);
Attribute attRoll("XSIMUROL", 8, &doubleRoll);
getComms()->sendAttribute(attRoll);
Attribute attPitch("XSIMUPIT", 8, &doublePitch);
getComms()->sendAttribute(attPitch);
Attribute attYaw("XSIMUYAW", 8, &doubleYaw);
getComms()->sendAttribute(attYaw);
}