ROLLOUT 3 · M0r13n/pyais
M0r13n__pyais-137
Solved
507 stotal time
10tool calls
10model calls
7,621tokens generated
69,915prompt tokens read
Agent exceeded session timeout (7.5 min)
Turn by turn
Token shading: p ≥ 0.9 0.5 – 0.9 0.2 – 0.5 below 0.2. Hover a token for its id and probability.
System instructions 4,822 characters
You are a principal Python software engineer resolving a repository issue in `/workspace`. Your sole objective is to produce a minimal, root-cause source patch that passes every hidden fail-to-pass and pass-to-pass regression test on the first grading run. ## Critical Environment & Grader Rules 1. **Air-Gapped Sandbox & Available CLI Tools**: No network access. Never run `pip`, `uv`, `conda`, or `git fetch`. Note: `rg` (`ripgrep`) is NOT installed in the container — always use `grep -rn --include="*.py"` for fast exact searches so you never waste a tool call on `command not found`. 2. **Protected Grading Paths**: Before running official verification, the grading harness **strips and reverts all changes** to `tests/`, `test/`, `testing/`, `test_*.py`, `*_test.py`, `conftest.py`, `pyproject.toml`, `setup.cfg`, `pytest.ini`, and `tox.ini`. Every part of your fix MUST reside in non-test production source files. 3. **Untracked File Trap (`git add -N .`)**: `submit_patch` stages ALL untracked files inside `/workspace`! Never leave reproduction or scratch files inside `/workspace` — run reproductions inline via `python3 - <<'PY'` or inside `/tmp/repro.py` in a single `run_command`. 4. **Stdout Truncation Rule**: `run_command` truncates stdout after the first 5,000 characters from the TOP. Always run pytest quietly with short tracebacks: `pytest path/to/test_file.py -k pattern -q --tb=short --no-header` If a test file is large, redirect output to `/tmp/pytest.log` and inspect `tail -n 80 /tmp/pytest.log` so failure tracebacks at the bottom are never cut off. 5. **Strict Pacing Discipline**: `get_status` and `submit_patch` are free and do NOT count toward `max_tool_calls`. Target **16-22 counted tool calls total**, and **begin your first `edit_file` change no later than tool call 15**. Reserve at least 4 calls for verification, `git status --short && git diff`, and submission. ## Execution Protocol ### Phase 1 — Pinpoint Code & Tests Immediately (1-2 tool calls) - The user message already contains `Workspace Layout` — do NOT waste a turn running `ls` on top-level directories. - Run a single compound `run_command` with `grep -rn --include="*.py"` to locate: (a) the exact symbols, error strings, or functions from the problem description, (b) **all parallel or sibling implementations** (for example sync + async variants, `Client` + `AsyncClient`, serializer + deserializer, multiple backends/dialects, base + subclass methods, `__init__.py` / `__all__` exports), and (c) the closest existing unit test files under `tests/`. ### Phase 2 — Deep Read & Spec Anchor (2-5 tool calls) - Use `read_file` with generous `start_line` and `end_line` windows (180-300 lines per call so you read whole modules in fewer calls) on the target function/class, callers, type annotations, and neighboring tests. - Before editing, verify every explicit and implicit requirement from the issue: exact parameter names, defaults, boundary/domain edge cases, exception types/messages, `None` vs empty collection vs falsy (`0`, `False`, `""`) semantics, mutability/copying, and return types. ### Phase 3 — Reproduce Bug in One Call (1 tool call) - Run ONE concise command (`python3 - <<'PY' ... PY` or targeted `pytest ... -q --tb=short --no-header`) via `run_command` to observe the exact pre-fix failure. Do NOT spend multiple tool calls writing separate reproduction scripts. ### Phase 4 — Surgical Implementation Across All Parallel Paths (1-4 tool calls) - Begin editing by tool call 15! Use `edit_file` with 4-6 lines of exact surrounding context copied from `read_file`. - Update ALL parallel codepaths (sync/async, multiple classes/dialects, type hints) consistently so partial fixes do not fail sibling tests. - **2-Strike Edit Rule**: If `edit_file` fails twice on the same block due to whitespace or escaping, do not retry `edit_file` a third time — apply an atomic Python transformation via `run_command` and immediately verify `python3 -m py_compile path/to/file.py`. ### Phase 5 — Edge-Case Verification, Diff Hygiene & Clean Exit (2-4 tool calls) - Run a single compound `run_command` testing both your reproduction + boundary edge cases (`None`, empty input, leading/trailing delimiters, subdomain/prefix boundaries, subclass/async parity) AND the relevant existing `pytest` module(s) with `-q --tb=short --no-header`. - Run `git status --short && git diff` via `run_command`. Confirm: - ZERO untracked (`??`) files in `/workspace` (`rm -f` any untracked workspace files immediately), - ZERO modifications to `tests/` or protected config files (`git checkout -- tests/` if touched), - Minimal, clean diff with valid imports and indentation. - Call `submit_patch` once verification passes, then respond with a brief 2-sentence plain-text summary and make NO further tool calls.
Task prompt 3,690 characters
You are evaluating a software engineering task for repository M0r13n/pyais.
Problem Statement:
Determination of the communication status field
## Problem
It seems that the current method of detecting and distinguishing communication status fields is not entirely reliable. For example, the following example incorrectly indicates that it is a SOTDMA communication status, although it is an ITDMA.
```python
decoded = pyais.decode('!AIVDM,1,1,,A,333ga6PP0jPPDUNM3I7DTOwDR59C,0*51')
com = decoded.get_communication_state()
print(f"SOTDMA: {decoded.is_sotdma}, ITDMA: {decoded.is_itdma}")
```
According to the following code it is checked whether the 20th bit of the communications status is set or not. However, as the com state is only 19 bits long, a bit from the previous data fields is checked here and not one from the com state. In the case of a type 1, 2 or 3 message, it would be checked whether the RAIM flag is set. Since 1 is the default value, pyais returns in almost all cases that it is a SOTDMA com state.
```python
@property
def is_sotdma(self) -> bool:
"""The radio status field has it's 20th bit (MSB) set to 0 or has less than 20 bits"""
return self.radio <= self.MAX_COMM_STATE_VALUE
@property
def is_itdma(self) -> bool:
"""The radio status field has it's 20th bit (MSB) set to 1"""
return self.radio > self.MAX_COMM_STATE_VALUE
```
## Solution
It is better to use the message type rather than the bit length to indicate the type of communication status. Table 46 of the M.1371-5 standard shows which type uses which com state. Type 9, 18 and 26 messages are edge cases, as they can include one of the two com state types. The existing implementation can be used here, as with these message types the bit before the actual com state determines its type.
## Task Budget (Session terminates when any budget is exhausted)
- Time allowance: 7.5 minutes
- Tool calls allowance: 30 calls
- Max loop iterations: 40 turns
## Execution Environment Rules
- Single command timeout: 90 seconds (commands exceeding this fail without ending the session)
- Command output limit: 5000 characters
- File view limit: 150 lines per read_file call
- File character limit: 10000 characters per read_file call
- Environment is offline (no network/PyPI access). All repository and test dependencies are ALREADY pre-installed. Do NOT attempt to run pip install or download packages.
## Instructions:
0. All source code is under `/workspace`. Do NOT search outside `/workspace` (e.g. `/usr/`, `/wheels/`, system site-packages). If imports fail, the issue is in the source code under `/workspace`, not in missing system packages.
1. Analyze the problem statement and any provided hints carefully to identify all requested script paths, CLI subcommands, or Python modules.
2. Inspect existing codebase conventions and test files before making edits.
3. Verify your implementation using targeted tests or inline assertions before submitting.
4. Call submit_patch only after your implementation is complete and verified.
5. As your final action, you must return a text-only response reporting your completion to terminate the session.
## Workspace Layout
The repository is located at `/workspace`. Here is the directory tree (up to 3 levels):
```
./CHANGELOG.txt
./CODE_OF_CONDUCT.md
./Dockerfile
./LICENSE
./Makefile
./README.md
./docs
./examples
./mypy.ini
./pyais
./pyais/__init__.py
./pyais/ais_types.py
./pyais/constants.py
./pyais/decode.py
./pyais/encode.py
./pyais/exceptions.py
./pyais/filter.py
./pyais/main.py
./pyais/messages.py
./pyais/py.typed
./pyais/stream.py
./pyais/tracker.py
./pyais/util.py
./pyproject.toml
./requirements.txt
./tests
```
Turn 1at 69 s231 tokens out3,064 prompt tokens
The user wants to fix a bug in `pyais` regarding the determination of communication status (SOTDMA vs ITDMA). The problem states that `is_sotdma` and `is_itdma` properties currently rely on checking the 20th bit of the `radio` (communication state) field, which is incorrect because the field might be shorter than 20 bits, causing it to check a bit from a previous field. The suggested solution is to use the message type to determine the communication status, except for types 9, 18, and 26, where the current bit-checking method should be kept (or adapted). Let's start by exploring the codebase to find where `is_sotdma`, `is_itdma`, and `radio` are defined.
TOOL CALL · run_command
{
"command": "grep -rn --include=\"*.py\" -e \"is_sotdma\" -e \"is_itdma\" -e \"MAX_COMM_STATE_VALUE\""
}Result of run_command 1,445 characters
{
"status": "ok",
"stdout": "pyais/messages.py:802: MAX_COMM_STATE_VALUE = 0x7ffff\npyais/messages.py:819: if self.is_sotdma:\npyais/messages.py:827: def is_sotdma(self) -> bool:\npyais/messages.py:829: return self.radio <= self.MAX_COMM_STATE_VALUE\npyais/messages.py:832: def is_itdma(self) -> bool:\npyais/messages.py:834: return self.radio > self.MAX_COMM_STATE_VALUE\npyais/messages.py:840: return self.radio & self.MAX_COMM_STATE_VALUE\nexamples/communication_state.py:19:print(\"Communication state is SOTMDA:\", decoded.is_sotdma)\nexamples/communication_state.py:20:print(\"Communication state is ITDMA:\", decoded.is_itdma)\ntests/test_decode.py:1393: assert decoded.is_sotdma\ntests/test_decode.py:1394: assert not decoded.is_itdma\ntests/test_decode.py:1417: assert decoded.is_sotdma\ntests/test_decode.py:1418: assert not decoded.is_itdma\ntests/test_decode.py:1441: assert decoded.is_sotdma\ntests/test_decode.py:1442: assert not decoded.is_itdma\ntests/test_decode.py:1465: assert decoded.is_itdma\ntests/test_decode.py:1466: assert not decoded.is_sotdma\ntests/test_decode.py:1487: assert not decoded.is_itdma\ntests/test_decode.py:1488: assert decoded.is_sotdma\ntests/test_decode.py:1513: assert not decoded.is_itdma\ntests/test_decode.py:1514: assert decoded.is_sotdma\n",
"stderr": "",
"exit_code": 0
}Generated tokens 231 tokens · stopped: STOP
<|channel>thought
The user wants to fix a bug in `pyais` regarding the determination of communication status (SOTDMA vs ITDMA).
The problem states that `is_sotdma` and `is_itdma` properties currently rely on checking the 20th bit of the `radio` (communication state) field, which is incorrect because the field might be shorter than 20 bits, causing it to check a bit from a previous field.
The suggested solution is to use the message type to determine the communication status, except for types 9, 18, and 26, where the current bit-checking method should be kept (or adapted).
Let's start by exploring the codebase to find where `is_sotdma`, `is_itdma`, and `radio` are defined.<channel|><|tool_call>call:run_command{command:<|"|>grep -rn --include="*.py" -e "is_sotdma" -e "is_itdma" -e "MAX_COMM_STATE_VALUE"<|"|>}<tool_call|><|tool_response>
Turn 2at 79 s100 tokens out3,875 prompt tokens
The properties `is_sotdma` and `is_itdma` are defined in `pyais/messages.py`. The `MAX_COMM_STATE_VALUE` is also defined there. Let's examine `pyais/messages.py` around those lines.
TOOL CALL · read_file
{
"end_line": 860,
"filepath": "pyais/messages.py",
"start_line": 790
}Result of read_file 3,034 characters
{
"status": "ok",
"filepath": "pyais/messages.py",
"content": "\n\nclass CommunicationStateMixin:\n \"\"\"\n Mixin class to access Communication State values by applicable messages.\n\n You may refer to 3.3.7.2.1 of:\n https://www.itu.int/dms_pubrec/itu-r/rec/m/R-REC-M.1371-1-200108-S!!PDF-E.pdf\n \"\"\"\n\n radio: int # Type hint to make mypy happy\n\n MAX_COMM_STATE_VALUE = 0x7ffff\n\n def get_communication_state(self) -> Dict[str, typing.Optional[int]]:\n \"\"\"Returns information used by the slot allocation algorithm as a dict.\"\"\"\n result: Dict[str, typing.Optional[int]] = {\n 'received_stations': None,\n 'slot_number': None,\n 'utc_hour': None,\n 'utc_minute': None,\n 'slot_offset': None,\n 'slot_timeout': None,\n 'sync_state': None,\n 'keep_flag': None,\n 'slot_increment': None,\n 'num_slots': None,\n }\n\n if self.is_sotdma:\n result.update(get_sotdma_comm_state(self.communication_state_raw))\n else:\n result.update(get_itdma_comm_state(self.communication_state_raw))\n\n return result\n\n @property\n def is_sotdma(self) -> bool:\n \"\"\"The radio status field has it's 20th bit (MSB) set to 0 or has less than 20 bits\"\"\"\n return self.radio <= self.MAX_COMM_STATE_VALUE\n\n @property\n def is_itdma(self) -> bool:\n \"\"\"The radio status field has it's 20th bit (MSB) set to 1\"\"\"\n return self.radio > self.MAX_COMM_STATE_VALUE\n\n @property\n def communication_state_raw(self) -> int:\n \"\"\"Get the raw radio status except 20th bit - if present\"\"\"\n try:\n return self.radio & self.MAX_COMM_STATE_VALUE\n except AttributeError as err:\n raise ValueError(\n 'Communication State is only available for messages with radio field'\n ) from err\n\n\n@attr.s(slots=True)\nclass MessageType1(Payload, CommunicationStateMixin):\n \"\"\"\n AIS Vessel position report using SOTDMA (Self-Organizing Time Division Multiple Access)\n Src: https://gpsd.gitlab.io/gpsd/AIVDM.html#_types_1_2_and_3_position_report_class_a\n \"\"\"\n msg_type = bit_field(6, int, default=1, signed=False)\n repeat = bit_field(2, int, default=0, signed=False)\n mmsi = bit_field(30, int, from_converter=from_mmsi)\n status = bit_field(4, int, default=0, converter=NavigationStatus.from_value, signed=False)\n turn = bit_field(8, float, default=TurnRate.NO_TI_DEFAULT, signed=True, to_converter=to_turn, from_converter=from_turn)\n speed = bit_field(10, float, from_converter=from_speed, to_converter=to_speed, default=0, signed=False)\n accuracy = bit_field(1, bool, default=0, signed=False)\n lon = bit_field(28, float, from_converter=from_lat_lon, to_converter=to_lat_lon, default=0, signed=True)",
"start_line": 790,
"end_line": 860,
"total_lines": 1736,
"is_truncated": false
}Generated tokens 100 tokens · stopped: STOP
The properties `is_sotdma` and `is_itdma` are defined in `pyais/messages.py`.
The `MAX_COMM_STATE_VALUE` is also defined there.
Let's examine `pyais/messages.py` around those lines.
<channel|><|tool_call>call:read_file{filepath:<|"|>pyais/messages.py<|"|>,start_line:790,end_line:860}<tool_call|><|tool_response>
Turn 3at 104 s480 tokens out4,987 prompt tokens
The `CommunicationStateMixin` is used by several message classes.
The problem description states: "It is better to use the message type rather than the bit length to indicate the type of communication status. Table 46 of the M.1371-5 standard shows which type uses which com state. Type 9, 18 and 26 messages are edge cases, as they can include one of the two com state types."
I need to know which message types use SOTDMA and which use ITDMA.
Based on the problem statement:
- Type 1, 2, 3 are likely SOTDMA (though implied as an example of where the current bug manifests: "In the case of a type 1, 2 or 3 message... pyais returns in almost all cases that it is a SOTDMA com state"). Wait, "pyais returns in almost all cases that it is a SOTDMA com state" because the 20th bit is 0, but the field might only be 19 bits.
Let's check the problem statement again.
"For example, the following example incorrectly indicates that it is a SOTDMA communication status, although it is an ITDMA."
```python
decoded = pyais.decode('!AIVDM,1,1,,A,333ga6PP0jPPDUNM3I7DTOwDR59C,0*51')
```
Let's reproduce this first.
TOOL CALL · run_command
{
"command": "python3 - <<'PY'\nimport pyais\ndecoded = pyais.decode('!AIVDM,1,1,,A,333ga6PP0jPPDUNM3I7DTOwDR59C,0*51')\ncom = decoded.get_communication_state()\nprint(f\"Message Type: {getattr(decoded, 'msg_type', 'Unknown')}\")\nprint(f\"SOTDMA: {decoded.is_sotdma}, ITDMA: {decoded.is_itdma}\")\nprint(f\"Radio value: {getattr(decoded, 'radio', 'N/A')}\")\nPY\n"
}Result of run_command 125 characters
{
"status": "ok",
"stdout": "Message Type: 3\nSOTDMA: True, ITDMA: False\nRadio value: 21075\n",
"stderr": "",
"exit_code": 0
}Generated tokens 480 tokens · stopped: STOP
The `CommunicationStateMixin` is used by several message classes.
The problem description states: "It is better to use the message type rather than the bit length to indicate the type of communication status. Table 46 of the M.1371-5 standard shows which type uses which com state. Type 9, 18 and 26 messages are edge cases, as they can include one of the two com state types."
I need to know which message types use SOTDMA and which use ITDMA.
Based on the problem statement:
- Type 1, 2, 3 are likely SOTDMA (though implied as an example of where the current bug manifests: "In the case of a type 1, 2 or 3 message... pyais returns in almost all cases that it is a SOTDMA com state"). Wait, "pyais returns in almost all cases that it is a SOTDMA com state" because the 20th bit is 0, but the field might only be 19 bits.
Let's check the problem statement again.
"For example, the following example incorrectly indicates that it is a SOTDMA communication status, although it is an ITDMA."
```python
decoded = pyais.decode('!AIVDM,1,1,,A,333ga6PP0jPPDUNM3I7DTOwDR59C,0*51')
```
Let's reproduce this first.
<channel|><|tool_call>call:run_command{command:<|"|>python3 - <<'PY'
import pyais
decoded = pyais.decode('!AIVDM,1,1,,A,333ga6PP0jPPDUNM3I7DTOwDR59C,0*51')
com = decoded.get_communication_state()
print(f"Message Type: {getattr(decoded, 'msg_type', 'Unknown')}")
print(f"SOTDMA: {decoded.is_sotdma}, ITDMA: {decoded.is_itdma}")
print(f"Radio value: {getattr(decoded, 'radio', 'N/A')}")
PY
<|"|>}<tool_call|><|tool_response>
Turn 4at 128 s353 tokens out5,535 prompt tokens
In the example, message type is 3. The radio value is 21075 (0x526B). `MAX_COMM_STATE_VALUE` is `0x7ffff` (524287). `21075 <= 524287` is True, so `is_sotdma` is True. But the problem says it should be ITDMA. Wait, if it's ITDMA, the 20th bit should be set. But the problem says: "as the com state is only 19 bits long, a bit from the previous data fields is checked here and not one from the com state." If the message type is 3, is it ALWAYS SOTDMA or ALWAYS ITDMA? The problem statement says: "It is better to use the message type rather than the bit length to indicate the type of communication status. Table 46 of the M.1371-5 standard shows which type uses which com state. Type 9, 18 and 26 messages are edge cases, as they can include one of the two com state types." I don't have Table 46 of the M.1371-5 standard, but I can try to infer which types are SOTDMA and which are ITDMA. Looking at the current code, `CommunicationStateMixin` is mixed into many classes. Let's see which classes use `CommunicationStateMixin`.
TOOL CALL · run_command
{
"command": "grep -rn \"CommunicationStateMixin\" pyais/messages.py"
}Result of run_command 658 characters
{
"status": "ok",
"stdout": "792:class CommunicationStateMixin:\n848:class MessageType1(Payload, CommunicationStateMixin):\n889:class MessageType4(Payload, CommunicationStateMixin):\n996:class MessageType9(Payload, CommunicationStateMixin):\n1141:class MessageType18(Payload, CommunicationStateMixin):\n1545:class MessageType26AddressedStructured(Payload, CommunicationStateMixin):\n1560:class MessageType26BroadcastStructured(Payload, CommunicationStateMixin):\n1574:class MessageType26AddressedUnstructured(Payload, CommunicationStateMixin):\n1589:class MessageType26BroadcastUnstructured(Payload, CommunicationStateMixin):\n",
"stderr": "",
"exit_code": 0
}Generated tokens 353 tokens · stopped: STOP
In the example, message type is 3.
The radio value is 21075 (0x526B).
`MAX_COMM_STATE_VALUE` is `0x7ffff` (524287).
`21075 <= 524287` is True, so `is_sotdma` is True.
But the problem says it should be ITDMA.
Wait, if it's ITDMA, the 20th bit should be set.
But the problem says: "as the com state is only 19 bits long, a bit from the previous data fields is checked here and not one from the com state."
If the message type is 3, is it ALWAYS SOTDMA or ALWAYS ITDMA?
The problem statement says: "It is better to use the message type rather than the bit length to indicate the type of communication status. Table 46 of the M.1371-5 standard shows which type uses which com state. Type 9, 18 and 26 messages are edge cases, as they can include one of the two com state types."
I don't have Table 46 of the M.1371-5 standard, but I can try to infer which types are SOTDMA and which are ITDMA.
Looking at the current code, `CommunicationStateMixin` is mixed into many classes.
Let's see which classes use `CommunicationStateMixin`.
<channel|><|tool_call>call:run_command{command:<|"|>grep -rn "CommunicationStateMixin" pyais/messages.py<|"|>}<tool_call|><|tool_response>
Turn 5at 143 s176 tokens out6,086 prompt tokens
The types using `CommunicationStateMixin` are: 1, 4, 9, 18, 26. The problem statement mentions types 9, 18, and 26 as edge cases. That means types 1 and 4 (and likely 2 and 3, but I need to check if they are also using the mixin) should definitely be determined by type. Wait, the reproduction says Message Type 3. But `grep` only showed 1, 4, 9, 18, 26. Let's check MessageType 2 and 3 in `pyais/messages.py`.
TOOL CALL · run_command
{
"command": "grep -E \"class MessageType[1-4]\" pyais/messages.py"
}Result of run_command 1,493 characters
{
"status": "ok",
"stdout": "class MessageType1(Payload, CommunicationStateMixin):\nclass MessageType2(MessageType1):\nclass MessageType3(MessageType1):\nclass MessageType4(Payload, CommunicationStateMixin):\nclass MessageType10(Payload):\nclass MessageType11(MessageType4):\nclass MessageType12(Payload):\nclass MessageType13(MessageType7):\nclass MessageType14(Payload):\nclass MessageType15(Payload):\nclass MessageType16(Payload):\nclass MessageType17(Payload):\nclass MessageType18(Payload, CommunicationStateMixin):\nclass MessageType19(Payload):\nclass MessageType20(Payload):\nclass MessageType21(Payload):\nclass MessageType22Addressed(Payload):\nclass MessageType22Broadcast(Payload):\nclass MessageType22(Payload):\nclass MessageType23(Payload):\nclass MessageType24PartA(Payload):\nclass MessageType24PartB(Payload):\nclass MessageType24(Payload):\nclass MessageType25AddressedStructured(Payload):\nclass MessageType25BroadcastStructured(Payload):\nclass MessageType25AddressedUnstructured(Payload):\nclass MessageType25BroadcastUnstructured(Payload):\nclass MessageType25(Payload):\nclass MessageType26AddressedStructured(Payload, CommunicationStateMixin):\nclass MessageType26BroadcastStructured(Payload, CommunicationStateMixin):\nclass MessageType26AddressedUnstructured(Payload, CommunicationStateMixin):\nclass MessageType26BroadcastUnstructured(Payload, CommunicationStateMixin):\nclass MessageType26(Payload):\nclass MessageType27(Payload):\n",
"stderr": "",
"exit_code": 0
}Generated tokens 176 tokens · stopped: STOP
The types using `CommunicationStateMixin` are: 1, 4, 9, 18, 26.
The problem statement mentions types 9, 18, and 26 as edge cases.
That means types 1 and 4 (and likely 2 and 3, but I need to check if they are also using the mixin) should definitely be determined by type.
Wait, the reproduction says Message Type 3. But `grep` only showed 1, 4, 9, 18, 26.
Let's check MessageType 2 and 3 in `pyais/messages.py`.
<channel|><|tool_call>call:run_command{command:<|"|>grep -E "class MessageType[1-4]" pyais/messages.py<|"|>}<tool_call|><|tool_response>
Turn 6at 192 s762 tokens out6,665 prompt tokens
`MessageType2` and `MessageType3` inherit from `MessageType1`, which inherits from `CommunicationStateMixin`. So we have: SOTDMA likely: 1, 2, 3, 4 (Wait, the reproduction said type 3 was ITDMA but `is_sotdma` was True. And the problem said "incorrectly indicates that it is a SOTDMA communication status, although it is an ITDMA"). Wait. "For example, the following example incorrectly indicates that it is a SOTDMA communication status, although it is an ITDMA." And the example is `MessageType 3`. So MessageType 3 should be ITDMA? No, let me read carefully: "In the case of a type 1, 2 or 3 message, it would be checked whether the RAIM flag is set. Since 1 is the default value, pyais returns in almost all cases that it is a SOTDMA com state." This means for type 1, 2, 3, the `radio` field is not long enough, and the bit being checked for `is_sotdma` is actually the RAIM flag. Wait, is RAIM flag 1 or 0? "Since 1 is the default value, pyais returns in almost all cases that it is a SOTDMA com state." If RAIM is 1, and `radio` is 21075 (0x526B), then `radio <= 0x7ffff` is True, so `is_sotdma` is True. If it's ITDMA, `is_itdma` should be True. `is_itdma` is `radio > 0x7ffff`. If the problem says it IS an ITDMA for that specific message, but it's reported as SOTDMA, it means the logic is wrong. Wait, if type 1, 2, 3 messages are ALWAYS something, then we can just check the type. But what are they? "Table 46 of the M.1371-5 standard shows which type uses which com state." I don't have the table. But the problem says: "Type 9, 18 and 26 messages are edge cases, as they can include one of the two com state types. The existing implementation can be used here, as with these message types the bit before the actual com state determines its type." So for types 1, 2, 3, 4,... (non 9, 18, 26), the communication state type is fixed? The problem says "In the case of a type 1, 2 or 3 message, it would be checked whether the RAIM flag is set." This implies that for types 1, 2, 3, the `radio` field doesn't even contain the SOTDMA/ITDMA bit at that position. Let's look at the `MessageType1` definition. `MessageType1` inherits `CommunicationStateMixin`. Where is `radio` defined for `MessageType1`? I saw `msg_type`, `repeat`, `mmsi`, `status`, `turn`, `speed`, `accuracy`, `lon`... I need to see the rest of `MessageType1`.
TOOL CALL · read_file
{
"end_line": 920,
"filepath": "pyais/messages.py",
"start_line": 848
}Result of read_file 3,986 characters
{
"status": "ok",
"filepath": "pyais/messages.py",
"content": "class MessageType1(Payload, CommunicationStateMixin):\n \"\"\"\n AIS Vessel position report using SOTDMA (Self-Organizing Time Division Multiple Access)\n Src: https://gpsd.gitlab.io/gpsd/AIVDM.html#_types_1_2_and_3_position_report_class_a\n \"\"\"\n msg_type = bit_field(6, int, default=1, signed=False)\n repeat = bit_field(2, int, default=0, signed=False)\n mmsi = bit_field(30, int, from_converter=from_mmsi)\n status = bit_field(4, int, default=0, converter=NavigationStatus.from_value, signed=False)\n turn = bit_field(8, float, default=TurnRate.NO_TI_DEFAULT, signed=True, to_converter=to_turn, from_converter=from_turn)\n speed = bit_field(10, float, from_converter=from_speed, to_converter=to_speed, default=0, signed=False)\n accuracy = bit_field(1, bool, default=0, signed=False)\n lon = bit_field(28, float, from_converter=from_lat_lon, to_converter=to_lat_lon, default=0, signed=True)\n lat = bit_field(27, float, from_converter=from_lat_lon, to_converter=to_lat_lon, default=0, signed=True)\n course = bit_field(12, float, from_converter=from_10th, to_converter=to_10th, default=0, signed=False)\n heading = bit_field(9, int, default=0, signed=False)\n second = bit_field(6, int, default=0, signed=False)\n maneuver = bit_field(2, int, default=0, from_converter=ManeuverIndicator.from_value,\n to_converter=ManeuverIndicator.from_value, signed=False)\n spare_1 = bit_field(3, bytes, default=b'')\n raim = bit_field(1, bool, default=0)\n radio = bit_field(19, int, default=0, signed=False)\n\n\nclass MessageType2(MessageType1):\n \"\"\"\n AIS Vessel position report using SOTDMA (Self-Organizing Time Division Multiple Access)\n Src: https://gpsd.gitlab.io/gpsd/AIVDM.html#_types_1_2_and_3_position_report_class_a\n \"\"\"\n msg_type = bit_field(6, int, default=2)\n\n\nclass MessageType3(MessageType1):\n \"\"\"\n AIS Vessel position report using ITDMA (Incremental Time Division Multiple Access)\n Src: https://gpsd.gitlab.io/gpsd/AIVDM.html#_types_1_2_and_3_position_report_class_a\n \"\"\"\n msg_type = bit_field(6, int, default=3)\n\n\n@attr.s(slots=True)\nclass MessageType4(Payload, CommunicationStateMixin):\n \"\"\"\n AIS Vessel position report using SOTDMA (Self-Organizing Time Division Multiple Access)\n Src: https://gpsd.gitlab.io/gpsd/AIVDM.html#_type_4_base_station_report\n \"\"\"\n msg_type = bit_field(6, int, default=4, signed=False)\n repeat = bit_field(2, int, default=0, signed=False)\n mmsi = bit_field(30, int, from_converter=from_mmsi)\n year = bit_field(14, int, default=1970, signed=False)\n month = bit_field(4, int, default=1, signed=False)\n day = bit_field(5, int, default=1, signed=False)\n hour = bit_field(5, int, default=0, signed=False)\n minute = bit_field(6, int, default=0, signed=False)\n second = bit_field(6, int, default=0, signed=False)\n accuracy = bit_field(1, bool, default=0, signed=False)\n lon = bit_field(28, float, from_converter=from_lat_lon, to_converter=to_lat_lon, signed=True, default=0)\n lat = bit_field(27, float, from_converter=from_lat_lon, to_converter=to_lat_lon, signed=True, default=0)\n epfd = bit_field(4, int, default=0, from_converter=EpfdType.from_value, to_converter=EpfdType.from_value,\n signed=False)\n spare_1 = bit_field(10, bytes, default=b'')\n raim = bit_field(1, bool, default=0)\n radio = bit_field(19, int, default=0, signed=False)\n\n\n@attr.s(slots=True)\nclass MessageType5(Payload):\n \"\"\"\n Static and Voyage Related Data\n Src: https://gpsd.gitlab.io/gpsd/AIVDM.html#_type_5_static_and_voyage_related_data\n \"\"\"\n msg_type = bit_field(6, int, default=5, signed=False)\n repeat = bit_field(2, int, default=0, signed=False)",
"start_line": 848,
"end_line": 920,
"total_lines": 1736,
"is_truncated": false
}Generated tokens 762 tokens · stopped: STOP
`MessageType2` and `MessageType3` inherit from `MessageType1`, which inherits from `CommunicationStateMixin`.
So we have:
SOTDMA likely: 1, 2, 3, 4 (Wait, the reproduction said type 3 was ITDMA but `is_sotdma` was True. And the problem said "incorrectly indicates that it is a SOTDMA communication status, although it is an ITDMA").
Wait. "For example, the following example incorrectly indicates that it is a SOTDMA communication status, although it is an ITDMA."
And the example is `MessageType 3`.
So MessageType 3 should be ITDMA?
No, let me read carefully:
"In the case of a type 1, 2 or 3 message, it would be checked whether the RAIM flag is set. Since 1 is the default value, pyais returns in almost all cases that it is a SOTDMA com state."
This means for type 1, 2, 3, the `radio` field is not long enough, and the bit being checked for `is_sotdma` is actually the RAIM flag.
Wait, is RAIM flag 1 or 0? "Since 1 is the default value, pyais returns in almost all cases that it is a SOTDMA com state."
If RAIM is 1, and `radio` is 21075 (0x526B), then `radio <= 0x7ffff` is True, so `is_sotdma` is True.
If it's ITDMA, `is_itdma` should be True. `is_itdma` is `radio > 0x7ffff`.
If the problem says it IS an ITDMA for that specific message, but it's reported as SOTDMA, it means the logic is wrong.
Wait, if type 1, 2, 3 messages are ALWAYS something, then we can just check the type.
But what are they?
"Table 46 of the M.1371-5 standard shows which type uses which com state."
I don't have the table. But the problem says:
"Type 9, 18 and 26 messages are edge cases, as they can include one of the two com state types. The existing implementation can be used here, as with these message types the bit before the actual com state determines its type."
So for types 1, 2, 3, 4,... (non 9, 18, 26), the communication state type is fixed?
The problem says "In the case of a type 1, 2 or 3 message, it would be checked whether the RAIM flag is set."
This implies that for types 1, 2, 3, the `radio` field doesn't even contain the SOTDMA/ITDMA bit at that position.
Let's look at the `MessageType1` definition.
`MessageType1` inherits `CommunicationStateMixin`.
Where is `radio` defined for `MessageType1`?
I saw `msg_type`, `repeat`, `mmsi`, `status`, `turn`, `speed`, `accuracy`, `lon`...
I need to see the rest of `MessageType1`.
<channel|><|tool_call>call:read_file{end_line:920,filepath:<|"|>pyais/messages.py<|"|>,start_line:848}<tool_call|><|tool_response>
Turn 7at 232 s680 tokens out8,855 prompt tokens
Aha! Look at the docstrings:
`MessageType1`: "AIS Vessel position report using SOTDMA"
`MessageType2`: "AIS Vessel position report using SOTDMA"
`MessageType3`: "AIS Vessel position report using ITDMA"
`MessageType4`: "AIS Vessel position report using SOTDMA"
Wait, so:
Type 1: SOTDMA
Type 2: SOTDMA
Type 3: ITDMA
Type 4: SOTDMA
In `MessageType1` (and 2, 3), `radio` is defined as `bit_field(19, int, default=0, signed=False)`.
And in `CommunicationStateMixin`:
```python
MAX_COMM_STATE_VALUE = 0x7ffff # 2^19 - 1
# ...
@property
def is_sotdma(self) -> bool:
return self.radio <= self.MAX_COMM_STATE_VALUE
@property
def is_itdma(self) -> bool:
return self.radio > self.MAX_COMM_STATE_VALUE
```
Since `radio` is only 19 bits, it will ALWAYS be `<= 0x7ffff`. So `is_sotdma` will always be True for types 1, 2, 3, 4.
But for type 3, it should be ITDMA.
The problem says: "It is better to use the message type rather than the bit length to indicate the type of communication status. Table 46 of the M.1371-5 standard shows which type uses which com state. Type 9, 18 and 26 messages are edge cases, as they can include one of the two com state types."
So for types 1, 2, 3, 4, the type alone tells us if it's SOTDMA or ITDMA.
Type 1: SOTDMA
Type 2: SOTDMA
Type 3: ITDMA
Type 4: SOTDMA
Wait, IF it can be different, why does it say "use the message type rather than the bit length"?
Maybe it means:
If type is 1 -> SOTDMA
If type is 2 -> SOTDMA
If type is 3 -> ITDMA
If type is 4 -> SOTDMA
If type is 9, 18, 26 -> use the current bit-length (actually, use the 20th bit if available).
Wait, if the `radio` field is only 19 bits, how can the 20th bit be used for types 9, 18, 26?
Let's check types 9, 18, 26.
TOOL CALL · run_command
{
"command": "grep -n \"class MessageType9\" pyais/messages.py\ngrep -n \"class MessageType18\" pyais/messages.py\ngrep -n \"class MessageType26\" pyais/messages.py"
}Result of run_command 540 characters
{
"status": "ok",
"stdout": "996:class MessageType9(Payload, CommunicationStateMixin):\n1141:class MessageType18(Payload, CommunicationStateMixin):\n1545:class MessageType26AddressedStructured(Payload, CommunicationStateMixin):\n1560:class MessageType26BroadcastStructured(Payload, CommunicationStateMixin):\n1574:class MessageType26AddressedUnstructured(Payload, CommunicationStateMixin):\n1589:class MessageType26BroadcastUnstructured(Payload, CommunicationStateMixin):\n1601:class MessageType26(Payload):\n",
"stderr": "",
"exit_code": 0
}Generated tokens 680 tokens · stopped: STOP
Aha! Look at the docstrings:
`MessageType1`: "AIS Vessel position report using SOTDMA"
`MessageType2`: "AIS Vessel position report using SOTDMA"
`MessageType3`: "AIS Vessel position report using ITDMA"
`MessageType4`: "AIS Vessel position report using SOTDMA"
Wait, so:
Type 1: SOTDMA
Type 2: SOTDMA
Type 3: ITDMA
Type 4: SOTDMA
In `MessageType1` (and 2, 3), `radio` is defined as `bit_field(19, int, default=0, signed=False)`.
And in `CommunicationStateMixin`:
```python
MAX_COMM_STATE_VALUE = 0x7ffff # 2^19 - 1
# ...
@property
def is_sotdma(self) -> bool:
return self.radio <= self.MAX_COMM_STATE_VALUE
@property
def is_itdma(self) -> bool:
return self.radio > self.MAX_COMM_STATE_VALUE
```
Since `radio` is only 19 bits, it will ALWAYS be `<= 0x7ffff`. So `is_sotdma` will always be True for types 1, 2, 3, 4.
But for type 3, it should be ITDMA.
The problem says: "It is better to use the message type rather than the bit length to indicate the type of communication status. Table 46 of the M.1371-5 standard shows which type uses which com state. Type 9, 18 and 26 messages are edge cases, as they can include one of the two com state types."
So for types 1, 2, 3, 4, the type alone tells us if it's SOTDMA or ITDMA.
Type 1: SOTDMA
Type 2: SOTDMA
Type 3: ITDMA
Type 4: SOTDMA
Wait, IF it can be different, why does it say "use the message type rather than the bit length"?
Maybe it means:
If type is 1 -> SOTDMA
If type is 2 -> SOTDMA
If type is 3 -> ITDMA
If type is 4 -> SOTDMA
If type is 9, 18, 26 -> use the current bit-length (actually, use the 20th bit if available).
Wait, if the `radio` field is only 19 bits, how can the 20th bit be used for types 9, 18, 26?
Let's check types 9, 18, 26.
<channel|><|tool_call>call:run_command{command:<|"|>grep -n "class MessageType9" pyais/messages.py
grep -n "class MessageType18" pyais/messages.py
grep -n "class MessageType26" pyais/messages.py<|"|>}<tool_call|><|tool_response>
Turn 8at 236 s42 tokens out9,705 prompt tokens
Let's read the `radio` field definition for these types.
TOOL CALL · run_command
{
"command": "grep -n \"radio =\" pyais/messages.py"
}Result of run_command 540 characters
{
"status": "ok",
"stdout": "869: radio = bit_field(19, int, default=0, signed=False)\n910: radio = bit_field(19, int, default=0, signed=False)\n1019: radio = bit_field(20, int, default=0, signed=False)\n1166: radio = bit_field(20, int, default=0)\n1556: radio = bit_field(20, int, default=0, signed=False)\n1570: radio = bit_field(20, int, default=0, signed=False)\n1585: radio = bit_field(20, int, default=0, signed=False)\n1598: radio = bit_field(20, int, default=0, signed=False)\n",
"stderr": "",
"exit_code": 0
}Generated tokens 42 tokens · stopped: STOP
Let's read the `radio` field definition for these types.
<channel|><|tool_call>call:run_command{command:<|"|>grep -n "radio =" pyais/messages.py<|"|>}<tool_call|><|tool_response>
Turn 9at 295 s929 tokens out9,987 prompt tokens
Okay!
Type 1 (line 869): 19 bits.
Type 4 (line 910): 19 bits.
Type 9 (line 1019): 20 bits.
Type 18 (line 1166): 20 bits.
Type 26 (lines 1556, 1570, 1585, 1598): 20 bits.
So for types 9, 18, 26, `radio` is 20 bits.
For types 1 (and 2, 3) and 4, `radio` is 19 bits.
So the current logic:
```python
@property
def is_sotdma(self) -> bool:
return self.radio <= self.MAX_COMM_STATE_VALUE # 0x7ffff (19 bits set to 1)
@property
def is_itdma(self) -> bool:
return self.radio > self.MAX_COMM_STATE_VALUE
```
For types 1, 2, 3, 4, `radio` is always `<= 0x7ffff`, so `is_sotdma` is always True.
But for type 3, it SHOULD be ITDMA.
So for types 1, 2, 3, 4, we should use the type to decide.
Type 1: SOTDMA
Type 2: SOTDMA
Type 3: ITDMA
Type 4: SOTDMA
Wait, are there other types using `CommunicationStateMixin`?
I saw 1, 4, 9, 18, 26.
And 2, 3 inherit from 1.
So:
If type is 3 -> ITDMA
If type is 1, 2, 4 -> SOTDMA
If type is 9, 18, 26 -> use the current `radio` value (since they are 20 bits).
Wait, what if there are other types?
The problem says: "Table 46 of the M.1371-5 standard shows which type uses which com state."
I should probably check if I can find more info or if the ones I found are all.
The mixin is only applied to 1, 4, 9, 18, 26 (and their subclasses).
Let's double check the `MessageType3` docstring:
`class MessageType3(MessageType1): ... AIS Vessel position report using ITDMA`
And `MessageType1` / `MessageType2` / `MessageType4`: "using SOTDMA".
So the logic should be:
For types 1, 2, 3, 4:
if type == 3: it's ITDMA
else: it's SOTDMA
For types 9, 18, 26:
use `radio > MAX_COMM_STATE_VALUE` for ITDMA
Let's implement this in `CommunicationStateMixin`.
I need access to the `msg_type` of the object.
The classes using the mixin all have `msg_type = bit_field(...)`.
Wait, is `msg_type` available on `self`?
Yes, it's defined in the classes.
Let's modify `is_sotdma` and `is_itdma`.
Wait, what about other types that might be added later?
The problem says "Table 46 ... shows which type uses which com state."
Maybe I should use a mapping.
SOTDMA types: 1, 2, 4
ITDMA types: 3
Edge cases: 9, 18, 26
Wait, are there any other types?
Looking at `pyais/messages.py` again.
Only these had `CommunicationStateMixin`.
Let's check if any other types have `radio` field but not `CommunicationStateMixin`.
TOOL CALL · run_command
{
"command": "grep -n \"radio =\" pyais/messages.py"
}Result of run_command 540 characters
{
"status": "ok",
"stdout": "869: radio = bit_field(19, int, default=0, signed=False)\n910: radio = bit_field(19, int, default=0, signed=False)\n1019: radio = bit_field(20, int, default=0, signed=False)\n1166: radio = bit_field(20, int, default=0)\n1556: radio = bit_field(20, int, default=0, signed=False)\n1570: radio = bit_field(20, int, default=0, signed=False)\n1585: radio = bit_field(20, int, default=0, signed=False)\n1598: radio = bit_field(20, int, default=0, signed=False)\n",
"stderr": "",
"exit_code": 0
}Generated tokens 929 tokens · stopped: STOP
Okay!
Type 1 (line 869): 19 bits.
Type 4 (line 910): 19 bits.
Type 9 (line 1019): 20 bits.
Type 18 (line 1166): 20 bits.
Type 26 (lines 1556, 1570, 1585, 1598): 20 bits.
So for types 9, 18, 26, `radio` is 20 bits.
For types 1 (and 2, 3) and 4, `radio` is 19 bits.
So the current logic:
```python
@property
def is_sotdma(self) -> bool:
return self.radio <= self.MAX_COMM_STATE_VALUE # 0x7ffff (19 bits set to 1)
@property
def is_itdma(self) -> bool:
return self.radio > self.MAX_COMM_STATE_VALUE
```
For types 1, 2, 3, 4, `radio` is always `<= 0x7ffff`, so `is_sotdma` is always True.
But for type 3, it SHOULD be ITDMA.
So for types 1, 2, 3, 4, we should use the type to decide.
Type 1: SOTDMA
Type 2: SOTDMA
Type 3: ITDMA
Type 4: SOTDMA
Wait, are there other types using `CommunicationStateMixin`?
I saw 1, 4, 9, 18, 26.
And 2, 3 inherit from 1.
So:
If type is 3 -> ITDMA
If type is 1, 2, 4 -> SOTDMA
If type is 9, 18, 26 -> use the current `radio` value (since they are 20 bits).
Wait, what if there are other types?
The problem says: "Table 46 of the M.1371-5 standard shows which type uses which com state."
I should probably check if I can find more info or if the ones I found are all.
The mixin is only applied to 1, 4, 9, 18, 26 (and their subclasses).
Let's double check the `MessageType3` docstring:
`class MessageType3(MessageType1): ... AIS Vessel position report using ITDMA`
And `MessageType1` / `MessageType2` / `MessageType4`: "using SOTDMA".
So the logic should be:
For types 1, 2, 3, 4:
if type == 3: it's ITDMA
else: it's SOTDMA
For types 9, 18, 26:
use `radio > MAX_COMM_STATE_VALUE` for ITDMA
Let's implement this in `CommunicationStateMixin`.
I need access to the `msg_type` of the object.
The classes using the mixin all have `msg_type = bit_field(...)`.
Wait, is `msg_type` available on `self`?
Yes, it's defined in the classes.
Let's modify `is_sotdma` and `is_itdma`.
Wait, what about other types that might be added later?
The problem says "Table 46 ... shows which type uses which com state."
Maybe I should use a mapping.
SOTDMA types: 1, 2, 4
ITDMA types: 3
Edge cases: 9, 18, 26
Wait, are there any other types?
Looking at `pyais/messages.py` again.
Only these had `CommunicationStateMixin`.
Let's check if any other types have `radio` field but not `CommunicationStateMixin`.
<channel|><|tool_call>call:run_command{command:<|"|>grep -n "radio =" pyais/messages.py<|"|>}<tool_call|><|tool_response>
Turn 10at 496 s3,868 tokens out11,156 prompt tokens
It seems all `radio` fields are covered by the mixin.
Wait, let's verify what types 1, 2, 3, 4 are exactly.
Type 1: SOTDMA
Type 2: SOTDMA
Type 3: ITDMA
Type 4: SOTDMA
Wait, the problem says: "In the case of a type 1, 2 or 3 message, it would be checked whether the RAIM flag is set. Since 1 is the default value, pyais returns in almost cases that it is a SOTDMA com state."
This means for type 1, 2, 3, the current code is checking the RAIM flag instead of the communication state.
But the problem says "SOTDMA: {decoded.is_sotdma}, ITDMA: {decoded.is_itdma}" for a type 3 message should indicate ITDMA.
Is it possible that type 1 and 2 are ALSO always SOTDMA?
Yes, the docstrings say so.
So:
- Type 1: SOTDMA
- Type 2: SOTDMA
- Type 3: ITDMA
- Type 4: SOTDMA
- Type 9: can be either (use 20th bit)
- Type 18: can be either (use 20th bit)
- Type 26: can be either (use 20th bit)
Wait, if type 1, 2, 4 are always SOTDMA and type 3 is always ITDMA, we can simply use:
```python
@property
def is_sotdma(self) -> bool:
if self.msg_type in (1, 2, 4):
return True
if self.msg_type == 3:
return False
return self.radio <= self.MAX_COMM_STATE_VALUE
@property
def is_itdma(self) -> bool:
if self.msg_type in (1, 2, 4):
return False
if self.msg_type == 3:
return True
return self.radio > self.MAX_COMM_STATE_VALUE
```
But wait, what about other types?
The problem says "Type 9, 18 and 26 messages are edge cases... The existing implementation can be used here".
The existing implementation uses `self.radio <= self.MAX_COMM_STATE_VALUE`.
For these types, `radio` is 20 bits.
If 20th bit is 0, `radio <= 0x7ffff` is True.
If 20th bit is 1, `radio > 0x7ffff` is True.
So the existing implementation WORKS for types 9, 18, 26.
What if `self.msg_type` is not 1, 2, 3, 4, 9, 18, or 26?
Well, only types that have `CommunicationStateMixin` will use these properties.
And we saw that only 1, 4, 9, 18, 26 (and their subclasses) use it.
MessageType 2 and 3 are subclasses of MessageType 1.
So the proposed logic seems correct and covers all cases.
Let's check if `msg_type` is available on all these classes.
`MessageType1` has `msg_type`.
`MessageType2` and `MessageType3` have `msg_type`.
`MessageType4` has `msg_type`.
`MessageType9` has `msg_type`.
`MessageType18` has `msg_type`.
`MessageType26...` has `msg_type`.
Yes.
One more thing: the problem mentions: "Table 46 of the M.1371-5 standard shows which type uses which com state."
I should make sure I'm not missing any other types.
If I only handle 1, 2, 3, 4 and let others fall through to the bit-check, it should be safe.
Let's refine the implementation.
Instead of `if self.msg_type in (1, 2, 4):`, maybe I should just check if it's 3 for ITDMA, and if it's 1, 2, 4 for SOTDMA.
Wait, what if a new type is added that is always SOTDMA or ITDMA?
The current logic for types 9, 18, 26 is: "the bit before the actual com state determines its type."
Actually, the 20th bit of the `radio` field is that bit.
Wait, "the bit before the actual com state determines its type."
For types 9, 18, 26, `radio` is 20 bits.
Bit 20 is the MSB.
If bit 20 is 0, it's SOTDMA.
If bit 20 is 1, it's ITDMA.
This is exactly what `radio <= 0x7ffff` and `radio > 0x7ffff` checks.
Wait, "the bit before the actual com state determines its type."
If `radio` is 20 bits, the first bit (MSB) is bit 20.
Then the remaining 19 bits are the "actual com state".
This matches the existing code.
So, the proposed fix:
```python
@property
def is_sotdma(self) -> bool:
"""The radio status field has it's 20th bit (MSB) set to 0 or has less than 20 bits"""
if self.msg_type == 3:
return False
if self.msg_type in (1, 2, 4):
return True
return self.radio <= self.MAX_COMM_STATE_VALUE
@property
def is_itdma(self) -> bool:
"""The radio status field has it's 20th bit (MSB) set to 1"""
if self.msg_type == 3:
return True
if self.msg_type in (1, 2, 4):
return False
return self.radio > self.MAX_COMM_STATE_VALUE
```
Wait, if `msg_type` is 1, 2, 4, then `is_sotdma` is True and `is_itdma` is False.
If `msg_type` is 3, then `is_sotdma` is False and `is_itdma` is True.
If `msg_type` is 9, 18, 26, it falls through to the bit check.
This looks correct.
Wait, let me double check Type 4 again.
`class MessageType4(Payload, CommunicationStateMixin): ... AIS Vessel position report using SOTDMA`
Yes, it's SOTDMA.
What about other types that might be missed?
I should check if any other types have `CommunicationStateMixin`.
I did `grep` already and it only found 1, 4, 9, 18, 26.
Causa 2 and 3 are subclasses of 1.
Let's verify the reproduction again with the fix (mentally).
Reproduction: Message Type 3.
Current: `is_sotdma` is True because `21075 <= 0x7ffff`.
Fixed: `is_sotdma` will check `self.msg_type == 3` and return False. `is_itdma` will return True. Correct.
Wait, one detail. The problem said "Table 46 of the M.1371-5 standard shows which type uses which com state."
Maybe I should use a mapping or something more explicit.
But since the number of types is small, this is probably fine.
Actually, to be more robust, I could do:
```python
@property
def is_sotdma(self) -> bool:
if self.msg_type == 3:
return False
if self.msg_type in (1, 2, 4):
return True
return self.radio <= self.MAX_COMM_STATE_VALUE
@property
def is_itdma(self) -> bool:
return not self.is_sotdma
```
Wait, is it always one or the other?
"The radio status field has it's 20th bit (MSB) set to 0 or has less than 20 bits" -> SOTDMA
"The radio status field has it's 20th bit (MSB) set to 1" -> ITDMA
Yes, it's a binary choice based on the 20th bit.
Wait, if I use `return not self.is_sotdma`, I have to be careful about what happens if `radio` is missing.
But `CommunicationStateMixin` is only used in classes that have `radio`.
Wait, the `communication_state_raw` property has a try-except for `AttributeError` on `self.radio`.
```python
@property
def communication_state_raw(self) -> bool:
try:
return self.radio & self.MAX_COMM_STATE_VALUE
except AttributeError as err:
raise ValueError(
'Communication State is only available for messages with radio field'
) from err
```
This means `self.radio` might be missing.
If `self.radio` is missing, `is_sotdma` would raise `AttributeError` (if we didn't handle it).
Let's see if `is_sotdma` should also raise `ValueError`.
Currently:
```python
@property
def is_sotdma(self) -> bool:
"""The radio status field has it's 20th bit (MSB) set to 0 or has less than 20 bits"""
return self.radio <= self.MAX_COMM_STATE_VALUE
```
If `self.radio` is missing, this raises `AttributeError`.
If I change it to:
```python
@property
def is_sotdma(self) -> bool:
if self.msg_type == 3:
return False
if self.msg_type in (1, 2, 4):
return True
try:
return self.radio <= self.MAX_COMM_STATE_VALUE
except AttributeError as err:
raise ValueError(
'Communication State is only available for messages with radio field'
) from err
```
This would be more consistent with `communication_state_raw`.
Wait, the problem statement says: "The existing implementation can be used here, as with these message types the bit before the actual com state determines its type."
The existing implementation for `is_sotdma` is just `return self.radio <= self.MAX_COMM_STATE_VALUE`.
Let's see if I can just use the message type and if the type is not one of the fixed ones, use the existing logic.
```python
@property
def is_sotdma(self) -> bool:
"""The radio status field has it's 20th bit (MSB) set to 0 or has less than 20 bits"""
if self.msg_type == 3:
return False
if self.msg_type in (1, 2, 4):
return True
return self.radio <= self.MAX_COMM_STATE_VALUE
@property
def is_itdma(self) -> bool:
"""The radio status field has it's 20th bit (MSS) set to 1"""
if self.msg_type == 3:
return True
if self.msg_type in (1, 2, 4):
return False
return self.radio > self.MAX_COMM_STATE_VALUE
```
Wait, what if `self.msg_type` is not present? (Unlikely given the architecture).
What if `self.radio` is not present for types other than 1, 2, 3, 4?
For types 9, 18, 26, `radio` IS present.
So the only case where `self.radio` might be missing is if someone uses `CommunicationStateMixin` in a class without `radio`.
But that's already the case now, and it raises `AttributeError`.
Let's check if I should improve the error handling.
The `communication_state_raw` property already handles it.
`get_communication_state` uses `self.is_sotdma` and `self.communication_state_raw`.
If `is_sotdma` raises `AttributeError`, `get_communication_state` will bubble it up.
I think the simplest fix is just to add the type checks.
One detail: the problem says "Type 9, 18 and 26 messages are edge cases... The existing implementation can be used here".
My proposed logic:
For Type 9, 18, 26: it goes to `return self.radio <= self.MAX_COMM_STATE_VALUE`.
This IS the existing implementation.
One more thing: the reproduction:
`!AIVDM,1,1,,A,333ga6PP0jPPDUNM3I7DTOwDR59C,0*51`
Type 3.
`is_sotdma` should be False.
`is_itdma` should be True.
Let's verify what happens for Type 3 with my proposed fix:
`is_sotdma`: `self.msg_type == 3` is True, return False.
`is_itdma`: `self.msg_type == 3` is True, return True.
Correct.
What happens for Type 1 with my proposed fix:
`is_sotdma`: `self.msg_type == 3` is False. `self.msg_type in (1, 2, 4)` is True. return True.
`is_itdma`: `self.msg_type == 3` is False. `self.msg_type in (1, 2, 4)` is True. return False.
Correct.
What happens for Type 9 with my proposed fix:
`is_sotdma`: `self.msg_type == 3` is False. `self.msg_type in (1, 2, 4)` is False.
Returns `self.radio <= self.MAX_COMM_STATE_VALUE`.
Correct.
Wait, I should use the exact same logic as requested: "use the message type rather than the bit length to indicate the type of communication status."
Let's see if there are any others.
Table 46 of M.1371-5. I don't have it, but the problem statement mentions "Type 1, 2 or 3".
And it mentioned 9, 18 and 26 as edge cases.
It didn't explicitly mention 4, but the docstrings in the code say Type 4 is SOTDMA.
Let's implement the fix.
TOOL CALL · edit_file
{
"filepath": "pyais/messages.py",
"new_string": " @property\n def is_sotdma(self) -> bool:\n \"\"\"The radio status field has it's 20th bit (MSB) set to 0 or has less than 20 bits\"\"\"\n if self.msg_type == 3:\n return False\n if self.msg_type in (1, 2, 4):\n return True\n return self.radio <= self.MAX_COMM_STATE_VALUE\n\n @property\n def is_itdma(self) -> bool:\n \"\"\"The radio status field has it's 20th bit (MSB) set to 1\"\"\"\n if self.msg_type == 3:\n return True\n if self.msg_type in (1, 2, 4):\n return False\n return self.radio > self.MAX_COMM_STATE_VALUE",
"old_string": " @property\n def is_sotdma(self) -> bool:\n \"\"\"The radio status field has it's 20th bit (MSB) set to 0 or has less than 20 bits\"\"\"\n return self.radio <= self.MAX_COMM_STATE_VALUE\n\n @property\n def is_itdma(self) -> bool:\n \"\"\"The radio status field has it's 20th bit (MSB) set to 1\"\"\"\n return self.radio > self.MAX_COMM_STATE_VALUE"
}Result of edit_file 867 characters
{
"status": "ok",
"filepath": "pyais/messages.py",
"occurrences": 1,
"strategy": "exact",
"diff": "--- a/pyais/messages.py\n+++ b/pyais/messages.py\n@@ -826,11 +826,19 @@\n @property\n def is_sotdma(self) -> bool:\n \"\"\"The radio status field has it's 20th bit (MSB) set to 0 or has less than 20 bits\"\"\"\n+ if self.msg_type == 3:\n+ return False\n+ if self.msg_type in (1, 2, 4):\n+ return True\n return self.radio <= self.MAX_COMM_STATE_VALUE\n \n @property\n def is_itdma(self) -> bool:\n \"\"\"The radio status field has it's 20th bit (MSB) set to 1\"\"\"\n+ if self.msg_type == 3:\n+ return True\n+ if self.msg_type in (1, 2, 4):\n+ return False\n return self.radio > self.MAX_COMM_STATE_VALUE\n \n @property\n",
"is_truncated": false
}Generated tokens 3,868 tokens · stopped: STOP
It seems all `radio` fields are covered by the mixin.
Wait, let's verify what types 1, 2, 3, 4 are exactly.
Type 1: SOTDMA
Type 2: SOTDMA
Type 3: ITDMA
Type 4: SOTDMA
Wait, the problem says: "In the case of a type 1, 2 or 3 message, it would be checked whether the RAIM flag is set. Since 1 is the default value, pyais returns in almost cases that it is a SOTDMA com state."
This means for type 1, 2, 3, the current code is checking the RAIM flag instead of the communication state.
But the problem says "SOTDMA: {decoded.is_sotdma}, ITDMA: {decoded.is_itdma}" for a type 3 message should indicate ITDMA.
Is it possible that type 1 and 2 are ALSO always SOTDMA?
Yes, the docstrings say so.
So:
- Type 1: SOTDMA
- Type 2: SOTDMA
- Type 3: ITDMA
- Type 4: SOTDMA
- Type 9: can be either (use 20th bit)
- Type 18: can be either (use 20th bit)
- Type 26: can be either (use 20th bit)
Wait, if type 1, 2, 4 are always SOTDMA and type 3 is always ITDMA, we can simply use:
```python
@property
def is_sotdma(self) -> bool:
if self.msg_type in (1, 2, 4):
return True
if self.msg_type == 3:
return False
return self.radio <= self.MAX_COMM_STATE_VALUE
@property
def is_itdma(self) -> bool:
if self.msg_type in (1, 2, 4):
return False
if self.msg_type == 3:
return True
return self.radio > self.MAX_COMM_STATE_VALUE
```
But wait, what about other types?
The problem says "Type 9, 18 and 26 messages are edge cases... The existing implementation can be used here".
The existing implementation uses `self.radio <= self.MAX_COMM_STATE_VALUE`.
For these types, `radio` is 20 bits.
If 20th bit is 0, `radio <= 0x7ffff` is True.
If 20th bit is 1, `radio > 0x7ffff` is True.
So the existing implementation WORKS for types 9, 18, 26.
What if `self.msg_type` is not 1, 2, 3, 4, 9, 18, or 26?
Well, only types that have `CommunicationStateMixin` will use these properties.
And we saw that only 1, 4, 9, 18, 26 (and their subclasses) use it.
MessageType 2 and 3 are subclasses of MessageType 1.
So the proposed logic seems correct and covers all cases.
Let's check if `msg_type` is available on all these classes.
`MessageType1` has `msg_type`.
`MessageType2` and `MessageType3` have `msg_type`.
`MessageType4` has `msg_type`.
`MessageType9` has `msg_type`.
`MessageType18` has `msg_type`.
`MessageType26...` has `msg_type`.
Yes.
One more thing: the problem mentions: "Table 46 of the M.1371-5 standard shows which type uses which com state."
I should make sure I'm not missing any other types.
If I only handle 1, 2, 3, 4 and let others fall through to the bit-check, it should be safe.
Let's refine the implementation.
Instead of `if self.msg_type in (1, 2, 4):`, maybe I should just check if it's 3 for ITDMA, and if it's 1, 2, 4 for SOTDMA.
Wait, what if a new type is added that is always SOTDMA or ITDMA?
The current logic for types 9, 18, 26 is: "the bit before the actual com state determines its type."
Actually, the 20th bit of the `radio` field is that bit.
Wait, "the bit before the actual com state determines its type."
For types 9, 18, 26, `radio` is 20 bits.
Bit 20 is the MSB.
If bit 20 is 0, it's SOTDMA.
If bit 20 is 1, it's ITDMA.
This is exactly what `radio <= 0x7ffff` and `radio > 0x7ffff` checks.
Wait, "the bit before the actual com state determines its type."
If `radio` is 20 bits, the first bit (MSB) is bit 20.
Then the remaining 19 bits are the "actual com state".
This matches the existing code.
So, the proposed fix:
```python
@property
def is_sotdma(self) -> bool:
"""The radio status field has it's 20th bit (MSB) set to 0 or has less than 20 bits"""
if self.msg_type == 3:
return False
if self.msg_type in (1, 2, 4):
return True
return self.radio <= self.MAX_COMM_STATE_VALUE
@property
def is_itdma(self) -> bool:
"""The radio status field has it's 20th bit (MSB) set to 1"""
if self.msg_type == 3:
return True
if self.msg_type in (1, 2, 4):
return False
return self.radio > self.MAX_COMM_STATE_VALUE
```
Wait, if `msg_type` is 1, 2, 4, then `is_sotdma` is True and `is_itdma` is False.
If `msg_type` is 3, then `is_sotdma` is False and `is_itdma` is True.
If `msg_type` is 9, 18, 26, it falls through to the bit check.
This looks correct.
Wait, let me double check Type 4 again.
`class MessageType4(Payload, CommunicationStateMixin): ... AIS Vessel position report using SOTDMA`
Yes, it's SOTDMA.
What about other types that might be missed?
I should check if any other types have `CommunicationStateMixin`.
I did `grep` already and it only found 1, 4, 9, 18, 26.
Causa 2 and 3 are subclasses of 1.
Let's verify the reproduction again with the fix (mentally).
Reproduction: Message Type 3.
Current: `is_sotdma` is True because `21075 <= 0x7ffff`.
Fixed: `is_sotdma` will check `self.msg_type == 3` and return False. `is_itdma` will return True. Correct.
Wait, one detail. The problem said "Table 46 of the M.1371-5 standard shows which type uses which com state."
Maybe I should use a mapping or something more explicit.
But since the number of types is small, this is probably fine.
Actually, to be more robust, I could do:
```python
@property
def is_sotdma(self) -> bool:
if self.msg_type == 3:
return False
if self.msg_type in (1, 2, 4):
return True
return self.radio <= self.MAX_COMM_STATE_VALUE
@property
def is_itdma(self) -> bool:
return not self.is_sotdma
```
Wait, is it always one or the other?
"The radio status field has it's 20th bit (MSB) set to 0 or has less than 20 bits" -> SOTDMA
"The radio status field has it's 20th bit (MSB) set to 1" -> ITDMA
Yes, it's a binary choice based on the 20th bit.
Wait, if I use `return not self.is_sotdma`, I have to be careful about what happens if `radio` is missing.
But `CommunicationStateMixin` is only used in classes that have `radio`.
Wait, the `communication_state_raw` property has a try-except for `AttributeError` on `self.radio`.
```python
@property
def communication_state_raw(self) -> bool:
try:
return self.radio & self.MAX_COMM_STATE_VALUE
except AttributeError as err:
raise ValueError(
'Communication State is only available for messages with radio field'
) from err
```
This means `self.radio` might be missing.
If `self.radio` is missing, `is_sotdma` would raise `AttributeError` (if we didn't handle it).
Let's see if `is_sotdma` should also raise `ValueError`.
Currently:
```python
@property
def is_sotdma(self) -> bool:
"""The radio status field has it's 20th bit (MSB) set to 0 or has less than 20 bits"""
return self.radio <= self.MAX_COMM_STATE_VALUE
```
If `self.radio` is missing, this raises `AttributeError`.
If I change it to:
```python
@property
def is_sotdma(self) -> bool:
if self.msg_type == 3:
return False
if self.msg_type in (1, 2, 4):
return True
try:
return self.radio <= self.MAX_COMM_STATE_VALUE
except AttributeError as err:
raise ValueError(
'Communication State is only available for messages with radio field'
) from err
```
This would be more consistent with `communication_state_raw`.
Wait, the problem statement says: "The existing implementation can be used here, as with these message types the bit before the actual com state determines its type."
The existing implementation for `is_sotdma` is just `return self.radio <= self.MAX_COMM_STATE_VALUE`.
Let's see if I can just use the message type and if the type is not one of the fixed ones, use the existing logic.
```python
@property
def is_sotdma(self) -> bool:
"""The radio status field has it's 20th bit (MSB) set to 0 or has less than 20 bits"""
if self.msg_type == 3:
return False
if self.msg_type in (1, 2, 4):
return True
return self.radio <= self.MAX_COMM_STATE_VALUE
@property
def is_itdma(self) -> bool:
"""The radio status field has it's 20th bit (MSS) set to 1"""
if self.msg_type == 3:
return True
if self.msg_type in (1, 2, 4):
return False
return self.radio > self.MAX_COMM_STATE_VALUE
```
Wait, what if `self.msg_type` is not present? (Unlikely given the architecture).
What if `self.radio` is not present for types other than 1, 2, 3, 4?
For types 9, 18, 26, `radio` IS present.
So the only case where `self.radio` might be missing is if someone uses `CommunicationStateMixin` in a class without `radio`.
But that's already the case now, and it raises `AttributeError`.
Let's check if I should improve the error handling.
The `communication_state_raw` property already handles it.
`get_communication_state` uses `self.is_sotdma` and `self.communication_state_raw`.
If `is_sotdma` raises `AttributeError`, `get_communication_state` will bubble it up.
I think the simplest fix is just to add the type checks.
One detail: the problem says "Type 9, 18 and 26 messages are edge cases... The existing implementation can be used here".
My proposed logic:
For Type 9, 18, 26: it goes to `return self.radio <= self.MAX_COMM_STATE_VALUE`.
This IS the existing implementation.
One more thing: the reproduction:
`!AIVDM,1,1,,A,333ga6PP0jPPDUNM3I7DTOwDR59C,0*51`
Type 3.
`is_sotdma` should be False.
`is_itdma` should be True.
Let's verify what happens for Type 3 with my proposed fix:
`is_sotdma`: `self.msg_type == 3` is True, return False.
`is_itdma`: `self.msg_type == 3` is True, return True.
Correct.
What happens for Type 1 with my proposed fix:
`is_sotdma`: `self.msg_type == 3` is False. `self.msg_type in (1, 2, 4)` is True. return True.
`is_itdma`: `self.msg_type == 3` is False. `self.msg_type in (1, 2, 4)` is True. return False.
Correct.
What happens for Type 9 with my proposed fix:
`is_sotdma`: `self.msg_type == 3` is False. `self.msg_type in (1, 2, 4)` is False.
Returns `self.radio <= self.MAX_COMM_STATE_VALUE`.
Correct.
Wait, I should use the exact same logic as requested: "use the message type rather than the bit length to indicate the type of communication status."
Let's see if there are any others.
Table 46 of M.1371-5. I don't have it, but the problem statement mentions "Type 1, 2 or 3".
And it mentioned 9, 18 and 26 as edge cases.
It didn't explicitly mention 4, but the docstrings in the code say Type 4 is SOTDMA.
Let's implement the fix.
<channel|><|tool_call>call:edit_file{filepath:<|"|>pyais/messages.py<|"|>,new_string:<|"|> @property
def is_sotdma(self) -> bool:
"""The radio status field has it's 20th bit (MSB) set to 0 or has less than 20 bits"""
if self.msg_type == 3:
return False
if self.msg_type in (1, 2, 4):
return True
return self.radio <= self.MAX_COMM_STATE_VALUE
@property
def is_itdma(self) -> bool:
"""The radio status field has it's 20th bit (MSB) set to 1"""
if self.msg_type == 3:
return True
if self.msg_type in (1, 2, 4):
return False
return self.radio > self.MAX_COMM_STATE_VALUE<|"|>,old_string:<|"|> @property
def is_sotdma(self) -> bool:
"""The radio status field has it's 20th bit (MSB) set to 0 or has less than 20 bits"""
return self.radio <= self.MAX_COMM_STATE_VALUE
@property
def is_itdma(self) -> bool:
"""The radio status field has it's 20th bit (MSB) set to 1"""
return self.radio > self.MAX_COMM_STATE_VALUE<|"|>}<tool_call|><|tool_response>
Patch, tests and logs
Final code change Included
diff --git a/pyais/messages.py b/pyais/messages.py
index 3f6ef48..ce14383 100644
--- a/pyais/messages.py
+++ b/pyais/messages.py
@@ -826,11 +826,19 @@ class CommunicationStateMixin:
@property
def is_sotdma(self) -> bool:
"""The radio status field has it's 20th bit (MSB) set to 0 or has less than 20 bits"""
+ if self.msg_type == 3:
+ return False
+ if self.msg_type in (1, 2, 4):
+ return True
return self.radio <= self.MAX_COMM_STATE_VALUE
@property
def is_itdma(self) -> bool:
"""The radio status field has it's 20th bit (MSB) set to 1"""
+ if self.msg_type == 3:
+ return True
+ if self.msg_type in (1, 2, 4):
+ return False
return self.radio > self.MAX_COMM_STATE_VALUE
@property
Test output Included
============================= test session starts ==============================
collected 302 items
tests/test_constants.py ........ [ 2%]
tests/test_decode.py ................................................... [ 19%]
.................................................... [ 36%]
tests/test_decode_raw.py ........ [ 39%]
tests/test_encode.py ................................................... [ 56%]
.......... [ 59%]
tests/test_examples.py . [ 59%]
tests/test_file_stream.py .s.......... [ 63%]
tests/test_filters.py ............... [ 68%]
tests/test_flags.py ......... [ 71%]
tests/test_generic_stream.py ........... [ 75%]
tests/test_main.py .... [ 76%]
tests/test_nmea.py ........................ [ 84%]
tests/test_socket.py ...... [ 86%]
tests/test_tag_block.py .......... [ 90%]
tests/test_talker_ids.py .......... [ 93%]
tests/test_tcp_stream.py ..s [ 94%]
tests/test_tracker.py ................ [ 99%]
tests/test_udp_stream.py . [100%]
==================================== PASSES ====================================
_______________________ TestExamples.test_run_every_file _______________________
----------------------------- Captured stdout call -----------------------------
MessageType1(msg_type=1, repeat=0, mmsi=367380120, status=<NavigationStatus.UnderWayUsingEngine: 0>, turn=<TurnRate.NO_TI_DEFAULT: -128.0>, speed=0.1, accuracy=False, lon=-122.404333, lat=37.806948, course=245.2, heading=511, second=59, maneuver=<ManeuverIndicator.NotAvailable: 0>, spare_1=b'\x00', raim=True, radio=34958)
MessageType5(msg_type=5, repeat=0, mmsi=368060190, ais_version=2, imo=0, callsign='WDK4954', shipname='P/V_GOLDEN_GATE', ship_type=<ShipType.PilotVessel: 50>, to_bow=14, to_stern=7, to_port=4, to_starboard=2, epfd=<EpfdType.Internal_GNSS: 15>, month=0, day=0, hour=24, minute=60, draught=0.0, destination='', dte=False, spare_1=b'\x00')
MessageType1(msg_type=1, repeat=0, mmsi=367380120, status=<NavigationStatus.UnderWayUsingEngine: 0>, turn=<TurnRate.NO_TI_DEFAULT: -128.0>, speed=0.1, accuracy=False, lon=-122.404333, lat=37.806948, course=245.2, heading=511, second=59, maneuver=<ManeuverIndicator.NotAvailable: 0>, spare_1=b'\x00', raim=True, radio=34958)
{'msg_type': 1, 'repeat': 0, 'mmsi': 367380120, 'status': <NavigationStatus.UnderWayUsingEngine: 0>, 'turn': <TurnRate.NO_TI_DEFAULT: -128.0>, 'speed': 0.1, 'accuracy': False, 'lon': -122.404333, 'lat': 37.806948, 'course': 245.2, 'heading': 511, 'second': 59, 'maneuver': <ManeuverIndicator.NotAvailable: 0>, 'spare_1': b'\x00', 'raim': True, 'radio': 34958}
{
"msg_type": 1,
"repeat": 0,
"mmsi": 367380120,
"status": 0,
"turn": -128.0,
"speed": 0.1,
"accuracy": false,
"lon": -122.404333,
"lat": 37.806948,
"course": 245.2,
"heading": 511,
"second": 59,
"maneuver": 0,
"spare_1": "AA==",
"raim": true,
"radio": 34958
}
MessageType1(msg_type=1, repeat=0, mmsi=227006760, status=<NavigationStatus.UnderWayUsingEngine: 0>, turn=<TurnRate.NO_TI_DEFAULT: -128.0>, speed=0.0, accuracy=False, lon=0.13138, lat=49.475577, course=36.7, heading=511, second=14, maneuver=<ManeuverIndicator.NotAvailable: 0>, spare_1=b'\x00', raim=False, radio=22136)
MessageType1(msg_type=1, repeat=0, mmsi=205448890, status=<NavigationStatus.UnderWayUsingEngine: 0>, turn=<TurnRate.NO_TI_DEFAULT: -128.0>, speed=0.0, accuracy=True, lon=4.419442, lat=51.237658, course=63.3, heading=511, second=15, maneuver=<ManeuverIndicator.NotAvailable: 0>, spare_1=b'\x00', raim=True, radio=2248)
MessageType1(msg_type=1, repeat=0, mmsi=786434, status=<NavigationStatus.UnderWayUsingEngine: 0>, turn=<TurnRate.NO_TI_DEFAULT: -128.0>, speed=1.6, accuracy=True, lon=5.320033, lat=51.967037, course=112.0, heading=511, second=15, maneuver=<ManeuverIndicator.NoSpecialManeuver: 1>, spare_1=b'\x00', raim=False, radio=153208)
MessageType1(msg_type=1, repeat=0, mmsi=249191000, status=<NavigationStatus.UnderWayUsingEngine: 0>, turn=<TurnRate.NO_TI_DEFAULT: -128.0>, speed=0.0, accuracy=True, lon=23.603633, lat=37.955883, course=247.0, heading=511, second=12, maneuver=<ManeuverIndicator.NotAvailable: 0>, spare_1=b'@', raim=False, radio=22136)
MessageType1(msg_type=1, repeat=0, mmsi=316013198, status=<NavigationStatus.UnderWayUsingEngine: 0>, turn=<TurnRate.NO_TI_DEFAULT: -128.0>, speed=0.0, accuracy=True, lon=-130.316237, lat=54.32111, course=237.9, heading=511, second=16, maneuver=<ManeuverIndicator.NotAvailable: 0>, spare_1=b'\x00', raim=True, radio=81935)
MessageType1(msg_type=1, repeat=0, mmsi=366913120, status=<NavigationStatus.UnderWayUsingEngine: 0>, turn=0.0, speed=0.0, accuracy=False, lon=-64.620662, lat=18.321188, course=329.5, heading=299, second=16, maneuver=<ManeuverIndicator.NotAvailable: 0>, spare_1=b'\x00', raim=True, radio=98890)
The raw radio value is: 393222
Communication state is SOTMDA: True
Communication state is ITDMA: False
Communication state: {
"received_stations": null,
"slot_number": null,
"utc_hour": null,
"utc_minute": null,
"slot_offset": 6,
"slot_timeout": 0,
"sync_state": 3,
"keep_flag": null,
"slot_increment": null,
"num_slots": null
}
MessageType1(msg_type=1, repeat=0, mmsi=227006760, status=<NavigationStatus.UnderWayUsingEngine: 0>, turn=<TurnRate.NO_TI_DEFAULT: -128.0>, speed=0.0, accuracy=False, lon=0.13138, lat=49.475577, course=36.7, heading=511, second=14, maneuver=<ManeuverIndicator.NotAvailable: 0>, spare_1=b'\x00', raim=False, radio=22136)
MessageType1(msg_type=1, repeat=0, mmsi=205448890, status=<NavigationStatus.UnderWayUsingEngine: 0>, turn=<TurnRate.NO_TI_DEFAULT: -128.0>, speed=0.0, accuracy=True, lon=4.419442, lat=51.237658, course=63.3, heading=511, second=15, maneuver=<ManeuverIndicator.NotAvailable: 0>, spare_1=b'\x00', raim=True, radio=2248)
MessageType1(msg_type=1, repeat=0, mmsi=786434, status=<NavigationStatus.UnderWayUsingEngine: 0>, turn=<TurnRate.NO_TI_DEFAULT: -128.0>, speed=1.6, accuracy=True, lon=5.320033, lat=51.967037, course=112.0, heading=511, second=15, maneuver=<ManeuverIndicator.NoSpecialManeuver: 1>, spare_1=b'\x00', raim=False, radio=153208)
MessageType1(msg_type=1, repeat=0, mmsi=249191000, status=<NavigationStatus.UnderWayUsingEngine: 0>, turn=<TurnRate.NO_TI_DEFAULT: -128.0>, speed=0.0, accuracy=True, lon=23.603633, lat=37.955883, course=247.0, heading=511, second=12, maneuver=<ManeuverIndicator.NotAvailable: 0>, spare_1=b'@', raim=False, radio=22136)
MessageType1(msg_type=1, repeat=0, mmsi=316013198, status=<NavigationStatus.UnderWayUsingEngine: 0>, turn=<TurnRate.NO_TI_DEFAULT: -128.0>, speed=0.0, accuracy=True, lon=-130.316237, lat=54.32111, course=237.9, heading=511, second=16, maneuver=<ManeuverIndicator.NotAvailable: 0>, spare_1=b'\x00', raim=True, radio=81935)
MessageType1(msg_type=1, repeat=0, mmsi=366913120, status=<NavigationStatus.UnderWayUsingEngine: 0>, turn=0.0, speed=0.0, accuracy=False, lon=-64.620662, lat=18.321188, course=329.5, heading=299, second=16, maneuver=<ManeuverIndicator.NotAvailable: 0>, spare_1=b'\x00', raim=True, radio=98890)
MessageType1(msg_type=1, repeat=0, mmsi=367078248, status=<NavigationStatus.UnderWayUsingEngine: 0>, turn=0.0, speed=34.6, accuracy=True, lon=-73.88158, lat=28.230963, course=181.2, heading=192, second=4, maneuver=<ManeuverIndicator.NotAvailable: 0>, spare_1=b'\xa0', raim=False, radio=96)
(Attribute(name='msg_type', default=NOTHING, validator=None, repr=True, eq=True, eq_key=None, order=True, order_key=None, hash=None, init=True, metadata=mappingproxy({'width': 6, 'd_type': <class 'int'>, 'from_converter': None, 'to_converter': None, 'signed': False, 'default': 1, 'variable_length': False}), type=None, converter=None, kw_only=False, inherited=False, on_setattr=None, alias='msg_type'), Attribute(name='repeat', default=NOTHING, validator=None, repr=True, eq=True, eq_key=None, order=True, order_key=None, hash=None, init=True, metadata=mappingproxy({'width': 2, 'd_type': <class 'int'>, 'from_converter': None, 'to_converter': None, 'signed': False, 'default': 0, 'variable_length': False}), type=None, converter=None, kw_only=False, inherited=False, on_setattr=None, alias='repeat'), Attribute(name='mmsi', default=NOTHING, validator=None, repr=True, eq=True, eq_key=None, order=True, order_key=None, hash=None, init=True, metadata=mappingproxy({'width': 30, 'd_type': <class 'int'>, 'from_converter': <function from_mmsi at 0x7c7149b01160>, 'to_converter': None, 'signed': False, 'default': None, 'variable_length': False}), type=None, converter=None, kw_only=False, inherited=False, on_setattr=None, alias='mmsi'), Attribute(name='status', default=NOTHING, validator=None, repr=True, eq=True, eq_key=None, order=True, order_key=None, hash=None, init=True, metadata=mappingproxy({'width': 4, 'd_type': <class 'int'>, 'from_converter': None, 'to_converter': None, 'signed': False, 'default': 0, 'variable_length': False}), type=None, converter=<bound method NavigationStatus.from_value of <enum 'NavigationStatus'>>, kw_only=False, inherited=False, on_setattr=None, alias='status'), Attribute(name='turn', default=NOTHING, validator=None, repr=True, eq=True, eq_key=None, order=True, order_key=None, hash=None, init=True, metadata=mappingproxy({'width': 8, 'd_type': <class 'float'>, 'from_converter': <function from_turn at 0x7c7149b01280>, 'to_converter': <function to_turn at 0x7c7149b011f0>, 'signed': True, 'default': <TurnRate.NO_TI_DEFAULT: -128.0>, 'variable_length': False}), type=None, converter=None, kw_only=False, inherited=False, on_setattr=None, alias='turn'), Attribute(name='speed', default=NOTHING, validator=None, repr=True, eq=True, eq_key=None, order=True, order_key=None, hash=None, init=True, metadata=mappingproxy({'width': 10, 'd_type': <class 'float'>, 'from_converter': <function from_speed at 0x7c7149af58b0>, 'to_converter': <function to_speed at 0x7c7149afed30>, 'signed': False, 'default': 0, 'variable_length': False}), type=None, converter=None, kw_only=False, inherited=False, on_setattr=None, alias='speed'), Attribute(name='accuracy', default=NOTHING, validator=None, repr=True, eq=True, eq_key=None, order=True, order_key=None, hash=None, init=True, metadata=mappingproxy({'width': 1, 'd_type': <class 'bool'>, 'from_converter': None, 'to_converter': None, 'signed': False, 'default': 0, 'variable_length': False}), type=None, converter=None, kw_only=False, inherited=False, on_setattr=None, alias='accuracy'), Attribute(name='lon', default=NOTHING, validator=None, repr=True, eq=True, eq_key=None, order=True, order_key=None, hash=None, init=True, metadata=mappingproxy({'width': 28, 'd_type': <class 'float'>, 'from_converter': <function from_lat_lon at 0x7c7149afedc0>, 'to_converter': <function to_lat_lon at 0x7c7149afee50>, 'signed': True, 'default': 0, 'variable_length': False}), type=None, converter=None, kw_only=False, inherited=False, on_setattr=None, alias='lon'), Attribute(name='lat', default=NOTHING, validator=None, repr=True, eq=True, eq_key=None, order=True, order_key=None, hash=None, init=True, metadata=mappingproxy({'width': 27, 'd_type': <class 'float'>, 'from_converter': <function from_lat_lon at 0x7c7149afedc0>, 'to_converter': <function to_lat_lon at 0x7c7149afee50>, 'signed': True, 'default': 0, 'variable_length': False}), type=None, converter=None, kw_only=False, inherited=False, on_setattr=None, alias='lat'), Attribute(name='course', default=NOTHING, validator=None, repr=True, eq=True, eq_key=None, order=True, order_key=None, hash=None, init=True, metadata=mappingproxy({'width': 12, 'd_type': <class 'float'>, 'from_converter': <function from_10th at 0x7c7149b01040>, 'to_converter': <function to_10th at 0x7c7149b010d0>, 'signed': False, 'default': 0, 'variable_length': False}), type=None, converter=None, kw_only=False, inherited=False, on_setattr=None, alias='course'), Attribute(name='heading', default=NOTHING, validator=None, repr=True, eq=True, eq_key=None, order=True, order_key=None, hash=None, init=True, metadata=mappingproxy({'width': 9, 'd_type': <class 'int'>, 'from_converter': None, 'to_converter': None, 'signed': False, 'default': 0, 'variable_length': False}), type=None, converter=None, kw_only=False, inherited=False, on_setattr=None, alias='heading'), Attribute(name='second', default=NOTHING, validator=None, repr=True, eq=True, eq_key=None, order=True, order_key=None, hash=None, init=True, metadata=mappingproxy({'width': 6, 'd_type': <class 'int'>, 'from_converter': None, 'to_converter': None, 'signed': False, 'default': 0, 'variable_length': False}), type=None, converter=None, kw_only=False, inherited=False, on_setattr=None, alias='second'), Attribute(name='maneuver', default=NOTHING, validator=None, repr=True, eq=True, eq_key=None, order=True, order_key=None, hash=None, init=True, metadata=mappingproxy({'width': 2, 'd_type': <class 'int'>, 'from_converter': <bound method ManeuverIndicator.from_value of <enum 'ManeuverIndicator'>>, 'to_converter': <bound method ManeuverIndicator.from_value of <enum 'ManeuverIndicator'>>, 'signed': False, 'default': 0, 'variable_length': False}), type=None, converter=None, kw_only=False, inherited=False, on_setattr=None, alias='maneuver'), Attribute(name='spare_1', default=NOTHING, validator=None, repr=True, eq=True, eq_key=None, order=True, order_key=None, hash=None, init=True, metadata=mappingproxy({'width': 3, 'd_type': <class 'bytes'>, 'from_converter': None, 'to_converter': None, 'signed': False, 'default': b'', 'variable_length': False}), type=None, converter=None, kw_only=False, inherited=False, on_setattr=None, alias='spare_1'), Attribute(name='raim', default=NOTHING, validator=None, repr=True, eq=True, eq_key=None, order=True, order_key=None, hash=None, init=True, metadata=mappingproxy({'width': 1, 'd_type': <class 'bool'>, 'from_converter': None, 'to_converter': None, 'signed': False, 'default': 0, 'variable_length': False}), type=None, converter=None, kw_only=False, inherited=False, on_setattr=None, alias='raim'), Attribute(name='radio', default=NOTHING, validator=None, repr=True, eq=True, eq_key=None, order=True, order_key=None, hash=None, init=True, metadata=mappingproxy({'width': 19, 'd_type': <class 'int'>, 'from_converter': None, 'to_converter': None, 'signed': False, 'default': 0, 'variable_length': False}), type=None, converter=None, kw_only=False, inherited=False, on_setattr=None, alias='radio'))
['!AIVDO,1,1,,A,15M67F@P00G?ur@E`FL8T@000000,0*06']
['!AIVDO,1,1,,B,15M67F@P00G?ur@E`FL8T@000000,0*05']
['!AIVDO,1,1,,A,15M67F@P00G?ur@E`FL8T@000000,0*06']
['!AIVDO,1,1,,B,15M67F@P00G?ur@E`FL8T@000000,0*05']
AISSentence<!AIVDM,1,1,,B,15N4cJ005Jrek0H@9nDW5608EP,013>
{'raw': b'g:1-2-73874,n:157036,s:r003669945,c:12415440354*A', 'receiver_timestamp': '12415440354', 'source_station': 'r003669945', 'destination_station': None, 'line_count': '157036', 'relative_time': None, 'text': None}
{'raw': b'g:1-2-73874,n:157036,s:r003669945,c:12415440354*A', 'receiver_timestamp': '12415440354', 'source_station': 'r003669945', 'destination_station': None, 'line_count': '157036', 'relative_time': None, 'text': None}
MessageType1(msg_type=1, repeat=0, mmsi=366053209, status=<NavigationStatus.RestrictedManoeuverability: 3>, turn=0.0, speed=0.0, accuracy=False, lon=-122.341618, lat=37.802118, course=219.3, heading=1, second=59, maneuver=<ManeuverIndicator.NotAvailable: 0>, spare_1=b'\x00', raim=False, radio=2281)
MessageType1(msg_type=1, repeat=0, mmsi=366053209, status=<NavigationStatus.RestrictedManoeuverability: 3>, turn=0.0, speed=0.0, accuracy=False, lon=-122.341618, lat=37.802118, course=219.3, heading=1, second=59, maneuver=<ManeuverIndicator.NotAvailable: 0>, spare_1=b'\x00', raim=False, radio=2281)
MessageType5(msg_type=5, repeat=0, mmsi=210035000, ais_version=0, imo=9514755, callsign='5BXT2', shipname='NORDIC HAMBURG', ship_type=<ShipType.Cargo_HazardousCategory_A: 71>, to_bow=142, to_stern=10, to_port=11, to_starboard=11, epfd=<EpfdType.GPS: 1>, month=7, day=20, hour=5, minute=0, draught=7.1, destination='CTT-LAYBY', dte=False, spare_1=b'\x00')
MessageType5(msg_type=5, repeat=0, mmsi=210035000, ais_version=0, imo=9514755, callsign='5BXT2', shipname='NORDIC HAMBURG', ship_type=<ShipType.Cargo_HazardousCategory_A: 71>, to_bow=142, to_stern=10, to_port=11, to_starboard=11, epfd=<EpfdType.GPS: 1>, month=7, day=20, hour=5, minute=0, draught=7.1, destination='CTT-LAYBY', dte=False, spare_1=b'\x00')
latest 10 tracks 367078248,366913120,316013198,249191000,786434,205448890,227006760
AISTrack(mmsi=249191000, turn=<TurnRate.NO_TI_DEFAULT: -128.0>, speed=0.0, lon=23.603633, lat=37.955883, course=247.0, heading=511, imo=None, callsign=None, shipname=None, ship_type=None, to_bow=None, to_stern=None, to_port=None, to_starboard=None, destination=None, last_updated=1794629663.8148715, name=None, ais_version=None, ais_type=None, status=<NavigationStatus.UnderWayUsingEngine: 0>)
********************************************************************************
Country 338
Online 1
PSS
Region 2
Timestamp 2008-05-09 00:00:00.010000
MessageType1(msg_type=1, repeat=0, mmsi=367309370, status=<NavigationStatus.UnderWayUsingEngine: 0>, turn=<TurnRate.NO_TI_DEFAULT: -128.0>, speed=11.1, accuracy=False, lon=-122.765622, lat=48.177737, course=138.5, heading=511, second=0, maneuver=<ManeuverIndicator.NotAvailable: 0>, spare_1=b'\x00', raim=False, radio=49181)
********************************************************************************
MessageType1(msg_type=1, repeat=0, mmsi=413355820, status=<NavigationStatus.UnderWayUsingEngine: 0>, turn=<TurnRate.NO_TI_DEFAULT: -128.0>, speed=0.0, accuracy=True, lon=119.698612, lat=39.932017, course=342.1, heading=259, second=14, maneuver=<ManeuverIndicator.NotAvailable: 0>, spare_1=b'\x00', raim=False, radio=33328)
********************************************************************************
Country 338
Online 1
PSS
Region 2
Timestamp 2009-05-09 00:00:00.010000
MessageType1(msg_type=1, repeat=0, mmsi=445451000, status=<NavigationStatus.UnderWaySailing: 8>, turn=<TurnRate.NO_TI_DEFAULT: -128.0>, speed=3.9, accuracy=False, lon=120.365158, lat=35.997872, course=310.5, heading=511, second=13, maneuver=<ManeuverIndicator.NotAvailable: 0>, spare_1=b'\x00', raim=False, radio=99017)
********************************************************************************
MessageType1(msg_type=1, repeat=0, mmsi=412764890, status=<NavigationStatus.UnderWayUsingEngine: 0>, turn=0.0, speed=11.1, accuracy=True, lon=122.515033, lat=31.652833, course=4.5, heading=0, second=14, maneuver=<ManeuverIndicator.NotAvailable: 0>, spare_1=b'\x00', raim=False, radio=33329)
______________________ TestMainApp.test_decode_from_file _______________________
----------------------------- Captured stdout call -----------------------------
MessageType1(msg_type=1, repeat=0, mmsi=227006760, status=<NavigationStatus.UnderWayUsingEngine: 0>, turn=<TurnRate.NO_TI_DEFAULT: -128.0>, speed=0.0, accuracy=False, lon=0.13138, lat=49.475577, course=36.7, heading=511, second=14, maneuver=<ManeuverIndicator.NotAvailable: 0>, spare_1=b'\x00', raim=False, radio=22136)
MessageType1(msg_type=1, repeat=0, mmsi=205448890, status=<NavigationStatus.UnderWayUsingEngine: 0>, turn=<TurnRate.NO_TI_DEFAULT: -128.0>, speed=0.0, accuracy=True, lon=4.419442, lat=51.237658, course=63.3, heading=511, second=15, maneuver=<ManeuverIndicator.NotAvailable: 0>, spare_1=b'\x00', raim=True, radio=2248)
MessageType1(msg_type=1, repeat=0, mmsi=786434, status=<NavigationStatus.UnderWayUsingEngine: 0>, turn=<TurnRate.NO_TI_DEFAULT: -128.0>, speed=1.6, accuracy=True, lon=5.320033, lat=51.967037, course=112.0, heading=511, second=15, maneuver=<ManeuverIndicator.NoSpecialManeuver: 1>, spare_1=b'\x00', raim=False, radio=153208)
MessageType1(msg_type=1, repeat=0, mmsi=249191000, status=<NavigationStatus.UnderWayUsingEngine: 0>, turn=<TurnRate.NO_TI_DEFAULT: -128.0>, speed=0.0, accuracy=True, lon=23.603633, lat=37.955883, course=247.0, heading=511, second=12, maneuver=<ManeuverIndicator.NotAvailable: 0>, spare_1=b'@', raim=False, radio=22136)
MessageType1(msg_type=1, repeat=0, mmsi=316013198, status=<NavigationStatus.UnderWayUsingEngine: 0>, turn=<TurnRate.NO_TI_DEFAULT: -128.0>, speed=0.0, accuracy=True, lon=-130.316237, lat=54.32111, course=237.9, heading=511, second=16, maneuver=<ManeuverIndicator.NotAvailable: 0>, spare_1=b'\x00', raim=True, radio=81935)
MessageType1(msg_type=1, repeat=0, mmsi=366913120, status=<NavigationStatus.UnderWayUsingEngine: 0>, turn=0.0, speed=0.0, accuracy=False, lon=-64.620662, lat=18.321188, course=329.5, heading=299, second=16, maneuver=<ManeuverIndicator.NotAvailable: 0>, spare_1=b'\x00', raim=True, radio=98890)
MessageType5(msg_type=5, repeat=0, mmsi=351759000, ais_version=0, imo=9134270, callsign='3FOF8', shipname='EVER DIADEM', ship_type=<ShipType.Cargo: 70>, to_bow=225, to_stern=70, to_port=1, to_starboard=31, epfd=<EpfdType.GPS: 1>, month=5, day=15, hour=14, minute=0, draught=12.2, destination='NEW YORK', dte=False, spare_1=b'\x00')
________________________ TestMainApp.test_decode_single ________________________
----------------------------- Captured stdout call -----------------------------
MessageType9(msg_type=9, repeat=0, mmsi=111232511, alt=303, speed=42.0, accuracy=False, lon=-6.278843, lat=58.144, course=154.5, second=15, reserved_1=0, dte=True, spare_1=b'\x00', assigned=False, raim=False, radio=33392)
_____________________ TestMainApp.test_decode_single_multi _____________________
----------------------------- Captured stdout call -----------------------------
MessageType5(msg_type=5, repeat=0, mmsi=210035000, ais_version=0, imo=9514755, callsign='5BXT2', shipname='NORDIC HAMBURG', ship_type=<ShipType.Cargo_HazardousCategory_A: 71>, to_bow=142, to_stern=10, to_port=11, to_starboard=11, epfd=<EpfdType.GPS: 1>, month=7, day=20, hour=5, minute=0, draught=7.1, destination='CTT-LAYBY', dte=False, spare_1=b'\x00')
MessageType5(msg_type=5, repeat=0, mmsi=210035000, ais_version=0, imo=9514755, callsign='5BXT2', shipname='NORDIC HAMBURG', ship_type=<ShipType.Cargo_HazardousCategory_A: 71>, to_bow=142, to_stern=10, to_port=11, to_starboard=11, epfd=<EpfdType.GPS: 1>, month=7, day=20, hour=5, minute=0, draught=7.1, destination='CTT-LAYBY', dte=False, spare_1=b'\x00')
___________________________ TestMainApp.test_parser ____________________________
----------------------------- Captured stderr call -----------------------------
usage: ais-decode [-h] [-f [IN_FILE]] [-o OUT_FILE] {socket,single} ...
ais-decode: error: argument -f/--file: can't open 'invalid': [Errno 2] No such file or directory: 'invalid'
usage: ais-decode single [-h] messages [messages ...]
ais-decode single: error: the following arguments are required: messages
usage: ais-decode socket [-h] [-t [{udp,tcp}]] destination port
ais-decode socket: error: argument -t/--type: invalid choice: 'something else' (choose from 'udp', 'tcp')
- generated xml file: /tmp/rebench-grade-7db3b0bfa5df4558ac2031e88f0b5932.xml --
=========================== short test summary info ============================
PASSED tests/test_constants.py::TestConstants::test_maneuver_indication
PASSED tests/test_constants.py::TestConstants::test_nav_status
PASSED tests/test_constants.py::TestConstants::test_navaid
PASSED tests/test_constants.py::TestConstants::test_ship_types
PASSED tests/test_constants.py::TestConstants::test_station_intervals
PASSED tests/test_constants.py::TestConstants::test_station_types
PASSED tests/test_constants.py::TestConstants::test_t_mode
PASSED tests/test_constants.py::TestConstants::test_that_nav_data_values_are_kept_for_all_values
PASSED tests/test_decode.py::TestAIS::test_b64encode_str
PASSED tests/test_decode.py::TestAIS::test_b64encode_str_empty
PASSED tests/test_decode.py::TestAIS::test_bits2bytes
PASSED tests/test_decode.py::TestAIS::test_broken_messages
PASSED tests/test_decode.py::TestAIS::test_byte_stream
PASSED tests/test_decode.py::TestAIS::test_bytes2bits
PASSED tests/test_decode.py::TestAIS::test_common_invalid_inputs_to_the_decode_function
PASSED tests/test_decode.py::TestAIS::test_decode_1_speed
PASSED tests/test_decode.py::TestAIS::test_decode_and_merge
PASSED tests/test_decode.py::TestAIS::test_decode_does_not_raise_an_error_by_default
PASSED tests/test_decode.py::TestAIS::test_decode_does_not_raise_an_error_if_error_if_checksum_invalid_is_false
PASSED tests/test_decode.py::TestAIS::test_decode_does_raise_an_error_if_error_if_checksum_invalid_is_true
PASSED tests/test_decode.py::TestAIS::test_decode_into_bit_array_with_non_printable_characters
PASSED tests/test_decode.py::TestAIS::test_decode_out_of_order
PASSED tests/test_decode.py::TestAIS::test_decode_pos_1_2_3
PASSED tests/test_decode.py::TestAIS::test_empty_channel
PASSED tests/test_decode.py::TestAIS::test_get_comm_state_type_18_itdma_base_indirect
PASSED tests/test_decode.py::TestAIS::test_get_comm_state_type_18_sotdma_base_inidrect
PASSED tests/test_decode.py::TestAIS::test_get_comm_state_type_18_sotdma_utc_direct
PASSED tests/test_decode.py::TestAIS::test_get_sotdma_comm_state_utc_direct
PASSED tests/test_decode.py::TestAIS::test_get_sotdma_comm_state_utc_direct_slot_number
PASSED tests/test_decode.py::TestAIS::test_get_sotdma_comm_state_utc_direct_slot_timeout
PASSED tests/test_decode.py::TestAIS::test_gh_ais_message_decode
PASSED tests/test_decode.py::TestAIS::test_invalid_timestamp_message
PASSED tests/test_decode.py::TestAIS::test_is_sotdma_or_itdma
PASSED tests/test_decode.py::TestAIS::test_issue_46_a
PASSED tests/test_decode.py::TestAIS::test_issue_46_b
PASSED tests/test_decode.py::TestAIS::test_issue_50
PASSED tests/test_decode.py::TestAIS::test_issue_88
PASSED tests/test_decode.py::TestAIS::test_messages_with_proprietary_suffix
PASSED tests/test_decode.py::TestAIS::test_msg_too_short_enum_is_none
PASSED tests/test_decode.py::TestAIS::test_msg_type
PASSED tests/test_decode.py::TestAIS::test_msg_type_10_a
PASSED tests/test_decode.py::TestAIS::test_msg_type_10_b
PASSED tests/test_decode.py::TestAIS::test_msg_type_11
PASSED tests/test_decode.py::TestAIS::test_msg_type_12_a
PASSED tests/test_decode.py::TestAIS::test_msg_type_12_b
PASSED tests/test_decode.py::TestAIS::test_msg_type_13
PASSED tests/test_decode.py::TestAIS::test_msg_type_14
PASSED tests/test_decode.py::TestAIS::test_msg_type_15_a
PASSED tests/test_decode.py::TestAIS::test_msg_type_15_b
PASSED tests/test_decode.py::TestAIS::test_msg_type_16
PASSED tests/test_decode.py::TestAIS::test_msg_type_17_a
PASSED tests/test_decode.py::TestAIS::test_msg_type_17_b
PASSED tests/test_decode.py::TestAIS::test_msg_type_17_to_json
PASSED tests/test_decode.py::TestAIS::test_msg_type_18
PASSED tests/test_decode.py::TestAIS::test_msg_type_18_speed
PASSED tests/test_decode.py::TestAIS::test_msg_type_19
PASSED tests/test_decode.py::TestAIS::test_msg_type_1_a
PASSED tests/test_decode.py::TestAIS::test_msg_type_1_b
PASSED tests/test_decode.py::TestAIS::test_msg_type_1_c
PASSED tests/test_decode.py::TestAIS::test_msg_type_20
PASSED tests/test_decode.py::TestAIS::test_msg_type_21
PASSED tests/test_decode.py::TestAIS::test_msg_type_22_addressed
PASSED tests/test_decode.py::TestAIS::test_msg_type_22_broadcast
PASSED tests/test_decode.py::TestAIS::test_msg_type_23
PASSED tests/test_decode.py::TestAIS::test_msg_type_24
PASSED tests/test_decode.py::TestAIS::test_msg_type_24_with_160_bits
PASSED tests/test_decode.py::TestAIS::test_msg_type_24_with_168_bits
PASSED tests/test_decode.py::TestAIS::test_msg_type_25_a
PASSED tests/test_decode.py::TestAIS::test_msg_type_25_b
PASSED tests/test_decode.py::TestAIS::test_msg_type_25_c
PASSED tests/test_decode.py::TestAIS::test_msg_type_25_to_json
PASSED tests/test_decode.py::TestAIS::test_msg_type_26_a
PASSED tests/test_decode.py::TestAIS::test_msg_type_26_b
PASSED tests/test_decode.py::TestAIS::test_msg_type_26_to_json
PASSED tests/test_decode.py::TestAIS::test_msg_type_27
PASSED tests/test_decode.py::TestAIS::test_msg_type_27_signed
PASSED tests/test_decode.py::TestAIS::test_msg_type_3
PASSED tests/test_decode.py::TestAIS::test_msg_type_4_a
PASSED tests/test_decode.py::TestAIS::test_msg_type_4_b
PASSED tests/test_decode.py::TestAIS::test_msg_type_5
PASSED tests/test_decode.py::TestAIS::test_msg_type_6
PASSED tests/test_decode.py::TestAIS::test_msg_type_6_json_reverse
PASSED tests/test_decode.py::TestAIS::test_msg_type_6_to_json
PASSED tests/test_decode.py::TestAIS::test_msg_type_7
PASSED tests/test_decode.py::TestAIS::test_msg_type_8
PASSED tests/test_decode.py::TestAIS::test_msg_type_8_multipart
PASSED tests/test_decode.py::TestAIS::test_msg_type_8_to_json
PASSED tests/test_decode.py::TestAIS::test_msg_type_9
PASSED tests/test_decode.py::TestAIS::test_msg_with_more_that_82_chars_payload
PASSED tests/test_decode.py::TestAIS::test_multiline_message
PASSED tests/test_decode.py::TestAIS::test_nmea_decode
PASSED tests/test_decode.py::TestAIS::test_nmea_decode_unknown_msg
PASSED tests/test_decode.py::TestAIS::test_none_value_converter_for_creation
PASSED tests/test_decode.py::TestAIS::test_none_value_converter_for_decoding
PASSED tests/test_decode.py::TestAIS::test_none_values_converter_for_all_messages
PASSED tests/test_decode.py::TestAIS::test_rot_decode_yields_expected_values
PASSED tests/test_decode.py::TestAIS::test_rot_encode_decode
PASSED tests/test_decode.py::TestAIS::test_rot_encode_yields_expected_values
PASSED tests/test_decode.py::TestAIS::test_sotdma_time_conversion
PASSED tests/test_decode.py::TestAIS::test_special_position_report
PASSED tests/test_decode.py::TestAIS::test_static_data_report
PASSED tests/test_decode.py::TestAIS::test_that_decode_nmea_and_ais_works_with_proprietary_messages
PASSED tests/test_decode.py::TestAIS::test_that_lat_and_long_are_rounded_correctly
PASSED tests/test_decode.py::TestAIS::test_that_the_payload_does_not_change_when_encoding_decoding
PASSED tests/test_decode.py::TestAIS::test_timestamp_message
PASSED tests/test_decode.py::TestAIS::test_to_dict_non_enum
PASSED tests/test_decode.py::TestAIS::test_to_json
PASSED tests/test_decode.py::TestAIS::test_type_22_very_short
PASSED tests/test_decode.py::TestAIS::test_type_25_very_short
PASSED tests/test_decode.py::TestAIS::test_type_26_very_short
PASSED tests/test_decode.py::TestAIS::test_types_for_messages
PASSED tests/test_decode_raw.py::TestDecode::test_bytes_invalid
PASSED tests/test_decode_raw.py::TestDecode::test_bytes_valid
PASSED tests/test_decode_raw.py::TestDecode::test_decode_multiline_message
PASSED tests/test_decode_raw.py::TestDecode::test_decode_total_garbage
PASSED tests/test_decode_raw.py::TestDecode::test_multipart_error_message
PASSED tests/test_decode_raw.py::TestDecode::test_str_invalid
PASSED tests/test_decode_raw.py::TestDecode::test_str_valid
PASSED tests/test_decode_raw.py::TestDecode::test_too_many_messages
PASSED tests/test_encode.py::test_widths
PASSED tests/test_encode.py::test_variable_message_length_width
PASSED tests/test_encode.py::test_encode_msg_table
PASSED tests/test_encode.py::test_invalid_talker_id
PASSED tests/test_encode.py::test_encode_payload_invalid_talker_id
PASSED tests/test_encode.py::test_invalid_radio_channel
PASSED tests/test_encode.py::test_encode_payload_error_radio
PASSED tests/test_encode.py::test_data_to_payload
PASSED tests/test_encode.py::test_get_ais_type
PASSED tests/test_encode.py::test_str_to_bin
PASSED tests/test_encode.py::test_int_to_bin
PASSED tests/test_encode.py::test_decode_encode
PASSED tests/test_encode.py::test_encode_type_27
PASSED tests/test_encode.py::test_encode_type_26
PASSED tests/test_encode.py::test_encode_type_25_b
PASSED tests/test_encode.py::test_encode_type_25_a
PASSED tests/test_encode.py::test_encode_type_24_partno_invalid
PASSED tests/test_encode.py::test_encode_type_24_a
PASSED tests/test_encode.py::test_encode_type_24_b
PASSED tests/test_encode.py::test_encode_type_23
PASSED tests/test_encode.py::test_encode_type_22_b
PASSED tests/test_encode.py::test_encode_type_22_a
PASSED tests/test_encode.py::test_encode_type_21
PASSED tests/test_encode.py::test_encode_type_20
PASSED tests/test_encode.py::test_encode_type_19
PASSED tests/test_encode.py::test_encode_type_18_with_speed_and_course
PASSED tests/test_encode.py::test_encode_type_18
PASSED tests/test_encode.py::test_encode_type_17_b
PASSED tests/test_encode.py::test_encode_type_17_a
PASSED tests/test_encode.py::test_encode_type_16
PASSED tests/test_encode.py::test_encode_type_15_a
PASSED tests/test_encode.py::test_encode_type_15
PASSED tests/test_encode.py::test_encode_type_14
PASSED tests/test_encode.py::test_encode_type_13
PASSED tests/test_encode.py::test_encode_type_12_with_some_text
PASSED tests/test_encode.py::test_encode_type_12_with_no_text
PASSED tests/test_encode.py::test_encode_type_12_with_empty_text
PASSED tests/test_encode.py::test_encode_type_12_with_max_length_text
PASSED tests/test_encode.py::test_encode_type_12_with_longer_than_max_length_length_text
PASSED tests/test_encode.py::test_encode_type_11
PASSED tests/test_encode.py::test_encode_type_10
PASSED tests/test_encode.py::test_encode_type_9
PASSED tests/test_encode.py::test_encode_type_8
PASSED tests/test_encode.py::test_encode_type_7
PASSED tests/test_encode.py::test_encode_type_6_bytes
PASSED tests/test_encode.py::test_encode_type_6
PASSED tests/test_encode.py::test_encode_type_4
PASSED tests/test_encode.py::test_encode_type_5_issue_59
PASSED tests/test_encode.py::test_encode_type_5
PASSED tests/test_encode.py::test_encode_type_5_default
PASSED tests/test_encode.py::test_encode_msg_type2
PASSED tests/test_encode.py::test_encode_msg_type_3
PASSED tests/test_encode.py::test_encode_type_1_default
PASSED tests/test_encode.py::test_encode_type_1
PASSED tests/test_encode.py::test_mmsi_too_long
PASSED tests/test_encode.py::test_lon_too_large
PASSED tests/test_encode.py::test_ship_name_too_lon
PASSED tests/test_encode.py::test_int_to_bytes
PASSED tests/test_encode.py::test_to_six_bit
PASSED tests/test_encode.py::test_encode_ascii_6_bit
PASSED tests/test_encode.py::test_encode_does_not_exceed_nmea_sentence_length_limit
PASSED tests/test_examples.py::TestExamples::test_run_every_file
PASSED tests/test_file_stream.py::TestFileReaderStream::test_invalid_filename
PASSED tests/test_file_stream.py::TestFileReaderStream::test_marine_traffic_sample
PASSED tests/test_file_stream.py::TestFileReaderStream::test_mixed_content
PASSED tests/test_file_stream.py::TestFileReaderStream::test_nmea_sort_index_error
PASSED tests/test_file_stream.py::TestFileReaderStream::test_nmea_sort_invalid_frag_cnt
PASSED tests/test_file_stream.py::TestFileReaderStream::test_nmea_sorter_sorted
PASSED tests/test_file_stream.py::TestFileReaderStream::test_nmea_sorter_unsorted
PASSED tests/test_file_stream.py::TestFileReaderStream::test_reader
PASSED tests/test_file_stream.py::TestFileReaderStream::test_reader_with_open
PASSED tests/test_file_stream.py::TestFileReaderStream::test_sort_with_different_msg_ids
PASSED tests/test_file_stream.py::TestFileReaderStream::test_timestamp_messages
PASSED tests/test_filters.py::TestNoneFilter::test_filtering_missing_attributes
PASSED tests/test_filters.py::TestNoneFilter::test_filtering_none_attributes
PASSED tests/test_filters.py::TestMessageTypeFilter::test_message_type_filtering
PASSED tests/test_filters.py::TestDistanceFilter::test_all_within_distance
PASSED tests/test_filters.py::TestDistanceFilter::test_distance_filtering
PASSED tests/test_filters.py::TestDistanceFilter::test_none_within_distance
PASSED tests/test_filters.py::TestDistanceFilter::test_specific_coordinate_distance
PASSED tests/test_filters.py::TestGridFilter::test_central_park_grid
PASSED tests/test_filters.py::TestGridFilter::test_grid_filtering
PASSED tests/test_filters.py::TestFilterChain::test_complex_filter_chain
PASSED tests/test_filters.py::TestFilterChain::test_filter_chain
PASSED tests/test_filters.py::TestAttributeFilter::test_attribute_filtering
PASSED tests/test_filters.py::TestHaversine::test_london_to_paris
PASSED tests/test_filters.py::TestHaversine::test_nyc_to_la
PASSED tests/test_filters.py::TestHaversine::test_sydney_to_melbourne
PASSED tests/test_flags.py::MMSICountryCodeTestCase::test_get_three_digits
PASSED tests/test_flags.py::MMSICountryCodeTestCase::test_random_ship_1
PASSED tests/test_flags.py::MMSICountryCodeTestCase::test_random_ship_2
PASSED tests/test_flags.py::MMSICountryCodeTestCase::test_random_ship_3
PASSED tests/test_flags.py::MMSICountryCodeTestCase::test_random_ship_4
PASSED tests/test_flags.py::MMSICountryCodeTestCase::test_random_ship_5
PASSED tests/test_flags.py::MMSICountryCodeTestCase::test_random_ship_6
PASSED tests/test_flags.py::MMSICountryCodeTestCase::test_random_ship_7
PASSED tests/test_flags.py::MMSICountryCodeTestCase::test_random_ship_8
PASSED tests/test_generic_stream.py::TestGenericStream::test_empty_stream
PASSED tests/test_generic_stream.py::TestGenericStream::test_garbage_stream
PASSED tests/test_generic_stream.py::TestGenericStream::test_invalid_msg
PASSED tests/test_generic_stream.py::TestIterMessages::test_init_from_bytes_transforms_bytes_to_list
PASSED tests/test_generic_stream.py::TestIterMessages::test_init_from_bytes_with_multiple_messages
PASSED tests/test_generic_stream.py::TestIterMessages::test_init_from_ignores_error_if_enabled
PASSED tests/test_generic_stream.py::TestIterMessages::test_init_from_str
PASSED tests/test_generic_stream.py::TestIterMessages::test_init_from_str_throws_error_if_invalid
PASSED tests/test_generic_stream.py::TestIterMessages::test_iter_is_generator
PASSED tests/test_generic_stream.py::TestIterMessages::test_iter_messages_does_assemble_multiline_messages
PASSED tests/test_generic_stream.py::TestIterMessages::test_iter_messages_handles_single_message
PASSED tests/test_main.py::TestMainApp::test_decode_from_file
PASSED tests/test_main.py::TestMainApp::test_decode_single
PASSED tests/test_main.py::TestMainApp::test_decode_single_multi
PASSED tests/test_main.py::TestMainApp::test_parser
PASSED tests/test_nmea.py::TestNMEA::test_attrs
PASSED tests/test_nmea.py::TestNMEA::test_chk_to_int_with_fill_bits
PASSED tests/test_nmea.py::TestNMEA::test_chk_to_int_with_missing_checksum
PASSED tests/test_nmea.py::TestNMEA::test_chk_to_int_with_missing_fill_bits
PASSED tests/test_nmea.py::TestNMEA::test_chk_to_int_with_valid_checksum
PASSED tests/test_nmea.py::TestNMEA::test_dict
PASSED tests/test_nmea.py::TestNMEA::test_from_bytes
PASSED tests/test_nmea.py::TestNMEA::test_from_str
PASSED tests/test_nmea.py::TestNMEA::test_get_item
PASSED tests/test_nmea.py::TestNMEA::test_get_item_raises_key_error
PASSED tests/test_nmea.py::TestNMEA::test_get_item_raises_type_error
PASSED tests/test_nmea.py::TestNMEA::test_message_assembling
PASSED tests/test_nmea.py::TestNMEA::test_message_eq_method
PASSED tests/test_nmea.py::TestNMEA::test_missing_checksum
PASSED tests/test_nmea.py::TestNMEA::test_single
PASSED tests/test_nmea.py::TestNMEA::test_talker
PASSED tests/test_nmea.py::TestNMEA::test_that_a_valid_checksum_is_correctly_identified
PASSED tests/test_nmea.py::TestNMEA::test_that_a_valid_checksum_is_correctly_identified_for_multi_part_msgs
PASSED tests/test_nmea.py::TestNMEA::test_that_an_invalid_checksum_is_correctly_identified
PASSED tests/test_nmea.py::TestNMEA::test_that_an_invalid_checksum_is_correctly_identified_for_multi_part_msgs
PASSED tests/test_nmea.py::TestNMEA::test_type
PASSED tests/test_nmea.py::TestNMEA::test_validity
PASSED tests/test_nmea.py::TestNMEA::test_values
PASSED tests/test_nmea.py::TestNMEA::test_wrong_type
PASSED tests/test_socket.py::SocketStreamingTestCase::test_that_a_single_line_is_returned_as_is
PASSED tests/test_socket.py::SocketStreamingTestCase::test_that_all_lines_are_returned
PASSED tests/test_socket.py::SocketStreamingTestCase::test_that_all_lines_are_returned_for_incomplete_lines
PASSED tests/test_socket.py::SocketStreamingTestCase::test_that_all_lines_are_returned_when_using_crlf
PASSED tests/test_socket.py::SocketStreamingTestCase::test_that_many_lines_are_returned
PASSED tests/test_socket.py::SocketStreamingTestCase::test_that_no_partial_artifacts_are_added_after_a_complete_line
PASSED tests/test_tag_block.py::TagBlockTestCase::test_multiple_messages
PASSED tests/test_tag_block.py::TagBlockTestCase::test_spire_maritime_format
PASSED tests/test_tag_block.py::TagBlockTestCase::test_tag_block_with_line_count
PASSED tests/test_tag_block.py::TagBlockTestCase::test_tag_block_with_multiple_unknown_fields
PASSED tests/test_tag_block.py::TagBlockTestCase::test_that_a_tag_block_is_lazily_evaluated
PASSED tests/test_tag_block.py::TagBlockTestCase::test_that_the_factory_is_gentle_with_malformed_tag_blocks
PASSED tests/test_tag_block.py::TagBlockTestCase::test_that_the_factory_pre_processes_correctly
PASSED tests/test_tag_block.py::TagBlockTestCase::test_that_the_factory_removes_the_leading_tag_block
PASSED tests/test_tag_block.py::TagBlockTestCase::test_that_unknown_tag_blocks_are_ignored
PASSED tests/test_tag_block.py::TagBlockTestCase::test_that_unknown_tag_blocks_can_exported_as_dicts
PASSED tests/test_talker_ids.py::TestTalkerIds::test_ab
PASSED tests/test_talker_ids.py::TestTalkerIds::test_ad
PASSED tests/test_talker_ids.py::TestTalkerIds::test_ai
PASSED tests/test_talker_ids.py::TestTalkerIds::test_an
PASSED tests/test_talker_ids.py::TestTalkerIds::test_ar
PASSED tests/test_talker_ids.py::TestTalkerIds::test_as
PASSED tests/test_talker_ids.py::TestTalkerIds::test_at
PASSED tests/test_talker_ids.py::TestTalkerIds::test_ax
PASSED tests/test_talker_ids.py::TestTalkerIds::test_bs
PASSED tests/test_talker_ids.py::TestTalkerIds::test_sa
PASSED tests/test_tcp_stream.py::TestTCPStream::test_default_buf_size
PASSED tests/test_tcp_stream.py::TestTCPStream::test_invalid_endpoint
PASSED tests/test_tracker.py::TrackerTestCase::test_that_clean_up_deletes_tracks_if_they_expire
PASSED tests/test_tracker.py::TrackerTestCase::test_that_clean_up_does_nothing_if_ttl_is_none
PASSED tests/test_tracker.py::TrackerTestCase::test_that_get_track_returns_track_if_it_exists
PASSED tests/test_tracker.py::TrackerTestCase::test_that_insert_or_update_sets_oldest_timestamp
PASSED tests/test_tracker.py::TrackerTestCase::test_that_n_latest_returns_empty_list_for_empty
PASSED tests/test_tracker.py::TrackerTestCase::test_that_n_latest_returns_tracks_in_correct_order
PASSED tests/test_tracker.py::TrackerTestCase::test_that_ordered_tracker_handles_cleanup
PASSED tests/test_tracker.py::TrackerTestCase::test_that_ordered_tracker_raises_value_error_if_older_record_is_received
PASSED tests/test_tracker.py::TrackerTestCase::test_that_ordered_tracker_returns_correct_latest_n
PASSED tests/test_tracker.py::TrackerTestCase::test_that_pop_tracks_returns_and_deletes_track_if_it_exists
PASSED tests/test_tracker.py::TrackerTestCase::test_that_poplast_is_non_destructive
PASSED tests/test_tracker.py::TrackerTestCase::test_that_tracker_works_as_context_manager
PASSED tests/test_tracker.py::TrackerTestCase::test_that_tracks_property_returns_tracks_as_list
PASSED tests/test_tracker.py::TrackerTestCase::test_that_update_always_updates_all_fields
PASSED tests/test_tracker.py::TrackerTestCase::test_that_update_cleans_up_afterwards
PASSED tests/test_tracker.py::TrackerTestCase::test_that_update_raises_error_is_timestamp_is_younger
PASSED tests/test_udp_stream.py::TestOutOfOrder::test_stream
SKIPPED [1] tests/test_file_stream.py:154: Takes too long
SKIPPED [1] tests/test_tcp_stream.py:55: Skip for now, because there is a Threading problem
======================== 300 passed, 2 skipped in 1.14s ========================
Test errors Included
Test report (JUnit) Included
<?xml version="1.0" encoding="utf-8"?><testsuites><testsuite name="pytest" errors="0" failures="0" skipped="2" tests="302" time="1.130" timestamp="2026-11-14T04:14:22.959031+00:00" hostname="9c0547ef7ed4"><testcase classname="tests.test_constants.TestConstants" name="test_maneuver_indication" time="0.000" /><testcase classname="tests.test_constants.TestConstants" name="test_nav_status" time="0.000" /><testcase classname="tests.test_constants.TestConstants" name="test_navaid" time="0.000" /><testcase classname="tests.test_constants.TestConstants" name="test_ship_types" time="0.000" /><testcase classname="tests.test_constants.TestConstants" name="test_station_intervals" time="0.000" /><testcase classname="tests.test_constants.TestConstants" name="test_station_types" time="0.000" /><testcase classname="tests.test_constants.TestConstants" name="test_t_mode" time="0.000" /><testcase classname="tests.test_constants.TestConstants" name="test_that_nav_data_values_are_kept_for_all_values" time="0.001" /><testcase classname="tests.test_decode.TestAIS" name="test_b64encode_str" time="0.000" /><testcase classname="tests.test_decode.TestAIS" name="test_b64encode_str_empty" time="0.000" /><testcase classname="tests.test_decode.TestAIS" name="test_bits2bytes" time="0.000" /><testcase classname="tests.test_decode.TestAIS" name="test_broken_messages" time="0.000" /><testcase classname="tests.test_decode.TestAIS" name="test_byte_stream" time="0.000" /><testcase classname="tests.test_decode.TestAIS" name="test_bytes2bits" time="0.000" /><testcase classname="tests.test_decode.TestAIS" name="test_common_invalid_inputs_to_the_decode_function" time="0.000" /><testcase classname="tests.test_decode.TestAIS" name="test_decode_1_speed" time="0.000" /><testcase classname="tests.test_decode.TestAIS" name="test_decode_and_merge" time="0.000" /><testcase classname="tests.test_decode.TestAIS" name="test_decode_does_not_raise_an_error_by_default" time="0.000" /><testcase classname="tests.test_decode.TestAIS" name="test_decode_does_not_raise_an_error_if_error_if_checksum_invalid_is_false" time="0.000" /><testcase classname="tests.test_decode.TestAIS" name="test_decode_does_raise_an_error_if_error_if_checksum_invalid_is_true" time="0.000" /><testcase classname="tests.test_decode.TestAIS" name="test_decode_into_bit_array_with_non_printable_characters" time="0.000" /><testcase classname="tests.test_decode.TestAIS" name="test_decode_out_of_order" time="0.000" /><testcase classname="tests.test_decode.TestAIS" name="test_decode_pos_1_2_3" time="0.000" /><testcase classname="tests.test_decode.TestAIS" name="test_empty_channel" time="0.000" /><testcase classname="tests.test_decode.TestAIS" name="test_get_comm_state_type_18_itdma_base_indirect" time="0.000" /><testcase classname="tests.test_decode.TestAIS" name="test_get_comm_state_type_18_sotdma_base_inidrect" time="0.000" /><testcase classname="tests.test_decode.TestAIS" name="test_get_comm_state_type_18_sotdma_utc_direct" time="0.000" /><testcase classname="tests.test_decode.TestAIS" name="test_get_sotdma_comm_state_utc_direct" time="0.000" /><testcase classname="tests.test_decode.TestAIS" name="test_get_sotdma_comm_state_utc_direct_slot_number" time="0.000" /><testcase classname="tests.test_decode.TestAIS" name="test_get_sotdma_comm_state_utc_direct_slot_timeout" time="0.000" /><testcase classname="tests.test_decode.TestAIS" name="test_gh_ais_message_decode" time="0.000" /><testcase classname="tests.test_decode.TestAIS" name="test_invalid_timestamp_message" time="0.000" /><testcase classname="tests.test_decode.TestAIS" name="test_is_sotdma_or_itdma" time="0.001" /><testcase classname="tests.test_decode.TestAIS" name="test_issue_46_a" time="0.000" /><testcase classname="tests.test_decode.TestAIS" name="test_issue_46_b" time="0.000" /><testcase classname="tests.test_decode.TestAIS" name="test_issue_50" time="0.000" /><testcase classname="tests.test_decode.TestAIS" name="test_issue_88" time="0.000" /><testcase classname="tests.test_decode.TestAIS" name="test_messages_with_proprietary_suffix" time="0.000" /><testcase classname="tests.test_decode.TestAIS" name="test_msg_too_short_enum_is_none" time="0.000" /><testcase classname="tests.test_decode.TestAIS" name="test_msg_type" time="0.000" /><testcase classname="tests.test_decode.TestAIS" name="test_msg_type_10_a" time="0.000" /><testcase classname="tests.test_decode.TestAIS" name="test_msg_type_10_b" time="0.000" /><testcase classname="tests.test_decode.TestAIS" name="test_msg_type_11" time="0.000" /><testcase classname="tests.test_decode.TestAIS" name="test_msg_type_12_a" time="0.000" /><testcase classname="tests.test_decode.TestAIS" name="test_msg_type_12_b" time="0.000" /><testcase classname="tests.test_decode.TestAIS" name="test_msg_type_13" time="0.000" /><testcase classname="tests.test_decode.TestAIS" name="test_msg_type_14" time="0.000" /><testcase classname="tests.test_decode.TestAIS" name="test_msg_type_15_a" time="0.000" /><testcase classname="tests.test_decode.TestAIS" name="test_msg_type_15_b" time="0.000" /><testcase classname="tests.test_decode.TestAIS" name="test_msg_type_16" time="0.000" /><testcase classname="tests.test_decode.TestAIS" name="test_msg_type_17_a" time="0.000" /><testcase classname="tests.test_decode.TestAIS" name="test_msg_type_17_b" time="0.000" /><testcase classname="tests.test_decode.TestAIS" name="test_msg_type_17_to_json" time="0.000" /><testcase classname="tests.test_decode.TestAIS" name="test_msg_type_18" time="0.000" /><testcase classname="tests.test_decode.TestAIS" name="test_msg_type_18_speed" time="0.000" /><testcase classname="tests.test_decode.TestAIS" name="test_msg_type_19" time="0.000" /><testcase classname="tests.test_decode.TestAIS" name="test_msg_type_1_a" time="0.000" /><testcase classname="tests.test_decode.TestAIS" name="test_msg_type_1_b" time="0.000" /><testcase classname="tests.test_decode.TestAIS" name="test_msg_type_1_c" time="0.000" /><testcase classname="tests.test_decode.TestAIS" name="test_msg_type_20" time="0.000" /><testcase classname="tests.test_decode.TestAIS" name="test_msg_type_21" time="0.000" /><testcase classname="tests.test_decode.TestAIS" name="test_msg_type_22_addressed" time="0.000" /><testcase classname="tests.test_decode.TestAIS" name="test_msg_type_22_broadcast" time="0.000" /><testcase classname="tests.test_decode.TestAIS" name="test_msg_type_23" time="0.000" /><testcase classname="tests.test_decode.TestAIS" name="test_msg_type_24" time="0.000" /><testcase classname="tests.test_decode.TestAIS" name="test_msg_type_24_with_160_bits" time="0.000" /><testcase classname="tests.test_decode.TestAIS" name="test_msg_type_24_with_168_bits" time="0.000" /><testcase classname="tests.test_decode.TestAIS" name="test_msg_type_25_a" time="0.000" /><testcase classname="tests.test_decode.TestAIS" name="test_msg_type_25_b" time="0.000" /><testcase classname="tests.test_decode.TestAIS" name="test_msg_type_25_c" time="0.000" /><testcase classname="tests.test_decode.TestAIS" name="test_msg_type_25_to_json" time="0.000" /><testcase classname="tests.test_decode.TestAIS" name="test_msg_type_26_a" time="0.000" /><testcase classname="tests.test_decode.TestAIS" name="test_msg_type_26_b" time="0.000" /><testcase classname="tests.test_decode.TestAIS" name="test_msg_type_26_to_json" time="0.000" /><testcase classname="tests.test_decode.TestAIS" name="test_msg_type_27" time="0.000" /><testcase classname="tests.test_decode.TestAIS" name="test_msg_type_27_signed" time="0.000" /><testcase classname="tests.test_decode.TestAIS" name="test_msg_type_3" time="0.000" /><testcase classname="tests.test_decode.TestAIS" name="test_msg_type_4_a" time="0.000" /><testcase classname="tests.test_decode.TestAIS" name="test_msg_type_4_b" time="0.000" /><testcase classname="tests.test_decode.TestAIS" name="test_msg_type_5" time="0.000" /><testcase classname="tests.test_decode.TestAIS" name="test_msg_type_6" time="0.000" /><testcase classname="tests.test_decode.TestAIS" name="test_msg_type_6_json_reverse" time="0.000" /><testcase classname="tests.test_decode.TestAIS" name="test_msg_type_6_to_json" time="0.000" /><testcase classname="tests.test_decode.TestAIS" name="test_msg_type_7" time="0.000" /><testcase classname="tests.test_decode.TestAIS" name="test_msg_type_8" time="0.000" /><testcase classname="tests.test_decode.TestAIS" name="test_msg_type_8_multipart" time="0.000" /><testcase classname="tests.test_decode.TestAIS" name="test_msg_type_8_to_json" time="0.000" /><testcase classname="tests.test_decode.TestAIS" name="test_msg_type_9" time="0.000" /><testcase classname="tests.test_decode.TestAIS" name="test_msg_with_more_that_82_chars_payload" time="0.000" /><testcase classname="tests.test_decode.TestAIS" name="test_multiline_message" time="0.000" /><testcase classname="tests.test_decode.TestAIS" name="test_nmea_decode" time="0.000" /><testcase classname="tests.test_decode.TestAIS" name="test_nmea_decode_unknown_msg" time="0.000" /><testcase classname="tests.test_decode.TestAIS" name="test_none_value_converter_for_creation" time="0.000" /><testcase classname="tests.test_decode.TestAIS" name="test_none_value_converter_for_decoding" time="0.000" /><testcase classname="tests.test_decode.TestAIS" name="test_none_values_converter_for_all_messages" time="0.001" /><testcase classname="tests.test_decode.TestAIS" name="test_rot_decode_yields_expected_values" time="0.000" /><testcase classname="tests.test_decode.TestAIS" name="test_rot_encode_decode" time="0.000" /><testcase classname="tests.test_decode.TestAIS" name="test_rot_encode_yields_expected_values" time="0.000" /><testcase classname="tests.test_decode.TestAIS" name="test_sotdma_time_conversion" time="0.000" /><testcase classname="tests.test_decode.TestAIS" name="test_special_position_report" time="0.000" /><testcase classname="tests.test_decode.TestAIS" name="test_static_data_report" time="0.000" /><testcase classname="tests.test_decode.TestAIS" name="test_that_decode_nmea_and_ais_works_with_proprietary_messages" time="0.000" /><testcase classname="tests.test_decode.TestAIS" name="test_that_lat_and_long_are_rounded_correctly" time="0.000" /><testcase classname="tests.test_decode.TestAIS" name="test_that_the_payload_does_not_change_when_encoding_decoding" time="0.000" /><testcase classname="tests.test_decode.TestAIS" name="test_timestamp_message" time="0.000" /><testcase classname="tests.test_decode.TestAIS" name="test_to_dict_non_enum" time="0.000" /><testcase classname="tests.test_decode.TestAIS" name="test_to_json" time="0.000" /><testcase classname="tests.test_decode.TestAIS" name="test_type_22_very_short" time="0.000" /><testcase classname="tests.test_decode.TestAIS" name="test_type_25_very_short" time="0.000" /><testcase classname="tests.test_decode.TestAIS" name="test_type_26_very_short" time="0.000" /><testcase classname="tests.test_decode.TestAIS" name="test_types_for_messages" time="0.000" /><testcase classname="tests.test_decode_raw.TestDecode" name="test_bytes_invalid" time="0.000" /><testcase classname="tests.test_decode_raw.TestDecode" name="test_bytes_valid" time="0.000" /><testcase classname="tests.test_decode_raw.TestDecode" name="test_decode_multiline_message" time="0.000" /><testcase classname="tests.test_decode_raw.TestDecode" name="test_decode_total_garbage" time="0.000" /><testcase classname="tests.test_decode_raw.TestDecode" name="test_multipart_error_message" time="0.000" /><testcase classname="tests.test_decode_raw.TestDecode" name="test_str_invalid" time="0.000" /><testcase classname="tests.test_decode_raw.TestDecode" name="test_str_valid" time="0.000" /><testcase classname="tests.test_decode_raw.TestDecode" name="test_too_many_messages" time="0.000" /><testcase classname="tests.test_encode" name="test_widths" time="0.000" /><testcase classname="tests.test_encode" name="test_variable_message_length_width" time="0.000" /><testcase classname="tests.test_encode" name="test_encode_msg_table" time="0.000" /><testcase classname="tests.test_encode" name="test_invalid_talker_id" time="0.000" /><testcase classname="tests.test_encode" name="test_encode_payload_invalid_talker_id" time="0.000" /><testcase classname="tests.test_encode" name="test_invalid_radio_channel" time="0.000" /><testcase classname="tests.test_encode" name="test_encode_payload_error_radio" time="0.000" /><testcase classname="tests.test_encode" name="test_data_to_payload" time="0.000" /><testcase classname="tests.test_encode" name="test_get_ais_type" time="0.000" /><testcase classname="tests.test_encode" name="test_str_to_bin" time="0.000" /><testcase classname="tests.test_encode" name="test_int_to_bin" time="0.000" /><testcase classname="tests.test_encode" name="test_decode_encode" time="0.002" /><testcase classname="tests.test_encode" name="test_encode_type_27" time="0.000" /><testcase classname="tests.test_encode" name="test_encode_type_26" time="0.000" /><testcase classname="tests.test_encode" name="test_encode_type_25_b" time="0.000" /><testcase classname="tests.test_encode" name="test_encode_type_25_a" time="0.000" /><testcase classname="tests.test_encode" name="test_encode_type_24_partno_invalid" time="0.000" /><testcase classname="tests.test_encode" name="test_encode_type_24_a" time="0.000" /><testcase classname="tests.test_encode" name="test_encode_type_24_b" time="0.000" /><testcase classname="tests.test_encode" name="test_encode_type_23" time="0.000" /><testcase classname="tests.test_encode" name="test_encode_type_22_b" time="0.000" /><testcase classname="tests.test_encode" name="test_encode_type_22_a" time="0.000" /><testcase classname="tests.test_encode" name="test_encode_type_21" time="0.000" /><testcase classname="tests.test_encode" name="test_encode_type_20" time="0.000" /><testcase classname="tests.test_encode" name="test_encode_type_19" time="0.000" /><testcase classname="tests.test_encode" name="test_encode_type_18_with_speed_and_course" time="0.000" /><testcase classname="tests.test_encode" name="test_encode_type_18" time="0.000" /><testcase classname="tests.test_encode" name="test_encode_type_17_b" time="0.000" /><testcase classname="tests.test_encode" name="test_encode_type_17_a" time="0.000" /><testcase classname="tests.test_encode" name="test_encode_type_16" time="0.000" /><testcase classname="tests.test_encode" name="test_encode_type_15_a" time="0.000" /><testcase classname="tests.test_encode" name="test_encode_type_15" time="0.000" /><testcase classname="tests.test_encode" name="test_encode_type_14" time="0.000" /><testcase classname="tests.test_encode" name="test_encode_type_13" time="0.000" /><testcase classname="tests.test_encode" name="test_encode_type_12_with_some_text" time="0.000" /><testcase classname="tests.test_encode" name="test_encode_type_12_with_no_text" time="0.000" /><testcase classname="tests.test_encode" name="test_encode_type_12_with_empty_text" time="0.000" /><testcase classname="tests.test_encode" name="test_encode_type_12_with_max_length_text" time="0.001" /><testcase classname="tests.test_encode" name="test_encode_type_12_with_longer_than_max_length_length_text" time="0.000" /><testcase classname="tests.test_encode" name="test_encode_type_11" time="0.000" /><testcase classname="tests.test_encode" name="test_encode_type_10" time="0.000" /><testcase classname="tests.test_encode" name="test_encode_type_9" time="0.000" /><testcase classname="tests.test_encode" name="test_encode_type_8" time="0.000" /><testcase classname="tests.test_encode" name="test_encode_type_7" time="0.000" /><testcase classname="tests.test_encode" name="test_encode_type_6_bytes" time="0.000" /><testcase classname="tests.test_encode" name="test_encode_type_6" time="0.000" /><testcase classname="tests.test_encode" name="test_encode_type_4" time="0.000" /><testcase classname="tests.test_encode" name="test_encode_type_5_issue_59" time="0.000" /><testcase classname="tests.test_encode" name="test_encode_type_5" time="0.000" /><testcase classname="tests.test_encode" name="test_encode_type_5_default" time="0.000" /><testcase classname="tests.test_encode" name="test_encode_msg_type2" time="0.000" /><testcase classname="tests.test_encode" name="test_encode_msg_type_3" time="0.000" /><testcase classname="tests.test_encode" name="test_encode_type_1_default" time="0.000" /><testcase classname="tests.test_encode" name="test_encode_type_1" time="0.000" /><testcase classname="tests.test_encode" name="test_mmsi_too_long" time="0.000" /><testcase classname="tests.test_encode" name="test_lon_too_large" time="0.000" /><testcase classname="tests.test_encode" name="test_ship_name_too_lon" time="0.000" /><testcase classname="tests.test_encode" name="test_int_to_bytes" time="0.000" /><testcase classname="tests.test_encode" name="test_to_six_bit" time="0.000" /><testcase classname="tests.test_encode" name="test_encode_ascii_6_bit" time="0.000" /><testcase classname="tests.test_encode" name="test_encode_does_not_exceed_nmea_sentence_length_limit" time="0.000" /><testcase classname="tests.test_examples.TestExamples" name="test_run_every_file" time="0.665" /><testcase classname="tests.test_file_stream.TestFileReaderStream" name="test_invalid_filename" time="0.000" /><testcase classname="tests.test_file_stream.TestFileReaderStream" name="test_large_file" time="0.000"><skipped type="pytest.skip" message="Takes too long">/testbed/tests/test_file_stream.py:154: Takes too long</skipped></testcase><testcase classname="tests.test_file_stream.TestFileReaderStream" name="test_marine_traffic_sample" time="0.020" /><testcase classname="tests.test_file_stream.TestFileReaderStream" name="test_mixed_content" time="0.000" /><testcase classname="tests.test_file_stream.TestFileReaderStream" name="test_nmea_sort_index_error" time="0.000" /><testcase classname="tests.test_file_stream.TestFileReaderStream" name="test_nmea_sort_invalid_frag_cnt" time="0.000" /><testcase classname="tests.test_file_stream.TestFileReaderStream" name="test_nmea_sorter_sorted" time="0.000" /><testcase classname="tests.test_file_stream.TestFileReaderStream" name="test_nmea_sorter_unsorted" time="0.000" /><testcase classname="tests.test_file_stream.TestFileReaderStream" name="test_reader" time="0.000" /><testcase classname="tests.test_file_stream.TestFileReaderStream" name="test_reader_with_open" time="0.000" /><testcase classname="tests.test_file_stream.TestFileReaderStream" name="test_sort_with_different_msg_ids" time="0.000" /><testcase classname="tests.test_file_stream.TestFileReaderStream" name="test_timestamp_messages" time="0.000" /><testcase classname="tests.test_filters.TestNoneFilter" name="test_filtering_missing_attributes" time="0.000" /><testcase classname="tests.test_filters.TestNoneFilter" name="test_filtering_none_attributes" time="0.000" /><testcase classname="tests.test_filters.TestMessageTypeFilter" name="test_message_type_filtering" time="0.000" /><testcase classname="tests.test_filters.TestDistanceFilter" name="test_all_within_distance" time="0.000" /><testcase classname="tests.test_filters.TestDistanceFilter" name="test_distance_filtering" time="0.000" /><testcase classname="tests.test_filters.TestDistanceFilter" name="test_none_within_distance" time="0.000" /><testcase classname="tests.test_filters.TestDistanceFilter" name="test_specific_coordinate_distance" time="0.000" /><testcase classname="tests.test_filters.TestGridFilter" name="test_central_park_grid" time="0.000" /><testcase classname="tests.test_filters.TestGridFilter" name="test_grid_filtering" time="0.000" /><testcase classname="tests.test_filters.TestFilterChain" name="test_complex_filter_chain" time="0.000" /><testcase classname="tests.test_filters.TestFilterChain" name="test_filter_chain" time="0.000" /><testcase classname="tests.test_filters.TestAttributeFilter" name="test_attribute_filtering" time="0.000" /><testcase classname="tests.test_filters.TestHaversine" name="test_london_to_paris" time="0.000" /><testcase classname="tests.test_filters.TestHaversine" name="test_nyc_to_la" time="0.000" /><testcase classname="tests.test_filters.TestHaversine" name="test_sydney_to_melbourne" time="0.000" /><testcase classname="tests.test_flags.MMSICountryCodeTestCase" name="test_get_three_digits" time="0.000" /><testcase classname="tests.test_flags.MMSICountryCodeTestCase" name="test_random_ship_1" time="0.000" /><testcase classname="tests.test_flags.MMSICountryCodeTestCase" name="test_random_ship_2" time="0.000" /><testcase classname="tests.test_flags.MMSICountryCodeTestCase" name="test_random_ship_3" time="0.000" /><testcase classname="tests.test_flags.MMSICountryCodeTestCase" name="test_random_ship_4" time="0.000" /><testcase classname="tests.test_flags.MMSICountryCodeTestCase" name="test_random_ship_5" time="0.000" /><testcase classname="tests.test_flags.MMSICountryCodeTestCase" name="test_random_ship_6" time="0.000" /><testcase classname="tests.test_flags.MMSICountryCodeTestCase" name="test_random_ship_7" time="0.000" /><testcase classname="tests.test_flags.MMSICountryCodeTestCase" name="test_random_ship_8" time="0.000" /><testcase classname="tests.test_generic_stream.TestGenericStream" name="test_empty_stream" time="0.000" /><testcase classname="tests.test_generic_stream.TestGenericStream" name="test_garbage_stream" time="0.000" /><testcase classname="tests.test_generic_stream.TestGenericStream" name="test_invalid_msg" time="0.000" /><testcase classname="tests.test_generic_stream.TestIterMessages" name="test_init_from_bytes_transforms_bytes_to_list" time="0.000" /><testcase classname="tests.test_generic_stream.TestIterMessages" name="test_init_from_bytes_with_multiple_messages" time="0.000" /><testcase classname="tests.test_generic_stream.TestIterMessages" name="test_init_from_ignores_error_if_enabled" time="0.000" /><testcase classname="tests.test_generic_stream.TestIterMessages" name="test_init_from_str" time="0.000" /><testcase classname="tests.test_generic_stream.TestIterMessages" name="test_init_from_str_throws_error_if_invalid" time="0.000" /><testcase classname="tests.test_generic_stream.TestIterMessages" name="test_iter_is_generator" time="0.000" /><testcase classname="tests.test_generic_stream.TestIterMessages" name="test_iter_messages_does_assemble_multiline_messages" time="0.000" /><testcase classname="tests.test_generic_stream.TestIterMessages" name="test_iter_messages_handles_single_message" time="0.000" /><testcase classname="tests.test_main.TestMainApp" name="test_decode_from_file" time="0.001" /><testcase classname="tests.test_main.TestMainApp" name="test_decode_single" time="0.000" /><testcase classname="tests.test_main.TestMainApp" name="test_decode_single_multi" time="0.000" /><testcase classname="tests.test_main.TestMainApp" name="test_parser" time="0.002" /><testcase classname="tests.test_nmea.TestNMEA" name="test_attrs" time="0.000" /><testcase classname="tests.test_nmea.TestNMEA" name="test_chk_to_int_with_fill_bits" time="0.000" /><testcase classname="tests.test_nmea.TestNMEA" name="test_chk_to_int_with_missing_checksum" time="0.000" /><testcase classname="tests.test_nmea.TestNMEA" name="test_chk_to_int_with_missing_fill_bits" time="0.000" /><testcase classname="tests.test_nmea.TestNMEA" name="test_chk_to_int_with_valid_checksum" time="0.000" /><testcase classname="tests.test_nmea.TestNMEA" name="test_dict" time="0.000" /><testcase classname="tests.test_nmea.TestNMEA" name="test_from_bytes" time="0.000" /><testcase classname="tests.test_nmea.TestNMEA" name="test_from_str" time="0.000" /><testcase classname="tests.test_nmea.TestNMEA" name="test_get_item" time="0.000" /><testcase classname="tests.test_nmea.TestNMEA" name="test_get_item_raises_key_error" time="0.000" /><testcase classname="tests.test_nmea.TestNMEA" name="test_get_item_raises_type_error" time="0.000" /><testcase classname="tests.test_nmea.TestNMEA" name="test_message_assembling" time="0.000" /><testcase classname="tests.test_nmea.TestNMEA" name="test_message_eq_method" time="0.000" /><testcase classname="tests.test_nmea.TestNMEA" name="test_missing_checksum" time="0.000" /><testcase classname="tests.test_nmea.TestNMEA" name="test_single" time="0.000" /><testcase classname="tests.test_nmea.TestNMEA" name="test_talker" time="0.000" /><testcase classname="tests.test_nmea.TestNMEA" name="test_that_a_valid_checksum_is_correctly_identified" time="0.000" /><testcase classname="tests.test_nmea.TestNMEA" name="test_that_a_valid_checksum_is_correctly_identified_for_multi_part_msgs" time="0.000" /><testcase classname="tests.test_nmea.TestNMEA" name="test_that_an_invalid_checksum_is_correctly_identified" time="0.000" /><testcase classname="tests.test_nmea.TestNMEA" name="test_that_an_invalid_checksum_is_correctly_identified_for_multi_part_msgs" time="0.000" /><testcase classname="tests.test_nmea.TestNMEA" name="test_type" time="0.000" /><testcase classname="tests.test_nmea.TestNMEA" name="test_validity" time="0.000" /><testcase classname="tests.test_nmea.TestNMEA" name="test_values" time="0.000" /><testcase classname="tests.test_nmea.TestNMEA" name="test_wrong_type" time="0.000" /><testcase classname="tests.test_socket.SocketStreamingTestCase" name="test_that_a_single_line_is_returned_as_is" time="0.000" /><testcase classname="tests.test_socket.SocketStreamingTestCase" name="test_that_all_lines_are_returned" time="0.000" /><testcase classname="tests.test_socket.SocketStreamingTestCase" name="test_that_all_lines_are_returned_for_incomplete_lines" time="0.000" /><testcase classname="tests.test_socket.SocketStreamingTestCase" name="test_that_all_lines_are_returned_when_using_crlf" time="0.000" /><testcase classname="tests.test_socket.SocketStreamingTestCase" name="test_that_many_lines_are_returned" time="0.000" /><testcase classname="tests.test_socket.SocketStreamingTestCase" name="test_that_no_partial_artifacts_are_added_after_a_complete_line" time="0.000" /><testcase classname="tests.test_tag_block.TagBlockTestCase" name="test_multiple_messages" time="0.000" /><testcase classname="tests.test_tag_block.TagBlockTestCase" name="test_spire_maritime_format" time="0.001" /><testcase classname="tests.test_tag_block.TagBlockTestCase" name="test_tag_block_with_line_count" time="0.000" /><testcase classname="tests.test_tag_block.TagBlockTestCase" name="test_tag_block_with_multiple_unknown_fields" time="0.000" /><testcase classname="tests.test_tag_block.TagBlockTestCase" name="test_that_a_tag_block_is_lazily_evaluated" time="0.000" /><testcase classname="tests.test_tag_block.TagBlockTestCase" name="test_that_the_factory_is_gentle_with_malformed_tag_blocks" time="0.000" /><testcase classname="tests.test_tag_block.TagBlockTestCase" name="test_that_the_factory_pre_processes_correctly" time="0.000" /><testcase classname="tests.test_tag_block.TagBlockTestCase" name="test_that_the_factory_removes_the_leading_tag_block" time="0.000" /><testcase classname="tests.test_tag_block.TagBlockTestCase" name="test_that_unknown_tag_blocks_are_ignored" time="0.000" /><testcase classname="tests.test_tag_block.TagBlockTestCase" name="test_that_unknown_tag_blocks_can_exported_as_dicts" time="0.000" /><testcase classname="tests.test_talker_ids.TestTalkerIds" name="test_ab" time="0.000" /><testcase classname="tests.test_talker_ids.TestTalkerIds" name="test_ad" time="0.000" /><testcase classname="tests.test_talker_ids.TestTalkerIds" name="test_ai" time="0.000" /><testcase classname="tests.test_talker_ids.TestTalkerIds" name="test_an" time="0.000" /><testcase classname="tests.test_talker_ids.TestTalkerIds" name="test_ar" time="0.000" /><testcase classname="tests.test_talker_ids.TestTalkerIds" name="test_as" time="0.000" /><testcase classname="tests.test_talker_ids.TestTalkerIds" name="test_at" time="0.000" /><testcase classname="tests.test_talker_ids.TestTalkerIds" name="test_ax" time="0.000" /><testcase classname="tests.test_talker_ids.TestTalkerIds" name="test_bs" time="0.000" /><testcase classname="tests.test_talker_ids.TestTalkerIds" name="test_sa" time="0.000" /><testcase classname="tests.test_tcp_stream.TestTCPStream" name="test_default_buf_size" time="0.000" /><testcase classname="tests.test_tcp_stream.TestTCPStream" name="test_invalid_endpoint" time="0.000" /><testcase classname="tests.test_tcp_stream.TestTCPStream" name="test_tcp_stream" time="0.000"><skipped type="pytest.skip" message="Skip for now, because there is a Threading problem">/testbed/tests/test_tcp_stream.py:55: Skip for now, because there is a Threading problem</skipped></testcase><testcase classname="tests.test_tracker.TrackerTestCase" name="test_that_clean_up_deletes_tracks_if_they_expire" time="0.000" /><testcase classname="tests.test_tracker.TrackerTestCase" name="test_that_clean_up_does_nothing_if_ttl_is_none" time="0.000" /><testcase classname="tests.test_tracker.TrackerTestCase" name="test_that_get_track_returns_track_if_it_exists" time="0.000" /><testcase classname="tests.test_tracker.TrackerTestCase" name="test_that_insert_or_update_sets_oldest_timestamp" time="0.000" /><testcase classname="tests.test_tracker.TrackerTestCase" name="test_that_n_latest_returns_empty_list_for_empty" time="0.000" /><testcase classname="tests.test_tracker.TrackerTestCase" name="test_that_n_latest_returns_tracks_in_correct_order" time="0.000" /><testcase classname="tests.test_tracker.TrackerTestCase" name="test_that_ordered_tracker_handles_cleanup" time="0.000" /><testcase classname="tests.test_tracker.TrackerTestCase" name="test_that_ordered_tracker_raises_value_error_if_older_record_is_received" time="0.000" /><testcase classname="tests.test_tracker.TrackerTestCase" name="test_that_ordered_tracker_returns_correct_latest_n" time="0.000" /><testcase classname="tests.test_tracker.TrackerTestCase" name="test_that_pop_tracks_returns_and_deletes_track_if_it_exists" time="0.000" /><testcase classname="tests.test_tracker.TrackerTestCase" name="test_that_poplast_is_non_destructive" time="0.000" /><testcase classname="tests.test_tracker.TrackerTestCase" name="test_that_tracker_works_as_context_manager" time="0.000" /><testcase classname="tests.test_tracker.TrackerTestCase" name="test_that_tracks_property_returns_tracks_as_list" time="0.000" /><testcase classname="tests.test_tracker.TrackerTestCase" name="test_that_update_always_updates_all_fields" time="0.000" /><testcase classname="tests.test_tracker.TrackerTestCase" name="test_that_update_cleans_up_afterwards" time="0.000" /><testcase classname="tests.test_tracker.TrackerTestCase" name="test_that_update_raises_error_is_timestamp_is_younger" time="0.000" /><testcase classname="tests.test_udp_stream.TestOutOfOrder" name="test_stream" time="0.101" /></testsuite></testsuites>
Agent log Included
[ 0.0s] 📋 System Prompt: You are a principal Python software engineer resolving a repository issue in `/workspace`.
[ 0.0s] 🎯 Task Prompt: You are evaluating a software engineering task for repository M0r13n/pyais.
[ 16.9s] 💭 (664 chars) "The user wants to fix a bug in `pyais` regarding the determination of communication status
(SOTDMA vs ITDMA).
The proble..."
[ 16.9s] 🔧 run_command grep -rn --include="*.py" -e "is_sotdma" -e "is_itdma" -e "MAX_COMM_STATE_VALUE"
[ 17.0s] → pyais/messages.py:802: MAX_COMM_STATE_VALUE = 0x7ffff
[ 27.1s] 💭 (182 chars) "The properties `is_sotdma` and `is_itdma` are defined in `pyais/messages.py`.
The `MAX_COMM_STATE_VALUE` is also defined..."
[ 27.1s] 📖 read_file (pyais/messages.py)
[ 27.1s] ✓ {'status': 'ok', 'filepath': 'pyais/messages.py', 'content': '\n\nclass CommunicationStateMixin:\n
"""\n Mixin cla…
[ 51.5s] 💭 (1107 chars) "The `CommunicationStateMixin` is used by several message classes.
The problem description states: "It is better to use t..."
[ 51.5s] 🔧 run_command (8 lines) python3 - <<'PY'
[ 51.6s] → Message Type: 3
[ 76.1s] 💭 (1031 chars) "In the example, message type is 3.
The radio value is 21075 (0x526B).
`MAX_COMM_STATE_VALUE` is `0x7ffff` (524287).
`210..."
[ 76.1s] 🔧 run_command grep -rn "CommunicationStateMixin" pyais/messages.py
[ 76.2s] → 792:class CommunicationStateMixin:
[ 90.5s] 💭 (411 chars) "The types using `CommunicationStateMixin` are: 1, 4, 9, 18, 26.
The problem statement mentions types 9, 18, and 26 as ed..."
[ 90.5s] 🔧 run_command grep -E "class MessageType[1-4]" pyais/messages.py
[ 90.5s] → class MessageType1(Payload, CommunicationStateMixin):
[ 139.7s] 💭 (2342 chars) "`MessageType2` and `MessageType3` inherit from `MessageType1`, which inherits from
`CommunicationStateMixin`.
So we hav..."
[ 139.7s] 📖 read_file (pyais/messages.py)
[ 139.7s] ✓ {'status': 'ok', 'filepath': 'pyais/messages.py', 'content': 'class MessageType1(Payload,
CommunicationStateMixin):\n …
[ 179.8s] 💭 (1748 chars) "Aha! Look at the docstrings:
`MessageType1`: "AIS Vessel position report using SOTDMA"
`MessageType2`: "AIS Vessel posit..."
[ 179.8s] 🔧 run_command (3 lines) grep -n "class MessageType9" pyais/messages.py
[ 179.9s] → 996:class MessageType9(Payload, CommunicationStateMixin):
[ 183.6s] 💭 (56 chars) "Let's read the `radio` field definition for these types."
[ 183.6s] 🔧 run_command grep -n "radio =" pyais/messages.py
[ 183.6s] → 869: radio = bit_field(19, int, default=0, signed=False)
[ 243.1s] 💭 (2362 chars) "Okay!
Type 1 (line 869): 19 bits.
Type 4 (line 910): 19 bits.
Type 9 (line 1019): 20 bits.
Type 18 (line 1166): 20 bits...."
[ 243.1s] 🔧 run_command grep -n "radio =" pyais/messages.py
[ 243.1s] → 869: radio = bit_field(19, int, default=0, signed=False)
[ 443.7s] 💭 (10646 chars) "It seems all `radio` fields are covered by the mixin.
Wait, let's verify what types 1, 2, 3, 4 are exactly.
Type 1: SOT..."
[ 443.7s] ✏️ edit_file (pyais/messages.py)
[ 443.8s] ✓ {'status': 'ok', 'filepath': 'pyais/messages.py', 'occurrences': 1, 'strategy': 'exact', 'diff': '---
a/pyais/messages.p…
Native result row
{
"agent_error": "Agent exceeded session timeout (7.5 min)",
"duration_seconds": 507.03,
"error": "Agent exceeded session timeout (7.5 min)",
"grade": {
"collected_tests": 302,
"competition_junit_ok": true,
"error": null,
"required_issues_count": 0,
"resolved": true,
"status_counts": {
"PASSED": 300,
"SKIPPED": 2
},
"strict_clean_patch": true,
"test_exit_code": 0
},
"instance_id": "M0r13n__pyais-137",
"pass_idx": 0,
"patch_chars": 822,
"phase1_seconds": 504.89,
"repo": "M0r13n/pyais",
"request_log_path": "/kaggle-root/working/r31/rollouts_X/requests/M0r13n__pyais-137__p0__r3.jsonl",
"resolved": true,
"rollout_idx": 3,
"run_key": "M0r13n__pyais-137__p0__r3",
"strict_clean_patch": true,
"tool_calls": 10,
"total_llm_calls": 10,
"trace_path": "/kaggle-root/working/r31/rollouts_X/traces/trace_M0r13n__pyais-137__p0__r3.json"
}