RREF - Get Datarefs

To get a dataref, you’ll create a simple structure consisting of the dataref string, the frequency you want to receive it and an index number. This structure is your request, sent to X-Plane. In a way, you’re subscribing to the dataref, and you’ll continuously receive its value, at the frequency you requested, until you unsubscribe from it.

Send

The dataref is a bytestring.:

dataref = 'sim/aircraft/engine/acf_num_engines'
msg = struct.pack("<4sxii400s", b'RREF',
                  freq,                     # Send data # times/second
                  index,                    # include this index number with results
                  dataref.encode('utf-8'))  # remember to encode as bytestring
sock.sendto(msg, (beacon['ip'], beacon['port']))

To stop subscription, request with freq=0


Receive

UDP packets will have header RREF and include the index you passed during the request (that’s how you’ll know which dataref this is) and a single floating point value regardless of which dataref you’re requesting.:

(header,         # 'RREF'
 idx,            # integer, matching what you sent
 value           # single floating point value
 ) = struct.unpack('<4xsif', data)

Note the received packet may contain multiple <index><value> pairs, see example below.

Assuming your request is well-formed, X-Plane will respond (eventually) with a structure consisting of the index (matching the one you provided) and the current dataref value. “Eventually” because this is all asynchronous.

To stop receiving the dataref, you’ll need to form another request, identical to the first with the exception of setting frequency to zero.

Example

For example to retrieve the integer number of engines for the user aircraft, we’ll use the following dataref. (X-Plane datarefs are listed in <XP>/Resources/plugins/DataRefs.txt. Other plugins and third-party aircraft & scenery may define additional datarefs.)

Dataref

Type

Writable

sim/aircraft/engine/acf_num_engines

int

y

The code might look like:

# 1) Subscribe to receive once per second
cmd = b'RREF'  # "Request DataRef(s)"
freq = 1       # number of times per second (integer)
index = 0      # "my" number, so I can match responsed with my request
msg = struct.pack("<4sxii400s", cmd, freq, index, b'sim/aircraft/engine/acf_num_engines')
sock.sendto(msg, (beacon['ip'], beacon['port']))

# 2) Block, waiting to receive a packet
data, addr = sock.recvfrom(2048)
header = data[0:4]
if header[0:4] != b'RREF':
    raise ValueError("Unknown packet")

# 3) Unpack the data:
idx, value = struct.unpack("<if", data[5:13])
assert idx == index
print("Number of engines is {}".format(int(value)))

# 4) Unsubscribe -- as otherwise we'll continue to get this data, once every second!
freq = 0
msg = struct.pack("<4sxii400s", cmd, freq, index, b'sim/aircraft/engine/acf_num_engines')
sock.sendto(msg, (beacon['ip'], beacon['port']))

You’ll always get a float

Unpacking the data is always this same. The returned data is always 8 bytes: a 4-byte integer (your index) and a 4-byte float (the dataref value). Your code needs to evaluate the resulting float based on the datatype. In the above example, ‘acf_num_engines’ is an ‘int’ type, so we cast the returned floating point value to an integer.

If you’re unpacking something which is string, unpacking is a bit more complicated.

Dataref

Type

Writable

Comment

sim/aircraft/view/acf_ICAO

byte[40]

y

string ICAO code for aircraft (a string) entered by author

First, remember that you’re getting a float in return, so the value you get will need to be cast to a character:

# value == 65.0, result == 'A'
result = chr(int(value))  # covert float to integer, and then convert it to a character.

Arrays are handled one-element-at-a-time!

Second, and this is true for all array types (i.e., int[], byte[], float[]), you’ll need to actually subscribe to each element in the array. Yes, that’s crazy, but that’s how the X-Plane UDP interface works.

So, for acf_ICAO, you’ll subscribe to 40 datarefs, using the following dataref names:

sim/aircraft/view/acf_ICAO[0]
sim/aircraft/view/acf_ICAO[1]
sim/aircraft/view/acf_ICAO[2]
...
sim/aircraft/view/acf_ICAO[39]

You’ll have a different index for each, and you’ll receive UDP packets with a single float value for each array element.

Multiple results in same packet

For performance reasons, X-Plane may send multiple dataref results in a single UDP packet, so your code needs to be prepared for this:

data, addr = sock.recvfrom(2048)
values = data[5:]                  # skipping over 'RREFx' header, get _all_ values
num_values = int(len(values) / 8)  # Each dataref is 8 bytes long (index + value)
for i in range(num_values):
    dref_info = data[(5 + 8 * i):(5 + 8 * (i + 1))]  # extract the 8 byte segment
    (index, value) = struct.unpack("<if", dref_info)
    ...

For acf_ICAO example, you may get a single UDB packet which is analagous to

RREF,      # the 5-byte header      -- data[0:5]
(0, 67.0)  # 8 bytes, index + value -- data[5:13]
(1, 49.0)  # 8 bytes, index + value -- data[13:21]
(2, 55.0)  # 8 bytes, index + value -- data[21:29]
(3, 50.0)  # 8 bytes, index + value -- data[29:37]
(4,  0.0)  # 8 bytes, index + value -- data[37:45]
(5,  0,0)  # 8 bytes, index + value -- data[45:53]
...

Based on your subscription, you’ll know the indices map to:

0 -> sim/aircraft/view/acf_ICAO[0]
1 -> sim/aircraft/view/acf_ICAO[1]
2 -> sim/aircraft/view/acf_ICAO[2]
3 -> sim/aircraft/view/acf_ICAO[3]
4 -> sim/aircraft/view/acf_ICAO[4]
5 -> sim/aircraft/view/acf_ICAO[5]
...

And you’ll know it is supposed to be a string, so you’ll convert the floating point values to characters to yield:

(0,  'C')
(1,  '1')
(2,  '7')
(3,  '2')
(4, '\0')
(5, '\0')
...

And of course, you’re not guaranteed to get them in the order you’d like, so be prepared for that.

Using threads

Because reception is asynchronous and order is not guaranteed, you’ll likely need to create a separate thread to receive on, and use an internal datastructure to hold index numbers with their most recently received values. perhaps something like:

t = treading.thread(target=get_drefs, args=(my_mapping, ))
t.start()

def get_drefs(my_mapping):
    while Not_Exit:
        data, arry = Sock.recvfrom(2048)
        values = data[5:]
        num_values = int(len(values) / 8)
        for i in range(num_values):
            dref_info = data[(5 + 8 * i):(5 + 8 * (i + 1))]
            (index, value) = struct.unpack("<if", dref_info)
            my_mapping[index]['value'] = value