OpenLCB: Train Control Protocol

This document has been superseded by the Traction Control protocol Working Note, Technical Note, and Standard. Reference them instead of this one. This only remains for historical interest. Move along, nothing to see here.

This document defines a protocol for the control of model trains.

Environment of Protocol

Requirements

Preferences

Design Points

Trains will be addressed using a well-known addressing scheme. See TODO

All communications will be point-to-point, between a controller and a train.

A controller is any OpenLCB Node that might need to control a train; controllers includes hand-held cabs, computer control systems, or decentralized control units (such as auto-reversers for automated operation, or ATC systems).

A train is any model that runs on rails (i.e. is not steerable; thus, a train might include vehicles using the Faller or Tomix car systems). A train might be a single locomotive, a locomotive with carriages (e.g. passenger cars with interior lighting or markerlights that can be digitally controlled), a consist of multiple locomotives, a multiple unit, etc. Take, for example a model of the Usui Pass in Japan, ca. 1970. Electric multiple units that wished to traverse the pass (either up or down) needed to be assisted by a matched pair of EF63 electric locomotives. In this scenario, there are several potential train objects: The EMU by itself (being permanently consisted, the individual carriages would not count as trains in the sense being used here), each individual EF63, the EF63s in a paired consist, and the entire EF63+EF63+EMU consist are all train objects.

The head of a train is the node with control. In the case of a single locomotive, that train is its own head. In the case of a consist of multiple trains, the train at the very front in the forward direction of travel is the head.

Protocol

NodeIDs for Legacy Train Control Systems

Train Command Stations designed to act as a bridge between an OpenLCB network and a train control network, and hence that provides proxy OpenLCB Nodes to stand in for Trains unable to represent themselves, should adhere to the following schema for generating OpenLCB NodeIDs, as existing trains are unable to generate or provide these addresses for themselves. See OpenLCB Standard "OpenLCB Unique Identifiers", §5.5 for additional details.

byte 1

byte 2

byte 3

byte 4

byte 5

byte 6

0x06

[TCS ID]

[DCS ID1]

[DCS ID2]

[Train ID1]

[Train ID2]

Where:

Command Summary

All communications via the Train Control Protocol use the Datagram transport method. Each datagram consists of an PTI (protocol type identifier) byte, which is 0x30 for the Train Control Protocol, a command identifier byte, and zero or more bytes of command-specific data:

Command

Command Identifier Byte

Arguments

Emergency Stop

0x00

Set Train Speed

0x01

direction and speed (16-bit float)

Inquire Train Speed

0x03

kind of speed (8-bit flags)

Report Train Speed

0x02

kind of speed (8-bit flags)

direction and speed (16-bit float)

Set Train FX

0x11

FX address (16-bit uint)

FX setting (16-bit uint)

Inquire Train FX

0x13

FX address (16-bit uint)

Report Train FX

0x12

FX address (16-bit uint)

FX setting (16-bit uint)

Request Attach to Train

0x21

Attach Permitted

0x22

Attach Denied

0x24

Train Release

0x23

Controller Dropped

0x26

Create Consist

TODO

TODO

Add to Consist

TODO

TODO

Set Consist Head

TODO

TODO

Remove from Consist

TODO

TODO

Destroy Consist

TODO

TODO

Train Movement

Summary: Commands for controlling the movement of trains.

Notes on the table columns:

Command

Direction

Controlled?

Protocol Identifier Byte

Command Identifier Byte

Arguments

Byte 1

Byte 2

Byte 3

Byte 4

Byte 5

Emergency Stop

C→T

yes

0x30

0x00

Set Train Speed

C→T

yes

0x30

0x01

direction and speed (float16)

Inquire Train Speed

C→T

no

0x30

0x03

kind of speed (8-bit flags)

Report Train Speed

T→C

n/a

0x30

0x02

kind of speed (8-bit flags)

direction and speed (float16)

Emergency Stop

Bring the train to an immediate halt, without regard to any physical simulation of momentum. Notice that this command is supplementary to the more general OpenLCB commands TODO, with the difference being primarily one of scope.

Set Train Speed

The speed and direction to set is encoded as a half-precision floating point number (aka 'float16'), with positive numbers indicating forward direction, negative indicating reverse, and zero indicating full (non-emergency) stop. The value specifies a speed in scale meters per second (scale-m/s).

