Skip to main content
Skip table of contents

Display Management System (DMS) Support

DMS_Blend_Example.png

Overview

Display Management System (DMS) is a screen warp, blend, and color correction interface from FlightSafety. DMS provides calibration data to Nova to conform the output from projectors to the screen being used in the simulation. It uses the Nova internal calibration configuration along with specified warp ".tif" files to render the warping. In addition, the DMS system allows for runtime changes to the warping of the system via commands sent directly to the DMS via a port specified within the configuration. 

Setup

DMS requires three tokens to set up the warping configuration which can be located within your cluster.cfg or override configs when running standalone and within your frontend configuration when running a cluster configuration. Below are the tokens required:

Token

Value

Use

CalibrationMode

INTERNAL

Automatic display Calibration/Warping mode specified for rendering. Internal setting represents a DMS configuration.

CalibrationDir

STRING

Directory containing images for Internal calibration

CalibrationConfig  

STRING

Additional per-channel parameters related to display calibration.

 The "CalibrationConfig" configuration file also contains it's own set of configuration tokens detailed below:

NOTE: * denotes the channel number to apply the warping to.

Token

Value(s)

Use

DMS_PORT

INT

Specifies port for receiving commands for runtime warp changes.

CH*_NAME

STRING

Name of the channel to apply warping to.

CH*_ID

INT

Identification number designating how the channel will be indexed internally.

CH*_OUTPUTS

INT

Number of displays being rendered to.

CH*_FBO_SIZE_X

INT

Frame buffer object resolution size along X axis.

CH*_FBO_SIZE_Y

INT

Frame buffer object resolution size along Y axis.

CH*_INVERT_OUT_X

ON/OFF

Invert the FBO output along the X axis.

CH*_INVERT_OUT_Y

ON/OFF

Invert the FBO output along the Y axis.

CH*_RESAMPLE_ONLY

ON/OFF

Enable/disable resampling only.

CH*_AUTO_MIPMAP

ON/OFF

Enable/disable auto mipmap option.

CH*_DISTORTION_MAP_MODE

ABSOLUTE/OFFSET/OFF

Mode specifying the mapping configuration to the FBO.

CH*_DISTORTION_MAP_FILE

INT and STRING

Configure eyepoint and warp file to be used located within the CalibrationMode directory.

CH*_DISTORTION_MAP_WIDTH

INT

Distortion map resolution size along the X axis.

CH*_DISTORTION_MAP_HEIGHT

INT

Distortion map resolution size along the Y axis.

CH*_LUMINANCE_MAP_MODE

DISTORTION_BLUE_CHANNEL/INDEPENDENT/OFF

Mode specifying the luminance configuration to the FBO.

CH*_LUMINANCE_MAP_FILE

INT and STRING

Specifies eyepoint and luminance file to be used located within the CalibrationMode directory.

CH*_LUMINANCE_MAP_WIDTH

INT

Luminance map resolution size along X axis.

CH*_LUMINANCE_MAP_HEIGHT

INT

Luminance map resolution size along Y axis.

CH*_BLACK_LEVEL_MAP_FILE

INT and STRING

Specifies eyepoint and black level file to be used located within the CalibrationMode directory.

CH*_GAMMA_FILE

STRING

Specifies a gamma file to be used.

CH*_DYNAMIC_WARP_UPDATES

ON/OFF

Allow for runtime updates to the warping.

Running DMS and Real-Time Commands

Start Nova with the appropriate configurations found within the setup and run Nova normally and the warping should appear on startup. If issues arise during setup information on configuration is usually printed to the command window.

With DMS there is the capability to make runtime changes to the warping via a TCP connection using the specified port within the configuration specified in the "CalibrationConfig" token. Below is a breakdown of the commands that can be sent/received using this interface:

Communication Protocol

Communication between the alignment system and DMS requires the following structure:

Start

1 byte

Command Code

1 byte

Data Mode

4 bytes

Data Length

4 bytes

Data

N bytes

The reply from a write command is a 32-bit signed integer. Negative values indicate errors. Positive or zero values indicate success.

The reply from a read command takes on the following format:

Data Length

4 bytes

Data

N bytes

Byte order is big-endian.

Command Set

Load Test Pattern

Command

Start

0x53

Command Code

0x1d

Data Mode

0x00

Zero-based channel ID

0x01

0x00

Data Length

Length of data (bytes)

Data

ASCII string containing path/filename of desired test pattern. String terminator is required.

Reply

Data Length

0x00

0x00

0x00

0x04

