blob: e163cc3dbdd64cd99e4e4afbc584c68b188274ed [file] [log] [blame]
Holger Hans Peter Freyther38adaa92018-01-20 19:09:47 +00001# osmo_ms_driver: A cumululative distribution function class.
2# Help to start processes over time.
3#
4# Copyright (C) 2018 by Holger Hans Peter Freyther
5#
6# This program is free software: you can redistribute it and/or modify
7# it under the terms of the GNU General Public License as
8# published by the Free Software Foundation, either version 3 of the
9# License, or (at your option) any later version.
10#
11# This program is distributed in the hope that it will be useful,
12# but WITHOUT ANY WARRANTY; without even the implied warranty of
13# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14# GNU General Public License for more details.
15#
16# You should have received a copy of the GNU General Public License
17# along with this program. If not, see <http://www.gnu.org/licenses/>.
18
19
20from datetime import timedelta
21
22class DistributionFunctionHandler(object):
23 """
24 The goal is to start n "mobile" processes. We like to see some
25 conflicts (RACH bursts being ignored) but starting n processes
26 at the same time is not a realistic model.
27 We use the concept of cumulative distribution function here. On
28 the x-axis we have time (maybe in steps of 10ms) and on the
29 y-axis we have the percentage (from 0.0 to 1.0) of how many
30 processes should run at the given time.
31 """
32
33 def __init__(self, step, duration, fun):
34 self._step = step
35 self._fun = fun
36 self._x = 0.0
37 self._y = self._fun(self._x)
38 self._target = 1.0
39 self._duration = duration
40
41 def step_size(self):
42 return self._step
43
44 def set_target(self, scale):
45 """
46 Scale the percentage to the target value..
47 """
48 self._target = scale
49
50 def is_done(self):
51 return self._y >= 1.0
52
53 def current_value(self):
54 return self._y
55
56 def current_scaled_value(self):
57 return self._y * self._target
58
59 def step_once(self):
60 self._x = self._x + self._step.total_seconds()
61 self._y = self._fun(self._x)
62
63 def duration(self):
64 return self._duration
65
66
67def immediate(step_size=timedelta(milliseconds=20)):
68 """
69 Reaches 100% at the first step.
70 """
71 duration = timedelta(seconds=0)
72 return DistributionFunctionHandler(step_size, duration, lambda x: 1)
73
74def linear_with_slope(slope, duration, step_size=timedelta(milliseconds=20)):
75 """
76 Use the slope and step size you want
77 """
78 return DistributionFunctionHandler(step_size, duration, lambda x: slope*x)
79
80def linear_with_duration(duration, step_size=timedelta(milliseconds=20)):
81 """
82 Linear progression that reaches 100% after duration.total_seconds()
83 """
84 slope = 1.0/duration.total_seconds()
85 return linear_with_slope(slope, duration, step_size)
86
87def _in_out(x):
88 """
89 Internal in/out function inspired by Qt
90 """
91 assert x <= 1.0
92 # Needs to be between 0..1 and increase first
93 if x < 0.5:
94 return (x*x) * 2
95 # deaccelerate now. in_out(0.5) == 0.5, in_out(1.0) == 1.0
96 x = x * 2 - 1
97 return -0.5 * (x*(x-2)- 1)
98
99def ease_in_out_duration(duration, step_size=timedelta(milliseconds=20)):
100 """
101 Example invocation
102 """
103 scale = 1.0/duration.total_seconds()
104 return DistributionFunctionHandler(step_size, duration,
105 lambda x: _in_out(x*scale))
Holger Hans Peter Freyther0f0ebd82018-06-23 14:40:42 +0100106
107
108cdfs = {
109 'immediate': lambda x,y: immediate(y),
110 'linear': linear_with_duration,
111 'ease_in_out': ease_in_out_duration,
112}