From 78340d3ca8fdc5d801fa0546b74092fc649cc750 Mon Sep 17 00:00:00 2001 From: Martin Scheidt <142348+kaat0@users.noreply.github.com> Date: Thu, 2 Jan 2025 23:18:20 +0100 Subject: [PATCH 01/24] updated creditials --- LICENSE | 2 +- README.md | 4 +--- 2 files changed, 2 insertions(+), 4 deletions(-) diff --git a/LICENSE b/LICENSE index 5d6f1b4..26ded92 100644 --- a/LICENSE +++ b/LICENSE @@ -1,6 +1,6 @@ ISC License -Copyright (c) 2022, Martin Scheidt \ +Copyright (c) 2022 - 2024, Martin Scheidt (orcid.org/0000-0002-9384-8945) Permission to use, copy, modify, and/or distribute this software for any purpose with or without fee is hereby granted, provided that the above copyright notice and this permission notice appear in all copies. diff --git a/README.md b/README.md index ec95285..cea500c 100644 --- a/README.md +++ b/README.md @@ -64,10 +64,8 @@ - - See [CONTRIBUTING.md](https://github.com/railtoolkit/schema/blob/main/CONTRIBUTING.md) file if you are interested to contribute. @@ -78,7 +76,7 @@ See [CONTRIBUTING.md](https://github.com/railtoolkit/schema/blob/main/CONTRIBUTI [![Open Source Initiative Approved License logo](https://149753425.v2.pressablecdn.com/wp-content/uploads/2009/06/OSIApproved_100X125.png "Open Source Initiative Approved License logo")](https://opensource.org) - Copyright (c) 2022, Martin Scheidt \ (ISC License) + Copyright (c) 2022 - 2024, Martin Scheidt (orcid.org/0000-0002-9384-8945) (ISC License) Permission to use, copy, modify, and/or distribute this software for any purpose with or without fee is hereby granted, provided that the above copyright notice and this permission notice appear in all copies. From a0420cc5b24acd610bbccd90c8990967d5ddc2b8 Mon Sep 17 00:00:00 2001 From: Martin Scheidt <142348+kaat0@users.noreply.github.com> Date: Thu, 2 Jan 2025 23:42:16 +0100 Subject: [PATCH 02/24] extended "points_of_interest --- doc/Running-Path.md | 22 ++++++++++-------- doc/running-path.example.yaml | 44 ++++++++++++++++++++++------------- src/running-path.json | 17 ++++++++++++-- 3 files changed, 55 insertions(+), 28 deletions(-) diff --git a/doc/Running-Path.md b/doc/Running-Path.md index e95f510..a44af18 100644 --- a/doc/Running-Path.md +++ b/doc/Running-Path.md @@ -17,12 +17,12 @@ All attributes for paths are collected under the array `paths: -` in alphabetical order: | Attributes | Necessity | Description | | ------------------------- | --------- | ------------------------------------------------------------------------------ | -| `characteristic_sections` | required | An array of [characteristic sections](#Attributes-in-characteristic-sections). | -| `id` | required | Identifier of the path (can be a UUID). | +| `characteristic_sections` | required | An array of [characteristic sections](#Attributes-in-characteristic_sections). | +| `id` | required | Identifier of the path. | | `description` | optional | Description of the path. | -| `points_of_interest` | optional | An array of [points of interest](#Attributes-in-points-of-interest). | +| `points_of_interest` | optional | An array of [points of interest](#Attributes-in-points_of_interest). | -## Attributes in "characteristic sections" +## Attributes in "characteristic_sections" All attributes for a characteristic section are collected under the array `characteristic_sections: -` sorted in ascending or descending order: @@ -34,11 +34,13 @@ All attributes for a characteristic section are collected under the array `chara [^1]: At least one optional attribute must be present. -## Attributes in "points of interest" +## Attributes in "points_of_interest" All attributes for a point of interest (poi) are collected under the array `points_of_interest: -` sorted in ascending or descending order: -| Attributes | Necessity | Description | -| ------------ | --------- | -------------------------------------------------------------------| -| `position` | required | mileage in meter | -| `label` | required | name for the point | -| `measure` | required | measurement applies to the `front` , `middle` or `rear` of a train | +| Attributes | Necessity | Description | +| ------------ | --------- | ------------------------------------------------------------------| +| `position` | required | mileage in meter | +| `id` | required | Identifier of the point | +| `description`| optional | Description of the point | +| `groups` | optional | Array of group names this point belongs to | +| `measure` | required | measurement applies to the `front`, `middle` or `rear` of a train | diff --git a/doc/running-path.example.yaml b/doc/running-path.example.yaml index d14d490..fb73595 100644 --- a/doc/running-path.example.yaml +++ b/doc/running-path.example.yaml @@ -6,24 +6,36 @@ paths: - id: example description: "Example" points_of_interest: - - position: 850.00 # mileage in meter - label: "1:view point" # label name - measure: front # front, middle or rear of the passing train + - position: 850.00 + id: "vp11" + description: "Viewpoint for distant signal 11" + groups: ["block 11", "view points"] + measure: front - position: 1000.00 - label: "1:distant signal" - measure: front + id: "ds11" + description: "Distant signal for main signal 11" + groups: ["block 11", "signals"] + measure: front - position: 2000.00 - label: "1:main signal" - measure: front - - position: 5500.00 - label: "A:platform" - measure: middle - - position: 9000.00 - label: "3:main signal" - measure: front - - position: 9050.00 - label: "1:clearing point" - measure: rear + id: "cs11" + description: "combined signal 11" + groups: ["block 11", "block 13", "signals"] + measure: front + - position: 2500.00 + id: "platform-a" + description: "Platform A" + groups: ["platforms", "station"] + measure: middle + - position: 3000.00 + id: "ms13" + description: "Main signal 13)" + groups: ["block 13", "signals"] + measure: front + - position: 3050.00 + id: "cp11" + description: "Block clearing point" + groups: ["block 11", "clearing points"] + measure: rear characteristic_sections: - position: 0.0 # mileage in meter speed: 160 # speed limit in km/h, diff --git a/src/running-path.json b/src/running-path.json index 2060801..a7dcd82 100644 --- a/src/running-path.json +++ b/src/running-path.json @@ -69,7 +69,7 @@ "type": "object", "required": [ "position", - "label", + "id", "measure" ], "properties": { @@ -77,9 +77,22 @@ "description": "mileage in meter", "type": "number" }, - "label": { + "id": { + "description": "Identifier of the point of interest", "type": "string" }, + "description": { + "description": "Description of the point of interest", + "type": "string" + }, + "groups": { + "description": "Groups this point belongs to", + "type": "array", + "items": { + "type": "string" + }, + "uniqueItems": true + }, "measure": { "enum": [ "front", "middle", "rear" ] } From 618d4d11f697fa5dc5aa478ec5eae782257a5b08 Mon Sep 17 00:00:00 2001 From: Martin Scheidt <142348+kaat0@users.noreply.github.com> Date: Fri, 3 Jan 2025 03:27:32 +0100 Subject: [PATCH 03/24] new tests + added track attribute to characteristic sections --- CHANGELOG.md | 9 +-- README.md | 72 ++++++++++--------- doc/Running-Path.md | 13 ++-- package.json | 4 +- src/running-path.json | 15 ++-- test/running-path/invalid/consistency.yaml | 20 ++++++ test/running-path/invalid/invalid-enums.yaml | 11 +++ test/running-path/invalid/logical-errors.yaml | 10 +++ .../invalid/missing-required.yaml | 8 +++ test/running-path/invalid/wrong-types.yaml | 11 +++ test/running-path/valid/complete.yaml | 27 +++++++ test/running-path/valid/edge-cases.yaml | 23 ++++++ test/running-path/valid/minimal.yaml | 11 +++ 13 files changed, 187 insertions(+), 47 deletions(-) create mode 100644 test/running-path/invalid/consistency.yaml create mode 100644 test/running-path/invalid/invalid-enums.yaml create mode 100644 test/running-path/invalid/logical-errors.yaml create mode 100644 test/running-path/invalid/missing-required.yaml create mode 100644 test/running-path/invalid/wrong-types.yaml create mode 100644 test/running-path/valid/complete.yaml create mode 100644 test/running-path/valid/edge-cases.yaml create mode 100644 test/running-path/valid/minimal.yaml diff --git a/CHANGELOG.md b/CHANGELOG.md index 5144056..f07cfd1 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -11,11 +11,15 @@ Categories: Added, Changed, Deprecated, Removed, Fixed, and Security. ### Added * running path: * added `description` attribute (see Issue #5) + * added `track` to characteristic sections for grouping track sections + * added `groups` array to points_of_interest for categorizing points + * added comprehensive test suite with valid and invalid test cases + * added example file with block sections and signals ### Changed * running path: * running path arrays now contain named attributes (see Issue #4) - * when using `characteristic_sections` only one optional attribute (`speed` or `resistance`) must be specified + * characteristic sections now require at least one of `speed`, `resistance`, or `track` attributes * measures for `points_of_interest` can now take three values: "front", "middle", and "rear" ### Removed @@ -34,15 +38,12 @@ Categories: Added, Changed, Deprecated, Removed, Fixed, and Security. * renamed `train` into `trains` and changed type to array (see Issue #2) * renamed `path` into `paths` and changed type to array (see Issue #2) - ## Version [2022.04] ### Added - * initial rolling-stock schema * initial running-path Schema - [Unreleased]: https://github.com/railtoolkit/schema/compare/2022.05...main [2022.05]: https://github.com/railtoolkit/schema/compare/2022.04...2022.05 [2022.04]: https://github.com/railtoolkit/schema/releases/tag/2022.04 \ No newline at end of file diff --git a/README.md b/README.md index cea500c..f8b251c 100644 --- a/README.md +++ b/README.md @@ -6,49 +6,53 @@ ## About - This repo collects the descriptions of the structure and the validation constraints of tools in the railtoolkit in JSON schemas. It is, therefore, an alternative to [RailML](https://www.railml.org/). The JSON schemas enable the validation of YAML files in [TrainRun.jl](https://github.com/railtoolkit/TrainRun.jl.git) and [rolling-stock](https://github.com/railtoolkit/rolling-stock.git). +This repo collects the descriptions of the structure and the validation constraints of tools in the railtoolkit in JSON schemas. It is, therefore, an alternative to [RailML](https://www.railml.org/). The JSON schemas enable the validation of YAML files in [TrainRun.jl](https://github.com/railtoolkit/TrainRun.jl.git) and [rolling-stock](https://github.com/railtoolkit/rolling-stock.git). ## Prerequisite - You will need a validator to validate the schema against data. This package provides a helper script that uses the [Ajv JSON schema validator](https://ajv.js.org). - Ajv rquires to have [node](https://nodejs.org/) installed. +You will need a validator to validate the schema against data. This package provides a helper script that uses the [Ajv JSON schema validator](https://ajv.js.org). +Ajv requires to have [node](https://nodejs.org/) installed. - ```bash - $ node --version # test if node is installed - ``` +```bash +$ node --version # test if node is installed +``` ## Usage - You will need the schema and some data. The repo contains among others the rolling-stock schema and example data: - ```bash - $ git clone https://github.com/railtoolkit/schema.git && cd schema - ``` - - Install all project dependencies: - ```bash - $ npm install - ``` - - You can now validate if the data follows the schema: - ```bash - $ npm run validate:rolling-stock doc/rolling-stock.example.yaml - ``` - This will return: - ```bash - $ doc/rolling-stock.example.yaml valid - ``` - Or: - ```bash - $ npm run validate:running-path doc/running-path.example.yaml - ``` - This will return: - ```bash - $ doc/running-path.example.yaml valid - ``` +You will need the schema and some data. The repo contains among others the rolling-stock schema and example data: +```bash +$ git clone https://github.com/railtoolkit/schema.git && cd schema +``` + +Install all project dependencies: +```bash +$ npm install +``` + +You can validate if the data follows the schema: +```bash +$ npm run validate:rolling-stock doc/rolling-stock.example.yaml +$ npm run validate:running-path doc/running-path.example.yaml +``` + +## Testing + +The repository includes comprehensive test suites for both schemas: + +```bash +$ npm run test # Run all tests +$ npm run test:stock # Run rolling-stock tests only +$ npm run test:paths # Run running-path tests only +``` + +Each test suite includes: +- Example file validation +- Valid test cases +- Invalid test cases ## Documentation - see [Rolling-Stock.md](https://github.com/railtoolkit/schema/blob/main/doc/Rolling-Stock.md) and [Running-Path.md](https://github.com/railtoolkit/schema/blob/main/doc/Running-Path.md) for information about the used attributes. +See [Rolling-Stock.md](doc/Rolling-Stock.md) and [Running-Path.md](doc/Running-Path.md) for information about the used attributes. ## Contributors @@ -68,7 +72,7 @@ -See [CONTRIBUTING.md](https://github.com/railtoolkit/schema/blob/main/CONTRIBUTING.md) file if you are interested to contribute. +See [CONTRIBUTING.md](CONTRIBUTING.md) file if you are interested to contribute. ------------ diff --git a/doc/Running-Path.md b/doc/Running-Path.md index a44af18..2fa1d20 100644 --- a/doc/Running-Path.md +++ b/doc/Running-Path.md @@ -17,12 +17,12 @@ All attributes for paths are collected under the array `paths: -` in alphabetical order: | Attributes | Necessity | Description | | ------------------------- | --------- | ------------------------------------------------------------------------------ | -| `characteristic_sections` | required | An array of [characteristic sections](#Attributes-in-characteristic_sections). | +| `characteristic_sections` | required | An array of [characteristic sections](#Attributes-in-characteristic-sections). | | `id` | required | Identifier of the path. | | `description` | optional | Description of the path. | -| `points_of_interest` | optional | An array of [points of interest](#Attributes-in-points_of_interest). | +| `points_of_interest` | optional | An array of [points of interest](#Attributes-in-points-of-interest). | -## Attributes in "characteristic_sections" +## Attributes in "characteristic sections" All attributes for a characteristic section are collected under the array `characteristic_sections: -` sorted in ascending or descending order: @@ -31,10 +31,11 @@ All attributes for a characteristic section are collected under the array `chara | `position` | required | mileage in meter | | `speed` | optional[^1] | speed limit in kilometers per hour | | `resistance` | optional[^1] | resistance in permil | +| `track` | optional | identifier for grouping sections | [^1]: At least one optional attribute must be present. -## Attributes in "points_of_interest" +## Attributes in "points of interest" All attributes for a point of interest (poi) are collected under the array `points_of_interest: -` sorted in ascending or descending order: | Attributes | Necessity | Description | @@ -44,3 +45,7 @@ All attributes for a point of interest (poi) are collected under the array `poin | `description`| optional | Description of the point | | `groups` | optional | Array of group names this point belongs to | | `measure` | required | measurement applies to the `front`, `middle` or `rear` of a train | + +# Example + +An example file showing a running path with block sections, signals, and platform can be found in [running-path.example.yaml](running-path.example.yaml). The example includes: diff --git a/package.json b/package.json index 4b003b5..d979d27 100644 --- a/package.json +++ b/package.json @@ -7,9 +7,11 @@ "test:stock:example": "ajv test --spec=draft2020 -c ajv-formats -s src/rolling-stock.json -d doc/rolling-stock.example.yaml --valid", "test:stock:valid": "ajv test --spec=draft2020 -c ajv-formats -s src/rolling-stock.json -d \"test/rolling-stock/valid/*.yaml\" --valid", "test:stock:invalid": "ajv test --spec=draft2020 -c ajv-formats -s src/rolling-stock.json -d \"test/rolling-stock/invalid/*.yaml\" --invalid", + "test:stock": "npm-run-all test:stock:**", "test:paths:example": "ajv test --spec=draft2020 -c ajv-formats -s src/running-path.json -d doc/running-path.example.yaml --valid", "test:paths:valid": "ajv test --spec=draft2020 -c ajv-formats -s src/running-path.json -d \"test/running-path/valid/*.yaml\" --valid", - "test:paths:invalid": "ajv test --spec=draft2020 -c ajv-formats -s src/running-path.json -d \"test/running-path/invalid/*.yaml\" --invalid" + "test:paths:invalid": "ajv test --spec=draft2020 -c ajv-formats -s src/running-path.json -d \"test/running-path/invalid/*.yaml\" --invalid", + "test:paths": "npm-run-all test:paths:**" }, "repository": { "type": "git", diff --git a/src/running-path.json b/src/running-path.json index a7dcd82..1b73df6 100644 --- a/src/running-path.json +++ b/src/running-path.json @@ -39,10 +39,13 @@ "uniqueItems": true, "items": { "type": "object", - "anyOf": [ - {"required": [ "position", "speed" ] }, - {"required": [ "position", "resistance" ] }, - {"required": [ "position", "speed", "resistance" ] } + "allOf": [ + {"required": ["position"]}, + {"anyOf": [ + {"required": ["speed"]}, + {"required": ["resistance"]}, + {"required": ["track"]} + ]} ], "properties": { "position": { @@ -57,6 +60,10 @@ "resistance": { "description": "resistance in permil", "type": "number" + }, + "track": { + "description": "identifier for grouping track sections", + "type": "string" } } } diff --git a/test/running-path/invalid/consistency.yaml b/test/running-path/invalid/consistency.yaml new file mode 100644 index 0000000..076fd96 --- /dev/null +++ b/test/running-path/invalid/consistency.yaml @@ -0,0 +1,20 @@ +%YAML 1.2 +--- +schema: https://railtoolkit.org/schema/running-path.json +schema_version: "2024.07" +paths: + - id: "consistency" + points_of_interest: + - position: 2000.0 # Position within characteristic sections range + id: "point1" + measure: front + - position: 500.0 + id: "point2" + measure: front + characteristic_sections: + - position: 0.0 + speed: 160 + - position: 1000.0 + speed: 160 + - position: 500.0 # Decreasing position value (invalid) + speed: 160 \ No newline at end of file diff --git a/test/running-path/invalid/invalid-enums.yaml b/test/running-path/invalid/invalid-enums.yaml new file mode 100644 index 0000000..dcf9017 --- /dev/null +++ b/test/running-path/invalid/invalid-enums.yaml @@ -0,0 +1,11 @@ +%YAML 1.2 +--- +schema: https://railtoolkit.org/schema/running-path.json +schema_version: "2024.07" +paths: + - id: "invalid-enums" + points_of_interest: + - position: 1000.0 + id: "point1" + measure: invalid # Invalid measure value + groups: [] # Empty groups array \ No newline at end of file diff --git a/test/running-path/invalid/logical-errors.yaml b/test/running-path/invalid/logical-errors.yaml new file mode 100644 index 0000000..be764ad --- /dev/null +++ b/test/running-path/invalid/logical-errors.yaml @@ -0,0 +1,10 @@ +%YAML 1.2 +--- +schema: https://railtoolkit.org/schema/running-path.json +schema_version: "2024.07" +paths: + - id: "logical-errors" + characteristic_sections: + - position: 0.0 + speed: -160 + resistance: 1.00 \ No newline at end of file diff --git a/test/running-path/invalid/missing-required.yaml b/test/running-path/invalid/missing-required.yaml new file mode 100644 index 0000000..8e4ecd8 --- /dev/null +++ b/test/running-path/invalid/missing-required.yaml @@ -0,0 +1,8 @@ +%YAML 1.2 +--- +schema: https://railtoolkit.org/schema/running-path.json +paths: + - id: "missing-required" + characteristic_sections: + - position: 0.0 + speed: 160 \ No newline at end of file diff --git a/test/running-path/invalid/wrong-types.yaml b/test/running-path/invalid/wrong-types.yaml new file mode 100644 index 0000000..8baa6de --- /dev/null +++ b/test/running-path/invalid/wrong-types.yaml @@ -0,0 +1,11 @@ +%YAML 1.2 +--- +schema: https://railtoolkit.org/schema/running-path.json +schema_version: "2024.07" +paths: + - id: "wrong-types" + characteristic_sections: + - position: "not a number" + speed: "160" + - position: 1000.0 + resistance: "invalid" \ No newline at end of file diff --git a/test/running-path/valid/complete.yaml b/test/running-path/valid/complete.yaml new file mode 100644 index 0000000..6764da2 --- /dev/null +++ b/test/running-path/valid/complete.yaml @@ -0,0 +1,27 @@ +%YAML 1.2 +--- +schema: https://railtoolkit.org/schema/running-path.json +schema_version: "2024.07" +paths: + - id: "complete" + description: "Complete test case with all fields" + points_of_interest: + - position: 850.00 + id: "vp11" + description: "Viewpoint" + groups: ["block 11", "view points"] + measure: front + - position: 1000.00 + id: "ds11" + description: "Signal" + groups: ["block 11", "signals"] + measure: middle + characteristic_sections: + - position: 0.0 + speed: 160 + resistance: 0.00 + section_id: "line_1" + - position: 1000.0 + speed: 120 + resistance: 1.00 + section_id: "line_1" \ No newline at end of file diff --git a/test/running-path/valid/edge-cases.yaml b/test/running-path/valid/edge-cases.yaml new file mode 100644 index 0000000..3a4f6f9 --- /dev/null +++ b/test/running-path/valid/edge-cases.yaml @@ -0,0 +1,23 @@ +%YAML 1.2 +--- +schema: https://railtoolkit.org/schema/running-path.json +schema_version: "2024.07" +paths: + - id: "edge-cases" + description: "Test with edge cases" + points_of_interest: + - position: 1000.00 + id: "point1" + description: "Unicode test: äöüß" + groups: ["group1", "group2", "group3"] + measure: front + - position: 1000.00 + id: "point2" + measure: rear + characteristic_sections: + - position: 0.0 + speed: 999999 + resistance: 0.00 + - position: 999999.999 + speed: 0.1 + resistance: -999.99 \ No newline at end of file diff --git a/test/running-path/valid/minimal.yaml b/test/running-path/valid/minimal.yaml new file mode 100644 index 0000000..6f9bb01 --- /dev/null +++ b/test/running-path/valid/minimal.yaml @@ -0,0 +1,11 @@ +%YAML 1.2 +--- +schema: https://railtoolkit.org/schema/running-path.json +schema_version: "2024.07" +paths: + - id: "minimal" + characteristic_sections: + - position: 0.0 + speed: 160 + - position: 1000.0 + speed: 160 \ No newline at end of file From 281f3ad4bcb2634e140533df371a9b34079facc1 Mon Sep 17 00:00:00 2001 From: Martin Scheidt <142348+kaat0@users.noreply.github.com> Date: Tue, 7 Jan 2025 23:59:19 +0100 Subject: [PATCH 04/24] extended running path example with track schematics --- doc/Running-Path.md | 4 +- doc/running-path.example.png | Bin 0 -> 69635 bytes doc/running-path.example.tex | 292 ++++++++++++++++++++++++++++++++++ doc/running-path.example.yaml | 187 +++++++++++++++++----- src/running-path.json | 9 +- 5 files changed, 442 insertions(+), 50 deletions(-) create mode 100644 doc/running-path.example.png create mode 100644 doc/running-path.example.tex diff --git a/doc/Running-Path.md b/doc/Running-Path.md index 2fa1d20..418cc91 100644 --- a/doc/Running-Path.md +++ b/doc/Running-Path.md @@ -31,7 +31,6 @@ All attributes for a characteristic section are collected under the array `chara | `position` | required | mileage in meter | | `speed` | optional[^1] | speed limit in kilometers per hour | | `resistance` | optional[^1] | resistance in permil | -| `track` | optional | identifier for grouping sections | [^1]: At least one optional attribute must be present. @@ -47,5 +46,6 @@ All attributes for a point of interest (poi) are collected under the array `poin | `measure` | required | measurement applies to the `front`, `middle` or `rear` of a train | # Example +An example file showing a running path with block sections, signals, and platforms can be found in [running-path.example.yaml](running-path.example.yaml). The example includes various points of interest such as platform tracks, route signals, and clearing points, each with specific positions and descriptions. Below is a visual representation of the YAML structure: -An example file showing a running path with block sections, signals, and platform can be found in [running-path.example.yaml](running-path.example.yaml). The example includes: +![Running Path Example](running-path.example.png) diff --git a/doc/running-path.example.png b/doc/running-path.example.png new file mode 100644 index 0000000000000000000000000000000000000000..b4ce132604ad1e9203c70a19955e9b0a435d8eb7 GIT binary patch literal 69635 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 Date: Tue, 7 Jan 2025 23:59:39 +0100 Subject: [PATCH 05/24] add gitignore --- .gitignore | 475 ++++++++++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 473 insertions(+), 2 deletions(-) diff --git a/.gitignore b/.gitignore index f124cea..07955e1 100644 --- a/.gitignore +++ b/.gitignore @@ -1,23 +1,34 @@ +# Created by https://www.toptal.com/developers/gitignore/api/macos,linux,windows,node,tex +# Edit at https://www.toptal.com/developers/gitignore?templates=macos,linux,windows,node,tex + ### Linux ### *~ + # temporary files which can be created if a process still has a handle open of a deleted file .fuse_hidden* + # KDE directory preferences .directory + # Linux trash folder which might appear on any partition or disk .Trash-* + # .nfs files are created when an open file is removed but is still being accessed .nfs* -### OSX ### +### macOS ### # General .DS_Store .AppleDouble .LSOverride + # Icon must end with two \r Icon + + # Thumbnails ._* + # Files that might appear in the root of a volume .DocumentRevisions-V100 .fseventsd @@ -26,6 +37,7 @@ Icon .Trashes .VolumeIcon.icns .com.apple.timemachine.donotpresent + # Directories potentially created on remote AFP share .AppleDB .AppleDesktop @@ -33,25 +45,484 @@ Network Trash Folder Temporary Items .apdisk +### macOS Patch ### +# iCloud generated files +*.icloud + +### Node ### +# Logs +logs +*.log +npm-debug.log* +yarn-debug.log* +yarn-error.log* +lerna-debug.log* +.pnpm-debug.log* + +# Diagnostic reports (https://nodejs.org/api/report.html) +report.[0-9]*.[0-9]*.[0-9]*.[0-9]*.json + +# Runtime data +pids +*.pid +*.seed +*.pid.lock + +# Directory for instrumented libs generated by jscoverage/JSCover +lib-cov + +# Coverage directory used by tools like istanbul +coverage +*.lcov + +# nyc test coverage +.nyc_output + +# Grunt intermediate storage (https://gruntjs.com/creating-plugins#storing-task-files) +.grunt + +# Bower dependency directory (https://bower.io/) +bower_components + +# node-waf configuration +.lock-wscript + +# Compiled binary addons (https://nodejs.org/api/addons.html) +build/Release + +# Dependency directories +node_modules/ +jspm_packages/ + +# Snowpack dependency directory (https://snowpack.dev/) +web_modules/ + +# TypeScript cache +*.tsbuildinfo + +# Optional npm cache directory +.npm + +# Optional eslint cache +.eslintcache + +# Optional stylelint cache +.stylelintcache + +# Microbundle cache +.rpt2_cache/ +.rts2_cache_cjs/ +.rts2_cache_es/ +.rts2_cache_umd/ + +# Optional REPL history +.node_repl_history + +# Output of 'npm pack' +*.tgz + +# Yarn Integrity file +.yarn-integrity + +# dotenv environment variable files +.env +.env.development.local +.env.test.local +.env.production.local +.env.local + +# parcel-bundler cache (https://parceljs.org/) +.cache +.parcel-cache + +# Next.js build output +.next +out + +# Nuxt.js build / generate output +.nuxt +dist + +# Gatsby files +.cache/ +# Comment in the public line in if your project uses Gatsby and not Next.js +# https://nextjs.org/blog/next-9-1#public-directory-support +# public + +# vuepress build output +.vuepress/dist + +# vuepress v2.x temp and cache directory +.temp + +# Docusaurus cache and generated files +.docusaurus + +# Serverless directories +.serverless/ + +# FuseBox cache +.fusebox/ + +# DynamoDB Local files +.dynamodb/ + +# TernJS port file +.tern-port + +# Stores VSCode versions used for testing VSCode extensions +.vscode-test + +# yarn v2 +.yarn/cache +.yarn/unplugged +.yarn/build-state.yml +.yarn/install-state.gz +.pnp.* + +### Node Patch ### +# Serverless Webpack directories +.webpack/ + +# Optional stylelint cache + +# SvelteKit build / generate output +.svelte-kit + +### TeX ### +.tex +## Core latex/pdflatex auxiliary files: +*.aux +*.lof +*.lot +*.fls +*.out +*.toc +*.fmt +*.fot +*.cb +*.cb2 +.*.lb + +## Intermediate documents: +*.dvi +*.xdv +*-converted-to.* +# these rules might exclude image files for figures etc. +# *.ps +# *.eps +# *.pdf + +## Generated if empty string is given at "Please type another file name for output:" +.pdf + +## Bibliography auxiliary files (bibtex/biblatex/biber): +*.bbl +*.bcf +*.blg +*-blx.aux +*-blx.bib +*.run.xml + +## Build tool auxiliary files: +*.fdb_latexmk +*.synctex +*.synctex(busy) +*.synctex.gz +*.synctex.gz(busy) +*.pdfsync + +## Build tool directories for auxiliary files +# latexrun +latex.out/ + +## Auxiliary and intermediate files from other packages: +# algorithms +*.alg +*.loa + +# achemso +acs-*.bib + +# amsthm +*.thm + +# beamer +*.nav +*.pre +*.snm +*.vrb + +# changes +*.soc + +# comment +*.cut + +# cprotect +*.cpt + +# elsarticle (documentclass of Elsevier journals) +*.spl + +# endnotes +*.ent + +# fixme +*.lox + +# feynmf/feynmp +*.mf +*.mp +*.t[1-9] +*.t[1-9][0-9] +*.tfm + +#(r)(e)ledmac/(r)(e)ledpar +*.end +*.?end +*.[1-9] +*.[1-9][0-9] +*.[1-9][0-9][0-9] +*.[1-9]R +*.[1-9][0-9]R +*.[1-9][0-9][0-9]R +*.eledsec[1-9] +*.eledsec[1-9]R +*.eledsec[1-9][0-9] +*.eledsec[1-9][0-9]R +*.eledsec[1-9][0-9][0-9] +*.eledsec[1-9][0-9][0-9]R + +# glossaries +*.acn +*.acr +*.glg +*.glo +*.gls +*.glsdefs +*.lzo +*.lzs +*.slg +*.slo +*.sls + +# uncomment this for glossaries-extra (will ignore makeindex's style files!) +# *.ist + +# gnuplot +*.gnuplot +*.table + +# gnuplottex +*-gnuplottex-* + +# gregoriotex +*.gaux +*.glog +*.gtex + +# htlatex +*.4ct +*.4tc +*.idv +*.lg +*.trc +*.xref + +# hyperref +*.brf + +# knitr +*-concordance.tex +# TODO Uncomment the next line if you use knitr and want to ignore its generated tikz files +# *.tikz +*-tikzDictionary + +# listings +*.lol + +# luatexja-ruby +*.ltjruby + +# makeidx +*.idx +*.ilg +*.ind + +# minitoc +*.maf +*.mlf +*.mlt +*.mtc[0-9]* +*.slf[0-9]* +*.slt[0-9]* +*.stc[0-9]* + +# minted +_minted* +*.pyg + +# morewrites +*.mw + +# newpax +*.newpax + +# nomencl +*.nlg +*.nlo +*.nls + +# pax +*.pax + +# pdfpcnotes +*.pdfpc + +# sagetex +*.sagetex.sage +*.sagetex.py +*.sagetex.scmd + +# scrwfile +*.wrt + +# svg +svg-inkscape/ + +# sympy +*.sout +*.sympy +sympy-plots-for-*.tex/ + +# pdfcomment +*.upa +*.upb + +# pythontex +*.pytxcode +pythontex-files-*/ + +# tcolorbox +*.listing + +# thmtools +*.loe + +# TikZ & PGF +*.dpth +*.md5 +*.auxlock + +# titletoc +*.ptc + +# todonotes +*.tdo + +# vhistory +*.hst +*.ver + +# easy-todo +*.lod + +# xcolor +*.xcp + +# xmpincl +*.xmpi + +# xindy +*.xdy + +# xypic precompiled matrices and outlines +*.xyc +*.xyd + +# endfloat +*.ttt +*.fff + +# Latexian +TSWLatexianTemp* + +## Editors: +# WinEdt +*.bak +*.sav + +# Texpad +.texpadtmp + +# LyX +*.lyx~ + +# Kile +*.backup + +# gummi +.*.swp + +# KBibTeX +*~[0-9]* + +# TeXnicCenter +*.tps + +# auto folder when using emacs and auctex +./auto/* +*.el + +# expex forward references with \gathertags +*-tags.tex + +# standalone packages +*.sta + +# Makeindex log files +*.lpz + +# xwatermark package +*.xwm + +# REVTeX puts footnotes in the bibliography by default, unless the nofootinbib +# option is specified. Footnotes are the stored in a file with suffix Notes.bib. +# Uncomment the next line to have this generated file ignored. +#*Notes.bib + +### TeX Patch ### +# LIPIcs / OASIcs +*.vtc + +# glossaries +*.glstex + ### Windows ### # Windows thumbnail cache files Thumbs.db Thumbs.db:encryptable ehthumbs.db ehthumbs_vista.db + # Dump file *.stackdump + # Folder config file [Dd]esktop.ini + # Recycle Bin used on file shares $RECYCLE.BIN/ + # Windows Installer files *.cab *.msi *.msix *.msm *.msp + # Windows shortcuts *.lnk -node_modules \ No newline at end of file +# End of https://www.toptal.com/developers/gitignore/api/macos,linux,windows,node,tex \ No newline at end of file From b7645693d67619546beca9e14c91e968f130dafa Mon Sep 17 00:00:00 2001 From: Martin Scheidt <142348+kaat0@users.noreply.github.com> Date: Wed, 8 Jan 2025 00:04:20 +0100 Subject: [PATCH 06/24] thinner resistance lines --- doc/running-path.example.png | Bin 69635 -> 70663 bytes 1 file changed, 0 insertions(+), 0 deletions(-) diff --git a/doc/running-path.example.png b/doc/running-path.example.png index b4ce132604ad1e9203c70a19955e9b0a435d8eb7..10f0f66c7896a42488d986b4ef88ec7937da0c6a 100644 GIT binary patch literal 70663 zcmbTd2UL??(=N;-JVrr+h=^h$3erSDq$aeW6cIyF0qIqGPv}iRKu`im)6gPaAksm) z2uPC}Is~Nm-fR9l=zG3*t+Up@&N*2Ulbv0rTr+!S_P&Evl;mltnW-r#C}k?eqQn_f=I@O-xMwe}?<~AHOIlNHjECK=;?DFW@Wi&&&*v6#2uSX}G!j85tLz zJSjn=Lx9GoPm3%qqks<@8kvHEzy9=m^(yYpon9m|ADHIPD4<DiHQLw z1SZkd{mjZrl#!{pc5PZf;McixJM{D`J3Bkm)6<_nf9~n&DJUoi4-c=as|%DIk_V*Y zj((=}oI*h6ZN(=wVwNdA>&h<|4DfAftgWMMeQaq^rEP3Uz_c+Vwn=Gq4ROCFEUABP zcIfxP;cn^VUT*!(jK;A(dxP)Yn+twY^fCghZ;8KLKCPIlRb*APwK-*clF9h>cC4{+ z%&E;KV+@!_U}dRoEUC1f*fz$>N>3wJ{1u?$A0tJ18H%_6LLUQP&f7oML{U)ivXTG( zaZQtQ1{y(V1*9x!{vQw%C1;CiGJxdQ6bdrZFIT!X-*Nlo^;6(c_|11FpUo@On4Pon5!P-?`DS9A=T~kc+Mnh%c^sb9L>@AK zx)yQG!?Su8t$*p)LkjwJ?@6~&{rc-5zo&BzU%X!1KkP1H7V zYev+@Wr>0O9SbJEb4i%V6Gy2tY&*$j7j8=E>i*Mv0gbJ)7kowTBM2qGtC(U@rPEd4 z%F5he{ZY__L8<@xO$m-yINaXoT@+y(t@a=p9W|_c)};bE804l;z99TV?z(U9Drs3I zS?O_T>Ki69nVw_ANpAm3-M;zz7&BJ~k6qIM<;h}ZtTUO&*SfHtKjc(H2WP_rt-oh8 zxj!IJ_Lq9L1XcN+g*LceP2LkS1Ft2pm`EFBBM5maic~@bZS1`F`g)uDik`b254lG| z*Pj{nAhfF$9>+6nJBc~Qc4SK4BA@@E6xMas*<^LpdS)e{x19V->L?mAs0)0c2)~V& z&UHF8y`CGCF#PHGU!dt`TKZ%_I(c=9OEa#W7R1WIcAG1^GYPu!C0ZG6#*O4onHTCB zef8wb;d-=^u37Ws&abCLuc5V)Sc^>TBriw~KHfB&f3K$|`0{cktm6Dy4$)fp?j^F0 z%;DXZ@B>H*la#CTh;@lX99gcoB^htXTw9!rh15m#z_Q)%%3jO1UGE^gLm0>TGe8GH zsD&WZ3*KgHHI7P=tmk<~<|8U$yv^Q@t=Pm)54`bl%R8!Fp{A9YXxA_K=`Qu?id|u^ zIDB>2((fjDh4+FWWaFYavJnYJ%BK*;mv;^^WgWB!;hUy~z0s89`M6-5^h^thynP9y z%(QL$amzn_;7cz0bu&xfDmr7xJu;05{GKs$@LUDht> z)!v->k)*fUvrZP#lR>oS+kSLv%#WPo(M_1ju!K3#N^z&ot%mLZQpSE5wLs(7ZbnCX z3i}i&WmYvBFiBRWIsB3>Jn+{n;+QUBTP$2|xTS|Y>`4c%u{?C-Llts60l!?Etr=T= zD4XjddO(OsyGHKthqN+CrohXOU-?}_zPLEft({6y$jpCq#&{}b3CRcjSvp87hF`Hj zRrkXeoQC~bwu<_l1a1K1RV3m54@kNZahQKlFcnf*Mw{NM=Nja>#>G=W^a!3sc4j0O znIsU_$12(eQ>2)PdaJ1jU%V^xIpBv*bZR#ma(5OHDYYv#La!TZWVoukx zQ(PmHeAVce{!>7(+rTBSl+9lizVQy$IXD-#?9DQ9jx1{~fpxt>cagn4eXHBe4bK#X zty@h+es5RgZyKHu=62elyg(-eAp%X{pBJBT#3`(H&2N{3Zvw5aAD}K}vmajWmReR| z+tR+VTjMHvuwR~FA<&1e1bznu`vciHC4ho;3f^4s`S}l794e+-$JH|&j*2_Bol@)- z;@Yl?&h~;&yf9|1_tBNfj6j!61%gA2A%Xw+snt&7d_O}+>gyMx)`D@Nfek$Xt5NQ^ zAhhu0ip+b1hk6sOtT`6VKk@xXj}XMv3h7z$8sXB0gLj>Q30iN1;5wc+9@~T_qnvx7 zHD?!7VQ_uzLJ{L)-kIz3CwbMPMUB9Ktd|RTv%D)0S9Rl~%fkw+n`0brLSK%cjE=b- zoJZ^ym-&HCOWH4YVAh=d=*nxJdxEX$#s{%|Iolu4s`dpuTDQOiE|_}?$8dt&QM3CE z<~m75%YuN2o;5@85(& z6I+7XA{}f8v6${cQx|u!Ssbl(LWaE$CqVGVy=^Sb@z4U&KI53L`t3Kly4fjxI>G)A zPVQ~xi*ve`j<*7Za*`l_H0I2~QK1ElZ) z(>LXIHFij|M#8LBUQB>lJxm*g1QACU9vCEyAAGk8AJ=)6WtV>rvFlYUla8`!}uw>`p(&h`7qj=Qagw5m=&s}_@@kgC1PiQQFqi9kn{0DOzw5X6&@_5 z&L%D_56AeTOaQC0@3T!TQHrDKlF1KhkPDY)c7{Nft1$+#VgaYGv>l z7=7OwK5AnJ*PH1uP;!)r`ryAEP zJiEuIpAU(x_pV)%B=b@x@Mxb7V1!81+KJuB877rZL%56x4k_~#6P<~YbtM+x6YnOG zn5BU}mND*)i!4W1dEc*%+oI%m+`ip?xUpMj(fmw;Jk|bJuM!#ZMsu++FFs}qAE4|k z#sgD{PiNa-K@fbix^)=l?p_#p7tJ^tcO?+$mud8+AEsH;vNO0wE6yjfV^UfrING1Z zu6P)KPJ;ak3YVAwL>{k+)U6f@QoH&^K{jQ_1nGlZr+M^fUVA6+3?$QEWLcG^yHv#Bq?t4 zY|7_(M0%w{_-f0BqTNeDZ^($SM!P5fMPNo*#VUL@sig59akjMe-?ePjM}1;X(%2MJ z*q@)+iLnn&iivgv+ARvC>z~CS zX%J^cinJXb1a=yg+GnfwhX>R_M2F`)8@)>>l@%_loV!P9#qN0!u3o^`X5Bazt|pN{ z!QuY^4&b#eO{;@~RI@L(YIJ>yjy$^~P6B4di=!WjP~tmM`@RTTQeB-2p9LrZ9Js6- z&D5ZES`eK`DW$EgDUTE+W4ofs7l2b={H27hT3aDz?y8;hKxalP;pAzAKQpRHA=hQp zvbewJGov{?B!;q>=~?up9N+%Ad%zmeDTBlMY_tO@QCIYVO$D)%HtLr8HN&BsCS&+% zu1XYTn!8et1UvKb%9{P&L?+E7dFl$Lm{wS)c-w+7RJPSuYuMSdE1Z(O-jJ^-L)%6E zw_7gzSwIw-CSOD<)A+h~ZGwbf$y@XVJK|n+1oy?oVz1yHC z+wlV*D7Sa3g@M^94jE$%t!PVy8TZx9;Zq0&2m?&zQv6O(=iRa@^RFzxTo2;OQQyGl z9A-0;(p~u>53J3JK5amvZ{nwT%iu5Is!AdE3XZ#!O9QO%8 zd!D=@jCbC67LjI07JpkfWiOsyT=d)A*Eg>XDV^7^?OpxW-2Gi)!-65f1oZvXT`g3YP z@vp1mLLb}&=2x$#CkGc%q!u7YCWl0qfeCH`A1S8E8P{t|Vd($X5a)>NPdaGw4I!MX zt|}|rhwRX~BDe`2li<*4S<;wfqG}ugmux$y(NrGa-#cj4aEZ30imS3`76gb=G3$E~jK332Qmh%CE&WSh zzL=FGE{tWyr-muQ9Sl>k4E_CayuC)(+j6mC>b{pNp3Khcn4GO?A0Gyh(npeG7knx- z{xewMGpv(1YpCzwak#SM-03y6Ny@DW;b$GUKu_@WkoB^f{@C3kV$kTj zt_XJ_SNla9C+1pbs~;>oK833pK4Pi20NUUJcp4vxrFssNUYJQDW$0@6;mf3rnv5f| zCr@WPhRm6s1QtpcuEYN`?=p$0D%L-3T#~Qq!5m@9;d6rzj|-cq$U=()P^zN1dHApr zJ5AcK>|D*rKGXC0XqB5xC=h~6fXZpDxLCI!j<_D?+FPq<7yanK?Zh8E{*oP1d_5jK zAi!<`VU0|a9Kl=eT66uMk7CJQ9l(J*c*}Y1AgLl8Vx;|tzj*QVZRdeA)!L#jbD1zB zVq`D^;rui&DHesRWs>Yo|L?(NA|5Md+$=*QfAw5(^or!8hdD8GFOs}_+ez^_8A3+8 zRzY_($*_Zx_+O))!Ge!nZ6Er-wUGcEM6sNG<=wID^@>k3iS^|krF-?Ap?`-u#*%KW zg37-r@AI8F@ZNR3&vGi9fS_MfJDUtU;znE7ZayZ5Gvqw~m+wOlz=T3zoo&|q-&_n^_p97C zcVq|L9n>tTocw@JnR@ai4!YmMO8qOoyO+BB$46LzFOLFh?QZoQ-ErreiFr%o7C<1j zHq*Sx8a}r`&fHYWFn7g4*liP+(&GbCoz;K%;{v%u$Bz+KWYcwrbuN2y?v;@~z4bC^ zV3tm}39Oja7hwZTTj@5OyDc?x3S=PTdI0Oh&3+r?KAVLCZ!M~Sc(XK2S2fxicb-K( zX>frhl8L1b+{eOx5J~IZzpAadnL9@&;^JsT@tyB|K;eQIloq#rNB4w=%oHHzk2U!t zu*xNpk&CtwZY^=rjzj+>mK|hy`{3TAvBskMchSZh@GWz%*g55x4*o83iQ{G^+oIaI z$WS6Btv$Ux=-gRV#`(pqx(rM8Rb!=m_Pt{0Bj7^9dV^`Oo79lWVu>Wt;QsFF+);z zQH@g#L&nc)A&<#Q#Ia>-gSSj2L0RtmtI+>hUjN>Kh0TSc&t%Z_o~zb=>vd3>L>DLw zjcl1h_~@SMKLE3&k5>f?zT`Y`(rqCno;qbL#zeEtS8vy+FjlM{aq1;v-?e@Z?F0ER zPJevC0kXU?PnIOw1SK@SU!$G^nM?0KREhf^ccrP0!i`_|lWYw01@JE9XXjDHq)5|H z_0iNRE=uZelSGGk#%ScM1S7H@Q(b;Jq2h0WA!DuoL;00xvB^)95 z(si}HhKHx-=vjFz{@6D?Uj^Di3jB|R&I5QHAztwA*v&l%ZLAoU=Gx+YLUxH6^hZ~B zs?+HviznQL%&9U?L5DVp#N@!=%pl&$o@5l0lF4ozD31;AsqGa?l+t|eI4o5qqaPmn zdsc5%ZB8*>vJX$+Lx#p&)1CeZn`U3Vk&)`r@QZ)J-zeNSg|HhP_5OJDGf7Hz@y~`; z@f`ohumMbH$)_J_kUbdSws~u`#vWB3qdhzh*Pl-%0+RbPH{yT#sNVFB)&c16{$r{* zw&y3OI~#7E>mm@0Mo^xr{J)#I{;!*_%^;XU&okGW%xûNI|R-2+oUhStY3#j$T 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 literal 69635 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 Date: Sun, 26 Jan 2025 11:37:06 +0100 Subject: [PATCH 07/24] cleanup cleanup, renamed "name" to "description", removed UUID, updated "power_type" and "vehicle_type" --- CHANGELOG.md | 11 +++++++++++ doc/Rolling-Stock.md | 18 ++++++++---------- src/rolling-stock.json | 32 ++++++++++---------------------- 3 files changed, 29 insertions(+), 32 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index f07cfd1..c626228 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -15,17 +15,28 @@ Categories: Added, Changed, Deprecated, Removed, Fixed, and Security. * added `groups` array to points_of_interest for categorizing points * added comprehensive test suite with valid and invalid test cases * added example file with block sections and signals +* rolling stock: + * added `description` attribute to trains and vehicles + * added `hydraulic` and `misc` to power_type options + * added `non-revenue` to vehicle_type options ### Changed * running path: * running path arrays now contain named attributes (see Issue #4) * characteristic sections now require at least one of `speed`, `resistance`, or `track` attributes * measures for `points_of_interest` can now take three values: "front", "middle", and "rear" +* rolling stock: + * changed tractive_effort to require at least 3 unique pairs + * changed rotation_mass description to specify >= 1 ### Removed * running path: * removed `name` attribute (see Issue #5) * removed `UUID` attribute (see Issue #5) +* rolling stock: + * removed `name` attribute from trains and vehicles + * removed `UUID` attribute from trains and vehicles + * removed `diesel` from power_type options ## Version [2022.05] diff --git a/doc/Rolling-Stock.md b/doc/Rolling-Stock.md index 75b2f9a..ad11699 100644 --- a/doc/Rolling-Stock.md +++ b/doc/Rolling-Stock.md @@ -21,10 +21,9 @@ All attributes for a train are collected under the array `trains: -` in alphabet | Attributes | Necessity | Description | | -------------------- | --------- | ----------- | -| `id` | required | Identifier of the train. | -| `name` | required | Name of the train. | -| `UUID` | optional | The unique identifier of the train. | +| `description` | optional | Description of the train. | | `formation` | required | A Collection of vehicles that form the train referenced by vehicle `id`. | +| `id` | required | Identifier of the train. | ## Attributes in "vehicles" @@ -33,19 +32,18 @@ All attributes for a vehicle are collected under the array `vehicles: -` in alph | Attributes | Necessity | Description | | -------------------- | --------- | ----------- | | `air_resistance` | optional | Coefficient for air resistance in permil. | -| `base_resistance` | optional | Coefficient for basic resistance in permil. | +| `base_resistance` | optional | Coefficient for basic resistance in permil. | +| `description` | optional | Description of the vehicle. | | `id` | required | Identifier of the vehicle. | | `length` | required | The length of the vehicle in meter. | | `load_limit` | optional | The maximum permitted load of the vehicle in metric ton. | | `mass_traction` | optional | The mass on the powered axles of the vehicle in metric ton. | | `mass` | required | The empty mass (dead weight) of the vehicle in metric ton. | -| `name` | required | Name of the vehicle. | | `picture` | optional | A [URI](https://en.wikipedia.org/wiki/Uniform_Resource_Identifier) with a picture for humans. | -| `power_type` | optional | Type of propulsion; values: `diesel`, `electric`, or `steam`. | +| `power_type` | optional | Type of propulsion; values: `hydraulic`, `electric`, `steam`, or `misc`. | | `rolling_resistance` | optional | Coefficient for resistance of rolling axles in permil. | -| `rotation_mass` | optional | Factor for rotating mass; larger then 1. | +| `rotation_mass` | optional | Factor for rotating mass; larger or equal to 1. | | `speed_limit` | optional | Maximum permitted speed in kilometers per hour. | -| `tractive_effort` | optional | Tractive effort as pairs of speed in kilometers per hour and tractive force in newton. | -| `UUID` | optional | The unique identifier for a vehicle. | -| `vehicle_type` | required | Type of vehicle; values: `traction unit`, `freight`, `passenger`, or `multiple unit`. | +| `tractive_effort` | optional | Tractive effort as pairs of speed in kilometers per hour and tractive force in newton. Must contain at least 3 unique pairs. | +| `vehicle_type` | required | Type of vehicle; values: `traction unit`, `freight`, `passenger`, `multiple unit`, or `non-revenue`. | diff --git a/src/rolling-stock.json b/src/rolling-stock.json index 7cd2f6e..7a6d2f1 100644 --- a/src/rolling-stock.json +++ b/src/rolling-stock.json @@ -1,47 +1,40 @@ { - "$comment": "================[HEADER]=====================", "$id": "https://railtoolkit.org/schema/rolling-stock.json", "$schema": "https://json-schema.org/draft/2020-12/schema", "title": "Rolling Stock", "description": "Rolling stock data", "type": "object", - "$comment": "=================[BODY]======================", "required": [ "schema", "schema_version" ], "anyOf": [ {"required": [ "trains" ] }, {"required": [ "vehicles" ] } ], - "$comment": "in alphabetical order", "properties": { "schema": { "description": "Identifier of the schema", - "enum": [ "https://railtoolkit.org/schema/rolling-stock.json" ] + "const": "https://railtoolkit.org/schema/rolling-stock.json" }, "schema_version": { "description": "Version of the schema", "type": "string", + "const": "2024.07", "pattern": "[2-9][0-9][0-9][0-9].[0-1][0-9]" }, "trains": { "type": "array", "minItems": 1, "items": { - "required": [ "name", "id", "formation" ], + "required": [ "id", "formation" ], "type": "object", "properties": { "id": { "description": "Identifier of the train", "type": "string" }, - "name": { - "description": "Name of the train", + "description": { + "description": "Description of the train", "type": "string" }, - "UUID": { - "description": "The unique identifier for a train", - "type": "string", - "format": "uuid" - }, "formation": { "description": "Collection of vehicles that form the train", "type": "array", @@ -58,7 +51,7 @@ "type": "array", "minItems": 1, "items": { - "required": [ "name", "id", "vehicle_type", "length", "mass" ], + "required": [ "id", "vehicle_type", "length", "mass" ], "type": "object", "properties": { "air_resistance": { @@ -95,8 +88,8 @@ "type": "number", "exclusiveMinimum": 0 }, - "name": { - "description": "Name of the vehicle", + "description": { + "description": "Description of the vehicle", "type": "string" }, "picture": { @@ -106,7 +99,7 @@ }, "power_type": { "description": "Type of propulsion", - "enum": [ "diesel", "electric", "steam" ] + "enum": [ "hydraulic", "electric", "steam", "misc" ] }, "rolling_resistance": { "description": "coefficient for resistance of rolling axles in permil", @@ -139,14 +132,9 @@ } } }, - "UUID": { - "description": "The unique identifier for a vehicle", - "type": "string", - "format": "uuid" - }, "vehicle_type": { "description": "Type of vehicle", - "enum": [ "traction unit", "freight", "passenger", "multiple unit" ] + "enum": [ "traction unit", "freight", "passenger", "multiple unit", "non-revenue" ] } } } From 9759c1812ca274f98a6cf7e5f0b3f2db6a823d1c Mon Sep 17 00:00:00 2001 From: Martin Scheidt <142348+kaat0@users.noreply.github.com> Date: Sun, 9 Feb 2025 22:55:39 +0100 Subject: [PATCH 08/24] attributes and description for simplified_characteristics --- CHANGELOG.md | 11 +++- doc/Rolling-Stock.md | 59 ++++++++++++++++++- ...ck.example.simplified_characteristics.yaml | 15 +++++ package.json | 2 +- src/rolling-stock.json | 54 ++++++++++++++++- .../invalid/invalid_deceleration.yaml | 13 ++++ .../invalid/invalid_train_type.yaml | 13 ++++ .../invalid/missing_simplified_chars.yaml | 13 ++++ .../neither_formation_nor_simplified.yaml | 9 +++ .../valid/default_model_fidelity.yaml | 13 ++++ test/rolling-stock/valid/formation.yaml | 19 ++++++ test/rolling-stock/valid/mixed_fidelity.yaml | 21 +++++++ .../valid/simplified_characteristics.yaml | 15 +++++ test/rolling-stock/valid/trains.yaml | 3 +- test/rolling-stock/valid/vehicles.yaml | 2 +- 15 files changed, 252 insertions(+), 10 deletions(-) create mode 100644 doc/rolling-stock.example.simplified_characteristics.yaml create mode 100644 test/rolling-stock/invalid/invalid_deceleration.yaml create mode 100644 test/rolling-stock/invalid/invalid_train_type.yaml create mode 100644 test/rolling-stock/invalid/missing_simplified_chars.yaml create mode 100644 test/rolling-stock/invalid/neither_formation_nor_simplified.yaml create mode 100644 test/rolling-stock/valid/default_model_fidelity.yaml create mode 100644 test/rolling-stock/valid/formation.yaml create mode 100644 test/rolling-stock/valid/mixed_fidelity.yaml create mode 100644 test/rolling-stock/valid/simplified_characteristics.yaml diff --git a/CHANGELOG.md b/CHANGELOG.md index c626228..9814627 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -16,9 +16,11 @@ Categories: Added, Changed, Deprecated, Removed, Fixed, and Security. * added comprehensive test suite with valid and invalid test cases * added example file with block sections and signals * rolling stock: - * added `description` attribute to trains and vehicles + * added `description` attribute to trains and vehicles (see Issue #5) * added `hydraulic` and `misc` to power_type options * added `non-revenue` to vehicle_type options + * added complete schema for `simplified_characteristics` with required fields and constraints + * added `model_fidelity` attribute with default value "effort_tables" ### Changed * running path: @@ -28,14 +30,17 @@ Categories: Added, Changed, Deprecated, Removed, Fixed, and Security. * rolling stock: * changed tractive_effort to require at least 3 unique pairs * changed rotation_mass description to specify >= 1 + * changed train requirements to need either `formation` or `simplified_characteristics` + * changed `train_type` to include comprehensive list of train service types + * changed `model_fidelity` to be optional with default value ### Removed * running path: * removed `name` attribute (see Issue #5) * removed `UUID` attribute (see Issue #5) * rolling stock: - * removed `name` attribute from trains and vehicles - * removed `UUID` attribute from trains and vehicles + * removed `name` attribute from trains and vehicles (see Issue #5) + * removed `UUID` attribute from trains and vehicles (see Issue #5) * removed `diesel` from power_type options ## Version [2022.05] diff --git a/doc/Rolling-Stock.md b/doc/Rolling-Stock.md index ad11699..b09431e 100644 --- a/doc/Rolling-Stock.md +++ b/doc/Rolling-Stock.md @@ -10,20 +10,73 @@ | -------------------- | ------------ | ----------- | | `schema` | required | Identifier of the JSON schema. | | `schema_version` | required | Version of the JSON schema. | +| `model_fidelity` | optional | Level of detail for train dynamics modeling. Values: `simplified_characteristics`, `effort_tables`, or `physical_model`. Defaults to `effort_tables` if not specified. | | `trains` | optional[^1] | An array of [trains](#Attributes-in-trains). | | `vehicles` | optional[^1] | An array of [vehicles](#Attributes-in-vehicles). | [^1]: At least one of attributes `trains` or `vehicles` must be present. +### Model Fidelity Rationale + +The `model_fidelity` attribute allows for different levels of detail in train dynamics modeling, each serving different use cases: + +- `simplified_characteristics`: Uses basic acceleration and deceleration parameters. Suitable for high-level planning and simple simulations where detailed dynamics are not required. Parametersare definied in `trains`. + +- `effort_tables`: Uses lookup tables for tractive and braking effort over speed. Appropriate when measured or manufacturer-provided performance data is available and precise force calculations are needed. Parameters are defined in `vehicles`. + +- `physical_model`: Uses physical parameters and formulas. Best suited for accurate simulations where component-level behavior and physical effects need to be considered. Parameters are defined in `vehicles`. + +A file may contain more than one level of fidelity. Even though, the fidelity levels are mutually exclusive - only values from the selected fidelity level will be used in calculations, even if data for other levels is present in the file. The attribute `model_fidelity` can be used as switch to select the level of fidelity to be used. + ## Attributes in "trains" All attributes for a train are collected under the array `trains: -` in alphabetical order: +| Attributes | Necessity | Description | +| -------------------------- | ------------ | ----------- | +| `id` | required | Identifier of the train. | +| `description` | optional | Description of the train. | +| `train_type` | optional | Type of train service. See [Train Types](#train-types) below. | +| `formation` | optional[^1] | A Collection of vehicles that form the train referenced by vehicle `id`. | +| `simplified_characteristics`| optional[^1] | Basic motion parameters when using `simplified_characteristics` model fidelity. | + +[^1]: At least one of attributes `formation` or `simplified_characteristics` must be present, depending on the selected `model_fidelity`. + +### Attributes in "simplified_characteristics" + +When using `model_fidelity: "simplified_characteristics"`, the following attributes define the basic motion parameters of a train: + | Attributes | Necessity | Description | | -------------------- | --------- | ----------- | -| `description` | optional | Description of the train. | -| `formation` | required | A Collection of vehicles that form the train referenced by vehicle `id`. | -| `id` | required | Identifier of the train. | +| `speed_limit` | required | Maximum permitted speed in kilometers per hour. | +| `length` | required | Total length of the train in meters. | +| `acceleration` | required | Constant acceleration rate in meters per second squared. | +| `deceleration` | required | Constant service braking rate in meters per second squared (negative value). | +| `coasting` | optional | Deceleration rate when coasting in meters per second squared (negative value). Defaults to 0 if not specified. | + +These simplified characteristics are used when detailed vehicle dynamics are not required or available. They provide a basic but efficient way to model train movement for high-level planning and simple simulations. + +### Train Types + +The `train_type` attribute categorizes trains into three main groups: + +#### 1. Passenger Trains +- `long_distance` - Fast, long-distance trains with few stops (e.g., ICE, TGV) +- `regional` - Medium speed trains for mid-range distances (e.g., RE, RB) +- `suburban` - Short-distance trains with frequent stops (e.g., S-Bahn) +- `metro` - Urban trains with high frequency (e.g., Metro, Trams) + +#### 2. Freight Trains +- `intermodal` - Container transport trains with medium speed +- `heavy_freight` - High axle load trains for bulk cargo (e.g., coal, ore) +- `block_freight` - Single cargo type trains with direct routes + +#### 3. Special Trains +- `shunting` - Low speed trains for wagon formation +- `maintenance` - Infrastructure maintenance trains +- `construction` - Construction site supply trains +- `emergency` - Fire, rescue, and emergency response trains +- `snow_removal` - Seasonal service trains ## Attributes in "vehicles" diff --git a/doc/rolling-stock.example.simplified_characteristics.yaml b/doc/rolling-stock.example.simplified_characteristics.yaml new file mode 100644 index 0000000..28c953d --- /dev/null +++ b/doc/rolling-stock.example.simplified_characteristics.yaml @@ -0,0 +1,15 @@ +%YAML 1.2 +--- +schema: https://railtoolkit.org/schema/rolling-stock.json +schema_version: "2024.07" +model_fidelity: "simplified_characteristics" # Options: "simplified_characteristics", "effort_tables", "physical_model" +trains: + - id: IC2000-simplified_characteristics + description: "Intercity with a Vectron locomotive, 2 first class, 1 dining car and 5 second class coaches" + train_type: long_distance + simplified_characteristics: + speed_limit: 160 # km/h + length: 207.4 # m + acceleration: 0.5 # m/s^2 + deceleration: -0.65 # m/s^2 + coasting: -0.04 # m/s^2 diff --git a/package.json b/package.json index d979d27..1dd5b8d 100644 --- a/package.json +++ b/package.json @@ -4,7 +4,7 @@ "validate:rolling-stock": "ajv --spec=draft2020 -c ajv-formats -s src/rolling-stock.json -d ", "validate:running-path": "ajv --spec=draft2020 -c ajv-formats -s src/running-path.json -d ", "test": "npm-run-all test:**", - "test:stock:example": "ajv test --spec=draft2020 -c ajv-formats -s src/rolling-stock.json -d doc/rolling-stock.example.yaml --valid", + "test:stock:example": "ajv test --spec=draft2020 -c ajv-formats -s src/rolling-stock.json -d \"doc/rolling-stock.example.*.yaml\" --valid", "test:stock:valid": "ajv test --spec=draft2020 -c ajv-formats -s src/rolling-stock.json -d \"test/rolling-stock/valid/*.yaml\" --valid", "test:stock:invalid": "ajv test --spec=draft2020 -c ajv-formats -s src/rolling-stock.json -d \"test/rolling-stock/invalid/*.yaml\" --invalid", "test:stock": "npm-run-all test:stock:**", diff --git a/src/rolling-stock.json b/src/rolling-stock.json index 7a6d2f1..89598db 100644 --- a/src/rolling-stock.json +++ b/src/rolling-stock.json @@ -20,12 +20,22 @@ "const": "2024.07", "pattern": "[2-9][0-9][0-9][0-9].[0-1][0-9]" }, + "model_fidelity": { + "description": "Level of detail for train dynamics modeling. Defaults to 'effort_tables' if not specified", + "type": "string", + "enum": [ "simplified_characteristics", "effort_tables", "physical_model" ], + "default": "effort_tables" + }, "trains": { "type": "array", "minItems": 1, "items": { - "required": [ "id", "formation" ], "type": "object", + "required": [ "id", "train_type" ], + "anyOf": [ + {"required": [ "formation" ]}, + {"required": [ "simplified_characteristics" ]} + ], "properties": { "id": { "description": "Identifier of the train", @@ -35,6 +45,15 @@ "description": "Description of the train", "type": "string" }, + "train_type": { + "description": "Type of train service", + "type": "string", + "enum": [ + "long_distance", "regional", "suburban", "metro", + "intermodal", "heavy_freight", "block_freight", "shunting", + "maintenance", "construction", "emergency", "snow_removal" + ] + }, "formation": { "description": "Collection of vehicles that form the train", "type": "array", @@ -43,6 +62,39 @@ "items": { "type": "string" } + }, + "simplified_characteristics": { + "description": "Basic motion parameters when using simplified_characteristics model fidelity", + "type": "object", + "required": ["speed_limit", "length", "acceleration", "deceleration"], + "properties": { + "speed_limit": { + "description": "Maximum permitted speed in kilometers per hour", + "type": "number", + "exclusiveMinimum": 0 + }, + "length": { + "description": "Total length of the train in meters", + "type": "number", + "exclusiveMinimum": 0 + }, + "acceleration": { + "description": "Constant acceleration rate in meters per second squared", + "type": "number", + "exclusiveMinimum": 0 + }, + "deceleration": { + "description": "Constant service braking rate in meters per second squared (negative value)", + "type": "number", + "exclusiveMaximum": 0 + }, + "coasting": { + "description": "Deceleration rate when coasting in meters per second squared (negative value). Defaults to 0 if not specified", + "type": "number", + "maximum": 0, + "default": 0 + } + } } } } diff --git a/test/rolling-stock/invalid/invalid_deceleration.yaml b/test/rolling-stock/invalid/invalid_deceleration.yaml new file mode 100644 index 0000000..bed11f8 --- /dev/null +++ b/test/rolling-stock/invalid/invalid_deceleration.yaml @@ -0,0 +1,13 @@ +%YAML 1.2 +--- +schema: https://railtoolkit.org/schema/rolling-stock.json +schema_version: "2024.07" +model_fidelity: "simplified_characteristics" +trains: + - id: "train1" + train_type: "regional" + simplified_characteristics: + v_max: 160 + length: 200 + acceleration: 0.5 + deceleration: 0.65 # should be negative \ No newline at end of file diff --git a/test/rolling-stock/invalid/invalid_train_type.yaml b/test/rolling-stock/invalid/invalid_train_type.yaml new file mode 100644 index 0000000..ae10ea7 --- /dev/null +++ b/test/rolling-stock/invalid/invalid_train_type.yaml @@ -0,0 +1,13 @@ +%YAML 1.2 +--- +schema: https://railtoolkit.org/schema/rolling-stock.json +schema_version: "2024.07" +model_fidelity: "simplified_characteristics" +trains: + - id: "train1" + train_type: "high_speed" + simplified_characteristics: + v_max: 160 + length: 200 + acceleration: 0.5 + deceleration: -0.65 \ No newline at end of file diff --git a/test/rolling-stock/invalid/missing_simplified_chars.yaml b/test/rolling-stock/invalid/missing_simplified_chars.yaml new file mode 100644 index 0000000..c3b5e79 --- /dev/null +++ b/test/rolling-stock/invalid/missing_simplified_chars.yaml @@ -0,0 +1,13 @@ +%YAML 1.2 +--- +schema: https://railtoolkit.org/schema/rolling-stock.json +schema_version: "2024.07" +model_fidelity: "simplified_characteristics" +trains: + - id: "train1" + train_type: "regional" + simplified_characteristics: + speed_limit: 160 + length: 200 + acceleration: 0.5 + # missing required deceleration \ No newline at end of file diff --git a/test/rolling-stock/invalid/neither_formation_nor_simplified.yaml b/test/rolling-stock/invalid/neither_formation_nor_simplified.yaml new file mode 100644 index 0000000..4e46943 --- /dev/null +++ b/test/rolling-stock/invalid/neither_formation_nor_simplified.yaml @@ -0,0 +1,9 @@ +%YAML 1.2 +--- +schema: https://railtoolkit.org/schema/rolling-stock.json +schema_version: "2024.07" +model_fidelity: "effort_tables" +trains: + - id: "train1" + train_type: "regional" + description: "Missing both formation and simplified_characteristics" \ No newline at end of file diff --git a/test/rolling-stock/valid/default_model_fidelity.yaml b/test/rolling-stock/valid/default_model_fidelity.yaml new file mode 100644 index 0000000..e05ca37 --- /dev/null +++ b/test/rolling-stock/valid/default_model_fidelity.yaml @@ -0,0 +1,13 @@ +%YAML 1.2 +--- +schema: https://railtoolkit.org/schema/rolling-stock.json +schema_version: "2024.07" +trains: + - id: "train1" + train_type: "regional" + formation: ["vehicle1"] +vehicles: + - id: "vehicle1" + vehicle_type: "traction unit" + length: 20 + mass: 80 \ No newline at end of file diff --git a/test/rolling-stock/valid/formation.yaml b/test/rolling-stock/valid/formation.yaml new file mode 100644 index 0000000..c626847 --- /dev/null +++ b/test/rolling-stock/valid/formation.yaml @@ -0,0 +1,19 @@ +%YAML 1.2 +--- +schema: https://railtoolkit.org/schema/rolling-stock.json +schema_version: "2024.07" +model_fidelity: "effort_tables" +trains: + - id: "train1" + train_type: "regional" + description: "Test train with formation" + formation: ["vehicle1", "vehicle2"] +vehicles: + - id: "vehicle1" + vehicle_type: "traction unit" + length: 20 + mass: 80 + - id: "vehicle2" + vehicle_type: "passenger" + length: 26.4 + mass: 32 \ No newline at end of file diff --git a/test/rolling-stock/valid/mixed_fidelity.yaml b/test/rolling-stock/valid/mixed_fidelity.yaml new file mode 100644 index 0000000..3b71a91 --- /dev/null +++ b/test/rolling-stock/valid/mixed_fidelity.yaml @@ -0,0 +1,21 @@ +%YAML 1.2 +--- +schema: https://railtoolkit.org/schema/rolling-stock.json +schema_version: "2024.07" +model_fidelity: "simplified_characteristics" +trains: + - id: "train1" + train_type: "regional" + simplified_characteristics: + speed_limit: 140 + length: 150 + acceleration: 0.4 + deceleration: -0.6 + - id: "train2" + train_type: "long_distance" + formation: ["vehicle1"] +vehicles: + - id: "vehicle1" + vehicle_type: "multiple unit" + length: 25 + mass: 85 \ No newline at end of file diff --git a/test/rolling-stock/valid/simplified_characteristics.yaml b/test/rolling-stock/valid/simplified_characteristics.yaml new file mode 100644 index 0000000..b1f267f --- /dev/null +++ b/test/rolling-stock/valid/simplified_characteristics.yaml @@ -0,0 +1,15 @@ +%YAML 1.2 +--- +schema: https://railtoolkit.org/schema/rolling-stock.json +schema_version: "2024.07" +model_fidelity: "simplified_characteristics" +trains: + - id: "train1" + train_type: "long_distance" + description: "Test train with simplified characteristics" + simplified_characteristics: + speed_limit: 160 + length: 200 + acceleration: 0.5 + deceleration: -0.65 + coasting: -0.04 \ No newline at end of file diff --git a/test/rolling-stock/valid/trains.yaml b/test/rolling-stock/valid/trains.yaml index d30aece..fb1776d 100644 --- a/test/rolling-stock/valid/trains.yaml +++ b/test/rolling-stock/valid/trains.yaml @@ -1,8 +1,9 @@ %YAML 1.2 --- schema: https://railtoolkit.org/schema/rolling-stock.json -schema_version: "2022.05" +schema_version: "2024.07" trains: - name: test id: "1" formation: ["1"] + train_type: "maintenance" diff --git a/test/rolling-stock/valid/vehicles.yaml b/test/rolling-stock/valid/vehicles.yaml index 1988129..a1339cd 100644 --- a/test/rolling-stock/valid/vehicles.yaml +++ b/test/rolling-stock/valid/vehicles.yaml @@ -1,7 +1,7 @@ %YAML 1.2 --- schema: https://railtoolkit.org/schema/rolling-stock.json -schema_version: "2022.05" +schema_version: "2024.07" vehicles: - name: test id: "1" From 53c50b5942e58939c82e29e94eb3c11c2cf21d23 Mon Sep 17 00:00:00 2001 From: Martin Scheidt <142348+kaat0@users.noreply.github.com> Date: Tue, 11 Feb 2025 21:54:54 +0100 Subject: [PATCH 09/24] minor changes and fixes --- doc/Rolling-Stock.md | 34 +++++++++++++++++----------------- 1 file changed, 17 insertions(+), 17 deletions(-) diff --git a/doc/Rolling-Stock.md b/doc/Rolling-Stock.md index b09431e..6d8704e 100644 --- a/doc/Rolling-Stock.md +++ b/doc/Rolling-Stock.md @@ -37,24 +37,10 @@ All attributes for a train are collected under the array `trains: -` in alphabet | `id` | required | Identifier of the train. | | `description` | optional | Description of the train. | | `train_type` | optional | Type of train service. See [Train Types](#train-types) below. | -| `formation` | optional[^1] | A Collection of vehicles that form the train referenced by vehicle `id`. | -| `simplified_characteristics`| optional[^1] | Basic motion parameters when using `simplified_characteristics` model fidelity. | +| `formation` | optional[^2] | A Collection of vehicles that form the train referenced by vehicle `id`. | +| `simplified_characteristics`| optional[^2] | Basic motion parameters when using `simplified_characteristics` model fidelity. | -[^1]: At least one of attributes `formation` or `simplified_characteristics` must be present, depending on the selected `model_fidelity`. - -### Attributes in "simplified_characteristics" - -When using `model_fidelity: "simplified_characteristics"`, the following attributes define the basic motion parameters of a train: - -| Attributes | Necessity | Description | -| -------------------- | --------- | ----------- | -| `speed_limit` | required | Maximum permitted speed in kilometers per hour. | -| `length` | required | Total length of the train in meters. | -| `acceleration` | required | Constant acceleration rate in meters per second squared. | -| `deceleration` | required | Constant service braking rate in meters per second squared (negative value). | -| `coasting` | optional | Deceleration rate when coasting in meters per second squared (negative value). Defaults to 0 if not specified. | - -These simplified characteristics are used when detailed vehicle dynamics are not required or available. They provide a basic but efficient way to model train movement for high-level planning and simple simulations. +[^2]: At least one of attributes `formation` or `simplified_characteristics` must be present, depending on the selected `model_fidelity`. ### Train Types @@ -78,6 +64,20 @@ The `train_type` attribute categorizes trains into three main groups: - `emergency` - Fire, rescue, and emergency response trains - `snow_removal` - Seasonal service trains +### Attributes in "simplified_characteristics" + +When using `model_fidelity: "simplified_characteristics"`, the following attributes define the basic motion parameters of a train: + +| Attributes | Necessity | Description | +| -------------------- | --------- | ----------- | +| `speed_limit` | required | Maximum permitted speed in kilometers per hour. | +| `length` | required | Total length of the train in meters. | +| `acceleration` | required | Constant acceleration rate in meters per second squared. | +| `deceleration` | required | Constant service braking rate in meters per second squared (negative value). | +| `coasting` | optional | Deceleration rate when coasting in meters per second squared (negative value). Defaults to 0 if not specified. | + +These simplified characteristics are used when detailed vehicle dynamics are not required or available. They provide a basic but efficient way to model train movement for high-level planning and simple simulations. + ## Attributes in "vehicles" All attributes for a vehicle are collected under the array `vehicles: -` in alphabetical order: From 857902ac58cc1880d98da129522b3758f2d39b3d Mon Sep 17 00:00:00 2001 From: Martin Scheidt <142348+kaat0@users.noreply.github.com> Date: Thu, 13 Feb 2025 22:21:14 +0100 Subject: [PATCH 10/24] changed "model_fidelity" to enum "simplified", "detailed" --- doc/Rolling-Stock.md | 16 ++--- doc/rolling-stock.example.effort_table.yaml | 71 +++++++++++++++++++ doc/rolling-stock.example.simplified.yaml | 15 ++++ doc/rolling-stock.example.yaml | 44 ------------ src/rolling-stock.json | 6 +- .../invalid/invalid_deceleration.yaml | 4 +- .../invalid/invalid_model_fidelity.yaml | 9 +++ .../invalid/missing_simplified_chars.yaml | 2 +- .../neither_formation_nor_simplified.yaml | 2 +- test/rolling-stock/valid/formation.yaml | 2 +- test/rolling-stock/valid/mixed_fidelity.yaml | 2 +- .../valid/simplified_characteristics.yaml | 2 +- 12 files changed, 112 insertions(+), 63 deletions(-) create mode 100644 doc/rolling-stock.example.effort_table.yaml create mode 100644 doc/rolling-stock.example.simplified.yaml delete mode 100644 doc/rolling-stock.example.yaml create mode 100644 test/rolling-stock/invalid/invalid_model_fidelity.yaml diff --git a/doc/Rolling-Stock.md b/doc/Rolling-Stock.md index 6d8704e..166ee16 100644 --- a/doc/Rolling-Stock.md +++ b/doc/Rolling-Stock.md @@ -10,7 +10,7 @@ | -------------------- | ------------ | ----------- | | `schema` | required | Identifier of the JSON schema. | | `schema_version` | required | Version of the JSON schema. | -| `model_fidelity` | optional | Level of detail for train dynamics modeling. Values: `simplified_characteristics`, `effort_tables`, or `physical_model`. Defaults to `effort_tables` if not specified. | +| `model_fidelity` | optional | Level of detail for train dynamics modeling. Values: `simplified`, `detailed`. Defaults to `detailed` if not specified. | | `trains` | optional[^1] | An array of [trains](#Attributes-in-trains). | | `vehicles` | optional[^1] | An array of [vehicles](#Attributes-in-vehicles). | @@ -18,15 +18,13 @@ ### Model Fidelity Rationale -The `model_fidelity` attribute allows for different levels of detail in train dynamics modeling, each serving different use cases: +The `model_fidelity` attribute specifies the level of detail for train dynamics modeling: -- `simplified_characteristics`: Uses basic acceleration and deceleration parameters. Suitable for high-level planning and simple simulations where detailed dynamics are not required. Parametersare definied in `trains`. +- `simplified`: Uses basic acceleration and deceleration parameters. Suitable for high-level planning and simple simulations where detailed dynamics are not required. Parameters are defined in `trains.simplified_characteristics`. -- `effort_tables`: Uses lookup tables for tractive and braking effort over speed. Appropriate when measured or manufacturer-provided performance data is available and precise force calculations are needed. Parameters are defined in `vehicles`. - -- `physical_model`: Uses physical parameters and formulas. Best suited for accurate simulations where component-level behavior and physical effects need to be considered. Parameters are defined in `vehicles`. +- `detailed`: Uses detailed vehicle parameters including physical properties and effort tables. Appropriate for precise simulations where accurate force calculations are needed. Parameters are defined in `vehicles`. -A file may contain more than one level of fidelity. Even though, the fidelity levels are mutually exclusive - only values from the selected fidelity level will be used in calculations, even if data for other levels is present in the file. The attribute `model_fidelity` can be used as switch to select the level of fidelity to be used. +A file may contain data for both fidelity levels, but only values from the selected level will be used in calculations. The attribute `model_fidelity` acts as a switch to select which level of detail to use. ## Attributes in "trains" @@ -37,7 +35,7 @@ All attributes for a train are collected under the array `trains: -` in alphabet | `id` | required | Identifier of the train. | | `description` | optional | Description of the train. | | `train_type` | optional | Type of train service. See [Train Types](#train-types) below. | -| `formation` | optional[^2] | A Collection of vehicles that form the train referenced by vehicle `id`. | +| `formation` | optional[^2] | A Collection of vehicles that form the train referenced by vehicle `id`. Front and rear end of the train are defined by the first and last vehicle in the array. | | `simplified_characteristics`| optional[^2] | Basic motion parameters when using `simplified_characteristics` model fidelity. | [^2]: At least one of attributes `formation` or `simplified_characteristics` must be present, depending on the selected `model_fidelity`. @@ -66,7 +64,7 @@ The `train_type` attribute categorizes trains into three main groups: ### Attributes in "simplified_characteristics" -When using `model_fidelity: "simplified_characteristics"`, the following attributes define the basic motion parameters of a train: +When using `model_fidelity: "simplified"`, the following attributes define the basic motion parameters of a train: | Attributes | Necessity | Description | | -------------------- | --------- | ----------- | diff --git a/doc/rolling-stock.example.effort_table.yaml b/doc/rolling-stock.example.effort_table.yaml new file mode 100644 index 0000000..9bfbeeb --- /dev/null +++ b/doc/rolling-stock.example.effort_table.yaml @@ -0,0 +1,71 @@ +%YAML 1.2 +--- +schema: https://railtoolkit.org/schema/rolling-stock.json +schema_version: "2024.07" +model_fidelity: "effort_tables" # Options: "simplified_characteristics", "effort_tables", "physical_model" +trains: + - id: IC2000-effort_tables + description: "Intercity with locomotive BR193, 2 first class, 1 dining car and 5 second class coaches" + formation: [Vectron,Avmmz,Avmmz,WRkmz,Bpmmz,Bpmmz,Bpmmz,Bpmmz,Bpmmz] + loading_factor: [1.0,0.6,0.6,0.4,0.8,0.8,0.8,0.8,0.8] + +vehicles: + - id: Vectron # (required) + vehicle_type: traction unit + power_type: electric + picture: https://commons.wikimedia.org/wiki/File:Siemens_Vectron_193_837.jpg + ## main attributes + length: 18.98 # in m (required) + mass: 89.0 # in t (required) + speed_limit: 200 # in km/h (required) + # coefficients for the vehicles resistance + resistances: + rotation_mass: 1.09 # source: "Railway Timetabling & Operations" by Hansen, et al., 2014, p. 71 for the traction unit + base_resistance: 2.2 # source: "Fahrdynamik des Schienenverkehrs" by Wende, 2003, p. 151 for "4-achsige Diesellokomot." -> 2.2 ‰ to 3.5 ‰ + rolling_resistance: 1.4 # source: "Fahrdynamik des Schienenverkehrs" by Wende, 2003, p. 151 for "f_WW0" -> 1.2 ‰ to 1.6 ‰ + air_resistance: 6.0 # source: "Fahrdynamik des Schienenverkehrs" by Wende, 2003, p. 151 for "4-achsig, eckige Kopfform" with "Stromabnehmer" -> 5000 N to 6000 N; modified for the used formula + traction: + mass_traction: 89.0 # in t (mass on driving axles) + #power: 4500 # kW + # tractive effort as pairs of speed and tractive effort + tractive_effort: + - speed: 0 + effort: 10000 + - speed: 100 + effort: 10000 + - speed: 200 + effort: 10000 + brakes: + + + - id: Avmmz + description: "First class car" + vehicle_type: passenger + ## main attributes + length: 26.4 # in m (required) + mass: 47.0 # in t (required) + load_limit: 10.8 # in t (required) + speed_limit: 200 # in km/h (required) + + - id: WRkmz + description: "dining car" + vehicle_type: passenger + ## main attributes + length: 26.4 # in m (required) + mass: 52.0 # in t (required) + load_limit: 12.0 # in t (required) + speed_limit: 200 # in km/h (required) + + - id: Bpmmz + description: "Second class car" + vehicle_type: passenger + ## main attributes + length: 26.4 # in m (required) + mass: 45.0 # in t (required) + load_limit: 16.0 # in t (required) + speed_limit: 200 # in km/h (required) + +# TODO: +# * add effort tables +# * resistance +# * 2 level braking model diff --git a/doc/rolling-stock.example.simplified.yaml b/doc/rolling-stock.example.simplified.yaml new file mode 100644 index 0000000..b21a47e --- /dev/null +++ b/doc/rolling-stock.example.simplified.yaml @@ -0,0 +1,15 @@ +%YAML 1.2 +--- +schema: https://railtoolkit.org/schema/rolling-stock.json +schema_version: "2024.07" +model_fidelity: "simplified" # Options: "simplified", "detailed" +trains: + - id: IC2000-simplified + description: "Intercity with a Vectron locomotive, 2 first class, 1 dining car and 5 second class coaches" + train_type: long_distance + simplified_characteristics: + speed_limit: 160 # km/h + length: 207.4 # m + acceleration: 0.5 # m/s^2 + deceleration: -0.65 # m/s^2 + coasting: -0.04 # m/s^2 diff --git a/doc/rolling-stock.example.yaml b/doc/rolling-stock.example.yaml deleted file mode 100644 index 294a4dc..0000000 --- a/doc/rolling-stock.example.yaml +++ /dev/null @@ -1,44 +0,0 @@ -%YAML 1.2 ---- -schema: https://railtoolkit.org/schema/rolling-stock.json -schema_version: "2022.05" -trains: - - name: Regional Train - id: RB50-1 - formation: [BR642,BR642] -vehicles: - - name: Siemens Desiro Classic # (source: https://de.wikipedia.org/wiki/Siemens_Desiro_Classic) - id: BR642 - UUID: bb53ecc8-35ff-4c63-b480-a342f99e973b - picture: https://commons.wikimedia.org/wiki/File:Liesel_28-11-10_642_055-8_im_Bahnhof_Scharfenstein.JPG - vehicle_type: multiple unit # "freight", "passenger", "traction unit" or "multiple unit" - power_type: diesel # "diesel", "electric", or "steam" - - ## main attributes - length: 41.7 # in m - mass: 68.0 # in t - load_limit: 20.0 # in t - mass_traction: 52.8 # in t # mass on driving axles of the traction unit - speed_limit: 120 # in km/h - a_braking: -0.4253 # in m/s^2 # TODO see Roadmap in README.md - - ## coefficients for the vehicle resistance - rotation_mass: 1.08 # without dimension - base_resistance: 3.0 # in ‰ # coefficient for basic resistance - rolling_resistance: 1.4 # in ‰ # coefficient for resistance of rolling axles - air_resistance: 0.003 # in ‰ # coefficient for air resistance - - ## tractive effort as pairs of speed and tractive force - tractive_effort: - # [ v in km/h, F_T in N ] - - [ 0.0, 94400 ] - - [ 10.0, 80000 ] - - [ 20.0, 61330 ] - - [ 30.0, 42630 ] - - [ 40.0, 35600 ] - - [ 50.0, 32220 ] - - [ 60.0, 25540 ] - - [ 70.0, 22990 ] - - [ 80.0, 19400 ] - - [ 100.0, 14810 ] - - [ 120.0, 13380 ] diff --git a/src/rolling-stock.json b/src/rolling-stock.json index 89598db..4b569fa 100644 --- a/src/rolling-stock.json +++ b/src/rolling-stock.json @@ -21,10 +21,10 @@ "pattern": "[2-9][0-9][0-9][0-9].[0-1][0-9]" }, "model_fidelity": { - "description": "Level of detail for train dynamics modeling. Defaults to 'effort_tables' if not specified", + "description": "Level of detail for train dynamics modeling. Defaults to 'detailed' if not specified", "type": "string", - "enum": [ "simplified_characteristics", "effort_tables", "physical_model" ], - "default": "effort_tables" + "enum": [ "simplified", "detailed" ], + "default": "detailed" }, "trains": { "type": "array", diff --git a/test/rolling-stock/invalid/invalid_deceleration.yaml b/test/rolling-stock/invalid/invalid_deceleration.yaml index bed11f8..527f28b 100644 --- a/test/rolling-stock/invalid/invalid_deceleration.yaml +++ b/test/rolling-stock/invalid/invalid_deceleration.yaml @@ -2,12 +2,12 @@ --- schema: https://railtoolkit.org/schema/rolling-stock.json schema_version: "2024.07" -model_fidelity: "simplified_characteristics" +model_fidelity: "simplified" trains: - id: "train1" train_type: "regional" simplified_characteristics: - v_max: 160 + speed_limit: 160 length: 200 acceleration: 0.5 deceleration: 0.65 # should be negative \ No newline at end of file diff --git a/test/rolling-stock/invalid/invalid_model_fidelity.yaml b/test/rolling-stock/invalid/invalid_model_fidelity.yaml new file mode 100644 index 0000000..bd1d99a --- /dev/null +++ b/test/rolling-stock/invalid/invalid_model_fidelity.yaml @@ -0,0 +1,9 @@ +%YAML 1.2 +--- +schema: https://railtoolkit.org/schema/rolling-stock.json +schema_version: "2024.07" +model_fidelity: "effort_tables" # invalid value +trains: + - id: "train1" + train_type: "regional" + formation: ["vehicle1"] \ No newline at end of file diff --git a/test/rolling-stock/invalid/missing_simplified_chars.yaml b/test/rolling-stock/invalid/missing_simplified_chars.yaml index c3b5e79..943654d 100644 --- a/test/rolling-stock/invalid/missing_simplified_chars.yaml +++ b/test/rolling-stock/invalid/missing_simplified_chars.yaml @@ -2,7 +2,7 @@ --- schema: https://railtoolkit.org/schema/rolling-stock.json schema_version: "2024.07" -model_fidelity: "simplified_characteristics" +model_fidelity: "simplified" trains: - id: "train1" train_type: "regional" diff --git a/test/rolling-stock/invalid/neither_formation_nor_simplified.yaml b/test/rolling-stock/invalid/neither_formation_nor_simplified.yaml index 4e46943..53e91cd 100644 --- a/test/rolling-stock/invalid/neither_formation_nor_simplified.yaml +++ b/test/rolling-stock/invalid/neither_formation_nor_simplified.yaml @@ -2,7 +2,7 @@ --- schema: https://railtoolkit.org/schema/rolling-stock.json schema_version: "2024.07" -model_fidelity: "effort_tables" +model_fidelity: "detailed" trains: - id: "train1" train_type: "regional" diff --git a/test/rolling-stock/valid/formation.yaml b/test/rolling-stock/valid/formation.yaml index c626847..4fd2e85 100644 --- a/test/rolling-stock/valid/formation.yaml +++ b/test/rolling-stock/valid/formation.yaml @@ -2,7 +2,7 @@ --- schema: https://railtoolkit.org/schema/rolling-stock.json schema_version: "2024.07" -model_fidelity: "effort_tables" +model_fidelity: "detailed" trains: - id: "train1" train_type: "regional" diff --git a/test/rolling-stock/valid/mixed_fidelity.yaml b/test/rolling-stock/valid/mixed_fidelity.yaml index 3b71a91..2ca8768 100644 --- a/test/rolling-stock/valid/mixed_fidelity.yaml +++ b/test/rolling-stock/valid/mixed_fidelity.yaml @@ -2,7 +2,7 @@ --- schema: https://railtoolkit.org/schema/rolling-stock.json schema_version: "2024.07" -model_fidelity: "simplified_characteristics" +model_fidelity: "simplified" trains: - id: "train1" train_type: "regional" diff --git a/test/rolling-stock/valid/simplified_characteristics.yaml b/test/rolling-stock/valid/simplified_characteristics.yaml index b1f267f..13c5dc1 100644 --- a/test/rolling-stock/valid/simplified_characteristics.yaml +++ b/test/rolling-stock/valid/simplified_characteristics.yaml @@ -2,7 +2,7 @@ --- schema: https://railtoolkit.org/schema/rolling-stock.json schema_version: "2024.07" -model_fidelity: "simplified_characteristics" +model_fidelity: "simplified" trains: - id: "train1" train_type: "long_distance" From a2fe215e9846543eb1c1603d3da0a110238d3a70 Mon Sep 17 00:00:00 2001 From: Martin Scheidt <142348+kaat0@users.noreply.github.com> Date: Sat, 15 Feb 2025 14:47:53 +0100 Subject: [PATCH 11/24] docs: standardize unit notation and add dimensionless values Update unit descriptions in schema files and documentation --- README.md | 25 +++- doc/Rolling-Stock.md | 55 +++++--- doc/Running-Path.md | 73 ++++++----- doc/rolling-stock.example.simplified.yaml | 6 +- ...ck.example.simplified_characteristics.yaml | 15 --- doc/running-path.example.yaml | 120 +++++++++--------- src/rolling-stock.json | 30 ++--- src/running-path.json | 8 +- test/running-path/invalid/logical-errors.yaml | 6 +- test/running-path/valid/basic.yaml | 12 ++ test/running-path/valid/with_points.yaml | 16 +++ 11 files changed, 214 insertions(+), 152 deletions(-) delete mode 100644 doc/rolling-stock.example.simplified_characteristics.yaml create mode 100644 test/running-path/valid/basic.yaml create mode 100644 test/running-path/valid/with_points.yaml diff --git a/README.md b/README.md index f8b251c..d24b426 100644 --- a/README.md +++ b/README.md @@ -40,7 +40,7 @@ $ npm run validate:running-path doc/running-path.example.yaml The repository includes comprehensive test suites for both schemas: ```bash -$ npm run test # Run all tests +$ npm run test # Run all tests $ npm run test:stock # Run rolling-stock tests only $ npm run test:paths # Run running-path tests only ``` @@ -52,7 +52,28 @@ Each test suite includes: ## Documentation -See [Rolling-Stock.md](doc/Rolling-Stock.md) and [Running-Path.md](doc/Running-Path.md) for information about the used attributes. +### Sub schemas + +See +* [Rolling-Stock.md](doc/Rolling-Stock.md) and +* [Running-Path.md](doc/Running-Path.md) +for information about the used attributes in the sub schemas. + +### Units + +The schema uses common railway units for all numerical values: + +| Quantity | Unit | Description | +|------------------|------|-------------| +| Speed | km/h | Kilometers per hour | +| Mass | t | Metric tons | +| Length | m | Meters | +| Force | kN | Kilonewton | +| Power | kW | Kilowatt | +| Acceleration | m/s² | Meters per second squared | +| Resistance | ‰ | Per mille (mm/m) | +| Unit-less | - | Dimensionless values | + ## Contributors diff --git a/doc/Rolling-Stock.md b/doc/Rolling-Stock.md index 166ee16..e63614e 100644 --- a/doc/Rolling-Stock.md +++ b/doc/Rolling-Stock.md @@ -68,11 +68,11 @@ When using `model_fidelity: "simplified"`, the following attributes define the b | Attributes | Necessity | Description | | -------------------- | --------- | ----------- | -| `speed_limit` | required | Maximum permitted speed in kilometers per hour. | -| `length` | required | Total length of the train in meters. | -| `acceleration` | required | Constant acceleration rate in meters per second squared. | -| `deceleration` | required | Constant service braking rate in meters per second squared (negative value). | -| `coasting` | optional | Deceleration rate when coasting in meters per second squared (negative value). Defaults to 0 if not specified. | +| `speed_limit` | required | Maximum permitted speed (km/h) | +| `length` | required | Total length of the train (m) | +| `acceleration` | required | Constant acceleration rate (m/s²) | +| `deceleration` | required | Constant service braking rate (m/s²) (negative value) | +| `coasting` | optional | Deceleration rate when coasting (m/s²) (negative value). Defaults to 0 if not specified | These simplified characteristics are used when detailed vehicle dynamics are not required or available. They provide a basic but efficient way to model train movement for high-level planning and simple simulations. @@ -82,19 +82,34 @@ All attributes for a vehicle are collected under the array `vehicles: -` in alph | Attributes | Necessity | Description | | -------------------- | --------- | ----------- | -| `air_resistance` | optional | Coefficient for air resistance in permil. | -| `base_resistance` | optional | Coefficient for basic resistance in permil. | -| `description` | optional | Description of the vehicle. | -| `id` | required | Identifier of the vehicle. | -| `length` | required | The length of the vehicle in meter. | -| `load_limit` | optional | The maximum permitted load of the vehicle in metric ton. | -| `mass_traction` | optional | The mass on the powered axles of the vehicle in metric ton. | -| `mass` | required | The empty mass (dead weight) of the vehicle in metric ton. | -| `picture` | optional | A [URI](https://en.wikipedia.org/wiki/Uniform_Resource_Identifier) with a picture for humans. | -| `power_type` | optional | Type of propulsion; values: `hydraulic`, `electric`, `steam`, or `misc`. | -| `rolling_resistance` | optional | Coefficient for resistance of rolling axles in permil. | -| `rotation_mass` | optional | Factor for rotating mass; larger or equal to 1. | -| `speed_limit` | optional | Maximum permitted speed in kilometers per hour. | -| `tractive_effort` | optional | Tractive effort as pairs of speed in kilometers per hour and tractive force in newton. Must contain at least 3 unique pairs. | -| `vehicle_type` | required | Type of vehicle; values: `traction unit`, `freight`, `passenger`, `multiple unit`, or `non-revenue`. | +| `air_resistance` | optional | Coefficient for air resistance (-) | +| `base_resistance` | optional | Coefficient for basic resistance (‰) | +| `description` | optional | Description of the vehicle | +| `id` | required | Identifier of the vehicle | +| `length` | required | Length of the vehicle (m) | +| `load_limit` | optional | Maximum permitted load (t) | +| `mass_traction` | optional | Mass on powered axles (t) | +| `mass` | required | Empty mass (dead weight) (t) | +| `picture` | optional | A [URI](https://en.wikipedia.org/wiki/Uniform_Resource_Identifier) with a picture for humans | +| `power_type` | optional | Type of propulsion; values: `hydraulic`, `electric`, `steam`, or `misc` | +| `rolling_resistance` | optional | Coefficient for resistance of rolling axles (-) | +| `rotation_mass` | optional | Factor for rotating mass; >= 1 | +| `speed_limit` | optional | Maximum permitted speed (km/h) | +| `tractive_effort` | optional | Tractive effort as pairs of speed (km/h) and tractive force (kN) | +| `vehicle_type` | required | Type of vehicle; values: `traction unit`, `freight`, `passenger`, `multiple unit`, or `non-revenue` | + +### Units + +The schema uses common railway units for all numerical values: + +| Quantity | Unit | Description | +|------------------|------|-------------| +| Speed | km/h | Kilometers per hour | +| Mass | t | Metric tons | +| Length | m | Meters | +| Force | kN | Kilonewton | +| Power | kW | Kilowatt | +| Acceleration | m/s² | Meters per second squared | +| Resistance | ‰ | Per mille (mm/m) | +| Unit-less | - | Dimensionless values | diff --git a/doc/Running-Path.md b/doc/Running-Path.md index 418cc91..f322bf0 100644 --- a/doc/Running-Path.md +++ b/doc/Running-Path.md @@ -14,36 +14,49 @@ ## Attributes in "paths" -All attributes for paths are collected under the array `paths: -` in alphabetical order: -| Attributes | Necessity | Description | -| ------------------------- | --------- | ------------------------------------------------------------------------------ | -| `characteristic_sections` | required | An array of [characteristic sections](#Attributes-in-characteristic-sections). | -| `id` | required | Identifier of the path. | -| `description` | optional | Description of the path. | -| `points_of_interest` | optional | An array of [points of interest](#Attributes-in-points-of-interest). | - -## Attributes in "characteristic sections" - -All attributes for a characteristic section are collected under the array `characteristic_sections: -` sorted in ascending or descending order: - -| Attributes | Necessity | Description | -| ------------ | ------------ | ---------------------------------- | -| `position` | required | mileage in meter | -| `speed` | optional[^1] | speed limit in kilometers per hour | -| `resistance` | optional[^1] | resistance in permil | - -[^1]: At least one optional attribute must be present. - -## Attributes in "points of interest" -All attributes for a point of interest (poi) are collected under the array `points_of_interest: -` sorted in ascending or descending order: - -| Attributes | Necessity | Description | -| ------------ | --------- | ------------------------------------------------------------------| -| `position` | required | mileage in meter | -| `id` | required | Identifier of the point | -| `description`| optional | Description of the point | -| `groups` | optional | Array of group names this point belongs to | -| `measure` | required | measurement applies to the `front`, `middle` or `rear` of a train | +All attributes for a path are collected under the array `paths: -` in alphabetical order: + +| Attributes | Necessity | Description | +| ------------------------ | --------- | ----------- | +| `id` | required | Identifier of the running path. | +| `description` | optional | Description of the running path. | +| `characteristic_sections`| required | An array of [characteristic sections](#Attributes-in-characteristic_sections). | +| `points_of_interest` | optional | An array of [points of interest](#Attributes-in-points_of_interest). | + +## Attributes in "characteristic_sections" + +Characteristic sections are sections of a running path within which properties, such as permitted speed or track resistances, do not change. + +| Attributes | Necessity | Description | +| -------------------- | --------- | ----------- | +| `position` | required | Position along the path (m) | +| `speed` | optional[^1] | Maximum permitted speed (km/h) | +| `resistance` | optional[^1] | Track resistance (‰) | + +[^1]: At least one of attributes `speed` or `resistance` must be present. + +## Attributes in "points_of_interest" + +Points of interest mark specific locations along the path where measurements or observations should be taken. + +| Attributes | Necessity | Description | +| -------------------- | --------- | ----------- | +| `position` | required | Position along the path (m) | +| `id` | required | Identifier of the point of interest | +| `description` | optional | Description of the point of interest | +| `groups` | optional | Groups this point belongs to | +| `measure` | required | Position on train to measure; values: `front`, `middle`, or `rear` | + +### Units + +The schema uses common railway units for all numerical values: + +| Quantity | Unit | Description | +|------------------|------|-------------| +| Position | m | Meters | +| Speed | km/h | Kilometers per hour | +| Resistance | ‰ | Per mille (mm/m) | +| Unit-less | - | Dimensionless values | # Example An example file showing a running path with block sections, signals, and platforms can be found in [running-path.example.yaml](running-path.example.yaml). The example includes various points of interest such as platform tracks, route signals, and clearing points, each with specific positions and descriptions. Below is a visual representation of the YAML structure: diff --git a/doc/rolling-stock.example.simplified.yaml b/doc/rolling-stock.example.simplified.yaml index b21a47e..e4731dd 100644 --- a/doc/rolling-stock.example.simplified.yaml +++ b/doc/rolling-stock.example.simplified.yaml @@ -10,6 +10,6 @@ trains: simplified_characteristics: speed_limit: 160 # km/h length: 207.4 # m - acceleration: 0.5 # m/s^2 - deceleration: -0.65 # m/s^2 - coasting: -0.04 # m/s^2 + acceleration: 0.5 # m/s² + deceleration: -0.65 # m/s² + coasting: -0.04 # m/s² diff --git a/doc/rolling-stock.example.simplified_characteristics.yaml b/doc/rolling-stock.example.simplified_characteristics.yaml deleted file mode 100644 index 28c953d..0000000 --- a/doc/rolling-stock.example.simplified_characteristics.yaml +++ /dev/null @@ -1,15 +0,0 @@ -%YAML 1.2 ---- -schema: https://railtoolkit.org/schema/rolling-stock.json -schema_version: "2024.07" -model_fidelity: "simplified_characteristics" # Options: "simplified_characteristics", "effort_tables", "physical_model" -trains: - - id: IC2000-simplified_characteristics - description: "Intercity with a Vectron locomotive, 2 first class, 1 dining car and 5 second class coaches" - train_type: long_distance - simplified_characteristics: - speed_limit: 160 # km/h - length: 207.4 # m - acceleration: 0.5 # m/s^2 - deceleration: -0.65 # m/s^2 - coasting: -0.04 # m/s^2 diff --git a/doc/running-path.example.yaml b/doc/running-path.example.yaml index 1e840a2..0998b5c 100644 --- a/doc/running-path.example.yaml +++ b/doc/running-path.example.yaml @@ -5,176 +5,176 @@ schema_version: "2024.07" paths: - id: example description: "Example path via track A1-AB1-AB2-B1-BC1-BC2-C1" + characteristic_sections: + - position: 0.0 # m + speed: 60 # km/h + resistance: 0.00 # ‰ + - position: 390.0 # m + speed: 160 # km/h + - position: 1000.0 # m + resistance: 1.00 # ‰ + - position: 2000.0 # m + resistance: 2.00 # ‰ + - position: 3000.0 # m + resistance: 5.00 # ‰ + - position: 4000.0 # m + resistance: -3.00 # ‰ + - position: 5000.0 # m + speed: 120 # km/h + resistance: 2.00 # ‰ + - position: 5810.0 # m + speed: 160 # km/h + - position: 6000.0 # m + resistance: -10.00 # ‰ + - position: 7000.0 # m + resistance: 15.00 # ‰ + - position: 8000.0 # m + resistance: -10.00 # ‰ + - position: 8500.0 # m + resistance: 20.00 # ‰ + - position: 9400.0 # m + speed: 40 # km/h + resistance: -1.00 # ‰ + - position: 9950.0 # m + speed: 0 # km/h points_of_interest: - id: "a1" - position: 100.00 + position: 100.00 # m description: "plattform track 1" groups: ["station A"] measure: middle - id: "s1" - position: 220.00 + position: 220.00 # m description: "route signal 1" groups: ["station A", "block 1", "route 1"] measure: front - id: "cp1" - position: 320.00 + position: 320.00 # m description: "block clearing point" groups: ["station A"] measure: rear - id: "cp3" - position: 320.00 + position: 320.00 # m description: "route clearing point" groups: ["station A", "block 1", "route 1"] measure: rear - id: "vp4" - position: 1450.00 + position: 1450.00 # m description: "view point distant signal 4" groups: ["block 2"] measure: front - id: "ds4" - position: 1700.00 + position: 1700.00 # m description: "distant signal 4" groups: ["block 2"] measure: front - id: "s4" - position: 2700.00 + position: 2700.00 # m description: "block signal 4" groups: ["block 2"] measure: front - id: "cp4" - position: 2800.00 + position: 2800.00 # m description: "block clearing point" groups: ["block 1"] measure: rear - id: "vp6" - position: 3750.00 + position: 3750.00 # m description: "view point distant signal 6" groups: ["station B", "route 2"] measure: front - id: "ds6" - position: 4000.00 + position: 4000.00 # m description: "distant signal 6" groups: ["station B", "route 2"] measure: front - id: "vp9" - position: 4750.00 + position: 4750.00 # m description: "view point distant signal 9" groups: ["block 3"] measure: front - id: "s6" - position: 5000.00 + position: 5000.00 # m description: "route signal 6 and distant signal 9" groups: ["station B", "route 2", "route 3", "block 3"] measure: front - id: "cp5" - position: 5100.00 + position: 5100.00 # m description: "block clearing point" groups: ["block 2", "station B"] measure: rear - id: "cp6" - position: 5170.00 + position: 5170.00 # m description: "route clearing point" groups: ["route 2", "station B"] measure: rear - id: "b1" - position: 5500.00 + position: 5500.00 # m description: "plattform track 1" groups: ["station B"] measure: middle - id: "s9" - position: 5650.00 + position: 5650.00 # m description: "route signal 9" groups: ["station B", "block 3", "route 3"] measure: front - id: "cp8" - position: 5750.00 + position: 5750.00 # m description: "block clearing point" groups: ["station B"] measure: rear - id: "vp12" - position: 5800.00 + position: 5800.00 # m description: "view point distant signal 12" groups: ["block 4"] measure: front - id: "cp10" - position: 5810.00 + position: 5810.00 # m description: "route clearing point" groups: ["station B", "block 3", "route 3"] measure: rear - id: "ds12" - position: 6100.00 + position: 6100.00 # m description: "distant signal 12" groups: ["block 4"] measure: front - id: "s12" - position: 7100.00 + position: 7100.00 # m description: "block signal 12" groups: ["block 4"] measure: front - id: "cp11" - position: 7200.00 + position: 7200.00 # m description: "block clearing point" groups: ["block 3"] measure: rear - id: "vp14" - position: 8100.00 + position: 8100.00 # m description: "view point distant signal 14" groups: ["station C", "route 4"] measure: front - id: "ds14" - position: 8400.00 + position: 8400.00 # m description: "distant signal 14" groups: ["station C", "route 4"] measure: front - id: "s14" - position: 9400.00 + position: 9400.00 # m description: "block signal 14" groups: ["station C", "route 4"] measure: front - id: "cp12" - position: 9500.00 + position: 9500.00 # m description: "block clearing point" groups: ["block 4"] measure: rear - id: "cp13" - position: 9560.00 + position: 9560.00 # m description: "route clearing point" groups: ["route 4"] measure: rear - id: "c1" - position: 9800.00 + position: 9800.00 # m description: "plattform track 1" groups: ["station C"] measure: middle - characteristic_sections: - - position: 0.0 # mileage in meter - speed: 60 # speed limit in km/h - resistance: 0.00 # resistance in permil - - position: 390.0 - speed: 160 - - position: 1000.0 - resistance: 1.00 - - position: 2000.0 - resistance: 2.00 - - position: 3000.0 - resistance: 5.00 - - position: 4000.0 - resistance: -3.00 - - position: 5000.0 - speed: 120 - resistance: 2.00 - - position: 5810.0 - speed: 160 - - position: 6000.0 - resistance: -10.00 - - position: 7000.0 - resistance: 15.00 - - position: 8000.0 - resistance: -10.00 - - position: 8500.0 - resistance: 20.00 - - position: 9400.0 - speed: 40 - resistance: -1.00 - - position: 9950.0 - speed: 0 diff --git a/src/rolling-stock.json b/src/rolling-stock.json index 4b569fa..f99ee20 100644 --- a/src/rolling-stock.json +++ b/src/rolling-stock.json @@ -64,32 +64,32 @@ } }, "simplified_characteristics": { - "description": "Basic motion parameters when using simplified_characteristics model fidelity", + "description": "Basic motion parameters when using simplified model fidelity", "type": "object", "required": ["speed_limit", "length", "acceleration", "deceleration"], "properties": { "speed_limit": { - "description": "Maximum permitted speed in kilometers per hour", + "description": "Maximum permitted speed (km/h)", "type": "number", "exclusiveMinimum": 0 }, "length": { - "description": "Total length of the train in meters", + "description": "Total length of the train (m)", "type": "number", "exclusiveMinimum": 0 }, "acceleration": { - "description": "Constant acceleration rate in meters per second squared", + "description": "Constant acceleration rate (m/s²)", "type": "number", "exclusiveMinimum": 0 }, "deceleration": { - "description": "Constant service braking rate in meters per second squared (negative value)", + "description": "Constant service braking rate (m/s²) (negative value)", "type": "number", "exclusiveMaximum": 0 }, "coasting": { - "description": "Deceleration rate when coasting in meters per second squared (negative value). Defaults to 0 if not specified", + "description": "Deceleration rate when coasting (m/s²) (negative value). Defaults to 0 if not specified", "type": "number", "maximum": 0, "default": 0 @@ -107,12 +107,12 @@ "type": "object", "properties": { "air_resistance": { - "description": "coefficient for air resistance in permil", + "description": "Coefficient for air resistance (-)", "type": "number", "exclusiveMinimum": 0 }, "base_resistance": { - "description": "coefficient for basic resistance in permil", + "description": "Coefficient for basic resistance (‰)", "type": "number", "exclusiveMinimum": 0 }, @@ -121,22 +121,22 @@ "type": "string" }, "length": { - "description": "The length of the vehicle in meter", + "description": "Length of the vehicle (m)", "type": "number", "exclusiveMinimum": 0 }, "load_limit": { - "description": "The maximum permitted load of the vehicle in metric ton", + "description": "Maximum permitted load (t)", "type": "number", "exclusiveMinimum": 0 }, "mass_traction": { - "description": "The mass on the powered axles of the vehicle in metric ton", + "description": "Mass on powered axles (t)", "type": "number", "exclusiveMinimum": 0 }, "mass": { - "description": "The empty mass of the vehicle in metric ton", + "description": "Empty mass (dead weight) (t)", "type": "number", "exclusiveMinimum": 0 }, @@ -154,7 +154,7 @@ "enum": [ "hydraulic", "electric", "steam", "misc" ] }, "rolling_resistance": { - "description": "coefficient for resistance of rolling axles in permil", + "description": "Coefficient for resistance of rolling axles (-)", "type": "number", "exclusiveMinimum": 0 }, @@ -164,12 +164,12 @@ "minimum": 1 }, "speed_limit": { - "description": "Maximum permitted speed in kilometers per hour", + "description": "Maximum permitted speed (km/h)", "type": "number", "exclusiveMinimum": 0 }, "tractive_effort": { - "description": "Tractive effort as pairs of speed in kilometers per hour and tractive force in newton", + "description": "Tractive effort as pairs of speed (km/h) and tractive force (kN)", "type": "array", "minItems": 3, "uniqueItems": true, diff --git a/src/running-path.json b/src/running-path.json index 8a56e59..3fdf7ff 100644 --- a/src/running-path.json +++ b/src/running-path.json @@ -48,16 +48,16 @@ ], "properties": { "position": { - "description": "mileage in meter", + "description": "Position along the path (m)", "type": "number" }, "speed": { - "description": "speed in kilometers per hour", + "description": "Maximum permitted speed (km/h)", "type": "number", "minimum": 0 }, "resistance": { - "description": "resistance in permil", + "description": "Track resistance (‰)", "type": "number" } } @@ -76,7 +76,7 @@ ], "properties": { "position": { - "description": "mileage in meter", + "description": "Position along the path (m)", "type": "number" }, "id": { diff --git a/test/running-path/invalid/logical-errors.yaml b/test/running-path/invalid/logical-errors.yaml index be764ad..9da06e9 100644 --- a/test/running-path/invalid/logical-errors.yaml +++ b/test/running-path/invalid/logical-errors.yaml @@ -5,6 +5,6 @@ schema_version: "2024.07" paths: - id: "logical-errors" characteristic_sections: - - position: 0.0 - speed: -160 - resistance: 1.00 \ No newline at end of file + - position: 0.0 # m + speed: -160 # km/h (invalid negative speed) + resistance: 1.00 # ‰ \ No newline at end of file diff --git a/test/running-path/valid/basic.yaml b/test/running-path/valid/basic.yaml new file mode 100644 index 0000000..20dc786 --- /dev/null +++ b/test/running-path/valid/basic.yaml @@ -0,0 +1,12 @@ +%YAML 1.2 +--- +schema: https://railtoolkit.org/schema/running-path.json +schema_version: "2024.07" +paths: + - id: "test-path" + characteristic_sections: + - position: 0.0 # m + speed: 160 # km/h + - position: 1000.0 # m + speed: 120 # km/h + resistance: 2.0 # ‰ \ No newline at end of file diff --git a/test/running-path/valid/with_points.yaml b/test/running-path/valid/with_points.yaml new file mode 100644 index 0000000..eb0dd7a --- /dev/null +++ b/test/running-path/valid/with_points.yaml @@ -0,0 +1,16 @@ +%YAML 1.2 +--- +schema: https://railtoolkit.org/schema/running-path.json +schema_version: "2024.07" +paths: + - id: "test-path" + description: "Test path with points of interest" + characteristic_sections: + - position: 0.0 # m + speed: 160 # km/h + - position: 1000.0 # m + speed: 120 # km/h + points_of_interest: + - position: 500.0 # m + id: "point-1" + measure: "front" \ No newline at end of file From d06255337bbe448c5a187eaf10deffa2ff8eb631 Mon Sep 17 00:00:00 2001 From: Martin Scheidt <142348+kaat0@users.noreply.github.com> Date: Sun, 16 Feb 2025 15:47:01 +0100 Subject: [PATCH 12/24] Updated name and description --- README.md | 16 ++++++++++++++-- 1 file changed, 14 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index d24b426..9dd0be1 100644 --- a/README.md +++ b/README.md @@ -1,4 +1,4 @@ -# RailToolKit Schema +# RailToolKit/schema [![License: ISC][license-img]][license-url] [![DOI][zenodo-img]][zenodo-url] [![Build Status][ci-img]][ci-url] [![All Contributors][Contributors-img]][Contributors-url] @@ -6,7 +6,19 @@ ## About -This repo collects the descriptions of the structure and the validation constraints of tools in the railtoolkit in JSON schemas. It is, therefore, an alternative to [RailML](https://www.railml.org/). The JSON schemas enable the validation of YAML files in [TrainRun.jl](https://github.com/railtoolkit/TrainRun.jl.git) and [rolling-stock](https://github.com/railtoolkit/rolling-stock.git). +The RailToolkit/schema provides JSON schemas for railway operations data, offering a lightweight alternative to RailML. It focuses on two main aspects: + +1. Rolling Stock Schema + - Defines train and vehicle characteristics + - Supports both simplified and detailed modeling approaches + - Includes parameters like speed, mass, resistance, and tractive effort + +2. Running Path Schema + - Describes railway paths with speed limits and track resistance + - Supports points of interest (signals, platforms, etc.) + - Enables precise position-based path descriptions + +The schemas use standardized railway units and can be validated using standard JSON schema tools. They are designed to support railway simulation and planning tools while maintaining simplicity and ease of use. ## Prerequisite From 387a754cc7909d02bfe9bb3847ce8a10caf9d100 Mon Sep 17 00:00:00 2001 From: Martin Scheidt <142348+kaat0@users.noreply.github.com> Date: Sat, 22 Feb 2025 11:59:43 +0100 Subject: [PATCH 13/24] added emergency_deceleration to the simplified use case --- doc/Rolling-Stock.md | 1 + doc/rolling-stock.example.simplified.yaml | 9 +++++---- src/rolling-stock.json | 5 +++++ .../invalid/invalid_emergency_brake.yaml | 14 ++++++++++++++ .../valid/simplified_with_emergency_brake.yaml | 15 +++++++++++++++ 5 files changed, 40 insertions(+), 4 deletions(-) create mode 100644 test/rolling-stock/invalid/invalid_emergency_brake.yaml create mode 100644 test/rolling-stock/valid/simplified_with_emergency_brake.yaml diff --git a/doc/Rolling-Stock.md b/doc/Rolling-Stock.md index e63614e..eba8b40 100644 --- a/doc/Rolling-Stock.md +++ b/doc/Rolling-Stock.md @@ -72,6 +72,7 @@ When using `model_fidelity: "simplified"`, the following attributes define the b | `length` | required | Total length of the train (m) | | `acceleration` | required | Constant acceleration rate (m/s²) | | `deceleration` | required | Constant service braking rate (m/s²) (negative value) | +| `emergency_deceleration` | optional | Emergency braking rate (m/s²) (negative value). If not specified, defaults to the value of `deceleration` | | `coasting` | optional | Deceleration rate when coasting (m/s²) (negative value). Defaults to 0 if not specified | These simplified characteristics are used when detailed vehicle dynamics are not required or available. They provide a basic but efficient way to model train movement for high-level planning and simple simulations. diff --git a/doc/rolling-stock.example.simplified.yaml b/doc/rolling-stock.example.simplified.yaml index e4731dd..8f98cca 100644 --- a/doc/rolling-stock.example.simplified.yaml +++ b/doc/rolling-stock.example.simplified.yaml @@ -8,8 +8,9 @@ trains: description: "Intercity with a Vectron locomotive, 2 first class, 1 dining car and 5 second class coaches" train_type: long_distance simplified_characteristics: - speed_limit: 160 # km/h - length: 207.4 # m - acceleration: 0.5 # m/s² + speed_limit: 160 # km/h + length: 207.4 # m + acceleration: 0.5 # m/s² deceleration: -0.65 # m/s² - coasting: -0.04 # m/s² + emergency_deceleration: -1.55 # m/s² + coasting: -0.04 # m/s² diff --git a/src/rolling-stock.json b/src/rolling-stock.json index f99ee20..9a77a29 100644 --- a/src/rolling-stock.json +++ b/src/rolling-stock.json @@ -88,6 +88,11 @@ "type": "number", "exclusiveMaximum": 0 }, + "emergency_deceleration": { + "description": "Emergency braking rate (m/s²) (negative value). If not specified, defaults to the value of deceleration", + "type": "number", + "exclusiveMaximum": 0 + }, "coasting": { "description": "Deceleration rate when coasting (m/s²) (negative value). Defaults to 0 if not specified", "type": "number", diff --git a/test/rolling-stock/invalid/invalid_emergency_brake.yaml b/test/rolling-stock/invalid/invalid_emergency_brake.yaml new file mode 100644 index 0000000..10cac1c --- /dev/null +++ b/test/rolling-stock/invalid/invalid_emergency_brake.yaml @@ -0,0 +1,14 @@ +%YAML 1.2 +--- +schema: https://railtoolkit.org/schema/rolling-stock.json +schema_version: "2024.07" +model_fidelity: "simplified" +trains: + - id: "train1" + train_type: "regional" + simplified_characteristics: + speed_limit: 160 + length: 200 + acceleration: 0.5 + deceleration: -0.65 + emergency_deceleration: 1.55 # should be negative \ No newline at end of file diff --git a/test/rolling-stock/valid/simplified_with_emergency_brake.yaml b/test/rolling-stock/valid/simplified_with_emergency_brake.yaml new file mode 100644 index 0000000..38f0c9d --- /dev/null +++ b/test/rolling-stock/valid/simplified_with_emergency_brake.yaml @@ -0,0 +1,15 @@ +%YAML 1.2 +--- +schema: https://railtoolkit.org/schema/rolling-stock.json +schema_version: "2024.07" +model_fidelity: "simplified" +trains: + - id: "train1" + train_type: "long_distance" + simplified_characteristics: + speed_limit: 160 + length: 200 + acceleration: 0.5 + deceleration: -0.65 + emergency_deceleration: -1.55 + coasting: -0.04 \ No newline at end of file From a6a9c466813da001bd15a8032ccfbb11333078bf Mon Sep 17 00:00:00 2001 From: Martin Scheidt <142348+kaat0@users.noreply.github.com> Date: Tue, 25 Feb 2025 09:29:51 +0100 Subject: [PATCH 14/24] fix section header --- doc/Running-Path.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/doc/Running-Path.md b/doc/Running-Path.md index f322bf0..a7a638d 100644 --- a/doc/Running-Path.md +++ b/doc/Running-Path.md @@ -47,7 +47,7 @@ Points of interest mark specific locations along the path where measurements or | `groups` | optional | Groups this point belongs to | | `measure` | required | Position on train to measure; values: `front`, `middle`, or `rear` | -### Units +# Units The schema uses common railway units for all numerical values: From 1bc7838920ff78d779fdec5e00045ff9610bfa6b Mon Sep 17 00:00:00 2001 From: Martin Scheidt <142348+kaat0@users.noreply.github.com> Date: Tue, 25 Feb 2025 23:33:22 +0100 Subject: [PATCH 15/24] added detailed model for rolling stock --- CHANGELOG.md | 14 +- README.md | 1 + doc/Rolling-Stock.md | 164 ++++++++-- doc/rolling-stock.example.detailed.yaml | 149 +++++++++ doc/rolling-stock.example.effort_table.yaml | 71 ----- doc/rolling-stock.example.simplified.yaml | 2 +- src/rolling-stock.json | 298 +++++++++++++----- .../invalid/formation_empty.yaml | 8 +- .../invalid/formation_missing.yaml | 6 +- .../invalid/invalid_brake_model.yaml | 13 + .../invalid/invalid_deceleration.yaml | 3 +- .../invalid/invalid_loading_factor.yaml | 9 + .../invalid/invalid_power_type.yaml | 10 + .../invalid/invalid_resistance_model.yaml | 12 + .../invalid/invalid_schema_version.yaml | 8 + .../invalid/invalid_tractive_effort.yaml | 13 + test/rolling-stock/invalid/length.yaml | 13 +- test/rolling-stock/invalid/mass.yaml | 13 +- test/rolling-stock/invalid/minimal.yaml | 3 +- .../invalid/missing_model_fidelity.yaml | 9 + test/rolling-stock/invalid/vehicle_type.yaml | 14 +- .../valid/default_model_fidelity.yaml | 1 + .../rolling-stock/valid/detailed_vehicle.yaml | 35 ++ test/rolling-stock/valid/formation.yaml | 4 +- .../rolling-stock/valid/simplified_train.yaml | 15 + test/rolling-stock/valid/trains.yaml | 1 + test/rolling-stock/valid/vehicles.yaml | 1 + 27 files changed, 681 insertions(+), 209 deletions(-) create mode 100644 doc/rolling-stock.example.detailed.yaml delete mode 100644 doc/rolling-stock.example.effort_table.yaml create mode 100644 test/rolling-stock/invalid/invalid_brake_model.yaml create mode 100644 test/rolling-stock/invalid/invalid_loading_factor.yaml create mode 100644 test/rolling-stock/invalid/invalid_power_type.yaml create mode 100644 test/rolling-stock/invalid/invalid_resistance_model.yaml create mode 100644 test/rolling-stock/invalid/invalid_schema_version.yaml create mode 100644 test/rolling-stock/invalid/invalid_tractive_effort.yaml create mode 100644 test/rolling-stock/invalid/missing_model_fidelity.yaml create mode 100644 test/rolling-stock/valid/detailed_vehicle.yaml create mode 100644 test/rolling-stock/valid/simplified_train.yaml diff --git a/CHANGELOG.md b/CHANGELOG.md index 9814627..7c147e8 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -20,7 +20,14 @@ Categories: Added, Changed, Deprecated, Removed, Fixed, and Security. * added `hydraulic` and `misc` to power_type options * added `non-revenue` to vehicle_type options * added complete schema for `simplified_characteristics` with required fields and constraints - * added `model_fidelity` attribute with default value "effort_tables" + * added `coasting` parameter to simplified characteristics + * added `emergency_deceleration` to simplified characteristics + * added `model_fidelity` attribute on different levels + * added comprehensive brake model configuration with multiple fidelity levels + * added detailed brake types: eddy_current_brake, electrodynamic_brake, and friction_brake + * added brake timing parameters: reaction_time, response_time, threshold_time + * added new train types: snow_removal, construction, emergency + * added comprehensive documentation for all attributes and model fidelity levels ### Changed * running path: @@ -32,7 +39,10 @@ Categories: Added, Changed, Deprecated, Removed, Fixed, and Security. * changed rotation_mass description to specify >= 1 * changed train requirements to need either `formation` or `simplified_characteristics` * changed `train_type` to include comprehensive list of train service types - * changed `model_fidelity` to be optional with default value + * changed brake modeling to support multiple fidelity levels + * changed model_fidelity to be required at top level and component levels + * refined train_type categories into passenger, freight, and special groups + * improved documentation with detailed explanations of model fidelity rationale ### Removed * running path: diff --git a/README.md b/README.md index 9dd0be1..a300b8d 100644 --- a/README.md +++ b/README.md @@ -80,6 +80,7 @@ The schema uses common railway units for all numerical values: | Speed | km/h | Kilometers per hour | | Mass | t | Metric tons | | Length | m | Meters | +| Time | s | Seconds | | Force | kN | Kilonewton | | Power | kW | Kilowatt | | Acceleration | m/s² | Meters per second squared | diff --git a/doc/Rolling-Stock.md b/doc/Rolling-Stock.md index eba8b40..21c99ac 100644 --- a/doc/Rolling-Stock.md +++ b/doc/Rolling-Stock.md @@ -9,8 +9,8 @@ | Attributes | Necessity | Description | | -------------------- | ------------ | ----------- | | `schema` | required | Identifier of the JSON schema. | -| `schema_version` | required | Version of the JSON schema. | -| `model_fidelity` | optional | Level of detail for train dynamics modeling. Values: `simplified`, `detailed`. Defaults to `detailed` if not specified. | +| `schema_version` | required | Version of the JSON schema (current version `2024.07`). | +| `model_fidelity` | required | Level of detail for train dynamics modeling. Values: `simplified`, `detailed`. | | `trains` | optional[^1] | An array of [trains](#Attributes-in-trains). | | `vehicles` | optional[^1] | An array of [vehicles](#Attributes-in-vehicles). | @@ -24,19 +24,22 @@ The `model_fidelity` attribute specifies the level of detail for train dynamics - `detailed`: Uses detailed vehicle parameters including physical properties and effort tables. Appropriate for precise simulations where accurate force calculations are needed. Parameters are defined in `vehicles`. -A file may contain data for both fidelity levels, but only values from the selected level will be used in calculations. The attribute `model_fidelity` acts as a switch to select which level of detail to use. +A file may contain data for both fidelity levels, but only values from the selected level will be used in calculations. The attribute `model_fidelity` acts as a switch to select which level of detail to use, ensuring that the appropriate parameters are applied based on the chosen fidelity level. + +The `model_fidelity` attribute is not only used at the top level but also plays a crucial role in lower-level objects such as `resistance`, `traction`, and `brakes`. This allows for varying levels of detail in the modeling of these components, ensuring that the appropriate parameters are applied based on the selected fidelity level. ## Attributes in "trains" All attributes for a train are collected under the array `trains: -` in alphabetical order: -| Attributes | Necessity | Description | -| -------------------------- | ------------ | ----------- | -| `id` | required | Identifier of the train. | -| `description` | optional | Description of the train. | -| `train_type` | optional | Type of train service. See [Train Types](#train-types) below. | -| `formation` | optional[^2] | A Collection of vehicles that form the train referenced by vehicle `id`. Front and rear end of the train are defined by the first and last vehicle in the array. | -| `simplified_characteristics`| optional[^2] | Basic motion parameters when using `simplified_characteristics` model fidelity. | +| Attributes | Necessity | Description | +| ---------------------------- | ------------ | ----------- | +| `id` | required | Identifier of the train. | +| `description` | optional | Description of the train. | +| `train_type` | optional | Type of train service. See [Train Types](#train-types) below. | +| `simplified_characteristics` | optional[^2] | Basic motion parameters when using `simplified_characteristics` model fidelity. See [Simplified Characteristics](#simplified-characteristics) below. | +| `formation` | optional[^2] | A Collection of vehicles that form the train referenced by vehicle `id`. Front and rear end of the train are defined by the first and last vehicle in the array. | +| `loading_factor` | optional | Ratio (between 0 and 1) of the actual load to the maximum load capacity of the train. Using the `load_limit` attribute of the vehicles in the `formation` array. | [^2]: At least one of attributes `formation` or `simplified_characteristics` must be present, depending on the selected `model_fidelity`. @@ -62,18 +65,18 @@ The `train_type` attribute categorizes trains into three main groups: - `emergency` - Fire, rescue, and emergency response trains - `snow_removal` - Seasonal service trains -### Attributes in "simplified_characteristics" +### Simplified Characteristics When using `model_fidelity: "simplified"`, the following attributes define the basic motion parameters of a train: -| Attributes | Necessity | Description | -| -------------------- | --------- | ----------- | -| `speed_limit` | required | Maximum permitted speed (km/h) | -| `length` | required | Total length of the train (m) | -| `acceleration` | required | Constant acceleration rate (m/s²) | -| `deceleration` | required | Constant service braking rate (m/s²) (negative value) | -| `emergency_deceleration` | optional | Emergency braking rate (m/s²) (negative value). If not specified, defaults to the value of `deceleration` | -| `coasting` | optional | Deceleration rate when coasting (m/s²) (negative value). Defaults to 0 if not specified | +| Attributes | Necessity | Description | +| ---------------------------- | --------- | ----------- | +| `speed_limit` | required | Maximum permitted speed (km/h) | +| `length` | required | Total length of the train (m) | +| `acceleration` | required | Constant acceleration rate (m/s²) | +| `deceleration` | required | Constant service braking rate (m/s²) (negative value) | +| `emergency_deceleration` | optional | Emergency braking rate (m/s²) (negative value). If not specified, defaults to the value of `deceleration` | +| `coasting` | optional | Deceleration rate when coasting (m/s²) (negative value). Defaults to 0 if not specified | These simplified characteristics are used when detailed vehicle dynamics are not required or available. They provide a basic but efficient way to model train movement for high-level planning and simple simulations. @@ -83,23 +86,112 @@ All attributes for a vehicle are collected under the array `vehicles: -` in alph | Attributes | Necessity | Description | | -------------------- | --------- | ----------- | -| `air_resistance` | optional | Coefficient for air resistance (-) | -| `base_resistance` | optional | Coefficient for basic resistance (‰) | -| `description` | optional | Description of the vehicle | | `id` | required | Identifier of the vehicle | +| `vehicle_type` | required | Type of vehicle; values: `traction unit`, `freight`, `passenger`, `multiple unit`, or `non-revenue` | +| `mass` | required | Empty mass (dead weight) (t)| | `length` | required | Length of the vehicle (m) | -| `load_limit` | optional | Maximum permitted load (t) | -| `mass_traction` | optional | Mass on powered axles (t) | -| `mass` | required | Empty mass (dead weight) (t) | -| `picture` | optional | A [URI](https://en.wikipedia.org/wiki/Uniform_Resource_Identifier) with a picture for humans | -| `power_type` | optional | Type of propulsion; values: `hydraulic`, `electric`, `steam`, or `misc` | -| `rolling_resistance` | optional | Coefficient for resistance of rolling axles (-) | -| `rotation_mass` | optional | Factor for rotating mass; >= 1 | +| `load_limit` | optional | Maximum permitted load (t), Defaults to 0 if not specified | | `speed_limit` | optional | Maximum permitted speed (km/h) | -| `tractive_effort` | optional | Tractive effort as pairs of speed (km/h) and tractive force (kN) | -| `vehicle_type` | required | Type of vehicle; values: `traction unit`, `freight`, `passenger`, `multiple unit`, or `non-revenue` | - -### Units +| `description` | optional | Description of the vehicle | +| `power_type` | optional | Type of propulsion; values: `hydraulic`, `electric`, `steam`, or `misc` | +| `mass_traction` | optional | Mass on powered axles (t), Defaults to 0 if not specified | +| `picture` | optional | A [URI](https://en.wikipedia.org/wiki/Uniform_Resource_Identifier) with a picture for humans | +| `resistance` | optional | Vehicle resistance characteristics. See [Resistance Configuration](#resistance-configuration) below. | +| `traction` | optional | Traction characteristics. See [Traction Configuration](#traction-configuration) below. | +| `brakes` | optional | Braking system configuration. See [Brakes Configuration](#brakes-configuration) below. In a Train formation, at least one vehicle must have a `brakes` object. | + +### Resistance Configuration + +The `resistance` object defines the vehicle's resistance characteristics: + +| Attributes | Necessity | Description | +| -------------------- | ------------- | ----------- | +| `model_fidelity` | required | Type of resistance model; values: `table` or `calculated`. | +| `rotation_mass` | optional[^3] | Factor for rotating mass (-), greater or equal to 1. | +| `base_resistance` | optional[^3] | Basic resistance coefficient (‰). | +| `rolling_resistance` | optional[^3] | Rolling resistance coefficient (-). | +| `air_resistance` | optional[^3] | Air resistance coefficient (-). | +| `resistance_effort` | optional[^4] | [Array of speed-force pairs](#array-of-speed-force-pairs) defining resistance. | + +[^3]: Required when using `model_fidelity: "calculated"` +[^4]: Required when using `model_fidelity: "table"` + +### Traction Configuration + +The `traction` object defines the vehicle's traction characteristics: + +| Attributes | Necessity | Description | +| ----------------------- | ------------- | ----------- | +| `model_fidelity` | required | Type of traction model; values: `table` or `calculated`. | +| `rated_power` | optional[^5] | Rated power of the vehicle (kW). | +| `initial_tractive_force`| optional[^5] | Initial tractive force (kN). | +| `tractive_effort` | optional[^6] | [Array of speed-force pairs](#array-of-speed-force-pairs) defining tractive effort. | + +[^5]: Required when using `model_fidelity: "calculated"` +[^6]: Required when using `model_fidelity: "table"` + +### Brakes Configuration + +The `brakes` object defines the vehicle's braking characteristics: + +| Attributes | Necessity | Description | +| ----------------------- | --------- | ----------- | +| `model_fidelity` | required | Type of brake model; values: `force`, `one-part-deceleration`, `two-part-deceleration`, `three-part-deceleration` | +| `deceleration` | optional[^7] | Final/maximum braking deceleration (m/s²). Negative value. | +| `emergency_deceleration`| optional | Maximum emergency braking deceleration (m/s²). Negative value. If not specified, defaults to the value of `deceleration`| +| `reaction_time` | optional[^8] | Time between need recognition and control activation (s) | +| `response_time` | optional[^9] | Time to reach 5% of final braking deceleration (s) | +| `threshold_time` | optional[^9] | Time to develop from 5% to 95% of final deceleration (s) | +| `eddy_current_brake` | optional[^10] | Eddy current brake characteristics | +| `electrodynamic_brake` | optional[^10] | Electrodynamic brake characteristics | +| `friction_brake` | optional[^10] | Friction brake characteristics | + +[^7]: Required when using `model_fidelity: "one-part-deceleration"`, `model_fidelity: "two-part-deceleration"`, or `model_fidelity: "three-part-deceleration"` +[^8]: Required when using `model_fidelity: "two-part-deceleration"` or `model_fidelity: "three-part-deceleration"` +[^9]: Required when using `model_fidelity: "three-part-deceleration"` +[^10]: Only used when using `model_fidelity: "force"`, at least one of `eddy_current_brake`, `electrodynamic_brake`, or `friction_brake` is required for `model_fidelity: "force"` + +The braking system can include one or more of these brake types: + +- `eddy_current_brake` + | Attributes | Necessity | Description | + | ----------------------- | ------------- | ----------- | + | `model_fidelity` | required | Type of eddy current brake model; values: `table` or `calculated`. | + | `min_speed` | optional[^11] | Minimum speed for the brake to engage (km/h). Minimum value is 0. | + | `max_brake_effort` | optional[^11] | Maximum brake effort (kN). Minimum value is 0. | + | `power` | optional[^11] | Power of the brake (kW). Minimum value is 0. | + | `brake_effort` | optional[^12] | [Array of speed-force pairs](#array-of-speed-force-pairs) defining brake effort | + +- `electrodynamic_brake` + | Attributes | Necessity | Description | + | ----------------------- | ------------- | ----------- | + | `model_fidelity` | required | Type of electrodynamic brake model; values: `table` or `calculated`. | + | `max_brake_force` | optional[^11] | Maximum brake force (kN). Minimum value is 0. | + | `speed_control_range` | optional[^11] | Speed range for control (km/h). Minimum value is 0. | + | `speed_power_limit` | optional[^11] | Speed limit for power (km/h). Minimum value is 0. | + | `speed_field_weakening` | optional[^11] | Field weakening speed (km/h). Minimum value is 0. | + | `brake_effort` | optional[^12] | [Array of speed-force pairs](#array-of-speed-force-pairs) defining brake effort | + +- `friction_brake` + | Attributes | Necessity | Description | + | ----------------------- | ------------- | ----------- | + | `model_fidelity` | required | Type of friction brake model; values: `table` or `calculated`. | + | `service_brake_effort` | optional[^11] | Full service brake force (kN). Minimum value is 0. | + | `emergency_brake_effort`| optional[^11] | Full emergency brake force (kN). Minimum value is 0. | + | `brake_regime` | optional[^11] | Brake regime type; values: `P`, `G`, or `R` | + | `brake_effort` | optional[^12] | [Array of speed-force pairs](#array-of-speed-force-pairs) defining brake effort | + +[^11]: Required when using `model_fidelity: "calculated"` +[^12]: Required when using `model_fidelity: "table"` + +### Array of speed-force pairs + +| Attributes | Necessity | Description | +| ----------------------- | ------------- | ----------- | +| `speed` | required | Speed at which the brake effort is applied (km/h) | +| `force` | required | Brake force applied at the specified speed (kN) | + +# Units The schema uses common railway units for all numerical values: @@ -108,9 +200,15 @@ The schema uses common railway units for all numerical values: | Speed | km/h | Kilometers per hour | | Mass | t | Metric tons | | Length | m | Meters | +| Time | s | Seconds | | Force | kN | Kilonewton | | Power | kW | Kilowatt | | Acceleration | m/s² | Meters per second squared | | Resistance | ‰ | Per mille (mm/m) | | Unit-less | - | Dimensionless values | +# Examples + +An example file showing a train with model_fidelity `simplified` vehicle dynamics can be found in [rolling-stock.example.simplified.yaml](rolling-stock.example.simplified.yaml). + +An example file showing a train with model_fidelity `detailed` vehicle dynamics can be found in [rolling-stock.example.detailed.yaml](rolling-stock.example.detailed.yaml). The example includes various vehicle types with different characteristics, including resistance, traction, and braking systems. diff --git a/doc/rolling-stock.example.detailed.yaml b/doc/rolling-stock.example.detailed.yaml new file mode 100644 index 0000000..a4706ab --- /dev/null +++ b/doc/rolling-stock.example.detailed.yaml @@ -0,0 +1,149 @@ +%YAML 1.2 +--- +schema: https://railtoolkit.org/schema/rolling-stock.json +schema_version: "2024.07" +model_fidelity: "detailed" # Options: "simplified", "detailed" +trains: + - id: IC2000-detailed + description: "Intercity with locomotive BR193, 2 first class, 1 dining car and 5 second class coaches" + train_type: long_distance + formation: [Vectron,Avmmz,Avmmz,WRkmz,Bpmmz,Bpmmz,Bpmmz,Bpmmz,Bpmmz] + loading_factor: [1.0,0.6,0.6,0.4,0.8,0.8,0.8,0.8,0.8] + +vehicles: + - id: Vectron + vehicle_type: traction unit + power_type: electric + picture: https://commons.wikimedia.org/wiki/File:Siemens_Vectron_193_837.jpg + description: "Siemens Vectron MS" + # Main attributes + length: 18.98 # m + mass: 89.0 # t + mass_traction: 89.0 # t + speed_limit: 200 # km/h + resistance: # Resistance coefficients (optional) + model_fidelity: "table" # "table" or "calculated" + rotation_mass: 1.09 # - + base_resistance: 2.2 # ‰ + rolling_resistance: 1.4 # - + air_resistance: 6.0 # - + resistance_effort: + - speed: 0.0 # in km/h + force: 2.2 # in ‰ + - speed: 60.0 + force: 2.2 + - speed: 100.0 + force: 2.2 + - speed: 160.0 + force: 2.2 + - speed: 200.0 + force: 2.2 + traction: # Traction characteristics (optional) + model_fidelity: "table" # "table" or "calculated" + rated_power: 8000.0 # kW + initial_tractive_force: 94.400 # kN + tractive_effort: + - speed: 0.0 # in km/h + force: 94.400 # in kN + - speed: 60.0 + force: 80.000 + - speed: 100.0 + force: 80.000 + - speed: 160.0 + force: 80.000 + - speed: 200.0 + force: 80.000 + brakes: # Braking system configuration (optional) + model_fidelity: "force" # "force", "deceleration-one-part", "deceleration-two-part" or "deceleration-three-part" + + reaction_time: 3 # in s the time span between recognizing the need to initiate a braking action and the actual activation of the control (required for model_fidelity: "deceleration-two-part", "deceleration-three-part") + response_time: 1.5 # in s the time span between the activation of the controls and the establishment of 5% of the final braking deceleration (required for model_fidelity: "deceleration-three-part") + threshold_time: 4 # in s the time span to develop the braking deceleration from 5% of the final/maximum deceleration to 95% of the final/maximum deceleration (required for model_fidelity: "deceleration-three-part") + deceleration: -0.95 # in m/s² the final/maximum braking deceleration (required for model_fidelity: "deceleration-one-part", "deceleration-two-part", "deceleration-three-part") + emergency_deceleration: -1.5 # in m/s² the final/maximum deceleration during emergency braking (required for model_fidelity: "deceleration-one-part", "deceleration-two-part", "deceleration-three-part") + + eddy_current_brake: # Eddy current brake characteristics (at least one of eddy_current_brake, electrodynamic_brake or friction_brake is required for model_fidelity: "force") + model_fidelity: "calculated" # "table" or "calculated" + min_speed: 10.0 # in km/h + max_brake_effort: 94.400 # in kN + power: 1000.0 # in kW + brake_effort: + - speed: 0.0 # in km/h + force: 94.400 # in kN + - speed: 10.0 # in km/h + force: 80.000 # in kN + electrodynamic_brake: # Electrodynamic brake characteristics (at least one of eddy_current_brake, electrodynamic_brake or friction_brake is required for model_fidelity: "force") + model_fidelity: "calculated" # "table" or "calculated" + max_brake_force: 94.400 # in kN + speed_control_range: 10.0 # in km/h + speed_power_limit: 160.0 # in km/h + speed_field_weakening: 200.0 # in km/h, optional, field weakening of the three-phase field + brake_effort: + - speed: 0.0 # in km/h + force: 94.400 # in kN + - speed: 10.0 # in km/h + force: 80.000 # in kN + friction_brake: # Friction brake characteristics (at least one of eddy_current_brake, electrodynamic_brake or friction_brake is required for model_fidelity: "force") + model_fidelity: "table" # "table" or "calculated" + service_brake_effort: # Full service brake force in kN + emergency_brake_effort: # Full emergency brake force in kN + brake_regime: "P" # "P","G" or "R" + brake_effort: + - speed: 0.0 # in km/h + force: 94.400 # in kN + - speed: 10.0 # in km/h + force: 80.000 # in kN + - id: Avmmz + description: "First class coach" + vehicle_type: passenger + length: 26.4 # m + mass: 47.0 # t + load_limit: 10.8 # t + speed_limit: 200 # km/h + brakes: + friction_brake: + model_fidelity: "table" # "table" or "calculated" + service_brake_effort: # Full service brake force in kN + emergency_brake_effort: # Full emergency brake force in kN + brake_regime: "P" # "P","G" or "R" + brake_effort: + - speed: 0.0 # in km/h + force: 94.400 # in kN + - speed: 10.0 # in km/h + force: 80.000 # in kN + - id: WRkmz + description: "Dining car" + vehicle_type: passenger + length: 26.4 # m + mass: 52.0 # t + load_limit: 12.0 # t + speed_limit: 200 # km/h + brakes: + friction_brake: + model_fidelity: "table" # "table" or "calculated" + service_brake_effort: # Full service brake force in kN + emergency_brake_effort: # Full emergency brake force in kN + brake_regime: "P" # "P","G" or "R" + brake_effort: + - speed: 0.0 # in km/h + force: 94.400 # in kN + - speed: 10.0 # in km/h + force: 80.000 # in kN + - id: Bpmmz + description: "Second class coach" + vehicle_type: passenger + length: 26.4 # m + mass: 45.0 # t + load_limit: 16.0 # t + speed_limit: 200 # km/h + brakes: + friction_brake: + model_fidelity: "table" # "table" or "calculated" + service_brake_effort: # Full service brake force in kN + emergency_brake_effort: # Full emergency brake force in kN + brake_regime: "P" # "P","G" or "R" + brake_effort: + - speed: 0.0 # in km/h + force: 94.400 # in kN + - speed: 10.0 # in km/h + force: 80.000 # in kN diff --git a/doc/rolling-stock.example.effort_table.yaml b/doc/rolling-stock.example.effort_table.yaml deleted file mode 100644 index 9bfbeeb..0000000 --- a/doc/rolling-stock.example.effort_table.yaml +++ /dev/null @@ -1,71 +0,0 @@ -%YAML 1.2 ---- -schema: https://railtoolkit.org/schema/rolling-stock.json -schema_version: "2024.07" -model_fidelity: "effort_tables" # Options: "simplified_characteristics", "effort_tables", "physical_model" -trains: - - id: IC2000-effort_tables - description: "Intercity with locomotive BR193, 2 first class, 1 dining car and 5 second class coaches" - formation: [Vectron,Avmmz,Avmmz,WRkmz,Bpmmz,Bpmmz,Bpmmz,Bpmmz,Bpmmz] - loading_factor: [1.0,0.6,0.6,0.4,0.8,0.8,0.8,0.8,0.8] - -vehicles: - - id: Vectron # (required) - vehicle_type: traction unit - power_type: electric - picture: https://commons.wikimedia.org/wiki/File:Siemens_Vectron_193_837.jpg - ## main attributes - length: 18.98 # in m (required) - mass: 89.0 # in t (required) - speed_limit: 200 # in km/h (required) - # coefficients for the vehicles resistance - resistances: - rotation_mass: 1.09 # source: "Railway Timetabling & Operations" by Hansen, et al., 2014, p. 71 for the traction unit - base_resistance: 2.2 # source: "Fahrdynamik des Schienenverkehrs" by Wende, 2003, p. 151 for "4-achsige Diesellokomot." -> 2.2 ‰ to 3.5 ‰ - rolling_resistance: 1.4 # source: "Fahrdynamik des Schienenverkehrs" by Wende, 2003, p. 151 for "f_WW0" -> 1.2 ‰ to 1.6 ‰ - air_resistance: 6.0 # source: "Fahrdynamik des Schienenverkehrs" by Wende, 2003, p. 151 for "4-achsig, eckige Kopfform" with "Stromabnehmer" -> 5000 N to 6000 N; modified for the used formula - traction: - mass_traction: 89.0 # in t (mass on driving axles) - #power: 4500 # kW - # tractive effort as pairs of speed and tractive effort - tractive_effort: - - speed: 0 - effort: 10000 - - speed: 100 - effort: 10000 - - speed: 200 - effort: 10000 - brakes: - - - - id: Avmmz - description: "First class car" - vehicle_type: passenger - ## main attributes - length: 26.4 # in m (required) - mass: 47.0 # in t (required) - load_limit: 10.8 # in t (required) - speed_limit: 200 # in km/h (required) - - - id: WRkmz - description: "dining car" - vehicle_type: passenger - ## main attributes - length: 26.4 # in m (required) - mass: 52.0 # in t (required) - load_limit: 12.0 # in t (required) - speed_limit: 200 # in km/h (required) - - - id: Bpmmz - description: "Second class car" - vehicle_type: passenger - ## main attributes - length: 26.4 # in m (required) - mass: 45.0 # in t (required) - load_limit: 16.0 # in t (required) - speed_limit: 200 # in km/h (required) - -# TODO: -# * add effort tables -# * resistance -# * 2 level braking model diff --git a/doc/rolling-stock.example.simplified.yaml b/doc/rolling-stock.example.simplified.yaml index 8f98cca..3886a0b 100644 --- a/doc/rolling-stock.example.simplified.yaml +++ b/doc/rolling-stock.example.simplified.yaml @@ -2,7 +2,7 @@ --- schema: https://railtoolkit.org/schema/rolling-stock.json schema_version: "2024.07" -model_fidelity: "simplified" # Options: "simplified", "detailed" +model_fidelity: "simplified" # Options: "simplified", "detailed" trains: - id: IC2000-simplified description: "Intercity with a Vectron locomotive, 2 first class, 1 dining car and 5 second class coaches" diff --git a/src/rolling-stock.json b/src/rolling-stock.json index 9a77a29..6aacc36 100644 --- a/src/rolling-stock.json +++ b/src/rolling-stock.json @@ -4,11 +4,60 @@ "title": "Rolling Stock", "description": "Rolling stock data", "type": "object", - "required": [ "schema", "schema_version" ], + "required": [ "schema", "schema_version", "model_fidelity" ], "anyOf": [ {"required": [ "trains" ] }, {"required": [ "vehicles" ] } ], + "$defs": { + "id": { + "type": "string", + "description": "Identifier of the train or vehicle" + }, + "speed": { + "type": "number", + "description": "Speed in kilometers per hour" + }, + "time": { + "type": "number", + "description": "Time in seconds" + }, + "length": { + "type": "number", + "description": "Length in meters" + }, + "mass": { + "type": "number", + "description": "Mass in metric tons" + }, + "force": { + "type": "number", + "description": "Force in kN" + }, + "power": { + "type": "number", + "description": "Power in kW" + }, + "resistance": { + "type": "number", + "description": "Resistance in per mille" + }, + "acceleration": { + "type": "number", + "description": "Acceleration in m/s²" + }, + "effort": { + "type": "array", + "items": { + "type": "object", + "required": ["speed", "force"], + "properties": { + "speed": { "$ref": "#/$defs/speed", "minimum": 0 }, + "force": { "$ref": "#/$defs/force" } + } + } + } + }, "properties": { "schema": { "description": "Identifier of the schema", @@ -23,24 +72,20 @@ "model_fidelity": { "description": "Level of detail for train dynamics modeling. Defaults to 'detailed' if not specified", "type": "string", - "enum": [ "simplified", "detailed" ], - "default": "detailed" + "enum": [ "simplified", "detailed" ] }, "trains": { "type": "array", "minItems": 1, "items": { "type": "object", - "required": [ "id", "train_type" ], + "required": [ "id" ], "anyOf": [ {"required": [ "formation" ]}, {"required": [ "simplified_characteristics" ]} ], "properties": { - "id": { - "description": "Identifier of the train", - "type": "string" - }, + "id": { "$ref": "#/$defs/id" }, "description": { "description": "Description of the train", "type": "string" @@ -63,40 +108,45 @@ "type": "string" } }, + "loading_factor": { + "description": "Loading factor of the train", + "type": "array", + "minItems": 1, + "uniqueItems": false, + "items": { + "type": "number", + "minimum": 0, + "maximum": 1 + } + }, "simplified_characteristics": { "description": "Basic motion parameters when using simplified model fidelity", "type": "object", "required": ["speed_limit", "length", "acceleration", "deceleration"], "properties": { "speed_limit": { - "description": "Maximum permitted speed (km/h)", - "type": "number", + "$ref": "#/$defs/speed", "exclusiveMinimum": 0 }, "length": { - "description": "Total length of the train (m)", - "type": "number", + "$ref": "#/$defs/length", "exclusiveMinimum": 0 }, "acceleration": { - "description": "Constant acceleration rate (m/s²)", - "type": "number", + "$ref": "#/$defs/acceleration", "exclusiveMinimum": 0 }, "deceleration": { - "description": "Constant service braking rate (m/s²) (negative value)", - "type": "number", + "$ref": "#/$defs/acceleration", "exclusiveMaximum": 0 }, "emergency_deceleration": { - "description": "Emergency braking rate (m/s²) (negative value). If not specified, defaults to the value of deceleration", - "type": "number", + "$ref": "#/$defs/acceleration", "exclusiveMaximum": 0 }, "coasting": { - "description": "Deceleration rate when coasting (m/s²) (negative value). Defaults to 0 if not specified", - "type": "number", - "maximum": 0, + "$ref": "#/$defs/acceleration", + "exclusiveMaximum": 0, "default": 0 } } @@ -105,44 +155,36 @@ } }, "vehicles": { - "type": "array", + "type": "array", "minItems": 1, "items": { - "required": [ "id", "vehicle_type", "length", "mass" ], + "required": ["id", "vehicle_type", "length", "mass"], "type": "object", "properties": { - "air_resistance": { - "description": "Coefficient for air resistance (-)", - "type": "number", - "exclusiveMinimum": 0 - }, - "base_resistance": { - "description": "Coefficient for basic resistance (‰)", - "type": "number", - "exclusiveMinimum": 0 + "id": { "$ref": "#/$defs/id" }, + "load_limit": { + "$ref": "#/$defs/mass", + "default": 0 }, - "id": { - "description": "Identifier of the vehicle", - "type": "string" + "vehicle_type": { + "description": "Type of vehicle", + "enum": ["traction unit", "freight", "passenger", "multiple unit", "non-revenue"] }, "length": { - "description": "Length of the vehicle (m)", - "type": "number", + "$ref": "#/$defs/length", "exclusiveMinimum": 0 }, - "load_limit": { - "description": "Maximum permitted load (t)", - "type": "number", + "mass": { + "$ref": "#/$defs/mass", "exclusiveMinimum": 0 }, "mass_traction": { - "description": "Mass on powered axles (t)", - "type": "number", - "exclusiveMinimum": 0 + "$ref": "#/$defs/mass", + "exclusiveMinimum": 0, + "default": 0 }, - "mass": { - "description": "Empty mass (dead weight) (t)", - "type": "number", + "speed_limit": { + "$ref": "#/$defs/speed", "exclusiveMinimum": 0 }, "description": { @@ -156,42 +198,146 @@ }, "power_type": { "description": "Type of propulsion", - "enum": [ "hydraulic", "electric", "steam", "misc" ] - }, - "rolling_resistance": { - "description": "Coefficient for resistance of rolling axles (-)", - "type": "number", - "exclusiveMinimum": 0 - }, - "rotation_mass": { - "description": "Factor for rotating mass; >= 1", - "type": "number", - "minimum": 1 + "enum": ["hydraulic", "electric", "steam", "misc"] }, - "speed_limit": { - "description": "Maximum permitted speed (km/h)", - "type": "number", - "exclusiveMinimum": 0 - }, - "tractive_effort": { - "description": "Tractive effort as pairs of speed (km/h) and tractive force (kN)", - "type": "array", - "minItems": 3, - "uniqueItems": true, - "items": { - "type": "array", - "minItems": 2, - "maxItems": 2, - "uniqueItems": true, - "items": { + "resistance": { + "type": "object", + "required": ["model_fidelity"], + "anyOf": [ + {"required": [ "rotation_mass", "base_resistance", "rolling_resistance", "air_resistance" ] }, + {"required": [ "resistance_effort" ] } + ], + "properties": { + "model_fidelity": { + "type": "string", + "enum": ["table", "calculated"] + }, + "rotation_mass": { + "type": "number", + "minimum": 1 + }, + "base_resistance": { + "type": "number", + "minimum": 0 + }, + "rolling_resistance": { + "type": "number", + "minimum": 0 + }, + "air_resistance": { "type": "number", "minimum": 0 + }, + "resistance_effort": { + "$ref": "#/$defs/effort" } } }, - "vehicle_type": { - "description": "Type of vehicle", - "enum": [ "traction unit", "freight", "passenger", "multiple unit", "non-revenue" ] + "traction": { + "type": "object", + "required": ["model_fidelity"], + "properties": { + "model_fidelity": { + "type": "string", + "enum": ["table", "calculated"] + }, + "rated_power": { + "$ref": "#/$defs/power", + "minimum": 0 + }, + "initial_tractive_force": { + "$ref": "#/$defs/force", + "minimum": 0 + }, + "tractive_effort": { + "$ref": "#/$defs/effort" + } + } + }, + "brakes": { + "type": "object", + "required": ["model_fidelity"], + "anyOf": [ + {"required": [ "deceleration" ] }, + {"required": [ "eddy_current_brake" ] }, + {"required": [ "electrodynamic_brake" ] }, + {"required": [ "friction_brake" ] } + ], + "properties": { + "model_fidelity": { + "type": "string", + "enum": ["force", "one-part-deceleration", "two-part-deceleration", "three-part-deceleration"] + }, + "reaction_time": { "$ref": "#/$defs/time", "minimum": 0 }, + "response_time": { "$ref": "#/$defs/time", "minimum": 0 }, + "threshold_time": { "$ref": "#/$defs/time", "minimum": 0 }, + "deceleration": { "$ref": "#/$defs/acceleration", "maximum": 0 }, + "emergency_deceleration": { "$ref": "#/$defs/acceleration", "maximum": 0 }, + "eddy_current_brake": { + "type": "object", + "required": ["model_fidelity"], + "anyOf": [ + {"required": [ "min_speed", "max_brake_effort", "power", "brake_effort" ] }, + {"required": [ "brake_effort" ] } + ], + "properties": { + "model_fidelity": { + "type": "string", + "enum": ["table", "calculated"] + }, + "min_speed": { "type": "number", "minimum": 0 }, + "max_brake_effort": { "type": "number", "minimum": 0 }, + "power": { "type": "number", "minimum": 0 }, + "brake_effort": { + "$ref": "#/$defs/effort" + } + } + }, + "electrodynamic_brake": { + "type": "object", + "required": ["model_fidelity"], + "anyOf": [ + {"required": [ "max_brake_force", "speed_control_range", "speed_power_limit", "speed_field_weakening", "brake_effort" ] }, + {"required": [ "brake_effort" ] } + ], + "properties": { + "model_fidelity": { + "type": "string", + "enum": ["table", "calculated"] + }, + "max_brake_force": { "type": "number", "minimum": 0 }, + "speed_control_range": { "type": "number", "minimum": 0 }, + "speed_power_limit": { "type": "number", "minimum": 0 }, + "speed_field_weakening": { "type": "number", "minimum": 0 }, + "brake_effort": { + "$ref": "#/$defs/effort" + } + } + }, + "friction_brake": { + "type": "object", + "required": ["model_fidelity"], + "anyOf": [ + {"required": [ "service_brake_effort", "emergency_brake_effort", "brake_regime" ] }, + {"required": [ "brake_effort" ] } + ], + "properties": { + "model_fidelity": { + "type": "string", + "enum": ["table", "calculated"] + }, + "service_brake_effort": { "type": "number", "minimum": 0 }, + "emergency_brake_effort": { "type": "number", "minimum": 0 }, + "brake_regime": { + "type": "string", + "enum": ["P", "G", "R"] + }, + "brake_effort": { + "$ref": "#/$defs/effort" + } + } + } + } } } } diff --git a/test/rolling-stock/invalid/formation_empty.yaml b/test/rolling-stock/invalid/formation_empty.yaml index c539e1c..839fb09 100644 --- a/test/rolling-stock/invalid/formation_empty.yaml +++ b/test/rolling-stock/invalid/formation_empty.yaml @@ -1,8 +1,8 @@ %YAML 1.2 --- schema: https://railtoolkit.org/schema/rolling-stock.json -schema_version: "2022.05" +schema_version: "2024.07" +model_fidelity: "detailed" trains: - - name: test - id: "1" - formation: [] + - id: "1" + formation: [] # Invalid: formation array must have at least 1 item diff --git a/test/rolling-stock/invalid/formation_missing.yaml b/test/rolling-stock/invalid/formation_missing.yaml index 73cc5ae..fd2111e 100644 --- a/test/rolling-stock/invalid/formation_missing.yaml +++ b/test/rolling-stock/invalid/formation_missing.yaml @@ -1,7 +1,7 @@ %YAML 1.2 --- schema: https://railtoolkit.org/schema/rolling-stock.json -schema_version: "2022.05" +schema_version: "2024.07" +model_fidelity: "detailed" trains: - - name: test - id: "1" + - id: "1" # Invalid: missing either formation or simplified_characteristics diff --git a/test/rolling-stock/invalid/invalid_brake_model.yaml b/test/rolling-stock/invalid/invalid_brake_model.yaml new file mode 100644 index 0000000..39d10fa --- /dev/null +++ b/test/rolling-stock/invalid/invalid_brake_model.yaml @@ -0,0 +1,13 @@ +%YAML 1.2 +--- +schema: https://railtoolkit.org/schema/rolling-stock.json +schema_version: "2024.07" +vehicles: + - id: "vehicle1" + vehicle_type: "traction unit" + length: 20 + mass: 80 + brakes: + model_fidelity: "invalid_type" # Invalid brake model type + reaction_time: 1.0 + deceleration: -0.5 \ No newline at end of file diff --git a/test/rolling-stock/invalid/invalid_deceleration.yaml b/test/rolling-stock/invalid/invalid_deceleration.yaml index 527f28b..cba00cb 100644 --- a/test/rolling-stock/invalid/invalid_deceleration.yaml +++ b/test/rolling-stock/invalid/invalid_deceleration.yaml @@ -10,4 +10,5 @@ trains: speed_limit: 160 length: 200 acceleration: 0.5 - deceleration: 0.65 # should be negative \ No newline at end of file + deceleration: 0.65 # Invalid: should be negative + coasting: -0.1 \ No newline at end of file diff --git a/test/rolling-stock/invalid/invalid_loading_factor.yaml b/test/rolling-stock/invalid/invalid_loading_factor.yaml new file mode 100644 index 0000000..8755925 --- /dev/null +++ b/test/rolling-stock/invalid/invalid_loading_factor.yaml @@ -0,0 +1,9 @@ +%YAML 1.2 +--- +schema: https://railtoolkit.org/schema/rolling-stock.json +schema_version: "2024.07" +trains: + - id: "train1" + train_type: "regional" + formation: ["vehicle1"] + loading_factor: [] # Invalid: array must have at least 1 item \ No newline at end of file diff --git a/test/rolling-stock/invalid/invalid_power_type.yaml b/test/rolling-stock/invalid/invalid_power_type.yaml new file mode 100644 index 0000000..9703aed --- /dev/null +++ b/test/rolling-stock/invalid/invalid_power_type.yaml @@ -0,0 +1,10 @@ +%YAML 1.2 +--- +schema: https://railtoolkit.org/schema/rolling-stock.json +schema_version: "2024.07" +vehicles: + - id: "vehicle1" + vehicle_type: "traction unit" + length: 20 + mass: 80 + power_type: "diesel" # Invalid power type - not in enum \ No newline at end of file diff --git a/test/rolling-stock/invalid/invalid_resistance_model.yaml b/test/rolling-stock/invalid/invalid_resistance_model.yaml new file mode 100644 index 0000000..94a10e0 --- /dev/null +++ b/test/rolling-stock/invalid/invalid_resistance_model.yaml @@ -0,0 +1,12 @@ +%YAML 1.2 +--- +schema: https://railtoolkit.org/schema/rolling-stock.json +schema_version: "2024.07" +vehicles: + - id: "vehicle1" + vehicle_type: "traction unit" + length: 20 + mass: 80 + resistance: + model_fidelity: "invalid_model" # Invalid model_fidelity + rotation_mass: 1.1 \ No newline at end of file diff --git a/test/rolling-stock/invalid/invalid_schema_version.yaml b/test/rolling-stock/invalid/invalid_schema_version.yaml new file mode 100644 index 0000000..6d7df86 --- /dev/null +++ b/test/rolling-stock/invalid/invalid_schema_version.yaml @@ -0,0 +1,8 @@ +%YAML 1.2 +--- +schema: https://railtoolkit.org/schema/rolling-stock.json +schema_version: "24.7" # Invalid format - should be YYYY.MM +trains: + - id: "train1" + train_type: "regional" + formation: ["vehicle1"] \ No newline at end of file diff --git a/test/rolling-stock/invalid/invalid_tractive_effort.yaml b/test/rolling-stock/invalid/invalid_tractive_effort.yaml new file mode 100644 index 0000000..f62c034 --- /dev/null +++ b/test/rolling-stock/invalid/invalid_tractive_effort.yaml @@ -0,0 +1,13 @@ +%YAML 1.2 +--- +schema: https://railtoolkit.org/schema/rolling-stock.json +schema_version: "2024.07" +vehicles: + - id: "vehicle1" + vehicle_type: "traction unit" + length: 20 + mass: 80 + tractive_effort: [ + [0, 300], # Invalid: needs at least 3 points + [50, 200] + ] \ No newline at end of file diff --git a/test/rolling-stock/invalid/length.yaml b/test/rolling-stock/invalid/length.yaml index 2bacdbc..fd3e0f6 100644 --- a/test/rolling-stock/invalid/length.yaml +++ b/test/rolling-stock/invalid/length.yaml @@ -1,9 +1,12 @@ %YAML 1.2 --- schema: https://railtoolkit.org/schema/rolling-stock.json -schema_version: "2022.05" +schema_version: "2024.07" +model_fidelity: "detailed" vehicles: - - name: test - id: "1" - vehicle_type: freight - mass: 1 + - id: "1" + vehicle_type: "freight" + mass: 80 + speed_limit: 100 + load_limit: 100 + # length missing - required field diff --git a/test/rolling-stock/invalid/mass.yaml b/test/rolling-stock/invalid/mass.yaml index 05a0370..b393e5b 100644 --- a/test/rolling-stock/invalid/mass.yaml +++ b/test/rolling-stock/invalid/mass.yaml @@ -1,9 +1,12 @@ %YAML 1.2 --- schema: https://railtoolkit.org/schema/rolling-stock.json -schema_version: "2022.05" +schema_version: "2024.07" +model_fidelity: "detailed" vehicles: - - name: test - id: "1" - vehicle_type: freight - length: 1 + - id: "1" + vehicle_type: "freight" + length: 20 + speed_limit: 100 + load_limit: 100 + # mass missing - required field diff --git a/test/rolling-stock/invalid/minimal.yaml b/test/rolling-stock/invalid/minimal.yaml index 49a646e..9961afb 100644 --- a/test/rolling-stock/invalid/minimal.yaml +++ b/test/rolling-stock/invalid/minimal.yaml @@ -1,4 +1,5 @@ %YAML 1.2 --- schema: https://railtoolkit.org/schema/rolling-stock.json -schema_version: "2022.05" +schema_version: "2024.07" +# Invalid: missing required fields model_fidelity and either trains or vehicles diff --git a/test/rolling-stock/invalid/missing_model_fidelity.yaml b/test/rolling-stock/invalid/missing_model_fidelity.yaml new file mode 100644 index 0000000..218dda0 --- /dev/null +++ b/test/rolling-stock/invalid/missing_model_fidelity.yaml @@ -0,0 +1,9 @@ +%YAML 1.2 +--- +schema: https://railtoolkit.org/schema/rolling-stock.json +schema_version: "2024.07" +# model_fidelity missing - required field +trains: + - id: "train1" + train_type: "regional" + formation: ["vehicle1"] \ No newline at end of file diff --git a/test/rolling-stock/invalid/vehicle_type.yaml b/test/rolling-stock/invalid/vehicle_type.yaml index cc353c3..74eb9d2 100644 --- a/test/rolling-stock/invalid/vehicle_type.yaml +++ b/test/rolling-stock/invalid/vehicle_type.yaml @@ -1,10 +1,12 @@ %YAML 1.2 --- schema: https://railtoolkit.org/schema/rolling-stock.json -schema_version: "2022.05" +schema_version: "2024.07" +model_fidelity: "detailed" vehicles: - - name: test - id: "1" - vehicle_type: cargo - length: 1 - mass: 1 + - id: "1" + vehicle_type: "cargo" # Invalid - not in enum + length: 20 + mass: 80 + speed_limit: 100 + load_limit: 100 diff --git a/test/rolling-stock/valid/default_model_fidelity.yaml b/test/rolling-stock/valid/default_model_fidelity.yaml index e05ca37..ade36cc 100644 --- a/test/rolling-stock/valid/default_model_fidelity.yaml +++ b/test/rolling-stock/valid/default_model_fidelity.yaml @@ -2,6 +2,7 @@ --- schema: https://railtoolkit.org/schema/rolling-stock.json schema_version: "2024.07" +model_fidelity: "detailed" trains: - id: "train1" train_type: "regional" diff --git a/test/rolling-stock/valid/detailed_vehicle.yaml b/test/rolling-stock/valid/detailed_vehicle.yaml new file mode 100644 index 0000000..fca4fbc --- /dev/null +++ b/test/rolling-stock/valid/detailed_vehicle.yaml @@ -0,0 +1,35 @@ +%YAML 1.2 +--- +schema: https://railtoolkit.org/schema/rolling-stock.json +schema_version: "2024.07" +model_fidelity: "detailed" +vehicles: + - id: "vehicle1" + vehicle_type: "traction unit" + length: 20 + mass: 80 + load_limit: 100 + speed_limit: 160 + power_type: "electric" + resistance: + model_fidelity: "calculated" + rotation_mass: 1.1 + base_resistance: 2.5 + rolling_resistance: 0.001 + air_resistance: 0.004 + traction: + model_fidelity: "table" + tractive_effort: + - speed: 0 + force: 300 + - speed: 50 + force: 200 + - speed: 100 + force: 100 + brakes: + model_fidelity: "force" + friction_brake: + model_fidelity: "calculated" + service_brake_effort: 150 + emergency_brake_effort: 200 + brake_regime: "P" \ No newline at end of file diff --git a/test/rolling-stock/valid/formation.yaml b/test/rolling-stock/valid/formation.yaml index 4fd2e85..bf1c7b1 100644 --- a/test/rolling-stock/valid/formation.yaml +++ b/test/rolling-stock/valid/formation.yaml @@ -13,7 +13,9 @@ vehicles: vehicle_type: "traction unit" length: 20 mass: 80 + load_limit: 0 - id: "vehicle2" vehicle_type: "passenger" length: 26.4 - mass: 32 \ No newline at end of file + mass: 32 + load_limit: 10 \ No newline at end of file diff --git a/test/rolling-stock/valid/simplified_train.yaml b/test/rolling-stock/valid/simplified_train.yaml new file mode 100644 index 0000000..96ea825 --- /dev/null +++ b/test/rolling-stock/valid/simplified_train.yaml @@ -0,0 +1,15 @@ +%YAML 1.2 +--- +schema: https://railtoolkit.org/schema/rolling-stock.json +schema_version: "2024.07" +model_fidelity: "simplified" +trains: + - id: "train1" + train_type: "regional" + simplified_characteristics: + speed_limit: 160 + length: 200 + acceleration: 0.5 + deceleration: -0.65 + emergency_deceleration: -1.2 + coasting: -0.1 \ No newline at end of file diff --git a/test/rolling-stock/valid/trains.yaml b/test/rolling-stock/valid/trains.yaml index fb1776d..3841a35 100644 --- a/test/rolling-stock/valid/trains.yaml +++ b/test/rolling-stock/valid/trains.yaml @@ -2,6 +2,7 @@ --- schema: https://railtoolkit.org/schema/rolling-stock.json schema_version: "2024.07" +model_fidelity: "detailed" trains: - name: test id: "1" diff --git a/test/rolling-stock/valid/vehicles.yaml b/test/rolling-stock/valid/vehicles.yaml index a1339cd..48d03eb 100644 --- a/test/rolling-stock/valid/vehicles.yaml +++ b/test/rolling-stock/valid/vehicles.yaml @@ -2,6 +2,7 @@ --- schema: https://railtoolkit.org/schema/rolling-stock.json schema_version: "2024.07" +model_fidelity: "detailed" vehicles: - name: test id: "1" From 695e6232c38c8a7717e46b0a142011ed3f997289 Mon Sep 17 00:00:00 2001 From: Martin Scheidt <142348+kaat0@users.noreply.github.com> Date: Tue, 25 Feb 2025 23:34:10 +0100 Subject: [PATCH 16/24] include units as $defs --- src/running-path.json | 46 ++++++++++++++++++++++--------------------- 1 file changed, 24 insertions(+), 22 deletions(-) diff --git a/src/running-path.json b/src/running-path.json index 3fdf7ff..ad673e4 100644 --- a/src/running-path.json +++ b/src/running-path.json @@ -16,6 +16,24 @@ "const": "2024.07", "pattern": "[2-9][0-9][0-9][0-9].[0-1][0-9]" }, + "$defs": { + "id": { + "type": "string", + "description": "Identifier of the train or vehicle" + }, + "speed": { + "type": "number", + "description": "Speed in kilometers per hour" + }, + "length": { + "type": "number", + "description": "Length in meters" + }, + "resistance": { + "type": "number", + "description": "Resistance in per mille" + } + }, "paths": { "type": "array", "minItems": 1, @@ -24,10 +42,7 @@ "required": [ "id", "characteristic_sections" ], "type": "object", "properties": { - "id": { - "description": "Identifier of the running path", - "type": "string" - }, + "id": { "$ref": "#/$defs/id" }, "description": { "description": "Description of the running path", "type": "string" @@ -47,19 +62,12 @@ ]} ], "properties": { - "position": { - "description": "Position along the path (m)", - "type": "number" - }, + "position": { "$ref": "#/$defs/length" }, "speed": { - "description": "Maximum permitted speed (km/h)", - "type": "number", + "$ref": "#/$defs/speed", "minimum": 0 }, - "resistance": { - "description": "Track resistance (‰)", - "type": "number" - } + "resistance": { "$ref": "#/$defs/resistance" } } } }, @@ -75,14 +83,8 @@ "measure" ], "properties": { - "position": { - "description": "Position along the path (m)", - "type": "number" - }, - "id": { - "description": "Identifier of the point of interest", - "type": "string" - }, + "id": { "$ref": "#/$defs/id" }, + "position": { "$ref": "#/$defs/length" }, "description": { "description": "Description of the point of interest", "type": "string" From f4c22a5e1cbdc5ffc945e6c7965ae42c3ade9199 Mon Sep 17 00:00:00 2001 From: Martin Scheidt <142348+kaat0@users.noreply.github.com> Date: Tue, 25 Feb 2025 23:45:11 +0100 Subject: [PATCH 17/24] added missing values for valid file --- doc/rolling-stock.example.detailed.yaml | 19 +++++++++++-------- 1 file changed, 11 insertions(+), 8 deletions(-) diff --git a/doc/rolling-stock.example.detailed.yaml b/doc/rolling-stock.example.detailed.yaml index a4706ab..df02650 100644 --- a/doc/rolling-stock.example.detailed.yaml +++ b/doc/rolling-stock.example.detailed.yaml @@ -85,8 +85,8 @@ vehicles: force: 80.000 # in kN friction_brake: # Friction brake characteristics (at least one of eddy_current_brake, electrodynamic_brake or friction_brake is required for model_fidelity: "force") model_fidelity: "table" # "table" or "calculated" - service_brake_effort: # Full service brake force in kN - emergency_brake_effort: # Full emergency brake force in kN + service_brake_effort: 20.0 # Full service brake force in kN + emergency_brake_effort: 100.0 # Full emergency brake force in kN brake_regime: "P" # "P","G" or "R" brake_effort: - speed: 0.0 # in km/h @@ -101,10 +101,11 @@ vehicles: load_limit: 10.8 # t speed_limit: 200 # km/h brakes: + model_fidelity: "force" # "force", "deceleration-one-part", "deceleration-two-part", "deceleration-three-part" friction_brake: model_fidelity: "table" # "table" or "calculated" - service_brake_effort: # Full service brake force in kN - emergency_brake_effort: # Full emergency brake force in kN + service_brake_effort: 20.0 # Full service brake force in kN + emergency_brake_effort: 100.0 # Full emergency brake force in kN brake_regime: "P" # "P","G" or "R" brake_effort: - speed: 0.0 # in km/h @@ -119,10 +120,11 @@ vehicles: load_limit: 12.0 # t speed_limit: 200 # km/h brakes: + model_fidelity: "force" # "force", "deceleration-one-part", "deceleration-two-part", "deceleration-three-part" friction_brake: model_fidelity: "table" # "table" or "calculated" - service_brake_effort: # Full service brake force in kN - emergency_brake_effort: # Full emergency brake force in kN + service_brake_effort: 20.0 # Full service brake force in kN + emergency_brake_effort: 100.0 # Full emergency brake force in kN brake_regime: "P" # "P","G" or "R" brake_effort: - speed: 0.0 # in km/h @@ -137,10 +139,11 @@ vehicles: load_limit: 16.0 # t speed_limit: 200 # km/h brakes: + model_fidelity: "force" # "force", "deceleration-one-part", "deceleration-two-part", "deceleration-three-part" friction_brake: model_fidelity: "table" # "table" or "calculated" - service_brake_effort: # Full service brake force in kN - emergency_brake_effort: # Full emergency brake force in kN + service_brake_effort: 20.0 # Full service brake force in kN + emergency_brake_effort: 100.0 # Full emergency brake force in kN brake_regime: "P" # "P","G" or "R" brake_effort: - speed: 0.0 # in km/h From da6e18bf05492bfa53dccbb0cd2e63ec5f7cec75 Mon Sep 17 00:00:00 2001 From: Martin Scheidt <142348+kaat0@users.noreply.github.com> Date: Wed, 26 Feb 2025 07:31:37 +0100 Subject: [PATCH 18/24] fixed running-path tests --- src/running-path.json | 36 +++++++++---------- test/running-path/invalid/not_null_speed.yaml | 2 +- .../{invalid => valid}/consistency.yaml | 0 3 files changed, 19 insertions(+), 19 deletions(-) rename test/running-path/{invalid => valid}/consistency.yaml (100%) diff --git a/src/running-path.json b/src/running-path.json index ad673e4..c59ea1f 100644 --- a/src/running-path.json +++ b/src/running-path.json @@ -4,6 +4,24 @@ "title": "Running Path", "description": "Consecutive values of permitted speed and resistance", "type": "object", + "$defs": { + "id": { + "type": "string", + "description": "Identifier of the train or vehicle" + }, + "speed": { + "type": "number", + "description": "Speed in kilometers per hour" + }, + "length": { + "type": "number", + "description": "Length in meters" + }, + "resistance": { + "type": "number", + "description": "Resistance in per mille" + } + }, "required": [ "schema", "schema_version", "paths" ], "properties": { "schema": { @@ -16,24 +34,6 @@ "const": "2024.07", "pattern": "[2-9][0-9][0-9][0-9].[0-1][0-9]" }, - "$defs": { - "id": { - "type": "string", - "description": "Identifier of the train or vehicle" - }, - "speed": { - "type": "number", - "description": "Speed in kilometers per hour" - }, - "length": { - "type": "number", - "description": "Length in meters" - }, - "resistance": { - "type": "number", - "description": "Resistance in per mille" - } - }, "paths": { "type": "array", "minItems": 1, diff --git a/test/running-path/invalid/not_null_speed.yaml b/test/running-path/invalid/not_null_speed.yaml index b746867..b510441 100644 --- a/test/running-path/invalid/not_null_speed.yaml +++ b/test/running-path/invalid/not_null_speed.yaml @@ -7,6 +7,6 @@ paths: id: "1" characteristic_sections: - position: 0.0 - speed: 0 + speed: -0.1 - position: 0.1 speed: 0 diff --git a/test/running-path/invalid/consistency.yaml b/test/running-path/valid/consistency.yaml similarity index 100% rename from test/running-path/invalid/consistency.yaml rename to test/running-path/valid/consistency.yaml From bff459322f678762e24e05a2249da344da953dc9 Mon Sep 17 00:00:00 2001 From: Martin Scheidt <142348+kaat0@users.noreply.github.com> Date: Wed, 26 Feb 2025 18:51:04 +0100 Subject: [PATCH 19/24] updated roadmap --- CONTRIBUTING.md | 29 ++++++++++++++++++++++------- 1 file changed, 22 insertions(+), 7 deletions(-) diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 9ab65c9..d7201e8 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -5,20 +5,35 @@ email, or any other method with the owners of this repository before making a ch Please note we have a code of conduct, please follow it in all your interactions with the project. -# Pull Request Process +## Pull Request Process -## Minor Changes and Fixes +### Minor Changes and Fixes -TODO: DESCRIPTION +For minor changes and fixes: +1. Ensure your code follows the existing style and conventions +2. Update the documentation if necessary +3. Add tests for any new functionality +4. Make sure all tests pass locally +5. Create a Pull Request with a clear description of the changes -## Breaking Changes +### Breaking Changes -TODO: DESCRIPTION +For breaking changes: +1. Open an issue first to discuss the proposed changes +2. Document all breaking changes clearly in your Pull Request +3. Update all relevant documentation +4. Add or update tests to cover the changes +5. Update the version number according to [Calendar Versioning](https://calver.org) +6. Provide migration instructions if applicable ## Roadmap - * include breaking model in rolling-stock schema - * fallback with constant acceleration and deceleration +Current development priorities: +* test braking model in rolling-stock schema +* improve braking model in rolling-stock schema +* add model for interlocking +* add model for topology +* add model for timetbling and blocking times ## Add yourself as a contributor From a0753075428cb8cb8c3f5dc2136de66b4cc192f2 Mon Sep 17 00:00:00 2001 From: Martin Scheidt <142348+kaat0@users.noreply.github.com> Date: Wed, 26 Feb 2025 18:54:24 +0100 Subject: [PATCH 20/24] updated license --- LICENSE | 2 +- README.md | 5 +++-- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/LICENSE b/LICENSE index 26ded92..d65d51d 100644 --- a/LICENSE +++ b/LICENSE @@ -1,6 +1,6 @@ ISC License -Copyright (c) 2022 - 2024, Martin Scheidt (orcid.org/0000-0002-9384-8945) +Copyright (c) 2022 - 2025, Martin Scheidt (orcid.org/0000-0002-9384-8945) Permission to use, copy, modify, and/or distribute this software for any purpose with or without fee is hereby granted, provided that the above copyright notice and this permission notice appear in all copies. diff --git a/README.md b/README.md index a300b8d..7dca0ea 100644 --- a/README.md +++ b/README.md @@ -68,7 +68,8 @@ Each test suite includes: See * [Rolling-Stock.md](doc/Rolling-Stock.md) and -* [Running-Path.md](doc/Running-Path.md) +* [Running-Path.md](doc/Running-Path.md) + for information about the used attributes in the sub schemas. ### Units @@ -114,7 +115,7 @@ See [CONTRIBUTING.md](CONTRIBUTING.md) file if you are interested to contribute. [![Open Source Initiative Approved License logo](https://149753425.v2.pressablecdn.com/wp-content/uploads/2009/06/OSIApproved_100X125.png "Open Source Initiative Approved License logo")](https://opensource.org) - Copyright (c) 2022 - 2024, Martin Scheidt (orcid.org/0000-0002-9384-8945) (ISC License) + Copyright (c) 2022 - 2025, Martin Scheidt (orcid.org/0000-0002-9384-8945) (ISC License) Permission to use, copy, modify, and/or distribute this software for any purpose with or without fee is hereby granted, provided that the above copyright notice and this permission notice appear in all copies. From 8ec15106b1fc83a87e1c1b16cdc542afb3e1b1d4 Mon Sep 17 00:00:00 2001 From: Martin Scheidt <142348+kaat0@users.noreply.github.com> Date: Mon, 25 Aug 2025 12:15:44 +0200 Subject: [PATCH 21/24] allowed for empty list (see issue #24) --- doc/Running-Path.md | 10 ++++++---- src/running-path.json | 2 +- 2 files changed, 7 insertions(+), 5 deletions(-) diff --git a/doc/Running-Path.md b/doc/Running-Path.md index a7a638d..fbc725f 100644 --- a/doc/Running-Path.md +++ b/doc/Running-Path.md @@ -10,7 +10,9 @@ | -------------------- | --------- | ------------------------------------------------------ | | `schema` | required | Identifier of the JSON schema. | | `schema_version` | required | Version of the JSON schema. | -| `paths` | required | An array of at least one [path](#Attributes-in-paths). | +| `paths` | required | An array of at least one[^1] [path](#Attributes-in-paths). | + +[^1]: At least one item in `paths` should be present. For scaffolding purposes, `paths` can be empty. ## Attributes in "paths" @@ -30,10 +32,10 @@ Characteristic sections are sections of a running path within which properties, | Attributes | Necessity | Description | | -------------------- | --------- | ----------- | | `position` | required | Position along the path (m) | -| `speed` | optional[^1] | Maximum permitted speed (km/h) | -| `resistance` | optional[^1] | Track resistance (‰) | +| `speed` | optional[^2] | Maximum permitted speed (km/h) | +| `resistance` | optional[^2] | Track resistance (‰) | -[^1]: At least one of attributes `speed` or `resistance` must be present. +[^2]: At least one of attributes `speed` or `resistance` must be present. ## Attributes in "points_of_interest" diff --git a/src/running-path.json b/src/running-path.json index c59ea1f..64e6d94 100644 --- a/src/running-path.json +++ b/src/running-path.json @@ -36,7 +36,7 @@ }, "paths": { "type": "array", - "minItems": 1, + "minItems": 0, "uniqueItems": true, "items": { "required": [ "id", "characteristic_sections" ], From c9584f483b85fac379b73ea1be9f0358712d424d Mon Sep 17 00:00:00 2001 From: Martin Scheidt <142348+kaat0@users.noreply.github.com> Date: Mon, 25 Aug 2025 12:29:41 +0200 Subject: [PATCH 22/24] added data type for attributes (see issue #25) --- doc/Rolling-Stock.md | 179 +++++++++++++++++++++---------------------- doc/Running-Path.md | 47 ++++++------ 2 files changed, 112 insertions(+), 114 deletions(-) diff --git a/doc/Rolling-Stock.md b/doc/Rolling-Stock.md index 21c99ac..02e9dbe 100644 --- a/doc/Rolling-Stock.md +++ b/doc/Rolling-Stock.md @@ -6,13 +6,13 @@ ## Preamble -| Attributes | Necessity | Description | -| -------------------- | ------------ | ----------- | -| `schema` | required | Identifier of the JSON schema. | -| `schema_version` | required | Version of the JSON schema (current version `2024.07`). | -| `model_fidelity` | required | Level of detail for train dynamics modeling. Values: `simplified`, `detailed`. | -| `trains` | optional[^1] | An array of [trains](#Attributes-in-trains). | -| `vehicles` | optional[^1] | An array of [vehicles](#Attributes-in-vehicles). | +| Attributes | Data Type | Necessity | Description | +| -------------------- | ------------ | ------------ | ----------- | +| `schema` | string | required | Identifier of the JSON schema. | +| `schema_version` | string | required | Version of the JSON schema (current version `2024.07`). | +| `model_fidelity` | string | required | Level of detail for train dynamics modeling. Values: `simplified`, `detailed`. | +| `trains` | array | optional[^1] | A list of [trains](#Attributes-in-trains). | +| `vehicles` | array | optional[^1] | A list of [vehicles](#Attributes-in-vehicles). | [^1]: At least one of attributes `trains` or `vehicles` must be present. @@ -32,14 +32,14 @@ The `model_fidelity` attribute is not only used at the top level but also plays All attributes for a train are collected under the array `trains: -` in alphabetical order: -| Attributes | Necessity | Description | -| ---------------------------- | ------------ | ----------- | -| `id` | required | Identifier of the train. | -| `description` | optional | Description of the train. | -| `train_type` | optional | Type of train service. See [Train Types](#train-types) below. | -| `simplified_characteristics` | optional[^2] | Basic motion parameters when using `simplified_characteristics` model fidelity. See [Simplified Characteristics](#simplified-characteristics) below. | -| `formation` | optional[^2] | A Collection of vehicles that form the train referenced by vehicle `id`. Front and rear end of the train are defined by the first and last vehicle in the array. | -| `loading_factor` | optional | Ratio (between 0 and 1) of the actual load to the maximum load capacity of the train. Using the `load_limit` attribute of the vehicles in the `formation` array. | +| Attributes | Data Type | Necessity | Description | +| ---------------------------- | ------------ | ------------ | ----------- | +| `id` | string | required | Identifier of the train. | +| `description` | string | optional | Description of the train. | +| `train_type` | string | optional | Type of train service. See [Train Types](#train-types) below. | +| `simplified_characteristics` | object | optional[^2] | Basic motion parameters when using `simplified_characteristics` model fidelity. See [Simplified Characteristics](#simplified-characteristics) below. | +| `formation` | array | optional[^2] | A Collection of vehicles that form the train referenced by vehicle `id`. Front and rear end of the train are defined by the first and last vehicle in the array. | +| `loading_factor` | array | optional | Ratio (between 0 and 1) of the actual load to the maximum load capacity of the train. Using the `load_limit` attribute of the vehicles in the `formation` array. | [^2]: At least one of attributes `formation` or `simplified_characteristics` must be present, depending on the selected `model_fidelity`. @@ -69,49 +69,48 @@ The `train_type` attribute categorizes trains into three main groups: When using `model_fidelity: "simplified"`, the following attributes define the basic motion parameters of a train: -| Attributes | Necessity | Description | -| ---------------------------- | --------- | ----------- | -| `speed_limit` | required | Maximum permitted speed (km/h) | -| `length` | required | Total length of the train (m) | -| `acceleration` | required | Constant acceleration rate (m/s²) | -| `deceleration` | required | Constant service braking rate (m/s²) (negative value) | -| `emergency_deceleration` | optional | Emergency braking rate (m/s²) (negative value). If not specified, defaults to the value of `deceleration` | -| `coasting` | optional | Deceleration rate when coasting (m/s²) (negative value). Defaults to 0 if not specified | - +| Attributes | Data Type | Necessity | Description | +| ---------------------------- | ------------ | --------- | ----------- | +| `speed_limit` | number | required | Maximum permitted speed (km/h) | +| `length` | number | required | Total length of the train (m) | +| `acceleration` | number | required | Constant acceleration rate (m/s²) | +| `deceleration` | number | required | Constant service braking rate (m/s²) (negative value) | +| `emergency_deceleration` | number | optional | Emergency braking rate (m/s²) (negative value). If not specified, defaults to the value of `deceleration` | +| `coasting` | number | optional | Deceleration rate when coasting (m/s²) (negative value). Defaults to 0 if not specified | These simplified characteristics are used when detailed vehicle dynamics are not required or available. They provide a basic but efficient way to model train movement for high-level planning and simple simulations. ## Attributes in "vehicles" All attributes for a vehicle are collected under the array `vehicles: -` in alphabetical order: -| Attributes | Necessity | Description | -| -------------------- | --------- | ----------- | -| `id` | required | Identifier of the vehicle | -| `vehicle_type` | required | Type of vehicle; values: `traction unit`, `freight`, `passenger`, `multiple unit`, or `non-revenue` | -| `mass` | required | Empty mass (dead weight) (t)| -| `length` | required | Length of the vehicle (m) | -| `load_limit` | optional | Maximum permitted load (t), Defaults to 0 if not specified | -| `speed_limit` | optional | Maximum permitted speed (km/h) | -| `description` | optional | Description of the vehicle | -| `power_type` | optional | Type of propulsion; values: `hydraulic`, `electric`, `steam`, or `misc` | -| `mass_traction` | optional | Mass on powered axles (t), Defaults to 0 if not specified | -| `picture` | optional | A [URI](https://en.wikipedia.org/wiki/Uniform_Resource_Identifier) with a picture for humans | -| `resistance` | optional | Vehicle resistance characteristics. See [Resistance Configuration](#resistance-configuration) below. | -| `traction` | optional | Traction characteristics. See [Traction Configuration](#traction-configuration) below. | -| `brakes` | optional | Braking system configuration. See [Brakes Configuration](#brakes-configuration) below. In a Train formation, at least one vehicle must have a `brakes` object. | +| Attributes | Data Type | Necessity | Description | +| -------------------- | ------------ | --------- | ----------- | +| `id` | string | required | Identifier of the vehicle | +| `vehicle_type` | string | required | Type of vehicle; values: `traction unit`, `freight`, `passenger`, `multiple unit`, or `non-revenue` | +| `mass` | number | required | Empty mass (dead weight) (t)| +| `length` | number | required | Length of the vehicle (m) | +| `load_limit` | number | optional | Maximum permitted load (t), Defaults to 0 if not specified | +| `speed_limit` | number | optional | Maximum permitted speed (km/h) | +| `description` | string | optional | Description of the vehicle | +| `power_type` | string | optional | Type of propulsion; values: `hydraulic`, `electric`, `steam`, or `misc` | +| `mass_traction` | number | optional | Mass on powered axles (t), Defaults to 0 if not specified | +| `picture` | string | optional | A [URI](https://en.wikipedia.org/wiki/Uniform_Resource_Identifier) with a picture for humans | +| `resistance` | object | optional | Vehicle resistance characteristics. See [Resistance Configuration](#resistance-configuration) below. | +| `traction` | object | optional | Traction characteristics. See [Traction Configuration](#traction-configuration) below. | +| `brakes` | object | optional | Braking system configuration. See [Brakes Configuration](#brakes-configuration) below. In a Train formation, at least one vehicle must have a `brakes` object. |Data Type ### Resistance Configuration The `resistance` object defines the vehicle's resistance characteristics: -| Attributes | Necessity | Description | -| -------------------- | ------------- | ----------- | -| `model_fidelity` | required | Type of resistance model; values: `table` or `calculated`. | -| `rotation_mass` | optional[^3] | Factor for rotating mass (-), greater or equal to 1. | -| `base_resistance` | optional[^3] | Basic resistance coefficient (‰). | -| `rolling_resistance` | optional[^3] | Rolling resistance coefficient (-). | -| `air_resistance` | optional[^3] | Air resistance coefficient (-). | -| `resistance_effort` | optional[^4] | [Array of speed-force pairs](#array-of-speed-force-pairs) defining resistance. | +| Attributes | Data Type | Necessity | Description | +| -------------------- | ------------ | ------------- | ----------- | +| `model_fidelity` | string | required | Type of resistance model; values: `table` or `calculated`. | +| `rotation_mass` | number | optional[^3] | Factor for rotating mass (-), greater or equal to 1. | +| `base_resistance` | number | optional[^3] | Basic resistance coefficient (‰). | +| `rolling_resistance` | number | optional[^3] | Rolling resistance coefficient (-). | +| `air_resistance` | number | optional[^3] | Air resistance coefficient (-). | +| `resistance_effort` | array | optional[^4] | [Array of speed-force pairs](#array-of-speed-force-pairs) defining resistance. | [^3]: Required when using `model_fidelity: "calculated"` [^4]: Required when using `model_fidelity: "table"` @@ -120,12 +119,12 @@ The `resistance` object defines the vehicle's resistance characteristics: The `traction` object defines the vehicle's traction characteristics: -| Attributes | Necessity | Description | -| ----------------------- | ------------- | ----------- | -| `model_fidelity` | required | Type of traction model; values: `table` or `calculated`. | -| `rated_power` | optional[^5] | Rated power of the vehicle (kW). | -| `initial_tractive_force`| optional[^5] | Initial tractive force (kN). | -| `tractive_effort` | optional[^6] | [Array of speed-force pairs](#array-of-speed-force-pairs) defining tractive effort. | +| Attributes | Data Type | Necessity | Description | +| ----------------------- | ------------ | ------------- | ----------- | +| `model_fidelity` | string | required | Type of traction model; values: `table` or `calculated`. | +| `rated_power` | number | optional[^5] | Rated power of the vehicle (kW). | +| `initial_tractive_force`| number | optional[^5] | Initial tractive force (kN). | +| `tractive_effort` | array | optional[^6] | [Array of speed-force pairs](#array-of-speed-force-pairs) defining tractive effort. | [^5]: Required when using `model_fidelity: "calculated"` [^6]: Required when using `model_fidelity: "table"` @@ -134,17 +133,17 @@ The `traction` object defines the vehicle's traction characteristics: The `brakes` object defines the vehicle's braking characteristics: -| Attributes | Necessity | Description | -| ----------------------- | --------- | ----------- | -| `model_fidelity` | required | Type of brake model; values: `force`, `one-part-deceleration`, `two-part-deceleration`, `three-part-deceleration` | -| `deceleration` | optional[^7] | Final/maximum braking deceleration (m/s²). Negative value. | -| `emergency_deceleration`| optional | Maximum emergency braking deceleration (m/s²). Negative value. If not specified, defaults to the value of `deceleration`| -| `reaction_time` | optional[^8] | Time between need recognition and control activation (s) | -| `response_time` | optional[^9] | Time to reach 5% of final braking deceleration (s) | -| `threshold_time` | optional[^9] | Time to develop from 5% to 95% of final deceleration (s) | -| `eddy_current_brake` | optional[^10] | Eddy current brake characteristics | -| `electrodynamic_brake` | optional[^10] | Electrodynamic brake characteristics | -| `friction_brake` | optional[^10] | Friction brake characteristics | +| Attributes | Data Type | Necessity | Description | +| ----------------------- | ------------ | --------- | ----------- | +| `model_fidelity` | string | required | Type of brake model; values: `force`, `one-part-deceleration`, `two-part-deceleration`, `three-part-deceleration` | +| `deceleration` | number | optional[^7] | Final/maximum braking deceleration (m/s²). Negative value. | +| `emergency_deceleration`| number | optional | Maximum emergency braking deceleration (m/s²). Negative value. If not specified, defaults to the value of `deceleration`| +| `reaction_time` | number | optional[^8] | Time between need recognition and control activation (s) | +| `response_time` | number | optional[^9] | Time to reach 5% of final braking deceleration (s) | +| `threshold_time` | number | optional[^9] | Time to develop from 5% to 95% of final deceleration (s) | +| `eddy_current_brake` | object | optional[^10] | Eddy current brake characteristics | +| `electrodynamic_brake` | object | optional[^10] | Electrodynamic brake characteristics | +| `friction_brake` | object | optional[^10] | Friction brake characteristics | [^7]: Required when using `model_fidelity: "one-part-deceleration"`, `model_fidelity: "two-part-deceleration"`, or `model_fidelity: "three-part-deceleration"` [^8]: Required when using `model_fidelity: "two-part-deceleration"` or `model_fidelity: "three-part-deceleration"` @@ -154,42 +153,42 @@ The `brakes` object defines the vehicle's braking characteristics: The braking system can include one or more of these brake types: - `eddy_current_brake` - | Attributes | Necessity | Description | - | ----------------------- | ------------- | ----------- | - | `model_fidelity` | required | Type of eddy current brake model; values: `table` or `calculated`. | - | `min_speed` | optional[^11] | Minimum speed for the brake to engage (km/h). Minimum value is 0. | - | `max_brake_effort` | optional[^11] | Maximum brake effort (kN). Minimum value is 0. | - | `power` | optional[^11] | Power of the brake (kW). Minimum value is 0. | - | `brake_effort` | optional[^12] | [Array of speed-force pairs](#array-of-speed-force-pairs) defining brake effort | + | Attributes | Data Type | Necessity | Description | + | ----------------------- | ------------ | ------------- | ----------- | + | `model_fidelity` | string | required | Type of eddy current brake model; values: `table` or `calculated`. | + | `min_speed` | number | optional[^11] | Minimum speed for the brake to engage (km/h). Minimum value is 0. | + | `max_brake_effort` | number | optional[^11] | Maximum brake effort (kN). Minimum value is 0. | + | `power` | number | optional[^11] | Power of the brake (kW). Minimum value is 0. | + | `brake_effort` | array | optional[^12] | [Array of speed-force pairs](#array-of-speed-force-pairs) defining brake effort | - `electrodynamic_brake` - | Attributes | Necessity | Description | - | ----------------------- | ------------- | ----------- | - | `model_fidelity` | required | Type of electrodynamic brake model; values: `table` or `calculated`. | - | `max_brake_force` | optional[^11] | Maximum brake force (kN). Minimum value is 0. | - | `speed_control_range` | optional[^11] | Speed range for control (km/h). Minimum value is 0. | - | `speed_power_limit` | optional[^11] | Speed limit for power (km/h). Minimum value is 0. | - | `speed_field_weakening` | optional[^11] | Field weakening speed (km/h). Minimum value is 0. | - | `brake_effort` | optional[^12] | [Array of speed-force pairs](#array-of-speed-force-pairs) defining brake effort | + | Attributes | Data Type | Necessity | Description | + | ----------------------- | ------------ | ------------- | ----------- | + | `model_fidelity` | string | required | Type of electrodynamic brake model; values: `table` or `calculated`. | + | `max_brake_force` | number | optional[^11] | Maximum brake force (kN). Minimum value is 0. | + | `speed_control_range` | number | optional[^11] | Speed range for control (km/h). Minimum value is 0. | + | `speed_power_limit` | number | optional[^11] | Speed limit for power (km/h). Minimum value is 0. | + | `speed_field_weakening` | number | optional[^11] | Field weakening speed (km/h). Minimum value is 0. | + | `brake_effort` | array | optional[^12] | [Array of speed-force pairs](#array-of-speed-force-pairs) defining brake effort | - `friction_brake` - | Attributes | Necessity | Description | - | ----------------------- | ------------- | ----------- | - | `model_fidelity` | required | Type of friction brake model; values: `table` or `calculated`. | - | `service_brake_effort` | optional[^11] | Full service brake force (kN). Minimum value is 0. | - | `emergency_brake_effort`| optional[^11] | Full emergency brake force (kN). Minimum value is 0. | - | `brake_regime` | optional[^11] | Brake regime type; values: `P`, `G`, or `R` | - | `brake_effort` | optional[^12] | [Array of speed-force pairs](#array-of-speed-force-pairs) defining brake effort | + | Attributes | Data Type | Necessity | Description | + | ----------------------- | ------------ | ------------- | ----------- | + | `model_fidelity` | string | required | Type of friction brake model; values: `table` or `calculated`. | + | `service_brake_effort` | number | optional[^11] | Full service brake force (kN). Minimum value is 0. | + | `emergency_brake_effort`| number | optional[^11] | Full emergency brake force (kN). Minimum value is 0. | + | `brake_regime` | string | optional[^11] | Brake regime type; values: `P`, `G`, or `R` | + | `brake_effort` | array | optional[^12] | [Array of speed-force pairs](#array-of-speed-force-pairs) defining brake effort | [^11]: Required when using `model_fidelity: "calculated"` [^12]: Required when using `model_fidelity: "table"` ### Array of speed-force pairs -| Attributes | Necessity | Description | -| ----------------------- | ------------- | ----------- | -| `speed` | required | Speed at which the brake effort is applied (km/h) | -| `force` | required | Brake force applied at the specified speed (kN) | +| Attributes | Data Type | Necessity | Description | +| ----------------------- | ------------ | ------------- | ----------- | +| `speed` | number | required | Speed at which the brake effort is applied (km/h) | +| `force` | number | required | Brake force applied at the specified speed (kN) | # Units diff --git a/doc/Running-Path.md b/doc/Running-Path.md index fbc725f..c029a53 100644 --- a/doc/Running-Path.md +++ b/doc/Running-Path.md @@ -6,11 +6,11 @@ ## Preamble -| Attributes | Necessity | Description | -| -------------------- | --------- | ------------------------------------------------------ | -| `schema` | required | Identifier of the JSON schema. | -| `schema_version` | required | Version of the JSON schema. | -| `paths` | required | An array of at least one[^1] [path](#Attributes-in-paths). | +| Attributes | Data Type | Necessity | Description | +| -------------------- | --------- | --------- | ------------------------------------------------------ | +| `schema` | string | required | Identifier of the JSON schema. | +| `schema_version` | string | required | Version of the JSON schema. | +| `paths` | array | required | An array of at least one[^1] [path](#Attributes-in-paths). | [^1]: At least one item in `paths` should be present. For scaffolding purposes, `paths` can be empty. @@ -18,22 +18,22 @@ All attributes for a path are collected under the array `paths: -` in alphabetical order: -| Attributes | Necessity | Description | -| ------------------------ | --------- | ----------- | -| `id` | required | Identifier of the running path. | -| `description` | optional | Description of the running path. | -| `characteristic_sections`| required | An array of [characteristic sections](#Attributes-in-characteristic_sections). | -| `points_of_interest` | optional | An array of [points of interest](#Attributes-in-points_of_interest). | +| Attributes | Data Type | Necessity | Description | +| ------------------------ | --------- | --------- | ----------- | +| `id` | string | required | Identifier of the running path. | +| `description` | string | optional | Description of the running path. | +| `characteristic_sections`| array | required | An array of [characteristic sections](#Attributes-in-characteristic_sections). | +| `points_of_interest` | array | optional | An array of [points of interest](#Attributes-in-points_of_interest). | ## Attributes in "characteristic_sections" Characteristic sections are sections of a running path within which properties, such as permitted speed or track resistances, do not change. -| Attributes | Necessity | Description | -| -------------------- | --------- | ----------- | -| `position` | required | Position along the path (m) | -| `speed` | optional[^2] | Maximum permitted speed (km/h) | -| `resistance` | optional[^2] | Track resistance (‰) | +| Attributes | Data Type | Necessity | Description | +| -------------------- | --------- | --------- | ----------- | +| `position` | number | required | Position along the path (m) | +| `speed` | number | optional[^2] | Maximum permitted speed (km/h) | +| `resistance` | number | optional[^2] | Track resistance (‰) | [^2]: At least one of attributes `speed` or `resistance` must be present. @@ -41,13 +41,13 @@ Characteristic sections are sections of a running path within which properties, Points of interest mark specific locations along the path where measurements or observations should be taken. -| Attributes | Necessity | Description | -| -------------------- | --------- | ----------- | -| `position` | required | Position along the path (m) | -| `id` | required | Identifier of the point of interest | -| `description` | optional | Description of the point of interest | -| `groups` | optional | Groups this point belongs to | -| `measure` | required | Position on train to measure; values: `front`, `middle`, or `rear` | +| Attributes | Data Type | Necessity | Description | +| -------------------- | --------- | --------- | ----------- | +| `position` | number | required | Position along the path (m) | +| `id` | string | required | Identifier of the point of interest | +| `description` | string | optional | Description of the point of interest | +| `groups` | array | optional | Groups this point belongs to; an array of unique string identifiers | +| `measure` | enum | required | Position on train to measure; values: `front`, `middle`, or `rear` | # Units @@ -58,7 +58,6 @@ The schema uses common railway units for all numerical values: | Position | m | Meters | | Speed | km/h | Kilometers per hour | | Resistance | ‰ | Per mille (mm/m) | -| Unit-less | - | Dimensionless values | # Example An example file showing a running path with block sections, signals, and platforms can be found in [running-path.example.yaml](running-path.example.yaml). The example includes various points of interest such as platform tracks, route signals, and clearing points, each with specific positions and descriptions. Below is a visual representation of the YAML structure: From 8a048b5ed728cf72057daf59719c3c1ae2949277 Mon Sep 17 00:00:00 2001 From: Martin Scheidt <142348+kaat0@users.noreply.github.com> Date: Mon, 25 Aug 2025 20:40:50 +0200 Subject: [PATCH 23/24] making descriptions for arrays for human-friendly --- doc/Rolling-Stock.md | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/doc/Rolling-Stock.md b/doc/Rolling-Stock.md index 02e9dbe..f97feb4 100644 --- a/doc/Rolling-Stock.md +++ b/doc/Rolling-Stock.md @@ -30,7 +30,7 @@ The `model_fidelity` attribute is not only used at the top level but also plays ## Attributes in "trains" -All attributes for a train are collected under the array `trains: -` in alphabetical order: +All attributes for a train are collected under `trains: -` in alphabetical order: | Attributes | Data Type | Necessity | Description | | ---------------------------- | ------------ | ------------ | ----------- | @@ -81,7 +81,7 @@ These simplified characteristics are used when detailed vehicle dynamics are not ## Attributes in "vehicles" -All attributes for a vehicle are collected under the array `vehicles: -` in alphabetical order: +All attributes for a vehicle are collected under `vehicles: -` in alphabetical order: | Attributes | Data Type | Necessity | Description | | -------------------- | ------------ | --------- | ----------- | @@ -110,7 +110,7 @@ The `resistance` object defines the vehicle's resistance characteristics: | `base_resistance` | number | optional[^3] | Basic resistance coefficient (‰). | | `rolling_resistance` | number | optional[^3] | Rolling resistance coefficient (-). | | `air_resistance` | number | optional[^3] | Air resistance coefficient (-). | -| `resistance_effort` | array | optional[^4] | [Array of speed-force pairs](#array-of-speed-force-pairs) defining resistance. | +| `resistance_effort` | array | optional[^4] | [List of speed-force pairs](#list-of-speed-force-pairs) defining resistance. | [^3]: Required when using `model_fidelity: "calculated"` [^4]: Required when using `model_fidelity: "table"` @@ -124,7 +124,7 @@ The `traction` object defines the vehicle's traction characteristics: | `model_fidelity` | string | required | Type of traction model; values: `table` or `calculated`. | | `rated_power` | number | optional[^5] | Rated power of the vehicle (kW). | | `initial_tractive_force`| number | optional[^5] | Initial tractive force (kN). | -| `tractive_effort` | array | optional[^6] | [Array of speed-force pairs](#array-of-speed-force-pairs) defining tractive effort. | +| `tractive_effort` | array | optional[^6] | [List of speed-force pairs](#list-of-speed-force-pairs) defining tractive effort. | [^5]: Required when using `model_fidelity: "calculated"` [^6]: Required when using `model_fidelity: "table"` @@ -159,7 +159,7 @@ The braking system can include one or more of these brake types: | `min_speed` | number | optional[^11] | Minimum speed for the brake to engage (km/h). Minimum value is 0. | | `max_brake_effort` | number | optional[^11] | Maximum brake effort (kN). Minimum value is 0. | | `power` | number | optional[^11] | Power of the brake (kW). Minimum value is 0. | - | `brake_effort` | array | optional[^12] | [Array of speed-force pairs](#array-of-speed-force-pairs) defining brake effort | + | `brake_effort` | array | optional[^12] | [List of speed-force pairs](#list-of-speed-force-pairs) defining brake effort | - `electrodynamic_brake` | Attributes | Data Type | Necessity | Description | @@ -169,7 +169,7 @@ The braking system can include one or more of these brake types: | `speed_control_range` | number | optional[^11] | Speed range for control (km/h). Minimum value is 0. | | `speed_power_limit` | number | optional[^11] | Speed limit for power (km/h). Minimum value is 0. | | `speed_field_weakening` | number | optional[^11] | Field weakening speed (km/h). Minimum value is 0. | - | `brake_effort` | array | optional[^12] | [Array of speed-force pairs](#array-of-speed-force-pairs) defining brake effort | + | `brake_effort` | array | optional[^12] | [List of speed-force pairs](#list-of-speed-force-pairs) defining brake effort | - `friction_brake` | Attributes | Data Type | Necessity | Description | @@ -178,12 +178,12 @@ The braking system can include one or more of these brake types: | `service_brake_effort` | number | optional[^11] | Full service brake force (kN). Minimum value is 0. | | `emergency_brake_effort`| number | optional[^11] | Full emergency brake force (kN). Minimum value is 0. | | `brake_regime` | string | optional[^11] | Brake regime type; values: `P`, `G`, or `R` | - | `brake_effort` | array | optional[^12] | [Array of speed-force pairs](#array-of-speed-force-pairs) defining brake effort | + | `brake_effort` | array | optional[^12] | [List of speed-force pairs](#list-of-speed-force-pairs) defining brake effort | [^11]: Required when using `model_fidelity: "calculated"` [^12]: Required when using `model_fidelity: "table"` -### Array of speed-force pairs +### List of speed-force pairs | Attributes | Data Type | Necessity | Description | | ----------------------- | ------------ | ------------- | ----------- | From 193df3ec1e7c87e23b4655447a7c058b24377788 Mon Sep 17 00:00:00 2001 From: Martin Scheidt <142348+kaat0@users.noreply.github.com> Date: Mon, 25 Aug 2025 20:48:43 +0200 Subject: [PATCH 24/24] snake_case for vehicle_type (see issue #26) --- doc/Rolling-Stock.md | 2 +- doc/rolling-stock.example.detailed.yaml | 2 +- src/rolling-stock.json | 2 +- test/rolling-stock/invalid/invalid_power_type.yaml | 2 +- test/rolling-stock/invalid/invalid_resistance_model.yaml | 2 +- test/rolling-stock/invalid/invalid_tractive_effort.yaml | 2 +- test/rolling-stock/valid/default_model_fidelity.yaml | 2 +- test/rolling-stock/valid/detailed_vehicle.yaml | 2 +- test/rolling-stock/valid/formation.yaml | 2 +- test/rolling-stock/valid/mixed_fidelity.yaml | 2 +- 10 files changed, 10 insertions(+), 10 deletions(-) diff --git a/doc/Rolling-Stock.md b/doc/Rolling-Stock.md index f97feb4..16e0cca 100644 --- a/doc/Rolling-Stock.md +++ b/doc/Rolling-Stock.md @@ -86,7 +86,7 @@ All attributes for a vehicle are collected under `vehicles: -` in alphabetical o | Attributes | Data Type | Necessity | Description | | -------------------- | ------------ | --------- | ----------- | | `id` | string | required | Identifier of the vehicle | -| `vehicle_type` | string | required | Type of vehicle; values: `traction unit`, `freight`, `passenger`, `multiple unit`, or `non-revenue` | +| `vehicle_type` | string | required | Type of vehicle; values: `traction_unit`, `freight`, `passenger`, `multiple_unit`, or `non-revenue` | | `mass` | number | required | Empty mass (dead weight) (t)| | `length` | number | required | Length of the vehicle (m) | | `load_limit` | number | optional | Maximum permitted load (t), Defaults to 0 if not specified | diff --git a/doc/rolling-stock.example.detailed.yaml b/doc/rolling-stock.example.detailed.yaml index df02650..71dae48 100644 --- a/doc/rolling-stock.example.detailed.yaml +++ b/doc/rolling-stock.example.detailed.yaml @@ -12,7 +12,7 @@ trains: vehicles: - id: Vectron - vehicle_type: traction unit + vehicle_type: traction_unit power_type: electric picture: https://commons.wikimedia.org/wiki/File:Siemens_Vectron_193_837.jpg description: "Siemens Vectron MS" diff --git a/src/rolling-stock.json b/src/rolling-stock.json index 6aacc36..20cd780 100644 --- a/src/rolling-stock.json +++ b/src/rolling-stock.json @@ -168,7 +168,7 @@ }, "vehicle_type": { "description": "Type of vehicle", - "enum": ["traction unit", "freight", "passenger", "multiple unit", "non-revenue"] + "enum": ["traction_unit", "freight", "passenger", "multiple_unit", "non-revenue"] }, "length": { "$ref": "#/$defs/length", diff --git a/test/rolling-stock/invalid/invalid_power_type.yaml b/test/rolling-stock/invalid/invalid_power_type.yaml index 9703aed..15088b9 100644 --- a/test/rolling-stock/invalid/invalid_power_type.yaml +++ b/test/rolling-stock/invalid/invalid_power_type.yaml @@ -4,7 +4,7 @@ schema: https://railtoolkit.org/schema/rolling-stock.json schema_version: "2024.07" vehicles: - id: "vehicle1" - vehicle_type: "traction unit" + vehicle_type: "traction_unit" length: 20 mass: 80 power_type: "diesel" # Invalid power type - not in enum \ No newline at end of file diff --git a/test/rolling-stock/invalid/invalid_resistance_model.yaml b/test/rolling-stock/invalid/invalid_resistance_model.yaml index 94a10e0..3e90dd1 100644 --- a/test/rolling-stock/invalid/invalid_resistance_model.yaml +++ b/test/rolling-stock/invalid/invalid_resistance_model.yaml @@ -4,7 +4,7 @@ schema: https://railtoolkit.org/schema/rolling-stock.json schema_version: "2024.07" vehicles: - id: "vehicle1" - vehicle_type: "traction unit" + vehicle_type: "traction_unit" length: 20 mass: 80 resistance: diff --git a/test/rolling-stock/invalid/invalid_tractive_effort.yaml b/test/rolling-stock/invalid/invalid_tractive_effort.yaml index f62c034..9cc0790 100644 --- a/test/rolling-stock/invalid/invalid_tractive_effort.yaml +++ b/test/rolling-stock/invalid/invalid_tractive_effort.yaml @@ -4,7 +4,7 @@ schema: https://railtoolkit.org/schema/rolling-stock.json schema_version: "2024.07" vehicles: - id: "vehicle1" - vehicle_type: "traction unit" + vehicle_type: "traction_unit" length: 20 mass: 80 tractive_effort: [ diff --git a/test/rolling-stock/valid/default_model_fidelity.yaml b/test/rolling-stock/valid/default_model_fidelity.yaml index ade36cc..96c569e 100644 --- a/test/rolling-stock/valid/default_model_fidelity.yaml +++ b/test/rolling-stock/valid/default_model_fidelity.yaml @@ -9,6 +9,6 @@ trains: formation: ["vehicle1"] vehicles: - id: "vehicle1" - vehicle_type: "traction unit" + vehicle_type: "traction_unit" length: 20 mass: 80 \ No newline at end of file diff --git a/test/rolling-stock/valid/detailed_vehicle.yaml b/test/rolling-stock/valid/detailed_vehicle.yaml index fca4fbc..5eb5081 100644 --- a/test/rolling-stock/valid/detailed_vehicle.yaml +++ b/test/rolling-stock/valid/detailed_vehicle.yaml @@ -5,7 +5,7 @@ schema_version: "2024.07" model_fidelity: "detailed" vehicles: - id: "vehicle1" - vehicle_type: "traction unit" + vehicle_type: "traction_unit" length: 20 mass: 80 load_limit: 100 diff --git a/test/rolling-stock/valid/formation.yaml b/test/rolling-stock/valid/formation.yaml index bf1c7b1..27e68ac 100644 --- a/test/rolling-stock/valid/formation.yaml +++ b/test/rolling-stock/valid/formation.yaml @@ -10,7 +10,7 @@ trains: formation: ["vehicle1", "vehicle2"] vehicles: - id: "vehicle1" - vehicle_type: "traction unit" + vehicle_type: "traction_unit" length: 20 mass: 80 load_limit: 0 diff --git a/test/rolling-stock/valid/mixed_fidelity.yaml b/test/rolling-stock/valid/mixed_fidelity.yaml index 2ca8768..fbb2f71 100644 --- a/test/rolling-stock/valid/mixed_fidelity.yaml +++ b/test/rolling-stock/valid/mixed_fidelity.yaml @@ -16,6 +16,6 @@ trains: formation: ["vehicle1"] vehicles: - id: "vehicle1" - vehicle_type: "multiple unit" + vehicle_type: "multiple_unit" length: 25 mass: 85 \ No newline at end of file