The PanelsController class

Running the G4 Host software initiates the IO card of the system to start a TCP/IP server on the localhost at port 62222. Through this TCP/IP connection, it is possible to communicate directly with the arena. Here we list the commands available in PanelsController which would allow you to implement the same functionality in a language of your choosing. For simplicity we document both, MATLAB’s PanelsController class as well as the underlying TCP/IP data exchange. For the TCP/IP we use python as an example.

The TCP/IP commands follow a common structure: the first byte represents the length of package following the length command and the second byte is a command ID (Stream frame and Change root directory are notably different, they start with the command IDs followed by two bytes representing the length of the packet). This means, a two-byte TCP/IP command consists of a length of 1 and the command ID, a three-byte command would start with a length of 2, the command ID, and a value.

Initiate the connection

Once the G4 Host application is running, you should be able to open a TCP/IP connection to port 62222 on the local machine.

import socket

HOST = 'localhost'
PORT = 62222

with socket.socket(socket.AF_INET, socket.SOCK_STREAM) as s:
    try:
        s.connect((HOST, PORT))
    except ConnectionRefusedError:
        print(f"G4 Host doesn't appear to be running on {HOST}:{PORT}")
    # … commands follow here

The code above establishes a tcp connection similar to what creating an instance of the matlab PanelsController does:

ctlr = PanelsController();
ctlr.open(true);

The PanelsController is a class that handles the connection on the TCP/IP level. It contains many methods which will handle sending the TCP commands for you.

Turn all panels on

This command turns all panels on to the brightest level. If the arena is already on, you should not see a change. Use this for checking if your arena works.

This is a two-byte command (notation in hexadecimal): length is set to 0x01 (1 in integer, 0b00000001 in binary), the command is 0xff (255 in decimal, 0b11111111 in binary). The following paragraphs will only use hexadecimal notation of the bytes.

    # … initiate the connection (see above)
    s.sendall(b'\x01\xff')

The corresponding MATLAB code:

% … initiate the connection (see above)
ctlr.allOn();

Turn all panels off

This command turns all panels off. If the arena is already turned off, there will be no change.

This is a two-byte command with the length 0x01 and the command 0x00.

    # … initiate the connection (see above)
    s.sendall(b'\x01\x00')

The corresponding MATLAB code:

% … initiate the connection (see above)
ctlr.allOff();

Stop Display

This command deactivates the display.

This is a two-byte command with the length 0x01 and the command 0x30.

    # … initiate the connection (see above)
    s.sendall(b'\x01\x30')

The corresponding MATLAB code:

% … initiate the connection (see above)
ctlr.stopDisplay();

Reset Display

Reset the display. All panels will be off.

This is a two-byte command with the length byte 0x01 and the command 0x01.

    # … initiate the connection (see above)
    s.sendall(b'\x01\x01')

The corresponding MATLAB code:

% … initiate the connection (see above)
ctlr.sendDisplayReset();

Reset Panel

Reset a single panel.

This is a three-byte command with the length 0x02, the command 0x01, and the third byte representing the address of the panel.

    # … initiate the connection (see above)
    panel_number = 2
    s.sendall(b'\x01\x01' + bytes([panel_number]))

The corresponding MATLAB code:

% … This is not implemented in PanelsController

Controller reset

This is a two-byte command with the length 0x01 and the command 0x60.

    # … initiate the connection (see above)
    s.sendall(b'\x01\x60')

The corresponding MATLAB code:

% … This is not implemented in PanelsController

Get Version

This is a two-byte command with the length 0x01 and the command 0x46. It returns the version and Panel_com logs this to the error log file.

    # … initiate the connection (see above)
    s.sendall(b'\x01\x46')
    # … add code to read data, eg:
    rsp = s.recv(1024)

The corresponding MATLAB code:

% … initiate the connection (see above)
ctlr.getVersion();

Reset Counter

This is a two-byte command with the length 0x01 and the command 0x42.

Note: This command is currently not used. TODO: Explain usage.

    # … initiate the connection (see above)
    s.sendall(b'\x01\x42')

The corresponding MATLAB code:

% … initiate the connection (see above)
ctlr.resetCounter();

