Crazyflie.cpp
Go to the documentation of this file.
00001 //#include <regex>
00002 #include <mutex>
00003 
00004 #include "Crazyflie.h"
00005 #include "crtp.h"
00006 #include "crtpBootloader.h"
00007 #include "crtpNRF51.h"
00008 
00009 #include "Crazyradio.h"
00010 #include "CrazyflieUSB.h"
00011 
00012 #include <fstream>
00013 #include <cstring>
00014 #include <stdexcept>
00015 #include <thread>
00016 #include <cmath>
00017 #include <inttypes.h>
00018 
00019 const static int MAX_RADIOS = 16;
00020 const static int MAX_USB = 4;
00021 const static bool LOG_COMMUNICATION = 0;
00022 
00023 Crazyradio* g_crazyradios[MAX_RADIOS];
00024 std::mutex g_radioMutex[MAX_RADIOS];
00025 
00026 CrazyflieUSB* g_crazyflieUSB[MAX_USB];
00027 std::mutex g_crazyflieusbMutex[MAX_USB];
00028 
00029 Logger EmptyLogger;
00030 
00031 
00032 Crazyflie::Crazyflie(
00033   const std::string& link_uri,
00034   Logger& logger,
00035   std::function<void(const char*)> consoleCb)
00036   : m_radio(nullptr)
00037   , m_transport(nullptr)
00038   , m_devId(0)
00039   , m_channel(0)
00040   , m_address(0)
00041   , m_datarate(Crazyradio::Datarate_250KPS)
00042   , m_logTocEntries()
00043   , m_logBlockCb()
00044   , m_paramTocEntries()
00045   , m_paramValues()
00046   , m_emptyAckCallback(nullptr)
00047   , m_linkQualityCallback(nullptr)
00048   , m_consoleCallback(consoleCb)
00049   , m_log_use_V2(false)
00050   , m_param_use_V2(false)
00051   , m_logger(logger)
00052 {
00053   int datarate;
00054   int channel;
00055   char datarateType;
00056   bool success = false;
00057 
00058   success = std::sscanf(link_uri.c_str(), "radio://%d/%d/%d%c/%" SCNx64,
00059      &m_devId, &channel, &datarate,
00060      &datarateType, &m_address) == 5;
00061   if (!success) {
00062     success = std::sscanf(link_uri.c_str(), "radio://%d/%d/%d%c",
00063        &m_devId, &channel, &datarate,
00064        &datarateType) == 4;
00065     m_address = 0xE7E7E7E7E7;
00066   }
00067 
00068   if (success)
00069   {
00070     m_channel = channel;
00071     if (datarate == 250 && datarateType == 'K') {
00072       m_datarate = Crazyradio::Datarate_250KPS;
00073     }
00074     else if (datarate == 1 && datarateType == 'M') {
00075       m_datarate = Crazyradio::Datarate_1MPS;
00076     }
00077     else if (datarate == 2 && datarateType == 'M') {
00078       m_datarate = Crazyradio::Datarate_2MPS;
00079     }
00080 
00081     if (m_devId >= MAX_RADIOS) {
00082       throw std::runtime_error("This version does not support that many radios. Adjust MAX_RADIOS and recompile!");
00083     }
00084 
00085     {
00086       std::unique_lock<std::mutex> mlock(g_radioMutex[m_devId]);
00087       if (!g_crazyradios[m_devId]) {
00088         g_crazyradios[m_devId] = new Crazyradio(m_devId);
00089         g_crazyradios[m_devId]->enableLogging(LOG_COMMUNICATION);
00090         // g_crazyradios[m_devId]->setAckEnable(false);
00091         g_crazyradios[m_devId]->setAckEnable(true);
00092         g_crazyradios[m_devId]->setArc(0);
00093       }
00094     }
00095 
00096     m_radio = g_crazyradios[m_devId];
00097   }
00098   else {
00099     success = std::sscanf(link_uri.c_str(), "usb://%d",
00100        &m_devId) == 1;
00101 
00102     if (m_devId >= MAX_USB) {
00103       throw std::runtime_error("This version does not support that many CFs over USB. Adjust MAX_USB and recompile!");
00104     }
00105 
00106     {
00107       std::unique_lock<std::mutex> mlock(g_crazyflieusbMutex[m_devId]);
00108       if (!g_crazyflieUSB[m_devId]) {
00109         g_crazyflieUSB[m_devId] = new CrazyflieUSB(m_devId);
00110         g_crazyflieUSB[m_devId]->enableLogging(LOG_COMMUNICATION);
00111       }
00112     }
00113 
00114     m_transport = g_crazyflieUSB[m_devId];
00115   }
00116 
00117   if (!success) {
00118     throw std::runtime_error("Uri is not valid!");
00119   }
00120 
00121   // enable safelink
00122   if (m_radio) {
00123     crtpNrf51SetSafelinkRequest request(ENABLE_SAFELINK);
00124     sendPacketOrTimeout(request, /*useSafeLink*/false);
00125   }
00126 
00127   m_curr_up = 0;
00128   m_curr_down = 0;
00129 
00130   m_protocolVersion = -1;
00131 
00132 }
00133 
00134 int Crazyflie::getProtocolVersion()
00135 {
00136   crtpGetProtocolVersionRequest req;
00137   startBatchRequest();
00138   addRequest(req, 1);
00139   handleRequests();
00140   return getRequestResult<crtpGetProtocolVersionResponse>(0)->version;
00141 }
00142 
00143 std::string Crazyflie::getFirmwareVersion()
00144 {
00145   crtpGetFirmwareVersionRequest req;
00146   startBatchRequest();
00147   addRequest(req, 1);
00148   handleRequests();
00149   return std::string(getRequestResult<crtpGetFirmwareVersionResponse>(0)->version);
00150 }
00151 
00152 std::string Crazyflie::getDeviceTypeName()
00153 {
00154   crtpGetDeviceTypeNameRequest req;
00155   startBatchRequest();
00156   addRequest(req, 1);
00157   handleRequests();
00158   return std::string(getRequestResult<crtpGetDeviceTypeNameResponse>(0)->name);
00159 }
00160 
00161 void Crazyflie::logReset()
00162 {
00163   crtpLogResetRequest request;
00164   startBatchRequest();
00165   addRequest(request, 1);
00166   handleRequests();
00167 }
00168 
00169 void Crazyflie::sendSetpoint(
00170   float roll,
00171   float pitch,
00172   float yawrate,
00173   uint16_t thrust)
00174 {
00175   crtpSetpointRequest request(roll, pitch, yawrate, thrust);
00176   sendPacket(request);
00177 }
00178 
00179 void Crazyflie::sendStop()
00180 {
00181   crtpStopRequest request;
00182   sendPacket(request);
00183 }
00184 
00185 void Crazyflie::emergencyStop()
00186 {
00187   crtpEmergencyStopRequest request;
00188   sendPacketOrTimeout(request);
00189 }
00190 
00191 void Crazyflie::emergencyStopWatchdog()
00192 {
00193   crtpEmergencyStopWatchdogRequest request;
00194   sendPacketOrTimeout(request);
00195 }
00196 
00197 void Crazyflie::sendPositionSetpoint(
00198   float x,
00199   float y,
00200   float z,
00201   float yaw)
00202 {
00203   crtpPositionSetpointRequest request(x, y, z, yaw);
00204   sendPacket(request);
00205 }
00206 
00207 void Crazyflie::sendHoverSetpoint(
00208   float vx,
00209   float vy,
00210   float yawrate,
00211   float zDistance)
00212 {
00213   crtpHoverSetpointRequest request(vx, vy, yawrate, zDistance);
00214   sendPacket(request);
00215 }
00216 
00217 void Crazyflie::sendFullStateSetpoint(
00218     float x, float y, float z,
00219     float vx, float vy, float vz,
00220     float ax, float ay, float az,
00221     float qx, float qy, float qz, float qw,
00222     float rollRate, float pitchRate, float yawRate)
00223 {
00224   crtpFullStateSetpointRequest request(
00225     x, y, z,
00226     vx, vy, vz,
00227     ax, ay, az,
00228     qx, qy, qz, qw,
00229     rollRate, pitchRate, yawRate);
00230   sendPacket(request);
00231 }
00232 
00233 void Crazyflie::sendExternalPositionUpdate(
00234   float x,
00235   float y,
00236   float z)
00237 {
00238   crtpExternalPositionUpdate position(x, y, z);
00239   sendPacket(position);
00240 }
00241 
00242 void Crazyflie::sendExternalPoseUpdate(
00243   float x, float y, float z,
00244   float qx, float qy, float qz, float qw)
00245 {
00246   crtpExternalPoseUpdate pose(x, y, z, qx, qy, qz, qw);
00247   sendPacket(pose);
00248 }
00249 
00250 void Crazyflie::sendPing()
00251 {
00252   crtpEmpty req;
00253   sendPacket(req);
00254 }
00255 
00259 void Crazyflie::transmitPackets()
00260 {
00261   if (!m_outgoing_packets.empty())
00262   {
00263     std::vector<crtpPacket_t>::iterator it;
00264     for (it = m_outgoing_packets.begin(); it != m_outgoing_packets.end(); it++)
00265     {
00266       sendPacketInternal(it->raw, it->size+1);
00267     }
00268     m_outgoing_packets.clear();
00269   }
00270 }
00271 
00272 // https://forum.bitcraze.io/viewtopic.php?f=9&t=1488
00273 void Crazyflie::reboot()
00274 {
00275   if (m_radio) {
00276     crtpNrf51ResetInitRequest req1;
00277     sendPacketOrTimeout(req1);
00278 
00279     crtpNrf51ResetRequest req2(/*bootToFirmware*/ 1);
00280     sendPacketOrTimeout(req2);
00281   }
00282 }
00283 
00284 uint64_t Crazyflie::rebootToBootloader()
00285 {
00286   if (m_radio) {
00287     crtpNrf51ResetInitRequest req;
00288     startBatchRequest();
00289     addRequest(req, 2);
00290     handleRequests();
00291     const crtpNrf51ResetInitResponse* response = getRequestResult<crtpNrf51ResetInitResponse>(0);
00292 
00293     uint64_t result =
00294         ((uint64_t)response->addr[0] << 0)
00295       | ((uint64_t)response->addr[1] << 8)
00296       | ((uint64_t)response->addr[2] << 16)
00297       | ((uint64_t)response->addr[3] << 24)
00298       | ((uint64_t)0xb1 << 32);
00299 
00300     crtpNrf51ResetRequest req2(/*bootToFirmware*/ 0);
00301     sendPacketOrTimeout(req2);
00302 
00303     // switch to new address
00304     m_address = result;
00305     m_channel = 0;
00306     m_datarate = Crazyradio::Datarate_2MPS;
00307 
00308 
00309     return result;
00310   } else {
00311     return -1;
00312   }
00313 }
00314 
00315 void Crazyflie::rebootFromBootloader()
00316 {
00317   if (m_radio) {
00318     bootloaderResetRequest req(/*bootToFirmware*/ 1);
00319     sendPacketOrTimeout(req, /*useSafeLink*/ false);
00320   }
00321 }
00322 
00323 void Crazyflie::sysoff()
00324 {
00325   if (m_radio) {
00326     crtpNrf51SysOffRequest req;
00327     sendPacketOrTimeout(req);
00328   }
00329 }
00330 
00331 void Crazyflie::alloff()
00332 {
00333   if (m_radio) {
00334     crtpNrf51AllOffRequest req;
00335     sendPacketOrTimeout(req);
00336   }
00337 }
00338 
00339 void Crazyflie::syson()
00340 {
00341   if (m_radio) {
00342     crtpNrf51SysOnRequest req;
00343     sendPacketOrTimeout(req);
00344   }
00345 }
00346 
00347 float Crazyflie::vbat()
00348 {
00349   if (m_radio) {
00350     crtpNrf51GetVBatRequest req;
00351     startBatchRequest();
00352     addRequest(req, 2);
00353     handleRequests();
00354     return getRequestResult<crtpNrf51GetVBatResponse>(0)->vbat;
00355   } else {
00356     return nan("");
00357   }
00358 }
00359 
00360 void Crazyflie::writeFlash(
00361   BootloaderTarget target,
00362   const std::vector<uint8_t>& data)
00363 {
00364   // Get info about the target
00365   bootloaderGetInfoRequest req(target);
00366   startBatchRequest();
00367   addRequest(req, 3);
00368   handleRequests(/*crtpMode=*/false, /*useSafeLink*/ false);
00369   const bootloaderGetInfoResponse* response = getRequestResult<bootloaderGetInfoResponse>(0);
00370   uint16_t pageSize = response->pageSize;
00371   uint16_t flashStart = response->flashStart;
00372   uint16_t nBuffPage = response->nBuffPage;
00373 
00374   uint16_t numPages = ceil(data.size() / (float)pageSize);
00375   if (numPages + flashStart >= response->nFlashPage) {
00376     std::stringstream sstr;
00377     sstr << "Requested size too large!";
00378     throw std::runtime_error(sstr.str());
00379   }
00380 
00381   std::stringstream sstr;
00382   sstr << "pageSize: " << pageSize
00383             << " nBuffPage: " << nBuffPage
00384             << " nFlashPage: " << response->nFlashPage
00385             << " flashStart: " << flashStart
00386             << " version: " << (int)response->version
00387             << " numPages: " << numPages;
00388   m_logger.info(sstr.str());
00389 
00390   // write flash
00391   size_t offset = 0;
00392   uint16_t usedBuffers = 0;
00393   // startBatchRequest();
00394   for (uint16_t page = flashStart; page < numPages + flashStart; ++page) {
00395     std::stringstream sstr;
00396     sstr << "page: " << page - flashStart + 1 << " / " << numPages;
00397     m_logger.info(sstr.str());
00398     for (uint16_t address = 0; address < pageSize; address += 25) {
00399 
00400       // std::cout << "request: " << page << " " << address << std::endl;
00401       bootloaderLoadBufferRequest req(target, usedBuffers, address);
00402       size_t requestedSize = std::min<size_t>(data.size() - offset, std::min<size_t>(25, pageSize - address));
00403       memcpy(req.data, &data[offset], requestedSize);
00404       // addRequest(req, 0);
00405       // for (size_t i = 0; i < 10; ++i)
00406       // std::cout << "request: " << req.page << " " << req.address << " " << requestedSize << std::endl;
00407       // for (size_t i = 0; i < 10; ++i) {
00408 
00409       // auto start = std::chrono::system_clock::now();
00410       // while (true) {
00411         sendPacketOrTimeoutInternal((uint8_t*)&req, 7 + requestedSize, false);
00412       //   startBatchRequest();
00413       //   bootloaderReadBufferRequest req2(target, usedBuffers, address);
00414       //   addRequest(req2, 7);
00415       //   handleRequests(/*crtpMode=*/false);
00416       //   const bootloaderReadBufferResponse* response = getRequestResult<bootloaderReadBufferResponse>(0);
00417       //   if (memcmp(req.data, response->data, requestedSize) == 0) {
00418       //     break;
00419       //   }
00420       //   auto end = std::chrono::system_clock::now();
00421       //   std::chrono::duration<double> elapsedSeconds = end-start;
00422       //   if (elapsedSeconds.count() > 1.0) {
00423       //     throw std::runtime_error("timeout");
00424       //   }
00425       // }
00426       offset += requestedSize;
00427       if (offset >= data.size()) {
00428         break;
00429       }
00430     }
00431     ++usedBuffers;
00432     if (usedBuffers == nBuffPage
00433         || page == numPages + flashStart - 1) {
00434 
00435 
00436       // startBatchRequest();
00437       // for (uint16_t buf = 0; buf < usedBuffers; ++buf) {
00438       //   for (uint16_t address = 0; address < pageSize; address += 25) {
00439       //     // std::cout << "request: " << page << " " << address << std::endl;
00440       //     bootloaderReadBufferRequest req(target, buf, address);
00441       //     addRequest(req, 7);
00442       //   }
00443       // }
00444       // handleRequests(/*crtpMode=*/false);
00445 
00446 
00447       // upload all the buffers now
00448       // std::cout << "try to upload buffers!" << std::endl;
00449       // handleRequests(/*crtpMode=*/false);
00450       // std::cout << "buffers uploaded!" << std::endl;
00451 
00452       // std::this_thread::sleep_for(std::chrono::milliseconds(100));
00453 
00454       // write flash
00455       bootloaderWriteFlashRequest req(target, 0, page - usedBuffers + 1, usedBuffers);
00456       sendPacketOrTimeoutInternal((uint8_t*)&req, sizeof(req), false);
00457 
00458       auto start = std::chrono::system_clock::now();
00459 
00460       size_t tries = 0;
00461       while (true) {
00462         ITransport::Ack ack;
00463         bootloaderFlashStatusRequest statReq(target);
00464         sendPacket(statReq, ack, false);
00465         if (   ack.ack
00466             && ack.size == 5
00467             && memcmp(&req, ack.data, 3) == 0) {
00468           if (ack.data[3] != 1 || ack.data[4] != 0) {
00469             throw std::runtime_error("Error during flashing!");
00470           }
00471           break;
00472         }
00473 
00474         // std::cout << page - usedBuffers + 1 << "," << usedBuffers << std::endl;
00475         // startBatchRequest();
00476         // bootloaderWriteFlashRequest req(target, 0, page - usedBuffers + 1, usedBuffers);
00477         // addRequest(req, 3);
00478         // handleRequests(/*crtpMode=*/false);
00479         // const bootloaderWriteFlashResponse* response = getRequestResult<bootloaderWriteFlashResponse>(0);
00480         // if (response->done == 1 && response->error == 0) {
00481         //   break;
00482         // }
00483         auto end = std::chrono::system_clock::now();
00484         std::chrono::duration<double> elapsedSeconds = end-start;
00485         if (elapsedSeconds.count() > 0.5) {
00486           start = end;
00487           sendPacketOrTimeout(req, false);
00488           ++tries;
00489           if (tries > 5) {
00490             throw std::runtime_error("timeout");
00491           }
00492         }
00493       }
00494 
00495       // std::cout << "Flashed: " << (page - flashStart) / (float)numPages * 100.0 << " %" << std::endl;
00496 
00497       // get ready to fill more buffers
00498       // if (page != numPages + flashStart - 1) {
00499       //   startBatchRequest();
00500       // }
00501       usedBuffers = 0;
00502     }
00503   }
00504 
00505 
00506 }
00507 
00508 void Crazyflie::readFlash(
00509   BootloaderTarget target,
00510   size_t size,
00511   std::vector<uint8_t>& data)
00512 {
00513   // Get info about the target
00514   bootloaderGetInfoRequest req(target);
00515   startBatchRequest();
00516   addRequest(req, 3);
00517   handleRequests(/*crtpMode=*/false, /*useSafelink*/false);
00518   const bootloaderGetInfoResponse* response = getRequestResult<bootloaderGetInfoResponse>(0);
00519   uint16_t pageSize = response->pageSize;
00520   uint16_t flashStart = response->flashStart;
00521 
00522   uint16_t numPages = ceil(size / (float)pageSize);
00523   if (numPages + flashStart >= response->nFlashPage) {
00524     std::stringstream sstr;
00525     sstr << "Requested size too large!";
00526     throw std::runtime_error(sstr.str());
00527   }
00528 
00529   std::stringstream sstr;
00530   sstr << "pageSize: " << pageSize
00531        << " nFlashPage: " << response->nFlashPage
00532        << " flashStart: " << flashStart
00533        << " version: " << (int)response->version
00534        << " numPages: " << numPages;
00535   m_logger.info(sstr.str());
00536 
00537   // read flash
00538   size_t offset = 0;
00539   startBatchRequest();
00540   for (uint16_t page = flashStart; page < numPages + flashStart; ++page) {
00541     for (uint16_t address = 0; address < pageSize; address += 25) {
00542       // std::cout << "request: " << page << " " << address << std::endl;
00543       bootloaderReadFlashRequest req(target, page, address);
00544       addRequest(req, 7);
00545       size_t requestedSize = std::min(25, pageSize - address);
00546       offset += requestedSize;
00547       if (offset > size) {
00548         break;
00549       }
00550     }
00551   }
00552   handleRequests(/*crtpMode=*/false, /*useSafelink*/false);
00553 
00554   // update output
00555   data.resize(size);
00556   size_t i = 0;
00557   offset = 0;
00558   for (uint16_t page = flashStart; page < numPages + flashStart; ++page) {
00559     for (uint16_t address = 0; address < pageSize; address += 25) {
00560       const bootloaderReadFlashResponse* response = getRequestResult<bootloaderReadFlashResponse>(i++);
00561       size_t requestedSize = std::min(25, pageSize - address);
00562       // std::cout << "offset: " << offset << " reqS: " << requestedSize;
00563       memcpy(&data[offset], response->data, std::min(size - offset, requestedSize));
00564       offset += requestedSize;
00565       if (offset > size) {
00566         break;
00567       }
00568     }
00569   }
00570 }
00571 
00572 void Crazyflie::requestLogToc(bool forceNoCache)
00573 {
00574   m_log_use_V2 = true;
00575   uint16_t len;
00576   uint32_t crc;
00577 
00578   // Lazily initialize protocol version
00579   if (m_protocolVersion < 0) {
00580     m_protocolVersion = getProtocolVersion();
00581   }
00582 
00583   crtpLogGetInfoV2Request infoRequest;
00584   startBatchRequest();
00585   addRequest(infoRequest, 1);
00586   if (m_protocolVersion >= 4) {
00587     handleRequests();
00588     len = getRequestResult<crtpLogGetInfoV2Response>(0)->log_len;
00589     crc = getRequestResult<crtpLogGetInfoV2Response>(0)->log_crc;
00590   } else {
00591     // std::cout << "Fall back to V1 param API" << std::endl;
00592     m_log_use_V2 = false;
00593 
00594     crtpLogGetInfoRequest infoRequest;
00595     startBatchRequest();
00596     addRequest(infoRequest, 1);
00597     handleRequests();
00598     len = getRequestResult<crtpLogGetInfoResponse>(0)->log_len;
00599     crc = getRequestResult<crtpLogGetInfoResponse>(0)->log_crc;
00600     // std::cout << len << std::endl;
00601   }
00602 
00603   // check if it is in the cache
00604   std::string fileName = "log" + std::to_string(crc) + ".csv";
00605   std::ifstream infile(fileName);
00606 
00607   if (forceNoCache || !infile.good()) {
00608     m_logger.info("Log: " + std::to_string(len));
00609 
00610     // Request detailed information
00611     startBatchRequest();
00612     if (m_log_use_V2) {
00613       for (size_t i = 0; i < len; ++i) {
00614         crtpLogGetItemV2Request itemRequest(i);
00615         addRequest(itemRequest, 2);
00616       }
00617     } else {
00618       for (size_t i = 0; i < len; ++i) {
00619         crtpLogGetItemRequest itemRequest(i);
00620         addRequest(itemRequest, 2);
00621       }
00622     }
00623     handleRequests();
00624 
00625     // Update internal structure with obtained data
00626     m_logTocEntries.resize(len);
00627     if (m_log_use_V2) {
00628       for (size_t i = 0; i < len; ++i) {
00629         auto response = getRequestResult<crtpLogGetItemV2Response>(i);
00630         LogTocEntry& entry = m_logTocEntries[i];
00631         entry.id = i;
00632         entry.type = (LogType)response->type;
00633         entry.group = std::string(&response->text[0]);
00634         entry.name = std::string(&response->text[entry.group.size() + 1]);
00635       }
00636     } else {
00637       for (size_t i = 0; i < len; ++i) {
00638         auto response = getRequestResult<crtpLogGetItemResponse>(i);
00639         LogTocEntry& entry = m_logTocEntries[i];
00640         entry.id = i;
00641         entry.type = (LogType)response->type;
00642         entry.group = std::string(&response->text[0]);
00643         entry.name = std::string(&response->text[entry.group.size() + 1]);
00644       }
00645     }
00646 
00647     // Write a cache file
00648     {
00649       // Atomic file write: write in temporary file first to avoid race conditions
00650       std::string fileNameTemp = fileName + ".tmp";
00651       std::ofstream output(fileNameTemp);
00652       output << "id,type,group,name" << std::endl;
00653       for (const auto& entry : m_logTocEntries) {
00654         output << std::to_string(entry.id) << ","
00655                << std::to_string(entry.type) << ","
00656                << entry.group << ","
00657                << entry.name << std::endl;
00658       }
00659       // change the filename
00660       rename(fileNameTemp.c_str(), fileName.c_str());
00661     }
00662   } else {
00663     m_logger.info("Found variables in cache.");
00664     m_logTocEntries.clear();
00665     std::string line, cell;
00666     std::getline(infile, line); // ignore header
00667     while (std::getline(infile, line)) {
00668       std::stringstream lineStream(line);
00669       m_logTocEntries.resize(m_logTocEntries.size() + 1);
00670       std::getline(lineStream, cell, ',');
00671       m_logTocEntries.back().id = std::stoi(cell);
00672       std::getline(lineStream, cell, ',');
00673       m_logTocEntries.back().type = (LogType)std::stoi(cell);
00674       std::getline(lineStream, cell, ',');
00675       m_logTocEntries.back().group = cell;
00676       std::getline(lineStream, cell, ',');
00677       m_logTocEntries.back().name = cell;
00678     }
00679   }
00680 }
00681 
00682 void Crazyflie::requestParamToc(bool forceNoCache)
00683 {
00684   m_param_use_V2 = true;
00685   uint16_t numParam;
00686   uint32_t crc;
00687 
00688   // Lazily initialize protocol version
00689   if (m_protocolVersion < 0) {
00690     m_protocolVersion = getProtocolVersion();
00691   }
00692 
00693   // Find the number of parameters in TOC
00694   crtpParamTocGetInfoV2Request infoRequest;
00695   startBatchRequest();
00696   // std::cout << "infoReq" << std::endl;
00697   addRequest(infoRequest, 1);
00698   if (m_protocolVersion >= 4) {
00699     handleRequests();
00700     numParam = getRequestResult<crtpParamTocGetInfoV2Response>(0)->numParam;
00701     crc = getRequestResult<crtpParamTocGetInfoV2Response>(0)->crc;
00702   } else {
00703     // std::cout << "Fall back to V1 param API" << std::endl;
00704     m_param_use_V2 = false;
00705 
00706     crtpParamTocGetInfoRequest infoRequest;
00707     startBatchRequest();
00708     addRequest(infoRequest, 1);
00709     handleRequests();
00710     numParam = getRequestResult<crtpParamTocGetInfoResponse>(0)->numParam;
00711     crc = getRequestResult<crtpParamTocGetInfoResponse>(0)->crc;
00712   }
00713 
00714   // check if it is in the cache
00715   std::string fileName = "params" + std::to_string(crc) + ".csv";
00716   std::ifstream infile(fileName);
00717 
00718   if (forceNoCache || !infile.good()) {
00719     m_logger.info("Params: " + std::to_string(numParam));
00720 
00721     // Request detailed information and values
00722     startBatchRequest();
00723     if (!m_param_use_V2) {
00724       for (uint16_t i = 0; i < numParam; ++i) {
00725         crtpParamTocGetItemRequest itemRequest(i);
00726         addRequest(itemRequest, 2);
00727         crtpParamReadRequest readRequest(i);
00728         addRequest(readRequest, 1);
00729       }
00730     } else {
00731       for (uint16_t i = 0; i < numParam; ++i) {
00732         crtpParamTocGetItemV2Request itemRequest(i);
00733         addRequest(itemRequest, 2);
00734         crtpParamReadV2Request readRequest(i);
00735         addRequest(readRequest, 1);
00736       }
00737     }
00738     handleRequests();
00739     // Update internal structure with obtained data
00740     m_paramTocEntries.resize(numParam);
00741 
00742     if (!m_param_use_V2) {
00743       for (uint16_t i = 0; i < numParam; ++i) {
00744         auto r = getRequestResult<crtpParamTocGetItemResponse>(i*2+0);
00745         auto val = getRequestResult<crtpParamValueResponse>(i*2+1);
00746 
00747         ParamTocEntry& entry = m_paramTocEntries[i];
00748         entry.id = i;
00749         entry.type = (ParamType)(r->length | r-> type << 2 | r->sign << 3);
00750         entry.readonly = r->readonly;
00751         entry.group = std::string(&r->text[0]);
00752         entry.name = std::string(&r->text[entry.group.size() + 1]);
00753 
00754         ParamValue v;
00755         std::memcpy(&v, &val->valueFloat, 4);
00756         m_paramValues[i] = v;
00757       }
00758     } else {
00759       for (uint16_t i = 0; i < numParam; ++i) {
00760         auto r = getRequestResult<crtpParamTocGetItemV2Response>(i*2+0);
00761         auto val = getRequestResult<crtpParamValueV2Response>(i*2+1);
00762 
00763         ParamTocEntry& entry = m_paramTocEntries[i];
00764         entry.id = i;
00765         entry.type = (ParamType)(r->length | r-> type << 2 | r->sign << 3);
00766         entry.readonly = r->readonly;
00767         entry.group = std::string(&r->text[0]);
00768         entry.name = std::string(&r->text[entry.group.size() + 1]);
00769 
00770         ParamValue v;
00771         std::memcpy(&v, &val->valueFloat, 4);
00772         m_paramValues[i] = v;
00773       }
00774     }
00775 
00776     // Write a cache file
00777     {
00778       // Atomic file write: write in temporary file first to avoid race conditions
00779       std::string fileNameTemp = fileName + ".tmp";
00780       std::ofstream output(fileNameTemp);
00781       output << "id,type,readonly,group,name" << std::endl;
00782       for (const auto& entry : m_paramTocEntries) {
00783         output << std::to_string(entry.id) << ","
00784                << std::to_string(entry.type) << ","
00785                << std::to_string(entry.readonly) << ","
00786                << entry.group << ","
00787                << entry.name << std::endl;
00788       }
00789       // change the filename
00790       rename(fileNameTemp.c_str(), fileName.c_str());
00791     }
00792   } else {
00793     m_logger.info("Found variables in cache.");
00794     m_paramTocEntries.clear();
00795     std::string line, cell;
00796     std::getline(infile, line); // ignore header
00797     while (std::getline(infile, line)) {
00798       std::stringstream lineStream(line);
00799       m_paramTocEntries.resize(m_paramTocEntries.size() + 1);
00800       std::getline(lineStream, cell, ',');
00801       m_paramTocEntries.back().id = std::stoi(cell);
00802       std::getline(lineStream, cell, ',');
00803       m_paramTocEntries.back().type = (ParamType)std::stoi(cell);
00804       std::getline(lineStream, cell, ',');
00805       m_paramTocEntries.back().readonly = std::stoi(cell);
00806       std::getline(lineStream, cell, ',');
00807       m_paramTocEntries.back().group = cell;
00808       std::getline(lineStream, cell, ',');
00809       m_paramTocEntries.back().name = cell;
00810     }
00811 
00812     // Request values
00813     if (!m_param_use_V2) {
00814       startBatchRequest();
00815       for (size_t i = 0; i < numParam; ++i) {
00816         crtpParamReadRequest readRequest(i);
00817         addRequest(readRequest, 1);
00818       }
00819       handleRequests();
00820       for (size_t i = 0; i < numParam; ++i) {
00821         auto val = getRequestResult<crtpParamValueResponse>(i);
00822         ParamValue v;
00823         std::memcpy(&v, &val->valueFloat, 4);
00824         m_paramValues[i] = v;
00825       }
00826     } else {
00827       startBatchRequest();
00828       for (size_t i = 0; i < numParam; ++i) {
00829         crtpParamReadV2Request readRequest(i);
00830         addRequest(readRequest, 1);
00831       }
00832       handleRequests();
00833       for (size_t i = 0; i < numParam; ++i) {
00834         auto val = getRequestResult<crtpParamValueV2Response>(i);
00835         ParamValue v;
00836         std::memcpy(&v, &val->valueFloat, 4);
00837         m_paramValues[i] = v;
00838       }
00839     }
00840   }
00841 }
00842 
00843 void Crazyflie::requestMemoryToc()
00844 {
00845   // Find the number of parameters in TOC
00846   crtpMemoryGetNumberRequest infoRequest;
00847   startBatchRequest();
00848   addRequest(infoRequest, 1);
00849   handleRequests();
00850   uint8_t len = getRequestResult<crtpMemoryGetNumberResponse>(0)->numberOfMemories;
00851 
00852   m_logger.info("Memories: " + std::to_string(len));
00853 
00854   // Request detailed information and values
00855   startBatchRequest();
00856   for (uint8_t i = 0; i < len; ++i) {
00857     crtpMemoryGetInfoRequest itemRequest(i);
00858     addRequest(itemRequest, 2);
00859   }
00860   handleRequests();
00861 
00862   // Update internal structure with obtained data
00863   m_memoryTocEntries.resize(len);
00864   for (uint8_t i = 0; i < len; ++i) {
00865     auto info = getRequestResult<crtpMemoryGetInfoResponse>(i);
00866 
00867     MemoryTocEntry& entry = m_memoryTocEntries[i];
00868     entry.id = i;
00869     entry.type = (MemoryType)info->memType;
00870     entry.size = info->memSize;
00871     entry.addr = info->memAddr;
00872   }
00873 }
00874 
00875 void Crazyflie::startSetParamRequest()
00876 {
00877   startBatchRequest();
00878 }
00879 
00880 void Crazyflie::addSetParam(uint8_t id, const ParamValue& value)
00881 {
00882   bool found = false;
00883   for (auto&& entry : m_paramTocEntries) {
00884     if (entry.id == id) {
00885       found = true;
00886       if (!m_param_use_V2) {
00887         switch (entry.type) {
00888           case ParamTypeUint8:
00889             {
00890               crtpParamWriteRequest<uint8_t> request(id, value.valueUint8);
00891               addRequest(request, 1);
00892               break;
00893             }
00894           case ParamTypeInt8:
00895             {
00896               crtpParamWriteRequest<int8_t> request(id, value.valueInt8);
00897               addRequest(request, 1);
00898               break;
00899             }
00900           case ParamTypeUint16:
00901             {
00902               crtpParamWriteRequest<uint16_t> request(id, value.valueUint16);
00903               addRequest(request, 1);
00904               break;
00905             }
00906           case ParamTypeInt16:
00907             {
00908               crtpParamWriteRequest<int16_t> request(id, value.valueInt16);
00909               addRequest(request, 1);
00910               break;
00911             }
00912           case ParamTypeUint32:
00913             {
00914               crtpParamWriteRequest<uint32_t> request(id, value.valueUint32);
00915               addRequest(request, 1);
00916               break;
00917             }
00918           case ParamTypeInt32:
00919             {
00920               crtpParamWriteRequest<int32_t> request(id, value.valueInt32);
00921               addRequest(request, 1);
00922               break;
00923             }
00924           case ParamTypeFloat:
00925             {
00926               crtpParamWriteRequest<float> request(id, value.valueFloat);
00927               addRequest(request, 1);
00928               break;
00929             }
00930         }
00931       } else {
00932         switch (entry.type) {
00933           case ParamTypeUint8:
00934             {
00935               crtpParamWriteV2Request<uint8_t> request(id, value.valueUint8);
00936               addRequest(request, 2);
00937               break;
00938             }
00939           case ParamTypeInt8:
00940             {
00941               crtpParamWriteV2Request<int8_t> request(id, value.valueInt8);
00942               addRequest(request, 2);
00943               break;
00944             }
00945           case ParamTypeUint16:
00946             {
00947               crtpParamWriteV2Request<uint16_t> request(id, value.valueUint16);
00948               addRequest(request, 2);
00949               break;
00950             }
00951           case ParamTypeInt16:
00952             {
00953               crtpParamWriteV2Request<int16_t> request(id, value.valueInt16);
00954               addRequest(request, 2);
00955               break;
00956             }
00957           case ParamTypeUint32:
00958             {
00959               crtpParamWriteV2Request<uint32_t> request(id, value.valueUint32);
00960               addRequest(request, 2);
00961               break;
00962             }
00963           case ParamTypeInt32:
00964             {
00965               crtpParamWriteV2Request<int32_t> request(id, value.valueInt32);
00966               addRequest(request, 2);
00967               break;
00968             }
00969           case ParamTypeFloat:
00970             {
00971               crtpParamWriteV2Request<float> request(id, value.valueFloat);
00972               addRequest(request, 2);
00973               break;
00974             }
00975         }
00976       }
00977     }
00978   }
00979 
00980   if (!found) {
00981     std::stringstream sstr;
00982     sstr << "Could not find parameter with id " << id;
00983     throw std::runtime_error(sstr.str());
00984   }
00985 
00986   m_paramValues[id] = value;
00987 }
00988 
00989 void Crazyflie::setRequestedParams()
00990 {
00991   handleRequests();
00992 }
00993 
00994 void Crazyflie::setParam(uint8_t id, const ParamValue& value)
00995 {
00996   startBatchRequest();
00997   addSetParam(id, value);
00998   setRequestedParams();
00999 }
01000 
01001 bool Crazyflie::sendPacketInternal(
01002   const uint8_t* data,
01003   uint32_t length,
01004   bool useSafeLink)
01005 {
01006   ITransport::Ack ack;
01007   sendPacketInternal(data, length, ack, useSafeLink);
01008   return ack.ack;
01009 }
01010 
01011  void Crazyflie::sendPacketOrTimeoutInternal(
01012    const uint8_t* data,
01013    uint32_t length,
01014    bool useSafeLink,
01015    float timeout)
01016 {
01017   auto start = std::chrono::system_clock::now();
01018   while (!sendPacketInternal(data, length, useSafeLink)) {
01019     auto end = std::chrono::system_clock::now();
01020     std::chrono::duration<double> elapsedSeconds = end-start;
01021     if (elapsedSeconds.count() > timeout) {
01022       throw std::runtime_error("timeout");
01023     }
01024   }
01025 }
01026 
01027 void Crazyflie::sendPacketInternal(
01028   const uint8_t* data,
01029   uint32_t length,
01030   ITransport::Ack& ack,
01031   bool useSafeLink)
01032 {
01033   static uint32_t numPackets = 0;
01034   static uint32_t numAcks = 0;
01035 
01036   numPackets++;
01037 
01038   if (m_radio) {
01039     std::unique_lock<std::mutex> mlock(g_radioMutex[m_devId]);
01040     if (m_radio->getAddress() != m_address) {
01041       m_radio->setAddress(m_address);
01042     }
01043     if (m_radio->getChannel() != m_channel) {
01044       m_radio->setChannel(m_channel);
01045     }
01046     if (m_radio->getDatarate() != m_datarate) {
01047       m_radio->setDatarate(m_datarate);
01048     }
01049     if (!m_radio->getAckEnable()) {
01050       m_radio->setAckEnable(true);
01051     }
01052     // consider safelink here:
01053     //    Adds 1bit counter to CRTP header to guarantee that no ack (downlink)
01054     //    payload are lost and no uplink packet are duplicated.
01055     //    The caller should resend packet if not acked (ie. same as with a
01056     //    direct call to crazyradio.send_packet)
01057     if (useSafeLink) {
01058       std::vector<uint8_t> dataCopy(data, data + length);
01059       dataCopy[0] &= 0xF3;
01060       dataCopy[0] |= m_curr_up << 3 | m_curr_down << 2;
01061       m_radio->sendPacket(dataCopy.data(), length, ack);
01062       if (ack.ack && ack.size > 0 && (ack.data[0] & 0x04) == (m_curr_down << 2)) {
01063         m_curr_down = 1 - m_curr_down;
01064       }
01065       if (ack.ack) {
01066         m_curr_up = 1 - m_curr_up;
01067       }
01068     } else {
01069       m_radio->sendPacket(data, length, ack);
01070     }
01071 
01072   } else {
01073     std::unique_lock<std::mutex> mlock(g_crazyflieusbMutex[m_devId]);
01074     m_transport->sendPacket(data, length, ack);
01075   }
01076   ack.data[ack.size] = 0;
01077   if (ack.ack) {
01078     handleAck(ack);
01079     numAcks++;
01080   }
01081   if (numPackets == 100) {
01082     if (m_linkQualityCallback) {
01083       // We just take the ratio of sent vs. acked packets here
01084       // for a sliding window of 100 packets
01085       float linkQuality = numAcks / (float)numPackets;
01086       m_linkQualityCallback(linkQuality);
01087     }
01088     numPackets = 0;
01089     numAcks = 0;
01090   }
01091 }
01092 
01093 void Crazyflie::handleAck(
01094   const ITransport::Ack& result)
01095 {
01096   if (crtpConsoleResponse::match(result)) {
01097     if (result.size > 0) {
01098       crtpConsoleResponse* r = (crtpConsoleResponse*)result.data;
01099       if (m_consoleCallback) {
01100         m_consoleCallback(r->text);
01101       }
01102     }
01103     // ROS_INFO("Console: %s", r->text);
01104   }
01105   else if (crtpLogGetInfoResponse::match(result)) {
01106     // handled in batch system
01107   }
01108   else if (crtpLogGetItemResponse::match(result)) {
01109     // handled in batch system
01110   }
01111   else if (crtpLogControlResponse::match(result)) {
01112     // handled in batch system
01113   }
01114   else if (crtpLogDataResponse::match(result)) {
01115     crtpLogDataResponse* r = (crtpLogDataResponse*)result.data;
01116     auto iter = m_logBlockCb.find(r->blockId);
01117     if (iter != m_logBlockCb.end()) {
01118       iter->second(r, result.size - 5);
01119     }
01120     else {
01121       m_logger.warning("Received unrequested data for block: " + std::to_string((int)r->blockId));
01122     }
01123   }
01124   else if (crtpParamTocGetInfoResponse::match(result)) {
01125     // handled in batch system
01126   }
01127   else if (crtpParamTocGetItemResponse::match(result)) {
01128     // handled in batch system
01129   }
01130   else if (crtpParamTocGetInfoV2Response::match(result)) {
01131     // handled in batch system
01132   }
01133   else if (crtpParamTocGetItemV2Response::match(result)) {
01134     // handled in batch system
01135   }
01136   else if (crtpMemoryGetNumberResponse::match(result)) {
01137     // handled in batch system
01138   }
01139   else if (crtpMemoryGetInfoResponse::match(result)) {
01140     // handled in batch system
01141   }
01142   else if (crtpParamValueResponse::match(result)) {
01143     // handled in batch system
01144   }
01145   else if (crtpMemoryGetNumberResponse::match(result)) {
01146     // handled in batch system
01147   }
01148   else if (crtpMemoryReadResponse::match(result)) {
01149     // handled in batch system
01150   }
01151   else if (crtpMemoryWriteResponse::match(result)) {
01152     // handled in batch system
01153   }
01154   else if (crtp(result.data[0]).port == 8) {
01155     // handled in batch system
01156   }
01157   else if (crtpPlatformRSSIAck::match(result)) {
01158     if (result.size >= 3) {
01159       crtpPlatformRSSIAck* r = (crtpPlatformRSSIAck*)result.data;
01160       if (m_emptyAckCallback) {
01161         m_emptyAckCallback(r);
01162       }
01163     }
01164   }
01165   else {
01166     crtp* header = (crtp*)result.data;
01167     m_logger.warning("Don't know ack: Port: " + std::to_string((int)header->port)
01168       + " Channel: " + std::to_string((int)header->channel)
01169       + " Len: " + std::to_string((int)result.size));
01170     // for (size_t i = 1; i < result.size; ++i) {
01171     //   std::cout << "    " << (int)result.data[i] << std::endl;
01172     // }
01173     if (m_genericPacketCallback) {
01174       m_genericPacketCallback(result);
01175     }
01176   }
01177 }
01178 
01179 const Crazyflie::LogTocEntry* Crazyflie::getLogTocEntry(
01180   const std::string& group,
01181   const std::string& name) const
01182 {
01183   for (auto&& entry : m_logTocEntries) {
01184     if (entry.group == group && entry.name == name) {
01185       return &entry;
01186     }
01187   }
01188   return nullptr;
01189 }
01190 
01191 const Crazyflie::ParamTocEntry* Crazyflie::getParamTocEntry(
01192   const std::string& group,
01193   const std::string& name) const
01194 {
01195   for (auto&& entry : m_paramTocEntries) {
01196     if (entry.group == group && entry.name == name) {
01197       return &entry;
01198     }
01199   }
01200   return nullptr;
01201 }
01202 
01203 uint8_t Crazyflie::registerLogBlock(
01204   std::function<void(crtpLogDataResponse*, uint8_t)> cb)
01205 {
01206   for (uint8_t id = 0; id < 255; ++id) {
01207     if (m_logBlockCb.find(id) == m_logBlockCb.end()) {
01208       m_logBlockCb[id] = cb;
01209       return id;
01210     }
01211   }
01212   return 255;
01213 }
01214 
01215 bool Crazyflie::unregisterLogBlock(
01216   uint8_t id)
01217 {
01218   m_logBlockCb.erase(m_logBlockCb.find(id));
01219   return true;
01220 }
01221 
01222 // Batch system
01223 
01224 void Crazyflie::startBatchRequest()
01225 {
01226   m_batchRequests.clear();
01227 }
01228 
01229 void Crazyflie::addRequestInternal(
01230   const uint8_t* data,
01231   size_t numBytes,
01232   size_t numBytesToMatch)
01233 {
01234   m_batchRequests.resize(m_batchRequests.size() + 1);
01235   m_batchRequests.back().request.resize(numBytes);
01236   memcpy(m_batchRequests.back().request.data(), data, numBytes);
01237   m_batchRequests.back().numBytesToMatch = numBytesToMatch;
01238   m_batchRequests.back().finished = false;
01239 }
01240 
01241 void Crazyflie::handleRequests(
01242   bool crtpMode,
01243   bool useSafeLink,
01244   float baseTime,
01245   float timePerRequest)
01246 {
01247   auto start = std::chrono::system_clock::now();
01248   ITransport::Ack ack;
01249   m_numRequestsFinished = 0;
01250   bool sendPing = false;
01251 
01252   float timeout = baseTime + timePerRequest * m_batchRequests.size();
01253 
01254   while (true) {
01255     if (!crtpMode || !sendPing) {
01256       for (const auto& request : m_batchRequests) {
01257         if (!request.finished) {
01258           // std::cout << "sendReq" << std::endl;
01259           sendPacketInternal(request.request.data(), request.request.size(), ack, useSafeLink);
01260           handleBatchAck(ack, crtpMode);
01261 
01262           auto end = std::chrono::system_clock::now();
01263           std::chrono::duration<double> elapsedSeconds = end-start;
01264           if (elapsedSeconds.count() > timeout) {
01265             throw std::runtime_error("timeout");
01266           }
01267         }
01268       }
01269       if (m_radio) {
01270         sendPing = true;
01271       }
01272     } else {
01273       size_t remainingRequests = m_batchRequests.size() - m_numRequestsFinished;
01274       for (size_t i = 0; i < remainingRequests; ++i) {
01275         crtpEmpty ping;
01276         sendPacket(ping, ack, useSafeLink);
01277         handleBatchAck(ack, crtpMode);
01278         // if (ack.ack && crtpPlatformRSSIAck::match(ack)) {
01279         //   sendPing = false;
01280         // }
01281 
01282         auto end = std::chrono::system_clock::now();
01283         std::chrono::duration<double> elapsedSeconds = end-start;
01284         if (elapsedSeconds.count() > timeout) {
01285           throw std::runtime_error("timeout");
01286         }
01287       }
01288 
01289       sendPing = false;
01290     }
01291     if (m_numRequestsFinished == m_batchRequests.size()) {
01292       break;
01293     }
01294   }
01295 }
01296 
01297 void Crazyflie::handleBatchAck(
01298   const ITransport::Ack& ack,
01299   bool crtpMode)
01300 {
01301   if (ack.ack) {
01302     for (auto& request : m_batchRequests) {
01303       if (crtpMode) {
01304         if ((crtp(ack.data[0]) == crtp(request.request[0]) || ack.data[0] == request.request[0])
01305             && memcmp(&ack.data[1], &request.request[1], request.numBytesToMatch) == 0
01306             && !request.finished) {
01307           request.ack = ack;
01308           request.finished = true;
01309           ++m_numRequestsFinished;
01310           // std::cout << "gotack" <<std::endl;
01311           return;
01312         }
01313       } else {
01314         if (!request.finished
01315             && memcmp(&ack.data[0], &request.request[0], request.numBytesToMatch) == 0) {
01316           request.ack = ack;
01317           request.finished = true;
01318           ++m_numRequestsFinished;
01319           // std::cout << m_numRequestsFinished / (float)m_batchRequests.size() * 100.0 << " %" << std::endl;
01320           return;
01321         }
01322       }
01323     }
01324     // ack is (also) handled in sendPacket
01325   }
01326 }
01327 
01328 void Crazyflie::setGroupMask(uint8_t groupMask)
01329 {
01330   crtpCommanderHighLevelSetGroupMaskRequest request(groupMask);
01331   startBatchRequest();
01332   addRequest(request, 2);
01333   handleRequests();
01334 }
01335 
01336 void Crazyflie::takeoff(float height, float duration, uint8_t groupMask)
01337 {
01338   crtpCommanderHighLevelTakeoffRequest req(groupMask, height, duration);
01339   sendPacketOrTimeout(req);
01340 }
01341 
01342 void Crazyflie::land(float height, float duration, uint8_t groupMask)
01343 {
01344   crtpCommanderHighLevelLandRequest req(groupMask, height, duration);
01345   sendPacketOrTimeout(req);
01346 }
01347 
01348 void Crazyflie::stop(uint8_t groupMask)
01349 {
01350   crtpCommanderHighLevelStopRequest req(groupMask);
01351   sendPacketOrTimeout(req);
01352 }
01353 
01354 void Crazyflie::goTo(float x, float y, float z, float yaw, float duration, bool relative, uint8_t groupMask)
01355 {
01356   crtpCommanderHighLevelGoToRequest req(groupMask, relative, x, y, z, yaw, duration);
01357   sendPacketOrTimeout(req);
01358 }
01359 
01360 void Crazyflie::uploadTrajectory(
01361   uint8_t trajectoryId,
01362   uint32_t pieceOffset,
01363   const std::vector<poly4d>& pieces)
01364 {
01365   for (const auto& entry : m_memoryTocEntries) {
01366     if (entry.type == MemoryTypeTRAJ) {
01367       startBatchRequest();
01368       // upload pieces
01369       size_t remainingBytes = sizeof(poly4d) * pieces.size();
01370       size_t numRequests = ceil(remainingBytes / 24);
01371       for (size_t i = 0; i < numRequests; ++i) {
01372         crtpMemoryWriteRequest req(entry.id, pieceOffset * sizeof(poly4d) + i*24);
01373         size_t size = std::min<size_t>(remainingBytes, 24);
01374         memcpy(req.data, reinterpret_cast<const uint8_t*>(pieces.data()) + i * 24, size);
01375         remainingBytes -= size;
01376         addRequestInternal(reinterpret_cast<const uint8_t*>(&req), 6 + size, 5);
01377       }
01378       // define trajectory
01379       crtpCommanderHighLevelDefineTrajectoryRequest req(trajectoryId);
01380       req.description.trajectoryLocation = TRAJECTORY_LOCATION_MEM;
01381       req.description.trajectoryType = TRAJECTORY_TYPE_POLY4D;
01382       req.description.trajectoryIdentifier.mem.offset = pieceOffset * sizeof(poly4d);
01383       req.description.trajectoryIdentifier.mem.n_pieces = (uint8_t)pieces.size();
01384       addRequest(req, 2);
01385       handleRequests();
01386       return;
01387     }
01388   }
01389   throw std::runtime_error("Could not find MemoryTypeTRAJ!");
01390 }
01391 
01392 void Crazyflie::startTrajectory(
01393   uint8_t trajectoryId,
01394   float timescale,
01395   bool reversed,
01396   bool relative,
01397   uint8_t groupMask)
01398 {
01399   crtpCommanderHighLevelStartTrajectoryRequest req(groupMask, relative, reversed, trajectoryId, timescale);
01400   sendPacketOrTimeout(req);
01401 }
01402 
01404 
01405 CrazyflieBroadcaster::CrazyflieBroadcaster(
01406   const std::string& link_uri)
01407   : m_radio(nullptr)
01408   , m_devId(0)
01409   , m_channel(0)
01410   , m_address(0)
01411   , m_datarate(Crazyradio::Datarate_250KPS)
01412 {
01413   int datarate;
01414   int channel;
01415   char datarateType;
01416   bool success = false;
01417 
01418   success = std::sscanf(link_uri.c_str(), "radio://%d/%d/%d%c/%" SCNx64,
01419      &m_devId, &channel, &datarate,
01420      &datarateType, &m_address) == 5;
01421   if (!success) {
01422     success = std::sscanf(link_uri.c_str(), "radio://%d/%d/%d%c",
01423        &m_devId, &channel, &datarate,
01424        &datarateType) == 4;
01425     m_address = 0xE7E7E7E7E7;
01426   }
01427 
01428   if (success)
01429   {
01430     m_channel = channel;
01431     if (datarate == 250 && datarateType == 'K') {
01432       m_datarate = Crazyradio::Datarate_250KPS;
01433     }
01434     else if (datarate == 1 && datarateType == 'M') {
01435       m_datarate = Crazyradio::Datarate_1MPS;
01436     }
01437     else if (datarate == 2 && datarateType == 'M') {
01438       m_datarate = Crazyradio::Datarate_2MPS;
01439     }
01440 
01441     if (m_devId >= MAX_RADIOS) {
01442       throw std::runtime_error("This version does not support that many radios. Adjust MAX_RADIOS and recompile!");
01443     }
01444 
01445     {
01446       std::unique_lock<std::mutex> mlock(g_radioMutex[m_devId]);
01447       if (!g_crazyradios[m_devId]) {
01448         g_crazyradios[m_devId] = new Crazyradio(m_devId);
01449         g_crazyradios[m_devId]->enableLogging(LOG_COMMUNICATION);
01450         // g_crazyradios[m_devId]->setAckEnable(false);
01451         g_crazyradios[m_devId]->setAckEnable(true);
01452         g_crazyradios[m_devId]->setArc(0);
01453       }
01454     }
01455 
01456     m_radio = g_crazyradios[m_devId];
01457   }
01458   else {
01459     throw std::runtime_error("Uri is not valid!");
01460   }
01461 }
01462 
01463 void CrazyflieBroadcaster::sendPacket(
01464   const uint8_t* data,
01465   uint32_t length)
01466 {
01467   std::unique_lock<std::mutex> mlock(g_radioMutex[m_devId]);
01468   if (m_radio->getAddress() != m_address) {
01469     m_radio->setAddress(m_address);
01470   }
01471   if (m_radio->getChannel() != m_channel) {
01472     m_radio->setChannel(m_channel);
01473   }
01474   if (m_radio->getDatarate() != m_datarate) {
01475     m_radio->setDatarate(m_datarate);
01476   }
01477   if (m_radio->getAckEnable()) {
01478     m_radio->setAckEnable(false);
01479   }
01480   m_radio->sendPacketNoAck(data, length);
01481 }
01482 
01483 void CrazyflieBroadcaster::send2Packets(
01484   const uint8_t* data,
01485   uint32_t length)
01486 {
01487   std::unique_lock<std::mutex> mlock(g_radioMutex[m_devId]);
01488   if (m_radio->getAddress() != m_address) {
01489     m_radio->setAddress(m_address);
01490   }
01491   if (m_radio->getChannel() != m_channel) {
01492     m_radio->setChannel(m_channel);
01493   }
01494   if (m_radio->getDatarate() != m_datarate) {
01495     m_radio->setDatarate(m_datarate);
01496   }
01497   if (m_radio->getAckEnable()) {
01498     m_radio->setAckEnable(false);
01499   }
01500   m_radio->send2PacketsNoAck(data, length);
01501 }
01502 
01503 void CrazyflieBroadcaster::takeoff(float height, float duration, uint8_t groupMask)
01504 {
01505   crtpCommanderHighLevelTakeoffRequest req(groupMask, height, duration);
01506   sendPacket((uint8_t*)&req, sizeof(req));
01507 }
01508 
01509 void CrazyflieBroadcaster::land(float height, float duration, uint8_t groupMask)
01510 {
01511   crtpCommanderHighLevelLandRequest req(groupMask, height, duration);
01512   sendPacket((uint8_t*)&req, sizeof(req));
01513 }
01514 
01515 void CrazyflieBroadcaster::stop(uint8_t groupMask)
01516 {
01517   crtpCommanderHighLevelStopRequest req(groupMask);
01518   sendPacket((uint8_t*)&req, sizeof(req));
01519 }
01520 
01521 // This is always in relative coordinates
01522 void CrazyflieBroadcaster::goTo(float x, float y, float z, float yaw, float duration, uint8_t groupMask)
01523 {
01524   crtpCommanderHighLevelGoToRequest req(groupMask, true, x, y, z, yaw, duration);
01525   sendPacket((uint8_t*)&req, sizeof(req));
01526 }
01527 
01528 // This is always in relative coordinates
01529 // TODO: this does not support trajectories that are of a different length!
01530 void CrazyflieBroadcaster::startTrajectory(
01531   uint8_t trajectoryId,
01532   float timescale,
01533   bool reversed,
01534   uint8_t groupMask)
01535 {
01536   crtpCommanderHighLevelStartTrajectoryRequest req(groupMask, true, reversed, trajectoryId, timescale);
01537   sendPacket((uint8_t*)&req, sizeof(req));
01538 }
01539 
01540 void CrazyflieBroadcaster::sendExternalPositions(
01541   const std::vector<externalPosition>& data)
01542 {
01543   if (data.size() == 0) {
01544     return;
01545   }
01546 
01547   std::vector<crtpExternalPositionPacked> requests(ceil(data.size() / 4.0));
01548   for (size_t i = 0; i < data.size(); ++i) {
01549     size_t j = i / 4;
01550     requests[j].positions[i%4].id = data[i].id;
01551     requests[j].positions[i%4].x = data[i].x * 1000;
01552     requests[j].positions[i%4].y = data[i].y * 1000;
01553     requests[j].positions[i%4].z = data[i].z * 1000;
01554   }
01555   // 1 header byte per packet; 7 bytes for each position
01556   size_t numBytes = requests.size() + data.size() * 7;
01557 
01558   size_t remainingRequests = requests.size();
01559   size_t i = 0;
01560   while (remainingRequests > 0) {
01561     // the crazyradio requires the two packets to be the same size
01562     // -> only send2packets if this is possible
01563     if (   remainingRequests >= 2
01564         && numBytes >= 2 * sizeof(crtpExternalPositionPacked)) {
01565       size_t size = std::min(numBytes, 2 * sizeof(crtpExternalPositionPacked));
01566       send2Packets(reinterpret_cast<const uint8_t*>(&requests[i]), size);
01567       remainingRequests -= 2;
01568       numBytes -= size;
01569       i += 2;
01570     } else {
01571       size_t size = std::min(numBytes, sizeof(crtpExternalPositionPacked));
01572       sendPacket(reinterpret_cast<const uint8_t*>(&requests[i]), size);
01573       remainingRequests -= 1;
01574       numBytes -= size;
01575       i += 1;
01576     }
01577   }
01578   // assert(numBytes == 0);
01579 }
01580 
01581 void CrazyflieBroadcaster::emergencyStop()
01582 {
01583   crtpEmergencyStopRequest req;
01584   sendPacket((uint8_t*)&req, sizeof(req));
01585 }
01586 
01587 void CrazyflieBroadcaster::emergencyStopWatchdog()
01588 {
01589   crtpEmergencyStopWatchdogRequest req;
01590   sendPacket((uint8_t*)&req, sizeof(req));
01591 }
01592 
01593 // assumes input quaternion is normalized. will fail if not.
01594 static inline uint32_t quatcompress(float const q[4])
01595 {
01596   // we send the values of the quaternion's smallest 3 elements.
01597   unsigned i_largest = 0;
01598   for (unsigned i = 1; i < 4; ++i) {
01599     if (fabsf(q[i]) > fabsf(q[i_largest])) {
01600       i_largest = i;
01601     }
01602   }
01603 
01604   // since -q represents the same rotation as q,
01605   // transform the quaternion so the largest element is positive.
01606   // this avoids having to send its sign bit.
01607   unsigned negate = q[i_largest] < 0;
01608 
01609   // 1/sqrt(2) is the largest possible value
01610   // of the second-largest element in a unit quaternion.
01611 
01612   // do compression using sign bit and 9-bit precision per element.
01613   uint32_t comp = i_largest;
01614   for (unsigned i = 0; i < 4; ++i) {
01615     if (i != i_largest) {
01616       unsigned negbit = (q[i] < 0) ^ negate;
01617       unsigned mag = ((1 << 9) - 1) * (fabsf(q[i]) / (float)M_SQRT1_2) + 0.5f;
01618       comp = (comp << 10) | (negbit << 9) | mag;
01619     }
01620   }
01621 
01622   return comp;
01623 }
01624 
01625 void CrazyflieBroadcaster::sendExternalPoses(
01626   const std::vector<externalPose>& data)
01627 {
01628   if (data.size() == 0) {
01629     return;
01630   }
01631 
01632   std::vector<crtpExternalPosePacked> requests(ceil(data.size() / 2.0));
01633   for (size_t i = 0; i < data.size(); ++i) {
01634     size_t j = i / 2;
01635     requests[j].poses[i%2].id = data[i].id;
01636     requests[j].poses[i%2].x = data[i].x * 1000;
01637     requests[j].poses[i%2].y = data[i].y * 1000;
01638     requests[j].poses[i%2].z = data[i].z * 1000;
01639     float q[4] = { data[i].qx, data[i].qy, data[i].qz, data[i].qw };
01640     requests[j].poses[i%2].quat = quatcompress(q);
01641   }
01642   // 2 header byte per packet; 11 bytes for each position
01643   size_t numBytes = requests.size() * 2 + data.size() * 11;
01644 
01645   size_t remainingRequests = requests.size();
01646   size_t i = 0;
01647   while (remainingRequests > 0) {
01648     // the crazyradio requires the two packets to be the same size
01649     // -> only send2packets if this is possible
01650     if (   remainingRequests >= 2
01651         && numBytes >= 2 * sizeof(crtpExternalPosePacked)) {
01652       size_t size = std::min(numBytes, 2 * sizeof(crtpExternalPosePacked));
01653       send2Packets(reinterpret_cast<const uint8_t*>(&requests[i]), size);
01654       remainingRequests -= 2;
01655       numBytes -= size;
01656       i += 2;
01657     } else {
01658       size_t size = std::min(numBytes, sizeof(crtpExternalPosePacked));
01659       sendPacket(reinterpret_cast<const uint8_t*>(&requests[i]), size);
01660       remainingRequests -= 1;
01661       numBytes -= size;
01662       i += 1;
01663     }
01664   }
01665   // assert(numBytes == 0);
01666 }
01667 
01668 // void CrazyflieBroadcaster::setParam(
01669 //   uint8_t group,
01670 //   uint8_t id,
01671 //   Crazyflie::ParamType type,
01672 //   const Crazyflie::ParamValue& value) {
01673 
01674 //   switch (type) {
01675 //     case Crazyflie::ParamTypeUint8:
01676 //       {
01677 //         crtpParamWriteBroadcastRequest<uint8_t> request(group, id, value.valueUint8);
01678 //         sendPacket((const uint8_t*)&request, sizeof(request));
01679 //         break;
01680 //       }
01681 //     case Crazyflie::ParamTypeInt8:
01682 //       {
01683 //         crtpParamWriteBroadcastRequest<int8_t> request(group, id, value.valueInt8);
01684 //         sendPacket((const uint8_t*)&request, sizeof(request));
01685 //         break;
01686 //       }
01687 //     case Crazyflie::ParamTypeUint16:
01688 //       {
01689 //         crtpParamWriteBroadcastRequest<uint16_t> request(group, id, value.valueUint16);
01690 //         sendPacket((const uint8_t*)&request, sizeof(request));
01691 //         break;
01692 //       }
01693 //     case Crazyflie::ParamTypeInt16:
01694 //       {
01695 //         crtpParamWriteBroadcastRequest<int16_t> request(group, id, value.valueInt16);
01696 //         sendPacket((const uint8_t*)&request, sizeof(request));
01697 //         break;
01698 //       }
01699 //     case Crazyflie::ParamTypeUint32:
01700 //       {
01701 //         crtpParamWriteBroadcastRequest<uint32_t> request(group, id, value.valueUint32);
01702 //         sendPacket((const uint8_t*)&request, sizeof(request));
01703 //         break;
01704 //       }
01705 //     case Crazyflie::ParamTypeInt32:
01706 //       {
01707 //         crtpParamWriteBroadcastRequest<int32_t> request(group, id, value.valueInt32);
01708 //         sendPacket((const uint8_t*)&request, sizeof(request));
01709 //         break;
01710 //       }
01711 //     case Crazyflie::ParamTypeFloat:
01712 //       {
01713 //         crtpParamWriteBroadcastRequest<float> request(group, id, value.valueFloat);
01714 //         sendPacket((const uint8_t*)&request, sizeof(request));
01715 //         break;
01716 //       }
01717 //   }
01718 //   // TODO: technically we should update the internal copy of the value of each CF object
01719 // }


crazyflie_cpp
Author(s): Wolfgang Hoenig
autogenerated on Wed Jun 12 2019 19:20:44