Data

32-bit signed integer status

Unload Test Pattern

Command

Start

0x53

Command Code

0x1d

Data Mode

0x00

Zero-based channel ID

0x01

0x00

Data Length

0x00

0x00

0x00

0x00

Reply

Data Length

0x00

0x00

0x00

0x04

Data

32-bit signed integer status

Test patterns are tiff images. Test patterns will be warped and color-corrected in the same manner as the rendered image.

Set Test Pattern Illumination

Command

Start

0x53

Command Code

0x24

Data Mode

0x00

Zero-based channel ID

0x69

0x6c

Data Length

0x00

0x00

0x00

0x0C

Data

Red Value

Green Value

Blue Value

RGB Values are single precision floats in the range of 0 to 1

Reply

Data Length

0x00

0x00

0x00

0x04

Data

32-bit signed integer status

Get Test Pattern Illumination

Command

Start

0x53

Command Code

0x23

Data Mode

0x00

Zero-based channel ID

0x69

0x6c

Reply

Data Length

0x00

0x00

0x00

0x0C

Data

Red Value

Green Value

Blue Value

RGB Values are single precision floats in the range of 0 to 1

Set Edge Mask

Command

Start

0x53

Command Code

0x24

Data Mode

0x00

Zero-based channel ID

0x65

0x62

Data Length

0x00

0x00

0x00

0x04

Data

0x00

0x00

0x00

0x00 (disable) or 0x01 (enable)

Reply

Data Length

0x00

0x00

0x00

0x04

Data

32-bit signed integer status

Get Edge Mask State

Command

Start

0x53

Command Code

0x23

Data Mode

0x00

Zero-based channel ID

0x65

0x62

Reply

Data Length

0x00

0x00

0x00

0x04

Data

0x00

0x00

0x00

0x00 (disabled) or 0x01 (enabled)

Set Edge Blends

Command

Start

0x53

Command Code

0x24

Data Mode

0x00

Zero-based channel ID

0x78

0x62

Data Length

0x00

0x00

0x00

0x04

Data

0x00

0x00

0x00

0x00 (disable) or 0x01 (enable)

Reply

Data Length

0x00

0x00

0x00

0x04

Data

32-bit signed integer status

Get Edge Blends State

Command

Start

0x53

Command Code

0x23

Data Mode

0x00

Zero-based channel ID

0x78

0x62

Reply

Data Length

0x00

0x00

0x00

0x04

Data

0x00

0x00

0x00

0x00 (disabled) or 0x01 (enabled)

Set Eyepoint Selection

Command

Start

0x53

Command Code

0x24

Data Mode

0x00

Zero-based channel ID

0x72

0x63

Data Length

0x00

0x00

0x00

0x04

Data

32-bit signed eyepoint index

Reply

Data Length

0x00

0x00

0x00

0x04

Data

32-bit signed integer status

Get Eyepoint Selection

Command

Start

0x53

Command Code

0x23

Data Mode

0x00

Zero-based channel ID

0x72

0x63

Reply

Data Length

0x00

0x00

0x00

0x04

Data

32-bit signed eyepoint index

The eyepoint selection causes an alternate warp map to be applied to the image.

Refresh render

Command

Start

0x53

Command Code

0x24

Data Mode

0x00

Zero-based channel ID

0x72

0x63

Data Length

0x00

0x00

0x00

0x04

Data

0x00

0x00

0x00

0x01

Reply

Data Length

0x00

0x00

0x00

0x04

Data

32-bit signed integer status

A refresh causes a reload of all corrections to the channel.

Get System Status

Command

Start

0x53

Command Code

0x23

Data Mode

0x00

Zero-based channel ID

0x73

0x74

Reply

Data Length

0x00

0x00

0x00

0x04

Data

32-bit signed integer status

System status is used as a connection check and to provide general-purpose status.

Runtime Communication

A secondary connection is made to facilitate runtime updates from DMS. This connection follows a different protocol than the control channel.

Start Runtime Service

Command

Start

0x53

Command Code

0x37

Reply

Command Code

0x44

0x01

Data

Desired black level

Minimum luminance

Once the service is started, replies are sent unsolicited with time of day changes. Desired black level is a single-precision float indicating the level of darkness desired for the time of day. Minimum luminance is a single-precision float indicating the lowest allowable brightness for the time of day. If the contrast of the system does not permit both requests to be met, the minimum brightness takes precedence over desired black level.

Example

Nova overrides:

