AcraNetwork 1.2.0__tar.gz → 1.2.3__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (69) hide show
  1. {acranetwork-1.2.0 → acranetwork-1.2.3}/AcraNetwork/IRIG106/Chapter7/__init__.py +85 -23
  2. {acranetwork-1.2.0 → acranetwork-1.2.3}/AcraNetwork/SimpleEthernet.py +1 -1
  3. {acranetwork-1.2.0 → acranetwork-1.2.3}/AcraNetwork/__version__.py +4 -1
  4. {acranetwork-1.2.0 → acranetwork-1.2.3}/AcraNetwork.egg-info/PKG-INFO +1 -1
  5. {acranetwork-1.2.0 → acranetwork-1.2.3}/PKG-INFO +1 -1
  6. {acranetwork-1.2.0 → acranetwork-1.2.3}/examples/validate_pcap.py +88 -63
  7. {acranetwork-1.2.0 → acranetwork-1.2.3}/test/test_ch7.py +132 -5
  8. {acranetwork-1.2.0 → acranetwork-1.2.3}/AcraNetwork/IENA.py +0 -0
  9. {acranetwork-1.2.0 → acranetwork-1.2.3}/AcraNetwork/IRIG106/Chapter10/Chapter10UDP.py +0 -0
  10. {acranetwork-1.2.0 → acranetwork-1.2.3}/AcraNetwork/IRIG106/Chapter10/FileParser.py +0 -0
  11. {acranetwork-1.2.0 → acranetwork-1.2.3}/AcraNetwork/IRIG106/Chapter10/__init__.py +0 -0
  12. {acranetwork-1.2.0 → acranetwork-1.2.3}/AcraNetwork/IRIG106/Chapter11/ARINC429.py +0 -0
  13. {acranetwork-1.2.0 → acranetwork-1.2.3}/AcraNetwork/IRIG106/Chapter11/Analog.py +0 -0
  14. {acranetwork-1.2.0 → acranetwork-1.2.3}/AcraNetwork/IRIG106/Chapter11/CAN.py +0 -0
  15. {acranetwork-1.2.0 → acranetwork-1.2.3}/AcraNetwork/IRIG106/Chapter11/ComputerData.py +0 -0
  16. {acranetwork-1.2.0 → acranetwork-1.2.3}/AcraNetwork/IRIG106/Chapter11/MILSTD1553.py +0 -0
  17. {acranetwork-1.2.0 → acranetwork-1.2.3}/AcraNetwork/IRIG106/Chapter11/PCM.py +0 -0
  18. {acranetwork-1.2.0 → acranetwork-1.2.3}/AcraNetwork/IRIG106/Chapter11/TimeDataFormat.py +0 -0
  19. {acranetwork-1.2.0 → acranetwork-1.2.3}/AcraNetwork/IRIG106/Chapter11/UART.py +0 -0
  20. {acranetwork-1.2.0 → acranetwork-1.2.3}/AcraNetwork/IRIG106/Chapter11/Video.py +0 -0
  21. {acranetwork-1.2.0 → acranetwork-1.2.3}/AcraNetwork/IRIG106/Chapter11/__init__.py +0 -0
  22. {acranetwork-1.2.0 → acranetwork-1.2.3}/AcraNetwork/IRIG106/Chapter7/Golay.py +0 -0
  23. {acranetwork-1.2.0 → acranetwork-1.2.3}/AcraNetwork/IRIG106/__init__.py +0 -0
  24. {acranetwork-1.2.0 → acranetwork-1.2.3}/AcraNetwork/MPEG/ADTS.py +0 -0
  25. {acranetwork-1.2.0 → acranetwork-1.2.3}/AcraNetwork/MPEG/H264.py +0 -0
  26. {acranetwork-1.2.0 → acranetwork-1.2.3}/AcraNetwork/MPEG/PES.py +0 -0
  27. {acranetwork-1.2.0 → acranetwork-1.2.3}/AcraNetwork/MPEG/PMT.py +0 -0
  28. {acranetwork-1.2.0 → acranetwork-1.2.3}/AcraNetwork/MPEG/STANAG4609.py +0 -0
  29. {acranetwork-1.2.0 → acranetwork-1.2.3}/AcraNetwork/MPEG/__init__.py +0 -0
  30. {acranetwork-1.2.0 → acranetwork-1.2.3}/AcraNetwork/MPEGTS.py +0 -0
  31. {acranetwork-1.2.0 → acranetwork-1.2.3}/AcraNetwork/McastSocket.py +0 -0
  32. {acranetwork-1.2.0 → acranetwork-1.2.3}/AcraNetwork/NPD.py +0 -0
  33. {acranetwork-1.2.0 → acranetwork-1.2.3}/AcraNetwork/ParserAligned.py +0 -0
  34. {acranetwork-1.2.0 → acranetwork-1.2.3}/AcraNetwork/Pcap.py +0 -0
  35. {acranetwork-1.2.0 → acranetwork-1.2.3}/AcraNetwork/SamDec008.py +0 -0
  36. {acranetwork-1.2.0 → acranetwork-1.2.3}/AcraNetwork/__init__.py +0 -0
  37. {acranetwork-1.2.0 → acranetwork-1.2.3}/AcraNetwork/iNET.py +0 -0
  38. {acranetwork-1.2.0 → acranetwork-1.2.3}/AcraNetwork/iNetX.py +0 -0
  39. {acranetwork-1.2.0 → acranetwork-1.2.3}/AcraNetwork/nanotime.py +0 -0
  40. {acranetwork-1.2.0 → acranetwork-1.2.3}/AcraNetwork/ptptime.py +0 -0
  41. {acranetwork-1.2.0 → acranetwork-1.2.3}/AcraNetwork.egg-info/SOURCES.txt +0 -0
  42. {acranetwork-1.2.0 → acranetwork-1.2.3}/AcraNetwork.egg-info/dependency_links.txt +0 -0
  43. {acranetwork-1.2.0 → acranetwork-1.2.3}/AcraNetwork.egg-info/top_level.txt +0 -0
  44. {acranetwork-1.2.0 → acranetwork-1.2.3}/LICENSE +0 -0
  45. {acranetwork-1.2.0 → acranetwork-1.2.3}/MANIFEST.in +0 -0
  46. {acranetwork-1.2.0 → acranetwork-1.2.3}/README.md +0 -0
  47. {acranetwork-1.2.0 → acranetwork-1.2.3}/examples/adau_to_ch10.py +0 -0
  48. {acranetwork-1.2.0 → acranetwork-1.2.3}/examples/ch10_recorder.py +0 -0
  49. {acranetwork-1.2.0 → acranetwork-1.2.3}/examples/ch10_to_pcap.py +0 -0
  50. {acranetwork-1.2.0 → acranetwork-1.2.3}/examples/pkg_gen.ini +0 -0
  51. {acranetwork-1.2.0 → acranetwork-1.2.3}/examples/tx_iena_udp.py +0 -0
  52. {acranetwork-1.2.0 → acranetwork-1.2.3}/examples/tx_inetx_udp.py +0 -0
  53. {acranetwork-1.2.0 → acranetwork-1.2.3}/examples/validate_ch10.py +0 -0
  54. {acranetwork-1.2.0 → acranetwork-1.2.3}/setup.cfg +0 -0
  55. {acranetwork-1.2.0 → acranetwork-1.2.3}/setup.py +0 -0
  56. {acranetwork-1.2.0 → acranetwork-1.2.3}/test/__init__.py +0 -0
  57. {acranetwork-1.2.0 → acranetwork-1.2.3}/test/test_afdx.py +0 -0
  58. {acranetwork-1.2.0 → acranetwork-1.2.3}/test/test_ch10.py +0 -0
  59. {acranetwork-1.2.0 → acranetwork-1.2.3}/test/test_golay.py +0 -0
  60. {acranetwork-1.2.0 → acranetwork-1.2.3}/test/test_iena.py +0 -0
  61. {acranetwork-1.2.0 → acranetwork-1.2.3}/test/test_inet.py +0 -0
  62. {acranetwork-1.2.0 → acranetwork-1.2.3}/test/test_inetx.py +0 -0
  63. {acranetwork-1.2.0 → acranetwork-1.2.3}/test/test_misc.py +0 -0
  64. {acranetwork-1.2.0 → acranetwork-1.2.3}/test/test_mpegts.py +0 -0
  65. {acranetwork-1.2.0 → acranetwork-1.2.3}/test/test_npd.py +0 -0
  66. {acranetwork-1.2.0 → acranetwork-1.2.3}/test/test_paligned.py +0 -0
  67. {acranetwork-1.2.0 → acranetwork-1.2.3}/test/test_pcap.py +0 -0
  68. {acranetwork-1.2.0 → acranetwork-1.2.3}/test/test_ptptime.py +0 -0
  69. {acranetwork-1.2.0 → acranetwork-1.2.3}/test/test_simpleethernet.py +0 -0