Rationale: The use of a 16-bit floating point permits relatively precise speed commands, especially at lower speeds; such fine granulatity ensures not just fine-grained control over the locomotive, but helps avoid aliasing issue that arise during the conversion to lower resolution system-specific speed commands (i.e. DCC's 14 or 28-step commands).

The use of meters per second is somewhat arbitrary, and reflects standard velocity units used throughout the metric-speaking world. By standardizing on m/s, we avoid any future unit conversion issues.

The use of scale meters per second has two distinct advantages. First, it permits us to transmit speed commands in a scale-independent way. Second, and because of this, it reduces the number of parameters that must be estimated when controlling a locomotive that has not yet been speed-calibrated (which, for new users using existing digital control systems, will be all of their models). For example, on a DCC system, if I issue a command to proceed at 30mph, the command station must convert the value in the speed command from 30mph to an interger in the range [0-26] (for 28-speed-step control). The command station need only estimate what a reasonable top speed for a locomotive might be: Let us say, 100mph. Thus, the command station could reasonably estimate that 30mph translates to speed step 8.

The alternative possibilities considered to date are absolute speed using real units (as opposed to scale units), and relative speed units. The difficulty with relative speed units (i.e., percentage of full throttle), is that TODO. The difficulty with using real (as opposed to scale) units is that it requires the estimation of an additional parameter for uncalibrated locomotives, specifically the train's scale. If I issue a command to a DCC locomotive to proceed at 0.1 (real)m/s, the command station must not only understand what a reasonable top speed for a train is, but how to scale the speed appropriately, as 0.1 m/s might be quite fast for Z scale, but quite slow for G. As there is really no reasonable scale to use as a default, users must configure their digital command station to set the scale for either the entire layout, or on a per-model basis—an additional configuration step that is easily avoided by the mechanism for scale units described above.

Inquire Train Speed

This is a request for a train to report its speed. Any OpenLCB node may make inquiries about a train's state without requesting permission first (see Security Commands below), although the train is under no particular obligation to respond. This command takes no additional arguments.

Of course, a request for a train's speed is itself ambiguous. The controller might be interested in knowing any of a number of possible measures: What speed the train has been commanded to take; what speed the train is attempting to reach; what speed the train is actually travelling at. Thus, a speed inquiry requires a parameter to indicate the interests of the controller:

Speed Kind

command argument

Commanded speed

0x00

Intended speed

0x01

Actual speed

0x03

Report Train Speed

This is a response to the Inquire Train Speed command, and combines the arguments to the Set Speed and Inquire Speed commands, returning both the kind of speed being reported using the same flags as above, and the speed itself as a float16.

Train FX

Summary: Commands for controlling a train's special effects, including lighting and sound.

Notes on the table columns:

Command

Direction

Controlled?

Protocol Identifier Byte

Command Identifier Byte

Arguments

Byte 1

Byte 2

Byte 3

Byte 4

Byte 5

Byte 6

Set Train FX

C→T

yes

0x30

0x11

FX address (uint_16)

FX value (uint_16)

Inquire Train FX

C→T

no

0x30

0x13

FX address (uint_16)

Report Train FX

T→C

n/a

0x30

0x12

FX address (uint_16)

FX value (uint_16)

Set Train FX

Many trains offer independent control of various special effects (FX, sometimes called "functions") such as lighting or sound. This command permits a controller to control these effects directly. The first argument is the address of the FX to control as a 16-bit unsigned integer. This protocol does not define a semantics for FX addresses; that is, there is no particular address that is singled out as represenging headlights or the airhorn. Instead, the addresses are deliberately abstract, permitting the user to decide how to map addresses to FX for each train.

Additionally, each FX can take a 16-bit value. Current technology only permits binary FX; thus 0x00 should be interpreted as "off" for a binary FX, and any non-zero value as "on". Analog (non-binary) FX should treat the 16-bit value as an unsigned integer.

Rationale: Most digital systems offer multiple FX, each of which is numbered. Although currently most DCC throttles only permit access to 12 or 29 functions, DCC (TODO to my knowledge!) technically permits as many as 32796* different binary FX (see RP-9.2.1: Function Group One Instruction, Function Group Two Instruction, and Feature Expansion Command Instruction especially the Binary State Control Subcommand). For this reason, it seems prudent to go ahead and use a 16-bit value.

Likewise, although current DCC decoders only permit binary FX, some (for example SoundTraxx Tsunami sound decoders) actually permit a kind of analog control of FX by combining multiple DCC Functions. Thus, it seems likewise prudent to permit a wide range of values, and not simply a binary on and off.

One problem that faces the decision to use a single command with a numerical FX addressing system is that any kind of standardized assignment of FX addresses to particular FX is impossible in practice. DCC, for example, makes no such prescription, although by convention function F0 controls direction-sensitive headlights. Beyond this lies only manufacturers' conventions. Thus, any kind of mapping is best handled on a per-train basis, by configuring the mapping between particular FX (e.g. headlights, airhorn) to FX addresses for each train.

One way to mitigate this problem is to not make fixed FX address assignments, but to map them directly onto the addressing scheme used by the various control systems, that is, to leave each address in the OpenLCB FX addres space uninterpreted. In this way, the default behavior of each address will map directly onto the default behavior of the decoder in the train, giving some degree of predictability to the system, and permitting a digital command station the make reasonable guesses about the possible address-to-FX mappings. Nevertheless, users will often need to be exposed to this implementational detail, which is deeply unfortunate, but necessary given the ultimate flexibility of current train control systems.

Thus, it is recommended that the FX address space be mapped directly to the particular control system's address space; thus DCC F0 becomes FX address 0x0000, F1 becomes 0x0001, etc. The DCC Binary State addresses should be mapped to 0x8000 to 0xFFFF (i.e., to the 15-bit range beginning with 0b1000.0000.0000.0000). Other systems should be handled analogously.

*RP-9.2.1 defines the following:
Function Group One: 5 (F0-F4)
Function Group Two: 8 (F5-F12)
Binary State Control: 32767 (15bits) (note: different address space!)
Feature Expansion Command 11110: 8 (F13-F20)
Feature Expansion Command 11111: 8 (F21-F28)
And in the future, perhaps even more. Possibly a lot more. There are just under 20bit of address space available in the Feature Expansion Comman Instruction for the potential manipulation of Binary State Controls.

Inquire Train FX

Any node may inquire the state of a train's FX. The only argument is the address of the FX to query.

Notice that, because the semantics for the FX address space may vary from train to train, this command does not provide a useful way to query the state of a particular FX, e.g. the headlights.

Report Train FX

This command is for trains to report the state of a particular FX in response to an Inquire FX command. The format of the parameters is the same as for the Set Train FX command.

Consisting

TODO Create Consist Add To Consist Remove From Consist Destroy Consist Consisting Acknowledged Consisting Denied

Train Access Control

Optional. Trains may restrict communications from controllers using the following packet kinds.

TODO Attach Request Attach Acknowledged Attach Denied Command Denied?





Email notes on use of configuration for legacy DCC feature and CV access


Oh, I did want to mention that I think we decided, at least tentatively, that functions would be handled via men config protocol, andthat for nodes with addresses in the well-known DCC range, we could use a convention where writes and reads to a pair of memoryspaces were to be interpreted as CV read/writes (in one space) and function updates (in the other). Did we talk about that, or wasthat all in my head?


We did talk about that for functions, and I really like the idea, but I wasn't sure we agreed on it.


For CVs, is the idea that we'd have a memory space that maps straight to the decoder CV space?


In both cases, that would allow decoder-specific CDI (if that was available) to customize what's presented. Like DecoderPro, the user wouldn't have to deal with CV 111, but rather with "Motor multisnarb angle offset" or whatever the user-fiendish manufacturer decides to provide as options.


There are two remaining issues. First, is handling "indexed" CVs. (Ones where you write 12 to CV 51, then 8 to CV52, and then CV54 is the value you want to read/write). QSI is the only extensive user of these, but they're starting to appear in other places too.


There are a couple ways to handle it. First, we could just map it as is: The configuring node would have to explicitly do those reads and writes. But that's a mess, well outside the OpenLCB model, and I've spent way to much time debugging weird failures with that.


A better approach, I think, is to use the large address space. E.g. CV 59 is found at 0x00 00 00 3B, while the one I mentioned above is found at 0x01 00 0C 08. (The 0x01 tells how to decode the address space, e.g. which CV the 2nd byte is written to, which the 3rd byte is written to. We'd have to extend CDI to carry that info, but it's within reason.


The other issue is packed CVs, e.g. mapping parts of one or more CVs to a single "variable". This could be just a single bit in one CV, or something more complicated split across two (like long addresses). I'm not sure how to map those as a general case. For bits, I'd suggest another mapping trick, where some other part of the space is actually bit mapped:


0xFF 00 53 3C


is the middle bits (the 3C mask) of CV 53, and the 0xFF is just an arbitrary key for this. That doesn't work with noncontiguous splits across generic CVs, though, as that takes a lot to configure.


(And don't get me started on CV1/CV29 sequencing; I think we just ignore that entirely at the CDI level & build it into the gateway to DCC)


In the end, these legacy pains can't really be avoided. We have to croft the OpenLCB protocols to do a reasonable job with them, but I don't think we have to go nearly as far into the weird special cases as e.g. DecoderPro does.


Notes from Implementing a Prototype


With a two-byte field for DCC address, one can use the NMRA coding to separate short, long, basic, etc.


http://nmra.org/standards/DCC/standards_rps/RP-921%202006%20Aug%2021.pdf


See section A on page 1.