AcraNetwork 1.3.0__tar.gz → 1.3.2__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.
- acranetwork-1.3.2/AcraNetwork/GRE.py +24 -0
- {acranetwork-1.3.0 → acranetwork-1.3.2}/AcraNetwork/IRIG106/Chapter24/__init__.py +4 -4
- {acranetwork-1.3.0 → acranetwork-1.3.2}/AcraNetwork/IRIG106/Chapter7/Golay.py +21 -15
- {acranetwork-1.3.0 → acranetwork-1.3.2}/AcraNetwork/IRIG106/Chapter7/golay_c.c +16 -10
- {acranetwork-1.3.0 → acranetwork-1.3.2}/AcraNetwork/__version__.py +4 -1
- {acranetwork-1.3.0 → acranetwork-1.3.2}/AcraNetwork.egg-info/PKG-INFO +2 -1
- {acranetwork-1.3.0 → acranetwork-1.3.2}/AcraNetwork.egg-info/SOURCES.txt +5 -0
- {acranetwork-1.3.0 → acranetwork-1.3.2}/PKG-INFO +2 -1
- {acranetwork-1.3.0 → acranetwork-1.3.2}/README.md +2 -1
- acranetwork-1.3.2/examples/parse_mpeg_pcap.py +101 -0
- acranetwork-1.3.2/examples/pcap_to_ascii.py +114 -0
- acranetwork-1.3.2/examples/proxy_to_udp.py +95 -0
- acranetwork-1.3.2/examples/simple_mcast_capture.py +55 -0
- {acranetwork-1.3.0 → acranetwork-1.3.2}/test/test_ch10.py +0 -7
- {acranetwork-1.3.0 → acranetwork-1.3.2}/test/test_ch24.py +1 -1
- {acranetwork-1.3.0 → acranetwork-1.3.2}/test/test_golay.py +213 -204
- {acranetwork-1.3.0 → acranetwork-1.3.2}/test/test_simpleethernet.py +33 -5
- {acranetwork-1.3.0 → acranetwork-1.3.2}/AcraNetwork/IENA.py +0 -0
- {acranetwork-1.3.0 → acranetwork-1.3.2}/AcraNetwork/IRIG106/Chapter10/Chapter10UDP.py +0 -0
- {acranetwork-1.3.0 → acranetwork-1.3.2}/AcraNetwork/IRIG106/Chapter10/FileParser.py +0 -0
- {acranetwork-1.3.0 → acranetwork-1.3.2}/AcraNetwork/IRIG106/Chapter10/__init__.py +0 -0
- {acranetwork-1.3.0 → acranetwork-1.3.2}/AcraNetwork/IRIG106/Chapter11/ARINC429.py +0 -0
- {acranetwork-1.3.0 → acranetwork-1.3.2}/AcraNetwork/IRIG106/Chapter11/Analog.py +0 -0
- {acranetwork-1.3.0 → acranetwork-1.3.2}/AcraNetwork/IRIG106/Chapter11/CAN.py +0 -0
- {acranetwork-1.3.0 → acranetwork-1.3.2}/AcraNetwork/IRIG106/Chapter11/ComputerData.py +0 -0
- {acranetwork-1.3.0 → acranetwork-1.3.2}/AcraNetwork/IRIG106/Chapter11/MILSTD1553.py +0 -0
- {acranetwork-1.3.0 → acranetwork-1.3.2}/AcraNetwork/IRIG106/Chapter11/PCM.py +0 -0
- {acranetwork-1.3.0 → acranetwork-1.3.2}/AcraNetwork/IRIG106/Chapter11/TimeDataFormat.py +0 -0
- {acranetwork-1.3.0 → acranetwork-1.3.2}/AcraNetwork/IRIG106/Chapter11/UART.py +0 -0
- {acranetwork-1.3.0 → acranetwork-1.3.2}/AcraNetwork/IRIG106/Chapter11/Video.py +0 -0
- {acranetwork-1.3.0 → acranetwork-1.3.2}/AcraNetwork/IRIG106/Chapter11/__init__.py +0 -0
- {acranetwork-1.3.0 → acranetwork-1.3.2}/AcraNetwork/IRIG106/Chapter7/__init__.py +0 -0
- {acranetwork-1.3.0 → acranetwork-1.3.2}/AcraNetwork/IRIG106/__init__.py +0 -0
- {acranetwork-1.3.0 → acranetwork-1.3.2}/AcraNetwork/MPEG/ADTS.py +0 -0
- {acranetwork-1.3.0 → acranetwork-1.3.2}/AcraNetwork/MPEG/H264.py +0 -0
- {acranetwork-1.3.0 → acranetwork-1.3.2}/AcraNetwork/MPEG/PES.py +0 -0
- {acranetwork-1.3.0 → acranetwork-1.3.2}/AcraNetwork/MPEG/PMT.py +0 -0
- {acranetwork-1.3.0 → acranetwork-1.3.2}/AcraNetwork/MPEG/STANAG4609.py +0 -0
- {acranetwork-1.3.0 → acranetwork-1.3.2}/AcraNetwork/MPEG/__init__.py +0 -0
- {acranetwork-1.3.0 → acranetwork-1.3.2}/AcraNetwork/MPEGTS.py +0 -0
- {acranetwork-1.3.0 → acranetwork-1.3.2}/AcraNetwork/McastSocket.py +0 -0
- {acranetwork-1.3.0 → acranetwork-1.3.2}/AcraNetwork/NPD.py +0 -0
- {acranetwork-1.3.0 → acranetwork-1.3.2}/AcraNetwork/ParserAligned.py +0 -0
- {acranetwork-1.3.0 → acranetwork-1.3.2}/AcraNetwork/Pcap.py +0 -0
- {acranetwork-1.3.0 → acranetwork-1.3.2}/AcraNetwork/SamDec008.py +0 -0
- {acranetwork-1.3.0 → acranetwork-1.3.2}/AcraNetwork/SimpleEthernet.py +0 -0
- {acranetwork-1.3.0 → acranetwork-1.3.2}/AcraNetwork/__init__.py +0 -0
- {acranetwork-1.3.0 → acranetwork-1.3.2}/AcraNetwork/iNET.py +0 -0
- {acranetwork-1.3.0 → acranetwork-1.3.2}/AcraNetwork/iNetX.py +0 -0
- {acranetwork-1.3.0 → acranetwork-1.3.2}/AcraNetwork/nanotime.py +0 -0
- {acranetwork-1.3.0 → acranetwork-1.3.2}/AcraNetwork/ptptime.py +0 -0
- {acranetwork-1.3.0 → acranetwork-1.3.2}/AcraNetwork.egg-info/dependency_links.txt +0 -0
- {acranetwork-1.3.0 → acranetwork-1.3.2}/AcraNetwork.egg-info/top_level.txt +0 -0
- {acranetwork-1.3.0 → acranetwork-1.3.2}/LICENSE +0 -0
- {acranetwork-1.3.0 → acranetwork-1.3.2}/MANIFEST.in +0 -0
- {acranetwork-1.3.0 → acranetwork-1.3.2}/examples/adau_to_ch10.py +0 -0
- {acranetwork-1.3.0 → acranetwork-1.3.2}/examples/ch10_recorder.py +0 -0
- {acranetwork-1.3.0 → acranetwork-1.3.2}/examples/ch10_to_pcap.py +0 -0
- {acranetwork-1.3.0 → acranetwork-1.3.2}/examples/pkg_gen.ini +0 -0
- {acranetwork-1.3.0 → acranetwork-1.3.2}/examples/tx_iena_udp.py +0 -0
- {acranetwork-1.3.0 → acranetwork-1.3.2}/examples/tx_inetx_udp.py +0 -0
- {acranetwork-1.3.0 → acranetwork-1.3.2}/examples/validate_ch10.py +0 -0
- {acranetwork-1.3.0 → acranetwork-1.3.2}/examples/validate_pcap.py +0 -0
- {acranetwork-1.3.0 → acranetwork-1.3.2}/setup.cfg +0 -0
- {acranetwork-1.3.0 → acranetwork-1.3.2}/setup.py +0 -0
- {acranetwork-1.3.0 → acranetwork-1.3.2}/test/__init__.py +0 -0
- {acranetwork-1.3.0 → acranetwork-1.3.2}/test/test_afdx.py +0 -0
- {acranetwork-1.3.0 → acranetwork-1.3.2}/test/test_ch7.py +0 -0
- {acranetwork-1.3.0 → acranetwork-1.3.2}/test/test_iena.py +0 -0
- {acranetwork-1.3.0 → acranetwork-1.3.2}/test/test_inet.py +0 -0
- {acranetwork-1.3.0 → acranetwork-1.3.2}/test/test_inetx.py +0 -0
- {acranetwork-1.3.0 → acranetwork-1.3.2}/test/test_misc.py +0 -0
- {acranetwork-1.3.0 → acranetwork-1.3.2}/test/test_mpegts.py +0 -0
- {acranetwork-1.3.0 → acranetwork-1.3.2}/test/test_npd.py +0 -0
- {acranetwork-1.3.0 → acranetwork-1.3.2}/test/test_paligned.py +0 -0
- {acranetwork-1.3.0 → acranetwork-1.3.2}/test/test_pcap.py +0 -0
- {acranetwork-1.3.0 → acranetwork-1.3.2}/test/test_ptptime.py +0 -0
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
import typing
|
|
2
|
+
import struct
|
|
3
|
+
|
|
4
|
+
GRE_MAX_SEQ = pow(2, 32)
|
|
5
|
+
GRE_TYPE_SECURE_DATA = 0x876D
|
|
6
|
+
GRE_TYPE_RESERVED = 0xFFFF
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
class GRE(object):
|
|
10
|
+
"""
|
|
11
|
+
Generic Routing Encapsulation (GRE) defined in RFC1701
|
|
12
|
+
This implements a single variant of this only.
|
|
13
|
+
Sequence number supported and no other optional fiend
|
|
14
|
+
"""
|
|
15
|
+
|
|
16
|
+
def __init__(self) -> None:
|
|
17
|
+
self.protocol_type: int = GRE_TYPE_RESERVED
|
|
18
|
+
self.sequence: int = 0
|
|
19
|
+
self.payload: typing.Optional[bytes] = None
|
|
20
|
+
|
|
21
|
+
def pack(self) -> bytes:
|
|
22
|
+
flags = 1 << 12
|
|
23
|
+
_hdr = struct.pack(">HHI", flags, self.protocol_type, self.sequence % GRE_MAX_SEQ)
|
|
24
|
+
return _hdr + self.payload
|
|
@@ -162,7 +162,7 @@ class TmNSMessage:
|
|
|
162
162
|
>>> pkt = ch24.TmNSMessage()
|
|
163
163
|
>>> pkt.flags.acquired = ch24.DataSourceAcquiredDataFlag.SIMULATED
|
|
164
164
|
>>> pkt.flags.fragmentation = ch24.MessageFragmentationFlags.LASTFRAGMENT
|
|
165
|
-
>>> pkt.
|
|
165
|
+
>>> pkt.definitionid = 0x1234
|
|
166
166
|
>>> pkt.sequence = 100
|
|
167
167
|
>>> pkt.payload = struct.pack(">HH", 0x1, 0x2)
|
|
168
168
|
>>> b = pkt.pack()
|
|
@@ -173,7 +173,7 @@ class TmNSMessage:
|
|
|
173
173
|
msgtype: int = TYPE_TMNS
|
|
174
174
|
optionwordcount: int = 0
|
|
175
175
|
version: int = VERSION
|
|
176
|
-
|
|
176
|
+
definitionid: int = 0
|
|
177
177
|
sequence: int = 0
|
|
178
178
|
length: int = 0
|
|
179
179
|
timestamp: int = 0
|
|
@@ -185,7 +185,7 @@ class TmNSMessage:
|
|
|
185
185
|
Convert the byte buffer into a TmNSMessage object
|
|
186
186
|
"""
|
|
187
187
|
self.flags.unpack(buffer[0:2])
|
|
188
|
-
(_type, _ver_opt, self.
|
|
188
|
+
(_type, _ver_opt, self.definitionid, self.sequence, self.length, self.timestamp) = struct.unpack_from(
|
|
189
189
|
TMNS_STRUCT, buffer, 2
|
|
190
190
|
)
|
|
191
191
|
self.msgtype = _type & 0xF
|
|
@@ -209,7 +209,7 @@ class TmNSMessage:
|
|
|
209
209
|
TMNS_STRUCT,
|
|
210
210
|
self.msgtype,
|
|
211
211
|
self.optionwordcount + (self.version << 4),
|
|
212
|
-
self.
|
|
212
|
+
self.definitionid,
|
|
213
213
|
self.sequence,
|
|
214
214
|
self.length,
|
|
215
215
|
self.timestamp,
|
|
@@ -20,12 +20,13 @@ from functools import lru_cache
|
|
|
20
20
|
import warnings
|
|
21
21
|
|
|
22
22
|
try:
|
|
23
|
-
from . import golay_c as _golay_native
|
|
23
|
+
from . import golay_c as _golay_native # type: ignore
|
|
24
24
|
|
|
25
|
-
|
|
25
|
+
_c_extension_available = True
|
|
26
26
|
except ImportError:
|
|
27
27
|
warnings.warn("C extension for Golay not found. Falling back to pure Python.", RuntimeWarning)
|
|
28
|
-
|
|
28
|
+
_c_extension_available = False
|
|
29
|
+
_use_c_extension = _c_extension_available # default behavior, unchanged
|
|
29
30
|
|
|
30
31
|
|
|
31
32
|
GOLAY_SIZE = 0x1000
|
|
@@ -47,14 +48,20 @@ class Golay:
|
|
|
47
48
|
CorrectTable = None
|
|
48
49
|
ErrorTable = None
|
|
49
50
|
|
|
50
|
-
def __init__(self):
|
|
51
|
-
if
|
|
51
|
+
def __init__(self, use_c_extension=None):
|
|
52
|
+
if use_c_extension:
|
|
53
|
+
use_c_extension = _use_c_extension
|
|
54
|
+
if use_c_extension and not _c_extension_available:
|
|
55
|
+
raise RuntimeError("C extension for Golay requested but not available")
|
|
56
|
+
self._use_c_extension = use_c_extension
|
|
57
|
+
|
|
58
|
+
if self._use_c_extension:
|
|
52
59
|
_golay_native.golay_init_tables()
|
|
53
60
|
else:
|
|
54
|
-
if Golay.EncodeTable is None:
|
|
61
|
+
if Golay.EncodeTable is None:
|
|
55
62
|
self._init_encode_table()
|
|
56
|
-
|
|
57
|
-
|
|
63
|
+
if Golay.SyndromeTable is None:
|
|
64
|
+
self._initgolaydecode()
|
|
58
65
|
|
|
59
66
|
def _init_encode_table(self):
|
|
60
67
|
Golay.EncodeTable = [0] * GOLAY_SIZE
|
|
@@ -68,7 +75,7 @@ class Golay:
|
|
|
68
75
|
if not (0 <= raw <= 0xFFF):
|
|
69
76
|
raise ValueError("Only 12-bit unsigned values allowed")
|
|
70
77
|
|
|
71
|
-
if _use_c_extension:
|
|
78
|
+
if self._use_c_extension:
|
|
72
79
|
encoded = _golay_native.golay_encode(raw)
|
|
73
80
|
else:
|
|
74
81
|
encoded = self._encode_python(raw)
|
|
@@ -78,7 +85,7 @@ class Golay:
|
|
|
78
85
|
return encoded
|
|
79
86
|
|
|
80
87
|
def decode(self, encoded):
|
|
81
|
-
if _use_c_extension:
|
|
88
|
+
if self._use_c_extension:
|
|
82
89
|
return _golay_native.golay_decode(encoded)
|
|
83
90
|
else:
|
|
84
91
|
# encoded is either an integer <= 0xFFFFFF, or is a bytes-like type
|
|
@@ -92,7 +99,6 @@ class Golay:
|
|
|
92
99
|
v = encoded
|
|
93
100
|
return self._decode_python(v)
|
|
94
101
|
|
|
95
|
-
|
|
96
102
|
def _encode_python(self, raw):
|
|
97
103
|
"""
|
|
98
104
|
Encode the value as a 24b code
|
|
@@ -104,7 +110,7 @@ class Golay:
|
|
|
104
110
|
:param raw: value to be encoded that is already validated to be 0..FFF
|
|
105
111
|
:return: encoded value as a 24-bit integer
|
|
106
112
|
"""
|
|
107
|
-
# self.encode() has already checked that 0 <= raw <= 0xFFF so do not
|
|
113
|
+
# self.encode() has already checked that 0 <= raw <= 0xFFF so do not
|
|
108
114
|
# check again
|
|
109
115
|
# Also, there is no to_string argument because that is handled by
|
|
110
116
|
# encode()
|
|
@@ -122,9 +128,9 @@ class Golay:
|
|
|
122
128
|
:param v: integer that has already been validated to be 24bit
|
|
123
129
|
:return: decoded 12-bit value
|
|
124
130
|
"""
|
|
125
|
-
# self.decode() has converted the value to an integer and verified
|
|
131
|
+
# self.decode() has converted the value to an integer and verified
|
|
126
132
|
# that it is valid. So do not repeat the check.
|
|
127
|
-
|
|
133
|
+
|
|
128
134
|
return self._decode2(((v) >> 12) & 0xFFF, (v) & 0xFFF)
|
|
129
135
|
|
|
130
136
|
def _syndrome2(self, v1, v2):
|
|
@@ -143,7 +149,7 @@ class Golay:
|
|
|
143
149
|
return self._errors2(((v) >> 12) & 0xFFF, (v) & 0xFFF)
|
|
144
150
|
|
|
145
151
|
def errors(self, v):
|
|
146
|
-
if _use_c_extension:
|
|
152
|
+
if self._use_c_extension:
|
|
147
153
|
return _golay_native.golay_errors(v)
|
|
148
154
|
else:
|
|
149
155
|
return self._errors(v)
|
|
@@ -52,7 +52,6 @@ static void InitGolayEncode(void)
|
|
|
52
52
|
}
|
|
53
53
|
}
|
|
54
54
|
|
|
55
|
-
// Initialize the Golay decoding lookup tables
|
|
56
55
|
static void InitGolayDecode(void)
|
|
57
56
|
{
|
|
58
57
|
for (uint32_t x = 0; x < GOLAY_SIZE; x++)
|
|
@@ -80,7 +79,7 @@ static void InitGolayDecode(void)
|
|
|
80
79
|
for (int k = 0; k < 24; k++)
|
|
81
80
|
{
|
|
82
81
|
uint32_t error = (1 << i) | (1 << j) | (1 << k);
|
|
83
|
-
uint16_t syndrome = SyndromeTable[
|
|
82
|
+
uint16_t syndrome = SyndromeTable[error & 0x0FFF] ^ ((error >> 12) & 0x0FFF);
|
|
84
83
|
CorrectTable[syndrome] = (error >> 12) & 0x0FFF;
|
|
85
84
|
ErrorTable[syndrome] = ones_in_code(error, 24);
|
|
86
85
|
}
|
|
@@ -124,17 +123,24 @@ static PyObject *py_golay_decode(PyObject *self, PyObject *args)
|
|
|
124
123
|
|
|
125
124
|
uint32_t encoded;
|
|
126
125
|
|
|
127
|
-
// Handle bytes input (
|
|
128
|
-
if (
|
|
126
|
+
// Handle bytes-like input (bytes, bytearray, memoryview, etc.)
|
|
127
|
+
if (PyObject_CheckBuffer(input))
|
|
129
128
|
{
|
|
130
|
-
|
|
129
|
+
Py_buffer view;
|
|
130
|
+
if (PyObject_GetBuffer(input, &view, PyBUF_SIMPLE) != 0)
|
|
131
131
|
{
|
|
132
|
+
return NULL; // GetBuffer already sets an exception
|
|
133
|
+
}
|
|
134
|
+
if (view.len != 3)
|
|
135
|
+
{
|
|
136
|
+
PyBuffer_Release(&view);
|
|
132
137
|
PyErr_SetString(PyExc_ValueError, "3-byte input required");
|
|
133
138
|
return NULL;
|
|
134
139
|
}
|
|
135
|
-
const unsigned char *buf = (const unsigned char *)
|
|
140
|
+
const unsigned char *buf = (const unsigned char *)view.buf;
|
|
136
141
|
// Convert big-endian bytes to 24-bit unsigned integer
|
|
137
142
|
encoded = ((uint32_t)buf[0] << 16) | ((uint32_t)buf[1] << 8) | buf[2];
|
|
143
|
+
PyBuffer_Release(&view);
|
|
138
144
|
}
|
|
139
145
|
// Handle integer input
|
|
140
146
|
else if (PyLong_Check(input))
|
|
@@ -142,21 +148,21 @@ static PyObject *py_golay_decode(PyObject *self, PyObject *args)
|
|
|
142
148
|
encoded = PyLong_AsUnsignedLong(input);
|
|
143
149
|
if (PyErr_Occurred())
|
|
144
150
|
{
|
|
145
|
-
return NULL;
|
|
151
|
+
return NULL;
|
|
146
152
|
}
|
|
147
153
|
if (encoded > 0xFFFFFF)
|
|
148
154
|
{
|
|
149
|
-
PyErr_SetString(PyExc_ValueError, "
|
|
155
|
+
PyErr_SetString(PyExc_ValueError, "Only 24-bit unsigned values supported");
|
|
150
156
|
return NULL;
|
|
151
157
|
}
|
|
152
158
|
}
|
|
153
159
|
else
|
|
154
160
|
{
|
|
155
|
-
PyErr_SetString(PyExc_TypeError, "Expected a
|
|
161
|
+
PyErr_SetString(PyExc_TypeError, "Expected a bytes-like object (3 bytes) or 24-bit integer.");
|
|
156
162
|
return NULL;
|
|
157
163
|
}
|
|
158
164
|
|
|
159
|
-
// Decode logic
|
|
165
|
+
// Decode logic unchanged
|
|
160
166
|
uint16_t v1 = (encoded >> 12) & 0x0FFF;
|
|
161
167
|
uint16_t v2 = encoded & 0x0FFF;
|
|
162
168
|
uint16_t syndrome = SyndromeTable[v2] ^ v1;
|
|
@@ -118,4 +118,7 @@
|
|
|
118
118
|
# - simplified Golay.py; removed some redundant checks
|
|
119
119
|
# 1.2.9 - Added __init__.py back into the test folder to allow pytest to run
|
|
120
120
|
# 1.3.0 - STart to add support for TmNSMessage
|
|
121
|
-
|
|
121
|
+
# 1.3.1 - Fixed typo in defintionid
|
|
122
|
+
# 1.3.2 - Aligned the C implementation of Golay with the python code. Unittest tests both C and Python. Performance improvements in python code
|
|
123
|
+
|
|
124
|
+
__version__ = "1.3.2"
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: AcraNetwork
|
|
3
|
-
Version: 1.3.
|
|
3
|
+
Version: 1.3.2
|
|
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
|
|
@@ -42,6 +42,7 @@ Full documentation is available here https://acranetwork.readthedocs.io/en/lates
|
|
|
42
42
|
* MPEGTS: MpegTransport stream packets
|
|
43
43
|
|
|
44
44
|
|
|
45
|
+
|
|
45
46
|
## Install
|
|
46
47
|
|
|
47
48
|
Install using pip
|
|
@@ -3,6 +3,7 @@ MANIFEST.in
|
|
|
3
3
|
README.md
|
|
4
4
|
setup.cfg
|
|
5
5
|
setup.py
|
|
6
|
+
AcraNetwork/GRE.py
|
|
6
7
|
AcraNetwork/IENA.py
|
|
7
8
|
AcraNetwork/MPEGTS.py
|
|
8
9
|
AcraNetwork/McastSocket.py
|
|
@@ -48,7 +49,11 @@ AcraNetwork/MPEG/__init__.py
|
|
|
48
49
|
examples/adau_to_ch10.py
|
|
49
50
|
examples/ch10_recorder.py
|
|
50
51
|
examples/ch10_to_pcap.py
|
|
52
|
+
examples/parse_mpeg_pcap.py
|
|
53
|
+
examples/pcap_to_ascii.py
|
|
51
54
|
examples/pkg_gen.ini
|
|
55
|
+
examples/proxy_to_udp.py
|
|
56
|
+
examples/simple_mcast_capture.py
|
|
52
57
|
examples/tx_iena_udp.py
|
|
53
58
|
examples/tx_inetx_udp.py
|
|
54
59
|
examples/validate_ch10.py
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: AcraNetwork
|
|
3
|
-
Version: 1.3.
|
|
3
|
+
Version: 1.3.2
|
|
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
|
|
@@ -42,6 +42,7 @@ Full documentation is available here https://acranetwork.readthedocs.io/en/lates
|
|
|
42
42
|
* MPEGTS: MpegTransport stream packets
|
|
43
43
|
|
|
44
44
|
|
|
45
|
+
|
|
45
46
|
## Install
|
|
46
47
|
|
|
47
48
|
Install using pip
|
|
@@ -23,6 +23,7 @@ Full documentation is available here https://acranetwork.readthedocs.io/en/lates
|
|
|
23
23
|
* MPEGTS: MpegTransport stream packets
|
|
24
24
|
|
|
25
25
|
|
|
26
|
+
|
|
26
27
|
## Install
|
|
27
28
|
|
|
28
29
|
Install using pip
|
|
@@ -58,4 +59,4 @@ pip install --upgrade pip wheel setuptools twine
|
|
|
58
59
|
rm dist/*
|
|
59
60
|
python ./setup.py sdist bdist_wheel --universal sdist
|
|
60
61
|
twine upload dist/*
|
|
61
|
-
```
|
|
62
|
+
```
|
|
@@ -0,0 +1,101 @@
|
|
|
1
|
+
#!/usr/bin/python
|
|
2
|
+
# -*- coding: utf-8 -*-
|
|
3
|
+
"""
|
|
4
|
+
=====
|
|
5
|
+
Parse MPEG TS packets in pcap....
|
|
6
|
+
=====
|
|
7
|
+
|
|
8
|
+
"""
|
|
9
|
+
__author__ = "Diarmuid Collins"
|
|
10
|
+
__copyright__ = "Copyright 2018"
|
|
11
|
+
__version__ = "0.0.1"
|
|
12
|
+
__maintainer__ = "Diarmuid Collins"
|
|
13
|
+
__email__ = "dcollins@curtisswright.com"
|
|
14
|
+
__status__ = "Production"
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
import sys
|
|
18
|
+
sys.path.append("..")
|
|
19
|
+
import AcraNetwork.Pcap as pcap
|
|
20
|
+
import AcraNetwork.iNetX as inetx
|
|
21
|
+
import AcraNetwork.MPEGTS as mpegts
|
|
22
|
+
import AcraNetwork.SimpleEthernet as eth
|
|
23
|
+
from AcraNetwork.MPEGTS import H264
|
|
24
|
+
import datetime
|
|
25
|
+
|
|
26
|
+
# This script shows how to parse either a pcap file or a TS file into the constituent
|
|
27
|
+
# NALS and finds the unique STANAG SEI User data with the timestamp
|
|
28
|
+
|
|
29
|
+
|
|
30
|
+
def pcap_to_ts(pcapfile,ts_file,udp_port=8010):
|
|
31
|
+
'''
|
|
32
|
+
Convert a pcap file to a TS file by extracting all data from a specified port
|
|
33
|
+
:param mpegfile: str
|
|
34
|
+
:param ts_file: str
|
|
35
|
+
:param udp_port: int
|
|
36
|
+
:return:
|
|
37
|
+
'''
|
|
38
|
+
|
|
39
|
+
mpegpcap = pcap.Pcap(pcapfile, mode='r')
|
|
40
|
+
|
|
41
|
+
ts = open(ts_file, mode='wb')
|
|
42
|
+
mpeghex = ""
|
|
43
|
+
rec_count = 0
|
|
44
|
+
|
|
45
|
+
for rec in mpegpcap:
|
|
46
|
+
try:
|
|
47
|
+
e = eth.Ethernet()
|
|
48
|
+
e.unpack(rec.packet)
|
|
49
|
+
i = eth.IP()
|
|
50
|
+
i.unpack(e.payload)
|
|
51
|
+
u = eth.UDP()
|
|
52
|
+
u.unpack(i.payload)
|
|
53
|
+
if u.dstport == udp_port:
|
|
54
|
+
rec_count += 1
|
|
55
|
+
inet = inetx.iNetX()
|
|
56
|
+
inet.unpack(u.payload)
|
|
57
|
+
mpegtspackets = mpegts.MPEGTS()
|
|
58
|
+
mpegtspackets.unpack(inet.payload)
|
|
59
|
+
for packet in mpegtspackets.blocks:
|
|
60
|
+
mpeghex += packet.payload
|
|
61
|
+
ts.write(inet.payload)
|
|
62
|
+
except:
|
|
63
|
+
continue
|
|
64
|
+
|
|
65
|
+
|
|
66
|
+
def parse_ts_file(tsfile):
|
|
67
|
+
|
|
68
|
+
ts_file = open(tsfile,mode='rb')
|
|
69
|
+
h264_data = H264()
|
|
70
|
+
h264_data.unpack(ts_file.read())
|
|
71
|
+
|
|
72
|
+
nal_counts = {}
|
|
73
|
+
timestamp_count = 0
|
|
74
|
+
for nal in h264_data.nals:
|
|
75
|
+
|
|
76
|
+
if not nal.type in nal_counts:
|
|
77
|
+
nal_counts[nal.type] = 1
|
|
78
|
+
else:
|
|
79
|
+
nal_counts[nal.type] += 1
|
|
80
|
+
|
|
81
|
+
if nal.type == mpegts.NAL_TYPES["SEI"]:
|
|
82
|
+
if nal.sei.unregdata:
|
|
83
|
+
print("Timestamp={} byte offset={} count ={}".format(datetime.datetime.strftime(nal.sei.time,"%d %b %Y %H:%M:%S.%f"),nal.offset,timestamp_count))
|
|
84
|
+
timestamp_count += 1
|
|
85
|
+
|
|
86
|
+
else:
|
|
87
|
+
pass
|
|
88
|
+
|
|
89
|
+
print("\n----- SUMMARY -----")
|
|
90
|
+
for type in nal_counts:
|
|
91
|
+
print("{} {} NALs in input".format(nal_counts[type],mpegts.NAL_TYPES_INV[type]))
|
|
92
|
+
print("{} STANAG Timestamps".format(timestamp_count))
|
|
93
|
+
|
|
94
|
+
|
|
95
|
+
def main():
|
|
96
|
+
# Read in a TS file and print out some useful information
|
|
97
|
+
parse_ts_file("../test/stanag_sample.ts")
|
|
98
|
+
|
|
99
|
+
|
|
100
|
+
if __name__ == "__main__":
|
|
101
|
+
main()
|
|
@@ -0,0 +1,114 @@
|
|
|
1
|
+
#!/usr/bin/python
|
|
2
|
+
# -*- coding: utf-8 -*-
|
|
3
|
+
"""
|
|
4
|
+
=====
|
|
5
|
+
PCAP to ASCII
|
|
6
|
+
=====
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
"""
|
|
10
|
+
__author__ = "Diarmuid Collins"
|
|
11
|
+
__copyright__ = "Copyright 2018"
|
|
12
|
+
__version__ = "0.0.1"
|
|
13
|
+
__maintainer__ = "Diarmuid Collins"
|
|
14
|
+
__email__ = "dcollins@curtisswright.com"
|
|
15
|
+
__status__ = "Production"
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
import sys
|
|
19
|
+
sys.path.append("..")
|
|
20
|
+
|
|
21
|
+
import os,struct
|
|
22
|
+
import argparse
|
|
23
|
+
|
|
24
|
+
import AcraNetwork.iNetX as inetx
|
|
25
|
+
import AcraNetwork.Pcap as pcap
|
|
26
|
+
import AcraNetwork.SimpleEthernet as SimpleEthernet
|
|
27
|
+
|
|
28
|
+
|
|
29
|
+
def main():
|
|
30
|
+
|
|
31
|
+
#----------------------------------
|
|
32
|
+
# Setup the command line parser
|
|
33
|
+
#----------------------------------
|
|
34
|
+
parser = argparse.ArgumentParser(description='Dump out the _payload of iNetX packets as ASCII representations')
|
|
35
|
+
parser.add_argument('--pcap', required=True, action='append', help='The input pcap file(s)')
|
|
36
|
+
parser.add_argument('--hex', required=False, action='store_true', default=False, help='Print the hex representation not the ASCII coded version')
|
|
37
|
+
parser.add_argument('--outdir', required=False, default="out", help='Name of output directory. Default is out')
|
|
38
|
+
args = parser.parse_args()
|
|
39
|
+
|
|
40
|
+
#------------------------------------------------------------
|
|
41
|
+
# Now read the input.
|
|
42
|
+
#------------------------------------------------------------
|
|
43
|
+
# The input will take multiple pcap files and loop through each
|
|
44
|
+
|
|
45
|
+
# Keep a track of the position in the line for each streamID
|
|
46
|
+
output_byte_count ={}
|
|
47
|
+
|
|
48
|
+
for pcapfilename in args.pcap:
|
|
49
|
+
try:
|
|
50
|
+
pcapfile = pcap.Pcap(pcapfilename)
|
|
51
|
+
except IOError:
|
|
52
|
+
print("ERROR: File {} not found".format(pcapfilename))
|
|
53
|
+
exit()
|
|
54
|
+
|
|
55
|
+
if not os.path.exists(args.outdir):
|
|
56
|
+
os.mkdir(args.outdir)
|
|
57
|
+
|
|
58
|
+
for pcaprecord in pcapfile:
|
|
59
|
+
eth = SimpleEthernet.Ethernet()
|
|
60
|
+
eth.unpack(pcaprecord.packet)
|
|
61
|
+
ip = SimpleEthernet.IP()
|
|
62
|
+
ip.unpack(eth.payload)
|
|
63
|
+
udp_packet = SimpleEthernet.UDP()
|
|
64
|
+
udp_packet.unpack(ip.payload)
|
|
65
|
+
(ctrl_word,) = struct.unpack('>I',udp_packet.payload[:4])
|
|
66
|
+
|
|
67
|
+
if ctrl_word == 0x11000000:
|
|
68
|
+
inetx_packet = inetx.iNetX()
|
|
69
|
+
# Unpack the udp _payload as an iNetx packet
|
|
70
|
+
inetx_packet.unpack(udp_packet.payload)
|
|
71
|
+
# Do we want to dump out an ascii or hex output
|
|
72
|
+
if args.hex == True:
|
|
73
|
+
prefix = "hex"
|
|
74
|
+
else:
|
|
75
|
+
prefix = "ascii"
|
|
76
|
+
|
|
77
|
+
# Create an output file per streamID and open it
|
|
78
|
+
output_file_name = "{}/{}_{:08X}.txt".format(args.outdir,prefix,inetx_packet.streamid)
|
|
79
|
+
# NB: We are appending to the file here so if you have existing files in the directory then it will be appended
|
|
80
|
+
output_file = open(output_file_name,'a')
|
|
81
|
+
|
|
82
|
+
# Start the byte count per streamID
|
|
83
|
+
if inetx_packet.streamid not in output_byte_count:
|
|
84
|
+
output_byte_count[inetx_packet.streamid] = 1
|
|
85
|
+
|
|
86
|
+
# Go thorough each byte in the _payload. Not particularly efficient
|
|
87
|
+
for offset in range(len(inetx_packet.payload)):
|
|
88
|
+
# Unpack the _payload as an unsigned integer
|
|
89
|
+
(byte_in_ascii,) =struct.unpack_from('B', inetx_packet.payload, offset)
|
|
90
|
+
|
|
91
|
+
# Write the output depending on what you want
|
|
92
|
+
if args.hex == True:
|
|
93
|
+
output_file.write("{:02X} ".format(byte_in_ascii))
|
|
94
|
+
else:
|
|
95
|
+
# Only some ASCII codes are printable so don't print out
|
|
96
|
+
# the non printable ones. Emulate the wireshark method of printing a period
|
|
97
|
+
if byte_in_ascii < 31 or byte_in_ascii > 126:
|
|
98
|
+
printable_string = "."
|
|
99
|
+
else:
|
|
100
|
+
printable_string = chr(byte_in_ascii)
|
|
101
|
+
|
|
102
|
+
output_file.write("{}".format(printable_string))
|
|
103
|
+
|
|
104
|
+
# Create a new line after 16 bytes for readability
|
|
105
|
+
if (output_byte_count[inetx_packet.streamid] % 16 == 0):
|
|
106
|
+
output_file.write('\n')
|
|
107
|
+
output_byte_count[inetx_packet.streamid] += 1
|
|
108
|
+
|
|
109
|
+
print("Output files created in {} directory".format(args.outdir))
|
|
110
|
+
|
|
111
|
+
|
|
112
|
+
|
|
113
|
+
if __name__ == '__main__':
|
|
114
|
+
main()
|
|
@@ -0,0 +1,95 @@
|
|
|
1
|
+
#!/usr/bin/python
|
|
2
|
+
# -*- coding: utf-8 -*-
|
|
3
|
+
"""
|
|
4
|
+
=====
|
|
5
|
+
Proxy iNetx or IENA packets to UDP
|
|
6
|
+
=====
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
"""
|
|
10
|
+
__author__ = "Diarmuid Collins"
|
|
11
|
+
__copyright__ = "Copyright 2018"
|
|
12
|
+
__version__ = "0.1.0"
|
|
13
|
+
__maintainer__ = "Diarmuid Collins"
|
|
14
|
+
__email__ = "dcollins@curtisswright.com"
|
|
15
|
+
__status__ = "Production"
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
import sys
|
|
19
|
+
sys.path.append("..")
|
|
20
|
+
import socket
|
|
21
|
+
import argparse
|
|
22
|
+
import AcraNetwork.iNetX as inetx
|
|
23
|
+
import AcraNetwork.IENA as iena
|
|
24
|
+
import AcraNetwork.McastSocket as McastSocket
|
|
25
|
+
|
|
26
|
+
VERSION = __version__
|
|
27
|
+
|
|
28
|
+
parser = argparse.ArgumentParser(description='Proxy iNetX or IENA packets to UDP')
|
|
29
|
+
parser.add_argument('--ipaddress', type=str, default="235.0.0.1", required=True, help='The multicast IP address on which the iNetX or IENA packets are being transmitted')
|
|
30
|
+
parser.add_argument('--inetx', type=int, default=None, required=False, help='Receiving iNetX packets on this UDP port. Either this argument of --iena should be supplpied')
|
|
31
|
+
parser.add_argument('--iena', type=int, default=None, required=False, help='Receiving IENA packets on this UDP port')
|
|
32
|
+
parser.add_argument('--udp', type=int, default=None, required=True, help='Transmit UDP packets on this UDP port')
|
|
33
|
+
parser.add_argument('--version', action='version', version='%(prog)s {}'.format(VERSION))
|
|
34
|
+
|
|
35
|
+
args = parser.parse_args()
|
|
36
|
+
|
|
37
|
+
if not (args.inetx or args.iena):
|
|
38
|
+
print(parser.print_help())
|
|
39
|
+
sys.exit(1)
|
|
40
|
+
|
|
41
|
+
# The incoming iNetx port
|
|
42
|
+
if args.inetx:
|
|
43
|
+
incoming_udp_port = args.inetx
|
|
44
|
+
else:
|
|
45
|
+
incoming_udp_port = args.iena
|
|
46
|
+
|
|
47
|
+
# Outgoing UDP port
|
|
48
|
+
outgoing_udp_port = args.udp
|
|
49
|
+
|
|
50
|
+
#------------------------------------------------------------
|
|
51
|
+
# Setup a socket to recieve all traffic
|
|
52
|
+
#------------------------------------------------------------
|
|
53
|
+
try:
|
|
54
|
+
recv_socket = McastSocket.McastSocket(local_port=incoming_udp_port, reuse=1)
|
|
55
|
+
recv_socket.mcast_add(args.ipaddress, '0.0.0.0')
|
|
56
|
+
recv_socket.settimeout(10)
|
|
57
|
+
except:
|
|
58
|
+
print("Can't bind to socket {} on multicast {}".format(incoming_udp_port, args.ipaddress))
|
|
59
|
+
exit()
|
|
60
|
+
|
|
61
|
+
tx_socket = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
|
|
62
|
+
|
|
63
|
+
packet_count = 1
|
|
64
|
+
|
|
65
|
+
|
|
66
|
+
while True:
|
|
67
|
+
# Capture some data
|
|
68
|
+
try:
|
|
69
|
+
data, addr = recv_socket.recvfrom(2048) # buffer size is 1500 bytes
|
|
70
|
+
except socket.timeout:
|
|
71
|
+
print("ERROR: No incoming packets received on UDP port {} on multicast {}. Timeout on socket".format(
|
|
72
|
+
incoming_udp_port, args.ipaddress))
|
|
73
|
+
exit()
|
|
74
|
+
|
|
75
|
+
(udpsrcport,srcipaddr) = addr
|
|
76
|
+
# Decode it as iNetx
|
|
77
|
+
if args.inetx:
|
|
78
|
+
avionics_packet = inetx.iNetX()
|
|
79
|
+
else:
|
|
80
|
+
avionics_packet = iena.IENA()
|
|
81
|
+
|
|
82
|
+
try:
|
|
83
|
+
avionics_packet.unpack(data)
|
|
84
|
+
except ValueError:
|
|
85
|
+
# This isn't an inetx packet
|
|
86
|
+
continue
|
|
87
|
+
else:
|
|
88
|
+
packet_count += 1
|
|
89
|
+
# Transmit the _payload back out
|
|
90
|
+
tx_socket.sendto(avionics_packet.payload, (args.ipaddress, outgoing_udp_port))
|
|
91
|
+
# Print some info for the user
|
|
92
|
+
if packet_count % 50 == 0:
|
|
93
|
+
print(".")
|
|
94
|
+
else:
|
|
95
|
+
print(".",)
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
#!/usr/bin/python
|
|
2
|
+
# -*- coding: utf-8 -*-
|
|
3
|
+
"""
|
|
4
|
+
=====
|
|
5
|
+
Multicast reception example
|
|
6
|
+
=====
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
"""
|
|
10
|
+
__author__ = "Diarmuid Collins"
|
|
11
|
+
__copyright__ = "Copyright 2018"
|
|
12
|
+
__version__ = "0.1.0"
|
|
13
|
+
__maintainer__ = "Diarmuid Collins"
|
|
14
|
+
__email__ = "dcollins@curtisswright.com"
|
|
15
|
+
__status__ = "Production"
|
|
16
|
+
|
|
17
|
+
import sys
|
|
18
|
+
sys.path.append("..")
|
|
19
|
+
|
|
20
|
+
import socket
|
|
21
|
+
import AcraNetwork.iNetX as inetx
|
|
22
|
+
import AcraNetwork.McastSocket as McastSocket
|
|
23
|
+
|
|
24
|
+
udp_port = 5567
|
|
25
|
+
|
|
26
|
+
#------------------------------------------------------------
|
|
27
|
+
# Setup a socket to recieve all traffic
|
|
28
|
+
#------------------------------------------------------------
|
|
29
|
+
try:
|
|
30
|
+
recv_socket = McastSocket.McastSocket(local_port=udp_port, reuse=1)
|
|
31
|
+
recv_socket.mcast_add('235.0.0.1', '0.0.0.0')
|
|
32
|
+
recv_socket.settimeout(10)
|
|
33
|
+
except:
|
|
34
|
+
print("Can't bind to socket {}".format(udp_port))
|
|
35
|
+
exit()
|
|
36
|
+
|
|
37
|
+
packet_count = 1
|
|
38
|
+
while True:
|
|
39
|
+
try:
|
|
40
|
+
data, addr = recv_socket.recvfrom(2048) # buffer size is 1500 bytes
|
|
41
|
+
except socket.timeout:
|
|
42
|
+
print("timeout on socket")
|
|
43
|
+
exit()
|
|
44
|
+
|
|
45
|
+
(udpsrcport,srcipaddr) = addr
|
|
46
|
+
avionics_packet = inetx.iNetX()
|
|
47
|
+
data_len = len(data)
|
|
48
|
+
try:
|
|
49
|
+
avionics_packet.unpack(data)
|
|
50
|
+
except ValueError:
|
|
51
|
+
# This isn't an inetx packet
|
|
52
|
+
packet_count += 1
|
|
53
|
+
continue
|
|
54
|
+
else:
|
|
55
|
+
print("Packet withStread ID = {:0X}".format(avionics_packet.streamid))
|