@@ -23,6 +23,7 @@ import logging
23
23
  import typing
24
24
  from enum import IntEnum
25
25
 
26
+
26
27
  ch7_logger = logging.getLogger(__name__)
27
28
 
28
29
 
@@ -34,6 +35,11 @@ class PTDPContent(IntEnum):
34
35
  ETHERNET_MAC = 0x4
35
36
  IP = 0x5
36
37
  CHAPTER_24 = 0x6
38
+ ILLEGAL = 0xF
39
+
40
+ @classmethod
41
+ def _missing_(cls, value):
42
+ return PTDPContent.ILLEGAL
37
43
 
38
44
 
39
45
  class PTDPFragment(IntEnum):
@@ -41,12 +47,17 @@ class PTDPFragment(IntEnum):
41
47
  FIRST = 0x1
42
48
  MIDDLE = 0x2
43
49
  LAST = 0x3
50
+ ILLEGAL = 0x4
51
+
52
+ @classmethod
53
+ def _missing_(cls, value):
54
+ return PTDPFragment.ILLEGAL
44
55
 
45
56
 
46
57
  PTDP_HDR_LEN = 0x6 # 24bits x2
47
58
  PTFR_HDR_LEN = 0x4 # 1 byte unprotected and 3 bytes protected
48
59
 
49
-
60
+ PTDT_LLP_TRAILER_LEN = 1
50
61
  PTDP_MAX_LEN = 0x800
51
62
 
52
63
 
