]> git.llucax.com Git - z.facultad/75.59/weathemulator.git/blob - src/sensor.py
con la poca inspiracionq ue tengo intento documentar un poco.
[z.facultad/75.59/weathemulator.git] / src / sensor.py
1 #!/usr/bin/env python
2 # -*- coding: iso-8859-1 -*-
3 # vim: set expandtab tabstop=4 shiftwidth=4 :
4 #----------------------------------------------------------------------------
5 #                               Weathemulator
6 #----------------------------------------------------------------------------
7 # This file is part of weathemulator.
8 #
9 # weathemulator is free software; you can redistribute it and/or modify it
10 # under the terms of the GNU General Public License as published by the Free
11 # Software Foundation; either version 2 of the License, or (at your option)
12 # any later version.
13 #
14 # weathemulator is distributed in the hope that it will be useful, but
15 # WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
16 # or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
17 # more details.
18 #
19 # You should have received a copy of the GNU General Public License along
20 # with weathemulator; if not, write to the Free Software Foundation, Inc., 59
21 # Temple Place, Suite 330, Boston, MA  02111-1307  USA
22 #----------------------------------------------------------------------------
23 # Creado:  sáb oct  1 18:26:32 ART 2005
24 # Autores: Leandro Lucarella <llucare@fi.uba.ar>
25 #----------------------------------------------------------------------------
26
27 import time
28 import math
29 import random
30 import threading
31 from sync import *
32
33
34 __all__ = ('SensorDireccionViento', 'SensorHumedad', 'SensorPresion',
35            'SensorTemperatura', 'SensorVelocidadViento')
36
37
38 DEBUG = False
39
40
41 def dprint(msg):
42     if DEBUG:
43         print msg
44
45
46 class Sensor(Synchronizable, threading.Thread):
47
48     valorActual = SyncProp("Valor actualmente medido por el sensor")
49
50     def __init__(self, sigma, periodo):
51         threading.Thread.__init__(self)
52         self.rnd = random.Random()
53         self.sigma = sigma
54         self.periodo = periodo
55         self.mu = self.initMu()
56         self.valorActual = self.getValor()
57
58     def initMu(self):
59         raise NotImplementedError # Abstracto
60
61     def getValor(self):
62         "Simula la medición del dispositivo físico."
63         self.mu = self.rnd.gauss(self.mu, self.sigma)
64         return self.mu
65
66     def run(self):
67         #TODO Alguna forma de pausarlo
68         while 1:
69             #TODO Hacer algún signal al monitor de los sensores
70             time.sleep(self.periodo)
71             self.valorActual = self.getValor()
72             dprint(self)
73
74     def __str__(self):
75         return self.valorActual
76
77     def __repr__(self):
78         return "<%s valorActual=%s>" % (self.__class__.__name__, self)
79
80
81 class SensorDireccionViento(Sensor):
82
83     N  = 0
84     NE = 1
85     E  = 2
86     SE = 3
87     S  = 4
88     SO = 5
89     O  = 6
90     NO = 7
91     ROSA = (N, NE, E, SE, S, SO, O, NO)
92
93     def __init__(self):
94         Sensor.__init__(self, sigma=0.01, periodo=0.1)
95
96     def rndAngle(self, base, arco):
97         return (base + arco * (self.rnd.random() - 0.5)) % math.pi
98
99     def initMu(self):
100         return self.rndAngle(0, 2 * math.pi)
101
102     def getValor(self):
103         self.mu = self.rndAngle(self.mu, self.sigma)
104         return SensorDireccionViento.ROSA[int(round(self.mu))]
105
106     def direccionToString(direccion):
107         if direccion == 0:
108             return "Norte"
109         if direccion == 1:
110             return "Noreste"
111         if direccion == 2:
112             return "Este"
113         if direccion == 3:
114             return "Sudeste"
115         if direccion == 4:
116             return "Sur"
117         if direccion == 5:
118             return "Sudoeste"
119         if direccion == 6:
120             return "Oeste"
121         if direccion == 7:
122             return "Noroeste"
123         raise NameError
124     direccionToString = staticmethod(direccionToString)
125
126     def __str__(self):
127         return SensorDireccionViento.direccionToString(self.valorActual)
128
129     def __repr__(self):
130         return "<%s dirección=%s valorActual=%d>" % (self.__class__.__name__,
131             self, self.valorActual)
132
133
134 class SensorCalibrado(Sensor):
135
136     valorBruto = SyncProp("Valor en bruto leido del dispositivo.")
137     valorBajo = SyncProp("Valor bajo de calibrado como tupla (valorBruto, " \
138         "valorCalibrado).")
139     valorAlto = SyncProp("Valor alto de calibrado como tupla (valorBruto, " \
140         "valorCalibrado).")
141
142     def __init__(self, valorBajo, valorAlto, sigma, periodo):
143         self.valorBajo = valorBajo
144         self.valorAlto = valorAlto
145         Sensor.__init__(self, sigma, periodo)
146
147     def getValor(self):
148         "Simula la medición del dispositivo físico."
149         self.valorBruto = self.mu = Sensor.getValor(self)
150         (x1, y1) = self.valorBajo
151         (x2, y2) = self.valorAlto
152         return (y2 - y1) / (x2 - x1) * (self.mu - x1) + y1
153
154     def __repr__(self):
155         return "<%s valorActual=%s valorBruto=%f valorBajo=%s valorAlto=%s>" \
156             % (self.__class__.__name__, self, self.valorBruto, self.valorBajo,
157                 self.valorAlto)
158
159
160 class SensorHistorico(SensorCalibrado):
161
162     def __init__(self, valorBajo, valorAlto, sigma, periodo):
163         # Listado de los valores de las últimas 24hs (inicializado con None)
164         self.historial = [(None, None) for i in xrange(int(24 * 60 * 60 / periodo))]
165         SensorCalibrado.__init__(self, valorBajo, valorAlto, sigma, periodo)
166
167     def getValor(self):
168         "Simula la medición del dispositivo físico."
169         nuevo = SensorCalibrado.getValor(self)
170         hora = time.strftime("%c")
171         self.actualizarHistorial(nuevo, hora)
172         return nuevo
173
174     def actualizarHistorial(self, valor, hora):
175         self.historial.pop(0)
176         self.historial.append((valor, hora))
177     actualizarHistorial = synchronized('historialLock')(actualizarHistorial)
178
179     def getMin(self):
180         minimo = (None, None)
181         for (valor, hora) in self.historial:
182             if minimo[0] is None or valor <= minimo[0]:
183                 minimo = (valor, hora)
184         return minimo
185     getMin = synchronized('historialLock')(getMin)
186
187     def getMax(self):
188         maximo = (None, None)
189         for (valor, hora) in self.historial:
190             if valor >= maximo[0]:
191                 maximo = (valor, hora)
192         return maximo
193     getMax = synchronized('historialLock')(getMax)
194
195     valorMinimo = property(getMin, doc="Mínimo de las últimas 24hs como " \
196         "tupla (minimo, hora).")
197     valorMaximo = property(getMax, doc="Máximo de las últimas 24hs como " \
198         "tupla (maximo, hora).")
199
200
201 class SensorHumedad(SensorHistorico):
202
203     def __init__(self):
204         SensorHistorico.__init__(self, (0.0, 0.0), (100.0, 100.0), 0.3, 300.0)
205
206     def initMu(self):
207         return self.rnd.gauss(70.0, 5.0)
208
209
210 class SensorVelocidadViento(SensorHistorico):
211
212     def __init__(self):
213         SensorHistorico.__init__(self, (0.0, 0.0), (100.0, 100.0), 0.1, 0.5)
214
215     def initMu(self):
216         return self.rnd.gauss(30.0, 8.0)
217
218
219 class SensorTendencia(SensorHistorico):
220
221     def __init__(self, cantidadTendencia, valorBajo, valorAlto, sigma, periodo):
222         """cantidadTendencia es la cantidad de valores del historial a tomar al
223         calcular la tendencia."""
224         # Listado de los valores de las últimas 24hs (inicializado con None)
225         self.cantidadTendencia = cantidadTendencia
226         SensorHistorico.__init__(self, valorBajo, valorAlto, sigma, periodo)
227
228     def getValoresTendencia(self):
229         return [
230             (x, y[0])
231             for (x, y) in enumerate(self.historial[-self.cantidadTendencia:])
232             if y[0] is not None
233         ]
234     getValoresTendencia = synchronized('historialLock')(getValoresTendencia)
235
236     def getTendencia(self):
237         def sumatoria(valores):
238             suma = 0
239             for i in valores:
240                 suma += i
241             return suma
242         valores = self.getValoresTendencia()
243         suma_xy = sumatoria([x*y for (x, y) in valores])
244         suma_x = sumatoria([x for (x, y) in valores])
245         suma_y = sumatoria([y for (x, y) in valores])
246         suma_x2 = sumatoria([x**2 for (x, y) in valores])
247         n = len(valores)
248         pendiente = (n * suma_xy - suma_x * suma_y) / (n * suma_x2 - suma_x**2)
249         return math.atan(pendiente) * 2 / math.pi
250
251     tendencia = property(getTendencia, doc="Tendencia de las últimas 24hs " \
252         "(como valor de punto flotante entre 1 y -1).")
253
254
255 class SensorTemperatura(SensorTendencia):
256
257     def __init__(self):
258         SensorTendencia.__init__(self, 10, (0.0, -20.0), (100.0, 80.0), 0.2, 300)
259
260     def initMu(self):
261         return self.rnd.gauss(35.0, 5.0)
262
263
264 class SensorPresion(SensorTendencia):
265
266     def __init__(self):
267         SensorTendencia.__init__(self, 10, (0.0, 1000.0), (100.0, 1048.0), 1, 300)
268
269     def initMu(self):
270         return self.rnd.gauss(50.0, 5.0)
271
272
273 if __name__ == '__main__':
274     DEBUG = True
275     sensor = SensorDireccionViento()
276     sensor.setDaemon(True)
277     sensor.start()
278     sensor2 = SensorHumedad()
279     sensor2.setDaemon(True)
280     sensor2.start()
281     sensor3 = SensorVelocidadViento()
282     sensor3.setDaemon(True)
283     sensor3.start()
284     sensor4 = SensorTemperatura()
285     sensor4.setDaemon(True)
286     sensor4.periodo = 0.1
287     sensor4.start()
288     time.sleep(2)
289     print sensor4.tendencia
290     sensor2.valorBajo = (0.0, -10.0)
291     sensor2.valorAlto = (100.0, 10.0)
292     print sensor3.valorMinimo, sensor3.valorMaximo
293     time.sleep(3)
294     print sensor3.valorMinimo, sensor3.valorMaximo
295