build.rsudp.packetloss   A
last analyzed

Complexity

Total Complexity 15

Size/Duplication

Total Lines 162
Duplicated Lines 0 %

Importance

Changes 0
Metric Value
wmc 15
eloc 80
dl 0
loc 162
rs 10
c 0
b 0
f 0

4 Functions

Rating   Name   Duplication   Size   Complexity  
B main() 0 57 6
A printTTLS() 0 18 2
A signal_handler() 0 11 1
B run() 0 52 6
1
import sys
2
import getopt
3
import signal
4
from rsudp import raspberryshake, printM, printW, printE, add_debug_handler
5
6
# some globals
7
DPtime = {}
8
timeStart = {}
9
DPttlLoss = {}
10
11
def signal_handler(signal, frame):
12
	'''
13
	The signal handler for the CTRL+C keystroke.
14
15
	:param int signum: signal number
16
	:param int frame: frame number
17
18
	'''
19
	print()
20
	printM("Quitting...")
21
	sys.exit(0)
22
23
signal.signal(signal.SIGINT, signal_handler)
24
25
def printTTLS(CHAN, TR):
26
	'''
27
	Report packets lost.
28
29
	:param int CHAN: The name of the channel to report packetloss statistics for
30
	:param int TR: Transmission rate in milliseconds between consecutive packets from a specific channel
31
	:rtype: bool
32
	:return: ``False`` if no time has elapsed since starting the program, otherwise ``True``
33
34
	'''
35
	ttlSecs = int(DPtime[CHAN] - timeStart[CHAN])
36
	if ttlSecs == 0:
37
		return False		# only once in any given second
38
	ttlDPs = ttlSecs * TR
39
	pct = float(float(DPttlLoss[CHAN]) / float(ttlDPs)) * 100.
40
	printM('CHANNEL %s: total packets lost in last %s seconds: %s ( %s%% / %s )' %
41
							(CHAN, ttlSecs, DPttlLoss[CHAN], round(pct, 2), ttlDPs))
42
	return True
43
44
def run(printFREQ=60, port=8888):
45
	'''
46
	Initialize stream and print constants, then process data for packet loss.
47
48
	:param int printFREQ: Value in seconds denoting the frequency with which this program will report packets lost
49
	:param int port: Local port to listen on
50
51
	'''
52
	global DPtime, DPttlLoss
53
	printM("Initializing...")
54
	raspberryshake.initRSlib(dport=port, rsstn='Z0000')	# runs in quiet mode; suppresses needless output but shows errors
55
	add_debug_handler()									# now start console output
56
	# initialize data stream constants
57
	printM('Opened data port successfully.')
58
	DP = raspberryshake.getDATA()
59
	CHAN = raspberryshake.getCHN(DP)					# first channel - doesn't matter which, used to stop looping
60
	TR = raspberryshake.tf								# transmission rate - in milliseconds
61
	TRE = (TR+TR*.5) / 1000.							# time diff / error to identify a missed packet
62
	SR = raspberryshake.sps								# sample / second
63
	ttlCHN = raspberryshake.getTTLCHN()					# total number of channels
64
	printM("	Total Channels: %s" % ttlCHN)
65
	printM("	   Sample Rate: %s samples / second" % SR)
66
	printM("	       TX Rate: Every %s milliseconds" % TR)
67
	
68
	# start processing data packets for packet loss detection
69
	# initialize
70
	chnNum = 0
71
	while chnNum < ttlCHN:
72
		DP = raspberryshake.getDATA()
73
		CHAN = raspberryshake.getCHN(DP)
74
		DPtime[CHAN] = raspberryshake.getTIME(DP)
75
		timeStart[CHAN] = DPtime[CHAN]
76
		DPttlLoss[CHAN] = 0
77
		chnNum += 1
78
	
79
	printM('Data Packet reading begun.')
80
	printM('Will report any DP loss as it happens and totals every %s seconds.' % printFREQ)
81
82
	while 1:                                # loop forever
83
		DP = raspberryshake.getDATA()
84
		CHAN = raspberryshake.getCHN(DP)
85
		timeS = raspberryshake.getTIME(DP)
86
		timeD = timeS - DPtime[CHAN]
87
		if abs(timeD) > TRE:
88
			printM("DP loss of %s second(s) Current TS: %s, Previous TS: %s" % (round(timeD, 3), timeS, DPtime[CHAN]))
89
			DPttlLoss[CHAN] += abs(int(timeD * TR))
90
		DPtime[CHAN] = timeS 
91
	
92
		if int(timeS) % printFREQ == 0:
93
			if printTTLS(CHAN, TR):
94
				timeStart[CHAN] = timeS
95
				DPttlLoss[CHAN] = 0
96
97
98
def main():
99
	'''
100
	When run from the command line, pass in a value of seconds as an argument
101
	to set the packet loss for reporting period.
102
103
	for example, to report packet loss statistics every hour, run the following command
104
	(if rsudp is installed in your environment, i.e. activate using ``conda activate rsudp``, then):
105
106
	.. code-block:: bash
107
108
		rs-packetloss -s 3600 -p 18001
109
110
	'''
111
112
	hlp_txt = '''
113
########################################################
114
##            R A S P B E R R Y  S H A K E            ##
115
##              UDP Packet Loss Reporter              ##
116
##                  by Richard Boaz                   ##
117
##                   Copyleft 2019                    ##
118
##                                                    ##
119
## Reports data packet loss over a specified period   ##
120
## of seconds.                                        ##
121
##                                                    ##
122
## Supply -p (port) and -f (frequency) to change      ##
123
## the port and frequency to report packet loss       ##
124
## statistics.                                        ##
125
##                                                    ##
126
## Requires:                                          ##
127
## - rsudp                                            ##
128
##                                                    ##
129
## The following example sets the port to 18001       ##
130
## and report frequency to 1 hour                     ##
131
##                                                    ##
132
########################################################
133
##                                                    ##
134
##    $ rs-packetloss -p 18001 -f 3600                ##
135
##                                                    ##
136
########################################################
137
138
	'''
139
140
	f, p = 60, 8888
141
	opts, args = getopt.getopt(sys.argv[1:], 'hp:f:', ['help', 'port=', 'frequency='])
142
	for o, a in opts:
143
		if o in ('-h, --help'):
144
			print(hlp_txt)
145
			exit(0)
146
		if o in ('-p', 'port='):
147
			p = int(a)
148
		if o in ('-f', 'frequency='):
149
			f = int(a)
150
	try:
151
		run(printFREQ=f, port=p)
152
	except KeyboardInterrupt:
153
		print('')
154
		printM('Quitting...')
155
156
157
if __name__== "__main__":
158
	'''
159
	Calls the main function.
160
	'''
161
	main()
162