PageRenderTime 50ms CodeModel.GetById 22ms RepoModel.GetById 0ms app.codeStats 0ms

/external/fieldtrip/trunk/realtime/acquisition/matlab/ft_realtime_fileproxy.m

http://open-realtime-fmri.googlecode.com/
MATLAB | 163 lines | 80 code | 26 blank | 57 comment | 26 complexity | cb4bb40fdd014772d383353b41b9578b MD5 | raw file
Possible License(s): GPL-2.0, GPL-3.0
  1. function ft_realtime_fileproxy(cfg)
  2. % FT_REALTIME_FILEPROXY reads continuous data from an EEG/MEG file and writes it to
  3. % a FieldTrip buffer. This works for any file format that is supported by
  4. % FieldTrip.
  5. %
  6. % The FieldTrip buffer is a network transparent server that allows the
  7. % acquisition client to stream data to it. An analysis client can connect
  8. % to read the data upon request. Multiple clients can connect simultaneously,
  9. % each analyzing a specific aspect of the data concurrently.
  10. %
  11. % Use as
  12. % ft_realtime_fileproxy(cfg)
  13. % with the following configuration options
  14. % cfg.minblocksize = number, in seconds (default = 0)
  15. % cfg.maxblocksize = number, in seconds (default = 1)
  16. % cfg.channel = cell-array, see FT_CHANNELSELECTION (default = 'all')
  17. % cfg.jumptoeof = jump to end of file at initialization (default = 'no')
  18. % cfg.readevent = whether or not to copy events (default = 'no'; event type can also be specified; e.g., 'UPPT002')
  19. % cfg.speed = relative speed at which data is written (default = inf)
  20. %
  21. % The source of the data is configured as
  22. % cfg.source.dataset = string
  23. % or alternatively to obtain more low-level control as
  24. % cfg.source.datafile = string
  25. % cfg.source.headerfile = string
  26. % cfg.source.eventfile = string
  27. % cfg.source.dataformat = string, default is determined automatic
  28. % cfg.source.headerformat = string, default is determined automatic
  29. % cfg.source.eventformat = string, default is determined automatic
  30. %
  31. % The target to write the data to is configured as
  32. % cfg.target.datafile = string, target destination for the data (default = 'buffer://localhost:1972')
  33. % cfg.target.dataformat = string, default is determined automatic
  34. %
  35. % To stop this realtime function, you have to press Ctrl-C
  36. % Copyright (C) 2008, Robert Oostenveld
  37. %
  38. % Subversion does not use the Log keyword, use 'svn log <filename>' or 'svn -v log | less' to get detailled information
  39. % set the defaults
  40. if ~isfield(cfg, 'source'), cfg.source = []; end
  41. if ~isfield(cfg, 'target'), cfg.target = []; end
  42. if ~isfield(cfg.source, 'headerformat'), cfg.source.headerformat = []; end % default is detected automatically
  43. if ~isfield(cfg.source, 'dataformat'), cfg.source.dataformat = []; end % default is detected automatically
  44. if ~isfield(cfg.target, 'headerformat'), cfg.target.headerformat = []; end % default is detected automatically
  45. if ~isfield(cfg.target, 'dataformat'), cfg.target.dataformat = []; end % default is detected automatically
  46. if ~isfield(cfg.target, 'datafile'), cfg.target.datafile = 'buffer://localhost:1972'; end
  47. if ~isfield(cfg, 'minblocksize'), cfg.minblocksize = 0; end % in seconds
  48. if ~isfield(cfg, 'maxblocksize'), cfg.maxblocksize = 1; end % in seconds
  49. if ~isfield(cfg, 'channel'), cfg.channel = 'all'; end
  50. if ~isfield(cfg, 'jumptoeof'), cfg.jumptoeof = 'no'; end % jump to end of file at initialization
  51. if ~isfield(cfg, 'readevent'), cfg.readevent = 'no'; end % capture events?
  52. if ~isfield(cfg, 'speed'), cfg.speed = inf ; end % inf -> run as fast as possible
  53. % translate dataset into datafile+headerfile
  54. cfg.source = ft_checkconfig(cfg.source, 'dataset2files', 'yes');
  55. cfg.target = ft_checkconfig(cfg.target, 'dataset2files', 'yes');
  56. ft_checkconfig(cfg.source, 'required', {'datafile' 'headerfile'});
  57. ft_checkconfig(cfg.target, 'required', {'datafile' 'headerfile'});
  58. if ~isfield(cfg.source,'eventfile') || isempty(cfg.source.eventfile)
  59. cfg.source.eventfile = cfg.source.datafile;
  60. end
  61. if ~isfield(cfg.target,'eventfile') || isempty(cfg.target.eventfile)
  62. cfg.target.eventfile = cfg.target.datafile;
  63. end
  64. % ensure that the persistent variables related to caching are cleared
  65. clear ft_read_header
  66. % read the header for the first time
  67. hdr = ft_read_header(cfg.source.headerfile);
  68. fprintf('updating the header information, %d samples available\n', hdr.nSamples*hdr.nTrials);
  69. % define a subset of channels for reading
  70. cfg.channel = ft_channelselection(cfg.channel, hdr.label);
  71. chanindx = match_str(hdr.label, cfg.channel);
  72. nchan = length(chanindx);
  73. if nchan==0
  74. error('no channels were selected');
  75. end
  76. minblocksmp = round(cfg.minblocksize*hdr.Fs);
  77. minblocksmp = max(minblocksmp, 1);
  78. maxblocksmp = round(cfg.maxblocksize*hdr.Fs);
  79. count = 0;
  80. if strcmp(cfg.jumptoeof, 'yes')
  81. prevSample = hdr.nSamples * hdr.nTrials;
  82. else
  83. prevSample = 0;
  84. end
  85. %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
  86. % this is the general BCI loop where realtime incoming data is handled
  87. %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
  88. evt = [];
  89. while true
  90. % determine number of samples available in buffer
  91. hdr = ft_read_header(cfg.source.headerfile, 'cache', true);
  92. % see whether new samples are available
  93. newsamples = (hdr.nSamples*hdr.nTrials-prevSample);
  94. if newsamples>=minblocksmp
  95. begsample = prevSample+1;
  96. endsample = prevSample+min(newsamples,maxblocksmp);
  97. % remember up to where the data was read
  98. count = count + 1;
  99. fprintf('processing segment %d from sample %d to %d\n', count, begsample, endsample);
  100. % read data segment
  101. dat = ft_read_data(cfg.source.datafile,'header', hdr, 'dataformat', cfg.source.dataformat, 'begsample', begsample, 'endsample', endsample, 'chanindx', chanindx, 'checkboundary', false);
  102. % it only makes sense to read those events associated with the currently processed data
  103. if ~strcmp(cfg.readevent,'no')
  104. evt = ft_read_event(cfg.source.eventfile, 'header', hdr, 'minsample', begsample, 'maxsample', endsample);
  105. if ~strcmp(cfg.readevent,'yes')
  106. evt = ft_filter_event(evt, 'type', cfg.readevent);
  107. end
  108. end
  109. prevSample = endsample;
  110. %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
  111. % from here onward it is specific to writing the data to another stream
  112. %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
  113. if count==1
  114. % the input file may have a different offset than the output file
  115. offset = begsample - 1;
  116. % flush the file, write the header and subsequently write the data segment
  117. ft_write_data(cfg.target.datafile, dat, 'header', hdr, 'dataformat', cfg.target.dataformat, 'chanindx', chanindx, 'append', false);
  118. if ~strcmp(cfg.readevent,'no')
  119. for i=1:numel(evt)
  120. evt(i).sample = evt(i).sample - offset;
  121. end
  122. ft_write_event(cfg.target.eventfile,evt,'append',false);
  123. end
  124. else
  125. % write the data segment
  126. ft_write_data(cfg.target.datafile, dat, 'header', hdr, 'dataformat', cfg.target.dataformat, 'chanindx', chanindx, 'append', true);
  127. if ~strcmp(cfg.readevent,'no')
  128. for i=1:numel(evt)
  129. evt(i).sample = evt(i).sample - offset;
  130. end
  131. ft_write_event(cfg.target.eventfile,evt,'append',true);
  132. end
  133. end % if count==1
  134. % wait for a realistic amount of time
  135. pause(((endsample-begsample+1)/hdr.Fs)/cfg.speed);
  136. end % if enough new samples
  137. end % while true