@@ -77,10 +88,25 @@ def datapkts_to_ptfr(
77
88
  # Create a new frame
78
89
  ptfr = _new_ptfr(ptfr_len, streamid, golay)
79
90
  for ptdp in datapkts_to_ptdp(eth_ch10_packets):
80
- # Encapsulate the data in PTDP packets
81
- # Add the payload to the PTFR frame. IF we get a remainder then this from is full
91
+
92
+ remaining_space = ptfr.remaining_space()
93
+ # logging.debug(f"Adding ptdp {ptdp} with remaining space {remaining_space}")
94
+ # If we have a low latency packet but not enough space to insert it full then
95
+ # insert a fill packet
96
+ if ptdp.low_latency and remaining_space <= (len(ptdp) + PTDT_LLP_TRAILER_LEN):
97
+ # logging.warning(f"Need to add fill packet to fill remaining space={remaining_space}")
98
+ fill = ptdp_fill(remaining_space)
99
+ remainder = ptfr.add_payload(fill.pack(), False)
100
+ yield ptfr
101
+ ptfr = _new_ptfr(ptfr_len, streamid, golay)
102
+ if len(remainder) > 0:
103
+ ptfr.add_payload(remainder)
104
+ # logging.debug(f"Added remainder of len {len(remainder)}. Remainder={ptfr.remaining_space()}")
105
+
106
+ # Add the packet and if there is remainder push out the ptfr packet and start a new one
82
107
  remainder = ptfr.add_payload(ptdp.pack(), ptdp.low_latency)
83
- while remainder != bytes():
108
+ ch7_logger.debug(f"PTDP ({ptdp}) to be added to PTRF leaving remainder of len={len(remainder)}")
109
+ while not ptfr.has_space():
84
110
  # Spit out the full frame
85
111
  yield ptfr
86
112
  # Create anew frame
@@ -93,10 +119,11 @@ def datapkts_to_ptfr(
93
119
  remainder = remainder[ptfr_len:] # If we have more data then save it for the next frame
94
120
  ptfr = _new_ptfr(ptfr_len, streamid, golay)
95
121
 
96
- # Point to the first offset of the frame.
97
- ptfr.ptdp_offset = len(remainder)
98
- # Add the PTDS to the frame
99
- remainder = ptfr.add_payload(remainder)
122
+ if len(remainder) > 0:
123
+ # Point to the first offset of the frame.
124
+ ptfr.ptdp_offset = len(remainder)
125
+ # Add the PTDS to the frame
126
+ remainder = ptfr.add_payload(remainder)
100
127
 
101
128
 
102
129
  def datapkts_to_ptdp(eth_ch10_packets: typing.Iterable[bytes, bool]) -> typing.Generator[PTDP, None, None]:
@@ -164,6 +191,7 @@ class PTDP(object):
164
191
  )
165
192
 
166
193
  self._payload = val
194
+ self.length = len(val)
167
195
 
168
196
  def pack(self) -> bytes:
169
197
  """
@@ -198,7 +226,7 @@ class PTDP(object):
198
226
  raise PTDPLengthError("GolayHdr=len={}. Must be corrupted".format(self.length))
199
227
  elif len(buffer[6:]) < self.length:
200
228
  raise PTDPRemainingData(
201
- "Buffer length={} GolayHdr=len={} fragment={} content={}".format(
229
+ "Not a full PTDP packet. Rest likely in next packet . Buffer length={} GolayHdr=len={} fragment={} content={}".format(
202
230
  len(buffer[6:]), self.length, self.fragment, self.content
203
231
  )
204
232
  )
@@ -222,9 +250,18 @@ class PTDP(object):
222
250
  return not self.__eq__(other)
223
251
 
224
252
  def __repr__(self):
225
- return "PTDP: Len={} Content={} Fragment={} LowLatency={}".format(
226
- self.length, self.content, self.fragment, self.low_latency
227
- )
253
+ return f"PTDP: Len={self.length} Content={repr(self.content)} Fragment={repr(self.fragment)} LowLatency={self.low_latency}"
254
+
255
+
256
+ def ptdp_fill(total_len_min: int) -> PTDP:
257
+ _p = PTDP()
258
+ _p.content = PTDPContent.FILL
259
+ if total_len_min < (PTDP_HDR_LEN + 1):
260
+ payload_len = 1
261
+ else:
262
+ payload_len = total_len_min - PTDP_HDR_LEN
263
+ _p.payload = struct.pack(f">{payload_len}B", *([0xFF] * payload_len))
264
+ return _p
228
265
 
229
266
 
230
267
  class PTFR(object):
@@ -255,11 +292,17 @@ class PTFR(object):
255
292
  def add_payload(self, buffer: bytes, is_llp: bool = False) -> bytes:
256
293
  """
257
294
  Add the buffer to the payload ensuring not to go over the length field
258
- Return the remained
295
+ Return the remainder
259
296
  """
297
+ if len(buffer) == 0:
298
+ raise Exception("Can't add payload of len = 0")
299
+ if is_llp and len(buffer) + len(self._payload) > self.length:
300
+ raise Exception(
301
+ f"LLP packet ({len(buffer)}) cannot be added (ptfrlen={self.length}) as it will push the payload ({len(self.payload)}) into the next frame. Add a fill word"
302
+ )
260
303
  if is_llp and len(self._payload) > 0 and self.llp:
261
304
  ch7_logger.debug(
262
- "Adding an LLP buffer to some existing llp payload. Shifting original data and adding llp at "
305
+ f"Adding an LLP buffer({len(buffer)}) to some existing llp payload ({len(self._payload)}). Shifting original data and adding llp at "
263
306
  "start. len={}".format(len(buffer) + 1)
264
307
  )
265
308
  self.ptdp_offset += len(buffer) + 1
@@ -276,10 +319,11 @@ class PTFR(object):
276
319
  elif is_llp and len(self.payload) == 0:
277
320
  ch7_logger.debug("Adding first LLP buffer len={}".format(len(buffer) + 1))
278
321
  self.llp = True
279
- self._payload = buffer + struct.pack(">B", 0xFF)
322
+ self._payload = buffer + struct.pack(">B", 0x00) # Further LLPs will be added in front of this one
323
+ self.ptdp_offset += len(buffer) + 1
280
324
  else:
281
- ch7_logger.debug("Adding a normal PTDP buffer len={}".format(len(buffer)))
282
325
  self._payload += buffer
326
+ ch7_logger.debug(f"Adding a normal PTDP buffer of len={len(buffer)} to a total len={len(self._payload)}")
283
327
 
284
328
  if len(self._payload) > self.length:
285
329
  len_to_take = self.length - len(self._payload)
@@ -289,6 +333,12 @@ class PTFR(object):
289
333
  else:
290
334
  return bytes()
291
335
 
336
+ def has_space(self, expected_addition: int = 0) -> bool:
337
+ return (len(self.payload) + expected_addition) < self.length
338
+
339
+ def remaining_space(self) -> int:
340
+ return self.length - len(self.payload)
341
+
292
342
  def pack(self) -> bytes:
293
343
  """
294
344
  Convert a PTFR object into a string for transmission. This will return the packed string and the remainder string
@@ -325,10 +375,8 @@ class PTFR(object):
325
375
 
326
376
  def check_offsets(self, act_offset: int) -> bool:
327
377
  if (act_offset != self.ptdp_offset) and (self.ptdp_offset != 2047):
328
- ch7_logger.warning(
329
- "Offset of unpacked PTDP packet ({}) does not match the declared offset ({})".format(
330
- act_offset, self.ptdp_offset
331
- )
378
+ ch7_logger.debug(
379
+ f"Offset of unpacked PTDP packet ({act_offset}) does not match the declared offset ({self.ptdp_offset})"
332
380
  )
333
381
  return False
334
382
  elif self.ptdp_offset != 2047:
@@ -383,15 +431,20 @@ class PTFR(object):
383
431
  )
384
432
  )
