PROPixx Demo 2 – Drawing dots at 1440 Hz

This demo is a simple demonstration of the PROPixx’s 1440 Hz sequencer. We display twelve white dots around the center of the screen, cycling at a rate of 1440 Hz. The demo ends when a key is pressed.

Currently, there is no graphics card capable of drawing individual frames at this refresh rate. To achieve this rate, the PROPixx uses a “sequencer” to break up a single 1920 x 1080, 120 Hz RGB video signal into 12 multiple frames displayed in sequence. First, the 1920 x 1080 image is divided into four 960 x 540 images, or “quadrants”, which are magnified to full screen. Next, each 8-bit RGB color channel is converted to grayscale and displayed separately. The order of appearance is Q1 red, Q2 red, Q3 red, Q4 red, and then this order is repeated for G and B channels.

_images/Quad12xStill.png

To create our stimuli, we determine target locations as if they were full resolution, full screen. The helper script convertToQuadrant reassigns and rescales the target positon to the correct quadrant. Depending on the frame, we draw our targets as either full red, full green or full blue.

A screen running in 120 Hz mode will display the twelve dots on the same frame, in the correct quadrant and with the correct color profile. Here is a demonstration, albeit slowed down:

_images/1440HzDemoFullHD.gif

The PROPixx in 1440 Hz mode will display the four quadrants in sequence, starting with red and repeating for green and blue. The image will be full screen, grayscale:

_images/1440HzDemo.gif
  1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
function PPxDraw1440HzDots
%This function draws a circle of 12 dots refreshing at 1440Hz. 

% First, we construct a single 1920 x 1080 RGB image, which is passed to
% the PROPixx. The sequencer breaks the image down and shows the quadrants
% and colour channels as 12 individual frames. These frames are 960 x 540
% resolution, shown full screen:
%_______________________________
%|             |                |           
%|     Q1      |       Q2       | 
%|             |                | 
%|_____________|________________|   
%|             |                |         
%|     Q3      |       Q4       |          
%|             |                |          
%|_____________|________________|          


%Quadrants are shown FULL SCREEN, GREYSCALE in the order: 
% Quadrant 1 red channel
% Quadrant 2 red channel
% Quadrant 3 red channel
% Quadrant 4 red channel
% Quadrant 1 green channel
% Quadrant 2 green channel
% Quadrant 3 green channel
% Quadrant 4 green channel
% Quadrant 1 blue channel
% Quadrant 2 blue channel
% Quadrant 3 blue channel
% Quadrant 4 blue channel

%In this demo we draw dots as if they were full screen. The helper function
%'convertToQuadrant' reassigns the positon of the dot (based on full
%display) to the correct quadrant.

% 2020 Mar 18       lef     written
% 2020 Mar 26       lef     revised

%Check connection and open Datapixx if it's not open yet
isConnected = Datapixx('isReady');
if ~isConnected
    Datapixx('Open');
end

Datapixx('SetPropixxDlpSequenceProgram', 5);        %Set Propixx to 1440Hz refresh (also known as Quad12x)
Datapixx('RegWrRd');                                %Push command to device register

%Open a display on the Propixx
AssertOpenGL;
KbName('UnifyKeyNames')
Screen('Preference', 'SkipSyncTests', 1);
screenID = 2;                                        %Change this value to change display
[windowPtr,rect] = Screen('OpenWindow', screenID, 0);

%Set up some stimulus characteristics
dotRadius = 15;
targetRadius = 200; 
center = [rect(3)/2, rect(4)/2];

%Let's define the 12 locations around a circle where we want to draw our
%stimuli
position = [-180:30:180];
locations = nan(12,2);
for k=1:12
    angle = position(k);
    locations(k, 1) = center(1) + targetRadius * cos(-angle*pi/180);
    locations(k, 2) = center(2) + targetRadius * sin(-angle*pi/180);
end
            
