#include "mission/devices/GyrAdis1650XHandler.h" #include "fsfw/action/HasActionsIF.h" #include "fsfw/datapool/PoolReadGuard.h" #include "mission/devices/devicedefinitions/acsPolling.h" GyrAdis1650XHandler::GyrAdis1650XHandler(object_id_t objectId, object_id_t deviceCommunication, CookieIF *comCookie, adis1650x::Type type) : DeviceHandlerBase(objectId, deviceCommunication, comCookie), adisType(type), primaryDataset(this), configDataset(this), breakCountdown() { adisRequest.type = adisType; } void GyrAdis1650XHandler::doStartUp() { if (internalState != InternalState::STARTUP) { internalState = InternalState::STARTUP; commandExecuted = false; } // Initial 310 ms start up time after power-up if (internalState == InternalState::STARTUP) { if (not commandExecuted) { warningSwitch = true; breakCountdown.setTimeout(adis1650x::START_UP_TIME); commandExecuted = true; } if (breakCountdown.hasTimedOut()) { updatePeriodicReply(true, adis1650x::REPLY); if (goToNormalMode) { setMode(MODE_NORMAL); } else { setMode(MODE_ON); } internalState = InternalState::NONE; } } } void GyrAdis1650XHandler::doShutDown() { if (internalState != InternalState::SHUTDOWN) { commandExecuted = false; primaryDataset.setValidity(false, true); internalState = InternalState::SHUTDOWN; } if (internalState == InternalState::SHUTDOWN and commandExecuted) { updatePeriodicReply(false, adis1650x::REPLY); internalState = InternalState::NONE; commandExecuted = false; setMode(_MODE_POWER_DOWN); } } ReturnValue_t GyrAdis1650XHandler::buildNormalDeviceCommand(DeviceCommandId_t *id) { *id = adis1650x::REQUEST; return preparePeriodicRequest(acs::SimpleSensorMode::NORMAL); } ReturnValue_t GyrAdis1650XHandler::buildTransitionDeviceCommand(DeviceCommandId_t *id) { switch (internalState) { case (InternalState::STARTUP): { *id = adis1650x::REQUEST; return preparePeriodicRequest(acs::SimpleSensorMode::NORMAL); } case (InternalState::SHUTDOWN): { *id = adis1650x::REQUEST; acs::Adis1650XRequest *request = reinterpret_cast(cmdBuf.data()); request->mode = acs::SimpleSensorMode::OFF; request->type = adisType; return returnvalue::OK; } default: { // Might be a configuration error sif::debug << "GyroADIS16507Handler::buildTransitionDeviceCommand: " "Unknown internal state!" << std::endl; return returnvalue::OK; } } return returnvalue::OK; } ReturnValue_t GyrAdis1650XHandler::buildCommandFromCommand(DeviceCommandId_t deviceCommand, const uint8_t *commandData, size_t commandDataLen) { // switch (deviceCommand) { // case (adis1650x::READ_OUT_CONFIG): { // this->rawPacketLen = adis1650x::CONFIG_READOUT_SIZE; // uint8_t regList[6] = {}; // 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; // adis1650x::prepareReadCommand(regList, sizeof(regList), commandBuffer.data(), // commandBuffer.size()); // this->rawPacket = commandBuffer.data(); // break; // } // case (adis1650x::READ_SENSOR_DATA): { // if (breakCountdown.isBusy()) { // // A glob command is pending and sensor data can't be read currently // return NO_REPLY_EXPECTED; // } // std::memcpy(commandBuffer.data(), adis1650x::BURST_READ_ENABLE.data(), // adis1650x::BURST_READ_ENABLE.size()); // std::memset(commandBuffer.data() + 2, 0, 10 * 2); // this->rawPacketLen = adis1650x::SENSOR_READOUT_SIZE; // this->rawPacket = commandBuffer.data(); // break; // } // TODO: Convert to special request // case (adis1650x::SELF_TEST_SENSORS): { // if (breakCountdown.isBusy()) { // // Another glob command is pending // return HasActionsIF::IS_BUSY; // } // prepareWriteCommand(adis1650x::GLOB_CMD, adis1650x::GlobCmds::SENSOR_SELF_TEST, 0x00); // breakCountdown.setTimeout(adis1650x::SELF_TEST_BREAK); // break; // } // case (adis1650x::SELF_TEST_MEMORY): { // if (breakCountdown.isBusy()) { // // Another glob command is pending // return HasActionsIF::IS_BUSY; // } // prepareWriteCommand(adis1650x::GLOB_CMD, adis1650x::GlobCmds::FLASH_MEMORY_TEST, 0x00); // breakCountdown.setTimeout(adis1650x::FLASH_MEMORY_TEST_BREAK); // break; // } // case (adis1650x::UPDATE_NV_CONFIGURATION): { // if (breakCountdown.isBusy()) { // // Another glob command is pending // return HasActionsIF::IS_BUSY; // } // prepareWriteCommand(adis1650x::GLOB_CMD, adis1650x::GlobCmds::FLASH_MEMORY_UPDATE, // 0x00); breakCountdown.setTimeout(adis1650x::FLASH_MEMORY_UPDATE_BREAK); break; // } // case (adis1650x::RESET_SENSOR_CONFIGURATION): { // if (breakCountdown.isBusy()) { // // Another glob command is pending // return HasActionsIF::IS_BUSY; // } // prepareWriteCommand(adis1650x::GLOB_CMD, adis1650x::GlobCmds::FACTORY_CALIBRATION, // 0x00); breakCountdown.setTimeout(adis1650x::FACTORY_CALIBRATION_BREAK); break; // } // case (adis1650x::SW_RESET): { // if (breakCountdown.isBusy()) { // // Another glob command is pending // return HasActionsIF::IS_BUSY; // } // prepareWriteCommand(adis1650x::GLOB_CMD, adis1650x::GlobCmds::SOFTWARE_RESET, 0x00); // breakCountdown.setTimeout(adis1650x::SW_RESET_BREAK); // break; // } // case (adis1650x::PRINT_CURRENT_CONFIGURATION): { //#if OBSW_VERBOSE_LEVEL >= 1 // PoolReadGuard pg(&configDataset); // sif::info << "ADIS16507 Sensor configuration: DIAG_STAT: 0x" << std::hex << std::setw(4) // << std::setfill('0') << "0x" << configDataset.diagStatReg.value << std::endl; // sif::info << "MSC_CTRL: " << std::hex << std::setw(4) << "0x" // << configDataset.mscCtrlReg.value << " | FILT_CTRL: 0x" // << configDataset.filterSetting.value << " | DEC_RATE: 0x" // << configDataset.decRateReg.value << std::setfill(' ') << std::endl; //#endif /* OBSW_VERBOSE_LEVEL >= 1 */ // //} //} // return returnvalue::OK; return NOTHING_TO_SEND; } void GyrAdis1650XHandler::fillCommandAndReplyMap() { insertInCommandMap(adis1650x::REQUEST); insertInReplyMap(adis1650x::REPLY, 5, nullptr, 0, true); // insertInCommandAndReplyMap(adis1650x::READ_SENSOR_DATA, 1, &primaryDataset); // insertInCommandAndReplyMap(adis1650x::READ_OUT_CONFIG, 1, &configDataset); // insertInCommandAndReplyMap(adis1650x::SELF_TEST_SENSORS, 1); // insertInCommandAndReplyMap(adis1650x::SELF_TEST_MEMORY, 1); // insertInCommandAndReplyMap(adis1650x::UPDATE_NV_CONFIGURATION, 1); // insertInCommandAndReplyMap(adis1650x::RESET_SENSOR_CONFIGURATION, 1); // insertInCommandAndReplyMap(adis1650x::SW_RESET, 1); // insertInCommandAndReplyMap(adis1650x::PRINT_CURRENT_CONFIGURATION, 1); } ReturnValue_t GyrAdis1650XHandler::scanForReply(const uint8_t *start, size_t remainingSize, DeviceCommandId_t *foundId, size_t *foundLen) { if (breakCountdown.isBusy() or getMode() == _MODE_WAIT_OFF or getMode() == _MODE_WAIT_ON) { return IGNORE_FULL_PACKET; } if (remainingSize != sizeof(acs::Adis1650XReply)) { *foundLen = remainingSize; return returnvalue::FAILED; } *foundId = adis1650x::REPLY; *foundLen = remainingSize; return returnvalue::OK; } ReturnValue_t GyrAdis1650XHandler::interpretDeviceReply(DeviceCommandId_t id, const uint8_t *packet) { using namespace adis1650x; const acs::Adis1650XReply *reply = reinterpret_cast(packet); if (internalState == InternalState::STARTUP and reply->cfgWasSet) { switch (adisType) { case (adis1650x::Type::ADIS16507): { if (reply->cfg.prodId != adis1650x::PROD_ID_16507) { sif::error << "GyroADIS1650XHandler::interpretDeviceReply: Invalid product ID " << reply->cfg.prodId << "for ADIS type 16507" << std::endl; return returnvalue::FAILED; } break; } case (adis1650x::Type::ADIS16505): { if (reply->cfg.prodId != adis1650x::PROD_ID_16505) { sif::error << "GyroADIS1650XHandler::interpretDeviceReply: Invalid product ID " << reply->cfg.prodId << "for ADIS type 16505" << std::endl; return returnvalue::FAILED; } break; } } PoolReadGuard rg(&configDataset); configDataset.setValidity(true, true); configDataset.mscCtrlReg = reply->cfg.mscCtrlReg; configDataset.rangMdl = reply->cfg.rangMdl; configDataset.diagStatReg = reply->cfg.diagStat; configDataset.filterSetting = reply->cfg.filterSetting; configDataset.decRateReg = reply->cfg.decRateReg; commandExecuted = true; } if (reply->dataWasSet) { { PoolReadGuard rg(&configDataset); configDataset.diagStatReg = reply->cfg.diagStat; } auto sensitivity = reply->data.sensitivity; auto accelSensitivity = reply->data.accelScaling; PoolReadGuard pg(&primaryDataset); primaryDataset.setValidity(true, true); primaryDataset.angVelocX = static_cast(reply->data.angVelocities[0]) * sensitivity; primaryDataset.angVelocY = static_cast(reply->data.angVelocities[1]) * sensitivity; primaryDataset.angVelocZ = static_cast(reply->data.angVelocities[2]) * sensitivity; // TODO: Check whether we need to divide by INT16_MAX primaryDataset.accelX = static_cast(reply->data.accelerations[0]) * accelSensitivity; primaryDataset.accelY = static_cast(reply->data.accelerations[1]) * accelSensitivity; primaryDataset.accelZ = static_cast(reply->data.accelerations[2]) * accelSensitivity; primaryDataset.temperature.value = static_cast(reply->data.temperatureRaw) * 0.1; } return returnvalue::OK; } LocalPoolDataSetBase *GyrAdis1650XHandler::getDataSetHandle(sid_t sid) { if (sid.ownerSetId == adis1650x::ADIS_DATASET_ID) { return &primaryDataset; } else if (sid.ownerSetId == adis1650x::ADIS_CFG_DATASET_ID) { return &configDataset; } return nullptr; } // ReturnValue_t GyroADIS1650XHandler::handleSensorData(const uint8_t *packet) { // using namespace adis1650x; // BurstModes burstMode = getBurstMode(); // switch (burstMode) { // case (BurstModes::BURST_16_BURST_SEL_1): // case (BurstModes::BURST_32_BURST_SEL_1): { // sif::warning << "GyroADIS1650XHandler::interpretDeviceReply: Analysis with BURST_SEL1" // " not implemented!" // << std::endl; // return returnvalue::OK; // } // case (adis1650x::BurstModes::BURST_16_BURST_SEL_0): { // uint16_t checksum = packet[20] << 8 | packet[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 += packet[idx]; // } // if (checksum != calcChecksum) { //#if OBSW_VERBOSE_LEVEL >= 1 // sif::warning << "GyroADIS1650XHandler::interpretDeviceReply: " // "Invalid checksum detected!" // << std::endl; //#endif // return returnvalue::FAILED; // } // // ReturnValue_t result = configDataset.diagStatReg.read(); // if (result == returnvalue::OK) { // configDataset.diagStatReg.value = packet[2] << 8 | packet[3]; // configDataset.diagStatReg.setValid(true); // } // configDataset.diagStatReg.commit(); // // { // PoolReadGuard pg(&primaryDataset); // int16_t angVelocXRaw = packet[4] << 8 | packet[5]; // primaryDataset.angVelocX.value = static_cast(angVelocXRaw) * sensitivity; // int16_t angVelocYRaw = packet[6] << 8 | packet[7]; // primaryDataset.angVelocY.value = static_cast(angVelocYRaw) * sensitivity; // int16_t angVelocZRaw = packet[8] << 8 | packet[9]; // primaryDataset.angVelocZ.value = static_cast(angVelocZRaw) * sensitivity; // // float accelScaling = 0; // if (adisType == adis1650x::Type::ADIS16507) { // accelScaling = adis1650x::ACCELEROMETER_RANGE_16507; // } else if (adisType == adis1650x::Type::ADIS16505) { // accelScaling = adis1650x::ACCELEROMETER_RANGE_16505; // } else { // sif::warning << "GyroADIS1650XHandler::handleSensorData: " // "Unknown ADIS type" // << std::endl; // } // int16_t accelXRaw = packet[10] << 8 | packet[11]; // primaryDataset.accelX.value = static_cast(accelXRaw) / INT16_MAX * accelScaling; // int16_t accelYRaw = packet[12] << 8 | packet[13]; // primaryDataset.accelY.value = static_cast(accelYRaw) / INT16_MAX * accelScaling; // int16_t accelZRaw = packet[14] << 8 | packet[15]; // primaryDataset.accelZ.value = static_cast(accelZRaw) / INT16_MAX * accelScaling; // // int16_t temperatureRaw = packet[16] << 8 | packet[17]; // primaryDataset.temperature.value = static_cast(temperatureRaw) * 0.1; // // Ignore data counter for now // primaryDataset.setValidity(true, true); // } // // if (periodicPrintout) { // if (debugDivider.checkAndIncrement()) { // sif::info << "GyroADIS1650XHandler: Angular velocities in deg / s" << std::endl; // sif::info << "X: " << primaryDataset.angVelocX.value << std::endl; // sif::info << "Y: " << primaryDataset.angVelocY.value << std::endl; // sif::info << "Z: " << primaryDataset.angVelocZ.value << std::endl; // sif::info << "GyroADIS1650XHandler: Accelerations in m / s^2: " << std::endl; // sif::info << "X: " << primaryDataset.accelX.value << std::endl; // sif::info << "Y: " << primaryDataset.accelY.value << std::endl; // sif::info << "Z: " << primaryDataset.accelZ.value << std::endl; // } // } // // break; // } // case (BurstModes::BURST_32_BURST_SEL_0): { // break; // } // } // return returnvalue::OK; // } uint32_t GyrAdis1650XHandler::getTransitionDelayMs(Mode_t modeFrom, Mode_t modeTo) { return 6000; } void GyrAdis1650XHandler::prepareWriteCommand(uint8_t startReg, uint8_t valueOne, uint8_t valueTwo) { uint8_t secondReg = startReg + 1; startReg |= adis1650x::WRITE_MASK; secondReg |= adis1650x::WRITE_MASK; cmdBuf[0] = startReg; cmdBuf[1] = valueOne; cmdBuf[2] = secondReg; cmdBuf[3] = valueTwo; this->rawPacketLen = 4; this->rawPacket = cmdBuf.data(); } ReturnValue_t GyrAdis1650XHandler::initializeLocalDataPool(localpool::DataPool &localDataPoolMap, LocalDataPoolManager &poolManager) { localDataPoolMap.emplace(adis1650x::ANG_VELOC_X, new PoolEntry({0.0})); localDataPoolMap.emplace(adis1650x::ANG_VELOC_Y, new PoolEntry({0.0})); localDataPoolMap.emplace(adis1650x::ANG_VELOC_Z, new PoolEntry({0.0})); localDataPoolMap.emplace(adis1650x::ACCELERATION_X, new PoolEntry({0.0})); localDataPoolMap.emplace(adis1650x::ACCELERATION_Y, new PoolEntry({0.0})); localDataPoolMap.emplace(adis1650x::ACCELERATION_Z, new PoolEntry({0.0})); localDataPoolMap.emplace(adis1650x::TEMPERATURE, new PoolEntry({0.0})); localDataPoolMap.emplace(adis1650x::DIAG_STAT_REGISTER, new PoolEntry()); localDataPoolMap.emplace(adis1650x::FILTER_SETTINGS, new PoolEntry()); localDataPoolMap.emplace(adis1650x::RANG_MDL, &rangMdl); localDataPoolMap.emplace(adis1650x::MSC_CTRL_REGISTER, new PoolEntry()); localDataPoolMap.emplace(adis1650x::DEC_RATE_REGISTER, new PoolEntry()); poolManager.subscribeForRegularPeriodicPacket( subdp::RegularHkPeriodicParams(primaryDataset.getSid(), false, 5.0)); return returnvalue::OK; } adis1650x::BurstModes GyrAdis1650XHandler::getBurstMode() { using namespace adis1650x; configDataset.mscCtrlReg.read(); uint16_t currentCtrlReg = configDataset.mscCtrlReg.value; configDataset.mscCtrlReg.commit(); return adis1650x::burstModeFromMscCtrl(currentCtrlReg); } //#ifdef FSFW_OSAL_LINUX // ReturnValue_t GyroADIS1650XHandler::spiSendCallback(SpiComIF *comIf, SpiCookie *cookie, // const uint8_t *sendData, size_t sendLen, // void *args) { // GyroADIS1650XHandler *handler = reinterpret_cast(args); // if (handler == nullptr) { // sif::error << "GyroADIS16507Handler::spiSendCallback: Passed handler pointer is invalid!" // << std::endl; // return returnvalue::FAILED; // } // DeviceCommandId_t currentCommand = handler->getPendingCommand(); // switch (currentCommand) { // case (adis1650x::READ_SENSOR_DATA): { // return comIf->performRegularSendOperation(cookie, sendData, sendLen); // } // case (adis1650x::READ_OUT_CONFIG): // default: { // ReturnValue_t result = returnvalue::OK; // int retval = 0; // // Prepare transfer // int fileDescriptor = 0; // std::string device = comIf->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); // comIf->setSpiSpeedAndMode(fileDescriptor, spiMode, spiSpeed); // cookie->assignWriteBuffer(sendData); // cookie->setTransferSize(2); // // gpioId_t gpioId = cookie->getChipSelectPin(); // GpioIF &gpioIF = comIf->getGpioInterface(); // MutexIF::TimeoutType timeoutType = MutexIF::TimeoutType::WAITING; // uint32_t timeoutMs = 0; // MutexIF *mutex = comIf->getCsMutex(); // cookie->getMutexParams(timeoutType, timeoutMs); // if (mutex == nullptr) { //#if OBSW_VERBOSE_LEVEL >= 1 // sif::warning << "GyroADIS16507Handler::spiSendCallback: " // "Mutex or GPIO interface invalid" // << std::endl; // return returnvalue::FAILED; //#endif // } // // if (gpioId != gpio::NO_GPIO) { // result = mutex->lockMutex(timeoutType, timeoutMs); // if (result != returnvalue::OK) { //#if FSFW_CPP_OSTREAM_ENABLED == 1 // sif::error << "SpiComIF::sendMessage: Failed to lock mutex" << std::endl; //#endif // return result; // } // } // // size_t idx = 0; // spi_ioc_transfer *transferStruct = cookie->getTransferStructHandle(); // uint64_t origTx = transferStruct->tx_buf; // uint64_t origRx = transferStruct->rx_buf; // while (idx < sendLen) { // // 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); // } // // idx += 2; // if (idx < sendLen) { // usleep(adis1650x::STALL_TIME_MICROSECONDS); // } // // transferStruct->tx_buf += 2; // transferStruct->rx_buf += 2; // } // transferStruct->tx_buf = origTx; // transferStruct->rx_buf = origRx; // if (gpioId != gpio::NO_GPIO) { // mutex->unlockMutex(); // } // } // } // return returnvalue::OK; // } void GyrAdis1650XHandler::setToGoToNormalModeImmediately() { goToNormalMode = true; } void GyrAdis1650XHandler::enablePeriodicPrintouts(bool enable, uint8_t divider) { periodicPrintout = enable; debugDivider.setDivider(divider); } ReturnValue_t GyrAdis1650XHandler::preparePeriodicRequest(acs::SimpleSensorMode mode) { adisRequest.mode = mode; rawPacket = reinterpret_cast(&adisRequest); rawPacketLen = sizeof(acs::Adis1650XRequest); return returnvalue::OK; }