Request Treadmill Data

This is a two-byte command with the length 0x01 and the command 0x45.

Note: This command is currently not used. TODO: Explain usage.

    # … initiate the connection (see above)
    s.sendall(b'\x01\x45')

The corresponding MATLAB code:

% … This is not implemented in PanelsController

Update GUI Info

This is a two-byte command with the length 0x01 and the command 0x19.

Note: This is supposed to update gain, offset, and position information (according to code comments). TODO: Explain usage.

    # … initiate the connection (see above)
    s.sendall(b'\x01\x19')

The corresponding MATLAB code:

% … This is not implemented in PanelsController

Start Log

Sending this command starts the logging process. The data is logged directly from the IO card and into the TDMS file format (see data handling).

This is a two-byte command with the length 0x01 and the command 0x41.

    # … initiate the connection (see above)
    s.sendall(b'\x01\x41')

The corresponding MATLAB code:

% … initiate the connection (see above)
ctlr.startLog();

Stop Log

Sending this command stops the logging process.

Note: This command seems to respond slowly. The MATLAB code has some fallback and potential manual intervention: if the stop_log fails, MATLAB tries again after 100ms. If that still fails, a popup window asks the user to manually stop the log file. If you use the TCP/IP command, try to use a similar strategy.

This is a two-byte command with the length 0x01 and command 0x40.

    # … initiate the connection (see above)
    s.sendall(b'\x01\x40')

The corresponding MATLAB code:

% … initiate the connection (see above)
ctlr.stopLog();

Set Control Mode

This defines the display mode and requires an argument between 0 and 7.

This is a three-byte command with the length 0x02, the command 0x10, and the third byte with the desired control mode. The MATLAB code sends the command in blocking mode, so you might want to set the MSG_WAITALL flag in the sendall call.

    # … initiate the connection (see above)
    mode = 1
    assert 0 <= mode <= 7, "display mode outside allowed range 0…7"
    s.sendall(b'\x02\x10' + bytes([mode]))

The corresponding MATLAB code:

% … initiate the connection (see above)
mode = 1;
ctlr.setControlMode(mode);

Set active Analog Output Channels

This command activates or deactivates the four possible output channels. The channels are encoded as binary flags, channel 0 for the lowest bit, channel 1 for the second etc… Channel 0 would therefore be 0b00000001, channels 1 and 3 would be 0b00001010.

This is a three-byte command with the length 0x02, the command 0x11, and the value in the third byte representing the required channels.

    # … initiate the connection (see above)
    ao_channels = 0b0000_0101 # Channel 0 and 2
    assert 0 <= ao_channels < 16, 
        "Wrong AO modes selected. Only 0b0000_0000 to 0b00001111 is allowed."
    s.sendall(b'\x02\x10' + bytes([ao_channels]))

The corresponding MATLAB code:

% … initiate the connection (see above)
ctlr.setActiveAOChannels(0101); % Only last 4 bits required.

Stream channels

The command is used to set the analog input channels.

This is a three-byte command with the length 0x02, the command 0x13, and the value in the third byte representing the active channels.

Note: TODO: The exact usage is unclear.

    # … initiate the connection (see above)
    channels = 1 # unclear what this represents
    s.sendall(b'\x02\x13' + bytes([channels]))

The corresponding MATLAB code:

% … This is not yet implemented in PanelsController

Set Pattern ID

Displays the pattern with the specified ID on the panels.

This is a four-byte command with the length 0x03, the command 0x03, and the third and fourth byte defining the address (in little endian).

    # … initiate the connection (see above)
    pattern_id = b'\x05\x05'
    s.sendall(b'\x03\x03' + pattern_id)

The corresponding MATLAB code:

% … initiate the connection (see above)
patID = 1;
ctlr.setPatternID(patID); 

Set Pattern Function ID

Use a the function with the specified ID.

This is a four-byte command with the length 0x03 and the command 0x15. The use of the third and fourth byte are currently unverified, but most likely they refer to an ID just like the pattern ID.

TODO: Verify use of function ID.

    # … initiate the connection (see above)
    func_pattern_id = b'\x02\x07'
    s.sendall(b'\x03\x15' + func_pattern_id)