385
433
 
434
+ do_offset_check = True
386
435
  if is_llp:
387
436
  byte_offset = 0
388
437
  elif remainder is None: # Fake the offset if we have jumped into the middle of a data stream
389
438
  byte_offset = self.ptdp_offset
390
439
  else:
391
440
  byte_offset = -1 * len(remainder)
392
- do_offset_check = True
441
+ if len(remainder) == 0:
442
+ do_offset_check = False
443
+
393
444
  offset_check_count = 0
445
+
394
446
  while aligned:
447
+ ch7_logger.debug(f"Starting to check buf of lenght={len(buf)}")
395
448
  p = PTDP(self._golay)
396
449
  try:
397
450
  buf = p.unpack(buf)
@@ -402,6 +455,7 @@ class PTFR(object):
402
455
  len(buf), e, self.ptdp_offset
403
456
  )
404
457
  )
458
+ exit()
405
459
  yield (None, None, e)
406
460
  except PTDPRemainingData as e:
407
461
  aligned = False
@@ -416,12 +470,16 @@ class PTFR(object):
416
470
  yield (None, buf, e)
417
471
  else:
418
472
  if not is_llp and do_offset_check and byte_offset >= 0:
473
+ # ch7_logger.debug(f"do_offset_check={do_offset_check} byte_offset={byte_offset}")
419
474
  self.check_offsets(byte_offset)
420
475
  do_offset_check = False
421
476
  offset_check_count += 1
422
477
  elif not is_llp and not do_offset_check and offset_check_count < 1:
423
478
  do_offset_check = True
424
479
  byte_offset += len(p)
480
+ # ch7_logger.debug(
481
+ # f"Enable do_offset_check because {offset_check_count} < 1 byte_offset={byte_offset} len_p={len(p)}"
482
+ # )
425
483
  elif not is_llp:
426
484
  byte_offset += len(p)
427
485
 
@@ -433,6 +491,7 @@ class PTFR(object):
433
491
  (next_llp,) = struct.unpack_from(">B", buf)
434
492
  # Check if the next PTDP is low latency before yielding
435
493
  if next_llp == 0xFF:
494
+ ch7_logger.debug("Next packet is LLP")
436
495
  is_llp = True
437
496
  buf = buf[1:]
438
497
  byte_offset += len(p) + 1
@@ -456,8 +515,11 @@ class PTFR(object):
456
515
  if len(remainder) > 0:
457
516
  do_offset_check = False
458
517
 
518
+ # ch7_logger.debug(f"Returning p={repr(p)} and no remainder")
459
519
  yield (p, bytes(), "")
460
520
 
521
+ # ch7_logger.debug("------PTFR expired-----")
522
+
461
523
  def __eq__(self, other: PTFR):
462
524
  if not isinstance(other, PTFR):
463
525
  return False
@@ -472,6 +534,6 @@ class PTFR(object):
472
534
  return not self.__eq__(other)
473
535
 
474
536
  def __repr__(self):