%Start displaying dots
while 1 
    
    %DRAW RED
    %Q1 red
    quadrant = 1;
    colour = [255,0,0];
    position = locations(1,:);
    [x,y] = convertToQuadrant(position, rect, quadrant); 
    Screen('FillOval', windowPtr, colour, [x-dotRadius, y-dotRadius, x+dotRadius, y+dotRadius]);
    
    %Q2 red
    quadrant = 2;
    colour = [255,0,0];
    position = locations(2,:);
    [x,y] = convertToQuadrant(position, rect, quadrant); 
    Screen('FillOval', windowPtr, colour, [x-dotRadius, y-dotRadius, x+dotRadius, y+dotRadius]);
    
    %Q3 red
    quadrant = 3;
    colour = [255,0,0];
    position = locations(3,:);
    [x,y] = convertToQuadrant(position, rect, quadrant); 
    Screen('FillOval', windowPtr, colour, [x-dotRadius, y-dotRadius, x+dotRadius, y+dotRadius]);
    
    %Q4 red
    quadrant = 4;
    colour = [255,0,0];
    position = locations(4,:);
    [x,y] = convertToQuadrant(position, rect, quadrant); 
    Screen('FillOval', windowPtr, colour, [x-dotRadius, y-dotRadius, x+dotRadius, y+dotRadius]);
    
    
    %DRAW GREEN
    %Q1 green
    quadrant = 1;
    colour = [0,255,0];
    position = locations(5,:);
    [x,y] = convertToQuadrant(position, rect, quadrant);  
    Screen('FillOval', windowPtr, colour, [x-dotRadius, y-dotRadius, x+dotRadius, y+dotRadius]);
    
    %Q2 green
    quadrant = 2;
    colour = [0,255,0];
    position = locations(6,:);
    [x,y] = convertToQuadrant(position, rect, quadrant); 
    Screen('FillOval', windowPtr, colour, [x-dotRadius, y-dotRadius, x+dotRadius, y+dotRadius]);
    
    %Q3 green
    quadrant = 3;
    colour = [0,255,0];
    position = locations(7,:);
    [x,y] = convertToQuadrant(position, rect, quadrant); 
    Screen('FillOval', windowPtr, colour, [x-dotRadius, y-dotRadius, x+dotRadius, y+dotRadius]);
    
    %Q4 green
    quadrant = 4;
    colour = [0,255,0];
    position = locations(8,:);
    [x,y] = convertToQuadrant(position, rect, quadrant); 
    Screen('FillOval', windowPtr, colour, [x-dotRadius, y-dotRadius, x+dotRadius, y+dotRadius]);
    
    
    %DRAW BLUE    
    %Q1 blue
    quadrant = 1;
    colour = [0,0,255];
    position = locations(9,:);
    [x,y] = convertToQuadrant(position, rect, quadrant); 
    Screen('FillOval', windowPtr, colour, [x-dotRadius, y-dotRadius, x+dotRadius, y+dotRadius]);
    
    %Q2 blue
    quadrant = 2;
    colour = [0,0,255];
    position = locations(10,:);
    [x,y] = convertToQuadrant(position, rect, quadrant); 
    Screen('FillOval', windowPtr, colour, [x-dotRadius, y-dotRadius, x+dotRadius, y+dotRadius]);
    
    %Q3 blue
    quadrant = 3;
    colour = [0,0,255];
    position = locations(11,:);
    [x,y] = convertToQuadrant(position, rect, quadrant); 
    Screen('FillOval', windowPtr, colour, [x-dotRadius, y-dotRadius, x+dotRadius, y+dotRadius]);
    
    %Q4 blue
    quadrant = 4;
    colour = [0,0,255];
    position = locations(12,:);
    [x,y] = convertToQuadrant(position, rect, quadrant); 
    Screen('FillOval', windowPtr, colour, [x-dotRadius, y-dotRadius, x+dotRadius, y+dotRadius]);

    
    %Now that we have drawn content to all 12 frames, it is time to flip.
    %The graphics card sends this as a single 1920 x 1080 image at 120 Hz,
    %which the sequencer breaks down into the 12 frames, presented in the
    %order they were drawn.
    Screen('Flip',windowPtr);

    
    %Keypress to exit
    [keyIsDown, ~, ~, ~] = KbCheck;
    if keyIsDown
        break
    end 
end
        
Screen('Closeall');
Datapixx('SetPropixxDlpSequenceProgram', 0);         %Revert to standard 120Hz refresh rate
Datapixx('RegWrRd');
Datapixx('Close');

end

function [x,y] = convertToQuadrant(position, displaySize, quad)
%This scales an x, y position into a specific quadrant of the screen    
scale = 0.5;

switch quad
    case 1; xOffset = 0; yOffset = 0;
    case 2; xOffset = displaySize(3)/2; yOffset = 0; 
    case 3; xOffset = 0; yOffset = displaySize(4)/2;
    case 4; xOffset = displaySize(3)/2; yOffset = displaySize(4)/2;
end
    
x = (position(1)*scale)+xOffset;
y = (position(2)*scale)+yOffset;

end