Jump to content

S9203 Site to Host Consist Report Decoder Reference: Difference between revisions

From DocDepot
DGast (talk | contribs)
No edit summary
DGast (talk | contribs)
No edit summary
 
(11 intermediate revisions by the same user not shown)
Line 1: Line 1:
{{DISPLAYTITLE:AEI Site-to-Host Consist Report (T-94) — Decoder Reference}}
{{DISPLAYTITLE:AEI Site-to-Host Consist Report (T-94) — Decoder Reference}}


Implementation notes for parsing '''AAR MSRP Standard S-9203A.V1.1''' consist reports ("T-94"). Covers the four segments required to walk a file end to end: '''AEM''', '''RRE''', '''EOT''', and '''EOC'''.
Implementation notes for parsing '''AAR MSRP Standard S-9203''' consist reports ("T-94"). Covers the segments required to walk a file end to end: '''AEM''', '''RRE''', '''EOT''', and '''EOC'''.


<div style="border-left:4px solid #2e5c8a; background:#eef3f9; padding:0.6em 1em; margin:1em 0;">
<div style="border-left:4px solid #2e5c8a; background:#eef3f9; padding:0.6em 1em; margin:1em 0;">
'''Scope'''
'''Scope:''' Field semantics are summarized for implementation only. Refer to the controlling AAR standard for authoritative definitions.
 
Field semantics are summarized for implementation only. Refer to the controlling AAR standard for authoritative definitions, and before publishing anything customer-facing.
</div>
</div>


__TOC__
__TOC__


== 1. File Model ==
== File Model ==


A T-94 file is a sequence of fixed-order, line-oriented segments. Each segment begins with a 3-character segment ID.
A T-94 file is a sequence of fixed-order, line-oriented segments. Each segment begins with a 3-character segment ID.
Line 21: Line 19:
* '''EOC''' — one per consist, always last. Carries the byte count.
* '''EOC''' — one per consist, always last. Carries the byte count.


Maintenance reports (AMH / MTS / EMS) are a separate message that may arrive attached to the consist or standalone.
Maintenance reports (AMH / MTS / EMS) are a separate message.


=== 1.1 Encoding Variants — Detect Before Parsing ===
=== Encoding Variants — Detect Before Parsing ===


Three output modes are user-selectable at the reader, so a decoder must handle all three.
Three output modes are user-selectable at the reader, so a decoder must handle all three.
Line 42: Line 40:
Read the first line. If characters 4 and 10 (1-based) are the delimiter candidate and total line length equals the fixed-width sum plus delimiter count, treat as Mode 2. If total length equals the bare fixed-width sum, Mode 1. Otherwise Mode 3.
Read the first line. If characters 4 and 10 (1-based) are the delimiter candidate and total line length equals the fixed-width sum plus delimiter count, treat as Mode 2. If total length equals the bare fixed-width sum, Mode 1. Otherwise Mode 3.


Sample the <code>*</code> character explicitly — V1.1 examples for DCL and TDM use asterisk delimiting, while older examples use space.
Sample the <code>*</code> character explicitly
</div>
</div>


Line 49: Line 47:
</div>
</div>


== 2. AEM — Consist Header ==
== AEM — Consist Header ==


One per consist. Establishes reporting site, timestamp, direction, speeds, counts, and site health.
One per consist. Establishes reporting site, timestamp, direction, speeds, counts, and site health.


=== 2.1 Offset Table ===
=== Offset Table ===


Mode 1 (fixed width, no delimiters), 0-based offsets, total '''78 bytes'''.
Mode 1 (fixed width, no delimiters), 0-based offsets, total '''78 bytes'''.
Line 119: Line 117:
|}
|}


=== 2.2 Implied Decimals ===
=== Implied Decimals ===


<pre>
<pre>
Line 133: Line 131:
Gate version-specific parsing on digit 1 of AEM08. A change in digit 1 may not be backward compatible; digits 2 and 3 are safe to ignore.
Gate version-specific parsing on digit 1 of AEM08. A change in digit 1 may not be backward compatible; digits 2 and 3 are safe to ignore.


=== 2.3 Code Lookups ===
=== Code Lookups ===