CODE
CalibrationMode    INTERNAL                        
CalibrationDir     custom\DMS_Warp_Files           
CalibrationConfig  custom\display_calibration.cfg

CalibrationConfig file settings:

CODE
DMS_PORT                        8002
CH4_NAME                        OTWNVG-1
CH4_ID                          1
CH4_OUTPUTS                     1
CH4_FBO_SIZE_X                  2560
CH4_FBO_SIZE_Y                  1600
CH4_DISTORTION_MAP_MODE         ABSOLUTE
CH4_DISTORTION_MAP_FILE         1    Ch1DesignWarpMap.tif
CH4_DISTORTION_MAP_WIDTH        512
CH4_DISTORTION_MAP_HEIGHT       512
CH4_LUMINANCE_MAP_MODE          INDEPENDENT
CH4_LUMINANCE_MAP_FILE          1    Ch1DesignBlendMap.tif
CH4_LUMINANCE_MAP_WIDTH         512
CH4_LUMINANCE_MAP_HEIGHT        512
CH4_BLACK_LEVEL_MAP_FILE        1    Ch1DesignBlackLevelMap.tif

Example Warp Files:

Ch1DesignBlackLevelMap.tif

Ch1DesignBlendMap.tif

Ch1DesignWarpMap.tif

Example Test Pattern File:

Checkerboard_2560x1600_1.tif

Testing

To fully test the DMS interface, a Python script can be run that allows for runtime changes to the configuration. The script is constructed in python and requires it to run. Simply run python.exe from the command line and input the script "dmsClient.py" as an argument. The interface is fairly straightforward with commands for loading/unloading different pattern types, setting various edge states, selecting eyepoints and getting the current DMS state. One important note when specifying a new test/illumination pattern, path location should be relative to the "CalibrationDir" directory.

dmsClient.png

dmsClient.py

CODE
#######################################################################################################################
#                          Copyright (C) 2024, Aechelon Technology, Inc. All Rights Reserved.
#                       THIS IS UNPUBLISHED PROPRIETARY SOURCE CODE OF AECHELON TECHNOLOGY, INC.
# The copyright notice and/or Restricted Rights Legend below does not evidence any actual or intended publication or
# disclosure of this source code, which includes information that is the confidential and/or proprietary to, and is a
# trade secret of, Aechelon Technology, Inc. Any use, duplication or disclosure not specifically authorized in writing
# by Aechelon Technology is strictly prohibited.
#
# U.S. GOVERNMENT RESTRICTED RIGHTS LEGEND. Use, duplication or disclosure by the Government is subject to restrictions
# as set forth in FAR 52.227.14, DFARS 252.227-7014 and/or in similar or successor clauses in the FAR, DFARS or any FAR
# Supplement. Unpublished-- rights reserved under the copyright laws of the United States. Contractor/manufacturer is
# Aechelon Technology, Inc., 888 Brannan Street, Suite 210, San Francisco, CA 94103.
#######################################################################################################################

# DMS Client Simulator
# Implements protocol defined in
# //depot/sw_design/runtime/Calibration/VITAL Communication Interface for Alignment Systems.docx

import socket
import struct
from argparse import ArgumentParser, RawDescriptionHelpFormatter

# Constants from VITAL communication interface
DMS_START = 0x53
DMS_LOAD  = 0x1d
DMS_SET   = 0x24
DMS_GET   = 0x23

DMS_TEST_PATTERN    = 0x0100
DMS_TP_ILLUMINATION = 0x696c
DMS_EDGE_MASK       = 0x6562
DMS_EDGE_BLEND      = 0x7862
DMS_EYEPOINT        = 0x7263
DMS_REFRESH_RENDER  = 0x7272
DMS_SYSTEM_STATUS   = 0x7374


def initArgParser(parser):
    parser.add_argument("--server", required=True,
                        help="Server name")
    parser.add_argument("--port", required=False, type=int, default=8002,
                        help="Server port")


def printStatus(reply):
    if len(reply) == 4:
        unpacked = struct.unpack('!I', reply)
        print("Received status %x" % unpacked[0])
    elif len(reply) == 12:
        unpacked = struct.unpack('!f f f', reply)
        print("red=%f green=%f blue=%f" % unpacked)
    else:
        print("Unexpected reply length: %d" % len(reply))


