/********************************************************************************
* FaceTrackNoIR This program is a private project of some enthusiastic *
* gamers from Holland, who don't like to pay much for *
* head-tracking. *
* *
* Copyright (C) 2012 Wim Vriend (Developing) *
* Ron Hendriks (Researching and Testing) *
* Homepage: http://facetracknoir.sourceforge.net/home/default.htm *
* *
* Copyright (C) 2012 FuraX49 (HAT Tracker plugins) *
* Homepage: http://hatire.sourceforge.net *
* *
* *
* 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 . *
* *
********************************************************************************/
#include "ftnoir_tracker_hat.h"
FTNoIR_Tracker::FTNoIR_Tracker()
{
ComPort = NULL;
HAT.Rot[0]=0;
HAT.Rot[1]=0;
HAT.Rot[2]=0;
HAT.Trans[0]=0;
HAT.Trans[1]=0;
HAT.Trans[2]=0;
// prepare & reserve QByteArray
dataRead.resize(4096);
dataRead.clear();
Begin.append((char) 0xAA);
Begin.append((char) 0xAA);
End.append((char) 0x55);
End.append((char) 0x55);
}
FTNoIR_Tracker::~FTNoIR_Tracker()
{
if (ComPort!=NULL) {
if (ComPort->isOpen() ) {
ComPort->close();
}
delete ComPort;
ComPort=NULL;
}
}
//send CENTER to Arduino
void FTNoIR_Tracker::notifyCenter() {
sendcmd(static_cast(settings.CmdCenter).toLatin1());
}
//send ZERO to Arduino
bool FTNoIR_Tracker::notifyZeroed() {
sendcmd(static_cast(settings.CmdZero).toLatin1());
return true;
}
//send RESET to Arduino
void FTNoIR_Tracker::reset() {
sendcmd(static_cast(settings.CmdReset).toLatin1());
}
// Info SerialPort
void FTNoIR_Tracker::SerialInfo() {
QByteArray Msg;
if (ComPort!=NULL) {
if (ComPort->isOpen() ) {
Msg.append("\r\n");
Msg.append(ComPort->portName());
Msg.append("\r\n");
Msg.append("BAUDRATE :");
Msg.append(QString::number(ComPort->baudRate()));
Msg.append("\r\n");
Msg.append("DataBits :");
Msg.append(QString::number(ComPort->dataBits()));
Msg.append("\r\n");
Msg.append("Parity :");
switch (ComPort->parity()) {
case 0: Msg.append("No parity");
break;
case 2: Msg.append("Even parity");
break;
case 3: Msg.append("Odd parity");
break;
case 4: Msg.append("Space parity");
break;
case 5: Msg.append("Mark parity");
break;
default: Msg.append("Unknown parity");
break;
}
Msg.append("\r\n");
Msg.append("Stop Bits :");
switch (ComPort->stopBits()) {
Msg.append(QString::number(ComPort->stopBits()));
case 1: Msg.append("1 stop bit.");
break;
case 2: Msg.append("2 stop bits.");
break;
case 3: Msg.append("1.5 stop bits.");
break;
default: Msg.append("Unknown number of stop bit.");
break;
}
Msg.append("\r\n");
Msg.append("Flow Control :");
switch (ComPort->flowControl()) {
case 0: Msg.append("No flow control");
break;
case 1: Msg.append("Hardware flow control (RTS/CTS)");
break;
case 2: Msg.append("Software flow control (XON/XOFF)");
break;
default: Msg.append("Unknown flow control");
break;
}
emit sendMsgInfo(Msg);
}
}
}
//send command to Arduino
void FTNoIR_Tracker::sendcmd(const QByteArray &cmd) {
QByteArray Msg;
if (cmd.length()>0) {
if (ComPort->isOpen() )
{
ComPort->write(cmd);
if (!ComPort->waitForBytesWritten(1000)) {
emit sendMsgInfo("TimeOut in writing CMD");
} else {
Msg.append("\r\n");
Msg.append("SEND '");
Msg.append(cmd);
Msg.append("'\r\n");
}
if ( !ComPort->waitForReadyRead(1000)) {
emit sendMsgInfo("TimeOut in response to CMD") ;
} else {
emit sendMsgInfo(Msg);
}
} else {
emit sendMsgInfo("ComPort not open") ;
}
}
}
// return FPS
void FTNoIR_Tracker::get_info( int *tps ){
*tps=frame_cnt;
frame_cnt=0;
}
void FTNoIR_Tracker::SerialRead()
{
QMutexLocker lck(&mutex);
dataRead+=ComPort->readAll();
}
#ifndef OPENTRACK_API
void FTNoIR_Tracker::Initialize( QFrame *videoframe )
{
CptError=0;
dataRead.clear();
frame_cnt=0;
settings.load_ini();
applysettings(settings);
ComPort = new QSerialPort(this);
ComPort->setPortName(sSerialPortName);
if (ComPort->open(QIODevice::ReadWrite ) == true) {
connect(ComPort, SIGNAL(readyRead()), this, SLOT(SerialRead()));
if (
ComPort->setBaudRate((QSerialPort::BaudRate)iBaudRate)
&& ComPort->setDataBits((QSerialPort::DataBits)iDataBits)
&& ComPort->setParity((QSerialPort::Parity)iParity)
&& ComPort->setStopBits((QSerialPort::StopBits)iStopBits)
&& ComPort->setFlowControl((QSerialPort::FlowControl)iFlowControl)
&& ComPort->clear(QSerialPort::AllDirections)
&& ComPort->setDataErrorPolicy(QSerialPort::IgnorePolicy)
) {
// Wait init arduino sequence
for (int i = 1; i <=iDelayInit; i+=50) {
if (ComPort->waitForReadyRead(50)) break;
}
sendcmd(sCmdInit);
// Wait init MPU sequence
for (int i = 1; i <=iDelayStart; i+=50) {
if (ComPort->waitForReadyRead(50)) break;
}
} else {
QMessageBox::warning(0,"FaceTrackNoIR Error", ComPort->errorString(),QMessageBox::Ok,QMessageBox::NoButton);
}
}
else {
QMessageBox::warning(0,"FaceTrackNoIR Error", "Unable to open ComPort",QMessageBox::Ok,QMessageBox::NoButton);
delete ComPort;
ComPort = NULL;
}
return;
}
void FTNoIR_Tracker::StartTracker(HWND parent_window)
{
// Send START cmd to IMU
sendcmd(sCmdStart);
// Wait start MPU sequence
for (int i = 1; i <=iDelaySeq; i+=50) {
if (ComPort->waitForReadyRead(50)) break;
}
return;
}
void FTNoIR_Tracker::StopTracker( bool exit )
{
QByteArray Msg;
if (sCmdStop.length()>0) {
if (ComPort->isOpen() )
{
ComPort->write(sCmdStop);
if (!ComPort->waitForBytesWritten(1000)) {
emit sendMsgInfo("TimeOut in writing CMD");
} else {
Msg.append("\r\n");
Msg.append("SEND '");
Msg.append(sCmdStop);
Msg.append("'\r\n");
}
emit sendMsgInfo(Msg);
}
}
// OK, the thread is not stopped, doing this. That might be dangerous anyway...
//
if (exit || !exit) return;
return;
}
#else
void FTNoIR_Tracker::StartTracker(QFrame*)
{
static const int databits_lookup[] = {
5,
6,
7,
8,
-1
};
struct Local {
static int idx(int max, int value)
{
if (value < 0)
return 0;
if (max > value)
return value;
return max - 1;
}
};
static const int parity_lookup[] = {
QSerialPort::NoParity,
QSerialPort::EvenParity,
QSerialPort::OddParity,
QSerialPort::SpaceParity,
QSerialPort::MarkParity,
QSerialPort::UnknownParity
};
static const int stopbits_lookup[] = {
QSerialPort::OneStop,
QSerialPort::OneAndHalfStop,
QSerialPort::TwoStop,
QSerialPort::UnknownStopBits
};
static const int flowctl_lookup[] = {
QSerialPort::NoFlowControl,
QSerialPort::HardwareControl,
QSerialPort::SoftwareControl,
};
static const int baudrate_lookup[] = {
QSerialPort::Baud1200,
QSerialPort::Baud2400,
QSerialPort::Baud4800,
QSerialPort::Baud9600,
QSerialPort::Baud19200,
QSerialPort::Baud38400,
QSerialPort::Baud57600,
QSerialPort::Baud115200,
QSerialPort::UnknownBaud
};
CptError=0;
dataRead.clear();
frame_cnt=0;
ComPort = new QSerialPort(this);
{
ComPort->setPortName(QSerialPortInfo::availablePorts().value(settings.SerialPortName).portName());
}
if (ComPort->open(QIODevice::ReadWrite ) == true) {
connect(ComPort, SIGNAL(readyRead()), this, SLOT(SerialRead()));
if (
ComPort->setBaudRate(baudrate_lookup[Local::idx(8, settings.pBaudRate)])
&& ComPort->setDataBits((QSerialPort::DataBits)databits_lookup[Local::idx(4, settings.pDataBits)])
&& ComPort->setParity((QSerialPort::Parity)parity_lookup[Local::idx(5, settings.pParity)])
&& ComPort->setStopBits((QSerialPort::StopBits)stopbits_lookup[Local::idx(3, settings.pStopBits)])
&& ComPort->setFlowControl((QSerialPort::FlowControl)flowctl_lookup[Local::idx(3, settings.pFlowControl)])
&& ComPort->clear(QSerialPort::AllDirections)
&& ComPort->setDataErrorPolicy(QSerialPort::IgnorePolicy)
){
// Wait init arduino sequence
for (int i = 1; i <=settings.DelayInit; i+=50) {
if (ComPort->waitForReadyRead(50)) break;
}
sendcmd(static_cast(settings.CmdInit).toLatin1());
// Wait init MPU sequence
for (int i = 1; i <=settings.DelayStart; i+=50) {
if (ComPort->waitForReadyRead(50)) break;
}
// Send START cmd to IMU
sendcmd(static_cast(settings.CmdStart).toLatin1());
// Wait start MPU sequence
for (int i = 1; i <=settings.DelaySeq; i+=50) {
if (ComPort->waitForReadyRead(50)) break;
}
} else {
QMessageBox::warning(0,"FaceTrackNoIR Error", ComPort->errorString(),QMessageBox::Ok,QMessageBox::NoButton);
}
}
else {
QMessageBox::warning(0,"FaceTrackNoIR Error", "Unable to open ComPort",QMessageBox::Ok,QMessageBox::NoButton);
delete ComPort;
ComPort = NULL;
}
return;
}
#endif
//
// Return 6DOF info
//
#ifdef OPENTRACK_API
#define THeadPoseData double
#endif
void FTNoIR_Tracker::GetHeadPoseData(THeadPoseData *data)
{
QMutexLocker lck(&mutex);
while (dataRead.length()>=30) {
if ((dataRead.startsWith(Begin) && ( dataRead.mid(28,2)==End )) ) { // .Begin==0xAAAA .End==0x5555
QDataStream datastream(dataRead.left(30));
if (settings.BigEndian) datastream.setByteOrder(QDataStream::BigEndian );
else datastream.setByteOrder(QDataStream::LittleEndian );
datastream>>ArduinoData;
frame_cnt++;
if (ArduinoData.Code <= 1000) {
HAT=ArduinoData;
} else {
emit sendMsgInfo(dataRead.mid(4,24)) ;
}
dataRead.remove(0,30);
} else {
// resynchro trame
int index = dataRead.indexOf(Begin);
if (index==-1) {
index=dataRead.length();
}
emit sendMsgInfo(dataRead.mid(0,index)) ;
dataRead.remove(0,index);
CptError++;
}
}
if (CptError>50) {
emit sendMsgInfo("Can't find HAT frame") ;
CptError=0;
return;
}
#ifdef OPENTRACK_API
data[frame_cnt] = (long) HAT.Code;
struct Fun {
static int clamp3(int foo)
{
if (foo > 2)
return 2;
if (foo < 0)
return 0;
return foo;
}
};
if (settings.EnableYaw) {
if (settings.InvertYaw) data[Yaw] = (double) HAT.Rot[Fun::clamp3(settings.YawAxe)] * -1.0f;
else data[Yaw] = (double) HAT.Rot[Fun::clamp3(settings.YawAxe)];
}
if (settings.EnablePitch) {
if (settings.InvertPitch) data[Pitch] = (double) HAT.Rot[Fun::clamp3(settings.PitchAxe)] * -1.0f;
else data[Pitch] = (double) HAT.Rot[Fun::clamp3(settings.InvertPitch)];
}
if (settings.EnableRoll) {
if (settings.InvertRoll) data[Roll] = (double) HAT.Rot[Fun::clamp3(settings.RollAxe)] * -1.0f;
else data[Roll] = (double) HAT.Rot[Fun::clamp3(settings.RollAxe)];
}
if (settings.EnableX) {
if (settings.InvertX) data[TX] =(double) HAT.Trans[Fun::clamp3(settings.XAxe)]* -1.0f;
else data[TX] = HAT.Trans[Fun::clamp3(settings.XAxe)];
}
if (settings.EnableY) {
if (settings.InvertY) data[TY] =(double) HAT.Trans[Fun::clamp3(settings.YAxe)]* -1.0f;
else data[TY] = HAT.Trans[Fun::clamp3(settings.YAxe)];
}
if (settings.EnableZ) {
if (settings.InvertZ) data[TZ] = HAT.Trans[Fun::clamp3(settings.ZAxe)]* -1.0f;
else data[TZ] = HAT.Trans[Fun::clamp3(settings.ZAxe)];
}
#else
data->frame_number = (long) HAT.Code;
if (bEnableYaw) {
if (bInvertYaw ) data->yaw = (double) HAT.Rot[iYawAxe] * -1.0f;
else data->yaw = (double) HAT.Rot[iYawAxe];
}
if (bEnablePitch) {
if (bInvertPitch)data->pitch = (double) HAT.Rot[iPitchAxe] * -1.0f;
else data->pitch = (double) HAT.Rot[iPitchAxe];
}
if (bEnableRoll) {
if (bInvertRoll) data->roll = (double) HAT.Rot[iRollAxe] * -1.0f;
else data->roll = (double) HAT.Rot[iRollAxe];
}
if (bEnableX) {
if (bInvertX) data->x = (double) HAT.Trans[iXAxe]* -1.0f;
else data->x = (double) HAT.Trans[iXAxe];
}
if (bEnableY) {
if (bInvertY) data->y = (double) HAT.Trans[iYAxe]* -1.0f;
else data->y = (double) HAT.Trans[iYAxe];
}
if (bEnableZ) {
if (bInvertZ) data->z = (double) HAT.Trans[iZAxe]* -1.0f;
else data->z = (double) HAT.Trans[iZAxe];
}
#endif
}
void FTNoIR_Tracker::applysettings(const TrackerSettings& settings){
QMutexLocker lck(&mutex);
settings.b->reload();
}
#ifdef OPENTRACK_API
extern "C" FTNOIR_TRACKER_BASE_EXPORT ITracker* CALLING_CONVENTION GetConstructor()
#else
#pragma comment(linker, "/export:GetTracker=_GetTracker@0")
FTNOIR_TRACKER_BASE_EXPORT ITrackerPtr __stdcall GetTracker()
#endif
{
return new FTNoIR_Tracker;
}