Sampling frequency of analogue input

License key:

Dear Psychtoolbox Experts,

I am using PTB3 in my behavioral experiment with monkey. The eye position of the animal is monitored by an eye tracker with the sampling frequency of 500 Hz. The x and y position of the corneal reflection is read via AI 00 and AI 01 of the DAQ (USB-1208FS), respectively. By using DaqAInScan and setting the options.f = 500, I confirmed that it can collect 500 samples per second for each of the AI 00 and AI 01 with some dropout of the data. However, since I want to monitor the position point by point (continuously) to check if the eye position is within a window, I used DaqAIn instead in the following code and found that only 16 samples are collected per second for each channel. Considering the sampling frequency of the eye tracker (500 Hz) and DAQ (50 and 25 kHz for the AI 00 and 01, respectively), this sampling rate is too low. How can I read out the analogue inputs point by point at higher frequency in PTB3?

clear all

PsychDefaultSetup( 1 );

DaqID = DaqFind; 

cX = [ ]; 
xY = []; 
array_cX = [];		% array to save x position
array_cY = [];		% array to save y position
array_tX = [];		% array to save time stamp of the x position
array_tY = [];		% array to save time stamp of the y position

% read out analogue inputs for 3 s and save them with the time stamp
T1 = GetSecs; T2 = GetSecs; 
while ( T2 - T1 ) < 3
    cX = DaqAIn( DaqID, 8, 0 );                 % read out AI 00
    array_cX = [ array_cX; cX ]; 
    array_tX = [ array_tX; GetSecs ]; 

    cY = DaqAIn( DaqID, 9, 0 );                 % read out AI 01 
    array_cY = [ array_cY; cY ]; 
    array_tY = [ array_tY; GetSecs ];

% Usually some codes come here to check if the eye position is within a window: 
%    if cX > 5 || cY > 5                             % break fixation
%        break                                           % exit the loop
%    end

    T2 = GetSecs; 

My operating environment is as follows:
PsychtoolboxVersion: 3.0.18 – Flavor: beta – Corresponds to SVN Revision 13009
Platform: Ubuntu 20.04 LTS, 64-bit
Matlab: R2021a (, 64-bit)
CPU: Intel(R) Core™ i7-8700 CPU @ 3.20 GHz
RAM: 64 GB

Any suggestions would be greatly appreciated.



In general DAQs can operate in a stream mode or a command-response mode, and the timing is very different. I’ve never used the 1208FS or the DaqXXX functions but you are using the command-response command and it seems this is the limitation. If you put a tic and toc around the DaqAIn command what time does it take to complete (or you can use the MATLAB profiler)?

You could try using the streaming mode but with the ability to read, at least according to the help:

Use DaqAInScan when you want to complete the whole operation in one call, tying up the computer until the sampling is done. Use DaqAInScanBegin, DaqAInScanContinue, and DaqAInScanEnd, instead, when you want to use the computer during sampling.

So using DaqAInScanBegin instead at least seems to allow to to read while the stream is running. I assume you also want to save this data for later use? This will require you to manage the data chunks, in addition to peeking at the current position.

The ideal interface is something like a separate process which collects the data in parallel and you can query for the current X and Y position while the data is also being stored. Which eyetracker is it and does it have an API you can use instead of having to deal with analog voltages? Analog output was common with scleral eye coils and then infrared trackers kept the same analog X Y output to be compatible with the then existing software tools like NIH’s Cortex… APIs for eyetrackers like the Tobii or Eyelink allow you to peek at the data at any time point while still saving the data stream, and this makes experiments much easier to program…

Dear Dr. Andolina,

Thank you for your kind reply.

If you put a tic and toc around the DaqAIn command what time does it take to

It takes 31 ms to read out analogue input for one DaqAin process and 62 ms for two, which leads to16 samples per second. That adds up. However, this sampling rate is too slow for control of eye movements. Is there any way to increase the sampling frequency (for example, how about using AD board like PCI-DAS 1602 instead of USB-1208FS)?

You could try using the streaming mode but with the ability to read, at least
according to the help

I tried the ‘DaqAInScan family’ to read out the analogue input and obtained eye position data in the ‘data’. The script below is a part of the Matlab code for presentation of the fixation target and collecting the eye position. In the script, I expected that DaqAInScan can collect the eye position for 3 s (2 s during the fixation target presentation and 1 s during the post fixation target), however, the ‘data’ contains eye position only for 1 s. The minimum of the time stamp of the eye position (params.times) is almost the same as T1 in the script, but the maximum is 2 s earlier than T4. What is the proper usage of the DaqAInScan family?