'''AEM10 / AEM11 — Conversion Status (consist confidence level)'''
'''AEM10 / AEM11 — Conversion Status (consist confidence level)'''
Line 277: Line 275:
This field is the natural hook for automated site-health alerting: map to a severity enum on ingest and raise on anything in <code>{B, F, I, Q, S, U, V, M}</code>.
This field is the natural hook for automated site-health alerting: map to a severity enum on ingest and raise on anything in <code>{B, F, I, Q, S, U, V, M}</code>.


=== 2.4 Derived Read-Rate Metrics ===
=== Derived Read-Rate Metrics ===


<pre>
<pre>
Line 286: Line 284:
Counts include untagged equipment; "Tagged" counts only valid tags. Guard against divide-by-zero — a locomotive-only or railcar-only consist yields a zero denominator on the other pair.
Counts include untagged equipment; "Tagged" counts only valid tags. Guard against divide-by-zero — a locomotive-only or railcar-only consist yields a zero denominator on the other pair.


== 3. RRE — Rail Equipment ==
== RRE — Rail Equipment ==


Opens the per-vehicle loop. At least one RRE per rail vehicle, tagged or not. Always the first segment reported for a vehicle.
Opens the per-vehicle loop. At least one RRE per rail vehicle, tagged or not. Always the first segment reported for a vehicle.


=== 3.1 Offset Table ===
=== Offset Table ===


Mode 1 (fixed width, no delimiters), 0-based offsets, total '''38 bytes'''.
Mode 1 (fixed width, no delimiters), 0-based offsets, total '''38 bytes'''.
Line 328: Line 326:
|}
|}


=== 3.2 Code Lookups ===
=== Code Lookups ===


'''RRE02 — Equipment Group Code:''' D = Locomotive, R = Railcar (includes former non-revenue rail), ? = Unknown. Derived from axle pattern when the vehicle is untagged.
'''RRE02 — Equipment Group Code:''' D = Locomotive, R = Railcar (includes former non-revenue rail), ? = Unknown. Derived from axle pattern when the vehicle is untagged.
Line 396: Line 394:
</div>
</div>


=== 3.3 The Duplicate-Sequence Rule ===
=== The Duplicate-Sequence Rule ===


<div style="border-left:4px solid #c77c1e; background:#fdf3e3; padding:0.6em 1em; margin:1em 0;">
<div style="border-left:4px solid #c77c1e; background:#fdf3e3; padding:0.6em 1em; margin:1em 0;">
Line 404: Line 402:
</div>
</div>


=== 3.4 Field Handling Notes ===
=== Field Handling Notes ===


* '''RRE03 (Owner Code)''' — 4 characters, space-padded SCAC. '''Blank if not tagged.''' Trim on read; distinguish blank-because-untagged from blank-because-suppressed by checking RRE08 = N.
* '''RRE03 (Owner Code)''' — 4 characters, space-padded SCAC. '''Blank if not tagged.''' Trim on read; distinguish blank-because-untagged from blank-because-suppressed by checking RRE08 = N.
Line 412: Line 410:
* Untagged vehicles still produce an RRE derived purely from axle pattern. Expect blank SCAC, zero equipment number, and RRE08 = N.
* Untagged vehicles still produce an RRE derived purely from axle pattern. Expect blank SCAC, zero equipment number, and RRE08 = N.


== 4. EOT — End of Train Device ==
== EOT — End of Train Device ==


One per EOT tag read. Usually the last tag in the consist, but it can appear on any vehicle (for example, dead-headed on a locomotive), so do not use EOT position as an end-of-train marker.
One per EOT tag read. Usually the last tag in the consist, but it can appear on any vehicle (for example, dead-headed on a locomotive), so do not use EOT position as an end-of-train marker.


=== 4.1 Offset Table ===
=== Offset Table ===


Mode 1 (fixed width, no delimiters), 0-based offsets, total '''26 bytes'''.
Mode 1 (fixed width, no delimiters), 0-based offsets, total '''26 bytes'''.
Line 440: Line 438:
|}
|}


=== 4.2 EOT07 — Tag Detail Status ===
=== EOT07 — Tag Detail Status ===


