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.

ovasCDriverGTecGUSBampLinux.cpp 13KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353
  1. #if defined TARGET_HAS_ThirdPartyGUSBampCAPI_Linux
  2. #include "ovasCDriverGTecGUSBampLinux.h"
  3. #include <toolkit/ovtk_all.h>
  4. namespace OpenViBE {
  5. namespace AcquisitionServer {
  6. CDriverGTecGUSBampLinux::CDriverGTecGUSBampLinux(IDriverContext& ctx)
  7. : IDriver(ctx), m_settings("AcquisitionServer_Driver_GTecGUSBampLinux", m_driverCtx.getConfigurationManager()),
  8. m_nSamplePerSentBlock(0), m_sampleSend(nullptr), m_sampleReceive(nullptr), m_sampleBuffer(nullptr), m_currentSample(0), m_currentChannel(0)
  9. {
  10. // Default values
  11. m_header.setSamplingFrequency(512);
  12. m_header.setChannelCount(8);
  13. m_config.ao_config = &m_analogOutConfig;
  14. // Configure some defaults so the settings are reasonable as soon as the driver loads and the user can tweak them from there
  15. // Configure the analog waveform to be created by the internal signal generator
  16. m_analogOutConfig.shape = GT_ANALOGOUT_SINE;
  17. m_analogOutConfig.frequency = 1;
  18. m_analogOutConfig.amplitude = 0;
  19. m_analogOutConfig.offset = 0;
  20. // This pretty much has to be GT_NOS_AUTOSET, don't know why, so says the documentation
  21. m_config.number_of_scans = GT_NOS_AUTOSET;
  22. // Disable the trigger line, digital io scan, slave mode and the shortcut
  23. m_config.enable_trigger_line = GT_FALSE;
  24. m_config.scan_dio = GT_FALSE;
  25. m_config.slave_mode = GT_FALSE;
  26. m_config.enable_sc = GT_FALSE;
  27. // Set the mode to just take readings
  28. m_config.mode = GT_MODE_NORMAL;
  29. // Set all the blocks A-D to use the common ground and reference voltages
  30. for (uint32_t i = 0; i < GT_USBAMP_NUM_GROUND; ++i)
  31. {
  32. m_config.common_ground[i] = GT_TRUE;
  33. m_config.common_reference[i] = GT_TRUE;
  34. }
  35. // Configure each input
  36. for (unsigned char i = 0; i < GT_USBAMP_NUM_ANALOG_IN; ++i)
  37. {
  38. // Should be from 1 - 16, specifies which channel to observe as input i
  39. m_config.analog_in_channel[i] = i + 1;
  40. // Don't use any of the filters on channel i
  41. m_config.bandpass[i] = GT_FILTER_NONE;
  42. // Don't use any of the notch filters on channel i
  43. m_config.notch[i] = GT_FILTER_NONE;
  44. // Don't use any of the other channels for bi-polar derivation
  45. m_config.bipolar[i] = GT_BIPOLAR_DERIVATION_NONE;
  46. }
  47. // Now look for any connected devices. If any exist we'll set the name to the first one found
  48. char** devices = nullptr;
  49. size_t nDevice = 0;
  50. // Refresh and get the list of currently connnected devices
  51. GT_UpdateDevices();
  52. nDevice = GT_GetDeviceListSize();
  53. devices = GT_GetDeviceList();
  54. // If any devices were found at all, set the combo box to the first one listed
  55. if (nDevice) { m_deviceName = devices[0]; }
  56. GT_FreeDeviceList(devices, nDevice);
  57. // Now retrieve all those configs from the settings file if they are there to be found (don't need to worry about sample rate or channel number though since they're already in the header)
  58. /*m_settings.add("Header", &m_header);
  59. m_settings.add("DeviceName", static_cast<std::string*>(&m_deviceName));
  60. m_settings.add("Mode", static_cast<int*>(&m_config.mode));
  61. m_settings.add("EnableTrigger", static_cast<bool*>(&m_config.enable_trigger_line));
  62. m_settings.add("ScanDIO", static_cast<bool*>(&m_config.scan_dio));
  63. m_settings.add("SlaveMode", static_cast<bool*>(&m_config.slave_mode));
  64. m_settings.add("EnableShortcut", static_cast<bool*>(&m_config.enable_sc));
  65. m_settings.add("AnalogOutShape", static_cast<int*>(&m_analogOutConfig.shape));
  66. m_settings.add("AnalogOutFrequency", static_cast<int*>(&m_analogOutConfig.frequency));
  67. m_settings.add("AnalogOutAmplitude", static_cast<int*>(&m_analogOutConfig.amplitude));
  68. m_settings.add("AnalogOutOffset", static_cast<int*>(&m_analogOutConfig.offset));*/
  69. /*// Set all the blocks A-D to use the common ground and reference voltages
  70. for (uint32_t i = 0; i < GT_USBAMP_NUM_GROUND; ++i)
  71. {
  72. std::stringstream gndConfigName, configName;
  73. gndConfigName << "CommonGround" << i;
  74. configName << "CommonReference" << i;
  75. m_settings.add(gndConfigName.str().c_str(), static_cast<bool*>(&m_config.common_ground[i]));
  76. m_settings.add(configName.str().c_str(), static_cast<bool*>(&m_config.common_reference[i]));
  77. }*/
  78. /*// Configure each input
  79. for (uint32_t i = 0; i < GT_USBAMP_NUM_ANALOG_IN; ++i)
  80. {
  81. std::stringstream bandpassConfigName, notchConfigName, bipolarConfigName;
  82. bandpassConfigName << "Bandpass" << i;
  83. notchConfigName << "Notch" << i;
  84. bipolarConfigName << "Bipolar" << i;
  85. m_settings.add(bandpassConfigName.str().c_str(), static_cast<int*>(&m_config.bandpass[i]));
  86. m_settings.add(notchConfigName.str().c_str(), static_cast<int*>(&m_config.notch[i]));
  87. m_settings.add(bipolarConfigName.str().c_str(), static_cast<int*>(&m_config.bipolar[i]));
  88. }*/
  89. // This restores saved settings if any, such as sampling rate
  90. m_settings.load();
  91. // Set the sampling rate that may have been changed by load
  92. m_config.sample_rate = m_header.getSamplingFrequency();
  93. // Number of channels that may have been changed by load
  94. m_config.num_analog_in = m_header.getChannelCount();
  95. }
  96. //___________________________________________________________________//
  97. // //
  98. bool CDriverGTecGUSBampLinux::initialize(const uint32_t nSamplePerSentBlock, IDriverCallback& callback)
  99. {
  100. if (m_driverCtx.isConnected()) return false;
  101. if (!m_header.isChannelCountSet() || !m_header.isSamplingFrequencySet()) return false;
  102. // If the scan digital inputs flag is set, the API will return one extra channel outside of the analog data requested, so we need to match that on the header
  103. if (m_config.scan_dio == GT_TRUE)
  104. {
  105. m_header.setChannelCount(m_config.num_analog_in + 1);
  106. m_header.setChannelName(m_config.num_analog_in, "Digital");
  107. }
  108. // Allocate buffers for...
  109. // Sending to OpenViBE
  110. m_sampleSend = new float[m_header.getChannelCount() * nSamplePerSentBlock];
  111. // Receiving from the hardware,
  112. m_sampleReceive = new float[m_header.getChannelCount() * nSamplePerSentBlock];
  113. // Storing the data so we pass it between the two threads - we're using the recommended buffer size put out by gtec, which is enormous
  114. m_sampleBuffer = new float[GT_USBAMP_RECOMMENDED_BUFFER_SIZE / sizeof(float)];
  115. // Set up the queue to help pass the data out of the hardware thread
  116. m_sampleQueue.SetBuffer(m_sampleBuffer, m_header.getChannelCount() * m_header.getSamplingFrequency() / 8);
  117. // If any of that allocation fails then give up. Not sure what setting it all to NULL is for, but we'll go with it.
  118. if (!m_sampleSend || !m_sampleReceive || !m_sampleBuffer)
  119. {
  120. delete[] m_sampleSend;
  121. delete[] m_sampleReceive;
  122. delete[] m_sampleBuffer;
  123. m_sampleSend = m_sampleReceive = m_sampleBuffer = nullptr;
  124. return false;
  125. }
  126. // Apparently this causes the API to print debug info to the console, I'm yet to see any though
  127. GT_ShowDebugInformation(GT_TRUE);
  128. // Try to open the device with the configured name, let the user know how it goes
  129. if (!GT_OpenDevice(m_deviceName.c_str()))
  130. {
  131. m_driverCtx.getLogManager() << Kernel::LogLevel_Error << "Could not open device: " << m_deviceName << "\n";
  132. return false;
  133. }
  134. if (!GT_SetConfiguration(m_deviceName.c_str(), &m_config))
  135. {
  136. m_driverCtx.getLogManager() << Kernel::LogLevel_Error << "Could not apply configuration to device: " << m_deviceName << "\n";
  137. return false;
  138. }
  139. GT_SetDataReadyCallBack(m_deviceName.c_str(), &OnDataReady, static_cast<void*>(this));
  140. // Saves parameters
  141. m_callback = &callback;
  142. m_nSamplePerSentBlock = nSamplePerSentBlock;
  143. return true;
  144. }
  145. bool CDriverGTecGUSBampLinux::start()
  146. {
  147. if (!m_driverCtx.isConnected()) return false;
  148. if (m_driverCtx.isStarted()) return false;
  149. // ...
  150. // request hardware to start
  151. // sending data
  152. // ...
  153. // Need to reset these in case the device is stopped mid-sample and then started again
  154. m_currentChannel = m_currentSample = 0;
  155. GT_StartAcquisition(m_deviceName.c_str());
  156. m_driverCtx.getLogManager() << Kernel::LogLevel_Info << "Acquisition Started\n";
  157. return true;
  158. }
  159. // So when the gtec buffer grows larger than a send buffer, copy it all to a send buffer sized array, then copy it into the actual send buffer one by one.
  160. bool CDriverGTecGUSBampLinux::loop()
  161. {
  162. if (!m_driverCtx.isConnected()) return false;
  163. if (!m_driverCtx.isStarted()) return true;
  164. const CStimulationSet stimSet;
  165. // while there's new data available on the queue
  166. while (m_sampleQueue.Avail())
  167. {
  168. // take it off and put it in the appropriate element in the outgoing buffer
  169. m_sampleQueue.Get(m_sampleSend + m_currentChannel * m_nSamplePerSentBlock + m_currentSample, 1);
  170. // Increment the current channel
  171. m_currentChannel++;
  172. // If the current channel reaches the channel count then move to the next sample
  173. if (m_currentChannel == m_header.getChannelCount())
  174. {
  175. m_currentChannel = 0;
  176. m_currentSample++;
  177. }
  178. // If the sample count reaches the number per sent block, then send it and start again
  179. if (m_currentSample == m_nSamplePerSentBlock)
  180. {
  181. m_callback->setSamples(m_sampleSend); // it looks as if this copies the buffer, so we're free modify it as soon as it executes
  182. // When your sample buffer is fully loaded,
  183. // it is advised to ask the acquisition server
  184. // to correct any drift in the acquisition automatically.
  185. m_driverCtx.correctDriftSampleCount(m_driverCtx.getSuggestedDriftCorrectionSampleCount());
  186. // ...
  187. // receive events from hardware
  188. // and put them the correct way in a CStimulationSet object
  189. //...
  190. m_callback->setStimulationSet(stimSet);
  191. m_currentSample = 0;
  192. }
  193. }
  194. return true;
  195. }
  196. bool CDriverGTecGUSBampLinux::stop()
  197. {
  198. if (!m_driverCtx.isConnected()) return false;
  199. if (!m_driverCtx.isStarted()) return false;
  200. // ...
  201. // request the hardware to stop
  202. // sending data
  203. // ...
  204. GT_StopAcquisition(m_deviceName.c_str());
  205. m_driverCtx.getLogManager() << Kernel::LogLevel_Info << "Acquisition Stopped";
  206. return true;
  207. }
  208. bool CDriverGTecGUSBampLinux::uninitialize()
  209. {
  210. if (!m_driverCtx.isConnected()) return false;
  211. if (m_driverCtx.isStarted()) return false;
  212. GT_CloseDevice(m_deviceName.c_str());
  213. m_driverCtx.getLogManager() << Kernel::LogLevel_Info << "Closed Device: " << m_deviceName << "\n";
  214. // ...
  215. // uninitialize hardware here
  216. // ...
  217. m_sampleQueue.SetBuffer(nullptr, 0);
  218. delete[] m_sampleSend;
  219. delete[] m_sampleBuffer;
  220. delete[] m_sampleReceive;
  221. m_sampleSend = m_sampleReceive = m_sampleBuffer = nullptr;
  222. m_callback = nullptr;
  223. return true;
  224. }
  225. //___________________________________________________________________//
  226. // //
  227. bool CDriverGTecGUSBampLinux::isConfigurable()
  228. {
  229. return false; // change to false if your device is not configurable
  230. }
  231. bool CDriverGTecGUSBampLinux::configure()
  232. {
  233. /*// Change this line if you need to specify some references to your driver attribute that need configuration, e.g. the connection ID.
  234. CConfigurationGTecGUSBampLinux config(m_driverCtx, Directories::getDataDir() + "/applications/acquisition-server/interface-GTecGUSBampLinux.ui", &m_deviceName, &m_config);
  235. if (!config.configure(m_header)) { return false; }
  236. m_header.setChannelCount(m_config.num_analog_in);
  237. m_header.setSamplingFrequency(m_config.sample_rate);
  238. m_settings.save();*/
  239. return true;
  240. }
  241. /*void AcquisitionServer::OnDataReady(void *param)
  242. {
  243. // Like the 'this' pointer, but for a friend function
  244. CDriverGTecGUSBampLinux *that = (CDriverGTecGUSBampLinux*)param;
  245. // This is pretty tricky to know in advance, the API decides how many values to spit out depnding on a few factors it seems.
  246. // We'll allocate a reasonble buffer and call GT_GetData as many times as is necessary
  247. while(size_t nSamplesToRead = GT_GetSamplesAvailable(that->m_deviceName.c_str()))
  248. {
  249. // If there are more samples than will fit in the buffer, just get as many as possible and we can get the rest next iteration
  250. if(nSamplesToRead > CDriverGTecGUSBampLinux::ReceiveBufferSize * sizeof(float))
  251. nSamplesToRead = CDriverGTecGUSBampLinux::ReceiveBufferSize * sizeof(float);
  252. // Get the data -- TODO: rewrite this algorithm such that we can copy directly from GT_GetData into the buffer read in the loop() function - is this a bug?? Maybe, but probably not since the calibration mode was always perfect
  253. GT_GetData(that->m_deviceName.c_str(), reinterpret_cast<unsigned char*>(that->m_sampleReceive), nSamplesToRead);
  254. // Put it on the sample queue
  255. that->m_sampleQueue.Put(that->m_sampleReceive, nSamplesToRead / sizeof(float));
  256. }
  257. }*/
  258. void OnDataReady(void* param)
  259. {
  260. // Like the 'this' pointer, but for a friend function
  261. CDriverGTecGUSBampLinux* that = static_cast<CDriverGTecGUSBampLinux*>(param);
  262. // This is pretty tricky to know in advance, the API decides how many values to spit out depnding on a few factors it seems.
  263. // We'll allocate a reasonble buffer and call GT_GetData as many times as is necessary
  264. while (size_t samplesToRead = GT_GetSamplesAvailable(that->m_deviceName.c_str()))
  265. {
  266. // If there are more samples than will fit in the buffer, just get as many as possible and we can get the rest next iteration
  267. if (samplesToRead > that->m_sampleQueue.FreeContiguous() * sizeof(float)) samplesToRead = that->m_sampleQueue.FreeContiguous() * sizeof(float);
  268. // Get the data and put it directly onto the queue
  269. GT_GetData(that->m_deviceName.c_str(), reinterpret_cast<unsigned char*>(that->m_sampleQueue.NextFreeAddress()), samplesToRead);
  270. // Pad the queue so it recognises how much data was just added to it
  271. that->m_sampleQueue.Pad(samplesToRead / sizeof(float));
  272. }
  273. }
  274. } // namespace AcquisitionServer
  275. } // namespace OpenViBE
  276. #endif // TARGET_HAS_ThirdPartyGUSBampCAPI_Linux