You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

CDriverUnicornLinux.cpp 15KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455
  1. /**
  2. * Software License Agreement (AGPL-3 License)
  3. *
  4. * \file CDriverUnicornLinux.cpp
  5. * \author Igor Beloschapkin, TH-Nuremberg, BCI team
  6. * \date 17/04/2022
  7. *
  8. * This program is free software: you can redistribute it and/or modify
  9. * it under the terms of the GNU Affero General Public License version 3,
  10. * as published by the Free Software Foundation.
  11. *
  12. * This program is distributed in the hope that it will be useful,
  13. * but WITHOUT ANY WARRANTY; without even the implied warranty of
  14. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  15. * GNU Affero General Public License for more details.
  16. *
  17. * You should have received a copy of the GNU Affero General Public License
  18. * along with this program.
  19. * If not, see <http://www.gnu.org/licenses/>.
  20. */
  21. #if defined TARGET_HAS_ThirdPartyUnicornLinux
  22. #include "CDriverUnicornLinux.hpp"
  23. #include <toolkit/ovtk_all.h>
  24. #include <system/ovCTime.h>
  25. #include <cmath>
  26. #include <cstring>
  27. #include <cstdlib>
  28. #include <cstdio>
  29. #include <limits>
  30. #include <functional>
  31. #include <mutex>
  32. #include <thread>
  33. #include "unicorn.h"
  34. namespace OpenViBE {
  35. namespace AcquisitionServer {
  36. CDriverUnicornLinux::CDriverUnicornLinux(IDriverContext& rDriverContext)
  37. : IDriver(rDriverContext), m_settings("AcquisitionServer_Driver_GTecGUSBamp", m_driverCtx.getConfigurationManager())
  38. {
  39. m_header.setSamplingFrequency(UNICORN_SAMPLING_RATE);
  40. m_header.setChannelCount(0);
  41. //m_settings.load();
  42. }
  43. //___________________________________________________________________//
  44. // //
  45. bool CDriverUnicornLinux::initialize(
  46. const uint32_t sampleCountPerSentBlock,
  47. IDriverCallback& callback)
  48. {
  49. if (m_driverCtx.isConnected()) { return false; }
  50. detectDevices();
  51. if (numDevices() == 0) { return false; }
  52. m_sampleCountPerSentBlock = sampleCountPerSentBlock;
  53. m_callback = &callback;
  54. // Set number of channels
  55. int errorCode = UNICORN_GetNumberOfAcquiredChannels(m_devices[kSelectedDevice].handle, &m_acquiredChannelCount);
  56. if (errorCode != UNICORN_ERROR_SUCCESS)
  57. {
  58. m_driverCtx.getLogManager() << Kernel::LogLevel_Error << "Unable to get channel count.\n";
  59. return false;
  60. }
  61. else { m_driverCtx.getLogManager() << Kernel::LogLevel_Info << "Number of available channels is: " << m_acquiredChannelCount << "\n"; }
  62. m_header.setChannelCount(m_acquiredChannelCount); //only 8 channels are EEG, but currently we provide everything
  63. m_lengthBufferRawUnicornDevice = m_acquiredChannelCount * kFrameLength;
  64. /*
  65. * Set channel names
  66. * 17 channels from Unicorn Black on every sample: | EEG1| EEG2| EEG3| EEG4| EEG5| EEG6| EEG7| EEG8| ACCX|ACCY| ACCZ| GYRX|GYRY| GYRZ|CNT|BATLVL|VALID|
  67. * Order might be different. This is why we need to use UNICORN_GetChannelIndex(channel name);
  68. */
  69. m_header.setChannelName(getChannelIndex(m_devices[kSelectedDevice].handle, "EEG 1"), "EEG1");
  70. m_header.setChannelName(getChannelIndex(m_devices[kSelectedDevice].handle, "EEG 2"), "EEG2");
  71. m_header.setChannelName(getChannelIndex(m_devices[kSelectedDevice].handle, "EEG 3"), "EEG3");
  72. m_header.setChannelName(getChannelIndex(m_devices[kSelectedDevice].handle, "EEG 4"), "EEG4");
  73. m_header.setChannelName(getChannelIndex(m_devices[kSelectedDevice].handle, "EEG 5"), "EEG5");
  74. m_header.setChannelName(getChannelIndex(m_devices[kSelectedDevice].handle, "EEG 6"), "EEG6");
  75. m_header.setChannelName(getChannelIndex(m_devices[kSelectedDevice].handle, "EEG 7"), "EEG7");
  76. m_header.setChannelName(getChannelIndex(m_devices[kSelectedDevice].handle, "EEG 8"), "EEG8");
  77. m_header.setChannelName(getChannelIndex(m_devices[kSelectedDevice].handle, "Accelerometer X"), "ACCX");
  78. m_header.setChannelName(getChannelIndex(m_devices[kSelectedDevice].handle, "Accelerometer Y"), "ACCY");
  79. m_header.setChannelName(getChannelIndex(m_devices[kSelectedDevice].handle, "Accelerometer Z"), "ACCZ");
  80. m_header.setChannelName(getChannelIndex(m_devices[kSelectedDevice].handle, "Gyroscope X"), "GYRX");
  81. m_header.setChannelName(getChannelIndex(m_devices[kSelectedDevice].handle, "Gyroscope Y"), "GYRY");
  82. m_header.setChannelName(getChannelIndex(m_devices[kSelectedDevice].handle, "Gyroscope Z"), "GYRZ");
  83. m_channelCounterIndex = getChannelIndex(m_devices[kSelectedDevice].handle, "Counter");
  84. m_header.setChannelName(m_channelCounterIndex, "Counter");
  85. m_header.setChannelName(getChannelIndex(m_devices[kSelectedDevice].handle, "Battery Level"), "Battery");
  86. m_header.setChannelName(getChannelIndex(m_devices[kSelectedDevice].handle, "Validation Indicator"), "VI");
  87. // Initialize buffers
  88. m_bufferReceivedDataFromRing = new float[m_lengthBufferRawUnicornDevice];
  89. m_bufferForOpenVibe = new float[m_lengthBufferRawUnicornDevice];
  90. m_ringBuffer.Initialize(static_cast<uint32_t>(kBufferSizeSeconds * m_header.getSamplingFrequency() * m_lengthBufferRawUnicornDevice));
  91. // Configure each device
  92. for (size_t i = 0; i < numDevices(); i++) { configureDevice(i); }
  93. return true;
  94. }
  95. void CDriverUnicornLinux::detectDevices()
  96. {
  97. int errorCode = UNICORN_ERROR_SUCCESS;
  98. // Get number of available devices
  99. unsigned int availableDevicesCount = 0;
  100. errorCode = UNICORN_GetAvailableDevices(NULL, &availableDevicesCount, TRUE);
  101. // Get serials of available devices
  102. UNICORN_DEVICE_SERIAL* availableDevices = new UNICORN_DEVICE_SERIAL[availableDevicesCount];
  103. errorCode = UNICORN_GetAvailableDevices(availableDevices, &availableDevicesCount, TRUE);
  104. if (errorCode != UNICORN_ERROR_SUCCESS || availableDevicesCount < 1)
  105. {
  106. m_driverCtx.getLogManager() << Kernel::LogLevel_Error << "No device available. Please pair with a Unicorn device first.\n";
  107. }
  108. else
  109. {
  110. // Create a GDevice list for all devices and print available device serials
  111. m_driverCtx.getLogManager() << Kernel::LogLevel_Info << "Available Unicorn devices:\n";
  112. for (unsigned int i = 0; i < availableDevicesCount; i++)
  113. {
  114. m_driverCtx.getLogManager() << Kernel::LogLevel_Info << "#" << i << ": " << availableDevices[i] << "\n";
  115. GDevice device;
  116. UNICORN_HANDLE deviceHandle;
  117. int errorCode = UNICORN_OpenDevice(availableDevices[i], &deviceHandle);
  118. if (errorCode != UNICORN_ERROR_SUCCESS)
  119. {
  120. m_driverCtx.getLogManager() << Kernel::LogLevel_Error << "Unable to connect to device: '" << availableDevices[i] << "'\n";
  121. }
  122. device.handle = deviceHandle;
  123. device.serial = availableDevices[i];
  124. m_devices.push_back(device);
  125. }
  126. }
  127. }
  128. bool CDriverUnicornLinux::configureDevice(size_t deviceNumber)
  129. {
  130. int errorCode = UNICORN_ERROR_SUCCESS;
  131. UNICORN_HANDLE device = m_devices[deviceNumber].handle;
  132. const std::string currentSerial = m_devices[deviceNumber].serial;
  133. UNICORN_AMPLIFIER_CONFIGURATION configuration;
  134. errorCode = UNICORN_GetConfiguration(device, &configuration);
  135. if (errorCode != UNICORN_ERROR_SUCCESS)
  136. {
  137. m_driverCtx.getLogManager() << Kernel::LogLevel_Error << "Unable to get configuration for: '" << currentSerial.c_str() << "'\n";
  138. return false;
  139. }
  140. return true;
  141. }
  142. bool CDriverUnicornLinux::start()
  143. {
  144. if (!m_driverCtx.isConnected()) { return false; }
  145. if (m_driverCtx.isStarted()) { return false; }
  146. m_totalHardwareStimulations = 0;
  147. m_totalRingBufferOverruns = 0;
  148. m_totalCounterErrors = 0;
  149. {
  150. std::lock_guard<std::mutex> lock(m_mutex);
  151. m_ringBuffer.Reset();
  152. }
  153. for (size_t i = 0; i < numDevices(); i++)
  154. {
  155. UNICORN_HANDLE device = m_devices[i].handle;
  156. UNICORN_StartAcquisition(device, kTestSignalEnabled);
  157. }
  158. m_isThreadRunning = true;
  159. m_flagIsFirstLoop = true;
  160. m_bufferOverrun = false;
  161. m_thread.reset(new std::thread(std::bind(&CDriverUnicornLinux::acquire, this)));
  162. return true;
  163. }
  164. // This method is called by the AS and it supplies the acquired data to the AS
  165. bool CDriverUnicornLinux::loop()
  166. {
  167. if (m_driverCtx.isStarted())
  168. {
  169. {
  170. std::unique_lock<std::mutex> lock(m_mutex);
  171. while (m_ringBuffer.GetSize() < static_cast<int>(m_lengthBufferRawUnicornDevice)) { m_itemAvailable.wait(lock); }
  172. try
  173. {
  174. if (m_bufferOverrun)
  175. {
  176. m_ringBuffer.Reset();
  177. m_bufferOverrun = false;
  178. m_totalRingBufferOverruns++;
  179. return true;
  180. }
  181. m_ringBuffer.Read(m_bufferReceivedDataFromRing, m_lengthBufferRawUnicornDevice);
  182. }
  183. catch (std::exception& e)
  184. {
  185. m_driverCtx.getLogManager() << Kernel::LogLevel_Error << "Error reading GTEC ring buffer! Error is:" << e.what() << "\n";
  186. }
  187. m_itemAvailable.notify_one();
  188. }
  189. // covert to openvibe format ch1,ch1,ch1,ch2,ch2,ch2 ...
  190. for (size_t i = 0; i < m_acquiredChannelCount; i++)
  191. {
  192. for (size_t j = 0; j < kFrameLength; j++)
  193. {
  194. m_bufferForOpenVibe[kFrameLength * i + j] = m_bufferReceivedDataFromRing[j * m_acquiredChannelCount + i];
  195. }
  196. }
  197. // verify counter
  198. const size_t counterPos = kFrameLength * m_channelCounterIndex;
  199. for (size_t i = counterPos; i < kFrameLength; i++) { if (!(m_bufferForOpenVibe[i] < m_bufferForOpenVibe[i + 1])) { m_totalCounterErrors++; } }
  200. // forward data
  201. m_callback->setSamples(m_bufferForOpenVibe, kFrameLength);
  202. CStimulationSet stimulationSet;
  203. m_callback->setStimulationSet(stimulationSet);
  204. m_driverCtx.correctDriftSampleCount(m_driverCtx.getSuggestedDriftCorrectionSampleCount());
  205. }
  206. else { System::Time::sleep(20); }
  207. return true;
  208. }
  209. // This function used by the thread
  210. bool CDriverUnicornLinux::acquire()
  211. {
  212. if (m_flagIsFirstLoop) //First time do some memory initialization, etc
  213. {
  214. m_bufferRawUnicornDevice = new float[m_lengthBufferRawUnicornDevice];
  215. m_flagIsFirstLoop = false;
  216. }
  217. while (m_isThreadRunning == true)
  218. {
  219. try
  220. {
  221. bool flagChunkLostDetected = false;
  222. bool flagChunkTimeOutDetected = false;
  223. UNICORN_HANDLE device = m_devices[kSelectedDevice].handle;
  224. // Get kFrameLength number of samples
  225. if (UNICORN_GetData(device, kFrameLength, m_bufferRawUnicornDevice, m_lengthBufferRawUnicornDevice * sizeof(float)) != UNICORN_ERROR_SUCCESS)
  226. {
  227. m_driverCtx.getLogManager() << Kernel::LogLevel_Error << "Error on GT_GetData\n";
  228. return false;
  229. }
  230. // store to ring buffer, lock it during insertion
  231. {
  232. std::lock_guard<std::mutex> lock(m_mutex);
  233. try
  234. {
  235. // if we are going to overrun on writing the received data into the buffer, set the appropriate flag; the reading thread will handle the overrun
  236. m_bufferOverrun = (m_ringBuffer.GetFreeSize() < static_cast<int>(m_lengthBufferRawUnicornDevice));
  237. m_ringBuffer.Write(m_bufferRawUnicornDevice, m_lengthBufferRawUnicornDevice);
  238. }
  239. catch (std::exception& e)
  240. {
  241. // buffer should be unclocked automatically once the scope is left
  242. m_driverCtx.getLogManager() << Kernel::LogLevel_Error << "Error writing to GTEC ring buffer! Error is: " << e.what() << "\n";
  243. }
  244. // buffer should be unclocked automatically once the scope is left
  245. m_itemAvailable.notify_one();
  246. }
  247. }
  248. catch (std::exception& e)
  249. {
  250. m_driverCtx.getLogManager() << Kernel::LogLevel_Error <<
  251. "General error in the thread function acquiring data from GTEC! Acquisition interrupted. Error is: " << e.what() << "\n";
  252. m_isThreadRunning = false;
  253. return false;
  254. }
  255. }
  256. // This code stops the amplifiers in the same thread:
  257. {
  258. m_driverCtx.getLogManager() << Kernel::LogLevel_Info << "Stopping devices and cleaning up...\n";
  259. // clean up allocated resources for each device
  260. for (size_t i = 0; i < numDevices(); i++)
  261. {
  262. UNICORN_HANDLE device = m_devices[i].handle;
  263. // stop device
  264. m_driverCtx.getLogManager() << Kernel::LogLevel_Debug << "Sending stop command ...\n";
  265. if (UNICORN_StopAcquisition(device) != UNICORN_ERROR_SUCCESS)
  266. {
  267. m_driverCtx.getLogManager() << Kernel::LogLevel_Error << "Stopping device failed! Serial = " << m_devices[numDevices() - 1].serial.c_str() << "\n";
  268. }
  269. // clear memory
  270. delete[] m_bufferRawUnicornDevice;
  271. }
  272. m_flagIsFirstLoop = true;
  273. m_isThreadRunning = false;
  274. }
  275. return true;
  276. }
  277. bool CDriverUnicornLinux::stop()
  278. {
  279. if (!m_driverCtx.isConnected()) { return false; }
  280. if (!m_driverCtx.isStarted()) { return false; }
  281. // stop thread
  282. m_isThreadRunning = false;
  283. m_thread->join(); //wait until the thread has stopped data acquisition
  284. m_driverCtx.getLogManager() << Kernel::LogLevel_Info << "Data acquisition completed.\n";
  285. if (m_totalRingBufferOverruns > 0)
  286. {
  287. m_driverCtx.getLogManager() << Kernel::LogLevel_Error << "Total internal ring buffer overruns: " << m_totalRingBufferOverruns << "\n";
  288. }
  289. if (m_totalCounterErrors > 0) { m_driverCtx.getLogManager() << Kernel::LogLevel_Error << "Total Unicorn counter errors: " << m_totalCounterErrors << "\n"; }
  290. else { m_driverCtx.getLogManager() << Kernel::LogLevel_Info << "Total Unicorn counter errors: " << m_totalCounterErrors << "\n"; }
  291. return true;
  292. }
  293. bool CDriverUnicornLinux::uninitialize()
  294. {
  295. if (!m_driverCtx.isConnected()) { return false; }
  296. if (m_driverCtx.isStarted()) { return false; }
  297. const size_t totalDevices = numDevices();
  298. size_t deviceClosed = 0;
  299. for (std::vector<GDevice>::iterator it = m_devices.begin(); it != m_devices.end();)
  300. {
  301. if (UNICORN_CloseDevice(&it->handle) != UNICORN_ERROR_SUCCESS)
  302. {
  303. m_driverCtx.getLogManager() << Kernel::LogLevel_Error << "Unable to close device: " << it->serial.c_str() << "\n";
  304. }
  305. else { deviceClosed++; }
  306. it = m_devices.erase(it);
  307. }
  308. m_driverCtx.getLogManager() << Kernel::LogLevel_Info << "Total devices closed: " << deviceClosed << " / " << totalDevices << "\n";
  309. if (!m_devices.empty() || deviceClosed != totalDevices)
  310. {
  311. m_driverCtx.getLogManager() << Kernel::LogLevel_Error << "Some devices were not closed properly!\n";
  312. }
  313. m_devices.clear();
  314. // clear memory
  315. if (m_bufferReceivedDataFromRing != nullptr)
  316. {
  317. delete[] m_bufferReceivedDataFromRing;
  318. m_bufferReceivedDataFromRing = nullptr;
  319. }
  320. if (m_bufferForOpenVibe != nullptr)
  321. {
  322. delete[] m_bufferForOpenVibe;
  323. m_bufferForOpenVibe = nullptr;
  324. }
  325. m_callback = nullptr;
  326. return true;
  327. }
  328. //___________________________________________________________________//
  329. // //
  330. inline size_t CDriverUnicornLinux::getChannelIndex(UNICORN_HANDLE device, const char* name)
  331. {
  332. uint32_t* result = new uint32_t[1];
  333. if (UNICORN_GetChannelIndex(device, name, result) != UNICORN_ERROR_SUCCESS)
  334. {
  335. m_driverCtx.getLogManager() << Kernel::LogLevel_Error << "Error getting channel index for channel: '" << name << "'\n";
  336. }
  337. return static_cast<size_t>(*result);
  338. }
  339. inline std::ostream& operator<<(std::ostream& out, const std::vector<std::string>& var)
  340. {
  341. for (size_t i = 0; i < var.size(); i++) { out << var[i] << " "; }
  342. return out;
  343. }
  344. inline std::istream& operator>>(std::istream& in, std::vector<std::string>& var)
  345. {
  346. var.clear();
  347. std::string tmp;
  348. while (in >> tmp) { var.push_back(tmp); }
  349. return in;
  350. }
  351. inline std::ostream& operator<<(std::ostream& out, const std::vector<GDevice>& var)
  352. {
  353. for (size_t i = 0; i < var.size(); i++) { out << var[i].serial << " "; }
  354. return out;
  355. }
  356. inline std::istream& operator>>(std::istream& in, std::vector<GDevice>& var)
  357. {
  358. // "Error not implemented operator >>!";
  359. return in;
  360. }
  361. } // namespace AcquisitionServer
  362. } // namespace OpenViBE
  363. #endif // TARGET_HAS_ThirdPartyUnicornLinux