Skip to content

Commit 2a41c93

Browse files
committed
Improved USLP checksum handling
1 parent b1d7114 commit 2a41c93

File tree

7 files changed

+117
-91
lines changed

7 files changed

+117
-91
lines changed

Diff for: CHANGELOG.md

+10
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,16 @@ and this project adheres to [Semantic Versioning](http://semver.org/).
88

99
# [unreleased]
1010

11+
## Changed
12+
13+
- USLP implementation: FECF is fixed to 2 bytes and always uses the standard CRC16 CCITT checksum
14+
calculation now. Consequently, the USLP API was adapted and simplified.
15+
- `FecfProperties` removed, not required anymore
16+
- `TransferFrame` constructor now expects `has_fecf` boolean parameter which defaults to true.
17+
The checksum will be calculated and appended by the `pack` method. The `unpack` method
18+
expects the `has_fecf` flag now as well and will perform a CRC16 calculation when the flag
19+
is set to True.
20+
1121
# [v0.26.1] 2024-11-30
1222

1323
## Fixed

Diff for: src/spacepackets/ecss/pus_1_verification.py

+8-2
Original file line numberDiff line numberDiff line change
@@ -51,7 +51,10 @@ def len(self) -> int:
5151

5252
@classmethod
5353
def unpack(
54-
cls, data: bytes | bytearray, num_bytes_err_code: int, num_bytes_data: int | None = None
54+
cls,
55+
data: bytes | bytearray,
56+
num_bytes_err_code: int,
57+
num_bytes_data: int | None = None,
5558
) -> FailureNotice:
5659
pfc = num_bytes_err_code * 8
5760
if num_bytes_data is None:
@@ -281,7 +284,10 @@ def error_code(self) -> ErrorCode | None:
281284

282285
@property
283286
def is_step_reply(self) -> bool:
284-
return self.subservice in (Subservice.TM_STEP_FAILURE, Subservice.TM_STEP_SUCCESS)
287+
return self.subservice in (
288+
Subservice.TM_STEP_FAILURE,
289+
Subservice.TM_STEP_SUCCESS,
290+
)
285291

286292
@property
287293
def step_id(self) -> StepId | None:

Diff for: src/spacepackets/uslp/defs.py

+4
Original file line numberDiff line numberDiff line change
@@ -24,3 +24,7 @@ class UslpVersionMissmatchError(Exception):
2424

2525
class UslpTypeMissmatchError(Exception):
2626
pass
27+
28+
29+
class UslpChecksumError(Exception):
30+
pass

Diff for: src/spacepackets/uslp/frame.py

+22-29
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,10 @@
44
import struct
55
from typing import Union
66

7+
from spacepackets.crc import CRC16_CCITT_FUNC
8+
79
from .defs import (
10+
UslpChecksumError,
811
UslpFhpVhopFieldMissingError,
912
UslpInvalidConstructionRulesError,
1013
UslpInvalidFrameHeaderError,
@@ -30,28 +33,19 @@ def __init__(self, present: bool, size: int):
3033
self.size = size
3134

3235

33-
class FecfProperties:
34-
def __init__(self, present: bool, size: int):
35-
self.present = present
36-
self.size = size
37-
38-
3936
class FramePropertiesBase:
4037
def __init__(
4138
self,
4239
has_insert_zone: bool,
4340
has_fecf: bool,
4441
insert_zone_len: int | None = None,
45-
fecf_len: int | None = None,
4642
):
4743
if has_insert_zone and insert_zone_len is None:
4844
raise ValueError
49-
if has_fecf and fecf_len is None:
50-
raise ValueError
5145
self.insert_zone_properties = InsertZoneProperties(
5246
present=has_insert_zone, size=insert_zone_len
5347
)
54-
self.fecf_properties = FecfProperties(present=has_fecf, size=fecf_len)
48+
self.has_fecf = has_fecf
5549

5650

5751
class FixedFrameProperties(FramePropertiesBase):
@@ -61,7 +55,6 @@ def __init__(
6155
has_insert_zone: bool,
6256
has_fecf: bool,
6357
insert_zone_len: int | None = None,
64-
fecf_len: int | None = None,
6558
):
6659
"""Contains properties required when unpacking fixed USLP frames. These properties
6760
can not be determined by parsing the frame. The standard refers to these properties
@@ -76,7 +69,6 @@ def __init__(
7669
has_insert_zone=has_insert_zone,
7770
has_fecf=has_fecf,
7871
insert_zone_len=insert_zone_len,
79-
fecf_len=fecf_len,
8072
)
8173
self.fixed_len = fixed_len
8274

@@ -88,7 +80,6 @@ def __init__(
8880
has_fecf: bool,
8981
truncated_frame_len: int,
9082
insert_zone_len: int | None = None,
91-
fecf_len: int | None = None,
9283
):
9384
"""Contains properties required when unpacking variable USLP frames. These properties
9485
can not be determined by parsing the frame. The standard refers to these properties
@@ -104,7 +95,6 @@ def __init__(
10495
has_insert_zone=has_insert_zone,
10596
has_fecf=has_fecf,
10697
insert_zone_len=insert_zone_len,
107-
fecf_len=fecf_len,
10898
)
10999
self.truncated_frame_len = truncated_frame_len
110100

@@ -324,13 +314,13 @@ def __init__(
324314
tfdf: TransferFrameDataField,
325315
insert_zone: bytes | None = None,
326316
op_ctrl_field: bytes | None = None,
327-
fecf: bytes | None = None,
317+
has_fecf: bool = True,
328318
):
329319
self.header = header
330320
self.tfdf = tfdf
331321
self.insert_zone = insert_zone
332322
self.op_ctrl_field = op_ctrl_field
333-
self.fecf = fecf
323+
self.has_fecf = has_fecf
334324

335325
def pack(self, truncated: bool = False, frame_type: FrameType | None = None) -> bytearray:
336326
frame = bytearray()
@@ -346,8 +336,8 @@ def pack(self, truncated: bool = False, frame_type: FrameType | None = None) ->
346336
frame.extend(self.op_ctrl_field)
347337
elif not truncated and self.header.op_ctrl_flag:
348338
raise UslpInvalidFrameHeaderError
349-
if self.fecf is not None:
350-
frame.extend(self.fecf)
339+
if self.has_fecf:
340+
frame.extend(struct.pack("!H", CRC16_CCITT_FUNC(frame)))
351341
return frame
352342

353343
def set_frame_len_in_header(self) -> None:
@@ -363,8 +353,8 @@ def len(self) -> int:
363353
size += len(self.insert_zone)
364354
if self.op_ctrl_field is not None:
365355
size += len(self.op_ctrl_field)
366-
if self.fecf is not None:
367-
size += len(self.fecf)
356+
if self.has_fecf:
357+
size += 2
368358
return size
369359

370360
@classmethod
@@ -383,13 +373,16 @@ def __empty(cls) -> TransferFrame:
383373
tfdf=empty_data_field,
384374
insert_zone=None,
385375
op_ctrl_field=None,
386-
fecf=None,
376+
has_fecf=False,
387377
)
388378

389379
# TODO: Fix lint by creating helper methods.
390380
@classmethod
391381
def unpack( # noqa: PLR0912 too many branches
392-
cls, raw_frame: bytes, frame_type: FrameType, frame_properties: FramePropertiesT
382+
cls,
383+
raw_frame: bytes | bytearray,
384+
frame_type: FrameType,
385+
frame_properties: FramePropertiesT,
393386
) -> TransferFrame:
394387
"""Unpack a USLP transfer frame from a raw bytearray. All managed parameters have
395388
to be passed explicitly.
@@ -457,11 +450,11 @@ def unpack( # noqa: PLR0912 too many branches
457450
frame.op_ctrl_field = raw_frame[current_idx : current_idx + 4]
458451
current_idx += 4
459452
# Parse Frame Error Control field if present
460-
if frame_properties.fecf_properties.present:
461-
frame.fecf = raw_frame[
462-
current_idx : current_idx + frame_properties.fecf_properties.size
463-
]
464-
current_idx += frame_properties.fecf_properties.size
453+
if frame_properties.has_fecf:
454+
crc_check = CRC16_CCITT_FUNC(raw_frame[0 : current_idx + 2])
455+
if crc_check != 0:
456+
raise UslpChecksumError
457+
465458
return frame
466459

467460
@staticmethod
@@ -497,8 +490,8 @@ def __get_tfdf_len(
497490
exact_tfdf_len = properties.truncated_frame_len - header_len
498491
else:
499492
exact_tfdf_len = header.frame_len + 1 - header_len
500-
if properties.fecf_properties.present:
501-
exact_tfdf_len -= properties.fecf_properties.size
493+
if properties.has_fecf:
494+
exact_tfdf_len -= 2
502495
if header_type != HeaderType.TRUNCATED and header.op_ctrl_flag:
503496
exact_tfdf_len -= 4
504497
if properties.insert_zone_properties.present:

Diff for: src/spacepackets/uslp/header.py

+4-4
Original file line numberDiff line numberDiff line change
@@ -183,9 +183,9 @@ def __init__(
183183
vcid: int,
184184
map_id: int,
185185
frame_len: int,
186-
bypass_seq_ctrl_flag: BypassSequenceControlFlag,
187-
prot_ctrl_cmd_flag: ProtocolCommandFlag,
188-
op_ctrl_flag: bool,
186+
bypass_seq_ctrl_flag: BypassSequenceControlFlag = BypassSequenceControlFlag.SEQ_CTRLD_QOS,
187+
prot_ctrl_cmd_flag: ProtocolCommandFlag = ProtocolCommandFlag.USER_DATA,
188+
op_ctrl_flag: bool = False,
189189
vcf_count_len: int = 0,
190190
vcf_count: int | None = None,
191191
):
@@ -286,7 +286,7 @@ def len(self) -> int:
286286
return 7 + self.vcf_count_len
287287

288288

289-
def determine_header_type(header_start: bytes) -> HeaderType:
289+
def determine_header_type(header_start: bytes | bytearray) -> HeaderType:
290290
"""Determine header type from raw header.
291291
:param header_start:
292292
:raises ValueError: Passed bytearray shorter than minimum length 4

Diff for: tests/cfdp/tlvslvs/test_msg_to_user.py

+6-1
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,11 @@
11
from unittest import TestCase
22

3-
from spacepackets.cfdp import MessageToUserTlv, TlvHolder, TlvType, TlvTypeMissmatchError
3+
from spacepackets.cfdp import (
4+
MessageToUserTlv,
5+
TlvHolder,
6+
TlvType,
7+
TlvTypeMissmatchError,
8+
)
49
from spacepackets.cfdp.tlv import (
510
CfdpTlv,
611
ProxyMessageType,

0 commit comments

Comments
 (0)