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.
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:
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:
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
|