2023-02-26 14:55:33 +01:00
|
|
|
#include "AcsBoardPolling.h"
|
|
|
|
|
|
|
|
#include <fcntl.h>
|
|
|
|
#include <fsfw/globalfunctions/arrayprinter.h>
|
|
|
|
#include <fsfw/tasks/SemaphoreFactory.h>
|
|
|
|
#include <fsfw/tasks/TaskFactory.h>
|
|
|
|
#include <fsfw_hal/devicehandlers/devicedefinitions/gyroL3gHelpers.h>
|
2023-02-26 21:26:49 +01:00
|
|
|
#include <fsfw_hal/devicehandlers/devicedefinitions/mgmLis3Helpers.h>
|
2023-02-26 14:55:33 +01:00
|
|
|
#include <fsfw_hal/linux/UnixFileGuard.h>
|
|
|
|
#include <fsfw_hal/linux/spi/SpiCookie.h>
|
|
|
|
#include <fsfw_hal/linux/utility.h>
|
|
|
|
#include <mission/devices/devicedefinitions/gyroAdisHelpers.h>
|
|
|
|
#include <sys/ioctl.h>
|
|
|
|
|
|
|
|
#include "devices/gpioIds.h"
|
|
|
|
|
|
|
|
using namespace returnvalue;
|
|
|
|
|
|
|
|
AcsBoardPolling::AcsBoardPolling(object_id_t objectId, SpiComIF& lowLevelComIF, GpioIF& gpioIF)
|
|
|
|
: SystemObject(objectId), spiComIF(lowLevelComIF), gpioIF(gpioIF) {
|
|
|
|
semaphore = SemaphoreFactory::instance()->createBinarySemaphore();
|
|
|
|
semaphore->acquire();
|
|
|
|
ipcLock = MutexFactory::instance()->createMutex();
|
|
|
|
}
|
|
|
|
|
|
|
|
ReturnValue_t AcsBoardPolling::performOperation(uint8_t operationCode) {
|
|
|
|
while (true) {
|
|
|
|
ipcLock->lockMutex();
|
|
|
|
state = InternalState::IDLE;
|
|
|
|
ipcLock->unlockMutex();
|
|
|
|
semaphore->acquire();
|
|
|
|
// Give all tasks or the PST some time to submit all consecutive requests.
|
|
|
|
TaskFactory::delayTask(2);
|
|
|
|
gyroAdisHandler(gyro0Adis);
|
|
|
|
gyroAdisHandler(gyro2Adis);
|
|
|
|
gyroL3gHandler(gyro1L3g);
|
|
|
|
gyroL3gHandler(gyro3L3g);
|
2023-02-26 21:26:49 +01:00
|
|
|
mgmRm3100Handler(mgm1Rm3100);
|
|
|
|
mgmRm3100Handler(mgm3Rm3100);
|
|
|
|
mgmLis3Handler(mgm0Lis3);
|
|
|
|
mgmLis3Handler(mgm2Lis3);
|
2023-02-26 14:55:33 +01:00
|
|
|
// To prevent task being not reactivated by tardy tasks
|
|
|
|
TaskFactory::delayTask(20);
|
|
|
|
}
|
|
|
|
return returnvalue::OK;
|
|
|
|
}
|
|
|
|
|
|
|
|
ReturnValue_t AcsBoardPolling::initialize() { return returnvalue::OK; }
|
|
|
|
|
|
|
|
ReturnValue_t AcsBoardPolling::initializeInterface(CookieIF* cookie) {
|
|
|
|
SpiCookie* spiCookie = dynamic_cast<SpiCookie*>(cookie);
|
|
|
|
if (spiCookie == nullptr) {
|
|
|
|
return returnvalue::FAILED;
|
|
|
|
}
|
|
|
|
switch (spiCookie->getChipSelectPin()) {
|
|
|
|
case (gpioIds::MGM_0_LIS3_CS): {
|
2023-02-26 21:26:49 +01:00
|
|
|
mgm0Lis3.cookie = spiCookie;
|
2023-02-26 14:55:33 +01:00
|
|
|
break;
|
|
|
|
}
|
|
|
|
case (gpioIds::MGM_1_RM3100_CS): {
|
2023-02-26 21:26:49 +01:00
|
|
|
mgm1Rm3100.cookie = spiCookie;
|
2023-02-26 14:55:33 +01:00
|
|
|
break;
|
|
|
|
}
|
|
|
|
case (gpioIds::MGM_2_LIS3_CS): {
|
2023-02-26 21:26:49 +01:00
|
|
|
mgm2Lis3.cookie = spiCookie;
|
2023-02-26 14:55:33 +01:00
|
|
|
break;
|
|
|
|
}
|
|
|
|
case (gpioIds::MGM_3_RM3100_CS): {
|
2023-02-26 21:26:49 +01:00
|
|
|
mgm3Rm3100.cookie = spiCookie;
|
2023-02-26 14:55:33 +01:00
|
|
|
break;
|
|
|
|
}
|
|
|
|
case (gpioIds::GYRO_0_ADIS_CS): {
|
|
|
|
gyro0Adis.cookie = spiCookie;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
case (gpioIds::GYRO_1_L3G_CS): {
|
|
|
|
gyro1L3g.cookie = spiCookie;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
case (gpioIds::GYRO_2_ADIS_CS): {
|
|
|
|
gyro2Adis.cookie = spiCookie;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
case (gpioIds::GYRO_3_L3G_CS): {
|
|
|
|
gyro3L3g.cookie = spiCookie;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
default: {
|
|
|
|
sif::error << "AcsBoardPollingTask: invalid spi cookie" << std::endl;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return spiComIF.initializeInterface(cookie);
|
|
|
|
}
|
|
|
|
|
|
|
|
ReturnValue_t AcsBoardPolling::sendMessage(CookieIF* cookie, const uint8_t* sendData,
|
|
|
|
size_t sendLen) {
|
|
|
|
SpiCookie* spiCookie = dynamic_cast<SpiCookie*>(cookie);
|
|
|
|
if (spiCookie == nullptr) {
|
|
|
|
return returnvalue::FAILED;
|
|
|
|
}
|
|
|
|
auto handleAdisRequest = [&](GyroAdis& adis) {
|
|
|
|
if (sendLen != sizeof(acs::Adis1650XRequest)) {
|
|
|
|
sif::error << "AcsBoardPolling: invalid adis request send length";
|
|
|
|
adis.replyResult = returnvalue::FAILED;
|
|
|
|
return returnvalue::FAILED;
|
|
|
|
}
|
|
|
|
auto* req = reinterpret_cast<const acs::Adis1650XRequest*>(sendData);
|
2023-03-02 15:32:12 +01:00
|
|
|
MutexGuard mg(ipcLock, LOCK_TYPE, LOCK_TIMEOUT, LOCK_CTX);
|
2023-02-26 14:55:33 +01:00
|
|
|
if (req->mode != adis.mode) {
|
|
|
|
if (req->mode == acs::SimpleSensorMode::NORMAL) {
|
|
|
|
adis.type = req->type;
|
|
|
|
adis.countdown.setTimeout(adis1650x::START_UP_TIME);
|
|
|
|
adis.countdown.resetTimer();
|
|
|
|
if (adis.type == adis1650x::Type::ADIS16507) {
|
|
|
|
adis.ownReply.data.accelScaling = adis1650x::ACCELEROMETER_RANGE_16507;
|
|
|
|
} else if (adis.type == adis1650x::Type::ADIS16505) {
|
|
|
|
adis.ownReply.data.accelScaling = adis1650x::ACCELEROMETER_RANGE_16505;
|
|
|
|
} else {
|
|
|
|
sif::warning << "AcsBoardPolling: Unknown ADIS type" << std::endl;
|
|
|
|
}
|
|
|
|
adis.performStartup = true;
|
|
|
|
} else if (req->mode == acs::SimpleSensorMode::OFF) {
|
|
|
|
adis.performStartup = false;
|
|
|
|
adis.ownReply.cfgWasSet = false;
|
|
|
|
adis.ownReply.dataWasSet = false;
|
|
|
|
}
|
|
|
|
adis.mode = req->mode;
|
|
|
|
}
|
|
|
|
return returnvalue::OK;
|
|
|
|
};
|
|
|
|
auto handleL3gRequest = [&](GyroL3g& gyro) {
|
|
|
|
if (sendLen != sizeof(acs::GyroL3gRequest)) {
|
|
|
|
sif::error << "AcsBoardPolling: invalid l3g request send length";
|
|
|
|
gyro.replyResult = returnvalue::FAILED;
|
|
|
|
return returnvalue::FAILED;
|
|
|
|
}
|
|
|
|
auto* req = reinterpret_cast<const acs::GyroL3gRequest*>(sendData);
|
2023-03-02 15:32:12 +01:00
|
|
|
MutexGuard mg(ipcLock, LOCK_TYPE, LOCK_TIMEOUT, LOCK_CTX);
|
2023-02-26 14:55:33 +01:00
|
|
|
if (req->mode != gyro.mode) {
|
|
|
|
if (req->mode == acs::SimpleSensorMode::NORMAL) {
|
2023-02-26 19:38:48 +01:00
|
|
|
std::memcpy(gyro.sensorCfg, req->ctrlRegs, 5);
|
2023-02-26 14:55:33 +01:00
|
|
|
gyro.performStartup = true;
|
|
|
|
} else {
|
|
|
|
gyro.ownReply.cfgWasSet = false;
|
|
|
|
}
|
|
|
|
gyro.mode = req->mode;
|
|
|
|
}
|
|
|
|
return returnvalue::OK;
|
|
|
|
};
|
2023-02-26 21:26:49 +01:00
|
|
|
auto handleLis3Request = [&](MgmLis3& mgm) {
|
|
|
|
if (sendLen != sizeof(acs::MgmLis3Request)) {
|
|
|
|
sif::error << "AcsBoardPolling: invalid lis3 request send length";
|
|
|
|
mgm.replyResult = returnvalue::FAILED;
|
|
|
|
return returnvalue::FAILED;
|
|
|
|
}
|
|
|
|
auto* req = reinterpret_cast<const acs::MgmLis3Request*>(sendData);
|
2023-03-02 15:32:12 +01:00
|
|
|
MutexGuard mg(ipcLock, LOCK_TYPE, LOCK_TIMEOUT, LOCK_CTX);
|
2023-02-26 21:26:49 +01:00
|
|
|
if (req->mode != mgm.mode) {
|
|
|
|
if (req->mode == acs::SimpleSensorMode::NORMAL) {
|
|
|
|
mgm.performStartup = true;
|
|
|
|
} else {
|
|
|
|
mgm.ownReply.dataWasSet = false;
|
|
|
|
mgm.ownReply.temperatureWasSet = false;
|
|
|
|
}
|
|
|
|
mgm.mode = req->mode;
|
|
|
|
}
|
|
|
|
return returnvalue::OK;
|
|
|
|
};
|
|
|
|
auto handleRm3100Request = [&](MgmRm3100& mgm) {
|
|
|
|
if (sendLen != sizeof(acs::MgmRm3100Request)) {
|
|
|
|
sif::error << "AcsBoardPolling: invalid rm3100 request send length";
|
|
|
|
mgm.replyResult = returnvalue::FAILED;
|
|
|
|
return returnvalue::FAILED;
|
|
|
|
}
|
|
|
|
auto* req = reinterpret_cast<const acs::MgmRm3100Request*>(sendData);
|
2023-03-02 15:32:12 +01:00
|
|
|
MutexGuard mg(ipcLock, LOCK_TYPE, LOCK_TIMEOUT, LOCK_CTX);
|
2023-02-26 21:26:49 +01:00
|
|
|
if (req->mode != mgm.mode) {
|
|
|
|
if (req->mode == acs::SimpleSensorMode::NORMAL) {
|
|
|
|
mgm.performStartup = true;
|
|
|
|
} else {
|
2023-02-28 01:25:25 +01:00
|
|
|
mgm.ownReply.dataWasRead = false;
|
2023-02-26 21:26:49 +01:00
|
|
|
}
|
|
|
|
mgm.mode = req->mode;
|
|
|
|
}
|
|
|
|
return returnvalue::OK;
|
|
|
|
};
|
2023-02-26 14:55:33 +01:00
|
|
|
switch (spiCookie->getChipSelectPin()) {
|
2023-02-26 21:26:49 +01:00
|
|
|
case (gpioIds::MGM_0_LIS3_CS): {
|
|
|
|
handleLis3Request(mgm0Lis3);
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
case (gpioIds::MGM_1_RM3100_CS): {
|
|
|
|
handleRm3100Request(mgm1Rm3100);
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
case (gpioIds::MGM_2_LIS3_CS): {
|
|
|
|
handleLis3Request(mgm2Lis3);
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
case (gpioIds::MGM_3_RM3100_CS): {
|
|
|
|
handleRm3100Request(mgm3Rm3100);
|
|
|
|
break;
|
|
|
|
}
|
2023-02-26 14:55:33 +01:00
|
|
|
case (gpioIds::GYRO_0_ADIS_CS): {
|
|
|
|
handleAdisRequest(gyro0Adis);
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
case (gpioIds::GYRO_2_ADIS_CS): {
|
|
|
|
handleAdisRequest(gyro2Adis);
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
case (gpioIds::GYRO_1_L3G_CS): {
|
|
|
|
handleL3gRequest(gyro1L3g);
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
case (gpioIds::GYRO_3_L3G_CS): {
|
|
|
|
handleL3gRequest(gyro3L3g);
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
2023-03-02 15:32:12 +01:00
|
|
|
MutexGuard mg(ipcLock, LOCK_TYPE, LOCK_TIMEOUT, LOCK_CTX);
|
2023-02-26 14:55:33 +01:00
|
|
|
if (state == InternalState::IDLE) {
|
|
|
|
state = InternalState::BUSY;
|
|
|
|
semaphore->release();
|
|
|
|
}
|
|
|
|
return returnvalue::OK;
|
|
|
|
}
|
|
|
|
|
|
|
|
ReturnValue_t AcsBoardPolling::getSendSuccess(CookieIF* cookie) { return returnvalue::OK; }
|
|
|
|
|
|
|
|
ReturnValue_t AcsBoardPolling::requestReceiveMessage(CookieIF* cookie, size_t requestLen) {
|
|
|
|
return returnvalue::OK;
|
|
|
|
}
|
|
|
|
|
|
|
|
ReturnValue_t AcsBoardPolling::readReceivedMessage(CookieIF* cookie, uint8_t** buffer,
|
|
|
|
size_t* size) {
|
|
|
|
SpiCookie* spiCookie = dynamic_cast<SpiCookie*>(cookie);
|
|
|
|
if (spiCookie == nullptr) {
|
|
|
|
return returnvalue::FAILED;
|
|
|
|
}
|
2023-03-02 15:32:12 +01:00
|
|
|
MutexGuard mg(ipcLock, LOCK_TYPE, LOCK_TIMEOUT, LOCK_CTX);
|
2023-02-26 14:55:33 +01:00
|
|
|
auto handleAdisReply = [&](GyroAdis& gyro) {
|
|
|
|
std::memcpy(&gyro.readerReply, &gyro.ownReply, sizeof(acs::Adis1650XReply));
|
|
|
|
*buffer = reinterpret_cast<uint8_t*>(&gyro.readerReply);
|
|
|
|
*size = sizeof(acs::Adis1650XReply);
|
2023-02-26 21:26:49 +01:00
|
|
|
return gyro.replyResult;
|
2023-02-26 14:55:33 +01:00
|
|
|
};
|
|
|
|
auto handleL3gReply = [&](GyroL3g& gyro) {
|
|
|
|
std::memcpy(&gyro.readerReply, &gyro.ownReply, sizeof(acs::GyroL3gReply));
|
|
|
|
*buffer = reinterpret_cast<uint8_t*>(&gyro.readerReply);
|
|
|
|
*size = sizeof(acs::GyroL3gReply);
|
2023-02-26 21:26:49 +01:00
|
|
|
return gyro.replyResult;
|
|
|
|
};
|
|
|
|
auto handleRm3100Reply = [&](MgmRm3100& mgm) {
|
|
|
|
std::memcpy(&mgm.readerReply, &mgm.ownReply, sizeof(acs::MgmRm3100Reply));
|
|
|
|
*buffer = reinterpret_cast<uint8_t*>(&mgm.readerReply);
|
|
|
|
*size = sizeof(acs::MgmRm3100Reply);
|
|
|
|
return mgm.replyResult;
|
|
|
|
};
|
|
|
|
auto handleLis3Reply = [&](MgmLis3& mgm) {
|
|
|
|
std::memcpy(&mgm.readerReply, &mgm.ownReply, sizeof(acs::MgmLis3Reply));
|
|
|
|
*buffer = reinterpret_cast<uint8_t*>(&mgm.readerReply);
|
|
|
|
*size = sizeof(acs::MgmLis3Reply);
|
|
|
|
return mgm.replyResult;
|
2023-02-26 14:55:33 +01:00
|
|
|
};
|
|
|
|
switch (spiCookie->getChipSelectPin()) {
|
2023-02-26 21:26:49 +01:00
|
|
|
case (gpioIds::MGM_0_LIS3_CS): {
|
|
|
|
return handleLis3Reply(mgm0Lis3);
|
|
|
|
}
|
|
|
|
case (gpioIds::MGM_1_RM3100_CS): {
|
|
|
|
return handleRm3100Reply(mgm1Rm3100);
|
|
|
|
}
|
|
|
|
case (gpioIds::MGM_2_LIS3_CS): {
|
|
|
|
return handleLis3Reply(mgm2Lis3);
|
|
|
|
}
|
|
|
|
case (gpioIds::MGM_3_RM3100_CS): {
|
|
|
|
return handleRm3100Reply(mgm3Rm3100);
|
|
|
|
}
|
2023-02-26 14:55:33 +01:00
|
|
|
case (gpioIds::GYRO_0_ADIS_CS): {
|
2023-02-26 21:26:49 +01:00
|
|
|
return handleAdisReply(gyro0Adis);
|
2023-02-26 14:55:33 +01:00
|
|
|
}
|
|
|
|
case (gpioIds::GYRO_2_ADIS_CS): {
|
2023-02-26 21:26:49 +01:00
|
|
|
return handleAdisReply(gyro2Adis);
|
2023-02-26 14:55:33 +01:00
|
|
|
}
|
|
|
|
case (gpioIds::GYRO_1_L3G_CS): {
|
2023-02-26 21:26:49 +01:00
|
|
|
return handleL3gReply(gyro1L3g);
|
2023-02-26 14:55:33 +01:00
|
|
|
}
|
|
|
|
case (gpioIds::GYRO_3_L3G_CS): {
|
2023-02-26 21:26:49 +01:00
|
|
|
return handleL3gReply(gyro3L3g);
|
2023-02-26 14:55:33 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
return returnvalue::OK;
|
|
|
|
}
|
|
|
|
|
|
|
|
void AcsBoardPolling::gyroL3gHandler(GyroL3g& l3g) {
|
|
|
|
ReturnValue_t result;
|
|
|
|
acs::SimpleSensorMode mode;
|
|
|
|
bool gyroPerformStartup;
|
|
|
|
{
|
2023-03-02 15:32:12 +01:00
|
|
|
MutexGuard mg(ipcLock, LOCK_TYPE, LOCK_TIMEOUT, LOCK_CTX);
|
2023-02-26 14:55:33 +01:00
|
|
|
mode = l3g.mode;
|
|
|
|
gyroPerformStartup = l3g.performStartup;
|
|
|
|
}
|
|
|
|
if (mode == acs::SimpleSensorMode::NORMAL) {
|
|
|
|
if (gyroPerformStartup) {
|
|
|
|
cmdBuf[0] = l3gd20h::CTRL_REG_1 | l3gd20h::AUTO_INCREMENT_MASK;
|
|
|
|
std::memcpy(cmdBuf.data() + 1, l3g.sensorCfg, 5);
|
|
|
|
result = spiComIF.sendMessage(l3g.cookie, cmdBuf.data(), 6);
|
|
|
|
if (result != returnvalue::OK) {
|
|
|
|
l3g.replyResult = returnvalue::OK;
|
|
|
|
}
|
|
|
|
// Ignore useless reply and red config
|
|
|
|
cmdBuf[0] = l3gd20h::CTRL_REG_1 | l3gd20h::AUTO_INCREMENT_MASK | l3gd20h::READ_MASK;
|
|
|
|
std::memset(cmdBuf.data() + 1, 0, 5);
|
|
|
|
result = spiComIF.sendMessage(l3g.cookie, cmdBuf.data(), 6);
|
|
|
|
if (result != returnvalue::OK) {
|
|
|
|
l3g.replyResult = returnvalue::OK;
|
|
|
|
}
|
|
|
|
result = spiComIF.readReceivedMessage(l3g.cookie, &rawReply, &dummy);
|
|
|
|
if (result != returnvalue::OK) {
|
|
|
|
l3g.replyResult = returnvalue::OK;
|
|
|
|
}
|
2023-03-02 15:32:12 +01:00
|
|
|
MutexGuard mg(ipcLock, LOCK_TYPE, LOCK_TIMEOUT, LOCK_CTX);
|
2023-02-26 14:55:33 +01:00
|
|
|
// Cross check configuration as verification that communication is working
|
|
|
|
for (uint8_t idx = 0; idx < 5; idx++) {
|
|
|
|
if (rawReply[idx + 1] != l3g.sensorCfg[idx]) {
|
|
|
|
sif::warning << "AcsBoardPolling: l3g config check missmatch" << std::endl;
|
|
|
|
l3g.replyResult = returnvalue::FAILED;
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
}
|
2023-02-26 19:23:55 +01:00
|
|
|
l3g.performStartup = false;
|
2023-02-26 14:55:33 +01:00
|
|
|
l3g.ownReply.cfgWasSet = true;
|
|
|
|
l3g.ownReply.sensitivity = l3gd20h::ctrlReg4ToSensitivity(l3g.sensorCfg[3]);
|
|
|
|
}
|
|
|
|
cmdBuf[0] = l3gd20h::READ_START | l3gd20h::AUTO_INCREMENT_MASK | l3gd20h::READ_MASK;
|
|
|
|
std::memset(cmdBuf.data() + 1, 0, l3gd20h::READ_LEN);
|
2023-02-26 19:09:12 +01:00
|
|
|
result = spiComIF.sendMessage(l3g.cookie, cmdBuf.data(), l3gd20h::READ_LEN + 1);
|
2023-02-26 14:55:33 +01:00
|
|
|
if (result != returnvalue::OK) {
|
|
|
|
l3g.replyResult = returnvalue::FAILED;
|
|
|
|
return;
|
|
|
|
}
|
2023-02-26 19:09:12 +01:00
|
|
|
result = spiComIF.readReceivedMessage(l3g.cookie, &rawReply, &dummy);
|
|
|
|
if (result != returnvalue::OK) {
|
|
|
|
l3g.replyResult = returnvalue::FAILED;
|
|
|
|
return;
|
|
|
|
}
|
2023-03-02 15:32:12 +01:00
|
|
|
MutexGuard mg(ipcLock, LOCK_TYPE, LOCK_TIMEOUT, LOCK_CTX);
|
2023-02-26 14:55:33 +01:00
|
|
|
// The regular read function always returns the full sensor config as well. Use that
|
|
|
|
// to verify communications.
|
|
|
|
for (uint8_t idx = 0; idx < 5; idx++) {
|
|
|
|
if (rawReply[idx + 1] != l3g.sensorCfg[idx]) {
|
|
|
|
sif::warning << "AcsBoardPolling: l3g config check missmatch" << std::endl;
|
|
|
|
l3g.replyResult = returnvalue::FAILED;
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
l3g.ownReply.statusReg = rawReply[l3gd20h::STATUS_IDX];
|
|
|
|
l3g.ownReply.angVelocities[0] = (rawReply[l3gd20h::OUT_X_H] << 8) | rawReply[l3gd20h::OUT_X_L];
|
|
|
|
l3g.ownReply.angVelocities[1] = (rawReply[l3gd20h::OUT_Y_H] << 8) | rawReply[l3gd20h::OUT_Y_L];
|
|
|
|
l3g.ownReply.angVelocities[2] = (rawReply[l3gd20h::OUT_Z_H] << 8) | rawReply[l3gd20h::OUT_Z_L];
|
|
|
|
l3g.ownReply.tempOffsetRaw = rawReply[l3gd20h::TEMPERATURE_IDX];
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
ReturnValue_t AcsBoardPolling::readAdisCfg(SpiCookie& cookie, size_t transferLen) {
|
|
|
|
ReturnValue_t result = returnvalue::OK;
|
|
|
|
int retval = 0;
|
|
|
|
// Prepare transfer
|
|
|
|
int fileDescriptor = 0;
|
|
|
|
std::string device = spiComIF.getSpiDev();
|
|
|
|
UnixFileGuard fileHelper(device, fileDescriptor, O_RDWR, "SpiComIF::sendMessage");
|
|
|
|
if (fileHelper.getOpenResult() != returnvalue::OK) {
|
|
|
|
return SpiComIF::OPENING_FILE_FAILED;
|
|
|
|
}
|
|
|
|
spi::SpiModes spiMode = spi::SpiModes::MODE_0;
|
|
|
|
uint32_t spiSpeed = 0;
|
|
|
|
cookie.getSpiParameters(spiMode, spiSpeed, nullptr);
|
|
|
|
spiComIF.setSpiSpeedAndMode(fileDescriptor, spiMode, spiSpeed);
|
|
|
|
cookie.assignWriteBuffer(cmdBuf.data());
|
|
|
|
cookie.setTransferSize(2);
|
|
|
|
|
|
|
|
gpioId_t gpioId = cookie.getChipSelectPin();
|
|
|
|
MutexIF::TimeoutType timeoutType = MutexIF::TimeoutType::WAITING;
|
|
|
|
uint32_t timeoutMs = 0;
|
|
|
|
MutexIF* mutex = spiComIF.getCsMutex();
|
|
|
|
cookie.getMutexParams(timeoutType, timeoutMs);
|
|
|
|
if (mutex == nullptr) {
|
|
|
|
sif::warning << "GyroADIS16507Handler::spiSendCallback: "
|
|
|
|
"Mutex or GPIO interface invalid"
|
|
|
|
<< std::endl;
|
|
|
|
return returnvalue::FAILED;
|
|
|
|
}
|
|
|
|
|
|
|
|
size_t idx = 0;
|
|
|
|
spi_ioc_transfer* transferStruct = cookie.getTransferStructHandle();
|
|
|
|
uint64_t origTx = transferStruct->tx_buf;
|
|
|
|
uint64_t origRx = transferStruct->rx_buf;
|
|
|
|
while (idx < transferLen) {
|
|
|
|
result = mutex->lockMutex(timeoutType, timeoutMs);
|
|
|
|
if (result != returnvalue::OK) {
|
|
|
|
#if FSFW_CPP_OSTREAM_ENABLED == 1
|
|
|
|
sif::error << "AcsBoardPolling: Failed to lock mutex" << std::endl;
|
|
|
|
#endif
|
|
|
|
return result;
|
|
|
|
}
|
|
|
|
// Pull SPI CS low. For now, no support for active high given
|
|
|
|
if (gpioId != gpio::NO_GPIO) {
|
|
|
|
gpioIF.pullLow(gpioId);
|
|
|
|
}
|
|
|
|
|
|
|
|
// Execute transfer
|
|
|
|
// Initiate a full duplex SPI transfer.
|
|
|
|
retval = ioctl(fileDescriptor, SPI_IOC_MESSAGE(1), cookie.getTransferStructHandle());
|
|
|
|
if (retval < 0) {
|
|
|
|
utility::handleIoctlError("SpiComIF::sendMessage: ioctl error.");
|
|
|
|
result = SpiComIF::FULL_DUPLEX_TRANSFER_FAILED;
|
|
|
|
}
|
|
|
|
#if FSFW_HAL_SPI_WIRETAPPING == 1
|
|
|
|
comIf->performSpiWiretapping(cookie);
|
|
|
|
#endif /* FSFW_LINUX_SPI_WIRETAPPING == 1 */
|
|
|
|
|
|
|
|
if (gpioId != gpio::NO_GPIO) {
|
|
|
|
gpioIF.pullHigh(gpioId);
|
|
|
|
}
|
|
|
|
mutex->unlockMutex();
|
|
|
|
|
|
|
|
idx += 2;
|
|
|
|
transferStruct->tx_buf += 2;
|
|
|
|
transferStruct->rx_buf += 2;
|
|
|
|
if (idx < transferLen) {
|
|
|
|
usleep(adis1650x::STALL_TIME_MICROSECONDS);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
transferStruct->tx_buf = origTx;
|
|
|
|
transferStruct->rx_buf = origRx;
|
|
|
|
cookie.setTransferSize(transferLen);
|
|
|
|
return returnvalue::OK;
|
|
|
|
}
|
|
|
|
|
|
|
|
void AcsBoardPolling::gyroAdisHandler(GyroAdis& gyro) {
|
|
|
|
ReturnValue_t result;
|
|
|
|
acs::SimpleSensorMode mode;
|
|
|
|
bool cdHasTimedOut = false;
|
|
|
|
bool mustPerformStartup = false;
|
|
|
|
{
|
2023-03-02 15:32:12 +01:00
|
|
|
MutexGuard mg(ipcLock, LOCK_TYPE, LOCK_TIMEOUT, LOCK_CTX);
|
2023-02-26 14:55:33 +01:00
|
|
|
mode = gyro.mode;
|
|
|
|
cdHasTimedOut = gyro.countdown.hasTimedOut();
|
|
|
|
mustPerformStartup = gyro.performStartup;
|
|
|
|
}
|
|
|
|
if (mode == acs::SimpleSensorMode::NORMAL and cdHasTimedOut) {
|
|
|
|
if (mustPerformStartup) {
|
|
|
|
uint8_t regList[6];
|
|
|
|
// Read configuration
|
|
|
|
regList[0] = adis1650x::DIAG_STAT_REG;
|
|
|
|
regList[1] = adis1650x::FILTER_CTRL_REG;
|
|
|
|
regList[2] = adis1650x::RANG_MDL_REG;
|
|
|
|
regList[3] = adis1650x::MSC_CTRL_REG;
|
|
|
|
regList[4] = adis1650x::DEC_RATE_REG;
|
|
|
|
regList[5] = adis1650x::PROD_ID_REG;
|
|
|
|
size_t transferLen =
|
|
|
|
adis1650x::prepareReadCommand(regList, sizeof(regList), cmdBuf.data(), cmdBuf.size());
|
|
|
|
result = readAdisCfg(*gyro.cookie, transferLen);
|
|
|
|
if (result != returnvalue::OK) {
|
|
|
|
gyro.replyResult = result;
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
result = spiComIF.readReceivedMessage(gyro.cookie, &rawReply, &dummy);
|
|
|
|
if (result != returnvalue::OK or rawReply == nullptr) {
|
|
|
|
gyro.replyResult = result;
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
uint16_t prodId = (rawReply[12] << 8) | rawReply[13];
|
|
|
|
if (((gyro.type == adis1650x::Type::ADIS16505) and (prodId != adis1650x::PROD_ID_16505)) or
|
|
|
|
((gyro.type == adis1650x::Type::ADIS16507) and (prodId != adis1650x::PROD_ID_16507))) {
|
|
|
|
sif::warning << "AcsPollingTask: Invalid ADIS product ID " << prodId << std::endl;
|
|
|
|
gyro.replyResult = returnvalue::FAILED;
|
|
|
|
return;
|
|
|
|
}
|
2023-03-02 15:32:12 +01:00
|
|
|
MutexGuard mg(ipcLock, LOCK_TYPE, LOCK_TIMEOUT, LOCK_CTX);
|
2023-02-26 14:55:33 +01:00
|
|
|
gyro.ownReply.cfgWasSet = true;
|
|
|
|
gyro.ownReply.cfg.diagStat = (rawReply[2] << 8) | rawReply[3];
|
|
|
|
gyro.ownReply.cfg.filterSetting = (rawReply[4] << 8) | rawReply[5];
|
|
|
|
gyro.ownReply.cfg.rangMdl = (rawReply[6] << 8) | rawReply[7];
|
|
|
|
gyro.ownReply.cfg.mscCtrlReg = (rawReply[8] << 8) | rawReply[9];
|
|
|
|
gyro.ownReply.cfg.decRateReg = (rawReply[10] << 8) | rawReply[11];
|
|
|
|
gyro.ownReply.cfg.prodId = prodId;
|
|
|
|
gyro.ownReply.data.sensitivity = adis1650x::rangMdlToSensitivity(gyro.ownReply.cfg.rangMdl);
|
|
|
|
gyro.performStartup = false;
|
|
|
|
}
|
|
|
|
// Read regular registers
|
|
|
|
std::memcpy(cmdBuf.data(), adis1650x::BURST_READ_ENABLE.data(),
|
|
|
|
adis1650x::BURST_READ_ENABLE.size());
|
|
|
|
std::memset(cmdBuf.data() + 2, 0, 10 * 2);
|
|
|
|
result = spiComIF.sendMessage(gyro.cookie, cmdBuf.data(), adis1650x::SENSOR_READOUT_SIZE);
|
|
|
|
if (result != returnvalue::OK) {
|
|
|
|
gyro.replyResult = returnvalue::FAILED;
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
result = spiComIF.readReceivedMessage(gyro.cookie, &rawReply, &dummy);
|
|
|
|
if (result != returnvalue::OK or rawReply == nullptr) {
|
|
|
|
gyro.replyResult = returnvalue::FAILED;
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
uint16_t checksum = (rawReply[20] << 8) | rawReply[21];
|
|
|
|
|
|
|
|
// Now verify the read checksum with the expected checksum according to datasheet p. 20
|
|
|
|
uint16_t calcChecksum = 0;
|
|
|
|
for (size_t idx = 2; idx < 20; idx++) {
|
|
|
|
calcChecksum += rawReply[idx];
|
|
|
|
}
|
|
|
|
if (checksum != calcChecksum) {
|
|
|
|
sif::warning << "AcsPollingTask: Invalid ADIS reply checksum" << std::endl;
|
|
|
|
gyro.replyResult = returnvalue::FAILED;
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
auto burstMode = adis1650x::burstModeFromMscCtrl(gyro.ownReply.cfg.mscCtrlReg);
|
|
|
|
if (burstMode != adis1650x::BurstModes::BURST_16_BURST_SEL_0) {
|
|
|
|
sif::error << "GyroADIS1650XHandler::interpretDeviceReply: Analysis for select burst mode"
|
|
|
|
" not implemented!"
|
|
|
|
<< std::endl;
|
|
|
|
gyro.replyResult = returnvalue::FAILED;
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2023-03-02 15:32:12 +01:00
|
|
|
MutexGuard mg(ipcLock, LOCK_TYPE, LOCK_TIMEOUT, LOCK_CTX);
|
2023-02-26 14:55:33 +01:00
|
|
|
gyro.ownReply.dataWasSet = true;
|
|
|
|
gyro.ownReply.cfg.diagStat = rawReply[2] << 8 | rawReply[3];
|
|
|
|
gyro.ownReply.data.angVelocities[0] = (rawReply[4] << 8) | rawReply[5];
|
|
|
|
gyro.ownReply.data.angVelocities[1] = (rawReply[6] << 8) | rawReply[7];
|
|
|
|
gyro.ownReply.data.angVelocities[2] = (rawReply[8] << 8) | rawReply[9];
|
|
|
|
|
|
|
|
gyro.ownReply.data.accelerations[0] = (rawReply[10] << 8) | rawReply[11];
|
|
|
|
gyro.ownReply.data.accelerations[1] = (rawReply[12] << 8) | rawReply[13];
|
|
|
|
gyro.ownReply.data.accelerations[2] = (rawReply[14] << 8) | rawReply[15];
|
|
|
|
|
|
|
|
gyro.ownReply.data.temperatureRaw = (rawReply[16] << 8) | rawReply[17];
|
|
|
|
}
|
|
|
|
}
|
2023-02-26 21:26:49 +01:00
|
|
|
|
|
|
|
void AcsBoardPolling::mgmLis3Handler(MgmLis3& mgm) {
|
|
|
|
ReturnValue_t result;
|
|
|
|
acs::SimpleSensorMode mode;
|
|
|
|
bool mustPerformStartup = false;
|
|
|
|
{
|
2023-03-02 15:32:12 +01:00
|
|
|
MutexGuard mg(ipcLock, LOCK_TYPE, LOCK_TIMEOUT, LOCK_CTX);
|
2023-02-26 21:26:49 +01:00
|
|
|
mode = mgm.mode;
|
|
|
|
mustPerformStartup = mgm.performStartup;
|
|
|
|
}
|
|
|
|
if (mode == acs::SimpleSensorMode::NORMAL) {
|
|
|
|
if (mustPerformStartup) {
|
|
|
|
// To check valid communication, read back identification
|
|
|
|
// register which should always be the same value.
|
|
|
|
cmdBuf[0] = mgmLis3::readCommand(mgmLis3::IDENTIFY_DEVICE_REG_ADDR);
|
|
|
|
cmdBuf[1] = 0x00;
|
|
|
|
result = spiComIF.sendMessage(mgm.cookie, cmdBuf.data(), 2);
|
|
|
|
if (result != OK) {
|
|
|
|
mgm.replyResult = result;
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
result = spiComIF.readReceivedMessage(mgm.cookie, &rawReply, &dummy);
|
|
|
|
if (result != OK) {
|
|
|
|
mgm.replyResult = result;
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
if (rawReply[1] != mgmLis3::DEVICE_ID) {
|
|
|
|
sif::error << "AcsPollingTask: invalid MGM lis3 device ID" << std::endl;
|
|
|
|
mgm.replyResult = result;
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
mgm.cfg[0] = mgmLis3::CTRL_REG1_DEFAULT;
|
|
|
|
mgm.cfg[1] = mgmLis3::CTRL_REG2_DEFAULT;
|
|
|
|
mgm.cfg[2] = mgmLis3::CTRL_REG3_DEFAULT;
|
|
|
|
mgm.cfg[3] = mgmLis3::CTRL_REG4_DEFAULT;
|
|
|
|
mgm.cfg[4] = mgmLis3::CTRL_REG5_DEFAULT;
|
|
|
|
cmdBuf[0] = mgmLis3::writeCommand(mgmLis3::CTRL_REG1, true);
|
|
|
|
std::memcpy(cmdBuf.data() + 1, mgm.cfg, 5);
|
|
|
|
result = spiComIF.sendMessage(mgm.cookie, cmdBuf.data(), 6);
|
|
|
|
if (result != OK) {
|
|
|
|
mgm.replyResult = result;
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
// Done here. We can always read back config and data during periodic handling
|
|
|
|
mgm.performStartup = false;
|
|
|
|
}
|
|
|
|
cmdBuf[0] = mgmLis3::readCommand(mgmLis3::CTRL_REG1, true);
|
|
|
|
std::memset(cmdBuf.data() + 1, 0, mgmLis3::NR_OF_DATA_AND_CFG_REGISTERS);
|
|
|
|
result =
|
|
|
|
spiComIF.sendMessage(mgm.cookie, cmdBuf.data(), mgmLis3::NR_OF_DATA_AND_CFG_REGISTERS + 1);
|
|
|
|
if (result != returnvalue::OK) {
|
|
|
|
mgm.replyResult = result;
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
result = spiComIF.readReceivedMessage(mgm.cookie, &rawReply, &dummy);
|
|
|
|
if (result != returnvalue::OK) {
|
|
|
|
mgm.replyResult = result;
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
// Verify communication by re-checking config
|
|
|
|
if (rawReply[1] != mgm.cfg[0] or rawReply[2] != mgm.cfg[1] or rawReply[3] != mgm.cfg[2] or
|
|
|
|
rawReply[4] != mgm.cfg[3] or rawReply[5] != mgm.cfg[4]) {
|
|
|
|
mgm.replyResult = result;
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
{
|
2023-03-02 15:32:12 +01:00
|
|
|
MutexGuard mg(ipcLock, LOCK_TYPE, LOCK_TIMEOUT, LOCK_CTX);
|
2023-02-26 21:26:49 +01:00
|
|
|
mgm.ownReply.dataWasSet = true;
|
|
|
|
mgm.ownReply.sensitivity = mgmLis3::getSensitivityFactor(mgmLis3::getSensitivity(mgm.cfg[1]));
|
|
|
|
mgm.ownReply.mgmValuesRaw[0] =
|
|
|
|
(rawReply[mgmLis3::X_HIGHBYTE_IDX] << 8) | rawReply[mgmLis3::X_LOWBYTE_IDX];
|
|
|
|
mgm.ownReply.mgmValuesRaw[1] =
|
|
|
|
(rawReply[mgmLis3::Y_HIGHBYTE_IDX] << 8) | rawReply[mgmLis3::Y_LOWBYTE_IDX];
|
|
|
|
mgm.ownReply.mgmValuesRaw[2] =
|
|
|
|
(rawReply[mgmLis3::Z_HIGHBYTE_IDX] << 8) | rawReply[mgmLis3::Z_LOWBYTE_IDX];
|
|
|
|
}
|
|
|
|
// Read tempetature
|
|
|
|
cmdBuf[0] = mgmLis3::readCommand(mgmLis3::TEMP_LOWBYTE, true);
|
|
|
|
result = spiComIF.sendMessage(mgm.cookie, cmdBuf.data(), 3);
|
|
|
|
if (result != returnvalue::OK) {
|
|
|
|
mgm.replyResult = result;
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
result = spiComIF.readReceivedMessage(mgm.cookie, &rawReply, &dummy);
|
|
|
|
if (result != returnvalue::OK) {
|
|
|
|
mgm.replyResult = result;
|
|
|
|
return;
|
|
|
|
}
|
2023-03-02 15:32:12 +01:00
|
|
|
MutexGuard mg(ipcLock, LOCK_TYPE, LOCK_TIMEOUT, LOCK_CTX);
|
2023-02-26 21:26:49 +01:00
|
|
|
mgm.ownReply.temperatureWasSet = true;
|
|
|
|
mgm.ownReply.temperatureRaw = (rawReply[2] << 8) | rawReply[1];
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void AcsBoardPolling::mgmRm3100Handler(MgmRm3100& mgm) {
|
|
|
|
ReturnValue_t result;
|
|
|
|
acs::SimpleSensorMode mode;
|
|
|
|
bool mustPerformStartup = false;
|
|
|
|
{
|
2023-03-02 15:32:12 +01:00
|
|
|
MutexGuard mg(ipcLock, LOCK_TYPE, LOCK_TIMEOUT, LOCK_CTX);
|
2023-02-26 21:26:49 +01:00
|
|
|
mode = mgm.mode;
|
|
|
|
mustPerformStartup = mgm.performStartup;
|
|
|
|
}
|
|
|
|
if (mode == acs::SimpleSensorMode::NORMAL) {
|
|
|
|
if (mustPerformStartup) {
|
|
|
|
// Configure CMM first
|
|
|
|
cmdBuf[0] = mgmRm3100::CMM_REGISTER;
|
|
|
|
cmdBuf[1] = mgmRm3100::CMM_VALUE;
|
|
|
|
result = spiComIF.sendMessage(mgm.cookie, cmdBuf.data(), 2);
|
|
|
|
if (result != OK) {
|
|
|
|
mgm.replyResult = result;
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
// Read back register
|
|
|
|
cmdBuf[0] = mgmRm3100::CMM_REGISTER | mgmRm3100::READ_MASK;
|
|
|
|
cmdBuf[1] = 0;
|
|
|
|
result = spiComIF.sendMessage(mgm.cookie, cmdBuf.data(), 2);
|
|
|
|
if (result != OK) {
|
|
|
|
mgm.replyResult = result;
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
result = spiComIF.readReceivedMessage(mgm.cookie, &rawReply, &dummy);
|
|
|
|
if (result != OK) {
|
|
|
|
mgm.replyResult = result;
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
if (rawReply[1] != mgmRm3100::CMM_VALUE) {
|
|
|
|
sif::error << "AcsBoardPolling: MGM RM3100 read back CMM invalid" << std::endl;
|
|
|
|
mgm.replyResult = result;
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
// Configure TMRC register
|
|
|
|
cmdBuf[0] = mgmRm3100::TMRC_REGISTER;
|
|
|
|
// hardcoded for now
|
|
|
|
cmdBuf[1] = mgm.tmrcValue;
|
|
|
|
result = spiComIF.sendMessage(mgm.cookie, cmdBuf.data(), 2);
|
|
|
|
if (result != OK) {
|
|
|
|
mgm.replyResult = result;
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
// Read back and verify value
|
|
|
|
cmdBuf[0] = mgmRm3100::TMRC_REGISTER | mgmRm3100::READ_MASK;
|
|
|
|
cmdBuf[1] = 0;
|
|
|
|
result = spiComIF.sendMessage(mgm.cookie, cmdBuf.data(), 2);
|
|
|
|
if (result != OK) {
|
|
|
|
mgm.replyResult = result;
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
result = spiComIF.readReceivedMessage(mgm.cookie, &rawReply, &dummy);
|
|
|
|
if (result != OK) {
|
|
|
|
mgm.replyResult = result;
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
if (rawReply[1] != mgm.tmrcValue) {
|
|
|
|
sif::error << "AcsBoardPolling: MGM RM3100 read back TMRC invalid" << std::endl;
|
|
|
|
mgm.replyResult = result;
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
mgm.performStartup = false;
|
|
|
|
}
|
|
|
|
// Regular read operation
|
|
|
|
cmdBuf[0] = mgmRm3100::MEASUREMENT_REG_START | mgmRm3100::READ_MASK;
|
|
|
|
std::memset(cmdBuf.data() + 1, 0, 9);
|
|
|
|
result = spiComIF.sendMessage(mgm.cookie, cmdBuf.data(), 10);
|
|
|
|
if (result != OK) {
|
|
|
|
mgm.replyResult = result;
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
result = spiComIF.readReceivedMessage(mgm.cookie, &rawReply, &dummy);
|
|
|
|
if (result != OK) {
|
|
|
|
mgm.replyResult = result;
|
|
|
|
return;
|
|
|
|
}
|
2023-03-02 15:32:12 +01:00
|
|
|
MutexGuard mg(ipcLock, LOCK_TYPE, LOCK_TIMEOUT, LOCK_CTX);
|
2023-02-26 21:26:49 +01:00
|
|
|
for (uint8_t idx = 0; idx < 3; idx++) {
|
|
|
|
// Hardcoded, but note that the gain depends on the cycle count
|
|
|
|
// value which is configurable!
|
|
|
|
mgm.ownReply.scaleFactors[idx] = 1.0 / mgmRm3100::DEFAULT_GAIN;
|
|
|
|
}
|
2023-02-28 01:25:25 +01:00
|
|
|
mgm.ownReply.dataWasRead = true;
|
|
|
|
// Bitshift trickery to account for 24 bit signed value.
|
2023-02-26 21:26:49 +01:00
|
|
|
mgm.ownReply.mgmValuesRaw[0] =
|
|
|
|
((rawReply[1] << 24) | (rawReply[2] << 16) | (rawReply[3] << 8)) >> 8;
|
|
|
|
mgm.ownReply.mgmValuesRaw[1] =
|
|
|
|
((rawReply[4] << 24) | (rawReply[5] << 16) | (rawReply[6] << 8)) >> 8;
|
|
|
|
mgm.ownReply.mgmValuesRaw[2] =
|
|
|
|
((rawReply[7] << 24) | (rawReply[8] << 16) | (rawReply[9] << 8)) >> 8;
|
|
|
|
}
|
|
|
|
}
|