475
- return "PTFR: Length={} StreamID={:#0X} Offset={} LowLatency={}\n".format(
476
- self.length, self.streamid, self.ptdp_offset, self.llp
537
+ return (
538
+ f"PTFR: Length={self.length} StreamID={self.streamid:#0X} Offset={self.ptdp_offset} LowLatency={self.llp}\n"
477
539
  )
@@ -318,7 +318,7 @@ class IP(object):
318
318
 
319
319
  computed_ip_checksum = ip_calc_checksum(buf[: IP.IP_HEADER_SIZE])
320
320
  if computed_ip_checksum != 0:
321
- logging.error(
321
+ logger.error(
322
322
  f"Invalid IP Header Checksum. Computed Checksum = 0x{computed_ip_checksum:04X} (should be 0x0000). Raw received Checksum = 0x{checksum:04X}."
323
323
  )
324
324
 
@@ -105,4 +105,7 @@
105
105
  # 1.1.8 - No change but tagging as 1.1.8
106
106
  # 1.1.9 - Fixed ARINC ch10 intra packet header
107
107
  # 1.2.0 - Moved Chapter 7 into the IRIG106 folder. Removed old Chapter10 directory
108
- __version__ = "1.2.0"
108
+ # 1.2.1 - Fixed logging in IP error message
109
+ # 1.2.2 - Fixed chapter 7 packet generation
110
+ # 1.2.3 - Last release was nto successful
111
+ __version__ = "1.2.3"
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: AcraNetwork
3
- Version: 1.2.0
3
+ Version: 1.2.3
4
4
  Summary: Classes and utilities to support Flight Test Instrumentation Ethernet networks
5
5
  Home-page: https://github.com/diarmuidcwc/AcraNetwork
6
6
  Author: Diarmuid Collins
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: AcraNetwork
3
- Version: 1.2.0
3
+ Version: 1.2.3
4
4
  Summary: Classes and utilities to support Flight Test Instrumentation Ethernet networks
5
5
  Home-page: https://github.com/diarmuidcwc/AcraNetwork
6
6
  Author: Diarmuid Collins
@@ -1,15 +1,12 @@
1
1
  #!/usr/bin/env python
2
2
 
3
-
3
+ """
4
4
  # Very rudimentary (but fast) validation of recorded data
5
5
  # Finds all inetx packets and validate no missing sequence numbers
6
6
  # It will also take the url to an axn mem and verify data after downloading
7
+ """
7
8
 
8
9
 
9
- import sys
10
-
11
- sys.path.append("..")
12
- sys.path.append(".")
13
10
  import AcraNetwork.Pcap as pcap
14
11
  import glob
15
12
  import os.path
@@ -24,13 +21,12 @@ from os import mkdir, remove
24
21
  import datetime
25
22
  from dataclasses import dataclass, field
26
23
  import typing
24
+ import zstandard as zstd
25
+ import sys
27
26
 
27
+ VERSION = "0.5.0"
28
28
 
29
- VERSION = "0.4.0"
30
-
31
- logging.basicConfig(
32
- level=logging.INFO, format="%(levelname)-6s %(asctime)-15s %(message)s"
33
- )
29
+ logging.basicConfig(level=logging.INFO, format="%(levelname)-6s %(asctime)-15s %(message)s")
34
30
 
35
31
 
36
32
  @dataclass
@@ -56,7 +52,7 @@ class Streams:
56
52
  return 0
57
53
  return int(self.length * 8 * self.pkt_count / (self.end_ts - self.start_ts))
58
54
 
59
- def timelen(self) ->float:
55
+ def timelen(self) -> float:
60
56
  return self.end_ts - self.start_ts
61
57
 
62
58
  def drops_to_hist(self):
@@ -66,7 +62,7 @@ class Streams:
66
62
  bin_cnt = [0] * bins
67
63
  for i in range(bins):
68
64
  for s in self.sequence_list:
69
- #print(f"{i}:{start_seq}:{s}:{bin_wdth}")
65
+ # print(f"{i}:{start_seq}:{s}:{bin_wdth}")
70
66
  if (start_seq + bin_wdth * i) <= s < (start_seq + bin_wdth * (i + 1)):
71
67
  bin_cnt[i] += 1
72
68
  rstring = "|"
@@ -83,9 +79,7 @@ class Streams:
83
79
 
84
80
  def create_parser():
85
81
  # Argument parser
86
- parser = argparse.ArgumentParser(
87
- description="Validate inetx sequence numbers quickly in a pcap file"
88
- )
82
+ parser = argparse.ArgumentParser(description="Validate inetx sequence numbers quickly in a pcap file")
89
83
  # Common
90
84
  parser.add_argument(
91
85
  "--folder",
@@ -122,9 +116,7 @@ def create_parser():
122
116
  default=False,
123
117
  help="print a rough histogram of where the drops happened",
124
118
  )
125
- parser.add_argument(
126
- "--version", action="version", version="%(prog)s {}".format(VERSION)
127
- )
119
+ parser.add_argument("--version", action="version", version="%(prog)s {}".format(VERSION))
128
120
 
129
121
  return parser
130
122
 
@@ -141,6 +133,11 @@ def uri_validator(x):
141
133
  return False
142
134
 
143
135
 
136
+ def is_compressed(x: str) -> bool:
137
+ (_root, _ext) = os.path.splitext(x)
138
+ return _ext == ".zst"
139
+
140
+
144
141
  def main(args):
145
142
  roll_over = pow(2, 32)
146
143
  fnames = {}
@@ -174,7 +171,7 @@ def main(args):
174
171
  first_pcap_time = None
175
172
  last_pcap_time = None
176
173
  packet_data_vol = 0
177
- tmp_folder = "httpdl"
174
+ tmp_folder = ".tmp"
178
175
  loss = 0
179
176
  floss = 0
180
177
  outf = ""
@@ -195,12 +192,27 @@ def main(args):
195
192
  out_file.write(chunk)
196
193
 
197
194
  dlspeed = data_len * 8 / (time.time() - sd) / 1e6
198
- logging.info(
199
- "Downloaded {} at {:.1f}Mbps and wrote to {}".format(
200
- pfile, dlspeed, outf
201
- )
202
- )
195
+ logging.info("Downloaded {} at {:.1f}Mbps and wrote to {}".format(pfile, dlspeed, outf))
203
196
  p = pcap.Pcap(outf)
197
+ elif is_compressed(pfile):
198
+
199
+ if not os.path.exists(tmp_folder):
200
+ mkdir(tmp_folder, 0o755)
201
+ fname_pcap, _ext = os.path.splitext(os.path.basename(pfile))
202
+ decompressed_f = os.path.join(tmp_folder, fname_pcap)
203
+ with open(pfile, "rb") as compressed:
204
+ with open(decompressed_f, "wb") as decompressed:
205
+ dctx = zstd.ZstdDecompressor()
206
+ dctx.copy_stream(compressed, decompressed)
207
+
208
+ # Get sizes
209
+ compressed_size = os.path.getsize(pfile)
210
+ decompressed_size = os.path.getsize(decompressed_f)
211
+
212
+ # Compute compression ratio
213
+ compression_ratio = decompressed_size / compressed_size
214
+ logging.info(f"Compression ratio: {compression_ratio:.2f}x")
215
+ p = pcap.Pcap(decompressed_f)
204
216
  else:
205
217
  p = pcap.Pcap(pfile)
206
218
  prev_rec_ts = None
@@ -215,34 +227,23 @@ def main(args):
215
227
  if prev_rec_ts is not None:
216
228
  if prev_rec_ts > last_pcap_time:
217
229
  delta = prev_rec_ts - last_pcap_time
218
- logging.warning(
219
- f"Record={i + 1} Record timestamp negative jump {delta}s")
230
+ logging.warning(f"Record={i + 1} Record timestamp negative jump {delta}s")
220
231
  prev_rec_ts = last_pcap_time
221
232
 
222
233
  packet_data_vol += len(r.payload)
223
234
  total_pkt_count += 1
224
- if len(r.payload) >= (
225
- 26 + 0x28
226
- ): # For short packets don't try to decode them as inetx
235
+ if len(r.payload) >= (26 + 0x28): # For short packets don't try to decode them as inetx
227
236
  # pull out the key fields
228
- (
229
- dst_port,
230
- udp_len,
231
- checksum,
232
- control,
233
- stream_id,
234
- seq,
235
- _len,
236
- ptpsec,
237
- ptpnsec
238
- ) = struct.unpack_from(">HHHIIIIII", r.payload, 0x24)
237
+ (dst_port, udp_len, checksum, control, stream_id, seq, _len, ptpsec, ptpnsec) = struct.unpack_from(
238
+ ">HHHIIIIII", r.payload, 0x24
239
+ )
239
240
  if control == args.control:
240
241
  if stream_id in streams:
241
242
  stream = streams[stream_id]
242
243
  if seq != (stream.sequence + 1) % roll_over:
243
- pkt_ts = datetime.datetime.fromtimestamp(
244
- r.sec + r.usec * 1e-6
245
- ).strftime("%H:%M:%S.%f %d %b")
244
+ pkt_ts = datetime.datetime.fromtimestamp(r.sec + r.usec * 1e-6).strftime(
245
+ "%H:%M:%S.%f %d %b"
246
+ )
246
247
  if seq < stream.sequence:
247
248
  logging.warning(
248
249
  "Source Restarted. File={} PktNum={} StreamID={:#0X} PrevSeq={} "
@@ -267,22 +268,31 @@ def main(args):
267
268
  stream.sequence,
268
269
  seq,
269
270
  loss,
270
- loss * stream.length
271
+ loss * stream.length,
271
272
  )
272
273
  )
273
274
  loss_count += loss
274
- loss_data += (loss * stream.length)
275
+ loss_data += loss * stream.length
275
276
  stream.dropcnt += loss
276
277
  stream.sequence_list.append(stream.sequence + 1)
277
278
  floss += loss
278
279
  stream.sequence = seq
279
280
  stream.pkt_count += 1
280
- stream.end_ts = ptpsec + ptpnsec/1e9
281
+ stream.end_ts = ptpsec + ptpnsec / 1e9
281
282
  stream.datavol += len(r.payload)
282
283
  else:
283
- stream = Streams(stream_id, seq, 1, ptpsec + ptpnsec/1e9,
284
- ptpsec + ptpnsec/1e6, 0, 0, len(r.payload),
285
- len(r.payload), [])
284
+ stream = Streams(
285
+ stream_id,
286
+ seq,
287
+ 1,
288
+ ptpsec + ptpnsec / 1e9,
289
+ ptpsec + ptpnsec / 1e6,
290
+ 0,
291
+ 0,
292
+ len(r.payload),
293
+ len(r.payload),
294
+ [],
295
+ )
286
296
  streams[stream_id] = stream
287
297
  inetx_pkts_validate += 1
288
298
  data_count_bytes += len(r.payload)
@@ -295,16 +305,12 @@ def main(args):
295
305
  dr = 100
296
306
 
297
307
  try:
298
- ave_rec_rate_mbps = (
299
- (packet_data_vol * 8) / (last_pcap_time - first_pcap_time) / 1e6
300
- )
308
+ ave_rec_rate_mbps = (packet_data_vol * 8) / (last_pcap_time - first_pcap_time) / 1e6
301
309
  except:
302
310
  ave_rec_rate_mbps = 0
303
311
  sids_found = len(streams)
304
312
  if first_pcap_time is not None:
305
- file_stamp = datetime.datetime.fromtimestamp(first_pcap_time).strftime(
306
- "%H:%M:%S %d %b"
307
- )
313
+ file_stamp = datetime.datetime.fromtimestamp(first_pcap_time).strftime("%H:%M:%S %d %b")
308
314
  else:
309
315
  file_stamp = "unknown"
310
316
  info_str = (
@@ -319,9 +325,7 @@ def main(args):
319
325
  if args.verbose:
320
326
  if len(streams) > 0:
321
327
  logging.info(
322
- "{:>7s} {:>9s} {:>9s} {:>9s} {:>9s}".format(
323
- "SID", "Seq", "LostCount", "ResetCnt", "Length"
324
- )
328
+ "{:>7s} {:>9s} {:>9s} {:>9s} {:>9s}".format("SID", "Seq", "LostCount", "ResetCnt", "Length")
325
329
  )
326
330
  for sid, stream in streams.items():
327
331
  logging.info(
@@ -337,12 +341,24 @@ def main(args):
337
341
  remove(outf)
338
342
  elif is_url and not args.verbose:
339
343
  remove(outf)
344
+ if is_compressed(pfile):
345
+ remove(decompressed_f)
346
+
340
347
  print("\n")
341
348
  if len(streams) > 0:
342
349
  logging.info(
343
350
  "{:>7s} {:>15s} {:>9s} {:>9s} {:>9s} {:>9s} {:>9s} {:>18s} {:>12s} {:>12s} {:>12s}".format(
344
- "SID", "Cnt", "LostCount", "ResetCnt", "Length", "PPS", "Mbps", "Elapsed Time(s)",
345
- "DataVol(MB)", "DropVol(Bytes)", "BitRate(Mbps)"
351
+ "SID",
352
+ "Cnt",
353
+ "LostCount",
354
+ "ResetCnt",
355
+ "Length",
356
+ "PPS",
357
+ "Mbps",
358
+ "Elapsed Time(s)",
359
+ "DataVol(MB)",
360
+ "DropVol(Bytes)",
361
+ "BitRate(Mbps)",
346
362
  )
347
363
  )
348
364
  for sid, stream in sorted(streams.items()):
@@ -352,9 +368,18 @@ def main(args):
352
368
  _hist = ""
353
369
  logging.info(
354
370
  "{:#07X} {:15,d} {:9d} {:9d} {:9d} {:9d} {:9.1f} {:18.1f} {:12,.1f} {:12,d} {:12,.1f} {}".format(
355
- sid, stream.pkt_count, stream.dropcnt, stream.rstcnt, stream.length,
356
- stream.pps(), stream.bitrate()/1e6, stream.timelen(), stream.datavol/1e6,
357
- stream.dropcnt * stream.length, stream.datavol * 8 / (stream.timelen() * 1e6), _hist
371
+ sid,
372
+ stream.pkt_count,
373
+ stream.dropcnt,
374
+ stream.rstcnt,
375
+ stream.length,
376
+ stream.pps(),
377
+ stream.bitrate() / 1e6,
378
+ stream.timelen(),
379
+ stream.datavol / 1e6,
380
+ stream.dropcnt * stream.length,
381
+ stream.datavol * 8 / (stream.timelen() * 1e6),
382
+ _hist,
358
383
  )
359
384
  )
360
385
 
@@ -8,10 +8,14 @@ import struct
8
8
  import logging
9
9
  from pstats import Stats
10
10
  import cProfile
11
+ import typing
12
+ import random
11
13
 
12
14
 
13
15
  THIS_DIR = os.path.dirname(os.path.abspath(__file__))
14
16
 
17
+ logging.basicConfig(level=logging.INFO, format="%(levelname)s:%(funcName)s:%(lineno)s:%(message)s")
18
+
15
19
 
16
20
  def buf_generator(count, llp_count=0):
17
21
  running_count = 0
@@ -25,6 +29,23 @@ def buf_generator(count, llp_count=0):
25
29
  yield buf_len, low_latency
26
30
 
27
31
 
32
+ def ptfr_to_pcm_frame(
33
+ count: int,
34
+ ):
35
+ remainder = bytes()
36
+ pcm_frame_len = 1024
37
+ offset_ptfr = 30
38
+ zero_buf = struct.pack(">B", 0) * offset_ptfr
39
+ pcm_frame = zero_buf
40
+ for ptfr in ch7.datapkts_to_ptfr(buf_generator(count)):
41
+ pcm_frame += ptfr.pack()
42
+ if len(pcm_frame) >= pcm_frame_len:
43
+ remainder = pcm_frame[pcm_frame_len:]
44
+ pcm_frame = pcm_frame[:pcm_frame_len]
45
+ yield pcm_frame
46
+ pcm_frame = zero_buf + remainder
47
+
48
+
28
49
  class TestCaseCh7(unittest.TestCase):
29
50
  def test_basic(self):
30
51
  ch7_pkt = ch7.PTFR()
@@ -85,7 +106,7 @@ class TestGenerators(unittest.TestCase):
85
106
  # print(repr(ptdp_pkt))
86
107
  self.assertIsInstance(ptdp_pkt, ch7.PTDP)
87
108
 
88
- def test_ptfr_geenerator(self):
109
+ def test_ptfr_generator(self):
89
110
  remainder = bytes()
90
111
  for ptfr in ch7.datapkts_to_ptfr(buf_generator(5), ptfr_len=200):
91
112
  # print(repr(ptfr))
@@ -223,9 +244,10 @@ class TestRealEthernet(unittest.TestCase):
223
244
  r.payload = eth_p
224
245
  # Verify the size of the packets and it's probably good. I could unpack them here too
225
246
  # self.assertEqual(2*64+eth.UDP.UDP_HEADER_SIZE+eth.IP.IP_HEADER_SIZE+eth.Ethernet.HEADERLEN, len(eth_p))
226
- if eth_p not in TestRealEthernet.pkts_sent:
227
- self.assertTrue(False)
228
- pkt_count += 1
247
+ if p.content != ch7.PTDPContent.FILL:
248
+ if eth_p not in TestRealEthernet.pkts_sent:
249
+ self.assertTrue(False)
250
+ pkt_count += 1
229
251
  pf.write(r)
230
252
  eth_p = bytes()
231
253
  ptdp_idx += 1
@@ -239,7 +261,6 @@ class TestRealEthernet(unittest.TestCase):
239
261
  llc_count = 0
240
262
  fill_count = 0
241
263
  mac_count = 0
242
- logging.basicConfig(level=logging.WARN)
243
264
  remainder = b""
244
265
  for i in range(3, 6):
245
266
  f = open(THIS_DIR + "/ptfr_{}.bin".format(i), "rb")
@@ -267,5 +288,111 @@ class TestRealEthernet(unittest.TestCase):
267
288
  self.assertEqual(4, mac_count)
268
289
 
269
290
 
291
+ def get_pkts(some_low_latency: bool = False, max_len: int = 178) -> typing.Generator[tuple[bytes, bool], None, None]:
292
+
293
+ count = 0
294
+ while True:
295
+ # pkt_len = random.randint(2, 180)
296
+ pkt_len = (count % max_len) + 2
297
+ paylaod_int = [pkt_len] + [count] * (pkt_len - 1)
298
+ payload = struct.pack(f">{pkt_len}Q", *paylaod_int)
299
+ count += 1
300
+ llc_pkts = [True, False, False, False, False, False, False]
301
+ # llc_pkts = [True, True, True, True, True, True, True]
302
+ if some_low_latency:
303
+ low_latency = llc_pkts[count % 7]
304
+ else:
305
+ low_latency = False
306
+ logging.debug(f"TX: Generated payload of length {pkt_len*8} count={count}")
307
+ yield payload, low_latency
308
+
309
+
310
+ def get_pcm_frame(offset_ptfr: int = 0, some_low_latency: bool = False, max_len: int = 178):
311
+ pcm_frame_len = 1024
312
+ ptfr_len = pcm_frame_len - offset_ptfr - 4
313
+ zero_buf = struct.pack(">B", 0) * offset_ptfr
314
+ for ptfr in ch7.datapkts_to_ptfr(get_pkts(some_low_latency, max_len), ptfr_len=ptfr_len):
315
+ pcm_frame = zero_buf + ptfr.pack()
316
+ logging.debug(f"TX pcm_frame_len={len(pcm_frame)} ptfr_len={ptfr_len}")
317
+ yield pcm_frame
318
+
319
+
320
+ def missing_elements(L):
321
+ start, end = L[0], L[-1]
322
+ return sorted(set(range(start, end + 1)).difference(L))
323
+
324
+
325
+ class TestRandomSizedDecom(unittest.TestCase):
326
+ def test_no_llc(self):
327
+ offset = 0
328
+ first_PTFR = True
329
+ eth_p = bytes()
330
+ prev_eth_count = None
331
+ remainder = None
332
+ count = 0
333
+ for frame in get_pcm_frame(offset, some_low_latency=False):
334
+ ch7_pkt = ch7.PTFR()
335
+ ch7_buffer = frame[offset:]
336
+ ch7_pkt.length = len(ch7_buffer)
337
+ ch7_pkt.unpack(ch7_buffer)
338
+ count += 1
339
+ if count > 10000:
340
+ return
341
+
342
+ for p, remainder, e in ch7_pkt.get_aligned_payload(first_PTFR, remainder):
343
+ first_PTFR = False
344
+ if p is not None:
345
+ if p.length != 0:
346
+ if p.fragment == ch7.PTDPFragment.COMPLETE or p.fragment == ch7.PTDPFragment.LAST:
347
+ eth_p += p.payload
348
+ logging.debug(repr(p))
349
+ self.assertGreaterEqual(len(eth_p), 16)
350
+ (expected_len, count) = struct.unpack_from(">QQ", eth_p, 0x0)
351
+ logging.debug(f"RX payload count={count} len={len(eth_p)}")
352
+ if prev_eth_count is not None:
353
+ if prev_eth_count + 1 != count:
354
+ self.assertEqual(prev_eth_count + 1, count)
355
+ self.assertEqual(expected_len * 8, len(eth_p))
356
+
357
+ prev_eth_count = count
358
+ eth_p = bytes()
359
+
360
+ def test_some_llc(self):
361
+ offset = 0
362
+ first_PTFR = True
363
+ eth_p = bytes()
364
+ remainder = None
365
+ golay = ch7.Golay.Golay()
366
+ count = 0
367
+ numbers_found = []
368
+ for frame in get_pcm_frame(offset, some_low_latency=True, max_len=50):
369
+ ch7_pkt = ch7.PTFR(golay)
370
+ ch7_buffer = frame[offset:]
371
+ ch7_pkt.length = len(ch7_buffer)
372
+ ch7_pkt.unpack(ch7_buffer)
373
+ count += 1
374
+ if count > 10000:
375
+ return
376
+
377
+ for p, remainder, e in ch7_pkt.get_aligned_payload(first_PTFR, remainder):
378
+ first_PTFR = False
379
+ if p is not None:
380
+ if p.length != 0:
381
+ if p.fragment == ch7.PTDPFragment.COMPLETE or p.fragment == ch7.PTDPFragment.LAST:
382
+ eth_p += p.payload
383
+ logging.debug(repr(p))
384
+ if p.content != ch7.PTDPContent.FILL:
385
+ self.assertGreaterEqual(len(eth_p), 16)
386
+ (expected_len, count) = struct.unpack_from(">QQ", eth_p, 0x0)
387
+ logging.debug(f"RX payload count={count} len={len(eth_p)}")
388
+ numbers_found.append(count)
389
+ self.assertEqual(expected_len * 8, len(eth_p))
390
+
391
+ eth_p = bytes()
392
+
393
+ numbers_found.sort()
394
+ self.assertEqual(missing_elements(numbers_found), [])
395
+
396
+
270
397
  if __name__ == "__main__":
271
398
  unittest.main()
File without changes
File without changes
File without changes
File without changes
File without changes