PIS - Passenger information systems

  • The topic PIS contains the interaction between board computer, displays and announcements inside and outside the vehicle. In special, it contains the content (stops, routes, etc.), which of course vary by map and operator.

    1 PIS group

    A PIS group currently contains stops, special chars and a route list of a traffic area. PIS groups are created in the Content Tool (see "Add. map content"). A traffic area is a closed system of stops and routes, for example the "Tram" division in Berlin.

    1.1 General and special PIS groups

    Base and requirement for every PIS group are the general PIS files and data. Those contain a standard set of information for every stop, every special char code and every other data. Thus, every map creator, board computer creator or display creator is provided with a default base so that - if everybody sticks to it - without the slightest amount of work, every vehicle on every map can use the map's local destinations, routes, etc.


    The keep the work manageable, the information per stop, route etc. are reduced to minimum, so that information e.g. for special effects (inverting, additional data, colors, ...) are not defined in the basic PIS file. That's why we implemented an additional concept named special PIS data and files. Those are on the one hand always linked to a basic PIS file and extend it, on the other hand, those are linked to and called by the board computer and display system by a so-called class.


    It is very important that the corresponding board gadgets have to be able to work without a special PIS file, as well, they have to use them as a fallback level.


    Example usage general PIS:


    Example usage special PIS:

    (click me, I am a GIF!)

    1.2 Stop list

    The stop list contains every stop, hence those, who are the last stop of a line and have a destination code, as well as those, which are exclusively used by the routes.

    1.3 Special chars

    To provide the line displays with special chars and symbols, the PIS devices usually offer the ability to enter a special char code, which amends the line number or replaces it. Therefore the PIS groups contain a coding table.

    1.4 Routes

    If there were only a line and a destination, the PIS would not have stop information and, furthermore, no traffic light requests or automatic switching could be done. In addition, the driver would have to make every destination change manually. All those information are contained in the routes. Those contain two number codes - the line code and the route code - a string, a list of stops, optionally alternative or changing destinations, following routes and a special char code, which is set automatically upon choosing the route.

    1.5 ITCS/RBL server

    To ensure that the assignment of the FIS/RBL device in the vehicle to the routes stored in the timetable also works when several modes of transport are simulated at the same time (or several transport companies), it is possible to run several independent RBL/ITCS servers on one card. In order to identify them unambiguously, the responsible server name is stored in the timetable as well as in the basic FIS group.

    2 Creating and editing of general PIS groups

    PIS groups - base files as well as special files - are created and edited in the ContentTool. The tool opens under "Add. map content" and there "PIS group (general)" or "PIS group (special)".


    The basic principles of the tool (loading, saving, packing, ContentIDs, etc.) are the same as in the other tools.



    On the left side, the stop list is located - destination stops as well as all other stops. In the left column, the assigned code is listed, afterwards, the string given with "ID". If it is not a destination stop, so that there is no code assigned, the first column shows a (H) symbol. If a code is assigned multiple times, the column shows a double circle. Above the list is a search field.

    2.1 Stops

    If a stop is selected, its data can be changed below the list - those are applied during editing, which is why there is no OK or Cancel button. Every stop contains the following parameters:

    • ID: Is used for internal management, e.g. in the MapEditor. If this is a destination where no passengers are to be transported, please enter "Service" here.
    • Destination code: is used for the access of the PIS device and corresponds to the real destination code. It has to be between 1 and 65535. As soon as an illegal input is made, the string is crossed out. In this case, just correct the string. If a non-destination-code is edited, the code may remain empty, which leads to an automatically generated internal code. In this case, the code should not be changed afterwards, since there may already exist references to the generated code, which could be lost.
    • Multiple strings, which can be used in the script. We strongly recommend to stick to the convention to keep the basic PIS group and PIS devices compatible with each other:
      • Stop display: The string should be used for inside displays. The inside displays should be able to handle strings of any length (e.g. with an automatic switching)
      • Destination display - one line: Designated for outside displays, which can only handle one line. We recommend a restriction to 16 chars, so that the PIS group is compatible with the single-char-flipdot-displays of the first generation.
      • Destination display - two lines: Like above, but of course for two-line displays. Both lines can be used by the script either with a CRLF char or individually.

    2.2 Special chars

    In the middle of the window there is the list for special chars. You edit this list like you did with the stop list. Each special char consists of a number (between 1 and 65535) and sa string, which describes the processing of the line number. The pattern for this string looks like the following:

    • Chars and symbols are shown normally
    • Sections in brackets are interpreted as the following:
      • (#) Adds the whole line number at this point
      • (L2-R1) Adds only a part of the line number, in this case beginning at the second chars from the left and ending at the first char from the right. The control chars "R" and "L" can be used at both sided with no restrictions.
      • \( Adds a simple beginning bracket (dismantles the rules written before)

    Example: The string "M(R2-R1)" and the line number 123 return the display of "M23". When using the line number 1234, the display would show "M34". If the string is "M(L2-L3)" and the line number 123, the display would show "M23"; when using 1234 as the line number, the display remains the same: "M23".

    2.3 Routes

    On the right you can find the area to define routes. The list entries describe the route and their attributes a bit. At first there is written the line number (with special chars) and the route code, followed by the start stop and the last stop as well as the listing of the stop between start and end.

    Every route has two codes for its classification. On the one hand you've got the line number and on the other hand you've got the route code (between 1 and 65535). The route code might not be unique, because you explicit name a line number. So each line has "their" route 1 and route 2 and so on. The next information is the foloowing route, if there is no line number set, LOTUS implies, that the line number doesn't change. If the line number/route combination can be found, the texfields will appear in bold, otherwise they appear in italic.


    The next part are the special chars code, which will be set automatically, when the route is selected. To set te code you chose the special char from the list in the middle and click on the angled arrow. The code will be applied and displayed. Using the score through circle you delete the special char. The line number will be displayed normal.


    Moving on to the stop list. This list should also contain the start stop and the end stop. The enter a stop you have to select a stop from the left and click on "+". Using the arrow buttons you can change the order of the stops.


    To the left there is the list with optional destinations. The method is the following: Every entry you add to this list has an alternative desination and the information, from whcih stop on the route this destination should branch off. Example: The TXL in Berlin drives from Tegel airport to Memhardstrasse near Alexanderplatz and drives past the central station of Berlin. To not misguide tourists the bus displays "Alexanderplatz via Hbf" and when passing by the central station the display changes to "Alexanderplatz". Before reaching the stop at Alexanderplatz the display changes to "Memhardstr." and when the last stop is reached the display changes to "Fahrt endet hier" - overall this is of course one route. For this purpose the list for destinations has to be set as the following: At first you add the special stop "Alexanderplatz via Hauptbahnhof" in the stop list. This stop also has to have its own code. Then you select the stop within the list and click on"+" under destination stops. The selected destination will be set and after the name of the destination the first stop of this route is written in brackets (in this case it is Tegel airport). So the vehicle won't display the destination "Memhardstraße" since the first stop (this is the case when the list is empty), but it will display "Alexanderplatz via Hauptbahnhof". Now you select the stop "Alexanderplatz" in the stop list, click on "+" and the arrow down button till the the name in brackets behind is "Hautpbahnhof". This means the display shows "Alexanderplatz via Hauptbahnhof" till the last stop before "Hautpbahnhof" is reached. When the stop switches to "Hauptbahnhof" the display changes. Like you did before you select the stop "Memhardstr." from the list, click on "+" move it down till "(Alexanderplatz)" and set the destination "Fahrt endet hier", which will be moving down to the entry "(Memhardstr.)".


    The last textfield can be used without restriction, but has no explicit function.

    3 Create and edit special PIS files

    The special PIS files are completely independant content elements and are more like an addition. Every special PIS file contain the ContentID of the bas PIS group and a string, die "class", which the script is using to access this PIS file. Saying it a bit different, the script expects some information when calling a concrete class, so the special PIS file should contain these data.


    By the way the script can get information from several classes, if the base PIS group and the special PIS group know these classes. There is no restriction to a set class.


    The tool to create and edit the special PIS file will be started by using the Content Tool -> "add. map content -> PIS group (special)



    The interface of this tool is basically the same as you use for basic PIS files. The main difference is the possibility to add unlimited amount of text for every stop, every route or for the whole PIS file itself. So you can add addtional information, e.g. alternative displays, additional special chars, ContentIDs, marker for special effects and so on. This is the point you clearly see the impact of the class information. You can set, that the PIS data of a class has a special char in the third row. This means, that all PIS files of the same class use this rule and can't change this system. The programmer of the PIS device sets convention for these classes.


    The text can be written as you do with the other data. The script can check the data of the row by the information about the class.


    The next function of special PIS files is the possibility to add stop, special chars and routes or even to replace the entry from the base PIS file. When using the replacement the type of information will be added and has the same code. Afterwards there is a cross in front of the deleted entry. The overriding entry will be marked with a star.

    4 Script

    Especially the PIS main unit has to get access to all data of the current selected PIS group files, e.g. to read the strings and send them to the displays. For all these purposes, you have the following script functions:


    Basic PIS:

    • PIS_GetStationMainListIndexByTerminusCode(self: integer; code: integer): integer; This function searchs the list index of the station in the station list of the current PIS group using its code.
    • PIS_GetStationStdString(self: integer; stationmainlistindex: integer; stringindex: integer): string; This function returns one of a station's standard strings. stationmainlistindex is the index of the station in the "main list" (which you can get with the previous function) and stringindex selects the string: 0 = PIS main unit and inside display string, 1 = single-line string for the outside displays, 2 = the two-line string (both lines, separated by "carriage return/line feed"), 3 and 4 = the two lines of the two-line string separately.
    • PIS_GetStationID(self: integer; stationmainlistindex: integer): string; This function returns the ID stored in the standard file, i.e. the string used to identify this stop in the timetables.
    • PIS_GetSpecialChar(self: integer; code: integer; lineinput: string): string; This function generates the final line/special char string using the special char code and the lineinput. E.g.: If there is a special char with code 34 and string "M(R2-R1)" in the list, the function call PIS_GetSpecialChar(Self, 34, '123') would return the string "M23".
    • function PIS_GetRouteIndexByCode(self: integer; line, code: integer): integer; This function returns the list index of the route with the line and route code given.
    • function PIS_GetRouteString(self: integer; routeindex: integer): string; This function returns the string of the route with index routeindex.
    • function PIS_GetRouteSpecialCharCode(self: integer; routeindex: integer): integer; This function returns the route's special char code.
    • function PIS_GetRouteStopCount(self: integer; routeindex: integer): integer; This route returns the route's count of stops, including the start and the terminus stop.
    • function PIS_GetRouteStopCode(self: integer; routeindex: integer; stopindex: integer): integer; This function returns the station code of the station at the given stop list entry. Attention: You really get the code, not the index! To get the index (e.g. for the function PIS_GetStationStdString), you will need the function PIS_GetStationMainListIndexByTerminusCode.
    • function PIS_GetRouteTerminusCode(self: integer; routeindex: integer; stopindex: integer): integer; This function returns the terminus which should be enabled at the given index in the route's stop list.
    • procedure PIS_GetRouteFollowingCode(self: integer; routeindex: integer; out line: integer; out code: integer); This procedure writes the IDs of the route, which is following the route routeindex, into the parameters line and code.
    • function PIS_LineRouteCount(self: integer; line: integer): integer; This function can be used to determine the number of routes belonging to the specified line.
    • function PIS_GetStationCode(self: integer; stationmainlistindex: integer): integer; This can be used to get the code of a given station. It is therefore the "counter-function" to PIS_GetStationMainListIndexByTerminusCode.
    • function PIS_GetRouteLine(self: integer; routeindex: integer): integer; returns the line belonging to the route specified by the index.
    • function PIS_GetRouteCode(self: integer; routeindex: integer): integer; returns the code belonging to the route specified by the index.
    • procedure PIS_GenerateTempRouteListByLine(self: integer; line: integer): integer; This procedure creates a temporary list of all routes belonging to the selected line in the background. Only one list (per object / script) can exist at a time. As soon as the procedure is called again, the old list is deleted.
    • procedure PIS_SortTempRouteList(self: integer); This procedure sorts the temporary route list by code.
    • function PIS_GetTempRouteListCount(self: integer): integer; returns the number of entries in the temporary route list.
    • function PIS_GetTempRouteListItemIndex(self: integer; templistindex: integer): integer; returns the route index (not code!) stored in the temporary route list at the "templistindex" position.
    • function PIS_GetITCSServer(self: integer): string; This function returns the name of the ITCS server. The ITCS on-board unit requires this information to log on to the correct ITCS server.

    Special PIS:

    • function PISSP_GetIndexByClass(self: integer; classid: string): integer; This function returns the index of the special PIS file, which fits the the current base PIS group and to the class classid. If there is no fitting special PIS file, it will return "-1".
    • procedure PISSP_SetByIndex(self: integer; index: integer); This procedure sets the special PIS file identified with index. From now on all previous functions and procedures will take all station, special char and route list changes into account and will be used for all the next described functions. Index may be received by the previous function "PISSP_GetIndexByClass".
    • function PISSP_GetGroupString(self: integer; stringindex: integer): string; This function returns the additional strings line with index stringindex from the currently set special PIS file.
    • function PISSP_GetStationString(self: integer; stncode: integer; stringindex: integer): string; This function returns the additional strings line with index stringindex from the given station stncode of the currently set special PIS file.
    • function PISSP_GetRouteString(self: integer; routelinecode, routecode: integer; stringindex: integer): string; This function returns the additional strings line with index stringindex from the given route routelinecode/routecode of the currently set special PIS file.
    • function PISSP_GetRouteStopString(self: integer; routelinecode, routecode: integer; stopindex: integer; stringindex: integer): string; This function returns the additional strings line with index stringindex from the given stop in the stop list of the route routelinecode/routecode of the currently set special PIS file.

    5 Announcements

    Announcements are basically realised as follows: The *.wav files are imported as so-called "Independed sounds". Thereby everyone of them gets its own ContentID. In addition, a special PIS file is created, which assigns the sound ContentIDs to the corresponding stop. The PIS device script receives the ContentIDs from the special PIS file, uses it to assign the sound to a speaker and hence starts playing. In detail:

    5.1 Import of the announcement sounds

    The announcements have to be in *.wav format. All sounds that shall appear in one container later have to be in a single folder together. Since the alphabetical order of the files is essential for the order of ContentIDs, the files should be named with a gapless sequence of numbers, which should be the last digits of the desired ContentIDs, including leading zero, e.g.:

    • 01_TownHall.wav
    • 02_School.wav
    • 03_Church.wav

    Those are only recommendations, not conditions. The order of the files has nothing to do with the order of the stops in the PIS file, only with the assignment of ContentIDs. Which ContentID will be which stop announcement will be set later.


    The next step is to start the ContentTool and choose "Independent sounds". The Tool now asks for a folder to be imported and the ContentID to start with at the alphabetical first sound. Its last digits should match the beginning of the file name, as recommended. Should one or more of the planned ContentIDs already be in use for other content, a warning appears. Please note: This means that with another import of maybe already imported sounds, the corresponding container in the MyContent folder should be deleted first! Otherwise, there will be another ContentID conflict.


    Before the actual import another confirmation is required. After successful import, the container need to be packed. Therefore, just use the "Pack container" feature in the ContentTool. Now all of the imported sounds should be available with their new ContentID.

    5.2 Creating a special PIS file

    Creating a special PIS file has already been explained. If you want to be compatible to our PIS system, the file has to follow this convention:

    • Class: "ANNOUNCEMENT"
    • The stops in the left list, which shall receive an announcement, are selected and below in the additional strings the ContentIDs are enteres: The UserID in the first line, the ContentSubID in the second line.
    • In case that on a special route a dedicated stop should use another announcement (e.g. terminus stops), the announcements are entered in a similar way, as additional string on the right side.

    This looks like the following:


    5.3 Script

    The announcements in the GT6N are separated to the IBIS script and the main script - for the reason that the speaker belongs to the vehicle and the announcements belong to the IBIS. This is why there are two script additions:

    5.3.1 PIS device

    The PIS device (e.g. IBIS) does nothing more than to read in the exact moment of the announcement the ContentID of the sound and forwarding it via broadcast to the vehicle. The actual implementation can be consulted in the OpenSource folder of LOTUS, IBIS_GT6N.pas file. The most important steps are:

    • reading the index of the special PIS file and write into a variable: Announcement_PISSP := PISSP_GetIndexByClass(Self, 'ANNOUNCEMENT');
    • upon calling the announcement, setting the special PIS file: PISSP_SetByIndex(Self, Announcement_PISSP);
    • with s1 := PISSP_GetRouteStopString(Self, LinieKurs div 100, Route, StopSeq, 0); and s2 := PISSP_GetRouteStopString(Self, LinieKurs div 100, Route, StopSeq, 1);* reading the two strings that contain the ContentID, in case a ContentID was set on the "right -->" side before, for a special stop of a dedicated route
    • checking with (s1 <> '') and (s2 <> '') that the strings contain content. If not, repeating with s1 := PISSP_GetStationString(Self, StopSeqCode, 0); and s2 := PISSP_GetStationString(Self, StopSeqCode, 1); checking the "left <--" list for strings
    • cast integers from the strings: i1 := strToInt(s1) and i2 := strToInt(s2)**
    • sending with SendBroadcastInteger(Self, 'PIS', 'ANNOUNCE_USERID', NextAnnouncement_UserID); and
      SendBroadcastInteger(Self, 'PIS', 'ANNOUNCE_SUBID', NextAnnouncement_SubID);*** both ContentID parts to the vehicle

    *) Our IBIS device uses "LinieKurs" (line and block) as you type it in: attaching the block numbers to the line numbers. For this reason, this variable has to be devided by 100 before forwarding it, so it matches the line number in the PIS route.

    **) The real IBIS script uses a try except block around the casts to prevent the script from stop running in case of an invalid cast.

    ***) The real IBIS script instead starts a timer at this point and hence both commands are executed with the end of the Timer (at another place in the script).

    5.3.2 Vehicle

    The vehicle needs a speaker sound source, which only contains one sound, doesn't matter, which. This sound source is now placed in the vehicle and receives a name, like "insideSpeaker".


    Thats the script of the vehicle:


    The variable Announcement_UserID is defined globally: The first broadcast sends the ContentUserID and the second one, who sends the ContentSubID, actually starts the announcement.


    The really important command is SndSetIndependent(Self, Sound name, Sound index inside sound source, ContentUserID, ContentSubID);, which assigns the sound ContentID to the sound source and hence replaces its standard sound, as soon as Snd_Announcement is set to 1 (which is the control variable that is assigned to the sound source).

    6 Special Symbols

    Symbols like transport company logos, stadiums, footballs, airplanes etc. are realized by selecting a fitting Unicode char, which then has to be included in the used bitmap fonts and the PIS file.


    Important: While working with the font or the PIS tools, you have not to use the unicode codes for themselves, but copy and paste the symbols directly into the dialog fields (e.g. from the table underneath or using the Windows character map tool).


    To standardize everything, you will find here a unicode table which should be used by everyone. Of course we will add further symbols if you propose one. Our special service is adapting our GT6N flip dot displays occasionally to this table.

    6.1 Included in our flip dot displays


    Symbol Description Unicode Character Unicode
    Airport U+2708
    Triangle U+25B3
    Pupils 🚸
    U+1F6B8
    Park and Ride / P+R 🅿
    U+1F17F
    DB Logo (German railroad company) U+2114
    Soccer
    U+26BD
    Circle arrow clockwise U+21BB
    Circle arrow counterclockwise U+21BA
    BVG Logo (Berlin public transport company) U+2422
    Brandenburger Tor U+220F
    Cross U+2715
    Detour arrow U+21AC
    "U" in square (German subway logo) 🚇 U+1F687
    "S" in circle (German suburban railroad logo) 🚈 U+1F688
    Cup of coffee U+2615
    Tram (side view) 🚃 U+1F683
    Grinning face 😀 U+1F600
    Winking face 😉 U+1F609
    Face with stuck-out tongue 😛 U+1F61B
    (Oriolus) Bird 🐦 U+1F426
    Upside down pentagram U+26E7
    Logo "RhönENERGIE" U+0C6F
    Workshop 🔧 U+1F527
    Beer Glass 🍺 U+1F37A
    Oxcart 🐂 U+1F402

    6.2 Not included in our fonts, but reserved

    Symbol Description Unicode Character Unicode
    Letters in circle Ⓐ - Ⓩ U+24B6 - U+24CF
    Figures in circle ① - ⑳ U+2460 - U+2473
    Letters in circle "negative" 🅐 - 🅩 U+1F150 - U+1F169
    Letters in square "negative" 🅰 - 🆉 U+1F170 - U+1F189
    Northeast plane 🛪 U+1F6EA
    Bus in circle 🚍 U+1F68D
    Tram in square 🚊 U+1F68A
    Superscript "A" U+00AA
    7/ U+2501
    5/6 U+2502
    14/15 U+2503
    5/13 U+2504
    5/18 U+2505
    / ("Fraction Slash") U+2044
    24/26 U+2506
    1/20 U+2507
    2/3 U+2508
    6/9 U+2509
    9/18 U+250A
    22/24 U+250B
    23/24 U+250C
    23/26 U+250D
    25/26 U+250E
    SB-Logo U+2101
    Logo "Wiener Linien" U+20A9
    "S" (Austrian version) U+2A93
    "U" (Austrian version) U+FE3C
    Logo of rail replacement bus service U+23E3
    Metro bus logo U+2133
    Bus symbol sidelong 🚐 U+1F690
    Inverted circle U+25D8
    Santa Claus 🎅 U+1F385
    Christmas Tree 🎄
    U+1F384
    Four-Leaf Clover 🍀 U+1F340
    Filled triangle pointing upwards U+23F6
    Filled square U+2BC0
    Up arrow to the left of Down arrow U+21C5
    VDV symbol
    U+2123
    DVB logo U+2145
    Clinking Glasses 🥂 U+1F942
    Logo of Dresdener Verkehrsbetriebe U+15EF
    Silhouette of Dresden 🏙 U+1F3D9
    Zwingerturm (Dresden) U+2A4E