The corresponding MATLAB code:

% … initiate the connection (see above)
funcID = 1;
ctlr.setPatternFunctionID(funcID); 

Start Display

Start Display tells the arena to show the currently configured function and pattern for a specified time. The time in the TCP/IP command is specified in tenth of a second.

This is a four-byte command with the length 0x03, the command 0x21 and the third and fourth representing the time.

    # … initiate the connection (see above)
    duration = 6 * 10  # 60 deciseconds = 6 seconds
    s.sendall(b'\x03\x15' + duration.to_bytes(2, 'little') )

The corresponding MATLAB code (internal conversion from second to decisecond):

% … initiate the connection (see above)
dur = 6;
ctlr.startDisplay(dur*10); 

Set Framerate

This four-byte command has a length of 0x03, command of 0x12, and sends a little-endian encoded two-byte framerate.

TODO: Verify that the framerate is in Hz and only accepts integer numbers? Add description.

    # … initiate the connection (see above)
    fps = 500
    s.sendall(b'\x03\x12' + fps.to_bytes(2, 'little') )

The corresponding MATLAB code:

% … initiate the connection (see above)
ctlr.setFrameRate(500); 

Set Position X

This four-byte command requires the length byte as 0x03, the command as 0x70, and has a little-endian encoded two-byte value for the X-position.

    # … initiate the connection (see above)
    pos_x = 17
    s.sendall(b'\x03\x70' + pos_x.to_bytes(2, 'little') )

The corresponding MATLAB code:

% … initiate the connection (see above)
ctlr.setPositionX(17); 

Set Position Y

This four-byte command requires the length byte as 0x03, the command as 0x71, and has a little-endian encoded two-byte value for the Y-position.

    # … initiate the connection (see above)
    pos_y = 10
    s.sendall(b'\x03\x71' + pos_y.to_bytes(2, 'little') )

The corresponding MATLAB code:

% … initiate the connection (see above)
ctlr.setPositionY(10); 

Set Analog Output Function ID

This five-byte command requires the length byte as 0x04, the command as 0x31. The third byte represents the analog output channel, a value between 0 and 3. The fourth and fifth byte is the little-endian encoded ID of the function.

    # … initiate the connection (see above)
    chan = 1 # 0…3
    index = 23
    assert 0 <= chan  <= 3, "channel outside range"
    s.sendall(b'\x04\x31' + bytes([chan]) + index.to_bytes(1, 'little'))

The corresponding MATLAB code:

% … initiate the connection (see above)
channel = 1;
ID = 23;
ctlr.setAOFunctionID(channel, ID); 

Set Analog Output

Set the voltage of a channel to a specified value.

The voltage of a analog output channel is set through two different five-byte TCP/IP commands: the length is 0x04 in both cases, for negative voltages the command is 0x11 and for positive voltages 0x10. Bytes 4 and 5 encode the actual voltage in a little-endian encoded 16bit integer value. According to the documentation, the maximum voltage of 10V is mapped to 32767 (although the split into two functions would allow higher values for the maximum). In MATLAB, both functions are mapped to the set_ao function.

    # … initiate the connection (see above)
    chan = 1 # 0…3
    voltage = 32767 # -32767…0…32767 → -10V…0V…10V
    assert 0 <= chan  <= 3, "channel outside range"
    assert -32767 <= voltage <= 32767, "voltage outside range"
    if voltage < 0:
        s.sendall(b'\x04\x11' + bytes([chan]) + abs(voltage).to_bytes(2, 'little'))
    else:
        s.sendall(b'\x04\x10' + bytes([chan]) + voltage.to_bytes(2, 'little'))

The corresponding MATLAB code:

% … initiate the connection (see above)
channel = 1;
voltage = 32767;
ctlr.setAO(channel, voltage); 

Set Gain Bias

This six-byte command has a length of 0x05 and a command byte of 0x01. Bytes 3 and 4 are a little-endian representation of the signed gain value, bytes 5 and 6 of the signed bias value.

    # … initiate the connection (see above)
    gain_x = 100
    bias_x = 200
    s.sendall(b'\x05\x01' +
        gain_x.to_bytes(2, 'little', signed=True) +
        bias_x.to_bytes(2, 'little', signed=True))