{| class="wikitable"
{| class="wikitable"
Line 460: Line 458:
Maximum occurrences per RRE loop: 99. EOT01 associates the device to its carrier — join on sequence number, and do not assume the EOT belongs to the highest-numbered vehicle.
Maximum occurrences per RRE loop: 99. EOT01 associates the device to its carrier — join on sequence number, and do not assume the EOT belongs to the highest-numbered vehicle.


== 5. EOC — End of Consist ==
== EOC — End of Consist ==


Mandatory terminating segment. Always the last segment of the consist report. Total '''13 bytes'''.
Mandatory terminating segment. Always the last segment of the consist report. Total '''13 bytes'''.
Line 492: Line 490:
Absence of EOC means a truncated transmission. Quarantine the consist; do not partially commit.
Absence of EOC means a truncated transmission. Quarantine the consist; do not partially commit.


== 6. Units of Measure ==
== Units of Measure ==


AEM14 governs the whole report. Fields affected across the four segments in scope:
AEM14 governs the whole report. Fields affected across the four segments in scope:
Line 512: Line 510:
Out of scope here but on the same latch: CEQ length (feet / decimeters), XSC weight (pounds / kilograms), DYL fuel volume (100 gallons / 100 liters) and cumulative energy (100 HP-hours / 100 kW-hours).
Out of scope here but on the same latch: CEQ length (feet / decimeters), XSC weight (pounds / kilograms), DYL fuel volume (100 gallons / 100 liters) and cumulative energy (100 HP-hours / 100 kW-hours).


== 7. Parser Contract ==
== Parser Contract ==


=== 7.1 Ordering and Looping ===
=== Ordering and Looping ===


Segments must be transmitted in the standard T-94 sequence. Relevant structure for the segments in scope:
Segments must be transmitted in the standard T-94 sequence. Relevant structure for the segments in scope:
Line 531: Line 529:
| <code>EOT</code> || 2 || 2 || Optional || 99
| <code>EOT</code> || 2 || 2 || Optional || 99
|-
|-
| <code>RTD</code> || 2 || 6 || Mandatory in V1.1 || 99
| <code>RTD</code> || 2 || 6 || Mandatory for Class 1 in V1.1 || 99
|-
|-
| <code>CEQ</code> || 2 || 13 || Optional — begins CEQ loop inside RRE loop || 999
| <code>CEQ</code> || 2 || 13 || Optional — begins CEQ loop inside RRE loop || 999
Line 542: Line 540:
Loop mechanics: the first segment of a loop appears exactly once per occurrence and never again inside that loop, so RRE reliably delimits vehicle boundaries, and CEQ and MMR reliably delimit their nested occurrences.
Loop mechanics: the first segment of a loop appears exactly once per occurrence and never again inside that loop, so RRE reliably delimits vehicle boundaries, and CEQ and MMR reliably delimit their nested occurrences.


=== 7.2 Recommended State Machine ===
=== Recommended State Machine ===


<pre>
<pre>
Line 566: Line 564:
</pre>
</pre>


=== 7.3 Defensive Rules ===
=== Defensive Rules ===


# '''Dispatch on segment ID, never on line position.''' Any segment may be disabled at the site.
# '''Dispatch on segment ID, never on line position.''' Any segment may be disabled at the site.
Line 577: Line 575:
# '''Numeric fields are zero-padded strings.''' Strip leading zeros only at the point of use. Equipment numbers in particular should round-trip as strings.
# '''Numeric fields are zero-padded strings.''' Strip leading zeros only at the point of use. Equipment numbers in particular should round-trip as strings.


=== 7.4 Validation Checklist per Consist ===
=== Validation Checklist per Consist ===


{| class="wikitable"
{| class="wikitable"
Line 603: Line 601:
|}
|}


== 8. Open Items ==
== Open Items ==


* Confirm per-vendor CR/LF and trailing-delimiter treatment in the EOC01 byte count before enforcing rejection.
* Confirm per-vendor CR/LF and trailing-delimiter treatment in the EOC01 byte count before enforcing rejection.
* Confirm whether source sites emit the V1.1-mandatory RTD and TDM segments, or legacy output without them. This determines whether their absence is an error or an expected legacy condition.
* Confirm whether source sites emit the RTD and TDM segments, or legacy output without them. This determines whether their absence is an error or an expected condition.
* Register the delimiter character actually used per site (space vs asterisk) in site configuration rather than detecting it per file.
* Register the delimiter character actually used per site (space vs asterisk) in site configuration rather than detecting it per file.
* DCL and TDM are marked '''Proposed''' in the standard while RTD is mandatory in V1.1. Verify current adoption state against the latest published revision before treating any of the three as guaranteed.


== 9. References ==
== References ==


* AAR Manual of Standards and Recommended Practices, Standard '''S-9203A.V1.1''' — AEI Site-to-Host Consist Report Format (formerly S-918A). Adopted 2003, revised 2020.
* AAR Manual of Standards and Recommended Practices, Standard '''S-9203A.V1.1''' — AEI Site-to-Host Consist Report Format (formerly S-918A). Adopted 2003, revised 2020.
Line 617: Line 614:


<div style="border-left:4px solid #2e5c8a; background:#eef3f9; padding:0.6em 1em; margin:1em 0;">
<div style="border-left:4px solid #2e5c8a; background:#eef3f9; padding:0.6em 1em; margin:1em 0;">
'''Do not host the standard on this wiki'''
'''<big>This page is an implementation summary. The AAR MSRP is licensed, copyrighted material</big>'''  
</div>


This page is an implementation summary. The AAR MSRP is licensed, copyrighted material — link to the controlled copy rather than attaching or transcribing the source document.
== Examples ==
</div>
[[Media:ConsistExamples.zip|Example Consists]]


[[Category:AEI]]
[[Category:AEI]]
[[Category:Standards]]
[[Category:Standards]]
[[Category:Software Development]]
[[Category:Software Development]]

Latest revision as of 13:08, 30 July 2026


Implementation notes for parsing AAR MSRP Standard S-9203 consist reports ("T-94"). Covers the segments required to walk a file end to end: AEM, RRE, EOT, and EOC.

Scope: Field semantics are summarized for implementation only. Refer to the controlling AAR standard for authoritative definitions.

File Model

A T-94 file is a sequence of fixed-order, line-oriented segments. Each segment begins with a 3-character segment ID.

  • AEM — one per consist, always first. Train-level header.
  • DCL — zero or more, immediately after AEM (GPS / site asset inventory). At least one required in V1.1.
  • RRE — one per rail vehicle, always the first segment for that vehicle. Opens the per-vehicle loop.
  • Per-vehicle children (TDM, RTD, CEQ, EOT, XSC, THI, HAT, DYx, GEN, ALM, MMR, TST) follow their RRE.
  • EOC — one per consist, always last. Carries the byte count.

Maintenance reports (AMH / MTS / EMS) are a separate message.

Encoding Variants — Detect Before Parsing

Three output modes are user-selectable at the reader, so a decoder must handle all three.

Mode Field widths Delimiter Parse strategy
1 Fixed None Slice by offset table
2 Fixed Present (space or *) Slice by offset, skip 1 delimiter char between fields
3 Variable Present Split on delimiter, index by position

Detection heuristic

Read the first line. If characters 4 and 10 (1-based) are the delimiter candidate and total line length equals the fixed-width sum plus delimiter count, treat as Mode 2. If total length equals the bare fixed-width sum, Mode 1. Otherwise Mode 3.

Sample the * character explicitly

Elements and whole segments may be turned off by site configuration, and turning off elements forces a delimited format. Never assume a fixed line length without validating it; treat a short line as "elements suppressed," not as corrupt data.

AEM — Consist Header

One per consist. Establishes reporting site, timestamp, direction, speeds, counts, and site health.

Offset Table

Mode 1 (fixed width, no delimiters), 0-based offsets, total 78 bytes.

Ref Off Len Type Field
AEM00 0 3 AN Segment ID = "AEM"
AEM01 3 5 AN AAR Billing Code
AEM02 8 7 AN Site ID
AEM03 15 6 N Event Start Date (YYMMDD)
AEM04 21 4 N Event Start Time (HHMM, local)
AEM05 25 4 N Event Stop Time (HHMM)
AEM06 29 3 N Time Zone offset from GMT
AEM07 32 1 A Daylight Savings Time Indicator (Y/N)
AEM08 33 3 AN Data Format Version Number
AEM09 36 4 N Train Sequence Number
AEM10 40 1 AN Locomotive Conversion Status
AEM11 41 1 AN Railcar Conversion Status
AEM12 42 1 A Direction of Travel
AEM13 43 1 N Switch / Direction Indicator
AEM14 44 1 A Units of Measure (E/M)
AEM15 45 3 N Maximum Speed
AEM16 48 3 N Minimum Speed
AEM17 51 3 N Average Speed
AEM18 54 1 AN Movement Status
AEM19 55 1 AN Termination Status
AEM20 56 1 AN Transmission Type
AEM21 57 1 AN Adjacent Track Occupied (Y/N)
AEM22 58 5 N Train Length
AEM23 63 1 AN Equipment Status Code
AEM24 64 2 N Locomotive Count
AEM25 66 2 N Locomotives Tagged
AEM26 68 3 N Railcar Count
AEM27 71 3 N Railcars Tagged
AEM28 74 4 N Total Axle Count

Implied Decimals

AEM06  Time Zone       "##.#" decimal assumed  ->  "080" = 8.0 hrs west of GMT
                       5 = Eastern   6 = Central   7 = Mountain   8 = Pacific

AEM08  Format Version  "#.##" decimal assumed  ->  "400" = 4.00
                       digit 1 = breaking change
                       digit 2 = backward-compatible enhancement
                       digit 3 = processing upgrade

Gate version-specific parsing on digit 1 of AEM08. A change in digit 1 may not be backward compatible; digits 2 and 3 are safe to ignore.

Code Lookups

AEM10 / AEM11 — Conversion Status (consist confidence level)

Code Meaning
G Good — no significant problems
A At least one questionable axle pattern was found
D Excessive disqualifiers — 25% or more show tag/axle inconsistency
M Multiple inconsistencies (both A and D)
? Other

AEM12 — Direction of Travel: N, S, E, W

AEM13 — Switch / Direction Indicator: 0–9, owner-defined. Reflects local switch position so the host can infer route where multiple routes exist. Do not hardcode meanings; make it a per-site lookup.

AEM14 — Units of Measure: E = English, M = Metric. Applies to the entire report. Latch this value and use it for all downstream unit conversions (see Section 6).

AEM18 — Movement Status

Code Meaning
A Through movement, over 5 mph
B Through movement, under 5 mph
C Through movement, stop and go
D Through movement, switching (direction changes detected)
E Simulation (software-generated train)
F Reverse exit (pull by)
R Arrival listing
? Other

Filter E (simulation) out of production analytics. It is a software-generated train, not traffic.

AEM19 — Termination Status

Code Meaning Decoder action
N Normal Accept
T Time-out; train stopped, partial consist reported Hold as fragment; expect an appended retransmission
O Presence forcibly cleared (failed presence circuit) Accept, flag data quality
P Processing limitation Accept, flag data quality
? Other Accept, flag

AEM20 — Transmission Type

Code Meaning Decoder action
F First transmission Insert
R Retransmission Idempotent upsert on consist key
S Summary Upsert
A Appended Replace the prior fragment entirely
? Other Log

Time-out / append pairing — the main state-machine trap

When AEM19 = T, the site reports a partial train. When movement resumes the train is reported again with the new cars appended, carrying the same train sequence number and the same start date/time as the original, and AEM20 = A.

The appended report replaces the earlier fragment — it is not a delta. Key idempotency on (AEM01, AEM02, AEM09, AEM03, AEM04) and overwrite rather than merge.

AEM23 — Equipment Status Code (site health)

Reflects abnormal site activity during or before this train. Detail lives in the maintenance report.

Code Condition Severity
G No problems detected OK
A Antenna warning — low tag count on one or more antennas Warning
B Antenna fatal — at least one antenna no longer reading Fatal
C Communication error with external components Error
E Intertrack communication warning (recovered) Warning
F Intertrack communication fatal — cannot reach external processor Fatal
H Power supply warning — charger temporarily lost power Warning
I Power supply fatal — charger lost power Fatal
K Software setup failure — parameter set inaccurately Error
M Multiple problems Escalate
P Presence circuit warning (recovered) Warning
Q Presence circuit fatal — no longer functional Fatal
R Reader / RF warning (recovered) Warning
S Reader / RF fatal — at least one reader no longer functional Fatal
T Wheel transducer warning (recovered) Warning
U Wheel transducer fatal Fatal
V No wheel transducer input Fatal
W External detector trigger (hut door, thermocouple) Info
X External processor communication warning (recovered) Warning
Y External processor communication fatal Fatal
Z Security — excessive failed logon attempts Security
? Other Warning

This field is the natural hook for automated site-health alerting: map to a severity enum on ingest and raise on anything in {B, F, I, Q, S, U, V, M}.

Derived Read-Rate Metrics

loco_read_rate    = AEM25 / AEM24     (Locomotives Tagged / Locomotive Count)
railcar_read_rate = AEM27 / AEM26     (Railcars Tagged / Railcar Count)

Counts include untagged equipment; "Tagged" counts only valid tags. Guard against divide-by-zero — a locomotive-only or railcar-only consist yields a zero denominator on the other pair.

RRE — Rail Equipment

Opens the per-vehicle loop. At least one RRE per rail vehicle, tagged or not. Always the first segment reported for a vehicle.

Offset Table

Mode 1 (fixed width, no delimiters), 0-based offsets, total 38 bytes.

Ref Off Len Type Field
RRE00 0 3 AN Segment ID = "RRE"
RRE01 3 3 N Sequence Number (standing order, 1 = first)
RRE02 6 1 A Equipment Group Code
RRE03 7 4 A Owner Code (SCAC)
RRE04 11 10 N Owner Equipment Number
RRE05 21 1 A Orientation
RRE06 22 1 AN Reserved for future use
RRE07 23 1 A Axle Conversion Code
RRE08 24 1 A Tag Status
RRE09 25 1 A Tag Detail Status
RRE10 26 2 N Handshakes Antenna 0
RRE11 28 2 N Handshakes Antenna 1
RRE12 30 3 N Speed of Vehicle
RRE13 33 3 N Axles counted by wheel sensor
RRE14 36 2 N Platform Count

Code Lookups

RRE02 — Equipment Group Code: D = Locomotive, R = Railcar (includes former non-revenue rail), ? = Unknown. Derived from axle pattern when the vehicle is untagged.

RRE05 — Orientation (which end faces forward)

Code Railcar Locomotive
A "A" end "F" end
B "B" end (also called the brake end) "R" end
U Unknown / not determined Unknown / not determined

RRE07 — Axle Conversion Code: G = good, no problem determining vehicles from axle input. B = bad, axle data may have created a phantom vehicle. S = sequence logically correct but software cannot determine vehicle type.

RRE08 — Tag Status (placement and read completeness)

Code Meaning
G Good — both right and left tags read
M Mismatched identification — owner code and equipment number differ between tags
L Left tag missing
R Right tag missing
N No tag read
? Other

RRE09 — Tag Detail Status (tag content and programming)

Code Meaning
K OK
A Axle error — tag axle count differs from axle pattern
E EGC error — tag equipment group code differs from axle pattern
L Length error — tag length differs from axle pattern by more than 15%
O Placement error — tag applied on wrong side or wrong end
D Unused tag read on equipment
W Window violation — tag outside the AAR acceptable application window
M Multiple errors
? Other

RRE09 = "M" triggers a required TDM segment

When Tag Detail Status is M, the RRE is followed by a TDM segment enumerating exactly which cross-checks failed. A decoder that ignores TDM loses all detail behind an "M".

Note also that a TDM is unconditionally required directly after the first RRE in every consist. That one declares which cross-checks the site is capable of measuring at all, so parse it before interpreting any RRE09 value.

The Duplicate-Sequence Rule

Sequence Number is not a unique key. When two tags are found on one vehicle with differing IDs (an ID mismatch), two RRE segments are emitted with the same RRE01, both carrying RRE08 = "M".

The primary key for a vehicle row must be (consist key, RRE01, occurrence) — or (consist key, RRE01, RRE03, RRE04). A naive upsert on sequence number silently drops the second tag and destroys the mismatch evidence.

Field Handling Notes

  • RRE03 (Owner Code) — 4 characters, space-padded SCAC. Blank if not tagged. Trim on read; distinguish blank-because-untagged from blank-because-suppressed by checking RRE08 = N.
  • RRE04 (Equipment Number) — 10 digits with leading zeros. Zero if not tagged. Store as a string if you need exact round-tripping; convert to integer only for matching.
  • RRE10 / RRE11 (Handshakes) — 2 digits, saturating: 99 means "99 or more," not literally 99. Do not average across a saturated field without flagging. A zero on one antenna alongside a healthy count on the other is the normal signature of a missing left or right tag.
  • RRE14 (Platform Count) — always 1 for non-articulated cars. A value greater than 1 indicates an articulated multi-platform car, which is what CEQ platform codes hang off.
  • Untagged vehicles still produce an RRE derived purely from axle pattern. Expect blank SCAC, zero equipment number, and RRE08 = N.

EOT — End of Train Device

One per EOT tag read. Usually the last tag in the consist, but it can appear on any vehicle (for example, dead-headed on a locomotive), so do not use EOT position as an end-of-train marker.

Offset Table

Mode 1 (fixed width, no delimiters), 0-based offsets, total 26 bytes.

Ref Off Len Type Field
EOT00 0 3 AN Segment ID = "EOT"
EOT01 3 3 N Sequence Number of the carrying vehicle
EOT02 6 1 A Equipment Group Code (from tag; "E" per EGC table)
EOT03 7 4 A Owner Code (SCAC)
EOT04 11 10 N Owner Equipment Number
EOT05 21 2 N Handshakes Antenna 0
EOT06 23 2 N Handshakes Antenna 1
EOT07 25 1 A Tag Detail Status

EOT07 — Tag Detail Status

Code Meaning
K OK
H Performance — low handshakes
W Window violation — tag in wrong location
? Other

Per the implementation-status notes in the standard, EOT Tag Detail Status is always "K" in practice, with all other fields complete. Treat a non-K value as an anomaly worth logging rather than a routine branch.

Maximum occurrences per RRE loop: 99. EOT01 associates the device to its carrier — join on sequence number, and do not assume the EOT belongs to the highest-numbered vehicle.

EOC — End of Consist

Mandatory terminating segment. Always the last segment of the consist report. Total 13 bytes.

Ref Off Len Type Field
EOC00 0 3 AN Segment ID = "EOC"
EOC01 3 10 N Total Byte Count

Byte count semantics

EOC01 counts from the consist header (AEM) up to but not including the EOC segment itself. Use it as the integrity check on every consist:

observed = byte_length(bytes from start of AEM through end of
                       the segment immediately preceding EOC)

if observed != int(EOC01):
    quarantine(consist)

Two things to pin down against live data from each site before enabling hard rejection: whether line terminators (CR/LF) are included in the count, and whether trailing delimiters are counted. These vary by vendor implementation.

Recommended rollout: log the delta in advisory mode first, then enforce once the per-vendor convention is confirmed.

Absence of EOC means a truncated transmission. Quarantine the consist; do not partially commit.

Units of Measure

AEM14 governs the whole report. Fields affected across the four segments in scope:

Segment Field E (English) M (Metric)
AEM Maximum / Minimum / Average Speed miles per hour kilometers per hour
AEM Train Length feet decimeters
RRE Speed miles per hour kilometers per hour

Metric train length is in decimeters, not meters. This is a frequent off-by-10 source. Normalize to a single internal unit at ingest and store the original AEM14 value alongside it for audit.

Out of scope here but on the same latch: CEQ length (feet / decimeters), XSC weight (pounds / kilograms), DYL fuel volume (100 gallons / 100 liters) and cumulative energy (100 HP-hours / 100 kW-hours).

Parser Contract

Ordering and Looping

Segments must be transmitted in the standard T-94 sequence. Relevant structure for the segments in scope:

Segment Loop L1 Loop L2 Status Max Occurs
AEM 1 1 Required 1
DCL 1 1 Optional loop 999
RRE 2 1 Optional — begins RRE loop 999
TDM 2 1 Required after RRE01 / conditional
EOT 2 2 Optional 99
RTD 2 6 Mandatory for Class 1 in V1.1 99
CEQ 2 13 Optional — begins CEQ loop inside RRE loop 999
MMR 2 14 Optional — begins MMR loop inside RRE loop 999
EOC 3 1 Required 1

Loop mechanics: the first segment of a loop appears exactly once per occurrence and never again inside that loop, so RRE reliably delimits vehicle boundaries, and CEQ and MMR reliably delimit their nested occurrences.

Recommended State Machine

EXPECT_AEM
  -> AEM    : latch units(AEM14), version(AEM08), consist key,
              site health(AEM23); start byte counter

EXPECT_DCL_OR_RRE
  -> DCL*   : accumulate site asset inventory
  -> RRE    : first RRE -> next segment MUST be TDM (capability decl.)

IN_VEHICLE
  -> RRE    : close prior vehicle, open new.
              if RRE01 == previous RRE01
                  -> ID-mismatch pair, SAME vehicle
  -> TDM    : cross-check detail (required when prior RRE09 == "M")
  -> RTD    : raw tag hex, 0..99 per vehicle
  -> EOT | CEQ | XSC | THI | HAT | DYx | GEN | ALM | MMR | TST
              : attach to current RRE01
  -> EOC    : close vehicle, validate byte count, emit consist

DONE

Defensive Rules

  1. Dispatch on segment ID, never on line position. Any segment may be disabled at the site.
  2. Treat "?" as a first-class value in every enumerated field. It means "other," not "invalid." Never throw on it.
  3. Preserve raw lines. Store the original segment text alongside the parsed row for the retention window. Reprocessing beats re-requesting from a Class I host.
  4. Never reject a consist over an unknown code value. Log unknown enum values and pass the record through with a data-quality flag. New codes get added to the standard.
  5. Idempotency key: (AEM01 Billing Code, AEM02 Site ID, AEM09 Sequence, AEM03 Start Date, AEM04 Start Time). AEM09 wraps from 9999 back to 1 and zero is invalid, so sequence alone is not unique across a long feed.
  6. Timestamps are local time. AEM03 and AEM04 plus the AEM06 offset plus the AEM07 DST flag. Convert to UTC on ingest and retain the original triple. Do not infer DST from the date; trust AEM07.
  7. Fixed-width examples in the standard are not perfectly consistent in their spacing. Build the parser off the field-size tables, not off the printed examples, and be tolerant of stray whitespace.
  8. Numeric fields are zero-padded strings. Strip leading zeros only at the point of use. Equipment numbers in particular should round-trip as strings.

Validation Checklist per Consist

Check Source On failure
Segment ID recognized 3-char prefix Log and skip segment, continue
AEM present and first State machine Reject consist
EOC present and last State machine Quarantine as truncated
Byte count matches EOC01 Advisory, then reject once convention confirmed
Format version major digit supported AEM08 digit 1 Route to quarantine for review
RRE count reconciles to AEM24 + AEM26 Counts Data-quality flag, do not reject
Tagged counts do not exceed total counts AEM24/25, AEM26/27 Data-quality flag
Site health nominal AEM23 Raise maintenance alert on fatal codes
Every RRE01 within 1..AEM24+AEM26 Sequence Data-quality flag
Duplicate RRE01 also carries RRE08 = M Mismatch rule Log unexpected duplicate

Open Items

  • Confirm per-vendor CR/LF and trailing-delimiter treatment in the EOC01 byte count before enforcing rejection.
  • Confirm whether source sites emit the RTD and TDM segments, or legacy output without them. This determines whether their absence is an error or an expected condition.
  • Register the delimiter character actually used per site (space vs asterisk) in site configuration rather than detecting it per file.

References

  • AAR Manual of Standards and Recommended Practices, Standard S-9203A.V1.1 — AEI Site-to-Host Consist Report Format (formerly S-918A). Adopted 2003, revised 2020.
  • AAR MSRP Standard S-9203 (latest version) — tag data formats. Appendices A and B for RRE, Appendix E for EOT, Appendix P for raw reader-to-processor data (RTD).
  • Appendix A, Section 1.0 of S-9203A — Equipment Group Code values.

This page is an implementation summary. The AAR MSRP is licensed, copyrighted material

Examples

Example Consists