{"id":20101020,"url":"https://github.com/fkie-cad/ipal_transcriber","last_synced_at":"2025-05-06T06:32:55.588Z","repository":{"id":37437046,"uuid":"423842882","full_name":"fkie-cad/ipal_transcriber","owner":"fkie-cad","description":"Industrial protocol transcriber - a common representation of industrial communication as input for protocol-independent industrial intrusion detection systems.","archived":false,"fork":false,"pushed_at":"2025-01-27T08:05:18.000Z","size":1047,"stargazers_count":19,"open_issues_count":1,"forks_count":15,"subscribers_count":4,"default_branch":"master","last_synced_at":"2025-04-12T05:52:48.164Z","etag":null,"topics":["cip","ids","iec-60870-5-104","iec-61162-450","industry","ipal","modbus","network","nmea0183","protocols","s7","security"],"latest_commit_sha":null,"homepage":"","language":"Python","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/fkie-cad.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":"CITATION.cff","codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null}},"created_at":"2021-11-02T12:54:02.000Z","updated_at":"2025-01-27T08:05:22.000Z","dependencies_parsed_at":"2024-11-18T07:15:38.294Z","dependency_job_id":"4723d406-b7e0-4fcb-9c26-2500e2822c23","html_url":"https://github.com/fkie-cad/ipal_transcriber","commit_stats":null,"previous_names":[],"tags_count":11,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/fkie-cad%2Fipal_transcriber","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/fkie-cad%2Fipal_transcriber/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/fkie-cad%2Fipal_transcriber/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/fkie-cad%2Fipal_transcriber/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/fkie-cad","download_url":"https://codeload.github.com/fkie-cad/ipal_transcriber/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":252633801,"owners_count":21779924,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["cip","ids","iec-60870-5-104","iec-61162-450","industry","ipal","modbus","network","nmea0183","protocols","s7","security"],"created_at":"2024-11-13T17:22:57.156Z","updated_at":"2025-05-06T06:32:54.229Z","avatar_url":"https://github.com/fkie-cad.png","language":"Python","funding_links":[],"categories":[],"sub_categories":[],"readme":"# IPAL - Transcriber\n\n\u003cimg src=\"./misc/Logo.png\" alt=\"Logo\" width=\"100\" height=\"auto\" align=\"right\"\u003e\n\nThis repository is part of [IPAL](https://github.com/fkie-cad/ipal) - an Industrial Protocol Abstraction Layer. IPAL aims to establish an abstract representation of industrial network traffic for subsequent unified and protocol-independent industrial intrusion detection. IPAL consists of a [transcriber](https://github.com/fkie-cad/ipal_transcriber) to automatically translate industrial traffic into the IPAL representation, an [IDS Framework](https://github.com/fkie-cad/ipal_ids_framework) implementing various industrial intrusion detection systems (IIDSs), and a collection of evaluation [datasets](https://github.com/fkie-cad/ipal_datasets). For details about IPAL, please refer to our publications listed down below.\n\nCyber-physical systems are increasingly threatened by sophisticated attackers, also attacking the physical aspect of systems. Supplementing protective measures, industrial intrusion detection systems promise to detect such attacks. However, due to industrial protocol diversity and lack of standard interfaces, great efforts are required to adapt these technologies to a large number of different protocols. To address this issue, we propose the Industrial Protocol Abstraction Layer (IPAL) - a common representation of industrial communication as input for industrial intrusion detection systems.\n\nThis software (`ipal-transcriber`) implements the automatic translation of industrial network traffic into IPAL for a variety of industrial protocols. As shown in the overview figure, the transcriber reads live network captures or pcap files and converts them into the IPAL representation.\n\n###### Overview Figure\n\n![Overview Figure](./misc/overview_figure/figure.jpg)\n\n###### Implemented Protocols\n\n| Implemented Protocols | Status      | Supported Message Types                                                                                                       |\n|-----------------------|-------------|-------------------------------------------------------------------------------------------------------------------------------|\n| CIP                   | Rudimentary | Code: 76, 77                                                                                                                  |\n| Goose                 | Moderate    |                                                                                                                               |\n| IEC 60870-5-104       | Well        | U\\_Format \u003cbr /\u003e I\\_Format: 1-21, 30-40, 45-51, 58-64, 70, 100-106                                                            |\n| IEC 61162-450         | Moderate    | UdPbC, no tags                                                                                                                |\n| Modbus TCP            | Moderate    | Function Codes: 1, 2, 3, 4, 5, 6, 8, 15, 16, 43                                                                               |\n| MQTT                  | Rudimentary | Basic MQTT 3.1                                                                                                                |\n| NMEA0183              | Well        | DBT, DPT, GGA, GLL, GNS, GSA, GSV, HDM, HDT, RMC, ROT, RPM, TLL, TTM, VBW, VHW, VLW, VTG, ZDA, RMB, APB, RSA, DTM, Q, AIVDM   |\n| S7                    | Rudimentary | Job: 1, 3\u003cbr /\u003eFunction Code: 4, 5                                                                                            |\n| DNP3                  | Rudimentary | Function Code: 0-2, 7,8, 13,14, 20, 24, 129, 130\u003cbr /\u003e objects (group-id:var): 1:2, 2:1, 20:{0,2}, 50:3, 52:2, 60:{1-4}, 80:1 |\n| EtherCAT              | Rudimentary | TODO: add matching data                                                                                                       |\n| MavLink 2.0           | Rudimentary | Depends on the message definitions parsed                                                                                     |\n| Navico BR24           | Moderate    | REP, REG, IMG                                                                                                                 |\n\n###### Publications\n\n- Konrad Wolsing, Eric Wagner, Antoine Saillard, and Martin Henze. 2022. IPAL: Breaking up Silos of Protocol-dependent and Domain-specific Industrial Intrusion Detection Systems. In 25th International Symposium on Research in Attacks, Intrusions and Defenses (RAID 2022), October 26–28, 2022, Limassol, Cyprus. ACM, New York, NY, USA, 17 pages. [https://doi.org/10.1145/3545948.3545968 ](https://doi.org/10.1145/3545948.3545968)\n- Wolsing, Konrad, Eric Wagner, and Martin Henze. \"Poster: Facilitating Protocol-independent Industrial Intrusion Detection Systems.\" *Proceedings of the 2020 ACM SIGSAC Conference on Computer and Communications Security*. 2020 [https://doi.org/10.1145/3372297.3420019](https://doi.org/10.1145/3372297.3420019)\n\n## Getting started\n\nIf you are new to IPAL and want to learn about the general idea or try out our tutorials, please refer to IPAL's main repository: [https://github.com/fkie-cad/ipal](https://github.com/fkie-cad/ipal).\n\n###### Prerequisites\n\n`ipal-trascriber` requires `tshark` to be installed. See [https://tshark.dev/setup/install/](https://tshark.dev/setup/install/) for installation instructions for your operating system.\n\n###### Installation (pip)\n\nUse `python3 -m pip install .` to install the scripts and dependencies system-wide using the `pip` [python package installer](https://pip.pypa.io/en/stable/installation/). This will install dependencies and the `transcriber` modules to the local site packages and add the `ipal-transcriber`, `ipal-state-extractor`, `ipal-minimize`, and `ipal-join` scripts to the `PATH`. The scripts can then be invoked system-wide (e.g. `ipal-transcriber -h`).\n\n###### Installation (venv)\n\nInstall it locally with `misc/install.sh` or manually with:\n\n```bash\npython3 -m venv venv\nsource venv/bin/activate\n\npython3 -m pip install -r requirements.txt\n```\n\nThe scripts can then be invoked after activating the virtual environment from the root of the project repository, e.g.:\n\n```bash\nsource venv/bin/activate\n./ipal-transcriber -h\ndeactivate\n```\n\n###### Installation (docker)\n\nUse `docker build -t ipal-ids-transcriber:latest .` to build a Docker image with a `pip` installation of the project and development dependencies. The scripts can then be used within containers using the built image, e.g.:\n\n```bash\ndocker run -it ipal-ids-transcriber:latest /bin/bash\nipal-transcriber -h\n```\n\n#### Using the Transcriber\n\n`ipal-transcriber` can be used on network interfaces (```--interface```)  or traffic captures (```--pcap```). Set  ```--ipal.output``` either to a file to write the output to, or use '-' to write to *stdout*. If the filename ends with '.gz' it is automatically compressed. \n\nAn optional rule file (```--rules```) allows modifying the output by renaming, removing, or modifying process names and values. For some protocols, e.g. Modbus, request packets need to be cached to properly parse responses. ```--timeout``` defines an upper bound on how long a packet is cached for. Each message can be labeled as malicious or benign, which can be used for the later evaluation of intrusion detection methods. The default value is taken from ```--malicious.default```. Provide a file with packet-specific or time interval labels  with ```--malicious```.\n\n```bash\n./ipal-transcriber -h\nusage: ipal-transcriber [-h] [--interface INTERFACE] [--pcap FILE] [--protocols STR [STR ...]] [--rules FILE] [--timeout INT] [--malicious FILE] [--malicious.default BOOL] [--crc STR]\n                        [--ipal.output FILE] [--log STR] [--logfile FILE] [--compresslevel INT] [--version] [--state.output FILE] [--filter LIST] [--complete-only BOOL]\n                        [--state-in-message BOOL]\n                        {default,timeslice} ...\n\noptions:\n  -h, --help            show this help message and exit\n  --interface INTERFACE\n                        traffic input interface (Use either this or --pcap)\n  --pcap FILE           path to pcap file (Use either this or --interface)\n  --protocols STR [STR ...]\n                        specify a subset of the available transcribers ['cip', 'dnp3', 'goose', 'iec104', 'iec450', 'modbus', 'mqtt', 'nmea0183udp', 's7', 'ethercat']. (Default: all)\n  --rules FILE          file containing rules to transform transcribed messages.\n  --timeout INT         number of milliseconds a packet can be responded to. Used for response matching (Default: 250ms)\n  --malicious FILE      Attack json file for labeling the packets according to the attacks in a dataset.\n  --malicious.default BOOL\n                        set this option to 'true' or 'false' to define default malicious annotation. (Default: None). Can be used in combination with --malicious\n  --crc STR             options for CRC calculations are at 'transport', 'application', combined with 'or', or 'and'. (Default: and)\n  --ipal.output FILE    output location for ipal messages ('-' stdout, '*.gz' compress).\n  --log STR             define logging level as one of DEBUG, INFO, WARNING, ERROR, or CRITICAL. (Default: WARNING)\n  --logfile FILE        define file to log to. (Default: stderr)\n  --compresslevel INT   set the gzip compress level. 0 no compress, 1 fast/large, ..., 9 slow/tiny. (Default: 6)\n  --version             show program's version number and exit\n  --state.output FILE   output location for state information. ('-' stdout, '*.gz' compress)\n  --filter LIST         semicolon separated list of state names to filter for. (Default: no filter)\n  --complete-only BOOL  output complete states after filtering only. (Default: True)\n  --state-in-message BOOL\n                        embed state inside the messages. (Default: False)\n\nState Extractors:\n  {default,timeslice}   These are available state extractor methods. Use -h for further options on each method.\n    default             Simple last value buffer of all variables\n    timeslice           Outputs complete state in regular time slices.\n```\n\n###### Message Format\n\nThe transcriber parses each industrial protocol packet and writes one JSON line for each packet to the output. The 'id' is unique for each message. Source ('src') and destination ('dest') are strings with different address levels separated by \":\". E.g. IP:Port:Device for Modbus which can address sub-devices within one connection in theory. Activity is one of the following:\n\n- Interrogate: Active request for data\n- Inform: Response to requested data or unsolicited message\n- Command: Sets new values or command an action\n- Action: Responds to command or (unsolicited) performed action\n- Confirmation: a packet solely designed as Layer-5 confirmation of prior msg - not restricted to Commands or Interrogations\n\nThe field 'data' contains a dictionary of all transmitted industrial process value names and values. Values are set to 'null', e.g., if this value is requested.\n\n```json\n{\n  \"id\": 0,\n  \"timestamp\": 1445465436.995232,\n  \"protocol\": \"s7\",\n  \"malicious\": null,\n  \"src\": \"10.10.10.20:49156\",\n  \"dest\": \"10.10.10.10:102\",\n  \"length\": 82,\n  \"crc\": true,\n  \"type\": 1,\n  \"activity\": \"interrogate\",\n  \"responds to\": [],\n  \"data\": {\n    \"16\": null,\n    \"17\": null,\n    \"18\": null,\n    \"19\": null,\n    \"20\": null\n  }\n}\n```\n\n###### Rules\n\nRules should be specified in a file passed through `--rules` to the transcriber. Rules allow renaming, removing, or modifying process names and values. More specifically, they allow the addition and removal of select data fields from IPAL messages matching certain patterns, and the renaming of message source and destination fields.\n\nA rules file is a python module containing a variable named `JS` pointing to a dictionary describing the desired post-processing step.\nA rules file may optionally also declare methods containing the post-processing logic. An example rules file can be found under `misc/rules/nmea.py` an extract of which is given below:\n\n```python\ndef position_sign(vars):\n    if vars[1] in [\"N\", \"E\"]:\n        return +vars[0]\n    elif vars[1] in [\"S\", \"W\"]:\n        return -vars[0]\n\nJS = {\n    \"protocols\": [\"nmea0183udp\", \"iec450\"],\n    \"rules\": [\n        {  # Position North-South\n            \"type\": \"RMC\",\n            \"var\": [\"RMC2\", \"RMC3\"],\n            \"method\": position_sign,\n            \"name\": \"latitude\",\n            \"remove\": True,\n        },\n    ],\n    \"rename\": {\n        \".*:GG\": \"GNSS\",\n    },\n}\n```\n\n`JS` may contain three key-value pairs:\n\n- `protocols`: a list of the protocols the packets of which transformation rules and rename operations should be applied to (required)\n- `rules`: a list of dictionaries describing transformation rules (optional). Each dictionary may contain the following keys:\n    - `var`: list containing the data fields to apply the rule to (required)\n    - `type`, `src`, `dst`: regular expression matching the type, source and destination field respectively of messages the rule should apply to. (All optional, default to matching any value when omitted).\n    - `method`: method that should be applied to the fields specified in `var`, requires `name` to also be present (optional)\n    - `name`: name of the new data field which will contain the result of `method`, requires `method` to be present (optional)\n    - `remove`: whether to remove the fields specified in `var` (optional, defaults to false if omitted)  \n- `rename`: a dictionary of key-value pairs describing renaming rules (optional). Each dictionary key-value pair should be of the form:\n    - `key`: regular expression matching the message source or destination\n    - `value`: string that should be used as the new value for matches\n\nIn the example above, the specified rule applies to messages of `type` `RMC`: the data fields `RMC2` and `RMC3` are removed from messages, and a new field `latitude` containing the return value of `position_sign([x,y])` where `x` and `y` are the value of fields `RMC2` and `RMC3` is added to the messages.\n\nThe `rename` key-value pair `\".*:GG\": \"GNSS\"` specifies that all destination and source fields containing the `:GG` should be updated to `GNSS` instead.\n\n\n#### Using the State Extractor\n\n`ipal-state-extractor` transforms the packet-wise message format into the state format used by many process-based IDSs. It can be used as standalone programs or directly within the transcriber tool passing the same arguments. The state is written to the file provided by ```--state.output``` or to stdout. There are different methods to derive the state from packets. Each state extractor has its options which can be retrieved by ```./state_extractor.py [extractor method] -h```.\n\n```bash\n./ipal-state-extractor -h\nusage: ipal-state-extractor [-h] [--ipal.input FILE] [--state.output FILE] [--filter LIST] [--complete-only BOOL] [--state-in-message BOOL] [--compresslevel INT] [--log STR] [--logfile FILE]\n                            [--version]\n                            {default,timeslice} ...\n\noptions:\n  -h, --help            show this help message and exit\n  --ipal.input FILE     input location for message information. ('-' stdin, '*gz' compressed)\n  --state.output FILE   output location for state information. ('-' stdout, '*.gz' compress)\n  --filter LIST         semicolon separated list of state names to filter for. (Default: no filter)\n  --complete-only BOOL  output complete states after filtering only. (Default: True)\n  --state-in-message BOOL\n                        embed state inside the messages. (Default: False)\n  --compresslevel INT   set the gzip compress level. 0 no compress, 1 fast/large, ..., 9 slow/tiny. (Default: 6)\n  --log STR             define logging level as one of DEBUG, INFO, WARNING, ERROR, or CRITICAL. (Default: WARNING)\n  --logfile FILE        define file to log to. (Default: stderr)\n  --version             show program's version number and exit\n\nState Extractors:\n  {default,timeslice}   These are available state extractor methods. Use -h for further options on each method.\n    default             Simple last value buffer of all variables\n    timeslice           Outputs complete state in regular time slices.\n```\n\n###### Implemented State Extractors\n\nCurrently, the following state extraction methods are implemented:\n\n| State Extractor | Description                                                                                      |\n|-----------------|--------------------------------------------------------------------------------------------------|\n| default         | Output one state for each message and keep the value of each variable.                           |\n| timeslice       | Keep the last value of each variable and output a state in regular intervals, e.g. every second. |\n\n###### State Format\n\nThe state format represents the entire state, the values of all sensors and actuators of a physical process, for a given time. 'state' contains all observed variables and values. Each variable's name is a ':' separated list of its device and variable name. A state is labeled as 'malicious' if at least a single packet is malicious since the output of the last state. \n\n```json\n{\n  \"timestamp\": 1445465437.00792,\n  \"state\": {\n    \"10.10.10.10:102:16\": 0,\n    \"10.10.10.10:102:17\": 0,\n    \"10.10.10.10:102:18\": 0,\n    \"10.10.10.10:102:19\": 0,\n    \"10.10.10.10:102:20\": 0\n  },\n  \"malicious\": null\n}\n```\n\n#### Minimizing IPAL messages\n\nThe `ipal-minimize` tool clears the process information (`data` and `state`) from IPAL messages or state files. This may be used to save disk space in case the actual process data is not required.\n\n```bash\nipal-minimize -h\nusage: ipal-minimize [-h] [--jobs INT] [--all] [--log STR] [--logfile FILE] [--version] FILE [FILE ...]\n\npositional arguments:\n  FILE            files to minimize ('*.gz' compressed).\n\noptions:\n  -h, --help      show this help message and exit\n  --jobs INT      Number of parallel workers (Default: 4).\n  --all           Removes all data except those required for evaluation.\n  --log STR       define logging level (DEBUG, INFO, WARNING, ERROR, CRITICAL). Default is WARNING.\n  --logfile FILE  File to log to. Default is stderr.\n  --version       show program's version number and exit\n```\n\n#### Combining IPAL messages\n\nThe `ipal-join` tool can be used to merge different IPAL dataset files from different IIDSs. Currently, the IDS outputs are ORed. For more sophisticated methods, especially to combine different IDS outputs, refer to the `combiner` feature of the IPAL [IDS Framework](https://github.com/fkie-cad/ipal_ids_framework).\n\n```bash\nipal-join -h\nusage: ipal-join [-h] --dataset FILE --output FILE [--force-rename] [--log STR] [--logfile FILE] [--version] FILE [FILE ...]\n\npositional arguments:\n  FILE            files to join ('*.gz' compressed).\n\noptions:\n  -h, --help      show this help message and exit\n  --dataset FILE  original dataset ('*.gz' compressed).\n  --output FILE   path to store joined output to ('*.gz' compressed).\n  --force-rename  Forces renaming dict entries, e.g., scores, metrics (Default: False).\n  --log STR       define logging level (DEBUG, INFO, WARNING, ERROR, CRITICAL). Default is WARNING.\n  --logfile FILE  File to log to. Default is stderr.\n  --version       show program's version number and exit\n```\n\n## Development\n\n##### Tooling\n\nThe set of tools used for development, code formatting, style checking, and testing can be installed with the following command:\n\n```bash\npython3 -m pip install -r requirements-dev.txt\n```\n\nAll tools can be executed manually with the following commands and report errors if encountered:\n\n```bash\nblack .\nflake8\npython3 -m pytest\n```\n\nA `black` and `flake8` check of modified files before any commit can also be forced using Git's pre-commit hook functionality:\n\n```bash\npre-commit install\n```\n\nMore information on the black and flake8 setup can be found at https://ljvmiranda921.github.io/notebook/2018/06/21/precommits-using-black-and-flake8/\n\n##### Adding a Protocol\n\nThe process for adding support for a new protocol is the following:\n\n1. Add a new module in ```transcribers/```\n2. Create a new parser class inheriting the Transcriber class (see ```transcribers/transcriber.py```). The parser class may implement:\n    - `matches_protocol`: given a packet, return `True` if the parser can handle it, `False` otherwise (required)\n    - `parse_packet`: given a packet the parser can handle, return a list of valid IPAL messages (required)\n    - `state_identifier`: given a packet and the name of a data field, return a string identifying the corresponding field (optional, used by the state extractor)\n    - `matches_response`: given a list of request messages and a response message, modify the response message's `responds_to` field, by adding matching request `id`s, may return a list of request messages to remove from the request queue (optional)\n3. Add the new transcriber to the list in ```transcribers/utils.py```\n4. Add the new protocol to the [implemented protocols](#implemented-protocols) table above\n5. Add test cases covering the added protocol\n\n##### Adding a State Extractor\n\nThe process for adding a new state extraction method is the following:\n\n1. Add a new file in ```state_extractors/```\n2. Create a new state extractor class inheriting the StateExtractor class (see ```state_extractors/state_extractor.py```). The state extractor class may implement:\n    - `update_state`: given an IPAL message, update the current process state and if desired call ``_write_state`` to write the state to output (required)\n    - `finalize`: called by the main state extractor script after all messages have been processed, implementing `finalize` allows to execute logic on completion, e.g. outputting one final state (required)\n    - `add_arguments_to_parser`: add arguments to the main state extractor script, the `args` namespace is passed to the class on initialization, allowing reading in additional user configuration and flags (optional)\n3. Add the new state extractor to the list in ```state_extractors/utils.py```\n4. Add the new state extractor to the [implemented state extractors](#implemented-state-extractors) table above\n\n##### Adding Tests\n\nThe process for adding tests depends on the type of tests to add.\n\n###### Adding Output Validation\n\nWhen adding support for a new protocol, add a check against validation output for the raw transcriber output, the state extractor output, and the combined transcriber and state extractor output. These checks should be added in the modules `tests/test_transcriber.py`, `tests/test_state_extractor.py` and `tests/test_combined.py` respectively, by adding an entry to the `RAW_FILES` list.\n\nAs an example, adding the three-tuple `(\"misc/pcaps/s7.pcap\", \"s7.ipal\", \"s7\")` to `RAW_FILES` in `tests/test_transcriber.py` signifies that a check of the output of the transcriber executed on the packet capture file `misc/pcaps/s7.pcap`, with the protocol flag set to `s7` will be conducted against the reference file in `tests/snapshots/validation/test_transcriber_raw_s7.ipal`.\n\nNote that upon adding a new test, a validation `IPAL` file will be created under `tests/snapshots/validation/` after the first test run. Simply edit such newly created validation files to remove the `== new file ==` marker on the first line and the files will be used as a validation reference for future tests.\n\n###### Adding Transcriber Tests\n\nTo ensure that a transcriber's protocol implementation stays compliant, a transcriber test module can be added in `tests/transcribers/`. It may contain methods testing individual features and properties of the added transcriber. Note that for it to be collected by pytest, the module and test methods must be prefixed by `test_` and test methods contain an `assert`. See [the pytest docs](https://docs.pytest.org/en/6.2.x/contents.html#toc) for more information.\n\n## Contributors\n\n- Antoine Saillard (RWTH Aachen University \u0026 Fraunhofer FKIE)\n- David Schachtschneider (RWTH Aachen University)\n- David Valero Ribes (RWTH Aachen University)\n- Eric Wagner (Fraunhofer FKIE \u0026 RWTH Aachen University)\n- Julia Kunz (RWTH Aachen University)\n- Konrad Wolsing (Fraunhofer FKIE \u0026 RWTH Aachen University)\n- Leonardo Pompe (RWTH Aachen University)\n- Olav Lamberts (RWTH Aachen University)\n- Samir Benzammour (RWTH Aachen University)\n- Simeon Ricking (RWTH Aachen University)\n- Stefan Lenz (RWTH Aachen University)\n- Sven Zemanek (Fraunhofer FKIE)\n- Tim Nebel (RWTH Aachen University)\n\n## License\n\nMIT License. See LICENSE for details.\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Ffkie-cad%2Fipal_transcriber","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Ffkie-cad%2Fipal_transcriber","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Ffkie-cad%2Fipal_transcriber/lists"}