def sendCommand(command, datum, data):

    if command == DMS_GET:
        header = (DMS_START, command, datum)
        packer = struct.Struct('!B B I')
        packed_header = packer.pack(*header)
        sock.sendall(packed_header)
    else:
        header = (DMS_START, command, datum, len(data))
        packer = struct.Struct('!B B I I')
        packed_header = packer.pack(*header)
        if isinstance(data, str):
            sock.sendall((packed_header.decode('utf-8') + data).encode('utf-8'))
        else:
            sock.sendall(packed_header + data)

    replyLength = sock.recv(4)
    reply = sock.recv(struct.unpack('!I', replyLength)[0])

    printStatus(reply)


def connectToServer(server, port):
    global sock
    sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
    serverAddress = (server, port)
    sock.connect(serverAddress)


def composeDatum(datum, channel):
    return channel * 65536 + datum


def menu():
    print("")
    print("Choose a DMS command by number:")
    print("================================")
    print("1. Load Test Pattern")
    print("2. Unload Test Pattern")
    print("3. Set Test Pattern Illumination")
    print("4. Get Test Pattern Illumination")
    print("5. Set Edge Mask")
    print("6. Get Edge Mask State")
    print("7. Set Edge Blends")
    print("8. Get Edge Blends State")
    print("9. Select Eyepoint")
    print("10. Get Eyepoint Selection")
    print("11. Refresh Render")
    print("12. Get System Status")
    print("13. Quit")
    print("================================")
    choice = int(input("Choice: "))

    if choice == 13:
        sock.close()
        print("Bye.")
        exit()
    elif choice < 1 or choice > 13:
        print("Invalid option")
        return
    else:
        channel = int(input("Enter Channel Number: "))

    if choice == 1:
        payload = input("Enter Test Pattern Name: ")
        payload += '\0'  # Add null-terminator
        sendCommand(DMS_LOAD, composeDatum(DMS_TEST_PATTERN, channel), payload)
    elif choice == 2:
        print("Unloading Test Pattern")
        sendCommand(DMS_LOAD, composeDatum(DMS_TEST_PATTERN, channel), "")
    elif choice == 3:
        red = float(input("Enter Red Illimination: "))
        green = float(input("Enter Green Illumination: "))
        blue = float(input("Enter Blue Illumination: "))
        payload = struct.pack('!f f f', red, green, blue)
        sendCommand(DMS_SET, composeDatum(DMS_TP_ILLUMINATION, channel), payload)
    elif choice == 4:
        print("Querying illumination for channel %d..." % channel)
        sendCommand(DMS_GET, composeDatum(DMS_TP_ILLUMINATION, channel), "")
    elif choice == 5:
        state = int(input("Enter State: "))
        payload = struct.pack('!I', state)
        sendCommand(DMS_SET, composeDatum(DMS_EDGE_MASK, channel), payload)
    elif choice == 6:
        print("Querying Mask State for channel %d..." % channel)
        sendCommand(DMS_GET, composeDatum(DMS_EDGE_MASK, channel), "")
    elif choice == 7:
        state = int(input("Enter State: "))
        payload = struct.pack('!I', state)
        sendCommand(DMS_SET, composeDatum(DMS_EDGE_BLEND, channel), payload)
    elif choice == 8:
        print("Querying Blend State for channel %d..." % channel)
        sendCommand(DMS_GET, composeDatum(DMS_EDGE_BLEND, channel), "")
    elif choice == 9:
        index = int(input("Enter Selection: "))
        payload = struct.pack('!I', index)
        sendCommand(DMS_SET, composeDatum(DMS_EYEPOINT, channel), payload)
    elif choice == 10:
        print("Querying Eyepoint for channel %d..." % channel)
        sendCommand(DMS_GET, composeDatum(DMS_EYEPOINT, channel), "")
    elif choice == 11:
        print("Refreshing Render for channel %d..." % channel)
        payload = struct.pack('!I', 0x01)
        sendCommand(DMS_SET, composeDatum(DMS_REFRESH_RENDER, channel), payload)
    elif choice == 12:
        print("Querying System Status of channel %d..." % channel)
        sendCommand(DMS_GET, composeDatum(DMS_SYSTEM_STATUS, channel), "")
    else:
        print("Unknown Command.")


def main():
    print("DMS Client Simulator")

    parser = ArgumentParser(formatter_class=RawDescriptionHelpFormatter, description=__doc__)
    initArgParser(parser)

    args = parser.parse_args()

    if args.server is None:
        parser.error("You must specify --server")

    print("Connecting to %s:%d" % (args.server, args.port))

    connectToServer(args.server, args.port)

    while True:
        menu()


if __name__ == "__main__":
    main()
JavaScript errors detected

Please note, these errors can depend on your browser setup.

If this problem persists, please contact our support.