% Setting the options = [ 8 9 ]; % 8: x coordinate, 9: y coordinate
options.range = [ 1 1 ];
options.f = 1000; % sampling frequency
options.immediate = 1;

% Fixation target on and start collecting the eye position
Screen( ‘FillRect’, win, allColors, allRects );
Screen( ‘Flip’, win );

DaqAInScanBegin( DaqID, options );
T1 = GetSecs; T2 = GetSecs;
while ( T2 - T1 ) < 2
DaqAInScanContinue( DaqID, options );
T2 = GetSecs;

% Fixation target off
Screen( ‘FillRect’, win, [ 0 0 0 ], [ 0 0 1920 1080 ] );
Screen( ‘Flip’, win );

% post fixation target
T3 = GetSecs; T4 = GetSecs;
while ( T4 - T3 ) < 1
DaqAInScanContinue( DaqID, options );
T4 = GetSecs;

% Stop collecting the eye position
[ data, params ] = DaqAInScanEnd( DaqID, options );

Which eyetracker is it and does it have an API you can use instead of having to deal
with analog voltages?

My eye tracker is not internationally famous system like ISCAN and tobii Pro. Unfortunately, the function of the tracker is just to collect eye position and has no API. I chose it for financial reasons – the sampling rate (500 Hz) is rather high for the price. I had been a user of NHI’s Cortex, and switched to Psychtoolbox three years ago, because it was difficult to find a computer with ISA slot (my Cortex worked on DOS). In retrospect, Cortex was an excellent device for neurophysiology.


your license key is not yet activated, which could mean an error on our side, or more likely that your payment has not been received yet. If you paid by classic invoice + wire transfer, instead of faster electronic cash (credit card etc.), it could take weeks to activate. Therefore i’ll be brief until the key is authorized:

Ian is right that you won’t ever be able to achieve 500 Hz sampling with DaqAIn, the overhead for the USB data transactions is too high. DaqAInScanBegin/Continue/End is the way - streaming the data from device to host (instead of sending a request for each single sample to the device, then waiting for the response, which is what DaqAIn would do).

I think the optimal options for your case would be:

options.f = 500; % 500 SPS per channel. = [ 8 9 ]; % 8: x coordinate, 9: y coordinate
options.range = [ 1 1 ]; % for Gain 2x (+-10 V)
options.count=inf; % Collect data until stopped. Otherwise defaults to max 1000 samples.

Your while loop which uses DaqAInScanContinue( DaqID, options ); could use then
[params, data] = DaqAInScanContinue( DaqID, options, 1);. This will return data as it becomes available in ‘data’, so you can do your fixation checks against returned data. Usually this will return not just one sample, but multiple, so you’ll have to iterate over the data. Normally the DAQ will only send data in packs of 62 Bytes / 2 Bytes/sample/channel = 31 / 2 channels = 16 samples / 500 Hz ~ 32 msecs of data, so it will lump together multiple samples. If you wanted lowest latency you could also set options.immediate=1 to get each sample from each channel immediately, however that might need more care in your code, and it will apparently (according to help DaqAInScan) peak achievable sampling rate to about 1000 SPS, maybe more, maybe less.

As a few more remarks: The returned params.times timestamp vector is considered to be unreliable, as it returns GetSecs() timestamps of when the data was received by PTB from the operating system, ie. these are not hardware timestamps of actual data collection!

There’s also options.nodiscard = 1 to prevent filtering out of reports which have a gap in their data: Each report has a serial number. By default only reports up to the first gap in serial numbers are returned, the rest is dropped, with some warning message. See help DaqAInScan.

Also, you can set options.sendChannelRange=0 after the initial DaqAInScanBegin command, and before the first DaqAInScanContinue, to reduce overhead - this loads the channel gain ranges only once into the device per session, instead of at each invocation.

As a Google search shows, many (including some of the PCI-DAS 1602, e.g., PCI-DAS 1602/16) cards are supported on Linux, either by MCC itself with their own drivers, or 3rd party driver, or by the Linux kernels builtin Comedi open-source DAQ control framework.


Psychtoolbox does not have builtin drivers to access these, and generally could have a much better I/O driver written, but that is a project that would only be possible if somebody would contract us for it, or the > 99% of the PTB user community would stop free-riding and actually contribute significant amounts of money.

There may be some rudimentary Comedi drivers somewhere on MW file exchange though if you search for Comedi and Matlab and Linux long enough. Possibly this:

This response took 1.45 hours to write, with only 30 minutes presumably paid for, so as is i donated 1.15 hours of my time to you. For any further paid support from myself, you’d need to buy at least 2 extra support hour licenses (1 hour to compensate for my already spent time, another 1 hour to pay for up to 45 minutes of more time for giving advice, with no guarantee of success - you pay for my time, not for a successful outcome) at a price of 2*300 = 600 Euros + sales tax under this link:

Advice would resume after both your current license key is activated and payment for the 2 extra hour packages would be received.

Have a good weekend,

[All paid support for this issue and auth token exhausted at 105 minutes of 30 minutes paid].

Dear Dr. Kleiner,

I thank you for your useful reply. I will check the payment of my licanse next week.



Dear Dr. Kleiner,

I have checked the payment of my license. I paid the yearly license fee through a bank transfer on 17th May 2022 and obtained an invoice. The order number is 2ZAAPNME. I confirmed that USD 156.33 was withdrawn from my bank account on 11th July 2022. I have never received any unsettled bill about this.

The invoice also says that the date of service performance is 16th May 2023. So, I thought that I can post a question on the Forum until the date of expiration. Is my understanding incorrect?



Not a Dr., just a Dipl. inf. in computer science. I did almost 10 years of all the work for a PhD in computer science, but sacrificed it about a 3/4 year before the goal, to save Psychtoolbox from demise ten years ago, trusting that a significant fraction of our ten-thousands of users would do what they stated they would - support us financially, so I can make a living out of developing, maintaining and supporting it. Probably one of the bigger mistakes in my life, seeing that less than 1% of our users contribute…

Thanks for checking this, and for actually supporting us! Looks somebody somewhere on the sales side made a mistake. I’ll notify our sales person to correct the mistake in our database.

Yes, you get up to 30 minutes of my work time for answering such questions, in total. Now you got 105 minutes instead of the 30 minutes, so your license is exhausted. But if you needed further advice on this issue, you’d receive the - normally 300 Euros/hours - extra hours package at a 25% discount, if you needed them. We’ll have to check what went wrong here.


Ok, the missing license key was an accounting mistake on our side, now cleared up.
Thanks for supporting us,

Dear Mario,

Thank you for letting me know about my license status. Since I have some questions about programming in PTB, I will use the paid support when my research fund of the next fiscal year is available.



Dear Mario,

Thank you for your kind reply on Jan 21th in this forum. After that, I tested the
DaqAInScan/Continue/End to read out analogue signal from the DAQ (USB-1208FS,
Measurement Computing). The following is the Matlab script:

PsychDefaultSetup( 2 );

DaqID = DaqFind;

% Define options = [ 8 9 ]; % 8: horizontal, 9: vertical position of the eye
options.range = [ 1 1 ];
options.f = 250;
options.immediate = 1;

warning off

array_H = ;
array_V = ;
array_T = ;

% Start collecting eye position
DaqAInScanBegin( DaqID, options );

T1 = GetSecs; T2 = GetSecs;
while ( T2 - T1 ) < 1
[ params, data ] = DaqAInScanContinue( DaqID, options, 1 );
if ~isempty( data )
for i = 1:length( data )
array_H = [ array_H; data( i, 1 ) ];
array_V = [ array_V; data( i, 2 ) ];
array_T = [ array_T; GetSecs ];
T2 = GetSecs;

    array_H = array_H; 
    array_V = array_V; 
    array_T = array_T; 
    T2 = GetSecs;


% Stop collecting eye position
DaqAInScanEnd( DaqID, options );

% Draw a graph of the eye position
plot( array_T, array_H, ‘r.-’ ); hold on
plot( array_T, array_V, ‘g.-’ ); hold off

The data was collected by using a static dummy eye. The obtained position was plotted in the
attached figure (eye_position_DAQ1). The red and green line indicates the horizontal and
vertical position, respectively. As you can see there, I could read out about 250 points per second with
the DaqAInScan/Continue/End. However, it seems that the horizontal and vertical position switches
randomly when a new chunk is read out (32 chunks per second). I tested with the other two DAQs and
obtained the similar result (attached figure ‘eye_position_DAQ2’ and ‘eye_position_DAQ3’).
The output signal from the eye tracker was bifurcated and monitored by using PowerLab
(ADInstruments), but the horizontal and vertical position did not switch during the whole test.
So, my questions are:

  1. How can I stop switching the horizontal and vertical position, or are there any mistakes in my
    usage of the DaqAInScan/Continue/End?
  2. During the test, I got the following message lines regardless of the statement of warning off:
    Missing 992.5 sample/channel, 3970 bytes.
    Missing 992.0 sample/channel, 3968 bytes.
    Nothing received.
    How can I stop the message lines?

