2 # -*- coding: iso-8859-1 -*-
3 # vim: set expandtab tabstop=4 shiftwidth=4 :
4 #----------------------------------------------------------------------------
6 #----------------------------------------------------------------------------
7 # This file is part of weathemulator.
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)
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
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 #----------------------------------------------------------------------------
34 __all__ = ('SensorDireccionViento', 'SensorHumedad', 'SensorPresion',
35 'SensorTemperatura', 'SensorVelocidadViento')
46 class Sensor(Synchronizable, threading.Thread):
48 valorActual = SyncProp("Valor actualmente medido por el sensor")
50 def __init__(self, sigma, periodo):
51 threading.Thread.__init__(self)
52 self.rnd = random.Random()
54 self.periodo = periodo
55 self.mu = self.initMu()
56 self.valorActual = self.getValor()
59 raise NotImplementedError # Abstracto
62 "Simula la medición del dispositivo físico."
63 self.mu = self.rnd.gauss(self.mu, self.sigma)
67 #TODO Alguna forma de pausarlo
69 #TODO Hacer algún signal al monitor de los sensores
70 time.sleep(self.periodo)
71 self.valorActual = self.getValor()
75 return self.valorActual
78 return "<%s valorActual=%s>" % (self.__class__.__name__, self)
81 class SensorDireccionViento(Sensor):
91 ROSA = (N, NE, E, SE, S, SO, O, NO)
94 Sensor.__init__(self, sigma=0.01, periodo=0.1)
96 def rndAngle(self, base, arco):
97 return (base + arco * (self.rnd.random() - 0.5)) % math.pi
100 return self.rndAngle(0, 2 * math.pi)
103 self.mu = self.rndAngle(self.mu, self.sigma)
104 return SensorDireccionViento.ROSA[int(round(self.mu))]
106 def direccionToString(direccion):
124 direccionToString = staticmethod(direccionToString)
127 return SensorDireccionViento.direccionToString(self.valorActual)
130 return "<%s dirección=%s valorActual=%d>" % (self.__class__.__name__,
131 self, self.valorActual)
134 class SensorCalibrado(Sensor):
136 valorBruto = SyncProp("Valor en bruto leido del dispositivo.")
137 valorBajo = SyncProp("Valor bajo de calibrado como tupla (valorBruto, " \
139 valorAlto = SyncProp("Valor alto de calibrado como tupla (valorBruto, " \
142 def __init__(self, valorBajo, valorAlto, sigma, periodo):
143 self.valorBajo = valorBajo
144 self.valorAlto = valorAlto
145 Sensor.__init__(self, sigma, periodo)
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
155 return "<%s valorActual=%s valorBruto=%f valorBajo=%s valorAlto=%s>" \
156 % (self.__class__.__name__, self, self.valorBruto, self.valorBajo,
160 class SensorHistorico(SensorCalibrado):
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)
168 "Simula la medición del dispositivo físico."
169 nuevo = SensorCalibrado.getValor(self)
170 hora = time.strftime("%c")
171 self.actualizarHistorial(nuevo, hora)
174 def actualizarHistorial(self, valor, hora):
175 self.historial.pop(0)
176 self.historial.append((valor, hora))
177 actualizarHistorial = synchronized('historialLock')(actualizarHistorial)
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)
185 getMin = synchronized('historialLock')(getMin)
188 maximo = (None, None)
189 for (valor, hora) in self.historial:
190 if valor >= maximo[0]:
191 maximo = (valor, hora)
193 getMax = synchronized('historialLock')(getMax)
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).")
201 class SensorHumedad(SensorHistorico):
204 SensorHistorico.__init__(self, (0.0, 0.0), (100.0, 100.0), 0.3, 300.0)
207 return self.rnd.gauss(70.0, 5.0)
210 class SensorVelocidadViento(SensorHistorico):
213 SensorHistorico.__init__(self, (0.0, 0.0), (100.0, 100.0), 0.1, 0.5)
216 return self.rnd.gauss(30.0, 8.0)
219 class SensorTendencia(SensorHistorico):
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)
228 def getValoresTendencia(self):
231 for (x, y) in enumerate(self.historial[-self.cantidadTendencia:])
234 getValoresTendencia = synchronized('historialLock')(getValoresTendencia)
236 def getTendencia(self):
237 def sumatoria(valores):
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])
248 pendiente = (n * suma_xy - suma_x * suma_y) / (n * suma_x2 - suma_x**2)
249 return math.atan(pendiente) * 2 / math.pi
251 tendencia = property(getTendencia, doc="Tendencia de las últimas 24hs " \
252 "(como valor de punto flotante entre 1 y -1).")
255 class SensorTemperatura(SensorTendencia):
258 SensorTendencia.__init__(self, 10, (0.0, -20.0), (100.0, 80.0), 0.2, 300)
261 return self.rnd.gauss(35.0, 5.0)
264 class SensorPresion(SensorTendencia):
267 SensorTendencia.__init__(self, 10, (0.0, 1000.0), (100.0, 1048.0), 1, 300)
270 return self.rnd.gauss(50.0, 5.0)
273 if __name__ == '__main__':
275 sensor = SensorDireccionViento()
276 sensor.setDaemon(True)
278 sensor2 = SensorHumedad()
279 sensor2.setDaemon(True)
281 sensor3 = SensorVelocidadViento()
282 sensor3.setDaemon(True)
284 sensor4 = SensorTemperatura()
285 sensor4.setDaemon(True)
286 sensor4.periodo = 0.1
289 print sensor4.tendencia
290 sensor2.valorBajo = (0.0, -10.0)
291 sensor2.valorAlto = (100.0, 10.0)
292 print sensor3.valorMinimo, sensor3.valorMaximo
294 print sensor3.valorMinimo, sensor3.valorMaximo