mirror of
https://bitbucket.org/librepilot/librepilot.git
synced 2024-12-10 18:24:11 +01:00
Extended HiTL API to the rest of the simulators.
This commit is contained in:
parent
cb377e0f1d
commit
d9139c6529
@ -34,9 +34,6 @@
|
|||||||
#define M_PI 3.14159265358979323846
|
#define M_PI 3.14159265358979323846
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
//FGSimulator::FGSimulator(QString hostAddr, int outPort, int inPort, bool manual, QString binPath, QString dataPath) :
|
//FGSimulator::FGSimulator(QString hostAddr, int outPort, int inPort, bool manual, QString binPath, QString dataPath) :
|
||||||
// Simulator(hostAddr, outPort, inPort, manual, binPath, dataPath),
|
// Simulator(hostAddr, outPort, inPort, manual, binPath, dataPath),
|
||||||
// fgProcess(NULL)
|
// fgProcess(NULL)
|
||||||
@ -50,7 +47,7 @@
|
|||||||
//}
|
//}
|
||||||
|
|
||||||
FGSimulator::FGSimulator(const SimulatorSettings& params) :
|
FGSimulator::FGSimulator(const SimulatorSettings& params) :
|
||||||
Simulator(params)
|
Simulator(params)
|
||||||
{
|
{
|
||||||
udpCounterFGrecv = 0;
|
udpCounterFGrecv = 0;
|
||||||
udpCounterGCSsend = 0;
|
udpCounterGCSsend = 0;
|
||||||
@ -58,13 +55,11 @@ FGSimulator::FGSimulator(const SimulatorSettings& params) :
|
|||||||
|
|
||||||
FGSimulator::~FGSimulator()
|
FGSimulator::~FGSimulator()
|
||||||
{
|
{
|
||||||
disconnect(simProcess, SIGNAL(readyReadStandardOutput()), this, SLOT(processReadyRead()));
|
disconnect(simProcess, SIGNAL(readyReadStandardOutput()), this, SLOT(processReadyRead()));
|
||||||
}
|
}
|
||||||
|
|
||||||
void FGSimulator::setupUdpPorts(const QString& host, int inPort, int outPort)
|
void FGSimulator::setupUdpPorts(const QString& host, int inPort, int outPort)
|
||||||
{
|
{
|
||||||
Q_UNUSED(outPort);
|
|
||||||
|
|
||||||
if(inSocket->bind(QHostAddress(host), inPort))
|
if(inSocket->bind(QHostAddress(host), inPort))
|
||||||
emit processOutput("Successfully bound to address " + host + " on port " + QString::number(inPort) + "\n");
|
emit processOutput("Successfully bound to address " + host + " on port " + QString::number(inPort) + "\n");
|
||||||
else
|
else
|
||||||
@ -73,31 +68,31 @@ void FGSimulator::setupUdpPorts(const QString& host, int inPort, int outPort)
|
|||||||
|
|
||||||
bool FGSimulator::setupProcess()
|
bool FGSimulator::setupProcess()
|
||||||
{
|
{
|
||||||
QMutexLocker locker(&lock);
|
QMutexLocker locker(&lock);
|
||||||
|
|
||||||
// Copy FlightGear generic protocol configuration file to the FG protocol directory
|
// Copy FlightGear generic protocol configuration file to the FG protocol directory
|
||||||
// NOTE: Not working on Windows 7, if FG is installed in the "Program Files",
|
// NOTE: Not working on Windows 7, if FG is installed in the "Program Files",
|
||||||
// likelly due to permissions. The file should be manually copied to data/Protocol/opfgprotocol.xml
|
// likelly due to permissions. The file should be manually copied to data/Protocol/opfgprotocol.xml
|
||||||
// QFile xmlFile(":/flightgear/genericprotocol/opfgprotocol.xml");
|
// QFile xmlFile(":/flightgear/genericprotocol/opfgprotocol.xml");
|
||||||
// xmlFile.open(QIODevice::ReadOnly | QIODevice::Text);
|
// xmlFile.open(QIODevice::ReadOnly | QIODevice::Text);
|
||||||
// QString xml = xmlFile.readAll();
|
// QString xml = xmlFile.readAll();
|
||||||
// xmlFile.close();
|
// xmlFile.close();
|
||||||
// QFile xmlFileOut(pathData + "/Protocol/opfgprotocol.xml");
|
// QFile xmlFileOut(pathData + "/Protocol/opfgprotocol.xml");
|
||||||
// xmlFileOut.open(QIODevice::WriteOnly | QIODevice::Text);
|
// xmlFileOut.open(QIODevice::WriteOnly | QIODevice::Text);
|
||||||
// xmlFileOut.write(xml.toAscii());
|
// xmlFileOut.write(xml.toAscii());
|
||||||
// xmlFileOut.close();
|
// xmlFileOut.close();
|
||||||
|
|
||||||
Qt::HANDLE mainThread = QThread::currentThreadId();
|
Qt::HANDLE mainThread = QThread::currentThreadId();
|
||||||
qDebug() << "setupProcess Thread: "<< mainThread;
|
qDebug() << "setupProcess Thread: "<< mainThread;
|
||||||
|
|
||||||
simProcess = new QProcess();
|
simProcess = new QProcess();
|
||||||
simProcess->setReadChannelMode(QProcess::MergedChannels);
|
simProcess->setReadChannelMode(QProcess::MergedChannels);
|
||||||
connect(simProcess, SIGNAL(readyReadStandardOutput()), this, SLOT(processReadyRead()));
|
connect(simProcess, SIGNAL(readyReadStandardOutput()), this, SLOT(processReadyRead()));
|
||||||
// Note: Only tested on windows 7
|
// Note: Only tested on windows 7
|
||||||
#if defined(Q_WS_WIN)
|
#if defined(Q_WS_WIN)
|
||||||
QString cmdShell("c:/windows/system32/cmd.exe");
|
QString cmdShell("c:/windows/system32/cmd.exe");
|
||||||
#else
|
#else
|
||||||
QString cmdShell("bash");
|
QString cmdShell("bash");
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
// Start shell (Note: Could not start FG directly on Windows, only through terminal!)
|
// Start shell (Note: Could not start FG directly on Windows, only through terminal!)
|
||||||
@ -139,19 +134,19 @@ bool FGSimulator::setupProcess()
|
|||||||
"Remote computer must have the correct OpenPilot protocol installed.");
|
"Remote computer must have the correct OpenPilot protocol installed.");
|
||||||
}
|
}
|
||||||
|
|
||||||
udpCounterGCSsend = 0;
|
udpCounterGCSsend = 0;
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
void FGSimulator::processReadyRead()
|
void FGSimulator::processReadyRead()
|
||||||
{
|
{
|
||||||
QByteArray bytes = simProcess->readAllStandardOutput();
|
QByteArray bytes = simProcess->readAllStandardOutput();
|
||||||
QString str(bytes);
|
QString str(bytes);
|
||||||
if ( !str.contains("Error reading data") ) // ignore error
|
if ( !str.contains("Error reading data") ) // ignore error
|
||||||
{
|
{
|
||||||
emit processOutput(str);
|
emit processOutput(str);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void FGSimulator::transmitUpdate()
|
void FGSimulator::transmitUpdate()
|
||||||
@ -178,7 +173,7 @@ void FGSimulator::transmitUpdate()
|
|||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
// Read ActuatorDesired from autopilot
|
// Read ActuatorDesired from autopilot
|
||||||
actData = actDesired->getData();
|
actData = actDesired->getData();
|
||||||
|
|
||||||
ailerons = actData.Roll;
|
ailerons = actData.Roll;
|
||||||
@ -198,16 +193,16 @@ void FGSimulator::transmitUpdate()
|
|||||||
{
|
{
|
||||||
udpCounterGCSsend++;
|
udpCounterGCSsend++;
|
||||||
|
|
||||||
// Send update to FlightGear
|
// Send update to FlightGear
|
||||||
QString cmd;
|
QString cmd;
|
||||||
cmd = QString("%1,%2,%3,%4,%5\n")
|
cmd = QString("%1,%2,%3,%4,%5\n")
|
||||||
.arg(ailerons) //ailerons
|
.arg(ailerons) //ailerons
|
||||||
.arg(elevator) //elevator
|
.arg(elevator) //elevator
|
||||||
.arg(rudder) //rudder
|
.arg(rudder) //rudder
|
||||||
.arg(throttle) //throttle
|
.arg(throttle) //throttle
|
||||||
.arg(udpCounterGCSsend); //UDP packet counter delay
|
.arg(udpCounterGCSsend); //UDP packet counter delay
|
||||||
|
|
||||||
QByteArray data = cmd.toAscii();
|
QByteArray data = cmd.toAscii();
|
||||||
|
|
||||||
if(outSocket->writeDatagram(data, QHostAddress(settings.remoteAddress), settings.outPort) == -1)
|
if(outSocket->writeDatagram(data, QHostAddress(settings.remoteAddress), settings.outPort) == -1)
|
||||||
{
|
{
|
||||||
@ -237,169 +232,116 @@ void FGSimulator::transmitUpdate()
|
|||||||
void FGSimulator::processUpdate(const QByteArray& inp)
|
void FGSimulator::processUpdate(const QByteArray& inp)
|
||||||
{
|
{
|
||||||
//TODO: this does not use the FLIGHT_PARAM structure, it should!
|
//TODO: this does not use the FLIGHT_PARAM structure, it should!
|
||||||
static char once=0;
|
// Split
|
||||||
// Split
|
QString data(inp);
|
||||||
QString data(inp);
|
QStringList fields = data.split(",");
|
||||||
QStringList fields = data.split(",");
|
// Get xRate (deg/s)
|
||||||
// Get xRate (deg/s)
|
// float xRate = fields[0].toFloat() * 180.0/M_PI;
|
||||||
// float xRate = fields[0].toFloat() * 180.0/M_PI;
|
// Get yRate (deg/s)
|
||||||
// Get yRate (deg/s)
|
// float yRate = fields[1].toFloat() * 180.0/M_PI;
|
||||||
// float yRate = fields[1].toFloat() * 180.0/M_PI;
|
// Get zRate (deg/s)
|
||||||
// Get zRate (deg/s)
|
// float zRate = fields[2].toFloat() * 180.0/M_PI;
|
||||||
// float zRate = fields[2].toFloat() * 180.0/M_PI;
|
// Get xAccel (m/s^2)
|
||||||
// Get xAccel (m/s^2)
|
float xAccel = fields[3].toFloat() * FT2M;
|
||||||
// float xAccel = fields[3].toFloat() * FT2M;
|
// Get yAccel (m/s^2)
|
||||||
// Get yAccel (m/s^2)
|
float yAccel = fields[4].toFloat() * FT2M;
|
||||||
// float yAccel = fields[4].toFloat() * FT2M;
|
// Get xAccel (m/s^2)
|
||||||
// Get xAccel (m/s^2)
|
float zAccel = fields[5].toFloat() * FT2M;
|
||||||
// float zAccel = fields[5].toFloat() * FT2M;
|
// Get pitch (deg)
|
||||||
// Get pitch (deg)
|
float pitch = fields[6].toFloat();
|
||||||
float pitch = fields[6].toFloat();
|
// Get pitchRate (deg/s)
|
||||||
// Get pitchRate (deg/s)
|
float pitchRate = fields[7].toFloat();
|
||||||
float pitchRate = fields[7].toFloat();
|
// Get roll (deg)
|
||||||
// Get roll (deg)
|
float roll = fields[8].toFloat();
|
||||||
float roll = fields[8].toFloat();
|
// Get rollRate (deg/s)
|
||||||
// Get rollRate (deg/s)
|
float rollRate = fields[9].toFloat();
|
||||||
float rollRate = fields[9].toFloat();
|
// Get yaw (deg)
|
||||||
// Get yaw (deg)
|
float yaw = fields[10].toFloat();
|
||||||
float yaw = fields[10].toFloat();
|
// Get yawRate (deg/s)
|
||||||
// Get yawRate (deg/s)
|
float yawRate = fields[11].toFloat();
|
||||||
float yawRate = fields[11].toFloat();
|
// Get latitude (deg)
|
||||||
// Get latitude (deg)
|
float latitude = fields[12].toFloat();
|
||||||
float latitude = fields[12].toFloat();
|
// Get longitude (deg)
|
||||||
// Get longitude (deg)
|
float longitude = fields[13].toFloat();
|
||||||
float longitude = fields[13].toFloat();
|
// Get heading (deg)
|
||||||
// Get heading (deg)
|
float heading = fields[14].toFloat();
|
||||||
float heading = fields[14].toFloat();
|
// Get altitude (m)
|
||||||
// Get altitude (m)
|
float altitude = fields[15].toFloat() * FT2M;
|
||||||
float altitude = fields[15].toFloat() * FT2M;
|
// Get altitudeAGL (m)
|
||||||
// Get altitudeAGL (m)
|
float altitudeAGL = fields[16].toFloat() * FT2M;
|
||||||
float altitudeAGL = fields[16].toFloat() * FT2M;
|
// Get groundspeed (m/s)
|
||||||
// Get groundspeed (m/s)
|
float groundspeed = fields[17].toFloat() * KT2MPS;
|
||||||
float groundspeed = fields[17].toFloat() * KT2MPS;
|
// Get airspeed (m/s)
|
||||||
// Get airspeed (m/s)
|
float airspeed = fields[18].toFloat() * KT2MPS;
|
||||||
// float airspeed = fields[18].toFloat() * KT2MPS;
|
// Get temperature (degC)
|
||||||
// Get temperature (degC)
|
float temperature = fields[19].toFloat();
|
||||||
float temperature = fields[19].toFloat();
|
// Get pressure (kpa)
|
||||||
// Get pressure (kpa)
|
float pressure = fields[20].toFloat() * INHG2KPA;
|
||||||
float pressure = fields[20].toFloat() * INHG2KPA;
|
// Get VelocityActual Down (cm/s)
|
||||||
// Get VelocityActual Down (cm/s)
|
float velocityActualDown = - fields[21].toFloat() * FPS2CMPS;
|
||||||
float velocityActualDown = - fields[21].toFloat() * FPS2CMPS;
|
// Get VelocityActual East (cm/s)
|
||||||
// Get VelocityActual East (cm/s)
|
float velocityActualEast = fields[22].toFloat() * FPS2CMPS;
|
||||||
float velocityActualEast = fields[22].toFloat() * FPS2CMPS;
|
// Get VelocityActual Down (cm/s)
|
||||||
// Get VelocityActual Down (cm/s)
|
float velocityActualNorth = fields[23].toFloat() * FPS2CMPS;
|
||||||
float velocityActualNorth = fields[23].toFloat() * FPS2CMPS;
|
|
||||||
|
|
||||||
// Get UDP packets received by FG
|
// Get UDP packets received by FG
|
||||||
int n = fields[24].toInt();
|
int n = fields[24].toInt();
|
||||||
udpCounterFGrecv = n;
|
udpCounterFGrecv = n;
|
||||||
|
|
||||||
//run once
|
///////
|
||||||
HomeLocation::DataFields homeData = posHome->getData();
|
// Output formatting
|
||||||
if(!once)
|
///////
|
||||||
{
|
Output2OP out;
|
||||||
memset(&homeData, 0, sizeof(HomeLocation::DataFields));
|
memset(&out, 0, sizeof(Output2OP));
|
||||||
// Update homelocation
|
|
||||||
homeData.Latitude = latitude * 10e6;
|
|
||||||
homeData.Longitude = longitude * 10e6;
|
|
||||||
homeData.Altitude = 0;
|
|
||||||
double LLA[3];
|
|
||||||
LLA[0]=latitude;
|
|
||||||
LLA[1]=longitude;
|
|
||||||
LLA[2]=0;
|
|
||||||
double ECEF[3];
|
|
||||||
double RNE[9];
|
|
||||||
Utils::CoordinateConversions().RneFromLLA(LLA,(double (*)[3])RNE);
|
|
||||||
Utils::CoordinateConversions().LLA2ECEF(LLA,ECEF);
|
|
||||||
homeData.Be[0]=0;
|
|
||||||
homeData.Be[1]=0;
|
|
||||||
homeData.Be[2]=0;
|
|
||||||
posHome->setData(homeData);
|
|
||||||
once=1;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Update VelocityActual.{Nort,East,Down}
|
float NED[3];
|
||||||
VelocityActual::DataFields velocityActualData;
|
// convert from cm back to meters
|
||||||
memset(&velocityActualData, 0, sizeof(VelocityActual::DataFields));
|
|
||||||
velocityActualData.North = velocityActualNorth;
|
|
||||||
velocityActualData.East = velocityActualEast;
|
|
||||||
velocityActualData.Down = velocityActualDown;
|
|
||||||
velActual->setData(velocityActualData);
|
|
||||||
|
|
||||||
// Update PositionActual.{Nort,East,Down}
|
double LLA[3] = {latitude, longitude, altitude};
|
||||||
PositionActual::DataFields positionActualData;
|
double ECEF[3];
|
||||||
memset(&positionActualData, 0, sizeof(PositionActual::DataFields));
|
double RNE[9];
|
||||||
positionActualData.North = 0; //Currently hardcoded as there is no way of setting up a reference point to calculate distance
|
Utils::CoordinateConversions().RneFromLLA(LLA,(double (*)[3])RNE);
|
||||||
positionActualData.East = 0; //Currently hardcoded as there is no way of setting up a reference point to calculate distance
|
Utils::CoordinateConversions().LLA2ECEF(LLA,ECEF);
|
||||||
positionActualData.Down = altitude ; //Multiply by 1 because positionActual expects input in meters.
|
Utils::CoordinateConversions().LLA2Base(LLA, ECEF, (float (*)[3]) RNE, NED);
|
||||||
posActual->setData(positionActualData);
|
|
||||||
|
|
||||||
// Update AltitudeActual object
|
|
||||||
BaroAltitude::DataFields baroAltData;
|
|
||||||
memset(&baroAltData, 0, sizeof(BaroAltitude::DataFields));
|
|
||||||
baroAltData.Altitude = altitudeAGL;
|
|
||||||
baroAltData.Temperature = temperature;
|
|
||||||
baroAltData.Pressure = pressure;
|
|
||||||
baroAlt->setData(baroAltData);
|
|
||||||
|
|
||||||
// Update attActual object
|
// Update GPS Position objects
|
||||||
AttitudeActual::DataFields attActualData;
|
out.latitude = latitude * 1e7;
|
||||||
memset(&attActualData, 0, sizeof(AttitudeActual::DataFields));
|
out.longitude = longitude * 1e7;
|
||||||
attActualData.Roll = roll;
|
out.altitude = altitude;
|
||||||
attActualData.Pitch = pitch;
|
out.groundspeed = groundspeed;
|
||||||
attActualData.Yaw = yaw;
|
|
||||||
attActualData.q1 = 0;
|
|
||||||
attActualData.q2 = 0;
|
|
||||||
attActualData.q3 = 0;
|
|
||||||
attActualData.q4 = 0;
|
|
||||||
attActual->setData(attActualData);
|
|
||||||
|
|
||||||
// Update gps objects
|
out.calibratedAirspeed = airspeed;
|
||||||
GPSPosition::DataFields gpsData;
|
|
||||||
memset(&gpsData, 0, sizeof(GPSPosition::DataFields));
|
|
||||||
gpsData.Altitude = altitude;
|
|
||||||
gpsData.Heading = heading;
|
|
||||||
gpsData.Groundspeed = groundspeed;
|
|
||||||
gpsData.Latitude = latitude*1e7;
|
|
||||||
gpsData.Longitude = longitude*1e7;
|
|
||||||
gpsData.Satellites = 10;
|
|
||||||
gpsData.Status = GPSPosition::STATUS_FIX3D;
|
|
||||||
gpsPos->setData(gpsData);
|
|
||||||
|
|
||||||
float NED[3];
|
|
||||||
// convert from cm back to meters
|
|
||||||
|
|
||||||
double hLLA[3] = {(double) homeData.Latitude / 1e7, (double) homeData.Longitude / 1e7, (double) (homeData.Altitude)};
|
// Update BaroAltitude object
|
||||||
double ECEF[3];
|
out.temperature = temperature;
|
||||||
double RNE[9];
|
out.pressure = pressure;
|
||||||
Utils::CoordinateConversions().RneFromLLA(hLLA,(double (*)[3])RNE);
|
|
||||||
Utils::CoordinateConversions().LLA2ECEF(hLLA,ECEF);
|
|
||||||
Utils::CoordinateConversions().LLA2Base(hLLA, ECEF, (float (*)[3]) RNE, NED);
|
|
||||||
|
|
||||||
positionActualData.North = NED[0]; //Currently hardcoded as there is no way of setting up a reference point to calculate distance
|
// Update attActual object
|
||||||
positionActualData.East = NED[1]; //Currently hardcoded as there is no way of setting up a reference point to calculate distance
|
out.roll = roll; //roll;
|
||||||
positionActualData.Down = NED[2]; //Multiply by 1 because positionActual expects input in meters.
|
out.pitch = pitch; // pitch
|
||||||
posActual->setData(positionActualData);
|
out.heading = yaw; // yaw
|
||||||
|
|
||||||
// Update AttitudeRaw object (filtered gyros only for now)
|
out.dstN= NED[0];
|
||||||
//AttitudeRaw::DataFields rawData;
|
out.dstE= NED[1];
|
||||||
//AttitudeRaw::DataFields rawData;
|
out.dstD= NED[2];
|
||||||
Gyros::DataFields gyroData;
|
|
||||||
Accels::DataFields accelData;
|
// Update VelocityActual.{North,East,Down}
|
||||||
memset(&gyroData, 0, sizeof(Gyros::DataFields));
|
out.velNorth = velocityActualNorth;
|
||||||
memset(&accelData, 0, sizeof(Accels::DataFields));
|
out.velEast = velocityActualEast;
|
||||||
gyroData = gyros->getData();
|
out.velDown = velocityActualDown;
|
||||||
accelData = accels->getData();
|
|
||||||
//rawData.gyros[0] = rollRate;
|
//Update gyroscope sensor data
|
||||||
//rawData.gyros[1] = cos(DEG2RAD * roll) * pitchRate + sin(DEG2RAD * roll) * yawRate;
|
out.rollRate = rollRate;
|
||||||
//rawData.gyros[2] = cos(DEG2RAD * roll) * yawRate - sin(DEG2RAD * roll) * pitchRate;
|
out.pitchRate = pitchRate;
|
||||||
//rawData.gyros[1] = pitchRate;
|
out.yawRate = yawRate;
|
||||||
//rawData.gyros[2] = yawRate;
|
|
||||||
gyroData.x = rollRate;
|
//Update accelerometer sensor data
|
||||||
gyroData.y = pitchRate;
|
out.accX = xAccel;
|
||||||
gyroData.z = yawRate;
|
out.accY = yAccel;
|
||||||
// TODO: Accels are still missing!!!!
|
out.accZ = -zAccel;
|
||||||
gyros->setData(gyroData);
|
|
||||||
// attRaw->updated();
|
updateUAVOs(out);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -84,7 +84,7 @@ const float IL2Simulator::TEMP_LAPSE_RATE = -0.0065; //degrees per meter
|
|||||||
const float IL2Simulator::AIR_CONST_FACTOR = -0.0341631947363104; //several nature constants calculated into one
|
const float IL2Simulator::AIR_CONST_FACTOR = -0.0341631947363104; //several nature constants calculated into one
|
||||||
|
|
||||||
IL2Simulator::IL2Simulator(const SimulatorSettings& params) :
|
IL2Simulator::IL2Simulator(const SimulatorSettings& params) :
|
||||||
Simulator(params)
|
Simulator(params)
|
||||||
{
|
{
|
||||||
|
|
||||||
}
|
}
|
||||||
@ -105,23 +105,23 @@ void IL2Simulator::setupUdpPorts(const QString& host, int inPort, int outPort)
|
|||||||
|
|
||||||
void IL2Simulator::transmitUpdate()
|
void IL2Simulator::transmitUpdate()
|
||||||
{
|
{
|
||||||
// Read ActuatorDesired from autopilot
|
// Read ActuatorDesired from autopilot
|
||||||
ActuatorDesired::DataFields actData = actDesired->getData();
|
ActuatorDesired::DataFields actData = actDesired->getData();
|
||||||
float ailerons = actData.Roll;
|
float ailerons = actData.Roll;
|
||||||
float elevator = actData.Pitch;
|
float elevator = actData.Pitch;
|
||||||
float rudder = actData.Yaw;
|
float rudder = actData.Yaw;
|
||||||
float throttle = actData.Throttle*2-1.0;
|
float throttle = actData.Throttle*2-1.0;
|
||||||
|
|
||||||
// Send update to Il2
|
// Send update to Il2
|
||||||
QString cmd;
|
QString cmd;
|
||||||
cmd=QString("R/30/32/40/42/46/48/81\\%1/85\\%2/87\\%3/89\\%4/")
|
cmd=QString("R/30/32/40/42/46/48/81\\%1/85\\%2/87\\%3/89\\%4/")
|
||||||
.arg(throttle)
|
.arg(throttle)
|
||||||
.arg(ailerons)
|
.arg(ailerons)
|
||||||
.arg(elevator)
|
.arg(elevator)
|
||||||
.arg(rudder);
|
.arg(rudder);
|
||||||
QByteArray data = cmd.toAscii();
|
QByteArray data = cmd.toAscii();
|
||||||
//outSocket->write(data);
|
//outSocket->write(data);
|
||||||
inSocket->write(data); // for IL2 must send to the same port as input!!!!!!!!!!!!!
|
inSocket->write(data); // for IL2 must send to the same port as input!!!!!!!!!!!!!
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@ -129,17 +129,17 @@ void IL2Simulator::transmitUpdate()
|
|||||||
* calculate air density from altitude
|
* calculate air density from altitude
|
||||||
*/
|
*/
|
||||||
float IL2Simulator::DENSITY(float alt) {
|
float IL2Simulator::DENSITY(float alt) {
|
||||||
return (GROUNDDENSITY * pow(
|
return (GROUNDDENSITY * pow(
|
||||||
((TEMP_GROUND+(TEMP_LAPSE_RATE*alt))/TEMP_GROUND),
|
((TEMP_GROUND+(TEMP_LAPSE_RATE*alt))/TEMP_GROUND),
|
||||||
((AIR_CONST_FACTOR/TEMP_LAPSE_RATE)-1) )
|
((AIR_CONST_FACTOR/TEMP_LAPSE_RATE)-1) )
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* calculate air pressure from altitude
|
* calculate air pressure from altitude
|
||||||
*/
|
*/
|
||||||
float IL2Simulator::PRESSURE(float alt) {
|
float IL2Simulator::PRESSURE(float alt) {
|
||||||
return DENSITY(alt)*(TEMP_GROUND+(alt*TEMP_LAPSE_RATE))*AIR_CONST;
|
return DENSITY(alt)*(TEMP_GROUND+(alt*TEMP_LAPSE_RATE))*AIR_CONST;
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -155,209 +155,166 @@ float IL2Simulator::TAS(float IAS, float alt) {
|
|||||||
*/
|
*/
|
||||||
void IL2Simulator::processUpdate(const QByteArray& inp)
|
void IL2Simulator::processUpdate(const QByteArray& inp)
|
||||||
{
|
{
|
||||||
// save old flight data to calculate delta's later
|
// save old flight data to calculate delta's later
|
||||||
old=current;
|
old=current;
|
||||||
QString data(inp);
|
QString data(inp);
|
||||||
// Split
|
// Split
|
||||||
QStringList fields = data.split("/");
|
QStringList fields = data.split("/");
|
||||||
|
|
||||||
// split up response string
|
// split up response string
|
||||||
int t;
|
int t;
|
||||||
for (t=0; t<fields.length(); t++) {
|
for (t=0; t<fields.length(); t++) {
|
||||||
QStringList values = fields[t].split("\\");
|
QStringList values = fields[t].split("\\");
|
||||||
// parse values
|
// parse values
|
||||||
if (values.length()>=2) {
|
if (values.length()>=2) {
|
||||||
int id = values[0].toInt();
|
int id = values[0].toInt();
|
||||||
float value = values[1].toFloat();
|
float value = values[1].toFloat();
|
||||||
switch (id) {
|
switch (id) {
|
||||||
case 30:
|
case 30:
|
||||||
current.ias=value * KMH2MPS;
|
current.ias=value * KMH2MPS;
|
||||||
break;
|
break;
|
||||||
case 32:
|
case 32:
|
||||||
current.dZ=value;
|
current.dZ=value;
|
||||||
break;
|
break;
|
||||||
case 40:
|
case 40:
|
||||||
current.Z=value;
|
current.Z=value;
|
||||||
break;
|
break;
|
||||||
case 42:
|
case 42:
|
||||||
current.azimuth=value;
|
current.azimuth=value;
|
||||||
break;
|
break;
|
||||||
case 46:
|
case 46:
|
||||||
current.roll=-value;
|
current.roll=-value;
|
||||||
break;
|
break;
|
||||||
case 48:
|
case 48:
|
||||||
current.pitch=value;
|
current.pitch=value;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// measure time
|
// measure time
|
||||||
current.dT = ((float)time->restart()) / 1000.0;
|
current.dT = ((float)time->restart()) / 1000.0;
|
||||||
if (current.dT<0.001) current.dT=0.001;
|
if (current.dT<0.001) current.dT=0.001;
|
||||||
current.T = old.T+current.dT;
|
current.T = old.T+current.dT;
|
||||||
current.i = old.i+1;
|
current.i = old.i+1;
|
||||||
if (current.i==1) {
|
if (current.i==1) {
|
||||||
old.dRoll=0;
|
old.dRoll=0;
|
||||||
old.dPitch=0;
|
old.dPitch=0;
|
||||||
old.dAzimuth=0;
|
old.dAzimuth=0;
|
||||||
old.ddX=0;
|
old.ddX=0;
|
||||||
old.ddX=0;
|
old.ddX=0;
|
||||||
old.ddX=0;
|
old.ddX=0;
|
||||||
}
|
}
|
||||||
|
|
||||||
// calculate TAS from alt and IAS
|
// calculate TAS from alt and IAS
|
||||||
current.tas = TAS(current.ias,current.Z);
|
current.tas = TAS(current.ias,current.Z);
|
||||||
|
|
||||||
// assume the plane actually flies straight and no wind
|
// assume the plane actually flies straight and no wind
|
||||||
// groundspeed is horizontal vector of TAS
|
// groundspeed is horizontal vector of TAS
|
||||||
current.groundspeed = current.tas*cos(current.pitch*DEG2RAD);
|
current.groundspeed = current.tas*cos(current.pitch*DEG2RAD);
|
||||||
// x and y vector components
|
// x and y vector components
|
||||||
current.dX = current.groundspeed*sin(current.azimuth*DEG2RAD);
|
current.dX = current.groundspeed*sin(current.azimuth*DEG2RAD);
|
||||||
current.dY = current.groundspeed*cos(current.azimuth*DEG2RAD);
|
current.dY = current.groundspeed*cos(current.azimuth*DEG2RAD);
|
||||||
|
|
||||||
// simple IMS - integration over time the easy way...
|
// simple IMS - integration over time the easy way...
|
||||||
current.X = old.X + (current.dX*current.dT);
|
current.X = old.X + (current.dX*current.dT);
|
||||||
current.Y = old.Y + (current.dY*current.dT);
|
current.Y = old.Y + (current.dY*current.dT);
|
||||||
|
|
||||||
// accelerations (filtered)
|
// accelerations (filtered)
|
||||||
if (isnan(old.ddX) || isinf(old.ddX)) old.ddX=0;
|
if (isnan(old.ddX) || isinf(old.ddX)) old.ddX=0;
|
||||||
if (isnan(old.ddY) || isinf(old.ddY)) old.ddY=0;
|
if (isnan(old.ddY) || isinf(old.ddY)) old.ddY=0;
|
||||||
if (isnan(old.ddZ) || isinf(old.ddZ)) old.ddZ=0;
|
if (isnan(old.ddZ) || isinf(old.ddZ)) old.ddZ=0;
|
||||||
#define SPEED_FILTER 10
|
#define SPEED_FILTER 10
|
||||||
current.ddX = ((current.dX-old.dX)/current.dT + SPEED_FILTER * (old.ddX)) / (SPEED_FILTER+1);
|
current.ddX = ((current.dX-old.dX)/current.dT + SPEED_FILTER * (old.ddX)) / (SPEED_FILTER+1);
|
||||||
current.ddY = ((current.dY-old.dY)/current.dT + SPEED_FILTER * (old.ddY)) / (SPEED_FILTER+1);
|
current.ddY = ((current.dY-old.dY)/current.dT + SPEED_FILTER * (old.ddY)) / (SPEED_FILTER+1);
|
||||||
current.ddZ = ((current.dZ-old.dZ)/current.dT + SPEED_FILTER * (old.ddZ)) / (SPEED_FILTER+1);
|
current.ddZ = ((current.dZ-old.dZ)/current.dT + SPEED_FILTER * (old.ddZ)) / (SPEED_FILTER+1);
|
||||||
|
|
||||||
#define TURN_FILTER 10
|
#define TURN_FILTER 10
|
||||||
// turn speeds (filtered)
|
// turn speeds (filtered)
|
||||||
if (isnan(old.dAzimuth) || isinf(old.dAzimuth)) old.dAzimuth=0;
|
if (isnan(old.dAzimuth) || isinf(old.dAzimuth)) old.dAzimuth=0;
|
||||||
if (isnan(old.dPitch) || isinf(old.dPitch)) old.dPitch=0;
|
if (isnan(old.dPitch) || isinf(old.dPitch)) old.dPitch=0;
|
||||||
if (isnan(old.dRoll) || isinf(old.dRoll)) old.dRoll=0;
|
if (isnan(old.dRoll) || isinf(old.dRoll)) old.dRoll=0;
|
||||||
current.dAzimuth = (angleDifference(current.azimuth,old.azimuth)/current.dT + TURN_FILTER * (old.dAzimuth)) / (TURN_FILTER+1);
|
current.dAzimuth = (angleDifference(current.azimuth,old.azimuth)/current.dT + TURN_FILTER * (old.dAzimuth)) / (TURN_FILTER+1);
|
||||||
current.dPitch = (angleDifference(current.pitch,old.pitch)/current.dT + TURN_FILTER * (old.dPitch)) / (TURN_FILTER+1);
|
current.dPitch = (angleDifference(current.pitch,old.pitch)/current.dT + TURN_FILTER * (old.dPitch)) / (TURN_FILTER+1);
|
||||||
current.dRoll = (angleDifference(current.roll,old.roll)/current.dT + TURN_FILTER * (old.dRoll)) / (TURN_FILTER+1);
|
current.dRoll = (angleDifference(current.roll,old.roll)/current.dT + TURN_FILTER * (old.dRoll)) / (TURN_FILTER+1);
|
||||||
// Update AltitudeActual object
|
|
||||||
BaroAltitude::DataFields baroAltData;
|
|
||||||
memset(&baroAltData, 0, sizeof(BaroAltitude::DataFields));
|
|
||||||
baroAltData.Altitude = current.Z;
|
|
||||||
baroAltData.Temperature = TEMP_GROUND + (current.Z * TEMP_LAPSE_RATE) - 273.0;
|
|
||||||
baroAltData.Pressure = PRESSURE(current.Z)/1000.0; // kpa
|
|
||||||
|
|
||||||
// Update attActual object
|
///////
|
||||||
AttitudeActual::DataFields attActualData;
|
// Output formatting
|
||||||
memset(&attActualData, 0, sizeof(AttitudeActual::DataFields));
|
///////
|
||||||
attActualData.Roll = current.roll;
|
Output2OP out;
|
||||||
attActualData.Pitch = current.pitch;
|
memset(&out, 0, sizeof(Output2OP));
|
||||||
attActualData.Yaw = current.azimuth;
|
|
||||||
float rpy[3];
|
|
||||||
float quat[4];
|
|
||||||
rpy[0]=current.roll;
|
|
||||||
rpy[1]=current.pitch;
|
|
||||||
rpy[2]=current.azimuth;
|
|
||||||
Utils::CoordinateConversions().RPY2Quaternion(rpy,quat);
|
|
||||||
attActualData.q1 = quat[0];
|
|
||||||
attActualData.q2 = quat[1];
|
|
||||||
attActualData.q3 = quat[2];
|
|
||||||
attActualData.q4 = quat[3];
|
|
||||||
|
|
||||||
// Update positionActual objects
|
// Compute rotation matrix, for later calculations
|
||||||
PositionActual::DataFields posData;
|
float Rbe[3][3];
|
||||||
memset(&posData, 0, sizeof(PositionActual::DataFields));
|
float rpy[3];
|
||||||
posData.North = current.Y;
|
float quat[4];
|
||||||
posData.East = current.X;
|
rpy[0]=current.roll;
|
||||||
posData.Down = current.Z*-1.;
|
rpy[1]=current.pitch;
|
||||||
|
rpy[2]=current.azimuth;
|
||||||
|
Utils::CoordinateConversions().RPY2Quaternion(rpy,quat);
|
||||||
|
Utils::CoordinateConversions().Quaternion2R(quat,Rbe);
|
||||||
|
|
||||||
// Update velocityActual objects
|
//Calculate ECEF
|
||||||
VelocityActual::DataFields velData;
|
double RNE[9];
|
||||||
memset(&velData, 0, sizeof(VelocityActual::DataFields));
|
double ECEF[3];
|
||||||
velData.North = current.dY;
|
double LLA[3];
|
||||||
velData.East = current.dX;
|
LLA[0]=settings.latitude.toFloat();
|
||||||
velData.Down = current.dZ*-1.;
|
LLA[1]=settings.longitude.toFloat();
|
||||||
|
LLA[2]=0;
|
||||||
|
Utils::CoordinateConversions().RneFromLLA(LLA,(double (*)[3])RNE);
|
||||||
|
Utils::CoordinateConversions().LLA2ECEF(LLA,ECEF);
|
||||||
|
|
||||||
// Update AttitudeRaw object (filtered gyros and accels only for now)
|
// Update GPS Position objects
|
||||||
//AttitudeRaw::DataFields rawData;
|
double NED[3];
|
||||||
//memset(&rawData, 0, sizeof(AttitudeRaw::DataFields));
|
NED[0] = current.Y;
|
||||||
//rawData = attRaw->getData();
|
NED[1] = current.X;
|
||||||
Gyros::DataFields gyroData;
|
NED[2] = -current.Z;
|
||||||
Accels::DataFields accelData;
|
Utils::CoordinateConversions().NED2LLA_HomeECEF(ECEF,NED,LLA);
|
||||||
memset(&gyroData, 0, sizeof(Gyros::DataFields));
|
out.latitude = settings.latitude.toFloat() * 1e7;
|
||||||
memset(&accelData, 0, sizeof(Accels::DataFields));
|
out.longitude = settings.longitude.toFloat() * 1e7;
|
||||||
gyroData = gyros->getData();
|
out.groundspeed = current.groundspeed;
|
||||||
accelData = accels->getData();
|
|
||||||
|
|
||||||
// rotate turn rates and accelerations into body frame
|
out.calibratedAirspeed = current.ias;
|
||||||
// (note: rotation deltas are NOT in NED frame but in RPY - manual conversion!)
|
|
||||||
gyroData.x = current.dRoll;
|
|
||||||
gyroData.y = cos(DEG2RAD * current.roll) * current.dPitch + sin(DEG2RAD * current.roll) * current.dAzimuth;
|
|
||||||
gyroData.z = cos(DEG2RAD * current.roll) * current.dAzimuth - sin(DEG2RAD * current.roll) * current.dPitch;
|
|
||||||
// accels are in NED and can be converted using standard ned->local rotation matrix
|
|
||||||
float Rbe[3][3];
|
|
||||||
Utils::CoordinateConversions().Quaternion2R(quat,Rbe);
|
|
||||||
accelData.x = current.ddX*Rbe[0][0]
|
|
||||||
+current.ddY*Rbe[0][1]
|
|
||||||
+(current.ddZ+GEE)*Rbe[0][2];
|
|
||||||
accelData.y = current.ddX*Rbe[1][0]
|
|
||||||
+current.ddY*Rbe[1][1]
|
|
||||||
+(current.ddZ+GEE)*Rbe[1][2];
|
|
||||||
accelData.z = - (current.ddX*Rbe[2][0]
|
|
||||||
+current.ddY*Rbe[2][1]
|
|
||||||
+(current.ddZ+GEE)*Rbe[2][2]);
|
|
||||||
|
|
||||||
// Update homelocation
|
out.dstN=current.Y;
|
||||||
HomeLocation::DataFields homeData;
|
out.dstE=current.X;
|
||||||
memset(&homeData, 0, sizeof(HomeLocation::DataFields));
|
out.dstD=-current.Z;
|
||||||
homeData = posHome->getData();
|
|
||||||
homeData.Latitude = settings.latitude.toFloat() * 10e6;
|
|
||||||
homeData.Longitude = settings.longitude.toFloat() * 10e6;
|
|
||||||
homeData.Altitude = 0;
|
|
||||||
double LLA[3];
|
|
||||||
LLA[0]=settings.latitude.toFloat();
|
|
||||||
LLA[1]=settings.longitude.toFloat();
|
|
||||||
LLA[2]=0;
|
|
||||||
double ECEF[3];
|
|
||||||
double RNE[9];
|
|
||||||
Utils::CoordinateConversions().RneFromLLA(LLA,(double (*)[3])RNE);
|
|
||||||
Utils::CoordinateConversions().LLA2ECEF(LLA,ECEF);
|
|
||||||
homeData.Be[0]=0;
|
|
||||||
homeData.Be[1]=0;
|
|
||||||
homeData.Be[2]=0;
|
|
||||||
homeData.Set=1;
|
|
||||||
|
|
||||||
// Update gps objects
|
// Update BaroAltitude object
|
||||||
GPSPosition::DataFields gpsData;
|
out.altitude = current.Z;
|
||||||
memset(&gpsData, 0, sizeof(GPSPosition::DataFields));
|
out.temperature = TEMP_GROUND + (current.Z * TEMP_LAPSE_RATE) - 273.0;
|
||||||
gpsData.Altitude = current.Z;
|
out.pressure = PRESSURE(current.Z)/1000.0; // kpa
|
||||||
gpsData.Heading = current.azimuth;
|
|
||||||
gpsData.Groundspeed = current.groundspeed;
|
|
||||||
double NED[3];
|
|
||||||
NED[0] = current.Y;
|
|
||||||
NED[1] = current.X;
|
|
||||||
NED[2] = -current.Z;
|
|
||||||
Utils::CoordinateConversions().NED2LLA_HomeECEF(ECEF,NED,LLA);
|
|
||||||
gpsData.Latitude = LLA[0] * 10e6;
|
|
||||||
gpsData.Longitude = LLA[1] * 10e6;
|
|
||||||
gpsData.Satellites = 7;
|
|
||||||
gpsData.Status = GPSPosition::STATUS_FIX3D;
|
|
||||||
|
|
||||||
// issue manual update
|
|
||||||
// update every time (50ms)
|
// Update attActual object
|
||||||
attActual->setData(attActualData);
|
out.roll = current.roll; //roll;
|
||||||
//attActual->updated();
|
out.pitch = current.pitch; // pitch
|
||||||
//attRaw->setData(rawData);
|
out.heading = current.azimuth; // yaw
|
||||||
gyros->setData(gyroData);
|
|
||||||
accels->setData(accelData);
|
|
||||||
//attRaw->updated();
|
// Update VelocityActual.{North,East,Down}
|
||||||
velActual->setData(velData);
|
out.velNorth = current.dY;
|
||||||
//velActual->updated();
|
out.velEast = current.dX;
|
||||||
posActual->setData(posData);
|
out.velDown = -current.dZ;
|
||||||
//posActual->updated();
|
|
||||||
baroAlt->setData(baroAltData);
|
// rotate turn rates and accelerations into body frame
|
||||||
//baroAlt->updated();
|
// (note: rotation deltas are NOT in NED frame but in RPY - manual conversion!)
|
||||||
gpsPos->setData(gpsData);
|
out.rollRate = current.dRoll;
|
||||||
//gpsPos->updated();
|
out.pitchRate = cos(DEG2RAD * current.roll) * current.dPitch + sin(DEG2RAD * current.roll) * current.dAzimuth;
|
||||||
posHome->setData(homeData);
|
out.yawRate = cos(DEG2RAD * current.roll) * current.dAzimuth - sin(DEG2RAD * current.roll) * current.dPitch;
|
||||||
//posHome->updated();
|
|
||||||
|
//Update accelerometer sensor data
|
||||||
|
out.accX = current.ddX*Rbe[0][0]
|
||||||
|
+current.ddY*Rbe[0][1]
|
||||||
|
+(current.ddZ+GEE)*Rbe[0][2];
|
||||||
|
out.accY = current.ddX*Rbe[1][0]
|
||||||
|
+current.ddY*Rbe[1][1]
|
||||||
|
+(current.ddZ+GEE)*Rbe[1][2];
|
||||||
|
out.accZ = - (current.ddX*Rbe[2][0]
|
||||||
|
+current.ddY*Rbe[2][1]
|
||||||
|
+(current.ddZ+GEE)*Rbe[2][2]);
|
||||||
|
|
||||||
|
updateUAVOs(out);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -365,8 +322,8 @@ void IL2Simulator::processUpdate(const QByteArray& inp)
|
|||||||
*/
|
*/
|
||||||
float IL2Simulator::angleDifference(float a, float b)
|
float IL2Simulator::angleDifference(float a, float b)
|
||||||
{
|
{
|
||||||
float d=a-b;
|
float d=a-b;
|
||||||
if (d>180) d-=360;
|
if (d>180) d-=360;
|
||||||
if (d<-180)d+=360;
|
if (d<-180)d+=360;
|
||||||
return d;
|
return d;
|
||||||
}
|
}
|
||||||
|
@ -368,6 +368,11 @@ void Simulator::telStatsUpdated(UAVObject* obj)
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void Simulator::resetInitialHomePosition(){
|
||||||
|
once=false;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
void Simulator::updateUAVOs(Output2OP out){
|
void Simulator::updateUAVOs(Output2OP out){
|
||||||
|
|
||||||
QTime currentTime = QTime::currentTime();
|
QTime currentTime = QTime::currentTime();
|
||||||
|
@ -25,7 +25,7 @@
|
|||||||
* 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
* 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||||
*/
|
*/
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Description of X-Plane Protocol:
|
* Description of X-Plane Protocol:
|
||||||
*
|
*
|
||||||
* To see what data can be sended/recieved to/from X-Plane, launch X-Plane -> goto main menu
|
* To see what data can be sended/recieved to/from X-Plane, launch X-Plane -> goto main menu
|
||||||
@ -41,9 +41,9 @@
|
|||||||
* struct data_struct
|
* struct data_struct
|
||||||
* {
|
* {
|
||||||
* int index; // data index, the index into the list of variables
|
* int index; // data index, the index into the list of variables
|
||||||
// you can output from the Data Output screen in X-Plane.
|
// you can output from the Data Output screen in X-Plane.
|
||||||
* float data[8]; // the up to 8 numbers you see in the data output screen associated with that selection..
|
* float data[8]; // the up to 8 numbers you see in the data output screen associated with that selection..
|
||||||
// many outputs do not use all 8, though.
|
// many outputs do not use all 8, though.
|
||||||
* };
|
* };
|
||||||
*
|
*
|
||||||
* For Example, update of aileron/elevon/rudder in X-Plane (11 row in Data Set)
|
* For Example, update of aileron/elevon/rudder in X-Plane (11 row in Data Set)
|
||||||
@ -66,9 +66,9 @@
|
|||||||
void TraceBuf(const char* buf,int len);
|
void TraceBuf(const char* buf,int len);
|
||||||
|
|
||||||
XplaneSimulator::XplaneSimulator(const SimulatorSettings& params) :
|
XplaneSimulator::XplaneSimulator(const SimulatorSettings& params) :
|
||||||
Simulator(params)
|
Simulator(params)
|
||||||
{
|
{
|
||||||
once = false;
|
resetInitialHomePosition();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@ -82,7 +82,7 @@ void XplaneSimulator::setupUdpPorts(const QString& host, int inPort, int outPort
|
|||||||
|
|
||||||
inSocket->bind(QHostAddress(host), inPort);
|
inSocket->bind(QHostAddress(host), inPort);
|
||||||
//outSocket->bind(QHostAddress(host), outPort);
|
//outSocket->bind(QHostAddress(host), outPort);
|
||||||
once = false;
|
resetInitialHomePosition();
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -99,41 +99,41 @@ bool XplaneSimulator::setupProcess()
|
|||||||
*/
|
*/
|
||||||
void XplaneSimulator::transmitUpdate()
|
void XplaneSimulator::transmitUpdate()
|
||||||
{
|
{
|
||||||
//Read ActuatorDesired from autopilot
|
//Read ActuatorDesired from autopilot
|
||||||
ActuatorDesired::DataFields actData = actDesired->getData();
|
ActuatorDesired::DataFields actData = actDesired->getData();
|
||||||
float ailerons = actData.Roll;
|
float ailerons = actData.Roll;
|
||||||
float elevator = actData.Pitch;
|
float elevator = actData.Pitch;
|
||||||
float rudder = actData.Yaw;
|
float rudder = actData.Yaw;
|
||||||
float throttle = actData.Throttle*2-1.0;
|
float throttle = actData.Throttle > 0? actData.Throttle : 0;
|
||||||
float none = -999;
|
float none = -999;
|
||||||
//quint32 none = *((quint32*)&tmp); // get float as 4 bytes
|
//quint32 none = *((quint32*)&tmp); // get float as 4 bytes
|
||||||
|
|
||||||
quint32 code;
|
quint32 code;
|
||||||
QByteArray buf;
|
QByteArray buf;
|
||||||
QDataStream stream(&buf,QIODevice::ReadWrite);
|
QDataStream stream(&buf,QIODevice::ReadWrite);
|
||||||
|
|
||||||
// !!! LAN byte order - Big Endian
|
// !!! LAN byte order - Big Endian
|
||||||
#if Q_BYTE_ORDER == Q_LITTLE_ENDIAN
|
#if Q_BYTE_ORDER == Q_LITTLE_ENDIAN
|
||||||
stream.setByteOrder(QDataStream::LittleEndian);
|
stream.setByteOrder(QDataStream::LittleEndian);
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
// 11th data settings (flight con: ail/elv/rud)
|
// 11th data settings (flight con: ail/elv/rud)
|
||||||
buf.clear();
|
buf.clear();
|
||||||
code = 11;
|
code = 11;
|
||||||
//quint8 header[] = "DATA";
|
//quint8 header[] = "DATA";
|
||||||
/*
|
/*
|
||||||
stream << *((quint32*)header) <<
|
stream << *((quint32*)header) <<
|
||||||
(quint8)0x30 <<
|
(quint8)0x30 <<
|
||||||
code <<
|
code <<
|
||||||
*((quint32*)&elevator) <<
|
*((quint32*)&elevator) <<
|
||||||
*((quint32*)&ailerons) <<
|
*((quint32*)&ailerons) <<
|
||||||
*((quint32*)&rudder) <<
|
*((quint32*)&rudder) <<
|
||||||
none <<
|
none <<
|
||||||
*((quint32*)&ailerons) <<
|
*((quint32*)&ailerons) <<
|
||||||
none <<
|
none <<
|
||||||
none <<
|
none <<
|
||||||
none;
|
none;
|
||||||
*/
|
*/
|
||||||
buf.append("DATA0");
|
buf.append("DATA0");
|
||||||
buf.append(reinterpret_cast<const char*>(&code), sizeof(code));
|
buf.append(reinterpret_cast<const char*>(&code), sizeof(code));
|
||||||
buf.append(reinterpret_cast<const char*>(&elevator), sizeof(elevator));
|
buf.append(reinterpret_cast<const char*>(&elevator), sizeof(elevator));
|
||||||
@ -173,18 +173,18 @@ void XplaneSimulator::transmitUpdate()
|
|||||||
emit processOutput("Error sending UDP packet to XPlane: " + outSocket->errorString() + "\n");
|
emit processOutput("Error sending UDP packet to XPlane: " + outSocket->errorString() + "\n");
|
||||||
}
|
}
|
||||||
|
|
||||||
//outSocket->write(buf);
|
//outSocket->write(buf);
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
/** !!! this settings was given from ardupilot X-Plane.pl, I comment them
|
/** !!! this settings was given from ardupilot X-Plane.pl, I comment them
|
||||||
but if it needed comment should be removed !!!
|
but if it needed comment should be removed !!!
|
||||||
|
|
||||||
// 8th data settings (joystick 1 ail/elv/rud)
|
// 8th data settings (joystick 1 ail/elv/rud)
|
||||||
stream << "DATA0" << quint32(11) << elevator << ailerons << rudder
|
stream << "DATA0" << quint32(11) << elevator << ailerons << rudder
|
||||||
<< float(-999) << float(-999) << float(-999) << float(-999) << float(-999);
|
<< float(-999) << float(-999) << float(-999) << float(-999) << float(-999);
|
||||||
outSocket->write(buf);
|
outSocket->write(buf);
|
||||||
*/
|
*/
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -193,83 +193,83 @@ void XplaneSimulator::transmitUpdate()
|
|||||||
*/
|
*/
|
||||||
void XplaneSimulator::processUpdate(const QByteArray& dataBuf)
|
void XplaneSimulator::processUpdate(const QByteArray& dataBuf)
|
||||||
{
|
{
|
||||||
float altitude = 0;
|
float altitude = 0;
|
||||||
float latitude = 0;
|
float latitude = 0;
|
||||||
float longitude = 0;
|
float longitude = 0;
|
||||||
float airspeed = 0;
|
float airspeed_keas = 0;
|
||||||
float speed = 0;
|
float groundspeed_ktgs = 0;
|
||||||
float pitch = 0;
|
float pitch = 0;
|
||||||
float roll = 0;
|
float roll = 0;
|
||||||
float heading = 0;
|
float heading = 0;
|
||||||
float pressure = 0;
|
float pressure = 0;
|
||||||
float temperature = 0;
|
float temperature = 0;
|
||||||
float velX = 0;
|
float velX = 0;
|
||||||
float velY = 0;
|
float velY = 0;
|
||||||
float velZ = 0;
|
float velZ = 0;
|
||||||
float dstX = 0;
|
float dstX = 0;
|
||||||
float dstY = 0;
|
float dstY = 0;
|
||||||
float dstZ = 0;
|
float dstZ = 0;
|
||||||
float accX = 0;
|
float accX = 0;
|
||||||
float accY = 0;
|
float accY = 0;
|
||||||
float accZ = 0;
|
float accZ = 0;
|
||||||
float rollRate_rad=0;
|
float rollRate_rad=0;
|
||||||
float pitchRate_rad=0;
|
float pitchRate_rad=0;
|
||||||
float yawRate_rad=0;
|
float yawRate_rad=0;
|
||||||
|
|
||||||
QString str;
|
// QString str;
|
||||||
QByteArray& buf = const_cast<QByteArray&>(dataBuf);
|
QByteArray& buf = const_cast<QByteArray&>(dataBuf);
|
||||||
QString data(buf);
|
QString data(buf);
|
||||||
|
|
||||||
if(data.left(4) == "DATA") // check type of packet
|
if(data.left(4) == "DATA") // check type of packet
|
||||||
{
|
{
|
||||||
buf.remove(0,5);
|
buf.remove(0,5);
|
||||||
if(dataBuf.size() % 36)
|
if(dataBuf.size() % 36)
|
||||||
{
|
{
|
||||||
qxtLog->info("incorrect length of UDP packet: ",buf);
|
qxtLog->info("incorrect length of UDP packet: ",buf);
|
||||||
return; // incorrect length of struct
|
return; // incorrect length of struct
|
||||||
}
|
}
|
||||||
// check correctness of data length, length must be multiple of (id_size+8*float_size)=4+8*4=36
|
// check correctness of data length, length must be multiple of (id_size+8*float_size)=4+8*4=36
|
||||||
int channelCounter = dataBuf.size() / 36;
|
int channelCounter = dataBuf.size() / 36;
|
||||||
do
|
do
|
||||||
{
|
{
|
||||||
switch(buf[0]) // switch by id
|
switch(buf[0]) // switch by id
|
||||||
{
|
{
|
||||||
case XplaneSimulator::LatitudeLongitude:
|
case XplaneSimulator::LatitudeLongitudeAltitude:
|
||||||
latitude = *((float*)(buf.data()+4*1));
|
latitude = *((float*)(buf.data()+4*1));
|
||||||
longitude = *((float*)(buf.data()+4*2));
|
longitude = *((float*)(buf.data()+4*2));
|
||||||
altitude = *((float*)(buf.data()+4*3))* FT2M;
|
altitude = *((float*)(buf.data()+4*3))* FT2M;
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case XplaneSimulator::Speed:
|
case XplaneSimulator::Speed:
|
||||||
airspeed = *((float*)(buf.data()+4*7));
|
airspeed_keas = *((float*)(buf.data()+4*2));
|
||||||
speed = *((float*)(buf.data()+4*8));
|
groundspeed_ktgs = *((float*)(buf.data()+4*4));
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case XplaneSimulator::PitchRollHeading:
|
case XplaneSimulator::PitchRollHeading:
|
||||||
pitch = *((float*)(buf.data()+4*1));
|
pitch = *((float*)(buf.data()+4*1));
|
||||||
roll = *((float*)(buf.data()+4*2));
|
roll = *((float*)(buf.data()+4*2));
|
||||||
heading = *((float*)(buf.data()+4*3));
|
heading = *((float*)(buf.data()+4*3));
|
||||||
break;
|
break;
|
||||||
|
|
||||||
/*
|
/*
|
||||||
case XplaneSimulator::SystemPressures:
|
case XplaneSimulator::SystemPressures:
|
||||||
pressure = *((float*)(buf.data()+4*1));
|
pressure = *((float*)(buf.data()+4*1));
|
||||||
break;
|
break;
|
||||||
*/
|
*/
|
||||||
|
|
||||||
case XplaneSimulator::AtmosphereWeather:
|
case XplaneSimulator::AtmosphereWeather:
|
||||||
pressure = *((float*)(buf.data()+4*1)) * INHG2KPA;
|
pressure = *((float*)(buf.data()+4*1)) * INHG2KPA;
|
||||||
temperature = *((float*)(buf.data()+4*2));
|
temperature = *((float*)(buf.data()+4*2));
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case XplaneSimulator::LocVelDistTraveled:
|
case XplaneSimulator::LocVelDistTraveled:
|
||||||
dstX = *((float*)(buf.data()+4*1));
|
dstX = *((float*)(buf.data()+4*1));
|
||||||
dstY = - *((float*)(buf.data()+4*3));
|
dstY = - *((float*)(buf.data()+4*3));
|
||||||
dstZ = *((float*)(buf.data()+4*2));
|
dstZ = *((float*)(buf.data()+4*2));
|
||||||
velX = *((float*)(buf.data()+4*4));
|
velX = *((float*)(buf.data()+4*4));
|
||||||
velY = - *((float*)(buf.data()+4*6));
|
velY = - *((float*)(buf.data()+4*6));
|
||||||
velZ = *((float*)(buf.data()+4*5));
|
velZ = *((float*)(buf.data()+4*5));
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case XplaneSimulator::AngularVelocities: //In [rad/s]
|
case XplaneSimulator::AngularVelocities: //In [rad/s]
|
||||||
pitchRate_rad = *((float*)(buf.data()+4*1));
|
pitchRate_rad = *((float*)(buf.data()+4*1));
|
||||||
@ -277,139 +277,70 @@ void XplaneSimulator::processUpdate(const QByteArray& dataBuf)
|
|||||||
yawRate_rad = *((float*)(buf.data()+4*3));
|
yawRate_rad = *((float*)(buf.data()+4*3));
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case XplaneSimulator::Gload:
|
case XplaneSimulator::Gload:
|
||||||
accX = *((float*)(buf.data()+4*6)) * GEE;
|
accX = *((float*)(buf.data()+4*6)) * GEE;
|
||||||
accY = *((float*)(buf.data()+4*7)) * GEE;
|
accY = *((float*)(buf.data()+4*7)) * GEE;
|
||||||
accZ = *((float*)(buf.data()+4*5)) * GEE;
|
accZ = *((float*)(buf.data()+4*5)) * GEE;
|
||||||
break;
|
break;
|
||||||
|
|
||||||
default:
|
default:
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
channelCounter--;
|
channelCounter--;
|
||||||
buf.remove(0,36);
|
buf.remove(0,36);
|
||||||
} while (channelCounter);
|
} while (channelCounter);
|
||||||
|
|
||||||
|
|
||||||
HomeLocation::DataFields homeData = posHome->getData();
|
///////
|
||||||
if(!once)
|
// Output formatting
|
||||||
{
|
///////
|
||||||
// Upon startup, we reset the HomeLocation object to
|
Output2OP out;
|
||||||
// the plane's location:
|
memset(&out, 0, sizeof(Output2OP));
|
||||||
memset(&homeData, 0, sizeof(HomeLocation::DataFields));
|
|
||||||
// Update homelocation
|
|
||||||
homeData.Latitude = latitude * 1e7;
|
|
||||||
homeData.Longitude = longitude * 1e7;
|
|
||||||
homeData.Altitude = altitude;
|
|
||||||
double LLA[3];
|
|
||||||
LLA[0]=latitude;
|
|
||||||
LLA[1]=longitude;
|
|
||||||
LLA[2]=altitude;
|
|
||||||
double ECEF[3];
|
|
||||||
double RNE[9];
|
|
||||||
Utils::CoordinateConversions().RneFromLLA(LLA,(double (*)[3])RNE);
|
|
||||||
Utils::CoordinateConversions().LLA2ECEF(LLA,ECEF);
|
|
||||||
homeData.Be[0]=0;
|
|
||||||
homeData.Be[1]=0;
|
|
||||||
homeData.Be[2]=0;
|
|
||||||
posHome->setData(homeData);
|
|
||||||
posHome->updated();
|
|
||||||
|
|
||||||
// Initialize the initial distance
|
// Update GPS Position objects
|
||||||
initX = dstX;
|
out.latitude = latitude * 1e7;
|
||||||
initY = dstY;
|
out.longitude = longitude * 1e7;
|
||||||
initZ = dstZ;
|
out.altitude = altitude;
|
||||||
once=1;
|
out.groundspeed = groundspeed_ktgs*1.15*1.6089/3.6; //Convert from [kts] to [m/s]
|
||||||
}
|
|
||||||
|
out.calibratedAirspeed = airspeed_keas*1.15*1.6089/3.6; //Convert from [kts] to [m/s]
|
||||||
|
|
||||||
|
// Update BaroAltitude object
|
||||||
|
out.temperature = temperature;
|
||||||
|
out.pressure = pressure;
|
||||||
|
|
||||||
|
// Update attActual object
|
||||||
|
out.roll = roll; //roll;
|
||||||
|
out.pitch = pitch; // pitch
|
||||||
|
out.heading = heading; // yaw
|
||||||
|
|
||||||
|
|
||||||
// Update AltitudeActual object
|
out.dstN=dstY;
|
||||||
BaroAltitude::DataFields baroAltData;
|
out.dstE=dstX;
|
||||||
memset(&baroAltData, 0, sizeof(BaroAltitude::DataFields));
|
out.dstD=-dstZ;
|
||||||
baroAltData.Altitude = altitude;
|
|
||||||
baroAltData.Temperature = temperature;
|
|
||||||
baroAltData.Pressure = pressure;
|
|
||||||
baroAlt->setData(baroAltData);
|
|
||||||
|
|
||||||
// Update attActual object
|
// Update VelocityActual.{North,East,Down}
|
||||||
AttitudeActual::DataFields attActualData;
|
out.velNorth = velY;
|
||||||
memset(&attActualData, 0, sizeof(AttitudeActual::DataFields));
|
out.velEast = velX;
|
||||||
attActualData.Roll = roll; //roll;
|
out.velDown = -velZ;
|
||||||
attActualData.Pitch = pitch; // pitch
|
|
||||||
attActualData.Yaw = heading; // Yaw
|
|
||||||
float rpy[3];
|
|
||||||
float quat[4];
|
|
||||||
rpy[0] = roll;
|
|
||||||
rpy[1] = pitch;
|
|
||||||
rpy[2] = heading;
|
|
||||||
Utils::CoordinateConversions().RPY2Quaternion(rpy,quat);
|
|
||||||
attActualData.q1 = quat[0];
|
|
||||||
attActualData.q2 = quat[1];
|
|
||||||
attActualData.q3 = quat[2];
|
|
||||||
attActualData.q4 = quat[3];
|
|
||||||
attActual->setData(attActualData);
|
|
||||||
|
|
||||||
// Update gps objects
|
//Update gyroscope sensor data
|
||||||
GPSPosition::DataFields gpsData;
|
out.rollRate = rollRate_rad;
|
||||||
memset(&gpsData, 0, sizeof(GPSPosition::DataFields));
|
out.pitchRate = pitchRate_rad;
|
||||||
gpsData.Altitude = altitude;
|
out.yawRate = yawRate_rad;
|
||||||
gpsData.Heading = heading;
|
|
||||||
gpsData.Groundspeed = speed;
|
|
||||||
gpsData.Latitude = latitude*1e7;
|
|
||||||
gpsData.Longitude = longitude*1e7;
|
|
||||||
gpsData.Satellites = 10;
|
|
||||||
gpsData.Status = GPSPosition::STATUS_FIX3D;
|
|
||||||
gpsPos->setData(gpsData);
|
|
||||||
|
|
||||||
// Update VelocityActual.{Nort,East,Down}
|
//Update accelerometer sensor data
|
||||||
VelocityActual::DataFields velocityActualData;
|
out.accX = accX;
|
||||||
memset(&velocityActualData, 0, sizeof(VelocityActual::DataFields));
|
out.accY = accY;
|
||||||
velocityActualData.North = velY;
|
out.accZ = -accZ;
|
||||||
velocityActualData.East = velX;
|
|
||||||
velocityActualData.Down = -velZ;
|
|
||||||
velActual->setData(velocityActualData);
|
|
||||||
|
|
||||||
// Update PositionActual.{Nort,East,Down}
|
updateUAVOs(out);
|
||||||
PositionActual::DataFields positionActualData;
|
}
|
||||||
memset(&positionActualData, 0, sizeof(PositionActual::DataFields));
|
// issue manual update
|
||||||
positionActualData.North = (dstY-initY);
|
//attActual->updated();
|
||||||
positionActualData.East = (dstX-initX);
|
//altActual->updated();
|
||||||
positionActualData.Down = -(dstZ-initZ);
|
//posActual->updated();
|
||||||
posActual->setData(positionActualData);
|
|
||||||
|
|
||||||
// Update AttitudeRaw object (filtered gyros only for now)
|
|
||||||
//AttitudeRaw::DataFields rawData;
|
|
||||||
//memset(&rawData, 0, sizeof(AttitudeRaw::DataFields));
|
|
||||||
//rawData = attRaw->getData();
|
|
||||||
//rawData.gyros[0] = rollRate;
|
|
||||||
//rawData.gyros_filtered[1] = cos(DEG2RAD * roll) * pitchRate_rad + sin(DEG2RAD * roll) * yawRate_rad;
|
|
||||||
//rawData.gyros_filtered[2] = cos(DEG2RAD * roll) * yawRate_rad - sin(DEG2RAD * roll) * pitchRate_rad;
|
|
||||||
//rawData.gyros[1] = pitchRate;
|
|
||||||
//rawData.gyros[2] = yawRate;
|
|
||||||
//rawData.accels[0] = accX;
|
|
||||||
//rawData.accels[1] = accY;
|
|
||||||
//rawData.accels[2] = -accZ;
|
|
||||||
//attRaw->setData(rawData);
|
|
||||||
Gyros::DataFields gyroData;
|
|
||||||
memset(&gyroData, 0, sizeof(Gyros::DataFields));
|
|
||||||
#define Pi 3.141529654
|
|
||||||
gyroData.x = rollRate_rad*180/Pi;
|
|
||||||
gyroData.y = pitchRate_rad*180/Pi;
|
|
||||||
gyroData.z = yawRate_rad*180/Pi;
|
|
||||||
gyros->setData(gyroData);
|
|
||||||
|
|
||||||
Accels::DataFields accelData;
|
|
||||||
memset(&accelData, 0, sizeof(Accels::DataFields));
|
|
||||||
accelData.x = accX;
|
|
||||||
accelData.y = accY;
|
|
||||||
accelData.z = -accZ;
|
|
||||||
accels->setData(accelData);
|
|
||||||
|
|
||||||
}
|
|
||||||
// issue manual update
|
|
||||||
//attActual->updated();
|
|
||||||
//baroAlt->updated();
|
|
||||||
//posActual->updated();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -45,12 +45,8 @@ private slots:
|
|||||||
void transmitUpdate();
|
void transmitUpdate();
|
||||||
|
|
||||||
private:
|
private:
|
||||||
bool once;
|
enum XplaneOutputData //***WARNING***: Elements in this enum are in a precise order, do
|
||||||
float initX;
|
{ // not change. Cf. http://www.nuclearprojects.com/xplane/info.shtml
|
||||||
float initY;
|
|
||||||
float initZ;
|
|
||||||
enum XplaneOutputData
|
|
||||||
{
|
|
||||||
FramRate,
|
FramRate,
|
||||||
Times,
|
Times,
|
||||||
SimStats,
|
SimStats,
|
||||||
@ -68,10 +64,10 @@ private:
|
|||||||
Brakes,
|
Brakes,
|
||||||
AngularMoments,
|
AngularMoments,
|
||||||
AngularAccelerations,
|
AngularAccelerations,
|
||||||
AngularVelocities,
|
AngularVelocities,
|
||||||
PitchRollHeading,
|
PitchRollHeading,
|
||||||
AoA,
|
AoA,
|
||||||
LatitudeLongitude,
|
LatitudeLongitudeAltitude,
|
||||||
LocVelDistTraveled
|
LocVelDistTraveled
|
||||||
};
|
};
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user