1 | # -*- coding: utf-8 -*- |
---|
2 | """ |
---|
3 | ------------------------------------------------------------------------------ |
---|
4 | Mango 802.11 Reference Design Experiments Framework |
---|
5 | - Local Traffic Generation (LTG) |
---|
6 | ------------------------------------------------------------------------------ |
---|
7 | License: Copyright 2019 Mango Communications, Inc. All rights reserved. |
---|
8 | Use and distribution subject to terms in LICENSE.txt |
---|
9 | ------------------------------------------------------------------------------ |
---|
10 | |
---|
11 | This module provides definitions for Local Traffic Generation (LTG) on |
---|
12 | wlan_exp nodes. |
---|
13 | |
---|
14 | It is assumed that users will extend these classes to create their own |
---|
15 | LTG flow configurations. When an LTG component is serialized, it follows |
---|
16 | the following structure: |
---|
17 | |
---|
18 | +------------+------------+---------------------------------+ |
---|
19 | | Word | Bits | Function | |
---|
20 | +============+============+=================================+ |
---|
21 | | [0] | [31:16] | Type | |
---|
22 | +------------+------------+---------------------------------+ |
---|
23 | | [0] | [15:0] | Length (number of 32 bit words) | |
---|
24 | +------------+------------+---------------------------------+ |
---|
25 | | [1:length] | Parameters | |
---|
26 | +------------+------------+---------------------------------+ |
---|
27 | |
---|
28 | When a LTG flow configuration is serialized, the schedule is serialized |
---|
29 | first and then the payload. |
---|
30 | """ |
---|
31 | |
---|
32 | from wlan_exp.info import InfoStruct |
---|
33 | import wlan_exp.util as util |
---|
34 | |
---|
35 | __all__ = ['Schedule', 'SchedulePeriodic', 'ScheduleUniformRandom', |
---|
36 | 'Payload', 'PayloadFixed', 'PayloadUniformRandom', 'PayloadControlResponse', |
---|
37 | 'FlowConfig', 'FlowConfigCBR', 'FlowConfigCTS', 'FlowConfigRandomRandom'] |
---|
38 | |
---|
39 | |
---|
40 | # LTG Schedule IDs - must match corresponding values in wlan_mac_ltg.h |
---|
41 | LTG_SCHED_TYPE_PERIODIC = 1 |
---|
42 | LTG_SCHED_TYPE_UNIFORM_RAND = 2 |
---|
43 | |
---|
44 | |
---|
45 | # LTG Payload IDs - must match corresponding values in wlan_mac_ltg.h |
---|
46 | LTG_PYLD_TYPE_FIXED = 1 |
---|
47 | LTG_PYLD_TYPE_UNIFORM_RAND = 2 |
---|
48 | LTG_PYLD_TYPE_ALL_ASSOC_FIXED = 3 |
---|
49 | LTG_PYLD_TYPE_CTRL_RESP = 4 |
---|
50 | |
---|
51 | # LTG Payload Min/Max |
---|
52 | # |
---|
53 | # Currently, the minimum payload size is determined by the size of the LTG |
---|
54 | # header in the payload (as of 0.96, the LTG header contains the LLC header, |
---|
55 | # a 64-bit unique sequence number and a 32-bit LTG ID). Currently, the maximum |
---|
56 | # payload size is 1500 bytes. This is a relatively arbitrary amount chosen |
---|
57 | # because 1) in 0.96 the 802.11 phy cannot transmit more than 1600 bytes total |
---|
58 | # per packet; 2) 1500 bytes is about the size of a standard Ethernet MTU. If |
---|
59 | # sizes outside the range are requested, the functions will print a warning and |
---|
60 | # adjust the value to the appropriate boundary. |
---|
61 | # |
---|
62 | LTG_PYLD_MIN = 20 |
---|
63 | LTG_PYLD_MAX = 1500 |
---|
64 | |
---|
65 | |
---|
66 | # LTG action constants - must match corresponding values in wlan_mac_ltg.h |
---|
67 | LTG_REMOVE_ALL = 0xFFFFFFFF |
---|
68 | LTG_START_ALL = 0xFFFFFFFF |
---|
69 | LTG_STOP_ALL = 0xFFFFFFFF |
---|
70 | |
---|
71 | #----------------------------------------------------------------------------- |
---|
72 | # LTG Schedules |
---|
73 | #----------------------------------------------------------------------------- |
---|
74 | class Schedule(InfoStruct): |
---|
75 | """Base class for LTG Schedules.""" |
---|
76 | ltg_type = None |
---|
77 | |
---|
78 | def __init__(self, field_defs): |
---|
79 | super(Schedule, self).__init__(field_sets=None, field_defs=field_defs) |
---|
80 | |
---|
81 | def enforce_min_resolution(self, resolution): |
---|
82 | """Updates time intervals in the schedule configuration for the minimum interval |
---|
83 | implemented by the node. Interval/duration values will be rounded down |
---|
84 | to the nearest multiple of the node's LTG polling interval. |
---|
85 | |
---|
86 | Subclasses of the Schedule class must implement their own verson of this method. |
---|
87 | |
---|
88 | Args: resolution (int): Resolution of the schedule |
---|
89 | """ |
---|
90 | raise NotImplementedError |
---|
91 | |
---|
92 | class SchedulePeriodic(Schedule): |
---|
93 | """LTG Schedule that will generate a payload every 'interval' seconds, stopping |
---|
94 | after 'duration' seconds. Both arguments are float seconds. These values are |
---|
95 | converted to microseconds for the command message to the node. |
---|
96 | |
---|
97 | Args: |
---|
98 | |
---|
99 | interval (float): Interval between packets in float seconds; actual packet |
---|
100 | creation interval will be quantized to the LTG polling interval in CPU |
---|
101 | High (typically 64 usec) usec. |
---|
102 | |
---|
103 | duration (float, optional): Duration of the traffic flow in float |
---|
104 | seconds |
---|
105 | """ |
---|
106 | |
---|
107 | # Must match _ltg_sched_periodic_params_otw typedef |
---|
108 | struct_fields = [ |
---|
109 | ('interval', 'I', 'uint32', 'Interval between packet creation, in microseconds'), |
---|
110 | ('duration', 'Q', 'uint64', 'Duration for traffic flow, in microseconds'), |
---|
111 | ] |
---|
112 | |
---|
113 | def __init__(self, interval, duration=None): |
---|
114 | super(SchedulePeriodic, self).__init__(field_defs=self.struct_fields) |
---|
115 | |
---|
116 | self.ltg_type = LTG_SCHED_TYPE_PERIODIC |
---|
117 | |
---|
118 | # Convert argument from float seconds to integer microseconds |
---|
119 | self['interval'] = int(float(interval) * 10**6) |
---|
120 | |
---|
121 | if duration is None: |
---|
122 | # LTG schedule runs forever by default |
---|
123 | self['duration'] = 0 |
---|
124 | else: |
---|
125 | # Convert argument from float seconds to integer microseconds |
---|
126 | self['duration'] = int(float(duration) * 10**6) |
---|
127 | |
---|
128 | def enforce_min_resolution(self, resolution): |
---|
129 | """Enforce the minimum resolution on the Schedule. |
---|
130 | |
---|
131 | Args: |
---|
132 | resolution (int): Resolution of the schedule |
---|
133 | """ |
---|
134 | temp_interval = (self['interval'] // resolution) * resolution |
---|
135 | if (temp_interval != self['interval']): |
---|
136 | # Set to True to print warning message when adjusting interval |
---|
137 | if (False): |
---|
138 | msg = "WARNING: Cannot schedule LTG with interval: {0} us\n".format(self['interval']) |
---|
139 | msg += " Minimum LTG resolution is {0} us.\n".format(resolution) |
---|
140 | msg += " Adjusting interval to {0} us".format(temp_interval) |
---|
141 | print(msg) |
---|
142 | self['interval'] = temp_interval |
---|
143 | |
---|
144 | # End Class WlanExpLTGSchedPeriodic |
---|
145 | |
---|
146 | |
---|
147 | class ScheduleUniformRandom(Schedule): |
---|
148 | """LTG Schedule that will generate a payload a uniformly random time |
---|
149 | between min_interval and max_interval microseconds. |
---|
150 | |
---|
151 | Args: |
---|
152 | min_interval (float): Minimum interval between packets (in float seconds); |
---|
153 | actual packet creation interval will be quantized |
---|
154 | to the interval of the fast timer in CPU High. |
---|
155 | Currently this interval is 64 usec. |
---|
156 | max_interval (float): Maximum interval between packets (in float seconds); |
---|
157 | actual packet creation interval will be quantized |
---|
158 | to the interval of the fast timer in CPU High. |
---|
159 | Currently this interval is 64 usec. |
---|
160 | duration (float, optional): Duration of the traffic flow (in float seconds) |
---|
161 | """ |
---|
162 | # Must match _ltg_sched_uniform_rand_params_otw typedef |
---|
163 | struct_fields = [ |
---|
164 | ('min_interval', 'I', 'uint32', 'Minimum interval between packet creation, in microseconds'), |
---|
165 | ('max_interval', 'I', 'uint32', 'Maximum interval between packet creation, in microseconds'), |
---|
166 | ('duration', 'Q', 'uint64', 'Duration for traffic flow, in microseconds'), |
---|
167 | ] |
---|
168 | |
---|
169 | def __init__(self, min_interval, max_interval, duration=None): |
---|
170 | super(ScheduleUniformRandom, self).__init__(field_defs=self.struct_fields) |
---|
171 | |
---|
172 | self.ltg_type = LTG_PYLD_TYPE_UNIFORM_RAND |
---|
173 | |
---|
174 | # Convert intervals from float seconds to integer usec |
---|
175 | self['min_interval'] = int(float(min_interval) * 10**6) |
---|
176 | self['max_interval'] = int(float(max_interval) * 10**6) |
---|
177 | |
---|
178 | if duration is None: |
---|
179 | # Default duration runs forever |
---|
180 | self['duration'] = 0 |
---|
181 | else: |
---|
182 | self['duration'] = int(float(duration) * 10**6) |
---|
183 | |
---|
184 | def enforce_min_resolution(self, resolution): |
---|
185 | """Enforce the minimum resolution on the Schedule. |
---|
186 | |
---|
187 | Args: |
---|
188 | resolution (int): Resolution of the scheduler in usec |
---|
189 | """ |
---|
190 | # Apply the scheduler resolution to all interval fields |
---|
191 | for f in ['min_interval', 'max_interval']: |
---|
192 | temp_interval = (self[f] // resolution) * resolution |
---|
193 | if (temp_interval != self[f]): |
---|
194 | if (False): |
---|
195 | msg = "WARNING: Cannot schedule LTG with {0} = {1} usec\n".format(f, self[f]) |
---|
196 | msg += " Minimum LTG resolution is {0} usec.\n".format(resolution) |
---|
197 | msg += " Adjusting {0} to {1} usec".format(f, temp_interval) |
---|
198 | print(msg) |
---|
199 | self[f] = temp_interval |
---|
200 | |
---|
201 | #----------------------------------------------------------------------------- |
---|
202 | # LTG Payloads |
---|
203 | #----------------------------------------------------------------------------- |
---|
204 | class Payload(InfoStruct): |
---|
205 | """Base class for LTG Payloads.""" |
---|
206 | ltg_type = None |
---|
207 | |
---|
208 | def __init__(self, field_defs): |
---|
209 | # InfoStruct constructor accepts either pre-defined fields in Info.py via 'field_sets' |
---|
210 | # or caller-specified fields via 'field_defs' |
---|
211 | super(Payload, self).__init__(field_sets=None, field_defs=field_defs) |
---|
212 | |
---|
213 | def validate_length(self, length): |
---|
214 | """Contrains a requested length to the min/max supported LTG payload lengths |
---|
215 | """ |
---|
216 | msg = "WARNING: Adjusting LTG Payload length from {0} ".format(length) |
---|
217 | |
---|
218 | if (length < LTG_PYLD_MIN): |
---|
219 | msg += "to {0} (min).".format(LTG_PYLD_MIN) |
---|
220 | print(msg) |
---|
221 | return LTG_PYLD_MIN |
---|
222 | |
---|
223 | if (length > LTG_PYLD_MAX): |
---|
224 | msg += "to {0} (max).".format(LTG_PYLD_MAX) |
---|
225 | print(msg) |
---|
226 | return LTG_PYLD_MAX |
---|
227 | |
---|
228 | return length |
---|
229 | |
---|
230 | class PayloadFixed(Payload): |
---|
231 | """LTG payload that will generate a fixed payload of the given length. |
---|
232 | |
---|
233 | Args: |
---|
234 | dest_addr (int): Destination MAC address |
---|
235 | length (int): Minimum length of the LTG payload (in bytes) |
---|
236 | |
---|
237 | """ |
---|
238 | # Must match ltg_pyld_fixed typedef |
---|
239 | struct_fields = [ |
---|
240 | ('type', 'I', 'uint32', 'LTG Payload spec type'), |
---|
241 | ('dest_addr', '6s', '6uint8', 'MAC address for destination of LTG flow'), |
---|
242 | ('length', 'H', 'uint16', 'Length of payload in bytes for LTG packets'), |
---|
243 | ] |
---|
244 | |
---|
245 | def __init__(self, dest_addr, length): |
---|
246 | super(PayloadFixed, self).__init__(field_defs=self.struct_fields) |
---|
247 | |
---|
248 | self['type'] = LTG_PYLD_TYPE_FIXED |
---|
249 | self['dest_addr'] = util.mac_addr_to_byte_str(dest_addr) |
---|
250 | self['length'] = self.validate_length(length) |
---|
251 | |
---|
252 | class PayloadControlResponse(Payload): |
---|
253 | """LTG payload that will generate an ACK or CTS. |
---|
254 | |
---|
255 | Args: |
---|
256 | dest_addr (int): Destination MAC address |
---|
257 | subtype (int): 0 for CTS, 1 for ACK |
---|
258 | duration (int): duration/ID value (ignored in ACK) |
---|
259 | |
---|
260 | """ |
---|
261 | # Must match ltg_pyld_ctrl_resp typedef |
---|
262 | struct_fields = [ |
---|
263 | ('type', 'I', 'uint32', 'LTG Payload spec type'), |
---|
264 | ('dest_addr', '6s', '6uint8', 'MAC address for destination of LTG flow'), |
---|
265 | ('subtype', 'H', 'uint16', 'Subtype, 0 for CTS, 1 for ACK'), |
---|
266 | ('duration', 'H', 'uint16', 'DURATION value for MAC header, ignored for ACKs'), |
---|
267 | ] |
---|
268 | |
---|
269 | def __init__(self, dest_addr, subtype, duration): |
---|
270 | super(PayloadControlResponse, self).__init__(field_defs=self.struct_fields) |
---|
271 | |
---|
272 | self['type'] = LTG_PYLD_TYPE_CTRL_RESP |
---|
273 | self['dest_addr'] = util.mac_addr_to_byte_str(dest_addr) |
---|
274 | self['subtype'] = subtype |
---|
275 | self['duration'] = duration |
---|
276 | |
---|
277 | class PayloadUniformRandom(Payload): |
---|
278 | """LTG payload that will generate a payload with uniformly random size |
---|
279 | between min_length and max_length bytes. |
---|
280 | |
---|
281 | Args: |
---|
282 | dest_addr (int): Destination MAC address |
---|
283 | min_length (int): Minimum length of the LTG payload (in bytes) |
---|
284 | max_length (int): Maximum length of the LTG payload (in bytes) |
---|
285 | """ |
---|
286 | |
---|
287 | # Must match ltg_pyld_uniform_rand typedef |
---|
288 | struct_fields = [ |
---|
289 | ('type', 'I', 'uint32', 'LTG Payload spec type'), |
---|
290 | ('dest_addr', '6s', '6uint8', 'MAC address for destination of LTG flow'), |
---|
291 | ('min_length', 'H', 'uint16', 'Minimum LTG payload length, in bytes'), |
---|
292 | ('max_length', 'H', 'uint16', 'Maximum LTG payload length, in bytes'), |
---|
293 | ] |
---|
294 | |
---|
295 | def __init__(self, dest_addr, min_length, max_length): |
---|
296 | super(PayloadUniformRandom, self).__init__(field_defs=self.struct_fields) |
---|
297 | |
---|
298 | self['type'] = LTG_PYLD_TYPE_UNIFORM_RAND |
---|
299 | self['dest_addr'] = util.mac_addr_to_byte_str(dest_addr) |
---|
300 | self['min_length'] = self.validate_length(min_length) |
---|
301 | self['max_length'] = self.validate_length(max_length) |
---|
302 | |
---|
303 | class PayloadAllAssocFixed(Payload): |
---|
304 | """LTG payload that will generate a fixed payload of the given length |
---|
305 | to all associated deivces. |
---|
306 | |
---|
307 | Args: |
---|
308 | length (int): Length of the LTG payload (in bytes) |
---|
309 | """ |
---|
310 | # Must match ltg_pyld_all_assoc_fixed_t typedef |
---|
311 | struct_fields = [ |
---|
312 | ('type', 'I', 'uint32', 'LTG Payload spec type'), |
---|
313 | ('length', 'H', 'uint16', 'LTG payload length, in bytes'), |
---|
314 | ] |
---|
315 | |
---|
316 | def __init__(self, length): |
---|
317 | super(PayloadAllAssocFixed, self).__init__(field_defs=self.struct_fields) |
---|
318 | |
---|
319 | self['type'] = LTG_PYLD_TYPE_ALL_ASSOC_FIXED |
---|
320 | self['length'] = self.validate_length(length) |
---|
321 | |
---|
322 | #----------------------------------------------------------------------------- |
---|
323 | # LTG Flow Configurations |
---|
324 | #----------------------------------------------------------------------------- |
---|
325 | class FlowConfig(object): |
---|
326 | """Base class for LTG Flow Configurations.""" |
---|
327 | ltg_schedule = None |
---|
328 | ltg_payload = None |
---|
329 | |
---|
330 | def serialize(self): |
---|
331 | """Converts the FlowConfig object to a byte array suitable for inclusion |
---|
332 | in a wlan_exp command payload |
---|
333 | """ |
---|
334 | |
---|
335 | # Concatenate and serialize the schedule and payload InfoStructs |
---|
336 | r = self.ltg_schedule + self.ltg_payload |
---|
337 | return r.serialize() |
---|
338 | |
---|
339 | def enforce_min_resolution(self, resolution): |
---|
340 | """Enforce the minimum resolution on time intervals in the LTG Schedule. |
---|
341 | |
---|
342 | Args: |
---|
343 | resolution (int): Minimum time interval supported by hardware, in usec |
---|
344 | """ |
---|
345 | self.ltg_schedule.enforce_min_resolution(resolution) |
---|
346 | |
---|
347 | |
---|
348 | class FlowConfigCBR(FlowConfig): |
---|
349 | """Class to implement a Constant Bit Rate LTG flow configuration to a given device. |
---|
350 | |
---|
351 | Args: |
---|
352 | dest_addr (int): Destination MAC address |
---|
353 | payload_length (int): Length of the LTG payload (in bytes) |
---|
354 | interval (float): Interval between packets (in float seconds); |
---|
355 | actual packet creation interval will be quantized |
---|
356 | to the interval of the fast timer in CPU High. |
---|
357 | Currently this interval is 64 usec. |
---|
358 | duration (float): Duration of the traffic flow (in float seconds) |
---|
359 | """ |
---|
360 | def __init__(self, dest_addr, payload_length, interval, duration=None): |
---|
361 | self.ltg_schedule = SchedulePeriodic(interval, duration) |
---|
362 | self.ltg_payload = PayloadFixed(dest_addr, payload_length) |
---|
363 | |
---|
364 | # End Class FlowConfigCBR |
---|
365 | |
---|
366 | class FlowConfigCTS(FlowConfig): |
---|
367 | """Class to implement a CTS flow configuration to a given device. |
---|
368 | |
---|
369 | Args: |
---|
370 | dest_addr (int): Destination MAC address |
---|
371 | interval (float): Interval between packets (in float seconds); |
---|
372 | actual packet creation interval will be quantized |
---|
373 | to the interval of the fast timer in CPU High. |
---|
374 | Currently this interval is 64 usec. |
---|
375 | duration (float): Duration of the traffic flow (in float seconds) |
---|
376 | duration_id (int): Value to be included in CTS Duration/ID field |
---|
377 | """ |
---|
378 | def __init__(self, dest_addr, interval, duration=None, duration_id=None): |
---|
379 | self.ltg_schedule = SchedulePeriodic(interval, duration) |
---|
380 | self.ltg_payload = PayloadControlResponse(dest_addr, 0, duration_id) #The 0 represents a CTS |
---|
381 | |
---|
382 | # End Class FlowConfigCTS |
---|
383 | |
---|
384 | class FlowConfigACK(FlowConfig): |
---|
385 | """Class to implement a ACK flow configuration to a given device. |
---|
386 | |
---|
387 | Args: |
---|
388 | dest_addr (int): Destination MAC address |
---|
389 | interval (float): Interval between packets (in float seconds); |
---|
390 | actual packet creation interval will be quantized |
---|
391 | to the interval of the fast timer in CPU High. |
---|
392 | Currently this interval is 64 usec. |
---|
393 | duration (float): Duration of the traffic flow (in float seconds) |
---|
394 | """ |
---|
395 | def __init__(self, dest_addr, interval, duration=None): |
---|
396 | self.ltg_schedule = SchedulePeriodic(interval, duration) |
---|
397 | self.ltg_payload = PayloadControlResponse(dest_addr, 1, 0) #The 1 represents a CTS |
---|
398 | |
---|
399 | # End Class FlowConfigCTS |
---|
400 | |
---|
401 | |
---|
402 | class FlowConfigAllAssocCBR(FlowConfig): |
---|
403 | """Class to implement a Constant Bit Rate LTG flow configuration to all associated |
---|
404 | devices. |
---|
405 | |
---|
406 | Args: |
---|
407 | payload_length (int): Length of the LTG payload (in bytes) |
---|
408 | interval (float): Interval between packets (in float seconds); |
---|
409 | actual packet creation interval will be quantized |
---|
410 | to the interval of the fast timer in CPU High. |
---|
411 | Currently this interval is 64 usec. |
---|
412 | duration (float): Duration of the traffic flow (in float seconds) |
---|
413 | """ |
---|
414 | def __init__(self, payload_length, interval, duration=None): |
---|
415 | self.ltg_schedule = SchedulePeriodic(interval, duration) |
---|
416 | self.ltg_payload = PayloadAllAssocFixed(payload_length) |
---|
417 | |
---|
418 | # End Class FlowConfigAllAssocCBR |
---|
419 | |
---|
420 | class FlowConfigRandomRandom(FlowConfig): |
---|
421 | """Class to implement an LTG flow configuration with random period and random |
---|
422 | sized payload to a given device. |
---|
423 | |
---|
424 | Args: |
---|
425 | dest_addr (int): Destination MAC address |
---|
426 | min_payload_length (int): Minimum length of the LTG payload (in bytes) |
---|
427 | max_payload_length (int): Maximum length of the LTG payload (in bytes) |
---|
428 | min_interval (float): Minimum interval between packets (in float seconds); |
---|
429 | actual packet creation interval will be quantized |
---|
430 | to the interval of the fast timer in CPU High. |
---|
431 | Currently this interval is 64 usec. |
---|
432 | max_interval (float): Maximum interval between packets (in float seconds) |
---|
433 | actual packet creation interval will be quantized |
---|
434 | to the interval of the fast timer in CPU High. |
---|
435 | Currently this interval is 64 usec. |
---|
436 | duration (float, optional): Duration of the traffic flow (in float seconds) |
---|
437 | """ |
---|
438 | def __init__(self, dest_addr, min_payload_length, max_payload_length, min_interval, max_interval, duration=None): |
---|
439 | self.ltg_schedule = ScheduleUniformRandom(min_interval, max_interval, duration) |
---|
440 | self.ltg_payload = PayloadUniformRandom(dest_addr, min_payload_length, max_payload_length) |
---|
441 | |
---|
442 | # End Class FlowConfigRandom |
---|
443 | |
---|
444 | |
---|
445 | |
---|
446 | |
---|
447 | |
---|
448 | |
---|
449 | |
---|
450 | |
---|
451 | |
---|
452 | |
---|
453 | |
---|
454 | |
---|
455 | |
---|
456 | |
---|
457 | |
---|
458 | |
---|
459 | |
---|
460 | |
---|
461 | |
---|
462 | |
---|
463 | |
---|
464 | |
---|
465 | |
---|
466 | |
---|
467 | |
---|
468 | |
---|
469 | |
---|
470 | |
---|
471 | |
---|
472 | |
---|
473 | |
---|