Skip to content

Commit

Permalink
Update the docs for Cyphal/UDP and Cyphal/serial (#301)
Browse files Browse the repository at this point in the history
This is a trivial update to the docs to remove duplication with the
official Specification.
  • Loading branch information
pavel-kirienko committed Jun 3, 2023
1 parent de11875 commit 01b9a9b
Show file tree
Hide file tree
Showing 9 changed files with 27 additions and 358 deletions.
2 changes: 1 addition & 1 deletion pycyphal/_version.py
Original file line number Diff line number Diff line change
@@ -1 +1 @@
__version__ = "1.15.1"
__version__ = "1.15.2"
4 changes: 2 additions & 2 deletions pycyphal/application/_transport_factory.py
Original file line number Diff line number Diff line change
Expand Up @@ -52,7 +52,7 @@ def make_transport(
* - ``uavcan.udp.duplicate_service_transfers``
- ``bit[1]``
- Apply deterministic data loss mitigation to RPC-service transfers by setting multiplication factor = 2.
- Apply forward error correction to RPC-service transfers by setting multiplication factor = 2.
* - ``uavcan.udp.mtu``
- ``natural16[1]``
Expand All @@ -73,7 +73,7 @@ def make_transport(
* - ``uavcan.serial.duplicate_service_transfers``
- ``bit[1]``
- Apply deterministic data loss mitigation to RPC-service transfers by setting multiplication factor = 2.
- Apply forward error correction to RPC-service transfers by setting multiplication factor = 2.
* - ``uavcan.serial.baudrate``
- ``natural32[1]``
Expand Down
2 changes: 1 addition & 1 deletion pycyphal/transport/_transport.py
Original file line number Diff line number Diff line change
Expand Up @@ -207,7 +207,7 @@ def begin_capture(self, handler: CaptureCallback) -> None:
Technically, the capture protocol, as you can see, does not present any requirements to the emitted events,
so an implementation that pretends to enter the capture mode while not actually doing anything is compliant.
Since capture reflects actual network events, deterministic data loss mitigation will make the instance emit
Since capture reflects actual network events, FEC will make the instance emit
duplicate frames for affected transfers (although this is probably obvious enough without this elaboration).
It is not possible to disable capture. Once enabled, it will go on until the transport instance is destroyed.
Expand Down
2 changes: 1 addition & 1 deletion pycyphal/transport/redundant/_tracer.py
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,7 @@ def get_transport_type() -> typing.Type[pycyphal.transport.redundant.RedundantTr
class RedundantDuplicateTransferTrace(pycyphal.transport.Trace):
"""
Indicates that the last capture object completed a valid transfer that was discarded as a duplicate
(either received from another redundant interface or deterministic data loss mitigation (DDLM) is employed).
(either received from another redundant interface or forward error correction is employed).
Observe that it is NOT a subclass of :class:`pycyphal.transport.TransferTrace`!
It shall not be one because duplicates should not be processed normally.
Expand Down
110 changes: 10 additions & 100 deletions pycyphal/transport/serial/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,118 +3,28 @@
# Author: Pavel Kirienko <pavel@opencyphal.org>

"""
Cyphal/Serial transport overview
Cyphal/serial transport overview
++++++++++++++++++++++++++++++++
The Cyphal/Serial transport is designed for OSI L1 byte-level duplex serial links and tunnels:
The Cyphal/serial transport is designed for byte-level communication channels, such as:
- UART, RS-422/485/232 (duplex); the recommended rates are: 115200 bps, 921600 bps, 3 Mbps, 10 Mbps, 100 Mbps.
- USB CDC ACM.
- TCP/IP encapsulation.
- TCP/IP
- UART, RS-422/232
- USB CDC ACM
It may also be suited for raw transport log storage, because one-dimensional flat binary files are structurally
similar to serial byte-level links.
It may also be suited for raw transport log storage.
This transport module contains no media sublayers because the media abstraction
is handled directly by the `PySerial <https://pypi.org/project/pyserial>`_
library and the underlying operating system.
The serial transport supports all transfer categories:
For the full protocol definition, please refer to the `Cyphal Specification <https://opencyphal.org/specification>`_.
+--------------------+--------------------------+---------------------------+
| Supported transfers| Unicast | Broadcast |
+====================+==========================+===========================+
|**Message** | Yes, non-spec extension | Yes |
+--------------------+--------------------------+---------------------------+
|**Service** | Yes | Banned by Specification |
+--------------------+--------------------------+---------------------------+
Forward error correction (FEC)
++++++++++++++++++++++++++++++
Protocol definition
+++++++++++++++++++
The packet header is defined as follows (byte/bit ordering in this definition follow the DSDL specification:
least significant first)::
uint4 version # = 1, Discard the frame if not.
void4
uint3 priority # 0 = highest, 7 = lowest; the rest are unused.
void5
uint16 source_node_id # 0xFFFF = anonymous.
uint16 destination_node_id # 0xFFFF = broadcast.
uint16 data_specifier # subject-ID | (service-ID + RNR (Request, Not Response))
uint64 transfer_id
uint31 frame_index
bool end_of_transfer # Set if last frame of the transfer
uint16 user_data # Opaque application-specific data with user-defined semantics.
# Generic implementations should ignore
uint8[2] header_crc_be # CRC-16-CCITT of the header (all fields above).
# Most significant byte first.
For message frames, the data specifier field contains the subject-ID value,
so that the most significant bit is always cleared.
For service frames, the most significant bit (15th) is always set,
and the second-to-most-significant bit (14th) is set for request transfers only;
the remaining 14 least significant bits contain the service-ID value.
Total header size: 24 bytes (192 bits).
The header is prepended before the frame payload; the resulting structure is
encoded into its serialized form using the following packet format:
+-------------------------+--------------+---------------+--------------------------------+-------------------------+
| Frame delimiter **0x00**|Escaped header|Escaped payload| Escaped CRC-32C of the payload | Frame delimiter **0x00**|
+=========================+==============+===============+================================+=========================+
| 1 byte | 24 bytes | >=0 bytes | 4 bytes | 1 byte |
+-------------------------+--------------+---------------+--------------------------------+-------------------------+
| Single-byte frame | | Four bytes long, little-endian | Same frame delimiter as |
| delimiter **0x00**. | | byte order; The CRC is | at the start. |
| Begins a new frame and | | computed over the unescaped | Terminates the current |
| possibly terminates the | | (i.e., original form) payload, | frame and possibly |
| previous frame. | | not including the header | begins the next frame. |
| | | (because the header has a | |
| | | dedicated CRC). | |
| +------------------------------+--------------------------------+ |
| | This part is escaped using COBS alorithm by Chesire and Baker | |
| | http://www.stuartcheshire.org/papers/COBSforToN.pdf. | |
| | A frame delimiter (0) is guaranteed to never occur here. | |
+-------------------------+------------------------------+--------------------------------+-------------------------+
The frame encoding overhead is 1 byte in every 254 bytes of the header+payload+CRC, which is about ~0.4%.
There is a somewhat relevant discussion at
https://forum.opencyphal.org/t/uavcan-serial-issues-with-dma-friendliness-and-bandwidth-overhead/846.
The last four bytes of a multi-frame transfer payload contain the CRC32C (Castagnoli) hash of the transfer
payload in little-endian byte order.
The multi-frame transfer logic (decomposition and reassembly) is implemented in a separate
transport-agnostic module :mod:`pycyphal.transport.commons.high_overhead_transport`.
**Despite the fact that the support for multi-frame transfers is built into this transport,
it should not be relied on and it may be removed later.**
The reason is that serial links do not have native support for framing, and as such,
it is possible to configure the MTU to be arbitrarily high to avoid multi-frame transfers completely.
The lack of multi-frame transfers simplifies implementations drastically, which is important for
deeply-embedded systems. As such, all serial transfers should be single-frame transfers.
Note that we use CRC-32C (Castagnoli) as the header/frame CRC instead of CRC-32K2 (Koopman-2)
which is superior at short data blocks offering the Hamming distance of 6 as opposed to 4.
This is because Castagnoli is superior for transfer CRC which is often sufficiently long
to flip the balance in favor of Castagnoli rather than Koopman.
We could use Koopman for the header/frame CRC and keep Castagnoli for the transfer CRC,
but such diversity is harmful because it would require implementers to keep two separate CRC tables
which may be costly in embedded applications and may deteriorate the performance of CPU caches.
Unreliable links and temporal redundancy
++++++++++++++++++++++++++++++++++++++++
The serial transport supports the deterministic data loss mitigation option,
where a transfer can be repeated several times to reduce the probability of its loss.
This transport supports optional FEC through full duplication of transfers.
This feature is discussed in detail in the documentation for the UDP transport :mod:`pycyphal.transport.udp`.
Expand Down
2 changes: 1 addition & 1 deletion pycyphal/transport/serial/_serial.py
Original file line number Diff line number Diff line change
Expand Up @@ -99,7 +99,7 @@ def __init__(
This setting does not affect transfer reception -- the RX MTU is always set to the maximum valid MTU
(i.e., practically unlimited).
:param service_transfer_multiplier: Deterministic data loss mitigation for service transfers.
:param service_transfer_multiplier: Forward error correction for service transfers.
This parameter specifies the number of times each outgoing service transfer will be repeated.
This setting does not affect message transfers.
Expand Down

0 comments on commit 01b9a9b

Please sign in to comment.