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.

ovasCPluginFiddler.cpp 4.0KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116
  1. /*
  2. * Linearly superposes a simple phase-locked template around 300ms after OVTK_StimulationId_Target
  3. *
  4. * Fiddler can be used to debug P300 scenarios. Note that the same effect could be
  5. * achieved with a box that tampers the signal after specific stimulation markers.
  6. * It can also be used as an example of how to manipulate the signal on the
  7. * acquisition server side using a plugin.
  8. *
  9. * Known limitations: Overlapping patterns not handled
  10. *
  11. */
  12. #include "ovasCPluginFiddler.h"
  13. #include <vector>
  14. #include <system/ovCMath.h>
  15. #include <cmath>
  16. #include "../ovasCSettingsHelper.h"
  17. #include "../ovasCSettingsHelperOperators.h"
  18. namespace OpenViBE {
  19. namespace AcquisitionServer {
  20. namespace Plugins {
  21. CPluginFiddler::CPluginFiddler(const Kernel::IKernelContext& ctx) : IAcquisitionServerPlugin(ctx, CString("AcquisitionServer_Plugin_Fiddler"))
  22. {
  23. m_kernelCtx.getLogManager() << Kernel::LogLevel_Info << "Loading plugin: Fiddler\n";
  24. m_settings.add("Fiddler_Strength", &m_BCI2000VersionFiddlerStrength); // The amplitude of the pattern, 0 = disable
  25. m_settings.load();
  26. }
  27. // Hooks
  28. bool CPluginFiddler::startHook(const std::vector<CString>& /*selectedChannelNames*/, const size_t sampling, const size_t /*nChannel*/, const size_t nSamplePerSentBlock)
  29. {
  30. if (m_BCI2000VersionFiddlerStrength > 10e-06)
  31. {
  32. m_nSamplePerSentBlock = nSamplePerSentBlock;
  33. m_StartSample = std::numeric_limits<uint64_t>::max();
  34. m_EndSample = 0;
  35. m_NProcessedSample = 0;
  36. m_Counter = 0;
  37. m_Sampling = sampling;
  38. m_LastBufferSize = 0;
  39. m_kernelCtx.getLogManager() << Kernel::LogLevel_Warning << "Fiddler is enabled! Only use for debug purposes. Set strength=0 to disable.\n";
  40. }
  41. return true;
  42. }
  43. void CPluginFiddler::loopHook(std::deque<std::vector<float>>& buffers, CStimulationSet& stimSet, const uint64_t /*start*/, const uint64_t /*end*/, const uint64_t /* sampleTime */)
  44. {
  45. if (m_BCI2000VersionFiddlerStrength > 10e-06)
  46. {
  47. // We need to make sure we don't process the same samples twice
  48. uint64_t nProcessed = 0;
  49. if (m_LastBufferSize > m_nSamplePerSentBlock) { nProcessed = m_LastBufferSize - m_nSamplePerSentBlock; }
  50. // Loop over the stimulations
  51. for (size_t i = 0; i < stimSet.getStimulationCount(); ++i)
  52. {
  53. const uint64_t id = stimSet.getStimulationIdentifier(i);
  54. const uint64_t time = stimSet.getStimulationDate(i);
  55. const uint64_t nSample = CTime(time).toSampleCount(m_Sampling);
  56. if (id == OVTK_StimulationId_Target && nSample > m_NProcessedSample)
  57. {
  58. m_StartSample = nSample + CTime(0.0).toSampleCount(m_Sampling);
  59. m_EndSample = nSample + CTime(0.5).toSampleCount(m_Sampling);
  60. m_Counter = 0;
  61. }
  62. }
  63. for (size_t i = size_t(nProcessed); i < buffers.size(); ++i)
  64. {
  65. // std::cout << "Sample " << CTime(l_SampleTime).toSeconds() << " range " << CTime(m_startTime).toSeconds() << " stop " << CTime(m_endTime).toSeconds();
  66. if (m_NProcessedSample > m_StartSample && m_NProcessedSample <= m_EndSample)
  67. {
  68. const float lobe1 = 0.25F; // Position, in seconds
  69. const float lobe2 = 0.30F;
  70. const float spread1 = 0.008F; // Width
  71. const float spread2 = 0.004F;
  72. // Two beta functions weighted by gaussians; the beta parameters are ^1 and ^4
  73. // n.b. not terribly efficient as the same pattern is created every time anew. In practice this is of little importance.
  74. const float st = float(CTime(m_Sampling, m_Counter).toSeconds());
  75. const float bump1 = std::exp(-std::pow(st - lobe1, 2) / spread1) * (st * std::pow(1 - st, 4));
  76. const float bump2 = std::exp(-std::pow(st - lobe2, 2) / spread2) * (st * std::pow(1 - st, 4));
  77. const float value = (-0.5F * bump1 + 0.9F * bump2) * 40.0F;
  78. std::vector<float>& sample = buffers[i];
  79. float* tmp = &sample[0];
  80. for (size_t j = 0; j < sample.size(); ++j) { tmp[j] += value * m_BCI2000VersionFiddlerStrength; }
  81. m_Counter++;
  82. }
  83. m_NProcessedSample++;
  84. }
  85. m_LastBufferSize = buffers.size();
  86. }
  87. }
  88. } // namespace Plugins
  89. } // namespace AcquisitionServer
  90. } // namespace OpenViBE