Hangar
Developer documentation
Welcome to SmartCopilot development team!
Dear X-Plane aircraft developers! At the moment we have a unique opportunity to realize an old dream of all X-Plane users: to fly in one cockpit together with friends. My team already spent a lot of time to make this true. However, we still need your help to bring the dream to a professional level. There are many addons in X-Plane which have different ways of simulating various systems, complex, commercial aircraft with custom systems and plugins driving them, and basic, beginner models with simple systems built on X-Plane defaults. All these models have one thing in common: they were not realized thinking of the possibility of synchronizing the flight between two machines to fly in a shared cockpit. As it is now, we are stagnating and we might use some help from you as we are sure that using the right approach we can make the plugin 100% compatible with present and future add-on aircraft’s. Even custom displays and FMS can be synchronized. Of course this will require additional time and effort! This page is for those who want us to realize every simmer’s dream. Here we try to provide information about how the SmartCopilot plugin works and what is the best way to implement the systems in terms of synchronization compatibility. Welcome to our crew!
Basic information about SmartCopilot syncronization
X-Plane simulator provides two main data-units which are easily accessed from outside – datarefs and commands. Using these units the developer can connect his plugin either with X-Plane or even with another plugin. If we need to synchronize two X-Plane computers then we need to synchronize these variables. After trying various synchronization methods we chose what we think is the best model to do this action. The main assumptions of this model are as follows:
- No default multiplayer.
- Master – Slave concept – aircraft position be calculated on the Master computer and transmitted to the Slave.
- Slave has fully functional systems.
- Request – Release logic for aircraft main controls (Yoke/Rudder).
The third point is the most important. It means that we need to transmit the minimum, essential information to the Slave and leave the Slave to calculate all the rest. As an example – we don’t transmit the ADF bearing, it is enough to send only the tuning frequency and the Slave will calculate the bearing on its own. The second point relates to avoiding feedback between the two machines. With a master-slave architecture there are no misunderstandings on who controls who (so where the information comes from).
The above-mentioned applies to the existing addons. If you start building a new X-Plane model from the beginning then you can use the provided API to adjust the plane logic to work with SmartCopilot. This is described in the chapter The Right Approach.
Configuration file
The plugin is set up to work with each aircraft by using a config file. This is a simple INI file named smartcopilot.cfg which should be placed inside the aircraft folder! The file tells the plugin how to create a data image for processing and transmitting the data. As all other INI files, smartcopilot.cfg is a text file containing some sections and keys. Here is a snippet out of a config file:
### SmartCopilot config file. Version of plugin 3.0 ### B58 Carenado v1.04 ### Created by Rhard ### This section describes the config file. The information is used to create usage statistics. ### All values max. 20 characters long [INFO] ID = CARENADO_B58 VERSION = 1.04 AUTHOR = rhard ### This section describes triggered dataref's. The information will be sent only if the value changes. ### Data goes in both directions. [TRIGGERS] ## Control sim/cockpit2/controls/rudder_trim = 0 sim/cockpit2/controls/aileron_trim = 0 sim/cockpit2/controls/elevator_trim = 0 sim/cockpit2/engine/actuators/cowl_flap_ratio[0] = 0 sim/cockpit2/engine/actuators/cowl_flap_ratio[1] = 0 ## Switches sim/cockpit2/engine/actuators/ignition_key[0] = 4 sim/cockpit2/engine/actuators/ignition_key[1] = 4 sim/cockpit2/switches/landing_lights_switch[0] = 0 sim/cockpit2/switches/landing_lights_switch[1] = 0 sim/cockpit2/switches/panel_brightness_ratio[2] = 0
There you see different sections (INFO and TRIGGERS) and it’s dataref-keys. The value (after the “=” sign) has a different meaning for different sections and is shown in table Summary sections. Now let’s have a look at the SmartCopilot Data Flow.
SmartCopilot Data Flow
[TRIGGERS]
This is the main section for bidirectional data transfer. Data will be sent only if the value of dataref changes. Frame time is 25 ms. (Here and next you need to understand that it is the minimal cycle time! In fact the time can be longer depending on X-Plane FPS). In this section we need to write the datarefs for switches and manipulators which can be managed from both pilots during the flight. Five types of datarefs can be used here: int, float, array_of_int, array_of_float, and byte_array(strings).
To transmit the “press and hold” buttons the value of the key should be set to the holding position:
sim/cockpit2/engine/actuators/ignition_key[0] = 4
Here if the value 4 appears (ignition) then the position of the button will remain until be released. In all other cases leave it to zero.
During the tests we have seen that X-Plane executes plugins in no particular order. This means that some datarefs could be processed by another plugin before (or after) getting processed by SmartCopilot. The next example illustrates the button execution logic in airplanes using SASL-LUA (but the same logic could be used in aircraft’s with plugins written in C++).
if get(button_1) ~= 0 then do something... set(button_1, 0) end
Here you can see, that by pressing button_1 something is triggered and then button_1 is reset back to 0. If such a process happens before SmartCopilot “sees” the action then it cannot be transmitted to the Slave computer. The solution to this issues in the next section, CLICKS.
[CLICKS]
The CLICKS Section is used to transmit the datarefs shown in example above. It can be different FMS, FCU buttons etc. (valid values are only 0 – 1 – 0). To make the section work properly for each original dataref one dataref-satellite should be created. The name of this dataref is ORIGINAL_NAME + _SCP .e.g. original dataref:
sim/custom/xap/mcdu/click_1
– additional dataref-satellite to be created:
sim/custom/xap/mcdu/click_1_SCP
In addition the button logic code should be changed to:
if get(button_1) ~= 0 then do something... set(button_1_satellite, 1) set(button_1, 0) end
Here dataref-satellite is set to 1 – set (button_1_satellite, 1). SmartCopilot will “see” this change and transmit the button push event. Warning: you need to write only the original dataref to the config file. The Value after “=” has no meaning and can be zero. Data types for this section are only int and array_of_int.
[CONTINUED]
The CONTINUED section is the main section for one-directional data transfer. The data can be sent continuously only from Master to Slave. This section is good for values which are always changing during the flight and should be copied to Slave. As usual they are the basic variables for the system logic. Using this section you can also “push” the Slave to have the same values as the Master. Example:
- Bus voltages
- Feeder currents
- Hydraulic pressures
- Fuel quantity
If the value after “=” is 1, then SmartCopilot will frize the dataref value on Slave, until the next telegram will be received. Zero value means normal syncronization when values from Master will overwrite the Slave values in a “soft” way. Data types for this section are int, float, array_of_int, array_of_float and byte_array(strings).
[SEND_BACK]
The section SEND_BACK is the same as CONTINUED with one only difference that the transmission direction depends from CONTROL/NO CONTROL status of the Plugin. The Data to be sent continuously from HAS CONTROL to NO CONTROL. In this section you need to write the joystick and throttle datarefs:
This section describes the data which will be sent from SLAVE to MASTER if the SLAVE takes control (Example: yoke or rudder pedals). [SEND_BACK] sim/joystick/yoke_pitch_ratio = 0 sim/joystick/yoke_roll_ratio = 0 sim/joystick/yoke_heading_ratio = 0 sim/flightmodel/engine/ENGN_thro_use[0] = 0 sim/flightmodel/engine/ENGN_thro_use[1] = 0 sim/cockpit2/engine/actuators/mixture_ratio[0] = 0 sim/cockpit2/engine/actuators/mixture_ratio[1] = 0 sim/cockpit2/engine/actuators/prop_rotation_speed_rad_sec[0] = 0 sim/cockpit2/engine/actuators/prop_rotation_speed_rad_sec[1] = 0
The value after “=” remains zero. Data types supported for this section are int, float, array_of_int, array_of_float and byte_array(strings).
[COMMANDS]
This section is used to send commands to X-Plane. They are written in the config file always with a zero value. Commands are transmitted bidirectionally at the moment of their execution and run a similar command on the receiver. Note that similar to the situation with TRIGGERS, often it is necessary to stop command execution inside the command callback. In this case, the command will not be seen by SmartCopilot. All below-mentioned type of situation is not suitable for synchronization commands:
function gear_down_handler(phase) if 0 == phase then do something... end return 0 end
We can see here that the command handler returns zero value (return 0)
, indicating that the command would stop here and will no longer be processed elsewhere. Accordingly, if the function in the example above will be called before SmartCopilot command handler, the command won’t be transmitted.
[OVERRIDE]
The OVERRIDE Section is used to set override datarefs. Currently the plugin supports five states: MASTER_CONTROL, MASTER_NO_CONTROL, SLAVE_CONTROL, SLAVE_NO_CONTROL, NOT_DEFINED. You can set the override dataref for each of the states (and thus determine what corresponds to what in this value). You need to specify a value mask after the equal sign. Mask is defined from these values:
- MASTER_CONTROL = 1
- MASTER_NO_CONTROL = 2
- SLAVE_CONTROL = 4
- SLAVE_NO_CONTROL = 8
- NOT_DEFINED = 16
Thus, if you want override dataref to be set only when there is no control, you will need to set the mask to 10 (2 + 8):
sim/operation/override/override_joystick = 10
If you need override dataref to be set for Slave, then specify: 4 + 8 = 12.
If you need override dataref to be set for all states, then specify: 1 + 2 + 4 + 8 + 16 = 31.
[SLOW]
This section is similar to CONTINUED section. It was designed to synchronize datarefs in one direction from Master to Slave, but with a long interval of time between every transmission. Supported dataref types: int, float, array_of_int, array_of_float, byte_array. Transmission cycle is defined in seconds with the SLOW_INTERVAL key in the SETUP section:
[SETUP] ## Section [SLOW] is setup for 15 sec time interval SLOW_INTERVAL = 15.0
[WEATHER]
This is another section similar to SLOW with only the difference that transmission can be enabled and disabled by the Master during the flight. It was designed to synchronize datarefs of weather. The values in this section are only transmitted from the Master to the Slave, with a transmission cycle defined in seconds with the WEATHER_INTERVAL key in the SETUP section. Supported dataref types: int, float, array_of_int, array_of_float, byte_array.
If the WEATHER_INTERVAL key is not set, the value is taken from SmartCopilot preferences (default 1s).
[SETUP] ## Section [WEATHER] is setup for 1 sec time interval WEATHER_INTERVAL = 1.0
[TRANSPONDER]
This section is similar to TRIGGERS. Like a WEATHER section, the transmission of data in this section can be enabled/disabled by Master from UI during the flight. It is designed to synchronize datarefs and commands for transponder equipment. The values in this section are transmitted in both directions on value change. Supported dataref types: int, float, array_of_int, array_of_float, byte_array. Also, the commands can be used in this section by setting the key value to 1:
[TRANSPONDER] ## Datarefs sim/cockpit/radios/transponder_mode = 0 ## Commands laminar/B738/knob/transponder_mode_up = 1 laminar/B738/knob/transponder_mode_dn = 1
[SETUP]
A section with basic settings. Presently it contains following keys:
- SLOW_INTERVAL – for the datarefs in the SLOW section, it is in seconds;
- WEATHER_INTERVAL – for the datartefs in the WEATHER section. If the key is not set, the value is taken from SmartCopilot settings (default 1s);
- USE_AUTOPILOT_SEQUENCER – specifies whether the built-in automatic autopilot sequencer works (1 – yes, 0 – no).
- ENHANCED_SLAVE_SMOOTHNESS – enable enhanced slave smoothness (1 – yes, 0 – no, default – 0)
- DISABLE_SMOOTHNESS_ON_GROUND – disable enhanced slave smoothness on ground. Works only if ENHANCED_SLAVE_SMOOTHNESS is enabled. (1 – disable, 0 – enable, default – 0)
- SMOOTHNESS_POS_KP – proportional gain for position update. Works only if ENHANCED_SLAVE_SMOOTHNESS is enabled (default 0.5).
- SMOOTHNESS_POS_KD – derivative gain for position update. Works only if ENHANCED_SLAVE_SMOOTHNESS is enabled (default 2.0) .
- SMOOTHNESS_ATT_KP – proportional gain for attitude (angular position) update. Works only if ENHANCED_SLAVE_SMOOTHNESS is enabled (default 140.0).
- SMOOTHNESS_ATT_KD – derivative gain for attitude (angular position) update. Works only if ENHANCED_SLAVE_SMOOTHNESS is enabled (default 9.0).
Sections summary data table
The above sections can be described by a single table:
Section | Transmission direction | Data type | Key value | Minimum cycle time, ms |
---|---|---|---|---|
TRIGGERS | Bidirectional | int, float, array_of_int, array_of_float, byte_array | 0 – default transmission, x – press and hold value | 30 |
TRANSPONDER | Bidirectional, enabled by Master from UI | int, float, array_of_int, array_of_float, byte_array, commands | 0 – dataref, 1 – command | 30 |
CLICKS | Bidirectional | int, array_of_int | 0 – default transmission | 30 |
CONTINUED | Directional, from Master to Slave | int, float, array_of_int, array_of_float, byte_array | 0 – default transmission, 1 – “hard” set on each frame | 30 |
SEND_BACK | Directional, from HAS CONTROL to NO CONTROL | int, float, array_of_int, array_of_float, byte_array | 0 – default transmission | 30 |
WEATHER | Directional, from Master to Slave, enabled by Master from UI | int, float, array_of_int, array_of_float, byte_array | 0 – default transmission | Setup with key WEATHER_INTERVAL |
SLOW | Directional, from Master to Slave | int, float, array_of_int, array_of_float, byte_array | 0 – default transmission | Setup with key SLOW_INTERVAL |
COMMANDS | Bidirectional | X-Plane command | 0 – default transmission | Immediately |
SETUP | N/A | N/A | N/A | N/A |
INFO | N/A | N/A | N/A | N/A |
Algorithm to create a configuration file
We have several sections through which synchronization can occur. So, where to put each dataref we need? The planes we tried to work on had a very varied approach on the implementation of the systems. One thing is for sure – the transmission of positioning data from the Master to the Slave machine works. It is important to understand that the Slave will fly like the Master, even if its own systems will not work. All we have to do is to make the instruments work the same as on Master (or in worst case make a visual representation of working instruments). The ideal case is described in a section below, but before is a list of recommended steps in creating a config file for existing addons.
Please look at what Ben Supnik says which datarefs to use:
- Try to use
sim/cockpit2
andsim/flightmodel2
when possible; - More recent datarefs are usually better;
- Use the most useful dataref you can find.
In the same article it is also mentioned that the commands are more preferable than datarefs. So I would recommend starting with the commands. But, unfortunately, there are many cases where the commands won’t work. Often cockpit manipulators are not assigned to the commands which mean that when you press the hot key, the action will be transmitted, and when you click the mouse on the corresponding manipulator in the virtual cockpit, nothing happens. However, for example, in the default Baron B58 3D manipulators of the starter switches will call Ignition command and respectively transmission through the command will work here.
If command synchronization does not work for some functions go to the section TRIGGERS. But in this section try to avoid any dataref which changes value very frequently. Initially, this section is assumed only for the transmission of buttons and switches, instrument manipulators and so on. For example, all light switches, the course and heading knobs are the best candidates in this section. If dataref changes own value very often, think about moving this dataref to CONTINUED section. Or better yet, it may be possible to do synchronisation without this dataref or to implement this feature in any other manner.
SEND_BACK section should not cause much difficulty.
Autopilot synchronization
The synchronization of the autopilot can be really a challenge. Originally we used commands to do that. However, this solution has a major drawback – in case communication is lost and the autopilot state changes then it is not possible to get the same state without manually disconnecting and bringing both autopilots in the same mode and reconnecting again. To prevent this situation a built-in autopilot analyser was developed – AUTOPILOT SEQUENCER. It analyses autopilot datarefs and makes the necessary settings on second machine. To enable this mode enable corresponding key in section SETUP.
USE_AUTOPILOT_SEQUENCER = 1
It is necessary to check that the configuration file doesn’t contain any default autopilot datarefs or commands in this mode. Custom autopilot logic in this case may require its own synchronization as a built-in autopilot sequencer only works with the default autopilot logic.
Attention: This mode was tested with the A320 and could not work or it might be broken with other addons.
The right approach
SmartCopilot plugin has a several control datarefs. These datarefs can be used to adjust the aircraft systems and tell in which state the plugin currently is. There are two datarefs now:
scp/api/ismaster
scp/api/hascontrol_1
Both dataref are type integer and are read-only. A value of 0 – plugin is not defined, the value of 1 – SmartCopilot mode Slave/NoControl, value 2 – Master/HasControl. Using these datarefs we can disable some parts of Slave systems and for example block throttle input for user which doesn’t have control etc.
Using these features we build KLN90B GPS synchronization where in Master state we serialize full display information in one string dataref for transition. In Slave state KLN reads display data from the same dataref while all own logic is fully disabled. So using these types of dataref-driven-switches you can build aircraft systems 100% ready for synchronization. Without any limitation!
Some features
- To start logging developer data just set “Logging”: {“Level”} value to 1 in the plugin settings file;
- For on-the-fly analysis of the log data there is a free Windows program LogExpert which is analogue to the UNIX “tail” command. Using this program you can keep track of entries in the log on the fly;
- Transmission in the sections CONTINUED and SEND_BACK is much more preferable for the network than section TRIGGERS as the values in this sections are sent all together in one telegram. However the packet size must be within MTU limits (which is usually around 1200 bytes). It means around 300 datarefs can be safely transmitted in CONTINUED section without fragmentation.
- A properly configured config file should have almost dormant triggers in steady state without user manipulation. If you have continuous transmission of the data back and forth there might be a feedback from Slave to Master. Try to move the triggers to the CONTINUED section to solve the issue.