The corresponding MATLAB code:

% … initiate the connection (see above)
gain = 100;
bias = 100;
ctlr.setGain(gain, bias); 

Set Pattern and Position Function

The six-byte command has a length of 0x05 and uses command 0x05. Bytes 3 and 4 are used for a little-endian representation of the pattern ID, 5 and 6 of a function ID.

    # … initiate the connection (see above)
    pattern = 5
    position = 37
    s.sendall(b'\x05\x05' +
        pattern.to_bytes(2, 'little') +
        position.to_bytes(2, 'little'))

The corresponding MATLAB code:

% … This is not implemented in PanelsController

Stream Frame

Stream a full frame to the panels.

This command has a variable length and starts with the first byte 0x32. Bytes 2 and 3 represent the length of the data, which is the length of the data minus seven bytes for the header.

TODO: Clarify if x_ao and y_ao specify a value or (more likely) the ID of a function.

    # … initiate the connection (see above)
    frame_content = b'…' # This is the actual content of the frame
    x_ao = 0
    y_ao = 0
    data_length = len(frame_content)
    s.sendall(b'\x32' +
        data_length.to_bytes(2, 'little'),
        x_ao.to_bytes(2, 'little', signed=True) +
        y_ao.to_bytes(2, 'little', signed=True) +
        frame_content)

The corresponding MATLAB code:

% … This is not yet implemented in PanelsController

Change Root Directory

Set the root directory where pattern and functions are stored.

This is a variable length command with the first byte 0x43. Bytes 2 and 3 define the length of the directory name and the following bytes contain the directory name.

    # … initiate the connection (see above)
    root_directory_name = 'C:\my path to the patterns' # actual path
    root_dir = root_directory_name.encode('utf-8')
    dir_length = len(root_dir)
    s.sendall(b'\x43' +
        dir_length.to_bytes(2, 'little'),
        root_dir)

The corresponding MATLAB code:

% … initiate the connection (see above)
ctlr.setRootDirectory("C:\\my path to the patterns");

Combined Command

This 19-byte command sends several settings at once. The length is set as 0x12 (=18) and the command ID is 0x07. The third byte represents the display mode and the following pairs of bytes represents IDs of pattern, function, and the four AO functions. This is followed by two bytes for the frame rate and two bytes for the duration in tenth of a second.

Info: This command is relatively new and mostly untested. The G4_run_protocol_combinedCommand.m used this, but apparently there were some timing issues. Needs more exploration.

    # … initiate the connection (see above)
    display_mode = 1
    pattern_id = 27
    function_id = 11
    ao1_id = 25
    ao2_id = 0
    ao3_id = 1512
    ao4_id = 0
    fps = 500
    duration = 6 * 10 # in deciseconds
    s.sendall(b'\x12\x07' +
        display_mode.to_bytes(1, 'little') + 
        pattern_id.to_bytes(2, 'little') +
        function_id.to_bytes(2, 'little') +
        ao1_id.to_bytes(2, 'little') +
        ao2_id.to_bytes(2, 'little') +
        ao3_id.to_bytes(2, 'little') +
        ao4_id.to_bytes(2, 'little') +
        fps.to_bytes(2, 'little') +
        duration.to_bytes(2, 'little')
        )

The corresponding MATLAB code:

% … initiate the connection (see above)
mode = 1;
patternID = 27;
functionID = 11;
ao0ID = 0;
ao1ID = 15;
ao2ID = 0;
ao3ID = 12;
fps = 500;
deciSeconds = 6;
ctlr.combinedCommand(mode, patternID, functionID, ao0ID, ao1ID, ao2ID, ao3ID, fps, deciSeconds);

Set panels controller parameters

Additionally, in PanelsController there is a command with which to set all parameters at once.

The MATLAB code:

% … initiate the connection (see above)
p = {mode, patternID, gain, offset, functionID, fps, frameIndex, activeAOchannels, aoIndices};
ctlr.setControllerParameters(p);