I would be appreciated if you could give me any suggestion.




Hi, I haven’t read your latest post at all, as your paid support membership is not covering this anymore. You’d need to buy 2 hours of the “extra hours paid support package”, first to back-pay for the 1.15 hours of past, not yet paid for, support, and then for up to 45 minutes more of my time to deal with your new questions. Additional hours if those 45 minutes wouldn’t cut it. Due to the long break since your last response, I lost all context wrt. this issue and may have to reread some of the previous discussion, which will count towards the 45 minutes of additional paid work time. You are eligible for a 25% discount on the “300 Euros per hour + potential sales tax” offer, I’ll send you instructions via private forum message, in case you want this paid extra support.

Update for side-watchers:

After some investigation and testing by Daq, the problem turned out to be an eleven years old bug in DaqAInScan.m which caused the problems and caused such channel switching as soon as more than 1 analog input channel was used and DaqAInScanContinue was used requesting live data by setting the optional ‘uselivedata’ flag in DaqAInScanContinue(). Apparently not many people used the function in this way. The problem has been fixed by myself, verified by our paying user Daq, and the bug fix is part of PTB release

A few more remarks I already gave to our paying user, just copied here for completeness / benefit to others:

options.immediate=1 may be too inefficient / cause lots of extra overhead / slowdown. But options.immediate=0 will receive chunks of ~15-16 gaze samples at once, so timing granularity would be around 32 msecs, probably good enough.
options.count if not specified will default to options.count = 1000, iow. record at most 1000 samples in total ~ 4 seconds at 250 SPS or ~2 seconds at 500 SPS from time of DaqAInScanBegin(). This may not be what you want, so options.count = Inf might be a better choice. array_T won’t collect meaningful timestamps, nothing will. And the
for i = 1:length(data) loop could likely be replaced by simply directly concatenating, ie.

if ~isempty(data)
  array_H = [ array_H; data( :, 1 ) ];
  array_V = [ array_V; data( :, 2 ) ];
T2 = GetSecs;

Dear Mario,

Thank you for your reply on May 23. I have payed for the extra 3 hours on May 24. If you cannot confirm it, please let me know.

May I ask other questions about the payed support?

  1. Could you issue a receipt of the payment for the renewal of yearly license? Administration office of my institute told me that it is better to have it for the process to pay the annual fee from my research fund.

  2. Is it possible to change my status of the payed support from “Yearly” to “One-time payment” (I took a look at the website, but could not find how to do it)? The annual fee is once payed from my personal bank account, and is supposed to be compensated from my research fund later. The administration office told me that automatic withdrawal is undesirable for the series of the process.

I am sorry for bothering you by the mean rules of my institute.

Many Thanks,



thanks for your ongoing support!

I’m not (supposed to be) involved in the actual sales handling / online shop part at all. Technically you bought the yearly license from our reseller Digistore24, so they deal with this payment handling. I can only tell you here what I gathered from a quick search of their webpage and their help pages, no guarantee I got this right.

Wrt. 1. I think you should have received an invoice from them when you bought the license last year, noting the yearly recurring payment plan. You could contact their customer support wrt. additional requests. If you had a one-time payment method, you’d have to manually buy a new license every 12 months and would get a new invoice for that license. I think is the right e-mail with questions for this.

Wrt. 2. I don’t know if it is possible to change the status. But you can cancel the yearly subscription, so it won’t rebill next year, and then just manually buy a new one-time license next May 2024 after expiry of your old one, if you want to.

  • The order confirmation mail or invoice you got when originally buying the license probably has some link or button in it, that allows cancelling the subscription or maybe changing the payment options. might also be able to do this if you don’t have that info anymore. I assume this would only cancel the renewal in the next year, not your already bought license.

  • If this doesn’t work out, would allow you to contact our own actual “sales person”, responsible for all things related to the Digistore online shop, who should be able to somehow manually cancel your subscription for you, although that might be the slower route.

  • If you cancel the yearly renewing license it is important to keep the old license key etc. around, and to buy a new one time license within at most 1 month after expiration of the old license, so if you’d need support for something past mid-May 2024, I could associate your new one-time license with your old yearly license. Otherwise you’d lose the 40% discount on extra work hours you earned so far.

  • Also note that our prices for one-time memberships have increased recently from 150 to 200 Euros + tax, so I think the future one-time purchase will be more expensive compared to past offers.


Dear Mario,

Thank you for your reply on May 26. I will contact to the Digistore24 for more details.
I really appreciate all your supports on my questions thus far.

Many Thanks,